Switch from xcb crate to x11rb (#173)
Replace the `xcb` and `xcb-util` crates with `x11rb`. We were using an old version of the `xcb` crate which had some soundness issue. `x11rb` doesn't have these issues and generally provides a safer and nicer to use API. It's possible to use `x11rb` without linking to xcb at all, using the `RustConnection` API, but unfortunately we have to use the `XCBConnection` API (which uses xcb under the hood) due to our use of the xlib GLX API for creating OpenGL contexts. In the future, it might be possible to avoid linking to xlib and xcb by replacing GLX with EGL. Getting the xlib-xcb integration to work also necessitated upgrading the version of the `x11` crate, since the version we were using was missing some necessary functionality that was previously being provided by the `xcb` crate.
This commit is contained in:
parent
998ced845c
commit
fdc5d282fc
|
@ -23,9 +23,8 @@ keyboard-types = { version = "0.6.1", default-features = false }
|
||||||
raw-window-handle = "0.5"
|
raw-window-handle = "0.5"
|
||||||
|
|
||||||
[target.'cfg(target_os="linux")'.dependencies]
|
[target.'cfg(target_os="linux")'.dependencies]
|
||||||
xcb = { version = "0.9", features = ["thread", "xlib_xcb", "dri2"] }
|
x11rb = { version = "0.13.0", features = ["cursor", "resource_manager", "allow-unsafe-code"] }
|
||||||
x11 = { version = "2.18", features = ["xlib", "xcursor"] }
|
x11 = { version = "2.21", features = ["xlib", "xcursor", "xlib_xcb"] }
|
||||||
xcb-util = { version = "0.3", features = ["icccm"] }
|
|
||||||
nix = "0.22.0"
|
nix = "0.22.0"
|
||||||
|
|
||||||
[target.'cfg(target_os="windows")'.dependencies]
|
[target.'cfg(target_os="windows")'.dependencies]
|
||||||
|
|
|
@ -1,105 +1,100 @@
|
||||||
use std::os::raw::c_char;
|
use std::error::Error;
|
||||||
|
|
||||||
|
use x11rb::connection::Connection;
|
||||||
|
use x11rb::cursor::Handle as CursorHandle;
|
||||||
|
use x11rb::protocol::xproto::{ConnectionExt as _, Cursor};
|
||||||
|
use x11rb::xcb_ffi::XCBConnection;
|
||||||
|
|
||||||
use crate::MouseCursor;
|
use crate::MouseCursor;
|
||||||
|
|
||||||
fn create_empty_cursor(display: *mut x11::xlib::Display) -> Option<u32> {
|
fn create_empty_cursor(conn: &XCBConnection, screen: usize) -> Result<Cursor, Box<dyn Error>> {
|
||||||
let data = 0;
|
let cursor_id = conn.generate_id()?;
|
||||||
let pixmap = unsafe {
|
let pixmap_id = conn.generate_id()?;
|
||||||
let screen = x11::xlib::XDefaultScreen(display);
|
let root_window = conn.setup().roots[screen].root;
|
||||||
let window = x11::xlib::XRootWindow(display, screen);
|
conn.create_pixmap(1, pixmap_id, root_window, 1, 1)?;
|
||||||
x11::xlib::XCreateBitmapFromData(display, window, &data, 1, 1)
|
conn.create_cursor(cursor_id, pixmap_id, pixmap_id, 0, 0, 0, 0, 0, 0, 0, 0)?;
|
||||||
};
|
conn.free_pixmap(pixmap_id)?;
|
||||||
|
|
||||||
if pixmap == 0 {
|
Ok(cursor_id)
|
||||||
return None;
|
|
||||||
}
|
|
||||||
|
|
||||||
unsafe {
|
|
||||||
// We don't care about this color, since it only fills bytes
|
|
||||||
// in the pixmap which are not 0 in the mask.
|
|
||||||
let mut color: x11::xlib::XColor = std::mem::zeroed();
|
|
||||||
|
|
||||||
let cursor = x11::xlib::XCreatePixmapCursor(
|
|
||||||
display,
|
|
||||||
pixmap,
|
|
||||||
pixmap,
|
|
||||||
&mut color as *mut _,
|
|
||||||
&mut color as *mut _,
|
|
||||||
0,
|
|
||||||
0,
|
|
||||||
);
|
|
||||||
x11::xlib::XFreePixmap(display, pixmap);
|
|
||||||
|
|
||||||
Some(cursor as u32)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn load_cursor(display: *mut x11::xlib::Display, name: &[u8]) -> Option<u32> {
|
fn load_cursor(
|
||||||
let xcursor =
|
conn: &XCBConnection, cursor_handle: &CursorHandle, name: &str,
|
||||||
unsafe { x11::xcursor::XcursorLibraryLoadCursor(display, name.as_ptr() as *const c_char) };
|
) -> Result<Option<Cursor>, Box<dyn Error>> {
|
||||||
|
let cursor = cursor_handle.load_cursor(conn, name)?;
|
||||||
if xcursor == 0 {
|
if cursor != x11rb::NONE {
|
||||||
None
|
Ok(Some(cursor))
|
||||||
} else {
|
} else {
|
||||||
Some(xcursor as u32)
|
Ok(None)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn load_first_existing_cursor(display: *mut x11::xlib::Display, names: &[&[u8]]) -> Option<u32> {
|
fn load_first_existing_cursor(
|
||||||
names
|
conn: &XCBConnection, cursor_handle: &CursorHandle, names: &[&str],
|
||||||
.iter()
|
) -> Result<Option<Cursor>, Box<dyn Error>> {
|
||||||
.map(|name| load_cursor(display, name))
|
for name in names {
|
||||||
.find(|xcursor| xcursor.is_some())
|
let cursor = load_cursor(conn, cursor_handle, name)?;
|
||||||
.unwrap_or(None)
|
if cursor.is_some() {
|
||||||
|
return Ok(cursor);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(None)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(super) fn get_xcursor(display: *mut x11::xlib::Display, cursor: MouseCursor) -> u32 {
|
pub(super) fn get_xcursor(
|
||||||
let load = |name: &[u8]| load_cursor(display, name);
|
conn: &XCBConnection, screen: usize, cursor_handle: &CursorHandle, cursor: MouseCursor,
|
||||||
let loadn = |names: &[&[u8]]| load_first_existing_cursor(display, names);
|
) -> Result<Cursor, Box<dyn Error>> {
|
||||||
|
let load = |name: &str| load_cursor(conn, cursor_handle, name);
|
||||||
|
let loadn = |names: &[&str]| load_first_existing_cursor(conn, cursor_handle, names);
|
||||||
|
|
||||||
let cursor = match cursor {
|
let cursor = match cursor {
|
||||||
MouseCursor::Default => None, // catch this in the fallback case below
|
MouseCursor::Default => None, // catch this in the fallback case below
|
||||||
|
|
||||||
MouseCursor::Hand => loadn(&[b"hand2\0", b"hand1\0"]),
|
MouseCursor::Hand => loadn(&["hand2", "hand1"])?,
|
||||||
MouseCursor::HandGrabbing => loadn(&[b"closedhand\0", b"grabbing\0"]),
|
MouseCursor::HandGrabbing => loadn(&["closedhand", "grabbing"])?,
|
||||||
MouseCursor::Help => load(b"question_arrow\0"),
|
MouseCursor::Help => load("question_arrow")?,
|
||||||
|
|
||||||
MouseCursor::Hidden => create_empty_cursor(display),
|
MouseCursor::Hidden => Some(create_empty_cursor(conn, screen)?),
|
||||||
|
|
||||||
MouseCursor::Text => loadn(&[b"text\0", b"xterm\0"]),
|
MouseCursor::Text => loadn(&["text", "xterm"])?,
|
||||||
MouseCursor::VerticalText => load(b"vertical-text\0"),
|
MouseCursor::VerticalText => load("vertical-text")?,
|
||||||
|
|
||||||
MouseCursor::Working => load(b"watch\0"),
|
MouseCursor::Working => load("watch")?,
|
||||||
MouseCursor::PtrWorking => load(b"left_ptr_watch\0"),
|
MouseCursor::PtrWorking => load("left_ptr_watch")?,
|
||||||
|
|
||||||
MouseCursor::NotAllowed => load(b"crossed_circle\0"),
|
MouseCursor::NotAllowed => load("crossed_circle")?,
|
||||||
MouseCursor::PtrNotAllowed => loadn(&[b"no-drop\0", b"crossed_circle\0"]),
|
MouseCursor::PtrNotAllowed => loadn(&["no-drop", "crossed_circle"])?,
|
||||||
|
|
||||||
MouseCursor::ZoomIn => load(b"zoom-in\0"),
|
MouseCursor::ZoomIn => load("zoom-in")?,
|
||||||
MouseCursor::ZoomOut => load(b"zoom-out\0"),
|
MouseCursor::ZoomOut => load("zoom-out")?,
|
||||||
|
|
||||||
MouseCursor::Alias => load(b"link\0"),
|
MouseCursor::Alias => load("link")?,
|
||||||
MouseCursor::Copy => load(b"copy\0"),
|
MouseCursor::Copy => load("copy")?,
|
||||||
MouseCursor::Move => load(b"move\0"),
|
MouseCursor::Move => load("move")?,
|
||||||
MouseCursor::AllScroll => load(b"all-scroll\0"),
|
MouseCursor::AllScroll => load("all-scroll")?,
|
||||||
MouseCursor::Cell => load(b"plus\0"),
|
MouseCursor::Cell => load("plus")?,
|
||||||
MouseCursor::Crosshair => load(b"crosshair\0"),
|
MouseCursor::Crosshair => load("crosshair")?,
|
||||||
|
|
||||||
MouseCursor::EResize => load(b"right_side\0"),
|
MouseCursor::EResize => load("right_side")?,
|
||||||
MouseCursor::NResize => load(b"top_side\0"),
|
MouseCursor::NResize => load("top_side")?,
|
||||||
MouseCursor::NeResize => load(b"top_right_corner\0"),
|
MouseCursor::NeResize => load("top_right_corner")?,
|
||||||
MouseCursor::NwResize => load(b"top_left_corner\0"),
|
MouseCursor::NwResize => load("top_left_corner")?,
|
||||||
MouseCursor::SResize => load(b"bottom_side\0"),
|
MouseCursor::SResize => load("bottom_side")?,
|
||||||
MouseCursor::SeResize => load(b"bottom_right_corner\0"),
|
MouseCursor::SeResize => load("bottom_right_corner")?,
|
||||||
MouseCursor::SwResize => load(b"bottom_left_corner\0"),
|
MouseCursor::SwResize => load("bottom_left_corner")?,
|
||||||
MouseCursor::WResize => load(b"left_side\0"),
|
MouseCursor::WResize => load("left_side")?,
|
||||||
MouseCursor::EwResize => load(b"h_double_arrow\0"),
|
MouseCursor::EwResize => load("h_double_arrow")?,
|
||||||
MouseCursor::NsResize => load(b"v_double_arrow\0"),
|
MouseCursor::NsResize => load("v_double_arrow")?,
|
||||||
MouseCursor::NwseResize => loadn(&[b"bd_double_arrow\0", b"size_bdiag\0"]),
|
MouseCursor::NwseResize => loadn(&["bd_double_arrow", "size_bdiag"])?,
|
||||||
MouseCursor::NeswResize => loadn(&[b"fd_double_arrow\0", b"size_fdiag\0"]),
|
MouseCursor::NeswResize => loadn(&["fd_double_arrow", "size_fdiag"])?,
|
||||||
MouseCursor::ColResize => loadn(&[b"split_h\0", b"h_double_arrow\0"]),
|
MouseCursor::ColResize => loadn(&["split_h", "h_double_arrow"])?,
|
||||||
MouseCursor::RowResize => loadn(&[b"split_v\0", b"v_double_arrow\0"]),
|
MouseCursor::RowResize => loadn(&["split_v", "v_double_arrow"])?,
|
||||||
};
|
};
|
||||||
|
|
||||||
cursor.or_else(|| load(b"left_ptr\0")).unwrap_or(0)
|
if let Some(cursor) = cursor {
|
||||||
|
Ok(cursor)
|
||||||
|
} else {
|
||||||
|
Ok(load("left_ptr")?.unwrap_or(x11rb::NONE))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -18,7 +18,7 @@
|
||||||
|
|
||||||
//! X11 keyboard handling
|
//! X11 keyboard handling
|
||||||
|
|
||||||
use xcb::xproto;
|
use x11rb::protocol::xproto::{KeyButMask, KeyPressEvent, KeyReleaseEvent};
|
||||||
|
|
||||||
use keyboard_types::*;
|
use keyboard_types::*;
|
||||||
|
|
||||||
|
@ -361,32 +361,32 @@ fn hardware_keycode_to_code(hw_keycode: u16) -> Code {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Extracts the keyboard modifiers from, e.g., the `state` field of
|
// Extracts the keyboard modifiers from, e.g., the `state` field of
|
||||||
// `xcb::xproto::ButtonPressEvent`
|
// `x11rb::protocol::xproto::ButtonPressEvent`
|
||||||
pub(super) fn key_mods(mods: u16) -> Modifiers {
|
pub(super) fn key_mods(mods: KeyButMask) -> Modifiers {
|
||||||
let mut ret = Modifiers::default();
|
let mut ret = Modifiers::default();
|
||||||
let mut key_masks = [
|
let key_masks = [
|
||||||
(xproto::MOD_MASK_SHIFT, Modifiers::SHIFT),
|
(KeyButMask::SHIFT, Modifiers::SHIFT),
|
||||||
(xproto::MOD_MASK_CONTROL, Modifiers::CONTROL),
|
(KeyButMask::CONTROL, Modifiers::CONTROL),
|
||||||
// X11's mod keys are configurable, but this seems
|
// X11's mod keys are configurable, but this seems
|
||||||
// like a reasonable default for US keyboards, at least,
|
// like a reasonable default for US keyboards, at least,
|
||||||
// where the "windows" key seems to be MOD_MASK_4.
|
// where the "windows" key seems to be MOD_MASK_4.
|
||||||
(xproto::MOD_MASK_1, Modifiers::ALT),
|
(KeyButMask::BUTTON1, Modifiers::ALT),
|
||||||
(xproto::MOD_MASK_2, Modifiers::NUM_LOCK),
|
(KeyButMask::BUTTON2, Modifiers::NUM_LOCK),
|
||||||
(xproto::MOD_MASK_4, Modifiers::META),
|
(KeyButMask::BUTTON4, Modifiers::META),
|
||||||
(xproto::MOD_MASK_LOCK, Modifiers::CAPS_LOCK),
|
(KeyButMask::LOCK, Modifiers::CAPS_LOCK),
|
||||||
];
|
];
|
||||||
for (mask, modifiers) in &mut key_masks {
|
for (mask, modifiers) in &key_masks {
|
||||||
if mods & (*mask as u16) != 0 {
|
if mods.contains(*mask) {
|
||||||
ret |= *modifiers;
|
ret |= *modifiers;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
ret
|
ret
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(super) fn convert_key_press_event(key_press: &xcb::KeyPressEvent) -> KeyboardEvent {
|
pub(super) fn convert_key_press_event(key_press: &KeyPressEvent) -> KeyboardEvent {
|
||||||
let hw_keycode = key_press.detail();
|
let hw_keycode = key_press.detail;
|
||||||
let code = hardware_keycode_to_code(hw_keycode.into());
|
let code = hardware_keycode_to_code(hw_keycode.into());
|
||||||
let modifiers = key_mods(key_press.state());
|
let modifiers = key_mods(key_press.state);
|
||||||
let key = code_to_key(code, modifiers);
|
let key = code_to_key(code, modifiers);
|
||||||
let location = code_to_location(code);
|
let location = code_to_location(code);
|
||||||
let state = KeyState::Down;
|
let state = KeyState::Down;
|
||||||
|
@ -394,10 +394,10 @@ pub(super) fn convert_key_press_event(key_press: &xcb::KeyPressEvent) -> Keyboar
|
||||||
KeyboardEvent { code, key, modifiers, location, state, repeat: false, is_composing: false }
|
KeyboardEvent { code, key, modifiers, location, state, repeat: false, is_composing: false }
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(super) fn convert_key_release_event(key_release: &xcb::KeyReleaseEvent) -> KeyboardEvent {
|
pub(super) fn convert_key_release_event(key_release: &KeyReleaseEvent) -> KeyboardEvent {
|
||||||
let hw_keycode = key_release.detail();
|
let hw_keycode = key_release.detail;
|
||||||
let code = hardware_keycode_to_code(hw_keycode.into());
|
let code = hardware_keycode_to_code(hw_keycode.into());
|
||||||
let modifiers = key_mods(key_release.state());
|
let modifiers = key_mods(key_release.state);
|
||||||
let key = code_to_key(code, modifiers);
|
let key = code_to_key(code, modifiers);
|
||||||
let location = code_to_location(code);
|
let location = code_to_location(code);
|
||||||
let state = KeyState::Up;
|
let state = KeyState::Up;
|
||||||
|
|
|
@ -1,4 +1,6 @@
|
||||||
|
use std::error::Error;
|
||||||
use std::ffi::c_void;
|
use std::ffi::c_void;
|
||||||
|
use std::os::fd::AsRawFd;
|
||||||
use std::sync::atomic::{AtomicBool, Ordering};
|
use std::sync::atomic::{AtomicBool, Ordering};
|
||||||
use std::sync::mpsc;
|
use std::sync::mpsc;
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
|
@ -9,8 +11,15 @@ use raw_window_handle::{
|
||||||
HasRawDisplayHandle, HasRawWindowHandle, RawDisplayHandle, RawWindowHandle, XlibDisplayHandle,
|
HasRawDisplayHandle, HasRawWindowHandle, RawDisplayHandle, RawWindowHandle, XlibDisplayHandle,
|
||||||
XlibWindowHandle,
|
XlibWindowHandle,
|
||||||
};
|
};
|
||||||
use xcb::ffi::xcb_screen_t;
|
|
||||||
use xcb::StructPtr;
|
use x11rb::connection::Connection;
|
||||||
|
use x11rb::protocol::xproto::{
|
||||||
|
AtomEnum, ChangeWindowAttributesAux, ColormapAlloc, ConfigureWindowAux, ConnectionExt as _,
|
||||||
|
CreateGCAux, CreateWindowAux, EventMask, PropMode, Screen, VisualClass, Visualid,
|
||||||
|
Window as XWindow, WindowClass,
|
||||||
|
};
|
||||||
|
use x11rb::protocol::Event as XEvent;
|
||||||
|
use x11rb::wrapper::ConnectionExt as _;
|
||||||
|
|
||||||
use super::XcbConnection;
|
use super::XcbConnection;
|
||||||
use crate::{
|
use crate::{
|
||||||
|
@ -89,9 +98,9 @@ impl Drop for ParentHandle {
|
||||||
|
|
||||||
struct WindowInner {
|
struct WindowInner {
|
||||||
xcb_connection: XcbConnection,
|
xcb_connection: XcbConnection,
|
||||||
window_id: u32,
|
window_id: XWindow,
|
||||||
window_info: WindowInfo,
|
window_info: WindowInfo,
|
||||||
visual_id: u32,
|
visual_id: Visualid,
|
||||||
mouse_cursor: MouseCursor,
|
mouse_cursor: MouseCursor,
|
||||||
|
|
||||||
frame_interval: Duration,
|
frame_interval: Duration,
|
||||||
|
@ -136,7 +145,8 @@ impl<'a> Window<'a> {
|
||||||
let (parent_handle, mut window_handle) = ParentHandle::new();
|
let (parent_handle, mut window_handle) = ParentHandle::new();
|
||||||
|
|
||||||
thread::spawn(move || {
|
thread::spawn(move || {
|
||||||
Self::window_thread(Some(parent_id), options, build, tx.clone(), Some(parent_handle));
|
Self::window_thread(Some(parent_id), options, build, tx.clone(), Some(parent_handle))
|
||||||
|
.unwrap();
|
||||||
});
|
});
|
||||||
|
|
||||||
let raw_window_handle = rx.recv().unwrap().unwrap();
|
let raw_window_handle = rx.recv().unwrap().unwrap();
|
||||||
|
@ -154,7 +164,7 @@ impl<'a> Window<'a> {
|
||||||
let (tx, rx) = mpsc::sync_channel::<WindowOpenResult>(1);
|
let (tx, rx) = mpsc::sync_channel::<WindowOpenResult>(1);
|
||||||
|
|
||||||
let thread = thread::spawn(move || {
|
let thread = thread::spawn(move || {
|
||||||
Self::window_thread(None, options, build, tx, None);
|
Self::window_thread(None, options, build, tx, None).unwrap();
|
||||||
});
|
});
|
||||||
|
|
||||||
let _ = rx.recv().unwrap().unwrap();
|
let _ = rx.recv().unwrap().unwrap();
|
||||||
|
@ -167,29 +177,28 @@ impl<'a> Window<'a> {
|
||||||
fn window_thread<H, B>(
|
fn window_thread<H, B>(
|
||||||
parent: Option<u32>, options: WindowOpenOptions, build: B,
|
parent: Option<u32>, options: WindowOpenOptions, build: B,
|
||||||
tx: mpsc::SyncSender<WindowOpenResult>, parent_handle: Option<ParentHandle>,
|
tx: mpsc::SyncSender<WindowOpenResult>, parent_handle: Option<ParentHandle>,
|
||||||
) where
|
) -> Result<(), Box<dyn Error>>
|
||||||
|
where
|
||||||
H: WindowHandler + 'static,
|
H: WindowHandler + 'static,
|
||||||
B: FnOnce(&mut crate::Window) -> H,
|
B: FnOnce(&mut crate::Window) -> H,
|
||||||
B: Send + 'static,
|
B: Send + 'static,
|
||||||
{
|
{
|
||||||
// Connect to the X server
|
// Connect to the X server
|
||||||
// FIXME: baseview error type instead of unwrap()
|
// FIXME: baseview error type instead of unwrap()
|
||||||
let xcb_connection = XcbConnection::new().unwrap();
|
let xcb_connection = XcbConnection::new()?;
|
||||||
|
|
||||||
// Get screen information (?)
|
// Get screen information (?)
|
||||||
let setup = xcb_connection.conn.get_setup();
|
let setup = xcb_connection.conn2.setup();
|
||||||
let screen = setup.roots().nth(xcb_connection.xlib_display as usize).unwrap();
|
let screen = &setup.roots[xcb_connection.screen];
|
||||||
|
|
||||||
let foreground = xcb_connection.conn.generate_id();
|
let parent_id = parent.unwrap_or_else(|| screen.root);
|
||||||
|
|
||||||
let parent_id = parent.unwrap_or_else(|| screen.root());
|
let gc_id = xcb_connection.conn2.generate_id()?;
|
||||||
|
xcb_connection.conn2.create_gc(
|
||||||
xcb::create_gc(
|
gc_id,
|
||||||
&xcb_connection.conn,
|
|
||||||
foreground,
|
|
||||||
parent_id,
|
parent_id,
|
||||||
&[(xcb::GC_FOREGROUND, screen.black_pixel()), (xcb::GC_GRAPHICS_EXPOSURES, 0)],
|
&CreateGCAux::new().foreground(screen.black_pixel).graphics_exposures(0),
|
||||||
);
|
)?;
|
||||||
|
|
||||||
let scaling = match options.scale {
|
let scaling = match options.scale {
|
||||||
WindowScalePolicy::SystemScaleFactor => xcb_connection.get_scaling().unwrap_or(1.0),
|
WindowScalePolicy::SystemScaleFactor => xcb_connection.get_scaling().unwrap_or(1.0),
|
||||||
|
@ -205,17 +214,14 @@ impl<'a> Window<'a> {
|
||||||
// with that visual, and then finally create an OpenGL context for the window. If we don't
|
// with that visual, and then finally create an OpenGL context for the window. If we don't
|
||||||
// use OpenGL, then we'll just take a random visual with a 32-bit depth.
|
// use OpenGL, then we'll just take a random visual with a 32-bit depth.
|
||||||
let create_default_config = || {
|
let create_default_config = || {
|
||||||
Self::find_visual_for_depth(&screen, 32)
|
Self::find_visual_for_depth(screen, 32)
|
||||||
.map(|visual| (32, visual))
|
.map(|visual| (32, visual))
|
||||||
.unwrap_or((xcb::COPY_FROM_PARENT as u8, xcb::COPY_FROM_PARENT as u32))
|
.unwrap_or((x11rb::COPY_FROM_PARENT as u8, x11rb::COPY_FROM_PARENT as u32))
|
||||||
};
|
};
|
||||||
#[cfg(feature = "opengl")]
|
#[cfg(feature = "opengl")]
|
||||||
let (fb_config, (depth, visual)) = match options.gl_config {
|
let (fb_config, (depth, visual)) = match options.gl_config {
|
||||||
Some(gl_config) => unsafe {
|
Some(gl_config) => unsafe {
|
||||||
platform::GlContext::get_fb_config_and_visual(
|
platform::GlContext::get_fb_config_and_visual(xcb_connection.dpy, gl_config)
|
||||||
xcb_connection.conn.get_raw_dpy(),
|
|
||||||
gl_config,
|
|
||||||
)
|
|
||||||
.map(|(fb_config, window_config)| {
|
.map(|(fb_config, window_config)| {
|
||||||
(Some(fb_config), (window_config.depth, window_config.visual))
|
(Some(fb_config), (window_config.depth, window_config.visual))
|
||||||
})
|
})
|
||||||
|
@ -228,18 +234,11 @@ impl<'a> Window<'a> {
|
||||||
|
|
||||||
// For this 32-bith depth to work, you also need to define a color map and set a border
|
// For this 32-bith depth to work, you also need to define a color map and set a border
|
||||||
// pixel: https://cgit.freedesktop.org/xorg/xserver/tree/dix/window.c#n818
|
// pixel: https://cgit.freedesktop.org/xorg/xserver/tree/dix/window.c#n818
|
||||||
let colormap = xcb_connection.conn.generate_id();
|
let colormap = xcb_connection.conn2.generate_id()?;
|
||||||
xcb::create_colormap(
|
xcb_connection.conn2.create_colormap(ColormapAlloc::NONE, colormap, screen.root, visual)?;
|
||||||
&xcb_connection.conn,
|
|
||||||
xcb::COLORMAP_ALLOC_NONE as u8,
|
|
||||||
colormap,
|
|
||||||
screen.root(),
|
|
||||||
visual,
|
|
||||||
);
|
|
||||||
|
|
||||||
let window_id = xcb_connection.conn.generate_id();
|
let window_id = xcb_connection.conn2.generate_id()?;
|
||||||
xcb::create_window_checked(
|
xcb_connection.conn2.create_window(
|
||||||
&xcb_connection.conn,
|
|
||||||
depth,
|
depth,
|
||||||
window_id,
|
window_id,
|
||||||
parent_id,
|
parent_id,
|
||||||
|
@ -248,56 +247,46 @@ impl<'a> Window<'a> {
|
||||||
window_info.physical_size().width as u16, // window width
|
window_info.physical_size().width as u16, // window width
|
||||||
window_info.physical_size().height as u16, // window height
|
window_info.physical_size().height as u16, // window height
|
||||||
0, // window border
|
0, // window border
|
||||||
xcb::WINDOW_CLASS_INPUT_OUTPUT as u16,
|
WindowClass::INPUT_OUTPUT,
|
||||||
visual,
|
visual,
|
||||||
&[
|
&CreateWindowAux::new()
|
||||||
(
|
.event_mask(
|
||||||
xcb::CW_EVENT_MASK,
|
EventMask::EXPOSURE
|
||||||
xcb::EVENT_MASK_EXPOSURE
|
| EventMask::POINTER_MOTION
|
||||||
| xcb::EVENT_MASK_POINTER_MOTION
|
| EventMask::BUTTON_PRESS
|
||||||
| xcb::EVENT_MASK_BUTTON_PRESS
|
| EventMask::BUTTON_RELEASE
|
||||||
| xcb::EVENT_MASK_BUTTON_RELEASE
|
| EventMask::KEY_PRESS
|
||||||
| xcb::EVENT_MASK_KEY_PRESS
|
| EventMask::KEY_RELEASE
|
||||||
| xcb::EVENT_MASK_KEY_RELEASE
|
| EventMask::STRUCTURE_NOTIFY
|
||||||
| xcb::EVENT_MASK_STRUCTURE_NOTIFY
|
| EventMask::ENTER_WINDOW
|
||||||
| xcb::EVENT_MASK_ENTER_WINDOW
|
| EventMask::LEAVE_WINDOW,
|
||||||
| xcb::EVENT_MASK_LEAVE_WINDOW,
|
)
|
||||||
),
|
|
||||||
// As mentioned above, these two values are needed to be able to create a window
|
// As mentioned above, these two values are needed to be able to create a window
|
||||||
// with a depth of 32-bits when the parent window has a different depth
|
// with a depth of 32-bits when the parent window has a different depth
|
||||||
(xcb::CW_COLORMAP, colormap),
|
.colormap(colormap)
|
||||||
(xcb::CW_BORDER_PIXEL, 0),
|
.border_pixel(0),
|
||||||
],
|
)?;
|
||||||
)
|
xcb_connection.conn2.map_window(window_id)?;
|
||||||
.request_check()
|
|
||||||
.unwrap();
|
|
||||||
|
|
||||||
xcb::map_window(&xcb_connection.conn, window_id);
|
|
||||||
|
|
||||||
// Change window title
|
// Change window title
|
||||||
let title = options.title;
|
let title = options.title;
|
||||||
xcb::change_property(
|
xcb_connection.conn2.change_property8(
|
||||||
&xcb_connection.conn,
|
PropMode::REPLACE,
|
||||||
xcb::PROP_MODE_REPLACE as u8,
|
|
||||||
window_id,
|
window_id,
|
||||||
xcb::ATOM_WM_NAME,
|
AtomEnum::WM_NAME,
|
||||||
xcb::ATOM_STRING,
|
AtomEnum::STRING,
|
||||||
8, // view data as 8-bit
|
|
||||||
title.as_bytes(),
|
title.as_bytes(),
|
||||||
);
|
)?;
|
||||||
|
|
||||||
if let Some((wm_protocols, wm_delete_window)) =
|
xcb_connection.conn2.change_property32(
|
||||||
xcb_connection.atoms.wm_protocols.zip(xcb_connection.atoms.wm_delete_window)
|
PropMode::REPLACE,
|
||||||
{
|
|
||||||
xcb_util::icccm::set_wm_protocols(
|
|
||||||
&xcb_connection.conn,
|
|
||||||
window_id,
|
window_id,
|
||||||
wm_protocols,
|
xcb_connection.atoms2.WM_PROTOCOLS,
|
||||||
&[wm_delete_window],
|
AtomEnum::ATOM,
|
||||||
);
|
&[xcb_connection.atoms2.WM_DELETE_WINDOW],
|
||||||
}
|
)?;
|
||||||
|
|
||||||
xcb_connection.conn.flush();
|
xcb_connection.conn2.flush()?;
|
||||||
|
|
||||||
// TODO: These APIs could use a couple tweaks now that everything is internal and there is
|
// TODO: These APIs could use a couple tweaks now that everything is internal and there is
|
||||||
// no error handling anymore at this point. Everything is more or less unchanged
|
// no error handling anymore at this point. Everything is more or less unchanged
|
||||||
|
@ -307,7 +296,7 @@ impl<'a> Window<'a> {
|
||||||
use std::ffi::c_ulong;
|
use std::ffi::c_ulong;
|
||||||
|
|
||||||
let window = window_id as c_ulong;
|
let window = window_id as c_ulong;
|
||||||
let display = xcb_connection.conn.get_raw_dpy();
|
let display = xcb_connection.dpy;
|
||||||
|
|
||||||
// Because of the visual negotation we had to take some extra steps to create this context
|
// Because of the visual negotation we had to take some extra steps to create this context
|
||||||
let context = unsafe { platform::GlContext::create(window, display, fb_config) }
|
let context = unsafe { platform::GlContext::create(window, display, fb_config) }
|
||||||
|
@ -343,7 +332,9 @@ impl<'a> Window<'a> {
|
||||||
|
|
||||||
let _ = tx.send(Ok(SendableRwh(window.raw_window_handle())));
|
let _ = tx.send(Ok(SendableRwh(window.raw_window_handle())));
|
||||||
|
|
||||||
inner.run_event_loop(&mut handler);
|
inner.run_event_loop(&mut handler)?;
|
||||||
|
|
||||||
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn set_mouse_cursor(&mut self, mouse_cursor: MouseCursor) {
|
pub fn set_mouse_cursor(&mut self, mouse_cursor: MouseCursor) {
|
||||||
|
@ -351,16 +342,14 @@ impl<'a> Window<'a> {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
let xid = self.inner.xcb_connection.get_cursor_xid(mouse_cursor);
|
let xid = self.inner.xcb_connection.get_cursor(mouse_cursor).unwrap();
|
||||||
|
|
||||||
if xid != 0 {
|
if xid != 0 {
|
||||||
xcb::change_window_attributes(
|
let _ = self.inner.xcb_connection.conn2.change_window_attributes(
|
||||||
&self.inner.xcb_connection.conn,
|
|
||||||
self.inner.window_id,
|
self.inner.window_id,
|
||||||
&[(xcb::CW_CURSOR, xid)],
|
&ChangeWindowAttributesAux::new().cursor(xid),
|
||||||
);
|
);
|
||||||
|
let _ = self.inner.xcb_connection.conn2.flush();
|
||||||
self.inner.xcb_connection.conn.flush();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
self.inner.mouse_cursor = mouse_cursor;
|
self.inner.mouse_cursor = mouse_cursor;
|
||||||
|
@ -374,15 +363,13 @@ impl<'a> Window<'a> {
|
||||||
let scaling = self.inner.window_info.scale();
|
let scaling = self.inner.window_info.scale();
|
||||||
let new_window_info = WindowInfo::from_logical_size(size, scaling);
|
let new_window_info = WindowInfo::from_logical_size(size, scaling);
|
||||||
|
|
||||||
xcb::configure_window(
|
let _ = self.inner.xcb_connection.conn2.configure_window(
|
||||||
&self.inner.xcb_connection.conn,
|
|
||||||
self.inner.window_id,
|
self.inner.window_id,
|
||||||
&[
|
&ConfigureWindowAux::new()
|
||||||
(xcb::CONFIG_WINDOW_WIDTH as u16, new_window_info.physical_size().width),
|
.width(new_window_info.physical_size().width)
|
||||||
(xcb::CONFIG_WINDOW_HEIGHT as u16, new_window_info.physical_size().height),
|
.height(new_window_info.physical_size().height),
|
||||||
],
|
|
||||||
);
|
);
|
||||||
self.inner.xcb_connection.conn.flush();
|
let _ = self.inner.xcb_connection.conn2.flush();
|
||||||
|
|
||||||
// This will trigger a `ConfigureNotify` event which will in turn change `self.window_info`
|
// This will trigger a `ConfigureNotify` event which will in turn change `self.window_info`
|
||||||
// and notify the window handler about it
|
// and notify the window handler about it
|
||||||
|
@ -393,15 +380,15 @@ impl<'a> Window<'a> {
|
||||||
self.inner.gl_context.as_ref()
|
self.inner.gl_context.as_ref()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn find_visual_for_depth(screen: &StructPtr<xcb_screen_t>, depth: u8) -> Option<u32> {
|
fn find_visual_for_depth(screen: &Screen, depth: u8) -> Option<Visualid> {
|
||||||
for candidate_depth in screen.allowed_depths() {
|
for candidate_depth in &screen.allowed_depths {
|
||||||
if candidate_depth.depth() != depth {
|
if candidate_depth.depth != depth {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
for candidate_visual in candidate_depth.visuals() {
|
for candidate_visual in &candidate_depth.visuals {
|
||||||
if candidate_visual.class() == xcb::VISUAL_CLASS_TRUE_COLOR as u8 {
|
if candidate_visual.class == VisualClass::TRUE_COLOR {
|
||||||
return Some(candidate_visual.visual_id());
|
return Some(candidate_visual.visual_id);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -412,13 +399,13 @@ impl<'a> Window<'a> {
|
||||||
|
|
||||||
impl WindowInner {
|
impl WindowInner {
|
||||||
#[inline]
|
#[inline]
|
||||||
fn drain_xcb_events(&mut self, handler: &mut dyn WindowHandler) {
|
fn drain_xcb_events(&mut self, handler: &mut dyn WindowHandler) -> Result<(), Box<dyn Error>> {
|
||||||
// the X server has a tendency to send spurious/extraneous configure notify events when a
|
// the X server has a tendency to send spurious/extraneous configure notify events when a
|
||||||
// window is resized, and we need to batch those together and just send one resize event
|
// window is resized, and we need to batch those together and just send one resize event
|
||||||
// when they've all been coalesced.
|
// when they've all been coalesced.
|
||||||
self.new_physical_size = None;
|
self.new_physical_size = None;
|
||||||
|
|
||||||
while let Some(event) = self.xcb_connection.conn.poll_for_event() {
|
while let Some(event) = self.xcb_connection.conn2.poll_for_event()? {
|
||||||
self.handle_xcb_event(handler, event);
|
self.handle_xcb_event(handler, event);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -432,19 +419,18 @@ impl WindowInner {
|
||||||
Event::Window(WindowEvent::Resized(window_info)),
|
Event::Window(WindowEvent::Resized(window_info)),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
// Event loop
|
// Event loop
|
||||||
// FIXME: poll() acts fine on linux, sometimes funky on *BSD. XCB upstream uses a define to
|
// FIXME: poll() acts fine on linux, sometimes funky on *BSD. XCB upstream uses a define to
|
||||||
// switch between poll() and select() (the latter of which is fine on *BSD), and we should do
|
// switch between poll() and select() (the latter of which is fine on *BSD), and we should do
|
||||||
// the same.
|
// the same.
|
||||||
fn run_event_loop(&mut self, handler: &mut dyn WindowHandler) {
|
fn run_event_loop(&mut self, handler: &mut dyn WindowHandler) -> Result<(), Box<dyn Error>> {
|
||||||
use nix::poll::*;
|
use nix::poll::*;
|
||||||
|
|
||||||
let xcb_fd = unsafe {
|
let xcb_fd = self.xcb_connection.conn2.as_raw_fd();
|
||||||
let raw_conn = self.xcb_connection.conn.get_raw_conn();
|
|
||||||
xcb::ffi::xcb_get_file_descriptor(raw_conn)
|
|
||||||
};
|
|
||||||
|
|
||||||
let mut last_frame = Instant::now();
|
let mut last_frame = Instant::now();
|
||||||
self.event_loop_running = true;
|
self.event_loop_running = true;
|
||||||
|
@ -466,7 +452,7 @@ impl WindowInner {
|
||||||
|
|
||||||
// Check for any events in the internal buffers
|
// Check for any events in the internal buffers
|
||||||
// before going to sleep:
|
// before going to sleep:
|
||||||
self.drain_xcb_events(handler);
|
self.drain_xcb_events(handler)?;
|
||||||
|
|
||||||
// FIXME: handle errors
|
// FIXME: handle errors
|
||||||
poll(&mut fds, next_frame.duration_since(Instant::now()).subsec_millis() as i32)
|
poll(&mut fds, next_frame.duration_since(Instant::now()).subsec_millis() as i32)
|
||||||
|
@ -478,7 +464,7 @@ impl WindowInner {
|
||||||
}
|
}
|
||||||
|
|
||||||
if revents.contains(PollFlags::POLLIN) {
|
if revents.contains(PollFlags::POLLIN) {
|
||||||
self.drain_xcb_events(handler);
|
self.drain_xcb_events(handler)?;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -501,6 +487,8 @@ impl WindowInner {
|
||||||
self.close_requested = false;
|
self.close_requested = false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn handle_close_requested(&mut self, handler: &mut dyn WindowHandler) {
|
fn handle_close_requested(&mut self, handler: &mut dyn WindowHandler) {
|
||||||
|
@ -522,9 +510,7 @@ impl WindowInner {
|
||||||
self.event_loop_running = false;
|
self.event_loop_running = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
fn handle_xcb_event(&mut self, handler: &mut dyn WindowHandler, event: xcb::GenericEvent) {
|
fn handle_xcb_event(&mut self, handler: &mut dyn WindowHandler, event: XEvent) {
|
||||||
let event_type = event.response_type() & !0x80;
|
|
||||||
|
|
||||||
// For all of the keyboard and mouse events, you can fetch
|
// For all of the keyboard and mouse events, you can fetch
|
||||||
// `x`, `y`, `detail`, and `state`.
|
// `x`, `y`, `detail`, and `state`.
|
||||||
// - `x` and `y` are the position inside the window where the cursor currently is
|
// - `x` and `y` are the position inside the window where the cursor currently is
|
||||||
|
@ -545,29 +531,20 @@ impl WindowInner {
|
||||||
// the keyboard modifier keys at the time of the event.
|
// the keyboard modifier keys at the time of the event.
|
||||||
// http://rtbo.github.io/rust-xcb/src/xcb/ffi/xproto.rs.html#445
|
// http://rtbo.github.io/rust-xcb/src/xcb/ffi/xproto.rs.html#445
|
||||||
|
|
||||||
match event_type {
|
match event {
|
||||||
////
|
////
|
||||||
// window
|
// window
|
||||||
////
|
////
|
||||||
xcb::CLIENT_MESSAGE => {
|
XEvent::ClientMessage(event) => {
|
||||||
let event = unsafe { xcb::cast_event::<xcb::ClientMessageEvent>(&event) };
|
if event.format == 32
|
||||||
|
&& event.data.as_data32()[0] == self.xcb_connection.atoms2.WM_DELETE_WINDOW
|
||||||
// what an absolute tragedy this all is
|
{
|
||||||
let data = event.data().data;
|
|
||||||
let (_, data32, _) = unsafe { data.align_to::<u32>() };
|
|
||||||
|
|
||||||
let wm_delete_window =
|
|
||||||
self.xcb_connection.atoms.wm_delete_window.unwrap_or(xcb::NONE);
|
|
||||||
|
|
||||||
if wm_delete_window == data32[0] {
|
|
||||||
self.handle_close_requested(handler);
|
self.handle_close_requested(handler);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
xcb::CONFIGURE_NOTIFY => {
|
XEvent::ConfigureNotify(event) => {
|
||||||
let event = unsafe { xcb::cast_event::<xcb::ConfigureNotifyEvent>(&event) };
|
let new_physical_size = PhySize::new(event.width as u32, event.height as u32);
|
||||||
|
|
||||||
let new_physical_size = PhySize::new(event.width() as u32, event.height() as u32);
|
|
||||||
|
|
||||||
if self.new_physical_size.is_some()
|
if self.new_physical_size.is_some()
|
||||||
|| new_physical_size != self.window_info.physical_size()
|
|| new_physical_size != self.window_info.physical_size()
|
||||||
|
@ -579,68 +556,57 @@ impl WindowInner {
|
||||||
////
|
////
|
||||||
// mouse
|
// mouse
|
||||||
////
|
////
|
||||||
xcb::MOTION_NOTIFY => {
|
XEvent::MotionNotify(event) => {
|
||||||
let event = unsafe { xcb::cast_event::<xcb::MotionNotifyEvent>(&event) };
|
let physical_pos = PhyPoint::new(event.event_x as i32, event.event_y as i32);
|
||||||
let detail = event.detail();
|
|
||||||
|
|
||||||
if detail != 4 && detail != 5 {
|
|
||||||
let physical_pos =
|
|
||||||
PhyPoint::new(event.event_x() as i32, event.event_y() as i32);
|
|
||||||
let logical_pos = physical_pos.to_logical(&self.window_info);
|
let logical_pos = physical_pos.to_logical(&self.window_info);
|
||||||
|
|
||||||
handler.on_event(
|
handler.on_event(
|
||||||
&mut crate::Window::new(Window { inner: self }),
|
&mut crate::Window::new(Window { inner: self }),
|
||||||
Event::Mouse(MouseEvent::CursorMoved {
|
Event::Mouse(MouseEvent::CursorMoved {
|
||||||
position: logical_pos,
|
position: logical_pos,
|
||||||
modifiers: key_mods(event.state()),
|
modifiers: key_mods(event.state),
|
||||||
}),
|
}),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
xcb::ENTER_NOTIFY => {
|
XEvent::EnterNotify(event) => {
|
||||||
handler.on_event(
|
handler.on_event(
|
||||||
&mut crate::Window::new(Window { inner: self }),
|
&mut crate::Window::new(Window { inner: self }),
|
||||||
Event::Mouse(MouseEvent::CursorEntered),
|
Event::Mouse(MouseEvent::CursorEntered),
|
||||||
);
|
);
|
||||||
// since no `MOTION_NOTIFY` event is generated when `ENTER_NOTIFY` is generated,
|
// since no `MOTION_NOTIFY` event is generated when `ENTER_NOTIFY` is generated,
|
||||||
// we generate a CursorMoved as well, so the mouse position from here isn't lost
|
// we generate a CursorMoved as well, so the mouse position from here isn't lost
|
||||||
let event = unsafe { xcb::cast_event::<xcb::EnterNotifyEvent>(&event) };
|
let physical_pos = PhyPoint::new(event.event_x as i32, event.event_y as i32);
|
||||||
let physical_pos = PhyPoint::new(event.event_x() as i32, event.event_y() as i32);
|
|
||||||
let logical_pos = physical_pos.to_logical(&self.window_info);
|
let logical_pos = physical_pos.to_logical(&self.window_info);
|
||||||
handler.on_event(
|
handler.on_event(
|
||||||
&mut crate::Window::new(Window { inner: self }),
|
&mut crate::Window::new(Window { inner: self }),
|
||||||
Event::Mouse(MouseEvent::CursorMoved {
|
Event::Mouse(MouseEvent::CursorMoved {
|
||||||
position: logical_pos,
|
position: logical_pos,
|
||||||
modifiers: key_mods(event.state()),
|
modifiers: key_mods(event.state),
|
||||||
}),
|
}),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
xcb::LEAVE_NOTIFY => {
|
XEvent::LeaveNotify(_) => {
|
||||||
handler.on_event(
|
handler.on_event(
|
||||||
&mut crate::Window::new(Window { inner: self }),
|
&mut crate::Window::new(Window { inner: self }),
|
||||||
Event::Mouse(MouseEvent::CursorLeft),
|
Event::Mouse(MouseEvent::CursorLeft),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
xcb::BUTTON_PRESS => {
|
XEvent::ButtonPress(event) => match event.detail {
|
||||||
let event = unsafe { xcb::cast_event::<xcb::ButtonPressEvent>(&event) };
|
|
||||||
let detail = event.detail();
|
|
||||||
|
|
||||||
match detail {
|
|
||||||
4..=7 => {
|
4..=7 => {
|
||||||
handler.on_event(
|
handler.on_event(
|
||||||
&mut crate::Window::new(Window { inner: self }),
|
&mut crate::Window::new(Window { inner: self }),
|
||||||
Event::Mouse(MouseEvent::WheelScrolled {
|
Event::Mouse(MouseEvent::WheelScrolled {
|
||||||
delta: match detail {
|
delta: match event.detail {
|
||||||
4 => ScrollDelta::Lines { x: 0.0, y: 1.0 },
|
4 => ScrollDelta::Lines { x: 0.0, y: 1.0 },
|
||||||
5 => ScrollDelta::Lines { x: 0.0, y: -1.0 },
|
5 => ScrollDelta::Lines { x: 0.0, y: -1.0 },
|
||||||
6 => ScrollDelta::Lines { x: -1.0, y: 0.0 },
|
6 => ScrollDelta::Lines { x: -1.0, y: 0.0 },
|
||||||
7 => ScrollDelta::Lines { x: 1.0, y: 0.0 },
|
7 => ScrollDelta::Lines { x: 1.0, y: 0.0 },
|
||||||
_ => unreachable!(),
|
_ => unreachable!(),
|
||||||
},
|
},
|
||||||
modifiers: key_mods(event.state()),
|
modifiers: key_mods(event.state),
|
||||||
}),
|
}),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -650,24 +616,20 @@ impl WindowInner {
|
||||||
&mut crate::Window::new(Window { inner: self }),
|
&mut crate::Window::new(Window { inner: self }),
|
||||||
Event::Mouse(MouseEvent::ButtonPressed {
|
Event::Mouse(MouseEvent::ButtonPressed {
|
||||||
button: button_id,
|
button: button_id,
|
||||||
modifiers: key_mods(event.state()),
|
modifiers: key_mods(event.state),
|
||||||
}),
|
}),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
},
|
||||||
}
|
|
||||||
|
|
||||||
xcb::BUTTON_RELEASE => {
|
XEvent::ButtonRelease(event) => {
|
||||||
let event = unsafe { xcb::cast_event::<xcb::ButtonPressEvent>(&event) };
|
if !(4..=7).contains(&event.detail) {
|
||||||
let detail = event.detail();
|
let button_id = mouse_id(event.detail);
|
||||||
|
|
||||||
if !(4..=7).contains(&detail) {
|
|
||||||
let button_id = mouse_id(detail);
|
|
||||||
handler.on_event(
|
handler.on_event(
|
||||||
&mut crate::Window::new(Window { inner: self }),
|
&mut crate::Window::new(Window { inner: self }),
|
||||||
Event::Mouse(MouseEvent::ButtonReleased {
|
Event::Mouse(MouseEvent::ButtonReleased {
|
||||||
button: button_id,
|
button: button_id,
|
||||||
modifiers: key_mods(event.state()),
|
modifiers: key_mods(event.state),
|
||||||
}),
|
}),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -676,21 +638,17 @@ impl WindowInner {
|
||||||
////
|
////
|
||||||
// keys
|
// keys
|
||||||
////
|
////
|
||||||
xcb::KEY_PRESS => {
|
XEvent::KeyPress(event) => {
|
||||||
let event = unsafe { xcb::cast_event::<xcb::KeyPressEvent>(&event) };
|
|
||||||
|
|
||||||
handler.on_event(
|
handler.on_event(
|
||||||
&mut crate::Window::new(Window { inner: self }),
|
&mut crate::Window::new(Window { inner: self }),
|
||||||
Event::Keyboard(convert_key_press_event(event)),
|
Event::Keyboard(convert_key_press_event(&event)),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
xcb::KEY_RELEASE => {
|
XEvent::KeyRelease(event) => {
|
||||||
let event = unsafe { xcb::cast_event::<xcb::KeyReleaseEvent>(&event) };
|
|
||||||
|
|
||||||
handler.on_event(
|
handler.on_event(
|
||||||
&mut crate::Window::new(Window { inner: self }),
|
&mut crate::Window::new(Window { inner: self }),
|
||||||
Event::Keyboard(convert_key_release_event(event)),
|
Event::Keyboard(convert_key_release_event(&event)),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -712,7 +670,7 @@ unsafe impl<'a> HasRawWindowHandle for Window<'a> {
|
||||||
|
|
||||||
unsafe impl<'a> HasRawDisplayHandle for Window<'a> {
|
unsafe impl<'a> HasRawDisplayHandle for Window<'a> {
|
||||||
fn raw_display_handle(&self) -> RawDisplayHandle {
|
fn raw_display_handle(&self) -> RawDisplayHandle {
|
||||||
let display = self.inner.xcb_connection.conn.get_raw_dpy();
|
let display = self.inner.xcb_connection.dpy;
|
||||||
let mut handle = XlibDisplayHandle::empty();
|
let mut handle = XlibDisplayHandle::empty();
|
||||||
|
|
||||||
handle.display = display as *mut c_void;
|
handle.display = display as *mut c_void;
|
||||||
|
|
|
@ -1,58 +1,61 @@
|
||||||
use std::collections::HashMap;
|
use std::collections::hash_map::{Entry, HashMap};
|
||||||
/// A very light abstraction around the XCB connection.
|
use std::error::Error;
|
||||||
///
|
|
||||||
/// Keeps track of the xcb connection itself and the xlib display ID that was used to connect.
|
use x11::{xlib, xlib::Display, xlib_xcb};
|
||||||
use std::ffi::{CStr, CString};
|
|
||||||
|
use x11rb::connection::Connection;
|
||||||
|
use x11rb::cursor::Handle as CursorHandle;
|
||||||
|
use x11rb::protocol::xproto::Cursor;
|
||||||
|
use x11rb::resource_manager;
|
||||||
|
use x11rb::xcb_ffi::XCBConnection;
|
||||||
|
|
||||||
use crate::MouseCursor;
|
use crate::MouseCursor;
|
||||||
|
|
||||||
use super::cursor;
|
use super::cursor;
|
||||||
|
|
||||||
pub(crate) struct Atoms {
|
x11rb::atom_manager! {
|
||||||
pub wm_protocols: Option<u32>,
|
pub Atoms2: AtomsCookie {
|
||||||
pub wm_delete_window: Option<u32>,
|
WM_PROTOCOLS,
|
||||||
|
WM_DELETE_WINDOW,
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// A very light abstraction around the XCB connection.
|
||||||
|
///
|
||||||
|
/// Keeps track of the xcb connection itself and the xlib display ID that was used to connect.
|
||||||
pub struct XcbConnection {
|
pub struct XcbConnection {
|
||||||
pub conn: xcb::Connection,
|
pub(crate) dpy: *mut Display,
|
||||||
pub xlib_display: i32,
|
pub(crate) conn2: XCBConnection,
|
||||||
|
pub(crate) screen: usize,
|
||||||
pub(crate) atoms: Atoms,
|
pub(crate) atoms2: Atoms2,
|
||||||
|
pub(crate) resources: resource_manager::Database,
|
||||||
|
pub(crate) cursor_handle: CursorHandle,
|
||||||
pub(super) cursor_cache: HashMap<MouseCursor, u32>,
|
pub(super) cursor_cache: HashMap<MouseCursor, u32>,
|
||||||
}
|
}
|
||||||
|
|
||||||
macro_rules! intern_atoms {
|
|
||||||
($conn:expr, $( $name:ident ),+ ) => {{
|
|
||||||
$(
|
|
||||||
#[allow(non_snake_case)]
|
|
||||||
let $name = xcb::intern_atom($conn, true, stringify!($name));
|
|
||||||
)+
|
|
||||||
|
|
||||||
// splitting request and reply to improve throughput
|
|
||||||
|
|
||||||
(
|
|
||||||
$( $name.get_reply()
|
|
||||||
.map(|r| r.atom())
|
|
||||||
.ok()),+
|
|
||||||
)
|
|
||||||
}};
|
|
||||||
}
|
|
||||||
|
|
||||||
impl XcbConnection {
|
impl XcbConnection {
|
||||||
pub fn new() -> Result<Self, xcb::base::ConnError> {
|
pub fn new() -> Result<Self, Box<dyn Error>> {
|
||||||
let (conn, xlib_display) = xcb::Connection::connect_with_xlib_display()?;
|
let dpy = unsafe { xlib::XOpenDisplay(std::ptr::null()) };
|
||||||
|
assert!(!dpy.is_null());
|
||||||
|
let xcb_connection = unsafe { xlib_xcb::XGetXCBConnection(dpy) };
|
||||||
|
assert!(!xcb_connection.is_null());
|
||||||
|
let screen = unsafe { xlib::XDefaultScreen(dpy) } as usize;
|
||||||
|
let conn2 = unsafe { XCBConnection::from_raw_xcb_connection(xcb_connection, true)? };
|
||||||
|
unsafe {
|
||||||
|
xlib_xcb::XSetEventQueueOwner(dpy, xlib_xcb::XEventQueueOwner::XCBOwnsEventQueue)
|
||||||
|
};
|
||||||
|
|
||||||
conn.set_event_queue_owner(xcb::base::EventQueueOwner::Xcb);
|
let atoms2 = Atoms2::new(&conn2)?.reply()?;
|
||||||
|
let resources = resource_manager::new_from_default(&conn2)?;
|
||||||
let (wm_protocols, wm_delete_window) = intern_atoms!(&conn, WM_PROTOCOLS, WM_DELETE_WINDOW);
|
let cursor_handle = CursorHandle::new(&conn2, screen, &resources)?.reply()?;
|
||||||
|
|
||||||
Ok(Self {
|
Ok(Self {
|
||||||
conn,
|
dpy,
|
||||||
xlib_display,
|
conn2,
|
||||||
|
screen,
|
||||||
atoms: Atoms { wm_protocols, wm_delete_window },
|
atoms2,
|
||||||
|
resources,
|
||||||
|
cursor_handle,
|
||||||
cursor_cache: HashMap::new(),
|
cursor_cache: HashMap::new(),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
@ -60,58 +63,21 @@ impl XcbConnection {
|
||||||
// Try to get the scaling with this function first.
|
// Try to get the scaling with this function first.
|
||||||
// If this gives you `None`, fall back to `get_scaling_screen_dimensions`.
|
// If this gives you `None`, fall back to `get_scaling_screen_dimensions`.
|
||||||
// If neither work, I guess just assume 96.0 and don't do any scaling.
|
// If neither work, I guess just assume 96.0 and don't do any scaling.
|
||||||
fn get_scaling_xft(&self) -> Option<f64> {
|
fn get_scaling_xft(&self) -> Result<Option<f64>, Box<dyn Error>> {
|
||||||
use x11::xlib::{
|
if let Some(dpi) = self.resources.get_value::<u32>("Xft.dpi", "")? {
|
||||||
XResourceManagerString, XrmDestroyDatabase, XrmGetResource, XrmGetStringDatabase,
|
Ok(Some(dpi as f64 / 96.0))
|
||||||
XrmValue,
|
|
||||||
};
|
|
||||||
|
|
||||||
let display = self.conn.get_raw_dpy();
|
|
||||||
unsafe {
|
|
||||||
let rms = XResourceManagerString(display);
|
|
||||||
if !rms.is_null() {
|
|
||||||
let db = XrmGetStringDatabase(rms);
|
|
||||||
if !db.is_null() {
|
|
||||||
let mut value = XrmValue { size: 0, addr: std::ptr::null_mut() };
|
|
||||||
|
|
||||||
let mut value_type: *mut std::os::raw::c_char = std::ptr::null_mut();
|
|
||||||
let name_c_str = CString::new("Xft.dpi").unwrap();
|
|
||||||
let c_str = CString::new("Xft.Dpi").unwrap();
|
|
||||||
|
|
||||||
let dpi = if XrmGetResource(
|
|
||||||
db,
|
|
||||||
name_c_str.as_ptr(),
|
|
||||||
c_str.as_ptr(),
|
|
||||||
&mut value_type,
|
|
||||||
&mut value,
|
|
||||||
) != 0
|
|
||||||
&& !value.addr.is_null()
|
|
||||||
{
|
|
||||||
let value_addr: &CStr = CStr::from_ptr(value.addr);
|
|
||||||
value_addr.to_str().ok();
|
|
||||||
let value_str = value_addr.to_str().ok()?;
|
|
||||||
let value_f64: f64 = value_str.parse().ok()?;
|
|
||||||
let dpi_to_scale = value_f64 / 96.0;
|
|
||||||
Some(dpi_to_scale)
|
|
||||||
} else {
|
} else {
|
||||||
None
|
Ok(None)
|
||||||
};
|
|
||||||
XrmDestroyDatabase(db);
|
|
||||||
|
|
||||||
return dpi;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
None
|
|
||||||
}
|
|
||||||
|
|
||||||
// Try to get the scaling with `get_scaling_xft` first.
|
// Try to get the scaling with `get_scaling_xft` first.
|
||||||
// Only use this function as a fallback.
|
// Only use this function as a fallback.
|
||||||
// If neither work, I guess just assume 96.0 and don't do any scaling.
|
// If neither work, I guess just assume 96.0 and don't do any scaling.
|
||||||
fn get_scaling_screen_dimensions(&self) -> Option<f64> {
|
fn get_scaling_screen_dimensions(&self) -> f64 {
|
||||||
// Figure out screen information
|
// Figure out screen information
|
||||||
let setup = self.conn.get_setup();
|
let setup = self.conn2.setup();
|
||||||
let screen = setup.roots().nth(self.xlib_display as usize).unwrap();
|
let screen = &setup.roots[self.screen];
|
||||||
|
|
||||||
// Get the DPI from the screen struct
|
// Get the DPI from the screen struct
|
||||||
//
|
//
|
||||||
|
@ -119,28 +85,34 @@ impl XcbConnection {
|
||||||
// dpi = N pixels / (M millimeters / (25.4 millimeters / 1 inch))
|
// dpi = N pixels / (M millimeters / (25.4 millimeters / 1 inch))
|
||||||
// = N pixels / (M inch / 25.4)
|
// = N pixels / (M inch / 25.4)
|
||||||
// = N * 25.4 pixels / M inch
|
// = N * 25.4 pixels / M inch
|
||||||
let width_px = screen.width_in_pixels() as f64;
|
let width_px = screen.width_in_pixels as f64;
|
||||||
let width_mm = screen.width_in_millimeters() as f64;
|
let width_mm = screen.width_in_millimeters as f64;
|
||||||
let height_px = screen.height_in_pixels() as f64;
|
let height_px = screen.height_in_pixels as f64;
|
||||||
let height_mm = screen.height_in_millimeters() as f64;
|
let height_mm = screen.height_in_millimeters as f64;
|
||||||
let _xres = width_px * 25.4 / width_mm;
|
let _xres = width_px * 25.4 / width_mm;
|
||||||
let yres = height_px * 25.4 / height_mm;
|
let yres = height_px * 25.4 / height_mm;
|
||||||
|
|
||||||
let yscale = yres / 96.0;
|
let yscale = yres / 96.0;
|
||||||
|
|
||||||
// TODO: choose between `xres` and `yres`? (probably both are the same?)
|
// TODO: choose between `xres` and `yres`? (probably both are the same?)
|
||||||
Some(yscale)
|
yscale
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn get_scaling(&self) -> Option<f64> {
|
pub fn get_scaling(&self) -> Result<f64, Box<dyn Error>> {
|
||||||
self.get_scaling_xft().or_else(|| self.get_scaling_screen_dimensions())
|
Ok(self.get_scaling_xft()?.unwrap_or(self.get_scaling_screen_dimensions()))
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn get_cursor_xid(&mut self, cursor: MouseCursor) -> u32 {
|
pub fn get_cursor(&mut self, cursor: MouseCursor) -> Result<Cursor, Box<dyn Error>> {
|
||||||
let dpy = self.conn.get_raw_dpy();
|
match self.cursor_cache.entry(cursor) {
|
||||||
|
Entry::Occupied(entry) => Ok(*entry.get()),
|
||||||
*self.cursor_cache.entry(cursor).or_insert_with(|| cursor::get_xcursor(dpy, cursor))
|
Entry::Vacant(entry) => {
|
||||||
|
let cursor =
|
||||||
|
cursor::get_xcursor(&self.conn2, self.screen, &self.cursor_handle, cursor)?;
|
||||||
|
entry.insert(cursor);
|
||||||
|
Ok(cursor)
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue