added basic ui
This commit is contained in:
parent
587969d304
commit
82bb529bc2
|
@ -5,6 +5,7 @@
|
||||||
"process_names": [
|
"process_names": [
|
||||||
"AppleMusic.exe"
|
"AppleMusic.exe"
|
||||||
],
|
],
|
||||||
|
"enabled": true,
|
||||||
"search_endpoint": "https://music.apple.com/search?term=",
|
"search_endpoint": "https://music.apple.com/search?term=",
|
||||||
"client_id": "1245257240890310686"
|
"client_id": "1245257240890310686"
|
||||||
},
|
},
|
||||||
|
@ -13,6 +14,7 @@
|
||||||
"process_names": [
|
"process_names": [
|
||||||
"Spotify.exe"
|
"Spotify.exe"
|
||||||
],
|
],
|
||||||
|
"enabled": true,
|
||||||
"search_endpoint": "https://open.spotify.com/search/",
|
"search_endpoint": "https://open.spotify.com/search/",
|
||||||
"client_id": "1245257414715113573"
|
"client_id": "1245257414715113573"
|
||||||
},
|
},
|
||||||
|
@ -21,6 +23,7 @@
|
||||||
"process_names": [
|
"process_names": [
|
||||||
"TIDAL.exe"
|
"TIDAL.exe"
|
||||||
],
|
],
|
||||||
|
"enabled": true,
|
||||||
"search_endpoint": "https://listen.tidal.com/search?q=",
|
"search_endpoint": "https://listen.tidal.com/search?q=",
|
||||||
"client_id": "1245257493966225488"
|
"client_id": "1245257493966225488"
|
||||||
}
|
}
|
124
src/main.cpp
124
src/main.cpp
|
@ -1,6 +1,7 @@
|
||||||
#include <discord-rpc/discord_rpc.h>
|
#include <discord-rpc/discord_rpc.h>
|
||||||
#include <wx/image.h>
|
#include <wx/image.h>
|
||||||
#include <wx/mstream.h>
|
#include <wx/mstream.h>
|
||||||
|
#include <wx/statline.h>
|
||||||
#include <wx/taskbar.h>
|
#include <wx/taskbar.h>
|
||||||
#include <wx/wx.h>
|
#include <wx/wx.h>
|
||||||
|
|
||||||
|
@ -102,51 +103,134 @@ void handleMediaTasks() {
|
||||||
Discord_UpdatePresence(&activity);
|
Discord_UpdatePresence(&activity);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
class MyTaskBarIcon : public wxTaskBarIcon {
|
class PlayerLinkIcon : public wxTaskBarIcon {
|
||||||
public:
|
public:
|
||||||
MyTaskBarIcon(wxFrame* mainFrame) : m_mainFrame(mainFrame) {}
|
PlayerLinkIcon(wxFrame* s) : settingsFrame(s) {}
|
||||||
|
|
||||||
void OnMenuOpen(wxCommandEvent& evt) { m_mainFrame->Show(true); }
|
void OnMenuOpen(wxCommandEvent& evt) { settingsFrame->Show(true); }
|
||||||
|
|
||||||
void OnMenuExit(wxCommandEvent& evt) { m_mainFrame->Close(true); }
|
void OnMenuExit(wxCommandEvent& evt) { settingsFrame->Close(true); }
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
virtual wxMenu* CreatePopupMenu() override {
|
virtual wxMenu* CreatePopupMenu() override {
|
||||||
wxMenu* menu = new wxMenu;
|
wxMenu* menu = new wxMenu;
|
||||||
menu->Append(10001, "Open");
|
menu->Append(10004, _("Not Playing")); // TODO: make this dynamic
|
||||||
|
menu->Enable(10004, false);
|
||||||
menu->AppendSeparator();
|
menu->AppendSeparator();
|
||||||
menu->Append(10002, "Quit");
|
menu->Append(10001, _("Settings"));
|
||||||
Bind(wxEVT_MENU, &MyTaskBarIcon::OnMenuOpen, this, 10001);
|
menu->Append(10003, _("About PlayerLink"));
|
||||||
Bind(wxEVT_MENU, &MyTaskBarIcon::OnMenuExit, this, 10002);
|
menu->AppendSeparator();
|
||||||
|
menu->Append(10002, _("Quit PlayerLink..."));
|
||||||
|
Bind(wxEVT_MENU, &PlayerLinkIcon::OnMenuOpen, this, 10001);
|
||||||
|
Bind(wxEVT_MENU, &PlayerLinkIcon::OnMenuExit, this, 10002);
|
||||||
return menu;
|
return menu;
|
||||||
}
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
wxFrame* m_mainFrame;
|
wxFrame* settingsFrame;
|
||||||
};
|
};
|
||||||
class MyApp : public wxApp {
|
|
||||||
|
class PlayerLinkFrame : public wxFrame {
|
||||||
|
protected:
|
||||||
|
wxStaticText* settingsText;
|
||||||
|
wxStaticLine* settingsDivider;
|
||||||
|
wxStaticText* enabledAppsText;
|
||||||
|
wxCheckBox* anyOtherCheckbox;
|
||||||
|
wxStaticLine* appsDivider;
|
||||||
|
wxStaticText* startupText;
|
||||||
|
wxCheckBox* autostartCheckbox;
|
||||||
|
|
||||||
|
public:
|
||||||
|
PlayerLinkFrame(wxWindow* parent, wxWindowID id = wxID_ANY, const wxString& title = wxEmptyString,
|
||||||
|
const wxPoint& pos = wxDefaultPosition, const wxSize& size = wxSize(300, 200),
|
||||||
|
long style = wxDEFAULT_FRAME_STYLE & ~wxRESIZE_BORDER & ~wxMAXIMIZE_BOX)
|
||||||
|
: wxFrame(parent, id, title, pos, size, style) {
|
||||||
|
this->SetSizeHints(wxDefaultSize, wxDefaultSize);
|
||||||
|
|
||||||
|
wxBoxSizer* mainContainer;
|
||||||
|
mainContainer = new wxBoxSizer(wxVERTICAL);
|
||||||
|
|
||||||
|
settingsText = new wxStaticText(this, wxID_ANY, _("Settings"), wxDefaultPosition, wxDefaultSize, 0);
|
||||||
|
settingsText->Wrap(-1);
|
||||||
|
mainContainer->Add(settingsText, 0, wxALIGN_CENTER | wxALL, 5);
|
||||||
|
|
||||||
|
settingsDivider = new wxStaticLine(this, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxLI_HORIZONTAL);
|
||||||
|
mainContainer->Add(settingsDivider, 0, wxEXPAND | wxALL, 5);
|
||||||
|
|
||||||
|
wxBoxSizer* enabledAppsContainer;
|
||||||
|
enabledAppsContainer = new wxBoxSizer(wxHORIZONTAL);
|
||||||
|
|
||||||
|
enabledAppsText = new wxStaticText(this, wxID_ANY, _("Enabled Apps:"), wxDefaultPosition, wxDefaultSize, 0);
|
||||||
|
enabledAppsText->Wrap(-1);
|
||||||
|
enabledAppsContainer->Add(enabledAppsText, 0, wxALL, 5);
|
||||||
|
|
||||||
|
wxBoxSizer* appCheckboxContainer;
|
||||||
|
appCheckboxContainer = new wxBoxSizer(wxVERTICAL);
|
||||||
|
|
||||||
|
auto apps = utils::getAllApps();
|
||||||
|
|
||||||
|
for (auto app : apps) {
|
||||||
|
auto checkbox = new wxCheckBox(this, wxID_ANY, _(app.appName), wxDefaultPosition, wxDefaultSize, 0);
|
||||||
|
appCheckboxContainer->Add(checkbox, 0, wxALL, 5);
|
||||||
|
}
|
||||||
|
|
||||||
|
anyOtherCheckbox = new wxCheckBox(this, wxID_ANY, _("Any other"), wxDefaultPosition, wxDefaultSize, 0);
|
||||||
|
appCheckboxContainer->Add(anyOtherCheckbox, 0, wxALL, 5);
|
||||||
|
|
||||||
|
enabledAppsContainer->Add(appCheckboxContainer, 1, wxEXPAND, 5);
|
||||||
|
|
||||||
|
mainContainer->Add(enabledAppsContainer, 0, 0, 5);
|
||||||
|
|
||||||
|
appsDivider = new wxStaticLine(this, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxLI_HORIZONTAL);
|
||||||
|
mainContainer->Add(appsDivider, 0, wxEXPAND | wxALL, 5);
|
||||||
|
|
||||||
|
wxBoxSizer* settingsContainer;
|
||||||
|
settingsContainer = new wxBoxSizer(wxHORIZONTAL);
|
||||||
|
|
||||||
|
startupText = new wxStaticText(this, wxID_ANY, _("Startup:"), wxDefaultPosition, wxDefaultSize, 0);
|
||||||
|
startupText->Wrap(-1);
|
||||||
|
settingsContainer->Add(startupText, 0, wxALL, 5);
|
||||||
|
|
||||||
|
autostartCheckbox = new wxCheckBox(this, wxID_ANY, _("Launch at login"), wxDefaultPosition, wxDefaultSize, 0);
|
||||||
|
settingsContainer->Add(autostartCheckbox, 0, wxALL, 5);
|
||||||
|
|
||||||
|
mainContainer->Add(settingsContainer, 0, wxEXPAND, 5);
|
||||||
|
|
||||||
|
this->SetSizerAndFit(mainContainer);
|
||||||
|
|
||||||
|
wxSize currentSize = this->GetSize();
|
||||||
|
this->SetSize(size.GetWidth(), currentSize.GetHeight());
|
||||||
|
this->Layout();
|
||||||
|
|
||||||
|
this->Centre(wxBOTH);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
class PlayerLink : public wxApp {
|
||||||
public:
|
public:
|
||||||
virtual bool OnInit() override {
|
virtual bool OnInit() override {
|
||||||
|
if (wxSystemSettings::GetAppearance().IsSystemDark()) // To support the native dark mode on windows 10 and up
|
||||||
|
this->SetAppearance(wxAppBase::Appearance::Dark);
|
||||||
|
|
||||||
wxInitAllImageHandlers();
|
wxInitAllImageHandlers();
|
||||||
wxFrame* frame = new wxFrame(nullptr, wxID_ANY, "Hello wxWidgets", wxDefaultPosition, wxSize(400, 300));
|
PlayerLinkFrame* frame = new PlayerLinkFrame(nullptr, wxID_ANY, _("PlayerLink"));
|
||||||
trayIcon = new MyTaskBarIcon(frame);
|
trayIcon = new PlayerLinkIcon(frame);
|
||||||
frame->Bind(wxEVT_CLOSE_WINDOW, [=](wxCloseEvent& event) {
|
frame->Bind(wxEVT_CLOSE_WINDOW, [=](wxCloseEvent& event) {
|
||||||
trayIcon->RemoveIcon();
|
if (event.CanVeto()) {
|
||||||
trayIcon->Destroy();
|
frame->Hide();
|
||||||
event.Skip();
|
event.Veto();
|
||||||
|
} else
|
||||||
|
this->ExitMainLoop();
|
||||||
});
|
});
|
||||||
wxIcon icon = utils::loadIconFromMemory(icon_png, icon_png_size);
|
wxIcon icon = utils::loadIconFromMemory(icon_png, icon_png_size);
|
||||||
trayIcon->SetIcon(icon, "My App");
|
trayIcon->SetIcon(icon, _("PlayerLink"));
|
||||||
wxStaticText* text = new wxStaticText(frame, wxID_ANY, "Hello World", wxPoint(150, 130));
|
|
||||||
frame->Show(true);
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
MyTaskBarIcon* trayIcon;
|
PlayerLinkIcon* trayIcon;
|
||||||
};
|
};
|
||||||
|
|
||||||
wxIMPLEMENT_APP_NO_MAIN(MyApp);
|
wxIMPLEMENT_APP_NO_MAIN(PlayerLink);
|
||||||
|
|
||||||
int main(int argc, char** argv) {
|
int main(int argc, char** argv) {
|
||||||
std::thread rpcThread(handleRPCTasks);
|
std::thread rpcThread(handleRPCTasks);
|
||||||
|
|
|
@ -9,15 +9,24 @@
|
||||||
#include <nlohmann-json/single_include/nlohmann/json.hpp>
|
#include <nlohmann-json/single_include/nlohmann/json.hpp>
|
||||||
#include <sstream>
|
#include <sstream>
|
||||||
#include <string>
|
#include <string>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
#define DEFAULT_CLIENT_ID "1301849203378622545"
|
#define DEFAULT_CLIENT_ID "1301849203378622545"
|
||||||
#define DEFAULT_APP_NAME "Music"
|
#define DEFAULT_APP_NAME "Music"
|
||||||
#define CONFIG_FILENAME "known.json"
|
#define CONFIG_FILENAME "settings.json"
|
||||||
|
|
||||||
namespace utils {
|
namespace utils {
|
||||||
|
struct App {
|
||||||
|
bool enabled;
|
||||||
|
std::string appName;
|
||||||
|
std::string clientId;
|
||||||
|
std::string searchEndpoint;
|
||||||
|
std::vector<std::string> processNames;
|
||||||
|
};
|
||||||
|
|
||||||
inline wxIcon loadIconFromMemory(const unsigned char* data, size_t size) {
|
inline wxIcon loadIconFromMemory(const unsigned char* data, size_t size) {
|
||||||
wxMemoryInputStream stream(data, size);
|
wxMemoryInputStream stream(data, size);
|
||||||
wxImage img(stream, wxBITMAP_TYPE_PNG);
|
wxImage img(stream, wxBITMAP_TYPE_PNG);
|
||||||
if (img.IsOk()) {
|
if (img.IsOk()) {
|
||||||
wxBitmap bmp(img);
|
wxBitmap bmp(img);
|
||||||
wxIcon icon;
|
wxIcon icon;
|
||||||
|
@ -26,6 +35,7 @@ namespace utils {
|
||||||
}
|
}
|
||||||
return wxNullIcon;
|
return wxNullIcon;
|
||||||
}
|
}
|
||||||
|
|
||||||
inline std::string ltrim(std::string& s) {
|
inline std::string ltrim(std::string& s) {
|
||||||
s.erase(s.begin(), std::find_if(s.begin(), s.end(), [](unsigned char ch) { return !std::isspace(ch); }));
|
s.erase(s.begin(), std::find_if(s.begin(), s.end(), [](unsigned char ch) { return !std::isspace(ch); }));
|
||||||
return s;
|
return s;
|
||||||
|
@ -35,11 +45,13 @@ namespace utils {
|
||||||
s.erase(std::find_if(s.rbegin(), s.rend(), [](unsigned char ch) { return !std::isspace(ch); }).base(), s.end());
|
s.erase(std::find_if(s.rbegin(), s.rend(), [](unsigned char ch) { return !std::isspace(ch); }).base(), s.end());
|
||||||
return s;
|
return s;
|
||||||
}
|
}
|
||||||
|
|
||||||
inline std::string trim(std::string& s) {
|
inline std::string trim(std::string& s) {
|
||||||
ltrim(s);
|
ltrim(s);
|
||||||
rtrim(s);
|
rtrim(s);
|
||||||
return s;
|
return s;
|
||||||
}
|
}
|
||||||
|
|
||||||
inline std::string urlEncode(std::string str) {
|
inline std::string urlEncode(std::string str) {
|
||||||
std::string new_str = "";
|
std::string new_str = "";
|
||||||
char c;
|
char c;
|
||||||
|
@ -101,8 +113,12 @@ namespace utils {
|
||||||
return "";
|
return "";
|
||||||
}
|
}
|
||||||
|
|
||||||
inline nlohmann::json getApp(std::string processName) {
|
inline std::vector<App> getAllApps() {
|
||||||
std::ifstream i("known.json");
|
std::vector<App> results;
|
||||||
|
if (!std::filesystem::exists(CONFIG_FILENAME))
|
||||||
|
return results;
|
||||||
|
|
||||||
|
std::ifstream i(CONFIG_FILENAME);
|
||||||
std::stringstream s;
|
std::stringstream s;
|
||||||
s << i.rdbuf();
|
s << i.rdbuf();
|
||||||
i.close();
|
i.close();
|
||||||
|
@ -111,36 +127,49 @@ namespace utils {
|
||||||
nlohmann::json j = nlohmann::json::parse(s.str());
|
nlohmann::json j = nlohmann::json::parse(s.str());
|
||||||
auto apps = j["apps"];
|
auto apps = j["apps"];
|
||||||
for (auto app : apps) {
|
for (auto app : apps) {
|
||||||
|
App a;
|
||||||
|
a.appName = app["name"].get<std::string>();
|
||||||
|
a.clientId = app["client_id"].get<std::string>();
|
||||||
|
a.searchEndpoint = app["search_endpoint"].get<std::string>();
|
||||||
|
a.enabled = app["enabled"].get<bool>();
|
||||||
auto processNames = app["process_names"];
|
auto processNames = app["process_names"];
|
||||||
for (auto process : processNames) {
|
for (auto process : processNames) a.processNames.push_back(process.get<std::string>());
|
||||||
if (process.get<std::string>() == processName)
|
results.push_back(a);
|
||||||
return app;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
} catch (nlohmann::json::parse_error& ex) {
|
} catch (nlohmann::json::parse_error& ex) {
|
||||||
} // TODO: handle parse errors
|
} // TODO: handle parse errors
|
||||||
return nlohmann::json();
|
return results;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline App getApp(std::string processName) {
|
||||||
|
auto apps = getAllApps();
|
||||||
|
for (auto app : apps) {
|
||||||
|
for(auto procName : app.processNames) {
|
||||||
|
if(procName == processName)
|
||||||
|
return app;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
App a;
|
||||||
|
a.clientId = DEFAULT_CLIENT_ID;
|
||||||
|
a.appName = DEFAULT_APP_NAME;
|
||||||
|
a.enabled = true;
|
||||||
|
a.searchEndpoint = "";
|
||||||
|
return a;
|
||||||
}
|
}
|
||||||
|
|
||||||
inline std::string getClientID(std::string processName) {
|
inline std::string getClientID(std::string processName) {
|
||||||
if (!std::filesystem::exists(CONFIG_FILENAME))
|
|
||||||
return DEFAULT_CLIENT_ID;
|
|
||||||
auto app = getApp(processName);
|
auto app = getApp(processName);
|
||||||
return app.contains("client_id") ? app["client_id"].get<std::string>() : DEFAULT_CLIENT_ID;
|
return app.clientId;
|
||||||
}
|
}
|
||||||
|
|
||||||
inline std::string getAppName(std::string processName) {
|
inline std::string getAppName(std::string processName) {
|
||||||
if (!std::filesystem::exists(CONFIG_FILENAME))
|
|
||||||
return DEFAULT_APP_NAME;
|
|
||||||
auto app = getApp(processName);
|
auto app = getApp(processName);
|
||||||
return app.contains("name") ? app["name"].get<std::string>() : DEFAULT_APP_NAME;
|
return app.appName;
|
||||||
}
|
}
|
||||||
|
|
||||||
inline std::string getSearchEndpoint(std::string processName) {
|
inline std::string getSearchEndpoint(std::string processName) {
|
||||||
if (!std::filesystem::exists(CONFIG_FILENAME))
|
|
||||||
return "";
|
|
||||||
auto app = getApp(processName);
|
auto app = getApp(processName);
|
||||||
return app.contains("search_endpoint") ? app["search_endpoint"].get<std::string>() : "";
|
return app.searchEndpoint;
|
||||||
}
|
}
|
||||||
} // namespace utils
|
} // namespace utils
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue