[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.
|
@ -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)
|
||||
|
|
260
base/applications/kbswitch/imemenu.c
Normal file
|
@ -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/debug.h>
|
||||
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;
|
||||
}
|
38
base/applications/kbswitch/imemenu.h
Normal file
|
@ -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 <immdev.h>
|
||||
|
||||
#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);
|
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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 <reactos/version.rc>
|
||||
|
||||
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"
|
||||
|
|
|
@ -1 +1,3 @@
|
|||
1 stdcall KbSwitchSetHooks(long)
|
||||
12 stdcall GetPenMenuData(ptr ptr)
|
||||
14 stdcall SetPenMenuData(long long)
|
||||
|
|
BIN
base/applications/kbswitch/indicdll/res/10.ico
Normal file
After Width: | Height: | Size: 318 B |
BIN
base/applications/kbswitch/indicdll/res/11.ico
Normal file
After Width: | Height: | Size: 318 B |
BIN
base/applications/kbswitch/indicdll/res/12.ico
Normal file
After Width: | Height: | Size: 318 B |
BIN
base/applications/kbswitch/indicdll/res/13.ico
Normal file
After Width: | Height: | Size: 318 B |
BIN
base/applications/kbswitch/indicdll/res/14.ico
Normal file
After Width: | Height: | Size: 318 B |
BIN
base/applications/kbswitch/indicdll/res/15.ico
Normal file
After Width: | Height: | Size: 318 B |
BIN
base/applications/kbswitch/indicdll/res/16.ico
Normal file
After Width: | Height: | Size: 318 B |
8
base/applications/kbswitch/indicdll/resource.h
Normal file
|
@ -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
|
|
@ -9,8 +9,11 @@
|
|||
#include "kbswitch.h"
|
||||
#include <shlobj.h>
|
||||
#include <shlwapi_undoc.h>
|
||||
#include <undocuser.h>
|
||||
#include <imm.h>
|
||||
#include <immdev.h>
|
||||
#include <imm32_undoc.h>
|
||||
#include "imemenu.h"
|
||||
|
||||
#include <wine/debug.h>
|
||||
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))
|
||||
{
|
||||
|
|
|
@ -13,16 +13,25 @@
|
|||
#include <ime/indicml.h> /* 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)
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
#pragma once
|
||||
|
||||
/* Icons */
|
||||
#define IDI_MAIN 100
|
||||
#define IDI_MAIN 150
|
||||
|
||||
/* Menus */
|
||||
#define IDR_POPUP 100
|
||||
|
|