Skip to main content

maolan_plugin_host/
gui_x11.rs

1//! Shared X11 container-window helpers for hosting plugin GUIs.
2
3#[cfg(all(unix, not(target_os = "macos")))]
4pub mod x11 {
5    use std::os::raw::{c_char, c_int, c_uint, c_ulong};
6
7    pub type Display = std::ffi::c_void;
8    pub type Window = c_ulong;
9
10    #[link(name = "X11")]
11    unsafe extern "C" {
12        pub fn XOpenDisplay(display_name: *const c_char) -> *mut Display;
13        pub fn XCloseDisplay(display: *mut Display) -> c_int;
14        pub fn XDefaultScreen(display: *mut Display) -> c_int;
15        pub fn XRootWindow(display: *mut Display, screen: c_int) -> Window;
16        pub fn XBlackPixel(display: *mut Display, screen: c_int) -> c_ulong;
17        pub fn XWhitePixel(display: *mut Display, screen: c_int) -> c_ulong;
18        pub fn XCreateSimpleWindow(
19            display: *mut Display,
20            parent: Window,
21            x: c_int,
22            y: c_int,
23            width: c_uint,
24            height: c_uint,
25            border_width: c_uint,
26            border: c_ulong,
27            background: c_ulong,
28        ) -> Window;
29        pub fn XStoreName(display: *mut Display, w: Window, name: *const c_char) -> c_int;
30        pub fn XMapWindow(display: *mut Display, w: Window) -> c_int;
31        pub fn XUnmapWindow(display: *mut Display, w: Window) -> c_int;
32        pub fn XDestroyWindow(display: *mut Display, w: Window) -> c_int;
33        pub fn XResizeWindow(
34            display: *mut Display,
35            w: Window,
36            width: c_uint,
37            height: c_uint,
38        ) -> c_int;
39        pub fn XFlush(display: *mut Display) -> c_int;
40    }
41
42    pub struct ContainerWindow {
43        display: *mut Display,
44        window: Window,
45    }
46
47    unsafe impl Send for ContainerWindow {}
48
49    impl ContainerWindow {
50        pub fn window(&self) -> Window {
51            self.window
52        }
53
54        pub fn map(&self) {
55            unsafe {
56                XMapWindow(self.display, self.window);
57                XFlush(self.display);
58            }
59        }
60
61        pub fn unmap(&self) {
62            unsafe {
63                XUnmapWindow(self.display, self.window);
64                XFlush(self.display);
65            }
66        }
67
68        pub fn resize(&self, width: u32, height: u32) {
69            unsafe {
70                XResizeWindow(self.display, self.window, width, height);
71                XFlush(self.display);
72            }
73        }
74    }
75
76    impl Drop for ContainerWindow {
77        fn drop(&mut self) {
78            unsafe {
79                XDestroyWindow(self.display, self.window);
80                XFlush(self.display);
81                XCloseDisplay(self.display);
82            }
83        }
84    }
85
86    pub fn create_container_window(
87        display_name: Option<&str>,
88        parent: Option<Window>,
89        title: &str,
90        width: u32,
91        height: u32,
92    ) -> Result<ContainerWindow, String> {
93        let display_name_c = display_name.and_then(|s| std::ffi::CString::new(s).ok());
94        let display_name_ptr = display_name_c
95            .as_ref()
96            .map(|s| s.as_ptr())
97            .unwrap_or(std::ptr::null());
98
99        let display = unsafe { XOpenDisplay(display_name_ptr) };
100        if display.is_null() {
101            return Err("failed to open X11 display".to_string());
102        }
103
104        let screen = unsafe { XDefaultScreen(display) };
105        let root = unsafe { XRootWindow(display, screen) };
106        let black = unsafe { XBlackPixel(display, screen) };
107        let white = unsafe { XWhitePixel(display, screen) };
108
109        let parent = parent.unwrap_or(root);
110
111        let window =
112            unsafe { XCreateSimpleWindow(display, parent, 0, 0, width, height, 1, black, white) };
113        if window == 0 {
114            unsafe { XCloseDisplay(display) };
115            return Err("failed to create X11 container window".to_string());
116        }
117
118        if let Ok(cstr) = std::ffi::CString::new(title) {
119            unsafe {
120                XStoreName(display, window, cstr.as_ptr());
121            }
122        }
123
124        Ok(ContainerWindow { display, window })
125    }
126}