OSDN Git Service

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