OSDN Git Service

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