Allegro.cc - Online Community

Allegro.cc Forums » Allegro Development » No Key-Up event received on Alt+Tab out

This thread is locked; no one can reply to it. rss feed Print
No Key-Up event received on Alt+Tab out
Bruce Pascoe
Member #15,931
April 2015
avatar

Currently Allegro doesn't send a key-up message when switching out while keys are pressed, as happens when using Alt+Tab. This also becomes an issue if, during gameplay, another app steals the focus.

When using the event system to handle keys, I am able to work around it by clearing my internal key state on receiving ALLEGRO_EVENT_DISPLAY_SWITCH_OUT, however this doesn't help for al_get_keyboard_state(), which I also use. I know the general wisdom is not to use the keyboard state API, but there are cases where it is useful when a more immediate state is needed than once per frame--blocking until a key is released, for example.

Would there be a way to modify the keyboard state API so that switching out clears any keys currently set as "down" internally?

Elias
Member #358
May 2000

Which platform? Windows uses a win32 call to update keyboard state as far as I know so I can't see how it would be a problem there... but maybe things changed since I last looked.

--
"Either help out or stop whining" - Evert

Bruce Pascoe
Member #15,931
April 2015
avatar

Windows, yeah.

From a cursory look at the source, it looks like Allegro only calls Win32 directly to get the state of Caps Lock et al, the event system is used to track all other state. So of course if the game loses focus, the display never gets the KEY_UP event and Allegro keeps thinking it's still pressed. I notice it happens a lot with the Shift key for me.

My quick fix was to add this function to wkeyboard.c:

#SelectExpand
406/* _al_win_kbd_clear_state: 407* Clears the keyboard state, e.g. when switching out 408*/ 409void _al_win_kbd_clear_state(void) 410{ 411 memset(&the_state, 0, sizeof(ALLEGRO_KEYBOARD_STATE)); 412}

And then call that in the WM_ACTIVATE handler in wwindow.c on switch-out.

But of course, naturally I'd prefer to use an unmolested Allegro build if at all possible. Unfortunately, there's no way to work around it right now without editing Allegro directly...

Chris Katko
Member #1,881
January 2002
avatar

Somewhat ironically, Linux supports overriding single or groups of library functions using LDPRELOAD.

-----sig:
“Programs should be written for people to read, and only incidentally for machines to execute.” - Structure and Interpretation of Computer Programs

beoran
Member #12,636
March 2011

This problem has popped up before. I guess it would be a reasonable workaround to add an al_clear_keyboard_state() API function for all platforms which you then can call on receiving an ALLEGRO_EVENT_DISPLAY_SWITCH_OUT or ALLEGRO_EVENT_DISPLAY_SWITCH_IN.

Bruce Pascoe
Member #15,931
April 2015
avatar

The root of the problem, of course, is the lack of KEY_UP events from the OS when focus is lost. But this is an OS limitation, so all we can do is work around it... Imagine if you never got button-up events when you dragged the mouse outside a window? Implementing drag and drop would be a nightmare. :P

But I'm not sure the state-clearing should be exposed to the outside, it seems more something that should be done internally. It's not exactly intuitive when keys are reported as pressed after they've been physically released just because the programmer didn't handle DISPLAY_SWITCH_OUT.

Pho75_
Member #12,377
November 2010

I think it's a valid use case to not have key_up events when window focus is lost, the most obvious being while your debugging your app/gui and the debugger interrupts you.

I also ran into this issue debugging my allegro apps in linux and android.
Anybody who wants to clear the input state can catch the ALLEGRO_EVENT_DISPLAY_SWITCH_OUT event and update your application key/mouse/touch states appropriately.
It's a minor inconvenience. Don't force this behavior onto people. Especially if there's no way to tell if the key_up event came because of the user or the window manager.
None of the OS's I've tested on generate key_up events when focus is lost, and it's probably for good reason.

Just my $0.02
Pho75

beoran
Member #12,636
March 2011

I agree more with Pho75_ here. Also, in Allegro 5 there is a precedent that on certain events, you have to manually call certain allegro functions to handle the event. Functions such as al_acknowledge_resize() come to mind.

However, I also think that al_get_keyboard_state() is clearly not well enough documented. It should be noted what will happen on a display lost event. If we introduce an al_clear_keyboard_state then it should be when it must be called.

Bruce Pascoe
Member #15,931
April 2015
avatar

My point here was, al_get_keyboard_state(), naming-wise, looks like a function which returns the current state of the keys at the time of call (similar to GetKeyState() in Win32). However, since it uses events under the hood, this is not actually the case. The behavior is not intuitive.

That's why I argue for clearing the internal state automatically on switch-out, to make the function adhere closer to the behavior suggested by its name.

Just to clear up any confusion: I'm not suggesting Allegro start sending automatic KEY_UP events on switch-out (this would be unexpected, as pointed out), just to not make the state get "stuck" if the app suddenly loses focus while a key is pressed. It's not always the programmer's fault if focus is lost--sometimes other apps on the system steal it.

I view it as a similar situation to D3D display loss: Allegro defaults to restoring the display automagically whenever it's lost, without any inconvenience to the library user.

beoran
Member #12,636
March 2011

No, your idea is very clear, just that I'm not sure that automatically clearing the key state is always what the end user wants. As Pho75_ says, it might cause problems when debugging.

The analogy with bitmap reloading on Directx is interesting though as that behavior is configurable. Perhaps we can do what you want, but make it configurable using al_set_new_display_flags or such...

Edgar Reynaldo
Member #8,592
May 2007
avatar

Err.. a display flag for a keyboard setting? How about al_clear_keyboard_state? That is the most simple and direct way of doing it, leaves it up to the user to do so, and we could add a note in the docs about ALLEGRO_EVENT_DISPLAY_SWITCH_IN and needing to clear the key state if desired, and leave it optional.

if (ev.type == ALLEGRO_EVENT_DISPLAY_SWITCH_IN) {
   al_clear_keyboard_state();
}

Bruce Pascoe
Member #15,931
April 2015
avatar

Eh, I'll take it. I'm still worried that it's not intuitive and raises the barrier to entry by giving the library user another responsibility (the one thing I love about Allegro is the shallow learning curve, it's easy to get started but turns out to be very powerful as you get more skilled...), but it would solve the issue at hand at least.

For what it's worth I think the clear should almost always be done on switch out though, otherwise if focus is lost the key is stuck in "pressed" state until the display window is reactivated.

Edgar Reynaldo
Member #8,592
May 2007
avatar

If you perform the clear on switch out, then you miss any keys pressed after that but before switch in. You shouldn't be checking the keyboard state on switch out for the very reason that it is not reliable due to os limitation.

Actually, wait. I may be wrong. If a window registers itself as an RIDEV_INPUTSINK device it can receive keyboard input even while the window is not in focus. However in my explorations of this recently I believe it only works with gui applications. If you create a console application it fails to register the device. I need to check that though.

Bruce Pascoe
Member #15,931
April 2015
avatar

That's kind of the point though, right? If your app doesn't have the focus you don't want to get any key events. Hence why you clear the state, to set all keys as not-pressed so they won't affect the game while it's not active.

Edgar Reynaldo
Member #8,592
May 2007
avatar

Well if you want correct keystate you're gonna have to listen to keyboard events when not in focus.

The kb state comes from the Raw Input API, it could easily be sent as ALLEGRO_EVENT_KEYBOARD events or not based on a simple flag, but at the same time, maintain internal state and consistency.

Bruce Pascoe
Member #15,931
April 2015
avatar

I think we're discussing two different things now. I was referring to the proposed al_clear_keyboard_state() API. The best place to call that is on switch-out, otherwise keys stay in "pressed" state because the display never gets the key_up event.

And unfortunately, no, the KB state is actually emulated via events, at least on Win32. I wish it were taken from raw input, but it's not. That's the source of the issue here--if the game loses focus, the KEY_UP is lost so the game keeps thinking it's down.

Edgar Reynaldo
Member #8,592
May 2007
avatar

Allegro monitors WM_INPUT which uses the Raw API, at least for the mouse. It could probably be extended fairly easily to monitor the keyboard info as well. All that needs to be done is to register the device type for the keyboard with windows.

Edit
And actually, now that I read Pho75's post again, I realize that you don't even need to clear allegro's key state but your own (and use events instead of keystates). Although there is the problem of keys being held while out of focus and then giving focus back to the window while the key is still pressed then allegro thinks the key is not down until it is released, and pressed again.

We could clear the key state during WM_ACTIVATE in wwindow.c, as it already calls _al_win_fix_modifiers() anyway.

Pho75_
Member #12,377
November 2010

Just to clear up any confusion: I'm not suggesting Allegro start sending automatic KEY_UP events on switch-out (this would be unexpected, as pointed out), just to not make the state get "stuck" if the app suddenly loses focus while a key is pressed.

I believe this would even worse behavior.
Now allegro's keyboard state and the keyboard event queue don't match.
If the keyboard state changes, there darn well better be a corresponding key_up/key_dn event generated in the event queue.

I make frequent use of both Polling keystate and listening for events simultaneously, and I'm not doing unholy sorcery, you might simply want to check if somebody is holding down LSHIFT, etc.

Otherwise nobody can ALT+tab out of an allegro app because it risks breaking the input state.
Regardless of which behavior you choose as default, allegro should not contradict itself.

Quote:

It's not always the programmer's fault if focus is lost--sometimes other apps on the system steal it.

What if the end user wants it. Suppose I want to walk from the west coast to the east in my RPG and it takes 15min. You're telling me I can't press RIGHT and alt+tab away, go make a mohito and come back later. I can't even ALT+TAB to my browser and read until my character reaches the destination. Now I have to sit and hold the key for 15min
or hire a day-laborer to hold the key down for me (economy, jobs, durka-dur! XP).
Ok, that's facetious, but you get my point. I'm not disagreeing the default behavior
you desire is probably what the end-user wants most of the time, but an app/user can be doing anything, so it's certainly not right for everyone all of the time.

Cheers

Bruce Pascoe
Member #15,931
April 2015
avatar

The LSHIFT thing was a perfect example actually, suppose you're typing something and press Shift to get an uppercase letter, and while you're doing that a background app steals the focus for a split second. The user doesn't realize this and releases Shift during focus loss. User keeps typing, and ends up with a pseudo caps-lock because Allegro still thinks Shift is pressed. That's the kind of issue I was referring to, and one that I get complaints from playtesters about all the time. Maybe some users would find it desirable (in the same way that cloning in a Pokemon game is desirable), but 9 times out of 10, in a game it will be treated as a bug.

The point I'm trying to make is, al_get_keyboard_state() as implemented doesn't actually return the current state of the keys, only what Allegro thinks the state is. This is not how any other polling input API (GetKeyState, joystick functions, etc.) works, if the user calls this function it's because they want the real, up-to-the-minute key state. It serves a completely different purpose than the event API.

Anyway, like I said, al_clear_keyboard_state() is a good compromise, and I'd accept that.

beoran
Member #12,636
March 2011

This topic is in risk of becoming a bikeshed paining session so, I'll stop arguing and agree that 'al_clear_keyboard_state()' would be a desirable workaround. I'll propose this on the mailing list.

EDIT: I proposed it and it looks like such a function is acceptable, but we're still discussing the details.

Go to: