OSDN Git Service

45ddeab0fa97c5c3c78571d111d18f9954a64ea5
[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 "TLS.h"\r
9 #include "TSFHandler.h"\r
10 #include "../xkeymacs/resource.h"\r
11 #include <math.h>\r
12 #include <Imm.h>\r
13 #include <vector>\r
14 \r
15 #ifdef _DEBUG\r
16 #define new DEBUG_NEW\r
17 #undef THIS_FILE\r
18 static char THIS_FILE[] = __FILE__;\r
19 #endif\r
20 \r
21 static AFX_EXTENSION_MODULE XkeymacsdllDLL = { NULL, NULL };\r
22 \r
23 static HINSTANCE g_hDllInst = NULL;\r
24 \r
25 extern "C" int APIENTRY\r
26 DllMain(HINSTANCE hInstance, DWORD dwReason, LPVOID lpReserved)\r
27 {\r
28         g_hDllInst = hInstance;\r
29         \r
30         // Remove this if you use lpReserved\r
31         UNREFERENCED_PARAMETER(lpReserved);\r
32 \r
33         switch (dwReason) {\r
34         case DLL_PROCESS_ATTACH:\r
35                 TRACE0("XKEYMACSDLL.DLL Initializing!\n");\r
36 \r
37                 // Extension DLL one-time initialization\r
38                 if (!AfxInitExtensionModule(XkeymacsdllDLL, hInstance)) {\r
39                         return 0;\r
40                 }\r
41 \r
42                 // Insert this DLL into the resource chain\r
43                 // NOTE: If this Extension DLL is being implicitly linked to by\r
44                 //  an MFC Regular DLL (such as an ActiveX Control)\r
45                 //  instead of an MFC application, then you will want to\r
46                 //  remove this line from DllMain and put it in a separate\r
47                 //  function exported from this Extension DLL.  The Regular DLL\r
48                 //  that uses this Extension DLL should then explicitly call that\r
49                 //  function to initialize this Extension DLL.  Otherwise,\r
50                 //  the CDynLinkLibrary object will not be attached to the\r
51                 //  Regular DLL's resource chain, and serious problems will\r
52                 //  result.\r
53 \r
54                 try {\r
55                         new CDynLinkLibrary(XkeymacsdllDLL);\r
56                 }\r
57                 catch (CMemoryException* e) {\r
58                         e->Delete();\r
59 //                      CUtils::Log("DllMain: 'new' threw an exception");\r
60                 }\r
61                 if (!TLS::Alloc())\r
62                         return FALSE;\r
63         case DLL_THREAD_ATTACH:\r
64                 break;\r
65         case DLL_PROCESS_DETACH:\r
66                 TRACE0("XKEYMACSDLL.DLL Terminating!\n");\r
67                 // Terminate the library before destructors are called\r
68                 AfxTermExtensionModule(XkeymacsdllDLL);\r
69                 CXkeymacsDll::ReleaseKeyboardHook();\r
70                 TLS::Free();\r
71                 break;\r
72         case DLL_THREAD_DETACH:\r
73                 CXkeymacsDll::ReleaseKeyboardHook();\r
74                 TLS::FreeLocal();\r
75                 break;\r
76         }\r
77         return 1;   // ok\r
78 }\r
79 \r
80 //////////////////////////////////////////////////////////////////////\r
81 // CXkeymacsDll Class\r
82 //////////////////////////////////////////////////////////////////////\r
83 \r
84 #pragma data_seg(".xkmcs")\r
85 Config CXkeymacsDll::m_Config = {0};\r
86 bool CXkeymacsDll::m_bEnableKeyboardHook = false;\r
87 bool CXkeymacsDll::m_bHook = true;\r
88 int CXkeymacsDll::m_nAccelerate = 0;\r
89 int CXkeymacsDll::m_nKeyboardSpeed = 31;\r
90 HCURSOR CXkeymacsDll::m_hCurrentCursor = NULL;\r
91 BOOL CXkeymacsDll::m_bCursor = FALSE;\r
92 HCURSOR CXkeymacsDll::m_hCursor[MAX_STATUS] = {'\0'};\r
93 #pragma data_seg()\r
94 \r
95 AppConfig* CXkeymacsDll::m_CurrentConfig = NULL;\r
96 BYTE (*CXkeymacsDll::m_CmdID)[MAX_KEY];\r
97 char (*CXkeymacsDll::m_FuncID)[MAX_KEY];\r
98 HHOOK CXkeymacsDll::m_hHookCallWnd = NULL;\r
99 HHOOK CXkeymacsDll::m_hHookCallWndRet = NULL;\r
100 HHOOK CXkeymacsDll::m_hHookGetMessage = NULL;\r
101 HHOOK CXkeymacsDll::m_hHookShell = NULL;\r
102 DWORD CXkeymacsDll::m_nHookAltRelease = 0;\r
103 BOOL CXkeymacsDll::m_bRightShift = FALSE;\r
104 BOOL CXkeymacsDll::m_bRightControl = FALSE;\r
105 BOOL CXkeymacsDll::m_bRightAlt = FALSE;\r
106 TCHAR CXkeymacsDll::m_M_xTip[128] = "";\r
107 BYTE CXkeymacsDll::m_nOriginal[MAX_COMMAND_TYPE][MAX_KEY] = {'\0'};\r
108 CList<CClipboardSnap *, CClipboardSnap *> CXkeymacsDll::m_oKillRing;\r
109 int CXkeymacsDll::m_nKillRing = 0;\r
110 KbdMacro* CXkeymacsDll::m_kbdMacro = NULL;\r
111 \r
112 BOOL CXkeymacsDll::SaveConfig()\r
113 {\r
114         TCHAR szTmp[MAX_PATH];\r
115         if (!GetTempPath(MAX_PATH, szTmp))\r
116                 return FALSE;\r
117         if (_tmakepath_s(szTmp, NULL, szTmp, _T("xkeymacs"), _T("tmp")))\r
118                 return FALSE;\r
119         HANDLE hFile = CreateFile(szTmp, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, 0, NULL);\r
120         if (hFile == INVALID_HANDLE_VALUE)\r
121                 return FALSE;\r
122         DWORD written;\r
123         BOOL res = WriteFile(hFile, &m_Config, sizeof(m_Config), &written, NULL) && written == sizeof(m_Config);\r
124         CloseHandle(hFile);\r
125         return res;\r
126 }\r
127 \r
128 BOOL CXkeymacsDll::LoadConfig()\r
129 {\r
130         TCHAR szTmp[MAX_PATH];\r
131         if (!GetTempPath(MAX_PATH, szTmp))\r
132                 return FALSE;\r
133         if (_tmakepath_s(szTmp, NULL, szTmp, _T("xkeymacs"), _T("tmp")))\r
134                 return FALSE;\r
135         HANDLE hFile = CreateFile(szTmp, GENERIC_READ, 0, NULL, OPEN_EXISTING, 0, NULL);\r
136         if (hFile == INVALID_HANDLE_VALUE)\r
137                 return FALSE;\r
138         DWORD read;\r
139         BOOL res = ReadFile(hFile, &m_Config, sizeof(m_Config), &read, NULL) && read == sizeof(m_Config);\r
140         CloseHandle(hFile);\r
141         return res;\r
142 }\r
143 \r
144 void CXkeymacsDll::SetConfig(const Config& config)\r
145 {\r
146         m_Config = config;\r
147 }\r
148 \r
149 void CXkeymacsDll::SetHooks()\r
150 {\r
151         m_hHookCallWnd = SetWindowsHookEx(WH_CALLWNDPROC, CallWndProc, g_hDllInst, 0);\r
152         m_hHookCallWndRet = SetWindowsHookEx(WH_CALLWNDPROCRET, CallWndRetProc, g_hDllInst, 0);\r
153         m_hHookGetMessage = SetWindowsHookEx(WH_GETMESSAGE, GetMsgProc, g_hDllInst, 0);\r
154         m_hHookShell = SetWindowsHookEx(WH_SHELL, ShellProc, g_hDllInst, 0);\r
155         m_bEnableKeyboardHook = true;\r
156 }\r
157 \r
158 void CXkeymacsDll::SetKeyboardHook(DWORD threadId)\r
159 {\r
160         if (!TLS::GetKeyboardHook())\r
161                 TLS::PutKeyboardHook(SetWindowsHookEx(WH_KEYBOARD, KeyboardProc, g_hDllInst, threadId ? threadId : GetCurrentThreadId()));\r
162 }\r
163 \r
164 inline void unhook(HHOOK &hh)\r
165 {\r
166         if (hh)\r
167                 UnhookWindowsHookEx(hh);\r
168         hh = NULL;\r
169 }\r
170 \r
171 void CXkeymacsDll::ResetHooks() \r
172 {\r
173         ReleaseHooks();\r
174         SetHooks();\r
175 }\r
176 \r
177 void CXkeymacsDll::ReleaseHooks()\r
178 {\r
179         unhook(m_hHookCallWnd);\r
180         unhook(m_hHookCallWndRet);\r
181         unhook(m_hHookGetMessage);\r
182         unhook(m_hHookShell);\r
183         m_bEnableKeyboardHook = false;\r
184 }\r
185 \r
186 void CXkeymacsDll::ReleaseKeyboardHook()\r
187 {\r
188         HHOOK hook = TLS::GetKeyboardHook();\r
189         if (!hook)\r
190                 return;\r
191         UnhookWindowsHookEx(hook);\r
192 }\r
193 \r
194 void CXkeymacsDll::SetHookStateDirect(bool enable)\r
195 {\r
196         m_bHook = enable;\r
197 }\r
198 \r
199 void CXkeymacsDll::ToggleHookState()\r
200 {\r
201         SetHookState(!m_bHook);\r
202 }\r
203 \r
204 void CXkeymacsDll::SetHookState(bool enable)\r
205 {\r
206         DWORD ack, read;\r
207         IPC32Message msg;\r
208         msg.Type = IPC32_HOOKSTATE;\r
209         msg.Enable = enable;\r
210         if (!CallNamedPipe(m_Config.PipeNameForIPC32, &msg, offsetof(IPC32Message, Enable) + sizeof(bool), &ack, sizeof(DWORD), &read, NMPWAIT_NOWAIT))\r
211                 CUtils::Log(_T("SetHookState: CallNamedPipe failed. (%d)"), GetLastError());\r
212 \r
213         ShowHookState();\r
214 }\r
215 \r
216 bool CXkeymacsDll::GetHookState()\r
217 {\r
218         return m_bHook;\r
219 }\r
220 \r
221 void CXkeymacsDll::ShowHookState()\r
222 {\r
223         IconState main = { MAIN_ICON, STATUS_ENABLE };\r
224         if (m_bHook) {\r
225                 if (CCommands::IsTemporarilyDisableXKeymacs()) {\r
226                         main.State = STATUS_DISABLE_TMP;\r
227                         m_hCurrentCursor = m_hCursor[STATUS_DISABLE_TMP];\r
228                 } else {\r
229                         main.State = STATUS_ENABLE;\r
230                         m_hCurrentCursor = m_hCursor[STATUS_ENABLE];\r
231                 }\r
232         } else\r
233                 main.State = STATUS_DISABLE_WOCQ;\r
234         if (m_CurrentConfig->SettingStyle == SETTING_DISABLE ||\r
235                         (!_tcsicmp(m_CurrentConfig->AppName, _T("Default")) && CUtils::IsDefaultIgnoreApplication())) {\r
236                 main.State = STATUS_DISABLE;\r
237                 m_hCurrentCursor = m_hCursor[STATUS_DISABLE];\r
238         }\r
239         SendIconMessage(&main, 1);\r
240         DoSetCursor();\r
241 }\r
242 \r
243 LRESULT CALLBACK CXkeymacsDll::CallWndProc(int nCode, WPARAM wParam, LPARAM lParam)\r
244 {\r
245         SetKeyboardHook();\r
246         TSFHandler::InitSink();\r
247         if (nCode >= 0) {\r
248                 const CWPSTRUCT *cwps = reinterpret_cast<CWPSTRUCT *>(lParam);\r
249                 switch (cwps->message) {\r
250                 case WM_IME_STARTCOMPOSITION:\r
251 #ifdef DEBUG_IME\r
252                         CUtils::Log(_T("WM_IME_STARTCOMPOSITION"));\r
253 #endif\r
254                         SetIMEState(true);\r
255                         break;\r
256                 case WM_IME_ENDCOMPOSITION:\r
257 #ifdef DEBUG_IME\r
258                         CUtils::Log(_T("WM_IME_ENDCOMPOSITION"));\r
259 #endif\r
260                         SetIMEState(false);\r
261                         break;\r
262                 case WM_SETFOCUS:\r
263                         if (cwps->hwnd == GetForegroundWindow() || GetWindowLong(cwps->hwnd, GWL_STYLE) == 0x56000000) {\r
264                                 SetIMEState(false);\r
265                                 ShowHookState();\r
266                         }\r
267                         break;\r
268                 case WM_NCACTIVATE:\r
269                         if (cwps->wParam && cwps->hwnd == GetForegroundWindow()) {\r
270                                 SetIMEState(false);\r
271                                 ShowHookState();\r
272                         }\r
273                         break;\r
274                 }\r
275         }\r
276         return CallNextHookEx(NULL, nCode, wParam, lParam);\r
277 }\r
278 \r
279 LRESULT CALLBACK CXkeymacsDll::CallWndRetProc(int nCode, WPARAM wParam, LPARAM lParam)\r
280 {\r
281         SetKeyboardHook();\r
282         if (nCode >= 0) {\r
283                 const CWPRETSTRUCT *cwprets = reinterpret_cast<CWPRETSTRUCT *>(lParam);\r
284                 switch (cwprets->message) {\r
285                 case WM_SETTEXT:\r
286                         if (cwprets->hwnd == GetForegroundWindow())\r
287                                 InitKeyboardProc();\r
288                         break;\r
289                 case WM_SETCURSOR:\r
290                         DoSetCursor();\r
291                         break;\r
292                 }\r
293         }\r
294         return CallNextHookEx(NULL, nCode, wParam, lParam);\r
295 }\r
296 \r
297 LRESULT CALLBACK CXkeymacsDll::GetMsgProc(int nCode, WPARAM wParam, LPARAM lParam)\r
298 {\r
299         SetKeyboardHook();\r
300         if (nCode >= 0) {\r
301                 const MSG *msg = reinterpret_cast<MSG *>(lParam);\r
302                 switch (msg->message) {\r
303                 case WM_IME_STARTCOMPOSITION:\r
304 #ifdef DEBUG_IME\r
305                         CUtils::Log(_T("WM_IME_STARTCOMPOSITION"));\r
306 #endif\r
307                         SetIMEState(true);\r
308                         break;\r
309                 case WM_IME_ENDCOMPOSITION:\r
310 #ifdef DEBUG_IME\r
311                         CUtils::Log(_T("WM_IME_ENDCOMPOSITION"));\r
312 #endif\r
313                         SetIMEState(false);\r
314                         break;\r
315                 }\r
316         }\r
317         return CallNextHookEx(NULL, nCode, wParam, lParam);\r
318 }\r
319 \r
320 void CXkeymacsDll::SetIMEState(bool on)\r
321 {\r
322         AppName::SetIMEState(on);\r
323         InitKeyboardProc();\r
324 }\r
325 \r
326 LRESULT CALLBACK CXkeymacsDll::ShellProc(int nCode, WPARAM wParam, LPARAM lParam)\r
327 {\r
328         if (nCode == HSHELL_WINDOWACTIVATED) {\r
329                 SetKeyboardHook(GetWindowThreadProcessId(reinterpret_cast<HWND>(wParam), NULL));\r
330                 TCHAR className[CLASS_NAME_LENGTH];\r
331                 GetClassName(reinterpret_cast<HWND>(wParam), className, CLASS_NAME_LENGTH);\r
332                 if (!_tcsicmp(className, _T("ConsoleWindowClass"))) {\r
333                         AppName::SetIMEState(false);\r
334                         InitKeyboardProc();\r
335                         ShowHookState();\r
336                 }\r
337         }\r
338         return CallNextHookEx(NULL, nCode, wParam, lParam);\r
339 }\r
340 \r
341 void CXkeymacsDll::InitKeyboardProc()\r
342 {\r
343         AppName::Init();\r
344         if (m_CurrentConfig == NULL ||\r
345                         _tcsnicmp(m_CurrentConfig->AppName, AppName::GetAppName(), 0xF) ||      // PROCESSENTRY32 has only 0xF bytes of Name\r
346                         !CUtils::IsMatchWindowText(m_CurrentConfig->WindowText)) {\r
347                 m_CurrentConfig = NULL;\r
348                 for (int nAppID = 0; nAppID < MAX_APP; ++nAppID) {\r
349                         AppConfig* appConfig = m_Config.AppConfig + nAppID;\r
350                         if (_tcsnicmp(appConfig->AppName, AppName::GetAppName(), 0xF) || !CUtils::IsMatchWindowText(appConfig->WindowText))\r
351                                 continue;\r
352                         if (m_CurrentConfig == NULL)\r
353                                 m_CurrentConfig = appConfig;\r
354                         else {\r
355                                 LPCTSTR curText = m_CurrentConfig->WindowText;\r
356                                 LPCTSTR newText = appConfig->WindowText;\r
357                                 int curType = CUtils::GetWindowTextType(curText);\r
358                                 int newType = CUtils::GetWindowTextType(newText);\r
359                                 if (curType < newType || curType == newType && _tcscmp(curText, newText) <= 0)\r
360                                         m_CurrentConfig = appConfig;\r
361                         }\r
362                 }\r
363                 if (m_CurrentConfig == NULL)\r
364                         m_CurrentConfig = GetAppConfig(_T("Default"), m_Config.AppConfig);\r
365         }\r
366         if (m_CurrentConfig->SettingStyle != SETTING_DISABLE &&\r
367                         (_tcsicmp(m_CurrentConfig->AppName, _T("Default")) || !CUtils::IsDefaultIgnoreApplication()) &&\r
368                         !AppName::GetIMEState() && CUtils::IsDialog() && m_CurrentConfig->UseDialogSetting)\r
369                 // Use Dialog Setting\r
370                 m_CurrentConfig = GetAppConfig(_T("Dialog"), m_CurrentConfig);\r
371         m_CmdID = m_CurrentConfig->CmdID;\r
372         m_FuncID = m_CurrentConfig->FuncID;\r
373 \r
374         IconState msg[3] = {\r
375                 {CX_ICON, OFF_ICON, ""},\r
376                 {MX_ICON, OFF_ICON, ""},\r
377                 {META_ICON, OFF_ICON, ""}\r
378         };\r
379         SendIconMessage(msg, 3);\r
380         CCommands::SetMark(FALSE);\r
381         CCommands::SetTemporarilyDisableXKeymacs(FALSE);\r
382         CCommands::Reset();\r
383 }\r
384 \r
385 AppConfig* CXkeymacsDll::GetAppConfig(LPCTSTR name, AppConfig* fallback)\r
386 {\r
387         for (int i = 0; i < MAX_APP; ++i)\r
388                 if (!_tcsicmp(m_Config.AppConfig[i].AppName, name))\r
389                         return m_Config.AppConfig + i;\r
390         return fallback;\r
391 }\r
392 \r
393 LRESULT CALLBACK CXkeymacsDll::KeyboardProc(int nCode, WPARAM wParam, LPARAM lParam)\r
394 {\r
395         BYTE nOrigKey = static_cast<BYTE>(wParam);\r
396         bool bRelease = (HIWORD(lParam) & KF_UP) != 0;\r
397         bool bExtended = (HIWORD(lParam) & KF_EXTENDED) != 0;\r
398         BYTE nKey = nOrigKey;\r
399 \r
400         static BOOL bLocked = FALSE;\r
401         static const BYTE RECURSIVE_KEY = 0x07;\r
402         static int (*fLastCommand)() = NULL;\r
403         static BYTE nOneShotModifier[MAX_KEY] = {'\0'};\r
404         static BOOL bCherryOneShotModifier = FALSE;\r
405 \r
406 //      CUtils::Log(_T("nCode = %#x, nKey = %#x, lParam = %#x"), nCode, nOrigKey, lParam);\r
407 \r
408         if (!m_bEnableKeyboardHook || m_CurrentConfig == NULL || CUtils::IsXkeymacs() ||\r
409                         nCode < 0 || nCode == HC_NOREMOVE)\r
410                 return CallNextHookEx(NULL, nCode, wParam, lParam);\r
411 \r
412 //      CUtils::Log(_T("nKey = %#x, ext = %d, rel = %d, pre = %d, %#hx, %#hx"), nOrigKey,\r
413 //              (HIWORD(lParam) & KF_EXTENDED) ? 1 : 0, (HIWORD(lParam) & KF_UP) ? 1 : 0, (HIWORD(lParam) & KF_REPEAT) ? 1 : 0,\r
414 //              GetKeyState(nOrigKey), GetAsyncKeyState(nOrigKey));\r
415 \r
416         if (nOrigKey == RECURSIVE_KEY) {\r
417                 if (bRelease)\r
418                         goto HOOK_RECURSIVE_KEY;\r
419                 else\r
420                         goto RECURSIVE_COMMAND;\r
421         }\r
422 \r
423         CancelMarkWithShift(nOrigKey, bRelease);\r
424 \r
425         switch (nKey) {\r
426         case VK_CONTROL:\r
427                 nKey = bExtended ? VK_RCONTROL : VK_LCONTROL;\r
428                 break;\r
429         case VK_MENU:\r
430                 nKey = bExtended ? VK_RMENU : VK_LMENU;\r
431                 break;\r
432         case VK_SHIFT:\r
433                 nKey = bExtended ? VK_RSHIFT : VK_LSHIFT;\r
434                 break;\r
435         }\r
436 \r
437         if (bRelease) {\r
438                 switch (nOrigKey) {\r
439                 case VK_MENU:\r
440                         if (m_nHookAltRelease) {\r
441                                 if (m_nHookAltRelease & ~HOOK_ALT_LATER)\r
442                                         m_nHookAltRelease--;\r
443                                 else if (m_nHookAltRelease & HOOK_ALT_LATER)\r
444                                         m_nHookAltRelease = 0;\r
445                                 goto HOOK;\r
446                         }\r
447                         // pass through\r
448                 case VK_LWIN:\r
449                 case VK_RWIN:\r
450                 case VK_APPS:\r
451                         for (int i = 0; i < MAX_COMMAND_TYPE; ++i) {\r
452                                 int (*fCommand)() = CmdTable::Command(m_CmdID[i][nKey]);\r
453                                 if (fCommand && !(nOrigKey == VK_MENU && fCommand == CCommands::MetaAlt))\r
454                                         goto HOOK;\r
455                         }\r
456                 }\r
457                 if (nOneShotModifier[nKey]) {\r
458                         ReleaseKey(nOneShotModifier[nKey]);\r
459                         nOneShotModifier[nKey] = 0;\r
460                         if (bCherryOneShotModifier) {\r
461                                 bCherryOneShotModifier = FALSE;\r
462                                 Kdu(nKey);\r
463                         }\r
464                 }\r
465                 goto DO_NOTHING;\r
466         }\r
467 \r
468         if (m_CurrentConfig->SettingStyle == SETTING_DISABLE)\r
469                 goto DO_NOTHING;\r
470 \r
471         // Do Nothing for Meadow, Mule for Win32, ... if those use default setting.\r
472         if (!_tcsicmp(m_CurrentConfig->AppName, _T("Default")) && CUtils::IsDefaultIgnoreApplication())\r
473                 goto DO_NOTHING;\r
474 \r
475         switch (IsPassThrough(nKey)) {\r
476         case GOTO_DO_NOTHING:\r
477                 goto DO_NOTHING;\r
478         case GOTO_HOOK:\r
479                 goto HOOK;\r
480         case CONTINUE:\r
481                 break;\r
482         }\r
483 \r
484         // set command type\r
485         int nType = IsDown(VK_SHIFT) * SHIFT | IsControl() * CONTROL | IsMeta() * META | CCommands::bC_x() * CONTROLX;\r
486         // Ignore undefined C-x ?\r
487         if (nType & CONTROLX && m_CmdID[nType][nKey] == 0 && m_FuncID[nType][nKey] < 0) {\r
488                 if (m_CurrentConfig->IgnoreUndefC_x) {\r
489                         CCommands::Reset(GOTO_HOOK);\r
490                         goto HOOK;\r
491                 }\r
492                 nType &= ~CONTROLX;\r
493         }\r
494         // Ignore undefined Meta Ctrl+?\r
495         if (CCommands::bM_() && nType & CONTROL) {\r
496                 if (m_CmdID[nType][nKey] == 0 && m_FuncID[nType][nKey] < 0) {\r
497                         if (m_CurrentConfig->IgnoreUndefMetaCtrl) {\r
498                                 if (CheckOriginal(CONTROL, nKey))\r
499                                         goto DO_NOTHING;\r
500                                 CCommands::Reset(GOTO_HOOK);\r
501                                 goto HOOK;\r
502                         }\r
503                         nType &= ~META;\r
504                 }\r
505         }\r
506 \r
507         int nVirtualType = GetModifierState(FALSE);\r
508         if (nOrigKey == VK_CONTROL)\r
509                 nVirtualType &= ~CONTROL;\r
510         if (nOrigKey == VK_MENU)\r
511                 nVirtualType &= ~META;\r
512         if (CheckOriginal(nVirtualType, nOrigKey))\r
513                 goto DO_NOTHING;\r
514 \r
515         int (*fCommand)() = CmdTable::Command(m_CmdID[nType][nKey]);\r
516         if (fCommand == CCommands::EnableOrDisableXKeymacs) {\r
517                 ToggleHookState();\r
518                 goto HOOK;\r
519         }\r
520         if (fCommand == CCommands::EnableXKeymacs) {\r
521                 SetHookState(true);\r
522                 goto HOOK;\r
523         }\r
524         if (fCommand == CCommands::DisableXKeymacs) {\r
525                 SetHookState(false);\r
526                 goto HOOK;\r
527         }\r
528         if (!m_bHook)\r
529                 goto DO_NOTHING;\r
530 \r
531         if (CCommands::bM_x() && !bRelease) {\r
532                 static size_t index = 0;\r
533                 static TCHAR szPath[MAX_PATH] = {'\0'};\r
534                 if (fCommand == CCommands::BackwardChar) {\r
535                         if (index)\r
536                                 --index;\r
537                         goto HOOKX;\r
538                 } else if (fCommand == CCommands::BeginningOfLine) {\r
539                         index = 0;\r
540                         goto HOOKX;\r
541                 } else if (fCommand == CCommands::DeleteBackwardChar) {\r
542                         if (index) {\r
543                                 --index;\r
544                                 memmove(szPath + index, szPath + index + 1, MAX_PATH - index);\r
545                                 SetM_xTip(szPath);\r
546                         }\r
547                         goto HOOKX;\r
548                 } else if (fCommand == CCommands::DeleteChar) {\r
549                         if (index < _tcslen(szPath)) {\r
550                                 memmove(szPath + index, szPath + index + 1, MAX_PATH - index);\r
551                                 SetM_xTip(szPath);\r
552                         }\r
553                         goto HOOKX;\r
554                 } else if (fCommand == CCommands::EndOfLine) {\r
555                         index = _tcslen(szPath);\r
556                         goto HOOKX;\r
557                 } else if (fCommand == CCommands::ForwardChar) {\r
558                         if (index < _tcslen(szPath))\r
559                                 ++index;\r
560                         goto HOOKX;\r
561                 } else if (fCommand == CCommands::KeyboardQuit) {\r
562                         CCommands::bM_x(FALSE);\r
563                         index = 0;\r
564                         memset(szPath, 0, sizeof(szPath));\r
565                         goto HOOK;\r
566                 } else if (nKey == VK_RETURN || fCommand == CCommands::Newline) {\r
567                         InvokeM_x(szPath);\r
568                         CCommands::bM_x(FALSE);\r
569                         index = 0;\r
570                         memset(szPath, 0, sizeof(szPath));\r
571                         goto HOOK;\r
572                 } else if (nKey && index < MAX_PATH - 1) {\r
573                         if (SHORT ascii = ConvVkey(nKey | (static_cast<BYTE>(IsDown(VK_SHIFT, FALSE)) << 8), 1)) {\r
574 //                              CUtils::Log("M-x: %#X (%c), %#X (%c)", nKey, nKey, ascii, ascii);\r
575                                 if (index < _tcslen(szPath))\r
576                                         memmove(szPath + index + 1, szPath + index, MAX_PATH - index - 1);\r
577                                 szPath[index++] = static_cast<TCHAR>(ascii);\r
578 //                              CUtils::Log("M-x: %c(%#04x)", ascii, ascii);\r
579                                 SetM_xTip(szPath);\r
580                                 goto HOOKX;\r
581                         }\r
582                 }\r
583         }\r
584 \r
585         if (CCommands::bC_u() && nType == NONE) {\r
586                 if ('0' <= nKey && nKey <= '9') {\r
587                         CCommands::NumericArgument(nKey - '0');\r
588                         goto HOOK0_9;\r
589                 }\r
590                 if (nKey == VK_OEM_MINUS) {\r
591                         CCommands::NumericArgumentMinus();\r
592                         goto HOOK0_9;\r
593                 }\r
594         }\r
595 \r
596 #define OneShotModifier(type, vk, mod) \\r
597         if (CmdTable::Command(m_CmdID[nType & ~type][nKey]) == CCommands::OneShotModifier ## mod || \\r
598                         CmdTable::Command(m_CmdID[nType][nKey]) == CCommands::OneShotModifier ## mod ## Repeat) { \\r
599                 nOneShotModifier[nKey] = vk; \\r
600                 DepressKey(vk); \\r
601                 bCherryOneShotModifier = TRUE; \\r
602                 goto HOOK; \\r
603         } else if (CmdTable::Command(m_CmdID[nType & ~CONTROL][nKey]) == CCommands::OneShotModifier ## mod ## Repeat) { \\r
604                 ReleaseKey(vk); \\r
605                 bCherryOneShotModifier = FALSE; \\r
606                 Kdu(nKey); \\r
607                 goto HOOK; \\r
608         }\r
609 \r
610         OneShotModifier(CONTROL, VK_CONTROL, Ctrl);\r
611         OneShotModifier(META, VK_MENU, Alt);\r
612         OneShotModifier(SHIFT, VK_SHIFT, Shift);\r
613         int i;\r
614         for (i = 0; i < MAX_KEY; ++i)\r
615                 if (nOneShotModifier[i] == nOrigKey)\r
616                         break;\r
617         if (i == MAX_KEY)\r
618                 bCherryOneShotModifier = FALSE;\r
619 \r
620         int id = m_FuncID[nType][nKey];\r
621         if (0 <= id && id < MAX_FUNCTION) {\r
622                 CallFunction(id);\r
623                 CCommands::Reset(GOTO_HOOK);\r
624                 goto HOOK;\r
625         }\r
626 \r
627         if (!fCommand) {\r
628                 if (nOrigKey == VK_CONTROL || nOrigKey == VK_MENU || nOrigKey == VK_SHIFT)\r
629                         goto DO_NOTHING;\r
630                 if (!(nType & SHIFT)) {\r
631                         if (CCommands::IsSetMark()) {\r
632                                 if (CCommands::MoveCaret(nKey, nType & CONTROL) != CONTINUE) {\r
633                                         CCommands::ClearNumericArgument();\r
634                                         goto HOOK;\r
635                                 }\r
636                                 CCommands::SetMark(FALSE);\r
637                         }\r
638                 }\r
639                 if (1 < CCommands::GetNumericArgument()) {\r
640                         Kdu(nKey, CCommands::GetNumericArgument());\r
641                         CCommands::ClearNumericArgument();\r
642                         goto HOOK;\r
643                 }\r
644                 goto DO_NOTHING;\r
645         }\r
646 \r
647         if (CCommands::IsTemporarilyDisableXKeymacs()  && fCommand != CCommands::KeyboardQuit) {\r
648                 CCommands::SetTemporarilyDisableXKeymacs(FALSE);\r
649                 goto DO_NOTHING;\r
650         }\r
651 \r
652         m_bRightControl = IsDown(VK_RCONTROL, FALSE);\r
653         m_bRightAlt = IsDown(VK_RMENU, FALSE);\r
654         m_bRightShift = IsDown(VK_RSHIFT, FALSE);\r
655 \r
656         if (bLocked)\r
657                 goto HOOK_RECURSIVE_KEY;\r
658         bLocked = TRUE;\r
659         fLastCommand = fCommand;\r
660 RECURSIVE_COMMAND:\r
661         switch (fLastCommand()) {\r
662         case GOTO_DO_NOTHING:\r
663                 bLocked = FALSE;\r
664                 goto DO_NOTHING;\r
665         case GOTO_HOOK:\r
666                 bLocked = FALSE;\r
667                 goto HOOK;\r
668         case GOTO_RECURSIVE:\r
669                 goto RECURSIVE;\r
670         case GOTO_HOOKX:\r
671                 bLocked = FALSE;\r
672                 goto HOOKX;\r
673         case GOTO_HOOK0_9:\r
674                 bLocked = FALSE;\r
675                 goto HOOK0_9;\r
676         }\r
677 \r
678 DO_NOTHING:\r
679         SetModifierIcons();\r
680         if (m_kbdMacro)\r
681                 m_kbdMacro->Record(nKey, bRelease);\r
682         return CallNextHookEx(NULL, nCode, wParam, lParam);\r
683 \r
684 RECURSIVE:\r
685         Kdu(RECURSIVE_KEY, 1, FALSE);\r
686         goto HOOKX;\r
687 HOOK:\r
688         CCommands::SetLastCommand(fLastCommand);\r
689 HOOK0_9:\r
690 HOOKX:\r
691         SetModifierIcons();\r
692 HOOK_RECURSIVE_KEY:\r
693         return TRUE;\r
694 }\r
695 \r
696 void CXkeymacsDll::CancelMarkWithShift(BYTE nKey, bool bRelease)\r
697 {\r
698         static bool bShift;\r
699         if (nKey != VK_SHIFT)\r
700                 goto exit;\r
701         BYTE bVk = 0;\r
702         do {\r
703                 if (bVk == VK_SHIFT || VK_LSHIFT || VK_RSHIFT)\r
704                         continue;\r
705                 if (IsDown(bVk, FALSE))\r
706                         goto exit;\r
707         } while (++bVk);\r
708         if (!bRelease) {\r
709                 bShift = true;\r
710                 return;\r
711         }\r
712         if (bShift)\r
713                 CCommands::SetMark(FALSE);\r
714 exit:\r
715         bShift = false;\r
716         return;\r
717 }\r
718 \r
719 int CXkeymacsDll::IsPassThrough(BYTE nKey)\r
720 {\r
721         BYTE bVk = 0;\r
722         const BYTE *pnID = m_CmdID[NONE]; \r
723         do {\r
724                 if (IsDown(bVk) && CmdTable::Command(pnID[bVk]) == CCommands::PassThrough) {\r
725                         if (bVk == nKey)\r
726                                 return GOTO_HOOK;\r
727                         return GOTO_DO_NOTHING;\r
728                 }\r
729         } while (++bVk);\r
730         return CONTINUE;\r
731 }\r
732 \r
733 void CXkeymacsDll::InvokeM_x(LPCTSTR szPath)\r
734 {\r
735 //      CUtils::Log("M-x: szPath=_%s_", szPath);\r
736         int (*fCommand)() = NULL;\r
737         for (int i = 0; i < MAX_COMMAND; ++i)\r
738                 if (_tcsicmp(szPath, CmdTable::Name(i)) == 0) {\r
739                         fCommand = CmdTable::Command(i);\r
740                         break;\r
741                 }\r
742         if (fCommand) {\r
743 //              CUtils::Log("M-x: Command: _%s_", Commands[i].szCommandName);\r
744                 fCommand();\r
745         } else {\r
746 //              CUtils::Log("M-x: Path: _%s_", szPath);\r
747                 ShellExecute(NULL, NULL, szPath, NULL, NULL, SW_SHOWNORMAL);\r
748         }\r
749 }\r
750 \r
751 void CXkeymacsDll::SetModifierIcons()\r
752 {\r
753         IconState icons[6] = {\r
754                 {MX_ICON, CCommands::bM_x(), ""},\r
755                 {CX_ICON, CCommands::bC_x(), ""},\r
756                 {META_ICON, CCommands::bM_(), ""},\r
757                 {SHIFT_ICON, IsDown(VK_SHIFT, FALSE), ""},\r
758                 {CTRL_ICON, IsControl(), ""},\r
759                 {ALT_ICON, IsDown(VK_MENU, FALSE), ""}\r
760         };\r
761         _tcscpy_s(icons[0].Tip, m_M_xTip);\r
762         SendIconMessage(icons, 6);\r
763 }\r
764 \r
765 void CXkeymacsDll::SetM_xTip(LPCTSTR szPath)\r
766 {\r
767         _tcscpy_s(m_M_xTip, "M-x LED");\r
768         if (szPath && _tcslen(szPath) < 128 - 5)\r
769                 _stprintf_s(m_M_xTip, "M-x %s", szPath);\r
770 }\r
771 \r
772 void CXkeymacsDll::SendIconMessage(IconState *state, int num)\r
773 {\r
774         DWORD ack, read;\r
775         IPC32Message msg;\r
776         msg.Type = IPC32_ICON;\r
777         memcpy(msg.IconState, state, num * sizeof(IconState));\r
778         if (!CallNamedPipe(m_Config.PipeNameForIPC32, &msg, offsetof(IPC32Message, IconState) + sizeof(IconState) * num, &ack, sizeof(DWORD), &read, NMPWAIT_NOWAIT)) {\r
779 #ifdef DEBUG_IPC\r
780                 CUtils::Log(_T("SendIconMessage: CallNamedPipe failed. (%d)"), GetLastError());\r
781 #endif\r
782         }\r
783 }\r
784 \r
785 void CXkeymacsDll::Kdu(BYTE bVk, DWORD n, BOOL bOriginal)\r
786 {\r
787         while (n--) {\r
788                 DepressKey(bVk, bOriginal);\r
789                 ReleaseKey(bVk);\r
790         }\r
791 }\r
792 \r
793 void CXkeymacsDll::DepressKey(BYTE bVk, BOOL bOriginal) // bVk is virtual-key code, MSDN said\r
794 {\r
795         if (bOriginal) {\r
796 //              CUtils::Log(_T("i: %x, %d, %d, %d, %d, %d, %d, %d, %d"), bVk,\r
797 //                      IsDown(VK_CONTROL), IsDown(VK_CONTROL, FALSE), IsDepressedModifier(CCommands::C_), IsDepressedModifier(CCommands::C_, FALSE),\r
798 //                      IsDown(VK_MENU), IsDown(VK_MENU, FALSE), IsDepressedModifier(CCommands::MetaAlt), IsDepressedModifier(CCommands::MetaAlt, FALSE));\r
799                 SetOriginal(GetModifierState(), bVk);\r
800         }\r
801         DoKeybd_event(bVk, 0);\r
802 }\r
803 \r
804 void CXkeymacsDll::ReleaseKey(BYTE bVk) // bVk is virtual-key code, MSDN said\r
805 {\r
806         DoKeybd_event(bVk, KEYEVENTF_KEYUP);\r
807 }\r
808 \r
809 void CXkeymacsDll::DoKeybd_event(BYTE bVk, DWORD dwFlags)\r
810 {\r
811         switch (bVk) {\r
812         case VK_CONTROL:\r
813                 if (m_bRightControl)\r
814                         dwFlags |= KEYEVENTF_EXTENDEDKEY;\r
815                 break;\r
816 \r
817         case VK_MENU:\r
818                 if (m_bRightAlt)\r
819                         dwFlags |= KEYEVENTF_EXTENDEDKEY;\r
820                 break;\r
821 \r
822         case VK_SHIFT:\r
823                 if (m_bRightShift)\r
824                         bVk = VK_RSHIFT;\r
825                 break;\r
826         case VK_PAUSE:\r
827                 if (IsDown(VK_CONTROL, FALSE)) // Break\r
828                         dwFlags |= KEYEVENTF_EXTENDEDKEY;\r
829                 break;\r
830         case VK_INSERT:\r
831         case VK_DELETE:\r
832         case VK_HOME:\r
833         case VK_END:\r
834         case VK_NEXT:\r
835         case VK_PRIOR:\r
836         case VK_UP:\r
837         case VK_DOWN:\r
838         case VK_RIGHT:\r
839         case VK_LEFT:\r
840         case VK_NUMLOCK:\r
841         case VK_PRINT:\r
842                 dwFlags |= KEYEVENTF_EXTENDEDKEY;\r
843                 break;\r
844         }\r
845 //      CUtils::Log(_T("b: %x, %x, %x, %#hx, %#hx"), bVk, dwFlags, GetMessageExtraInfo(), GetKeyState(bVk), GetAsyncKeyState(bVk));\r
846         keybd_event(bVk, 0, dwFlags, GetMessageExtraInfo());\r
847 //      CUtils::Log(_T("a: %x, %x, %x, %#hx, %#hx"), bVk, dwFlags, GetMessageExtraInfo(), GetKeyState(bVk), GetAsyncKeyState(bVk));\r
848 }\r
849 \r
850 void CXkeymacsDll::SetOriginal(UINT nType, BYTE bVk)\r
851 {\r
852         m_nOriginal[nType & ~SHIFT][bVk]++;\r
853 }\r
854 \r
855 int CXkeymacsDll::CheckOriginal(UINT nType, BYTE bVk)\r
856 {\r
857         nType &= ~SHIFT;\r
858         if (m_nOriginal[nType][bVk])\r
859                 return m_nOriginal[nType][bVk]--;\r
860         return 0;\r
861 }\r
862 \r
863 UINT CXkeymacsDll::GetModifierState(BOOL bPhysicalKey)\r
864 {\r
865         UINT result = 0;\r
866         if (IsDown(VK_SHIFT, bPhysicalKey))\r
867                 result |= SHIFT;\r
868         if (IsDown(VK_CONTROL, bPhysicalKey))\r
869                 result |= CONTROL;\r
870         if (IsDown(VK_MENU, bPhysicalKey))\r
871                 result |= META;\r
872         return result;\r
873 }\r
874 \r
875 void CXkeymacsDll::SetModifierState(UINT after, UINT before)\r
876 {\r
877         if (after & SHIFT && !(before & SHIFT))\r
878                 DepressKey(VK_SHIFT);\r
879         else if (!(after & SHIFT) && before & SHIFT)\r
880                 ReleaseKey(VK_SHIFT);\r
881 \r
882         if (after & CONTROL && !(before & CONTROL))\r
883                 DepressKey(VK_CONTROL);\r
884         else if (!(after & CONTROL) && before & CONTROL) {\r
885                 ReleaseKey(VK_CONTROL);\r
886                 UpdateKeyboardState(VK_CONTROL, 0);\r
887         }\r
888 \r
889         BOOL bHookApp =\r
890                 CUtils::IsVisualCpp() ||  CUtils::IsVisualStudio() ||\r
891                 CUtils::IsInternetExplorer() || CUtils::IsFirefox() || CUtils::IsChrome();\r
892         if (after & META && !(before & META)) {\r
893                 if (bHookApp)\r
894                         m_nHookAltRelease |= HOOK_ALT_LATER;\r
895                 DepressKey(VK_MENU);\r
896         } else if (!(after & META) && before & META) {\r
897                 if (bHookApp)\r
898                         ++m_nHookAltRelease;\r
899                 ReleaseKey(VK_MENU);\r
900         }\r
901 }\r
902 \r
903 BOOL CXkeymacsDll::UpdateKeyboardState(BYTE bVk, BYTE bState)\r
904 {\r
905         BYTE ks[256] = {'\0'};\r
906         if (!GetKeyboardState(ks))\r
907                 return FALSE;\r
908         ks[bVk] = bState;\r
909         return SetKeyboardState(ks);\r
910 }\r
911 \r
912 BOOL CXkeymacsDll::IsControl()\r
913 {\r
914         return CCommands::bC_() || IsDepressedModifier(CCommands::C_);\r
915 }\r
916 \r
917 BOOL CXkeymacsDll::IsMeta()\r
918 {\r
919         return CCommands::bM_() || IsDepressedModifier(CCommands::MetaAlt);\r
920 }\r
921 \r
922 BOOL CXkeymacsDll::IsDepressedModifier(int (__cdecl *Modifier)(void), BOOL bPhysicalKey)\r
923 {\r
924         BYTE bVk = 0;\r
925         const BYTE *pnID = m_CmdID[NONE];\r
926         do {\r
927                 switch (bVk) {\r
928                 case VK_SHIFT:\r
929                 case VK_CONTROL:\r
930                 case VK_MENU:\r
931                 case 0xf0: // Eisu key. GetAsyncKeyState returns the wrong state of Eisu key.\r
932                         continue;\r
933                 }\r
934                 if (IsDown(bVk, bPhysicalKey) && CmdTable::Command(pnID[bVk]) == Modifier)\r
935                         return TRUE;\r
936         } while (++bVk);\r
937         return FALSE;\r
938 }\r
939 \r
940 BOOL CXkeymacsDll::IsDown(BYTE bVk, BOOL bPhysicalKey)\r
941 {\r
942         return bPhysicalKey ? GetAsyncKeyState(bVk) < 0 : GetKeyState(bVk) < 0;\r
943 }\r
944 \r
945 void CXkeymacsDll::AddKillRing(BOOL bNewData)\r
946 {\r
947         if (m_CurrentConfig->KillRingMax == 0) {\r
948                 return;\r
949         }\r
950 \r
951         CClipboardSnap *pSnap = new CClipboardSnap;\r
952         if( !pSnap ) return;\r
953 \r
954         BOOL bCapture = pSnap->Capture();\r
955         bCapture = pSnap->Capture();    // for "office drawing shape format". Can CClipboardSnap care this problem?\r
956 \r
957         if( bCapture ) {\r
958                 if (bNewData) {\r
959                         m_oKillRing.AddHead(pSnap);\r
960                 } else {\r
961                         if (m_oKillRing.IsEmpty()) {\r
962                                 m_oKillRing.AddHead(pSnap);\r
963                         } else {\r
964                                 CClipboardSnap *pParent;\r
965                                 for (pParent = m_oKillRing.GetHead(); pParent->GetNext(); pParent = pParent->GetNext()) {\r
966                                         ;\r
967                                 }\r
968                                 pParent->SetNext(pSnap);\r
969                         }\r
970                 }\r
971         } else {\r
972                 delete pSnap;\r
973                 pSnap = NULL;\r
974         }\r
975 \r
976         m_nKillRing = 0;\r
977 \r
978         if (m_CurrentConfig->KillRingMax < m_oKillRing.GetCount()) {\r
979                 CClipboardSnap *pSnap = m_oKillRing.GetTail();\r
980                 delete pSnap;\r
981                 pSnap = NULL;\r
982                 m_oKillRing.RemoveTail();\r
983         }\r
984 }\r
985 \r
986 // Return TRUE if there is another data\r
987 // Return FALSE if there is no more data\r
988 CClipboardSnap* CXkeymacsDll::GetKillRing(CClipboardSnap* pSnap, BOOL bForce)\r
989 {\r
990         if (m_CurrentConfig->KillRingMax == 0) {\r
991                 return NULL;\r
992         }\r
993 \r
994         if (m_oKillRing.IsEmpty()) {\r
995                 return NULL;\r
996         }\r
997 \r
998         m_nKillRing %= m_oKillRing.GetCount();\r
999 \r
1000         if (!bForce) {\r
1001                 CClipboardSnap oCurrentSnap;\r
1002                 oCurrentSnap.Capture();\r
1003 \r
1004                 CClipboardSnap *pKillRing = m_oKillRing.GetAt(m_oKillRing.FindIndex(m_nKillRing));\r
1005                 if (!pKillRing) {\r
1006                         return NULL;\r
1007                 }\r
1008                 for (; pKillRing->GetNext(); pKillRing = pKillRing->GetNext()) {\r
1009                         ;\r
1010                 }\r
1011                 if (*pKillRing != oCurrentSnap) {\r
1012                         return NULL;\r
1013                 }\r
1014         }\r
1015 \r
1016         if (!pSnap) {\r
1017                 pSnap = m_oKillRing.GetAt(m_oKillRing.FindIndex(m_nKillRing));\r
1018         }\r
1019         pSnap->Restore();\r
1020 \r
1021         return pSnap->GetNext();\r
1022 }\r
1023 \r
1024 void CXkeymacsDll::IncreaseKillRingIndex(int nKillRing)\r
1025 {\r
1026         m_nKillRing += nKillRing;\r
1027 }\r
1028 \r
1029 bool CXkeymacsDll::GetEnableCUA()\r
1030 {\r
1031         return m_CurrentConfig->EnableCUA;\r
1032 }\r
1033 \r
1034 bool CXkeymacsDll::Get326Compatible()\r
1035 {\r
1036         return m_CurrentConfig->Is326Compatible;\r
1037 }\r
1038 \r
1039 bool CXkeymacsDll::Is106Keyboard()\r
1040 {\r
1041         return m_Config.Is106Keyboard;\r
1042 }\r
1043 \r
1044 void CXkeymacsDll::SetKbMacro(KbdMacro* kbdMacro)\r
1045 {\r
1046         m_kbdMacro = kbdMacro;\r
1047 }\r
1048 \r
1049 // call an original command which is defined in dot.xkeymacs\r
1050 void CXkeymacsDll::CallFunction(int id)\r
1051 {\r
1052         if (id < 0 || id >= MAX_FUNCTION)\r
1053                 return;\r
1054         BOOL bM_x = FALSE;\r
1055         TCHAR szPath[MAX_PATH] = {'\0'};\r
1056         unsigned int index = 0;\r
1057         BOOL bInitialized = FALSE;\r
1058         UINT before = GetModifierState(FALSE);\r
1059 \r
1060         for (KeyBind *p = m_Config.FuncDefs[id]; p->bVk; ++p) {\r
1061                 int nType = p->nType;\r
1062                 BYTE bVk = p->bVk;\r
1063                 int (*fCommand)() = nType < MAX_COMMAND_TYPE ? CmdTable::Command(m_CmdID[nType][bVk]) : NULL;\r
1064                 if (fCommand) {\r
1065                         if (fCommand == CCommands::ExecuteExtendedCommand)\r
1066                                 bM_x = TRUE;\r
1067                         else if (!bInitialized) {\r
1068                                 SetModifierState(0, before);\r
1069                                 bInitialized = TRUE;\r
1070                         }\r
1071 //                      CUtils::Log("CallFunction: Command Name: %s", Commands[m_CurrentConfig->CmdID[nType][bVk]].szCommandName);\r
1072                         while (fCommand() == GOTO_RECURSIVE)\r
1073                                 ;\r
1074                         continue;\r
1075                 }\r
1076                 if (bM_x) {\r
1077                         if (bVk == VK_RETURN)\r
1078                                 InvokeM_x(szPath);\r
1079                         else if (bVk != 0)\r
1080                                 szPath[index++] = static_cast<TCHAR>(ConvVkey((bVk | (nType << 8)) & 0x7ff /* drop CONTROLX */, 1));\r
1081                         continue;\r
1082                 }\r
1083                 if (!bInitialized) {\r
1084                         SetModifierState(0, before);\r
1085                         bInitialized = TRUE;\r
1086                 }\r
1087                 if (nType & WIN_WIN)\r
1088                         DepressKey(VK_LWIN);\r
1089                 if (nType & WIN_CTRL)\r
1090                         DepressKey(VK_CONTROL);\r
1091                 if (nType & WIN_ALT)\r
1092                         DepressKey(VK_MENU);\r
1093                 if (nType & SHIFT)\r
1094                         DepressKey(VK_SHIFT);\r
1095                 Kdu(bVk);\r
1096                 int nNextType = (p + 1)->nType;\r
1097                 if (nType & SHIFT && !(nNextType & SHIFT))\r
1098                         ReleaseKey(VK_SHIFT);\r
1099                 if (nType & WIN_ALT && !(nNextType & WIN_ALT))\r
1100                         ReleaseKey(VK_MENU);\r
1101                 if (nType & WIN_CTRL && !(nNextType & WIN_CTRL))\r
1102                         ReleaseKey(VK_CONTROL);\r
1103                 if (nType & WIN_WIN && !(nNextType & WIN_WIN))\r
1104                         ReleaseKey(VK_LWIN);\r
1105         }\r
1106 \r
1107         if (bInitialized)\r
1108                 // If this lines is invoked on M-x, a window transition does not work well.\r
1109                 SetModifierState(before, 0);\r
1110         return;\r
1111 }\r
1112 \r
1113 SHORT CXkeymacsDll::ConvVkey(SHORT in, int mode)\r
1114 {\r
1115         HKL h = GetKeyboardLayout(0);\r
1116         if (mode == 0) { // ASCII to VKey and state\r
1117                 SHORT r = VkKeyScanEx(static_cast<TCHAR>(in), h);\r
1118                 if (r < 0) // no key correcpont to the char\r
1119                         return 0;\r
1120                 return r & 0x7ff; // drop state flags of Hankaku and others\r
1121         }\r
1122         // VKey and state to ASCII\r
1123         const BYTE down = 0x80;\r
1124         BYTE state[256] = {0};\r
1125         if (in & (1 << 8))\r
1126                 state[VK_SHIFT] = down;\r
1127         if (in & (2 << 8))\r
1128                 state[VK_CONTROL] = down;\r
1129         if (in & (4 << 8))\r
1130                 state[VK_MENU] = down;\r
1131         UINT vkey = in & 0xff;\r
1132         state[vkey] = down;\r
1133         WORD word = 0;\r
1134         int r = ToAsciiEx(vkey, MapVirtualKeyEx(vkey, MAPVK_VK_TO_VSC, h), state, &word, 0, h);\r
1135         if (r == 0)\r
1136                 return 0;\r
1137         if (r == 1)\r
1138                 return static_cast<SHORT>(word);\r
1139         return static_cast<SHORT>(word >> 8); // drop a dead key\r
1140 }\r
1141 \r
1142 void CXkeymacsDll::SetAccelerate(int nAccelerate)\r
1143 {\r
1144         m_nAccelerate = nAccelerate;\r
1145 }\r
1146 \r
1147 int CXkeymacsDll::GetAccelerate()\r
1148 {\r
1149         return m_nAccelerate;\r
1150 }\r
1151 \r
1152 void CXkeymacsDll::SetKeyboardSpeed(int nKeyboardSpeed)\r
1153 {\r
1154         m_nKeyboardSpeed = nKeyboardSpeed;\r
1155 }\r
1156 \r
1157 unsigned int CXkeymacsDll::GetMaxKeyInterval()\r
1158 {\r
1159         // m_nKeyboardSpeed == 0:       slowest repeat rate; approximately  2 characters per second\r
1160         // m_nKeyboardSpeed == 31:      fastest repeat rate; approximately 30 characters per second\r
1161         // 47 ms is max on my machine w/ KeyboardSpeed 31.\r
1162         // 1000 /  2 + 50 = 550\r
1163         // 1000 / 30 + 50 = 83\r
1164         return (unsigned int) (1000.0 / (2.0 + m_nKeyboardSpeed % 32 * 28.0 / 31.0) + 50.0);\r
1165 }\r
1166 \r
1167 void CXkeymacsDll::SetCursorData(HCURSOR hEnable, HCURSOR hDisableTMP, HCURSOR hDisableWOCQ, HICON hDisable, BOOL bEnable)\r
1168 {\r
1169         m_hCursor[STATUS_ENABLE] = hEnable;\r
1170         m_hCursor[STATUS_DISABLE_TMP] = hDisableTMP;\r
1171         m_hCursor[STATUS_DISABLE_WOCQ] = hDisableWOCQ;\r
1172         m_hCursor[STATUS_DISABLE] = hDisable;\r
1173         m_bCursor = bEnable;\r
1174 }\r
1175 \r
1176 void CXkeymacsDll::DoSetCursor()\r
1177 {\r
1178         if (m_bCursor && m_hCurrentCursor)\r
1179                 ::SetCursor(m_hCurrentCursor);\r
1180 }\r