Improve error handling and resource cleanup in linux/x11/window.rs
(#21079)
* Fixes registration of event handler for xinput-2 device changes, revealed by this improvement. * Pushes `.unwrap()` panic-ing outwards to callers. * Includes a description of what the X11 call was doing when a failure was encountered. * Fixes a variety of places where the X11 reply wasn't being inspected for failures. * Destroys windows on failure during setup. New structure makes it possible for the caller of `open_window` to carry on despite failures, and so partially initialized window should be removed (though all calls I looked at also panic currently). Considered pushing this through `linux/x11/client.rs` too but figured it'd be nice to minimize merge conflicts with #20853. Release Notes: - N/A
This commit is contained in:
parent
8240a52a39
commit
c9f2c2792c
3 changed files with 454 additions and 349 deletions
|
@ -776,11 +776,11 @@ impl X11Client {
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
let window = self.get_window(event.window)?;
|
let window = self.get_window(event.window)?;
|
||||||
window.configure(bounds);
|
window.configure(bounds).unwrap();
|
||||||
}
|
}
|
||||||
Event::PropertyNotify(event) => {
|
Event::PropertyNotify(event) => {
|
||||||
let window = self.get_window(event.window)?;
|
let window = self.get_window(event.window)?;
|
||||||
window.property_notify(event);
|
window.property_notify(event).unwrap();
|
||||||
}
|
}
|
||||||
Event::FocusIn(event) => {
|
Event::FocusIn(event) => {
|
||||||
let window = self.get_window(event.event)?;
|
let window = self.get_window(event.event)?;
|
||||||
|
@ -1258,11 +1258,9 @@ impl LinuxClient for X11Client {
|
||||||
.iter()
|
.iter()
|
||||||
.enumerate()
|
.enumerate()
|
||||||
.filter_map(|(root_id, _)| {
|
.filter_map(|(root_id, _)| {
|
||||||
Some(Rc::new(X11Display::new(
|
Some(Rc::new(
|
||||||
&state.xcb_connection,
|
X11Display::new(&state.xcb_connection, state.scale_factor, root_id).ok()?,
|
||||||
state.scale_factor,
|
) as Rc<dyn PlatformDisplay>)
|
||||||
root_id,
|
|
||||||
)?) as Rc<dyn PlatformDisplay>)
|
|
||||||
})
|
})
|
||||||
.collect()
|
.collect()
|
||||||
}
|
}
|
||||||
|
@ -1283,11 +1281,9 @@ impl LinuxClient for X11Client {
|
||||||
fn display(&self, id: DisplayId) -> Option<Rc<dyn PlatformDisplay>> {
|
fn display(&self, id: DisplayId) -> Option<Rc<dyn PlatformDisplay>> {
|
||||||
let state = self.0.borrow();
|
let state = self.0.borrow();
|
||||||
|
|
||||||
Some(Rc::new(X11Display::new(
|
Some(Rc::new(
|
||||||
&state.xcb_connection,
|
X11Display::new(&state.xcb_connection, state.scale_factor, id.0 as usize).ok()?,
|
||||||
state.scale_factor,
|
))
|
||||||
id.0 as usize,
|
|
||||||
)?))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn open_window(
|
fn open_window(
|
||||||
|
|
|
@ -13,12 +13,17 @@ pub(crate) struct X11Display {
|
||||||
|
|
||||||
impl X11Display {
|
impl X11Display {
|
||||||
pub(crate) fn new(
|
pub(crate) fn new(
|
||||||
xc: &XCBConnection,
|
xcb: &XCBConnection,
|
||||||
scale_factor: f32,
|
scale_factor: f32,
|
||||||
x_screen_index: usize,
|
x_screen_index: usize,
|
||||||
) -> Option<Self> {
|
) -> anyhow::Result<Self> {
|
||||||
let screen = xc.setup().roots.get(x_screen_index).unwrap();
|
let Some(screen) = xcb.setup().roots.get(x_screen_index) else {
|
||||||
Some(Self {
|
return Err(anyhow::anyhow!(
|
||||||
|
"No screen found with index {}",
|
||||||
|
x_screen_index
|
||||||
|
));
|
||||||
|
};
|
||||||
|
Ok(Self {
|
||||||
x_screen_index,
|
x_screen_index,
|
||||||
bounds: Bounds {
|
bounds: Bounds {
|
||||||
origin: Default::default(),
|
origin: Default::default(),
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
use anyhow::Context;
|
use anyhow::{anyhow, Context};
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
platform::blade::{BladeRenderer, BladeSurfaceConfig},
|
platform::blade::{BladeRenderer, BladeSurfaceConfig},
|
||||||
|
@ -14,6 +14,8 @@ use raw_window_handle as rwh;
|
||||||
use util::{maybe, ResultExt};
|
use util::{maybe, ResultExt};
|
||||||
use x11rb::{
|
use x11rb::{
|
||||||
connection::Connection,
|
connection::Connection,
|
||||||
|
cookie::{Cookie, VoidCookie},
|
||||||
|
errors::ConnectionError,
|
||||||
properties::WmSizeHints,
|
properties::WmSizeHints,
|
||||||
protocol::{
|
protocol::{
|
||||||
sync,
|
sync,
|
||||||
|
@ -25,7 +27,7 @@ use x11rb::{
|
||||||
};
|
};
|
||||||
|
|
||||||
use std::{
|
use std::{
|
||||||
cell::RefCell, ffi::c_void, mem::size_of, num::NonZeroU32, ops::Div, ptr::NonNull, rc::Rc,
|
cell::RefCell, ffi::c_void, fmt::Display, num::NonZeroU32, ops::Div, ptr::NonNull, rc::Rc,
|
||||||
sync::Arc,
|
sync::Arc,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -77,17 +79,16 @@ x11rb::atom_manager! {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn query_render_extent(xcb_connection: &XCBConnection, x_window: xproto::Window) -> gpu::Extent {
|
fn query_render_extent(
|
||||||
let reply = xcb_connection
|
xcb: &Rc<XCBConnection>,
|
||||||
.get_geometry(x_window)
|
x_window: xproto::Window,
|
||||||
.unwrap()
|
) -> anyhow::Result<gpu::Extent> {
|
||||||
.reply()
|
let reply = get_reply(|| "X11 GetGeometry failed.", xcb.get_geometry(x_window))?;
|
||||||
.unwrap();
|
Ok(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,
|
||||||
}
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ResizeEdge {
|
impl ResizeEdge {
|
||||||
|
@ -148,7 +149,7 @@ impl EdgeConstraints {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Copy, Clone, Debug)]
|
||||||
struct Visual {
|
struct Visual {
|
||||||
id: xproto::Visualid,
|
id: xproto::Visualid,
|
||||||
colormap: u32,
|
colormap: u32,
|
||||||
|
@ -163,8 +164,8 @@ struct VisualSet {
|
||||||
black_pixel: u32,
|
black_pixel: u32,
|
||||||
}
|
}
|
||||||
|
|
||||||
fn find_visuals(xcb_connection: &XCBConnection, screen_index: usize) -> VisualSet {
|
fn find_visuals(xcb: &XCBConnection, screen_index: usize) -> VisualSet {
|
||||||
let screen = &xcb_connection.setup().roots[screen_index];
|
let screen = &xcb.setup().roots[screen_index];
|
||||||
let mut set = VisualSet {
|
let mut set = VisualSet {
|
||||||
inherit: Visual {
|
inherit: Visual {
|
||||||
id: screen.root_visual,
|
id: screen.root_visual,
|
||||||
|
@ -277,13 +278,16 @@ impl X11WindowState {
|
||||||
pub(crate) struct X11WindowStatePtr {
|
pub(crate) struct X11WindowStatePtr {
|
||||||
pub state: Rc<RefCell<X11WindowState>>,
|
pub state: Rc<RefCell<X11WindowState>>,
|
||||||
pub(crate) callbacks: Rc<RefCell<Callbacks>>,
|
pub(crate) callbacks: Rc<RefCell<Callbacks>>,
|
||||||
xcb_connection: Rc<XCBConnection>,
|
xcb: Rc<XCBConnection>,
|
||||||
x_window: xproto::Window,
|
x_window: xproto::Window,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl rwh::HasWindowHandle for RawWindow {
|
impl rwh::HasWindowHandle for RawWindow {
|
||||||
fn window_handle(&self) -> Result<rwh::WindowHandle, rwh::HandleError> {
|
fn window_handle(&self) -> Result<rwh::WindowHandle, rwh::HandleError> {
|
||||||
let non_zero = NonZeroU32::new(self.window_id).unwrap();
|
let Some(non_zero) = NonZeroU32::new(self.window_id) else {
|
||||||
|
log::error!("RawWindow.window_id zero when getting window handle.");
|
||||||
|
return Err(rwh::HandleError::Unavailable);
|
||||||
|
};
|
||||||
let mut handle = rwh::XcbWindowHandle::new(non_zero);
|
let mut handle = rwh::XcbWindowHandle::new(non_zero);
|
||||||
handle.visual_id = NonZeroU32::new(self.visual_id);
|
handle.visual_id = NonZeroU32::new(self.visual_id);
|
||||||
Ok(unsafe { rwh::WindowHandle::borrow_raw(handle.into()) })
|
Ok(unsafe { rwh::WindowHandle::borrow_raw(handle.into()) })
|
||||||
|
@ -291,7 +295,10 @@ impl rwh::HasWindowHandle for RawWindow {
|
||||||
}
|
}
|
||||||
impl rwh::HasDisplayHandle for RawWindow {
|
impl rwh::HasDisplayHandle for RawWindow {
|
||||||
fn display_handle(&self) -> Result<rwh::DisplayHandle, rwh::HandleError> {
|
fn display_handle(&self) -> Result<rwh::DisplayHandle, rwh::HandleError> {
|
||||||
let non_zero = NonNull::new(self.connection).unwrap();
|
let Some(non_zero) = NonNull::new(self.connection) else {
|
||||||
|
log::error!("Null RawWindow.connection when getting display handle.");
|
||||||
|
return Err(rwh::HandleError::Unavailable);
|
||||||
|
};
|
||||||
let handle = rwh::XcbDisplayHandle::new(Some(non_zero), self.screen_id as i32);
|
let handle = rwh::XcbDisplayHandle::new(Some(non_zero), self.screen_id as i32);
|
||||||
Ok(unsafe { rwh::DisplayHandle::borrow_raw(handle.into()) })
|
Ok(unsafe { rwh::DisplayHandle::borrow_raw(handle.into()) })
|
||||||
}
|
}
|
||||||
|
@ -308,6 +315,43 @@ impl rwh::HasDisplayHandle for X11Window {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn check_reply<C, F>(
|
||||||
|
failure_context: F,
|
||||||
|
result: Result<VoidCookie<'_, Rc<XCBConnection>>, ConnectionError>,
|
||||||
|
) -> anyhow::Result<()>
|
||||||
|
where
|
||||||
|
C: Display + Send + Sync + 'static,
|
||||||
|
F: FnOnce() -> C,
|
||||||
|
{
|
||||||
|
result
|
||||||
|
.map_err(|connection_error| anyhow!(connection_error))
|
||||||
|
.and_then(|response| {
|
||||||
|
response
|
||||||
|
.check()
|
||||||
|
.map_err(|error_response| anyhow!(error_response))
|
||||||
|
})
|
||||||
|
.with_context(failure_context)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get_reply<C, F, O>(
|
||||||
|
failure_context: F,
|
||||||
|
result: Result<Cookie<'_, Rc<XCBConnection>, O>, ConnectionError>,
|
||||||
|
) -> anyhow::Result<O>
|
||||||
|
where
|
||||||
|
C: Display + Send + Sync + 'static,
|
||||||
|
F: FnOnce() -> C,
|
||||||
|
O: x11rb::x11_utils::TryParse,
|
||||||
|
{
|
||||||
|
result
|
||||||
|
.map_err(|connection_error| anyhow!(connection_error))
|
||||||
|
.and_then(|response| {
|
||||||
|
response
|
||||||
|
.reply()
|
||||||
|
.map_err(|error_response| anyhow!(error_response))
|
||||||
|
})
|
||||||
|
.with_context(failure_context)
|
||||||
|
}
|
||||||
|
|
||||||
impl X11WindowState {
|
impl X11WindowState {
|
||||||
#[allow(clippy::too_many_arguments)]
|
#[allow(clippy::too_many_arguments)]
|
||||||
pub fn new(
|
pub fn new(
|
||||||
|
@ -315,7 +359,7 @@ impl X11WindowState {
|
||||||
client: X11ClientStatePtr,
|
client: X11ClientStatePtr,
|
||||||
executor: ForegroundExecutor,
|
executor: ForegroundExecutor,
|
||||||
params: WindowParams,
|
params: WindowParams,
|
||||||
xcb_connection: &Rc<XCBConnection>,
|
xcb: &Rc<XCBConnection>,
|
||||||
client_side_decorations_supported: bool,
|
client_side_decorations_supported: bool,
|
||||||
x_main_screen_index: usize,
|
x_main_screen_index: usize,
|
||||||
x_window: xproto::Window,
|
x_window: xproto::Window,
|
||||||
|
@ -327,7 +371,7 @@ impl X11WindowState {
|
||||||
.display_id
|
.display_id
|
||||||
.map_or(x_main_screen_index, |did| did.0 as usize);
|
.map_or(x_main_screen_index, |did| did.0 as usize);
|
||||||
|
|
||||||
let visual_set = find_visuals(&xcb_connection, x_screen_index);
|
let visual_set = find_visuals(&xcb, x_screen_index);
|
||||||
|
|
||||||
let visual = match visual_set.transparent {
|
let visual = match visual_set.transparent {
|
||||||
Some(visual) => visual,
|
Some(visual) => visual,
|
||||||
|
@ -341,12 +385,12 @@ impl X11WindowState {
|
||||||
let colormap = if visual.colormap != 0 {
|
let colormap = if visual.colormap != 0 {
|
||||||
visual.colormap
|
visual.colormap
|
||||||
} else {
|
} else {
|
||||||
let id = xcb_connection.generate_id().unwrap();
|
let id = xcb.generate_id()?;
|
||||||
log::info!("Creating colormap {}", id);
|
log::info!("Creating colormap {}", id);
|
||||||
xcb_connection
|
check_reply(
|
||||||
.create_colormap(xproto::ColormapAlloc::NONE, id, visual_set.root, visual.id)
|
|| format!("X11 CreateColormap failed. id: {}", id),
|
||||||
.unwrap()
|
xcb.create_colormap(xproto::ColormapAlloc::NONE, id, visual_set.root, visual.id),
|
||||||
.check()?;
|
)?;
|
||||||
id
|
id
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -370,8 +414,12 @@ impl X11WindowState {
|
||||||
bounds.size.height = 600.into();
|
bounds.size.height = 600.into();
|
||||||
}
|
}
|
||||||
|
|
||||||
xcb_connection
|
check_reply(
|
||||||
.create_window(
|
|| {
|
||||||
|
format!("X11 CreateWindow failed. depth: {}, x_window: {}, visual_set.root: {}, bounds.origin.x.0: {}, bounds.origin.y.0: {}, bounds.size.width.0: {}, bounds.size.height.0: {}",
|
||||||
|
visual.depth, x_window, visual_set.root, bounds.origin.x.0 + 2, bounds.origin.y.0, bounds.size.width.0, bounds.size.height.0)
|
||||||
|
},
|
||||||
|
xcb.create_window(
|
||||||
visual.depth,
|
visual.depth,
|
||||||
x_window,
|
x_window,
|
||||||
visual_set.root,
|
visual_set.root,
|
||||||
|
@ -383,96 +431,101 @@ impl X11WindowState {
|
||||||
xproto::WindowClass::INPUT_OUTPUT,
|
xproto::WindowClass::INPUT_OUTPUT,
|
||||||
visual.id,
|
visual.id,
|
||||||
&win_aux,
|
&win_aux,
|
||||||
)
|
),
|
||||||
.unwrap()
|
)?;
|
||||||
.check().with_context(|| {
|
|
||||||
format!("CreateWindow request to X server failed. depth: {}, x_window: {}, visual_set.root: {}, bounds.origin.x.0: {}, bounds.origin.y.0: {}, bounds.size.width.0: {}, bounds.size.height.0: {}",
|
|
||||||
visual.depth, x_window, visual_set.root, bounds.origin.x.0 + 2, bounds.origin.y.0, bounds.size.width.0, bounds.size.height.0)
|
|
||||||
})?;
|
|
||||||
|
|
||||||
|
// Collect errors during setup, so that window can be destroyed on failure.
|
||||||
|
let setup_result = maybe!({
|
||||||
if let Some(size) = params.window_min_size {
|
if let Some(size) = params.window_min_size {
|
||||||
let mut size_hints = WmSizeHints::new();
|
let mut size_hints = WmSizeHints::new();
|
||||||
size_hints.min_size = Some((size.width.0 as i32, size.height.0 as i32));
|
let min_size = (size.width.0 as i32, size.height.0 as i32);
|
||||||
size_hints
|
size_hints.min_size = Some(min_size);
|
||||||
.set_normal_hints(xcb_connection, x_window)
|
check_reply(
|
||||||
.unwrap();
|
|| {
|
||||||
|
format!(
|
||||||
|
"X11 change of WM_SIZE_HINTS failed. min_size: {:?}",
|
||||||
|
min_size
|
||||||
|
)
|
||||||
|
},
|
||||||
|
size_hints.set_normal_hints(xcb, x_window),
|
||||||
|
)?;
|
||||||
}
|
}
|
||||||
|
|
||||||
let reply = xcb_connection
|
let reply = get_reply(|| "X11 GetGeometry failed.", xcb.get_geometry(x_window))?;
|
||||||
.get_geometry(x_window)
|
|
||||||
.unwrap()
|
|
||||||
.reply()
|
|
||||||
.unwrap();
|
|
||||||
if reply.x == 0 && reply.y == 0 {
|
if reply.x == 0 && reply.y == 0 {
|
||||||
bounds.origin.x.0 += 2;
|
bounds.origin.x.0 += 2;
|
||||||
// Work around a bug where our rendered content appears
|
// Work around a bug where our rendered content appears
|
||||||
// outside the window bounds when opened at the default position
|
// outside the window bounds when opened at the default position
|
||||||
// (14px, 49px on X + Gnome + Ubuntu 22).
|
// (14px, 49px on X + Gnome + Ubuntu 22).
|
||||||
xcb_connection
|
let x = bounds.origin.x.0;
|
||||||
.configure_window(
|
let y = bounds.origin.y.0;
|
||||||
x_window,
|
check_reply(
|
||||||
&xproto::ConfigureWindowAux::new()
|
|| format!("X11 ConfigureWindow failed. x: {}, y: {}", x, y),
|
||||||
.x(bounds.origin.x.0)
|
xcb.configure_window(x_window, &xproto::ConfigureWindowAux::new().x(x).y(y)),
|
||||||
.y(bounds.origin.y.0),
|
)?;
|
||||||
)
|
|
||||||
.unwrap();
|
|
||||||
}
|
}
|
||||||
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
|
check_reply(
|
||||||
.change_property8(
|
|| "X11 ChangeProperty8 on window title failed.",
|
||||||
|
xcb.change_property8(
|
||||||
xproto::PropMode::REPLACE,
|
xproto::PropMode::REPLACE,
|
||||||
x_window,
|
x_window,
|
||||||
xproto::AtomEnum::WM_NAME,
|
xproto::AtomEnum::WM_NAME,
|
||||||
xproto::AtomEnum::STRING,
|
xproto::AtomEnum::STRING,
|
||||||
title.as_bytes(),
|
title.as_bytes(),
|
||||||
)
|
),
|
||||||
.unwrap();
|
)?;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if params.kind == WindowKind::PopUp {
|
if params.kind == WindowKind::PopUp {
|
||||||
xcb_connection
|
check_reply(
|
||||||
.change_property32(
|
|| "X11 ChangeProperty32 setting window type for pop-up failed.",
|
||||||
|
xcb.change_property32(
|
||||||
xproto::PropMode::REPLACE,
|
xproto::PropMode::REPLACE,
|
||||||
x_window,
|
x_window,
|
||||||
atoms._NET_WM_WINDOW_TYPE,
|
atoms._NET_WM_WINDOW_TYPE,
|
||||||
xproto::AtomEnum::ATOM,
|
xproto::AtomEnum::ATOM,
|
||||||
&[atoms._NET_WM_WINDOW_TYPE_NOTIFICATION],
|
&[atoms._NET_WM_WINDOW_TYPE_NOTIFICATION],
|
||||||
)
|
),
|
||||||
.unwrap();
|
)?;
|
||||||
}
|
}
|
||||||
|
|
||||||
xcb_connection
|
check_reply(
|
||||||
.change_property32(
|
|| "X11 ChangeProperty32 setting protocols failed.",
|
||||||
|
xcb.change_property32(
|
||||||
xproto::PropMode::REPLACE,
|
xproto::PropMode::REPLACE,
|
||||||
x_window,
|
x_window,
|
||||||
atoms.WM_PROTOCOLS,
|
atoms.WM_PROTOCOLS,
|
||||||
xproto::AtomEnum::ATOM,
|
xproto::AtomEnum::ATOM,
|
||||||
&[atoms.WM_DELETE_WINDOW, atoms._NET_WM_SYNC_REQUEST],
|
&[atoms.WM_DELETE_WINDOW, atoms._NET_WM_SYNC_REQUEST],
|
||||||
)
|
),
|
||||||
.unwrap();
|
)?;
|
||||||
|
|
||||||
sync::initialize(xcb_connection, 3, 1).unwrap();
|
get_reply(
|
||||||
let sync_request_counter = xcb_connection.generate_id().unwrap();
|
|| "X11 sync protocol initialize failed.",
|
||||||
sync::create_counter(
|
sync::initialize(xcb, 3, 1),
|
||||||
xcb_connection,
|
)?;
|
||||||
sync_request_counter,
|
let sync_request_counter = xcb.generate_id()?;
|
||||||
sync::Int64 { lo: 0, hi: 0 },
|
check_reply(
|
||||||
)
|
|| "X11 sync CreateCounter failed.",
|
||||||
.unwrap();
|
sync::create_counter(xcb, sync_request_counter, sync::Int64 { lo: 0, hi: 0 }),
|
||||||
|
)?;
|
||||||
|
|
||||||
xcb_connection
|
check_reply(
|
||||||
.change_property32(
|
|| "X11 ChangeProperty32 setting sync request counter failed.",
|
||||||
|
xcb.change_property32(
|
||||||
xproto::PropMode::REPLACE,
|
xproto::PropMode::REPLACE,
|
||||||
x_window,
|
x_window,
|
||||||
atoms._NET_WM_SYNC_REQUEST_COUNTER,
|
atoms._NET_WM_SYNC_REQUEST_COUNTER,
|
||||||
xproto::AtomEnum::CARDINAL,
|
xproto::AtomEnum::CARDINAL,
|
||||||
&[sync_request_counter],
|
&[sync_request_counter],
|
||||||
)
|
),
|
||||||
.unwrap();
|
)?;
|
||||||
|
|
||||||
xcb_connection
|
check_reply(
|
||||||
.xinput_xi_select_events(
|
|| "X11 XiSelectEvents failed.",
|
||||||
|
xcb.xinput_xi_select_events(
|
||||||
x_window,
|
x_window,
|
||||||
&[xinput::EventMask {
|
&[xinput::EventMask {
|
||||||
deviceid: XINPUT_ALL_DEVICE_GROUPS,
|
deviceid: XINPUT_ALL_DEVICE_GROUPS,
|
||||||
|
@ -484,28 +537,27 @@ impl X11WindowState {
|
||||||
| xinput::XIEventMask::LEAVE,
|
| xinput::XIEventMask::LEAVE,
|
||||||
],
|
],
|
||||||
}],
|
}],
|
||||||
)
|
),
|
||||||
.unwrap();
|
)?;
|
||||||
|
|
||||||
xcb_connection
|
check_reply(
|
||||||
.xinput_xi_select_events(
|
|| "X11 XiSelectEvents for device changes failed.",
|
||||||
|
xcb.xinput_xi_select_events(
|
||||||
x_window,
|
x_window,
|
||||||
&[xinput::EventMask {
|
&[xinput::EventMask {
|
||||||
deviceid: XINPUT_ALL_DEVICES,
|
deviceid: XINPUT_ALL_DEVICES,
|
||||||
mask: vec![
|
mask: vec![
|
||||||
xinput::XIEventMask::HIERARCHY,
|
xinput::XIEventMask::HIERARCHY | xinput::XIEventMask::DEVICE_CHANGED,
|
||||||
xinput::XIEventMask::DEVICE_CHANGED,
|
|
||||||
],
|
],
|
||||||
}],
|
}],
|
||||||
)
|
),
|
||||||
.unwrap();
|
)?;
|
||||||
|
|
||||||
xcb_connection.flush().unwrap();
|
xcb.flush().with_context(|| "X11 Flush failed.")?;
|
||||||
|
|
||||||
let raw = RawWindow {
|
let raw = RawWindow {
|
||||||
connection: as_raw_xcb_connection::AsRawXcbConnection::as_raw_xcb_connection(
|
connection: as_raw_xcb_connection::AsRawXcbConnection::as_raw_xcb_connection(xcb)
|
||||||
xcb_connection,
|
as *mut _,
|
||||||
) as *mut _,
|
|
||||||
screen_id: x_screen_index,
|
screen_id: x_screen_index,
|
||||||
window_id: x_window,
|
window_id: x_window,
|
||||||
visual_id: visual.id,
|
visual_id: visual.id,
|
||||||
|
@ -521,27 +573,27 @@ impl X11WindowState {
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
.map_err(|e| anyhow::anyhow!("{:?}", e))?,
|
.map_err(|e| anyhow!("{:?}", e))?,
|
||||||
);
|
);
|
||||||
|
|
||||||
let config = BladeSurfaceConfig {
|
let config = BladeSurfaceConfig {
|
||||||
// Note: this has to be done after the GPU init, or otherwise
|
// Note: this has to be done after the GPU init, or otherwise
|
||||||
// the sizes are immediately invalidated.
|
// the sizes are immediately invalidated.
|
||||||
size: query_render_extent(xcb_connection, x_window),
|
size: query_render_extent(xcb, x_window)?,
|
||||||
// We set it to transparent by default, even if we have client-side
|
// We set it to transparent by default, even if we have client-side
|
||||||
// decorations, since those seem to work on X11 even without `true` here.
|
// decorations, since those seem to work on X11 even without `true` here.
|
||||||
// If the window appearance changes, then the renderer will get updated
|
// If the window appearance changes, then the renderer will get updated
|
||||||
// too
|
// too
|
||||||
transparent: false,
|
transparent: false,
|
||||||
};
|
};
|
||||||
xcb_connection.map_window(x_window).unwrap();
|
check_reply(|| "X11 MapWindow failed.", xcb.map_window(x_window))?;
|
||||||
|
|
||||||
|
let display = Rc::new(X11Display::new(xcb, scale_factor, x_screen_index)?);
|
||||||
|
|
||||||
Ok(Self {
|
Ok(Self {
|
||||||
client,
|
client,
|
||||||
executor,
|
executor,
|
||||||
display: Rc::new(
|
display,
|
||||||
X11Display::new(xcb_connection, scale_factor, x_screen_index).unwrap(),
|
|
||||||
),
|
|
||||||
_raw: raw,
|
_raw: raw,
|
||||||
x_root_window: visual_set.root,
|
x_root_window: visual_set.root,
|
||||||
bounds: bounds.to_pixels(scale_factor),
|
bounds: bounds.to_pixels(scale_factor),
|
||||||
|
@ -566,6 +618,18 @@ impl X11WindowState {
|
||||||
counter_id: sync_request_counter,
|
counter_id: sync_request_counter,
|
||||||
last_sync_counter: None,
|
last_sync_counter: None,
|
||||||
})
|
})
|
||||||
|
});
|
||||||
|
|
||||||
|
if setup_result.is_err() {
|
||||||
|
check_reply(
|
||||||
|
|| "X11 DestroyWindow failed while cleaning it up after setup failure.",
|
||||||
|
xcb.destroy_window(x_window),
|
||||||
|
)?;
|
||||||
|
xcb.flush()
|
||||||
|
.with_context(|| "X11 Flush failed while cleaning it up after setup failure.")?;
|
||||||
|
}
|
||||||
|
|
||||||
|
setup_result
|
||||||
}
|
}
|
||||||
|
|
||||||
fn content_size(&self) -> Size<Pixels> {
|
fn content_size(&self) -> Size<Pixels> {
|
||||||
|
@ -577,6 +641,28 @@ impl X11WindowState {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// A handle to an X11 window which destroys it on Drop.
|
||||||
|
pub struct X11WindowHandle {
|
||||||
|
id: xproto::Window,
|
||||||
|
xcb: Rc<XCBConnection>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Drop for X11WindowHandle {
|
||||||
|
fn drop(&mut self) {
|
||||||
|
maybe!({
|
||||||
|
check_reply(
|
||||||
|
|| "X11 DestroyWindow failed while dropping X11WindowHandle.",
|
||||||
|
self.xcb.destroy_window(self.id),
|
||||||
|
)?;
|
||||||
|
self.xcb
|
||||||
|
.flush()
|
||||||
|
.with_context(|| "X11 Flush failed while dropping X11WindowHandle.")?;
|
||||||
|
anyhow::Ok(())
|
||||||
|
})
|
||||||
|
.log_err();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub(crate) struct X11Window(pub X11WindowStatePtr);
|
pub(crate) struct X11Window(pub X11WindowStatePtr);
|
||||||
|
|
||||||
impl Drop for X11Window {
|
impl Drop for X11Window {
|
||||||
|
@ -585,13 +671,17 @@ impl Drop for X11Window {
|
||||||
state.renderer.destroy();
|
state.renderer.destroy();
|
||||||
|
|
||||||
let destroy_x_window = maybe!({
|
let destroy_x_window = maybe!({
|
||||||
self.0.xcb_connection.unmap_window(self.0.x_window)?;
|
check_reply(
|
||||||
self.0.xcb_connection.destroy_window(self.0.x_window)?;
|
|| "X11 DestroyWindow failure.",
|
||||||
self.0.xcb_connection.flush()?;
|
self.0.xcb.destroy_window(self.0.x_window),
|
||||||
|
)?;
|
||||||
|
self.0
|
||||||
|
.xcb
|
||||||
|
.flush()
|
||||||
|
.with_context(|| "X11 Flush failed after calling DestroyWindow.")?;
|
||||||
|
|
||||||
anyhow::Ok(())
|
anyhow::Ok(())
|
||||||
})
|
})
|
||||||
.context("unmapping and destroying X11 window")
|
|
||||||
.log_err();
|
.log_err();
|
||||||
|
|
||||||
if destroy_x_window.is_some() {
|
if destroy_x_window.is_some() {
|
||||||
|
@ -627,7 +717,7 @@ impl X11Window {
|
||||||
client: X11ClientStatePtr,
|
client: X11ClientStatePtr,
|
||||||
executor: ForegroundExecutor,
|
executor: ForegroundExecutor,
|
||||||
params: WindowParams,
|
params: WindowParams,
|
||||||
xcb_connection: &Rc<XCBConnection>,
|
xcb: &Rc<XCBConnection>,
|
||||||
client_side_decorations_supported: bool,
|
client_side_decorations_supported: bool,
|
||||||
x_main_screen_index: usize,
|
x_main_screen_index: usize,
|
||||||
x_window: xproto::Window,
|
x_window: xproto::Window,
|
||||||
|
@ -641,7 +731,7 @@ impl X11Window {
|
||||||
client,
|
client,
|
||||||
executor,
|
executor,
|
||||||
params,
|
params,
|
||||||
xcb_connection,
|
xcb,
|
||||||
client_side_decorations_supported,
|
client_side_decorations_supported,
|
||||||
x_main_screen_index,
|
x_main_screen_index,
|
||||||
x_window,
|
x_window,
|
||||||
|
@ -650,17 +740,23 @@ impl X11Window {
|
||||||
appearance,
|
appearance,
|
||||||
)?)),
|
)?)),
|
||||||
callbacks: Rc::new(RefCell::new(Callbacks::default())),
|
callbacks: Rc::new(RefCell::new(Callbacks::default())),
|
||||||
xcb_connection: xcb_connection.clone(),
|
xcb: xcb.clone(),
|
||||||
x_window,
|
x_window,
|
||||||
};
|
};
|
||||||
|
|
||||||
let state = ptr.state.borrow_mut();
|
let state = ptr.state.borrow_mut();
|
||||||
ptr.set_wm_properties(state);
|
ptr.set_wm_properties(state)?;
|
||||||
|
|
||||||
Ok(Self(ptr))
|
Ok(Self(ptr))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn set_wm_hints(&self, wm_hint_property_state: WmHintPropertyState, prop1: u32, prop2: u32) {
|
fn set_wm_hints<C: Display + Send + Sync + 'static, F: FnOnce() -> C>(
|
||||||
|
&self,
|
||||||
|
failure_context: F,
|
||||||
|
wm_hint_property_state: WmHintPropertyState,
|
||||||
|
prop1: u32,
|
||||||
|
prop2: u32,
|
||||||
|
) -> anyhow::Result<()> {
|
||||||
let state = self.0.state.borrow();
|
let state = self.0.state.borrow();
|
||||||
let message = ClientMessageEvent::new(
|
let message = ClientMessageEvent::new(
|
||||||
32,
|
32,
|
||||||
|
@ -668,51 +764,45 @@ impl X11Window {
|
||||||
state.atoms._NET_WM_STATE,
|
state.atoms._NET_WM_STATE,
|
||||||
[wm_hint_property_state as u32, prop1, prop2, 1, 0],
|
[wm_hint_property_state as u32, prop1, prop2, 1, 0],
|
||||||
);
|
);
|
||||||
self.0
|
check_reply(
|
||||||
.xcb_connection
|
failure_context,
|
||||||
.send_event(
|
self.0.xcb.send_event(
|
||||||
false,
|
false,
|
||||||
state.x_root_window,
|
state.x_root_window,
|
||||||
EventMask::SUBSTRUCTURE_REDIRECT | EventMask::SUBSTRUCTURE_NOTIFY,
|
EventMask::SUBSTRUCTURE_REDIRECT | EventMask::SUBSTRUCTURE_NOTIFY,
|
||||||
message,
|
message,
|
||||||
|
),
|
||||||
)
|
)
|
||||||
.unwrap()
|
|
||||||
.check()
|
|
||||||
.unwrap();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_root_position(&self, position: Point<Pixels>) -> TranslateCoordinatesReply {
|
fn get_root_position(
|
||||||
|
&self,
|
||||||
|
position: Point<Pixels>,
|
||||||
|
) -> anyhow::Result<TranslateCoordinatesReply> {
|
||||||
let state = self.0.state.borrow();
|
let state = self.0.state.borrow();
|
||||||
self.0
|
get_reply(
|
||||||
.xcb_connection
|
|| "X11 TranslateCoordinates failed.",
|
||||||
.translate_coordinates(
|
self.0.xcb.translate_coordinates(
|
||||||
self.0.x_window,
|
self.0.x_window,
|
||||||
state.x_root_window,
|
state.x_root_window,
|
||||||
(position.x.0 * state.scale_factor) as i16,
|
(position.x.0 * state.scale_factor) as i16,
|
||||||
(position.y.0 * state.scale_factor) as i16,
|
(position.y.0 * state.scale_factor) as i16,
|
||||||
|
),
|
||||||
)
|
)
|
||||||
.unwrap()
|
|
||||||
.reply()
|
|
||||||
.unwrap()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn send_moveresize(&self, flag: u32) {
|
fn send_moveresize(&self, flag: u32) -> anyhow::Result<()> {
|
||||||
let state = self.0.state.borrow();
|
let state = self.0.state.borrow();
|
||||||
|
|
||||||
self.0
|
check_reply(
|
||||||
.xcb_connection
|
|| "X11 UngrabPointer before move/resize of window ailed.",
|
||||||
.ungrab_pointer(x11rb::CURRENT_TIME)
|
self.0.xcb.ungrab_pointer(x11rb::CURRENT_TIME),
|
||||||
.unwrap()
|
)?;
|
||||||
.check()
|
|
||||||
.unwrap();
|
|
||||||
|
|
||||||
let pointer = self
|
let pointer = get_reply(
|
||||||
.0
|
|| "X11 QueryPointer before move/resize of window failed.",
|
||||||
.xcb_connection
|
self.0.xcb.query_pointer(self.0.x_window),
|
||||||
.query_pointer(self.0.x_window)
|
)?;
|
||||||
.unwrap()
|
|
||||||
.reply()
|
|
||||||
.unwrap();
|
|
||||||
let message = ClientMessageEvent::new(
|
let message = ClientMessageEvent::new(
|
||||||
32,
|
32,
|
||||||
self.0.x_window,
|
self.0.x_window,
|
||||||
|
@ -725,17 +815,21 @@ impl X11Window {
|
||||||
0,
|
0,
|
||||||
],
|
],
|
||||||
);
|
);
|
||||||
self.0
|
check_reply(
|
||||||
.xcb_connection
|
|| "X11 SendEvent to move/resize window failed.",
|
||||||
.send_event(
|
self.0.xcb.send_event(
|
||||||
false,
|
false,
|
||||||
state.x_root_window,
|
state.x_root_window,
|
||||||
EventMask::SUBSTRUCTURE_REDIRECT | EventMask::SUBSTRUCTURE_NOTIFY,
|
EventMask::SUBSTRUCTURE_REDIRECT | EventMask::SUBSTRUCTURE_NOTIFY,
|
||||||
message,
|
message,
|
||||||
)
|
),
|
||||||
.unwrap();
|
)?;
|
||||||
|
|
||||||
self.0.xcb_connection.flush().unwrap();
|
self.flush()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn flush(&self) -> anyhow::Result<()> {
|
||||||
|
self.0.xcb.flush().with_context(|| "X11 Flush failed.")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -751,51 +845,56 @@ impl X11WindowStatePtr {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn property_notify(&self, event: xproto::PropertyNotifyEvent) {
|
pub fn property_notify(&self, event: xproto::PropertyNotifyEvent) -> anyhow::Result<()> {
|
||||||
let mut state = self.state.borrow_mut();
|
let mut state = self.state.borrow_mut();
|
||||||
if event.atom == state.atoms._NET_WM_STATE {
|
if event.atom == state.atoms._NET_WM_STATE {
|
||||||
self.set_wm_properties(state);
|
self.set_wm_properties(state)?;
|
||||||
} else if event.atom == state.atoms._GTK_EDGE_CONSTRAINTS {
|
} else if event.atom == state.atoms._GTK_EDGE_CONSTRAINTS {
|
||||||
self.set_edge_constraints(state);
|
self.set_edge_constraints(state)?;
|
||||||
}
|
}
|
||||||
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn set_edge_constraints(&self, mut state: std::cell::RefMut<X11WindowState>) {
|
fn set_edge_constraints(
|
||||||
let reply = self
|
&self,
|
||||||
.xcb_connection
|
mut state: std::cell::RefMut<X11WindowState>,
|
||||||
.get_property(
|
) -> anyhow::Result<()> {
|
||||||
|
let reply = get_reply(
|
||||||
|
|| "X11 GetProperty for _GTK_EDGE_CONSTRAINTS failed.",
|
||||||
|
self.xcb.get_property(
|
||||||
false,
|
false,
|
||||||
self.x_window,
|
self.x_window,
|
||||||
state.atoms._GTK_EDGE_CONSTRAINTS,
|
state.atoms._GTK_EDGE_CONSTRAINTS,
|
||||||
xproto::AtomEnum::CARDINAL,
|
xproto::AtomEnum::CARDINAL,
|
||||||
0,
|
0,
|
||||||
4,
|
4,
|
||||||
)
|
),
|
||||||
.unwrap()
|
)?;
|
||||||
.reply()
|
|
||||||
.unwrap();
|
|
||||||
|
|
||||||
if reply.value_len != 0 {
|
if reply.value_len != 0 {
|
||||||
let atom = u32::from_ne_bytes(reply.value[0..4].try_into().unwrap());
|
let atom = u32::from_ne_bytes(reply.value[0..4].try_into().unwrap());
|
||||||
let edge_constraints = EdgeConstraints::from_atom(atom);
|
let edge_constraints = EdgeConstraints::from_atom(atom);
|
||||||
state.edge_constraints.replace(edge_constraints);
|
state.edge_constraints.replace(edge_constraints);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn set_wm_properties(&self, mut state: std::cell::RefMut<X11WindowState>) {
|
fn set_wm_properties(
|
||||||
let reply = self
|
&self,
|
||||||
.xcb_connection
|
mut state: std::cell::RefMut<X11WindowState>,
|
||||||
.get_property(
|
) -> anyhow::Result<()> {
|
||||||
|
let reply = get_reply(
|
||||||
|
|| "X11 GetProperty for _NET_WM_STATE failed.",
|
||||||
|
self.xcb.get_property(
|
||||||
false,
|
false,
|
||||||
self.x_window,
|
self.x_window,
|
||||||
state.atoms._NET_WM_STATE,
|
state.atoms._NET_WM_STATE,
|
||||||
xproto::AtomEnum::ATOM,
|
xproto::AtomEnum::ATOM,
|
||||||
0,
|
0,
|
||||||
u32::MAX,
|
u32::MAX,
|
||||||
)
|
),
|
||||||
.unwrap()
|
)?;
|
||||||
.reply()
|
|
||||||
.unwrap();
|
|
||||||
|
|
||||||
let atoms = reply
|
let atoms = reply
|
||||||
.value
|
.value
|
||||||
|
@ -821,6 +920,8 @@ impl X11WindowStatePtr {
|
||||||
state.hidden = true;
|
state.hidden = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn close(&self) {
|
pub fn close(&self) {
|
||||||
|
@ -912,7 +1013,7 @@ impl X11WindowStatePtr {
|
||||||
bounds
|
bounds
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn configure(&self, bounds: Bounds<i32>) {
|
pub fn configure(&self, bounds: Bounds<i32>) -> anyhow::Result<()> {
|
||||||
let mut resize_args = None;
|
let mut resize_args = None;
|
||||||
let is_resize;
|
let is_resize;
|
||||||
{
|
{
|
||||||
|
@ -930,7 +1031,7 @@ impl X11WindowStatePtr {
|
||||||
state.bounds = bounds;
|
state.bounds = bounds;
|
||||||
}
|
}
|
||||||
|
|
||||||
let gpu_size = query_render_extent(&self.xcb_connection, self.x_window);
|
let gpu_size = query_render_extent(&self.xcb, self.x_window)?;
|
||||||
if true {
|
if true {
|
||||||
state.renderer.update_drawable_size(size(
|
state.renderer.update_drawable_size(size(
|
||||||
DevicePixels(gpu_size.width as i32),
|
DevicePixels(gpu_size.width as i32),
|
||||||
|
@ -939,7 +1040,10 @@ impl X11WindowStatePtr {
|
||||||
resize_args = Some((state.content_size(), state.scale_factor));
|
resize_args = Some((state.content_size(), state.scale_factor));
|
||||||
}
|
}
|
||||||
if let Some(value) = state.last_sync_counter.take() {
|
if let Some(value) = state.last_sync_counter.take() {
|
||||||
sync::set_counter(&self.xcb_connection, state.counter_id, value).unwrap();
|
check_reply(
|
||||||
|
|| "X11 sync SetCounter failed.",
|
||||||
|
sync::set_counter(&self.xcb, state.counter_id, value),
|
||||||
|
)?;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -951,9 +1055,11 @@ impl X11WindowStatePtr {
|
||||||
}
|
}
|
||||||
if !is_resize {
|
if !is_resize {
|
||||||
if let Some(ref mut fun) = callbacks.moved {
|
if let Some(ref mut fun) = callbacks.moved {
|
||||||
fun()
|
fun();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn set_active(&self, focus: bool) {
|
pub fn set_active(&self, focus: bool) {
|
||||||
|
@ -1025,12 +1131,10 @@ impl PlatformWindow for X11Window {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn mouse_position(&self) -> Point<Pixels> {
|
fn mouse_position(&self) -> Point<Pixels> {
|
||||||
let reply = self
|
let reply = get_reply(
|
||||||
.0
|
|| "X11 QueryPointer failed.",
|
||||||
.xcb_connection
|
self.0.xcb.query_pointer(self.0.x_window),
|
||||||
.query_pointer(self.0.x_window)
|
)
|
||||||
.unwrap()
|
|
||||||
.reply()
|
|
||||||
.unwrap();
|
.unwrap();
|
||||||
Point::new((reply.root_x as u32).into(), (reply.root_y as u32).into())
|
Point::new((reply.root_x as u32).into(), (reply.root_y as u32).into())
|
||||||
}
|
}
|
||||||
|
@ -1073,7 +1177,7 @@ impl PlatformWindow for X11Window {
|
||||||
data,
|
data,
|
||||||
);
|
);
|
||||||
self.0
|
self.0
|
||||||
.xcb_connection
|
.xcb
|
||||||
.send_event(
|
.send_event(
|
||||||
false,
|
false,
|
||||||
self.0.state.borrow().x_root_window,
|
self.0.state.borrow().x_root_window,
|
||||||
|
@ -1082,14 +1186,14 @@ impl PlatformWindow for X11Window {
|
||||||
)
|
)
|
||||||
.log_err();
|
.log_err();
|
||||||
self.0
|
self.0
|
||||||
.xcb_connection
|
.xcb
|
||||||
.set_input_focus(
|
.set_input_focus(
|
||||||
xproto::InputFocus::POINTER_ROOT,
|
xproto::InputFocus::POINTER_ROOT,
|
||||||
self.0.x_window,
|
self.0.x_window,
|
||||||
xproto::Time::CURRENT_TIME,
|
xproto::Time::CURRENT_TIME,
|
||||||
)
|
)
|
||||||
.log_err();
|
.log_err();
|
||||||
self.0.xcb_connection.flush().unwrap();
|
self.flush().unwrap();
|
||||||
}
|
}
|
||||||
|
|
||||||
fn is_active(&self) -> bool {
|
fn is_active(&self) -> bool {
|
||||||
|
@ -1101,28 +1205,30 @@ impl PlatformWindow for X11Window {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn set_title(&mut self, title: &str) {
|
fn set_title(&mut self, title: &str) {
|
||||||
self.0
|
check_reply(
|
||||||
.xcb_connection
|
|| "X11 ChangeProperty8 on WM_NAME failed.",
|
||||||
.change_property8(
|
self.0.xcb.change_property8(
|
||||||
xproto::PropMode::REPLACE,
|
xproto::PropMode::REPLACE,
|
||||||
self.0.x_window,
|
self.0.x_window,
|
||||||
xproto::AtomEnum::WM_NAME,
|
xproto::AtomEnum::WM_NAME,
|
||||||
xproto::AtomEnum::STRING,
|
xproto::AtomEnum::STRING,
|
||||||
title.as_bytes(),
|
title.as_bytes(),
|
||||||
|
),
|
||||||
)
|
)
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
self.0
|
check_reply(
|
||||||
.xcb_connection
|
|| "X11 ChangeProperty8 on _NET_WM_NAME failed.",
|
||||||
.change_property8(
|
self.0.xcb.change_property8(
|
||||||
xproto::PropMode::REPLACE,
|
xproto::PropMode::REPLACE,
|
||||||
self.0.x_window,
|
self.0.x_window,
|
||||||
self.0.state.borrow().atoms._NET_WM_NAME,
|
self.0.state.borrow().atoms._NET_WM_NAME,
|
||||||
self.0.state.borrow().atoms.UTF8_STRING,
|
self.0.state.borrow().atoms.UTF8_STRING,
|
||||||
title.as_bytes(),
|
title.as_bytes(),
|
||||||
|
),
|
||||||
)
|
)
|
||||||
.unwrap();
|
.unwrap();
|
||||||
self.0.xcb_connection.flush().unwrap();
|
self.flush().unwrap();
|
||||||
}
|
}
|
||||||
|
|
||||||
fn set_app_id(&mut self, app_id: &str) {
|
fn set_app_id(&mut self, app_id: &str) {
|
||||||
|
@ -1131,17 +1237,16 @@ impl PlatformWindow for X11Window {
|
||||||
data.push(b'\0');
|
data.push(b'\0');
|
||||||
data.extend(app_id.bytes()); // class
|
data.extend(app_id.bytes()); // class
|
||||||
|
|
||||||
self.0
|
check_reply(
|
||||||
.xcb_connection
|
|| "X11 ChangeProperty8 for WM_CLASS failed.",
|
||||||
.change_property8(
|
self.0.xcb.change_property8(
|
||||||
xproto::PropMode::REPLACE,
|
xproto::PropMode::REPLACE,
|
||||||
self.0.x_window,
|
self.0.x_window,
|
||||||
xproto::AtomEnum::WM_CLASS,
|
xproto::AtomEnum::WM_CLASS,
|
||||||
xproto::AtomEnum::STRING,
|
xproto::AtomEnum::STRING,
|
||||||
&data,
|
&data,
|
||||||
|
),
|
||||||
)
|
)
|
||||||
.unwrap()
|
|
||||||
.check()
|
|
||||||
.unwrap();
|
.unwrap();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1169,35 +1274,38 @@ impl PlatformWindow for X11Window {
|
||||||
state.atoms.WM_CHANGE_STATE,
|
state.atoms.WM_CHANGE_STATE,
|
||||||
[WINDOW_ICONIC_STATE, 0, 0, 0, 0],
|
[WINDOW_ICONIC_STATE, 0, 0, 0, 0],
|
||||||
);
|
);
|
||||||
self.0
|
check_reply(
|
||||||
.xcb_connection
|
|| "X11 SendEvent to minimize window failed.",
|
||||||
.send_event(
|
self.0.xcb.send_event(
|
||||||
false,
|
false,
|
||||||
state.x_root_window,
|
state.x_root_window,
|
||||||
EventMask::SUBSTRUCTURE_REDIRECT | EventMask::SUBSTRUCTURE_NOTIFY,
|
EventMask::SUBSTRUCTURE_REDIRECT | EventMask::SUBSTRUCTURE_NOTIFY,
|
||||||
message,
|
message,
|
||||||
|
),
|
||||||
)
|
)
|
||||||
.unwrap()
|
|
||||||
.check()
|
|
||||||
.unwrap();
|
.unwrap();
|
||||||
}
|
}
|
||||||
|
|
||||||
fn zoom(&self) {
|
fn zoom(&self) {
|
||||||
let state = self.0.state.borrow();
|
let state = self.0.state.borrow();
|
||||||
self.set_wm_hints(
|
self.set_wm_hints(
|
||||||
|
|| "X11 SendEvent to maximize a window failed.",
|
||||||
WmHintPropertyState::Toggle,
|
WmHintPropertyState::Toggle,
|
||||||
state.atoms._NET_WM_STATE_MAXIMIZED_VERT,
|
state.atoms._NET_WM_STATE_MAXIMIZED_VERT,
|
||||||
state.atoms._NET_WM_STATE_MAXIMIZED_HORZ,
|
state.atoms._NET_WM_STATE_MAXIMIZED_HORZ,
|
||||||
);
|
)
|
||||||
|
.unwrap();
|
||||||
}
|
}
|
||||||
|
|
||||||
fn toggle_fullscreen(&self) {
|
fn toggle_fullscreen(&self) {
|
||||||
let state = self.0.state.borrow();
|
let state = self.0.state.borrow();
|
||||||
self.set_wm_hints(
|
self.set_wm_hints(
|
||||||
|
|| "X11 SendEvent to fullscreen a window failed.",
|
||||||
WmHintPropertyState::Toggle,
|
WmHintPropertyState::Toggle,
|
||||||
state.atoms._NET_WM_STATE_FULLSCREEN,
|
state.atoms._NET_WM_STATE_FULLSCREEN,
|
||||||
xproto::AtomEnum::NONE.into(),
|
xproto::AtomEnum::NONE.into(),
|
||||||
);
|
)
|
||||||
|
.unwrap();
|
||||||
}
|
}
|
||||||
|
|
||||||
fn is_fullscreen(&self) -> bool {
|
fn is_fullscreen(&self) -> bool {
|
||||||
|
@ -1253,14 +1361,13 @@ impl PlatformWindow for X11Window {
|
||||||
fn show_window_menu(&self, position: Point<Pixels>) {
|
fn show_window_menu(&self, position: Point<Pixels>) {
|
||||||
let state = self.0.state.borrow();
|
let state = self.0.state.borrow();
|
||||||
|
|
||||||
self.0
|
check_reply(
|
||||||
.xcb_connection
|
|| "X11 UngrabPointer failed.",
|
||||||
.ungrab_pointer(x11rb::CURRENT_TIME)
|
self.0.xcb.ungrab_pointer(x11rb::CURRENT_TIME),
|
||||||
.unwrap()
|
)
|
||||||
.check()
|
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
let coords = self.get_root_position(position);
|
let coords = self.get_root_position(position).unwrap();
|
||||||
let message = ClientMessageEvent::new(
|
let message = ClientMessageEvent::new(
|
||||||
32,
|
32,
|
||||||
self.0.x_window,
|
self.0.x_window,
|
||||||
|
@ -1273,26 +1380,25 @@ impl PlatformWindow for X11Window {
|
||||||
0,
|
0,
|
||||||
],
|
],
|
||||||
);
|
);
|
||||||
self.0
|
check_reply(
|
||||||
.xcb_connection
|
|| "X11 SendEvent to show window menu failed.",
|
||||||
.send_event(
|
self.0.xcb.send_event(
|
||||||
false,
|
false,
|
||||||
state.x_root_window,
|
state.x_root_window,
|
||||||
EventMask::SUBSTRUCTURE_REDIRECT | EventMask::SUBSTRUCTURE_NOTIFY,
|
EventMask::SUBSTRUCTURE_REDIRECT | EventMask::SUBSTRUCTURE_NOTIFY,
|
||||||
message,
|
message,
|
||||||
|
),
|
||||||
)
|
)
|
||||||
.unwrap()
|
|
||||||
.check()
|
|
||||||
.unwrap();
|
.unwrap();
|
||||||
}
|
}
|
||||||
|
|
||||||
fn start_window_move(&self) {
|
fn start_window_move(&self) {
|
||||||
const MOVERESIZE_MOVE: u32 = 8;
|
const MOVERESIZE_MOVE: u32 = 8;
|
||||||
self.send_moveresize(MOVERESIZE_MOVE);
|
self.send_moveresize(MOVERESIZE_MOVE).unwrap();
|
||||||
}
|
}
|
||||||
|
|
||||||
fn start_window_resize(&self, edge: ResizeEdge) {
|
fn start_window_resize(&self, edge: ResizeEdge) {
|
||||||
self.send_moveresize(edge.to_moveresize());
|
self.send_moveresize(edge.to_moveresize()).unwrap();
|
||||||
}
|
}
|
||||||
|
|
||||||
fn window_decorations(&self) -> crate::Decorations {
|
fn window_decorations(&self) -> crate::Decorations {
|
||||||
|
@ -1355,9 +1461,9 @@ impl PlatformWindow for X11Window {
|
||||||
if state.last_insets != insets {
|
if state.last_insets != insets {
|
||||||
state.last_insets = insets;
|
state.last_insets = insets;
|
||||||
|
|
||||||
self.0
|
check_reply(
|
||||||
.xcb_connection
|
|| "X11 ChangeProperty for _GTK_FRAME_EXTENTS failed.",
|
||||||
.change_property(
|
self.0.xcb.change_property(
|
||||||
xproto::PropMode::REPLACE,
|
xproto::PropMode::REPLACE,
|
||||||
self.0.x_window,
|
self.0.x_window,
|
||||||
state.atoms._GTK_FRAME_EXTENTS,
|
state.atoms._GTK_FRAME_EXTENTS,
|
||||||
|
@ -1365,9 +1471,8 @@ impl PlatformWindow for X11Window {
|
||||||
size_of::<u32>() as u8 * 8,
|
size_of::<u32>() as u8 * 8,
|
||||||
4,
|
4,
|
||||||
bytemuck::cast_slice::<u32, u8>(&insets),
|
bytemuck::cast_slice::<u32, u8>(&insets),
|
||||||
|
),
|
||||||
)
|
)
|
||||||
.unwrap()
|
|
||||||
.check()
|
|
||||||
.unwrap();
|
.unwrap();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1390,19 +1495,18 @@ impl PlatformWindow for X11Window {
|
||||||
WindowDecorations::Client => [1 << 1, 0, 0, 0, 0],
|
WindowDecorations::Client => [1 << 1, 0, 0, 0, 0],
|
||||||
};
|
};
|
||||||
|
|
||||||
self.0
|
check_reply(
|
||||||
.xcb_connection
|
|| "X11 ChangeProperty for _MOTIF_WM_HINTS failed.",
|
||||||
.change_property(
|
self.0.xcb.change_property(
|
||||||
xproto::PropMode::REPLACE,
|
xproto::PropMode::REPLACE,
|
||||||
self.0.x_window,
|
self.0.x_window,
|
||||||
state.atoms._MOTIF_WM_HINTS,
|
state.atoms._MOTIF_WM_HINTS,
|
||||||
state.atoms._MOTIF_WM_HINTS,
|
state.atoms._MOTIF_WM_HINTS,
|
||||||
std::mem::size_of::<u32>() as u8 * 8,
|
size_of::<u32>() as u8 * 8,
|
||||||
5,
|
5,
|
||||||
bytemuck::cast_slice::<u32, u8>(&hints_data),
|
bytemuck::cast_slice::<u32, u8>(&hints_data),
|
||||||
|
),
|
||||||
)
|
)
|
||||||
.unwrap()
|
|
||||||
.check()
|
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
match decorations {
|
match decorations {
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue