Compare commits
5 commits
feature_cu
...
refactor
Author | SHA1 | Date | |
---|---|---|---|
|
adc71e517f | ||
|
1b2e2d848c | ||
|
98459d620c | ||
|
507bc75ccc | ||
|
db94c82185 |
1
.gitignore
vendored
1
.gitignore
vendored
|
@ -3,4 +3,3 @@
|
||||||
/Cargo.lock
|
/Cargo.lock
|
||||||
/.cargo/
|
/.cargo/
|
||||||
/wmlog
|
/wmlog
|
||||||
/xinitrc.tmp
|
|
||||||
|
|
29
Cargo.toml
29
Cargo.toml
|
@ -1,27 +1,18 @@
|
||||||
[package]
|
[package]
|
||||||
name = "wm"
|
name = "wm"
|
||||||
version = "0.3.0"
|
version = "0.2.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.19", features = ["xlib", "xft"] }
|
x11 = {version = "2.18.2", features = ["xlib"] }
|
||||||
log = "0.4"
|
x11rb = "0.8.1"
|
||||||
simple_logger = "2.0"
|
num-derive = "0.3.3"
|
||||||
dirs = "3.0.0"
|
num-traits = "0.2.14"
|
||||||
log4rs = "1.0"
|
log = "0.4.13"
|
||||||
indexmap = "1.0"
|
simple_logger = "1.11.0"
|
||||||
thiserror = "1.0"
|
dirs = "3.0.2"
|
||||||
bitflags = "1.3"
|
log4rs = "1.0.0"
|
||||||
derivative = "2.2.0"
|
indexmap = "1.6.2"
|
||||||
serde = { version = "1.0", features = ["derive"] }
|
|
||||||
toml = "0.5"
|
|
||||||
num-traits = "0.2"
|
|
||||||
strum = {version = "0.24.0", features = ["derive"]}
|
|
||||||
bytemuck = "1.0"
|
|
|
@ -1,5 +1,4 @@
|
||||||
# No WM
|
# Unnamed 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/).
|
||||||
|
|
||||||
|
@ -12,5 +11,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`.
|
||||||
|
|
||||||
No WM also has optional gaps :^)
|
Unnamed WM also has optional gaps :^)
|
||||||

|

|
||||||
|
|
BIN
abstract1.jpg
Normal file
BIN
abstract1.jpg
Normal file
Binary file not shown.
After Width: | Height: | Size: 853 KiB |
|
@ -1,8 +0,0 @@
|
||||||
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", []]
|
|
|
@ -1,4 +0,0 @@
|
||||||
num_virtualscreens = 10
|
|
||||||
mod_key = "Super"
|
|
||||||
gap = 5
|
|
||||||
kill_clients_on_exit = false
|
|
12
runner.sh
12
runner.sh
|
@ -1,12 +0,0 @@
|
||||||
#!/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
|
|
|
@ -1,2 +1,2 @@
|
||||||
#wrap_comments = true
|
wrap_comments = true
|
||||||
max_width = 80
|
max_width = 70
|
|
@ -1,12 +1,8 @@
|
||||||
#![allow(dead_code)]
|
#![allow(dead_code)]
|
||||||
|
|
||||||
#[derive(Debug, Hash, Ord, PartialOrd, PartialEq, Eq, Clone, Copy)]
|
#[derive(
|
||||||
pub enum KeyOrButton {
|
Debug, Hash, Ord, PartialOrd, PartialEq, Eq, Clone, Copy,
|
||||||
Key(VirtualKeyCode),
|
)]
|
||||||
Button(MouseButton),
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, Hash, Ord, PartialOrd, PartialEq, Eq, Clone, Copy)]
|
|
||||||
pub enum MouseButton {
|
pub enum MouseButton {
|
||||||
Left,
|
Left,
|
||||||
Middle,
|
Middle,
|
||||||
|
@ -20,19 +16,21 @@ pub enum MouseButton {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// from winit
|
/// from winit
|
||||||
#[derive(Debug, Hash, Ord, PartialOrd, PartialEq, Eq, Clone, Copy)]
|
#[derive(
|
||||||
|
Debug, Hash, Ord, PartialOrd, PartialEq, Eq, Clone, Copy,
|
||||||
|
)]
|
||||||
#[repr(u32)]
|
#[repr(u32)]
|
||||||
pub enum VirtualKeyCode {
|
pub enum VirtualKeyCode {
|
||||||
One,
|
Key1,
|
||||||
Two,
|
Key2,
|
||||||
Three,
|
Key3,
|
||||||
Four,
|
Key4,
|
||||||
Five,
|
Key5,
|
||||||
Six,
|
Key6,
|
||||||
Seven,
|
Key7,
|
||||||
Eight,
|
Key8,
|
||||||
Nine,
|
Key9,
|
||||||
Zero,
|
Key0,
|
||||||
A,
|
A,
|
||||||
B,
|
B,
|
||||||
C,
|
C,
|
||||||
|
@ -89,7 +87,7 @@ pub enum VirtualKeyCode {
|
||||||
F24,
|
F24,
|
||||||
|
|
||||||
/// Print Screen/SysRq.
|
/// Print Screen/SysRq.
|
||||||
Print,
|
Snapshot,
|
||||||
/// Scroll Lock.
|
/// Scroll Lock.
|
||||||
Scroll,
|
Scroll,
|
||||||
/// Pause/Break key, next to Scroll lock.
|
/// Pause/Break key, next to Scroll lock.
|
||||||
|
|
|
@ -1,28 +1,6 @@
|
||||||
pub mod keycodes;
|
mod keycodes;
|
||||||
pub mod traits;
|
mod window_event;
|
||||||
pub mod window_event;
|
mod xcb;
|
||||||
pub mod xlib;
|
mod xlib;
|
||||||
|
|
||||||
pub use traits::*;
|
pub trait WindowServerBackend {}
|
||||||
|
|
||||||
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,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
|
@ -1,61 +0,0 @@
|
||||||
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);
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,37 +1,64 @@
|
||||||
#![allow(dead_code)]
|
#![allow(dead_code)]
|
||||||
|
use x11::xlib::Window;
|
||||||
|
|
||||||
use super::{
|
use super::keycodes::{MouseButton, VirtualKeyCode};
|
||||||
keycodes::{KeyOrButton, MouseButton, VirtualKeyCode},
|
|
||||||
structs::WindowType,
|
|
||||||
};
|
|
||||||
use crate::util::{Point, Size};
|
|
||||||
use bitflags::bitflags;
|
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug)]
|
||||||
pub enum WindowEvent<Window> {
|
pub enum WindowEvent {
|
||||||
KeyEvent(KeyEvent<Window>),
|
KeyEvent {
|
||||||
ButtonEvent(ButtonEvent<Window>),
|
window: Window,
|
||||||
MotionEvent(MotionEvent<Window>),
|
event: KeyEvent,
|
||||||
MapRequestEvent(MapEvent<Window>),
|
},
|
||||||
MapEvent(MapEvent<Window>),
|
ButtonEvent {
|
||||||
UnmapEvent(UnmapEvent<Window>),
|
window: Window,
|
||||||
CreateEvent(CreateEvent<Window>),
|
event: ButtonEvent,
|
||||||
DestroyEvent(DestroyEvent<Window>),
|
},
|
||||||
EnterEvent(EnterEvent<Window>),
|
MotionEvent {
|
||||||
ConfigureEvent(ConfigureEvent<Window>),
|
window: Window,
|
||||||
FullscreenEvent(FullscreenEvent<Window>), //1 { window: Window, event: 1 },
|
event: MotionEvent,
|
||||||
WindowNameEvent(WindowNameEvent<Window>),
|
},
|
||||||
WindowTypeChangedEvent(WindowTypeChangedEvent<Window>),
|
MapRequestEvent {
|
||||||
|
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, PartialEq, Eq, Copy, Clone)]
|
#[derive(Debug)]
|
||||||
pub enum KeyState {
|
pub enum KeyState {
|
||||||
Pressed,
|
Pressed,
|
||||||
Released,
|
Released,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(
|
#[derive(
|
||||||
Debug, PartialEq, Eq, PartialOrd, Ord, Hash, Clone, Copy, serde::Deserialize,
|
Debug, PartialEq, Eq, PartialOrd, Ord, Hash, Clone, Copy,
|
||||||
)]
|
)]
|
||||||
#[repr(u8)]
|
#[repr(u8)]
|
||||||
pub enum ModifierKey {
|
pub enum ModifierKey {
|
||||||
|
@ -45,89 +72,43 @@ pub enum ModifierKey {
|
||||||
NumLock,
|
NumLock,
|
||||||
}
|
}
|
||||||
|
|
||||||
bitflags! {
|
#[derive(Default, Debug, Clone)]
|
||||||
pub struct ModifierState: u32 {
|
pub struct ModifierState {
|
||||||
const SHIFT = 0x01;
|
modifiers: std::collections::HashSet<ModifierKey>,
|
||||||
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 eq_ignore_lock(&self, rhs: &Self) -> bool {
|
pub fn new() -> Self {
|
||||||
let mask = Self::IGNORE_LOCK;
|
Self::default()
|
||||||
*self & mask == *rhs & mask
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn with_mod(mut self, modifier: ModifierKey) -> Self {
|
pub fn set_modifier(&mut self, modifier: ModifierKey) {
|
||||||
self.insert_mod(modifier);
|
self.modifiers.insert(modifier);
|
||||||
self
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn unset_mod(&mut self, modifier: ModifierKey) {
|
pub fn unset_modifier(&mut self, modifier: ModifierKey) {
|
||||||
self.set_mod(modifier, false);
|
self.modifiers.remove(&modifier);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn set_mod(&mut self, modifier: ModifierKey, state: bool) {
|
pub fn get_modifier(&mut self, modifier: ModifierKey) -> bool {
|
||||||
self.set(
|
self.modifiers.contains(&modifier)
|
||||||
match modifier {
|
|
||||||
ModifierKey::Shift => Self::SHIFT,
|
|
||||||
ModifierKey::ShiftLock => Self::SHIFT_LOCK,
|
|
||||||
ModifierKey::Control => Self::CONTROL,
|
|
||||||
ModifierKey::Alt => Self::ALT,
|
|
||||||
ModifierKey::AltGr => Self::ALT_GR,
|
|
||||||
ModifierKey::Super => Self::SUPER,
|
|
||||||
ModifierKey::NumLock => Self::NUM_LOCK,
|
|
||||||
},
|
|
||||||
state,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn insert_mod(&mut self, modifier: ModifierKey) {
|
|
||||||
self.set_mod(modifier, true);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Into<u8> for ModifierKey {
|
#[derive(Debug)]
|
||||||
fn into(self) -> u8 {
|
pub struct KeyEvent {
|
||||||
self as u8
|
state: KeyState,
|
||||||
}
|
keycode: VirtualKeyCode,
|
||||||
|
modifierstate: ModifierState,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
impl KeyEvent {
|
||||||
pub struct KeyEvent<Window> {
|
|
||||||
pub window: Window,
|
|
||||||
pub state: KeyState,
|
|
||||||
pub keycode: VirtualKeyCode,
|
|
||||||
pub modifierstate: ModifierState,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<Window> KeyEvent<Window> {
|
|
||||||
pub fn new(
|
pub fn new(
|
||||||
window: Window,
|
|
||||||
state: KeyState,
|
state: KeyState,
|
||||||
keycode: VirtualKeyCode,
|
keycode: VirtualKeyCode,
|
||||||
modifierstate: ModifierState,
|
modifierstate: ModifierState,
|
||||||
) -> Self {
|
) -> Self {
|
||||||
Self {
|
Self {
|
||||||
window,
|
|
||||||
state,
|
state,
|
||||||
keycode,
|
keycode,
|
||||||
modifierstate,
|
modifierstate,
|
||||||
|
@ -135,80 +116,87 @@ impl<Window> KeyEvent<Window> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug)]
|
||||||
pub struct ButtonEvent<Window> {
|
pub struct ButtonEvent {
|
||||||
pub window: Window,
|
state: KeyState,
|
||||||
pub state: KeyState,
|
keycode: MouseButton,
|
||||||
pub keycode: MouseButton,
|
modifierstate: ModifierState,
|
||||||
pub cursor_position: Point<i32>,
|
}
|
||||||
pub modifierstate: ModifierState,
|
|
||||||
}
|
impl ButtonEvent {
|
||||||
|
pub fn new(
|
||||||
impl<Window> ButtonEvent<Window> {
|
|
||||||
pub fn new(
|
|
||||||
window: Window,
|
|
||||||
state: KeyState,
|
state: KeyState,
|
||||||
keycode: MouseButton,
|
keycode: MouseButton,
|
||||||
cursor_position: Point<i32>,
|
|
||||||
modifierstate: ModifierState,
|
modifierstate: ModifierState,
|
||||||
) -> Self {
|
) -> Self {
|
||||||
Self {
|
Self {
|
||||||
window,
|
|
||||||
state,
|
state,
|
||||||
keycode,
|
keycode,
|
||||||
cursor_position,
|
|
||||||
modifierstate,
|
modifierstate,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug)]
|
||||||
pub struct MotionEvent<Window> {
|
pub struct MotionEvent {
|
||||||
pub position: Point<i32>,
|
position: [i32; 2],
|
||||||
pub window: Window,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<Window> MotionEvent<Window> {
|
impl MotionEvent {
|
||||||
pub fn new(position: Point<i32>, window: Window) -> Self {
|
pub fn new(position: [i32; 2]) -> Self {
|
||||||
Self { position, window }
|
Self { position }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug)]
|
||||||
pub struct MapEvent<Window> {
|
pub struct MapEvent {
|
||||||
pub window: Window,
|
window: Window,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
impl MapEvent {
|
||||||
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 {
|
pub fn new(window: Window) -> Self {
|
||||||
Self { window }
|
Self { window }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug)]
|
||||||
pub struct CreateEvent<Window> {
|
pub struct UnmapEvent {
|
||||||
pub window: Window,
|
window: Window,
|
||||||
pub position: Point<i32>,
|
|
||||||
pub size: Size<i32>,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<Window> CreateEvent<Window> {
|
impl UnmapEvent {
|
||||||
pub fn new(window: Window, position: Point<i32>, size: Size<i32>) -> Self {
|
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 {
|
Self {
|
||||||
window,
|
window,
|
||||||
position,
|
position,
|
||||||
|
@ -217,15 +205,19 @@ impl<Window> CreateEvent<Window> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug)]
|
||||||
pub struct ConfigureEvent<Window> {
|
pub struct ConfigureEvent {
|
||||||
pub window: Window,
|
pub window: Window,
|
||||||
pub position: Point<i32>,
|
pub position: [i32; 2],
|
||||||
pub size: Size<i32>,
|
pub size: [i32; 2],
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<Window> ConfigureEvent<Window> {
|
impl ConfigureEvent {
|
||||||
pub fn new(window: Window, position: Point<i32>, size: Size<i32>) -> Self {
|
pub fn new(
|
||||||
|
window: Window,
|
||||||
|
position: [i32; 2],
|
||||||
|
size: [i32; 2],
|
||||||
|
) -> Self {
|
||||||
Self {
|
Self {
|
||||||
window,
|
window,
|
||||||
position,
|
position,
|
||||||
|
@ -234,153 +226,13 @@ impl<Window> ConfigureEvent<Window> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug)]
|
||||||
pub enum FullscreenState {
|
pub struct FullscreenEvent {
|
||||||
On,
|
new_fullscreen: bool,
|
||||||
Off,
|
|
||||||
Toggle,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl From<bool> for FullscreenState {
|
impl FullscreenEvent {
|
||||||
fn from(value: bool) -> Self {
|
pub fn new(new_fullscreen: bool) -> Self {
|
||||||
match value {
|
Self { new_fullscreen }
|
||||||
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,
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
402
src/backends/xcb.rs
Normal file
402
src/backends/xcb.rs
Normal file
|
@ -0,0 +1,402 @@
|
||||||
|
//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)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
1233
src/backends/xlib.rs.old
Normal file
1233
src/backends/xlib.rs.old
Normal file
File diff suppressed because it is too large
Load diff
|
@ -1,46 +0,0 @@
|
||||||
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.",
|
|
||||||
))
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,7 +1,7 @@
|
||||||
#![allow(unreachable_patterns)]
|
use crate::backends::{
|
||||||
use std::{borrow::Borrow, ops::Deref};
|
keycodes::{MouseButton, VirtualKeyCode},
|
||||||
|
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,52 +34,10 @@ 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(keysym: u32) -> Option<VirtualKeyCode> {
|
pub fn keysym_to_virtual_keycode(
|
||||||
|
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,
|
||||||
|
@ -120,11 +78,11 @@ pub fn keysym_to_virtual_keycode(keysym: u32) -> Option<VirtualKeyCode> {
|
||||||
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::LWin,
|
//x11::keysym::XK_Win_L => VirtualKeyCode::Win_l,
|
||||||
x11::keysym::XK_Win_R => VirtualKeyCode::RWin,
|
//x11::keysym::XK_Win_R => VirtualKeyCode::Win_r,
|
||||||
//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,
|
||||||
|
@ -266,16 +224,16 @@ pub fn keysym_to_virtual_keycode(keysym: u32) -> Option<VirtualKeyCode> {
|
||||||
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::Zero,
|
x11::keysym::XK_0 => VirtualKeyCode::Key0,
|
||||||
x11::keysym::XK_1 => VirtualKeyCode::One,
|
x11::keysym::XK_1 => VirtualKeyCode::Key1,
|
||||||
x11::keysym::XK_2 => VirtualKeyCode::Two,
|
x11::keysym::XK_2 => VirtualKeyCode::Key2,
|
||||||
x11::keysym::XK_3 => VirtualKeyCode::Three,
|
x11::keysym::XK_3 => VirtualKeyCode::Key3,
|
||||||
x11::keysym::XK_4 => VirtualKeyCode::Four,
|
x11::keysym::XK_4 => VirtualKeyCode::Key4,
|
||||||
x11::keysym::XK_5 => VirtualKeyCode::Five,
|
x11::keysym::XK_5 => VirtualKeyCode::Key5,
|
||||||
x11::keysym::XK_6 => VirtualKeyCode::Six,
|
x11::keysym::XK_6 => VirtualKeyCode::Key6,
|
||||||
x11::keysym::XK_7 => VirtualKeyCode::Seven,
|
x11::keysym::XK_7 => VirtualKeyCode::Key7,
|
||||||
x11::keysym::XK_8 => VirtualKeyCode::Eight,
|
x11::keysym::XK_8 => VirtualKeyCode::Key8,
|
||||||
x11::keysym::XK_9 => VirtualKeyCode::Nine,
|
x11::keysym::XK_9 => VirtualKeyCode::Key9,
|
||||||
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,
|
||||||
|
@ -1076,7 +1034,9 @@ pub fn keysym_to_virtual_keycode(keysym: u32) -> Option<VirtualKeyCode> {
|
||||||
//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 => VirtualKeyCode::NavigateForward,
|
x11::keysym::XF86XK_Forward => {
|
||||||
|
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,
|
||||||
|
@ -1084,7 +1044,9 @@ pub fn keysym_to_virtual_keycode(keysym: u32) -> Option<VirtualKeyCode> {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn virtual_keycode_to_keysym(keycode: VirtualKeyCode) -> Option<u32> {
|
pub fn virtual_keycode_to_keysym(
|
||||||
|
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,
|
||||||
|
@ -1125,11 +1087,11 @@ pub fn virtual_keycode_to_keysym(keycode: VirtualKeyCode) -> Option<u32> {
|
||||||
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::LWin => x11::keysym::XK_Win_L,
|
//VirtualKeyCode::Win_l => x11::keysym::XK_Win_L,
|
||||||
VirtualKeyCode::RWin => x11::keysym::XK_Win_R,
|
//VirtualKeyCode::Win_r => 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,
|
||||||
|
@ -1271,16 +1233,16 @@ pub fn virtual_keycode_to_keysym(keycode: VirtualKeyCode) -> Option<u32> {
|
||||||
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::Zero => x11::keysym::XK_0,
|
VirtualKeyCode::Key0 => x11::keysym::XK_0,
|
||||||
VirtualKeyCode::One => x11::keysym::XK_1,
|
VirtualKeyCode::Key1 => x11::keysym::XK_1,
|
||||||
VirtualKeyCode::Two => x11::keysym::XK_2,
|
VirtualKeyCode::Key2 => x11::keysym::XK_2,
|
||||||
VirtualKeyCode::Three => x11::keysym::XK_3,
|
VirtualKeyCode::Key3 => x11::keysym::XK_3,
|
||||||
VirtualKeyCode::Four => x11::keysym::XK_4,
|
VirtualKeyCode::Key4 => x11::keysym::XK_4,
|
||||||
VirtualKeyCode::Five => x11::keysym::XK_5,
|
VirtualKeyCode::Key5 => x11::keysym::XK_5,
|
||||||
VirtualKeyCode::Six => x11::keysym::XK_6,
|
VirtualKeyCode::Key6 => x11::keysym::XK_6,
|
||||||
VirtualKeyCode::Seven => x11::keysym::XK_7,
|
VirtualKeyCode::Key7 => x11::keysym::XK_7,
|
||||||
VirtualKeyCode::Eight => x11::keysym::XK_8,
|
VirtualKeyCode::Key8 => x11::keysym::XK_8,
|
||||||
VirtualKeyCode::Nine => x11::keysym::XK_9,
|
VirtualKeyCode::Key9 => 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,
|
||||||
|
@ -2081,28 +2043,12 @@ pub fn virtual_keycode_to_keysym(keycode: VirtualKeyCode) -> Option<u32> {
|
||||||
//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 => x11::keysym::XF86XK_Forward,
|
VirtualKeyCode::NavigateForward => {
|
||||||
|
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
481
src/client_logic.rs
Normal file
481
src/client_logic.rs
Normal file
|
@ -0,0 +1,481 @@
|
||||||
|
#![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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
436
src/clients.rs
436
src/clients.rs
|
@ -1,41 +1,31 @@
|
||||||
|
use std::num::NonZeroI32;
|
||||||
use std::{ops::Rem, usize};
|
use std::{ops::Rem, usize};
|
||||||
|
|
||||||
use indexmap::IndexMap;
|
use indexmap::IndexMap;
|
||||||
use log::error;
|
use log::{error, info};
|
||||||
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: Size<i32>,
|
pub(crate) size: (i32, i32),
|
||||||
pub(crate) position: Point<i32>,
|
pub(crate) position: (i32, i32),
|
||||||
pub(crate) parent_window: Option<Window>,
|
pub(crate) transient_for: 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).into(),
|
size: (100, 100),
|
||||||
position: (0, 0).into(),
|
position: (0, 0),
|
||||||
parent_window: None,
|
transient_for: None,
|
||||||
fullscreen: false,
|
|
||||||
window_type: WindowType::Normal,
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -44,26 +34,26 @@ mod client {
|
||||||
#[allow(dead_code)]
|
#[allow(dead_code)]
|
||||||
pub fn new(
|
pub fn new(
|
||||||
window: Window,
|
window: Window,
|
||||||
size: Size<i32>,
|
size: (i32, i32),
|
||||||
position: Point<i32>,
|
position: (i32, i32),
|
||||||
) -> Self {
|
) -> Self {
|
||||||
Self {
|
Self {
|
||||||
window,
|
window,
|
||||||
size,
|
size,
|
||||||
position,
|
position,
|
||||||
..Self::default()
|
transient_for: None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn new_dialog(
|
pub fn new_transient(
|
||||||
window: Window,
|
window: Window,
|
||||||
size: Size<i32>,
|
size: (i32, i32),
|
||||||
transient: Window,
|
transient: Window,
|
||||||
) -> Self {
|
) -> Self {
|
||||||
Self {
|
Self {
|
||||||
window,
|
window,
|
||||||
size,
|
size,
|
||||||
parent_window: Some(transient),
|
transient_for: Some(transient),
|
||||||
..Default::default()
|
..Default::default()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -75,47 +65,8 @@ mod client {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn with_window_type(self, window_type: WindowType) -> Self {
|
pub fn is_transient(&self) -> bool {
|
||||||
Self {
|
self.transient_for.is_some()
|
||||||
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()
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -189,7 +140,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: Size<i32>,
|
pub(self) screen_size: (i32, i32),
|
||||||
pub(self) master_size: f32,
|
pub(self) master_size: f32,
|
||||||
border_size: i32,
|
border_size: i32,
|
||||||
}
|
}
|
||||||
|
@ -215,7 +166,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).into(),
|
screen_size: (1, 1),
|
||||||
master_size: 1.0,
|
master_size: 1.0,
|
||||||
border_size: 0,
|
border_size: 0,
|
||||||
}
|
}
|
||||||
|
@ -238,7 +189,7 @@ impl ClientState {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn with_screen_size(self, screen_size: Size<i32>) -> Self {
|
pub fn with_screen_size(self, screen_size: (i32, i32)) -> Self {
|
||||||
Self {
|
Self {
|
||||||
screen_size,
|
screen_size,
|
||||||
..self
|
..self
|
||||||
|
@ -256,7 +207,6 @@ 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;
|
||||||
}
|
}
|
||||||
|
@ -264,42 +214,27 @@ 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();
|
||||||
|
|
||||||
match client.window_type {
|
if client.is_transient()
|
||||||
// idk how to handle docks and desktops, for now they float innit
|
&& self.contains(&client.transient_for.unwrap())
|
||||||
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 = {
|
||||||
(
|
(
|
||||||
parent.position.x
|
transient.position.0
|
||||||
+ (parent.size.width - client.size.width) / 2,
|
+ (transient.size.0 - client.size.0) / 2,
|
||||||
parent.position.y
|
transient.position.1
|
||||||
+ (parent.size.height - client.size.height) / 2,
|
+ (transient.size.1 - client.size.1) / 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();
|
||||||
|
@ -337,7 +272,9 @@ impl ClientState {
|
||||||
|| self.floating_clients.contains_key(&key)
|
|| self.floating_clients.contains_key(&key)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn iter_floating(&self) -> impl Iterator<Item = (&u64, &Client)> {
|
pub fn iter_floating(
|
||||||
|
&self,
|
||||||
|
) -> impl Iterator<Item = (&u64, &Client)> {
|
||||||
self.floating_clients.iter()
|
self.floating_clients.iter()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -349,40 +286,44 @@ impl ClientState {
|
||||||
.filter(move |&(k, _)| self.is_client_visible(k))
|
.filter(move |&(k, _)| self.is_client_visible(k))
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn iter_all_clients(&self) -> impl Iterator<Item = (&u64, &Client)> {
|
fn iter_all_clients(
|
||||||
|
&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(&self) -> impl Iterator<Item = (&u64, &Client)> {
|
pub fn iter_hidden(
|
||||||
|
&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(&self) -> impl Iterator<Item = (&u64, &Client)> {
|
pub fn iter_transient(
|
||||||
self.iter_floating().filter(|&(_, c)| c.has_parent_window())
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn iter_by_window_type(
|
|
||||||
&self,
|
&self,
|
||||||
window_type: WindowType,
|
|
||||||
) -> impl Iterator<Item = (&u64, &Client)> {
|
) -> impl Iterator<Item = (&u64, &Client)> {
|
||||||
self.iter_floating()
|
self.iter_floating().filter(|&(_, c)| c.is_transient())
|
||||||
.filter(move |&(_, c)| c.window_type == window_type)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn iter_visible(&self) -> impl Iterator<Item = (&u64, &Client)> {
|
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(&self) -> impl Iterator<Item = (&u64, &Client)> {
|
pub fn iter_current_screen(
|
||||||
|
&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(&self) -> impl Iterator<Item = (&u64, &Client)> {
|
pub fn iter_master_stack(
|
||||||
|
&self,
|
||||||
|
) -> impl Iterator<Item = (&u64, &Client)> {
|
||||||
self.virtual_screens
|
self.virtual_screens
|
||||||
.get_current()
|
.get_current()
|
||||||
.master
|
.master
|
||||||
|
@ -390,7 +331,9 @@ impl ClientState {
|
||||||
.map(move |k| (k, self.get(k).unwrap()))
|
.map(move |k| (k, self.get(k).unwrap()))
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn iter_aux_stack(&self) -> impl Iterator<Item = (&u64, &Client)> {
|
pub fn iter_aux_stack(
|
||||||
|
&self,
|
||||||
|
) -> impl Iterator<Item = (&u64, &Client)> {
|
||||||
self.virtual_screens
|
self.virtual_screens
|
||||||
.get_current()
|
.get_current()
|
||||||
.aux
|
.aux
|
||||||
|
@ -404,7 +347,7 @@ impl ClientState {
|
||||||
{
|
{
|
||||||
match self.get(key) {
|
match self.get(key) {
|
||||||
ClientEntry::Floating(c) => {
|
ClientEntry::Floating(c) => {
|
||||||
if let Some(transient_for) = c.parent_window {
|
if let Some(transient_for) = c.transient_for {
|
||||||
self.is_client_visible(&transient_for)
|
self.is_client_visible(&transient_for)
|
||||||
} else {
|
} else {
|
||||||
true
|
true
|
||||||
|
@ -477,49 +420,6 @@ 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.
|
||||||
|
@ -537,23 +437,6 @@ 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).
|
||||||
|
@ -561,16 +444,6 @@ 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);
|
||||||
|
@ -583,49 +456,37 @@ impl ClientState {
|
||||||
}
|
}
|
||||||
(None, Some(floating_client)) => {
|
(None, Some(floating_client)) => {
|
||||||
// transient clients cannot be tiled
|
// transient clients cannot be tiled
|
||||||
// only normal windows can be tiled
|
match floating_client.is_transient() {
|
||||||
match floating_client.window_type {
|
true => {
|
||||||
WindowType::Normal => {
|
self.floating_clients
|
||||||
|
.insert(key, floating_client);
|
||||||
|
}
|
||||||
|
|
||||||
|
false => {
|
||||||
self.clients.insert(key, floating_client);
|
self.clients.insert(key, floating_client);
|
||||||
self.virtual_screens.get_mut_current().insert(&key);
|
self.virtual_screens
|
||||||
}
|
.get_mut_current()
|
||||||
_ => {
|
.insert(&key);
|
||||||
self.floating_clients.insert(key, floating_client);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
_ => {
|
_ => {
|
||||||
error!(
|
error!("wtf? Client was present in tiled and floating list.")
|
||||||
"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) = self.get_mut_virtualscreen_for_client(key) {
|
if let Some(vs) =
|
||||||
|
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
|
||||||
|
@ -634,7 +495,10 @@ impl ClientState {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_virtualscreen_for_client<K>(&self, key: &K) -> Option<&VirtualScreen>
|
fn get_virtualscreen_for_client<K>(
|
||||||
|
&self,
|
||||||
|
key: &K,
|
||||||
|
) -> Option<&VirtualScreen>
|
||||||
where
|
where
|
||||||
K: ClientKey,
|
K: ClientKey,
|
||||||
{
|
{
|
||||||
|
@ -663,7 +527,10 @@ impl ClientState {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_stack_for_client<K>(&self, key: &K) -> Option<&Vec<u64>>
|
pub fn get_stack_for_client<K>(
|
||||||
|
&self,
|
||||||
|
key: &K,
|
||||||
|
) -> Option<&Vec<u64>>
|
||||||
where
|
where
|
||||||
K: ClientKey,
|
K: ClientKey,
|
||||||
{
|
{
|
||||||
|
@ -693,9 +560,7 @@ impl ClientState {
|
||||||
{
|
{
|
||||||
let (new, old) = self.focus_client_inner(key);
|
let (new, old) = self.focus_client_inner(key);
|
||||||
|
|
||||||
if !(new.is_vacant() && old.is_vacant()) {
|
info!("Swapping focus: new({:?}) old({:?})", new, old);
|
||||||
//info!("Swapping focus: new({:?}) old({:?})", new, old);
|
|
||||||
}
|
|
||||||
|
|
||||||
(new, old)
|
(new, old)
|
||||||
}
|
}
|
||||||
|
@ -785,89 +650,56 @@ 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.as_tuple();
|
let (width, height) = self.screen_size;
|
||||||
|
|
||||||
// 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 vs_width = width - gap * 2;
|
let (master_width, aux_width) = {
|
||||||
|
let effective_width = width - gap * 2;
|
||||||
|
|
||||||
let master_position = Point::new(0, 0);
|
let master_size = if vs.aux.is_empty() {
|
||||||
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 width = (vs_width as f32 * factor) as i32;
|
let master_width =
|
||||||
|
(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 height = match vs.master.len() as i32 {
|
let master_height = (height - gap * 2)
|
||||||
0 => 1,
|
/ match NonZeroI32::new(vs.master.len() as i32) {
|
||||||
n => (height - gap * 2) / n,
|
Some(i) => i.get(),
|
||||||
|
None => 1,
|
||||||
};
|
};
|
||||||
|
|
||||||
Size::new(width, height)
|
|
||||||
};
|
|
||||||
|
|
||||||
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
|
// height is max height / number of clients in the stack
|
||||||
let height = match vs.aux.len() as i32 {
|
let aux_height = (height - gap * 2)
|
||||||
0 => 1,
|
/ match NonZeroI32::new(vs.aux.len() as i32) {
|
||||||
n => (height - gap * 2) / n,
|
Some(i) => i.get(),
|
||||||
|
None => 1,
|
||||||
};
|
};
|
||||||
|
|
||||||
Size::new(width, height)
|
|
||||||
};
|
|
||||||
|
|
||||||
fn calculate_window_dimensions(
|
|
||||||
screen_size: Size<i32>,
|
|
||||||
stack_size: Size<i32>,
|
|
||||||
stack_position: Point<i32>,
|
|
||||||
fullscreen: bool,
|
|
||||||
nth: i32,
|
|
||||||
gap: i32,
|
|
||||||
border: i32,
|
|
||||||
) -> (Size<i32>, Point<i32>) {
|
|
||||||
if fullscreen {
|
|
||||||
let size = Size::new(screen_size.width, screen_size.height);
|
|
||||||
let pos = Point::new(0, 0);
|
|
||||||
(size, pos)
|
|
||||||
} else {
|
|
||||||
let size = Size::new(
|
|
||||||
stack_size.width - gap * 2 - border * 2,
|
|
||||||
stack_size.height - gap * 2 - border * 2,
|
|
||||||
);
|
|
||||||
let pos = Point::new(
|
|
||||||
stack_position.x + gap * 2,
|
|
||||||
stack_position.y + stack_size.height * nth + gap * 2,
|
|
||||||
);
|
|
||||||
(size, pos)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Master
|
// Master
|
||||||
for (i, key) in vs.master.iter().enumerate() {
|
for (i, key) in vs.master.iter().enumerate() {
|
||||||
if let Some(client) = self.clients.get_mut(key) {
|
let size = (
|
||||||
let (size, position) = calculate_window_dimensions(
|
master_width - gap * 2 - self.border_size * 2,
|
||||||
self.screen_size.into(),
|
master_height - gap * 2 - self.border_size * 2,
|
||||||
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.into(),
|
size,
|
||||||
position,
|
position,
|
||||||
..*client
|
..*client
|
||||||
};
|
};
|
||||||
|
@ -876,19 +708,19 @@ impl ClientState {
|
||||||
|
|
||||||
// Aux
|
// Aux
|
||||||
for (i, key) in vs.aux.iter().enumerate() {
|
for (i, key) in vs.aux.iter().enumerate() {
|
||||||
if let Some(client) = self.clients.get_mut(key) {
|
let size = (
|
||||||
let (size, position) = calculate_window_dimensions(
|
aux_width - gap * 2 - self.border_size * 2,
|
||||||
self.screen_size.into(),
|
aux_height - gap * 2 - self.border_size * 2,
|
||||||
aux_window_size,
|
|
||||||
aux_position,
|
|
||||||
client.is_fullscreen(),
|
|
||||||
i as i32,
|
|
||||||
gap,
|
|
||||||
self.border_size,
|
|
||||||
);
|
);
|
||||||
|
|
||||||
|
let position = (
|
||||||
|
master_width + gap * 2,
|
||||||
|
aux_height * i as i32 + gap * 2,
|
||||||
|
);
|
||||||
|
|
||||||
|
if let Some(client) = self.clients.get_mut(key) {
|
||||||
*client = Client {
|
*client = Client {
|
||||||
size: size.into(),
|
size,
|
||||||
position,
|
position,
|
||||||
..*client
|
..*client
|
||||||
};
|
};
|
||||||
|
@ -920,7 +752,8 @@ impl VirtualScreen {
|
||||||
where
|
where
|
||||||
K: ClientKey,
|
K: ClientKey,
|
||||||
{
|
{
|
||||||
self.master.contains(&key.key()) || self.aux.contains(&key.key())
|
self.master.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
|
||||||
|
@ -966,8 +799,11 @@ impl VirtualScreen {
|
||||||
self.aux.extend(self.master.drain(index..=index));
|
self.aux.extend(self.master.drain(index..=index));
|
||||||
}
|
}
|
||||||
None => {
|
None => {
|
||||||
let index =
|
let index = self
|
||||||
self.aux.iter().position(|&k| k == key.key()).unwrap();
|
.aux
|
||||||
|
.iter()
|
||||||
|
.position(|&k| k == key.key())
|
||||||
|
.unwrap();
|
||||||
self.master.extend(self.aux.drain(index..=index));
|
self.master.extend(self.aux.drain(index..=index));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1014,7 +850,9 @@ impl VirtualScreenStore {
|
||||||
self.screens.iter()
|
self.screens.iter()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn iter_mut(&mut self) -> impl Iterator<Item = &mut VirtualScreen> {
|
fn iter_mut(
|
||||||
|
&mut self,
|
||||||
|
) -> impl Iterator<Item = &mut VirtualScreen> {
|
||||||
self.screens.iter_mut()
|
self.screens.iter_mut()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1051,7 +889,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.min(self.screens.len() - 1);
|
self.current_idx = n % self.screens.len();
|
||||||
|
|
||||||
self.current_idx
|
self.current_idx
|
||||||
}
|
}
|
||||||
|
@ -1061,7 +899,9 @@ 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) => Some(client),
|
Self::Tiled(client) | Self::Floating(client) => {
|
||||||
|
Some(client)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1102,25 +942,3 @@ 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,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
409
src/clients2.rs
Normal file
409
src/clients2.rs
Normal file
|
@ -0,0 +1,409 @@
|
||||||
|
#![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
26
src/lib.rs
|
@ -1,26 +0,0 @@
|
||||||
#![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),
|
|
||||||
}
|
|
||||||
}
|
|
47
src/main.rs
47
src/main.rs
|
@ -1,5 +1,3 @@
|
||||||
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},
|
||||||
|
@ -7,31 +5,45 @@ use log4rs::{
|
||||||
encode::pattern::PatternEncoder,
|
encode::pattern::PatternEncoder,
|
||||||
Config,
|
Config,
|
||||||
};
|
};
|
||||||
|
use state::WMConfig;
|
||||||
|
|
||||||
use wm::state::WMConfig;
|
#[macro_use]
|
||||||
|
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 = ConsoleAppender::builder().encoder(encoder.clone()).build();
|
let stdout =
|
||||||
|
ConsoleAppender::builder().encoder(encoder.clone()).build();
|
||||||
|
|
||||||
let home = dirs::home_dir().expect("Failed to get $HOME env var.");
|
let home =
|
||||||
|
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/nirgendwm.log"))
|
.build(home.join(".local/portlights.log"))
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
let config = Config::builder()
|
let config = Config::builder()
|
||||||
.appender(Appender::builder().build("stdout", Box::new(stdout)))
|
.appender(
|
||||||
|
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::Debug),
|
.build(log::LevelFilter::Info),
|
||||||
)
|
)
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
|
@ -43,23 +55,7 @@ fn main() {
|
||||||
|
|
||||||
log_prologue();
|
log_prologue();
|
||||||
|
|
||||||
let mut config_path = std::path::PathBuf::from(env!("HOME"));
|
state::WindowManager::new(WMConfig::default()).run();
|
||||||
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() {
|
||||||
|
@ -78,6 +74,7 @@ fn log_prologue() {
|
||||||
#[test]
|
#[test]
|
||||||
fn test_logger() {
|
fn test_logger() {
|
||||||
init_logger();
|
init_logger();
|
||||||
|
// asdf
|
||||||
|
|
||||||
log_prologue();
|
log_prologue();
|
||||||
}
|
}
|
||||||
|
|
757
src/state.rs
757
src/state.rs
File diff suppressed because it is too large
Load diff
222
src/util.rs
222
src/util.rs
|
@ -22,225 +22,3 @@ 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)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
841
src/xlib.rs
Normal file
841
src/xlib.rs
Normal file
|
@ -0,0 +1,841 @@
|
||||||
|
use log::info;
|
||||||
|
use std::ptr::{null, null_mut};
|
||||||
|
use std::{ffi::CString, rc::Rc};
|
||||||
|
|
||||||
|
use x11::xlib::{
|
||||||
|
self, AnyButton, AnyKey, AnyModifier, Atom, ButtonPressMask,
|
||||||
|
ButtonReleaseMask, CWEventMask, ControlMask, CurrentTime,
|
||||||
|
EnterWindowMask, FocusChangeMask, LockMask, Mod1Mask, Mod2Mask,
|
||||||
|
Mod3Mask, Mod4Mask, Mod5Mask, PointerMotionMask,
|
||||||
|
PropertyChangeMask, ShiftMask, StructureNotifyMask,
|
||||||
|
SubstructureNotifyMask, SubstructureRedirectMask, Window,
|
||||||
|
XCloseDisplay, XConfigureRequestEvent, XDefaultScreen, XEvent,
|
||||||
|
XGetTransientForHint, XGrabPointer, XInternAtom, XKillClient,
|
||||||
|
XMapWindow, XOpenDisplay, XRaiseWindow, XRootWindow,
|
||||||
|
XSetErrorHandler, XSync, XUngrabButton, XUngrabKey,
|
||||||
|
XUngrabPointer, XWarpPointer, XWindowAttributes,
|
||||||
|
};
|
||||||
|
use xlib::GrabModeAsync;
|
||||||
|
|
||||||
|
use log::error;
|
||||||
|
|
||||||
|
use crate::clients::Client;
|
||||||
|
|
||||||
|
pub struct XLib {
|
||||||
|
display: Display,
|
||||||
|
root: Window,
|
||||||
|
_screen: i32,
|
||||||
|
atoms: Atoms,
|
||||||
|
global_keybinds: Vec<KeyOrButton>,
|
||||||
|
}
|
||||||
|
|
||||||
|
struct Atoms {
|
||||||
|
protocols: Atom,
|
||||||
|
delete_window: Atom,
|
||||||
|
active_window: Atom,
|
||||||
|
take_focus: Atom,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
|
||||||
|
pub enum KeyOrButton {
|
||||||
|
Key(i32, u32),
|
||||||
|
Button(u32, u32, u64),
|
||||||
|
}
|
||||||
|
|
||||||
|
impl KeyOrButton {
|
||||||
|
#[allow(dead_code)]
|
||||||
|
pub fn key(keycode: i32, modmask: u32) -> Self {
|
||||||
|
Self::Key(keycode, modmask)
|
||||||
|
}
|
||||||
|
pub fn button(
|
||||||
|
button: u32,
|
||||||
|
modmask: u32,
|
||||||
|
buttonmask: i64,
|
||||||
|
) -> Self {
|
||||||
|
Self::Button(button, modmask, buttonmask as u64)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone)]
|
||||||
|
pub struct Display(Rc<*mut xlib::Display>);
|
||||||
|
|
||||||
|
impl XLib {
|
||||||
|
pub fn new() -> Self {
|
||||||
|
let (display, _screen, root) = unsafe {
|
||||||
|
let display = XOpenDisplay(null());
|
||||||
|
|
||||||
|
assert_ne!(display, null_mut());
|
||||||
|
|
||||||
|
let display = Display::new(display);
|
||||||
|
let screen = XDefaultScreen(display.get());
|
||||||
|
let root = XRootWindow(display.get(), screen);
|
||||||
|
|
||||||
|
(display, screen, root)
|
||||||
|
};
|
||||||
|
|
||||||
|
Self {
|
||||||
|
atoms: Atoms::init(display.clone()),
|
||||||
|
global_keybinds: Vec::new(),
|
||||||
|
root,
|
||||||
|
_screen,
|
||||||
|
display,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn init(&mut self) {
|
||||||
|
unsafe {
|
||||||
|
let mut window_attributes = std::mem::MaybeUninit::<
|
||||||
|
xlib::XSetWindowAttributes,
|
||||||
|
>::zeroed()
|
||||||
|
.assume_init();
|
||||||
|
|
||||||
|
window_attributes.event_mask = SubstructureRedirectMask
|
||||||
|
| StructureNotifyMask
|
||||||
|
| SubstructureNotifyMask
|
||||||
|
| EnterWindowMask
|
||||||
|
| PointerMotionMask
|
||||||
|
| ButtonPressMask;
|
||||||
|
|
||||||
|
xlib::XChangeWindowAttributes(
|
||||||
|
self.dpy(),
|
||||||
|
self.root,
|
||||||
|
CWEventMask,
|
||||||
|
&mut window_attributes,
|
||||||
|
);
|
||||||
|
|
||||||
|
xlib::XSelectInput(
|
||||||
|
self.dpy(),
|
||||||
|
self.root,
|
||||||
|
window_attributes.event_mask,
|
||||||
|
);
|
||||||
|
|
||||||
|
XSetErrorHandler(Some(xlib_error_handler));
|
||||||
|
|
||||||
|
XSync(self.dpy(), 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
self.grab_global_keybinds(self.root);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn add_global_keybind(&mut self, key: KeyOrButton) {
|
||||||
|
self.global_keybinds.push(key);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[allow(dead_code)]
|
||||||
|
fn ungrab_global_keybings(&self, window: Window) {
|
||||||
|
unsafe {
|
||||||
|
XUngrabButton(
|
||||||
|
self.dpy(),
|
||||||
|
AnyButton as u32,
|
||||||
|
AnyModifier,
|
||||||
|
window,
|
||||||
|
);
|
||||||
|
XUngrabKey(self.dpy(), AnyKey, AnyModifier, window);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn grab_global_keybinds(&self, window: Window) {
|
||||||
|
for kb in self.global_keybinds.iter() {
|
||||||
|
self.grab_key_or_button(window, kb);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[allow(dead_code)]
|
||||||
|
pub fn remove_global_keybind(&mut self, key: &KeyOrButton) {
|
||||||
|
if self.global_keybinds.contains(key) {
|
||||||
|
self.global_keybinds.retain(|kb| kb != key);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn dpy(&self) -> *mut xlib::Display {
|
||||||
|
self.display.get()
|
||||||
|
}
|
||||||
|
|
||||||
|
#[allow(dead_code)]
|
||||||
|
pub fn squash_event(&self, event_type: i32) -> XEvent {
|
||||||
|
unsafe {
|
||||||
|
let mut event =
|
||||||
|
std::mem::MaybeUninit::<xlib::XEvent>::zeroed()
|
||||||
|
.assume_init();
|
||||||
|
|
||||||
|
while xlib::XCheckTypedEvent(
|
||||||
|
self.dpy(),
|
||||||
|
event_type,
|
||||||
|
&mut event,
|
||||||
|
) != 0
|
||||||
|
{}
|
||||||
|
|
||||||
|
event
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn next_event(&self) -> XEvent {
|
||||||
|
unsafe {
|
||||||
|
let mut event =
|
||||||
|
std::mem::MaybeUninit::<xlib::XEvent>::zeroed()
|
||||||
|
.assume_init();
|
||||||
|
xlib::XNextEvent(self.dpy(), &mut event);
|
||||||
|
|
||||||
|
event
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn grab_key_or_button(
|
||||||
|
&self,
|
||||||
|
window: Window,
|
||||||
|
key: &KeyOrButton,
|
||||||
|
) {
|
||||||
|
let numlock_mask = self.get_numlock_mask();
|
||||||
|
let modifiers =
|
||||||
|
vec![0, LockMask, numlock_mask, LockMask | numlock_mask];
|
||||||
|
|
||||||
|
for modifier in modifiers.iter() {
|
||||||
|
match key {
|
||||||
|
&KeyOrButton::Key(keycode, modmask) => {
|
||||||
|
unsafe {
|
||||||
|
xlib::XGrabKey(
|
||||||
|
self.dpy(),
|
||||||
|
keycode,
|
||||||
|
modmask | modifier,
|
||||||
|
window,
|
||||||
|
1, /* true */
|
||||||
|
GrabModeAsync,
|
||||||
|
GrabModeAsync,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
&KeyOrButton::Button(button, modmask, buttonmask) => {
|
||||||
|
unsafe {
|
||||||
|
xlib::XGrabButton(
|
||||||
|
self.dpy(),
|
||||||
|
button,
|
||||||
|
modmask | modifier,
|
||||||
|
window,
|
||||||
|
1, /*true */
|
||||||
|
buttonmask as u32,
|
||||||
|
GrabModeAsync,
|
||||||
|
GrabModeAsync,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn focus_client(&self, client: &Client) {
|
||||||
|
unsafe {
|
||||||
|
xlib::XSetInputFocus(
|
||||||
|
self.dpy(),
|
||||||
|
client.window,
|
||||||
|
xlib::RevertToPointerRoot,
|
||||||
|
xlib::CurrentTime,
|
||||||
|
);
|
||||||
|
|
||||||
|
let screen =
|
||||||
|
xlib::XDefaultScreenOfDisplay(self.dpy()).as_ref();
|
||||||
|
|
||||||
|
if let Some(screen) = screen {
|
||||||
|
xlib::XSetWindowBorder(
|
||||||
|
self.dpy(),
|
||||||
|
client.window,
|
||||||
|
screen.white_pixel,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
xlib::XChangeProperty(
|
||||||
|
self.dpy(),
|
||||||
|
self.root,
|
||||||
|
self.atoms.active_window,
|
||||||
|
xlib::XA_WINDOW,
|
||||||
|
32,
|
||||||
|
xlib::PropModeReplace,
|
||||||
|
&client.window as *const u64 as *const _,
|
||||||
|
1,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
self.send_protocol(client, self.atoms.take_focus);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn unfocus_client(&self, client: &Client) {
|
||||||
|
//info!("unfocusing client: {:?}", client);
|
||||||
|
|
||||||
|
unsafe {
|
||||||
|
xlib::XSetInputFocus(
|
||||||
|
self.dpy(),
|
||||||
|
self.root,
|
||||||
|
xlib::RevertToPointerRoot,
|
||||||
|
xlib::CurrentTime,
|
||||||
|
);
|
||||||
|
|
||||||
|
let screen =
|
||||||
|
xlib::XDefaultScreenOfDisplay(self.dpy()).as_ref();
|
||||||
|
|
||||||
|
if let Some(screen) = screen {
|
||||||
|
xlib::XSetWindowBorder(
|
||||||
|
self.dpy(),
|
||||||
|
client.window,
|
||||||
|
screen.black_pixel,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// xlib::XDeleteProperty(
|
||||||
|
// self.dpy(),
|
||||||
|
// self.root,
|
||||||
|
// self.atoms.active_window,
|
||||||
|
// );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn move_resize_client(&self, client: &Client) {
|
||||||
|
let mut windowchanges = xlib::XWindowChanges {
|
||||||
|
x: client.position.0,
|
||||||
|
y: client.position.1,
|
||||||
|
width: client.size.0,
|
||||||
|
height: client.size.1,
|
||||||
|
border_width: 0,
|
||||||
|
sibling: 0,
|
||||||
|
stack_mode: 0,
|
||||||
|
};
|
||||||
|
|
||||||
|
if client.size.0 < 1 || client.size.1 < 1 {
|
||||||
|
error!("client {:?} size is less than 1 pixel!", client);
|
||||||
|
} else {
|
||||||
|
unsafe {
|
||||||
|
xlib::XConfigureWindow(
|
||||||
|
self.dpy(),
|
||||||
|
client.window,
|
||||||
|
(xlib::CWY
|
||||||
|
| xlib::CWX
|
||||||
|
| xlib::CWHeight
|
||||||
|
| xlib::CWWidth) as u32,
|
||||||
|
&mut windowchanges,
|
||||||
|
);
|
||||||
|
|
||||||
|
// I don't think I have to call this ~
|
||||||
|
//self.configure_client(client);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn move_client(&self, client: &Client) {
|
||||||
|
let mut wc = xlib::XWindowChanges {
|
||||||
|
x: client.position.0,
|
||||||
|
y: client.position.1,
|
||||||
|
width: client.size.0,
|
||||||
|
height: client.size.1,
|
||||||
|
border_width: 0,
|
||||||
|
sibling: 0,
|
||||||
|
stack_mode: 0,
|
||||||
|
};
|
||||||
|
|
||||||
|
if client.size.0 < 1 || client.size.1 < 1 {
|
||||||
|
error!("client {:?} size is less than 1 pixel!", client);
|
||||||
|
} else {
|
||||||
|
unsafe {
|
||||||
|
xlib::XConfigureWindow(
|
||||||
|
self.dpy(),
|
||||||
|
client.window,
|
||||||
|
(xlib::CWX | xlib::CWY) as u32,
|
||||||
|
&mut wc,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn resize_client(&self, client: &Client) {
|
||||||
|
let mut wc = xlib::XWindowChanges {
|
||||||
|
x: client.position.0,
|
||||||
|
y: client.position.1,
|
||||||
|
width: client.size.0,
|
||||||
|
height: client.size.1,
|
||||||
|
border_width: 0,
|
||||||
|
sibling: 0,
|
||||||
|
stack_mode: 0,
|
||||||
|
};
|
||||||
|
|
||||||
|
if client.size.0 < 1 || client.size.1 < 1 {
|
||||||
|
error!("client {:?} size is less than 1 pixel!", client);
|
||||||
|
} else {
|
||||||
|
unsafe {
|
||||||
|
xlib::XConfigureWindow(
|
||||||
|
self.dpy(),
|
||||||
|
client.window,
|
||||||
|
(xlib::CWWidth | xlib::CWHeight) as u32,
|
||||||
|
&mut wc,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn hide_client(&self, client: &Client) {
|
||||||
|
let mut wc = xlib::XWindowChanges {
|
||||||
|
x: client.size.0 * -2,
|
||||||
|
y: client.position.1,
|
||||||
|
width: client.size.0,
|
||||||
|
height: client.size.1,
|
||||||
|
border_width: 0,
|
||||||
|
sibling: 0,
|
||||||
|
stack_mode: 0,
|
||||||
|
};
|
||||||
|
|
||||||
|
if client.size.0 < 1 || client.size.1 < 1 {
|
||||||
|
error!("client {:?} size is less than 1 pixel!", client);
|
||||||
|
} else {
|
||||||
|
unsafe {
|
||||||
|
xlib::XConfigureWindow(
|
||||||
|
self.dpy(),
|
||||||
|
client.window,
|
||||||
|
(xlib::CWX | xlib::CWY) as u32,
|
||||||
|
&mut wc,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn raise_client(&self, client: &Client) {
|
||||||
|
unsafe {
|
||||||
|
XRaiseWindow(self.dpy(), client.window);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_window_size(
|
||||||
|
&self,
|
||||||
|
window: Window,
|
||||||
|
) -> Option<(i32, i32)> {
|
||||||
|
let mut wa = unsafe {
|
||||||
|
std::mem::MaybeUninit::<xlib::XWindowAttributes>::zeroed()
|
||||||
|
.assume_init()
|
||||||
|
};
|
||||||
|
|
||||||
|
if unsafe {
|
||||||
|
xlib::XGetWindowAttributes(self.dpy(), window, &mut wa)
|
||||||
|
!= 0
|
||||||
|
} {
|
||||||
|
Some((wa.width, wa.height))
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get_window_attributes(
|
||||||
|
&self,
|
||||||
|
window: Window,
|
||||||
|
) -> Option<XWindowAttributes> {
|
||||||
|
let mut wa = unsafe {
|
||||||
|
std::mem::MaybeUninit::<xlib::XWindowAttributes>::zeroed()
|
||||||
|
.assume_init()
|
||||||
|
};
|
||||||
|
|
||||||
|
if unsafe {
|
||||||
|
xlib::XGetWindowAttributes(self.dpy(), window, &mut wa)
|
||||||
|
!= 0
|
||||||
|
} {
|
||||||
|
Some(wa)
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_transient_for_window(
|
||||||
|
&self,
|
||||||
|
window: Window,
|
||||||
|
) -> Option<Window> {
|
||||||
|
let mut transient_for: Window = 0;
|
||||||
|
|
||||||
|
if unsafe {
|
||||||
|
XGetTransientForHint(
|
||||||
|
self.dpy(),
|
||||||
|
window,
|
||||||
|
&mut transient_for,
|
||||||
|
) != 0
|
||||||
|
} {
|
||||||
|
Some(transient_for)
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn expose_client(&self, client: &Client) {
|
||||||
|
self.expose_window(client.window);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn expose_window(&self, window: Window) {
|
||||||
|
if let Some(wa) = self.get_window_attributes(window) {
|
||||||
|
unsafe {
|
||||||
|
xlib::XClearArea(
|
||||||
|
self.dpy(),
|
||||||
|
window,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
wa.width as u32,
|
||||||
|
wa.height as u32,
|
||||||
|
1,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn configure_window(&self, event: &XConfigureRequestEvent) {
|
||||||
|
let mut wc = xlib::XWindowChanges {
|
||||||
|
x: event.x,
|
||||||
|
y: event.y,
|
||||||
|
width: event.width,
|
||||||
|
height: event.height,
|
||||||
|
border_width: event.border_width,
|
||||||
|
sibling: event.above,
|
||||||
|
stack_mode: event.detail,
|
||||||
|
};
|
||||||
|
|
||||||
|
unsafe {
|
||||||
|
xlib::XConfigureWindow(
|
||||||
|
self.dpy(),
|
||||||
|
event.window,
|
||||||
|
event.value_mask as u32,
|
||||||
|
&mut wc,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn configure_client(&self, client: &Client, border: i32) {
|
||||||
|
let mut event = xlib::XConfigureEvent {
|
||||||
|
type_: xlib::ConfigureNotify,
|
||||||
|
display: self.dpy(),
|
||||||
|
event: client.window,
|
||||||
|
window: client.window,
|
||||||
|
x: client.position.0,
|
||||||
|
y: client.position.1,
|
||||||
|
width: client.size.0,
|
||||||
|
height: client.size.1,
|
||||||
|
border_width: border,
|
||||||
|
override_redirect: 0,
|
||||||
|
send_event: 0,
|
||||||
|
serial: 0,
|
||||||
|
above: 0,
|
||||||
|
};
|
||||||
|
|
||||||
|
unsafe {
|
||||||
|
xlib::XSetWindowBorderWidth(
|
||||||
|
self.dpy(),
|
||||||
|
event.window,
|
||||||
|
event.border_width as u32,
|
||||||
|
);
|
||||||
|
|
||||||
|
xlib::XSendEvent(
|
||||||
|
self.dpy(),
|
||||||
|
event.window,
|
||||||
|
0,
|
||||||
|
StructureNotifyMask,
|
||||||
|
&mut event as *mut _ as *mut XEvent,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn map_window(&self, window: Window) {
|
||||||
|
unsafe {
|
||||||
|
XMapWindow(self.dpy(), window);
|
||||||
|
|
||||||
|
xlib::XSelectInput(
|
||||||
|
self.dpy(),
|
||||||
|
window,
|
||||||
|
EnterWindowMask
|
||||||
|
| FocusChangeMask
|
||||||
|
| PropertyChangeMask
|
||||||
|
| StructureNotifyMask,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
self.grab_global_keybinds(window);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn dimensions(&self) -> (i32, i32) {
|
||||||
|
unsafe {
|
||||||
|
let mut wa = std::mem::MaybeUninit::<
|
||||||
|
xlib::XWindowAttributes,
|
||||||
|
>::zeroed()
|
||||||
|
.assume_init();
|
||||||
|
|
||||||
|
xlib::XGetWindowAttributes(
|
||||||
|
self.dpy(),
|
||||||
|
self.root,
|
||||||
|
&mut wa,
|
||||||
|
);
|
||||||
|
|
||||||
|
info!(
|
||||||
|
"Root window dimensions: {}, {}",
|
||||||
|
wa.width, wa.height
|
||||||
|
);
|
||||||
|
|
||||||
|
(wa.width, wa.height)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn close_dpy(&self) {
|
||||||
|
unsafe {
|
||||||
|
XCloseDisplay(self.dpy());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn kill_client(&self, client: &Client) {
|
||||||
|
if !self.send_protocol(client, self.atoms.delete_window) {
|
||||||
|
unsafe {
|
||||||
|
XKillClient(self.dpy(), client.window);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn grab_cursor(&self) {
|
||||||
|
unsafe {
|
||||||
|
XGrabPointer(
|
||||||
|
self.dpy(),
|
||||||
|
self.root,
|
||||||
|
0,
|
||||||
|
(ButtonPressMask
|
||||||
|
| ButtonReleaseMask
|
||||||
|
| PointerMotionMask) as u32,
|
||||||
|
GrabModeAsync,
|
||||||
|
GrabModeAsync,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
CurrentTime,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn release_cursor(&self) {
|
||||||
|
unsafe {
|
||||||
|
XUngrabPointer(self.dpy(), CurrentTime);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn move_cursor(
|
||||||
|
&self,
|
||||||
|
window: Option<Window>,
|
||||||
|
position: (i32, i32),
|
||||||
|
) {
|
||||||
|
unsafe {
|
||||||
|
XWarpPointer(
|
||||||
|
self.dpy(),
|
||||||
|
0,
|
||||||
|
window.unwrap_or(self.root),
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
position.0,
|
||||||
|
position.1,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn check_for_protocol(
|
||||||
|
&self,
|
||||||
|
client: &Client,
|
||||||
|
proto: xlib::Atom,
|
||||||
|
) -> bool {
|
||||||
|
let mut protos: *mut xlib::Atom = null_mut();
|
||||||
|
let mut num_protos: i32 = 0;
|
||||||
|
|
||||||
|
unsafe {
|
||||||
|
if xlib::XGetWMProtocols(
|
||||||
|
self.dpy(),
|
||||||
|
client.window,
|
||||||
|
&mut protos,
|
||||||
|
&mut num_protos,
|
||||||
|
) != 0
|
||||||
|
{
|
||||||
|
for i in 0..num_protos {
|
||||||
|
if *protos.offset(i as isize) == proto {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
fn send_protocol(
|
||||||
|
&self,
|
||||||
|
client: &Client,
|
||||||
|
proto: xlib::Atom,
|
||||||
|
) -> bool {
|
||||||
|
if self.check_for_protocol(client, proto) {
|
||||||
|
let mut data = xlib::ClientMessageData::default();
|
||||||
|
data.set_long(0, proto as i64);
|
||||||
|
|
||||||
|
let mut event = XEvent {
|
||||||
|
client_message: xlib::XClientMessageEvent {
|
||||||
|
type_: xlib::ClientMessage,
|
||||||
|
serial: 0,
|
||||||
|
display: self.dpy(),
|
||||||
|
send_event: 0,
|
||||||
|
window: client.window,
|
||||||
|
format: 32,
|
||||||
|
message_type: self.atoms.protocols,
|
||||||
|
data,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
unsafe {
|
||||||
|
xlib::XSendEvent(
|
||||||
|
self.dpy(),
|
||||||
|
client.window,
|
||||||
|
0,
|
||||||
|
xlib::NoEventMask,
|
||||||
|
&mut event,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
true
|
||||||
|
} else {
|
||||||
|
false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn make_key<S>(&self, key: S, modmask: u32) -> KeyOrButton
|
||||||
|
where
|
||||||
|
S: AsRef<str>,
|
||||||
|
{
|
||||||
|
let key = self.keycode(key);
|
||||||
|
|
||||||
|
KeyOrButton::Key(key, modmask)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn keycode<S>(&self, string: S) -> i32
|
||||||
|
where
|
||||||
|
S: AsRef<str>,
|
||||||
|
{
|
||||||
|
let c_string = CString::new(string.as_ref()).unwrap();
|
||||||
|
|
||||||
|
unsafe {
|
||||||
|
let keysym = xlib::XStringToKeysym(c_string.as_ptr());
|
||||||
|
xlib::XKeysymToKeycode(self.dpy(), keysym) as i32
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get_numlock_mask(&self) -> u32 {
|
||||||
|
unsafe {
|
||||||
|
let modmap = xlib::XGetModifierMapping(self.dpy());
|
||||||
|
let max_keypermod = (*modmap).max_keypermod;
|
||||||
|
|
||||||
|
for i in 0..8 {
|
||||||
|
for j in 0..max_keypermod {
|
||||||
|
if *(*modmap)
|
||||||
|
.modifiermap
|
||||||
|
.offset((i * max_keypermod + j) as isize)
|
||||||
|
== xlib::XKeysymToKeycode(
|
||||||
|
self.dpy(),
|
||||||
|
x11::keysym::XK_Num_Lock as u64,
|
||||||
|
)
|
||||||
|
{
|
||||||
|
return 1 << i;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
0
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_clean_mask(&self) -> u32 {
|
||||||
|
!(self.get_numlock_mask() | LockMask)
|
||||||
|
& (ShiftMask
|
||||||
|
| ControlMask
|
||||||
|
| Mod1Mask
|
||||||
|
| Mod2Mask
|
||||||
|
| Mod3Mask
|
||||||
|
| Mod4Mask
|
||||||
|
| Mod5Mask)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[allow(dead_code)]
|
||||||
|
pub fn clean_mask(&self, mask: u32) -> u32 {
|
||||||
|
mask & self.get_clean_mask()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn are_masks_equal(&self, rhs: u32, lhs: u32) -> bool {
|
||||||
|
let clean = self.get_clean_mask();
|
||||||
|
|
||||||
|
rhs & clean == lhs & clean
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Display {
|
||||||
|
pub fn new(display: *mut x11::xlib::Display) -> Self {
|
||||||
|
Self {
|
||||||
|
0: Rc::new(display),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get(&self) -> *mut x11::xlib::Display {
|
||||||
|
*self.0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Atoms {
|
||||||
|
fn init(display: Display) -> Self {
|
||||||
|
unsafe {
|
||||||
|
Self {
|
||||||
|
protocols: {
|
||||||
|
let name = CString::new("WM_PROTOCOLS").unwrap();
|
||||||
|
XInternAtom(
|
||||||
|
display.get(),
|
||||||
|
name.as_c_str().as_ptr(),
|
||||||
|
0,
|
||||||
|
)
|
||||||
|
},
|
||||||
|
delete_window: {
|
||||||
|
let name =
|
||||||
|
CString::new("WM_DELETE_WINDOW").unwrap();
|
||||||
|
XInternAtom(
|
||||||
|
display.get(),
|
||||||
|
name.as_c_str().as_ptr(),
|
||||||
|
0,
|
||||||
|
)
|
||||||
|
},
|
||||||
|
active_window: {
|
||||||
|
let name =
|
||||||
|
CString::new("WM_ACTIVE_WINDOW").unwrap();
|
||||||
|
XInternAtom(
|
||||||
|
display.get(),
|
||||||
|
name.as_c_str().as_ptr(),
|
||||||
|
0,
|
||||||
|
)
|
||||||
|
},
|
||||||
|
take_focus: {
|
||||||
|
let name = CString::new("WM_TAKE_FOCUS").unwrap();
|
||||||
|
XInternAtom(
|
||||||
|
display.get(),
|
||||||
|
name.as_c_str().as_ptr(),
|
||||||
|
0,
|
||||||
|
)
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[allow(dead_code)]
|
||||||
|
unsafe extern "C" fn xlib_error_handler(
|
||||||
|
_dpy: *mut x11::xlib::Display,
|
||||||
|
ee: *mut x11::xlib::XErrorEvent,
|
||||||
|
) -> std::os::raw::c_int {
|
||||||
|
let err = ee.as_ref().unwrap();
|
||||||
|
|
||||||
|
if err.error_code == x11::xlib::BadWindow
|
||||||
|
|| err.error_code == x11::xlib::BadDrawable
|
||||||
|
|| err.error_code == x11::xlib::BadAccess
|
||||||
|
|| err.error_code == x11::xlib::BadMatch
|
||||||
|
{
|
||||||
|
0
|
||||||
|
} else {
|
||||||
|
error!(
|
||||||
|
"wm: fatal error:\nrequest_code: {}\nerror_code: {}",
|
||||||
|
err.request_code, err.error_code
|
||||||
|
);
|
||||||
|
std::process::exit(1);
|
||||||
|
}
|
||||||
|
}
|
BIN
starship.jpg
Normal file
BIN
starship.jpg
Normal file
Binary file not shown.
After Width: | Height: | Size: 210 KiB |
Loading…
Reference in a new issue