Merge branch 'refactor-2'
This commit is contained in:
commit
4810d88dc1
1
.gitignore
vendored
1
.gitignore
vendored
|
@ -3,3 +3,4 @@
|
|||
/Cargo.lock
|
||||
/.cargo/
|
||||
/wmlog
|
||||
/xinitrc.tmp
|
||||
|
|
|
@ -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"
|
5
Makefile
5
Makefile
|
@ -1,5 +0,0 @@
|
|||
run:
|
||||
startx ./xinitrc :0 vt2
|
||||
|
||||
run-root:
|
||||
sudo startx ./xinitrc :0 vt2
|
|
@ -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 :^)
|
||||

|
||||
No WM also has optional gaps :^)
|
||||

|
||||
|
|
BIN
abstract1.jpg
BIN
abstract1.jpg
Binary file not shown.
Before Width: | Height: | Size: 853 KiB |
4
nowm.toml
Normal file
4
nowm.toml
Normal file
|
@ -0,0 +1,4 @@
|
|||
num_virtualscreens = 10
|
||||
mod_key = "Super"
|
||||
gap = 5
|
||||
kill_clients_on_exit = false
|
12
runner.sh
Executable file
12
runner.sh
Executable 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
211
src/backends/keycodes.rs
Normal 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
6
src/backends/mod.rs
Normal 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
53
src/backends/traits.rs
Normal 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);
|
||||
}
|
||||
}
|
377
src/backends/window_event.rs
Normal file
377
src/backends/window_event.rs
Normal 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,
|
||||
}
|
||||
}
|
||||
}
|
46
src/backends/xlib/color.rs
Normal file
46
src/backends/xlib/color.rs
Normal 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
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
978
src/backends/xlib/mod.rs
Normal 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)
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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
|
||||
}
|
||||
|
|
43
src/main.rs
43
src/main.rs
|
@ -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() {
|
||||
|
|
645
src/state.rs
645
src/state.rs
|
@ -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)
|
||||
|
|
59
src/xlib.rs
59
src/xlib.rs
|
@ -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());
|
||||
}
|
||||
|
|
BIN
starship.jpg
BIN
starship.jpg
Binary file not shown.
Before Width: | Height: | Size: 210 KiB |
Loading…
Reference in a new issue