client.rs

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