OSDN Git Service

delete Win9x/NT support code
[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   _TCHAR *buf = new _TCHAR(i_size);
524   DWORD len = 0;
525   _TCHAR ImeDesc[GANA_MAX_ATOM_LENGTH];
526   UINT ImeDescLen;
527   DWORD error;
528   DWORD denom = 1;
529   HANDLE hPipe
530     = CreateFile(addSessionId(HOOK_PIPE_NAME).c_str(), GENERIC_READ,
531                  FILE_SHARE_READ, (SECURITY_ATTRIBUTES *)NULL,
532                  OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, (HANDLE)NULL);
533   error = ReadFile(hPipe, buf, i_size, &len, NULL);
534   CloseHandle(hPipe);
535
536   ImeDescLen = ImmGetDescription(GetKeyboardLayout(0),
537                                  ImeDesc, sizeof(ImeDesc));
538   if (_tcsncmp(ImeDesc, _T("SKKIME"), ImeDescLen) > 0)
539     denom = sizeof(_TCHAR);
540
541   HIMC hIMC = ImmGetContext(i_hwnd);
542   if (hIMC == INVALID_HANDLE_VALUE)
543     return;
544
545   int status = ImmGetOpenStatus(hIMC);
546   ImmSetCompositionString(hIMC, SCS_SETSTR, buf, len / denom, NULL, 0);
547   delete buf;
548   ImmNotifyIME(hIMC, NI_COMPOSITIONSTR, CPS_COMPLETE, 0);
549   if (!status)
550     ImmSetOpenStatus(hIMC, status);
551   ImmReleaseContext(i_hwnd, hIMC);
552 }
553
554 /// notify lock state
555 /*DllExport*/ void notifyLockState(int i_cause)
556 {
557   NotifyLockState n;
558   n.m_type = Notify::Type_lockState;
559   n.m_isNumLockToggled = !!(GetKeyState(VK_NUMLOCK) & 1);
560   n.m_isCapsLockToggled = !!(GetKeyState(VK_CAPITAL) & 1);
561   n.m_isScrollLockToggled = !!(GetKeyState(VK_SCROLL) & 1);
562   n.m_isKanaLockToggled = !!(GetKeyState(VK_KANA) & 1);
563   n.m_isImeLockToggled = g.m_isImeLock;
564   n.m_isImeCompToggled = g.m_isImeCompositioning;
565   n.m_debugParam = i_cause;
566   notify(&n, sizeof(n));
567 }
568
569 DllExport void notifyLockState()
570 {
571   notifyLockState(9);
572 }
573
574
575 /// hook of GetMessage
576 LRESULT CALLBACK getMessageProc(int i_nCode, WPARAM i_wParam, LPARAM i_lParam)
577 {
578   if (!g.m_isInitialized)
579     initialize();
580
581   if (!g_hookData)
582     return 0;
583   
584   MSG &msg = (*(MSG *)i_lParam);
585
586   if (i_wParam != PM_REMOVE)
587     goto finally;
588
589   switch (msg.message)
590   {
591     case WM_COMMAND:
592     case WM_SYSCOMMAND:
593       notifyCommand(msg.hwnd, msg.message, msg.wParam, msg.lParam);
594       break;
595     case WM_KEYDOWN:
596     case WM_KEYUP:
597     case WM_SYSKEYDOWN:
598     case WM_SYSKEYUP:
599     {
600       if (HIMC hIMC = ImmGetContext(msg.hwnd))
601       {
602         bool prev = g.m_isImeLock;
603         g.m_isImeLock = !!ImmGetOpenStatus(hIMC);
604         ImmReleaseContext(msg.hwnd, hIMC);
605         if (prev != g.m_isImeLock) {
606           notifyLockState(1);
607         }
608       }
609       int nVirtKey = (int)msg.wParam;
610       // int repeatCount = (msg.lParam & 0xffff);
611       BYTE scanCode   = (BYTE)((msg.lParam >> 16) & 0xff);
612       bool isExtended = !!(msg.lParam & (1 << 24));
613       // bool isAltDown  = !!(msg.lParam & (1 << 29));
614       // bool isKeyup    = !!(msg.lParam & (1 << 31));
615       
616       if (nVirtKey == VK_CAPITAL ||
617           nVirtKey == VK_NUMLOCK ||
618           nVirtKey == VK_KANA ||
619           nVirtKey == VK_SCROLL)
620         notifyLockState(2);
621       else if (scanCode == g_hookData->m_syncKey &&
622                isExtended == g_hookData->m_syncKeyIsExtended)
623         notifySync();
624       break;
625     }
626     case WM_IME_STARTCOMPOSITION:
627       g.m_isImeCompositioning = true;
628       notifyLockState(3);
629       break;
630     case WM_IME_ENDCOMPOSITION:
631       g.m_isImeCompositioning = false;
632       notifyLockState(4);
633       break;
634     default:
635       if (i_wParam == PM_REMOVE && msg.message == g.m_WM_MAYU_MESSAGE)
636       {
637         switch (msg.wParam)
638         {
639           case MayuMessage_notifyName:
640             notifyName(msg.hwnd);
641             break;
642           case MayuMessage_funcRecenter:
643             funcRecenter(msg.hwnd);
644             break;
645           case MayuMessage_funcSetImeStatus:
646             funcSetImeStatus(msg.hwnd, msg.lParam);
647             break;
648           case MayuMessage_funcSetImeString:
649             funcSetImeString(msg.hwnd, msg.lParam);
650             break;
651         }
652       }
653       break;
654   }
655  finally:
656   return CallNextHookEx(s_hookDataArch->m_hHookGetMessage,
657                         i_nCode, i_wParam, i_lParam);
658 }
659
660
661 /// hook of SendMessage
662 LRESULT CALLBACK callWndProc(int i_nCode, WPARAM i_wParam, LPARAM i_lParam)
663 {
664   if (!g.m_isInitialized)
665     initialize();
666
667   if (!g_hookData)
668     return 0;
669   
670   CWPSTRUCT &cwps = *(CWPSTRUCT *)i_lParam;
671   
672   if (0 <= i_nCode)
673   {
674     switch (cwps.message)
675     {
676       case WM_ACTIVATEAPP:
677       case WM_NCACTIVATE:
678         if (i_wParam)
679           notifySetFocus();
680         break;
681       case WM_SYSCOMMAND:
682         switch (cwps.wParam)
683         {
684           case SC_MAXIMIZE:
685           case SC_MAXIMIZE2:
686             updateShow(cwps.hwnd, NotifyShow::Show_Maximized);
687             break;
688           case SC_MINIMIZE:
689           case SC_MINIMIZE2:
690             updateShow(cwps.hwnd, NotifyShow::Show_Minimized);
691             break;
692           case SC_RESTORE:
693           case SC_RESTORE2:
694             updateShow(cwps.hwnd, NotifyShow::Show_Normal);
695             break;
696           default:
697             break;
698         }
699         /* through below */
700       case WM_COMMAND:
701         notifyCommand(cwps.hwnd, cwps.message, cwps.wParam, cwps.lParam);
702         break;
703       case WM_SIZE:
704         switch (cwps.wParam)
705         {
706           case SIZE_MAXIMIZED:
707             updateShow(cwps.hwnd, NotifyShow::Show_Maximized);
708             break;
709           case SIZE_MINIMIZED:
710             updateShow(cwps.hwnd, NotifyShow::Show_Minimized);
711             break;
712           case SIZE_RESTORED:
713             updateShow(cwps.hwnd, NotifyShow::Show_Normal);
714             break;
715           default:
716             break;
717         }
718         break;
719       case WM_MOUSEACTIVATE:
720         notifySetFocus();
721         break;
722       case WM_ACTIVATE:
723         if (LOWORD(cwps.wParam) != WA_INACTIVE)
724         {
725           notifySetFocus();
726           if (HIWORD(cwps.wParam)) // check minimized flag
727           {
728             // minimized flag on
729             notifyShow(NotifyShow::Show_Minimized, false);
730             //notifyShow(NotifyShow::Show_Normal, true);
731           }
732         }
733         break;
734       case WM_ENTERMENULOOP:
735         g.m_isInMenu = true;
736         notifySetFocus(true);
737         break;
738       case WM_EXITMENULOOP:
739         g.m_isInMenu = false;
740         notifySetFocus(true);
741         break;
742       case WM_SETFOCUS:
743         g.m_isInMenu = false;
744         // for kana
745         if (g_hookData->m_correctKanaLockHandling) {
746           if (HIMC hIMC = ImmGetContext(cwps.hwnd)) {
747             bool status = !!ImmGetOpenStatus(hIMC);
748             // this code set the VK_KANA state correctly.
749             ImmSetOpenStatus(hIMC, !status);
750             ImmSetOpenStatus(hIMC, status);
751             ImmReleaseContext(cwps.hwnd, hIMC);
752           }
753         }
754         notifySetFocus();
755         notifyLockState(5);
756         break;
757       case WM_IME_STARTCOMPOSITION:
758         g.m_isImeCompositioning = true;
759         notifyLockState(6);
760         break;
761       case WM_IME_ENDCOMPOSITION:
762         g.m_isImeCompositioning = false;
763         notifyLockState(7);
764         break;
765       case WM_IME_NOTIFY:
766         if (cwps.wParam == IMN_SETOPENSTATUS)
767           if (HIMC hIMC = ImmGetContext(cwps.hwnd))
768           {
769             g.m_isImeLock = !!ImmGetOpenStatus(hIMC);
770             ImmReleaseContext(cwps.hwnd, hIMC);
771             notifyLockState(8);
772           }
773         break;
774     }
775   }
776   return CallNextHookEx(s_hookDataArch->m_hHookCallWndProc, i_nCode,
777                         i_wParam, i_lParam);
778 }
779
780
781 static LRESULT CALLBACK lowLevelMouseProc(int nCode, WPARAM wParam, LPARAM lParam)
782 {
783   MSLLHOOKSTRUCT *pMsll = (MSLLHOOKSTRUCT*)lParam;
784   LONG dx = pMsll->pt.x - g_hookData->m_mousePos.x;
785   LONG dy = pMsll->pt.y - g_hookData->m_mousePos.y;
786   HWND target = reinterpret_cast<HWND>(g_hookData->m_hwndMouseHookTarget);
787
788   if (!g.m_isInitialized)
789     initialize();
790
791   if (!g_hookData || nCode < 0 || wParam != WM_MOUSEMOVE)
792     goto through;
793
794   switch (g_hookData->m_mouseHookType)
795   {
796     case MouseHookType_Wheel:
797       // For this type, g_hookData->m_mouseHookParam means
798       // translate rate mouse move to wheel.
799       mouse_event(MOUSEEVENTF_WHEEL, 0, 0,
800                   g_hookData->m_mouseHookParam * dy, 0);
801       return 1;
802       break;
803     case MouseHookType_WindowMove:
804     {
805       RECT curRect;
806
807       if (!GetWindowRect(target, &curRect))
808         goto through;
809
810       // g_hookData->m_mouseHookParam < 0 means
811       // target window to move is MDI.
812       if (g_hookData->m_mouseHookParam < 0)
813       {
814         HWND parent = GetParent(target);
815         POINT p = {curRect.left, curRect.top};
816
817         if (parent == NULL || !ScreenToClient(parent, &p))
818           goto through;
819
820         curRect.left = p.x;
821         curRect.top = p.y;
822       }
823         
824       SetWindowPos(target, NULL,
825                    curRect.left + dx,
826                    curRect.top + dy,
827                    0, 0,
828                    SWP_ASYNCWINDOWPOS | SWP_NOACTIVATE |
829                    SWP_NOOWNERZORDER | SWP_NOSIZE | SWP_NOZORDER);
830       g_hookData->m_mousePos = pMsll->pt;
831       goto through;
832       break;
833     }
834     case MouseHookType_None:
835     default:
836       goto through;
837       break;
838   }
839     
840  through:
841   return CallNextHookEx(g.m_hHookMouseProc,
842                         nCode, wParam, lParam);
843 }
844
845
846 #ifdef NO_DRIVER
847 static LRESULT CALLBACK lowLevelKeyboardProc(int nCode, WPARAM wParam, LPARAM lParam)
848 {
849   KBDLLHOOKSTRUCT *pKbll = (KBDLLHOOKSTRUCT*)lParam;
850
851   if (!g.m_isInitialized)
852     initialize();
853
854   if (!g_hookData || nCode < 0)
855     goto through;
856
857   if (g.m_keyboardDetour && g.m_engine)
858   {
859     unsigned int result;
860     result = g.m_keyboardDetour(g.m_engine, pKbll);
861     if (result)
862     {
863       return 1;
864     }
865   }
866  through:
867   return CallNextHookEx(g.m_hHookKeyboardProc,
868                         nCode, wParam, lParam);
869 }
870 #endif // NO_DRIVER
871
872
873 /// install hooks
874 DllExport int installHooks(KEYBOARD_DETOUR i_keyboardDetour, Engine *i_engine)
875 {
876   if (!g.m_isInitialized)
877     initialize();
878
879   g.m_hwndTaskTray = g_hookData->m_hwndTaskTray;
880   s_hookDataArch->m_hHookGetMessage =
881     SetWindowsHookEx(WH_GETMESSAGE, (HOOKPROC)getMessageProc,
882                      g.m_hInstDLL, 0);
883   s_hookDataArch->m_hHookCallWndProc =
884     SetWindowsHookEx(WH_CALLWNDPROC, (HOOKPROC)callWndProc, g.m_hInstDLL, 0);
885   g_hookData->m_mouseHookType = MouseHookType_None;
886   if (i_engine != NULL)
887   {
888 #ifdef NO_DRIVER
889     g.m_keyboardDetour = i_keyboardDetour;
890     g.m_engine = i_engine;
891     g.m_hHookKeyboardProc =
892       SetWindowsHookEx(WH_KEYBOARD_LL, (HOOKPROC)lowLevelKeyboardProc,
893                        g.m_hInstDLL, 0);
894 #endif // NO_DRIVER
895     g.m_hHookMouseProc =
896       SetWindowsHookEx(WH_MOUSE_LL, (HOOKPROC)lowLevelMouseProc,
897                        g.m_hInstDLL, 0);
898   }
899   return 0;
900 }
901
902
903 /// uninstall hooks
904 DllExport int uninstallHooks()
905 {
906   if (s_hookDataArch->m_hHookGetMessage)
907     UnhookWindowsHookEx(s_hookDataArch->m_hHookGetMessage);
908   s_hookDataArch->m_hHookGetMessage = NULL;
909   if (s_hookDataArch->m_hHookCallWndProc)
910     UnhookWindowsHookEx(s_hookDataArch->m_hHookCallWndProc);
911   s_hookDataArch->m_hHookCallWndProc = NULL;
912   if (g.m_hHookMouseProc)
913     UnhookWindowsHookEx(g.m_hHookMouseProc);
914   g.m_hHookMouseProc = NULL;
915 #ifdef NO_DRIVER
916   if (g.m_hHookKeyboardProc)
917     UnhookWindowsHookEx(g.m_hHookKeyboardProc);
918   g.m_hHookKeyboardProc = NULL;
919 #endif // NO_DRIVER
920   g.m_hwndTaskTray = 0;
921   return 0;
922 }