1use std::sync::Arc;
4use std::time::Duration;
5use std::{convert::TryInto, num::NonZeroU32};
6
7use smithay_client_toolkit::reexports::client::{
8 globals::registry_queue_init,
9 protocol::{wl_output, wl_pointer, wl_seat, wl_shm, wl_surface},
10 Connection, Proxy, QueueHandle,
11};
12use smithay_client_toolkit::reexports::csd_frame::{
13 CursorIcon, DecorationsFrame, FrameAction, FrameClick, ResizeEdge,
14};
15use smithay_client_toolkit::reexports::protocols::xdg::shell::client::xdg_toplevel::ResizeEdge as XdgResizeEdge;
16use smithay_client_toolkit::{
17 compositor::{CompositorHandler, CompositorState},
18 delegate_compositor, delegate_output, delegate_pointer, delegate_registry, delegate_seat,
19 delegate_shm, delegate_subcompositor, delegate_xdg_shell, delegate_xdg_window,
20 output::{OutputHandler, OutputState},
21 registry::{ProvidesRegistryState, RegistryState},
22 registry_handlers,
23 seat::{
24 pointer::{
25 PointerData, PointerEvent, PointerEventKind, PointerHandler, ThemeSpec, ThemedPointer,
26 },
27 Capability, SeatHandler, SeatState,
28 },
29 shell::{
30 xdg::{
31 window::{DecorationMode, Window, WindowConfigure, WindowDecorations, WindowHandler},
32 XdgShell, XdgSurface,
33 },
34 WaylandSurface,
35 },
36 shm::{
37 slot::{Buffer, SlotPool},
38 Shm, ShmHandler,
39 },
40 subcompositor::SubcompositorState,
41};
42
43use sctk_adwaita::{AdwaitaFrame, FrameConfig};
44
45fn main() {
46 let conn = Connection::connect_to_env().unwrap();
47
48 let (globals, mut event_queue) = registry_queue_init(&conn).unwrap();
49 let qh = event_queue.handle();
50 let registry_state = RegistryState::new(&globals);
51 let seat_state = SeatState::new(&globals, &qh);
52 let output_state = OutputState::new(&globals, &qh);
53 let compositor_state =
54 CompositorState::bind(&globals, &qh).expect("wl_compositor not available");
55 let subcompositor_state =
56 SubcompositorState::bind(compositor_state.wl_compositor().clone(), &globals, &qh)
57 .expect("wl_subcompositor not available");
58 let shm_state = Shm::bind(&globals, &qh).expect("wl_shm not available");
59 let xdg_shell_state = XdgShell::bind(&globals, &qh).expect("xdg shell not available");
60
61 let width = 256;
62 let height = 256;
63 let pool = SlotPool::new(width as usize * height as usize * 4, &shm_state)
64 .expect("Failed to create pool");
65
66 let window_surface = compositor_state.create_surface(&qh);
67
68 let window = xdg_shell_state.create_window(window_surface, WindowDecorations::ClientOnly, &qh);
69 window.set_title("A wayland window");
70 window.set_app_id("simple-window");
72 window.set_min_size(Some((2, 1)));
73
74 window.commit();
80
81 let mut simple_window = SimpleWindow {
82 title: String::from("/usr/lib/xorg/modules/input"),
83 registry_state,
84 seat_state,
85 output_state,
86 compositor_state: Arc::new(compositor_state),
87 subcompositor_state: Arc::new(subcompositor_state),
88 shm_state,
89 _xdg_shell_state: xdg_shell_state,
90
91 exit: false,
92 first_configure: true,
93 pool,
94 width: NonZeroU32::new(width).unwrap(),
95 height: NonZeroU32::new(height).unwrap(),
96 shift: None,
97 buffer: None,
98 window,
99 window_frame: None,
100 themed_pointer: None,
101 set_cursor: false,
102 cursor_icon: CursorIcon::Crosshair,
103 hide_titlebar: false,
104 };
105
106 loop {
109 event_queue.blocking_dispatch(&mut simple_window).unwrap();
110
111 if simple_window.exit {
112 println!("exiting example");
113 break;
114 }
115 }
116}
117
118struct SimpleWindow {
119 title: String,
120 registry_state: RegistryState,
121 seat_state: SeatState,
122 output_state: OutputState,
123 compositor_state: Arc<CompositorState>,
124 subcompositor_state: Arc<SubcompositorState>,
125 shm_state: Shm,
126 _xdg_shell_state: XdgShell,
127
128 exit: bool,
129 first_configure: bool,
130 pool: SlotPool,
131 width: NonZeroU32,
132 height: NonZeroU32,
133 shift: Option<u32>,
134 buffer: Option<Buffer>,
135 window: Window,
136 window_frame: Option<AdwaitaFrame<Self>>,
137 themed_pointer: Option<ThemedPointer>,
138 set_cursor: bool,
139 cursor_icon: CursorIcon,
140
141 hide_titlebar: bool,
142}
143
144impl CompositorHandler for SimpleWindow {
145 fn scale_factor_changed(
146 &mut self,
147 _conn: &Connection,
148 _qh: &QueueHandle<Self>,
149 surface: &wl_surface::WlSurface,
150 new_factor: i32,
151 ) {
152 if self.window.wl_surface() == surface {
153 if let Some(frame) = self.window_frame.as_mut() {
154 frame.set_scaling_factor(new_factor as f64);
155 }
156 }
157 }
158
159 fn transform_changed(
160 &mut self,
161 _: &Connection,
162 _: &QueueHandle<Self>,
163 _: &wl_surface::WlSurface,
164 _: wl_output::Transform,
165 ) {
166 }
168
169 fn frame(
170 &mut self,
171 conn: &Connection,
172 qh: &QueueHandle<Self>,
173 _surface: &wl_surface::WlSurface,
174 _time: u32,
175 ) {
176 self.draw(conn, qh);
177 }
178
179 fn surface_enter(
180 &mut self,
181 _conn: &Connection,
182 _qh: &QueueHandle<Self>,
183 _surface: &wl_surface::WlSurface,
184 _output: &wl_output::WlOutput,
185 ) {
186 }
187
188 fn surface_leave(
189 &mut self,
190 _conn: &Connection,
191 _qh: &QueueHandle<Self>,
192 _surface: &wl_surface::WlSurface,
193 _output: &wl_output::WlOutput,
194 ) {
195 }
196}
197
198impl OutputHandler for SimpleWindow {
199 fn output_state(&mut self) -> &mut OutputState {
200 &mut self.output_state
201 }
202
203 fn new_output(
204 &mut self,
205 _conn: &Connection,
206 _qh: &QueueHandle<Self>,
207 _output: wl_output::WlOutput,
208 ) {
209 }
210
211 fn update_output(
212 &mut self,
213 _conn: &Connection,
214 _qh: &QueueHandle<Self>,
215 _output: wl_output::WlOutput,
216 ) {
217 }
218
219 fn output_destroyed(
220 &mut self,
221 _conn: &Connection,
222 _qh: &QueueHandle<Self>,
223 _output: wl_output::WlOutput,
224 ) {
225 }
226}
227
228impl WindowHandler for SimpleWindow {
229 fn request_close(&mut self, _: &Connection, _: &QueueHandle<Self>, _: &Window) {
230 self.exit = true;
231 }
232
233 fn configure(
234 &mut self,
235 conn: &Connection,
236 qh: &QueueHandle<Self>,
237 window: &Window,
238 configure: WindowConfigure,
239 _serial: u32,
240 ) {
241 self.buffer = None;
242
243 println!(
244 "Configure size {:?}, decorations: {:?}",
245 configure.new_size, configure.decoration_mode
246 );
247
248 let (width, height) = if configure.decoration_mode == DecorationMode::Client {
249 let window_frame = self.window_frame.get_or_insert_with(|| {
250 let mut frame = AdwaitaFrame::new(
251 &self.window,
252 &self.shm_state,
253 self.compositor_state.clone(),
254 self.subcompositor_state.clone(),
255 qh.clone(),
256 FrameConfig::auto().hide_titlebar(self.hide_titlebar),
257 )
258 .expect("failed to create client side decorations frame.");
259 frame.set_title(self.title.clone());
260 frame
261 });
262
263 window_frame.set_hidden(false);
265
266 window_frame.update_state(configure.state);
268
269 window_frame.update_wm_capabilities(configure.capabilities);
271
272 let (width, height) = match configure.new_size {
273 (Some(width), Some(height)) => {
274 window_frame.subtract_borders(width, height)
276 }
277 _ => {
278 (Some(self.width), Some(self.height))
280 }
281 };
282
283 let width = width.unwrap_or(NonZeroU32::new(1).unwrap());
285 let height = height.unwrap_or(NonZeroU32::new(1).unwrap());
286
287 window_frame.resize(width, height);
288
289 let (x, y) = window_frame.location();
290 let outer_size = window_frame.add_borders(width.get(), height.get());
291 window.xdg_surface().set_window_geometry(
292 x,
293 y,
294 outer_size.0 as i32,
295 outer_size.1 as i32,
296 );
297
298 (width, height)
299 } else {
300 if let Some(frame) = self.window_frame.as_mut() {
302 frame.set_hidden(true)
303 }
304 let width = configure.new_size.0.unwrap_or(self.width);
305 let height = configure.new_size.1.unwrap_or(self.height);
306 self.window.xdg_surface().set_window_geometry(
307 0,
308 0,
309 width.get() as i32,
310 height.get() as i32,
311 );
312 (width, height)
313 };
314
315 self.width = width;
317 self.height = height;
318
319 if self.first_configure {
321 self.first_configure = false;
322 self.draw(conn, qh);
323 }
324 }
325}
326
327impl SeatHandler for SimpleWindow {
328 fn seat_state(&mut self) -> &mut SeatState {
329 &mut self.seat_state
330 }
331
332 fn new_seat(&mut self, _: &Connection, _: &QueueHandle<Self>, _: wl_seat::WlSeat) {}
333
334 fn new_capability(
335 &mut self,
336 _conn: &Connection,
337 qh: &QueueHandle<Self>,
338 seat: wl_seat::WlSeat,
339 capability: Capability,
340 ) {
341 if capability == Capability::Pointer && self.themed_pointer.is_none() {
342 println!("Set pointer capability");
343 println!("Creating pointer theme");
344 let surface = self.compositor_state.create_surface(qh);
345 let themed_pointer = self
346 .seat_state
347 .get_pointer_with_theme(
348 qh,
349 &seat,
350 self.shm_state.wl_shm(),
351 surface,
352 ThemeSpec::default(),
353 )
354 .expect("Failed to create pointer");
355 self.themed_pointer.replace(themed_pointer);
356 }
357 }
358
359 fn remove_capability(
360 &mut self,
361 _conn: &Connection,
362 _: &QueueHandle<Self>,
363 _: wl_seat::WlSeat,
364 capability: Capability,
365 ) {
366 if capability == Capability::Pointer && self.themed_pointer.is_some() {
367 println!("Unset pointer capability");
368 self.themed_pointer.take().unwrap().pointer().release();
369 }
370 }
371
372 fn remove_seat(&mut self, _: &Connection, _: &QueueHandle<Self>, _: wl_seat::WlSeat) {}
373}
374
375impl PointerHandler for SimpleWindow {
376 fn pointer_frame(
377 &mut self,
378 _conn: &Connection,
379 _qh: &QueueHandle<Self>,
380 pointer: &wl_pointer::WlPointer,
381 events: &[PointerEvent],
382 ) {
383 use PointerEventKind::*;
384 for event in events {
385 let (x, y) = event.position;
386 match event.kind {
387 Enter { .. } => {
388 self.set_cursor = true;
389 self.cursor_icon = self
390 .window_frame
391 .as_mut()
392 .and_then(|frame| {
393 frame.click_point_moved(Duration::ZERO, &event.surface.id(), x, y)
394 })
395 .unwrap_or(CursorIcon::Crosshair)
396 .to_owned();
397
398 if &event.surface == self.window.wl_surface() {
399 println!("Pointer entered @{:?}", event.position);
400 }
401 }
402 Leave { .. } => {
403 if &event.surface != self.window.wl_surface() {
404 if let Some(window_frame) = self.window_frame.as_mut() {
405 window_frame.click_point_left();
406 }
407 }
408 println!("Pointer left");
409 }
410 Motion { time } => {
411 if let Some(new_cursor) = self.window_frame.as_mut().and_then(|frame| {
412 frame.click_point_moved(
413 Duration::from_millis(time as u64),
414 &event.surface.id(),
415 x,
416 y,
417 )
418 }) {
419 self.set_cursor = true;
420 self.cursor_icon = new_cursor.to_owned();
421 }
422 }
423 Press {
424 button,
425 serial,
426 time,
427 }
428 | Release {
429 button,
430 serial,
431 time,
432 } => {
433 let pressed = if matches!(event.kind, Press { .. }) {
434 true
435 } else {
436 false
437 };
438 if &event.surface != self.window.wl_surface() {
439 let click = match button {
440 0x110 => FrameClick::Normal,
441 0x111 => FrameClick::Alternate,
442 _ => continue,
443 };
444
445 if let Some(action) = self.window_frame.as_mut().and_then(|frame| {
446 frame.on_click(Duration::from_millis(time as u64), click, pressed)
447 }) {
448 self.frame_action(pointer, serial, action);
449 }
450 } else if pressed {
451 println!("Press {:x} @ {:?}", button, event.position);
452
453 if button == 0x111 {
455 self.hide_titlebar = !self.hide_titlebar;
456
457 if let Some(frame) = self.window_frame.as_mut() {
458 let config = FrameConfig::auto();
460
461 if self.hide_titlebar {
462 frame.set_config(config.hide_titlebar(true));
463 self.window.xdg_surface().set_window_geometry(
464 0,
465 0,
466 self.width.get() as i32,
467 self.height.get() as i32,
468 );
469 } else {
470 let (width, height) = (self.width, self.height);
471
472 frame.set_config(config.hide_titlebar(false));
473 frame.resize(width, height);
474
475 let (x, y) = frame.location();
476 let outer_size = frame.add_borders(width.get(), height.get());
477 self.window.xdg_surface().set_window_geometry(
478 x,
479 y,
480 outer_size.0 as i32,
481 outer_size.1 as i32,
482 );
483
484 self.width = width;
486 self.height = height;
487 }
488 }
489 } else {
490 self.shift = self.shift.xor(Some(0));
491 }
492 }
493 }
494 Axis {
495 horizontal,
496 vertical,
497 ..
498 } => {
499 if &event.surface == self.window.wl_surface() {
500 println!("Scroll H:{horizontal:?}, V:{vertical:?}");
501 }
502 }
503 }
504 }
505 }
506}
507impl SimpleWindow {
508 fn frame_action(&mut self, pointer: &wl_pointer::WlPointer, serial: u32, action: FrameAction) {
509 let pointer_data = pointer.data::<PointerData>().unwrap();
510 let seat = pointer_data.seat();
511 match action {
512 FrameAction::Close => self.exit = true,
513 FrameAction::Minimize => self.window.set_minimized(),
514 FrameAction::Maximize => self.window.set_maximized(),
515 FrameAction::UnMaximize => self.window.unset_maximized(),
516 FrameAction::ShowMenu(x, y) => self.window.show_window_menu(seat, serial, (x, y)),
517 FrameAction::Resize(edge) => {
518 let edge = match edge {
519 ResizeEdge::None => XdgResizeEdge::None,
520 ResizeEdge::Top => XdgResizeEdge::Top,
521 ResizeEdge::Bottom => XdgResizeEdge::Bottom,
522 ResizeEdge::Left => XdgResizeEdge::Left,
523 ResizeEdge::TopLeft => XdgResizeEdge::TopLeft,
524 ResizeEdge::BottomLeft => XdgResizeEdge::BottomLeft,
525 ResizeEdge::Right => XdgResizeEdge::Right,
526 ResizeEdge::TopRight => XdgResizeEdge::TopRight,
527 ResizeEdge::BottomRight => XdgResizeEdge::BottomRight,
528 _ => return,
529 };
530 self.window.resize(seat, serial, edge);
531 }
532 FrameAction::Move => self.window.move_(seat, serial),
533 _ => (),
534 }
535 }
536}
537
538impl ShmHandler for SimpleWindow {
539 fn shm_state(&mut self) -> &mut Shm {
540 &mut self.shm_state
541 }
542}
543
544impl SimpleWindow {
545 pub fn draw(&mut self, conn: &Connection, qh: &QueueHandle<Self>) {
546 if self.set_cursor {
547 let _ = self
548 .themed_pointer
549 .as_mut()
550 .unwrap()
551 .set_cursor(conn, self.cursor_icon);
552 self.set_cursor = false;
553 }
554
555 let width = self.width.get();
556 let height = self.height.get();
557 let stride = width as i32 * 4;
558
559 let buffer = self.buffer.get_or_insert_with(|| {
560 self.pool
561 .create_buffer(
562 width as i32,
563 height as i32,
564 stride,
565 wl_shm::Format::Argb8888,
566 )
567 .expect("create buffer")
568 .0
569 });
570
571 let canvas = match self.pool.canvas(buffer) {
572 Some(canvas) => canvas,
573 None => {
574 let (second_buffer, canvas) = self
577 .pool
578 .create_buffer(
579 width as i32,
580 height as i32,
581 stride,
582 wl_shm::Format::Argb8888,
583 )
584 .expect("create buffer");
585 *buffer = second_buffer;
586 canvas
587 }
588 };
589
590 {
592 let shift = self.shift.unwrap_or(0);
593 canvas
594 .chunks_exact_mut(4)
595 .enumerate()
596 .for_each(|(index, chunk)| {
597 let x = ((index + shift as usize) % width as usize) as u32;
598 let y = (index / width as usize) as u32;
599
600 let a = 0xFF;
601 let r = u32::min(((width - x) * 0xFF) / width, ((height - y) * 0xFF) / height);
602 let g = u32::min((x * 0xFF) / width, ((height - y) * 0xFF) / height);
603 let b = u32::min(((width - x) * 0xFF) / width, (y * 0xFF) / height);
604 let color = (a << 24) + (r << 16) + (g << 8) + b;
605
606 let array: &mut [u8; 4] = chunk.try_into().unwrap();
607 *array = color.to_le_bytes();
608 });
609
610 if let Some(shift) = &mut self.shift {
611 *shift = (*shift + 1) % width;
612 }
613 }
614
615 self.window_frame.as_mut().map(|frame| {
617 if frame.is_dirty() && !frame.is_hidden() {
618 frame.draw();
619 }
620 });
621
622 self.window.wl_surface().damage_buffer(
624 0,
625 0,
626 self.width.get() as i32,
627 self.height.get() as i32,
628 );
629
630 self.window
632 .wl_surface()
633 .frame(qh, self.window.wl_surface().clone());
634
635 buffer
637 .attach_to(self.window.wl_surface())
638 .expect("buffer attach");
639 self.window.wl_surface().commit();
640 }
641}
642
643delegate_compositor!(SimpleWindow);
644delegate_subcompositor!(SimpleWindow);
645delegate_output!(SimpleWindow);
646delegate_shm!(SimpleWindow);
647
648delegate_seat!(SimpleWindow);
649delegate_pointer!(SimpleWindow);
650
651delegate_xdg_shell!(SimpleWindow);
652delegate_xdg_window!(SimpleWindow);
653
654delegate_registry!(SimpleWindow);
655
656impl ProvidesRegistryState for SimpleWindow {
657 fn registry(&mut self) -> &mut RegistryState {
658 &mut self.registry_state
659 }
660 registry_handlers![OutputState, SeatState,];
661}