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() { private void releaseInput() {
if (isLegacyFullscreen() || input_released) if (isLegacyFullscreen() || input_released)
return; return;
if ( keyboard != null )
keyboard.releaseAll();
input_released = true; input_released = true;
updateInputGrab(); updateInputGrab();
if (current_window_mode == FULLSCREEN_NETWM) { if (current_window_mode == FULLSCREEN_NETWM) {

View File

@ -296,7 +296,7 @@ final class LinuxKeyboard {
return keycode; return keycode;
} }
private byte getKeyState(int event_type) { private static byte getKeyState(int event_type) {
switch (event_type) { switch (event_type) {
case LinuxEvent.KeyPress: case LinuxEvent.KeyPress:
return 1; 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) { private void handleKeyEvent(long event_ptr, long millis, int event_type, int event_keycode, int event_state) {
int keycode = getKeycode(event_ptr, event_state); int keycode = getKeycode(event_ptr, event_state);
byte key_state = getKeyState(event_type); byte key_state = getKeyState(event_type);
boolean repeat = key_state == key_down_buffer[keycode]; 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; key_down_buffer[keycode] = key_state;
long nanos = millis*1000000; long nanos = millis*1000000;
if (event_type == LinuxEvent.KeyPress) { 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 * Called when the application is alt-tabbed to or from
*/ */
private void appActivate(boolean active) { private void appActivate(boolean active, long millis) {
if (inAppActivate) { if (inAppActivate) {
return; return;
} }
@ -379,14 +379,15 @@ final class WindowsDisplay implements DisplayImplementation {
redoMakeContextCurrent = true; redoMakeContextCurrent = true;
if (Display.isFullscreen()) if (Display.isFullscreen())
updateClipping(); updateClipping();
} else {
if ( keyboard != null ) if ( keyboard != null )
keyboard.fireLostKeyEvents(); keyboard.releaseAll(millis);
} else if (Display.isFullscreen()) { if ( Display.isFullscreen() ) {
showWindow(getHwnd(), SW_SHOWMINNOACTIVE); showWindow(getHwnd(), SW_SHOWMINNOACTIVE);
resetDisplayMode(); resetDisplayMode();
} else } else
updateClipping(); updateClipping();
}
updateCursor(); updateCursor();
inAppActivate = false; inAppActivate = false;
} }
@ -997,10 +998,10 @@ final class WindowsDisplay implements DisplayImplementation {
return defWindowProc(hwnd, msg, wParam, lParam); return defWindowProc(hwnd, msg, wParam, lParam);
} }
case WM_KILLFOCUS: case WM_KILLFOCUS:
appActivate(false); appActivate(false, millis);
return 0L; return 0L;
case WM_SETFOCUS: case WM_SETFOCUS:
appActivate(true); appActivate(true, millis);
return 0L; return 0L;
case WM_MOUSEACTIVATE: case WM_MOUSEACTIVATE:
if ( parent != null ) { if ( parent != null ) {

View File

@ -122,6 +122,18 @@ final class WindowsKeyboard {
return (GetAsyncKeyState(virt_key) & 0x8000) != 0; 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) { 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); virt_key = translateExtended(virt_key, scan_code, extended);
if ( !repeat && isKeyPressed(event_state) == isKeyPressed(virt_key_down_buffer[virt_key]) ) 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); int keycode = WindowsKeycodes.mapVirtualKeyToLWJGLCode(virt_key);
if (keycode < key_down_buffer.length) { if (keycode < key_down_buffer.length) {
key_down_buffer[keycode] = event_state; 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; virt_key_down_buffer[virt_key] = event_state;
} }
retained_key_code = keycode; retained_key_code = keycode;
@ -141,13 +154,6 @@ final class WindowsKeyboard {
retained_repeat = repeat; 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) { void handleChar(int event_char, long millis, boolean repeat) {
if (has_retained_event && retained_char != 0) if (has_retained_event && retained_char != 0)
flushRetained(); flushRetained();