OSDN Git Service

release input queue and wait input event atomically
[yamy/yamy.git] / hook.cpp
1 //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\r
2 // hook.cpp\r
3 \r
4 \r
5 #define _HOOK_CPP\r
6 \r
7 #include "misc.h"\r
8 \r
9 #include "hook.h"\r
10 #include "stringtool.h"\r
11 \r
12 #include <locale.h>\r
13 #include <imm.h>\r
14 #include <richedit.h>\r
15 \r
16 \r
17 ///\r
18 #define HOOK_DATA_NAME _T("{08D6E55C-5103-4e00-8209-A1C4AB13BBEF}") _T(VERSION)\r
19 #ifdef _WIN64\r
20 #define HOOK_DATA_NAME_ARCH _T("{290C0D51-8AEE-403d-9172-E43D46270996}") _T(VERSION)\r
21 #else // !_WIN64\r
22 #define HOOK_DATA_NAME_ARCH _T("{716A5DEB-CB02-4438-ABC8-D00E48673E45}") _T(VERSION)\r
23 #endif // !_WIN64\r
24 \r
25 // Some applications use different values for below messages\r
26 // when double click of title bar.\r
27 #define SC_MAXIMIZE2 (SC_MAXIMIZE + 2)\r
28 #define SC_MINIMIZE2 (SC_MINIMIZE + 2)\r
29 #define SC_RESTORE2 (SC_RESTORE + 2)\r
30 \r
31 // Debug Macros\r
32 #ifdef NDEBUG\r
33 #define HOOK_RPT0(msg)\r
34 #define HOOK_RPT1(msg, arg1)\r
35 #define HOOK_RPT2(msg, arg1, arg2)\r
36 #define HOOK_RPT3(msg, arg1, arg2, arg3)\r
37 #define HOOK_RPT4(msg, arg1, arg2, arg3, arg4)\r
38 #define HOOK_RPT5(msg, arg1, arg2, arg3, arg4, arg5)\r
39 #else\r
40 #define HOOK_RPT0(msg) if (g.m_isLogging) { _RPT0(_CRT_WARN, msg); }\r
41 #define HOOK_RPT1(msg, arg1) if (g.m_isLogging) { _RPT1(_CRT_WARN, msg, arg1); }\r
42 #define HOOK_RPT2(msg, arg1, arg2) if (g.m_isLogging) { _RPT2(_CRT_WARN, msg, arg1, arg2); }\r
43 #define HOOK_RPT3(msg, arg1, arg2, arg3) if (g.m_isLogging) { _RPT3(_CRT_WARN, msg, arg1, arg2, arg3); }\r
44 #define HOOK_RPT4(msg, arg1, arg2, arg3, arg4) if (g.m_isLogging) { _RPT4(_CRT_WARN, msg, arg1, arg2, arg3, arg4); }\r
45 #define HOOK_RPT5(msg, arg1, arg2, arg3, arg4, arg5) if (g.m_isLogging) { _RPT5(_CRT_WARN, msg, arg1, arg2, arg3, arg4, arg5); }\r
46 #endif\r
47 \r
48 //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\r
49 // Global Variables\r
50 \r
51 \r
52 DllExport HookData *g_hookData;                 ///\r
53 \r
54 ///\r
55 class HookDataArch\r
56 {\r
57 public:\r
58         HHOOK m_hHookGetMessage;                        ///\r
59         HHOOK m_hHookCallWndProc;                       ///\r
60 };\r
61 \r
62 static HookDataArch *s_hookDataArch;\r
63 \r
64 struct Globals {\r
65         HANDLE m_hHookData;                             ///\r
66         HANDLE m_hHookDataArch;                 ///\r
67         HWND m_hwndFocus;                               ///\r
68         HINSTANCE m_hInstDLL;                           ///\r
69         bool m_isInMenu;                                ///\r
70         UINT m_WM_MAYU_MESSAGE;                 ///\r
71         bool m_isImeLock;                               ///\r
72         bool m_isImeCompositioning;                     ///\r
73         HHOOK m_hHookMouseProc;                 ///\r
74         HHOOK m_hHookKeyboardProc;                      ///\r
75         INPUT_DETOUR m_keyboardDetour;\r
76         INPUT_DETOUR m_mouseDetour;\r
77         Engine *m_engine;\r
78         DWORD m_hwndTaskTray;                           ///\r
79         HANDLE m_hMailslot;\r
80         bool m_isInitialized;\r
81 #ifndef NDEBUG\r
82         bool m_isLogging;\r
83         _TCHAR m_moduleName[GANA_MAX_PATH];\r
84 #endif // !NDEBUG\r
85 };\r
86 \r
87 static Globals g;\r
88 \r
89 \r
90 //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\r
91 // Prototypes\r
92 \r
93 \r
94 static void notifyThreadDetach();\r
95 static void notifyShow(NotifyShow::Show i_show, bool i_isMDI);\r
96 static void notifyLog(_TCHAR *i_msg);\r
97 static bool mapHookData();\r
98 static void unmapHookData();\r
99 static bool initialize();\r
100 \r
101 \r
102 //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\r
103 // Functions\r
104 \r
105 bool initialize()\r
106 {\r
107 #ifndef NDEBUG\r
108         _TCHAR path[GANA_MAX_PATH];\r
109         GetModuleFileName(NULL, path, GANA_MAX_PATH);\r
110         _tsplitpath_s(path, NULL, 0, NULL, 0, g.m_moduleName, GANA_MAX_PATH, NULL, 0);\r
111         if (_tcsnicmp(g.m_moduleName, _T("Dbgview"), sizeof(_T("Dbgview"))/sizeof(_TCHAR)) != 0 &&\r
112                         _tcsnicmp(g.m_moduleName, _T("windbg"), sizeof(_T("windbg"))/sizeof(_TCHAR)) != 0) {\r
113                 g.m_isLogging = true;\r
114         }\r
115 #endif // !NDEBUG\r
116 #ifdef USE_MAILSLOT\r
117         g.m_hMailslot =\r
118                 CreateFile(NOTIFY_MAILSLOT_NAME, GENERIC_WRITE,\r
119                                    FILE_SHARE_READ | FILE_SHARE_WRITE,\r
120                                    (SECURITY_ATTRIBUTES *)NULL, OPEN_EXISTING,\r
121                                    FILE_ATTRIBUTE_NORMAL, (HANDLE)NULL);\r
122         if (g.m_hMailslot == INVALID_HANDLE_VALUE) {\r
123                 HOOK_RPT2("MAYU: %S create mailslot failed(0x%08x)\r\n", g.m_moduleName, GetLastError());\r
124         } else {\r
125                 HOOK_RPT1("MAYU: %S create mailslot successed\r\n", g.m_moduleName);\r
126         }\r
127 #endif //USE_MAILSLOT\r
128         if (!mapHookData())\r
129                 return false;\r
130         _tsetlocale(LC_ALL, _T(""));\r
131         g.m_WM_MAYU_MESSAGE =\r
132                 RegisterWindowMessage(addSessionId(WM_MAYU_MESSAGE_NAME).c_str());\r
133         g.m_hwndTaskTray = g_hookData->m_hwndTaskTray;\r
134         g.m_isInitialized = true;\r
135         return true;\r
136 }\r
137 \r
138 /// EntryPoint\r
139 BOOL WINAPI DllMain(HINSTANCE i_hInstDLL, DWORD i_fdwReason,\r
140                                         LPVOID /* i_lpvReserved */)\r
141 {\r
142         switch (i_fdwReason) {\r
143         case DLL_PROCESS_ATTACH: {\r
144 #ifndef NDEBUG\r
145                 g.m_isLogging = false;\r
146 #endif // !NDEBUG\r
147                 g.m_isInitialized = false;\r
148                 g.m_hInstDLL = i_hInstDLL;\r
149                 break;\r
150         }\r
151         case DLL_THREAD_ATTACH:\r
152                 break;\r
153         case DLL_PROCESS_DETACH:\r
154                 notifyThreadDetach();\r
155                 unmapHookData();\r
156 #ifdef USE_MAILSLOT\r
157                 if (g.m_hMailslot != INVALID_HANDLE_VALUE) {\r
158                         CloseHandle(g.m_hMailslot);\r
159                         g.m_hMailslot = INVALID_HANDLE_VALUE;\r
160                 }\r
161 #endif //USE_MAILSLOT\r
162                 break;\r
163         case DLL_THREAD_DETACH:\r
164                 notifyThreadDetach();\r
165                 break;\r
166         default:\r
167                 break;\r
168         }\r
169         return TRUE;\r
170 }\r
171 \r
172 \r
173 /// map hook data\r
174 static bool mapHookData()\r
175 {\r
176         g.m_hHookData = CreateFileMapping(INVALID_HANDLE_VALUE, NULL, PAGE_READWRITE,\r
177                                                                           0, sizeof(HookData),\r
178                                                                           addSessionId(HOOK_DATA_NAME).c_str());\r
179         if (!g.m_hHookData) {\r
180                 unmapHookData();\r
181                 return false;\r
182         }\r
183 \r
184         g_hookData =\r
185                 (HookData *)MapViewOfFile(g.m_hHookData, FILE_MAP_READ | FILE_MAP_WRITE,\r
186                                                                   0, 0, sizeof(HookData));\r
187         if (!g_hookData) {\r
188                 unmapHookData();\r
189                 return false;\r
190         }\r
191 \r
192         g.m_hHookDataArch = CreateFileMapping(INVALID_HANDLE_VALUE, NULL, PAGE_READWRITE,\r
193                                                                                   0, sizeof(HookDataArch),\r
194                                                                                   addSessionId(HOOK_DATA_NAME_ARCH).c_str());\r
195         if (!g.m_hHookDataArch) {\r
196                 unmapHookData();\r
197                 return false;\r
198         }\r
199 \r
200         s_hookDataArch =\r
201                 (HookDataArch *)MapViewOfFile(g.m_hHookDataArch, FILE_MAP_READ | FILE_MAP_WRITE,\r
202                                                                           0, 0, sizeof(HookDataArch));\r
203         if (!s_hookDataArch) {\r
204                 unmapHookData();\r
205                 return false;\r
206         }\r
207 \r
208         return true;\r
209 }\r
210 \r
211 \r
212 /// unmap hook data\r
213 static void unmapHookData()\r
214 {\r
215         if (g_hookData)\r
216                 UnmapViewOfFile(g_hookData);\r
217         g_hookData = NULL;\r
218         if (g.m_hHookData)\r
219                 CloseHandle(g.m_hHookData);\r
220         g.m_hHookData = NULL;\r
221         if (s_hookDataArch)\r
222                 UnmapViewOfFile(s_hookDataArch);\r
223         s_hookDataArch = NULL;\r
224         if (g.m_hHookDataArch)\r
225                 CloseHandle(g.m_hHookDataArch);\r
226         g.m_hHookDataArch = NULL;\r
227 }\r
228 \r
229 \r
230 /// notify\r
231 DllExport bool notify(void *i_data, size_t i_dataSize)\r
232 {\r
233         COPYDATASTRUCT cd;\r
234 #ifdef MAYU64\r
235         DWORD_PTR result;\r
236 #else  // MAYU64\r
237         DWORD result;\r
238 #endif // MAYU64\r
239 \r
240 #ifdef USE_MAILSLOT\r
241         DWORD len;\r
242         if (g.m_hMailslot != INVALID_HANDLE_VALUE) {\r
243                 BOOL ret;\r
244                 ret = WriteFile(g.m_hMailslot, i_data, i_dataSize, &len, NULL);\r
245 #ifndef NDEBUG\r
246                 if (ret == 0) {\r
247                         HOOK_RPT2("MAYU: %S WriteFile to mailslot failed(0x%08x)\r\n", g.m_moduleName, GetLastError());\r
248                 } else {\r
249                         HOOK_RPT1("MAYU: %S WriteFile to mailslot successed\r\n", g.m_moduleName);\r
250                 }\r
251 #endif // !NDEBUG\r
252         }\r
253 #else // !USE_MAILSLOT\r
254         cd.dwData = reinterpret_cast<Notify *>(i_data)->m_type;\r
255         cd.cbData = i_dataSize;\r
256         cd.lpData = i_data;\r
257         if (g.m_hwndTaskTray == 0)\r
258                 return false;\r
259         if (!SendMessageTimeout(reinterpret_cast<HWND>(g.m_hwndTaskTray),\r
260                                                         WM_COPYDATA, NULL, reinterpret_cast<LPARAM>(&cd),\r
261                                                         SMTO_ABORTIFHUNG | SMTO_NORMAL, 5000, &result)) {\r
262                 _RPT0(_CRT_WARN, "MAYU: SendMessageTimeout() timeouted\r\n");\r
263                 return false;\r
264         }\r
265 #endif // !USE_MAILSLOT\r
266         return true;\r
267 }\r
268 \r
269 \r
270 /// get class name and title name\r
271 static void getClassNameTitleName(HWND i_hwnd, bool i_isInMenu,\r
272                                                                   tstringi *o_className,\r
273                                                                   tstring *o_titleName)\r
274 {\r
275         tstringi &className = *o_className;\r
276         tstring &titleName = *o_titleName;\r
277 \r
278         bool isTheFirstTime = true;\r
279 \r
280         if (i_isInMenu) {\r
281                 className = titleName = _T("MENU");\r
282                 isTheFirstTime = false;\r
283         }\r
284 \r
285         while (true) {\r
286                 _TCHAR buf[MAX(GANA_MAX_PATH, GANA_MAX_ATOM_LENGTH)];\r
287 \r
288                 // get class name\r
289                 if (i_hwnd)\r
290                         GetClassName(i_hwnd, buf, NUMBER_OF(buf));\r
291                 else\r
292                         GetModuleFileName(GetModuleHandle(NULL), buf, NUMBER_OF(buf));\r
293                 buf[NUMBER_OF(buf) - 1] = _T('\0');\r
294                 if (isTheFirstTime)\r
295                         className = buf;\r
296                 else\r
297                         className = tstringi(buf) + _T(":") + className;\r
298 \r
299                 // get title name\r
300                 if (i_hwnd) {\r
301                         GetWindowText(i_hwnd, buf, NUMBER_OF(buf));\r
302                         buf[NUMBER_OF(buf) - 1] = _T('\0');\r
303                         for (_TCHAR *b = buf; *b; ++ b)\r
304                                 if (_istlead(*b) && b[1])\r
305                                         b ++;\r
306                                 else if (_istcntrl(*b))\r
307                                         *b = _T('?');\r
308                 }\r
309                 if (isTheFirstTime)\r
310                         titleName = buf;\r
311                 else\r
312                         titleName = tstring(buf) + _T(":") + titleName;\r
313 \r
314                 // next loop or exit\r
315                 if (!i_hwnd)\r
316                         break;\r
317                 i_hwnd = GetParent(i_hwnd);\r
318                 isTheFirstTime = false;\r
319         }\r
320 }\r
321 \r
322 \r
323 /// update show\r
324 static void updateShow(HWND i_hwnd, NotifyShow::Show i_show)\r
325 {\r
326         bool isMDI = false;\r
327 \r
328         if (!i_hwnd)\r
329                 return;\r
330 \r
331 #ifdef MAYU64\r
332         LONG_PTR style = GetWindowLongPtr(i_hwnd, GWL_STYLE);\r
333 #else\r
334         LONG style = GetWindowLong(i_hwnd, GWL_STYLE);\r
335 #endif\r
336         if (!(style & WS_MAXIMIZEBOX) && !(style & WS_MAXIMIZEBOX))\r
337                 return; // ignore window that has neither maximize or minimize button\r
338 \r
339         if (style & WS_CHILD) {\r
340 #ifdef MAYU64\r
341                 LONG_PTR exStyle = GetWindowLongPtr(i_hwnd, GWL_EXSTYLE);\r
342 #else\r
343                 LONG exStyle = GetWindowLong(i_hwnd, GWL_EXSTYLE);\r
344 #endif\r
345                 if (exStyle & WS_EX_MDICHILD) {\r
346                         isMDI = true;\r
347                 } else\r
348                         return; // ignore non-MDI child window case\r
349         }\r
350 \r
351         notifyShow(i_show, isMDI);\r
352 }\r
353 \r
354 \r
355 /// notify WM_Targetted\r
356 static void notifyName(HWND i_hwnd, Notify::Type i_type = Notify::Type_name)\r
357 {\r
358         tstringi className;\r
359         tstring titleName;\r
360         getClassNameTitleName(i_hwnd, g.m_isInMenu, &className, &titleName);\r
361 \r
362         NotifySetFocus *nfc = new NotifySetFocus;\r
363         nfc->m_type = i_type;\r
364         nfc->m_threadId = GetCurrentThreadId();\r
365         nfc->m_hwnd = reinterpret_cast<DWORD>(i_hwnd);\r
366         tcslcpy(nfc->m_className, className.c_str(), NUMBER_OF(nfc->m_className));\r
367         tcslcpy(nfc->m_titleName, titleName.c_str(), NUMBER_OF(nfc->m_titleName));\r
368 \r
369         notify(nfc, sizeof(*nfc));\r
370         delete nfc;\r
371 }\r
372 \r
373 \r
374 /// notify WM_SETFOCUS\r
375 static void notifySetFocus(bool i_doesForce = false)\r
376 {\r
377         HWND hwnd = GetFocus();\r
378         if (i_doesForce || hwnd != g.m_hwndFocus) {\r
379                 g.m_hwndFocus = hwnd;\r
380                 notifyName(hwnd, Notify::Type_setFocus);\r
381         }\r
382 }\r
383 \r
384 \r
385 /// notify sync\r
386 static void notifySync()\r
387 {\r
388         Notify n;\r
389         n.m_type = Notify::Type_sync;\r
390         notify(&n, sizeof(n));\r
391 }\r
392 \r
393 \r
394 /// notify DLL_THREAD_DETACH\r
395 static void notifyThreadDetach()\r
396 {\r
397         NotifyThreadDetach ntd;\r
398         ntd.m_type = Notify::Type_threadDetach;\r
399         ntd.m_threadId = GetCurrentThreadId();\r
400         notify(&ntd, sizeof(ntd));\r
401 }\r
402 \r
403 \r
404 /// notify WM_COMMAND, WM_SYSCOMMAND\r
405 static void notifyCommand(\r
406         HWND i_hwnd, UINT i_message, WPARAM i_wParam, LPARAM i_lParam)\r
407 {\r
408 #ifndef _WIN64\r
409         if (g_hookData->m_doesNotifyCommand) {\r
410                 NotifyCommand ntc;\r
411                 ntc.m_type = Notify::Type_command;\r
412                 ntc.m_hwnd = i_hwnd;\r
413                 ntc.m_message = i_message;\r
414                 ntc.m_wParam = i_wParam;\r
415                 ntc.m_lParam = i_lParam;\r
416                 notify(&ntc, sizeof(ntc));\r
417         }\r
418 #endif\r
419 }\r
420 \r
421 \r
422 /// notify show of current window\r
423 static void notifyShow(NotifyShow::Show i_show, bool i_isMDI)\r
424 {\r
425         NotifyShow ns;\r
426         ns.m_type = Notify::Type_show;\r
427         ns.m_show = i_show;\r
428         ns.m_isMDI = i_isMDI;\r
429         notify(&ns, sizeof(ns));\r
430 }\r
431 \r
432 \r
433 /// notify log\r
434 static void notifyLog(_TCHAR *i_msg)\r
435 {\r
436         NotifyLog nl;\r
437         nl.m_type = Notify::Type_log;\r
438         tcslcpy(nl.m_msg, i_msg, NUMBER_OF(nl.m_msg));\r
439         notify(&nl, sizeof(nl));\r
440 }\r
441 \r
442 \r
443 /// &Recenter\r
444 static void funcRecenter(HWND i_hwnd)\r
445 {\r
446         _TCHAR buf[MAX(GANA_MAX_PATH, GANA_MAX_ATOM_LENGTH)];\r
447         GetClassName(i_hwnd, buf, NUMBER_OF(buf));\r
448         bool isEdit;\r
449         if (_tcsicmp(buf, _T("Edit")) == 0)\r
450                 isEdit = true;\r
451         else if (_tcsnicmp(buf, _T("RichEdit"), 8) == 0)\r
452                 isEdit = false;\r
453         else\r
454                 return; // this function only works for Edit control\r
455 \r
456 #ifdef MAYU64\r
457         LONG_PTR style = GetWindowLongPtr(i_hwnd, GWL_STYLE);\r
458 #else\r
459         LONG style = GetWindowLong(i_hwnd, GWL_STYLE);\r
460 #endif\r
461         if (!(style & ES_MULTILINE))\r
462                 return; // this function only works for multi line Edit control\r
463 \r
464         RECT rc;\r
465         GetClientRect(i_hwnd, &rc);\r
466         POINTL p = { (rc.right + rc.left) / 2, (rc.top + rc.bottom) / 2 };\r
467         int line;\r
468         if (isEdit) {\r
469                 line = SendMessage(i_hwnd, EM_CHARFROMPOS, 0, MAKELPARAM(p.x, p.y));\r
470                 line = HIWORD(line);\r
471         } else {\r
472                 int ci = SendMessage(i_hwnd, EM_CHARFROMPOS, 0, (LPARAM)&p);\r
473                 line = SendMessage(i_hwnd, EM_EXLINEFROMCHAR, 0, ci);\r
474         }\r
475         int caretLine = SendMessage(i_hwnd, EM_LINEFROMCHAR, -1, 0);\r
476         SendMessage(i_hwnd, EM_LINESCROLL, 0, caretLine - line);\r
477 }\r
478 \r
479 \r
480 // &SetImeStatus\r
481 static void funcSetImeStatus(HWND i_hwnd, int i_status)\r
482 {\r
483         HIMC hIMC;\r
484 \r
485         hIMC = ImmGetContext(i_hwnd);\r
486         if (hIMC == INVALID_HANDLE_VALUE)\r
487                 return;\r
488 \r
489         if (i_status < 0)\r
490                 i_status = !ImmGetOpenStatus(hIMC);\r
491 \r
492         ImmSetOpenStatus(hIMC, i_status);\r
493         ImmReleaseContext(i_hwnd, hIMC);\r
494 }\r
495 \r
496 \r
497 // &SetImeString\r
498 static void funcSetImeString(HWND i_hwnd, int i_size)\r
499 {\r
500         _TCHAR *buf = new _TCHAR(i_size);\r
501         DWORD len = 0;\r
502         _TCHAR ImeDesc[GANA_MAX_ATOM_LENGTH];\r
503         UINT ImeDescLen;\r
504         DWORD error;\r
505         DWORD denom = 1;\r
506         HANDLE hPipe\r
507         = CreateFile(addSessionId(HOOK_PIPE_NAME).c_str(), GENERIC_READ,\r
508                                  FILE_SHARE_READ, (SECURITY_ATTRIBUTES *)NULL,\r
509                                  OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, (HANDLE)NULL);\r
510         error = ReadFile(hPipe, buf, i_size, &len, NULL);\r
511         CloseHandle(hPipe);\r
512 \r
513         ImeDescLen = ImmGetDescription(GetKeyboardLayout(0),\r
514                                                                    ImeDesc, sizeof(ImeDesc));\r
515         if (_tcsncmp(ImeDesc, _T("SKKIME"), ImeDescLen) > 0)\r
516                 denom = sizeof(_TCHAR);\r
517 \r
518         HIMC hIMC = ImmGetContext(i_hwnd);\r
519         if (hIMC == INVALID_HANDLE_VALUE)\r
520                 return;\r
521 \r
522         int status = ImmGetOpenStatus(hIMC);\r
523         ImmSetCompositionString(hIMC, SCS_SETSTR, buf, len / denom, NULL, 0);\r
524         delete buf;\r
525         ImmNotifyIME(hIMC, NI_COMPOSITIONSTR, CPS_COMPLETE, 0);\r
526         if (!status)\r
527                 ImmSetOpenStatus(hIMC, status);\r
528         ImmReleaseContext(i_hwnd, hIMC);\r
529 }\r
530 \r
531 /// notify lock state\r
532 /*DllExport*/\r
533 void notifyLockState(int i_cause)\r
534 {\r
535         NotifyLockState n;\r
536         n.m_type = Notify::Type_lockState;\r
537         n.m_isNumLockToggled = !!(GetKeyState(VK_NUMLOCK) & 1);\r
538         n.m_isCapsLockToggled = !!(GetKeyState(VK_CAPITAL) & 1);\r
539         n.m_isScrollLockToggled = !!(GetKeyState(VK_SCROLL) & 1);\r
540         n.m_isKanaLockToggled = !!(GetKeyState(VK_KANA) & 1);\r
541         n.m_isImeLockToggled = g.m_isImeLock;\r
542         n.m_isImeCompToggled = g.m_isImeCompositioning;\r
543         n.m_debugParam = i_cause;\r
544         notify(&n, sizeof(n));\r
545 }\r
546 \r
547 DllExport void notifyLockState()\r
548 {\r
549         notifyLockState(9);\r
550 }\r
551 \r
552 \r
553 /// hook of GetMessage\r
554 LRESULT CALLBACK getMessageProc(int i_nCode, WPARAM i_wParam, LPARAM i_lParam)\r
555 {\r
556         if (!g.m_isInitialized)\r
557                 initialize();\r
558 \r
559         if (!g_hookData)\r
560                 return 0;\r
561 \r
562         MSG &msg = (*(MSG *)i_lParam);\r
563 \r
564         if (i_wParam != PM_REMOVE)\r
565                 goto finally;\r
566 \r
567         switch (msg.message) {\r
568         case WM_COMMAND:\r
569         case WM_SYSCOMMAND:\r
570                 notifyCommand(msg.hwnd, msg.message, msg.wParam, msg.lParam);\r
571                 break;\r
572         case WM_KEYDOWN:\r
573         case WM_KEYUP:\r
574         case WM_SYSKEYDOWN:\r
575         case WM_SYSKEYUP: {\r
576                 if (HIMC hIMC = ImmGetContext(msg.hwnd)) {\r
577                         bool prev = g.m_isImeLock;\r
578                         g.m_isImeLock = !!ImmGetOpenStatus(hIMC);\r
579                         ImmReleaseContext(msg.hwnd, hIMC);\r
580                         if (prev != g.m_isImeLock) {\r
581                                 notifyLockState(1);\r
582                         }\r
583                 }\r
584                 int nVirtKey = (int)msg.wParam;\r
585                 // int repeatCount = (msg.lParam & 0xffff);\r
586                 BYTE scanCode   = (BYTE)((msg.lParam >> 16) & 0xff);\r
587                 bool isExtended = !!(msg.lParam & (1 << 24));\r
588                 // bool isAltDown  = !!(msg.lParam & (1 << 29));\r
589                 // bool isKeyup    = !!(msg.lParam & (1 << 31));\r
590 \r
591                 if (nVirtKey == VK_CAPITAL ||\r
592                                 nVirtKey == VK_NUMLOCK ||\r
593                                 nVirtKey == VK_KANA ||\r
594                                 nVirtKey == VK_SCROLL)\r
595                         notifyLockState(2);\r
596                 else if (scanCode == g_hookData->m_syncKey &&\r
597                                  isExtended == g_hookData->m_syncKeyIsExtended)\r
598                         notifySync();\r
599                 break;\r
600         }\r
601         case WM_IME_STARTCOMPOSITION:\r
602                 g.m_isImeCompositioning = true;\r
603                 notifyLockState(3);\r
604                 break;\r
605         case WM_IME_ENDCOMPOSITION:\r
606                 g.m_isImeCompositioning = false;\r
607                 notifyLockState(4);\r
608                 break;\r
609         default:\r
610                 if (i_wParam == PM_REMOVE && msg.message == g.m_WM_MAYU_MESSAGE) {\r
611                         switch (msg.wParam) {\r
612                         case MayuMessage_notifyName:\r
613                                 notifyName(msg.hwnd);\r
614                                 break;\r
615                         case MayuMessage_funcRecenter:\r
616                                 funcRecenter(msg.hwnd);\r
617                                 break;\r
618                         case MayuMessage_funcSetImeStatus:\r
619                                 funcSetImeStatus(msg.hwnd, msg.lParam);\r
620                                 break;\r
621                         case MayuMessage_funcSetImeString:\r
622                                 funcSetImeString(msg.hwnd, msg.lParam);\r
623                                 break;\r
624                         }\r
625                 }\r
626                 break;\r
627         }\r
628 finally:\r
629         return CallNextHookEx(s_hookDataArch->m_hHookGetMessage,\r
630                                                   i_nCode, i_wParam, i_lParam);\r
631 }\r
632 \r
633 \r
634 /// hook of SendMessage\r
635 LRESULT CALLBACK callWndProc(int i_nCode, WPARAM i_wParam, LPARAM i_lParam)\r
636 {\r
637         if (!g.m_isInitialized)\r
638                 initialize();\r
639 \r
640         if (!g_hookData)\r
641                 return 0;\r
642 \r
643         CWPSTRUCT &cwps = *(CWPSTRUCT *)i_lParam;\r
644 \r
645         if (0 <= i_nCode) {\r
646                 switch (cwps.message) {\r
647                 case WM_ACTIVATEAPP:\r
648                 case WM_NCACTIVATE:\r
649                         if (i_wParam)\r
650                                 notifySetFocus();\r
651                         break;\r
652                 case WM_SYSCOMMAND:\r
653                         switch (cwps.wParam) {\r
654                         case SC_MAXIMIZE:\r
655                         case SC_MAXIMIZE2:\r
656                                 updateShow(cwps.hwnd, NotifyShow::Show_Maximized);\r
657                                 break;\r
658                         case SC_MINIMIZE:\r
659                         case SC_MINIMIZE2:\r
660                                 updateShow(cwps.hwnd, NotifyShow::Show_Minimized);\r
661                                 break;\r
662                         case SC_RESTORE:\r
663                         case SC_RESTORE2:\r
664                                 updateShow(cwps.hwnd, NotifyShow::Show_Normal);\r
665                                 break;\r
666                         default:\r
667                                 break;\r
668                         }\r
669                         /* through below */\r
670                 case WM_COMMAND:\r
671                         notifyCommand(cwps.hwnd, cwps.message, cwps.wParam, cwps.lParam);\r
672                         break;\r
673                 case WM_SIZE:\r
674                         switch (cwps.wParam) {\r
675                         case SIZE_MAXIMIZED:\r
676                                 updateShow(cwps.hwnd, NotifyShow::Show_Maximized);\r
677                                 break;\r
678                         case SIZE_MINIMIZED:\r
679                                 updateShow(cwps.hwnd, NotifyShow::Show_Minimized);\r
680                                 break;\r
681                         case SIZE_RESTORED:\r
682                                 updateShow(cwps.hwnd, NotifyShow::Show_Normal);\r
683                                 break;\r
684                         default:\r
685                                 break;\r
686                         }\r
687                         break;\r
688                 case WM_MOUSEACTIVATE:\r
689                         notifySetFocus();\r
690                         break;\r
691                 case WM_ACTIVATE:\r
692                         if (LOWORD(cwps.wParam) != WA_INACTIVE) {\r
693                                 notifySetFocus();\r
694                                 if (HIWORD(cwps.wParam)) { // check minimized flag\r
695                                         // minimized flag on\r
696                                         notifyShow(NotifyShow::Show_Minimized, false);\r
697                                         //notifyShow(NotifyShow::Show_Normal, true);\r
698                                 }\r
699                         }\r
700                         break;\r
701                 case WM_ENTERMENULOOP:\r
702                         g.m_isInMenu = true;\r
703                         notifySetFocus(true);\r
704                         break;\r
705                 case WM_EXITMENULOOP:\r
706                         g.m_isInMenu = false;\r
707                         notifySetFocus(true);\r
708                         break;\r
709                 case WM_SETFOCUS:\r
710                         g.m_isInMenu = false;\r
711                         // for kana\r
712                         if (g_hookData->m_correctKanaLockHandling) {\r
713                                 if (HIMC hIMC = ImmGetContext(cwps.hwnd)) {\r
714                                         bool status = !!ImmGetOpenStatus(hIMC);\r
715                                         // this code set the VK_KANA state correctly.\r
716                                         ImmSetOpenStatus(hIMC, !status);\r
717                                         ImmSetOpenStatus(hIMC, status);\r
718                                         ImmReleaseContext(cwps.hwnd, hIMC);\r
719                                 }\r
720                         }\r
721                         notifySetFocus();\r
722                         notifyLockState(5);\r
723                         break;\r
724                 case WM_IME_STARTCOMPOSITION:\r
725                         g.m_isImeCompositioning = true;\r
726                         notifyLockState(6);\r
727                         break;\r
728                 case WM_IME_ENDCOMPOSITION:\r
729                         g.m_isImeCompositioning = false;\r
730                         notifyLockState(7);\r
731                         break;\r
732                 case WM_IME_NOTIFY:\r
733                         if (cwps.wParam == IMN_SETOPENSTATUS)\r
734                                 if (HIMC hIMC = ImmGetContext(cwps.hwnd)) {\r
735                                         g.m_isImeLock = !!ImmGetOpenStatus(hIMC);\r
736                                         ImmReleaseContext(cwps.hwnd, hIMC);\r
737                                         notifyLockState(8);\r
738                                 }\r
739                         break;\r
740                 }\r
741         }\r
742         return CallNextHookEx(s_hookDataArch->m_hHookCallWndProc, i_nCode,\r
743                                                   i_wParam, i_lParam);\r
744 }\r
745 \r
746 \r
747 static LRESULT CALLBACK lowLevelMouseProc(int nCode, WPARAM wParam, LPARAM lParam)\r
748 {\r
749         if (!g.m_isInitialized)\r
750                 initialize();\r
751 \r
752         if (!g_hookData || nCode < 0)\r
753                 goto through;\r
754 \r
755         if (g.m_mouseDetour && g.m_engine) {\r
756                 unsigned int result;\r
757                 result = g.m_mouseDetour(g.m_engine, wParam, lParam);\r
758                 if (result) {\r
759                         return 1;\r
760                 }\r
761         }\r
762 \r
763 through:\r
764         return CallNextHookEx(g.m_hHookMouseProc,\r
765                                                   nCode, wParam, lParam);\r
766 }\r
767 \r
768 \r
769 static LRESULT CALLBACK lowLevelKeyboardProc(int nCode, WPARAM wParam, LPARAM lParam)\r
770 {\r
771         KBDLLHOOKSTRUCT *pKbll = (KBDLLHOOKSTRUCT*)lParam;\r
772 \r
773         if (!g.m_isInitialized)\r
774                 initialize();\r
775 \r
776         if (!g_hookData || nCode < 0)\r
777                 goto through;\r
778 \r
779         if (g.m_keyboardDetour && g.m_engine) {\r
780                 unsigned int result;\r
781                 result = g.m_keyboardDetour(g.m_engine, wParam, lParam);\r
782                 if (result) {\r
783                         return 1;\r
784                 }\r
785         }\r
786 through:\r
787         return CallNextHookEx(g.m_hHookKeyboardProc,\r
788                                                   nCode, wParam, lParam);\r
789 }\r
790 \r
791 \r
792 /// install message hook\r
793 DllExport int installMessageHook()\r
794 {\r
795         if (!g.m_isInitialized)\r
796                 initialize();\r
797 \r
798         g.m_hwndTaskTray = g_hookData->m_hwndTaskTray;\r
799         s_hookDataArch->m_hHookGetMessage =\r
800                 SetWindowsHookEx(WH_GETMESSAGE, (HOOKPROC)getMessageProc,\r
801                                                  g.m_hInstDLL, 0);\r
802         s_hookDataArch->m_hHookCallWndProc =\r
803                 SetWindowsHookEx(WH_CALLWNDPROC, (HOOKPROC)callWndProc, g.m_hInstDLL, 0);\r
804         return 0;\r
805 }\r
806 \r
807 \r
808 /// uninstall message hook\r
809 DllExport int uninstallMessageHook()\r
810 {\r
811         if (s_hookDataArch->m_hHookGetMessage)\r
812                 UnhookWindowsHookEx(s_hookDataArch->m_hHookGetMessage);\r
813         s_hookDataArch->m_hHookGetMessage = NULL;\r
814         if (s_hookDataArch->m_hHookCallWndProc)\r
815                 UnhookWindowsHookEx(s_hookDataArch->m_hHookCallWndProc);\r
816         s_hookDataArch->m_hHookCallWndProc = NULL;\r
817         g.m_hwndTaskTray = 0;\r
818         return 0;\r
819 }\r
820 \r
821 \r
822 /// install keyboard hook\r
823 DllExport int installKeyboardHook(INPUT_DETOUR i_keyboardDetour, Engine *i_engine, bool i_install)\r
824 {\r
825         if (i_install) {\r
826                 if (!g.m_isInitialized)\r
827                         initialize();\r
828 \r
829                 g.m_keyboardDetour = i_keyboardDetour;\r
830                 g.m_engine = i_engine;\r
831                 g.m_hHookKeyboardProc =\r
832                         SetWindowsHookEx(WH_KEYBOARD_LL, (HOOKPROC)lowLevelKeyboardProc,\r
833                                                          g.m_hInstDLL, 0);\r
834         } else {\r
835                 if (g.m_hHookKeyboardProc)\r
836                         UnhookWindowsHookEx(g.m_hHookKeyboardProc);\r
837                 g.m_hHookKeyboardProc = NULL;\r
838         }\r
839         return 0;\r
840 }\r
841 \r
842 \r
843 /// install mouse hook\r
844 DllExport int installMouseHook(INPUT_DETOUR i_mouseDetour, Engine *i_engine, bool i_install)\r
845 {\r
846         if (i_install) {\r
847                 if (!g.m_isInitialized)\r
848                         initialize();\r
849 \r
850                 g.m_mouseDetour = i_mouseDetour;\r
851                 g.m_engine = i_engine;\r
852                 g_hookData->m_mouseHookType = MouseHookType_None;\r
853                 g.m_hHookMouseProc =\r
854                         SetWindowsHookEx(WH_MOUSE_LL, (HOOKPROC)lowLevelMouseProc,\r
855                                                          g.m_hInstDLL, 0);\r
856         } else {\r
857                 if (g.m_hHookMouseProc)\r
858                         UnhookWindowsHookEx(g.m_hHookMouseProc);\r
859                 g.m_hHookMouseProc = NULL;\r
860         }\r
861         return 0;\r
862 }\r