From f54cc11e4441a706a276c05f0e65f48a69f779bd Mon Sep 17 00:00:00 2001 From: Andrew Baxter Date: Thu, 27 Jun 2024 11:22:21 +0900 Subject: [PATCH] feat: Add Linux method to use existing GTK window (#938) --- .changes/gtk-app-getter.md | 5 ++ .changes/new-from-gtk-window.md | 5 ++ src/platform/unix.rs | 27 ++++++++++- src/platform_impl/linux/window.rs | 79 ++++++++++++++++++++++++++++++- 4 files changed, 114 insertions(+), 2 deletions(-) create mode 100644 .changes/gtk-app-getter.md create mode 100644 .changes/new-from-gtk-window.md diff --git a/.changes/gtk-app-getter.md b/.changes/gtk-app-getter.md new file mode 100644 index 000000000..9855c1b9e --- /dev/null +++ b/.changes/gtk-app-getter.md @@ -0,0 +1,5 @@ +--- +"tao": patch +--- + +Add `EventLoopWindowTargetExtUnix::gtk_app` getter. \ No newline at end of file diff --git a/.changes/new-from-gtk-window.md b/.changes/new-from-gtk-window.md new file mode 100644 index 000000000..6edd797d6 --- /dev/null +++ b/.changes/new-from-gtk-window.md @@ -0,0 +1,5 @@ +--- +"tao": patch +--- + +Add `WindowExtUnix::new_from_gtk_window`. \ No newline at end of file diff --git a/src/platform/unix.rs b/src/platform/unix.rs index c7c49328c..781c6b36b 100644 --- a/src/platform/unix.rs +++ b/src/platform/unix.rs @@ -18,8 +18,9 @@ pub use crate::platform_impl::x11; pub use crate::platform_impl::EventLoop as UnixEventLoop; use crate::{ + error::OsError, event_loop::{EventLoopBuilder, EventLoopWindowTarget}, - platform_impl::{x11::xdisplay::XError, Parent}, + platform_impl::{x11::xdisplay::XError, Parent, Window as UnixWindow}, window::{Window, WindowBuilder}, }; @@ -60,6 +61,14 @@ impl EventLoopBuilderExtUnix for EventLoopBuilder { /// Additional methods on `Window` that are specific to Unix. pub trait WindowExtUnix { + /// Create a new Tao window from an existing GTK window. Generally you should use + /// the non-Linux `WindowBuilder`, this is for those who need lower level window access + /// and know what they're doing. + fn new_from_gtk_window( + event_loop_window_target: &EventLoopWindowTarget, + window: gtk::ApplicationWindow, + ) -> Result; + /// Returns the `gtk::ApplicatonWindow` from gtk crate that is used by this window. fn gtk_window(&self) -> >k::ApplicationWindow; @@ -83,6 +92,14 @@ impl WindowExtUnix for Window { fn set_skip_taskbar(&self, skip: bool) { self.window.set_skip_taskbar(skip); } + + fn new_from_gtk_window( + event_loop_window_target: &EventLoopWindowTarget, + window: gtk::ApplicationWindow, + ) -> Result { + let window = UnixWindow::new_from_gtk_window(&event_loop_window_target.p, window)?; + Ok(Window { window: window }) + } } pub trait WindowBuilderExtUnix { @@ -188,6 +205,9 @@ pub trait EventLoopWindowTargetExtUnix { // /// // /// The pointer will become invalid when the winit `EventLoop` is destroyed. // fn wayland_display(&self) -> Option<*mut raw::c_void>; + + /// Returns the gtk application for this event loop. + fn gtk_app(&self) -> >k::Application; } impl EventLoopWindowTargetExtUnix for EventLoopWindowTarget { @@ -224,6 +244,11 @@ impl EventLoopWindowTargetExtUnix for EventLoopWindowTarget { // _ => None, // } // } + + #[inline] + fn gtk_app(&self) -> >k::Application { + &self.p.app + } } unsafe extern "C" fn x_error_callback( diff --git a/src/platform_impl/linux/window.rs b/src/platform_impl/linux/window.rs index 733f4f431..7bc9a44ba 100644 --- a/src/platform_impl/linux/window.rs +++ b/src/platform_impl/linux/window.rs @@ -314,6 +314,83 @@ impl Window { Ok(win) } + pub(crate) fn new_from_gtk_window( + event_loop_window_target: &EventLoopWindowTarget, + window: gtk::ApplicationWindow, + ) -> Result { + let window_requests_tx = event_loop_window_target.window_requests_tx.clone(); + let draw_tx = event_loop_window_target.draw_tx.clone(); + + let window_id = WindowId(window.id()); + event_loop_window_target + .windows + .borrow_mut() + .insert(window_id); + + let win_scale_factor = window.scale_factor(); + + let w_pos = window.position(); + let position: Rc<(AtomicI32, AtomicI32)> = Rc::new((w_pos.0.into(), w_pos.1.into())); + let position_clone = position.clone(); + + let w_size = window.size(); + let size: Rc<(AtomicI32, AtomicI32)> = Rc::new((w_size.0.into(), w_size.1.into())); + let size_clone = size.clone(); + + window.connect_configure_event(move |_, event| { + let (x, y) = event.position(); + position_clone.0.store(x, Ordering::Release); + position_clone.1.store(y, Ordering::Release); + + let (w, h) = event.size(); + size_clone.0.store(w as i32, Ordering::Release); + size_clone.1.store(h as i32, Ordering::Release); + + false + }); + + let w_max = window.is_maximized(); + let maximized: Rc = Rc::new(w_max.into()); + let max_clone = maximized.clone(); + let minimized = Rc::new(AtomicBool::new(false)); + let minimized_clone = minimized.clone(); + + window.connect_window_state_event(move |_, event| { + let state = event.new_window_state(); + max_clone.store(state.contains(WindowState::MAXIMIZED), Ordering::Release); + minimized_clone.store(state.contains(WindowState::ICONIFIED), Ordering::Release); + glib::Propagation::Proceed + }); + + let scale_factor: Rc = Rc::new(win_scale_factor.into()); + let scale_factor_clone = scale_factor.clone(); + window.connect_scale_factor_notify(move |window| { + scale_factor_clone.store(window.scale_factor(), Ordering::Release); + }); + + if let Err(e) = draw_tx.send(window_id) { + log::warn!("Failed to send redraw event to event channel: {}", e); + } + + let win = Self { + window_id, + window, + default_vbox: None, + window_requests_tx, + draw_tx, + scale_factor, + position, + size, + maximized, + minimized, + fullscreen: RefCell::new(None), + inner_size_constraints: RefCell::new(WindowSizeConstraints::default()), + preferred_theme: None, + }; + + Ok(win) + } + pub fn id(&self) -> WindowId { self.window_id } @@ -843,7 +920,7 @@ impl Window { } } - pub(crate) fn set_skip_taskbar(&self, skip: bool) { + pub fn set_skip_taskbar(&self, skip: bool) { if let Err(e) = self .window_requests_tx .send((self.window_id, WindowRequest::SetSkipTaskbar(skip)))