summaryrefslogtreecommitdiff
path: root/plugins/KeyboardNotify/src/main.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'plugins/KeyboardNotify/src/main.cpp')
-rw-r--r--plugins/KeyboardNotify/src/main.cpp1448
1 files changed, 1448 insertions, 0 deletions
diff --git a/plugins/KeyboardNotify/src/main.cpp b/plugins/KeyboardNotify/src/main.cpp
new file mode 100644
index 0000000000..e82cc2cfdc
--- /dev/null
+++ b/plugins/KeyboardNotify/src/main.cpp
@@ -0,0 +1,1448 @@
+/*
+ KeyboardNotify plugin v1.5 for Miranda IM
+ _________________________________________
+
+ Copyright (C) 2002,2003 Martin Öberg
+ Copyright (C) 2004 Std
+ Copyright (C) 2005,2006 TioDuke (tioduke@yahoo.ca)
+
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License
+ as published by the Free Software Foundation; either version 2
+ of the License, or (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+
+ Description
+ -----------
+ This plugin for Miranda-IM notifies user of specified events (as incoming messages,
+ incoming files, incoming URLs or other events). This plugin is based on the original one
+ by Martin Öberg (aka strickz) and Std's modifications (mainly the idea of using direct
+ port handling using a driver).
+ It has many options allowing:
+ a) To select on which events to react
+ b) Under which conditions (eg: fullscreen mode, ScreenSaver running, workstation locked)
+ c) To act only if the protocol receiving the event is under specified status
+ d) For message events you can choose to be notified if the message window is open or not
+ e) A notification feature allowing to be notified of pending events (unopen events)
+ after specified period of time
+ f) To select method for stopping the blinking (after x secs, if Miranda is re-attended,
+ if Windows is re-attended, if all notified events are opened or when the notify conditions
+ end)
+ g) And several flashing options: select leds to blink, effects (all at the same time,
+ in turn, in sequence and like KITT!), preview button
+ It was designed to be flexible and performing several different tasks. It can be
+ configured to act like the original one and has several functions from other Miranda's
+ keyboard notifier plugins.
+ It also provides a service to allow third party plugins use its notifier abilities.
+
+ Options
+ -------
+ Options page Options->Plugins->Keyboard Flash. Tabbed: Protocols, Rules (when), Flashing (how), Themes and Ignore.
+
+ Thanks
+ ------
+ - Pete for the numerous patches he sent, actively helping to improve the code and
+ functionality
+ - UnregistereD for great help in solving problem with Windows activity detection
+ - Slacktarn, Sir_qwerty and Tweety for giving great help with ideas (most of the new
+ features included in this plugin were suggested by them) and testing
+ - The authors of AAA, PopUp+, KeyScrollNotify, original KeyboardNotify, Neweventnotify,
+ IEView, NGEventNotify for part of their code used in this plugin.
+ - Vampik fot the InTurn flashing option
+ - Miranda IM developers for this amazing program
+ - all other people from Miranda community
+
+ History
+ -------
+ 1.5.7.7:
+ [!] Added support for Miranda 0.8.x.x.
+ 1.5.7.6:
+ [!] Fixed bug in Ignore module.
+ 1.5.7.5:
+ [!] Updated TriggerPlugin support for latest version.
+ 1.5.7.4:
+ [*] Updated screenshot
+ [*] Minor code cleaning
+ 1.5.7.3:
+ [+] Added xstatus support
+ 1.5.7.2:
+ [+] Added per contact Ignore options
+ 1.5.7.1:
+ [!] Fix in Options for themes under WinXP+ (really essential feature)
+ 1.5.7.0:
+ [+] Added support for Trigger plugin
+ 1.5.6.3:
+ [-] Removed device presence validation: it is not needed now that the plugin works on USB (thanks Nick, aka Aestetic)
+ [+] Added a new service to the API for 'normalizing' a custom flashing sequence string
+ [-] Simplified the API (the extended version of the start blink service is no longer needed).
+ 1.5.6.2:
+ [!] Fixed problem while trying to detect if message window is in foreground.
+ 1.5.6.1:
+ [!] Fixed bug with keypress emulation and "until Windows is re-attended" option.
+ 1.5.6.0:
+ [+] Option to emulate keypresses (for the USB people)
+ [*] Changed the emergency key (to make it stop with PAUSE instead of SCROLL LOCK key).
+ 1.5.5.4:
+ [*] Improved ListView control handling
+ [*] Changed the default values (for the sake of new users).
+ 1.5.5.3:
+ [*] More code optimization.
+ 1.5.5.2:
+ [+] Support for Update plugin.
+ 1.5.5.1:
+ [!] Minor source fixes.
+ 1.5.5.0:
+ [+] New 'notify when' option: while defined programs are running (just like gamerstatus)
+ [+] Extended the API to add two new services to disable and re-enable keyboard notifications (for use by bosskey plugin).
+ 1.5.4.4:
+ [!] Fixed (hopefully) problems with some system configurations (with PS2 keyboards) where the KeyboardClass0 device was not the apropriate one (thanks pete!)
+ [+] Extended the plugin API (requested bt tweety).
+ 1.5.4.3:
+ [!] Fixed some compatibility issues with nconvers++ (thank you donatas for your help).
+ 1.5.4.2:
+ [!] Fixed problem with Windows' activity detection under Win9X when using other plugins that do the same.
+ [!] Fixed crash caused by incoming authorisation requests when metacontacts was enabled.
+ 1.5.4.1:
+ [!] Some corrections on third party plugins events handling (now they are more assimilated to the 'other events')
+ [*] Some code cleaning
+ [!] Fixed problem with first message in Metacontacts recognition while checking for pending events (thank you again NirG)
+ 1.5.4.0:
+ [+] New plugin API (thank you CriS for your ideas and great help)
+ [!] Added Offline status to status check list (thank you Slaktarn for finding it).
+ 1.5.3.4:
+ [!] Fixed Metacontacts recognition in checking and counting of pending events (thank you NirG for finding the problem)
+ [!] Fixed problems with multiple instances of the plugin running (thank you tweety for reporting and testing).
+ 1.5.3.3:
+ [!] Changed behaviour of Preview button to make it independent of the rules' options.
+ 1.5.3.2:
+ [+] New dialog to asign leds to specific events for the trillian-like sequences.
+ 1.5.3.1:
+ [!] Fixed bug of loosing any other until setting when selecting 'Events are opened'.
+ 1.5.3.0:
+ [+] Applied pete's patches (thank you very much for your great work)
+ - Use of GetLastInputInfo when possible for detecting Windows' activity
+ - Made Windows' mouse hooks also aware of mouse clicking
+ - Made Miranda re-attended option react on windows restoring and ignoring mouse hovering an unfocused window
+ - New option for message events to avoid blinking if message window is focused
+ - Made the plugin handle metacontact's special issues
+ [!] Use of the new message API for windows detection when possible
+ [+] New message event option to check last message timestamp (requested by D46MD)
+ [+] Possibility of choosing more than one flash until option at the same time
+ [+] New flashing effect to make the leds blink accordingly to number of events
+ [+] Possibility of selecting/unselecting protocols (requested by tweety, usuful to avoid flashing on some protocols as rss)
+ 1.5.2.2:
+ [!] scriver's message window detection (thanks D46MD for your great help)
+ [!] corrected 'flash until' checking accordingly to pete's patch (thank you)
+ 1.5.2.1:
+ [!] nconvers++'s message window detection
+ [!] checked window detection for srmm, scriver, sramm and srmm_mod
+ 1.5.2.0:
+ [+] Custom theme support
+ [-] Custom order history
+ 1.5.1.0:
+ [+] Custom order effect
+ [+] Custom order history
+ 1.5.0.0:
+ [+] Drivers aren't needed anymore
+ [+] Status selection option
+ [+] Miranda/Windows activity detection (thank you Peter Boon)
+ [+] 'Opened events' stop method
+ [+] x seconds stop method
+ [+] Hooking database event for detecting incoming events
+ [+] Notifier option for pending events
+ [+] Checkbox for enabling disabling open messages notification
+ [+] In sequence and KIT flashing effects
+ [+] Preview button
+ [+] Tabbed options
+ [!] Several corrections/improvements in options page
+ [!] Not selected leds will preserve their original state
+ 1.4.1.0: (by me and Vampik)
+ [+] Notify on fullscreen, screensaver, worksation locked
+ [!] Try to improve Win98 keysimulation routines
+ [+] Added InTurn effect (thank you Vampik)
+ [!] Corrected speed of blinking (thank you Vampik)
+ 1.4.0.0: (by Std, unreleased)
+ [+] Added direct port handling using PortTalk.sys driver
+ 1.3.0.0: (by strickz)
+ This is strickz' final release. It still uses keypress simulation. It was nice (thanks *g*)
+
+
+*/
+
+#define WIN32_LEAN_AND_MEAN
+#define _WIN32_WINNT 0x0500
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <windows.h>
+#include "flash.h"
+#include "ignore.h"
+#include "keyboard.h"
+#include "trigger.h"
+#include "constants.h"
+#include "protolist.h"
+#include "EnumProc.h"
+#include "utils.h"
+#include "m_kbdnotify.h"
+#include <newpluginapi.h>
+#include <m_database.h>
+#include <m_options.h>
+#include <m_clist.h>
+#include <m_system.h>
+#include <m_langpack.h>
+#include <m_protocols.h>
+#include <m_protosvc.h>
+#include <m_contacts.h>
+#include <m_message.h>
+#include <m_utils.h>
+#include <m_icq.h>
+#include <m_metacontacts.h>
+#pragma comment(lib, "advapi32.lib")
+
+#define NCONVERS_BLINKID ((HANDLE)123456) //nconvers' random identifier used to flash an icon for "incoming message" on contact list
+
+#ifndef SPI_GETSCREENSAVERRUNNING
+#define SPI_GETSCREENSAVERRUNNING 114
+#endif
+
+#ifndef WM_XBUTTONDBLCLK
+#define WM_XBUTTONDBLCLK 0x020D
+#endif
+#ifndef WM_NCXBUTTONDBLCLK
+#define WM_NCXBUTTONDBLCLK 0x00AD
+#endif
+
+
+HINSTANCE hInst;
+
+int hLangpack;
+
+DWORD IDThread = 0;
+HANDLE hThread = NULL;
+HANDLE hFlashEvent;
+HANDLE hExitEvent;
+
+HANDLE hModulesLoaded = NULL;
+HANDLE hMsgEventHook = NULL;
+HANDLE hOptionsInitialize = NULL;
+HANDLE hEnableService = NULL;
+HANDLE hDisableService = NULL;
+HANDLE hStartBlinkService = NULL;
+HANDLE hEventsOpenedService = NULL;
+HANDLE hFlashingEventService = NULL;
+HANDLE hNormalizeSequenceService = NULL;
+
+HHOOK hMirandaMouseHook = NULL;
+HHOOK hMirandaKeyBoardHook = NULL;
+HHOOK hMirandaWndProcHook = NULL;
+UINT hReminderTimer = 0;
+
+#pragma data_seg("Shared")
+HHOOK hMouseHook = NULL;
+HHOOK hKeyBoardHook = NULL;
+BYTE bEmulateKeypresses = 0;
+DWORD dwLastInput = 0;
+POINT lastGlobalMousePos = {0, 0};
+#pragma data_seg()
+#pragma comment(linker, "/section:Shared,rws")
+
+static BOOL (WINAPI * MyGetLastInputInfo)(PLASTINPUTINFO);
+
+
+BYTE bFlashOnMsg;
+BYTE bFlashOnURL;
+BYTE bFlashOnFile;
+BYTE bFlashOnOther;
+BYTE bFullScreenMode;
+BYTE bScreenSaverRunning;
+BYTE bWorkstationLocked;
+BYTE bProcessesAreRunning;
+BYTE bWorkstationActive;
+BYTE bFlashIfMsgOpen;
+BYTE bFlashIfMsgWinNotTop;
+BYTE bFlashIfMsgOlder;
+WORD wSecondsOlder;
+BYTE bFlashUntil;
+WORD wBlinksNumber;
+BYTE bMirandaOrWindows;
+WORD wStatusMap;
+WORD wReminderCheck;
+BYTE bFlashLed[3];
+BYTE bFlashEffect;
+BYTE bSequenceOrder;
+WORD wCustomTheme;
+WORD wStartDelay;
+BYTE bFlashSpeed;
+BYTE bOverride;
+BYTE bTrillianLedsMsg;
+BYTE bTrillianLedsURL;
+BYTE bTrillianLedsFile;
+BYTE bTrillianLedsOther;
+
+PROTOCOL_LIST ProtoList = {0, NULL};
+PROCESS_LIST ProcessList = {0, NULL};
+
+double dWinVer;
+BOOL bWindowsNT;
+
+int nWaitDelay;
+unsigned int nExternCount = 0;
+BOOL bFlashingEnabled = TRUE;
+BOOL bReminderDisabled = FALSE;
+
+char *szMetaProto = NULL;
+BYTE bMetaProtoEnabled = 0;
+
+#define MIID_KBDNOTIFY {0x119d7288, 0x2050, 0x448d, { 0x99, 0x00, 0xd8, 0x6a, 0xc7, 0x04, 0x26, 0xbf }}
+
+PLUGININFOEX pluginInfo={
+ sizeof(PLUGININFOEX),
+ "Keyboard Notify Ext.",
+ PLUGIN_MAKE_VERSION(1,5,7,7),
+ "Flashes your keyboard LEDs when a message has arrived",
+ "TioDuke",
+ "tioduke@yahoo.ca",
+ "© 2002-2003 M.Öberg, 2004 Std, 2005-2008 TioDuke",
+ "http://addons.miranda-im.org/",
+ UNICODE_AWARE, //doesn't replace anything built-in
+ MIID_KBDNOTIFY //{119D7288-2050-448d-9900-D86AC70426BF}
+};
+
+
+
+int InitializeOptions(WPARAM,LPARAM);
+void LoadSettings(void);
+int HookWindowsHooks(void);
+int UnhookWindowsHooks(void);
+static LRESULT CALLBACK MouseHookFunction(int, WPARAM, LPARAM);
+static LRESULT CALLBACK KeyBoardHookFunction(int, WPARAM, LPARAM);
+static LRESULT CALLBACK MirandaMouseHookFunction(int, WPARAM, LPARAM);
+static LRESULT CALLBACK MirandaKeyBoardHookFunction(int, WPARAM, LPARAM);
+static LRESULT CALLBACK MirandaWndProcHookFunction(int, WPARAM, LPARAM);
+BOOL CheckMsgWnd(HANDLE, BOOL *);
+
+
+BOOL isMetaContactsSubContact(HANDLE hMetaContact, HANDLE hContact)
+{
+ char *szProto = (char *)CallService(MS_PROTO_GETCONTACTBASEPROTO, (WPARAM)hMetaContact, 0);
+ if (szProto && !strcmp(szMetaProto, szProto)) { // Safety check
+ int i = DBGetContactSettingDword(hContact, szMetaProto, "ContactNumber", -1);
+ if (i >= 0 && hContact == (HANDLE)CallService(MS_MC_GETSUBCONTACT, (WPARAM)hMetaContact, i))
+ return TRUE;
+ }
+ return FALSE;
+}
+
+
+BOOL checkOpenWindow(HANDLE hContact)
+{
+ BOOL found, focus;
+
+ if (bFlashIfMsgOpen && !bFlashIfMsgWinNotTop)
+ return TRUE;
+
+ found = CheckMsgWnd(hContact, &focus);
+ if (!found && szMetaProto && bMetaProtoEnabled) {
+ HANDLE hMetaContact = (HANDLE)DBGetContactSettingDword(hContact, szMetaProto, "Handle", 0);
+ if (hMetaContact && isMetaContactsSubContact(hMetaContact, hContact))
+ found = CheckMsgWnd(hMetaContact, &focus);
+ }
+ if (!found)
+ return TRUE;
+
+ if (bFlashIfMsgOpen && !focus)
+ return TRUE;
+
+ return FALSE;
+}
+
+
+BOOL IsSaverOnNT4()
+{
+ HDESK hd = OpenDesktop(L"screen-saver", 0, FALSE, MAXIMUM_ALLOWED);
+
+ if(hd == NULL)
+ return GetLastError()==ERROR_ACCESS_DENIED;
+
+ CloseDesktop(hd);
+ return TRUE;
+}
+
+
+BOOL isScreenSaverRunning()
+{
+ BOOL screenSaverIsRunning=FALSE;
+
+ if (bWindowsNT && dWinVer < 5) return IsSaverOnNT4();
+
+ SystemParametersInfo(SPI_GETSCREENSAVERRUNNING, 0, &screenSaverIsRunning, FALSE);
+ return screenSaverIsRunning;
+}
+
+
+/* this function is from the original idle module */
+BOOL isWorkstationLocked()
+{
+ HDESK hd;
+ char buf[MAX_PATH];
+
+ if (!bWindowsNT) return FALSE;
+
+ hd = OpenInputDesktop(0, FALSE, MAXIMUM_ALLOWED); /* if it fails then the workstation is prolly locked anyway */
+ if (hd == NULL) return TRUE;
+ GetUserObjectInformation(hd, UOI_NAME, buf, sizeof(buf), NULL); /* if we got it (hmm,) get a name */
+ CloseDesktop(hd);
+ return strcmp(buf, "Winlogon")==0;
+}
+
+
+BOOL isFullScreen()
+{
+ int w = GetSystemMetrics(SM_CXSCREEN);
+ int h = GetSystemMetrics(SM_CYSCREEN);
+
+ HWND hWnd = 0;
+ while (hWnd = FindWindowEx(NULL, hWnd, NULL, NULL)) {
+ RECT WindowRect;
+
+ if (!(GetWindowLongPtr(hWnd, GWL_EXSTYLE) & WS_EX_TOPMOST))
+ continue;
+
+ GetWindowRect(hWnd, &WindowRect);
+ if ((w != (WindowRect.right - WindowRect.left)) || (h != (WindowRect.bottom - WindowRect.top)))
+ continue;
+
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
+
+BOOL checkNotifyOptions()
+{
+ BOOL fullScreenMode, screenSaverIsRunning, workstationIsLocked, processesRunning;
+
+ screenSaverIsRunning = isScreenSaverRunning();
+ if (screenSaverIsRunning && bScreenSaverRunning)
+ return TRUE;
+
+ workstationIsLocked = isWorkstationLocked();
+ if (workstationIsLocked && bWorkstationLocked)
+ return TRUE;
+
+ fullScreenMode = isFullScreen() && !screenSaverIsRunning;
+ if (fullScreenMode && bFullScreenMode)
+ return TRUE;
+
+ processesRunning = areThereProcessesRunning();
+ if (processesRunning && bProcessesAreRunning)
+ return TRUE;
+
+ return (!fullScreenMode && !screenSaverIsRunning && !workstationIsLocked && !processesRunning && bWorkstationActive);
+}
+
+
+BOOL isStatusEnabled(int status)
+{
+ switch (status) {
+ case ID_STATUS_OFFLINE: return wStatusMap & MAP_OFFLINE;
+ case ID_STATUS_ONLINE: return wStatusMap & MAP_ONLINE;
+ case ID_STATUS_AWAY: return wStatusMap & MAP_AWAY;
+ case ID_STATUS_NA: return wStatusMap & MAP_NA;
+ case ID_STATUS_OCCUPIED: return wStatusMap & MAP_OCCUPIED;
+ case ID_STATUS_DND: return wStatusMap & MAP_DND;
+ case ID_STATUS_FREECHAT: return wStatusMap & MAP_FREECHAT;
+ case ID_STATUS_INVISIBLE: return wStatusMap & MAP_INVISIBLE;
+ case ID_STATUS_ONTHEPHONE: return wStatusMap & MAP_ONTHEPHONE;
+ case ID_STATUS_OUTTOLUNCH: return wStatusMap & MAP_OUTTOLUNCH;
+ default: return FALSE;
+ }
+}
+
+
+BOOL checkGlobalStatus()
+{
+ return isStatusEnabled(CallService(MS_CLIST_GETSTATUSMODE, 0, 0));
+}
+
+
+BOOL checkGlobalXstatus()
+{
+ ICQ_CUSTOM_STATUS xstatus={0};
+ unsigned int i, protosSupporting; int status=0;
+
+ for(i=0, protosSupporting=0; i < ProtoList.protoCount; i++) {
+ if (!ProtoList.protoInfo[i].enabled || !ProtoList.protoInfo[i].xstatus.count) continue;
+
+ protosSupporting++;
+ // Retrieve xstatus for protocol
+ xstatus.cbSize = sizeof(ICQ_CUSTOM_STATUS);
+ xstatus.flags = CSSF_MASK_STATUS;
+ xstatus.status = &status;
+ CallProtoService(ProtoList.protoInfo[i].szProto, PS_ICQ_GETCUSTOMSTATUSEX, 0, (LPARAM)&xstatus);
+
+ if (ProtoList.protoInfo[i].xstatus.enabled[status]) return TRUE;
+ }
+
+ if (!protosSupporting)
+ return TRUE;
+ else
+ return FALSE;
+}
+
+
+DBEVENTINFO createMsgEventInfo(HANDLE hContact)
+{
+ DBEVENTINFO einfo = {0};
+
+ einfo.cbSize = sizeof(einfo);
+ einfo.eventType = EVENTTYPE_MESSAGE;
+ einfo.szModule = (char *)CallService(MS_PROTO_GETCONTACTBASEPROTO, (WPARAM)hContact, 0);
+
+ return einfo;
+}
+
+
+DBEVENTINFO readEventInfo(HANDLE hDbEvent, HANDLE hContact)
+{
+ DBEVENTINFO einfo = {0};
+
+ if (hDbEvent == NCONVERS_BLINKID) // we need to handle nconvers' blink event
+ return createMsgEventInfo(hContact);
+
+ einfo.cbSize = sizeof(einfo);
+ einfo.cbBlob = 0;
+ einfo.pBlob = NULL;
+ CallService(MS_DB_EVENT_GET, (WPARAM)hDbEvent, (LPARAM)&einfo);
+
+ return einfo;
+}
+
+
+BOOL checkIgnore(HANDLE hContact, WORD eventType)
+{
+ return !IsIgnored(hContact, eventType);
+}
+
+
+BOOL checkProtocol(char *szProto)
+{
+ unsigned int i;
+
+ if (!szProto)
+ return FALSE;
+
+ for(i=0; i < ProtoList.protoCount; i++)
+ if (ProtoList.protoInfo[i].szProto && !strcmp(ProtoList.protoInfo[i].szProto, szProto))
+ return ProtoList.protoInfo[i].enabled;
+
+ return FALSE;
+}
+
+
+BOOL metaCheckProtocol(char *szProto, HANDLE hContact, WORD eventType)
+{
+ HANDLE hSubContact=NULL;
+
+ if (szMetaProto && bMetaProtoEnabled && szProto && !strcmp(szMetaProto, szProto))
+ if (hSubContact = (HANDLE)CallService(MS_MC_GETMOSTONLINECONTACT, (WPARAM)hContact, 0))
+ szProto = (char *)CallService(MS_PROTO_GETCONTACTBASEPROTO, (WPARAM)hSubContact, 0);
+
+ return checkProtocol(szProto) && checkIgnore(hSubContact?hSubContact:hContact, eventType);
+}
+
+
+BOOL checkUnopenEvents()
+{
+ int nIndex;
+ CLISTEVENT *pCLEvent;
+
+ if (nExternCount && bFlashOnOther)
+ return TRUE;
+
+ for (nIndex = 0; pCLEvent = (CLISTEVENT*)CallService(MS_CLIST_GETEVENT, -1, nIndex); nIndex++) {
+ DBEVENTINFO einfo = readEventInfo(pCLEvent->hDbEvent, pCLEvent->hContact);
+
+ if ((einfo.eventType == EVENTTYPE_MESSAGE && bFlashOnMsg) ||
+ (einfo.eventType == EVENTTYPE_URL && bFlashOnURL) ||
+ (einfo.eventType == EVENTTYPE_FILE && bFlashOnFile) ||
+ (einfo.eventType != EVENTTYPE_MESSAGE && einfo.eventType != EVENTTYPE_URL && einfo.eventType != EVENTTYPE_FILE && bFlashOnOther))
+
+ if (metaCheckProtocol(einfo.szModule, pCLEvent->hContact, einfo.eventType))
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
+
+static void FlashThreadFunction()
+{
+ BOOL bEvent = FALSE;
+ DWORD dwEventStarted, dwFlashStarted;
+ BYTE data, unchangedLeds;
+
+ while (TRUE) {
+ unchangedLeds = (BYTE)(LedState(VK_PAUSE) * !bFlashLed[2] + ((LedState(VK_NUMLOCK) * !bFlashLed[0])<<1) + ((LedState(VK_CAPITAL) * !bFlashLed[1])<<2));
+ GetAsyncKeyState(VK_PAUSE); // empty Pause/Break's keystroke buffer
+
+ // Start flashing
+ while(bEvent && bFlashingEnabled)
+ {
+ // Let's give the user the opportunity of finishing flashing manually :)
+ if (GetAsyncKeyState(VK_PAUSE) & 1)
+ break;
+
+ if ((bFlashUntil & UNTIL_NBLINKS) && GetTickCount() > (dwFlashStarted + wBlinksNumber * 1000))
+ break;
+ if (bFlashUntil & UNTIL_REATTENDED) {
+ if (bMirandaOrWindows == ACTIVE_WINDOWS && MyGetLastInputInfo && !bEmulateKeypresses) {
+ LASTINPUTINFO lii;
+ ZeroMemory(&lii, sizeof(lii));
+ lii.cbSize = sizeof(lii);
+ MyGetLastInputInfo(&lii);
+ dwLastInput = lii.dwTime;
+ }
+ if (dwLastInput > dwEventStarted)
+ break;
+ }
+ if ((bFlashUntil & UNTIL_EVENTSOPEN) && !checkUnopenEvents())
+ break;
+ if ((bFlashUntil & UNTIL_CONDITIONS) && (!checkNotifyOptions() || !checkGlobalStatus() || !checkGlobalXstatus()))
+ break;
+
+ data = getBlinkingLeds();
+ ToggleKeyboardLights((BYTE)(data|unchangedLeds));
+
+ // Wait for exit event
+ if (WaitForSingleObject(hExitEvent, nWaitDelay) == WAIT_OBJECT_0)
+ return;
+ }
+ RestoreLEDState();
+
+ setFlashingSequence();
+ bReminderDisabled = FALSE;
+
+ // Wait for new event
+ {
+ DWORD dwEvent;
+ HANDLE Objects[2];
+ Objects[0] = hFlashEvent;
+ Objects[1] = hExitEvent;
+ dwEvent = WaitForMultipleObjects(2, Objects, FALSE, INFINITE);
+ if ((dwEvent - WAIT_OBJECT_0) == 1)
+ return;
+ }
+
+ bEvent = TRUE;
+ bReminderDisabled = TRUE;
+ dwEventStarted = GetTickCount();
+ // Wait StartDelay seconds
+ if (wStartDelay > 0)
+ Sleep(wStartDelay * 1000);
+ dwFlashStarted = GetTickCount();
+
+ }
+
+}
+
+
+BOOL checkMsgTimestamp(HANDLE hEventCurrent, DWORD timestampCurrent)
+{
+ HANDLE hEvent;
+
+ if (!bFlashIfMsgOlder)
+ return TRUE;
+
+ for (hEvent=(HANDLE)CallService(MS_DB_EVENT_FINDPREV, (WPARAM)hEventCurrent, 0); hEvent; hEvent=(HANDLE)CallService(MS_DB_EVENT_FINDPREV, (WPARAM)hEvent, 0)) {
+ DBEVENTINFO einfo = {0};
+
+ einfo.cbSize = sizeof(einfo);
+ einfo.cbBlob = 0;
+ einfo.pBlob = NULL;
+ CallService(MS_DB_EVENT_GET, (WPARAM)hEvent, (LPARAM)&einfo);
+ if ((einfo.timestamp + wSecondsOlder) <= timestampCurrent)
+ return TRUE;
+ if (einfo.eventType == EVENTTYPE_MESSAGE)
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+
+BOOL contactCheckProtocol(char *szProto, HANDLE hContact, WORD eventType)
+{
+ if (szMetaProto && bMetaProtoEnabled && hContact) {
+ HANDLE hMetaContact = (HANDLE)DBGetContactSettingDword(hContact, szMetaProto, "Handle", 0);
+ if (hMetaContact && isMetaContactsSubContact(hMetaContact, hContact))
+ return FALSE;
+ }
+
+ return (metaCheckProtocol(szProto, hContact, eventType));
+}
+
+
+BOOL checkStatus(char *szProto)
+{
+ if (!szProto)
+ return checkGlobalStatus();
+
+ return isStatusEnabled(CallProtoService(szProto, PS_GETSTATUS, 0, 0));
+}
+
+
+BOOL checkXstatus(char *szProto)
+{
+ unsigned int i; int status=0;
+ ICQ_CUSTOM_STATUS xstatus={0};
+
+ if (!szProto)
+ return checkGlobalXstatus();
+
+ for(i=0; i < ProtoList.protoCount; i++)
+ if (ProtoList.protoInfo[i].szProto && !strcmp(ProtoList.protoInfo[i].szProto, szProto)) {
+ if (!ProtoList.protoInfo[i].xstatus.count) return TRUE;
+
+ // Retrieve xstatus for protocol
+ xstatus.cbSize = sizeof(ICQ_CUSTOM_STATUS);
+ xstatus.flags = CSSF_MASK_STATUS;
+ xstatus.status = &status;
+ CallProtoService(ProtoList.protoInfo[i].szProto, PS_ICQ_GETCUSTOMSTATUSEX, 0, (LPARAM)&xstatus);
+
+ return ProtoList.protoInfo[i].xstatus.enabled[status];
+ }
+
+ return TRUE;
+}
+
+
+// 'Pings' the FlashThread to keep the LEDs flashing.
+static int PluginMessageEventHook(WPARAM wParam, LPARAM lParam)
+{
+ DBEVENTINFO einfo = {0};
+ HANDLE hContact = (HANDLE)wParam;
+ HANDLE hEvent = (HANDLE)lParam;
+
+ //get DBEVENTINFO without pBlob
+ einfo.cbSize = sizeof(einfo);
+ einfo.cbBlob = 0;
+ einfo.pBlob = NULL;
+ CallService(MS_DB_EVENT_GET, (WPARAM)hEvent, (LPARAM)&einfo);
+
+ if (!(einfo.flags & DBEF_SENT))
+ if ((einfo.eventType == EVENTTYPE_MESSAGE && bFlashOnMsg && checkOpenWindow(hContact) && checkMsgTimestamp(hEvent, einfo.timestamp)) ||
+ (einfo.eventType == EVENTTYPE_URL && bFlashOnURL) ||
+ (einfo.eventType == EVENTTYPE_FILE && bFlashOnFile) ||
+ (einfo.eventType != EVENTTYPE_MESSAGE && einfo.eventType != EVENTTYPE_URL && einfo.eventType != EVENTTYPE_FILE && bFlashOnOther)) {
+
+ if (contactCheckProtocol(einfo.szModule, hContact, einfo.eventType) && checkNotifyOptions() && checkStatus(einfo.szModule) && checkXstatus(einfo.szModule))
+
+ SetEvent(hFlashEvent);
+ }
+
+ return 0;
+}
+
+
+// **
+// ** Checks for pending events. If it finds any, it pings the FlashThread to keep the LEDs flashing.
+// **
+
+static VOID CALLBACK ReminderTimer(HWND hwnd, UINT message, UINT_PTR idEvent, DWORD dwTime)
+{
+ int nIndex;
+ CLISTEVENT *pCLEvent;
+
+ if (!bReminderDisabled && nExternCount && bFlashOnOther) {
+ SetEvent(hFlashEvent);
+ return;
+ }
+
+ for (nIndex = 0; !bReminderDisabled && (pCLEvent = (CLISTEVENT*)CallService(MS_CLIST_GETEVENT, -1, nIndex)); nIndex++) {
+ DBEVENTINFO einfo = readEventInfo(pCLEvent->hDbEvent, pCLEvent->hContact);
+
+ if ((einfo.eventType == EVENTTYPE_MESSAGE && bFlashOnMsg) ||
+ (einfo.eventType == EVENTTYPE_URL && bFlashOnURL) ||
+ (einfo.eventType == EVENTTYPE_FILE && bFlashOnFile) ||
+ (einfo.eventType != EVENTTYPE_MESSAGE && einfo.eventType != EVENTTYPE_URL && einfo.eventType != EVENTTYPE_FILE && bFlashOnOther))
+
+ if (metaCheckProtocol(einfo.szModule, pCLEvent->hContact, einfo.eventType) && checkNotifyOptions() && checkStatus(einfo.szModule) && checkXstatus(einfo.szModule)) {
+
+ SetEvent(hFlashEvent);
+ return;
+ }
+ }
+
+}
+
+
+// Support for third-party plugins and mBot's scripts
+static INT_PTR EnableService(WPARAM wParam, LPARAM lParam)
+{
+ bFlashingEnabled = TRUE;
+ return 0;
+}
+
+static INT_PTR DisableService(WPARAM wParam, LPARAM lParam)
+{
+ bFlashingEnabled = FALSE;
+ return 0;
+}
+
+static INT_PTR StartBlinkService(WPARAM wParam, LPARAM lParam)
+{
+ nExternCount += (unsigned int)wParam;
+ if (bFlashOnOther && checkNotifyOptions() && checkGlobalStatus() && checkGlobalXstatus()) {
+ if (lParam)
+ useExternSequence((TCHAR *)lParam);
+ SetEvent(hFlashEvent);
+ }
+
+ return 0;
+}
+
+static INT_PTR EventsWereOpenedService(WPARAM wParam, LPARAM lParam)
+{
+ if ((unsigned int)wParam > nExternCount)
+ nExternCount = 0;
+ else
+ nExternCount -= (unsigned int)wParam;
+
+ return 0;
+}
+
+
+static INT_PTR IsFlashingActiveService(WPARAM wParam, LPARAM lParam)
+{
+ if (!bReminderDisabled)
+ return 0;
+
+ return (int)getCurrentSequenceString();
+}
+
+
+INT_PTR NormalizeSequenceService(WPARAM wParam, LPARAM lParam)
+{
+ TCHAR strAux[MAX_PATH+1], *strIn = (TCHAR *)lParam;
+
+ _snwprintf(strAux, MAX_PATH, _T("%s"), strIn);
+ _snwprintf(strIn, MAX_PATH, _T("%s"), normalizeCustomString(strAux));
+
+ return (int)strIn;
+}
+
+
+// Support for Trigger plugin
+static void ForceEventsWereOpenedThread(void *eventMaxSeconds)
+{
+ Sleep(((WORD)eventMaxSeconds) * 1000);
+ CallService(MS_KBDNOTIFY_EVENTSOPENED, 1, 0);
+}
+
+
+void StartBlinkAction(char *flashSequence, WORD eventMaxSeconds)
+{
+ DWORD threadID = 0;
+
+ if (eventMaxSeconds)
+ CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)ForceEventsWereOpenedThread, (void *)eventMaxSeconds, 0, &threadID);
+
+ CallService(MS_KBDNOTIFY_STARTBLINK, 1, (LPARAM)flashSequence);
+}
+
+
+void createProcessList(void)
+{
+ DBVARIANT dbv;
+ unsigned int i, count;
+
+ count = (unsigned int)DBGetContactSettingWord(NULL, KEYBDMODULE, "processcount", 0);
+
+ ProcessList.count = 0;
+ ProcessList.szFileName = (TCHAR **)malloc(count * sizeof(TCHAR *));
+ if (ProcessList.szFileName) {
+ for(i=0; i < count; i++)
+ if (DBGetContactSetting(NULL, KEYBDMODULE, fmtDBSettingName("process%d", i), &dbv))
+ ProcessList.szFileName[i] = NULL;
+ else {
+ ProcessList.szFileName[i] = (TCHAR *)malloc(wcslen(dbv.ptszVal) + 1);
+ if (ProcessList.szFileName[i])
+ wcscpy(ProcessList.szFileName[i], dbv.ptszVal);
+ DBFreeVariant(&dbv);
+ }
+ ProcessList.count = count;
+ }
+
+}
+
+
+void destroyProcessList(void)
+{
+ unsigned int i, count;
+
+ count = ProcessList.count;
+
+ ProcessList.count = 0;
+ for(i=0; i < count; i++)
+ if (ProcessList.szFileName[i])
+ free(ProcessList.szFileName[i]);
+
+ if (ProcessList.szFileName)
+ free(ProcessList.szFileName);
+ ProcessList.szFileName = NULL;
+}
+
+
+void LoadSettings(void)
+{
+ unsigned int i;
+
+ bFlashOnMsg = DBGetContactSettingByte(NULL, KEYBDMODULE, "onmsg", DEF_SETTING_ONMSG);
+ bFlashOnURL = DBGetContactSettingByte(NULL, KEYBDMODULE, "onurl", DEF_SETTING_ONURL);
+ bFlashOnFile = DBGetContactSettingByte(NULL, KEYBDMODULE, "onfile", DEF_SETTING_ONFILE);
+ bFlashOnOther = DBGetContactSettingByte(NULL, KEYBDMODULE, "onother", DEF_SETTING_OTHER);
+ bFullScreenMode = DBGetContactSettingByte(NULL, KEYBDMODULE, "fscreenmode", DEF_SETTING_FSCREEN);
+ bScreenSaverRunning = DBGetContactSettingByte(NULL, KEYBDMODULE, "ssaverrunning", DEF_SETTING_SSAVER);
+ bWorkstationLocked = (bWindowsNT ? DBGetContactSettingByte(NULL, KEYBDMODULE, "wstationlocked", DEF_SETTING_LOCKED):0);
+ bProcessesAreRunning = DBGetContactSettingByte(NULL, KEYBDMODULE, "procsrunning", DEF_SETTING_PROCS);
+ bWorkstationActive = DBGetContactSettingByte(NULL, KEYBDMODULE, "wstationactive", DEF_SETTING_ACTIVE);
+ bFlashIfMsgOpen = DBGetContactSettingByte(NULL, KEYBDMODULE, "ifmsgopen", DEF_SETTING_IFMSGOPEN);
+ bFlashIfMsgWinNotTop = DBGetContactSettingByte(NULL, KEYBDMODULE, "ifmsgnottop", DEF_SETTING_IFMSGNOTTOP);
+ bFlashIfMsgOlder = DBGetContactSettingByte(NULL, KEYBDMODULE, "ifmsgolder", DEF_SETTING_IFMSGOLDER);
+ wSecondsOlder = DBGetContactSettingWord(NULL, KEYBDMODULE, "secsolder", DEF_SETTING_SECSOLDER);
+ bFlashUntil = DBGetContactSettingByte(NULL, KEYBDMODULE, "funtil", DEF_SETTING_FLASHUNTIL);
+ wBlinksNumber = DBGetContactSettingWord(NULL, KEYBDMODULE, "nblinks", DEF_SETTING_NBLINKS);
+ bMirandaOrWindows = DBGetContactSettingByte(NULL, KEYBDMODULE, "mirorwin", DEF_SETTING_MIRORWIN);
+ wStatusMap = DBGetContactSettingWord(NULL, KEYBDMODULE, "status", DEF_SETTING_STATUS);
+ wReminderCheck = DBGetContactSettingWord(NULL, KEYBDMODULE, "remcheck", DEF_SETTING_CHECKTIME);
+ bFlashLed[0] = !!DBGetContactSettingByte(NULL, KEYBDMODULE, "fnum", DEF_SETTING_FLASHNUM);
+ bFlashLed[1] = !!DBGetContactSettingByte(NULL, KEYBDMODULE, "fcaps", DEF_SETTING_FLASHCAPS);
+ bFlashLed[2] = !!DBGetContactSettingByte(NULL, KEYBDMODULE, "fscroll", DEF_SETTING_FLASHSCROLL);
+ bFlashEffect = DBGetContactSettingByte(NULL, KEYBDMODULE, "feffect", DEF_SETTING_FLASHEFFECT);
+ bSequenceOrder = DBGetContactSettingByte(NULL, KEYBDMODULE, "order", DEF_SETTING_SEQORDER);
+ wCustomTheme = DBGetContactSettingWord(NULL, KEYBDMODULE, "custom", DEF_SETTING_CUSTOMTHEME);
+ bTrillianLedsMsg = DBGetContactSettingByte(NULL, KEYBDMODULE, "ledsmsg", DEF_SETTING_LEDSMSG);
+ bTrillianLedsURL = DBGetContactSettingByte(NULL, KEYBDMODULE, "ledsurl", DEF_SETTING_LEDSURL);
+ bTrillianLedsFile = DBGetContactSettingByte(NULL, KEYBDMODULE, "ledsfile", DEF_SETTING_LEDSFILE);
+ bTrillianLedsOther = DBGetContactSettingByte(NULL, KEYBDMODULE, "ledsother", DEF_SETTING_LEDSOTHER);
+ wStartDelay = DBGetContactSettingWord(NULL, KEYBDMODULE, "sdelay", DEF_SETTING_STARTDELAY);
+ bFlashSpeed = DBGetContactSettingByte(NULL, KEYBDMODULE, "speed", DEF_SETTING_FLASHSPEED);
+ switch (bFlashSpeed) {
+ case 0: nWaitDelay = 1500; break;
+ case 1: nWaitDelay = 0750; break;
+ case 2: nWaitDelay = 0250; break;
+ case 3: nWaitDelay = 0150; break;
+ case 4: nWaitDelay = 0100; break;
+ default: nWaitDelay = 0050; break;
+ }
+ setFlashingSequence();
+ bEmulateKeypresses = DBGetContactSettingByte(NULL, KEYBDMODULE, "keypresses", DEF_SETTING_KEYPRESSES);
+ bOverride = DBGetContactSettingByte(NULL, KEYBDMODULE, "override", DEF_SETTING_OVERRIDE);
+ // Create hidden settings (for test button) if needed
+ if (DBGetContactSettingByte(NULL, KEYBDMODULE, "testnum", -1) == -1)
+ DBWriteContactSettingByte(NULL, KEYBDMODULE, "testnum", DEF_SETTING_TESTNUM);
+ if (DBGetContactSettingByte(NULL, KEYBDMODULE, "testsecs", -1) == -1)
+ DBWriteContactSettingByte(NULL, KEYBDMODULE, "testsecs", DEF_SETTING_TESTSECS);
+ for(i=0; i < ProtoList.protoCount; i++)
+ if (ProtoList.protoInfo[i].visible) {
+ unsigned int j;
+ ProtoList.protoInfo[i].enabled = DBGetContactSettingByte(NULL, KEYBDMODULE, ProtoList.protoInfo[i].szProto, DEF_SETTING_PROTOCOL);
+ for(j=0; j < ProtoList.protoInfo[i].xstatus.count; j++)
+ ProtoList.protoInfo[i].xstatus.enabled[j] = DBGetContactSettingByte(NULL, KEYBDMODULE, fmtDBSettingName("%sxstatus%d", ProtoList.protoInfo[i].szProto, j), DEF_SETTING_XSTATUS);
+ }
+
+ if (szMetaProto)
+ bMetaProtoEnabled = DBGetContactSettingByte(NULL, szMetaProto, "Enabled", 1);
+
+ destroyProcessList();
+ createProcessList();
+ UnhookWindowsHooks();
+ HookWindowsHooks();
+}
+
+
+void GetWindowsVersion(void)
+{
+ OSVERSIONINFOEX osvi;
+ BOOL bOsVersionInfoEx;
+
+ ZeroMemory(&osvi, sizeof(OSVERSIONINFOEX));
+ osvi.dwOSVersionInfoSize = sizeof(OSVERSIONINFOEX);
+ if (!(bOsVersionInfoEx = GetVersionEx((OSVERSIONINFO *) &osvi))) {
+ osvi.dwOSVersionInfoSize = sizeof(OSVERSIONINFO);
+ if (!GetVersionEx((OSVERSIONINFO *)&osvi))
+ osvi.dwPlatformId = VER_PLATFORM_WIN32_WINDOWS;
+ }
+ bWindowsNT = osvi.dwPlatformId==VER_PLATFORM_WIN32_NT;
+ dWinVer = osvi.dwMajorVersion + osvi.dwMinorVersion / 10.0;
+}
+
+
+void updateXstatusProto(PROTOCOL_INFO *protoInfo)
+{
+ unsigned int i;
+ char szServiceName[MAXMODULELABELLENGTH];
+ ICQ_CUSTOM_STATUS xstatus={0};
+
+ mir_snprintf(szServiceName, sizeof(szServiceName), "%s%s", protoInfo->szProto, PS_ICQ_GETCUSTOMSTATUSEX);
+ if (!ServiceExists(szServiceName)) return;
+
+ // Retrieve xstatus.count
+ xstatus.cbSize = sizeof(ICQ_CUSTOM_STATUS);
+ xstatus.flags = CSSF_STATUSES_COUNT;
+ xstatus.wParam = &(protoInfo->xstatus.count);
+ CallProtoService(protoInfo->szProto, PS_ICQ_GETCUSTOMSTATUSEX, 0, (LPARAM)&xstatus);
+ (protoInfo->xstatus.count)++; // Don't forget about xstatus=0 (None)
+
+ // Alloc and initiailize xstatus.enabled array
+ protoInfo->xstatus.enabled = (BOOL *)malloc(protoInfo->xstatus.count * sizeof(BOOL));
+ if (!protoInfo->xstatus.enabled)
+ protoInfo->xstatus.count = 0;
+ else
+ for(i=0; i < protoInfo->xstatus.count; i++)
+ protoInfo->xstatus.enabled[i] = FALSE;
+
+}
+
+
+void createProtocolList(void)
+{
+ unsigned int i;
+ PROTOCOLDESCRIPTOR **proto;
+
+ if (ServiceExists(MS_MC_GETPROTOCOLNAME))
+ szMetaProto = (char *)CallService(MS_MC_GETPROTOCOLNAME, 0, 0);
+
+ CallService(MS_PROTO_ENUMPROTOCOLS, (WPARAM)&ProtoList.protoCount, (LPARAM)&proto);
+ ProtoList.protoInfo = (PROTOCOL_INFO *)malloc(ProtoList.protoCount * sizeof(PROTOCOL_INFO));
+ if (!ProtoList.protoInfo)
+ ProtoList.protoCount = 0;
+ else
+ for(i=0; i < ProtoList.protoCount; i++) {
+ ProtoList.protoInfo[i].xstatus.count = 0;
+ ProtoList.protoInfo[i].xstatus.enabled = NULL;
+ ProtoList.protoInfo[i].szProto = (char *)malloc(strlen(proto[i]->szName) + 1);
+ if (!ProtoList.protoInfo[i].szProto) {
+ ProtoList.protoInfo[i].enabled = FALSE;
+ ProtoList.protoInfo[i].visible = FALSE;
+ } else {
+ strcpy(ProtoList.protoInfo[i].szProto, proto[i]->szName);
+ ProtoList.protoInfo[i].enabled = FALSE;
+ if (proto[i]->type != PROTOTYPE_PROTOCOL)
+ ProtoList.protoInfo[i].visible = FALSE;
+ else
+ if (szMetaProto && !strcmp(proto[i]->szName, szMetaProto))
+ ProtoList.protoInfo[i].visible = FALSE;
+ else {
+ ProtoList.protoInfo[i].visible = TRUE;
+ updateXstatusProto(&(ProtoList.protoInfo[i]));
+ }
+ }
+ }
+
+}
+
+
+// We use the profile name to create the first part of each event name
+// We do so to avoid problems between different instances of the plugin concurrently running
+void createEventPrefix(TCHAR *prefixName, size_t maxLen)
+{
+ size_t len;
+ TCHAR profileName[MAX_PATH+1], *str;
+
+ getAbsoluteProfileName(profileName, MAX_PATH);
+
+ while (str = wcschr(profileName, _T('\\')))
+ *str = _T('/');
+ if ((len = wcslen(profileName)) <= maxLen)
+ wcscpy(prefixName, profileName);
+ else {
+ str = profileName + len - maxLen / 2;
+ _snwprintf(prefixName, maxLen / 2, L"%s", profileName);
+ wcscat(prefixName, str);
+ }
+}
+
+
+// **
+// ** Everything below is just Miranda init/uninit stuff
+// **
+
+
+static int ModulesLoaded(WPARAM wParam, LPARAM lParam)
+{
+ TCHAR eventPrefix[MAX_PATH+1], eventName[MAX_PATH+1];
+
+ LoadProcsLibrary();
+ if (bWindowsNT && dWinVer >= 5)
+ MyGetLastInputInfo = (BOOL (WINAPI *)(PLASTINPUTINFO)) GetProcAddress(GetModuleHandle(L"user32"), "GetLastInputInfo");
+ else
+ MyGetLastInputInfo = NULL;
+
+ createProtocolList();
+ LoadSettings();
+
+ // Create some synchronisation objects
+ createEventPrefix(eventPrefix, MAX_PATH - 11);
+ _snwprintf(eventName, sizeof(eventName), _T("%s/FlashEvent"), eventPrefix);
+ hFlashEvent = CreateEvent(NULL, FALSE, FALSE, eventName);
+ _snwprintf(eventName, sizeof(eventName), _T("%s/ExitEvent"), eventPrefix);
+ hExitEvent = CreateEvent(NULL, FALSE, FALSE, eventName);
+
+ hThread = CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)FlashThreadFunction, NULL, 0, &IDThread);
+
+ hMsgEventHook = HookEvent(ME_DB_EVENT_ADDED, PluginMessageEventHook);
+ hOptionsInitialize = HookEvent(ME_OPT_INITIALISE, InitializeOptions);
+ hEnableService = CreateServiceFunction(MS_KBDNOTIFY_ENABLE, EnableService);
+ hDisableService = CreateServiceFunction(MS_KBDNOTIFY_DISABLE, DisableService);
+ hStartBlinkService = CreateServiceFunction(MS_KBDNOTIFY_STARTBLINK, StartBlinkService);
+ hEventsOpenedService = CreateServiceFunction(MS_KBDNOTIFY_EVENTSOPENED, EventsWereOpenedService);
+ hFlashingEventService = CreateServiceFunction(MS_KBDNOTIFY_FLASHINGACTIVE, IsFlashingActiveService);
+ hNormalizeSequenceService = CreateServiceFunction(MS_KBDNOTIFY_NORMALSEQUENCE, NormalizeSequenceService);
+
+ RegisterAction();
+ if (ServiceExists("DBEditorpp/RegisterSingleModule"))
+ CallService("DBEditorpp/RegisterSingleModule", (WPARAM)KEYBDMODULE, 0);
+
+ return 0;
+}
+
+
+
+BOOL WINAPI DllMain(HINSTANCE hinstDLL, DWORD fdwReason, LPVOID lpvReserved)
+{
+
+ hInst = hinstDLL;
+
+ return TRUE;
+
+}
+
+extern "C" __declspec(dllexport) PLUGININFOEX* MirandaPluginInfoEx(DWORD mirandaVersion)
+{
+ return &pluginInfo;
+}
+
+extern "C" __declspec(dllexport) int Load(void)
+{
+
+ mir_getLP(&pluginInfo);
+
+ GetWindowsVersion();
+ OpenKeyboardDevice();
+ hModulesLoaded = HookEvent(ME_SYSTEM_MODULESLOADED, ModulesLoaded);
+
+ return 0;
+}
+
+
+
+void destroyProtocolList(void)
+{
+ unsigned int i;
+
+ for(i=0; i < ProtoList.protoCount; i++) {
+ if (ProtoList.protoInfo[i].szProto)
+ free(ProtoList.protoInfo[i].szProto);
+ if (ProtoList.protoInfo[i].xstatus.enabled)
+ free(ProtoList.protoInfo[i].xstatus.enabled);
+ }
+
+ ProtoList.protoCount = 0;
+ if (ProtoList.protoInfo)
+ free(ProtoList.protoInfo);
+}
+
+
+extern "C" __declspec(dllexport) int Unload(void)
+{
+
+ UnhookWindowsHooks();
+ DeInitAction();
+ if (hModulesLoaded)
+ UnhookEvent(hModulesLoaded);
+ if (hMsgEventHook)
+ UnhookEvent(hMsgEventHook);
+ if (hOptionsInitialize)
+ UnhookEvent(hOptionsInitialize);
+ if (hEnableService)
+ DestroyServiceFunction(hEnableService);
+ if (hDisableService)
+ DestroyServiceFunction(hDisableService);
+ if (hStartBlinkService)
+ DestroyServiceFunction(hStartBlinkService);
+ if (hEventsOpenedService)
+ DestroyServiceFunction(hEventsOpenedService);
+ if (hFlashingEventService)
+ DestroyServiceFunction(hFlashingEventService);
+ if (hNormalizeSequenceService)
+ DestroyServiceFunction(hNormalizeSequenceService);
+
+ // Wait for thread to exit
+ SetEvent(hExitEvent);
+ WaitForSingleObject(hThread, INFINITE);
+
+ RestoreLEDState();
+ CloseKeyboardDevice();
+
+ UnloadProcsLibrary();
+ destroyProcessList();
+ destroyProtocolList();
+
+ return 0;
+}
+
+
+// ========================== Windows hooks ==========================
+int HookWindowsHooks()
+{
+ if (wReminderCheck)
+ hReminderTimer = SetTimer(NULL,0, wReminderCheck * 60000, ReminderTimer);
+
+ if (bFlashUntil & UNTIL_REATTENDED)
+ switch (bMirandaOrWindows) {
+ case ACTIVE_WINDOWS:
+ if (!MyGetLastInputInfo || bEmulateKeypresses) {
+ if (hMouseHook == NULL)
+ hMouseHook = SetWindowsHookEx(WH_MOUSE, MouseHookFunction, hInst, 0);
+ if (hKeyBoardHook == NULL)
+ hKeyBoardHook = SetWindowsHookEx(WH_KEYBOARD, KeyBoardHookFunction, hInst, 0);
+ }
+ break;
+ case ACTIVE_MIRANDA:
+ if (hMirandaMouseHook == NULL)
+ hMirandaMouseHook = SetWindowsHookEx(WH_MOUSE, MirandaMouseHookFunction, NULL, GetCurrentThreadId());
+ if (hMirandaKeyBoardHook == NULL)
+ hMirandaKeyBoardHook = SetWindowsHookEx(WH_KEYBOARD, MirandaKeyBoardHookFunction, NULL, GetCurrentThreadId());
+ if (hMirandaWndProcHook == NULL)
+ hMirandaWndProcHook = SetWindowsHookEx(WH_CALLWNDPROC, MirandaWndProcHookFunction, NULL, GetCurrentThreadId());
+ }
+
+ return 0;
+}
+
+int UnhookWindowsHooks()
+{
+ if (hReminderTimer)
+ KillTimer(NULL, hReminderTimer);
+ if (hMouseHook)
+ UnhookWindowsHookEx(hMouseHook);
+ if (hKeyBoardHook)
+ UnhookWindowsHookEx(hKeyBoardHook);
+ if (hMirandaMouseHook)
+ UnhookWindowsHookEx(hMirandaMouseHook);
+ if (hMirandaKeyBoardHook)
+ UnhookWindowsHookEx(hMirandaKeyBoardHook);
+ if (hMirandaWndProcHook)
+ UnhookWindowsHookEx(hMirandaWndProcHook);
+
+ hReminderTimer = 0;
+ hMouseHook = hKeyBoardHook = hMirandaMouseHook = hMirandaKeyBoardHook = hMirandaWndProcHook = NULL;
+
+ return 0;
+}
+
+static LRESULT CALLBACK MouseHookFunction(int code, WPARAM wParam, LPARAM lParam)
+{
+ if (code >= 0) {
+ /* This should handle all mouse buttons ... */
+ if ((wParam >= WM_NCLBUTTONDOWN && wParam <= WM_NCXBUTTONDBLCLK && wParam != 0x00AA) || (wParam >= WM_LBUTTONDOWN && wParam <= WM_XBUTTONDBLCLK))
+ dwLastInput = GetTickCount();
+ /* ... and here it is either mouse move, hover, leave or something unexpected */
+ else {
+ PMOUSEHOOKSTRUCT mouseInfo = (PMOUSEHOOKSTRUCT)lParam;
+ POINT pt = mouseInfo->pt;
+ if (pt.x!=lastGlobalMousePos.x || pt.y!=lastGlobalMousePos.y) {
+ lastGlobalMousePos = pt;
+ dwLastInput = GetTickCount();
+ }
+ }
+ }
+
+ return CallNextHookEx(hMouseHook, code, wParam, lParam);
+}
+
+static LRESULT CALLBACK KeyBoardHookFunction(int code, WPARAM wParam, LPARAM lParam)
+{
+ if (code >= 0 && (!bEmulateKeypresses || (bEmulateKeypresses && wParam != VK_NUMLOCK && wParam != VK_CAPITAL && wParam != VK_SCROLL)))
+ dwLastInput = GetTickCount();
+
+ return CallNextHookEx(hKeyBoardHook, code, wParam, lParam);
+}
+
+static LRESULT CALLBACK MirandaMouseHookFunction(int code, WPARAM wParam, LPARAM lParam)
+{
+ static POINT lastMousePos = {0, 0};
+
+ if (code >= 0) {
+ /* Movement mouse messages are for some reason incoming in inactive/background window too, that is not input */
+ DWORD pid;
+ GetWindowThreadProcessId(GetForegroundWindow(), &pid);
+ if(pid == GetCurrentProcessId()) {
+ /* This should handle all mouse buttons ... */
+ if ((wParam >= WM_NCLBUTTONDOWN && wParam <= WM_NCXBUTTONDBLCLK && wParam != 0x00AA) || (wParam >= WM_LBUTTONDOWN && wParam <= WM_XBUTTONDBLCLK))
+ dwLastInput = GetTickCount();
+ /* ... and here it is either mouse move, hover, leave or something unexpected */
+ else {
+ PMOUSEHOOKSTRUCT mouseInfo = (PMOUSEHOOKSTRUCT)lParam;
+ POINT pt = mouseInfo->pt;
+ if (pt.x!=lastMousePos.x || pt.y!=lastMousePos.y) {
+ lastMousePos = pt;
+ dwLastInput = GetTickCount();
+ }
+ }
+ }
+ }
+
+ return CallNextHookEx(hMirandaMouseHook, code, wParam, lParam);
+}
+
+static LRESULT CALLBACK MirandaKeyBoardHookFunction(int code, WPARAM wParam, LPARAM lParam) {
+
+ if (code >= 0 && (!bEmulateKeypresses || (bEmulateKeypresses && wParam != VK_NUMLOCK && wParam != VK_CAPITAL && wParam != VK_SCROLL)))
+ dwLastInput = GetTickCount();
+
+ return CallNextHookEx(hMirandaKeyBoardHook, code, wParam, lParam);
+}
+
+static LRESULT CALLBACK MirandaWndProcHookFunction(int code, WPARAM wParam, LPARAM lParam) {
+
+ if (code >= 0) {
+ /* WM_ACTIVATEAPP with nonzero wParam means someone brought miranda to foreground, that equals to input */
+ PCWPSTRUCT cwpInfo = (PCWPSTRUCT)lParam;
+ if(cwpInfo->message == WM_ACTIVATEAPP && cwpInfo->wParam)
+ dwLastInput = GetTickCount();
+ }
+
+ return CallNextHookEx(hMirandaWndProcHook, code, wParam, lParam);
+}
+
+
+//===================== Check Window Message function =====================
+
+// Took this snippet of code from "EventNotify" by micron-x, thx *g*
+// and updated with NGEventNotify and pete's patch
+// checks if the message-dialog window is already opened and returns:
+// TRUE - Windows found
+// FALSE - No window found
+
+HWND findMessageWindow(HANDLE hContact)
+{
+ HWND hwnd;
+ TCHAR newtitle[256];
+ char *szProto, *contactName, *szStatus;
+ CONTACTINFO ci = {0};
+
+ szProto = (char *)CallService(MS_PROTO_GETCONTACTBASEPROTO, (WPARAM)hContact, 0);
+ contactName = (char *)CallService(MS_CLIST_GETCONTACTDISPLAYNAME, (WPARAM)hContact, 0);
+ szStatus = (char *)CallService(MS_CLIST_GETSTATUSMODEDESCRIPTION, szProto==NULL?ID_STATUS_OFFLINE:DBGetContactSettingWord(hContact, szProto, "Status", ID_STATUS_OFFLINE), 0);
+
+ _snwprintf(newtitle, sizeof(newtitle), _T("%s (%s): %s"), contactName, szStatus, TranslateT("Message Received"));
+ if(hwnd = FindWindow(NULL, newtitle))
+ return hwnd;
+
+ _snwprintf(newtitle, sizeof(newtitle), _T("%s %s"), contactName, szStatus);
+ if(hwnd = FindWindow(NULL, newtitle))
+ return hwnd;
+ _snwprintf(newtitle, sizeof(newtitle), _T("%s (%s): %s"), contactName, szStatus, TranslateT("Message Session"));
+ if(hwnd = FindWindow(NULL, newtitle))
+ return hwnd;
+ _snwprintf(newtitle, sizeof(newtitle), _T("%s (%s): %s"), contactName, szStatus, TranslateT("Message Session is typing..."));
+ if(hwnd = FindWindow(NULL, newtitle))
+ return hwnd;
+ // search for the nconvers++ message window that uses the UIN
+ ci.cbSize = sizeof(CONTACTINFO);
+ ci.dwFlag = CNF_UNIQUEID;
+ ci.hContact = hContact;
+ if (!CallService(MS_CONTACT_GETCONTACTINFO, 0, (LPARAM)&ci)) {
+ switch(ci.type) {
+ case CNFT_BYTE:
+ _snwprintf(newtitle, sizeof(newtitle), _T("%s (%d) %s"), contactName, ci.bVal, szStatus);
+ break;
+ case CNFT_WORD:
+ _snwprintf(newtitle, sizeof(newtitle), _T("%s (%d) %s"), contactName, ci.wVal, szStatus);
+ break;
+ case CNFT_DWORD:
+ _snwprintf(newtitle, sizeof(newtitle), _T("%s (%d) %s"), contactName, ci.dVal, szStatus);
+ break;
+ case CNFT_ASCIIZ:
+ _snwprintf(newtitle, sizeof(newtitle), _T("%s (%s) %s"), contactName, ci.pszVal, szStatus);
+ break;
+ }
+ if(hwnd = FindWindow(NULL, newtitle))
+ return hwnd;
+ }
+
+ return NULL;
+}
+
+BOOL CheckMsgWnd(HANDLE hContact, BOOL *focus)
+{
+ if (ServiceExists(MS_MSG_GETWINDOWDATA)) { // use the new message API
+ MessageWindowData mwd;
+ MessageWindowInputData mwid;
+ mwid.cbSize = sizeof(MessageWindowInputData);
+ mwid.hContact = hContact;
+ mwid.uFlags = MSG_WINDOW_UFLAG_MSG_BOTH;
+ mwd.cbSize = sizeof(MessageWindowData);
+ mwd.hContact = hContact;
+ if (!CallService(MS_MSG_GETWINDOWDATA, (WPARAM)&mwid, (LPARAM)&mwd) && mwd.hwndWindow) {
+ *focus = mwd.uState & MSG_WINDOW_STATE_FOCUS;
+ return TRUE;
+ }
+ } else { // old way: find it by using the window class & title
+ HWND hwnd;
+
+ if(hwnd = findMessageWindow(hContact)) {
+ *focus = hwnd==GetForegroundWindow();
+ return TRUE;
+ }
+ }
+
+ *focus = FALSE;
+ return FALSE;
+}
+
+
+void countUnopenEvents(int *msgCount, int *fileCount, int *urlCount, int *otherCount)
+{
+ int nIndex;
+ CLISTEVENT *pCLEvent;
+
+ for (nIndex = 0; pCLEvent = (CLISTEVENT*)CallService(MS_CLIST_GETEVENT, -1, nIndex); nIndex++) {
+ DBEVENTINFO einfo = readEventInfo(pCLEvent->hDbEvent, pCLEvent->hContact);
+
+ if (metaCheckProtocol(einfo.szModule, pCLEvent->hContact, einfo.eventType))
+ switch (einfo.eventType) {
+ case EVENTTYPE_MESSAGE:
+ if (bFlashOnMsg)
+ (*msgCount)++;
+ break;
+ case EVENTTYPE_URL:
+ if (bFlashOnURL)
+ (*urlCount)++;
+ break;
+ case EVENTTYPE_FILE:
+ if (bFlashOnFile)
+ (*fileCount)++;
+ break;
+ default:
+ if (bFlashOnOther)
+ (*otherCount)++;
+ }
+ }
+ if (bFlashOnOther)
+ (*otherCount) += nExternCount;
+}