2097 lines · 77.6 KB · Rust
RawDownload
use std::{    cell::{RefCell, RefMut},    hash::Hash,    os::fd::{AsRawFd, BorrowedFd},    path::PathBuf,    rc::{Rc, Weak},    time::{Duration, Instant},}; use calloop::{    timer::{TimeoutAction, Timer},    EventLoop, LoopHandle,};use calloop_wayland_source::WaylandSource;use collections::HashMap;use filedescriptor::Pipe; use http_client::Url;use smallvec::SmallVec;use util::ResultExt;use wayland_backend::client::ObjectId;use wayland_backend::protocol::WEnum;use wayland_client::event_created_child;use wayland_client::globals::{registry_queue_init, GlobalList, GlobalListContents};use wayland_client::protocol::wl_callback::{self, WlCallback};use wayland_client::protocol::wl_data_device_manager::DndAction;use wayland_client::protocol::wl_data_offer::WlDataOffer;use wayland_client::protocol::wl_pointer::AxisSource;use wayland_client::protocol::{    wl_data_device, wl_data_device_manager, wl_data_offer, wl_data_source, wl_output, wl_region,};use wayland_client::{    delegate_noop,    protocol::{        wl_buffer, wl_compositor, wl_keyboard, wl_pointer, wl_registry, wl_seat, wl_shm,        wl_shm_pool, wl_surface,    },    Connection, Dispatch, Proxy, QueueHandle,};use wayland_protocols::wp::cursor_shape::v1::client::{    wp_cursor_shape_device_v1, wp_cursor_shape_manager_v1,};use wayland_protocols::wp::fractional_scale::v1::client::{    wp_fractional_scale_manager_v1, wp_fractional_scale_v1,};use wayland_protocols::wp::primary_selection::zv1::client::zwp_primary_selection_offer_v1::{    self, ZwpPrimarySelectionOfferV1,};use wayland_protocols::wp::primary_selection::zv1::client::{    zwp_primary_selection_device_manager_v1, zwp_primary_selection_device_v1,    zwp_primary_selection_source_v1,};use wayland_protocols::wp::text_input::zv3::client::zwp_text_input_v3::{    ContentHint, ContentPurpose,};use wayland_protocols::wp::text_input::zv3::client::{    zwp_text_input_manager_v3, zwp_text_input_v3,};use wayland_protocols::wp::viewporter::client::{wp_viewport, wp_viewporter};use wayland_protocols::xdg::activation::v1::client::{xdg_activation_token_v1, xdg_activation_v1};use wayland_protocols::xdg::decoration::zv1::client::{    zxdg_decoration_manager_v1, zxdg_toplevel_decoration_v1,};use wayland_protocols::xdg::shell::client::{xdg_surface, xdg_toplevel, xdg_wm_base};use wayland_protocols_plasma::blur::client::{org_kde_kwin_blur, org_kde_kwin_blur_manager};use xkbcommon::xkb::ffi::XKB_KEYMAP_FORMAT_TEXT_V1;use xkbcommon::xkb::{self, Keycode, KEYMAP_COMPILE_NO_FLAGS}; use super::display::WaylandDisplay;use super::window::{ImeInput, WaylandWindowStatePtr}; use crate::platform::linux::{    get_xkb_compose_state, is_within_click_distance, open_uri_internal, read_fd,    reveal_path_internal,    wayland::{        clipboard::{Clipboard, DataOffer, FILE_LIST_MIME_TYPE, TEXT_MIME_TYPE},        cursor::Cursor,        serial::{SerialKind, SerialTracker},        window::WaylandWindow,    },    xdg_desktop_portal::{Event as XDPEvent, XDPEventSource},    LinuxClient,};use crate::platform::{blade::BladeContext, PlatformWindow};use crate::{    point, px, size, AnyWindowHandle, Bounds, CursorStyle, DevicePixels, DisplayId, FileDropEvent,    ForegroundExecutor, KeyDownEvent, KeyUpEvent, Keystroke, LinuxCommon, Modifiers,    ModifiersChangedEvent, MouseButton, MouseDownEvent, MouseExitEvent, MouseMoveEvent,    MouseUpEvent, NavigationDirection, Pixels, PlatformDisplay, PlatformInput, Point, ScaledPixels,    ScrollDelta, ScrollWheelEvent, Size, TouchPhase, WindowParams, DOUBLE_CLICK_INTERVAL,    SCROLL_LINES,}; /// Used to convert evdev scancode to xkb scancodeconst MIN_KEYCODE: u32 = 8; #[derive(Clone)]pub struct Globals {    pub qh: QueueHandle<WaylandClientStatePtr>,    pub activation: Option<xdg_activation_v1::XdgActivationV1>,    pub compositor: wl_compositor::WlCompositor,    pub cursor_shape_manager: Option<wp_cursor_shape_manager_v1::WpCursorShapeManagerV1>,    pub data_device_manager: Option<wl_data_device_manager::WlDataDeviceManager>,    pub primary_selection_manager:        Option<zwp_primary_selection_device_manager_v1::ZwpPrimarySelectionDeviceManagerV1>,    pub wm_base: xdg_wm_base::XdgWmBase,    pub shm: wl_shm::WlShm,    pub seat: wl_seat::WlSeat,    pub viewporter: Option<wp_viewporter::WpViewporter>,    pub fractional_scale_manager:        Option<wp_fractional_scale_manager_v1::WpFractionalScaleManagerV1>,    pub decoration_manager: Option<zxdg_decoration_manager_v1::ZxdgDecorationManagerV1>,    pub blur_manager: Option<org_kde_kwin_blur_manager::OrgKdeKwinBlurManager>,    pub text_input_manager: Option<zwp_text_input_manager_v3::ZwpTextInputManagerV3>,    pub executor: ForegroundExecutor,} impl Globals {    fn new(        globals: GlobalList,        executor: ForegroundExecutor,        qh: QueueHandle<WaylandClientStatePtr>,        seat: wl_seat::WlSeat,    ) -> Self {        Globals {            activation: globals.bind(&qh, 1..=1, ()).ok(),            compositor: globals                .bind(                    &qh,                    wl_surface::REQ_SET_BUFFER_SCALE_SINCE                        ..=wl_surface::EVT_PREFERRED_BUFFER_SCALE_SINCE,                    (),                )                .unwrap(),            cursor_shape_manager: globals.bind(&qh, 1..=1, ()).ok(),            data_device_manager: globals                .bind(                    &qh,                    WL_DATA_DEVICE_MANAGER_VERSION..=WL_DATA_DEVICE_MANAGER_VERSION,                    (),                )                .ok(),            primary_selection_manager: globals.bind(&qh, 1..=1, ()).ok(),            shm: globals.bind(&qh, 1..=1, ()).unwrap(),            seat,            wm_base: globals.bind(&qh, 2..=5, ()).unwrap(),            viewporter: globals.bind(&qh, 1..=1, ()).ok(),            fractional_scale_manager: globals.bind(&qh, 1..=1, ()).ok(),            decoration_manager: globals.bind(&qh, 1..=1, ()).ok(),            blur_manager: globals.bind(&qh, 1..=1, ()).ok(),            text_input_manager: globals.bind(&qh, 1..=1, ()).ok(),            executor,            qh,        }    }} #[derive(Default, Debug, Clone, PartialEq, Eq, Hash)]pub struct InProgressOutput {    name: Option<String>,    scale: Option<i32>,    position: Option<Point<DevicePixels>>,    size: Option<Size<DevicePixels>>,} impl InProgressOutput {    fn complete(&self) -> Option<Output> {        if let Some((position, size)) = self.position.zip(self.size) {            let scale = self.scale.unwrap_or(1);            Some(Output {                name: self.name.clone(),                scale,                bounds: Bounds::new(position, size),            })        } else {            None        }    }} #[derive(Debug, Clone, Eq, PartialEq, Hash)]pub struct Output {    pub name: Option<String>,    pub scale: i32,    pub bounds: Bounds<DevicePixels>,} pub(crate) struct WaylandClientState {    serial_tracker: SerialTracker,    globals: Globals,    gpu_context: BladeContext,    wl_seat: wl_seat::WlSeat, // TODO: Multi seat support    wl_pointer: Option<wl_pointer::WlPointer>,    wl_keyboard: Option<wl_keyboard::WlKeyboard>,    cursor_shape_device: Option<wp_cursor_shape_device_v1::WpCursorShapeDeviceV1>,    data_device: Option<wl_data_device::WlDataDevice>,    primary_selection: Option<zwp_primary_selection_device_v1::ZwpPrimarySelectionDeviceV1>,    text_input: Option<zwp_text_input_v3::ZwpTextInputV3>,    pre_edit_text: Option<String>,    ime_pre_edit: Option<String>,    composing: bool,    // Surface to Window mapping    windows: HashMap<ObjectId, WaylandWindowStatePtr>,    // Output to scale mapping    outputs: HashMap<ObjectId, Output>,    in_progress_outputs: HashMap<ObjectId, InProgressOutput>,    keymap_state: Option<xkb::State>,    compose_state: Option<xkb::compose::State>,    drag: DragState,    click: ClickState,    repeat: KeyRepeat,    pub modifiers: Modifiers,    axis_source: AxisSource,    pub mouse_location: Option<Point<Pixels>>,    continuous_scroll_delta: Option<Point<Pixels>>,    discrete_scroll_delta: Option<Point<f32>>,    vertical_modifier: f32,    horizontal_modifier: f32,    scroll_event_received: bool,    enter_token: Option<()>,    button_pressed: Option<MouseButton>,    mouse_focused_window: Option<WaylandWindowStatePtr>,    keyboard_focused_window: Option<WaylandWindowStatePtr>,    loop_handle: LoopHandle<'static, WaylandClientStatePtr>,    cursor_style: Option<CursorStyle>,    clipboard: Clipboard,    data_offers: Vec<DataOffer<WlDataOffer>>,    primary_data_offer: Option<DataOffer<ZwpPrimarySelectionOfferV1>>,    cursor: Cursor,    pending_activation: Option<PendingActivation>,    event_loop: Option<EventLoop<'static, WaylandClientStatePtr>>,    common: LinuxCommon,} pub struct DragState {    data_offer: Option<wl_data_offer::WlDataOffer>,    window: Option<WaylandWindowStatePtr>,    position: Point<Pixels>,} pub struct ClickState {    last_mouse_button: Option<MouseButton>,    last_click: Instant,    last_location: Point<Pixels>,    current_count: usize,} pub(crate) struct KeyRepeat {    characters_per_second: u32,    delay: Duration,    current_id: u64,    current_keycode: Option<xkb::Keycode>,} pub(crate) enum PendingActivation {    /// URI to open in the web browser.    Uri(String),    /// Path to open in the file explorer.    Path(PathBuf),    /// A window from ourselves to raise.    Window(ObjectId),} /// This struct is required to conform to Rust's orphan rules, so we can dispatch on the state but hand the/// window to GPUI.#[derive(Clone)]pub struct WaylandClientStatePtr(Weak<RefCell<WaylandClientState>>); impl WaylandClientStatePtr {    pub fn get_client(&self) -> Rc<RefCell<WaylandClientState>> {        self.0            .upgrade()            .expect("The pointer should always be valid when dispatching in wayland")    }     pub fn get_serial(&self, kind: SerialKind) -> u32 {        self.0.upgrade().unwrap().borrow().serial_tracker.get(kind)    }     pub fn set_pending_activation(&self, window: ObjectId) {        self.0.upgrade().unwrap().borrow_mut().pending_activation =            Some(PendingActivation::Window(window));    }     pub fn enable_ime(&self) {        let client = self.get_client();        let mut state = client.borrow_mut();        let Some(mut text_input) = state.text_input.take() else {            return;        };         text_input.enable();        text_input.set_content_type(ContentHint::None, ContentPurpose::Normal);        if let Some(window) = state.keyboard_focused_window.clone() {            drop(state);            if let Some(area) = window.get_ime_area() {                text_input.set_cursor_rectangle(                    area.origin.x.0 as i32,                    area.origin.y.0 as i32,                    area.size.width.0 as i32,                    area.size.height.0 as i32,                );            }            state = client.borrow_mut();        }        text_input.commit();        state.text_input = Some(text_input);    }     pub fn disable_ime(&self) {        let client = self.get_client();        let mut state = client.borrow_mut();        state.composing = false;        if let Some(text_input) = &state.text_input {            text_input.disable();            text_input.commit();        }    }     pub fn update_ime_position(&self, bounds: Bounds<ScaledPixels>) {        let client = self.get_client();        let mut state = client.borrow_mut();        if state.composing || state.text_input.is_none() || state.pre_edit_text.is_some() {            return;        }         let text_input = state.text_input.as_ref().unwrap();        text_input.set_cursor_rectangle(            bounds.origin.x.0 as i32,            bounds.origin.y.0 as i32,            bounds.size.width.0 as i32,            bounds.size.height.0 as i32,        );        text_input.commit();    }     pub fn drop_window(&self, surface_id: &ObjectId) {        let mut client = self.get_client();        let mut state = client.borrow_mut();        let closed_window = state.windows.remove(surface_id).unwrap();        if let Some(window) = state.mouse_focused_window.take() {            if !window.ptr_eq(&closed_window) {                state.mouse_focused_window = Some(window);            }        }        if let Some(window) = state.keyboard_focused_window.take() {            if !window.ptr_eq(&closed_window) {                state.keyboard_focused_window = Some(window);            }        }        if state.windows.is_empty() {            state.common.signal.stop();        }    }} #[derive(Clone)]pub struct WaylandClient(Rc<RefCell<WaylandClientState>>); impl Drop for WaylandClient {    fn drop(&mut self) {        let mut state = self.0.borrow_mut();        state.windows.clear();         if let Some(wl_pointer) = &state.wl_pointer {            wl_pointer.release();        }        if let Some(cursor_shape_device) = &state.cursor_shape_device {            cursor_shape_device.destroy();        }        if let Some(data_device) = &state.data_device {            data_device.release();        }        if let Some(text_input) = &state.text_input {            text_input.destroy();        }    }} const WL_DATA_DEVICE_MANAGER_VERSION: u32 = 3; fn wl_seat_version(version: u32) -> u32 {    // We rely on the wl_pointer.frame event    const WL_SEAT_MIN_VERSION: u32 = 5;    const WL_SEAT_MAX_VERSION: u32 = 9;     if version < WL_SEAT_MIN_VERSION {        panic!(            "wl_seat below required version: {} < {}",            version, WL_SEAT_MIN_VERSION        );    }     version.clamp(WL_SEAT_MIN_VERSION, WL_SEAT_MAX_VERSION)} fn wl_output_version(version: u32) -> u32 {    const WL_OUTPUT_MIN_VERSION: u32 = 2;    const WL_OUTPUT_MAX_VERSION: u32 = 4;     if version < WL_OUTPUT_MIN_VERSION {        panic!(            "wl_output below required version: {} < {}",            version, WL_OUTPUT_MIN_VERSION        );    }     version.clamp(WL_OUTPUT_MIN_VERSION, WL_OUTPUT_MAX_VERSION)} impl WaylandClient {    pub(crate) fn new() -> Self {        let conn = Connection::connect_to_env().unwrap();         let (globals, mut event_queue) =            registry_queue_init::<WaylandClientStatePtr>(&conn).unwrap();        let qh = event_queue.handle();         let mut seat: Option<wl_seat::WlSeat> = None;        #[allow(clippy::mutable_key_type)]        let mut in_progress_outputs = HashMap::default();        globals.contents().with_list(|list| {            for global in list {                match &global.interface[..] {                    "wl_seat" => {                        seat = Some(globals.registry().bind::<wl_seat::WlSeat, _, _>(                            global.name,                            wl_seat_version(global.version),                            &qh,                            (),                        ));                    }                    "wl_output" => {                        let output = globals.registry().bind::<wl_output::WlOutput, _, _>(                            global.name,                            wl_output_version(global.version),                            &qh,                            (),                        );                        in_progress_outputs.insert(output.id(), InProgressOutput::default());                    }                    _ => {}                }            }        });         let event_loop = EventLoop::<WaylandClientStatePtr>::try_new().unwrap();         let (common, main_receiver) = LinuxCommon::new(event_loop.get_signal());         let handle = event_loop.handle();        handle            .insert_source(main_receiver, {                let handle = handle.clone();                move |event, _, _: &mut WaylandClientStatePtr| {                    if let calloop::channel::Event::Msg(runnable) = event {                        handle.insert_idle(|_| {                            runnable.run();                        });                    }                }            })            .unwrap();         let gpu_context = BladeContext::new().expect("Unable to init GPU context");         let seat = seat.unwrap();        let globals = Globals::new(            globals,            common.foreground_executor.clone(),            qh.clone(),            seat.clone(),        );         let data_device = globals            .data_device_manager            .as_ref()            .map(|data_device_manager| data_device_manager.get_data_device(&seat, &qh, ()));         let primary_selection = globals            .primary_selection_manager            .as_ref()            .map(|primary_selection_manager| primary_selection_manager.get_device(&seat, &qh, ()));         let mut cursor = Cursor::new(&conn, &globals, 24);         handle            .insert_source(XDPEventSource::new(&common.background_executor), {                move |event, _, client| match event {                    XDPEvent::WindowAppearance(appearance) => {                        if let Some(client) = client.0.upgrade() {                            let mut client = client.borrow_mut();                             client.common.appearance = appearance;                             for (_, window) in &mut client.windows {                                window.set_appearance(appearance);                            }                        }                    }

Lines 1–500 of 2097.