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 num_traits::Zero;
|
||||||
use std::{ffi::CString, mem::MaybeUninit, rc::Rc};
|
use std::{ffi::CString, rc::Rc};
|
||||||
|
|
||||||
use thiserror::Error;
|
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::{
|
use crate::backends::{
|
||||||
keycodes::KeyOrButton, xlib::keysym::mouse_button_to_xbutton,
|
keycodes::KeyOrButton, xlib::keysym::mouse_button_to_xbutton,
|
||||||
|
@ -290,49 +290,62 @@ impl XLib {
|
||||||
xlib::PropertyNotify => {
|
xlib::PropertyNotify => {
|
||||||
let ev = unsafe { &event.property };
|
let ev = unsafe { &event.property };
|
||||||
|
|
||||||
(ev.atom == self.atoms.net_wm_window_type)
|
match ev.atom {
|
||||||
.then(|| {
|
atom if atom == self.atoms.net_wm_window_type => {
|
||||||
(self.get_atom_property(
|
if self
|
||||||
ev.window,
|
.get_atom_property(
|
||||||
self.atoms.net_wm_state,
|
ev.window,
|
||||||
) == Some(self.atoms.net_wm_state_fullscreen))
|
self.atoms.net_wm_state,
|
||||||
.then(|| {
|
)
|
||||||
XLibWindowEvent::FullscreenEvent(
|
.map(|atom| {
|
||||||
|
*atom == self.atoms.net_wm_state_fullscreen
|
||||||
|
})
|
||||||
|
.unwrap_or(false)
|
||||||
|
{
|
||||||
|
debug!("fullscreen event");
|
||||||
|
Some(XLibWindowEvent::FullscreenEvent(
|
||||||
FullscreenEvent::new(
|
FullscreenEvent::new(
|
||||||
ev.window,
|
ev.window,
|
||||||
FullscreenState::On,
|
FullscreenState::On,
|
||||||
),
|
),
|
||||||
)
|
))
|
||||||
})
|
} else {
|
||||||
})
|
None
|
||||||
.flatten()
|
}
|
||||||
|
}
|
||||||
|
_ => None,
|
||||||
|
}
|
||||||
}
|
}
|
||||||
xlib::ClientMessage => {
|
xlib::ClientMessage => {
|
||||||
let ev = unsafe { &event.client_message };
|
let ev = unsafe { &event.client_message };
|
||||||
|
|
||||||
(ev.message_type == self.atoms.net_wm_state)
|
match ev.message_type {
|
||||||
.then(|| {
|
message_type if message_type == self.atoms.net_wm_state => {
|
||||||
let data = ev.data.as_longs();
|
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
|
|| data[2] as u64
|
||||||
== self.atoms.net_wm_state_fullscreen)
|
== self.atoms.net_wm_state_fullscreen
|
||||||
.then(|| {
|
{
|
||||||
XLibWindowEvent::FullscreenEvent(
|
debug!("fullscreen event");
|
||||||
FullscreenEvent::new(
|
Some(XLibWindowEvent::FullscreenEvent(
|
||||||
ev.window,
|
FullscreenEvent::new(
|
||||||
match data[0] /* as u64 */ {
|
ev.window,
|
||||||
0 => FullscreenState::Off,
|
match data[0] /* as u64 */ {
|
||||||
1 => FullscreenState::On,
|
0 => FullscreenState::Off,
|
||||||
2 => FullscreenState::Toggle,
|
1 => FullscreenState::On,
|
||||||
_ => {
|
2 => FullscreenState::Toggle,
|
||||||
unreachable!()
|
_ => {
|
||||||
}
|
unreachable!()
|
||||||
},
|
}
|
||||||
),
|
},
|
||||||
)
|
),
|
||||||
})
|
))
|
||||||
})
|
} else {
|
||||||
.flatten()
|
None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_ => None,
|
||||||
|
}
|
||||||
}
|
}
|
||||||
_ => None,
|
_ => None,
|
||||||
}
|
}
|
||||||
|
@ -361,31 +374,33 @@ impl XLib {
|
||||||
&self,
|
&self,
|
||||||
window: xlib::Window,
|
window: xlib::Window,
|
||||||
atom: xlib::Atom,
|
atom: xlib::Atom,
|
||||||
) -> Option<xlib::Atom> {
|
) -> Option<xpointer::XPointer<xlib::Atom>> {
|
||||||
let mut di = 0;
|
let mut di = 0;
|
||||||
let mut dl0 = 0;
|
let mut dl0 = 0;
|
||||||
let mut dl1 = 0;
|
let mut dl1 = 0;
|
||||||
let mut da = 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 {
|
debug!("get_atom_property: {} {:?}", success, atom_out);
|
||||||
(xlib::XGetWindowProperty(
|
|
||||||
self.dpy(),
|
success.then(|| atom_out).flatten()
|
||||||
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())
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn check_for_protocol(
|
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()
|
Root::builder()
|
||||||
.appender("stdout")
|
.appender("stdout")
|
||||||
//.appender("logfile")
|
//.appender("logfile")
|
||||||
.build(log::LevelFilter::Info),
|
.build(log::LevelFilter::Debug),
|
||||||
)
|
)
|
||||||
.unwrap();
|
.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