Sign in to follow this  
Stephany

Impossible to Detect Window Starting Unfocused? (Windows)

Recommended Posts

Stephany    166

I'm trying to simulate the rare situation where a user starts my game, but then gets impatient and/or clicks on something before the window appears. To debug this problem, I placed a Sleep(1000) right before my game window is created to provide a delay. Then I start my game, and click on another (folder) window to bring it to the foreground before the game window opens.

 

What happens is that my app gets the typical WM_ACTIVATEAPP + WM_SETFOCUS messages of a newly created window, and that's it. It never receives a WM_KILLFOCUS, or any other message that differs from starting the game with normal focus. To check this, I logged every message type the window received in the foreground and background, then compared them.

 

To be clear about this behavior, once my game receives WM_SETFOCUS and is behind another window, I can click on buttons and type into text boxes in that front window without my game window ever getting a WM_KILLFOCUS message.

 

I'm currently fixing this problem by comparing my window handle to GetForegroundWindow() after I create the window - and if it doesn't match, I call SetFocus(null) to remove input focus and trigger WM_KILLFOCUS. This seems to get around the issue by allowing Windows and my program to *notice* it is unfocused. But it feels like a hack. I'm hoping there is a better solution. edit: This temporary fix causes another problem: the next time my game is actually focused (such as by clicking on it in the task bar or using alt+tab to switch to it), Windows never sends it a WM_SETFOCUS message, even though the window is brought to the foreground. However, the next time I click or alt+tab to it, it does send the message. So it's as if the first focus is ignored because I called SetFocus(null).

 

Could any of these issues be related to my window's creation styles/options/flags? The style flags I use are (WS_POPUP | WS_CAPTION | WS_SYSMENU | WS_MINIMIZEBOX | WS_MAXIMIZEBOX | WS_VISIBLE). Are there any other things I may be doing wrong to cause this to happen? Or is this just a limitation/bug/design of Windows?

 

edit: it looks like this poster had the same problem: http://www.gamedev.net/topic/618042-win23-api-detecting-lost-focus/

 

Thanks for any advice

Edited by Stephany

Share this post


Link to post
Share on other sites
Stephany    166

Why would you expect to receive messages about losing the focus of the window you created... when you never had it in the first place?

I'm recieving a WM_SETFOCUS message at startup. And then nothing after that to indicate that focus has been lost.

 

 

Additionally: attempting to steal the focus back from the user is a bad idea. The user decides where the focus goes, you should write your code to handle this situation properly (i.e. "don't do something stupid like crash or take back the focus.")

I'm not trying to change the focus at all. I'm just trying to detect what state the focus is in. My game locks the mouse when its focused, so if I don't notice they clicked on something while my game was opening, their cursor gets frozen while my game is in the background. It's very annoying, and it happens quite a lot while debugging as well.

 

edit: Here are some other people explaining this problem. They may explain it better:

http://blogs.msdn.com/b/oldnewthing/archive/2013/10/16/10456992.aspx

https://social.msdn.microsoft.com/Forums/windowsdesktop/en-US/6ac68d3c-0186-4c7f-90bd-b8cc9dc24abe/odd-wmactivate-behaviour?forum=windowssdk

http://www.gamedev.net/topic/618042-win23-api-detecting-lost-focus/

Edited by Stephany

Share this post


Link to post
Share on other sites
Stephany    166

After further testing, it seems that the only way I can find to solve this is to call GetForegroundWindow() every frame, and change the focus accordingly. Basically, I'm handling WM_SETFOCUS and WM_KILLFOCUS like normal, and have this code in my update/loop code, to handle abnormal situations like starting behind another window:

BOOL fore_state = GetForegroundWindow() == Handle;
if( GameFocusState != fore_state )
    SetFocus( fore_state ? Handle : null ); // this triggers windows focus changes and sends WM_SETFOCUS or WM_KILLFOCUS

I don't like this solution, but it is working. I don't know how expensive GetForegroundWindow() is, or can get on other OS types. I would definitely prefer a solution that relied only on messaging if anyone has any ideas.

 

The reason GetForegroundWindow() has to be called every frame is because calling SetFocus(null) (to recover from starting unfocused), for some odd reason, prevents Windows from sending WM_SETFOCUS or WM_ACTIVATEAPP* messages when the app is brought back to the foreground the next time. This makes no sense to me, but there is probably some reason for it.

Share this post


Link to post
Share on other sites
Stephany    166

Reread your first link that you added very carefully, as it contains the answer.

WM_ACTIVATE does not mean that you have the focus, only that a window is being activated/deactivated. Recieving WM_ACTIVATE does not mean that your window is the foreground active window. A window is becoming active within your process, it is not necessarily becoming the system foreground window.

A process with windows can have an active window. It may or may not be the system's foreground window. If you've got multiple threads you can have multiple active windows, one for each.

 

I apologize if I confused/mixed WM_ACTIVATE into this situation. I mentioned it above because I wanted to show that windows is also sending that message, but I'm actually relying on WM_SETFOCUS and WM_KILLFOCUS to detect focus changes. My app doesn't actually use WM_ACTIVATE*. According to Microsoft, WM_SETFOCUS is sent after a window gains keyboard focus, and WM_KILLFOCUS is sent "immediately before it loses keyboard focus". When I start my window behind another window, it receives WM_SETFOCUS, then just sits there. It comes down to this: For some reason, Windows wants my application to think it has input focus, even though it doesn't. And as far as I can tell, there is no way to ask windows if it is lying to me.

 

In code terms: GetActiveWindow != GetForegroundWindow. They may happen to occasionally return the same result, your thread's active window might occasionally also happen to be the system's foreground window, but the two functions are different, they refer to different aspects of the system.

 

Yes, this threw me off at first. However, there doesn't appear to be any type of GetRealInputFocusWindow() function. Calling GetFocus() returns the handle to my window, as if the user is focused on my window. The only way to notice the problem is by calling GetForegroundWindow() - and even that doesn't help, because once you know there is a problem, there is no way to fix it without messing things up more (such as by calling SetFocus(...), which makes windows do even stranger things later on).

 

No need to set focus or change focus, just test for equality when you get WM_ACTIVATE if you need to do something special when your thread's active window becomes the system's foreground window.

 

That's the problem - there is no way to detect when your window becomes the input-focus window (or even the foreground window) via messaging. Or if there is, I haven't yet discovered it. WM_SHOWWINDOW seems to indicate that it will tell you when your app becomes top-most, but after testing, it doesn't seem to be reliable - I get wparam={some_window}, even when my window is on top.

 

My game uses the mouse to rotate a 3D camera, so it hides and locks the real cursor to the center of the screen so it won't get stuck against a display edge (where it can't generate correct delta values in that direction), or just wander off to click outside of the window and disturb gameplay. So when the game window loses focus, I need to restore the cursor back to normal and unlock it, Otherwise, the user is literally stuck in the center - they can't even click on my game's window to fix the problem (they have to alt+tab to it, or etc).

 

If I could think of some alternate way to handle the cursor issue, I would just ignore this problem and let my game run its pants off in the background. It might bog down the system, but at least they would be able to use their cursor.

Share this post


Link to post
Share on other sites
Amr0    2230

My advice is not to worry too much about it - if memory serves, I've noticed this behavior in many titles. You start the game, nothing seems to happen, you click on something and then the cursor disappears. Users will know to alt-tab back to your game and then out to regain the mouse. But to lock the mouse to the center and hide it, you'd better do it after you capture the mouse, and you'd better stop messing with the mouse when mouse capture is lost. Windows will not let a background process capture the mouse, so maybe if you do it like that, users will not lose the mouse at all.

 

I'd leave it alone and maybe come back to it when the game is almost ready to ship. One thing you can do is load the "waiting" cursor at game start and restore the arrow cursor when loading is finished. That way, users will know the game is starting up and hopefully they will wait.

Share this post


Link to post
Share on other sites
Erik Rufelt    5901

I think this is just some sort of bug in Windows that's always been there.. or perhaps there's some strange reason for it. The "local app system" or whatever the part of Windows deals with window focus probably considers the window to have focus, whereas the higher level system that determines if to send the input to the app or thread at all knows that it shouldn't send it there.

You can use WM_NCACTIVATE and check wParam to know if the window title-bar will be drawn active or inactive, which should be enough to know if you're active..

Edited by Erik Rufelt

Share this post


Link to post
Share on other sites
Stephany    166

But to lock the mouse to the center and hide it, you'd better do it after you capture the mouse, and you'd better stop messing with the mouse when mouse capture is lost. Windows will not let a background process capture the mouse, so maybe if you do it like that, users will not lose the mouse at all.

This seems like a good idea. I still have hope of making something like this work. But currently, unless I'm doing something wrong, it seems that Windows will allow my app to "capture the mouse" as long as the cursor is hovering over it. It doesn't seem to matter if another window is above it in the Z-stack. So if the user accidentially slides the cursor into the rectangle region of my game window's visibility, Windows still allows me to capture it. And as far as my game knows, my window is top-most and focused, so there seems to be no way to ignore the situation.

 

I'd leave it alone and maybe come back to it when the game is almost ready to ship.

Yes, I may have to. But I'm worried that I'll forget most of this confusion when it comes time to fix it again. This is probably the 3rd or 4th time I've attempted to fix this focus problem. And each time, I forget most of the issues involved with it. I rarely mess with windows API functions, so its easy (and nice) to forget it when I'm working on other things.

 

I think this is just some sort of bug in Windows that's always been there.. or perhaps there's some strange reason for it. The "local app system" or whatever the part of Windows deals with window focus probably considers the window to have focus, whereas the higher level system that determines if to send the input to the app or thread at all knows that it shouldn't send it there.

You can use WM_NCACTIVATE and check wParam to know if the window title-bar will be drawn active or inactive, which should be enough to know if you're active..

Yes, I agree. It probably wouldn't cause any problems with normal applications. It also wouldn't cause any serious issues with full-screen applications, because they don't really need to lock/center the cursor when controlling a 3D camera. The reason this is giving my app so much grief is because its windowed, and trying to make use of the Windows hardware cursor instead of generating its own cursor position via Direct Input. If I don't lock it, the cursor will fly off the window into other places while the user is trying to look around in the game. Then as soon as they click, they accidentally click some random thing in the background and lose game focus.

Edited by Stephany

Share this post


Link to post
Share on other sites
Erik Rufelt    5901


It also wouldn't cause any serious issues with full-screen applications

 

The window still receives those messages, even if it is a fullscreen borderless window.

Share this post


Link to post
Share on other sites
Buckeye    10747


WM_SHOWWINDOW seems to indicate that it will tell you when your app becomes top-most

 

I don't see anything in the docs for WM_SHOWWINDOW regarding z-order, only minimizing, maximizing or restoring under specific circumstances (and a specific call to ShowWindow). Can you provide a reference?

Share this post


Link to post
Share on other sites
Stephany    166

 


It also wouldn't cause any serious issues with full-screen applications

 

The window still receives those messages, even if it is a fullscreen borderless window.

 

Yes, but they don't need to worry about the cursor wandering off of the screen while its hidden. Their window covers the entire desktop/display, so there is no danger of accidentally hovering/clicking something in the background.

 

Or at least there shouldn't be. In all honesty, I believe there is a slight danger of it happening, even then. I remember having windows unfocus my fullscreen app while clicking inside my game, before I started locking the mouse to the center. I believe it is still possible to scroll over the edge of your window region in fullscreen - although it may only happen when the game switches to a lower resolution display. I'm not sure, as its been a while since I experienced it.

 

 


WM_SHOWWINDOW seems to indicate that it will tell you when your app becomes top-most

 

I don't see anything in the docs for WM_SHOWWINDOW regarding z-order, only minimizing, maximizing or restoring under specific circumstances (and a specific call to ShowWindow). Can you provide a reference?

 

No, you are right. I got that mixed up. It mentions that it sends messages when the "window is being uncovered" and "window is being covered" but it appears that it only sends these regarding maximized windows for some reason. That explains why it didn't work.

Edited by Stephany

Share this post


Link to post
Share on other sites
Buckeye    10747


It mentions that it sends messages when the "window is being uncovered" and "window is being covered" but it appears that it only sends these regarding maximized windows for some reason. That explains why it didn't work.

 

It "didn't work" because being uncovered for any reason doesn't necessarily mean the window is now top-most, or was activated, or got the focus. It means, well, it was uncovered. E.g., if a non-maximized window completely covers your app window, and the user "uncovers" the app window by dragging the covering window out of the way, there's no change in z-order or focus. Other than the parent-change reasons given in the docs, I can't think of a good use for the message. Perhaps more importantly, I couldn't get the message sent at all to a test window under the (non-parent-related reasons) conditions specified! laugh.png  It appears that the docs for messages should be understood to include the proviso: "If this message is, in fact, actually received by a window, here are the conditions the message implies. However, it should not be assumed that the message is always sent when those conditions exist."

Share this post


Link to post
Share on other sites
Stephany    166

I did manage to get things working well enough to move on. I use GetForegroundWindow() on startup to detect how things start. If my window starts in the background, then my game internally unfocuses (without any changes to the actual window - it still thinks its focused), and I keep checking GetForegroundWindow() until it becomes the foreground window. Once it does, I stop checking it altogether, and deal with things as normal. As far as I know, there is no way for this situation to occur after the window is created, so it should work well enough. It's not a solution to the problem, but it seems to work around it well enough.

Share this post


Link to post
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now

Sign in to follow this