OSDN Git Service

d1ef4fd8fbee923619fe82458d7833eb10286341
[xkeymacs/xkeymacs.git] / xkeymacsdll / xkeymacsdll.cpp
1 // xkeymacsdll.cpp : Defines the initialization routines for the DLL.\r
2 //\r
3 \r
4 #include "xkeymacsdll.h"\r
5 #include "Utils.h"\r
6 #include "Commands.h"\r
7 #include "../xkeymacs/resource.h"\r
8 #include <math.h>\r
9 #include <Imm.h>\r
10 #include <vector>\r
11 \r
12 #ifdef _DEBUG\r
13 #define new DEBUG_NEW\r
14 #undef THIS_FILE\r
15 static char THIS_FILE[] = __FILE__;\r
16 #endif\r
17 \r
18 struct Modifier {\r
19         LPCTSTR name;\r
20         int id;\r
21 };\r
22 \r
23 static const Modifier Modifiers[] = {\r
24 //      { _T("A-"), ALT },\r
25         { _T("C-"), CONTROL},\r
26 //      { _T("H-"), HYPER },\r
27         { _T("M-"), META },\r
28         { _T("S-"), SHIFT },\r
29 //      { _T("s-"), SUPER },\r
30         { _T("Ctrl+"), WIN_CTRL },\r
31         { _T("Alt+"), WIN_ALT },\r
32         { _T("Win+"), WIN_WIN },\r
33 };\r
34 static const int MAX_MODIFIER = _countof(Modifiers);\r
35 \r
36 static const KeyName KeyNames[] = {\r
37 //      { VK_LBUTTON,           _T("mouse-1") },                                // does not work well\r
38 //      { VK_RBUTTON,           _T("mouse-3") },                                // does not work well\r
39         { VK_CANCEL,            _T("break") },\r
40 //      { VK_MBUTTON,           _T("mouse-2") },                                // does not work well\r
41         { VK_BACK,                      _T("backspace") },\r
42         { VK_TAB,                       _T("tab") },\r
43         { VK_RETURN,            _T("return") },\r
44         { VK_CAPITAL,           _T("capslock") },\r
45         { VK_KANA,                      _T("kana") },\r
46         { VK_KANJI,                     _T("kanji") },\r
47         { VK_ESCAPE,            _T("escape") },\r
48         { VK_CONVERT,           _T("convert") },\r
49         { VK_NONCONVERT,        _T("nonconvert") },\r
50 //      { VK_SPACE,                     _T("SPC") },                                    // [? ]\r
51         { VK_PRIOR,                     _T("prior") },\r
52         { VK_NEXT,                      _T("next") },\r
53         { VK_END,                       _T("end") },\r
54         { VK_HOME,                      _T("home") },\r
55         { VK_LEFT,                      _T("left") },\r
56         { VK_UP,                        _T("up") },\r
57         { VK_RIGHT,                     _T("right") },\r
58         { VK_DOWN,                      _T("down") },\r
59         { VK_SELECT,            _T("select") },\r
60         { VK_PRINT,                     _T("print") },\r
61         { VK_EXECUTE,           _T("execute") },\r
62         { VK_SNAPSHOT,          _T("printscreen") },                    // work as print\r
63         { VK_INSERT,            _T("insert") },\r
64         { VK_DELETE,            _T("delete") },\r
65         { VK_LWIN,                      _T("lwindow") },\r
66         { VK_RWIN,                      _T("rwindow") },\r
67         { VK_APPS,                      _T("apps") },\r
68         { VK_SLEEP,                     _T("sleep") },\r
69         { VK_NUMPAD0,           _T("kp-0") },\r
70         { VK_NUMPAD1,           _T("kp-1") },\r
71         { VK_NUMPAD2,           _T("kp-2") },\r
72         { VK_NUMPAD3,           _T("kp-3") },\r
73         { VK_NUMPAD4,           _T("kp-4") },\r
74         { VK_NUMPAD5,           _T("kp-5") },\r
75         { VK_NUMPAD6,           _T("kp-6") },\r
76         { VK_NUMPAD7,           _T("kp-7") },\r
77         { VK_NUMPAD8,           _T("kp-8") },\r
78         { VK_NUMPAD9,           _T("kp-9") },\r
79         { VK_MULTIPLY,          _T("kp-multiply") },\r
80         { VK_ADD,                       _T("kp-add") },\r
81         { VK_SUBTRACT,          _T("kp-subtract") },\r
82         { VK_DECIMAL,           _T("kp-decimal") },\r
83         { VK_DIVIDE,            _T("kp-divide") },\r
84 //      { VK_F1,                        _T("f1") },                                             // FIXME\r
85 //      { VK_F2,                        _T("f2") },                                             // Move at the end of definition of function keys to keep away confliction f1/f2 and f1?/f2? by _tcsncmp() i.e. strncmp()\r
86         { VK_F3,                        _T("f3") },\r
87         { VK_F4,                        _T("f4") },\r
88         { VK_F5,                        _T("f5") },\r
89         { VK_F6,                        _T("f6") },\r
90         { VK_F7,                        _T("f7") },\r
91         { VK_F8,                        _T("f8") },\r
92         { VK_F9,                        _T("f9") },\r
93         { VK_F10,                       _T("f10") },\r
94         { VK_F11,                       _T("f11") },\r
95         { VK_F12,                       _T("f12") },\r
96         { VK_F13,                       _T("f13") },\r
97         { VK_F14,                       _T("f14") },\r
98         { VK_F15,                       _T("f15") },\r
99         { VK_F16,                       _T("f16") },\r
100         { VK_F17,                       _T("f17") },\r
101         { VK_F18,                       _T("f18") },\r
102         { VK_F19,                       _T("f19") },\r
103         { VK_F20,                       _T("f20") },\r
104         { VK_F21,                       _T("f21") },\r
105         { VK_F22,                       _T("f22") },\r
106         { VK_F23,                       _T("f23") },\r
107         { VK_F24,                       _T("f24") },\r
108         { VK_F1,                        _T("f1") },\r
109         { VK_F2,                        _T("f2") },\r
110         { VK_NUMLOCK,           _T("kp-numlock") },\r
111         { VK_SCROLL,            _T("scroll") },\r
112         { 0xa6,                         _T("browser-back") },                   // VK_BROWSER_BACK\r
113         { 0xa7,                         _T("browser-forward") },                // VK_BROWSER_FORWARD\r
114         { 0xa8,                         _T("browser-refresh") },                // VK_BROWSER_REFRESH\r
115         { 0xa9,                         _T("browser-stop") },                   // VK_BROWSER_STOP\r
116         { 0xaa,                         _T("browser-search") },                 // VK_BROWSER_SEARCH\r
117         { 0xab,                         _T("browser-favorites") },              // VK_BROWSER_FAVORITES\r
118         { 0xac,                         _T("browser-home") },                   // VK_BROWSER_HOME\r
119         { 0xad,                         _T("volume-mute") },                    // VK_VOLUME_MUTE\r
120         { 0xae,                         _T("volume-down") },                    // VK_VOLUME_DOWN\r
121         { 0xaf,                         _T("volume-up") },                              // VK_VOLUME_UP\r
122         { 0xb0,                         _T("media-next-track") },               // VK_MEDIA_NEXT_TRACK\r
123         { 0xb1,                         _T("media-prev-track") },               // VK_MEDIA_PREV_TRACK\r
124         { 0xb2,                         _T("media-stop") },                             // VK_MEDIA_STOP\r
125         { 0xb3,                         _T("media-play-pause") },               // VK_MEDIA_PLAY_PAUSE\r
126         { 0xb4,                         _T("launch-mail") },                    // VK_LAUNCH_MAIL\r
127         { 0xb5,                         _T("launch-media-select") },    // VK_LAUNCH_MEDIA_SELECT\r
128         { 0xb6,                         _T("launch-1") },                               // VK_LAUNCH_APP1\r
129         { 0xb7,                         _T("launch-2") },                               // VK_LAUNCH_APP2\r
130 };\r
131 static const int MAX_KEYNAME = _countof(KeyNames);\r
132 \r
133 static AFX_EXTENSION_MODULE XkeymacsdllDLL = { NULL, NULL };\r
134 \r
135 static HINSTANCE g_hDllInst = NULL;\r
136 static DWORD g_TlsIndex = 0;\r
137 \r
138 extern "C" int APIENTRY\r
139 DllMain(HINSTANCE hInstance, DWORD dwReason, LPVOID lpReserved)\r
140 {\r
141         g_hDllInst = hInstance;\r
142         LPVOID lpData;\r
143         \r
144         // Remove this if you use lpReserved\r
145         UNREFERENCED_PARAMETER(lpReserved);\r
146 \r
147         switch (dwReason) {\r
148         case DLL_PROCESS_ATTACH:\r
149                 TRACE0("XKEYMACSDLL.DLL Initializing!\n");\r
150 \r
151                 // Extension DLL one-time initialization\r
152                 if (!AfxInitExtensionModule(XkeymacsdllDLL, hInstance)) {\r
153                         return 0;\r
154                 }\r
155 \r
156                 // Insert this DLL into the resource chain\r
157                 // NOTE: If this Extension DLL is being implicitly linked to by\r
158                 //  an MFC Regular DLL (such as an ActiveX Control)\r
159                 //  instead of an MFC application, then you will want to\r
160                 //  remove this line from DllMain and put it in a separate\r
161                 //  function exported from this Extension DLL.  The Regular DLL\r
162                 //  that uses this Extension DLL should then explicitly call that\r
163                 //  function to initialize this Extension DLL.  Otherwise,\r
164                 //  the CDynLinkLibrary object will not be attached to the\r
165                 //  Regular DLL's resource chain, and serious problems will\r
166                 //  result.\r
167 \r
168                 try {\r
169                         new CDynLinkLibrary(XkeymacsdllDLL);\r
170                 }\r
171                 catch (CMemoryException* e) {\r
172                         e->Delete();\r
173 //                      CUtils::Log("DllMain: 'new' threw an exception");\r
174                 }\r
175 \r
176                 if ((g_TlsIndex = TlsAlloc()) == TLS_OUT_OF_INDEXES)\r
177                         return FALSE;\r
178                 // fall through\r
179         case DLL_THREAD_ATTACH:\r
180                 if ((lpData = LocalAlloc(LPTR, sizeof(HHOOK))) != NULL)\r
181                         TlsSetValue(g_TlsIndex, lpData);\r
182                 break;\r
183         case DLL_PROCESS_DETACH:\r
184                 TRACE0("XKEYMACSDLL.DLL Terminating!\n");\r
185                 // Terminate the library before destructors are called\r
186                 AfxTermExtensionModule(XkeymacsdllDLL);\r
187                 CXkeymacsDll::ReleaseKeyboardHook();\r
188                 if ((lpData = TlsGetValue(g_TlsIndex)) != NULL)\r
189                         LocalFree(lpData);\r
190                 TlsFree(g_TlsIndex);\r
191                 break;\r
192         case DLL_THREAD_DETACH:\r
193                 CXkeymacsDll::ReleaseKeyboardHook();\r
194                 if ((lpData = TlsGetValue(g_TlsIndex)) != NULL)\r
195                         LocalFree(lpData);\r
196                 break;\r
197         }\r
198         return 1;   // ok\r
199 }\r
200 \r
201 //////////////////////////////////////////////////////////////////////\r
202 // CXkeymacsDll Class\r
203 //////////////////////////////////////////////////////////////////////\r
204 \r
205 #pragma data_seg(".xkmcs")\r
206 Config CXkeymacsDll::m_Config = {0};\r
207 bool CXkeymacsDll::m_bEnableKeyboardHook = false;\r
208 BOOL CXkeymacsDll::m_bHook = TRUE;\r
209 DWORD CXkeymacsDll::m_nHookAltRelease = 0;\r
210 BOOL CXkeymacsDll::m_bRightShift = FALSE;\r
211 BOOL CXkeymacsDll::m_bRightControl = FALSE;\r
212 BOOL CXkeymacsDll::m_bRightAlt = FALSE;\r
213 TCHAR CXkeymacsDll::m_M_xTip[128] = "";\r
214 BYTE CXkeymacsDll::m_nOriginal[MAX_COMMAND_TYPE][MAX_KEY] = {'\0'};\r
215 int CXkeymacsDll::m_nAccelerate = 0;\r
216 int CXkeymacsDll::m_nKeyboardSpeed = 31;\r
217 HCURSOR CXkeymacsDll::m_hCurrentCursor = NULL;\r
218 BOOL CXkeymacsDll::m_bCursor = FALSE;\r
219 HCURSOR CXkeymacsDll::m_hCursor[MAX_STATUS] = {'\0'};\r
220 #pragma data_seg()\r
221 \r
222 HHOOK CXkeymacsDll::m_hHookCallWnd = NULL;\r
223 HHOOK CXkeymacsDll::m_hHookCallWndRet = NULL;\r
224 HHOOK CXkeymacsDll::m_hHookGetMessage = NULL;\r
225 HHOOK CXkeymacsDll::m_hHookShell = NULL;\r
226 int CXkeymacsDll::m_nAppID = 0;\r
227 CList<CClipboardSnap *, CClipboardSnap *> CXkeymacsDll::m_oKillRing;\r
228 int CXkeymacsDll::m_nKillRing = 0;\r
229 KbdMacro* CXkeymacsDll::m_kbdMacro = NULL;\r
230 \r
231 BOOL CXkeymacsDll::SaveConfig()\r
232 {\r
233         TCHAR szTmp[MAX_PATH];\r
234         if (!GetTempPath(MAX_PATH, szTmp))\r
235                 return FALSE;\r
236         if (_tmakepath_s(szTmp, NULL, szTmp, _T("xkeymacs"), _T("tmp")))\r
237                 return FALSE;\r
238         HANDLE hFile = CreateFile(szTmp, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, 0, NULL);\r
239         if (hFile == INVALID_HANDLE_VALUE)\r
240                 return FALSE;\r
241         DWORD written;\r
242         BOOL res = WriteFile(hFile, &m_Config, sizeof(m_Config), &written, NULL) && written == sizeof(m_Config);\r
243         CloseHandle(hFile);\r
244         return res;\r
245 }\r
246 \r
247 BOOL CXkeymacsDll::LoadConfig()\r
248 {\r
249         TCHAR szTmp[MAX_PATH];\r
250         if (!GetTempPath(MAX_PATH, szTmp))\r
251                 return FALSE;\r
252         if (_tmakepath_s(szTmp, NULL, szTmp, _T("xkeymacs"), _T("tmp")))\r
253                 return FALSE;\r
254         HANDLE hFile = CreateFile(szTmp, GENERIC_READ, 0, NULL, OPEN_EXISTING, 0, NULL);\r
255         if (hFile == INVALID_HANDLE_VALUE)\r
256                 return FALSE;\r
257         DWORD read;\r
258         BOOL res = ReadFile(hFile, &m_Config, sizeof(m_Config), &read, NULL) && read == sizeof(m_Config);\r
259         CloseHandle(hFile);\r
260         return res;\r
261 }\r
262 \r
263 void CXkeymacsDll::SetConfig(const Config& config)\r
264 {\r
265         m_Config = config;\r
266 }\r
267 \r
268 void CXkeymacsDll::SetHooks()\r
269 {\r
270         m_hHookCallWnd = SetWindowsHookEx(WH_CALLWNDPROC, CallWndProc, g_hDllInst, 0);\r
271         m_hHookCallWndRet = SetWindowsHookEx(WH_CALLWNDPROCRET, CallWndRetProc, g_hDllInst, 0);\r
272         m_hHookGetMessage = SetWindowsHookEx(WH_GETMESSAGE, GetMsgProc, g_hDllInst, 0);\r
273         m_hHookShell = SetWindowsHookEx(WH_SHELL, ShellProc, g_hDllInst, 0);\r
274         m_bEnableKeyboardHook = true;\r
275 }\r
276 \r
277 void CXkeymacsDll::SetKeyboardHook(DWORD threadId)\r
278 {\r
279         LPVOID lpData = TlsGetValue(g_TlsIndex);\r
280         if (!lpData) {\r
281                 lpData = LocalAlloc(LPTR, sizeof(HHOOK));\r
282                 if (!lpData)\r
283                         return;\r
284                 if (!TlsSetValue(g_TlsIndex, lpData))\r
285                         return;\r
286         }\r
287         HHOOK *phHook = reinterpret_cast<HHOOK *>(lpData);\r
288         if (!*phHook)\r
289                 *phHook = SetWindowsHookEx(WH_KEYBOARD, KeyboardProc, g_hDllInst, threadId ? threadId : GetCurrentThreadId());\r
290 }\r
291 \r
292 inline void unhook(HHOOK &hh)\r
293 {\r
294         if (hh)\r
295                 UnhookWindowsHookEx(hh);\r
296         hh = NULL;\r
297 }\r
298 \r
299 void CXkeymacsDll::ResetHooks() \r
300 {\r
301         ReleaseHooks();\r
302         SetHooks();\r
303 }\r
304 \r
305 void CXkeymacsDll::ReleaseHooks()\r
306 {\r
307         unhook(m_hHookCallWnd);\r
308         unhook(m_hHookCallWndRet);\r
309         unhook(m_hHookGetMessage);\r
310         unhook(m_hHookShell);\r
311         m_bEnableKeyboardHook = false;\r
312 }\r
313 \r
314 void CXkeymacsDll::ReleaseKeyboardHook()\r
315 {\r
316         HHOOK *phHook = reinterpret_cast<HHOOK *>(TlsGetValue(g_TlsIndex));\r
317         if (phHook)\r
318                 unhook(*phHook);\r
319 }\r
320 \r
321 void CXkeymacsDll::ToggleKeyboardHookState()\r
322 {\r
323         m_bHook = !m_bHook;\r
324         ShowKeyboardHookState();\r
325 }\r
326 \r
327 BOOL CXkeymacsDll::IsKeyboardHook()\r
328 {\r
329         return m_bHook;\r
330 }\r
331 \r
332 void CXkeymacsDll::ShowKeyboardHookState()\r
333 {\r
334         IconMsg msg = {MAIN_ICON,};\r
335         if (m_bHook) {\r
336                 if (CCommands::IsTemporarilyDisableXKeymacs()) {\r
337                         msg.nState = STATUS_DISABLE_TMP;\r
338                         m_hCurrentCursor = m_hCursor[STATUS_DISABLE_TMP];\r
339                 } else {\r
340                         msg.nState = STATUS_ENABLE;\r
341                         m_hCurrentCursor = m_hCursor[STATUS_ENABLE];\r
342                 }\r
343         } else {\r
344                 msg.nState = STATUS_DISABLE_WOCQ;\r
345         }\r
346         if (m_Config.nSettingStyle[m_nAppID] == SETTING_DISABLE\r
347          || (!_tcsicmp(m_Config.szSpecialApp[m_nAppID], _T("Default"))\r
348           && CUtils::IsDefaultIgnoreApplication())) {\r
349                 msg.nState = STATUS_DISABLE;\r
350                 m_hCurrentCursor = m_hCursor[STATUS_DISABLE];\r
351         }\r
352         SendIconMessage(&msg, 1);\r
353         DoSetCursor();\r
354 }\r
355 \r
356 LRESULT CALLBACK CXkeymacsDll::CallWndProc(int nCode, WPARAM wParam, LPARAM lParam)\r
357 {\r
358         SetKeyboardHook();\r
359         if (nCode >= 0) {\r
360                 const CWPSTRUCT *cwps = reinterpret_cast<CWPSTRUCT *>(lParam);\r
361                 switch (cwps->message) {\r
362                 case WM_IME_STARTCOMPOSITION:\r
363                         InitKeyboardProc(TRUE);\r
364                         break;\r
365                 case WM_IME_ENDCOMPOSITION:\r
366                         InitKeyboardProc(FALSE);\r
367                         break;\r
368                 case WM_SETFOCUS:\r
369                         if (cwps->hwnd == GetForegroundWindow()) {\r
370                                 InitKeyboardProc(FALSE);\r
371                                 ShowKeyboardHookState();\r
372                         }\r
373                         break;\r
374                 case WM_NCACTIVATE:\r
375                         if (cwps->wParam && cwps->hwnd == GetForegroundWindow()) {\r
376                                 InitKeyboardProc(FALSE);\r
377                                 ShowKeyboardHookState();\r
378                         }\r
379                         break;\r
380                 }\r
381         }\r
382         return CallNextHookEx(NULL, nCode, wParam, lParam);\r
383 }\r
384 \r
385 LRESULT CALLBACK CXkeymacsDll::CallWndRetProc(int nCode, WPARAM wParam, LPARAM lParam)\r
386 {\r
387         SetKeyboardHook();\r
388         if (nCode >= 0) {\r
389                 const CWPRETSTRUCT *cwprets = reinterpret_cast<CWPRETSTRUCT *>(lParam);\r
390                 switch (cwprets->message) {\r
391                 case WM_SETTEXT:\r
392                         if (cwprets->hwnd == GetForegroundWindow())\r
393                                 InitKeyboardProc(FALSE);\r
394                         break;\r
395                 case WM_SETCURSOR:\r
396                         DoSetCursor();\r
397                         break;\r
398                 }\r
399         }\r
400         return CallNextHookEx(NULL, nCode, wParam, lParam);\r
401 }\r
402 \r
403 LRESULT CALLBACK CXkeymacsDll::GetMsgProc(int nCode, WPARAM wParam, LPARAM lParam)\r
404 {\r
405         SetKeyboardHook();\r
406         if (nCode >= 0) {\r
407                 const MSG *msg = reinterpret_cast<MSG *>(lParam);\r
408                 switch (msg->message) {\r
409                 case WM_IME_STARTCOMPOSITION:\r
410                         InitKeyboardProc(TRUE);\r
411                         break;\r
412                 case WM_IME_ENDCOMPOSITION:\r
413                         InitKeyboardProc(FALSE);\r
414                         break;\r
415                 }\r
416         }\r
417         return CallNextHookEx(NULL, nCode, wParam, lParam);\r
418 }\r
419 \r
420 LRESULT CALLBACK CXkeymacsDll::ShellProc(int nCode, WPARAM wParam, LPARAM lParam)\r
421 {\r
422         if (nCode == HSHELL_WINDOWACTIVATED) {\r
423                 SetKeyboardHook(GetWindowThreadProcessId(reinterpret_cast<HWND>(wParam), NULL));\r
424                 TCHAR className[CLASS_NAME_LENGTH];\r
425                 GetClassName(reinterpret_cast<HWND>(wParam), className, CLASS_NAME_LENGTH);\r
426                 if (!_tcsicmp(className, _T("ConsoleWindowClass"))) {\r
427                         InitKeyboardProc(FALSE);\r
428                         ShowKeyboardHookState();\r
429                 }\r
430         }\r
431         return CallNextHookEx(NULL, nCode, wParam, lParam);\r
432 }\r
433 \r
434 void CXkeymacsDll::InitKeyboardProc(BOOL bImeComposition)\r
435 {\r
436         CUtils::SetApplicationName(bImeComposition);\r
437         if (_tcsnicmp(m_Config.szSpecialApp[m_nAppID], CUtils::GetApplicationName(), 0xF) || !IsMatchWindowText(m_Config.szWindowText[m_nAppID])) {     // PROCESSENTRY32 has only 0xF bytes of Name\r
438                 m_nAppID = -1;\r
439                 for (int nAppID = 0; nAppID < MAX_APP; ++nAppID) {\r
440                         if (_tcsnicmp(m_Config.szSpecialApp[nAppID], CUtils::GetApplicationName(), 0xF) || !IsMatchWindowText(m_Config.szWindowText[nAppID]))\r
441                                 continue;\r
442                         if (m_nAppID < 0)\r
443                                 m_nAppID = nAppID;\r
444                         else {\r
445                                 const LPCSTR curText = m_Config.szWindowText[m_nAppID];\r
446                                 const LPCSTR newText = m_Config.szWindowText[nAppID];\r
447                                 const int curType = CUtils::GetWindowTextType(curText);\r
448                                 const int newType = CUtils::GetWindowTextType(newText);\r
449                                 if (curType < newType || curType == newType && _tcscmp(curText, newText) <= 0)\r
450                                         m_nAppID = nAppID;\r
451                         }\r
452                 }\r
453                 if (m_nAppID < 0)\r
454                         m_nAppID = GetAppID(_T("Default"), 0);\r
455         }\r
456         if (m_Config.nSettingStyle[m_nAppID] != SETTING_DISABLE &&\r
457                         (_tcsicmp(m_Config.szSpecialApp[m_nAppID], _T("Default")) || !CUtils::IsDefaultIgnoreApplication()) &&\r
458                         !bImeComposition && CUtils::IsDialog() && m_Config.bUseDialogSetting[m_nAppID])\r
459                 // Use Dialog Setting\r
460                 m_nAppID = GetAppID(_T("Dialog"), m_nAppID);\r
461 \r
462         IconMsg msg[3] = {\r
463                 {CX_ICON, OFF_ICON, ""},\r
464                 {MX_ICON, OFF_ICON, ""},\r
465                 {META_ICON, OFF_ICON, ""}\r
466         };\r
467         SendIconMessage(msg, 3);\r
468         CCommands::SetMark(FALSE);\r
469         CCommands::SetTemporarilyDisableXKeymacs(FALSE);\r
470         CCommands::Reset();\r
471 }\r
472 \r
473 int CXkeymacsDll::GetAppID(LPCSTR szName, int fallback)\r
474 {\r
475         for (int i = 0; i < MAX_APP; ++i)\r
476                 if (!_tcsicmp(m_Config.szSpecialApp[i], szName))\r
477                         return i;\r
478         return fallback;\r
479 }\r
480 \r
481 LRESULT CALLBACK CXkeymacsDll::KeyboardProc(int nCode, WPARAM wParam, LPARAM lParam)\r
482 {\r
483         const BYTE nOrigKey = static_cast<BYTE>(wParam);\r
484         const bool bRelease = (HIWORD(lParam) & KF_UP) != 0;\r
485         const bool bExtended = (HIWORD(lParam) & KF_EXTENDED) != 0;\r
486         BYTE nKey = nOrigKey;\r
487 \r
488         static BOOL bLocked = FALSE;\r
489         static const BYTE RECURSIVE_KEY = 0x07;\r
490         static int (*fLastCommand)() = NULL;\r
491         static BYTE nOneShotModifier[MAX_KEY] = {'\0'};\r
492         static BOOL bCherryOneShotModifier = FALSE;\r
493 \r
494 //      CUtils::Log(_T("nCode = %#x, nKey = %#x, lParam = %#x"), nCode, nOrigKey, lParam);\r
495 \r
496         if (!m_bEnableKeyboardHook || CUtils::IsXkeymacs() ||\r
497                         nCode < 0 || nCode == HC_NOREMOVE)\r
498                 return CallNextHookEx(NULL, nCode, wParam, lParam);\r
499 \r
500 //      CUtils::Log(_T("nKey = %#x, ext = %d, rel = %d, pre = %d, %#hx, %#hx"), nOrigKey,\r
501 //              (HIWORD(lParam) & KF_EXTENDED) ? 1 : 0, (HIWORD(lParam) & KF_UP) ? 1 : 0, (HIWORD(lParam) & KF_REPEAT) ? 1 : 0,\r
502 //              GetKeyState(nOrigKey), GetAsyncKeyState(nOrigKey));\r
503 \r
504         if (nOrigKey == RECURSIVE_KEY) {\r
505                 if (bRelease)\r
506                         goto HOOK_RECURSIVE_KEY;\r
507                 else\r
508                         goto RECURSIVE_COMMAND;\r
509         }\r
510 \r
511         CancelMarkWithShift(nOrigKey, bRelease);\r
512 \r
513         switch (nKey) {\r
514         case VK_CONTROL:\r
515                 nKey = bExtended ? VK_RCONTROL : VK_LCONTROL;\r
516                 break;\r
517         case VK_MENU:\r
518                 nKey = bExtended ? VK_RMENU : VK_LMENU;\r
519                 break;\r
520         case VK_SHIFT:\r
521                 nKey = bExtended ? VK_RSHIFT : VK_LSHIFT;\r
522                 break;\r
523         }\r
524 \r
525 #define fCommand(type) (Commands[m_Config.nCommandID[m_nAppID][(type)][nKey]].fCommand)\r
526 #define nFunctionID (m_Config.nFunctionID[m_nAppID][nType][nKey])\r
527 \r
528         if (bRelease) {\r
529                 switch (nOrigKey) {\r
530                 case VK_MENU:\r
531                         if (m_nHookAltRelease) {\r
532                                 if (m_nHookAltRelease & ~HOOK_ALT_LATER)\r
533                                         m_nHookAltRelease--;\r
534                                 else if (m_nHookAltRelease & HOOK_ALT_LATER)\r
535                                         m_nHookAltRelease = 0;\r
536                                 goto HOOK;\r
537                         }\r
538                         // pass through\r
539                 case VK_LWIN:\r
540                 case VK_RWIN:\r
541                 case VK_APPS:\r
542                         for (int i = 0; i < MAX_COMMAND_TYPE; ++i) {\r
543                                 int (*const fCommand)() = fCommand(i);\r
544                                 if (fCommand && !(nOrigKey == VK_MENU && fCommand == CCommands::MetaAlt))\r
545                                         goto HOOK;\r
546                         }\r
547                 }\r
548                 if (nOneShotModifier[nKey]) {\r
549                         ReleaseKey(nOneShotModifier[nKey]);\r
550                         nOneShotModifier[nKey] = 0;\r
551                         if (bCherryOneShotModifier) {\r
552                                 bCherryOneShotModifier = FALSE;\r
553                                 Kdu(nKey);\r
554                         }\r
555                 }\r
556                 goto DO_NOTHING;\r
557         }\r
558 \r
559         if (m_Config.nSettingStyle[m_nAppID] == SETTING_DISABLE)\r
560                 goto DO_NOTHING;\r
561 \r
562         // Do Nothing for Meadow, Mule for Win32, ... if those use default setting.\r
563         if (!_tcsicmp(m_Config.szSpecialApp[m_nAppID], _T("Default")) && CUtils::IsDefaultIgnoreApplication())\r
564                 goto DO_NOTHING;\r
565 \r
566         switch (IsPassThrough(nKey)) {\r
567         case GOTO_DO_NOTHING:\r
568                 goto DO_NOTHING;\r
569         case GOTO_HOOK:\r
570                 goto HOOK;\r
571         case CONTINUE:\r
572                 break;\r
573         }\r
574 \r
575         // set command type\r
576         int nType = IsDown(VK_SHIFT) * SHIFT | IsControl() * CONTROL | IsMeta() * META | CCommands::bC_x() * CONTROLX;\r
577         // Ignore undefined C-x ?\r
578         if (nType & CONTROLX && fCommand(nType) == NULL && nFunctionID < 0) {\r
579                 if (m_Config.bIgnoreUndefinedC_x[m_nAppID]) {\r
580                         CCommands::Reset(GOTO_HOOK);\r
581                         goto HOOK;\r
582                 }\r
583                 nType &= ~CONTROLX;\r
584         }\r
585         // Ignore undefined Meta Ctrl+?\r
586         if (CCommands::bM_() && nType & CONTROL) {\r
587                 if (fCommand(nType) == NULL && nFunctionID < 0) {\r
588                         if (m_Config.bIgnoreUndefinedMetaCtrl[m_nAppID]) {\r
589                                 if (CheckOriginal(CONTROL, nKey))\r
590                                         goto DO_NOTHING;\r
591                                 CCommands::Reset(GOTO_HOOK);\r
592                                 goto HOOK;\r
593                         }\r
594                         nType &= ~META;\r
595                 }\r
596         }\r
597 \r
598         int nVirtualType = GetModifierState(FALSE);\r
599         if (nOrigKey == VK_CONTROL)\r
600                 nVirtualType &= ~CONTROL;\r
601         if (nOrigKey == VK_MENU)\r
602                 nVirtualType &= ~META;\r
603         if (CheckOriginal(nVirtualType, nOrigKey))\r
604                 goto DO_NOTHING;\r
605 \r
606         int (*const fCommand)() = fCommand(nType);\r
607         if (fCommand == CCommands::EnableOrDisableXKeymacs) {\r
608                 ToggleKeyboardHookState();\r
609                 goto HOOK;\r
610         }\r
611         if (fCommand == CCommands::EnableXKeymacs) {\r
612                 if (!m_bHook)\r
613                         ToggleKeyboardHookState();\r
614                 goto HOOK;\r
615         }\r
616         if (fCommand == CCommands::DisableXKeymacs) {\r
617                 if (m_bHook)\r
618                         ToggleKeyboardHookState();\r
619                 goto HOOK;\r
620         }\r
621         if (!m_bHook)\r
622                 goto DO_NOTHING;\r
623 \r
624         if (CCommands::bM_x() && !bRelease) {\r
625                 static size_t index = 0;\r
626                 static TCHAR szPath[MAX_PATH] = {'\0'};\r
627                 if (fCommand == CCommands::BackwardChar) {\r
628                         if (index)\r
629                                 --index;\r
630                         goto HOOKX;\r
631                 } else if (fCommand == CCommands::BeginningOfLine) {\r
632                         index = 0;\r
633                         goto HOOKX;\r
634                 } else if (fCommand == CCommands::DeleteBackwardChar) {\r
635                         if (index) {\r
636                                 --index;\r
637                                 memmove(szPath + index, szPath + index + 1, MAX_PATH - index);\r
638                                 SetM_xTip(szPath);\r
639                         }\r
640                         goto HOOKX;\r
641                 } else if (fCommand == CCommands::DeleteChar) {\r
642                         if (index < _tcslen(szPath)) {\r
643                                 memmove(szPath + index, szPath + index + 1, MAX_PATH - index);\r
644                                 SetM_xTip(szPath);\r
645                         }\r
646                         goto HOOKX;\r
647                 } else if (fCommand == CCommands::EndOfLine) {\r
648                         index = _tcslen(szPath);\r
649                         goto HOOKX;\r
650                 } else if (fCommand == CCommands::ForwardChar) {\r
651                         if (index < _tcslen(szPath))\r
652                                 ++index;\r
653                         goto HOOKX;\r
654                 } else if (fCommand == CCommands::KeyboardQuit) {\r
655                         CCommands::bM_x(FALSE);\r
656                         index = 0;\r
657                         memset(szPath, 0, sizeof(szPath));\r
658                         goto HOOK;\r
659                 } else if (nKey == VK_RETURN || fCommand == CCommands::Newline) {\r
660                         InvokeM_x(szPath);\r
661                         CCommands::bM_x(FALSE);\r
662                         index = 0;\r
663                         memset(szPath, 0, sizeof(szPath));\r
664                         goto HOOK;\r
665                 } else if (nKey && index < MAX_PATH - 1) {\r
666                         const BOOL bIsShiftDown = IsDown(VK_SHIFT, FALSE);\r
667                         TCHAR nAscii = 0;\r
668                         do { // 1-127\r
669                                 if (a2v(++nAscii) == nKey && bIsShiftDown == IsShift(nAscii)) {\r
670 //                                      CUtils::Log("M-x: %#X (%c), %#X (%c)", nKey, nKey, nAscii, nAscii);\r
671                                         if (index < _tcslen(szPath))\r
672                                                 memmove(szPath + index + 1, szPath + index, MAX_PATH - index - 1);\r
673                                         szPath[index++] = nAscii;\r
674 //                                      CUtils::Log("M-x: %c(%#04x)", nAscii, nAscii);\r
675                                         SetM_xTip(szPath);\r
676                                         goto HOOKX;\r
677                                 }\r
678                         } while (nAscii != 127);\r
679                 }\r
680         }\r
681 \r
682         if (CCommands::bC_u() && nType == NONE) {\r
683                 if ('0' <= nKey && nKey <= '9') {\r
684                         CCommands::NumericArgument(nKey - '0');\r
685                         goto HOOK0_9;\r
686                 }\r
687                 if (nKey == VK_OEM_MINUS) {\r
688                         CCommands::NumericArgumentMinus();\r
689                         goto HOOK0_9;\r
690                 }\r
691         }\r
692 \r
693 #define OneShotModifier(type, vk, mod) \\r
694         if (fCommand(nType & ~type) == CCommands::OneShotModifier ## mod || \\r
695                         fCommand(nType) == CCommands::OneShotModifier ## mod ## Repeat) { \\r
696                 nOneShotModifier[nKey] = vk; \\r
697                 DepressKey(vk); \\r
698                 bCherryOneShotModifier = TRUE; \\r
699                 goto HOOK; \\r
700         } else if (fCommand(nType & ~CONTROL) == CCommands::OneShotModifier ## mod ## Repeat) { \\r
701                 ReleaseKey(vk); \\r
702                 bCherryOneShotModifier = FALSE; \\r
703                 Kdu(nKey); \\r
704                 goto HOOK; \\r
705         }\r
706 \r
707         OneShotModifier(CONTROL, VK_CONTROL, Ctrl);\r
708         OneShotModifier(META, VK_MENU, Alt);\r
709         OneShotModifier(SHIFT, VK_SHIFT, Shift);\r
710         int i;\r
711         for (i = 0; i < MAX_KEY; ++i)\r
712                 if (nOneShotModifier[i] == nOrigKey)\r
713                         break;\r
714         if (i == MAX_KEY)\r
715                 bCherryOneShotModifier = FALSE;\r
716 \r
717         if (0 <= nFunctionID && nFunctionID < MAX_FUNCTION && m_Config.szFunctionDefinition[nFunctionID][0]) {\r
718                 CallFunction(nFunctionID);\r
719                 CCommands::Reset(GOTO_HOOK);\r
720                 goto HOOK;\r
721         }\r
722 #undef fCommand\r
723 #undef nFunctionID\r
724 \r
725         if (!fCommand) {\r
726                 if (nOrigKey == VK_CONTROL || nOrigKey == VK_MENU || nOrigKey == VK_SHIFT)\r
727                         goto DO_NOTHING;\r
728                 if (!(nType & SHIFT)) {\r
729                         if (CCommands::IsSetMark()) {\r
730                                 if (CCommands::MoveCaret(nKey, nType & CONTROL) != CONTINUE) {\r
731                                         CCommands::ClearNumericArgument();\r
732                                         goto HOOK;\r
733                                 }\r
734                                 CCommands::SetMark(FALSE);\r
735                         }\r
736                 }\r
737                 if (1 < CCommands::GetNumericArgument()) {\r
738                         Kdu(nKey, CCommands::GetNumericArgument());\r
739                         CCommands::ClearNumericArgument();\r
740                         goto HOOK;\r
741                 }\r
742                 goto DO_NOTHING;\r
743         }\r
744 \r
745         if (CCommands::IsTemporarilyDisableXKeymacs()  && fCommand != CCommands::KeyboardQuit) {\r
746                 CCommands::SetTemporarilyDisableXKeymacs(FALSE);\r
747                 goto DO_NOTHING;\r
748         }\r
749 \r
750         m_bRightControl = IsDown(VK_RCONTROL, FALSE);\r
751         m_bRightAlt = IsDown(VK_RMENU, FALSE);\r
752         m_bRightShift = IsDown(VK_RSHIFT, FALSE);\r
753 \r
754         if (bLocked)\r
755                 goto HOOK_RECURSIVE_KEY;\r
756         bLocked = TRUE;\r
757         fLastCommand = fCommand;\r
758 RECURSIVE_COMMAND:\r
759         switch (fLastCommand()) {\r
760         case GOTO_DO_NOTHING:\r
761                 bLocked = FALSE;\r
762                 goto DO_NOTHING;\r
763         case GOTO_HOOK:\r
764                 bLocked = FALSE;\r
765                 goto HOOK;\r
766         case GOTO_RECURSIVE:\r
767                 goto RECURSIVE;\r
768         case GOTO_HOOKX:\r
769                 bLocked = FALSE;\r
770                 goto HOOKX;\r
771         case GOTO_HOOK0_9:\r
772                 bLocked = FALSE;\r
773                 goto HOOK0_9;\r
774         }\r
775 \r
776 DO_NOTHING:\r
777         SetModifierIcons();\r
778         if (m_kbdMacro)\r
779                 m_kbdMacro->Record(nKey, bRelease);\r
780         return CallNextHookEx(NULL, nCode, wParam, lParam);\r
781 \r
782 RECURSIVE:\r
783         Kdu(RECURSIVE_KEY, 1, FALSE);\r
784         goto HOOKX;\r
785 HOOK:\r
786         CCommands::SetLastCommand(fLastCommand);\r
787 HOOK0_9:\r
788 HOOKX:\r
789         SetModifierIcons();\r
790 HOOK_RECURSIVE_KEY:\r
791         return TRUE;\r
792 }\r
793 \r
794 void CXkeymacsDll::CancelMarkWithShift(BYTE nKey, bool bRelease)\r
795 {\r
796         static bool bShift;\r
797         if (nKey != VK_SHIFT)\r
798                 goto exit;\r
799         BYTE bVk = 0;\r
800         do {\r
801                 if (bVk == VK_SHIFT || VK_LSHIFT || VK_RSHIFT)\r
802                         continue;\r
803                 if (IsDown(bVk, FALSE))\r
804                         goto exit;\r
805         } while (++bVk);\r
806         if (!bRelease) {\r
807                 bShift = true;\r
808                 return;\r
809         }\r
810         if (bShift)\r
811                 CCommands::SetMark(FALSE);\r
812 exit:\r
813         bShift = false;\r
814         return;\r
815 }\r
816 \r
817 int CXkeymacsDll::IsPassThrough(BYTE nKey)\r
818 {\r
819         BYTE bVk = 0;\r
820         const BYTE *pnID = m_Config.nCommandID[m_nAppID][NONE]; \r
821         do {\r
822                 if (IsDown(bVk) && Commands[pnID[bVk]].fCommand == CCommands::PassThrough) {\r
823                         if (bVk == nKey)\r
824                                 return GOTO_HOOK;\r
825                         return GOTO_DO_NOTHING;\r
826                 }\r
827         } while (++bVk);\r
828         return CONTINUE;\r
829 }\r
830 \r
831 void CXkeymacsDll::InvokeM_x(LPCTSTR szPath)\r
832 {\r
833 //      CUtils::Log("M-x: szPath=_%s_", szPath);\r
834         int (*fCommand)() = NULL;\r
835         for (int i = 0; i < MAX_COMMAND; ++i)\r
836                 if (_tcsicmp(szPath, Commands[i].szCommandName) == 0) {\r
837                         fCommand = Commands[i].fCommand;\r
838                         break;\r
839                 }\r
840         if (fCommand) {\r
841 //              CUtils::Log("M-x: Command: _%s_", Commands[i].szCommandName);\r
842                 fCommand();\r
843         } else {\r
844 //              CUtils::Log("M-x: Path: _%s_", szPath);\r
845                 ShellExecute(NULL, NULL, szPath, NULL, NULL, SW_SHOWNORMAL);\r
846         }\r
847 }\r
848 \r
849 void CXkeymacsDll::SetModifierIcons()\r
850 {\r
851         IconMsg msg[6] = {\r
852                 {MX_ICON, CCommands::bM_x(), ""},\r
853                 {CX_ICON, CCommands::bC_x(), ""},\r
854                 {META_ICON, CCommands::bM_(), ""},\r
855                 {SHIFT_ICON, IsDown(VK_SHIFT, FALSE), ""},\r
856                 {CTRL_ICON, IsControl(), ""},\r
857                 {ALT_ICON, IsDown(VK_MENU, FALSE), ""}\r
858         };\r
859         _tcscpy_s(msg[0].szTip, m_M_xTip);\r
860         SendIconMessage(msg, 6);\r
861 }\r
862 \r
863 void CXkeymacsDll::SetM_xTip(LPCTSTR szPath)\r
864 {\r
865         _tcscpy_s(m_M_xTip, "M-x LED");\r
866         if (szPath && _tcslen(szPath) < 128 - 5)\r
867                 _stprintf_s(m_M_xTip, "M-x %s", szPath);\r
868 }\r
869 \r
870 BOOL CXkeymacsDll::SendIconMessage(IconMsg *pMsg, DWORD num)\r
871 {\r
872         DWORD ack, read;\r
873         return CallNamedPipe(ICON_PIPE, pMsg, sizeof(IconMsg) * num, &ack, sizeof(DWORD), &read, NMPWAIT_NOWAIT) && read == sizeof(DWORD);\r
874 }\r
875 \r
876 void CXkeymacsDll::Kdu(BYTE bVk, DWORD n, BOOL bOriginal)\r
877 {\r
878         while (n--) {\r
879                 DepressKey(bVk, bOriginal);\r
880                 ReleaseKey(bVk);\r
881         }\r
882 }\r
883 \r
884 void CXkeymacsDll::DepressKey(BYTE bVk, BOOL bOriginal) // bVk is virtual-key code, MSDN said\r
885 {\r
886         if (bOriginal) {\r
887 //              CUtils::Log(_T("i: %x, %d, %d, %d, %d, %d, %d, %d, %d"), bVk,\r
888 //                      IsDown(VK_CONTROL), IsDown(VK_CONTROL, FALSE), IsDepressedModifier(CCommands::C_), IsDepressedModifier(CCommands::C_, FALSE),\r
889 //                      IsDown(VK_MENU), IsDown(VK_MENU, FALSE), IsDepressedModifier(CCommands::MetaAlt), IsDepressedModifier(CCommands::MetaAlt, FALSE));\r
890                 SetOriginal(GetModifierState(), bVk);\r
891         }\r
892         DoKeybd_event(bVk, 0);\r
893 }\r
894 \r
895 void CXkeymacsDll::ReleaseKey(BYTE bVk) // bVk is virtual-key code, MSDN said\r
896 {\r
897         DoKeybd_event(bVk, KEYEVENTF_KEYUP);\r
898 }\r
899 \r
900 void CXkeymacsDll::DoKeybd_event(BYTE bVk, DWORD dwFlags)\r
901 {\r
902         switch (bVk) {\r
903         case VK_CONTROL:\r
904                 if (m_bRightControl)\r
905                         dwFlags |= KEYEVENTF_EXTENDEDKEY;\r
906                 break;\r
907 \r
908         case VK_MENU:\r
909                 if (m_bRightAlt)\r
910                         dwFlags |= KEYEVENTF_EXTENDEDKEY;\r
911                 break;\r
912 \r
913         case VK_SHIFT:\r
914                 if (m_bRightShift)\r
915                         bVk = VK_RSHIFT;\r
916                 break;\r
917         case VK_PAUSE:\r
918                 if (IsDown(VK_CONTROL, FALSE)) // Break\r
919                         dwFlags |= KEYEVENTF_EXTENDEDKEY;\r
920                 break;\r
921         case VK_INSERT:\r
922         case VK_DELETE:\r
923         case VK_HOME:\r
924         case VK_END:\r
925         case VK_NEXT:\r
926         case VK_PRIOR:\r
927         case VK_UP:\r
928         case VK_DOWN:\r
929         case VK_RIGHT:\r
930         case VK_LEFT:\r
931         case VK_NUMLOCK:\r
932         case VK_PRINT:\r
933                 dwFlags |= KEYEVENTF_EXTENDEDKEY;\r
934                 break;\r
935         }\r
936 //      CUtils::Log(_T("b: %x, %x, %x, %#hx, %#hx"), bVk, dwFlags, GetMessageExtraInfo(), GetKeyState(bVk), GetAsyncKeyState(bVk));\r
937         keybd_event(bVk, 0, dwFlags, GetMessageExtraInfo());\r
938 //      CUtils::Log(_T("a: %x, %x, %x, %#hx, %#hx"), bVk, dwFlags, GetMessageExtraInfo(), GetKeyState(bVk), GetAsyncKeyState(bVk));\r
939 }\r
940 \r
941 void CXkeymacsDll::SetOriginal(UINT nType, BYTE bVk)\r
942 {\r
943         m_nOriginal[nType & ~SHIFT][bVk]++;\r
944 }\r
945 \r
946 int CXkeymacsDll::CheckOriginal(UINT nType, BYTE bVk)\r
947 {\r
948         nType &= ~SHIFT;\r
949         if (m_nOriginal[nType][bVk])\r
950                 return m_nOriginal[nType][bVk]--;\r
951         return 0;\r
952 }\r
953 \r
954 UINT CXkeymacsDll::GetModifierState(BOOL bPhysicalKey)\r
955 {\r
956         UINT result = 0;\r
957         if (IsDown(VK_SHIFT, bPhysicalKey))\r
958                 result |= SHIFT;\r
959         if (IsDown(VK_CONTROL, bPhysicalKey))\r
960                 result |= CONTROL;\r
961         if (IsDown(VK_MENU, bPhysicalKey))\r
962                 result |= META;\r
963         return result;\r
964 }\r
965 \r
966 void CXkeymacsDll::SetModifierState(UINT after, UINT before)\r
967 {\r
968         if (after & SHIFT && !(before & SHIFT))\r
969                 DepressKey(VK_SHIFT);\r
970         else if (!(after & SHIFT) && before & SHIFT)\r
971                 ReleaseKey(VK_SHIFT);\r
972 \r
973         if (after & CONTROL && !(before & CONTROL)) {\r
974                 UpdateKeyboardState(VK_CONTROL, 1);\r
975                 DepressKey(VK_CONTROL);\r
976         } else if (!(after & CONTROL) && before & CONTROL) {\r
977                 ReleaseKey(VK_CONTROL);\r
978                 UpdateKeyboardState(VK_CONTROL, 0);\r
979         }\r
980 \r
981         const BOOL bHookApp =\r
982                 CUtils::IsVisualCpp() ||  CUtils::IsVisualStudio() ||\r
983                 CUtils::IsInternetExplorer() || CUtils::IsFirefox() || CUtils::IsChrome();\r
984         if (after & META && !(before & META)) {\r
985                 if (bHookApp)\r
986                         m_nHookAltRelease |= HOOK_ALT_LATER;\r
987                 DepressKey(VK_MENU);\r
988         } else if (!(after & META) && before & META) {\r
989                 if (bHookApp)\r
990                         ++m_nHookAltRelease;\r
991                 ReleaseKey(VK_MENU);\r
992         }\r
993 }\r
994 \r
995 BOOL CXkeymacsDll::UpdateKeyboardState(BYTE bVk, BYTE bState)\r
996 {\r
997         BYTE ks[256] = {'\0'};\r
998         if (!GetKeyboardState(ks))\r
999                 return FALSE;\r
1000         ks[bVk] = bState;\r
1001         return SetKeyboardState(ks);\r
1002 }\r
1003 \r
1004 BOOL CXkeymacsDll::IsControl()\r
1005 {\r
1006         return CCommands::bC_() || IsDepressedModifier(CCommands::C_);\r
1007 }\r
1008 \r
1009 BOOL CXkeymacsDll::IsMeta()\r
1010 {\r
1011         return CCommands::bM_() || IsDepressedModifier(CCommands::MetaAlt);\r
1012 }\r
1013 \r
1014 BOOL CXkeymacsDll::IsDepressedModifier(int (__cdecl *Modifier)(void), BOOL bPhysicalKey)\r
1015 {\r
1016         BYTE bVk = 0;\r
1017         const BYTE *pnID = m_Config.nCommandID[m_nAppID][NONE];\r
1018         do {\r
1019                 switch (bVk) {\r
1020                 case VK_SHIFT:\r
1021                 case VK_CONTROL:\r
1022                 case VK_MENU:\r
1023                 case 0xf0: // Eisu key. GetAsyncKeyState returns the wrong state of Eisu key.\r
1024                         continue;\r
1025                 }\r
1026                 if (IsDown(bVk, bPhysicalKey) && Commands[pnID[bVk]].fCommand == Modifier)\r
1027                         return TRUE;\r
1028         } while (++bVk);\r
1029         return FALSE;\r
1030 }\r
1031 \r
1032 BOOL CXkeymacsDll::IsDown(BYTE bVk, BOOL bPhysicalKey)\r
1033 {\r
1034         return bPhysicalKey ? GetAsyncKeyState(bVk) < 0 : GetKeyState(bVk) < 0;\r
1035 }\r
1036 \r
1037 void CXkeymacsDll::AddKillRing(BOOL bNewData)\r
1038 {\r
1039         if (m_Config.nKillRingMax[m_nAppID] == 0) {\r
1040                 return;\r
1041         }\r
1042 \r
1043         CClipboardSnap *pSnap = new CClipboardSnap;\r
1044         if( !pSnap ) return;\r
1045 \r
1046         BOOL bCapture = pSnap->Capture();\r
1047         bCapture = pSnap->Capture();    // for "office drawing shape format". Can CClipboardSnap care this problem?\r
1048 \r
1049         if( bCapture ) {\r
1050                 if (bNewData) {\r
1051                         m_oKillRing.AddHead(pSnap);\r
1052                 } else {\r
1053                         if (m_oKillRing.IsEmpty()) {\r
1054                                 m_oKillRing.AddHead(pSnap);\r
1055                         } else {\r
1056                                 CClipboardSnap *pParent;\r
1057                                 for (pParent = m_oKillRing.GetHead(); pParent->GetNext(); pParent = pParent->GetNext()) {\r
1058                                         ;\r
1059                                 }\r
1060                                 pParent->SetNext(pSnap);\r
1061                         }\r
1062                 }\r
1063         } else {\r
1064                 delete pSnap;\r
1065                 pSnap = NULL;\r
1066         }\r
1067 \r
1068         m_nKillRing = 0;\r
1069 \r
1070         if (m_Config.nKillRingMax[m_nAppID] < m_oKillRing.GetCount()) {\r
1071                 CClipboardSnap *pSnap = m_oKillRing.GetTail();\r
1072                 delete pSnap;\r
1073                 pSnap = NULL;\r
1074                 m_oKillRing.RemoveTail();\r
1075         }\r
1076 }\r
1077 \r
1078 // Return TRUE if there is another data\r
1079 // Return FALSE if there is no more data\r
1080 CClipboardSnap* CXkeymacsDll::GetKillRing(CClipboardSnap* pSnap, BOOL bForce)\r
1081 {\r
1082         if (m_Config.nKillRingMax[m_nAppID] == 0) {\r
1083                 return NULL;\r
1084         }\r
1085 \r
1086         if (m_oKillRing.IsEmpty()) {\r
1087                 return NULL;\r
1088         }\r
1089 \r
1090         m_nKillRing %= m_oKillRing.GetCount();\r
1091 \r
1092         if (!bForce) {\r
1093                 CClipboardSnap oCurrentSnap;\r
1094                 oCurrentSnap.Capture();\r
1095 \r
1096                 CClipboardSnap *pKillRing = m_oKillRing.GetAt(m_oKillRing.FindIndex(m_nKillRing));\r
1097                 if (!pKillRing) {\r
1098                         return NULL;\r
1099                 }\r
1100                 for (; pKillRing->GetNext(); pKillRing = pKillRing->GetNext()) {\r
1101                         ;\r
1102                 }\r
1103                 if (*pKillRing != oCurrentSnap) {\r
1104                         return NULL;\r
1105                 }\r
1106         }\r
1107 \r
1108         if (!pSnap) {\r
1109                 pSnap = m_oKillRing.GetAt(m_oKillRing.FindIndex(m_nKillRing));\r
1110         }\r
1111         pSnap->Restore();\r
1112 \r
1113         return pSnap->GetNext();\r
1114 }\r
1115 \r
1116 void CXkeymacsDll::IncreaseKillRingIndex(int nKillRing)\r
1117 {\r
1118         m_nKillRing += nKillRing;\r
1119 }\r
1120 \r
1121 BOOL CXkeymacsDll::GetEnableCUA()\r
1122 {\r
1123         return m_Config.bEnableCUA[m_nAppID];\r
1124 }\r
1125 \r
1126 BOOL CXkeymacsDll::Get326Compatible()\r
1127 {\r
1128         return m_Config.b326Compatible[m_nAppID];\r
1129 }\r
1130 \r
1131 BOOL CXkeymacsDll::Is106Keyboard()\r
1132 {\r
1133         return m_Config.b106Keyboard;\r
1134 }\r
1135 \r
1136 void CXkeymacsDll::SetKbMacro(KbdMacro* kbdMacro)\r
1137 {\r
1138         m_kbdMacro = kbdMacro;\r
1139 }\r
1140 \r
1141 // call an original command which is defined in dot.xkeymacs\r
1142 void CXkeymacsDll::CallFunction(int nFuncID)\r
1143 {\r
1144         if (nFuncID < 0 || nFuncID >= MAX_FUNCTION)\r
1145                 return;\r
1146         LPCTSTR def = m_Config.szFunctionDefinition[nFuncID];\r
1147         if (!def[0])\r
1148                 return;\r
1149         std::vector<KeyBind> keybinds;\r
1150         const LPCTSTR last = def + _tcslen(def) - 1;\r
1151         if (*def == _T('"') && *last == _T('"')) {\r
1152                 ++def; // skip '"'\r
1153                 while (def < last)\r
1154                         keybinds.push_back(ParseKey(def));\r
1155         } else if (*def == _T('[') && *last == _T(']')) {\r
1156                 while (++def < last) { // skip '[', ']', and ' '\r
1157                         if (*def == _T('?')) { // [?f ?o ?o]\r
1158                                 keybinds.push_back(ParseKey(++def));\r
1159                                 continue;\r
1160                         }\r
1161                         // [VK]\r
1162                         for (int i = 0; i < MAX_KEYNAME; ++i) {\r
1163                                 size_t keylen = _tcslen(KeyNames[i].name);\r
1164                                 if (!_tcsncmp(def, KeyNames[i].name, keylen)) {\r
1165                                         KeyBind keybind = {NONE, KeyNames[i].bVk};\r
1166                                         keybinds.push_back(keybind);\r
1167                                         def += keylen;\r
1168                                         break;\r
1169                                 }\r
1170                         }\r
1171                 }\r
1172         } else\r
1173                 return;\r
1174 \r
1175         BOOL bM_x = FALSE;\r
1176         TCHAR szPath[MAX_PATH] = {'\0'};\r
1177         unsigned int index = 0;\r
1178         BOOL bInitialized = FALSE;\r
1179         UINT before = GetModifierState(FALSE);\r
1180 \r
1181         for (std::vector<KeyBind>::const_iterator p = keybinds.begin(); p != keybinds.end(); ++p) {\r
1182                 const int nType = p->nType;\r
1183                 const BYTE bVk = p->bVk;\r
1184                 int (*fCommand)() = nType < MAX_COMMAND_TYPE ? Commands[m_Config.nCommandID[m_nAppID][nType][bVk]].fCommand : NULL;\r
1185                 if (fCommand) {\r
1186                         if (fCommand == CCommands::ExecuteExtendedCommand)\r
1187                                 bM_x = TRUE;\r
1188                         else if (!bInitialized) {\r
1189                                 SetModifierState(0, before);\r
1190                                 bInitialized = TRUE;\r
1191                         }\r
1192 //                      CUtils::Log("CallFunction: Command Name: %s", Commands[m_Config.nCommandID[m_nAppID][nType][bVk]].szCommandName);\r
1193                         while (fCommand() == GOTO_RECURSIVE)\r
1194                                 ;\r
1195                         continue;\r
1196                 }\r
1197                 if (bM_x) {\r
1198                         if (bVk == VK_RETURN)\r
1199                                 InvokeM_x(szPath);\r
1200                         else if (bVk != 0) {\r
1201                                 TCHAR nAscii = 0;\r
1202                                 do { // 1-127\r
1203                                         if (a2v(++nAscii) == bVk && ((nType & SHIFT) != 0) == IsShift(nAscii)) {\r
1204 //                                              CUtils::Log("M-x: %#X (%c), %#X (%c)", bVk, bVk, nAscii, nAscii);\r
1205                                                 szPath[index++] = nAscii;\r
1206                                                 break;\r
1207                                         }\r
1208                                 } while (nAscii != 127);\r
1209                         }\r
1210                         continue;\r
1211                 }\r
1212                 if (!bInitialized) {\r
1213                         SetModifierState(0, before);\r
1214                         bInitialized = TRUE;\r
1215                 }\r
1216                 if (nType & WIN_WIN)\r
1217                         DepressKey(VK_LWIN);\r
1218                 if (nType & WIN_CTRL)\r
1219                         DepressKey(VK_CONTROL);\r
1220                 if (nType & WIN_ALT)\r
1221                         DepressKey(VK_MENU);\r
1222                 if (nType & SHIFT)\r
1223                         DepressKey(VK_SHIFT);\r
1224                 Kdu(bVk);\r
1225                 const int nNextType = (p + 1) != keybinds.end() ? (p + 1)->nType : 0;\r
1226                 if (nType & SHIFT && !(nNextType & SHIFT))\r
1227                         ReleaseKey(VK_SHIFT);\r
1228                 if (nType & WIN_ALT && !(nNextType & WIN_ALT))\r
1229                         ReleaseKey(VK_MENU);\r
1230                 if (nType & WIN_CTRL && !(nNextType & WIN_CTRL))\r
1231                         ReleaseKey(VK_CONTROL);\r
1232                 if (nType & WIN_WIN && !(nNextType & WIN_WIN))\r
1233                         ReleaseKey(VK_LWIN);\r
1234         }\r
1235 \r
1236         if (bInitialized)\r
1237                 // If this lines is invoked on M-x, a window transition does not work well.\r
1238                 SetModifierState(before, 0);\r
1239         return;\r
1240 }\r
1241 \r
1242 KeyBind CXkeymacsDll::ParseKey(LPCTSTR& def)\r
1243 {\r
1244         KeyBind keybind = {NONE};\r
1245         if (*def == _T('\\')) { // set modifiers\r
1246                 ++def;\r
1247         LOOP:\r
1248                 for (int i = 0; i < MAX_MODIFIER; ++i) {\r
1249                         size_t len = _tcslen(Modifiers[i].name);\r
1250                         if (!_tcsncmp(def, Modifiers[i].name, len)) {\r
1251                                 keybind.nType |= Modifiers[i].id;\r
1252                                 def += len;\r
1253                                 goto LOOP;\r
1254                         }\r
1255                 }\r
1256         }\r
1257         if (IsShift(*def) && !(keybind.nType & (WIN_CTRL | WIN_ALT | WIN_WIN)))\r
1258                 keybind.nType |= SHIFT;\r
1259         int i = 0;\r
1260         for (; i < MAX_KEYNAME; ++i) {\r
1261                 size_t len = _tcslen(KeyNames[i].name);\r
1262                 if (!_tcsncmp(def, KeyNames[i].name, len)) {\r
1263                         def += len;\r
1264                         break;\r
1265                 }\r
1266         }\r
1267         keybind.bVk = i < MAX_KEYNAME ? KeyNames[i].bVk : a2v(*def++);\r
1268         return keybind;\r
1269 }\r
1270 \r
1271 BOOL CXkeymacsDll::IsShift(TCHAR nAscii)\r
1272 {\r
1273         switch (nAscii) {\r
1274         case _T(' '):\r
1275                 return FALSE;\r
1276         case _T('!'):\r
1277         case _T('"'):\r
1278         case _T('#'):\r
1279         case _T('$'):\r
1280         case _T('%'):\r
1281         case _T('&'):\r
1282                 return TRUE;\r
1283         case _T('\''):\r
1284                 return m_Config.b106Keyboard;\r
1285         case _T('('):\r
1286         case _T(')'):\r
1287         case _T('*'):\r
1288         case _T('+'):\r
1289                 return TRUE;\r
1290         case _T(','):\r
1291         case _T('-'):\r
1292         case _T('.'):\r
1293         case _T('/'):\r
1294         case _T('0'): case _T('1'): case _T('2'): case _T('3'): case _T('4'): case _T('5'): case _T('6'): case _T('7'): case _T('8'): case _T('9'):\r
1295                 return FALSE;\r
1296         case _T(':'):\r
1297                 return !m_Config.b106Keyboard;\r
1298         case _T(';'):\r
1299                 return FALSE;\r
1300         case _T('<'):\r
1301                 return TRUE;\r
1302         case _T('='):\r
1303                 return m_Config.b106Keyboard;\r
1304         case _T('>'):\r
1305         case _T('?'):\r
1306                 return TRUE;\r
1307         case _T('@'):\r
1308                 return !m_Config.b106Keyboard;\r
1309         case _T('A'): case _T('B'): case _T('C'): case _T('D'): case _T('E'): case _T('F'): case _T('G'): case _T('H'): case _T('I'): case _T('J'): \r
1310         case _T('K'): case _T('L'): case _T('M'): case _T('N'): case _T('O'): case _T('P'): case _T('Q'): case _T('R'): case _T('S'): case _T('T'): \r
1311         case _T('U'): case _T('V'): case _T('W'): case _T('X'): case _T('Y'): case _T('Z'): \r
1312                 return TRUE;\r
1313         case _T('['):\r
1314         case _T('\\'):\r
1315         case _T(']'):\r
1316                 return FALSE;\r
1317         case _T('^'):\r
1318                 return !m_Config.b106Keyboard;\r
1319         case _T('_'):\r
1320                 return TRUE;\r
1321         case _T('`'):\r
1322                 return m_Config.b106Keyboard;\r
1323         case _T('a'): case _T('b'): case _T('c'): case _T('d'): case _T('e'): case _T('f'): case _T('g'): case _T('h'): case _T('i'): case _T('j'): \r
1324         case _T('k'): case _T('l'): case _T('m'): case _T('n'): case _T('o'): case _T('p'): case _T('q'): case _T('r'): case _T('s'): case _T('t'): \r
1325         case _T('u'): case _T('v'): case _T('w'): case _T('x'): case _T('y'): case _T('z'): \r
1326                 return FALSE;\r
1327         case _T('{'):\r
1328         case _T('|'):\r
1329         case _T('}'):\r
1330         case _T('~'):\r
1331                 return TRUE;\r
1332         default:\r
1333                 return FALSE;\r
1334         }\r
1335 }\r
1336 \r
1337 BYTE CXkeymacsDll::a2v(TCHAR nAscii)\r
1338 {\r
1339         switch (nAscii) {\r
1340         case _T(' '):\r
1341                 return VK_SPACE;\r
1342         case _T('!'):\r
1343                 return '1';\r
1344         case _T('"'):\r
1345                 return m_Config.b106Keyboard ? '2' : (BYTE) 0xde;       // VK_OEM_7\r
1346         case _T('#'):\r
1347                 return '3';\r
1348         case _T('$'):\r
1349                 return '4';\r
1350         case _T('%'):\r
1351                 return '5';\r
1352         case _T('&'):\r
1353                 return m_Config.b106Keyboard ? '6' : '7';\r
1354         case _T('\''):\r
1355                 return m_Config.b106Keyboard ? '7' : (BYTE) 0xde;       // VK_OEM_7\r
1356         case _T('('):\r
1357                 return m_Config.b106Keyboard ? '8' : '9';\r
1358         case _T(')'):\r
1359                 return m_Config.b106Keyboard ? '9' : '0';\r
1360         case _T('*'):\r
1361                 return m_Config.b106Keyboard ? (BYTE) 0xba : '8';       // VK_OEM_1\r
1362         case _T('+'):\r
1363                 return 0xbb;    // VK_OEM_PLUS\r
1364         case _T(','):\r
1365                 return 0xbc;    // VK_OEM_COMMA\r
1366         case _T('-'):\r
1367                 return 0xbd;    // VK_OEM_MINUS\r
1368         case _T('.'):\r
1369                 return 0xbe;    // VK_OEM_PERIOD\r
1370         case _T('/'):\r
1371                 return 0xbf;    // VK_OEM_2\r
1372         case _T('0'): case _T('1'): case _T('2'): case _T('3'): case _T('4'): case _T('5'): case _T('6'): case _T('7'): case _T('8'): case _T('9'):\r
1373                 return nAscii;\r
1374         case _T(':'):\r
1375                 return 0xba;    // VK_OEM_1\r
1376         case _T(';'):\r
1377                 return m_Config.b106Keyboard ? (BYTE) 0xbb : (BYTE) 0xba;       // VK_OEM_PLUS  VK_OEM_1\r
1378         case _T('<'):\r
1379                 return 0xbc;    // VK_OEM_COMMA\r
1380         case _T('='):\r
1381                 return m_Config.b106Keyboard ? (BYTE) 0xbd : (BYTE) 0xbb;       // VK_OEM_MINUS VK_OEM_PLUS\r
1382         case _T('>'):\r
1383                 return 0xbe;    // VK_OEM_PERIOD\r
1384         case _T('?'):\r
1385                 return 0xbf;    // VK_OEM_2\r
1386         case _T('@'):\r
1387                 return m_Config.b106Keyboard ? (BYTE) 0xc0 : '2';\r
1388         case _T('A'): case _T('B'): case _T('C'): case _T('D'): case _T('E'): case _T('F'): case _T('G'): case _T('H'): case _T('I'): case _T('J'): \r
1389         case _T('K'): case _T('L'): case _T('M'): case _T('N'): case _T('O'): case _T('P'): case _T('Q'): case _T('R'): case _T('S'): case _T('T'): \r
1390         case _T('U'): case _T('V'): case _T('W'): case _T('X'): case _T('Y'): case _T('Z'): \r
1391                 return nAscii;\r
1392         case _T('['):\r
1393                 return 0xdb;    // VK_OEM_4\r
1394         case _T('\\'):\r
1395                 return 0xdc;    // VK_OEM_5\r
1396         case _T(']'):\r
1397                 return 0xdd;    // VK_OEM_6\r
1398         case _T('^'):\r
1399                 return m_Config.b106Keyboard ? (BYTE) 0xde : '6';       // VK_OEM_7\r
1400         case _T('_'):\r
1401                 return m_Config.b106Keyboard ? (BYTE) 0xe2 : (BYTE) 0xbd;       // VK_OEM_102   VK_OEM_MINUS\r
1402         case _T('`'):\r
1403                 return 0xc0;    // VK_OEM_3\r
1404         case _T('a'): case _T('b'): case _T('c'): case _T('d'): case _T('e'): case _T('f'): case _T('g'): case _T('h'): case _T('i'): case _T('j'): \r
1405         case _T('k'): case _T('l'): case _T('m'): case _T('n'): case _T('o'): case _T('p'): case _T('q'): case _T('r'): case _T('s'): case _T('t'): \r
1406         case _T('u'): case _T('v'): case _T('w'): case _T('x'): case _T('y'): case _T('z'): \r
1407                 return (BYTE) (nAscii - (_T('a') - _T('A')));\r
1408         case _T('{'):\r
1409                 return 0xdb;    // VK_OEM_4\r
1410         case _T('|'):\r
1411                 return 0xdc;    // VK_OEM_5\r
1412         case _T('}'):\r
1413                 return 0xdd;    // VK_OEM_6\r
1414         case _T('~'):\r
1415                 return m_Config.b106Keyboard ? (BYTE) 0xde : (BYTE) 0xc0;       // VK_OEM_7     VK_OEM_3\r
1416         default:\r
1417                 return 0;\r
1418         }\r
1419 }\r
1420 \r
1421 BOOL CXkeymacsDll::IsMatchWindowText(CString szWindowText)\r
1422 {\r
1423         BOOL bIsMatchWindowText = TRUE;\r
1424 \r
1425         TCHAR szCurrentWindowText[WINDOW_TEXT_LENGTH] = {'\0'};\r
1426         GetWindowText(GetForegroundWindow(), szCurrentWindowText, sizeof(szCurrentWindowText));\r
1427 \r
1428         switch (CUtils::GetWindowTextType(szWindowText)) {\r
1429         case IDS_WINDOW_TEXT_MATCH:                                                             // *foo*\r
1430                 szWindowText.Delete(0);                                                         // Delete first '*'\r
1431                 szWindowText.Delete(szWindowText.GetLength() - 1);      // Delete last '*'\r
1432                 bIsMatchWindowText = 0 <= CString(szCurrentWindowText).Find(szWindowText);\r
1433                 break;\r
1434         case IDS_WINDOW_TEXT_MATCH_FORWARD:                                             // foo*\r
1435                 szWindowText.Delete(szWindowText.GetLength() - 1);      // Delete last '*'\r
1436                 bIsMatchWindowText = 0 == CString(szCurrentWindowText).Find(szWindowText);\r
1437                 break;\r
1438         case IDS_WINDOW_TEXT_MATCH_BACKWARD:                                    // *foo\r
1439                 szWindowText.Delete(0);                                                         // Delete first '*'\r
1440                 bIsMatchWindowText = 0 <= CString(szCurrentWindowText).Find(szWindowText, CString(szCurrentWindowText).GetLength() - szWindowText.GetLength());\r
1441                 break;\r
1442         case IDS_WINDOW_TEXT_MATCH_FULL:                                                // foo\r
1443                 bIsMatchWindowText = szWindowText == CString(szCurrentWindowText);\r
1444                 break;\r
1445         case IDS_WINDOW_TEXT_IGNORE:                                                    // *\r
1446                 bIsMatchWindowText = TRUE;\r
1447                 break;\r
1448         }\r
1449 //      CUtils::Log(_T("IsMatchWindowText: %d, _%s_, _%s_"), bIsMatchWindowText, szCurrentWindowText, szWindowText);\r
1450         return bIsMatchWindowText;\r
1451 }\r
1452 \r
1453 void CXkeymacsDll::SetAccelerate(int nAccelerate)\r
1454 {\r
1455         m_nAccelerate = nAccelerate;\r
1456 }\r
1457 \r
1458 int CXkeymacsDll::GetAccelerate()\r
1459 {\r
1460         return m_nAccelerate;\r
1461 }\r
1462 \r
1463 void CXkeymacsDll::SetKeyboardSpeed(int nKeyboardSpeed)\r
1464 {\r
1465         m_nKeyboardSpeed = nKeyboardSpeed;\r
1466 }\r
1467 \r
1468 unsigned int CXkeymacsDll::GetMaxKeyInterval()\r
1469 {\r
1470         // m_nKeyboardSpeed == 0:       slowest repeat rate; approximately  2 characters per second\r
1471         // m_nKeyboardSpeed == 31:      fastest repeat rate; approximately 30 characters per second\r
1472         // 47 ms is max on my machine w/ KeyboardSpeed 31.\r
1473         // 1000 /  2 + 50 = 550\r
1474         // 1000 / 30 + 50 = 83\r
1475         return (unsigned int) (1000.0 / (2.0 + m_nKeyboardSpeed % 32 * 28.0 / 31.0) + 50.0);\r
1476 }\r
1477 \r
1478 void CXkeymacsDll::SetCursorData(HCURSOR hEnable, HCURSOR hDisableTMP, HCURSOR hDisableWOCQ, HICON hDisable, BOOL bEnable)\r
1479 {\r
1480         m_hCursor[STATUS_ENABLE] = hEnable;\r
1481         m_hCursor[STATUS_DISABLE_TMP] = hDisableTMP;\r
1482         m_hCursor[STATUS_DISABLE_WOCQ] = hDisableWOCQ;\r
1483         m_hCursor[STATUS_DISABLE] = hDisable;\r
1484         m_bCursor = bEnable;\r
1485 }\r
1486 \r
1487 void CXkeymacsDll::DoSetCursor()\r
1488 {\r
1489         if (m_bCursor && m_hCurrentCursor)\r
1490                 ::SetCursor(m_hCurrentCursor);\r
1491 }\r