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 DWORD bkColor
, bkText
;
37 HFONT hFontOld
, hFont
= NULL
;
41 lId
= (LANGID
)_tcstoul(szLCID
, NULL
, 16);
42 if (GetLocaleInfo(lId
,
43 LOCALE_SISO639LANGNAME
,
45 sizeof(szBuf
) / sizeof(TCHAR
)) == 0)
47 lstrcpy(szBuf
, _T("??\0"));
51 hdc
= CreateCompatibleDC(hdcsrc
);
52 hBitmap
= CreateCompatibleBitmap(hdcsrc
, 16, 16);
53 ReleaseDC(NULL
, hdcsrc
);
57 hBmpNew
= CreateBitmap(16, 16, 1, 1, NULL
);
60 hBmpOld
= SelectObject(hdc
, hBitmap
);
66 bkColor
= SetBkColor(hdc
, GetSysColor(COLOR_HIGHLIGHT
));
67 bkText
= SetTextColor(hdc
, GetSysColor(COLOR_HIGHLIGHTTEXT
));
69 ExtTextOut(hdc
, rect
.left
, rect
.top
, ETO_OPAQUE
, &rect
, _T(""), 0, NULL
);
71 hFont
= CreateFont(-11, 0, 0, 0, FW_NORMAL
, FALSE
, FALSE
, FALSE
, ANSI_CHARSET
,
72 OUT_DEFAULT_PRECIS
, CLIP_DEFAULT_PRECIS
,
73 DEFAULT_QUALITY
, FF_DONTCARE
, _T("Tahoma"));
75 hFontOld
= SelectObject(hdc
, hFont
);
76 DrawText(hdc
, _tcsupr(szBuf
), 2, &rect
, DT_SINGLELINE
|DT_CENTER
|DT_VCENTER
);
77 SelectObject(hdc
, hBmpNew
);
78 PatBlt(hdc
, 0, 0, 16, 16, BLACKNESS
);
79 SelectObject(hdc
, hBmpOld
);
80 SelectObject(hdc
, hFontOld
);
82 IconInfo
.hbmColor
= hBitmap
;
83 IconInfo
.hbmMask
= hBmpNew
;
84 IconInfo
.fIcon
= TRUE
;
86 hIcon
= CreateIconIndirect(&IconInfo
);
88 DeleteObject(hBmpNew
);
89 DeleteObject(hBmpOld
);
95 DeleteObject(hBitmap
);
101 AddTrayIcon(HWND hwnd
)
104 TCHAR szLCID
[CCH_LAYOUT_ID
+ 1];
105 TCHAR szName
[MAX_PATH
];
107 GetLayoutID(_T("1"), szLCID
);
108 GetLayoutName(_T("1"), szName
);
110 tnid
.cbSize
= sizeof(NOTIFYICONDATA
);
113 tnid
.uFlags
= NIF_ICON
| NIF_MESSAGE
| NIF_TIP
;
114 tnid
.uCallbackMessage
= WM_NOTIFYICONMSG
;
115 tnid
.hIcon
= CreateTrayIcon(szLCID
);
117 lstrcpyn(tnid
.szTip
, szName
, sizeof(tnid
.szTip
));
119 Shell_NotifyIcon(NIM_ADD
, &tnid
);
123 DelTrayIcon(HWND hwnd
)
127 tnid
.cbSize
= sizeof(NOTIFYICONDATA
);
131 Shell_NotifyIcon(NIM_DELETE
, &tnid
);
135 UpdateTrayIcon(HWND hwnd
, LPTSTR szLCID
, LPTSTR szName
)
139 tnid
.cbSize
= sizeof(NOTIFYICONDATA
);
142 tnid
.uFlags
= NIF_ICON
| NIF_MESSAGE
| NIF_TIP
;
143 tnid
.uCallbackMessage
= WM_NOTIFYICONMSG
;
144 tnid
.hIcon
= CreateTrayIcon(szLCID
);
146 lstrcpyn(tnid
.szTip
, szName
, sizeof(tnid
.szTip
));
148 Shell_NotifyIcon(NIM_MODIFY
, &tnid
);
152 GetLayoutID(LPTSTR szLayoutNum
, LPTSTR szLCID
)
157 TCHAR szTempLCID
[CCH_LAYOUT_ID
+ 1];
160 if (RegOpenKeyEx(HKEY_CURRENT_USER
, _T("Keyboard Layout\\Preload"), 0, KEY_QUERY_VALUE
, &hKey
) == ERROR_SUCCESS
)
162 dwBufLen
= sizeof(szTempLCID
);
163 dwRes
= RegQueryValueEx(hKey
, szLayoutNum
, NULL
, NULL
, (LPBYTE
)szTempLCID
, &dwBufLen
);
165 if (dwRes
!= ERROR_SUCCESS
)
174 // Look for a substitude of this layout
175 if (RegOpenKeyEx(HKEY_CURRENT_USER
, _T("Keyboard Layout\\Substitutes"), 0, KEY_QUERY_VALUE
, &hKey
) == ERROR_SUCCESS
)
177 dwBufLen
= sizeof(szTempLCID
);
179 if (RegQueryValueEx(hKey
, szTempLCID
, NULL
, NULL
, (LPBYTE
)szLCID
, &dwBufLen
) != ERROR_SUCCESS
)
181 // No substitute found, then use the old LCID
182 lstrcpy(szLCID
, szTempLCID
);
189 // Substitutes key couldn't be opened, so use the old LCID
190 lstrcpy(szLCID
, szTempLCID
);
197 GetLayoutIDByHkl(HKL hKl
, LPTSTR szLayoutID
)
200 FIXME!!! This way of getting layout ID incorrect!
201 This will not work correctly for 0001040a, 00010410, etc
203 wsprintf(szLayoutID
, _T("%08x"), LOWORD(hKl
));
207 GetLayoutName(LPTSTR szLayoutNum
, LPTSTR szName
)
211 TCHAR szBuf
[MAX_PATH
], szDispName
[MAX_PATH
], szIndex
[MAX_PATH
], szPath
[MAX_PATH
];
212 TCHAR szLCID
[CCH_LAYOUT_ID
+ 1];
216 if(!GetLayoutID(szLayoutNum
, szLCID
))
219 wsprintf(szBuf
, _T("SYSTEM\\CurrentControlSet\\Control\\Keyboard Layouts\\%s"), szLCID
);
221 if (RegOpenKeyEx(HKEY_LOCAL_MACHINE
, (LPCTSTR
)szBuf
, 0, KEY_QUERY_VALUE
, &hKey
) == ERROR_SUCCESS
)
223 dwBufLen
= sizeof(szBuf
);
225 if (RegQueryValueEx(hKey
, _T("Layout Display Name"), NULL
, NULL
, (LPBYTE
)szDispName
, &dwBufLen
) == ERROR_SUCCESS
)
227 if (szDispName
[0] == '@')
229 for (i
= 0; i
< _tcslen(szDispName
); i
++)
231 if ((szDispName
[i
] == ',') && (szDispName
[i
+ 1] == '-'))
233 for (j
= i
+ 2, k
= 0; j
< _tcslen(szDispName
)+1; j
++, k
++)
235 szIndex
[k
] = szDispName
[j
];
237 szDispName
[i
- 1] = '\0';
240 else szDispName
[i
] = szDispName
[i
+ 1];
243 if (ExpandEnvironmentStrings(szDispName
, szPath
, MAX_PATH
))
245 hLib
= LoadLibrary(szPath
);
248 if (LoadString(hLib
, _ttoi(szIndex
), szPath
, sizeof(szPath
) / sizeof(TCHAR
)) != 0)
250 _tcscpy(szName
, szPath
);
261 dwBufLen
= sizeof(szBuf
);
263 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 WndProc(HWND hwnd
, UINT Message
, WPARAM wParam
, LPARAM lParam
)
392 static HMENU hRightPopupMenu
;
393 static TCHAR szLCID
[MAX_PATH
], szLangName
[MAX_PATH
];
401 hRightPopupMenu
= GetSubMenu(LoadMenu(hInst
, MAKEINTRESOURCE(IDR_POPUP
)), 0);
403 ActivateLayout(hwnd
, ulCurrentLayoutNum
);
408 case WM_LANG_CHANGED
:
410 GetLayoutIDByHkl((HKL
)lParam
, szLCID
);
411 GetLocaleInfo((LANGID
)_tcstoul(szLCID
, NULL
, 16), LOCALE_SLANGUAGE
, (LPTSTR
)szLangName
, sizeof(szLangName
) / sizeof(TCHAR
));
412 UpdateTrayIcon(hwnd
, szLCID
, szLangName
);
419 ActivateLayout(hwnd
, GetNextLayout());
424 case WM_WINDOW_ACTIVATE
:
426 GetLayoutIDByHkl(GetKeyboardLayout(GetWindowThreadProcessId((HWND
)wParam
, 0)), szLCID
);
427 GetLocaleInfo((LANGID
)_tcstoul(szLCID
, NULL
, 16), LOCALE_SLANGUAGE
, (LPTSTR
)szLangName
, sizeof(szLangName
) / sizeof(TCHAR
));
428 UpdateTrayIcon(hwnd
, szLCID
, szLangName
);
433 case WM_NOTIFYICONMSG
:
442 SetForegroundWindow(hwnd
);
444 if (lParam
== WM_LBUTTONDOWN
)
446 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 return DefWindowProc(hwnd
, Message
, wParam
, lParam
);
516 _tWinMain(HINSTANCE hInstance
, HINSTANCE hPrevInst
, LPTSTR lpCmdLine
, INT nCmdShow
)
518 WNDCLASS WndClass
= {0};
522 hMutex
= CreateMutex(NULL
, FALSE
, szKbSwitcherName
);
523 if ((!hMutex
) || (GetLastError() == ERROR_ALREADY_EXISTS
))
527 hProcessHeap
= GetProcessHeap();
530 WndClass
.lpfnWndProc
= (WNDPROC
)WndProc
;
531 WndClass
.cbClsExtra
= 0;
532 WndClass
.cbWndExtra
= 0;
533 WndClass
.hInstance
= hInstance
;
534 WndClass
.hIcon
= NULL
;
535 WndClass
.hCursor
= NULL
;
536 WndClass
.hbrBackground
= NULL
;
537 WndClass
.lpszMenuName
= NULL
;
538 WndClass
.lpszClassName
= szKbSwitcherName
;
540 if (!RegisterClass(&WndClass
))
543 CreateWindow(szKbSwitcherName
, NULL
, 0, 0, 0, 1, 1, HWND_DESKTOP
, NULL
, hInstance
, NULL
);
545 while(GetMessage(&msg
,NULL
,0,0))
547 TranslateMessage(&msg
);
548 DispatchMessage(&msg
);
551 if (hMutex
) CloseHandle(hMutex
);