Compare commits

..

73 commits

Author SHA1 Message Date
Janis 1d15ef6336 fullscreen fix? 2022-05-17 16:46:40 +02:00
Janis ae89404de3 added cursor types 2022-05-08 18:59:42 +02:00
Janis fdf81d8d6a update dependencies, bump version to 0.3 2022-05-08 13:33:58 +02:00
janis 1aab741b49 Merge pull request 'feature_ewmh' (#4) from feature_ewmh into main
Reviewed-on: https://desktop-host/git/janis/wm/pulls/4
2022-05-08 13:27:50 +02:00
Janis c826556e83 partial EWMH support now, window type, wmname, clientlist,wmcheck 2022-05-08 13:27:50 +02:00
Janis 30867df46c added update_window_type function to clients which updates floating for dialog
style windows
2022-05-08 13:27:50 +02:00
Janis 364d621b72 added set_tiled function to change window from floating to tiled 2022-05-08 13:27:50 +02:00
Janis bae880c5e1 refactor Client to better reflect different window types 2022-05-08 13:27:50 +02:00
Janis 449b4cccd8 added clamp function to Size<T> 2022-05-08 13:27:50 +02:00
Janis 6c4f0d54bd added wm_transient_for atom 2022-05-08 13:27:50 +02:00
Janis 4eb1cb4555 added WindowType enum 2022-05-08 13:27:50 +02:00
Janis f9afdc990d setting supported ewmh atoms fixes fullscreen 2022-05-08 13:27:50 +02:00
Janis ac433847c5 added window name event 2022-05-08 13:27:50 +02:00
Janis 0dd42a7039 refactored atoms 2022-05-08 13:27:50 +02:00
Janis 85d3c3ce79 about to breka everything 2022-05-08 13:27:50 +02:00
Janis fb011ea23f added XLibConnection, EWMHAtoms type that stores atoms 2022-05-08 13:27:50 +02:00
Janis 2f805dab21 added test to EWMH atoms 2022-05-08 13:27:50 +02:00
Janis 56fff2698b added all EWMH atoms as enum 2022-05-08 13:27:50 +02:00
Janis db6ffb9416 added nirgendwm.toml config file example 2022-05-08 13:25:14 +02:00
Janis ba047217a6 changed name to nirgendwm, bumped version 2022-05-08 13:16:05 +02:00
Janis bc13bf43d6 changed default terminal to xterm 2022-05-08 13:15:52 +02:00
janis 590af3a06c Merge pull request 'feature_window-border' (#3) from feature_window-border into main
Reviewed-on: https://desktop-host/git/janis/wm/pulls/3
2022-05-08 13:12:56 +02:00
Janis 702004d2d2 remove border for fullscreen clients 2022-05-08 13:06:08 +02:00
Janis daf9f72a89 added border size to config 2022-05-08 12:56:43 +02:00
janis 71ddeb6af1 Merge pull request 'get_atom_property_leak_fix' (#2) from get_atom_property_leak_fix into main
Reviewed-on: https://desktop-host/git/janis/wm/pulls/2
2022-05-07 16:39:24 +02:00
Janis b1895bdd07 fixed get_atom_property() 2022-05-07 13:11:06 +02:00
Janis e49fdfa5be adds new XPointer<T> type 2022-05-07 00:32:37 +02:00
Janis b3f586ea6a removed old xlib.rs file 2022-05-07 00:29:28 +02:00
Janis 2c6d4fd465 fullscreen windows can no longer be resized or moved 2021-12-02 22:16:57 +01:00
Janis 192f865fec Merge branch 'feature-fullscreen' 2021-12-02 21:42:57 +01:00
Janis 25c0d94217 Merge branch 'refactor_point-size' into feature-fullscreen 2021-12-02 21:42:10 +01:00
Janis b49bfed1f0 removed debug logging 2021-12-02 21:36:51 +01:00
Janis c3f3ad7203 rebase/corrected all cases of Point<I> that were sizes 2021-12-02 21:32:56 +01:00
Janis 5dbfa6fbcf added fullscreen mechanics 2021-12-02 20:22:21 +01:00
Janis c9b926f5ba moved Point<I> to module utils
and also added `Size<I>`
both types depend on `num-traits`
2021-12-02 20:21:21 +01:00
Janis f6a871d1e7 changed FullscreenEvent to be On, Off, Toggle instead of a simple bool 2021-12-02 18:15:45 +01:00
Janis 7961c97d2f fix: transient windows appear as 1x1 sized 2021-12-02 18:13:00 +01:00
Janis 81a49e8290 seperated crate into lib and binary 2021-11-30 15:47:42 +01:00
Janis f26ca7948b added enwline to end of Cargo.toml 2021-11-29 20:49:50 +01:00
Janis 4810d88dc1 Merge branch 'refactor-2' 2021-11-29 20:47:57 +01:00
Janis f57a5f8033 updated readme 2021-11-29 20:39:00 +01:00
Janis 4d49ae52fd removed pictures 2021-11-29 01:06:06 +01:00
Janis 8f5f60455c made terminal command configurable 2021-11-29 00:41:15 +01:00
Janis c72356a087 disabled screenshot keybind since it doesnt work and also somehow grabs up 2021-11-29 00:07:56 +01:00
Janis 6404888941 cleanup, warnings, dead code, et cetera 2021-11-28 22:54:09 +01:00
Janis ece0eb7903 made border colors configurable in the config file 2021-11-28 22:45:02 +01:00
Janis 3a56102ec2 added config file and deserialization 2021-11-28 22:00:08 +01:00
Janis d3b4fcbf18 added a way to add already existing windows to wm 2021-11-28 21:15:51 +01:00
Janis df3c2e33ce config value for killing clients on exit 2021-11-28 21:02:38 +01:00
Janis 053afa576e removed Makefile and xinitrc
both files are now no longer needed since i added the runner command
2021-11-28 21:01:50 +01:00
Janis 2e589bf94b custom runner using Xephyr 2021-11-28 20:54:38 +01:00
Janis 91b5c91bd5 fixed window borders 2021-11-28 19:32:05 +01:00
Janis b47f245250 seems to work now :^) 2021-11-28 19:11:16 +01:00
Janis 2b4ddc8b5a fixed keysym_to_virtual_keycode and virtual_keycode_to_keysym 2021-11-28 19:10:36 +01:00
Janis d3f630549e fixed keybinds in backend trait/impl 2021-11-28 16:44:33 +01:00
Janis 964d6fe748 window server backend cursor api 2021-11-28 15:46:54 +01:00
Janis 72129ba61e keybind grabbing in xlib backend 2021-11-27 22:14:09 +01:00
Janis 696559d0af keybinds are not stored in a Rc<RefCell<>> to prevent cloning
this could be blocking if i ever add dynamic keybinds
2021-11-27 22:12:48 +01:00
Janis aafbcf2314 changed multiple (i32, i32) tuples to Point<i32> 2021-11-26 22:52:27 +01:00
Janis af21769d52 implemented most backend trait functions for XLib 2021-11-26 22:51:39 +01:00
Janis d3afc30ceb moved backend trait to own file 2021-11-26 22:51:08 +01:00
Janis a85d8d0df5 changed to using Point<I> instead of tuples or slices 2021-11-25 11:27:41 +01:00
Janis db17c9dbfe more work on XLib backend 2021-11-24 22:57:17 +01:00
Janis 57863e2eb7 changed ModifierState to use bitflags crate 2021-11-24 22:56:53 +01:00
Janis bea2ad6688 cargo check throws no error 2021-11-24 18:40:55 +01:00
No One 6c3999caab test 2021-11-22 20:17:43 +01:00
user ee1aa9cfae fixes and suppressing useless warnings 2021-11-21 20:41:25 +01:00
user ff27ec18d9 VirtualKeyCode conversion traits 2021-11-21 18:07:04 +01:00
user a175362a32 merged keysym.rs from branch refactor 2021-11-21 17:05:27 +01:00
user 1bc0c98156 idk 2021-11-21 17:05:07 +01:00
user 94c5cd9111 starting to abstract away xlib backend 2021-11-21 15:43:17 +01:00
user 8bd8894736 added filed from refactor branch 2021-11-20 23:51:23 +01:00
user 9f77d5f570 disabled mod clients2, fixed xinitrc 2021-11-19 04:30:36 +01:00
27 changed files with 3485 additions and 4631 deletions

1
.gitignore vendored
View file

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

View file

@ -1,18 +1,27 @@
[package] [package]
name = "wm" name = "wm"
version = "0.2.0" version = "0.3.0"
authors = ["noonebtw <noonebtw@gmail.com>"] authors = ["noonebtw <noonebtw@gmail.com>"]
edition = "2018" 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 # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies] [dependencies]
x11 = {version = "2.18.2", features = ["xlib"] } x11 = {version = "2.19", features = ["xlib", "xft"] }
x11rb = "0.8.1" log = "0.4"
num-derive = "0.3.3" simple_logger = "2.0"
num-traits = "0.2.14" dirs = "3.0.0"
log = "0.4.13" log4rs = "1.0"
simple_logger = "1.11.0" indexmap = "1.0"
dirs = "3.0.2" thiserror = "1.0"
log4rs = "1.0.0" bitflags = "1.3"
indexmap = "1.6.2" 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"

View file

@ -1,4 +1,5 @@
# Unnamed WM # No WM
## formerly Unnamed
This Project is a x11 tiling window manager written in Rust and losely based on / inspired by suckless' [dwm](https://dwm.suckless.org/). 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`. 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 :^)
![Unnamed WM in a VM](/vm-ss.png) ![No WM in a VM](/vm-ss.png)

Binary file not shown.

Before

Width:  |  Height:  |  Size: 853 KiB

8
nirgendwm.toml Normal file
View 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
View file

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

12
runner.sh Executable file
View file

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

View file

@ -1,2 +1,2 @@
wrap_comments = true #wrap_comments = true
max_width = 70 max_width = 80

View file

@ -1,8 +1,12 @@
#![allow(dead_code)] #![allow(dead_code)]
#[derive( #[derive(Debug, Hash, Ord, PartialOrd, PartialEq, Eq, Clone, Copy)]
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 { pub enum MouseButton {
Left, Left,
Middle, Middle,
@ -16,21 +20,19 @@ pub enum MouseButton {
} }
/// from winit /// from winit
#[derive( #[derive(Debug, Hash, Ord, PartialOrd, PartialEq, Eq, Clone, Copy)]
Debug, Hash, Ord, PartialOrd, PartialEq, Eq, Clone, Copy,
)]
#[repr(u32)] #[repr(u32)]
pub enum VirtualKeyCode { pub enum VirtualKeyCode {
Key1, One,
Key2, Two,
Key3, Three,
Key4, Four,
Key5, Five,
Key6, Six,
Key7, Seven,
Key8, Eight,
Key9, Nine,
Key0, Zero,
A, A,
B, B,
C, C,
@ -87,7 +89,7 @@ pub enum VirtualKeyCode {
F24, F24,
/// Print Screen/SysRq. /// Print Screen/SysRq.
Snapshot, Print,
/// Scroll Lock. /// Scroll Lock.
Scroll, Scroll,
/// Pause/Break key, next to Scroll lock. /// Pause/Break key, next to Scroll lock.

View file

@ -1,6 +1,28 @@
mod keycodes; pub mod keycodes;
mod window_event; pub mod traits;
mod xcb; pub mod window_event;
mod xlib; pub mod xlib;
pub trait WindowServerBackend {} 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
View 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);
}
}

View file

@ -1,64 +1,37 @@
#![allow(dead_code)] #![allow(dead_code)]
use x11::xlib::Window;
use super::keycodes::{MouseButton, VirtualKeyCode}; use super::{
keycodes::{KeyOrButton, MouseButton, VirtualKeyCode},
structs::WindowType,
};
use crate::util::{Point, Size};
use bitflags::bitflags;
#[derive(Debug)] #[derive(Debug, Clone)]
pub enum WindowEvent { pub enum WindowEvent<Window> {
KeyEvent { KeyEvent(KeyEvent<Window>),
window: Window, ButtonEvent(ButtonEvent<Window>),
event: KeyEvent, MotionEvent(MotionEvent<Window>),
}, MapRequestEvent(MapEvent<Window>),
ButtonEvent { MapEvent(MapEvent<Window>),
window: Window, UnmapEvent(UnmapEvent<Window>),
event: ButtonEvent, CreateEvent(CreateEvent<Window>),
}, DestroyEvent(DestroyEvent<Window>),
MotionEvent { EnterEvent(EnterEvent<Window>),
window: Window, ConfigureEvent(ConfigureEvent<Window>),
event: MotionEvent, FullscreenEvent(FullscreenEvent<Window>), //1 { window: Window, event: 1 },
}, WindowNameEvent(WindowNameEvent<Window>),
MapRequestEvent { WindowTypeChangedEvent(WindowTypeChangedEvent<Window>),
window: Window,
event: MapEvent,
},
MapEvent {
window: Window,
event: MapEvent,
},
UnmapEvent {
window: Window,
event: UnmapEvent,
},
CreateEvent {
window: Window,
event: CreateEvent,
},
DestroyEvent {
window: Window,
event: DestroyEvent,
},
EnterEvent {
window: Window,
event: EnterEvent,
},
ConfigureEvent {
window: Window,
event: ConfigureEvent,
},
FullscreenEvent {
window: Window,
event: FullscreenEvent,
}, //1 { window: Window, event: 1 },
} }
#[derive(Debug)] #[derive(Debug, PartialEq, Eq, Copy, Clone)]
pub enum KeyState { pub enum KeyState {
Pressed, Pressed,
Released, Released,
} }
#[derive( #[derive(
Debug, PartialEq, Eq, PartialOrd, Ord, Hash, Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash, Clone, Copy, serde::Deserialize,
)] )]
#[repr(u8)] #[repr(u8)]
pub enum ModifierKey { pub enum ModifierKey {
@ -72,152 +45,170 @@ pub enum ModifierKey {
NumLock, NumLock,
} }
#[derive(Default, Debug, Clone)] bitflags! {
pub struct ModifierState { pub struct ModifierState: u32 {
modifiers: std::collections::HashSet<ModifierKey>, 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 { impl ModifierState {
pub fn new() -> Self { pub fn eq_ignore_lock(&self, rhs: &Self) -> bool {
Self::default() let mask = Self::IGNORE_LOCK;
*self & mask == *rhs & mask
} }
pub fn set_modifier(&mut self, modifier: ModifierKey) { pub fn with_mod(mut self, modifier: ModifierKey) -> Self {
self.modifiers.insert(modifier); self.insert_mod(modifier);
self
} }
pub fn unset_modifier(&mut self, modifier: ModifierKey) { pub fn unset_mod(&mut self, modifier: ModifierKey) {
self.modifiers.remove(&modifier); self.set_mod(modifier, false);
} }
pub fn get_modifier(&mut self, modifier: ModifierKey) -> bool { pub fn set_mod(&mut self, modifier: ModifierKey, state: bool) {
self.modifiers.contains(&modifier) self.set(
} match modifier {
} ModifierKey::Shift => Self::SHIFT,
ModifierKey::ShiftLock => Self::SHIFT_LOCK,
#[derive(Debug)] ModifierKey::Control => Self::CONTROL,
pub struct KeyEvent { ModifierKey::Alt => Self::ALT,
state: KeyState, ModifierKey::AltGr => Self::ALT_GR,
keycode: VirtualKeyCode, ModifierKey::Super => Self::SUPER,
modifierstate: ModifierState, ModifierKey::NumLock => Self::NUM_LOCK,
} },
impl KeyEvent {
pub fn new(
state: KeyState,
keycode: VirtualKeyCode,
modifierstate: ModifierState,
) -> Self {
Self {
state, state,
keycode, );
modifierstate,
} }
pub fn insert_mod(&mut self, modifier: ModifierKey) {
self.set_mod(modifier, true);
} }
} }
#[derive(Debug)] impl Into<u8> for ModifierKey {
pub struct ButtonEvent { fn into(self) -> u8 {
state: KeyState, self as u8
keycode: MouseButton,
modifierstate: ModifierState,
}
impl ButtonEvent {
pub fn new(
state: KeyState,
keycode: MouseButton,
modifierstate: ModifierState,
) -> Self {
Self {
state,
keycode,
modifierstate,
}
} }
} }
#[derive(Debug)] #[derive(Debug, Clone)]
pub struct MotionEvent { pub struct KeyEvent<Window> {
position: [i32; 2],
}
impl MotionEvent {
pub fn new(position: [i32; 2]) -> Self {
Self { position }
}
}
#[derive(Debug)]
pub struct MapEvent {
window: Window,
}
impl MapEvent {
pub fn new(window: Window) -> Self {
Self { window }
}
}
#[derive(Debug)]
pub struct UnmapEvent {
window: Window,
}
impl UnmapEvent {
pub fn new(window: Window) -> Self {
Self { window }
}
}
#[derive(Debug)]
pub struct EnterEvent {}
#[derive(Debug)]
pub struct DestroyEvent {
window: Window,
}
impl DestroyEvent {
pub fn new(window: Window) -> Self {
Self { window }
}
}
#[derive(Debug)]
pub struct CreateEvent {
window: Window,
position: [i32; 2],
size: [i32; 2],
}
impl CreateEvent {
pub fn new(
window: Window,
position: [i32; 2],
size: [i32; 2],
) -> Self {
Self {
window,
position,
size,
}
}
}
#[derive(Debug)]
pub struct ConfigureEvent {
pub window: Window, pub window: Window,
pub position: [i32; 2], pub state: KeyState,
pub size: [i32; 2], pub keycode: VirtualKeyCode,
pub modifierstate: ModifierState,
} }
impl ConfigureEvent { impl<Window> KeyEvent<Window> {
pub fn new( pub fn new(
window: Window, window: Window,
position: [i32; 2], state: KeyState,
size: [i32; 2], keycode: VirtualKeyCode,
modifierstate: ModifierState,
) -> Self { ) -> 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 { Self {
window, window,
position, position,
@ -226,13 +217,170 @@ impl ConfigureEvent {
} }
} }
#[derive(Debug)] #[derive(Debug, Clone)]
pub struct FullscreenEvent { pub struct ConfigureEvent<Window> {
new_fullscreen: bool, pub window: Window,
pub position: Point<i32>,
pub size: Size<i32>,
} }
impl FullscreenEvent { impl<Window> ConfigureEvent<Window> {
pub fn new(new_fullscreen: bool) -> Self { pub fn new(window: Window, position: Point<i32>, size: Size<i32>) -> Self {
Self { new_fullscreen } 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,
}
} }
} }

View file

@ -1,402 +0,0 @@
//x11 backend
#![allow(dead_code)]
use log::error;
use num_traits::FromPrimitive;
use num_traits::ToPrimitive;
use std::sync::Arc;
use x11rb::{
connect,
connection::Connection,
errors::ReplyError,
errors::ReplyOrIdError,
protocol::xproto::{
Atom, ChangeWindowAttributesAux, ConnectionExt, EventMask,
Screen, Setup,
},
};
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn keysyms() {
let xcb = create_backend().unwrap();
let mapping = xcb
.connection
.get_keyboard_mapping(
xcb.setup().min_keycode,
xcb.setup().max_keycode - xcb.setup().min_keycode + 1,
)
.unwrap();
let mapping = mapping.reply().unwrap();
for (i, keysyms) in mapping
.keysyms
.chunks(mapping.keysyms_per_keycode as usize)
.enumerate()
{
println!(
"keycode: {:#?}\tkeysyms: {:0x?}",
xcb.setup().min_keycode as usize + i,
keysyms
);
}
}
#[test]
fn modifier_masks() {
let xcb = create_backend().unwrap();
let mapping = xcb
.connection
.get_modifier_mapping()
.unwrap()
.reply()
.unwrap();
for (modifier_index, keycodes) in mapping
.keycodes
.chunks(mapping.keycodes_per_modifier() as usize)
.enumerate()
{
println!(
"Mod: {}[{:#x?}] keycodes: {:?}",
modifier_index,
1 << modifier_index,
keycodes
);
}
}
}
#[repr(u8)]
#[derive(FromPrimitive, ToPrimitive)]
pub enum MouseButton {
Left = 1,
Middle,
Right,
ScrollUp,
ScrollDown,
ScrollLeft,
ScrollRight,
Backwards,
Forwards,
}
#[derive(FromPrimitive, ToPrimitive)]
pub enum Key {
BackSpace = 0xff08,
Tab = 0xff09,
Linefeed = 0xff0a,
Clear = 0xff0b,
Return = 0xff0d,
Pause = 0xff13,
ScrollLock = 0xff14,
SysReq = 0xff15,
Escape = 0xff1b,
Delete = 0xffff,
Home = 0xff50,
Left = 0xff51,
Up = 0xff52,
Right = 0xff53,
Down = 0xff54,
PageUp = 0xff55,
PageDown = 0xff56,
End = 0xff57,
Begin = 0xff58,
Space = 0x0020,
Exclam = 0x0021,
Quotedbl = 0x0022,
Numbersign = 0x0023,
Dollar = 0x0024,
Percent = 0x0025,
Ampersand = 0x0026,
Apostrophe = 0x0027,
ParenLeft = 0x0028,
ParenRight = 0x0029,
Asterisk = 0x002a,
Plus = 0x002b,
Comma = 0x002c,
Minus = 0x002d,
Period = 0x002e,
Slash = 0x002f,
Zero = 0x0030,
One = 0x0031,
Two = 0x0032,
Three = 0x0033,
Four = 0x0034,
Five = 0x0035,
Six = 0x0036,
Seven = 0x0037,
Eight = 0x0038,
Nine = 0x0039,
Colon = 0x003a,
Semicolon = 0x003b,
Less = 0x003c,
Equal = 0x003d,
Greater = 0x003e,
Question = 0x003f,
At = 0x0040,
UppercaseA = 0x0041,
UppercaseB = 0x0042,
UppercaseC = 0x0043,
UppercaseD = 0x0044,
UppercaseE = 0x0045,
UppercaseF = 0x0046,
UppercaseG = 0x0047,
UppercaseH = 0x0048,
UppercaseI = 0x0049,
UppercaseJ = 0x004a,
UppercaseK = 0x004b,
UppercaseL = 0x004c,
UppercaseM = 0x004d,
UppercaseN = 0x004e,
UppercaseO = 0x004f,
UppercaseP = 0x0050,
UppercaseQ = 0x0051,
UppercaseR = 0x0052,
UppercaseS = 0x0053,
UppercaseT = 0x0054,
UppercaseU = 0x0055,
UppercaseV = 0x0056,
UppercaseW = 0x0057,
UppercaseX = 0x0058,
UppercaseY = 0x0059,
UppercaseZ = 0x005a,
BracketLeft = 0x005b,
Backslash = 0x005c,
BracketRight = 0x005d,
AsciiCircum = 0x005e,
Underscore = 0x005f,
Grave = 0x0060,
LowercaseA = 0x0061,
LowercaseB = 0x0062,
LowercaseC = 0x0063,
LowercaseD = 0x0064,
LowercaseE = 0x0065,
LowercaseF = 0x0066,
LowercaseG = 0x0067,
LowercaseH = 0x0068,
LowercaseI = 0x0069,
LowercaseJ = 0x006a,
LowercaseK = 0x006b,
LowercaseL = 0x006c,
LowercaseM = 0x006d,
LowercaseN = 0x006e,
LowercaseO = 0x006f,
LowercaseP = 0x0070,
LowercaseQ = 0x0071,
LowercaseR = 0x0072,
LowercaseS = 0x0073,
LowercaseT = 0x0074,
LowercaseU = 0x0075,
LowercaseV = 0x0076,
LowercaseW = 0x0077,
LowercaseX = 0x0078,
LowercaseY = 0x0079,
LowercaseZ = 0x007a,
BraceLeft = 0x007b,
Bar = 0x007c,
BraceRight = 0x007d,
AsciiTilde = 0x007e,
}
impl Key {}
// '<,'>s/.* XK_\([A-z,_]*\)[ ]*\(0x[a-f,0-9]*\).*/\1 = \2,/
struct Atoms {
wm_protocols: Atom,
wm_state: Atom,
wm_delete_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 Atoms {
fn new<C>(connection: Arc<C>) -> Result<Self, ReplyOrIdError>
where
C: Connection,
{
let wm_protocols =
connection.intern_atom(false, b"WM_PROTOCOLS")?;
let wm_state = connection.intern_atom(false, b"WM_STATE")?;
let wm_delete_window =
connection.intern_atom(false, b"WM_DELETE_WINDOW")?;
let wm_take_focus =
connection.intern_atom(false, b"WM_TAKE_FOCUS")?;
let net_supported =
connection.intern_atom(false, b"_NET_SUPPORTED")?;
let net_active_window =
connection.intern_atom(false, b"_NET_ACTIVE_WINDOW")?;
let net_client_list =
connection.intern_atom(false, b"_NET_CLIENT_LIST")?;
let net_wm_name =
connection.intern_atom(false, b"_NET_WM_NAME")?;
let net_wm_state =
connection.intern_atom(false, b"_NET_WM_STATE")?;
let net_wm_state_fullscreen = connection
.intern_atom(false, b"_NET_WM_STATE_FULLSCREEN")?;
let net_wm_window_type =
connection.intern_atom(false, b"_NET_WM_WINDOW_TYPE")?;
let net_wm_window_type_dialog = connection
.intern_atom(false, b"_NET_WM_WINDOW_TYPE_DIALOG")?;
Ok(Self {
wm_protocols: wm_protocols.reply()?.atom,
wm_state: wm_state.reply()?.atom,
wm_delete_window: wm_delete_window.reply()?.atom,
wm_take_focus: wm_take_focus.reply()?.atom,
net_supported: net_supported.reply()?.atom,
net_active_window: net_active_window.reply()?.atom,
net_client_list: net_client_list.reply()?.atom,
net_wm_name: net_wm_name.reply()?.atom,
net_wm_state: net_wm_state.reply()?.atom,
net_wm_state_fullscreen: net_wm_state_fullscreen
.reply()?
.atom,
net_wm_window_type: net_wm_window_type.reply()?.atom,
net_wm_window_type_dialog: net_wm_window_type_dialog
.reply()?
.atom,
})
}
}
pub struct X11Backend<C>
where
C: Connection,
{
connection: Arc<C>,
screen: usize,
atoms: Atoms,
}
pub fn create_backend() -> Result<
X11Backend<impl Connection + Send + Sync>,
Box<dyn std::error::Error>,
> {
let (connection, screen) = connect(None)?;
Ok(X11Backend::new(Arc::new(connection), screen)?)
}
impl<C> X11Backend<C>
where
C: Connection,
{
pub fn new(
connection: Arc<C>,
screen: usize,
) -> Result<Self, ReplyOrIdError> {
let atoms = Atoms::new(connection.clone())?;
Ok(Self {
connection,
screen,
atoms,
})
}
fn setup(&self) -> &Setup {
self.connection.setup()
}
fn screen(&self) -> &Screen {
&self.connection.setup().roots[self.screen]
}
fn root(&self) -> u32 {
self.screen().root
}
// this needs the mask aswell to determine the keysym
fn keysym_for_keycode(&self, keycode: u8) -> Option<Key> {
let setup = self.setup();
let mapping = self
.connection
.get_keyboard_mapping(
setup.min_keycode,
setup.max_keycode - setup.min_keycode + 1,
)
.ok()?;
let mapping = mapping.reply().ok()?;
mapping
.keysyms
.chunks(mapping.keysyms_per_keycode as usize)
.nth(keycode as usize)
.and_then(|keysyms| Key::from_u32(keysyms[0]))
}
fn keycode_for_keysym<K>(&self, keysym: &K) -> Option<u8>
where
K: num_traits::ToPrimitive,
{
if let Some(keysym) = keysym.to_u32() {
let setup = self.setup();
let mapping = self
.connection
.get_keyboard_mapping(
setup.min_keycode,
setup.max_keycode - setup.min_keycode + 1,
)
.ok()?;
let mapping = mapping.reply().ok()?;
mapping
.keysyms
.chunks(mapping.keysyms_per_keycode as usize)
.enumerate()
.find_map(|(i, keysyms)| {
if keysyms.contains(&keysym) {
Some(setup.min_keycode + i as u8)
} else {
None
}
})
} else {
None
}
}
pub fn request_substructure_events(
&self,
) -> Result<(), ReplyError> {
let attributes = ChangeWindowAttributesAux::default()
.event_mask(
EventMask::SUBSTRUCTURE_REDIRECT
| EventMask::SUBSTRUCTURE_NOTIFY,
);
match self
.connection
.change_window_attributes(self.root(), &attributes)?
.check()
{
Ok(_) => Ok(()),
Err(err) => {
error!(
"Failed to request substructure redirect/notify: another \
window manager is running. {:#?}",
err
);
Err(err)
}
}
}
}

File diff suppressed because it is too large Load diff

View file

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

View file

@ -1,7 +1,7 @@
use crate::backends::{ #![allow(unreachable_patterns)]
keycodes::{MouseButton, VirtualKeyCode}, use std::{borrow::Borrow, ops::Deref};
window_event::ModifierKey,
}; use crate::backends::keycodes::{MouseButton, VirtualKeyCode};
pub fn xev_to_mouse_button( pub fn xev_to_mouse_button(
button: &x11::xlib::XButtonEvent, button: &x11::xlib::XButtonEvent,
@ -34,10 +34,52 @@ pub fn mouse_button_to_xbutton(button: MouseButton) -> i32 {
} }
} }
#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Copy, Clone)]
pub struct XKeySym(pub u32);
impl XKeySym {
pub fn new(value: u32) -> Self {
Self(value)
}
pub fn get(&self) -> u32 {
self.0
}
}
impl Borrow<u32> for XKeySym {
fn borrow(&self) -> &u32 {
&self.0
}
}
impl AsRef<u32> for XKeySym {
fn as_ref(&self) -> &u32 {
&self.0
}
}
impl Deref for XKeySym {
type Target = u32;
fn deref(&self) -> &Self::Target {
&self.0
}
}
impl From<XKeySym> for VirtualKeyCode {
fn from(value: XKeySym) -> Self {
keysym_to_virtual_keycode(*value).unwrap()
}
}
impl From<VirtualKeyCode> for XKeySym {
fn from(value: VirtualKeyCode) -> Self {
Self(virtual_keycode_to_keysym(value).unwrap())
}
}
/// from winit /// from winit
pub fn keysym_to_virtual_keycode( pub fn keysym_to_virtual_keycode(keysym: u32) -> Option<VirtualKeyCode> {
keysym: u32,
) -> Option<VirtualKeyCode> {
Some(match keysym { Some(match keysym {
x11::keysym::XK_BackSpace => VirtualKeyCode::Back, x11::keysym::XK_BackSpace => VirtualKeyCode::Back,
x11::keysym::XK_Tab => VirtualKeyCode::Tab, x11::keysym::XK_Tab => VirtualKeyCode::Tab,
@ -78,11 +120,11 @@ pub fn keysym_to_virtual_keycode(
x11::keysym::XK_Page_Down => VirtualKeyCode::PageDown, x11::keysym::XK_Page_Down => VirtualKeyCode::PageDown,
x11::keysym::XK_End => VirtualKeyCode::End, x11::keysym::XK_End => VirtualKeyCode::End,
//x11::keysym::XK_Begin => VirtualKeyCode::Begin, //x11::keysym::XK_Begin => VirtualKeyCode::Begin,
//x11::keysym::XK_Win_L => VirtualKeyCode::Win_l, x11::keysym::XK_Win_L => VirtualKeyCode::LWin,
//x11::keysym::XK_Win_R => VirtualKeyCode::Win_r, x11::keysym::XK_Win_R => VirtualKeyCode::RWin,
//x11::keysym::XK_App => VirtualKeyCode::App, //x11::keysym::XK_App => VirtualKeyCode::App,
//x11::keysym::XK_Select => VirtualKeyCode::Select, //x11::keysym::XK_Select => VirtualKeyCode::Select,
//x11::keysym::XK_Print => VirtualKeyCode::Print, x11::keysym::XK_Print => VirtualKeyCode::Print,
//x11::keysym::XK_Execute => VirtualKeyCode::Execute, //x11::keysym::XK_Execute => VirtualKeyCode::Execute,
x11::keysym::XK_Insert => VirtualKeyCode::Insert, x11::keysym::XK_Insert => VirtualKeyCode::Insert,
//x11::keysym::XK_Undo => VirtualKeyCode::Undo, //x11::keysym::XK_Undo => VirtualKeyCode::Undo,
@ -224,16 +266,16 @@ pub fn keysym_to_virtual_keycode(
x11::keysym::XK_minus => VirtualKeyCode::Minus, x11::keysym::XK_minus => VirtualKeyCode::Minus,
x11::keysym::XK_period => VirtualKeyCode::Period, x11::keysym::XK_period => VirtualKeyCode::Period,
x11::keysym::XK_slash => VirtualKeyCode::Slash, x11::keysym::XK_slash => VirtualKeyCode::Slash,
x11::keysym::XK_0 => VirtualKeyCode::Key0, x11::keysym::XK_0 => VirtualKeyCode::Zero,
x11::keysym::XK_1 => VirtualKeyCode::Key1, x11::keysym::XK_1 => VirtualKeyCode::One,
x11::keysym::XK_2 => VirtualKeyCode::Key2, x11::keysym::XK_2 => VirtualKeyCode::Two,
x11::keysym::XK_3 => VirtualKeyCode::Key3, x11::keysym::XK_3 => VirtualKeyCode::Three,
x11::keysym::XK_4 => VirtualKeyCode::Key4, x11::keysym::XK_4 => VirtualKeyCode::Four,
x11::keysym::XK_5 => VirtualKeyCode::Key5, x11::keysym::XK_5 => VirtualKeyCode::Five,
x11::keysym::XK_6 => VirtualKeyCode::Key6, x11::keysym::XK_6 => VirtualKeyCode::Six,
x11::keysym::XK_7 => VirtualKeyCode::Key7, x11::keysym::XK_7 => VirtualKeyCode::Seven,
x11::keysym::XK_8 => VirtualKeyCode::Key8, x11::keysym::XK_8 => VirtualKeyCode::Eight,
x11::keysym::XK_9 => VirtualKeyCode::Key9, x11::keysym::XK_9 => VirtualKeyCode::Nine,
x11::keysym::XK_colon => VirtualKeyCode::Colon, x11::keysym::XK_colon => VirtualKeyCode::Colon,
x11::keysym::XK_semicolon => VirtualKeyCode::Semicolon, x11::keysym::XK_semicolon => VirtualKeyCode::Semicolon,
//x11::keysym::XK_less => VirtualKeyCode::Less, //x11::keysym::XK_less => VirtualKeyCode::Less,
@ -1034,9 +1076,7 @@ pub fn keysym_to_virtual_keycode(
//x11::keysym::XK_hebrew_taf => VirtualKeyCode::Hebrew_taf, //x11::keysym::XK_hebrew_taf => VirtualKeyCode::Hebrew_taf,
//x11::keysym::XK_Hebrew_switch => VirtualKeyCode::Hebrew_switch, //x11::keysym::XK_Hebrew_switch => VirtualKeyCode::Hebrew_switch,
x11::keysym::XF86XK_Back => VirtualKeyCode::NavigateBackward, x11::keysym::XF86XK_Back => VirtualKeyCode::NavigateBackward,
x11::keysym::XF86XK_Forward => { x11::keysym::XF86XK_Forward => VirtualKeyCode::NavigateForward,
VirtualKeyCode::NavigateForward
}
x11::keysym::XF86XK_Copy => VirtualKeyCode::Copy, x11::keysym::XF86XK_Copy => VirtualKeyCode::Copy,
x11::keysym::XF86XK_Paste => VirtualKeyCode::Paste, x11::keysym::XF86XK_Paste => VirtualKeyCode::Paste,
x11::keysym::XF86XK_Cut => VirtualKeyCode::Cut, x11::keysym::XF86XK_Cut => VirtualKeyCode::Cut,
@ -1044,9 +1084,7 @@ pub fn keysym_to_virtual_keycode(
}) })
} }
pub fn virtual_keycode_to_keysym( pub fn virtual_keycode_to_keysym(keycode: VirtualKeyCode) -> Option<u32> {
keycode: VirtualKeyCode,
) -> Option<u32> {
Some(match keycode { Some(match keycode {
VirtualKeyCode::Back => x11::keysym::XK_BackSpace, VirtualKeyCode::Back => x11::keysym::XK_BackSpace,
VirtualKeyCode::Tab => x11::keysym::XK_Tab, VirtualKeyCode::Tab => x11::keysym::XK_Tab,
@ -1087,11 +1125,11 @@ pub fn virtual_keycode_to_keysym(
VirtualKeyCode::PageDown => x11::keysym::XK_Page_Down, VirtualKeyCode::PageDown => x11::keysym::XK_Page_Down,
VirtualKeyCode::End => x11::keysym::XK_End, VirtualKeyCode::End => x11::keysym::XK_End,
//VirtualKeyCode::Begin => x11::keysym::XK_Begin, //VirtualKeyCode::Begin => x11::keysym::XK_Begin,
//VirtualKeyCode::Win_l => x11::keysym::XK_Win_L, VirtualKeyCode::LWin => x11::keysym::XK_Win_L,
//VirtualKeyCode::Win_r => x11::keysym::XK_Win_R, VirtualKeyCode::RWin => x11::keysym::XK_Win_R,
//VirtualKeyCode::App => x11::keysym::XK_App, //VirtualKeyCode::App => x11::keysym::XK_App,
//VirtualKeyCode::Select => x11::keysym::XK_Select, //VirtualKeyCode::Select => x11::keysym::XK_Select,
//VirtualKeyCode::Print => x11::keysym::XK_Print, VirtualKeyCode::Print => x11::keysym::XK_Print,
//VirtualKeyCode::Execute => x11::keysym::XK_Execute, //VirtualKeyCode::Execute => x11::keysym::XK_Execute,
VirtualKeyCode::Insert => x11::keysym::XK_Insert, VirtualKeyCode::Insert => x11::keysym::XK_Insert,
//VirtualKeyCode::Undo => x11::keysym::XK_Undo, //VirtualKeyCode::Undo => x11::keysym::XK_Undo,
@ -1233,16 +1271,16 @@ pub fn virtual_keycode_to_keysym(
VirtualKeyCode::Minus => x11::keysym::XK_minus, VirtualKeyCode::Minus => x11::keysym::XK_minus,
VirtualKeyCode::Period => x11::keysym::XK_period, VirtualKeyCode::Period => x11::keysym::XK_period,
VirtualKeyCode::Slash => x11::keysym::XK_slash, VirtualKeyCode::Slash => x11::keysym::XK_slash,
VirtualKeyCode::Key0 => x11::keysym::XK_0, VirtualKeyCode::Zero => x11::keysym::XK_0,
VirtualKeyCode::Key1 => x11::keysym::XK_1, VirtualKeyCode::One => x11::keysym::XK_1,
VirtualKeyCode::Key2 => x11::keysym::XK_2, VirtualKeyCode::Two => x11::keysym::XK_2,
VirtualKeyCode::Key3 => x11::keysym::XK_3, VirtualKeyCode::Three => x11::keysym::XK_3,
VirtualKeyCode::Key4 => x11::keysym::XK_4, VirtualKeyCode::Four => x11::keysym::XK_4,
VirtualKeyCode::Key5 => x11::keysym::XK_5, VirtualKeyCode::Five => x11::keysym::XK_5,
VirtualKeyCode::Key6 => x11::keysym::XK_6, VirtualKeyCode::Six => x11::keysym::XK_6,
VirtualKeyCode::Key7 => x11::keysym::XK_7, VirtualKeyCode::Seven => x11::keysym::XK_7,
VirtualKeyCode::Key8 => x11::keysym::XK_8, VirtualKeyCode::Eight => x11::keysym::XK_8,
VirtualKeyCode::Key9 => x11::keysym::XK_9, VirtualKeyCode::Nine => x11::keysym::XK_9,
VirtualKeyCode::Colon => x11::keysym::XK_colon, VirtualKeyCode::Colon => x11::keysym::XK_colon,
VirtualKeyCode::Semicolon => x11::keysym::XK_semicolon, VirtualKeyCode::Semicolon => x11::keysym::XK_semicolon,
//VirtualKeyCode::Less => x11::keysym::XK_less, //VirtualKeyCode::Less => x11::keysym::XK_less,
@ -2043,12 +2081,28 @@ pub fn virtual_keycode_to_keysym(
//VirtualKeyCode::Hebrew_taf => x11::keysym::XK_hebrew_taf, //VirtualKeyCode::Hebrew_taf => x11::keysym::XK_hebrew_taf,
//VirtualKeyCode::Hebrew_switch => x11::keysym::XK_Hebrew_switch, //VirtualKeyCode::Hebrew_switch => x11::keysym::XK_Hebrew_switch,
VirtualKeyCode::NavigateBackward => x11::keysym::XF86XK_Back, VirtualKeyCode::NavigateBackward => x11::keysym::XF86XK_Back,
VirtualKeyCode::NavigateForward => { VirtualKeyCode::NavigateForward => x11::keysym::XF86XK_Forward,
x11::keysym::XF86XK_Forward
}
VirtualKeyCode::Copy => x11::keysym::XF86XK_Copy, VirtualKeyCode::Copy => x11::keysym::XF86XK_Copy,
VirtualKeyCode::Paste => x11::keysym::XF86XK_Paste, VirtualKeyCode::Paste => x11::keysym::XF86XK_Paste,
VirtualKeyCode::Cut => x11::keysym::XF86XK_Cut, VirtualKeyCode::Cut => x11::keysym::XF86XK_Cut,
_ => return None, _ => return None,
}) })
} }
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_keysym_to_vkc() {
let keysym: XKeySym = VirtualKeyCode::W.into();
let keycode: VirtualKeyCode = keysym.into();
assert_eq!(keycode, VirtualKeyCode::W);
let keysym2: XKeySym = keycode.into();
assert_eq!(keysym2, keysym);
assert_eq!(&x11::keysym::XK_W, keysym.as_ref());
}
}

File diff suppressed because it is too large Load diff

View file

@ -1,481 +0,0 @@
#![allow(dead_code)]
use crate::clients2::*;
use std::hash::Hash;
pub struct Size<T> {
width: T,
height: T,
}
impl<T> Size<T> {
pub fn new(width: T, height: T) -> Self {
Self { width, height }
}
/// Get a reference to the size's width.
pub fn width(&self) -> &T {
&self.width
}
/// Get a reference to the size's height.
pub fn height(&self) -> &T {
&self.height
}
/// Get a mutable reference to the size's width.
pub fn width_mut(&mut self) -> &mut T {
&mut self.width
}
/// Get a mutable reference to the size's height.
pub fn height_mut(&mut self) -> &mut T {
&mut self.height
}
}
impl<T> Size<T>
where
T: Copy,
{
pub fn dimensions(&self) -> (T, T) {
(self.width, self.height)
}
}
pub struct Workspace<T>
where
T: Eq,
{
master: Vec<T>,
aux: Vec<T>,
}
pub struct WorkspaceStore<T>
where
T: Eq,
{
workspaces: Vec<Workspace<T>>,
current_indices: Vec<usize>,
previous_indices: Option<Vec<usize>>,
}
pub struct ClientManager<T>
where
T: Hash + Eq + Copy,
{
store: ClientStore<T>,
focused: Option<T>,
workspaces: WorkspaceStore<T>,
// config
gap: i32,
border_size: i32,
master_size: f32,
screen_size: Size<i32>,
// experimental
invalidated: std::collections::HashSet<T>,
}
impl<T> Default for Workspace<T>
where
T: Eq,
{
fn default() -> Self {
Self {
master: vec![],
aux: vec![],
}
}
}
impl<T> Default for WorkspaceStore<T>
where
T: Eq,
{
fn default() -> Self {
Self {
workspaces: vec![Default::default()],
current_indices: vec![0],
previous_indices: None,
}
}
}
impl<T> Default for ClientManager<T>
where
T: Hash + Eq + Copy,
{
fn default() -> Self {
Self {
store: Default::default(),
focused: None,
workspaces: Default::default(),
gap: 0,
border_size: 0,
master_size: 1.0,
screen_size: Size::new(1, 1),
invalidated: Default::default(),
}
}
}
enum WorkspaceEntry<T> {
Master(T),
Aux(T),
Vacant,
}
impl<T> Workspace<T>
where
T: Eq,
{
pub fn contains(&self, key: &T) -> bool {
self.aux.contains(key) || self.master.contains(key)
}
pub fn is_master(&self, key: &T) -> bool {
self.master.contains(key)
}
pub fn is_aux(&self, key: &T) -> bool {
self.aux.contains(key)
}
pub fn push(&mut self, key: T) {
self.aux.push(key);
}
pub fn remove(&mut self, key: &T) {
self.master.retain(|k| k != key);
self.aux.retain(|k| k != key);
}
pub fn entry(&self, key: &T) -> Workspace<&T> {
todo!()
}
}
impl<T> WorkspaceStore<T>
where
T: Eq,
{
pub fn new(num: usize) -> Self {
let mut workspaces = Vec::with_capacity(num);
workspaces.resize_with(num, Default::default);
Self {
workspaces,
..Default::default()
}
}
pub fn remove(&mut self, key: &T) {
self.iter_mut().for_each(|w| w.remove(key));
}
fn len(&self) -> usize {
self.workspaces.len()
}
fn get_current(&self) -> impl Iterator<Item = &Workspace<T>> {
self.current_indices
.iter()
.map(move |&i| &self.workspaces[i])
}
fn get_current_mut(
&mut self,
) -> impl Iterator<Item = &mut Workspace<T>> {
let current_indices = &self.current_indices;
self.workspaces
.iter_mut()
.enumerate()
.filter(move |(i, _)| current_indices.contains(i))
.map(|(_, w)| w)
}
fn iter_current_master(&self) -> impl Iterator<Item = &T> {
let current_indices = &self.current_indices;
self.workspaces
.iter()
.enumerate()
.filter(move |(i, _)| current_indices.contains(i))
.map(|(_, w)| w)
.flat_map(|w| w.master.iter())
}
fn iter_current_aux(&self) -> impl Iterator<Item = &T> {
let current_indices = &self.current_indices;
self.workspaces
.iter()
.enumerate()
.filter(move |(i, _)| current_indices.contains(i))
.map(|(_, w)| w)
.flat_map(|w| w.aux.iter())
}
fn iter_mut_current_master(
&mut self,
) -> impl Iterator<Item = &mut T> {
let current_indices = &self.current_indices;
self.workspaces
.iter_mut()
.enumerate()
.filter(move |(i, _)| current_indices.contains(i))
.map(|(_, w)| w)
.flat_map(|w| w.master.iter_mut())
}
fn iter_mut_current_aux(
&mut self,
) -> impl Iterator<Item = &mut T> {
let current_indices = &self.current_indices;
self.workspaces
.iter_mut()
.enumerate()
.filter(move |(i, _)| current_indices.contains(i))
.map(|(_, w)| w)
.flat_map(|w| w.aux.iter_mut())
}
fn iter(&self) -> impl Iterator<Item = &Workspace<T>> {
self.workspaces.iter()
}
fn iter_mut(
&mut self,
) -> impl Iterator<Item = &mut Workspace<T>> {
self.workspaces.iter_mut()
}
fn select_workspace(&mut self, idx: usize) {
let len = self.len();
self.previous_indices = Some(std::mem::replace(
&mut self.current_indices,
vec![idx % len],
));
}
fn toggle_additional_workspace(&mut self, idx: usize) {
let idx = idx % self.len();
if self.current_indices.contains(&idx) {
self.current_indices.retain(|&i| i != idx);
} else {
self.current_indices.push(idx);
}
}
fn select_workspaces<I>(&mut self, idx: I)
where
Vec<usize>: From<I>,
{
self.previous_indices = Some(std::mem::replace(
&mut self.current_indices,
idx.into(),
));
}
fn select_previous_workspaces(&mut self) {
if let Some(previous_indices) = &mut self.previous_indices {
std::mem::swap(
previous_indices,
&mut self.current_indices,
);
}
}
/// Rotate n times left
fn rotate_left(&mut self, n: usize) {
let len = self.len();
let rotate_index = |i| -> usize {
let a = n % len;
let b = i & len;
((b + len) - a) % len
};
self.select_workspaces(
self.current_indices
.iter()
.map(rotate_index)
.collect::<Vec<_>>(),
);
}
/// Rotate n times left
fn rotate_right(&mut self, n: usize) {
let len = self.len();
let rotate_index = |i| -> usize {
let a = n % len;
let b = i & len;
((b + len) + a) % len
};
self.select_workspaces(
self.current_indices
.iter()
.map(rotate_index)
.collect::<Vec<_>>(),
);
}
}
impl<T> ClientManager<T>
where
T: Hash + Eq + Copy,
{
pub fn new() -> Self {
Self::default()
}
pub fn with_gap(self, gap: i32) -> Self {
Self { gap, ..self }
}
pub fn with_border(self, border_size: i32) -> Self {
Self {
border_size,
..self
}
}
pub fn with_screen_size(self, screen_size: Size<i32>) -> Self {
Self {
screen_size,
..self
}
}
pub fn with_workspaces(self, num: usize) -> Self {
Self {
workspaces: WorkspaceStore::new(num),
..self
}
}
pub fn new_client(&mut self, client: Client<T>) {
let entry = self.store.insert(Entry::Tiled(client));
match entry {
Entry::Tiled(client) => {
self.workspaces
.get_current_mut()
.next()
.unwrap()
.push(client.window_id());
self.tile_clients()
}
_ => {}
}
}
pub fn remove_client(&mut self, key: &T) {
if let Some(id) = self.focused {
if id == *key {
self.focused = None;
}
}
match self.store.remove(key) {
// if the window was tiled, remove it from all workspaces and retile all clients
Entry::Tiled(_) => {
self.workspaces.remove(key);
self.tile_clients();
}
_ => {}
};
}
pub fn tile_clients(&mut self) {
let gap = self.gap;
let border = self.border_size;
let (width, height) = {
let dimensions = self.screen_size.dimensions();
(dimensions.0 - gap * 2, dimensions.1 - gap * 2)
};
let len_master =
self.workspaces.iter_current_master().count();
let len_aux = self.workspaces.iter_current_aux().count();
let width_master = match len_aux {
0 => width,
_ => width * (self.master_size / 2.0) as i32,
};
let width_aux = width - width_master;
let height_master = match len_master {
0 | 1 => height,
n => height / n as i32,
};
let height_aux = match len_aux {
0 | 1 => height,
n => height / n as i32,
};
for (i, id) in
self.workspaces.iter_mut_current_master().enumerate()
{
let size = (
width_master - gap * 2 - border * 2,
height_master - gap * 2 - border * 2,
);
let position =
(gap * 2, height_master * i as i32 + gap * 2);
if let Some(client) =
Option::<&mut Client<T>>::from(self.store.get_mut(id))
{
if *client.position() != position
|| *client.size() != size
{
*client.position_mut() = position;
*client.size_mut() = size;
self.invalidated.insert(*id);
}
}
}
for (i, id) in
self.workspaces.iter_mut_current_aux().enumerate()
{
let size = (
width_aux - gap * 2 - border * 2,
height_aux - gap * 2 - border * 2,
);
let position = (
width_master + gap * 2,
height_aux * i as i32 + gap * 2,
);
if let Some(client) =
Option::<&mut Client<T>>::from(self.store.get_mut(id))
{
if *client.position() != position
|| *client.size() != size
{
*client.position_mut() = position;
*client.size_mut() = size;
self.invalidated.insert(*id);
}
}
}
}
}

View file

@ -1,31 +1,41 @@
use std::num::NonZeroI32;
use std::{ops::Rem, usize}; use std::{ops::Rem, usize};
use indexmap::IndexMap; 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::BuildIdentityHasher;
use crate::util::{Point, Size};
mod client { mod client {
use std::hash::{Hash, Hasher}; use std::hash::{Hash, Hasher};
use crate::{
backends::structs::WindowType,
util::{Point, Size},
};
use x11::xlib::Window; use x11::xlib::Window;
#[derive(Clone, Debug)] #[derive(Clone, Debug)]
pub struct Client { pub struct Client {
pub(crate) window: Window, pub(crate) window: Window,
pub(crate) size: (i32, i32), pub(crate) size: Size<i32>,
pub(crate) position: (i32, i32), pub(crate) position: Point<i32>,
pub(crate) transient_for: Option<Window>, pub(crate) parent_window: Option<Window>,
pub(crate) window_type: WindowType,
pub(crate) fullscreen: bool,
} }
impl Default for Client { impl Default for Client {
fn default() -> Self { fn default() -> Self {
Self { Self {
window: 0, window: 0,
size: (100, 100), size: (100, 100).into(),
position: (0, 0), position: (0, 0).into(),
transient_for: None, parent_window: None,
fullscreen: false,
window_type: WindowType::Normal,
} }
} }
} }
@ -34,26 +44,26 @@ mod client {
#[allow(dead_code)] #[allow(dead_code)]
pub fn new( pub fn new(
window: Window, window: Window,
size: (i32, i32), size: Size<i32>,
position: (i32, i32), position: Point<i32>,
) -> Self { ) -> Self {
Self { Self {
window, window,
size, size,
position, position,
transient_for: None, ..Self::default()
} }
} }
pub fn new_transient( pub fn new_dialog(
window: Window, window: Window,
size: (i32, i32), size: Size<i32>,
transient: Window, transient: Window,
) -> Self { ) -> Self {
Self { Self {
window, window,
size, size,
transient_for: Some(transient), parent_window: Some(transient),
..Default::default() ..Default::default()
} }
} }
@ -65,8 +75,47 @@ mod client {
} }
} }
pub fn is_transient(&self) -> bool { pub fn with_window_type(self, window_type: WindowType) -> Self {
self.transient_for.is_some() 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) virtual_screens: VirtualScreenStore,
pub(self) gap: i32, pub(self) gap: i32,
pub(self) screen_size: (i32, i32), pub(self) screen_size: Size<i32>,
pub(self) master_size: f32, pub(self) master_size: f32,
border_size: i32, border_size: i32,
} }
@ -166,7 +215,7 @@ impl Default for ClientState {
focused: None, focused: None,
virtual_screens: VirtualScreenStore::new(1), virtual_screens: VirtualScreenStore::new(1),
gap: 0, gap: 0,
screen_size: (1, 1), screen_size: (1, 1).into(),
master_size: 1.0, master_size: 1.0,
border_size: 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 { Self {
screen_size, screen_size,
..self ..self
@ -207,6 +256,7 @@ impl ClientState {
self.border_size self.border_size
} }
#[allow(dead_code)]
pub fn set_border_mut(&mut self, new: i32) { pub fn set_border_mut(&mut self, new: i32) {
self.border_size = new; self.border_size = new;
} }
@ -214,27 +264,42 @@ impl ClientState {
pub fn insert(&mut self, mut client: Client) -> Option<&Client> { pub fn insert(&mut self, mut client: Client) -> Option<&Client> {
let key = client.key(); let key = client.key();
if client.is_transient() match client.window_type {
&& self.contains(&client.transient_for.unwrap()) // 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())
{ {
let transient =
self.get(&client.transient_for.unwrap()).unwrap();
client.position = { client.position = {
( (
transient.position.0 parent.position.x
+ (transient.size.0 - client.size.0) / 2, + (parent.size.width - client.size.width) / 2,
transient.position.1 parent.position.y
+ (transient.size.1 - client.size.1) / 2, + (parent.size.height - client.size.height) / 2,
) )
.into()
}; };
}
client.size = client.size.clamp(
self.screen_size
- Size::new(self.border_size * 2, self.border_size * 2),
);
self.floating_clients.insert(key, client); self.floating_clients.insert(key, client);
} else { }
WindowType::Normal => {
self.clients.insert(key, client); self.clients.insert(key, client);
self.virtual_screens.get_mut_current().insert(&key); self.virtual_screens.get_mut_current().insert(&key);
} }
}
// adding a client changes the liling layout, rearrange // adding a client changes the liling layout, rearrange
self.arrange_virtual_screen(); self.arrange_virtual_screen();
@ -272,9 +337,7 @@ impl ClientState {
|| self.floating_clients.contains_key(&key) || self.floating_clients.contains_key(&key)
} }
pub fn iter_floating( pub fn iter_floating(&self) -> impl Iterator<Item = (&u64, &Client)> {
&self,
) -> impl Iterator<Item = (&u64, &Client)> {
self.floating_clients.iter() self.floating_clients.iter()
} }
@ -286,44 +349,40 @@ impl ClientState {
.filter(move |&(k, _)| self.is_client_visible(k)) .filter(move |&(k, _)| self.is_client_visible(k))
} }
fn iter_all_clients( pub fn iter_all_clients(&self) -> impl Iterator<Item = (&u64, &Client)> {
&self,
) -> impl Iterator<Item = (&u64, &Client)> {
self.floating_clients.iter().chain(self.clients.iter()) self.floating_clients.iter().chain(self.clients.iter())
} }
pub fn iter_hidden( pub fn iter_hidden(&self) -> impl Iterator<Item = (&u64, &Client)> {
&self,
) -> impl Iterator<Item = (&u64, &Client)> {
self.iter_all_clients() self.iter_all_clients()
.filter(move |&(k, _)| !self.is_client_visible(k)) .filter(move |&(k, _)| !self.is_client_visible(k))
} }
pub fn iter_transient( pub fn iter_transient(&self) -> impl Iterator<Item = (&u64, &Client)> {
&self, self.iter_floating().filter(|&(_, c)| c.has_parent_window())
) -> impl Iterator<Item = (&u64, &Client)> {
self.iter_floating().filter(|&(_, c)| c.is_transient())
} }
pub fn iter_visible( pub fn iter_by_window_type(
&self, &self,
window_type: WindowType,
) -> impl Iterator<Item = (&u64, &Client)> { ) -> 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)> {
self.iter_all_clients() self.iter_all_clients()
.filter(move |&(k, _)| self.is_client_visible(k)) .filter(move |&(k, _)| self.is_client_visible(k))
} }
#[allow(dead_code)] #[allow(dead_code)]
pub fn iter_current_screen( pub fn iter_current_screen(&self) -> impl Iterator<Item = (&u64, &Client)> {
&self,
) -> impl Iterator<Item = (&u64, &Client)> {
self.clients.iter().filter(move |&(k, _)| { self.clients.iter().filter(move |&(k, _)| {
self.virtual_screens.get_current().contains(k) self.virtual_screens.get_current().contains(k)
}) })
} }
pub fn iter_master_stack( pub fn iter_master_stack(&self) -> impl Iterator<Item = (&u64, &Client)> {
&self,
) -> impl Iterator<Item = (&u64, &Client)> {
self.virtual_screens self.virtual_screens
.get_current() .get_current()
.master .master
@ -331,9 +390,7 @@ impl ClientState {
.map(move |k| (k, self.get(k).unwrap())) .map(move |k| (k, self.get(k).unwrap()))
} }
pub fn iter_aux_stack( pub fn iter_aux_stack(&self) -> impl Iterator<Item = (&u64, &Client)> {
&self,
) -> impl Iterator<Item = (&u64, &Client)> {
self.virtual_screens self.virtual_screens
.get_current() .get_current()
.aux .aux
@ -347,7 +404,7 @@ impl ClientState {
{ {
match self.get(key) { match self.get(key) {
ClientEntry::Floating(c) => { 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) self.is_client_visible(&transient_for)
} else { } else {
true true
@ -420,6 +477,49 @@ impl ClientState {
self.arrange_virtual_screen(); 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 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. returns false. If this function returns `true` you have to call `arrange_clients` after.
@ -437,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 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). automatically since xlib has to move and resize all windows anyways).
@ -444,6 +561,16 @@ impl ClientState {
pub fn toggle_floating<K>(&mut self, key: &K) pub fn toggle_floating<K>(&mut self, key: &K)
where where
K: ClientKey, K: ClientKey,
{
// 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 key = key.key();
let client = self.clients.remove(&key); let client = self.clients.remove(&key);
@ -456,37 +583,49 @@ impl ClientState {
} }
(None, Some(floating_client)) => { (None, Some(floating_client)) => {
// transient clients cannot be tiled // transient clients cannot be tiled
match floating_client.is_transient() { // only normal windows can be tiled
true => { match floating_client.window_type {
self.floating_clients WindowType::Normal => {
.insert(key, floating_client);
}
false => {
self.clients.insert(key, floating_client); self.clients.insert(key, floating_client);
self.virtual_screens self.virtual_screens.get_mut_current().insert(&key);
.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 // we added or removed a client from the tiling so the layout changed, rearrange
self.arrange_virtual_screen(); 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) fn remove_from_virtual_screens<K>(&mut self, key: &K)
where where
K: ClientKey, K: ClientKey,
{ {
if self.contains(key) { if self.contains(key) {
if let Some(vs) = if let Some(vs) = self.get_mut_virtualscreen_for_client(key) {
self.get_mut_virtualscreen_for_client(key)
{
vs.remove(key); vs.remove(key);
// we removed a client so the layout changed, rearrange // we removed a client so the layout changed, rearrange
@ -495,10 +634,7 @@ impl ClientState {
} }
} }
fn get_virtualscreen_for_client<K>( fn get_virtualscreen_for_client<K>(&self, key: &K) -> Option<&VirtualScreen>
&self,
key: &K,
) -> Option<&VirtualScreen>
where where
K: ClientKey, K: ClientKey,
{ {
@ -527,10 +663,7 @@ impl ClientState {
}) })
} }
pub fn get_stack_for_client<K>( pub fn get_stack_for_client<K>(&self, key: &K) -> Option<&Vec<u64>>
&self,
key: &K,
) -> Option<&Vec<u64>>
where where
K: ClientKey, K: ClientKey,
{ {
@ -560,7 +693,9 @@ impl ClientState {
{ {
let (new, old) = self.focus_client_inner(key); 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) (new, old)
} }
@ -650,56 +785,89 @@ impl ClientState {
*/ */
pub fn arrange_virtual_screen(&mut self) { pub fn arrange_virtual_screen(&mut self) {
let gap = self.gap; 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 // should be fine to unwrap since we will always have at least 1 virtual screen
let vs = self.virtual_screens.get_mut_current(); let vs = self.virtual_screens.get_mut_current();
// if aux is empty -> width : width / 2 // if aux is empty -> width : width / 2
let (master_width, aux_width) = { let vs_width = width - gap * 2;
let effective_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 1.0
} else { } else {
self.master_size / 2.0 self.master_size / 2.0
}; };
let master_width = let width = (vs_width as f32 * factor) as i32;
(effective_width as f32 * master_size) as i32;
let aux_width = effective_width - master_width;
(master_width, aux_width)
};
// make sure we dont devide by 0 // make sure we dont devide by 0
// height is max height / number of clients in the stack // height is max height / number of clients in the stack
let master_height = (height - gap * 2) let height = match vs.master.len() as i32 {
/ match NonZeroI32::new(vs.master.len() as i32) { 0 => 1,
Some(i) => i.get(), n => (height - gap * 2) / n,
None => 1,
}; };
// height is max height / number of clients in the stack Size::new(width, height)
let aux_height = (height - gap * 2)
/ match NonZeroI32::new(vs.aux.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,
};
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 // Master
for (i, key) in vs.master.iter().enumerate() { for (i, key) in vs.master.iter().enumerate() {
let size = ( if let Some(client) = self.clients.get_mut(key) {
master_width - gap * 2 - self.border_size * 2, let (size, position) = calculate_window_dimensions(
master_height - gap * 2 - self.border_size * 2, self.screen_size.into(),
master_window_size,
master_position,
client.is_fullscreen(),
i as i32,
gap,
self.border_size,
); );
let position =
(gap * 2, master_height * i as i32 + gap * 2);
if let Some(client) = self.clients.get_mut(key) {
*client = Client { *client = Client {
size, size: size.into(),
position, position,
..*client ..*client
}; };
@ -708,19 +876,19 @@ impl ClientState {
// Aux // Aux
for (i, key) in vs.aux.iter().enumerate() { 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) { 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 { *client = Client {
size, size: size.into(),
position, position,
..*client ..*client
}; };
@ -752,8 +920,7 @@ impl VirtualScreen {
where where
K: ClientKey, K: ClientKey,
{ {
self.master.contains(&key.key()) self.master.contains(&key.key()) || self.aux.contains(&key.key())
|| self.aux.contains(&key.key())
} }
fn is_in_master<K>(&self, key: &K) -> bool fn is_in_master<K>(&self, key: &K) -> bool
@ -799,11 +966,8 @@ impl VirtualScreen {
self.aux.extend(self.master.drain(index..=index)); self.aux.extend(self.master.drain(index..=index));
} }
None => { None => {
let index = self let index =
.aux self.aux.iter().position(|&k| k == key.key()).unwrap();
.iter()
.position(|&k| k == key.key())
.unwrap();
self.master.extend(self.aux.drain(index..=index)); self.master.extend(self.aux.drain(index..=index));
} }
} }
@ -850,9 +1014,7 @@ impl VirtualScreenStore {
self.screens.iter() self.screens.iter()
} }
fn iter_mut( fn iter_mut(&mut self) -> impl Iterator<Item = &mut VirtualScreen> {
&mut self,
) -> impl Iterator<Item = &mut VirtualScreen> {
self.screens.iter_mut() self.screens.iter_mut()
} }
@ -889,7 +1051,7 @@ impl VirtualScreenStore {
fn go_to_nth(&mut self, n: usize) -> usize { fn go_to_nth(&mut self, n: usize) -> usize {
self.last_idx = Some(self.current_idx); 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 self.current_idx
} }
@ -899,9 +1061,7 @@ impl<T> Into<Option<T>> for ClientEntry<T> {
fn into(self) -> Option<T> { fn into(self) -> Option<T> {
match self { match self {
Self::Vacant => None, Self::Vacant => None,
Self::Tiled(client) | Self::Floating(client) => { Self::Tiled(client) | Self::Floating(client) => Some(client),
Some(client)
}
} }
} }
} }
@ -942,3 +1102,25 @@ impl<T> ClientEntry<T> {
!self.is_vacant() !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,
}
}
}

View file

@ -1,409 +0,0 @@
#![allow(dead_code)]
use std::{borrow::Borrow, hash::Hash};
/// Client structure.
#[derive(Clone, Debug)]
pub struct Client<T> {
window_id: T,
size: (i32, i32),
position: (i32, i32),
transient_for: Option<T>,
}
#[derive(Debug, PartialEq, Eq)]
pub enum Entry<T> {
Tiled(T),
Floating(T),
Transient(T),
Fullscreen(T),
Vacant,
}
type ClientSet<T> = indexmap::IndexMap<T, Client<T>>;
//type ClientSet<T> = std::collections::HashMap<T, Client<T>>;
pub struct ClientStore<T>
where
T: Hash + Eq,
{
tiled_clients: ClientSet<T>,
floating_clients: ClientSet<T>,
transient_clients: ClientSet<T>,
fullscreen_clients: ClientSet<T>,
}
impl<T> PartialEq for Client<T>
where
T: PartialEq,
{
fn eq(&self, other: &Self) -> bool {
self.window_id == other.window_id
}
}
impl<T> Eq for Client<T> where T: Eq {}
impl<T> PartialOrd for Client<T>
where
T: PartialOrd,
{
fn partial_cmp(
&self,
other: &Self,
) -> Option<std::cmp::Ordering> {
self.window_id.partial_cmp(&other.window_id)
}
}
impl<T> Ord for Client<T>
where
T: Ord,
{
fn cmp(&self, other: &Self) -> std::cmp::Ordering {
self.window_id.cmp(&other.window_id)
}
}
impl<T> Hash for Client<T>
where
T: Hash,
{
fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
self.window_id.hash(state)
}
}
impl<T> Borrow<T> for Client<T> {
fn borrow(&self) -> &T {
&self.window_id
}
}
impl<T> Client<T> {
pub fn new(window_id: T) -> Self {
Self {
window_id,
size: (100, 100),
position: (0, 0),
transient_for: None,
}
}
pub fn window_id_ref(&self) -> &T {
&self.window_id
}
/// Get a mutable reference to the client's size.
pub fn size_mut(&mut self) -> &mut (i32, i32) {
&mut self.size
}
/// Get a mutable reference to the client's position.
pub fn position_mut(&mut self) -> &mut (i32, i32) {
&mut self.position
}
/// Get a reference to the client's size.
pub fn size(&self) -> &(i32, i32) {
&self.size
}
/// Get a reference to the client's position.
pub fn position(&self) -> &(i32, i32) {
&self.position
}
}
impl<T> Client<T>
where
T: Copy,
{
pub fn window_id(&self) -> T {
self.window_id
}
}
impl<T> From<Entry<T>> for Option<T> {
fn from(entry: Entry<T>) -> Self {
match entry {
Entry::Floating(c)
| Entry::Tiled(c)
| Entry::Fullscreen(c)
| Entry::Transient(c) => Some(c),
_ => None,
}
}
}
impl<'a, T> From<&'a Entry<T>> for Option<&'a T> {
fn from(entry: &'a Entry<T>) -> Self {
match entry {
Entry::Floating(c)
| Entry::Tiled(c)
| Entry::Fullscreen(c)
| Entry::Transient(c) => Some(c),
_ => None,
}
}
}
impl<'a, T> From<&'a mut Entry<T>> for Option<&'a mut T> {
fn from(entry: &'a mut Entry<T>) -> Self {
match entry {
Entry::Floating(c)
| Entry::Tiled(c)
| Entry::Fullscreen(c)
| Entry::Transient(c) => Some(c),
_ => None,
}
}
}
impl<T> From<Option<Entry<T>>> for Entry<T> {
fn from(opt: Option<Entry<T>>) -> Self {
match opt {
Some(entry) => entry,
None => Entry::Vacant,
}
}
}
impl<T> Entry<T> {
pub fn unwrap(self) -> T {
Option::<T>::from(self).unwrap()
}
pub fn unwrap_ref(&self) -> &T {
Option::<&T>::from(self).unwrap()
}
pub fn unwrap_mut(&mut self) -> &mut T {
Option::<&mut T>::from(self).unwrap()
}
pub fn is_floating(&self) -> bool {
match self {
Self::Floating(_) => true,
_ => false,
}
}
pub fn is_tiled(&self) -> bool {
match self {
Self::Tiled(_) => true,
_ => false,
}
}
pub fn is_transient(&self) -> bool {
match self {
Self::Transient(_) => true,
_ => false,
}
}
pub fn is_fullscreen(&self) -> bool {
match self {
Self::Fullscreen(_) => true,
_ => false,
}
}
}
impl<T> Default for ClientStore<T>
where
T: Hash + Eq,
{
fn default() -> Self {
Self {
tiled_clients: Default::default(),
floating_clients: Default::default(),
transient_clients: Default::default(),
fullscreen_clients: Default::default(),
}
}
}
impl<T> ClientStore<T>
where
T: Hash + Eq + Copy,
{
pub fn new() -> Self {
Self::default()
}
pub fn insert(
&mut self,
entry: Entry<Client<T>>,
) -> Entry<&Client<T>> {
if let Some(key) =
Option::<&Client<T>>::from(&entry).map(|c| c.window_id())
{
match entry {
Entry::Floating(client) => {
self.floating_clients.insert(key, client);
}
Entry::Tiled(client) => {
self.tiled_clients.insert(key, client);
}
Entry::Transient(client) => {
self.transient_clients.insert(key, client);
}
Entry::Fullscreen(client) => {
self.fullscreen_clients.insert(key, client);
}
_ => unreachable!(),
}
self.get(&key).into()
} else {
Entry::Vacant
}
}
pub fn remove(&mut self, key: &T) -> Entry<Client<T>> {
if let Some(client) = self.tiled_clients.remove(key) {
Entry::Tiled(client)
} else if let Some(client) = self.floating_clients.remove(key)
{
Entry::Floating(client)
} else if let Some(client) =
self.transient_clients.remove(key)
{
Entry::Transient(client)
} else if let Some(client) =
self.fullscreen_clients.remove(key)
{
Entry::Fullscreen(client)
} else {
Entry::Vacant
}
}
pub fn get(&self, key: &T) -> Entry<&Client<T>> {
if let Some(client) = self.tiled_clients.get(key) {
Entry::Tiled(client)
} else if let Some(client) = self.floating_clients.get(key) {
Entry::Floating(client)
} else if let Some(client) = self.transient_clients.get(key) {
Entry::Transient(client)
} else if let Some(client) = self.fullscreen_clients.get(key)
{
Entry::Fullscreen(client)
} else {
Entry::Vacant
}
}
pub fn get_mut(&mut self, key: &T) -> Entry<&mut Client<T>> {
if let Some(client) = self.tiled_clients.get_mut(key) {
Entry::Tiled(client)
} else if let Some(client) =
self.floating_clients.get_mut(key)
{
Entry::Floating(client)
} else if let Some(client) =
self.transient_clients.get_mut(key)
{
Entry::Transient(client)
} else if let Some(client) =
self.fullscreen_clients.get_mut(key)
{
Entry::Fullscreen(client)
} else {
Entry::Vacant
}
}
pub fn contains(&self, key: &T) -> bool {
self.tiled_clients.contains_key(key)
|| self.floating_clients.contains_key(key)
|| self.transient_clients.contains_key(key)
|| self.fullscreen_clients.contains_key(key)
}
pub fn iter_tiled(
&self,
) -> impl Iterator<Item = (&T, &Client<T>)> {
self.tiled_clients.iter()
}
pub fn iter_mut_tiled(
&mut self,
) -> impl Iterator<Item = (&T, &mut Client<T>)> {
self.tiled_clients.iter_mut()
}
pub fn iter_floating(
&self,
) -> impl Iterator<Item = (&T, &Client<T>)> {
self.floating_clients.iter()
}
pub fn iter_mut_floating(
&mut self,
) -> impl Iterator<Item = (&T, &mut Client<T>)> {
self.floating_clients.iter_mut()
}
pub fn iter_transient(
&self,
) -> impl Iterator<Item = (&T, &Client<T>)> {
self.transient_clients.iter()
}
pub fn iter_mut_transient(
&mut self,
) -> impl Iterator<Item = (&T, &mut Client<T>)> {
self.transient_clients.iter_mut()
}
pub fn iter_fullscreen(
&self,
) -> impl Iterator<Item = (&T, &Client<T>)> {
self.fullscreen_clients.iter()
}
pub fn iter_mut_fullscreen(
&mut self,
) -> impl Iterator<Item = (&T, &mut Client<T>)> {
self.fullscreen_clients.iter_mut()
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn clientstore_insert_contain() {
let client = Client::new(1u64);
let mut client_store = ClientStore::new();
client_store.insert(Entry::Tiled(client.clone()));
assert!(client_store.contains(client.borrow()));
assert!(client_store.contains(&1));
let client2 = Client::new(3u64);
client_store.insert(Entry::Floating(client2.clone()));
assert!(client_store.contains(&client.borrow()));
assert!(client_store.contains(&1));
assert!(client_store.contains(&client2.borrow()));
assert!(client_store.contains(&3));
assert_eq!(
Entry::Tiled(client.clone()),
client_store.remove(&client.borrow())
);
assert_eq!(
Entry::Vacant,
client_store.remove(&client.borrow())
);
assert_eq!(Entry::Vacant, client_store.remove(&1));
assert!(client_store.contains(&client2.borrow()));
}
}

26
src/lib.rs Normal file
View 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),
}
}

View file

@ -1,3 +1,5 @@
use std::io::Read;
use log::{debug, error, info, trace, warn}; use log::{debug, error, info, trace, warn};
use log4rs::{ use log4rs::{
append::{console::ConsoleAppender, file::FileAppender}, append::{console::ConsoleAppender, file::FileAppender},
@ -5,45 +7,31 @@ use log4rs::{
encode::pattern::PatternEncoder, encode::pattern::PatternEncoder,
Config, Config,
}; };
use state::WMConfig;
#[macro_use] use wm::state::WMConfig;
extern crate num_derive;
mod backends;
mod client_logic;
mod clients;
mod clients2;
mod state;
mod util;
mod xlib;
fn init_logger() { fn init_logger() {
let encoder = Box::new(PatternEncoder::new( let encoder = Box::new(PatternEncoder::new(
"{d(%Y-%m-%d %H:%M:%S %Z)(utc)} │ {({M}::{f}:{L}):>25} │ {h({l:>5})} │ {m}{n}", "{d(%Y-%m-%d %H:%M:%S %Z)(utc)} │ {({M}::{f}:{L}):>25} │ {h({l:>5})} │ {m}{n}",
)); ));
let stdout = let stdout = ConsoleAppender::builder().encoder(encoder.clone()).build();
ConsoleAppender::builder().encoder(encoder.clone()).build();
let home = let home = dirs::home_dir().expect("Failed to get $HOME env var.");
dirs::home_dir().expect("Failed to get $HOME env var.");
let _logfile = FileAppender::builder() let _logfile = FileAppender::builder()
.encoder(encoder) .encoder(encoder)
.build(home.join(".local/portlights.log")) .build(home.join(".local/nirgendwm.log"))
.unwrap(); .unwrap();
let config = Config::builder() let config = Config::builder()
.appender( .appender(Appender::builder().build("stdout", Box::new(stdout)))
Appender::builder().build("stdout", Box::new(stdout)),
)
//.appender(Appender::builder().build("logfile", Box::new(logfile))) //.appender(Appender::builder().build("logfile", Box::new(logfile)))
.build( .build(
Root::builder() Root::builder()
.appender("stdout") .appender("stdout")
//.appender("logfile") //.appender("logfile")
.build(log::LevelFilter::Info), .build(log::LevelFilter::Debug),
) )
.unwrap(); .unwrap();
@ -55,7 +43,23 @@ fn main() {
log_prologue(); 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() { fn log_prologue() {
@ -74,7 +78,6 @@ fn log_prologue() {
#[test] #[test]
fn test_logger() { fn test_logger() {
init_logger(); init_logger();
// asdf
log_prologue(); log_prologue();
} }

File diff suppressed because it is too large Load diff

View file

@ -22,3 +22,225 @@ impl Hasher for IdentityHasher {
} }
pub type BuildIdentityHasher = BuildHasherDefault<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)
}
}
}

View file

@ -1,841 +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);
}
}

Binary file not shown.

Before

Width:  |  Height:  |  Size: 210 KiB

View file

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