mirror of
https://github.com/italicsjenga/winit-sonoma-fix.git
synced 2024-12-23 05:41:31 +11:00
Implement X11 extensions using x11rb instead of Xlib
Removes Xlib code by replacing it with the x11rb equivalent, the commit handles xrandr, xinput, xinput2, and xkb. Signed-off-by: John Nunley <dev@notgull.net>
This commit is contained in:
parent
0c8cf94a70
commit
bb9b629bc3
|
@ -152,7 +152,7 @@ wayland-protocols = { version = "0.30.0", features = [ "staging"], optional = tr
|
|||
calloop = "0.10.5"
|
||||
rustix = { version = "0.38.4", default-features = false, features = ["std", "system", "thread", "process"] }
|
||||
x11-dl = { version = "2.18.5", optional = true }
|
||||
x11rb = { version = "0.12.0", default-features = false, features = ["allow-unsafe-code", "dl-libxcb", "xinput", "xkb"], optional = true }
|
||||
x11rb = { version = "0.12.0", default-features = false, features = ["allow-unsafe-code", "dl-libxcb", "randr", "resource_manager", "xinput", "xkb"], optional = true }
|
||||
xkbcommon-dl = "0.4.0"
|
||||
memmap2 = { version = "0.5.0", optional = true }
|
||||
|
||||
|
|
|
@ -851,6 +851,7 @@ impl<T> EventLoopWindowTarget<T> {
|
|||
.x_connection()
|
||||
.available_monitors()
|
||||
.into_iter()
|
||||
.flatten()
|
||||
.map(MonitorHandle::X)
|
||||
.collect(),
|
||||
}
|
||||
|
@ -863,7 +864,7 @@ impl<T> EventLoopWindowTarget<T> {
|
|||
EventLoopWindowTarget::Wayland(ref evlp) => evlp.primary_monitor(),
|
||||
#[cfg(x11_platform)]
|
||||
EventLoopWindowTarget::X(ref evlp) => {
|
||||
let primary_monitor = MonitorHandle::X(evlp.x_connection().primary_monitor());
|
||||
let primary_monitor = MonitorHandle::X(evlp.x_connection().primary_monitor().ok()?);
|
||||
Some(primary_monitor)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -7,13 +7,18 @@ use std::{
|
|||
sync::{Arc, Mutex},
|
||||
};
|
||||
|
||||
use x11rb::protocol::xproto::{self, ConnectionExt as _};
|
||||
use x11rb::x11_utils::Serialize;
|
||||
use x11rb::{
|
||||
protocol::{
|
||||
xinput,
|
||||
xproto::{self, ConnectionExt as _},
|
||||
},
|
||||
x11_utils::ExtensionInformation,
|
||||
};
|
||||
|
||||
use super::{
|
||||
atoms::*, ffi, get_xtarget, mkdid, mkwid, monitor, util, CookieResultExt, Device, DeviceId,
|
||||
DeviceInfo, Dnd, DndState, GenericEventCookie, ImeReceiver, ScrollOrientation, UnownedWindow,
|
||||
WindowId, XExtension,
|
||||
atoms::*, ffi, get_xtarget, mkdid, mkwid, util, CookieResultExt, Device, DeviceId, DeviceInfo,
|
||||
Dnd, DndState, GenericEventCookie, ImeReceiver, ScrollOrientation, UnownedWindow, WindowId,
|
||||
};
|
||||
|
||||
use crate::{
|
||||
|
@ -35,10 +40,10 @@ pub(super) struct EventProcessor<T: 'static> {
|
|||
pub(super) dnd: Dnd,
|
||||
pub(super) ime_receiver: ImeReceiver,
|
||||
pub(super) ime_event_receiver: ImeEventReceiver,
|
||||
pub(super) randr_event_offset: c_int,
|
||||
pub(super) randr_event_offset: u8,
|
||||
pub(super) devices: RefCell<HashMap<DeviceId, Device>>,
|
||||
pub(super) xi2ext: XExtension,
|
||||
pub(super) xkbext: XExtension,
|
||||
pub(super) xi2ext: ExtensionInformation,
|
||||
pub(super) xkbext: ExtensionInformation,
|
||||
pub(super) target: Rc<RootELW<T>>,
|
||||
pub(super) kb_state: KbdState,
|
||||
// Number of touch events currently in progress
|
||||
|
@ -55,12 +60,12 @@ pub(super) struct EventProcessor<T: 'static> {
|
|||
}
|
||||
|
||||
impl<T: 'static> EventProcessor<T> {
|
||||
pub(super) fn init_device(&self, device: c_int) {
|
||||
pub(super) fn init_device(&self, device: xinput::DeviceId) {
|
||||
let wt = get_xtarget(&self.target);
|
||||
let mut devices = self.devices.borrow_mut();
|
||||
if let Some(info) = DeviceInfo::get(&wt.xconn, device) {
|
||||
if let Some(info) = DeviceInfo::get(&wt.xconn, device as _) {
|
||||
for info in info.iter() {
|
||||
devices.insert(DeviceId(info.deviceid), Device::new(info));
|
||||
devices.insert(DeviceId(info.deviceid as _), Device::new(info));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -420,7 +425,10 @@ impl<T: 'static> EventProcessor<T> {
|
|||
let last_scale_factor = shared_state_lock.last_monitor.scale_factor;
|
||||
let new_scale_factor = {
|
||||
let window_rect = util::AaRect::new(new_outer_position, new_inner_size);
|
||||
let monitor = wt.xconn.get_monitor_for_window(Some(window_rect));
|
||||
let monitor = wt
|
||||
.xconn
|
||||
.get_monitor_for_window(Some(window_rect))
|
||||
.expect("Failed to find monitor for window");
|
||||
|
||||
if monitor.is_dummy() {
|
||||
// Avoid updating monitor using a dummy monitor handle
|
||||
|
@ -596,7 +604,7 @@ impl<T: 'static> EventProcessor<T> {
|
|||
};
|
||||
|
||||
let window_id = mkwid(window);
|
||||
let device_id = mkdid(util::VIRTUAL_CORE_KEYBOARD.into());
|
||||
let device_id = mkdid(util::VIRTUAL_CORE_KEYBOARD);
|
||||
|
||||
let keycode = xkev.keycode as _;
|
||||
|
||||
|
@ -672,7 +680,7 @@ impl<T: 'static> EventProcessor<T> {
|
|||
return;
|
||||
};
|
||||
let xev = &guard.cookie;
|
||||
if self.xi2ext.opcode != xev.extension {
|
||||
if self.xi2ext.major_opcode != xev.extension as u8 {
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -691,7 +699,7 @@ impl<T: 'static> EventProcessor<T> {
|
|||
ffi::XI_ButtonPress | ffi::XI_ButtonRelease => {
|
||||
let xev: &ffi::XIDeviceEvent = unsafe { &*(xev.data as *const _) };
|
||||
let window_id = mkwid(xev.event as xproto::Window);
|
||||
let device_id = mkdid(xev.deviceid);
|
||||
let device_id = mkdid(xev.deviceid as xinput::DeviceId);
|
||||
|
||||
// Set the timestamp.
|
||||
wt.xconn.set_timestamp(xev.time as xproto::Timestamp);
|
||||
|
@ -787,7 +795,7 @@ impl<T: 'static> EventProcessor<T> {
|
|||
// Set the timestamp.
|
||||
wt.xconn.set_timestamp(xev.time as xproto::Timestamp);
|
||||
|
||||
let device_id = mkdid(xev.deviceid);
|
||||
let device_id = mkdid(xev.deviceid as xinput::DeviceId);
|
||||
let window = xev.event as xproto::Window;
|
||||
let window_id = mkwid(window);
|
||||
let new_cursor_pos = (xev.event_x, xev.event_y);
|
||||
|
@ -820,7 +828,9 @@ impl<T: 'static> EventProcessor<T> {
|
|||
)
|
||||
};
|
||||
let mut devices = self.devices.borrow_mut();
|
||||
let physical_device = match devices.get_mut(&DeviceId(xev.sourceid)) {
|
||||
let physical_device = match devices
|
||||
.get_mut(&DeviceId(xev.sourceid as xinput::DeviceId))
|
||||
{
|
||||
Some(device) => device,
|
||||
None => return,
|
||||
};
|
||||
|
@ -832,7 +842,7 @@ impl<T: 'static> EventProcessor<T> {
|
|||
if let Some(&mut (_, ref mut info)) = physical_device
|
||||
.scroll_axes
|
||||
.iter_mut()
|
||||
.find(|&&mut (axis, _)| axis == i)
|
||||
.find(|&&mut (axis, _)| axis == i as _)
|
||||
{
|
||||
let delta = (x - info.position) / info.increment;
|
||||
info.position = x;
|
||||
|
@ -879,9 +889,11 @@ impl<T: 'static> EventProcessor<T> {
|
|||
|
||||
let window = xev.event as xproto::Window;
|
||||
let window_id = mkwid(window);
|
||||
let device_id = mkdid(xev.deviceid);
|
||||
let device_id = mkdid(xev.deviceid as xinput::DeviceId);
|
||||
|
||||
if let Some(all_info) = DeviceInfo::get(&wt.xconn, ffi::XIAllDevices) {
|
||||
if let Some(all_info) =
|
||||
DeviceInfo::get(&wt.xconn, super::ALL_DEVICES.into())
|
||||
{
|
||||
let mut devices = self.devices.borrow_mut();
|
||||
for device_info in all_info.iter() {
|
||||
if device_info.deviceid == xev.sourceid
|
||||
|
@ -891,7 +903,7 @@ impl<T: 'static> EventProcessor<T> {
|
|||
// the virtual device.
|
||||
|| device_info.attachment == xev.sourceid
|
||||
{
|
||||
let device_id = DeviceId(device_info.deviceid);
|
||||
let device_id = DeviceId(device_info.deviceid as _);
|
||||
if let Some(device) = devices.get_mut(&device_id) {
|
||||
device.reset_scroll_position(device_info);
|
||||
}
|
||||
|
@ -930,7 +942,7 @@ impl<T: 'static> EventProcessor<T> {
|
|||
callback(Event::WindowEvent {
|
||||
window_id: mkwid(window),
|
||||
event: CursorLeft {
|
||||
device_id: mkdid(xev.deviceid),
|
||||
device_id: mkdid(xev.deviceid as xinput::DeviceId),
|
||||
},
|
||||
});
|
||||
}
|
||||
|
@ -978,14 +990,14 @@ impl<T: 'static> EventProcessor<T> {
|
|||
let pointer_id = self
|
||||
.devices
|
||||
.borrow()
|
||||
.get(&DeviceId(xev.deviceid))
|
||||
.get(&DeviceId(xev.deviceid as xinput::DeviceId))
|
||||
.map(|device| device.attachment)
|
||||
.unwrap_or(2);
|
||||
|
||||
callback(Event::WindowEvent {
|
||||
window_id,
|
||||
event: CursorMoved {
|
||||
device_id: mkdid(pointer_id),
|
||||
device_id: mkdid(pointer_id as _),
|
||||
position,
|
||||
},
|
||||
});
|
||||
|
@ -1076,7 +1088,7 @@ impl<T: 'static> EventProcessor<T> {
|
|||
callback(Event::WindowEvent {
|
||||
window_id,
|
||||
event: WindowEvent::CursorMoved {
|
||||
device_id: mkdid(util::VIRTUAL_CORE_POINTER.into()),
|
||||
device_id: mkdid(util::VIRTUAL_CORE_POINTER),
|
||||
position: location.cast(),
|
||||
},
|
||||
});
|
||||
|
@ -1085,7 +1097,7 @@ impl<T: 'static> EventProcessor<T> {
|
|||
callback(Event::WindowEvent {
|
||||
window_id,
|
||||
event: WindowEvent::Touch(Touch {
|
||||
device_id: mkdid(xev.deviceid),
|
||||
device_id: mkdid(xev.deviceid as xinput::DeviceId),
|
||||
phase,
|
||||
location,
|
||||
force: None, // TODO
|
||||
|
@ -1103,7 +1115,7 @@ impl<T: 'static> EventProcessor<T> {
|
|||
|
||||
if xev.flags & ffi::XIPointerEmulated == 0 {
|
||||
callback(Event::DeviceEvent {
|
||||
device_id: mkdid(xev.deviceid),
|
||||
device_id: mkdid(xev.deviceid as xinput::DeviceId),
|
||||
event: DeviceEvent::Button {
|
||||
button: xev.detail as u32,
|
||||
state: match xev.evtype {
|
||||
|
@ -1122,7 +1134,7 @@ impl<T: 'static> EventProcessor<T> {
|
|||
// Set the timestamp.
|
||||
wt.xconn.set_timestamp(xev.time as xproto::Timestamp);
|
||||
|
||||
let did = mkdid(xev.deviceid);
|
||||
let did = mkdid(xev.deviceid as xinput::DeviceId);
|
||||
|
||||
let mask = unsafe {
|
||||
slice::from_raw_parts(
|
||||
|
@ -1182,7 +1194,7 @@ impl<T: 'static> EventProcessor<T> {
|
|||
_ => unreachable!(),
|
||||
};
|
||||
|
||||
let device_id = mkdid(xev.sourceid);
|
||||
let device_id = mkdid(xev.sourceid as xinput::DeviceId);
|
||||
let keycode = xev.detail as u32;
|
||||
if keycode < KEYCODE_OFFSET as u32 {
|
||||
return;
|
||||
|
@ -1208,19 +1220,19 @@ impl<T: 'static> EventProcessor<T> {
|
|||
unsafe { slice::from_raw_parts(xev.info, xev.num_info as usize) }
|
||||
{
|
||||
if 0 != info.flags & (ffi::XISlaveAdded | ffi::XIMasterAdded) {
|
||||
self.init_device(info.deviceid);
|
||||
self.init_device(info.deviceid as xinput::DeviceId);
|
||||
callback(Event::DeviceEvent {
|
||||
device_id: mkdid(info.deviceid),
|
||||
device_id: mkdid(info.deviceid as xinput::DeviceId),
|
||||
event: DeviceEvent::Added,
|
||||
});
|
||||
} else if 0 != info.flags & (ffi::XISlaveRemoved | ffi::XIMasterRemoved)
|
||||
{
|
||||
callback(Event::DeviceEvent {
|
||||
device_id: mkdid(info.deviceid),
|
||||
device_id: mkdid(info.deviceid as xinput::DeviceId),
|
||||
event: DeviceEvent::Removed,
|
||||
});
|
||||
let mut devices = self.devices.borrow_mut();
|
||||
devices.remove(&DeviceId(info.deviceid));
|
||||
devices.remove(&DeviceId(info.deviceid as xinput::DeviceId));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1229,7 +1241,7 @@ impl<T: 'static> EventProcessor<T> {
|
|||
}
|
||||
}
|
||||
_ => {
|
||||
if event_type == self.xkbext.first_event_id {
|
||||
if event_type == self.xkbext.first_event as _ {
|
||||
let xev = unsafe { &*(xev as *const _ as *const ffi::XkbAnyEvent) };
|
||||
match xev.xkb_type {
|
||||
ffi::XkbNewKeyboardNotify => {
|
||||
|
@ -1285,11 +1297,14 @@ impl<T: 'static> EventProcessor<T> {
|
|||
_ => {}
|
||||
}
|
||||
}
|
||||
if event_type == self.randr_event_offset {
|
||||
if event_type == self.randr_event_offset as c_int {
|
||||
// In the future, it would be quite easy to emit monitor hotplug events.
|
||||
let prev_list = monitor::invalidate_cached_monitor_list();
|
||||
let prev_list = wt.xconn.invalidate_cached_monitor_list();
|
||||
if let Some(prev_list) = prev_list {
|
||||
let new_list = wt.xconn.available_monitors();
|
||||
let new_list = wt
|
||||
.xconn
|
||||
.available_monitors()
|
||||
.expect("Failed to get monitor list");
|
||||
for new_monitor in new_list {
|
||||
// Previous list may be empty, in case of disconnecting and
|
||||
// reconnecting the only one monitor. We still need to emit events in
|
||||
|
@ -1419,7 +1434,7 @@ impl<T: 'static> EventProcessor<T> {
|
|||
) where
|
||||
F: FnMut(Event<T>),
|
||||
{
|
||||
let device_id = mkdid(util::VIRTUAL_CORE_KEYBOARD.into());
|
||||
let device_id = mkdid(util::VIRTUAL_CORE_KEYBOARD);
|
||||
|
||||
// Update modifiers state and emit key events based on which keys are currently pressed.
|
||||
for keycode in wt
|
||||
|
|
|
@ -1,7 +1,6 @@
|
|||
use x11_dl::xmd::CARD32;
|
||||
pub use x11_dl::{
|
||||
error::OpenError, keysym::*, xcursor::*, xinput::*, xinput2::*, xlib::*, xlib_xcb::*,
|
||||
xrandr::*, xrender::*,
|
||||
};
|
||||
|
||||
// Isn't defined by x11_dl
|
||||
|
|
|
@ -36,7 +36,7 @@ use std::{
|
|||
},
|
||||
ptr,
|
||||
rc::Rc,
|
||||
slice,
|
||||
slice, str,
|
||||
sync::mpsc::{Receiver, Sender, TryRecvError},
|
||||
sync::{mpsc, Arc, Weak},
|
||||
time::{Duration, Instant},
|
||||
|
@ -47,11 +47,15 @@ use libc::{self, setlocale, LC_CTYPE};
|
|||
use atoms::*;
|
||||
use raw_window_handle::{RawDisplayHandle, XlibDisplayHandle};
|
||||
|
||||
use x11rb::protocol::{
|
||||
xinput,
|
||||
xproto::{self, ConnectionExt},
|
||||
};
|
||||
use x11rb::x11_utils::X11Error as LogicalError;
|
||||
use x11rb::{
|
||||
connection::RequestConnection,
|
||||
protocol::{
|
||||
xinput::{self, ConnectionExt as _},
|
||||
xkb,
|
||||
xproto::{self, ConnectionExt as _},
|
||||
},
|
||||
};
|
||||
use x11rb::{
|
||||
errors::{ConnectError, ConnectionError, IdsExhausted, ReplyError},
|
||||
xcb_ffi::ReplyOrIdError,
|
||||
|
@ -75,6 +79,10 @@ use crate::{
|
|||
window::WindowAttributes,
|
||||
};
|
||||
|
||||
// Xinput constants not defined in x11rb
|
||||
const ALL_DEVICES: u16 = 0;
|
||||
const ALL_MASTER_DEVICES: u16 = 1;
|
||||
|
||||
type X11Source = Generic<RawFd>;
|
||||
|
||||
struct WakeSender<T> {
|
||||
|
@ -229,71 +237,27 @@ impl<T: 'static> EventLoop<T> {
|
|||
});
|
||||
|
||||
let randr_event_offset = xconn
|
||||
.select_xrandr_input(root as ffi::Window)
|
||||
.select_xrandr_input(root)
|
||||
.expect("Failed to query XRandR extension");
|
||||
|
||||
let xi2ext = unsafe {
|
||||
let mut ext = XExtension::default();
|
||||
let xi2ext = xconn
|
||||
.xcb_connection()
|
||||
.extension_information(xinput::X11_EXTENSION_NAME)
|
||||
.expect("Failed to query XInput extension")
|
||||
.expect("X server missing XInput extension");
|
||||
let xkbext = xconn
|
||||
.xcb_connection()
|
||||
.extension_information(xkb::X11_EXTENSION_NAME)
|
||||
.expect("Failed to query XKB extension")
|
||||
.expect("X server missing XKB extension");
|
||||
|
||||
let res = (xconn.xlib.XQueryExtension)(
|
||||
xconn.display,
|
||||
b"XInputExtension\0".as_ptr() as *const c_char,
|
||||
&mut ext.opcode,
|
||||
&mut ext.first_event_id,
|
||||
&mut ext.first_error_id,
|
||||
);
|
||||
|
||||
if res == ffi::False {
|
||||
panic!("X server missing XInput extension");
|
||||
}
|
||||
|
||||
ext
|
||||
};
|
||||
|
||||
let xkbext = {
|
||||
let mut ext = XExtension::default();
|
||||
|
||||
let res = unsafe {
|
||||
(xconn.xlib.XkbQueryExtension)(
|
||||
xconn.display,
|
||||
&mut ext.opcode,
|
||||
&mut ext.first_event_id,
|
||||
&mut ext.first_error_id,
|
||||
&mut 1,
|
||||
&mut 0,
|
||||
)
|
||||
};
|
||||
|
||||
if res == ffi::False {
|
||||
panic!("X server missing XKB extension");
|
||||
}
|
||||
|
||||
// Enable detectable auto repeat.
|
||||
let mut supported = 0;
|
||||
unsafe {
|
||||
(xconn.xlib.XkbSetDetectableAutoRepeat)(xconn.display, 1, &mut supported);
|
||||
}
|
||||
if supported == 0 {
|
||||
warn!("Detectable auto repeart is not supported");
|
||||
}
|
||||
|
||||
ext
|
||||
};
|
||||
|
||||
unsafe {
|
||||
let mut xinput_major_ver = ffi::XI_2_Major;
|
||||
let mut xinput_minor_ver = ffi::XI_2_Minor;
|
||||
if (xconn.xinput2.XIQueryVersion)(
|
||||
xconn.display,
|
||||
&mut xinput_major_ver,
|
||||
&mut xinput_minor_ver,
|
||||
) != ffi::Success as std::os::raw::c_int
|
||||
{
|
||||
panic!(
|
||||
"X server has XInput extension {xinput_major_ver}.{xinput_minor_ver} but does not support XInput2",
|
||||
);
|
||||
}
|
||||
}
|
||||
// Check for XInput2 support.
|
||||
xconn
|
||||
.xcb_connection()
|
||||
.xinput_xi_query_version(2, 3)
|
||||
.expect("Failed to send XInput2 query version request")
|
||||
.reply()
|
||||
.expect("Error while checking for XInput2 query version reply");
|
||||
|
||||
xconn.update_cached_wm_info(root);
|
||||
|
||||
|
@ -387,7 +351,7 @@ impl<T: 'static> EventLoop<T> {
|
|||
.xconn
|
||||
.select_xinput_events(
|
||||
root,
|
||||
ffi::XIAllDevices as _,
|
||||
ALL_DEVICES,
|
||||
x11rb::protocol::xinput::XIEventMask::HIERARCHY,
|
||||
)
|
||||
.expect_then_ignore_error("Failed to register for XInput2 device hotplug events");
|
||||
|
@ -396,11 +360,11 @@ impl<T: 'static> EventLoop<T> {
|
|||
.xconn
|
||||
.select_xkb_events(
|
||||
0x100, // Use the "core keyboard device"
|
||||
ffi::XkbNewKeyboardNotifyMask | ffi::XkbStateNotifyMask,
|
||||
xkb::EventType::NEW_KEYBOARD_NOTIFY | xkb::EventType::STATE_NOTIFY,
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
event_processor.init_device(ffi::XIAllDevices);
|
||||
event_processor.init_device(ALL_DEVICES);
|
||||
|
||||
EventLoop {
|
||||
loop_running: false,
|
||||
|
@ -761,7 +725,7 @@ impl<T> EventLoopWindowTarget<T> {
|
|||
}
|
||||
|
||||
self.xconn
|
||||
.select_xinput_events(self.root, ffi::XIAllMasterDevices as _, mask)
|
||||
.select_xinput_events(self.root, ALL_MASTER_DEVICES, mask)
|
||||
.expect_then_ignore_error("Failed to update device event filter");
|
||||
}
|
||||
|
||||
|
@ -822,7 +786,7 @@ impl<'a> Deref for DeviceInfo<'a> {
|
|||
}
|
||||
|
||||
#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
|
||||
pub struct DeviceId(c_int);
|
||||
pub struct DeviceId(xinput::DeviceId);
|
||||
|
||||
impl DeviceId {
|
||||
#[allow(unused)]
|
||||
|
@ -894,6 +858,9 @@ pub enum X11Error {
|
|||
/// Got an invalid activation token.
|
||||
InvalidActivationToken(Vec<u8>),
|
||||
|
||||
/// An extension that we rely on is not available.
|
||||
MissingExtension(&'static str),
|
||||
|
||||
/// Could not find a matching X11 visual for this visualid
|
||||
NoSuchVisual(xproto::Visualid),
|
||||
}
|
||||
|
@ -912,6 +879,7 @@ impl fmt::Display for X11Error {
|
|||
"Invalid activation token: {}",
|
||||
std::str::from_utf8(s).unwrap_or("<invalid utf8>")
|
||||
),
|
||||
X11Error::MissingExtension(s) => write!(f, "Missing X11 extension: {}", s),
|
||||
X11Error::NoSuchVisual(visualid) => {
|
||||
write!(
|
||||
f,
|
||||
|
@ -1033,17 +1001,10 @@ impl<'a> Drop for GenericEventCookie<'a> {
|
|||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Default, Copy, Clone)]
|
||||
struct XExtension {
|
||||
opcode: c_int,
|
||||
first_event_id: c_int,
|
||||
first_error_id: c_int,
|
||||
}
|
||||
|
||||
fn mkwid(w: xproto::Window) -> crate::window::WindowId {
|
||||
crate::window::WindowId(crate::platform_impl::platform::WindowId(w as _))
|
||||
}
|
||||
fn mkdid(w: c_int) -> crate::event::DeviceId {
|
||||
fn mkdid(w: xinput::DeviceId) -> crate::event::DeviceId {
|
||||
crate::event::DeviceId(crate::platform_impl::DeviceId::X(DeviceId(w)))
|
||||
}
|
||||
|
||||
|
|
|
@ -1,29 +1,24 @@
|
|||
use std::os::raw::*;
|
||||
use std::slice;
|
||||
use std::sync::Mutex;
|
||||
|
||||
use once_cell::sync::Lazy;
|
||||
|
||||
use super::{
|
||||
ffi::{
|
||||
self, RRCrtc, RRCrtcChangeNotifyMask, RRMode, RROutputPropertyNotifyMask,
|
||||
RRScreenChangeNotifyMask, True, Window, XRRCrtcInfo, XRRModeInfo, XRRScreenResources,
|
||||
},
|
||||
util, X11Error, XConnection,
|
||||
};
|
||||
use super::{util, X11Error, XConnection};
|
||||
use crate::{
|
||||
dpi::{PhysicalPosition, PhysicalSize},
|
||||
platform_impl::{MonitorHandle as PlatformMonitorHandle, VideoMode as PlatformVideoMode},
|
||||
};
|
||||
use x11rb::{
|
||||
connection::RequestConnection,
|
||||
protocol::{
|
||||
randr::{self, ConnectionExt as _},
|
||||
xproto,
|
||||
},
|
||||
};
|
||||
|
||||
// Used for testing. This should always be committed as false.
|
||||
const DISABLE_MONITOR_LIST_CACHING: bool = false;
|
||||
|
||||
static MONITORS: Lazy<Mutex<Option<Vec<MonitorHandle>>>> = Lazy::new(Mutex::default);
|
||||
|
||||
pub fn invalidate_cached_monitor_list() -> Option<Vec<MonitorHandle>> {
|
||||
// We update this lazily.
|
||||
(*MONITORS.lock().unwrap()).take()
|
||||
impl XConnection {
|
||||
pub fn invalidate_cached_monitor_list(&self) -> Option<Vec<MonitorHandle>> {
|
||||
// We update this lazily.
|
||||
self.monitor_handles.lock().unwrap().take()
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
|
||||
|
@ -31,7 +26,7 @@ pub struct VideoMode {
|
|||
pub(crate) size: (u32, u32),
|
||||
pub(crate) bit_depth: u16,
|
||||
pub(crate) refresh_rate_millihertz: u32,
|
||||
pub(crate) native_mode: RRMode,
|
||||
pub(crate) native_mode: randr::Mode,
|
||||
pub(crate) monitor: Option<MonitorHandle>,
|
||||
}
|
||||
|
||||
|
@ -60,7 +55,7 @@ impl VideoMode {
|
|||
#[derive(Debug, Clone)]
|
||||
pub struct MonitorHandle {
|
||||
/// The actual id
|
||||
pub(crate) id: RRCrtc,
|
||||
pub(crate) id: randr::Crtc,
|
||||
/// The name of the monitor
|
||||
pub(crate) name: String,
|
||||
/// The size of the monitor
|
||||
|
@ -106,10 +101,10 @@ impl std::hash::Hash for MonitorHandle {
|
|||
}
|
||||
|
||||
#[inline]
|
||||
pub fn mode_refresh_rate_millihertz(mode: &XRRModeInfo) -> Option<u32> {
|
||||
if mode.dotClock > 0 && mode.hTotal > 0 && mode.vTotal > 0 {
|
||||
pub fn mode_refresh_rate_millihertz(mode: &randr::ModeInfo) -> Option<u32> {
|
||||
if mode.dot_clock > 0 && mode.htotal > 0 && mode.vtotal > 0 {
|
||||
#[allow(clippy::unnecessary_cast)]
|
||||
Some((mode.dotClock as u64 * 1000 / (mode.hTotal as u64 * mode.vTotal as u64)) as u32)
|
||||
Some((mode.dot_clock as u64 * 1000 / (mode.htotal as u64 * mode.vtotal as u64)) as u32)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
|
@ -118,19 +113,18 @@ pub fn mode_refresh_rate_millihertz(mode: &XRRModeInfo) -> Option<u32> {
|
|||
impl MonitorHandle {
|
||||
fn new(
|
||||
xconn: &XConnection,
|
||||
resources: *mut XRRScreenResources,
|
||||
id: RRCrtc,
|
||||
crtc: *mut XRRCrtcInfo,
|
||||
resources: &ScreenResources,
|
||||
id: randr::Crtc,
|
||||
crtc: &randr::GetCrtcInfoReply,
|
||||
primary: bool,
|
||||
) -> Option<Self> {
|
||||
let (name, scale_factor, video_modes) = unsafe { xconn.get_output_info(resources, crtc)? };
|
||||
let dimensions = unsafe { ((*crtc).width, (*crtc).height) };
|
||||
let position = unsafe { ((*crtc).x, (*crtc).y) };
|
||||
let (name, scale_factor, video_modes) = xconn.get_output_info(resources, crtc)?;
|
||||
let dimensions = (crtc.width as u32, crtc.height as u32);
|
||||
let position = (crtc.x as i32, crtc.y as i32);
|
||||
|
||||
// Get the refresh rate of the current video mode.
|
||||
let current_mode = unsafe { (*crtc).mode };
|
||||
let screen_modes =
|
||||
unsafe { slice::from_raw_parts((*resources).modes, (*resources).nmode as usize) };
|
||||
let current_mode = crtc.mode;
|
||||
let screen_modes = resources.modes();
|
||||
let refresh_rate_millihertz = screen_modes
|
||||
.iter()
|
||||
.find(|mode| mode.id == current_mode)
|
||||
|
@ -207,19 +201,22 @@ impl MonitorHandle {
|
|||
}
|
||||
|
||||
impl XConnection {
|
||||
pub fn get_monitor_for_window(&self, window_rect: Option<util::AaRect>) -> MonitorHandle {
|
||||
let monitors = self.available_monitors();
|
||||
pub fn get_monitor_for_window(
|
||||
&self,
|
||||
window_rect: Option<util::AaRect>,
|
||||
) -> Result<MonitorHandle, X11Error> {
|
||||
let monitors = self.available_monitors()?;
|
||||
|
||||
if monitors.is_empty() {
|
||||
// Return a dummy monitor to avoid panicking
|
||||
return MonitorHandle::dummy();
|
||||
return Ok(MonitorHandle::dummy());
|
||||
}
|
||||
|
||||
let default = monitors.get(0).unwrap();
|
||||
|
||||
let window_rect = match window_rect {
|
||||
Some(rect) => rect,
|
||||
None => return default.to_owned(),
|
||||
None => return Ok(default.to_owned()),
|
||||
};
|
||||
|
||||
let mut largest_overlap = 0;
|
||||
|
@ -232,110 +229,153 @@ impl XConnection {
|
|||
}
|
||||
}
|
||||
|
||||
matched_monitor.to_owned()
|
||||
Ok(matched_monitor.to_owned())
|
||||
}
|
||||
|
||||
fn query_monitor_list(&self) -> Vec<MonitorHandle> {
|
||||
unsafe {
|
||||
let mut major = 0;
|
||||
let mut minor = 0;
|
||||
(self.xrandr.XRRQueryVersion)(self.display, &mut major, &mut minor);
|
||||
fn query_monitor_list(&self) -> Result<Vec<MonitorHandle>, X11Error> {
|
||||
let root = self.default_root();
|
||||
let resources = ScreenResources::from_connection(self.xcb_connection(), root)?;
|
||||
|
||||
let root = self.default_root().root;
|
||||
let resources = if (major == 1 && minor >= 3) || major > 1 {
|
||||
(self.xrandr.XRRGetScreenResourcesCurrent)(self.display, root as ffi::Window)
|
||||
} else {
|
||||
// WARNING: this function is supposedly very slow, on the order of hundreds of ms.
|
||||
// Upon failure, `resources` will be null.
|
||||
(self.xrandr.XRRGetScreenResources)(self.display, root as ffi::Window)
|
||||
};
|
||||
|
||||
if resources.is_null() {
|
||||
panic!("[winit] `XRRGetScreenResources` returned NULL. That should only happen if the root window doesn't exist.");
|
||||
}
|
||||
|
||||
let mut has_primary = false;
|
||||
|
||||
let primary = (self.xrandr.XRRGetOutputPrimary)(self.display, root as ffi::Window);
|
||||
let mut available = Vec::with_capacity((*resources).ncrtc as usize);
|
||||
|
||||
for crtc_index in 0..(*resources).ncrtc {
|
||||
let crtc_id = *((*resources).crtcs.offset(crtc_index as isize));
|
||||
let crtc = (self.xrandr.XRRGetCrtcInfo)(self.display, resources, crtc_id);
|
||||
let is_active = (*crtc).width > 0 && (*crtc).height > 0 && (*crtc).noutput > 0;
|
||||
if is_active {
|
||||
let is_primary = *(*crtc).outputs.offset(0) == primary;
|
||||
has_primary |= is_primary;
|
||||
if let Some(monitor_id) =
|
||||
MonitorHandle::new(self, resources, crtc_id, crtc, is_primary)
|
||||
{
|
||||
available.push(monitor_id)
|
||||
}
|
||||
}
|
||||
(self.xrandr.XRRFreeCrtcInfo)(crtc);
|
||||
}
|
||||
|
||||
// If no monitors were detected as being primary, we just pick one ourselves!
|
||||
if !has_primary {
|
||||
if let Some(ref mut fallback) = available.first_mut() {
|
||||
// Setting this here will come in handy if we ever add an `is_primary` method.
|
||||
fallback.primary = true;
|
||||
}
|
||||
}
|
||||
|
||||
(self.xrandr.XRRFreeScreenResources)(resources);
|
||||
available
|
||||
// Pipeline all of the get-crtc requests.
|
||||
let mut crtc_cookies = Vec::with_capacity(resources.crtcs().len());
|
||||
for &crtc in resources.crtcs() {
|
||||
crtc_cookies.push(
|
||||
self.xcb_connection()
|
||||
.randr_get_crtc_info(crtc, x11rb::CURRENT_TIME)?,
|
||||
);
|
||||
}
|
||||
|
||||
// Do this here so we do all of our requests in one shot.
|
||||
let primary = self
|
||||
.xcb_connection()
|
||||
.randr_get_output_primary(root.root)?
|
||||
.reply()?
|
||||
.output;
|
||||
|
||||
let mut crtc_infos = Vec::with_capacity(crtc_cookies.len());
|
||||
for cookie in crtc_cookies {
|
||||
let reply = cookie.reply()?;
|
||||
crtc_infos.push(reply);
|
||||
}
|
||||
|
||||
let mut has_primary = false;
|
||||
let mut available_monitors = Vec::with_capacity(resources.crtcs().len());
|
||||
for (crtc_id, crtc) in resources.crtcs().iter().zip(crtc_infos.iter()) {
|
||||
if crtc.width == 0 || crtc.height == 0 || crtc.outputs.is_empty() {
|
||||
continue;
|
||||
}
|
||||
|
||||
let is_primary = crtc.outputs[0] == primary;
|
||||
has_primary |= is_primary;
|
||||
let monitor = MonitorHandle::new(self, &resources, *crtc_id, crtc, is_primary);
|
||||
available_monitors.extend(monitor);
|
||||
}
|
||||
|
||||
// If we don't have a primary monitor, just pick one ourselves!
|
||||
if !has_primary {
|
||||
if let Some(ref mut fallback) = available_monitors.first_mut() {
|
||||
// Setting this here will come in handy if we ever add an `is_primary` method.
|
||||
fallback.primary = true;
|
||||
}
|
||||
}
|
||||
|
||||
Ok(available_monitors)
|
||||
}
|
||||
|
||||
pub fn available_monitors(&self) -> Vec<MonitorHandle> {
|
||||
let mut monitors_lock = MONITORS.lock().unwrap();
|
||||
pub fn available_monitors(&self) -> Result<Vec<MonitorHandle>, X11Error> {
|
||||
let mut monitors_lock = self.monitor_handles.lock().unwrap();
|
||||
(*monitors_lock)
|
||||
.as_ref()
|
||||
.cloned()
|
||||
.map(Ok)
|
||||
.or_else(|| {
|
||||
let monitors = Some(self.query_monitor_list());
|
||||
if !DISABLE_MONITOR_LIST_CACHING {
|
||||
(*monitors_lock) = monitors.clone();
|
||||
}
|
||||
monitors
|
||||
self.query_monitor_list()
|
||||
.map(|mon_list| {
|
||||
let monitors = Some(mon_list);
|
||||
if !DISABLE_MONITOR_LIST_CACHING {
|
||||
(*monitors_lock) = monitors.clone();
|
||||
}
|
||||
monitors
|
||||
})
|
||||
.transpose()
|
||||
})
|
||||
.unwrap()
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn primary_monitor(&self) -> MonitorHandle {
|
||||
self.available_monitors()
|
||||
pub fn primary_monitor(&self) -> Result<MonitorHandle, X11Error> {
|
||||
Ok(self
|
||||
.available_monitors()?
|
||||
.into_iter()
|
||||
.find(|monitor| monitor.primary)
|
||||
.unwrap_or_else(MonitorHandle::dummy)
|
||||
.unwrap_or_else(MonitorHandle::dummy))
|
||||
}
|
||||
|
||||
pub fn select_xrandr_input(&self, root: Window) -> Result<c_int, X11Error> {
|
||||
let has_xrandr = unsafe {
|
||||
let mut major = 0;
|
||||
let mut minor = 0;
|
||||
(self.xrandr.XRRQueryVersion)(self.display, &mut major, &mut minor)
|
||||
};
|
||||
assert!(
|
||||
has_xrandr == True,
|
||||
"[winit] XRandR extension not available."
|
||||
);
|
||||
pub fn select_xrandr_input(&self, root: xproto::Window) -> Result<u8, X11Error> {
|
||||
use randr::NotifyMask;
|
||||
|
||||
let mut event_offset = 0;
|
||||
let mut error_offset = 0;
|
||||
let status = unsafe {
|
||||
(self.xrandr.XRRQueryExtension)(self.display, &mut event_offset, &mut error_offset)
|
||||
};
|
||||
// Get extension info.
|
||||
let info = self
|
||||
.xcb_connection()
|
||||
.extension_information(randr::X11_EXTENSION_NAME)?
|
||||
.ok_or_else(|| X11Error::MissingExtension(randr::X11_EXTENSION_NAME))?;
|
||||
|
||||
if status != True {
|
||||
self.check_errors()?;
|
||||
unreachable!("[winit] `XRRQueryExtension` failed but no error was received.");
|
||||
}
|
||||
// Select input data.
|
||||
let event_mask =
|
||||
NotifyMask::CRTC_CHANGE | NotifyMask::OUTPUT_PROPERTY | NotifyMask::SCREEN_CHANGE;
|
||||
self.xcb_connection().randr_select_input(root, event_mask)?;
|
||||
|
||||
let mask = RRCrtcChangeNotifyMask | RROutputPropertyNotifyMask | RRScreenChangeNotifyMask;
|
||||
unsafe { (self.xrandr.XRRSelectInput)(self.display, root, mask) };
|
||||
|
||||
Ok(event_offset)
|
||||
Ok(info.first_event)
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) struct ScreenResources {
|
||||
/// List of attached modes.
|
||||
modes: Vec<randr::ModeInfo>,
|
||||
|
||||
/// List of attached CRTCs.
|
||||
crtcs: Vec<randr::Crtc>,
|
||||
}
|
||||
|
||||
impl ScreenResources {
|
||||
pub(crate) fn modes(&self) -> &[randr::ModeInfo] {
|
||||
&self.modes
|
||||
}
|
||||
|
||||
pub(crate) fn crtcs(&self) -> &[randr::Crtc] {
|
||||
&self.crtcs
|
||||
}
|
||||
|
||||
pub(crate) fn from_connection(
|
||||
conn: &impl x11rb::connection::Connection,
|
||||
root: &x11rb::protocol::xproto::Screen,
|
||||
) -> Result<Self, X11Error> {
|
||||
let version = conn.randr_query_version(0, 0)?.reply()?;
|
||||
|
||||
if (version.major_version == 1 && version.minor_version >= 3) || version.major_version > 1 {
|
||||
let reply = conn
|
||||
.randr_get_screen_resources_current(root.root)?
|
||||
.reply()?;
|
||||
Ok(Self::from_get_screen_resources_current_reply(reply))
|
||||
} else {
|
||||
let reply = conn.randr_get_screen_resources(root.root)?.reply()?;
|
||||
Ok(Self::from_get_screen_resources_reply(reply))
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn from_get_screen_resources_reply(reply: randr::GetScreenResourcesReply) -> Self {
|
||||
Self {
|
||||
modes: reply.modes,
|
||||
crtcs: reply.crtcs,
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn from_get_screen_resources_current_reply(
|
||||
reply: randr::GetScreenResourcesCurrentReply,
|
||||
) -> Self {
|
||||
Self {
|
||||
modes: reply.modes,
|
||||
crtcs: reply.crtcs,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,5 +1,9 @@
|
|||
use std::{slice, str};
|
||||
use x11rb::protocol::xinput::{self, ConnectionExt as _};
|
||||
use x11rb::errors::{ConnectionError, ReplyError};
|
||||
use x11rb::protocol::{
|
||||
xinput::{self, ConnectionExt as _},
|
||||
xkb::{self, ConnectionExt as _},
|
||||
};
|
||||
|
||||
use super::*;
|
||||
|
||||
|
@ -11,31 +15,6 @@ pub const VIRTUAL_CORE_KEYBOARD: u16 = 3;
|
|||
// To test if `lookup_utf8` works correctly, set this to 1.
|
||||
const TEXT_BUFFER_SIZE: usize = 1024;
|
||||
|
||||
// NOTE: Some of these fields are not used, but may be of use in the future.
|
||||
pub struct PointerState<'a> {
|
||||
xconn: &'a XConnection,
|
||||
pub root: xproto::Window,
|
||||
pub child: xproto::Window,
|
||||
pub root_x: c_double,
|
||||
pub root_y: c_double,
|
||||
pub win_x: c_double,
|
||||
pub win_y: c_double,
|
||||
buttons: ffi::XIButtonState,
|
||||
pub group: ffi::XIGroupState,
|
||||
pub relative_to_window: bool,
|
||||
}
|
||||
|
||||
impl<'a> Drop for PointerState<'a> {
|
||||
fn drop(&mut self) {
|
||||
if !self.buttons.mask.is_null() {
|
||||
unsafe {
|
||||
// This is why you need to read the docs carefully...
|
||||
(self.xconn.xlib.XFree)(self.buttons.mask as _);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl XConnection {
|
||||
pub fn select_xinput_events(
|
||||
&self,
|
||||
|
@ -54,16 +33,36 @@ impl XConnection {
|
|||
.map_err(Into::into)
|
||||
}
|
||||
|
||||
pub fn select_xkb_events(&self, device_id: c_int, mask: c_ulong) -> Result<bool, X11Error> {
|
||||
let status =
|
||||
unsafe { (self.xlib.XkbSelectEvents)(self.display, device_id as _, mask, mask) };
|
||||
pub fn select_xkb_events(
|
||||
&self,
|
||||
device_id: xkb::DeviceSpec,
|
||||
mask: xkb::EventType,
|
||||
) -> Result<bool, X11Error> {
|
||||
let result = self
|
||||
.xcb_connection()
|
||||
.xkb_select_events(
|
||||
device_id,
|
||||
xkb::EventType::from(0u8),
|
||||
mask,
|
||||
xkb::MapPart::from(u16::from(mask)),
|
||||
xkb::MapPart::EXPLICIT_COMPONENTS
|
||||
| xkb::MapPart::KEY_ACTIONS
|
||||
| xkb::MapPart::KEY_BEHAVIORS
|
||||
| xkb::MapPart::VIRTUAL_MODS
|
||||
| xkb::MapPart::MODIFIER_MAP
|
||||
| xkb::MapPart::VIRTUAL_MOD_MAP,
|
||||
&xkb::SelectEventsAux::new(),
|
||||
)
|
||||
.map_err(ReplyError::from)
|
||||
.and_then(|x| x.check());
|
||||
|
||||
if status == ffi::True {
|
||||
self.flush_requests()?;
|
||||
Ok(true)
|
||||
} else {
|
||||
error!("Could not select XKB events: The XKB extension is not initialized!");
|
||||
Ok(false)
|
||||
match result {
|
||||
Ok(()) => Ok(true),
|
||||
Err(ReplyError::ConnectionError(ConnectionError::UnsupportedExtension)) => {
|
||||
error!("Could not select XKB events: The XKB extension is not initialized!");
|
||||
Ok(false)
|
||||
}
|
||||
Err(e) => Err(e.into()),
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -1,12 +1,11 @@
|
|||
use std::{env, slice, str::FromStr};
|
||||
use std::{env, str, str::FromStr};
|
||||
|
||||
use super::{
|
||||
ffi::{CurrentTime, RRCrtc, RRMode, Success, XRRCrtcInfo, XRRScreenResources},
|
||||
*,
|
||||
};
|
||||
use super::*;
|
||||
use crate::platform_impl::platform::x11::monitor;
|
||||
use crate::{dpi::validate_scale_factor, platform_impl::platform::x11::VideoMode};
|
||||
|
||||
use x11rb::protocol::randr::{self, ConnectionExt as _};
|
||||
|
||||
/// Represents values of `WINIT_HIDPI_FACTOR`.
|
||||
pub enum EnvVarDPI {
|
||||
Randr,
|
||||
|
@ -37,42 +36,32 @@ pub fn calc_dpi_factor(
|
|||
|
||||
impl XConnection {
|
||||
// Retrieve DPI from Xft.dpi property
|
||||
pub unsafe fn get_xft_dpi(&self) -> Option<f64> {
|
||||
(self.xlib.XrmInitialize)();
|
||||
let resource_manager_str = (self.xlib.XResourceManagerString)(self.display);
|
||||
if resource_manager_str.is_null() {
|
||||
return None;
|
||||
}
|
||||
if let Ok(res) = ::std::ffi::CStr::from_ptr(resource_manager_str).to_str() {
|
||||
let name: &str = "Xft.dpi:\t";
|
||||
for pair in res.split('\n') {
|
||||
if let Some(stripped) = pair.strip_prefix(name) {
|
||||
return f64::from_str(stripped).ok();
|
||||
}
|
||||
}
|
||||
}
|
||||
None
|
||||
pub fn get_xft_dpi(&self) -> Option<f64> {
|
||||
self.database()
|
||||
.get_string("Xfi.dpi", "")
|
||||
.and_then(|s| f64::from_str(s).ok())
|
||||
}
|
||||
pub unsafe fn get_output_info(
|
||||
pub fn get_output_info(
|
||||
&self,
|
||||
resources: *mut XRRScreenResources,
|
||||
crtc: *mut XRRCrtcInfo,
|
||||
resources: &monitor::ScreenResources,
|
||||
crtc: &randr::GetCrtcInfoReply,
|
||||
) -> Option<(String, f64, Vec<VideoMode>)> {
|
||||
let output_info =
|
||||
(self.xrandr.XRRGetOutputInfo)(self.display, resources, *(*crtc).outputs.offset(0));
|
||||
if output_info.is_null() {
|
||||
// When calling `XRRGetOutputInfo` on a virtual monitor (versus a physical display)
|
||||
// it's possible for it to return null.
|
||||
// https://bugs.debian.org/cgi-bin/bugreport.cgi?bug=816596
|
||||
let _ = self.check_errors(); // discard `BadRROutput` error
|
||||
return None;
|
||||
}
|
||||
let output_info = match self
|
||||
.xcb_connection()
|
||||
.randr_get_output_info(crtc.outputs[0], x11rb::CURRENT_TIME)
|
||||
.map_err(X11Error::from)
|
||||
.and_then(|r| r.reply().map_err(X11Error::from))
|
||||
{
|
||||
Ok(output_info) => output_info,
|
||||
Err(err) => {
|
||||
warn!("Failed to get output info: {:?}", err);
|
||||
return None;
|
||||
}
|
||||
};
|
||||
|
||||
let bit_depth = self.default_root().root_depth;
|
||||
|
||||
let output_modes =
|
||||
slice::from_raw_parts((*output_info).modes, (*output_info).nmode as usize);
|
||||
let resource_modes = slice::from_raw_parts((*resources).modes, (*resources).nmode as usize);
|
||||
let output_modes = &output_info.modes;
|
||||
let resource_modes = resources.modes();
|
||||
|
||||
let modes = resource_modes
|
||||
.iter()
|
||||
|
@ -81,7 +70,7 @@ impl XConnection {
|
|||
.filter(|x| output_modes.iter().any(|id| x.id == *id))
|
||||
.map(|mode| {
|
||||
VideoMode {
|
||||
size: (mode.width, mode.height),
|
||||
size: (mode.width.into(), mode.height.into()),
|
||||
refresh_rate_millihertz: monitor::mode_refresh_rate_millihertz(mode)
|
||||
.unwrap_or(0),
|
||||
bit_depth: bit_depth as u16,
|
||||
|
@ -93,11 +82,13 @@ impl XConnection {
|
|||
})
|
||||
.collect();
|
||||
|
||||
let name_slice = slice::from_raw_parts(
|
||||
(*output_info).name as *mut u8,
|
||||
(*output_info).nameLen as usize,
|
||||
);
|
||||
let name = String::from_utf8_lossy(name_slice).into();
|
||||
let name = match str::from_utf8(&output_info.name) {
|
||||
Ok(name) => name.to_owned(),
|
||||
Err(err) => {
|
||||
warn!("Failed to get output name: {:?}", err);
|
||||
return None;
|
||||
}
|
||||
};
|
||||
// Override DPI if `WINIT_X11_SCALE_FACTOR` variable is set
|
||||
let deprecated_dpi_override = env::var("WINIT_HIDPI_FACTOR").ok();
|
||||
if deprecated_dpi_override.is_some() {
|
||||
|
@ -124,8 +115,8 @@ impl XConnection {
|
|||
|
||||
let scale_factor = match dpi_env {
|
||||
EnvVarDPI::Randr => calc_dpi_factor(
|
||||
((*crtc).width, (*crtc).height),
|
||||
((*output_info).mm_width as _, (*output_info).mm_height as _),
|
||||
(crtc.width.into(), crtc.height.into()),
|
||||
(output_info.mm_width as _, output_info.mm_height as _),
|
||||
),
|
||||
EnvVarDPI::Scale(dpi_override) => {
|
||||
if !validate_scale_factor(dpi_override) {
|
||||
|
@ -140,74 +131,47 @@ impl XConnection {
|
|||
dpi / 96.
|
||||
} else {
|
||||
calc_dpi_factor(
|
||||
((*crtc).width, (*crtc).height),
|
||||
((*output_info).mm_width as _, (*output_info).mm_height as _),
|
||||
(crtc.width.into(), crtc.height.into()),
|
||||
(output_info.mm_width as _, output_info.mm_height as _),
|
||||
)
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
(self.xrandr.XRRFreeOutputInfo)(output_info);
|
||||
Some((name, scale_factor, modes))
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
pub fn set_crtc_config(&self, crtc_id: RRCrtc, mode_id: RRMode) -> Option<()> {
|
||||
unsafe {
|
||||
let mut major = 0;
|
||||
let mut minor = 0;
|
||||
(self.xrandr.XRRQueryVersion)(self.display, &mut major, &mut minor);
|
||||
pub fn set_crtc_config(
|
||||
&self,
|
||||
crtc_id: randr::Crtc,
|
||||
mode_id: randr::Mode,
|
||||
) -> Result<(), X11Error> {
|
||||
let crtc = self
|
||||
.xcb_connection()
|
||||
.randr_get_crtc_info(crtc_id, x11rb::CURRENT_TIME)?
|
||||
.reply()?;
|
||||
|
||||
let root = self.default_root().root;
|
||||
let resources = if (major == 1 && minor >= 3) || major > 1 {
|
||||
(self.xrandr.XRRGetScreenResourcesCurrent)(self.display, root as ffi::Window)
|
||||
} else {
|
||||
(self.xrandr.XRRGetScreenResources)(self.display, root as ffi::Window)
|
||||
};
|
||||
|
||||
let crtc = (self.xrandr.XRRGetCrtcInfo)(self.display, resources, crtc_id);
|
||||
let status = (self.xrandr.XRRSetCrtcConfig)(
|
||||
self.display,
|
||||
resources,
|
||||
self.xcb_connection()
|
||||
.randr_set_crtc_config(
|
||||
crtc_id,
|
||||
CurrentTime,
|
||||
(*crtc).x,
|
||||
(*crtc).y,
|
||||
crtc.timestamp,
|
||||
x11rb::CURRENT_TIME,
|
||||
crtc.x,
|
||||
crtc.y,
|
||||
mode_id,
|
||||
(*crtc).rotation,
|
||||
(*crtc).outputs.offset(0),
|
||||
1,
|
||||
);
|
||||
|
||||
(self.xrandr.XRRFreeCrtcInfo)(crtc);
|
||||
(self.xrandr.XRRFreeScreenResources)(resources);
|
||||
|
||||
if status == Success as i32 {
|
||||
Some(())
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
crtc.rotation,
|
||||
&crtc.outputs,
|
||||
)?
|
||||
.reply()
|
||||
.map(|_| ())
|
||||
.map_err(Into::into)
|
||||
}
|
||||
|
||||
pub fn get_crtc_mode(&self, crtc_id: RRCrtc) -> RRMode {
|
||||
unsafe {
|
||||
let mut major = 0;
|
||||
let mut minor = 0;
|
||||
(self.xrandr.XRRQueryVersion)(self.display, &mut major, &mut minor);
|
||||
|
||||
let root = self.default_root().root;
|
||||
let resources = if (major == 1 && minor >= 3) || major > 1 {
|
||||
(self.xrandr.XRRGetScreenResourcesCurrent)(self.display, root as ffi::Window)
|
||||
} else {
|
||||
(self.xrandr.XRRGetScreenResources)(self.display, root as ffi::Window)
|
||||
};
|
||||
|
||||
let crtc = (self.xrandr.XRRGetCrtcInfo)(self.display, resources, crtc_id);
|
||||
let mode = (*crtc).mode;
|
||||
(self.xrandr.XRRFreeCrtcInfo)(crtc);
|
||||
(self.xrandr.XRRFreeScreenResources)(resources);
|
||||
mode
|
||||
}
|
||||
pub fn get_crtc_mode(&self, crtc_id: randr::Crtc) -> Result<randr::Mode, X11Error> {
|
||||
Ok(self
|
||||
.xcb_connection()
|
||||
.randr_get_crtc_info(crtc_id, x11rb::CURRENT_TIME)?
|
||||
.reply()?
|
||||
.mode)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -10,12 +10,11 @@ use std::{
|
|||
use raw_window_handle::{RawDisplayHandle, RawWindowHandle, XlibDisplayHandle, XlibWindowHandle};
|
||||
use x11rb::{
|
||||
connection::Connection,
|
||||
properties::WmHintsState,
|
||||
protocol::xproto::{self, ConnectionExt as _},
|
||||
};
|
||||
use x11rb::{
|
||||
properties::{WmHints, WmSizeHints, WmSizeHintsSpecification},
|
||||
protocol::xinput,
|
||||
properties::{WmHints, WmHintsState, WmSizeHints, WmSizeHintsSpecification},
|
||||
protocol::{
|
||||
randr, xinput,
|
||||
xproto::{self, ConnectionExt as _},
|
||||
},
|
||||
};
|
||||
|
||||
use crate::{
|
||||
|
@ -55,7 +54,7 @@ pub struct SharedState {
|
|||
// Used to restore position after exiting fullscreen
|
||||
pub restore_position: Option<(i32, i32)>,
|
||||
// Used to restore video mode after exiting fullscreen
|
||||
pub desktop_video_mode: Option<(ffi::RRCrtc, ffi::RRMode)>,
|
||||
pub desktop_video_mode: Option<(randr::Crtc, randr::Mode)>,
|
||||
pub frame_extents: Option<util::FrameExtentsHeuristic>,
|
||||
pub min_inner_size: Option<Size>,
|
||||
pub max_inner_size: Option<Size>,
|
||||
|
@ -151,7 +150,7 @@ impl UnownedWindow {
|
|||
None => event_loop.root,
|
||||
};
|
||||
|
||||
let mut monitors = xconn.available_monitors();
|
||||
let mut monitors = leap!(xconn.available_monitors());
|
||||
let guessed_monitor = if monitors.is_empty() {
|
||||
X11MonitorHandle::dummy()
|
||||
} else {
|
||||
|
@ -524,7 +523,7 @@ impl UnownedWindow {
|
|||
| xinput::XIEventMask::TOUCH_BEGIN
|
||||
| xinput::XIEventMask::TOUCH_UPDATE
|
||||
| xinput::XIEventMask::TOUCH_END;
|
||||
leap!(xconn.select_xinput_events(window.xwindow, ffi::XIAllMasterDevices as u16, mask))
|
||||
leap!(xconn.select_xinput_events(window.xwindow, super::ALL_MASTER_DEVICES, mask))
|
||||
.ignore_error();
|
||||
|
||||
{
|
||||
|
@ -740,8 +739,12 @@ impl UnownedWindow {
|
|||
&Some(Fullscreen::Exclusive(PlatformVideoMode::X(ref video_mode))),
|
||||
) => {
|
||||
let monitor = video_mode.monitor.as_ref().unwrap();
|
||||
shared_state_lock.desktop_video_mode =
|
||||
Some((monitor.id, self.xconn.get_crtc_mode(monitor.id)));
|
||||
shared_state_lock.desktop_video_mode = Some((
|
||||
monitor.id,
|
||||
self.xconn
|
||||
.get_crtc_mode(monitor.id)
|
||||
.expect("Failed to get desktop video mode"),
|
||||
));
|
||||
}
|
||||
// Restore desktop video mode upon exiting exclusive fullscreen
|
||||
(&Some(Fullscreen::Exclusive(_)), &None)
|
||||
|
@ -877,11 +880,15 @@ impl UnownedWindow {
|
|||
}
|
||||
|
||||
pub fn available_monitors(&self) -> Vec<X11MonitorHandle> {
|
||||
self.xconn.available_monitors()
|
||||
self.xconn
|
||||
.available_monitors()
|
||||
.expect("Failed to get available monitors")
|
||||
}
|
||||
|
||||
pub fn primary_monitor(&self) -> X11MonitorHandle {
|
||||
self.xconn.primary_monitor()
|
||||
self.xconn
|
||||
.primary_monitor()
|
||||
.expect("Failed to get primary monitor")
|
||||
}
|
||||
|
||||
#[inline]
|
||||
|
|
|
@ -10,17 +10,20 @@ use std::{
|
|||
|
||||
use crate::window::CursorIcon;
|
||||
|
||||
use super::{atoms::Atoms, ffi};
|
||||
use x11rb::{connection::Connection, protocol::xproto, xcb_ffi::XCBConnection};
|
||||
use super::{atoms::Atoms, ffi, monitor::MonitorHandle};
|
||||
use x11rb::{connection::Connection, protocol::xproto, resource_manager, xcb_ffi::XCBConnection};
|
||||
|
||||
/// A connection to an X server.
|
||||
pub(crate) struct XConnection {
|
||||
pub xlib: ffi::Xlib,
|
||||
/// Exposes XRandR functions from version < 1.5
|
||||
pub xrandr: ffi::Xrandr_2_2_0,
|
||||
pub xcursor: ffi::Xcursor,
|
||||
|
||||
// TODO(notgull): I'd like to remove this, but apparently Xlib and Xinput2 are tied together
|
||||
// for some reason.
|
||||
pub xinput2: ffi::XInput2,
|
||||
|
||||
pub display: *mut ffi::Display,
|
||||
|
||||
/// The manager for the XCB connection.
|
||||
///
|
||||
/// The `Option` ensures that we can drop it before we close the `Display`.
|
||||
|
@ -38,6 +41,12 @@ pub(crate) struct XConnection {
|
|||
/// The last timestamp received by this connection.
|
||||
timestamp: AtomicU32,
|
||||
|
||||
/// List of monitor handles.
|
||||
pub monitor_handles: Mutex<Option<Vec<MonitorHandle>>>,
|
||||
|
||||
/// The resource database.
|
||||
database: resource_manager::Database,
|
||||
|
||||
pub latest_error: Mutex<Option<XError>>,
|
||||
pub cursor_cache: Mutex<HashMap<Option<CursorIcon>, ffi::Cursor>>,
|
||||
}
|
||||
|
@ -53,9 +62,8 @@ impl XConnection {
|
|||
// opening the libraries
|
||||
let xlib = ffi::Xlib::open()?;
|
||||
let xcursor = ffi::Xcursor::open()?;
|
||||
let xrandr = ffi::Xrandr_2_2_0::open()?;
|
||||
let xinput2 = ffi::XInput2::open()?;
|
||||
let xlib_xcb = ffi::Xlib_xcb::open()?;
|
||||
let xinput2 = ffi::XInput2::open()?;
|
||||
|
||||
unsafe { (xlib.XInitThreads)() };
|
||||
unsafe { (xlib.XSetErrorHandler)(error_handler) };
|
||||
|
@ -92,9 +100,12 @@ impl XConnection {
|
|||
.reply()
|
||||
.map_err(|e| XNotSupported::XcbConversionError(Arc::new(e)))?;
|
||||
|
||||
// Load the database.
|
||||
let database = resource_manager::new_from_default(&xcb)
|
||||
.map_err(|e| XNotSupported::XcbConversionError(Arc::new(e)))?;
|
||||
|
||||
Ok(XConnection {
|
||||
xlib,
|
||||
xrandr,
|
||||
xcursor,
|
||||
xinput2,
|
||||
display,
|
||||
|
@ -103,6 +114,8 @@ impl XConnection {
|
|||
default_screen,
|
||||
timestamp: AtomicU32::new(0),
|
||||
latest_error: Mutex::new(None),
|
||||
monitor_handles: Mutex::new(None),
|
||||
database,
|
||||
cursor_cache: Default::default(),
|
||||
})
|
||||
}
|
||||
|
@ -144,6 +157,12 @@ impl XConnection {
|
|||
&self.xcb_connection().setup().roots[self.default_screen]
|
||||
}
|
||||
|
||||
/// Get the resource database.
|
||||
#[inline]
|
||||
pub fn database(&self) -> &resource_manager::Database {
|
||||
&self.database
|
||||
}
|
||||
|
||||
/// Get the latest timestamp.
|
||||
#[inline]
|
||||
pub fn timestamp(&self) -> u32 {
|
||||
|
|
Loading…
Reference in a new issue