Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Replace parts of the Xlib backend with X11RB #2825

Merged
merged 3 commits into from
Jul 12, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 3 additions & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ rustdoc-args = ["--cfg", "docsrs"]

[features]
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-dlopen = ["wayland-backend/dlopen"]
wayland-csd-adwaita = ["sctk-adwaita", "sctk-adwaita/ab_glyph"]
Expand Down Expand Up @@ -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]
bytemuck = { version = "1.13.1", default-features = false, optional = true }
libc = "0.2.64"
percent-encoding = { version = "2.0", optional = true }
fnv = { version = "1.0.3", optional = true }
Expand All @@ -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 }
calloop = "0.10.5"
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"
memmap2 = { version = "0.5.0", optional = true }

Expand Down
6 changes: 3 additions & 3 deletions src/platform_impl/linux/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ use smol_str::SmolStr;
#[cfg(x11_platform)]
pub use self::x11::XNotSupported;
#[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)]
use crate::platform::x11::XlibErrorHook;
use crate::{
Expand Down Expand Up @@ -124,7 +124,7 @@ pub(crate) static X11_BACKEND: Lazy<Mutex<Result<Arc<XConnection>, XNotSupported
#[derive(Debug, Clone)]
pub enum OsError {
#[cfg(x11_platform)]
XError(XError),
XError(Arc<X11Error>),
#[cfg(x11_platform)]
XMisc(&'static str),
#[cfg(wayland_platform)]
Expand All @@ -135,7 +135,7 @@ impl fmt::Display for OsError {
fn fmt(&self, _f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> {
match *self {
#[cfg(x11_platform)]
OsError::XError(ref e) => _f.pad(&e.description),
OsError::XError(ref e) => fmt::Display::fmt(e, _f),
#[cfg(x11_platform)]
OsError::XMisc(e) => _f.pad(e),
#[cfg(wayland_platform)]
Expand Down
110 changes: 110 additions & 0 deletions src/platform_impl/linux/x11/atoms.rs
Original file line number Diff line number Diff line change
@@ -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;
142 changes: 55 additions & 87 deletions src/platform_impl/linux/x11/dnd.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,55 +7,12 @@ use std::{
};

use percent_encoding::percent_decode;
use x11rb::protocol::xproto::{self, ConnectionExt};

use super::{ffi, util, XConnection, XError};

#[derive(Debug)]
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],
})
}
}
use super::{
atoms::{AtomName::None as DndNone, *},
util, CookieResultExt, X11Error, XConnection,
};

#[derive(Debug, Clone, Copy)]
pub enum DndState {
Expand Down Expand Up @@ -86,22 +43,19 @@ impl From<io::Error> for DndDataParseError {

pub(crate) struct Dnd {
xconn: Arc<XConnection>,
pub atoms: DndAtoms,
// Populated by XdndEnter event handler
pub version: Option<c_long>,
pub type_list: Option<Vec<c_ulong>>,
pub type_list: Option<Vec<xproto::Atom>>,
// 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)
pub result: Option<Result<Vec<PathBuf>, DndDataParseError>>,
}

impl Dnd {
pub fn new(xconn: Arc<XConnection>) -> Result<Self, XError> {
let atoms = DndAtoms::new(&xconn)?;
pub fn new(xconn: Arc<XConnection>) -> Result<Self, X11Error> {
Ok(Dnd {
xconn,
atoms,
version: None,
type_list: None,
source_window: None,
Expand All @@ -118,71 +72,85 @@ impl Dnd {

pub unsafe fn send_status(
&self,
this_window: c_ulong,
target_window: c_ulong,
this_window: xproto::Window,
target_window: xproto::Window,
state: DndState,
) -> Result<(), XError> {
) -> Result<(), X11Error> {
let atoms = self.xconn.atoms();
let (accepted, action) = match state {
DndState::Accepted => (1, self.atoms.action_private as c_long),
DndState::Rejected => (0, self.atoms.none as c_long),
DndState::Accepted => (1, atoms[XdndActionPrivate]),
DndState::Rejected => (0, atoms[DndNone]),
};
self.xconn
.send_client_msg(
target_window,
target_window,
self.atoms.status,
atoms[XdndStatus] as _,
None,
[this_window as c_long, accepted, 0, 0, action],
)
.flush()
[this_window, accepted, 0, 0, action as _],
)?
.ignore_error();

Ok(())
}

pub unsafe fn send_finished(
&self,
this_window: c_ulong,
target_window: c_ulong,
this_window: xproto::Window,
target_window: xproto::Window,
state: DndState,
) -> Result<(), XError> {
) -> Result<(), X11Error> {
let atoms = self.xconn.atoms();
let (accepted, action) = match state {
DndState::Accepted => (1, self.atoms.action_private as c_long),
DndState::Rejected => (0, self.atoms.none as c_long),
DndState::Accepted => (1, atoms[XdndActionPrivate]),
DndState::Rejected => (0, atoms[DndNone]),
};
self.xconn
.send_client_msg(
target_window,
target_window,
self.atoms.finished,
atoms[XdndFinished] as _,
None,
[this_window as c_long, accepted, action, 0, 0],
)
.flush()
[this_window, accepted, action as _, 0, 0],
)?
.ignore_error();

Ok(())
}

pub unsafe fn get_type_list(
&self,
source_window: c_ulong,
) -> Result<Vec<ffi::Atom>, util::GetPropertyError> {
self.xconn
.get_property(source_window, self.atoms.type_list, ffi::XA_ATOM)
source_window: xproto::Window,
) -> Result<Vec<xproto::Atom>, util::GetPropertyError> {
let atoms = self.xconn.atoms();
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) {
(self.xconn.xlib.XConvertSelection)(
self.xconn.display,
self.atoms.selection,
self.atoms.uri_list,
self.atoms.selection,
window,
time,
);
pub unsafe fn convert_selection(&self, window: xproto::Window, time: xproto::Timestamp) {
let atoms = self.xconn.atoms();
self.xconn
.xcb_connection()
.convert_selection(
window,
atoms[XdndSelection],
atoms[TextUriList],
atoms[XdndSelection],
time,
)
.expect_then_ignore_error("Failed to send XdndSelection event")
}

pub unsafe fn read_data(
&self,
window: c_ulong,
window: xproto::Window,
) -> Result<Vec<c_uchar>, util::GetPropertyError> {
let atoms = self.xconn.atoms();
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> {
Expand Down
Loading