mirror of
https://github.com/italicsjenga/winit-sonoma-fix.git
synced 2025-01-26 03:36:32 +11:00
Replace parts of the Xlib backend with x11-rb
This commit is contained in:
parent
5379d60e4d
commit
d7ec899d69
24 changed files with 1550 additions and 1395 deletions
|
@ -36,7 +36,7 @@ rustdoc-args = ["--cfg", "docsrs"]
|
||||||
|
|
||||||
[features]
|
[features]
|
||||||
default = ["x11", "wayland", "wayland-dlopen", "wayland-csd-adwaita"]
|
default = ["x11", "wayland", "wayland-dlopen", "wayland-csd-adwaita"]
|
||||||
x11 = ["x11-dl", "percent-encoding", "xkbcommon-dl/x11"]
|
x11 = ["x11-dl", "bytemuck", "percent-encoding", "xkbcommon-dl/x11", "x11rb"]
|
||||||
wayland = ["wayland-client", "wayland-backend", "wayland-protocols", "sctk", "fnv", "memmap2"]
|
wayland = ["wayland-client", "wayland-backend", "wayland-protocols", "sctk", "fnv", "memmap2"]
|
||||||
wayland-dlopen = ["wayland-backend/dlopen"]
|
wayland-dlopen = ["wayland-backend/dlopen"]
|
||||||
wayland-csd-adwaita = ["sctk-adwaita", "sctk-adwaita/ab_glyph"]
|
wayland-csd-adwaita = ["sctk-adwaita", "sctk-adwaita/ab_glyph"]
|
||||||
|
@ -113,6 +113,7 @@ features = [
|
||||||
]
|
]
|
||||||
|
|
||||||
[target.'cfg(all(unix, not(any(target_os = "redox", target_family = "wasm", target_os = "android", target_os = "ios", target_os = "macos"))))'.dependencies]
|
[target.'cfg(all(unix, not(any(target_os = "redox", target_family = "wasm", target_os = "android", target_os = "ios", target_os = "macos"))))'.dependencies]
|
||||||
|
bytemuck = { version = "1.13.1", default-features = false, optional = true }
|
||||||
libc = "0.2.64"
|
libc = "0.2.64"
|
||||||
percent-encoding = { version = "2.0", optional = true }
|
percent-encoding = { version = "2.0", optional = true }
|
||||||
fnv = { version = "1.0.3", optional = true }
|
fnv = { version = "1.0.3", optional = true }
|
||||||
|
@ -123,6 +124,7 @@ wayland-backend = { version = "0.1.0", default_features = false, features = ["cl
|
||||||
wayland-protocols = { version = "0.30.0", features = [ "staging"], optional = true }
|
wayland-protocols = { version = "0.30.0", features = [ "staging"], optional = true }
|
||||||
calloop = "0.10.5"
|
calloop = "0.10.5"
|
||||||
x11-dl = { version = "2.18.5", optional = true }
|
x11-dl = { version = "2.18.5", optional = true }
|
||||||
|
x11rb = { version = "0.12.0", default-features = false, features = ["allow-unsafe-code", "dl-libxcb", "xinput", "xkb"], optional = true }
|
||||||
xkbcommon-dl = "0.4.0"
|
xkbcommon-dl = "0.4.0"
|
||||||
memmap2 = { version = "0.5.0", optional = true }
|
memmap2 = { version = "0.5.0", optional = true }
|
||||||
|
|
||||||
|
|
|
@ -23,7 +23,7 @@ use smol_str::SmolStr;
|
||||||
#[cfg(x11_platform)]
|
#[cfg(x11_platform)]
|
||||||
pub use self::x11::XNotSupported;
|
pub use self::x11::XNotSupported;
|
||||||
#[cfg(x11_platform)]
|
#[cfg(x11_platform)]
|
||||||
use self::x11::{ffi::XVisualInfo, util::WindowType as XWindowType, XConnection, XError};
|
use self::x11::{ffi::XVisualInfo, util::WindowType as XWindowType, X11Error, XConnection, XError};
|
||||||
#[cfg(x11_platform)]
|
#[cfg(x11_platform)]
|
||||||
use crate::platform::x11::XlibErrorHook;
|
use crate::platform::x11::XlibErrorHook;
|
||||||
use crate::{
|
use crate::{
|
||||||
|
@ -124,7 +124,7 @@ pub(crate) static X11_BACKEND: Lazy<Mutex<Result<Arc<XConnection>, XNotSupported
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
pub enum OsError {
|
pub enum OsError {
|
||||||
#[cfg(x11_platform)]
|
#[cfg(x11_platform)]
|
||||||
XError(XError),
|
XError(Arc<X11Error>),
|
||||||
#[cfg(x11_platform)]
|
#[cfg(x11_platform)]
|
||||||
XMisc(&'static str),
|
XMisc(&'static str),
|
||||||
#[cfg(wayland_platform)]
|
#[cfg(wayland_platform)]
|
||||||
|
@ -135,7 +135,7 @@ impl fmt::Display for OsError {
|
||||||
fn fmt(&self, _f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> {
|
fn fmt(&self, _f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> {
|
||||||
match *self {
|
match *self {
|
||||||
#[cfg(x11_platform)]
|
#[cfg(x11_platform)]
|
||||||
OsError::XError(ref e) => _f.pad(&e.description),
|
OsError::XError(ref e) => fmt::Display::fmt(e, _f),
|
||||||
#[cfg(x11_platform)]
|
#[cfg(x11_platform)]
|
||||||
OsError::XMisc(e) => _f.pad(e),
|
OsError::XMisc(e) => _f.pad(e),
|
||||||
#[cfg(wayland_platform)]
|
#[cfg(wayland_platform)]
|
||||||
|
|
110
src/platform_impl/linux/x11/atoms.rs
Normal file
110
src/platform_impl/linux/x11/atoms.rs
Normal file
|
@ -0,0 +1,110 @@
|
||||||
|
//! Collects every atom used by the platform implementation.
|
||||||
|
|
||||||
|
use core::ops::Index;
|
||||||
|
|
||||||
|
macro_rules! atom_manager {
|
||||||
|
($($name:ident $(:$lit:literal)?),*) => {
|
||||||
|
x11rb::atom_manager! {
|
||||||
|
/// The atoms used by `winit`
|
||||||
|
pub(crate) Atoms: AtomsCookie {
|
||||||
|
$($name $(:$lit)?,)*
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Indices into the `Atoms` struct.
|
||||||
|
#[derive(Copy, Clone, Debug)]
|
||||||
|
#[allow(non_camel_case_types)]
|
||||||
|
pub(crate) enum AtomName {
|
||||||
|
$($name,)*
|
||||||
|
}
|
||||||
|
|
||||||
|
impl AtomName {
|
||||||
|
pub(crate) fn atom_from(
|
||||||
|
self,
|
||||||
|
atoms: &Atoms
|
||||||
|
) -> &x11rb::protocol::xproto::Atom {
|
||||||
|
match self {
|
||||||
|
$(AtomName::$name => &atoms.$name,)*
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
atom_manager! {
|
||||||
|
// General Use Atoms
|
||||||
|
CARD32,
|
||||||
|
UTF8_STRING,
|
||||||
|
WM_CHANGE_STATE,
|
||||||
|
WM_CLIENT_MACHINE,
|
||||||
|
WM_DELETE_WINDOW,
|
||||||
|
WM_PROTOCOLS,
|
||||||
|
WM_STATE,
|
||||||
|
XIM_SERVERS,
|
||||||
|
|
||||||
|
// Assorted ICCCM Atoms
|
||||||
|
_NET_WM_ICON,
|
||||||
|
_NET_WM_MOVERESIZE,
|
||||||
|
_NET_WM_NAME,
|
||||||
|
_NET_WM_PID,
|
||||||
|
_NET_WM_PING,
|
||||||
|
_NET_WM_STATE,
|
||||||
|
_NET_WM_STATE_ABOVE,
|
||||||
|
_NET_WM_STATE_BELOW,
|
||||||
|
_NET_WM_STATE_FULLSCREEN,
|
||||||
|
_NET_WM_STATE_HIDDEN,
|
||||||
|
_NET_WM_STATE_MAXIMIZED_HORZ,
|
||||||
|
_NET_WM_STATE_MAXIMIZED_VERT,
|
||||||
|
_NET_WM_WINDOW_TYPE,
|
||||||
|
|
||||||
|
// WM window types.
|
||||||
|
_NET_WM_WINDOW_TYPE_DESKTOP,
|
||||||
|
_NET_WM_WINDOW_TYPE_DOCK,
|
||||||
|
_NET_WM_WINDOW_TYPE_TOOLBAR,
|
||||||
|
_NET_WM_WINDOW_TYPE_MENU,
|
||||||
|
_NET_WM_WINDOW_TYPE_UTILITY,
|
||||||
|
_NET_WM_WINDOW_TYPE_SPLASH,
|
||||||
|
_NET_WM_WINDOW_TYPE_DIALOG,
|
||||||
|
_NET_WM_WINDOW_TYPE_DROPDOWN_MENU,
|
||||||
|
_NET_WM_WINDOW_TYPE_POPUP_MENU,
|
||||||
|
_NET_WM_WINDOW_TYPE_TOOLTIP,
|
||||||
|
_NET_WM_WINDOW_TYPE_NOTIFICATION,
|
||||||
|
_NET_WM_WINDOW_TYPE_COMBO,
|
||||||
|
_NET_WM_WINDOW_TYPE_DND,
|
||||||
|
_NET_WM_WINDOW_TYPE_NORMAL,
|
||||||
|
|
||||||
|
// Drag-N-Drop Atoms
|
||||||
|
XdndAware,
|
||||||
|
XdndEnter,
|
||||||
|
XdndLeave,
|
||||||
|
XdndDrop,
|
||||||
|
XdndPosition,
|
||||||
|
XdndStatus,
|
||||||
|
XdndActionPrivate,
|
||||||
|
XdndSelection,
|
||||||
|
XdndFinished,
|
||||||
|
XdndTypeList,
|
||||||
|
TextUriList: b"text/uri-list",
|
||||||
|
None: b"None",
|
||||||
|
|
||||||
|
// Miscellaneous Atoms
|
||||||
|
_GTK_THEME_VARIANT,
|
||||||
|
_MOTIF_WM_HINTS,
|
||||||
|
_NET_ACTIVE_WINDOW,
|
||||||
|
_NET_CLIENT_LIST,
|
||||||
|
_NET_FRAME_EXTENTS,
|
||||||
|
_NET_SUPPORTED,
|
||||||
|
_NET_SUPPORTING_WM_CHECK
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Index<AtomName> for Atoms {
|
||||||
|
type Output = x11rb::protocol::xproto::Atom;
|
||||||
|
|
||||||
|
fn index(&self, index: AtomName) -> &Self::Output {
|
||||||
|
index.atom_from(self)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) use AtomName::*;
|
||||||
|
// Make sure `None` is still defined.
|
||||||
|
pub(crate) use core::option::Option::None;
|
|
@ -7,55 +7,12 @@ use std::{
|
||||||
};
|
};
|
||||||
|
|
||||||
use percent_encoding::percent_decode;
|
use percent_encoding::percent_decode;
|
||||||
|
use x11rb::protocol::xproto::{self, ConnectionExt};
|
||||||
|
|
||||||
use super::{ffi, util, XConnection, XError};
|
use super::{
|
||||||
|
atoms::{AtomName::None as DndNone, *},
|
||||||
#[derive(Debug)]
|
util, CookieResultExt, X11Error, XConnection,
|
||||||
pub(crate) struct DndAtoms {
|
};
|
||||||
pub enter: ffi::Atom,
|
|
||||||
pub leave: ffi::Atom,
|
|
||||||
pub drop: ffi::Atom,
|
|
||||||
pub position: ffi::Atom,
|
|
||||||
pub status: ffi::Atom,
|
|
||||||
pub action_private: ffi::Atom,
|
|
||||||
pub selection: ffi::Atom,
|
|
||||||
pub finished: ffi::Atom,
|
|
||||||
pub type_list: ffi::Atom,
|
|
||||||
pub uri_list: ffi::Atom,
|
|
||||||
pub none: ffi::Atom,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl DndAtoms {
|
|
||||||
pub fn new(xconn: &Arc<XConnection>) -> Result<Self, XError> {
|
|
||||||
let names = [
|
|
||||||
b"XdndEnter\0".as_ptr() as *mut c_char,
|
|
||||||
b"XdndLeave\0".as_ptr() as *mut c_char,
|
|
||||||
b"XdndDrop\0".as_ptr() as *mut c_char,
|
|
||||||
b"XdndPosition\0".as_ptr() as *mut c_char,
|
|
||||||
b"XdndStatus\0".as_ptr() as *mut c_char,
|
|
||||||
b"XdndActionPrivate\0".as_ptr() as *mut c_char,
|
|
||||||
b"XdndSelection\0".as_ptr() as *mut c_char,
|
|
||||||
b"XdndFinished\0".as_ptr() as *mut c_char,
|
|
||||||
b"XdndTypeList\0".as_ptr() as *mut c_char,
|
|
||||||
b"text/uri-list\0".as_ptr() as *mut c_char,
|
|
||||||
b"None\0".as_ptr() as *mut c_char,
|
|
||||||
];
|
|
||||||
let atoms = unsafe { xconn.get_atoms(&names) }?;
|
|
||||||
Ok(DndAtoms {
|
|
||||||
enter: atoms[0],
|
|
||||||
leave: atoms[1],
|
|
||||||
drop: atoms[2],
|
|
||||||
position: atoms[3],
|
|
||||||
status: atoms[4],
|
|
||||||
action_private: atoms[5],
|
|
||||||
selection: atoms[6],
|
|
||||||
finished: atoms[7],
|
|
||||||
type_list: atoms[8],
|
|
||||||
uri_list: atoms[9],
|
|
||||||
none: atoms[10],
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, Clone, Copy)]
|
#[derive(Debug, Clone, Copy)]
|
||||||
pub enum DndState {
|
pub enum DndState {
|
||||||
|
@ -86,22 +43,19 @@ impl From<io::Error> for DndDataParseError {
|
||||||
|
|
||||||
pub(crate) struct Dnd {
|
pub(crate) struct Dnd {
|
||||||
xconn: Arc<XConnection>,
|
xconn: Arc<XConnection>,
|
||||||
pub atoms: DndAtoms,
|
|
||||||
// Populated by XdndEnter event handler
|
// Populated by XdndEnter event handler
|
||||||
pub version: Option<c_long>,
|
pub version: Option<c_long>,
|
||||||
pub type_list: Option<Vec<c_ulong>>,
|
pub type_list: Option<Vec<xproto::Atom>>,
|
||||||
// Populated by XdndPosition event handler
|
// Populated by XdndPosition event handler
|
||||||
pub source_window: Option<c_ulong>,
|
pub source_window: Option<xproto::Window>,
|
||||||
// Populated by SelectionNotify event handler (triggered by XdndPosition event handler)
|
// Populated by SelectionNotify event handler (triggered by XdndPosition event handler)
|
||||||
pub result: Option<Result<Vec<PathBuf>, DndDataParseError>>,
|
pub result: Option<Result<Vec<PathBuf>, DndDataParseError>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Dnd {
|
impl Dnd {
|
||||||
pub fn new(xconn: Arc<XConnection>) -> Result<Self, XError> {
|
pub fn new(xconn: Arc<XConnection>) -> Result<Self, X11Error> {
|
||||||
let atoms = DndAtoms::new(&xconn)?;
|
|
||||||
Ok(Dnd {
|
Ok(Dnd {
|
||||||
xconn,
|
xconn,
|
||||||
atoms,
|
|
||||||
version: None,
|
version: None,
|
||||||
type_list: None,
|
type_list: None,
|
||||||
source_window: None,
|
source_window: None,
|
||||||
|
@ -118,71 +72,85 @@ impl Dnd {
|
||||||
|
|
||||||
pub unsafe fn send_status(
|
pub unsafe fn send_status(
|
||||||
&self,
|
&self,
|
||||||
this_window: c_ulong,
|
this_window: xproto::Window,
|
||||||
target_window: c_ulong,
|
target_window: xproto::Window,
|
||||||
state: DndState,
|
state: DndState,
|
||||||
) -> Result<(), XError> {
|
) -> Result<(), X11Error> {
|
||||||
|
let atoms = self.xconn.atoms();
|
||||||
let (accepted, action) = match state {
|
let (accepted, action) = match state {
|
||||||
DndState::Accepted => (1, self.atoms.action_private as c_long),
|
DndState::Accepted => (1, atoms[XdndActionPrivate]),
|
||||||
DndState::Rejected => (0, self.atoms.none as c_long),
|
DndState::Rejected => (0, atoms[DndNone]),
|
||||||
};
|
};
|
||||||
self.xconn
|
self.xconn
|
||||||
.send_client_msg(
|
.send_client_msg(
|
||||||
target_window,
|
target_window,
|
||||||
target_window,
|
target_window,
|
||||||
self.atoms.status,
|
atoms[XdndStatus] as _,
|
||||||
None,
|
None,
|
||||||
[this_window as c_long, accepted, 0, 0, action],
|
[this_window, accepted, 0, 0, action as _],
|
||||||
)
|
)?
|
||||||
.flush()
|
.ignore_error();
|
||||||
|
|
||||||
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub unsafe fn send_finished(
|
pub unsafe fn send_finished(
|
||||||
&self,
|
&self,
|
||||||
this_window: c_ulong,
|
this_window: xproto::Window,
|
||||||
target_window: c_ulong,
|
target_window: xproto::Window,
|
||||||
state: DndState,
|
state: DndState,
|
||||||
) -> Result<(), XError> {
|
) -> Result<(), X11Error> {
|
||||||
|
let atoms = self.xconn.atoms();
|
||||||
let (accepted, action) = match state {
|
let (accepted, action) = match state {
|
||||||
DndState::Accepted => (1, self.atoms.action_private as c_long),
|
DndState::Accepted => (1, atoms[XdndActionPrivate]),
|
||||||
DndState::Rejected => (0, self.atoms.none as c_long),
|
DndState::Rejected => (0, atoms[DndNone]),
|
||||||
};
|
};
|
||||||
self.xconn
|
self.xconn
|
||||||
.send_client_msg(
|
.send_client_msg(
|
||||||
target_window,
|
target_window,
|
||||||
target_window,
|
target_window,
|
||||||
self.atoms.finished,
|
atoms[XdndFinished] as _,
|
||||||
None,
|
None,
|
||||||
[this_window as c_long, accepted, action, 0, 0],
|
[this_window, accepted, action as _, 0, 0],
|
||||||
)
|
)?
|
||||||
.flush()
|
.ignore_error();
|
||||||
|
|
||||||
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub unsafe fn get_type_list(
|
pub unsafe fn get_type_list(
|
||||||
&self,
|
&self,
|
||||||
source_window: c_ulong,
|
source_window: xproto::Window,
|
||||||
) -> Result<Vec<ffi::Atom>, util::GetPropertyError> {
|
) -> Result<Vec<xproto::Atom>, util::GetPropertyError> {
|
||||||
self.xconn
|
let atoms = self.xconn.atoms();
|
||||||
.get_property(source_window, self.atoms.type_list, ffi::XA_ATOM)
|
self.xconn.get_property(
|
||||||
|
source_window,
|
||||||
|
atoms[XdndTypeList],
|
||||||
|
xproto::Atom::from(xproto::AtomEnum::ATOM),
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub unsafe fn convert_selection(&self, window: c_ulong, time: c_ulong) {
|
pub unsafe fn convert_selection(&self, window: xproto::Window, time: xproto::Timestamp) {
|
||||||
(self.xconn.xlib.XConvertSelection)(
|
let atoms = self.xconn.atoms();
|
||||||
self.xconn.display,
|
self.xconn
|
||||||
self.atoms.selection,
|
.xcb_connection()
|
||||||
self.atoms.uri_list,
|
.convert_selection(
|
||||||
self.atoms.selection,
|
window,
|
||||||
window,
|
atoms[XdndSelection],
|
||||||
time,
|
atoms[TextUriList],
|
||||||
);
|
atoms[XdndSelection],
|
||||||
|
time,
|
||||||
|
)
|
||||||
|
.expect_then_ignore_error("Failed to send XdndSelection event")
|
||||||
}
|
}
|
||||||
|
|
||||||
pub unsafe fn read_data(
|
pub unsafe fn read_data(
|
||||||
&self,
|
&self,
|
||||||
window: c_ulong,
|
window: xproto::Window,
|
||||||
) -> Result<Vec<c_uchar>, util::GetPropertyError> {
|
) -> Result<Vec<c_uchar>, util::GetPropertyError> {
|
||||||
|
let atoms = self.xconn.atoms();
|
||||||
self.xconn
|
self.xconn
|
||||||
.get_property(window, self.atoms.selection, self.atoms.uri_list)
|
.get_property(window, atoms[XdndSelection], atoms[TextUriList])
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn parse_data(&self, data: &mut [c_uchar]) -> Result<Vec<PathBuf>, DndDataParseError> {
|
pub fn parse_data(&self, data: &mut [c_uchar]) -> Result<Vec<PathBuf>, DndDataParseError> {
|
||||||
|
|
|
@ -2,9 +2,13 @@ use std::{cell::RefCell, collections::HashMap, rc::Rc, slice, sync::Arc};
|
||||||
|
|
||||||
use libc::{c_char, c_int, c_long, c_ulong};
|
use libc::{c_char, c_int, c_long, c_ulong};
|
||||||
|
|
||||||
|
use x11rb::protocol::xproto::{self, ConnectionExt as _};
|
||||||
|
use x11rb::x11_utils::Serialize;
|
||||||
|
|
||||||
use super::{
|
use super::{
|
||||||
ffi, get_xtarget, mkdid, mkwid, monitor, util, Device, DeviceId, DeviceInfo, Dnd, DndState,
|
atoms::*, ffi, get_xtarget, mkdid, mkwid, monitor, util, CookieResultExt, Device, DeviceId,
|
||||||
GenericEventCookie, ImeReceiver, ScrollOrientation, UnownedWindow, WindowId, XExtension,
|
DeviceInfo, Dnd, DndState, GenericEventCookie, ImeReceiver, ScrollOrientation, UnownedWindow,
|
||||||
|
WindowId, XExtension,
|
||||||
};
|
};
|
||||||
|
|
||||||
use crate::platform_impl::platform::x11::ime::{ImeEvent, ImeEventReceiver, ImeRequest};
|
use crate::platform_impl::platform::x11::ime::{ImeEvent, ImeEventReceiver, ImeRequest};
|
||||||
|
@ -38,7 +42,7 @@ pub(super) struct EventProcessor<T: 'static> {
|
||||||
pub(super) held_key_press: Option<u32>,
|
pub(super) held_key_press: Option<u32>,
|
||||||
pub(super) first_touch: Option<u64>,
|
pub(super) first_touch: Option<u64>,
|
||||||
// Currently focused window belonging to this process
|
// Currently focused window belonging to this process
|
||||||
pub(super) active_window: Option<ffi::Window>,
|
pub(super) active_window: Option<xproto::Window>,
|
||||||
pub(super) is_composing: bool,
|
pub(super) is_composing: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -53,7 +57,7 @@ impl<T: 'static> EventProcessor<T> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn with_window<F, Ret>(&self, window_id: ffi::Window, callback: F) -> Option<Ret>
|
fn with_window<F, Ret>(&self, window_id: xproto::Window, callback: F) -> Option<Ret>
|
||||||
where
|
where
|
||||||
F: Fn(&Arc<UnownedWindow>) -> Ret,
|
F: Fn(&Arc<UnownedWindow>) -> Ret,
|
||||||
{
|
{
|
||||||
|
@ -77,7 +81,7 @@ impl<T: 'static> EventProcessor<T> {
|
||||||
result
|
result
|
||||||
}
|
}
|
||||||
|
|
||||||
fn window_exists(&self, window_id: ffi::Window) -> bool {
|
fn window_exists(&self, window_id: xproto::Window) -> bool {
|
||||||
self.with_window(window_id, |_| ()).is_some()
|
self.with_window(window_id, |_| ()).is_some()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -120,6 +124,7 @@ impl<T: 'static> EventProcessor<T> {
|
||||||
F: FnMut(Event<'_, T>),
|
F: FnMut(Event<'_, T>),
|
||||||
{
|
{
|
||||||
let wt = get_xtarget(&self.target);
|
let wt = get_xtarget(&self.target);
|
||||||
|
let atoms = wt.x_connection().atoms();
|
||||||
// XFilterEvent tells us when an event has been discarded by the input method.
|
// XFilterEvent tells us when an event has been discarded by the input method.
|
||||||
// Specifically, this involves all of the KeyPress events in compose/pre-edit sequences,
|
// Specifically, this involves all of the KeyPress events in compose/pre-edit sequences,
|
||||||
// along with an extra copy of the KeyRelease events. This also prevents backspace and
|
// along with an extra copy of the KeyRelease events. This also prevents backspace and
|
||||||
|
@ -140,42 +145,57 @@ impl<T: 'static> EventProcessor<T> {
|
||||||
ffi::ClientMessage => {
|
ffi::ClientMessage => {
|
||||||
let client_msg: &ffi::XClientMessageEvent = xev.as_ref();
|
let client_msg: &ffi::XClientMessageEvent = xev.as_ref();
|
||||||
|
|
||||||
let window = client_msg.window;
|
let window = client_msg.window as xproto::Window;
|
||||||
let window_id = mkwid(window);
|
let window_id = mkwid(window);
|
||||||
|
|
||||||
if client_msg.data.get_long(0) as ffi::Atom == wt.wm_delete_window {
|
if client_msg.data.get_long(0) as xproto::Atom == wt.wm_delete_window {
|
||||||
callback(Event::WindowEvent {
|
callback(Event::WindowEvent {
|
||||||
window_id,
|
window_id,
|
||||||
event: WindowEvent::CloseRequested,
|
event: WindowEvent::CloseRequested,
|
||||||
});
|
});
|
||||||
} else if client_msg.data.get_long(0) as ffi::Atom == wt.net_wm_ping {
|
} else if client_msg.data.get_long(0) as xproto::Atom == wt.net_wm_ping {
|
||||||
let response_msg: &mut ffi::XClientMessageEvent = xev.as_mut();
|
let response_msg: &mut ffi::XClientMessageEvent = xev.as_mut();
|
||||||
response_msg.window = wt.root;
|
let client_msg = xproto::ClientMessageEvent {
|
||||||
|
response_type: xproto::CLIENT_MESSAGE_EVENT,
|
||||||
|
format: response_msg.format as _,
|
||||||
|
sequence: response_msg.serial as _,
|
||||||
|
window: wt.root,
|
||||||
|
type_: response_msg.message_type as _,
|
||||||
|
data: xproto::ClientMessageData::from({
|
||||||
|
let [a, b, c, d, e]: [c_long; 5] =
|
||||||
|
response_msg.data.as_longs().try_into().unwrap();
|
||||||
|
[a as u32, b as u32, c as u32, d as u32, e as u32]
|
||||||
|
}),
|
||||||
|
};
|
||||||
|
|
||||||
wt.xconn
|
wt.xconn
|
||||||
|
.xcb_connection()
|
||||||
.send_event(
|
.send_event(
|
||||||
|
false,
|
||||||
wt.root,
|
wt.root,
|
||||||
Some(ffi::SubstructureNotifyMask | ffi::SubstructureRedirectMask),
|
xproto::EventMask::SUBSTRUCTURE_NOTIFY
|
||||||
*response_msg,
|
| xproto::EventMask::SUBSTRUCTURE_REDIRECT,
|
||||||
|
client_msg.serialize(),
|
||||||
)
|
)
|
||||||
.queue();
|
.expect_then_ignore_error("Failed to send `ClientMessage` event.");
|
||||||
} else if client_msg.message_type == self.dnd.atoms.enter {
|
} else if client_msg.message_type == atoms[XdndEnter] as c_ulong {
|
||||||
let source_window = client_msg.data.get_long(0) as c_ulong;
|
let source_window = client_msg.data.get_long(0) as xproto::Window;
|
||||||
let flags = client_msg.data.get_long(1);
|
let flags = client_msg.data.get_long(1);
|
||||||
let version = flags >> 24;
|
let version = flags >> 24;
|
||||||
self.dnd.version = Some(version);
|
self.dnd.version = Some(version);
|
||||||
let has_more_types = flags - (flags & (c_long::max_value() - 1)) == 1;
|
let has_more_types = flags - (flags & (c_long::max_value() - 1)) == 1;
|
||||||
if !has_more_types {
|
if !has_more_types {
|
||||||
let type_list = vec![
|
let type_list = vec![
|
||||||
client_msg.data.get_long(2) as c_ulong,
|
client_msg.data.get_long(2) as xproto::Atom,
|
||||||
client_msg.data.get_long(3) as c_ulong,
|
client_msg.data.get_long(3) as xproto::Atom,
|
||||||
client_msg.data.get_long(4) as c_ulong,
|
client_msg.data.get_long(4) as xproto::Atom,
|
||||||
];
|
];
|
||||||
self.dnd.type_list = Some(type_list);
|
self.dnd.type_list = Some(type_list);
|
||||||
} else if let Ok(more_types) = unsafe { self.dnd.get_type_list(source_window) }
|
} else if let Ok(more_types) = unsafe { self.dnd.get_type_list(source_window) }
|
||||||
{
|
{
|
||||||
self.dnd.type_list = Some(more_types);
|
self.dnd.type_list = Some(more_types);
|
||||||
}
|
}
|
||||||
} else if client_msg.message_type == self.dnd.atoms.position {
|
} else if client_msg.message_type == atoms[XdndPosition] as c_ulong {
|
||||||
// This event occurs every time the mouse moves while a file's being dragged
|
// This event occurs every time the mouse moves while a file's being dragged
|
||||||
// over our window. We emit HoveredFile in response; while the macOS backend
|
// over our window. We emit HoveredFile in response; while the macOS backend
|
||||||
// does that upon a drag entering, XDND doesn't have access to the actual drop
|
// does that upon a drag entering, XDND doesn't have access to the actual drop
|
||||||
|
@ -184,7 +204,7 @@ impl<T: 'static> EventProcessor<T> {
|
||||||
// supply position updates with `HoveredFile` or another event, implementing
|
// supply position updates with `HoveredFile` or another event, implementing
|
||||||
// that here would be trivial.
|
// that here would be trivial.
|
||||||
|
|
||||||
let source_window = client_msg.data.get_long(0) as c_ulong;
|
let source_window = client_msg.data.get_long(0) as xproto::Window;
|
||||||
|
|
||||||
// Equivalent to `(x << shift) | y`
|
// Equivalent to `(x << shift) | y`
|
||||||
// where `shift = mem::size_of::<c_short>() * 8`
|
// where `shift = mem::size_of::<c_short>() * 8`
|
||||||
|
@ -202,7 +222,7 @@ impl<T: 'static> EventProcessor<T> {
|
||||||
//let action = client_msg.data.get_long(4);
|
//let action = client_msg.data.get_long(4);
|
||||||
|
|
||||||
let accepted = if let Some(ref type_list) = self.dnd.type_list {
|
let accepted = if let Some(ref type_list) = self.dnd.type_list {
|
||||||
type_list.contains(&self.dnd.atoms.uri_list)
|
type_list.contains(&atoms[TextUriList])
|
||||||
} else {
|
} else {
|
||||||
false
|
false
|
||||||
};
|
};
|
||||||
|
@ -212,10 +232,10 @@ impl<T: 'static> EventProcessor<T> {
|
||||||
unsafe {
|
unsafe {
|
||||||
if self.dnd.result.is_none() {
|
if self.dnd.result.is_none() {
|
||||||
let time = if version >= 1 {
|
let time = if version >= 1 {
|
||||||
client_msg.data.get_long(3) as c_ulong
|
client_msg.data.get_long(3) as xproto::Timestamp
|
||||||
} else {
|
} else {
|
||||||
// In version 0, time isn't specified
|
// In version 0, time isn't specified
|
||||||
ffi::CurrentTime
|
x11rb::CURRENT_TIME
|
||||||
};
|
};
|
||||||
// This results in the `SelectionNotify` event below
|
// This results in the `SelectionNotify` event below
|
||||||
self.dnd.convert_selection(window, time);
|
self.dnd.convert_selection(window, time);
|
||||||
|
@ -232,7 +252,7 @@ impl<T: 'static> EventProcessor<T> {
|
||||||
}
|
}
|
||||||
self.dnd.reset();
|
self.dnd.reset();
|
||||||
}
|
}
|
||||||
} else if client_msg.message_type == self.dnd.atoms.drop {
|
} else if client_msg.message_type == atoms[XdndDrop] as c_ulong {
|
||||||
let (source_window, state) = if let Some(source_window) = self.dnd.source_window
|
let (source_window, state) = if let Some(source_window) = self.dnd.source_window
|
||||||
{
|
{
|
||||||
if let Some(Ok(ref path_list)) = self.dnd.result {
|
if let Some(Ok(ref path_list)) = self.dnd.result {
|
||||||
|
@ -247,7 +267,7 @@ impl<T: 'static> EventProcessor<T> {
|
||||||
} else {
|
} else {
|
||||||
// `source_window` won't be part of our DND state if we already rejected the drop in our
|
// `source_window` won't be part of our DND state if we already rejected the drop in our
|
||||||
// `XdndPosition` handler.
|
// `XdndPosition` handler.
|
||||||
let source_window = client_msg.data.get_long(0) as c_ulong;
|
let source_window = client_msg.data.get_long(0) as xproto::Window;
|
||||||
(source_window, DndState::Rejected)
|
(source_window, DndState::Rejected)
|
||||||
};
|
};
|
||||||
unsafe {
|
unsafe {
|
||||||
|
@ -256,7 +276,7 @@ impl<T: 'static> EventProcessor<T> {
|
||||||
.expect("Failed to send `XdndFinished` message.");
|
.expect("Failed to send `XdndFinished` message.");
|
||||||
}
|
}
|
||||||
self.dnd.reset();
|
self.dnd.reset();
|
||||||
} else if client_msg.message_type == self.dnd.atoms.leave {
|
} else if client_msg.message_type == atoms[XdndLeave] as c_ulong {
|
||||||
self.dnd.reset();
|
self.dnd.reset();
|
||||||
callback(Event::WindowEvent {
|
callback(Event::WindowEvent {
|
||||||
window_id,
|
window_id,
|
||||||
|
@ -268,10 +288,10 @@ impl<T: 'static> EventProcessor<T> {
|
||||||
ffi::SelectionNotify => {
|
ffi::SelectionNotify => {
|
||||||
let xsel: &ffi::XSelectionEvent = xev.as_ref();
|
let xsel: &ffi::XSelectionEvent = xev.as_ref();
|
||||||
|
|
||||||
let window = xsel.requestor;
|
let window = xsel.requestor as xproto::Window;
|
||||||
let window_id = mkwid(window);
|
let window_id = mkwid(window);
|
||||||
|
|
||||||
if xsel.property == self.dnd.atoms.selection {
|
if xsel.property == atoms[XdndSelection] as c_ulong {
|
||||||
let mut result = None;
|
let mut result = None;
|
||||||
|
|
||||||
// This is where we receive data from drag and drop
|
// This is where we receive data from drag and drop
|
||||||
|
@ -294,7 +314,7 @@ impl<T: 'static> EventProcessor<T> {
|
||||||
|
|
||||||
ffi::ConfigureNotify => {
|
ffi::ConfigureNotify => {
|
||||||
let xev: &ffi::XConfigureEvent = xev.as_ref();
|
let xev: &ffi::XConfigureEvent = xev.as_ref();
|
||||||
let xwindow = xev.window;
|
let xwindow = xev.window as xproto::Window;
|
||||||
let window_id = mkwid(xwindow);
|
let window_id = mkwid(xwindow);
|
||||||
|
|
||||||
if let Some(window) = self.with_window(xwindow, Arc::clone) {
|
if let Some(window) = self.with_window(xwindow, Arc::clone) {
|
||||||
|
@ -469,13 +489,13 @@ impl<T: 'static> EventProcessor<T> {
|
||||||
// effect is that we waste some time trying to query unsupported properties.
|
// effect is that we waste some time trying to query unsupported properties.
|
||||||
wt.xconn.update_cached_wm_info(wt.root);
|
wt.xconn.update_cached_wm_info(wt.root);
|
||||||
|
|
||||||
self.with_window(xev.window, |window| {
|
self.with_window(xev.window as xproto::Window, |window| {
|
||||||
window.invalidate_cached_frame_extents();
|
window.invalidate_cached_frame_extents();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
ffi::MapNotify => {
|
ffi::MapNotify => {
|
||||||
let xev: &ffi::XMapEvent = xev.as_ref();
|
let xev: &ffi::XMapEvent = xev.as_ref();
|
||||||
let window = xev.window;
|
let window = xev.window as xproto::Window;
|
||||||
let window_id = mkwid(window);
|
let window_id = mkwid(window);
|
||||||
|
|
||||||
// XXX re-issue the focus state when mapping the window.
|
// XXX re-issue the focus state when mapping the window.
|
||||||
|
@ -494,7 +514,7 @@ impl<T: 'static> EventProcessor<T> {
|
||||||
ffi::DestroyNotify => {
|
ffi::DestroyNotify => {
|
||||||
let xev: &ffi::XDestroyWindowEvent = xev.as_ref();
|
let xev: &ffi::XDestroyWindowEvent = xev.as_ref();
|
||||||
|
|
||||||
let window = xev.window;
|
let window = xev.window as xproto::Window;
|
||||||
let window_id = mkwid(window);
|
let window_id = mkwid(window);
|
||||||
|
|
||||||
// In the event that the window's been destroyed without being dropped first, we
|
// In the event that the window's been destroyed without being dropped first, we
|
||||||
|
@ -505,7 +525,7 @@ impl<T: 'static> EventProcessor<T> {
|
||||||
// context here instead of when dropping the window.
|
// context here instead of when dropping the window.
|
||||||
wt.ime
|
wt.ime
|
||||||
.borrow_mut()
|
.borrow_mut()
|
||||||
.remove_context(window)
|
.remove_context(window as ffi::Window)
|
||||||
.expect("Failed to destroy input context");
|
.expect("Failed to destroy input context");
|
||||||
|
|
||||||
callback(Event::WindowEvent {
|
callback(Event::WindowEvent {
|
||||||
|
@ -516,7 +536,7 @@ impl<T: 'static> EventProcessor<T> {
|
||||||
|
|
||||||
ffi::VisibilityNotify => {
|
ffi::VisibilityNotify => {
|
||||||
let xev: &ffi::XVisibilityEvent = xev.as_ref();
|
let xev: &ffi::XVisibilityEvent = xev.as_ref();
|
||||||
let xwindow = xev.window;
|
let xwindow = xev.window as xproto::Window;
|
||||||
callback(Event::WindowEvent {
|
callback(Event::WindowEvent {
|
||||||
window_id: mkwid(xwindow),
|
window_id: mkwid(xwindow),
|
||||||
event: WindowEvent::Occluded(xev.state == ffi::VisibilityFullyObscured),
|
event: WindowEvent::Occluded(xev.state == ffi::VisibilityFullyObscured),
|
||||||
|
@ -532,7 +552,7 @@ impl<T: 'static> EventProcessor<T> {
|
||||||
// Multiple Expose events may be received for subareas of a window.
|
// Multiple Expose events may be received for subareas of a window.
|
||||||
// We issue `RedrawRequested` only for the last event of such a series.
|
// We issue `RedrawRequested` only for the last event of such a series.
|
||||||
if xev.count == 0 {
|
if xev.count == 0 {
|
||||||
let window = xev.window;
|
let window = xev.window as xproto::Window;
|
||||||
let window_id = mkwid(window);
|
let window_id = mkwid(window);
|
||||||
|
|
||||||
callback(Event::RedrawRequested(window_id));
|
callback(Event::RedrawRequested(window_id));
|
||||||
|
@ -548,7 +568,7 @@ impl<T: 'static> EventProcessor<T> {
|
||||||
};
|
};
|
||||||
|
|
||||||
let window_id = mkwid(window);
|
let window_id = mkwid(window);
|
||||||
let device_id = mkdid(util::VIRTUAL_CORE_KEYBOARD);
|
let device_id = mkdid(util::VIRTUAL_CORE_KEYBOARD.into());
|
||||||
|
|
||||||
let keycode = xkev.keycode as _;
|
let keycode = xkev.keycode as _;
|
||||||
|
|
||||||
|
@ -597,7 +617,7 @@ impl<T: 'static> EventProcessor<T> {
|
||||||
is_synthetic: false,
|
is_synthetic: false,
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
} else if let Some(ic) = wt.ime.borrow().get_context(window) {
|
} else if let Some(ic) = wt.ime.borrow().get_context(window as ffi::Window) {
|
||||||
let written = wt.xconn.lookup_utf8(ic, xkev);
|
let written = wt.xconn.lookup_utf8(ic, xkev);
|
||||||
if !written.is_empty() {
|
if !written.is_empty() {
|
||||||
let event = Event::WindowEvent {
|
let event = Event::WindowEvent {
|
||||||
|
@ -642,7 +662,7 @@ impl<T: 'static> EventProcessor<T> {
|
||||||
match xev.evtype {
|
match xev.evtype {
|
||||||
ffi::XI_ButtonPress | ffi::XI_ButtonRelease => {
|
ffi::XI_ButtonPress | ffi::XI_ButtonRelease => {
|
||||||
let xev: &ffi::XIDeviceEvent = unsafe { &*(xev.data as *const _) };
|
let xev: &ffi::XIDeviceEvent = unsafe { &*(xev.data as *const _) };
|
||||||
let window_id = mkwid(xev.event);
|
let window_id = mkwid(xev.event as xproto::Window);
|
||||||
let device_id = mkdid(xev.deviceid);
|
let device_id = mkdid(xev.deviceid);
|
||||||
if (xev.flags & ffi::XIPointerEmulated) != 0 {
|
if (xev.flags & ffi::XIPointerEmulated) != 0 {
|
||||||
// Deliver multi-touch events instead of emulated mouse events.
|
// Deliver multi-touch events instead of emulated mouse events.
|
||||||
|
@ -732,10 +752,11 @@ impl<T: 'static> EventProcessor<T> {
|
||||||
ffi::XI_Motion => {
|
ffi::XI_Motion => {
|
||||||
let xev: &ffi::XIDeviceEvent = unsafe { &*(xev.data as *const _) };
|
let xev: &ffi::XIDeviceEvent = unsafe { &*(xev.data as *const _) };
|
||||||
let device_id = mkdid(xev.deviceid);
|
let device_id = mkdid(xev.deviceid);
|
||||||
let window_id = mkwid(xev.event);
|
let window = xev.event as xproto::Window;
|
||||||
|
let window_id = mkwid(window);
|
||||||
let new_cursor_pos = (xev.event_x, xev.event_y);
|
let new_cursor_pos = (xev.event_x, xev.event_y);
|
||||||
|
|
||||||
let cursor_moved = self.with_window(xev.event, |window| {
|
let cursor_moved = self.with_window(window, |window| {
|
||||||
let mut shared_state_lock = window.shared_state_lock();
|
let mut shared_state_lock = window.shared_state_lock();
|
||||||
util::maybe_change(&mut shared_state_lock.cursor_pos, new_cursor_pos)
|
util::maybe_change(&mut shared_state_lock.cursor_pos, new_cursor_pos)
|
||||||
});
|
});
|
||||||
|
@ -817,7 +838,8 @@ impl<T: 'static> EventProcessor<T> {
|
||||||
ffi::XI_Enter => {
|
ffi::XI_Enter => {
|
||||||
let xev: &ffi::XIEnterEvent = unsafe { &*(xev.data as *const _) };
|
let xev: &ffi::XIEnterEvent = unsafe { &*(xev.data as *const _) };
|
||||||
|
|
||||||
let window_id = mkwid(xev.event);
|
let window = xev.event as xproto::Window;
|
||||||
|
let window_id = mkwid(window);
|
||||||
let device_id = mkdid(xev.deviceid);
|
let device_id = mkdid(xev.deviceid);
|
||||||
|
|
||||||
if let Some(all_info) = DeviceInfo::get(&wt.xconn, ffi::XIAllDevices) {
|
if let Some(all_info) = DeviceInfo::get(&wt.xconn, ffi::XIAllDevices) {
|
||||||
|
@ -838,7 +860,7 @@ impl<T: 'static> EventProcessor<T> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if self.window_exists(xev.event) {
|
if self.window_exists(window) {
|
||||||
callback(Event::WindowEvent {
|
callback(Event::WindowEvent {
|
||||||
window_id,
|
window_id,
|
||||||
event: CursorEntered { device_id },
|
event: CursorEntered { device_id },
|
||||||
|
@ -857,13 +879,14 @@ impl<T: 'static> EventProcessor<T> {
|
||||||
}
|
}
|
||||||
ffi::XI_Leave => {
|
ffi::XI_Leave => {
|
||||||
let xev: &ffi::XILeaveEvent = unsafe { &*(xev.data as *const _) };
|
let xev: &ffi::XILeaveEvent = unsafe { &*(xev.data as *const _) };
|
||||||
|
let window = xev.event as xproto::Window;
|
||||||
|
|
||||||
// Leave, FocusIn, and FocusOut can be received by a window that's already
|
// Leave, FocusIn, and FocusOut can be received by a window that's already
|
||||||
// been destroyed, which the user presumably doesn't want to deal with.
|
// been destroyed, which the user presumably doesn't want to deal with.
|
||||||
let window_closed = !self.window_exists(xev.event);
|
let window_closed = !self.window_exists(window);
|
||||||
if !window_closed {
|
if !window_closed {
|
||||||
callback(Event::WindowEvent {
|
callback(Event::WindowEvent {
|
||||||
window_id: mkwid(xev.event),
|
window_id: mkwid(window),
|
||||||
event: CursorLeft {
|
event: CursorLeft {
|
||||||
device_id: mkdid(xev.deviceid),
|
device_id: mkdid(xev.deviceid),
|
||||||
},
|
},
|
||||||
|
@ -872,21 +895,22 @@ impl<T: 'static> EventProcessor<T> {
|
||||||
}
|
}
|
||||||
ffi::XI_FocusIn => {
|
ffi::XI_FocusIn => {
|
||||||
let xev: &ffi::XIFocusInEvent = unsafe { &*(xev.data as *const _) };
|
let xev: &ffi::XIFocusInEvent = unsafe { &*(xev.data as *const _) };
|
||||||
|
let window = xev.event as xproto::Window;
|
||||||
|
|
||||||
wt.ime
|
wt.ime
|
||||||
.borrow_mut()
|
.borrow_mut()
|
||||||
.focus(xev.event)
|
.focus(xev.event)
|
||||||
.expect("Failed to focus input context");
|
.expect("Failed to focus input context");
|
||||||
|
|
||||||
if self.active_window != Some(xev.event) {
|
if self.active_window != Some(window) {
|
||||||
self.active_window = Some(xev.event);
|
self.active_window = Some(window);
|
||||||
|
|
||||||
wt.update_listen_device_events(true);
|
wt.update_listen_device_events(true);
|
||||||
|
|
||||||
let window_id = mkwid(xev.event);
|
let window_id = mkwid(window);
|
||||||
let position = PhysicalPosition::new(xev.event_x, xev.event_y);
|
let position = PhysicalPosition::new(xev.event_x, xev.event_y);
|
||||||
|
|
||||||
if let Some(window) = self.with_window(xev.event, Arc::clone) {
|
if let Some(window) = self.with_window(window, Arc::clone) {
|
||||||
window.shared_state_lock().has_focus = true;
|
window.shared_state_lock().has_focus = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -933,7 +957,8 @@ impl<T: 'static> EventProcessor<T> {
|
||||||
}
|
}
|
||||||
ffi::XI_FocusOut => {
|
ffi::XI_FocusOut => {
|
||||||
let xev: &ffi::XIFocusOutEvent = unsafe { &*(xev.data as *const _) };
|
let xev: &ffi::XIFocusOutEvent = unsafe { &*(xev.data as *const _) };
|
||||||
if !self.window_exists(xev.event) {
|
let window = xev.event as xproto::Window;
|
||||||
|
if !self.window_exists(window) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -942,8 +967,8 @@ impl<T: 'static> EventProcessor<T> {
|
||||||
.unfocus(xev.event)
|
.unfocus(xev.event)
|
||||||
.expect("Failed to unfocus input context");
|
.expect("Failed to unfocus input context");
|
||||||
|
|
||||||
if self.active_window.take() == Some(xev.event) {
|
if self.active_window.take() == Some(window) {
|
||||||
let window_id = mkwid(xev.event);
|
let window_id = mkwid(window);
|
||||||
|
|
||||||
wt.update_listen_device_events(false);
|
wt.update_listen_device_events(false);
|
||||||
|
|
||||||
|
@ -966,7 +991,7 @@ impl<T: 'static> EventProcessor<T> {
|
||||||
),
|
),
|
||||||
});
|
});
|
||||||
|
|
||||||
if let Some(window) = self.with_window(xev.event, Arc::clone) {
|
if let Some(window) = self.with_window(window, Arc::clone) {
|
||||||
window.shared_state_lock().has_focus = false;
|
window.shared_state_lock().has_focus = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -979,14 +1004,15 @@ impl<T: 'static> EventProcessor<T> {
|
||||||
|
|
||||||
ffi::XI_TouchBegin | ffi::XI_TouchUpdate | ffi::XI_TouchEnd => {
|
ffi::XI_TouchBegin | ffi::XI_TouchUpdate | ffi::XI_TouchEnd => {
|
||||||
let xev: &ffi::XIDeviceEvent = unsafe { &*(xev.data as *const _) };
|
let xev: &ffi::XIDeviceEvent = unsafe { &*(xev.data as *const _) };
|
||||||
let window_id = mkwid(xev.event);
|
let window = xev.event as xproto::Window;
|
||||||
|
let window_id = mkwid(window);
|
||||||
let phase = match xev.evtype {
|
let phase = match xev.evtype {
|
||||||
ffi::XI_TouchBegin => TouchPhase::Started,
|
ffi::XI_TouchBegin => TouchPhase::Started,
|
||||||
ffi::XI_TouchUpdate => TouchPhase::Moved,
|
ffi::XI_TouchUpdate => TouchPhase::Moved,
|
||||||
ffi::XI_TouchEnd => TouchPhase::Ended,
|
ffi::XI_TouchEnd => TouchPhase::Ended,
|
||||||
_ => unreachable!(),
|
_ => unreachable!(),
|
||||||
};
|
};
|
||||||
if self.window_exists(xev.event) {
|
if self.window_exists(window) {
|
||||||
let id = xev.detail as u64;
|
let id = xev.detail as u64;
|
||||||
let location = PhysicalPosition::new(xev.event_x, xev.event_y);
|
let location = PhysicalPosition::new(xev.event_x, xev.event_y);
|
||||||
|
|
||||||
|
@ -997,7 +1023,7 @@ impl<T: 'static> EventProcessor<T> {
|
||||||
callback(Event::WindowEvent {
|
callback(Event::WindowEvent {
|
||||||
window_id,
|
window_id,
|
||||||
event: WindowEvent::CursorMoved {
|
event: WindowEvent::CursorMoved {
|
||||||
device_id: mkdid(util::VIRTUAL_CORE_POINTER),
|
device_id: mkdid(util::VIRTUAL_CORE_POINTER.into()),
|
||||||
position: location.cast(),
|
position: location.cast(),
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
@ -1260,7 +1286,7 @@ impl<T: 'static> EventProcessor<T> {
|
||||||
}
|
}
|
||||||
|
|
||||||
let (window, event) = match self.ime_event_receiver.try_recv() {
|
let (window, event) = match self.ime_event_receiver.try_recv() {
|
||||||
Ok((window, event)) => (window, event),
|
Ok((window, event)) => (window as xproto::Window, event),
|
||||||
Err(_) => return,
|
Err(_) => return,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -1313,7 +1339,7 @@ impl<T: 'static> EventProcessor<T> {
|
||||||
) where
|
) where
|
||||||
F: FnMut(Event<'_, T>),
|
F: FnMut(Event<'_, T>),
|
||||||
{
|
{
|
||||||
let device_id = mkdid(util::VIRTUAL_CORE_KEYBOARD);
|
let device_id = mkdid(util::VIRTUAL_CORE_KEYBOARD.into());
|
||||||
|
|
||||||
// Update modifiers state and emit key events based on which keys are currently pressed.
|
// Update modifiers state and emit key events based on which keys are currently pressed.
|
||||||
for keycode in wt
|
for keycode in wt
|
||||||
|
|
|
@ -274,7 +274,7 @@ impl ImeContext {
|
||||||
client_data: ffi::XPointer,
|
client_data: ffi::XPointer,
|
||||||
) -> Option<ffi::XIC> {
|
) -> Option<ffi::XIC> {
|
||||||
let preedit_callbacks = PreeditCallbacks::new(client_data);
|
let preedit_callbacks = PreeditCallbacks::new(client_data);
|
||||||
let preedit_attr = util::XSmartPointer::new(
|
let preedit_attr = util::memory::XSmartPointer::new(
|
||||||
xconn,
|
xconn,
|
||||||
(xconn.xlib.XVaCreateNestedList)(
|
(xconn.xlib.XVaCreateNestedList)(
|
||||||
0,
|
0,
|
||||||
|
@ -354,7 +354,7 @@ impl ImeContext {
|
||||||
self.ic_spot = ffi::XPoint { x, y };
|
self.ic_spot = ffi::XPoint { x, y };
|
||||||
|
|
||||||
unsafe {
|
unsafe {
|
||||||
let preedit_attr = util::XSmartPointer::new(
|
let preedit_attr = util::memory::XSmartPointer::new(
|
||||||
xconn,
|
xconn,
|
||||||
(xconn.xlib.XVaCreateNestedList)(
|
(xconn.xlib.XVaCreateNestedList)(
|
||||||
0,
|
0,
|
||||||
|
|
|
@ -7,9 +7,9 @@ use std::{
|
||||||
sync::{Arc, Mutex},
|
sync::{Arc, Mutex},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
use super::{super::atoms::*, ffi, util, XConnection, XError};
|
||||||
use once_cell::sync::Lazy;
|
use once_cell::sync::Lazy;
|
||||||
|
use x11rb::protocol::xproto;
|
||||||
use super::{ffi, util, XConnection, XError};
|
|
||||||
|
|
||||||
static GLOBAL_LOCK: Lazy<Mutex<()>> = Lazy::new(Default::default);
|
static GLOBAL_LOCK: Lazy<Mutex<()>> = Lazy::new(Default::default);
|
||||||
|
|
||||||
|
@ -162,6 +162,12 @@ enum GetXimServersError {
|
||||||
InvalidUtf8(IntoStringError),
|
InvalidUtf8(IntoStringError),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl From<util::GetPropertyError> for GetXimServersError {
|
||||||
|
fn from(error: util::GetPropertyError) -> Self {
|
||||||
|
GetXimServersError::GetPropertyError(error)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// The root window has a property named XIM_SERVERS, which contains a list of atoms represeting
|
// The root window has a property named XIM_SERVERS, which contains a list of atoms represeting
|
||||||
// the availabile XIM servers. For instance, if you're using ibus, it would contain an atom named
|
// the availabile XIM servers. For instance, if you're using ibus, it would contain an atom named
|
||||||
// "@server=ibus". It's possible for this property to contain multiple atoms, though presumably
|
// "@server=ibus". It's possible for this property to contain multiple atoms, though presumably
|
||||||
|
@ -169,13 +175,21 @@ enum GetXimServersError {
|
||||||
// modifiers, since we don't want a user who's looking at logs to ask "am I supposed to set
|
// modifiers, since we don't want a user who's looking at logs to ask "am I supposed to set
|
||||||
// XMODIFIERS to `@server=ibus`?!?"
|
// XMODIFIERS to `@server=ibus`?!?"
|
||||||
unsafe fn get_xim_servers(xconn: &Arc<XConnection>) -> Result<Vec<String>, GetXimServersError> {
|
unsafe fn get_xim_servers(xconn: &Arc<XConnection>) -> Result<Vec<String>, GetXimServersError> {
|
||||||
let servers_atom = xconn.get_atom_unchecked(b"XIM_SERVERS\0");
|
let atoms = xconn.atoms();
|
||||||
|
let servers_atom = atoms[XIM_SERVERS];
|
||||||
|
|
||||||
let root = (xconn.xlib.XDefaultRootWindow)(xconn.display);
|
let root = (xconn.xlib.XDefaultRootWindow)(xconn.display);
|
||||||
|
|
||||||
let mut atoms: Vec<ffi::Atom> = xconn
|
let mut atoms: Vec<ffi::Atom> = xconn
|
||||||
.get_property(root, servers_atom, ffi::XA_ATOM)
|
.get_property::<xproto::Atom>(
|
||||||
.map_err(GetXimServersError::GetPropertyError)?;
|
root as xproto::Window,
|
||||||
|
servers_atom,
|
||||||
|
xproto::Atom::from(xproto::AtomEnum::ATOM),
|
||||||
|
)
|
||||||
|
.map_err(GetXimServersError::GetPropertyError)?
|
||||||
|
.into_iter()
|
||||||
|
.map(ffi::Atom::from)
|
||||||
|
.collect::<Vec<_>>();
|
||||||
|
|
||||||
let mut names: Vec<*const c_char> = Vec::with_capacity(atoms.len());
|
let mut names: Vec<*const c_char> = Vec::with_capacity(atoms.len());
|
||||||
(xconn.xlib.XGetAtomNames)(
|
(xconn.xlib.XGetAtomNames)(
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
#![cfg(x11_platform)]
|
#![cfg(x11_platform)]
|
||||||
|
|
||||||
|
mod atoms;
|
||||||
mod dnd;
|
mod dnd;
|
||||||
mod event_processor;
|
mod event_processor;
|
||||||
pub mod ffi;
|
pub mod ffi;
|
||||||
|
@ -25,10 +26,13 @@ use std::{
|
||||||
cell::{Cell, RefCell},
|
cell::{Cell, RefCell},
|
||||||
collections::{HashMap, HashSet, VecDeque},
|
collections::{HashMap, HashSet, VecDeque},
|
||||||
ffi::CStr,
|
ffi::CStr,
|
||||||
|
fmt,
|
||||||
mem::{self, MaybeUninit},
|
mem::{self, MaybeUninit},
|
||||||
ops::Deref,
|
ops::Deref,
|
||||||
os::raw::*,
|
os::{
|
||||||
os::unix::io::RawFd,
|
raw::*,
|
||||||
|
unix::io::{AsRawFd, RawFd},
|
||||||
|
},
|
||||||
ptr,
|
ptr,
|
||||||
rc::Rc,
|
rc::Rc,
|
||||||
slice,
|
slice,
|
||||||
|
@ -37,8 +41,20 @@ use std::{
|
||||||
};
|
};
|
||||||
|
|
||||||
use libc::{self, setlocale, LC_CTYPE};
|
use libc::{self, setlocale, LC_CTYPE};
|
||||||
|
|
||||||
|
use atoms::*;
|
||||||
use raw_window_handle::{RawDisplayHandle, XlibDisplayHandle};
|
use raw_window_handle::{RawDisplayHandle, XlibDisplayHandle};
|
||||||
|
|
||||||
|
use x11rb::protocol::{
|
||||||
|
xinput,
|
||||||
|
xproto::{self, ConnectionExt},
|
||||||
|
};
|
||||||
|
use x11rb::x11_utils::X11Error as LogicalError;
|
||||||
|
use x11rb::{
|
||||||
|
errors::{ConnectError, ConnectionError, IdsExhausted, ReplyError},
|
||||||
|
xcb_ffi::ReplyOrIdError,
|
||||||
|
};
|
||||||
|
|
||||||
use self::{
|
use self::{
|
||||||
dnd::{Dnd, DndState},
|
dnd::{Dnd, DndState},
|
||||||
event_processor::EventProcessor,
|
event_processor::EventProcessor,
|
||||||
|
@ -60,10 +76,10 @@ type X11Source = Generic<RawFd>;
|
||||||
|
|
||||||
pub struct EventLoopWindowTarget<T> {
|
pub struct EventLoopWindowTarget<T> {
|
||||||
xconn: Arc<XConnection>,
|
xconn: Arc<XConnection>,
|
||||||
wm_delete_window: ffi::Atom,
|
wm_delete_window: xproto::Atom,
|
||||||
net_wm_ping: ffi::Atom,
|
net_wm_ping: xproto::Atom,
|
||||||
ime_sender: ImeSender,
|
ime_sender: ImeSender,
|
||||||
root: ffi::Window,
|
root: xproto::Window,
|
||||||
ime: RefCell<Ime>,
|
ime: RefCell<Ime>,
|
||||||
windows: RefCell<HashMap<WindowId, Weak<UnownedWindow>>>,
|
windows: RefCell<HashMap<WindowId, Weak<UnownedWindow>>>,
|
||||||
redraw_sender: Sender<WindowId>,
|
redraw_sender: Sender<WindowId>,
|
||||||
|
@ -106,11 +122,11 @@ impl<T: 'static> Clone for EventLoopProxy<T> {
|
||||||
|
|
||||||
impl<T: 'static> EventLoop<T> {
|
impl<T: 'static> EventLoop<T> {
|
||||||
pub(crate) fn new(xconn: Arc<XConnection>) -> EventLoop<T> {
|
pub(crate) fn new(xconn: Arc<XConnection>) -> EventLoop<T> {
|
||||||
let root = unsafe { (xconn.xlib.XDefaultRootWindow)(xconn.display) };
|
let root = xconn.default_root().root;
|
||||||
|
let atoms = xconn.atoms();
|
||||||
|
|
||||||
let wm_delete_window = unsafe { xconn.get_atom_unchecked(b"WM_DELETE_WINDOW\0") };
|
let wm_delete_window = atoms[WM_DELETE_WINDOW];
|
||||||
|
let net_wm_ping = atoms[_NET_WM_PING];
|
||||||
let net_wm_ping = unsafe { xconn.get_atom_unchecked(b"_NET_WM_PING\0") };
|
|
||||||
|
|
||||||
let dnd = Dnd::new(Arc::clone(&xconn))
|
let dnd = Dnd::new(Arc::clone(&xconn))
|
||||||
.expect("Failed to call XInternAtoms when initializing drag and drop");
|
.expect("Failed to call XInternAtoms when initializing drag and drop");
|
||||||
|
@ -149,7 +165,7 @@ impl<T: 'static> EventLoop<T> {
|
||||||
});
|
});
|
||||||
|
|
||||||
let randr_event_offset = xconn
|
let randr_event_offset = xconn
|
||||||
.select_xrandr_input(root)
|
.select_xrandr_input(root as ffi::Window)
|
||||||
.expect("Failed to query XRandR extension");
|
.expect("Failed to query XRandR extension");
|
||||||
|
|
||||||
let xi2ext = unsafe {
|
let xi2ext = unsafe {
|
||||||
|
@ -223,7 +239,11 @@ impl<T: 'static> EventLoop<T> {
|
||||||
let handle = event_loop.handle();
|
let handle = event_loop.handle();
|
||||||
|
|
||||||
// Create the X11 event dispatcher.
|
// Create the X11 event dispatcher.
|
||||||
let source = X11Source::new(xconn.x11_fd, calloop::Interest::READ, calloop::Mode::Level);
|
let source = X11Source::new(
|
||||||
|
xconn.xcb_connection().as_raw_fd(),
|
||||||
|
calloop::Interest::READ,
|
||||||
|
calloop::Mode::Level,
|
||||||
|
);
|
||||||
handle
|
handle
|
||||||
.insert_source(source, |_, _, _| Ok(calloop::PostAction::Continue))
|
.insert_source(source, |_, _, _| Ok(calloop::PostAction::Continue))
|
||||||
.expect("Failed to register the X11 event dispatcher");
|
.expect("Failed to register the X11 event dispatcher");
|
||||||
|
@ -254,8 +274,7 @@ impl<T: 'static> EventLoop<T> {
|
||||||
.expect("Failed to register the redraw event channel with the event loop");
|
.expect("Failed to register the redraw event channel with the event loop");
|
||||||
|
|
||||||
let kb_state =
|
let kb_state =
|
||||||
KbdState::from_x11_xkb(unsafe { (xconn.xlib_xcb.XGetXCBConnection)(xconn.display) })
|
KbdState::from_x11_xkb(xconn.xcb_connection().get_raw_xcb_connection()).unwrap();
|
||||||
.unwrap();
|
|
||||||
|
|
||||||
let window_target = EventLoopWindowTarget {
|
let window_target = EventLoopWindowTarget {
|
||||||
ime,
|
ime,
|
||||||
|
@ -299,8 +318,12 @@ impl<T: 'static> EventLoop<T> {
|
||||||
// (The request buffer is flushed during `init_device`)
|
// (The request buffer is flushed during `init_device`)
|
||||||
get_xtarget(&target)
|
get_xtarget(&target)
|
||||||
.xconn
|
.xconn
|
||||||
.select_xinput_events(root, ffi::XIAllDevices, ffi::XI_HierarchyChangedMask)
|
.select_xinput_events(
|
||||||
.queue();
|
root,
|
||||||
|
ffi::XIAllDevices as _,
|
||||||
|
x11rb::protocol::xinput::XIEventMask::HIERARCHY,
|
||||||
|
)
|
||||||
|
.expect_then_ignore_error("Failed to register for XInput2 device hotplug events");
|
||||||
|
|
||||||
get_xtarget(&target)
|
get_xtarget(&target)
|
||||||
.xconn
|
.xconn
|
||||||
|
@ -308,8 +331,7 @@ impl<T: 'static> EventLoop<T> {
|
||||||
0x100, // Use the "core keyboard device"
|
0x100, // Use the "core keyboard device"
|
||||||
ffi::XkbNewKeyboardNotifyMask | ffi::XkbStateNotifyMask,
|
ffi::XkbNewKeyboardNotifyMask | ffi::XkbStateNotifyMask,
|
||||||
)
|
)
|
||||||
.unwrap()
|
.unwrap();
|
||||||
.queue();
|
|
||||||
|
|
||||||
event_processor.init_device(ffi::XIAllDevices);
|
event_processor.init_device(ffi::XIAllDevices);
|
||||||
|
|
||||||
|
@ -592,25 +614,24 @@ impl<T> EventLoopWindowTarget<T> {
|
||||||
let device_events = self.device_events.get() == DeviceEvents::Always
|
let device_events = self.device_events.get() == DeviceEvents::Always
|
||||||
|| (focus && self.device_events.get() == DeviceEvents::WhenFocused);
|
|| (focus && self.device_events.get() == DeviceEvents::WhenFocused);
|
||||||
|
|
||||||
let mut mask = 0;
|
let mut mask = xinput::XIEventMask::from(0u32);
|
||||||
if device_events {
|
if device_events {
|
||||||
mask = ffi::XI_RawMotionMask
|
mask = xinput::XIEventMask::RAW_MOTION
|
||||||
| ffi::XI_RawButtonPressMask
|
| xinput::XIEventMask::RAW_BUTTON_PRESS
|
||||||
| ffi::XI_RawButtonReleaseMask
|
| xinput::XIEventMask::RAW_BUTTON_RELEASE
|
||||||
| ffi::XI_RawKeyPressMask
|
| xinput::XIEventMask::RAW_KEY_PRESS
|
||||||
| ffi::XI_RawKeyReleaseMask;
|
| xinput::XIEventMask::RAW_KEY_RELEASE;
|
||||||
}
|
}
|
||||||
|
|
||||||
self.xconn
|
self.xconn
|
||||||
.select_xinput_events(self.root, ffi::XIAllMasterDevices, mask)
|
.select_xinput_events(self.root, ffi::XIAllMasterDevices as _, mask)
|
||||||
.queue();
|
.expect_then_ignore_error("Failed to update device event filter");
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn raw_display_handle(&self) -> raw_window_handle::RawDisplayHandle {
|
pub fn raw_display_handle(&self) -> raw_window_handle::RawDisplayHandle {
|
||||||
let mut display_handle = XlibDisplayHandle::empty();
|
let mut display_handle = XlibDisplayHandle::empty();
|
||||||
display_handle.display = self.xconn.display as *mut _;
|
display_handle.display = self.xconn.display as *mut _;
|
||||||
display_handle.screen =
|
display_handle.screen = self.xconn.default_screen_index() as c_int;
|
||||||
unsafe { (self.xconn.xlib.XDefaultScreen)(self.xconn.display as *mut _) };
|
|
||||||
RawDisplayHandle::Xlib(display_handle)
|
RawDisplayHandle::Xlib(display_handle)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -702,14 +723,133 @@ impl Drop for Window {
|
||||||
fn drop(&mut self) {
|
fn drop(&mut self) {
|
||||||
let window = self.deref();
|
let window = self.deref();
|
||||||
let xconn = &window.xconn;
|
let xconn = &window.xconn;
|
||||||
unsafe {
|
|
||||||
(xconn.xlib.XDestroyWindow)(xconn.display, window.id().0 as ffi::Window);
|
if let Ok(c) = xconn
|
||||||
// If the window was somehow already destroyed, we'll get a `BadWindow` error, which we don't care about.
|
.xcb_connection()
|
||||||
let _ = xconn.check_errors();
|
.destroy_window(window.id().0 as xproto::Window)
|
||||||
|
{
|
||||||
|
c.ignore_error();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Generic sum error type for X11 errors.
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub enum X11Error {
|
||||||
|
/// An error from the Xlib library.
|
||||||
|
Xlib(XError),
|
||||||
|
|
||||||
|
/// An error that occurred while trying to connect to the X server.
|
||||||
|
Connect(ConnectError),
|
||||||
|
|
||||||
|
/// An error that occurred over the connection medium.
|
||||||
|
Connection(ConnectionError),
|
||||||
|
|
||||||
|
/// An error that occurred logically on the X11 end.
|
||||||
|
X11(LogicalError),
|
||||||
|
|
||||||
|
/// The XID range has been exhausted.
|
||||||
|
XidsExhausted(IdsExhausted),
|
||||||
|
|
||||||
|
/// Got `null` from an Xlib function without a reason.
|
||||||
|
UnexpectedNull(&'static str),
|
||||||
|
}
|
||||||
|
|
||||||
|
impl fmt::Display for X11Error {
|
||||||
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||||
|
match self {
|
||||||
|
X11Error::Xlib(e) => write!(f, "Xlib error: {}", e),
|
||||||
|
X11Error::Connect(e) => write!(f, "X11 connection error: {}", e),
|
||||||
|
X11Error::Connection(e) => write!(f, "X11 connection error: {}", e),
|
||||||
|
X11Error::XidsExhausted(e) => write!(f, "XID range exhausted: {}", e),
|
||||||
|
X11Error::X11(e) => write!(f, "X11 error: {:?}", e),
|
||||||
|
X11Error::UnexpectedNull(s) => write!(f, "Xlib function returned null: {}", s),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl std::error::Error for X11Error {
|
||||||
|
fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
|
||||||
|
match self {
|
||||||
|
X11Error::Xlib(e) => Some(e),
|
||||||
|
X11Error::Connect(e) => Some(e),
|
||||||
|
X11Error::Connection(e) => Some(e),
|
||||||
|
X11Error::XidsExhausted(e) => Some(e),
|
||||||
|
_ => None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<XError> for X11Error {
|
||||||
|
fn from(e: XError) -> Self {
|
||||||
|
X11Error::Xlib(e)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<ConnectError> for X11Error {
|
||||||
|
fn from(e: ConnectError) -> Self {
|
||||||
|
X11Error::Connect(e)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<ConnectionError> for X11Error {
|
||||||
|
fn from(e: ConnectionError) -> Self {
|
||||||
|
X11Error::Connection(e)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<LogicalError> for X11Error {
|
||||||
|
fn from(e: LogicalError) -> Self {
|
||||||
|
X11Error::X11(e)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<ReplyError> for X11Error {
|
||||||
|
fn from(value: ReplyError) -> Self {
|
||||||
|
match value {
|
||||||
|
ReplyError::ConnectionError(e) => e.into(),
|
||||||
|
ReplyError::X11Error(e) => e.into(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<ime::ImeContextCreationError> for X11Error {
|
||||||
|
fn from(value: ime::ImeContextCreationError) -> Self {
|
||||||
|
match value {
|
||||||
|
ime::ImeContextCreationError::XError(e) => e.into(),
|
||||||
|
ime::ImeContextCreationError::Null => Self::UnexpectedNull("XOpenIM"),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<ReplyOrIdError> for X11Error {
|
||||||
|
fn from(value: ReplyOrIdError) -> Self {
|
||||||
|
match value {
|
||||||
|
ReplyOrIdError::ConnectionError(e) => e.into(),
|
||||||
|
ReplyOrIdError::X11Error(e) => e.into(),
|
||||||
|
ReplyOrIdError::IdsExhausted => Self::XidsExhausted(IdsExhausted),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// The underlying x11rb connection that we are using.
|
||||||
|
type X11rbConnection = x11rb::xcb_ffi::XCBConnection;
|
||||||
|
|
||||||
|
/// Type alias for a void cookie.
|
||||||
|
type VoidCookie<'a> = x11rb::cookie::VoidCookie<'a, X11rbConnection>;
|
||||||
|
|
||||||
|
/// Extension trait for `Result<VoidCookie, E>`.
|
||||||
|
trait CookieResultExt {
|
||||||
|
/// Unwrap the send error and ignore the result.
|
||||||
|
fn expect_then_ignore_error(self, msg: &str);
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a, E: fmt::Debug> CookieResultExt for Result<VoidCookie<'a>, E> {
|
||||||
|
fn expect_then_ignore_error(self, msg: &str) {
|
||||||
|
self.expect(msg).ignore_error()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// XEvents of type GenericEvent store their actual data in an XGenericEventCookie data structure. This is a wrapper to
|
/// XEvents of type GenericEvent store their actual data in an XGenericEventCookie data structure. This is a wrapper to
|
||||||
/// extract the cookie from a GenericEvent XEvent and release the cookie data once it has been processed
|
/// extract the cookie from a GenericEvent XEvent and release the cookie data once it has been processed
|
||||||
struct GenericEventCookie<'a> {
|
struct GenericEventCookie<'a> {
|
||||||
|
@ -745,7 +885,7 @@ struct XExtension {
|
||||||
first_error_id: c_int,
|
first_error_id: c_int,
|
||||||
}
|
}
|
||||||
|
|
||||||
fn mkwid(w: ffi::Window) -> crate::window::WindowId {
|
fn mkwid(w: xproto::Window) -> crate::window::WindowId {
|
||||||
crate::window::WindowId(crate::platform_impl::platform::WindowId(w as _))
|
crate::window::WindowId(crate::platform_impl::platform::WindowId(w as _))
|
||||||
}
|
}
|
||||||
fn mkdid(w: c_int) -> crate::event::DeviceId {
|
fn mkdid(w: c_int) -> crate::event::DeviceId {
|
||||||
|
|
|
@ -6,10 +6,10 @@ use once_cell::sync::Lazy;
|
||||||
|
|
||||||
use super::{
|
use super::{
|
||||||
ffi::{
|
ffi::{
|
||||||
RRCrtc, RRCrtcChangeNotifyMask, RRMode, RROutputPropertyNotifyMask,
|
self, RRCrtc, RRCrtcChangeNotifyMask, RRMode, RROutputPropertyNotifyMask,
|
||||||
RRScreenChangeNotifyMask, True, Window, XRRCrtcInfo, XRRModeInfo, XRRScreenResources,
|
RRScreenChangeNotifyMask, True, Window, XRRCrtcInfo, XRRModeInfo, XRRScreenResources,
|
||||||
},
|
},
|
||||||
util, XConnection, XError,
|
util, X11Error, XConnection,
|
||||||
};
|
};
|
||||||
use crate::{
|
use crate::{
|
||||||
dpi::{PhysicalPosition, PhysicalSize},
|
dpi::{PhysicalPosition, PhysicalSize},
|
||||||
|
@ -241,13 +241,13 @@ impl XConnection {
|
||||||
let mut minor = 0;
|
let mut minor = 0;
|
||||||
(self.xrandr.XRRQueryVersion)(self.display, &mut major, &mut minor);
|
(self.xrandr.XRRQueryVersion)(self.display, &mut major, &mut minor);
|
||||||
|
|
||||||
let root = (self.xlib.XDefaultRootWindow)(self.display);
|
let root = self.default_root().root;
|
||||||
let resources = if (major == 1 && minor >= 3) || major > 1 {
|
let resources = if (major == 1 && minor >= 3) || major > 1 {
|
||||||
(self.xrandr.XRRGetScreenResourcesCurrent)(self.display, root)
|
(self.xrandr.XRRGetScreenResourcesCurrent)(self.display, root as ffi::Window)
|
||||||
} else {
|
} else {
|
||||||
// WARNING: this function is supposedly very slow, on the order of hundreds of ms.
|
// WARNING: this function is supposedly very slow, on the order of hundreds of ms.
|
||||||
// Upon failure, `resources` will be null.
|
// Upon failure, `resources` will be null.
|
||||||
(self.xrandr.XRRGetScreenResources)(self.display, root)
|
(self.xrandr.XRRGetScreenResources)(self.display, root as ffi::Window)
|
||||||
};
|
};
|
||||||
|
|
||||||
if resources.is_null() {
|
if resources.is_null() {
|
||||||
|
@ -256,7 +256,7 @@ impl XConnection {
|
||||||
|
|
||||||
let mut has_primary = false;
|
let mut has_primary = false;
|
||||||
|
|
||||||
let primary = (self.xrandr.XRRGetOutputPrimary)(self.display, root);
|
let primary = (self.xrandr.XRRGetOutputPrimary)(self.display, root as ffi::Window);
|
||||||
let mut available = Vec::with_capacity((*resources).ncrtc as usize);
|
let mut available = Vec::with_capacity((*resources).ncrtc as usize);
|
||||||
|
|
||||||
for crtc_index in 0..(*resources).ncrtc {
|
for crtc_index in 0..(*resources).ncrtc {
|
||||||
|
@ -311,7 +311,7 @@ impl XConnection {
|
||||||
.unwrap_or_else(MonitorHandle::dummy)
|
.unwrap_or_else(MonitorHandle::dummy)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn select_xrandr_input(&self, root: Window) -> Result<c_int, XError> {
|
pub fn select_xrandr_input(&self, root: Window) -> Result<c_int, X11Error> {
|
||||||
let has_xrandr = unsafe {
|
let has_xrandr = unsafe {
|
||||||
let mut major = 0;
|
let mut major = 0;
|
||||||
let mut minor = 0;
|
let mut minor = 0;
|
||||||
|
|
|
@ -1,70 +0,0 @@
|
||||||
use std::{
|
|
||||||
collections::HashMap,
|
|
||||||
ffi::{CStr, CString},
|
|
||||||
fmt::Debug,
|
|
||||||
os::raw::*,
|
|
||||||
sync::Mutex,
|
|
||||||
};
|
|
||||||
|
|
||||||
use once_cell::sync::Lazy;
|
|
||||||
|
|
||||||
use super::*;
|
|
||||||
|
|
||||||
type AtomCache = HashMap<CString, ffi::Atom>;
|
|
||||||
|
|
||||||
static ATOM_CACHE: Lazy<Mutex<AtomCache>> = Lazy::new(|| Mutex::new(HashMap::with_capacity(2048)));
|
|
||||||
|
|
||||||
impl XConnection {
|
|
||||||
pub fn get_atom<T: AsRef<CStr> + Debug>(&self, name: T) -> ffi::Atom {
|
|
||||||
let name = name.as_ref();
|
|
||||||
let mut atom_cache_lock = ATOM_CACHE.lock().unwrap();
|
|
||||||
let cached_atom = (*atom_cache_lock).get(name).cloned();
|
|
||||||
if let Some(atom) = cached_atom {
|
|
||||||
atom
|
|
||||||
} else {
|
|
||||||
let atom = unsafe {
|
|
||||||
(self.xlib.XInternAtom)(self.display, name.as_ptr() as *const c_char, ffi::False)
|
|
||||||
};
|
|
||||||
if atom == 0 {
|
|
||||||
panic!(
|
|
||||||
"`XInternAtom` failed, which really shouldn't happen. Atom: {:?}, Error: {:#?}",
|
|
||||||
name,
|
|
||||||
self.check_errors(),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
/*println!(
|
|
||||||
"XInternAtom name:{:?} atom:{:?}",
|
|
||||||
name,
|
|
||||||
atom,
|
|
||||||
);*/
|
|
||||||
(*atom_cache_lock).insert(name.to_owned(), atom);
|
|
||||||
atom
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub unsafe fn get_atom_unchecked(&self, name: &[u8]) -> ffi::Atom {
|
|
||||||
debug_assert!(CStr::from_bytes_with_nul(name).is_ok());
|
|
||||||
let name = CStr::from_bytes_with_nul_unchecked(name);
|
|
||||||
self.get_atom(name)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Note: this doesn't use caching, for the sake of simplicity.
|
|
||||||
// If you're dealing with this many atoms, you'll usually want to cache them locally anyway.
|
|
||||||
pub unsafe fn get_atoms(&self, names: &[*mut c_char]) -> Result<Vec<ffi::Atom>, XError> {
|
|
||||||
let mut atoms = Vec::with_capacity(names.len());
|
|
||||||
(self.xlib.XInternAtoms)(
|
|
||||||
self.display,
|
|
||||||
names.as_ptr() as *mut _,
|
|
||||||
names.len() as c_int,
|
|
||||||
ffi::False,
|
|
||||||
atoms.as_mut_ptr(),
|
|
||||||
);
|
|
||||||
self.check_errors()?;
|
|
||||||
atoms.set_len(names.len());
|
|
||||||
/*println!(
|
|
||||||
"XInternAtoms atoms:{:?}",
|
|
||||||
atoms,
|
|
||||||
);*/
|
|
||||||
Ok(atoms)
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,46 +1,31 @@
|
||||||
use super::*;
|
use super::*;
|
||||||
|
use x11rb::x11_utils::Serialize;
|
||||||
pub type ClientMsgPayload = [c_long; 5];
|
|
||||||
|
|
||||||
impl XConnection {
|
impl XConnection {
|
||||||
pub fn send_event<T: Into<ffi::XEvent>>(
|
|
||||||
&self,
|
|
||||||
target_window: c_ulong,
|
|
||||||
event_mask: Option<c_long>,
|
|
||||||
event: T,
|
|
||||||
) -> Flusher<'_> {
|
|
||||||
let event_mask = event_mask.unwrap_or(ffi::NoEventMask);
|
|
||||||
unsafe {
|
|
||||||
(self.xlib.XSendEvent)(
|
|
||||||
self.display,
|
|
||||||
target_window,
|
|
||||||
ffi::False,
|
|
||||||
event_mask,
|
|
||||||
&mut event.into(),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
Flusher::new(self)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn send_client_msg(
|
pub fn send_client_msg(
|
||||||
&self,
|
&self,
|
||||||
window: c_ulong, // The window this is "about"; not necessarily this window
|
window: xproto::Window, // The window this is "about"; not necessarily this window
|
||||||
target_window: c_ulong, // The window we're sending to
|
target_window: xproto::Window, // The window we're sending to
|
||||||
message_type: ffi::Atom,
|
message_type: xproto::Atom,
|
||||||
event_mask: Option<c_long>,
|
event_mask: Option<xproto::EventMask>,
|
||||||
data: ClientMsgPayload,
|
data: impl Into<xproto::ClientMessageData>,
|
||||||
) -> Flusher<'_> {
|
) -> Result<VoidCookie<'_>, X11Error> {
|
||||||
let event = ffi::XClientMessageEvent {
|
let event = xproto::ClientMessageEvent {
|
||||||
type_: ffi::ClientMessage,
|
response_type: xproto::CLIENT_MESSAGE_EVENT,
|
||||||
display: self.display,
|
|
||||||
window,
|
window,
|
||||||
message_type,
|
format: 32,
|
||||||
format: c_long::FORMAT as c_int,
|
data: data.into(),
|
||||||
data: unsafe { mem::transmute(data) },
|
sequence: 0,
|
||||||
// These fields are ignored by `XSendEvent`
|
type_: message_type,
|
||||||
serial: 0,
|
|
||||||
send_event: 0,
|
|
||||||
};
|
};
|
||||||
self.send_event(target_window, event_mask, event)
|
|
||||||
|
self.xcb_connection()
|
||||||
|
.send_event(
|
||||||
|
false,
|
||||||
|
target_window,
|
||||||
|
event_mask.unwrap_or(xproto::EventMask::NO_EVENT),
|
||||||
|
event.serialize(),
|
||||||
|
)
|
||||||
|
.map_err(Into::into)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,11 +1,13 @@
|
||||||
use std::ffi::CString;
|
use std::ffi::CString;
|
||||||
|
|
||||||
|
use x11rb::connection::Connection;
|
||||||
|
|
||||||
use crate::window::CursorIcon;
|
use crate::window::CursorIcon;
|
||||||
|
|
||||||
use super::*;
|
use super::*;
|
||||||
|
|
||||||
impl XConnection {
|
impl XConnection {
|
||||||
pub fn set_cursor_icon(&self, window: ffi::Window, cursor: Option<CursorIcon>) {
|
pub fn set_cursor_icon(&self, window: xproto::Window, cursor: Option<CursorIcon>) {
|
||||||
let cursor = *self
|
let cursor = *self
|
||||||
.cursor_cache
|
.cursor_cache
|
||||||
.lock()
|
.lock()
|
||||||
|
@ -13,7 +15,8 @@ impl XConnection {
|
||||||
.entry(cursor)
|
.entry(cursor)
|
||||||
.or_insert_with(|| self.get_cursor(cursor));
|
.or_insert_with(|| self.get_cursor(cursor));
|
||||||
|
|
||||||
self.update_cursor(window, cursor);
|
self.update_cursor(window, cursor)
|
||||||
|
.expect("Failed to set cursor");
|
||||||
}
|
}
|
||||||
|
|
||||||
fn create_empty_cursor(&self) -> ffi::Cursor {
|
fn create_empty_cursor(&self) -> ffi::Cursor {
|
||||||
|
@ -59,11 +62,15 @@ impl XConnection {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn update_cursor(&self, window: ffi::Window, cursor: ffi::Cursor) {
|
fn update_cursor(&self, window: xproto::Window, cursor: ffi::Cursor) -> Result<(), X11Error> {
|
||||||
unsafe {
|
self.xcb_connection()
|
||||||
(self.xlib.XDefineCursor)(self.display, window, cursor);
|
.change_window_attributes(
|
||||||
|
window,
|
||||||
|
&xproto::ChangeWindowAttributesAux::new().cursor(cursor as xproto::Cursor),
|
||||||
|
)?
|
||||||
|
.ignore_error();
|
||||||
|
|
||||||
self.flush_requests().expect("Failed to set the cursor");
|
self.xcb_connection().flush()?;
|
||||||
}
|
Ok(())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,55 +0,0 @@
|
||||||
use std::{fmt::Debug, mem, os::raw::*};
|
|
||||||
|
|
||||||
// This isn't actually the number of the bits in the format.
|
|
||||||
// X11 does a match on this value to determine which type to call sizeof on.
|
|
||||||
// Thus, we use 32 for c_long, since 32 maps to c_long which maps to 64.
|
|
||||||
// ...if that sounds confusing, then you know why this enum is here.
|
|
||||||
#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord)]
|
|
||||||
pub enum Format {
|
|
||||||
Char = 8,
|
|
||||||
Short = 16,
|
|
||||||
Long = 32,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Format {
|
|
||||||
pub fn from_format(format: usize) -> Option<Self> {
|
|
||||||
match format {
|
|
||||||
8 => Some(Format::Char),
|
|
||||||
16 => Some(Format::Short),
|
|
||||||
32 => Some(Format::Long),
|
|
||||||
_ => None,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn get_actual_size(&self) -> usize {
|
|
||||||
match self {
|
|
||||||
Format::Char => mem::size_of::<c_char>(),
|
|
||||||
Format::Short => mem::size_of::<c_short>(),
|
|
||||||
Format::Long => mem::size_of::<c_long>(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub trait Formattable: Debug + Clone + Copy + PartialEq + PartialOrd {
|
|
||||||
const FORMAT: Format;
|
|
||||||
}
|
|
||||||
|
|
||||||
// You might be surprised by the absence of c_int, but not as surprised as X11 would be by the presence of it.
|
|
||||||
impl Formattable for c_schar {
|
|
||||||
const FORMAT: Format = Format::Char;
|
|
||||||
}
|
|
||||||
impl Formattable for c_uchar {
|
|
||||||
const FORMAT: Format = Format::Char;
|
|
||||||
}
|
|
||||||
impl Formattable for c_short {
|
|
||||||
const FORMAT: Format = Format::Short;
|
|
||||||
}
|
|
||||||
impl Formattable for c_ushort {
|
|
||||||
const FORMAT: Format = Format::Short;
|
|
||||||
}
|
|
||||||
impl Formattable for c_long {
|
|
||||||
const FORMAT: Format = Format::Long;
|
|
||||||
}
|
|
||||||
impl Formattable for c_ulong {
|
|
||||||
const FORMAT: Format = Format::Long;
|
|
||||||
}
|
|
|
@ -40,16 +40,9 @@ impl AaRect {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Default)]
|
|
||||||
pub struct TranslatedCoords {
|
|
||||||
pub x_rel_root: c_int,
|
|
||||||
pub y_rel_root: c_int,
|
|
||||||
pub child: ffi::Window,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, Default)]
|
#[derive(Debug, Default)]
|
||||||
pub struct Geometry {
|
pub struct Geometry {
|
||||||
pub root: ffi::Window,
|
pub root: xproto::Window,
|
||||||
// If you want positions relative to the root window, use translate_coords.
|
// If you want positions relative to the root window, use translate_coords.
|
||||||
// Note that the overwhelming majority of window managers are reparenting WMs, thus the window
|
// Note that the overwhelming majority of window managers are reparenting WMs, thus the window
|
||||||
// ID we get from window creation is for a nested window used as the window's client area. If
|
// ID we get from window creation is for a nested window used as the window's client area. If
|
||||||
|
@ -69,14 +62,14 @@ pub struct Geometry {
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
pub struct FrameExtents {
|
pub struct FrameExtents {
|
||||||
pub left: c_ulong,
|
pub left: u32,
|
||||||
pub right: c_ulong,
|
pub right: u32,
|
||||||
pub top: c_ulong,
|
pub top: u32,
|
||||||
pub bottom: c_ulong,
|
pub bottom: u32,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl FrameExtents {
|
impl FrameExtents {
|
||||||
pub fn new(left: c_ulong, right: c_ulong, top: c_ulong, bottom: c_ulong) -> Self {
|
pub fn new(left: u32, right: u32, top: u32, bottom: u32) -> Self {
|
||||||
FrameExtents {
|
FrameExtents {
|
||||||
left,
|
left,
|
||||||
right,
|
right,
|
||||||
|
@ -85,7 +78,7 @@ impl FrameExtents {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn from_border(border: c_ulong) -> Self {
|
pub fn from_border(border: u32) -> Self {
|
||||||
Self::new(border, border, border, border)
|
Self::new(border, border, border, border)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -144,52 +137,29 @@ impl XConnection {
|
||||||
// This is adequate for inner_position
|
// This is adequate for inner_position
|
||||||
pub fn translate_coords(
|
pub fn translate_coords(
|
||||||
&self,
|
&self,
|
||||||
window: ffi::Window,
|
window: xproto::Window,
|
||||||
root: ffi::Window,
|
root: xproto::Window,
|
||||||
) -> Result<TranslatedCoords, XError> {
|
) -> Result<xproto::TranslateCoordinatesReply, X11Error> {
|
||||||
let mut coords = TranslatedCoords::default();
|
self.xcb_connection()
|
||||||
|
.translate_coordinates(window, root, 0, 0)?
|
||||||
unsafe {
|
.reply()
|
||||||
(self.xlib.XTranslateCoordinates)(
|
.map_err(Into::into)
|
||||||
self.display,
|
|
||||||
window,
|
|
||||||
root,
|
|
||||||
0,
|
|
||||||
0,
|
|
||||||
&mut coords.x_rel_root,
|
|
||||||
&mut coords.y_rel_root,
|
|
||||||
&mut coords.child,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
self.check_errors()?;
|
|
||||||
Ok(coords)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// This is adequate for inner_size
|
// This is adequate for inner_size
|
||||||
pub fn get_geometry(&self, window: ffi::Window) -> Result<Geometry, XError> {
|
pub fn get_geometry(
|
||||||
let mut geometry = Geometry::default();
|
&self,
|
||||||
|
window: xproto::Window,
|
||||||
let _status = unsafe {
|
) -> Result<xproto::GetGeometryReply, X11Error> {
|
||||||
(self.xlib.XGetGeometry)(
|
self.xcb_connection()
|
||||||
self.display,
|
.get_geometry(window)?
|
||||||
window,
|
.reply()
|
||||||
&mut geometry.root,
|
.map_err(Into::into)
|
||||||
&mut geometry.x_rel_parent,
|
|
||||||
&mut geometry.y_rel_parent,
|
|
||||||
&mut geometry.width,
|
|
||||||
&mut geometry.height,
|
|
||||||
&mut geometry.border,
|
|
||||||
&mut geometry.depth,
|
|
||||||
)
|
|
||||||
};
|
|
||||||
|
|
||||||
self.check_errors()?;
|
|
||||||
Ok(geometry)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_frame_extents(&self, window: ffi::Window) -> Option<FrameExtents> {
|
fn get_frame_extents(&self, window: xproto::Window) -> Option<FrameExtents> {
|
||||||
let extents_atom = unsafe { self.get_atom_unchecked(b"_NET_FRAME_EXTENTS\0") };
|
let atoms = self.atoms();
|
||||||
|
let extents_atom = atoms[_NET_FRAME_EXTENTS];
|
||||||
|
|
||||||
if !hint_is_supported(extents_atom) {
|
if !hint_is_supported(extents_atom) {
|
||||||
return None;
|
return None;
|
||||||
|
@ -198,8 +168,12 @@ impl XConnection {
|
||||||
// Of the WMs tested, xmonad, i3, dwm, IceWM (1.3.x and earlier), and blackbox don't
|
// Of the WMs tested, xmonad, i3, dwm, IceWM (1.3.x and earlier), and blackbox don't
|
||||||
// support this. As this is part of EWMH (Extended Window Manager Hints), it's likely to
|
// support this. As this is part of EWMH (Extended Window Manager Hints), it's likely to
|
||||||
// be unsupported by many smaller WMs.
|
// be unsupported by many smaller WMs.
|
||||||
let extents: Option<Vec<c_ulong>> = self
|
let extents: Option<Vec<u32>> = self
|
||||||
.get_property(window, extents_atom, ffi::XA_CARDINAL)
|
.get_property(
|
||||||
|
window,
|
||||||
|
extents_atom,
|
||||||
|
xproto::Atom::from(xproto::AtomEnum::CARDINAL),
|
||||||
|
)
|
||||||
.ok();
|
.ok();
|
||||||
|
|
||||||
extents.and_then(|extents| {
|
extents.and_then(|extents| {
|
||||||
|
@ -216,52 +190,35 @@ impl XConnection {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn is_top_level(&self, window: ffi::Window, root: ffi::Window) -> Option<bool> {
|
pub fn is_top_level(&self, window: xproto::Window, root: xproto::Window) -> Option<bool> {
|
||||||
let client_list_atom = unsafe { self.get_atom_unchecked(b"_NET_CLIENT_LIST\0") };
|
let atoms = self.atoms();
|
||||||
|
let client_list_atom = atoms[_NET_CLIENT_LIST];
|
||||||
|
|
||||||
if !hint_is_supported(client_list_atom) {
|
if !hint_is_supported(client_list_atom) {
|
||||||
return None;
|
return None;
|
||||||
}
|
}
|
||||||
|
|
||||||
let client_list: Option<Vec<ffi::Window>> = self
|
let client_list: Option<Vec<xproto::Window>> = self
|
||||||
.get_property(root, client_list_atom, ffi::XA_WINDOW)
|
.get_property(
|
||||||
|
root,
|
||||||
|
client_list_atom,
|
||||||
|
xproto::Atom::from(xproto::AtomEnum::WINDOW),
|
||||||
|
)
|
||||||
.ok();
|
.ok();
|
||||||
|
|
||||||
client_list.map(|client_list| client_list.contains(&window))
|
client_list.map(|client_list| client_list.contains(&(window as xproto::Window)))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_parent_window(&self, window: ffi::Window) -> Result<ffi::Window, XError> {
|
fn get_parent_window(&self, window: xproto::Window) -> Result<xproto::Window, X11Error> {
|
||||||
let parent = unsafe {
|
let parent = self.xcb_connection().query_tree(window)?.reply()?.parent;
|
||||||
let mut root = 0;
|
Ok(parent)
|
||||||
let mut parent = 0;
|
|
||||||
let mut children: *mut ffi::Window = ptr::null_mut();
|
|
||||||
let mut nchildren = 0;
|
|
||||||
|
|
||||||
// What's filled into `parent` if `window` is the root window?
|
|
||||||
let _status = (self.xlib.XQueryTree)(
|
|
||||||
self.display,
|
|
||||||
window,
|
|
||||||
&mut root,
|
|
||||||
&mut parent,
|
|
||||||
&mut children,
|
|
||||||
&mut nchildren,
|
|
||||||
);
|
|
||||||
|
|
||||||
// The list of children isn't used
|
|
||||||
if !children.is_null() {
|
|
||||||
(self.xlib.XFree)(children as *mut _);
|
|
||||||
}
|
|
||||||
|
|
||||||
parent
|
|
||||||
};
|
|
||||||
self.check_errors().map(|_| parent)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn climb_hierarchy(
|
fn climb_hierarchy(
|
||||||
&self,
|
&self,
|
||||||
window: ffi::Window,
|
window: xproto::Window,
|
||||||
root: ffi::Window,
|
root: xproto::Window,
|
||||||
) -> Result<ffi::Window, XError> {
|
) -> Result<xproto::Window, X11Error> {
|
||||||
let mut outer_window = window;
|
let mut outer_window = window;
|
||||||
loop {
|
loop {
|
||||||
let candidate = self.get_parent_window(outer_window)?;
|
let candidate = self.get_parent_window(outer_window)?;
|
||||||
|
@ -275,8 +232,8 @@ impl XConnection {
|
||||||
|
|
||||||
pub fn get_frame_extents_heuristic(
|
pub fn get_frame_extents_heuristic(
|
||||||
&self,
|
&self,
|
||||||
window: ffi::Window,
|
window: xproto::Window,
|
||||||
root: ffi::Window,
|
root: xproto::Window,
|
||||||
) -> FrameExtentsHeuristic {
|
) -> FrameExtentsHeuristic {
|
||||||
use self::FrameExtentsHeuristicPath::*;
|
use self::FrameExtentsHeuristicPath::*;
|
||||||
|
|
||||||
|
@ -288,7 +245,7 @@ impl XConnection {
|
||||||
let coords = self
|
let coords = self
|
||||||
.translate_coords(window, root)
|
.translate_coords(window, root)
|
||||||
.expect("Failed to translate window coordinates");
|
.expect("Failed to translate window coordinates");
|
||||||
(coords.y_rel_root, coords.child)
|
(coords.dst_y, coords.child)
|
||||||
};
|
};
|
||||||
|
|
||||||
let (width, height, border) = {
|
let (width, height, border) = {
|
||||||
|
@ -298,7 +255,7 @@ impl XConnection {
|
||||||
(
|
(
|
||||||
inner_geometry.width,
|
inner_geometry.width,
|
||||||
inner_geometry.height,
|
inner_geometry.height,
|
||||||
inner_geometry.border,
|
inner_geometry.border_width,
|
||||||
)
|
)
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -353,7 +310,7 @@ impl XConnection {
|
||||||
.get_geometry(outer_window)
|
.get_geometry(outer_window)
|
||||||
.expect("Failed to get outer window geometry");
|
.expect("Failed to get outer window geometry");
|
||||||
(
|
(
|
||||||
outer_geometry.y_rel_parent,
|
outer_geometry.y,
|
||||||
outer_geometry.width,
|
outer_geometry.width,
|
||||||
outer_geometry.height,
|
outer_geometry.height,
|
||||||
)
|
)
|
||||||
|
@ -361,21 +318,16 @@ impl XConnection {
|
||||||
|
|
||||||
// Since we have the geometry of the outermost window and the geometry of the client
|
// Since we have the geometry of the outermost window and the geometry of the client
|
||||||
// area, we can figure out what's in between.
|
// area, we can figure out what's in between.
|
||||||
let diff_x = outer_width.saturating_sub(width);
|
let diff_x = outer_width.saturating_sub(width) as u32;
|
||||||
let diff_y = outer_height.saturating_sub(height);
|
let diff_y = outer_height.saturating_sub(height) as u32;
|
||||||
let offset_y = inner_y_rel_root.saturating_sub(outer_y) as c_uint;
|
let offset_y = inner_y_rel_root.saturating_sub(outer_y) as u32;
|
||||||
|
|
||||||
let left = diff_x / 2;
|
let left = diff_x / 2;
|
||||||
let right = left;
|
let right = left;
|
||||||
let top = offset_y;
|
let top = offset_y;
|
||||||
let bottom = diff_y.saturating_sub(offset_y);
|
let bottom = diff_y.saturating_sub(offset_y);
|
||||||
|
|
||||||
let frame_extents = FrameExtents::new(
|
let frame_extents = FrameExtents::new(left, right, top, bottom);
|
||||||
left as c_ulong,
|
|
||||||
right as c_ulong,
|
|
||||||
top as c_ulong,
|
|
||||||
bottom as c_ulong,
|
|
||||||
);
|
|
||||||
FrameExtentsHeuristic {
|
FrameExtentsHeuristic {
|
||||||
frame_extents,
|
frame_extents,
|
||||||
heuristic_path: UnsupportedNested,
|
heuristic_path: UnsupportedNested,
|
||||||
|
@ -383,7 +335,7 @@ impl XConnection {
|
||||||
} else {
|
} else {
|
||||||
// This is the case for xmonad and dwm, AKA the only WMs tested that supplied a
|
// This is the case for xmonad and dwm, AKA the only WMs tested that supplied a
|
||||||
// border value. This is convenient, since we can use it to get an accurate frame.
|
// border value. This is convenient, since we can use it to get an accurate frame.
|
||||||
let frame_extents = FrameExtents::from_border(border as c_ulong);
|
let frame_extents = FrameExtents::from_border(border.into());
|
||||||
FrameExtentsHeuristic {
|
FrameExtentsHeuristic {
|
||||||
frame_extents,
|
frame_extents,
|
||||||
heuristic_path: UnsupportedBordered,
|
heuristic_path: UnsupportedBordered,
|
||||||
|
|
|
@ -1,4 +1,3 @@
|
||||||
use std::slice;
|
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
|
|
||||||
use super::*;
|
use super::*;
|
||||||
|
@ -66,25 +65,27 @@ pub enum WindowType {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl WindowType {
|
impl WindowType {
|
||||||
pub(crate) fn as_atom(&self, xconn: &Arc<XConnection>) -> ffi::Atom {
|
pub(crate) fn as_atom(&self, xconn: &Arc<XConnection>) -> xproto::Atom {
|
||||||
use self::WindowType::*;
|
use self::WindowType::*;
|
||||||
let atom_name: &[u8] = match *self {
|
let atom_name = match *self {
|
||||||
Desktop => b"_NET_WM_WINDOW_TYPE_DESKTOP\0",
|
Desktop => _NET_WM_WINDOW_TYPE_DESKTOP,
|
||||||
Dock => b"_NET_WM_WINDOW_TYPE_DOCK\0",
|
Dock => _NET_WM_WINDOW_TYPE_DOCK,
|
||||||
Toolbar => b"_NET_WM_WINDOW_TYPE_TOOLBAR\0",
|
Toolbar => _NET_WM_WINDOW_TYPE_TOOLBAR,
|
||||||
Menu => b"_NET_WM_WINDOW_TYPE_MENU\0",
|
Menu => _NET_WM_WINDOW_TYPE_MENU,
|
||||||
Utility => b"_NET_WM_WINDOW_TYPE_UTILITY\0",
|
Utility => _NET_WM_WINDOW_TYPE_UTILITY,
|
||||||
Splash => b"_NET_WM_WINDOW_TYPE_SPLASH\0",
|
Splash => _NET_WM_WINDOW_TYPE_SPLASH,
|
||||||
Dialog => b"_NET_WM_WINDOW_TYPE_DIALOG\0",
|
Dialog => _NET_WM_WINDOW_TYPE_DIALOG,
|
||||||
DropdownMenu => b"_NET_WM_WINDOW_TYPE_DROPDOWN_MENU\0",
|
DropdownMenu => _NET_WM_WINDOW_TYPE_DROPDOWN_MENU,
|
||||||
PopupMenu => b"_NET_WM_WINDOW_TYPE_POPUP_MENU\0",
|
PopupMenu => _NET_WM_WINDOW_TYPE_POPUP_MENU,
|
||||||
Tooltip => b"_NET_WM_WINDOW_TYPE_TOOLTIP\0",
|
Tooltip => _NET_WM_WINDOW_TYPE_TOOLTIP,
|
||||||
Notification => b"_NET_WM_WINDOW_TYPE_NOTIFICATION\0",
|
Notification => _NET_WM_WINDOW_TYPE_NOTIFICATION,
|
||||||
Combo => b"_NET_WM_WINDOW_TYPE_COMBO\0",
|
Combo => _NET_WM_WINDOW_TYPE_COMBO,
|
||||||
Dnd => b"_NET_WM_WINDOW_TYPE_DND\0",
|
Dnd => _NET_WM_WINDOW_TYPE_DND,
|
||||||
Normal => b"_NET_WM_WINDOW_TYPE_NORMAL\0",
|
Normal => _NET_WM_WINDOW_TYPE_NORMAL,
|
||||||
};
|
};
|
||||||
unsafe { xconn.get_atom_unchecked(atom_name) }
|
|
||||||
|
let atoms = xconn.atoms();
|
||||||
|
atoms[atom_name]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -92,30 +93,27 @@ pub struct MotifHints {
|
||||||
hints: MwmHints,
|
hints: MwmHints,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[repr(C)]
|
|
||||||
struct MwmHints {
|
struct MwmHints {
|
||||||
flags: c_ulong,
|
flags: u32,
|
||||||
functions: c_ulong,
|
functions: u32,
|
||||||
decorations: c_ulong,
|
decorations: u32,
|
||||||
input_mode: c_long,
|
input_mode: u32,
|
||||||
status: c_ulong,
|
status: u32,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[allow(dead_code)]
|
#[allow(dead_code)]
|
||||||
mod mwm {
|
mod mwm {
|
||||||
use libc::c_ulong;
|
|
||||||
|
|
||||||
// Motif WM hints are obsolete, but still widely supported.
|
// Motif WM hints are obsolete, but still widely supported.
|
||||||
// https://stackoverflow.com/a/1909708
|
// https://stackoverflow.com/a/1909708
|
||||||
pub const MWM_HINTS_FUNCTIONS: c_ulong = 1 << 0;
|
pub const MWM_HINTS_FUNCTIONS: u32 = 1 << 0;
|
||||||
pub const MWM_HINTS_DECORATIONS: c_ulong = 1 << 1;
|
pub const MWM_HINTS_DECORATIONS: u32 = 1 << 1;
|
||||||
|
|
||||||
pub const MWM_FUNC_ALL: c_ulong = 1 << 0;
|
pub const MWM_FUNC_ALL: u32 = 1 << 0;
|
||||||
pub const MWM_FUNC_RESIZE: c_ulong = 1 << 1;
|
pub const MWM_FUNC_RESIZE: u32 = 1 << 1;
|
||||||
pub const MWM_FUNC_MOVE: c_ulong = 1 << 2;
|
pub const MWM_FUNC_MOVE: u32 = 1 << 2;
|
||||||
pub const MWM_FUNC_MINIMIZE: c_ulong = 1 << 3;
|
pub const MWM_FUNC_MINIMIZE: u32 = 1 << 3;
|
||||||
pub const MWM_FUNC_MAXIMIZE: c_ulong = 1 << 4;
|
pub const MWM_FUNC_MAXIMIZE: u32 = 1 << 4;
|
||||||
pub const MWM_FUNC_CLOSE: c_ulong = 1 << 5;
|
pub const MWM_FUNC_CLOSE: u32 = 1 << 5;
|
||||||
}
|
}
|
||||||
|
|
||||||
impl MotifHints {
|
impl MotifHints {
|
||||||
|
@ -133,7 +131,7 @@ impl MotifHints {
|
||||||
|
|
||||||
pub fn set_decorations(&mut self, decorations: bool) {
|
pub fn set_decorations(&mut self, decorations: bool) {
|
||||||
self.hints.flags |= mwm::MWM_HINTS_DECORATIONS;
|
self.hints.flags |= mwm::MWM_HINTS_DECORATIONS;
|
||||||
self.hints.decorations = decorations as c_ulong;
|
self.hints.decorations = decorations as u32;
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn set_maximizable(&mut self, maximizable: bool) {
|
pub fn set_maximizable(&mut self, maximizable: bool) {
|
||||||
|
@ -144,7 +142,7 @@ impl MotifHints {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn add_func(&mut self, func: c_ulong) {
|
fn add_func(&mut self, func: u32) {
|
||||||
if self.hints.flags & mwm::MWM_HINTS_FUNCTIONS != 0 {
|
if self.hints.flags & mwm::MWM_HINTS_FUNCTIONS != 0 {
|
||||||
if self.hints.functions & mwm::MWM_FUNC_ALL != 0 {
|
if self.hints.functions & mwm::MWM_FUNC_ALL != 0 {
|
||||||
self.hints.functions &= !func;
|
self.hints.functions &= !func;
|
||||||
|
@ -154,7 +152,7 @@ impl MotifHints {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn remove_func(&mut self, func: c_ulong) {
|
fn remove_func(&mut self, func: u32) {
|
||||||
if self.hints.flags & mwm::MWM_HINTS_FUNCTIONS == 0 {
|
if self.hints.flags & mwm::MWM_HINTS_FUNCTIONS == 0 {
|
||||||
self.hints.flags |= mwm::MWM_HINTS_FUNCTIONS;
|
self.hints.flags |= mwm::MWM_HINTS_FUNCTIONS;
|
||||||
self.hints.functions = mwm::MWM_FUNC_ALL;
|
self.hints.functions = mwm::MWM_FUNC_ALL;
|
||||||
|
@ -174,170 +172,47 @@ impl Default for MotifHints {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl MwmHints {
|
|
||||||
fn as_slice(&self) -> &[c_ulong] {
|
|
||||||
unsafe { slice::from_raw_parts(self as *const _ as *const c_ulong, 5) }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub(crate) struct NormalHints<'a> {
|
|
||||||
size_hints: XSmartPointer<'a, ffi::XSizeHints>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'a> NormalHints<'a> {
|
|
||||||
pub fn new(xconn: &'a XConnection) -> Self {
|
|
||||||
NormalHints {
|
|
||||||
size_hints: xconn.alloc_size_hints(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn get_resize_increments(&self) -> Option<(u32, u32)> {
|
|
||||||
has_flag(self.size_hints.flags, ffi::PResizeInc).then_some({
|
|
||||||
(
|
|
||||||
self.size_hints.width_inc as u32,
|
|
||||||
self.size_hints.height_inc as u32,
|
|
||||||
)
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn set_position(&mut self, position: Option<(i32, i32)>) {
|
|
||||||
if let Some((x, y)) = position {
|
|
||||||
self.size_hints.flags |= ffi::PPosition;
|
|
||||||
self.size_hints.x = x as c_int;
|
|
||||||
self.size_hints.y = y as c_int;
|
|
||||||
} else {
|
|
||||||
self.size_hints.flags &= !ffi::PPosition;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// WARNING: This hint is obsolete
|
|
||||||
pub fn set_size(&mut self, size: Option<(u32, u32)>) {
|
|
||||||
if let Some((width, height)) = size {
|
|
||||||
self.size_hints.flags |= ffi::PSize;
|
|
||||||
self.size_hints.width = width as c_int;
|
|
||||||
self.size_hints.height = height as c_int;
|
|
||||||
} else {
|
|
||||||
self.size_hints.flags &= !ffi::PSize;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn set_max_size(&mut self, max_size: Option<(u32, u32)>) {
|
|
||||||
if let Some((max_width, max_height)) = max_size {
|
|
||||||
self.size_hints.flags |= ffi::PMaxSize;
|
|
||||||
self.size_hints.max_width = max_width as c_int;
|
|
||||||
self.size_hints.max_height = max_height as c_int;
|
|
||||||
} else {
|
|
||||||
self.size_hints.flags &= !ffi::PMaxSize;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn set_min_size(&mut self, min_size: Option<(u32, u32)>) {
|
|
||||||
if let Some((min_width, min_height)) = min_size {
|
|
||||||
self.size_hints.flags |= ffi::PMinSize;
|
|
||||||
self.size_hints.min_width = min_width as c_int;
|
|
||||||
self.size_hints.min_height = min_height as c_int;
|
|
||||||
} else {
|
|
||||||
self.size_hints.flags &= !ffi::PMinSize;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn set_resize_increments(&mut self, resize_increments: Option<(u32, u32)>) {
|
|
||||||
if let Some((width_inc, height_inc)) = resize_increments {
|
|
||||||
self.size_hints.flags |= ffi::PResizeInc;
|
|
||||||
self.size_hints.width_inc = width_inc as c_int;
|
|
||||||
self.size_hints.height_inc = height_inc as c_int;
|
|
||||||
} else {
|
|
||||||
self.size_hints.flags &= !ffi::PResizeInc;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn set_base_size(&mut self, base_size: Option<(u32, u32)>) {
|
|
||||||
if let Some((base_width, base_height)) = base_size {
|
|
||||||
self.size_hints.flags |= ffi::PBaseSize;
|
|
||||||
self.size_hints.base_width = base_width as c_int;
|
|
||||||
self.size_hints.base_height = base_height as c_int;
|
|
||||||
} else {
|
|
||||||
self.size_hints.flags &= !ffi::PBaseSize;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl XConnection {
|
impl XConnection {
|
||||||
pub fn get_wm_hints(
|
pub fn get_motif_hints(&self, window: xproto::Window) -> MotifHints {
|
||||||
&self,
|
let atoms = self.atoms();
|
||||||
window: ffi::Window,
|
let motif_hints = atoms[_MOTIF_WM_HINTS];
|
||||||
) -> Result<XSmartPointer<'_, ffi::XWMHints>, XError> {
|
|
||||||
let wm_hints = unsafe { (self.xlib.XGetWMHints)(self.display, window) };
|
|
||||||
self.check_errors()?;
|
|
||||||
let wm_hints = if wm_hints.is_null() {
|
|
||||||
self.alloc_wm_hints()
|
|
||||||
} else {
|
|
||||||
XSmartPointer::new(self, wm_hints).unwrap()
|
|
||||||
};
|
|
||||||
Ok(wm_hints)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn set_wm_hints(
|
|
||||||
&self,
|
|
||||||
window: ffi::Window,
|
|
||||||
wm_hints: XSmartPointer<'_, ffi::XWMHints>,
|
|
||||||
) -> Flusher<'_> {
|
|
||||||
unsafe {
|
|
||||||
(self.xlib.XSetWMHints)(self.display, window, wm_hints.ptr);
|
|
||||||
}
|
|
||||||
Flusher::new(self)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn get_normal_hints(&self, window: ffi::Window) -> Result<NormalHints<'_>, XError> {
|
|
||||||
let size_hints = self.alloc_size_hints();
|
|
||||||
let mut supplied_by_user = MaybeUninit::uninit();
|
|
||||||
unsafe {
|
|
||||||
(self.xlib.XGetWMNormalHints)(
|
|
||||||
self.display,
|
|
||||||
window,
|
|
||||||
size_hints.ptr,
|
|
||||||
supplied_by_user.as_mut_ptr(),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
self.check_errors().map(|_| NormalHints { size_hints })
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn set_normal_hints(
|
|
||||||
&self,
|
|
||||||
window: ffi::Window,
|
|
||||||
normal_hints: NormalHints<'_>,
|
|
||||||
) -> Flusher<'_> {
|
|
||||||
unsafe {
|
|
||||||
(self.xlib.XSetWMNormalHints)(self.display, window, normal_hints.size_hints.ptr);
|
|
||||||
}
|
|
||||||
Flusher::new(self)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn get_motif_hints(&self, window: ffi::Window) -> MotifHints {
|
|
||||||
let motif_hints = unsafe { self.get_atom_unchecked(b"_MOTIF_WM_HINTS\0") };
|
|
||||||
|
|
||||||
let mut hints = MotifHints::new();
|
let mut hints = MotifHints::new();
|
||||||
|
|
||||||
if let Ok(props) = self.get_property::<c_ulong>(window, motif_hints, motif_hints) {
|
if let Ok(props) = self.get_property::<u32>(window, motif_hints, motif_hints) {
|
||||||
hints.hints.flags = props.first().cloned().unwrap_or(0);
|
hints.hints.flags = props.first().cloned().unwrap_or(0);
|
||||||
hints.hints.functions = props.get(1).cloned().unwrap_or(0);
|
hints.hints.functions = props.get(1).cloned().unwrap_or(0);
|
||||||
hints.hints.decorations = props.get(2).cloned().unwrap_or(0);
|
hints.hints.decorations = props.get(2).cloned().unwrap_or(0);
|
||||||
hints.hints.input_mode = props.get(3).cloned().unwrap_or(0) as c_long;
|
hints.hints.input_mode = props.get(3).cloned().unwrap_or(0);
|
||||||
hints.hints.status = props.get(4).cloned().unwrap_or(0);
|
hints.hints.status = props.get(4).cloned().unwrap_or(0);
|
||||||
}
|
}
|
||||||
|
|
||||||
hints
|
hints
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn set_motif_hints(&self, window: ffi::Window, hints: &MotifHints) -> Flusher<'_> {
|
#[allow(clippy::unnecessary_cast)]
|
||||||
let motif_hints = unsafe { self.get_atom_unchecked(b"_MOTIF_WM_HINTS\0") };
|
pub fn set_motif_hints(
|
||||||
|
&self,
|
||||||
|
window: xproto::Window,
|
||||||
|
hints: &MotifHints,
|
||||||
|
) -> Result<VoidCookie<'_>, X11Error> {
|
||||||
|
let atoms = self.atoms();
|
||||||
|
let motif_hints = atoms[_MOTIF_WM_HINTS];
|
||||||
|
|
||||||
|
let hints_data: [u32; 5] = [
|
||||||
|
hints.hints.flags as u32,
|
||||||
|
hints.hints.functions as u32,
|
||||||
|
hints.hints.decorations as u32,
|
||||||
|
hints.hints.input_mode as u32,
|
||||||
|
hints.hints.status as u32,
|
||||||
|
];
|
||||||
|
|
||||||
self.change_property(
|
self.change_property(
|
||||||
window,
|
window,
|
||||||
motif_hints,
|
motif_hints,
|
||||||
motif_hints,
|
motif_hints,
|
||||||
PropMode::Replace,
|
xproto::PropMode::REPLACE,
|
||||||
hints.hints.as_slice(),
|
&hints_data,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,9 +1,10 @@
|
||||||
use std::{slice, str};
|
use std::{slice, str};
|
||||||
|
use x11rb::protocol::xinput::{self, ConnectionExt as _};
|
||||||
|
|
||||||
use super::*;
|
use super::*;
|
||||||
|
|
||||||
pub const VIRTUAL_CORE_POINTER: c_int = 2;
|
pub const VIRTUAL_CORE_POINTER: u16 = 2;
|
||||||
pub const VIRTUAL_CORE_KEYBOARD: c_int = 3;
|
pub const VIRTUAL_CORE_KEYBOARD: u16 = 3;
|
||||||
|
|
||||||
// A base buffer size of 1kB uses a negligible amount of RAM while preventing us from having to
|
// A base buffer size of 1kB uses a negligible amount of RAM while preventing us from having to
|
||||||
// re-allocate (and make another round-trip) in the *vast* majority of cases.
|
// re-allocate (and make another round-trip) in the *vast* majority of cases.
|
||||||
|
@ -13,8 +14,8 @@ const TEXT_BUFFER_SIZE: usize = 1024;
|
||||||
// NOTE: Some of these fields are not used, but may be of use in the future.
|
// NOTE: Some of these fields are not used, but may be of use in the future.
|
||||||
pub struct PointerState<'a> {
|
pub struct PointerState<'a> {
|
||||||
xconn: &'a XConnection,
|
xconn: &'a XConnection,
|
||||||
pub root: ffi::Window,
|
pub root: xproto::Window,
|
||||||
pub child: ffi::Window,
|
pub child: xproto::Window,
|
||||||
pub root_x: c_double,
|
pub root_x: c_double,
|
||||||
pub root_y: c_double,
|
pub root_y: c_double,
|
||||||
pub win_x: c_double,
|
pub win_x: c_double,
|
||||||
|
@ -38,82 +39,43 @@ impl<'a> Drop for PointerState<'a> {
|
||||||
impl XConnection {
|
impl XConnection {
|
||||||
pub fn select_xinput_events(
|
pub fn select_xinput_events(
|
||||||
&self,
|
&self,
|
||||||
window: c_ulong,
|
window: xproto::Window,
|
||||||
device_id: c_int,
|
device_id: u16,
|
||||||
mask: i32,
|
mask: xinput::XIEventMask,
|
||||||
) -> Flusher<'_> {
|
) -> Result<VoidCookie<'_>, X11Error> {
|
||||||
let mut event_mask = ffi::XIEventMask {
|
self.xcb_connection()
|
||||||
deviceid: device_id,
|
.xinput_xi_select_events(
|
||||||
mask: &mask as *const _ as *mut c_uchar,
|
|
||||||
mask_len: mem::size_of_val(&mask) as c_int,
|
|
||||||
};
|
|
||||||
unsafe {
|
|
||||||
(self.xinput2.XISelectEvents)(
|
|
||||||
self.display,
|
|
||||||
window,
|
window,
|
||||||
&mut event_mask as *mut ffi::XIEventMask,
|
&[xinput::EventMask {
|
||||||
1, // number of masks to read from pointer above
|
deviceid: device_id,
|
||||||
);
|
mask: vec![mask],
|
||||||
}
|
}],
|
||||||
Flusher::new(self)
|
)
|
||||||
|
.map_err(Into::into)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn select_xkb_events(&self, device_id: c_uint, mask: c_ulong) -> Option<Flusher<'_>> {
|
pub fn select_xkb_events(&self, device_id: c_int, mask: c_ulong) -> Result<bool, X11Error> {
|
||||||
let status = unsafe { (self.xlib.XkbSelectEvents)(self.display, device_id, mask, mask) };
|
let status =
|
||||||
|
unsafe { (self.xlib.XkbSelectEvents)(self.display, device_id as _, mask, mask) };
|
||||||
|
|
||||||
if status == ffi::True {
|
if status == ffi::True {
|
||||||
Some(Flusher::new(self))
|
self.flush_requests()?;
|
||||||
|
Ok(true)
|
||||||
} else {
|
} else {
|
||||||
error!("Could not select XKB events: The XKB extension is not initialized!");
|
error!("Could not select XKB events: The XKB extension is not initialized!");
|
||||||
None
|
Ok(false)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn query_pointer(
|
pub fn query_pointer(
|
||||||
&self,
|
&self,
|
||||||
window: ffi::Window,
|
window: xproto::Window,
|
||||||
device_id: c_int,
|
device_id: u16,
|
||||||
) -> Result<PointerState<'_>, XError> {
|
) -> Result<xinput::XIQueryPointerReply, X11Error> {
|
||||||
unsafe {
|
self.xcb_connection()
|
||||||
let mut root = 0;
|
.xinput_xi_query_pointer(window, device_id)?
|
||||||
let mut child = 0;
|
.reply()
|
||||||
let mut root_x = 0.0;
|
.map_err(Into::into)
|
||||||
let mut root_y = 0.0;
|
|
||||||
let mut win_x = 0.0;
|
|
||||||
let mut win_y = 0.0;
|
|
||||||
let mut buttons = Default::default();
|
|
||||||
let mut modifiers = Default::default();
|
|
||||||
let mut group = Default::default();
|
|
||||||
|
|
||||||
let relative_to_window = (self.xinput2.XIQueryPointer)(
|
|
||||||
self.display,
|
|
||||||
device_id,
|
|
||||||
window,
|
|
||||||
&mut root,
|
|
||||||
&mut child,
|
|
||||||
&mut root_x,
|
|
||||||
&mut root_y,
|
|
||||||
&mut win_x,
|
|
||||||
&mut win_y,
|
|
||||||
&mut buttons,
|
|
||||||
&mut modifiers,
|
|
||||||
&mut group,
|
|
||||||
) == ffi::True;
|
|
||||||
|
|
||||||
self.check_errors()?;
|
|
||||||
|
|
||||||
Ok(PointerState {
|
|
||||||
xconn: self,
|
|
||||||
root,
|
|
||||||
child,
|
|
||||||
root_x,
|
|
||||||
root_y,
|
|
||||||
win_x,
|
|
||||||
win_y,
|
|
||||||
buttons,
|
|
||||||
group,
|
|
||||||
relative_to_window,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn lookup_utf8_inner(
|
fn lookup_utf8_inner(
|
||||||
|
|
|
@ -40,20 +40,3 @@ impl<'a, T> Drop for XSmartPointer<'a, T> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl XConnection {
|
|
||||||
pub fn alloc_class_hint(&self) -> XSmartPointer<'_, ffi::XClassHint> {
|
|
||||||
XSmartPointer::new(self, unsafe { (self.xlib.XAllocClassHint)() })
|
|
||||||
.expect("`XAllocClassHint` returned null; out of memory")
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn alloc_size_hints(&self) -> XSmartPointer<'_, ffi::XSizeHints> {
|
|
||||||
XSmartPointer::new(self, unsafe { (self.xlib.XAllocSizeHints)() })
|
|
||||||
.expect("`XAllocSizeHints` returned null; out of memory")
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn alloc_wm_hints(&self) -> XSmartPointer<'_, ffi::XWMHints> {
|
|
||||||
XSmartPointer::new(self, unsafe { (self.xlib.XAllocWMHints)() })
|
|
||||||
.expect("`XAllocWMHints` returned null; out of memory")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
|
@ -1,35 +1,30 @@
|
||||||
// Welcome to the util module, where we try to keep you from shooting yourself in the foot.
|
// Welcome to the util module, where we try to keep you from shooting yourself in the foot.
|
||||||
// *results may vary
|
// *results may vary
|
||||||
|
|
||||||
mod atom;
|
|
||||||
mod client_msg;
|
mod client_msg;
|
||||||
mod cursor;
|
mod cursor;
|
||||||
mod format;
|
|
||||||
mod geometry;
|
mod geometry;
|
||||||
mod hint;
|
mod hint;
|
||||||
mod icon;
|
mod icon;
|
||||||
mod input;
|
mod input;
|
||||||
pub mod keys;
|
pub mod keys;
|
||||||
mod memory;
|
pub(crate) mod memory;
|
||||||
mod randr;
|
mod randr;
|
||||||
mod window_property;
|
mod window_property;
|
||||||
mod wm;
|
mod wm;
|
||||||
|
|
||||||
pub use self::{
|
pub use self::{
|
||||||
atom::*, client_msg::*, format::*, geometry::*, hint::*, icon::*, input::*, randr::*,
|
client_msg::*, geometry::*, hint::*, icon::*, input::*, randr::*, window_property::*, wm::*,
|
||||||
window_property::*, wm::*,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
pub(crate) use self::memory::*;
|
|
||||||
|
|
||||||
use std::{
|
use std::{
|
||||||
mem::{self, MaybeUninit},
|
mem::{self, MaybeUninit},
|
||||||
ops::BitAnd,
|
ops::BitAnd,
|
||||||
os::raw::*,
|
os::raw::*,
|
||||||
ptr,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
use super::{ffi, XConnection, XError};
|
use super::{atoms::*, ffi, VoidCookie, X11Error, XConnection, XError};
|
||||||
|
use x11rb::protocol::xproto::{self, ConnectionExt as _};
|
||||||
|
|
||||||
pub fn maybe_change<T: PartialEq>(field: &mut Option<T>, value: T) -> bool {
|
pub fn maybe_change<T: PartialEq>(field: &mut Option<T>, value: T) -> bool {
|
||||||
let wrapped = Some(value);
|
let wrapped = Some(value);
|
||||||
|
@ -48,30 +43,6 @@ where
|
||||||
bitset & flag == flag
|
bitset & flag == flag
|
||||||
}
|
}
|
||||||
|
|
||||||
#[must_use = "This request was made asynchronously, and is still in the output buffer. You must explicitly choose to either `.flush()` (empty the output buffer, sending the request now) or `.queue()` (wait to send the request, allowing you to continue to add more requests without additional round-trips). For more information, see the documentation for `util::flush_requests`."]
|
|
||||||
pub(crate) struct Flusher<'a> {
|
|
||||||
xconn: &'a XConnection,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'a> Flusher<'a> {
|
|
||||||
pub fn new(xconn: &'a XConnection) -> Self {
|
|
||||||
Flusher { xconn }
|
|
||||||
}
|
|
||||||
|
|
||||||
// "I want this request sent now!"
|
|
||||||
pub fn flush(self) -> Result<(), XError> {
|
|
||||||
self.xconn.flush_requests()
|
|
||||||
}
|
|
||||||
|
|
||||||
// "I want the response now too!"
|
|
||||||
pub fn sync(self) -> Result<(), XError> {
|
|
||||||
self.xconn.sync_with_server()
|
|
||||||
}
|
|
||||||
|
|
||||||
// "I'm aware that this request hasn't been sent, and I'm okay with waiting."
|
|
||||||
pub fn queue(self) {}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl XConnection {
|
impl XConnection {
|
||||||
// This is impoartant, so pay attention!
|
// This is impoartant, so pay attention!
|
||||||
// Xlib has an output buffer, and tries to hide the async nature of X from you.
|
// Xlib has an output buffer, and tries to hide the async nature of X from you.
|
||||||
|
|
|
@ -48,8 +48,8 @@ impl ModifierKeymap {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn reset_from_x_connection(&mut self, xconn: &XConnection) {
|
pub fn reset_from_x_connection(&mut self, xconn: &XConnection) {
|
||||||
unsafe {
|
{
|
||||||
let keymap = (xconn.xlib.XGetModifierMapping)(xconn.display);
|
let keymap = xconn.xcb_connection().get_modifier_mapping().expect("get_modifier_mapping failed").reply().expect("get_modifier_mapping failed");
|
||||||
|
|
||||||
if keymap.is_null() {
|
if keymap.is_null() {
|
||||||
panic!("failed to allocate XModifierKeymap");
|
panic!("failed to allocate XModifierKeymap");
|
||||||
|
|
|
@ -68,8 +68,7 @@ impl XConnection {
|
||||||
return None;
|
return None;
|
||||||
}
|
}
|
||||||
|
|
||||||
let screen = (self.xlib.XDefaultScreen)(self.display);
|
let bit_depth = self.default_root().root_depth;
|
||||||
let bit_depth = (self.xlib.XDefaultDepth)(self.display, screen);
|
|
||||||
|
|
||||||
let output_modes =
|
let output_modes =
|
||||||
slice::from_raw_parts((*output_info).modes, (*output_info).nmode as usize);
|
slice::from_raw_parts((*output_info).modes, (*output_info).nmode as usize);
|
||||||
|
@ -159,11 +158,11 @@ impl XConnection {
|
||||||
let mut minor = 0;
|
let mut minor = 0;
|
||||||
(self.xrandr.XRRQueryVersion)(self.display, &mut major, &mut minor);
|
(self.xrandr.XRRQueryVersion)(self.display, &mut major, &mut minor);
|
||||||
|
|
||||||
let root = (self.xlib.XDefaultRootWindow)(self.display);
|
let root = self.default_root().root;
|
||||||
let resources = if (major == 1 && minor >= 3) || major > 1 {
|
let resources = if (major == 1 && minor >= 3) || major > 1 {
|
||||||
(self.xrandr.XRRGetScreenResourcesCurrent)(self.display, root)
|
(self.xrandr.XRRGetScreenResourcesCurrent)(self.display, root as ffi::Window)
|
||||||
} else {
|
} else {
|
||||||
(self.xrandr.XRRGetScreenResources)(self.display, root)
|
(self.xrandr.XRRGetScreenResources)(self.display, root as ffi::Window)
|
||||||
};
|
};
|
||||||
|
|
||||||
let crtc = (self.xrandr.XRRGetCrtcInfo)(self.display, resources, crtc_id);
|
let crtc = (self.xrandr.XRRGetCrtcInfo)(self.display, resources, crtc_id);
|
||||||
|
@ -197,11 +196,11 @@ impl XConnection {
|
||||||
let mut minor = 0;
|
let mut minor = 0;
|
||||||
(self.xrandr.XRRQueryVersion)(self.display, &mut major, &mut minor);
|
(self.xrandr.XRRQueryVersion)(self.display, &mut major, &mut minor);
|
||||||
|
|
||||||
let root = (self.xlib.XDefaultRootWindow)(self.display);
|
let root = self.default_root().root;
|
||||||
let resources = if (major == 1 && minor >= 3) || major > 1 {
|
let resources = if (major == 1 && minor >= 3) || major > 1 {
|
||||||
(self.xrandr.XRRGetScreenResourcesCurrent)(self.display, root)
|
(self.xrandr.XRRGetScreenResourcesCurrent)(self.display, root as ffi::Window)
|
||||||
} else {
|
} else {
|
||||||
(self.xrandr.XRRGetScreenResources)(self.display, root)
|
(self.xrandr.XRRGetScreenResources)(self.display, root as ffi::Window)
|
||||||
};
|
};
|
||||||
|
|
||||||
let crtc = (self.xrandr.XRRGetCrtcInfo)(self.display, resources, crtc_id);
|
let crtc = (self.xrandr.XRRGetCrtcInfo)(self.display, resources, crtc_id);
|
||||||
|
|
|
@ -1,18 +1,28 @@
|
||||||
use super::*;
|
use super::*;
|
||||||
|
use bytemuck::{NoUninit, Pod};
|
||||||
|
use std::sync::Arc;
|
||||||
|
|
||||||
pub type Cardinal = c_long;
|
use x11rb::connection::Connection;
|
||||||
pub const CARDINAL_SIZE: usize = mem::size_of::<c_long>();
|
use x11rb::errors::ReplyError;
|
||||||
|
|
||||||
|
pub type Cardinal = u32;
|
||||||
|
pub const CARDINAL_SIZE: usize = mem::size_of::<u32>();
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
pub enum GetPropertyError {
|
pub enum GetPropertyError {
|
||||||
XError(XError),
|
X11rbError(Arc<ReplyError>),
|
||||||
TypeMismatch(ffi::Atom),
|
TypeMismatch(xproto::Atom),
|
||||||
FormatMismatch(c_int),
|
FormatMismatch(c_int),
|
||||||
NothingAllocated,
|
}
|
||||||
|
|
||||||
|
impl<T: Into<ReplyError>> From<T> for GetPropertyError {
|
||||||
|
fn from(e: T) -> Self {
|
||||||
|
Self::X11rbError(Arc::new(e.into()))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl GetPropertyError {
|
impl GetPropertyError {
|
||||||
pub fn is_actual_property_type(&self, t: ffi::Atom) -> bool {
|
pub fn is_actual_property_type(&self, t: xproto::Atom) -> bool {
|
||||||
if let GetPropertyError::TypeMismatch(actual_type) = *self {
|
if let GetPropertyError::TypeMismatch(actual_type) = *self {
|
||||||
actual_type == t
|
actual_type == t
|
||||||
} else {
|
} else {
|
||||||
|
@ -23,120 +33,150 @@ impl GetPropertyError {
|
||||||
|
|
||||||
// Number of 32-bit chunks to retrieve per iteration of get_property's inner loop.
|
// Number of 32-bit chunks to retrieve per iteration of get_property's inner loop.
|
||||||
// To test if `get_property` works correctly, set this to 1.
|
// To test if `get_property` works correctly, set this to 1.
|
||||||
const PROPERTY_BUFFER_SIZE: c_long = 1024; // 4k of RAM ought to be enough for anyone!
|
const PROPERTY_BUFFER_SIZE: u32 = 1024; // 4k of RAM ought to be enough for anyone!
|
||||||
|
|
||||||
#[derive(Debug)]
|
|
||||||
#[allow(dead_code)]
|
|
||||||
pub enum PropMode {
|
|
||||||
Replace = ffi::PropModeReplace as isize,
|
|
||||||
Prepend = ffi::PropModePrepend as isize,
|
|
||||||
Append = ffi::PropModeAppend as isize,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl XConnection {
|
impl XConnection {
|
||||||
pub fn get_property<T: Formattable>(
|
pub fn get_property<T: Pod>(
|
||||||
&self,
|
&self,
|
||||||
window: c_ulong,
|
window: xproto::Window,
|
||||||
property: ffi::Atom,
|
property: xproto::Atom,
|
||||||
property_type: ffi::Atom,
|
property_type: xproto::Atom,
|
||||||
) -> Result<Vec<T>, GetPropertyError> {
|
) -> Result<Vec<T>, GetPropertyError> {
|
||||||
let mut data = Vec::new();
|
let mut iter = PropIterator::new(self.xcb_connection(), window, property, property_type);
|
||||||
let mut offset = 0;
|
let mut data = vec![];
|
||||||
|
|
||||||
let mut done = false;
|
loop {
|
||||||
let mut actual_type = 0;
|
if !iter.next_window(&mut data)? {
|
||||||
let mut actual_format = 0;
|
break;
|
||||||
let mut quantity_returned = 0;
|
|
||||||
let mut bytes_after = 0;
|
|
||||||
let mut buf: *mut c_uchar = ptr::null_mut();
|
|
||||||
|
|
||||||
while !done {
|
|
||||||
unsafe {
|
|
||||||
(self.xlib.XGetWindowProperty)(
|
|
||||||
self.display,
|
|
||||||
window,
|
|
||||||
property,
|
|
||||||
// This offset is in terms of 32-bit chunks.
|
|
||||||
offset,
|
|
||||||
// This is the quantity of 32-bit chunks to receive at once.
|
|
||||||
PROPERTY_BUFFER_SIZE,
|
|
||||||
ffi::False,
|
|
||||||
property_type,
|
|
||||||
&mut actual_type,
|
|
||||||
&mut actual_format,
|
|
||||||
// This is the quantity of items we retrieved in our format, NOT of 32-bit chunks!
|
|
||||||
&mut quantity_returned,
|
|
||||||
// ...and this is a quantity of bytes. So, this function deals in 3 different units.
|
|
||||||
&mut bytes_after,
|
|
||||||
&mut buf,
|
|
||||||
);
|
|
||||||
|
|
||||||
if let Err(e) = self.check_errors() {
|
|
||||||
return Err(GetPropertyError::XError(e));
|
|
||||||
}
|
|
||||||
|
|
||||||
if actual_type != property_type {
|
|
||||||
return Err(GetPropertyError::TypeMismatch(actual_type));
|
|
||||||
}
|
|
||||||
|
|
||||||
let format_mismatch = Format::from_format(actual_format as _) != Some(T::FORMAT);
|
|
||||||
if format_mismatch {
|
|
||||||
return Err(GetPropertyError::FormatMismatch(actual_format));
|
|
||||||
}
|
|
||||||
|
|
||||||
if !buf.is_null() {
|
|
||||||
offset += PROPERTY_BUFFER_SIZE;
|
|
||||||
let new_data =
|
|
||||||
std::slice::from_raw_parts(buf as *mut T, quantity_returned as usize);
|
|
||||||
/*println!(
|
|
||||||
"XGetWindowProperty prop:{:?} fmt:{:02} len:{:02} off:{:02} out:{:02}, buf:{:?}",
|
|
||||||
property,
|
|
||||||
mem::size_of::<T>() * 8,
|
|
||||||
data.len(),
|
|
||||||
offset,
|
|
||||||
quantity_returned,
|
|
||||||
new_data,
|
|
||||||
);*/
|
|
||||||
data.extend_from_slice(new_data);
|
|
||||||
// Fun fact: XGetWindowProperty allocates one extra byte at the end.
|
|
||||||
(self.xlib.XFree)(buf as _); // Don't try to access new_data after this.
|
|
||||||
} else {
|
|
||||||
return Err(GetPropertyError::NothingAllocated);
|
|
||||||
}
|
|
||||||
|
|
||||||
done = bytes_after == 0;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(data)
|
Ok(data)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn change_property<'a, T: Formattable>(
|
pub fn change_property<'a, T: NoUninit>(
|
||||||
&'a self,
|
&'a self,
|
||||||
window: c_ulong,
|
window: xproto::Window,
|
||||||
property: ffi::Atom,
|
property: xproto::Atom,
|
||||||
property_type: ffi::Atom,
|
property_type: xproto::Atom,
|
||||||
mode: PropMode,
|
mode: xproto::PropMode,
|
||||||
new_value: &[T],
|
new_value: &[T],
|
||||||
) -> Flusher<'a> {
|
) -> Result<VoidCookie<'a>, X11Error> {
|
||||||
debug_assert_eq!(mem::size_of::<T>(), T::FORMAT.get_actual_size());
|
assert!([1usize, 2, 4].contains(&mem::size_of::<T>()));
|
||||||
unsafe {
|
self.xcb_connection()
|
||||||
(self.xlib.XChangeProperty)(
|
.change_property(
|
||||||
self.display,
|
mode,
|
||||||
window,
|
window,
|
||||||
property,
|
property,
|
||||||
property_type,
|
property_type,
|
||||||
T::FORMAT as c_int,
|
(mem::size_of::<T>() * 8) as u8,
|
||||||
mode as c_int,
|
new_value
|
||||||
new_value.as_ptr() as *const c_uchar,
|
.len()
|
||||||
new_value.len() as c_int,
|
.try_into()
|
||||||
);
|
.expect("too many items for propery"),
|
||||||
}
|
bytemuck::cast_slice::<T, u8>(new_value),
|
||||||
/*println!(
|
)
|
||||||
"XChangeProperty prop:{:?} val:{:?}",
|
.map_err(Into::into)
|
||||||
property,
|
}
|
||||||
new_value,
|
}
|
||||||
);*/
|
|
||||||
Flusher::new(self)
|
/// An iterator over the "windows" of the property that we are fetching.
|
||||||
|
struct PropIterator<'a, C: ?Sized, T> {
|
||||||
|
/// Handle to the connection.
|
||||||
|
conn: &'a C,
|
||||||
|
|
||||||
|
/// The window that we're fetching the property from.
|
||||||
|
window: xproto::Window,
|
||||||
|
|
||||||
|
/// The property that we're fetching.
|
||||||
|
property: xproto::Atom,
|
||||||
|
|
||||||
|
/// The type of the property that we're fetching.
|
||||||
|
property_type: xproto::Atom,
|
||||||
|
|
||||||
|
/// The offset of the next window, in 32-bit chunks.
|
||||||
|
offset: u32,
|
||||||
|
|
||||||
|
/// The format of the type.
|
||||||
|
format: u8,
|
||||||
|
|
||||||
|
/// Keep a reference to `T`.
|
||||||
|
_phantom: std::marker::PhantomData<T>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a, C: Connection + ?Sized, T: Pod> PropIterator<'a, C, T> {
|
||||||
|
/// Create a new property iterator.
|
||||||
|
fn new(
|
||||||
|
conn: &'a C,
|
||||||
|
window: xproto::Window,
|
||||||
|
property: xproto::Atom,
|
||||||
|
property_type: xproto::Atom,
|
||||||
|
) -> Self {
|
||||||
|
let format = match mem::size_of::<T>() {
|
||||||
|
1 => 8,
|
||||||
|
2 => 16,
|
||||||
|
4 => 32,
|
||||||
|
_ => unreachable!(),
|
||||||
|
};
|
||||||
|
|
||||||
|
Self {
|
||||||
|
conn,
|
||||||
|
window,
|
||||||
|
property,
|
||||||
|
property_type,
|
||||||
|
offset: 0,
|
||||||
|
format,
|
||||||
|
_phantom: Default::default(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Get the next window and append it to `data`.
|
||||||
|
///
|
||||||
|
/// Returns whether there are more windows to fetch.
|
||||||
|
fn next_window(&mut self, data: &mut Vec<T>) -> Result<bool, GetPropertyError> {
|
||||||
|
// Send the request and wait for the reply.
|
||||||
|
let reply = self
|
||||||
|
.conn
|
||||||
|
.get_property(
|
||||||
|
false,
|
||||||
|
self.window,
|
||||||
|
self.property,
|
||||||
|
self.property_type,
|
||||||
|
self.offset,
|
||||||
|
PROPERTY_BUFFER_SIZE,
|
||||||
|
)?
|
||||||
|
.reply()?;
|
||||||
|
|
||||||
|
// Make sure that the reply is of the correct type.
|
||||||
|
if reply.type_ != self.property_type {
|
||||||
|
return Err(GetPropertyError::TypeMismatch(reply.type_));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Make sure that the reply is of the correct format.
|
||||||
|
if reply.format != self.format {
|
||||||
|
return Err(GetPropertyError::FormatMismatch(reply.format.into()));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Append the data to the output.
|
||||||
|
if mem::size_of::<T>() == 1 && mem::align_of::<T>() == 1 {
|
||||||
|
// We can just do a bytewise append.
|
||||||
|
data.extend_from_slice(bytemuck::cast_slice(&reply.value));
|
||||||
|
} else {
|
||||||
|
// Rust's borrowing and types system makes this a bit tricky.
|
||||||
|
//
|
||||||
|
// We need to make sure that the data is properly aligned. Unfortunately the best
|
||||||
|
// safe way to do this is to copy the data to another buffer and then append.
|
||||||
|
//
|
||||||
|
// TODO(notgull): It may be worth it to use `unsafe` to copy directly from
|
||||||
|
// `reply.value` to `data`; check if this is faster. Use benchmarks!
|
||||||
|
let old_len = data.len();
|
||||||
|
let added_len = reply.value.len() / mem::size_of::<T>();
|
||||||
|
data.resize(old_len + added_len, T::zeroed());
|
||||||
|
bytemuck::cast_slice_mut::<T, u8>(&mut data[old_len..]).copy_from_slice(&reply.value);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check `bytes_after` to see if there are more windows to fetch.
|
||||||
|
self.offset += PROPERTY_BUFFER_SIZE;
|
||||||
|
Ok(reply.bytes_after != 0)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -16,11 +16,11 @@ pub const MOVERESIZE_LEFT: isize = 7;
|
||||||
pub const MOVERESIZE_MOVE: isize = 8;
|
pub const MOVERESIZE_MOVE: isize = 8;
|
||||||
|
|
||||||
// This info is global to the window manager.
|
// This info is global to the window manager.
|
||||||
static SUPPORTED_HINTS: Lazy<Mutex<Vec<ffi::Atom>>> =
|
static SUPPORTED_HINTS: Lazy<Mutex<Vec<xproto::Atom>>> =
|
||||||
Lazy::new(|| Mutex::new(Vec::with_capacity(0)));
|
Lazy::new(|| Mutex::new(Vec::with_capacity(0)));
|
||||||
static WM_NAME: Lazy<Mutex<Option<String>>> = Lazy::new(|| Mutex::new(None));
|
static WM_NAME: Lazy<Mutex<Option<String>>> = Lazy::new(|| Mutex::new(None));
|
||||||
|
|
||||||
pub fn hint_is_supported(hint: ffi::Atom) -> bool {
|
pub fn hint_is_supported(hint: xproto::Atom) -> bool {
|
||||||
(*SUPPORTED_HINTS.lock().unwrap()).contains(&hint)
|
(*SUPPORTED_HINTS.lock().unwrap()).contains(&hint)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -33,20 +33,27 @@ pub fn wm_name_is_one_of(names: &[&str]) -> bool {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl XConnection {
|
impl XConnection {
|
||||||
pub fn update_cached_wm_info(&self, root: ffi::Window) {
|
pub fn update_cached_wm_info(&self, root: xproto::Window) {
|
||||||
*SUPPORTED_HINTS.lock().unwrap() = self.get_supported_hints(root);
|
*SUPPORTED_HINTS.lock().unwrap() = self.get_supported_hints(root);
|
||||||
*WM_NAME.lock().unwrap() = self.get_wm_name(root);
|
*WM_NAME.lock().unwrap() = self.get_wm_name(root);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_supported_hints(&self, root: ffi::Window) -> Vec<ffi::Atom> {
|
fn get_supported_hints(&self, root: xproto::Window) -> Vec<xproto::Atom> {
|
||||||
let supported_atom = unsafe { self.get_atom_unchecked(b"_NET_SUPPORTED\0") };
|
let atoms = self.atoms();
|
||||||
self.get_property(root, supported_atom, ffi::XA_ATOM)
|
let supported_atom = atoms[_NET_SUPPORTED];
|
||||||
.unwrap_or_else(|_| Vec::with_capacity(0))
|
self.get_property(
|
||||||
|
root,
|
||||||
|
supported_atom,
|
||||||
|
xproto::Atom::from(xproto::AtomEnum::ATOM),
|
||||||
|
)
|
||||||
|
.unwrap_or_else(|_| Vec::with_capacity(0))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_wm_name(&self, root: ffi::Window) -> Option<String> {
|
#[allow(clippy::useless_conversion)]
|
||||||
let check_atom = unsafe { self.get_atom_unchecked(b"_NET_SUPPORTING_WM_CHECK\0") };
|
fn get_wm_name(&self, root: xproto::Window) -> Option<String> {
|
||||||
let wm_name_atom = unsafe { self.get_atom_unchecked(b"_NET_WM_NAME\0") };
|
let atoms = self.atoms();
|
||||||
|
let check_atom = atoms[_NET_SUPPORTING_WM_CHECK];
|
||||||
|
let wm_name_atom = atoms[_NET_WM_NAME];
|
||||||
|
|
||||||
// Mutter/Muffin/Budgie doesn't have _NET_SUPPORTING_WM_CHECK in its _NET_SUPPORTED, despite
|
// Mutter/Muffin/Budgie doesn't have _NET_SUPPORTING_WM_CHECK in its _NET_SUPPORTED, despite
|
||||||
// it working and being supported. This has been reported upstream, but due to the
|
// it working and being supported. This has been reported upstream, but due to the
|
||||||
|
@ -70,7 +77,11 @@ impl XConnection {
|
||||||
// Querying this property on the root window will give us the ID of a child window created by
|
// Querying this property on the root window will give us the ID of a child window created by
|
||||||
// the WM.
|
// the WM.
|
||||||
let root_window_wm_check = {
|
let root_window_wm_check = {
|
||||||
let result = self.get_property(root, check_atom, ffi::XA_WINDOW);
|
let result = self.get_property::<xproto::Window>(
|
||||||
|
root,
|
||||||
|
check_atom,
|
||||||
|
xproto::Atom::from(xproto::AtomEnum::WINDOW),
|
||||||
|
);
|
||||||
|
|
||||||
let wm_check = result.ok().and_then(|wm_check| wm_check.first().cloned());
|
let wm_check = result.ok().and_then(|wm_check| wm_check.first().cloned());
|
||||||
|
|
||||||
|
@ -80,7 +91,11 @@ impl XConnection {
|
||||||
// Querying the same property on the child window we were given, we should get this child
|
// Querying the same property on the child window we were given, we should get this child
|
||||||
// window's ID again.
|
// window's ID again.
|
||||||
let child_window_wm_check = {
|
let child_window_wm_check = {
|
||||||
let result = self.get_property(root_window_wm_check, check_atom, ffi::XA_WINDOW);
|
let result = self.get_property::<xproto::Window>(
|
||||||
|
root_window_wm_check.into(),
|
||||||
|
check_atom,
|
||||||
|
xproto::Atom::from(xproto::AtomEnum::WINDOW),
|
||||||
|
);
|
||||||
|
|
||||||
let wm_check = result.ok().and_then(|wm_check| wm_check.first().cloned());
|
let wm_check = result.ok().and_then(|wm_check| wm_check.first().cloned());
|
||||||
|
|
||||||
|
@ -94,9 +109,11 @@ impl XConnection {
|
||||||
|
|
||||||
// All of that work gives us a window ID that we can get the WM name from.
|
// All of that work gives us a window ID that we can get the WM name from.
|
||||||
let wm_name = {
|
let wm_name = {
|
||||||
let utf8_string_atom = unsafe { self.get_atom_unchecked(b"UTF8_STRING\0") };
|
let atoms = self.atoms();
|
||||||
|
let utf8_string_atom = atoms[UTF8_STRING];
|
||||||
|
|
||||||
let result = self.get_property(root_window_wm_check, wm_name_atom, utf8_string_atom);
|
let result =
|
||||||
|
self.get_property(root_window_wm_check.into(), wm_name_atom, utf8_string_atom);
|
||||||
|
|
||||||
// IceWM requires this. IceWM was also the only WM tested that returns a null-terminated
|
// IceWM requires this. IceWM was also the only WM tested that returns a null-terminated
|
||||||
// string. For more fun trivia, IceWM is also unique in including version and uname
|
// string. For more fun trivia, IceWM is also unique in including version and uname
|
||||||
|
@ -105,13 +122,17 @@ impl XConnection {
|
||||||
// The unofficial 1.4 fork of IceWM still includes the extra details, but properly
|
// The unofficial 1.4 fork of IceWM still includes the extra details, but properly
|
||||||
// returns a UTF8 string that isn't null-terminated.
|
// returns a UTF8 string that isn't null-terminated.
|
||||||
let no_utf8 = if let Err(ref err) = result {
|
let no_utf8 = if let Err(ref err) = result {
|
||||||
err.is_actual_property_type(ffi::XA_STRING)
|
err.is_actual_property_type(xproto::Atom::from(xproto::AtomEnum::STRING))
|
||||||
} else {
|
} else {
|
||||||
false
|
false
|
||||||
};
|
};
|
||||||
|
|
||||||
if no_utf8 {
|
if no_utf8 {
|
||||||
self.get_property(root_window_wm_check, wm_name_atom, ffi::XA_STRING)
|
self.get_property(
|
||||||
|
root_window_wm_check.into(),
|
||||||
|
wm_name_atom,
|
||||||
|
xproto::Atom::from(xproto::AtomEnum::STRING),
|
||||||
|
)
|
||||||
} else {
|
} else {
|
||||||
result
|
result
|
||||||
}
|
}
|
||||||
|
|
File diff suppressed because it is too large
Load diff
|
@ -1,8 +1,14 @@
|
||||||
use std::{collections::HashMap, error::Error, fmt, os::raw::c_int, ptr, sync::Mutex};
|
use std::{
|
||||||
|
collections::HashMap,
|
||||||
|
error::Error,
|
||||||
|
fmt, ptr,
|
||||||
|
sync::{Arc, Mutex},
|
||||||
|
};
|
||||||
|
|
||||||
use crate::window::CursorIcon;
|
use crate::window::CursorIcon;
|
||||||
|
|
||||||
use super::ffi;
|
use super::{atoms::Atoms, ffi};
|
||||||
|
use x11rb::{connection::Connection, protocol::xproto, xcb_ffi::XCBConnection};
|
||||||
|
|
||||||
/// A connection to an X server.
|
/// A connection to an X server.
|
||||||
pub(crate) struct XConnection {
|
pub(crate) struct XConnection {
|
||||||
|
@ -11,9 +17,21 @@ pub(crate) struct XConnection {
|
||||||
pub xrandr: ffi::Xrandr_2_2_0,
|
pub xrandr: ffi::Xrandr_2_2_0,
|
||||||
pub xcursor: ffi::Xcursor,
|
pub xcursor: ffi::Xcursor,
|
||||||
pub xinput2: ffi::XInput2,
|
pub xinput2: ffi::XInput2,
|
||||||
pub xlib_xcb: ffi::Xlib_xcb,
|
|
||||||
pub display: *mut ffi::Display,
|
pub display: *mut ffi::Display,
|
||||||
pub x11_fd: c_int,
|
/// The manager for the XCB connection.
|
||||||
|
///
|
||||||
|
/// The `Option` ensures that we can drop it before we close the `Display`.
|
||||||
|
xcb: Option<XCBConnection>,
|
||||||
|
|
||||||
|
/// The atoms used by `winit`.
|
||||||
|
///
|
||||||
|
/// This is a large structure, so I've elected to Box it to make accessing the fields of
|
||||||
|
/// this struct easier. Feel free to unbox it if you like kicking puppies.
|
||||||
|
atoms: Box<Atoms>,
|
||||||
|
|
||||||
|
/// The index of the default screen.
|
||||||
|
default_screen: usize,
|
||||||
|
|
||||||
pub latest_error: Mutex<Option<XError>>,
|
pub latest_error: Mutex<Option<XError>>,
|
||||||
pub cursor_cache: Mutex<HashMap<Option<CursorIcon>, ffi::Cursor>>,
|
pub cursor_cache: Mutex<HashMap<Option<CursorIcon>, ffi::Cursor>>,
|
||||||
}
|
}
|
||||||
|
@ -45,17 +63,38 @@ impl XConnection {
|
||||||
display
|
display
|
||||||
};
|
};
|
||||||
|
|
||||||
// Get X11 socket file descriptor
|
// Open the x11rb XCB connection.
|
||||||
let fd = unsafe { (xlib.XConnectionNumber)(display) };
|
let xcb = {
|
||||||
|
// Get a pointer to the underlying XCB connection
|
||||||
|
let xcb_connection =
|
||||||
|
unsafe { (xlib_xcb.XGetXCBConnection)(display as *mut ffi::Display) };
|
||||||
|
assert!(!xcb_connection.is_null());
|
||||||
|
|
||||||
|
// Wrap the XCB connection in an x11rb XCB connection
|
||||||
|
let conn =
|
||||||
|
unsafe { XCBConnection::from_raw_xcb_connection(xcb_connection.cast(), false) };
|
||||||
|
|
||||||
|
conn.map_err(|e| XNotSupported::XcbConversionError(Arc::new(WrapConnectError(e))))?
|
||||||
|
};
|
||||||
|
|
||||||
|
// Get the default screen.
|
||||||
|
let default_screen = unsafe { (xlib.XDefaultScreen)(display) } as usize;
|
||||||
|
|
||||||
|
// Fetch the atoms.
|
||||||
|
let atoms = Atoms::new(&xcb)
|
||||||
|
.map_err(|e| XNotSupported::XcbConversionError(Arc::new(e)))?
|
||||||
|
.reply()
|
||||||
|
.map_err(|e| XNotSupported::XcbConversionError(Arc::new(e)))?;
|
||||||
|
|
||||||
Ok(XConnection {
|
Ok(XConnection {
|
||||||
xlib,
|
xlib,
|
||||||
xrandr,
|
xrandr,
|
||||||
xcursor,
|
xcursor,
|
||||||
xinput2,
|
xinput2,
|
||||||
xlib_xcb,
|
|
||||||
display,
|
display,
|
||||||
x11_fd: fd,
|
xcb: Some(xcb),
|
||||||
|
atoms: Box::new(atoms),
|
||||||
|
default_screen,
|
||||||
latest_error: Mutex::new(None),
|
latest_error: Mutex::new(None),
|
||||||
cursor_cache: Default::default(),
|
cursor_cache: Default::default(),
|
||||||
})
|
})
|
||||||
|
@ -71,6 +110,32 @@ impl XConnection {
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Get the underlying XCB connection.
|
||||||
|
#[inline]
|
||||||
|
pub fn xcb_connection(&self) -> &XCBConnection {
|
||||||
|
self.xcb
|
||||||
|
.as_ref()
|
||||||
|
.expect("xcb_connection somehow called after drop?")
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Get the list of atoms.
|
||||||
|
#[inline]
|
||||||
|
pub fn atoms(&self) -> &Atoms {
|
||||||
|
&self.atoms
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Get the index of the default screen.
|
||||||
|
#[inline]
|
||||||
|
pub fn default_screen_index(&self) -> usize {
|
||||||
|
self.default_screen
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Get the default screen.
|
||||||
|
#[inline]
|
||||||
|
pub fn default_root(&self) -> &xproto::Screen {
|
||||||
|
&self.xcb_connection().setup().roots[self.default_screen]
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl fmt::Debug for XConnection {
|
impl fmt::Debug for XConnection {
|
||||||
|
@ -82,6 +147,7 @@ impl fmt::Debug for XConnection {
|
||||||
impl Drop for XConnection {
|
impl Drop for XConnection {
|
||||||
#[inline]
|
#[inline]
|
||||||
fn drop(&mut self) {
|
fn drop(&mut self) {
|
||||||
|
self.xcb = None;
|
||||||
unsafe { (self.xlib.XCloseDisplay)(self.display) };
|
unsafe { (self.xlib.XCloseDisplay)(self.display) };
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -112,8 +178,12 @@ impl fmt::Display for XError {
|
||||||
pub enum XNotSupported {
|
pub enum XNotSupported {
|
||||||
/// Failed to load one or several shared libraries.
|
/// Failed to load one or several shared libraries.
|
||||||
LibraryOpenError(ffi::OpenError),
|
LibraryOpenError(ffi::OpenError),
|
||||||
|
|
||||||
/// Connecting to the X server with `XOpenDisplay` failed.
|
/// Connecting to the X server with `XOpenDisplay` failed.
|
||||||
XOpenDisplayFailed, // TODO: add better message
|
XOpenDisplayFailed, // TODO: add better message.
|
||||||
|
|
||||||
|
/// We encountered an error while converting the connection to XCB.
|
||||||
|
XcbConversionError(Arc<dyn Error + Send + Sync + 'static>),
|
||||||
}
|
}
|
||||||
|
|
||||||
impl From<ffi::OpenError> for XNotSupported {
|
impl From<ffi::OpenError> for XNotSupported {
|
||||||
|
@ -128,6 +198,7 @@ impl XNotSupported {
|
||||||
match self {
|
match self {
|
||||||
XNotSupported::LibraryOpenError(_) => "Failed to load one of xlib's shared libraries",
|
XNotSupported::LibraryOpenError(_) => "Failed to load one of xlib's shared libraries",
|
||||||
XNotSupported::XOpenDisplayFailed => "Failed to open connection to X server",
|
XNotSupported::XOpenDisplayFailed => "Failed to open connection to X server",
|
||||||
|
XNotSupported::XcbConversionError(_) => "Failed to convert Xlib connection to XCB",
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -137,6 +208,7 @@ impl Error for XNotSupported {
|
||||||
fn source(&self) -> Option<&(dyn Error + 'static)> {
|
fn source(&self) -> Option<&(dyn Error + 'static)> {
|
||||||
match *self {
|
match *self {
|
||||||
XNotSupported::LibraryOpenError(ref err) => Some(err),
|
XNotSupported::LibraryOpenError(ref err) => Some(err),
|
||||||
|
XNotSupported::XcbConversionError(ref err) => Some(&**err),
|
||||||
_ => None,
|
_ => None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -147,3 +219,19 @@ impl fmt::Display for XNotSupported {
|
||||||
formatter.write_str(self.description())
|
formatter.write_str(self.description())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// A newtype wrapper around a `ConnectError` that can't be accessed by downstream libraries.
|
||||||
|
///
|
||||||
|
/// Without this, `x11rb` would become a public dependency.
|
||||||
|
#[derive(Debug)]
|
||||||
|
struct WrapConnectError(x11rb::rust_connection::ConnectError);
|
||||||
|
|
||||||
|
impl fmt::Display for WrapConnectError {
|
||||||
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||||
|
fmt::Display::fmt(&self.0, f)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Error for WrapConnectError {
|
||||||
|
// We can't implement `source()` here or otherwise risk exposing `x11rb`.
|
||||||
|
}
|
||||||
|
|
Loading…
Add table
Reference in a new issue