Home | Info | Community | Development | myReactOS | Contact Us
ReactOS Development > Doxygenkbswitch.c
Go to the documentation of this file.
00001 /* 00002 * PROJECT: Keyboard Layout Switcher 00003 * FILE: base\applications\kbswitch\kbswitch.c 00004 * PURPOSE: Switching Keyboard Layouts 00005 * PROGRAMMERS: Dmitry Chapyshev (dmitry@reactos.org) 00006 * Colin Finck (mail@colinfinck.de) 00007 */ 00008 00009 #include "kbswitch.h" 00010 00011 #define WM_NOTIFYICONMSG (WM_USER + 248) 00012 00013 PROC KbSwitchSetHooks = NULL; 00014 PROC KbSwitchDeleteHooks = NULL; 00015 00016 00017 static BOOL 00018 GetLayoutID(LPTSTR szLayoutNum, LPTSTR szLCID); 00019 00020 static BOOL 00021 GetLayoutName(LPTSTR szLayoutNum, LPTSTR szName); 00022 00023 HINSTANCE hInst; 00024 HANDLE hProcessHeap; 00025 HMODULE hDllLib; 00026 ULONG ulCurrentLayoutNum = 1; 00027 00028 static HICON 00029 CreateTrayIcon(LPTSTR szLCID) 00030 { 00031 LANGID lId; 00032 TCHAR szBuf[3]; 00033 HDC hdc, hdcsrc; 00034 HBITMAP hBitmap, hBmpNew, hBmpOld; 00035 RECT rect; 00036 HFONT hFontOld, hFont = NULL; 00037 ICONINFO IconInfo; 00038 HICON hIcon = NULL; 00039 00040 lId = (LANGID)_tcstoul(szLCID, NULL, 16); 00041 if (GetLocaleInfo(lId, 00042 LOCALE_SISO639LANGNAME, 00043 szBuf, 00044 sizeof(szBuf) / sizeof(TCHAR)) == 0) 00045 { 00046 lstrcpy(szBuf, _T("??\0")); 00047 } 00048 00049 hdcsrc = GetDC(NULL); 00050 hdc = CreateCompatibleDC(hdcsrc); 00051 hBitmap = CreateCompatibleBitmap(hdcsrc, 16, 16); 00052 ReleaseDC(NULL, hdcsrc); 00053 00054 if (hdc && hBitmap) 00055 { 00056 hBmpNew = CreateBitmap(16, 16, 1, 1, NULL); 00057 if (hBmpNew) 00058 { 00059 hBmpOld = SelectObject(hdc, hBitmap); 00060 rect.right = 16; 00061 rect.left = 0; 00062 rect.bottom = 16; 00063 rect.top = 0; 00064 00065 SetBkColor(hdc, GetSysColor(COLOR_HIGHLIGHT)); 00066 SetTextColor(hdc, GetSysColor(COLOR_HIGHLIGHTTEXT)); 00067 00068 ExtTextOut(hdc, rect.left, rect.top, ETO_OPAQUE, &rect, _T(""), 0, NULL); 00069 00070 hFont = CreateFont(-11, 0, 0, 0, FW_NORMAL, FALSE, FALSE, FALSE, ANSI_CHARSET, 00071 OUT_DEFAULT_PRECIS, CLIP_DEFAULT_PRECIS, 00072 DEFAULT_QUALITY, FF_DONTCARE, _T("Tahoma")); 00073 00074 hFontOld = SelectObject(hdc, hFont); 00075 DrawText(hdc, _tcsupr(szBuf), 2, &rect, DT_SINGLELINE|DT_CENTER|DT_VCENTER); 00076 SelectObject(hdc, hBmpNew); 00077 PatBlt(hdc, 0, 0, 16, 16, BLACKNESS); 00078 SelectObject(hdc, hBmpOld); 00079 SelectObject(hdc, hFontOld); 00080 00081 IconInfo.hbmColor = hBitmap; 00082 IconInfo.hbmMask = hBmpNew; 00083 IconInfo.fIcon = TRUE; 00084 00085 hIcon = CreateIconIndirect(&IconInfo); 00086 00087 DeleteObject(hBmpNew); 00088 DeleteObject(hBmpOld); 00089 DeleteObject(hFont); 00090 } 00091 } 00092 00093 DeleteDC(hdc); 00094 DeleteObject(hBitmap); 00095 00096 return hIcon; 00097 } 00098 00099 static VOID 00100 AddTrayIcon(HWND hwnd) 00101 { 00102 NOTIFYICONDATA tnid; 00103 TCHAR szLCID[CCH_LAYOUT_ID + 1]; 00104 TCHAR szName[MAX_PATH]; 00105 00106 GetLayoutID(_T("1"), szLCID); 00107 GetLayoutName(_T("1"), szName); 00108 00109 tnid.cbSize = sizeof(NOTIFYICONDATA); 00110 tnid.hWnd = hwnd; 00111 tnid.uID = 1; 00112 tnid.uFlags = NIF_ICON | NIF_MESSAGE | NIF_TIP; 00113 tnid.uCallbackMessage = WM_NOTIFYICONMSG; 00114 tnid.hIcon = CreateTrayIcon(szLCID); 00115 00116 lstrcpyn(tnid.szTip, szName, sizeof(tnid.szTip)); 00117 00118 Shell_NotifyIcon(NIM_ADD, &tnid); 00119 } 00120 00121 static VOID 00122 DelTrayIcon(HWND hwnd) 00123 { 00124 NOTIFYICONDATA tnid; 00125 00126 tnid.cbSize = sizeof(NOTIFYICONDATA); 00127 tnid.hWnd = hwnd; 00128 tnid.uID = 1; 00129 00130 Shell_NotifyIcon(NIM_DELETE, &tnid); 00131 } 00132 00133 static VOID 00134 UpdateTrayIcon(HWND hwnd, LPTSTR szLCID, LPTSTR szName) 00135 { 00136 NOTIFYICONDATA tnid; 00137 00138 tnid.cbSize = sizeof(NOTIFYICONDATA); 00139 tnid.hWnd = hwnd; 00140 tnid.uID = 1; 00141 tnid.uFlags = NIF_ICON | NIF_MESSAGE | NIF_TIP; 00142 tnid.uCallbackMessage = WM_NOTIFYICONMSG; 00143 tnid.hIcon = CreateTrayIcon(szLCID); 00144 00145 lstrcpyn(tnid.szTip, szName, sizeof(tnid.szTip)); 00146 00147 Shell_NotifyIcon(NIM_MODIFY, &tnid); 00148 } 00149 00150 static BOOL 00151 GetLayoutID(LPTSTR szLayoutNum, LPTSTR szLCID) 00152 { 00153 DWORD dwBufLen; 00154 DWORD dwRes; 00155 HKEY hKey; 00156 TCHAR szTempLCID[CCH_LAYOUT_ID + 1]; 00157 00158 // Get the Layout ID 00159 if (RegOpenKeyEx(HKEY_CURRENT_USER, _T("Keyboard Layout\\Preload"), 0, KEY_QUERY_VALUE, &hKey) == ERROR_SUCCESS) 00160 { 00161 dwBufLen = sizeof(szTempLCID); 00162 dwRes = RegQueryValueEx(hKey, szLayoutNum, NULL, NULL, (LPBYTE)szTempLCID, &dwBufLen); 00163 00164 if (dwRes != ERROR_SUCCESS) 00165 { 00166 RegCloseKey(hKey); 00167 return FALSE; 00168 } 00169 00170 RegCloseKey(hKey); 00171 } 00172 00173 // Look for a substitude of this layout 00174 if (RegOpenKeyEx(HKEY_CURRENT_USER, _T("Keyboard Layout\\Substitutes"), 0, KEY_QUERY_VALUE, &hKey) == ERROR_SUCCESS) 00175 { 00176 dwBufLen = sizeof(szTempLCID); 00177 00178 if (RegQueryValueEx(hKey, szTempLCID, NULL, NULL, (LPBYTE)szLCID, &dwBufLen) != ERROR_SUCCESS) 00179 { 00180 // No substitute found, then use the old LCID 00181 lstrcpy(szLCID, szTempLCID); 00182 } 00183 00184 RegCloseKey(hKey); 00185 } 00186 else 00187 { 00188 // Substitutes key couldn't be opened, so use the old LCID 00189 lstrcpy(szLCID, szTempLCID); 00190 } 00191 00192 return TRUE; 00193 } 00194 00195 VOID 00196 GetLayoutIDByHkl(HKL hKl, LPTSTR szLayoutID) 00197 { 00198 /* 00199 FIXME!!! This way of getting layout ID incorrect! 00200 This will not work correctly for 0001040a, 00010410, etc 00201 */ 00202 wsprintf(szLayoutID, _T("%08x"), LOWORD(hKl)); 00203 } 00204 00205 static BOOL 00206 GetLayoutName(LPTSTR szLayoutNum, LPTSTR szName) 00207 { 00208 HKEY hKey; 00209 DWORD dwBufLen; 00210 TCHAR szBuf[MAX_PATH], szDispName[MAX_PATH], szIndex[MAX_PATH], szPath[MAX_PATH]; 00211 TCHAR szLCID[CCH_LAYOUT_ID + 1]; 00212 HANDLE hLib; 00213 UINT i, j, k; 00214 00215 if(!GetLayoutID(szLayoutNum, szLCID)) 00216 return FALSE; 00217 00218 wsprintf(szBuf, _T("SYSTEM\\CurrentControlSet\\Control\\Keyboard Layouts\\%s"), szLCID); 00219 00220 if (RegOpenKeyEx(HKEY_LOCAL_MACHINE, (LPCTSTR)szBuf, 0, KEY_QUERY_VALUE, &hKey) == ERROR_SUCCESS) 00221 { 00222 dwBufLen = sizeof(szBuf); 00223 00224 if (RegQueryValueEx(hKey, _T("Layout Display Name"), NULL, NULL, (LPBYTE)szDispName, &dwBufLen) == ERROR_SUCCESS) 00225 { 00226 if (szDispName[0] == '@') 00227 { 00228 for (i = 0; i < _tcslen(szDispName); i++) 00229 { 00230 if ((szDispName[i] == ',') && (szDispName[i + 1] == '-')) 00231 { 00232 for (j = i + 2, k = 0; j < _tcslen(szDispName)+1; j++, k++) 00233 { 00234 szIndex[k] = szDispName[j]; 00235 } 00236 szDispName[i - 1] = '\0'; 00237 break; 00238 } 00239 else szDispName[i] = szDispName[i + 1]; 00240 } 00241 00242 if (ExpandEnvironmentStrings(szDispName, szPath, MAX_PATH)) 00243 { 00244 hLib = LoadLibrary(szPath); 00245 if (hLib) 00246 { 00247 if (LoadString(hLib, _ttoi(szIndex), szPath, sizeof(szPath) / sizeof(TCHAR)) != 0) 00248 { 00249 _tcscpy(szName, szPath); 00250 RegCloseKey(hKey); 00251 FreeLibrary(hLib); 00252 return TRUE; 00253 } 00254 FreeLibrary(hLib); 00255 } 00256 } 00257 } 00258 } 00259 00260 dwBufLen = sizeof(szBuf); 00261 00262 if (RegQueryValueEx(hKey, _T("Layout Text"), NULL, NULL, (LPBYTE)szName, &dwBufLen) == ERROR_SUCCESS) 00263 { 00264 RegCloseKey(hKey); 00265 return TRUE; 00266 } 00267 00268 RegCloseKey(hKey); 00269 } 00270 00271 return FALSE; 00272 } 00273 00274 BOOL CALLBACK 00275 EnumWindowsProc(HWND hwnd, LPARAM lParam) 00276 { 00277 PostMessage(hwnd, WM_INPUTLANGCHANGEREQUEST, 0, lParam); 00278 return TRUE; 00279 } 00280 00281 static VOID 00282 ActivateLayout(HWND hwnd, ULONG uLayoutNum) 00283 { 00284 HKL hKl; 00285 TCHAR szLayoutNum[CCH_ULONG_DEC + 1]; 00286 TCHAR szLCID[CCH_LAYOUT_ID + 1]; 00287 TCHAR szLangName[MAX_PATH]; 00288 00289 _ultot(uLayoutNum, szLayoutNum, 10); 00290 GetLayoutID(szLayoutNum, szLCID); 00291 00292 // Switch to the new keyboard layout 00293 GetLocaleInfo((LANGID)_tcstoul(szLCID, NULL, 16), LOCALE_SLANGUAGE, (LPTSTR)szLangName, sizeof(szLangName) / sizeof(TCHAR)); 00294 UpdateTrayIcon(hwnd, szLCID, szLangName); 00295 hKl = LoadKeyboardLayout(szLCID, KLF_ACTIVATE); 00296 00297 EnumWindows(EnumWindowsProc, (LPARAM) hKl); 00298 00299 ulCurrentLayoutNum = uLayoutNum; 00300 } 00301 00302 static HMENU 00303 BuildLeftPopupMenu() 00304 { 00305 HMENU hMenu; 00306 HKEY hKey; 00307 DWORD dwIndex, dwSize; 00308 TCHAR szLayoutNum[CCH_ULONG_DEC + 1]; 00309 TCHAR szName[MAX_PATH]; 00310 00311 hMenu = CreatePopupMenu(); 00312 00313 // Add the keyboard layouts to the popup menu 00314 if (RegOpenKeyEx(HKEY_CURRENT_USER, _T("Keyboard Layout\\Preload"), 0, KEY_QUERY_VALUE, &hKey) == ERROR_SUCCESS) 00315 { 00316 for(dwIndex = 0; ; dwIndex++) 00317 { 00318 dwSize = sizeof(szLayoutNum); 00319 if(RegEnumValue(hKey, dwIndex, szLayoutNum, &dwSize, NULL, NULL, NULL, NULL) != ERROR_SUCCESS) 00320 break; 00321 00322 if(!GetLayoutName(szLayoutNum, szName)) 00323 break; 00324 00325 AppendMenu(hMenu, MF_STRING, _ttoi(szLayoutNum), szName); 00326 } 00327 00328 (void)CheckMenuItem(hMenu, ulCurrentLayoutNum, MF_CHECKED); 00329 00330 RegCloseKey(hKey); 00331 } 00332 00333 return hMenu; 00334 } 00335 00336 BOOL 00337 SetHooks() 00338 { 00339 hDllLib = LoadLibrary(_T("kbsdll.dll")); 00340 if (!hDllLib) return FALSE; 00341 00342 KbSwitchSetHooks = (PROC) GetProcAddress(hDllLib, MAKEINTRESOURCEA(1)); 00343 KbSwitchDeleteHooks = (PROC) GetProcAddress(hDllLib, MAKEINTRESOURCEA(2)); 00344 00345 if ((KbSwitchSetHooks == NULL)||(KbSwitchDeleteHooks == NULL)) 00346 return FALSE; 00347 00348 return KbSwitchSetHooks(); 00349 } 00350 00351 VOID 00352 DeleteHooks() 00353 { 00354 if (KbSwitchDeleteHooks) KbSwitchDeleteHooks(); 00355 if (hDllLib) FreeLibrary(hDllLib); 00356 } 00357 00358 ULONG 00359 GetNextLayout() 00360 { 00361 TCHAR szLayoutNum[3 + 1], szLayoutID[CCH_LAYOUT_ID + 1]; 00362 ULONG Ret = ulCurrentLayoutNum; 00363 00364 _ultot(ulCurrentLayoutNum, szLayoutNum, 10); 00365 if (!GetLayoutID(szLayoutNum, szLayoutID)) 00366 { 00367 return -1; 00368 } 00369 00370 _ultot(Ret + 1, szLayoutNum, 10); 00371 00372 if (GetLayoutID(szLayoutNum, szLayoutID)) 00373 { 00374 return (Ret + 1); 00375 } 00376 else 00377 { 00378 _ultot(Ret - 1, szLayoutNum, 10); 00379 if (GetLayoutID(szLayoutNum, szLayoutID)) 00380 return (Ret - 1); 00381 else 00382 return -1; 00383 } 00384 00385 return -1; 00386 } 00387 00388 LRESULT CALLBACK 00389 WndProc(HWND hwnd, UINT Message, WPARAM wParam, LPARAM lParam) 00390 { 00391 static HMENU hRightPopupMenu; 00392 static TCHAR szLCID[MAX_PATH], szLangName[MAX_PATH]; 00393 00394 switch (Message) 00395 { 00396 case WM_CREATE: 00397 { 00398 SetHooks(); 00399 AddTrayIcon(hwnd); 00400 hRightPopupMenu = GetSubMenu(LoadMenu(hInst, MAKEINTRESOURCE(IDR_POPUP)), 0); 00401 00402 ActivateLayout(hwnd, ulCurrentLayoutNum); 00403 00404 return 0; 00405 } 00406 00407 case WM_LANG_CHANGED: 00408 { 00409 GetLayoutIDByHkl((HKL)lParam, szLCID); 00410 GetLocaleInfo((LANGID)_tcstoul(szLCID, NULL, 16), LOCALE_SLANGUAGE, (LPTSTR)szLangName, sizeof(szLangName) / sizeof(TCHAR)); 00411 UpdateTrayIcon(hwnd, szLCID, szLangName); 00412 00413 return 0; 00414 } 00415 00416 case WM_LOAD_LAYOUT: 00417 { 00418 ActivateLayout(hwnd, GetNextLayout()); 00419 00420 return 0; 00421 } 00422 00423 case WM_WINDOW_ACTIVATE: 00424 { 00425 GetLayoutIDByHkl(GetKeyboardLayout(GetWindowThreadProcessId((HWND)wParam, 0)), szLCID); 00426 GetLocaleInfo((LANGID)_tcstoul(szLCID, NULL, 16), LOCALE_SLANGUAGE, (LPTSTR)szLangName, sizeof(szLangName) / sizeof(TCHAR)); 00427 UpdateTrayIcon(hwnd, szLCID, szLangName); 00428 00429 return 0; 00430 } 00431 00432 case WM_NOTIFYICONMSG: 00433 switch (lParam) 00434 { 00435 case WM_RBUTTONDOWN: 00436 case WM_LBUTTONDOWN: 00437 { 00438 POINT pt; 00439 00440 GetCursorPos(&pt); 00441 SetForegroundWindow(hwnd); 00442 00443 if (lParam == WM_LBUTTONDOWN) 00444 { 00445 HMENU hLeftPopupMenu; 00446 00447 /* Rebuild the left popup menu on every click to take care of keyboard layout changes */ 00448 hLeftPopupMenu = BuildLeftPopupMenu(); 00449 TrackPopupMenu(hLeftPopupMenu, 0, pt.x, pt.y, 0, hwnd, NULL); 00450 DestroyMenu(hLeftPopupMenu); 00451 } 00452 else 00453 { 00454 TrackPopupMenu(hRightPopupMenu, 0, pt.x, pt.y, 0, hwnd, NULL); 00455 } 00456 00457 PostMessage(hwnd, WM_NULL, 0, 0); 00458 00459 return 0; 00460 } 00461 } 00462 break; 00463 00464 case WM_COMMAND: 00465 switch (LOWORD(wParam)) 00466 { 00467 case ID_EXIT: 00468 SendMessage(hwnd, WM_CLOSE, 0, 0); 00469 return 0; 00470 00471 case ID_PREFERENCES: 00472 { 00473 SHELLEXECUTEINFO shInputDll = {0}; 00474 00475 shInputDll.cbSize = sizeof(shInputDll); 00476 shInputDll.hwnd = hwnd; 00477 shInputDll.lpVerb = _T("open"); 00478 shInputDll.lpFile = _T("rundll32.exe"); 00479 shInputDll.lpParameters = _T("shell32.dll,Control_RunDLL input.dll"); 00480 00481 if (!ShellExecuteEx(&shInputDll)) 00482 MessageBox(hwnd, _T("Can't start input.dll"), NULL, MB_OK | MB_ICONERROR); 00483 } 00484 00485 default: 00486 ActivateLayout(hwnd, LOWORD(wParam)); 00487 return 0; 00488 } 00489 break; 00490 00491 case WM_SETTINGCHANGE: 00492 { 00493 if (wParam == SPI_SETDEFAULTINPUTLANG) 00494 { 00495 //FIXME: Should detect default language changes by CPL applet or by other tools and update UI 00496 } 00497 } 00498 break; 00499 00500 case WM_DESTROY: 00501 { 00502 DeleteHooks(); 00503 DestroyMenu(hRightPopupMenu); 00504 DelTrayIcon(hwnd); 00505 PostQuitMessage(0); 00506 00507 return 0; 00508 } 00509 } 00510 00511 return DefWindowProc(hwnd, Message, wParam, lParam); 00512 } 00513 00514 INT WINAPI 00515 _tWinMain(HINSTANCE hInstance, HINSTANCE hPrevInst, LPTSTR lpCmdLine, INT nCmdShow) 00516 { 00517 WNDCLASS WndClass = {0}; 00518 MSG msg; 00519 HANDLE hMutex; 00520 00521 hMutex = CreateMutex(NULL, FALSE, szKbSwitcherName); 00522 if (!hMutex) 00523 return 1; 00524 00525 if (GetLastError() == ERROR_ALREADY_EXISTS) 00526 { 00527 CloseHandle(hMutex); 00528 return 1; 00529 } 00530 00531 hInst = hInstance; 00532 hProcessHeap = GetProcessHeap(); 00533 00534 WndClass.style = 0; 00535 WndClass.lpfnWndProc = (WNDPROC)WndProc; 00536 WndClass.cbClsExtra = 0; 00537 WndClass.cbWndExtra = 0; 00538 WndClass.hInstance = hInstance; 00539 WndClass.hIcon = NULL; 00540 WndClass.hCursor = NULL; 00541 WndClass.hbrBackground = NULL; 00542 WndClass.lpszMenuName = NULL; 00543 WndClass.lpszClassName = szKbSwitcherName; 00544 00545 if (!RegisterClass(&WndClass)) 00546 { 00547 CloseHandle(hMutex); 00548 return 1; 00549 } 00550 00551 CreateWindow(szKbSwitcherName, NULL, 0, 0, 0, 1, 1, HWND_DESKTOP, NULL, hInstance, NULL); 00552 00553 while(GetMessage(&msg,NULL,0,0)) 00554 { 00555 TranslateMessage(&msg); 00556 DispatchMessage(&msg); 00557 } 00558 00559 CloseHandle(hMutex); 00560 00561 return 0; 00562 } Generated on Sun May 27 2012 04:16:45 for ReactOS by
1.7.6.1
|