simple_window/common/
simple_window.rs

1use {
2    crate::common::{
3        protocols::{
4            cursor_shape_v1::{
5                wp_cursor_shape_device_v1::{WpCursorShapeDeviceV1, WpCursorShapeDeviceV1Shape},
6                wp_cursor_shape_manager_v1::WpCursorShapeManagerV1,
7            },
8            viewporter::{wp_viewport::WpViewport, wp_viewporter::WpViewporter},
9            wayland::{
10                wl_buffer::WlBuffer,
11                wl_compositor::WlCompositor,
12                wl_display::WlDisplay,
13                wl_pointer::{WlPointer, WlPointerEventHandler, WlPointerRef},
14                wl_registry::{WlRegistry, WlRegistryEventHandler, WlRegistryRef},
15                wl_seat::{WlSeat, WlSeatCapability, WlSeatEventHandler, WlSeatRef},
16                wl_shm::{WlShm, WlShmFormat},
17                wl_surface::{WlSurface, WlSurfaceRef},
18            },
19            xdg_shell::{
20                xdg_surface::{XdgSurface, XdgSurfaceEventHandler, XdgSurfaceRef},
21                xdg_toplevel::{XdgToplevel, XdgToplevelEventHandler, XdgToplevelRef},
22                xdg_wm_base::{XdgWmBase, XdgWmBaseEventHandler, XdgWmBaseRef},
23            },
24        },
25        singletons::Singletons,
26    },
27    std::{
28        cell::{Cell, RefCell},
29        collections::HashMap,
30        io::Write,
31        os::fd::AsFd,
32        rc::Rc,
33    },
34    tempfile::tempfile,
35    wl_client::{
36        Fixed, Libwayland,
37        proxy::{self, OwnedProxy},
38    },
39};
40
41pub struct SimpleWindow {
42    pub exit: Rc<Cell<bool>>,
43    wl_compositor: WlCompositor,
44    wl_shm: WlShm,
45    xdg_wm_base: XdgWmBase,
46    wp_viewporter: WpViewporter,
47    wl_surface: WlSurface,
48    wp_viewport: WpViewport,
49    xdg_surface: XdgSurface,
50    xdg_toplevel: XdgToplevel,
51}
52
53pub fn prepare(singletons: Singletons) -> SimpleWindow {
54    let lib = Libwayland::open().unwrap();
55    let con = lib.connect_to_default_display().unwrap();
56    let queue = con.create_local_queue(c"simple-window");
57    let display: WlDisplay = queue.display();
58
59    // We have a hard dependency on these globals.
60    let wl_compositor = singletons.get::<WlCompositor>(1, 1);
61    let wl_shm = singletons.get::<WlShm>(1, 1);
62    let xdg_wm_base = singletons.get::<XdgWmBase>(1, 1);
63    let wp_viewporter = singletons.get::<WpViewporter>(1, 1);
64
65    proxy::set_event_handler_local(&xdg_wm_base, XdgWmPingPong);
66
67    // Create the window.
68    let buffer = {
69        let mut tempfile = tempfile().unwrap();
70        tempfile.write_all(&[0, 0, 0, 255]).unwrap();
71        let pool = wl_shm.create_pool(tempfile.as_fd(), 4);
72        let buffer = pool.create_buffer(0, 1, 1, 4, WlShmFormat::ARGB8888);
73        pool.destroy();
74        buffer
75    };
76    let wl_surface = wl_compositor.create_surface();
77    let wp_viewport = wp_viewporter.get_viewport(&wl_surface);
78    let xdg_surface = xdg_wm_base.get_xdg_surface(&wl_surface);
79    let xdg_toplevel = xdg_surface.get_toplevel();
80    xdg_toplevel.set_title("simple-window");
81    wl_surface.commit();
82
83    let exit = Rc::new(Cell::new(false));
84
85    let event_handler = WindowEventHandler {
86        state: Rc::new(WindowState {
87            surface: wl_surface.clone(),
88            buffer,
89            viewport: wp_viewport.clone(),
90            exit: exit.clone(),
91            attached_buffer: Cell::new(false),
92            pending_size: Cell::new((DEFAULT_WIDTH, DEFAULT_HEIGHT)),
93            current_size: Cell::new((0, 0)),
94        }),
95    };
96    proxy::set_event_handler_local(&xdg_surface, event_handler.clone());
97    proxy::set_event_handler_local(&xdg_toplevel, event_handler.clone());
98
99    // If we have the cursor shape manager, create a new registry to handle seats
100    // and give the pointers the default shape when they enter our window.
101    if let Some(cursor_shape) = singletons.get_opt::<WpCursorShapeManagerV1>(1, 1) {
102        let registry = display.get_registry();
103        proxy::set_event_handler_local(
104            &registry,
105            SeatRegistryHandler {
106                registry: registry.clone(),
107                wp_cursor_shape_manager_v1: cursor_shape,
108                seats: Default::default(),
109            },
110        );
111    }
112
113    SimpleWindow {
114        exit,
115        wl_compositor,
116        wl_shm,
117        xdg_wm_base,
118        wp_viewporter,
119        wl_surface,
120        wp_viewport,
121        xdg_surface,
122        xdg_toplevel,
123    }
124}
125
126struct SeatRegistryHandler {
127    registry: WlRegistry,
128    wp_cursor_shape_manager_v1: WpCursorShapeManagerV1,
129    seats: RefCell<HashMap<u32, Rc<RefCell<SeatData>>>>,
130}
131
132impl WlRegistryEventHandler for SeatRegistryHandler {
133    fn global(&self, _slf: &WlRegistryRef, name: u32, interface: &str, version: u32) {
134        if interface != WlSeat::INTERFACE {
135            return;
136        }
137        let seat: WlSeat = self.registry.bind(name, version.min(5));
138        let data = Rc::new(RefCell::new(SeatData {
139            seat: seat.clone(),
140            pointer: None,
141            shape: None,
142        }));
143        proxy::set_event_handler_local(
144            &seat,
145            SeatEventHandler {
146                wp_cursor_shape_manager_v1: self.wp_cursor_shape_manager_v1.clone(),
147                data: data.clone(),
148            },
149        );
150        self.seats.borrow_mut().insert(name, data);
151    }
152
153    fn global_remove(&self, _slf: &WlRegistryRef, name: u32) {
154        let seats = &mut *self.seats.borrow_mut();
155        let Some(seat) = seats.remove(&name) else {
156            return;
157        };
158        let data = &mut *seat.borrow_mut();
159        data.destroy_pointer();
160        if proxy::version(&*data.seat) >= WlSeat::REQ__RELEASE__SINCE {
161            data.seat.release();
162        } else {
163            proxy::destroy(&data.seat);
164        }
165    }
166}
167
168struct SeatEventHandler {
169    wp_cursor_shape_manager_v1: WpCursorShapeManagerV1,
170    data: Rc<RefCell<SeatData>>,
171}
172
173struct SeatData {
174    seat: WlSeat,
175    pointer: Option<WlPointer>,
176    shape: Option<WpCursorShapeDeviceV1>,
177}
178
179impl SeatData {
180    fn destroy_pointer(&mut self) {
181        if let Some(shape) = self.shape.take() {
182            shape.destroy();
183        }
184        if let Some(pointer) = self.pointer.take() {
185            if proxy::version(&*pointer) >= WlPointer::REQ__RELEASE__SINCE {
186                pointer.release();
187            } else {
188                proxy::destroy(&pointer);
189            }
190        }
191    }
192}
193
194impl WlSeatEventHandler for SeatEventHandler {
195    fn capabilities(&self, _slf: &WlSeatRef, capabilities: WlSeatCapability) {
196        let data = &mut *self.data.borrow_mut();
197        if capabilities.contains(WlSeatCapability::POINTER) {
198            if data.pointer.is_some() {
199                return;
200            }
201            let pointer = data.seat.get_pointer();
202            let shape = self.wp_cursor_shape_manager_v1.get_pointer(&pointer);
203            proxy::set_event_handler_local(
204                &pointer,
205                PointerEventHandler {
206                    shape: shape.clone(),
207                },
208            );
209            data.pointer = Some(pointer);
210            data.shape = Some(shape);
211        } else {
212            data.destroy_pointer();
213        }
214    }
215}
216
217#[derive(Clone)]
218struct PointerEventHandler {
219    shape: WpCursorShapeDeviceV1,
220}
221
222impl WlPointerEventHandler for PointerEventHandler {
223    fn enter(
224        &self,
225        _slf: &WlPointerRef,
226        serial: u32,
227        _surface: Option<&WlSurfaceRef>,
228        _surface_x: Fixed,
229        _surface_y: Fixed,
230    ) {
231        self.shape
232            .set_shape(serial, WpCursorShapeDeviceV1Shape::DEFAULT);
233    }
234}
235
236#[derive(Clone)]
237struct WindowEventHandler {
238    state: Rc<WindowState>,
239}
240
241struct WindowState {
242    surface: WlSurface,
243    buffer: WlBuffer,
244    viewport: WpViewport,
245    exit: Rc<Cell<bool>>,
246    attached_buffer: Cell<bool>,
247    pending_size: Cell<(i32, i32)>,
248    current_size: Cell<(i32, i32)>,
249}
250
251const DEFAULT_WIDTH: i32 = 800;
252const DEFAULT_HEIGHT: i32 = 600;
253
254impl XdgToplevelEventHandler for WindowEventHandler {
255    fn configure(&self, _slf: &XdgToplevelRef, mut width: i32, mut height: i32, _states: &[u8]) {
256        if width == 0 {
257            width = DEFAULT_WIDTH;
258        }
259        if height == 0 {
260            height = DEFAULT_HEIGHT;
261        }
262        self.state.pending_size.set((width, height));
263    }
264
265    fn close(&self, _slf: &XdgToplevelRef) {
266        self.state.exit.set(true);
267    }
268}
269
270impl XdgSurfaceEventHandler for WindowEventHandler {
271    fn configure(&self, slf: &XdgSurfaceRef, serial: u32) {
272        slf.ack_configure(serial);
273        let state = &*self.state;
274        let mut need_commit = false;
275        if state.current_size.get() != state.pending_size.get() {
276            let (width, height) = state.pending_size.get();
277            state.current_size.set((width, height));
278            state.viewport.set_destination(width, height);
279            need_commit = true;
280        }
281        if !state.attached_buffer.get() {
282            state.attached_buffer.set(true);
283            state.surface.attach(Some(&state.buffer), 0, 0);
284            need_commit = true;
285        }
286        if need_commit {
287            state.surface.commit();
288        }
289    }
290}
291
292struct XdgWmPingPong;
293
294impl XdgWmBaseEventHandler for XdgWmPingPong {
295    fn ping(&self, slf: &XdgWmBaseRef, serial: u32) {
296        slf.pong(serial);
297    }
298}