Compare commits
5 commits
feature_cu
...
refactor
Author | SHA1 | Date | |
---|---|---|---|
|
adc71e517f | ||
|
1b2e2d848c | ||
|
98459d620c | ||
|
507bc75ccc | ||
|
db94c82185 |
|
@ -8,6 +8,9 @@ edition = "2018"
|
|||
|
||||
[dependencies]
|
||||
x11 = {version = "2.18.2", features = ["xlib"] }
|
||||
x11rb = "0.8.1"
|
||||
num-derive = "0.3.3"
|
||||
num-traits = "0.2.14"
|
||||
log = "0.4.13"
|
||||
simple_logger = "1.11.0"
|
||||
dirs = "3.0.2"
|
||||
|
|
|
@ -1,2 +1,2 @@
|
|||
#wrap_comments = true
|
||||
max_width = 80
|
||||
wrap_comments = true
|
||||
max_width = 70
|
209
src/backends/keycodes.rs
Normal file
209
src/backends/keycodes.rs
Normal file
|
@ -0,0 +1,209 @@
|
|||
#![allow(dead_code)]
|
||||
|
||||
#[derive(
|
||||
Debug, Hash, Ord, PartialOrd, PartialEq, Eq, Clone, Copy,
|
||||
)]
|
||||
pub enum MouseButton {
|
||||
Left,
|
||||
Middle,
|
||||
Right,
|
||||
ScrollUp,
|
||||
ScrollDown,
|
||||
ScrollLeft,
|
||||
ScrollRight,
|
||||
Forward,
|
||||
Backward,
|
||||
}
|
||||
|
||||
/// from winit
|
||||
#[derive(
|
||||
Debug, Hash, Ord, PartialOrd, PartialEq, Eq, Clone, Copy,
|
||||
)]
|
||||
#[repr(u32)]
|
||||
pub enum VirtualKeyCode {
|
||||
Key1,
|
||||
Key2,
|
||||
Key3,
|
||||
Key4,
|
||||
Key5,
|
||||
Key6,
|
||||
Key7,
|
||||
Key8,
|
||||
Key9,
|
||||
Key0,
|
||||
A,
|
||||
B,
|
||||
C,
|
||||
D,
|
||||
E,
|
||||
F,
|
||||
G,
|
||||
H,
|
||||
I,
|
||||
J,
|
||||
K,
|
||||
L,
|
||||
M,
|
||||
N,
|
||||
O,
|
||||
P,
|
||||
Q,
|
||||
R,
|
||||
S,
|
||||
T,
|
||||
U,
|
||||
V,
|
||||
W,
|
||||
X,
|
||||
Y,
|
||||
Z,
|
||||
|
||||
/// The Escape key, next to F1.
|
||||
Escape,
|
||||
|
||||
F1,
|
||||
F2,
|
||||
F3,
|
||||
F4,
|
||||
F5,
|
||||
F6,
|
||||
F7,
|
||||
F8,
|
||||
F9,
|
||||
F10,
|
||||
F11,
|
||||
F12,
|
||||
F13,
|
||||
F14,
|
||||
F15,
|
||||
F16,
|
||||
F17,
|
||||
F18,
|
||||
F19,
|
||||
F20,
|
||||
F21,
|
||||
F22,
|
||||
F23,
|
||||
F24,
|
||||
|
||||
/// Print Screen/SysRq.
|
||||
Snapshot,
|
||||
/// Scroll Lock.
|
||||
Scroll,
|
||||
/// Pause/Break key, next to Scroll lock.
|
||||
Pause,
|
||||
|
||||
/// `Insert`, next to Backspace.
|
||||
Insert,
|
||||
Home,
|
||||
Delete,
|
||||
End,
|
||||
PageDown,
|
||||
PageUp,
|
||||
|
||||
Left,
|
||||
Up,
|
||||
Right,
|
||||
Down,
|
||||
|
||||
/// The Backspace key, right over Enter.
|
||||
// TODO: rename
|
||||
Back,
|
||||
/// The Enter key.
|
||||
Return,
|
||||
/// The space bar.
|
||||
Space,
|
||||
|
||||
/// The "Compose" key on Linux.
|
||||
Compose,
|
||||
|
||||
Caret,
|
||||
|
||||
Numlock,
|
||||
Numpad0,
|
||||
Numpad1,
|
||||
Numpad2,
|
||||
Numpad3,
|
||||
Numpad4,
|
||||
Numpad5,
|
||||
Numpad6,
|
||||
Numpad7,
|
||||
Numpad8,
|
||||
Numpad9,
|
||||
NumpadAdd,
|
||||
NumpadDivide,
|
||||
NumpadDecimal,
|
||||
NumpadComma,
|
||||
NumpadEnter,
|
||||
NumpadEquals,
|
||||
NumpadMultiply,
|
||||
NumpadSubtract,
|
||||
|
||||
AbntC1,
|
||||
AbntC2,
|
||||
Apostrophe,
|
||||
Apps,
|
||||
Asterisk,
|
||||
At,
|
||||
Ax,
|
||||
Backslash,
|
||||
Calculator,
|
||||
Capital,
|
||||
Colon,
|
||||
Comma,
|
||||
Convert,
|
||||
Equals,
|
||||
Grave,
|
||||
Kana,
|
||||
Kanji,
|
||||
LAlt,
|
||||
LBracket,
|
||||
LControl,
|
||||
LShift,
|
||||
LWin,
|
||||
Mail,
|
||||
MediaSelect,
|
||||
MediaStop,
|
||||
Minus,
|
||||
Mute,
|
||||
MyComputer,
|
||||
// also called "Next"
|
||||
NavigateForward,
|
||||
// also called "Prior"
|
||||
NavigateBackward,
|
||||
NextTrack,
|
||||
NoConvert,
|
||||
OEM102,
|
||||
Period,
|
||||
PlayPause,
|
||||
Plus,
|
||||
Power,
|
||||
PrevTrack,
|
||||
RAlt,
|
||||
RBracket,
|
||||
RControl,
|
||||
RShift,
|
||||
RWin,
|
||||
Semicolon,
|
||||
Slash,
|
||||
Sleep,
|
||||
Stop,
|
||||
Sysrq,
|
||||
Tab,
|
||||
Underline,
|
||||
Unlabeled,
|
||||
VolumeDown,
|
||||
VolumeUp,
|
||||
Wake,
|
||||
WebBack,
|
||||
WebFavorites,
|
||||
WebForward,
|
||||
WebHome,
|
||||
WebRefresh,
|
||||
WebSearch,
|
||||
WebStop,
|
||||
Yen,
|
||||
Copy,
|
||||
Paste,
|
||||
Cut,
|
||||
}
|
6
src/backends/mod.rs
Normal file
6
src/backends/mod.rs
Normal file
|
@ -0,0 +1,6 @@
|
|||
mod keycodes;
|
||||
mod window_event;
|
||||
mod xcb;
|
||||
mod xlib;
|
||||
|
||||
pub trait WindowServerBackend {}
|
238
src/backends/window_event.rs
Normal file
238
src/backends/window_event.rs
Normal file
|
@ -0,0 +1,238 @@
|
|||
#![allow(dead_code)]
|
||||
use x11::xlib::Window;
|
||||
|
||||
use super::keycodes::{MouseButton, VirtualKeyCode};
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum WindowEvent {
|
||||
KeyEvent {
|
||||
window: Window,
|
||||
event: KeyEvent,
|
||||
},
|
||||
ButtonEvent {
|
||||
window: Window,
|
||||
event: ButtonEvent,
|
||||
},
|
||||
MotionEvent {
|
||||
window: Window,
|
||||
event: MotionEvent,
|
||||
},
|
||||
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)]
|
||||
pub enum KeyState {
|
||||
Pressed,
|
||||
Released,
|
||||
}
|
||||
|
||||
#[derive(
|
||||
Debug, PartialEq, Eq, PartialOrd, Ord, Hash, Clone, Copy,
|
||||
)]
|
||||
#[repr(u8)]
|
||||
pub enum ModifierKey {
|
||||
Shift,
|
||||
ShiftLock,
|
||||
Control,
|
||||
Alt,
|
||||
AltGr,
|
||||
/// Windows key on most keyboards
|
||||
Super,
|
||||
NumLock,
|
||||
}
|
||||
|
||||
#[derive(Default, Debug, Clone)]
|
||||
pub struct ModifierState {
|
||||
modifiers: std::collections::HashSet<ModifierKey>,
|
||||
}
|
||||
|
||||
impl ModifierState {
|
||||
pub fn new() -> Self {
|
||||
Self::default()
|
||||
}
|
||||
|
||||
pub fn set_modifier(&mut self, modifier: ModifierKey) {
|
||||
self.modifiers.insert(modifier);
|
||||
}
|
||||
|
||||
pub fn unset_modifier(&mut self, modifier: ModifierKey) {
|
||||
self.modifiers.remove(&modifier);
|
||||
}
|
||||
|
||||
pub fn get_modifier(&mut self, modifier: ModifierKey) -> bool {
|
||||
self.modifiers.contains(&modifier)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct KeyEvent {
|
||||
state: KeyState,
|
||||
keycode: VirtualKeyCode,
|
||||
modifierstate: ModifierState,
|
||||
}
|
||||
|
||||
impl KeyEvent {
|
||||
pub fn new(
|
||||
state: KeyState,
|
||||
keycode: VirtualKeyCode,
|
||||
modifierstate: ModifierState,
|
||||
) -> Self {
|
||||
Self {
|
||||
state,
|
||||
keycode,
|
||||
modifierstate,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct ButtonEvent {
|
||||
state: KeyState,
|
||||
keycode: MouseButton,
|
||||
modifierstate: ModifierState,
|
||||
}
|
||||
|
||||
impl ButtonEvent {
|
||||
pub fn new(
|
||||
state: KeyState,
|
||||
keycode: MouseButton,
|
||||
modifierstate: ModifierState,
|
||||
) -> Self {
|
||||
Self {
|
||||
state,
|
||||
keycode,
|
||||
modifierstate,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct MotionEvent {
|
||||
position: [i32; 2],
|
||||
}
|
||||
|
||||
impl MotionEvent {
|
||||
pub fn new(position: [i32; 2]) -> Self {
|
||||
Self { position }
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct MapEvent {
|
||||
window: Window,
|
||||
}
|
||||
|
||||
impl MapEvent {
|
||||
pub fn new(window: Window) -> Self {
|
||||
Self { window }
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct UnmapEvent {
|
||||
window: Window,
|
||||
}
|
||||
|
||||
impl UnmapEvent {
|
||||
pub fn new(window: Window) -> Self {
|
||||
Self { window }
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct EnterEvent {}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct DestroyEvent {
|
||||
window: Window,
|
||||
}
|
||||
|
||||
impl DestroyEvent {
|
||||
pub fn new(window: Window) -> Self {
|
||||
Self { window }
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct CreateEvent {
|
||||
window: Window,
|
||||
position: [i32; 2],
|
||||
size: [i32; 2],
|
||||
}
|
||||
|
||||
impl CreateEvent {
|
||||
pub fn new(
|
||||
window: Window,
|
||||
position: [i32; 2],
|
||||
size: [i32; 2],
|
||||
) -> Self {
|
||||
Self {
|
||||
window,
|
||||
position,
|
||||
size,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct ConfigureEvent {
|
||||
pub window: Window,
|
||||
pub position: [i32; 2],
|
||||
pub size: [i32; 2],
|
||||
}
|
||||
|
||||
impl ConfigureEvent {
|
||||
pub fn new(
|
||||
window: Window,
|
||||
position: [i32; 2],
|
||||
size: [i32; 2],
|
||||
) -> Self {
|
||||
Self {
|
||||
window,
|
||||
position,
|
||||
size,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct FullscreenEvent {
|
||||
new_fullscreen: bool,
|
||||
}
|
||||
|
||||
impl FullscreenEvent {
|
||||
pub fn new(new_fullscreen: bool) -> Self {
|
||||
Self { new_fullscreen }
|
||||
}
|
||||
}
|
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
2054
src/backends/xlib/keysym.rs
Normal file
2054
src/backends/xlib/keysym.rs
Normal file
File diff suppressed because it is too large
Load diff
535
src/backends/xlib/mod.rs
Normal file
535
src/backends/xlib/mod.rs
Normal file
|
@ -0,0 +1,535 @@
|
|||
#![allow(non_upper_case_globals)]
|
||||
pub mod keysym;
|
||||
|
||||
use std::ptr::null;
|
||||
|
||||
use x11::xlib::{
|
||||
AnyModifier, Atom, ButtonPress, ButtonPressMask, ButtonRelease,
|
||||
ButtonReleaseMask, ClientMessage, ConfigureRequest, CreateNotify,
|
||||
DestroyNotify, EnterWindowMask, FocusChangeMask, GrabModeAsync,
|
||||
KeyPress, KeyPressMask, KeyRelease, KeyReleaseMask, MapNotify,
|
||||
MapRequest, MotionNotify, PropertyChangeMask, PropertyNewValue,
|
||||
PropertyNotify, StructureNotifyMask, UnmapNotify, Window,
|
||||
XButtonEvent, XClientMessageEvent, XConfigureRequestEvent,
|
||||
XConfigureWindow, XCreateWindowEvent, XDestroyWindowEvent,
|
||||
XEvent, XGrabButton, XGrabKey, XInternAtom, XKeyEvent,
|
||||
XKeysymToKeycode, XLookupKeysym, XMapRequestEvent, XMapWindow,
|
||||
XMotionEvent, XNextEvent, XPropertyEvent, XRootWindow,
|
||||
XSelectInput, XUnmapEvent, XWindowChanges,
|
||||
};
|
||||
|
||||
use crate::backends::window_event::{
|
||||
ButtonEvent, KeyEvent, KeyState, ModifierKey,
|
||||
};
|
||||
|
||||
use self::keysym::{
|
||||
keysym_to_virtual_keycode, mouse_button_to_xbutton,
|
||||
virtual_keycode_to_keysym, xev_to_mouse_button,
|
||||
};
|
||||
|
||||
use super::{
|
||||
keycodes::{MouseButton, VirtualKeyCode},
|
||||
window_event::{
|
||||
ConfigureEvent, CreateEvent, DestroyEvent, FullscreenEvent,
|
||||
MapEvent, ModifierState, MotionEvent, UnmapEvent,
|
||||
WindowEvent,
|
||||
},
|
||||
};
|
||||
|
||||
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,
|
||||
}
|
||||
|
||||
// xlib backend
|
||||
pub struct XLib {
|
||||
display: *mut x11::xlib::Display,
|
||||
modifier_state: ModifierState,
|
||||
atoms: Atoms,
|
||||
screen: i32,
|
||||
}
|
||||
|
||||
impl Drop for XLib {
|
||||
fn drop(&mut self) {
|
||||
unsafe {
|
||||
x11::xlib::XCloseDisplay(self.display);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl XLib {
|
||||
pub fn new() -> Self {
|
||||
let (display, screen) = unsafe {
|
||||
let display = x11::xlib::XOpenDisplay(null());
|
||||
let screen = x11::xlib::XDefaultScreen(display);
|
||||
|
||||
(display, screen)
|
||||
};
|
||||
|
||||
Self {
|
||||
display,
|
||||
screen,
|
||||
atoms: Self::init_atoms(display),
|
||||
modifier_state: Default::default(),
|
||||
}
|
||||
}
|
||||
|
||||
fn root_window(&self) -> Window {
|
||||
unsafe { XRootWindow(self.display, self.screen) }
|
||||
}
|
||||
|
||||
fn init_atoms(display: *mut x11::xlib::Display) -> Atoms {
|
||||
unsafe {
|
||||
let wm_protocols = XInternAtom(
|
||||
display,
|
||||
b"WM_PROTOCOLS\0".as_ptr() as *const _,
|
||||
0,
|
||||
);
|
||||
let wm_state = XInternAtom(
|
||||
display,
|
||||
b"WM_STATE\0".as_ptr() as *const _,
|
||||
0,
|
||||
);
|
||||
let wm_delete_window = XInternAtom(
|
||||
display,
|
||||
b"WM_DELETE_WINDOW\0".as_ptr() as *const _,
|
||||
0,
|
||||
);
|
||||
let wm_take_focus = XInternAtom(
|
||||
display,
|
||||
b"WM_TAKE_FOCUS\0".as_ptr() as *const _,
|
||||
0,
|
||||
);
|
||||
let net_supported = XInternAtom(
|
||||
display,
|
||||
b"_NET_SUPPORTED\0".as_ptr() as *const _,
|
||||
0,
|
||||
);
|
||||
let net_active_window = XInternAtom(
|
||||
display,
|
||||
b"_NET_ACTIVE_WINDOW\0".as_ptr() as *const _,
|
||||
0,
|
||||
);
|
||||
let net_client_list = XInternAtom(
|
||||
display,
|
||||
b"_NET_CLIENT_LIST\0".as_ptr() as *const _,
|
||||
0,
|
||||
);
|
||||
let net_wm_name = XInternAtom(
|
||||
display,
|
||||
b"_NET_WM_NAME\0".as_ptr() as *const _,
|
||||
0,
|
||||
);
|
||||
let net_wm_state = XInternAtom(
|
||||
display,
|
||||
b"_NET_WM_STATE\0".as_ptr() as *const _,
|
||||
0,
|
||||
);
|
||||
let net_wm_state_fullscreen = XInternAtom(
|
||||
display,
|
||||
b"_NET_WM_STATE_FULLSCREEN\0".as_ptr() as *const _,
|
||||
0,
|
||||
);
|
||||
let net_wm_window_type = XInternAtom(
|
||||
display,
|
||||
b"_NET_WM_WINDOW_TYPE\0".as_ptr() as *const _,
|
||||
0,
|
||||
);
|
||||
let net_wm_window_type_dialog = XInternAtom(
|
||||
display,
|
||||
b"_NET_WM_WINDOW_TYPE_DIALOG\0".as_ptr() as *const _,
|
||||
0,
|
||||
);
|
||||
|
||||
Atoms {
|
||||
wm_protocols,
|
||||
wm_state,
|
||||
wm_delete_window,
|
||||
wm_take_focus,
|
||||
net_supported,
|
||||
net_active_window,
|
||||
net_client_list,
|
||||
net_wm_name,
|
||||
net_wm_state,
|
||||
net_wm_state_fullscreen,
|
||||
net_wm_window_type,
|
||||
net_wm_window_type_dialog,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn update_modifier_state(
|
||||
&mut self,
|
||||
keyevent: &x11::xlib::XKeyEvent,
|
||||
) {
|
||||
//keyevent.keycode
|
||||
let keysym = self.keyev_to_keysym(keyevent);
|
||||
|
||||
use x11::keysym::*;
|
||||
|
||||
let modifier = match keysym as u32 {
|
||||
XK_Shift_L | XK_Shift_R => Some(ModifierKey::Shift),
|
||||
XK_Control_L | XK_Control_R => Some(ModifierKey::Control),
|
||||
XK_Alt_L | XK_Alt_R => Some(ModifierKey::Alt),
|
||||
XK_ISO_Level3_Shift => Some(ModifierKey::AltGr),
|
||||
XK_Caps_Lock => Some(ModifierKey::ShiftLock),
|
||||
XK_Num_Lock => Some(ModifierKey::NumLock),
|
||||
XK_Win_L | XK_Win_R => Some(ModifierKey::Super),
|
||||
XK_Super_L | XK_Super_R => Some(ModifierKey::Super),
|
||||
_ => None,
|
||||
};
|
||||
|
||||
if let Some(modifier) = modifier {
|
||||
match keyevent.type_ {
|
||||
KeyPress => {
|
||||
self.modifier_state.set_modifier(modifier)
|
||||
}
|
||||
KeyRelease => {
|
||||
self.modifier_state.unset_modifier(modifier)
|
||||
}
|
||||
_ => unreachable!(
|
||||
"keyyevent != (KeyPress | KeyRelease)"
|
||||
),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn keyev_to_keysym(&self, keyev: &XKeyEvent) -> u32 {
|
||||
unsafe {
|
||||
XLookupKeysym(keyev as *const _ as *mut _, 0) as u32
|
||||
}
|
||||
}
|
||||
|
||||
pub fn next_event(&self) -> XEvent {
|
||||
unsafe {
|
||||
let mut event = std::mem::MaybeUninit::zeroed();
|
||||
XNextEvent(self.display, event.as_mut_ptr());
|
||||
|
||||
event.assume_init()
|
||||
}
|
||||
}
|
||||
|
||||
/// should probabbly make this use some variable that the user can chose for selected events.
|
||||
fn map_window(&self, window: Window) {
|
||||
unsafe {
|
||||
XMapWindow(self.display, window);
|
||||
|
||||
XSelectInput(
|
||||
self.display,
|
||||
window,
|
||||
EnterWindowMask
|
||||
| FocusChangeMask
|
||||
| PropertyChangeMask
|
||||
| StructureNotifyMask,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
fn select_input(&self, window: Window) {
|
||||
unsafe {
|
||||
XSelectInput(
|
||||
self.display,
|
||||
window,
|
||||
EnterWindowMask
|
||||
| FocusChangeMask
|
||||
| PropertyChangeMask
|
||||
| StructureNotifyMask
|
||||
| ButtonPressMask
|
||||
| ButtonReleaseMask
|
||||
| KeyPressMask
|
||||
| KeyReleaseMask,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
fn configure_window(
|
||||
&self,
|
||||
window: Window,
|
||||
event: &ConfigureEvent,
|
||||
) {
|
||||
unsafe {
|
||||
let mut wc =
|
||||
std::mem::MaybeUninit::<XWindowChanges>::zeroed()
|
||||
.assume_init();
|
||||
|
||||
wc.x = event.position[0];
|
||||
wc.y = event.position[1];
|
||||
|
||||
wc.width = event.size[0];
|
||||
wc.height = event.size[1];
|
||||
|
||||
XConfigureWindow(
|
||||
self.display,
|
||||
window,
|
||||
(1 << 4) - 1,
|
||||
&mut wc,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
fn handle_window_event(&mut self, event: WindowEvent) {
|
||||
match event {
|
||||
WindowEvent::MapEvent { window, .. } => {
|
||||
self.map_window(window);
|
||||
}
|
||||
WindowEvent::ConfigureEvent { window, event } => {
|
||||
self.configure_window(window, &event);
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
|
||||
fn grab_key(&self, keycode: VirtualKeyCode) {
|
||||
unsafe {
|
||||
XGrabKey(
|
||||
self.display,
|
||||
XKeysymToKeycode(
|
||||
self.display,
|
||||
virtual_keycode_to_keysym(keycode).unwrap()
|
||||
as u64,
|
||||
) as i32,
|
||||
AnyModifier,
|
||||
self.root_window(),
|
||||
1,
|
||||
GrabModeAsync,
|
||||
GrabModeAsync,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
fn grab_button(&self, button: MouseButton) {
|
||||
unsafe {
|
||||
XGrabButton(
|
||||
self.display,
|
||||
mouse_button_to_xbutton(button) as u32,
|
||||
AnyModifier,
|
||||
self.root_window(),
|
||||
1,
|
||||
(ButtonPressMask | ButtonReleaseMask) as u32,
|
||||
GrabModeAsync,
|
||||
GrabModeAsync,
|
||||
0,
|
||||
0,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
fn next_window_event(&mut self) -> WindowEvent {
|
||||
loop {
|
||||
let event = self.next_event();
|
||||
|
||||
match event.get_type() {
|
||||
KeyPress | KeyRelease => {
|
||||
let key_ev: &XKeyEvent = event.as_ref();
|
||||
|
||||
self.update_modifier_state(key_ev);
|
||||
|
||||
let keycode = keysym_to_virtual_keycode(
|
||||
self.keyev_to_keysym(event.as_ref()),
|
||||
);
|
||||
|
||||
if let Some(keycode) = keycode {
|
||||
return WindowEvent::KeyEvent {
|
||||
window: key_ev.subwindow,
|
||||
event: KeyEvent::new(
|
||||
match event.get_type() {
|
||||
KeyPress => KeyState::Pressed,
|
||||
KeyRelease => KeyState::Released,
|
||||
_ => unreachable!(),
|
||||
},
|
||||
keycode,
|
||||
self.modifier_state.clone(),
|
||||
),
|
||||
};
|
||||
}
|
||||
}
|
||||
ButtonPress | ButtonRelease => {
|
||||
let button_ev: &XButtonEvent = event.as_ref();
|
||||
let button = xev_to_mouse_button(button_ev);
|
||||
|
||||
if let Some(button) = button {
|
||||
return WindowEvent::ButtonEvent {
|
||||
window: button_ev.subwindow,
|
||||
event: ButtonEvent::new(
|
||||
match event.get_type() {
|
||||
ButtonPress => KeyState::Pressed,
|
||||
ButtonRelease => {
|
||||
KeyState::Released
|
||||
}
|
||||
_ => unreachable!(),
|
||||
},
|
||||
button,
|
||||
self.modifier_state.clone(),
|
||||
),
|
||||
};
|
||||
}
|
||||
}
|
||||
MotionNotify => {
|
||||
let motion_ev: &XMotionEvent = event.as_ref();
|
||||
|
||||
return WindowEvent::MotionEvent {
|
||||
window: motion_ev.subwindow,
|
||||
event: MotionEvent::new([
|
||||
motion_ev.x_root,
|
||||
motion_ev.y_root,
|
||||
]),
|
||||
};
|
||||
}
|
||||
MapRequest => {
|
||||
// MapEvent
|
||||
let map_ev: &XMapRequestEvent = event.as_ref();
|
||||
|
||||
return WindowEvent::MapEvent {
|
||||
window: map_ev.window,
|
||||
event: MapEvent::new(map_ev.window),
|
||||
};
|
||||
}
|
||||
MapNotify => {
|
||||
// MapEvent
|
||||
let map_ev: &XMapRequestEvent = event.as_ref();
|
||||
|
||||
return WindowEvent::MapEvent {
|
||||
window: map_ev.window,
|
||||
event: MapEvent::new(map_ev.window),
|
||||
};
|
||||
}
|
||||
UnmapNotify => {
|
||||
// UnmapEvent
|
||||
let unmap_ev: &XUnmapEvent = event.as_ref();
|
||||
|
||||
return WindowEvent::UnmapEvent {
|
||||
window: unmap_ev.window,
|
||||
event: UnmapEvent::new(unmap_ev.window),
|
||||
};
|
||||
}
|
||||
CreateNotify => {
|
||||
// CreateEvent
|
||||
let create_ev: &XCreateWindowEvent =
|
||||
event.as_ref();
|
||||
|
||||
return WindowEvent::CreateEvent {
|
||||
window: create_ev.window,
|
||||
event: CreateEvent::new(
|
||||
create_ev.window,
|
||||
[create_ev.x, create_ev.y],
|
||||
[create_ev.width, create_ev.height],
|
||||
),
|
||||
};
|
||||
}
|
||||
DestroyNotify => {
|
||||
// DestroyEvent
|
||||
let destroy_ev: &XDestroyWindowEvent =
|
||||
event.as_ref();
|
||||
|
||||
return WindowEvent::DestroyEvent {
|
||||
window: destroy_ev.window,
|
||||
event: DestroyEvent::new(destroy_ev.window),
|
||||
};
|
||||
}
|
||||
ConfigureRequest => {
|
||||
// ConfigureEvent
|
||||
let configure_ev: &XConfigureRequestEvent =
|
||||
event.as_ref();
|
||||
|
||||
return WindowEvent::ConfigureEvent {
|
||||
window: configure_ev.window,
|
||||
event: ConfigureEvent::new(
|
||||
configure_ev.window,
|
||||
[configure_ev.x, configure_ev.y],
|
||||
[configure_ev.width, configure_ev.height],
|
||||
),
|
||||
};
|
||||
}
|
||||
ClientMessage => {
|
||||
let msg_ev: &XClientMessageEvent = event.as_ref();
|
||||
|
||||
// not sure?
|
||||
}
|
||||
PropertyNotify => {
|
||||
let property_ev: &XPropertyEvent = event.as_ref();
|
||||
|
||||
if property_ev.atom
|
||||
== self.atoms.net_wm_state_fullscreen
|
||||
{
|
||||
return WindowEvent::FullscreenEvent {
|
||||
window: property_ev.window,
|
||||
event: FullscreenEvent::new(
|
||||
property_ev.state == PropertyNewValue,
|
||||
),
|
||||
};
|
||||
}
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use x11::xlib::{
|
||||
XBlackPixel, XCreateSimpleWindow, XCreateWindow,
|
||||
XDefaultScreen,
|
||||
};
|
||||
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn window_events() {
|
||||
let mut xlib = XLib::new();
|
||||
|
||||
//xlib.grab_key(VirtualKeyCode::A);
|
||||
|
||||
let window = unsafe {
|
||||
//XCreateWindow(xlib.display, , 10, 9, 8, 7, 6, 5, 4, 3, 2, 1)
|
||||
let black_pixel = XBlackPixel(
|
||||
xlib.display,
|
||||
XDefaultScreen(xlib.display),
|
||||
);
|
||||
let window = XCreateSimpleWindow(
|
||||
xlib.display,
|
||||
xlib.root_window(),
|
||||
10,
|
||||
10,
|
||||
100,
|
||||
100,
|
||||
1,
|
||||
black_pixel,
|
||||
black_pixel,
|
||||
);
|
||||
|
||||
XMapWindow(xlib.display, window);
|
||||
xlib.select_input(window);
|
||||
|
||||
window
|
||||
};
|
||||
|
||||
loop {
|
||||
let event = xlib.next_window_event();
|
||||
println!("{:#?}", event);
|
||||
}
|
||||
}
|
||||
|
||||
//#[test]
|
||||
// fn window_events() {
|
||||
// let mut xlib = XLib::new();
|
||||
|
||||
// loop {
|
||||
// if let Some(event) =
|
||||
// xlib.xevent_to_window_event(xlib.next_event())
|
||||
// {
|
||||
// println!("{:#?}", event);
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
}
|
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -217,7 +217,8 @@ impl ClientState {
|
|||
if client.is_transient()
|
||||
&& self.contains(&client.transient_for.unwrap())
|
||||
{
|
||||
let transient = self.get(&client.transient_for.unwrap()).unwrap();
|
||||
let transient =
|
||||
self.get(&client.transient_for.unwrap()).unwrap();
|
||||
|
||||
client.position = {
|
||||
(
|
||||
|
@ -271,7 +272,9 @@ impl ClientState {
|
|||
|| 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()
|
||||
}
|
||||
|
||||
|
@ -283,32 +286,44 @@ impl ClientState {
|
|||
.filter(move |&(k, _)| self.is_client_visible(k))
|
||||
}
|
||||
|
||||
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())
|
||||
}
|
||||
|
||||
pub fn iter_hidden(&self) -> impl Iterator<Item = (&u64, &Client)> {
|
||||
pub fn iter_hidden(
|
||||
&self,
|
||||
) -> impl Iterator<Item = (&u64, &Client)> {
|
||||
self.iter_all_clients()
|
||||
.filter(move |&(k, _)| !self.is_client_visible(k))
|
||||
}
|
||||
|
||||
pub fn iter_transient(&self) -> impl Iterator<Item = (&u64, &Client)> {
|
||||
pub fn iter_transient(
|
||||
&self,
|
||||
) -> impl Iterator<Item = (&u64, &Client)> {
|
||||
self.iter_floating().filter(|&(_, c)| c.is_transient())
|
||||
}
|
||||
|
||||
pub fn iter_visible(&self) -> impl Iterator<Item = (&u64, &Client)> {
|
||||
pub fn iter_visible(
|
||||
&self,
|
||||
) -> impl Iterator<Item = (&u64, &Client)> {
|
||||
self.iter_all_clients()
|
||||
.filter(move |&(k, _)| self.is_client_visible(k))
|
||||
}
|
||||
|
||||
#[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.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
|
||||
.get_current()
|
||||
.master
|
||||
|
@ -316,7 +331,9 @@ impl ClientState {
|
|||
.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
|
||||
.get_current()
|
||||
.aux
|
||||
|
@ -441,12 +458,15 @@ impl ClientState {
|
|||
// transient clients cannot be tiled
|
||||
match floating_client.is_transient() {
|
||||
true => {
|
||||
self.floating_clients.insert(key, floating_client);
|
||||
self.floating_clients
|
||||
.insert(key, floating_client);
|
||||
}
|
||||
|
||||
false => {
|
||||
self.clients.insert(key, floating_client);
|
||||
self.virtual_screens.get_mut_current().insert(&key);
|
||||
self.virtual_screens
|
||||
.get_mut_current()
|
||||
.insert(&key);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -464,7 +484,9 @@ impl ClientState {
|
|||
K: ClientKey,
|
||||
{
|
||||
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);
|
||||
|
||||
// we removed a client so the layout changed, rearrange
|
||||
|
@ -473,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
|
||||
K: ClientKey,
|
||||
{
|
||||
|
@ -502,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
|
||||
K: ClientKey,
|
||||
{
|
||||
|
@ -637,7 +665,8 @@ impl ClientState {
|
|||
self.master_size / 2.0
|
||||
};
|
||||
|
||||
let master_width = (effective_width as f32 * master_size) as i32;
|
||||
let master_width =
|
||||
(effective_width as f32 * master_size) as i32;
|
||||
let aux_width = effective_width - master_width;
|
||||
|
||||
(master_width, aux_width)
|
||||
|
@ -665,7 +694,8 @@ impl ClientState {
|
|||
master_height - gap * 2 - self.border_size * 2,
|
||||
);
|
||||
|
||||
let position = (gap * 2, master_height * i as i32 + gap * 2);
|
||||
let position =
|
||||
(gap * 2, master_height * i as i32 + gap * 2);
|
||||
|
||||
if let Some(client) = self.clients.get_mut(key) {
|
||||
*client = Client {
|
||||
|
@ -683,8 +713,10 @@ impl ClientState {
|
|||
aux_height - gap * 2 - self.border_size * 2,
|
||||
);
|
||||
|
||||
let position =
|
||||
(master_width + gap * 2, aux_height * i as i32 + gap * 2);
|
||||
let position = (
|
||||
master_width + gap * 2,
|
||||
aux_height * i as i32 + gap * 2,
|
||||
);
|
||||
|
||||
if let Some(client) = self.clients.get_mut(key) {
|
||||
*client = Client {
|
||||
|
@ -720,7 +752,8 @@ impl VirtualScreen {
|
|||
where
|
||||
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
|
||||
|
@ -766,8 +799,11 @@ impl VirtualScreen {
|
|||
self.aux.extend(self.master.drain(index..=index));
|
||||
}
|
||||
None => {
|
||||
let index =
|
||||
self.aux.iter().position(|&k| k == key.key()).unwrap();
|
||||
let index = self
|
||||
.aux
|
||||
.iter()
|
||||
.position(|&k| k == key.key())
|
||||
.unwrap();
|
||||
self.master.extend(self.aux.drain(index..=index));
|
||||
}
|
||||
}
|
||||
|
@ -814,7 +850,9 @@ impl VirtualScreenStore {
|
|||
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()
|
||||
}
|
||||
|
||||
|
@ -861,7 +899,9 @@ impl<T> Into<Option<T>> for ClientEntry<T> {
|
|||
fn into(self) -> Option<T> {
|
||||
match self {
|
||||
Self::Vacant => None,
|
||||
Self::Tiled(client) | Self::Floating(client) => Some(client),
|
||||
Self::Tiled(client) | Self::Floating(client) => {
|
||||
Some(client)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
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()));
|
||||
}
|
||||
}
|
15
src/main.rs
15
src/main.rs
|
@ -7,6 +7,11 @@ use log4rs::{
|
|||
};
|
||||
use state::WMConfig;
|
||||
|
||||
#[macro_use]
|
||||
extern crate num_derive;
|
||||
|
||||
mod backends;
|
||||
mod client_logic;
|
||||
mod clients;
|
||||
mod clients2;
|
||||
mod state;
|
||||
|
@ -18,9 +23,11 @@ fn init_logger() {
|
|||
"{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()
|
||||
.encoder(encoder)
|
||||
|
@ -28,7 +35,9 @@ fn init_logger() {
|
|||
.unwrap();
|
||||
|
||||
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)))
|
||||
.build(
|
||||
Root::builder()
|
||||
|
|
134
src/state.rs
134
src/state.rs
|
@ -151,7 +151,9 @@ impl WindowManager {
|
|||
.get_focused()
|
||||
.into_option()
|
||||
.map(|c| c.key())
|
||||
.and_then(|k| Some(wm.clients.toggle_floating(&k)));
|
||||
.and_then(|k| {
|
||||
Some(wm.clients.toggle_floating(&k))
|
||||
});
|
||||
|
||||
wm.arrange_clients();
|
||||
},
|
||||
|
@ -224,15 +226,24 @@ impl WindowManager {
|
|||
}
|
||||
|
||||
fn add_vs_switch_keybinds(&mut self) {
|
||||
fn rotate_west<const N: usize>(wm: &mut WindowManager, _: &XKeyEvent) {
|
||||
fn rotate_west<const N: usize>(
|
||||
wm: &mut WindowManager,
|
||||
_: &XKeyEvent,
|
||||
) {
|
||||
wm.rotate_virtual_screen(Direction::West(N));
|
||||
}
|
||||
|
||||
fn rotate_east<const N: usize>(wm: &mut WindowManager, _: &XKeyEvent) {
|
||||
fn rotate_east<const N: usize>(
|
||||
wm: &mut WindowManager,
|
||||
_: &XKeyEvent,
|
||||
) {
|
||||
wm.rotate_virtual_screen(Direction::East(N));
|
||||
}
|
||||
|
||||
fn goto_nth<const N: usize>(wm: &mut WindowManager, _: &XKeyEvent) {
|
||||
fn goto_nth<const N: usize>(
|
||||
wm: &mut WindowManager,
|
||||
_: &XKeyEvent,
|
||||
) {
|
||||
wm.go_to_nth_virtual_screen(N)
|
||||
}
|
||||
|
||||
|
@ -333,13 +344,23 @@ impl WindowManager {
|
|||
match event.get_type() {
|
||||
xlib::MapRequest => self.map_request(&event),
|
||||
xlib::UnmapNotify => self.unmap_notify(&event),
|
||||
xlib::ConfigureRequest => self.configure_request(&event),
|
||||
xlib::ConfigureRequest => {
|
||||
self.configure_request(&event)
|
||||
}
|
||||
xlib::EnterNotify => self.enter_notify(&event),
|
||||
xlib::DestroyNotify => self.destroy_notify(&event),
|
||||
xlib::ButtonPress => self.button_press(event.as_ref()),
|
||||
xlib::ButtonRelease => self.button_release(event.as_ref()),
|
||||
xlib::MotionNotify => self.motion_notify(event.as_ref()),
|
||||
xlib::KeyPress => self.handle_keybinds(event.as_ref()),
|
||||
xlib::ButtonPress => {
|
||||
self.button_press(event.as_ref())
|
||||
}
|
||||
xlib::ButtonRelease => {
|
||||
self.button_release(event.as_ref())
|
||||
}
|
||||
xlib::MotionNotify => {
|
||||
self.motion_notify(event.as_ref())
|
||||
}
|
||||
xlib::KeyPress => {
|
||||
self.handle_keybinds(event.as_ref())
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
|
@ -354,7 +375,8 @@ impl WindowManager {
|
|||
}
|
||||
|
||||
fn kill_client(&mut self, _event: &XKeyEvent) {
|
||||
if let Some(client) = self.clients.get_focused().into_option() {
|
||||
if let Some(client) = self.clients.get_focused().into_option()
|
||||
{
|
||||
self.xlib.kill_client(client);
|
||||
}
|
||||
}
|
||||
|
@ -365,7 +387,8 @@ impl WindowManager {
|
|||
for kb in self.keybinds.clone().into_iter() {
|
||||
if let KeyOrButton::Key(keycode, modmask) = kb.key {
|
||||
if keycode as u32 == event.keycode
|
||||
&& modmask & clean_mask == event.state & clean_mask
|
||||
&& modmask & clean_mask
|
||||
== event.state & clean_mask
|
||||
{
|
||||
(kb.closure)(self, event);
|
||||
}
|
||||
|
@ -409,8 +432,12 @@ impl WindowManager {
|
|||
|
||||
fn focus_any(&mut self) {
|
||||
// focus first client in all visible clients
|
||||
let to_focus =
|
||||
self.clients.iter_visible().next().map(|(k, _)| k).cloned();
|
||||
let to_focus = self
|
||||
.clients
|
||||
.iter_visible()
|
||||
.next()
|
||||
.map(|(k, _)| k)
|
||||
.cloned();
|
||||
|
||||
if let Some(key) = to_focus {
|
||||
self.focus_client(&key, false);
|
||||
|
@ -418,7 +445,8 @@ impl WindowManager {
|
|||
}
|
||||
|
||||
fn focus_master_stack(&mut self) {
|
||||
let focused = self.clients.get_focused().into_option().map(|c| c.key());
|
||||
let focused =
|
||||
self.clients.get_focused().into_option().map(|c| c.key());
|
||||
|
||||
let k = self
|
||||
.clients
|
||||
|
@ -436,7 +464,8 @@ impl WindowManager {
|
|||
}
|
||||
|
||||
fn focus_aux_stack(&mut self) {
|
||||
let focused = self.clients.get_focused().into_option().map(|c| c.key());
|
||||
let focused =
|
||||
self.clients.get_focused().into_option().map(|c| c.key());
|
||||
|
||||
let k = self
|
||||
.clients
|
||||
|
@ -454,12 +483,12 @@ impl WindowManager {
|
|||
}
|
||||
|
||||
fn focus_up(&mut self) {
|
||||
let focused = self.clients.get_focused().into_option().map(|c| c.key());
|
||||
let focused =
|
||||
self.clients.get_focused().into_option().map(|c| c.key());
|
||||
|
||||
let k = focused.and_then(|focused| {
|
||||
self.clients
|
||||
.get_stack_for_client(&focused)
|
||||
.and_then(|stack| {
|
||||
self.clients.get_stack_for_client(&focused).and_then(
|
||||
|stack| {
|
||||
stack
|
||||
.iter()
|
||||
.rev()
|
||||
|
@ -467,7 +496,8 @@ impl WindowManager {
|
|||
.skip(1)
|
||||
.next()
|
||||
.cloned()
|
||||
})
|
||||
},
|
||||
)
|
||||
});
|
||||
|
||||
if let Some(k) = k {
|
||||
|
@ -476,19 +506,20 @@ impl WindowManager {
|
|||
}
|
||||
|
||||
fn focus_down(&mut self) {
|
||||
let focused = self.clients.get_focused().into_option().map(|c| c.key());
|
||||
let focused =
|
||||
self.clients.get_focused().into_option().map(|c| c.key());
|
||||
|
||||
let k = focused.and_then(|focused| {
|
||||
self.clients
|
||||
.get_stack_for_client(&focused)
|
||||
.and_then(|stack| {
|
||||
self.clients.get_stack_for_client(&focused).and_then(
|
||||
|stack| {
|
||||
stack
|
||||
.iter()
|
||||
.skip_while(|&&k| k != focused)
|
||||
.skip(1)
|
||||
.next()
|
||||
.cloned()
|
||||
})
|
||||
},
|
||||
)
|
||||
});
|
||||
|
||||
if let Some(k) = k {
|
||||
|
@ -573,7 +604,9 @@ impl WindowManager {
|
|||
{
|
||||
Client::new_transient(
|
||||
window,
|
||||
self.xlib.get_window_size(window).unwrap_or((100, 100)),
|
||||
self.xlib
|
||||
.get_window_size(window)
|
||||
.unwrap_or((100, 100)),
|
||||
transient_window,
|
||||
)
|
||||
} else {
|
||||
|
@ -632,7 +665,10 @@ impl WindowManager {
|
|||
}
|
||||
|
||||
/// ensure event.subwindow refers to a valid client.
|
||||
fn start_move_resize_window(&mut self, event: &XButtonPressedEvent) {
|
||||
fn start_move_resize_window(
|
||||
&mut self,
|
||||
event: &XButtonPressedEvent,
|
||||
) {
|
||||
let window = event.subwindow;
|
||||
|
||||
match event.button {
|
||||
|
@ -641,15 +677,16 @@ impl WindowManager {
|
|||
self.arrange_clients();
|
||||
}
|
||||
|
||||
self.move_resize_window = MoveResizeInfo::Move(MoveInfoInner {
|
||||
window,
|
||||
starting_cursor_pos: (event.x, event.y),
|
||||
starting_window_pos: self
|
||||
.clients
|
||||
.get(&window)
|
||||
.unwrap()
|
||||
.position,
|
||||
});
|
||||
self.move_resize_window =
|
||||
MoveResizeInfo::Move(MoveInfoInner {
|
||||
window,
|
||||
starting_cursor_pos: (event.x, event.y),
|
||||
starting_window_pos: self
|
||||
.clients
|
||||
.get(&window)
|
||||
.unwrap()
|
||||
.position,
|
||||
});
|
||||
}
|
||||
3 => {
|
||||
if self.clients.set_floating(&window) {
|
||||
|
@ -679,7 +716,10 @@ impl WindowManager {
|
|||
}
|
||||
}
|
||||
|
||||
fn end_move_resize_window(&mut self, event: &XButtonReleasedEvent) {
|
||||
fn end_move_resize_window(
|
||||
&mut self,
|
||||
event: &XButtonReleasedEvent,
|
||||
) {
|
||||
if event.button == 1 || event.button == 3 {
|
||||
self.move_resize_window = MoveResizeInfo::None;
|
||||
}
|
||||
|
@ -718,8 +758,14 @@ impl WindowManager {
|
|||
{
|
||||
let size = &mut client.size;
|
||||
|
||||
size.0 = std::cmp::max(1, info.starting_window_size.0 + x);
|
||||
size.1 = std::cmp::max(1, info.starting_window_size.1 + y);
|
||||
size.0 = std::cmp::max(
|
||||
1,
|
||||
info.starting_window_size.0 + x,
|
||||
);
|
||||
size.1 = std::cmp::max(
|
||||
1,
|
||||
info.starting_window_size.1 + y,
|
||||
);
|
||||
|
||||
self.xlib.resize_client(client);
|
||||
}
|
||||
|
@ -734,10 +780,12 @@ impl WindowManager {
|
|||
match event.button {
|
||||
1 | 3 => match self.move_resize_window {
|
||||
MoveResizeInfo::None
|
||||
if self
|
||||
.xlib
|
||||
.are_masks_equal(event.state, self.config.mod_key)
|
||||
&& self.clients.contains(&event.subwindow) =>
|
||||
if self.xlib.are_masks_equal(
|
||||
event.state,
|
||||
self.config.mod_key,
|
||||
) && self
|
||||
.clients
|
||||
.contains(&event.subwindow) =>
|
||||
{
|
||||
self.start_move_resize_window(event)
|
||||
}
|
||||
|
|
162
src/xlib.rs
162
src/xlib.rs
|
@ -4,14 +4,16 @@ 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,
|
||||
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;
|
||||
|
||||
|
@ -45,7 +47,11 @@ impl KeyOrButton {
|
|||
pub fn key(keycode: i32, modmask: u32) -> Self {
|
||||
Self::Key(keycode, modmask)
|
||||
}
|
||||
pub fn button(button: u32, modmask: u32, buttonmask: i64) -> Self {
|
||||
pub fn button(
|
||||
button: u32,
|
||||
modmask: u32,
|
||||
buttonmask: i64,
|
||||
) -> Self {
|
||||
Self::Button(button, modmask, buttonmask as u64)
|
||||
}
|
||||
}
|
||||
|
@ -78,9 +84,10 @@ impl XLib {
|
|||
|
||||
pub fn init(&mut self) {
|
||||
unsafe {
|
||||
let mut window_attributes =
|
||||
std::mem::MaybeUninit::<xlib::XSetWindowAttributes>::zeroed()
|
||||
.assume_init();
|
||||
let mut window_attributes = std::mem::MaybeUninit::<
|
||||
xlib::XSetWindowAttributes,
|
||||
>::zeroed()
|
||||
.assume_init();
|
||||
|
||||
window_attributes.event_mask = SubstructureRedirectMask
|
||||
| StructureNotifyMask
|
||||
|
@ -117,7 +124,12 @@ impl XLib {
|
|||
#[allow(dead_code)]
|
||||
fn ungrab_global_keybings(&self, window: Window) {
|
||||
unsafe {
|
||||
XUngrabButton(self.dpy(), AnyButton as u32, AnyModifier, window);
|
||||
XUngrabButton(
|
||||
self.dpy(),
|
||||
AnyButton as u32,
|
||||
AnyModifier,
|
||||
window,
|
||||
);
|
||||
XUngrabKey(self.dpy(), AnyKey, AnyModifier, window);
|
||||
}
|
||||
}
|
||||
|
@ -143,10 +155,14 @@ impl XLib {
|
|||
pub fn squash_event(&self, event_type: i32) -> XEvent {
|
||||
unsafe {
|
||||
let mut event =
|
||||
std::mem::MaybeUninit::<xlib::XEvent>::zeroed().assume_init();
|
||||
std::mem::MaybeUninit::<xlib::XEvent>::zeroed()
|
||||
.assume_init();
|
||||
|
||||
while xlib::XCheckTypedEvent(self.dpy(), event_type, &mut event)
|
||||
!= 0
|
||||
while xlib::XCheckTypedEvent(
|
||||
self.dpy(),
|
||||
event_type,
|
||||
&mut event,
|
||||
) != 0
|
||||
{}
|
||||
|
||||
event
|
||||
|
@ -156,14 +172,19 @@ impl XLib {
|
|||
pub fn next_event(&self) -> XEvent {
|
||||
unsafe {
|
||||
let mut event =
|
||||
std::mem::MaybeUninit::<xlib::XEvent>::zeroed().assume_init();
|
||||
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) {
|
||||
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];
|
||||
|
@ -212,7 +233,8 @@ impl XLib {
|
|||
xlib::CurrentTime,
|
||||
);
|
||||
|
||||
let screen = xlib::XDefaultScreenOfDisplay(self.dpy()).as_ref();
|
||||
let screen =
|
||||
xlib::XDefaultScreenOfDisplay(self.dpy()).as_ref();
|
||||
|
||||
if let Some(screen) = screen {
|
||||
xlib::XSetWindowBorder(
|
||||
|
@ -248,7 +270,8 @@ impl XLib {
|
|||
xlib::CurrentTime,
|
||||
);
|
||||
|
||||
let screen = xlib::XDefaultScreenOfDisplay(self.dpy()).as_ref();
|
||||
let screen =
|
||||
xlib::XDefaultScreenOfDisplay(self.dpy()).as_ref();
|
||||
|
||||
if let Some(screen) = screen {
|
||||
xlib::XSetWindowBorder(
|
||||
|
@ -284,8 +307,10 @@ impl XLib {
|
|||
xlib::XConfigureWindow(
|
||||
self.dpy(),
|
||||
client.window,
|
||||
(xlib::CWY | xlib::CWX | xlib::CWHeight | xlib::CWWidth)
|
||||
as u32,
|
||||
(xlib::CWY
|
||||
| xlib::CWX
|
||||
| xlib::CWHeight
|
||||
| xlib::CWWidth) as u32,
|
||||
&mut windowchanges,
|
||||
);
|
||||
|
||||
|
@ -376,14 +401,18 @@ impl XLib {
|
|||
}
|
||||
}
|
||||
|
||||
pub fn get_window_size(&self, window: Window) -> Option<(i32, i32)> {
|
||||
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
|
||||
xlib::XGetWindowAttributes(self.dpy(), window, &mut wa)
|
||||
!= 0
|
||||
} {
|
||||
Some((wa.width, wa.height))
|
||||
} else {
|
||||
|
@ -401,7 +430,8 @@ impl XLib {
|
|||
};
|
||||
|
||||
if unsafe {
|
||||
xlib::XGetWindowAttributes(self.dpy(), window, &mut wa) != 0
|
||||
xlib::XGetWindowAttributes(self.dpy(), window, &mut wa)
|
||||
!= 0
|
||||
} {
|
||||
Some(wa)
|
||||
} else {
|
||||
|
@ -409,11 +439,18 @@ impl XLib {
|
|||
}
|
||||
}
|
||||
|
||||
pub fn get_transient_for_window(&self, window: Window) -> Option<Window> {
|
||||
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
|
||||
XGetTransientForHint(
|
||||
self.dpy(),
|
||||
window,
|
||||
&mut transient_for,
|
||||
) != 0
|
||||
} {
|
||||
Some(transient_for)
|
||||
} else {
|
||||
|
@ -515,13 +552,21 @@ impl XLib {
|
|||
|
||||
pub fn dimensions(&self) -> (i32, i32) {
|
||||
unsafe {
|
||||
let mut wa =
|
||||
std::mem::MaybeUninit::<xlib::XWindowAttributes>::zeroed()
|
||||
.assume_init();
|
||||
let mut wa = std::mem::MaybeUninit::<
|
||||
xlib::XWindowAttributes,
|
||||
>::zeroed()
|
||||
.assume_init();
|
||||
|
||||
xlib::XGetWindowAttributes(self.dpy(), self.root, &mut wa);
|
||||
xlib::XGetWindowAttributes(
|
||||
self.dpy(),
|
||||
self.root,
|
||||
&mut wa,
|
||||
);
|
||||
|
||||
info!("Root window dimensions: {}, {}", wa.width, wa.height);
|
||||
info!(
|
||||
"Root window dimensions: {}, {}",
|
||||
wa.width, wa.height
|
||||
);
|
||||
|
||||
(wa.width, wa.height)
|
||||
}
|
||||
|
@ -547,8 +592,9 @@ impl XLib {
|
|||
self.dpy(),
|
||||
self.root,
|
||||
0,
|
||||
(ButtonPressMask | ButtonReleaseMask | PointerMotionMask)
|
||||
as u32,
|
||||
(ButtonPressMask
|
||||
| ButtonReleaseMask
|
||||
| PointerMotionMask) as u32,
|
||||
GrabModeAsync,
|
||||
GrabModeAsync,
|
||||
0,
|
||||
|
@ -564,7 +610,11 @@ impl XLib {
|
|||
}
|
||||
}
|
||||
|
||||
pub fn move_cursor(&self, window: Option<Window>, position: (i32, i32)) {
|
||||
pub fn move_cursor(
|
||||
&self,
|
||||
window: Option<Window>,
|
||||
position: (i32, i32),
|
||||
) {
|
||||
unsafe {
|
||||
XWarpPointer(
|
||||
self.dpy(),
|
||||
|
@ -580,7 +630,11 @@ impl XLib {
|
|||
}
|
||||
}
|
||||
|
||||
fn check_for_protocol(&self, client: &Client, proto: xlib::Atom) -> bool {
|
||||
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;
|
||||
|
||||
|
@ -603,7 +657,11 @@ impl XLib {
|
|||
return false;
|
||||
}
|
||||
|
||||
fn send_protocol(&self, client: &Client, proto: xlib::Atom) -> bool {
|
||||
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);
|
||||
|
@ -723,19 +781,37 @@ impl Atoms {
|
|||
Self {
|
||||
protocols: {
|
||||
let name = CString::new("WM_PROTOCOLS").unwrap();
|
||||
XInternAtom(display.get(), name.as_c_str().as_ptr(), 0)
|
||||
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)
|
||||
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)
|
||||
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)
|
||||
XInternAtom(
|
||||
display.get(),
|
||||
name.as_c_str().as_ptr(),
|
||||
0,
|
||||
)
|
||||
},
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue