feat: support interactive window move and resize
This commit is contained in:
1 parent
3c1cfe351c
commit
0cc0488257
7 files changed
+660
-13
No files matched your search
@@ -1,8 +1,8 @@
|
|||||||
# Ferese
|
# Ferese
|
||||||
|
|
||||||
Ferese is an experimental Wayland tiling compositor written in Rust with
|
Ferese is an experimental Wayland tiling compositor written in Rust with
|
||||||
Smithay. Development currently targets the Phase 0 nested compositor described
|
Smithay. The nested compositor is complete and development currently targets
|
||||||
in [spec.md](spec.md).
|
the M1 native protocol and interaction milestone in [spec.md](spec.md).
|
||||||
|
|
||||||
## Development
|
## Development
|
||||||
|
|
||||||
@@ -20,4 +20,3 @@ cargo run -p ferese -- foot
|
|||||||
```
|
```
|
||||||
|
|
||||||
Set `RUST_LOG=ferese=debug` for detailed compositor logging.
|
Set `RUST_LOG=ferese=debug` for detailed compositor logging.
|
||||||
|
|
||||||
@@ -0,0 +1,5 @@
|
|||||||
|
mod move_grab;
|
||||||
|
mod resize_grab;
|
||||||
|
|
||||||
|
pub use move_grab::MoveSurfaceGrab;
|
||||||
|
pub use resize_grab::{ResizeEdge, ResizeSurfaceGrab, handle_resize_commit};
|
||||||
@@ -0,0 +1,147 @@
|
|||||||
|
use smithay::{
|
||||||
|
desktop::Window,
|
||||||
|
input::pointer::{
|
||||||
|
AxisFrame, ButtonEvent, GestureHoldBeginEvent, GestureHoldEndEvent, GesturePinchBeginEvent,
|
||||||
|
GesturePinchEndEvent, GesturePinchUpdateEvent, GestureSwipeBeginEvent,
|
||||||
|
GestureSwipeEndEvent, GestureSwipeUpdateEvent, GrabStartData, MotionEvent, PointerGrab,
|
||||||
|
PointerInnerHandle, RelativeMotionEvent,
|
||||||
|
},
|
||||||
|
reexports::wayland_server::protocol::wl_surface::WlSurface,
|
||||||
|
utils::{Logical, Point},
|
||||||
|
};
|
||||||
|
|
||||||
|
use crate::Ferese;
|
||||||
|
|
||||||
|
pub struct MoveSurfaceGrab {
|
||||||
|
pub start_data: GrabStartData<Ferese>,
|
||||||
|
pub window: Window,
|
||||||
|
pub initial_location: Point<i32, Logical>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl PointerGrab<Ferese> for MoveSurfaceGrab {
|
||||||
|
fn motion(
|
||||||
|
&mut self,
|
||||||
|
data: &mut Ferese,
|
||||||
|
handle: &mut PointerInnerHandle<'_, Ferese>,
|
||||||
|
_focus: Option<(WlSurface, Point<f64, Logical>)>,
|
||||||
|
event: &MotionEvent,
|
||||||
|
) {
|
||||||
|
handle.motion(data, None, event);
|
||||||
|
let location = self.initial_location.to_f64() + (event.location - self.start_data.location);
|
||||||
|
data.space
|
||||||
|
.map_element(self.window.clone(), location.to_i32_round(), true);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn relative_motion(
|
||||||
|
&mut self,
|
||||||
|
data: &mut Ferese,
|
||||||
|
handle: &mut PointerInnerHandle<'_, Ferese>,
|
||||||
|
focus: Option<(WlSurface, Point<f64, Logical>)>,
|
||||||
|
event: &RelativeMotionEvent,
|
||||||
|
) {
|
||||||
|
handle.relative_motion(data, focus, event);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn button(
|
||||||
|
&mut self,
|
||||||
|
data: &mut Ferese,
|
||||||
|
handle: &mut PointerInnerHandle<'_, Ferese>,
|
||||||
|
event: &ButtonEvent,
|
||||||
|
) {
|
||||||
|
handle.button(data, event);
|
||||||
|
if handle.current_pressed().is_empty() {
|
||||||
|
handle.unset_grab(self, data, event.serial, event.time, true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn axis(
|
||||||
|
&mut self,
|
||||||
|
data: &mut Ferese,
|
||||||
|
handle: &mut PointerInnerHandle<'_, Ferese>,
|
||||||
|
frame: AxisFrame,
|
||||||
|
) {
|
||||||
|
handle.axis(data, frame);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn frame(&mut self, data: &mut Ferese, handle: &mut PointerInnerHandle<'_, Ferese>) {
|
||||||
|
handle.frame(data);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn gesture_swipe_begin(
|
||||||
|
&mut self,
|
||||||
|
data: &mut Ferese,
|
||||||
|
handle: &mut PointerInnerHandle<'_, Ferese>,
|
||||||
|
event: &GestureSwipeBeginEvent,
|
||||||
|
) {
|
||||||
|
handle.gesture_swipe_begin(data, event);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn gesture_swipe_update(
|
||||||
|
&mut self,
|
||||||
|
data: &mut Ferese,
|
||||||
|
handle: &mut PointerInnerHandle<'_, Ferese>,
|
||||||
|
event: &GestureSwipeUpdateEvent,
|
||||||
|
) {
|
||||||
|
handle.gesture_swipe_update(data, event);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn gesture_swipe_end(
|
||||||
|
&mut self,
|
||||||
|
data: &mut Ferese,
|
||||||
|
handle: &mut PointerInnerHandle<'_, Ferese>,
|
||||||
|
event: &GestureSwipeEndEvent,
|
||||||
|
) {
|
||||||
|
handle.gesture_swipe_end(data, event);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn gesture_pinch_begin(
|
||||||
|
&mut self,
|
||||||
|
data: &mut Ferese,
|
||||||
|
handle: &mut PointerInnerHandle<'_, Ferese>,
|
||||||
|
event: &GesturePinchBeginEvent,
|
||||||
|
) {
|
||||||
|
handle.gesture_pinch_begin(data, event);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn gesture_pinch_update(
|
||||||
|
&mut self,
|
||||||
|
data: &mut Ferese,
|
||||||
|
handle: &mut PointerInnerHandle<'_, Ferese>,
|
||||||
|
event: &GesturePinchUpdateEvent,
|
||||||
|
) {
|
||||||
|
handle.gesture_pinch_update(data, event);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn gesture_pinch_end(
|
||||||
|
&mut self,
|
||||||
|
data: &mut Ferese,
|
||||||
|
handle: &mut PointerInnerHandle<'_, Ferese>,
|
||||||
|
event: &GesturePinchEndEvent,
|
||||||
|
) {
|
||||||
|
handle.gesture_pinch_end(data, event);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn gesture_hold_begin(
|
||||||
|
&mut self,
|
||||||
|
data: &mut Ferese,
|
||||||
|
handle: &mut PointerInnerHandle<'_, Ferese>,
|
||||||
|
event: &GestureHoldBeginEvent,
|
||||||
|
) {
|
||||||
|
handle.gesture_hold_begin(data, event);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn gesture_hold_end(
|
||||||
|
&mut self,
|
||||||
|
data: &mut Ferese,
|
||||||
|
handle: &mut PointerInnerHandle<'_, Ferese>,
|
||||||
|
event: &GestureHoldEndEvent,
|
||||||
|
) {
|
||||||
|
handle.gesture_hold_end(data, event);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn start_data(&self) -> &GrabStartData<Ferese> {
|
||||||
|
&self.start_data
|
||||||
|
}
|
||||||
|
|
||||||
|
fn unset(&mut self, _data: &mut Ferese) {}
|
||||||
|
}
|
||||||
@@ -0,0 +1,401 @@
|
|||||||
|
use std::cell::RefCell;
|
||||||
|
|
||||||
|
use smithay::{
|
||||||
|
desktop::{Space, Window},
|
||||||
|
input::pointer::{
|
||||||
|
AxisFrame, ButtonEvent, GestureHoldBeginEvent, GestureHoldEndEvent, GesturePinchBeginEvent,
|
||||||
|
GesturePinchEndEvent, GesturePinchUpdateEvent, GestureSwipeBeginEvent,
|
||||||
|
GestureSwipeEndEvent, GestureSwipeUpdateEvent, GrabStartData, MotionEvent, PointerGrab,
|
||||||
|
PointerInnerHandle, RelativeMotionEvent,
|
||||||
|
},
|
||||||
|
reexports::{
|
||||||
|
wayland_protocols::xdg::shell::server::xdg_toplevel,
|
||||||
|
wayland_server::protocol::wl_surface::WlSurface,
|
||||||
|
},
|
||||||
|
utils::{Logical, Point, Rectangle, Size},
|
||||||
|
wayland::{compositor::with_states, shell::xdg::SurfaceCachedState},
|
||||||
|
};
|
||||||
|
|
||||||
|
use crate::Ferese;
|
||||||
|
|
||||||
|
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
|
||||||
|
pub struct ResizeEdge(xdg_toplevel::ResizeEdge);
|
||||||
|
|
||||||
|
impl From<xdg_toplevel::ResizeEdge> for ResizeEdge {
|
||||||
|
fn from(edge: xdg_toplevel::ResizeEdge) -> Self {
|
||||||
|
Self(edge)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ResizeEdge {
|
||||||
|
fn left(self) -> bool {
|
||||||
|
matches!(
|
||||||
|
self.0,
|
||||||
|
xdg_toplevel::ResizeEdge::Left
|
||||||
|
| xdg_toplevel::ResizeEdge::TopLeft
|
||||||
|
| xdg_toplevel::ResizeEdge::BottomLeft
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn right(self) -> bool {
|
||||||
|
matches!(
|
||||||
|
self.0,
|
||||||
|
xdg_toplevel::ResizeEdge::Right
|
||||||
|
| xdg_toplevel::ResizeEdge::TopRight
|
||||||
|
| xdg_toplevel::ResizeEdge::BottomRight
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn top(self) -> bool {
|
||||||
|
matches!(
|
||||||
|
self.0,
|
||||||
|
xdg_toplevel::ResizeEdge::Top
|
||||||
|
| xdg_toplevel::ResizeEdge::TopLeft
|
||||||
|
| xdg_toplevel::ResizeEdge::TopRight
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn bottom(self) -> bool {
|
||||||
|
matches!(
|
||||||
|
self.0,
|
||||||
|
xdg_toplevel::ResizeEdge::Bottom
|
||||||
|
| xdg_toplevel::ResizeEdge::BottomLeft
|
||||||
|
| xdg_toplevel::ResizeEdge::BottomRight
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct ResizeSurfaceGrab {
|
||||||
|
start_data: GrabStartData<Ferese>,
|
||||||
|
window: Window,
|
||||||
|
edges: ResizeEdge,
|
||||||
|
initial_rect: Rectangle<i32, Logical>,
|
||||||
|
last_size: Size<i32, Logical>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ResizeSurfaceGrab {
|
||||||
|
pub fn new(
|
||||||
|
start_data: GrabStartData<Ferese>,
|
||||||
|
window: Window,
|
||||||
|
edges: ResizeEdge,
|
||||||
|
initial_rect: Rectangle<i32, Logical>,
|
||||||
|
) -> Self {
|
||||||
|
ResizeState::with(
|
||||||
|
window
|
||||||
|
.toplevel()
|
||||||
|
.expect("managed window has a toplevel")
|
||||||
|
.wl_surface(),
|
||||||
|
|state| {
|
||||||
|
*state = ResizeState::Resizing {
|
||||||
|
edges,
|
||||||
|
initial_rect,
|
||||||
|
}
|
||||||
|
},
|
||||||
|
);
|
||||||
|
Self {
|
||||||
|
start_data,
|
||||||
|
window,
|
||||||
|
edges,
|
||||||
|
initial_rect,
|
||||||
|
last_size: initial_rect.size,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl PointerGrab<Ferese> for ResizeSurfaceGrab {
|
||||||
|
fn motion(
|
||||||
|
&mut self,
|
||||||
|
data: &mut Ferese,
|
||||||
|
handle: &mut PointerInnerHandle<'_, Ferese>,
|
||||||
|
_focus: Option<(WlSurface, Point<f64, Logical>)>,
|
||||||
|
event: &MotionEvent,
|
||||||
|
) {
|
||||||
|
handle.motion(data, None, event);
|
||||||
|
let delta = event.location - self.start_data.location;
|
||||||
|
let surface = self
|
||||||
|
.window
|
||||||
|
.toplevel()
|
||||||
|
.expect("managed window has a toplevel");
|
||||||
|
let (minimum, maximum) = with_states(surface.wl_surface(), |states| {
|
||||||
|
let mut cached = states.cached_state.get::<SurfaceCachedState>();
|
||||||
|
let state = cached.current();
|
||||||
|
(state.min_size, state.max_size)
|
||||||
|
});
|
||||||
|
self.last_size =
|
||||||
|
constrained_size(self.initial_rect.size, delta, self.edges, minimum, maximum);
|
||||||
|
|
||||||
|
surface.with_pending_state(|state| {
|
||||||
|
state.states.set(xdg_toplevel::State::Resizing);
|
||||||
|
state.size = Some(self.last_size);
|
||||||
|
});
|
||||||
|
surface.send_pending_configure();
|
||||||
|
}
|
||||||
|
|
||||||
|
fn relative_motion(
|
||||||
|
&mut self,
|
||||||
|
data: &mut Ferese,
|
||||||
|
handle: &mut PointerInnerHandle<'_, Ferese>,
|
||||||
|
focus: Option<(WlSurface, Point<f64, Logical>)>,
|
||||||
|
event: &RelativeMotionEvent,
|
||||||
|
) {
|
||||||
|
handle.relative_motion(data, focus, event);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn button(
|
||||||
|
&mut self,
|
||||||
|
data: &mut Ferese,
|
||||||
|
handle: &mut PointerInnerHandle<'_, Ferese>,
|
||||||
|
event: &ButtonEvent,
|
||||||
|
) {
|
||||||
|
handle.button(data, event);
|
||||||
|
if handle.current_pressed().is_empty() {
|
||||||
|
handle.unset_grab(self, data, event.serial, event.time, true);
|
||||||
|
let surface = self
|
||||||
|
.window
|
||||||
|
.toplevel()
|
||||||
|
.expect("managed window has a toplevel");
|
||||||
|
surface.with_pending_state(|state| {
|
||||||
|
state.states.unset(xdg_toplevel::State::Resizing);
|
||||||
|
state.size = Some(self.last_size);
|
||||||
|
});
|
||||||
|
surface.send_pending_configure();
|
||||||
|
ResizeState::with(surface.wl_surface(), |state| {
|
||||||
|
*state = ResizeState::WaitingForFinalCommit {
|
||||||
|
edges: self.edges,
|
||||||
|
initial_rect: self.initial_rect,
|
||||||
|
};
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn axis(
|
||||||
|
&mut self,
|
||||||
|
data: &mut Ferese,
|
||||||
|
handle: &mut PointerInnerHandle<'_, Ferese>,
|
||||||
|
frame: AxisFrame,
|
||||||
|
) {
|
||||||
|
handle.axis(data, frame);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn frame(&mut self, data: &mut Ferese, handle: &mut PointerInnerHandle<'_, Ferese>) {
|
||||||
|
handle.frame(data);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn gesture_swipe_begin(
|
||||||
|
&mut self,
|
||||||
|
data: &mut Ferese,
|
||||||
|
handle: &mut PointerInnerHandle<'_, Ferese>,
|
||||||
|
event: &GestureSwipeBeginEvent,
|
||||||
|
) {
|
||||||
|
handle.gesture_swipe_begin(data, event);
|
||||||
|
}
|
||||||
|
fn gesture_swipe_update(
|
||||||
|
&mut self,
|
||||||
|
data: &mut Ferese,
|
||||||
|
handle: &mut PointerInnerHandle<'_, Ferese>,
|
||||||
|
event: &GestureSwipeUpdateEvent,
|
||||||
|
) {
|
||||||
|
handle.gesture_swipe_update(data, event);
|
||||||
|
}
|
||||||
|
fn gesture_swipe_end(
|
||||||
|
&mut self,
|
||||||
|
data: &mut Ferese,
|
||||||
|
handle: &mut PointerInnerHandle<'_, Ferese>,
|
||||||
|
event: &GestureSwipeEndEvent,
|
||||||
|
) {
|
||||||
|
handle.gesture_swipe_end(data, event);
|
||||||
|
}
|
||||||
|
fn gesture_pinch_begin(
|
||||||
|
&mut self,
|
||||||
|
data: &mut Ferese,
|
||||||
|
handle: &mut PointerInnerHandle<'_, Ferese>,
|
||||||
|
event: &GesturePinchBeginEvent,
|
||||||
|
) {
|
||||||
|
handle.gesture_pinch_begin(data, event);
|
||||||
|
}
|
||||||
|
fn gesture_pinch_update(
|
||||||
|
&mut self,
|
||||||
|
data: &mut Ferese,
|
||||||
|
handle: &mut PointerInnerHandle<'_, Ferese>,
|
||||||
|
event: &GesturePinchUpdateEvent,
|
||||||
|
) {
|
||||||
|
handle.gesture_pinch_update(data, event);
|
||||||
|
}
|
||||||
|
fn gesture_pinch_end(
|
||||||
|
&mut self,
|
||||||
|
data: &mut Ferese,
|
||||||
|
handle: &mut PointerInnerHandle<'_, Ferese>,
|
||||||
|
event: &GesturePinchEndEvent,
|
||||||
|
) {
|
||||||
|
handle.gesture_pinch_end(data, event);
|
||||||
|
}
|
||||||
|
fn gesture_hold_begin(
|
||||||
|
&mut self,
|
||||||
|
data: &mut Ferese,
|
||||||
|
handle: &mut PointerInnerHandle<'_, Ferese>,
|
||||||
|
event: &GestureHoldBeginEvent,
|
||||||
|
) {
|
||||||
|
handle.gesture_hold_begin(data, event);
|
||||||
|
}
|
||||||
|
fn gesture_hold_end(
|
||||||
|
&mut self,
|
||||||
|
data: &mut Ferese,
|
||||||
|
handle: &mut PointerInnerHandle<'_, Ferese>,
|
||||||
|
event: &GestureHoldEndEvent,
|
||||||
|
) {
|
||||||
|
handle.gesture_hold_end(data, event);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn start_data(&self) -> &GrabStartData<Ferese> {
|
||||||
|
&self.start_data
|
||||||
|
}
|
||||||
|
|
||||||
|
fn unset(&mut self, _data: &mut Ferese) {}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Copy, Debug, Default)]
|
||||||
|
enum ResizeState {
|
||||||
|
#[default]
|
||||||
|
Idle,
|
||||||
|
Resizing {
|
||||||
|
edges: ResizeEdge,
|
||||||
|
initial_rect: Rectangle<i32, Logical>,
|
||||||
|
},
|
||||||
|
WaitingForFinalCommit {
|
||||||
|
edges: ResizeEdge,
|
||||||
|
initial_rect: Rectangle<i32, Logical>,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ResizeState {
|
||||||
|
fn with<T>(surface: &WlSurface, callback: impl FnOnce(&mut Self) -> T) -> T {
|
||||||
|
with_states(surface, |states| {
|
||||||
|
states.data_map.insert_if_missing(RefCell::<Self>::default);
|
||||||
|
callback(
|
||||||
|
&mut states
|
||||||
|
.data_map
|
||||||
|
.get::<RefCell<Self>>()
|
||||||
|
.expect("resize state was inserted")
|
||||||
|
.borrow_mut(),
|
||||||
|
)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
fn commit(&mut self) -> Option<(ResizeEdge, Rectangle<i32, Logical>)> {
|
||||||
|
match *self {
|
||||||
|
Self::Idle => None,
|
||||||
|
Self::Resizing {
|
||||||
|
edges,
|
||||||
|
initial_rect,
|
||||||
|
} => Some((edges, initial_rect)),
|
||||||
|
Self::WaitingForFinalCommit {
|
||||||
|
edges,
|
||||||
|
initial_rect,
|
||||||
|
} => {
|
||||||
|
*self = Self::Idle;
|
||||||
|
Some((edges, initial_rect))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn handle_resize_commit(space: &mut Space<Window>, surface: &WlSurface) {
|
||||||
|
let Some(window) = space
|
||||||
|
.elements()
|
||||||
|
.find(|window| {
|
||||||
|
window
|
||||||
|
.toplevel()
|
||||||
|
.is_some_and(|toplevel| toplevel.wl_surface() == surface)
|
||||||
|
})
|
||||||
|
.cloned()
|
||||||
|
else {
|
||||||
|
return;
|
||||||
|
};
|
||||||
|
let Some(mut location) = space.element_location(&window) else {
|
||||||
|
return;
|
||||||
|
};
|
||||||
|
let geometry = window.geometry();
|
||||||
|
let Some((edges, initial_rect)) = ResizeState::with(surface, ResizeState::commit) else {
|
||||||
|
return;
|
||||||
|
};
|
||||||
|
|
||||||
|
if edges.left() {
|
||||||
|
location.x = initial_rect.loc.x + initial_rect.size.w - geometry.size.w;
|
||||||
|
}
|
||||||
|
if edges.top() {
|
||||||
|
location.y = initial_rect.loc.y + initial_rect.size.h - geometry.size.h;
|
||||||
|
}
|
||||||
|
if edges.left() || edges.top() {
|
||||||
|
space.map_element(window, location, false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn constrained_size(
|
||||||
|
initial: Size<i32, Logical>,
|
||||||
|
delta: Point<f64, Logical>,
|
||||||
|
edges: ResizeEdge,
|
||||||
|
minimum: Size<i32, Logical>,
|
||||||
|
maximum: Size<i32, Logical>,
|
||||||
|
) -> Size<i32, Logical> {
|
||||||
|
let mut width = initial.w;
|
||||||
|
let mut height = initial.h;
|
||||||
|
if edges.left() {
|
||||||
|
width -= delta.x as i32;
|
||||||
|
} else if edges.right() {
|
||||||
|
width += delta.x as i32;
|
||||||
|
}
|
||||||
|
if edges.top() {
|
||||||
|
height -= delta.y as i32;
|
||||||
|
} else if edges.bottom() {
|
||||||
|
height += delta.y as i32;
|
||||||
|
}
|
||||||
|
|
||||||
|
let maximum_width = if maximum.w == 0 { i32::MAX } else { maximum.w };
|
||||||
|
let maximum_height = if maximum.h == 0 { i32::MAX } else { maximum.h };
|
||||||
|
(
|
||||||
|
width.clamp(minimum.w.max(1), maximum_width),
|
||||||
|
height.clamp(minimum.h.max(1), maximum_height),
|
||||||
|
)
|
||||||
|
.into()
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use super::*;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn left_and_top_edges_invert_pointer_delta() {
|
||||||
|
let size = constrained_size(
|
||||||
|
(800, 600).into(),
|
||||||
|
(100.0, 50.0).into(),
|
||||||
|
ResizeEdge(xdg_toplevel::ResizeEdge::TopLeft),
|
||||||
|
(1, 1).into(),
|
||||||
|
(0, 0).into(),
|
||||||
|
);
|
||||||
|
assert_eq!(size, (700, 550).into());
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn right_and_bottom_edges_follow_pointer_delta() {
|
||||||
|
let size = constrained_size(
|
||||||
|
(800, 600).into(),
|
||||||
|
(100.0, 50.0).into(),
|
||||||
|
ResizeEdge(xdg_toplevel::ResizeEdge::BottomRight),
|
||||||
|
(1, 1).into(),
|
||||||
|
(0, 0).into(),
|
||||||
|
);
|
||||||
|
assert_eq!(size, (900, 650).into());
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn client_constraints_clamp_interactive_size() {
|
||||||
|
let size = constrained_size(
|
||||||
|
(800, 600).into(),
|
||||||
|
(1_000.0, 1_000.0).into(),
|
||||||
|
ResizeEdge(xdg_toplevel::ResizeEdge::BottomRight),
|
||||||
|
(640, 480).into(),
|
||||||
|
(1_024, 768).into(),
|
||||||
|
);
|
||||||
|
assert_eq!(size, (1_024, 768).into());
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -44,7 +44,7 @@ impl CompositorHandler for Ferese {
|
|||||||
window.on_commit();
|
window.on_commit();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
xdg_shell::handle_commit(&mut self.popups, &self.space, surface);
|
xdg_shell::handle_commit(&mut self.popups, &mut self.space, surface);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -2,9 +2,16 @@ use smithay::{
|
|||||||
desktop::{
|
desktop::{
|
||||||
PopupKind, PopupManager, Space, Window, find_popup_root_surface, get_popup_toplevel_coords,
|
PopupKind, PopupManager, Space, Window, find_popup_root_surface, get_popup_toplevel_coords,
|
||||||
},
|
},
|
||||||
|
input::{
|
||||||
|
Seat,
|
||||||
|
pointer::{Focus, GrabStartData},
|
||||||
|
},
|
||||||
reexports::wayland_protocols::xdg::decoration::zv1::server::zxdg_toplevel_decoration_v1::Mode,
|
reexports::wayland_protocols::xdg::decoration::zv1::server::zxdg_toplevel_decoration_v1::Mode,
|
||||||
reexports::wayland_server::protocol::{wl_seat, wl_surface::WlSurface},
|
reexports::wayland_server::{
|
||||||
utils::Serial,
|
Resource,
|
||||||
|
protocol::{wl_seat, wl_surface::WlSurface},
|
||||||
|
},
|
||||||
|
utils::{Rectangle, Serial},
|
||||||
wayland::{
|
wayland::{
|
||||||
compositor::with_states,
|
compositor::with_states,
|
||||||
shell::xdg::decoration::XdgDecorationHandler,
|
shell::xdg::decoration::XdgDecorationHandler,
|
||||||
@@ -15,7 +22,10 @@ use smithay::{
|
|||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
use crate::Ferese;
|
use crate::{
|
||||||
|
Ferese,
|
||||||
|
grabs::{MoveSurfaceGrab, ResizeEdge, ResizeSurfaceGrab, handle_resize_commit},
|
||||||
|
};
|
||||||
|
|
||||||
impl XdgShellHandler for Ferese {
|
impl XdgShellHandler for Ferese {
|
||||||
fn xdg_shell_state(&mut self) -> &mut XdgShellState {
|
fn xdg_shell_state(&mut self) -> &mut XdgShellState {
|
||||||
@@ -48,20 +58,103 @@ impl XdgShellHandler for Ferese {
|
|||||||
surface.send_repositioned(token);
|
surface.send_repositioned(token);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn move_request(&mut self, _: ToplevelSurface, _: wl_seat::WlSeat, _: Serial) {}
|
fn move_request(&mut self, surface: ToplevelSurface, seat: wl_seat::WlSeat, serial: Serial) {
|
||||||
|
let Some(seat) = Seat::from_resource(&seat) else {
|
||||||
|
return;
|
||||||
|
};
|
||||||
|
let Some(start_data) = check_grab(&seat, surface.wl_surface(), serial) else {
|
||||||
|
return;
|
||||||
|
};
|
||||||
|
let Some(pointer) = seat.get_pointer() else {
|
||||||
|
return;
|
||||||
|
};
|
||||||
|
let Some(window) = self
|
||||||
|
.space
|
||||||
|
.elements()
|
||||||
|
.find(|window| {
|
||||||
|
window
|
||||||
|
.toplevel()
|
||||||
|
.is_some_and(|toplevel| toplevel.wl_surface() == surface.wl_surface())
|
||||||
|
})
|
||||||
|
.cloned()
|
||||||
|
else {
|
||||||
|
return;
|
||||||
|
};
|
||||||
|
let Some(initial_location) = self.space.element_location(&window) else {
|
||||||
|
return;
|
||||||
|
};
|
||||||
|
pointer.set_grab(
|
||||||
|
self,
|
||||||
|
MoveSurfaceGrab {
|
||||||
|
start_data,
|
||||||
|
window,
|
||||||
|
initial_location,
|
||||||
|
},
|
||||||
|
serial,
|
||||||
|
Focus::Clear,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
fn resize_request(
|
fn resize_request(
|
||||||
&mut self,
|
&mut self,
|
||||||
_: ToplevelSurface,
|
surface: ToplevelSurface,
|
||||||
_: wl_seat::WlSeat,
|
seat: wl_seat::WlSeat,
|
||||||
_: Serial,
|
serial: Serial,
|
||||||
_: smithay::reexports::wayland_protocols::xdg::shell::server::xdg_toplevel::ResizeEdge,
|
edges: smithay::reexports::wayland_protocols::xdg::shell::server::xdg_toplevel::ResizeEdge,
|
||||||
) {
|
) {
|
||||||
|
let Some(seat) = Seat::from_resource(&seat) else {
|
||||||
|
return;
|
||||||
|
};
|
||||||
|
let Some(start_data) = check_grab(&seat, surface.wl_surface(), serial) else {
|
||||||
|
return;
|
||||||
|
};
|
||||||
|
let Some(pointer) = seat.get_pointer() else {
|
||||||
|
return;
|
||||||
|
};
|
||||||
|
let Some(window) = self
|
||||||
|
.space
|
||||||
|
.elements()
|
||||||
|
.find(|window| {
|
||||||
|
window
|
||||||
|
.toplevel()
|
||||||
|
.is_some_and(|toplevel| toplevel.wl_surface() == surface.wl_surface())
|
||||||
|
})
|
||||||
|
.cloned()
|
||||||
|
else {
|
||||||
|
return;
|
||||||
|
};
|
||||||
|
let Some(location) = self.space.element_location(&window) else {
|
||||||
|
return;
|
||||||
|
};
|
||||||
|
let rect = Rectangle::new(location, window.geometry().size);
|
||||||
|
pointer.set_grab(
|
||||||
|
self,
|
||||||
|
ResizeSurfaceGrab::new(start_data, window, ResizeEdge::from(edges), rect),
|
||||||
|
serial,
|
||||||
|
Focus::Clear,
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn grab(&mut self, _: PopupSurface, _: wl_seat::WlSeat, _: Serial) {}
|
fn grab(&mut self, _: PopupSurface, _: wl_seat::WlSeat, _: Serial) {}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn check_grab(
|
||||||
|
seat: &Seat<Ferese>,
|
||||||
|
surface: &WlSurface,
|
||||||
|
serial: Serial,
|
||||||
|
) -> Option<GrabStartData<Ferese>> {
|
||||||
|
let pointer = seat.get_pointer()?;
|
||||||
|
if !pointer.has_grab(serial) {
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
let start_data = pointer.grab_start_data()?;
|
||||||
|
let (focus, _) = start_data.focus.as_ref()?;
|
||||||
|
focus
|
||||||
|
.id()
|
||||||
|
.same_client_as(&surface.id())
|
||||||
|
.then_some(start_data)
|
||||||
|
}
|
||||||
|
|
||||||
impl XdgDecorationHandler for Ferese {
|
impl XdgDecorationHandler for Ferese {
|
||||||
fn new_decoration(&mut self, toplevel: ToplevelSurface) {
|
fn new_decoration(&mut self, toplevel: ToplevelSurface) {
|
||||||
set_decoration_mode(&toplevel, Mode::ClientSide);
|
set_decoration_mode(&toplevel, Mode::ClientSide);
|
||||||
@@ -87,7 +180,8 @@ fn set_decoration_mode(toplevel: &ToplevelSurface, mode: Mode) {
|
|||||||
toplevel.send_pending_configure();
|
toplevel.send_pending_configure();
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn handle_commit(popups: &mut PopupManager, space: &Space<Window>, surface: &WlSurface) {
|
pub fn handle_commit(popups: &mut PopupManager, space: &mut Space<Window>, surface: &WlSurface) {
|
||||||
|
handle_resize_commit(space, surface);
|
||||||
if let Some(window) = space
|
if let Some(window) = space
|
||||||
.elements()
|
.elements()
|
||||||
.find(|window| {
|
.find(|window| {
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
#![allow(irrefutable_let_patterns)]
|
#![allow(irrefutable_let_patterns)]
|
||||||
|
|
||||||
|
mod grabs;
|
||||||
mod handlers;
|
mod handlers;
|
||||||
mod input;
|
mod input;
|
||||||
mod state;
|
mod state;
|
||||||
|
|||||||
Reference in new issue
Block a user