1 /* 2 * PROJECT: Keyboard Layout Switcher 3 * FILE: base/applications/kbswitch/kbswitch.c 4 * PURPOSE: Switching Keyboard Layouts 5 * PROGRAMMERS: Dmitry Chapyshev (dmitry@reactos.org) 6 * Colin Finck (mail@colinfinck.de) 7 * Katayama Hirofumi MZ (katayama.hirofumi.mz@gmail.com) 8 */ 9 10 #include "kbswitch.h" 11 #include <imm.h> 12 13 /* 14 * This program kbswitch is a mimic of Win2k's internat.exe. 15 * However, there are some differences. 16 * 17 * Comparing with WinNT4 ActivateKeyboardLayout, WinXP ActivateKeyboardLayout has 18 * process boundary, so we cannot activate the IME keyboard layout from the outer process. 19 * It needs special care. 20 * 21 * We use global hook by our kbsdll.dll, to watch the shell and the windows. 22 * 23 * It might not work correctly on Vista+ because keyboard layout change notification 24 * won't be generated in Vista+. 25 */ 26 27 #define WM_NOTIFYICONMSG (WM_USER + 248) 28 29 PKBSWITCHSETHOOKS KbSwitchSetHooks = NULL; 30 PKBSWITCHDELETEHOOKS KbSwitchDeleteHooks = NULL; 31 UINT ShellHookMessage = 0; 32 33 HINSTANCE hInst; 34 HANDLE hProcessHeap; 35 HMODULE g_hHookDLL = NULL; 36 ULONG ulCurrentLayoutNum = 1; 37 HICON g_hTrayIcon = NULL; 38 HWND g_hwndLastActive = NULL; 39 40 static BOOL 41 GetLayoutID(LPCTSTR szLayoutNum, LPTSTR szLCID, SIZE_T LCIDLength) 42 { 43 DWORD dwBufLen, dwRes; 44 HKEY hKey; 45 TCHAR szTempLCID[CCH_LAYOUT_ID + 1]; 46 47 /* Get the Layout ID */ 48 if (RegOpenKeyEx(HKEY_CURRENT_USER, _T("Keyboard Layout\\Preload"), 0, KEY_QUERY_VALUE, 49 &hKey) == ERROR_SUCCESS) 50 { 51 dwBufLen = sizeof(szTempLCID); 52 dwRes = RegQueryValueEx(hKey, szLayoutNum, NULL, NULL, (LPBYTE)szTempLCID, &dwBufLen); 53 if (dwRes != ERROR_SUCCESS) 54 { 55 RegCloseKey(hKey); 56 return FALSE; 57 } 58 59 RegCloseKey(hKey); 60 } 61 62 /* Look for a substitute of this layout */ 63 if (RegOpenKeyEx(HKEY_CURRENT_USER, _T("Keyboard Layout\\Substitutes"), 0, 64 KEY_QUERY_VALUE, &hKey) == ERROR_SUCCESS) 65 { 66 dwBufLen = sizeof(szTempLCID); 67 if (RegQueryValueEx(hKey, szTempLCID, NULL, NULL, (LPBYTE)szLCID, &dwBufLen) != ERROR_SUCCESS) 68 { 69 /* No substitute found, then use the old LCID */ 70 StringCchCopy(szLCID, LCIDLength, szTempLCID); 71 } 72 73 RegCloseKey(hKey); 74 } 75 else 76 { 77 /* Substitutes key couldn't be opened, so use the old LCID */ 78 StringCchCopy(szLCID, LCIDLength, szTempLCID); 79 } 80 81 return TRUE; 82 } 83 84 static BOOL 85 GetSystemLibraryPath(LPTSTR szPath, SIZE_T cchPath, LPCTSTR FileName) 86 { 87 if (!GetSystemDirectory(szPath, cchPath)) 88 return FALSE; 89 90 StringCchCat(szPath, cchPath, TEXT("\\")); 91 StringCchCat(szPath, cchPath, FileName); 92 return TRUE; 93 } 94 95 static BOOL 96 GetLayoutName(LPCTSTR szLayoutNum, LPTSTR szName, SIZE_T NameLength) 97 { 98 HKEY hKey; 99 DWORD dwBufLen; 100 TCHAR szBuf[MAX_PATH], szDispName[MAX_PATH], szIndex[MAX_PATH], szPath[MAX_PATH]; 101 TCHAR szLCID[CCH_LAYOUT_ID + 1]; 102 HANDLE hLib; 103 UINT i, j, k; 104 105 if (!GetLayoutID(szLayoutNum, szLCID, ARRAYSIZE(szLCID))) 106 return FALSE; 107 108 StringCchPrintf(szBuf, ARRAYSIZE(szBuf), 109 _T("SYSTEM\\CurrentControlSet\\Control\\Keyboard Layouts\\%s"), szLCID); 110 111 if (RegOpenKeyEx(HKEY_LOCAL_MACHINE, szBuf, 0, KEY_QUERY_VALUE, &hKey) != ERROR_SUCCESS) 112 { 113 return FALSE; 114 } 115 116 /* Use "Layout Display Name" value as an entry name if possible */ 117 dwBufLen = sizeof(szDispName); 118 if (RegQueryValueEx(hKey, _T("Layout Display Name"), NULL, NULL, 119 (LPBYTE)szDispName, &dwBufLen) == ERROR_SUCCESS) 120 { 121 /* FIXME: Use shlwapi!SHLoadRegUIStringW instead if it was implemented */ 122 if (szDispName[0] == '@') 123 { 124 size_t len = _tcslen(szDispName); 125 126 for (i = 0; i < len; i++) 127 { 128 if ((szDispName[i] == ',') && (szDispName[i + 1] == '-')) 129 { 130 for (j = i + 2, k = 0; j < _tcslen(szDispName)+1; j++, k++) 131 { 132 szIndex[k] = szDispName[j]; 133 } 134 szDispName[i - 1] = '\0'; 135 break; 136 } 137 else szDispName[i] = szDispName[i + 1]; 138 } 139 140 if (ExpandEnvironmentStrings(szDispName, szPath, ARRAYSIZE(szPath))) 141 { 142 hLib = LoadLibrary(szPath); 143 if (hLib) 144 { 145 if (LoadString(hLib, _ttoi(szIndex), szPath, ARRAYSIZE(szPath))) 146 { 147 StringCchCopy(szName, NameLength, szPath); 148 RegCloseKey(hKey); 149 FreeLibrary(hLib); 150 return TRUE; 151 } 152 FreeLibrary(hLib); 153 } 154 } 155 } 156 } 157 158 /* Otherwise, use "Layout Text" value as an entry name */ 159 dwBufLen = NameLength * sizeof(TCHAR); 160 if (RegQueryValueEx(hKey, _T("Layout Text"), NULL, NULL, 161 (LPBYTE)szName, &dwBufLen) != ERROR_SUCCESS) 162 { 163 RegCloseKey(hKey); 164 return FALSE; 165 } 166 167 RegCloseKey(hKey); 168 return TRUE; 169 } 170 171 static BOOL GetImeFile(LPTSTR szImeFile, SIZE_T cchImeFile, LPCTSTR szLCID) 172 { 173 HKEY hKey; 174 DWORD dwBufLen; 175 TCHAR szBuf[MAX_PATH]; 176 177 szImeFile[0] = UNICODE_NULL; 178 179 if (_tcslen(szLCID) != CCH_LAYOUT_ID) 180 return FALSE; /* Invalid LCID */ 181 182 if (szLCID[0] != TEXT('E') && szLCID[0] != TEXT('e')) 183 return FALSE; /* Not an IME HKL */ 184 185 StringCchPrintf(szBuf, ARRAYSIZE(szBuf), 186 _T("SYSTEM\\CurrentControlSet\\Control\\Keyboard Layouts\\%s"), szLCID); 187 188 if (RegOpenKeyEx(HKEY_LOCAL_MACHINE, szBuf, 0, KEY_QUERY_VALUE, &hKey) != ERROR_SUCCESS) 189 { 190 return FALSE; 191 } 192 193 dwBufLen = cchImeFile * sizeof(TCHAR); 194 if (RegQueryValueEx(hKey, _T("IME File"), NULL, NULL, 195 (LPBYTE)szImeFile, &dwBufLen) != ERROR_SUCCESS) 196 { 197 szImeFile[0] = UNICODE_NULL; 198 } 199 200 RegCloseKey(hKey); 201 202 return (szImeFile[0] != UNICODE_NULL); 203 } 204 205 typedef struct tagLOAD_ICON 206 { 207 INT cxIcon, cyIcon; 208 HICON hIcon; 209 } LOAD_ICON, *PLOAD_ICON; 210 211 static BOOL CALLBACK 212 EnumResNameProc( 213 HMODULE hModule, 214 LPCTSTR lpszType, 215 LPTSTR lpszName, 216 LPARAM lParam) 217 { 218 PLOAD_ICON pLoadIcon = (PLOAD_ICON)lParam; 219 pLoadIcon->hIcon = (HICON)LoadImage(hModule, lpszName, IMAGE_ICON, 220 pLoadIcon->cxIcon, pLoadIcon->cyIcon, 221 LR_DEFAULTCOLOR); 222 if (pLoadIcon->hIcon) 223 return FALSE; /* Stop enumeration */ 224 return TRUE; 225 } 226 227 static HICON FakeExtractIcon(LPCTSTR szIconPath, INT cxIcon, INT cyIcon) 228 { 229 LOAD_ICON LoadIcon = { cxIcon, cyIcon, NULL }; 230 HMODULE hImeDLL = LoadLibraryEx(szIconPath, NULL, LOAD_LIBRARY_AS_DATAFILE); 231 if (hImeDLL) 232 { 233 EnumResourceNames(hImeDLL, RT_GROUP_ICON, EnumResNameProc, (LPARAM)&LoadIcon); 234 FreeLibrary(hImeDLL); 235 } 236 return LoadIcon.hIcon; 237 } 238 239 static HICON 240 CreateTrayIcon(LPTSTR szLCID, LPCTSTR szImeFile OPTIONAL) 241 { 242 LANGID LangID; 243 TCHAR szBuf[4]; 244 HDC hdcScreen, hdc; 245 HBITMAP hbmColor, hbmMono, hBmpOld; 246 HFONT hFont, hFontOld; 247 LOGFONT lf; 248 RECT rect; 249 ICONINFO IconInfo; 250 HICON hIcon; 251 INT cxIcon = GetSystemMetrics(SM_CXSMICON); 252 INT cyIcon = GetSystemMetrics(SM_CYSMICON); 253 TCHAR szPath[MAX_PATH]; 254 255 if (szImeFile && szImeFile[0]) 256 { 257 if (GetSystemLibraryPath(szPath, ARRAYSIZE(szPath), szImeFile)) 258 return FakeExtractIcon(szPath, cxIcon, cyIcon); 259 } 260 261 /* Getting "EN", "FR", etc. from English, French, ... */ 262 LangID = LANGIDFROMLCID(_tcstoul(szLCID, NULL, 16)); 263 if (GetLocaleInfo(LangID, 264 LOCALE_SABBREVLANGNAME | LOCALE_NOUSEROVERRIDE, 265 szBuf, 266 ARRAYSIZE(szBuf)) == 0) 267 { 268 szBuf[0] = szBuf[1] = _T('?'); 269 } 270 szBuf[2] = 0; /* Truncate the identifier to two characters: "ENG" --> "EN" etc. */ 271 272 /* Create hdc, hbmColor and hbmMono */ 273 hdcScreen = GetDC(NULL); 274 hdc = CreateCompatibleDC(hdcScreen); 275 hbmColor = CreateCompatibleBitmap(hdcScreen, cxIcon, cyIcon); 276 ReleaseDC(NULL, hdcScreen); 277 hbmMono = CreateBitmap(cxIcon, cyIcon, 1, 1, NULL); 278 279 /* Checking NULL */ 280 if (!hdc || !hbmColor || !hbmMono) 281 { 282 if (hbmMono) 283 DeleteObject(hbmMono); 284 if (hbmColor) 285 DeleteObject(hbmColor); 286 if (hdc) 287 DeleteDC(hdc); 288 return NULL; 289 } 290 291 /* Create a font */ 292 hFont = NULL; 293 if (SystemParametersInfo(SPI_GETICONTITLELOGFONT, sizeof(lf), &lf, 0)) 294 { 295 /* Override the current size with something manageable */ 296 lf.lfHeight = -11; 297 lf.lfWidth = 0; 298 hFont = CreateFontIndirect(&lf); 299 } 300 if (!hFont) 301 hFont = (HFONT)GetStockObject(DEFAULT_GUI_FONT); 302 303 SetRect(&rect, 0, 0, cxIcon, cyIcon); 304 305 /* Draw hbmColor */ 306 hBmpOld = SelectObject(hdc, hbmColor); 307 SetDCBrushColor(hdc, GetSysColor(COLOR_HIGHLIGHT)); 308 FillRect(hdc, &rect, (HBRUSH)GetStockObject(DC_BRUSH)); 309 hFontOld = SelectObject(hdc, hFont); 310 SetTextColor(hdc, GetSysColor(COLOR_HIGHLIGHTTEXT)); 311 SetBkMode(hdc, TRANSPARENT); 312 DrawText(hdc, szBuf, 2, &rect, DT_SINGLELINE | DT_CENTER | DT_VCENTER); 313 SelectObject(hdc, hFontOld); 314 315 /* Fill hbmMono with black */ 316 SelectObject(hdc, hbmMono); 317 PatBlt(hdc, 0, 0, cxIcon, cyIcon, BLACKNESS); 318 SelectObject(hdc, hBmpOld); 319 320 /* Create an icon from hbmColor and hbmMono */ 321 IconInfo.fIcon = TRUE; 322 IconInfo.xHotspot = IconInfo.yHotspot = 0; 323 IconInfo.hbmColor = hbmColor; 324 IconInfo.hbmMask = hbmMono; 325 hIcon = CreateIconIndirect(&IconInfo); 326 327 /* Clean up */ 328 DeleteObject(hFont); 329 DeleteObject(hbmMono); 330 DeleteObject(hbmColor); 331 DeleteDC(hdc); 332 333 return hIcon; 334 } 335 336 static VOID 337 AddTrayIcon(HWND hwnd) 338 { 339 NOTIFYICONDATA tnid = { sizeof(tnid), hwnd, 1, NIF_ICON | NIF_MESSAGE | NIF_TIP }; 340 TCHAR szLCID[CCH_LAYOUT_ID + 1], szName[MAX_PATH]; 341 TCHAR szImeFile[80]; 342 343 GetLayoutID(_T("1"), szLCID, ARRAYSIZE(szLCID)); 344 GetLayoutName(_T("1"), szName, ARRAYSIZE(szName)); 345 GetImeFile(szImeFile, ARRAYSIZE(szImeFile), szLCID); 346 347 tnid.uCallbackMessage = WM_NOTIFYICONMSG; 348 tnid.hIcon = CreateTrayIcon(szLCID, szImeFile); 349 StringCchCopy(tnid.szTip, ARRAYSIZE(tnid.szTip), szName); 350 351 Shell_NotifyIcon(NIM_ADD, &tnid); 352 353 if (g_hTrayIcon) 354 DestroyIcon(g_hTrayIcon); 355 g_hTrayIcon = tnid.hIcon; 356 } 357 358 static VOID 359 DeleteTrayIcon(HWND hwnd) 360 { 361 NOTIFYICONDATA tnid = { sizeof(tnid), hwnd, 1 }; 362 Shell_NotifyIcon(NIM_DELETE, &tnid); 363 364 if (g_hTrayIcon) 365 { 366 DestroyIcon(g_hTrayIcon); 367 g_hTrayIcon = NULL; 368 } 369 } 370 371 static VOID 372 UpdateTrayIcon(HWND hwnd, LPTSTR szLCID, LPTSTR szName) 373 { 374 NOTIFYICONDATA tnid = { sizeof(tnid), hwnd, 1, NIF_ICON | NIF_MESSAGE | NIF_TIP }; 375 TCHAR szImeFile[80]; 376 377 GetImeFile(szImeFile, ARRAYSIZE(szImeFile), szLCID); 378 379 tnid.uCallbackMessage = WM_NOTIFYICONMSG; 380 tnid.hIcon = CreateTrayIcon(szLCID, szImeFile); 381 StringCchCopy(tnid.szTip, ARRAYSIZE(tnid.szTip), szName); 382 383 Shell_NotifyIcon(NIM_MODIFY, &tnid); 384 385 if (g_hTrayIcon) 386 DestroyIcon(g_hTrayIcon); 387 g_hTrayIcon = tnid.hIcon; 388 } 389 390 static VOID 391 GetLayoutIDByHkl(HKL hKl, LPTSTR szLayoutID, SIZE_T LayoutIDLength) 392 { 393 StringCchPrintf(szLayoutID, LayoutIDLength, _T("%08lx"), (DWORD)(DWORD_PTR)(hKl)); 394 } 395 396 static BOOL CALLBACK 397 EnumWindowsProc(HWND hwnd, LPARAM lParam) 398 { 399 PostMessage(hwnd, WM_INPUTLANGCHANGEREQUEST, INPUTLANGCHANGE_SYSCHARSET, lParam); 400 return TRUE; 401 } 402 403 static VOID 404 ActivateLayout(HWND hwnd, ULONG uLayoutNum, HWND hwndTarget OPTIONAL, BOOL bNoActivate) 405 { 406 HKL hKl; 407 TCHAR szLayoutNum[CCH_ULONG_DEC + 1], szLCID[CCH_LAYOUT_ID + 1], szLangName[MAX_PATH]; 408 LANGID LangID; 409 410 /* The layout number starts from one. Zero is invalid */ 411 if (uLayoutNum == 0 || uLayoutNum > 0xFF) /* Invalid */ 412 return; 413 414 _ultot(uLayoutNum, szLayoutNum, 10); 415 GetLayoutID(szLayoutNum, szLCID, ARRAYSIZE(szLCID)); 416 LangID = (LANGID)_tcstoul(szLCID, NULL, 16); 417 418 /* Switch to the new keyboard layout */ 419 GetLocaleInfo(LangID, LOCALE_SLANGUAGE, szLangName, ARRAYSIZE(szLangName)); 420 UpdateTrayIcon(hwnd, szLCID, szLangName); 421 422 if (hwndTarget && !bNoActivate) 423 SetForegroundWindow(hwndTarget); 424 425 hKl = LoadKeyboardLayout(szLCID, KLF_ACTIVATE); 426 if (hKl) 427 ActivateKeyboardLayout(hKl, KLF_SETFORPROCESS); 428 429 /* Post WM_INPUTLANGCHANGEREQUEST */ 430 if (hwndTarget) 431 { 432 PostMessage(hwndTarget, WM_INPUTLANGCHANGEREQUEST, 433 INPUTLANGCHANGE_SYSCHARSET, (LPARAM)hKl); 434 } 435 else 436 { 437 EnumWindows(EnumWindowsProc, (LPARAM) hKl); 438 } 439 440 ulCurrentLayoutNum = uLayoutNum; 441 } 442 443 static HMENU 444 BuildLeftPopupMenu(VOID) 445 { 446 HMENU hMenu = CreatePopupMenu(); 447 HKEY hKey; 448 DWORD dwIndex, dwSize; 449 TCHAR szLayoutNum[CCH_ULONG_DEC + 1], szName[MAX_PATH]; 450 451 /* Add the keyboard layouts to the popup menu */ 452 if (RegOpenKeyEx(HKEY_CURRENT_USER, _T("Keyboard Layout\\Preload"), 0, 453 KEY_QUERY_VALUE, &hKey) == ERROR_SUCCESS) 454 { 455 for (dwIndex = 0; ; dwIndex++) 456 { 457 dwSize = sizeof(szLayoutNum); 458 if (RegEnumValue(hKey, dwIndex, szLayoutNum, &dwSize, NULL, NULL, 459 NULL, NULL) != ERROR_SUCCESS) 460 { 461 break; 462 } 463 464 if (!GetLayoutName(szLayoutNum, szName, ARRAYSIZE(szName))) 465 continue; 466 467 AppendMenu(hMenu, MF_STRING, _ttoi(szLayoutNum), szName); 468 } 469 470 CheckMenuItem(hMenu, ulCurrentLayoutNum, MF_CHECKED); 471 472 RegCloseKey(hKey); 473 } 474 475 return hMenu; 476 } 477 478 static ULONG 479 GetMaxLayoutNum(VOID) 480 { 481 HKEY hKey; 482 ULONG dwIndex, dwSize, uLayoutNum, uMaxLayoutNum = 0; 483 TCHAR szLayoutNum[CCH_ULONG_DEC + 1], szLayoutID[CCH_LAYOUT_ID + 1]; 484 485 /* Get the maximum layout number in the Preload key */ 486 if (RegOpenKeyEx(HKEY_CURRENT_USER, _T("Keyboard Layout\\Preload"), 0, 487 KEY_QUERY_VALUE, &hKey) == ERROR_SUCCESS) 488 { 489 for (dwIndex = 0; ; dwIndex++) 490 { 491 dwSize = sizeof(szLayoutNum); 492 if (RegEnumValue(hKey, dwIndex, szLayoutNum, &dwSize, NULL, NULL, 493 NULL, NULL) != ERROR_SUCCESS) 494 { 495 break; 496 } 497 498 if (GetLayoutID(szLayoutNum, szLayoutID, ARRAYSIZE(szLayoutID))) 499 { 500 uLayoutNum = _ttoi(szLayoutNum); 501 if (uMaxLayoutNum < uLayoutNum) 502 uMaxLayoutNum = uLayoutNum; 503 } 504 } 505 506 RegCloseKey(hKey); 507 } 508 509 return uMaxLayoutNum; 510 } 511 512 BOOL 513 SetHooks(VOID) 514 { 515 g_hHookDLL = LoadLibrary(_T("kbsdll.dll")); 516 if (!g_hHookDLL) 517 { 518 return FALSE; 519 } 520 521 KbSwitchSetHooks = (PKBSWITCHSETHOOKS) GetProcAddress(g_hHookDLL, "KbSwitchSetHooks"); 522 KbSwitchDeleteHooks = (PKBSWITCHDELETEHOOKS) GetProcAddress(g_hHookDLL, "KbSwitchDeleteHooks"); 523 524 if (KbSwitchSetHooks == NULL || KbSwitchDeleteHooks == NULL) 525 { 526 return FALSE; 527 } 528 529 return KbSwitchSetHooks(); 530 } 531 532 VOID 533 DeleteHooks(VOID) 534 { 535 if (KbSwitchDeleteHooks) 536 { 537 KbSwitchDeleteHooks(); 538 KbSwitchDeleteHooks = NULL; 539 } 540 if (g_hHookDLL) 541 { 542 FreeLibrary(g_hHookDLL); 543 g_hHookDLL = NULL; 544 } 545 } 546 547 ULONG 548 GetNextLayout(VOID) 549 { 550 TCHAR szLayoutNum[3 + 1], szLayoutID[CCH_LAYOUT_ID + 1]; 551 ULONG uLayoutNum, uMaxNum = GetMaxLayoutNum(); 552 553 for (uLayoutNum = ulCurrentLayoutNum + 1; ; ++uLayoutNum) 554 { 555 if (uLayoutNum > uMaxNum) 556 uLayoutNum = 1; 557 if (uLayoutNum == ulCurrentLayoutNum) 558 break; 559 560 _ultot(uLayoutNum, szLayoutNum, 10); 561 if (GetLayoutID(szLayoutNum, szLayoutID, ARRAYSIZE(szLayoutID))) 562 return uLayoutNum; 563 } 564 565 return ulCurrentLayoutNum; 566 } 567 568 UINT 569 UpdateLanguageDisplay(HWND hwnd, HKL hKl) 570 { 571 TCHAR szLCID[MAX_PATH], szLangName[MAX_PATH]; 572 LANGID LangID; 573 574 GetLayoutIDByHkl(hKl, szLCID, ARRAYSIZE(szLCID)); 575 LangID = (LANGID)_tcstoul(szLCID, NULL, 16); 576 GetLocaleInfo(LangID, LOCALE_SLANGUAGE, szLangName, ARRAYSIZE(szLangName)); 577 UpdateTrayIcon(hwnd, szLCID, szLangName); 578 579 return 0; 580 } 581 582 HWND 583 GetTargetWindow(HWND hwndFore) 584 { 585 TCHAR szClass[64]; 586 HWND hwndIME; 587 HWND hwndTarget = hwndFore; 588 if (hwndTarget == NULL) 589 hwndTarget = GetForegroundWindow(); 590 591 GetClassName(hwndTarget, szClass, ARRAYSIZE(szClass)); 592 if (_tcsicmp(szClass, szKbSwitcherName) == 0) 593 hwndTarget = g_hwndLastActive; 594 595 hwndIME = ImmGetDefaultIMEWnd(hwndTarget); 596 return (hwndIME ? hwndIME : hwndTarget); 597 } 598 599 UINT 600 UpdateLanguageDisplayCurrent(HWND hwnd, HWND hwndFore) 601 { 602 DWORD dwThreadID = GetWindowThreadProcessId(GetTargetWindow(hwndFore), NULL); 603 HKL hKL = GetKeyboardLayout(dwThreadID); 604 UpdateLanguageDisplay(hwnd, hKL); 605 606 if (IsWindow(g_hwndLastActive)) 607 SetForegroundWindow(g_hwndLastActive); 608 609 return 0; 610 } 611 612 static UINT GetCurLayoutNum(HKL hKL) 613 { 614 UINT i, nCount; 615 HKL ahKL[256]; 616 617 nCount = GetKeyboardLayoutList(ARRAYSIZE(ahKL), ahKL); 618 for (i = 0; i < nCount; ++i) 619 { 620 if (ahKL[i] == hKL) 621 return i + 1; 622 } 623 624 return 0; 625 } 626 627 static BOOL RememberLastActive(HWND hwnd, HWND hwndFore) 628 { 629 TCHAR szClass[64]; 630 631 hwndFore = GetAncestor(hwndFore, GA_ROOT); 632 633 if (!IsWindowVisible(hwndFore) || !GetClassName(hwndFore, szClass, ARRAYSIZE(szClass))) 634 return FALSE; 635 636 if (_tcsicmp(szClass, szKbSwitcherName) == 0 || 637 _tcsicmp(szClass, TEXT("Shell_TrayWnd")) == 0) 638 { 639 return FALSE; /* Special window */ 640 } 641 642 /* FIXME: CONWND is multithreaded but KLF_SETFORPROCESS and 643 DefWindowProc.WM_INPUTLANGCHANGEREQUEST won't work yet */ 644 if (_tcsicmp(szClass, TEXT("ConsoleWindowClass")) == 0) 645 { 646 HKL hKL = GetKeyboardLayout(0); 647 UpdateLanguageDisplay(hwnd, hKL); 648 } 649 650 g_hwndLastActive = hwndFore; 651 return TRUE; 652 } 653 654 LRESULT CALLBACK 655 WndProc(HWND hwnd, UINT Message, WPARAM wParam, LPARAM lParam) 656 { 657 static HMENU s_hMenu = NULL, s_hRightPopupMenu = NULL; 658 static UINT s_uTaskbarRestart; 659 POINT pt; 660 HMENU hLeftPopupMenu; 661 662 switch (Message) 663 { 664 case WM_CREATE: 665 { 666 if (!SetHooks()) 667 { 668 MessageBox(NULL, TEXT("SetHooks failed."), NULL, MB_ICONERROR); 669 return -1; 670 } 671 672 AddTrayIcon(hwnd); 673 674 ActivateLayout(hwnd, ulCurrentLayoutNum, NULL, FALSE); 675 s_uTaskbarRestart = RegisterWindowMessage(TEXT("TaskbarCreated")); 676 break; 677 } 678 679 case WM_LANG_CHANGED: /* Comes from kbsdll.dll and this module */ 680 { 681 UpdateLanguageDisplay(hwnd, (HKL)lParam); 682 break; 683 } 684 685 case WM_WINDOW_ACTIVATE: /* Comes from kbsdll.dll and this module */ 686 { 687 HWND hwndFore = GetForegroundWindow(); 688 if (RememberLastActive(hwnd, hwndFore)) 689 return UpdateLanguageDisplayCurrent(hwnd, hwndFore); 690 break; 691 } 692 693 case WM_NOTIFYICONMSG: 694 { 695 switch (lParam) 696 { 697 case WM_RBUTTONUP: 698 case WM_LBUTTONUP: 699 { 700 GetCursorPos(&pt); 701 SetForegroundWindow(hwnd); 702 703 if (lParam == WM_LBUTTONUP) 704 { 705 /* Rebuild the left popup menu on every click to take care of keyboard layout changes */ 706 hLeftPopupMenu = BuildLeftPopupMenu(); 707 TrackPopupMenu(hLeftPopupMenu, 0, pt.x, pt.y, 0, hwnd, NULL); 708 DestroyMenu(hLeftPopupMenu); 709 } 710 else 711 { 712 if (!s_hRightPopupMenu) 713 { 714 s_hMenu = LoadMenu(hInst, MAKEINTRESOURCE(IDR_POPUP)); 715 s_hRightPopupMenu = GetSubMenu(s_hMenu, 0); 716 } 717 TrackPopupMenu(s_hRightPopupMenu, 0, pt.x, pt.y, 0, hwnd, NULL); 718 } 719 720 PostMessage(hwnd, WM_NULL, 0, 0); 721 break; 722 } 723 } 724 break; 725 } 726 727 case WM_COMMAND: 728 switch (LOWORD(wParam)) 729 { 730 case ID_EXIT: 731 { 732 PostMessage(hwnd, WM_CLOSE, 0, 0); 733 break; 734 } 735 736 case ID_PREFERENCES: 737 { 738 INT_PTR ret = (INT_PTR)ShellExecute(hwnd, NULL, 739 TEXT("control.exe"), TEXT("input.dll"), 740 NULL, SW_SHOWNORMAL); 741 if (ret <= 32) 742 MessageBox(hwnd, _T("Can't start input.dll"), NULL, MB_ICONERROR); 743 break; 744 } 745 746 case ID_NEXTLAYOUT: 747 { 748 HWND hwndTarget = (HWND)lParam, hwndTargetSave = NULL; 749 DWORD dwThreadID; 750 HKL hKL; 751 UINT uNum; 752 TCHAR szClass[64]; 753 BOOL bCONWND = FALSE; 754 755 if (hwndTarget == NULL) 756 hwndTarget = g_hwndLastActive; 757 758 /* FIXME: CONWND is multithreaded but KLF_SETFORPROCESS and 759 DefWindowProc.WM_INPUTLANGCHANGEREQUEST won't work yet */ 760 if (hwndTarget && 761 GetClassName(hwndTarget, szClass, ARRAYSIZE(szClass)) && 762 _tcsicmp(szClass, TEXT("ConsoleWindowClass")) == 0) 763 { 764 bCONWND = TRUE; 765 hwndTargetSave = hwndTarget; 766 hwndTarget = NULL; 767 } 768 769 if (hwndTarget) 770 { 771 dwThreadID = GetWindowThreadProcessId(hwndTarget, NULL); 772 hKL = GetKeyboardLayout(dwThreadID); 773 uNum = GetCurLayoutNum(hKL); 774 if (uNum != 0) 775 ulCurrentLayoutNum = uNum; 776 } 777 778 ActivateLayout(hwnd, GetNextLayout(), hwndTarget, TRUE); 779 780 /* FIXME: CONWND is multithreaded but KLF_SETFORPROCESS and 781 DefWindowProc.WM_INPUTLANGCHANGEREQUEST won't work yet */ 782 if (bCONWND) 783 { 784 ActivateLayout(hwnd, ulCurrentLayoutNum, hwndTargetSave, TRUE); 785 } 786 break; 787 } 788 789 default: 790 { 791 if (1 <= LOWORD(wParam) && LOWORD(wParam) <= 1000) 792 { 793 if (!IsWindow(g_hwndLastActive)) 794 { 795 g_hwndLastActive = NULL; 796 } 797 ActivateLayout(hwnd, LOWORD(wParam), g_hwndLastActive, FALSE); 798 } 799 break; 800 } 801 } 802 break; 803 804 case WM_SETTINGCHANGE: 805 { 806 if (wParam == SPI_SETNONCLIENTMETRICS) 807 { 808 PostMessage(hwnd, WM_WINDOW_ACTIVATE, wParam, lParam); 809 break; 810 } 811 } 812 break; 813 814 case WM_DESTROY: 815 { 816 DeleteHooks(); 817 DestroyMenu(s_hMenu); 818 DeleteTrayIcon(hwnd); 819 PostQuitMessage(0); 820 break; 821 } 822 823 default: 824 { 825 if (Message == s_uTaskbarRestart) 826 { 827 AddTrayIcon(hwnd); 828 break; 829 } 830 else if (Message == ShellHookMessage) 831 { 832 if (wParam == HSHELL_LANGUAGE) 833 PostMessage(hwnd, WM_LANG_CHANGED, wParam, lParam); 834 else if (wParam == HSHELL_WINDOWACTIVATED) 835 PostMessage(hwnd, WM_WINDOW_ACTIVATE, wParam, lParam); 836 837 break; 838 } 839 return DefWindowProc(hwnd, Message, wParam, lParam); 840 } 841 } 842 843 return 0; 844 } 845 846 INT WINAPI 847 _tWinMain(HINSTANCE hInstance, HINSTANCE hPrevInst, LPTSTR lpCmdLine, INT nCmdShow) 848 { 849 WNDCLASS WndClass; 850 MSG msg; 851 HANDLE hMutex; 852 HWND hwnd; 853 854 switch (GetUserDefaultUILanguage()) 855 { 856 case MAKELANGID(LANG_HEBREW, SUBLANG_DEFAULT): 857 SetProcessDefaultLayout(LAYOUT_RTL); 858 break; 859 default: 860 break; 861 } 862 863 hMutex = CreateMutex(NULL, FALSE, szKbSwitcherName); 864 if (!hMutex) 865 return 1; 866 867 if (GetLastError() == ERROR_ALREADY_EXISTS) 868 { 869 CloseHandle(hMutex); 870 return 1; 871 } 872 873 hInst = hInstance; 874 hProcessHeap = GetProcessHeap(); 875 876 ZeroMemory(&WndClass, sizeof(WndClass)); 877 WndClass.lpfnWndProc = WndProc; 878 WndClass.hInstance = hInstance; 879 WndClass.hCursor = LoadCursor(NULL, IDC_ARROW); 880 WndClass.lpszClassName = szKbSwitcherName; 881 if (!RegisterClass(&WndClass)) 882 { 883 CloseHandle(hMutex); 884 return 1; 885 } 886 887 hwnd = CreateWindow(szKbSwitcherName, NULL, 0, 0, 0, 1, 1, HWND_DESKTOP, NULL, hInstance, NULL); 888 ShellHookMessage = RegisterWindowMessage(L"SHELLHOOK"); 889 RegisterShellHookWindow(hwnd); 890 891 while (GetMessage(&msg, NULL, 0, 0)) 892 { 893 TranslateMessage(&msg); 894 DispatchMessage(&msg); 895 } 896 897 CloseHandle(hMutex); 898 return 0; 899 } 900