client.rs

   1use std::cell::RefCell;
   2use std::collections::HashSet;
   3use std::ops::Deref;
   4use std::os::fd::AsRawFd;
   5use std::rc::{Rc, Weak};
   6use std::sync::Arc;
   7use std::time::{Duration, Instant};
   8
   9use anyhow::Context;
  10use async_task::Runnable;
  11use calloop::channel::Channel;
  12
  13use collections::HashMap;
  14
  15use futures::channel::oneshot;
  16use mio::{Interest, Token, Waker};
  17use util::ResultExt;
  18use x11rb::connection::{Connection, RequestConnection};
  19use x11rb::cursor;
  20use x11rb::errors::ConnectionError;
  21use x11rb::protocol::xinput::ConnectionExt;
  22use x11rb::protocol::xkb::ConnectionExt as _;
  23use x11rb::protocol::xproto::{ChangeWindowAttributesAux, ConnectionExt as _};
  24use x11rb::protocol::{randr, render, xinput, xkb, xproto, Event};
  25use x11rb::resource_manager::Database;
  26use x11rb::xcb_ffi::XCBConnection;
  27use xim::{x11rb::X11rbClient, Client};
  28use xim::{AttributeName, InputStyle};
  29use xkbc::x11::ffi::{XKB_X11_MIN_MAJOR_XKB_VERSION, XKB_X11_MIN_MINOR_XKB_VERSION};
  30use xkbcommon::xkb as xkbc;
  31
  32use crate::platform::linux::LinuxClient;
  33use crate::platform::{LinuxCommon, PlatformWindow};
  34use crate::{
  35    modifiers_from_xinput_info, point, px, AnyWindowHandle, Bounds, ClipboardItem, CursorStyle,
  36    DisplayId, Keystroke, Modifiers, ModifiersChangedEvent, Pixels, PlatformDisplay, PlatformInput,
  37    Point, QuitSignal, ScrollDelta, Size, TouchPhase, WindowParams, X11Window,
  38};
  39
  40use super::{
  41    super::{get_xkb_compose_state, open_uri_internal, SCROLL_LINES},
  42    X11Display, X11WindowStatePtr, XcbAtoms,
  43};
  44use super::{button_of_key, modifiers_from_state, pressed_button_from_mask};
  45use super::{XimCallbackEvent, XimHandler};
  46use crate::platform::linux::is_within_click_distance;
  47use crate::platform::linux::platform::DOUBLE_CLICK_INTERVAL;
  48use crate::platform::linux::xdg_desktop_portal::{Event as XDPEvent, XDPEventSource};
  49
  50pub(super) const XINPUT_MASTER_DEVICE: u16 = 1;
  51
  52pub(crate) struct WindowRef {
  53    window: X11WindowStatePtr,
  54}
  55
  56impl WindowRef {
  57    pub fn handle(&self) -> AnyWindowHandle {
  58        self.window.state.borrow().handle
  59    }
  60}
  61
  62impl Deref for WindowRef {
  63    type Target = X11WindowStatePtr;
  64
  65    fn deref(&self) -> &Self::Target {
  66        &self.window
  67    }
  68}
  69
  70#[derive(Debug)]
  71#[non_exhaustive]
  72pub enum EventHandlerError {
  73    XCBConnectionError(ConnectionError),
  74    XIMClientError(xim::ClientError),
  75}
  76
  77impl std::error::Error for EventHandlerError {}
  78
  79impl std::fmt::Display for EventHandlerError {
  80    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
  81        match self {
  82            EventHandlerError::XCBConnectionError(err) => err.fmt(f),
  83            EventHandlerError::XIMClientError(err) => err.fmt(f),
  84        }
  85    }
  86}
  87
  88impl From<ConnectionError> for EventHandlerError {
  89    fn from(err: ConnectionError) -> Self {
  90        EventHandlerError::XCBConnectionError(err)
  91    }
  92}
  93
  94impl From<xim::ClientError> for EventHandlerError {
  95    fn from(err: xim::ClientError) -> Self {
  96        EventHandlerError::XIMClientError(err)
  97    }
  98}
  99
 100pub struct X11ClientState {
 101    /// poll is in an Option so we can take it out in `run()` without
 102    /// mutating self.
 103    poll: Option<mio::Poll>,
 104    quit_signal_rx: oneshot::Receiver<()>,
 105    runnables: Channel<Runnable>,
 106    xdp_event_source: XDPEventSource,
 107
 108    pub(crate) last_click: Instant,
 109    pub(crate) last_location: Point<Pixels>,
 110    pub(crate) current_count: usize,
 111
 112    pub(crate) scale_factor: f32,
 113    pub(crate) xcb_connection: Rc<XCBConnection>,
 114    pub(crate) x_root_index: usize,
 115    pub(crate) _resource_database: Database,
 116    pub(crate) atoms: XcbAtoms,
 117    pub(crate) windows: HashMap<xproto::Window, WindowRef>,
 118    pub(crate) focused_window: Option<xproto::Window>,
 119    pub(crate) xkb: xkbc::State,
 120    pub(crate) ximc: Option<X11rbClient<Rc<XCBConnection>>>,
 121    pub(crate) xim_handler: Option<XimHandler>,
 122    pub modifiers: Modifiers,
 123
 124    pub(crate) compose_state: Option<xkbc::compose::State>,
 125    pub(crate) pre_edit_text: Option<String>,
 126    pub(crate) composing: bool,
 127    pub(crate) cursor_handle: cursor::Handle,
 128    pub(crate) cursor_styles: HashMap<xproto::Window, CursorStyle>,
 129    pub(crate) cursor_cache: HashMap<CursorStyle, xproto::Cursor>,
 130
 131    pub(crate) scroll_class_data: Vec<xinput::DeviceClassDataScroll>,
 132    pub(crate) scroll_x: Option<f32>,
 133    pub(crate) scroll_y: Option<f32>,
 134
 135    pub(crate) common: LinuxCommon,
 136    pub(crate) clipboard: x11_clipboard::Clipboard,
 137    pub(crate) clipboard_item: Option<ClipboardItem>,
 138}
 139
 140#[derive(Clone)]
 141pub struct X11ClientStatePtr(pub Weak<RefCell<X11ClientState>>);
 142
 143impl X11ClientStatePtr {
 144    pub fn drop_window(&self, x_window: u32) {
 145        let client = X11Client(self.0.upgrade().expect("client already dropped"));
 146        let mut state = client.0.borrow_mut();
 147
 148        if state.windows.remove(&x_window).is_none() {
 149            log::warn!(
 150                "failed to remove X window {} from client state, does not exist",
 151                x_window
 152            );
 153        }
 154
 155        state.cursor_styles.remove(&x_window);
 156
 157        if state.windows.is_empty() {
 158            state.common.quit_signal.quit();
 159        }
 160    }
 161}
 162
 163struct ChannelQuitSignal {
 164    tx: Option<oneshot::Sender<()>>,
 165    waker: Option<Arc<Waker>>,
 166}
 167
 168impl ChannelQuitSignal {
 169    fn new(waker: Option<Arc<Waker>>) -> (Self, oneshot::Receiver<()>) {
 170        let (tx, rx) = oneshot::channel::<()>();
 171
 172        let quit_signal = ChannelQuitSignal {
 173            tx: Some(tx),
 174            waker,
 175        };
 176
 177        (quit_signal, rx)
 178    }
 179}
 180
 181impl QuitSignal for ChannelQuitSignal {
 182    fn quit(&mut self) {
 183        if let Some(tx) = self.tx.take() {
 184            tx.send(()).log_err();
 185            if let Some(waker) = self.waker.as_ref() {
 186                waker.wake().ok();
 187            }
 188        }
 189    }
 190}
 191
 192#[derive(Clone)]
 193pub(crate) struct X11Client(Rc<RefCell<X11ClientState>>);
 194
 195impl X11Client {
 196    pub(crate) fn new() -> Self {
 197        let mut poll = mio::Poll::new().unwrap();
 198
 199        let waker = Arc::new(Waker::new(poll.registry(), WAKER_TOKEN).unwrap());
 200
 201        let (quit_signal, quit_signal_rx) = ChannelQuitSignal::new(Some(waker.clone()));
 202        let (common, runnables) = LinuxCommon::new(Box::new(quit_signal), Some(waker.clone()));
 203
 204        let (xcb_connection, x_root_index) = XCBConnection::connect(None).unwrap();
 205        xcb_connection
 206            .prefetch_extension_information(xkb::X11_EXTENSION_NAME)
 207            .unwrap();
 208        xcb_connection
 209            .prefetch_extension_information(randr::X11_EXTENSION_NAME)
 210            .unwrap();
 211        xcb_connection
 212            .prefetch_extension_information(render::X11_EXTENSION_NAME)
 213            .unwrap();
 214        xcb_connection
 215            .prefetch_extension_information(xinput::X11_EXTENSION_NAME)
 216            .unwrap();
 217
 218        let xinput_version = xcb_connection
 219            .xinput_xi_query_version(2, 0)
 220            .unwrap()
 221            .reply()
 222            .unwrap();
 223        assert!(
 224            xinput_version.major_version >= 2,
 225            "XInput Extension v2 not supported."
 226        );
 227
 228        let master_device_query = xcb_connection
 229            .xinput_xi_query_device(XINPUT_MASTER_DEVICE)
 230            .unwrap()
 231            .reply()
 232            .unwrap();
 233        let scroll_class_data = master_device_query
 234            .infos
 235            .iter()
 236            .find(|info| info.type_ == xinput::DeviceType::MASTER_POINTER)
 237            .unwrap()
 238            .classes
 239            .iter()
 240            .filter_map(|class| class.data.as_scroll())
 241            .map(|class| *class)
 242            .collect::<Vec<_>>();
 243
 244        let atoms = XcbAtoms::new(&xcb_connection).unwrap();
 245        let xkb = xcb_connection
 246            .xkb_use_extension(XKB_X11_MIN_MAJOR_XKB_VERSION, XKB_X11_MIN_MINOR_XKB_VERSION)
 247            .unwrap();
 248
 249        let atoms = atoms.reply().unwrap();
 250        let xkb = xkb.reply().unwrap();
 251        let events = xkb::EventType::STATE_NOTIFY;
 252        xcb_connection
 253            .xkb_select_events(
 254                xkb::ID::USE_CORE_KBD.into(),
 255                0u8.into(),
 256                events,
 257                0u8.into(),
 258                0u8.into(),
 259                &xkb::SelectEventsAux::new(),
 260            )
 261            .unwrap();
 262        assert!(xkb.supported);
 263
 264        let xkb_context = xkbc::Context::new(xkbc::CONTEXT_NO_FLAGS);
 265        let xkb_state = {
 266            let xkb_device_id = xkbc::x11::get_core_keyboard_device_id(&xcb_connection);
 267            let xkb_keymap = xkbc::x11::keymap_new_from_device(
 268                &xkb_context,
 269                &xcb_connection,
 270                xkb_device_id,
 271                xkbc::KEYMAP_COMPILE_NO_FLAGS,
 272            );
 273            xkbc::x11::state_new_from_device(&xkb_keymap, &xcb_connection, xkb_device_id)
 274        };
 275        let compose_state = get_xkb_compose_state(&xkb_context);
 276        let resource_database = x11rb::resource_manager::new_from_default(&xcb_connection).unwrap();
 277
 278        let scale_factor = resource_database
 279            .get_value("Xft.dpi", "Xft.dpi")
 280            .ok()
 281            .flatten()
 282            .map(|dpi: f32| dpi / 96.0)
 283            .unwrap_or(1.0);
 284
 285        let cursor_handle = cursor::Handle::new(&xcb_connection, x_root_index, &resource_database)
 286            .unwrap()
 287            .reply()
 288            .unwrap();
 289
 290        let clipboard = x11_clipboard::Clipboard::new().unwrap();
 291
 292        let xcb_connection = Rc::new(xcb_connection);
 293
 294        let ximc = X11rbClient::init(Rc::clone(&xcb_connection), x_root_index, None).ok();
 295        let xim_handler = if ximc.is_some() {
 296            Some(XimHandler::new())
 297        } else {
 298            None
 299        };
 300
 301        let xdp_event_source =
 302            XDPEventSource::new(&common.background_executor, Some(waker.clone()));
 303
 304        X11Client(Rc::new(RefCell::new(X11ClientState {
 305            poll: Some(poll),
 306            runnables,
 307
 308            xdp_event_source,
 309            quit_signal_rx,
 310            common,
 311
 312            modifiers: Modifiers::default(),
 313            last_click: Instant::now(),
 314            last_location: Point::new(px(0.0), px(0.0)),
 315            current_count: 0,
 316            scale_factor,
 317
 318            xcb_connection,
 319            x_root_index,
 320            _resource_database: resource_database,
 321            atoms,
 322            windows: HashMap::default(),
 323            focused_window: None,
 324            xkb: xkb_state,
 325            ximc,
 326            xim_handler,
 327
 328            compose_state,
 329            pre_edit_text: None,
 330            composing: false,
 331
 332            cursor_handle,
 333            cursor_styles: HashMap::default(),
 334            cursor_cache: HashMap::default(),
 335
 336            scroll_class_data,
 337            scroll_x: None,
 338            scroll_y: None,
 339
 340            clipboard,
 341            clipboard_item: None,
 342        })))
 343    }
 344
 345    pub fn enable_ime(&self) {
 346        let mut state = self.0.borrow_mut();
 347        if state.ximc.is_none() {
 348            return;
 349        }
 350
 351        let mut ximc = state.ximc.take().unwrap();
 352        let mut xim_handler = state.xim_handler.take().unwrap();
 353        let mut ic_attributes = ximc
 354            .build_ic_attributes()
 355            .push(
 356                AttributeName::InputStyle,
 357                InputStyle::PREEDIT_CALLBACKS
 358                    | InputStyle::STATUS_NOTHING
 359                    | InputStyle::PREEDIT_NONE,
 360            )
 361            .push(AttributeName::ClientWindow, xim_handler.window)
 362            .push(AttributeName::FocusWindow, xim_handler.window);
 363
 364        let window_id = state.focused_window;
 365        drop(state);
 366        if let Some(window_id) = window_id {
 367            let window = self.get_window(window_id).unwrap();
 368            if let Some(area) = window.get_ime_area() {
 369                ic_attributes =
 370                    ic_attributes.nested_list(xim::AttributeName::PreeditAttributes, |b| {
 371                        b.push(
 372                            xim::AttributeName::SpotLocation,
 373                            xim::Point {
 374                                x: u32::from(area.origin.x + area.size.width) as i16,
 375                                y: u32::from(area.origin.y + area.size.height) as i16,
 376                            },
 377                        );
 378                    });
 379            }
 380        }
 381        ximc.create_ic(xim_handler.im_id, ic_attributes.build())
 382            .ok();
 383        state = self.0.borrow_mut();
 384        state.xim_handler = Some(xim_handler);
 385        state.ximc = Some(ximc);
 386    }
 387
 388    pub fn disable_ime(&self) {
 389        let mut state = self.0.borrow_mut();
 390        state.composing = false;
 391        if let Some(mut ximc) = state.ximc.take() {
 392            let xim_handler = state.xim_handler.as_ref().unwrap();
 393            ximc.destroy_ic(xim_handler.im_id, xim_handler.ic_id).ok();
 394            state.ximc = Some(ximc);
 395        }
 396    }
 397
 398    fn get_window(&self, win: xproto::Window) -> Option<X11WindowStatePtr> {
 399        let state = self.0.borrow();
 400        state
 401            .windows
 402            .get(&win)
 403            .filter(|window_reference| !window_reference.window.state.borrow().destroyed)
 404            .map(|window_reference| window_reference.window.clone())
 405    }
 406
 407    fn read_x11_events(&self) -> (HashSet<u32>, Vec<Event>) {
 408        let mut events = Vec::new();
 409        let mut windows_to_refresh = HashSet::new();
 410        let mut state = self.0.borrow_mut();
 411
 412        let mut last_key_release: Option<Event> = None;
 413
 414        loop {
 415            match state.xcb_connection.poll_for_event() {
 416                Ok(Some(event)) => {
 417                    if let Event::Expose(expose_event) = event {
 418                        windows_to_refresh.insert(expose_event.window);
 419                    } else {
 420                        match event {
 421                            Event::KeyRelease(_) => {
 422                                last_key_release = Some(event);
 423                            }
 424                            Event::KeyPress(key_press) => {
 425                                if let Some(Event::KeyRelease(key_release)) =
 426                                    last_key_release.take()
 427                                {
 428                                    // We ignore that last KeyRelease if it's too close to this KeyPress,
 429                                    // suggesting that it's auto-generated by X11 as a key-repeat event.
 430                                    if key_release.detail != key_press.detail
 431                                        || key_press.time.wrapping_sub(key_release.time) > 20
 432                                    {
 433                                        events.push(Event::KeyRelease(key_release));
 434                                    }
 435                                }
 436                                events.push(Event::KeyPress(key_press));
 437                            }
 438                            _ => {
 439                                if let Some(release_event) = last_key_release.take() {
 440                                    events.push(release_event);
 441                                }
 442                                events.push(event);
 443                            }
 444                        }
 445                    }
 446                }
 447                Ok(None) => {
 448                    // Add any remaining stored KeyRelease event
 449                    if let Some(release_event) = last_key_release.take() {
 450                        events.push(release_event);
 451                    }
 452                    break;
 453                }
 454                Err(e) => {
 455                    log::warn!("error polling for X11 events: {e:?}");
 456                    break;
 457                }
 458            }
 459        }
 460
 461        (windows_to_refresh, events)
 462    }
 463
 464    fn process_x11_events(&self, events: Vec<Event>) {
 465        for event in events.into_iter() {
 466            let mut state = self.0.borrow_mut();
 467            if state.ximc.is_none() || state.xim_handler.is_none() {
 468                drop(state);
 469                self.handle_event(event);
 470                continue;
 471            }
 472
 473            let mut ximc = state.ximc.take().unwrap();
 474            let mut xim_handler = state.xim_handler.take().unwrap();
 475            let xim_connected = xim_handler.connected;
 476            drop(state);
 477
 478            // let xim_filtered = false;
 479            let xim_filtered = match ximc.filter_event(&event, &mut xim_handler) {
 480                Ok(handled) => handled,
 481                Err(err) => {
 482                    log::error!("XIMClientError: {}", err);
 483                    false
 484                }
 485            };
 486            let xim_callback_event = xim_handler.last_callback_event.take();
 487
 488            let mut state = self.0.borrow_mut();
 489            state.ximc = Some(ximc);
 490            state.xim_handler = Some(xim_handler);
 491
 492            if let Some(event) = xim_callback_event {
 493                drop(state);
 494                self.handle_xim_callback_event(event);
 495            } else {
 496                drop(state);
 497            }
 498
 499            if xim_filtered {
 500                continue;
 501            }
 502
 503            if xim_connected {
 504                self.xim_handle_event(event);
 505            } else {
 506                self.handle_event(event);
 507            }
 508        }
 509    }
 510
 511    fn handle_event(&self, event: Event) -> Option<()> {
 512        match event {
 513            Event::ClientMessage(event) => {
 514                let window = self.get_window(event.window)?;
 515                let [atom, ..] = event.data.as_data32();
 516                let mut state = self.0.borrow_mut();
 517
 518                if atom == state.atoms.WM_DELETE_WINDOW {
 519                    // window "x" button clicked by user
 520                    if window.should_close() {
 521                        // Rest of the close logic is handled in drop_window()
 522                        window.close();
 523                    }
 524                }
 525            }
 526            Event::ConfigureNotify(event) => {
 527                let bounds = Bounds {
 528                    origin: Point {
 529                        x: event.x.into(),
 530                        y: event.y.into(),
 531                    },
 532                    size: Size {
 533                        width: event.width.into(),
 534                        height: event.height.into(),
 535                    },
 536                };
 537                let window = self.get_window(event.window)?;
 538                window.configure(bounds);
 539            }
 540            Event::Expose(event) => {
 541                let window = self.get_window(event.window)?;
 542                window.refresh();
 543            }
 544            Event::FocusIn(event) => {
 545                let window = self.get_window(event.event)?;
 546                window.set_focused(true);
 547                let mut state = self.0.borrow_mut();
 548                state.focused_window = Some(event.event);
 549                drop(state);
 550                self.enable_ime();
 551            }
 552            Event::FocusOut(event) => {
 553                let window = self.get_window(event.event)?;
 554                window.set_focused(false);
 555                let mut state = self.0.borrow_mut();
 556                state.focused_window = None;
 557                if let Some(compose_state) = state.compose_state.as_mut() {
 558                    compose_state.reset();
 559                }
 560                state.pre_edit_text.take();
 561                drop(state);
 562                self.disable_ime();
 563                window.handle_ime_delete();
 564            }
 565            Event::XkbStateNotify(event) => {
 566                let mut state = self.0.borrow_mut();
 567                state.xkb.update_mask(
 568                    event.base_mods.into(),
 569                    event.latched_mods.into(),
 570                    event.locked_mods.into(),
 571                    0,
 572                    0,
 573                    event.locked_group.into(),
 574                );
 575
 576                let modifiers = Modifiers::from_xkb(&state.xkb);
 577                if state.modifiers == modifiers {
 578                    drop(state);
 579                } else {
 580                    let focused_window_id = state.focused_window?;
 581                    state.modifiers = modifiers;
 582                    drop(state);
 583
 584                    let focused_window = self.get_window(focused_window_id)?;
 585                    focused_window.handle_input(PlatformInput::ModifiersChanged(
 586                        ModifiersChangedEvent { modifiers },
 587                    ));
 588                }
 589            }
 590            Event::KeyPress(event) => {
 591                let window = self.get_window(event.event)?;
 592                let mut state = self.0.borrow_mut();
 593
 594                let modifiers = modifiers_from_state(event.state);
 595                state.modifiers = modifiers;
 596
 597                let keystroke = {
 598                    let code = event.detail.into();
 599                    let mut keystroke = crate::Keystroke::from_xkb(&state.xkb, modifiers, code);
 600                    state.xkb.update_key(code, xkbc::KeyDirection::Down);
 601                    let keysym = state.xkb.key_get_one_sym(code);
 602                    if keysym.is_modifier_key() {
 603                        return Some(());
 604                    }
 605                    if let Some(mut compose_state) = state.compose_state.take() {
 606                        compose_state.feed(keysym);
 607                        match compose_state.status() {
 608                            xkbc::Status::Composed => {
 609                                state.pre_edit_text.take();
 610                                keystroke.ime_key = compose_state.utf8();
 611                                if let Some(keysym) = compose_state.keysym() {
 612                                    keystroke.key = xkbc::keysym_get_name(keysym);
 613                                }
 614                            }
 615                            xkbc::Status::Composing => {
 616                                keystroke.ime_key = None;
 617                                state.pre_edit_text = compose_state
 618                                    .utf8()
 619                                    .or(crate::Keystroke::underlying_dead_key(keysym));
 620                                let pre_edit =
 621                                    state.pre_edit_text.clone().unwrap_or(String::default());
 622                                drop(state);
 623                                window.handle_ime_preedit(pre_edit);
 624                                state = self.0.borrow_mut();
 625                            }
 626                            xkbc::Status::Cancelled => {
 627                                let pre_edit = state.pre_edit_text.take();
 628                                drop(state);
 629                                if let Some(pre_edit) = pre_edit {
 630                                    window.handle_ime_commit(pre_edit);
 631                                }
 632                                if let Some(current_key) = Keystroke::underlying_dead_key(keysym) {
 633                                    window.handle_ime_preedit(current_key);
 634                                }
 635                                state = self.0.borrow_mut();
 636                                compose_state.feed(keysym);
 637                            }
 638                            _ => {}
 639                        }
 640                        state.compose_state = Some(compose_state);
 641                    }
 642                    keystroke
 643                };
 644                drop(state);
 645                window.handle_input(PlatformInput::KeyDown(crate::KeyDownEvent {
 646                    keystroke,
 647                    is_held: false,
 648                }));
 649            }
 650            Event::KeyRelease(event) => {
 651                let window = self.get_window(event.event)?;
 652                let mut state = self.0.borrow_mut();
 653
 654                let modifiers = modifiers_from_state(event.state);
 655                state.modifiers = modifiers;
 656
 657                let keystroke = {
 658                    let code = event.detail.into();
 659                    let keystroke = crate::Keystroke::from_xkb(&state.xkb, modifiers, code);
 660                    state.xkb.update_key(code, xkbc::KeyDirection::Up);
 661                    let keysym = state.xkb.key_get_one_sym(code);
 662                    if keysym.is_modifier_key() {
 663                        return Some(());
 664                    }
 665                    keystroke
 666                };
 667                drop(state);
 668                window.handle_input(PlatformInput::KeyUp(crate::KeyUpEvent { keystroke }));
 669            }
 670            Event::XinputButtonPress(event) => {
 671                let window = self.get_window(event.event)?;
 672                let mut state = self.0.borrow_mut();
 673
 674                let modifiers = modifiers_from_xinput_info(event.mods);
 675                state.modifiers = modifiers;
 676
 677                let position = point(
 678                    px(event.event_x as f32 / u16::MAX as f32 / state.scale_factor),
 679                    px(event.event_y as f32 / u16::MAX as f32 / state.scale_factor),
 680                );
 681
 682                if state.composing && state.ximc.is_some() {
 683                    drop(state);
 684                    self.disable_ime();
 685                    self.enable_ime();
 686                    window.handle_ime_unmark();
 687                    state = self.0.borrow_mut();
 688                } else if let Some(text) = state.pre_edit_text.take() {
 689                    if let Some(compose_state) = state.compose_state.as_mut() {
 690                        compose_state.reset();
 691                    }
 692                    drop(state);
 693                    window.handle_ime_commit(text);
 694                    state = self.0.borrow_mut();
 695                }
 696                if let Some(button) = button_of_key(event.detail.try_into().unwrap()) {
 697                    let click_elapsed = state.last_click.elapsed();
 698
 699                    if click_elapsed < DOUBLE_CLICK_INTERVAL
 700                        && is_within_click_distance(state.last_location, position)
 701                    {
 702                        state.current_count += 1;
 703                    } else {
 704                        state.current_count = 1;
 705                    }
 706
 707                    state.last_click = Instant::now();
 708                    state.last_location = position;
 709                    let current_count = state.current_count;
 710
 711                    drop(state);
 712                    window.handle_input(PlatformInput::MouseDown(crate::MouseDownEvent {
 713                        button,
 714                        position,
 715                        modifiers,
 716                        click_count: current_count,
 717                        first_mouse: false,
 718                    }));
 719                } else {
 720                    log::warn!("Unknown button press: {event:?}");
 721                }
 722            }
 723            Event::XinputButtonRelease(event) => {
 724                let window = self.get_window(event.event)?;
 725                let mut state = self.0.borrow_mut();
 726                let modifiers = modifiers_from_xinput_info(event.mods);
 727                state.modifiers = modifiers;
 728
 729                let position = point(
 730                    px(event.event_x as f32 / u16::MAX as f32 / state.scale_factor),
 731                    px(event.event_y as f32 / u16::MAX as f32 / state.scale_factor),
 732                );
 733                if let Some(button) = button_of_key(event.detail.try_into().unwrap()) {
 734                    let click_count = state.current_count;
 735                    drop(state);
 736                    window.handle_input(PlatformInput::MouseUp(crate::MouseUpEvent {
 737                        button,
 738                        position,
 739                        modifiers,
 740                        click_count,
 741                    }));
 742                }
 743            }
 744            Event::XinputMotion(event) => {
 745                let window = self.get_window(event.event)?;
 746                let mut state = self.0.borrow_mut();
 747                let pressed_button = pressed_button_from_mask(event.button_mask[0]);
 748                let position = point(
 749                    px(event.event_x as f32 / u16::MAX as f32 / state.scale_factor),
 750                    px(event.event_y as f32 / u16::MAX as f32 / state.scale_factor),
 751                );
 752                let modifiers = modifiers_from_xinput_info(event.mods);
 753                state.modifiers = modifiers;
 754                drop(state);
 755
 756                let axisvalues = event
 757                    .axisvalues
 758                    .iter()
 759                    .map(|axisvalue| fp3232_to_f32(*axisvalue))
 760                    .collect::<Vec<_>>();
 761
 762                if event.valuator_mask[0] & 3 != 0 {
 763                    window.handle_input(PlatformInput::MouseMove(crate::MouseMoveEvent {
 764                        position,
 765                        pressed_button,
 766                        modifiers,
 767                    }));
 768                }
 769
 770                let mut valuator_idx = 0;
 771                let scroll_class_data = self.0.borrow().scroll_class_data.clone();
 772                for shift in 0..32 {
 773                    if (event.valuator_mask[0] >> shift) & 1 == 0 {
 774                        continue;
 775                    }
 776
 777                    for scroll_class in &scroll_class_data {
 778                        if scroll_class.scroll_type == xinput::ScrollType::HORIZONTAL
 779                            && scroll_class.number == shift
 780                        {
 781                            let new_scroll = axisvalues[valuator_idx]
 782                                / fp3232_to_f32(scroll_class.increment)
 783                                * SCROLL_LINES as f32;
 784                            let old_scroll = self.0.borrow().scroll_x;
 785                            self.0.borrow_mut().scroll_x = Some(new_scroll);
 786
 787                            if let Some(old_scroll) = old_scroll {
 788                                let delta_scroll = old_scroll - new_scroll;
 789                                window.handle_input(PlatformInput::ScrollWheel(
 790                                    crate::ScrollWheelEvent {
 791                                        position,
 792                                        delta: ScrollDelta::Lines(Point::new(delta_scroll, 0.0)),
 793                                        modifiers,
 794                                        touch_phase: TouchPhase::default(),
 795                                    },
 796                                ));
 797                            }
 798                        } else if scroll_class.scroll_type == xinput::ScrollType::VERTICAL
 799                            && scroll_class.number == shift
 800                        {
 801                            // the `increment` is the valuator delta equivalent to one positive unit of scrolling. Here that means SCROLL_LINES lines.
 802                            let new_scroll = axisvalues[valuator_idx]
 803                                / fp3232_to_f32(scroll_class.increment)
 804                                * SCROLL_LINES as f32;
 805                            let old_scroll = self.0.borrow().scroll_y;
 806                            self.0.borrow_mut().scroll_y = Some(new_scroll);
 807
 808                            if let Some(old_scroll) = old_scroll {
 809                                let delta_scroll = old_scroll - new_scroll;
 810                                window.handle_input(PlatformInput::ScrollWheel(
 811                                    crate::ScrollWheelEvent {
 812                                        position,
 813                                        delta: ScrollDelta::Lines(Point::new(0.0, delta_scroll)),
 814                                        modifiers,
 815                                        touch_phase: TouchPhase::default(),
 816                                    },
 817                                ));
 818                            }
 819                        }
 820                    }
 821
 822                    valuator_idx += 1;
 823                }
 824            }
 825            Event::XinputLeave(event) if event.mode == xinput::NotifyMode::NORMAL => {
 826                self.0.borrow_mut().scroll_x = None; // Set last scroll to `None` so that a large delta isn't created if scrolling is done outside the window (the valuator is global)
 827                self.0.borrow_mut().scroll_y = None;
 828
 829                let window = self.get_window(event.event)?;
 830                let mut state = self.0.borrow_mut();
 831                let pressed_button = pressed_button_from_mask(event.buttons[0]);
 832                let position = point(
 833                    px(event.event_x as f32 / u16::MAX as f32 / state.scale_factor),
 834                    px(event.event_y as f32 / u16::MAX as f32 / state.scale_factor),
 835                );
 836                let modifiers = modifiers_from_xinput_info(event.mods);
 837                state.modifiers = modifiers;
 838                drop(state);
 839
 840                window.handle_input(PlatformInput::MouseExited(crate::MouseExitEvent {
 841                    pressed_button,
 842                    position,
 843                    modifiers,
 844                }));
 845            }
 846            _ => {}
 847        };
 848
 849        Some(())
 850    }
 851
 852    fn handle_xim_callback_event(&self, event: XimCallbackEvent) {
 853        match event {
 854            XimCallbackEvent::XimXEvent(event) => {
 855                self.handle_event(event);
 856            }
 857            XimCallbackEvent::XimCommitEvent(window, text) => {
 858                self.xim_handle_commit(window, text);
 859            }
 860            XimCallbackEvent::XimPreeditEvent(window, text) => {
 861                self.xim_handle_preedit(window, text);
 862            }
 863        };
 864    }
 865
 866    fn xim_handle_event(&self, event: Event) -> Option<()> {
 867        match event {
 868            Event::KeyPress(event) | Event::KeyRelease(event) => {
 869                let mut state = self.0.borrow_mut();
 870                let mut ximc = state.ximc.take().unwrap();
 871                let mut xim_handler = state.xim_handler.take().unwrap();
 872                drop(state);
 873                xim_handler.window = event.event;
 874                ximc.forward_event(
 875                    xim_handler.im_id,
 876                    xim_handler.ic_id,
 877                    xim::ForwardEventFlag::empty(),
 878                    &event,
 879                )
 880                .unwrap();
 881                let mut state = self.0.borrow_mut();
 882                state.ximc = Some(ximc);
 883                state.xim_handler = Some(xim_handler);
 884                drop(state);
 885            }
 886            event => {
 887                self.handle_event(event);
 888            }
 889        }
 890        Some(())
 891    }
 892
 893    fn xim_handle_commit(&self, window: xproto::Window, text: String) -> Option<()> {
 894        let window = self.get_window(window).unwrap();
 895        let mut state = self.0.borrow_mut();
 896        state.composing = false;
 897        drop(state);
 898
 899        window.handle_ime_commit(text);
 900        Some(())
 901    }
 902
 903    fn xim_handle_preedit(&self, window: xproto::Window, text: String) -> Option<()> {
 904        let window = self.get_window(window).unwrap();
 905        window.handle_ime_preedit(text);
 906
 907        let mut state = self.0.borrow_mut();
 908        let mut ximc = state.ximc.take().unwrap();
 909        let mut xim_handler = state.xim_handler.take().unwrap();
 910        state.composing = true;
 911        drop(state);
 912
 913        if let Some(area) = window.get_ime_area() {
 914            let ic_attributes = ximc
 915                .build_ic_attributes()
 916                .push(
 917                    xim::AttributeName::InputStyle,
 918                    xim::InputStyle::PREEDIT_CALLBACKS
 919                        | xim::InputStyle::STATUS_NOTHING
 920                        | xim::InputStyle::PREEDIT_POSITION,
 921                )
 922                .push(xim::AttributeName::ClientWindow, xim_handler.window)
 923                .push(xim::AttributeName::FocusWindow, xim_handler.window)
 924                .nested_list(xim::AttributeName::PreeditAttributes, |b| {
 925                    b.push(
 926                        xim::AttributeName::SpotLocation,
 927                        xim::Point {
 928                            x: u32::from(area.origin.x + area.size.width) as i16,
 929                            y: u32::from(area.origin.y + area.size.height) as i16,
 930                        },
 931                    );
 932                })
 933                .build();
 934            ximc.set_ic_values(xim_handler.im_id, xim_handler.ic_id, ic_attributes)
 935                .ok();
 936        }
 937        let mut state = self.0.borrow_mut();
 938        state.ximc = Some(ximc);
 939        state.xim_handler = Some(xim_handler);
 940        drop(state);
 941        Some(())
 942    }
 943}
 944
 945const XCB_CONNECTION_TOKEN: Token = Token(0);
 946const WAKER_TOKEN: Token = Token(1);
 947
 948impl LinuxClient for X11Client {
 949    fn compositor_name(&self) -> &'static str {
 950        "X11"
 951    }
 952    fn with_common<R>(&self, f: impl FnOnce(&mut LinuxCommon) -> R) -> R {
 953        f(&mut self.0.borrow_mut().common)
 954    }
 955
 956    fn displays(&self) -> Vec<Rc<dyn PlatformDisplay>> {
 957        let state = self.0.borrow();
 958        let setup = state.xcb_connection.setup();
 959        setup
 960            .roots
 961            .iter()
 962            .enumerate()
 963            .filter_map(|(root_id, _)| {
 964                Some(Rc::new(X11Display::new(
 965                    &state.xcb_connection,
 966                    state.scale_factor,
 967                    root_id,
 968                )?) as Rc<dyn PlatformDisplay>)
 969            })
 970            .collect()
 971    }
 972
 973    fn primary_display(&self) -> Option<Rc<dyn PlatformDisplay>> {
 974        let state = self.0.borrow();
 975
 976        Some(Rc::new(
 977            X11Display::new(
 978                &state.xcb_connection,
 979                state.scale_factor,
 980                state.x_root_index,
 981            )
 982            .expect("There should always be a root index"),
 983        ))
 984    }
 985
 986    fn display(&self, id: DisplayId) -> Option<Rc<dyn PlatformDisplay>> {
 987        let state = self.0.borrow();
 988
 989        Some(Rc::new(X11Display::new(
 990            &state.xcb_connection,
 991            state.scale_factor,
 992            id.0 as usize,
 993        )?))
 994    }
 995
 996    fn open_window(
 997        &self,
 998        handle: AnyWindowHandle,
 999        params: WindowParams,
1000    ) -> anyhow::Result<Box<dyn PlatformWindow>> {
1001        let mut state = self.0.borrow_mut();
1002        let x_window = state.xcb_connection.generate_id().unwrap();
1003
1004        let window = X11Window::new(
1005            handle,
1006            X11ClientStatePtr(Rc::downgrade(&self.0)),
1007            state.common.foreground_executor.clone(),
1008            params,
1009            &state.xcb_connection,
1010            state.x_root_index,
1011            x_window,
1012            &state.atoms,
1013            state.scale_factor,
1014            state.common.appearance,
1015        )?;
1016
1017        let window_ref = WindowRef {
1018            window: window.0.clone(),
1019        };
1020
1021        state.windows.insert(x_window, window_ref);
1022        Ok(Box::new(window))
1023    }
1024
1025    fn set_cursor_style(&self, style: CursorStyle) {
1026        let mut state = self.0.borrow_mut();
1027        let Some(focused_window) = state.focused_window else {
1028            return;
1029        };
1030        let current_style = state
1031            .cursor_styles
1032            .get(&focused_window)
1033            .unwrap_or(&CursorStyle::Arrow);
1034        if *current_style == style {
1035            return;
1036        }
1037
1038        let cursor = match state.cursor_cache.get(&style) {
1039            Some(cursor) => *cursor,
1040            None => {
1041                let cursor = state
1042                    .cursor_handle
1043                    .load_cursor(&state.xcb_connection, &style.to_icon_name())
1044                    .expect("failed to load cursor");
1045                state.cursor_cache.insert(style, cursor);
1046                cursor
1047            }
1048        };
1049
1050        state.cursor_styles.insert(focused_window, style);
1051        state
1052            .xcb_connection
1053            .change_window_attributes(
1054                focused_window,
1055                &ChangeWindowAttributesAux {
1056                    cursor: Some(cursor),
1057                    ..Default::default()
1058                },
1059            )
1060            .expect("failed to change window cursor");
1061    }
1062
1063    fn open_uri(&self, uri: &str) {
1064        open_uri_internal(uri, None);
1065    }
1066
1067    fn write_to_primary(&self, item: crate::ClipboardItem) {
1068        let state = self.0.borrow_mut();
1069        state
1070            .clipboard
1071            .store(
1072                state.clipboard.setter.atoms.primary,
1073                state.clipboard.setter.atoms.utf8_string,
1074                item.text().as_bytes(),
1075            )
1076            .ok();
1077    }
1078
1079    fn write_to_clipboard(&self, item: crate::ClipboardItem) {
1080        let mut state = self.0.borrow_mut();
1081        state
1082            .clipboard
1083            .store(
1084                state.clipboard.setter.atoms.clipboard,
1085                state.clipboard.setter.atoms.utf8_string,
1086                item.text().as_bytes(),
1087            )
1088            .ok();
1089        state.clipboard_item.replace(item);
1090    }
1091
1092    fn read_from_primary(&self) -> Option<crate::ClipboardItem> {
1093        let state = self.0.borrow_mut();
1094        state
1095            .clipboard
1096            .load(
1097                state.clipboard.getter.atoms.primary,
1098                state.clipboard.getter.atoms.utf8_string,
1099                state.clipboard.getter.atoms.property,
1100                Duration::from_secs(3),
1101            )
1102            .map(|text| crate::ClipboardItem {
1103                text: String::from_utf8(text).unwrap(),
1104                metadata: None,
1105            })
1106            .ok()
1107    }
1108
1109    fn read_from_clipboard(&self) -> Option<crate::ClipboardItem> {
1110        let state = self.0.borrow_mut();
1111        // if the last copy was from this app, return our cached item
1112        // which has metadata attached.
1113        if state
1114            .clipboard
1115            .setter
1116            .connection
1117            .get_selection_owner(state.clipboard.setter.atoms.clipboard)
1118            .ok()
1119            .and_then(|r| r.reply().ok())
1120            .map(|reply| reply.owner == state.clipboard.setter.window)
1121            .unwrap_or(false)
1122        {
1123            return state.clipboard_item.clone();
1124        }
1125        state
1126            .clipboard
1127            .load(
1128                state.clipboard.getter.atoms.clipboard,
1129                state.clipboard.getter.atoms.utf8_string,
1130                state.clipboard.getter.atoms.property,
1131                Duration::from_secs(3),
1132            )
1133            .map(|text| crate::ClipboardItem {
1134                text: String::from_utf8(text).unwrap(),
1135                metadata: None,
1136            })
1137            .ok()
1138    }
1139
1140    fn run(&self) {
1141        let mut poll = self
1142            .0
1143            .borrow_mut()
1144            .poll
1145            .take()
1146            .context("no poll set on X11Client. calling run more than once is not possible")
1147            .unwrap();
1148
1149        let xcb_fd = self.0.borrow().xcb_connection.as_raw_fd();
1150        let mut xcb_source = mio::unix::SourceFd(&xcb_fd);
1151        poll.registry()
1152            .register(&mut xcb_source, XCB_CONNECTION_TOKEN, Interest::READABLE)
1153            .unwrap();
1154
1155        let mut events = mio::Events::with_capacity(1024);
1156        let mut next_refresh_needed = Instant::now();
1157
1158        'run_loop: loop {
1159            let poll_timeout = next_refresh_needed - Instant::now();
1160            // We rounding the poll_timeout down so `mio` doesn't round it up to the next higher milliseconds
1161            let poll_timeout = Duration::from_millis(poll_timeout.as_millis() as u64);
1162
1163            if poll_timeout >= Duration::from_millis(1) {
1164                let _ = poll.poll(&mut events, Some(poll_timeout));
1165            };
1166
1167            let mut state = self.0.borrow_mut();
1168
1169            // Check if we need to quit
1170            if let Ok(Some(())) = state.quit_signal_rx.try_recv() {
1171                return;
1172            }
1173
1174            // Redraw windows
1175            let now = Instant::now();
1176            if now > next_refresh_needed {
1177                // This will be pulled down to 16ms (or less) if a window is open
1178                let mut frame_length = Duration::from_millis(100);
1179
1180                let mut windows = vec![];
1181                for (_, window_ref) in state.windows.iter() {
1182                    if !window_ref.window.state.borrow().destroyed {
1183                        frame_length = frame_length.min(window_ref.window.refresh_rate());
1184                        windows.push(window_ref.window.clone());
1185                    }
1186                }
1187
1188                drop(state);
1189
1190                for window in windows {
1191                    window.refresh();
1192                }
1193
1194                state = self.0.borrow_mut();
1195
1196                // In the case that we're looping a bit too fast, slow down
1197                next_refresh_needed = now.max(next_refresh_needed) + frame_length;
1198            }
1199
1200            // X11 events
1201            drop(state);
1202
1203            loop {
1204                let (x_windows, events) = self.read_x11_events();
1205                for x_window in x_windows {
1206                    if let Some(window) = self.get_window(x_window) {
1207                        window.refresh();
1208                    }
1209                }
1210
1211                if events.len() == 0 {
1212                    break;
1213                }
1214                self.process_x11_events(events);
1215
1216                // When X11 is sending us events faster than we can handle we'll
1217                // let the frame rate drop to 10fps to try and avoid getting too behind.
1218                if Instant::now() > next_refresh_needed + Duration::from_millis(80) {
1219                    continue 'run_loop;
1220                }
1221            }
1222
1223            state = self.0.borrow_mut();
1224
1225            // Runnables
1226            while let Ok(runnable) = state.runnables.try_recv() {
1227                drop(state);
1228                runnable.run();
1229                state = self.0.borrow_mut();
1230
1231                if Instant::now() + Duration::from_millis(1) >= next_refresh_needed {
1232                    continue 'run_loop;
1233                }
1234            }
1235
1236            // XDG events
1237            if let Ok(event) = state.xdp_event_source.try_recv() {
1238                match event {
1239                    XDPEvent::WindowAppearance(appearance) => {
1240                        let mut windows = state
1241                            .windows
1242                            .values()
1243                            .map(|window| window.window.clone())
1244                            .collect::<Vec<_>>();
1245                        drop(state);
1246
1247                        self.with_common(|common| common.appearance = appearance);
1248                        for mut window in windows {
1249                            window.set_appearance(appearance);
1250                        }
1251                    }
1252                    XDPEvent::CursorTheme(_) | XDPEvent::CursorSize(_) => {
1253                        // noop, X11 manages this for us.
1254                    }
1255                };
1256            };
1257        }
1258    }
1259
1260    fn active_window(&self) -> Option<AnyWindowHandle> {
1261        let state = self.0.borrow();
1262        state.focused_window.and_then(|focused_window| {
1263            state
1264                .windows
1265                .get(&focused_window)
1266                .map(|window| window.handle())
1267        })
1268    }
1269}
1270
1271fn fp3232_to_f32(value: xinput::Fp3232) -> f32 {
1272    value.integral as f32 + value.frac as f32 / u32::MAX as f32
1273}