A couple changes to support "single instance" mode in win32.

This commit is contained in:
casey langen 2017-06-09 15:24:58 -07:00
parent a60f33fa19
commit 2e2ee29cbb
8 changed files with 238 additions and 124 deletions

View File

@ -44,6 +44,21 @@
<Filter Include="src\i18n">
<UniqueIdentifier>{a8776591-b7ad-4f6f-a927-c4bfaaa1e8c8}</UniqueIdentifier>
</Filter>
<Filter Include="src\sdk\audio">
<UniqueIdentifier>{fe28fe15-6cf7-40e0-bec5-9ca46932d279}</UniqueIdentifier>
</Filter>
<Filter Include="src\sdk\vis">
<UniqueIdentifier>{dd68aae0-9c01-4915-a4b3-c5e5b5ff8058}</UniqueIdentifier>
</Filter>
<Filter Include="src\sdk\io">
<UniqueIdentifier>{578b0af6-0dc3-4a69-a784-5e0293a5708a}</UniqueIdentifier>
</Filter>
<Filter Include="src\sdk\library">
<UniqueIdentifier>{a39a447a-65ad-465c-839d-b96cc4a8e2e9}</UniqueIdentifier>
</Filter>
<Filter Include="src\sdk\indexer">
<UniqueIdentifier>{d35af273-0f2b-4669-aa83-d7eee8c2c5c5}</UniqueIdentifier>
</Filter>
</ItemGroup>
<ItemGroup>
<ClCompile Include="pch.cpp">
@ -201,30 +216,6 @@
<ClInclude Include="audio\Player.h">
<Filter>src\audio</Filter>
</ClInclude>
<ClInclude Include="sdk\IDecoder.h">
<Filter>src\sdk</Filter>
</ClInclude>
<ClInclude Include="sdk\IDecoderFactory.h">
<Filter>src\sdk</Filter>
</ClInclude>
<ClInclude Include="sdk\IDSP.h">
<Filter>src\sdk</Filter>
</ClInclude>
<ClInclude Include="sdk\IOutput.h">
<Filter>src\sdk</Filter>
</ClInclude>
<ClInclude Include="sdk\IAnalyzer.h">
<Filter>src\sdk</Filter>
</ClInclude>
<ClInclude Include="sdk\IBuffer.h">
<Filter>src\sdk</Filter>
</ClInclude>
<ClInclude Include="sdk\IDataStream.h">
<Filter>src\sdk</Filter>
</ClInclude>
<ClInclude Include="sdk\IDataStreamFactory.h">
<Filter>src\sdk</Filter>
</ClInclude>
<ClInclude Include="io\LocalFileStream.h">
<Filter>src\io</Filter>
</ClInclude>
@ -258,15 +249,9 @@
<ClInclude Include="plugin\PluginFactory.h">
<Filter>src\plugin</Filter>
</ClInclude>
<ClInclude Include="sdk\IMetadataReader.h">
<Filter>src\sdk</Filter>
</ClInclude>
<ClInclude Include="sdk\IPlugin.h">
<Filter>src\sdk</Filter>
</ClInclude>
<ClInclude Include="sdk\IBufferProvider.h">
<Filter>src\sdk</Filter>
</ClInclude>
<ClInclude Include="debug.h">
<Filter>src</Filter>
</ClInclude>
@ -294,36 +279,15 @@
<ClInclude Include="support\PreferenceKeys.h">
<Filter>src\support</Filter>
</ClInclude>
<ClInclude Include="sdk\IPcmVisualizer.h">
<Filter>src\sdk</Filter>
</ClInclude>
<ClInclude Include="sdk\ISpectrumVisualizer.h">
<Filter>src\sdk</Filter>
</ClInclude>
<ClInclude Include="audio\Visualizer.h">
<Filter>src\audio</Filter>
</ClInclude>
<ClInclude Include="sdk\IVisualizer.h">
<Filter>src\sdk</Filter>
</ClInclude>
<ClInclude Include="sdk\IPlaybackRemote.h">
<Filter>src\sdk</Filter>
</ClInclude>
<ClInclude Include="sdk\IPlaybackService.h">
<Filter>src\sdk</Filter>
</ClInclude>
<ClInclude Include="sdk\ITrack.h">
<Filter>src\sdk</Filter>
</ClInclude>
<ClInclude Include="utfutil.h">
<Filter>src</Filter>
</ClInclude>
<ClInclude Include="sdk\constants.h">
<Filter>src\sdk</Filter>
</ClInclude>
<ClInclude Include="sdk\IRetainedTrack.h">
<Filter>src\sdk</Filter>
</ClInclude>
<ClInclude Include="library\track\RetainedTrack.h">
<Filter>src\library\track</Filter>
</ClInclude>
@ -366,21 +330,9 @@
<ClInclude Include="library\track\TrackList.h">
<Filter>src\library\track</Filter>
</ClInclude>
<ClInclude Include="sdk\ITrackListEditor.h">
<Filter>src\sdk</Filter>
</ClInclude>
<ClInclude Include="sdk\ITrackList.h">
<Filter>src\sdk</Filter>
</ClInclude>
<ClInclude Include="sdk\IPreferences.h">
<Filter>src\sdk</Filter>
</ClInclude>
<ClInclude Include="sdk\IMetadataValue.h">
<Filter>src\sdk</Filter>
</ClInclude>
<ClInclude Include="sdk\IMetadataValueList.h">
<Filter>src\sdk</Filter>
</ClInclude>
<ClInclude Include="library\query\local\CategoryTrackListQuery.h">
<Filter>src\library\query\local</Filter>
</ClInclude>
@ -405,9 +357,6 @@
<ClInclude Include="library\query\local\CategoryListQuery.h">
<Filter>src\library\query\local</Filter>
</ClInclude>
<ClInclude Include="sdk\ISimpleDataProvider.h">
<Filter>src\sdk</Filter>
</ClInclude>
<ClInclude Include="Library\query\local\LocalQueryBase.h">
<Filter>src\library\query\local</Filter>
</ClInclude>
@ -417,15 +366,6 @@
<ClInclude Include="plugin\Plugins.h">
<Filter>src\plugin</Filter>
</ClInclude>
<ClInclude Include="sdk\IMetadataMapList.h">
<Filter>src\sdk</Filter>
</ClInclude>
<ClInclude Include="sdk\IMetadataMap.h">
<Filter>src\sdk</Filter>
</ClInclude>
<ClInclude Include="sdk\ITrackWriter.h">
<Filter>src\sdk</Filter>
</ClInclude>
<ClInclude Include="library\metadata\MetadataMapList.h">
<Filter>src\library\metadata</Filter>
</ClInclude>
@ -438,23 +378,98 @@
<ClInclude Include="i18n\Locale.h">
<Filter>src\i18n</Filter>
</ClInclude>
<ClInclude Include="sdk\IIndexerSource.h">
<Filter>src\sdk</Filter>
</ClInclude>
<ClInclude Include="sdk\IRetainedTrackWriter.h">
<Filter>src\sdk</Filter>
</ClInclude>
<ClInclude Include="library\query\local\TrackMetadataQuery.h">
<Filter>src\library\query\local</Filter>
</ClInclude>
<ClInclude Include="sdk\IIndexerWriter.h">
<Filter>src\sdk</Filter>
</ClInclude>
<ClInclude Include="sdk\IIndexerNotifier.h">
<Filter>src\sdk</Filter>
</ClInclude>
<ClInclude Include="sdk\IEnvironment.h">
<Filter>src\sdk</Filter>
</ClInclude>
<ClInclude Include="sdk\IDSP.h">
<Filter>src\sdk\audio</Filter>
</ClInclude>
<ClInclude Include="sdk\IDecoderFactory.h">
<Filter>src\sdk\audio</Filter>
</ClInclude>
<ClInclude Include="sdk\IDecoder.h">
<Filter>src\sdk\audio</Filter>
</ClInclude>
<ClInclude Include="sdk\IOutput.h">
<Filter>src\sdk\audio</Filter>
</ClInclude>
<ClInclude Include="sdk\IBuffer.h">
<Filter>src\sdk\audio</Filter>
</ClInclude>
<ClInclude Include="sdk\IBufferProvider.h">
<Filter>src\sdk\audio</Filter>
</ClInclude>
<ClInclude Include="sdk\IDataStreamFactory.h">
<Filter>src\sdk\io</Filter>
</ClInclude>
<ClInclude Include="sdk\IDataStream.h">
<Filter>src\sdk\io</Filter>
</ClInclude>
<ClInclude Include="sdk\IVisualizer.h">
<Filter>src\sdk\vis</Filter>
</ClInclude>
<ClInclude Include="sdk\IPcmVisualizer.h">
<Filter>src\sdk\vis</Filter>
</ClInclude>
<ClInclude Include="sdk\ISpectrumVisualizer.h">
<Filter>src\sdk\vis</Filter>
</ClInclude>
<ClInclude Include="sdk\ISimpleDataProvider.h">
<Filter>src\sdk\library</Filter>
</ClInclude>
<ClInclude Include="sdk\IPlaybackRemote.h">
<Filter>src\sdk\audio</Filter>
</ClInclude>
<ClInclude Include="sdk\IMetadataReader.h">
<Filter>src\sdk\library</Filter>
</ClInclude>
<ClInclude Include="sdk\IPlaybackService.h">
<Filter>src\sdk\audio</Filter>
</ClInclude>
<ClInclude Include="sdk\IAnalyzer.h">
<Filter>src\sdk\audio</Filter>
</ClInclude>
<ClInclude Include="sdk\IIndexerNotifier.h">
<Filter>src\sdk\indexer</Filter>
</ClInclude>
<ClInclude Include="sdk\IIndexerSource.h">
<Filter>src\sdk\indexer</Filter>
</ClInclude>
<ClInclude Include="sdk\IIndexerWriter.h">
<Filter>src\sdk\indexer</Filter>
</ClInclude>
<ClInclude Include="sdk\ITrackWriter.h">
<Filter>src\sdk\library</Filter>
</ClInclude>
<ClInclude Include="sdk\ITrackListEditor.h">
<Filter>src\sdk\library</Filter>
</ClInclude>
<ClInclude Include="sdk\ITrackList.h">
<Filter>src\sdk\library</Filter>
</ClInclude>
<ClInclude Include="sdk\ITrack.h">
<Filter>src\sdk\library</Filter>
</ClInclude>
<ClInclude Include="sdk\IRetainedTrackWriter.h">
<Filter>src\sdk\library</Filter>
</ClInclude>
<ClInclude Include="sdk\IRetainedTrack.h">
<Filter>src\sdk\library</Filter>
</ClInclude>
<ClInclude Include="sdk\IMetadataMap.h">
<Filter>src\sdk\library</Filter>
</ClInclude>
<ClInclude Include="sdk\IMetadataMapList.h">
<Filter>src\sdk\library</Filter>
</ClInclude>
<ClInclude Include="sdk\IMetadataValue.h">
<Filter>src\sdk\library</Filter>
</ClInclude>
<ClInclude Include="sdk\IMetadataValueList.h">
<Filter>src\sdk\library</Filter>
</ClInclude>
</ItemGroup>
</Project>

View File

@ -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)) {

View File

@ -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);

View File

@ -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()) {

View File

@ -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
};
}

View File

@ -35,6 +35,7 @@
#pragma once
#include "stdafx.h"
#include "Win32Util.h"
#include <Windows.h>
#include <Commctrl.h>
#include <shellapi.h>
@ -42,6 +43,7 @@
#ifdef WIN32
#define WM_TRAYICON (WM_USER + 2000)
#define WM_SHOW_OTHER_INSTANCE (WM_USER + 2001)
static std::basic_string<TCHAR> className = L"Curses_App";
static HWND mainWindow = nullptr;
@ -50,8 +52,10 @@ static std::unique_ptr<NOTIFYICONDATA> 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<TCHAR>(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<TCHAR>(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();
}
}
}
}

View File

@ -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();
}
}

View File

@ -95,7 +95,7 @@ abstract class PlayerWrapper {
if (preDuckGlobalVolume != DUCK_NONE) {
preDuckGlobalVolume = volume
volume = volume * DUCK_COEF
volume *= DUCK_COEF
}
if (volume != globalVolume) {