1pub mod keys;
2
3use std::collections::{HashMap, HashSet};
4use std::time::Duration;
5
6use smithay_client_toolkit::activation::RequestData;
7use smithay_client_toolkit::reexports::calloop::EventLoop;
8use smithay_client_toolkit::reexports::calloop_wayland_source::WaylandSource;
9use smithay_client_toolkit::{
10 activation::{ActivationHandler, ActivationState},
11 compositor::{CompositorHandler, CompositorState},
12 delegate_activation, delegate_compositor, delegate_keyboard, delegate_output, delegate_pointer,
13 delegate_registry, delegate_seat, delegate_shm, delegate_xdg_shell, delegate_xdg_window,
14 output::{OutputHandler, OutputState},
15 registry::{ProvidesRegistryState, RegistryState},
16 registry_handlers,
17 seat::{
18 Capability, SeatHandler, SeatState,
19 keyboard::{KeyEvent, KeyboardHandler, Keysym, Modifiers, RawModifiers},
20 pointer::PointerHandler,
21 },
22 shell::{
23 WaylandSurface,
24 xdg::{
25 XdgShell,
26 window::{Window, WindowConfigure, WindowDecorations, WindowHandler},
27 },
28 },
29 shm::{
30 Shm, ShmHandler,
31 slot::{Buffer, SlotPool},
32 },
33};
34
35use wayland_client::{
36 Connection, QueueHandle,
37 globals::registry_queue_init,
38 protocol::{wl_keyboard, wl_output, wl_pointer, wl_seat, wl_shm, wl_surface},
39};
40
41pub use smithay_client_toolkit::seat::{
42 keyboard,
43 pointer::{PointerEvent, PointerEventKind},
44};
45
46pub trait WindowAble {
51 fn update(&mut self, context: Context) -> Option<WLibRequest>;
54
55 fn draw(&mut self, pixel_buffer: &mut [u8], frame_info: WindowSize);
82}
83
84#[derive(Debug, Clone)]
86pub enum Event {
87 KeyPress(KeyEvent),
88 KeyRelease(KeyEvent),
89 PointerEvent(PointerEvent),
90 CloseRequested,
91}
92
93pub enum WLibRequest {
95 CloseAccepted,
98}
99
100#[derive(Debug, Clone)]
102pub struct Context {
103 pub pressed_keys: HashMap<keys::RawKeyCode, keys::KeySym>,
105
106 pub mouse_state: MouseState,
108
109 pub delta_time: std::time::Duration,
111
112 pub close_requested: bool,
115
116 pub is_window_focused: bool,
118
119 pub event_queue: Vec<Event>,
122
123 pub window_size: WindowSize,
125}
126
127#[derive(Debug, Clone)]
129pub struct MouseState {
130 pub position: (f64, f64),
132 pub mouse_buttons_pressed: HashSet<MouseButton>,
134}
135
136#[non_exhaustive]
139#[derive(Debug, Clone, Hash, Eq, PartialEq)]
140pub enum MouseButton {
141 BtnLeft,
142 BtnRight,
143 BtnMiddle,
144 BtnSide,
145 BtnExtra,
146 BtnForward,
147 BtnBack,
148}
149
150impl TryFrom<u32> for MouseButton {
151 type Error = ();
152
153 fn try_from(value: u32) -> Result<Self, Self::Error> {
154 match value {
155 0x110 => Ok(MouseButton::BtnLeft),
156 0x111 => Ok(MouseButton::BtnRight),
157 0x112 => Ok(MouseButton::BtnMiddle),
158 0x113 => Ok(MouseButton::BtnSide),
159 0x114 => Ok(MouseButton::BtnExtra),
160 0x115 => Ok(MouseButton::BtnForward),
161 0x116 => Ok(MouseButton::BtnBack),
162 _ => Err(()),
163 }
164 }
165}
166
167struct WindowManager {
168 registry_state: RegistryState,
169 seat_state: SeatState,
170 output_state: OutputState,
171 shm: Shm,
172 xdg_activation: Option<ActivationState>,
173
174 close_accepted: bool,
175 first_configure: bool,
176 pool: SlotPool,
177 width: u32,
178 height: u32,
179 buffer: Option<Buffer>,
180 window: Window,
181 keyboard: Option<wl_keyboard::WlKeyboard>,
182 keyboard_focus: bool,
183 pointer: Option<wl_pointer::WlPointer>,
184 last_frame_time: Option<std::time::Instant>,
185
186 managed_window: Box<dyn WindowAble>,
187 settings: WLibSettings,
188 context: Context,
189}
190
191#[derive(Debug, Clone)]
193pub struct WindowSize {
194 pub width: u32,
195 pub height: u32,
196}
197
198#[derive(Default)]
200pub struct WLibSettings {
201 window_static_size: Option<WindowSize>,
203
204 window_title: String,
206
207 app_id: String,
210}
211
212impl WLibSettings {
213 pub fn new() -> Self {
214 Self::default()
215 }
216
217 pub fn with_static_size(mut self, size: WindowSize) -> Self {
218 self.window_static_size = Some(size);
219 self
220 }
221
222 pub fn with_title(mut self, title: &str) -> Self {
223 self.window_title = title.to_string();
224 self
225 }
226
227 pub fn with_app_id(mut self, id: &str) -> Self {
228 self.app_id = id.to_string();
229 self
230 }
231}
232
233pub fn run(state: Box<dyn WindowAble>, settings: WLibSettings) {
235 let conn = Connection::connect_to_env().unwrap();
237
238 let (globals, event_queue) = registry_queue_init(&conn).unwrap();
240 let qh = event_queue.handle();
241 let mut event_loop: EventLoop<WindowManager> =
242 EventLoop::try_new().expect("Failed to initialize the event loop!");
243 let loop_handle = event_loop.handle();
244 WaylandSource::new(conn.clone(), event_queue)
245 .insert(loop_handle)
246 .unwrap();
247
248 let compositor = CompositorState::bind(&globals, &qh).expect("wl_compositor not available");
251 let xdg_shell = XdgShell::bind(&globals, &qh).expect("xdg shell is not available");
253
254 let shm = Shm::bind(&globals, &qh).expect("wl shm is not available.");
257 let xdg_activation = ActivationState::bind(&globals, &qh).ok();
259
260 let surface = compositor.create_surface(&qh);
262
263 let window = xdg_shell.create_window(surface, WindowDecorations::RequestServer, &qh);
265
266 window.set_title(&settings.window_title);
269
270 window.set_app_id(&settings.app_id);
272
273 window.commit();
279
280 if let Some(activation) = xdg_activation.as_ref() {
282 activation.request_token(
283 &qh,
284 RequestData {
285 seat_and_serial: None,
286 surface: Some(window.wl_surface().clone()),
287 app_id: Some(String::from(
288 "io.github.smithay.client-toolkit.SimpleWindow",
289 )),
290 },
291 )
292 }
293
294 let (width, height) = if let Some(ref dimensions) = settings.window_static_size {
295 window.set_min_size(Some((dimensions.width, dimensions.height)));
299 window.set_max_size(Some((dimensions.width, dimensions.height)));
300
301 (dimensions.width, dimensions.height)
302 } else {
303 (200, 200)
304 };
305
306 let pool = SlotPool::new((width * height * 4) as usize, &shm).expect("Failed to create pool");
309
310 let mut window_manager = WindowManager {
311 registry_state: RegistryState::new(&globals),
314 seat_state: SeatState::new(&globals, &qh),
315 output_state: OutputState::new(&globals, &qh),
316 shm,
317 xdg_activation,
318
319 close_accepted: false,
320 first_configure: true,
321 pool,
322 width,
323 height,
324 buffer: None,
325 window,
326 keyboard: None,
327 keyboard_focus: false,
328 pointer: None,
329 last_frame_time: None,
330
331 managed_window: state,
332 context: Context {
333 delta_time: std::time::Duration::from_millis(0),
334 pressed_keys: HashMap::new(),
335 close_requested: false,
336 event_queue: Vec::new(),
337 is_window_focused: true,
338
339 mouse_state: MouseState {
340 position: (0.0, 0.0),
341 mouse_buttons_pressed: HashSet::new(),
342 },
343 window_size: WindowSize {
344 height: 0,
345 width: 0,
346 },
347 },
348 settings,
349 };
350
351 loop {
353 event_loop
354 .dispatch(Duration::ZERO, &mut window_manager)
355 .unwrap();
356
357 if window_manager.close_accepted {
358 break;
359 }
360 }
361}
362
363impl CompositorHandler for WindowManager {
364 fn scale_factor_changed(
365 &mut self,
366 _conn: &Connection,
367 _qh: &QueueHandle<Self>,
368 _surface: &wl_surface::WlSurface,
369 _new_factor: i32,
370 ) {
371 }
373
374 fn transform_changed(
375 &mut self,
376 _conn: &Connection,
377 _qh: &QueueHandle<Self>,
378 _surface: &wl_surface::WlSurface,
379 _new_transform: wl_output::Transform,
380 ) {
381 }
383
384 fn frame(
385 &mut self,
386 conn: &Connection,
387 qh: &QueueHandle<Self>,
388 _surface: &wl_surface::WlSurface,
389 _time: u32,
390 ) {
391 let now = std::time::Instant::now();
392 let delta = self
393 .last_frame_time
394 .map(|last| now - last)
395 .unwrap_or(Duration::ZERO);
396
397 self.last_frame_time = Some(now);
398 self.context.delta_time = delta;
399
400 let request = self.managed_window.update(self.context.clone());
401 self.handle_update(request);
402
403 self.draw(conn, qh);
404
405 self.context.event_queue.clear();
406 }
407
408 fn surface_enter(
409 &mut self,
410 _conn: &Connection,
411 _qh: &QueueHandle<Self>,
412 _surface: &wl_surface::WlSurface,
413 _output: &wl_output::WlOutput,
414 ) {
415 }
417
418 fn surface_leave(
419 &mut self,
420 _conn: &Connection,
421 _qh: &QueueHandle<Self>,
422 _surface: &wl_surface::WlSurface,
423 _output: &wl_output::WlOutput,
424 ) {
425 }
427}
428
429impl OutputHandler for WindowManager {
430 fn output_state(&mut self) -> &mut OutputState {
431 &mut self.output_state
432 }
433
434 fn new_output(
435 &mut self,
436 _conn: &Connection,
437 _qh: &QueueHandle<Self>,
438 _output: wl_output::WlOutput,
439 ) {
440 }
441
442 fn update_output(
443 &mut self,
444 _conn: &Connection,
445 _qh: &QueueHandle<Self>,
446 _output: wl_output::WlOutput,
447 ) {
448 }
449
450 fn output_destroyed(
451 &mut self,
452 _conn: &Connection,
453 _qh: &QueueHandle<Self>,
454 _output: wl_output::WlOutput,
455 ) {
456 }
457}
458
459impl WindowHandler for WindowManager {
460 fn request_close(&mut self, _: &Connection, _: &QueueHandle<Self>, _: &Window) {
461 self.context.close_requested = true;
462 }
463
464 fn configure(
465 &mut self,
466 conn: &Connection,
467 qh: &QueueHandle<Self>,
468 _window: &Window,
469 configure: WindowConfigure,
470 _serial: u32,
471 ) {
472 self.buffer = None;
475
476 if self.settings.window_static_size.is_none() {
477 self.width = configure.new_size.0.map(|v| v.get()).unwrap_or(256);
478 self.height = configure.new_size.1.map(|v| v.get()).unwrap_or(256);
479 }
480
481 self.context.window_size = WindowSize {
482 width: self.width,
483 height: self.height,
484 };
485
486 if self.first_configure {
491 self.first_configure = false;
492 self.draw(conn, qh);
493 }
494 }
495}
496
497impl ActivationHandler for WindowManager {
498 type RequestData = RequestData;
499
500 fn new_token(&mut self, token: String, _data: &Self::RequestData) {
501 self.xdg_activation
502 .as_ref()
503 .unwrap()
504 .activate::<WindowManager>(self.window.wl_surface(), token);
505 }
506}
507
508impl SeatHandler for WindowManager {
509 fn seat_state(&mut self) -> &mut SeatState {
510 &mut self.seat_state
511 }
512
513 fn new_seat(&mut self, _: &Connection, _: &QueueHandle<Self>, _: wl_seat::WlSeat) {}
514
515 fn new_capability(
516 &mut self,
517 _conn: &Connection,
518 qh: &QueueHandle<Self>,
519 seat: wl_seat::WlSeat,
520 capability: Capability,
521 ) {
522 if capability == Capability::Keyboard && self.keyboard.is_none() {
523 let keyboard = self
525 .seat_state
526 .get_keyboard(qh, &seat, None)
527 .expect("Failed to create keyboard");
528
529 self.keyboard = Some(keyboard);
530 }
531
532 if capability == Capability::Pointer && self.pointer.is_none() {
533 let pointer = self
535 .seat_state
536 .get_pointer(qh, &seat)
537 .expect("Failed to create pointer");
538 self.pointer = Some(pointer);
539 }
540 }
541
542 fn remove_capability(
543 &mut self,
544 _conn: &Connection,
545 _: &QueueHandle<Self>,
546 _: wl_seat::WlSeat,
547 capability: Capability,
548 ) {
549 if capability == Capability::Keyboard && self.keyboard.is_some() {
550 self.keyboard.take().unwrap().release();
552 }
553
554 if capability == Capability::Pointer && self.pointer.is_some() {
555 self.pointer.take().unwrap().release();
557 }
558 }
559
560 fn remove_seat(&mut self, _: &Connection, _: &QueueHandle<Self>, _: wl_seat::WlSeat) {}
561}
562
563impl KeyboardHandler for WindowManager {
564 fn enter(
565 &mut self,
566 _: &Connection,
567 _: &QueueHandle<Self>,
568 _: &wl_keyboard::WlKeyboard,
569 surface: &wl_surface::WlSurface,
570 _: u32,
571 raw: &[u32],
572 keysyms: &[Keysym],
573 ) {
574 if self.window.wl_surface() == surface {
575 self.keyboard_focus = true;
577 for (rawk, sym) in raw.iter().zip(keysyms.iter()) {
578 self.context.pressed_keys.insert(*rawk, *sym);
579 }
580 }
581 }
582
583 fn leave(
584 &mut self,
585 _: &Connection,
586 _: &QueueHandle<Self>,
587 _: &wl_keyboard::WlKeyboard,
588 surface: &wl_surface::WlSurface,
589 _: u32,
590 ) {
591 if self.window.wl_surface() == surface {
592 self.keyboard_focus = false;
594 self.context.pressed_keys.clear();
595 }
596 }
597
598 fn press_key(
599 &mut self,
600 _conn: &Connection,
601 _qh: &QueueHandle<Self>,
602 _: &wl_keyboard::WlKeyboard,
603 _: u32,
604 event: KeyEvent,
605 ) {
606 self.context
607 .event_queue
608 .push(Event::KeyPress(event.clone()));
609
610 self.context
611 .pressed_keys
612 .insert(event.raw_code, event.keysym);
613 }
614
615 fn release_key(
616 &mut self,
617 _: &Connection,
618 _: &QueueHandle<Self>,
619 _: &wl_keyboard::WlKeyboard,
620 _: u32,
621 event: KeyEvent,
622 ) {
623 self.context
624 .event_queue
625 .push(Event::KeyRelease(event.clone()));
626
627 self.context.pressed_keys.remove(&event.raw_code);
628 }
629
630 fn repeat_key(
631 &mut self,
632 _: &Connection,
633 _: &QueueHandle<Self>,
634 _: &wl_keyboard::WlKeyboard,
635 _: u32,
636 _event: KeyEvent,
637 ) {
638 }
640
641 fn update_modifiers(
642 &mut self,
643 _: &Connection,
644 _: &QueueHandle<Self>,
645 _: &wl_keyboard::WlKeyboard,
646 _serial: u32,
647 _modifiers: Modifiers,
648 _raw_modifiers: RawModifiers,
649 _layout: u32,
650 ) {
651 }
653}
654
655impl PointerHandler for WindowManager {
656 fn pointer_frame(
657 &mut self,
658 _conn: &Connection,
659 _qh: &QueueHandle<Self>,
660 _pointer: &wl_pointer::WlPointer,
661 events: &[PointerEvent],
662 ) {
663 for event in events {
664 if &event.surface != self.window.wl_surface() {
666 continue;
667 }
668
669 use PointerEventKind as PEK;
670 match event.kind {
671 PEK::Press {
672 time: _,
673 button: b,
674 serial: _,
675 } => {
676 if let Ok(bttn) = MouseButton::try_from(b) {
680 self.context.mouse_state.mouse_buttons_pressed.insert(bttn);
681 }
682 }
683 PEK::Release { button: b, .. } => {
684 if let Ok(bttn) = MouseButton::try_from(b) {
686 self.context.mouse_state.mouse_buttons_pressed.remove(&bttn);
687 }
688 }
689 PEK::Enter { .. } => {
690 self.context.is_window_focused = true;
691 }
692 PEK::Leave { .. } => {
693 self.context.is_window_focused = false;
694 }
695 _ => {}
696 }
697
698 self.context.mouse_state.position = event.position;
699
700 self.context
701 .event_queue
702 .push(Event::PointerEvent(event.clone()));
703 }
704 }
705}
706
707impl ShmHandler for WindowManager {
708 fn shm_state(&mut self) -> &mut Shm {
709 &mut self.shm
710 }
711}
712
713impl WindowManager {
714 pub fn draw(&mut self, _conn: &Connection, qh: &QueueHandle<Self>) {
715 let width = self.width;
716 let height = self.height;
717 let stride = self.width as i32 * 4;
718
719 let buffer = self.buffer.get_or_insert_with(|| {
720 self.pool
721 .create_buffer(
722 width as i32,
723 height as i32,
724 stride,
725 wl_shm::Format::Argb8888,
726 )
727 .expect("create buffer")
728 .0
729 });
730 let canvas = match self.pool.canvas(buffer) {
731 Some(canvas) => canvas,
732 None => {
733 let (second_buffer, canvas) = self
736 .pool
737 .create_buffer(
738 self.width as i32,
739 self.height as i32,
740 stride,
741 wl_shm::Format::Argb8888,
742 )
743 .expect("create buffer");
744 *buffer = second_buffer;
745 canvas
746 }
747 };
748
749 self.managed_window.draw(
751 canvas,
752 WindowSize {
753 width: self.width,
754 height: self.height,
755 },
756 );
757
758 self.window
760 .wl_surface()
761 .damage_buffer(0, 0, self.width as i32, self.height as i32);
762
763 self.window
765 .wl_surface()
766 .frame(qh, self.window.wl_surface().clone());
767
768 buffer
770 .attach_to(self.window.wl_surface())
771 .expect("buffer attach");
772 self.window.commit();
773 }
774
775 fn handle_update(&mut self, request: Option<WLibRequest>) {
776 match request {
777 Some(WLibRequest::CloseAccepted) => self.close_accepted = true,
778 None => {}
779 }
780 }
781}
782
783delegate_compositor!(WindowManager);
784delegate_output!(WindowManager);
785delegate_shm!(WindowManager);
786
787delegate_seat!(WindowManager);
788delegate_keyboard!(WindowManager);
789delegate_pointer!(WindowManager);
790
791delegate_xdg_shell!(WindowManager);
792delegate_xdg_window!(WindowManager);
793delegate_activation!(WindowManager);
794
795delegate_registry!(WindowManager);
796
797impl ProvidesRegistryState for WindowManager {
798 fn registry(&mut self) -> &mut RegistryState {
799 &mut self.registry_state
800 }
801 registry_handlers![OutputState, SeatState,];
802}