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_clipboard(&self, item: crate::ClipboardItem) {
 381        self.0.borrow_mut().clipboard.set_contents(item.text);
 382    }
 383
 384    fn read_from_clipboard(&self) -> Option<crate::ClipboardItem> {
 385        self.0
 386            .borrow_mut()
 387            .clipboard
 388            .get_contents()
 389            .ok()
 390            .map(|s| crate::ClipboardItem {
 391                text: s,
 392                metadata: None,
 393            })
 394    }
 395}
 396
 397impl Dispatch<wl_registry::WlRegistry, GlobalListContents> for WaylandClientStatePtr {
 398    fn event(
 399        this: &mut Self,
 400        registry: &wl_registry::WlRegistry,
 401        event: wl_registry::Event,
 402        _: &GlobalListContents,
 403        _: &Connection,
 404        qh: &QueueHandle<Self>,
 405    ) {
 406        let mut client = this.get_client();
 407        let mut state = client.borrow_mut();
 408
 409        match event {
 410            wl_registry::Event::Global {
 411                name,
 412                interface,
 413                version,
 414            } => match &interface[..] {
 415                "wl_seat" => {
 416                    state.wl_pointer = None;
 417                    registry.bind::<wl_seat::WlSeat, _, _>(name, wl_seat_version(version), qh, ());
 418                }
 419                "wl_output" => {
 420                    let output =
 421                        registry.bind::<wl_output::WlOutput, _, _>(name, WL_OUTPUT_VERSION, qh, ());
 422
 423                    state.output_scales.insert(output.id(), 1);
 424                }
 425                _ => {}
 426            },
 427            wl_registry::Event::GlobalRemove { name: _ } => {}
 428            _ => {}
 429        }
 430    }
 431}
 432
 433delegate_noop!(WaylandClientStatePtr: ignore wl_compositor::WlCompositor);
 434delegate_noop!(WaylandClientStatePtr: ignore wl_shm::WlShm);
 435delegate_noop!(WaylandClientStatePtr: ignore wl_shm_pool::WlShmPool);
 436delegate_noop!(WaylandClientStatePtr: ignore wl_buffer::WlBuffer);
 437delegate_noop!(WaylandClientStatePtr: ignore wp_fractional_scale_manager_v1::WpFractionalScaleManagerV1);
 438delegate_noop!(WaylandClientStatePtr: ignore zxdg_decoration_manager_v1::ZxdgDecorationManagerV1);
 439delegate_noop!(WaylandClientStatePtr: ignore wp_viewporter::WpViewporter);
 440delegate_noop!(WaylandClientStatePtr: ignore wp_viewport::WpViewport);
 441
 442impl Dispatch<WlCallback, ObjectId> for WaylandClientStatePtr {
 443    fn event(
 444        state: &mut WaylandClientStatePtr,
 445        _: &wl_callback::WlCallback,
 446        event: wl_callback::Event,
 447        surface_id: &ObjectId,
 448        _: &Connection,
 449        qh: &QueueHandle<Self>,
 450    ) {
 451        let client = state.get_client();
 452        let mut state = client.borrow_mut();
 453        let Some(window) = get_window(&mut state, surface_id) else {
 454            return;
 455        };
 456        drop(state);
 457
 458        match event {
 459            wl_callback::Event::Done { callback_data } => {
 460                window.frame(true);
 461            }
 462            _ => {}
 463        }
 464    }
 465}
 466
 467fn get_window(
 468    mut state: &mut RefMut<WaylandClientState>,
 469    surface_id: &ObjectId,
 470) -> Option<WaylandWindowStatePtr> {
 471    state.windows.get(surface_id).cloned()
 472}
 473
 474impl Dispatch<wl_surface::WlSurface, ()> for WaylandClientStatePtr {
 475    fn event(
 476        this: &mut Self,
 477        surface: &wl_surface::WlSurface,
 478        event: <wl_surface::WlSurface as Proxy>::Event,
 479        _: &(),
 480        _: &Connection,
 481        _: &QueueHandle<Self>,
 482    ) {
 483        let mut client = this.get_client();
 484        let mut state = client.borrow_mut();
 485
 486        let Some(window) = get_window(&mut state, &surface.id()) else {
 487            return;
 488        };
 489        let scales = state.output_scales.clone();
 490        drop(state);
 491
 492        window.handle_surface_event(event, scales);
 493    }
 494}
 495
 496impl Dispatch<wl_output::WlOutput, ()> for WaylandClientStatePtr {
 497    fn event(
 498        this: &mut Self,
 499        output: &wl_output::WlOutput,
 500        event: <wl_output::WlOutput as Proxy>::Event,
 501        _: &(),
 502        _: &Connection,
 503        _: &QueueHandle<Self>,
 504    ) {
 505        let mut client = this.get_client();
 506        let mut state = client.borrow_mut();
 507
 508        let Some(mut output_scale) = state.output_scales.get_mut(&output.id()) else {
 509            return;
 510        };
 511
 512        match event {
 513            wl_output::Event::Scale { factor } => {
 514                *output_scale = factor;
 515            }
 516            _ => {}
 517        }
 518    }
 519}
 520
 521impl Dispatch<xdg_surface::XdgSurface, ObjectId> for WaylandClientStatePtr {
 522    fn event(
 523        state: &mut Self,
 524        xdg_surface: &xdg_surface::XdgSurface,
 525        event: xdg_surface::Event,
 526        surface_id: &ObjectId,
 527        _: &Connection,
 528        _: &QueueHandle<Self>,
 529    ) {
 530        let client = state.get_client();
 531        let mut state = client.borrow_mut();
 532        let Some(window) = get_window(&mut state, surface_id) else {
 533            return;
 534        };
 535        drop(state);
 536        window.handle_xdg_surface_event(event);
 537    }
 538}
 539
 540impl Dispatch<xdg_toplevel::XdgToplevel, ObjectId> for WaylandClientStatePtr {
 541    fn event(
 542        this: &mut Self,
 543        xdg_toplevel: &xdg_toplevel::XdgToplevel,
 544        event: <xdg_toplevel::XdgToplevel as Proxy>::Event,
 545        surface_id: &ObjectId,
 546        _: &Connection,
 547        _: &QueueHandle<Self>,
 548    ) {
 549        let client = this.get_client();
 550        let mut state = client.borrow_mut();
 551        let Some(window) = get_window(&mut state, surface_id) else {
 552            return;
 553        };
 554
 555        drop(state);
 556        let should_close = window.handle_toplevel_event(event);
 557
 558        if should_close {
 559            this.drop_window(surface_id);
 560        }
 561    }
 562}
 563
 564impl Dispatch<xdg_wm_base::XdgWmBase, ()> for WaylandClientStatePtr {
 565    fn event(
 566        _: &mut Self,
 567        wm_base: &xdg_wm_base::XdgWmBase,
 568        event: <xdg_wm_base::XdgWmBase as Proxy>::Event,
 569        _: &(),
 570        _: &Connection,
 571        _: &QueueHandle<Self>,
 572    ) {
 573        if let xdg_wm_base::Event::Ping { serial } = event {
 574            wm_base.pong(serial);
 575        }
 576    }
 577}
 578
 579impl Dispatch<wl_seat::WlSeat, ()> for WaylandClientStatePtr {
 580    fn event(
 581        state: &mut Self,
 582        seat: &wl_seat::WlSeat,
 583        event: wl_seat::Event,
 584        data: &(),
 585        conn: &Connection,
 586        qh: &QueueHandle<Self>,
 587    ) {
 588        if let wl_seat::Event::Capabilities {
 589            capabilities: WEnum::Value(capabilities),
 590        } = event
 591        {
 592            if capabilities.contains(wl_seat::Capability::Keyboard) {
 593                seat.get_keyboard(qh, ());
 594            }
 595            if capabilities.contains(wl_seat::Capability::Pointer) {
 596                let client = state.get_client();
 597                let mut state = client.borrow_mut();
 598                state.wl_pointer = Some(seat.get_pointer(qh, ()));
 599            }
 600        }
 601    }
 602}
 603
 604impl Dispatch<wl_keyboard::WlKeyboard, ()> for WaylandClientStatePtr {
 605    fn event(
 606        this: &mut Self,
 607        keyboard: &wl_keyboard::WlKeyboard,
 608        event: wl_keyboard::Event,
 609        data: &(),
 610        conn: &Connection,
 611        qh: &QueueHandle<Self>,
 612    ) {
 613        let mut client = this.get_client();
 614        let mut state = client.borrow_mut();
 615        match event {
 616            wl_keyboard::Event::RepeatInfo { rate, delay } => {
 617                state.repeat.characters_per_second = rate as u32;
 618                state.repeat.delay = Duration::from_millis(delay as u64);
 619            }
 620            wl_keyboard::Event::Keymap {
 621                format: WEnum::Value(format),
 622                fd,
 623                size,
 624                ..
 625            } => {
 626                assert_eq!(
 627                    format,
 628                    wl_keyboard::KeymapFormat::XkbV1,
 629                    "Unsupported keymap format"
 630                );
 631                let keymap = unsafe {
 632                    xkb::Keymap::new_from_fd(
 633                        &xkb::Context::new(xkb::CONTEXT_NO_FLAGS),
 634                        fd,
 635                        size as usize,
 636                        XKB_KEYMAP_FORMAT_TEXT_V1,
 637                        KEYMAP_COMPILE_NO_FLAGS,
 638                    )
 639                    .log_err()
 640                    .flatten()
 641                    .expect("Failed to create keymap")
 642                };
 643                state.keymap_state = Some(xkb::State::new(&keymap));
 644            }
 645            wl_keyboard::Event::Enter { surface, .. } => {
 646                state.keyboard_focused_window = get_window(&mut state, &surface.id());
 647
 648                if let Some(window) = state.keyboard_focused_window.clone() {
 649                    drop(state);
 650                    window.set_focused(true);
 651                }
 652            }
 653            wl_keyboard::Event::Leave { surface, .. } => {
 654                let keyboard_focused_window = get_window(&mut state, &surface.id());
 655                state.keyboard_focused_window = None;
 656
 657                if let Some(window) = keyboard_focused_window {
 658                    drop(state);
 659                    window.set_focused(false);
 660                }
 661            }
 662            wl_keyboard::Event::Modifiers {
 663                mods_depressed,
 664                mods_latched,
 665                mods_locked,
 666                group,
 667                ..
 668            } => {
 669                let focused_window = state.keyboard_focused_window.clone();
 670                let Some(focused_window) = focused_window else {
 671                    return;
 672                };
 673
 674                let keymap_state = state.keymap_state.as_mut().unwrap();
 675                keymap_state.update_mask(mods_depressed, mods_latched, mods_locked, 0, 0, group);
 676                state.modifiers = Modifiers::from_xkb(keymap_state);
 677
 678                let input = PlatformInput::ModifiersChanged(ModifiersChangedEvent {
 679                    modifiers: state.modifiers,
 680                });
 681
 682                drop(state);
 683                focused_window.handle_input(input);
 684            }
 685            wl_keyboard::Event::Key {
 686                key,
 687                state: WEnum::Value(key_state),
 688                ..
 689            } => {
 690                let focused_window = state.keyboard_focused_window.clone();
 691                let Some(focused_window) = focused_window else {
 692                    return;
 693                };
 694                let focused_window = focused_window.clone();
 695
 696                let keymap_state = state.keymap_state.as_ref().unwrap();
 697                let keycode = Keycode::from(key + MIN_KEYCODE);
 698                let keysym = keymap_state.key_get_one_sym(keycode);
 699
 700                match key_state {
 701                    wl_keyboard::KeyState::Pressed if !keysym.is_modifier_key() => {
 702                        let input = PlatformInput::KeyDown(KeyDownEvent {
 703                            keystroke: Keystroke::from_xkb(keymap_state, state.modifiers, keycode),
 704                            is_held: false, // todo(linux)
 705                        });
 706
 707                        state.repeat.current_id += 1;
 708                        state.repeat.current_keysym = Some(keysym);
 709
 710                        let rate = state.repeat.characters_per_second;
 711                        let id = state.repeat.current_id;
 712                        state
 713                            .loop_handle
 714                            .insert_source(Timer::from_duration(state.repeat.delay), {
 715                                let input = input.clone();
 716                                move |event, _metadata, this| {
 717                                    let mut client = this.get_client();
 718                                    let mut state = client.borrow_mut();
 719                                    let is_repeating = id == state.repeat.current_id
 720                                        && state.repeat.current_keysym.is_some()
 721                                        && state.keyboard_focused_window.is_some();
 722
 723                                    if !is_repeating {
 724                                        return TimeoutAction::Drop;
 725                                    }
 726
 727                                    let focused_window =
 728                                        state.keyboard_focused_window.as_ref().unwrap().clone();
 729
 730                                    drop(state);
 731                                    focused_window.handle_input(input.clone());
 732
 733                                    TimeoutAction::ToDuration(Duration::from_secs(1) / rate)
 734                                }
 735                            })
 736                            .unwrap();
 737
 738                        drop(state);
 739                        focused_window.handle_input(input);
 740                    }
 741                    wl_keyboard::KeyState::Released if !keysym.is_modifier_key() => {
 742                        let input = PlatformInput::KeyUp(KeyUpEvent {
 743                            keystroke: Keystroke::from_xkb(keymap_state, state.modifiers, keycode),
 744                        });
 745
 746                        state.repeat.current_keysym = None;
 747
 748                        drop(state);
 749                        focused_window.handle_input(input);
 750                    }
 751                    _ => {}
 752                }
 753            }
 754            _ => {}
 755        }
 756    }
 757}
 758
 759fn linux_button_to_gpui(button: u32) -> Option<MouseButton> {
 760    // These values are coming from <linux/input-event-codes.h>.
 761    const BTN_LEFT: u32 = 0x110;
 762    const BTN_RIGHT: u32 = 0x111;
 763    const BTN_MIDDLE: u32 = 0x112;
 764    const BTN_SIDE: u32 = 0x113;
 765    const BTN_EXTRA: u32 = 0x114;
 766    const BTN_FORWARD: u32 = 0x115;
 767    const BTN_BACK: u32 = 0x116;
 768
 769    Some(match button {
 770        BTN_LEFT => MouseButton::Left,
 771        BTN_RIGHT => MouseButton::Right,
 772        BTN_MIDDLE => MouseButton::Middle,
 773        BTN_BACK | BTN_SIDE => MouseButton::Navigate(NavigationDirection::Back),
 774        BTN_FORWARD | BTN_EXTRA => MouseButton::Navigate(NavigationDirection::Forward),
 775        _ => return None,
 776    })
 777}
 778
 779impl Dispatch<wl_pointer::WlPointer, ()> for WaylandClientStatePtr {
 780    fn event(
 781        this: &mut Self,
 782        wl_pointer: &wl_pointer::WlPointer,
 783        event: wl_pointer::Event,
 784        data: &(),
 785        conn: &Connection,
 786        qh: &QueueHandle<Self>,
 787    ) {
 788        let mut client = this.get_client();
 789        let mut state = client.borrow_mut();
 790        let cursor_icon_name = state.cursor_icon_name.clone();
 791
 792        match event {
 793            wl_pointer::Event::Enter {
 794                serial,
 795                surface,
 796                surface_x,
 797                surface_y,
 798                ..
 799            } => {
 800                state.mouse_location = Some(point(px(surface_x as f32), px(surface_y as f32)));
 801
 802                if let Some(window) = get_window(&mut state, &surface.id()) {
 803                    state.enter_token = Some(());
 804                    state.mouse_focused_window = Some(window.clone());
 805                    state.cursor.mark_dirty();
 806                    state.cursor.set_serial_id(serial);
 807                    state
 808                        .cursor
 809                        .set_icon(&wl_pointer, cursor_icon_name.as_str());
 810                    drop(state);
 811                    window.set_focused(true);
 812                }
 813            }
 814            wl_pointer::Event::Leave { surface, .. } => {
 815                if let Some(focused_window) = state.mouse_focused_window.clone() {
 816                    state.enter_token.take();
 817                    let input = PlatformInput::MouseExited(MouseExitEvent {
 818                        position: state.mouse_location.unwrap(),
 819                        pressed_button: state.button_pressed,
 820                        modifiers: state.modifiers,
 821                    });
 822                    state.mouse_focused_window = None;
 823                    state.mouse_location = None;
 824
 825                    drop(state);
 826                    focused_window.handle_input(input);
 827                    focused_window.set_focused(false);
 828                }
 829            }
 830            wl_pointer::Event::Motion {
 831                time,
 832                surface_x,
 833                surface_y,
 834                ..
 835            } => {
 836                if state.mouse_focused_window.is_none() {
 837                    return;
 838                }
 839                state.mouse_location = Some(point(px(surface_x as f32), px(surface_y as f32)));
 840
 841                if let Some(window) = state.mouse_focused_window.clone() {
 842                    let input = PlatformInput::MouseMove(MouseMoveEvent {
 843                        position: state.mouse_location.unwrap(),
 844                        pressed_button: state.button_pressed,
 845                        modifiers: state.modifiers,
 846                    });
 847                    drop(state);
 848                    window.handle_input(input);
 849                }
 850            }
 851            wl_pointer::Event::Button {
 852                button,
 853                state: WEnum::Value(button_state),
 854                ..
 855            } => {
 856                let button = linux_button_to_gpui(button);
 857                let Some(button) = button else { return };
 858                if state.mouse_focused_window.is_none() {
 859                    return;
 860                }
 861                match button_state {
 862                    wl_pointer::ButtonState::Pressed => {
 863                        let click_elapsed = state.click.last_click.elapsed();
 864
 865                        if click_elapsed < DOUBLE_CLICK_INTERVAL
 866                            && is_within_click_distance(
 867                                state.click.last_location,
 868                                state.mouse_location.unwrap(),
 869                            )
 870                        {
 871                            state.click.current_count += 1;
 872                        } else {
 873                            state.click.current_count = 1;
 874                        }
 875
 876                        state.click.last_click = Instant::now();
 877                        state.click.last_location = state.mouse_location.unwrap();
 878
 879                        state.button_pressed = Some(button);
 880
 881                        if let Some(window) = state.mouse_focused_window.clone() {
 882                            let input = PlatformInput::MouseDown(MouseDownEvent {
 883                                button,
 884                                position: state.mouse_location.unwrap(),
 885                                modifiers: state.modifiers,
 886                                click_count: state.click.current_count,
 887                                first_mouse: state.enter_token.take().is_some(),
 888                            });
 889                            drop(state);
 890                            window.handle_input(input);
 891                        }
 892                    }
 893                    wl_pointer::ButtonState::Released => {
 894                        state.button_pressed = None;
 895
 896                        if let Some(window) = state.mouse_focused_window.clone() {
 897                            let input = PlatformInput::MouseUp(MouseUpEvent {
 898                                button,
 899                                position: state.mouse_location.unwrap(),
 900                                modifiers: state.modifiers,
 901                                click_count: state.click.current_count,
 902                            });
 903                            drop(state);
 904                            window.handle_input(input);
 905                        }
 906                    }
 907                    _ => {}
 908                }
 909            }
 910
 911            // Axis Events
 912            wl_pointer::Event::AxisSource {
 913                axis_source: WEnum::Value(axis_source),
 914            } => {
 915                state.axis_source = axis_source;
 916            }
 917            wl_pointer::Event::Axis {
 918                time,
 919                axis: WEnum::Value(axis),
 920                value,
 921                ..
 922            } => {
 923                let axis_source = state.axis_source;
 924                let axis_modifier = match axis {
 925                    wl_pointer::Axis::VerticalScroll => state.vertical_modifier,
 926                    wl_pointer::Axis::HorizontalScroll => state.horizontal_modifier,
 927                    _ => 1.0,
 928                };
 929                let supports_relative_direction =
 930                    wl_pointer.version() >= wl_pointer::EVT_AXIS_RELATIVE_DIRECTION_SINCE;
 931                state.scroll_event_received = true;
 932                let scroll_delta = state
 933                    .continuous_scroll_delta
 934                    .get_or_insert(point(px(0.0), px(0.0)));
 935                // TODO: Make nice feeling kinetic scrolling that integrates with the platform's scroll settings
 936                let modifier = 3.0;
 937                match axis {
 938                    wl_pointer::Axis::VerticalScroll => {
 939                        scroll_delta.y += px(value as f32 * modifier * axis_modifier);
 940                    }
 941                    wl_pointer::Axis::HorizontalScroll => {
 942                        scroll_delta.x += px(value as f32 * modifier * axis_modifier);
 943                    }
 944                    _ => unreachable!(),
 945                }
 946            }
 947            wl_pointer::Event::AxisDiscrete {
 948                axis: WEnum::Value(axis),
 949                discrete,
 950            } => {
 951                state.scroll_event_received = true;
 952                let axis_modifier = match axis {
 953                    wl_pointer::Axis::VerticalScroll => state.vertical_modifier,
 954                    wl_pointer::Axis::HorizontalScroll => state.horizontal_modifier,
 955                    _ => 1.0,
 956                };
 957
 958                // TODO: Make nice feeling kinetic scrolling that integrates with the platform's scroll settings
 959                let modifier = 3.0;
 960
 961                let scroll_delta = state.discrete_scroll_delta.get_or_insert(point(0.0, 0.0));
 962                match axis {
 963                    wl_pointer::Axis::VerticalScroll => {
 964                        scroll_delta.y += discrete as f32 * axis_modifier * modifier;
 965                    }
 966                    wl_pointer::Axis::HorizontalScroll => {
 967                        scroll_delta.x += discrete as f32 * axis_modifier * modifier;
 968                    }
 969                    _ => unreachable!(),
 970                }
 971            }
 972            wl_pointer::Event::AxisRelativeDirection {
 973                axis: WEnum::Value(axis),
 974                direction: WEnum::Value(direction),
 975            } => match (axis, direction) {
 976                (wl_pointer::Axis::VerticalScroll, AxisRelativeDirection::Identical) => {
 977                    state.vertical_modifier = -1.0
 978                }
 979                (wl_pointer::Axis::VerticalScroll, AxisRelativeDirection::Inverted) => {
 980                    state.vertical_modifier = 1.0
 981                }
 982                (wl_pointer::Axis::HorizontalScroll, AxisRelativeDirection::Identical) => {
 983                    state.horizontal_modifier = -1.0
 984                }
 985                (wl_pointer::Axis::HorizontalScroll, AxisRelativeDirection::Inverted) => {
 986                    state.horizontal_modifier = 1.0
 987                }
 988                _ => unreachable!(),
 989            },
 990            wl_pointer::Event::AxisValue120 {
 991                axis: WEnum::Value(axis),
 992                value120,
 993            } => {
 994                state.scroll_event_received = true;
 995                let axis_modifier = match axis {
 996                    wl_pointer::Axis::VerticalScroll => state.vertical_modifier,
 997                    wl_pointer::Axis::HorizontalScroll => state.horizontal_modifier,
 998                    _ => unreachable!(),
 999                };
1000
1001                let scroll_delta = state.discrete_scroll_delta.get_or_insert(point(0.0, 0.0));
1002                let wheel_percent = value120 as f32 / 120.0;
1003                match axis {
1004                    wl_pointer::Axis::VerticalScroll => {
1005                        scroll_delta.y += wheel_percent * axis_modifier;
1006                    }
1007                    wl_pointer::Axis::HorizontalScroll => {
1008                        scroll_delta.x += wheel_percent * axis_modifier;
1009                    }
1010                    _ => unreachable!(),
1011                }
1012            }
1013            wl_pointer::Event::Frame => {
1014                if state.scroll_event_received {
1015                    state.scroll_event_received = false;
1016                    let continuous = state.continuous_scroll_delta.take();
1017                    let discrete = state.discrete_scroll_delta.take();
1018                    if let Some(continuous) = continuous {
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::Pixels(continuous),
1023                                modifiers: state.modifiers,
1024                                touch_phase: TouchPhase::Moved,
1025                            });
1026                            drop(state);
1027                            window.handle_input(input);
1028                        }
1029                    } else if let Some(discrete) = discrete {
1030                        if let Some(window) = state.mouse_focused_window.clone() {
1031                            let input = PlatformInput::ScrollWheel(ScrollWheelEvent {
1032                                position: state.mouse_location.unwrap(),
1033                                delta: ScrollDelta::Lines(discrete),
1034                                modifiers: state.modifiers,
1035                                touch_phase: TouchPhase::Moved,
1036                            });
1037                            drop(state);
1038                            window.handle_input(input);
1039                        }
1040                    }
1041                }
1042            }
1043            _ => {}
1044        }
1045    }
1046}
1047
1048impl Dispatch<wp_fractional_scale_v1::WpFractionalScaleV1, ObjectId> for WaylandClientStatePtr {
1049    fn event(
1050        this: &mut Self,
1051        _: &wp_fractional_scale_v1::WpFractionalScaleV1,
1052        event: <wp_fractional_scale_v1::WpFractionalScaleV1 as Proxy>::Event,
1053        surface_id: &ObjectId,
1054        _: &Connection,
1055        _: &QueueHandle<Self>,
1056    ) {
1057        let client = this.get_client();
1058        let mut state = client.borrow_mut();
1059
1060        let Some(window) = get_window(&mut state, surface_id) else {
1061            return;
1062        };
1063
1064        drop(state);
1065        window.handle_fractional_scale_event(event);
1066    }
1067}
1068
1069impl Dispatch<zxdg_toplevel_decoration_v1::ZxdgToplevelDecorationV1, ObjectId>
1070    for WaylandClientStatePtr
1071{
1072    fn event(
1073        this: &mut Self,
1074        _: &zxdg_toplevel_decoration_v1::ZxdgToplevelDecorationV1,
1075        event: zxdg_toplevel_decoration_v1::Event,
1076        surface_id: &ObjectId,
1077        _: &Connection,
1078        _: &QueueHandle<Self>,
1079    ) {
1080        let client = this.get_client();
1081        let mut state = client.borrow_mut();
1082        let Some(window) = get_window(&mut state, surface_id) else {
1083            return;
1084        };
1085
1086        drop(state);
1087        window.handle_toplevel_decoration_event(event);
1088    }
1089}