client.rs

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