Commit 25731952c6b8
tarball3 files changed+179-20
@@ -7,9 +7,11 @@ use smithay::backend::input::{ButtonState, InputTime, KeyState};
77
use smithay::backend::session::Session;88
use smithay::input::keyboard::{FilterResult, Keycode, xkb};99
use smithay::input::pointer::{AxisFrame, ButtonEvent, CursorImageStatus, MotionEvent};10
+use smithay::reexports::wayland_protocols::xdg::shell::server::xdg_toplevel;1011
use smithay::reexports::wayland_server::Resource;1112
use smithay::utils::{Logical, Point, SERIAL_COUNTER};1213
use smithay::wayland::shell::wlr_layer::Layer;14
+use smithay::xwayland::xwm::ResizeEdge;1315
1416
impl State {1517
pub fn pointer_to(&mut self, pos: Point<f64, Logical>, time: InputTime) {@@ -42,8 +44,14 @@ impl State {
4244
4345
pub fn pointer_button(&mut self, button: u32, pressed: ButtonState, time: InputTime) {4446
self.activity();45
- if pressed == ButtonState::Released && self.input.drag.take().is_some() {46
- return;47
+ if pressed == ButtonState::Released {48
+ let ended = self49
+ .input50
+ .drag51
+ .take_if(|d| d.button.is_none_or(|b| b == button));52
+ if ended.is_some_and(|d| d.button.is_none()) {53
+ return;54
+ }4755
}4856
if pressed == ButtonState::Pressed && !self.services.lock.active() {4957
focus_under_pointer(self);@@ -236,13 +244,76 @@ impl State {
236244
237245
const BTN_LEFT: u32 = 0x110;238246
const BTN_RIGHT: u32 = 0x111;247
+const MIN_SIZE: f64 = 32.0;248
+249
+/// The sides of a window a resize moves.250
+#[derive(Clone, Copy, Default)]251
+pub struct Edges {252
+ left: bool,253
+ right: bool,254
+ top: bool,255
+ bottom: bool,256
+}239257
240
-/// A floating window being moved or resized with Mod and a mouse button.258
+impl Edges {259
+ const BOTTOM_RIGHT: Self = Self {260
+ left: false,261
+ right: true,262
+ top: false,263
+ bottom: true,264
+ };265
+266
+ pub fn from_xdg(edge: xdg_toplevel::ResizeEdge) -> Option<Self> {267
+ use xdg_toplevel::ResizeEdge as E;268
+ let (left, right, top, bottom) = match edge {269
+ E::Top => (false, false, true, false),270
+ E::Bottom => (false, false, false, true),271
+ E::Left => (true, false, false, false),272
+ E::Right => (false, true, false, false),273
+ E::TopLeft => (true, false, true, false),274
+ E::TopRight => (false, true, true, false),275
+ E::BottomLeft => (true, false, false, true),276
+ E::BottomRight => (false, true, false, true),277
+ _ => return None,278
+ };279
+ Some(Self {280
+ left,281
+ right,282
+ top,283
+ bottom,284
+ })285
+ }286
+287
+ pub fn from_x11(edge: ResizeEdge) -> Self {288
+ let (left, right, top, bottom) = match edge {289
+ ResizeEdge::Top => (false, false, true, false),290
+ ResizeEdge::Bottom => (false, false, false, true),291
+ ResizeEdge::Left => (true, false, false, false),292
+ ResizeEdge::Right => (false, true, false, false),293
+ ResizeEdge::TopLeft => (true, false, true, false),294
+ ResizeEdge::TopRight => (false, true, true, false),295
+ ResizeEdge::BottomLeft => (true, false, false, true),296
+ ResizeEdge::BottomRight => (false, true, false, true),297
+ };298
+ Self {299
+ left,300
+ right,301
+ top,302
+ bottom,303
+ }304
+ }305
+}306
+307
+/// A floating window being moved or resized, either with Mod and a mouse308
+/// button or because its client asked while holding `button`.241309
pub struct Drag {242310
window: WindowId,243311
from: Point<f64, Logical>,244312
rect: Rect,245
- resize: bool,313
+ /// None moves the window.314
+ edges: Option<Edges>,315
+ /// The client's button, whose release is still the client's to see.316
+ button: Option<u32>,246317
}247318
248319
fn start_drag(state: &mut State, button: u32) -> bool {@@ -258,6 +329,22 @@ fn start_drag(state: &mut State, button: u32) -> bool {
258329
let Some(id) = state.wm.layout.focused() else {259330
return false;260331
};332
+ if !held {333
+ return false;334
+ }335
+ let edges = (button == BTN_RIGHT).then_some(Edges::BOTTOM_RIGHT);336
+ begin_drag(state, id, edges, None)337
+}338
+339
+impl State {340
+ /// Starts moving, or with `edges` resizing, a floating window at a341
+ /// client's request. Tiled windows are left alone.342
+ pub fn client_drag(&mut self, id: WindowId, edges: Option<Edges>, button: u32) {343
+ begin_drag(self, id, edges, Some(button));344
+ }345
+}346
+347
+fn begin_drag(state: &mut State, id: WindowId, edges: Option<Edges>, button: Option<u32>) -> bool {261348
let Some(rect) = state262349
.wm263350
.layout@@ -267,28 +354,40 @@ fn start_drag(state: &mut State, button: u32) -> bool {
267354
else {268355
return false;269356
};270
- if !held {271
- return false;272
- }273357
state.input.drag = Some(Drag {274358
window: id,275359
from: state.input.pointer_pos,276360
rect,277
- resize: button == BTN_RIGHT,361
+ edges,362
+ button,278363
});279364
true280365
}281366
367
+fn resized(start: f64, len: f64, delta: f64, low: bool, high: bool) -> (f64, f64) {368
+ if low {369
+ let len_now = (len - delta).max(MIN_SIZE);370
+ (start + len - len_now, len_now)371
+ } else if high {372
+ (start, (len + delta).max(MIN_SIZE))373
+ } else {374
+ (start, len)375
+ }376
+}377
+282378
fn drag_to(state: &mut State, pos: Point<f64, Logical>) -> bool {283379
let Some(d) = &state.input.drag else {284380
return false;285381
};286382
let (dx, dy) = (pos.x - d.from.x, pos.y - d.from.y);287383
let r = d.rect;288
- let rect = if d.resize {289
- Rect::new(r.x, r.y, (r.w + dx).max(32.0), (r.h + dy).max(32.0))290
- } else {291
- Rect::new(r.x + dx, r.y + dy, r.w, r.h)384
+ let rect = match d.edges {385
+ Some(e) => {386
+ let (x, w) = resized(r.x, r.w, dx, e.left, e.right);387
+ let (y, h) = resized(r.y, r.h, dy, e.top, e.bottom);388
+ Rect::new(x, y, w, h)389
+ }390
+ None => Rect::new(r.x + dx, r.y + dy, r.w, r.h),292391
};293392
let id = d.window;294393
state.wm.layout.set_float_rect(id, rect);@@ -1,10 +1,14 @@
1
+use crate::input::Edges;12
use crate::state::State;23
use monolith_layout::OutputId;34
use smithay::desktop::{PopupKind, Window};5
+use smithay::input::Seat;6
+use smithay::reexports::wayland_protocols::xdg::shell::server::xdg_toplevel;47
use smithay::reexports::wayland_protocols::xdg::decoration::zv1::server::zxdg_toplevel_decoration_v1::Mode as DecorationMode;58
use smithay::reexports::wayland_protocols::xdg::shell::server::xdg_toplevel::State as XdgState;69
use smithay::reexports::wayland_server::protocol::wl_output::WlOutput;710
use smithay::reexports::wayland_server::protocol::wl_seat::WlSeat;11
+use smithay::reexports::wayland_server::Resource;812
use smithay::utils::{Logical, Serial, Size};913
use smithay::wayland::shell::xdg::decoration::XdgDecorationHandler;1014
use smithay::wayland::compositor::with_states;@@ -46,6 +50,22 @@ impl XdgShellHandler for State {
4650
self.unmap_window(surface.wl_surface());4751
}4852
53
+ fn move_request(&mut self, surface: ToplevelSurface, seat: WlSeat, serial: Serial) {54
+ self.xdg_drag(&surface, &seat, serial, None);55
+ }56
+57
+ fn resize_request(58
+ &mut self,59
+ surface: ToplevelSurface,60
+ seat: WlSeat,61
+ serial: Serial,62
+ edges: xdg_toplevel::ResizeEdge,63
+ ) {64
+ if let Some(edges) = Edges::from_xdg(edges) {65
+ self.xdg_drag(&surface, &seat, serial, Some(edges));66
+ }67
+ }68
+4969
fn fullscreen_request(&mut self, surface: ToplevelSurface, output: Option<WlOutput>) {5070
self.set_fullscreen(&surface, true, output);5171
}@@ -102,6 +122,33 @@ fn server_side(toplevel: &ToplevelSurface) {
102122
}103123
104124
impl State {125
+ /// Honours a move or resize only for a serial that is the press the126
+ /// pointer is holding on one of the requesting client's surfaces.127
+ fn xdg_drag(128
+ &mut self,129
+ surface: &ToplevelSurface,130
+ seat: &WlSeat,131
+ serial: Serial,132
+ edges: Option<Edges>,133
+ ) {134
+ let Some(pointer) = Seat::<State>::from_resource(seat).and_then(|s| s.get_pointer()) else {135
+ return;136
+ };137
+ let Some(start) = pointer.grab_start_data() else {138
+ return;139
+ };140
+ let held_by_client = start141
+ .focus142
+ .as_ref()143
+ .is_some_and(|(f, _)| f.id().same_client_as(&surface.wl_surface().id()));144
+ if !pointer.has_grab(serial) || !held_by_client {145
+ return;146
+ }147
+ if let Some(id) = self.window_id(surface.wl_surface()) {148
+ self.client_drag(id, edges, start.button);149
+ }150
+ }151
+105152
/// A toplevel that has not mapped has no window to change, so its wish is106153
/// kept in its pending state for `map_window` to find.107154
fn set_fullscreen(&mut self, toplevel: &ToplevelSurface, on: bool, output: Option<WlOutput>) {@@ -4,6 +4,7 @@
44
//! leaves and the socket is watched again.55
66
use crate::focus::Focus;7
+use crate::input::Edges;78
use crate::state::State;89
use monolith_config::SelectionAccess;910
use smithay::desktop::Window;@@ -374,16 +375,13 @@ impl XwmHandler for State {
374375
}375376
}376377
377
- fn resize_request(378
- &mut self,379
- _xwm: XwmId,380
- _window: X11Surface,381
- _button: u32,382
- _edge: ResizeEdge,383
- ) {378
+ fn resize_request(&mut self, _xwm: XwmId, window: X11Surface, _button: u32, edge: ResizeEdge) {379
+ self.x11_drag(&window, Some(Edges::from_x11(edge)));384380
}385381
386
- fn move_request(&mut self, _xwm: XwmId, _window: X11Surface, _button: u32) {}382
+ fn move_request(&mut self, _xwm: XwmId, window: X11Surface, _button: u32) {383
+ self.x11_drag(&window, None);384
+ }387385
388386
fn fullscreen_request(&mut self, _xwm: XwmId, window: X11Surface) {389387
self.x11_fullscreen(&window, true);@@ -524,6 +522,21 @@ impl State {
524522
.map(|(id, _)| id)525523
}526524
525
+ /// X11 requests carry no serial, so the pointer must be holding a press.526
+ fn x11_drag(&mut self, window: &X11Surface, edges: Option<Edges>) {527
+ let Some(start) = self528
+ .input529
+ .seat530
+ .get_pointer()531
+ .and_then(|p| p.grab_start_data())532
+ else {533
+ return;534
+ };535
+ if let Some(id) = self.x11_window_id(window) {536
+ self.client_drag(id, edges, start.button);537
+ }538
+ }539
+527540
fn forget_x11(&mut self, window: &X11Surface) {528541
if let Some(x) = self.services.xwayland.as_mut() {529542
x.unmanaged.retain(|w| w != window);