diff options
Diffstat (limited to 'kbdnotify/main.c')
-rw-r--r-- | kbdnotify/main.c | 1461 |
1 files changed, 1461 insertions, 0 deletions
diff --git a/kbdnotify/main.c b/kbdnotify/main.c new file mode 100644 index 0000000..033b95b --- /dev/null +++ b/kbdnotify/main.c @@ -0,0 +1,1461 @@ + +/* + 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.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 "AggressiveOptimize.h" + +#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 "../headers_c/newpluginapi.h"
+#include "../headers_c/m_database.h"
+#include "../headers_c/m_options.h" +#include "../headers_c/m_clist.h" +#include "../headers_c/m_system.h" +#include "../headers_c/m_langpack.h" +#include "../headers_c/m_protocols.h" +#include "../headers_c/m_protosvc.h" +#include "../headers_c/m_contacts.h" +#include "../headers_c/m_message.h" +#include "../headers_c/m_utils.h" +#include "../headers_c/m_icq.h" +#include "../headers_c/m_metacontacts.h" +#include "../headers_c/m_updater.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; +PLUGINLINK *pluginLink; + + +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 = FALSE; +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; + + +PLUGININFO pluginInfo={ + sizeof(PLUGININFO), + "Keyboard Notify Ext.", + PLUGIN_MAKE_VERSION(1,5,7,6), + "Flashes your keyboard LEDs when a message has arrived", + "TioDuke", + "tioduke@yahoo.ca", + "© 2002-2003 M.Öberg, 2004 Std, 2005-2006 TioDuke", + "http://addons.miranda-im.org/", + 0, //not transient + 0 //doesn't replace anything built-in +}; + + +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("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 (!(GetWindowLong(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 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 EnableService(WPARAM wParam, LPARAM lParam) +{ + bFlashingEnabled = TRUE; + + return 0; +} + + +static int DisableService(WPARAM wParam, LPARAM lParam) +{ + bFlashingEnabled = FALSE; + + return 0; +} + + +static int StartBlinkService(WPARAM wParam, LPARAM lParam) +{ + nExternCount += (unsigned int)wParam; + if (bFlashOnOther && checkNotifyOptions() && checkGlobalStatus() && checkGlobalXstatus()) { + if (lParam) + useExternSequence((char *)lParam); + SetEvent(hFlashEvent); + } + + return 0; +} + + +static int EventsWereOpenedService(WPARAM wParam, LPARAM lParam) +{ + if ((unsigned int)wParam > nExternCount) + nExternCount = 0; + else + nExternCount -= (unsigned int)wParam; + + return 0; +} + + +static int IsFlashingActiveService(WPARAM wParam, LPARAM lParam) +{ + if (!bReminderDisabled) + return 0; + + return (int)getCurrentSequenceString(); +} + + +static int NormalizeSequenceService(WPARAM wParam, LPARAM lParam) +{ + char strAux[MAX_PATH+1], *strIn = (char *)lParam; + + mir_snprintf(strAux, MAX_PATH, "%s", strIn); + mir_snprintf(strIn, MAX_PATH, "%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 = (char **)malloc(count * sizeof(char *));
+ 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] = (char *)malloc(strlen(dbv.pszVal) + 1);
+ if (ProcessList.szFileName[i]) + strcpy(ProcessList.szFileName[i], dbv.pszVal); + 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(char *prefixName, size_t maxLen)
+{ + size_t len; + char profileName[MAX_PATH+1], *str; + + getAbsoluteProfileName(profileName, MAX_PATH); + + while (str = strchr(profileName, '\\')) + *str = '/'; + if ((len = strlen(profileName)) <= maxLen) + strcpy(prefixName, profileName); + else { + str = profileName + len - maxLen / 2; + mir_snprintf(prefixName, maxLen / 2, "%s", profileName); + strcat(prefixName, str); + } +} + + +// ** +// ** Everything below is just Miranda init/uninit stuff +// ** + + +static int ModulesLoaded(WPARAM wParam, LPARAM lParam) +{ + char eventPrefix[MAX_PATH+1], eventName[MAX_PATH+1]; + + LoadProcsLibrary(); + if (bWindowsNT && dWinVer >= 5) + MyGetLastInputInfo = (BOOL (WINAPI *)(PLASTINPUTINFO)) GetProcAddress(GetModuleHandle("user32"), "GetLastInputInfo"); + else + MyGetLastInputInfo = NULL; + + createProtocolList(); + LoadSettings(); + + // Create some synchronisation objects + createEventPrefix(eventPrefix, MAX_PATH - 11); + mir_snprintf(eventName, sizeof(eventName), "%s/FlashEvent", eventPrefix); + hFlashEvent = CreateEvent(NULL, FALSE, FALSE, eventName); + mir_snprintf(eventName, sizeof(eventName), "%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); + if (ServiceExists(MS_UPDATE_REGISTERFL)) + CallService(MS_UPDATE_REGISTERFL, (WPARAM)2071, (LPARAM)&pluginInfo); + + return 0; +} + + + +BOOL WINAPI DllMain(HINSTANCE hinstDLL, DWORD fdwReason, LPVOID lpvReserved) +{ + + hInst = hinstDLL; + + return TRUE; + +} + + + +__declspec(dllexport) PLUGININFO* MirandaPluginInfo(DWORD mirandaVersion) +{ + + return &pluginInfo; + +} + + + +int __declspec(dllexport) Load(PLUGINLINK *link) +{ + + pluginLink = link; + + 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); +} + + +int __declspec(dllexport) 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; + char 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); + + mir_snprintf(newtitle, sizeof(newtitle), "%s (%s): %s", contactName, szStatus, Translate("Message Received")); + if(hwnd = FindWindow(NULL, newtitle)) + return hwnd; + + mir_snprintf(newtitle, sizeof(newtitle), "%s %s", contactName, szStatus); + if(hwnd = FindWindow(NULL, newtitle)) + return hwnd; + mir_snprintf(newtitle, sizeof(newtitle), "%s (%s): %s", contactName, szStatus, Translate("Message Session")); + if(hwnd = FindWindow(NULL, newtitle)) + return hwnd; + mir_snprintf(newtitle, sizeof(newtitle), "%s (%s): %s", contactName, szStatus, Translate("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: + mir_snprintf(newtitle, sizeof(newtitle), "%s (%d) %s", contactName, ci.bVal, szStatus); + break; + case CNFT_WORD: + mir_snprintf(newtitle, sizeof(newtitle), "%s (%d) %s", contactName, ci.wVal, szStatus); + break; + case CNFT_DWORD: + mir_snprintf(newtitle, sizeof(newtitle), "%s (%d) %s", contactName, ci.dVal, szStatus); + break; + case CNFT_ASCIIZ: + mir_snprintf(newtitle, sizeof(newtitle), "%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; +} |