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)
11 #define WM_NOTIFYICONMSG (WM_USER + 248)
13 PROC KbSwitchSetHooks
= NULL
;
14 PROC KbSwitchDeleteHooks
= NULL
;
18 GetLayoutID(LPTSTR szLayoutNum
, LPTSTR szLCID
);
21 GetLayoutName(LPTSTR szLayoutNum
, LPTSTR szName
);
26 ULONG ulCurrentLayoutNum
= 1;
27 UINT cxSmIcon
, cySmIcon
;
31 CreateTrayIcon(LPTSTR szLCID
)
36 HBITMAP hBitmap
, hBmpNew
, hBmpOld
;
38 DWORD bkColor
, bkText
;
39 HFONT hFontOld
, hFont
= NULL
;
43 lId
= (LANGID
)_tcstoul(szLCID
, NULL
, 16);
44 if (GetLocaleInfo(lId
,
45 LOCALE_SISO639LANGNAME
,
47 sizeof(szBuf
) / sizeof(TCHAR
)) == 0)
49 lstrcpy(szBuf
, _T("??\0"));
53 hdc
= CreateCompatibleDC(hdcsrc
);
54 hBitmap
= CreateCompatibleBitmap(hdcsrc
, cxSmIcon
, cySmIcon
);
55 ReleaseDC(NULL
, hdcsrc
);
59 hBmpNew
= CreateBitmap(cxSmIcon
, cySmIcon
, 1, 1, NULL
);
62 hBmpOld
= SelectObject(hdc
, hBitmap
);
63 rect
.right
= cxSmIcon
;
65 rect
.bottom
= cySmIcon
;
68 bkColor
= SetBkColor(hdc
, GetSysColor(COLOR_HIGHLIGHT
));
69 bkText
= SetTextColor(hdc
, GetSysColor(COLOR_HIGHLIGHTTEXT
));
71 ExtTextOut(hdc
, rect
.left
, rect
.top
, ETO_OPAQUE
, &rect
, _T(""), 0, NULL
);
73 hFont
= (HFONT
) GetStockObject(DEFAULT_GUI_FONT
);
77 hFontOld
= SelectObject(hdc
, hFont
);
78 DrawText(hdc
, _tcsupr(szBuf
), 2, &rect
, DT_SINGLELINE
|DT_CENTER
|DT_VCENTER
);
79 SelectObject(hdc
, hBmpNew
);
80 PatBlt(hdc
, 0, 0, cxSmIcon
, cySmIcon
, BLACKNESS
);
81 SelectObject(hdc
, hBmpOld
);
82 SelectObject(hdc
, hFontOld
);
84 IconInfo
.hbmColor
= hBitmap
;
85 IconInfo
.hbmMask
= hBmpNew
;
86 IconInfo
.fIcon
= TRUE
;
88 hIcon
= CreateIconIndirect(&IconInfo
);
90 if (hBmpNew
) DeleteObject(hBmpNew
);
91 if (hBmpOld
) DeleteObject(hBmpOld
);
92 if (hFont
) DeleteObject(hFont
);
96 if (hdc
) DeleteDC(hdc
);
97 if (hBitmap
) DeleteObject(hBitmap
);
103 AddTrayIcon(HWND hwnd
)
106 TCHAR szLCID
[CCH_LAYOUT_ID
+ 1];
107 TCHAR szName
[MAX_PATH
];
109 GetLayoutID(_T("1"), szLCID
);
110 GetLayoutName(_T("1"), szName
);
112 tnid
.cbSize
= sizeof(NOTIFYICONDATA
);
115 tnid
.uFlags
= NIF_ICON
| NIF_MESSAGE
| NIF_TIP
;
116 tnid
.uCallbackMessage
= WM_NOTIFYICONMSG
;
117 tnid
.hIcon
= CreateTrayIcon(szLCID
);
119 lstrcpyn(tnid
.szTip
, szName
, sizeof(tnid
.szTip
));
121 Shell_NotifyIcon(NIM_ADD
, &tnid
);
125 DelTrayIcon(HWND hwnd
)
129 tnid
.cbSize
= sizeof(NOTIFYICONDATA
);
133 Shell_NotifyIcon(NIM_DELETE
, &tnid
);
137 UpdateTrayIcon(HWND hwnd
, LPTSTR szLCID
, LPTSTR szName
)
141 tnid
.cbSize
= sizeof(NOTIFYICONDATA
);
144 tnid
.uFlags
= NIF_ICON
| NIF_MESSAGE
| NIF_TIP
;
145 tnid
.uCallbackMessage
= WM_NOTIFYICONMSG
;
146 tnid
.hIcon
= CreateTrayIcon(szLCID
);
148 lstrcpyn(tnid
.szTip
, szName
, sizeof(tnid
.szTip
));
150 Shell_NotifyIcon(NIM_MODIFY
, &tnid
);
154 GetLayoutID(LPTSTR szLayoutNum
, LPTSTR szLCID
)
159 TCHAR szTempLCID
[CCH_LAYOUT_ID
+ 1];
162 if (RegOpenKeyEx(HKEY_CURRENT_USER
, _T("Keyboard Layout\\Preload"), 0, KEY_QUERY_VALUE
, &hKey
) == ERROR_SUCCESS
)
164 dwBufLen
= sizeof(szTempLCID
);
165 dwRes
= RegQueryValueEx(hKey
, szLayoutNum
, NULL
, NULL
, (LPBYTE
)szTempLCID
, &dwBufLen
);
167 if (dwRes
!= ERROR_SUCCESS
)
176 // Look for a substitude of this layout
177 if (RegOpenKeyEx(HKEY_CURRENT_USER
, _T("Keyboard Layout\\Substitutes"), 0, KEY_QUERY_VALUE
, &hKey
) == ERROR_SUCCESS
)
179 dwBufLen
= sizeof(szTempLCID
);
181 if (RegQueryValueEx(hKey
, szTempLCID
, NULL
, NULL
, (LPBYTE
)szLCID
, &dwBufLen
) != ERROR_SUCCESS
)
183 // No substitute found, then use the old LCID
184 lstrcpy(szLCID
, szTempLCID
);
191 // Substitutes key couldn't be opened, so use the old LCID
192 lstrcpy(szLCID
, szTempLCID
);
199 GetLayoutIDByHkl(HKL hKl
, LPTSTR szLayoutID
)
202 FIXME!!! This way of getting layout ID incorrect!
203 This will not work correctly for 0001040a, 00010410, etc
205 wsprintf(szLayoutID
, _T("%08x"), LOWORD(hKl
));
209 GetLayoutName(LPTSTR szLayoutNum
, LPTSTR szName
)
213 TCHAR szBuf
[MAX_PATH
], szDispName
[MAX_PATH
], szIndex
[MAX_PATH
], szPath
[MAX_PATH
];
214 TCHAR szLCID
[CCH_LAYOUT_ID
+ 1];
218 if(!GetLayoutID(szLayoutNum
, szLCID
))
221 wsprintf(szBuf
, _T("SYSTEM\\CurrentControlSet\\Control\\Keyboard Layouts\\%s"), szLCID
);
223 if (RegOpenKeyEx(HKEY_LOCAL_MACHINE
, (LPCTSTR
)szBuf
, 0, KEY_QUERY_VALUE
, &hKey
) == ERROR_SUCCESS
)
225 dwBufLen
= sizeof(szBuf
);
227 if (RegQueryValueEx(hKey
, _T("Layout Display Name"), NULL
, NULL
, (LPBYTE
)szDispName
, &dwBufLen
) == ERROR_SUCCESS
)
229 if (szDispName
[0] == '@')
231 for (i
= 0; i
< _tcslen(szDispName
); i
++)
233 if ((szDispName
[i
] == ',') && (szDispName
[i
+ 1] == '-'))
235 for (j
= i
+ 2, k
= 0; j
< _tcslen(szDispName
)+1; j
++, k
++)
237 szIndex
[k
] = szDispName
[j
];
239 szDispName
[i
- 1] = '\0';
242 else szDispName
[i
] = szDispName
[i
+ 1];
245 if (ExpandEnvironmentStrings(szDispName
, szPath
, MAX_PATH
))
247 hLib
= LoadLibrary(szPath
);
250 if (LoadString(hLib
, _ttoi(szIndex
), szPath
, sizeof(szPath
) / sizeof(TCHAR
)) != 0)
252 _tcscpy(szName
, szPath
);
263 dwBufLen
= sizeof(szBuf
);
265 if (RegQueryValueEx(hKey
, _T("Layout Text"), NULL
, NULL
, (LPBYTE
)szName
, &dwBufLen
) == ERROR_SUCCESS
)
276 EnumWindowsProc(HWND hwnd
, LPARAM lParam
)
278 PostMessage(hwnd
, WM_INPUTLANGCHANGEREQUEST
, 0, lParam
);
283 ActivateLayout(HWND hwnd
, ULONG uLayoutNum
)
286 TCHAR szLayoutNum
[CCH_ULONG_DEC
+ 1];
287 TCHAR szLCID
[CCH_LAYOUT_ID
+ 1];
288 TCHAR szLangName
[MAX_PATH
];
290 _ultot(uLayoutNum
, szLayoutNum
, 10);
291 GetLayoutID(szLayoutNum
, szLCID
);
293 // Switch to the new keyboard layout
294 GetLocaleInfo((LANGID
)_tcstoul(szLCID
, NULL
, 16), LOCALE_SLANGUAGE
, (LPTSTR
)szLangName
, sizeof(szLangName
) / sizeof(TCHAR
));
295 UpdateTrayIcon(hwnd
, szLCID
, szLangName
);
296 hKl
= LoadKeyboardLayout(szLCID
, KLF_ACTIVATE
);
298 EnumWindows(EnumWindowsProc
, (LPARAM
) hKl
);
300 ulCurrentLayoutNum
= uLayoutNum
;
308 DWORD dwIndex
, dwSize
;
309 TCHAR szLayoutNum
[CCH_ULONG_DEC
+ 1];
310 TCHAR szName
[MAX_PATH
];
312 hMenu
= CreatePopupMenu();
314 // Add the keyboard layouts to the popup menu
315 if (RegOpenKeyEx(HKEY_CURRENT_USER
, _T("Keyboard Layout\\Preload"), 0, KEY_QUERY_VALUE
, &hKey
) == ERROR_SUCCESS
)
317 for(dwIndex
= 0; ; dwIndex
++)
319 dwSize
= sizeof(szLayoutNum
);
320 if(RegEnumValue(hKey
, dwIndex
, szLayoutNum
, &dwSize
, NULL
, NULL
, NULL
, NULL
) != ERROR_SUCCESS
)
323 if(!GetLayoutName(szLayoutNum
, szName
))
326 AppendMenu(hMenu
, MF_STRING
, _ttoi(szLayoutNum
), szName
);
329 (void)CheckMenuItem(hMenu
, ulCurrentLayoutNum
, MF_CHECKED
);
340 hDllLib
= LoadLibrary(_T("kbsdll.dll"));
341 if (!hDllLib
) return FALSE
;
343 KbSwitchSetHooks
= (PROC
) GetProcAddress(hDllLib
, MAKEINTRESOURCEA(1));
344 KbSwitchDeleteHooks
= (PROC
) GetProcAddress(hDllLib
, MAKEINTRESOURCEA(2));
346 if ((KbSwitchSetHooks
== NULL
)||(KbSwitchDeleteHooks
== NULL
))
349 return KbSwitchSetHooks();
355 if (KbSwitchDeleteHooks
) KbSwitchDeleteHooks();
356 if (hDllLib
) FreeLibrary(hDllLib
);
362 TCHAR szLayoutNum
[3 + 1], szLayoutID
[CCH_LAYOUT_ID
+ 1];
363 ULONG Ret
= ulCurrentLayoutNum
;
365 _ultot(ulCurrentLayoutNum
, szLayoutNum
, 10);
366 if (!GetLayoutID(szLayoutNum
, szLayoutID
))
371 _ultot(Ret
+ 1, szLayoutNum
, 10);
373 if (GetLayoutID(szLayoutNum
, szLayoutID
))
379 _ultot(Ret
- 1, szLayoutNum
, 10);
380 if (GetLayoutID(szLayoutNum
, szLayoutID
))
390 SettingsChanging(HWND hwnd
)
392 UINT cxSmIconCur
= 0, cySmIconCur
= 0;
394 cxSmIconCur
= GetSystemMetrics(SM_CXSMICON
);
395 cySmIconCur
= GetSystemMetrics(SM_CYSMICON
);
397 if ((cxSmIcon
!= cxSmIconCur
) || (cySmIcon
!= cySmIconCur
))
399 cxSmIcon
= cxSmIconCur
;
400 cySmIcon
= cySmIconCur
;
403 ActivateLayout(hwnd
, ulCurrentLayoutNum
);
407 WndProc(HWND hwnd
, UINT Message
, WPARAM wParam
, LPARAM lParam
)
409 static HMENU hRightPopupMenu
;
410 static TCHAR szLCID
[MAX_PATH
], szLangName
[MAX_PATH
];
416 cxSmIcon
= GetSystemMetrics(SM_CXSMICON
);
417 cySmIcon
= GetSystemMetrics(SM_CYSMICON
);
421 hRightPopupMenu
= GetSubMenu(LoadMenu(hInst
, MAKEINTRESOURCE(IDR_POPUP
)), 0);
423 ActivateLayout(hwnd
, ulCurrentLayoutNum
);
428 case WM_LANG_CHANGED
:
430 GetLayoutIDByHkl((HKL
)lParam
, szLCID
);
431 GetLocaleInfo((LANGID
)_tcstoul(szLCID
, NULL
, 16), LOCALE_SLANGUAGE
, (LPTSTR
)szLangName
, sizeof(szLangName
) / sizeof(TCHAR
));
432 UpdateTrayIcon(hwnd
, szLCID
, szLangName
);
439 ActivateLayout(hwnd
, GetNextLayout());
444 case WM_WINDOW_ACTIVATE
:
446 GetLayoutIDByHkl(GetKeyboardLayout(GetWindowThreadProcessId((HWND
)wParam
, 0)), szLCID
);
447 GetLocaleInfo((LANGID
)_tcstoul(szLCID
, NULL
, 16), LOCALE_SLANGUAGE
, (LPTSTR
)szLangName
, sizeof(szLangName
) / sizeof(TCHAR
));
448 UpdateTrayIcon(hwnd
, szLCID
, szLangName
);
453 case WM_NOTIFYICONMSG
:
462 SetForegroundWindow(hwnd
);
464 if (lParam
== WM_LBUTTONDOWN
)
466 HMENU hLeftPopupMenu
;
468 /* Rebuild the left popup menu on every click to take care of keyboard layout changes */
469 hLeftPopupMenu
= BuildLeftPopupMenu();
470 TrackPopupMenu(hLeftPopupMenu
, 0, pt
.x
, pt
.y
, 0, hwnd
, NULL
);
471 DestroyMenu(hLeftPopupMenu
);
475 TrackPopupMenu(hRightPopupMenu
, 0, pt
.x
, pt
.y
, 0, hwnd
, NULL
);
478 PostMessage(hwnd
, WM_NULL
, 0, 0);
486 switch (LOWORD(wParam
))
489 SendMessage(hwnd
, WM_CLOSE
, 0, 0);
494 SHELLEXECUTEINFO shInputDll
= {0};
496 shInputDll
.cbSize
= sizeof(shInputDll
);
497 shInputDll
.hwnd
= hwnd
;
498 shInputDll
.lpVerb
= _T("open");
499 shInputDll
.lpFile
= _T("rundll32.exe");
500 shInputDll
.lpParameters
= _T("shell32.dll,Control_RunDLL input.dll");
502 if (!ShellExecuteEx(&shInputDll
))
503 MessageBox(hwnd
, _T("Can't start input.dll"), NULL
, MB_OK
| MB_ICONERROR
);
507 ActivateLayout(hwnd
, LOWORD(wParam
));
512 case WM_SETTINGCHANGE
:
514 SettingsChanging(hwnd
);
516 if (wParam
== SPI_SETDEFAULTINPUTLANG
)
518 //FIXME: Should detect default language changes by CPL applet or by other tools and update UI
526 DestroyMenu(hRightPopupMenu
);
534 return DefWindowProc(hwnd
, Message
, wParam
, lParam
);
538 _tWinMain(HINSTANCE hInstance
, HINSTANCE hPrevInst
, LPTSTR lpCmdLine
, INT nCmdShow
)
540 WNDCLASS WndClass
= {0};
544 hMutex
= CreateMutex(NULL
, FALSE
, szKbSwitcherName
);
545 if ((!hMutex
) || (GetLastError() == ERROR_ALREADY_EXISTS
))
549 hProcessHeap
= GetProcessHeap();
552 WndClass
.lpfnWndProc
= (WNDPROC
)WndProc
;
553 WndClass
.cbClsExtra
= 0;
554 WndClass
.cbWndExtra
= 0;
555 WndClass
.hInstance
= hInstance
;
556 WndClass
.hIcon
= NULL
;
557 WndClass
.hCursor
= NULL
;
558 WndClass
.hbrBackground
= NULL
;
559 WndClass
.lpszMenuName
= NULL
;
560 WndClass
.lpszClassName
= szKbSwitcherName
;
562 if (!RegisterClass(&WndClass
))
565 CreateWindow(szKbSwitcherName
, NULL
, 0, 0, 0, 1, 1, HWND_DESKTOP
, NULL
, hInstance
, NULL
);
567 while(GetMessage(&msg
,NULL
,0,0))
569 TranslateMessage(&msg
);
570 DispatchMessage(&msg
);
573 if (hMutex
) CloseHandle(hMutex
);