summaryrefslogtreecommitdiff
path: root/src/buttons.rs
diff options
context:
space:
mode:
Diffstat (limited to 'src/buttons.rs')
-rw-r--r--src/buttons.rs125
1 files changed, 121 insertions, 4 deletions
diff --git a/src/buttons.rs b/src/buttons.rs
index a7d8a34..da6c9c2 100644
--- a/src/buttons.rs
+++ b/src/buttons.rs
@@ -1,10 +1,10 @@
//! Mouse button related code
-use crate::{config::BUTTON_BINDS, conn_info::Connection, debug, WM};
+use crate::{config::BUTTON_BINDS, conn_info::Connection, debug, Error, Result, WM};
use xcb::{
x::{
- ButtonIndex, ButtonPressEvent, Cursor, EventMask, GrabButton, GrabMode, ModMask,
- UngrabButton, Window,
+ self, ButtonIndex, ButtonPressEvent, Cursor, EventMask, GrabButton, GrabMode, GrabPointer,
+ ModMask, MotionNotifyEvent, QueryPointer, UngrabButton, UngrabPointer, Window,
},
Xid,
};
@@ -27,13 +27,130 @@ impl WM<'_> {
.difference(self.conn.keyboard_state.numlock_mask() | ModMask::LOCK),
);
}
+
+ /// Move the currently focused window until the mouse button is released.
+ /// This starts its own event loop, so won't exit till the user is done moving the window.
+ pub fn mouse_move(&mut self) -> Result<()> {
+ let Some(w) = self.clients.focused_mut().map(|c| c.window()) else {
+ return Ok(());
+ };
+
+ let reply = self
+ .conn
+ .wait_for_reply(self.conn.send_request(&QueryPointer { window: w }))?;
+ let (original_x, original_y) = (reply.win_x(), reply.win_y());
+
+ self.track_mouse_movement(w, |this, e| {
+ if let Some(c) = this.clients.focused_mut() {
+ c.set_geom(
+ &this.conn,
+ e.root_x() - original_x,
+ e.root_y() - original_y,
+ c.width(),
+ c.height(),
+ c.border_width(),
+ );
+ c.configure_notify(&this.conn);
+ }
+
+ Ok(())
+ })
+ }
+
+ /// Resize the currently focused window with the mouse.
+ /// This starts its own event loop, so won't exit till the user is done resizing the window.
+ pub fn mouse_resize(&mut self) -> Result<()> {
+ let Some((w, original_width, original_height)) = self
+ .clients
+ .focused_mut()
+ .map(|c| (c.window(), c.width(), c.height()))
+ else {
+ return Ok(());
+ };
+
+ let reply = self
+ .conn
+ .wait_for_reply(self.conn.send_request(&QueryPointer { window: w }))?;
+ let (original_x, original_y) = (reply.win_x(), reply.win_y());
+
+ self.track_mouse_movement(w, |this, e| {
+ if let Some(c) = this.clients.focused_mut() {
+ c.set_geom(
+ &this.conn,
+ c.x(),
+ c.y(),
+ original_width.saturating_add_signed(e.root_x() - original_x),
+ original_height.saturating_add_signed(e.root_y() - original_y),
+ c.border_width(),
+ );
+ c.configure_notify(&this.conn);
+ }
+
+ Ok(())
+ })
+ }
+
+ /// Start tracking pointer movement, calling `f` with each generated motion event.
+ /// This starts its own event loop, so won't exit till the user is done resizing the window.
+ pub fn track_mouse_movement(
+ &mut self,
+ w: Window,
+ mut f: impl FnMut(&mut Self, MotionNotifyEvent) -> Result<()>,
+ ) -> Result<()> {
+ self.conn
+ .wait_for_reply(self.conn.send_request(&GrabPointer {
+ owner_events: false,
+ grab_window: w,
+ event_mask: EventMask::BUTTON_PRESS
+ | EventMask::BUTTON_RELEASE
+ | EventMask::POINTER_MOTION,
+ pointer_mode: GrabMode::Async,
+ keyboard_mode: GrabMode::Async,
+ confine_to: Window::none(),
+ cursor: self.conn.cursors.moving(),
+ time: x::CURRENT_TIME,
+ }))?;
+
+ let mut last_time = 0;
+ let mut res = Ok(());
+ while res.is_ok() {
+ match self.conn.wait_for_event() {
+ Ok(xcb::Event::X(x::Event::MotionNotify(e))) => {
+ if e.time() - last_time <= (1000 / 60) {
+ continue;
+ }
+
+ last_time = e.time();
+ res = f(self, e);
+ res = res.and(self.conn.flush());
+ }
+ Ok(xcb::Event::X(x::Event::ButtonRelease(_))) => {
+ break;
+ }
+ Ok(e) => self.dispatch_event(e)?,
+ Err(Error::Xcb(xcb::Error::Protocol(e))) => {
+ eprintln!("protocol error in event loop: {e:#?}\ncontinuing anyway");
+ }
+ Err(e) => {
+ eprintln!("unrecoverable error: {e:#?}\nexiting event loop");
+ res = Err(e);
+ }
+ }
+ }
+
+ self.conn.send_request(&UngrabPointer {
+ time: x::CURRENT_TIME,
+ });
+
+ res
+ }
}
pub fn grab(conn: &Connection<'_>, window: Window, focused: bool) {
conn.send_request(&UngrabButton {
button: ButtonIndex::Any,
- grab_window: window,
modifiers: ModMask::ANY,
+ grab_window: window,
});
if !focused {