Compare commits
73 commits
refactor
...
feature_cu
Author | SHA1 | Date | |
---|---|---|---|
|
1d15ef6336 | ||
|
ae89404de3 | ||
|
fdf81d8d6a | ||
|
1aab741b49 | ||
|
c826556e83 | ||
|
30867df46c | ||
|
364d621b72 | ||
|
bae880c5e1 | ||
|
449b4cccd8 | ||
|
6c4f0d54bd | ||
|
4eb1cb4555 | ||
|
f9afdc990d | ||
|
ac433847c5 | ||
|
0dd42a7039 | ||
|
85d3c3ce79 | ||
|
fb011ea23f | ||
|
2f805dab21 | ||
|
56fff2698b | ||
|
db6ffb9416 | ||
|
ba047217a6 | ||
|
bc13bf43d6 | ||
|
590af3a06c | ||
|
702004d2d2 | ||
|
daf9f72a89 | ||
|
71ddeb6af1 | ||
|
b1895bdd07 | ||
|
e49fdfa5be | ||
|
b3f586ea6a | ||
|
2c6d4fd465 | ||
|
192f865fec | ||
|
25c0d94217 | ||
|
b49bfed1f0 | ||
|
c3f3ad7203 | ||
|
5dbfa6fbcf | ||
|
c9b926f5ba | ||
|
f6a871d1e7 | ||
|
7961c97d2f | ||
|
81a49e8290 | ||
|
f26ca7948b | ||
|
4810d88dc1 | ||
|
f57a5f8033 | ||
|
4d49ae52fd | ||
|
8f5f60455c | ||
|
c72356a087 | ||
|
6404888941 | ||
|
ece0eb7903 | ||
|
3a56102ec2 | ||
|
d3b4fcbf18 | ||
|
df3c2e33ce | ||
|
053afa576e | ||
|
2e589bf94b | ||
|
91b5c91bd5 | ||
|
b47f245250 | ||
|
2b4ddc8b5a | ||
|
d3f630549e | ||
|
964d6fe748 | ||
|
72129ba61e | ||
|
696559d0af | ||
|
aafbcf2314 | ||
|
af21769d52 | ||
|
d3afc30ceb | ||
|
a85d8d0df5 | ||
|
db17c9dbfe | ||
|
57863e2eb7 | ||
|
bea2ad6688 | ||
|
6c3999caab | ||
|
ee1aa9cfae | ||
|
ff27ec18d9 | ||
|
a175362a32 | ||
|
1bc0c98156 | ||
|
94c5cd9111 | ||
|
8bd8894736 | ||
|
9f77d5f570 |
1
.gitignore
vendored
1
.gitignore
vendored
|
@ -3,3 +3,4 @@
|
|||
/Cargo.lock
|
||||
/.cargo/
|
||||
/wmlog
|
||||
/xinitrc.tmp
|
||||
|
|
26
Cargo.toml
26
Cargo.toml
|
@ -1,15 +1,27 @@
|
|||
[package]
|
||||
name = "wm"
|
||||
version = "0.2.0"
|
||||
version = "0.3.0"
|
||||
authors = ["noonebtw <noonebtw@gmail.com>"]
|
||||
edition = "2018"
|
||||
|
||||
[[bin]]
|
||||
name = "nirgendwm"
|
||||
path = "src/main.rs"
|
||||
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
[dependencies]
|
||||
x11 = {version = "2.18.2", features = ["xlib"] }
|
||||
log = "0.4.13"
|
||||
simple_logger = "1.11.0"
|
||||
dirs = "3.0.2"
|
||||
log4rs = "1.0.0"
|
||||
indexmap = "1.6.2"
|
||||
x11 = {version = "2.19", features = ["xlib", "xft"] }
|
||||
log = "0.4"
|
||||
simple_logger = "2.0"
|
||||
dirs = "3.0.0"
|
||||
log4rs = "1.0"
|
||||
indexmap = "1.0"
|
||||
thiserror = "1.0"
|
||||
bitflags = "1.3"
|
||||
derivative = "2.2.0"
|
||||
serde = { version = "1.0", features = ["derive"] }
|
||||
toml = "0.5"
|
||||
num-traits = "0.2"
|
||||
strum = {version = "0.24.0", features = ["derive"]}
|
||||
bytemuck = "1.0"
|
||||
|
|
|
@ -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 |
8
nirgendwm.toml
Normal file
8
nirgendwm.toml
Normal file
|
@ -0,0 +1,8 @@
|
|||
num_virtualscreens = 10
|
||||
mod_key = "Super"
|
||||
gap = 10
|
||||
border_width = 5
|
||||
active_window_border_color = "#6E0AC4"
|
||||
inactive_window_border_color = "#CE9CFA"
|
||||
kill_clients_on_exit = false
|
||||
terminal_command = ["alacritty", []]
|
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,
|
||||
}
|
28
src/backends/mod.rs
Normal file
28
src/backends/mod.rs
Normal file
|
@ -0,0 +1,28 @@
|
|||
pub mod keycodes;
|
||||
pub mod traits;
|
||||
pub mod window_event;
|
||||
pub mod xlib;
|
||||
|
||||
pub use traits::*;
|
||||
|
||||
pub mod structs {
|
||||
|
||||
#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Clone, Copy)]
|
||||
pub enum WindowType {
|
||||
Splash,
|
||||
Dialog,
|
||||
Normal,
|
||||
Utility,
|
||||
Menu,
|
||||
Toolbar,
|
||||
Dock,
|
||||
Desktop,
|
||||
}
|
||||
|
||||
#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Clone)]
|
||||
pub enum Cursor {
|
||||
Normal,
|
||||
Resize,
|
||||
Move,
|
||||
}
|
||||
}
|
61
src/backends/traits.rs
Normal file
61
src/backends/traits.rs
Normal file
|
@ -0,0 +1,61 @@
|
|||
use super::{
|
||||
structs::WindowType,
|
||||
window_event::{self, KeyOrMouseBind},
|
||||
};
|
||||
use crate::util::{Point, Size};
|
||||
|
||||
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 set_window_fullscreen_state(
|
||||
&self,
|
||||
window: Self::Window,
|
||||
fullscreen: bool,
|
||||
);
|
||||
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<Size<i32>>,
|
||||
new_pos: Option<Point<i32>>,
|
||||
new_border: Option<i32>,
|
||||
);
|
||||
|
||||
fn screen_size(&self) -> Size<i32>;
|
||||
fn get_window_size(&self, window: Self::Window) -> Option<Size<i32>>;
|
||||
fn get_window_name(&self, window: Self::Window) -> Option<String>;
|
||||
fn get_window_type(&self, window: Self::Window) -> WindowType;
|
||||
|
||||
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: Size<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);
|
||||
}
|
||||
}
|
386
src/backends/window_event.rs
Normal file
386
src/backends/window_event.rs
Normal file
|
@ -0,0 +1,386 @@
|
|||
#![allow(dead_code)]
|
||||
|
||||
use super::{
|
||||
keycodes::{KeyOrButton, MouseButton, VirtualKeyCode},
|
||||
structs::WindowType,
|
||||
};
|
||||
use crate::util::{Point, Size};
|
||||
use bitflags::bitflags;
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
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 },
|
||||
WindowNameEvent(WindowNameEvent<Window>),
|
||||
WindowTypeChangedEvent(WindowTypeChangedEvent<Window>),
|
||||
}
|
||||
|
||||
#[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, Clone)]
|
||||
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, Clone)]
|
||||
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, Clone)]
|
||||
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, Clone)]
|
||||
pub struct MapEvent<Window> {
|
||||
pub window: Window,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct UnmapEvent<Window> {
|
||||
pub window: Window,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct EnterEvent<Window> {
|
||||
pub window: Window,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct DestroyEvent<Window> {
|
||||
pub window: Window,
|
||||
}
|
||||
|
||||
impl<Window> DestroyEvent<Window> {
|
||||
pub fn new(window: Window) -> Self {
|
||||
Self { window }
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct CreateEvent<Window> {
|
||||
pub window: Window,
|
||||
pub position: Point<i32>,
|
||||
pub size: Size<i32>,
|
||||
}
|
||||
|
||||
impl<Window> CreateEvent<Window> {
|
||||
pub fn new(window: Window, position: Point<i32>, size: Size<i32>) -> Self {
|
||||
Self {
|
||||
window,
|
||||
position,
|
||||
size,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct ConfigureEvent<Window> {
|
||||
pub window: Window,
|
||||
pub position: Point<i32>,
|
||||
pub size: Size<i32>,
|
||||
}
|
||||
|
||||
impl<Window> ConfigureEvent<Window> {
|
||||
pub fn new(window: Window, position: Point<i32>, size: Size<i32>) -> Self {
|
||||
Self {
|
||||
window,
|
||||
position,
|
||||
size,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub enum FullscreenState {
|
||||
On,
|
||||
Off,
|
||||
Toggle,
|
||||
}
|
||||
|
||||
impl From<bool> for FullscreenState {
|
||||
fn from(value: bool) -> Self {
|
||||
match value {
|
||||
true => Self::On,
|
||||
false => Self::Off,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct FullscreenEvent<Window> {
|
||||
pub window: Window,
|
||||
pub state: FullscreenState,
|
||||
}
|
||||
|
||||
impl<Window> FullscreenEvent<Window> {
|
||||
pub fn new(window: Window, state: FullscreenState) -> Self {
|
||||
Self { window, state }
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct WindowNameEvent<Window> {
|
||||
pub window: Window,
|
||||
pub name: String,
|
||||
}
|
||||
|
||||
impl<Window> WindowNameEvent<Window> {
|
||||
pub fn new(window: Window, name: String) -> Self {
|
||||
Self { window, name }
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct WindowTypeChangedEvent<Window> {
|
||||
pub window: Window,
|
||||
pub window_type: WindowType,
|
||||
}
|
||||
|
||||
impl<Window> WindowTypeChangedEvent<Window> {
|
||||
pub fn new(window: Window, window_type: WindowType) -> Self {
|
||||
Self {
|
||||
window,
|
||||
window_type,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[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
1877
src/backends/xlib/mod.rs
Normal file
1877
src/backends/xlib/mod.rs
Normal file
File diff suppressed because it is too large
Load diff
426
src/clients.rs
426
src/clients.rs
|
@ -1,31 +1,41 @@
|
|||
use std::num::NonZeroI32;
|
||||
use std::{ops::Rem, usize};
|
||||
|
||||
use indexmap::IndexMap;
|
||||
use log::{error, info};
|
||||
use log::error;
|
||||
use num_traits::Zero;
|
||||
|
||||
use crate::backends::structs::WindowType;
|
||||
use crate::util::BuildIdentityHasher;
|
||||
use crate::util::{Point, Size};
|
||||
|
||||
mod client {
|
||||
use std::hash::{Hash, Hasher};
|
||||
|
||||
use crate::{
|
||||
backends::structs::WindowType,
|
||||
util::{Point, Size},
|
||||
};
|
||||
use x11::xlib::Window;
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct Client {
|
||||
pub(crate) window: Window,
|
||||
pub(crate) size: (i32, i32),
|
||||
pub(crate) position: (i32, i32),
|
||||
pub(crate) transient_for: Option<Window>,
|
||||
pub(crate) size: Size<i32>,
|
||||
pub(crate) position: Point<i32>,
|
||||
pub(crate) parent_window: Option<Window>,
|
||||
pub(crate) window_type: WindowType,
|
||||
pub(crate) fullscreen: bool,
|
||||
}
|
||||
|
||||
impl Default for Client {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
window: 0,
|
||||
size: (100, 100),
|
||||
position: (0, 0),
|
||||
transient_for: None,
|
||||
size: (100, 100).into(),
|
||||
position: (0, 0).into(),
|
||||
parent_window: None,
|
||||
fullscreen: false,
|
||||
window_type: WindowType::Normal,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -34,26 +44,26 @@ mod client {
|
|||
#[allow(dead_code)]
|
||||
pub fn new(
|
||||
window: Window,
|
||||
size: (i32, i32),
|
||||
position: (i32, i32),
|
||||
size: Size<i32>,
|
||||
position: Point<i32>,
|
||||
) -> Self {
|
||||
Self {
|
||||
window,
|
||||
size,
|
||||
position,
|
||||
transient_for: None,
|
||||
..Self::default()
|
||||
}
|
||||
}
|
||||
|
||||
pub fn new_transient(
|
||||
pub fn new_dialog(
|
||||
window: Window,
|
||||
size: (i32, i32),
|
||||
size: Size<i32>,
|
||||
transient: Window,
|
||||
) -> Self {
|
||||
Self {
|
||||
window,
|
||||
size,
|
||||
transient_for: Some(transient),
|
||||
parent_window: Some(transient),
|
||||
..Default::default()
|
||||
}
|
||||
}
|
||||
|
@ -65,8 +75,47 @@ mod client {
|
|||
}
|
||||
}
|
||||
|
||||
pub fn is_transient(&self) -> bool {
|
||||
self.transient_for.is_some()
|
||||
pub fn with_window_type(self, window_type: WindowType) -> Self {
|
||||
Self {
|
||||
window_type,
|
||||
..self
|
||||
}
|
||||
}
|
||||
|
||||
pub fn with_parent_window(self, parent_window: Option<Window>) -> Self {
|
||||
Self {
|
||||
parent_window,
|
||||
..self
|
||||
}
|
||||
}
|
||||
|
||||
pub fn with_size(self, size: Size<i32>) -> Self {
|
||||
Self { size, ..self }
|
||||
}
|
||||
|
||||
/// toggles the clients fullscreen flag.
|
||||
/// returns `true` if the client is now fullscreen.
|
||||
pub fn toggle_fullscreen(&mut self) -> bool {
|
||||
self.fullscreen = !self.fullscreen;
|
||||
|
||||
self.is_fullscreen()
|
||||
}
|
||||
|
||||
pub fn set_fullscreen(&mut self, fullscreen: bool) -> bool {
|
||||
if self.fullscreen == fullscreen {
|
||||
false
|
||||
} else {
|
||||
self.fullscreen = fullscreen;
|
||||
true
|
||||
}
|
||||
}
|
||||
|
||||
pub fn is_fullscreen(&self) -> bool {
|
||||
self.fullscreen
|
||||
}
|
||||
|
||||
pub fn has_parent_window(&self) -> bool {
|
||||
self.parent_window.is_some()
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -140,7 +189,7 @@ pub struct ClientState {
|
|||
pub(self) virtual_screens: VirtualScreenStore,
|
||||
|
||||
pub(self) gap: i32,
|
||||
pub(self) screen_size: (i32, i32),
|
||||
pub(self) screen_size: Size<i32>,
|
||||
pub(self) master_size: f32,
|
||||
border_size: i32,
|
||||
}
|
||||
|
@ -166,7 +215,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 +238,7 @@ impl ClientState {
|
|||
}
|
||||
}
|
||||
|
||||
pub fn with_screen_size(self, screen_size: (i32, i32)) -> Self {
|
||||
pub fn with_screen_size(self, screen_size: Size<i32>) -> Self {
|
||||
Self {
|
||||
screen_size,
|
||||
..self
|
||||
|
@ -207,6 +256,7 @@ impl ClientState {
|
|||
self.border_size
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
pub fn set_border_mut(&mut self, new: i32) {
|
||||
self.border_size = new;
|
||||
}
|
||||
|
@ -214,25 +264,41 @@ impl ClientState {
|
|||
pub fn insert(&mut self, mut client: Client) -> Option<&Client> {
|
||||
let key = client.key();
|
||||
|
||||
if client.is_transient()
|
||||
&& self.contains(&client.transient_for.unwrap())
|
||||
{
|
||||
let transient = self.get(&client.transient_for.unwrap()).unwrap();
|
||||
match client.window_type {
|
||||
// idk how to handle docks and desktops, for now they float innit
|
||||
WindowType::Splash
|
||||
| WindowType::Dialog
|
||||
| WindowType::Utility
|
||||
| WindowType::Menu
|
||||
| WindowType::Toolbar
|
||||
| WindowType::Dock
|
||||
| WindowType::Desktop => {
|
||||
if let Some(parent) = client
|
||||
.parent_window
|
||||
.and_then(|window| self.get(&window).into_option())
|
||||
{
|
||||
client.position = {
|
||||
(
|
||||
parent.position.x
|
||||
+ (parent.size.width - client.size.width) / 2,
|
||||
parent.position.y
|
||||
+ (parent.size.height - client.size.height) / 2,
|
||||
)
|
||||
.into()
|
||||
};
|
||||
}
|
||||
|
||||
client.position = {
|
||||
(
|
||||
transient.position.0
|
||||
+ (transient.size.0 - client.size.0) / 2,
|
||||
transient.position.1
|
||||
+ (transient.size.1 - client.size.1) / 2,
|
||||
)
|
||||
};
|
||||
client.size = client.size.clamp(
|
||||
self.screen_size
|
||||
- Size::new(self.border_size * 2, self.border_size * 2),
|
||||
);
|
||||
|
||||
self.floating_clients.insert(key, client);
|
||||
} else {
|
||||
self.clients.insert(key, client);
|
||||
|
||||
self.virtual_screens.get_mut_current().insert(&key);
|
||||
self.floating_clients.insert(key, client);
|
||||
}
|
||||
WindowType::Normal => {
|
||||
self.clients.insert(key, client);
|
||||
self.virtual_screens.get_mut_current().insert(&key);
|
||||
}
|
||||
}
|
||||
|
||||
// adding a client changes the liling layout, rearrange
|
||||
|
@ -283,7 +349,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())
|
||||
}
|
||||
|
||||
|
@ -293,7 +359,15 @@ impl ClientState {
|
|||
}
|
||||
|
||||
pub fn iter_transient(&self) -> impl Iterator<Item = (&u64, &Client)> {
|
||||
self.iter_floating().filter(|&(_, c)| c.is_transient())
|
||||
self.iter_floating().filter(|&(_, c)| c.has_parent_window())
|
||||
}
|
||||
|
||||
pub fn iter_by_window_type(
|
||||
&self,
|
||||
window_type: WindowType,
|
||||
) -> impl Iterator<Item = (&u64, &Client)> {
|
||||
self.iter_floating()
|
||||
.filter(move |&(_, c)| c.window_type == window_type)
|
||||
}
|
||||
|
||||
pub fn iter_visible(&self) -> impl Iterator<Item = (&u64, &Client)> {
|
||||
|
@ -330,7 +404,7 @@ impl ClientState {
|
|||
{
|
||||
match self.get(key) {
|
||||
ClientEntry::Floating(c) => {
|
||||
if let Some(transient_for) = c.transient_for {
|
||||
if let Some(transient_for) = c.parent_window {
|
||||
self.is_client_visible(&transient_for)
|
||||
} else {
|
||||
true
|
||||
|
@ -403,6 +477,49 @@ impl ClientState {
|
|||
self.arrange_virtual_screen();
|
||||
}
|
||||
|
||||
pub fn set_fullscreen<K>(&mut self, key: &K, fullscreen: bool) -> bool
|
||||
where
|
||||
K: ClientKey,
|
||||
{
|
||||
self.get(key)
|
||||
.into_option()
|
||||
.map(|client| client.is_fullscreen() != fullscreen)
|
||||
.unwrap_or(false)
|
||||
.then(|| self.toggle_fullscreen(key))
|
||||
.unwrap_or(false)
|
||||
}
|
||||
|
||||
/// returns `true` if window layout changed
|
||||
pub fn toggle_fullscreen<K>(&mut self, key: &K) -> bool
|
||||
where
|
||||
K: ClientKey,
|
||||
{
|
||||
if let Some(_new_fullscreen_state) = self.inner_toggle_fullscreen(key) {
|
||||
self.arrange_virtual_screen();
|
||||
true
|
||||
} else {
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
fn inner_toggle_fullscreen<K>(&mut self, key: &K) -> Option<bool>
|
||||
where
|
||||
K: ClientKey,
|
||||
{
|
||||
let fullscreen_size = self.screen_size;
|
||||
|
||||
self.get_mut(key).into_option().map(|client| {
|
||||
if client.toggle_fullscreen() {
|
||||
client.size = fullscreen_size;
|
||||
client.position = Point::zero();
|
||||
|
||||
true
|
||||
} else {
|
||||
false
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
Sets a tiled client to floating and returns true, does nothing for a floating client and
|
||||
returns false. If this function returns `true` you have to call `arrange_clients` after.
|
||||
|
@ -420,6 +537,23 @@ impl ClientState {
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
Sets a floating client to tiled and returns true, does nothing for a tiled client and
|
||||
returns false. If this function returns `true` you have to call `arrange_clients` after.
|
||||
*/
|
||||
pub fn set_tiled<K>(&mut self, key: &K) -> bool
|
||||
where
|
||||
K: ClientKey,
|
||||
{
|
||||
if self.get(key).is_floating() {
|
||||
self.toggle_floating(key);
|
||||
|
||||
true
|
||||
} else {
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
This function invalidates the tiling, call `arrange_clients` to fix it again (it doesn't do it
|
||||
automatically since xlib has to move and resize all windows anyways).
|
||||
|
@ -428,35 +562,62 @@ impl ClientState {
|
|||
where
|
||||
K: ClientKey,
|
||||
{
|
||||
let key = key.key();
|
||||
let client = self.clients.remove(&key);
|
||||
let floating_client = self.floating_clients.remove(&key);
|
||||
// do nothing if either no client matches the key or the client is fullscreen.
|
||||
// FIXME: this should probably disable fullscreen mode (but that has to
|
||||
// be handled in the wm state so that the backend can notify the client
|
||||
// that it is no longer fullscreen)
|
||||
if !self
|
||||
.get(key)
|
||||
.into_option()
|
||||
.map(|c| c.is_fullscreen())
|
||||
.unwrap_or(true)
|
||||
{
|
||||
let key = key.key();
|
||||
let client = self.clients.remove(&key);
|
||||
let floating_client = self.floating_clients.remove(&key);
|
||||
|
||||
match (client, floating_client) {
|
||||
(Some(client), None) => {
|
||||
self.floating_clients.insert(key, client);
|
||||
self.remove_from_virtual_screens(&key);
|
||||
}
|
||||
(None, Some(floating_client)) => {
|
||||
// transient clients cannot be tiled
|
||||
match floating_client.is_transient() {
|
||||
true => {
|
||||
self.floating_clients.insert(key, floating_client);
|
||||
}
|
||||
|
||||
false => {
|
||||
self.clients.insert(key, floating_client);
|
||||
self.virtual_screens.get_mut_current().insert(&key);
|
||||
match (client, floating_client) {
|
||||
(Some(client), None) => {
|
||||
self.floating_clients.insert(key, client);
|
||||
self.remove_from_virtual_screens(&key);
|
||||
}
|
||||
(None, Some(floating_client)) => {
|
||||
// transient clients cannot be tiled
|
||||
// only normal windows can be tiled
|
||||
match floating_client.window_type {
|
||||
WindowType::Normal => {
|
||||
self.clients.insert(key, floating_client);
|
||||
self.virtual_screens.get_mut_current().insert(&key);
|
||||
}
|
||||
_ => {
|
||||
self.floating_clients.insert(key, floating_client);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
_ => {
|
||||
error!("wtf? Client was present in tiled and floating list.")
|
||||
}
|
||||
};
|
||||
_ => {
|
||||
error!(
|
||||
"wtf? Client was present in tiled and floating list."
|
||||
)
|
||||
}
|
||||
};
|
||||
|
||||
// we added or removed a client from the tiling so the layout changed, rearrange
|
||||
self.arrange_virtual_screen();
|
||||
// we added or removed a client from the tiling so the layout changed, rearrange
|
||||
self.arrange_virtual_screen();
|
||||
}
|
||||
}
|
||||
|
||||
pub fn update_window_type<K>(&mut self, key: &K, window_type: WindowType)
|
||||
where
|
||||
K: ClientKey,
|
||||
{
|
||||
if let Some(client) = self.get_mut(key).into_option() {
|
||||
client.window_type = window_type;
|
||||
|
||||
match window_type {
|
||||
WindowType::Normal => self.set_floating(key),
|
||||
_ => self.set_tiled(key),
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
fn remove_from_virtual_screens<K>(&mut self, key: &K)
|
||||
|
@ -532,7 +693,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,54 +785,89 @@ 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();
|
||||
// if aux is empty -> width : width / 2
|
||||
|
||||
let (master_width, aux_width) = {
|
||||
let effective_width = width - gap * 2;
|
||||
let vs_width = width - gap * 2;
|
||||
|
||||
let master_size = if vs.aux.is_empty() {
|
||||
let master_position = Point::new(0, 0);
|
||||
let master_window_size = {
|
||||
let factor = if vs.aux.is_empty() {
|
||||
1.0
|
||||
} else {
|
||||
self.master_size / 2.0
|
||||
};
|
||||
|
||||
let master_width = (effective_width as f32 * master_size) as i32;
|
||||
let aux_width = effective_width - master_width;
|
||||
let width = (vs_width as f32 * factor) as i32;
|
||||
|
||||
(master_width, aux_width)
|
||||
// make sure we dont devide by 0
|
||||
// height is max height / number of clients in the stack
|
||||
let height = match vs.master.len() as i32 {
|
||||
0 => 1,
|
||||
n => (height - gap * 2) / n,
|
||||
};
|
||||
|
||||
Size::new(width, height)
|
||||
};
|
||||
|
||||
// make sure we dont devide by 0
|
||||
// height is max height / number of clients in the stack
|
||||
let master_height = (height - gap * 2)
|
||||
/ match NonZeroI32::new(vs.master.len() as i32) {
|
||||
Some(i) => i.get(),
|
||||
None => 1,
|
||||
let aux_position = Point::new(master_window_size.width, 0);
|
||||
let aux_window_size = {
|
||||
let width = vs_width - master_window_size.width;
|
||||
|
||||
// make sure we dont devide by 0
|
||||
// height is max height / number of clients in the stack
|
||||
let height = match vs.aux.len() as i32 {
|
||||
0 => 1,
|
||||
n => (height - gap * 2) / n,
|
||||
};
|
||||
|
||||
// height is max height / number of clients in the stack
|
||||
let aux_height = (height - gap * 2)
|
||||
/ match NonZeroI32::new(vs.aux.len() as i32) {
|
||||
Some(i) => i.get(),
|
||||
None => 1,
|
||||
};
|
||||
Size::new(width, height)
|
||||
};
|
||||
|
||||
fn calculate_window_dimensions(
|
||||
screen_size: Size<i32>,
|
||||
stack_size: Size<i32>,
|
||||
stack_position: Point<i32>,
|
||||
fullscreen: bool,
|
||||
nth: i32,
|
||||
gap: i32,
|
||||
border: i32,
|
||||
) -> (Size<i32>, Point<i32>) {
|
||||
if fullscreen {
|
||||
let size = Size::new(screen_size.width, screen_size.height);
|
||||
let pos = Point::new(0, 0);
|
||||
(size, pos)
|
||||
} else {
|
||||
let size = Size::new(
|
||||
stack_size.width - gap * 2 - border * 2,
|
||||
stack_size.height - gap * 2 - border * 2,
|
||||
);
|
||||
let pos = Point::new(
|
||||
stack_position.x + gap * 2,
|
||||
stack_position.y + stack_size.height * nth + gap * 2,
|
||||
);
|
||||
(size, pos)
|
||||
}
|
||||
}
|
||||
|
||||
// Master
|
||||
for (i, key) in vs.master.iter().enumerate() {
|
||||
let size = (
|
||||
master_width - gap * 2 - self.border_size * 2,
|
||||
master_height - gap * 2 - self.border_size * 2,
|
||||
);
|
||||
|
||||
let position = (gap * 2, master_height * i as i32 + gap * 2);
|
||||
|
||||
if let Some(client) = self.clients.get_mut(key) {
|
||||
let (size, position) = calculate_window_dimensions(
|
||||
self.screen_size.into(),
|
||||
master_window_size,
|
||||
master_position,
|
||||
client.is_fullscreen(),
|
||||
i as i32,
|
||||
gap,
|
||||
self.border_size,
|
||||
);
|
||||
|
||||
*client = Client {
|
||||
size,
|
||||
size: size.into(),
|
||||
position,
|
||||
..*client
|
||||
};
|
||||
|
@ -678,17 +876,19 @@ impl ClientState {
|
|||
|
||||
// Aux
|
||||
for (i, key) in vs.aux.iter().enumerate() {
|
||||
let size = (
|
||||
aux_width - gap * 2 - self.border_size * 2,
|
||||
aux_height - gap * 2 - self.border_size * 2,
|
||||
);
|
||||
|
||||
let position =
|
||||
(master_width + gap * 2, aux_height * i as i32 + gap * 2);
|
||||
|
||||
if let Some(client) = self.clients.get_mut(key) {
|
||||
let (size, position) = calculate_window_dimensions(
|
||||
self.screen_size.into(),
|
||||
aux_window_size,
|
||||
aux_position,
|
||||
client.is_fullscreen(),
|
||||
i as i32,
|
||||
gap,
|
||||
self.border_size,
|
||||
);
|
||||
|
||||
*client = Client {
|
||||
size,
|
||||
size: size.into(),
|
||||
position,
|
||||
..*client
|
||||
};
|
||||
|
@ -851,7 +1051,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
|
||||
}
|
||||
|
@ -902,3 +1102,25 @@ impl<T> ClientEntry<T> {
|
|||
!self.is_vacant()
|
||||
}
|
||||
}
|
||||
|
||||
impl ClientEntry<&client::Client> {
|
||||
pub fn is_fullscreen(&self) -> bool {
|
||||
match self {
|
||||
ClientEntry::Tiled(c) | ClientEntry::Floating(c) => {
|
||||
c.is_fullscreen()
|
||||
}
|
||||
ClientEntry::Vacant => false,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl ClientEntry<&mut client::Client> {
|
||||
pub fn is_fullscreen(&self) -> bool {
|
||||
match self {
|
||||
ClientEntry::Tiled(c) | ClientEntry::Floating(c) => {
|
||||
c.is_fullscreen()
|
||||
}
|
||||
ClientEntry::Vacant => false,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
26
src/lib.rs
Normal file
26
src/lib.rs
Normal file
|
@ -0,0 +1,26 @@
|
|||
#![deny(unsafe_op_in_unsafe_fn)]
|
||||
|
||||
pub mod backends;
|
||||
pub mod clients;
|
||||
pub mod state;
|
||||
pub mod util;
|
||||
|
||||
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),
|
||||
}
|
||||
}
|
32
src/main.rs
32
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},
|
||||
|
@ -5,13 +7,8 @@ use log4rs::{
|
|||
encode::pattern::PatternEncoder,
|
||||
Config,
|
||||
};
|
||||
use state::WMConfig;
|
||||
|
||||
mod clients;
|
||||
mod clients2;
|
||||
mod state;
|
||||
mod util;
|
||||
mod xlib;
|
||||
use wm::state::WMConfig;
|
||||
|
||||
fn init_logger() {
|
||||
let encoder = Box::new(PatternEncoder::new(
|
||||
|
@ -24,7 +21,7 @@ fn init_logger() {
|
|||
|
||||
let _logfile = FileAppender::builder()
|
||||
.encoder(encoder)
|
||||
.build(home.join(".local/portlights.log"))
|
||||
.build(home.join(".local/nirgendwm.log"))
|
||||
.unwrap();
|
||||
|
||||
let config = Config::builder()
|
||||
|
@ -34,7 +31,7 @@ fn init_logger() {
|
|||
Root::builder()
|
||||
.appender("stdout")
|
||||
//.appender("logfile")
|
||||
.build(log::LevelFilter::Info),
|
||||
.build(log::LevelFilter::Debug),
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
|
@ -46,7 +43,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/nirgendwm.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()
|
||||
});
|
||||
|
||||
wm::state::WindowManager::<wm::backends::xlib::XLib>::new(config).run();
|
||||
}
|
||||
|
||||
fn log_prologue() {
|
||||
|
@ -65,7 +78,6 @@ fn log_prologue() {
|
|||
#[test]
|
||||
fn test_logger() {
|
||||
init_logger();
|
||||
// asdf
|
||||
|
||||
log_prologue();
|
||||
}
|
||||
|
|
775
src/state.rs
775
src/state.rs
File diff suppressed because it is too large
Load diff
222
src/util.rs
222
src/util.rs
|
@ -22,3 +22,225 @@ impl Hasher for IdentityHasher {
|
|||
}
|
||||
|
||||
pub type BuildIdentityHasher = BuildHasherDefault<IdentityHasher>;
|
||||
|
||||
pub use point::Point;
|
||||
pub use size::Size;
|
||||
|
||||
mod size {
|
||||
#[derive(Debug, Copy, Clone, PartialEq, PartialOrd)]
|
||||
pub struct Size<I>
|
||||
where
|
||||
I: num_traits::PrimInt + num_traits::Zero,
|
||||
{
|
||||
pub width: I,
|
||||
pub height: I,
|
||||
}
|
||||
|
||||
impl<I> std::ops::Add for Size<I>
|
||||
where
|
||||
I: num_traits::PrimInt + num_traits::Zero,
|
||||
{
|
||||
type Output = Self;
|
||||
|
||||
fn add(self, rhs: Self) -> Self::Output {
|
||||
Self {
|
||||
width: self.width + rhs.width,
|
||||
height: self.height + rhs.height,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<I> std::ops::Sub for Size<I>
|
||||
where
|
||||
I: num_traits::PrimInt + num_traits::Zero,
|
||||
{
|
||||
type Output = Self;
|
||||
|
||||
fn sub(self, rhs: Self) -> Self::Output {
|
||||
Self {
|
||||
width: self.width - rhs.width,
|
||||
height: self.height - rhs.height,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<I> num_traits::Zero for Size<I>
|
||||
where
|
||||
I: num_traits::PrimInt + num_traits::Zero,
|
||||
{
|
||||
fn zero() -> Self {
|
||||
Self::default()
|
||||
}
|
||||
|
||||
fn is_zero(&self) -> bool {
|
||||
self.width == I::zero() && self.height == I::zero()
|
||||
}
|
||||
}
|
||||
|
||||
impl<I> Default for Size<I>
|
||||
where
|
||||
I: num_traits::PrimInt + num_traits::Zero,
|
||||
{
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
width: I::zero(),
|
||||
height: I::zero(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<I> From<(I, I)> for Size<I>
|
||||
where
|
||||
I: num_traits::PrimInt + num_traits::Zero,
|
||||
{
|
||||
fn from(value: (I, I)) -> Self {
|
||||
Self::from_tuple(value)
|
||||
}
|
||||
}
|
||||
|
||||
impl<I> From<super::point::Point<I>> for Size<I>
|
||||
where
|
||||
I: num_traits::PrimInt + num_traits::Zero,
|
||||
{
|
||||
fn from(value: super::point::Point<I>) -> Self {
|
||||
Self::new(value.x, value.y)
|
||||
}
|
||||
}
|
||||
|
||||
impl<I> Size<I>
|
||||
where
|
||||
I: num_traits::PrimInt + num_traits::Zero,
|
||||
{
|
||||
pub fn new(width: I, height: I) -> Self {
|
||||
Self { width, height }
|
||||
}
|
||||
|
||||
pub fn from_tuple(tuple: (I, I)) -> Self {
|
||||
Self::new(tuple.0, tuple.1)
|
||||
}
|
||||
|
||||
pub fn as_tuple(&self) -> (I, I) {
|
||||
(self.width, self.height)
|
||||
}
|
||||
|
||||
pub fn clamp(self, other: Self) -> Self {
|
||||
Self::new(
|
||||
self.width.min(other.width),
|
||||
self.height.min(other.height),
|
||||
)
|
||||
}
|
||||
|
||||
pub fn map<F>(self, f: F) -> Self
|
||||
where
|
||||
F: FnOnce(I, I) -> Self,
|
||||
{
|
||||
f(self.width, self.height)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
mod point {
|
||||
#[derive(Debug, Copy, Clone, PartialEq, PartialOrd)]
|
||||
pub struct Point<I>
|
||||
where
|
||||
I: num_traits::PrimInt + num_traits::Zero,
|
||||
{
|
||||
pub x: I,
|
||||
pub y: I,
|
||||
}
|
||||
|
||||
impl<I> std::ops::Add for Point<I>
|
||||
where
|
||||
I: num_traits::PrimInt + num_traits::Zero,
|
||||
{
|
||||
type Output = Self;
|
||||
|
||||
fn add(self, rhs: Self) -> Self::Output {
|
||||
Self {
|
||||
x: self.x + rhs.x,
|
||||
y: self.y + rhs.y,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<I> std::ops::Sub for Point<I>
|
||||
where
|
||||
I: num_traits::PrimInt + num_traits::Zero,
|
||||
{
|
||||
type Output = Self;
|
||||
|
||||
fn sub(self, rhs: Self) -> Self::Output {
|
||||
Self {
|
||||
x: self.x - rhs.x,
|
||||
y: self.y - rhs.y,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<I> num_traits::Zero for Point<I>
|
||||
where
|
||||
I: num_traits::PrimInt + num_traits::Zero,
|
||||
{
|
||||
fn zero() -> Self {
|
||||
Self::default()
|
||||
}
|
||||
|
||||
fn is_zero(&self) -> bool {
|
||||
self.x == I::zero() && self.y == I::zero()
|
||||
}
|
||||
}
|
||||
|
||||
impl<I> Default for Point<I>
|
||||
where
|
||||
I: num_traits::PrimInt + num_traits::Zero,
|
||||
{
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
x: I::zero(),
|
||||
y: I::zero(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<I> From<(I, I)> for Point<I>
|
||||
where
|
||||
I: num_traits::PrimInt + num_traits::Zero,
|
||||
{
|
||||
fn from(value: (I, I)) -> Self {
|
||||
Self::from_tuple(value)
|
||||
}
|
||||
}
|
||||
|
||||
impl<I> From<super::size::Size<I>> for Point<I>
|
||||
where
|
||||
I: num_traits::PrimInt + num_traits::Zero,
|
||||
{
|
||||
fn from(value: super::size::Size<I>) -> Self {
|
||||
Self::new(value.width, value.height)
|
||||
}
|
||||
}
|
||||
|
||||
impl<I> Point<I>
|
||||
where
|
||||
I: num_traits::PrimInt + num_traits::Zero,
|
||||
{
|
||||
pub fn new(x: I, y: I) -> Self {
|
||||
Self { x, y }
|
||||
}
|
||||
|
||||
pub fn from_tuple(tuple: (I, I)) -> Self {
|
||||
Self::new(tuple.0, tuple.1)
|
||||
}
|
||||
|
||||
pub fn as_tuple(&self) -> (I, I) {
|
||||
(self.x, self.y)
|
||||
}
|
||||
|
||||
pub fn map<F, T>(self, f: F) -> T
|
||||
where
|
||||
F: FnOnce(I, I) -> T,
|
||||
{
|
||||
f(self.x, self.y)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
765
src/xlib.rs
765
src/xlib.rs
|
@ -1,765 +0,0 @@
|
|||
use log::info;
|
||||
use std::ptr::{null, null_mut};
|
||||
use std::{ffi::CString, rc::Rc};
|
||||
|
||||
use x11::xlib::{
|
||||
self, AnyButton, AnyKey, AnyModifier, Atom, ButtonPressMask,
|
||||
ButtonReleaseMask, CWEventMask, ControlMask, CurrentTime, EnterWindowMask,
|
||||
FocusChangeMask, LockMask, Mod1Mask, Mod2Mask, Mod3Mask, Mod4Mask,
|
||||
Mod5Mask, PointerMotionMask, PropertyChangeMask, ShiftMask,
|
||||
StructureNotifyMask, SubstructureNotifyMask, SubstructureRedirectMask,
|
||||
Window, XCloseDisplay, XConfigureRequestEvent, XDefaultScreen, XEvent,
|
||||
XGetTransientForHint, XGrabPointer, XInternAtom, XKillClient, XMapWindow,
|
||||
XOpenDisplay, XRaiseWindow, XRootWindow, XSetErrorHandler, XSync,
|
||||
XUngrabButton, XUngrabKey, XUngrabPointer, XWarpPointer, XWindowAttributes,
|
||||
};
|
||||
use xlib::GrabModeAsync;
|
||||
|
||||
use log::error;
|
||||
|
||||
use crate::clients::Client;
|
||||
|
||||
pub struct XLib {
|
||||
display: Display,
|
||||
root: Window,
|
||||
_screen: i32,
|
||||
atoms: Atoms,
|
||||
global_keybinds: Vec<KeyOrButton>,
|
||||
}
|
||||
|
||||
struct Atoms {
|
||||
protocols: Atom,
|
||||
delete_window: Atom,
|
||||
active_window: Atom,
|
||||
take_focus: Atom,
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
|
||||
pub enum KeyOrButton {
|
||||
Key(i32, u32),
|
||||
Button(u32, u32, u64),
|
||||
}
|
||||
|
||||
impl KeyOrButton {
|
||||
#[allow(dead_code)]
|
||||
pub fn key(keycode: i32, modmask: u32) -> Self {
|
||||
Self::Key(keycode, modmask)
|
||||
}
|
||||
pub fn button(button: u32, modmask: u32, buttonmask: i64) -> Self {
|
||||
Self::Button(button, modmask, buttonmask as u64)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct Display(Rc<*mut xlib::Display>);
|
||||
|
||||
impl XLib {
|
||||
pub fn new() -> Self {
|
||||
let (display, _screen, root) = unsafe {
|
||||
let display = XOpenDisplay(null());
|
||||
|
||||
assert_ne!(display, null_mut());
|
||||
|
||||
let display = Display::new(display);
|
||||
let screen = XDefaultScreen(display.get());
|
||||
let root = XRootWindow(display.get(), screen);
|
||||
|
||||
(display, screen, root)
|
||||
};
|
||||
|
||||
Self {
|
||||
atoms: Atoms::init(display.clone()),
|
||||
global_keybinds: Vec::new(),
|
||||
root,
|
||||
_screen,
|
||||
display,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn init(&mut self) {
|
||||
unsafe {
|
||||
let mut window_attributes =
|
||||
std::mem::MaybeUninit::<xlib::XSetWindowAttributes>::zeroed()
|
||||
.assume_init();
|
||||
|
||||
window_attributes.event_mask = SubstructureRedirectMask
|
||||
| StructureNotifyMask
|
||||
| SubstructureNotifyMask
|
||||
| EnterWindowMask
|
||||
| PointerMotionMask
|
||||
| ButtonPressMask;
|
||||
|
||||
xlib::XChangeWindowAttributes(
|
||||
self.dpy(),
|
||||
self.root,
|
||||
CWEventMask,
|
||||
&mut window_attributes,
|
||||
);
|
||||
|
||||
xlib::XSelectInput(
|
||||
self.dpy(),
|
||||
self.root,
|
||||
window_attributes.event_mask,
|
||||
);
|
||||
|
||||
XSetErrorHandler(Some(xlib_error_handler));
|
||||
|
||||
XSync(self.dpy(), 0);
|
||||
}
|
||||
|
||||
self.grab_global_keybinds(self.root);
|
||||
}
|
||||
|
||||
pub fn add_global_keybind(&mut self, key: KeyOrButton) {
|
||||
self.global_keybinds.push(key);
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
fn ungrab_global_keybings(&self, window: Window) {
|
||||
unsafe {
|
||||
XUngrabButton(self.dpy(), AnyButton as u32, AnyModifier, window);
|
||||
XUngrabKey(self.dpy(), AnyKey, AnyModifier, window);
|
||||
}
|
||||
}
|
||||
|
||||
fn grab_global_keybinds(&self, window: Window) {
|
||||
for kb in self.global_keybinds.iter() {
|
||||
self.grab_key_or_button(window, kb);
|
||||
}
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
pub fn remove_global_keybind(&mut self, key: &KeyOrButton) {
|
||||
if self.global_keybinds.contains(key) {
|
||||
self.global_keybinds.retain(|kb| kb != key);
|
||||
}
|
||||
}
|
||||
|
||||
fn dpy(&self) -> *mut xlib::Display {
|
||||
self.display.get()
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
pub fn squash_event(&self, event_type: i32) -> XEvent {
|
||||
unsafe {
|
||||
let mut event =
|
||||
std::mem::MaybeUninit::<xlib::XEvent>::zeroed().assume_init();
|
||||
|
||||
while xlib::XCheckTypedEvent(self.dpy(), event_type, &mut event)
|
||||
!= 0
|
||||
{}
|
||||
|
||||
event
|
||||
}
|
||||
}
|
||||
|
||||
pub fn next_event(&self) -> XEvent {
|
||||
unsafe {
|
||||
let mut event =
|
||||
std::mem::MaybeUninit::<xlib::XEvent>::zeroed().assume_init();
|
||||
xlib::XNextEvent(self.dpy(), &mut event);
|
||||
|
||||
event
|
||||
}
|
||||
}
|
||||
|
||||
pub fn grab_key_or_button(&self, window: Window, key: &KeyOrButton) {
|
||||
let numlock_mask = self.get_numlock_mask();
|
||||
let modifiers =
|
||||
vec![0, LockMask, numlock_mask, LockMask | numlock_mask];
|
||||
|
||||
for modifier in modifiers.iter() {
|
||||
match key {
|
||||
&KeyOrButton::Key(keycode, modmask) => {
|
||||
unsafe {
|
||||
xlib::XGrabKey(
|
||||
self.dpy(),
|
||||
keycode,
|
||||
modmask | modifier,
|
||||
window,
|
||||
1, /* true */
|
||||
GrabModeAsync,
|
||||
GrabModeAsync,
|
||||
);
|
||||
}
|
||||
}
|
||||
&KeyOrButton::Button(button, modmask, buttonmask) => {
|
||||
unsafe {
|
||||
xlib::XGrabButton(
|
||||
self.dpy(),
|
||||
button,
|
||||
modmask | modifier,
|
||||
window,
|
||||
1, /*true */
|
||||
buttonmask as u32,
|
||||
GrabModeAsync,
|
||||
GrabModeAsync,
|
||||
0,
|
||||
0,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn focus_client(&self, client: &Client) {
|
||||
unsafe {
|
||||
xlib::XSetInputFocus(
|
||||
self.dpy(),
|
||||
client.window,
|
||||
xlib::RevertToPointerRoot,
|
||||
xlib::CurrentTime,
|
||||
);
|
||||
|
||||
let screen = xlib::XDefaultScreenOfDisplay(self.dpy()).as_ref();
|
||||
|
||||
if let Some(screen) = screen {
|
||||
xlib::XSetWindowBorder(
|
||||
self.dpy(),
|
||||
client.window,
|
||||
screen.white_pixel,
|
||||
);
|
||||
}
|
||||
|
||||
xlib::XChangeProperty(
|
||||
self.dpy(),
|
||||
self.root,
|
||||
self.atoms.active_window,
|
||||
xlib::XA_WINDOW,
|
||||
32,
|
||||
xlib::PropModeReplace,
|
||||
&client.window as *const u64 as *const _,
|
||||
1,
|
||||
);
|
||||
}
|
||||
|
||||
self.send_protocol(client, self.atoms.take_focus);
|
||||
}
|
||||
|
||||
pub fn unfocus_client(&self, client: &Client) {
|
||||
//info!("unfocusing client: {:?}", client);
|
||||
|
||||
unsafe {
|
||||
xlib::XSetInputFocus(
|
||||
self.dpy(),
|
||||
self.root,
|
||||
xlib::RevertToPointerRoot,
|
||||
xlib::CurrentTime,
|
||||
);
|
||||
|
||||
let screen = xlib::XDefaultScreenOfDisplay(self.dpy()).as_ref();
|
||||
|
||||
if let Some(screen) = screen {
|
||||
xlib::XSetWindowBorder(
|
||||
self.dpy(),
|
||||
client.window,
|
||||
screen.black_pixel,
|
||||
);
|
||||
}
|
||||
|
||||
// xlib::XDeleteProperty(
|
||||
// self.dpy(),
|
||||
// self.root,
|
||||
// self.atoms.active_window,
|
||||
// );
|
||||
}
|
||||
}
|
||||
|
||||
pub fn move_resize_client(&self, client: &Client) {
|
||||
let mut windowchanges = xlib::XWindowChanges {
|
||||
x: client.position.0,
|
||||
y: client.position.1,
|
||||
width: client.size.0,
|
||||
height: client.size.1,
|
||||
border_width: 0,
|
||||
sibling: 0,
|
||||
stack_mode: 0,
|
||||
};
|
||||
|
||||
if client.size.0 < 1 || client.size.1 < 1 {
|
||||
error!("client {:?} size is less than 1 pixel!", client);
|
||||
} else {
|
||||
unsafe {
|
||||
xlib::XConfigureWindow(
|
||||
self.dpy(),
|
||||
client.window,
|
||||
(xlib::CWY | xlib::CWX | xlib::CWHeight | xlib::CWWidth)
|
||||
as u32,
|
||||
&mut windowchanges,
|
||||
);
|
||||
|
||||
// I don't think I have to call this ~
|
||||
//self.configure_client(client);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn move_client(&self, client: &Client) {
|
||||
let mut wc = xlib::XWindowChanges {
|
||||
x: client.position.0,
|
||||
y: client.position.1,
|
||||
width: client.size.0,
|
||||
height: client.size.1,
|
||||
border_width: 0,
|
||||
sibling: 0,
|
||||
stack_mode: 0,
|
||||
};
|
||||
|
||||
if client.size.0 < 1 || client.size.1 < 1 {
|
||||
error!("client {:?} size is less than 1 pixel!", client);
|
||||
} else {
|
||||
unsafe {
|
||||
xlib::XConfigureWindow(
|
||||
self.dpy(),
|
||||
client.window,
|
||||
(xlib::CWX | xlib::CWY) as u32,
|
||||
&mut wc,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn resize_client(&self, client: &Client) {
|
||||
let mut wc = xlib::XWindowChanges {
|
||||
x: client.position.0,
|
||||
y: client.position.1,
|
||||
width: client.size.0,
|
||||
height: client.size.1,
|
||||
border_width: 0,
|
||||
sibling: 0,
|
||||
stack_mode: 0,
|
||||
};
|
||||
|
||||
if client.size.0 < 1 || client.size.1 < 1 {
|
||||
error!("client {:?} size is less than 1 pixel!", client);
|
||||
} else {
|
||||
unsafe {
|
||||
xlib::XConfigureWindow(
|
||||
self.dpy(),
|
||||
client.window,
|
||||
(xlib::CWWidth | xlib::CWHeight) as u32,
|
||||
&mut wc,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn hide_client(&self, client: &Client) {
|
||||
let mut wc = xlib::XWindowChanges {
|
||||
x: client.size.0 * -2,
|
||||
y: client.position.1,
|
||||
width: client.size.0,
|
||||
height: client.size.1,
|
||||
border_width: 0,
|
||||
sibling: 0,
|
||||
stack_mode: 0,
|
||||
};
|
||||
|
||||
if client.size.0 < 1 || client.size.1 < 1 {
|
||||
error!("client {:?} size is less than 1 pixel!", client);
|
||||
} else {
|
||||
unsafe {
|
||||
xlib::XConfigureWindow(
|
||||
self.dpy(),
|
||||
client.window,
|
||||
(xlib::CWX | xlib::CWY) as u32,
|
||||
&mut wc,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn raise_client(&self, client: &Client) {
|
||||
unsafe {
|
||||
XRaiseWindow(self.dpy(), client.window);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get_window_size(&self, window: Window) -> Option<(i32, i32)> {
|
||||
let mut wa = unsafe {
|
||||
std::mem::MaybeUninit::<xlib::XWindowAttributes>::zeroed()
|
||||
.assume_init()
|
||||
};
|
||||
|
||||
if unsafe {
|
||||
xlib::XGetWindowAttributes(self.dpy(), window, &mut wa) != 0
|
||||
} {
|
||||
Some((wa.width, wa.height))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
fn get_window_attributes(
|
||||
&self,
|
||||
window: Window,
|
||||
) -> Option<XWindowAttributes> {
|
||||
let mut wa = unsafe {
|
||||
std::mem::MaybeUninit::<xlib::XWindowAttributes>::zeroed()
|
||||
.assume_init()
|
||||
};
|
||||
|
||||
if unsafe {
|
||||
xlib::XGetWindowAttributes(self.dpy(), window, &mut wa) != 0
|
||||
} {
|
||||
Some(wa)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get_transient_for_window(&self, window: Window) -> Option<Window> {
|
||||
let mut transient_for: Window = 0;
|
||||
|
||||
if unsafe {
|
||||
XGetTransientForHint(self.dpy(), window, &mut transient_for) != 0
|
||||
} {
|
||||
Some(transient_for)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
pub fn expose_client(&self, client: &Client) {
|
||||
self.expose_window(client.window);
|
||||
}
|
||||
|
||||
fn expose_window(&self, window: Window) {
|
||||
if let Some(wa) = self.get_window_attributes(window) {
|
||||
unsafe {
|
||||
xlib::XClearArea(
|
||||
self.dpy(),
|
||||
window,
|
||||
0,
|
||||
0,
|
||||
wa.width as u32,
|
||||
wa.height as u32,
|
||||
1,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn configure_window(&self, event: &XConfigureRequestEvent) {
|
||||
let mut wc = xlib::XWindowChanges {
|
||||
x: event.x,
|
||||
y: event.y,
|
||||
width: event.width,
|
||||
height: event.height,
|
||||
border_width: event.border_width,
|
||||
sibling: event.above,
|
||||
stack_mode: event.detail,
|
||||
};
|
||||
|
||||
unsafe {
|
||||
xlib::XConfigureWindow(
|
||||
self.dpy(),
|
||||
event.window,
|
||||
event.value_mask as u32,
|
||||
&mut wc,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn configure_client(&self, client: &Client, border: i32) {
|
||||
let mut event = xlib::XConfigureEvent {
|
||||
type_: xlib::ConfigureNotify,
|
||||
display: self.dpy(),
|
||||
event: client.window,
|
||||
window: client.window,
|
||||
x: client.position.0,
|
||||
y: client.position.1,
|
||||
width: client.size.0,
|
||||
height: client.size.1,
|
||||
border_width: border,
|
||||
override_redirect: 0,
|
||||
send_event: 0,
|
||||
serial: 0,
|
||||
above: 0,
|
||||
};
|
||||
|
||||
unsafe {
|
||||
xlib::XSetWindowBorderWidth(
|
||||
self.dpy(),
|
||||
event.window,
|
||||
event.border_width as u32,
|
||||
);
|
||||
|
||||
xlib::XSendEvent(
|
||||
self.dpy(),
|
||||
event.window,
|
||||
0,
|
||||
StructureNotifyMask,
|
||||
&mut event as *mut _ as *mut XEvent,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn map_window(&self, window: Window) {
|
||||
unsafe {
|
||||
XMapWindow(self.dpy(), window);
|
||||
|
||||
xlib::XSelectInput(
|
||||
self.dpy(),
|
||||
window,
|
||||
EnterWindowMask
|
||||
| FocusChangeMask
|
||||
| PropertyChangeMask
|
||||
| StructureNotifyMask,
|
||||
);
|
||||
}
|
||||
|
||||
self.grab_global_keybinds(window);
|
||||
}
|
||||
|
||||
pub fn dimensions(&self) -> (i32, i32) {
|
||||
unsafe {
|
||||
let mut wa =
|
||||
std::mem::MaybeUninit::<xlib::XWindowAttributes>::zeroed()
|
||||
.assume_init();
|
||||
|
||||
xlib::XGetWindowAttributes(self.dpy(), self.root, &mut wa);
|
||||
|
||||
info!("Root window dimensions: {}, {}", wa.width, wa.height);
|
||||
|
||||
(wa.width, wa.height)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn close_dpy(&self) {
|
||||
unsafe {
|
||||
XCloseDisplay(self.dpy());
|
||||
}
|
||||
}
|
||||
|
||||
pub fn kill_client(&self, client: &Client) {
|
||||
if !self.send_protocol(client, self.atoms.delete_window) {
|
||||
unsafe {
|
||||
XKillClient(self.dpy(), client.window);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn grab_cursor(&self) {
|
||||
unsafe {
|
||||
XGrabPointer(
|
||||
self.dpy(),
|
||||
self.root,
|
||||
0,
|
||||
(ButtonPressMask | ButtonReleaseMask | PointerMotionMask)
|
||||
as u32,
|
||||
GrabModeAsync,
|
||||
GrabModeAsync,
|
||||
0,
|
||||
0,
|
||||
CurrentTime,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn release_cursor(&self) {
|
||||
unsafe {
|
||||
XUngrabPointer(self.dpy(), CurrentTime);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn move_cursor(&self, window: Option<Window>, position: (i32, i32)) {
|
||||
unsafe {
|
||||
XWarpPointer(
|
||||
self.dpy(),
|
||||
0,
|
||||
window.unwrap_or(self.root),
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
position.0,
|
||||
position.1,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
fn check_for_protocol(&self, client: &Client, proto: xlib::Atom) -> bool {
|
||||
let mut protos: *mut xlib::Atom = null_mut();
|
||||
let mut num_protos: i32 = 0;
|
||||
|
||||
unsafe {
|
||||
if xlib::XGetWMProtocols(
|
||||
self.dpy(),
|
||||
client.window,
|
||||
&mut protos,
|
||||
&mut num_protos,
|
||||
) != 0
|
||||
{
|
||||
for i in 0..num_protos {
|
||||
if *protos.offset(i as isize) == proto {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
fn send_protocol(&self, client: &Client, proto: xlib::Atom) -> bool {
|
||||
if self.check_for_protocol(client, proto) {
|
||||
let mut data = xlib::ClientMessageData::default();
|
||||
data.set_long(0, proto as i64);
|
||||
|
||||
let mut event = XEvent {
|
||||
client_message: xlib::XClientMessageEvent {
|
||||
type_: xlib::ClientMessage,
|
||||
serial: 0,
|
||||
display: self.dpy(),
|
||||
send_event: 0,
|
||||
window: client.window,
|
||||
format: 32,
|
||||
message_type: self.atoms.protocols,
|
||||
data,
|
||||
},
|
||||
};
|
||||
|
||||
unsafe {
|
||||
xlib::XSendEvent(
|
||||
self.dpy(),
|
||||
client.window,
|
||||
0,
|
||||
xlib::NoEventMask,
|
||||
&mut event,
|
||||
);
|
||||
}
|
||||
|
||||
true
|
||||
} else {
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
pub fn make_key<S>(&self, key: S, modmask: u32) -> KeyOrButton
|
||||
where
|
||||
S: AsRef<str>,
|
||||
{
|
||||
let key = self.keycode(key);
|
||||
|
||||
KeyOrButton::Key(key, modmask)
|
||||
}
|
||||
|
||||
fn keycode<S>(&self, string: S) -> i32
|
||||
where
|
||||
S: AsRef<str>,
|
||||
{
|
||||
let c_string = CString::new(string.as_ref()).unwrap();
|
||||
|
||||
unsafe {
|
||||
let keysym = xlib::XStringToKeysym(c_string.as_ptr());
|
||||
xlib::XKeysymToKeycode(self.dpy(), keysym) as i32
|
||||
}
|
||||
}
|
||||
|
||||
fn get_numlock_mask(&self) -> u32 {
|
||||
unsafe {
|
||||
let modmap = xlib::XGetModifierMapping(self.dpy());
|
||||
let max_keypermod = (*modmap).max_keypermod;
|
||||
|
||||
for i in 0..8 {
|
||||
for j in 0..max_keypermod {
|
||||
if *(*modmap)
|
||||
.modifiermap
|
||||
.offset((i * max_keypermod + j) as isize)
|
||||
== xlib::XKeysymToKeycode(
|
||||
self.dpy(),
|
||||
x11::keysym::XK_Num_Lock as u64,
|
||||
)
|
||||
{
|
||||
return 1 << i;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
0
|
||||
}
|
||||
|
||||
pub fn get_clean_mask(&self) -> u32 {
|
||||
!(self.get_numlock_mask() | LockMask)
|
||||
& (ShiftMask
|
||||
| ControlMask
|
||||
| Mod1Mask
|
||||
| Mod2Mask
|
||||
| Mod3Mask
|
||||
| Mod4Mask
|
||||
| Mod5Mask)
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
pub fn clean_mask(&self, mask: u32) -> u32 {
|
||||
mask & self.get_clean_mask()
|
||||
}
|
||||
|
||||
pub fn are_masks_equal(&self, rhs: u32, lhs: u32) -> bool {
|
||||
let clean = self.get_clean_mask();
|
||||
|
||||
rhs & clean == lhs & clean
|
||||
}
|
||||
}
|
||||
|
||||
impl Display {
|
||||
pub fn new(display: *mut x11::xlib::Display) -> Self {
|
||||
Self {
|
||||
0: Rc::new(display),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get(&self) -> *mut x11::xlib::Display {
|
||||
*self.0
|
||||
}
|
||||
}
|
||||
|
||||
impl Atoms {
|
||||
fn init(display: Display) -> Self {
|
||||
unsafe {
|
||||
Self {
|
||||
protocols: {
|
||||
let name = CString::new("WM_PROTOCOLS").unwrap();
|
||||
XInternAtom(display.get(), name.as_c_str().as_ptr(), 0)
|
||||
},
|
||||
delete_window: {
|
||||
let name = CString::new("WM_DELETE_WINDOW").unwrap();
|
||||
XInternAtom(display.get(), name.as_c_str().as_ptr(), 0)
|
||||
},
|
||||
active_window: {
|
||||
let name = CString::new("WM_ACTIVE_WINDOW").unwrap();
|
||||
XInternAtom(display.get(), name.as_c_str().as_ptr(), 0)
|
||||
},
|
||||
take_focus: {
|
||||
let name = CString::new("WM_TAKE_FOCUS").unwrap();
|
||||
XInternAtom(display.get(), name.as_c_str().as_ptr(), 0)
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
unsafe extern "C" fn xlib_error_handler(
|
||||
_dpy: *mut x11::xlib::Display,
|
||||
ee: *mut x11::xlib::XErrorEvent,
|
||||
) -> std::os::raw::c_int {
|
||||
let err = ee.as_ref().unwrap();
|
||||
|
||||
if err.error_code == x11::xlib::BadWindow
|
||||
|| err.error_code == x11::xlib::BadDrawable
|
||||
|| err.error_code == x11::xlib::BadAccess
|
||||
|| err.error_code == x11::xlib::BadMatch
|
||||
{
|
||||
0
|
||||
} else {
|
||||
error!(
|
||||
"wm: fatal error:\nrequest_code: {}\nerror_code: {}",
|
||||
err.request_code, err.error_code
|
||||
);
|
||||
std::process::exit(1);
|
||||
}
|
||||
}
|
BIN
starship.jpg
BIN
starship.jpg
Binary file not shown.
Before Width: | Height: | Size: 210 KiB |
Loading…
Reference in a new issue