client.rs

   1use std::cell::{RefCell, RefMut};
   2use std::rc::{Rc, Weak};
   3use std::time::{Duration, Instant};
   4
   5use async_task::Runnable;
   6use calloop::timer::{TimeoutAction, Timer};
   7use calloop::{EventLoop, LoopHandle};
   8use calloop_wayland_source::WaylandSource;
   9use collections::HashMap;
  10use copypasta::wayland_clipboard::{create_clipboards_from_external, Clipboard, Primary};
  11use copypasta::ClipboardProvider;
  12use util::ResultExt;
  13use wayland_backend::client::ObjectId;
  14use wayland_backend::protocol::WEnum;
  15use wayland_client::globals::{registry_queue_init, GlobalList, GlobalListContents};
  16use wayland_client::protocol::wl_callback::{self, WlCallback};
  17use wayland_client::protocol::wl_output;
  18use wayland_client::protocol::wl_pointer::{AxisRelativeDirection, AxisSource};
  19use wayland_client::{
  20    delegate_noop,
  21    protocol::{
  22        wl_buffer, wl_compositor, wl_keyboard, wl_pointer, wl_registry, wl_seat, wl_shm,
  23        wl_shm_pool, wl_surface,
  24    },
  25    Connection, Dispatch, Proxy, QueueHandle,
  26};
  27use wayland_protocols::wp::fractional_scale::v1::client::{
  28    wp_fractional_scale_manager_v1, wp_fractional_scale_v1,
  29};
  30use wayland_protocols::wp::viewporter::client::{wp_viewport, wp_viewporter};
  31use wayland_protocols::xdg::decoration::zv1::client::{
  32    zxdg_decoration_manager_v1, zxdg_toplevel_decoration_v1,
  33};
  34use wayland_protocols::xdg::shell::client::{xdg_surface, xdg_toplevel, xdg_wm_base};
  35use xkbcommon::xkb::ffi::XKB_KEYMAP_FORMAT_TEXT_V1;
  36use xkbcommon::xkb::{self, Keycode, KEYMAP_COMPILE_NO_FLAGS};
  37
  38use super::super::DOUBLE_CLICK_INTERVAL;
  39use super::window::{WaylandWindowState, WaylandWindowStatePtr};
  40use crate::platform::linux::is_within_click_distance;
  41use crate::platform::linux::wayland::cursor::Cursor;
  42use crate::platform::linux::wayland::window::WaylandWindow;
  43use crate::platform::linux::LinuxClient;
  44use crate::platform::PlatformWindow;
  45use crate::{point, px, ForegroundExecutor, MouseExitEvent};
  46use crate::{
  47    AnyWindowHandle, CursorStyle, DisplayId, KeyDownEvent, KeyUpEvent, Keystroke, Modifiers,
  48    ModifiersChangedEvent, MouseButton, MouseDownEvent, MouseMoveEvent, MouseUpEvent,
  49    NavigationDirection, Pixels, PlatformDisplay, PlatformInput, Point, ScrollDelta,
  50    ScrollWheelEvent, TouchPhase,
  51};
  52use crate::{LinuxCommon, WindowParams};
  53
  54/// Used to convert evdev scancode to xkb scancode
  55const MIN_KEYCODE: u32 = 8;
  56
  57#[derive(Clone)]
  58pub struct Globals {
  59    pub qh: QueueHandle<WaylandClientStatePtr>,
  60    pub compositor: wl_compositor::WlCompositor,
  61    pub wm_base: xdg_wm_base::XdgWmBase,
  62    pub shm: wl_shm::WlShm,
  63    pub viewporter: Option<wp_viewporter::WpViewporter>,
  64    pub fractional_scale_manager:
  65        Option<wp_fractional_scale_manager_v1::WpFractionalScaleManagerV1>,
  66    pub decoration_manager: Option<zxdg_decoration_manager_v1::ZxdgDecorationManagerV1>,
  67    pub executor: ForegroundExecutor,
  68}
  69
  70impl Globals {
  71    fn new(
  72        globals: GlobalList,
  73        executor: ForegroundExecutor,
  74        qh: QueueHandle<WaylandClientStatePtr>,
  75    ) -> Self {
  76        Globals {
  77            compositor: globals
  78                .bind(
  79                    &qh,
  80                    wl_surface::REQ_SET_BUFFER_SCALE_SINCE
  81                        ..=wl_surface::EVT_PREFERRED_BUFFER_SCALE_SINCE,
  82                    (),
  83                )
  84                .unwrap(),
  85            shm: globals.bind(&qh, 1..=1, ()).unwrap(),
  86            wm_base: globals.bind(&qh, 1..=1, ()).unwrap(),
  87            viewporter: globals.bind(&qh, 1..=1, ()).ok(),
  88            fractional_scale_manager: globals.bind(&qh, 1..=1, ()).ok(),
  89            decoration_manager: globals.bind(&qh, 1..=1, ()).ok(),
  90            executor,
  91            qh,
  92        }
  93    }
  94}
  95
  96pub(crate) struct WaylandClientState {
  97    globals: Globals,
  98    wl_pointer: Option<wl_pointer::WlPointer>,
  99    // Surface to Window mapping
 100    windows: HashMap<ObjectId, WaylandWindowStatePtr>,
 101    // Output to scale mapping
 102    output_scales: HashMap<ObjectId, i32>,
 103    keymap_state: Option<xkb::State>,
 104    click: ClickState,
 105    repeat: KeyRepeat,
 106    modifiers: Modifiers,
 107    axis_source: AxisSource,
 108    mouse_location: Option<Point<Pixels>>,
 109    continuous_scroll_delta: Option<Point<Pixels>>,
 110    discrete_scroll_delta: Option<Point<f32>>,
 111    vertical_modifier: f32,
 112    horizontal_modifier: f32,
 113    scroll_event_received: bool,
 114    enter_token: Option<()>,
 115    button_pressed: Option<MouseButton>,
 116    mouse_focused_window: Option<WaylandWindowStatePtr>,
 117    keyboard_focused_window: Option<WaylandWindowStatePtr>,
 118    loop_handle: LoopHandle<'static, WaylandClientStatePtr>,
 119    cursor_icon_name: String,
 120    cursor: Cursor,
 121    clipboard: Option<Clipboard>,
 122    primary: Option<Primary>,
 123    event_loop: Option<EventLoop<'static, WaylandClientStatePtr>>,
 124    common: LinuxCommon,
 125}
 126
 127pub struct ClickState {
 128    last_click: Instant,
 129    last_location: Point<Pixels>,
 130    current_count: usize,
 131}
 132
 133pub(crate) struct KeyRepeat {
 134    characters_per_second: u32,
 135    delay: Duration,
 136    current_id: u64,
 137    current_keysym: Option<xkb::Keysym>,
 138}
 139
 140/// This struct is required to conform to Rust's orphan rules, so we can dispatch on the state but hand the
 141/// window to GPUI.
 142#[derive(Clone)]
 143pub struct WaylandClientStatePtr(Weak<RefCell<WaylandClientState>>);
 144
 145impl WaylandClientStatePtr {
 146    fn get_client(&self) -> Rc<RefCell<WaylandClientState>> {
 147        self.0
 148            .upgrade()
 149            .expect("The pointer should always be valid when dispatching in wayland")
 150    }
 151
 152    pub fn drop_window(&self, surface_id: &ObjectId) {
 153        let mut client = self.get_client();
 154        let mut state = client.borrow_mut();
 155        let closed_window = state.windows.remove(surface_id).unwrap();
 156        if let Some(window) = state.mouse_focused_window.take() {
 157            if !window.ptr_eq(&closed_window) {
 158                state.mouse_focused_window = Some(window);
 159            }
 160        }
 161        if let Some(window) = state.keyboard_focused_window.take() {
 162            if !window.ptr_eq(&closed_window) {
 163                state.mouse_focused_window = Some(window);
 164            }
 165        }
 166        if state.windows.is_empty() {
 167            // Drop the clipboard to prevent a seg fault after we've closed all Wayland connections.
 168            state.clipboard = None;
 169            state.primary = None;
 170            state.common.signal.stop();
 171        }
 172    }
 173}
 174
 175#[derive(Clone)]
 176pub struct WaylandClient(Rc<RefCell<WaylandClientState>>);
 177
 178const WL_OUTPUT_VERSION: u32 = 2;
 179
 180fn wl_seat_version(version: u32) -> u32 {
 181    // We rely on the wl_pointer.frame event
 182    const WL_SEAT_MIN_VERSION: u32 = 5;
 183    const WL_SEAT_MAX_VERSION: u32 = 9;
 184
 185    if version < WL_SEAT_MIN_VERSION {
 186        panic!(
 187            "wl_seat below required version: {} < {}",
 188            version, WL_SEAT_MIN_VERSION
 189        );
 190    }
 191
 192    version.clamp(WL_SEAT_MIN_VERSION, WL_SEAT_MAX_VERSION)
 193}
 194
 195impl WaylandClient {
 196    pub(crate) fn new() -> Self {
 197        let conn = Connection::connect_to_env().unwrap();
 198
 199        let (globals, mut event_queue) =
 200            registry_queue_init::<WaylandClientStatePtr>(&conn).unwrap();
 201        let qh = event_queue.handle();
 202        let mut outputs = HashMap::default();
 203
 204        globals.contents().with_list(|list| {
 205            for global in list {
 206                match &global.interface[..] {
 207                    "wl_seat" => {
 208                        globals.registry().bind::<wl_seat::WlSeat, _, _>(
 209                            global.name,
 210                            wl_seat_version(global.version),
 211                            &qh,
 212                            (),
 213                        );
 214                    }
 215                    "wl_output" => {
 216                        let output = globals.registry().bind::<wl_output::WlOutput, _, _>(
 217                            global.name,
 218                            WL_OUTPUT_VERSION,
 219                            &qh,
 220                            (),
 221                        );
 222                        outputs.insert(output.id(), 1);
 223                    }
 224                    _ => {}
 225                }
 226            }
 227        });
 228
 229        let display = conn.backend().display_ptr() as *mut std::ffi::c_void;
 230        let (primary, clipboard) = unsafe { create_clipboards_from_external(display) };
 231
 232        let event_loop = EventLoop::<WaylandClientStatePtr>::try_new().unwrap();
 233
 234        let (common, main_receiver) = LinuxCommon::new(event_loop.get_signal());
 235
 236        let handle = event_loop.handle();
 237
 238        handle.insert_source(main_receiver, |event, _, _: &mut WaylandClientStatePtr| {
 239            if let calloop::channel::Event::Msg(runnable) = event {
 240                runnable.run();
 241            }
 242        });
 243
 244        let globals = Globals::new(globals, common.foreground_executor.clone(), qh);
 245
 246        let cursor = Cursor::new(&conn, &globals, 24);
 247
 248        let mut state = Rc::new(RefCell::new(WaylandClientState {
 249            globals,
 250            wl_pointer: None,
 251            output_scales: outputs,
 252            windows: HashMap::default(),
 253            common,
 254            keymap_state: None,
 255            click: ClickState {
 256                last_click: Instant::now(),
 257                last_location: Point::new(px(0.0), px(0.0)),
 258                current_count: 0,
 259            },
 260            repeat: KeyRepeat {
 261                characters_per_second: 16,
 262                delay: Duration::from_millis(500),
 263                current_id: 0,
 264                current_keysym: None,
 265            },
 266            modifiers: Modifiers {
 267                shift: false,
 268                control: false,
 269                alt: false,
 270                function: false,
 271                platform: false,
 272            },
 273            scroll_event_received: false,
 274            axis_source: AxisSource::Wheel,
 275            mouse_location: None,
 276            continuous_scroll_delta: None,
 277            discrete_scroll_delta: None,
 278            vertical_modifier: -1.0,
 279            horizontal_modifier: -1.0,
 280            button_pressed: None,
 281            mouse_focused_window: None,
 282            keyboard_focused_window: None,
 283            loop_handle: handle.clone(),
 284            cursor_icon_name: "arrow".to_string(),
 285            enter_token: None,
 286            cursor,
 287            clipboard: Some(clipboard),
 288            primary: Some(primary),
 289            event_loop: Some(event_loop),
 290        }));
 291
 292        WaylandSource::new(conn, event_queue).insert(handle);
 293
 294        Self(state)
 295    }
 296}
 297
 298impl LinuxClient for WaylandClient {
 299    fn displays(&self) -> Vec<Rc<dyn PlatformDisplay>> {
 300        Vec::new()
 301    }
 302
 303    fn display(&self, id: DisplayId) -> Option<Rc<dyn PlatformDisplay>> {
 304        unimplemented!()
 305    }
 306
 307    fn primary_display(&self) -> Option<Rc<dyn PlatformDisplay>> {
 308        None
 309    }
 310
 311    fn open_window(
 312        &self,
 313        handle: AnyWindowHandle,
 314        params: WindowParams,
 315    ) -> Box<dyn PlatformWindow> {
 316        let mut state = self.0.borrow_mut();
 317
 318        let (window, surface_id) = WaylandWindow::new(
 319            state.globals.clone(),
 320            WaylandClientStatePtr(Rc::downgrade(&self.0)),
 321            params,
 322        );
 323        state.windows.insert(surface_id, window.0.clone());
 324
 325        Box::new(window)
 326    }
 327
 328    fn set_cursor_style(&self, style: CursorStyle) {
 329        // Based on cursor names from https://gitlab.gnome.org/GNOME/adwaita-icon-theme (GNOME)
 330        // and https://github.com/KDE/breeze (KDE). Both of them seem to be also derived from
 331        // Web CSS cursor names: https://developer.mozilla.org/en-US/docs/Web/CSS/cursor#values
 332        let cursor_icon_name = match style {
 333            CursorStyle::Arrow => "arrow",
 334            CursorStyle::IBeam => "text",
 335            CursorStyle::Crosshair => "crosshair",
 336            CursorStyle::ClosedHand => "grabbing",
 337            CursorStyle::OpenHand => "grab",
 338            CursorStyle::PointingHand => "pointer",
 339            CursorStyle::ResizeLeft => "w-resize",
 340            CursorStyle::ResizeRight => "e-resize",
 341            CursorStyle::ResizeLeftRight => "ew-resize",
 342            CursorStyle::ResizeUp => "n-resize",
 343            CursorStyle::ResizeDown => "s-resize",
 344            CursorStyle::ResizeUpDown => "ns-resize",
 345            CursorStyle::DisappearingItem => "grabbing", // todo(linux) - couldn't find equivalent icon in linux
 346            CursorStyle::IBeamCursorForVerticalLayout => "vertical-text",
 347            CursorStyle::OperationNotAllowed => "not-allowed",
 348            CursorStyle::DragLink => "alias",
 349            CursorStyle::DragCopy => "copy",
 350            CursorStyle::ContextualMenu => "context-menu",
 351        }
 352        .to_string();
 353
 354        let mut state = self.0.borrow_mut();
 355        state.cursor_icon_name = cursor_icon_name.clone();
 356        if state.mouse_focused_window.is_some() {
 357            let wl_pointer = state
 358                .wl_pointer
 359                .clone()
 360                .expect("window is focused by pointer");
 361            state.cursor.set_icon(&wl_pointer, &cursor_icon_name);
 362        }
 363    }
 364
 365    fn with_common<R>(&self, f: impl FnOnce(&mut LinuxCommon) -> R) -> R {
 366        f(&mut self.0.borrow_mut().common)
 367    }
 368
 369    fn run(&self) {
 370        let mut event_loop = self
 371            .0
 372            .borrow_mut()
 373            .event_loop
 374            .take()
 375            .expect("App is already running");
 376
 377        event_loop
 378            .run(
 379                None,
 380                &mut WaylandClientStatePtr(Rc::downgrade(&self.0)),
 381                |_| {},
 382            )
 383            .log_err();
 384    }
 385
 386    fn write_to_primary(&self, item: crate::ClipboardItem) {
 387        self.0
 388            .borrow_mut()
 389            .primary
 390            .as_mut()
 391            .unwrap()
 392            .set_contents(item.text);
 393    }
 394
 395    fn write_to_clipboard(&self, item: crate::ClipboardItem) {
 396        self.0
 397            .borrow_mut()
 398            .clipboard
 399            .as_mut()
 400            .unwrap()
 401            .set_contents(item.text);
 402    }
 403
 404    fn read_from_primary(&self) -> Option<crate::ClipboardItem> {
 405        self.0
 406            .borrow_mut()
 407            .primary
 408            .as_mut()
 409            .unwrap()
 410            .get_contents()
 411            .ok()
 412            .map(|s| crate::ClipboardItem {
 413                text: s,
 414                metadata: None,
 415            })
 416    }
 417
 418    fn read_from_clipboard(&self) -> Option<crate::ClipboardItem> {
 419        self.0
 420            .borrow_mut()
 421            .clipboard
 422            .as_mut()
 423            .unwrap()
 424            .get_contents()
 425            .ok()
 426            .map(|s| crate::ClipboardItem {
 427                text: s,
 428                metadata: None,
 429            })
 430    }
 431}
 432
 433impl Dispatch<wl_registry::WlRegistry, GlobalListContents> for WaylandClientStatePtr {
 434    fn event(
 435        this: &mut Self,
 436        registry: &wl_registry::WlRegistry,
 437        event: wl_registry::Event,
 438        _: &GlobalListContents,
 439        _: &Connection,
 440        qh: &QueueHandle<Self>,
 441    ) {
 442        let mut client = this.get_client();
 443        let mut state = client.borrow_mut();
 444
 445        match event {
 446            wl_registry::Event::Global {
 447                name,
 448                interface,
 449                version,
 450            } => match &interface[..] {
 451                "wl_seat" => {
 452                    state.wl_pointer = None;
 453                    registry.bind::<wl_seat::WlSeat, _, _>(name, wl_seat_version(version), qh, ());
 454                }
 455                "wl_output" => {
 456                    let output =
 457                        registry.bind::<wl_output::WlOutput, _, _>(name, WL_OUTPUT_VERSION, qh, ());
 458
 459                    state.output_scales.insert(output.id(), 1);
 460                }
 461                _ => {}
 462            },
 463            wl_registry::Event::GlobalRemove { name: _ } => {}
 464            _ => {}
 465        }
 466    }
 467}
 468
 469delegate_noop!(WaylandClientStatePtr: ignore wl_compositor::WlCompositor);
 470delegate_noop!(WaylandClientStatePtr: ignore wl_shm::WlShm);
 471delegate_noop!(WaylandClientStatePtr: ignore wl_shm_pool::WlShmPool);
 472delegate_noop!(WaylandClientStatePtr: ignore wl_buffer::WlBuffer);
 473delegate_noop!(WaylandClientStatePtr: ignore wp_fractional_scale_manager_v1::WpFractionalScaleManagerV1);
 474delegate_noop!(WaylandClientStatePtr: ignore zxdg_decoration_manager_v1::ZxdgDecorationManagerV1);
 475delegate_noop!(WaylandClientStatePtr: ignore wp_viewporter::WpViewporter);
 476delegate_noop!(WaylandClientStatePtr: ignore wp_viewport::WpViewport);
 477
 478impl Dispatch<WlCallback, ObjectId> for WaylandClientStatePtr {
 479    fn event(
 480        state: &mut WaylandClientStatePtr,
 481        _: &wl_callback::WlCallback,
 482        event: wl_callback::Event,
 483        surface_id: &ObjectId,
 484        _: &Connection,
 485        qh: &QueueHandle<Self>,
 486    ) {
 487        let client = state.get_client();
 488        let mut state = client.borrow_mut();
 489        let Some(window) = get_window(&mut state, surface_id) else {
 490            return;
 491        };
 492        drop(state);
 493
 494        match event {
 495            wl_callback::Event::Done { callback_data } => {
 496                window.frame(true);
 497            }
 498            _ => {}
 499        }
 500    }
 501}
 502
 503fn get_window(
 504    mut state: &mut RefMut<WaylandClientState>,
 505    surface_id: &ObjectId,
 506) -> Option<WaylandWindowStatePtr> {
 507    state.windows.get(surface_id).cloned()
 508}
 509
 510impl Dispatch<wl_surface::WlSurface, ()> for WaylandClientStatePtr {
 511    fn event(
 512        this: &mut Self,
 513        surface: &wl_surface::WlSurface,
 514        event: <wl_surface::WlSurface as Proxy>::Event,
 515        _: &(),
 516        _: &Connection,
 517        _: &QueueHandle<Self>,
 518    ) {
 519        let mut client = this.get_client();
 520        let mut state = client.borrow_mut();
 521
 522        let Some(window) = get_window(&mut state, &surface.id()) else {
 523            return;
 524        };
 525        let scales = state.output_scales.clone();
 526        drop(state);
 527
 528        window.handle_surface_event(event, scales);
 529    }
 530}
 531
 532impl Dispatch<wl_output::WlOutput, ()> for WaylandClientStatePtr {
 533    fn event(
 534        this: &mut Self,
 535        output: &wl_output::WlOutput,
 536        event: <wl_output::WlOutput as Proxy>::Event,
 537        _: &(),
 538        _: &Connection,
 539        _: &QueueHandle<Self>,
 540    ) {
 541        let mut client = this.get_client();
 542        let mut state = client.borrow_mut();
 543
 544        let Some(mut output_scale) = state.output_scales.get_mut(&output.id()) else {
 545            return;
 546        };
 547
 548        match event {
 549            wl_output::Event::Scale { factor } => {
 550                *output_scale = factor;
 551            }
 552            _ => {}
 553        }
 554    }
 555}
 556
 557impl Dispatch<xdg_surface::XdgSurface, ObjectId> for WaylandClientStatePtr {
 558    fn event(
 559        state: &mut Self,
 560        xdg_surface: &xdg_surface::XdgSurface,
 561        event: xdg_surface::Event,
 562        surface_id: &ObjectId,
 563        _: &Connection,
 564        _: &QueueHandle<Self>,
 565    ) {
 566        let client = state.get_client();
 567        let mut state = client.borrow_mut();
 568        let Some(window) = get_window(&mut state, surface_id) else {
 569            return;
 570        };
 571        drop(state);
 572        window.handle_xdg_surface_event(event);
 573    }
 574}
 575
 576impl Dispatch<xdg_toplevel::XdgToplevel, ObjectId> for WaylandClientStatePtr {
 577    fn event(
 578        this: &mut Self,
 579        xdg_toplevel: &xdg_toplevel::XdgToplevel,
 580        event: <xdg_toplevel::XdgToplevel as Proxy>::Event,
 581        surface_id: &ObjectId,
 582        _: &Connection,
 583        _: &QueueHandle<Self>,
 584    ) {
 585        let client = this.get_client();
 586        let mut state = client.borrow_mut();
 587        let Some(window) = get_window(&mut state, surface_id) else {
 588            return;
 589        };
 590
 591        drop(state);
 592        let should_close = window.handle_toplevel_event(event);
 593
 594        if should_close {
 595            this.drop_window(surface_id);
 596        }
 597    }
 598}
 599
 600impl Dispatch<xdg_wm_base::XdgWmBase, ()> for WaylandClientStatePtr {
 601    fn event(
 602        _: &mut Self,
 603        wm_base: &xdg_wm_base::XdgWmBase,
 604        event: <xdg_wm_base::XdgWmBase as Proxy>::Event,
 605        _: &(),
 606        _: &Connection,
 607        _: &QueueHandle<Self>,
 608    ) {
 609        if let xdg_wm_base::Event::Ping { serial } = event {
 610            wm_base.pong(serial);
 611        }
 612    }
 613}
 614
 615impl Dispatch<wl_seat::WlSeat, ()> for WaylandClientStatePtr {
 616    fn event(
 617        state: &mut Self,
 618        seat: &wl_seat::WlSeat,
 619        event: wl_seat::Event,
 620        data: &(),
 621        conn: &Connection,
 622        qh: &QueueHandle<Self>,
 623    ) {
 624        if let wl_seat::Event::Capabilities {
 625            capabilities: WEnum::Value(capabilities),
 626        } = event
 627        {
 628            if capabilities.contains(wl_seat::Capability::Keyboard) {
 629                seat.get_keyboard(qh, ());
 630            }
 631            if capabilities.contains(wl_seat::Capability::Pointer) {
 632                let client = state.get_client();
 633                let mut state = client.borrow_mut();
 634                state.wl_pointer = Some(seat.get_pointer(qh, ()));
 635            }
 636        }
 637    }
 638}
 639
 640impl Dispatch<wl_keyboard::WlKeyboard, ()> for WaylandClientStatePtr {
 641    fn event(
 642        this: &mut Self,
 643        keyboard: &wl_keyboard::WlKeyboard,
 644        event: wl_keyboard::Event,
 645        data: &(),
 646        conn: &Connection,
 647        qh: &QueueHandle<Self>,
 648    ) {
 649        let mut client = this.get_client();
 650        let mut state = client.borrow_mut();
 651        match event {
 652            wl_keyboard::Event::RepeatInfo { rate, delay } => {
 653                state.repeat.characters_per_second = rate as u32;
 654                state.repeat.delay = Duration::from_millis(delay as u64);
 655            }
 656            wl_keyboard::Event::Keymap {
 657                format: WEnum::Value(format),
 658                fd,
 659                size,
 660                ..
 661            } => {
 662                assert_eq!(
 663                    format,
 664                    wl_keyboard::KeymapFormat::XkbV1,
 665                    "Unsupported keymap format"
 666                );
 667                let keymap = unsafe {
 668                    xkb::Keymap::new_from_fd(
 669                        &xkb::Context::new(xkb::CONTEXT_NO_FLAGS),
 670                        fd,
 671                        size as usize,
 672                        XKB_KEYMAP_FORMAT_TEXT_V1,
 673                        KEYMAP_COMPILE_NO_FLAGS,
 674                    )
 675                    .log_err()
 676                    .flatten()
 677                    .expect("Failed to create keymap")
 678                };
 679                state.keymap_state = Some(xkb::State::new(&keymap));
 680            }
 681            wl_keyboard::Event::Enter { surface, .. } => {
 682                state.keyboard_focused_window = get_window(&mut state, &surface.id());
 683
 684                if let Some(window) = state.keyboard_focused_window.clone() {
 685                    drop(state);
 686                    window.set_focused(true);
 687                }
 688            }
 689            wl_keyboard::Event::Leave { surface, .. } => {
 690                let keyboard_focused_window = get_window(&mut state, &surface.id());
 691                state.keyboard_focused_window = None;
 692
 693                if let Some(window) = keyboard_focused_window {
 694                    drop(state);
 695                    window.set_focused(false);
 696                }
 697            }
 698            wl_keyboard::Event::Modifiers {
 699                mods_depressed,
 700                mods_latched,
 701                mods_locked,
 702                group,
 703                ..
 704            } => {
 705                let focused_window = state.keyboard_focused_window.clone();
 706                let Some(focused_window) = focused_window else {
 707                    return;
 708                };
 709
 710                let keymap_state = state.keymap_state.as_mut().unwrap();
 711                keymap_state.update_mask(mods_depressed, mods_latched, mods_locked, 0, 0, group);
 712                state.modifiers = Modifiers::from_xkb(keymap_state);
 713
 714                let input = PlatformInput::ModifiersChanged(ModifiersChangedEvent {
 715                    modifiers: state.modifiers,
 716                });
 717
 718                drop(state);
 719                focused_window.handle_input(input);
 720            }
 721            wl_keyboard::Event::Key {
 722                key,
 723                state: WEnum::Value(key_state),
 724                ..
 725            } => {
 726                let focused_window = state.keyboard_focused_window.clone();
 727                let Some(focused_window) = focused_window else {
 728                    return;
 729                };
 730                let focused_window = focused_window.clone();
 731
 732                let keymap_state = state.keymap_state.as_ref().unwrap();
 733                let keycode = Keycode::from(key + MIN_KEYCODE);
 734                let keysym = keymap_state.key_get_one_sym(keycode);
 735
 736                match key_state {
 737                    wl_keyboard::KeyState::Pressed if !keysym.is_modifier_key() => {
 738                        let input = PlatformInput::KeyDown(KeyDownEvent {
 739                            keystroke: Keystroke::from_xkb(keymap_state, state.modifiers, keycode),
 740                            is_held: false, // todo(linux)
 741                        });
 742
 743                        state.repeat.current_id += 1;
 744                        state.repeat.current_keysym = Some(keysym);
 745
 746                        let rate = state.repeat.characters_per_second;
 747                        let id = state.repeat.current_id;
 748                        state
 749                            .loop_handle
 750                            .insert_source(Timer::from_duration(state.repeat.delay), {
 751                                let input = input.clone();
 752                                move |event, _metadata, this| {
 753                                    let mut client = this.get_client();
 754                                    let mut state = client.borrow_mut();
 755                                    let is_repeating = id == state.repeat.current_id
 756                                        && state.repeat.current_keysym.is_some()
 757                                        && state.keyboard_focused_window.is_some();
 758
 759                                    if !is_repeating {
 760                                        return TimeoutAction::Drop;
 761                                    }
 762
 763                                    let focused_window =
 764                                        state.keyboard_focused_window.as_ref().unwrap().clone();
 765
 766                                    drop(state);
 767                                    focused_window.handle_input(input.clone());
 768
 769                                    TimeoutAction::ToDuration(Duration::from_secs(1) / rate)
 770                                }
 771                            })
 772                            .unwrap();
 773
 774                        drop(state);
 775                        focused_window.handle_input(input);
 776                    }
 777                    wl_keyboard::KeyState::Released if !keysym.is_modifier_key() => {
 778                        let input = PlatformInput::KeyUp(KeyUpEvent {
 779                            keystroke: Keystroke::from_xkb(keymap_state, state.modifiers, keycode),
 780                        });
 781
 782                        state.repeat.current_keysym = None;
 783
 784                        drop(state);
 785                        focused_window.handle_input(input);
 786                    }
 787                    _ => {}
 788                }
 789            }
 790            _ => {}
 791        }
 792    }
 793}
 794
 795fn linux_button_to_gpui(button: u32) -> Option<MouseButton> {
 796    // These values are coming from <linux/input-event-codes.h>.
 797    const BTN_LEFT: u32 = 0x110;
 798    const BTN_RIGHT: u32 = 0x111;
 799    const BTN_MIDDLE: u32 = 0x112;
 800    const BTN_SIDE: u32 = 0x113;
 801    const BTN_EXTRA: u32 = 0x114;
 802    const BTN_FORWARD: u32 = 0x115;
 803    const BTN_BACK: u32 = 0x116;
 804
 805    Some(match button {
 806        BTN_LEFT => MouseButton::Left,
 807        BTN_RIGHT => MouseButton::Right,
 808        BTN_MIDDLE => MouseButton::Middle,
 809        BTN_BACK | BTN_SIDE => MouseButton::Navigate(NavigationDirection::Back),
 810        BTN_FORWARD | BTN_EXTRA => MouseButton::Navigate(NavigationDirection::Forward),
 811        _ => return None,
 812    })
 813}
 814
 815impl Dispatch<wl_pointer::WlPointer, ()> for WaylandClientStatePtr {
 816    fn event(
 817        this: &mut Self,
 818        wl_pointer: &wl_pointer::WlPointer,
 819        event: wl_pointer::Event,
 820        data: &(),
 821        conn: &Connection,
 822        qh: &QueueHandle<Self>,
 823    ) {
 824        let mut client = this.get_client();
 825        let mut state = client.borrow_mut();
 826        let cursor_icon_name = state.cursor_icon_name.clone();
 827
 828        match event {
 829            wl_pointer::Event::Enter {
 830                serial,
 831                surface,
 832                surface_x,
 833                surface_y,
 834                ..
 835            } => {
 836                state.mouse_location = Some(point(px(surface_x as f32), px(surface_y as f32)));
 837
 838                if let Some(window) = get_window(&mut state, &surface.id()) {
 839                    state.enter_token = Some(());
 840                    state.mouse_focused_window = Some(window.clone());
 841                    state.cursor.mark_dirty();
 842                    state.cursor.set_serial_id(serial);
 843                    state
 844                        .cursor
 845                        .set_icon(&wl_pointer, cursor_icon_name.as_str());
 846                    drop(state);
 847                    window.set_focused(true);
 848                }
 849            }
 850            wl_pointer::Event::Leave { surface, .. } => {
 851                if let Some(focused_window) = state.mouse_focused_window.clone() {
 852                    state.enter_token.take();
 853                    let input = PlatformInput::MouseExited(MouseExitEvent {
 854                        position: state.mouse_location.unwrap(),
 855                        pressed_button: state.button_pressed,
 856                        modifiers: state.modifiers,
 857                    });
 858                    state.mouse_focused_window = None;
 859                    state.mouse_location = None;
 860
 861                    drop(state);
 862                    focused_window.handle_input(input);
 863                    focused_window.set_focused(false);
 864                }
 865            }
 866            wl_pointer::Event::Motion {
 867                time,
 868                surface_x,
 869                surface_y,
 870                ..
 871            } => {
 872                if state.mouse_focused_window.is_none() {
 873                    return;
 874                }
 875                state.mouse_location = Some(point(px(surface_x as f32), px(surface_y as f32)));
 876
 877                if let Some(window) = state.mouse_focused_window.clone() {
 878                    let input = PlatformInput::MouseMove(MouseMoveEvent {
 879                        position: state.mouse_location.unwrap(),
 880                        pressed_button: state.button_pressed,
 881                        modifiers: state.modifiers,
 882                    });
 883                    drop(state);
 884                    window.handle_input(input);
 885                }
 886            }
 887            wl_pointer::Event::Button {
 888                button,
 889                state: WEnum::Value(button_state),
 890                ..
 891            } => {
 892                let button = linux_button_to_gpui(button);
 893                let Some(button) = button else { return };
 894                if state.mouse_focused_window.is_none() {
 895                    return;
 896                }
 897                match button_state {
 898                    wl_pointer::ButtonState::Pressed => {
 899                        let click_elapsed = state.click.last_click.elapsed();
 900
 901                        if click_elapsed < DOUBLE_CLICK_INTERVAL
 902                            && is_within_click_distance(
 903                                state.click.last_location,
 904                                state.mouse_location.unwrap(),
 905                            )
 906                        {
 907                            state.click.current_count += 1;
 908                        } else {
 909                            state.click.current_count = 1;
 910                        }
 911
 912                        state.click.last_click = Instant::now();
 913                        state.click.last_location = state.mouse_location.unwrap();
 914
 915                        state.button_pressed = Some(button);
 916
 917                        if let Some(window) = state.mouse_focused_window.clone() {
 918                            let input = PlatformInput::MouseDown(MouseDownEvent {
 919                                button,
 920                                position: state.mouse_location.unwrap(),
 921                                modifiers: state.modifiers,
 922                                click_count: state.click.current_count,
 923                                first_mouse: state.enter_token.take().is_some(),
 924                            });
 925                            drop(state);
 926                            window.handle_input(input);
 927                        }
 928                    }
 929                    wl_pointer::ButtonState::Released => {
 930                        state.button_pressed = None;
 931
 932                        if let Some(window) = state.mouse_focused_window.clone() {
 933                            let input = PlatformInput::MouseUp(MouseUpEvent {
 934                                button,
 935                                position: state.mouse_location.unwrap(),
 936                                modifiers: state.modifiers,
 937                                click_count: state.click.current_count,
 938                            });
 939                            drop(state);
 940                            window.handle_input(input);
 941                        }
 942                    }
 943                    _ => {}
 944                }
 945            }
 946
 947            // Axis Events
 948            wl_pointer::Event::AxisSource {
 949                axis_source: WEnum::Value(axis_source),
 950            } => {
 951                state.axis_source = axis_source;
 952            }
 953            wl_pointer::Event::Axis {
 954                time,
 955                axis: WEnum::Value(axis),
 956                value,
 957                ..
 958            } => {
 959                let axis_source = state.axis_source;
 960                let axis_modifier = match axis {
 961                    wl_pointer::Axis::VerticalScroll => state.vertical_modifier,
 962                    wl_pointer::Axis::HorizontalScroll => state.horizontal_modifier,
 963                    _ => 1.0,
 964                };
 965                let supports_relative_direction =
 966                    wl_pointer.version() >= wl_pointer::EVT_AXIS_RELATIVE_DIRECTION_SINCE;
 967                state.scroll_event_received = true;
 968                let scroll_delta = state
 969                    .continuous_scroll_delta
 970                    .get_or_insert(point(px(0.0), px(0.0)));
 971                // TODO: Make nice feeling kinetic scrolling that integrates with the platform's scroll settings
 972                let modifier = 3.0;
 973                match axis {
 974                    wl_pointer::Axis::VerticalScroll => {
 975                        scroll_delta.y += px(value as f32 * modifier * axis_modifier);
 976                    }
 977                    wl_pointer::Axis::HorizontalScroll => {
 978                        scroll_delta.x += px(value as f32 * modifier * axis_modifier);
 979                    }
 980                    _ => unreachable!(),
 981                }
 982            }
 983            wl_pointer::Event::AxisDiscrete {
 984                axis: WEnum::Value(axis),
 985                discrete,
 986            } => {
 987                state.scroll_event_received = true;
 988                let axis_modifier = match axis {
 989                    wl_pointer::Axis::VerticalScroll => state.vertical_modifier,
 990                    wl_pointer::Axis::HorizontalScroll => state.horizontal_modifier,
 991                    _ => 1.0,
 992                };
 993
 994                // TODO: Make nice feeling kinetic scrolling that integrates with the platform's scroll settings
 995                let modifier = 3.0;
 996
 997                let scroll_delta = state.discrete_scroll_delta.get_or_insert(point(0.0, 0.0));
 998                match axis {
 999                    wl_pointer::Axis::VerticalScroll => {
1000                        scroll_delta.y += discrete as f32 * axis_modifier * modifier;
1001                    }
1002                    wl_pointer::Axis::HorizontalScroll => {
1003                        scroll_delta.x += discrete as f32 * axis_modifier * modifier;
1004                    }
1005                    _ => unreachable!(),
1006                }
1007            }
1008            wl_pointer::Event::AxisRelativeDirection {
1009                axis: WEnum::Value(axis),
1010                direction: WEnum::Value(direction),
1011            } => match (axis, direction) {
1012                (wl_pointer::Axis::VerticalScroll, AxisRelativeDirection::Identical) => {
1013                    state.vertical_modifier = -1.0
1014                }
1015                (wl_pointer::Axis::VerticalScroll, AxisRelativeDirection::Inverted) => {
1016                    state.vertical_modifier = 1.0
1017                }
1018                (wl_pointer::Axis::HorizontalScroll, AxisRelativeDirection::Identical) => {
1019                    state.horizontal_modifier = -1.0
1020                }
1021                (wl_pointer::Axis::HorizontalScroll, AxisRelativeDirection::Inverted) => {
1022                    state.horizontal_modifier = 1.0
1023                }
1024                _ => unreachable!(),
1025            },
1026            wl_pointer::Event::AxisValue120 {
1027                axis: WEnum::Value(axis),
1028                value120,
1029            } => {
1030                state.scroll_event_received = true;
1031                let axis_modifier = match axis {
1032                    wl_pointer::Axis::VerticalScroll => state.vertical_modifier,
1033                    wl_pointer::Axis::HorizontalScroll => state.horizontal_modifier,
1034                    _ => unreachable!(),
1035                };
1036
1037                let scroll_delta = state.discrete_scroll_delta.get_or_insert(point(0.0, 0.0));
1038                let wheel_percent = value120 as f32 / 120.0;
1039                match axis {
1040                    wl_pointer::Axis::VerticalScroll => {
1041                        scroll_delta.y += wheel_percent * axis_modifier;
1042                    }
1043                    wl_pointer::Axis::HorizontalScroll => {
1044                        scroll_delta.x += wheel_percent * axis_modifier;
1045                    }
1046                    _ => unreachable!(),
1047                }
1048            }
1049            wl_pointer::Event::Frame => {
1050                if state.scroll_event_received {
1051                    state.scroll_event_received = false;
1052                    let continuous = state.continuous_scroll_delta.take();
1053                    let discrete = state.discrete_scroll_delta.take();
1054                    if let Some(continuous) = continuous {
1055                        if let Some(window) = state.mouse_focused_window.clone() {
1056                            let input = PlatformInput::ScrollWheel(ScrollWheelEvent {
1057                                position: state.mouse_location.unwrap(),
1058                                delta: ScrollDelta::Pixels(continuous),
1059                                modifiers: state.modifiers,
1060                                touch_phase: TouchPhase::Moved,
1061                            });
1062                            drop(state);
1063                            window.handle_input(input);
1064                        }
1065                    } else if let Some(discrete) = discrete {
1066                        if let Some(window) = state.mouse_focused_window.clone() {
1067                            let input = PlatformInput::ScrollWheel(ScrollWheelEvent {
1068                                position: state.mouse_location.unwrap(),
1069                                delta: ScrollDelta::Lines(discrete),
1070                                modifiers: state.modifiers,
1071                                touch_phase: TouchPhase::Moved,
1072                            });
1073                            drop(state);
1074                            window.handle_input(input);
1075                        }
1076                    }
1077                }
1078            }
1079            _ => {}
1080        }
1081    }
1082}
1083
1084impl Dispatch<wp_fractional_scale_v1::WpFractionalScaleV1, ObjectId> for WaylandClientStatePtr {
1085    fn event(
1086        this: &mut Self,
1087        _: &wp_fractional_scale_v1::WpFractionalScaleV1,
1088        event: <wp_fractional_scale_v1::WpFractionalScaleV1 as Proxy>::Event,
1089        surface_id: &ObjectId,
1090        _: &Connection,
1091        _: &QueueHandle<Self>,
1092    ) {
1093        let client = this.get_client();
1094        let mut state = client.borrow_mut();
1095
1096        let Some(window) = get_window(&mut state, surface_id) else {
1097            return;
1098        };
1099
1100        drop(state);
1101        window.handle_fractional_scale_event(event);
1102    }
1103}
1104
1105impl Dispatch<zxdg_toplevel_decoration_v1::ZxdgToplevelDecorationV1, ObjectId>
1106    for WaylandClientStatePtr
1107{
1108    fn event(
1109        this: &mut Self,
1110        _: &zxdg_toplevel_decoration_v1::ZxdgToplevelDecorationV1,
1111        event: zxdg_toplevel_decoration_v1::Event,
1112        surface_id: &ObjectId,
1113        _: &Connection,
1114        _: &QueueHandle<Self>,
1115    ) {
1116        let client = this.get_client();
1117        let mut state = client.borrow_mut();
1118        let Some(window) = get_window(&mut state, surface_id) else {
1119            return;
1120        };
1121
1122        drop(state);
1123        window.handle_toplevel_decoration_event(event);
1124    }
1125}