From 71dc17f69a75e15b31c935ce1323a269bc96ed62 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Remko=20Tron=C3=A7on?= Date: Wed, 8 Dec 2010 19:42:59 +0100 Subject: Update to new Snarl API. diff --git a/3rdParty/Snarl/SnarlInterface.cpp b/3rdParty/Snarl/SnarlInterface.cpp index b780140..0ae0b37 100644 --- a/3rdParty/Snarl/SnarlInterface.cpp +++ b/3rdParty/Snarl/SnarlInterface.cpp @@ -1,484 +1,412 @@ -// About: -// Snarl C++ interface implementation -// To understand what the different functions do and what they return, please -// have a look at the API on http://www.fullphat.net/dev/api.htm. -// Also have a look at mSnarl_i.bas found in the CVS/SVN repository for Snarl. -// -// The functions in SnarlInterface both have ANSI(UTF8) and UNICODE versions. -// If the LPCWSTR (unicode) version of the functions are called, the strings -// are converted to UTF8 by SnarlInterface before sent to Snarl. So using the -// ANSI/UTF8/LPCSTR versions of the functions are faster! -// -// -// Difference to VB implementation: -// Please note that string functions return NULL when they fail and not an -// empty string. So check for NULL... -// Function names doesn't have the pre "sn". -// -// -// Authors: -// Written and maintained by Toke Noer Nøttrup -// Original C++ version by "Whitman" -// -// License etc. : -// Feel free to use this code and class as you see fit. -// If you improve the code, it would be nice of you to take contact to the -// authors, so all can get the benifit. -// -// There is no guarantee that the code is correct or functional. -// USE AT YOUR OWN RISK -//----------------------------------------------------------------------------- - -// History -// 2008/12/31 : Implemented V39 API -// : Moved SnarlInterface into new Snarl namespace and moved enums etc. out of class -// : Added WCHAR overloads for all functions -// 2008/08/27 : Fixed return value of IsMessageVisible and HideMessage (returns false on failure now) -// : Fixed critical error in the new overloaded UpdateMessage() function -// 2008/08/27 : x64 compiler fix -// 2008/08/24 : Renamed all functions to not have prepended "sn". -// : Memory allocation functions added. (Use FreeString to free strings returned by Snarl) -// : Added m_nLastMessageId to the class. (Use GetLastMessageId() to get it) -// : Overloaded a few functions, so one don't have include the message id. (UpdateMessage f.i.) - -// 2008/06/20 : Fixed snShowMessageEx so it actually sends the extended version - oops -// : Fixed compiler warning C4800: forcing value to bool 'true' or 'false' (performance warning) - -// 2008/05/19 : uSend and uSendEx would always set return value to M_OK on successfull call -// 2008/04/14 : Updated to follow (what should be) the final Snarl 2.0 API -// 2008/03/28 : Few fixes for Snarl 2.0 -// 2007/05/23 : snGetGlobalMsg & snGetSnarlWindow made static -// 2007/03/25 : 1.6 RC1 fixup -// 2007/03/04 : Added - snGetAppPath, snGetIconsPath, snGetVersionEx, -// snSetTimeout, uSendEx - +/// +/// Snarl C++ interface implementation +/// +/// Written and maintained by Toke Noer Nøttrup (toke@noer.it) +/// +/// Please note the following changes compared to the VB6 (official API) dokumentation: +/// - Function names doesn't have the prefix "sn". Naming of constants and variables are +/// generally changed to follow Microsoft C# standard. This naming convention is kept for +/// the C++ version, to keep them alike. +/// - Grouped variables like SNARL_LAUNCHED, SNARL_QUIT is enums in SnarlEnums namespace. +/// - Message events like SNARL_NOTIFICATION_CLICKED, is found in SnarlEnums::MessageEvent. +/// - Please note that string functions return NULL when they fail and not an empty string. +/// - Some functions in the VB API takes an appToken as first parameter. This token is a +/// member variable in C++ version, so it is omitted from the functions. +/// (Always call RegisterApp as first function!) +/// - Functions manipulating messages (Update, Hide etc.) still takes a message token as +/// parameter, but you can get the last message token calling GetLastMsgToken(); +/// Example: snarl.Hide(snarl.GetLastMsgToken()); +/// +/// The functions in SnarlInterface both have ANSI(UTF8) and UNICODE versions. +/// If the LPCWSTR (unicode) version of the functions are called, the strings +/// are converted to UTF8 by SnarlInterface before sent to Snarl. So using the +/// ANSI/UTF8/LPCSTR versions of the functions are faster! +/// +/// Funtions special to C++ V41 API compared to VB version: +/// GetLastMsgToken() +/// GetAppPath() +/// GetIconsPath() +/// +///---------------------------------------------------------------------------- +/// +/// SnarlInterface snarl; +/// snarl.RegisterApp(_T("CppTest"), _T("C++ test app"), NULL); +/// snarl.AddClass(_T("Class1"), _T("Class 1")); +/// snarl.EZNotify(_T("Class1"), _T("C++ example 1"), _T("Some text"), 10); +/// snarl.UnregisterApp(); +/// +/// Please see the SimpleTest.cpp and SnarlV41Test.cpp for more example code. +/// +///---------------------------------------------------------------------------- +/// +/// 2010-08-13 : First release of V41 Snarl API implementation +/// + +#define _CRT_SECURE_NO_WARNINGS #include "SnarlInterface.h" -using namespace Snarl; +namespace Snarl { +namespace V41 { //----------------------------------------------------------------------------- // Constructor/Destructor //----------------------------------------------------------------------------- SnarlInterface::SnarlInterface() -: m_hwndFrom(NULL), m_nLastMessageId(0) + : appToken(0), lastMsgToken(0), localError(SnarlEnums::Success) { - } SnarlInterface::~SnarlInterface() { - } +// ---------------------------------------------------------------------------- -//----------------------------------------------------------------------------- -// snShowMessage() - -/// Displays a message with Title and Text. Timeout controls how long the -/// message is displayed for (in seconds) (omitting this value means the message -/// is displayed indefinately). IconPath specifies the location of a PNG image -/// which will be displayed alongside the message text. -/// Message Id on success or M_RESULT on failure - -LONG32 SnarlInterface::ShowMessage(LPCSTR szTitle, LPCSTR szText, LONG32 timeout, LPCSTR szIconPath, HWND hWndReply, WPARAM uReplyMsg) +LONG32 SnarlInterface::RegisterApp(LPCSTR signature, LPCSTR title, LPCSTR icon, HWND hWndReply /* = NULL */, LONG32 msgReply /* = 0 */, SnarlEnums::AppFlags flags /* = SnarlEnums::AppDefault */) { - SNARLSTRUCT ss; - ZeroMemory((void*)&ss, sizeof(ss)); - - ss.Cmd = SNARL_SHOW; - StringCbCopyA((LPSTR)&ss.Title, SNARL_STRING_LENGTH, szTitle); - StringCbCopyA((LPSTR)&ss.Text, SNARL_STRING_LENGTH, szText); - StringCbCopyA((LPSTR)&ss.Icon, SNARL_STRING_LENGTH, szIconPath); - ss.Timeout = timeout; + SnarlMessage msg; + msg.Command = SnarlEnums::RegisterApp; + msg.Token = 0; + PackData(msg.PacketData, + "id::%s#?title::%s#?icon::%s#?hwnd::%d#?umsg::%d#?flags::%d", + signature, title, icon, hWndReply, msgReply, flags); - ss.LngData2 = reinterpret_cast(hWndReply); - ss.Id = static_cast(uReplyMsg); + appToken = Send(msg); + lastMsgToken = 0; - m_nLastMessageId = Send(ss); - return m_nLastMessageId; + return appToken; } -LONG32 SnarlInterface::ShowMessage(LPCWSTR szTitle, LPCWSTR szText, LONG32 timeout, LPCWSTR szIconPath, HWND hWndReply, WPARAM uReplyMsg) +LONG32 SnarlInterface::RegisterApp(LPCWSTR signature, LPCWSTR title, LPCWSTR icon, HWND hWndReply /* = NULL */, LONG32 msgReply /* = 0 */, SnarlEnums::AppFlags flags /* = SnarlEnums::AppDefault */) { - LPSTR szUTF8Title = WideToUTF8(szTitle); - LPSTR szUTF8Text = WideToUTF8(szText); - LPSTR szUFT8IconPath = WideToUTF8(szIconPath); + LPCSTR szParam1 = WideToUTF8(signature); + LPCSTR szParam2 = WideToUTF8(title); + LPCSTR szParam3 = WideToUTF8(icon); - LONG32 result = ShowMessage(szUTF8Title, szUTF8Text, timeout, szUFT8IconPath, hWndReply, uReplyMsg); - - delete [] szUTF8Title; - delete [] szUTF8Text; - delete [] szUFT8IconPath; + LONG32 result = RegisterApp(szParam1, szParam2, szParam3, hWndReply, msgReply, flags); + delete [] szParam1; + delete [] szParam2; + delete [] szParam3; + return result; } - -//----------------------------------------------------------------------------- -// snShowMessageEx() - -/// Displays a notification. This function is identical to snShowMessage() -/// except that Class specifies an alert previously registered with -/// snRegisterAlert() and SoundFile can optionally specify a WAV sound to play -/// when the notification is displayed on screen. -/// Message Id on success or M_RESULT on failure - -LONG32 SnarlInterface::ShowMessageEx(LPCSTR szClass, LPCSTR szTitle, LPCSTR szText, LONG32 timeout, LPCSTR szIconPath, HWND hWndReply, WPARAM uReplyMsg, LPCSTR szSoundFile) +LONG32 SnarlInterface::UnregisterApp() { - SNARLSTRUCTEX ssex; - ZeroMemory((void*)&ssex, sizeof(ssex)); - - ssex.Cmd = SNARL_EX_SHOW; - ssex.Timeout = timeout; - ssex.LngData2 = reinterpret_cast(hWndReply); - ssex.Id = static_cast(uReplyMsg); + SnarlMessage msg; + msg.Command = SnarlEnums::UnregisterApp; + msg.Token = appToken; + PackData(msg.PacketData, NULL); - StringCbCopyA((LPSTR)&ssex.Class, SNARL_STRING_LENGTH, szClass); - StringCbCopyA((LPSTR)&ssex.Title, SNARL_STRING_LENGTH, szTitle); - StringCbCopyA((LPSTR)&ssex.Text, SNARL_STRING_LENGTH, szText); - StringCbCopyA((LPSTR)&ssex.Icon, SNARL_STRING_LENGTH, szIconPath); - StringCbCopyA((LPSTR)&ssex.Extra, SNARL_STRING_LENGTH, szSoundFile); + appToken = 0; + lastMsgToken = 0; - m_nLastMessageId = Send(ssex); - return m_nLastMessageId; + return Send(msg); } -LONG32 SnarlInterface::ShowMessageEx(LPCWSTR szClass, LPCWSTR szTitle, LPCWSTR szText, LONG32 timeout, LPCWSTR szIconPath, HWND hWndReply, WPARAM uReplyMsg, LPCWSTR szSoundFile) +LONG32 SnarlInterface::UpdateApp(LPCSTR title /* = NULL */, LPCSTR icon /* = NULL */) { - LPSTR szUTF8Class = WideToUTF8(szClass); - LPSTR szUTF8Title = WideToUTF8(szTitle); - LPSTR szUTF8Text = WideToUTF8(szText); - LPSTR szUFT8IconPath = WideToUTF8(szIconPath); - LPSTR szUFT8SoundFile = WideToUTF8(szSoundFile); - - LONG32 result = ShowMessageEx(szUTF8Class, szUTF8Title, szUTF8Text, timeout, szUFT8IconPath, hWndReply, uReplyMsg, szUFT8SoundFile); + if (title == NULL && icon == NULL) + return 0; + + SnarlMessage msg; + msg.Command = SnarlEnums::UpdateApp; + msg.Token = appToken; - delete [] szUTF8Class; - delete [] szUTF8Title; - delete [] szUTF8Text; - delete [] szUFT8IconPath; - delete [] szUFT8SoundFile; + // TODO: Uckly code ahead + if (title != NULL && title[0] != 0 && icon != NULL && icon[0] != 0) + PackData(msg.PacketData, "title::%s#?icon::%s", title, icon); + else if (title != NULL && title[0] != 0) + PackData(msg.PacketData, "title::%s", title); + else if (icon != NULL && icon[0] != 0) + PackData(msg.PacketData, "icon::%s", icon); - return result; + return Send(msg); } -//----------------------------------------------------------------------------- -// snHideMessage() - -/// Hides the notification specified by Id. Id is the value returned by -/// snShowMessage() or snShowMessageEx() when the notification was initially -/// created. This function returns True if the notification was successfully -/// hidden or False otherwise (for example, the notification may no longer exist). - -BOOL SnarlInterface::HideMessage(LONG32 Id) +LONG32 SnarlInterface::UpdateApp(LPCWSTR title /* = NULL */, LPCWSTR icon /* = NULL */) { - SNARLSTRUCT ss; - ss.Cmd = SNARL_HIDE; - ss.Id = Id; + LPCSTR szParam1 = WideToUTF8(title); + LPCSTR szParam2 = WideToUTF8(icon); + + LONG32 result = UpdateApp(szParam1, szParam2); + + delete [] szParam1; + delete [] szParam2; - LONG32 n = Send(ss); - return (n == -1 || n == 1) ? TRUE : FALSE; + return result; } -BOOL SnarlInterface::HideMessage() +LONG32 SnarlInterface::AddClass(LPCSTR className, LPCSTR description, bool enabled /* = true */) { - return HideMessage(m_nLastMessageId); -} - -//----------------------------------------------------------------------------- -// snIsMessageVisible() - -/// Returns True if the notification specified by Id is still visible, or -/// False if not. Id is the value returned by snShowMessage() or -/// snShowMessageEx() when the notification was initially created. + SnarlMessage msg; + msg.Command = SnarlEnums::AddClass; + msg.Token = appToken; + PackData(msg.PacketData, "id::%s#?name::%s#?enabled::%d", className, description, (enabled ? 1 : 0)); -BOOL SnarlInterface::IsMessageVisible(LONG32 Id) -{ - SNARLSTRUCT ss; - ss.Cmd = SNARL_IS_VISIBLE; - ss.Id = Id; - - // We are getting -1 when true, checking for 1 just in case. We don't want to return true for the other M_RESULT returns - LONG32 n = Send(ss); - return (n == -1 || n == 1) ? TRUE : FALSE; + return Send(msg); } -BOOL SnarlInterface::IsMessageVisible() +LONG32 SnarlInterface::AddClass(LPCWSTR className, LPCWSTR description, bool enabled /* = true */) { - if (m_nLastMessageId == 0) - return FALSE; + LPCSTR szParam1 = WideToUTF8(className); + LPCSTR szParam2 = WideToUTF8(description); + + LONG32 result = AddClass(szParam1, szParam2, enabled); + + delete [] szParam1; + delete [] szParam2; - return IsMessageVisible(m_nLastMessageId); + return result; } -//----------------------------------------------------------------------------- -// snUpdateMessage() - -/// Changes the title and text in the message specified by Id to the values -/// specified by Title and Text respectively. Id is the value returned by -/// snShowMessage() or snShowMessageEx() when the notification was originally -/// created. To change the timeout parameter of a notification, use snSetTimeout() - -M_RESULT SnarlInterface::UpdateMessage(LONG32 id, LPCSTR szTitle, LPCSTR szText, LPCSTR szIconPath) +LONG32 SnarlInterface::RemoveClass(LPCSTR className, bool forgetSettings /* = false */) { - SNARLSTRUCT ss; - ZeroMemory((void*)&ss, sizeof(ss)); - - ss.Cmd = SNARL_UPDATE; - ss.Id = id; - - StringCbCopyA((LPSTR)&ss.Title, SNARL_STRING_LENGTH, szTitle); - StringCbCopyA((LPSTR)&ss.Text, SNARL_STRING_LENGTH, szText); - StringCbCopyA((LPSTR)&ss.Icon, SNARL_STRING_LENGTH, szIconPath); + SnarlMessage msg; + msg.Command = SnarlEnums::RemoveClass; + msg.Token = appToken; + PackData(msg.PacketData, "id::%s#?forget::%d", className, (forgetSettings ? 1 : 0)); - return static_cast(Send(ss)); + return Send(msg); } -M_RESULT SnarlInterface::UpdateMessage(LONG32 id, LPCWSTR szTitle, LPCWSTR szText, LPCWSTR szIconPath) +LONG32 SnarlInterface::RemoveClass(LPCWSTR className, bool forgetSettings /* = false */) { - LPSTR szParam1 = WideToUTF8(szTitle); - LPSTR szParam2 = WideToUTF8(szText); - LPSTR szParam3 = WideToUTF8(szIconPath); + LPCSTR szParam1 = WideToUTF8(className); - M_RESULT result = UpdateMessage(id, szParam1, szParam2, szParam3); + LONG32 result = RemoveClass(szParam1, forgetSettings); delete [] szParam1; - delete [] szParam2; - delete [] szParam3; - + return result; } -M_RESULT SnarlInterface::UpdateMessage(LPCSTR szTitle, LPCSTR szText, LPCSTR szIconPath) +LONG32 SnarlInterface::RemoveAllClasses(bool forgetSettings /* = false */) { - return UpdateMessage(m_nLastMessageId, szTitle, szText, szIconPath); -} + SnarlMessage msg; + msg.Command = SnarlEnums::RemoveClass; + msg.Token = appToken; + PackData(msg.PacketData, "all::1#?forget::%d", (forgetSettings ? 1 : 0)); -M_RESULT SnarlInterface::UpdateMessage(LPCWSTR szTitle, LPCWSTR szText, LPCWSTR szIconPath) -{ - return UpdateMessage(m_nLastMessageId, szTitle, szText, szIconPath); + return Send(msg); } -//----------------------------------------------------------------------------- -// snRegisterConfig - -/// Registers an application's configuration interface with Snarl. -/// AppName is the text that's displayed in the Applications list so it should -/// be people friendly ("My cool app" rather than "my_cool_app"). - -M_RESULT SnarlInterface::RegisterConfig(HWND hWnd, LPCSTR szAppName, LONG32 replyMsg) +LONG32 SnarlInterface::EZNotify(LPCSTR className, LPCSTR title, LPCSTR text, LONG32 timeout /* = -1 */, LPCSTR icon /* = NULL */, LONG32 priority /* = 0 */, LPCSTR acknowledge /* = NULL */, LPCSTR value /* = NULL */) { - return RegisterConfig2(hWnd, szAppName, replyMsg, ""); -} + SnarlMessage msg; + msg.Command = SnarlEnums::Notify; + msg.Token = appToken; + PackData(msg.PacketData, + "id::%s#?title::%s#?text::%s#?timeout::%d#?icon::%s#?priority::%d#?ack::%s#?value::%s", + className, title, text, timeout, (icon ? icon : ""), priority, (acknowledge ? acknowledge : ""), (value ? value : "")); -M_RESULT SnarlInterface::RegisterConfig(HWND hWnd, LPCWSTR szAppName, LONG32 replyMsg) -{ - return RegisterConfig2(hWnd, szAppName, replyMsg, L""); + lastMsgToken = Send(msg); + return lastMsgToken; } -//----------------------------------------------------------------------------- -// snRegisterConfig2 - -/// Registers an application's configuration interface with Snarl. -/// This function is identical to snRegisterConfig() except that Icon can be -/// used to specify a PNG image which will be displayed against the -/// application's entry in Snarl's Preferences panel. - -M_RESULT SnarlInterface::RegisterConfig2(HWND hWnd, LPCSTR szAppName, LONG32 replyMsg, LPCSTR szIcon) +LONG32 SnarlInterface::EZNotify(LPCWSTR className, LPCWSTR title, LPCWSTR text, LONG32 timeout /* = -1 */, LPCWSTR icon /* = NULL */, LONG32 priority /* = 0 */, LPCWSTR acknowledge /* = NULL */, LPCWSTR value /* = NULL */) { - if (!szAppName || !szIcon) - return M_BAD_POINTER; - - SNARLSTRUCT ss; + LPCSTR szParam1 = WideToUTF8(className); + LPCSTR szParam2 = WideToUTF8(title); + LPCSTR szParam3 = WideToUTF8(text); + LPCSTR szParam4 = WideToUTF8(icon); + LPCSTR szParam5 = WideToUTF8(acknowledge); + LPCSTR szParam6 = WideToUTF8(value); + + LONG32 result = EZNotify(szParam1, szParam2, szParam3, timeout, szParam4, priority, szParam5, szParam6); + + delete [] szParam1; delete [] szParam2; delete [] szParam3; + delete [] szParam4; delete [] szParam5; delete [] szParam6; - m_hwndFrom = hWnd; + return result; +} - ss.Cmd = SNARL_REGISTER_CONFIG_WINDOW_2; - ss.LngData2 = reinterpret_cast(hWnd); - ss.Id = replyMsg; - StringCbCopyA((LPSTR)&ss.Title, SNARL_STRING_LENGTH, szAppName); - StringCbCopyA((LPSTR)&ss.Icon, SNARL_STRING_LENGTH, szIcon); +LONG32 SnarlInterface::Notify(LPCSTR className, LPCSTR packetData) +{ + SnarlMessage msg; + msg.Command = SnarlEnums::Notify; + msg.Token = appToken; + PackData(msg.PacketData, "id::%s#?%s", className, packetData); - return static_cast(Send(ss)); + lastMsgToken = Send(msg); + return lastMsgToken; } -M_RESULT SnarlInterface::RegisterConfig2(HWND hWnd, LPCWSTR szAppName, LONG32 replyMsg, LPCWSTR szIcon) +LONG32 SnarlInterface::Notify(LPCWSTR className, LPCWSTR packetData) { - LPSTR szParam1 = WideToUTF8(szAppName); - LPSTR szParam2 = WideToUTF8(szIcon); - - M_RESULT result = RegisterConfig2(hWnd, szParam1, replyMsg, szParam2); + LPCSTR szParam1 = WideToUTF8(className); + LPCSTR szParam2 = WideToUTF8(packetData); + + LONG32 result = Notify(szParam1, szParam2); - delete [] szParam1; - delete [] szParam2; + delete [] szParam1; delete [] szParam2; return result; } - -//----------------------------------------------------------------------------- -// snRevokeConfig - -/// Removes the application previously registered using snRegisterConfig() or -/// snRegisterConfig2(). hWnd should be the same as that used during registration. - -M_RESULT SnarlInterface::RevokeConfig(HWND hWnd) +LONG32 SnarlInterface::EZUpdate(LONG32 msgToken, LPCSTR title /* = NULL */, LPCSTR text /* = NULL */, LONG32 timeout /* = -1 */, LPCSTR icon /* = NULL */) { - SNARLSTRUCT ss; + SnarlMessage msg; + msg.Command = SnarlEnums::UpdateNotification; + msg.Token = msgToken; - m_hwndFrom = NULL; + // Create packed data + errno_t err = 0; + ZeroMemory(msg.PacketData, sizeof(msg.PacketData)); + char* pData = reinterpret_cast(msg.PacketData); - ss.Cmd = SNARL_REVOKE_CONFIG_WINDOW; - ss.LngData2 = reinterpret_cast(hWnd); + if (title != NULL) { + err |= strncat_s(pData, SnarlPacketDataSize, (pData[0] != NULL) ? "#?title::" : "title::", _TRUNCATE); //StringCbCat(tmp, SnarlPacketDataSize, "title::%s"); + err |= strncat_s(pData, SnarlPacketDataSize, title, _TRUNCATE); + } + if (text != NULL) { + err |= strncat_s(pData, SnarlPacketDataSize, (pData[0] != NULL) ? "#?text::" : "text::", _TRUNCATE); + err |= strncat_s(pData, SnarlPacketDataSize, text, _TRUNCATE); + } + if (icon != NULL) { + err |= strncat_s(pData, SnarlPacketDataSize, (pData[0] != NULL) ? "#?icon::" : "icon::", _TRUNCATE); + err |= strncat_s(pData, SnarlPacketDataSize, icon, _TRUNCATE); + } + if (timeout != -1) { + char tmp[32]; + _itoa_s(timeout, tmp, 10); + + err |= strncat_s(pData, SnarlPacketDataSize, (pData[0] != NULL) ? "#?timeout::" : "timeout::", _TRUNCATE); + err |= strncat_s(pData, SnarlPacketDataSize, tmp, _TRUNCATE); + } + + // Check for strcat errors and exit on error + if (err != 0) { + localError = SnarlEnums::ErrorFailed; + return 0; + } - return static_cast(Send(ss)); + return Send(msg); } - -//----------------------------------------------------------------------------- -// snGetVersion() - -/// Checks if Snarl is currently running and, if it is, retrieves the major and -/// minor release version numbers in Major and Minor respectively. -/// Returns True if Snarl is running, False otherwise. - -BOOL SnarlInterface::GetVersion(WORD* Major, WORD* Minor) +LONG32 SnarlInterface::EZUpdate(LONG32 msgToken, LPCWSTR title /* = NULL */, LPCWSTR text /* = NULL */, LONG32 timeout /* = -1 */, LPCWSTR icon /* = NULL */) { - SNARLSTRUCT ss; - ss.Cmd = SNARL_GET_VERSION; - LONG32 versionInfo = Send(ss); - if (versionInfo > 0 && versionInfo != M_FAILED && versionInfo != M_TIMED_OUT) { - *Major = HIWORD(versionInfo); - *Minor = LOWORD(versionInfo); - return TRUE; - } - return FALSE; -} + LPCSTR szParam1 = WideToUTF8(title); + LPCSTR szParam2 = WideToUTF8(text); + LPCSTR szParam3 = WideToUTF8(icon); + LONG32 result = EZUpdate(msgToken, szParam1, szParam2, timeout, szParam3); + + delete [] szParam1; delete [] szParam2; delete [] szParam3; + + return result; +} -//----------------------------------------------------------------------------- -// snGetVersionEx +LONG32 SnarlInterface::Update(LONG32 msgToken, LPCSTR packetData) +{ + SnarlMessage msg; + msg.Command = SnarlEnums::UpdateNotification; + msg.Token = msgToken; + PackData(msg.PacketData, packetData); -/// Returns the Snarl system version number. This is an integer value which -/// represents the system build number and can be used to identify the specific -/// version of Snarl running + return Send(msg); +} -LONG32 SnarlInterface::GetVersionEx() +LONG32 SnarlInterface::Update(LONG32 msgToken, LPCWSTR packetData) { - SNARLSTRUCT ss; - ss.Cmd = SNARL_GET_VERSION_EX; - return Send(ss); -} + LPCSTR szParam1 = WideToUTF8(packetData); + LONG32 result = Update(msgToken, szParam1); + + delete [] szParam1; + + return result; +} -//----------------------------------------------------------------------------- -// snSetTimeout() +LONG32 SnarlInterface::Hide(LONG32 msgToken) +{ + SnarlMessage msg; + msg.Command = SnarlEnums::HideNotification; + msg.Token = msgToken; + PackData(msg.PacketData, NULL); -/// Sets the timeout of existing notification Id to Timeout seconds. Id is the -/// value returned by snShowMessage() or snShowMessageEx() when the notification -/// was first created. + return Send(msg); +} -M_RESULT SnarlInterface::SetTimeout(LONG32 Id, LONG32 Timeout) +LONG32 SnarlInterface::IsVisible(LONG32 msgToken) { - SNARLSTRUCT ss; - ss.Cmd = SNARL_SET_TIMEOUT; - ss.Id = Id; - ss.LngData2 = Timeout; + SnarlMessage msg; + msg.Command = SnarlEnums::IsNotificationVisible; + msg.Token = msgToken; + PackData(msg.PacketData, NULL); - return static_cast(Send(ss)); + return Send(msg); } -M_RESULT SnarlInterface::SetTimeout(LONG32 Timeout) +SnarlEnums::SnarlStatus SnarlInterface::GetLastError() { - return SetTimeout(m_nLastMessageId, Timeout); + return localError; } -//----------------------------------------------------------------------------- -// snRegisterAlert() - -/// Registers an alert of Class for application AppName which must have previously -/// been registered with either snRegisterConfig() or snRegisterConfig2(). - -M_RESULT SnarlInterface::RegisterAlert(LPCSTR szAppName, LPCSTR szClass) +// static +BOOL SnarlInterface::IsSnarlRunning() { - SNARLSTRUCT ss; - ss.Cmd = SNARL_REGISTER_ALERT; - StringCbCopyA((LPSTR)&ss.Title, SNARL_STRING_LENGTH, szAppName); - StringCbCopyA((LPSTR)&ss.Text, SNARL_STRING_LENGTH, szClass); - - return static_cast(Send(ss)); + return IsWindow(GetSnarlWindow()); } -M_RESULT SnarlInterface::RegisterAlert(LPCWSTR szAppName, LPCWSTR szClass) +LONG32 SnarlInterface::GetVersion() { - LPSTR szParam1 = WideToUTF8(szAppName); - LPSTR szParam2 = WideToUTF8(szClass); - - M_RESULT result = RegisterAlert(szParam1, szParam2); - - delete [] szParam1; - delete [] szParam2; - - return result; -} + localError = SnarlEnums::Success; -//----------------------------------------------------------------------------- -// snGetGlobalMsg() + HWND hWnd = GetSnarlWindow(); + if (!IsWindow(hWnd)) + { + localError = SnarlEnums::ErrorNotRunning; + return 0; + } -/// Returns the atom that corresponds to the "SnarlGlobalEvent" registered -/// Windows message. This message is sent by Snarl when it is first starts and -/// when it shuts down. + HANDLE hProp = GetProp(hWnd, _T("_version")); + return reinterpret_cast(hProp); +} -LONG32 SnarlInterface::GetGlobalMsg() +// static +UINT SnarlInterface::Broadcast() { - return RegisterWindowMessage(SNARL_GLOBAL_MSG); + return RegisterWindowMessage(SnarlGlobalMsg); } +// static +UINT SnarlInterface::AppMsg() +{ + return RegisterWindowMessage(SnarlAppMsg); +} -//----------------------------------------------------------------------------- -// snGetSnarlWindow - +// static HWND SnarlInterface::GetSnarlWindow() { - return FindWindow(NULL, _T("Snarl")); + return FindWindow(SnarlWindowClass, SnarlWindowTitle);; } - -//----------------------------------------------------------------------------- -// snGetAppPath() - -/// Returns a pointer to the path. -/// ** Remember to call FreeString - LPCTSTR SnarlInterface::GetAppPath() { HWND hWnd = GetSnarlWindow(); if (hWnd) { - HWND hWndPath = FindWindowEx(hWnd, 0, _T("static"), NULL); + HWND hWndPath = FindWindowEx(hWnd, NULL, _T("static"), NULL); if (hWndPath) { TCHAR strTmp[MAX_PATH] = {0}; - int nReturn = GetWindowText(hWndPath, strTmp, MAX_PATH); + int nReturn = GetWindowText(hWndPath, strTmp, MAX_PATH-1); if (nReturn > 0) { TCHAR* strReturn = AllocateString(nReturn + 1); - StringCchCopy(strReturn, nReturn + 1, strTmp); + _tcsncpy(strReturn, strTmp, nReturn + 1); + strReturn[nReturn] = 0; return strReturn; } } } + return NULL; } - -//----------------------------------------------------------------------------- -// snGetIconsPath() - -/// Returns a pointer to the iconpath. -/// ** Remember to call FreeString when done with the string - LPCTSTR SnarlInterface::GetIconsPath() { TCHAR* szIconPath = NULL; @@ -487,13 +415,14 @@ LPCTSTR SnarlInterface::GetIconsPath() return NULL; size_t nLen = 0; - if (SUCCEEDED(StringCbLength(szPath, MAX_PATH, &nLen))) + // TODO: _tcsnlen MAX_PATH + if (nLen = _tcsnlen(szPath, MAX_PATH)) { nLen += 10 + 1; // etc\\icons\\ + NULL szIconPath = AllocateString(nLen); - StringCbCopy(szIconPath, nLen * sizeof(TCHAR), szPath); - StringCbCat(szIconPath, nLen * sizeof(TCHAR), _T("etc\\icons\\")); + _tcsncpy(szIconPath, szPath, nLen); + _tcsncat(szIconPath, _T("etc\\icons\\"), nLen); } FreeString(szPath); @@ -501,312 +430,88 @@ LPCTSTR SnarlInterface::GetIconsPath() return szIconPath; } - -//----------------------------------------------------------------------------- -// snSetAsSnarlApp() - -/// Identifies an application as a Snarl App. (V39) - -void SnarlInterface::SetAsSnarlApp(HWND hWndOwner, SNARL_APP_FLAGS Flags) +LONG32 SnarlInterface::GetLastMsgToken() const { - if (IsWindow(hWndOwner)) { - SetProp(hWndOwner, _T("snarl_app"), reinterpret_cast(1)); - SetProp(hWndOwner, _T("snarl_app_flags"), reinterpret_cast(Flags)); - } + return lastMsgToken; } //----------------------------------------------------------------------------- -// snGetAppMsg() - -/// Returns the global Snarl Application message (V39) - -LONG32 SnarlInterface::GetAppMsg() -{ - return RegisterWindowMessage(SNARL_APP_MSG); -} - - -//----------------------------------------------------------------------------- -// snRegisterApp() - -/// Registers an application with Snarl (V39) - -M_RESULT SnarlInterface::RegisterApp(LPCSTR Application, LPCSTR SmallIcon, LPCSTR LargeIcon, HWND hWnd, LONG32 ReplyMsg) -{ - m_hwndFrom = hWnd; - - SNARLSTRUCT ss; - ss.Cmd = SNARL_REGISTER_APP; - - StringCbCopyA((LPSTR)&ss.Title, SNARL_STRING_LENGTH, Application); - StringCbCopyA((LPSTR)&ss.Icon, SNARL_STRING_LENGTH, SmallIcon); - StringCbCopyA((LPSTR)&ss.Text, SNARL_STRING_LENGTH, LargeIcon); - - ss.LngData2 = reinterpret_cast(hWnd); - ss.Id = ReplyMsg; - ss.Timeout = GetCurrentProcessId(); - - return static_cast(Send(ss)); -} - -M_RESULT SnarlInterface::RegisterApp(LPCWSTR Application, LPCWSTR SmallIcon, LPCWSTR LargeIcon, HWND hWnd, LONG32 ReplyMsg) -{ - LPSTR szParam1 = WideToUTF8(Application); - LPSTR szParam2 = WideToUTF8(SmallIcon); - LPSTR szParam3 = WideToUTF8(LargeIcon); - - M_RESULT result = RegisterApp(szParam1, szParam2, szParam3, hWnd, ReplyMsg); - - delete [] szParam1; - delete [] szParam2; - delete [] szParam3; - - return result; -} - - +// Private functions //----------------------------------------------------------------------------- -// snUnregisterApp() - -/// Unregisters an application with Snarl (V39) -M_RESULT SnarlInterface::UnregisterApp() +LONG32 SnarlInterface::Send(SnarlMessage msg) { - SNARLSTRUCT ss; - ss.Cmd = SNARL_UNREGISTER_APP; - ss.LngData2 = GetCurrentProcessId(); - - m_hwndFrom = NULL; - - return static_cast(Send(ss)); -} - + DWORD_PTR nReturn = 0; // Failure -//----------------------------------------------------------------------------- -// snShowNotification() - -/// Displays a Snarl notification using registered class (V39) -/// Message Id on success or M_RESULT on failure - -LONG32 SnarlInterface::ShowNotification(LPCSTR Class, LPCSTR Title, LPCSTR Text, LONG32 Timeout, LPCSTR Icon, HWND hWndReply, LONG32 uReplyMsg, LPCSTR Sound) -{ - SNARLSTRUCTEX ssex; - ssex.Cmd = SNARL_SHOW_NOTIFICATION; - - StringCbCopyExA((LPSTR)&ssex.Title, SNARL_STRING_LENGTH, Title, NULL, NULL, STRSAFE_IGNORE_NULLS); - StringCbCopyExA((LPSTR)&ssex.Text, SNARL_STRING_LENGTH, Text, NULL, NULL, STRSAFE_IGNORE_NULLS); - StringCbCopyExA((LPSTR)&ssex.Icon, SNARL_STRING_LENGTH, Icon, NULL, NULL, STRSAFE_IGNORE_NULLS); - - ssex.Timeout = Timeout; - ssex.LngData2 = reinterpret_cast(hWndReply); - ssex.Id = uReplyMsg; - - StringCbCopyExA((LPSTR)&ssex.Extra, SNARL_STRING_LENGTH, Sound, NULL, NULL, STRSAFE_IGNORE_NULLS); - StringCbCopyA((LPSTR)&ssex.Class, SNARL_STRING_LENGTH, Class); - - ssex.Reserved1 = GetCurrentProcessId(); - - m_nLastMessageId = Send(ssex); - return m_nLastMessageId; -} - -LONG32 SnarlInterface::ShowNotification(LPCWSTR Class, LPCWSTR Title, LPCWSTR Text, LONG32 Timeout, LPCWSTR Icon, HWND hWndReply, LONG32 uReplyMsg, LPCWSTR Sound) -{ - LPSTR szParam1 = WideToUTF8(Class); - LPSTR szParam2 = WideToUTF8(Title); - LPSTR szParam3 = WideToUTF8(Text); - LPSTR szParam4 = WideToUTF8(Icon); - LPSTR szParam5 = WideToUTF8(Sound); - - LONG32 result = ShowNotification(szParam1, szParam2, szParam3, Timeout, szParam4, hWndReply, uReplyMsg, szParam5); - - delete [] szParam1; - delete [] szParam2; - delete [] szParam3; - delete [] szParam4; - delete [] szParam5; - - return result; -} - - -//----------------------------------------------------------------------------- -// snChangeAttribute() - -/// (V39) + HWND hWnd = GetSnarlWindow(); + if (!IsWindow(hWnd)) + { + localError = SnarlEnums::ErrorNotRunning; + return 0; + } -M_RESULT SnarlInterface::ChangeAttribute(LONG32 Id, SNARL_ATTRIBUTES Attr, LPCSTR Value) -{ - SNARLSTRUCT ss; - ss.Cmd = SNARL_CHANGE_ATTR; - ss.Id = Id; - ss.LngData2 = Attr; - - StringCbCopyExA((LPSTR)&ss.Text, SNARL_STRING_LENGTH, Value, NULL, NULL, STRSAFE_IGNORE_NULLS); - - return static_cast(Send(ss)); -} + COPYDATASTRUCT cds; + cds.dwData = 0x534E4C02; // "SNL",2; + cds.cbData = sizeof(SnarlMessage); + cds.lpData = &msg; -M_RESULT SnarlInterface::ChangeAttribute(LONG32 Id, SNARL_ATTRIBUTES Attr, LPCWSTR Value) -{ - LPSTR szParam1 = WideToUTF8(Value); - - M_RESULT result = ChangeAttribute(Id, Attr, szParam1); - - delete [] szParam1; - - return result; -} + if (SendMessageTimeout(hWnd, WM_COPYDATA, (WPARAM)GetCurrentProcessId(), (LPARAM)&cds, SMTO_ABORTIFHUNG | SMTO_NOTIMEOUTIFNOTHUNG, 500, &nReturn) == 0) + { + // return zero on failure + if (GetLastError() == ERROR_TIMEOUT) + localError = SnarlEnums::ErrorTimedOut; + else + localError = SnarlEnums::ErrorFailed; + + return 0; + } -M_RESULT SnarlInterface::ChangeAttribute(SNARL_ATTRIBUTES Attr, LPCSTR Value) -{ - return ChangeAttribute(m_nLastMessageId, Attr, Value); -} + // return result and cache LastError + HANDLE hProp = GetProp(hWnd, _T("last_error")); + localError = static_cast(reinterpret_cast(hProp)); -M_RESULT SnarlInterface::ChangeAttribute(SNARL_ATTRIBUTES Attr, LPCWSTR Value) -{ - return ChangeAttribute(m_nLastMessageId, Attr, Value); + return nReturn; } - //----------------------------------------------------------------------------- -// snSetClassDefault() -/// Sets the default value for an alert class (V39) - -M_RESULT SnarlInterface::SetClassDefault(LPCSTR Class, SNARL_ATTRIBUTES Attr, LPCSTR Value) +// Remember to delete [] returned string +inline +LPSTR SnarlInterface::WideToUTF8(LPCWSTR szWideStr) { - SNARLSTRUCT ss; - ss.Cmd = SNARL_SET_CLASS_DEFAULT; - ss.LngData2 = Attr; - ss.Timeout = GetCurrentProcessId(); - - StringCbCopyExA((LPSTR)&ss.Text, SNARL_STRING_LENGTH, Class, NULL, NULL, STRSAFE_IGNORE_NULLS); - StringCbCopyExA((LPSTR)&ss.Icon, SNARL_STRING_LENGTH, Value, NULL, NULL, STRSAFE_IGNORE_NULLS); - - return static_cast(Send(ss)); -} + if (szWideStr == NULL) + return NULL; -M_RESULT SnarlInterface::SetClassDefault(LPCWSTR Class, SNARL_ATTRIBUTES Attr, LPCWSTR Value) -{ - LPSTR szParam1 = WideToUTF8(Class); - LPSTR szParam2 = WideToUTF8(Value); + int nSize = WideCharToMultiByte(CP_UTF8, 0, szWideStr, -1, NULL, 0, NULL, NULL); + LPSTR szUTF8 = new char[nSize]; + WideCharToMultiByte(CP_UTF8, 0, szWideStr, -1, szUTF8, nSize, NULL, NULL); - M_RESULT result = SetClassDefault(szParam1, Attr, szParam2); - - delete [] szParam1; - delete [] szParam2; - - return result; -} - - -//----------------------------------------------------------------------------- -// snGetRevision() - -/// Gets the current Snarl revision (build) number (V39) -/// Returns the build version number, or M_RESULT on failure. - -LONG32 SnarlInterface::GetRevision() -{ - SNARLSTRUCT ss; - ss.Cmd = SNARL_GET_REVISION; - ss.LngData2 = 0xFFFE; - - return Send(ss); + return szUTF8; } - -//----------------------------------------------------------------------------- -// snAddClass() - -/// (V39) - -M_RESULT SnarlInterface::AddClass(LPCSTR Class, LPCSTR Description, SNARL_CLASS_FLAGS Flags, LPCSTR DefaultTitle, LPCSTR DefaultIcon, LONG32 DefaultTimeout) +void SnarlInterface::PackData(BYTE* data, LPCSTR format, ...) { - SNARLSTRUCT ss; - ss.Cmd = SNARL_ADD_CLASS; - ss.LngData2 = Flags; - ss.Timeout = GetCurrentProcessId(); - - StringCbCopyExA((LPSTR)&ss.Text, SNARL_STRING_LENGTH, Class, NULL, NULL, STRSAFE_IGNORE_NULLS); - StringCbCopyExA((LPSTR)&ss.Title, SNARL_STRING_LENGTH, Description, NULL, NULL, STRSAFE_IGNORE_NULLS); + // Always zero array - Used to clear the array in member functions + ZeroMemory(data, SnarlPacketDataSize); - LONG32 result = Send(ss); + // Return if format string is empty + if (format == NULL || format[0] == 0) + return; - if (static_cast(result) == M_OK) - { - SetClassDefault(Class, SNARL_ATTRIBUTE_TITLE, DefaultTitle); - SetClassDefault(Class, SNARL_ATTRIBUTE_ICON, DefaultIcon); - if (DefaultTimeout > 0) { - char str[64] = {0}; - StringCbPrintfA((LPSTR)&str, sizeof(str), "%d", DefaultTimeout); - SetClassDefault(Class, SNARL_ATTRIBUTE_TIMEOUT, str); - } - - return M_OK; - } - else - return M_FAILED; -} - -M_RESULT SnarlInterface::AddClass(LPCWSTR Class, LPCWSTR Description, SNARL_CLASS_FLAGS Flags, LPCWSTR DefaultTitle, LPCWSTR DefaultIcon, LONG32 DefaultTimeout) -{ - LPCSTR szClass = WideToUTF8(Class); - LPCSTR szDescription = WideToUTF8(Description); - LPCSTR szDefaultTitle = WideToUTF8(DefaultTitle); - LPCSTR szDefaultIcon = WideToUTF8(DefaultIcon); + int cchStrTextLen = 0; + va_list args; + va_start(args, format); - M_RESULT result = AddClass(szClass, szDescription, Flags, szDefaultTitle, szDefaultIcon, DefaultTimeout); - - delete [] szClass; - delete [] szDescription; - delete [] szDefaultTitle; - delete [] szDefaultIcon; - - return result; -} + // Get size of buffer + cchStrTextLen = _vscprintf(format, args) + 1; // + NULL + if (cchStrTextLen <= 1) + return; -//----------------------------------------------------------------------------- -// Private functions -//----------------------------------------------------------------------------- - -template -LONG32 SnarlInterface::Send(T ss) -{ - DWORD_PTR nReturn = M_FAILED; + // Create formated string - _TRUNCATE will ensure zero terminated + _vsnprintf_s((char*)data, SnarlPacketDataSize, _TRUNCATE, format, args); - HWND hWnd = GetSnarlWindow(); - if (IsWindow(hWnd)) - { - COPYDATASTRUCT cds; - cds.dwData = 2; - cds.cbData = sizeof(ss); - cds.lpData = &ss; - - if (SendMessageTimeout(hWnd, WM_COPYDATA, (WPARAM)m_hwndFrom, (LPARAM)&cds, SMTO_ABORTIFHUNG | SMTO_NOTIMEOUTIFNOTHUNG, 1000, &nReturn) == 0) - { - if (GetLastError() == ERROR_TIMEOUT) - nReturn = M_TIMED_OUT; - } - } - - return static_cast(nReturn); + va_end(args); } -//----------------------------------------------------------------------------- - -// Remember to : delete [] returned string - -LPSTR SnarlInterface::WideToUTF8(LPCWSTR szWideStr) -{ - if (szWideStr == NULL) - return NULL; - - int nSize = WideCharToMultiByte(CP_UTF8, 0, szWideStr, -1, NULL, 0, NULL, NULL); - LPSTR szUTF8 = new char[nSize]; - WideCharToMultiByte(CP_UTF8, 0, szWideStr, -1, szUTF8, nSize, NULL, NULL); - - return szUTF8; -} +}} // namespace Snarl::V41 diff --git a/3rdParty/Snarl/SnarlInterface.h b/3rdParty/Snarl/SnarlInterface.h index 137d597..9440451 100644 --- a/3rdParty/Snarl/SnarlInterface.h +++ b/3rdParty/Snarl/SnarlInterface.h @@ -1,218 +1,276 @@ -#ifndef SNARL_INTERFACE -#define SNARL_INTERFACE +#ifndef SNARL_INTERFACE_V41 +#define SNARL_INTERFACE_V41 #include #include -#include +#include + +#ifndef SMTO_NOTIMEOUTIFNOTHUNG + #define SMTO_NOTIMEOUTIFNOTHUNG 8 +#endif namespace Snarl { + namespace V41 { - static const LPCTSTR SNARL_GLOBAL_MSG = _T("SnarlGlobalEvent"); - static const LPCTSTR SNARL_APP_MSG = _T("SnarlAppMessage"); + static const LPCTSTR SnarlWindowClass = _T("w>Snarl"); + static const LPCTSTR SnarlWindowTitle = _T("Snarl"); - static const int SNARL_STRING_LENGTH = 1024; - static const int SNARL_UNICODE_LENGTH = SNARL_STRING_LENGTH / 2; + static const LPCTSTR SnarlGlobalMsg = _T("SnarlGlobalEvent"); + static const LPCTSTR SnarlAppMsg = _T("SnarlAppMessage"); - static const LONG32 SNARL_LAUNCHED = 1; // Snarl has just started running - static const LONG32 SNARL_QUIT = 2; // Snarl is about to stop running - static const LONG32 SNARL_ASK_APPLET_VER = 3; // (R1.5) Reserved for future use - static const LONG32 SNARL_SHOW_APP_UI = 4; // (R1.6) Application should show its UI + static const int SnarlPacketDataSize = 4096; - static const LONG32 SNARL_NOTIFICATION_CLICKED = 32; // notification was right-clicked by user - static const LONG32 SNARL_NOTIFICATION_TIMED_OUT = 33; - static const LONG32 SNARL_NOTIFICATION_ACK = 34; // notification was left-clicked by user - static const LONG32 SNARL_NOTIFICATION_MENU = 35; // V39 - menu item selected - static const LONG32 SNARL_NOTIFICATION_MIDDLE_BUTTON = 36; // V39 - notification middle-clicked by user - static const LONG32 SNARL_NOTIFICATION_CLOSED = 37; // V39 - user clicked the close gadget + // Enums put in own namespace, because ANSI C++ doesn't decorate enums with tagname :( + namespace SnarlEnums { - static const LONG32 SNARL_NOTIFICATION_CANCELLED = SNARL_NOTIFICATION_CLICKED; // Added in R1.6 + /// + /// Global event identifiers. + /// Identifiers marked with a '*' are sent by Snarl in two ways: + /// 1. As a broadcast message (uMsg = 'SNARL_GLOBAL_MSG') + /// 2. To the window registered in snRegisterConfig() or snRegisterConfig2() + /// (uMsg = reply message specified at the time of registering) + /// In both cases these values appear in wParam. + /// + /// Identifiers not marked are not broadcast; they are simply sent to the application's registered window. + /// + enum GlobalEvent + { + SnarlLaunched = 1, // Snarl has just started running* + SnarlQuit = 2, // Snarl is about to stop running* + SnarlAskAppletVer = 3, // (R1.5) Reserved for future use + SnarlShowAppUi = 4 // (R1.6) Application should show its UI + }; - static const DWORD WM_SNARLTEST = WM_USER + 237; // note hardcoded WM_USER value! + /// + /// Message event identifiers. + /// These are sent by Snarl to the window specified in RegisterApp() when the + /// Snarl Notification raised times out or the user clicks on it. + /// + enum MessageEvent + { + NotificationClicked = 32, // Notification was right-clicked by user + NotificationCancelled = 32, // Added in V37 (R1.6) -- same value, just improved the meaning of it + NotificationTimedOut = 33, // + NotificationAck = 34, // Notification was left-clicked by user + NotificationMenu = 35, // Menu item selected (V39) + NotificationMiddleButton = 36, // Notification middle-clicked by user (V39) + NotificationClosed = 37 // User clicked the close gadget (V39) + }; - // -------------------------------------------------------------------- - - typedef enum M_RESULT { - M_ABORTED = 0x80000007, - M_ACCESS_DENIED = 0x80000009, - M_ALREADY_EXISTS = 0x8000000C, - M_BAD_HANDLE = 0x80000006, - M_BAD_POINTER = 0x80000005, - M_FAILED = 0x80000008, - M_INVALID_ARGS = 0x80000003, - M_NO_INTERFACE = 0x80000004, - M_NOT_FOUND = 0x8000000B, - M_NOT_IMPLEMENTED = 0x80000001, - M_OK = 0x00000000, - M_OUT_OF_MEMORY = 0x80000002, - M_TIMED_OUT = 0x8000000A - }; + /// + /// Error values returned by calls to GetLastError(). + /// + enum SnarlStatus + { + Success = 0, - enum SNARL_COMMANDS { - SNARL_SHOW = 1, - SNARL_HIDE, - SNARL_UPDATE, - SNARL_IS_VISIBLE, - SNARL_GET_VERSION, - SNARL_REGISTER_CONFIG_WINDOW, - SNARL_REVOKE_CONFIG_WINDOW, - - /* R1.6 onwards */ - SNARL_REGISTER_ALERT, - SNARL_REVOKE_ALERT, // for future use - SNARL_REGISTER_CONFIG_WINDOW_2, - SNARL_GET_VERSION_EX, - SNARL_SET_TIMEOUT, - - /* following introduced in Snarl V39 (R2.1) */ - SNARL_SET_CLASS_DEFAULT, - SNARL_CHANGE_ATTR, - SNARL_REGISTER_APP, - SNARL_UNREGISTER_APP, - SNARL_ADD_CLASS, - - /* extended commands (all use SNARLSTRUCTEX) */ - SNARL_EX_SHOW = 0x20, - SNARL_SHOW_NOTIFICATION // V39 - }; - - static const SNARL_COMMANDS SNARL_GET_REVISION = SNARL_REVOKE_ALERT; - - typedef enum SNARL_APP_FLAGS { - SNARL_APP_HAS_PREFS = 1, - SNARL_APP_HAS_ABOUT = 2 - }; - - static const LONG32 SNARL_APP_PREFS = 1; - static const LONG32 SNARL_APP_ABOUT = 2; + ErrorFailed = 101, // miscellaneous failure + ErrorUnknownCommand, // specified command not recognised + ErrorTimedOut, // Snarl took too long to respond - - /* --------------- V39 additions --------------- */ - - /* snAddClass() flags */ - enum SNARL_CLASS_FLAGS { - SNARL_CLASS_ENABLED = 0, - SNARL_CLASS_DISABLED = 1, - SNARL_CLASS_NO_DUPLICATES = 2, // means Snarl will suppress duplicate notifications - SNARL_CLASS_DELAY_DUPLICATES = 4 // means Snarl will suppress duplicate notifications within a pre-set time period - }; + ErrorArgMissing = 109, // required argument missing + ErrorSystem, // internal system error - /* Class attributes */ - typedef enum SNARL_ATTRIBUTES { - SNARL_ATTRIBUTE_TITLE = 1, - SNARL_ATTRIBUTE_TEXT, - SNARL_ATTRIBUTE_ICON, - SNARL_ATTRIBUTE_TIMEOUT, - SNARL_ATTRIBUTE_SOUND, - SNARL_ATTRIBUTE_ACK, // file to run on ACK - SNARL_ATTRIBUTE_MENU - }; + ErrorNotRunning = 201, // Snarl handling window not found + ErrorNotRegistered, // + ErrorAlreadyRegistered, // not used yet; RegisterApp() returns existing token + ErrorClassAlreadyExists, // not used yet; AddClass() returns existing token + ErrorClassBlocked, + ErrorClassNotFound, + ErrorNotificationNotFound + }; - /* ------------------- end of V39 additions ------------------ */ - - struct SNARLSTRUCT { - SNARL_COMMANDS Cmd; - LONG32 Id; - LONG32 Timeout; - LONG32 LngData2; - char Title[SNARL_STRING_LENGTH]; - char Text[SNARL_STRING_LENGTH]; - char Icon[SNARL_STRING_LENGTH]; - }; + /// + /// Application flags - features this app supports. + /// + enum AppFlags + { + AppDefault = 0, + AppHasPrefs = 1, + AppHasAbout = 2, + AppIsWindowless = 0x8000 + }; - struct SNARLSTRUCTEX { - SNARL_COMMANDS Cmd; - LONG32 Id; - LONG32 Timeout; - LONG32 LngData2; - char Title[SNARL_STRING_LENGTH]; - char Text[SNARL_STRING_LENGTH]; - char Icon[SNARL_STRING_LENGTH]; - - char Class[SNARL_STRING_LENGTH]; - char Extra[SNARL_STRING_LENGTH]; - char Extra2[SNARL_STRING_LENGTH]; - LONG32 Reserved1; - LONG32 Reserved2; + enum SnarlCommand + { + RegisterApp = 1, + UnregisterApp, + UpdateApp, + SetCallback, + AddClass, + RemoveClass, + Notify, + UpdateNotification, + HideNotification, + IsNotificationVisible, + LastError // deprecated but retained for backwards compatability + }; + } + + struct SnarlMessage + { + SnarlEnums::SnarlCommand Command; + LONG32 Token; + BYTE PacketData[SnarlPacketDataSize]; }; + static const DWORD WM_SNARLTEST = WM_USER + 237; // ------------------------------------------------------------------------ - // SnarlInterface class definition + /// SnarlInterface class definition // ------------------------------------------------------------------------ - class SnarlInterface { public: SnarlInterface(); ~SnarlInterface(); - - static HWND GetSnarlWindow(); - static LONG32 GetGlobalMsg(); - LPTSTR AllocateString(size_t n) { return new TCHAR[n]; } - void FreeString(LPCTSTR str) { delete [] str; str = NULL; } + void FreeString(LPTSTR str) { delete [] str; str = NULL; } + void FreeString(LPCTSTR str) { delete [] str; } + + /// Register application with Snarl. + /// The application token or 0 on failure. + /// The application token is saved in SnarlInterface member variable, so just use return value to check for error. + LONG32 RegisterApp(LPCSTR signature, LPCSTR title, LPCSTR icon, HWND hWndReply = NULL, LONG32 msgReply = 0, SnarlEnums::AppFlags flags = SnarlEnums::AppDefault); + LONG32 RegisterApp(LPCWSTR signature, LPCWSTR title, LPCWSTR icon, HWND hWndReply = NULL, LONG32 msgReply = 0, SnarlEnums::AppFlags flags = SnarlEnums::AppDefault); + + /// Unregister application with Snarl when application is closing. + /// 0 on failure. + LONG32 UnregisterApp(); + + /// Update information provided when calling RegisterApp. + /// 0 on failure. + LONG32 UpdateApp(LPCSTR title = NULL, LPCSTR icon = NULL); + LONG32 UpdateApp(LPCWSTR title = NULL, LPCWSTR icon = NULL); + + /// Add a notification class to Snarl. + /// 0 on failure. + LONG32 AddClass(LPCSTR className, LPCSTR description, bool enabled = true); + LONG32 AddClass(LPCWSTR className, LPCWSTR description, bool enabled = true); + /// Remove a notification class added with AddClass(). + /// 0 on failure. + LONG32 RemoveClass(LPCSTR className, bool forgetSettings = false); + LONG32 RemoveClass(LPCWSTR className, bool forgetSettings = false); - LONG32 ShowMessage(LPCSTR szTitle, LPCSTR szText, LONG32 timeout = 0, LPCSTR szIconPath = "", HWND hWndReply = NULL, WPARAM uReplyMsg = 0); - LONG32 ShowMessage(LPCWSTR szTitle, LPCWSTR szText, LONG32 timeout = 0, LPCWSTR szIconPath = L"", HWND hWndReply = NULL, WPARAM uReplyMsg = 0); - LONG32 ShowMessageEx(LPCSTR szClass, LPCSTR szTitle, LPCSTR szText, LONG32 timeout = 0, LPCSTR szIconPath = "", HWND hWndReply = NULL, WPARAM uReplyMsg = 0, LPCSTR szSoundFile = ""); - LONG32 ShowMessageEx(LPCWSTR szClass, LPCWSTR szTitle, LPCWSTR szText, LONG32 timeout = 0, LPCWSTR szIconPath = L"", HWND hWndReply = NULL, WPARAM uReplyMsg = 0, LPCWSTR szSoundFile = L""); - - LPCTSTR GetAppPath(); // ** Remember to FreeString when finished with the string ! - LPCTSTR GetIconsPath(); // ** Remember to FreeString when finished with the string ! - - BOOL GetVersion(WORD* Major, WORD* Minor); - LONG32 GetVersionEx(); - BOOL HideMessage(); - BOOL HideMessage(LONG32 Id); - BOOL IsMessageVisible(); - BOOL IsMessageVisible(LONG32 Id); - M_RESULT RegisterAlert(LPCSTR szAppName, LPCSTR szClass); - M_RESULT RegisterAlert(LPCWSTR szAppName, LPCWSTR szClass); - M_RESULT RegisterConfig(HWND hWnd, LPCSTR szAppName, LONG32 replyMsg); - M_RESULT RegisterConfig(HWND hWnd, LPCWSTR szAppName, LONG32 replyMsg); - M_RESULT RegisterConfig2(HWND hWnd, LPCSTR szAppName, LONG32 replyMsg, LPCSTR szIcon); - M_RESULT RegisterConfig2(HWND hWnd, LPCWSTR szAppName, LONG32 replyMsg, LPCWSTR szIcon); - M_RESULT RevokeConfig(HWND hWnd); - M_RESULT SetTimeout(LONG32 Timeout); - M_RESULT SetTimeout(LONG32 Id, LONG32 Timeout); - M_RESULT UpdateMessage(LPCSTR szTitle, LPCSTR szText, LPCSTR szIconPath = ""); - M_RESULT UpdateMessage(LPCWSTR szTitle, LPCWSTR szText, LPCWSTR szIconPath = L""); - M_RESULT UpdateMessage(LONG32 Id, LPCSTR szTitle, LPCSTR szText, LPCSTR szIconPath = ""); - M_RESULT UpdateMessage(LONG32 Id, LPCWSTR szTitle, LPCWSTR szText, LPCWSTR szIconPath = L""); + /// Remove all notification classes in one call. + /// 0 on failure. + LONG32 RemoveAllClasses(bool forgetSettings = false); - /* V39 */ - M_RESULT AddClass(LPCSTR Class, LPCSTR Description = NULL, SNARL_CLASS_FLAGS Flags = SNARL_CLASS_ENABLED, LPCSTR DefaultTitle = NULL, LPCSTR DefaultIcon = NULL, LONG32 DefaultTimeout = 0); - M_RESULT AddClass(LPCWSTR Class, LPCWSTR Description = NULL, SNARL_CLASS_FLAGS Flags = SNARL_CLASS_ENABLED, LPCWSTR DefaultTitle = NULL, LPCWSTR DefaultIcon = NULL, LONG32 DefaultTimeout = 0); - M_RESULT ChangeAttribute(SNARL_ATTRIBUTES Attr, LPCSTR Value); - M_RESULT ChangeAttribute(SNARL_ATTRIBUTES Attr, LPCWSTR Value); - M_RESULT ChangeAttribute(LONG32 Id, SNARL_ATTRIBUTES Attr, LPCSTR Value); - M_RESULT ChangeAttribute(LONG32 Id, SNARL_ATTRIBUTES Attr, LPCWSTR Value); - LONG32 GetAppMsg(); - LONG32 GetRevision(); + /// Show a Snarl notification. + /// Returns the notification token or 0 on failure. + /// You can use to get the last token. + LONG32 EZNotify(LPCSTR className, LPCSTR title, LPCSTR text, LONG32 timeout = -1, LPCSTR icon = NULL, LONG32 priority = 0, LPCSTR acknowledge = NULL, LPCSTR value = NULL); + LONG32 EZNotify(LPCWSTR className, LPCWSTR title, LPCWSTR text, LONG32 timeout = -1, LPCWSTR icon = NULL, LONG32 priority = 0, LPCWSTR acknowledge = NULL, LPCWSTR value = NULL); + + /// + /// Show a Snarl notification. + /// This function requires that you write your own packet data. + /// + /// Returns the notification token or 0 on failure. + /// You can use to get the last token. + LONG32 Notify(LPCSTR className, LPCSTR packetData); + LONG32 Notify(LPCWSTR className, LPCWSTR packetData); + + /// Update the text or other parameters of a visible Snarl notification. + /// 0 on failure. + LONG32 EZUpdate(LONG32 msgToken, LPCSTR title = NULL, LPCSTR text = NULL, LONG32 timeout = -1, LPCSTR icon = NULL); + LONG32 EZUpdate(LONG32 msgToken, LPCWSTR title = NULL, LPCWSTR text = NULL, LONG32 timeout = -1, LPCWSTR icon = NULL); - M_RESULT RegisterApp(LPCSTR Application, LPCSTR SmallIcon, LPCSTR LargeIcon, HWND hWnd = 0, LONG32 ReplyMsg = 0); - M_RESULT RegisterApp(LPCWSTR Application, LPCWSTR SmallIcon, LPCWSTR LargeIcon, HWND hWnd = 0, LONG32 ReplyMsg = 0); - void SetAsSnarlApp(HWND hWndOwner, SNARL_APP_FLAGS Flags = (SNARL_APP_FLAGS)(SNARL_APP_HAS_ABOUT | SNARL_APP_HAS_PREFS)); - M_RESULT SetClassDefault(LPCSTR Class, SNARL_ATTRIBUTES Attr, LPCSTR Value); - M_RESULT SetClassDefault(LPCWSTR Class, SNARL_ATTRIBUTES Attr, LPCWSTR Value); - LONG32 ShowNotification(LPCSTR Class, LPCSTR Title = NULL, LPCSTR Text = NULL, LONG32 Timeout = 0, LPCSTR Icon = NULL, HWND hWndReply = NULL, LONG32 uReplyMsg = 0, LPCSTR Sound = NULL); - LONG32 ShowNotification(LPCWSTR Class, LPCWSTR Title = NULL, LPCWSTR Text = NULL, LONG32 Timeout = 0, LPCWSTR Icon = NULL, HWND hWndReply = NULL, LONG32 uReplyMsg = 0, LPCWSTR Sound = NULL); - M_RESULT UnregisterApp(); + /// + /// Update the text or other parameters of a visible Snarl notification. + /// This function requires that you write your own packet data. + /// + /// 0 on failure. + LONG32 Update(LONG32 msgToken, LPCSTR packetData); + LONG32 Update(LONG32 msgToken, LPCWSTR packetData); + + /// Hide a Snarl notification. + /// 0 on failure. + LONG32 Hide(LONG32 msgToken); + + /// Test if a Snarl notification is visible. + /// Returns -1 if message is visible. 0 if not visible or if an error occured. + LONG32 IsVisible(LONG32 msgToken); + + /// Get the last error from Snarl. Call after other functions return 0 to know why it failed. + /// Returns one of the SnarlEnums::SnarlStatus values. + SnarlEnums::SnarlStatus GetLastError(); + + /// Get Snarl version, if it is running. + /// Returns a number indicating Snarl version. + LONG32 GetVersion(); + + /// + /// Get the path to where Snarl is installed. + /// ** Remember to call on the returned string !!! + /// + /// Returns the path to where Snarl is installed. + /// This is a V39 API method. + LPCTSTR GetAppPath(); + + /// + /// Get the path to where the default Snarl icons are located. + /// ** Remember to call on the returned string !!! + /// + /// Returns the path to where the default Snarl icons are located. + /// This is a V39 API method. + LPCTSTR GetIconsPath(); + + /// GetLastMsgToken() returns token of the last message sent to Snarl. + /// Returns message token of last message. + /// This function is not in the official API! + LONG32 GetLastMsgToken() const; - LONG32 GetLastMessageId() { return m_nLastMessageId; } + /// Check whether Snarl is running + /// Returns true if Snarl system was found running. + static BOOL IsSnarlRunning(); + + /// + /// Returns the value of Snarl's global registered message. + /// Notes: + /// Snarl registers SNARL_GLOBAL_MSG during startup which it then uses to communicate + /// with all running applications through a Windows broadcast message. This function can + /// only fail if for some reason the Windows RegisterWindowMessage() function fails + /// - given this, this function *cannnot* be used to test for the presence of Snarl. + /// + /// A 16-bit value (translated to 32-bit) which is the registered Windows message for Snarl. + static UINT Broadcast(); + /// Returns the global Snarl Application message (V39) + /// Returns Snarl application registered message. + static UINT AppMsg(); + + /// Returns a handle to the Snarl Dispatcher window (V37) + /// Returns handle to Snarl Dispatcher window, or zero if it's not found. + /// This is now the preferred way to test if Snarl is actually running. + static HWND GetSnarlWindow(); + private: - template LONG32 Send(T ss); + /// Send message to Snarl. + /// Return zero on failure. + LONG32 Send(SnarlMessage msg); + + /// Convert a unicode string to UTF8 + /// Returns pointer to the new string - Remember to delete [] returned string ! + /// Remember to delete [] returned string !!! LPSTR WideToUTF8(LPCWSTR szWideStr); - - LONG32 m_nLastMessageId; - HWND m_hwndFrom; // set during snRegisterConfig() or snRegisterConfig2() - }; + /// Pack data into the PackedData member field. + /// Should always be a pointer to the PackedData field + /// The format string. Can be NULL or "" to just zero PackedData! + /// Variable number of objects to convert + void PackData(BYTE* data, LPCSTR format, ...); + + LONG32 appToken; + LONG32 lastMsgToken; + SnarlEnums::SnarlStatus localError; + + }; // class -} + } // namespace V41 +} // namespace Snarl -#endif // SNARL_INTERFACE +#endif // SNARL_INTERFACE_V41 diff --git a/SwifTools/Notifier/SnarlNotifier.cpp b/SwifTools/Notifier/SnarlNotifier.cpp index eb4addc..9e82340 100644 --- a/SwifTools/Notifier/SnarlNotifier.cpp +++ b/SwifTools/Notifier/SnarlNotifier.cpp @@ -19,14 +19,14 @@ namespace Swift { SnarlNotifier::SnarlNotifier(const String& name, Win32NotifierWindow* window, const boost::filesystem::path& icon) : window(window) { window->onMessageReceived.connect(boost::bind(&SnarlNotifier::handleMessageReceived, this, _1)); - snarl.RegisterConfig2(window->getID(), name.getUTF8Data(), 0, icon.string().c_str()); + snarl.RegisterApp(name.getUTF8Data(), name.getUTF8Data(), icon.string().c_str(), window->getID(), SWIFT_SNARLNOTIFIER_MESSAGE_ID); foreach(Notifier::Type type, getAllTypes()) { - snarl.RegisterAlert(name.getUTF8Data(), typeToString(type).getUTF8Data()); + snarl.AddClass(typeToString(type).getUTF8Data(), typeToString(type).getUTF8Data()); } } SnarlNotifier::~SnarlNotifier() { - snarl.RevokeConfig(window->getID()); + snarl.UnregisterApp(); window->onMessageReceived.disconnect(boost::bind(&SnarlNotifier::handleMessageReceived, this, _1)); if (!notifications.empty()) { std::cerr << "Warning: " << notifications.size() << " Snarl notifications pending" << std::endl; @@ -34,8 +34,13 @@ SnarlNotifier::~SnarlNotifier() { } void SnarlNotifier::showMessage(Type type, const String& subject, const String& description, const boost::filesystem::path& picture, boost::function callback) { - int timeout = (type == Type::IncomingMessage || type == Type::SystemMessage) ? DEFAULT_MESSAGE_NOTIFICATION_TIMEOUT_SECONDS : DEFAULT_STATUS_NOTIFICATION_TIMEOUT_SECONDS; - int notificationID = snarl.ShowMessageEx(typeToString(type).getUTF8Data(), subject.getUTF8Data(), description.getUTF8Data(), timeout, picture.string().c_str(), window->getID(), SWIFT_SNARLNOTIFIER_MESSAGE_ID); + int timeout = (type == IncomingMessage || type == SystemMessage) ? DEFAULT_MESSAGE_NOTIFICATION_TIMEOUT_SECONDS : DEFAULT_STATUS_NOTIFICATION_TIMEOUT_SECONDS; + int notificationID = snarl.EZNotify( + typeToString(type).getUTF8Data(), + subject.getUTF8Data(), + description.getUTF8Data(), + timeout, + picture.string().c_str()); if (notificationID > 0) { notifications.insert(std::make_pair(notificationID, callback)); } @@ -44,11 +49,11 @@ void SnarlNotifier::showMessage(Type type, const String& subject, const String& void SnarlNotifier::handleMessageReceived(MSG* message) { if (message->message == SWIFT_SNARLNOTIFIER_MESSAGE_ID) { int action = message->wParam; - if (action == Snarl::SNARL_NOTIFICATION_TIMED_OUT || action == Snarl::SNARL_NOTIFICATION_ACK || action == Snarl::SNARL_NOTIFICATION_CLOSED) { + if (action == Snarl::V41::SnarlEnums::NotificationTimedOut || action == Snarl::V41::SnarlEnums::NotificationAck || action == Snarl::V41::SnarlEnums::NotificationClosed) { int notificationID = message->lParam; NotificationsMap::iterator i = notifications.find(notificationID); if (i != notifications.end()) { - if (action == Snarl::SNARL_NOTIFICATION_ACK) { + if (action == Snarl::V41::SnarlEnums::NotificationAck) { i->second(); } notifications.erase(i); diff --git a/SwifTools/Notifier/SnarlNotifier.h b/SwifTools/Notifier/SnarlNotifier.h index 2350e29..8470326 100644 --- a/SwifTools/Notifier/SnarlNotifier.h +++ b/SwifTools/Notifier/SnarlNotifier.h @@ -25,7 +25,7 @@ namespace Swift { void handleMessageReceived(MSG* message); private: - Snarl::SnarlInterface snarl; + Snarl::V41::SnarlInterface snarl; Win32NotifierWindow* window; typedef std::map > NotificationsMap; NotificationsMap notifications; -- cgit v0.10.2-6-g49f6