client.rs

  1use std::cell::RefCell;
  2use std::rc::Rc;
  3use std::sync::Arc;
  4use std::time::Duration;
  5
  6use calloop::timer::{TimeoutAction, Timer};
  7use calloop::LoopHandle;
  8use calloop_wayland_source::WaylandSource;
  9use copypasta::wayland_clipboard::{create_clipboards_from_external, Clipboard, Primary};
 10use copypasta::ClipboardProvider;
 11use wayland_backend::client::ObjectId;
 12use wayland_backend::protocol::WEnum;
 13use wayland_client::globals::{registry_queue_init, GlobalListContents};
 14use wayland_client::protocol::wl_callback::WlCallback;
 15use wayland_client::protocol::wl_pointer::AxisRelativeDirection;
 16use wayland_client::{
 17    delegate_noop,
 18    protocol::{
 19        wl_buffer, wl_callback, wl_compositor, wl_keyboard, wl_pointer, wl_registry, wl_seat,
 20        wl_shm, wl_shm_pool,
 21        wl_surface::{self, WlSurface},
 22    },
 23    Connection, Dispatch, Proxy, QueueHandle,
 24};
 25use wayland_protocols::wp::fractional_scale::v1::client::{
 26    wp_fractional_scale_manager_v1, wp_fractional_scale_v1,
 27};
 28use wayland_protocols::wp::viewporter::client::{wp_viewport, wp_viewporter};
 29use wayland_protocols::xdg::decoration::zv1::client::{
 30    zxdg_decoration_manager_v1, zxdg_toplevel_decoration_v1,
 31};
 32use wayland_protocols::xdg::shell::client::{xdg_surface, xdg_toplevel, xdg_wm_base};
 33use xkbcommon::xkb::ffi::XKB_KEYMAP_FORMAT_TEXT_V1;
 34use xkbcommon::xkb::{self, Keycode, KEYMAP_COMPILE_NO_FLAGS};
 35
 36use crate::platform::linux::client::Client;
 37use crate::platform::linux::wayland::cursor::Cursor;
 38use crate::platform::linux::wayland::window::{WaylandDecorationState, WaylandWindow};
 39use crate::platform::{LinuxPlatformInner, PlatformWindow};
 40use crate::{
 41    platform::linux::wayland::window::WaylandWindowState, AnyWindowHandle, CursorStyle, DisplayId,
 42    KeyDownEvent, KeyUpEvent, Keystroke, Modifiers, MouseButton, MouseDownEvent, MouseMoveEvent,
 43    MouseUpEvent, NavigationDirection, Pixels, PlatformDisplay, PlatformInput, Point, ScrollDelta,
 44    ScrollWheelEvent, TouchPhase, WindowOptions,
 45};
 46
 47/// Used to convert evdev scancode to xkb scancode
 48const MIN_KEYCODE: u32 = 8;
 49
 50pub(crate) struct WaylandClientStateInner {
 51    compositor: wl_compositor::WlCompositor,
 52    wm_base: xdg_wm_base::XdgWmBase,
 53    shm: wl_shm::WlShm,
 54    viewporter: Option<wp_viewporter::WpViewporter>,
 55    fractional_scale_manager: Option<wp_fractional_scale_manager_v1::WpFractionalScaleManagerV1>,
 56    decoration_manager: Option<zxdg_decoration_manager_v1::ZxdgDecorationManagerV1>,
 57    windows: Vec<(xdg_surface::XdgSurface, Rc<WaylandWindowState>)>,
 58    platform_inner: Rc<LinuxPlatformInner>,
 59    keymap_state: Option<xkb::State>,
 60    repeat: KeyRepeat,
 61    modifiers: Modifiers,
 62    scroll_direction: f64,
 63    mouse_location: Option<Point<Pixels>>,
 64    button_pressed: Option<MouseButton>,
 65    mouse_focused_window: Option<Rc<WaylandWindowState>>,
 66    keyboard_focused_window: Option<Rc<WaylandWindowState>>,
 67    loop_handle: Rc<LoopHandle<'static, ()>>,
 68}
 69
 70pub(crate) struct CursorState {
 71    cursor_icon_name: String,
 72    cursor: Option<Cursor>,
 73}
 74
 75#[derive(Clone)]
 76pub(crate) struct WaylandClientState {
 77    client_state_inner: Rc<RefCell<WaylandClientStateInner>>,
 78    cursor_state: Rc<RefCell<CursorState>>,
 79    clipboard: Rc<RefCell<Clipboard>>,
 80    primary: Rc<RefCell<Primary>>,
 81}
 82
 83pub(crate) struct KeyRepeat {
 84    characters_per_second: u32,
 85    delay: Duration,
 86    current_id: u64,
 87    current_keysym: Option<xkb::Keysym>,
 88}
 89
 90pub(crate) struct WaylandClient {
 91    platform_inner: Rc<LinuxPlatformInner>,
 92    state: WaylandClientState,
 93    qh: Arc<QueueHandle<WaylandClientState>>,
 94}
 95
 96const WL_SEAT_VERSION: u32 = 4;
 97
 98impl WaylandClient {
 99    pub(crate) fn new(linux_platform_inner: Rc<LinuxPlatformInner>) -> Self {
100        let conn = Connection::connect_to_env().unwrap();
101
102        let (globals, mut event_queue) = registry_queue_init::<WaylandClientState>(&conn).unwrap();
103        let qh = event_queue.handle();
104
105        globals.contents().with_list(|list| {
106            for global in list {
107                if global.interface == "wl_seat" {
108                    globals.registry().bind::<wl_seat::WlSeat, _, _>(
109                        global.name,
110                        WL_SEAT_VERSION,
111                        &qh,
112                        (),
113                    );
114                }
115            }
116        });
117
118        let display = conn.backend().display_ptr() as *mut std::ffi::c_void;
119        let (primary, clipboard) = unsafe { create_clipboards_from_external(display) };
120
121        let mut state_inner = Rc::new(RefCell::new(WaylandClientStateInner {
122            compositor: globals.bind(&qh, 1..=1, ()).unwrap(),
123            wm_base: globals.bind(&qh, 1..=1, ()).unwrap(),
124            shm: globals.bind(&qh, 1..=1, ()).unwrap(),
125            viewporter: globals.bind(&qh, 1..=1, ()).ok(),
126            fractional_scale_manager: globals.bind(&qh, 1..=1, ()).ok(),
127            decoration_manager: globals.bind(&qh, 1..=1, ()).ok(),
128            windows: Vec::new(),
129            platform_inner: Rc::clone(&linux_platform_inner),
130            keymap_state: None,
131            repeat: KeyRepeat {
132                characters_per_second: 16,
133                delay: Duration::from_millis(500),
134                current_id: 0,
135                current_keysym: None,
136            },
137            modifiers: Modifiers {
138                shift: false,
139                control: false,
140                alt: false,
141                function: false,
142                command: false,
143            },
144            scroll_direction: -1.0,
145            mouse_location: None,
146            button_pressed: None,
147            mouse_focused_window: None,
148            keyboard_focused_window: None,
149            loop_handle: Rc::clone(&linux_platform_inner.loop_handle),
150        }));
151
152        let mut cursor_state = Rc::new(RefCell::new(CursorState {
153            cursor_icon_name: "arrow".to_string(),
154            cursor: None,
155        }));
156
157        let source = WaylandSource::new(conn, event_queue);
158
159        let mut state = WaylandClientState {
160            client_state_inner: Rc::clone(&state_inner),
161            cursor_state: Rc::clone(&cursor_state),
162            clipboard: Rc::new(RefCell::new(clipboard)),
163            primary: Rc::new(RefCell::new(primary)),
164        };
165        let mut state_loop = state.clone();
166        linux_platform_inner
167            .loop_handle
168            .insert_source(source, move |_, queue, _| {
169                queue.dispatch_pending(&mut state_loop)
170            })
171            .unwrap();
172
173        Self {
174            platform_inner: linux_platform_inner,
175            state,
176            qh: Arc::new(qh),
177        }
178    }
179}
180
181impl Client for WaylandClient {
182    fn displays(&self) -> Vec<Rc<dyn PlatformDisplay>> {
183        Vec::new()
184    }
185
186    fn display(&self, id: DisplayId) -> Option<Rc<dyn PlatformDisplay>> {
187        unimplemented!()
188    }
189
190    fn open_window(
191        &self,
192        handle: AnyWindowHandle,
193        options: WindowOptions,
194    ) -> Box<dyn PlatformWindow> {
195        let mut state = self.state.client_state_inner.borrow_mut();
196
197        let wl_surface = state.compositor.create_surface(&self.qh, ());
198        let xdg_surface = state.wm_base.get_xdg_surface(&wl_surface, &self.qh, ());
199        let toplevel = xdg_surface.get_toplevel(&self.qh, ());
200        let wl_surface = Arc::new(wl_surface);
201
202        // Attempt to set up window decorations based on the requested configuration
203        //
204        // Note that wayland compositors may either not support decorations at all, or may
205        // support them but not allow clients to choose whether they are enabled or not.
206        // We attempt to account for these cases here.
207
208        if let Some(decoration_manager) = state.decoration_manager.as_ref() {
209            // The protocol for managing decorations is present at least, but that doesn't
210            // mean that the compositor will allow us to use it.
211
212            let decoration =
213                decoration_manager.get_toplevel_decoration(&toplevel, &self.qh, xdg_surface.id());
214
215            // todo(linux) - options.titlebar is lacking information required for wayland.
216            //                Especially, whether a titlebar is wanted in itself.
217            //
218            // Removing the titlebar also removes the entire window frame (ie. the ability to
219            // close, move and resize the window [snapping still works]). This needs additional
220            // handling in Zed, in order to implement drag handlers on a titlebar element.
221            //
222            // Since all of this handling is not present, we request server-side decorations
223            // for now as a stopgap solution.
224            decoration.set_mode(zxdg_toplevel_decoration_v1::Mode::ServerSide);
225        }
226
227        let viewport = state
228            .viewporter
229            .as_ref()
230            .map(|viewporter| viewporter.get_viewport(&wl_surface, &self.qh, ()));
231
232        wl_surface.frame(&self.qh, wl_surface.clone());
233        wl_surface.commit();
234
235        let window_state: Rc<WaylandWindowState> = Rc::new(WaylandWindowState::new(
236            wl_surface.clone(),
237            viewport,
238            Arc::new(toplevel),
239            options,
240        ));
241
242        if let Some(fractional_scale_manager) = state.fractional_scale_manager.as_ref() {
243            fractional_scale_manager.get_fractional_scale(&wl_surface, &self.qh, xdg_surface.id());
244        }
245
246        state.windows.push((xdg_surface, Rc::clone(&window_state)));
247        Box::new(WaylandWindow(window_state))
248    }
249
250    fn set_cursor_style(&self, style: CursorStyle) {
251        let cursor_icon_name = match style {
252            CursorStyle::Arrow => "arrow".to_string(),
253            CursorStyle::IBeam => "text".to_string(),
254            CursorStyle::Crosshair => "crosshair".to_string(),
255            CursorStyle::ClosedHand => "grabbing".to_string(),
256            CursorStyle::OpenHand => "openhand".to_string(),
257            CursorStyle::PointingHand => "hand".to_string(),
258            CursorStyle::ResizeLeft => "w-resize".to_string(),
259            CursorStyle::ResizeRight => "e-resize".to_string(),
260            CursorStyle::ResizeLeftRight => "ew-resize".to_string(),
261            CursorStyle::ResizeUp => "n-resize".to_string(),
262            CursorStyle::ResizeDown => "s-resize".to_string(),
263            CursorStyle::ResizeUpDown => "ns-resize".to_string(),
264            CursorStyle::DisappearingItem => "grabbing".to_string(), // todo!(linux) - couldn't find equivalent icon in linux
265            CursorStyle::IBeamCursorForVerticalLayout => "vertical-text".to_string(),
266            CursorStyle::OperationNotAllowed => "not-allowed".to_string(),
267            CursorStyle::DragLink => "dnd-link".to_string(),
268            CursorStyle::DragCopy => "dnd-copy".to_string(),
269            CursorStyle::ContextualMenu => "context-menu".to_string(),
270        };
271
272        let mut cursor_state = self.state.cursor_state.borrow_mut();
273        cursor_state.cursor_icon_name = cursor_icon_name;
274    }
275
276    fn get_clipboard(&self) -> Rc<RefCell<dyn ClipboardProvider>> {
277        self.state.clipboard.clone()
278    }
279
280    fn get_primary(&self) -> Rc<RefCell<dyn ClipboardProvider>> {
281        self.state.primary.clone()
282    }
283}
284
285impl Dispatch<wl_registry::WlRegistry, GlobalListContents> for WaylandClientState {
286    fn event(
287        state: &mut Self,
288        registry: &wl_registry::WlRegistry,
289        event: wl_registry::Event,
290        _: &GlobalListContents,
291        _: &Connection,
292        qh: &QueueHandle<Self>,
293    ) {
294        match event {
295            wl_registry::Event::Global {
296                name,
297                interface,
298                version: _,
299            } => {
300                if interface.as_str() == "wl_seat" {
301                    registry.bind::<wl_seat::WlSeat, _, _>(name, 4, qh, ());
302                }
303            }
304            wl_registry::Event::GlobalRemove { name: _ } => {}
305            _ => {}
306        }
307    }
308}
309
310delegate_noop!(WaylandClientState: ignore wl_compositor::WlCompositor);
311delegate_noop!(WaylandClientState: ignore wl_surface::WlSurface);
312delegate_noop!(WaylandClientState: ignore wl_shm::WlShm);
313delegate_noop!(WaylandClientState: ignore wl_shm_pool::WlShmPool);
314delegate_noop!(WaylandClientState: ignore wl_buffer::WlBuffer);
315delegate_noop!(WaylandClientState: ignore wp_fractional_scale_manager_v1::WpFractionalScaleManagerV1);
316delegate_noop!(WaylandClientState: ignore zxdg_decoration_manager_v1::ZxdgDecorationManagerV1);
317delegate_noop!(WaylandClientState: ignore wp_viewporter::WpViewporter);
318delegate_noop!(WaylandClientState: ignore wp_viewport::WpViewport);
319
320impl Dispatch<WlCallback, Arc<WlSurface>> for WaylandClientState {
321    fn event(
322        state: &mut Self,
323        _: &WlCallback,
324        event: wl_callback::Event,
325        surf: &Arc<WlSurface>,
326        _: &Connection,
327        qh: &QueueHandle<Self>,
328    ) {
329        let mut state = state.client_state_inner.borrow_mut();
330        if let wl_callback::Event::Done { .. } = event {
331            for window in &state.windows {
332                if window.1.surface.id() == surf.id() {
333                    window.1.surface.frame(qh, surf.clone());
334                    window.1.update();
335                    window.1.surface.commit();
336                }
337            }
338        }
339    }
340}
341
342impl Dispatch<xdg_surface::XdgSurface, ()> for WaylandClientState {
343    fn event(
344        state: &mut Self,
345        xdg_surface: &xdg_surface::XdgSurface,
346        event: xdg_surface::Event,
347        _: &(),
348        _: &Connection,
349        _: &QueueHandle<Self>,
350    ) {
351        let mut state = state.client_state_inner.borrow_mut();
352        if let xdg_surface::Event::Configure { serial, .. } = event {
353            xdg_surface.ack_configure(serial);
354            for window in &state.windows {
355                if &window.0 == xdg_surface {
356                    window.1.update();
357                    window.1.surface.commit();
358                    return;
359                }
360            }
361        }
362    }
363}
364
365impl Dispatch<xdg_toplevel::XdgToplevel, ()> for WaylandClientState {
366    fn event(
367        state: &mut Self,
368        xdg_toplevel: &xdg_toplevel::XdgToplevel,
369        event: <xdg_toplevel::XdgToplevel as Proxy>::Event,
370        _: &(),
371        _: &Connection,
372        _: &QueueHandle<Self>,
373    ) {
374        let mut state = state.client_state_inner.borrow_mut();
375        if let xdg_toplevel::Event::Configure {
376            width,
377            height,
378            states: _states,
379        } = event
380        {
381            if width == 0 || height == 0 {
382                return;
383            }
384            for window in &state.windows {
385                if window.1.toplevel.id() == xdg_toplevel.id() {
386                    window.1.resize(width, height);
387                    window.1.surface.commit();
388                    return;
389                }
390            }
391        } else if let xdg_toplevel::Event::Close = event {
392            state.windows.retain(|(_, window)| {
393                if window.toplevel.id() == xdg_toplevel.id() {
394                    window.toplevel.destroy();
395                    false
396                } else {
397                    true
398                }
399            });
400            state.platform_inner.loop_signal.stop();
401        }
402    }
403}
404
405impl Dispatch<xdg_wm_base::XdgWmBase, ()> for WaylandClientState {
406    fn event(
407        _: &mut Self,
408        wm_base: &xdg_wm_base::XdgWmBase,
409        event: <xdg_wm_base::XdgWmBase as Proxy>::Event,
410        _: &(),
411        _: &Connection,
412        _: &QueueHandle<Self>,
413    ) {
414        if let xdg_wm_base::Event::Ping { serial } = event {
415            wm_base.pong(serial);
416        }
417    }
418}
419
420impl Dispatch<wl_seat::WlSeat, ()> for WaylandClientState {
421    fn event(
422        state: &mut Self,
423        seat: &wl_seat::WlSeat,
424        event: wl_seat::Event,
425        data: &(),
426        conn: &Connection,
427        qh: &QueueHandle<Self>,
428    ) {
429        if let wl_seat::Event::Capabilities {
430            capabilities: WEnum::Value(capabilities),
431        } = event
432        {
433            if capabilities.contains(wl_seat::Capability::Keyboard) {
434                seat.get_keyboard(qh, ());
435            }
436            if capabilities.contains(wl_seat::Capability::Pointer) {
437                seat.get_pointer(qh, ());
438            }
439        }
440    }
441}
442
443impl Dispatch<wl_keyboard::WlKeyboard, ()> for WaylandClientState {
444    fn event(
445        this: &mut Self,
446        keyboard: &wl_keyboard::WlKeyboard,
447        event: wl_keyboard::Event,
448        data: &(),
449        conn: &Connection,
450        qh: &QueueHandle<Self>,
451    ) {
452        let mut state = this.client_state_inner.borrow_mut();
453        match event {
454            wl_keyboard::Event::RepeatInfo { rate, delay } => {
455                state.repeat.characters_per_second = rate as u32;
456                state.repeat.delay = Duration::from_millis(delay as u64);
457            }
458            wl_keyboard::Event::Keymap {
459                format: WEnum::Value(format),
460                fd,
461                size,
462                ..
463            } => {
464                assert_eq!(
465                    format,
466                    wl_keyboard::KeymapFormat::XkbV1,
467                    "Unsupported keymap format"
468                );
469                let keymap = unsafe {
470                    xkb::Keymap::new_from_fd(
471                        &xkb::Context::new(xkb::CONTEXT_NO_FLAGS),
472                        fd,
473                        size as usize,
474                        XKB_KEYMAP_FORMAT_TEXT_V1,
475                        KEYMAP_COMPILE_NO_FLAGS,
476                    )
477                    .unwrap()
478                }
479                .unwrap();
480                state.keymap_state = Some(xkb::State::new(&keymap));
481            }
482            wl_keyboard::Event::Enter { surface, .. } => {
483                state.keyboard_focused_window = state
484                    .windows
485                    .iter()
486                    .find(|&w| w.1.surface.id() == surface.id())
487                    .map(|w| w.1.clone());
488
489                if let Some(window) = &state.keyboard_focused_window {
490                    window.set_focused(true);
491                }
492            }
493            wl_keyboard::Event::Leave { surface, .. } => {
494                let keyboard_focused_window = state
495                    .windows
496                    .iter()
497                    .find(|&w| w.1.surface.id() == surface.id())
498                    .map(|w| w.1.clone());
499
500                if let Some(window) = keyboard_focused_window {
501                    window.set_focused(false);
502                }
503
504                state.keyboard_focused_window = None;
505            }
506            wl_keyboard::Event::Modifiers {
507                mods_depressed,
508                mods_latched,
509                mods_locked,
510                group,
511                ..
512            } => {
513                let keymap_state = state.keymap_state.as_mut().unwrap();
514                keymap_state.update_mask(mods_depressed, mods_latched, mods_locked, 0, 0, group);
515
516                let shift =
517                    keymap_state.mod_name_is_active(xkb::MOD_NAME_SHIFT, xkb::STATE_MODS_EFFECTIVE);
518                let alt =
519                    keymap_state.mod_name_is_active(xkb::MOD_NAME_ALT, xkb::STATE_MODS_EFFECTIVE);
520                let control =
521                    keymap_state.mod_name_is_active(xkb::MOD_NAME_CTRL, xkb::STATE_MODS_EFFECTIVE);
522                let command =
523                    keymap_state.mod_name_is_active(xkb::MOD_NAME_LOGO, xkb::STATE_MODS_EFFECTIVE);
524
525                state.modifiers.shift = shift;
526                state.modifiers.alt = alt;
527                state.modifiers.control = control;
528                state.modifiers.command = command;
529            }
530            wl_keyboard::Event::Key {
531                key,
532                state: WEnum::Value(key_state),
533                ..
534            } => {
535                let focused_window = &state.keyboard_focused_window;
536                let Some(focused_window) = focused_window else {
537                    return;
538                };
539
540                let keymap_state = state.keymap_state.as_ref().unwrap();
541                let keycode = Keycode::from(key + MIN_KEYCODE);
542                let keysym = keymap_state.key_get_one_sym(keycode);
543
544                match key_state {
545                    wl_keyboard::KeyState::Pressed => {
546                        let input = PlatformInput::KeyDown(KeyDownEvent {
547                            keystroke: Keystroke::from_xkb(keymap_state, state.modifiers, keycode),
548                            is_held: false, // todo(linux)
549                        });
550
551                        focused_window.handle_input(input.clone());
552
553                        if !keysym.is_modifier_key() {
554                            state.repeat.current_id += 1;
555                            state.repeat.current_keysym = Some(keysym);
556
557                            let rate = state.repeat.characters_per_second;
558                            let delay = state.repeat.delay;
559                            let id = state.repeat.current_id;
560                            let this = this.clone();
561
562                            let timer = Timer::from_duration(delay);
563                            let state_ = Rc::clone(&this.client_state_inner);
564                            state
565                                .loop_handle
566                                .insert_source(timer, move |event, _metadata, shared_data| {
567                                    let state_ = state_.borrow_mut();
568                                    let is_repeating = id == state_.repeat.current_id
569                                        && state_.repeat.current_keysym.is_some()
570                                        && state_.keyboard_focused_window.is_some();
571
572                                    if !is_repeating {
573                                        return TimeoutAction::Drop;
574                                    }
575
576                                    let focused_window =
577                                        state_.keyboard_focused_window.as_ref().unwrap().clone();
578
579                                    drop(state_);
580
581                                    focused_window.handle_input(input.clone());
582
583                                    TimeoutAction::ToDuration(Duration::from_secs(1) / rate)
584                                })
585                                .unwrap();
586                        }
587                    }
588                    wl_keyboard::KeyState::Released => {
589                        focused_window.handle_input(PlatformInput::KeyUp(KeyUpEvent {
590                            keystroke: Keystroke::from_xkb(keymap_state, state.modifiers, keycode),
591                        }));
592
593                        if !keysym.is_modifier_key() {
594                            state.repeat.current_keysym = None;
595                        }
596                    }
597                    _ => {}
598                }
599            }
600            _ => {}
601        }
602    }
603}
604
605fn linux_button_to_gpui(button: u32) -> Option<MouseButton> {
606    // These values are coming from <linux/input-event-codes.h>.
607    const BTN_LEFT: u32 = 0x110;
608    const BTN_RIGHT: u32 = 0x111;
609    const BTN_MIDDLE: u32 = 0x112;
610    const BTN_SIDE: u32 = 0x113;
611    const BTN_EXTRA: u32 = 0x114;
612    const BTN_FORWARD: u32 = 0x115;
613    const BTN_BACK: u32 = 0x116;
614
615    Some(match button {
616        BTN_LEFT => MouseButton::Left,
617        BTN_RIGHT => MouseButton::Right,
618        BTN_MIDDLE => MouseButton::Middle,
619        BTN_BACK | BTN_SIDE => MouseButton::Navigate(NavigationDirection::Back),
620        BTN_FORWARD | BTN_EXTRA => MouseButton::Navigate(NavigationDirection::Forward),
621        _ => return None,
622    })
623}
624
625impl Dispatch<wl_pointer::WlPointer, ()> for WaylandClientState {
626    fn event(
627        state: &mut Self,
628        wl_pointer: &wl_pointer::WlPointer,
629        event: wl_pointer::Event,
630        data: &(),
631        conn: &Connection,
632        qh: &QueueHandle<Self>,
633    ) {
634        let mut cursor_state = state.cursor_state.borrow_mut();
635        let mut state = state.client_state_inner.borrow_mut();
636
637        if cursor_state.cursor.is_none() {
638            cursor_state.cursor = Some(Cursor::new(&conn, &state.compositor, &qh, &state.shm, 24));
639        }
640        let cursor_icon_name = cursor_state.cursor_icon_name.clone();
641        let mut cursor: &mut Cursor = cursor_state.cursor.as_mut().unwrap();
642
643        match event {
644            wl_pointer::Event::Enter {
645                serial,
646                surface,
647                surface_x,
648                surface_y,
649                ..
650            } => {
651                let mut mouse_focused_window = None;
652                for window in &state.windows {
653                    if window.1.surface.id() == surface.id() {
654                        window.1.set_focused(true);
655                        mouse_focused_window = Some(Rc::clone(&window.1));
656                    }
657                }
658                if mouse_focused_window.is_some() {
659                    state.mouse_focused_window = mouse_focused_window;
660                    cursor.set_serial_id(serial);
661                    cursor.set_icon(&wl_pointer, cursor_icon_name);
662                }
663
664                state.mouse_location = Some(Point {
665                    x: Pixels::from(surface_x),
666                    y: Pixels::from(surface_y),
667                });
668            }
669            wl_pointer::Event::Motion {
670                time,
671                surface_x,
672                surface_y,
673                ..
674            } => {
675                if state.mouse_focused_window.is_none() {
676                    return;
677                }
678                state.mouse_location = Some(Point {
679                    x: Pixels::from(surface_x),
680                    y: Pixels::from(surface_y),
681                });
682                state.mouse_focused_window.as_ref().unwrap().handle_input(
683                    PlatformInput::MouseMove(MouseMoveEvent {
684                        position: state.mouse_location.unwrap(),
685                        pressed_button: state.button_pressed,
686                        modifiers: state.modifiers,
687                    }),
688                );
689                cursor.set_icon(&wl_pointer, cursor_icon_name);
690            }
691            wl_pointer::Event::Button {
692                button,
693                state: WEnum::Value(button_state),
694                ..
695            } => {
696                let button = linux_button_to_gpui(button);
697                let Some(button) = button else { return };
698                if state.mouse_focused_window.is_none() || state.mouse_location.is_none() {
699                    return;
700                }
701                match button_state {
702                    wl_pointer::ButtonState::Pressed => {
703                        state.button_pressed = Some(button);
704                        state.mouse_focused_window.as_ref().unwrap().handle_input(
705                            PlatformInput::MouseDown(MouseDownEvent {
706                                button,
707                                position: state.mouse_location.unwrap(),
708                                modifiers: state.modifiers,
709                                click_count: 1,
710                            }),
711                        );
712                    }
713                    wl_pointer::ButtonState::Released => {
714                        state.button_pressed = None;
715                        state.mouse_focused_window.as_ref().unwrap().handle_input(
716                            PlatformInput::MouseUp(MouseUpEvent {
717                                button,
718                                position: state.mouse_location.unwrap(),
719                                modifiers: Modifiers::default(),
720                                click_count: 1,
721                            }),
722                        );
723                    }
724                    _ => {}
725                }
726            }
727            wl_pointer::Event::AxisRelativeDirection {
728                direction: WEnum::Value(direction),
729                ..
730            } => {
731                state.scroll_direction = match direction {
732                    AxisRelativeDirection::Identical => -1.0,
733                    AxisRelativeDirection::Inverted => 1.0,
734                    _ => -1.0,
735                }
736            }
737            wl_pointer::Event::Axis {
738                time,
739                axis: WEnum::Value(axis),
740                value,
741                ..
742            } => {
743                let focused_window = &state.mouse_focused_window;
744                let mouse_location = &state.mouse_location;
745                if let (Some(focused_window), Some(mouse_location)) =
746                    (focused_window, mouse_location)
747                {
748                    let value = value * state.scroll_direction;
749                    focused_window.handle_input(PlatformInput::ScrollWheel(ScrollWheelEvent {
750                        position: *mouse_location,
751                        delta: match axis {
752                            wl_pointer::Axis::VerticalScroll => {
753                                ScrollDelta::Pixels(Point::new(Pixels(0.0), Pixels(value as f32)))
754                            }
755                            wl_pointer::Axis::HorizontalScroll => {
756                                ScrollDelta::Pixels(Point::new(Pixels(value as f32), Pixels(0.0)))
757                            }
758                            _ => unimplemented!(),
759                        },
760                        modifiers: state.modifiers,
761                        touch_phase: TouchPhase::Started,
762                    }))
763                }
764            }
765            wl_pointer::Event::Leave { surface, .. } => {
766                let focused_window = &state.mouse_focused_window;
767                if let Some(focused_window) = focused_window {
768                    focused_window.handle_input(PlatformInput::MouseMove(MouseMoveEvent {
769                        position: Point::<Pixels>::default(),
770                        pressed_button: None,
771                        modifiers: Modifiers::default(),
772                    }));
773                    focused_window.set_focused(false);
774                }
775                state.mouse_focused_window = None;
776                state.mouse_location = None;
777            }
778            _ => {}
779        }
780    }
781}
782
783impl Dispatch<wp_fractional_scale_v1::WpFractionalScaleV1, ObjectId> for WaylandClientState {
784    fn event(
785        state: &mut Self,
786        _: &wp_fractional_scale_v1::WpFractionalScaleV1,
787        event: <wp_fractional_scale_v1::WpFractionalScaleV1 as Proxy>::Event,
788        id: &ObjectId,
789        _: &Connection,
790        _: &QueueHandle<Self>,
791    ) {
792        let mut state = state.client_state_inner.borrow_mut();
793        if let wp_fractional_scale_v1::Event::PreferredScale { scale, .. } = event {
794            for window in &state.windows {
795                if window.0.id() == *id {
796                    window.1.rescale(scale as f32 / 120.0);
797                    return;
798                }
799            }
800        }
801    }
802}
803
804impl Dispatch<zxdg_toplevel_decoration_v1::ZxdgToplevelDecorationV1, ObjectId>
805    for WaylandClientState
806{
807    fn event(
808        state: &mut Self,
809        _: &zxdg_toplevel_decoration_v1::ZxdgToplevelDecorationV1,
810        event: zxdg_toplevel_decoration_v1::Event,
811        surface_id: &ObjectId,
812        _: &Connection,
813        _: &QueueHandle<Self>,
814    ) {
815        let mut state = state.client_state_inner.borrow_mut();
816        if let zxdg_toplevel_decoration_v1::Event::Configure { mode, .. } = event {
817            for window in &state.windows {
818                if window.0.id() == *surface_id {
819                    match mode {
820                        WEnum::Value(zxdg_toplevel_decoration_v1::Mode::ServerSide) => {
821                            window
822                                .1
823                                .set_decoration_state(WaylandDecorationState::Server);
824                        }
825                        WEnum::Value(zxdg_toplevel_decoration_v1::Mode::ClientSide) => {
826                            window
827                                .1
828                                .set_decoration_state(WaylandDecorationState::Client);
829                        }
830                        WEnum::Value(_) => {
831                            log::warn!("Unknown decoration mode");
832                        }
833                        WEnum::Unknown(v) => {
834                            log::warn!("Unknown decoration mode: {}", v);
835                        }
836                    }
837                    return;
838                }
839            }
840        }
841    }
842}