By Jason Sandlin, Software Development Lead
Microsoft Game Technology Group
December 2004
This document describes how to temporarily disable Microsoft Windows keyboard shortcuts to prevent disruption of game play for full screen games. The SHIFT key and the CTRL key are often used as fire or run buttons in games. Accidentally pressing the Windows key (located near these keys) can cause the user to suddenly jump out of the application, ruining the game experience. Simply using the SHIFT key as a game button can inadvertently execute the StickyKeys shortcut which may display a warning dialog. To avoid these issues, you should disable these keys when running in full-screen mode, and either enable the keys back to their default handlers when running in windowed mode or exit the application.
Contents:
Use a low-level keyboard hook to filter out the Windows key from being processed. This method is used by DXUT and is illustrated in the following code example:
HHOOK g_hKeyboardHook; BOOL g_bFullscreen; INT WINAPI WinMain( HINSTANCE, HINSTANCE, LPSTR, int ) { // Initialization g_hKeyboardHook = SetWindowsHookEx( WH_KEYBOARD_LL, LowLevelKeyboardProc, GetModuleHandle(NULL), 0 ); // // main application code here // // Cleanup before shutdown UnhookWindowsHookEx( g_hKeyboardHook ); } LRESULT CALLBACK LowLevelKeyboardProc( int nCode, WPARAM wParam, LPARAM lParam ) { if (nCode < 0 || nCode != HC_ACTION ) // do not process message return CallNextHookEx( g_hKeyboardHook, nCode, wParam, lParam); bool bEatKeystroke = false; KBDLLHOOKSTRUCT* p = (KBDLLHOOKSTRUCT*)lParam; switch (wParam) { case WM_KEYDOWN: case WM_KEYUP: { bEatKeystroke = (g_bFullscreen &, & g_bWindowActive &, & ((p->vkCode == VK_LWIN) || (p->vkCode == VK_RWIN))); break; } } if( bEatKeystroke ) return 1; else return CallNextHookEx( g_hKeyboardHook, nCode, wParam, lParam ); } LRESULT CALLBACK WndProc( HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam ) { switch( uMsg ) { case WM_ACTIVATEAPP: // g_bWindowActive is used to control if the Windows key is filtered by the keyboard hook or not. if( wParam == TRUE ) g_bWindowActive = true; else g_bWindowActive = false; break; } }
Figure 1. Using a low-level keyboard hook to disable the Windows key
This low-level keyboard hook remains in effect even if a user minimizes the window or switches to another application. This means that you must be careful to ensure the Windows key is not disabled when the application is deactivated. The code in Figure 1 does this by handling the WM_ACTIVATEAPP message.
Note This method works on Windows 2000 and later versions of Windows. This method also works with least-privileged user accounts (also known as limited-user accounts).
Windows includes accessibility features such as StickyKeys, FilterKeys, and ToggleKeys (see Windows Accessability). Each of these serves a different purpose; StickyKeys for example, is designed for people who have difficulty holding down two or more keys simultaneously. Each of these accessibility features also has a keyboard shortcut that allows the feature to be turned on or off. For example, the StickyKeys shortcut is triggered by pressing the SHIFT key five times. If the SHIFT key is also used in the game, the user could accidentally trigger this shortcut during game play. When the shortcut is activated, Windows (by default) presents a warning dialog causing a game running in full-screen mode to minimize. This, of course, can have a drastic effect on game play.
The accessibility features are required for some customers and do not themselves interfere with full-screen games; therefore, you should not change the accessibility settings. However, because the shortcuts for the accessibility features can disrupt game play if pressed accidentally, you should turn off an accessibility shortcut only when that feature is not enabled using SystemParametersInfo. If you do so, this change is permanent even after the application has exited. This means that you must restore the default settings before exiting the application. Because it is possible for the application to fail to exit correctly, you should write these default settings to persistent storage so they can be restored when the application is run again. You could also use an exception handler to restore these settings if a crash occurs.
To turn off these shortcuts, you need to:
This method is used in DXUT, and is illustrated in this code example:
STICKYKEYS g_StartupStickyKeys = {sizeof(STICKYKEYS), 0}; TOGGLEKEYS g_StartupToggleKeys = {sizeof(TOGGLEKEYS), 0}; FILTERKEYS g_StartupFilterKeys = {sizeof(FILTERKEYS), 0}; INT WINAPI WinMain( HINSTANCE, HINSTANCE, LPSTR, int ) { // Save the current sticky/toggle/filter key settings so they can be restored them later SystemParametersInfo(SPI_GETSTICKYKEYS, sizeof(STICKYKEYS), &g_StartupStickyKeys, 0); SystemParametersInfo(SPI_GETTOGGLEKEYS, sizeof(TOGGLEKEYS), &g_StartupToggleKeys, 0); SystemParametersInfo(SPI_GETFILTERKEYS, sizeof(FILTERKEYS), &g_StartupFilterKeys, 0); // Disable when full screen AllowAccessibilityShortcutKeys( true ); // Restore back when going to windowed or shutting down AllowAccessibilityShortcutKeys( false ); } void AllowAccessibilityShortcutKeys( bool bAllowKeys ) { if( bAllowKeys ) { // Restore StickyKeys/etc to original state and enable Windows key STICKYKEYS sk = g_StartupStickyKeys; TOGGLEKEYS tk = g_StartupToggleKeys; FILTERKEYS fk = g_StartupFilterKeys; SystemParametersInfo(SPI_SETSTICKYKEYS, sizeof(STICKYKEYS), &g_StartupStickyKeys, 0); SystemParametersInfo(SPI_SETTOGGLEKEYS, sizeof(TOGGLEKEYS), &g_StartupToggleKeys, 0); SystemParametersInfo(SPI_SETFILTERKEYS, sizeof(FILTERKEYS), &g_StartupFilterKeys, 0); } else { // Disable StickyKeys/etc shortcuts but if the accessibility feature is on, // then leave the settings alone as its probably being usefully used STICKYKEYS skOff = g_StartupStickyKeys; if( (skOff.dwFlags & SKF_STICKYKEYSON) == 0 ) { // Disable the hotkey and the confirmation skOff.dwFlags &= ~SKF_HOTKEYACTIVE; skOff.dwFlags &= ~SKF_CONFIRMHOTKEY; SystemParametersInfo(SPI_SETSTICKYKEYS, sizeof(STICKYKEYS), &skOff, 0); } TOGGLEKEYS tkOff = g_StartupToggleKeys; if( (tkOff.dwFlags & TKF_TOGGLEKEYSON) == 0 ) { // Disable the hotkey and the confirmation tkOff.dwFlags &= ~TKF_HOTKEYACTIVE; tkOff.dwFlags &= ~TKF_CONFIRMHOTKEY; SystemParametersInfo(SPI_SETTOGGLEKEYS, sizeof(TOGGLEKEYS), &tkOff, 0); } FILTERKEYS fkOff = g_StartupFilterKeys; if( (fkOff.dwFlags & FKF_FILTERKEYSON) == 0 ) { // Disable the hotkey and the confirmation fkOff.dwFlags &= ~FKF_HOTKEYACTIVE; fkOff.dwFlags &= ~FKF_CONFIRMHOTKEY; SystemParametersInfo(SPI_SETFILTERKEYS, sizeof(FILTERKEYS), &fkOff, 0); } } }
Figure 2. Disabling accessibility shortcut keys
Note This method works when running on a limited user account.