OSDN Git Service

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