diff --git a/base/applications/kbswitch/CMakeLists.txt b/base/applications/kbswitch/CMakeLists.txt index 1d09f6bd380..ab9eeaf5a2e 100644 --- a/base/applications/kbswitch/CMakeLists.txt +++ b/base/applications/kbswitch/CMakeLists.txt @@ -1,9 +1,10 @@ add_rc_deps(kbswitch.rc ${CMAKE_CURRENT_SOURCE_DIR}/res/kbswitch.ico) -add_executable(kbswitch kbswitch.c kbswitch.rc) +add_executable(kbswitch kbswitch.c imemenu.c kbswitch.rc) set_module_type(kbswitch win32gui UNICODE) target_link_libraries(kbswitch wine) add_importlibs(kbswitch advapi32 imm32 user32 shell32 shlwapi gdi32 msvcrt kernel32 ntdll) add_cd_file(TARGET kbswitch DESTINATION reactos/system32 FOR all) add_subdirectory(indicdll) +add_dependencies(kbswitch indicdll) diff --git a/base/applications/kbswitch/imemenu.c b/base/applications/kbswitch/imemenu.c new file mode 100644 index 00000000000..b04bbe5a907 --- /dev/null +++ b/base/applications/kbswitch/imemenu.c @@ -0,0 +1,260 @@ +/* + * PROJECT: ReactOS Keyboard Layout Switcher + * LICENSE: GPL-2.0-or-later (https://spdx.org/licenses/GPL-2.0-or-later) + * PURPOSE: IME menu handling + * COPYRIGHT: Copyright 2025 Katayama Hirofumi MZ (katayama.hirofumi.mz@gmail.com) + */ + +#include "kbswitch.h" +#include "imemenu.h" + +#include +WINE_DEFAULT_DEBUG_CHANNEL(internat); + +PIMEMENUNODE g_pMenuList = NULL; +INT g_nNextMenuID = 0; + +static BOOL MakeImeMenu(_In_ HMENU hMenu, _In_ const IMEMENUNODE *pMenu); + +static VOID +AddImeMenuNode(_In_ PIMEMENUNODE pMenu) +{ + if (!g_pMenuList) + { + g_pMenuList = pMenu; + return; + } + + pMenu->m_pNext = g_pMenuList; + g_pMenuList = pMenu; +} + +static PIMEMENUNODE +AllocateImeMenu(_In_ DWORD itemCount) +{ + SIZE_T cbMenu = sizeof(IMEMENUNODE) + (itemCount - 1) * sizeof(IMEMENUITEM); + PIMEMENUNODE pMenu = LocalAlloc(LPTR, cbMenu); + if (!pMenu) + return NULL; + pMenu->m_nItems = itemCount; + AddImeMenuNode(pMenu); + return pMenu; +} + +static VOID +GetImeMenuItem( + _In_ HIMC hIMC, + _Out_ PIMEMENUITEMINFO lpImeParentMenu, + _In_ BOOL bRightMenu, + _Out_ PIMEMENUITEM pItem) +{ + ZeroMemory(pItem, sizeof(IMEMENUITEM)); + pItem->m_Info = *lpImeParentMenu; + + if (lpImeParentMenu->fType & IMFT_SUBMENU) + pItem->m_pSubMenu = CreateImeMenu(hIMC, lpImeParentMenu, bRightMenu); + + pItem->m_nRealID = pItem->m_Info.wID; + pItem->m_Info.wID = ID_STARTIMEMENU + g_nNextMenuID++; +} + +PIMEMENUNODE +CreateImeMenu( + _In_ HIMC hIMC, + _Inout_opt_ PIMEMENUITEMINFO lpImeParentMenu, + _In_ BOOL bRightMenu) +{ + const DWORD dwFlags = (bRightMenu ? IGIMIF_RIGHTMENU : 0); + const DWORD dwTypes = IGIMII_CMODE | + IGIMII_SMODE | + IGIMII_CONFIGURE | + IGIMII_TOOLS | + IGIMII_HELP | + IGIMII_OTHER; + DWORD itemCount = ImmGetImeMenuItems(hIMC, dwFlags, dwTypes, lpImeParentMenu, NULL, 0); + if (!itemCount) + return NULL; + + PIMEMENUNODE pMenu = AllocateImeMenu(itemCount); + if (!pMenu) + return NULL; + + DWORD cbItems = sizeof(IMEMENUITEMINFO) * itemCount; + PIMEMENUITEMINFO pImeMenuItems = LocalAlloc(LPTR, cbItems); + if (!pImeMenuItems) + { + LocalFree(pMenu); + return NULL; + } + + itemCount = ImmGetImeMenuItems(hIMC, dwFlags, dwTypes, lpImeParentMenu, pImeMenuItems, cbItems); + if (!itemCount) + { + LocalFree(pImeMenuItems); + LocalFree(pMenu); + return NULL; + } + + PIMEMENUITEM pItems = pMenu->m_Items; + for (DWORD iItem = 0; iItem < itemCount; ++iItem) + { + GetImeMenuItem(hIMC, &pImeMenuItems[iItem], bRightMenu, &pItems[iItem]); + } + + LocalFree(pImeMenuItems); + return pMenu; +} + +static BOOL +FillImeMenuItem(_Out_ LPMENUITEMINFO pItemInfo, _In_ const IMEMENUITEM *pItem) +{ + ZeroMemory(pItemInfo, sizeof(MENUITEMINFO)); + pItemInfo->cbSize = sizeof(MENUITEMINFO); + pItemInfo->fMask = MIIM_ID | MIIM_STATE | MIIM_DATA; + pItemInfo->wID = pItem->m_Info.wID; + pItemInfo->fState = pItem->m_Info.fState; + pItemInfo->dwItemData = pItem->m_Info.dwItemData; + + if (pItem->m_Info.fType) + { + pItemInfo->fMask |= MIIM_FTYPE; + pItemInfo->fType = 0; + if (pItem->m_Info.fType & IMFT_RADIOCHECK) + pItemInfo->fType |= MFT_RADIOCHECK; + if (pItem->m_Info.fType & IMFT_SEPARATOR) + pItemInfo->fType |= MFT_SEPARATOR; + } + + if (pItem->m_Info.fType & IMFT_SUBMENU) + { + pItemInfo->fMask |= MIIM_SUBMENU; + pItemInfo->hSubMenu = CreatePopupMenu(); + if (!MakeImeMenu(pItemInfo->hSubMenu, pItem->m_pSubMenu)) + { + DestroyMenu(pItemInfo->hSubMenu); + pItemInfo->hSubMenu = NULL; + return FALSE; + } + } + + if (pItem->m_Info.hbmpChecked && pItem->m_Info.hbmpUnchecked) + { + pItemInfo->fMask |= MIIM_CHECKMARKS; + pItemInfo->hbmpChecked = pItem->m_Info.hbmpChecked; + pItemInfo->hbmpUnchecked = pItem->m_Info.hbmpUnchecked; + } + + if (pItem->m_Info.hbmpItem) + { + pItemInfo->fMask |= MIIM_BITMAP; + pItemInfo->hbmpItem = pItem->m_Info.hbmpItem; + } + + PCTSTR szString = pItem->m_Info.szString; + if (szString && szString[0]) + { + pItemInfo->fMask |= MIIM_STRING; + pItemInfo->dwTypeData = (PTSTR)szString; + pItemInfo->cch = lstrlen(szString); + } + + return TRUE; +} + +static BOOL +MakeImeMenu(_In_ HMENU hMenu, _In_ const IMEMENUNODE *pMenu) +{ + if (!pMenu || !pMenu->m_nItems) + return FALSE; + + for (INT iItem = 0; iItem < pMenu->m_nItems; ++iItem) + { + MENUITEMINFO mi = { sizeof(mi) }; + if (!FillImeMenuItem(&mi, &pMenu->m_Items[iItem])) + { + ERR("FillImeMenuItem failed\n"); + return FALSE; + } + if (!InsertMenuItem(hMenu, iItem, TRUE, &mi)) + { + ERR("InsertMenuItem failed\n"); + return FALSE; + } + } + + return TRUE; +} + +HMENU MenuFromImeMenu(_In_ const IMEMENUNODE *pMenu) +{ + HMENU hMenu = CreatePopupMenu(); + if (!pMenu) + return hMenu; + if (!MakeImeMenu(hMenu, pMenu)) + { + DestroyMenu(hMenu); + return NULL; + } + return hMenu; +} + +INT +GetRealImeMenuID(_In_ const IMEMENUNODE *pMenu, _In_ INT nFakeID) +{ + if (!pMenu || !pMenu->m_nItems || nFakeID < ID_STARTIMEMENU) + return 0; + + for (INT iItem = 0; iItem < pMenu->m_nItems; ++iItem) + { + const IMEMENUITEM *pItem = &pMenu->m_Items[iItem]; + if (pItem->m_Info.wID == nFakeID) + return pItem->m_nRealID; + + if (pItem->m_pSubMenu) + { + INT nRealID = GetRealImeMenuID(pItem->m_pSubMenu, nFakeID); + if (nRealID) + return nRealID; + } + } + + return 0; +} + +static BOOL +FreeMenuNode(_In_ PIMEMENUNODE pMenuNode) +{ + if (!pMenuNode) + return FALSE; + + for (INT iItem = 0; iItem < pMenuNode->m_nItems; ++iItem) + { + PIMEMENUITEM pItem = &pMenuNode->m_Items[iItem]; + if (pItem->m_Info.hbmpChecked) + DeleteObject(pItem->m_Info.hbmpChecked); + if (pItem->m_Info.hbmpUnchecked) + DeleteObject(pItem->m_Info.hbmpUnchecked); + if (pItem->m_Info.hbmpItem) + DeleteObject(pItem->m_Info.hbmpItem); + } + + LocalFree(pMenuNode); + return TRUE; +} + +VOID +CleanupImeMenus(VOID) +{ + if (!g_pMenuList) + return; + + PIMEMENUNODE pNext; + for (PIMEMENUNODE pNode = g_pMenuList; pNode; pNode = pNext) + { + pNext = pNode->m_pNext; + FreeMenuNode(pNode); + } + + g_pMenuList = NULL; + g_nNextMenuID = 0; +} diff --git a/base/applications/kbswitch/imemenu.h b/base/applications/kbswitch/imemenu.h new file mode 100644 index 00000000000..e496a16e854 --- /dev/null +++ b/base/applications/kbswitch/imemenu.h @@ -0,0 +1,38 @@ +/* + * PROJECT: ReactOS Keyboard Layout Switcher + * LICENSE: GPL-2.0-or-later (https://spdx.org/licenses/GPL-2.0-or-later) + * PURPOSE: IME menu handling + * COPYRIGHT: Copyright 2025 Katayama Hirofumi MZ (katayama.hirofumi.mz@gmail.com) + */ + +#pragma once + +#include + +#define ID_STARTIMEMENU 1000 + +struct tagIMEMENUNODE; + +typedef struct tagIMEMENUITEM +{ + IMEMENUITEMINFO m_Info; + UINT m_nRealID; + struct tagIMEMENUNODE *m_pSubMenu; +} IMEMENUITEM, *PIMEMENUITEM; + +typedef struct tagIMEMENUNODE +{ + struct tagIMEMENUNODE *m_pNext; + INT m_nItems; + IMEMENUITEM m_Items[ANYSIZE_ARRAY]; +} IMEMENUNODE, *PIMEMENUNODE; + +PIMEMENUNODE +CreateImeMenu( + _In_ HIMC hIMC, + _Inout_opt_ PIMEMENUITEMINFO lpImeParentMenu, + _In_ BOOL bRightMenu); + +HMENU MenuFromImeMenu(_In_ const IMEMENUNODE *pMenu); +INT GetRealImeMenuID(_In_ const IMEMENUNODE *pMenu, _In_ INT nFakeID); +VOID CleanupImeMenus(VOID); diff --git a/base/applications/kbswitch/indicdll/CMakeLists.txt b/base/applications/kbswitch/indicdll/CMakeLists.txt index bd693b310f0..22b13b9cfda 100644 --- a/base/applications/kbswitch/indicdll/CMakeLists.txt +++ b/base/applications/kbswitch/indicdll/CMakeLists.txt @@ -1,6 +1,9 @@ spec2def(indicdll.dll indicdll.spec) +file(GLOB indicdll_rc_deps res/*.*) +add_rc_deps(indicdll.rc ${indicdll_rc_deps}) + list(APPEND SOURCE indicdll.c indicdll.rc diff --git a/base/applications/kbswitch/indicdll/indicdll.c b/base/applications/kbswitch/indicdll/indicdll.c index 49ec3ac0791..50aebde4402 100644 --- a/base/applications/kbswitch/indicdll/indicdll.c +++ b/base/applications/kbswitch/indicdll/indicdll.c @@ -8,61 +8,75 @@ */ #include "../kbswitch.h" +#include "resource.h" -HHOOK hWinHook = NULL; -HHOOK hShellHook = NULL; -HHOOK hKeyboardLLHook = NULL; -HINSTANCE hInstance = NULL; -HWND hKbSwitchWnd = NULL; +typedef struct tagSHARED_DATA +{ + HHOOK hWinHook; + HHOOK hShellHook; + HHOOK hKeyboardLLHook; + HWND hKbSwitchWnd; + UINT nHotID; + DWORD_PTR dwHotMenuItemData; + CRITICAL_SECTION csLock; +} SHARED_DATA, *PSHARED_DATA; + +HINSTANCE g_hInstance = NULL; +HANDLE g_hShared = NULL; +PSHARED_DATA g_pShared = NULL; +BOOL g_bCriticalSectionInitialized = 0; static VOID PostMessageToMainWnd(UINT Msg, WPARAM wParam, LPARAM lParam) { - PostMessage(hKbSwitchWnd, Msg, wParam, lParam); + PostMessage(g_pShared->hKbSwitchWnd, Msg, wParam, lParam); } static LRESULT CALLBACK WinHookProc(INT code, WPARAM wParam, LPARAM lParam) { if (code < 0) - return CallNextHookEx(hWinHook, code, wParam, lParam); + return CallNextHookEx(g_pShared->hWinHook, code, wParam, lParam); switch (code) { case HCBT_ACTIVATE: case HCBT_SETFOCUS: { + OutputDebugStringA("HCBT_ACTIVATE / HCBT_SETFOCUS\n"); HWND hwndFocus = (HWND)wParam; - if (hwndFocus && hwndFocus != hKbSwitchWnd) + if (hwndFocus && hwndFocus != g_pShared->hKbSwitchWnd) PostMessageToMainWnd(WM_WINDOW_ACTIVATE, (WPARAM)hwndFocus, 0); break; } } - return CallNextHookEx(hWinHook, code, wParam, lParam); + return CallNextHookEx(g_pShared->hWinHook, code, wParam, lParam); } static LRESULT CALLBACK ShellHookProc(INT code, WPARAM wParam, LPARAM lParam) { if (code < 0) - return CallNextHookEx(hShellHook, code, wParam, lParam); + return CallNextHookEx(g_pShared->hShellHook, code, wParam, lParam); switch (code) { case HSHELL_WINDOWACTIVATED: { + OutputDebugStringA("HSHELL_WINDOWACTIVATED\n"); PostMessageToMainWnd(WM_WINDOW_ACTIVATE, wParam, 0); break; } case HSHELL_LANGUAGE: { + OutputDebugStringA("HSHELL_LANGUAGE\n"); PostMessageToMainWnd(WM_LANG_CHANGED, wParam, lParam); break; } } - return CallNextHookEx(hShellHook, code, wParam, lParam); + return CallNextHookEx(g_pShared->hShellHook, code, wParam, lParam); } static inline BOOL @@ -75,11 +89,11 @@ static LRESULT CALLBACK KeyboardLLHook(INT code, WPARAM wParam, LPARAM lParam) { if (code < 0) - return CallNextHookEx(hKeyboardLLHook, code, wParam, lParam); + return CallNextHookEx(g_pShared->hKeyboardLLHook, code, wParam, lParam); if (code == HC_ACTION) { - KBDLLHOOKSTRUCT *pKbStruct = (KBDLLHOOKSTRUCT *)lParam; + PKBDLLHOOKSTRUCT pKbStruct = (PKBDLLHOOKSTRUCT)lParam; if (wParam == WM_KEYDOWN || wParam == WM_SYSKEYDOWN) { BOOL bShiftPressed = GetAsyncKeyState(VK_SHIFT) < 0; @@ -97,41 +111,69 @@ KeyboardLLHook(INT code, WPARAM wParam, LPARAM lParam) } } - return CallNextHookEx(hKeyboardLLHook, code, wParam, lParam); + return CallNextHookEx(g_pShared->hKeyboardLLHook, code, wParam, lParam); } BOOL APIENTRY KbSwitchSetHooks(_In_ BOOL bDoHook) { + EnterCriticalSection(&g_pShared->csLock); if (bDoHook) { - hWinHook = SetWindowsHookEx(WH_CBT, WinHookProc, hInstance, 0); - hShellHook = SetWindowsHookEx(WH_SHELL, ShellHookProc, hInstance, 0); - hKeyboardLLHook = SetWindowsHookEx(WH_KEYBOARD_LL, KeyboardLLHook, hInstance, 0); + g_pShared->hWinHook = SetWindowsHookEx(WH_CBT, WinHookProc, g_hInstance, 0); + g_pShared->hShellHook = SetWindowsHookEx(WH_SHELL, ShellHookProc, g_hInstance, 0); + g_pShared->hKeyboardLLHook = SetWindowsHookEx(WH_KEYBOARD_LL, KeyboardLLHook, g_hInstance, 0); - if (hWinHook && hShellHook && hKeyboardLLHook) + if (g_pShared->hWinHook && + g_pShared->hShellHook && + g_pShared->hKeyboardLLHook) + { + LeaveCriticalSection(&g_pShared->csLock); return TRUE; + } } /* Unhook */ - if (hKeyboardLLHook) + if (g_pShared->hKeyboardLLHook) { - UnhookWindowsHookEx(hKeyboardLLHook); - hKeyboardLLHook = NULL; + UnhookWindowsHookEx(g_pShared->hKeyboardLLHook); + g_pShared->hKeyboardLLHook = NULL; } - if (hShellHook) + if (g_pShared->hShellHook) { - UnhookWindowsHookEx(hShellHook); - hShellHook = NULL; + UnhookWindowsHookEx(g_pShared->hShellHook); + g_pShared->hShellHook = NULL; } - if (hWinHook) + if (g_pShared->hWinHook) { - UnhookWindowsHookEx(hWinHook); - hWinHook = NULL; + UnhookWindowsHookEx(g_pShared->hWinHook); + g_pShared->hWinHook = NULL; } + + LeaveCriticalSection(&g_pShared->csLock); return !bDoHook; } +// indicdll!12 +VOID APIENTRY +GetPenMenuData(PUINT pnID, PDWORD_PTR pdwItemData) +{ + EnterCriticalSection(&g_pShared->csLock); + *pnID = g_pShared->nHotID; + *pdwItemData = g_pShared->dwHotMenuItemData; + LeaveCriticalSection(&g_pShared->csLock); +} + +// indicdll!14 +VOID APIENTRY +SetPenMenuData(_In_ UINT nID, _In_ DWORD_PTR dwItemData) +{ + EnterCriticalSection(&g_pShared->csLock); + g_pShared->nHotID = nID; + g_pShared->dwHotMenuItemData = dwItemData; + LeaveCriticalSection(&g_pShared->csLock); +} + BOOL WINAPI DllMain(IN HINSTANCE hinstDLL, IN DWORD dwReason, @@ -141,12 +183,37 @@ DllMain(IN HINSTANCE hinstDLL, { case DLL_PROCESS_ATTACH: { - hInstance = hinstDLL; - hKbSwitchWnd = FindWindow(szKbSwitcherName, NULL); - if (!hKbSwitchWnd) + g_hInstance = hinstDLL; + g_hShared = CreateFileMapping(INVALID_HANDLE_VALUE, NULL, PAGE_READWRITE, + 0, sizeof(SHARED_DATA), TEXT("InternatSHData")); + if (!g_hShared) return FALSE; + + BOOL bAlreadyExists = GetLastError() == ERROR_ALREADY_EXISTS; + + g_pShared = (PSHARED_DATA)MapViewOfFile(g_hShared, FILE_MAP_WRITE, 0, 0, 0); + if (!g_pShared) + return FALSE; + + if (!bAlreadyExists) + { + ZeroMemory(g_pShared, sizeof(*g_pShared)); + g_pShared->hKbSwitchWnd = FindWindow(INDICATOR_CLASS, NULL); + InitializeCriticalSection(&g_pShared->csLock); + g_bCriticalSectionInitialized = TRUE; + } + break; + } + case DLL_PROCESS_DETACH: + { + if (g_bCriticalSectionInitialized) + { + DeleteCriticalSection(&g_pShared->csLock); + } + UnmapViewOfFile(g_pShared); + CloseHandle(g_hShared); + break; } - break; } return TRUE; diff --git a/base/applications/kbswitch/indicdll/indicdll.rc b/base/applications/kbswitch/indicdll/indicdll.rc index 78d6fb8f087..b7a8d960230 100644 --- a/base/applications/kbswitch/indicdll/indicdll.rc +++ b/base/applications/kbswitch/indicdll/indicdll.rc @@ -1,5 +1,15 @@ +#include "resource.h" + #define REACTOS_VERSION_DLL #define REACTOS_STR_FILE_DESCRIPTION "ReactOS Keyboard Layout Switcher" #define REACTOS_STR_INTERNAL_NAME "indicdll" #define REACTOS_STR_ORIGINAL_FILENAME "indicdll.dll" #include + +IDI_IME_OPEN ICON "res/10.ico" +IDI_IME_CLOSED ICON "res/11.ico" +IDI_IME_DISABLED ICON "res/12.ico" +IDI_KOREAN_A_HALF ICON "res/13.ico" +IDI_KOREAN_A_FULL ICON "res/14.ico" +IDI_KOREAN_JR_HALF ICON "res/15.ico" +IDI_KOREAN_JR_FULL ICON "res/16.ico" diff --git a/base/applications/kbswitch/indicdll/indicdll.spec b/base/applications/kbswitch/indicdll/indicdll.spec index bf2ebbdd33a..4b4ce8b2c9a 100644 --- a/base/applications/kbswitch/indicdll/indicdll.spec +++ b/base/applications/kbswitch/indicdll/indicdll.spec @@ -1 +1,3 @@ 1 stdcall KbSwitchSetHooks(long) +12 stdcall GetPenMenuData(ptr ptr) +14 stdcall SetPenMenuData(long long) diff --git a/base/applications/kbswitch/indicdll/res/10.ico b/base/applications/kbswitch/indicdll/res/10.ico new file mode 100644 index 00000000000..36ba4c1ab9f Binary files /dev/null and b/base/applications/kbswitch/indicdll/res/10.ico differ diff --git a/base/applications/kbswitch/indicdll/res/11.ico b/base/applications/kbswitch/indicdll/res/11.ico new file mode 100644 index 00000000000..3751cc9b81c Binary files /dev/null and b/base/applications/kbswitch/indicdll/res/11.ico differ diff --git a/base/applications/kbswitch/indicdll/res/12.ico b/base/applications/kbswitch/indicdll/res/12.ico new file mode 100644 index 00000000000..f2e467f2575 Binary files /dev/null and b/base/applications/kbswitch/indicdll/res/12.ico differ diff --git a/base/applications/kbswitch/indicdll/res/13.ico b/base/applications/kbswitch/indicdll/res/13.ico new file mode 100644 index 00000000000..f9c6b941fc1 Binary files /dev/null and b/base/applications/kbswitch/indicdll/res/13.ico differ diff --git a/base/applications/kbswitch/indicdll/res/14.ico b/base/applications/kbswitch/indicdll/res/14.ico new file mode 100644 index 00000000000..84dc02884ae Binary files /dev/null and b/base/applications/kbswitch/indicdll/res/14.ico differ diff --git a/base/applications/kbswitch/indicdll/res/15.ico b/base/applications/kbswitch/indicdll/res/15.ico new file mode 100644 index 00000000000..5da20543f2d Binary files /dev/null and b/base/applications/kbswitch/indicdll/res/15.ico differ diff --git a/base/applications/kbswitch/indicdll/res/16.ico b/base/applications/kbswitch/indicdll/res/16.ico new file mode 100644 index 00000000000..4b459f4c891 Binary files /dev/null and b/base/applications/kbswitch/indicdll/res/16.ico differ diff --git a/base/applications/kbswitch/indicdll/resource.h b/base/applications/kbswitch/indicdll/resource.h new file mode 100644 index 00000000000..ee385abd44b --- /dev/null +++ b/base/applications/kbswitch/indicdll/resource.h @@ -0,0 +1,8 @@ + +#define IDI_IME_OPEN 10 +#define IDI_IME_CLOSED 11 +#define IDI_IME_DISABLED 12 +#define IDI_KOREAN_A_HALF 13 +#define IDI_KOREAN_A_FULL 14 +#define IDI_KOREAN_JR_HALF 15 +#define IDI_KOREAN_JR_FULL 16 diff --git a/base/applications/kbswitch/kbswitch.c b/base/applications/kbswitch/kbswitch.c index 94e0c082e7e..16f7ed71780 100644 --- a/base/applications/kbswitch/kbswitch.c +++ b/base/applications/kbswitch/kbswitch.c @@ -9,8 +9,11 @@ #include "kbswitch.h" #include #include +#include #include +#include #include +#include "imemenu.h" #include WINE_DEFAULT_DEBUG_CHANNEL(internat); @@ -29,12 +32,22 @@ WINE_DEFAULT_DEBUG_CHANNEL(internat); * won't be generated in Vista+. */ -#define WM_NOTIFYICONMSG (WM_USER + 248) +#define WM_NOTIFYICONMSG 0x8064 +#define WM_PENICONMSG 0x8065 + +#define NOTIFY_ICON_ID_LANGUAGE 223 +#define NOTIFY_ICON_ID_SYSTEM_PEN 224 #define TIMER_ID_LANG_CHANGED_DELAYED 0x10000 #define TIMER_LANG_CHANGED_DELAY 200 +#define MAX_KLS 64 + +typedef BOOL (APIENTRY *FN_KbSwitchSetHooks)(BOOL bDoHook); +typedef VOID (APIENTRY *FN_SetPenMenuData)(UINT nID, DWORD_PTR dwItemData); // indic!14 + FN_KbSwitchSetHooks KbSwitchSetHooks = NULL; +FN_SetPenMenuData SetPenMenuData = NULL; HINSTANCE g_hInst = NULL; HMODULE g_hHookDLL = NULL; @@ -42,9 +55,29 @@ HICON g_hTrayIcon = NULL; HWND g_hwndLastActive = NULL; UINT g_iKL = 0; UINT g_cKLs = 0; -HKL g_ahKLs[64]; +HKL g_ahKLs[MAX_KLS] = { NULL }; +WORD g_aiSysPenIcons[MAX_KLS] = { 0 }; +WORD g_anToolTipAtoms[MAX_KLS] = { 0 }; +HICON g_ahSysPenIcons[MAX_KLS] = { NULL }; +BYTE g_anFlags[MAX_KLS] = { 0 }; UINT g_uTaskbarRestartMsg = 0; UINT g_uShellHookMessage = 0; +HWND g_hTrayWnd = NULL; +HWND g_hTrayNotifyWnd = NULL; + +// Flags for g_anFlags +#define LAYOUTF_FAR_EAST 0x1 +#define LAYOUTF_IME_ICON 0x2 +#define LAYOUTF_TOOLTIP_ATOM 0x4 +#define LAYOUTF_REMOVE_LEFT_DEF_MENU 0x8 +#define LAYOUTF_REMOVE_RIGHT_DEF_MENU 0x10 + +static VOID +UpdateTrayInfo(VOID) +{ + g_hTrayWnd = FindWindow(TEXT("Shell_TrayWnd"), NULL); + g_hTrayNotifyWnd = FindWindowEx(g_hTrayWnd, NULL, TEXT("TrayNotifyWnd"), NULL); +} typedef struct tagSPECIAL_ID { @@ -139,32 +172,126 @@ GetKLIDFromHKL(HKL hKL, LPTSTR szKLID, SIZE_T KLIDLength) } } +static HWND +GetTargetWindow(HWND hwndFore OPTIONAL) +{ + HWND hwndTarget = (hwndFore ? hwndFore : GetForegroundWindow()); + if (!hwndTarget || + IsWndClassName(hwndTarget, INDICATOR_CLASS) || + IsWndClassName(hwndTarget, TEXT("Shell_TrayWnd"))) + { + hwndTarget = g_hwndLastActive; + } + return hwndTarget; +} + static HKL GetActiveKL(VOID) { /* FIXME: Get correct console window's HKL when console window */ - HWND hwndTarget = (g_hwndLastActive ? g_hwndLastActive : GetForegroundWindow()); + HWND hwndTarget = GetTargetWindow(NULL); + ERR("hwndTarget: %p\n", hwndTarget); DWORD dwTID = GetWindowThreadProcessId(hwndTarget, NULL); return GetKeyboardLayout(dwTID); } +static VOID +DeletePenIcon(HWND hwnd, UINT iKL) +{ + UNREFERENCED_PARAMETER(hwnd); + + if (g_ahSysPenIcons[iKL]) + { + DestroyIcon(g_ahSysPenIcons[iKL]); + g_ahSysPenIcons[iKL] = NULL; + } +} + +static VOID +DestroyPenIcons(VOID) +{ + INT iKL; + for (iKL = 0; iKL < g_cKLs; ++iKL) + { + if (g_ahSysPenIcons[iKL]) + { + DestroyIcon(g_ahSysPenIcons[iKL]); + g_ahSysPenIcons[iKL] = NULL; + } + } +} + +static UINT +GetLayoutIndexFromHKL(HKL hKL) +{ + for (UINT iKL = 0; iKL < g_cKLs; ++iKL) + { + if (g_ahKLs[iKL] == hKL) + return iKL; + } + return 0; +} + static VOID UpdateLayoutList(HKL hKL OPTIONAL) { - UINT iKL; - if (!hKL) hKL = GetActiveKL(); - g_cKLs = GetKeyboardLayoutList(_countof(g_ahKLs), g_ahKLs); + HICON ahSysPenIcons[MAX_KLS]; + WORD aiSysPenIcons[MAX_KLS]; + BYTE anFlags[MAX_KLS]; + ZeroMemory(ahSysPenIcons, sizeof(ahSysPenIcons)); + FillMemory(aiSysPenIcons, sizeof(aiSysPenIcons), 0xFF); + ZeroMemory(anFlags, sizeof(anFlags)); - g_iKL = 0; - for (iKL = 0; iKL < g_cKLs; ++iKL) + HKL ahKLs[MAX_KLS]; + UINT iKL, nKLs = GetKeyboardLayoutList(_countof(ahKLs), ahKLs); + + /* Reuse old icons and flags */ + for (iKL = 0; iKL < nKLs; ++iKL) { - if (g_ahKLs[iKL] == hKL) + LANGID wLangID = LOWORD(ahKLs[iKL]); + switch (wLangID) { - g_iKL = iKL; - break; + case LANGID_CHINESE_SIMPLIFIED: + case LANGID_CHINESE_TRADITIONAL: + case LANGID_JAPANESE: + case LANGID_KOREAN: + anFlags[iKL] |= LAYOUTF_FAR_EAST; + break; + default: + anFlags[iKL] &= ~LAYOUTF_FAR_EAST; + break; + } + + UINT iKL2; + for (iKL2 = 0; iKL2 < g_cKLs; ++iKL2) + { + if (ahKLs[iKL] == g_ahKLs[iKL2]) + { + ahSysPenIcons[iKL] = g_ahSysPenIcons[iKL2]; + aiSysPenIcons[iKL] = g_aiSysPenIcons[iKL2]; + anFlags[iKL] = g_anFlags[iKL2]; + + g_ahSysPenIcons[iKL2] = NULL; + break; + } } } + + DestroyPenIcons(); + + g_cKLs = nKLs; + + C_ASSERT(sizeof(g_ahKLs) == sizeof(ahKLs)); + CopyMemory(g_ahKLs, ahKLs, sizeof(g_ahKLs)); + + C_ASSERT(sizeof(g_ahSysPenIcons) == sizeof(ahSysPenIcons)); + CopyMemory(g_ahSysPenIcons, ahSysPenIcons, sizeof(g_ahSysPenIcons)); + + C_ASSERT(sizeof(g_anFlags) == sizeof(anFlags)); + CopyMemory(g_anFlags, anFlags, sizeof(g_anFlags)); + + g_iKL = GetLayoutIndexFromHKL(hKL); } static HKL GetHKLFromLayoutNum(UINT iKL) @@ -260,7 +387,9 @@ static BOOL GetImeFile(LPTSTR szImeFile, SIZE_T cchImeFile, LPCTSTR szKLID) typedef struct tagLOAD_ICON { + INT nIconIndex; INT cxIcon, cyIcon; + INT iIcon; HICON hIcon; } LOAD_ICON, *PLOAD_ICON; @@ -271,24 +400,37 @@ EnumResNameProc( LPTSTR lpszName, LPARAM lParam) { + UNREFERENCED_PARAMETER(lpszType); + PLOAD_ICON pLoadIcon = (PLOAD_ICON)lParam; - pLoadIcon->hIcon = (HICON)LoadImage(hModule, lpszName, IMAGE_ICON, - pLoadIcon->cxIcon, pLoadIcon->cyIcon, - LR_DEFAULTCOLOR); - if (pLoadIcon->hIcon) - return FALSE; /* Stop enumeration */ + if (pLoadIcon->iIcon == pLoadIcon->nIconIndex) + { + pLoadIcon->hIcon = (HICON)LoadImage(hModule, lpszName, IMAGE_ICON, + pLoadIcon->cxIcon, pLoadIcon->cyIcon, + LR_DEFAULTCOLOR); + if (pLoadIcon->hIcon) + return FALSE; /* Stop enumeration */ + } + + ++pLoadIcon->iIcon; return TRUE; } -static HICON FakeExtractIcon(LPCTSTR szIconPath, INT cxIcon, INT cyIcon) +static HICON +FakeExtractIcon(PCTSTR pszImeFile, INT nIconIndex) { - LOAD_ICON LoadIcon = { cxIcon, cyIcon, NULL }; - HMODULE hImeDLL = LoadLibraryEx(szIconPath, NULL, LOAD_LIBRARY_AS_DATAFILE); - if (hImeDLL) + HMODULE hImeDLL = LoadLibraryEx(pszImeFile, NULL, LOAD_LIBRARY_AS_DATAFILE); + if (!hImeDLL) + return NULL; + + LOAD_ICON LoadIcon = { - EnumResourceNames(hImeDLL, RT_GROUP_ICON, EnumResNameProc, (LPARAM)&LoadIcon); - FreeLibrary(hImeDLL); - } + nIconIndex, GetSystemMetrics(SM_CXSMICON), GetSystemMetrics(SM_CXSMICON) + }; + EnumResourceNames(hImeDLL, RT_GROUP_ICON, EnumResNameProc, (LPARAM)&LoadIcon); + + FreeLibrary(hImeDLL); + return LoadIcon.hIcon; } @@ -313,6 +455,141 @@ static HBITMAP BitmapFromIcon(HICON hIcon) return hbm; } +static DWORD +GetImeStatus(HWND hwndTarget) +{ + HWND hwndIme = ImmGetDefaultIMEWnd(hwndTarget); + if (!hwndIme) + return IME_STATUS_IME_CLOSED; + + HIMC hIMC = (HIMC)SendMessage(hwndIme, WM_IME_SYSTEM, IMS_GETCONTEXT, (LPARAM)hwndTarget); + if (!hIMC) + return IME_STATUS_NO_IME; + + DWORD dwImeStatus = (ImmGetOpenStatus(hIMC) ? IME_STATUS_IME_OPEN : IME_STATUS_IME_CLOSED); + if (GetACP() == 949) // Korean + { + DWORD dwConversion = 0, dwSentence = 0; + if (ImmGetConversionStatus(hIMC, &dwConversion, &dwSentence)) + { + if (dwConversion & IME_CMODE_NATIVE) + dwImeStatus |= IME_STATUS_IME_NATIVE; + + if (dwConversion & IME_CMODE_FULLSHAPE) + dwImeStatus |= IME_STATUS_IME_FULLSHAPE; + } + } + + return dwImeStatus; +} + +static HICON +LoadDefaultPenIcon(PCWSTR szImeFile, HKL hKL) +{ + HWND hwndTarget = g_hwndLastActive ? g_hwndLastActive : GetForegroundWindow(); + DWORD dwImeStatus = GetImeStatus(hwndTarget); + + INT nIconID = -1; + if ((HandleToUlong(hKL) & 0xF000FFFF) == 0xE0000412) // Special Korean IME + { + if (dwImeStatus != IME_STATUS_NO_IME) + { + if (dwImeStatus & IME_STATUS_IME_CLOSED) + { + nIconID = IDI_KOREAN_A_HALF; + } + else + { + if (dwImeStatus & IME_STATUS_IME_FULLSHAPE) + { + if (dwImeStatus & IME_STATUS_IME_NATIVE) + nIconID = IDI_KOREAN_JR_FULL; + else + nIconID = IDI_KOREAN_A_FULL; + } + else + { + if (dwImeStatus & IME_STATUS_IME_NATIVE) + nIconID = IDI_KOREAN_JR_HALF; + else + nIconID = IDI_KOREAN_A_HALF; + } + } + } + } + else + { + if (dwImeStatus & IME_STATUS_IME_CLOSED) + nIconID = IDI_IME_CLOSED; + else if (dwImeStatus & IME_STATUS_IME_OPEN) + nIconID = IDI_IME_OPEN; + else + nIconID = IDI_IME_DISABLED; + } + + if (nIconID < 0) + return NULL; + + return LoadIcon(g_hHookDLL, MAKEINTRESOURCE(nIconID)); +} + +static VOID +DeletePenNotifyIcon(HWND hwnd) +{ + NOTIFYICONDATA nid = { sizeof(nid), hwnd, NOTIFY_ICON_ID_SYSTEM_PEN }; + Shell_NotifyIcon(NIM_DELETE, &nid); +} + +static VOID +UpdatePenIcon(HWND hwnd, UINT iKL) +{ + BOOL bHadIcon = (g_ahSysPenIcons[iKL] != NULL); + DeletePenIcon(hwnd, iKL); + + // Not Far-East? + if (!(g_anFlags[iKL] & LAYOUTF_FAR_EAST)) + { + if (bHadIcon) + DeletePenNotifyIcon(hwnd); + return; + } + + // Get IME file + TCHAR szKLID[CCH_LAYOUT_ID + 1], szImeFile[MAX_PATH]; + GetKLIDFromHKL(g_ahKLs[iKL], szKLID, _countof(szKLID)); + if (!GetImeFile(szImeFile, _countof(szImeFile), szKLID)) + { + if (bHadIcon) + DeletePenNotifyIcon(hwnd); + return; + } + + // Load pen icon + if (g_anFlags[iKL] & LAYOUTF_IME_ICON) + g_ahSysPenIcons[iKL] = FakeExtractIcon(szImeFile, g_aiSysPenIcons[iKL]); + if (!g_ahSysPenIcons[iKL]) + g_ahSysPenIcons[iKL] = LoadDefaultPenIcon(szImeFile, g_ahKLs[iKL]); + if (!g_ahSysPenIcons[iKL]) + { + if (bHadIcon) + DeletePenNotifyIcon(hwnd); + return; + } + + // Add pen icon + NOTIFYICONDATA nid = { sizeof(nid), hwnd, NOTIFY_ICON_ID_SYSTEM_PEN }; + nid.uFlags = NIF_ICON | NIF_MESSAGE | NIF_TIP; + nid.uCallbackMessage = WM_PENICONMSG; + nid.hIcon = g_ahSysPenIcons[iKL]; + + if (g_anToolTipAtoms[iKL]) + GlobalGetAtomName(g_anToolTipAtoms[iKL], nid.szTip, _countof(nid.szTip)); + else + ImmGetDescription(g_ahKLs[iKL], nid.szTip, _countof(nid.szTip)); + + Shell_NotifyIcon((bHadIcon ? NIM_MODIFY : NIM_ADD), &nid); +} + static HICON CreateTrayIcon(LPTSTR szKLID, LPCTSTR szImeFile OPTIONAL) { @@ -332,7 +609,7 @@ CreateTrayIcon(LPTSTR szKLID, LPCTSTR szImeFile OPTIONAL) if (szImeFile && szImeFile[0]) { if (GetSystemLibraryPath(szPath, _countof(szPath), szImeFile)) - return FakeExtractIcon(szPath, cxIcon, cyIcon); + return FakeExtractIcon(szPath, 0); } /* Getting "EN", "FR", etc. from English, French, ... */ @@ -413,7 +690,10 @@ CreateTrayIcon(LPTSTR szKLID, LPCTSTR szImeFile OPTIONAL) static VOID AddTrayIcon(HWND hwnd) { - NOTIFYICONDATA tnid = { sizeof(tnid), hwnd, 1, NIF_ICON | NIF_MESSAGE | NIF_TIP }; + NOTIFYICONDATA tnid = + { + sizeof(tnid), hwnd, NOTIFY_ICON_ID_LANGUAGE, NIF_ICON | NIF_MESSAGE | NIF_TIP + }; TCHAR szKLID[CCH_LAYOUT_ID + 1], szName[MAX_PATH], szImeFile[80]; GetKLIDFromLayoutNum(g_iKL, szKLID, _countof(szKLID)); @@ -434,7 +714,7 @@ AddTrayIcon(HWND hwnd) static VOID DeleteTrayIcon(HWND hwnd) { - NOTIFYICONDATA tnid = { sizeof(tnid), hwnd, 1 }; + NOTIFYICONDATA tnid = { sizeof(tnid), hwnd, NOTIFY_ICON_ID_LANGUAGE }; Shell_NotifyIcon(NIM_DELETE, &tnid); if (g_hTrayIcon) @@ -447,7 +727,10 @@ DeleteTrayIcon(HWND hwnd) static VOID UpdateTrayIcon(HWND hwnd, LPTSTR szKLID, LPTSTR szName) { - NOTIFYICONDATA tnid = { sizeof(tnid), hwnd, 1, NIF_ICON | NIF_MESSAGE | NIF_TIP }; + NOTIFYICONDATA tnid = + { + sizeof(tnid), hwnd, NOTIFY_ICON_ID_LANGUAGE, NIF_ICON | NIF_MESSAGE | NIF_TIP + }; TCHAR szImeFile[80]; GetImeFile(szImeFile, _countof(szImeFile), szKLID); @@ -546,19 +829,22 @@ BuildLeftPopupMenu(VOID) return hMenu; } +#define IFN_KbSwitchSetHooks 1 +#define IFN_SetPenMenuData 14 + static BOOL SetHooks(VOID) { g_hHookDLL = LoadLibrary(_T("indicdll.dll")); if (!g_hHookDLL) - { return FALSE; - } -#define IHOOK_SET 1 - KbSwitchSetHooks = (FN_KbSwitchSetHooks)GetProcAddress(g_hHookDLL, MAKEINTRESOURCEA(IHOOK_SET)); + KbSwitchSetHooks = + (FN_KbSwitchSetHooks)GetProcAddress(g_hHookDLL, MAKEINTRESOURCEA(IFN_KbSwitchSetHooks)); + SetPenMenuData = + (FN_SetPenMenuData)GetProcAddress(g_hHookDLL, MAKEINTRESOURCEA(IFN_SetPenMenuData)); - if (!KbSwitchSetHooks || !KbSwitchSetHooks(TRUE)) + if (!KbSwitchSetHooks || !SetPenMenuData || !KbSwitchSetHooks(TRUE)) { ERR("SetHooks failed\n"); return FALSE; @@ -614,15 +900,6 @@ UpdateLanguageDisplay(HWND hwnd, HKL hKL) return 0; } -HWND -GetTargetWindow(HWND hwndFore OPTIONAL) -{ - HWND hwndTarget = (hwndFore ? hwndFore : GetForegroundWindow()); - if (IsWndClassName(hwndTarget, szKbSwitcherName)) - hwndTarget = g_hwndLastActive; - return hwndTarget; -} - UINT UpdateLanguageDisplayCurrent(HWND hwnd, HWND hwndFore) { @@ -640,7 +917,7 @@ static BOOL RememberLastActive(HWND hwnd, HWND hwndFore) if (!IsWindowVisible(hwndFore)) return FALSE; - if (IsWndClassName(hwndFore, szKbSwitcherName) || + if (IsWndClassName(hwndFore, INDICATOR_CLASS) || IsWndClassName(hwndFore, TEXT("Shell_TrayWnd"))) { return FALSE; /* Special window */ @@ -661,9 +938,10 @@ KbSwitch_OnCreate(HWND hwnd) } LoadSpecialIds(); - + UpdateTrayInfo(); UpdateLayoutList(NULL); AddTrayIcon(hwnd); + UpdatePenIcon(hwnd, g_iKL); ActivateLayout(hwnd, g_iKL, NULL, TRUE); g_uTaskbarRestartMsg = RegisterWindowMessage(TEXT("TaskbarCreated")); @@ -678,6 +956,7 @@ KbSwitch_OnDestroy(HWND hwnd) KillTimer(hwnd, TIMER_ID_LANG_CHANGED_DELAYED); DeleteHooks(); DeleteTrayIcon(hwnd); + DestroyPenIcons(); PostQuitMessage(0); } @@ -691,6 +970,7 @@ KbSwitch_OnTimer(HWND hwnd, UINT_PTR nTimerID) HKL hKL = GetActiveKL(); UpdateLayoutList(hKL); UpdateLanguageDisplay(hwnd, hKL); + UpdatePenIcon(hwnd, g_iKL); } } @@ -698,7 +978,7 @@ KbSwitch_OnTimer(HWND hwnd, UINT_PTR nTimerID) static void KbSwitch_OnNotifyIconMsg(HWND hwnd, UINT uMouseMsg) { - if (uMouseMsg != WM_LBUTTONUP && uMouseMsg != WM_RBUTTONUP && uMouseMsg != WM_CONTEXTMENU) + if (uMouseMsg != WM_LBUTTONUP && uMouseMsg != WM_RBUTTONUP) return; UpdateLayoutList(NULL); @@ -708,21 +988,24 @@ KbSwitch_OnNotifyIconMsg(HWND hwnd, UINT uMouseMsg) SetForegroundWindow(hwnd); + TPMPARAMS params = { sizeof(params) }; + GetWindowRect(g_hTrayNotifyWnd, ¶ms.rcExclude); + INT nID; if (uMouseMsg == WM_LBUTTONUP) { /* Rebuild the left popup menu on every click to take care of keyboard layout changes */ HMENU hPopupMenu = BuildLeftPopupMenu(); - nID = TrackPopupMenuEx(hPopupMenu, TPM_LEFTALIGN | TPM_RETURNCMD | TPM_LEFTBUTTON, - pt.x, pt.y, hwnd, NULL); + UINT uFlags = TPM_VERTICAL | TPM_RIGHTALIGN | TPM_RETURNCMD | TPM_LEFTBUTTON; + nID = TrackPopupMenuEx(hPopupMenu, uFlags, pt.x, pt.y, hwnd, ¶ms); DestroyMenu(hPopupMenu); } - else /* WM_RBUTTONUP or WM_CONTEXTMENU */ + else /* WM_RBUTTONUP */ { HMENU hPopupMenu = LoadMenu(g_hInst, MAKEINTRESOURCE(IDR_POPUP)); HMENU hSubMenu = GetSubMenu(hPopupMenu, 0); - nID = TrackPopupMenuEx(hSubMenu, TPM_LEFTALIGN | TPM_RETURNCMD | TPM_RIGHTBUTTON, - pt.x, pt.y, hwnd, NULL); + UINT uFlags = TPM_VERTICAL | TPM_RIGHTALIGN | TPM_RETURNCMD | TPM_RIGHTBUTTON; + nID = TrackPopupMenuEx(hSubMenu, uFlags, pt.x, pt.y, hwnd, ¶ms); DestroyMenu(hPopupMenu); } @@ -732,6 +1015,96 @@ KbSwitch_OnNotifyIconMsg(HWND hwnd, UINT uMouseMsg) PostMessage(hwnd, WM_COMMAND, nID, 0); } +// WM_PENICONMSG +static VOID +KbSwitch_OnPenIconMsg(HWND hwnd, UINT uMouseMsg) +{ + if (uMouseMsg != WM_LBUTTONUP && uMouseMsg != WM_RBUTTONUP) + return; + + if (!(g_anFlags[g_iKL] & LAYOUTF_FAR_EAST)) + return; + + POINT pt; + GetCursorPos(&pt); + + ERR("g_hwndLastActive: %p\n", g_hwndLastActive); + HWND hwndTarget = GetTargetWindow(g_hwndLastActive); + ERR("hwndTarget: %p\n", hwndTarget); + + HWND hwndIme = ImmGetDefaultIMEWnd(hwndTarget); + if (!hwndIme) + { + WARN("No default IME\n"); + return; + } + + HIMC hIMC = (HIMC)SendMessage(hwndIme, WM_IME_SYSTEM, IMS_GETCONTEXT, (LPARAM)hwndTarget); + if (!hIMC) + { + WARN("No HIMC\n"); + return; + } + + SetForegroundWindow(hwnd); + + BOOL bRightButton = (uMouseMsg == WM_RBUTTONUP); + PIMEMENUNODE pImeMenu = CreateImeMenu(hIMC, NULL, bRightButton); + HMENU hMenu = MenuFromImeMenu(pImeMenu); + + if (bRightButton) + { + if (!(g_anFlags[g_iKL] & LAYOUTF_REMOVE_RIGHT_DEF_MENU)) + { + // FIXME: Add default menu items + } + } + else + { + if (!(g_anFlags[g_iKL] & LAYOUTF_REMOVE_LEFT_DEF_MENU)) + { + // FIXME: Add default menu items + } + } + + UINT uFlags = TPM_VERTICAL | TPM_RIGHTALIGN | TPM_RETURNCMD; + uFlags |= (bRightButton ? TPM_RIGHTBUTTON : TPM_LEFTBUTTON); + + TPMPARAMS params = { sizeof(params) }; + GetWindowRect(g_hTrayNotifyWnd, ¶ms.rcExclude); + + INT nID = TrackPopupMenuEx(hMenu, uFlags, pt.x, pt.y, hwnd, ¶ms); + + PostMessage(hwnd, WM_NULL, 0, 0); + + if (nID) + { + if (nID >= ID_STARTIMEMENU) + { + MENUITEMINFO mii = { sizeof(mii), MIIM_DATA }; + GetMenuItemInfo(hMenu, nID, FALSE, &mii); + + if (pImeMenu) + nID = GetRealImeMenuID(pImeMenu, nID); + + if (SetPenMenuData) + SetPenMenuData(nID, mii.dwItemData); + + if (IsWindow(hwndIme)) + SendMessage(hwndIme, WM_IME_SYSTEM, IMS_IMEMENUITEMSELECTED, (LPARAM)hwndTarget); + } + else + { + PostMessage(hwnd, WM_COMMAND, nID, 0); + } + } + + DestroyMenu(hMenu); + CleanupImeMenus(); + + SetForegroundWindow(hwndTarget); +} + // WM_COMMAND static void KbSwitch_OnCommand(HWND hwnd, UINT nID) @@ -802,8 +1175,10 @@ KbSwitch_OnDefault(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) { if (uMsg == g_uTaskbarRestartMsg) { + UpdateTrayInfo(); UpdateLayoutList(NULL); AddTrayIcon(hwnd); + UpdatePenIcon(hwnd, g_iKL); return 0; } @@ -820,6 +1195,67 @@ KbSwitch_OnDefault(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) return DefWindowProc(hwnd, uMsg, wParam, lParam); } +static VOID +OnIndicatorMsg(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) +{ + HKL hKL = (HKL)lParam; + UINT iKL = GetLayoutIndexFromHKL(hKL); + if (iKL >= g_cKLs) + return; + + g_iKL = iKL; + + switch (uMsg) + { + case INDICM_SETIMEICON: + if (LOWORD(wParam) == MAXWORD) + { + g_anFlags[iKL] &= ~LAYOUTF_IME_ICON; + g_aiSysPenIcons[iKL] = MAXWORD; + } + else + { + g_anFlags[iKL] |= LAYOUTF_IME_ICON; + g_aiSysPenIcons[iKL] = (WORD)wParam; + } + UpdatePenIcon(hwnd, iKL); + break; + + case INDICM_SETIMETOOLTIPS: + if (LOWORD(wParam) == MAXWORD) + { + g_anFlags[iKL] &= ~LAYOUTF_TOOLTIP_ATOM; + } + else + { + g_anFlags[iKL] |= LAYOUTF_TOOLTIP_ATOM; + g_anToolTipAtoms[iKL] = LOWORD(wParam); + } + UpdatePenIcon(hwnd, iKL); + break; + + case INDICM_REMOVEDEFAULTMENUITEMS: + if (wParam) + { + if (wParam & RDMI_LEFT) + g_anFlags[iKL] |= LAYOUTF_REMOVE_LEFT_DEF_MENU; + if (wParam & RDMI_RIGHT) + g_anFlags[iKL] |= LAYOUTF_REMOVE_RIGHT_DEF_MENU; + } + else + { + g_anFlags[iKL] &= ~(LAYOUTF_REMOVE_LEFT_DEF_MENU | LAYOUTF_REMOVE_RIGHT_DEF_MENU); + } + break; + + default: + { + ERR("uMsg: %u\n", uMsg); + return; + } + } +} + LRESULT CALLBACK WndProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) { @@ -842,6 +1278,10 @@ WndProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) KbSwitch_OnNotifyIconMsg(hwnd, (UINT)lParam); break; + case WM_PENICONMSG: + KbSwitch_OnPenIconMsg(hwnd, (UINT)lParam); + break; + case WM_COMMAND: KbSwitch_OnCommand(hwnd, LOWORD(wParam)); break; @@ -854,6 +1294,18 @@ WndProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) KbSwitch_OnDestroy(hwnd); break; + case INDICM_SETIMEICON: + case INDICM_SETIMETOOLTIPS: + case INDICM_REMOVEDEFAULTMENUITEMS: + if (InSendMessageEx(NULL)) + break; /* Must be a PostMessage call for quick response, not SendMessage */ + + OnIndicatorMsg(hwnd, uMsg, wParam, lParam); + break; + + case WM_INPUTLANGCHANGEREQUEST: + break; + default: return KbSwitch_OnDefault(hwnd, uMsg, wParam, lParam); } @@ -879,7 +1331,7 @@ _tWinMain(HINSTANCE hInstance, HINSTANCE hPrevInst, LPTSTR lpCmdLine, INT nCmdSh break; } - hMutex = CreateMutex(NULL, FALSE, szKbSwitcherName); + hMutex = CreateMutex(NULL, FALSE, INDICATOR_CLASS); if (!hMutex) { ERR("!hMutex\n"); @@ -899,14 +1351,14 @@ _tWinMain(HINSTANCE hInstance, HINSTANCE hPrevInst, LPTSTR lpCmdLine, INT nCmdSh WndClass.lpfnWndProc = WndProc; WndClass.hInstance = hInstance; WndClass.hCursor = LoadCursor(NULL, IDC_ARROW); - WndClass.lpszClassName = szKbSwitcherName; + WndClass.lpszClassName = INDICATOR_CLASS; if (!RegisterClass(&WndClass)) { CloseHandle(hMutex); return 1; } - hwnd = CreateWindow(szKbSwitcherName, NULL, 0, 0, 0, 1, 1, HWND_DESKTOP, NULL, hInstance, NULL); + hwnd = CreateWindow(INDICATOR_CLASS, NULL, 0, 0, 0, 1, 1, HWND_DESKTOP, NULL, hInstance, NULL); g_uShellHookMessage = RegisterWindowMessage(L"SHELLHOOK"); if (!RegisterShellHookWindow(hwnd)) { diff --git a/base/applications/kbswitch/kbswitch.h b/base/applications/kbswitch/kbswitch.h index 41907a8d57d..8117ae7dfb3 100644 --- a/base/applications/kbswitch/kbswitch.h +++ b/base/applications/kbswitch/kbswitch.h @@ -13,16 +13,25 @@ #include /* INDICATOR_CLASS, INDICM_... */ #include "resource.h" +#include "indicdll/resource.h" #define CCH_LAYOUT_ID 8 // Character Count of a layout ID like "00000409" #define CCH_ULONG_DEC 10 // Maximum Character Count of a ULONG in decimal +// Far East Language IDs +#define LANGID_CHINESE_SIMPLIFIED MAKELANGID(LANG_CHINESE, SUBLANG_CHINESE_SIMPLIFIED) +#define LANGID_CHINESE_TRADITIONAL MAKELANGID(LANG_CHINESE, SUBLANG_CHINESE_TRADITIONAL) +#define LANGID_JAPANESE MAKELANGID(LANG_JAPANESE, SUBLANG_DEFAULT) +#define LANGID_KOREAN MAKELANGID(LANG_KOREAN, SUBLANG_DEFAULT) + #define WM_LANG_CHANGED (WM_USER + 10200) #define WM_WINDOW_ACTIVATE (WM_USER + 10300) -typedef BOOL (APIENTRY *FN_KbSwitchSetHooks)(BOOL bDoHook); - -const TCHAR szKbSwitcherName[] = INDICATOR_CLASS; +#define IME_STATUS_NO_IME 0 +#define IME_STATUS_IME_CLOSED 1 +#define IME_STATUS_IME_OPEN 2 +#define IME_STATUS_IME_NATIVE 4 +#define IME_STATUS_IME_FULLSHAPE 8 static inline BOOL IsWndClassName(_In_opt_ HWND hwndTarget, PCTSTR pszName) diff --git a/base/applications/kbswitch/resource.h b/base/applications/kbswitch/resource.h index f74b0792f60..3a1109b6aaa 100644 --- a/base/applications/kbswitch/resource.h +++ b/base/applications/kbswitch/resource.h @@ -1,7 +1,7 @@ #pragma once /* Icons */ -#define IDI_MAIN 100 +#define IDI_MAIN 150 /* Menus */ #define IDR_POPUP 100