Implementation of notifications for Windows OS.
This commit is contained in:
parent
e827728e78
commit
2fc758377a
7 changed files with 297 additions and 3 deletions
|
@ -1,6 +1,8 @@
|
|||
Version 1.11.4+dev:
|
||||
* Language and i18n:
|
||||
* Updated translations:
|
||||
* User interface:
|
||||
* Added notification support for Windows
|
||||
|
||||
Version 1.11.4:
|
||||
* AI:
|
||||
|
|
|
@ -5,6 +5,8 @@ changelog: https://github.com/wesnoth/wesnoth-old/blob/master/changelog
|
|||
Version 1.11.4+dev:
|
||||
* Language and i18n:
|
||||
* Updated translations:
|
||||
* User interface:
|
||||
* Added notification support for Windows
|
||||
|
||||
|
||||
Version 1.11.4:
|
||||
|
|
|
@ -21,6 +21,9 @@
|
|||
#include "preferences_display.hpp"
|
||||
#include "sound.hpp"
|
||||
#include "video.hpp"
|
||||
#if defined _WIN32
|
||||
#include "windows_tray_notification.hpp"
|
||||
#endif
|
||||
|
||||
#include "SDL.h"
|
||||
|
||||
|
@ -362,6 +365,13 @@ void pump()
|
|||
}
|
||||
#endif
|
||||
|
||||
#if defined _WIN32
|
||||
case SDL_SYSWMEVENT: {
|
||||
windows_tray_notification::handle_system_event(event);
|
||||
break;
|
||||
}
|
||||
#endif
|
||||
|
||||
case SDL_QUIT: {
|
||||
throw CVideo::quit();
|
||||
}
|
||||
|
|
|
@ -490,7 +490,7 @@ static int do_gameloop(int argc, char** argv)
|
|||
loadscreen::start_stage("refresh addons");
|
||||
refresh_addon_version_info_cache();
|
||||
|
||||
#if defined(_X11) && !defined(__APPLE__)
|
||||
#if (defined(_X11) && !defined(__APPLE__)) || defined(_WIN32)
|
||||
SDL_EventState(SDL_SYSWMEVENT, SDL_ENABLE);
|
||||
#endif
|
||||
|
||||
|
|
|
@ -44,6 +44,9 @@ Growl_Delegate growl_obj;
|
|||
#include "tod_manager.hpp"
|
||||
#include "sound.hpp"
|
||||
#include "whiteboard/manager.hpp"
|
||||
#ifdef _WIN32
|
||||
#include "windows_tray_notification.hpp"
|
||||
#endif
|
||||
|
||||
#include <boost/foreach.hpp>
|
||||
|
||||
|
@ -973,13 +976,13 @@ static uint32_t send_dbus_notification(DBusConnection *connection, uint32_t repl
|
|||
}
|
||||
#endif
|
||||
|
||||
#if defined(HAVE_LIBDBUS) || defined(HAVE_GROWL)
|
||||
#if defined(HAVE_LIBDBUS) || defined(HAVE_GROWL) || defined(_WIN32)
|
||||
void game_display::send_notification(const std::string& owner, const std::string& message)
|
||||
#else
|
||||
void game_display::send_notification(const std::string& /*owner*/, const std::string& /*message*/)
|
||||
#endif
|
||||
{
|
||||
#if defined(HAVE_LIBDBUS) || defined(HAVE_GROWL)
|
||||
#if defined(HAVE_LIBDBUS) || defined(HAVE_GROWL) || defined(_WIN32)
|
||||
Uint8 app_state = SDL_GetAppState();
|
||||
|
||||
// Do not show notifications when the window is visible...
|
||||
|
@ -1038,6 +1041,21 @@ void game_display::send_notification(const std::string& /*owner*/, const std::st
|
|||
CFRelease(cf_message);
|
||||
CFRelease(cf_note_name);
|
||||
#endif
|
||||
|
||||
#ifdef _WIN32
|
||||
std::string notification_title;
|
||||
std::string notification_message;
|
||||
|
||||
if (owner == "Turn changed") {
|
||||
notification_title = owner;
|
||||
notification_message = message;
|
||||
} else {
|
||||
notification_title = "Chat message";
|
||||
notification_message = owner + ": " + message;
|
||||
}
|
||||
|
||||
windows_tray_notification::show(notification_title, notification_message);
|
||||
#endif
|
||||
}
|
||||
|
||||
void game_display::set_team(size_t teamindex, bool show_everything)
|
||||
|
|
193
src/windows_tray_notification.cpp
Normal file
193
src/windows_tray_notification.cpp
Normal file
|
@ -0,0 +1,193 @@
|
|||
#include "windows_tray_notification.hpp"
|
||||
|
||||
#include <SDL_syswm.h>
|
||||
|
||||
#include "serialization/string_utils.hpp"
|
||||
|
||||
NOTIFYICONDATA* windows_tray_notification::nid = NULL;
|
||||
bool windows_tray_notification::message_reset = false;
|
||||
|
||||
void windows_tray_notification::destroy_tray_icon()
|
||||
{
|
||||
if (nid == NULL) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!message_reset){
|
||||
Shell_NotifyIcon(NIM_DELETE, nid);
|
||||
delete nid;
|
||||
nid = NULL;
|
||||
} else {
|
||||
message_reset = false;
|
||||
}
|
||||
}
|
||||
|
||||
void windows_tray_notification::handle_system_event(const SDL_Event& event)
|
||||
{
|
||||
if (event.syswm.msg->msg != WM_TRAYNOTIFY) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (event.syswm.msg->lParam == NIN_BALLOONUSERCLICK) {
|
||||
switch_to_wesnoth_window();
|
||||
destroy_tray_icon();
|
||||
} else if (event.syswm.msg->lParam == NIN_BALLOONTIMEOUT) {
|
||||
destroy_tray_icon();
|
||||
}
|
||||
}
|
||||
|
||||
bool windows_tray_notification::create_tray_icon()
|
||||
{
|
||||
// getting hanlde to a 32x32 icon, contained in "WESNOTH_ICON" icon group of wesnoth.exe resources
|
||||
const HMODULE wesnoth_exe = GetModuleHandle(NULL);
|
||||
if (wesnoth_exe == NULL) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const HRSRC group_icon_info = FindResource(wesnoth_exe, L"WESNOTH_ICON", RT_GROUP_ICON);
|
||||
if (group_icon_info == NULL) {
|
||||
return false;
|
||||
}
|
||||
|
||||
HGLOBAL hGlobal = LoadResource(wesnoth_exe, group_icon_info);
|
||||
if (hGlobal == NULL) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const PBYTE group_icon_res = static_cast<PBYTE>(LockResource(hGlobal));
|
||||
if (group_icon_res == NULL) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const int nID = LookupIconIdFromDirectoryEx(group_icon_res, TRUE, 32, 32, LR_DEFAULTCOLOR);
|
||||
if (nID == 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const HRSRC icon_info = FindResource(wesnoth_exe, MAKEINTRESOURCE(nID), MAKEINTRESOURCE(3));
|
||||
if (icon_info == NULL) {
|
||||
return false;
|
||||
}
|
||||
|
||||
hGlobal = LoadResource(wesnoth_exe, icon_info);
|
||||
if (hGlobal == NULL) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const PBYTE icon_res = static_cast<PBYTE>(LockResource(hGlobal));
|
||||
if (icon_res == NULL) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const HICON icon = CreateIconFromResource(icon_res, SizeofResource(wesnoth_exe, icon_info), TRUE, 0x00030000);
|
||||
if (icon == NULL) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const HWND window = get_window_hanlde();
|
||||
if (window == NULL) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// filling notification structure
|
||||
nid = new NOTIFYICONDATA;
|
||||
memset(nid, 0, sizeof(&nid));
|
||||
nid->cbSize = NOTIFYICONDATA_V2_SIZE;
|
||||
nid->hWnd = window;
|
||||
nid->uFlags = NIF_ICON | NIF_TIP | NIF_MESSAGE;
|
||||
nid->dwInfoFlags = NIIF_USER;
|
||||
nid->uVersion = NOTIFYICON_VERSION;
|
||||
nid->uCallbackMessage = WM_TRAYNOTIFY;
|
||||
nid->uID = ICON_ID;
|
||||
nid->hIcon = icon;
|
||||
#if _WIN32_WINNT >= 0x600
|
||||
nid->hBalloonIcon = icon;
|
||||
#endif
|
||||
lstrcpy(nid->szTip, L"The Battle For Wesnoth");
|
||||
|
||||
// creating icon notification
|
||||
return Shell_NotifyIcon(NIM_ADD, nid) != FALSE;
|
||||
}
|
||||
|
||||
bool windows_tray_notification::set_tray_message(const std::string& title, const std::string& message)
|
||||
{
|
||||
// prevents deletion of icon when resetting already existing notification
|
||||
message_reset = (nid->uFlags & NIF_INFO) != 0;
|
||||
|
||||
nid->uFlags |= NIF_INFO;
|
||||
lstrcpy(nid->szInfoTitle, string_to_wstring(title).data());
|
||||
lstrcpy(nid->szInfo, string_to_wstring(message).data());
|
||||
|
||||
// setting notification
|
||||
return Shell_NotifyIcon(NIM_MODIFY, nid) != FALSE;
|
||||
}
|
||||
|
||||
void windows_tray_notification::adjust_length(std::string& title, std::string& message)
|
||||
{
|
||||
static const int ELIPSIS_LENGTH = 3;
|
||||
|
||||
// limitations set by winapi
|
||||
if (title.length() > MAX_TITLE_LENGTH) {
|
||||
utils::ellipsis_truncate(title, MAX_TITLE_LENGTH - ELIPSIS_LENGTH);
|
||||
}
|
||||
if (message.length() > MAX_MESSAGE_LENGTH) {
|
||||
utils::ellipsis_truncate(message, MAX_MESSAGE_LENGTH - ELIPSIS_LENGTH);
|
||||
}
|
||||
}
|
||||
|
||||
HWND windows_tray_notification::get_window_hanlde()
|
||||
{
|
||||
SDL_SysWMinfo wmInfo;
|
||||
SDL_VERSION(&wmInfo.version);
|
||||
if (SDL_GetWMInfo(&wmInfo) != 1) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return wmInfo.window;
|
||||
}
|
||||
|
||||
void windows_tray_notification::switch_to_wesnoth_window()
|
||||
{
|
||||
const HWND window = get_window_hanlde();
|
||||
if (window == NULL) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (IsIconic(window)) {
|
||||
ShowWindow(window, SW_RESTORE);
|
||||
}
|
||||
SetForegroundWindow(window);
|
||||
}
|
||||
|
||||
std::wstring windows_tray_notification::string_to_wstring(const std::string& string)
|
||||
{
|
||||
const std::vector<wchar_t> wide_string = utils::string_to_wstring(string);
|
||||
return std::wstring(wide_string.begin(), wide_string.end());
|
||||
}
|
||||
|
||||
bool windows_tray_notification::show(std::string title, std::string message)
|
||||
{
|
||||
adjust_length(title, message);
|
||||
|
||||
const bool tray_icon_exist = nid != NULL;
|
||||
if (!tray_icon_exist) {
|
||||
const bool tray_icon_created = create_tray_icon();
|
||||
if (!tray_icon_created) {
|
||||
const bool memory_allocated = nid != NULL;
|
||||
if (memory_allocated) {
|
||||
destroy_tray_icon();
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// at this point tray icon was just created or already existed before, so it's safe to call `set_tray_message`
|
||||
|
||||
const bool result = set_tray_message(title, message);
|
||||
// the `destroy_tray_icon` will be called by event only if `set_tray_message` succeeded
|
||||
// if it doesn't succeed, we have to call `destroy_tray_icon` manually
|
||||
if (!result) {
|
||||
destroy_tray_icon();
|
||||
}
|
||||
return result;
|
||||
}
|
69
src/windows_tray_notification.hpp
Normal file
69
src/windows_tray_notification.hpp
Normal file
|
@ -0,0 +1,69 @@
|
|||
#ifndef WINDOWS_TRAY_NOTIFICATION_HPP_INCLUDED
|
||||
#define WINDOWS_TRAY_NOTIFICATION_HPP_INCLUDED
|
||||
|
||||
#include <SDL.h>
|
||||
#include <string>
|
||||
//forces to call Unicode winapi functions instead of ASCII (default)
|
||||
#define UNICODE
|
||||
//defines that mingw misses
|
||||
#ifndef _WIN32_IE
|
||||
#define _WIN32_IE 0x0600 //specifying target platform to be Windows XP and higher
|
||||
#endif
|
||||
#ifndef NIIF_USER
|
||||
#define NIIF_USER 0x00000004
|
||||
#endif
|
||||
#ifndef NIN_BALLOONTIMEOUT
|
||||
#define NIN_BALLOONTIMEOUT (WM_USER + 4)
|
||||
#endif
|
||||
#ifndef NIN_BALLOONUSERCLICK
|
||||
#define NIN_BALLOONUSERCLICK (WM_USER + 5)
|
||||
#endif
|
||||
// ShellAPI.h should be included after Windows.h only!
|
||||
#include <Windows.h>
|
||||
#include <ShellAPI.h>
|
||||
|
||||
class windows_tray_notification {
|
||||
public:
|
||||
/**
|
||||
* Displays a tray notification.
|
||||
* When user clicks on the notification popup, the user switches to the wesnoth window.
|
||||
*
|
||||
* @param title Title of a notification. Gets truncated if longer than 64 characters, including
|
||||
* the terminating null character.
|
||||
* @param message Message of a notification. Gets truncated if longer than 256 characters, including
|
||||
* the terminating null character.
|
||||
*
|
||||
* @return True if message was shown successfully, False otherwise.
|
||||
*/
|
||||
static bool show(std::string title, std::string message);
|
||||
|
||||
/**
|
||||
* Frees resources when a notification disappears, switches user to the wesnoth
|
||||
* window if the notification popup was clicked by user.
|
||||
*
|
||||
* @param event System event.
|
||||
*/
|
||||
static void handle_system_event(const SDL_Event& event);
|
||||
|
||||
private:
|
||||
static NOTIFYICONDATA* nid;
|
||||
static bool message_reset;
|
||||
static const int ICON_ID = 1007; // just a random number
|
||||
static const unsigned int WM_TRAYNOTIFY = 32868; // WM_APP+100
|
||||
static const size_t MAX_TITLE_LENGTH = 63; // 64 including the terminating null character
|
||||
static const size_t MAX_MESSAGE_LENGTH = 255; // 256 including the terminating null character
|
||||
|
||||
static bool create_tray_icon();
|
||||
static void destroy_tray_icon();
|
||||
static bool set_tray_message(const std::string& title, const std::string& message);
|
||||
static void adjust_length(std::string& title, std::string& message);
|
||||
static HWND get_window_hanlde();
|
||||
static void switch_to_wesnoth_window();
|
||||
static std::wstring string_to_wstring(const std::string& string);
|
||||
|
||||
explicit windows_tray_notification();
|
||||
windows_tray_notification(const windows_tray_notification& w);
|
||||
windows_tray_notification& operator=(const windows_tray_notification& w);
|
||||
};
|
||||
|
||||
#endif // WINDOWS_TRAY_NOTIFICATION_HPP_INCLUDED
|
Loading…
Add table
Reference in a new issue