diff --git a/CHANGELOG.md b/CHANGELOG.md index 6c30e7e5..f2ad6446 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -23,6 +23,7 @@ And please only add new entries to the top of this list, right below the `# Unre - On Wayland, `wayland-csd-adwaita` now uses `ab_glyph` instead of `crossfont` to render the title for decorations. - On Wayland, a new `wayland-csd-adwaita-crossfont` feature was added to use `crossfont` instead of `ab_glyph` for decorations. - On Wayland, if not otherwise specified use upstream automatic CSD theme selection. +- On X11, added `WindowExtX11::with_parent` to create child windows. # 0.27.3 diff --git a/FEATURES.md b/FEATURES.md index 7e889970..4890db8c 100644 --- a/FEATURES.md +++ b/FEATURES.md @@ -137,6 +137,7 @@ If your PR makes notable changes to Winit's features, please update this section * X11 Override Redirect Flag * GTK Theme Variant * Base window size +* Setting the X11 parent window ### iOS * `winit` has a minimum OS requirement of iOS 8 diff --git a/examples/child_window.rs b/examples/child_window.rs new file mode 100644 index 00000000..38c4d861 --- /dev/null +++ b/examples/child_window.rs @@ -0,0 +1,83 @@ +#[cfg(all(target_os = "linux", feature = "x11"))] +use std::collections::HashMap; + +#[cfg(all(target_os = "linux", feature = "x11"))] +use winit::{ + dpi::{LogicalPosition, LogicalSize, Position}, + event::{ElementState, Event, KeyboardInput, WindowEvent}, + event_loop::{ControlFlow, EventLoop, EventLoopWindowTarget}, + platform::x11::{WindowBuilderExtX11, WindowExtX11}, + window::{Window, WindowBuilder, WindowId}, +}; + +#[cfg(all(target_os = "linux", feature = "x11"))] +fn spawn_child_window( + parent: u32, + event_loop: &EventLoopWindowTarget<()>, + windows: &mut HashMap, +) { + let child_window = WindowBuilder::new() + .with_parent(WindowId::from(parent as u64)) + .with_title("child window") + .with_inner_size(LogicalSize::new(200.0f32, 200.0f32)) + .with_position(Position::Logical(LogicalPosition::new(0.0, 0.0))) + .with_visible(true) + .build(event_loop) + .unwrap(); + + let id = child_window.xlib_window().unwrap() as u32; + windows.insert(id, child_window); + println!("child window created with id: {}", id); +} + +#[cfg(all(target_os = "linux", feature = "x11"))] +fn main() { + let mut windows = HashMap::new(); + + let event_loop: EventLoop<()> = EventLoop::new(); + let parent_window = WindowBuilder::new() + .with_title("parent window") + .with_position(Position::Logical(LogicalPosition::new(0.0, 0.0))) + .with_inner_size(LogicalSize::new(640.0f32, 480.0f32)) + .build(&event_loop) + .unwrap(); + + let root = parent_window.xlib_window().unwrap() as u32; + println!("parent window id: {})", root); + + event_loop.run(move |event: Event<'_, ()>, event_loop, control_flow| { + *control_flow = ControlFlow::Wait; + + if let Event::WindowEvent { event, window_id } = event { + match event { + WindowEvent::CloseRequested => { + windows.clear(); + *control_flow = ControlFlow::Exit; + } + WindowEvent::CursorEntered { device_id: _ } => { + // println when the cursor entered in a window even if the child window is created + // by some key inputs. + // the child windows are always placed at (0, 0) with size (200, 200) in the parent window, + // so we also can see this log when we move the cursor arround (200, 200) in parent window. + println!("cursor entered in the window {:?}", window_id); + } + WindowEvent::KeyboardInput { + input: + KeyboardInput { + state: ElementState::Pressed, + .. + }, + .. + } => { + spawn_child_window(root, event_loop, &mut windows); + } + _ => (), + } + } + }) +} + +#[cfg(not(all(target_os = "linux", feature = "x11")))] +fn main() { + panic!("This example is supported only on x11."); +} diff --git a/src/platform/x11.rs b/src/platform/x11.rs index 655d6281..37c52424 100644 --- a/src/platform/x11.rs +++ b/src/platform/x11.rs @@ -1,6 +1,7 @@ use std::os::raw; use std::{ptr, sync::Arc}; +use crate::window::WindowId; use crate::{ event_loop::{EventLoopBuilder, EventLoopWindowTarget}, monitor::MonitorHandle, @@ -171,6 +172,8 @@ pub trait WindowBuilderExtX11 { fn with_x11_visual(self, visual_infos: *const T) -> Self; fn with_x11_screen(self, screen_id: i32) -> Self; + /// Build window with parent window. + fn with_parent(self, parent_id: WindowId) -> Self; /// Build window with the given `general` and `instance` names. /// @@ -227,6 +230,12 @@ impl WindowBuilderExtX11 for WindowBuilder { self } + #[inline] + fn with_parent(mut self, parent_id: WindowId) -> Self { + self.platform_specific.parent_id = Some(parent_id.0); + self + } + #[inline] fn with_override_redirect(mut self, override_redirect: bool) -> Self { self.platform_specific.override_redirect = override_redirect; diff --git a/src/platform_impl/linux/mod.rs b/src/platform_impl/linux/mod.rs index 6f472be2..5948e876 100644 --- a/src/platform_impl/linux/mod.rs +++ b/src/platform_impl/linux/mod.rs @@ -95,6 +95,8 @@ pub struct PlatformSpecificWindowBuilderAttributes { #[cfg(feature = "x11")] pub screen_id: Option, #[cfg(feature = "x11")] + pub parent_id: Option, + #[cfg(feature = "x11")] pub base_size: Option, #[cfg(feature = "x11")] pub override_redirect: bool, @@ -115,6 +117,8 @@ impl Default for PlatformSpecificWindowBuilderAttributes { #[cfg(feature = "x11")] screen_id: None, #[cfg(feature = "x11")] + parent_id: None, + #[cfg(feature = "x11")] base_size: None, #[cfg(feature = "x11")] override_redirect: false, diff --git a/src/platform_impl/linux/x11/window.rs b/src/platform_impl/linux/x11/window.rs index fa340e16..7f685105 100644 --- a/src/platform_impl/linux/x11/window.rs +++ b/src/platform_impl/linux/x11/window.rs @@ -119,7 +119,12 @@ impl UnownedWindow { pl_attribs: PlatformSpecificWindowBuilderAttributes, ) -> Result { let xconn = &event_loop.xconn; - let root = event_loop.root; + let root = if let Some(id) = pl_attribs.parent_id { + // WindowId is XID under the hood which doesn't exceed u32, so this conversion is lossless + u64::from(id) as _ + } else { + event_loop.root + }; let mut monitors = xconn.available_monitors(); let guessed_monitor = if monitors.is_empty() {