gpui: Switch to x11rb
(#9113)
Switch to using `x11rb` crate instead of current `xcb` crate for gpui's x11 platform. Also fixes the crash on resize, and white flashing on resize. Release Notes: - N/A --------- Co-authored-by: Mikayla Maki <mikayla@zed.dev>
This commit is contained in:
parent
47afc70979
commit
80b80dfa78
6 changed files with 288 additions and 276 deletions
16
Cargo.lock
generated
16
Cargo.lock
generated
|
@ -4409,7 +4409,7 @@ dependencies = [
|
||||||
"wayland-cursor",
|
"wayland-cursor",
|
||||||
"wayland-protocols",
|
"wayland-protocols",
|
||||||
"windows 0.53.0",
|
"windows 0.53.0",
|
||||||
"xcb",
|
"x11rb",
|
||||||
"xkbcommon",
|
"xkbcommon",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
@ -12516,7 +12516,9 @@ version = "0.13.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "f8f25ead8c7e4cba123243a6367da5d3990e0d3affa708ea19dce96356bd9f1a"
|
checksum = "f8f25ead8c7e4cba123243a6367da5d3990e0d3affa708ea19dce96356bd9f1a"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
|
"as-raw-xcb-connection",
|
||||||
"gethostname",
|
"gethostname",
|
||||||
|
"libc",
|
||||||
"rustix 0.38.30",
|
"rustix 0.38.30",
|
||||||
"x11rb-protocol",
|
"x11rb-protocol",
|
||||||
]
|
]
|
||||||
|
@ -12536,18 +12538,6 @@ dependencies = [
|
||||||
"libc",
|
"libc",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "xcb"
|
|
||||||
version = "1.3.0"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "5d27b37e69b8c05bfadcd968eb1a4fe27c9c52565b727f88512f43b89567e262"
|
|
||||||
dependencies = [
|
|
||||||
"as-raw-xcb-connection",
|
|
||||||
"bitflags 1.3.2",
|
|
||||||
"libc",
|
|
||||||
"quick-xml 0.30.0",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "xcursor"
|
name = "xcursor"
|
||||||
version = "0.3.5"
|
version = "0.3.5"
|
||||||
|
|
|
@ -108,7 +108,7 @@ copypasta = "0.10.1"
|
||||||
[target.'cfg(target_os = "linux")'.dependencies]
|
[target.'cfg(target_os = "linux")'.dependencies]
|
||||||
open = "5.0.1"
|
open = "5.0.1"
|
||||||
ashpd = "0.7.0"
|
ashpd = "0.7.0"
|
||||||
xcb = { version = "1.3", features = ["as-raw-xcb-connection", "randr", "xkb"] }
|
x11rb = { version = "0.13.0", features = ["allow-unsafe-code", "xkb", "randr"] }
|
||||||
wayland-client = { version = "0.31.2" }
|
wayland-client = { version = "0.31.2" }
|
||||||
wayland-cursor = "0.31.1"
|
wayland-cursor = "0.31.1"
|
||||||
wayland-protocols = { version = "0.31.2", features = [
|
wayland-protocols = { version = "0.31.2", features = [
|
||||||
|
|
|
@ -2,18 +2,25 @@ use std::cell::RefCell;
|
||||||
use std::rc::Rc;
|
use std::rc::Rc;
|
||||||
use std::time::Duration;
|
use std::time::Duration;
|
||||||
|
|
||||||
use xcb::{x, Xid as _};
|
|
||||||
use xkbcommon::xkb;
|
|
||||||
|
|
||||||
use collections::HashMap;
|
use collections::HashMap;
|
||||||
use copypasta::x11_clipboard::{Clipboard, Primary, X11ClipboardContext};
|
use copypasta::x11_clipboard::{Clipboard, Primary, X11ClipboardContext};
|
||||||
use copypasta::ClipboardProvider;
|
use copypasta::ClipboardProvider;
|
||||||
|
|
||||||
|
use x11rb::connection::{Connection, RequestConnection};
|
||||||
|
use x11rb::errors::ConnectionError;
|
||||||
|
use x11rb::protocol::randr::ConnectionExt as _;
|
||||||
|
use x11rb::protocol::xkb::ConnectionExt as _;
|
||||||
|
use x11rb::protocol::xproto::ConnectionExt as _;
|
||||||
|
use x11rb::protocol::{randr, xkb, xproto, Event};
|
||||||
|
use x11rb::xcb_ffi::XCBConnection;
|
||||||
|
use xkbc::x11::ffi::{XKB_X11_MIN_MAJOR_XKB_VERSION, XKB_X11_MIN_MINOR_XKB_VERSION};
|
||||||
|
use xkbcommon::xkb as xkbc;
|
||||||
|
|
||||||
use crate::platform::linux::client::Client;
|
use crate::platform::linux::client::Client;
|
||||||
use crate::platform::{LinuxPlatformInner, PlatformWindow};
|
use crate::platform::{LinuxPlatformInner, PlatformWindow};
|
||||||
use crate::{
|
use crate::{
|
||||||
AnyWindowHandle, Bounds, CursorStyle, DisplayId, PlatformDisplay, PlatformInput, Point,
|
AnyWindowHandle, Bounds, CursorStyle, DisplayId, PlatformDisplay, PlatformInput, Point,
|
||||||
ScrollDelta, Size, TouchPhase,
|
ScrollDelta, Size, TouchPhase, WindowParams,
|
||||||
};
|
};
|
||||||
|
|
||||||
use super::{X11Display, X11Window, X11WindowState, XcbAtoms};
|
use super::{X11Display, X11Window, X11WindowState, XcbAtoms};
|
||||||
|
@ -28,55 +35,56 @@ struct WindowRef {
|
||||||
}
|
}
|
||||||
|
|
||||||
struct X11ClientState {
|
struct X11ClientState {
|
||||||
windows: HashMap<x::Window, WindowRef>,
|
windows: HashMap<xproto::Window, WindowRef>,
|
||||||
xkb: xkbcommon::xkb::State,
|
xkb: xkbc::State,
|
||||||
clipboard: Rc<RefCell<X11ClipboardContext<Clipboard>>>,
|
clipboard: Rc<RefCell<X11ClipboardContext<Clipboard>>>,
|
||||||
primary: Rc<RefCell<X11ClipboardContext<Primary>>>,
|
primary: Rc<RefCell<X11ClipboardContext<Primary>>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) struct X11Client {
|
pub(crate) struct X11Client {
|
||||||
platform_inner: Rc<LinuxPlatformInner>,
|
platform_inner: Rc<LinuxPlatformInner>,
|
||||||
xcb_connection: Rc<xcb::Connection>,
|
xcb_connection: Rc<XCBConnection>,
|
||||||
x_root_index: i32,
|
x_root_index: usize,
|
||||||
atoms: XcbAtoms,
|
atoms: XcbAtoms,
|
||||||
state: RefCell<X11ClientState>,
|
state: RefCell<X11ClientState>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl X11Client {
|
impl X11Client {
|
||||||
pub(crate) fn new(inner: Rc<LinuxPlatformInner>) -> Rc<Self> {
|
pub(crate) fn new(inner: Rc<LinuxPlatformInner>) -> Rc<Self> {
|
||||||
let (xcb_connection, x_root_index) = xcb::Connection::connect_with_extensions(
|
let (xcb_connection, x_root_index) = XCBConnection::connect(None).unwrap();
|
||||||
None,
|
xcb_connection
|
||||||
&[xcb::Extension::RandR, xcb::Extension::Xkb],
|
.prefetch_extension_information(xkb::X11_EXTENSION_NAME)
|
||||||
&[],
|
.unwrap();
|
||||||
)
|
xcb_connection
|
||||||
.unwrap();
|
.prefetch_extension_information(randr::X11_EXTENSION_NAME)
|
||||||
|
|
||||||
let xkb_ver = xcb_connection
|
|
||||||
.wait_for_reply(xcb_connection.send_request(&xcb::xkb::UseExtension {
|
|
||||||
wanted_major: xcb::xkb::MAJOR_VERSION as u16,
|
|
||||||
wanted_minor: xcb::xkb::MINOR_VERSION as u16,
|
|
||||||
}))
|
|
||||||
.unwrap();
|
.unwrap();
|
||||||
assert!(xkb_ver.supported());
|
|
||||||
|
|
||||||
let atoms = XcbAtoms::intern_all(&xcb_connection).unwrap();
|
let atoms = XcbAtoms::new(&xcb_connection).unwrap();
|
||||||
let xcb_connection = Rc::new(xcb_connection);
|
let xkb = xcb_connection
|
||||||
|
.xkb_use_extension(XKB_X11_MIN_MAJOR_XKB_VERSION, XKB_X11_MIN_MINOR_XKB_VERSION)
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
let atoms = atoms.reply().unwrap();
|
||||||
|
let xkb = xkb.reply().unwrap();
|
||||||
|
assert!(xkb.supported);
|
||||||
|
|
||||||
let xkb_state = {
|
let xkb_state = {
|
||||||
let xkb_context = xkb::Context::new(xkb::CONTEXT_NO_FLAGS);
|
let xkb_context = xkbc::Context::new(xkbc::CONTEXT_NO_FLAGS);
|
||||||
let xkb_device_id = xkb::x11::get_core_keyboard_device_id(&xcb_connection);
|
let xkb_device_id = xkbc::x11::get_core_keyboard_device_id(&xcb_connection);
|
||||||
let xkb_keymap = xkb::x11::keymap_new_from_device(
|
let xkb_keymap = xkbc::x11::keymap_new_from_device(
|
||||||
&xkb_context,
|
&xkb_context,
|
||||||
&xcb_connection,
|
&xcb_connection,
|
||||||
xkb_device_id,
|
xkb_device_id,
|
||||||
xkb::KEYMAP_COMPILE_NO_FLAGS,
|
xkbc::KEYMAP_COMPILE_NO_FLAGS,
|
||||||
);
|
);
|
||||||
xkb::x11::state_new_from_device(&xkb_keymap, &xcb_connection, xkb_device_id)
|
xkbc::x11::state_new_from_device(&xkb_keymap, &xcb_connection, xkb_device_id)
|
||||||
};
|
};
|
||||||
|
|
||||||
let clipboard = X11ClipboardContext::<Clipboard>::new().unwrap();
|
let clipboard = X11ClipboardContext::<Clipboard>::new().unwrap();
|
||||||
let primary = X11ClipboardContext::<Primary>::new().unwrap();
|
let primary = X11ClipboardContext::<Primary>::new().unwrap();
|
||||||
|
|
||||||
|
let xcb_connection = Rc::new(xcb_connection);
|
||||||
|
|
||||||
let client: Rc<X11Client> = Rc::new(Self {
|
let client: Rc<X11Client> = Rc::new(Self {
|
||||||
platform_inner: inner.clone(),
|
platform_inner: inner.clone(),
|
||||||
xcb_connection: Rc::clone(&xcb_connection),
|
xcb_connection: Rc::clone(&xcb_connection),
|
||||||
|
@ -96,7 +104,7 @@ impl X11Client {
|
||||||
inner
|
inner
|
||||||
.loop_handle
|
.loop_handle
|
||||||
.insert_source(
|
.insert_source(
|
||||||
Generic::new_with_error::<xcb::Error>(
|
Generic::new_with_error::<ConnectionError>(
|
||||||
fd,
|
fd,
|
||||||
calloop::Interest::READ,
|
calloop::Interest::READ,
|
||||||
calloop::Mode::Level,
|
calloop::Mode::Level,
|
||||||
|
@ -116,69 +124,68 @@ impl X11Client {
|
||||||
client
|
client
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_window(&self, win: x::Window) -> Option<Rc<X11WindowState>> {
|
fn get_window(&self, win: xproto::Window) -> Option<Rc<X11WindowState>> {
|
||||||
let state = self.state.borrow();
|
let state = self.state.borrow();
|
||||||
state.windows.get(&win).map(|wr| Rc::clone(&wr.state))
|
state.windows.get(&win).map(|wr| Rc::clone(&wr.state))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn handle_event(&self, event: xcb::Event) -> Option<()> {
|
fn handle_event(&self, event: Event) -> Option<()> {
|
||||||
match event {
|
match event {
|
||||||
xcb::Event::X(x::Event::ClientMessage(event)) => {
|
Event::ClientMessage(event) => {
|
||||||
if let x::ClientMessageData::Data32([atom, ..]) = event.data() {
|
let [atom, ..] = event.data.as_data32();
|
||||||
if atom == self.atoms.wm_del_window.resource_id() {
|
if atom == self.atoms.WM_DELETE_WINDOW {
|
||||||
// window "x" button clicked by user, we gracefully exit
|
// window "x" button clicked by user, we gracefully exit
|
||||||
let window_ref = self
|
let window_ref = self
|
||||||
.state
|
.state
|
||||||
.borrow_mut()
|
.borrow_mut()
|
||||||
.windows
|
.windows
|
||||||
.remove(&event.window())
|
.remove(&event.window)
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
self.platform_inner
|
self.platform_inner
|
||||||
.loop_handle
|
.loop_handle
|
||||||
.remove(window_ref.refresh_event_token);
|
.remove(window_ref.refresh_event_token);
|
||||||
window_ref.state.destroy();
|
window_ref.state.destroy();
|
||||||
|
|
||||||
if self.state.borrow().windows.is_empty() {
|
if self.state.borrow().windows.is_empty() {
|
||||||
self.platform_inner.loop_signal.stop();
|
self.platform_inner.loop_signal.stop();
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
xcb::Event::X(x::Event::ConfigureNotify(event)) => {
|
Event::ConfigureNotify(event) => {
|
||||||
let bounds = Bounds {
|
let bounds = Bounds {
|
||||||
origin: Point {
|
origin: Point {
|
||||||
x: event.x().into(),
|
x: event.x.into(),
|
||||||
y: event.y().into(),
|
y: event.y.into(),
|
||||||
},
|
},
|
||||||
size: Size {
|
size: Size {
|
||||||
width: event.width().into(),
|
width: event.width.into(),
|
||||||
height: event.height().into(),
|
height: event.height.into(),
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
let window = self.get_window(event.window())?;
|
let window = self.get_window(event.window)?;
|
||||||
window.configure(bounds);
|
window.configure(bounds);
|
||||||
}
|
}
|
||||||
xcb::Event::X(x::Event::Expose(event)) => {
|
Event::Expose(event) => {
|
||||||
let window = self.get_window(event.window())?;
|
let window = self.get_window(event.window)?;
|
||||||
window.refresh();
|
window.refresh();
|
||||||
}
|
}
|
||||||
xcb::Event::X(x::Event::FocusIn(event)) => {
|
Event::FocusIn(event) => {
|
||||||
let window = self.get_window(event.event())?;
|
let window = self.get_window(event.event)?;
|
||||||
window.set_focused(true);
|
window.set_focused(true);
|
||||||
}
|
}
|
||||||
xcb::Event::X(x::Event::FocusOut(event)) => {
|
Event::FocusOut(event) => {
|
||||||
let window = self.get_window(event.event())?;
|
let window = self.get_window(event.event)?;
|
||||||
window.set_focused(false);
|
window.set_focused(false);
|
||||||
}
|
}
|
||||||
xcb::Event::X(x::Event::KeyPress(event)) => {
|
Event::KeyPress(event) => {
|
||||||
let window = self.get_window(event.event())?;
|
let window = self.get_window(event.event)?;
|
||||||
let modifiers = super::modifiers_from_state(event.state());
|
let modifiers = super::modifiers_from_state(event.state);
|
||||||
let keystroke = {
|
let keystroke = {
|
||||||
let code = event.detail().into();
|
let code = event.detail.into();
|
||||||
let mut state = self.state.borrow_mut();
|
let mut state = self.state.borrow_mut();
|
||||||
let keystroke = crate::Keystroke::from_xkb(&state.xkb, modifiers, code);
|
let keystroke = crate::Keystroke::from_xkb(&state.xkb, modifiers, code);
|
||||||
state.xkb.update_key(code, xkb::KeyDirection::Down);
|
state.xkb.update_key(code, xkbc::KeyDirection::Down);
|
||||||
keystroke
|
keystroke
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -187,36 +194,34 @@ impl X11Client {
|
||||||
is_held: false,
|
is_held: false,
|
||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
xcb::Event::X(x::Event::KeyRelease(event)) => {
|
Event::KeyRelease(event) => {
|
||||||
let window = self.get_window(event.event())?;
|
let window = self.get_window(event.event)?;
|
||||||
let modifiers = super::modifiers_from_state(event.state());
|
let modifiers = super::modifiers_from_state(event.state);
|
||||||
let keystroke = {
|
let keystroke = {
|
||||||
let code = event.detail().into();
|
let code = event.detail.into();
|
||||||
let mut state = self.state.borrow_mut();
|
let mut state = self.state.borrow_mut();
|
||||||
let keystroke = crate::Keystroke::from_xkb(&state.xkb, modifiers, code);
|
let keystroke = crate::Keystroke::from_xkb(&state.xkb, modifiers, code);
|
||||||
state.xkb.update_key(code, xkb::KeyDirection::Up);
|
state.xkb.update_key(code, xkbc::KeyDirection::Up);
|
||||||
keystroke
|
keystroke
|
||||||
};
|
};
|
||||||
|
|
||||||
window.handle_input(PlatformInput::KeyUp(crate::KeyUpEvent { keystroke }));
|
window.handle_input(PlatformInput::KeyUp(crate::KeyUpEvent { keystroke }));
|
||||||
}
|
}
|
||||||
xcb::Event::X(x::Event::ButtonPress(event)) => {
|
Event::ButtonPress(event) => {
|
||||||
let window = self.get_window(event.event())?;
|
let window = self.get_window(event.event)?;
|
||||||
let modifiers = super::modifiers_from_state(event.state());
|
let modifiers = super::modifiers_from_state(event.state);
|
||||||
let position = Point::new(
|
let position =
|
||||||
(event.event_x() as f32).into(),
|
Point::new((event.event_x as f32).into(), (event.event_y as f32).into());
|
||||||
(event.event_y() as f32).into(),
|
if let Some(button) = super::button_of_key(event.detail) {
|
||||||
);
|
|
||||||
if let Some(button) = super::button_of_key(event.detail()) {
|
|
||||||
window.handle_input(PlatformInput::MouseDown(crate::MouseDownEvent {
|
window.handle_input(PlatformInput::MouseDown(crate::MouseDownEvent {
|
||||||
button,
|
button,
|
||||||
position,
|
position,
|
||||||
modifiers,
|
modifiers,
|
||||||
click_count: 1,
|
click_count: 1,
|
||||||
}));
|
}));
|
||||||
} else if event.detail() >= 4 && event.detail() <= 5 {
|
} else if event.detail >= 4 && event.detail <= 5 {
|
||||||
// https://stackoverflow.com/questions/15510472/scrollwheel-event-in-x11
|
// https://stackoverflow.com/questions/15510472/scrollwheel-event-in-x11
|
||||||
let delta_x = if event.detail() == 4 { 1.0 } else { -1.0 };
|
let delta_x = if event.detail == 4 { 1.0 } else { -1.0 };
|
||||||
window.handle_input(PlatformInput::ScrollWheel(crate::ScrollWheelEvent {
|
window.handle_input(PlatformInput::ScrollWheel(crate::ScrollWheelEvent {
|
||||||
position,
|
position,
|
||||||
delta: ScrollDelta::Lines(Point::new(0.0, delta_x)),
|
delta: ScrollDelta::Lines(Point::new(0.0, delta_x)),
|
||||||
|
@ -227,14 +232,12 @@ impl X11Client {
|
||||||
log::warn!("Unknown button press: {event:?}");
|
log::warn!("Unknown button press: {event:?}");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
xcb::Event::X(x::Event::ButtonRelease(event)) => {
|
Event::ButtonRelease(event) => {
|
||||||
let window = self.get_window(event.event())?;
|
let window = self.get_window(event.event)?;
|
||||||
let modifiers = super::modifiers_from_state(event.state());
|
let modifiers = super::modifiers_from_state(event.state);
|
||||||
let position = Point::new(
|
let position =
|
||||||
(event.event_x() as f32).into(),
|
Point::new((event.event_x as f32).into(), (event.event_y as f32).into());
|
||||||
(event.event_y() as f32).into(),
|
if let Some(button) = super::button_of_key(event.detail) {
|
||||||
);
|
|
||||||
if let Some(button) = super::button_of_key(event.detail()) {
|
|
||||||
window.handle_input(PlatformInput::MouseUp(crate::MouseUpEvent {
|
window.handle_input(PlatformInput::MouseUp(crate::MouseUpEvent {
|
||||||
button,
|
button,
|
||||||
position,
|
position,
|
||||||
|
@ -243,28 +246,24 @@ impl X11Client {
|
||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
xcb::Event::X(x::Event::MotionNotify(event)) => {
|
Event::MotionNotify(event) => {
|
||||||
let window = self.get_window(event.event())?;
|
let window = self.get_window(event.event)?;
|
||||||
let pressed_button = super::button_from_state(event.state());
|
let pressed_button = super::button_from_state(event.state);
|
||||||
let position = Point::new(
|
let position =
|
||||||
(event.event_x() as f32).into(),
|
Point::new((event.event_x as f32).into(), (event.event_y as f32).into());
|
||||||
(event.event_y() as f32).into(),
|
let modifiers = super::modifiers_from_state(event.state);
|
||||||
);
|
|
||||||
let modifiers = super::modifiers_from_state(event.state());
|
|
||||||
window.handle_input(PlatformInput::MouseMove(crate::MouseMoveEvent {
|
window.handle_input(PlatformInput::MouseMove(crate::MouseMoveEvent {
|
||||||
pressed_button,
|
pressed_button,
|
||||||
position,
|
position,
|
||||||
modifiers,
|
modifiers,
|
||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
xcb::Event::X(x::Event::LeaveNotify(event)) => {
|
Event::LeaveNotify(event) => {
|
||||||
let window = self.get_window(event.event())?;
|
let window = self.get_window(event.event)?;
|
||||||
let pressed_button = super::button_from_state(event.state());
|
let pressed_button = super::button_from_state(event.state);
|
||||||
let position = Point::new(
|
let position =
|
||||||
(event.event_x() as f32).into(),
|
Point::new((event.event_x as f32).into(), (event.event_y as f32).into());
|
||||||
(event.event_y() as f32).into(),
|
let modifiers = super::modifiers_from_state(event.state);
|
||||||
);
|
|
||||||
let modifiers = super::modifiers_from_state(event.state());
|
|
||||||
window.handle_input(PlatformInput::MouseExited(crate::MouseExitEvent {
|
window.handle_input(PlatformInput::MouseExited(crate::MouseExitEvent {
|
||||||
pressed_button,
|
pressed_button,
|
||||||
position,
|
position,
|
||||||
|
@ -280,21 +279,23 @@ impl X11Client {
|
||||||
|
|
||||||
impl Client for X11Client {
|
impl Client for X11Client {
|
||||||
fn displays(&self) -> Vec<Rc<dyn PlatformDisplay>> {
|
fn displays(&self) -> Vec<Rc<dyn PlatformDisplay>> {
|
||||||
let setup = self.xcb_connection.get_setup();
|
let setup = self.xcb_connection.setup();
|
||||||
setup
|
setup
|
||||||
.roots()
|
.roots
|
||||||
|
.iter()
|
||||||
.enumerate()
|
.enumerate()
|
||||||
.filter_map(|(root_id, _)| {
|
.filter_map(|(root_id, _)| {
|
||||||
Some(
|
Some(Rc::new(X11Display::new(&self.xcb_connection, root_id)?)
|
||||||
Rc::new(X11Display::new(&self.xcb_connection, root_id as i32)?)
|
as Rc<dyn PlatformDisplay>)
|
||||||
as Rc<dyn PlatformDisplay>,
|
|
||||||
)
|
|
||||||
})
|
})
|
||||||
.collect()
|
.collect()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn display(&self, id: DisplayId) -> Option<Rc<dyn PlatformDisplay>> {
|
fn display(&self, id: DisplayId) -> Option<Rc<dyn PlatformDisplay>> {
|
||||||
Some(Rc::new(X11Display::new(&self.xcb_connection, id.0 as i32)?))
|
Some(Rc::new(X11Display::new(
|
||||||
|
&self.xcb_connection,
|
||||||
|
id.0 as usize,
|
||||||
|
)?))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn primary_display(&self) -> Option<Rc<dyn PlatformDisplay>> {
|
fn primary_display(&self) -> Option<Rc<dyn PlatformDisplay>> {
|
||||||
|
@ -307,36 +308,44 @@ impl Client for X11Client {
|
||||||
fn open_window(
|
fn open_window(
|
||||||
&self,
|
&self,
|
||||||
_handle: AnyWindowHandle,
|
_handle: AnyWindowHandle,
|
||||||
params: crate::WindowParams,
|
options: WindowParams,
|
||||||
) -> Box<dyn PlatformWindow> {
|
) -> Box<dyn PlatformWindow> {
|
||||||
let x_window = self.xcb_connection.generate_id();
|
let x_window = self.xcb_connection.generate_id().unwrap();
|
||||||
|
|
||||||
let window_ptr = Rc::new(X11WindowState::new(
|
let window_ptr = Rc::new(X11WindowState::new(
|
||||||
params,
|
options,
|
||||||
&self.xcb_connection,
|
&self.xcb_connection,
|
||||||
self.x_root_index,
|
self.x_root_index,
|
||||||
x_window,
|
x_window,
|
||||||
&self.atoms,
|
&self.atoms,
|
||||||
));
|
));
|
||||||
|
|
||||||
let cookie = self
|
let screen_resources = self
|
||||||
.xcb_connection
|
.xcb_connection
|
||||||
.send_request(&xcb::randr::GetScreenResourcesCurrent { window: x_window });
|
.randr_get_screen_resources(x_window)
|
||||||
let screen_resources = self.xcb_connection.wait_for_reply(cookie).expect("TODO");
|
.unwrap()
|
||||||
|
.reply()
|
||||||
|
.expect("TODO");
|
||||||
|
|
||||||
let mode = screen_resources
|
let mode = screen_resources
|
||||||
.crtcs()
|
.crtcs
|
||||||
.iter()
|
.iter()
|
||||||
.find_map(|crtc| {
|
.find_map(|crtc| {
|
||||||
let cookie = self.xcb_connection.send_request(&xcb::randr::GetCrtcInfo {
|
let crtc_info = self
|
||||||
crtc: crtc.to_owned(),
|
.xcb_connection
|
||||||
config_timestamp: xcb::x::Time::CurrentTime as u32,
|
.randr_get_crtc_info(*crtc, x11rb::CURRENT_TIME)
|
||||||
});
|
.ok()?
|
||||||
let crtc_info = self.xcb_connection.wait_for_reply(cookie).expect("TODO");
|
.reply()
|
||||||
|
.ok()?;
|
||||||
|
|
||||||
let mode_id = crtc_info.mode().resource_id();
|
screen_resources
|
||||||
screen_resources.modes().iter().find(|m| m.id == mode_id)
|
.modes
|
||||||
|
.iter()
|
||||||
|
.find(|m| m.id == crtc_info.mode)
|
||||||
})
|
})
|
||||||
.expect("Missing screen mode for crtc specified mode id");
|
.expect("Unable to find screen refresh rate");
|
||||||
|
|
||||||
|
// .expect("Missing screen mode for crtc specified mode id");
|
||||||
|
|
||||||
let refresh_event_token = self
|
let refresh_event_token = self
|
||||||
.platform_inner
|
.platform_inner
|
||||||
|
@ -345,13 +354,24 @@ impl Client for X11Client {
|
||||||
let refresh_duration = mode_refresh_rate(mode);
|
let refresh_duration = mode_refresh_rate(mode);
|
||||||
let xcb_connection = Rc::clone(&self.xcb_connection);
|
let xcb_connection = Rc::clone(&self.xcb_connection);
|
||||||
move |mut instant, (), _| {
|
move |mut instant, (), _| {
|
||||||
xcb_connection.send_request(&x::SendEvent {
|
xcb_connection
|
||||||
propagate: false,
|
.send_event(
|
||||||
destination: x::SendEventDest::Window(x_window),
|
false,
|
||||||
event_mask: x::EventMask::EXPOSURE,
|
x_window,
|
||||||
event: &x::ExposeEvent::new(x_window, 0, 0, 0, 0, 1),
|
xproto::EventMask::EXPOSURE,
|
||||||
});
|
xproto::ExposeEvent {
|
||||||
let _ = xcb_connection.flush();
|
response_type: xproto::EXPOSE_EVENT,
|
||||||
|
sequence: 0,
|
||||||
|
window: x_window,
|
||||||
|
x: 0,
|
||||||
|
y: 0,
|
||||||
|
width: 0,
|
||||||
|
height: 0,
|
||||||
|
count: 1,
|
||||||
|
},
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
let _ = xcb_connection.flush().unwrap();
|
||||||
// Take into account that some frames have been skipped
|
// Take into account that some frames have been skipped
|
||||||
let now = time::Instant::now();
|
let now = time::Instant::now();
|
||||||
while instant < now {
|
while instant < now {
|
||||||
|
@ -384,7 +404,7 @@ impl Client for X11Client {
|
||||||
|
|
||||||
// Adatpted from:
|
// Adatpted from:
|
||||||
// https://docs.rs/winit/0.29.11/src/winit/platform_impl/linux/x11/monitor.rs.html#103-111
|
// https://docs.rs/winit/0.29.11/src/winit/platform_impl/linux/x11/monitor.rs.html#103-111
|
||||||
pub fn mode_refresh_rate(mode: &xcb::randr::ModeInfo) -> Duration {
|
pub fn mode_refresh_rate(mode: &randr::ModeInfo) -> Duration {
|
||||||
let millihertz = mode.dot_clock as u64 * 1_000 / (mode.htotal as u64 * mode.vtotal as u64);
|
let millihertz = mode.dot_clock as u64 * 1_000 / (mode.htotal as u64 * mode.vtotal as u64);
|
||||||
let micros = 1_000_000_000 / millihertz;
|
let micros = 1_000_000_000 / millihertz;
|
||||||
log::info!("Refreshing at {} micros", micros);
|
log::info!("Refreshing at {} micros", micros);
|
||||||
|
|
|
@ -1,25 +1,26 @@
|
||||||
use anyhow::Result;
|
use anyhow::Result;
|
||||||
use uuid::Uuid;
|
use uuid::Uuid;
|
||||||
|
use x11rb::{connection::Connection as _, xcb_ffi::XCBConnection};
|
||||||
|
|
||||||
use crate::{Bounds, DisplayId, GlobalPixels, PlatformDisplay, Size};
|
use crate::{Bounds, DisplayId, GlobalPixels, PlatformDisplay, Size};
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub(crate) struct X11Display {
|
pub(crate) struct X11Display {
|
||||||
x_screen_index: i32,
|
x_screen_index: usize,
|
||||||
bounds: Bounds<GlobalPixels>,
|
bounds: Bounds<GlobalPixels>,
|
||||||
uuid: Uuid,
|
uuid: Uuid,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl X11Display {
|
impl X11Display {
|
||||||
pub(crate) fn new(xc: &xcb::Connection, x_screen_index: i32) -> Option<Self> {
|
pub(crate) fn new(xc: &XCBConnection, x_screen_index: usize) -> Option<Self> {
|
||||||
let screen = xc.get_setup().roots().nth(x_screen_index as usize)?;
|
let screen = xc.setup().roots.get(x_screen_index).unwrap();
|
||||||
Some(Self {
|
Some(Self {
|
||||||
x_screen_index,
|
x_screen_index: x_screen_index,
|
||||||
bounds: Bounds {
|
bounds: Bounds {
|
||||||
origin: Default::default(),
|
origin: Default::default(),
|
||||||
size: Size {
|
size: Size {
|
||||||
width: GlobalPixels(screen.width_in_pixels() as f32),
|
width: GlobalPixels(screen.width_in_pixels as f32),
|
||||||
height: GlobalPixels(screen.height_in_pixels() as f32),
|
height: GlobalPixels(screen.height_in_pixels as f32),
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
uuid: Uuid::from_bytes([0; 16]),
|
uuid: Uuid::from_bytes([0; 16]),
|
||||||
|
|
|
@ -1,8 +1,8 @@
|
||||||
use xcb::x;
|
use x11rb::protocol::xproto;
|
||||||
|
|
||||||
use crate::{Modifiers, MouseButton, NavigationDirection};
|
use crate::{Modifiers, MouseButton, NavigationDirection};
|
||||||
|
|
||||||
pub(crate) fn button_of_key(detail: x::Button) -> Option<MouseButton> {
|
pub(crate) fn button_of_key(detail: xproto::Button) -> Option<MouseButton> {
|
||||||
Some(match detail {
|
Some(match detail {
|
||||||
1 => MouseButton::Left,
|
1 => MouseButton::Left,
|
||||||
2 => MouseButton::Middle,
|
2 => MouseButton::Middle,
|
||||||
|
@ -13,22 +13,22 @@ pub(crate) fn button_of_key(detail: x::Button) -> Option<MouseButton> {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn modifiers_from_state(state: x::KeyButMask) -> Modifiers {
|
pub(crate) fn modifiers_from_state(state: xproto::KeyButMask) -> Modifiers {
|
||||||
Modifiers {
|
Modifiers {
|
||||||
control: state.contains(x::KeyButMask::CONTROL),
|
control: state.contains(xproto::KeyButMask::CONTROL),
|
||||||
alt: state.contains(x::KeyButMask::MOD1),
|
alt: state.contains(xproto::KeyButMask::MOD1),
|
||||||
shift: state.contains(x::KeyButMask::SHIFT),
|
shift: state.contains(xproto::KeyButMask::SHIFT),
|
||||||
command: state.contains(x::KeyButMask::MOD4),
|
command: state.contains(xproto::KeyButMask::MOD4),
|
||||||
function: false,
|
function: false,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn button_from_state(state: x::KeyButMask) -> Option<MouseButton> {
|
pub(crate) fn button_from_state(state: xproto::KeyButMask) -> Option<MouseButton> {
|
||||||
Some(if state.contains(x::KeyButMask::BUTTON1) {
|
Some(if state.contains(xproto::KeyButMask::BUTTON1) {
|
||||||
MouseButton::Left
|
MouseButton::Left
|
||||||
} else if state.contains(x::KeyButMask::BUTTON2) {
|
} else if state.contains(xproto::KeyButMask::BUTTON2) {
|
||||||
MouseButton::Middle
|
MouseButton::Middle
|
||||||
} else if state.contains(x::KeyButMask::BUTTON3) {
|
} else if state.contains(xproto::KeyButMask::BUTTON3) {
|
||||||
MouseButton::Right
|
MouseButton::Right
|
||||||
} else {
|
} else {
|
||||||
return None;
|
return None;
|
||||||
|
|
|
@ -9,10 +9,11 @@ use crate::{
|
||||||
use blade_graphics as gpu;
|
use blade_graphics as gpu;
|
||||||
use parking_lot::Mutex;
|
use parking_lot::Mutex;
|
||||||
use raw_window_handle as rwh;
|
use raw_window_handle as rwh;
|
||||||
|
use x11rb::{
|
||||||
use xcb::{
|
connection::Connection,
|
||||||
x::{self, StackMode},
|
protocol::xproto::{self, ConnectionExt as _, CreateWindowAux},
|
||||||
Xid as _,
|
wrapper::ConnectionExt,
|
||||||
|
xcb_ffi::XCBConnection,
|
||||||
};
|
};
|
||||||
|
|
||||||
use std::{
|
use std::{
|
||||||
|
@ -40,14 +41,13 @@ struct Callbacks {
|
||||||
appearance_changed: Option<Box<dyn FnMut()>>,
|
appearance_changed: Option<Box<dyn FnMut()>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
xcb::atoms_struct! {
|
x11rb::atom_manager! {
|
||||||
#[derive(Debug)]
|
pub XcbAtoms: AtomsCookie {
|
||||||
pub(crate) struct XcbAtoms {
|
WM_PROTOCOLS,
|
||||||
pub wm_protocols => b"WM_PROTOCOLS",
|
WM_DELETE_WINDOW,
|
||||||
pub wm_del_window => b"WM_DELETE_WINDOW",
|
_NET_WM_STATE,
|
||||||
wm_state => b"_NET_WM_STATE",
|
_NET_WM_STATE_MAXIMIZED_VERT,
|
||||||
wm_state_maxv => b"_NET_WM_STATE_MAXIMIZED_VERT",
|
_NET_WM_STATE_MAXIMIZED_HORZ,
|
||||||
wm_state_maxh => b"_NET_WM_STATE_MAXIMIZED_HORZ",
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -68,30 +68,31 @@ impl LinuxWindowInner {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn query_render_extent(xcb_connection: &xcb::Connection, x_window: x::Window) -> gpu::Extent {
|
fn query_render_extent(xcb_connection: &XCBConnection, x_window: xproto::Window) -> gpu::Extent {
|
||||||
let cookie = xcb_connection.send_request(&x::GetGeometry {
|
let reply = xcb_connection
|
||||||
drawable: x::Drawable::Window(x_window),
|
.get_geometry(x_window)
|
||||||
});
|
.unwrap()
|
||||||
let reply = xcb_connection.wait_for_reply(cookie).unwrap();
|
.reply()
|
||||||
|
.unwrap();
|
||||||
gpu::Extent {
|
gpu::Extent {
|
||||||
width: reply.width() as u32,
|
width: reply.width as u32,
|
||||||
height: reply.height() as u32,
|
height: reply.height as u32,
|
||||||
depth: 1,
|
depth: 1,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
struct RawWindow {
|
struct RawWindow {
|
||||||
connection: *mut c_void,
|
connection: *mut c_void,
|
||||||
screen_id: i32,
|
screen_id: usize,
|
||||||
window_id: u32,
|
window_id: u32,
|
||||||
visual_id: u32,
|
visual_id: u32,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) struct X11WindowState {
|
pub(crate) struct X11WindowState {
|
||||||
xcb_connection: Rc<xcb::Connection>,
|
xcb_connection: Rc<XCBConnection>,
|
||||||
display: Rc<dyn PlatformDisplay>,
|
display: Rc<dyn PlatformDisplay>,
|
||||||
raw: RawWindow,
|
raw: RawWindow,
|
||||||
x_window: x::Window,
|
x_window: xproto::Window,
|
||||||
callbacks: RefCell<Callbacks>,
|
callbacks: RefCell<Callbacks>,
|
||||||
inner: RefCell<LinuxWindowInner>,
|
inner: RefCell<LinuxWindowInner>,
|
||||||
}
|
}
|
||||||
|
@ -112,7 +113,7 @@ unsafe impl blade_rwh::HasRawDisplayHandle for RawWindow {
|
||||||
fn raw_display_handle(&self) -> blade_rwh::RawDisplayHandle {
|
fn raw_display_handle(&self) -> blade_rwh::RawDisplayHandle {
|
||||||
let mut dh = blade_rwh::XcbDisplayHandle::empty();
|
let mut dh = blade_rwh::XcbDisplayHandle::empty();
|
||||||
dh.connection = self.connection;
|
dh.connection = self.connection;
|
||||||
dh.screen = self.screen_id;
|
dh.screen = self.screen_id as i32;
|
||||||
dh.into()
|
dh.into()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -130,7 +131,7 @@ impl rwh::HasDisplayHandle for X11Window {
|
||||||
fn display_handle(&self) -> Result<rwh::DisplayHandle, rwh::HandleError> {
|
fn display_handle(&self) -> Result<rwh::DisplayHandle, rwh::HandleError> {
|
||||||
Ok(unsafe {
|
Ok(unsafe {
|
||||||
let non_zero = NonNull::new(self.0.raw.connection).unwrap();
|
let non_zero = NonNull::new(self.0.raw.connection).unwrap();
|
||||||
let handle = rwh::XcbDisplayHandle::new(Some(non_zero), self.0.raw.screen_id);
|
let handle = rwh::XcbDisplayHandle::new(Some(non_zero), self.0.raw.screen_id as i32);
|
||||||
rwh::DisplayHandle::borrow_raw(handle.into())
|
rwh::DisplayHandle::borrow_raw(handle.into())
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
@ -139,76 +140,76 @@ impl rwh::HasDisplayHandle for X11Window {
|
||||||
impl X11WindowState {
|
impl X11WindowState {
|
||||||
pub fn new(
|
pub fn new(
|
||||||
params: WindowParams,
|
params: WindowParams,
|
||||||
xcb_connection: &Rc<xcb::Connection>,
|
xcb_connection: &Rc<XCBConnection>,
|
||||||
x_main_screen_index: i32,
|
x_main_screen_index: usize,
|
||||||
x_window: x::Window,
|
x_window: xproto::Window,
|
||||||
atoms: &XcbAtoms,
|
atoms: &XcbAtoms,
|
||||||
) -> Self {
|
) -> Self {
|
||||||
let x_screen_index = params
|
let x_screen_index = params
|
||||||
.display_id
|
.display_id
|
||||||
.map_or(x_main_screen_index, |did| did.0 as i32);
|
.map_or(x_main_screen_index, |did| did.0 as usize);
|
||||||
let screen = xcb_connection
|
let screen = xcb_connection.setup().roots.get(x_screen_index).unwrap();
|
||||||
.get_setup()
|
|
||||||
.roots()
|
let win_aux = xproto::CreateWindowAux::new().event_mask(
|
||||||
.nth(x_screen_index as usize)
|
xproto::EventMask::EXPOSURE
|
||||||
|
| xproto::EventMask::STRUCTURE_NOTIFY
|
||||||
|
| xproto::EventMask::ENTER_WINDOW
|
||||||
|
| xproto::EventMask::LEAVE_WINDOW
|
||||||
|
| xproto::EventMask::FOCUS_CHANGE
|
||||||
|
| xproto::EventMask::KEY_PRESS
|
||||||
|
| xproto::EventMask::KEY_RELEASE
|
||||||
|
| xproto::EventMask::BUTTON_PRESS
|
||||||
|
| xproto::EventMask::BUTTON_RELEASE
|
||||||
|
| xproto::EventMask::POINTER_MOTION
|
||||||
|
| xproto::EventMask::BUTTON1_MOTION
|
||||||
|
| xproto::EventMask::BUTTON2_MOTION
|
||||||
|
| xproto::EventMask::BUTTON3_MOTION
|
||||||
|
| xproto::EventMask::BUTTON4_MOTION
|
||||||
|
| xproto::EventMask::BUTTON5_MOTION
|
||||||
|
| xproto::EventMask::BUTTON_MOTION,
|
||||||
|
);
|
||||||
|
|
||||||
|
xcb_connection
|
||||||
|
.create_window(
|
||||||
|
x11rb::COPY_FROM_PARENT as _,
|
||||||
|
x_window,
|
||||||
|
screen.root,
|
||||||
|
params.bounds.origin.x.0 as i16,
|
||||||
|
params.bounds.origin.y.0 as i16,
|
||||||
|
params.bounds.size.width.0 as u16,
|
||||||
|
params.bounds.size.height.0 as u16,
|
||||||
|
0,
|
||||||
|
xproto::WindowClass::INPUT_OUTPUT,
|
||||||
|
screen.root_visual,
|
||||||
|
&win_aux,
|
||||||
|
)
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
let xcb_values = [
|
|
||||||
x::Cw::BackPixel(screen.white_pixel()),
|
|
||||||
x::Cw::EventMask(
|
|
||||||
x::EventMask::EXPOSURE
|
|
||||||
| x::EventMask::STRUCTURE_NOTIFY
|
|
||||||
| x::EventMask::ENTER_WINDOW
|
|
||||||
| x::EventMask::LEAVE_WINDOW
|
|
||||||
| x::EventMask::FOCUS_CHANGE
|
|
||||||
| x::EventMask::KEY_PRESS
|
|
||||||
| x::EventMask::KEY_RELEASE
|
|
||||||
| x::EventMask::BUTTON_PRESS
|
|
||||||
| x::EventMask::BUTTON_RELEASE
|
|
||||||
| x::EventMask::POINTER_MOTION
|
|
||||||
| x::EventMask::BUTTON1_MOTION
|
|
||||||
| x::EventMask::BUTTON2_MOTION
|
|
||||||
| x::EventMask::BUTTON3_MOTION
|
|
||||||
| x::EventMask::BUTTON4_MOTION
|
|
||||||
| x::EventMask::BUTTON5_MOTION
|
|
||||||
| x::EventMask::BUTTON_MOTION,
|
|
||||||
),
|
|
||||||
];
|
|
||||||
|
|
||||||
xcb_connection.send_request(&x::CreateWindow {
|
|
||||||
depth: x::COPY_FROM_PARENT as u8,
|
|
||||||
wid: x_window,
|
|
||||||
parent: screen.root(),
|
|
||||||
x: params.bounds.origin.x.0 as i16,
|
|
||||||
y: params.bounds.origin.y.0 as i16,
|
|
||||||
width: params.bounds.size.width.0 as u16,
|
|
||||||
height: params.bounds.size.height.0 as u16,
|
|
||||||
border_width: 0,
|
|
||||||
class: x::WindowClass::InputOutput,
|
|
||||||
visual: screen.root_visual(),
|
|
||||||
value_list: &xcb_values,
|
|
||||||
});
|
|
||||||
|
|
||||||
if let Some(titlebar) = params.titlebar {
|
if let Some(titlebar) = params.titlebar {
|
||||||
if let Some(title) = titlebar.title {
|
if let Some(title) = titlebar.title {
|
||||||
xcb_connection.send_request(&x::ChangeProperty {
|
xcb_connection
|
||||||
mode: x::PropMode::Replace,
|
.change_property8(
|
||||||
window: x_window,
|
xproto::PropMode::REPLACE,
|
||||||
property: x::ATOM_WM_NAME,
|
x_window,
|
||||||
r#type: x::ATOM_STRING,
|
xproto::AtomEnum::WM_NAME,
|
||||||
data: title.as_bytes(),
|
xproto::AtomEnum::STRING,
|
||||||
});
|
title.as_bytes(),
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
xcb_connection.send_request(&x::ChangeProperty {
|
|
||||||
mode: x::PropMode::Replace,
|
|
||||||
window: x_window,
|
|
||||||
property: atoms.wm_protocols,
|
|
||||||
r#type: x::ATOM_ATOM,
|
|
||||||
data: &[atoms.wm_del_window],
|
|
||||||
});
|
|
||||||
|
|
||||||
xcb_connection.send_request(&x::MapWindow { window: x_window });
|
xcb_connection
|
||||||
|
.change_property32(
|
||||||
|
xproto::PropMode::REPLACE,
|
||||||
|
x_window,
|
||||||
|
atoms.WM_PROTOCOLS,
|
||||||
|
xproto::AtomEnum::ATOM,
|
||||||
|
&[atoms.WM_DELETE_WINDOW],
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
xcb_connection.map_window(x_window).unwrap();
|
||||||
xcb_connection.flush().unwrap();
|
xcb_connection.flush().unwrap();
|
||||||
|
|
||||||
let raw = RawWindow {
|
let raw = RawWindow {
|
||||||
|
@ -216,8 +217,8 @@ impl X11WindowState {
|
||||||
xcb_connection,
|
xcb_connection,
|
||||||
) as *mut _,
|
) as *mut _,
|
||||||
screen_id: x_screen_index,
|
screen_id: x_screen_index,
|
||||||
window_id: x_window.resource_id(),
|
window_id: x_window,
|
||||||
visual_id: screen.root_visual(),
|
visual_id: screen.root_visual,
|
||||||
};
|
};
|
||||||
let gpu = Arc::new(
|
let gpu = Arc::new(
|
||||||
unsafe {
|
unsafe {
|
||||||
|
@ -254,12 +255,8 @@ impl X11WindowState {
|
||||||
|
|
||||||
pub fn destroy(&self) {
|
pub fn destroy(&self) {
|
||||||
self.inner.borrow_mut().renderer.destroy();
|
self.inner.borrow_mut().renderer.destroy();
|
||||||
self.xcb_connection.send_request(&x::UnmapWindow {
|
self.xcb_connection.unmap_window(self.x_window).unwrap();
|
||||||
window: self.x_window,
|
self.xcb_connection.destroy_window(self.x_window).unwrap();
|
||||||
});
|
|
||||||
self.xcb_connection.send_request(&x::DestroyWindow {
|
|
||||||
window: self.x_window,
|
|
||||||
});
|
|
||||||
if let Some(fun) = self.callbacks.borrow_mut().close.take() {
|
if let Some(fun) = self.callbacks.borrow_mut().close.take() {
|
||||||
fun();
|
fun();
|
||||||
}
|
}
|
||||||
|
@ -359,14 +356,14 @@ impl PlatformWindow for X11Window {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn mouse_position(&self) -> Point<Pixels> {
|
fn mouse_position(&self) -> Point<Pixels> {
|
||||||
let cookie = self.0.xcb_connection.send_request(&x::QueryPointer {
|
let reply = self
|
||||||
window: self.0.x_window,
|
.0
|
||||||
});
|
.xcb_connection
|
||||||
let reply: x::QueryPointerReply = self.0.xcb_connection.wait_for_reply(cookie).unwrap();
|
.query_pointer(self.0.x_window)
|
||||||
Point::new(
|
.unwrap()
|
||||||
(reply.root_x() as u32).into(),
|
.reply()
|
||||||
(reply.root_y() as u32).into(),
|
.unwrap();
|
||||||
)
|
Point::new((reply.root_x as u32).into(), (reply.root_y as u32).into())
|
||||||
}
|
}
|
||||||
|
|
||||||
// todo(linux)
|
// todo(linux)
|
||||||
|
@ -397,20 +394,24 @@ impl PlatformWindow for X11Window {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn activate(&self) {
|
fn activate(&self) {
|
||||||
self.0.xcb_connection.send_request(&x::ConfigureWindow {
|
let win_aux = xproto::ConfigureWindowAux::new().stack_mode(xproto::StackMode::ABOVE);
|
||||||
window: self.0.x_window,
|
self.0
|
||||||
value_list: &[x::ConfigWindow::StackMode(x::StackMode::Above)],
|
.xcb_connection
|
||||||
});
|
.configure_window(self.0.x_window, &win_aux)
|
||||||
|
.unwrap();
|
||||||
}
|
}
|
||||||
|
|
||||||
fn set_title(&mut self, title: &str) {
|
fn set_title(&mut self, title: &str) {
|
||||||
self.0.xcb_connection.send_request(&x::ChangeProperty {
|
self.0
|
||||||
mode: x::PropMode::Replace,
|
.xcb_connection
|
||||||
window: self.0.x_window,
|
.change_property8(
|
||||||
property: x::ATOM_WM_NAME,
|
xproto::PropMode::REPLACE,
|
||||||
r#type: x::ATOM_STRING,
|
self.0.x_window,
|
||||||
data: title.as_bytes(),
|
xproto::AtomEnum::WM_NAME,
|
||||||
});
|
xproto::AtomEnum::STRING,
|
||||||
|
title.as_bytes(),
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
}
|
}
|
||||||
|
|
||||||
// todo(linux)
|
// todo(linux)
|
||||||
|
@ -443,7 +444,7 @@ impl PlatformWindow for X11Window {
|
||||||
|
|
||||||
// todo(linux)
|
// todo(linux)
|
||||||
fn is_full_screen(&self) -> bool {
|
fn is_full_screen(&self) -> bool {
|
||||||
unimplemented!()
|
false
|
||||||
}
|
}
|
||||||
|
|
||||||
fn on_request_frame(&self, callback: Box<dyn FnMut()>) {
|
fn on_request_frame(&self, callback: Box<dyn FnMut()>) {
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue