Merge branch 'refactor-2'

This commit is contained in:
Janis 2021-11-29 20:47:57 +01:00
commit 4810d88dc1
20 changed files with 4275 additions and 348 deletions

1
.gitignore vendored
View file

@ -3,3 +3,4 @@
/Cargo.lock
/.cargo/
/wmlog
/xinitrc.tmp

View file

@ -7,9 +7,14 @@ edition = "2018"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
x11 = {version = "2.18.2", features = ["xlib"] }
x11 = {version = "2.18.2", features = ["xlib", "xft"] }
log = "0.4.13"
simple_logger = "1.11.0"
dirs = "3.0.2"
log4rs = "1.0.0"
indexmap = "1.6.2"
indexmap = "1.6.2"
thiserror = "1.0.30"
bitflags = "1.3.2"
derivative = "2.2.0"
serde = { version = "1.0", features = ["derive"] }
toml = "0.5"

View file

@ -1,5 +0,0 @@
run:
startx ./xinitrc :0 vt2
run-root:
sudo startx ./xinitrc :0 vt2

View file

@ -1,4 +1,5 @@
# Unnamed WM
# No WM
## formerly Unnamed
This Project is a x11 tiling window manager written in Rust and losely based on / inspired by suckless' [dwm](https://dwm.suckless.org/).
@ -11,5 +12,5 @@ Both `M-S-T` and `M-S-RET` will spawn an instance of `xterm`, `M-q` will kill th
One big difference from dwm is the way I handle virtual screens, although this is mostly a placeholder mechanic that I will most likely change in the future. Currently I have 3 (or more) virtual screens in a list that can be rotated with `M-left` and `M-right`.
Unnamed WM also has optional gaps :^)
![Unnamed WM in a VM](/vm-ss.png)
No WM also has optional gaps :^)
![No WM in a VM](/vm-ss.png)

Binary file not shown.

Before

Width:  |  Height:  |  Size: 853 KiB

4
nowm.toml Normal file
View file

@ -0,0 +1,4 @@
num_virtualscreens = 10
mod_key = "Super"
gap = 5
kill_clients_on_exit = false

12
runner.sh Executable file
View file

@ -0,0 +1,12 @@
#!/bin/sh
# binary supplied by cargo
bin=$1
# write temporary xinitrc
printf "exec $bin\n" > xinitrc.tmp
# call xinit
XEPHYR_BIN=$(which Xephyr)
[ -z "$XEPHYR_BIN" ] || exec xinit ./xinitrc.tmp -- $XEPHYR_BIN :100 -ac -screen 800x600

211
src/backends/keycodes.rs Normal file
View file

@ -0,0 +1,211 @@
#![allow(dead_code)]
#[derive(Debug, Hash, Ord, PartialOrd, PartialEq, Eq, Clone, Copy)]
pub enum KeyOrButton {
Key(VirtualKeyCode),
Button(MouseButton),
}
#[derive(Debug, Hash, Ord, PartialOrd, PartialEq, Eq, Clone, Copy)]
pub enum MouseButton {
Left,
Middle,
Right,
ScrollUp,
ScrollDown,
ScrollLeft,
ScrollRight,
Forward,
Backward,
}
/// from winit
#[derive(Debug, Hash, Ord, PartialOrd, PartialEq, Eq, Clone, Copy)]
#[repr(u32)]
pub enum VirtualKeyCode {
One,
Two,
Three,
Four,
Five,
Six,
Seven,
Eight,
Nine,
Zero,
A,
B,
C,
D,
E,
F,
G,
H,
I,
J,
K,
L,
M,
N,
O,
P,
Q,
R,
S,
T,
U,
V,
W,
X,
Y,
Z,
/// The Escape key, next to F1.
Escape,
F1,
F2,
F3,
F4,
F5,
F6,
F7,
F8,
F9,
F10,
F11,
F12,
F13,
F14,
F15,
F16,
F17,
F18,
F19,
F20,
F21,
F22,
F23,
F24,
/// Print Screen/SysRq.
Print,
/// Scroll Lock.
Scroll,
/// Pause/Break key, next to Scroll lock.
Pause,
/// `Insert`, next to Backspace.
Insert,
Home,
Delete,
End,
PageDown,
PageUp,
Left,
Up,
Right,
Down,
/// The Backspace key, right over Enter.
// TODO: rename
Back,
/// The Enter key.
Return,
/// The space bar.
Space,
/// The "Compose" key on Linux.
Compose,
Caret,
Numlock,
Numpad0,
Numpad1,
Numpad2,
Numpad3,
Numpad4,
Numpad5,
Numpad6,
Numpad7,
Numpad8,
Numpad9,
NumpadAdd,
NumpadDivide,
NumpadDecimal,
NumpadComma,
NumpadEnter,
NumpadEquals,
NumpadMultiply,
NumpadSubtract,
AbntC1,
AbntC2,
Apostrophe,
Apps,
Asterisk,
At,
Ax,
Backslash,
Calculator,
Capital,
Colon,
Comma,
Convert,
Equals,
Grave,
Kana,
Kanji,
LAlt,
LBracket,
LControl,
LShift,
LWin,
Mail,
MediaSelect,
MediaStop,
Minus,
Mute,
MyComputer,
// also called "Next"
NavigateForward,
// also called "Prior"
NavigateBackward,
NextTrack,
NoConvert,
OEM102,
Period,
PlayPause,
Plus,
Power,
PrevTrack,
RAlt,
RBracket,
RControl,
RShift,
RWin,
Semicolon,
Slash,
Sleep,
Stop,
Sysrq,
Tab,
Underline,
Unlabeled,
VolumeDown,
VolumeUp,
Wake,
WebBack,
WebFavorites,
WebForward,
WebHome,
WebRefresh,
WebSearch,
WebStop,
Yen,
Copy,
Paste,
Cut,
}

6
src/backends/mod.rs Normal file
View file

@ -0,0 +1,6 @@
pub mod keycodes;
pub mod traits;
pub mod window_event;
pub mod xlib;
pub use traits::*;

53
src/backends/traits.rs Normal file
View file

@ -0,0 +1,53 @@
use super::{
window_event,
window_event::{KeyOrMouseBind, Point},
};
pub trait WindowServerBackend {
type Window;
//type WindowEvent = super::window_event::WindowEvent<Self::Window>;
fn build() -> Self;
fn next_event(&mut self) -> window_event::WindowEvent<Self::Window>;
fn handle_event(&mut self, event: window_event::WindowEvent<Self::Window>);
/// adds a keybind to the specified `window`, or globally if `window` is `None`.
/// add global keybind
fn add_keybind(&mut self, keybind: KeyOrMouseBind);
fn remove_keybind(&mut self, keybind: &KeyOrMouseBind);
fn focus_window(&self, window: Self::Window);
fn unfocus_window(&self, window: Self::Window);
fn raise_window(&self, window: Self::Window);
fn hide_window(&self, window: Self::Window);
fn kill_window(&self, window: Self::Window);
fn get_parent_window(&self, window: Self::Window) -> Option<Self::Window>;
fn configure_window(
&self,
window: Self::Window,
new_size: Option<Point<i32>>,
new_pos: Option<Point<i32>>,
new_border: Option<i32>,
);
fn screen_size(&self) -> Point<i32>;
fn get_window_size(&self, window: Self::Window) -> Option<Point<i32>>;
fn grab_cursor(&self);
fn ungrab_cursor(&self);
fn move_cursor(&self, window: Option<Self::Window>, position: Point<i32>);
fn all_windows(&self) -> Option<Vec<Self::Window>>;
fn set_active_window_border_color(&mut self, color_name: &str);
fn set_inactive_window_border_color(&mut self, color_name: &str);
fn resize_window(&self, window: Self::Window, new_size: Point<i32>) {
self.configure_window(window, Some(new_size), None, None);
}
fn move_window(&self, window: Self::Window, new_pos: Point<i32>) {
self.configure_window(window, None, Some(new_pos), None);
}
}

View file

@ -0,0 +1,377 @@
#![allow(dead_code)]
use super::keycodes::{KeyOrButton, MouseButton, VirtualKeyCode};
use bitflags::bitflags;
#[derive(Debug)]
pub enum WindowEvent<Window> {
KeyEvent(KeyEvent<Window>),
ButtonEvent(ButtonEvent<Window>),
MotionEvent(MotionEvent<Window>),
MapRequestEvent(MapEvent<Window>),
MapEvent(MapEvent<Window>),
UnmapEvent(UnmapEvent<Window>),
CreateEvent(CreateEvent<Window>),
DestroyEvent(DestroyEvent<Window>),
EnterEvent(EnterEvent<Window>),
ConfigureEvent(ConfigureEvent<Window>),
FullscreenEvent(FullscreenEvent<Window>), //1 { window: Window, event: 1 },
}
#[derive(Debug, PartialEq, Eq, Copy, Clone)]
pub enum KeyState {
Pressed,
Released,
}
#[derive(
Debug, PartialEq, Eq, PartialOrd, Ord, Hash, Clone, Copy, serde::Deserialize,
)]
#[repr(u8)]
pub enum ModifierKey {
Shift,
ShiftLock,
Control,
Alt,
AltGr,
/// Windows key on most keyboards
Super,
NumLock,
}
bitflags! {
pub struct ModifierState: u32 {
const SHIFT = 0x01;
const SHIFT_LOCK = 0x010;
const CONTROL = 0x0100;
const ALT = 0x01000;
const ALT_GR = 0x010000;
const SUPER = 0x0100000;
const NUM_LOCK = 0x01000000;
const IGNORE_LOCK = Self::CONTROL.bits | Self::ALT.bits |
Self::ALT_GR.bits | Self::SUPER.bits| Self::SHIFT.bits;
}
}
impl<const N: usize> From<[ModifierKey; N]> for ModifierState {
fn from(slice: [ModifierKey; N]) -> Self {
let mut state = ModifierState::empty();
for ele in slice {
state.insert_mod(ele);
}
state
}
}
impl ModifierState {
pub fn eq_ignore_lock(&self, rhs: &Self) -> bool {
let mask = Self::IGNORE_LOCK;
*self & mask == *rhs & mask
}
pub fn with_mod(mut self, modifier: ModifierKey) -> Self {
self.insert_mod(modifier);
self
}
pub fn unset_mod(&mut self, modifier: ModifierKey) {
self.set_mod(modifier, false);
}
pub fn set_mod(&mut self, modifier: ModifierKey, state: bool) {
self.set(
match modifier {
ModifierKey::Shift => Self::SHIFT,
ModifierKey::ShiftLock => Self::SHIFT_LOCK,
ModifierKey::Control => Self::CONTROL,
ModifierKey::Alt => Self::ALT,
ModifierKey::AltGr => Self::ALT_GR,
ModifierKey::Super => Self::SUPER,
ModifierKey::NumLock => Self::NUM_LOCK,
},
state,
);
}
pub fn insert_mod(&mut self, modifier: ModifierKey) {
self.set_mod(modifier, true);
}
}
impl Into<u8> for ModifierKey {
fn into(self) -> u8 {
self as u8
}
}
#[derive(Debug)]
pub struct KeyEvent<Window> {
pub window: Window,
pub state: KeyState,
pub keycode: VirtualKeyCode,
pub modifierstate: ModifierState,
}
impl<Window> KeyEvent<Window> {
pub fn new(
window: Window,
state: KeyState,
keycode: VirtualKeyCode,
modifierstate: ModifierState,
) -> Self {
Self {
window,
state,
keycode,
modifierstate,
}
}
}
#[derive(Debug, Copy, Clone, PartialEq, PartialOrd)]
pub struct Point<I>
where
I: Copy + Clone + PartialEq + PartialOrd,
{
pub x: I,
pub y: I,
}
impl<I> From<(I, I)> for Point<I>
where
I: Copy + Clone + PartialEq + PartialOrd,
{
fn from(value: (I, I)) -> Self {
Self::from_tuple(value)
}
}
impl<I> Point<I>
where
I: Copy + Clone + PartialEq + PartialOrd,
{
pub fn new(x: I, y: I) -> Self {
Self { x, y }
}
pub fn from_tuple(tuple: (I, I)) -> Self {
Self {
x: tuple.0,
y: tuple.1,
}
}
pub fn as_tuple(&self) -> (I, I) {
(self.x, self.y)
}
}
#[derive(Debug)]
pub struct ButtonEvent<Window> {
pub window: Window,
pub state: KeyState,
pub keycode: MouseButton,
pub cursor_position: Point<i32>,
pub modifierstate: ModifierState,
}
impl<Window> ButtonEvent<Window> {
pub fn new(
window: Window,
state: KeyState,
keycode: MouseButton,
cursor_position: Point<i32>,
modifierstate: ModifierState,
) -> Self {
Self {
window,
state,
keycode,
cursor_position,
modifierstate,
}
}
}
#[derive(Debug)]
pub struct MotionEvent<Window> {
pub position: Point<i32>,
pub window: Window,
}
impl<Window> MotionEvent<Window> {
pub fn new(position: Point<i32>, window: Window) -> Self {
Self { position, window }
}
}
#[derive(Debug)]
pub struct MapEvent<Window> {
pub window: Window,
}
#[derive(Debug)]
pub struct UnmapEvent<Window> {
pub window: Window,
}
#[derive(Debug)]
pub struct EnterEvent<Window> {
pub window: Window,
}
#[derive(Debug)]
pub struct DestroyEvent<Window> {
pub window: Window,
}
impl<Window> DestroyEvent<Window> {
pub fn new(window: Window) -> Self {
Self { window }
}
}
#[derive(Debug)]
pub struct CreateEvent<Window> {
pub window: Window,
pub position: Point<i32>,
pub size: Point<i32>,
}
impl<Window> CreateEvent<Window> {
pub fn new(window: Window, position: Point<i32>, size: Point<i32>) -> Self {
Self {
window,
position,
size,
}
}
}
#[derive(Debug)]
pub struct ConfigureEvent<Window> {
pub window: Window,
pub position: Point<i32>,
pub size: Point<i32>,
}
impl<Window> ConfigureEvent<Window> {
pub fn new(window: Window, position: Point<i32>, size: Point<i32>) -> Self {
Self {
window,
position,
size,
}
}
}
#[derive(Debug)]
pub struct FullscreenEvent<Window> {
window: Window,
new_fullscreen: bool,
}
impl<Window> FullscreenEvent<Window> {
pub fn new(window: Window, new_fullscreen: bool) -> Self {
Self {
window,
new_fullscreen,
}
}
}
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord)]
pub struct KeyBind {
pub key: VirtualKeyCode,
pub modifiers: ModifierState,
}
impl KeyBind {
pub fn new(key: VirtualKeyCode) -> Self {
Self {
key,
modifiers: ModifierState::empty(),
}
}
pub fn with_mod(mut self, modifier_key: ModifierKey) -> Self {
self.modifiers.insert_mod(modifier_key);
self
}
}
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord)]
pub struct MouseBind {
pub button: MouseButton,
pub modifiers: ModifierState,
}
impl MouseBind {
pub fn new(button: MouseButton) -> Self {
Self {
button,
modifiers: ModifierState::empty(),
}
}
pub fn with_mod(mut self, modifier_key: ModifierKey) -> Self {
self.modifiers.insert_mod(modifier_key);
self
}
}
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord)]
pub struct KeyOrMouseBind {
pub key: KeyOrButton,
pub modifiers: ModifierState,
}
impl KeyOrMouseBind {
pub fn new(key: KeyOrButton) -> Self {
Self {
key,
modifiers: ModifierState::empty(),
}
}
pub fn with_mod(mut self, modifier_key: ModifierKey) -> Self {
self.modifiers.insert_mod(modifier_key);
self
}
}
impl From<&KeyBind> for KeyOrMouseBind {
fn from(keybind: &KeyBind) -> Self {
Self {
key: KeyOrButton::Key(keybind.key),
modifiers: keybind.modifiers,
}
}
}
impl From<KeyBind> for KeyOrMouseBind {
fn from(keybind: KeyBind) -> Self {
Self {
key: KeyOrButton::Key(keybind.key),
modifiers: keybind.modifiers,
}
}
}
impl From<&MouseBind> for KeyOrMouseBind {
fn from(mousebind: &MouseBind) -> Self {
Self {
key: KeyOrButton::Button(mousebind.button),
modifiers: mousebind.modifiers,
}
}
}
impl From<MouseBind> for KeyOrMouseBind {
fn from(mousebind: MouseBind) -> Self {
Self {
key: KeyOrButton::Button(mousebind.button),
modifiers: mousebind.modifiers,
}
}
}

View file

@ -0,0 +1,46 @@
use std::mem::MaybeUninit;
use x11::{xft, xlib};
use super::Display;
pub struct XftColor {
inner: xft::XftColor,
}
impl XftColor {
pub fn pixel(&self) -> u64 {
self.inner.pixel
}
#[allow(dead_code)]
pub fn color(&self) -> x11::xrender::XRenderColor {
self.inner.color
}
pub fn new(
dpy: Display,
screen: i32,
mut color_name: String,
) -> Result<Self, std::io::Error> {
color_name.push('\0');
let mut color = MaybeUninit::<xft::XftColor>::zeroed();
unsafe {
xft::XftColorAllocName(
dpy.get(),
xlib::XDefaultVisual(dpy.get(), screen),
xlib::XDefaultColormap(dpy.get(), screen),
color_name.as_ptr() as *mut _,
color.as_mut_ptr(),
) != 0
}
.then(|| Self {
inner: unsafe { color.assume_init() },
})
.ok_or(std::io::Error::new(
std::io::ErrorKind::NotFound,
"Unable to allocate color.",
))
}
}

2108
src/backends/xlib/keysym.rs Normal file

File diff suppressed because it is too large Load diff

978
src/backends/xlib/mod.rs Normal file
View file

@ -0,0 +1,978 @@
use log::{error, warn};
use std::{ffi::CString, rc::Rc};
use thiserror::Error;
use x11::xlib::{self, Atom, Window, XEvent, XInternAtom, XKeyEvent};
use crate::backends::{
keycodes::KeyOrButton, xlib::keysym::mouse_button_to_xbutton,
};
use self::keysym::{
keysym_to_virtual_keycode, virtual_keycode_to_keysym, xev_to_mouse_button,
XKeySym,
};
use super::{
keycodes::VirtualKeyCode,
window_event::{
ButtonEvent, ConfigureEvent, DestroyEvent, EnterEvent, KeyEvent,
KeyOrMouseBind, KeyState, MapEvent, ModifierState, MotionEvent, Point,
UnmapEvent, WindowEvent,
},
WindowServerBackend,
};
pub mod color;
pub mod keysym;
pub type XLibWindowEvent = WindowEvent<xlib::Window>;
#[derive(Clone)]
pub struct Display(Rc<*mut x11::xlib::Display>);
#[derive(Debug, Error)]
pub enum XlibError {
#[error("BadAccess")]
BadAccess,
#[error("BadAlloc")]
BadAlloc,
#[error("BadAtom")]
BadAtom,
#[error("BadColor")]
BadColor,
#[error("BadCursor")]
BadCursor,
#[error("BadDrawable")]
BadDrawable,
#[error("BadFont")]
BadFont,
#[error("BadGC")]
BadGC,
#[error("BadIDChoice")]
BadIDChoice,
#[error("BadImplementation")]
BadImplementation,
#[error("BadLength")]
BadLength,
#[error("BadMatch")]
BadMatch,
#[error("BadName")]
BadName,
#[error("BadPixmap")]
BadPixmap,
#[error("BadRequest")]
BadRequest,
#[error("BadValue")]
BadValue,
#[error("BadWindow")]
BadWindow,
#[error("Invalid XError: {0}")]
InvalidError(u8),
}
impl From<u8> for XlibError {
fn from(value: u8) -> Self {
match value {
xlib::BadAccess => XlibError::BadAccess,
xlib::BadAlloc => XlibError::BadAlloc,
xlib::BadAtom => XlibError::BadAtom,
xlib::BadColor => XlibError::BadColor,
xlib::BadCursor => XlibError::BadCursor,
xlib::BadDrawable => XlibError::BadDrawable,
xlib::BadFont => XlibError::BadFont,
xlib::BadGC => XlibError::BadGC,
xlib::BadIDChoice => XlibError::BadIDChoice,
xlib::BadImplementation => XlibError::BadImplementation,
xlib::BadLength => XlibError::BadLength,
xlib::BadMatch => XlibError::BadMatch,
xlib::BadName => XlibError::BadName,
xlib::BadPixmap => XlibError::BadPixmap,
xlib::BadRequest => XlibError::BadRequest,
xlib::BadValue => XlibError::BadValue,
xlib::BadWindow => XlibError::BadWindow,
any => XlibError::InvalidError(any),
}
}
}
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
}
}
pub struct XLib {
display: Display,
root: Window,
screen: i32,
atoms: XLibAtoms,
keybinds: Vec<KeyOrMouseBind>,
active_border_color: Option<color::XftColor>,
inactive_border_color: Option<color::XftColor>,
}
impl Drop for XLib {
fn drop(&mut self) {
self.close_dpy();
}
}
impl XLib {
fn new() -> Self {
let (display, screen, root) = {
let display = unsafe { xlib::XOpenDisplay(std::ptr::null()) };
assert_ne!(display, std::ptr::null_mut());
let screen = unsafe { xlib::XDefaultScreen(display) };
let root = unsafe { xlib::XRootWindow(display, screen) };
let display = Display::new(display);
(display, screen, root)
};
let atoms = XLibAtoms::init(display.clone());
Self {
display,
screen,
root,
atoms,
keybinds: Vec::new(),
active_border_color: None,
inactive_border_color: None,
}
}
unsafe fn init_as_wm(&self) {
let mut window_attributes =
std::mem::MaybeUninit::<xlib::XSetWindowAttributes>::zeroed()
.assume_init();
window_attributes.event_mask = xlib::SubstructureRedirectMask
| xlib::StructureNotifyMask
| xlib::SubstructureNotifyMask
| xlib::EnterWindowMask
| xlib::PointerMotionMask
| xlib::ButtonPressMask;
xlib::XChangeWindowAttributes(
self.dpy(),
self.root,
xlib::CWEventMask,
&mut window_attributes,
);
xlib::XSelectInput(self.dpy(), self.root, window_attributes.event_mask);
xlib::XSetErrorHandler(Some(xlib_error_handler));
xlib::XSync(self.dpy(), 0);
}
fn dpy(&self) -> *mut xlib::Display {
self.display.get()
}
fn close_dpy(&self) {
unsafe {
xlib::XCloseDisplay(self.dpy());
}
}
fn next_xevent(&mut self) -> XEvent {
let event = unsafe {
let mut event = std::mem::MaybeUninit::<xlib::XEvent>::zeroed();
xlib::XNextEvent(self.dpy(), event.as_mut_ptr());
event.assume_init()
};
// match event.get_type() {
// xlib::KeyPress | xlib::KeyRelease => {
// self.update_modifier_state(AsRef::<xlib::XKeyEvent>::as_ref(
// &event,
// ));
// }
// _ => {}
// }
event
}
fn xevent_to_window_event(&self, event: XEvent) -> Option<XLibWindowEvent> {
match event.get_type() {
xlib::MapRequest => {
let ev = unsafe { &event.map_request };
Some(XLibWindowEvent::MapRequestEvent(MapEvent {
window: ev.window,
}))
}
xlib::UnmapNotify => {
let ev = unsafe { &event.unmap };
Some(XLibWindowEvent::UnmapEvent(UnmapEvent {
window: ev.window,
}))
}
xlib::ConfigureRequest => {
let ev = unsafe { &event.configure_request };
Some(XLibWindowEvent::ConfigureEvent(ConfigureEvent {
window: ev.window,
position: (ev.x, ev.y).into(),
size: (ev.width, ev.height).into(),
}))
}
xlib::EnterNotify => {
let ev = unsafe { &event.crossing };
Some(XLibWindowEvent::EnterEvent(EnterEvent {
window: ev.window,
}))
}
xlib::DestroyNotify => {
let ev = unsafe { &event.destroy_window };
Some(XLibWindowEvent::DestroyEvent(DestroyEvent {
window: ev.window,
}))
}
xlib::MotionNotify => {
let ev = unsafe { &event.motion };
Some(XLibWindowEvent::MotionEvent(MotionEvent {
position: (ev.x, ev.y).into(),
window: ev.window,
}))
}
// both ButtonPress and ButtonRelease use the XButtonEvent structure, aliased as either
// XButtonReleasedEvent or XButtonPressedEvent
xlib::ButtonPress | xlib::ButtonRelease => {
let ev = unsafe { &event.button };
let keycode = xev_to_mouse_button(ev).unwrap();
let state = if ev.type_ == xlib::ButtonPress {
KeyState::Pressed
} else {
KeyState::Released
};
Some(XLibWindowEvent::ButtonEvent(ButtonEvent::new(
ev.subwindow,
state,
keycode,
(ev.x, ev.y).into(),
ModifierState::from_modmask(ev.state),
)))
}
xlib::KeyPress | xlib::KeyRelease => {
let ev = unsafe { &event.key };
let keycode =
keysym_to_virtual_keycode(self.keyev_to_keysym(ev).get());
let state = if ev.type_ == xlib::KeyPress {
KeyState::Pressed
} else {
KeyState::Released
};
keycode.map(|keycode| {
XLibWindowEvent::KeyEvent(KeyEvent::new(
ev.subwindow,
state,
keycode,
ModifierState::from_modmask(ev.state),
))
})
}
_ => None,
}
}
#[allow(dead_code)]
fn get_window_attributes(
&self,
window: xlib::Window,
) -> Option<xlib::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
}
}
fn check_for_protocol(
&self,
window: xlib::Window,
proto: xlib::Atom,
) -> bool {
let mut protos: *mut xlib::Atom = std::ptr::null_mut();
let mut num_protos: i32 = 0;
unsafe {
if xlib::XGetWMProtocols(
self.dpy(),
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, window: xlib::Window, proto: Atom) -> bool {
if self.check_for_protocol(window, 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,
format: 32,
message_type: self.atoms.wm_protocols,
data,
},
};
unsafe {
xlib::XSendEvent(
self.dpy(),
window,
0,
xlib::NoEventMask,
&mut event,
);
}
true
} else {
false
}
}
// #[allow(non_upper_case_globals)]
// fn update_modifier_state(&mut self, keyevent: &XKeyEvent) {
// //keyevent.keycode
// let keysym = self.keyev_to_keysym(keyevent);
// use x11::keysym::*;
// let modifier = match keysym.get() {
// XK_Shift_L | XK_Shift_R => Some(ModifierKey::Shift),
// XK_Control_L | XK_Control_R => Some(ModifierKey::Control),
// XK_Alt_L | XK_Alt_R => Some(ModifierKey::Alt),
// XK_ISO_Level3_Shift => Some(ModifierKey::AltGr),
// XK_Caps_Lock => Some(ModifierKey::ShiftLock),
// XK_Num_Lock => Some(ModifierKey::NumLock),
// XK_Win_L | XK_Win_R => Some(ModifierKey::Super),
// XK_Super_L | XK_Super_R => Some(ModifierKey::Super),
// _ => None,
// };
// if let Some(modifier) = modifier {
// match keyevent.type_ {
// KeyPress => self.modifier_state.insert_mod(modifier),
// KeyRelease => self.modifier_state.unset_mod(modifier),
// _ => unreachable!("keyyevent != (KeyPress | KeyRelease)"),
// }
// }
// }
fn get_numlock_mask(&self) -> Option<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 Some(1 << i);
}
}
}
}
None
}
fn grab_key_or_button(
&self,
binding: &KeyOrMouseBind,
window: xlib::Window,
) {
let modmask = binding.modifiers.as_modmask(self);
let numlock_mask = self
.get_numlock_mask()
.expect("failed to query numlock mask.");
let modifiers = vec![
0,
xlib::LockMask,
numlock_mask,
xlib::LockMask | numlock_mask,
];
let keycode = match binding.key {
KeyOrButton::Key(key) => self.vk_to_keycode(key),
KeyOrButton::Button(button) => mouse_button_to_xbutton(button),
};
for modifier in modifiers.iter() {
match binding.key {
KeyOrButton::Key(_) => unsafe {
xlib::XGrabKey(
self.dpy(),
keycode,
modmask | modifier,
window,
1,
xlib::GrabModeAsync,
xlib::GrabModeAsync,
);
},
KeyOrButton::Button(_) => unsafe {
xlib::XGrabButton(
self.dpy(),
keycode as u32,
modmask | modifier,
window,
1,
(xlib::ButtonPressMask
| xlib::ButtonReleaseMask
| xlib::PointerMotionMask)
as u32,
xlib::GrabModeAsync,
xlib::GrabModeAsync,
0,
0,
);
},
}
}
}
#[allow(dead_code)]
fn ungrab_key_or_button(
&self,
binding: &KeyOrMouseBind,
window: xlib::Window,
) {
let modmask = binding.modifiers.as_modmask(self);
let numlock_mask = self
.get_numlock_mask()
.expect("failed to query numlock mask.");
let modifiers = vec![
0,
xlib::LockMask,
numlock_mask,
xlib::LockMask | numlock_mask,
];
let keycode = match binding.key {
KeyOrButton::Key(key) => self.vk_to_keycode(key),
KeyOrButton::Button(button) => mouse_button_to_xbutton(button),
};
for modifier in modifiers.iter() {
match binding.key {
KeyOrButton::Key(_) => unsafe {
xlib::XUngrabKey(
self.dpy(),
keycode,
modmask | modifier,
window,
);
},
KeyOrButton::Button(_) => unsafe {
xlib::XUngrabButton(
self.dpy(),
keycode as u32,
modmask | modifier,
window,
);
},
}
}
}
fn grab_global_keybinds(&self, window: xlib::Window) {
for binding in self.keybinds.iter() {
self.grab_key_or_button(binding, window);
}
}
fn vk_to_keycode(&self, vk: VirtualKeyCode) -> i32 {
unsafe {
xlib::XKeysymToKeycode(
self.dpy(),
virtual_keycode_to_keysym(vk).unwrap() as u64,
) as i32
}
}
fn keyev_to_keysym(&self, ev: &XKeyEvent) -> XKeySym {
let keysym =
unsafe { xlib::XLookupKeysym(ev as *const _ as *mut _, 0) };
XKeySym::new(keysym as u32)
}
}
trait ModifierStateExt {
fn as_modmask(&self, xlib: &XLib) -> u32;
fn from_modmask(modmask: u32) -> Self;
}
impl ModifierStateExt for ModifierState {
fn as_modmask(&self, xlib: &XLib) -> u32 {
let mut mask = 0;
let _numlock_mask = xlib
.get_numlock_mask()
.expect("failed to query numlock mask");
mask |= xlib::ShiftMask * u32::from(self.contains(Self::SHIFT));
//mask |= xlib::LockMask * u32::from(self.contains(Self::SHIFT_LOCK));
mask |= xlib::ControlMask * u32::from(self.contains(Self::CONTROL));
mask |= xlib::Mod1Mask * u32::from(self.contains(Self::ALT));
//mask |= xlib::Mod2Mask * u32::from(self.contains(Self::NUM_LOCK));
//mask |= xlib::Mod3Mask * u32::from(self.contains(Self::ALT_GR));
mask |= xlib::Mod4Mask * u32::from(self.contains(Self::SUPER));
//mask |= numlock_mask * u32::from(self.contains(Self::NUM_LOCK));
mask
}
fn from_modmask(modmask: u32) -> Self {
let mut state = Self::empty();
state.set(Self::SHIFT, (modmask & xlib::ShiftMask) != 0);
//state.set(Self::SHIFT_LOCK, (modmask & xlib::LockMask) != 0);
state.set(Self::CONTROL, (modmask & xlib::ControlMask) != 0);
state.set(Self::ALT, (modmask & xlib::Mod1Mask) != 0);
//state.set(Self::NUM_LOCK, (modmask & xlib::Mod2Mask) != 0);
state.set(Self::ALT_GR, (modmask & xlib::Mod3Mask) != 0);
state.set(Self::SUPER, (modmask & xlib::Mod4Mask) != 0);
state
}
}
impl WindowServerBackend for XLib {
type Window = xlib::Window;
fn build() -> Self {
let xlib = Self::new();
unsafe { xlib.init_as_wm() };
xlib
}
fn next_event(&mut self) -> super::window_event::WindowEvent<Self::Window> {
loop {
let ev = self.next_xevent();
let ev = self.xevent_to_window_event(ev);
if let Some(ev) = ev {
return ev;
}
}
}
fn handle_event(
&mut self,
event: super::window_event::WindowEvent<Self::Window>,
) {
match event {
WindowEvent::MapRequestEvent(event) => {
unsafe {
xlib::XMapWindow(self.dpy(), event.window);
xlib::XSelectInput(
self.dpy(),
event.window,
xlib::EnterWindowMask
| xlib::FocusChangeMask
| xlib::PropertyChangeMask
| xlib::StructureNotifyMask,
);
}
self.grab_global_keybinds(event.window);
}
WindowEvent::ConfigureEvent(event) => {
self.configure_window(
event.window,
Some(event.size),
Some(event.position),
None,
);
}
_ => {}
}
}
fn add_keybind(&mut self, keybind: super::window_event::KeyOrMouseBind) {
self.grab_key_or_button(&keybind, self.root);
self.keybinds.push(keybind);
}
fn remove_keybind(
&mut self,
keybind: &super::window_event::KeyOrMouseBind,
) {
self.keybinds.retain(|kb| kb != keybind);
}
fn focus_window(&self, window: Self::Window) {
unsafe {
xlib::XSetInputFocus(
self.dpy(),
window,
xlib::RevertToPointerRoot,
xlib::CurrentTime,
);
let border_color = self
.active_border_color
.as_ref()
.map(|color| color.pixel())
.unwrap_or_else(|| {
xlib::XDefaultScreenOfDisplay(self.dpy())
.as_ref()
.unwrap()
.white_pixel
});
xlib::XSetWindowBorder(self.dpy(), window, border_color);
xlib::XChangeProperty(
self.dpy(),
self.root,
self.atoms.wm_active_window,
xlib::XA_WINDOW,
32,
xlib::PropModeReplace,
&window as *const u64 as *const _,
1,
);
}
self.send_protocol(window, self.atoms.wm_take_focus);
}
fn unfocus_window(&self, window: Self::Window) {
unsafe {
xlib::XSetInputFocus(
self.dpy(),
self.root,
xlib::RevertToPointerRoot,
xlib::CurrentTime,
);
// TODO: make painting the window border a seperate function, and configurable
let border_color = self
.inactive_border_color
.as_ref()
.map(|color| color.pixel())
.unwrap_or_else(|| {
xlib::XDefaultScreenOfDisplay(self.dpy())
.as_ref()
.unwrap()
.black_pixel
});
xlib::XSetWindowBorder(self.dpy(), window, border_color);
xlib::XDeleteProperty(
self.dpy(),
self.root,
self.atoms.wm_active_window,
);
}
}
fn raise_window(&self, window: Self::Window) {
unsafe {
xlib::XRaiseWindow(self.dpy(), window);
}
}
fn hide_window(&self, window: Self::Window) {
let screen_size = self.screen_size();
self.move_window(window, screen_size);
}
fn kill_window(&self, window: Self::Window) {
if !self.send_protocol(window, self.atoms.wm_delete_window) {
unsafe {
xlib::XKillClient(self.dpy(), window);
}
}
}
fn get_parent_window(&self, window: Self::Window) -> Option<Self::Window> {
let mut parent_window: Self::Window = 0;
if unsafe {
xlib::XGetTransientForHint(self.dpy(), window, &mut parent_window)
!= 0
} {
Some(parent_window)
} else {
None
}
}
fn configure_window(
&self,
window: Self::Window,
new_size: Option<super::window_event::Point<i32>>,
new_pos: Option<super::window_event::Point<i32>>,
new_border: Option<i32>,
) {
let position = new_pos.unwrap_or(Point::new(0, 0));
let size = new_size.unwrap_or(Point::new(0, 0));
let mut wc = xlib::XWindowChanges {
x: position.x,
y: position.y,
width: size.x,
height: size.y,
border_width: new_border.unwrap_or(0),
sibling: 0,
stack_mode: 0,
};
let mask = {
let mut mask = 0;
if new_pos.is_some() {
mask |= xlib::CWX | xlib::CWY;
}
if new_size.is_some() {
mask |= xlib::CWWidth | xlib::CWHeight;
}
if new_border.is_some() {
mask |= xlib::CWBorderWidth;
}
u32::from(mask)
};
unsafe {
xlib::XConfigureWindow(self.dpy(), window, mask, &mut wc);
}
}
fn screen_size(&self) -> Point<i32> {
unsafe {
let mut wa =
std::mem::MaybeUninit::<xlib::XWindowAttributes>::zeroed();
xlib::XGetWindowAttributes(self.dpy(), self.root, wa.as_mut_ptr());
let wa = wa.assume_init();
(wa.width, wa.height).into()
}
}
fn get_window_size(&self, window: Self::Window) -> Option<Point<i32>> {
self.get_window_attributes(window)
.map(|wa| (wa.width, wa.height).into())
}
fn grab_cursor(&self) {
unsafe {
xlib::XGrabPointer(
self.dpy(),
self.root,
0,
(xlib::ButtonPressMask
| xlib::ButtonReleaseMask
| xlib::PointerMotionMask) as u32,
xlib::GrabModeAsync,
xlib::GrabModeAsync,
0,
0,
xlib::CurrentTime,
);
}
}
fn ungrab_cursor(&self) {
unsafe {
xlib::XUngrabPointer(self.dpy(), xlib::CurrentTime);
}
}
fn move_cursor(&self, window: Option<Self::Window>, position: Point<i32>) {
unsafe {
xlib::XWarpPointer(
self.dpy(),
0,
window.unwrap_or(self.root),
0,
0,
0,
0,
position.x,
position.y,
);
}
}
fn all_windows(&self) -> Option<Vec<Self::Window>> {
let mut parent = 0;
let mut root = 0;
let mut children = std::ptr::null_mut();
let mut num_children = 0;
unsafe {
xlib::XQueryTree(
self.dpy(),
self.root,
&mut root,
&mut parent,
&mut children,
&mut num_children,
) != 0
}
.then(|| {
let windows = unsafe {
std::slice::from_raw_parts(children, num_children as usize)
.to_vec()
};
unsafe { xlib::XFree(children as *mut _) };
windows
})
}
fn set_active_window_border_color(&mut self, color_name: &str) {
self.active_border_color = color::XftColor::new(
self.display.clone(),
self.screen,
color_name.to_owned(),
)
.ok();
}
fn set_inactive_window_border_color(&mut self, color_name: &str) {
self.inactive_border_color = color::XftColor::new(
self.display.clone(),
self.screen,
color_name.to_owned(),
)
.ok();
}
}
#[allow(dead_code)]
struct XLibAtoms {
wm_protocols: Atom,
wm_delete_window: Atom,
wm_active_window: Atom,
wm_take_focus: Atom,
net_supported: Atom,
net_active_window: Atom,
net_client_list: Atom,
net_wm_name: Atom,
net_wm_state: Atom,
net_wm_state_fullscreen: Atom,
net_wm_window_type: Atom,
net_wm_window_type_dialog: Atom,
}
impl XLibAtoms {
fn init(display: Display) -> Self {
Self {
wm_protocols: Self::get_atom(&display, "WM_PROTOCOLS").unwrap(),
wm_delete_window: Self::get_atom(&display, "WM_DELETE_WINDOW")
.unwrap(),
wm_active_window: Self::get_atom(&display, "WM_ACTIVE_WINDOW")
.unwrap(),
wm_take_focus: Self::get_atom(&display, "WM_TAKE_FOCUS").unwrap(),
net_supported: Self::get_atom(&display, "_NET_SUPPORTED").unwrap(),
net_active_window: Self::get_atom(&display, "_NET_ACTIVE_WINDOW")
.unwrap(),
net_client_list: Self::get_atom(&display, "_NET_CLIENT_LIST")
.unwrap(),
net_wm_name: Self::get_atom(&display, "_NET_WM_NAME").unwrap(),
net_wm_state: Self::get_atom(&display, "_NET_WM_STATE").unwrap(),
net_wm_state_fullscreen: Self::get_atom(
&display,
"_NET_WM_STATE_FULLSCREEN",
)
.unwrap(),
net_wm_window_type: Self::get_atom(&display, "_NET_WM_WINDOW_TYPE")
.unwrap(),
net_wm_window_type_dialog: Self::get_atom(
&display,
"_NET_WM_WINDOW_TYPE_DIALOG",
)
.unwrap(),
}
}
fn get_atom(display: &Display, atom: &str) -> Option<Atom> {
let name = CString::new(atom).ok()?;
match unsafe { XInternAtom(display.get(), name.as_c_str().as_ptr(), 0) }
{
0 => None,
atom => Some(atom),
}
}
}
#[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_event = ee.as_ref().unwrap();
let err = XlibError::from(err_event.error_code);
match err {
err @ XlibError::BadAccess
| err @ XlibError::BadMatch
| err @ XlibError::BadWindow
| err @ XlibError::BadDrawable => {
warn!("{:?}", err);
0
}
_ => {
error!(
"wm: fatal error:\nrequest_code: {}\nerror_code: {}",
err_event.request_code, err_event.error_code
);
std::process::exit(1)
}
}
}

View file

@ -4,6 +4,7 @@ use std::{ops::Rem, usize};
use indexmap::IndexMap;
use log::{error, info};
use crate::backends::window_event::Point;
use crate::util::BuildIdentityHasher;
mod client {
@ -11,11 +12,13 @@ mod client {
use x11::xlib::Window;
use crate::backends::window_event::Point;
#[derive(Clone, Debug)]
pub struct Client {
pub(crate) window: Window,
pub(crate) size: (i32, i32),
pub(crate) position: (i32, i32),
pub(crate) size: Point<i32>,
pub(crate) position: Point<i32>,
pub(crate) transient_for: Option<Window>,
}
@ -23,8 +26,8 @@ mod client {
fn default() -> Self {
Self {
window: 0,
size: (100, 100),
position: (0, 0),
size: (100, 100).into(),
position: (0, 0).into(),
transient_for: None,
}
}
@ -34,8 +37,8 @@ mod client {
#[allow(dead_code)]
pub fn new(
window: Window,
size: (i32, i32),
position: (i32, i32),
size: Point<i32>,
position: Point<i32>,
) -> Self {
Self {
window,
@ -47,7 +50,7 @@ mod client {
pub fn new_transient(
window: Window,
size: (i32, i32),
size: Point<i32>,
transient: Window,
) -> Self {
Self {
@ -140,7 +143,7 @@ pub struct ClientState {
pub(self) virtual_screens: VirtualScreenStore,
pub(self) gap: i32,
pub(self) screen_size: (i32, i32),
pub(self) screen_size: Point<i32>,
pub(self) master_size: f32,
border_size: i32,
}
@ -166,7 +169,7 @@ impl Default for ClientState {
focused: None,
virtual_screens: VirtualScreenStore::new(1),
gap: 0,
screen_size: (1, 1),
screen_size: (1, 1).into(),
master_size: 1.0,
border_size: 0,
}
@ -189,7 +192,7 @@ impl ClientState {
}
}
pub fn with_screen_size(self, screen_size: (i32, i32)) -> Self {
pub fn with_screen_size(self, screen_size: Point<i32>) -> Self {
Self {
screen_size,
..self
@ -207,6 +210,7 @@ impl ClientState {
self.border_size
}
#[allow(dead_code)]
pub fn set_border_mut(&mut self, new: i32) {
self.border_size = new;
}
@ -221,11 +225,12 @@ impl ClientState {
client.position = {
(
transient.position.0
+ (transient.size.0 - client.size.0) / 2,
transient.position.1
+ (transient.size.1 - client.size.1) / 2,
transient.position.x
+ (transient.size.x - client.size.x) / 2,
transient.position.y
+ (transient.size.y - client.size.y) / 2,
)
.into()
};
self.floating_clients.insert(key, client);
@ -283,7 +288,7 @@ impl ClientState {
.filter(move |&(k, _)| self.is_client_visible(k))
}
fn iter_all_clients(&self) -> impl Iterator<Item = (&u64, &Client)> {
pub fn iter_all_clients(&self) -> impl Iterator<Item = (&u64, &Client)> {
self.floating_clients.iter().chain(self.clients.iter())
}
@ -532,7 +537,9 @@ impl ClientState {
{
let (new, old) = self.focus_client_inner(key);
info!("Swapping focus: new({:?}) old({:?})", new, old);
if !(new.is_vacant() && old.is_vacant()) {
info!("Swapping focus: new({:?}) old({:?})", new, old);
}
(new, old)
}
@ -622,7 +629,7 @@ impl ClientState {
*/
pub fn arrange_virtual_screen(&mut self) {
let gap = self.gap;
let (width, height) = self.screen_size;
let (width, height) = self.screen_size.as_tuple();
// should be fine to unwrap since we will always have at least 1 virtual screen
let vs = self.virtual_screens.get_mut_current();
@ -669,8 +676,8 @@ impl ClientState {
if let Some(client) = self.clients.get_mut(key) {
*client = Client {
size,
position,
size: size.into(),
position: position.into(),
..*client
};
}
@ -688,8 +695,8 @@ impl ClientState {
if let Some(client) = self.clients.get_mut(key) {
*client = Client {
size,
position,
size: size.into(),
position: position.into(),
..*client
};
}
@ -851,7 +858,7 @@ impl VirtualScreenStore {
fn go_to_nth(&mut self, n: usize) -> usize {
self.last_idx = Some(self.current_idx);
self.current_idx = n % self.screens.len();
self.current_idx = n.min(self.screens.len() - 1);
self.current_idx
}

View file

@ -1,3 +1,5 @@
use std::io::Read;
use log::{debug, error, info, trace, warn};
use log4rs::{
append::{console::ConsoleAppender, file::FileAppender},
@ -7,11 +9,30 @@ use log4rs::{
};
use state::WMConfig;
mod backends;
mod clients;
//mod clients2;
mod state;
mod util;
mod xlib;
pub mod error {
use thiserror::Error;
#[derive(Debug, Error)]
pub enum Error {
#[error("placeholder error for Result<T> as Option<T>")]
NonError,
#[error("Unknown Event")]
UnknownEvent,
#[error("Unhandled VirtualKeyCode")]
UnhandledVirtualKeyCode,
#[error(transparent)]
IoError(#[from] std::io::Error),
#[error(transparent)]
FmtError(#[from] std::fmt::Error),
#[error(transparent)]
XlibError(#[from] crate::backends::xlib::XlibError),
}
}
fn init_logger() {
let encoder = Box::new(PatternEncoder::new(
@ -46,7 +67,23 @@ fn main() {
log_prologue();
state::WindowManager::new(WMConfig::default()).run();
let mut config_path = std::path::PathBuf::from(env!("HOME"));
config_path.push(".config/nowm.toml");
let config = std::fs::File::open(config_path)
.and_then(|mut file| {
let mut content = String::new();
file.read_to_string(&mut content)?;
Ok(content)
})
.and_then(|content| Ok(toml::from_str::<WMConfig>(&content)?))
.unwrap_or_else(|e| {
warn!("error parsing config file: {}", e);
info!("falling back to default config.");
WMConfig::default()
});
state::WindowManager::<backends::xlib::XLib>::new(config).run();
}
fn log_prologue() {

View file

@ -1,38 +1,81 @@
use std::rc::Rc;
use std::{cell::RefCell, rc::Rc};
use log::{error, info};
use x11::xlib::{
self, Mod4Mask, ShiftMask, Window, XButtonPressedEvent,
XButtonReleasedEvent, XEvent, XKeyEvent, XMotionEvent,
};
use xlib::{
ButtonPressMask, ButtonReleaseMask, PointerMotionMask,
XConfigureRequestEvent, XCrossingEvent, XDestroyWindowEvent,
XMapRequestEvent, XUnmapEvent,
};
use x11::xlib::{self, Window};
use crate::{
backends::{
keycodes::{MouseButton, VirtualKeyCode},
window_event::{
ButtonEvent, ConfigureEvent, KeyBind, KeyEvent, KeyState, MapEvent,
ModifierKey, ModifierState, MotionEvent, MouseBind, Point,
WindowEvent,
},
xlib::XLib,
WindowServerBackend,
},
clients::{Client, ClientEntry, ClientKey, ClientState},
xlib::KeyOrButton,
xlib::XLib,
};
use serde::Deserialize;
/**
Contains static config data for the window manager, the sort of stuff you might want to
be able to configure in a config file.
*/
*/
#[derive(Debug, Deserialize)]
pub struct WMConfig {
num_virtualscreens: usize,
mod_key: u32,
mod_key: ModifierKey,
gap: Option<i32>,
kill_clients_on_exit: bool,
#[serde(default = "WMConfig::default_active_window_border_color")]
active_window_border_color: String,
#[serde(default = "WMConfig::default_inactive_window_border_color")]
inactive_window_border_color: String,
#[serde(default = "WMConfig::default_terminal")]
terminal_command: (String, Vec<String>),
}
pub struct WindowManager {
impl WMConfig {
fn default_active_window_border_color() -> String {
"#ffffff".to_string()
}
fn default_inactive_window_border_color() -> String {
"#444444".to_string()
}
fn default_terminal() -> (String, Vec<String>) {
("alacritty".to_string(), vec![])
}
}
impl Default for WMConfig {
fn default() -> Self {
Self {
num_virtualscreens: 10,
mod_key: ModifierKey::Super,
gap: Some(2),
kill_clients_on_exit: false,
active_window_border_color:
Self::default_active_window_border_color(),
inactive_window_border_color:
Self::default_inactive_window_border_color(),
terminal_command: Self::default_terminal(),
}
}
}
pub struct WindowManager<B = XLib>
where
B: WindowServerBackend,
{
clients: ClientState,
move_resize_window: MoveResizeInfo,
keybinds: Vec<KeyBinding>,
xlib: XLib,
keybinds: Rc<RefCell<Vec<KeyBinding<B>>>>,
backend: B,
config: WMConfig,
}
@ -51,66 +94,91 @@ enum MoveResizeInfo {
None,
}
#[derive(Debug)]
struct MoveInfoInner {
window: Window,
starting_cursor_pos: (i32, i32),
starting_window_pos: (i32, i32),
starting_cursor_pos: Point<i32>,
starting_window_pos: Point<i32>,
}
#[derive(Debug)]
struct ResizeInfoInner {
window: Window,
starting_cursor_pos: (i32, i32),
starting_window_size: (i32, i32),
starting_cursor_pos: Point<i32>,
starting_window_size: Point<i32>,
}
#[derive(Clone)]
struct KeyBinding {
key: KeyOrButton,
closure: Rc<dyn Fn(&mut WindowManager, &XKeyEvent)>,
use derivative::*;
#[derive(Derivative)]
#[derivative(Clone(bound = ""))]
struct KeyBinding<B: WindowServerBackend> {
key: KeyBind,
closure: Rc<dyn Fn(&mut WindowManager<B>, &KeyEvent<B::Window>)>,
}
impl WindowManager {
impl<B: WindowServerBackend> KeyBinding<B> {
pub fn new<F>(key: KeyBind, cb: F) -> Self
where
F: Fn(&mut WindowManager<B>, &KeyEvent<B::Window>),
F: 'static,
{
Self {
key,
closure: Rc::new(cb),
}
}
pub fn call(&self, wm: &mut WindowManager<B>, ev: &KeyEvent<B::Window>) {
(self.closure)(wm, ev);
}
}
impl<B> WindowManager<B>
where
B: WindowServerBackend<Window = xlib::Window>,
{
pub fn new(config: WMConfig) -> Self {
let xlib = XLib::new();
let backend = B::build();
let clients = ClientState::new()
.with_virtualscreens(config.num_virtualscreens)
.with_gap(config.gap.unwrap_or(1))
.with_border(1)
.with_screen_size(xlib.dimensions());
.with_screen_size(backend.screen_size());
Self {
clients,
move_resize_window: MoveResizeInfo::None,
keybinds: Vec::new(),
xlib,
keybinds: Rc::new(RefCell::new(Vec::new())),
backend,
config,
}
.init()
}
fn init(mut self) -> Self {
self.xlib.add_global_keybind(KeyOrButton::button(
1,
self.config.mod_key,
ButtonPressMask | ButtonReleaseMask | PointerMotionMask,
));
self.xlib.add_global_keybind(KeyOrButton::button(
2,
self.config.mod_key,
ButtonPressMask | ButtonReleaseMask | PointerMotionMask,
));
self.xlib.add_global_keybind(KeyOrButton::button(
3,
self.config.mod_key,
ButtonPressMask | ButtonReleaseMask | PointerMotionMask,
));
self.backend.add_keybind(
MouseBind::new(MouseButton::Left)
.with_mod(self.config.mod_key)
.into(),
);
self.backend.add_keybind(
MouseBind::new(MouseButton::Middle)
.with_mod(self.config.mod_key)
.into(),
);
self.backend.add_keybind(
MouseBind::new(MouseButton::Right)
.with_mod(self.config.mod_key)
.into(),
);
self.add_keybind(KeyBinding::new(
self.xlib.make_key("P", self.config.mod_key),
KeyBind::new(VirtualKeyCode::P).with_mod(self.config.mod_key),
|wm, _| {
wm.spawn(
"dmenu_run",
&"dmenu_run",
&[
"-m",
"0",
@ -129,23 +197,23 @@ impl WindowManager {
},
));
// self.add_keybind(KeyBinding::new(
// KeyBind::new(VirtualKeyCode::Print),
// |wm, _| wm.spawn("screenshot.sh", &[]),
// ));
// self.add_keybind(KeyBinding::new(
// KeyBind::new(VirtualKeyCode::Print).with_mod(ModifierKey::Shift),
// |wm, _| wm.spawn("screenshot.sh", &["-edit"]),
// ));
self.add_keybind(KeyBinding::new(
self.xlib.make_key("Print", 0),
|wm, _| wm.spawn("screenshot.sh", &[]),
KeyBind::new(VirtualKeyCode::M).with_mod(self.config.mod_key),
|wm, _| wm.handle_switch_stack(),
));
self.add_keybind(KeyBinding::new(
self.xlib.make_key("Print", ShiftMask),
|wm, _| wm.spawn("screenshot.sh", &["-edit"]),
));
self.add_keybind(KeyBinding::new(
self.xlib.make_key("M", self.config.mod_key),
Self::handle_switch_stack,
));
self.add_keybind(KeyBinding::new(
self.xlib.make_key("F", self.config.mod_key),
KeyBind::new(VirtualKeyCode::F).with_mod(self.config.mod_key),
|wm, _| {
wm.clients
.get_focused()
@ -158,45 +226,55 @@ impl WindowManager {
));
self.add_keybind(KeyBinding::new(
self.xlib.make_key("Q", self.config.mod_key),
Self::kill_client,
KeyBind::new(VirtualKeyCode::Q).with_mod(self.config.mod_key),
|wm, _| wm.kill_client(),
));
self.add_keybind(KeyBinding::new(
self.xlib.make_key("Q", self.config.mod_key | ShiftMask),
KeyBind::new(VirtualKeyCode::Q)
.with_mod(self.config.mod_key)
.with_mod(ModifierKey::Shift),
|wm, _| wm.quit(),
));
self.add_keybind(KeyBinding::new(
self.xlib
.make_key("Return", self.config.mod_key | ShiftMask),
|wm, _| wm.spawn("alacritty", &[]),
KeyBind::new(VirtualKeyCode::Return)
.with_mod(self.config.mod_key)
.with_mod(ModifierKey::Shift),
|wm, _| {
wm.spawn(
&wm.config.terminal_command.0,
&wm.config.terminal_command.1,
)
},
));
self.add_keybind(KeyBinding::new(
self.xlib.make_key("J", self.config.mod_key),
KeyBind::new(VirtualKeyCode::J).with_mod(self.config.mod_key),
|wm, _| wm.move_focus(Direction::south()),
));
self.add_keybind(KeyBinding::new(
self.xlib.make_key("K", self.config.mod_key),
KeyBind::new(VirtualKeyCode::K).with_mod(self.config.mod_key),
|wm, _| wm.move_focus(Direction::north()),
));
self.add_keybind(KeyBinding::new(
self.xlib.make_key("H", self.config.mod_key),
KeyBind::new(VirtualKeyCode::H).with_mod(self.config.mod_key),
|wm, _| wm.move_focus(Direction::west()),
));
self.add_keybind(KeyBinding::new(
self.xlib.make_key("L", self.config.mod_key),
KeyBind::new(VirtualKeyCode::L).with_mod(self.config.mod_key),
|wm, _| wm.move_focus(Direction::east()),
));
// resize master stack
self.add_keybind(KeyBinding::new(
self.xlib.make_key("K", self.config.mod_key | ShiftMask),
KeyBind::new(VirtualKeyCode::K)
.with_mod(self.config.mod_key)
.with_mod(ModifierKey::Shift),
|wm, _| {
wm.clients.change_master_size(0.1);
wm.arrange_clients();
@ -204,7 +282,9 @@ impl WindowManager {
));
self.add_keybind(KeyBinding::new(
self.xlib.make_key("J", self.config.mod_key | ShiftMask),
KeyBind::new(VirtualKeyCode::J)
.with_mod(self.config.mod_key)
.with_mod(ModifierKey::Shift),
|wm, _| {
wm.clients.change_master_size(-0.1);
wm.arrange_clients();
@ -213,53 +293,57 @@ impl WindowManager {
self.add_vs_switch_keybinds();
self.xlib.init();
self.backend.set_active_window_border_color(
&self.config.active_window_border_color,
);
self.backend.set_inactive_window_border_color(
&self.config.inactive_window_border_color,
);
// add all already existing windows to the WM
if let Some(windows) = self.backend.all_windows() {
windows
.into_iter()
.for_each(|window| self.new_client(window));
}
self
}
fn add_keybind(&mut self, keybind: KeyBinding) {
self.xlib.add_global_keybind(keybind.key);
self.keybinds.push(keybind);
fn add_keybind(&mut self, keybind: KeyBinding<B>) {
self.backend.add_keybind((&keybind.key).into());
self.keybinds.borrow_mut().push(keybind);
}
fn add_vs_switch_keybinds(&mut self) {
fn rotate_west<const N: usize>(wm: &mut WindowManager, _: &XKeyEvent) {
wm.rotate_virtual_screen(Direction::West(N));
}
fn rotate_east<const N: usize>(wm: &mut WindowManager, _: &XKeyEvent) {
wm.rotate_virtual_screen(Direction::East(N));
}
fn goto_nth<const N: usize>(wm: &mut WindowManager, _: &XKeyEvent) {
wm.go_to_nth_virtual_screen(N)
}
// Old keybinds
self.add_keybind(KeyBinding::new(
self.xlib.make_key("Left", self.config.mod_key),
rotate_west::<1>,
KeyBind::new(VirtualKeyCode::Left).with_mod(self.config.mod_key),
|wm, _| wm.rotate_virtual_screen(Direction::West(1)),
));
self.add_keybind(KeyBinding::new(
self.xlib.make_key("H", self.config.mod_key | ShiftMask),
rotate_west::<1>,
KeyBind::new(VirtualKeyCode::H)
.with_mod(self.config.mod_key)
.with_mod(ModifierKey::Shift),
|wm, _| wm.rotate_virtual_screen(Direction::West(1)),
));
self.add_keybind(KeyBinding::new(
self.xlib.make_key("Right", self.config.mod_key),
rotate_east::<1>,
KeyBind::new(VirtualKeyCode::Right).with_mod(self.config.mod_key),
|wm, _| wm.rotate_virtual_screen(Direction::East(1)),
));
self.add_keybind(KeyBinding::new(
self.xlib.make_key("L", self.config.mod_key | ShiftMask),
rotate_east::<1>,
KeyBind::new(VirtualKeyCode::L)
.with_mod(self.config.mod_key)
.with_mod(ModifierKey::Shift),
|wm, _| wm.rotate_virtual_screen(Direction::East(1)),
));
self.add_keybind(KeyBinding::new(
self.xlib.make_key("Tab", self.config.mod_key),
KeyBind::new(VirtualKeyCode::Tab).with_mod(self.config.mod_key),
|wm, _| wm.rotate_virtual_screen_back(),
));
@ -267,113 +351,153 @@ impl WindowManager {
// Press Mod + `1` to move go to the `1`th virtual screen
self.add_keybind(KeyBinding::new(
self.xlib.make_key("1", self.config.mod_key),
goto_nth::<1>,
KeyBind::new(VirtualKeyCode::One).with_mod(self.config.mod_key),
|wm, _| wm.go_to_nth_virtual_screen(1),
));
// Press Mod + `2` to move go to the `2`th virtual screen
self.add_keybind(KeyBinding::new(
self.xlib.make_key("2", self.config.mod_key),
goto_nth::<2>,
KeyBind::new(VirtualKeyCode::Two).with_mod(self.config.mod_key),
|wm, _| wm.go_to_nth_virtual_screen(2),
));
// Press Mod + `3` to move go to the `3`th virtual screen
self.add_keybind(KeyBinding::new(
self.xlib.make_key("3", self.config.mod_key),
goto_nth::<3>,
KeyBind::new(VirtualKeyCode::Three).with_mod(self.config.mod_key),
|wm, _| wm.go_to_nth_virtual_screen(3),
));
// Press Mod + `4` to move go to the `4`th virtual screen
self.add_keybind(KeyBinding::new(
self.xlib.make_key("4", self.config.mod_key),
goto_nth::<4>,
KeyBind::new(VirtualKeyCode::Four).with_mod(self.config.mod_key),
|wm, _| wm.go_to_nth_virtual_screen(4),
));
// Press Mod + `5` to move go to the `5`th virtual screen
self.add_keybind(KeyBinding::new(
self.xlib.make_key("5", self.config.mod_key),
goto_nth::<5>,
KeyBind::new(VirtualKeyCode::Five).with_mod(self.config.mod_key),
|wm, _| wm.go_to_nth_virtual_screen(5),
));
// Press Mod + `6` to move go to the `6`th virtual screen
self.add_keybind(KeyBinding::new(
self.xlib.make_key("6", self.config.mod_key),
goto_nth::<6>,
KeyBind::new(VirtualKeyCode::Six).with_mod(self.config.mod_key),
|wm, _| wm.go_to_nth_virtual_screen(6),
));
// Press Mod + `7` to move go to the `7`th virtual screen
self.add_keybind(KeyBinding::new(
self.xlib.make_key("7", self.config.mod_key),
goto_nth::<7>,
KeyBind::new(VirtualKeyCode::Seven).with_mod(self.config.mod_key),
|wm, _| wm.go_to_nth_virtual_screen(7),
));
// Press Mod + `8` to move go to the `8`th virtual screen
self.add_keybind(KeyBinding::new(
self.xlib.make_key("8", self.config.mod_key),
goto_nth::<8>,
KeyBind::new(VirtualKeyCode::Eight).with_mod(self.config.mod_key),
|wm, _| wm.go_to_nth_virtual_screen(8),
));
// Press Mod + `9` to move go to the `9`th virtual screen
self.add_keybind(KeyBinding::new(
self.xlib.make_key("9", self.config.mod_key),
goto_nth::<9>,
KeyBind::new(VirtualKeyCode::Nine).with_mod(self.config.mod_key),
|wm, _| wm.go_to_nth_virtual_screen(9),
));
// Press Mod + `0` to move go to the `0`th virtual screen
self.add_keybind(KeyBinding::new(
self.xlib.make_key("0", self.config.mod_key),
goto_nth::<10>,
KeyBind::new(VirtualKeyCode::Zero).with_mod(self.config.mod_key),
|wm, _| wm.go_to_nth_virtual_screen(10),
));
}
#[allow(unused_mut)]
pub fn run(mut self) -> ! {
loop {
let event = self.xlib.next_event();
let event = self.backend.next_event();
match event.get_type() {
xlib::MapRequest => self.map_request(&event),
xlib::UnmapNotify => self.unmap_notify(&event),
xlib::ConfigureRequest => self.configure_request(&event),
xlib::EnterNotify => self.enter_notify(&event),
xlib::DestroyNotify => self.destroy_notify(&event),
xlib::ButtonPress => self.button_press(event.as_ref()),
xlib::ButtonRelease => self.button_release(event.as_ref()),
xlib::MotionNotify => self.motion_notify(event.as_ref()),
xlib::KeyPress => self.handle_keybinds(event.as_ref()),
match event {
WindowEvent::KeyEvent(event) => {
if event.state == KeyState::Pressed {
self.handle_keybinds(&event);
}
}
WindowEvent::ButtonEvent(event) => {
self.button_event(&event);
}
WindowEvent::MapRequestEvent(MapEvent { window }) => {
if !self.clients.contains(&window) {
self.new_client(window);
}
self.backend.handle_event(event);
}
WindowEvent::UnmapEvent(event) => {
self.clients.remove(&event.window);
self.arrange_clients();
}
WindowEvent::EnterEvent(event) => {
self.focus_client(&event.window, false);
}
WindowEvent::MotionEvent(event) => {
self.do_move_resize_window(&event);
}
WindowEvent::ConfigureEvent(ConfigureEvent {
window, ..
}) => {
if !self.clients.contains(&window) {
self.backend.handle_event(event);
}
// TODO
// match self.clients.get(&event.window).into_option() {
// Some(client) => self
// .xlib
// .configure_client(client, self.clients.get_border()),
// None => self.xlib.configure_window(event),
// }
}
// i dont think i actually have to handle destroy notify events.
// every window should be unmapped regardless
// xlib::DestroyNotify => self.destroy_notify(&event),
_ => {}
}
}
}
fn quit(&self) -> ! {
self.xlib.close_dpy();
// TODO: should the window manager kill all clients on exit? probably
if self.config.kill_clients_on_exit {
self.clients
.iter_all_clients()
.for_each(|(&window, _)| self.backend.kill_window(window));
}
info!("Goodbye.");
std::process::exit(0);
}
fn kill_client(&mut self, _event: &XKeyEvent) {
fn kill_client(&mut self) {
if let Some(client) = self.clients.get_focused().into_option() {
self.xlib.kill_client(client);
self.backend.kill_window(client.window);
}
}
// TODO: change this somehow cuz I'm not a big fan of this "hardcoded" keybind stuff
fn handle_keybinds(&mut self, event: &XKeyEvent) {
let clean_mask = self.xlib.get_clean_mask();
for kb in self.keybinds.clone().into_iter() {
if let KeyOrButton::Key(keycode, modmask) = kb.key {
if keycode as u32 == event.keycode
&& modmask & clean_mask == event.state & clean_mask
{
(kb.closure)(self, event);
}
fn handle_keybinds(&mut self, event: &KeyEvent<B::Window>) {
// I'm not sure if this has to be a Rc<RefCell>> or if it would be better as a Cell<>
let keybinds = self.keybinds.clone();
for kb in keybinds.borrow().iter() {
if kb.key.key == event.keycode
&& kb.key.modifiers == event.modifierstate
{
kb.call(self, event);
}
}
}
fn handle_switch_stack(&mut self, _event: &XKeyEvent) {
fn handle_switch_stack(&mut self) {
if let Some(client) =
self.clients.get_focused().into_option().map(|c| c.key())
{
@ -508,22 +632,23 @@ impl WindowManager {
fn hide_hidden_clients(&self) {
self.clients
.iter_hidden()
.for_each(|(_, c)| self.xlib.hide_client(c));
.for_each(|(_, c)| self.backend.hide_window(c.window));
}
fn raise_floating_clients(&self) {
self.clients
.iter_floating()
.for_each(|(_, c)| self.xlib.raise_client(c));
.for_each(|(_, c)| self.backend.raise_window(c.window));
self.clients
.iter_transient()
.for_each(|(_, c)| self.xlib.raise_client(c));
.for_each(|(_, c)| self.backend.raise_window(c.window));
}
fn arrange_clients(&mut self) {
self.clients.iter_visible().for_each(|(_, c)| {
self.xlib.move_resize_client(c);
self.backend.move_window(c.window, c.position);
self.backend.resize_window(c.window, c.size);
//self.xlib.expose_client(c);
});
@ -548,19 +673,19 @@ impl WindowManager {
let (new, old) = self.clients.focus_client(key);
if let Some(old) = old.into_option() {
self.xlib.unfocus_client(old);
self.backend.unfocus_window(old.window);
}
match new {
ClientEntry::Floating(new) => {
self.xlib.focus_client(new);
self.backend.focus_window(new.window);
if try_raise {
self.xlib.raise_client(new);
self.backend.raise_window(new.window);
}
}
ClientEntry::Tiled(new) => {
self.xlib.focus_client(new);
self.backend.focus_window(new.window);
}
_ => {}
}
@ -569,81 +694,44 @@ impl WindowManager {
fn new_client(&mut self, window: Window) {
info!("new client: {:?}", window);
let client = if let Some(transient_window) =
self.xlib.get_transient_for_window(window)
self.backend.get_parent_window(window)
{
Client::new_transient(
window,
self.xlib.get_window_size(window).unwrap_or((100, 100)),
self.backend
.get_window_size(window)
.unwrap_or((100, 100).into()),
transient_window,
)
} else {
Client::new_default(window)
};
self.xlib
.configure_client(&client, self.clients.get_border());
self.backend.configure_window(
window,
None,
None,
Some(self.clients.get_border()),
);
self.clients.insert(client).unwrap();
self.arrange_clients();
self.xlib.map_window(window);
self.focus_client(&window, true);
}
fn map_request(&mut self, event: &XEvent) {
let event: &XMapRequestEvent = event.as_ref();
if !self.clients.contains(&event.window) {
self.new_client(event.window);
}
}
fn unmap_notify(&mut self, event: &XEvent) {
let event: &XUnmapEvent = event.as_ref();
self.clients.remove(&event.window);
self.arrange_clients();
}
fn destroy_notify(&mut self, event: &XEvent) {
let event: &XDestroyWindowEvent = event.as_ref();
self.clients.remove(&event.window);
self.arrange_clients();
}
fn configure_request(&mut self, event: &XEvent) {
let event: &XConfigureRequestEvent = event.as_ref();
match self.clients.get(&event.window).into_option() {
Some(client) => self
.xlib
.configure_client(client, self.clients.get_border()),
None => self.xlib.configure_window(event),
}
}
fn enter_notify(&mut self, event: &XEvent) {
let event: &XCrossingEvent = event.as_ref();
self.focus_client(&event.window, false);
}
/// ensure event.subwindow refers to a valid client.
fn start_move_resize_window(&mut self, event: &XButtonPressedEvent) {
let window = event.subwindow;
fn start_move_resize_window(&mut self, event: &ButtonEvent<B::Window>) {
let window = event.window; // xev.subwindow
match event.button {
1 => {
match event.keycode {
MouseButton::Left => {
if self.clients.set_floating(&window) {
self.arrange_clients();
}
self.move_resize_window = MoveResizeInfo::Move(MoveInfoInner {
window,
starting_cursor_pos: (event.x, event.y),
starting_cursor_pos: event.cursor_position,
starting_window_pos: self
.clients
.get(&window)
@ -651,7 +739,7 @@ impl WindowManager {
.position,
});
}
3 => {
MouseButton::Right => {
if self.clients.set_floating(&window) {
self.arrange_clients();
}
@ -660,18 +748,18 @@ impl WindowManager {
let corner_pos = {
(
client.position.0 + client.size.0,
client.position.1 + client.size.1,
client.position.x + client.size.x,
client.position.y + client.size.y,
)
};
self.xlib.move_cursor(None, corner_pos);
self.xlib.grab_cursor();
self.backend.move_cursor(None, corner_pos.into());
self.backend.grab_cursor();
self.move_resize_window =
MoveResizeInfo::Resize(ResizeInfoInner {
window,
starting_cursor_pos: corner_pos,
starting_cursor_pos: corner_pos.into(),
starting_window_size: client.size,
});
}
@ -679,21 +767,25 @@ impl WindowManager {
}
}
fn end_move_resize_window(&mut self, event: &XButtonReleasedEvent) {
if event.button == 1 || event.button == 3 {
self.move_resize_window = MoveResizeInfo::None;
}
if event.button == 3 {
self.xlib.release_cursor();
fn end_move_resize_window(&mut self, event: &ButtonEvent<B::Window>) {
match event.keycode {
MouseButton::Left => {
self.move_resize_window = MoveResizeInfo::None;
}
MouseButton::Right => {
self.move_resize_window = MoveResizeInfo::None;
self.backend.ungrab_cursor();
}
_ => {}
}
}
fn do_move_resize_window(&mut self, event: &XMotionEvent) {
fn do_move_resize_window(&mut self, event: &MotionEvent<B::Window>) {
match &self.move_resize_window {
MoveResizeInfo::Move(info) => {
let (x, y) = (
event.x - info.starting_cursor_pos.0,
event.y - info.starting_cursor_pos.1,
event.position.x - info.starting_cursor_pos.x,
event.position.y - info.starting_cursor_pos.y,
);
if let Some(client) =
@ -701,16 +793,18 @@ impl WindowManager {
{
let position = &mut client.position;
position.0 = info.starting_window_pos.0 + x;
position.1 = info.starting_window_pos.1 + y;
position.x = info.starting_window_pos.x + x;
position.y = info.starting_window_pos.y + y;
self.xlib.move_client(client);
self.backend.move_window(client.window, client.position);
}
}
MoveResizeInfo::Resize(info) => {
info!("do_resize: {:#?}", info);
let (x, y) = (
event.x - info.starting_cursor_pos.0,
event.y - info.starting_cursor_pos.1,
event.position.x - info.starting_cursor_pos.x,
event.position.y - info.starting_cursor_pos.y,
);
if let Some(client) =
@ -718,85 +812,76 @@ impl WindowManager {
{
let size = &mut client.size;
size.0 = std::cmp::max(1, info.starting_window_size.0 + x);
size.1 = std::cmp::max(1, info.starting_window_size.1 + y);
size.x = std::cmp::max(1, info.starting_window_size.x + x);
size.y = std::cmp::max(1, info.starting_window_size.y + y);
self.xlib.resize_client(client);
self.backend.resize_window(client.window, client.size);
}
}
_ => {}
}
}
fn button_press(&mut self, event: &XButtonPressedEvent) {
self.focus_client(&event.subwindow, true);
fn button_event(&mut self, event: &ButtonEvent<B::Window>) {
match event.state {
KeyState::Pressed => {
self.focus_client(&event.window, true);
match event.button {
1 | 3 => match self.move_resize_window {
MoveResizeInfo::None
if self
.xlib
.are_masks_equal(event.state, self.config.mod_key)
&& self.clients.contains(&event.subwindow) =>
{
self.start_move_resize_window(event)
match event.keycode {
MouseButton::Left | MouseButton::Right => {
match self.move_resize_window {
MoveResizeInfo::None
if ModifierState::from([self
.config
.mod_key])
.eq(&event.modifierstate)
&& self.clients.contains(&event.window) =>
{
self.start_move_resize_window(event)
}
_ => {}
}
}
MouseButton::Middle => {
self.clients.toggle_floating(&event.window);
self.arrange_clients();
}
_ => {}
}
}
KeyState::Released => match self.move_resize_window {
MoveResizeInfo::None => {}
_ => {
self.end_move_resize_window(event);
}
_ => {}
},
2 => {
self.clients.toggle_floating(&event.subwindow);
self.arrange_clients();
}
_ => {}
}
}
fn button_release(&mut self, event: &XButtonReleasedEvent) {
match self.move_resize_window {
MoveResizeInfo::None => {}
_ => {
self.end_move_resize_window(event);
}
}
}
fn motion_notify(&mut self, event: &XMotionEvent) {
self.do_move_resize_window(event);
}
pub fn spawn(&self, command: &str, args: &[&str]) {
info!("spawn: {:?} {:?}", command, args.join(" "));
match std::process::Command::new(command).args(args).spawn() {
pub fn spawn<'a, S, I>(&self, command: S, args: I)
where
S: AsRef<str> + AsRef<std::ffi::OsStr>,
I: IntoIterator<Item = S> + std::fmt::Debug,
{
info!("spawn: {:?} {:?}", AsRef::<str>::as_ref(&command), args);
match std::process::Command::new(AsRef::<std::ffi::OsStr>::as_ref(
&command,
))
.args(args)
.spawn()
{
Ok(_) => {}
Err(err) => {
error!("Failed to spawn {:?}: {:?}", command, err);
error!(
"Failed to spawn {:?}: {:?}",
AsRef::<str>::as_ref(&command),
err
);
}
}
}
}
impl KeyBinding {
fn new<F>(key: KeyOrButton, closure: F) -> Self
where
F: Fn(&mut WindowManager, &XKeyEvent) + 'static,
{
Self {
key,
closure: Rc::new(closure),
}
}
}
impl Default for WMConfig {
fn default() -> Self {
Self {
num_virtualscreens: 10,
mod_key: Mod4Mask,
gap: Some(2),
}
}
}
impl Direction {
fn west() -> Self {
Direction::West(1)

View file

@ -53,6 +53,12 @@ impl KeyOrButton {
#[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 {
@ -268,16 +274,16 @@ impl XLib {
pub fn move_resize_client(&self, client: &Client) {
let mut windowchanges = xlib::XWindowChanges {
x: client.position.0,
y: client.position.1,
width: client.size.0,
height: client.size.1,
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.0 < 1 || client.size.1 < 1 {
if client.size.x < 1 || client.size.y < 1 {
error!("client {:?} size is less than 1 pixel!", client);
} else {
unsafe {
@ -297,16 +303,16 @@ impl XLib {
pub fn move_client(&self, client: &Client) {
let mut wc = xlib::XWindowChanges {
x: client.position.0,
y: client.position.1,
width: client.size.0,
height: client.size.1,
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.0 < 1 || client.size.1 < 1 {
if client.size.x < 1 || client.size.y < 1 {
error!("client {:?} size is less than 1 pixel!", client);
} else {
unsafe {
@ -322,16 +328,16 @@ impl XLib {
pub fn resize_client(&self, client: &Client) {
let mut wc = xlib::XWindowChanges {
x: client.position.0,
y: client.position.1,
width: client.size.0,
height: client.size.1,
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.0 < 1 || client.size.1 < 1 {
if client.size.x < 1 || client.size.y < 1 {
error!("client {:?} size is less than 1 pixel!", client);
} else {
unsafe {
@ -347,16 +353,16 @@ impl XLib {
pub fn hide_client(&self, client: &Client) {
let mut wc = xlib::XWindowChanges {
x: client.size.0 * -2,
y: client.position.1,
width: client.size.0,
height: client.size.1,
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.0 < 1 || client.size.1 < 1 {
if client.size.x < 1 || client.size.y < 1 {
error!("client {:?} size is less than 1 pixel!", client);
} else {
unsafe {
@ -391,6 +397,7 @@ impl XLib {
}
}
#[allow(dead_code)]
fn get_window_attributes(
&self,
window: Window,
@ -421,10 +428,12 @@ impl XLib {
}
}
#[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 {
@ -468,10 +477,10 @@ impl XLib {
display: self.dpy(),
event: client.window,
window: client.window,
x: client.position.0,
y: client.position.1,
width: client.size.0,
height: client.size.1,
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,
@ -527,7 +536,7 @@ impl XLib {
}
}
pub fn close_dpy(&self) {
fn close_dpy(&self) {
unsafe {
XCloseDisplay(self.dpy());
}

Binary file not shown.

Before

Width:  |  Height:  |  Size: 210 KiB

View file

@ -1,8 +0,0 @@
#!/bin/sh
xset b off
xsetroot -solid darkslategrey
feh --bg-fill "/mnt/storage/rust/wm/starship.jpg"
xset r rate 250 30
export RUST_BACKTRACE=1
exec /mnt/storage/code/rust/wm/target/release/wm 2>&1 /home/user/.local/portlights.log