diff --git a/client/CMT.cpp b/client/CMT.cpp index eec7874f6..79fe2b734 100644 --- a/client/CMT.cpp +++ b/client/CMT.cpp @@ -59,6 +59,7 @@ #include "gui/CAnimation.h" #include "../lib/serializer/Connection.h" #include "CServerHandler.h" +#include "gui/NotificationHandler.h" #include @@ -1225,6 +1226,11 @@ static bool recreateWindow(int w, int h, int bpp, bool fullscreen, int displayIn SDL_RenderClear(mainRenderer); SDL_RenderPresent(mainRenderer); + if(!settings["session"]["headless"].Bool() && settings["general"]["notifications"].Bool()) + { + NotificationHandler::init(mainWindow); + } + return true; } @@ -1355,6 +1361,13 @@ static void handleEvent(SDL_Event & ev) } return; } + else if(ev.type == SDL_SYSWMEVENT) + { + if(!settings["session"]["headless"].Bool() && settings["general"]["notifications"].Bool()) + { + NotificationHandler::handleSdlEvent(ev); + } + } //preprocessing if(ev.type == SDL_MOUSEMOTION) @@ -1430,6 +1443,11 @@ void handleQuit(bool ask) boost::this_thread::sleep(boost::posix_time::milliseconds(750));//??? if(!settings["session"]["headless"].Bool()) { + if(settings["general"]["notifications"].Bool()) + { + NotificationHandler::destroy(); + } + cleanupRenderer(); if(nullptr != mainRenderer) diff --git a/client/CMakeLists.txt b/client/CMakeLists.txt index f6fc03a77..3b436ff3e 100644 --- a/client/CMakeLists.txt +++ b/client/CMakeLists.txt @@ -14,6 +14,7 @@ set(client_SRCS gui/Fonts.cpp gui/Geometries.cpp gui/SDL_Extensions.cpp + gui/NotificationHandler.cpp widgets/AdventureMapClasses.cpp widgets/Buttons.cpp @@ -88,6 +89,7 @@ set(client_HEADERS gui/SDL_Compat.h gui/SDL_Extensions.h gui/SDL_Pixels.h + gui/NotificationHandler.h widgets/AdventureMapClasses.h widgets/Buttons.h diff --git a/client/CPlayerInterface.cpp b/client/CPlayerInterface.cpp index e2bc22279..a5f00ff1a 100644 --- a/client/CPlayerInterface.cpp +++ b/client/CPlayerInterface.cpp @@ -62,6 +62,7 @@ #include "CServerHandler.h" // FIXME: only needed for CGameState::mutex #include "../lib/CGameState.h" +#include "gui/NotificationHandler.h" // The macro below is used to mark functions that are called by client when game state changes. @@ -166,6 +167,8 @@ void CPlayerInterface::yourTurn() GH.curInt = this; adventureInt->selection = nullptr; + NotificationHandler::notify("Your turn"); + std::string prefix = settings["session"]["saveprefix"].String(); int frequency = static_cast(settings["general"]["saveFrequency"].Integer()); if (firstCall) diff --git a/client/gui/NotificationHandler.cpp b/client/gui/NotificationHandler.cpp new file mode 100644 index 000000000..3f4d5027f --- /dev/null +++ b/client/gui/NotificationHandler.cpp @@ -0,0 +1,159 @@ +/* +* NotificationHandler.cpp, part of VCMI engine +* +* Authors: listed in file AUTHORS in main folder +* +* License: GNU General Public License v2.0 or later +* Full text of license available in license.txt file, in main folder +* +*/ + +#include "StdInc.h" +#include "NotificationHandler.h" +#include +#include + +#if defined(VCMI_WINDOWS) && defined(_WIN64) + +#define WIN32_LEAN_AND_MEAN // Exclude rarely-used stuff from Windows headers +// Windows Header Files: +#include +#include +// C RunTime Header Files + +#define WM_USER_SHELLICON WM_USER + 1 + +// Global Variables: + +struct NotificationState +{ + HINSTANCE hInst; // current instance + NOTIFYICONDATA niData; // notify icon data + bool initialized = false; + SDL_Window * window; +}; + +NotificationState state; + +void NotificationHandler::notify(std::string msg) +{ + NOTIFYICONDATA niData; + SDL_SysWMinfo info; + SDL_VERSION(&info.version); + + if(!SDL_GetWindowWMInfo(state.window, &info)) + return; + + if(info.info.win.window == GetForegroundWindow()) + return; + + ZeroMemory(&niData, sizeof(NOTIFYICONDATA)); + + niData.cbSize = sizeof(NOTIFYICONDATA); + niData.hWnd = info.info.win.window; + niData.uID = 1; + niData.uFlags = NIF_INFO | NIF_MESSAGE; + niData.uCallbackMessage = WM_USER_SHELLICON; + + niData.dwInfoFlags = NIIF_INFO; + msg.copy(niData.szInfo, msg.length()); + + Shell_NotifyIcon(NIM_MODIFY, &niData); +} + +void NotificationHandler::init(SDL_Window * window) +{ + state.window = window; + + if(state.initialized) + return; + + SDL_EventState(SDL_SYSWMEVENT, SDL_ENABLE); + + NOTIFYICONDATA niData; + SDL_SysWMinfo info; + SDL_VERSION(&info.version); + + if(!SDL_GetWindowWMInfo(state.window, &info)) + return; + + ZeroMemory(&niData, sizeof(NOTIFYICONDATA)); + + state.hInst = (HINSTANCE)GetModuleHandle("VCMI_client.exe"); + + niData.cbSize = sizeof(NOTIFYICONDATA); + niData.hWnd = info.info.win.window; + niData.uID = 1; + niData.uFlags = NIF_ICON | NIF_MESSAGE; + niData.uCallbackMessage = WM_USER_SHELLICON; + + niData.hIcon = (HICON)LoadImage( + state.hInst, + "IDI_ICON1", + IMAGE_ICON, + GetSystemMetrics(SM_CXSMICON), + GetSystemMetrics(SM_CYSMICON), + LR_DEFAULTSIZE); + + Shell_NotifyIcon(NIM_ADD, &niData); + + state.initialized = true; +} + +void NotificationHandler::destroy() +{ + NOTIFYICONDATA niData; + SDL_SysWMinfo info; + SDL_VERSION(&info.version); + + if(!SDL_GetWindowWMInfo(state.window, &info)) + return; + + ZeroMemory(&niData, sizeof(NOTIFYICONDATA)); + + niData.cbSize = sizeof(NOTIFYICONDATA); + niData.hWnd = info.info.win.window; + niData.uID = 1; + + Shell_NotifyIcon(NIM_DELETE, &niData); +} + +bool NotificationHandler::handleSdlEvent(const SDL_Event & ev) +{ + if(ev.syswm.msg->msg.win.msg == WM_USER_SHELLICON) + { + auto winMsg = LOWORD(ev.syswm.msg->msg.win.lParam); + + if(winMsg == WM_LBUTTONUP || winMsg == NIN_BALLOONUSERCLICK) + { + SDL_MinimizeWindow(state.window); + SDL_RestoreWindow(state.window); + SDL_RaiseWindow(state.window); + + return true; + } + } + + return false; +} + +#else + +void NotificationHandler::notify(std::string msg) +{ +} + +void NotificationHandler::init(SDL_Window * window) +{ +} + +void NotificationHandler::destroy() +{ +} + +bool NotificationHandler::handleSdlEvent(const SDL_Event & ev) +{ + return false; +} + +#endif \ No newline at end of file diff --git a/client/gui/NotificationHandler.h b/client/gui/NotificationHandler.h new file mode 100644 index 000000000..a1ac262a7 --- /dev/null +++ b/client/gui/NotificationHandler.h @@ -0,0 +1,22 @@ +/* +* NotificationHandler.cpp, part of VCMI engine +* +* Authors: listed in file AUTHORS in main folder +* +* License: GNU General Public License v2.0 or later +* Full text of license available in license.txt file, in main folder +* +*/ + +#pragma once + +#include + +class NotificationHandler +{ +public: + static void notify(std::string msg); + static void init(SDL_Window * window); + static bool handleSdlEvent(const SDL_Event & ev); + static void destroy(); +}; \ No newline at end of file diff --git a/config/schemas/settings.json b/config/schemas/settings.json index 4c51bdc75..4488317be 100644 --- a/config/schemas/settings.json +++ b/config/schemas/settings.json @@ -17,7 +17,7 @@ "type" : "object", "default": {}, "additionalProperties" : false, - "required" : [ "playerName", "showfps", "music", "sound", "encoding", "swipe", "saveRandomMaps", "saveFrequency" ], + "required" : [ "playerName", "showfps", "music", "sound", "encoding", "swipe", "saveRandomMaps", "saveFrequency", "notifications" ], "properties" : { "playerName" : { "type":"string", @@ -62,6 +62,10 @@ "saveFrequency" : { "type" : "number", "default" : 1 + }, + "notifications" : { + "type" : "boolean", + "default" : false } } },