Skip to content

Commit

Permalink
fixing #113 and adding proper mouse support (#300)
Browse files Browse the repository at this point in the history
* fixing #113 and adding proper mouse support

* removing commented out fields of the WindowManager struct
  • Loading branch information
sminez authored Jun 22, 2024
1 parent cecad76 commit a0c77a1
Show file tree
Hide file tree
Showing 12 changed files with 332 additions and 49 deletions.
4 changes: 2 additions & 2 deletions Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[package]
name = "penrose"
version = "0.3.5"
version = "0.3.6"
edition = "2021"
authors = ["sminez <[email protected]>"]
license = "MIT"
Expand Down Expand Up @@ -30,7 +30,7 @@ x11rb-xcb = ["x11rb", "x11rb/allow-unsafe-code"]
anymap = "0.12"
bitflags = { version = "2.5", features = ["serde"] }
nix = { version = "0.29", default-features = false, features = ["signal"] }
penrose_keysyms = { version = "0.3.4", path = "crates/penrose_keysyms", optional = true }
penrose_keysyms = { version = "0.3.6", path = "crates/penrose_keysyms", optional = true }
serde = { version = "1.0", features = ["derive"], optional = true }
strum = { version = "0.26", features = ["derive"] }
thiserror = "1.0"
Expand Down
2 changes: 1 addition & 1 deletion crates/penrose_keysyms/Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[package]
name = "penrose_keysyms"
version = "0.3.5"
version = "0.3.6"
authors = ["IDAM <[email protected]>"]
edition = "2018"
license = "MIT"
Expand Down
4 changes: 2 additions & 2 deletions crates/penrose_ui/Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[package]
name = "penrose_ui"
version = "0.3.5"
version = "0.3.6"
edition = "2021"
authors = ["sminez <[email protected]>"]
license = "MIT"
Expand All @@ -12,7 +12,7 @@ description = "UI elements for the penrose window manager library"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html

[dependencies]
penrose = { version = "0.3.5", path = "../../" }
penrose = { version = "0.3.6", path = "../../" }
tracing = { version = "0.1", features = ["attributes"] }
thiserror = "1.0"
yeslogic-fontconfig-sys = "5.0"
Expand Down
27 changes: 24 additions & 3 deletions examples/minimal/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,17 @@
//! has multiple workspaces and simple client / workspace movement.
use penrose::{
builtin::{
actions::{exit, modify_with, send_layout_message, spawn},
actions::{
exit,
floating::{sink_clicked, MouseDragHandler, MouseResizeHandler},
modify_with, send_layout_message, spawn,
},
layout::messages::{ExpandMain, IncMain, ShrinkMain},
},
core::{
bindings::{parse_keybindings_with_xmodmap, KeyEventHandler},
bindings::{
parse_keybindings_with_xmodmap, KeyEventHandler, MouseEventHandler, MouseState,
},
Config, WindowManager,
},
map,
Expand Down Expand Up @@ -57,6 +63,21 @@ fn raw_key_bindings() -> HashMap<String, Box<dyn KeyEventHandler<RustConn>>> {
raw_bindings
}

fn mouse_bindings() -> HashMap<MouseState, Box<dyn MouseEventHandler<RustConn>>> {
use penrose::core::bindings::{
ModifierKey::{Meta, Shift},
MouseButton::{Left, Middle, Right},
};

map! {
map_keys: |(button, modifiers)| MouseState { button, modifiers };

(Left, vec![Shift, Meta]) => MouseDragHandler::boxed_default(),
(Right, vec![Shift, Meta]) => MouseResizeHandler::boxed_default(),
(Middle, vec![Shift, Meta]) => sink_clicked(),
}
}

fn main() -> Result<()> {
tracing_subscriber::fmt()
.with_env_filter("info")
Expand All @@ -65,7 +86,7 @@ fn main() -> Result<()> {

let conn = RustConn::new()?;
let key_bindings = parse_keybindings_with_xmodmap(raw_key_bindings())?;
let wm = WindowManager::new(Config::default(), key_bindings, HashMap::new(), conn)?;
let wm = WindowManager::new(Config::default(), key_bindings, mouse_bindings(), conn)?;

wm.run()
}
Expand Down
161 changes: 159 additions & 2 deletions src/builtin/actions/floating.rs
Original file line number Diff line number Diff line change
@@ -1,8 +1,16 @@
//! Actions for manipulating floating windows.
use crate::{
builtin::actions::{key_handler, modify_with},
core::bindings::KeyEventHandler,
builtin::actions::{key_handler, modify_with, mouse_modify_with},
core::{
bindings::{
KeyEventHandler, MotionNotifyEvent, MouseEvent, MouseEventHandler, MouseEventKind,
},
State,
},
custom_error,
pure::geometry::{Point, Rect},
x::{XConn, XConnExt},
Result, Xid,
};
use tracing::error;

Expand Down Expand Up @@ -93,3 +101,152 @@ pub fn float_all<X: XConn>() -> Box<dyn KeyEventHandler<X>> {
pub fn sink_all<X: XConn>() -> Box<dyn KeyEventHandler<X>> {
modify_with(|cs| cs.floating.clear())
}

#[derive(Debug, Default, Clone, Copy)]
struct ClickData {
x_initial: i32,
y_initial: i32,
r_initial: Rect,
}

impl ClickData {
fn on_motion<X: XConn>(
&self,
f: impl Fn(&mut Rect, i32, i32),
id: Xid,
rpt: Point,
state: &mut State<X>,
x: &X,
) -> Result<()> {
let (dx, dy) = (rpt.x as i32 - self.x_initial, rpt.y as i32 - self.y_initial);

let mut r = self.r_initial;
(f)(&mut r, dx, dy);

state.client_set.float(id, r)?;
x.position_client(id, r)?;

Ok(())
}
}

trait ClickWrapper {
fn data(&mut self) -> &mut Option<ClickData>;

fn motion_fn(&self) -> impl Fn(&mut Rect, i32, i32);

fn on_mouse_event<X: XConn>(
&mut self,
evt: &MouseEvent,
state: &mut State<X>,
x: &X,
) -> Result<()> {
let id = evt.data.id;

match evt.kind {
MouseEventKind::Press => {
let r_client = x.client_geometry(id)?;
state.client_set.float(id, r_client)?;
*self.data() = Some(ClickData {
x_initial: evt.data.rpt.x as i32,
y_initial: evt.data.rpt.y as i32,
r_initial: r_client,
});
}

MouseEventKind::Release => *self.data() = None,
}

Ok(())
}

fn on_motion<X: XConn>(
&mut self,
evt: &MotionNotifyEvent,
state: &mut State<X>,
x: &X,
) -> Result<()> {
match *self.data() {
Some(data) => data.on_motion(self.motion_fn(), evt.data.id, evt.data.rpt, state, x),
None => Err(custom_error!("mouse motion without held state")),
}
}
}

/// A simple mouse event handler for dragging a window
#[derive(Debug, Default, Clone)]
pub struct MouseDragHandler {
data: Option<ClickData>,
}

impl MouseDragHandler {
/// Construct a boxed [MouseEventHandler] trait object ready to be added to your bindings
pub fn boxed_default<X: XConn>() -> Box<dyn MouseEventHandler<X>> {
Box::<MouseDragHandler>::default()
}
}

impl ClickWrapper for MouseDragHandler {
fn data(&mut self) -> &mut Option<ClickData> {
&mut self.data
}

fn motion_fn(&self) -> impl Fn(&mut Rect, i32, i32) {
|r, dx, dy| r.reposition(dx, dy)
}
}

impl<X: XConn> MouseEventHandler<X> for MouseDragHandler {
fn on_mouse_event(&mut self, evt: &MouseEvent, state: &mut State<X>, x: &X) -> Result<()> {
ClickWrapper::on_mouse_event(self, evt, state, x)
}

fn on_motion(&mut self, evt: &MotionNotifyEvent, state: &mut State<X>, x: &X) -> Result<()> {
ClickWrapper::on_motion(self, evt, state, x)
}
}

/// A simple mouse event handler for resizing a window
#[derive(Debug, Default, Clone)]
pub struct MouseResizeHandler {
data: Option<ClickData>,
}

impl MouseResizeHandler {
/// Construct a boxed [MouseEventHandler] trait object ready to be added to your bindings
pub fn boxed_default<X: XConn>() -> Box<dyn MouseEventHandler<X>> {
Box::<MouseResizeHandler>::default()
}
}

impl ClickWrapper for MouseResizeHandler {
fn data(&mut self) -> &mut Option<ClickData> {
&mut self.data
}

fn motion_fn(&self) -> impl Fn(&mut Rect, i32, i32) {
|r, dw, dh| r.resize(dw, dh)
}
}

impl<X: XConn> MouseEventHandler<X> for MouseResizeHandler {
fn on_mouse_event(&mut self, evt: &MouseEvent, state: &mut State<X>, x: &X) -> Result<()> {
ClickWrapper::on_mouse_event(self, evt, state, x)
}

fn on_motion(&mut self, evt: &MotionNotifyEvent, state: &mut State<X>, x: &X) -> Result<()> {
ClickWrapper::on_motion(self, evt, state, x)
}
}

/// Sink the current window back into tiling mode if it was floating
pub fn sink_clicked<X: XConn>() -> Box<dyn MouseEventHandler<X>> {
mouse_modify_with(|cs| {
let id = match cs.current_client() {
Some(&id) => id,
None => return,
};

cs.sink(&id);
})
}
30 changes: 29 additions & 1 deletion src/builtin/actions/mod.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,10 @@
//! Helpers and pre-defined actions for use in user defined key bindings
use crate::{
core::{bindings::KeyEventHandler, layout::IntoMessage, ClientSet, State},
core::{
bindings::{KeyEventHandler, MouseEventHandler},
layout::IntoMessage,
ClientSet, State,
},
util,
x::{XConn, XConnExt},
Result,
Expand Down Expand Up @@ -102,3 +106,27 @@ pub fn remove_and_unmap_focused_client<X: XConn>() -> Box<dyn KeyEventHandler<X>
}
})
}

// NOTE: this is here to force the correct lifetime requirements on closures being
// used as handlers. The generic impl in crate::bindings for functions of the
// right signature isn't sufficient on its own.

/// Construct a [MouseEventHandler] from a closure or free function.
///
/// The resulting handler will run on button press events.
pub fn mouse_handler<F, X>(f: F) -> Box<dyn MouseEventHandler<X>>
where
F: FnMut(&mut State<X>, &X) -> Result<()> + 'static,
X: XConn,
{
Box::new(f)
}

/// Mutate the [ClientSet] and refresh the on screen state
pub fn mouse_modify_with<F, X>(f: F) -> Box<dyn MouseEventHandler<X>>
where
F: FnMut(&mut ClientSet) + Clone + 'static,
X: XConn,
{
Box::new(move |s: &mut State<X>, x: &X| x.modify_and_refresh(s, f.clone()))
}
Loading

0 comments on commit a0c77a1

Please sign in to comment.