Desabilitando teclas de atalho em jogos
Este artigo descreve como desabilitar temporariamente os atalhos de teclado no Microsoft Windows para evitar interrupções do jogo para jogos em tela inteira. A tecla SHIFT e a tecla CTRL geralmente são usadas como botões de acionar ou executar em jogos. Se os usuários pressionarem acidentalmente a tecla Windows (localizada perto dessas teclas), eles poderão fazer com que eles mesmos saiam repentinamente do aplicativo, o que estraga a experiência do jogo. Simplesmente usar a tecla SHIFT como um botão de jogo pode executar inadvertidamente o atalho StickyKeys que pode exibir uma caixa de diálogo de aviso. Para evitar esses problemas, você deve desabilitar essas chaves ao executar no modo de tela inteira e habilitar as chaves de volta para seus manipuladores padrão ao executar no modo de janela ou sair do aplicativo.
Este artigo descreve como fazer o seguinte:
- Desabilitar a tecla Windows com um gancho de teclado
- Desabilitar as teclas de atalho de acessibilidade
Desabilitar a tecla Windows com um gancho de teclado
Use um gancho de teclado de baixo nível para filtrar a tecla Windows de ser processada. O gancho de teclado de baixo nível mostrado no Exemplo 1 permanece em vigor mesmo se um usuário minimizar a janela ou alternar para outro aplicativo. Isso significa que você deve ter cuidado para garantir que a chave do Windows não esteja desabilitada quando o aplicativo for desativado. O código no Exemplo 1 faz isso manipulando a mensagem WM_ACTIVATEAPP.
Observação
Esse método funciona no Windows 2000 e versões posteriores do Windows. Esse método também funciona com contas de usuário com privilégios mínimos (também conhecidas como contas de usuário padrão).
Esse método é usado pelo DXUT e é ilustrado no exemplo de código a seguir.
Exemplo 1. Usando um gancho de teclado de baixo nível para desabilitar a tecla Windows
HHOOK g_hKeyboardHook = nullptr;
bool g_bWindowActive = false;
bool g_bFullscreen;
INT WINAPI WinMain( HINSTANCE, HINSTANCE, LPSTR, int )
{
// Initialization
g_hKeyboardHook = SetWindowsHookEx( WH_KEYBOARD_LL, LowLevelKeyboardProc, GetModuleHandle(nullptr), 0 );
//
// main application code here
//
// Cleanup before shutdown
UnhookWindowsHookEx( g_hKeyboardHook );
g_hKeyboardHook = nullptr;
return 0;
}
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;
auto p = reinterpret_cast<KBDLLHOOKSTRUCT*>(lParam);
switch (wParam)
{
case WM_KEYDOWN:
case WM_KEYUP:
{
bEatKeystroke = (g_bFullscreen && g_bWindowActive && ((p->vkCode == VK_LWIN) || (p->vkCode == VK_RWIN)));
// Note that this will not block the Xbox Game Bar hotkeys (Win+G, Win+Alt+R, etc.)
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 )
g_bWindowActive = true;
else
g_bWindowActive = false;
break;
case WM_SYSKEYDOWN:
if (wParam == VK_RETURN && (lParam & 0x60000000) == 0x20000000)
{
// Implement the classic ALT+ENTER fullscreen toggle
...
// g_bFullscreen is used to control if the Windows key is filtered by the keyboard hook or not.
g_bFullscreen = !g_bFullscreen;
// Remember to use DXGI_MWA_NO_ALT_ENTER when you call the DXGI method MakeWindowAssociation
// so you control the fullscreen toggling in your application.
}
break;
}
}
Desabilitar as teclas de atalho de acessibilidade
O Windows inclui recursos de acessibilidade, como StickyKeys, FilterKeys e ToggleKeys (consulte Acessibilidade do Windows). Cada uma delas tem uma finalidade diferente; StickyKeys, por exemplo, foi projetado para pessoas que têm dificuldade em segurar duas ou mais teclas simultaneamente. Cada um desses recursos de acessibilidade também tem um atalho de teclado que permite que o recurso seja ativado ou desativado. Por exemplo, o atalho StickyKeys é disparado pressionando a tecla SHIFT cinco vezes. Se a tecla SHIFT também for usada no jogo, o usuário poderá disparar acidentalmente esse atalho durante o jogo. Quando o atalho é disparado, o Windows (por padrão) apresenta um aviso em uma caixa de diálogo, o que faria com que o Windows minimizasse um jogo em execução no modo de tela inteira. Isso, é claro, pode ter um efeito drástico no jogo.
Os recursos de acessibilidade são necessários para alguns clientes e não interferem em jogos de tela inteira; portanto, você não deve alterar as configurações de acessibilidade. No entanto, como os atalhos para recursos de acessibilidade podem interromper o jogo se disparados acidentalmente, você deve desativar um atalho de acessibilidade somente quando esse recurso não estiver habilitado chamando SystemParametersInfo.
Um atalho de acessibilidade desativado por SystemParametersInfo permanece desativado mesmo após a saída do aplicativo. Isso significa que você deve restaurar as configurações antes de sair do aplicativo. Como é possível que o aplicativo falhe ao sair corretamente, você deve gravar essas configurações no armazenamento persistente para que elas possam ser restauradas quando o aplicativo for executado novamente. Você também pode usar um manipulador de exceção para restaurar essas configurações se ocorrer uma falha.
Para desativar esses atalhos
- Capture as configurações de acessibilidade atuais antes de desabilitá-las.
- Desabilite o atalho de acessibilidade quando o aplicativo entrar no modo de tela inteira se o recurso de acessibilidade estiver desativado.
- Restaure as configurações de acessibilidade quando o aplicativo entrar no modo de janela ou sair.
Esse método é usado em DXUT e é ilustrado no exemplo de código a seguir.
Observação
Esse método funciona quando executado em contas de usuário padrão.
Exemplo 2. Desabilitando teclas de atalho de acessibilidade
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( false );
...
// Restore back when going to windowed or shutting down
AllowAccessibilityShortcutKeys( true );
}
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);
}
}
}