Merge branch 'feature-fullscreen'
This commit is contained in:
commit
192f865fec
|
@ -22,3 +22,4 @@ bitflags = "1.3.2"
|
||||||
derivative = "2.2.0"
|
derivative = "2.2.0"
|
||||||
serde = { version = "1.0", features = ["derive"] }
|
serde = { version = "1.0", features = ["derive"] }
|
||||||
toml = "0.5"
|
toml = "0.5"
|
||||||
|
num-traits = "0.2.14"
|
||||||
|
|
|
@ -1,7 +1,5 @@
|
||||||
use super::{
|
use super::window_event::{self, KeyOrMouseBind};
|
||||||
window_event,
|
use crate::util::{Point, Size};
|
||||||
window_event::{KeyOrMouseBind, Point},
|
|
||||||
};
|
|
||||||
|
|
||||||
pub trait WindowServerBackend {
|
pub trait WindowServerBackend {
|
||||||
type Window;
|
type Window;
|
||||||
|
@ -26,13 +24,13 @@ pub trait WindowServerBackend {
|
||||||
fn configure_window(
|
fn configure_window(
|
||||||
&self,
|
&self,
|
||||||
window: Self::Window,
|
window: Self::Window,
|
||||||
new_size: Option<Point<i32>>,
|
new_size: Option<Size<i32>>,
|
||||||
new_pos: Option<Point<i32>>,
|
new_pos: Option<Point<i32>>,
|
||||||
new_border: Option<i32>,
|
new_border: Option<i32>,
|
||||||
);
|
);
|
||||||
|
|
||||||
fn screen_size(&self) -> Point<i32>;
|
fn screen_size(&self) -> Size<i32>;
|
||||||
fn get_window_size(&self, window: Self::Window) -> Option<Point<i32>>;
|
fn get_window_size(&self, window: Self::Window) -> Option<Size<i32>>;
|
||||||
|
|
||||||
fn grab_cursor(&self);
|
fn grab_cursor(&self);
|
||||||
fn ungrab_cursor(&self);
|
fn ungrab_cursor(&self);
|
||||||
|
@ -43,7 +41,7 @@ pub trait WindowServerBackend {
|
||||||
fn set_active_window_border_color(&mut self, color_name: &str);
|
fn set_active_window_border_color(&mut self, color_name: &str);
|
||||||
fn set_inactive_window_border_color(&mut self, color_name: &str);
|
fn set_inactive_window_border_color(&mut self, color_name: &str);
|
||||||
|
|
||||||
fn resize_window(&self, window: Self::Window, new_size: Point<i32>) {
|
fn resize_window(&self, window: Self::Window, new_size: Size<i32>) {
|
||||||
self.configure_window(window, Some(new_size), None, None);
|
self.configure_window(window, Some(new_size), None, None);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
#![allow(dead_code)]
|
#![allow(dead_code)]
|
||||||
|
|
||||||
use super::keycodes::{KeyOrButton, MouseButton, VirtualKeyCode};
|
use super::keycodes::{KeyOrButton, MouseButton, VirtualKeyCode};
|
||||||
|
use crate::util::{Point, Size};
|
||||||
use bitflags::bitflags;
|
use bitflags::bitflags;
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
|
@ -129,43 +130,6 @@ impl<Window> KeyEvent<Window> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Copy, Clone, PartialEq, PartialOrd)]
|
|
||||||
pub struct Point<I>
|
|
||||||
where
|
|
||||||
I: Copy + Clone + PartialEq + PartialOrd,
|
|
||||||
{
|
|
||||||
pub x: I,
|
|
||||||
pub y: I,
|
|
||||||
}
|
|
||||||
impl<I> From<(I, I)> for Point<I>
|
|
||||||
where
|
|
||||||
I: Copy + Clone + PartialEq + PartialOrd,
|
|
||||||
{
|
|
||||||
fn from(value: (I, I)) -> Self {
|
|
||||||
Self::from_tuple(value)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<I> Point<I>
|
|
||||||
where
|
|
||||||
I: Copy + Clone + PartialEq + PartialOrd,
|
|
||||||
{
|
|
||||||
pub fn new(x: I, y: I) -> Self {
|
|
||||||
Self { x, y }
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn from_tuple(tuple: (I, I)) -> Self {
|
|
||||||
Self {
|
|
||||||
x: tuple.0,
|
|
||||||
y: tuple.1,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn as_tuple(&self) -> (I, I) {
|
|
||||||
(self.x, self.y)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct ButtonEvent<Window> {
|
pub struct ButtonEvent<Window> {
|
||||||
pub window: Window,
|
pub window: Window,
|
||||||
|
@ -235,11 +199,11 @@ impl<Window> DestroyEvent<Window> {
|
||||||
pub struct CreateEvent<Window> {
|
pub struct CreateEvent<Window> {
|
||||||
pub window: Window,
|
pub window: Window,
|
||||||
pub position: Point<i32>,
|
pub position: Point<i32>,
|
||||||
pub size: Point<i32>,
|
pub size: Size<i32>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<Window> CreateEvent<Window> {
|
impl<Window> CreateEvent<Window> {
|
||||||
pub fn new(window: Window, position: Point<i32>, size: Point<i32>) -> Self {
|
pub fn new(window: Window, position: Point<i32>, size: Size<i32>) -> Self {
|
||||||
Self {
|
Self {
|
||||||
window,
|
window,
|
||||||
position,
|
position,
|
||||||
|
@ -252,11 +216,11 @@ impl<Window> CreateEvent<Window> {
|
||||||
pub struct ConfigureEvent<Window> {
|
pub struct ConfigureEvent<Window> {
|
||||||
pub window: Window,
|
pub window: Window,
|
||||||
pub position: Point<i32>,
|
pub position: Point<i32>,
|
||||||
pub size: Point<i32>,
|
pub size: Size<i32>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<Window> ConfigureEvent<Window> {
|
impl<Window> ConfigureEvent<Window> {
|
||||||
pub fn new(window: Window, position: Point<i32>, size: Point<i32>) -> Self {
|
pub fn new(window: Window, position: Point<i32>, size: Size<i32>) -> Self {
|
||||||
Self {
|
Self {
|
||||||
window,
|
window,
|
||||||
position,
|
position,
|
||||||
|
@ -265,18 +229,31 @@ impl<Window> ConfigureEvent<Window> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub enum FullscreenState {
|
||||||
|
On,
|
||||||
|
Off,
|
||||||
|
Toggle,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<bool> for FullscreenState {
|
||||||
|
fn from(value: bool) -> Self {
|
||||||
|
match value {
|
||||||
|
true => Self::On,
|
||||||
|
false => Self::Off,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct FullscreenEvent<Window> {
|
pub struct FullscreenEvent<Window> {
|
||||||
window: Window,
|
pub window: Window,
|
||||||
new_fullscreen: bool,
|
pub state: FullscreenState,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<Window> FullscreenEvent<Window> {
|
impl<Window> FullscreenEvent<Window> {
|
||||||
pub fn new(window: Window, new_fullscreen: bool) -> Self {
|
pub fn new(window: Window, state: FullscreenState) -> Self {
|
||||||
Self {
|
Self { window, state }
|
||||||
window,
|
|
||||||
new_fullscreen,
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
use log::{error, warn};
|
use log::{error, warn};
|
||||||
use std::{ffi::CString, rc::Rc};
|
use num_traits::Zero;
|
||||||
|
use std::{ffi::CString, mem::MaybeUninit, rc::Rc};
|
||||||
|
|
||||||
use thiserror::Error;
|
use thiserror::Error;
|
||||||
|
|
||||||
|
@ -17,12 +18,13 @@ use self::keysym::{
|
||||||
use super::{
|
use super::{
|
||||||
keycodes::VirtualKeyCode,
|
keycodes::VirtualKeyCode,
|
||||||
window_event::{
|
window_event::{
|
||||||
ButtonEvent, ConfigureEvent, DestroyEvent, EnterEvent, KeyEvent,
|
ButtonEvent, ConfigureEvent, DestroyEvent, EnterEvent, FullscreenEvent,
|
||||||
KeyOrMouseBind, KeyState, MapEvent, ModifierState, MotionEvent, Point,
|
FullscreenState, KeyEvent, KeyOrMouseBind, KeyState, MapEvent,
|
||||||
UnmapEvent, WindowEvent,
|
ModifierState, MotionEvent, UnmapEvent, WindowEvent,
|
||||||
},
|
},
|
||||||
WindowServerBackend,
|
WindowServerBackend,
|
||||||
};
|
};
|
||||||
|
use crate::util::{Point, Size};
|
||||||
|
|
||||||
pub mod color;
|
pub mod color;
|
||||||
pub mod keysym;
|
pub mod keysym;
|
||||||
|
@ -285,6 +287,53 @@ impl XLib {
|
||||||
))
|
))
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
xlib::PropertyNotify => {
|
||||||
|
let ev = unsafe { &event.property };
|
||||||
|
|
||||||
|
(ev.atom == self.atoms.net_wm_window_type)
|
||||||
|
.then(|| {
|
||||||
|
(self.get_atom_property(
|
||||||
|
ev.window,
|
||||||
|
self.atoms.net_wm_state,
|
||||||
|
) == Some(self.atoms.net_wm_state_fullscreen))
|
||||||
|
.then(|| {
|
||||||
|
XLibWindowEvent::FullscreenEvent(
|
||||||
|
FullscreenEvent::new(
|
||||||
|
ev.window,
|
||||||
|
FullscreenState::On,
|
||||||
|
),
|
||||||
|
)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
.flatten()
|
||||||
|
}
|
||||||
|
xlib::ClientMessage => {
|
||||||
|
let ev = unsafe { &event.client_message };
|
||||||
|
|
||||||
|
(ev.message_type == self.atoms.net_wm_state)
|
||||||
|
.then(|| {
|
||||||
|
let data = ev.data.as_longs();
|
||||||
|
(data[1] as u64 == self.atoms.net_wm_state_fullscreen
|
||||||
|
|| data[2] as u64
|
||||||
|
== self.atoms.net_wm_state_fullscreen)
|
||||||
|
.then(|| {
|
||||||
|
XLibWindowEvent::FullscreenEvent(
|
||||||
|
FullscreenEvent::new(
|
||||||
|
ev.window,
|
||||||
|
match data[0] /* as u64 */ {
|
||||||
|
0 => FullscreenState::Off,
|
||||||
|
1 => FullscreenState::On,
|
||||||
|
2 => FullscreenState::Toggle,
|
||||||
|
_ => {
|
||||||
|
unreachable!()
|
||||||
|
}
|
||||||
|
},
|
||||||
|
),
|
||||||
|
)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
.flatten()
|
||||||
|
}
|
||||||
_ => None,
|
_ => None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -308,6 +357,37 @@ impl XLib {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn get_atom_property(
|
||||||
|
&self,
|
||||||
|
window: xlib::Window,
|
||||||
|
atom: xlib::Atom,
|
||||||
|
) -> Option<xlib::Atom> {
|
||||||
|
let mut di = 0;
|
||||||
|
let mut dl0 = 0;
|
||||||
|
let mut dl1 = 0;
|
||||||
|
let mut da = 0;
|
||||||
|
|
||||||
|
let mut atom_out = MaybeUninit::<xlib::Atom>::zeroed();
|
||||||
|
|
||||||
|
unsafe {
|
||||||
|
(xlib::XGetWindowProperty(
|
||||||
|
self.dpy(),
|
||||||
|
window,
|
||||||
|
atom,
|
||||||
|
0,
|
||||||
|
std::mem::size_of::<xlib::Atom>() as i64,
|
||||||
|
0,
|
||||||
|
xlib::XA_ATOM,
|
||||||
|
&mut da,
|
||||||
|
&mut di,
|
||||||
|
&mut dl0,
|
||||||
|
&mut dl1,
|
||||||
|
atom_out.as_mut_ptr() as *mut _,
|
||||||
|
) != 0)
|
||||||
|
.then(|| atom_out.assume_init())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fn check_for_protocol(
|
fn check_for_protocol(
|
||||||
&self,
|
&self,
|
||||||
window: xlib::Window,
|
window: xlib::Window,
|
||||||
|
@ -726,8 +806,8 @@ impl WindowServerBackend for XLib {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn hide_window(&self, window: Self::Window) {
|
fn hide_window(&self, window: Self::Window) {
|
||||||
let screen_size = self.screen_size();
|
let screen_size = self.screen_size() + Size::new(100, 100);
|
||||||
self.move_window(window, screen_size);
|
self.move_window(window, screen_size.into());
|
||||||
}
|
}
|
||||||
|
|
||||||
fn kill_window(&self, window: Self::Window) {
|
fn kill_window(&self, window: Self::Window) {
|
||||||
|
@ -753,17 +833,17 @@ impl WindowServerBackend for XLib {
|
||||||
fn configure_window(
|
fn configure_window(
|
||||||
&self,
|
&self,
|
||||||
window: Self::Window,
|
window: Self::Window,
|
||||||
new_size: Option<super::window_event::Point<i32>>,
|
new_size: Option<crate::util::Size<i32>>,
|
||||||
new_pos: Option<super::window_event::Point<i32>>,
|
new_pos: Option<crate::util::Point<i32>>,
|
||||||
new_border: Option<i32>,
|
new_border: Option<i32>,
|
||||||
) {
|
) {
|
||||||
let position = new_pos.unwrap_or(Point::new(0, 0));
|
let position = new_pos.unwrap_or(Point::zero());
|
||||||
let size = new_size.unwrap_or(Point::new(0, 0));
|
let size = new_size.unwrap_or(Size::zero());
|
||||||
let mut wc = xlib::XWindowChanges {
|
let mut wc = xlib::XWindowChanges {
|
||||||
x: position.x,
|
x: position.x,
|
||||||
y: position.y,
|
y: position.y,
|
||||||
width: size.x,
|
width: size.width,
|
||||||
height: size.y,
|
height: size.height,
|
||||||
border_width: new_border.unwrap_or(0),
|
border_width: new_border.unwrap_or(0),
|
||||||
sibling: 0,
|
sibling: 0,
|
||||||
stack_mode: 0,
|
stack_mode: 0,
|
||||||
|
@ -789,7 +869,7 @@ impl WindowServerBackend for XLib {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn screen_size(&self) -> Point<i32> {
|
fn screen_size(&self) -> Size<i32> {
|
||||||
unsafe {
|
unsafe {
|
||||||
let mut wa =
|
let mut wa =
|
||||||
std::mem::MaybeUninit::<xlib::XWindowAttributes>::zeroed();
|
std::mem::MaybeUninit::<xlib::XWindowAttributes>::zeroed();
|
||||||
|
@ -802,7 +882,7 @@ impl WindowServerBackend for XLib {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_window_size(&self, window: Self::Window) -> Option<Point<i32>> {
|
fn get_window_size(&self, window: Self::Window) -> Option<Size<i32>> {
|
||||||
self.get_window_attributes(window)
|
self.get_window_attributes(window)
|
||||||
.map(|wa| (wa.width, wa.height).into())
|
.map(|wa| (wa.width, wa.height).into())
|
||||||
}
|
}
|
||||||
|
|
190
src/clients.rs
190
src/clients.rs
|
@ -1,25 +1,24 @@
|
||||||
use std::num::NonZeroI32;
|
|
||||||
use std::{ops::Rem, usize};
|
use std::{ops::Rem, usize};
|
||||||
|
|
||||||
use indexmap::IndexMap;
|
use indexmap::IndexMap;
|
||||||
use log::{error, info};
|
use log::error;
|
||||||
|
|
||||||
use crate::backends::window_event::Point;
|
|
||||||
use crate::util::BuildIdentityHasher;
|
use crate::util::BuildIdentityHasher;
|
||||||
|
use crate::util::{Point, Size};
|
||||||
|
|
||||||
mod client {
|
mod client {
|
||||||
use std::hash::{Hash, Hasher};
|
use std::hash::{Hash, Hasher};
|
||||||
|
|
||||||
|
use crate::util::{Point, Size};
|
||||||
use x11::xlib::Window;
|
use x11::xlib::Window;
|
||||||
|
|
||||||
use crate::backends::window_event::Point;
|
|
||||||
|
|
||||||
#[derive(Clone, Debug)]
|
#[derive(Clone, Debug)]
|
||||||
pub struct Client {
|
pub struct Client {
|
||||||
pub(crate) window: Window,
|
pub(crate) window: Window,
|
||||||
pub(crate) size: Point<i32>,
|
pub(crate) size: Size<i32>,
|
||||||
pub(crate) position: Point<i32>,
|
pub(crate) position: Point<i32>,
|
||||||
pub(crate) transient_for: Option<Window>,
|
pub(crate) transient_for: Option<Window>,
|
||||||
|
pub(crate) fullscreen: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Default for Client {
|
impl Default for Client {
|
||||||
|
@ -29,6 +28,7 @@ mod client {
|
||||||
size: (100, 100).into(),
|
size: (100, 100).into(),
|
||||||
position: (0, 0).into(),
|
position: (0, 0).into(),
|
||||||
transient_for: None,
|
transient_for: None,
|
||||||
|
fullscreen: false,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -37,20 +37,20 @@ mod client {
|
||||||
#[allow(dead_code)]
|
#[allow(dead_code)]
|
||||||
pub fn new(
|
pub fn new(
|
||||||
window: Window,
|
window: Window,
|
||||||
size: Point<i32>,
|
size: Size<i32>,
|
||||||
position: Point<i32>,
|
position: Point<i32>,
|
||||||
) -> Self {
|
) -> Self {
|
||||||
Self {
|
Self {
|
||||||
window,
|
window,
|
||||||
size,
|
size,
|
||||||
position,
|
position,
|
||||||
transient_for: None,
|
..Self::default()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn new_transient(
|
pub fn new_transient(
|
||||||
window: Window,
|
window: Window,
|
||||||
size: Point<i32>,
|
size: Size<i32>,
|
||||||
transient: Window,
|
transient: Window,
|
||||||
) -> Self {
|
) -> Self {
|
||||||
Self {
|
Self {
|
||||||
|
@ -68,6 +68,27 @@ mod client {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// toggles the clients fullscreen flag.
|
||||||
|
/// returns `true` if the client is now fullscreen.
|
||||||
|
pub fn toggle_fullscreen(&mut self) -> bool {
|
||||||
|
self.fullscreen = !self.fullscreen;
|
||||||
|
|
||||||
|
self.is_fullscreen()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn set_fullscreen(&mut self, fullscreen: bool) -> bool {
|
||||||
|
if self.fullscreen == fullscreen {
|
||||||
|
false
|
||||||
|
} else {
|
||||||
|
self.fullscreen = fullscreen;
|
||||||
|
true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn is_fullscreen(&self) -> bool {
|
||||||
|
self.fullscreen
|
||||||
|
}
|
||||||
|
|
||||||
pub fn is_transient(&self) -> bool {
|
pub fn is_transient(&self) -> bool {
|
||||||
self.transient_for.is_some()
|
self.transient_for.is_some()
|
||||||
}
|
}
|
||||||
|
@ -143,7 +164,7 @@ pub struct ClientState {
|
||||||
pub(self) virtual_screens: VirtualScreenStore,
|
pub(self) virtual_screens: VirtualScreenStore,
|
||||||
|
|
||||||
pub(self) gap: i32,
|
pub(self) gap: i32,
|
||||||
pub(self) screen_size: Point<i32>,
|
pub(self) screen_size: Size<i32>,
|
||||||
pub(self) master_size: f32,
|
pub(self) master_size: f32,
|
||||||
border_size: i32,
|
border_size: i32,
|
||||||
}
|
}
|
||||||
|
@ -192,7 +213,7 @@ impl ClientState {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn with_screen_size(self, screen_size: Point<i32>) -> Self {
|
pub fn with_screen_size(self, screen_size: Size<i32>) -> Self {
|
||||||
Self {
|
Self {
|
||||||
screen_size,
|
screen_size,
|
||||||
..self
|
..self
|
||||||
|
@ -226,9 +247,9 @@ impl ClientState {
|
||||||
client.position = {
|
client.position = {
|
||||||
(
|
(
|
||||||
transient.position.x
|
transient.position.x
|
||||||
+ (transient.size.x - client.size.x) / 2,
|
+ (transient.size.width - client.size.width) / 2,
|
||||||
transient.position.y
|
transient.position.y
|
||||||
+ (transient.size.y - client.size.y) / 2,
|
+ (transient.size.height - client.size.height) / 2,
|
||||||
)
|
)
|
||||||
.into()
|
.into()
|
||||||
};
|
};
|
||||||
|
@ -408,6 +429,43 @@ impl ClientState {
|
||||||
self.arrange_virtual_screen();
|
self.arrange_virtual_screen();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn set_fullscreen<K>(&mut self, key: &K, fullscreen: bool) -> bool
|
||||||
|
where
|
||||||
|
K: ClientKey,
|
||||||
|
{
|
||||||
|
self.get(key)
|
||||||
|
.into_option()
|
||||||
|
.map(|client| client.is_fullscreen() != fullscreen)
|
||||||
|
.unwrap_or(false)
|
||||||
|
.then(|| self.toggle_fullscreen(key))
|
||||||
|
.unwrap_or(false)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn toggle_fullscreen<K>(&mut self, key: &K) -> bool
|
||||||
|
where
|
||||||
|
K: ClientKey,
|
||||||
|
{
|
||||||
|
if self.inner_toggle_fullscreen(key) {
|
||||||
|
self.arrange_virtual_screen();
|
||||||
|
true
|
||||||
|
} else {
|
||||||
|
false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn inner_toggle_fullscreen<K>(&mut self, key: &K) -> bool
|
||||||
|
where
|
||||||
|
K: ClientKey,
|
||||||
|
{
|
||||||
|
self.get_mut(key)
|
||||||
|
.into_option()
|
||||||
|
.map(|client| {
|
||||||
|
client.toggle_fullscreen();
|
||||||
|
true
|
||||||
|
})
|
||||||
|
.unwrap_or(false)
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
Sets a tiled client to floating and returns true, does nothing for a floating client and
|
Sets a tiled client to floating and returns true, does nothing for a floating client and
|
||||||
returns false. If this function returns `true` you have to call `arrange_clients` after.
|
returns false. If this function returns `true` you have to call `arrange_clients` after.
|
||||||
|
@ -538,7 +596,7 @@ impl ClientState {
|
||||||
let (new, old) = self.focus_client_inner(key);
|
let (new, old) = self.focus_client_inner(key);
|
||||||
|
|
||||||
if !(new.is_vacant() && old.is_vacant()) {
|
if !(new.is_vacant() && old.is_vacant()) {
|
||||||
info!("Swapping focus: new({:?}) old({:?})", new, old);
|
//info!("Swapping focus: new({:?}) old({:?})", new, old);
|
||||||
}
|
}
|
||||||
|
|
||||||
(new, old)
|
(new, old)
|
||||||
|
@ -635,49 +693,87 @@ impl ClientState {
|
||||||
let vs = self.virtual_screens.get_mut_current();
|
let vs = self.virtual_screens.get_mut_current();
|
||||||
// if aux is empty -> width : width / 2
|
// if aux is empty -> width : width / 2
|
||||||
|
|
||||||
let (master_width, aux_width) = {
|
let vs_width = width - gap * 2;
|
||||||
let effective_width = width - gap * 2;
|
|
||||||
|
|
||||||
let master_size = if vs.aux.is_empty() {
|
let master_position = Point::new(0, 0);
|
||||||
|
let master_window_size = {
|
||||||
|
let factor = if vs.aux.is_empty() {
|
||||||
1.0
|
1.0
|
||||||
} else {
|
} else {
|
||||||
self.master_size / 2.0
|
self.master_size / 2.0
|
||||||
};
|
};
|
||||||
|
|
||||||
let master_width = (effective_width as f32 * master_size) as i32;
|
let width = (vs_width as f32 * factor) as i32;
|
||||||
let aux_width = effective_width - master_width;
|
|
||||||
|
|
||||||
(master_width, aux_width)
|
|
||||||
};
|
|
||||||
|
|
||||||
// make sure we dont devide by 0
|
// make sure we dont devide by 0
|
||||||
// height is max height / number of clients in the stack
|
// height is max height / number of clients in the stack
|
||||||
let master_height = (height - gap * 2)
|
let height = match vs.master.len() as i32 {
|
||||||
/ match NonZeroI32::new(vs.master.len() as i32) {
|
0 => 1,
|
||||||
Some(i) => i.get(),
|
n => (height - gap * 2) / n,
|
||||||
None => 1,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
// height is max height / number of clients in the stack
|
Size::new(width, height)
|
||||||
let aux_height = (height - gap * 2)
|
|
||||||
/ match NonZeroI32::new(vs.aux.len() as i32) {
|
|
||||||
Some(i) => i.get(),
|
|
||||||
None => 1,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
let aux_position = Point::new(master_window_size.width, 0);
|
||||||
|
let aux_window_size = {
|
||||||
|
let width = vs_width - master_window_size.width;
|
||||||
|
|
||||||
|
// make sure we dont devide by 0
|
||||||
|
// height is max height / number of clients in the stack
|
||||||
|
let height = match vs.aux.len() as i32 {
|
||||||
|
0 => 1,
|
||||||
|
n => (height - gap * 2) / n,
|
||||||
|
};
|
||||||
|
|
||||||
|
Size::new(width, height)
|
||||||
|
};
|
||||||
|
|
||||||
|
fn calculate_window_dimensions(
|
||||||
|
screen_size: Size<i32>,
|
||||||
|
stack_size: Size<i32>,
|
||||||
|
stack_position: Point<i32>,
|
||||||
|
fullscreen: bool,
|
||||||
|
nth: i32,
|
||||||
|
gap: i32,
|
||||||
|
border: i32,
|
||||||
|
) -> (Size<i32>, Point<i32>) {
|
||||||
|
if fullscreen {
|
||||||
|
let size = Size::new(
|
||||||
|
screen_size.width - border * 2,
|
||||||
|
screen_size.height - border * 2,
|
||||||
|
);
|
||||||
|
let pos = Point::new(0, 0);
|
||||||
|
(size, pos)
|
||||||
|
} else {
|
||||||
|
let size = Size::new(
|
||||||
|
stack_size.width - gap * 2 - border * 2,
|
||||||
|
stack_size.height - gap * 2 - border * 2,
|
||||||
|
);
|
||||||
|
let pos = Point::new(
|
||||||
|
stack_position.x + gap * 2,
|
||||||
|
stack_position.y + stack_size.height * nth + gap * 2,
|
||||||
|
);
|
||||||
|
(size, pos)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Master
|
// Master
|
||||||
for (i, key) in vs.master.iter().enumerate() {
|
for (i, key) in vs.master.iter().enumerate() {
|
||||||
let size = (
|
if let Some(client) = self.clients.get_mut(key) {
|
||||||
master_width - gap * 2 - self.border_size * 2,
|
let (size, position) = calculate_window_dimensions(
|
||||||
master_height - gap * 2 - self.border_size * 2,
|
self.screen_size.into(),
|
||||||
|
master_window_size,
|
||||||
|
master_position,
|
||||||
|
client.is_fullscreen(),
|
||||||
|
i as i32,
|
||||||
|
gap,
|
||||||
|
self.border_size,
|
||||||
);
|
);
|
||||||
|
|
||||||
let position = (gap * 2, master_height * i as i32 + gap * 2);
|
|
||||||
|
|
||||||
if let Some(client) = self.clients.get_mut(key) {
|
|
||||||
*client = Client {
|
*client = Client {
|
||||||
size: size.into(),
|
size: size.into(),
|
||||||
position: position.into(),
|
position,
|
||||||
..*client
|
..*client
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
@ -685,18 +781,20 @@ impl ClientState {
|
||||||
|
|
||||||
// Aux
|
// Aux
|
||||||
for (i, key) in vs.aux.iter().enumerate() {
|
for (i, key) in vs.aux.iter().enumerate() {
|
||||||
let size = (
|
if let Some(client) = self.clients.get_mut(key) {
|
||||||
aux_width - gap * 2 - self.border_size * 2,
|
let (size, position) = calculate_window_dimensions(
|
||||||
aux_height - gap * 2 - self.border_size * 2,
|
self.screen_size.into(),
|
||||||
|
aux_window_size,
|
||||||
|
aux_position,
|
||||||
|
client.is_fullscreen(),
|
||||||
|
i as i32,
|
||||||
|
gap,
|
||||||
|
self.border_size,
|
||||||
);
|
);
|
||||||
|
|
||||||
let position =
|
|
||||||
(master_width + gap * 2, aux_height * i as i32 + gap * 2);
|
|
||||||
|
|
||||||
if let Some(client) = self.clients.get_mut(key) {
|
|
||||||
*client = Client {
|
*client = Client {
|
||||||
size: size.into(),
|
size: size.into(),
|
||||||
position: position.into(),
|
position,
|
||||||
..*client
|
..*client
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
44
src/state.rs
44
src/state.rs
|
@ -4,13 +4,14 @@ use log::{error, info};
|
||||||
|
|
||||||
use x11::xlib::{self, Window};
|
use x11::xlib::{self, Window};
|
||||||
|
|
||||||
|
use crate::backends::window_event::{FullscreenEvent, FullscreenState};
|
||||||
|
use crate::util::{Point, Size};
|
||||||
use crate::{
|
use crate::{
|
||||||
backends::{
|
backends::{
|
||||||
keycodes::{MouseButton, VirtualKeyCode},
|
keycodes::{MouseButton, VirtualKeyCode},
|
||||||
window_event::{
|
window_event::{
|
||||||
ButtonEvent, ConfigureEvent, KeyBind, KeyEvent, KeyState, MapEvent,
|
ButtonEvent, ConfigureEvent, KeyBind, KeyEvent, KeyState, MapEvent,
|
||||||
ModifierKey, ModifierState, MotionEvent, MouseBind, Point,
|
ModifierKey, ModifierState, MotionEvent, MouseBind, WindowEvent,
|
||||||
WindowEvent,
|
|
||||||
},
|
},
|
||||||
xlib::XLib,
|
xlib::XLib,
|
||||||
WindowServerBackend,
|
WindowServerBackend,
|
||||||
|
@ -105,7 +106,7 @@ struct MoveInfoInner {
|
||||||
struct ResizeInfoInner {
|
struct ResizeInfoInner {
|
||||||
window: Window,
|
window: Window,
|
||||||
starting_cursor_pos: Point<i32>,
|
starting_cursor_pos: Point<i32>,
|
||||||
starting_window_size: Point<i32>,
|
starting_window_size: Size<i32>,
|
||||||
}
|
}
|
||||||
|
|
||||||
use derivative::*;
|
use derivative::*;
|
||||||
|
@ -464,6 +465,24 @@ where
|
||||||
// None => self.xlib.configure_window(event),
|
// None => self.xlib.configure_window(event),
|
||||||
// }
|
// }
|
||||||
}
|
}
|
||||||
|
WindowEvent::FullscreenEvent(FullscreenEvent {
|
||||||
|
window,
|
||||||
|
state,
|
||||||
|
}) => {
|
||||||
|
if match state {
|
||||||
|
FullscreenState::On => {
|
||||||
|
self.clients.set_fullscreen(&window, true)
|
||||||
|
}
|
||||||
|
FullscreenState::Off => {
|
||||||
|
self.clients.set_fullscreen(&window, false)
|
||||||
|
}
|
||||||
|
FullscreenState::Toggle => {
|
||||||
|
self.clients.toggle_fullscreen(&window)
|
||||||
|
}
|
||||||
|
} {
|
||||||
|
self.arrange_clients();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// i dont think i actually have to handle destroy notify events.
|
// i dont think i actually have to handle destroy notify events.
|
||||||
// every window should be unmapped regardless
|
// every window should be unmapped regardless
|
||||||
|
@ -652,6 +671,12 @@ where
|
||||||
self.clients
|
self.clients
|
||||||
.iter_transient()
|
.iter_transient()
|
||||||
.for_each(|(_, c)| self.backend.raise_window(c.window));
|
.for_each(|(_, c)| self.backend.raise_window(c.window));
|
||||||
|
|
||||||
|
//raise fullscreen windows
|
||||||
|
self.clients
|
||||||
|
.iter_current_screen()
|
||||||
|
.filter(|(_, c)| c.is_fullscreen())
|
||||||
|
.for_each(|(_, c)| self.backend.raise_window(c.window));
|
||||||
}
|
}
|
||||||
|
|
||||||
fn arrange_clients(&mut self) {
|
fn arrange_clients(&mut self) {
|
||||||
|
@ -756,12 +781,7 @@ where
|
||||||
|
|
||||||
let client = self.clients.get(&window).unwrap();
|
let client = self.clients.get(&window).unwrap();
|
||||||
|
|
||||||
let corner_pos = {
|
let corner_pos = client.position + client.size.into();
|
||||||
(
|
|
||||||
client.position.x + client.size.x,
|
|
||||||
client.position.y + client.size.y,
|
|
||||||
)
|
|
||||||
};
|
|
||||||
|
|
||||||
self.backend.move_cursor(None, corner_pos.into());
|
self.backend.move_cursor(None, corner_pos.into());
|
||||||
self.backend.grab_cursor();
|
self.backend.grab_cursor();
|
||||||
|
@ -820,8 +840,10 @@ where
|
||||||
{
|
{
|
||||||
let size = &mut client.size;
|
let size = &mut client.size;
|
||||||
|
|
||||||
size.x = std::cmp::max(1, info.starting_window_size.x + x);
|
size.width =
|
||||||
size.y = std::cmp::max(1, info.starting_window_size.y + y);
|
std::cmp::max(1, info.starting_window_size.width + x);
|
||||||
|
size.height =
|
||||||
|
std::cmp::max(1, info.starting_window_size.height + y);
|
||||||
|
|
||||||
self.backend.resize_window(client.window, client.size);
|
self.backend.resize_window(client.window, client.size);
|
||||||
}
|
}
|
||||||
|
|
215
src/util.rs
215
src/util.rs
|
@ -22,3 +22,218 @@ impl Hasher for IdentityHasher {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub type BuildIdentityHasher = BuildHasherDefault<IdentityHasher>;
|
pub type BuildIdentityHasher = BuildHasherDefault<IdentityHasher>;
|
||||||
|
|
||||||
|
pub use point::Point;
|
||||||
|
pub use size::Size;
|
||||||
|
|
||||||
|
mod size {
|
||||||
|
#[derive(Debug, Copy, Clone, PartialEq, PartialOrd)]
|
||||||
|
pub struct Size<I>
|
||||||
|
where
|
||||||
|
I: num_traits::PrimInt + num_traits::Zero,
|
||||||
|
{
|
||||||
|
pub width: I,
|
||||||
|
pub height: I,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<I> std::ops::Add for Size<I>
|
||||||
|
where
|
||||||
|
I: num_traits::PrimInt + num_traits::Zero,
|
||||||
|
{
|
||||||
|
type Output = Self;
|
||||||
|
|
||||||
|
fn add(self, rhs: Self) -> Self::Output {
|
||||||
|
Self {
|
||||||
|
width: self.width + rhs.width,
|
||||||
|
height: self.height + rhs.height,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<I> std::ops::Sub for Size<I>
|
||||||
|
where
|
||||||
|
I: num_traits::PrimInt + num_traits::Zero,
|
||||||
|
{
|
||||||
|
type Output = Self;
|
||||||
|
|
||||||
|
fn sub(self, rhs: Self) -> Self::Output {
|
||||||
|
Self {
|
||||||
|
width: self.width - rhs.width,
|
||||||
|
height: self.height - rhs.height,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<I> num_traits::Zero for Size<I>
|
||||||
|
where
|
||||||
|
I: num_traits::PrimInt + num_traits::Zero,
|
||||||
|
{
|
||||||
|
fn zero() -> Self {
|
||||||
|
Self::default()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn is_zero(&self) -> bool {
|
||||||
|
self.width == I::zero() && self.height == I::zero()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<I> Default for Size<I>
|
||||||
|
where
|
||||||
|
I: num_traits::PrimInt + num_traits::Zero,
|
||||||
|
{
|
||||||
|
fn default() -> Self {
|
||||||
|
Self {
|
||||||
|
width: I::zero(),
|
||||||
|
height: I::zero(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<I> From<(I, I)> for Size<I>
|
||||||
|
where
|
||||||
|
I: num_traits::PrimInt + num_traits::Zero,
|
||||||
|
{
|
||||||
|
fn from(value: (I, I)) -> Self {
|
||||||
|
Self::from_tuple(value)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<I> From<super::point::Point<I>> for Size<I>
|
||||||
|
where
|
||||||
|
I: num_traits::PrimInt + num_traits::Zero,
|
||||||
|
{
|
||||||
|
fn from(value: super::point::Point<I>) -> Self {
|
||||||
|
Self::new(value.x, value.y)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<I> Size<I>
|
||||||
|
where
|
||||||
|
I: num_traits::PrimInt + num_traits::Zero,
|
||||||
|
{
|
||||||
|
pub fn new(width: I, height: I) -> Self {
|
||||||
|
Self { width, height }
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn from_tuple(tuple: (I, I)) -> Self {
|
||||||
|
Self::new(tuple.0, tuple.1)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn as_tuple(&self) -> (I, I) {
|
||||||
|
(self.width, self.height)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn map<F>(self, f: F) -> Self
|
||||||
|
where
|
||||||
|
F: FnOnce(I, I) -> Self,
|
||||||
|
{
|
||||||
|
f(self.width, self.height)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
mod point {
|
||||||
|
#[derive(Debug, Copy, Clone, PartialEq, PartialOrd)]
|
||||||
|
pub struct Point<I>
|
||||||
|
where
|
||||||
|
I: num_traits::PrimInt + num_traits::Zero,
|
||||||
|
{
|
||||||
|
pub x: I,
|
||||||
|
pub y: I,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<I> std::ops::Add for Point<I>
|
||||||
|
where
|
||||||
|
I: num_traits::PrimInt + num_traits::Zero,
|
||||||
|
{
|
||||||
|
type Output = Self;
|
||||||
|
|
||||||
|
fn add(self, rhs: Self) -> Self::Output {
|
||||||
|
Self {
|
||||||
|
x: self.x + rhs.x,
|
||||||
|
y: self.y + rhs.y,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<I> std::ops::Sub for Point<I>
|
||||||
|
where
|
||||||
|
I: num_traits::PrimInt + num_traits::Zero,
|
||||||
|
{
|
||||||
|
type Output = Self;
|
||||||
|
|
||||||
|
fn sub(self, rhs: Self) -> Self::Output {
|
||||||
|
Self {
|
||||||
|
x: self.x - rhs.x,
|
||||||
|
y: self.y - rhs.y,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<I> num_traits::Zero for Point<I>
|
||||||
|
where
|
||||||
|
I: num_traits::PrimInt + num_traits::Zero,
|
||||||
|
{
|
||||||
|
fn zero() -> Self {
|
||||||
|
Self::default()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn is_zero(&self) -> bool {
|
||||||
|
self.x == I::zero() && self.y == I::zero()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<I> Default for Point<I>
|
||||||
|
where
|
||||||
|
I: num_traits::PrimInt + num_traits::Zero,
|
||||||
|
{
|
||||||
|
fn default() -> Self {
|
||||||
|
Self {
|
||||||
|
x: I::zero(),
|
||||||
|
y: I::zero(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<I> From<(I, I)> for Point<I>
|
||||||
|
where
|
||||||
|
I: num_traits::PrimInt + num_traits::Zero,
|
||||||
|
{
|
||||||
|
fn from(value: (I, I)) -> Self {
|
||||||
|
Self::from_tuple(value)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<I> From<super::size::Size<I>> for Point<I>
|
||||||
|
where
|
||||||
|
I: num_traits::PrimInt + num_traits::Zero,
|
||||||
|
{
|
||||||
|
fn from(value: super::size::Size<I>) -> Self {
|
||||||
|
Self::new(value.width, value.height)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<I> Point<I>
|
||||||
|
where
|
||||||
|
I: num_traits::PrimInt + num_traits::Zero,
|
||||||
|
{
|
||||||
|
pub fn new(x: I, y: I) -> Self {
|
||||||
|
Self { x, y }
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn from_tuple(tuple: (I, I)) -> Self {
|
||||||
|
Self::new(tuple.0, tuple.1)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn as_tuple(&self) -> (I, I) {
|
||||||
|
(self.x, self.y)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn map<F, T>(self, f: F) -> T
|
||||||
|
where
|
||||||
|
F: FnOnce(I, I) -> T,
|
||||||
|
{
|
||||||
|
f(self.x, self.y)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
Loading…
Reference in a new issue