diff options
Diffstat (limited to 'plugins/StatusManager/src/aaa_main.cpp')
-rw-r--r-- | plugins/StatusManager/src/aaa_main.cpp | 541 |
1 files changed, 541 insertions, 0 deletions
diff --git a/plugins/StatusManager/src/aaa_main.cpp b/plugins/StatusManager/src/aaa_main.cpp new file mode 100644 index 0000000000..aa630ccb36 --- /dev/null +++ b/plugins/StatusManager/src/aaa_main.cpp @@ -0,0 +1,541 @@ +/* + AdvancedAutoAway Plugin for Miranda-IM (www.miranda-im.org) + Copyright 2003-2006 P. Boon + + 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 + + + Some code is copied from Miranda's AutoAway module +*/ + +#include "stdafx.h" + +#ifdef _DEBUG +#define SECS_PER_MINUTE 20 /* speedup */ +#else +#define SECS_PER_MINUTE 60 /* default I believe */ +#endif + +///////////////////////////////////////////////////////////////////////////////////////// + +CFakePlugin AAAPlugin(AAAMODULENAME); + +static HANDLE hEvents[3]; + +static BOOL ignoreLockKeys = FALSE; +static BOOL ignoreSysKeys = FALSE; +static BOOL ignoreAltCombo = FALSE; +static BOOL monitorMouse = TRUE; +static BOOL monitorKeyboard = TRUE; +static HWND confirmDialog; +static int mouseStationaryTimer; +HHOOK hMirandaMouseHook = nullptr; +HHOOK hMirandaKeyBoardHook = nullptr; +#pragma data_seg("Shared") +DWORD lastInput = 0; +POINT lastMousePos = { 0 }; +HHOOK hMouseHook = nullptr; +HHOOK hKeyBoardHook = nullptr; +#pragma data_seg() +#pragma comment(linker, "/section:Shared,rws") +DWORD lastMirandaInput = 0; +static UINT_PTR hAutoAwayTimer; +// prototypes +extern DWORD StatusModeToProtoFlag(int status); + +int AutoAwayOptInitialise(WPARAM wParam, LPARAM lParam); + +static int ProcessProtoAck(WPARAM, LPARAM lParam) +{ + ACKDATA *ack = (ACKDATA*)lParam; + if (ack->type != ACKTYPE_STATUS || ack->result != ACKRESULT_SUCCESS) + return 0; + + log_debug(0, "ProcessProtoAck: ack->szModule: %s", ack->szModule); + for (auto &it : protoList) { + log_debug(0, "chk: %s", it->m_szName); + if (!mir_strcmp(it->m_szName, ack->szModule)) { + log_debug(0, "ack->szModule: %s p.statusChanged: %d", ack->szModule, it->bStatusChanged); + if (!it->bStatusChanged) + it->bManualStatus = true; + + it->bStatusChanged = false; + } + } + + return 0; +} + +///////////////////////////////////////////////////////////////////////////////////////// +// Account control event + +static char* status2descr(int status) +{ + switch (status) { + case ACTIVE: return "ACTIVE"; + case STATUS1_SET: return "STATUS1_SET"; + case STATUS2_SET: return "STATUS2_SET"; + case SET_ORGSTATUS: return "SET_ORGSTATUS"; + case HIDDEN_ACTIVE: return "HIDDEN_ACTIVE"; + } + return "ERROR"; +} + +static int changeState(SMProto &setting, int mode, STATES newState) +{ + if (setting.curState == newState) + return 0; + + setting.oldState = setting.curState; + setting.curState = newState; + + log_debug(0, "%s state change: %s -> %s", setting.m_szName, status2descr(setting.oldState), status2descr(setting.curState)); + + if (setting.curState != SET_ORGSTATUS && setting.curState != ACTIVE && setting.bStatusChanged) { + /* change the awaymessage */ + if (setting.m_szMsg != nullptr) { + mir_free(setting.m_szMsg); + setting.m_szMsg = nullptr; + } + + if (AAAPlugin.getByte(StatusModeToDbSetting(setting.aaaStatus, SETTING_MSGCUSTOM), FALSE)) + setting.m_szMsg = AAAPlugin.getWStringA(StatusModeToDbSetting(setting.aaaStatus, SETTING_STATUSMSG)); + } + else if (setting.m_szMsg != nullptr) { + mir_free(setting.m_szMsg); + setting.m_szMsg = nullptr; + } + + if (setting.optionFlags & FLAG_ENTERIDLE) { + if (newState == ACTIVE) // we're returning back + Idle_Enter(-1); + else + Idle_Enter(mode); + } + + return 0; +} + +static int getIdleMode(int options) +{ + if ((options & FLAG_ONSAVER) && IsScreenSaverRunning()) + return 1; + + if ((options & FLAG_ONLOCK) && IsWorkstationLocked()) + return 2; + + if ((options & FLAG_ONTS) && IsTerminalDisconnected()) + return 3; + + if ((options & FLAG_FULLSCREEN) && IsFullScreen()) + return 4; + + return 0; +} + +static VOID CALLBACK AutoAwayTimer(HWND, UINT, UINT_PTR, DWORD) +{ + int statusChanged = FALSE; + int confirm = FALSE; + + for (auto &it : protoList) { + it->aaaStatus = ID_STATUS_DISABLED; + + if (it->optionFlags & FLAG_MONITORMIRANDA) + mouseStationaryTimer = (GetTickCount() - lastMirandaInput) / 1000; + else { + LASTINPUTINFO ii = { sizeof(ii) }; + GetLastInputInfo(&ii); + mouseStationaryTimer = (GetTickCount() - ii.dwTime) / 1000; + } + + int sts1Time = it->awayTime * SECS_PER_MINUTE; + int sts2Time = it->naTime * SECS_PER_MINUTE; + int sts1setTime = it->sts1setTimer == 0 ? 0 : (GetTickCount() - it->sts1setTimer) / 1000; + int currentMode = Proto_GetStatus(it->m_szName); + + int mode = getIdleMode(it->optionFlags); + + /* check states */ + if (it->curState == ACTIVE) { + if (((mouseStationaryTimer >= sts1Time && (it->optionFlags & FLAG_ONMOUSE)) || mode) && currentMode != it->lv1Status && it->statusFlags & StatusModeToProtoFlag(currentMode)) { + if (it->optionFlags & FLAG_ONMOUSE) + mode = 5; + + /* from ACTIVE to STATUS1_SET */ + it->m_lastStatus = it->originalStatusMode = Proto_GetStatus(it->m_szName); + it->aaaStatus = it->lv1Status; + it->sts1setTimer = GetTickCount(); + sts1setTime = 0; + it->bStatusChanged = statusChanged = true; + changeState(*it, mode, STATUS1_SET); + } + else if (mouseStationaryTimer >= sts2Time && currentMode == it->lv1Status && currentMode != it->lv2Status && (it->optionFlags & FLAG_SETNA) && (it->statusFlags & StatusModeToProtoFlag(currentMode))) { + /* from ACTIVE to STATUS2_SET */ + it->m_lastStatus = it->originalStatusMode = Proto_GetStatus(it->m_szName); + it->aaaStatus = it->lv2Status; + it->bStatusChanged = statusChanged = true; + changeState(*it, mode, STATUS2_SET); + } + } + + if (it->curState == STATUS1_SET) { + if ((mouseStationaryTimer < sts1Time && !mode) && !(it->optionFlags & FLAG_RESET)) { + /* from STATUS1_SET to HIDDEN_ACTIVE */ + changeState(*it, mode, HIDDEN_ACTIVE); + it->m_lastStatus = Proto_GetStatus(it->m_szName); + } + else if (((mouseStationaryTimer < sts1Time) && !mode) && + ((it->optionFlags & FLAG_LV2ONINACTIVE) || (!(it->optionFlags & FLAG_SETNA))) && + (it->optionFlags & FLAG_RESET)) { + /* from STATUS1_SET to SET_ORGSTATUS */ + changeState(*it, mode, SET_ORGSTATUS); + } + else if ((it->optionFlags & FLAG_SETNA) && sts1setTime >= sts2Time) { + /* when set STATUS2, currentMode doesn't have to be in the selected status list (statusFlags) */ + /* from STATUS1_SET to STATUS2_SET */ + it->m_lastStatus = Proto_GetStatus(it->m_szName); + it->aaaStatus = it->lv2Status; + it->bStatusChanged = statusChanged = true; + changeState(*it, mode, STATUS2_SET); + } + } + + if (it->curState == STATUS2_SET) { + if (mouseStationaryTimer < sts2Time && !mode && (it->optionFlags & FLAG_RESET)) { + /* from STATUS2_SET to SET_ORGSTATUS */ + changeState(*it, mode, SET_ORGSTATUS); + } + else if (mouseStationaryTimer < sts2Time && !mode && !(it->optionFlags & FLAG_RESET)) { + /* from STATUS2_SET to HIDDEN_ACTIVE */ + /* Remember: after status1 is set, and "only on inactive" is NOT set, it implies !reset. */ + changeState(*it, mode, HIDDEN_ACTIVE); + it->m_lastStatus = Proto_GetStatus(it->m_szName); + } + } + + if (it->curState == HIDDEN_ACTIVE) { + if (it->bManualStatus) { + /* HIDDEN_ACTIVE to ACTIVE */ + // it->bStatusChanged = false; + changeState(*it, mode, ACTIVE); + it->sts1setTimer = 0; + it->bManualStatus = false; + } + else if ((it->optionFlags & FLAG_SETNA) && currentMode == it->lv1Status && + currentMode != it->lv2Status && (it->statusFlags & StatusModeToProtoFlag(currentMode)) && + (mouseStationaryTimer >= sts2Time || (sts1setTime >= sts2Time && !(it->optionFlags & FLAG_LV2ONINACTIVE)))) { + /* HIDDEN_ACTIVE to STATUS2_SET */ + it->m_lastStatus = it->originalStatusMode = Proto_GetStatus(it->m_szName); + it->aaaStatus = it->lv2Status; + it->bStatusChanged = statusChanged = true; + changeState(*it, mode, STATUS2_SET); + } + } + if (it->curState == SET_ORGSTATUS) { + /* SET_ORGSTATUS to ACTIVE */ + it->m_lastStatus = Proto_GetStatus(it->m_szName); + it->aaaStatus = it->originalStatusMode; + confirm = (it->optionFlags & FLAG_CONFIRM) ? TRUE : confirm; + it->bStatusChanged = statusChanged = true; + changeState(*it, mode, ACTIVE); + it->sts1setTimer = 0; + } + it->bManualStatus = false; + } + + if (confirm || statusChanged) { + TProtoSettings ps(protoList); // make a copy of data not to pollute main array + for (auto &it : ps) + it->m_status = it->aaaStatus; + + if (confirm) + confirmDialog = ShowConfirmDialogEx(&ps, AAAPlugin.getWord(SETTING_CONFIRMDELAY, 5)); + else if (statusChanged) + SetStatusEx(ps); + } +} + +///////////////////////////////////////////////////////////////////////////////////////// +// Windows hooks + +static LRESULT CALLBACK MirandaMouseHookFunction(int code, WPARAM wParam, LPARAM lParam) +{ + if (code >= 0) { + PMOUSEHOOKSTRUCT mouseInfo = (PMOUSEHOOKSTRUCT)lParam; + POINT pt = mouseInfo->pt; + + /* TioDuke's KeyBoardNotifyExt: only update if a Miranda window is focused */ + DWORD pid; + GetWindowThreadProcessId(GetForegroundWindow(), &pid); + if (pid != GetCurrentProcessId()) + return CallNextHookEx(hMirandaMouseHook, code, wParam, lParam); + + if (pt.x != lastMousePos.x || pt.y != lastMousePos.y) { + lastMousePos = pt; + lastMirandaInput = GetTickCount(); + } + } + + return CallNextHookEx(hMirandaMouseHook, code, wParam, lParam); +} + +static LRESULT CALLBACK MirandaKeyBoardHookFunction(int code, WPARAM wParam, LPARAM lParam) +{ + if (code >= 0) { + if (ignoreAltCombo) { + if (((GetKeyState(VK_MENU) < 0) || (wParam == VK_MENU)) || + ((GetKeyState(VK_TAB) < 0) || (wParam == VK_TAB)) || + ((GetKeyState(VK_SHIFT) < 0) || (wParam == VK_SHIFT)) || + ((GetKeyState(VK_CONTROL) < 0) || (wParam == VK_CONTROL)) || + ((GetKeyState(VK_ESCAPE) < 0) || (wParam == VK_ESCAPE)) || + ((GetKeyState(VK_LWIN) < 0) || (wParam == VK_LWIN)) || + ((GetKeyState(VK_RWIN) < 0) || (wParam == VK_RWIN))) { + return CallNextHookEx(hMirandaKeyBoardHook, code, wParam, lParam); + } + } + + switch (wParam) { + case VK_NUMLOCK: + case VK_CAPITAL: + case VK_SCROLL: + if (!ignoreLockKeys) + lastMirandaInput = GetTickCount(); + break; + + case VK_TAB: + case VK_SHIFT: + case VK_CONTROL: + case VK_MENU: + case VK_ESCAPE: + case VK_LWIN: + case VK_RWIN: + if (!ignoreSysKeys) + lastMirandaInput = GetTickCount(); + break; + + default: + lastMirandaInput = GetTickCount(); + break; + } + } + + return CallNextHookEx(hMirandaKeyBoardHook, code, wParam, lParam); +} + +static LRESULT CALLBACK MouseHookFunction(int code, WPARAM wParam, LPARAM lParam) +{ + if (code >= 0) { + PMOUSEHOOKSTRUCT mouseInfo = (PMOUSEHOOKSTRUCT)lParam; + POINT pt = mouseInfo->pt; + + /* TioDuke's KeyBoardNotifyExt: also grab clicks */ + if ((wParam >= WM_NCLBUTTONDOWN && wParam <= WM_NCXBUTTONDBLCLK && wParam != 0x00AA) || (wParam >= WM_LBUTTONDOWN && wParam <= WM_XBUTTONDBLCLK)) + lastInput = GetTickCount(); + + if (pt.x != lastMousePos.x || pt.y != lastMousePos.y) { + lastMousePos = pt; + lastInput = GetTickCount(); + } + } + + return CallNextHookEx(hMouseHook, code, wParam, lParam); +} + +static LRESULT CALLBACK KeyBoardHookFunction(int code, WPARAM wParam, LPARAM lParam) +{ + if (code >= 0) { + if (ignoreAltCombo) { + if (((GetKeyState(VK_MENU) < 0) || (wParam == VK_MENU)) || + ((GetKeyState(VK_TAB) < 0) || (wParam == VK_TAB)) || + ((GetKeyState(VK_SHIFT) < 0) || (wParam == VK_SHIFT)) || + ((GetKeyState(VK_CONTROL) < 0) || (wParam == VK_CONTROL)) || + ((GetKeyState(VK_ESCAPE) < 0) || (wParam == VK_ESCAPE)) || + ((GetKeyState(VK_LWIN) < 0) || (wParam == VK_LWIN)) || + ((GetKeyState(VK_RWIN) < 0) || (wParam == VK_RWIN))) { + return CallNextHookEx(hKeyBoardHook, code, wParam, lParam); + } + } + + switch (wParam) { + case VK_NUMLOCK: + case VK_CAPITAL: + case VK_SCROLL: + if (!ignoreLockKeys) + lastInput = GetTickCount(); + break; + + case VK_TAB: + case VK_SHIFT: + case VK_CONTROL: + case VK_MENU: + case VK_ESCAPE: + case VK_LWIN: + case VK_RWIN: + if (!ignoreSysKeys) + lastInput = GetTickCount(); + break; + + default: + lastInput = GetTickCount(); + break; + } + } + + return CallNextHookEx(hKeyBoardHook, code, wParam, lParam); +} + +static int HookWindowsHooks(int hookMiranda, int hookAll) +{ + if (hookMiranda) { + if (monitorKeyboard && hMirandaKeyBoardHook == nullptr) + hMirandaKeyBoardHook = SetWindowsHookEx(WH_KEYBOARD, MirandaKeyBoardHookFunction, nullptr, GetCurrentThreadId()); + if (monitorMouse && hMirandaMouseHook == nullptr) + hMirandaMouseHook = SetWindowsHookEx(WH_MOUSE, MirandaMouseHookFunction, nullptr, GetCurrentThreadId()); + } + if (hookAll) { + if (monitorKeyboard && hKeyBoardHook == nullptr) + hKeyBoardHook = SetWindowsHookEx(WH_KEYBOARD, KeyBoardHookFunction, nullptr, GetCurrentThreadId()); + if (monitorMouse && hMouseHook == nullptr) + hMouseHook = SetWindowsHookEx(WH_MOUSE, MouseHookFunction, nullptr, GetCurrentThreadId()); + } + + return 0; +} + +static int UnhookWindowsHooks() +{ + UnhookWindowsHookEx(hMouseHook); + UnhookWindowsHookEx(hKeyBoardHook); + UnhookWindowsHookEx(hMirandaMouseHook); + UnhookWindowsHookEx(hMirandaKeyBoardHook); + + hMouseHook = hKeyBoardHook = hMirandaMouseHook = hMirandaKeyBoardHook = nullptr; + return 0; +} + +///////////////////////////////////////////////////////////////////////////////////////// +// Load from DB + +int LoadAutoAwaySetting(SMProto &autoAwaySetting, char *protoName) +{ + char setting[128]; + mir_snprintf(setting, "%s_OptionFlags", protoName); + autoAwaySetting.optionFlags = AAAPlugin.getWord(setting, FLAG_LV2ONINACTIVE | FLAG_RESET | FLAG_ENTERIDLE); + mir_snprintf(setting, "%s_AwayTime", protoName); + autoAwaySetting.awayTime = AAAPlugin.getWord(setting, SETTING_AWAYTIME_DEFAULT); + mir_snprintf(setting, "%s_NATime", protoName); + autoAwaySetting.naTime = AAAPlugin.getWord(setting, SETTING_NATIME_DEFAULT); + mir_snprintf(setting, "%s_StatusFlags", protoName); + autoAwaySetting.statusFlags = AAAPlugin.getWord(setting, StatusModeToProtoFlag(ID_STATUS_ONLINE) | StatusModeToProtoFlag(ID_STATUS_FREECHAT)); + + int flags; + if (g_bAAASettingSame) + flags = 0xFFFFFF; + else + flags = CallProtoService(protoName, PS_GETCAPS, PFLAGNUM_2, 0) & ~CallProtoService(protoName, PS_GETCAPS, (WPARAM)PFLAGNUM_5, 0); + mir_snprintf(setting, "%s_Lv1Status", protoName); + autoAwaySetting.lv1Status = AAAPlugin.getWord(setting, (flags & StatusModeToProtoFlag(ID_STATUS_AWAY)) ? ID_STATUS_AWAY : ID_STATUS_OFFLINE); + mir_snprintf(setting, "%s_Lv2Status", protoName); + autoAwaySetting.lv2Status = AAAPlugin.getWord(setting, (flags & StatusModeToProtoFlag(ID_STATUS_NA)) ? ID_STATUS_NA : ID_STATUS_OFFLINE); + + return 0; +} + +void AAAUnloadOptions() +{ + UnhookWindowsHooks(); + if (hAutoAwayTimer != 0) + KillTimer(nullptr, hAutoAwayTimer); +} + +void AAALoadOptions() +{ + // if bOverride is enabled, samesettings will be ignored (for options loading) + AAAUnloadOptions(); + + bool monitorMiranda = false, monitorAll = false; + + ignoreLockKeys = AAAPlugin.getByte(SETTING_IGNLOCK, FALSE); + ignoreSysKeys = AAAPlugin.getByte(SETTING_IGNSYSKEYS, FALSE); + ignoreAltCombo = AAAPlugin.getByte(SETTING_IGNALTCOMBO, FALSE); + monitorMouse = AAAPlugin.getByte(SETTING_MONITORMOUSE, TRUE) != 0; + monitorKeyboard = AAAPlugin.getByte(SETTING_MONITORKEYBOARD, TRUE) != 0; + lastInput = lastMirandaInput = GetTickCount(); + + for (auto &it : protoList) { + char *protoName; + if (g_bAAASettingSame) + protoName = SETTING_ALL; + else + protoName = it->m_szName; + LoadAutoAwaySetting(*it, protoName); + + if (it->optionFlags & FLAG_MONITORMIRANDA) + monitorMiranda = true; + else if (ignoreLockKeys || ignoreSysKeys || ignoreAltCombo || (monitorMouse != monitorKeyboard)) + monitorAll = true; + } + + HookWindowsHooks(monitorMiranda, monitorAll); + hAutoAwayTimer = SetTimer(nullptr, 0, AAAPlugin.getWord(SETTING_AWAYCHECKTIMEINSECS, 5) * 1000, AutoAwayTimer); +} + +///////////////////////////////////////////////////////////////////////////////////////// +// Inits & stuff + +static int AutoAwayShutdown(WPARAM, LPARAM) +{ + AAAUnloadOptions(); + return 0; +} + +static int AAAModuleLoaded(WPARAM, LPARAM) +{ + hEvents[0] = HookEvent(ME_OPT_INITIALISE, AutoAwayOptInitialise); + hEvents[1] = HookEvent(ME_SYSTEM_PRESHUTDOWN, AutoAwayShutdown); + hEvents[2] = HookEvent(ME_PROTO_ACK, ProcessProtoAck); + + mouseStationaryTimer = 0; + lastInput = lastMirandaInput = GetTickCount(); + + AAALoadOptions(); + return 0; +} + +void AdvancedAutoAwayLoad() +{ + g_bAAASettingSame = AAAPlugin.getByte(SETTING_SAMESETTINGS); + + if (g_bMirandaLoaded) + AAAModuleLoaded(0, 0); + else + HookEvent(ME_SYSTEM_MODULESLOADED, AAAModuleLoaded); +} + +void AdvancedAutoAwayUnload() +{ + if (g_bMirandaLoaded) + AutoAwayShutdown(0, 0); + + KillModuleOptions(&AAAPlugin); + + for (auto &it : hEvents) { + UnhookEvent(it); + it = nullptr; + } + + protoList.destroy(); +} |