diff --git a/window/src/os/x11/connection.rs b/window/src/os/x11/connection.rs index 439b466ab..791022450 100644 --- a/window/src/os/x11/connection.rs +++ b/window/src/os/x11/connection.rs @@ -19,6 +19,7 @@ use xcb_util::ffi::keysyms::{xcb_key_symbols_alloc, xcb_key_symbols_free, xcb_ke pub struct XConnection { pub conn: xcb_util::ewmh::Connection, pub screen_num: i32, + pub root: xcb::xproto::Window, pub keyboard: Keyboard, pub kbd_ev: u8, pub atom_protocols: xcb::Atom, @@ -390,10 +391,13 @@ impl XConnection { .request_check() .context("xcb::open_font_checked")?; + let root = screen.root(); + let conn = XConnection { conn, cursor_font_id, screen_num, + root, atom_protocols, atom_clipboard, atom_delete, diff --git a/window/src/os/x11/window.rs b/window/src/os/x11/window.rs index c77555878..8f2a08f6c 100644 --- a/window/src/os/x11/window.rs +++ b/window/src/os/x11/window.rs @@ -629,8 +629,69 @@ impl XWindowInner { Ok(()) } + fn is_fullscreen(&self) -> anyhow::Result { + let conn = self.conn(); + + let net_wm_state = xcb::intern_atom(conn.conn(), false, "_NET_WM_STATE") + .get_reply()? + .atom(); + let net_wm_state_fullscreen = + xcb::intern_atom(conn.conn(), false, "_NET_WM_STATE_FULLSCREEN") + .get_reply()? + .atom(); + + let reply = xcb::xproto::get_property( + &conn, + false, + self.window_id, + net_wm_state, + xcb::xproto::ATOM_ATOM, + 0, + 1024, + ) + .get_reply()?; + + let state = reply.value::(); + + Ok(state + .iter() + .position(|&x| x == net_wm_state_fullscreen) + .is_some()) + } + + fn set_fullscreen_hint(&mut self, enable: bool) -> anyhow::Result<()> { + let conn = self.conn(); + + let net_wm_state = xcb::intern_atom(conn.conn(), false, "_NET_WM_STATE") + .get_reply()? + .atom(); + let net_wm_state_fullscreen = + xcb::intern_atom(conn.conn(), false, "_NET_WM_STATE_FULLSCREEN") + .get_reply()? + .atom(); + + let data: [u32; 5] = [if enable { 1 } else { 0 }, net_wm_state_fullscreen, 0, 0, 0]; + + // Ask window manager to change our fullscreen state + xcb::xproto::send_event( + &conn, + true, + conn.root, + xcb::xproto::EVENT_MASK_SUBSTRUCTURE_REDIRECT + | xcb::xproto::EVENT_MASK_SUBSTRUCTURE_NOTIFY, + &xcb::xproto::ClientMessageEvent::new( + 32, + self.window_id, + net_wm_state, + xcb::ClientMessageData::from_data32(data), + ), + ); + + Ok(()) + } + #[allow(dead_code, clippy::identity_op)] - fn disable_decorations(&mut self) -> anyhow::Result<()> { + fn adjust_decorations(&mut self, enable: bool) -> anyhow::Result<()> { // Set the motif hints to disable decorations. // See https://stackoverflow.com/a/1909708 #[repr(C)] @@ -654,7 +715,7 @@ impl XWindowInner { let hints = MwmHints { flags: HINTS_DECORATIONS, functions: 0, - decorations: 0, // off + decorations: if enable { FUNC_ALL } else { 0 }, input_mode: 0, status: 0, }; @@ -843,6 +904,17 @@ impl WindowOpsMut for XWindowInner { self.paint_all = true; } + fn toggle_fullscreen(&mut self) { + let fullscreen = match self.is_fullscreen() { + Ok(f) => f, + Err(err) => { + log::error!("Failed to determine fullscreen state: {}", err); + return; + } + }; + self.set_fullscreen_hint(!fullscreen).ok(); + } + fn set_inner_size(&mut self, width: usize, height: usize) { xcb::configure_window( self.conn().conn(), @@ -872,24 +944,9 @@ impl WindowOpsMut for XWindowInner { | xcb_util::ewmh::MOVE_RESIZE_WINDOW_Y, coords.x as u32, coords.y as u32, - // these dimensions are ignored because we're not - // passing the relevant MOVE_RESIZE_XX flags above, - // but are preserved here for clarity on what these - // parameters do self.width as u32, self.height as u32, ); - - /* - xcb::configure_window( - self.conn.conn(), - self.window_id, - &[ - (xcb::CONFIG_WINDOW_X as u16, coords.x as u32), - (xcb::CONFIG_WINDOW_Y as u16, coords.y as u32), - ], - ); - */ } /// Change the title for the window manager @@ -933,6 +990,13 @@ impl WindowOps for XWindow { }) } + fn toggle_fullscreen(&self) -> Future<()> { + XConnection::with_window_inner(self.0, |inner| { + inner.toggle_fullscreen(); + Ok(()) + }) + } + fn show(&self) -> Future<()> { XConnection::with_window_inner(self.0, |inner| { inner.show(); diff --git a/window/src/os/x_and_wayland.rs b/window/src/os/x_and_wayland.rs index fa893ee5c..979d475bb 100644 --- a/window/src/os/x_and_wayland.rs +++ b/window/src/os/x_and_wayland.rs @@ -134,6 +134,14 @@ impl WindowOps for Window { } } + fn toggle_fullscreen(&self) -> Future<()> { + match self { + Self::X11(x) => x.toggle_fullscreen(), + #[cfg(feature = "wayland")] + Self::Wayland(w) => w.toggle_fullscreen(), + } + } + fn show(&self) -> Future<()> { match self { Self::X11(x) => x.show(),