Impossible to Detect Window Starting Unfocused? (Windows)

Started by
12 comments, last by Stephany 9 years, 2 months ago

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

Advertisement
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?

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.")

If you really want to notify the the user that the application is ready for use: use the windows taskbar notification API's to highlight your application icon (see this).

In time the project grows, the ignorance of its devs it shows, with many a convoluted function, it plunges into deep compunction, the price of failure is high, Washu's mirth is nigh.

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/

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.

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.

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.

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.

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.

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.

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..

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.


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.

This topic is closed to new replies.

Advertisement