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