diff --git a/CHANGELOG.md b/CHANGELOG.md index bfba47c3..d1386c0d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -15,6 +15,8 @@ - On macOS, fix events not being emitted during modal loops, such as when windows are being resized by the user. - 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) diff --git a/src/platform_impl/linux/x11/window.rs b/src/platform_impl/linux/x11/window.rs index 73e53987..078cc4d5 100644 --- a/src/platform_impl/linux/x11/window.rs +++ b/src/platform_impl/linux/x11/window.rs @@ -56,12 +56,14 @@ pub struct SharedState { pub frame_extents: Option, pub min_inner_size: Option, pub max_inner_size: Option, + pub is_visible: bool, } impl SharedState { - fn new(dpi_factor: f64) -> Mutex { + fn new(dpi_factor: f64, is_visible: bool) -> Mutex { let mut shared_state = SharedState::default(); shared_state.guessed_dpi = Some(dpi_factor); + shared_state.is_visible = is_visible; Mutex::new(shared_state) } } @@ -230,7 +232,7 @@ impl UnownedWindow { cursor_grabbed: Mutex::new(false), cursor_visible: Mutex::new(true), 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(), }; @@ -355,6 +357,8 @@ impl UnownedWindow { unsafe { (xconn.xlib.XMapRaised)(xconn.display, window.xwindow); } //.queue(); + + window.wait_for_visibility_notify(); } // Attempt to make keyboard input repeat detectable @@ -420,27 +424,6 @@ impl UnownedWindow { .set_always_on_top_inner(window_attrs.always_on_top) .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. @@ -566,11 +549,32 @@ impl UnownedWindow { fn set_fullscreen_hint(&self, fullscreen: bool) -> util::Flusher<'_> { let fullscreen_atom = 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) -> Option> { 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(); if old_fullscreen == fullscreen { return None; @@ -681,7 +685,7 @@ impl UnownedWindow { pub fn set_fullscreen(&self, fullscreen: Option) { if let Some(flusher) = self.set_fullscreen_inner(fullscreen) { flusher - .flush() + .sync() .expect("Failed to change window fullscreen state"); self.invalidate_cached_frame_extents(); } @@ -837,12 +841,22 @@ impl UnownedWindow { #[inline] pub fn set_visible(&self, visible: bool) { + let is_visible = self.shared_state.lock().is_visible; + + if visible == is_visible { + return; + } + match visible { true => unsafe { (self.xconn.xlib.XMapRaised)(self.xconn.display, self.xwindow); self.xconn .flush_requests() .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 { (self.xconn.xlib.XUnmapWindow)(self.xconn.display, self.xwindow); @@ -851,6 +865,21 @@ impl UnownedWindow { .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) {