client.rs

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