RFR: 8359899: Stage.isFocused() returns invalid value when Stage fails to receive focus
Lukasz Kostyra
lkostyra at openjdk.org
Fri Sep 19 12:49:01 UTC 2025
On Thu, 18 Sep 2025 15:47:03 GMT, Martin Fox <mfox at openjdk.org> wrote:
>> This PR fixes `isFocused()` returning invalid value when Stage fails to receive focus after calling `Stage.show()`. This problem is Windows-only.
>>
>> In Windows the `SetForegroundWindow()` API lists [a set of conditions to successfully grant focus to a Window](https://learn.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-setforegroundwindow#remarks). If any of the conditions are not met, the API will return FALSE. JavaFX did not respect that and instead assumed that receiving `WM_ACTIVATE` with our Window being activated is enough to assume the Window is in focus (which in some cases is not true).
>>
>> I first tried reacting to `WM_SETFOCUS` and `WM_KILLFOCUS` but it seems those messages are not sent when the window is shown for the first time (instead `WM_ACTIVATE` is used).
>>
>> To correct this behavior, I noticed the following path is the most reliable:
>> - Call `ShowWindow()` using `SW_SHOWNA` instead of `SW_SHOW` - that makes the window visible but does NOT activate it
>> - Call `SetForegroundWindow()` - that will attempt to give the Window focus and will also activate it if it is successful
>> - If successful, Java `notifyFocus` callback will be called via `WM_ACTIVATE` handler
>> - If it fails, we call the `notifyFocus` callback manually informing the upper layers the focus is lost. This establishes the correct state of `Window.focused` property.
>>
>> With this change I observed that all tests pass as intended as long as two conditions are met (these are needed to satisfy `SetForegroundWindow()` restrictions):
>> - Gradle build is ran without the Gradle daemon
>> - The terminal running Gradle test is in foreground
>>
>> If any of above two conditions is not met, some tests (including canary test from https://github.com/openjdk/jfx/pull/1804) now timeout/fail when checking whether `Window.isFocused()` is true.
>>
>> Manually started JavaFX apps (ex. Ensemble) run as they used to and still receive focus upon Stage showing.
>
> I’ve been testing this PR by launching the manual KeyboardTest app.
>
> java @build/run.args tests/manual/events/KeyboardTest.java
>
> The call to SetForegroundWindow fails but not before activating the window. Since it’s not the foreground window this PR correctly sets the JavaFX focused property back to false. But when I later click on the title bar to bring the window to the foreground the focused property is not updated.
>
> I’m not sure how we’re expected to detect that our HWND has come to the foreground. There’s no specific message sent when this happens and since the OS thinks the window is already active and focused we don’t get WM_ACTIVATE or WM_SETFOCUS. There’s some messages we could use heuristically (like WM_NCACTIVATE) but I couldn’t find anything more clear cut. I'll keep looking but it doesn't look like Windows makes this easy.
@beldenfox thanks for checking. I remember doing a fair share of looking for a reliable answer to this and couldn't find anything specific, other that SetForegroundWindow being able to fail and having to "handle it". I'll get back to testing this starting next week as well, maybe we'll manage to track down some more reasonable solution.
-------------
PR Comment: https://git.openjdk.org/jfx/pull/1849#issuecomment-3312065779
More information about the openjfx-dev
mailing list