From 3df71d678dd4b1e2bbed100633ce0d751f0bb69a Mon Sep 17 00:00:00 2001 From: Katayama Hirofumi MZ Date: Tue, 27 May 2025 20:09:03 +0900 Subject: [PATCH] [KBSWITCH] Make Alt+Shift working Part 1 (#8039) Recently language switching (Alt+Shift) was not working. This PR will fix Alt+Shift (partially). JIRA issue: CORE-18546 - Add WH_KEYBOARD_LL hook to detect Alt+Shift. - Add delay to the action after language change. - Increase g_SpecialIds not to be full. - Delete useless ID_NEXTLAYOUT command. --- base/applications/kbswitch/kbsdll/kbsdll.c | 94 +++++--- base/applications/kbswitch/kbsdll/kbsdll.spec | 3 +- base/applications/kbswitch/kbswitch.c | 201 +++++++----------- base/applications/kbswitch/kbswitch.h | 27 ++- base/applications/kbswitch/resource.h | 1 - 5 files changed, 162 insertions(+), 164 deletions(-) diff --git a/base/applications/kbswitch/kbsdll/kbsdll.c b/base/applications/kbswitch/kbsdll/kbsdll.c index ffcc0344d6d..787e719b7d1 100644 --- a/base/applications/kbswitch/kbsdll/kbsdll.c +++ b/base/applications/kbswitch/kbsdll/kbsdll.c @@ -1,14 +1,17 @@ /* - * PROJECT: ReactOS Keyboard Layout Switcher - * FILE: base/applications/kbswitch/kbsdll/kbsdll.c - * PROGRAMMER: Dmitry Chapyshev - * + * PROJECT: ReactOS Keyboard Layout Switcher + * LICENSE: GPL-2.0-or-later (https://spdx.org/licenses/GPL-2.0-or-later) + * PURPOSE: Switching Keyboard Layouts + * COPYRIGHT: Copyright Dmitry Chapyshev (dmitry@reactos.org) + * Copyright Colin Finck (mail@colinfinck.de) + * Copyright 2022-2025 Katayama Hirofumi MZ (katayama.hirofumi.mz@gmail.com) */ #include "../kbswitch.h" HHOOK hWinHook = NULL; HHOOK hShellHook = NULL; +HHOOK hKeyboardLLHook = NULL; HINSTANCE hInstance = NULL; HWND hKbSwitchWnd = NULL; @@ -18,77 +21,108 @@ PostMessageToMainWnd(UINT Msg, WPARAM wParam, LPARAM lParam) PostMessage(hKbSwitchWnd, Msg, wParam, lParam); } -LRESULT CALLBACK -WinHookProc(int code, WPARAM wParam, LPARAM lParam) +static LRESULT CALLBACK +WinHookProc(INT code, WPARAM wParam, LPARAM lParam) { if (code < 0) - { return CallNextHookEx(hWinHook, code, wParam, lParam); - } switch (code) { + case HCBT_ACTIVATE: case HCBT_SETFOCUS: { HWND hwndFocus = (HWND)wParam; if (hwndFocus && hwndFocus != hKbSwitchWnd) - { - PostMessageToMainWnd(WM_WINDOW_ACTIVATE, wParam, lParam); - } + PostMessageToMainWnd(WM_WINDOW_ACTIVATE, (WPARAM)hwndFocus, 0); + break; } - break; } return CallNextHookEx(hWinHook, code, wParam, lParam); } -LRESULT CALLBACK -ShellHookProc(int code, WPARAM wParam, LPARAM lParam) +static LRESULT CALLBACK +ShellHookProc(INT code, WPARAM wParam, LPARAM lParam) { if (code < 0) - { return CallNextHookEx(hShellHook, code, wParam, lParam); - } switch (code) { + case HSHELL_WINDOWACTIVATED: + { + PostMessageToMainWnd(WM_WINDOW_ACTIVATE, wParam, 0); + break; + } case HSHELL_LANGUAGE: { PostMessageToMainWnd(WM_LANG_CHANGED, wParam, lParam); + break; } - break; } return CallNextHookEx(hShellHook, code, wParam, lParam); } -BOOL WINAPI -KbSwitchSetHooks(VOID) +static LRESULT CALLBACK +KeyboardLLHook(INT code, WPARAM wParam, LPARAM lParam) { - hWinHook = SetWindowsHookEx(WH_CBT, WinHookProc, hInstance, 0); - hShellHook = SetWindowsHookEx(WH_SHELL, ShellHookProc, hInstance, 0); + if (code < 0) + return CallNextHookEx(hKeyboardLLHook, code, wParam, lParam); - if (!hWinHook || !hShellHook) + if (code == HC_ACTION) { - return FALSE; + KBDLLHOOKSTRUCT *pKbStruct = (KBDLLHOOKSTRUCT *)lParam; + if (wParam == WM_KEYDOWN || wParam == WM_SYSKEYDOWN) + { + BOOL bShiftPressed = GetAsyncKeyState(VK_SHIFT) < 0; + BOOL bAltPressed = GetAsyncKeyState(VK_MENU) < 0; + BOOL bCtrlPressed = GetAsyncKeyState(VK_CONTROL) < 0; + // Detect Alt+Shift and Ctrl+Shift + if ((pKbStruct->vkCode == VK_SHIFT && bAltPressed) || + (pKbStruct->vkCode == VK_MENU && bShiftPressed) || + (pKbStruct->vkCode == VK_SHIFT && bCtrlPressed) || + (pKbStruct->vkCode == VK_CONTROL && bShiftPressed)) + { + PostMessageToMainWnd(WM_LANG_CHANGED, 0, 0); + } + } } - return TRUE; + return CallNextHookEx(hKeyboardLLHook, code, wParam, lParam); } -VOID WINAPI -KbSwitchDeleteHooks(VOID) +BOOL APIENTRY +KbSwitchSetHooks(_In_ BOOL bDoHook) { - if (hWinHook) + if (bDoHook) { - UnhookWindowsHookEx(hWinHook); - hWinHook = NULL; + hWinHook = SetWindowsHookEx(WH_CBT, WinHookProc, hInstance, 0); + hShellHook = SetWindowsHookEx(WH_SHELL, ShellHookProc, hInstance, 0); + hKeyboardLLHook = SetWindowsHookEx(WH_KEYBOARD_LL, KeyboardLLHook, hInstance, 0); + + if (hWinHook && hShellHook && hKeyboardLLHook) + return TRUE; + } + + /* Unhook */ + if (hKeyboardLLHook) + { + UnhookWindowsHookEx(hKeyboardLLHook); + hKeyboardLLHook = NULL; } if (hShellHook) { UnhookWindowsHookEx(hShellHook); hShellHook = NULL; } + if (hWinHook) + { + UnhookWindowsHookEx(hWinHook); + hWinHook = NULL; + } + return !bDoHook; } BOOL WINAPI @@ -103,9 +137,7 @@ DllMain(IN HINSTANCE hinstDLL, hInstance = hinstDLL; hKbSwitchWnd = FindWindow(szKbSwitcherName, NULL); if (!hKbSwitchWnd) - { return FALSE; - } } break; } diff --git a/base/applications/kbswitch/kbsdll/kbsdll.spec b/base/applications/kbswitch/kbsdll/kbsdll.spec index e878227c085..bf2ebbdd33a 100644 --- a/base/applications/kbswitch/kbsdll/kbsdll.spec +++ b/base/applications/kbswitch/kbsdll/kbsdll.spec @@ -1,2 +1 @@ -1 stdcall KbSwitchSetHooks() -2 stdcall KbSwitchDeleteHooks() +1 stdcall KbSwitchSetHooks(long) diff --git a/base/applications/kbswitch/kbswitch.c b/base/applications/kbswitch/kbswitch.c index 69a53dba4b3..3c8885b1314 100644 --- a/base/applications/kbswitch/kbswitch.c +++ b/base/applications/kbswitch/kbswitch.c @@ -1,12 +1,11 @@ /* - * PROJECT: Keyboard Layout Switcher - * FILE: base/applications/kbswitch/kbswitch.c - * PURPOSE: Switching Keyboard Layouts - * PROGRAMMERS: Dmitry Chapyshev (dmitry@reactos.org) - * Colin Finck (mail@colinfinck.de) - * Katayama Hirofumi MZ (katayama.hirofumi.mz@gmail.com) + * PROJECT: ReactOS Keyboard Layout Switcher + * LICENSE: GPL-2.0-or-later (https://spdx.org/licenses/GPL-2.0-or-later) + * PURPOSE: Switching Keyboard Layouts + * COPYRIGHT: Copyright Dmitry Chapyshev (dmitry@reactos.org) + * Copyright Colin Finck (mail@colinfinck.de) + * Copyright 2022-2025 Katayama Hirofumi MZ (katayama.hirofumi.mz@gmail.com) */ - #include "kbswitch.h" #include #include @@ -32,12 +31,13 @@ WINE_DEFAULT_DEBUG_CHANNEL(internat); #define WM_NOTIFYICONMSG (WM_USER + 248) -PKBSWITCHSETHOOKS KbSwitchSetHooks = NULL; -PKBSWITCHDELETEHOOKS KbSwitchDeleteHooks = NULL; +#define TIMER_ID_LANG_CHANGED_DELAYED 0x10000 +#define TIMER_LANG_CHANGED_DELAY 200 + +FN_KbSwitchSetHooks KbSwitchSetHooks = NULL; UINT ShellHookMessage = 0; -HINSTANCE hInst; -HANDLE hProcessHeap; +HINSTANCE g_hInst = NULL; HMODULE g_hHookDLL = NULL; INT g_nCurrentLayoutNum = 1; HICON g_hTrayIcon = NULL; @@ -45,8 +45,8 @@ HWND g_hwndLastActive = NULL; INT g_cKLs = 0; HKL g_ahKLs[64]; -ULONG -NTAPI +/* Debug logging */ +ULONG NTAPI vDbgPrintExWithPrefix(IN PCCH Prefix, IN ULONG ComponentId, IN ULONG Level, @@ -54,27 +54,24 @@ vDbgPrintExWithPrefix(IN PCCH Prefix, IN va_list ap) { CHAR Buffer[512]; - SIZE_T PrefixLength = strlen(Prefix); strncpy(Buffer, Prefix, PrefixLength); - - _vsnprintf(Buffer + PrefixLength, - sizeof(Buffer) - PrefixLength, - Format, - ap); - + _vsnprintf(Buffer + PrefixLength, _countof(Buffer) - PrefixLength, Format, ap); + Buffer[_countof(Buffer) - 1] = ANSI_NULL; /* Avoid buffer overrun */ OutputDebugStringA(Buffer); return 0; } -typedef struct +typedef struct tagSPECIAL_ID { DWORD dwLayoutId; HKL hKL; TCHAR szKLID[CCH_LAYOUT_ID + 1]; } SPECIAL_ID, *PSPECIAL_ID; -SPECIAL_ID g_SpecialIds[80]; +#define MAX_SPECIAL_IDS 256 + +SPECIAL_ID g_SpecialIds[MAX_SPECIAL_IDS]; INT g_cSpecialIds = 0; static VOID LoadSpecialIds(VOID) @@ -121,7 +118,7 @@ static VOID LoadSpecialIds(VOID) if (g_cSpecialIds >= _countof(g_SpecialIds)) { - OutputDebugStringA("g_SpecialIds is full!"); + ERR("g_SpecialIds is full!"); break; } } @@ -158,16 +155,20 @@ GetKLIDFromHKL(HKL hKL, LPTSTR szKLID, SIZE_T KLIDLength) } } +static HKL GetActiveKL(VOID) +{ + /* FIXME: Get correct console window's HKL when console window */ + HWND hwndTarget = (g_hwndLastActive ? g_hwndLastActive : GetForegroundWindow()); + DWORD dwTID = GetWindowThreadProcessId(hwndTarget, NULL); + return GetKeyboardLayout(dwTID); +} + static VOID UpdateLayoutList(HKL hKL OPTIONAL) { INT iKL; if (!hKL) - { - HWND hwndTarget = (g_hwndLastActive ? g_hwndLastActive : GetForegroundWindow()); - DWORD dwTID = GetWindowThreadProcessId(hwndTarget, NULL); - hKL = GetKeyboardLayout(dwTID); - } + hKL = GetActiveKL(); g_cKLs = GetKeyboardLayoutList(_countof(g_ahKLs), g_ahKLs); @@ -191,15 +192,9 @@ static VOID UpdateLayoutList(HKL hKL OPTIONAL) static HKL GetHKLFromLayoutNum(INT nLayoutNum) { if (0 <= (nLayoutNum - 1) && (nLayoutNum - 1) < g_cKLs) - { return g_ahKLs[nLayoutNum - 1]; - } else - { - HWND hwndTarget = (g_hwndLastActive ? g_hwndLastActive : GetForegroundWindow()); - DWORD dwTID = GetWindowThreadProcessId(hwndTarget, NULL); - return GetKeyboardLayout(dwTID); - } + return GetActiveKL(); } static VOID @@ -587,11 +582,9 @@ SetHooks(VOID) } #define IHOOK_SET 1 -#define IHOOK_DELETE 2 - KbSwitchSetHooks = (PKBSWITCHSETHOOKS) GetProcAddress(g_hHookDLL, MAKEINTRESOURCEA(IHOOK_SET)); - KbSwitchDeleteHooks = (PKBSWITCHDELETEHOOKS) GetProcAddress(g_hHookDLL, MAKEINTRESOURCEA(IHOOK_DELETE)); + KbSwitchSetHooks = (FN_KbSwitchSetHooks)GetProcAddress(g_hHookDLL, MAKEINTRESOURCEA(IHOOK_SET)); - if (!KbSwitchSetHooks || !KbSwitchDeleteHooks || !KbSwitchSetHooks()) + if (!KbSwitchSetHooks || !KbSwitchSetHooks(TRUE)) { ERR("SetHooks failed\n"); return FALSE; @@ -604,10 +597,10 @@ SetHooks(VOID) VOID DeleteHooks(VOID) { - if (KbSwitchDeleteHooks) + if (KbSwitchSetHooks) { - KbSwitchDeleteHooks(); - KbSwitchDeleteHooks = NULL; + KbSwitchSetHooks(FALSE); + KbSwitchSetHooks = NULL; } if (g_hHookDLL) @@ -654,20 +647,12 @@ UpdateLanguageDisplay(HWND hwnd, HKL hKL) } HWND -GetTargetWindow(HWND hwndFore) +GetTargetWindow(HWND hwndFore OPTIONAL) { - TCHAR szClass[64]; - HWND hwndIME; - HWND hwndTarget = hwndFore; - if (hwndTarget == NULL) - hwndTarget = GetForegroundWindow(); - - GetClassName(hwndTarget, szClass, _countof(szClass)); - if (_tcsicmp(szClass, szKbSwitcherName) == 0) + HWND hwndTarget = (hwndFore ? hwndFore : GetForegroundWindow()); + if (IsWndClassName(hwndTarget, szKbSwitcherName)) hwndTarget = g_hwndLastActive; - - hwndIME = ImmGetDefaultIMEWnd(hwndTarget); - return (hwndIME ? hwndIME : hwndTarget); + return hwndTarget; } UINT @@ -682,26 +667,17 @@ UpdateLanguageDisplayCurrent(HWND hwnd, HWND hwndFore) static BOOL RememberLastActive(HWND hwnd, HWND hwndFore) { - TCHAR szClass[64]; - hwndFore = GetAncestor(hwndFore, GA_ROOT); - if (!IsWindowVisible(hwndFore) || !GetClassName(hwndFore, szClass, _countof(szClass))) + if (!IsWindowVisible(hwndFore)) return FALSE; - if (_tcsicmp(szClass, szKbSwitcherName) == 0 || - _tcsicmp(szClass, TEXT("Shell_TrayWnd")) == 0) + if (IsWndClassName(hwndFore, szKbSwitcherName) || + IsWndClassName(hwndFore, TEXT("Shell_TrayWnd"))) { return FALSE; /* Special window */ } - /* FIXME: CONWND needs special handling */ - if (_tcsicmp(szClass, TEXT("ConsoleWindowClass")) == 0) - { - HKL hKL = GetKeyboardLayout(0); - UpdateLanguageDisplay(hwnd, hKL); - } - g_hwndLastActive = hwndFore; return TRUE; } @@ -734,19 +710,37 @@ WndProc(HWND hwnd, UINT Message, WPARAM wParam, LPARAM lParam) break; } - case WM_LANG_CHANGED: /* Comes from kbsdll.dll and this module */ + case WM_TIMER: { - TRACE("WM_LANG_CHANGED: wParam:%p, lParam:%p\n", wParam, lParam); - UpdateLayoutList((HKL)lParam); - UpdateLanguageDisplay(hwnd, (HKL)lParam); + if (wParam == TIMER_ID_LANG_CHANGED_DELAYED) + { + KillTimer(hwnd, TIMER_ID_LANG_CHANGED_DELAYED); + HKL hKL = GetActiveKL(); + UpdateLayoutList(hKL); + UpdateLanguageDisplay(hwnd, hKL); + } break; } + // WM_LANG_CHANGED message: + // wParam: HWND hwndTarget or zero + // lParam: HKL hKL or zero + case WM_LANG_CHANGED: /* Comes from kbsdll.dll and this module */ + { + TRACE("WM_LANG_CHANGED: wParam:%p, lParam:%p\n", wParam, lParam); + /* Delayed action */ + KillTimer(hwnd, TIMER_ID_LANG_CHANGED_DELAYED); + SetTimer(hwnd, TIMER_ID_LANG_CHANGED_DELAYED, TIMER_LANG_CHANGED_DELAY, NULL); + break; + } + + // WM_WINDOW_ACTIVATE message: + // wParam: HWND hwndTarget or zero + // lParam: zero case WM_WINDOW_ACTIVATE: /* Comes from kbsdll.dll and this module */ { - HWND hwndFore; TRACE("WM_WINDOW_ACTIVATE: wParam:%p, lParam:%p\n", wParam, lParam); - hwndFore = GetForegroundWindow(); + HWND hwndFore = wParam ? (HWND)wParam : GetForegroundWindow(); if (RememberLastActive(hwnd, hwndFore)) return UpdateLanguageDisplayCurrent(hwnd, hwndFore); break; @@ -764,24 +758,29 @@ WndProc(HWND hwnd, UINT Message, WPARAM wParam, LPARAM lParam) GetCursorPos(&pt); SetForegroundWindow(hwnd); + INT nID; if (lParam == WM_LBUTTONUP) { /* Rebuild the left popup menu on every click to take care of keyboard layout changes */ hLeftPopupMenu = BuildLeftPopupMenu(); - TrackPopupMenu(hLeftPopupMenu, 0, pt.x, pt.y, 0, hwnd, NULL); + nID = TrackPopupMenu(hLeftPopupMenu, TPM_RETURNCMD, pt.x, pt.y, 0, hwnd, NULL); DestroyMenu(hLeftPopupMenu); } else { if (!s_hRightPopupMenu) { - s_hMenu = LoadMenu(hInst, MAKEINTRESOURCE(IDR_POPUP)); + s_hMenu = LoadMenu(g_hInst, MAKEINTRESOURCE(IDR_POPUP)); s_hRightPopupMenu = GetSubMenu(s_hMenu, 0); } - TrackPopupMenu(s_hRightPopupMenu, 0, pt.x, pt.y, 0, hwnd, NULL); + nID = TrackPopupMenu(s_hRightPopupMenu, TPM_RETURNCMD, pt.x, pt.y, 0, hwnd, NULL); } PostMessage(hwnd, WM_NULL, 0, 0); + + if (nID) + PostMessage(hwnd, WM_COMMAND, nID, 0); + break; } } @@ -807,46 +806,6 @@ WndProc(HWND hwnd, UINT Message, WPARAM wParam, LPARAM lParam) break; } - case ID_NEXTLAYOUT: - { - HWND hwndTarget = (HWND)lParam, hwndTargetSave = NULL; - DWORD dwThreadID; - HKL hKL; - UINT uNum; - TCHAR szClass[64]; - BOOL bCONWND = FALSE; - - if (hwndTarget == NULL) - hwndTarget = g_hwndLastActive; - - /* FIXME: CONWND needs special handling */ - if (hwndTarget && - GetClassName(hwndTarget, szClass, _countof(szClass)) && - _tcsicmp(szClass, TEXT("ConsoleWindowClass")) == 0) - { - bCONWND = TRUE; - hwndTargetSave = hwndTarget; - hwndTarget = NULL; - } - - if (hwndTarget) - { - dwThreadID = GetWindowThreadProcessId(hwndTarget, NULL); - hKL = GetKeyboardLayout(dwThreadID); - uNum = GetLayoutNum(hKL); - if (uNum != 0) - g_nCurrentLayoutNum = uNum; - } - - ActivateLayout(hwnd, GetNextLayout(), hwndTarget, TRUE); - - /* FIXME: CONWND needs special handling */ - if (bCONWND) - ActivateLayout(hwnd, g_nCurrentLayoutNum, hwndTargetSave, TRUE); - - break; - } - default: { if (1 <= LOWORD(wParam) && LOWORD(wParam) <= 1000) @@ -866,7 +825,7 @@ WndProc(HWND hwnd, UINT Message, WPARAM wParam, LPARAM lParam) { if (wParam == SPI_SETNONCLIENTMETRICS) { - PostMessage(hwnd, WM_WINDOW_ACTIVATE, wParam, lParam); + PostMessage(hwnd, WM_WINDOW_ACTIVATE, 0, 0); break; } } @@ -874,6 +833,7 @@ WndProc(HWND hwnd, UINT Message, WPARAM wParam, LPARAM lParam) case WM_DESTROY: { + KillTimer(hwnd, TIMER_ID_LANG_CHANGED_DELAYED); DeleteHooks(); DestroyMenu(s_hMenu); DeleteTrayIcon(hwnd); @@ -893,9 +853,9 @@ WndProc(HWND hwnd, UINT Message, WPARAM wParam, LPARAM lParam) { TRACE("ShellHookMessage: wParam:%p, lParam:%p\n", wParam, lParam); if (wParam == HSHELL_LANGUAGE) - PostMessage(hwnd, WM_LANG_CHANGED, wParam, lParam); - else if (wParam == HSHELL_WINDOWACTIVATED) - PostMessage(hwnd, WM_WINDOW_ACTIVATE, wParam, lParam); + PostMessage(hwnd, WM_LANG_CHANGED, 0, 0); + else if (wParam == HSHELL_WINDOWACTIVATED || wParam == HSHELL_RUDEAPPACTIVATED) + PostMessage(hwnd, WM_WINDOW_ACTIVATE, 0, 0); break; } @@ -938,8 +898,7 @@ _tWinMain(HINSTANCE hInstance, HINSTANCE hPrevInst, LPTSTR lpCmdLine, INT nCmdSh return 1; } - hInst = hInstance; - hProcessHeap = GetProcessHeap(); + g_hInst = hInstance; ZeroMemory(&WndClass, sizeof(WndClass)); WndClass.lpfnWndProc = WndProc; diff --git a/base/applications/kbswitch/kbswitch.h b/base/applications/kbswitch/kbswitch.h index 79ea09f7e0e..41907a8d57d 100644 --- a/base/applications/kbswitch/kbswitch.h +++ b/base/applications/kbswitch/kbswitch.h @@ -1,6 +1,6 @@ #pragma once -#include +#include #include #include #include @@ -14,17 +14,26 @@ #include "resource.h" -// Character Count of a layout ID like "00000409" -#define CCH_LAYOUT_ID 8 +#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 -// Maximum Character Count of a ULONG in decimal -#define CCH_ULONG_DEC 10 - -#define WM_KEY_PRESSED (WM_USER + 10100) #define WM_LANG_CHANGED (WM_USER + 10200) #define WM_WINDOW_ACTIVATE (WM_USER + 10300) -typedef BOOL (WINAPI *PKBSWITCHSETHOOKS) (VOID); -typedef VOID (WINAPI *PKBSWITCHDELETEHOOKS) (VOID); +typedef BOOL (APIENTRY *FN_KbSwitchSetHooks)(BOOL bDoHook); const TCHAR szKbSwitcherName[] = INDICATOR_CLASS; + +static inline BOOL +IsWndClassName(_In_opt_ HWND hwndTarget, PCTSTR pszName) +{ + TCHAR szClass[32]; + GetClassName(hwndTarget, szClass, _countof(szClass)); + return lstrcmpi(szClass, pszName) == 0; +} + +static inline BOOL +IsConsoleWnd(_In_opt_ HWND hwndTarget) +{ + return IsWndClassName(hwndTarget, TEXT("ConsoleWindowClass")); +} diff --git a/base/applications/kbswitch/resource.h b/base/applications/kbswitch/resource.h index ef71d848be4..b7410bd20e0 100644 --- a/base/applications/kbswitch/resource.h +++ b/base/applications/kbswitch/resource.h @@ -9,4 +9,3 @@ /* Menu items */ #define ID_EXIT 10001 #define ID_PREFERENCES 10002 -#define ID_NEXTLAYOUT 10003