Create virtual key up events for all pressed keys when the display loses focus.

This is a workaround for missed key events and incorrect key state reporting. This is actually a cleaner solution to the previous workaround on Windows and works nicely on Linux too.
This commit is contained in:
Ioannis Tsakpinis 2014-10-01 21:04:41 +03:00
parent 035a4d679b
commit 42ccc83499
4 changed files with 39 additions and 18 deletions

View File

@ -1125,6 +1125,8 @@ final class LinuxDisplay implements DisplayImplementation {
private void releaseInput() {
if (isLegacyFullscreen() || input_released)
return;
if ( keyboard != null )
keyboard.releaseAll();
input_released = true;
updateInputGrab();
if (current_window_mode == FULLSCREEN_NETWM) {

View File

@ -296,7 +296,7 @@ final class LinuxKeyboard {
return keycode;
}
private byte getKeyState(int event_type) {
private static byte getKeyState(int event_type) {
switch (event_type) {
case LinuxEvent.KeyPress:
return 1;
@ -307,10 +307,22 @@ final class LinuxKeyboard {
}
}
/** This is called when the window loses focus: we release all currently pressed keys. */
void releaseAll() {
for ( int i = 0; i < key_down_buffer.length; i++ ) {
if ( key_down_buffer[i] != 0 ) {
key_down_buffer[i] = 0;
putKeyboardEvent(i, (byte)0, 0, 0L, false);
}
}
}
private void handleKeyEvent(long event_ptr, long millis, int event_type, int event_keycode, int event_state) {
int keycode = getKeycode(event_ptr, event_state);
byte key_state = getKeyState(event_type);
boolean repeat = key_state == key_down_buffer[keycode];
if ( repeat && event_type == LinuxEvent.KeyRelease ) // This can happen for modifier keys after losing and regaining focus.
return;
key_down_buffer[keycode] = key_state;
long nanos = millis*1000000;
if (event_type == LinuxEvent.KeyPress) {

View File

@ -362,7 +362,7 @@ final class WindowsDisplay implements DisplayImplementation {
/*
* Called when the application is alt-tabbed to or from
*/
private void appActivate(boolean active) {
private void appActivate(boolean active, long millis) {
if (inAppActivate) {
return;
}
@ -379,14 +379,15 @@ final class WindowsDisplay implements DisplayImplementation {
redoMakeContextCurrent = true;
if (Display.isFullscreen())
updateClipping();
} else {
if ( keyboard != null )
keyboard.fireLostKeyEvents();
} else if (Display.isFullscreen()) {
showWindow(getHwnd(), SW_SHOWMINNOACTIVE);
resetDisplayMode();
} else
updateClipping();
keyboard.releaseAll(millis);
if ( Display.isFullscreen() ) {
showWindow(getHwnd(), SW_SHOWMINNOACTIVE);
resetDisplayMode();
} else
updateClipping();
}
updateCursor();
inAppActivate = false;
}
@ -997,10 +998,10 @@ final class WindowsDisplay implements DisplayImplementation {
return defWindowProc(hwnd, msg, wParam, lParam);
}
case WM_KILLFOCUS:
appActivate(false);
appActivate(false, millis);
return 0L;
case WM_SETFOCUS:
appActivate(true);
appActivate(true, millis);
return 0L;
case WM_MOUSEACTIVATE:
if ( parent != null ) {

View File

@ -122,6 +122,18 @@ final class WindowsKeyboard {
return (GetAsyncKeyState(virt_key) & 0x8000) != 0;
}
/**
* This is called when the window loses focus: we release all currently pressed keys. If a key has been pressed (or hasn't been released at all), before we
* regain focus, we'll start receiving repeat press events. We'll treat the first of those as a non-repeat press.
*/
void releaseAll(long millis) {
for ( int i = 0; i < virt_key_down_buffer.length; i++ ) {
if ( isKeyPressed(virt_key_down_buffer[i]) ) {
handleKey(i, 0, false, (byte)0, millis, false);
}
}
}
void handleKey(int virt_key, int scan_code, boolean extended, byte event_state, long millis, boolean repeat) {
virt_key = translateExtended(virt_key, scan_code, extended);
if ( !repeat && isKeyPressed(event_state) == isKeyPressed(virt_key_down_buffer[virt_key]) )
@ -132,6 +144,7 @@ final class WindowsKeyboard {
int keycode = WindowsKeycodes.mapVirtualKeyToLWJGLCode(virt_key);
if (keycode < key_down_buffer.length) {
key_down_buffer[keycode] = event_state;
repeat &= isKeyPressed(virt_key_down_buffer[virt_key]); // Treat the first repeat event after releaseAll() as a non-repeat press.
virt_key_down_buffer[virt_key] = event_state;
}
retained_key_code = keycode;
@ -141,13 +154,6 @@ final class WindowsKeyboard {
retained_repeat = repeat;
}
void fireLostKeyEvents() {
for ( int i = 0; i < virt_key_down_buffer.length; i++ ) {
if ( isKeyPressed(virt_key_down_buffer[i]) && !isKeyPressedAsync(i) )
handleKey(i, 0, false, (byte)0, 0L, false);
}
}
void handleChar(int event_char, long millis, boolean repeat) {
if (has_retained_event && retained_char != 0)
flushRetained();