atagen

monolith

public
Star0
Watch0

Commit ccb74661e8c4

tarball

atagen <boss@atagen.co>2026-10-19 10:59 UTC

1 file changed+21-15
input: end a drag only on its own button

Any button release ended a Mod drag, so pressing and releasing a second button mid-drag stopped it and the later release of the real button reached the client with no press. Drag now always records its starting button and a separate flag says whether the client still sees the release. A new press can no longer replace a drag in progress.
Modified file: crates/monolith/src/input.rs+21 additions−15 deletions
@@ -130,11 +130,8 @@ impl State {
130130 pub fn pointer_button(&mut self, button: u32, pressed: ButtonState, time: InputTime) {
131131 self.activity();
132132 if pressed == ButtonState::Released {
133- let ended = self
134- .input
135- .drag
136- .take_if(|d| d.button.is_none_or(|b| b == button));
137- if ended.is_some() {
133+ let ended = self.input.drag.take_if(|d| d.button == button);
134+ if let Some(d) = ended {
138135 if self.wm.resizing.take().is_some() {
139136 self.apply_layout();
140137 }
@@ -143,15 +140,15 @@ impl State {
143140 .edge_at(pos, self.cover_under(pos).map(|c| c.2))
144141 .map(|(_, e)| e);
145142 self.dirty.cursor = true;
146- }
147- if ended.is_some_and(|d| d.button.is_none()) {
148- return;
143+ if !d.client {
144+ return;
145+ }
149146 }
150147 }
151148 if pressed == ButtonState::Pressed && !self.services.lock.active() && !self.pointer_locked()
152149 {
153150 focus_under_pointer(self);
154- if start_drag(self, button) {
151+ if self.input.drag.is_none() && start_drag(self, button) {
155152 return;
156153 }
157154 }
@@ -490,8 +487,10 @@ pub struct Drag {
490487 tiled: bool,
491488 /// None moves the window.
492489 edges: Option<Edges>,
493- /// The client's button, whose release is still the client's to see.
494- button: Option<u32>,
490+ /// The button the drag started with, whose release ends it.
491+ button: u32,
492+ /// Whether the client asked for the drag and so still sees the release.
493+ client: bool,
495494 }
496495
497496 impl Input {
@@ -580,7 +579,7 @@ fn start_drag(state: &mut State, button: u32) -> bool {
580579 state.input.followed = state.wm.layout.focus(id);
581580 state.apply_layout();
582581 }
583- return begin_drag(state, id, Some(edges), None);
582+ return begin_drag(state, id, Some(edges), button, false);
584583 }
585584 let Some(id) = state.wm.layout.focused() else {
586585 return false;
@@ -594,7 +593,7 @@ fn start_drag(state: &mut State, button: u32) -> bool {
594593 }
595594 _ => None,
596595 };
597- begin_drag(state, id, edges, None)
596+ begin_drag(state, id, edges, button, false)
598597 }
599598
600599 fn window_rect(layout: &Layout, id: WindowId) -> Option<Rect> {
@@ -615,12 +614,18 @@ impl State {
615614 /// client's request. Tiled windows are left alone.
616615 pub fn client_drag(&mut self, id: WindowId, edges: Option<Edges>, button: u32) {
617616 if self.wm.layout.window(id).is_some_and(|w| w.floating) {
618- begin_drag(self, id, edges, Some(button));
617+ begin_drag(self, id, edges, button, true);
619618 }
620619 }
621620 }
622621
623-fn begin_drag(state: &mut State, id: WindowId, edges: Option<Edges>, button: Option<u32>) -> bool {
622+fn begin_drag(
623+ state: &mut State,
624+ id: WindowId,
625+ edges: Option<Edges>,
626+ button: u32,
627+ client: bool,
628+) -> bool {
624629 let layout = &state.wm.layout;
625630 let Some(win) = layout.window(id).filter(|w| !w.fullscreen) else {
626631 return false;
@@ -656,6 +661,7 @@ fn begin_drag(state: &mut State, id: WindowId, edges: Option<Edges>, button: Opt
656661 tiled,
657662 edges,
658663 button,
664+ client,
659665 });
660666 true
661667 }