On Thu, 18 Sep 2025 15:47:03 GMT, Martin Fox <[email protected]> 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.

Reviewers: @beldenfox @kevinrushforth

-------------

PR Comment: https://git.openjdk.org/jfx/pull/1849#issuecomment-3462804291

Reply via email to