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, _arg1, arg2, arg3, _arg4] = 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                } else if atom == state.atoms._NET_WM_SYNC_REQUEST {
 525                    window.state.borrow_mut().last_sync_counter =
 526                        Some(x11rb::protocol::sync::Int64 {
 527                            lo: arg2,
 528                            hi: arg3 as i32,
 529                        })
 530                }
 531            }
 532            Event::ConfigureNotify(event) => {
 533                let bounds = Bounds {
 534                    origin: Point {
 535                        x: event.x.into(),
 536                        y: event.y.into(),
 537                    },
 538                    size: Size {
 539                        width: event.width.into(),
 540                        height: event.height.into(),
 541                    },
 542                };
 543                let window = self.get_window(event.window)?;
 544                window.configure(bounds);
 545            }
 546            Event::PropertyNotify(event) => {
 547                let window = self.get_window(event.window)?;
 548                window.property_notify(event);
 549            }
 550            Event::Expose(event) => {
 551                let window = self.get_window(event.window)?;
 552                window.refresh();
 553            }
 554            Event::FocusIn(event) => {
 555                let window = self.get_window(event.event)?;
 556                window.set_focused(true);
 557                let mut state = self.0.borrow_mut();
 558                state.focused_window = Some(event.event);
 559                drop(state);
 560                self.enable_ime();
 561            }
 562            Event::FocusOut(event) => {
 563                let window = self.get_window(event.event)?;
 564                window.set_focused(false);
 565                let mut state = self.0.borrow_mut();
 566                state.focused_window = None;
 567                if let Some(compose_state) = state.compose_state.as_mut() {
 568                    compose_state.reset();
 569                }
 570                state.pre_edit_text.take();
 571                drop(state);
 572                self.disable_ime();
 573                window.handle_ime_delete();
 574            }
 575            Event::XkbStateNotify(event) => {
 576                let mut state = self.0.borrow_mut();
 577                state.xkb.update_mask(
 578                    event.base_mods.into(),
 579                    event.latched_mods.into(),
 580                    event.locked_mods.into(),
 581                    0,
 582                    0,
 583                    event.locked_group.into(),
 584                );
 585
 586                let modifiers = Modifiers::from_xkb(&state.xkb);
 587                if state.modifiers == modifiers {
 588                    drop(state);
 589                } else {
 590                    let focused_window_id = state.focused_window?;
 591                    state.modifiers = modifiers;
 592                    drop(state);
 593
 594                    let focused_window = self.get_window(focused_window_id)?;
 595                    focused_window.handle_input(PlatformInput::ModifiersChanged(
 596                        ModifiersChangedEvent { modifiers },
 597                    ));
 598                }
 599            }
 600            Event::KeyPress(event) => {
 601                let window = self.get_window(event.event)?;
 602                let mut state = self.0.borrow_mut();
 603
 604                let modifiers = modifiers_from_state(event.state);
 605                state.modifiers = modifiers;
 606
 607                let keystroke = {
 608                    let code = event.detail.into();
 609                    let mut keystroke = crate::Keystroke::from_xkb(&state.xkb, modifiers, code);
 610                    state.xkb.update_key(code, xkbc::KeyDirection::Down);
 611                    let keysym = state.xkb.key_get_one_sym(code);
 612                    if keysym.is_modifier_key() {
 613                        return Some(());
 614                    }
 615                    if let Some(mut compose_state) = state.compose_state.take() {
 616                        compose_state.feed(keysym);
 617                        match compose_state.status() {
 618                            xkbc::Status::Composed => {
 619                                state.pre_edit_text.take();
 620                                keystroke.ime_key = compose_state.utf8();
 621                                if let Some(keysym) = compose_state.keysym() {
 622                                    keystroke.key = xkbc::keysym_get_name(keysym);
 623                                }
 624                            }
 625                            xkbc::Status::Composing => {
 626                                keystroke.ime_key = None;
 627                                state.pre_edit_text = compose_state
 628                                    .utf8()
 629                                    .or(crate::Keystroke::underlying_dead_key(keysym));
 630                                let pre_edit =
 631                                    state.pre_edit_text.clone().unwrap_or(String::default());
 632                                drop(state);
 633                                window.handle_ime_preedit(pre_edit);
 634                                state = self.0.borrow_mut();
 635                            }
 636                            xkbc::Status::Cancelled => {
 637                                let pre_edit = state.pre_edit_text.take();
 638                                drop(state);
 639                                if let Some(pre_edit) = pre_edit {
 640                                    window.handle_ime_commit(pre_edit);
 641                                }
 642                                if let Some(current_key) = Keystroke::underlying_dead_key(keysym) {
 643                                    window.handle_ime_preedit(current_key);
 644                                }
 645                                state = self.0.borrow_mut();
 646                                compose_state.feed(keysym);
 647                            }
 648                            _ => {}
 649                        }
 650                        state.compose_state = Some(compose_state);
 651                    }
 652                    keystroke
 653                };
 654                drop(state);
 655                window.handle_input(PlatformInput::KeyDown(crate::KeyDownEvent {
 656                    keystroke,
 657                    is_held: false,
 658                }));
 659            }
 660            Event::KeyRelease(event) => {
 661                let window = self.get_window(event.event)?;
 662                let mut state = self.0.borrow_mut();
 663
 664                let modifiers = modifiers_from_state(event.state);
 665                state.modifiers = modifiers;
 666
 667                let keystroke = {
 668                    let code = event.detail.into();
 669                    let keystroke = crate::Keystroke::from_xkb(&state.xkb, modifiers, code);
 670                    state.xkb.update_key(code, xkbc::KeyDirection::Up);
 671                    let keysym = state.xkb.key_get_one_sym(code);
 672                    if keysym.is_modifier_key() {
 673                        return Some(());
 674                    }
 675                    keystroke
 676                };
 677                drop(state);
 678                window.handle_input(PlatformInput::KeyUp(crate::KeyUpEvent { keystroke }));
 679            }
 680            Event::XinputButtonPress(event) => {
 681                let window = self.get_window(event.event)?;
 682                let mut state = self.0.borrow_mut();
 683
 684                let modifiers = modifiers_from_xinput_info(event.mods);
 685                state.modifiers = modifiers;
 686
 687                let position = point(
 688                    px(event.event_x as f32 / u16::MAX as f32 / state.scale_factor),
 689                    px(event.event_y as f32 / u16::MAX as f32 / state.scale_factor),
 690                );
 691
 692                if state.composing && state.ximc.is_some() {
 693                    drop(state);
 694                    self.disable_ime();
 695                    self.enable_ime();
 696                    window.handle_ime_unmark();
 697                    state = self.0.borrow_mut();
 698                } else if let Some(text) = state.pre_edit_text.take() {
 699                    if let Some(compose_state) = state.compose_state.as_mut() {
 700                        compose_state.reset();
 701                    }
 702                    drop(state);
 703                    window.handle_ime_commit(text);
 704                    state = self.0.borrow_mut();
 705                }
 706                if let Some(button) = button_of_key(event.detail.try_into().unwrap()) {
 707                    let click_elapsed = state.last_click.elapsed();
 708
 709                    if click_elapsed < DOUBLE_CLICK_INTERVAL
 710                        && is_within_click_distance(state.last_location, position)
 711                    {
 712                        state.current_count += 1;
 713                    } else {
 714                        state.current_count = 1;
 715                    }
 716
 717                    state.last_click = Instant::now();
 718                    state.last_location = position;
 719                    let current_count = state.current_count;
 720
 721                    drop(state);
 722                    window.handle_input(PlatformInput::MouseDown(crate::MouseDownEvent {
 723                        button,
 724                        position,
 725                        modifiers,
 726                        click_count: current_count,
 727                        first_mouse: false,
 728                    }));
 729                } else {
 730                    log::warn!("Unknown button press: {event:?}");
 731                }
 732            }
 733            Event::XinputButtonRelease(event) => {
 734                let window = self.get_window(event.event)?;
 735                let mut state = self.0.borrow_mut();
 736                let modifiers = modifiers_from_xinput_info(event.mods);
 737                state.modifiers = modifiers;
 738
 739                let position = point(
 740                    px(event.event_x as f32 / u16::MAX as f32 / state.scale_factor),
 741                    px(event.event_y as f32 / u16::MAX as f32 / state.scale_factor),
 742                );
 743                if let Some(button) = button_of_key(event.detail.try_into().unwrap()) {
 744                    let click_count = state.current_count;
 745                    drop(state);
 746                    window.handle_input(PlatformInput::MouseUp(crate::MouseUpEvent {
 747                        button,
 748                        position,
 749                        modifiers,
 750                        click_count,
 751                    }));
 752                }
 753            }
 754            Event::XinputMotion(event) => {
 755                let window = self.get_window(event.event)?;
 756                let mut state = self.0.borrow_mut();
 757                let pressed_button = pressed_button_from_mask(event.button_mask[0]);
 758                let position = point(
 759                    px(event.event_x as f32 / u16::MAX as f32 / state.scale_factor),
 760                    px(event.event_y as f32 / u16::MAX as f32 / state.scale_factor),
 761                );
 762                let modifiers = modifiers_from_xinput_info(event.mods);
 763                state.modifiers = modifiers;
 764                drop(state);
 765
 766                let axisvalues = event
 767                    .axisvalues
 768                    .iter()
 769                    .map(|axisvalue| fp3232_to_f32(*axisvalue))
 770                    .collect::<Vec<_>>();
 771
 772                if event.valuator_mask[0] & 3 != 0 {
 773                    window.handle_input(PlatformInput::MouseMove(crate::MouseMoveEvent {
 774                        position,
 775                        pressed_button,
 776                        modifiers,
 777                    }));
 778                }
 779
 780                let mut valuator_idx = 0;
 781                let scroll_class_data = self.0.borrow().scroll_class_data.clone();
 782                for shift in 0..32 {
 783                    if (event.valuator_mask[0] >> shift) & 1 == 0 {
 784                        continue;
 785                    }
 786
 787                    for scroll_class in &scroll_class_data {
 788                        if scroll_class.scroll_type == xinput::ScrollType::HORIZONTAL
 789                            && scroll_class.number == shift
 790                        {
 791                            let new_scroll = axisvalues[valuator_idx]
 792                                / fp3232_to_f32(scroll_class.increment)
 793                                * SCROLL_LINES as f32;
 794                            let old_scroll = self.0.borrow().scroll_x;
 795                            self.0.borrow_mut().scroll_x = Some(new_scroll);
 796
 797                            if let Some(old_scroll) = old_scroll {
 798                                let delta_scroll = old_scroll - new_scroll;
 799                                window.handle_input(PlatformInput::ScrollWheel(
 800                                    crate::ScrollWheelEvent {
 801                                        position,
 802                                        delta: ScrollDelta::Lines(Point::new(delta_scroll, 0.0)),
 803                                        modifiers,
 804                                        touch_phase: TouchPhase::default(),
 805                                    },
 806                                ));
 807                            }
 808                        } else if scroll_class.scroll_type == xinput::ScrollType::VERTICAL
 809                            && scroll_class.number == shift
 810                        {
 811                            // the `increment` is the valuator delta equivalent to one positive unit of scrolling. Here that means SCROLL_LINES lines.
 812                            let new_scroll = axisvalues[valuator_idx]
 813                                / fp3232_to_f32(scroll_class.increment)
 814                                * SCROLL_LINES as f32;
 815                            let old_scroll = self.0.borrow().scroll_y;
 816                            self.0.borrow_mut().scroll_y = Some(new_scroll);
 817
 818                            if let Some(old_scroll) = old_scroll {
 819                                let delta_scroll = old_scroll - new_scroll;
 820                                window.handle_input(PlatformInput::ScrollWheel(
 821                                    crate::ScrollWheelEvent {
 822                                        position,
 823                                        delta: ScrollDelta::Lines(Point::new(0.0, delta_scroll)),
 824                                        modifiers,
 825                                        touch_phase: TouchPhase::default(),
 826                                    },
 827                                ));
 828                            }
 829                        }
 830                    }
 831
 832                    valuator_idx += 1;
 833                }
 834            }
 835            Event::XinputLeave(event) if event.mode == xinput::NotifyMode::NORMAL => {
 836                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)
 837                self.0.borrow_mut().scroll_y = None;
 838
 839                let window = self.get_window(event.event)?;
 840                let mut state = self.0.borrow_mut();
 841                let pressed_button = pressed_button_from_mask(event.buttons[0]);
 842                let position = point(
 843                    px(event.event_x as f32 / u16::MAX as f32 / state.scale_factor),
 844                    px(event.event_y as f32 / u16::MAX as f32 / state.scale_factor),
 845                );
 846                let modifiers = modifiers_from_xinput_info(event.mods);
 847                state.modifiers = modifiers;
 848                drop(state);
 849
 850                window.handle_input(PlatformInput::MouseExited(crate::MouseExitEvent {
 851                    pressed_button,
 852                    position,
 853                    modifiers,
 854                }));
 855            }
 856            _ => {}
 857        };
 858
 859        Some(())
 860    }
 861
 862    fn handle_xim_callback_event(&self, event: XimCallbackEvent) {
 863        match event {
 864            XimCallbackEvent::XimXEvent(event) => {
 865                self.handle_event(event);
 866            }
 867            XimCallbackEvent::XimCommitEvent(window, text) => {
 868                self.xim_handle_commit(window, text);
 869            }
 870            XimCallbackEvent::XimPreeditEvent(window, text) => {
 871                self.xim_handle_preedit(window, text);
 872            }
 873        };
 874    }
 875
 876    fn xim_handle_event(&self, event: Event) -> Option<()> {
 877        match event {
 878            Event::KeyPress(event) | Event::KeyRelease(event) => {
 879                let mut state = self.0.borrow_mut();
 880                let mut ximc = state.ximc.take().unwrap();
 881                let mut xim_handler = state.xim_handler.take().unwrap();
 882                drop(state);
 883                xim_handler.window = event.event;
 884                ximc.forward_event(
 885                    xim_handler.im_id,
 886                    xim_handler.ic_id,
 887                    xim::ForwardEventFlag::empty(),
 888                    &event,
 889                )
 890                .unwrap();
 891                let mut state = self.0.borrow_mut();
 892                state.ximc = Some(ximc);
 893                state.xim_handler = Some(xim_handler);
 894                drop(state);
 895            }
 896            event => {
 897                self.handle_event(event);
 898            }
 899        }
 900        Some(())
 901    }
 902
 903    fn xim_handle_commit(&self, window: xproto::Window, text: String) -> Option<()> {
 904        let window = self.get_window(window).unwrap();
 905        let mut state = self.0.borrow_mut();
 906        state.composing = false;
 907        drop(state);
 908
 909        window.handle_ime_commit(text);
 910        Some(())
 911    }
 912
 913    fn xim_handle_preedit(&self, window: xproto::Window, text: String) -> Option<()> {
 914        let window = self.get_window(window).unwrap();
 915        window.handle_ime_preedit(text);
 916
 917        let mut state = self.0.borrow_mut();
 918        let mut ximc = state.ximc.take().unwrap();
 919        let mut xim_handler = state.xim_handler.take().unwrap();
 920        state.composing = true;
 921        drop(state);
 922
 923        if let Some(area) = window.get_ime_area() {
 924            let ic_attributes = ximc
 925                .build_ic_attributes()
 926                .push(
 927                    xim::AttributeName::InputStyle,
 928                    xim::InputStyle::PREEDIT_CALLBACKS
 929                        | xim::InputStyle::STATUS_NOTHING
 930                        | xim::InputStyle::PREEDIT_POSITION,
 931                )
 932                .push(xim::AttributeName::ClientWindow, xim_handler.window)
 933                .push(xim::AttributeName::FocusWindow, xim_handler.window)
 934                .nested_list(xim::AttributeName::PreeditAttributes, |b| {
 935                    b.push(
 936                        xim::AttributeName::SpotLocation,
 937                        xim::Point {
 938                            x: u32::from(area.origin.x + area.size.width) as i16,
 939                            y: u32::from(area.origin.y + area.size.height) as i16,
 940                        },
 941                    );
 942                })
 943                .build();
 944            ximc.set_ic_values(xim_handler.im_id, xim_handler.ic_id, ic_attributes)
 945                .ok();
 946        }
 947        let mut state = self.0.borrow_mut();
 948        state.ximc = Some(ximc);
 949        state.xim_handler = Some(xim_handler);
 950        drop(state);
 951        Some(())
 952    }
 953}
 954
 955const XCB_CONNECTION_TOKEN: Token = Token(0);
 956const WAKER_TOKEN: Token = Token(1);
 957
 958impl LinuxClient for X11Client {
 959    fn compositor_name(&self) -> &'static str {
 960        "X11"
 961    }
 962    fn with_common<R>(&self, f: impl FnOnce(&mut LinuxCommon) -> R) -> R {
 963        f(&mut self.0.borrow_mut().common)
 964    }
 965
 966    fn displays(&self) -> Vec<Rc<dyn PlatformDisplay>> {
 967        let state = self.0.borrow();
 968        let setup = state.xcb_connection.setup();
 969        setup
 970            .roots
 971            .iter()
 972            .enumerate()
 973            .filter_map(|(root_id, _)| {
 974                Some(Rc::new(X11Display::new(
 975                    &state.xcb_connection,
 976                    state.scale_factor,
 977                    root_id,
 978                )?) as Rc<dyn PlatformDisplay>)
 979            })
 980            .collect()
 981    }
 982
 983    fn primary_display(&self) -> Option<Rc<dyn PlatformDisplay>> {
 984        let state = self.0.borrow();
 985
 986        Some(Rc::new(
 987            X11Display::new(
 988                &state.xcb_connection,
 989                state.scale_factor,
 990                state.x_root_index,
 991            )
 992            .expect("There should always be a root index"),
 993        ))
 994    }
 995
 996    fn display(&self, id: DisplayId) -> Option<Rc<dyn PlatformDisplay>> {
 997        let state = self.0.borrow();
 998
 999        Some(Rc::new(X11Display::new(
1000            &state.xcb_connection,
1001            state.scale_factor,
1002            id.0 as usize,
1003        )?))
1004    }
1005
1006    fn open_window(
1007        &self,
1008        handle: AnyWindowHandle,
1009        params: WindowParams,
1010    ) -> anyhow::Result<Box<dyn PlatformWindow>> {
1011        let mut state = self.0.borrow_mut();
1012        let x_window = state.xcb_connection.generate_id().unwrap();
1013
1014        let window = X11Window::new(
1015            handle,
1016            X11ClientStatePtr(Rc::downgrade(&self.0)),
1017            state.common.foreground_executor.clone(),
1018            params,
1019            &state.xcb_connection,
1020            state.x_root_index,
1021            x_window,
1022            &state.atoms,
1023            state.scale_factor,
1024            state.common.appearance,
1025        )?;
1026
1027        let window_ref = WindowRef {
1028            window: window.0.clone(),
1029        };
1030
1031        state.windows.insert(x_window, window_ref);
1032        Ok(Box::new(window))
1033    }
1034
1035    fn set_cursor_style(&self, style: CursorStyle) {
1036        let mut state = self.0.borrow_mut();
1037        let Some(focused_window) = state.focused_window else {
1038            return;
1039        };
1040        let current_style = state
1041            .cursor_styles
1042            .get(&focused_window)
1043            .unwrap_or(&CursorStyle::Arrow);
1044        if *current_style == style {
1045            return;
1046        }
1047
1048        let cursor = match state.cursor_cache.get(&style) {
1049            Some(cursor) => *cursor,
1050            None => {
1051                let cursor = state
1052                    .cursor_handle
1053                    .load_cursor(&state.xcb_connection, &style.to_icon_name())
1054                    .expect("failed to load cursor");
1055                state.cursor_cache.insert(style, cursor);
1056                cursor
1057            }
1058        };
1059
1060        state.cursor_styles.insert(focused_window, style);
1061        state
1062            .xcb_connection
1063            .change_window_attributes(
1064                focused_window,
1065                &ChangeWindowAttributesAux {
1066                    cursor: Some(cursor),
1067                    ..Default::default()
1068                },
1069            )
1070            .expect("failed to change window cursor");
1071    }
1072
1073    fn open_uri(&self, uri: &str) {
1074        open_uri_internal(uri, None);
1075    }
1076
1077    fn write_to_primary(&self, item: crate::ClipboardItem) {
1078        let state = self.0.borrow_mut();
1079        state
1080            .clipboard
1081            .store(
1082                state.clipboard.setter.atoms.primary,
1083                state.clipboard.setter.atoms.utf8_string,
1084                item.text().as_bytes(),
1085            )
1086            .ok();
1087    }
1088
1089    fn write_to_clipboard(&self, item: crate::ClipboardItem) {
1090        let mut state = self.0.borrow_mut();
1091        state
1092            .clipboard
1093            .store(
1094                state.clipboard.setter.atoms.clipboard,
1095                state.clipboard.setter.atoms.utf8_string,
1096                item.text().as_bytes(),
1097            )
1098            .ok();
1099        state.clipboard_item.replace(item);
1100    }
1101
1102    fn read_from_primary(&self) -> Option<crate::ClipboardItem> {
1103        let state = self.0.borrow_mut();
1104        state
1105            .clipboard
1106            .load(
1107                state.clipboard.getter.atoms.primary,
1108                state.clipboard.getter.atoms.utf8_string,
1109                state.clipboard.getter.atoms.property,
1110                Duration::from_secs(3),
1111            )
1112            .map(|text| crate::ClipboardItem {
1113                text: String::from_utf8(text).unwrap(),
1114                metadata: None,
1115            })
1116            .ok()
1117    }
1118
1119    fn read_from_clipboard(&self) -> Option<crate::ClipboardItem> {
1120        let state = self.0.borrow_mut();
1121        // if the last copy was from this app, return our cached item
1122        // which has metadata attached.
1123        if state
1124            .clipboard
1125            .setter
1126            .connection
1127            .get_selection_owner(state.clipboard.setter.atoms.clipboard)
1128            .ok()
1129            .and_then(|r| r.reply().ok())
1130            .map(|reply| reply.owner == state.clipboard.setter.window)
1131            .unwrap_or(false)
1132        {
1133            return state.clipboard_item.clone();
1134        }
1135        state
1136            .clipboard
1137            .load(
1138                state.clipboard.getter.atoms.clipboard,
1139                state.clipboard.getter.atoms.utf8_string,
1140                state.clipboard.getter.atoms.property,
1141                Duration::from_secs(3),
1142            )
1143            .map(|text| crate::ClipboardItem {
1144                text: String::from_utf8(text).unwrap(),
1145                metadata: None,
1146            })
1147            .ok()
1148    }
1149
1150    fn run(&self) {
1151        let mut poll = self
1152            .0
1153            .borrow_mut()
1154            .poll
1155            .take()
1156            .context("no poll set on X11Client. calling run more than once is not possible")
1157            .unwrap();
1158
1159        let xcb_fd = self.0.borrow().xcb_connection.as_raw_fd();
1160        let mut xcb_source = mio::unix::SourceFd(&xcb_fd);
1161        poll.registry()
1162            .register(&mut xcb_source, XCB_CONNECTION_TOKEN, Interest::READABLE)
1163            .unwrap();
1164
1165        let mut events = mio::Events::with_capacity(1024);
1166        let mut next_refresh_needed = Instant::now();
1167
1168        'run_loop: loop {
1169            let poll_timeout = next_refresh_needed - Instant::now();
1170            // We rounding the poll_timeout down so `mio` doesn't round it up to the next higher milliseconds
1171            let poll_timeout = Duration::from_millis(poll_timeout.as_millis() as u64);
1172
1173            if poll_timeout >= Duration::from_millis(1) {
1174                let _ = poll.poll(&mut events, Some(poll_timeout));
1175            };
1176
1177            let mut state = self.0.borrow_mut();
1178
1179            // Check if we need to quit
1180            if let Ok(Some(())) = state.quit_signal_rx.try_recv() {
1181                return;
1182            }
1183
1184            // Redraw windows
1185            let now = Instant::now();
1186            if now > next_refresh_needed {
1187                // This will be pulled down to 16ms (or less) if a window is open
1188                let mut frame_length = Duration::from_millis(100);
1189
1190                let mut windows = vec![];
1191                for (_, window_ref) in state.windows.iter() {
1192                    if !window_ref.window.state.borrow().destroyed {
1193                        frame_length = frame_length.min(window_ref.window.refresh_rate());
1194                        windows.push(window_ref.window.clone());
1195                    }
1196                }
1197
1198                drop(state);
1199
1200                for window in windows {
1201                    window.refresh();
1202                }
1203
1204                state = self.0.borrow_mut();
1205
1206                // In the case that we're looping a bit too fast, slow down
1207                next_refresh_needed = now.max(next_refresh_needed) + frame_length;
1208            }
1209
1210            // X11 events
1211            drop(state);
1212
1213            loop {
1214                let (x_windows, events) = self.read_x11_events();
1215                for x_window in x_windows {
1216                    if let Some(window) = self.get_window(x_window) {
1217                        window.refresh();
1218                    }
1219                }
1220
1221                if events.len() == 0 {
1222                    break;
1223                }
1224                self.process_x11_events(events);
1225
1226                // When X11 is sending us events faster than we can handle we'll
1227                // let the frame rate drop to 10fps to try and avoid getting too behind.
1228                if Instant::now() > next_refresh_needed + Duration::from_millis(80) {
1229                    continue 'run_loop;
1230                }
1231            }
1232
1233            state = self.0.borrow_mut();
1234
1235            // Runnables
1236            while let Ok(runnable) = state.runnables.try_recv() {
1237                drop(state);
1238                runnable.run();
1239                state = self.0.borrow_mut();
1240
1241                if Instant::now() + Duration::from_millis(1) >= next_refresh_needed {
1242                    continue 'run_loop;
1243                }
1244            }
1245
1246            // XDG events
1247            if let Ok(event) = state.xdp_event_source.try_recv() {
1248                match event {
1249                    XDPEvent::WindowAppearance(appearance) => {
1250                        let mut windows = state
1251                            .windows
1252                            .values()
1253                            .map(|window| window.window.clone())
1254                            .collect::<Vec<_>>();
1255                        drop(state);
1256
1257                        self.with_common(|common| common.appearance = appearance);
1258                        for mut window in windows {
1259                            window.set_appearance(appearance);
1260                        }
1261                    }
1262                    XDPEvent::CursorTheme(_) | XDPEvent::CursorSize(_) => {
1263                        // noop, X11 manages this for us.
1264                    }
1265                };
1266            };
1267        }
1268    }
1269
1270    fn active_window(&self) -> Option<AnyWindowHandle> {
1271        let state = self.0.borrow();
1272        state.focused_window.and_then(|focused_window| {
1273            state
1274                .windows
1275                .get(&focused_window)
1276                .map(|window| window.handle())
1277        })
1278    }
1279}
1280
1281fn fp3232_to_f32(value: xinput::Fp3232) -> f32 {
1282    value.integral as f32 + value.frac as f32 / u32::MAX as f32
1283}