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 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 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 let Some(cursor_shape) = singletons.get_opt::<WpCursorShapeManagerV1>(1, 1) {
102 let registry = display.get_registry();
103 proxy::set_event_handler_local(
104 ®istry,
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}