Prevent stealing focus on new windows (#1176)

This commit is contained in:
Murarth 2019-09-19 08:47:51 -07:00 committed by Osspial
parent 695547f4ca
commit eb20612d77
2 changed files with 56 additions and 25 deletions

View file

@ -15,6 +15,8 @@
- On macOS, fix events not being emitted during modal loops, such as when windows are being resized - On macOS, fix events not being emitted during modal loops, such as when windows are being resized
by the user. by the user.
- On Windows, fix hovering the mouse over the active window creating an endless stream of CursorMoved events. - On Windows, fix hovering the mouse over the active window creating an endless stream of CursorMoved events.
- On X11, prevent stealing input focus when creating a new window.
Only steal input focus when entering fullscreen mode.
# 0.20.0 Alpha 3 (2019-08-14) # 0.20.0 Alpha 3 (2019-08-14)

View file

@ -56,12 +56,14 @@ pub struct SharedState {
pub frame_extents: Option<util::FrameExtentsHeuristic>, pub frame_extents: Option<util::FrameExtentsHeuristic>,
pub min_inner_size: Option<LogicalSize>, pub min_inner_size: Option<LogicalSize>,
pub max_inner_size: Option<LogicalSize>, pub max_inner_size: Option<LogicalSize>,
pub is_visible: bool,
} }
impl SharedState { impl SharedState {
fn new(dpi_factor: f64) -> Mutex<Self> { fn new(dpi_factor: f64, is_visible: bool) -> Mutex<Self> {
let mut shared_state = SharedState::default(); let mut shared_state = SharedState::default();
shared_state.guessed_dpi = Some(dpi_factor); shared_state.guessed_dpi = Some(dpi_factor);
shared_state.is_visible = is_visible;
Mutex::new(shared_state) Mutex::new(shared_state)
} }
} }
@ -230,7 +232,7 @@ impl UnownedWindow {
cursor_grabbed: Mutex::new(false), cursor_grabbed: Mutex::new(false),
cursor_visible: Mutex::new(true), cursor_visible: Mutex::new(true),
ime_sender: Mutex::new(event_loop.ime_sender.clone()), ime_sender: Mutex::new(event_loop.ime_sender.clone()),
shared_state: SharedState::new(dpi_factor), shared_state: SharedState::new(dpi_factor, window_attrs.visible),
pending_redraws: event_loop.pending_redraws.clone(), pending_redraws: event_loop.pending_redraws.clone(),
}; };
@ -355,6 +357,8 @@ impl UnownedWindow {
unsafe { unsafe {
(xconn.xlib.XMapRaised)(xconn.display, window.xwindow); (xconn.xlib.XMapRaised)(xconn.display, window.xwindow);
} //.queue(); } //.queue();
window.wait_for_visibility_notify();
} }
// Attempt to make keyboard input repeat detectable // Attempt to make keyboard input repeat detectable
@ -420,27 +424,6 @@ impl UnownedWindow {
.set_always_on_top_inner(window_attrs.always_on_top) .set_always_on_top_inner(window_attrs.always_on_top)
.queue(); .queue();
} }
if window_attrs.visible {
unsafe {
// XSetInputFocus generates an error if the window is not visible, so we wait
// until we receive VisibilityNotify.
let mut event = MaybeUninit::uninit();
(xconn.xlib.XIfEvent)(
// This will flush the request buffer IF it blocks.
xconn.display,
event.as_mut_ptr(),
Some(visibility_predicate),
window.xwindow as _,
);
(xconn.xlib.XSetInputFocus)(
xconn.display,
window.xwindow,
ffi::RevertToParent,
ffi::CurrentTime,
);
}
}
} }
// We never want to give the user a broken window, since by then, it's too late to handle. // We never want to give the user a broken window, since by then, it's too late to handle.
@ -566,11 +549,32 @@ impl UnownedWindow {
fn set_fullscreen_hint(&self, fullscreen: bool) -> util::Flusher<'_> { fn set_fullscreen_hint(&self, fullscreen: bool) -> util::Flusher<'_> {
let fullscreen_atom = let fullscreen_atom =
unsafe { self.xconn.get_atom_unchecked(b"_NET_WM_STATE_FULLSCREEN\0") }; unsafe { self.xconn.get_atom_unchecked(b"_NET_WM_STATE_FULLSCREEN\0") };
self.set_netwm(fullscreen.into(), (fullscreen_atom as c_long, 0, 0, 0)) let flusher = self.set_netwm(fullscreen.into(), (fullscreen_atom as c_long, 0, 0, 0));
if fullscreen {
// Ensure that the fullscreen window receives input focus to prevent
// locking up the user's display.
unsafe {
(self.xconn.xlib.XSetInputFocus)(
self.xconn.display,
self.xwindow,
ffi::RevertToParent,
ffi::CurrentTime,
);
}
}
flusher
} }
fn set_fullscreen_inner(&self, fullscreen: Option<Fullscreen>) -> Option<util::Flusher<'_>> { fn set_fullscreen_inner(&self, fullscreen: Option<Fullscreen>) -> Option<util::Flusher<'_>> {
let mut shared_state_lock = self.shared_state.lock(); let mut shared_state_lock = self.shared_state.lock();
if !shared_state_lock.is_visible {
// Setting fullscreen on a window that is not visible will generate an error.
return None;
}
let old_fullscreen = shared_state_lock.fullscreen.clone(); let old_fullscreen = shared_state_lock.fullscreen.clone();
if old_fullscreen == fullscreen { if old_fullscreen == fullscreen {
return None; return None;
@ -681,7 +685,7 @@ impl UnownedWindow {
pub fn set_fullscreen(&self, fullscreen: Option<Fullscreen>) { pub fn set_fullscreen(&self, fullscreen: Option<Fullscreen>) {
if let Some(flusher) = self.set_fullscreen_inner(fullscreen) { if let Some(flusher) = self.set_fullscreen_inner(fullscreen) {
flusher flusher
.flush() .sync()
.expect("Failed to change window fullscreen state"); .expect("Failed to change window fullscreen state");
self.invalidate_cached_frame_extents(); self.invalidate_cached_frame_extents();
} }
@ -837,12 +841,22 @@ impl UnownedWindow {
#[inline] #[inline]
pub fn set_visible(&self, visible: bool) { pub fn set_visible(&self, visible: bool) {
let is_visible = self.shared_state.lock().is_visible;
if visible == is_visible {
return;
}
match visible { match visible {
true => unsafe { true => unsafe {
(self.xconn.xlib.XMapRaised)(self.xconn.display, self.xwindow); (self.xconn.xlib.XMapRaised)(self.xconn.display, self.xwindow);
self.xconn self.xconn
.flush_requests() .flush_requests()
.expect("Failed to call XMapRaised"); .expect("Failed to call XMapRaised");
// Some X requests may generate an error if the window is not
// visible, so we must wait until the window becomes visible.
self.wait_for_visibility_notify();
}, },
false => unsafe { false => unsafe {
(self.xconn.xlib.XUnmapWindow)(self.xconn.display, self.xwindow); (self.xconn.xlib.XUnmapWindow)(self.xconn.display, self.xwindow);
@ -851,6 +865,21 @@ impl UnownedWindow {
.expect("Failed to call XUnmapWindow"); .expect("Failed to call XUnmapWindow");
}, },
} }
self.shared_state.lock().is_visible = visible;
}
fn wait_for_visibility_notify(&self) {
unsafe {
let mut event = MaybeUninit::uninit();
(self.xconn.xlib.XIfEvent)(
self.xconn.display,
event.as_mut_ptr(),
Some(visibility_predicate),
self.xwindow as _,
);
}
} }
fn update_cached_frame_extents(&self) { fn update_cached_frame_extents(&self) {