- Added the ability to configure server settings in the app, as long as

the plugin is present and loaded.
- Fixed some color-related bugs in Checkbox
- Fixed some nasty bugs in Window that could cause views to be drawn
outside of parent or screen bounds, leading to curses crashes
- Added the ability for the app to get references to plugin preferences,
so it can create settings windows for them
- Fixed WebSocketServer so it can be shut down and restarted
This commit is contained in:
casey langen 2017-05-28 21:57:06 -07:00
parent f132f778f0
commit 6836af2aec
26 changed files with 682 additions and 99 deletions

View File

@ -98,23 +98,21 @@ static bool stringToFile(const std::string& fn, const std::string& str) {
return (written == str.size());
}
static std::string pluginFilename(std::string name) {
name.erase(std::remove_if(name.begin(), name.end(), ::isspace), name.end());
std::transform(name.begin(), name.end(), name.begin(), ::tolower);
name = "plugin_" + name; /* filename = nowhitespace(tolower(name)).json */
return name;
}
void Preferences::LoadPluginPreferences() {
typedef void(*SetPreferencesPlugin)(musik::core::sdk::IPreferences*);
PluginFactory::Instance().QueryFunction<SetPreferencesPlugin>(
"SetPreferences",
[](musik::core::sdk::IPlugin* plugin, SetPreferencesPlugin func) {
std::string name = plugin->Name();
name.erase(std::remove_if(name.begin(), name.end(), ::isspace), name.end());
std::transform(name.begin(), name.end(), name.begin(), ::tolower);
name = "plugin_" + name; /* filename = nowhitespace(tolower(name)).json */
if (pluginCache.find(name) == pluginCache.end()) {
pluginCache[name] = std::shared_ptr<Preferences>(
new Preferences(name, Preferences::ModeAutoSave));
}
func(pluginCache[name].get());
auto prefs = Preferences::ForPlugin(plugin->Name());
func(prefs.get());
});
}
@ -122,6 +120,17 @@ void Preferences::SavePluginPreferences() {
pluginCache.clear(); /* dtors will save */
}
std::shared_ptr<Preferences> Preferences::ForPlugin(const std::string& pluginName) {
std::string name = pluginFilename(pluginName);
if (pluginCache.find(name) == pluginCache.end()) {
pluginCache[name] = std::shared_ptr<Preferences>(
new Preferences(name, Preferences::ModeAutoSave));
}
return pluginCache[name];
}
std::shared_ptr<Preferences> Preferences::ForComponent(
const std::string& c, Preferences::Mode mode)
{

View File

@ -41,7 +41,6 @@
#include <core/config.h>
#include <core/db/Connection.h>
#include <core/sdk/IPreferences.h>
#include <json.hpp>
namespace musik { namespace core {
@ -56,6 +55,9 @@ namespace musik { namespace core {
static void LoadPluginPreferences();
static void SavePluginPreferences();
static std::shared_ptr<Preferences>
ForPlugin(const std::string& pluginName);
static std::shared_ptr<Preferences>
ForComponent(const std::string& c, Mode mode = ModeAutoSave);

View File

@ -15,6 +15,7 @@ set (BOX_SRCS
./app/overlay/PlaybackOverlays.cpp
./app/overlay/PlayQueueOverlays.cpp
./app/overlay/PluginOverlay.cpp
./app/overlay/ServerOverlay.cpp
./app/overlay/VisualizerOverlay.cpp
./app/util/Hotkeys.cpp
./app/util/GlobalHotkeys.cpp

View File

@ -52,6 +52,7 @@
#include <app/overlay/LocaleOverlay.h>
#include <app/overlay/PlaybackOverlays.h>
#include <app/overlay/PluginOverlay.h>
#include <app/overlay/ServerOverlay.h>
#include <boost/format.hpp>
@ -99,16 +100,17 @@ SettingsLayout::SettingsLayout(
musik::core::ILibraryPtr library,
musik::core::sdk::IPlaybackService& playback,
musik::glue::audio::MasterTransport& transport)
: LayoutBase()
, app(app)
, library(library)
, indexer(library->Indexer())
, transport(transport)
, playback(playback)
, pathsUpdated(false) {
: LayoutBase()
, app(app)
, library(library)
, indexer(library->Indexer())
, transport(transport)
, playback(playback)
, pathsUpdated(false) {
this->prefs = Preferences::ForComponent(core::prefs::components::Settings);
this->browseAdapter.reset(new DirectoryAdapter());
this->addedPathsAdapter.reset(new SimpleScrollAdapter());
this->UpdateServerAvailability();
this->InitializeWindows();
}
@ -201,6 +203,10 @@ void SettingsLayout::OnHotkeyDropdownActivate(cursespp::TextLabel* label) {
App::Overlays().Push(overlay);
}
void SettingsLayout::OnServerDropdownActivate(cursespp::TextLabel* label) {
ServerOverlay::Show([this]() { /* nothing, for now */ });
}
void SettingsLayout::OnThemeDropdownActivate(cursespp::TextLabel* label) {
ColorThemeOverlay::Show([this]() { this->LoadPreferences(); });
}
@ -238,6 +244,10 @@ void SettingsLayout::OnLayout() {
this->themeDropdown->MoveAndResize(column1, y++, columnCx, LABEL_HEIGHT);
this->hotkeyDropdown->MoveAndResize(column1, y++, columnCx, LABEL_HEIGHT);
if (serverAvailable) {
this->serverDropdown->MoveAndResize(column1, y++, columnCx, LABEL_HEIGHT);
}
y = BOTTOM(this->browseList);
#ifdef ENABLE_256_COLOR_OPTION
this->paletteCheckbox->MoveAndResize(column2, y++, columnCx, LABEL_HEIGHT);
@ -329,6 +339,12 @@ void SettingsLayout::InitializeWindows() {
this->hotkeyDropdown->SetText(arrow + _TSTR("settings_hotkey_tester"));
this->hotkeyDropdown->Activated.connect(this, &SettingsLayout::OnHotkeyDropdownActivate);
if (this->serverAvailable) {
this->serverDropdown.reset(new TextLabel());
this->serverDropdown->SetText(arrow + _TSTR("settings_server_setup"));
this->serverDropdown->Activated.connect(this, &SettingsLayout::OnServerDropdownActivate);
}
CREATE_CHECKBOX(this->dotfileCheckbox, _TSTR("settings_show_dotfiles"));
CREATE_CHECKBOX(this->syncOnStartupCheckbox, _TSTR("settings_sync_on_startup"));
CREATE_CHECKBOX(this->removeCheckbox, _TSTR("settings_remove_missing"));
@ -351,6 +367,11 @@ void SettingsLayout::InitializeWindows() {
this->pluginsDropdown->SetFocusOrder(order++);
this->themeDropdown->SetFocusOrder(order++);
this->hotkeyDropdown->SetFocusOrder(order++);
if (this->serverAvailable) {
this->serverDropdown->SetFocusOrder(order++);
}
#ifdef ENABLE_256_COLOR_OPTION
this->paletteCheckbox->SetFocusOrder(order++);
#endif
@ -372,6 +393,11 @@ void SettingsLayout::InitializeWindows() {
this->AddWindow(this->transportDropdown);
this->AddWindow(this->pluginsDropdown);
this->AddWindow(this->themeDropdown);
if (this->serverAvailable) {
this->AddWindow(this->serverDropdown);
}
#ifdef ENABLE_256_COLOR_OPTION
this->AddWindow(this->paletteCheckbox);
#endif
@ -526,6 +552,10 @@ void SettingsLayout::DrillIntoSelectedDirectory() {
this->browseList->ScrollTo(selectIndexAt);
}
void SettingsLayout::UpdateServerAvailability() {
this->serverAvailable = !!ServerOverlay::FindServerPlugin().get();
}
bool SettingsLayout::KeyPress(const std::string& key) {
if (key == "KEY_ENTER") {
if (this->GetFocus() == this->browseList) {

View File

@ -93,6 +93,7 @@ namespace musik {
void RemoveSelectedDirectory();
void DrillIntoSelectedDirectory();
void CheckShowFirstRunDialog();
void UpdateServerAvailability();
void OnCheckboxChanged(
cursespp::Checkbox* checkbox, bool checked);
@ -103,6 +104,7 @@ namespace musik {
void OnHotkeyDropdownActivate(cursespp::TextLabel* label);
void OnThemeDropdownActivate(cursespp::TextLabel* label);
void OnLocaleDropdownActivate(cursespp::TextLabel* label);
void OnServerDropdownActivate(cursespp::TextLabel* label);
int64_t ListItemDecorator(
cursespp::ScrollableWindow* w,
@ -123,6 +125,7 @@ namespace musik {
std::shared_ptr<cursespp::TextLabel> transportDropdown;
std::shared_ptr<cursespp::TextLabel> pluginsDropdown;
std::shared_ptr<cursespp::TextLabel> hotkeyDropdown;
std::shared_ptr<cursespp::TextLabel> serverDropdown;
std::shared_ptr<cursespp::TextLabel> themeDropdown;
std::shared_ptr<cursespp::Checkbox> paletteCheckbox;
@ -145,6 +148,7 @@ namespace musik {
std::shared_ptr<DirectoryAdapter> browseAdapter;
bool pathsUpdated = false;
bool serverAvailable = false;
};
}
}

View File

@ -0,0 +1,326 @@
//////////////////////////////////////////////////////////////////////////////
//
// Copyright (c) 2007-2016 musikcube team
//
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are met:
//
// * Redistributions of source code must retain the above copyright notice,
// this list of conditions and the following disclaimer.
//
// * Redistributions in binary form must reproduce the above copyright
// notice, this list of conditions and the following disclaimer in the
// documentation and/or other materials provided with the distribution.
//
// * Neither the name of the author nor the names of other contributors may
// be used to endorse or promote products derived from this software
// without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
// POSSIBILITY OF SUCH DAMAGE.
//
//////////////////////////////////////////////////////////////////////////////
#include "stdafx.h"
#include "ServerOverlay.h"
#include <core/plugin/PluginFactory.h>
#include <core/i18n/Locale.h>
#include <app/util/PreferenceKeys.h>
#include <cursespp/App.h>
#include <cursespp/DialogOverlay.h>
#include <cursespp/Screen.h>
using namespace musik;
using namespace musik::core;
using namespace musik::core::sdk;
using namespace musik::box;
using namespace cursespp;
static const std::string WEBSOCKET_PLUGIN_GUID = "9fc897a3-dfd5-4524-a0fc-b02f46aea4a9";
static const char* KEY_METADATA_SERVER_ENABLED = "websocket_server_enabled";
static const char* KEY_METADATA_SERVER_PORT = "websocket_server_port";
static const char* KEY_AUDIO_SERVER_ENABLED = "http_server_enabled";
static const char* KEY_AUDIO_SERVER_PORT = "http_server_port";
static const char* KEY_TRANSCODER_CACHE_COUNT = "transcoder_cache_count";
static const char* KEY_TRANSCODER_SYNCHRONOUS = "transcoder_synchronous";
static const char* KEY_PASSWORD = "password";
#define VERTICAL_PADDING 1
#define DEFAULT_HEIGHT 18
#define DEFAULT_WIDTH 45
#define PORT_INPUT_WIDTH 10
#define PORT_INPUT_HEIGHT 3
#define STYLE_OVERLAY_LABEL(x) \
x->SetContentColor(CURSESPP_OVERLAY_CONTENT);
#define STYLE_OVERLAY_CHECKBOX(x) \
x->SetContentColor(CURSESPP_OVERLAY_CONTENT); \
x->SetFocusedContentColor(CURSESPP_OVERLAY_TEXT_FOCUSED);
#define STYLE_OVERLAY_INPUT(x) \
x->SetFrameColor(CURSESPP_OVERLAY_FRAME); \
x->SetContentColor(CURSESPP_OVERLAY_CONTENT); \
x->SetFocusedFrameColor(CURSESPP_OVERLAY_INPUT_FRAME); \
x->SetFocusedContentColor(CURSESPP_OVERLAY_CONTENT);
#define RIGHT(x) (x->GetX() + x->GetWidth())
#define TEXT_WIDTH(x) ((int) u8cols(x->GetText()))
#ifdef WIN32
#define SAVE_KEY "^S"
#else
#define SAVE_KEY "M-s"
#endif
using Callback = ServerOverlay::Callback;
using Prefs = ServerOverlay::Prefs;
static std::string settingIntToString(Prefs prefs, const std::string& key, int defaultValue) {
int resolved = prefs->GetInt(key, defaultValue);
try {
return std::to_string(resolved);
}
catch (...) {
return std::to_string(defaultValue);
}
}
static void showInvalidDialog(Callback cb = Callback()) {
std::shared_ptr<DialogOverlay> dialog(new DialogOverlay());
(*dialog)
.SetTitle(_TSTR("settings_server_invalid_settings_title"))
.SetMessage(_TSTR("settings_server_invalid_settings_message"))
.AddButton("KEY_ENTER", "ENTER", _TSTR("button_ok"), [cb](std::string key) {
if (cb) {
cb();
}
});
App::Overlays().Push(dialog);
}
static int getIntFromTextInput(TextInput* input) {
const std::string value = input->GetText();
if (value.size()) {
try {
return std::stoi(value);
}
catch (...) {
/* swallow */
}
}
return -1;
}
ServerOverlay::ServerOverlay(Callback callback, Plugin plugin)
: OverlayBase() {
this->callback = callback;
this->plugin = plugin;
this->prefs = Preferences::ForPlugin(plugin->Name());
this->width = this->height = 0;
this->SetFrameVisible(true);
this->SetFrameColor(CURSESPP_OVERLAY_FRAME);
this->SetContentColor(CURSESPP_OVERLAY_CONTENT);
this->InitViews();
this->Load();
}
void ServerOverlay::InitViews() {
/* title */
this->titleLabel.reset(new TextLabel());
this->titleLabel->SetText(_TSTR("settings_server_setup"), text::AlignCenter);
/* shortcuts */
this->shortcuts.reset(new ShortcutsWindow());
this->shortcuts->SetAlignment(text::AlignRight);
this->shortcuts->AddShortcut("ESC", _TSTR("button_cancel"));
this->shortcuts->AddShortcut(SAVE_KEY, _TSTR("button_save"));
/* web socket server */
this->enableWssCb.reset(new Checkbox());
this->enableWssCb->SetText(_TSTR("settings_server_enable_websockets"));
this->wssPortLabel.reset(new TextLabel());
this->wssPortLabel->SetText(_TSTR("settings_server_metadata_port"), text::AlignRight);
this->wssPortInput.reset(new TextInput());
/* http server */
this->enableHttpCb.reset(new Checkbox());
this->enableHttpCb->SetText(_TSTR("settings_server_enable_http"));
this->httpPortLabel.reset(new TextLabel());
this->httpPortLabel->SetText(_TSTR("settings_server_audio_port"), text::AlignRight);
this->httpPortInput.reset(new TextInput());
/* transcoder */
this->enableSyncTransCb.reset(new Checkbox());
this->enableSyncTransCb->SetText(_TSTR("settings_server_transcoder_synchronous"));
/* password */
this->pwLabel.reset(new TextLabel());
this->pwLabel->SetText(_TSTR("settings_server_password"), text::AlignRight);
this->pwInput.reset(new TextInput(IInput::InputPassword));
/* style 'em */
STYLE_OVERLAY_LABEL(this->titleLabel);
STYLE_OVERLAY_CHECKBOX(this->enableWssCb);
STYLE_OVERLAY_LABEL(this->wssPortLabel);
STYLE_OVERLAY_INPUT(this->wssPortInput);
STYLE_OVERLAY_CHECKBOX(this->enableHttpCb);
STYLE_OVERLAY_LABEL(this->httpPortLabel);
STYLE_OVERLAY_INPUT(this->httpPortInput);
STYLE_OVERLAY_CHECKBOX(this->enableSyncTransCb);
STYLE_OVERLAY_LABEL(this->pwLabel);
STYLE_OVERLAY_INPUT(this->pwInput);
/* add 'em */
this->AddWindow(this->titleLabel);
this->AddWindow(this->enableWssCb);
this->AddWindow(this->enableHttpCb);
this->AddWindow(this->enableSyncTransCb);
this->AddWindow(this->wssPortLabel);
this->AddWindow(this->wssPortInput);
this->AddWindow(this->httpPortLabel);
this->AddWindow(this->httpPortInput);
this->AddWindow(this->pwLabel);
this->AddWindow(this->pwInput);
this->AddWindow(this->shortcuts);
/* focus order */
int order = 0;
this->enableWssCb->SetFocusOrder(order++);
this->enableHttpCb->SetFocusOrder(order++);
this->enableSyncTransCb->SetFocusOrder(order++);
this->wssPortInput->SetFocusOrder(order++);
this->httpPortInput->SetFocusOrder(order++);
this->pwInput->SetFocusOrder(order++);
}
void ServerOverlay::Layout() {
this->RecalculateSize();
this->MoveAndResize(this->x, this->y, this->width, this->height);
auto clientHeight = this->GetContentHeight();
auto clientWidth = this->GetContentWidth();
this->titleLabel->MoveAndResize(0, 0, clientWidth, 1);
this->shortcuts->MoveAndResize(0, clientHeight - 1, clientWidth, 1);
int x = 1;
int y = 2;
clientWidth -= 2;
int labelWidth = std::max(TEXT_WIDTH(this->wssPortLabel), TEXT_WIDTH(this->httpPortLabel));
labelWidth = std::max(labelWidth, TEXT_WIDTH(this->pwLabel));
int inputWidth = clientWidth - labelWidth - 1;
this->enableWssCb->MoveAndResize(x, y++, clientWidth, 1);
this->enableHttpCb->MoveAndResize(x, y++, clientWidth, 1);
this->enableSyncTransCb->MoveAndResize(x, y++, clientWidth, 1);
this->wssPortLabel->MoveAndResize(x, y + 1, labelWidth, 1);
this->wssPortInput->MoveAndResize(labelWidth + 2, y, inputWidth, PORT_INPUT_HEIGHT);
y += 3;
this->httpPortLabel->MoveAndResize(x, y + 1, labelWidth, 1);
this->httpPortInput->MoveAndResize(labelWidth + 2, y, inputWidth, PORT_INPUT_HEIGHT);
y += 3;
this->pwLabel->MoveAndResize(x, y + 1, labelWidth, 1);
this->pwInput->MoveAndResize(labelWidth + 2, y, inputWidth, PORT_INPUT_HEIGHT);
}
void ServerOverlay::Show(Callback callback) {
Plugin plugin = FindServerPlugin();
std::shared_ptr<ServerOverlay> overlay(new ServerOverlay(callback, plugin));
App::Overlays().Push(overlay);
}
std::shared_ptr<IPlugin> ServerOverlay::FindServerPlugin() {
std::shared_ptr<IPlugin> result;
using Deleter = PluginFactory::DestroyDeleter<IPlugin>;
PluginFactory::Instance().QueryInterface<IPlugin, Deleter>(
"GetPlugin",
[&result](std::shared_ptr<IPlugin> plugin, const std::string& fn) {
if (std::string(plugin->Guid()) == WEBSOCKET_PLUGIN_GUID) {
result = plugin;
}
});
return result;
}
void ServerOverlay::Load() {
this->enableWssCb->SetChecked(prefs->GetBool(KEY_METADATA_SERVER_ENABLED, false));
this->enableHttpCb->SetChecked(prefs->GetBool(KEY_AUDIO_SERVER_ENABLED, false));
this->enableSyncTransCb->SetChecked(prefs->GetBool(KEY_TRANSCODER_SYNCHRONOUS, false));
this->wssPortInput->SetText(settingIntToString(prefs, KEY_METADATA_SERVER_PORT, 7905));
this->httpPortInput->SetText(settingIntToString(prefs, KEY_AUDIO_SERVER_PORT, 7906));
this->pwInput->SetText(prefs->GetString(KEY_PASSWORD, ""));
}
bool ServerOverlay::Save() {
int wssPort = getIntFromTextInput(this->wssPortInput.get());
int httpPort = getIntFromTextInput(this->httpPortInput.get());
if (wssPort <= 0 || httpPort <= 0) {
return false;
}
this->prefs->SetBool(KEY_METADATA_SERVER_ENABLED, this->enableWssCb->IsChecked());
this->prefs->SetBool(KEY_AUDIO_SERVER_ENABLED, this->enableHttpCb->IsChecked());
this->prefs->SetBool(KEY_TRANSCODER_SYNCHRONOUS, this->enableSyncTransCb->IsChecked());
this->prefs->SetInt(KEY_METADATA_SERVER_PORT, wssPort);
this->prefs->SetInt(KEY_AUDIO_SERVER_PORT, httpPort);
this->prefs->SetString(KEY_PASSWORD, this->pwInput->GetText().c_str());
this->prefs->Save();
this->plugin->Reload();
return true;
}
bool ServerOverlay::KeyPress(const std::string& key) {
if (key == "^[") { /* esc closes */
this->Dismiss();
return true;
}
else if (key == SAVE_KEY) {
if (Save()) {
this->Dismiss();
}
else {
showInvalidDialog();
}
return true;
}
return OverlayBase::KeyPress(key);
}
void ServerOverlay::RecalculateSize() {
this->width = _DIMEN("server_overlay_width", DEFAULT_WIDTH);
this->height = std::max(0, std::min(Screen::GetHeight() - 2, DEFAULT_HEIGHT));
this->width = std::max(0, std::min(Screen::GetWidth(), this->width));
this->y = VERTICAL_PADDING;
this->x = (Screen::GetWidth() / 2) - (this->width / 2);
}

View File

@ -0,0 +1,86 @@
//////////////////////////////////////////////////////////////////////////////
//
// Copyright (c) 2007-2016 musikcube team
//
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are met:
//
// * Redistributions of source code must retain the above copyright notice,
// this list of conditions and the following disclaimer.
//
// * Redistributions in binary form must reproduce the above copyright
// notice, this list of conditions and the following disclaimer in the
// documentation and/or other materials provided with the distribution.
//
// * Neither the name of the author nor the names of other contributors may
// be used to endorse or promote products derived from this software
// without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
// POSSIBILITY OF SUCH DAMAGE.
//
//////////////////////////////////////////////////////////////////////////////
#pragma once
#include <functional>
#include <core/sdk/IPlugin.h>
#include <core/support/Preferences.h>
#include <cursespp/Checkbox.h>
#include <cursespp/TextInput.h>
#include <cursespp/TextLabel.h>
#include <cursespp/OverlayBase.h>
#include <cursespp/ShortcutsWindow.h>
namespace musik {
namespace box {
class ServerOverlay : public cursespp::OverlayBase, public sigslot::has_slots<>
#if (__clang_major__ == 7 && __clang_minor__ == 3)
, public std::enable_shared_from_this<ServerOverlay>
#endif
{
public:
using Callback = std::function<void()>;
using Plugin = std::shared_ptr<musik::core::sdk::IPlugin>;
using Prefs = std::shared_ptr<musik::core::Preferences>;
static void Show(Callback callback);
static std::shared_ptr<musik::core::sdk::IPlugin> FindServerPlugin();
virtual void Layout();
virtual bool KeyPress(const std::string& key);
private:
ServerOverlay(Callback callback, Plugin plugin);
void RecalculateSize();
void InitViews();
bool Save();
void Load();
Callback callback;
Plugin plugin;
Prefs prefs;
int width, height, x, y;
std::shared_ptr<cursespp::TextLabel> titleLabel;
std::shared_ptr<cursespp::Checkbox> enableWssCb, enableHttpCb, enableSyncTransCb;
std::shared_ptr<cursespp::TextLabel> wssPortLabel, httpPortLabel, pwLabel;
std::shared_ptr<cursespp::TextInput> wssPortInput, httpPortInput, pwInput;
std::shared_ptr<cursespp::ShortcutsWindow> shortcuts;
};
}
}

View File

@ -49,6 +49,7 @@ Checkbox::Checkbox()
: Window()
, checked(false) {
this->SetFrameVisible(false);
this->SetFocusedContentColor(CURSESPP_TEXT_FOCUSED);
}
Checkbox::~Checkbox() {
@ -81,7 +82,9 @@ void Checkbox::OnRedraw() {
std::string symbol = (this->checked ? CHECKED : UNCHECKED);
std::string ellipsized = text::Ellipsize(symbol + " " + this->buffer, cx);
int64_t attrs = this->IsFocused() ? CURSESPP_TEXT_FOCUSED : CURSESPP_DEFAULT_COLOR;
int64_t attrs = this->IsFocused()
? this->GetFocusedContentColor()
: this->GetContentColor();
if (attrs != -1) {
wattron(c, COLOR_PAIR(attrs));

View File

@ -54,26 +54,27 @@ indicies we'll use to store them */
#define THEME_COLOR_OVERLAY_FOREGROUND 26
#define THEME_COLOR_OVERLAY_BORDER 27
#define THEME_COLOR_OVERLAY_FOCUSED_BORDER 28
#define THEME_COLOR_SHORTCUTS_BACKGROUND 29
#define THEME_COLOR_SHORTCUTS_FOREGROUND 30
#define THEME_COLOR_SHORTCUTS_BACKGROUND_FOCUSED 31
#define THEME_COLOR_SHORTCUTS_FOREGROUND_FOCUSED 32
#define THEME_COLOR_BUTTON_BACKGROUND_NORMAL 33
#define THEME_COLOR_BUTTON_FOREGROUND_NORMAL 34
#define THEME_COLOR_BUTTON_BACKGROUND_ACTIVE 35
#define THEME_COLOR_BUTTON_FOREGROUND_ACTIVE 36
#define THEME_COLOR_BANNER_BACKGROUND 37
#define THEME_COLOR_BANNER_FOREGROUND 38
#define THEME_COLOR_LIST_HEADER_BACKGROUND 39
#define THEME_COLOR_LIST_HEADER_FOREGROUND 40
#define THEME_COLOR_LIST_HEADER_HIGHLIGHTED_BACKGROUND 41
#define THEME_COLOR_LIST_HEADER_HIGHLIGHTED_FOREGROUND 42
#define THEME_COLOR_LIST_ITEM_HIGHLIGHTED_BACKGROUND 43
#define THEME_COLOR_LIST_ITEM_HIGHLIGHTED_FOREGROUND 44
#define THEME_COLOR_LIST_ITEM_ACTIVE_BACKGROUND 45
#define THEME_COLOR_LIST_ITEM_ACTIVE_FOREGROUND 46
#define THEME_COLOR_LIST_ITEM_ACTIVE_HIGHLIGHTED_BACKGROUND 47
#define THEME_COLOR_LIST_ITEM_ACTIVE_HIGHLIGHTED_FOREGROUND 48
#define THEME_COLOR_OVERLAY_FOCUSED_TEXT 29
#define THEME_COLOR_SHORTCUTS_BACKGROUND 30
#define THEME_COLOR_SHORTCUTS_FOREGROUND 31
#define THEME_COLOR_SHORTCUTS_BACKGROUND_FOCUSED 32
#define THEME_COLOR_SHORTCUTS_FOREGROUND_FOCUSED 33
#define THEME_COLOR_BUTTON_BACKGROUND_NORMAL 34
#define THEME_COLOR_BUTTON_FOREGROUND_NORMAL 35
#define THEME_COLOR_BUTTON_BACKGROUND_ACTIVE 36
#define THEME_COLOR_BUTTON_FOREGROUND_ACTIVE 37
#define THEME_COLOR_BANNER_BACKGROUND 38
#define THEME_COLOR_BANNER_FOREGROUND 39
#define THEME_COLOR_LIST_HEADER_BACKGROUND 40
#define THEME_COLOR_LIST_HEADER_FOREGROUND 41
#define THEME_COLOR_LIST_HEADER_HIGHLIGHTED_BACKGROUND 42
#define THEME_COLOR_LIST_HEADER_HIGHLIGHTED_FOREGROUND 43
#define THEME_COLOR_LIST_ITEM_HIGHLIGHTED_BACKGROUND 44
#define THEME_COLOR_LIST_ITEM_HIGHLIGHTED_FOREGROUND 45
#define THEME_COLOR_LIST_ITEM_ACTIVE_BACKGROUND 46
#define THEME_COLOR_LIST_ITEM_ACTIVE_FOREGROUND 47
#define THEME_COLOR_LIST_ITEM_ACTIVE_HIGHLIGHTED_BACKGROUND 48
#define THEME_COLOR_LIST_ITEM_ACTIVE_HIGHLIGHTED_FOREGROUND 49
/* user-readable names for the color identifiers above. these are
used as key names in the config files */
@ -90,6 +91,7 @@ used as key names in the config files */
#define JSON_KEY_COLOR_OVERLAY_FOREGROUND "overlay_foreground"
#define JSON_KEY_COLOR_OVERLAY_BORDER "overlay_border"
#define JSON_KEY_COLOR_OVERLAY_FOCUSED_BORDER "overlay_focused_border"
#define JSON_KEY_COLOR_OVERLAY_FOCUSED_TEXT "overlay_focused_text"
#define JSON_KEY_COLOR_SHORTCUTS_BACKGROUND "shortcuts_background"
#define JSON_KEY_COLOR_SHORTCUTS_FOREGROUND "shortcuts_foreground"
#define JSON_KEY_COLOR_SHORTCUTS_BACKGROUND_FOCUSED "shortcuts_background_focused"
@ -266,6 +268,7 @@ struct Theme {
overlayForeground.Set(THEME_COLOR_OVERLAY_FOREGROUND, 220, 220, 220, COLOR_256_OFFWHITE);
overlayBorder.Set(THEME_COLOR_OVERLAY_BORDER, 102, 217, 238, COLOR_256_BLUE);
overlayFocusedBorder.Set(THEME_COLOR_OVERLAY_FOCUSED_BORDER, 220, 82, 86, COLOR_256_RED);
overlayFocusedText.Set(THEME_COLOR_OVERLAY_FOCUSED_TEXT, 220, 82, 86, COLOR_256_RED);
/* shortcut bar */
shortcutsBackground.Set(THEME_COLOR_SHORTCUTS_BACKGROUND, 66, 66, 56, COLOR_256_MEDIUM_GRAY);
@ -330,6 +333,7 @@ struct Theme {
this->overlayForeground.Set(colors.value(JSON_KEY_COLOR_OVERLAY_FOREGROUND, unset));
this->overlayBorder.Set(colors.value(JSON_KEY_COLOR_OVERLAY_BORDER, unset));
this->overlayFocusedBorder.Set(colors.value(JSON_KEY_COLOR_OVERLAY_FOCUSED_BORDER, unset));
this->overlayFocusedText.Set(colors.value(JSON_KEY_COLOR_OVERLAY_FOCUSED_TEXT, unset));
this->shortcutsBackground.Set(colors.value(JSON_KEY_COLOR_SHORTCUTS_BACKGROUND, unset));
this->shortcutsForeground.Set(colors.value(JSON_KEY_COLOR_SHORTCUTS_FOREGROUND, unset));
this->focusedShortcutsBackground.Set(colors.value(JSON_KEY_COLOR_SHORTCUTS_BACKGROUND_FOCUSED, unset));
@ -387,6 +391,7 @@ struct Theme {
init_pair(CURSESPP_OVERLAY_FRAME, overlayBorder.Id(mode, COLOR_BLUE), overlayBgId);
init_pair(CURSESPP_OVERLAY_CONTENT, overlayForeground.Id(mode, -1), overlayBgId);
init_pair(CURSESPP_OVERLAY_INPUT_FRAME, overlayFocusedBorder.Id(mode, COLOR_RED), overlayBgId);
init_pair(CURSESPP_OVERLAY_TEXT_FOCUSED, overlayFocusedText.Id(mode, COLOR_RED), overlayBgId);
/* shortcuts */
init_pair(
@ -466,6 +471,7 @@ struct Theme {
Color overlayForeground;
Color overlayBorder;
Color overlayFocusedBorder;
Color overlayFocusedText;
/* shortcut bar */
Color shortcutsBackground;

View File

@ -66,8 +66,9 @@
#define CURSESPP_OVERLAY_FRAME 21
#define CURSESPP_OVERLAY_CONTENT 22
#define CURSESPP_OVERLAY_INPUT_FRAME 23
#define CURSESPP_OVERLAY_TEXT_FOCUSED 24
#define CURSESPP_BANNER 24
#define CURSESPP_BANNER 25
namespace cursespp {
class Colors {

View File

@ -41,7 +41,8 @@ namespace cursespp {
public:
enum InputMode {
InputRaw,
InputNormal
InputNormal,
InputPassword
};
virtual ~IInput() { }

View File

@ -348,7 +348,10 @@ IWindowPtr LayoutBase::FocusLast() {
IWindowPtr LayoutBase::GetFocus() {
if (this->focused >= 0 && this->focusable.size() > 0) {
return this->focusable[this->focused];
auto view = this->focusable[this->focused];
if (view->IsVisible()) {
return view;
}
}
return IWindowPtr();

View File

@ -41,6 +41,10 @@
namespace cursespp {
class OverlayBase : public LayoutBase, public IOverlay {
public:
OverlayBase() : LayoutBase() {
}
virtual ~OverlayBase() {
this->stack = nullptr;
}

View File

@ -67,7 +67,8 @@ TextInput::TextInput(IInput::InputMode inputMode)
: Window()
, bufferLength(0)
, position(0)
, inputMode(inputMode) {
, inputMode(inputMode)
, enterEnabled(true) {
}
TextInput::~TextInput() {
@ -76,7 +77,20 @@ TextInput::~TextInput() {
void TextInput::OnRedraw() {
WINDOW* c = this->GetContent();
werase(c);
waddstr(c, buffer.c_str());
if (buffer.size()) {
if (inputMode == InputPassword) {
size_t count = u8cols(buffer);
std::string masked(count, '*');
waddstr(c, masked.c_str());
}
else {
waddstr(c, buffer.c_str());
}
}
else if (!this->IsFocused() && hintText.size()) {
waddstr(c, hintText.c_str());
}
}
size_t TextInput::Length() {
@ -116,6 +130,10 @@ bool TextInput::Write(const std::string& key) {
return false;
}
void TextInput::SetEnterEnabled(bool enabled) {
this->enterEnabled = enabled;
}
bool TextInput::KeyPress(const std::string& key) {
if (key == "M-KEY_BACKSPACE") {
this->SetText("");
@ -133,8 +151,13 @@ bool TextInput::KeyPress(const std::string& key) {
return true;
}
else if (key == "KEY_ENTER") {
this->EnterPressed(this);
return true;
if (enterEnabled) {
this->EnterPressed(this);
return true;
}
else {
return false;
}
}
else if (key == "KEY_LEFT") {
return this->OffsetPosition(-1);
@ -187,3 +210,8 @@ void TextInput::SetText(const std::string& value) {
this->Redraw();
}
}
void TextInput::SetHint(const std::string& hint) {
this->hintText = hint;
this->Redraw();
}

View File

@ -72,11 +72,15 @@ namespace cursespp {
virtual void SetText(const std::string& value);
virtual std::string GetText() { return this->buffer; }
void SetHint(const std::string& hint);
void SetEnterEnabled(bool enabled);
private:
bool OffsetPosition(int delta);
std::string buffer;
std::string buffer, hintText;
int position;
bool enterEnabled;
size_t bufferLength;
InputMode inputMode;
};

View File

@ -142,7 +142,7 @@ void Window::ProcessMessage(musik::core::runtime::IMessage &message) {
}
bool Window::IsVisible() {
return this->isVisible;
return !this->badBounds && this->isVisible;
}
bool Window::IsFocused() {
@ -245,13 +245,23 @@ void Window::MoveAndResize(int x, int y, int width, int height) {
absX != this->lastAbsoluteX ||
absY != this->lastAbsoluteY;
if (sizeChanged || positionChanged) {
if (sizeChanged || positionChanged || badBounds) {
this->lastAbsoluteX = absX;
this->lastAbsoluteY = absY;
this->width = width;
this->height = height;
this->RecreateForUpdatedDimensions();
}
else {
this->DestroyIfBadBounds();
}
}
void Window::DestroyIfBadBounds() {
if (this->CheckForBoundsError()) {
this->badBounds = true;
this->Destroy();
}
}
void Window::SetSize(int width, int height) {
@ -260,6 +270,9 @@ void Window::SetSize(int width, int height) {
this->height = height;
this->RecreateForUpdatedDimensions();
}
else {
this->DestroyIfBadBounds();
}
}
void Window::SetPosition(int x, int y) {
@ -273,6 +286,9 @@ void Window::SetPosition(int x, int y) {
this->lastAbsoluteY = absY;
this->RecreateForUpdatedDimensions();
}
else {
this->DestroyIfBadBounds();
}
}
void Window::OnDimensionsChanged() {
@ -510,9 +526,17 @@ bool Window::CheckForBoundsError() {
return false;
}
if (this->width > this->parent->GetContentWidth() ||
this->height > this->parent->GetContentHeight())
{
const int parentWidth = this->parent->GetContentWidth();
const int parentHeight = this->parent->GetContentHeight();
if (this->width > parentWidth || this->height > parentHeight) {
return true;
}
const int right = this->x + cx;
const int bottom = this->y + cy;
if (right > parentWidth || bottom > parentHeight) {
return true;
}

View File

@ -131,6 +131,7 @@ namespace cursespp {
void Clear();
void RepaintBackground();
void RecreateForUpdatedDimensions();
void DestroyIfBadBounds();
bool CheckForBoundsError();

View File

@ -6,6 +6,8 @@
"button_ok": "ok",
"button_yes": "yes",
"button_no": "no",
"button_save": "save",
"button_cancel": "cancel",
"browse_title_artists": "artists",
"browse_title_albums": "albums",
@ -36,6 +38,17 @@
"settings_seek_not_scrub": "seek playback (don't scrub)",
"settings_minimize_to_tray": "minimize to tray",
"settings_start_minimized": "start minimized",
"settings_server_setup": "server setup",
"settings_server_enable_websockets": "metadata server enabled",
"settings_server_metadata_port": "metadata port:",
"settings_server_enable_http": "audio streaming enabled",
"settings_server_audio_port": "audio port:",
"settings_server_transcoder_synchronous": "synchronous transcoding",
"settings_server_transcoder_cache_size": "transcoder file cache count",
"settings_server_password": "password:",
"settings_server_invalid_settings_title": "invalid settings",
"settings_server_invalid_settings_message": "invalid or missing settings. please check the values and try again.",
"locale_overlay_select_title": "select locale",
@ -107,6 +120,7 @@
"playqueue_album_header_overlay": 35,
"playqueue_playlist_add_to_queue_overlay": 35,
"playqueue_playlist_list_overlay": 35,
"playqueue_playlist_name_overlay": 35
"playqueue_playlist_name_overlay": 35,
"server_overlay_width": 45
}
}

View File

@ -54,6 +54,10 @@
"hex": "#dc322f",
"palette": 160
},
"overlay_focused_text": {
"hex": "#dc322f",
"palette": 160
},
"shortcuts_background": {
"hex": "#586e75",
"palette": 240

View File

@ -54,6 +54,10 @@
"hex": "#dc322f",
"palette": 160
},
"overlay_focused_text": {
"hex": "#dc322f",
"palette": 160
},
"shortcuts_background": {
"hex": "#586e75",
"palette": 240

View File

@ -149,6 +149,7 @@ xcopy "$(SolutionDir)src\plugins\websocket_remote\3rdparty\win32_bin\$(Configura
<ClCompile Include="app\overlay\PlaybackOverlays.cpp" />
<ClCompile Include="app\overlay\PlayQueueOverlays.cpp" />
<ClCompile Include="app\overlay\PluginOverlay.cpp" />
<ClCompile Include="app\overlay\ServerOverlay.cpp" />
<ClCompile Include="app\overlay\VisualizerOverlay.cpp" />
<ClCompile Include="app\util\GlobalHotkeys.cpp" />
<ClCompile Include="app\util\Hotkeys.cpp" />
@ -201,6 +202,7 @@ xcopy "$(SolutionDir)src\plugins\websocket_remote\3rdparty\win32_bin\$(Configura
<ClInclude Include="app\overlay\PlaybackOverlays.h" />
<ClInclude Include="app\overlay\PlayQueueOverlays.h" />
<ClInclude Include="app\overlay\PluginOverlay.h" />
<ClInclude Include="app\overlay\ServerOverlay.h" />
<ClInclude Include="app\overlay\VisualizerOverlay.h" />
<ClInclude Include="app\util\GlobalHotkeys.h" />
<ClInclude Include="app\util\Hotkeys.h" />

View File

@ -135,6 +135,9 @@
<ClCompile Include="app\overlay\LocaleOverlay.cpp">
<Filter>app\overlay</Filter>
</ClCompile>
<ClCompile Include="app\overlay\ServerOverlay.cpp">
<Filter>app\overlay</Filter>
</ClCompile>
</ItemGroup>
<ItemGroup>
<ClInclude Include="stdafx.h" />
@ -319,6 +322,9 @@
<ClInclude Include="app\util\Messages.h">
<Filter>app\util</Filter>
</ClInclude>
<ClInclude Include="app\overlay\ServerOverlay.h">
<Filter>app\overlay</Filter>
</ClInclude>
</ItemGroup>
<ItemGroup>
<Filter Include="cursespp">

View File

@ -47,6 +47,7 @@ namespace defaults {
}
namespace prefs {
static const std::string websocket_server_enabled = "websocket_server_enabled";
static const std::string websocket_server_port = "websocket_server_port";
static const std::string http_server_enabled = "http_server_enabled";
static const std::string http_server_port = "http_server_port";

View File

@ -79,25 +79,27 @@ bool WebSocketServer::Start() {
void WebSocketServer::ThreadProc() {
try {
wss.reset(new server());
if (context.prefs->GetBool("debug")) {
wss.get_alog().set_ostream(&std::cerr);
wss.get_elog().set_ostream(&std::cerr);
wss.set_access_channels(websocketpp::log::alevel::all);
wss.clear_access_channels(websocketpp::log::alevel::frame_payload);
wss->get_alog().set_ostream(&std::cerr);
wss->get_elog().set_ostream(&std::cerr);
wss->set_access_channels(websocketpp::log::alevel::all);
wss->clear_access_channels(websocketpp::log::alevel::frame_payload);
}
else {
wss.set_access_channels(websocketpp::log::alevel::none);
wss.clear_access_channels(websocketpp::log::alevel::none);
wss->set_access_channels(websocketpp::log::alevel::none);
wss->clear_access_channels(websocketpp::log::alevel::none);
}
wss.init_asio();
wss.set_message_handler(std::bind(&WebSocketServer::OnMessage, this, &wss, ::_1, ::_2));
wss.set_open_handler(std::bind(&WebSocketServer::OnOpen, this, ::_1));
wss.set_close_handler(std::bind(&WebSocketServer::OnClose, this, ::_1));
wss.listen(context.prefs->GetInt(prefs::websocket_server_port.c_str(), defaults::websocket_server_port));
wss.start_accept();
wss->init_asio();
wss->set_message_handler(std::bind(&WebSocketServer::OnMessage, this, wss.get(), ::_1, ::_2));
wss->set_open_handler(std::bind(&WebSocketServer::OnOpen, this, ::_1));
wss->set_close_handler(std::bind(&WebSocketServer::OnClose, this, ::_1));
wss->listen(context.prefs->GetInt(prefs::websocket_server_port.c_str(), defaults::websocket_server_port));
wss->start_accept();
wss.run();
wss->run();
}
catch (websocketpp::exception const & e) {
std::cerr << e.what() << std::endl;
@ -109,13 +111,17 @@ void WebSocketServer::ThreadProc() {
std::cerr << "unknown exception" << std::endl;
}
this->wss.reset();
this->running = false;
this->exitCondition.notify_all();
}
bool WebSocketServer::Stop() {
if (this->thread) {
wss.stop();
if (this->wss) {
wss->stop();
}
this->thread->join();
this->thread.reset();
}
@ -167,7 +173,7 @@ void WebSocketServer::HandleAuthentication(connection_hdl connection, json& requ
}
}
this->wss.close(
this->wss->close(
connection,
websocketpp::close::status::policy_violation,
value::unauthenticated);
@ -329,7 +335,7 @@ void WebSocketServer::Broadcast(const std::string& name, json& options) {
auto rl = connectionLock.Read();
for (const auto &keyValue : this->connections) {
wss.send(keyValue.first, str.c_str(), websocketpp::frame::opcode::text);
wss->send(keyValue.first, str.c_str(), websocketpp::frame::opcode::text);
}
}
@ -341,7 +347,7 @@ void WebSocketServer::RespondWithOptions(connection_hdl connection, json& reques
{ message::options, options }
};
wss.send(connection, response.dump().c_str(), websocketpp::frame::opcode::text);
wss->send(connection, response.dump().c_str(), websocketpp::frame::opcode::text);
}
void WebSocketServer::RespondWithOptions(connection_hdl connection, json& request, json&& options) {
@ -352,7 +358,7 @@ void WebSocketServer::RespondWithOptions(connection_hdl connection, json& reques
{ message::options, options }
};
wss.send(connection, response.dump().c_str(), websocketpp::frame::opcode::text);
wss->send(connection, response.dump().c_str(), websocketpp::frame::opcode::text);
}
void WebSocketServer::RespondWithInvalidRequest(connection_hdl connection, const std::string& name, const std::string& id)
@ -364,7 +370,7 @@ void WebSocketServer::RespondWithInvalidRequest(connection_hdl connection, const
{ key::error, value::invalid }
} }
};
wss.send(connection, error.dump().c_str(), websocketpp::frame::opcode::text);
wss->send(connection, error.dump().c_str(), websocketpp::frame::opcode::text);
}
void WebSocketServer::RespondWithSuccess(connection_hdl connection, json& request) {
@ -382,7 +388,7 @@ void WebSocketServer::RespondWithSuccess(connection_hdl connection, const std::s
{ message::options,{ key::success, true } }
};
wss.send(connection, success.dump().c_str(), websocketpp::frame::opcode::text);
wss->send(connection, success.dump().c_str(), websocketpp::frame::opcode::text);
}
void WebSocketServer::RespondWithFailure(connection_hdl connection, json& request) {
@ -393,7 +399,7 @@ void WebSocketServer::RespondWithFailure(connection_hdl connection, json& reques
{ message::options,{ key::success, false } }
};
wss.send(connection, error.dump().c_str(), websocketpp::frame::opcode::text);
wss->send(connection, error.dump().c_str(), websocketpp::frame::opcode::text);
}
void WebSocketServer::RespondWithSetVolume(connection_hdl connection, json& request) {

View File

@ -113,8 +113,8 @@ class WebSocketServer {
/* vars */
Context& context;
ConnectionList connections;
server wss;
ReadWriteLock connectionLock;
std::shared_ptr<server> wss;
std::shared_ptr<std::thread> thread;
std::mutex exitMutex;
std::condition_variable exitCondition;

View File

@ -53,19 +53,6 @@ using namespace musik::core::sdk;
static Context context;
static class Plugin : public IPlugin {
public:
virtual void Destroy() { }
virtual const char* Name() { return "WebSockets IPlaybackRemote"; }
virtual const char* Version() { return "0.6.0"; }
virtual const char* Author() { return "clangen"; }
virtual const char* Guid() { return "9fc897a3-dfd5-4524-a0fc-b02f46aea4a9"; }
virtual bool Configurable() { return false; }
virtual void Configure() { }
virtual void Reload() { }
virtual int SdkVersion() { return musik::core::sdk::SdkVersion; }
} plugin;
static class PlaybackRemote : public IPlaybackRemote {
private:
HttpServer httpServer;
@ -78,25 +65,24 @@ static class PlaybackRemote : public IPlaybackRemote {
}
virtual ~PlaybackRemote() {
if (this->thread) {
httpServer.Stop();
webSocketServer.Stop();
this->thread->join();
}
this->Stop();
}
virtual void Destroy() {
}
void Reload() {
auto wl = context.lock.Write();
this->Stop();
this->CheckRunningStatus();
}
void CheckRunningStatus() {
if (!thread && context.environment && context.playback && context.prefs && context.dataProvider) {
thread.reset(new std::thread(std::bind(&PlaybackRemote::ThreadProc, this)));
}
else if (thread && (!context.environment || !context.playback || !context.prefs || !context.dataProvider)) {
httpServer.Stop();
webSocketServer.Stop();
this->thread->join();
this->thread.reset();
this->Stop();
}
}
@ -132,14 +118,40 @@ static class PlaybackRemote : public IPlaybackRemote {
httpServer.Start();
}
webSocketServer.Start();
if (context.prefs->GetBool(prefs::websocket_server_enabled.c_str(), true)) {
webSocketServer.Start();
}
httpServer.Wait();
webSocketServer.Wait();
}
void Stop() {
httpServer.Stop();
webSocketServer.Stop();
if (this->thread) {
this->thread->join();
this->thread.reset();
}
}
std::shared_ptr<std::thread> thread;
} remote;
static class Plugin : public IPlugin {
public:
virtual void Destroy() { }
virtual const char* Name() { return "WebSockets IPlaybackRemote"; }
virtual const char* Version() { return "0.6.0"; }
virtual const char* Author() { return "clangen"; }
virtual const char* Guid() { return "9fc897a3-dfd5-4524-a0fc-b02f46aea4a9"; }
virtual bool Configurable() { return false; }
virtual void Configure() { }
virtual void Reload() { remote.Reload(); }
virtual int SdkVersion() { return musik::core::sdk::SdkVersion; }
} plugin;
extern "C" DLL_EXPORT IPlugin* GetPlugin() {
return &plugin;
}
@ -159,6 +171,7 @@ extern "C" DLL_EXPORT void SetPreferences(musik::core::sdk::IPreferences* prefs)
context.prefs = prefs;
if (prefs) {
prefs->GetBool(prefs::websocket_server_enabled.c_str(), true);
prefs->GetInt(prefs::websocket_server_port.c_str(), defaults::websocket_server_port);
prefs->GetInt(prefs::http_server_port.c_str(), defaults::http_server_port);
prefs->GetBool(prefs::http_server_enabled.c_str(), true);