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