Merge pull request 'get_atom_property_leak_fix' (#2) from get_atom_property_leak_fix into main
Reviewed-on: https://desktop-host/git/janis/wm/pulls/2
This commit is contained in:
commit
71ddeb6af1
|
@ -1,10 +1,10 @@
|
|||
use log::{error, warn};
|
||||
use log::{debug, error, warn};
|
||||
use num_traits::Zero;
|
||||
use std::{ffi::CString, mem::MaybeUninit, rc::Rc};
|
||||
use std::{ffi::CString, rc::Rc};
|
||||
|
||||
use thiserror::Error;
|
||||
|
||||
use x11::xlib::{self, Atom, Window, XEvent, XInternAtom, XKeyEvent};
|
||||
use x11::xlib::{self, Atom, Success, Window, XEvent, XInternAtom, XKeyEvent};
|
||||
|
||||
use crate::backends::{
|
||||
keycodes::KeyOrButton, xlib::keysym::mouse_button_to_xbutton,
|
||||
|
@ -290,49 +290,62 @@ impl XLib {
|
|||
xlib::PropertyNotify => {
|
||||
let ev = unsafe { &event.property };
|
||||
|
||||
(ev.atom == self.atoms.net_wm_window_type)
|
||||
.then(|| {
|
||||
(self.get_atom_property(
|
||||
ev.window,
|
||||
self.atoms.net_wm_state,
|
||||
) == Some(self.atoms.net_wm_state_fullscreen))
|
||||
.then(|| {
|
||||
XLibWindowEvent::FullscreenEvent(
|
||||
match ev.atom {
|
||||
atom if atom == self.atoms.net_wm_window_type => {
|
||||
if self
|
||||
.get_atom_property(
|
||||
ev.window,
|
||||
self.atoms.net_wm_state,
|
||||
)
|
||||
.map(|atom| {
|
||||
*atom == self.atoms.net_wm_state_fullscreen
|
||||
})
|
||||
.unwrap_or(false)
|
||||
{
|
||||
debug!("fullscreen event");
|
||||
Some(XLibWindowEvent::FullscreenEvent(
|
||||
FullscreenEvent::new(
|
||||
ev.window,
|
||||
FullscreenState::On,
|
||||
),
|
||||
)
|
||||
})
|
||||
})
|
||||
.flatten()
|
||||
))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
xlib::ClientMessage => {
|
||||
let ev = unsafe { &event.client_message };
|
||||
|
||||
(ev.message_type == self.atoms.net_wm_state)
|
||||
.then(|| {
|
||||
match ev.message_type {
|
||||
message_type if message_type == self.atoms.net_wm_state => {
|
||||
let data = ev.data.as_longs();
|
||||
(data[1] as u64 == self.atoms.net_wm_state_fullscreen
|
||||
if data[1] as u64 == self.atoms.net_wm_state_fullscreen
|
||||
|| data[2] as u64
|
||||
== self.atoms.net_wm_state_fullscreen)
|
||||
.then(|| {
|
||||
XLibWindowEvent::FullscreenEvent(
|
||||
FullscreenEvent::new(
|
||||
ev.window,
|
||||
match data[0] /* as u64 */ {
|
||||
0 => FullscreenState::Off,
|
||||
1 => FullscreenState::On,
|
||||
2 => FullscreenState::Toggle,
|
||||
_ => {
|
||||
unreachable!()
|
||||
}
|
||||
},
|
||||
),
|
||||
)
|
||||
})
|
||||
})
|
||||
.flatten()
|
||||
== self.atoms.net_wm_state_fullscreen
|
||||
{
|
||||
debug!("fullscreen event");
|
||||
Some(XLibWindowEvent::FullscreenEvent(
|
||||
FullscreenEvent::new(
|
||||
ev.window,
|
||||
match data[0] /* as u64 */ {
|
||||
0 => FullscreenState::Off,
|
||||
1 => FullscreenState::On,
|
||||
2 => FullscreenState::Toggle,
|
||||
_ => {
|
||||
unreachable!()
|
||||
}
|
||||
},
|
||||
),
|
||||
))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
_ => None,
|
||||
}
|
||||
|
@ -361,31 +374,33 @@ impl XLib {
|
|||
&self,
|
||||
window: xlib::Window,
|
||||
atom: xlib::Atom,
|
||||
) -> Option<xlib::Atom> {
|
||||
) -> Option<xpointer::XPointer<xlib::Atom>> {
|
||||
let mut di = 0;
|
||||
let mut dl0 = 0;
|
||||
let mut dl1 = 0;
|
||||
let mut da = 0;
|
||||
|
||||
let mut atom_out = MaybeUninit::<xlib::Atom>::zeroed();
|
||||
let (atom_out, success) =
|
||||
xpointer::XPointer::<xlib::Atom>::build_with_result(|ptr| unsafe {
|
||||
xlib::XGetWindowProperty(
|
||||
self.dpy(),
|
||||
window,
|
||||
atom,
|
||||
0,
|
||||
std::mem::size_of::<xlib::Atom>() as i64,
|
||||
0,
|
||||
xlib::XA_ATOM,
|
||||
&mut da,
|
||||
&mut di,
|
||||
&mut dl0,
|
||||
&mut dl1,
|
||||
ptr as *mut _ as *mut _,
|
||||
) == Success.into()
|
||||
});
|
||||
|
||||
unsafe {
|
||||
(xlib::XGetWindowProperty(
|
||||
self.dpy(),
|
||||
window,
|
||||
atom,
|
||||
0,
|
||||
std::mem::size_of::<xlib::Atom>() as i64,
|
||||
0,
|
||||
xlib::XA_ATOM,
|
||||
&mut da,
|
||||
&mut di,
|
||||
&mut dl0,
|
||||
&mut dl1,
|
||||
atom_out.as_mut_ptr() as *mut _,
|
||||
) != 0)
|
||||
.then(|| atom_out.assume_init())
|
||||
}
|
||||
debug!("get_atom_property: {} {:?}", success, atom_out);
|
||||
|
||||
success.then(|| atom_out).flatten()
|
||||
}
|
||||
|
||||
fn check_for_protocol(
|
||||
|
@ -1056,3 +1071,79 @@ unsafe extern "C" fn xlib_error_handler(
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub mod xpointer {
|
||||
use std::{
|
||||
ops::{Deref, DerefMut},
|
||||
ptr::{null, NonNull},
|
||||
};
|
||||
|
||||
use x11::xlib::XFree;
|
||||
|
||||
#[repr(C)]
|
||||
#[derive(Debug)]
|
||||
pub struct XPointer<T>(NonNull<T>);
|
||||
|
||||
impl<T> XPointer<T> {
|
||||
pub fn build_with<F>(cb: F) -> Option<Self>
|
||||
where
|
||||
F: FnOnce(&mut *const ()),
|
||||
{
|
||||
let mut ptr = null() as *const ();
|
||||
cb(&mut ptr);
|
||||
NonNull::new(ptr as *mut T).map(|ptr| Self(ptr))
|
||||
}
|
||||
|
||||
pub fn build_with_result<F, R>(cb: F) -> (Option<Self>, R)
|
||||
where
|
||||
F: FnOnce(&mut *const ()) -> R,
|
||||
{
|
||||
let mut ptr = null() as *const ();
|
||||
let result = cb(&mut ptr);
|
||||
(NonNull::new(ptr as *mut T).map(|ptr| Self(ptr)), result)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> AsRef<T> for XPointer<T> {
|
||||
fn as_ref(&self) -> &T {
|
||||
&**self
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> AsMut<T> for XPointer<T> {
|
||||
fn as_mut(&mut self) -> &mut T {
|
||||
&mut **self
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> PartialEq for XPointer<T>
|
||||
where
|
||||
T: PartialEq,
|
||||
{
|
||||
fn eq(&self, other: &Self) -> bool {
|
||||
self.0 == other.0
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> Eq for XPointer<T> where T: Eq {}
|
||||
|
||||
impl<T> Deref for XPointer<T> {
|
||||
type Target = T;
|
||||
|
||||
fn deref(&self) -> &Self::Target {
|
||||
unsafe { self.0.as_ref() }
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> DerefMut for XPointer<T> {
|
||||
fn deref_mut(&mut self) -> &mut Self::Target {
|
||||
unsafe { self.0.as_mut() }
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> Drop for XPointer<T> {
|
||||
fn drop(&mut self) {
|
||||
unsafe { XFree(self.0.as_ptr() as _) };
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -31,7 +31,7 @@ fn init_logger() {
|
|||
Root::builder()
|
||||
.appender("stdout")
|
||||
//.appender("logfile")
|
||||
.build(log::LevelFilter::Info),
|
||||
.build(log::LevelFilter::Debug),
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
|
|
774
src/xlib.rs
774
src/xlib.rs
|
@ -1,774 +0,0 @@
|
|||
use log::info;
|
||||
use std::ptr::{null, null_mut};
|
||||
use std::{ffi::CString, rc::Rc};
|
||||
|
||||
use x11::xlib::{
|
||||
self, AnyButton, AnyKey, AnyModifier, Atom, ButtonPressMask,
|
||||
ButtonReleaseMask, CWEventMask, ControlMask, CurrentTime, EnterWindowMask,
|
||||
FocusChangeMask, LockMask, Mod1Mask, Mod2Mask, Mod3Mask, Mod4Mask,
|
||||
Mod5Mask, PointerMotionMask, PropertyChangeMask, ShiftMask,
|
||||
StructureNotifyMask, SubstructureNotifyMask, SubstructureRedirectMask,
|
||||
Window, XCloseDisplay, XConfigureRequestEvent, XDefaultScreen, XEvent,
|
||||
XGetTransientForHint, XGrabPointer, XInternAtom, XKillClient, XMapWindow,
|
||||
XOpenDisplay, XRaiseWindow, XRootWindow, XSetErrorHandler, XSync,
|
||||
XUngrabButton, XUngrabKey, XUngrabPointer, XWarpPointer, XWindowAttributes,
|
||||
};
|
||||
use xlib::GrabModeAsync;
|
||||
|
||||
use log::error;
|
||||
|
||||
use crate::clients::Client;
|
||||
|
||||
pub struct XLib {
|
||||
display: Display,
|
||||
root: Window,
|
||||
_screen: i32,
|
||||
atoms: Atoms,
|
||||
global_keybinds: Vec<KeyOrButton>,
|
||||
}
|
||||
|
||||
struct Atoms {
|
||||
protocols: Atom,
|
||||
delete_window: Atom,
|
||||
active_window: Atom,
|
||||
take_focus: Atom,
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
|
||||
pub enum KeyOrButton {
|
||||
Key(i32, u32),
|
||||
Button(u32, u32, u64),
|
||||
}
|
||||
|
||||
impl KeyOrButton {
|
||||
#[allow(dead_code)]
|
||||
pub fn key(keycode: i32, modmask: u32) -> Self {
|
||||
Self::Key(keycode, modmask)
|
||||
}
|
||||
pub fn button(button: u32, modmask: u32, buttonmask: i64) -> Self {
|
||||
Self::Button(button, modmask, buttonmask as u64)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct Display(Rc<*mut xlib::Display>);
|
||||
|
||||
impl Drop for XLib {
|
||||
fn drop(&mut self) {
|
||||
self.close_dpy();
|
||||
}
|
||||
}
|
||||
|
||||
impl XLib {
|
||||
pub fn new() -> Self {
|
||||
let (display, _screen, root) = unsafe {
|
||||
let display = XOpenDisplay(null());
|
||||
|
||||
assert_ne!(display, null_mut());
|
||||
|
||||
let display = Display::new(display);
|
||||
let screen = XDefaultScreen(display.get());
|
||||
let root = XRootWindow(display.get(), screen);
|
||||
|
||||
(display, screen, root)
|
||||
};
|
||||
|
||||
Self {
|
||||
atoms: Atoms::init(display.clone()),
|
||||
global_keybinds: Vec::new(),
|
||||
root,
|
||||
_screen,
|
||||
display,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn init(&mut self) {
|
||||
unsafe {
|
||||
let mut window_attributes =
|
||||
std::mem::MaybeUninit::<xlib::XSetWindowAttributes>::zeroed()
|
||||
.assume_init();
|
||||
|
||||
window_attributes.event_mask = SubstructureRedirectMask
|
||||
| StructureNotifyMask
|
||||
| SubstructureNotifyMask
|
||||
| EnterWindowMask
|
||||
| PointerMotionMask
|
||||
| ButtonPressMask;
|
||||
|
||||
xlib::XChangeWindowAttributes(
|
||||
self.dpy(),
|
||||
self.root,
|
||||
CWEventMask,
|
||||
&mut window_attributes,
|
||||
);
|
||||
|
||||
xlib::XSelectInput(
|
||||
self.dpy(),
|
||||
self.root,
|
||||
window_attributes.event_mask,
|
||||
);
|
||||
|
||||
XSetErrorHandler(Some(xlib_error_handler));
|
||||
|
||||
XSync(self.dpy(), 0);
|
||||
}
|
||||
|
||||
self.grab_global_keybinds(self.root);
|
||||
}
|
||||
|
||||
pub fn add_global_keybind(&mut self, key: KeyOrButton) {
|
||||
self.global_keybinds.push(key);
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
fn ungrab_global_keybings(&self, window: Window) {
|
||||
unsafe {
|
||||
XUngrabButton(self.dpy(), AnyButton as u32, AnyModifier, window);
|
||||
XUngrabKey(self.dpy(), AnyKey, AnyModifier, window);
|
||||
}
|
||||
}
|
||||
|
||||
fn grab_global_keybinds(&self, window: Window) {
|
||||
for kb in self.global_keybinds.iter() {
|
||||
self.grab_key_or_button(window, kb);
|
||||
}
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
pub fn remove_global_keybind(&mut self, key: &KeyOrButton) {
|
||||
if self.global_keybinds.contains(key) {
|
||||
self.global_keybinds.retain(|kb| kb != key);
|
||||
}
|
||||
}
|
||||
|
||||
fn dpy(&self) -> *mut xlib::Display {
|
||||
self.display.get()
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
pub fn squash_event(&self, event_type: i32) -> XEvent {
|
||||
unsafe {
|
||||
let mut event =
|
||||
std::mem::MaybeUninit::<xlib::XEvent>::zeroed().assume_init();
|
||||
|
||||
while xlib::XCheckTypedEvent(self.dpy(), event_type, &mut event)
|
||||
!= 0
|
||||
{}
|
||||
|
||||
event
|
||||
}
|
||||
}
|
||||
|
||||
pub fn next_event(&self) -> XEvent {
|
||||
unsafe {
|
||||
let mut event =
|
||||
std::mem::MaybeUninit::<xlib::XEvent>::zeroed().assume_init();
|
||||
xlib::XNextEvent(self.dpy(), &mut event);
|
||||
|
||||
event
|
||||
}
|
||||
}
|
||||
|
||||
pub fn grab_key_or_button(&self, window: Window, key: &KeyOrButton) {
|
||||
let numlock_mask = self.get_numlock_mask();
|
||||
let modifiers =
|
||||
vec![0, LockMask, numlock_mask, LockMask | numlock_mask];
|
||||
|
||||
for modifier in modifiers.iter() {
|
||||
match key {
|
||||
&KeyOrButton::Key(keycode, modmask) => {
|
||||
unsafe {
|
||||
xlib::XGrabKey(
|
||||
self.dpy(),
|
||||
keycode,
|
||||
modmask | modifier,
|
||||
window,
|
||||
1, /* true */
|
||||
GrabModeAsync,
|
||||
GrabModeAsync,
|
||||
);
|
||||
}
|
||||
}
|
||||
&KeyOrButton::Button(button, modmask, buttonmask) => {
|
||||
unsafe {
|
||||
xlib::XGrabButton(
|
||||
self.dpy(),
|
||||
button,
|
||||
modmask | modifier,
|
||||
window,
|
||||
1, /*true */
|
||||
buttonmask as u32,
|
||||
GrabModeAsync,
|
||||
GrabModeAsync,
|
||||
0,
|
||||
0,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn focus_client(&self, client: &Client) {
|
||||
unsafe {
|
||||
xlib::XSetInputFocus(
|
||||
self.dpy(),
|
||||
client.window,
|
||||
xlib::RevertToPointerRoot,
|
||||
xlib::CurrentTime,
|
||||
);
|
||||
|
||||
let screen = xlib::XDefaultScreenOfDisplay(self.dpy()).as_ref();
|
||||
|
||||
if let Some(screen) = screen {
|
||||
xlib::XSetWindowBorder(
|
||||
self.dpy(),
|
||||
client.window,
|
||||
screen.white_pixel,
|
||||
);
|
||||
}
|
||||
|
||||
xlib::XChangeProperty(
|
||||
self.dpy(),
|
||||
self.root,
|
||||
self.atoms.active_window,
|
||||
xlib::XA_WINDOW,
|
||||
32,
|
||||
xlib::PropModeReplace,
|
||||
&client.window as *const u64 as *const _,
|
||||
1,
|
||||
);
|
||||
}
|
||||
|
||||
self.send_protocol(client, self.atoms.take_focus);
|
||||
}
|
||||
|
||||
pub fn unfocus_client(&self, client: &Client) {
|
||||
//info!("unfocusing client: {:?}", client);
|
||||
|
||||
unsafe {
|
||||
xlib::XSetInputFocus(
|
||||
self.dpy(),
|
||||
self.root,
|
||||
xlib::RevertToPointerRoot,
|
||||
xlib::CurrentTime,
|
||||
);
|
||||
|
||||
let screen = xlib::XDefaultScreenOfDisplay(self.dpy()).as_ref();
|
||||
|
||||
if let Some(screen) = screen {
|
||||
xlib::XSetWindowBorder(
|
||||
self.dpy(),
|
||||
client.window,
|
||||
screen.black_pixel,
|
||||
);
|
||||
}
|
||||
|
||||
// xlib::XDeleteProperty(
|
||||
// self.dpy(),
|
||||
// self.root,
|
||||
// self.atoms.active_window,
|
||||
// );
|
||||
}
|
||||
}
|
||||
|
||||
pub fn move_resize_client(&self, client: &Client) {
|
||||
let mut windowchanges = xlib::XWindowChanges {
|
||||
x: client.position.x,
|
||||
y: client.position.y,
|
||||
width: client.size.x,
|
||||
height: client.size.y,
|
||||
border_width: 0,
|
||||
sibling: 0,
|
||||
stack_mode: 0,
|
||||
};
|
||||
|
||||
if client.size.x < 1 || client.size.y < 1 {
|
||||
error!("client {:?} size is less than 1 pixel!", client);
|
||||
} else {
|
||||
unsafe {
|
||||
xlib::XConfigureWindow(
|
||||
self.dpy(),
|
||||
client.window,
|
||||
(xlib::CWY | xlib::CWX | xlib::CWHeight | xlib::CWWidth)
|
||||
as u32,
|
||||
&mut windowchanges,
|
||||
);
|
||||
|
||||
// I don't think I have to call this ~
|
||||
//self.configure_client(client);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn move_client(&self, client: &Client) {
|
||||
let mut wc = xlib::XWindowChanges {
|
||||
x: client.position.x,
|
||||
y: client.position.y,
|
||||
width: client.size.x,
|
||||
height: client.size.y,
|
||||
border_width: 0,
|
||||
sibling: 0,
|
||||
stack_mode: 0,
|
||||
};
|
||||
|
||||
if client.size.x < 1 || client.size.y < 1 {
|
||||
error!("client {:?} size is less than 1 pixel!", client);
|
||||
} else {
|
||||
unsafe {
|
||||
xlib::XConfigureWindow(
|
||||
self.dpy(),
|
||||
client.window,
|
||||
(xlib::CWX | xlib::CWY) as u32,
|
||||
&mut wc,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn resize_client(&self, client: &Client) {
|
||||
let mut wc = xlib::XWindowChanges {
|
||||
x: client.position.x,
|
||||
y: client.position.y,
|
||||
width: client.size.x,
|
||||
height: client.size.y,
|
||||
border_width: 0,
|
||||
sibling: 0,
|
||||
stack_mode: 0,
|
||||
};
|
||||
|
||||
if client.size.x < 1 || client.size.y < 1 {
|
||||
error!("client {:?} size is less than 1 pixel!", client);
|
||||
} else {
|
||||
unsafe {
|
||||
xlib::XConfigureWindow(
|
||||
self.dpy(),
|
||||
client.window,
|
||||
(xlib::CWWidth | xlib::CWHeight) as u32,
|
||||
&mut wc,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn hide_client(&self, client: &Client) {
|
||||
let mut wc = xlib::XWindowChanges {
|
||||
x: client.size.x * -2,
|
||||
y: client.position.y,
|
||||
width: client.size.x,
|
||||
height: client.size.y,
|
||||
border_width: 0,
|
||||
sibling: 0,
|
||||
stack_mode: 0,
|
||||
};
|
||||
|
||||
if client.size.x < 1 || client.size.y < 1 {
|
||||
error!("client {:?} size is less than 1 pixel!", client);
|
||||
} else {
|
||||
unsafe {
|
||||
xlib::XConfigureWindow(
|
||||
self.dpy(),
|
||||
client.window,
|
||||
(xlib::CWX | xlib::CWY) as u32,
|
||||
&mut wc,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn raise_client(&self, client: &Client) {
|
||||
unsafe {
|
||||
XRaiseWindow(self.dpy(), client.window);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get_window_size(&self, window: Window) -> Option<(i32, i32)> {
|
||||
let mut wa = unsafe {
|
||||
std::mem::MaybeUninit::<xlib::XWindowAttributes>::zeroed()
|
||||
.assume_init()
|
||||
};
|
||||
|
||||
if unsafe {
|
||||
xlib::XGetWindowAttributes(self.dpy(), window, &mut wa) != 0
|
||||
} {
|
||||
Some((wa.width, wa.height))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
fn get_window_attributes(
|
||||
&self,
|
||||
window: Window,
|
||||
) -> Option<XWindowAttributes> {
|
||||
let mut wa = unsafe {
|
||||
std::mem::MaybeUninit::<xlib::XWindowAttributes>::zeroed()
|
||||
.assume_init()
|
||||
};
|
||||
|
||||
if unsafe {
|
||||
xlib::XGetWindowAttributes(self.dpy(), window, &mut wa) != 0
|
||||
} {
|
||||
Some(wa)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get_transient_for_window(&self, window: Window) -> Option<Window> {
|
||||
let mut transient_for: Window = 0;
|
||||
|
||||
if unsafe {
|
||||
XGetTransientForHint(self.dpy(), window, &mut transient_for) != 0
|
||||
} {
|
||||
Some(transient_for)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
pub fn expose_client(&self, client: &Client) {
|
||||
self.expose_window(client.window);
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
fn expose_window(&self, window: Window) {
|
||||
if let Some(wa) = self.get_window_attributes(window) {
|
||||
unsafe {
|
||||
xlib::XClearArea(
|
||||
self.dpy(),
|
||||
window,
|
||||
0,
|
||||
0,
|
||||
wa.width as u32,
|
||||
wa.height as u32,
|
||||
1,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn configure_window(&self, event: &XConfigureRequestEvent) {
|
||||
let mut wc = xlib::XWindowChanges {
|
||||
x: event.x,
|
||||
y: event.y,
|
||||
width: event.width,
|
||||
height: event.height,
|
||||
border_width: event.border_width,
|
||||
sibling: event.above,
|
||||
stack_mode: event.detail,
|
||||
};
|
||||
|
||||
unsafe {
|
||||
xlib::XConfigureWindow(
|
||||
self.dpy(),
|
||||
event.window,
|
||||
event.value_mask as u32,
|
||||
&mut wc,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn configure_client(&self, client: &Client, border: i32) {
|
||||
let mut event = xlib::XConfigureEvent {
|
||||
type_: xlib::ConfigureNotify,
|
||||
display: self.dpy(),
|
||||
event: client.window,
|
||||
window: client.window,
|
||||
x: client.position.x,
|
||||
y: client.position.y,
|
||||
width: client.size.x,
|
||||
height: client.size.y,
|
||||
border_width: border,
|
||||
override_redirect: 0,
|
||||
send_event: 0,
|
||||
serial: 0,
|
||||
above: 0,
|
||||
};
|
||||
|
||||
unsafe {
|
||||
xlib::XSetWindowBorderWidth(
|
||||
self.dpy(),
|
||||
event.window,
|
||||
event.border_width as u32,
|
||||
);
|
||||
|
||||
xlib::XSendEvent(
|
||||
self.dpy(),
|
||||
event.window,
|
||||
0,
|
||||
StructureNotifyMask,
|
||||
&mut event as *mut _ as *mut XEvent,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn map_window(&self, window: Window) {
|
||||
unsafe {
|
||||
XMapWindow(self.dpy(), window);
|
||||
|
||||
xlib::XSelectInput(
|
||||
self.dpy(),
|
||||
window,
|
||||
EnterWindowMask
|
||||
| FocusChangeMask
|
||||
| PropertyChangeMask
|
||||
| StructureNotifyMask,
|
||||
);
|
||||
}
|
||||
|
||||
self.grab_global_keybinds(window);
|
||||
}
|
||||
|
||||
pub fn dimensions(&self) -> (i32, i32) {
|
||||
unsafe {
|
||||
let mut wa =
|
||||
std::mem::MaybeUninit::<xlib::XWindowAttributes>::zeroed()
|
||||
.assume_init();
|
||||
|
||||
xlib::XGetWindowAttributes(self.dpy(), self.root, &mut wa);
|
||||
|
||||
info!("Root window dimensions: {}, {}", wa.width, wa.height);
|
||||
|
||||
(wa.width, wa.height)
|
||||
}
|
||||
}
|
||||
|
||||
fn close_dpy(&self) {
|
||||
unsafe {
|
||||
XCloseDisplay(self.dpy());
|
||||
}
|
||||
}
|
||||
|
||||
pub fn kill_client(&self, client: &Client) {
|
||||
if !self.send_protocol(client, self.atoms.delete_window) {
|
||||
unsafe {
|
||||
XKillClient(self.dpy(), client.window);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn grab_cursor(&self) {
|
||||
unsafe {
|
||||
XGrabPointer(
|
||||
self.dpy(),
|
||||
self.root,
|
||||
0,
|
||||
(ButtonPressMask | ButtonReleaseMask | PointerMotionMask)
|
||||
as u32,
|
||||
GrabModeAsync,
|
||||
GrabModeAsync,
|
||||
0,
|
||||
0,
|
||||
CurrentTime,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn release_cursor(&self) {
|
||||
unsafe {
|
||||
XUngrabPointer(self.dpy(), CurrentTime);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn move_cursor(&self, window: Option<Window>, position: (i32, i32)) {
|
||||
unsafe {
|
||||
XWarpPointer(
|
||||
self.dpy(),
|
||||
0,
|
||||
window.unwrap_or(self.root),
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
position.0,
|
||||
position.1,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
fn check_for_protocol(&self, client: &Client, proto: xlib::Atom) -> bool {
|
||||
let mut protos: *mut xlib::Atom = null_mut();
|
||||
let mut num_protos: i32 = 0;
|
||||
|
||||
unsafe {
|
||||
if xlib::XGetWMProtocols(
|
||||
self.dpy(),
|
||||
client.window,
|
||||
&mut protos,
|
||||
&mut num_protos,
|
||||
) != 0
|
||||
{
|
||||
for i in 0..num_protos {
|
||||
if *protos.offset(i as isize) == proto {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
fn send_protocol(&self, client: &Client, proto: xlib::Atom) -> bool {
|
||||
if self.check_for_protocol(client, proto) {
|
||||
let mut data = xlib::ClientMessageData::default();
|
||||
data.set_long(0, proto as i64);
|
||||
|
||||
let mut event = XEvent {
|
||||
client_message: xlib::XClientMessageEvent {
|
||||
type_: xlib::ClientMessage,
|
||||
serial: 0,
|
||||
display: self.dpy(),
|
||||
send_event: 0,
|
||||
window: client.window,
|
||||
format: 32,
|
||||
message_type: self.atoms.protocols,
|
||||
data,
|
||||
},
|
||||
};
|
||||
|
||||
unsafe {
|
||||
xlib::XSendEvent(
|
||||
self.dpy(),
|
||||
client.window,
|
||||
0,
|
||||
xlib::NoEventMask,
|
||||
&mut event,
|
||||
);
|
||||
}
|
||||
|
||||
true
|
||||
} else {
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
pub fn make_key<S>(&self, key: S, modmask: u32) -> KeyOrButton
|
||||
where
|
||||
S: AsRef<str>,
|
||||
{
|
||||
let key = self.keycode(key);
|
||||
|
||||
KeyOrButton::Key(key, modmask)
|
||||
}
|
||||
|
||||
fn keycode<S>(&self, string: S) -> i32
|
||||
where
|
||||
S: AsRef<str>,
|
||||
{
|
||||
let c_string = CString::new(string.as_ref()).unwrap();
|
||||
|
||||
unsafe {
|
||||
let keysym = xlib::XStringToKeysym(c_string.as_ptr());
|
||||
xlib::XKeysymToKeycode(self.dpy(), keysym) as i32
|
||||
}
|
||||
}
|
||||
|
||||
fn get_numlock_mask(&self) -> u32 {
|
||||
unsafe {
|
||||
let modmap = xlib::XGetModifierMapping(self.dpy());
|
||||
let max_keypermod = (*modmap).max_keypermod;
|
||||
|
||||
for i in 0..8 {
|
||||
for j in 0..max_keypermod {
|
||||
if *(*modmap)
|
||||
.modifiermap
|
||||
.offset((i * max_keypermod + j) as isize)
|
||||
== xlib::XKeysymToKeycode(
|
||||
self.dpy(),
|
||||
x11::keysym::XK_Num_Lock as u64,
|
||||
)
|
||||
{
|
||||
return 1 << i;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
0
|
||||
}
|
||||
|
||||
pub fn get_clean_mask(&self) -> u32 {
|
||||
!(self.get_numlock_mask() | LockMask)
|
||||
& (ShiftMask
|
||||
| ControlMask
|
||||
| Mod1Mask
|
||||
| Mod2Mask
|
||||
| Mod3Mask
|
||||
| Mod4Mask
|
||||
| Mod5Mask)
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
pub fn clean_mask(&self, mask: u32) -> u32 {
|
||||
mask & self.get_clean_mask()
|
||||
}
|
||||
|
||||
pub fn are_masks_equal(&self, rhs: u32, lhs: u32) -> bool {
|
||||
let clean = self.get_clean_mask();
|
||||
|
||||
rhs & clean == lhs & clean
|
||||
}
|
||||
}
|
||||
|
||||
impl Display {
|
||||
pub fn new(display: *mut x11::xlib::Display) -> Self {
|
||||
Self {
|
||||
0: Rc::new(display),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get(&self) -> *mut x11::xlib::Display {
|
||||
*self.0
|
||||
}
|
||||
}
|
||||
|
||||
impl Atoms {
|
||||
fn init(display: Display) -> Self {
|
||||
unsafe {
|
||||
Self {
|
||||
protocols: {
|
||||
let name = CString::new("WM_PROTOCOLS").unwrap();
|
||||
XInternAtom(display.get(), name.as_c_str().as_ptr(), 0)
|
||||
},
|
||||
delete_window: {
|
||||
let name = CString::new("WM_DELETE_WINDOW").unwrap();
|
||||
XInternAtom(display.get(), name.as_c_str().as_ptr(), 0)
|
||||
},
|
||||
active_window: {
|
||||
let name = CString::new("WM_ACTIVE_WINDOW").unwrap();
|
||||
XInternAtom(display.get(), name.as_c_str().as_ptr(), 0)
|
||||
},
|
||||
take_focus: {
|
||||
let name = CString::new("WM_TAKE_FOCUS").unwrap();
|
||||
XInternAtom(display.get(), name.as_c_str().as_ptr(), 0)
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
unsafe extern "C" fn xlib_error_handler(
|
||||
_dpy: *mut x11::xlib::Display,
|
||||
ee: *mut x11::xlib::XErrorEvent,
|
||||
) -> std::os::raw::c_int {
|
||||
let err = ee.as_ref().unwrap();
|
||||
|
||||
if err.error_code == x11::xlib::BadWindow
|
||||
|| err.error_code == x11::xlib::BadDrawable
|
||||
|| err.error_code == x11::xlib::BadAccess
|
||||
|| err.error_code == x11::xlib::BadMatch
|
||||
{
|
||||
0
|
||||
} else {
|
||||
error!(
|
||||
"wm: fatal error:\nrequest_code: {}\nerror_code: {}",
|
||||
err.request_code, err.error_code
|
||||
);
|
||||
std::process::exit(1);
|
||||
}
|
||||
}
|
Loading…
Reference in a new issue