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;
29 CreateTrayIcon(LPTSTR szLCID
)
34 HBITMAP hBitmap
, hBmpNew
, hBmpOld
;
36 HFONT hFontOld
, hFont
= NULL
;
40 lId
= (LANGID
)_tcstoul(szLCID
, NULL
, 16);
41 if (GetLocaleInfo(lId
,
42 LOCALE_SISO639LANGNAME
,
44 sizeof(szBuf
) / sizeof(TCHAR
)) == 0)
46 lstrcpy(szBuf
, _T("??"));
50 hdc
= CreateCompatibleDC(hdcsrc
);
51 hBitmap
= CreateCompatibleBitmap(hdcsrc
, 16, 16);
52 ReleaseDC(NULL
, hdcsrc
);
56 hBmpNew
= CreateBitmap(16, 16, 1, 1, NULL
);
59 hBmpOld
= SelectObject(hdc
, hBitmap
);
65 SetBkColor(hdc
, GetSysColor(COLOR_HIGHLIGHT
));
66 SetTextColor(hdc
, GetSysColor(COLOR_HIGHLIGHTTEXT
));
68 ExtTextOut(hdc
, rect
.left
, rect
.top
, ETO_OPAQUE
, &rect
, _T(""), 0, NULL
);
70 hFont
= CreateFont(-11, 0, 0, 0, FW_NORMAL
, FALSE
, FALSE
, FALSE
, ANSI_CHARSET
,
71 OUT_DEFAULT_PRECIS
, CLIP_DEFAULT_PRECIS
,
72 DEFAULT_QUALITY
, FF_DONTCARE
, _T("Tahoma"));
74 hFontOld
= SelectObject(hdc
, hFont
);
75 DrawText(hdc
, _tcsupr(szBuf
), 2, &rect
, DT_SINGLELINE
|DT_CENTER
|DT_VCENTER
);
76 SelectObject(hdc
, hBmpNew
);
77 PatBlt(hdc
, 0, 0, 16, 16, BLACKNESS
);
78 SelectObject(hdc
, hBmpOld
);
79 SelectObject(hdc
, hFontOld
);
81 IconInfo
.hbmColor
= hBitmap
;
82 IconInfo
.hbmMask
= hBmpNew
;
83 IconInfo
.fIcon
= TRUE
;
85 hIcon
= CreateIconIndirect(&IconInfo
);
87 DeleteObject(hBmpNew
);
88 DeleteObject(hBmpOld
);
94 DeleteObject(hBitmap
);
100 AddTrayIcon(HWND hwnd
)
103 TCHAR szLCID
[CCH_LAYOUT_ID
+ 1];
104 TCHAR szName
[MAX_PATH
];
106 GetLayoutID(_T("1"), szLCID
);
107 GetLayoutName(_T("1"), szName
);
109 tnid
.cbSize
= sizeof(NOTIFYICONDATA
);
112 tnid
.uFlags
= NIF_ICON
| NIF_MESSAGE
| NIF_TIP
;
113 tnid
.uCallbackMessage
= WM_NOTIFYICONMSG
;
114 tnid
.hIcon
= CreateTrayIcon(szLCID
);
116 lstrcpyn(tnid
.szTip
, szName
, sizeof(tnid
.szTip
) / sizeof(TCHAR
));
118 Shell_NotifyIcon(NIM_ADD
, &tnid
);
122 DelTrayIcon(HWND hwnd
)
126 tnid
.cbSize
= sizeof(NOTIFYICONDATA
);
130 Shell_NotifyIcon(NIM_DELETE
, &tnid
);
134 UpdateTrayIcon(HWND hwnd
, LPTSTR szLCID
, LPTSTR szName
)
138 tnid
.cbSize
= sizeof(NOTIFYICONDATA
);
141 tnid
.uFlags
= NIF_ICON
| NIF_MESSAGE
| NIF_TIP
;
142 tnid
.uCallbackMessage
= WM_NOTIFYICONMSG
;
143 tnid
.hIcon
= CreateTrayIcon(szLCID
);
145 lstrcpyn(tnid
.szTip
, szName
, sizeof(tnid
.szTip
) / sizeof(TCHAR
));
147 Shell_NotifyIcon(NIM_MODIFY
, &tnid
);
151 GetLayoutID(LPTSTR szLayoutNum
, LPTSTR szLCID
)
156 TCHAR szTempLCID
[CCH_LAYOUT_ID
+ 1];
159 if (RegOpenKeyEx(HKEY_CURRENT_USER
, _T("Keyboard Layout\\Preload"), 0, KEY_QUERY_VALUE
, &hKey
) == ERROR_SUCCESS
)
161 dwBufLen
= sizeof(szTempLCID
);
162 dwRes
= RegQueryValueEx(hKey
, szLayoutNum
, NULL
, NULL
, (LPBYTE
)szTempLCID
, &dwBufLen
);
164 if (dwRes
!= ERROR_SUCCESS
)
173 // Look for a substitude of this layout
174 if (RegOpenKeyEx(HKEY_CURRENT_USER
, _T("Keyboard Layout\\Substitutes"), 0, KEY_QUERY_VALUE
, &hKey
) == ERROR_SUCCESS
)
176 dwBufLen
= sizeof(szTempLCID
);
178 if (RegQueryValueEx(hKey
, szTempLCID
, NULL
, NULL
, (LPBYTE
)szLCID
, &dwBufLen
) != ERROR_SUCCESS
)
180 // No substitute found, then use the old LCID
181 lstrcpy(szLCID
, szTempLCID
);
188 // Substitutes key couldn't be opened, so use the old LCID
189 lstrcpy(szLCID
, szTempLCID
);
196 GetLayoutIDByHkl(HKL hKl
, LPTSTR szLayoutID
)
199 FIXME!!! This way of getting layout ID incorrect!
200 This will not work correctly for 0001040a, 00010410, etc
202 wsprintf(szLayoutID
, _T("%08x"), LOWORD(hKl
));
206 GetLayoutName(LPTSTR szLayoutNum
, LPTSTR szName
)
210 TCHAR szBuf
[MAX_PATH
], szDispName
[MAX_PATH
], szIndex
[MAX_PATH
], szPath
[MAX_PATH
];
211 TCHAR szLCID
[CCH_LAYOUT_ID
+ 1];
215 if(!GetLayoutID(szLayoutNum
, szLCID
))
218 wsprintf(szBuf
, _T("SYSTEM\\CurrentControlSet\\Control\\Keyboard Layouts\\%s"), szLCID
);
220 if (RegOpenKeyEx(HKEY_LOCAL_MACHINE
, (LPCTSTR
)szBuf
, 0, KEY_QUERY_VALUE
, &hKey
) == ERROR_SUCCESS
)
222 dwBufLen
= sizeof(szBuf
);
224 if (RegQueryValueEx(hKey
, _T("Layout Display Name"), NULL
, NULL
, (LPBYTE
)szDispName
, &dwBufLen
) == ERROR_SUCCESS
)
226 if (szDispName
[0] == '@')
228 for (i
= 0; i
< _tcslen(szDispName
); i
++)
230 if ((szDispName
[i
] == ',') && (szDispName
[i
+ 1] == '-'))
232 for (j
= i
+ 2, k
= 0; j
< _tcslen(szDispName
)+1; j
++, k
++)
234 szIndex
[k
] = szDispName
[j
];
236 szDispName
[i
- 1] = '\0';
239 else szDispName
[i
] = szDispName
[i
+ 1];
242 if (ExpandEnvironmentStrings(szDispName
, szPath
, MAX_PATH
))
244 hLib
= LoadLibrary(szPath
);
247 if (LoadString(hLib
, _ttoi(szIndex
), szPath
, sizeof(szPath
) / sizeof(TCHAR
)) != 0)
249 _tcscpy(szName
, szPath
);
260 dwBufLen
= sizeof(szBuf
);
262 if (RegQueryValueEx(hKey
, _T("Layout Text"), NULL
, NULL
, (LPBYTE
)szName
, &dwBufLen
) == ERROR_SUCCESS
)
275 EnumWindowsProc(HWND hwnd
, LPARAM lParam
)
277 PostMessage(hwnd
, WM_INPUTLANGCHANGEREQUEST
, 0, lParam
);
282 ActivateLayout(HWND hwnd
, ULONG uLayoutNum
)
285 TCHAR szLayoutNum
[CCH_ULONG_DEC
+ 1];
286 TCHAR szLCID
[CCH_LAYOUT_ID
+ 1];
287 TCHAR szLangName
[MAX_PATH
];
289 _ultot(uLayoutNum
, szLayoutNum
, 10);
290 GetLayoutID(szLayoutNum
, szLCID
);
292 // Switch to the new keyboard layout
293 GetLocaleInfo((LANGID
)_tcstoul(szLCID
, NULL
, 16), LOCALE_SLANGUAGE
, (LPTSTR
)szLangName
, sizeof(szLangName
) / sizeof(TCHAR
));
294 UpdateTrayIcon(hwnd
, szLCID
, szLangName
);
295 hKl
= LoadKeyboardLayout(szLCID
, KLF_ACTIVATE
);
297 EnumWindows(EnumWindowsProc
, (LPARAM
) hKl
);
299 ulCurrentLayoutNum
= uLayoutNum
;
307 DWORD dwIndex
, dwSize
;
308 TCHAR szLayoutNum
[CCH_ULONG_DEC
+ 1];
309 TCHAR szName
[MAX_PATH
];
311 hMenu
= CreatePopupMenu();
313 // Add the keyboard layouts to the popup menu
314 if (RegOpenKeyEx(HKEY_CURRENT_USER
, _T("Keyboard Layout\\Preload"), 0, KEY_QUERY_VALUE
, &hKey
) == ERROR_SUCCESS
)
316 for(dwIndex
= 0; ; dwIndex
++)
318 dwSize
= sizeof(szLayoutNum
);
319 if(RegEnumValue(hKey
, dwIndex
, szLayoutNum
, &dwSize
, NULL
, NULL
, NULL
, NULL
) != ERROR_SUCCESS
)
322 if(!GetLayoutName(szLayoutNum
, szName
))
325 AppendMenu(hMenu
, MF_STRING
, _ttoi(szLayoutNum
), szName
);
328 (void)CheckMenuItem(hMenu
, ulCurrentLayoutNum
, MF_CHECKED
);
339 hDllLib
= LoadLibrary(_T("kbsdll.dll"));
340 if (!hDllLib
) return FALSE
;
342 KbSwitchSetHooks
= (PROC
) GetProcAddress(hDllLib
, MAKEINTRESOURCEA(1));
343 KbSwitchDeleteHooks
= (PROC
) GetProcAddress(hDllLib
, MAKEINTRESOURCEA(2));
345 if ((KbSwitchSetHooks
== NULL
)||(KbSwitchDeleteHooks
== NULL
))
348 return KbSwitchSetHooks();
354 if (KbSwitchDeleteHooks
) KbSwitchDeleteHooks();
355 if (hDllLib
) FreeLibrary(hDllLib
);
361 TCHAR szLayoutNum
[3 + 1], szLayoutID
[CCH_LAYOUT_ID
+ 1];
362 ULONG Ret
= ulCurrentLayoutNum
;
364 _ultot(ulCurrentLayoutNum
, szLayoutNum
, 10);
365 if (!GetLayoutID(szLayoutNum
, szLayoutID
))
370 _ultot(Ret
+ 1, szLayoutNum
, 10);
372 if (GetLayoutID(szLayoutNum
, szLayoutID
))
378 _ultot(Ret
- 1, szLayoutNum
, 10);
379 if (GetLayoutID(szLayoutNum
, szLayoutID
))
389 WndProc(HWND hwnd
, UINT Message
, WPARAM wParam
, LPARAM lParam
)
391 static HMENU hRightPopupMenu
;
392 static TCHAR szLCID
[MAX_PATH
], szLangName
[MAX_PATH
];
393 static UINT s_uTaskbarRestart
;
401 hRightPopupMenu
= GetSubMenu(LoadMenu(hInst
, MAKEINTRESOURCE(IDR_POPUP
)), 0);
403 ActivateLayout(hwnd
, ulCurrentLayoutNum
);
404 s_uTaskbarRestart
= RegisterWindowMessage(TEXT("TaskbarCreated"));
409 case WM_LANG_CHANGED
:
411 GetLayoutIDByHkl((HKL
)lParam
, szLCID
);
412 GetLocaleInfo((LANGID
)_tcstoul(szLCID
, NULL
, 16), LOCALE_SLANGUAGE
, (LPTSTR
)szLangName
, sizeof(szLangName
) / sizeof(TCHAR
));
413 UpdateTrayIcon(hwnd
, szLCID
, szLangName
);
420 ActivateLayout(hwnd
, GetNextLayout());
425 case WM_WINDOW_ACTIVATE
:
427 GetLayoutIDByHkl(GetKeyboardLayout(GetWindowThreadProcessId((HWND
)wParam
, 0)), szLCID
);
428 GetLocaleInfo((LANGID
)_tcstoul(szLCID
, NULL
, 16), LOCALE_SLANGUAGE
, (LPTSTR
)szLangName
, sizeof(szLangName
) / sizeof(TCHAR
));
429 UpdateTrayIcon(hwnd
, szLCID
, szLangName
);
434 case WM_NOTIFYICONMSG
:
443 SetForegroundWindow(hwnd
);
445 if (lParam
== WM_LBUTTONUP
)
447 HMENU hLeftPopupMenu
;
448 /* Rebuild the left popup menu on every click to take care of keyboard layout changes */
449 hLeftPopupMenu
= BuildLeftPopupMenu();
450 TrackPopupMenu(hLeftPopupMenu
, 0, pt
.x
, pt
.y
, 0, hwnd
, NULL
);
451 DestroyMenu(hLeftPopupMenu
);
455 TrackPopupMenu(hRightPopupMenu
, 0, pt
.x
, pt
.y
, 0, hwnd
, NULL
);
458 PostMessage(hwnd
, WM_NULL
, 0, 0);
466 switch (LOWORD(wParam
))
469 SendMessage(hwnd
, WM_CLOSE
, 0, 0);
474 SHELLEXECUTEINFO shInputDll
= {0};
476 shInputDll
.cbSize
= sizeof(shInputDll
);
477 shInputDll
.hwnd
= hwnd
;
478 shInputDll
.lpVerb
= _T("open");
479 shInputDll
.lpFile
= _T("rundll32.exe");
480 shInputDll
.lpParameters
= _T("shell32.dll,Control_RunDLL input.dll");
482 if (!ShellExecuteEx(&shInputDll
))
483 MessageBox(hwnd
, _T("Can't start input.dll"), NULL
, MB_OK
| MB_ICONERROR
);
487 ActivateLayout(hwnd
, LOWORD(wParam
));
492 case WM_SETTINGCHANGE
:
494 if (wParam
== SPI_SETDEFAULTINPUTLANG
)
496 //FIXME: Should detect default language changes by CPL applet or by other tools and update UI
504 DestroyMenu(hRightPopupMenu
);
512 if(Message
== s_uTaskbarRestart
)
517 return DefWindowProc(hwnd
, Message
, wParam
, lParam
);
521 _tWinMain(HINSTANCE hInstance
, HINSTANCE hPrevInst
, LPTSTR lpCmdLine
, INT nCmdShow
)
523 WNDCLASS WndClass
= {0};
527 switch (GetUserDefaultUILanguage())
529 case MAKELANGID(LANG_HEBREW
, SUBLANG_DEFAULT
):
530 SetProcessDefaultLayout(LAYOUT_RTL
);
536 hMutex
= CreateMutex(NULL
, FALSE
, szKbSwitcherName
);
540 if (GetLastError() == ERROR_ALREADY_EXISTS
)
547 hProcessHeap
= GetProcessHeap();
550 WndClass
.lpfnWndProc
= WndProc
;
551 WndClass
.cbClsExtra
= 0;
552 WndClass
.cbWndExtra
= 0;
553 WndClass
.hInstance
= hInstance
;
554 WndClass
.hIcon
= NULL
;
555 WndClass
.hCursor
= NULL
;
556 WndClass
.hbrBackground
= NULL
;
557 WndClass
.lpszMenuName
= NULL
;
558 WndClass
.lpszClassName
= szKbSwitcherName
;
560 if (!RegisterClass(&WndClass
))
566 CreateWindow(szKbSwitcherName
, NULL
, 0, 0, 0, 1, 1, HWND_DESKTOP
, NULL
, hInstance
, NULL
);
568 while(GetMessage(&msg
,NULL
,0,0))
570 TranslateMessage(&msg
);
571 DispatchMessage(&msg
);