Compare commits

..

No commits in common. "feature_cursor" and "get_atom_property_leak_fix" have entirely different histories.

11 changed files with 323 additions and 1234 deletions

View file

@ -1,27 +1,25 @@
[package]
name = "wm"
version = "0.3.0"
version = "0.2.0"
authors = ["noonebtw <noonebtw@gmail.com>"]
edition = "2018"
[[bin]]
name = "nirgendwm"
name = "nowm"
path = "src/main.rs"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
x11 = {version = "2.19", features = ["xlib", "xft"] }
log = "0.4"
simple_logger = "2.0"
dirs = "3.0.0"
log4rs = "1.0"
indexmap = "1.0"
thiserror = "1.0"
bitflags = "1.3"
x11 = {version = "2.18.2", features = ["xlib", "xft"] }
log = "0.4.13"
simple_logger = "1.11.0"
dirs = "3.0.2"
log4rs = "1.0.0"
indexmap = "1.6.2"
thiserror = "1.0.30"
bitflags = "1.3.2"
derivative = "2.2.0"
serde = { version = "1.0", features = ["derive"] }
toml = "0.5"
num-traits = "0.2"
strum = {version = "0.24.0", features = ["derive"]}
bytemuck = "1.0"
num-traits = "0.2.14"

View file

@ -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", []]

View file

@ -4,25 +4,3 @@ pub mod window_event;
pub mod xlib;
pub use traits::*;
pub mod structs {
#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Clone, Copy)]
pub enum WindowType {
Splash,
Dialog,
Normal,
Utility,
Menu,
Toolbar,
Dock,
Desktop,
}
#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Clone)]
pub enum Cursor {
Normal,
Resize,
Move,
}
}

View file

@ -1,7 +1,4 @@
use super::{
structs::WindowType,
window_event::{self, KeyOrMouseBind},
};
use super::window_event::{self, KeyOrMouseBind};
use crate::util::{Point, Size};
pub trait WindowServerBackend {
@ -21,11 +18,6 @@ pub trait WindowServerBackend {
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>;
@ -39,8 +31,6 @@ pub trait WindowServerBackend {
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);

View file

@ -1,13 +1,10 @@
#![allow(dead_code)]
use super::{
keycodes::{KeyOrButton, MouseButton, VirtualKeyCode},
structs::WindowType,
};
use super::keycodes::{KeyOrButton, MouseButton, VirtualKeyCode};
use crate::util::{Point, Size};
use bitflags::bitflags;
#[derive(Debug, Clone)]
#[derive(Debug)]
pub enum WindowEvent<Window> {
KeyEvent(KeyEvent<Window>),
ButtonEvent(ButtonEvent<Window>),
@ -20,8 +17,6 @@ pub enum WindowEvent<Window> {
EnterEvent(EnterEvent<Window>),
ConfigureEvent(ConfigureEvent<Window>),
FullscreenEvent(FullscreenEvent<Window>), //1 { window: Window, event: 1 },
WindowNameEvent(WindowNameEvent<Window>),
WindowTypeChangedEvent(WindowTypeChangedEvent<Window>),
}
#[derive(Debug, PartialEq, Eq, Copy, Clone)]
@ -111,7 +106,7 @@ impl Into<u8> for ModifierKey {
}
}
#[derive(Debug, Clone)]
#[derive(Debug)]
pub struct KeyEvent<Window> {
pub window: Window,
pub state: KeyState,
@ -135,7 +130,7 @@ impl<Window> KeyEvent<Window> {
}
}
#[derive(Debug, Clone)]
#[derive(Debug)]
pub struct ButtonEvent<Window> {
pub window: Window,
pub state: KeyState,
@ -162,7 +157,7 @@ impl<Window> ButtonEvent<Window> {
}
}
#[derive(Debug, Clone)]
#[derive(Debug)]
pub struct MotionEvent<Window> {
pub position: Point<i32>,
pub window: Window,
@ -174,22 +169,22 @@ impl<Window> MotionEvent<Window> {
}
}
#[derive(Debug, Clone)]
#[derive(Debug)]
pub struct MapEvent<Window> {
pub window: Window,
}
#[derive(Debug, Clone)]
#[derive(Debug)]
pub struct UnmapEvent<Window> {
pub window: Window,
}
#[derive(Debug, Clone)]
#[derive(Debug)]
pub struct EnterEvent<Window> {
pub window: Window,
}
#[derive(Debug, Clone)]
#[derive(Debug)]
pub struct DestroyEvent<Window> {
pub window: Window,
}
@ -200,7 +195,7 @@ impl<Window> DestroyEvent<Window> {
}
}
#[derive(Debug, Clone)]
#[derive(Debug)]
pub struct CreateEvent<Window> {
pub window: Window,
pub position: Point<i32>,
@ -217,7 +212,7 @@ impl<Window> CreateEvent<Window> {
}
}
#[derive(Debug, Clone)]
#[derive(Debug)]
pub struct ConfigureEvent<Window> {
pub window: Window,
pub position: Point<i32>,
@ -234,7 +229,7 @@ impl<Window> ConfigureEvent<Window> {
}
}
#[derive(Debug, Clone)]
#[derive(Debug)]
pub enum FullscreenState {
On,
Off,
@ -250,7 +245,7 @@ impl From<bool> for FullscreenState {
}
}
#[derive(Debug, Clone)]
#[derive(Debug)]
pub struct FullscreenEvent<Window> {
pub window: Window,
pub state: FullscreenState,
@ -262,33 +257,6 @@ impl<Window> FullscreenEvent<Window> {
}
}
#[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,

File diff suppressed because it is too large Load diff

View file

@ -4,17 +4,13 @@ use indexmap::IndexMap;
use log::error;
use num_traits::Zero;
use crate::backends::structs::WindowType;
use crate::util::BuildIdentityHasher;
use crate::util::{Point, Size};
mod client {
use std::hash::{Hash, Hasher};
use crate::{
backends::structs::WindowType,
util::{Point, Size},
};
use crate::util::{Point, Size};
use x11::xlib::Window;
#[derive(Clone, Debug)]
@ -22,8 +18,7 @@ mod client {
pub(crate) window: Window,
pub(crate) size: Size<i32>,
pub(crate) position: Point<i32>,
pub(crate) parent_window: Option<Window>,
pub(crate) window_type: WindowType,
pub(crate) transient_for: Option<Window>,
pub(crate) fullscreen: bool,
}
@ -33,9 +28,8 @@ mod client {
window: 0,
size: (100, 100).into(),
position: (0, 0).into(),
parent_window: None,
transient_for: None,
fullscreen: false,
window_type: WindowType::Normal,
}
}
}
@ -55,7 +49,7 @@ mod client {
}
}
pub fn new_dialog(
pub fn new_transient(
window: Window,
size: Size<i32>,
transient: Window,
@ -63,7 +57,7 @@ mod client {
Self {
window,
size,
parent_window: Some(transient),
transient_for: Some(transient),
..Default::default()
}
}
@ -75,24 +69,6 @@ mod client {
}
}
pub fn with_window_type(self, window_type: WindowType) -> Self {
Self {
window_type,
..self
}
}
pub fn with_parent_window(self, parent_window: Option<Window>) -> Self {
Self {
parent_window,
..self
}
}
pub fn with_size(self, size: Size<i32>) -> Self {
Self { size, ..self }
}
/// toggles the clients fullscreen flag.
/// returns `true` if the client is now fullscreen.
pub fn toggle_fullscreen(&mut self) -> bool {
@ -114,8 +90,8 @@ mod client {
self.fullscreen
}
pub fn has_parent_window(&self) -> bool {
self.parent_window.is_some()
pub fn is_transient(&self) -> bool {
self.transient_for.is_some()
}
}
@ -264,41 +240,26 @@ impl ClientState {
pub fn insert(&mut self, mut client: Client) -> Option<&Client> {
let key = client.key();
match client.window_type {
// idk how to handle docks and desktops, for now they float innit
WindowType::Splash
| WindowType::Dialog
| WindowType::Utility
| WindowType::Menu
| WindowType::Toolbar
| WindowType::Dock
| WindowType::Desktop => {
if let Some(parent) = client
.parent_window
.and_then(|window| self.get(&window).into_option())
{
client.position = {
(
parent.position.x
+ (parent.size.width - client.size.width) / 2,
parent.position.y
+ (parent.size.height - client.size.height) / 2,
)
.into()
};
}
if client.is_transient()
&& self.contains(&client.transient_for.unwrap())
{
let transient = self.get(&client.transient_for.unwrap()).unwrap();
client.size = client.size.clamp(
self.screen_size
- Size::new(self.border_size * 2, self.border_size * 2),
);
client.position = {
(
transient.position.x
+ (transient.size.width - client.size.width) / 2,
transient.position.y
+ (transient.size.height - client.size.height) / 2,
)
.into()
};
self.floating_clients.insert(key, client);
}
WindowType::Normal => {
self.clients.insert(key, client);
self.virtual_screens.get_mut_current().insert(&key);
}
self.floating_clients.insert(key, client);
} else {
self.clients.insert(key, client);
self.virtual_screens.get_mut_current().insert(&key);
}
// adding a client changes the liling layout, rearrange
@ -359,15 +320,7 @@ impl ClientState {
}
pub fn iter_transient(&self) -> impl Iterator<Item = (&u64, &Client)> {
self.iter_floating().filter(|&(_, c)| c.has_parent_window())
}
pub fn iter_by_window_type(
&self,
window_type: WindowType,
) -> impl Iterator<Item = (&u64, &Client)> {
self.iter_floating()
.filter(move |&(_, c)| c.window_type == window_type)
self.iter_floating().filter(|&(_, c)| c.is_transient())
}
pub fn iter_visible(&self) -> impl Iterator<Item = (&u64, &Client)> {
@ -404,7 +357,7 @@ impl ClientState {
{
match self.get(key) {
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)
} else {
true
@ -489,12 +442,11 @@ impl ClientState {
.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) {
if self.inner_toggle_fullscreen(key) {
self.arrange_virtual_screen();
true
} else {
@ -502,22 +454,24 @@ impl ClientState {
}
}
fn inner_toggle_fullscreen<K>(&mut self, key: &K) -> Option<bool>
fn inner_toggle_fullscreen<K>(&mut self, key: &K) -> bool
where
K: ClientKey,
{
let fullscreen_size = self.screen_size;
let fullscreen_size = self.screen_size
- Size::new(self.border_size * 2, self.border_size * 2);
self.get_mut(key).into_option().map(|client| {
if client.toggle_fullscreen() {
client.size = fullscreen_size;
client.position = Point::zero();
match self.get_mut(key).into_option() {
Some(client) => {
if client.toggle_fullscreen() {
client.size = fullscreen_size;
client.position = Point::zero();
}
true
} else {
false
}
})
None => false,
}
}
/**
@ -537,23 +491,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
automatically since xlib has to move and resize all windows anyways).
@ -583,15 +520,15 @@ impl ClientState {
}
(None, Some(floating_client)) => {
// transient clients cannot be tiled
// only normal windows can be tiled
match floating_client.window_type {
WindowType::Normal => {
match floating_client.is_transient() {
true => {
self.floating_clients.insert(key, floating_client);
}
false => {
self.clients.insert(key, floating_client);
self.virtual_screens.get_mut_current().insert(&key);
}
_ => {
self.floating_clients.insert(key, floating_client);
}
}
}
_ => {
@ -606,20 +543,6 @@ impl ClientState {
}
}
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)
where
K: ClientKey,
@ -837,7 +760,10 @@ impl ClientState {
border: i32,
) -> (Size<i32>, Point<i32>) {
if fullscreen {
let size = Size::new(screen_size.width, screen_size.height);
let size = Size::new(
screen_size.width - border * 2,
screen_size.height - border * 2,
);
let pos = Point::new(0, 0);
(size, pos)
} else {

View file

@ -1,5 +1,3 @@
#![deny(unsafe_op_in_unsafe_fn)]
pub mod backends;
pub mod clients;
pub mod state;

View file

@ -21,7 +21,7 @@ fn init_logger() {
let _logfile = FileAppender::builder()
.encoder(encoder)
.build(home.join(".local/nirgendwm.log"))
.build(home.join(".local/portlights.log"))
.unwrap();
let config = Config::builder()
@ -31,7 +31,7 @@ fn init_logger() {
Root::builder()
.appender("stdout")
//.appender("logfile")
.build(log::LevelFilter::Debug),
.build(log::LevelFilter::Info),
)
.unwrap();
@ -44,7 +44,7 @@ fn main() {
log_prologue();
let mut config_path = std::path::PathBuf::from(env!("HOME"));
config_path.push(".config/nirgendwm.toml");
config_path.push(".config/nowm.toml");
let config = std::fs::File::open(config_path)
.and_then(|mut file| {

View file

@ -4,10 +4,7 @@ use log::{error, info};
use x11::xlib::{self, Window};
use crate::backends::structs::WindowType;
use crate::backends::window_event::{
FullscreenEvent, FullscreenState, WindowNameEvent, WindowTypeChangedEvent,
};
use crate::backends::window_event::{FullscreenEvent, FullscreenState};
use crate::util::{Point, Size};
use crate::{
backends::{
@ -40,7 +37,6 @@ pub struct WMConfig {
inactive_window_border_color: String,
#[serde(default = "WMConfig::default_terminal")]
terminal_command: (String, Vec<String>),
border_width: Option<i32>,
}
impl WMConfig {
@ -53,7 +49,7 @@ impl WMConfig {
}
fn default_terminal() -> (String, Vec<String>) {
("xterm".to_string(), vec![])
("alacritty".to_string(), vec![])
}
}
@ -69,7 +65,6 @@ impl Default for WMConfig {
inactive_window_border_color:
Self::default_inactive_window_border_color(),
terminal_command: Self::default_terminal(),
border_width: Some(1),
}
}
}
@ -150,7 +145,7 @@ where
let clients = ClientState::new()
.with_virtualscreens(config.num_virtualscreens)
.with_gap(config.gap.unwrap_or(1))
.with_border(config.border_width.unwrap_or(1))
.with_border(1)
.with_screen_size(backend.screen_size());
Self {
@ -203,6 +198,16 @@ where
},
));
// self.add_keybind(KeyBinding::new(
// KeyBind::new(VirtualKeyCode::Print),
// |wm, _| wm.spawn("screenshot.sh", &[]),
// ));
// self.add_keybind(KeyBinding::new(
// KeyBind::new(VirtualKeyCode::Print).with_mod(ModifierKey::Shift),
// |wm, _| wm.spawn("screenshot.sh", &["-edit"]),
// ));
self.add_keybind(KeyBinding::new(
KeyBind::new(VirtualKeyCode::M).with_mod(self.config.mod_key),
|wm, _| wm.handle_switch_stack(),
@ -296,6 +301,13 @@ where
&self.config.inactive_window_border_color,
);
// add all already existing windows to the WM
if let Some(windows) = self.backend.all_windows() {
windows
.into_iter()
.for_each(|window| self.new_client(window));
}
self
}
@ -414,6 +426,8 @@ where
self.button_event(&event);
}
WindowEvent::MapRequestEvent(MapEvent { window }) => {
self.backend.handle_event(event);
if !self.clients.contains(&window) {
self.new_client(window);
}
@ -429,27 +443,28 @@ where
self.do_move_resize_window(&event);
}
WindowEvent::ConfigureEvent(ConfigureEvent {
window,
size,
position,
..
}) => match self.clients.get(&window) {
ClientEntry::Tiled(client)
| ClientEntry::Floating(client) => {
self.backend.configure_window(
window,
Some(client.size),
Some(client.position),
None,
)
window, ..
}) => {
match self.clients.get(&window) {
ClientEntry::Tiled(client)
| ClientEntry::Floating(client) => {
self.backend.configure_window(
window,
Some(client.size),
Some(client.position),
None,
)
}
ClientEntry::Vacant => self.backend.handle_event(event),
}
ClientEntry::Vacant => self.backend.configure_window(
window,
Some(size),
Some(position),
None,
),
},
// TODO
// match self.clients.get(&event.window).into_option() {
// Some(client) => self
// .xlib
// .configure_client(client, self.clients.get_border()),
// None => self.xlib.configure_window(event),
// }
}
WindowEvent::FullscreenEvent(FullscreenEvent {
window,
state,
@ -465,40 +480,9 @@ where
self.clients.toggle_fullscreen(&window)
}
} {
if let Some(client) =
self.clients.get(&window).into_option()
{
self.backend.configure_window(
window,
None,
None,
if client.is_fullscreen() {
Some(0)
} else {
Some(self.clients.get_border())
},
);
self.backend.set_window_fullscreen_state(
client.window,
client.is_fullscreen(),
);
};
self.arrange_clients();
}
}
WindowEvent::WindowNameEvent(WindowNameEvent { .. }) => {
info!("{:#?}", event);
}
WindowEvent::WindowTypeChangedEvent(
WindowTypeChangedEvent {
window,
window_type,
},
) => {
self.clients.update_window_type(&window, window_type);
}
// i dont think i actually have to handle destroy notify events.
// every window should be unmapped regardless
@ -742,16 +726,19 @@ where
}
fn new_client(&mut self, window: Window) {
let client = match self.backend.get_window_type(window) {
WindowType::Normal => Client::new_default(window),
window_type @ _ => Client::new_default(window)
.with_window_type(window_type)
.with_size(
self.backend
.get_window_size(window)
.unwrap_or((100, 100).into()),
)
.with_parent_window(self.backend.get_parent_window(window)),
info!("new client: {:?}", window);
let client = if let Some(transient_window) =
self.backend.get_parent_window(window)
{
Client::new_transient(
window,
self.backend
.get_window_size(window)
.unwrap_or((100, 100).into()),
transient_window,
)
} else {
Client::new_default(window)
};
self.backend.configure_window(
@ -761,8 +748,6 @@ where
Some(self.clients.get_border()),
);
info!("new client: {:#?}", client);
self.clients.insert(client).unwrap();
self.arrange_clients();

View file

@ -123,13 +123,6 @@ mod size {
(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,