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