1mod renderer;
9mod state;
10
11pub use state::{ActivationResult, AppState};
12
13use crate::config::Config;
14use crate::core::WindowHint;
15use crate::util::{IpcCommand, IpcServer, Result};
16use renderer::Renderer;
17use smithay_client_toolkit::{
18 compositor::{CompositorHandler, CompositorState},
19 delegate_compositor, delegate_keyboard, delegate_layer, delegate_output, delegate_registry,
20 delegate_seat, delegate_shm,
21 output::{OutputHandler, OutputState},
22 registry::{ProvidesRegistryState, RegistryState},
23 registry_handlers,
24 seat::{
25 Capability, SeatHandler, SeatState,
26 keyboard::{KeyEvent, KeyboardHandler, Keysym, Modifiers, RawModifiers},
27 },
28 shell::{
29 WaylandSurface,
30 wlr_layer::{
31 Anchor, KeyboardInteractivity, Layer, LayerShell, LayerShellHandler, LayerSurface,
32 LayerSurfaceConfigure,
33 },
34 },
35 shm::{Shm, ShmHandler},
36};
37use state::{Action, Event, Transition};
38use std::sync::Arc;
39use wayland_client::{
40 Connection, QueueHandle,
41 globals::registry_queue_init,
42 protocol::{wl_keyboard, wl_output, wl_seat, wl_surface},
43};
44
45pub struct App {
47 registry_state: RegistryState,
49 seat_state: SeatState,
50 output_state: OutputState,
51 compositor_state: CompositorState,
52 layer_shell: LayerShell,
53 shm: Shm,
54
55 state: AppState,
57 config: Arc<Config>,
58 hints: Vec<WindowHint>,
59 previous_window_id: Option<String>,
60
61 renderer: Renderer,
63 layer_surface: Option<LayerSurface>,
64
65 running: bool,
67
68 alt_held: bool,
70 shift_held: bool,
71
72 ipc_server: Option<IpcServer>,
74}
75
76impl App {
77 pub fn run(
79 config: Config,
80 hints: Vec<WindowHint>,
81 previous_window_id: Option<String>,
82 launcher_mode: bool,
83 ipc_server: Option<IpcServer>,
84 ) -> Result<Option<(usize, String)>> {
85 let conn = Connection::connect_to_env()
86 .map_err(|e| crate::util::Error::WaylandConnection(Box::new(e)))?;
87
88 let (globals, mut event_queue) = registry_queue_init(&conn)
89 .map_err(|e| crate::util::Error::WaylandConnection(Box::new(e)))?;
90 let qh = event_queue.handle();
91
92 let registry_state = RegistryState::new(&globals);
93 let seat_state = SeatState::new(&globals, &qh);
94 let output_state = OutputState::new(&globals, &qh);
95 let compositor_state = CompositorState::bind(&globals, &qh)
96 .map_err(|e| crate::util::Error::WaylandConnection(Box::new(e)))?;
97 let shm = Shm::bind(&globals, &qh)
98 .map_err(|e| crate::util::Error::WaylandConnection(Box::new(e)))?;
99 let layer_shell = LayerShell::bind(&globals, &qh)
100 .map_err(|e| crate::util::Error::WaylandConnection(Box::new(e)))?;
101
102 let config = Arc::new(config);
103
104 tracing::info!(
105 "App::run starting with {} hints, mode={}",
106 hints.len(),
107 if launcher_mode {
108 "launcher"
109 } else {
110 "switcher"
111 }
112 );
113 tracing::info!(" Previous window: {:?}", previous_window_id);
114 for (i, hint) in hints.iter().enumerate() {
115 tracing::info!(
116 " Hint[{}]: {} -> {} ({})",
117 i,
118 hint.hint,
119 hint.app_id,
120 hint.window_id
121 );
122 }
123
124 let initial_state = AppState::initial(launcher_mode, &hints, previous_window_id.as_deref());
125
126 let mut app = App {
127 registry_state,
128 seat_state,
129 output_state,
130 compositor_state,
131 layer_shell,
132 shm,
133 state: initial_state,
134 config,
135 hints,
136 previous_window_id,
137 renderer: Renderer::new(),
138 layer_surface: None,
139 running: true,
140 alt_held: !launcher_mode, shift_held: false,
142 ipc_server,
143 };
144
145 app.create_layer_surface(&qh);
147 tracing::info!("Layer surface created");
148
149 let mut event_loop = calloop::EventLoop::try_new()
151 .map_err(|e| crate::util::Error::WaylandConnection(Box::new(e)))?;
152 let loop_handle = event_loop.handle();
153 tracing::info!("Event loop created");
154
155 loop_handle
157 .insert_source(
158 calloop::generic::Generic::new(conn, calloop::Interest::READ, calloop::Mode::Level),
159 move |_, conn, app: &mut App| {
160 if let Some(guard) = conn.prepare_read() {
161 match guard.read() {
162 Ok(_) => {}
163 Err(wayland_client::backend::WaylandError::Io(io_err))
164 if io_err.kind() == std::io::ErrorKind::WouldBlock =>
165 {
166 }
168 Err(e) => {
169 tracing::error!("Wayland read error: {}. Shutting down.", e);
170 app.running = false;
171 return Ok(calloop::PostAction::Remove);
172 }
173 }
174 }
175 Ok(calloop::PostAction::Continue)
176 },
177 )
178 .map_err(|e| crate::util::Error::WaylandConnection(Box::new(e)))?;
179
180 tracing::info!("Entering event loop, running={}", app.running);
181 let mut loop_count = 0u64;
182 while app.running {
183 loop_count += 1;
184 if loop_count <= 5 || loop_count.is_multiple_of(100) {
185 tracing::debug!("Event loop iteration {}", loop_count);
186 }
187
188 event_queue
190 .dispatch_pending(&mut app)
191 .map_err(|e| crate::util::Error::WaylandConnection(Box::new(e)))?;
192
193 event_queue
195 .flush()
196 .map_err(|e| crate::util::Error::WaylandConnection(Box::new(e)))?;
197
198 app.process_event(Event::Tick, &qh);
200
201 let ipc_commands: Vec<IpcCommand> = app
204 .ipc_server
205 .as_ref()
206 .map(|s| {
207 let mut cmds = Vec::new();
208 while let Some(cmd) = s.try_recv() {
209 cmds.push(cmd);
210 }
211 cmds
212 })
213 .unwrap_or_default();
214
215 for cmd in ipc_commands {
216 match cmd {
217 IpcCommand::CycleForward => {
218 tracing::info!("IPC: FORWARD command received");
219 app.process_event(Event::CycleForward, &qh);
220 }
221 IpcCommand::CycleBackward => {
222 tracing::info!("IPC: BACKWARD command received");
223 app.process_event(Event::CycleBackward, &qh);
224 }
225 IpcCommand::Ping => {
226 }
228 }
229 }
230
231 if app.renderer.needs_redraw() {
233 app.draw(&qh);
234 }
235
236 event_loop
238 .dispatch(std::time::Duration::from_millis(10), &mut app)
239 .ok();
240 }
241 tracing::info!("Exited event loop after {} iterations", loop_count);
242
243 tracing::info!("BORDER DEACTIVATING");
245
246 match app.state.activation_result() {
248 Some(ActivationResult::Window(idx)) if *idx < app.hints.len() => {
249 let hint = &app.hints[*idx];
250 tracing::info!("Activating window: {} ({})", hint.app_id, hint.window_id);
251 Ok(Some((*idx, hint.window_id.to_string())))
252 }
253 Some(ActivationResult::Window(idx)) => {
254 tracing::warn!("Window index {} out of bounds, falling back", idx);
256 if !app.hints.is_empty() {
257 let hint = &app.hints[0];
258 Ok(Some((0, hint.window_id.to_string())))
259 } else {
260 Ok(None)
261 }
262 }
263 Some(ActivationResult::QuickSwitch) => {
264 if let Some(ref prev_id) = app.previous_window_id
266 && let Some((idx, hint)) = app
267 .hints
268 .iter()
269 .enumerate()
270 .find(|(_, h)| h.window_id.as_str() == prev_id)
271 {
272 tracing::info!("Quick switch to: {} ({})", hint.app_id, hint.window_id);
273 return Ok(Some((idx, hint.window_id.to_string())));
274 }
275 if !app.hints.is_empty() {
277 let hint = &app.hints[0];
278 tracing::info!(
279 "Quick switch fallback: {} ({})",
280 hint.app_id,
281 hint.window_id
282 );
283 Ok(Some((0, hint.window_id.to_string())))
284 } else {
285 Ok(None)
286 }
287 }
288 Some(ActivationResult::Launch(key)) => {
289 tracing::info!("Launching: {}", key);
290 Ok(Some((usize::MAX, key.clone())))
291 }
292 Some(ActivationResult::Cancelled) => {
293 tracing::info!("Cancelled");
294 Ok(None)
295 }
296 None => {
297 tracing::info!("No result");
298 Ok(None)
299 }
300 }
301 }
302
303 fn process_event(&mut self, event: Event, qh: &QueueHandle<Self>) {
305 let Transition { new_state, actions } = self.state.handle_event(
306 event,
307 &self.config,
308 &self.hints,
309 self.previous_window_id.as_deref(),
310 );
311
312 self.state = new_state;
313
314 let had_actions = !actions.is_empty();
315 for action in actions {
316 match action {
317 Action::ScheduleRedraw => {
318 self.renderer.schedule_redraw();
319 }
320 Action::Exit => {
321 self.running = false;
322 }
323 }
324 }
325
326 if had_actions {
328 self.draw(qh);
329 }
330 }
331
332 fn create_layer_surface(&mut self, qh: &QueueHandle<Self>) {
334 let surface = self.compositor_state.create_surface(qh);
335
336 let layer_surface = self.layer_shell.create_layer_surface(
337 qh,
338 surface,
339 Layer::Overlay,
340 Some("sesame"),
341 None,
342 );
343
344 layer_surface.set_anchor(Anchor::all());
345 layer_surface.set_exclusive_zone(-1);
346 layer_surface.set_keyboard_interactivity(KeyboardInteractivity::Exclusive);
347 layer_surface.commit();
348
349 self.layer_surface = Some(layer_surface);
350 }
351
352 fn draw(&mut self, _qh: &QueueHandle<Self>) {
354 let Some(layer_surface) = &self.layer_surface else {
355 return;
356 };
357
358 let show_full = self.state.is_full_overlay();
359 let selected = self.state.selected_hint_index();
360 let input = self.state.input();
361
362 if let Some(result) = self.renderer.render(
363 &self.shm,
364 &self.config,
365 &self.hints,
366 input,
367 selected,
368 show_full,
369 ) {
370 layer_surface
371 .wl_surface()
372 .attach(Some(result.buffer.wl_buffer()), 0, 0);
373 layer_surface
374 .wl_surface()
375 .damage_buffer(0, 0, result.width, result.height);
376 layer_surface.commit();
377
378 tracing::debug!(
379 "Frame rendered: {}x{}, full={}, selected={}",
380 result.width,
381 result.height,
382 show_full,
383 selected
384 );
385 }
386 }
387}
388
389impl CompositorHandler for App {
392 fn scale_factor_changed(
393 &mut self,
394 _conn: &Connection,
395 _qh: &QueueHandle<Self>,
396 _surface: &wl_surface::WlSurface,
397 new_factor: i32,
398 ) {
399 self.renderer.set_scale(new_factor as f32);
400 }
401
402 fn transform_changed(
403 &mut self,
404 _conn: &Connection,
405 _qh: &QueueHandle<Self>,
406 _surface: &wl_surface::WlSurface,
407 _new_transform: wl_output::Transform,
408 ) {
409 }
410
411 fn frame(
412 &mut self,
413 _conn: &Connection,
414 qh: &QueueHandle<Self>,
415 _surface: &wl_surface::WlSurface,
416 _time: u32,
417 ) {
418 self.process_event(Event::FrameCallback, qh);
420 }
421
422 fn surface_enter(
423 &mut self,
424 _conn: &Connection,
425 _qh: &QueueHandle<Self>,
426 _surface: &wl_surface::WlSurface,
427 _output: &wl_output::WlOutput,
428 ) {
429 }
430
431 fn surface_leave(
432 &mut self,
433 _conn: &Connection,
434 _qh: &QueueHandle<Self>,
435 _surface: &wl_surface::WlSurface,
436 _output: &wl_output::WlOutput,
437 ) {
438 }
439}
440
441impl OutputHandler for App {
442 fn output_state(&mut self) -> &mut OutputState {
443 &mut self.output_state
444 }
445
446 fn new_output(
447 &mut self,
448 _conn: &Connection,
449 _qh: &QueueHandle<Self>,
450 _output: wl_output::WlOutput,
451 ) {
452 }
453 fn update_output(
454 &mut self,
455 _conn: &Connection,
456 _qh: &QueueHandle<Self>,
457 _output: wl_output::WlOutput,
458 ) {
459 }
460 fn output_destroyed(
461 &mut self,
462 _conn: &Connection,
463 _qh: &QueueHandle<Self>,
464 _output: wl_output::WlOutput,
465 ) {
466 }
467}
468
469impl LayerShellHandler for App {
470 fn closed(&mut self, _conn: &Connection, _qh: &QueueHandle<Self>, _layer: &LayerSurface) {
471 self.running = false;
472 }
473
474 fn configure(
475 &mut self,
476 _conn: &Connection,
477 qh: &QueueHandle<Self>,
478 layer: &LayerSurface,
479 configure: LayerSurfaceConfigure,
480 _serial: u32,
481 ) {
482 tracing::info!(
483 "CONFIGURE EVENT: {}x{}",
484 configure.new_size.0,
485 configure.new_size.1
486 );
487
488 self.renderer
489 .configure(configure.new_size.0, configure.new_size.1);
490 layer.set_size(configure.new_size.0, configure.new_size.1);
491
492 self.process_event(
494 Event::Configure {
495 width: configure.new_size.0,
496 height: configure.new_size.1,
497 },
498 qh,
499 );
500
501 self.draw(qh);
503 tracing::info!("CONFIGURE done, draw called");
504 }
505}
506
507impl SeatHandler for App {
508 fn seat_state(&mut self) -> &mut SeatState {
509 &mut self.seat_state
510 }
511
512 fn new_seat(&mut self, _conn: &Connection, _qh: &QueueHandle<Self>, _seat: wl_seat::WlSeat) {}
513
514 fn new_capability(
515 &mut self,
516 _conn: &Connection,
517 qh: &QueueHandle<Self>,
518 seat: wl_seat::WlSeat,
519 capability: Capability,
520 ) {
521 if capability == Capability::Keyboard
522 && let Err(e) = self.seat_state.get_keyboard(qh, &seat, None)
523 {
524 tracing::error!("Failed to get keyboard: {}", e);
525 }
526 }
527
528 fn remove_capability(
529 &mut self,
530 _conn: &Connection,
531 _qh: &QueueHandle<Self>,
532 _seat: wl_seat::WlSeat,
533 _capability: Capability,
534 ) {
535 }
536
537 fn remove_seat(&mut self, _conn: &Connection, _qh: &QueueHandle<Self>, _seat: wl_seat::WlSeat) {
538 }
539}
540
541impl KeyboardHandler for App {
542 fn enter(
543 &mut self,
544 _conn: &Connection,
545 _qh: &QueueHandle<Self>,
546 _keyboard: &wl_keyboard::WlKeyboard,
547 _surface: &wl_surface::WlSurface,
548 _serial: u32,
549 raw: &[u32],
550 keysyms: &[Keysym],
551 ) {
552 tracing::info!(
553 "KEYBOARD ENTER: {} raw keys, {} keysyms",
554 raw.len(),
555 keysyms.len()
556 );
557 }
558
559 fn leave(
560 &mut self,
561 _conn: &Connection,
562 _qh: &QueueHandle<Self>,
563 _keyboard: &wl_keyboard::WlKeyboard,
564 _surface: &wl_surface::WlSurface,
565 _serial: u32,
566 ) {
567 tracing::info!("KEYBOARD LEAVE");
568 }
569
570 fn press_key(
571 &mut self,
572 _conn: &Connection,
573 qh: &QueueHandle<Self>,
574 _keyboard: &wl_keyboard::WlKeyboard,
575 _serial: u32,
576 event: KeyEvent,
577 ) {
578 tracing::info!(
579 "KEY PRESS: keysym={:?} raw={:#x} shift={}",
580 event.keysym,
581 event.keysym.raw(),
582 self.shift_held
583 );
584
585 self.process_event(
586 Event::KeyPress {
587 keysym: event.keysym,
588 shift: self.shift_held,
589 },
590 qh,
591 );
592 }
593
594 fn release_key(
595 &mut self,
596 _conn: &Connection,
597 _qh: &QueueHandle<Self>,
598 _keyboard: &wl_keyboard::WlKeyboard,
599 _serial: u32,
600 _event: KeyEvent,
601 ) {
602 }
603
604 fn repeat_key(
605 &mut self,
606 _conn: &Connection,
607 qh: &QueueHandle<Self>,
608 _keyboard: &wl_keyboard::WlKeyboard,
609 _serial: u32,
610 event: KeyEvent,
611 ) {
612 self.process_event(
613 Event::KeyPress {
614 keysym: event.keysym,
615 shift: self.shift_held,
616 },
617 qh,
618 );
619 }
620
621 fn update_modifiers(
622 &mut self,
623 _conn: &Connection,
624 qh: &QueueHandle<Self>,
625 _keyboard: &wl_keyboard::WlKeyboard,
626 _serial: u32,
627 modifiers: Modifiers,
628 _raw_modifiers: RawModifiers,
629 _layout: u32,
630 ) {
631 let was_alt_held = self.alt_held;
632 self.alt_held = modifiers.alt;
633 self.shift_held = modifiers.shift;
634
635 tracing::debug!(
636 "Modifiers: alt={} (was {}), shift={}",
637 self.alt_held,
638 was_alt_held,
639 self.shift_held
640 );
641
642 if was_alt_held && !self.alt_held {
644 self.process_event(Event::AltReleased, qh);
645 }
646 }
647}
648
649impl ShmHandler for App {
650 fn shm_state(&mut self) -> &mut Shm {
651 &mut self.shm
652 }
653}
654
655impl ProvidesRegistryState for App {
656 fn registry(&mut self) -> &mut RegistryState {
657 &mut self.registry_state
658 }
659
660 registry_handlers!(OutputState, SeatState);
661}
662
663delegate_compositor!(App);
664delegate_output!(App);
665delegate_shm!(App);
666delegate_seat!(App);
667delegate_keyboard!(App);
668delegate_layer!(App);
669delegate_registry!(App);