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