🔒 Repository is read-only – file editing is disabled.
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360
use crate::Smallvil;
use smithay::{
desktop::{Space, Window},
input::pointer::{
AxisFrame, ButtonEvent, GestureHoldBeginEvent, GestureHoldEndEvent, GesturePinchBeginEvent,
GesturePinchEndEvent, GesturePinchUpdateEvent, GestureSwipeBeginEvent, GestureSwipeEndEvent,
GestureSwipeUpdateEvent, GrabStartData as PointerGrabStartData, 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, shell::xdg::SurfaceCachedState},
};
use std::cell::RefCell;
bitflags::bitflags! {
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
pub struct ResizeEdge: u32 {
const TOP = 0b0001;
const BOTTOM = 0b0010;
const LEFT = 0b0100;
const RIGHT = 0b1000;
const TOP_LEFT = Self::TOP.bits() | Self::LEFT.bits();
const BOTTOM_LEFT = Self::BOTTOM.bits() | Self::LEFT.bits();
const TOP_RIGHT = Self::TOP.bits() | Self::RIGHT.bits();
const BOTTOM_RIGHT = Self::BOTTOM.bits() | Self::RIGHT.bits();
}
}
impl From<xdg_toplevel::ResizeEdge> for ResizeEdge {
#[inline]
fn from(x: xdg_toplevel::ResizeEdge) -> Self {
Self::from_bits(x as u32).unwrap()
}
}
pub struct ResizeSurfaceGrab {
start_data: PointerGrabStartData<Smallvil>,
window: Window,
edges: ResizeEdge,
initial_rect: Rectangle<i32, Logical>,
last_window_size: Size<i32, Logical>,
}
impl ResizeSurfaceGrab {
pub fn start(
start_data: PointerGrabStartData<Smallvil>,
window: Window,
edges: ResizeEdge,
initial_window_rect: Rectangle<i32, Logical>,
) -> Self {
let initial_rect = initial_window_rect;
ResizeSurfaceState::with(window.toplevel().unwrap().wl_surface(), |state| {
*state = ResizeSurfaceState::Resizing { edges, initial_rect };
});
Self {
start_data,
window,
edges,
initial_rect,
last_window_size: initial_rect.size,
}
}
}
impl PointerGrab<Smallvil> for ResizeSurfaceGrab {
fn motion(
&mut self,
data: &mut Smallvil,
handle: &mut PointerInnerHandle<'_, Smallvil>,
_focus: Option<(WlSurface, Point<f64, Logical>)>,
event: &MotionEvent,
) {
// While the grab is active, no client has pointer focus
handle.motion(data, None, event);
let mut delta = event.location - self.start_data.location;
let mut new_window_width = self.initial_rect.size.w;
let mut new_window_height = self.initial_rect.size.h;
if self.edges.intersects(ResizeEdge::LEFT | ResizeEdge::RIGHT) {
if self.edges.intersects(ResizeEdge::LEFT) {
delta.x = -delta.x;
}
new_window_width = (self.initial_rect.size.w as f64 + delta.x) as i32;
}
if self.edges.intersects(ResizeEdge::TOP | ResizeEdge::BOTTOM) {
if self.edges.intersects(ResizeEdge::TOP) {
delta.y = -delta.y;
}
new_window_height = (self.initial_rect.size.h as f64 + delta.y) as i32;
}
let (min_size, max_size) =
compositor::with_states(self.window.toplevel().unwrap().wl_surface(), |states| {
let mut guard = states.cached_state.get::<SurfaceCachedState>();
let data = guard.current();
(data.min_size, data.max_size)
});
let min_width = min_size.w.max(1);
let min_height = min_size.h.max(1);
let max_width = if max_size.w == 0 { i32::MAX } else { max_size.w };
let max_height = if max_size.h == 0 { i32::MAX } else { max_size.h };
self.last_window_size = Size::from((
new_window_width.max(min_width).min(max_width),
new_window_height.max(min_height).min(max_height),
));
let xdg = self.window.toplevel().unwrap();
xdg.with_pending_state(|state| {
state.states.set(xdg_toplevel::State::Resizing);
state.size = Some(self.last_window_size);
});
xdg.send_pending_configure();
}
fn relative_motion(
&mut self,
data: &mut Smallvil,
handle: &mut PointerInnerHandle<'_, Smallvil>,
focus: Option<(WlSurface, Point<f64, Logical>)>,
event: &RelativeMotionEvent,
) {
handle.relative_motion(data, focus, event);
}
fn button(
&mut self,
data: &mut Smallvil,
handle: &mut PointerInnerHandle<'_, Smallvil>,
event: &ButtonEvent,
) {
handle.button(data, event);
// The button is a button code as defined in the
// Linux kernel's linux/input-event-codes.h header file, e.g. BTN_LEFT.
const BTN_LEFT: u32 = 0x110;
if !handle.current_pressed().contains(&BTN_LEFT) {
// No more buttons are pressed, release the grab.
handle.unset_grab(self, data, event.serial, event.time, true);
let xdg = self.window.toplevel().unwrap();
xdg.with_pending_state(|state| {
state.states.unset(xdg_toplevel::State::Resizing);
state.size = Some(self.last_window_size);
});
xdg.send_pending_configure();
ResizeSurfaceState::with(xdg.wl_surface(), |state| {
*state = ResizeSurfaceState::WaitingForLastCommit {
edges: self.edges,
initial_rect: self.initial_rect,
};
});
}
}
fn axis(
&mut self,
data: &mut Smallvil,
handle: &mut PointerInnerHandle<'_, Smallvil>,
details: AxisFrame,
) {
handle.axis(data, details)
}
fn frame(&mut self, data: &mut Smallvil, handle: &mut PointerInnerHandle<'_, Smallvil>) {
handle.frame(data);
}
fn gesture_swipe_begin(
&mut self,
data: &mut Smallvil,
handle: &mut PointerInnerHandle<'_, Smallvil>,
event: &GestureSwipeBeginEvent,
) {
handle.gesture_swipe_begin(data, event)
}
fn gesture_swipe_update(
&mut self,
data: &mut Smallvil,
handle: &mut PointerInnerHandle<'_, Smallvil>,
event: &GestureSwipeUpdateEvent,
) {
handle.gesture_swipe_update(data, event)
}
fn gesture_swipe_end(
&mut self,
data: &mut Smallvil,
handle: &mut PointerInnerHandle<'_, Smallvil>,
event: &GestureSwipeEndEvent,
) {
handle.gesture_swipe_end(data, event)
}
fn gesture_pinch_begin(
&mut self,
data: &mut Smallvil,
handle: &mut PointerInnerHandle<'_, Smallvil>,
event: &GesturePinchBeginEvent,
) {
handle.gesture_pinch_begin(data, event)
}
fn gesture_pinch_update(
&mut self,
data: &mut Smallvil,
handle: &mut PointerInnerHandle<'_, Smallvil>,
event: &GesturePinchUpdateEvent,
) {
handle.gesture_pinch_update(data, event)
}
fn gesture_pinch_end(
&mut self,
data: &mut Smallvil,
handle: &mut PointerInnerHandle<'_, Smallvil>,
event: &GesturePinchEndEvent,
) {
handle.gesture_pinch_end(data, event)
}
fn gesture_hold_begin(
&mut self,
data: &mut Smallvil,
handle: &mut PointerInnerHandle<'_, Smallvil>,
event: &GestureHoldBeginEvent,
) {
handle.gesture_hold_begin(data, event)
}
fn gesture_hold_end(
&mut self,
data: &mut Smallvil,
handle: &mut PointerInnerHandle<'_, Smallvil>,
event: &GestureHoldEndEvent,
) {
handle.gesture_hold_end(data, event)
}
fn start_data(&self) -> &PointerGrabStartData<Smallvil> {
&self.start_data
}
fn unset(&mut self, _data: &mut Smallvil) {}
}
/// State of the resize operation.
///
/// It is stored inside of WlSurface,
/// and can be accessed using [`ResizeSurfaceState::with`]
#[derive(Debug, Clone, Copy, Eq, PartialEq, Default)]
enum ResizeSurfaceState {
#[default]
Idle,
Resizing {
edges: ResizeEdge,
/// The initial window size and location.
initial_rect: Rectangle<i32, Logical>,
},
/// Resize is done, we are now waiting for last commit, to do the final move
WaitingForLastCommit {
edges: ResizeEdge,
/// The initial window size and location.
initial_rect: Rectangle<i32, Logical>,
},
}
impl ResizeSurfaceState {
fn with<F, T>(surface: &WlSurface, cb: F) -> T
where
F: FnOnce(&mut Self) -> T,
{
compositor::with_states(surface, |states| {
states.data_map.insert_if_missing(RefCell::<Self>::default);
let state = states.data_map.get::<RefCell<Self>>().unwrap();
cb(&mut state.borrow_mut())
})
}
fn commit(&mut self) -> Option<(ResizeEdge, Rectangle<i32, Logical>)> {
match *self {
Self::Resizing { edges, initial_rect } => Some((edges, initial_rect)),
Self::WaitingForLastCommit { edges, initial_rect } => {
// The resize is done, let's go back to idle
*self = Self::Idle;
Some((edges, initial_rect))
}
Self::Idle => None,
}
}
}
/// Should be called on `WlSurface::commit`
pub fn handle_commit(space: &mut Space<Window>, surface: &WlSurface) -> Option<()> {
let window = space
.elements()
.find(|w| w.toplevel().unwrap().wl_surface() == surface)
.cloned()?;
let mut window_loc = space.element_location(&window)?;
let geometry = window.geometry();
let new_loc: Point<Option<i32>, Logical> = ResizeSurfaceState::with(surface, |state| {
state
.commit()
.and_then(|(edges, initial_rect)| {
// If the window is being resized by top or left, its location must be adjusted
// accordingly.
edges.intersects(ResizeEdge::TOP_LEFT).then(|| {
let new_x = edges
.intersects(ResizeEdge::LEFT)
.then_some(initial_rect.loc.x + (initial_rect.size.w - geometry.size.w));
let new_y = edges
.intersects(ResizeEdge::TOP)
.then_some(initial_rect.loc.y + (initial_rect.size.h - geometry.size.h));
(new_x, new_y).into()
})
})
.unwrap_or_default()
});
if let Some(new_x) = new_loc.x {
window_loc.x = new_x;
}
if let Some(new_y) = new_loc.y {
window_loc.y = new_y;
}
if new_loc.x.is_some() || new_loc.y.is_some() {
// If TOP or LEFT side of the window got resized, we have to move it
space.map_element(window, window_loc, false);
}
Some(())
}