mirror of
https://github.com/italicsjenga/winit-sonoma-fix.git
synced 2024-12-24 22:31:30 +11:00
X11: util design improvements (#534)
This commit is contained in:
parent
282770f11a
commit
30f798b246
|
@ -40,7 +40,7 @@ impl DndAtoms {
|
||||||
b"text/uri-list\0".as_ptr() as *mut c_char,
|
b"text/uri-list\0".as_ptr() as *mut c_char,
|
||||||
b"None\0".as_ptr() as *mut c_char,
|
b"None\0".as_ptr() as *mut c_char,
|
||||||
];
|
];
|
||||||
let atoms = unsafe { util::get_atoms(xconn, &names) }?;
|
let atoms = unsafe { xconn.get_atoms(&names) }?;
|
||||||
Ok(DndAtoms {
|
Ok(DndAtoms {
|
||||||
aware: atoms[0],
|
aware: atoms[0],
|
||||||
enter: atoms[1],
|
enter: atoms[1],
|
||||||
|
@ -127,13 +127,12 @@ impl Dnd {
|
||||||
DndState::Accepted => (1, self.atoms.action_private as c_long),
|
DndState::Accepted => (1, self.atoms.action_private as c_long),
|
||||||
DndState::Rejected => (0, self.atoms.none as c_long),
|
DndState::Rejected => (0, self.atoms.none as c_long),
|
||||||
};
|
};
|
||||||
util::send_client_msg(
|
self.xconn.send_client_msg(
|
||||||
&self.xconn,
|
|
||||||
target_window,
|
target_window,
|
||||||
target_window,
|
target_window,
|
||||||
self.atoms.status,
|
self.atoms.status,
|
||||||
None,
|
None,
|
||||||
(this_window as c_long, accepted, 0, 0, action),
|
[this_window as c_long, accepted, 0, 0, action],
|
||||||
).flush()
|
).flush()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -147,13 +146,12 @@ impl Dnd {
|
||||||
DndState::Accepted => (1, self.atoms.action_private as c_long),
|
DndState::Accepted => (1, self.atoms.action_private as c_long),
|
||||||
DndState::Rejected => (0, self.atoms.none as c_long),
|
DndState::Rejected => (0, self.atoms.none as c_long),
|
||||||
};
|
};
|
||||||
util::send_client_msg(
|
self.xconn.send_client_msg(
|
||||||
&self.xconn,
|
|
||||||
target_window,
|
target_window,
|
||||||
target_window,
|
target_window,
|
||||||
self.atoms.finished,
|
self.atoms.finished,
|
||||||
None,
|
None,
|
||||||
(this_window as c_long, accepted, action, 0, 0),
|
[this_window as c_long, accepted, action, 0, 0],
|
||||||
).flush()
|
).flush()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -161,8 +159,7 @@ impl Dnd {
|
||||||
&self,
|
&self,
|
||||||
source_window: c_ulong,
|
source_window: c_ulong,
|
||||||
) -> Result<Vec<ffi::Atom>, util::GetPropertyError> {
|
) -> Result<Vec<ffi::Atom>, util::GetPropertyError> {
|
||||||
util::get_property(
|
self.xconn.get_property(
|
||||||
&self.xconn,
|
|
||||||
source_window,
|
source_window,
|
||||||
self.atoms.type_list,
|
self.atoms.type_list,
|
||||||
ffi::XA_ATOM,
|
ffi::XA_ATOM,
|
||||||
|
@ -184,8 +181,7 @@ impl Dnd {
|
||||||
&self,
|
&self,
|
||||||
window: c_ulong,
|
window: c_ulong,
|
||||||
) -> Result<Vec<c_uchar>, util::GetPropertyError> {
|
) -> Result<Vec<c_uchar>, util::GetPropertyError> {
|
||||||
util::get_property(
|
self.xconn.get_property(
|
||||||
&self.xconn,
|
|
||||||
window,
|
window,
|
||||||
self.atoms.selection,
|
self.atoms.selection,
|
||||||
self.atoms.uri_list,
|
self.atoms.uri_list,
|
||||||
|
|
|
@ -85,13 +85,11 @@ enum GetXimServersError {
|
||||||
// modifiers, since we don't want a user who's looking at logs to ask "am I supposed to set
|
// modifiers, since we don't want a user who's looking at logs to ask "am I supposed to set
|
||||||
// XMODIFIERS to `@server=ibus`?!?"
|
// XMODIFIERS to `@server=ibus`?!?"
|
||||||
unsafe fn get_xim_servers(xconn: &Arc<XConnection>) -> Result<Vec<String>, GetXimServersError> {
|
unsafe fn get_xim_servers(xconn: &Arc<XConnection>) -> Result<Vec<String>, GetXimServersError> {
|
||||||
let servers_atom = util::get_atom(&xconn, b"XIM_SERVERS\0")
|
let servers_atom = xconn.get_atom_unchecked(b"XIM_SERVERS\0");
|
||||||
.map_err(GetXimServersError::XError)?;
|
|
||||||
|
|
||||||
let root = (xconn.xlib.XDefaultRootWindow)(xconn.display);
|
let root = (xconn.xlib.XDefaultRootWindow)(xconn.display);
|
||||||
|
|
||||||
let mut atoms: Vec<ffi::Atom> = util::get_property(
|
let mut atoms: Vec<ffi::Atom> = xconn.get_property(
|
||||||
&xconn,
|
|
||||||
root,
|
root,
|
||||||
servers_atom,
|
servers_atom,
|
||||||
ffi::XA_ATOM,
|
ffi::XA_ATOM,
|
||||||
|
|
|
@ -75,8 +75,7 @@ impl EventsLoop {
|
||||||
pub fn new(display: Arc<XConnection>) -> EventsLoop {
|
pub fn new(display: Arc<XConnection>) -> EventsLoop {
|
||||||
let root = unsafe { (display.xlib.XDefaultRootWindow)(display.display) };
|
let root = unsafe { (display.xlib.XDefaultRootWindow)(display.display) };
|
||||||
|
|
||||||
let wm_delete_window = unsafe { util::get_atom(&display, b"WM_DELETE_WINDOW\0") }
|
let wm_delete_window = unsafe { display.get_atom_unchecked(b"WM_DELETE_WINDOW\0") };
|
||||||
.expect("Failed to call XInternAtom (WM_DELETE_WINDOW)");
|
|
||||||
|
|
||||||
let dnd = Dnd::new(Arc::clone(&display))
|
let dnd = Dnd::new(Arc::clone(&display))
|
||||||
.expect("Failed to call XInternAtoms when initializing drag and drop");
|
.expect("Failed to call XInternAtoms when initializing drag and drop");
|
||||||
|
@ -130,7 +129,7 @@ impl EventsLoop {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
util::update_cached_wm_info(&display, root);
|
display.update_cached_wm_info(root);
|
||||||
|
|
||||||
let wakeup_dummy_window = unsafe {
|
let wakeup_dummy_window = unsafe {
|
||||||
let (x, y, w, h) = (10, 10, 10, 10);
|
let (x, y, w, h) = (10, 10, 10, 10);
|
||||||
|
@ -166,14 +165,8 @@ impl EventsLoop {
|
||||||
};
|
};
|
||||||
|
|
||||||
// Register for device hotplug events
|
// Register for device hotplug events
|
||||||
unsafe {
|
// (The request buffer is flushed during `init_device`)
|
||||||
util::select_xinput_events(
|
result.display.select_xinput_events(root, ffi::XIAllDevices, ffi::XI_HierarchyChangedMask).queue();
|
||||||
&result.display,
|
|
||||||
root,
|
|
||||||
ffi::XIAllDevices,
|
|
||||||
ffi::XI_HierarchyChangedMask,
|
|
||||||
)
|
|
||||||
}.queue(); // The request buffer is flushed during init_device
|
|
||||||
|
|
||||||
result.init_device(ffi::XIAllDevices);
|
result.init_device(ffi::XIAllDevices);
|
||||||
|
|
||||||
|
@ -484,11 +477,7 @@ impl EventsLoop {
|
||||||
.unwrap()
|
.unwrap()
|
||||||
.inner_pos_to_outer(inner_x, inner_y)
|
.inner_pos_to_outer(inner_x, inner_y)
|
||||||
} else {
|
} else {
|
||||||
let extents = util::get_frame_extents_heuristic(
|
let extents = self.display.get_frame_extents_heuristic(window, self.root);
|
||||||
&self.display,
|
|
||||||
window,
|
|
||||||
self.root,
|
|
||||||
);
|
|
||||||
let outer_pos = extents.inner_pos_to_outer(inner_x, inner_y);
|
let outer_pos = extents.inner_pos_to_outer(inner_x, inner_y);
|
||||||
(*window_state_lock).frame_extents = Some(extents);
|
(*window_state_lock).frame_extents = Some(extents);
|
||||||
outer_pos
|
outer_pos
|
||||||
|
@ -513,7 +502,7 @@ impl EventsLoop {
|
||||||
// (which is almost all of them). Failing to correctly update WM info doesn't
|
// (which is almost all of them). Failing to correctly update WM info doesn't
|
||||||
// really have much impact, since on the WMs affected (xmonad, dwm, etc.) the only
|
// really have much impact, since on the WMs affected (xmonad, dwm, etc.) the only
|
||||||
// effect is that we waste some time trying to query unsupported properties.
|
// effect is that we waste some time trying to query unsupported properties.
|
||||||
util::update_cached_wm_info(&self.display, self.root);
|
self.display.update_cached_wm_info(self.root);
|
||||||
|
|
||||||
self.shared_state
|
self.shared_state
|
||||||
.borrow()
|
.borrow()
|
||||||
|
@ -614,7 +603,7 @@ impl EventsLoop {
|
||||||
|
|
||||||
if state == Pressed {
|
if state == Pressed {
|
||||||
let written = if let Some(ic) = self.ime.borrow().get_context(window) {
|
let written = if let Some(ic) = self.ime.borrow().get_context(window) {
|
||||||
unsafe { util::lookup_utf8(&self.display, ic, xkev) }
|
self.display.lookup_utf8(ic, xkev)
|
||||||
} else {
|
} else {
|
||||||
return;
|
return;
|
||||||
};
|
};
|
||||||
|
@ -841,13 +830,8 @@ impl EventsLoop {
|
||||||
// The mods field on this event isn't actually populated, so query the
|
// The mods field on this event isn't actually populated, so query the
|
||||||
// pointer device. In the future, we can likely remove this round-trip by
|
// pointer device. In the future, we can likely remove this round-trip by
|
||||||
// relying on Xkb for modifier values.
|
// relying on Xkb for modifier values.
|
||||||
let modifiers = unsafe {
|
let modifiers = self.display.query_pointer(xev.event, xev.deviceid)
|
||||||
util::query_pointer(
|
.expect("Failed to query pointer device").get_modifier_state();
|
||||||
&self.display,
|
|
||||||
xev.event,
|
|
||||||
xev.deviceid,
|
|
||||||
)
|
|
||||||
}.expect("Failed to query pointer device").get_modifier_state();
|
|
||||||
|
|
||||||
callback(Event::WindowEvent { window_id, event: CursorMoved {
|
callback(Event::WindowEvent { window_id, event: CursorMoved {
|
||||||
device_id,
|
device_id,
|
||||||
|
@ -1094,16 +1078,13 @@ impl EventsLoopProxy {
|
||||||
// NOTE: This design is taken from the old `WindowProxy::wakeup` implementation. It
|
// NOTE: This design is taken from the old `WindowProxy::wakeup` implementation. It
|
||||||
// assumes that X11 is thread safe. Is this true?
|
// assumes that X11 is thread safe. Is this true?
|
||||||
// (WARNING: it's probably not true)
|
// (WARNING: it's probably not true)
|
||||||
unsafe {
|
display.send_client_msg(
|
||||||
util::send_client_msg(
|
self.wakeup_dummy_window,
|
||||||
&display,
|
self.wakeup_dummy_window,
|
||||||
self.wakeup_dummy_window,
|
0,
|
||||||
self.wakeup_dummy_window,
|
None,
|
||||||
0,
|
[0, 0, 0, 0, 0],
|
||||||
None,
|
).flush().expect("Failed to call XSendEvent after wakeup");
|
||||||
(0, 0, 0, 0, 0),
|
|
||||||
)
|
|
||||||
}.flush().expect("Failed to call XSendEvent after wakeup");
|
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
@ -1320,14 +1301,8 @@ impl Device {
|
||||||
| ffi::XI_RawButtonReleaseMask
|
| ffi::XI_RawButtonReleaseMask
|
||||||
| ffi::XI_RawKeyPressMask
|
| ffi::XI_RawKeyPressMask
|
||||||
| ffi::XI_RawKeyReleaseMask;
|
| ffi::XI_RawKeyReleaseMask;
|
||||||
unsafe {
|
// The request buffer is flushed when we poll for events
|
||||||
util::select_xinput_events(
|
el.display.select_xinput_events(el.root, info.deviceid, mask).queue();
|
||||||
&el.display,
|
|
||||||
el.root,
|
|
||||||
info.deviceid,
|
|
||||||
mask,
|
|
||||||
)
|
|
||||||
}.queue(); // The request buffer is flushed when we poll for events
|
|
||||||
|
|
||||||
// Identify scroll axes
|
// Identify scroll axes
|
||||||
for class_ptr in Device::classes(info) {
|
for class_ptr in Device::classes(info) {
|
||||||
|
|
|
@ -66,7 +66,7 @@ impl MonitorId {
|
||||||
repr: util::MonitorRepr,
|
repr: util::MonitorRepr,
|
||||||
primary: bool,
|
primary: bool,
|
||||||
) -> Self {
|
) -> Self {
|
||||||
let (name, hidpi_factor) = unsafe { util::get_output_info(xconn, resources, &repr) };
|
let (name, hidpi_factor) = unsafe { xconn.get_output_info(resources, &repr) };
|
||||||
let (dimensions, position) = unsafe { (repr.get_dimensions(), repr.get_position()) };
|
let (dimensions, position) = unsafe { (repr.get_dimensions(), repr.get_position()) };
|
||||||
let rect = util::Rect::new(position, dimensions);
|
let rect = util::Rect::new(position, dimensions);
|
||||||
MonitorId {
|
MonitorId {
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
use std::ffi::{CStr, CString};
|
use std::ffi::{CStr, CString};
|
||||||
|
use std::fmt::Debug;
|
||||||
use std::os::raw::*;
|
use std::os::raw::*;
|
||||||
|
|
||||||
use parking_lot::Mutex;
|
use parking_lot::Mutex;
|
||||||
|
@ -12,48 +13,60 @@ lazy_static! {
|
||||||
static ref ATOM_CACHE: Mutex<AtomCache> = Mutex::new(HashMap::with_capacity(2048));
|
static ref ATOM_CACHE: Mutex<AtomCache> = Mutex::new(HashMap::with_capacity(2048));
|
||||||
}
|
}
|
||||||
|
|
||||||
pub unsafe fn get_atom(xconn: &Arc<XConnection>, name: &[u8]) -> Result<ffi::Atom, XError> {
|
impl XConnection {
|
||||||
let name = CStr::from_bytes_with_nul_unchecked(name); // I trust you. Don't let me down.
|
pub fn get_atom<T: AsRef<CStr> + Debug>(&self, name: T) -> ffi::Atom {
|
||||||
let mut atom_cache_lock = ATOM_CACHE.lock();
|
let name = name.as_ref();
|
||||||
let cached_atom = (*atom_cache_lock).get(name).cloned();
|
let mut atom_cache_lock = ATOM_CACHE.lock();
|
||||||
if let Some(atom) = cached_atom {
|
let cached_atom = (*atom_cache_lock).get(name).cloned();
|
||||||
Ok(atom)
|
if let Some(atom) = cached_atom {
|
||||||
} else {
|
atom
|
||||||
let atom = (xconn.xlib.XInternAtom)(
|
} else {
|
||||||
xconn.display,
|
let atom = unsafe { (self.xlib.XInternAtom)(
|
||||||
name.as_ptr() as *const c_char,
|
self.display,
|
||||||
|
name.as_ptr() as *const c_char,
|
||||||
|
ffi::False,
|
||||||
|
) };
|
||||||
|
if atom == 0 {
|
||||||
|
let msg = format!(
|
||||||
|
"`XInternAtom` failed, which really shouldn't happen. Atom: {:?}, Error: {:#?}",
|
||||||
|
name,
|
||||||
|
self.check_errors(),
|
||||||
|
);
|
||||||
|
panic!(msg);
|
||||||
|
}
|
||||||
|
/*println!(
|
||||||
|
"XInternAtom name:{:?} atom:{:?}",
|
||||||
|
name,
|
||||||
|
atom,
|
||||||
|
);*/
|
||||||
|
(*atom_cache_lock).insert(name.to_owned(), atom);
|
||||||
|
atom
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub unsafe fn get_atom_unchecked(&self, name: &[u8]) -> ffi::Atom {
|
||||||
|
debug_assert!(CStr::from_bytes_with_nul(name).is_ok());
|
||||||
|
let name = CStr::from_bytes_with_nul_unchecked(name);
|
||||||
|
self.get_atom(name)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Note: this doesn't use caching, for the sake of simplicity.
|
||||||
|
// If you're dealing with this many atoms, you'll usually want to cache them locally anyway.
|
||||||
|
pub unsafe fn get_atoms(&self, names: &[*mut c_char]) -> Result<Vec<ffi::Atom>, XError> {
|
||||||
|
let mut atoms = Vec::with_capacity(names.len());
|
||||||
|
(self.xlib.XInternAtoms)(
|
||||||
|
self.display,
|
||||||
|
names.as_ptr() as *mut _,
|
||||||
|
names.len() as c_int,
|
||||||
ffi::False,
|
ffi::False,
|
||||||
|
atoms.as_mut_ptr(),
|
||||||
);
|
);
|
||||||
|
self.check_errors()?;
|
||||||
|
atoms.set_len(names.len());
|
||||||
/*println!(
|
/*println!(
|
||||||
"XInternAtom name:{:?} atom:{:?}",
|
"XInternAtoms atoms:{:?}",
|
||||||
name,
|
atoms,
|
||||||
atom,
|
|
||||||
);*/
|
);*/
|
||||||
xconn.check_errors()?;
|
Ok(atoms)
|
||||||
(*atom_cache_lock).insert(name.to_owned(), atom);
|
|
||||||
Ok(atom)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Note: this doesn't use caching, for the sake of simplicity.
|
|
||||||
// If you're dealing with this many atoms, you'll usually want to cache them locally anyway.
|
|
||||||
pub unsafe fn get_atoms(
|
|
||||||
xconn: &Arc<XConnection>,
|
|
||||||
names: &[*mut c_char],
|
|
||||||
) -> Result<Vec<ffi::Atom>, XError> {
|
|
||||||
let mut atoms = Vec::with_capacity(names.len());
|
|
||||||
(xconn.xlib.XInternAtoms)(
|
|
||||||
xconn.display,
|
|
||||||
names.as_ptr() as *mut _,
|
|
||||||
names.len() as c_int,
|
|
||||||
ffi::False,
|
|
||||||
atoms.as_mut_ptr(),
|
|
||||||
);
|
|
||||||
xconn.check_errors()?;
|
|
||||||
atoms.set_len(names.len());
|
|
||||||
/*println!(
|
|
||||||
"XInternAtoms atoms:{:?}",
|
|
||||||
atoms,
|
|
||||||
);*/
|
|
||||||
Ok(atoms)
|
|
||||||
}
|
|
||||||
|
|
95
src/platform/linux/x11/util/client_msg.rs
Normal file
95
src/platform/linux/x11/util/client_msg.rs
Normal file
|
@ -0,0 +1,95 @@
|
||||||
|
use super::*;
|
||||||
|
|
||||||
|
pub type ClientMsgPayload = [c_long; 5];
|
||||||
|
|
||||||
|
impl XConnection {
|
||||||
|
pub fn send_event<T: Into<ffi::XEvent>>(
|
||||||
|
&self,
|
||||||
|
target_window: c_ulong,
|
||||||
|
event_mask: Option<c_long>,
|
||||||
|
event: T,
|
||||||
|
) -> Flusher {
|
||||||
|
let event_mask = event_mask.unwrap_or(ffi::NoEventMask);
|
||||||
|
unsafe {
|
||||||
|
(self.xlib.XSendEvent)(
|
||||||
|
self.display,
|
||||||
|
target_window,
|
||||||
|
ffi::False,
|
||||||
|
event_mask,
|
||||||
|
&mut event.into(),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
Flusher::new(self)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn send_client_msg(
|
||||||
|
&self,
|
||||||
|
window: c_ulong, // The window this is "about"; not necessarily this window
|
||||||
|
target_window: c_ulong, // The window we're sending to
|
||||||
|
message_type: ffi::Atom,
|
||||||
|
event_mask: Option<c_long>,
|
||||||
|
data: ClientMsgPayload,
|
||||||
|
) -> Flusher {
|
||||||
|
let mut event: ffi::XClientMessageEvent = unsafe { mem::uninitialized() };
|
||||||
|
event.type_ = ffi::ClientMessage;
|
||||||
|
event.display = self.display;
|
||||||
|
event.window = window;
|
||||||
|
event.message_type = message_type;
|
||||||
|
event.format = c_long::FORMAT as c_int;
|
||||||
|
event.data = unsafe { mem::transmute(data) };
|
||||||
|
self.send_event(target_window, event_mask, event)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Prepare yourself for the ultimate in unsafety!
|
||||||
|
// You should favor `send_client_msg` whenever possible, but some protocols (i.e. startup notification) require you
|
||||||
|
// to send more than one message worth of data.
|
||||||
|
pub fn send_client_msg_multi<T: Formattable>(
|
||||||
|
&self,
|
||||||
|
window: c_ulong, // The window this is "about"; not necessarily this window
|
||||||
|
target_window: c_ulong, // The window we're sending to
|
||||||
|
message_type: ffi::Atom,
|
||||||
|
event_mask: Option<c_long>,
|
||||||
|
data: &[T],
|
||||||
|
) -> Flusher {
|
||||||
|
let format = T::FORMAT;
|
||||||
|
let size_of_t = mem::size_of::<T>();
|
||||||
|
debug_assert_eq!(size_of_t, format.get_actual_size());
|
||||||
|
let mut event: ffi::XClientMessageEvent = unsafe { mem::uninitialized() };
|
||||||
|
event.type_ = ffi::ClientMessage;
|
||||||
|
event.display = self.display;
|
||||||
|
event.window = window;
|
||||||
|
event.message_type = message_type;
|
||||||
|
event.format = format as c_int;
|
||||||
|
|
||||||
|
let t_per_payload = format.get_payload_size() / size_of_t;
|
||||||
|
assert!(t_per_payload > 0);
|
||||||
|
let payload_count = data.len() / t_per_payload;
|
||||||
|
let payload_remainder = data.len() % t_per_payload;
|
||||||
|
let payload_ptr = data.as_ptr() as *const ClientMsgPayload;
|
||||||
|
|
||||||
|
let mut payload_index = 0;
|
||||||
|
while payload_index < payload_count {
|
||||||
|
let payload = unsafe { payload_ptr.offset(payload_index as isize) };
|
||||||
|
payload_index += 1;
|
||||||
|
event.data = unsafe { mem::transmute(*payload) };
|
||||||
|
self.send_event(target_window, event_mask, &event).queue();
|
||||||
|
}
|
||||||
|
|
||||||
|
if payload_remainder > 0 {
|
||||||
|
let mut payload: ClientMsgPayload = [0; 5];
|
||||||
|
let t_payload = payload.as_mut_ptr() as *mut T;
|
||||||
|
let invalid_payload = unsafe { payload_ptr.offset(payload_index as isize) };
|
||||||
|
let invalid_t_payload = invalid_payload as *const T;
|
||||||
|
let mut t_index = 0;
|
||||||
|
while t_index < payload_remainder {
|
||||||
|
let valid_t = unsafe { invalid_t_payload.offset(t_index as isize) };
|
||||||
|
unsafe { (*t_payload.offset(t_index as isize)) = (*valid_t).clone() };
|
||||||
|
t_index += 1;
|
||||||
|
}
|
||||||
|
event.data = unsafe { mem::transmute(payload) };
|
||||||
|
self.send_event(target_window, event_mask, &event).queue();
|
||||||
|
}
|
||||||
|
|
||||||
|
Flusher::new(self)
|
||||||
|
}
|
||||||
|
}
|
58
src/platform/linux/x11/util/format.rs
Normal file
58
src/platform/linux/x11/util/format.rs
Normal file
|
@ -0,0 +1,58 @@
|
||||||
|
use std::fmt::Debug;
|
||||||
|
use std::mem;
|
||||||
|
use std::os::raw::*;
|
||||||
|
|
||||||
|
// This isn't actually the number of the bits in the format.
|
||||||
|
// X11 does a match on this value to determine which type to call sizeof on.
|
||||||
|
// Thus, we use 32 for c_long, since 32 maps to c_long which maps to 64.
|
||||||
|
// ...if that sounds confusing, then you know why this enum is here.
|
||||||
|
#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord)]
|
||||||
|
pub enum Format {
|
||||||
|
Char = 8,
|
||||||
|
Short = 16,
|
||||||
|
Long = 32,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Format {
|
||||||
|
pub fn from_format(format: usize) -> Option<Self> {
|
||||||
|
match format {
|
||||||
|
8 => Some(Format::Char),
|
||||||
|
16 => Some(Format::Short),
|
||||||
|
32 => Some(Format::Long),
|
||||||
|
_ => None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn is_same_size_as<T>(&self) -> bool {
|
||||||
|
mem::size_of::<T>() == self.get_actual_size()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_actual_size(&self) -> usize {
|
||||||
|
match self {
|
||||||
|
&Format::Char => mem::size_of::<c_char>(),
|
||||||
|
&Format::Short => mem::size_of::<c_short>(),
|
||||||
|
&Format::Long => mem::size_of::<c_long>(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_payload_size(&self) -> usize {
|
||||||
|
match self {
|
||||||
|
// Due to the wonders of X11, half the space goes unused if you're not using longs (on 64-bit).
|
||||||
|
&Format::Char => mem::size_of::<c_char>() * 20,
|
||||||
|
&Format::Short => mem::size_of::<c_short>() * 10,
|
||||||
|
&Format::Long => mem::size_of::<c_long>() * 5,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub trait Formattable: Debug + Clone + Copy + PartialEq + PartialOrd {
|
||||||
|
const FORMAT: Format;
|
||||||
|
}
|
||||||
|
|
||||||
|
// You might be surprised by the absence of c_int, but not as surprised as X11 would be by the presence of it.
|
||||||
|
impl Formattable for c_char { const FORMAT: Format = Format::Char; }
|
||||||
|
impl Formattable for c_uchar { const FORMAT: Format = Format::Char; }
|
||||||
|
impl Formattable for c_short { const FORMAT: Format = Format::Short; }
|
||||||
|
impl Formattable for c_ushort { const FORMAT: Format = Format::Short; }
|
||||||
|
impl Formattable for c_long { const FORMAT: Format = Format::Long; }
|
||||||
|
impl Formattable for c_ulong { const FORMAT: Format = Format::Long; }
|
|
@ -42,30 +42,6 @@ pub struct TranslatedCoords {
|
||||||
pub child: ffi::Window,
|
pub child: ffi::Window,
|
||||||
}
|
}
|
||||||
|
|
||||||
// This is adequate for get_inner_position
|
|
||||||
pub unsafe fn translate_coords(
|
|
||||||
xconn: &Arc<XConnection>,
|
|
||||||
window: ffi::Window,
|
|
||||||
root: ffi::Window,
|
|
||||||
) -> Result<TranslatedCoords, XError> {
|
|
||||||
let mut translated_coords: TranslatedCoords = mem::uninitialized();
|
|
||||||
|
|
||||||
(xconn.xlib.XTranslateCoordinates)(
|
|
||||||
xconn.display,
|
|
||||||
window,
|
|
||||||
root,
|
|
||||||
0,
|
|
||||||
0,
|
|
||||||
&mut translated_coords.x_rel_root,
|
|
||||||
&mut translated_coords.y_rel_root,
|
|
||||||
&mut translated_coords.child,
|
|
||||||
);
|
|
||||||
|
|
||||||
//println!("XTranslateCoordinates coords:{:?}", translated_coords);
|
|
||||||
|
|
||||||
xconn.check_errors().map(|_| translated_coords)
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct Geometry {
|
pub struct Geometry {
|
||||||
pub root: ffi::Window,
|
pub root: ffi::Window,
|
||||||
|
@ -86,30 +62,6 @@ pub struct Geometry {
|
||||||
pub depth: c_uint,
|
pub depth: c_uint,
|
||||||
}
|
}
|
||||||
|
|
||||||
// This is adequate for get_inner_size
|
|
||||||
pub unsafe fn get_geometry(
|
|
||||||
xconn: &Arc<XConnection>,
|
|
||||||
window: ffi::Window,
|
|
||||||
) -> Result<Geometry, XError> {
|
|
||||||
let mut geometry: Geometry = mem::uninitialized();
|
|
||||||
|
|
||||||
let _status = (xconn.xlib.XGetGeometry)(
|
|
||||||
xconn.display,
|
|
||||||
window,
|
|
||||||
&mut geometry.root,
|
|
||||||
&mut geometry.x_rel_parent,
|
|
||||||
&mut geometry.y_rel_parent,
|
|
||||||
&mut geometry.width,
|
|
||||||
&mut geometry.height,
|
|
||||||
&mut geometry.border,
|
|
||||||
&mut geometry.depth,
|
|
||||||
);
|
|
||||||
|
|
||||||
//println!("XGetGeometry geo:{:?}", geometry);
|
|
||||||
|
|
||||||
xconn.check_errors().map(|_| geometry)
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
pub struct FrameExtents {
|
pub struct FrameExtents {
|
||||||
pub left: c_ulong,
|
pub left: c_ulong,
|
||||||
|
@ -128,109 +80,6 @@ impl FrameExtents {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_frame_extents(
|
|
||||||
xconn: &Arc<XConnection>,
|
|
||||||
window: ffi::Window,
|
|
||||||
) -> Option<self::FrameExtents> {
|
|
||||||
let extents_atom = unsafe { self::get_atom(xconn, b"_NET_FRAME_EXTENTS\0") }
|
|
||||||
.expect("Failed to call XInternAtom (_NET_FRAME_EXTENTS)");
|
|
||||||
|
|
||||||
if !self::hint_is_supported(extents_atom) {
|
|
||||||
return None;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Of the WMs tested, xmonad, i3, dwm, IceWM (1.3.x and earlier), and blackbox don't
|
|
||||||
// support this. As this is part of EWMH (Extended Window Manager Hints), it's likely to
|
|
||||||
// be unsupported by many smaller WMs.
|
|
||||||
let extents: Option<Vec<c_ulong>> = unsafe {
|
|
||||||
self::get_property(
|
|
||||||
xconn,
|
|
||||||
window,
|
|
||||||
extents_atom,
|
|
||||||
ffi::XA_CARDINAL,
|
|
||||||
)
|
|
||||||
}.ok();
|
|
||||||
|
|
||||||
extents.and_then(|extents| {
|
|
||||||
if extents.len() >= 4 {
|
|
||||||
Some(self::FrameExtents {
|
|
||||||
left: extents[0],
|
|
||||||
right: extents[1],
|
|
||||||
top: extents[2],
|
|
||||||
bottom: extents[3],
|
|
||||||
})
|
|
||||||
} else {
|
|
||||||
None
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn is_top_level(
|
|
||||||
xconn: &Arc<XConnection>,
|
|
||||||
window: ffi::Window,
|
|
||||||
root: ffi::Window,
|
|
||||||
) -> Option<bool> {
|
|
||||||
let client_list_atom = unsafe { self::get_atom(xconn, b"_NET_CLIENT_LIST\0") }
|
|
||||||
.expect("Failed to call XInternAtom (_NET_CLIENT_LIST)");
|
|
||||||
|
|
||||||
if !self::hint_is_supported(client_list_atom) {
|
|
||||||
return None;
|
|
||||||
}
|
|
||||||
|
|
||||||
let client_list: Option<Vec<ffi::Window>> = unsafe {
|
|
||||||
self::get_property(
|
|
||||||
xconn,
|
|
||||||
root,
|
|
||||||
client_list_atom,
|
|
||||||
ffi::XA_WINDOW,
|
|
||||||
)
|
|
||||||
}.ok();
|
|
||||||
|
|
||||||
client_list.map(|client_list| client_list.contains(&window))
|
|
||||||
}
|
|
||||||
|
|
||||||
unsafe fn get_parent_window(
|
|
||||||
xconn: &Arc<XConnection>,
|
|
||||||
window: ffi::Window,
|
|
||||||
) -> Result<ffi::Window, XError> {
|
|
||||||
let mut root: ffi::Window = mem::uninitialized();
|
|
||||||
let mut parent: ffi::Window = mem::uninitialized();
|
|
||||||
let mut children: *mut ffi::Window = ptr::null_mut();
|
|
||||||
let mut nchildren: c_uint = mem::uninitialized();
|
|
||||||
|
|
||||||
let _status = (xconn.xlib.XQueryTree)(
|
|
||||||
xconn.display,
|
|
||||||
window,
|
|
||||||
&mut root,
|
|
||||||
&mut parent,
|
|
||||||
&mut children,
|
|
||||||
&mut nchildren,
|
|
||||||
);
|
|
||||||
|
|
||||||
// The list of children isn't used
|
|
||||||
if children != ptr::null_mut() {
|
|
||||||
(xconn.xlib.XFree)(children as *mut _);
|
|
||||||
}
|
|
||||||
|
|
||||||
xconn.check_errors().map(|_| parent)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn climb_hierarchy(
|
|
||||||
xconn: &Arc<XConnection>,
|
|
||||||
window: ffi::Window,
|
|
||||||
root: ffi::Window,
|
|
||||||
) -> Result<ffi::Window, XError> {
|
|
||||||
let mut outer_window = window;
|
|
||||||
loop {
|
|
||||||
let candidate = unsafe { get_parent_window(xconn, outer_window) }?;
|
|
||||||
if candidate == root {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
outer_window = candidate;
|
|
||||||
}
|
|
||||||
Ok(outer_window)
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, Clone, PartialEq)]
|
#[derive(Debug, Clone, PartialEq)]
|
||||||
pub enum FrameExtentsHeuristicPath {
|
pub enum FrameExtentsHeuristicPath {
|
||||||
Supported,
|
Supported,
|
||||||
|
@ -266,120 +115,237 @@ impl FrameExtentsHeuristic {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_frame_extents_heuristic(
|
impl XConnection {
|
||||||
xconn: &Arc<XConnection>,
|
// This is adequate for get_inner_position
|
||||||
window: ffi::Window,
|
pub fn translate_coords(&self, window: ffi::Window, root: ffi::Window) -> Result<TranslatedCoords, XError> {
|
||||||
root: ffi::Window,
|
let mut translated_coords: TranslatedCoords = unsafe { mem::uninitialized() };
|
||||||
) -> FrameExtentsHeuristic {
|
unsafe {
|
||||||
use self::FrameExtentsHeuristicPath::*;
|
(self.xlib.XTranslateCoordinates)(
|
||||||
|
self.display,
|
||||||
|
window,
|
||||||
|
root,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
&mut translated_coords.x_rel_root,
|
||||||
|
&mut translated_coords.y_rel_root,
|
||||||
|
&mut translated_coords.child,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
//println!("XTranslateCoordinates coords:{:?}", translated_coords);
|
||||||
|
self.check_errors().map(|_| translated_coords)
|
||||||
|
}
|
||||||
|
|
||||||
// Position relative to root window.
|
// This is adequate for get_inner_size
|
||||||
// With rare exceptions, this is the position of a nested window. Cases where the window
|
pub fn get_geometry(&self, window: ffi::Window) -> Result<Geometry, XError> {
|
||||||
// isn't nested are outlined in the comments throghout this function, but in addition to
|
let mut geometry: Geometry = unsafe { mem::uninitialized() };
|
||||||
// that, fullscreen windows often aren't nested.
|
let _status = unsafe {
|
||||||
let (inner_y_rel_root, child) = {
|
(self.xlib.XGetGeometry)(
|
||||||
let coords = unsafe { translate_coords(xconn, window, root) }
|
self.display,
|
||||||
.expect("Failed to translate window coordinates");
|
window,
|
||||||
(
|
&mut geometry.root,
|
||||||
coords.y_rel_root,
|
&mut geometry.x_rel_parent,
|
||||||
coords.child,
|
&mut geometry.y_rel_parent,
|
||||||
)
|
&mut geometry.width,
|
||||||
};
|
&mut geometry.height,
|
||||||
|
&mut geometry.border,
|
||||||
|
&mut geometry.depth,
|
||||||
|
)
|
||||||
|
};
|
||||||
|
//println!("XGetGeometry geo:{:?}", geometry);
|
||||||
|
self.check_errors().map(|_| geometry)
|
||||||
|
}
|
||||||
|
|
||||||
let (width, height, border) = {
|
fn get_frame_extents(&self, window: ffi::Window) -> Option<FrameExtents> {
|
||||||
let inner_geometry = unsafe { get_geometry(xconn, window) }
|
let extents_atom = unsafe { self.get_atom_unchecked(b"_NET_FRAME_EXTENTS\0") };
|
||||||
.expect("Failed to get inner window geometry");
|
|
||||||
(
|
|
||||||
inner_geometry.width,
|
|
||||||
inner_geometry.height,
|
|
||||||
inner_geometry.border,
|
|
||||||
)
|
|
||||||
};
|
|
||||||
|
|
||||||
// The first condition is only false for un-nested windows, but isn't always false for
|
if !hint_is_supported(extents_atom) {
|
||||||
// un-nested windows. Mutter/Muffin/Budgie and Marco present a mysterious discrepancy:
|
return None;
|
||||||
// when y is on the range [0, 2] and if the window has been unfocused since being
|
|
||||||
// undecorated (or was undecorated upon construction), the first condition is true,
|
|
||||||
// requiring us to rely on the second condition.
|
|
||||||
let nested = !(window == child || is_top_level(xconn, child, root) == Some(true));
|
|
||||||
|
|
||||||
// Hopefully the WM supports EWMH, allowing us to get exact info on the window frames.
|
|
||||||
if let Some(mut frame_extents) = get_frame_extents(xconn, window) {
|
|
||||||
// Mutter/Muffin/Budgie and Marco preserve their decorated frame extents when
|
|
||||||
// decorations are disabled, but since the window becomes un-nested, it's easy to
|
|
||||||
// catch.
|
|
||||||
if !nested {
|
|
||||||
frame_extents = FrameExtents::new(0, 0, 0, 0);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// The difference between the nested window's position and the outermost window's
|
// Of the WMs tested, xmonad, i3, dwm, IceWM (1.3.x and earlier), and blackbox don't
|
||||||
// position is equivalent to the frame size. In most scenarios, this is equivalent to
|
// support this. As this is part of EWMH (Extended Window Manager Hints), it's likely to
|
||||||
// manually climbing the hierarchy as is done in the case below. Here's a list of
|
// be unsupported by many smaller WMs.
|
||||||
// known discrepancies:
|
let extents: Option<Vec<c_ulong>> = self.get_property(
|
||||||
// * Mutter/Muffin/Budgie gives decorated windows a margin of 9px (only 7px on top) in
|
window,
|
||||||
// addition to a 1px semi-transparent border. The margin can be easily observed by
|
extents_atom,
|
||||||
// using a screenshot tool to get a screenshot of a selected window, and is
|
ffi::XA_CARDINAL,
|
||||||
// presumably used for drawing drop shadows. Getting window geometry information
|
).ok();
|
||||||
// via hierarchy-climbing results in this margin being included in both the
|
|
||||||
// position and outer size, so a window positioned at (0, 0) would be reported as
|
|
||||||
// having a position (-10, -8).
|
|
||||||
// * Compiz has a drop shadow margin just like Mutter/Muffin/Budgie, though it's 10px
|
|
||||||
// on all sides, and there's no additional border.
|
|
||||||
// * Enlightenment otherwise gets a y position equivalent to inner_y_rel_root.
|
|
||||||
// Without decorations, there's no difference. This is presumably related to
|
|
||||||
// Enlightenment's fairly unique concept of window position; it interprets
|
|
||||||
// positions given to XMoveWindow as a client area position rather than a position
|
|
||||||
// of the overall window.
|
|
||||||
|
|
||||||
FrameExtentsHeuristic {
|
extents.and_then(|extents| {
|
||||||
frame_extents,
|
if extents.len() >= 4 {
|
||||||
heuristic_path: Supported,
|
Some(FrameExtents {
|
||||||
|
left: extents[0],
|
||||||
|
right: extents[1],
|
||||||
|
top: extents[2],
|
||||||
|
bottom: extents[3],
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn is_top_level(&self, window: ffi::Window, root: ffi::Window) -> Option<bool> {
|
||||||
|
let client_list_atom = unsafe { self.get_atom_unchecked(b"_NET_CLIENT_LIST\0") };
|
||||||
|
|
||||||
|
if !hint_is_supported(client_list_atom) {
|
||||||
|
return None;
|
||||||
}
|
}
|
||||||
} else if nested {
|
|
||||||
// If the position value we have is for a nested window used as the client area, we'll
|
|
||||||
// just climb up the hierarchy and get the geometry of the outermost window we're
|
|
||||||
// nested in.
|
|
||||||
let outer_window = climb_hierarchy(xconn, window, root)
|
|
||||||
.expect("Failed to climb window hierarchy");
|
|
||||||
|
|
||||||
let (outer_y, outer_width, outer_height) = {
|
let client_list: Option<Vec<ffi::Window>> = self.get_property(
|
||||||
let outer_geometry = unsafe { get_geometry(xconn, outer_window) }
|
root,
|
||||||
.expect("Failed to get outer window geometry");
|
client_list_atom,
|
||||||
|
ffi::XA_WINDOW,
|
||||||
|
).ok();
|
||||||
|
|
||||||
|
client_list.map(|client_list| client_list.contains(&window))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get_parent_window(&self, window: ffi::Window) -> Result<ffi::Window, XError> {
|
||||||
|
let parent = unsafe {
|
||||||
|
let mut root: ffi::Window = mem::uninitialized();
|
||||||
|
let mut parent: ffi::Window = mem::uninitialized();
|
||||||
|
let mut children: *mut ffi::Window = ptr::null_mut();
|
||||||
|
let mut nchildren: c_uint = mem::uninitialized();
|
||||||
|
|
||||||
|
// What's filled into `parent` if `window` is the root window?
|
||||||
|
let _status = (self.xlib.XQueryTree)(
|
||||||
|
self.display,
|
||||||
|
window,
|
||||||
|
&mut root,
|
||||||
|
&mut parent,
|
||||||
|
&mut children,
|
||||||
|
&mut nchildren,
|
||||||
|
);
|
||||||
|
|
||||||
|
// The list of children isn't used
|
||||||
|
if children != ptr::null_mut() {
|
||||||
|
(self.xlib.XFree)(children as *mut _);
|
||||||
|
}
|
||||||
|
|
||||||
|
parent
|
||||||
|
};
|
||||||
|
self.check_errors().map(|_| parent)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn climb_hierarchy(&self, window: ffi::Window, root: ffi::Window) -> Result<ffi::Window, XError> {
|
||||||
|
let mut outer_window = window;
|
||||||
|
loop {
|
||||||
|
let candidate = self.get_parent_window(outer_window)?;
|
||||||
|
if candidate == root {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
outer_window = candidate;
|
||||||
|
}
|
||||||
|
Ok(outer_window)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_frame_extents_heuristic(&self, window: ffi::Window, root: ffi::Window) -> FrameExtentsHeuristic {
|
||||||
|
use self::FrameExtentsHeuristicPath::*;
|
||||||
|
|
||||||
|
// Position relative to root window.
|
||||||
|
// With rare exceptions, this is the position of a nested window. Cases where the window
|
||||||
|
// isn't nested are outlined in the comments throghout this function, but in addition to
|
||||||
|
// that, fullscreen windows often aren't nested.
|
||||||
|
let (inner_y_rel_root, child) = {
|
||||||
|
let coords = self.translate_coords(window, root).expect("Failed to translate window coordinates");
|
||||||
(
|
(
|
||||||
outer_geometry.y_rel_parent,
|
coords.y_rel_root,
|
||||||
outer_geometry.width,
|
coords.child,
|
||||||
outer_geometry.height,
|
|
||||||
)
|
)
|
||||||
};
|
};
|
||||||
|
|
||||||
// Since we have the geometry of the outermost window and the geometry of the client
|
let (width, height, border) = {
|
||||||
// area, we can figure out what's in between.
|
let inner_geometry = self.get_geometry(window).expect("Failed to get inner window geometry");
|
||||||
let diff_x = outer_width.saturating_sub(width);
|
(
|
||||||
let diff_y = outer_height.saturating_sub(height);
|
inner_geometry.width,
|
||||||
let offset_y = inner_y_rel_root.saturating_sub(outer_y) as c_uint;
|
inner_geometry.height,
|
||||||
|
inner_geometry.border,
|
||||||
|
)
|
||||||
|
};
|
||||||
|
|
||||||
let left = diff_x / 2;
|
// The first condition is only false for un-nested windows, but isn't always false for
|
||||||
let right = left;
|
// un-nested windows. Mutter/Muffin/Budgie and Marco present a mysterious discrepancy:
|
||||||
let top = offset_y;
|
// when y is on the range [0, 2] and if the window has been unfocused since being
|
||||||
let bottom = diff_y.saturating_sub(offset_y);
|
// undecorated (or was undecorated upon construction), the first condition is true,
|
||||||
|
// requiring us to rely on the second condition.
|
||||||
|
let nested = !(window == child || self.is_top_level(child, root) == Some(true));
|
||||||
|
|
||||||
let frame_extents = FrameExtents::new(
|
// Hopefully the WM supports EWMH, allowing us to get exact info on the window frames.
|
||||||
left.into(),
|
if let Some(mut frame_extents) = self.get_frame_extents(window) {
|
||||||
right.into(),
|
// Mutter/Muffin/Budgie and Marco preserve their decorated frame extents when
|
||||||
top.into(),
|
// decorations are disabled, but since the window becomes un-nested, it's easy to
|
||||||
bottom.into(),
|
// catch.
|
||||||
);
|
if !nested {
|
||||||
FrameExtentsHeuristic {
|
frame_extents = FrameExtents::new(0, 0, 0, 0);
|
||||||
frame_extents,
|
}
|
||||||
heuristic_path: UnsupportedNested,
|
|
||||||
}
|
// The difference between the nested window's position and the outermost window's
|
||||||
} else {
|
// position is equivalent to the frame size. In most scenarios, this is equivalent to
|
||||||
// This is the case for xmonad and dwm, AKA the only WMs tested that supplied a
|
// manually climbing the hierarchy as is done in the case below. Here's a list of
|
||||||
// border value. This is convenient, since we can use it to get an accurate frame.
|
// known discrepancies:
|
||||||
let frame_extents = FrameExtents::from_border(border.into());
|
// * Mutter/Muffin/Budgie gives decorated windows a margin of 9px (only 7px on top) in
|
||||||
FrameExtentsHeuristic {
|
// addition to a 1px semi-transparent border. The margin can be easily observed by
|
||||||
frame_extents,
|
// using a screenshot tool to get a screenshot of a selected window, and is
|
||||||
heuristic_path: UnsupportedBordered,
|
// presumably used for drawing drop shadows. Getting window geometry information
|
||||||
|
// via hierarchy-climbing results in this margin being included in both the
|
||||||
|
// position and outer size, so a window positioned at (0, 0) would be reported as
|
||||||
|
// having a position (-10, -8).
|
||||||
|
// * Compiz has a drop shadow margin just like Mutter/Muffin/Budgie, though it's 10px
|
||||||
|
// on all sides, and there's no additional border.
|
||||||
|
// * Enlightenment otherwise gets a y position equivalent to inner_y_rel_root.
|
||||||
|
// Without decorations, there's no difference. This is presumably related to
|
||||||
|
// Enlightenment's fairly unique concept of window position; it interprets
|
||||||
|
// positions given to XMoveWindow as a client area position rather than a position
|
||||||
|
// of the overall window.
|
||||||
|
|
||||||
|
FrameExtentsHeuristic {
|
||||||
|
frame_extents,
|
||||||
|
heuristic_path: Supported,
|
||||||
|
}
|
||||||
|
} else if nested {
|
||||||
|
// If the position value we have is for a nested window used as the client area, we'll
|
||||||
|
// just climb up the hierarchy and get the geometry of the outermost window we're
|
||||||
|
// nested in.
|
||||||
|
let outer_window = self.climb_hierarchy(window, root).expect("Failed to climb window hierarchy");
|
||||||
|
let (outer_y, outer_width, outer_height) = {
|
||||||
|
let outer_geometry = self.get_geometry(outer_window).expect("Failed to get outer window geometry");
|
||||||
|
(
|
||||||
|
outer_geometry.y_rel_parent,
|
||||||
|
outer_geometry.width,
|
||||||
|
outer_geometry.height,
|
||||||
|
)
|
||||||
|
};
|
||||||
|
|
||||||
|
// Since we have the geometry of the outermost window and the geometry of the client
|
||||||
|
// area, we can figure out what's in between.
|
||||||
|
let diff_x = outer_width.saturating_sub(width);
|
||||||
|
let diff_y = outer_height.saturating_sub(height);
|
||||||
|
let offset_y = inner_y_rel_root.saturating_sub(outer_y) as c_uint;
|
||||||
|
|
||||||
|
let left = diff_x / 2;
|
||||||
|
let right = left;
|
||||||
|
let top = offset_y;
|
||||||
|
let bottom = diff_y.saturating_sub(offset_y);
|
||||||
|
|
||||||
|
let frame_extents = FrameExtents::new(
|
||||||
|
left.into(),
|
||||||
|
right.into(),
|
||||||
|
top.into(),
|
||||||
|
bottom.into(),
|
||||||
|
);
|
||||||
|
FrameExtentsHeuristic {
|
||||||
|
frame_extents,
|
||||||
|
heuristic_path: UnsupportedNested,
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// This is the case for xmonad and dwm, AKA the only WMs tested that supplied a
|
||||||
|
// border value. This is convenient, since we can use it to get an accurate frame.
|
||||||
|
let frame_extents = FrameExtents::from_border(border.into());
|
||||||
|
FrameExtentsHeuristic {
|
||||||
|
frame_extents,
|
||||||
|
heuristic_path: UnsupportedBordered,
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,3 +1,5 @@
|
||||||
|
use std::sync::Arc;
|
||||||
|
|
||||||
use super::*;
|
use super::*;
|
||||||
|
|
||||||
pub const MWM_HINTS_DECORATIONS: c_ulong = 2;
|
pub const MWM_HINTS_DECORATIONS: c_ulong = 2;
|
||||||
|
@ -6,12 +8,12 @@ pub const MWM_HINTS_DECORATIONS: c_ulong = 2;
|
||||||
pub enum StateOperation {
|
pub enum StateOperation {
|
||||||
Remove = 0, // _NET_WM_STATE_REMOVE
|
Remove = 0, // _NET_WM_STATE_REMOVE
|
||||||
Add = 1, // _NET_WM_STATE_ADD
|
Add = 1, // _NET_WM_STATE_ADD
|
||||||
_Toggle = 2, // _NET_WM_STATE_TOGGLE
|
Toggle = 2, // _NET_WM_STATE_TOGGLE
|
||||||
}
|
}
|
||||||
|
|
||||||
impl From<bool> for StateOperation {
|
impl From<bool> for StateOperation {
|
||||||
fn from(b: bool) -> Self {
|
fn from(op: bool) -> Self {
|
||||||
if b {
|
if op {
|
||||||
StateOperation::Add
|
StateOperation::Add
|
||||||
} else {
|
} else {
|
||||||
StateOperation::Remove
|
StateOperation::Remove
|
||||||
|
@ -62,7 +64,30 @@ impl WindowType {
|
||||||
&Dialog => b"_NET_WM_WINDOW_TYPE_DIALOG\0",
|
&Dialog => b"_NET_WM_WINDOW_TYPE_DIALOG\0",
|
||||||
&Normal => b"_NET_WM_WINDOW_TYPE_NORMAL\0",
|
&Normal => b"_NET_WM_WINDOW_TYPE_NORMAL\0",
|
||||||
};
|
};
|
||||||
unsafe { get_atom(xconn, atom_name) }
|
unsafe { xconn.get_atom_unchecked(atom_name) }
|
||||||
.expect("Failed to get atom for `WindowType`")
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl XConnection {
|
||||||
|
pub fn get_wm_hints(&self, window: ffi::Window) -> Result<XSmartPointer<ffi::XWMHints>, XError> {
|
||||||
|
let wm_hints = unsafe { (self.xlib.XGetWMHints)(self.display, window) };
|
||||||
|
self.check_errors()?;
|
||||||
|
let wm_hints = if wm_hints.is_null() {
|
||||||
|
self.alloc_wm_hints()
|
||||||
|
} else {
|
||||||
|
XSmartPointer::new(self, wm_hints).unwrap()
|
||||||
|
};
|
||||||
|
Ok(wm_hints)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn set_wm_hints(&self, window: ffi::Window, wm_hints: XSmartPointer<ffi::XWMHints>) -> Flusher {
|
||||||
|
unsafe {
|
||||||
|
(self.xlib.XSetWMHints)(
|
||||||
|
self.display,
|
||||||
|
window,
|
||||||
|
wm_hints.ptr,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
Flusher::new(self)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,44 +1,12 @@
|
||||||
|
use std::str;
|
||||||
|
|
||||||
use super::*;
|
use super::*;
|
||||||
use events::ModifiersState;
|
use events::ModifiersState;
|
||||||
|
|
||||||
pub unsafe fn select_xinput_events(
|
// A base buffer size of 1kB uses a negligible amount of RAM while preventing us from having to
|
||||||
xconn: &Arc<XConnection>,
|
// re-allocate (and make another round-trip) in the *vast* majority of cases.
|
||||||
window: c_ulong,
|
// To test if `lookup_utf8` works correctly, set this to 1.
|
||||||
device_id: c_int,
|
const TEXT_BUFFER_SIZE: usize = 1024;
|
||||||
mask: i32,
|
|
||||||
) -> Flusher {
|
|
||||||
let mut event_mask = ffi::XIEventMask {
|
|
||||||
deviceid: device_id,
|
|
||||||
mask: &mask as *const _ as *mut c_uchar,
|
|
||||||
mask_len: mem::size_of_val(&mask) as c_int,
|
|
||||||
};
|
|
||||||
(xconn.xinput2.XISelectEvents)(
|
|
||||||
xconn.display,
|
|
||||||
window,
|
|
||||||
&mut event_mask as *mut ffi::XIEventMask,
|
|
||||||
1, // number of masks to read from pointer above
|
|
||||||
);
|
|
||||||
Flusher::new(xconn)
|
|
||||||
}
|
|
||||||
|
|
||||||
#[allow(dead_code)]
|
|
||||||
pub unsafe fn select_xkb_events(
|
|
||||||
xconn: &Arc<XConnection>,
|
|
||||||
device_id: c_uint,
|
|
||||||
mask: c_ulong,
|
|
||||||
) -> Option<Flusher> {
|
|
||||||
let status = (xconn.xlib.XkbSelectEvents)(
|
|
||||||
xconn.display,
|
|
||||||
device_id,
|
|
||||||
mask,
|
|
||||||
mask,
|
|
||||||
);
|
|
||||||
if status == ffi::True {
|
|
||||||
Some(Flusher::new(xconn))
|
|
||||||
} else {
|
|
||||||
None
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl From<ffi::XIModifierState> for ModifiersState {
|
impl From<ffi::XIModifierState> for ModifiersState {
|
||||||
fn from(mods: ffi::XIModifierState) -> Self {
|
fn from(mods: ffi::XIModifierState) -> Self {
|
||||||
|
@ -53,17 +21,17 @@ impl From<ffi::XIModifierState> for ModifiersState {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct PointerState<'a> {
|
pub struct PointerState<'a> {
|
||||||
xconn: &'a Arc<XConnection>,
|
xconn: &'a XConnection,
|
||||||
_root: ffi::Window,
|
root: ffi::Window,
|
||||||
_child: ffi::Window,
|
child: ffi::Window,
|
||||||
_root_x: c_double,
|
root_x: c_double,
|
||||||
_root_y: c_double,
|
root_y: c_double,
|
||||||
_win_x: c_double,
|
win_x: c_double,
|
||||||
_win_y: c_double,
|
win_y: c_double,
|
||||||
_buttons: ffi::XIButtonState,
|
buttons: ffi::XIButtonState,
|
||||||
modifiers: ffi::XIModifierState,
|
modifiers: ffi::XIModifierState,
|
||||||
_group: ffi::XIGroupState,
|
group: ffi::XIGroupState,
|
||||||
_relative_to_window: bool,
|
relative_to_window: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> PointerState<'a> {
|
impl<'a> PointerState<'a> {
|
||||||
|
@ -74,114 +42,115 @@ impl<'a> PointerState<'a> {
|
||||||
|
|
||||||
impl<'a> Drop for PointerState<'a> {
|
impl<'a> Drop for PointerState<'a> {
|
||||||
fn drop(&mut self) {
|
fn drop(&mut self) {
|
||||||
unsafe {
|
if !self.buttons.mask.is_null() {
|
||||||
// This is why you need to read the docs carefully...
|
unsafe {
|
||||||
(self.xconn.xlib.XFree)(self._buttons.mask as _);
|
// This is why you need to read the docs carefully...
|
||||||
|
(self.xconn.xlib.XFree)(self.buttons.mask as _);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub unsafe fn query_pointer(
|
impl XConnection {
|
||||||
xconn: &Arc<XConnection>,
|
pub fn select_xinput_events(&self, window: c_ulong, device_id: c_int, mask: i32) -> Flusher {
|
||||||
window: ffi::Window,
|
let mut event_mask = ffi::XIEventMask {
|
||||||
device_id: c_int,
|
deviceid: device_id,
|
||||||
) -> Result<PointerState, XError> {
|
mask: &mask as *const _ as *mut c_uchar,
|
||||||
let mut root_return = mem::uninitialized();
|
mask_len: mem::size_of_val(&mask) as c_int,
|
||||||
let mut child_return = mem::uninitialized();
|
};
|
||||||
let mut root_x_return = mem::uninitialized();
|
unsafe {
|
||||||
let mut root_y_return = mem::uninitialized();
|
(self.xinput2.XISelectEvents)(
|
||||||
let mut win_x_return = mem::uninitialized();
|
self.display,
|
||||||
let mut win_y_return = mem::uninitialized();
|
window,
|
||||||
let mut buttons_return = mem::uninitialized();
|
&mut event_mask as *mut ffi::XIEventMask,
|
||||||
let mut modifiers_return = mem::uninitialized();
|
1, // number of masks to read from pointer above
|
||||||
let mut group_return = mem::uninitialized();
|
);
|
||||||
|
}
|
||||||
|
Flusher::new(self)
|
||||||
|
}
|
||||||
|
|
||||||
let relative_to_window = (xconn.xinput2.XIQueryPointer)(
|
#[allow(dead_code)]
|
||||||
xconn.display,
|
pub fn select_xkb_events(&self, device_id: c_uint, mask: c_ulong) -> Option<Flusher> {
|
||||||
device_id,
|
let status = unsafe {
|
||||||
window,
|
(self.xlib.XkbSelectEvents)(
|
||||||
&mut root_return,
|
self.display,
|
||||||
&mut child_return,
|
device_id,
|
||||||
&mut root_x_return,
|
mask,
|
||||||
&mut root_y_return,
|
mask,
|
||||||
&mut win_x_return,
|
)
|
||||||
&mut win_y_return,
|
};
|
||||||
&mut buttons_return,
|
if status == ffi::True {
|
||||||
&mut modifiers_return,
|
Some(Flusher::new(self))
|
||||||
&mut group_return,
|
} else {
|
||||||
) == ffi::True;
|
None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
xconn.check_errors()?;
|
pub fn query_pointer(&self, window: ffi::Window, device_id: c_int) -> Result<PointerState, XError> {
|
||||||
|
unsafe {
|
||||||
|
let mut pointer_state: PointerState = mem::uninitialized();
|
||||||
|
pointer_state.xconn = self;
|
||||||
|
pointer_state.relative_to_window = (self.xinput2.XIQueryPointer)(
|
||||||
|
self.display,
|
||||||
|
device_id,
|
||||||
|
window,
|
||||||
|
&mut pointer_state.root,
|
||||||
|
&mut pointer_state.child,
|
||||||
|
&mut pointer_state.root_x,
|
||||||
|
&mut pointer_state.root_y,
|
||||||
|
&mut pointer_state.win_x,
|
||||||
|
&mut pointer_state.win_y,
|
||||||
|
&mut pointer_state.buttons,
|
||||||
|
&mut pointer_state.modifiers,
|
||||||
|
&mut pointer_state.group,
|
||||||
|
) == ffi::True;
|
||||||
|
if let Err(err) = self.check_errors() {
|
||||||
|
// Running the destrutor would be bad news for us...
|
||||||
|
mem::forget(pointer_state);
|
||||||
|
Err(err)
|
||||||
|
} else {
|
||||||
|
Ok(pointer_state)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
Ok(PointerState {
|
fn lookup_utf8_inner(
|
||||||
xconn,
|
&self,
|
||||||
_root: root_return,
|
ic: ffi::XIC,
|
||||||
_child: child_return,
|
key_event: &mut ffi::XKeyEvent,
|
||||||
_root_x: root_x_return,
|
buffer: &mut [u8],
|
||||||
_root_y: root_y_return,
|
) -> (ffi::KeySym, ffi::Status, c_int) {
|
||||||
_win_x: win_x_return,
|
let mut keysym: ffi::KeySym = 0;
|
||||||
_win_y: win_y_return,
|
let mut status: ffi::Status = 0;
|
||||||
_buttons: buttons_return,
|
let count = unsafe {
|
||||||
modifiers: modifiers_return,
|
(self.xlib.Xutf8LookupString)(
|
||||||
_group: group_return,
|
ic,
|
||||||
_relative_to_window: relative_to_window,
|
key_event,
|
||||||
})
|
buffer.as_mut_ptr() as *mut c_char,
|
||||||
}
|
buffer.len() as c_int,
|
||||||
|
&mut keysym,
|
||||||
|
&mut status,
|
||||||
|
)
|
||||||
|
};
|
||||||
|
(keysym, status, count)
|
||||||
|
}
|
||||||
|
|
||||||
unsafe fn lookup_utf8_inner(
|
pub fn lookup_utf8(&self, ic: ffi::XIC, key_event: &mut ffi::XKeyEvent) -> String {
|
||||||
xconn: &Arc<XConnection>,
|
let mut buffer: [u8; TEXT_BUFFER_SIZE] = unsafe { mem::uninitialized() };
|
||||||
ic: ffi::XIC,
|
let (_, status, count) = self.lookup_utf8_inner(ic, key_event, &mut buffer);
|
||||||
key_event: &mut ffi::XKeyEvent,
|
// The buffer overflowed, so we'll make a new one on the heap.
|
||||||
buffer: &mut [u8],
|
if status == ffi::XBufferOverflow {
|
||||||
) -> (ffi::KeySym, ffi::Status, c_int) {
|
let mut buffer = Vec::with_capacity(count as usize);
|
||||||
let mut keysym: ffi::KeySym = 0;
|
unsafe { buffer.set_len(count as usize) };
|
||||||
let mut status: ffi::Status = 0;
|
let (_, _, new_count) = self.lookup_utf8_inner(ic, key_event, &mut buffer);
|
||||||
let count = (xconn.xlib.Xutf8LookupString)(
|
debug_assert_eq!(count, new_count);
|
||||||
ic,
|
str::from_utf8(&buffer[..count as usize])
|
||||||
key_event,
|
.unwrap_or("")
|
||||||
buffer.as_mut_ptr() as *mut c_char,
|
.to_string()
|
||||||
buffer.len() as c_int,
|
} else {
|
||||||
&mut keysym,
|
str::from_utf8(&buffer[..count as usize])
|
||||||
&mut status,
|
.unwrap_or("")
|
||||||
);
|
.to_string()
|
||||||
(keysym, status, count)
|
}
|
||||||
}
|
|
||||||
|
|
||||||
// A base buffer size of 1kB uses a negligible amount of RAM while preventing us from having to
|
|
||||||
// re-allocate (and make another round-trip) in the *vast* majority of cases.
|
|
||||||
// To test if lookup_utf8 works correctly, set this to 1.
|
|
||||||
const TEXT_BUFFER_SIZE: usize = 1024;
|
|
||||||
|
|
||||||
pub unsafe fn lookup_utf8(
|
|
||||||
xconn: &Arc<XConnection>,
|
|
||||||
ic: ffi::XIC,
|
|
||||||
key_event: &mut ffi::XKeyEvent,
|
|
||||||
) -> String {
|
|
||||||
let mut buffer: [u8; TEXT_BUFFER_SIZE] = mem::uninitialized();
|
|
||||||
let (_, status, count) = lookup_utf8_inner(
|
|
||||||
xconn,
|
|
||||||
ic,
|
|
||||||
key_event,
|
|
||||||
&mut buffer,
|
|
||||||
);
|
|
||||||
|
|
||||||
// The buffer overflowed, so we'll make a new one on the heap.
|
|
||||||
if status == ffi::XBufferOverflow {
|
|
||||||
let mut buffer = Vec::with_capacity(count as usize);
|
|
||||||
buffer.set_len(count as usize);
|
|
||||||
let (_, _, new_count) = lookup_utf8_inner(
|
|
||||||
xconn,
|
|
||||||
ic,
|
|
||||||
key_event,
|
|
||||||
&mut buffer,
|
|
||||||
);
|
|
||||||
debug_assert_eq!(count, new_count);
|
|
||||||
str::from_utf8(&buffer[..count as usize])
|
|
||||||
.unwrap_or("")
|
|
||||||
.to_string()
|
|
||||||
} else {
|
|
||||||
str::from_utf8(&buffer[..count as usize])
|
|
||||||
.unwrap_or("")
|
|
||||||
.to_string()
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
62
src/platform/linux/x11/util/memory.rs
Normal file
62
src/platform/linux/x11/util/memory.rs
Normal file
|
@ -0,0 +1,62 @@
|
||||||
|
use std::ops::{Deref, DerefMut};
|
||||||
|
|
||||||
|
use super::*;
|
||||||
|
|
||||||
|
pub struct XSmartPointer<'a, T> {
|
||||||
|
xconn: &'a XConnection,
|
||||||
|
pub ptr: *mut T,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a, T> XSmartPointer<'a, T> {
|
||||||
|
// You're responsible for only passing things to this that should be XFree'd.
|
||||||
|
// Returns None if ptr is null.
|
||||||
|
pub fn new(xconn: &'a XConnection, ptr: *mut T) -> Option<Self> {
|
||||||
|
if !ptr.is_null() {
|
||||||
|
Some(XSmartPointer {
|
||||||
|
xconn,
|
||||||
|
ptr,
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a, T> Deref for XSmartPointer<'a, T> {
|
||||||
|
type Target = T;
|
||||||
|
|
||||||
|
fn deref(&self) -> &T {
|
||||||
|
unsafe { &*self.ptr }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a, T> DerefMut for XSmartPointer<'a, T> {
|
||||||
|
fn deref_mut(&mut self) -> &mut T {
|
||||||
|
unsafe { &mut *self.ptr }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a, T> Drop for XSmartPointer<'a, T> {
|
||||||
|
fn drop(&mut self) {
|
||||||
|
unsafe {
|
||||||
|
(self.xconn.xlib.XFree)(self.ptr as *mut _);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl XConnection {
|
||||||
|
pub fn alloc_class_hint(&self) -> XSmartPointer<ffi::XClassHint> {
|
||||||
|
XSmartPointer::new(self, unsafe { (self.xlib.XAllocClassHint)() })
|
||||||
|
.expect("`XAllocClassHint` returned null; out of memory")
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn alloc_size_hints(&self) -> XSmartPointer<ffi::XSizeHints> {
|
||||||
|
XSmartPointer::new(self, unsafe { (self.xlib.XAllocSizeHints)() })
|
||||||
|
.expect("`XAllocSizeHints` returned null; out of memory")
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn alloc_wm_hints(&self) -> XSmartPointer<ffi::XWMHints> {
|
||||||
|
XSmartPointer::new(self, unsafe { (self.xlib.XAllocWMHints)() })
|
||||||
|
.expect("`XAllocWMHints` returned null; out of memory")
|
||||||
|
}
|
||||||
|
}
|
|
@ -2,180 +2,80 @@
|
||||||
// *results may vary
|
// *results may vary
|
||||||
|
|
||||||
mod atom;
|
mod atom;
|
||||||
|
mod client_msg;
|
||||||
|
mod format;
|
||||||
mod geometry;
|
mod geometry;
|
||||||
mod hint;
|
mod hint;
|
||||||
mod icon;
|
mod icon;
|
||||||
mod input;
|
mod input;
|
||||||
|
mod memory;
|
||||||
mod randr;
|
mod randr;
|
||||||
mod window_property;
|
mod window_property;
|
||||||
mod wm;
|
mod wm;
|
||||||
|
|
||||||
pub use self::atom::*;
|
pub use self::atom::*;
|
||||||
|
pub use self::client_msg::*;
|
||||||
|
pub use self::format::*;
|
||||||
pub use self::geometry::*;
|
pub use self::geometry::*;
|
||||||
pub use self::hint::*;
|
pub use self::hint::*;
|
||||||
pub use self::icon::*;
|
pub use self::icon::*;
|
||||||
pub use self::input::*;
|
pub use self::input::*;
|
||||||
|
pub use self::memory::*;
|
||||||
pub use self::randr::*;
|
pub use self::randr::*;
|
||||||
pub use self::window_property::*;
|
pub use self::window_property::*;
|
||||||
pub use self::wm::*;
|
pub use self::wm::*;
|
||||||
|
|
||||||
use std::mem;
|
use std::mem;
|
||||||
use std::ptr;
|
use std::ptr;
|
||||||
use std::str;
|
|
||||||
use std::sync::Arc;
|
|
||||||
use std::ops::{Deref, DerefMut};
|
|
||||||
use std::os::raw::*;
|
use std::os::raw::*;
|
||||||
|
|
||||||
use super::{ffi, XConnection, XError};
|
use super::{ffi, XConnection, XError};
|
||||||
|
|
||||||
// This isn't actually the number of the bits in the format.
|
|
||||||
// X11 does a match on this value to determine which type to call sizeof on.
|
|
||||||
// Thus, we use 32 for c_long, since 32 maps to c_long which maps to 64.
|
|
||||||
// ...if that sounds confusing, then you know why this enum is here.
|
|
||||||
#[derive(Debug, Copy, Clone)]
|
|
||||||
pub enum Format {
|
|
||||||
Char = 8,
|
|
||||||
Short = 16,
|
|
||||||
Long = 32,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Format {
|
|
||||||
pub fn from_format(format: usize) -> Option<Self> {
|
|
||||||
match format {
|
|
||||||
8 => Some(Format::Char),
|
|
||||||
16 => Some(Format::Short),
|
|
||||||
32 => Some(Format::Long),
|
|
||||||
_ => None,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn is_same_size_as<T>(&self) -> bool {
|
|
||||||
mem::size_of::<T>() == self.get_actual_size()
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn get_actual_size(&self) -> usize {
|
|
||||||
match self {
|
|
||||||
&Format::Char => mem::size_of::<c_char>(),
|
|
||||||
&Format::Short => mem::size_of::<c_short>(),
|
|
||||||
&Format::Long => mem::size_of::<c_long>(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub struct XSmartPointer<'a, T> {
|
|
||||||
xconn: &'a Arc<XConnection>,
|
|
||||||
pub ptr: *mut T,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'a, T> XSmartPointer<'a, T> {
|
|
||||||
// You're responsible for only passing things to this that should be XFree'd.
|
|
||||||
// Returns None if ptr is null.
|
|
||||||
pub fn new(xconn: &'a Arc<XConnection>, ptr: *mut T) -> Option<Self> {
|
|
||||||
if !ptr.is_null() {
|
|
||||||
Some(XSmartPointer {
|
|
||||||
xconn,
|
|
||||||
ptr,
|
|
||||||
})
|
|
||||||
} else {
|
|
||||||
None
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'a, T> Deref for XSmartPointer<'a, T> {
|
|
||||||
type Target = T;
|
|
||||||
|
|
||||||
fn deref(&self) -> &T {
|
|
||||||
unsafe { &*self.ptr }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'a, T> DerefMut for XSmartPointer<'a, T> {
|
|
||||||
fn deref_mut(&mut self) -> &mut T {
|
|
||||||
unsafe { &mut *self.ptr }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'a, T> Drop for XSmartPointer<'a, T> {
|
|
||||||
fn drop(&mut self) {
|
|
||||||
unsafe {
|
|
||||||
(self.xconn.xlib.XFree)(self.ptr as *mut _);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// This is impoartant, so pay attention!
|
|
||||||
// Xlib has an output buffer, and tries to hide the async nature of X from you.
|
|
||||||
// This buffer contains the requests you make, and is flushed under various circumstances:
|
|
||||||
// 1. XPending, XNextEvent, and XWindowEvent flush "as needed"
|
|
||||||
// 2. XFlush explicitly flushes
|
|
||||||
// 3. XSync flushes and blocks until all requests are responded to
|
|
||||||
// 4. Calls that have a return dependent on a response (i.e. XGetWindowProperty) sync internally.
|
|
||||||
// When in doubt, check the X11 source; if a function calls _XReply, it flushes and waits.
|
|
||||||
// All util functions that abstract an async function will return a Flusher.
|
|
||||||
pub unsafe fn flush_requests(xconn: &Arc<XConnection>) -> Result<(), XError> {
|
|
||||||
(xconn.xlib.XFlush)(xconn.display);
|
|
||||||
//println!("XFlush");
|
|
||||||
// This isn't necessarily a useful time to check for errors (since our request hasn't
|
|
||||||
// necessarily been processed yet)
|
|
||||||
xconn.check_errors()
|
|
||||||
}
|
|
||||||
|
|
||||||
pub unsafe fn sync_with_server(xconn: &Arc<XConnection>) -> Result<(), XError> {
|
|
||||||
(xconn.xlib.XSync)(xconn.display, ffi::False);
|
|
||||||
//println!("XSync");
|
|
||||||
xconn.check_errors()
|
|
||||||
}
|
|
||||||
|
|
||||||
#[must_use = "This request was made asynchronously, and is still in the output buffer. You must explicitly choose to either `.flush()` (empty the output buffer, sending the request now) or `.queue()` (wait to send the request, allowing you to continue to add more requests without additional round-trips). For more information, see the documentation for `util::flush_requests`."]
|
#[must_use = "This request was made asynchronously, and is still in the output buffer. You must explicitly choose to either `.flush()` (empty the output buffer, sending the request now) or `.queue()` (wait to send the request, allowing you to continue to add more requests without additional round-trips). For more information, see the documentation for `util::flush_requests`."]
|
||||||
pub struct Flusher<'a> {
|
pub struct Flusher<'a> {
|
||||||
xconn: &'a Arc<XConnection>,
|
xconn: &'a XConnection,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> Flusher<'a> {
|
impl<'a> Flusher<'a> {
|
||||||
pub fn new(xconn: &'a Arc<XConnection>) -> Self {
|
pub fn new(xconn: &'a XConnection) -> Self {
|
||||||
Flusher { xconn }
|
Flusher { xconn }
|
||||||
}
|
}
|
||||||
|
|
||||||
// "I want this request sent now!"
|
// "I want this request sent now!"
|
||||||
pub fn flush(self) -> Result<(), XError> {
|
pub fn flush(self) -> Result<(), XError> {
|
||||||
unsafe { flush_requests(self.xconn) }
|
self.xconn.flush_requests()
|
||||||
|
}
|
||||||
|
|
||||||
|
// "I want the response now too!"
|
||||||
|
pub fn sync(self) -> Result<(), XError> {
|
||||||
|
self.xconn.sync_with_server()
|
||||||
}
|
}
|
||||||
|
|
||||||
// "I'm aware that this request hasn't been sent, and I'm okay with waiting."
|
// "I'm aware that this request hasn't been sent, and I'm okay with waiting."
|
||||||
pub fn queue(self) {}
|
pub fn queue(self) {}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub unsafe fn send_client_msg(
|
impl XConnection {
|
||||||
xconn: &Arc<XConnection>,
|
// This is impoartant, so pay attention!
|
||||||
window: c_ulong, // The window this is "about"; not necessarily this window
|
// Xlib has an output buffer, and tries to hide the async nature of X from you.
|
||||||
target_window: c_ulong, // The window we're sending to
|
// This buffer contains the requests you make, and is flushed under various circumstances:
|
||||||
message_type: ffi::Atom,
|
// 1. `XPending`, `XNextEvent`, and `XWindowEvent` flush "as needed"
|
||||||
event_mask: Option<c_long>,
|
// 2. `XFlush` explicitly flushes
|
||||||
data: (c_long, c_long, c_long, c_long, c_long),
|
// 3. `XSync` flushes and blocks until all requests are responded to
|
||||||
) -> Flusher {
|
// 4. Calls that have a return dependent on a response (i.e. `XGetWindowProperty`) sync internally.
|
||||||
let mut event: ffi::XClientMessageEvent = mem::uninitialized();
|
// When in doubt, check the X11 source; if a function calls `_XReply`, it flushes and waits.
|
||||||
event.type_ = ffi::ClientMessage;
|
// All util functions that abstract an async function will return a `Flusher`.
|
||||||
event.display = xconn.display;
|
pub fn flush_requests(&self) -> Result<(), XError> {
|
||||||
event.window = window;
|
unsafe { (self.xlib.XFlush)(self.display) };
|
||||||
event.message_type = message_type;
|
//println!("XFlush");
|
||||||
event.format = Format::Long as c_int;
|
// This isn't necessarily a useful time to check for errors (since our request hasn't
|
||||||
event.data = ffi::ClientMessageData::new();
|
// necessarily been processed yet)
|
||||||
event.data.set_long(0, data.0);
|
self.check_errors()
|
||||||
event.data.set_long(1, data.1);
|
}
|
||||||
event.data.set_long(2, data.2);
|
|
||||||
event.data.set_long(3, data.3);
|
|
||||||
event.data.set_long(4, data.4);
|
|
||||||
|
|
||||||
let event_mask = event_mask.unwrap_or(ffi::NoEventMask);
|
pub fn sync_with_server(&self) -> Result<(), XError> {
|
||||||
|
unsafe { (self.xlib.XSync)(self.display, ffi::False) };
|
||||||
(xconn.xlib.XSendEvent)(
|
//println!("XSync");
|
||||||
xconn.display,
|
self.check_errors()
|
||||||
target_window,
|
}
|
||||||
ffi::False,
|
|
||||||
event_mask,
|
|
||||||
&mut event.into(),
|
|
||||||
);
|
|
||||||
|
|
||||||
Flusher::new(xconn)
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -60,25 +60,23 @@ pub fn calc_dpi_factor(
|
||||||
((ppmm * (12.0 * 25.4 / 96.0)).round() / 12.0).max(1.0)
|
((ppmm * (12.0 * 25.4 / 96.0)).round() / 12.0).max(1.0)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub unsafe fn get_output_info(
|
impl XConnection {
|
||||||
xconn: &Arc<XConnection>,
|
pub unsafe fn get_output_info(&self, resources: *mut XRRScreenResources, repr: &MonitorRepr) -> (String, f32) {
|
||||||
resources: *mut XRRScreenResources,
|
let output_info = (self.xrandr.XRRGetOutputInfo)(
|
||||||
repr: &MonitorRepr,
|
self.display,
|
||||||
) -> (String, f32) {
|
resources,
|
||||||
let output_info = (xconn.xrandr.XRRGetOutputInfo)(
|
repr.get_output(),
|
||||||
xconn.display,
|
);
|
||||||
resources,
|
let name_slice = slice::from_raw_parts(
|
||||||
repr.get_output(),
|
(*output_info).name as *mut u8,
|
||||||
);
|
(*output_info).nameLen as usize,
|
||||||
let name_slice = slice::from_raw_parts(
|
);
|
||||||
(*output_info).name as *mut u8,
|
let name = String::from_utf8_lossy(name_slice).into();
|
||||||
(*output_info).nameLen as usize,
|
let hidpi_factor = calc_dpi_factor(
|
||||||
);
|
repr.get_dimensions(),
|
||||||
let name = String::from_utf8_lossy(name_slice).into();
|
((*output_info).mm_width as u64, (*output_info).mm_height as u64),
|
||||||
let hidpi_factor = calc_dpi_factor(
|
) as f32;
|
||||||
repr.get_dimensions(),
|
(self.xrandr.XRRFreeOutputInfo)(output_info);
|
||||||
((*output_info).mm_width as u64, (*output_info).mm_height as u64),
|
(name, hidpi_factor)
|
||||||
) as f32;
|
}
|
||||||
(xconn.xrandr.XRRFreeOutputInfo)(output_info);
|
|
||||||
(name, hidpi_factor)
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,5 +1,4 @@
|
||||||
use std;
|
use std;
|
||||||
use std::fmt::Debug;
|
|
||||||
|
|
||||||
use super::*;
|
use super::*;
|
||||||
|
|
||||||
|
@ -25,139 +24,121 @@ impl GetPropertyError {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Number of 32-bit chunks to retrieve per iteration of get_property's inner loop.
|
// Number of 32-bit chunks to retrieve per iteration of get_property's inner loop.
|
||||||
// To test if get_property works correctly, set this to 1.
|
// To test if `get_property` works correctly, set this to 1.
|
||||||
const PROPERTY_BUFFER_SIZE: c_long = 1024; // 4k of RAM ought to be enough for anyone!
|
const PROPERTY_BUFFER_SIZE: c_long = 1024; // 4k of RAM ought to be enough for anyone!
|
||||||
|
|
||||||
pub unsafe fn get_property<T: Debug + Clone>(
|
|
||||||
xconn: &Arc<XConnection>,
|
|
||||||
window: c_ulong,
|
|
||||||
property: ffi::Atom,
|
|
||||||
property_type: ffi::Atom,
|
|
||||||
) -> Result<Vec<T>, GetPropertyError> {
|
|
||||||
let mut data = Vec::new();
|
|
||||||
let mut offset = 0;
|
|
||||||
|
|
||||||
let mut done = false;
|
|
||||||
while !done {
|
|
||||||
let mut actual_type: ffi::Atom = mem::uninitialized();
|
|
||||||
let mut actual_format: c_int = mem::uninitialized();
|
|
||||||
let mut quantity_returned: c_ulong = mem::uninitialized();
|
|
||||||
let mut bytes_after: c_ulong = mem::uninitialized();
|
|
||||||
let mut buf: *mut c_uchar = ptr::null_mut();
|
|
||||||
(xconn.xlib.XGetWindowProperty)(
|
|
||||||
xconn.display,
|
|
||||||
window,
|
|
||||||
property,
|
|
||||||
// This offset is in terms of 32-bit chunks.
|
|
||||||
offset,
|
|
||||||
// This is the quanity of 32-bit chunks to receive at once.
|
|
||||||
PROPERTY_BUFFER_SIZE,
|
|
||||||
ffi::False,
|
|
||||||
property_type,
|
|
||||||
&mut actual_type,
|
|
||||||
&mut actual_format,
|
|
||||||
// This is the quantity of items we retrieved in our format, NOT of 32-bit chunks!
|
|
||||||
&mut quantity_returned,
|
|
||||||
// ...and this is a quantity of bytes. So, this function deals in 3 different units.
|
|
||||||
&mut bytes_after,
|
|
||||||
&mut buf,
|
|
||||||
);
|
|
||||||
|
|
||||||
if let Err(e) = xconn.check_errors() {
|
|
||||||
return Err(GetPropertyError::XError(e));
|
|
||||||
}
|
|
||||||
|
|
||||||
if actual_type != property_type {
|
|
||||||
return Err(GetPropertyError::TypeMismatch(actual_type));
|
|
||||||
}
|
|
||||||
|
|
||||||
let format_mismatch = Format::from_format(actual_format as _)
|
|
||||||
.map(|actual_format| !actual_format.is_same_size_as::<T>())
|
|
||||||
// This won't actually be reached; the XError condition above is triggered first.
|
|
||||||
.unwrap_or(true);
|
|
||||||
|
|
||||||
if format_mismatch {
|
|
||||||
return Err(GetPropertyError::FormatMismatch(actual_format));
|
|
||||||
}
|
|
||||||
|
|
||||||
if !buf.is_null() {
|
|
||||||
offset += PROPERTY_BUFFER_SIZE;
|
|
||||||
let new_data = std::slice::from_raw_parts(
|
|
||||||
buf as *mut T,
|
|
||||||
quantity_returned as usize,
|
|
||||||
);
|
|
||||||
/*println!(
|
|
||||||
"XGetWindowProperty prop:{:?} fmt:{:02} len:{:02} off:{:02} out:{:02}, buf:{:?}",
|
|
||||||
property,
|
|
||||||
mem::size_of::<T>() * 8,
|
|
||||||
data.len(),
|
|
||||||
offset,
|
|
||||||
quantity_returned,
|
|
||||||
new_data,
|
|
||||||
);*/
|
|
||||||
data.extend_from_slice(&new_data);
|
|
||||||
// Fun fact: XGetWindowProperty allocates one extra byte at the end.
|
|
||||||
(xconn.xlib.XFree)(buf as _); // Don't try to access new_data after this.
|
|
||||||
} else {
|
|
||||||
return Err(GetPropertyError::NothingAllocated);
|
|
||||||
}
|
|
||||||
|
|
||||||
done = bytes_after == 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
Ok(data)
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub enum PropMode {
|
pub enum PropMode {
|
||||||
Replace = ffi::PropModeReplace as isize,
|
Replace = ffi::PropModeReplace as isize,
|
||||||
_Prepend = ffi::PropModePrepend as isize,
|
Prepend = ffi::PropModePrepend as isize,
|
||||||
_Append = ffi::PropModeAppend as isize,
|
Append = ffi::PropModeAppend as isize,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
impl XConnection {
|
||||||
pub struct InvalidFormat {
|
pub fn get_property<T: Formattable>(
|
||||||
format_used: Format,
|
&self,
|
||||||
size_passed: usize,
|
window: c_ulong,
|
||||||
size_expected: usize,
|
property: ffi::Atom,
|
||||||
}
|
property_type: ffi::Atom,
|
||||||
|
) -> Result<Vec<T>, GetPropertyError> {
|
||||||
|
let mut data = Vec::new();
|
||||||
|
let mut offset = 0;
|
||||||
|
|
||||||
pub unsafe fn change_property<'a, T: Debug>(
|
let mut done = false;
|
||||||
xconn: &'a Arc<XConnection>,
|
while !done {
|
||||||
window: c_ulong,
|
unsafe {
|
||||||
property: ffi::Atom,
|
let mut actual_type: ffi::Atom = mem::uninitialized();
|
||||||
property_type: ffi::Atom,
|
let mut actual_format: c_int = mem::uninitialized();
|
||||||
format: Format,
|
let mut quantity_returned: c_ulong = mem::uninitialized();
|
||||||
mode: PropMode,
|
let mut bytes_after: c_ulong = mem::uninitialized();
|
||||||
new_value: &[T],
|
let mut buf: *mut c_uchar = ptr::null_mut();
|
||||||
) -> Flusher<'a> {
|
(self.xlib.XGetWindowProperty)(
|
||||||
if !format.is_same_size_as::<T>() {
|
self.display,
|
||||||
panic!(format!(
|
window,
|
||||||
"[winit developer error] Incorrect usage of `util::change_property`: {:#?}",
|
property,
|
||||||
InvalidFormat {
|
// This offset is in terms of 32-bit chunks.
|
||||||
format_used: format,
|
offset,
|
||||||
size_passed: mem::size_of::<T>() * 8,
|
// This is the quanity of 32-bit chunks to receive at once.
|
||||||
size_expected: format.get_actual_size() * 8,
|
PROPERTY_BUFFER_SIZE,
|
||||||
},
|
ffi::False,
|
||||||
));
|
property_type,
|
||||||
|
&mut actual_type,
|
||||||
|
&mut actual_format,
|
||||||
|
// This is the quantity of items we retrieved in our format, NOT of 32-bit chunks!
|
||||||
|
&mut quantity_returned,
|
||||||
|
// ...and this is a quantity of bytes. So, this function deals in 3 different units.
|
||||||
|
&mut bytes_after,
|
||||||
|
&mut buf,
|
||||||
|
);
|
||||||
|
|
||||||
|
if let Err(e) = self.check_errors() {
|
||||||
|
return Err(GetPropertyError::XError(e));
|
||||||
|
}
|
||||||
|
|
||||||
|
if actual_type != property_type {
|
||||||
|
return Err(GetPropertyError::TypeMismatch(actual_type));
|
||||||
|
}
|
||||||
|
|
||||||
|
let format_mismatch = Format::from_format(actual_format as _) != Some(T::FORMAT);
|
||||||
|
if format_mismatch {
|
||||||
|
return Err(GetPropertyError::FormatMismatch(actual_format));
|
||||||
|
}
|
||||||
|
|
||||||
|
if !buf.is_null() {
|
||||||
|
offset += PROPERTY_BUFFER_SIZE;
|
||||||
|
let new_data = std::slice::from_raw_parts(
|
||||||
|
buf as *mut T,
|
||||||
|
quantity_returned as usize,
|
||||||
|
);
|
||||||
|
/*println!(
|
||||||
|
"XGetWindowProperty prop:{:?} fmt:{:02} len:{:02} off:{:02} out:{:02}, buf:{:?}",
|
||||||
|
property,
|
||||||
|
mem::size_of::<T>() * 8,
|
||||||
|
data.len(),
|
||||||
|
offset,
|
||||||
|
quantity_returned,
|
||||||
|
new_data,
|
||||||
|
);*/
|
||||||
|
data.extend_from_slice(&new_data);
|
||||||
|
// Fun fact: XGetWindowProperty allocates one extra byte at the end.
|
||||||
|
(self.xlib.XFree)(buf as _); // Don't try to access new_data after this.
|
||||||
|
} else {
|
||||||
|
return Err(GetPropertyError::NothingAllocated);
|
||||||
|
}
|
||||||
|
|
||||||
|
done = bytes_after == 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(data)
|
||||||
}
|
}
|
||||||
|
|
||||||
(xconn.xlib.XChangeProperty)(
|
pub fn change_property<'a, T: Formattable>(
|
||||||
xconn.display,
|
&'a self,
|
||||||
window,
|
window: c_ulong,
|
||||||
property,
|
property: ffi::Atom,
|
||||||
property_type,
|
property_type: ffi::Atom,
|
||||||
format as c_int,
|
mode: PropMode,
|
||||||
mode as c_int,
|
new_value: &[T],
|
||||||
new_value.as_ptr() as *const c_uchar,
|
) -> Flusher<'a> {
|
||||||
new_value.len() as c_int,
|
debug_assert_eq!(mem::size_of::<T>(), T::FORMAT.get_actual_size());
|
||||||
);
|
unsafe {
|
||||||
|
(self.xlib.XChangeProperty)(
|
||||||
/*println!(
|
self.display,
|
||||||
"XChangeProperty prop:{:?} val:{:?}",
|
window,
|
||||||
property,
|
property,
|
||||||
new_value,
|
property_type,
|
||||||
);*/
|
T::FORMAT as c_int,
|
||||||
|
mode as c_int,
|
||||||
Flusher::new(xconn)
|
new_value.as_ptr() as *const c_uchar,
|
||||||
|
new_value.len() as c_int,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
/*println!(
|
||||||
|
"XChangeProperty prop:{:?} val:{:?}",
|
||||||
|
property,
|
||||||
|
new_value,
|
||||||
|
);*/
|
||||||
|
Flusher::new(self)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -20,139 +20,122 @@ pub fn wm_name_is_one_of(names: &[&str]) -> bool {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn update_cached_wm_info(xconn: &Arc<XConnection>, root: ffi::Window) {
|
impl XConnection {
|
||||||
*SUPPORTED_HINTS.lock() = self::get_supported_hints(xconn, root);
|
pub fn update_cached_wm_info(&self, root: ffi::Window) {
|
||||||
*WM_NAME.lock() = self::get_wm_name(xconn, root);
|
*SUPPORTED_HINTS.lock() = self.get_supported_hints(root);
|
||||||
}
|
*WM_NAME.lock() = self.get_wm_name(root);
|
||||||
|
}
|
||||||
|
|
||||||
fn get_supported_hints(xconn: &Arc<XConnection>, root: ffi::Window) -> Vec<ffi::Atom> {
|
fn get_supported_hints(&self, root: ffi::Window) -> Vec<ffi::Atom> {
|
||||||
let supported_atom = unsafe { self::get_atom(xconn, b"_NET_SUPPORTED\0") }
|
let supported_atom = unsafe { self.get_atom_unchecked(b"_NET_SUPPORTED\0") };
|
||||||
.expect("Failed to call XInternAtom (_NET_SUPPORTED)");
|
self.get_property(
|
||||||
unsafe {
|
|
||||||
self::get_property(
|
|
||||||
xconn,
|
|
||||||
root,
|
root,
|
||||||
supported_atom,
|
supported_atom,
|
||||||
ffi::XA_ATOM,
|
ffi::XA_ATOM,
|
||||||
)
|
).unwrap_or_else(|_| Vec::with_capacity(0))
|
||||||
}.unwrap_or_else(|_| Vec::with_capacity(0))
|
}
|
||||||
}
|
|
||||||
|
|
||||||
fn get_wm_name(xconn: &Arc<XConnection>, root: ffi::Window) -> Option<String> {
|
fn get_wm_name(&self, root: ffi::Window) -> Option<String> {
|
||||||
let check_atom = unsafe { self::get_atom(xconn, b"_NET_SUPPORTING_WM_CHECK\0") }
|
let check_atom = unsafe { self.get_atom_unchecked(b"_NET_SUPPORTING_WM_CHECK\0") };
|
||||||
.expect("Failed to call XInternAtom (_NET_SUPPORTING_WM_CHECK)");
|
let wm_name_atom = unsafe { self.get_atom_unchecked(b"_NET_WM_NAME\0") };
|
||||||
let wm_name_atom = unsafe { self::get_atom(xconn, b"_NET_WM_NAME\0") }
|
|
||||||
.expect("Failed to call XInternAtom (_NET_WM_NAME)");
|
|
||||||
|
|
||||||
// Mutter/Muffin/Budgie doesn't have _NET_SUPPORTING_WM_CHECK in its _NET_SUPPORTED, despite
|
// Mutter/Muffin/Budgie doesn't have _NET_SUPPORTING_WM_CHECK in its _NET_SUPPORTED, despite
|
||||||
// it working and being supported. This has been reported upstream, but due to the
|
// it working and being supported. This has been reported upstream, but due to the
|
||||||
// inavailability of time machines, we'll just try to get _NET_SUPPORTING_WM_CHECK
|
// inavailability of time machines, we'll just try to get _NET_SUPPORTING_WM_CHECK
|
||||||
// regardless of whether or not the WM claims to support it.
|
// regardless of whether or not the WM claims to support it.
|
||||||
//
|
//
|
||||||
// Blackbox 0.70 also incorrectly reports not supporting this, though that appears to be fixed
|
// Blackbox 0.70 also incorrectly reports not supporting this, though that appears to be fixed
|
||||||
// in 0.72.
|
// in 0.72.
|
||||||
/*if !supported_hints.contains(&check_atom) {
|
/*if !supported_hints.contains(&check_atom) {
|
||||||
return None;
|
return None;
|
||||||
}*/
|
}*/
|
||||||
|
|
||||||
// IceWM (1.3.x and earlier) doesn't report supporting _NET_WM_NAME, but will nonetheless
|
// IceWM (1.3.x and earlier) doesn't report supporting _NET_WM_NAME, but will nonetheless
|
||||||
// provide us with a value for it. Note that the unofficial 1.4 fork of IceWM works fine.
|
// provide us with a value for it. Note that the unofficial 1.4 fork of IceWM works fine.
|
||||||
/*if !supported_hints.contains(&wm_name_atom) {
|
/*if !supported_hints.contains(&wm_name_atom) {
|
||||||
return None;
|
return None;
|
||||||
}*/
|
}*/
|
||||||
|
|
||||||
// Of the WMs tested, only xmonad and dwm fail to provide a WM name.
|
// Of the WMs tested, only xmonad and dwm fail to provide a WM name.
|
||||||
|
|
||||||
// Querying this property on the root window will give us the ID of a child window created by
|
// Querying this property on the root window will give us the ID of a child window created by
|
||||||
// the WM.
|
// the WM.
|
||||||
let root_window_wm_check = {
|
let root_window_wm_check = {
|
||||||
let result = unsafe {
|
let result = self.get_property(
|
||||||
self::get_property(
|
|
||||||
xconn,
|
|
||||||
root,
|
root,
|
||||||
check_atom,
|
check_atom,
|
||||||
ffi::XA_WINDOW,
|
ffi::XA_WINDOW,
|
||||||
)
|
);
|
||||||
|
|
||||||
|
let wm_check = result
|
||||||
|
.ok()
|
||||||
|
.and_then(|wm_check| wm_check.get(0).cloned());
|
||||||
|
|
||||||
|
if let Some(wm_check) = wm_check {
|
||||||
|
wm_check
|
||||||
|
} else {
|
||||||
|
return None;
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
let wm_check = result
|
// Querying the same property on the child window we were given, we should get this child
|
||||||
.ok()
|
// window's ID again.
|
||||||
.and_then(|wm_check| wm_check.get(0).cloned());
|
let child_window_wm_check = {
|
||||||
|
let result = self.get_property(
|
||||||
if let Some(wm_check) = wm_check {
|
|
||||||
wm_check
|
|
||||||
} else {
|
|
||||||
return None;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
// Querying the same property on the child window we were given, we should get this child
|
|
||||||
// window's ID again.
|
|
||||||
let child_window_wm_check = {
|
|
||||||
let result = unsafe {
|
|
||||||
self::get_property(
|
|
||||||
xconn,
|
|
||||||
root_window_wm_check,
|
root_window_wm_check,
|
||||||
check_atom,
|
check_atom,
|
||||||
ffi::XA_WINDOW,
|
ffi::XA_WINDOW,
|
||||||
)
|
);
|
||||||
|
|
||||||
|
let wm_check = result
|
||||||
|
.ok()
|
||||||
|
.and_then(|wm_check| wm_check.get(0).cloned());
|
||||||
|
|
||||||
|
if let Some(wm_check) = wm_check {
|
||||||
|
wm_check
|
||||||
|
} else {
|
||||||
|
return None;
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
let wm_check = result
|
// These values should be the same.
|
||||||
.ok()
|
if root_window_wm_check != child_window_wm_check {
|
||||||
.and_then(|wm_check| wm_check.get(0).cloned());
|
|
||||||
|
|
||||||
if let Some(wm_check) = wm_check {
|
|
||||||
wm_check
|
|
||||||
} else {
|
|
||||||
return None;
|
return None;
|
||||||
}
|
}
|
||||||
};
|
|
||||||
|
|
||||||
// These values should be the same.
|
// All of that work gives us a window ID that we can get the WM name from.
|
||||||
if root_window_wm_check != child_window_wm_check {
|
let wm_name = {
|
||||||
return None;
|
let utf8_string_atom = unsafe { self.get_atom_unchecked(b"UTF8_STRING\0") };
|
||||||
}
|
|
||||||
|
|
||||||
// All of that work gives us a window ID that we can get the WM name from.
|
let result = self.get_property(
|
||||||
let wm_name = {
|
|
||||||
let utf8_string_atom = unsafe { self::get_atom(xconn, b"UTF8_STRING\0") }
|
|
||||||
.expect("Failed to call XInternAtom (UTF8_STRING)");
|
|
||||||
|
|
||||||
let result = unsafe {
|
|
||||||
self::get_property(
|
|
||||||
xconn,
|
|
||||||
root_window_wm_check,
|
root_window_wm_check,
|
||||||
wm_name_atom,
|
wm_name_atom,
|
||||||
utf8_string_atom,
|
utf8_string_atom,
|
||||||
)
|
);
|
||||||
};
|
|
||||||
|
|
||||||
// IceWM requires this. IceWM was also the only WM tested that returns a null-terminated
|
// IceWM requires this. IceWM was also the only WM tested that returns a null-terminated
|
||||||
// string. For more fun trivia, IceWM is also unique in including version and uname
|
// string. For more fun trivia, IceWM is also unique in including version and uname
|
||||||
// information in this string (this means you'll have to be careful if you want to match
|
// information in this string (this means you'll have to be careful if you want to match
|
||||||
// against it, though).
|
// against it, though).
|
||||||
// The unofficial 1.4 fork of IceWM still includes the extra details, but properly
|
// The unofficial 1.4 fork of IceWM still includes the extra details, but properly
|
||||||
// returns a UTF8 string that isn't null-terminated.
|
// returns a UTF8 string that isn't null-terminated.
|
||||||
let no_utf8 = if let Err(ref err) = result {
|
let no_utf8 = if let Err(ref err) = result {
|
||||||
err.is_actual_property_type(ffi::XA_STRING)
|
err.is_actual_property_type(ffi::XA_STRING)
|
||||||
} else {
|
} else {
|
||||||
false
|
false
|
||||||
};
|
};
|
||||||
|
|
||||||
if no_utf8 {
|
if no_utf8 {
|
||||||
unsafe {
|
self.get_property(
|
||||||
self::get_property(
|
|
||||||
xconn,
|
|
||||||
root_window_wm_check,
|
root_window_wm_check,
|
||||||
wm_name_atom,
|
wm_name_atom,
|
||||||
ffi::XA_STRING,
|
ffi::XA_STRING,
|
||||||
)
|
)
|
||||||
|
} else {
|
||||||
|
result
|
||||||
}
|
}
|
||||||
} else {
|
}.ok();
|
||||||
result
|
|
||||||
}
|
|
||||||
}.ok();
|
|
||||||
|
|
||||||
wm_name.and_then(|wm_name| String::from_utf8(wm_name).ok())
|
wm_name.and_then(|wm_name| String::from_utf8(wm_name).ok())
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -169,15 +169,12 @@ impl Window2 {
|
||||||
|
|
||||||
// Enable drag and drop (TODO: extend API to make this toggleable)
|
// Enable drag and drop (TODO: extend API to make this toggleable)
|
||||||
unsafe {
|
unsafe {
|
||||||
let dnd_aware_atom = util::get_atom(xconn, b"XdndAware\0")
|
let dnd_aware_atom = xconn.get_atom_unchecked(b"XdndAware\0");
|
||||||
.expect("Failed to call XInternAtom (XdndAware)");
|
|
||||||
let version = &[5 as c_ulong]; // Latest version; hasn't changed since 2002
|
let version = &[5 as c_ulong]; // Latest version; hasn't changed since 2002
|
||||||
util::change_property(
|
xconn.change_property(
|
||||||
xconn,
|
|
||||||
x_window.window,
|
x_window.window,
|
||||||
dnd_aware_atom,
|
dnd_aware_atom,
|
||||||
ffi::XA_ATOM,
|
ffi::XA_ATOM,
|
||||||
util::Format::Long,
|
|
||||||
util::PropMode::Replace,
|
util::PropMode::Replace,
|
||||||
version,
|
version,
|
||||||
)
|
)
|
||||||
|
@ -185,11 +182,6 @@ impl Window2 {
|
||||||
|
|
||||||
// WM_CLASS must be set *before* mapping the window, as per ICCCM!
|
// WM_CLASS must be set *before* mapping the window, as per ICCCM!
|
||||||
{
|
{
|
||||||
let mut class_hints = {
|
|
||||||
let class_hints = unsafe { (xconn.xlib.XAllocClassHint)() };
|
|
||||||
util::XSmartPointer::new(xconn, class_hints)
|
|
||||||
}.expect("`XAllocClassHint` returned null; out of memory");
|
|
||||||
|
|
||||||
let (class, instance) = if let Some((instance, class)) = pl_attribs.class {
|
let (class, instance) = if let Some((instance, class)) = pl_attribs.class {
|
||||||
let instance = CString::new(instance.as_str())
|
let instance = CString::new(instance.as_str())
|
||||||
.expect("`WM_CLASS` instance contained null byte");
|
.expect("`WM_CLASS` instance contained null byte");
|
||||||
|
@ -216,14 +208,15 @@ impl Window2 {
|
||||||
(instance, class)
|
(instance, class)
|
||||||
};
|
};
|
||||||
|
|
||||||
(*class_hints).res_name = class.as_ptr() as *mut c_char;
|
let mut class_hint = xconn.alloc_class_hint();
|
||||||
(*class_hints).res_class = instance.as_ptr() as *mut c_char;
|
(*class_hint).res_name = class.as_ptr() as *mut c_char;
|
||||||
|
(*class_hint).res_class = instance.as_ptr() as *mut c_char;
|
||||||
|
|
||||||
unsafe {
|
unsafe {
|
||||||
(xconn.xlib.XSetClassHint)(
|
(xconn.xlib.XSetClassHint)(
|
||||||
xconn.display,
|
xconn.display,
|
||||||
x_window.window,
|
x_window.window,
|
||||||
class_hints.ptr,
|
class_hint.ptr,
|
||||||
);
|
);
|
||||||
}//.queue();
|
}//.queue();
|
||||||
}
|
}
|
||||||
|
@ -241,10 +234,7 @@ impl Window2 {
|
||||||
|
|
||||||
// set size hints
|
// set size hints
|
||||||
{
|
{
|
||||||
let mut size_hints = {
|
let mut size_hints = xconn.alloc_size_hints();
|
||||||
let size_hints = unsafe { (xconn.xlib.XAllocSizeHints)() };
|
|
||||||
util::XSmartPointer::new(xconn, size_hints)
|
|
||||||
}.expect("XAllocSizeHints returned null; out of memory");
|
|
||||||
(*size_hints).flags = ffi::PSize;
|
(*size_hints).flags = ffi::PSize;
|
||||||
(*size_hints).width = dimensions.0 as c_int;
|
(*size_hints).width = dimensions.0 as c_int;
|
||||||
(*size_hints).height = dimensions.1 as c_int;
|
(*size_hints).height = dimensions.1 as c_int;
|
||||||
|
@ -330,14 +320,7 @@ impl Window2 {
|
||||||
}
|
}
|
||||||
mask
|
mask
|
||||||
};
|
};
|
||||||
unsafe {
|
xconn.select_xinput_events(x_window.window, ffi::XIAllMasterDevices, mask).queue();
|
||||||
util::select_xinput_events(
|
|
||||||
xconn,
|
|
||||||
x_window.window,
|
|
||||||
ffi::XIAllMasterDevices,
|
|
||||||
mask,
|
|
||||||
)
|
|
||||||
}.queue();
|
|
||||||
|
|
||||||
// These properties must be set after mapping
|
// These properties must be set after mapping
|
||||||
if window_attrs.maximized {
|
if window_attrs.maximized {
|
||||||
|
@ -372,7 +355,7 @@ impl Window2 {
|
||||||
}
|
}
|
||||||
|
|
||||||
// We never want to give the user a broken window, since by then, it's too late to handle.
|
// We never want to give the user a broken window, since by then, it's too late to handle.
|
||||||
unsafe { util::sync_with_server(xconn) }
|
xconn.sync_with_server()
|
||||||
.map(|_| window)
|
.map(|_| window)
|
||||||
.map_err(|x_err| OsError(
|
.map_err(|x_err| OsError(
|
||||||
format!("X server returned error while building window: {:?}", x_err)
|
format!("X server returned error while building window: {:?}", x_err)
|
||||||
|
@ -380,10 +363,8 @@ impl Window2 {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn set_pid(xconn: &Arc<XConnection>, window: ffi::Window) -> Option<util::Flusher> {
|
fn set_pid(xconn: &Arc<XConnection>, window: ffi::Window) -> Option<util::Flusher> {
|
||||||
let pid_atom = unsafe { util::get_atom(xconn, b"_NET_WM_PID\0") }
|
let pid_atom = unsafe { xconn.get_atom_unchecked(b"_NET_WM_PID\0") };
|
||||||
.expect("Failed to call XInternAtom (_NET_WM_PID)");
|
let client_machine_atom = unsafe { xconn.get_atom_unchecked(b"WM_CLIENT_MACHINE\0") };
|
||||||
let client_machine_atom = unsafe { util::get_atom(xconn, b"WM_CLIENT_MACHINE\0") }
|
|
||||||
.expect("Failed to call XInternAtom (WM_CLIENT_MACHINE)");
|
|
||||||
unsafe {
|
unsafe {
|
||||||
let (hostname, hostname_length) = {
|
let (hostname, hostname_length) = {
|
||||||
// 64 would suffice for Linux, but 256 will be enough everywhere (as per SUSv2). For instance, this is
|
// 64 would suffice for Linux, but 256 will be enough everywhere (as per SUSv2). For instance, this is
|
||||||
|
@ -396,21 +377,17 @@ impl Window2 {
|
||||||
let hostname_length = libc::strlen(hostname.as_ptr());
|
let hostname_length = libc::strlen(hostname.as_ptr());
|
||||||
(hostname, hostname_length as usize)
|
(hostname, hostname_length as usize)
|
||||||
};
|
};
|
||||||
util::change_property(
|
xconn.change_property(
|
||||||
xconn,
|
|
||||||
window,
|
window,
|
||||||
pid_atom,
|
pid_atom,
|
||||||
ffi::XA_CARDINAL,
|
ffi::XA_CARDINAL,
|
||||||
util::Format::Long,
|
|
||||||
util::PropMode::Replace,
|
util::PropMode::Replace,
|
||||||
&[libc::getpid() as util::Cardinal],
|
&[libc::getpid() as util::Cardinal],
|
||||||
).queue();
|
).queue();
|
||||||
let flusher = util::change_property(
|
let flusher = xconn.change_property(
|
||||||
xconn,
|
|
||||||
window,
|
window,
|
||||||
client_machine_atom,
|
client_machine_atom,
|
||||||
ffi::XA_STRING,
|
ffi::XA_STRING,
|
||||||
util::Format::Char,
|
|
||||||
util::PropMode::Replace,
|
util::PropMode::Replace,
|
||||||
&hostname[0..hostname_length],
|
&hostname[0..hostname_length],
|
||||||
);
|
);
|
||||||
|
@ -423,50 +400,26 @@ impl Window2 {
|
||||||
window: ffi::Window,
|
window: ffi::Window,
|
||||||
window_type: util::WindowType,
|
window_type: util::WindowType,
|
||||||
) -> util::Flusher {
|
) -> util::Flusher {
|
||||||
let hint_atom = unsafe { util::get_atom(xconn, b"_NET_WM_WINDOW_TYPE\0") }
|
let hint_atom = unsafe { xconn.get_atom_unchecked(b"_NET_WM_WINDOW_TYPE\0") };
|
||||||
.expect("Failed to call XInternAtom (_NET_WM_WINDOW_TYPE)");
|
|
||||||
let window_type_atom = window_type.as_atom(xconn);
|
let window_type_atom = window_type.as_atom(xconn);
|
||||||
unsafe {
|
xconn.change_property(
|
||||||
util::change_property(
|
window,
|
||||||
xconn,
|
hint_atom,
|
||||||
window,
|
ffi::XA_ATOM,
|
||||||
hint_atom,
|
util::PropMode::Replace,
|
||||||
ffi::XA_ATOM,
|
&[window_type_atom],
|
||||||
util::Format::Long,
|
)
|
||||||
util::PropMode::Replace,
|
|
||||||
&[window_type_atom],
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn set_urgent(&self, is_urgent: bool) {
|
pub fn set_urgent(&self, is_urgent: bool) {
|
||||||
let xconn = &self.x.display;
|
let xconn = &self.x.display;
|
||||||
|
let mut wm_hints = xconn.get_wm_hints(self.x.window).expect("`XGetWMHints` failed");
|
||||||
let mut wm_hints = {
|
|
||||||
let mut wm_hints = unsafe {
|
|
||||||
(xconn.xlib.XGetWMHints)(xconn.display, self.x.window)
|
|
||||||
};
|
|
||||||
xconn.check_errors().expect("`XGetWMHints` failed");
|
|
||||||
if wm_hints.is_null() {
|
|
||||||
wm_hints = unsafe { (xconn.xlib.XAllocWMHints)() };
|
|
||||||
}
|
|
||||||
util::XSmartPointer::new(xconn, wm_hints)
|
|
||||||
}.expect("`XAllocWMHints` returned null; out of memory");
|
|
||||||
|
|
||||||
if is_urgent {
|
if is_urgent {
|
||||||
(*wm_hints).flags |= ffi::XUrgencyHint;
|
(*wm_hints).flags |= ffi::XUrgencyHint;
|
||||||
} else {
|
} else {
|
||||||
(*wm_hints).flags &= !ffi::XUrgencyHint;
|
(*wm_hints).flags &= !ffi::XUrgencyHint;
|
||||||
}
|
}
|
||||||
|
xconn.set_wm_hints(self.x.window, wm_hints).flush().expect("Failed to set urgency hint");
|
||||||
unsafe {
|
|
||||||
(xconn.xlib.XSetWMHints)(
|
|
||||||
xconn.display,
|
|
||||||
self.x.window,
|
|
||||||
wm_hints.ptr,
|
|
||||||
);
|
|
||||||
util::flush_requests(xconn).expect("Failed to set urgency hint");
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn set_netwm(
|
fn set_netwm(
|
||||||
|
@ -476,33 +429,25 @@ impl Window2 {
|
||||||
properties: (c_long, c_long, c_long, c_long),
|
properties: (c_long, c_long, c_long, c_long),
|
||||||
operation: util::StateOperation
|
operation: util::StateOperation
|
||||||
) -> util::Flusher {
|
) -> util::Flusher {
|
||||||
let state_atom = unsafe { util::get_atom(xconn, b"_NET_WM_STATE\0") }
|
let state_atom = unsafe { xconn.get_atom_unchecked(b"_NET_WM_STATE\0") };
|
||||||
.expect("Failed to call XInternAtom (_NET_WM_STATE)");
|
xconn.send_client_msg(
|
||||||
|
window,
|
||||||
unsafe {
|
root,
|
||||||
util::send_client_msg(
|
state_atom,
|
||||||
xconn,
|
Some(ffi::SubstructureRedirectMask | ffi::SubstructureNotifyMask),
|
||||||
window,
|
[
|
||||||
root,
|
operation as c_long,
|
||||||
state_atom,
|
properties.0,
|
||||||
Some(ffi::SubstructureRedirectMask | ffi::SubstructureNotifyMask),
|
properties.1,
|
||||||
(
|
properties.2,
|
||||||
operation as c_long,
|
properties.3,
|
||||||
properties.0,
|
],
|
||||||
properties.1,
|
)
|
||||||
properties.2,
|
|
||||||
properties.3,
|
|
||||||
)
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn set_fullscreen_hint(&self, fullscreen: bool) -> util::Flusher {
|
fn set_fullscreen_hint(&self, fullscreen: bool) -> util::Flusher {
|
||||||
let xconn = &self.x.display;
|
let xconn = &self.x.display;
|
||||||
|
let fullscreen_atom = unsafe { xconn.get_atom_unchecked(b"_NET_WM_STATE_FULLSCREEN\0") };
|
||||||
let fullscreen_atom = unsafe { util::get_atom(xconn, b"_NET_WM_STATE_FULLSCREEN\0") }
|
|
||||||
.expect("Failed to call XInternAtom (_NET_WM_STATE_FULLSCREEN)");
|
|
||||||
|
|
||||||
Window2::set_netwm(
|
Window2::set_netwm(
|
||||||
xconn,
|
xconn,
|
||||||
self.x.window,
|
self.x.window,
|
||||||
|
@ -548,12 +493,8 @@ impl Window2 {
|
||||||
|
|
||||||
fn set_maximized_inner(&self, maximized: bool) -> util::Flusher {
|
fn set_maximized_inner(&self, maximized: bool) -> util::Flusher {
|
||||||
let xconn = &self.x.display;
|
let xconn = &self.x.display;
|
||||||
|
let horz_atom = unsafe { xconn.get_atom_unchecked(b"_NET_WM_STATE_MAXIMIZED_HORZ\0") };
|
||||||
let horz_atom = unsafe { util::get_atom(xconn, b"_NET_WM_STATE_MAXIMIZED_HORZ\0") }
|
let vert_atom = unsafe { xconn.get_atom_unchecked(b"_NET_WM_STATE_MAXIMIZED_VERT\0") };
|
||||||
.expect("Failed to call XInternAtom (_NET_WM_STATE_MAXIMIZED_HORZ)");
|
|
||||||
let vert_atom = unsafe { util::get_atom(xconn, b"_NET_WM_STATE_MAXIMIZED_VERT\0") }
|
|
||||||
.expect("Failed to call XInternAtom (_NET_WM_STATE_MAXIMIZED_VERT)");
|
|
||||||
|
|
||||||
Window2::set_netwm(
|
Window2::set_netwm(
|
||||||
xconn,
|
xconn,
|
||||||
self.x.window,
|
self.x.window,
|
||||||
|
@ -572,12 +513,8 @@ impl Window2 {
|
||||||
|
|
||||||
fn set_title_inner(&self, title: &str) -> util::Flusher {
|
fn set_title_inner(&self, title: &str) -> util::Flusher {
|
||||||
let xconn = &self.x.display;
|
let xconn = &self.x.display;
|
||||||
|
let wm_name_atom = unsafe { xconn.get_atom_unchecked(b"_NET_WM_NAME\0") };
|
||||||
let wm_name_atom = unsafe { util::get_atom(xconn, b"_NET_WM_NAME\0") }
|
let utf8_atom = unsafe { xconn.get_atom_unchecked(b"UTF8_STRING\0") };
|
||||||
.expect("Failed to call XInternAtom (_NET_WM_NAME)");
|
|
||||||
let utf8_atom = unsafe { util::get_atom(xconn, b"UTF8_STRING\0") }
|
|
||||||
.expect("Failed to call XInternAtom (UTF8_STRING)");
|
|
||||||
|
|
||||||
let title = CString::new(title).expect("Window title contained null byte");
|
let title = CString::new(title).expect("Window title contained null byte");
|
||||||
unsafe {
|
unsafe {
|
||||||
(xconn.xlib.XStoreName)(
|
(xconn.xlib.XStoreName)(
|
||||||
|
@ -585,13 +522,10 @@ impl Window2 {
|
||||||
self.x.window,
|
self.x.window,
|
||||||
title.as_ptr() as *const c_char,
|
title.as_ptr() as *const c_char,
|
||||||
);
|
);
|
||||||
|
xconn.change_property(
|
||||||
util::change_property(
|
|
||||||
xconn,
|
|
||||||
self.x.window,
|
self.x.window,
|
||||||
wm_name_atom,
|
wm_name_atom,
|
||||||
utf8_atom,
|
utf8_atom,
|
||||||
util::Format::Char,
|
|
||||||
util::PropMode::Replace,
|
util::PropMode::Replace,
|
||||||
title.as_bytes_with_nul(),
|
title.as_bytes_with_nul(),
|
||||||
)
|
)
|
||||||
|
@ -606,27 +540,20 @@ impl Window2 {
|
||||||
|
|
||||||
fn set_decorations_inner(&self, decorations: bool) -> util::Flusher {
|
fn set_decorations_inner(&self, decorations: bool) -> util::Flusher {
|
||||||
let xconn = &self.x.display;
|
let xconn = &self.x.display;
|
||||||
|
let wm_hints = unsafe { xconn.get_atom_unchecked(b"_MOTIF_WM_HINTS\0") };
|
||||||
let wm_hints = unsafe { util::get_atom(xconn, b"_MOTIF_WM_HINTS\0") }
|
xconn.change_property(
|
||||||
.expect("Failed to call XInternAtom (_MOTIF_WM_HINTS)");
|
self.x.window,
|
||||||
|
wm_hints,
|
||||||
unsafe {
|
wm_hints,
|
||||||
util::change_property(
|
util::PropMode::Replace,
|
||||||
xconn,
|
&[
|
||||||
self.x.window,
|
util::MWM_HINTS_DECORATIONS, // flags
|
||||||
wm_hints,
|
0, // functions
|
||||||
wm_hints,
|
decorations as c_ulong, // decorations
|
||||||
util::Format::Long,
|
0, // input mode
|
||||||
util::PropMode::Replace,
|
0, // status
|
||||||
&[
|
],
|
||||||
util::MWM_HINTS_DECORATIONS, // flags
|
)
|
||||||
0, // functions
|
|
||||||
decorations as c_ulong, // decorations
|
|
||||||
0, // input mode
|
|
||||||
0, // status
|
|
||||||
],
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn set_decorations(&self, decorations: bool) {
|
pub fn set_decorations(&self, decorations: bool) {
|
||||||
|
@ -638,10 +565,7 @@ impl Window2 {
|
||||||
|
|
||||||
fn set_always_on_top_inner(&self, always_on_top: bool) -> util::Flusher {
|
fn set_always_on_top_inner(&self, always_on_top: bool) -> util::Flusher {
|
||||||
let xconn = &self.x.display;
|
let xconn = &self.x.display;
|
||||||
|
let above_atom = unsafe { xconn.get_atom_unchecked(b"_NET_WM_STATE_ABOVE\0") };
|
||||||
let above_atom = unsafe { util::get_atom(xconn, b"_NET_WM_STATE_ABOVE\0") }
|
|
||||||
.expect("Failed to call XInternAtom (_NET_WM_STATE_ABOVE)");
|
|
||||||
|
|
||||||
Window2::set_netwm(
|
Window2::set_netwm(
|
||||||
xconn,
|
xconn,
|
||||||
self.x.window,
|
self.x.window,
|
||||||
|
@ -659,42 +583,28 @@ impl Window2 {
|
||||||
|
|
||||||
fn set_icon_inner(&self, icon: Icon) -> util::Flusher {
|
fn set_icon_inner(&self, icon: Icon) -> util::Flusher {
|
||||||
let xconn = &self.x.display;
|
let xconn = &self.x.display;
|
||||||
|
let icon_atom = unsafe { xconn.get_atom_unchecked(b"_NET_WM_ICON\0") };
|
||||||
let icon_atom = unsafe { util::get_atom(xconn, b"_NET_WM_ICON\0") }
|
|
||||||
.expect("Failed to call XInternAtom (_NET_WM_ICON)");
|
|
||||||
|
|
||||||
let data = icon.to_cardinals();
|
let data = icon.to_cardinals();
|
||||||
unsafe {
|
xconn.change_property(
|
||||||
util::change_property(
|
self.x.window,
|
||||||
xconn,
|
icon_atom,
|
||||||
self.x.window,
|
ffi::XA_CARDINAL,
|
||||||
icon_atom,
|
util::PropMode::Replace,
|
||||||
ffi::XA_CARDINAL,
|
data.as_slice(),
|
||||||
util::Format::Long,
|
)
|
||||||
util::PropMode::Replace,
|
|
||||||
data.as_slice(),
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn unset_icon_inner(&self) -> util::Flusher {
|
fn unset_icon_inner(&self) -> util::Flusher {
|
||||||
let xconn = &self.x.display;
|
let xconn = &self.x.display;
|
||||||
|
let icon_atom = unsafe { xconn.get_atom_unchecked(b"_NET_WM_ICON\0") };
|
||||||
let icon_atom = unsafe { util::get_atom(xconn, b"_NET_WM_ICON\0") }
|
|
||||||
.expect("Failed to call XInternAtom (_NET_WM_ICON)");
|
|
||||||
|
|
||||||
let empty_data: [util::Cardinal; 0] = [];
|
let empty_data: [util::Cardinal; 0] = [];
|
||||||
unsafe {
|
xconn.change_property(
|
||||||
util::change_property(
|
self.x.window,
|
||||||
xconn,
|
icon_atom,
|
||||||
self.x.window,
|
ffi::XA_CARDINAL,
|
||||||
icon_atom,
|
util::PropMode::Replace,
|
||||||
ffi::XA_CARDINAL,
|
&empty_data,
|
||||||
util::Format::Long,
|
)
|
||||||
util::PropMode::Replace,
|
|
||||||
&empty_data,
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn set_window_icon(&self, icon: Option<Icon>) {
|
pub fn set_window_icon(&self, icon: Option<Icon>) {
|
||||||
|
@ -707,7 +617,7 @@ impl Window2 {
|
||||||
pub fn show(&self) {
|
pub fn show(&self) {
|
||||||
unsafe {
|
unsafe {
|
||||||
(self.x.display.xlib.XMapRaised)(self.x.display.display, self.x.window);
|
(self.x.display.xlib.XMapRaised)(self.x.display.display, self.x.window);
|
||||||
util::flush_requests(&self.x.display)
|
self.x.display.flush_requests()
|
||||||
.expect("Failed to call XMapRaised");
|
.expect("Failed to call XMapRaised");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -715,17 +625,13 @@ impl Window2 {
|
||||||
pub fn hide(&self) {
|
pub fn hide(&self) {
|
||||||
unsafe {
|
unsafe {
|
||||||
(self.x.display.xlib.XUnmapWindow)(self.x.display.display, self.x.window);
|
(self.x.display.xlib.XUnmapWindow)(self.x.display.display, self.x.window);
|
||||||
util::flush_requests(&self.x.display)
|
self.x.display.flush_requests()
|
||||||
.expect("Failed to call XUnmapWindow");
|
.expect("Failed to call XUnmapWindow");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn update_cached_frame_extents(&self) {
|
fn update_cached_frame_extents(&self) {
|
||||||
let extents = util::get_frame_extents_heuristic(
|
let extents = self.x.display.get_frame_extents_heuristic(self.x.window, self.x.root);
|
||||||
&self.x.display,
|
|
||||||
self.x.window,
|
|
||||||
self.x.root,
|
|
||||||
);
|
|
||||||
(*self.shared_state.lock()).frame_extents = Some(extents);
|
(*self.shared_state.lock()).frame_extents = Some(extents);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -748,7 +654,7 @@ impl Window2 {
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn get_inner_position(&self) -> Option<(i32, i32)> {
|
pub fn get_inner_position(&self) -> Option<(i32, i32)> {
|
||||||
unsafe { util::translate_coords(&self.x.display, self.x.window, self.x.root )}
|
self.x.display.translate_coords(self.x.window, self.x.root )
|
||||||
.ok()
|
.ok()
|
||||||
.map(|coords| (coords.x_rel_root, coords.y_rel_root))
|
.map(|coords| (coords.x_rel_root, coords.y_rel_root))
|
||||||
}
|
}
|
||||||
|
@ -773,13 +679,13 @@ impl Window2 {
|
||||||
x as c_int,
|
x as c_int,
|
||||||
y as c_int,
|
y as c_int,
|
||||||
);
|
);
|
||||||
util::flush_requests(&self.x.display)
|
self.x.display.flush_requests()
|
||||||
}.expect("Failed to call XMoveWindow");
|
}.expect("Failed to call XMoveWindow");
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn get_inner_size(&self) -> Option<(u32, u32)> {
|
pub fn get_inner_size(&self) -> Option<(u32, u32)> {
|
||||||
unsafe { util::get_geometry(&self.x.display, self.x.window) }
|
self.x.display.get_geometry(self.x.window)
|
||||||
.ok()
|
.ok()
|
||||||
.map(|geo| (geo.width, geo.height))
|
.map(|geo| (geo.width, geo.height))
|
||||||
}
|
}
|
||||||
|
@ -806,7 +712,7 @@ impl Window2 {
|
||||||
width as c_uint,
|
width as c_uint,
|
||||||
height as c_uint,
|
height as c_uint,
|
||||||
);
|
);
|
||||||
util::flush_requests(&self.x.display)
|
self.x.display.flush_requests()
|
||||||
}.expect("Failed to call XResizeWindow");
|
}.expect("Failed to call XResizeWindow");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -815,14 +721,8 @@ impl Window2 {
|
||||||
{
|
{
|
||||||
let xconn = &self.x.display;
|
let xconn = &self.x.display;
|
||||||
|
|
||||||
let size_hints = {
|
let size_hints = xconn.alloc_size_hints();
|
||||||
let size_hints = (xconn.xlib.XAllocSizeHints)();
|
|
||||||
util::XSmartPointer::new(&xconn, size_hints)
|
|
||||||
.expect("XAllocSizeHints returned null; out of memory")
|
|
||||||
};
|
|
||||||
|
|
||||||
let mut flags: c_long = mem::uninitialized();
|
let mut flags: c_long = mem::uninitialized();
|
||||||
|
|
||||||
(xconn.xlib.XGetWMNormalHints)(
|
(xconn.xlib.XGetWMNormalHints)(
|
||||||
xconn.display,
|
xconn.display,
|
||||||
self.x.window,
|
self.x.window,
|
||||||
|
@ -838,7 +738,7 @@ impl Window2 {
|
||||||
self.x.window,
|
self.x.window,
|
||||||
size_hints.ptr,
|
size_hints.ptr,
|
||||||
);
|
);
|
||||||
util::flush_requests(xconn)?;
|
xconn.flush_requests()?;
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
@ -994,7 +894,7 @@ impl Window2 {
|
||||||
if cursor != 0 {
|
if cursor != 0 {
|
||||||
(self.x.display.xlib.XFreeCursor)(self.x.display.display, cursor);
|
(self.x.display.xlib.XFreeCursor)(self.x.display.display, cursor);
|
||||||
}
|
}
|
||||||
util::flush_requests(&self.x.display).expect("Failed to set or free the cursor");
|
self.x.display.flush_requests().expect("Failed to set or free the cursor");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1057,7 +957,7 @@ impl Window2 {
|
||||||
Grab => {
|
Grab => {
|
||||||
unsafe {
|
unsafe {
|
||||||
(self.x.display.xlib.XUngrabPointer)(self.x.display.display, ffi::CurrentTime);
|
(self.x.display.xlib.XUngrabPointer)(self.x.display.display, ffi::CurrentTime);
|
||||||
util::flush_requests(&self.x.display).expect("Failed to call XUngrabPointer");
|
self.x.display.flush_requests().expect("Failed to call XUngrabPointer");
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
Normal => {},
|
Normal => {},
|
||||||
|
@ -1123,7 +1023,7 @@ impl Window2 {
|
||||||
x,
|
x,
|
||||||
y,
|
y,
|
||||||
);
|
);
|
||||||
util::flush_requests(&self.x.display).map_err(|_| ())
|
self.x.display.flush_requests().map_err(|_| ())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue