/* Name: kmhook_callwndproc Copyright: Copyright (C) SIL International. Documentation: Description: Create Date: 1 Aug 2006 Modified Date: 22 Apr 2015 Authors: mcdurdin Related Files: Dependencies: Bugs: Todo: Notes: History: 01 Aug 2006 - mcdurdin - Visual keyboard updating 30 May 2007 - mcdurdin - I864 - Log exceptions in hook procedures 11 Mar 2009 - mcdurdin - I1894 - Fix threading bugs introduced in I1888 11 Dec 2009 - mcdurdin - I934 - x64 - Initial version 11 Dec 2009 - mcdurdin - I934 - x64 - keyboard hook per thread 12 Mar 2010 - mcdurdin - I934 - x64 - Complete 12 Mar 2010 - mcdurdin - I2229 - Remove hints and warnings 29 Mar 2010 - mcdurdin - I2266 - Reduce chatter when switching languages 09 Apr 2010 - mcdurdin - I2292 - Keyman init was re-entrant 19 Apr 2010 - mcdurdin - I2297 - Use global cache to remember last active keyboard for all apps 04 May 2010 - mcdurdin - I2351 - Robustness - verify _td return value 04 May 2010 - mcdurdin - I2356 - Don't clear keyboard hook if new focus is in same thread 04 May 2010 - mcdurdin - I2297 - Focus and keyboard switching reliability 10 May 2010 - mcdurdin - Enable exception logging 29 Jun 2010 - mcdurdin - I2443 - Always do focus change so language switch affects keyman.exe too 29 Jun 2010 - mcdurdin - I2435 - Make Keyman Engine unload more reliably by sending rather than posting KM_EXIT 29 Jun 2010 - mcdurdin - I2448 - Regression - keyboard switching in .285 30 Nov 2010 - mcdurdin - I2546 - When switching languages, Keyman Engine did not get notified of new language reliably 30 Nov 2010 - mcdurdin - I2547 - Language switch window sometimes appears after Alt+Tab, Shift 19 Aug 2011 - mcdurdin - I3025 - Alt+LShift, Esc should cancel language switch window 06 Feb 2012 - mcdurdin - I3226 - Keyman Engine appears to not be receiving some WM_SETFOCUS notifications 04 Nov 2012 - mcdurdin - I3531 - V9.0 - Merge of I3226 - Keyman Engine appears to not be receiving some WM_SETFOCUS notifications 28 Nov 2012 - mcdurdin - I3594 - V9.0 - Remove old SelectKeyboard code and related messages 01 Dec 2012 - mcdurdin - I3617 - V9.0 - Keyboard hook obsolete, strip out code 07 Nov 2013 - mcdurdin - I3949 - V9.0 - Keyboard selection and notification needs consolidation 16 Jun 2014 - mcdurdin - I4271 - V9.0 - Switch language for all applications is not working 23 Jun 2014 - mcdurdin - I4288 - V9.0 - Consolidate reporting of active keyboard into ReportActiveKeyboard 03 Aug 2014 - mcdurdin - I4326 - V9.0 - Switch-off hotkey not working, then keyboard hotkey stopped working (win 8.1 jeremy) [High] 22 Apr 2015 - mcdurdin - I4674 - V9.0 - Hotkeys do not always work consistently */ #include "pch.h" /* * kmnCallWndProc * * Parameters: nCode Specifies whether to process or ignore the message * wParam (unused) * lParam Points to a message structure to be processed * * Returns: Should return 0 * * Called by: Windows * * CallWndFunc handles non-client window messages and window activation * messages that aren't Sent or Posted. */ LRESULT _kmnCallWndProc(int nCode, WPARAM wParam, LPARAM lParam); LRESULT CALLBACK kmnCallWndProc(int nCode, WPARAM wParam, LPARAM lParam) { LRESULT res = 0; #ifdef _DEBUG_EXCEPTION res = _kmnCallWndProc(nCode,wParam,lParam); #else __try { res = _kmnCallWndProc(nCode,wParam,lParam); } __except(ExceptionMessage("kmnCallWndProc", GetExceptionInformation())) { } #endif return res; } // I3617 BOOL IsSysTrayWindow(HWND hwnd); void ProcessWMKeyman(HWND hwnd, WPARAM wParam, LPARAM lParam); void ProcessWMKeymanControlInternal(HWND hwnd, WPARAM wParam, LPARAM lParam); /* Note: following copied from kmhook_keyboard for now */ HWND IsLanguageSwitchWindowVisible() { // I4326 HWND hwndLanguageSwitch = FindWindow("TfrmLanguageSwitch", NULL); if(hwndLanguageSwitch == NULL) return FALSE; return IsWindowVisible(hwndLanguageSwitch) ? hwndLanguageSwitch : NULL; } void SendToLanguageSwitchWindow(HWND hwndLanguageSwitch, WPARAM wParam, LPARAM lParam) { // I4326 DWORD keyFlags = (DWORD) lParam; // I4124 PostMessage(hwndLanguageSwitch, wm_keyman_control, keyFlags & LLKHF_UP ? KMC_KEYUP : KMC_KEYDOWN, wParam); // I4124 } LRESULT _kmnCallWndProc(int nCode, WPARAM wParam, LPARAM lParam) { CWPSTRUCT *cp; if(nCode >= 0) { cp = (CWPSTRUCT *)lParam; if(!Globals::get_Keyman_Initialised()) { //DebugLastError("get_Keyman_Initialised"); } else { /* SendDebugMessageFormat(cp->hwnd, sdmGlobal, 0, "kmnCallWndProc %s %x %x " "{hwnd:%d msg:%d wp:%d lp:%d} ctrl: %x/%x alt: %x/%x shift: %x/%x", MessageName(cp->message), cp->wParam, cp->lParam, cp->hwnd, cp->message, cp->wParam, cp->lParam, GetKeyState(VK_CONTROL), GetAsyncKeyState(VK_CONTROL), GetKeyState(VK_MENU), GetAsyncKeyState(VK_MENU), GetKeyState(VK_SHIFT), GetAsyncKeyState(VK_SHIFT)); */ PKEYMAN64THREADDATA _td = ThreadGlobals(); if(!_td) { if(!Globals::CheckControllers()) { DebugLastError("CheckControllers"); return CallNextHookEx(Globals::get_hhookCallWndProc(), nCode, wParam, lParam); } if(!Globals_InitThread()) { DebugLastError("Globals_InitThread"); return CallNextHookEx(Globals::get_hhookCallWndProc(), nCode, wParam, lParam); } _td = ThreadGlobals(); if(!_td) { DebugLastError("ThreadGlobals"); return CallNextHookEx(Globals::get_hhookCallWndProc(), nCode, wParam, lParam); } } if(!_td->FInitialised) // Keyman keyboard is being selected for first time in active process { if(!Globals::CheckControllers()) { DebugLastError("CheckControllers"); return CallNextHookEx(Globals::get_hhookCallWndProc(), nCode, wParam, lParam); } if(!InitialiseProcess(cp->hwnd)) { DebugLastError("InitialiseProcess"); return CallNextHookEx(Globals::get_hhookCallWndProc(), nCode, wParam, lParam); } } switch(cp->message) { case WM_KILLFOCUS: { HWND hwnd = IsLanguageSwitchWindowVisible(); // I4326 if(hwnd != NULL && !Globals::IsControllerProcess()) SendToLanguageSwitchWindow(hwnd, VK_ESCAPE, 0); // I3025 SendDebugMessageFormat(cp->hwnd, sdmGlobal, 0, "WM_KILLFOCUS %x -> %x", cp->hwnd, cp->wParam); // I3226 // I3531 if(!IsSysTrayWindow(cp->hwnd) && !Globals::IsControllerWindow(cp->hwnd) && !Globals::IsControllerProcess()) // I2443 - always do the focus change now? really unsure about this one { PostMessage(cp->hwnd, wm_keyman, KM_FOCUSCHANGED, 0); SendDebugMessageFormat(cp->hwnd, sdmGlobal, 0, "WM_KILLFOCUS -- Telling Keyman focus has changed"); KMHideIM(); if(_td->app) _td->app->ResetContext(); } } break; case WM_SETFOCUS: if (IsSysTrayWindow(cp->hwnd)) // I2443 - always do the focus change now? really unsure about this one SendDebugMessageFormat(cp->hwnd, sdmGlobal, 0, "WM_SETFOCUS -- not hooking because IsSysTrayWindow"); else if (Globals::IsControllerWindow(cp->hwnd)) SendDebugMessageFormat(cp->hwnd, sdmGlobal, 0, "WM_SETFOCUS -- not hooking because IsControllerWindow"); else if (Globals::IsControllerProcess()) SendDebugMessageFormat(cp->hwnd, sdmGlobal, 0, "WM_SETFOCUS -- not hooking because IsControllerProcess"); else { PostMessage(cp->hwnd, wm_keyman, KM_FOCUSCHANGED, KMF_WINDOWCHANGED); SendDebugMessageFormat(cp->hwnd, sdmGlobal, 0, "WM_SETFOCUS %x <- %x", cp->hwnd, cp->wParam); // I3226 // I3531 if (_td->app) _td->app->ResetContext(); } break; case WM_ACTIVATE: if(cp->wParam == WA_ACTIVE || cp->wParam == WA_CLICKACTIVE) { } break; case WM_INPUTLANGCHANGE: SendDebugMessageFormat(cp->hwnd, sdmGlobal, 0, "WM_INPUTLANGCHANGE %x %x Hwnd=%x Parent=%x Focus=%x Active=%x", cp->wParam, cp->lParam, cp->hwnd, GetParent(cp->hwnd), GetFocus(), GetActiveWindow()); ReportActiveKeyboard(_td, 0); // I4288 // The input language has changed, so tell Keyman window about it break; default: if(cp->message == wm_keyman) // I2435 - Make Keyman Engine unload more reliably by sending a broadcast message to all apps rather than posting { SendDebugMessageFormat(0, sdmInternat, 0, "CallWndProc: wm_keyman hwnd=%x %x %x", cp->hwnd, cp->wParam, cp->lParam); // I4674 ProcessWMKeyman(cp->hwnd, cp->wParam, cp->lParam); } else if(cp->message == wm_keyman_control_internal) { SendDebugMessageFormat(0, sdmInternat, 0, "CallWndProc: wm_keyman_control_internal hwnd=%x %x %x", cp->hwnd, cp->wParam, cp->lParam); // I4674 ProcessWMKeymanControlInternal(cp->hwnd, cp->wParam, cp->lParam); // I4271 } } } } return CallNextHookEx(Globals::get_hhookCallWndProc(), nCode, wParam, lParam ); }