OSDN Git Service

758dcfef9ec7cf36c5b3515edcebaf3307502675
[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         if (nCode >= 0) {\r
247                 const CWPSTRUCT *cwps = reinterpret_cast<CWPSTRUCT *>(lParam);\r
248                 switch (cwps->message) {\r
249                 case WM_IME_STARTCOMPOSITION:\r
250 #ifdef DEBUG_IME\r
251                         CUtils::Log(_T("WM_IME_STARTCOMPOSITION"));\r
252 #endif\r
253                         SetIMEState(true);\r
254                         break;\r
255                 case WM_IME_ENDCOMPOSITION:\r
256 #ifdef DEBUG_IME\r
257                         CUtils::Log(_T("WM_IME_ENDCOMPOSITION"));\r
258 #endif\r
259                         SetIMEState(false);\r
260                         break;\r
261                 case WM_SETFOCUS:\r
262                         if (cwps->hwnd == GetForegroundWindow() || GetWindowLong(cwps->hwnd, GWL_STYLE) == 0x56000000) {\r
263                                 SetIMEState(false);\r
264                                 ShowHookState();\r
265                         }\r
266                         break;\r
267                 case WM_NCACTIVATE:\r
268                         if (cwps->wParam && cwps->hwnd == GetForegroundWindow()) {\r
269                                 SetIMEState(false);\r
270                                 ShowHookState();\r
271                         }\r
272                         break;\r
273                 }\r
274         }\r
275         return CallNextHookEx(NULL, nCode, wParam, lParam);\r
276 }\r
277 \r
278 LRESULT CALLBACK CXkeymacsDll::CallWndRetProc(int nCode, WPARAM wParam, LPARAM lParam)\r
279 {\r
280         SetKeyboardHook();\r
281         if (nCode >= 0) {\r
282                 const CWPRETSTRUCT *cwprets = reinterpret_cast<CWPRETSTRUCT *>(lParam);\r
283                 switch (cwprets->message) {\r
284                 case WM_SETTEXT:\r
285                         if (cwprets->hwnd == GetForegroundWindow())\r
286                                 InitKeyboardProc();\r
287                         break;\r
288                 case WM_SETCURSOR:\r
289                         DoSetCursor();\r
290                         break;\r
291                 }\r
292         }\r
293         return CallNextHookEx(NULL, nCode, wParam, lParam);\r
294 }\r
295 \r
296 LRESULT CALLBACK CXkeymacsDll::GetMsgProc(int nCode, WPARAM wParam, LPARAM lParam)\r
297 {\r
298         SetKeyboardHook();\r
299         if (nCode >= 0) {\r
300                 const MSG *msg = reinterpret_cast<MSG *>(lParam);\r
301                 switch (msg->message) {\r
302                 case WM_IME_STARTCOMPOSITION:\r
303 #ifdef DEBUG_IME\r
304                         CUtils::Log(_T("WM_IME_STARTCOMPOSITION"));\r
305 #endif\r
306                         SetIMEState(true);\r
307                         break;\r
308                 case WM_IME_ENDCOMPOSITION:\r
309 #ifdef DEBUG_IME\r
310                         CUtils::Log(_T("WM_IME_ENDCOMPOSITION"));\r
311 #endif\r
312                         SetIMEState(false);\r
313                         break;\r
314                 }\r
315         }\r
316         return CallNextHookEx(NULL, nCode, wParam, lParam);\r
317 }\r
318 \r
319 void CXkeymacsDll::SetIMEState(bool on)\r
320 {\r
321         AppName::SetIMEState(on);\r
322         InitKeyboardProc();\r
323 }\r
324 \r
325 LRESULT CALLBACK CXkeymacsDll::ShellProc(int nCode, WPARAM wParam, LPARAM lParam)\r
326 {\r
327         if (nCode == HSHELL_WINDOWACTIVATED) {\r
328                 SetKeyboardHook(GetWindowThreadProcessId(reinterpret_cast<HWND>(wParam), NULL));\r
329                 TCHAR className[CLASS_NAME_LENGTH];\r
330                 GetClassName(reinterpret_cast<HWND>(wParam), className, CLASS_NAME_LENGTH);\r
331                 if (!_tcsicmp(className, _T("ConsoleWindowClass"))) {\r
332                         AppName::SetIMEState(false);\r
333                         InitKeyboardProc();\r
334                         ShowHookState();\r
335                 }\r
336         }\r
337         return CallNextHookEx(NULL, nCode, wParam, lParam);\r
338 }\r
339 \r
340 void CXkeymacsDll::InitKeyboardProc()\r
341 {\r
342         AppName::Init();\r
343         TSFHandler::InitSink();\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 (IsDepressedShiftKeyOnly(nKey))      {\r
700                 if (bRelease) {\r
701                         if (bShift)\r
702                                 CCommands::SetMark(false);\r
703                 } else  {\r
704                         bShift = true;\r
705                 }\r
706                 return;\r
707         }\r
708         bShift = false;\r
709 }\r
710 \r
711 static bool IsShift(byte bVk)\r
712 {\r
713         return bVk == VK_SHIFT || bVk == VK_LSHIFT || bVk == VK_RSHIFT;\r
714 }\r
715 \r
716 bool CXkeymacsDll::IsDepressedShiftKeyOnly(byte nKey)\r
717 {\r
718         if (!IsShift(nKey))\r
719                 return false;\r
720         for (byte bVk = 1; bVk < 255; bVk++) {\r
721                 if (IsShift(bVk))\r
722                         continue;\r
723                 if (IsDown(bVk))\r
724                         return false;\r
725         }\r
726         return true;\r
727 }\r
728 \r
729 int CXkeymacsDll::IsPassThrough(BYTE nKey)\r
730 {\r
731         BYTE bVk = 0;\r
732         const BYTE *pnID = m_CmdID[NONE]; \r
733         do {\r
734                 if (IsDown(bVk) && CmdTable::Command(pnID[bVk]) == CCommands::PassThrough) {\r
735                         if (bVk == nKey)\r
736                                 return GOTO_HOOK;\r
737                         return GOTO_DO_NOTHING;\r
738                 }\r
739         } while (++bVk);\r
740         return CONTINUE;\r
741 }\r
742 \r
743 void CXkeymacsDll::InvokeM_x(LPCTSTR szPath)\r
744 {\r
745 //      CUtils::Log("M-x: szPath=_%s_", szPath);\r
746         int (*fCommand)() = NULL;\r
747         for (int i = 0; i < MAX_COMMAND; ++i)\r
748                 if (_tcsicmp(szPath, CmdTable::Name(i)) == 0) {\r
749                         fCommand = CmdTable::Command(i);\r
750                         break;\r
751                 }\r
752         if (fCommand) {\r
753 //              CUtils::Log("M-x: Command: _%s_", Commands[i].szCommandName);\r
754                 fCommand();\r
755         } else {\r
756 //              CUtils::Log("M-x: Path: _%s_", szPath);\r
757                 ShellExecute(NULL, NULL, szPath, NULL, NULL, SW_SHOWNORMAL);\r
758         }\r
759 }\r
760 \r
761 void CXkeymacsDll::SetModifierIcons()\r
762 {\r
763         IconState icons[6] = {\r
764                 {MX_ICON, CCommands::bM_x(), ""},\r
765                 {CX_ICON, CCommands::bC_x(), ""},\r
766                 {META_ICON, CCommands::bM_(), ""},\r
767                 {SHIFT_ICON, IsDown(VK_SHIFT, FALSE), ""},\r
768                 {CTRL_ICON, IsControl(), ""},\r
769                 {ALT_ICON, IsDown(VK_MENU, FALSE), ""}\r
770         };\r
771         _tcscpy_s(icons[0].Tip, m_M_xTip);\r
772         SendIconMessage(icons, 6);\r
773 }\r
774 \r
775 void CXkeymacsDll::SetM_xTip(LPCTSTR szPath)\r
776 {\r
777         _tcscpy_s(m_M_xTip, "M-x LED");\r
778         if (szPath && _tcslen(szPath) < 128 - 5)\r
779                 _stprintf_s(m_M_xTip, "M-x %s", szPath);\r
780 }\r
781 \r
782 void CXkeymacsDll::SendIconMessage(IconState *state, int num)\r
783 {\r
784         DWORD ack, read;\r
785         IPC32Message msg;\r
786         msg.Type = IPC32_ICON;\r
787         memcpy(msg.IconState, state, num * sizeof(IconState));\r
788         if (!CallNamedPipe(m_Config.PipeNameForIPC32, &msg, offsetof(IPC32Message, IconState) + sizeof(IconState) * num, &ack, sizeof(DWORD), &read, NMPWAIT_NOWAIT)) {\r
789 #ifdef DEBUG_IPC\r
790                 CUtils::Log(_T("SendIconMessage: CallNamedPipe failed. (%d)"), GetLastError());\r
791 #endif\r
792         }\r
793 }\r
794 \r
795 void CXkeymacsDll::Kdu(BYTE bVk, DWORD n, BOOL bOriginal)\r
796 {\r
797         while (n--) {\r
798                 DepressKey(bVk, bOriginal);\r
799                 ReleaseKey(bVk);\r
800         }\r
801 }\r
802 \r
803 void CXkeymacsDll::DepressKey(BYTE bVk, BOOL bOriginal) // bVk is virtual-key code, MSDN said\r
804 {\r
805         if (bOriginal) {\r
806 //              CUtils::Log(_T("i: %x, %d, %d, %d, %d, %d, %d, %d, %d"), bVk,\r
807 //                      IsDown(VK_CONTROL), IsDown(VK_CONTROL, FALSE), IsDepressedModifier(CCommands::C_), IsDepressedModifier(CCommands::C_, FALSE),\r
808 //                      IsDown(VK_MENU), IsDown(VK_MENU, FALSE), IsDepressedModifier(CCommands::MetaAlt), IsDepressedModifier(CCommands::MetaAlt, FALSE));\r
809                 SetOriginal(GetModifierState(), bVk);\r
810         }\r
811         DoKeybd_event(bVk, 0);\r
812 }\r
813 \r
814 void CXkeymacsDll::ReleaseKey(BYTE bVk) // bVk is virtual-key code, MSDN said\r
815 {\r
816         DoKeybd_event(bVk, KEYEVENTF_KEYUP);\r
817 }\r
818 \r
819 void CXkeymacsDll::DoKeybd_event(BYTE bVk, DWORD dwFlags)\r
820 {\r
821         switch (bVk) {\r
822         case VK_CONTROL:\r
823                 if (m_bRightControl)\r
824                         dwFlags |= KEYEVENTF_EXTENDEDKEY;\r
825                 break;\r
826 \r
827         case VK_MENU:\r
828                 if (m_bRightAlt)\r
829                         dwFlags |= KEYEVENTF_EXTENDEDKEY;\r
830                 break;\r
831 \r
832         case VK_SHIFT:\r
833                 if (m_bRightShift)\r
834                         bVk = VK_RSHIFT;\r
835                 break;\r
836         case VK_PAUSE:\r
837                 if (IsDown(VK_CONTROL, FALSE)) // Break\r
838                         dwFlags |= KEYEVENTF_EXTENDEDKEY;\r
839                 break;\r
840         case VK_INSERT:\r
841         case VK_DELETE:\r
842         case VK_HOME:\r
843         case VK_END:\r
844         case VK_NEXT:\r
845         case VK_PRIOR:\r
846         case VK_UP:\r
847         case VK_DOWN:\r
848         case VK_RIGHT:\r
849         case VK_LEFT:\r
850         case VK_NUMLOCK:\r
851         case VK_PRINT:\r
852                 dwFlags |= KEYEVENTF_EXTENDEDKEY;\r
853                 break;\r
854         }\r
855 //      CUtils::Log(_T("b: %x, %x, %x, %#hx, %#hx"), bVk, dwFlags, GetMessageExtraInfo(), GetKeyState(bVk), GetAsyncKeyState(bVk));\r
856         keybd_event(bVk, 0, dwFlags, GetMessageExtraInfo());\r
857 //      CUtils::Log(_T("a: %x, %x, %x, %#hx, %#hx"), bVk, dwFlags, GetMessageExtraInfo(), GetKeyState(bVk), GetAsyncKeyState(bVk));\r
858 }\r
859 \r
860 void CXkeymacsDll::SetOriginal(UINT nType, BYTE bVk)\r
861 {\r
862         m_nOriginal[nType & ~SHIFT][bVk]++;\r
863 }\r
864 \r
865 int CXkeymacsDll::CheckOriginal(UINT nType, BYTE bVk)\r
866 {\r
867         nType &= ~SHIFT;\r
868         if (m_nOriginal[nType][bVk])\r
869                 return m_nOriginal[nType][bVk]--;\r
870         return 0;\r
871 }\r
872 \r
873 UINT CXkeymacsDll::GetModifierState(BOOL bPhysicalKey)\r
874 {\r
875         UINT result = 0;\r
876         if (IsDown(VK_SHIFT, bPhysicalKey))\r
877                 result |= SHIFT;\r
878         if (IsDown(VK_CONTROL, bPhysicalKey))\r
879                 result |= CONTROL;\r
880         if (IsDown(VK_MENU, bPhysicalKey))\r
881                 result |= META;\r
882         return result;\r
883 }\r
884 \r
885 void CXkeymacsDll::SetModifierState(UINT after, UINT before)\r
886 {\r
887         if (after & SHIFT && !(before & SHIFT))\r
888                 DepressKey(VK_SHIFT);\r
889         else if (!(after & SHIFT) && before & SHIFT)\r
890                 ReleaseKey(VK_SHIFT);\r
891 \r
892         if (after & CONTROL && !(before & CONTROL))\r
893                 DepressKey(VK_CONTROL);\r
894         else if (!(after & CONTROL) && before & CONTROL) {\r
895                 ReleaseKey(VK_CONTROL);\r
896                 UpdateKeyboardState(VK_CONTROL, 0);\r
897         }\r
898 \r
899         BOOL bHookApp =\r
900                 CUtils::IsVisualCpp() ||  CUtils::IsVisualStudio() ||\r
901                 CUtils::IsInternetExplorer() || CUtils::IsFirefox() || CUtils::IsChrome();\r
902         if (after & META && !(before & META)) {\r
903                 if (bHookApp)\r
904                         m_nHookAltRelease |= HOOK_ALT_LATER;\r
905                 DepressKey(VK_MENU);\r
906         } else if (!(after & META) && before & META) {\r
907                 if (bHookApp)\r
908                         ++m_nHookAltRelease;\r
909                 ReleaseKey(VK_MENU);\r
910         }\r
911 }\r
912 \r
913 BOOL CXkeymacsDll::UpdateKeyboardState(BYTE bVk, BYTE bState)\r
914 {\r
915         BYTE ks[256] = {'\0'};\r
916         if (!GetKeyboardState(ks))\r
917                 return FALSE;\r
918         ks[bVk] = bState;\r
919         return SetKeyboardState(ks);\r
920 }\r
921 \r
922 BOOL CXkeymacsDll::IsControl()\r
923 {\r
924         return CCommands::bC_() || IsDepressedModifier(CCommands::C_);\r
925 }\r
926 \r
927 BOOL CXkeymacsDll::IsMeta()\r
928 {\r
929         return CCommands::bM_() || IsDepressedModifier(CCommands::MetaAlt);\r
930 }\r
931 \r
932 BOOL CXkeymacsDll::IsDepressedModifier(int (__cdecl *Modifier)(void), BOOL bPhysicalKey)\r
933 {\r
934         BYTE bVk = 0;\r
935         const BYTE *pnID = m_CmdID[NONE];\r
936         do {\r
937                 switch (bVk) {\r
938                 case VK_SHIFT:\r
939                 case VK_CONTROL:\r
940                 case VK_MENU:\r
941                 case 0xf0: // Eisu key. GetAsyncKeyState returns the wrong state of Eisu key.\r
942                         continue;\r
943                 }\r
944                 if (IsDown(bVk, bPhysicalKey) && CmdTable::Command(pnID[bVk]) == Modifier)\r
945                         return TRUE;\r
946         } while (++bVk);\r
947         return FALSE;\r
948 }\r
949 \r
950 BOOL CXkeymacsDll::IsDown(BYTE bVk, BOOL bPhysicalKey)\r
951 {\r
952         return bPhysicalKey ? GetAsyncKeyState(bVk) < 0 : GetKeyState(bVk) < 0;\r
953 }\r
954 \r
955 void CXkeymacsDll::AddKillRing(BOOL bNewData)\r
956 {\r
957         if (m_CurrentConfig->KillRingMax == 0) {\r
958                 return;\r
959         }\r
960 \r
961         CClipboardSnap *pSnap = new CClipboardSnap;\r
962         if( !pSnap ) return;\r
963 \r
964         BOOL bCapture = pSnap->Capture();\r
965         bCapture = pSnap->Capture();    // for "office drawing shape format". Can CClipboardSnap care this problem?\r
966 \r
967         if( bCapture ) {\r
968                 if (bNewData) {\r
969                         m_oKillRing.AddHead(pSnap);\r
970                 } else {\r
971                         if (m_oKillRing.IsEmpty()) {\r
972                                 m_oKillRing.AddHead(pSnap);\r
973                         } else {\r
974                                 CClipboardSnap *pParent;\r
975                                 for (pParent = m_oKillRing.GetHead(); pParent->GetNext(); pParent = pParent->GetNext()) {\r
976                                         ;\r
977                                 }\r
978                                 pParent->SetNext(pSnap);\r
979                         }\r
980                 }\r
981         } else {\r
982                 delete pSnap;\r
983                 pSnap = NULL;\r
984         }\r
985 \r
986         m_nKillRing = 0;\r
987 \r
988         if (m_CurrentConfig->KillRingMax < m_oKillRing.GetCount()) {\r
989                 CClipboardSnap *pSnap = m_oKillRing.GetTail();\r
990                 delete pSnap;\r
991                 pSnap = NULL;\r
992                 m_oKillRing.RemoveTail();\r
993         }\r
994 }\r
995 \r
996 // Return TRUE if there is another data\r
997 // Return FALSE if there is no more data\r
998 CClipboardSnap* CXkeymacsDll::GetKillRing(CClipboardSnap* pSnap, BOOL bForce)\r
999 {\r
1000         if (m_CurrentConfig->KillRingMax == 0) {\r
1001                 return NULL;\r
1002         }\r
1003 \r
1004         if (m_oKillRing.IsEmpty()) {\r
1005                 return NULL;\r
1006         }\r
1007 \r
1008         m_nKillRing %= m_oKillRing.GetCount();\r
1009 \r
1010         if (!bForce) {\r
1011                 CClipboardSnap oCurrentSnap;\r
1012                 oCurrentSnap.Capture();\r
1013 \r
1014                 CClipboardSnap *pKillRing = m_oKillRing.GetAt(m_oKillRing.FindIndex(m_nKillRing));\r
1015                 if (!pKillRing) {\r
1016                         return NULL;\r
1017                 }\r
1018                 for (; pKillRing->GetNext(); pKillRing = pKillRing->GetNext()) {\r
1019                         ;\r
1020                 }\r
1021                 if (*pKillRing != oCurrentSnap) {\r
1022                         return NULL;\r
1023                 }\r
1024         }\r
1025 \r
1026         if (!pSnap) {\r
1027                 pSnap = m_oKillRing.GetAt(m_oKillRing.FindIndex(m_nKillRing));\r
1028         }\r
1029         pSnap->Restore();\r
1030 \r
1031         return pSnap->GetNext();\r
1032 }\r
1033 \r
1034 void CXkeymacsDll::IncreaseKillRingIndex(int nKillRing)\r
1035 {\r
1036         m_nKillRing += nKillRing;\r
1037 }\r
1038 \r
1039 bool CXkeymacsDll::GetEnableCUA()\r
1040 {\r
1041         return m_CurrentConfig->EnableCUA;\r
1042 }\r
1043 \r
1044 bool CXkeymacsDll::Get326Compatible()\r
1045 {\r
1046         return m_CurrentConfig->Is326Compatible;\r
1047 }\r
1048 \r
1049 bool CXkeymacsDll::Is106Keyboard()\r
1050 {\r
1051         return m_Config.Is106Keyboard;\r
1052 }\r
1053 \r
1054 void CXkeymacsDll::SetKbMacro(KbdMacro* kbdMacro)\r
1055 {\r
1056         m_kbdMacro = kbdMacro;\r
1057 }\r
1058 \r
1059 // call an original command which is defined in dot.xkeymacs\r
1060 void CXkeymacsDll::CallFunction(int id)\r
1061 {\r
1062         if (id < 0 || id >= MAX_FUNCTION)\r
1063                 return;\r
1064         BOOL bM_x = FALSE;\r
1065         TCHAR szPath[MAX_PATH] = {'\0'};\r
1066         unsigned int index = 0;\r
1067         BOOL bInitialized = FALSE;\r
1068         UINT before = GetModifierState(FALSE);\r
1069 \r
1070         for (KeyBind *p = m_Config.FuncDefs[id]; p->bVk; ++p) {\r
1071                 int nType = p->nType;\r
1072                 BYTE bVk = p->bVk;\r
1073                 int (*fCommand)() = nType < MAX_COMMAND_TYPE ? CmdTable::Command(m_CmdID[nType][bVk]) : NULL;\r
1074                 if (fCommand) {\r
1075                         if (fCommand == CCommands::ExecuteExtendedCommand)\r
1076                                 bM_x = TRUE;\r
1077                         else if (!bInitialized) {\r
1078                                 SetModifierState(0, before);\r
1079                                 bInitialized = TRUE;\r
1080                         }\r
1081 //                      CUtils::Log("CallFunction: Command Name: %s", Commands[m_CurrentConfig->CmdID[nType][bVk]].szCommandName);\r
1082                         while (fCommand() == GOTO_RECURSIVE)\r
1083                                 ;\r
1084                         continue;\r
1085                 }\r
1086                 if (bM_x) {\r
1087                         if (bVk == VK_RETURN)\r
1088                                 InvokeM_x(szPath);\r
1089                         else if (bVk != 0)\r
1090                                 szPath[index++] = static_cast<TCHAR>(ConvVkey((bVk | (nType << 8)) & 0x7ff /* drop CONTROLX */, 1));\r
1091                         continue;\r
1092                 }\r
1093                 if (!bInitialized) {\r
1094                         SetModifierState(0, before);\r
1095                         bInitialized = TRUE;\r
1096                 }\r
1097                 if (nType & WIN_WIN)\r
1098                         DepressKey(VK_LWIN);\r
1099                 if (nType & WIN_CTRL)\r
1100                         DepressKey(VK_CONTROL);\r
1101                 if (nType & WIN_ALT)\r
1102                         DepressKey(VK_MENU);\r
1103                 if (nType & SHIFT)\r
1104                         DepressKey(VK_SHIFT);\r
1105                 Kdu(bVk);\r
1106                 int nNextType = (p + 1)->nType;\r
1107                 if (nType & SHIFT && !(nNextType & SHIFT))\r
1108                         ReleaseKey(VK_SHIFT);\r
1109                 if (nType & WIN_ALT && !(nNextType & WIN_ALT))\r
1110                         ReleaseKey(VK_MENU);\r
1111                 if (nType & WIN_CTRL && !(nNextType & WIN_CTRL))\r
1112                         ReleaseKey(VK_CONTROL);\r
1113                 if (nType & WIN_WIN && !(nNextType & WIN_WIN))\r
1114                         ReleaseKey(VK_LWIN);\r
1115         }\r
1116 \r
1117         if (bInitialized)\r
1118                 // If this lines is invoked on M-x, a window transition does not work well.\r
1119                 SetModifierState(before, 0);\r
1120         return;\r
1121 }\r
1122 \r
1123 SHORT CXkeymacsDll::ConvVkey(SHORT in, int mode)\r
1124 {\r
1125         HKL h = GetKeyboardLayout(0);\r
1126         if (mode == 0) { // ASCII to VKey and state\r
1127                 SHORT r = VkKeyScanEx(static_cast<TCHAR>(in), h);\r
1128                 if (r < 0) // no key correcpont to the char\r
1129                         return 0;\r
1130                 return r & 0x7ff; // drop state flags of Hankaku and others\r
1131         }\r
1132         // VKey and state to ASCII\r
1133         const BYTE down = 0x80;\r
1134         BYTE state[256] = {0};\r
1135         if (in & (1 << 8))\r
1136                 state[VK_SHIFT] = down;\r
1137         if (in & (2 << 8))\r
1138                 state[VK_CONTROL] = down;\r
1139         if (in & (4 << 8))\r
1140                 state[VK_MENU] = down;\r
1141         UINT vkey = in & 0xff;\r
1142         state[vkey] = down;\r
1143         WORD word = 0;\r
1144         int r = ToAsciiEx(vkey, MapVirtualKeyEx(vkey, MAPVK_VK_TO_VSC, h), state, &word, 0, h);\r
1145         if (r == 0)\r
1146                 return 0;\r
1147         if (r == 1)\r
1148                 return static_cast<SHORT>(word);\r
1149         return static_cast<SHORT>(word >> 8); // drop a dead key\r
1150 }\r
1151 \r
1152 void CXkeymacsDll::SetAccelerate(int nAccelerate)\r
1153 {\r
1154         m_nAccelerate = nAccelerate;\r
1155 }\r
1156 \r
1157 int CXkeymacsDll::GetAccelerate()\r
1158 {\r
1159         return m_nAccelerate;\r
1160 }\r
1161 \r
1162 void CXkeymacsDll::SetKeyboardSpeed(int nKeyboardSpeed)\r
1163 {\r
1164         m_nKeyboardSpeed = nKeyboardSpeed;\r
1165 }\r
1166 \r
1167 unsigned int CXkeymacsDll::GetMaxKeyInterval()\r
1168 {\r
1169         // m_nKeyboardSpeed == 0:       slowest repeat rate; approximately  2 characters per second\r
1170         // m_nKeyboardSpeed == 31:      fastest repeat rate; approximately 30 characters per second\r
1171         // 47 ms is max on my machine w/ KeyboardSpeed 31.\r
1172         // 1000 /  2 + 50 = 550\r
1173         // 1000 / 30 + 50 = 83\r
1174         return (unsigned int) (1000.0 / (2.0 + m_nKeyboardSpeed % 32 * 28.0 / 31.0) + 50.0);\r
1175 }\r
1176 \r
1177 void CXkeymacsDll::SetCursorData(HCURSOR hEnable, HCURSOR hDisableTMP, HCURSOR hDisableWOCQ, HICON hDisable, BOOL bEnable)\r
1178 {\r
1179         m_hCursor[STATUS_ENABLE] = hEnable;\r
1180         m_hCursor[STATUS_DISABLE_TMP] = hDisableTMP;\r
1181         m_hCursor[STATUS_DISABLE_WOCQ] = hDisableWOCQ;\r
1182         m_hCursor[STATUS_DISABLE] = hDisable;\r
1183         m_bCursor = bEnable;\r
1184 }\r
1185 \r
1186 void CXkeymacsDll::DoSetCursor()\r
1187 {\r
1188         if (m_bCursor && m_hCurrentCursor)\r
1189                 ::SetCursor(m_hCurrentCursor);\r
1190 }\r