Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Incorrect focus events for windows created without focus #2102

Closed
swooster opened this issue Dec 18, 2021 · 3 comments
Closed

Incorrect focus events for windows created without focus #2102

swooster opened this issue Dec 18, 2021 · 3 comments
Labels
B - bug Dang, that shouldn't have happened C - needs investigation Issue must be confirmed and researched DS - windows

Comments

@swooster
Copy link
Contributor

On Windows 10, I've noticed that if cargo run takes a while to build my executable and I switch focus to another application, then the focus events sent to my executable are incorrect, preventing it from determining whether it's currently focused.

Here's a minimal reproducible example:

use std::{thread, time};

use winit::{
    event::{Event, WindowEvent},
    event_loop::{ControlFlow, EventLoop},
    window::Window,
};

fn main() {
    println!("Select another window within 5 seconds.");
    thread::sleep(time::Duration::from_secs(5));

    let event_loop = EventLoop::new();
    let window = Window::new(&event_loop).unwrap();

    event_loop.run(move |event, _event_loop_target, control_flow| match event {
        Event::WindowEvent {
            event: WindowEvent::CloseRequested,
            window_id,
        } if window_id == window.id() => {
            *control_flow = ControlFlow::Exit;
        }
        Event::WindowEvent {
            event: WindowEvent::Focused(focused),
            window_id,
        } if window_id == window.id() => {
            println!("Focused: {}", focused);
            // window.set_cursor_grab(focused).unwrap();
            // window.set_cursor_visible(!focused);
        }
        _ => (),
    });
}

The behavior I'd expect is at least one (ideally both) of the following:

  • The winit window receives a Focused(true) event if it's created in a focused state.
  • The winit window receives a Focused(false) event if it's created in an unfocused state.

With winit 0.26.0, if you run this and focus another application during the 5 second wait, then the winit window will be created in the background, yet the application will receive a Focused(true) event. If the application tries to act on this by grabbing the cursor (uncomment the lines near the end), then you end up with a strange situation where the cursor is constrained to the bounds of the winit window, despite another application having focus and being in front.

With winit 0.20.0 through 0.25.0, if you follow the above steps, then the winit window receives no event, even when focused for the first time.

I haven't tested with versions of winit older than 0.20.0.

@maroider maroider added DS - windows C - needs investigation Issue must be confirmed and researched B - bug Dang, that shouldn't have happened labels Dec 18, 2021
@maroider
Copy link
Member

So I put a panic!() inside our WM_SETFOCUS handler, and it looks like having it be invoked while inside the init function is causing some trouble. #1933 strikes again.

the backtrace
thread 'main' panicked at 'explicit panic', C:\Users\Markus Røyset\source-2\github.com\maroider\winit\src\platform_impl\windows\event_loop.rs:1678:13
stack backtrace:
   0: std::panicking::begin_panic<str>
             at /rustc/09c42c45858d5f3aedfa670698275303a3d19afa\library\std\src\panicking.rs:543
   1: winit::platform_impl::platform::event_loop::public_window_callback_inner::closure$0<tuple$<> >
             at .\src\platform_impl\windows\event_loop.rs:1678
   2: core::ops::function::FnOnce::call_once<winit::platform_impl::platform::event_loop::public_window_callback_inner::closure$0,tuple$<> >
             at /rustc/09c42c45858d5f3aedfa670698275303a3d19afa\library\core\src\ops\function.rs:227
   3: core::panic::unwind_safe::impl$23::call_once<isize,winit::platform_impl::platform::event_loop::public_window_callback_inner::closure$0>
             at /rustc/09c42c45858d5f3aedfa670698275303a3d19afa\library\core\src\panic\unwind_safe.rs:271
   4: std::panicking::try::do_call<core::panic::unwind_safe::AssertUnwindSafe<winit::platform_impl::platform::event_loop::public_window_callback_inner::closure$0>,isize>
             at /rustc/09c42c45858d5f3aedfa670698275303a3d19afa\library\std\src\panicking.rs:403
   5: std::panicking::try::do_catch<core::panic::unwind_safe::AssertUnwindSafe<winit::platform_impl::platform::window::impl$4::on_nccreate::closure$0>,tuple$<winit::platform_impl::platform::window::Window,winit::platform_impl::platform::event_loop::WindowData<t
   6: std::panicking::try<isize,core::panic::unwind_safe::AssertUnwindSafe<winit::platform_impl::platform::event_loop::public_window_callback_inner::closure$0> >
             at /rustc/09c42c45858d5f3aedfa670698275303a3d19afa\library\std\src\panicking.rs:367
   7: std::panic::catch_unwind<core::panic::unwind_safe::AssertUnwindSafe<winit::platform_impl::platform::event_loop::public_window_callback_inner::closure$0>,isize>
             at /rustc/09c42c45858d5f3aedfa670698275303a3d19afa\library\std\src\panic.rs:129
   8: winit::platform_impl::platform::event_loop::runner::EventLoopRunner<tuple$<> >::catch_unwind<tuple$<>,isize,winit::platform_impl::platform::event_loop::public_window_callback_inner::closure$0>
             at .\src\platform_impl\windows\event_loop\runner.rs:152
   9: winit::platform_impl::platform::event_loop::public_window_callback_inner<tuple$<> >
             at .\src\platform_impl\windows\event_loop.rs:2075
  10: winit::platform_impl::platform::event_loop::public_window_callback<tuple$<> >
             at .\src\platform_impl\windows\event_loop.rs:848
  11: CallWindowProcW
  12: DispatchMessageW
  13: IsWindowVisible
  14: KiUserCallbackDispatcher
  15: NtUserMessageCall
  16: GetWindowTextW
  17: GetWindowTextW
  18: winit::platform_impl::platform::event_loop::public_window_callback_inner::closure$0<tuple$<> >
             at .\src\platform_impl\windows\event_loop.rs:2070
  19: core::ops::function::FnOnce::call_once<winit::platform_impl::platform::event_loop::public_window_callback_inner::closure$0,tuple$<> >
             at /rustc/09c42c45858d5f3aedfa670698275303a3d19afa\library\core\src\ops\function.rs:227
  20: core::panic::unwind_safe::impl$23::call_once<isize,winit::platform_impl::platform::event_loop::public_window_callback_inner::closure$0>
             at /rustc/09c42c45858d5f3aedfa670698275303a3d19afa\library\core\src\panic\unwind_safe.rs:271
  21: std::panicking::try::do_call<core::panic::unwind_safe::AssertUnwindSafe<winit::platform_impl::platform::event_loop::public_window_callback_inner::closure$0>,isize>
             at /rustc/09c42c45858d5f3aedfa670698275303a3d19afa\library\std\src\panicking.rs:403
  22: std::panicking::try::do_catch<core::panic::unwind_safe::AssertUnwindSafe<winit::platform_impl::platform::window::impl$4::on_nccreate::closure$0>,tuple$<winit::platform_impl::platform::window::Window,winit::platform_impl::platform::event_loop::WindowData<t
  23: std::panicking::try<isize,core::panic::unwind_safe::AssertUnwindSafe<winit::platform_impl::platform::event_loop::public_window_callback_inner::closure$0> >
             at /rustc/09c42c45858d5f3aedfa670698275303a3d19afa\library\std\src\panicking.rs:367
  24: std::panic::catch_unwind<core::panic::unwind_safe::AssertUnwindSafe<winit::platform_impl::platform::event_loop::public_window_callback_inner::closure$0>,isize>
             at /rustc/09c42c45858d5f3aedfa670698275303a3d19afa\library\std\src\panic.rs:129
  25: winit::platform_impl::platform::event_loop::runner::EventLoopRunner<tuple$<> >::catch_unwind<tuple$<>,isize,winit::platform_impl::platform::event_loop::public_window_callback_inner::closure$0>
             at .\src\platform_impl\windows\event_loop\runner.rs:152
  26: winit::platform_impl::platform::event_loop::public_window_callback_inner<tuple$<> >
             at .\src\platform_impl\windows\event_loop.rs:2075
  27: winit::platform_impl::platform::event_loop::public_window_callback<tuple$<> >
             at .\src\platform_impl\windows\event_loop.rs:848
  28: CallWindowProcW
  29: DispatchMessageW
  30: IsWindowVisible
  31: KiUserCallbackDispatcher
  32: NtUserShowWindow
  33: winit::platform_impl::platform::window_state::WindowFlags::apply_diff
             at .\src\platform_impl\windows\window_state.rs:251
  34: winit::platform_impl::platform::window_state::WindowState::set_window_flags<winit::platform_impl::platform::window::impl$0::set_visible::closure$0::closure$0>
             at .\src\platform_impl\windows\window_state.rs:142
  35: winit::platform_impl::platform::window::impl$0::set_visible::closure$0
             at .\src\platform_impl\windows\window.rs:92
  36: winit::platform_impl::platform::event_loop::EventLoopThreadExecutor::execute_in_thread<winit::platform_impl::platform::window::impl$0::set_visible::closure$0>
             at .\src\platform_impl\windows\event_loop.rs:489
  37: winit::platform_impl::platform::window::Window::set_visible
             at .\src\platform_impl\windows\window.rs:91
  38: winit::platform_impl::platform::window::InitData<tuple$<> >::on_create<tuple$<> >
             at .\src\platform_impl\windows\window.rs:816
  39: winit::platform_impl::platform::event_loop::public_window_callback<tuple$<> >
             at .\src\platform_impl\windows\event_loop.rs:834

@swooster
Copy link
Contributor Author

swooster commented Jan 21, 2022

I'm ignorant about the windows API so I don't know if this is helpful, and it sounds like you already have an approach to solve this, but I tried printing most window/thread events to see if I could find a sensible difference between when the window is/isn't focused. I was only able to spot one difference: the window is sent a WM_NCACTIVATE with a wparam that seems to correspond to whether the window is focused or not... According to the WM_NCACTIVATE MSDN page:

wParam

Indicates when a title bar or icon needs to be changed to indicate an active or inactive state. If an active title bar or icon is to be drawn, the wParam parameter is TRUE. If an inactive title bar or icon is to be drawn, wParam is FALSE.

I don't see any windows event loop code handling WM_NCACTIVATE, though that kinda makes sense since that same MSDN article recommends against handling nonclient events. However, I don't see any other way to detect of the window is really active..?

@swooster
Copy link
Contributor Author

Just a note that with #2159 merged, this issue should be resolved.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
B - bug Dang, that shouldn't have happened C - needs investigation Issue must be confirmed and researched DS - windows
Development

No branches or pull requests

2 participants