From 586989350bb894b8e2034b172abb17c929bd26e5 Mon Sep 17 00:00:00 2001 From: Katayama Hirofumi MZ Date: Sun, 8 Jun 2025 17:21:24 +0900 Subject: [PATCH] [KBSWITCH] Support System Pen Icon (#8080) ## Purpose Supporting System Pen icon and IME menus for East-Asian users. JIRA issue: CORE-20142 ## Overview The East-Asian system has the system pen icon in taskbar additionally. The system pen icon shows the current IME status. If the user clicked the IME system pen icon, the IME menu will open. The IME system pen icon can be customized by the IME module by posting indicator messages. ## Proposed changes - Add default pen icon resources. - Add base/applications/kbswitch/imemenu.c to handle IME menus. - Add code for adding, modifying and deleting the System Pen icon. - Modify indicdll.spec. - Fix popup menu alignment. --- base/applications/kbswitch/CMakeLists.txt | 3 +- base/applications/kbswitch/imemenu.c | 260 ++++++++ base/applications/kbswitch/imemenu.h | 38 ++ .../kbswitch/indicdll/CMakeLists.txt | 3 + .../applications/kbswitch/indicdll/indicdll.c | 129 +++- .../kbswitch/indicdll/indicdll.rc | 10 + .../kbswitch/indicdll/indicdll.spec | 2 + .../applications/kbswitch/indicdll/res/10.ico | Bin 0 -> 318 bytes .../applications/kbswitch/indicdll/res/11.ico | Bin 0 -> 318 bytes .../applications/kbswitch/indicdll/res/12.ico | Bin 0 -> 318 bytes .../applications/kbswitch/indicdll/res/13.ico | Bin 0 -> 318 bytes .../applications/kbswitch/indicdll/res/14.ico | Bin 0 -> 318 bytes .../applications/kbswitch/indicdll/res/15.ico | Bin 0 -> 318 bytes .../applications/kbswitch/indicdll/res/16.ico | Bin 0 -> 318 bytes .../applications/kbswitch/indicdll/resource.h | 8 + base/applications/kbswitch/kbswitch.c | 556 ++++++++++++++++-- base/applications/kbswitch/kbswitch.h | 15 +- base/applications/kbswitch/resource.h | 2 +- 18 files changed, 938 insertions(+), 88 deletions(-) create mode 100644 base/applications/kbswitch/imemenu.c create mode 100644 base/applications/kbswitch/imemenu.h create mode 100644 base/applications/kbswitch/indicdll/res/10.ico create mode 100644 base/applications/kbswitch/indicdll/res/11.ico create mode 100644 base/applications/kbswitch/indicdll/res/12.ico create mode 100644 base/applications/kbswitch/indicdll/res/13.ico create mode 100644 base/applications/kbswitch/indicdll/res/14.ico create mode 100644 base/applications/kbswitch/indicdll/res/15.ico create mode 100644 base/applications/kbswitch/indicdll/res/16.ico create mode 100644 base/applications/kbswitch/indicdll/resource.h 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 0000000000000000000000000000000000000000..36ba4c1ab9fe4326ae7d845193ef84ea69609e3b GIT binary patch literal 318 zcmb7AD-HrN6r2VURy1ibU=YZ?#a^i%Q;FN-VA~ULm9-=!kC|5_hS$#L&4?xi0#DAF zyvH1X6M&h>#FbF4sm{NMjmlbwkZFmE)*1`*3o|)_N0I0|(_K?>SeRPH^jKZfEe?Gj tpURW&*I@ZI{^cLqhhS&8G77|Jkx9Xf5;(ftelk!{2a_yD?8OAP=3 literal 0 HcmV?d00001 diff --git a/base/applications/kbswitch/indicdll/res/11.ico b/base/applications/kbswitch/indicdll/res/11.ico new file mode 100644 index 0000000000000000000000000000000000000000..3751cc9b81c96b8fb2e76d2754a1a8af325a2c94 GIT binary patch literal 318 zcmaiuF$%&!5JmrjV2Xfiqm7_tN^jtm(p$J)N{`?%Y&Ya2xj{+;;`%mY8WG&t_viCx zSw?b{_a0rSa?(ap1s)EN^L)ajzl2F1hF}fFLQss6L7J{o$_}U;W%)YGqXor1m^qi$ z%XKuB)XA_6~^9zE3v_bw~RdkSBcb<^adG=gb2?~SP%<#-jDUaWLfIy z5CXc<&`Enq0|YpN`(C!{FCp>JtbQmXm{LjvWqm5eC!pPO`Ks-?8vSB*7_ZH^G$a0R nSl@Phw#TsNOkF)O literal 0 HcmV?d00001 diff --git a/base/applications/kbswitch/indicdll/res/13.ico b/base/applications/kbswitch/indicdll/res/13.ico new file mode 100644 index 0000000000000000000000000000000000000000..f9c6b941fc116bf30f414259e7c7ff1bc7d47346 GIT binary patch literal 318 zcmZvWp$@_@6h-e6l1(&utpP&GLnFl5(l|F$XOHN(w53vU5rs|xc2dXv^Y_p7h63Uv! literal 0 HcmV?d00001 diff --git a/base/applications/kbswitch/indicdll/res/15.ico b/base/applications/kbswitch/indicdll/res/15.ico new file mode 100644 index 0000000000000000000000000000000000000000..5da20543f2dd5bd61e0a7984c998c0b0fbb83e18 GIT binary patch literal 318 zcmZvWp$@_@6h&_X2@_3TXTXqv++R!}`3fXKFpr>eZl8l~-9Bd$k|pK7b^^ic&bznw zwc7y0<(y+*qcOl1Ktmik63w-I?*AGmI`qB>J1y}DA#jlLgOt<}E{(J;RrP(ZcGaf0 zYV{hj%EK0&>rrz_ai3M@I^=x`L`oohY25J~%hnyw^CO+rD}Xg!F>xqbO*qrt$aT>> U_4z|fe;`6O;=QbclSs3<6Z2bRhPxcu&iVS)Svtnw`qk~uq+-(cs?zB?(A0wi{= zm3+jMfD3?$sKkwM-%Eq{h>3@)W01KLirzZ|ivu&?2)0{JzsmEhIV(;NGZ968E+)K3 x?j^K*&Mz@)Td(HRjnHnmcEfX{5t>E|ZLN$~N@` #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