From 2e2ee29cbbe9e5bfc35d01c7017d44692c68fdb0 Mon Sep 17 00:00:00 2001 From: casey langen Date: Fri, 9 Jun 2017 15:24:58 -0700 Subject: [PATCH] A couple changes to support "single instance" mode in win32. --- src/core/core.vcxproj.filters | 189 ++++++++++-------- src/musikbox/Main.cpp | 1 + src/musikbox/app/overlay/ServerOverlay.cpp | 57 +++--- src/musikbox/cursespp/App.cpp | 15 ++ src/musikbox/cursespp/App.h | 2 + src/musikbox/cursespp/Win32Util.cpp | 93 ++++++++- src/musikbox/cursespp/Win32Util.h | 3 + .../remote/playback/PlayerWrapper.kt | 2 +- 8 files changed, 238 insertions(+), 124 deletions(-) diff --git a/src/core/core.vcxproj.filters b/src/core/core.vcxproj.filters index 194f60891..c3e130f5b 100755 --- a/src/core/core.vcxproj.filters +++ b/src/core/core.vcxproj.filters @@ -44,6 +44,21 @@ {a8776591-b7ad-4f6f-a927-c4bfaaa1e8c8} + + {fe28fe15-6cf7-40e0-bec5-9ca46932d279} + + + {dd68aae0-9c01-4915-a4b3-c5e5b5ff8058} + + + {578b0af6-0dc3-4a69-a784-5e0293a5708a} + + + {a39a447a-65ad-465c-839d-b96cc4a8e2e9} + + + {d35af273-0f2b-4669-aa83-d7eee8c2c5c5} + @@ -201,30 +216,6 @@ src\audio - - src\sdk - - - src\sdk - - - src\sdk - - - src\sdk - - - src\sdk - - - src\sdk - - - src\sdk - - - src\sdk - src\io @@ -258,15 +249,9 @@ src\plugin - - src\sdk - src\sdk - - src\sdk - src @@ -294,36 +279,15 @@ src\support - - src\sdk - - - src\sdk - src\audio - - src\sdk - - - src\sdk - - - src\sdk - - - src\sdk - src src\sdk - - src\sdk - src\library\track @@ -366,21 +330,9 @@ src\library\track - - src\sdk - - - src\sdk - src\sdk - - src\sdk - - - src\sdk - src\library\query\local @@ -405,9 +357,6 @@ src\library\query\local - - src\sdk - src\library\query\local @@ -417,15 +366,6 @@ src\plugin - - src\sdk - - - src\sdk - - - src\sdk - src\library\metadata @@ -438,23 +378,98 @@ src\i18n - - src\sdk - - - src\sdk - src\library\query\local - - src\sdk - - - src\sdk - src\sdk + + src\sdk\audio + + + src\sdk\audio + + + src\sdk\audio + + + src\sdk\audio + + + src\sdk\audio + + + src\sdk\audio + + + src\sdk\io + + + src\sdk\io + + + src\sdk\vis + + + src\sdk\vis + + + src\sdk\vis + + + src\sdk\library + + + src\sdk\audio + + + src\sdk\library + + + src\sdk\audio + + + src\sdk\audio + + + src\sdk\indexer + + + src\sdk\indexer + + + src\sdk\indexer + + + src\sdk\library + + + src\sdk\library + + + src\sdk\library + + + src\sdk\library + + + src\sdk\library + + + src\sdk\library + + + src\sdk\library + + + src\sdk\library + + + src\sdk\library + + + src\sdk\library + \ No newline at end of file diff --git a/src/musikbox/Main.cpp b/src/musikbox/Main.cpp index 0cdfe11d6..755a44593 100644 --- a/src/musikbox/Main.cpp +++ b/src/musikbox/Main.cpp @@ -143,6 +143,7 @@ int main(int argc, char* argv[]) { #ifdef WIN32 app.SetIcon(IDI_ICON1); + app.SetSingleInstanceId("musikbox"); #endif /* fire up the indexer if configured to run on startup */ if (prefs->GetBool(musik::core::prefs::keys::SyncOnStartup, true)) { diff --git a/src/musikbox/app/overlay/ServerOverlay.cpp b/src/musikbox/app/overlay/ServerOverlay.cpp index 30e927bce..2fb6697b8 100644 --- a/src/musikbox/app/overlay/ServerOverlay.cpp +++ b/src/musikbox/app/overlay/ServerOverlay.cpp @@ -64,24 +64,27 @@ static const char* KEY_PASSWORD = "password"; #define DEFAULT_HEIGHT 17 #define DEFAULT_WIDTH 45 -#define STYLE_OVERLAY_LABEL(x) \ - x->SetContentColor(CURSESPP_OVERLAY_CONTENT); +static void applyLabelOverlayStyle(TextLabel& label) { + label.SetContentColor(CURSESPP_OVERLAY_CONTENT); +} -#define STYLE_OVERLAY_CHECKBOX(x) \ - x->SetContentColor(CURSESPP_OVERLAY_CONTENT); \ - x->SetFocusedContentColor(CURSESPP_OVERLAY_TEXT_FOCUSED); +static void applyCheckboxOverlayStyle(Checkbox& cb) { + cb.SetContentColor(CURSESPP_OVERLAY_CONTENT); + cb.SetFocusedContentColor(CURSESPP_OVERLAY_TEXT_FOCUSED); +} -#define STYLE_OVERLAY_INPUT(x) \ - if (x->GetStyle() == TextInput::StyleBox) { \ - x->SetFrameColor(CURSESPP_OVERLAY_FRAME); \ - x->SetContentColor(CURSESPP_OVERLAY_CONTENT); \ - x->SetFocusedFrameColor(CURSESPP_OVERLAY_INPUT_FRAME); \ - x->SetFocusedContentColor(CURSESPP_OVERLAY_CONTENT); \ - } \ - else { \ - x->SetContentColor(CURSESPP_OVERLAY_CONTENT); \ - x->SetFocusedContentColor(CURSESPP_OVERLAY_TEXT_FOCUSED); \ +static void applyInputOverlayStyle(TextInput& input) { + if (input.GetStyle() == TextInput::StyleBox) { + input.SetFrameColor(CURSESPP_OVERLAY_FRAME); + input.SetContentColor(CURSESPP_OVERLAY_CONTENT); + input.SetFocusedFrameColor(CURSESPP_OVERLAY_INPUT_FRAME); + input.SetFocusedContentColor(CURSESPP_OVERLAY_CONTENT); } + else { + input.SetContentColor(CURSESPP_OVERLAY_CONTENT); + input.SetFocusedContentColor(CURSESPP_OVERLAY_TEXT_FOCUSED); + } +} #define RIGHT(x) (x->GetX() + x->GetWidth()) #define TEXT_WIDTH(x) ((int) u8cols(x->GetText())) @@ -183,18 +186,18 @@ void ServerOverlay::InitViews() { this->pwInput.reset(new TextInput(TextInput::StyleLine, 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->transCacheLabel); - STYLE_OVERLAY_INPUT(this->transCacheInput); - STYLE_OVERLAY_LABEL(this->pwLabel); - STYLE_OVERLAY_INPUT(this->pwInput); + applyLabelOverlayStyle(*this->titleLabel); + applyCheckboxOverlayStyle(*this->enableWssCb); + applyLabelOverlayStyle(*this->wssPortLabel); + applyInputOverlayStyle(*this->wssPortInput); + applyCheckboxOverlayStyle(*this->enableHttpCb); + applyLabelOverlayStyle(*this->httpPortLabel); + applyInputOverlayStyle(*this->httpPortInput); + applyCheckboxOverlayStyle(*this->enableSyncTransCb); + applyLabelOverlayStyle(*this->transCacheLabel); + applyInputOverlayStyle(*this->transCacheInput); + applyLabelOverlayStyle(*this->pwLabel); + applyInputOverlayStyle(*this->pwInput); /* add 'em */ this->AddWindow(this->titleLabel); diff --git a/src/musikbox/cursespp/App.cpp b/src/musikbox/cursespp/App.cpp index 47939fd4b..4d1697e2e 100755 --- a/src/musikbox/cursespp/App.cpp +++ b/src/musikbox/cursespp/App.cpp @@ -137,6 +137,10 @@ void App::SetIcon(int resourceId) { win32::SetIcon(resourceId); } } + +void App::SetSingleInstanceId(const std::string& uniqueId) { + this->uniqueId = uniqueId; +} #endif void App::SetMinimizeToTray(bool minimizeToTray) { @@ -183,6 +187,17 @@ void App::OnResized() { } void App::Run(ILayoutPtr layout) { +#ifdef WIN32 + if (this->uniqueId.size()) { + win32::EnableSingleInstance(uniqueId); + + if (win32::AlreadyRunning()) { + win32::ShowOtherInstance(); + return; + } + } +#endif + Colors::Init(this->colorMode); if (this->colorTheme.size()) { diff --git a/src/musikbox/cursespp/App.h b/src/musikbox/cursespp/App.h index d1c901b39..af2f801c2 100755 --- a/src/musikbox/cursespp/App.h +++ b/src/musikbox/cursespp/App.h @@ -62,6 +62,7 @@ namespace cursespp { #ifdef WIN32 void SetIcon(int resourceId); + void SetSingleInstanceId(const std::string& uniqueId); #endif void Run(ILayoutPtr layout); @@ -110,6 +111,7 @@ namespace cursespp { #ifdef WIN32 int iconId; + std::string uniqueId; #endif }; } diff --git a/src/musikbox/cursespp/Win32Util.cpp b/src/musikbox/cursespp/Win32Util.cpp index 5cd79843a..56cdfb9ef 100644 --- a/src/musikbox/cursespp/Win32Util.cpp +++ b/src/musikbox/cursespp/Win32Util.cpp @@ -35,6 +35,7 @@ #pragma once #include "stdafx.h" +#include "Win32Util.h" #include #include #include @@ -42,6 +43,7 @@ #ifdef WIN32 #define WM_TRAYICON (WM_USER + 2000) +#define WM_SHOW_OTHER_INSTANCE (WM_USER + 2001) static std::basic_string className = L"Curses_App"; static HWND mainWindow = nullptr; @@ -50,8 +52,10 @@ static std::unique_ptr trayIcon; static bool minimizeToTray = false, minimizedToTray = false; static std::string appTitle; static HICON icon16 = nullptr, icon32 = nullptr; +static HANDLE runningMutex; +static DWORD runningMutexLastError = 0; -static void findMainWindow() { +static HWND findThisProcessMainWindow() { static TCHAR buffer[256]; if (mainWindow == nullptr) { @@ -64,12 +68,39 @@ static void findMainWindow() { GetClassName(hWnd, buffer, sizeof(buffer)); if (className == std::basic_string(buffer)) { mainWindow = hWnd; - return; + return hWnd; } } hWnd = GetNextWindow(hWnd, GW_HWNDNEXT); } } + + return nullptr; +} + +static HWND findOtherProcessMainWindow() { + static TCHAR buffer[256]; + + DWORD dwProcID = GetCurrentProcessId(); + HWND hWnd = GetTopWindow(GetDesktopWindow()); + + while (hWnd) { + DWORD dwWndProcID = 0; + GetWindowThreadProcessId(hWnd, &dwWndProcID); + if (dwWndProcID != dwProcID) { /* not in this process */ + GetClassName(hWnd, buffer, sizeof(buffer)); + if (className == std::basic_string(buffer)) { + ::GetWindowText(hWnd, buffer, sizeof(buffer)); + if (appTitle == u16to8(buffer)) { /* title must match*/ + return hWnd; + } + } + } + + hWnd = GetNextWindow(hWnd, GW_HWNDNEXT); + } + + return nullptr; } static HICON loadIcon(int resourceId, int size) { @@ -97,6 +128,19 @@ static void initTrayIcon(HWND hwnd) { } } +static void restoreFromTray(HWND hwnd) { + Shell_NotifyIcon(NIM_DELETE, trayIcon.get()); + minimizedToTray = false; + ShowWindow(hwnd, SW_SHOWNORMAL); + +} + +static void resetMutex() { + CloseHandle(runningMutex); + runningMutex = nullptr; + runningMutexLastError = 0; +} + static LRESULT CALLBACK wndProc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam, UINT_PTR id, DWORD_PTR data) { if (minimizeToTray) { if ((msg == WM_SIZE && wparam == SIZE_MINIMIZED) || @@ -117,41 +161,49 @@ static LRESULT CALLBACK wndProc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lpara if (msg == WM_TRAYICON) { if (LOWORD(lparam) == WM_LBUTTONUP) { - Shell_NotifyIcon(NIM_DELETE, trayIcon.get()); - minimizedToTray = false; - ShowWindow(hwnd, SW_SHOWNORMAL); + restoreFromTray(hwnd); return 1; } } + if (msg == WM_SHOW_OTHER_INSTANCE) { + cursespp::win32::ShowMainWindow(); + restoreFromTray(hwnd); + return 1; + } + + if (msg == WM_QUIT) { + resetMutex(); + } + return DefSubclassProc(hwnd, msg, wparam, lparam); } namespace cursespp { namespace win32 { void ShowMainWindow() { - findMainWindow(); + findThisProcessMainWindow(); if (mainWindow) { ShowWindow(mainWindow, SW_SHOWNORMAL); } } void HideMainWindow() { - findMainWindow(); + findThisProcessMainWindow(); if (mainWindow) { ShowWindow(mainWindow, SW_HIDE); } } void Minimize() { - findMainWindow(); + findThisProcessMainWindow(); if (mainWindow) { ShowWindow(mainWindow, SW_SHOWMINIMIZED); } } HWND GetMainWindow() { - findMainWindow(); + findThisProcessMainWindow(); return mainWindow; } @@ -177,6 +229,29 @@ namespace cursespp { void SetAppTitle(const std::string& title) { appTitle = title; } + + bool AlreadyRunning() { + return !IsDebuggerPresent() && (runningMutexLastError == ERROR_ALREADY_EXISTS); + } + + void ShowOtherInstance() { + HWND otherHwnd = findOtherProcessMainWindow(); + if (otherHwnd) { + SendMessage(otherHwnd, WM_SHOW_OTHER_INSTANCE, 0, 0); + ::SetForegroundWindow(otherHwnd); + } + } + + void EnableSingleInstance(const std::string& uniqueId) { + if (uniqueId.size() && !runningMutex) { + std::string mutexName = "cursespp::" + uniqueId; + runningMutex = CreateMutexA(nullptr, false, mutexName.c_str()); + runningMutexLastError = GetLastError(); + } + else { + resetMutex(); + } + } } } diff --git a/src/musikbox/cursespp/Win32Util.h b/src/musikbox/cursespp/Win32Util.h index 5e12a64d5..0615a63fc 100644 --- a/src/musikbox/cursespp/Win32Util.h +++ b/src/musikbox/cursespp/Win32Util.h @@ -48,6 +48,9 @@ namespace cursespp { void SetIcon(int resourceId); void SetAppTitle(const std::string& title); void SetMinimizeToTray(bool enabled); + void EnableSingleInstance(const std::string& uniqueId); + bool AlreadyRunning(); + void ShowOtherInstance(); } } diff --git a/src/musikdroid/app/src/main/java/io/casey/musikcube/remote/playback/PlayerWrapper.kt b/src/musikdroid/app/src/main/java/io/casey/musikcube/remote/playback/PlayerWrapper.kt index b54eb4cf3..0d6cf03f6 100644 --- a/src/musikdroid/app/src/main/java/io/casey/musikcube/remote/playback/PlayerWrapper.kt +++ b/src/musikdroid/app/src/main/java/io/casey/musikcube/remote/playback/PlayerWrapper.kt @@ -95,7 +95,7 @@ abstract class PlayerWrapper { if (preDuckGlobalVolume != DUCK_NONE) { preDuckGlobalVolume = volume - volume = volume * DUCK_COEF + volume *= DUCK_COEF } if (volume != globalVolume) {