max / shop
- Co-Authored-By
- Claude Opus 4.7 (1M context) <noreply@anthropic.com>
1 file changed,
+137 insertions,
-5 deletions
| @@ -21,8 +21,13 @@ | |||
| 21 | 21 | WaylandChrome, WaylandSurface, WindowConfigure, WindowDecorations, WindowHandler, WindowSpec, | |
| 22 | 22 | XdgShell, XdgWindow, registry_queue_init, | |
| 23 | 23 | }; | |
| 24 | - | use smithay_client_toolkit::{delegate_dispatch2, delegate_registry, registry_handlers}; | |
| 24 | + | use smithay_client_toolkit::{ | |
| 25 | + | delegate_dispatch2, delegate_registry, | |
| 26 | + | registry_handlers, | |
| 27 | + | seat::keyboard::{KeyEvent, KeyboardHandler, Keysym, Modifiers, RawModifiers, RepeatInfo}, | |
| 28 | + | }; | |
| 25 | 29 | use std::collections::VecDeque; | |
| 30 | + | use wayland_client::protocol::wl_keyboard; | |
| 26 | 31 | use tracing::{info, warn}; | |
| 27 | 32 | use wayland_client::protocol::{wl_output, wl_seat, wl_surface}; | |
| 28 | 33 | ||
| @@ -154,6 +159,8 @@ | |||
| 154 | 159 | parser, | |
| 155 | 160 | font_data: std::fs::read(FONT_PATH)?, | |
| 156 | 161 | scale: 1, | |
| 162 | + | keyboard: None, | |
| 163 | + | modifiers: Modifiers::default(), | |
| 157 | 164 | pending_redraw: true, | |
| 158 | 165 | }; | |
| 159 | 166 | app.surface.configure(&app.device, &app.surface_config); | |
| @@ -270,6 +277,8 @@ | |||
| 270 | 277 | font_data: Vec<u8>, | |
| 271 | 278 | /// HiDPI integer scale from `wl_surface.enter` outputs. | |
| 272 | 279 | scale: u32, | |
| 280 | + | keyboard: Option<wl_keyboard::WlKeyboard>, | |
| 281 | + | modifiers: Modifiers, | |
| 273 | 282 | pending_redraw: bool, | |
| 274 | 283 | } | |
| 275 | 284 | ||
| @@ -417,22 +426,145 @@ | |||
| 417 | 426 | fn new_capability( | |
| 418 | 427 | &mut self, | |
| 419 | 428 | _: &Connection, | |
| 420 | - | _: &QueueHandle<Self>, | |
| 421 | - | _: wl_seat::WlSeat, | |
| 422 | - | _: Capability, | |
| 429 | + | qh: &QueueHandle<Self>, | |
| 430 | + | seat: wl_seat::WlSeat, | |
| 431 | + | capability: Capability, | |
| 423 | 432 | ) { | |
| 433 | + | if capability == Capability::Keyboard && self.keyboard.is_none() { | |
| 434 | + | match self.chrome.seat.get_keyboard(qh, &seat, None) { | |
| 435 | + | Ok(kb) => self.keyboard = Some(kb), | |
| 436 | + | Err(e) => warn!("get_keyboard: {e}"), | |
| 437 | + | } | |
| 438 | + | } | |
| 424 | 439 | } | |
| 425 | 440 | fn remove_capability( | |
| 426 | 441 | &mut self, | |
| 427 | 442 | _: &Connection, | |
| 428 | 443 | _: &QueueHandle<Self>, | |
| 429 | 444 | _: wl_seat::WlSeat, | |
| 430 | - | _: Capability, | |
| 445 | + | capability: Capability, | |
| 431 | 446 | ) { | |
| 447 | + | if capability == Capability::Keyboard | |
| 448 | + | && let Some(kb) = self.keyboard.take() | |
| 449 | + | { | |
| 450 | + | kb.release(); | |
| 451 | + | } | |
| 432 | 452 | } | |
| 433 | 453 | fn remove_seat(&mut self, _: &Connection, _: &QueueHandle<Self>, _: wl_seat::WlSeat) {} | |
| 434 | 454 | } | |
| 435 | 455 | ||
| 456 | + | impl KeyboardHandler for App { | |
| 457 | + | fn enter( | |
| 458 | + | &mut self, | |
| 459 | + | _: &Connection, | |
| 460 | + | _: &QueueHandle<Self>, | |
| 461 | + | _: &wl_keyboard::WlKeyboard, | |
| 462 | + | _: &wl_surface::WlSurface, | |
| 463 | + | _: u32, | |
| 464 | + | _: &[u32], | |
| 465 | + | _: &[Keysym], | |
| 466 | + | ) { | |
| 467 | + | } | |
| 468 | + | fn leave( | |
| 469 | + | &mut self, | |
| 470 | + | _: &Connection, | |
| 471 | + | _: &QueueHandle<Self>, | |
| 472 | + | _: &wl_keyboard::WlKeyboard, | |
| 473 | + | _: &wl_surface::WlSurface, | |
| 474 | + | _: u32, | |
| 475 | + | ) { | |
| 476 | + | } | |
| 477 | + | fn press_key( | |
| 478 | + | &mut self, | |
| 479 | + | _: &Connection, | |
| 480 | + | _: &QueueHandle<Self>, | |
| 481 | + | _: &wl_keyboard::WlKeyboard, | |
| 482 | + | _: u32, | |
| 483 | + | event: KeyEvent, | |
| 484 | + | ) { | |
| 485 | + | self.handle_key(&event); | |
| 486 | + | } | |
| 487 | + | fn repeat_key( | |
| 488 | + | &mut self, | |
| 489 | + | _: &Connection, | |
| 490 | + | _: &QueueHandle<Self>, | |
| 491 | + | _: &wl_keyboard::WlKeyboard, | |
| 492 | + | _: u32, | |
| 493 | + | event: KeyEvent, | |
| 494 | + | ) { | |
| 495 | + | self.handle_key(&event); | |
| 496 | + | } | |
| 497 | + | fn release_key( | |
| 498 | + | &mut self, | |
| 499 | + | _: &Connection, | |
| 500 | + | _: &QueueHandle<Self>, | |
| 501 | + | _: &wl_keyboard::WlKeyboard, | |
| 502 | + | _: u32, | |
| 503 | + | _: KeyEvent, | |
| 504 | + | ) { | |
| 505 | + | } | |
| 506 | + | fn update_modifiers( | |
| 507 | + | &mut self, | |
| 508 | + | _: &Connection, | |
| 509 | + | _: &QueueHandle<Self>, | |
| 510 | + | _: &wl_keyboard::WlKeyboard, | |
| 511 | + | _: u32, | |
| 512 | + | modifiers: Modifiers, | |
| 513 | + | _: RawModifiers, | |
| 514 | + | _: u32, | |
| 515 | + | ) { | |
| 516 | + | self.modifiers = modifiers; | |
| 517 | + | } | |
| 518 | + | fn update_repeat_info( | |
| 519 | + | &mut self, | |
| 520 | + | _: &Connection, | |
| 521 | + | _: &QueueHandle<Self>, | |
| 522 | + | _: &wl_keyboard::WlKeyboard, | |
| 523 | + | _: RepeatInfo, | |
| 524 | + | ) { | |
| 525 | + | } | |
| 526 | + | } | |
| 527 | + | ||
| 528 | + | impl App { | |
| 529 | + | fn handle_key(&mut self, event: &KeyEvent) { | |
| 530 | + | let bytes = key_to_bytes(event); | |
| 531 | + | if !bytes.is_empty() | |
| 532 | + | && let Err(e) = self.pty.write(&bytes) | |
| 533 | + | { | |
| 534 | + | warn!("pty write: {e}"); | |
| 535 | + | } | |
| 536 | + | } | |
| 537 | + | } | |
| 538 | + | ||
| 539 | + | /// Translate a Wayland key event into the byte sequence the shell expects. | |
| 540 | + | /// SCTK/xkbcommon already runs the keymap so `event.utf8` carries the printable | |
| 541 | + | /// bytes (including Ctrl-modified chars like Ctrl+C -> "\x03"). Special keys | |
| 542 | + | /// with no printable form get an xterm-style escape sequence. | |
| 543 | + | fn key_to_bytes(event: &KeyEvent) -> Vec<u8> { | |
| 544 | + | match event.keysym { | |
| 545 | + | Keysym::Return | Keysym::KP_Enter => return b"\r".to_vec(), | |
| 546 | + | Keysym::BackSpace => return b"\x7f".to_vec(), | |
| 547 | + | Keysym::Tab => return b"\t".to_vec(), | |
| 548 | + | Keysym::Escape => return b"\x1b".to_vec(), | |
| 549 | + | Keysym::Up => return b"\x1b[A".to_vec(), | |
| 550 | + | Keysym::Down => return b"\x1b[B".to_vec(), | |
| 551 | + | Keysym::Right => return b"\x1b[C".to_vec(), | |
| 552 | + | Keysym::Left => return b"\x1b[D".to_vec(), | |
| 553 | + | Keysym::Home => return b"\x1b[H".to_vec(), | |
| 554 | + | Keysym::End => return b"\x1b[F".to_vec(), | |
| 555 | + | Keysym::Page_Up => return b"\x1b[5~".to_vec(), | |
| 556 | + | Keysym::Page_Down => return b"\x1b[6~".to_vec(), | |
| 557 | + | Keysym::Insert => return b"\x1b[2~".to_vec(), | |
| 558 | + | Keysym::Delete => return b"\x1b[3~".to_vec(), | |
| 559 | + | _ => {} | |
| 560 | + | } | |
| 561 | + | event | |
| 562 | + | .utf8 | |
| 563 | + | .as_ref() | |
| 564 | + | .map(|s| s.as_bytes().to_vec()) | |
| 565 | + | .unwrap_or_default() | |
| 566 | + | } | |
| 567 | + | ||
| 436 | 568 | impl WindowHandler for App { | |
| 437 | 569 | fn request_close(&mut self, _: &Connection, _: &QueueHandle<Self>, _: &XdgWindow) { | |
| 438 | 570 | self.chrome.exit = true; |