cosmic-comp/src/shell/layout/tiling/grabs/resize.rs
2026-08-26 20:07:05 +02:00

565 lines
18 KiB
Rust

// SPDX-License-Identifier: GPL-3.0-only
use crate::{
backend::render::cursor::CursorState,
shell::{
focus::target::PointerFocusTarget,
grabs::{GrabStartData, ReleaseMode},
layout::Orientation,
},
utils::prelude::*,
};
use id_tree::{NodeId, Tree};
use smithay::{
backend::input::{ButtonState, InputTime},
input::{
Seat,
pointer::{
AxisFrame, ButtonEvent, CursorIcon, Focus, GestureHoldBeginEvent, GestureHoldEndEvent,
GesturePinchBeginEvent, GesturePinchEndEvent, GesturePinchUpdateEvent,
GestureSwipeBeginEvent, GestureSwipeEndEvent, GestureSwipeUpdateEvent,
GrabStartData as PointerGrabStartData, MotionEvent, PointerGrab, PointerInnerHandle,
PointerTarget, RelativeMotionEvent,
},
touch::{
DownEvent, FrameMarker, GrabStartData as TouchGrabStartData,
MotionEvent as TouchMotionEvent, OrientationEvent, ShapeEvent, TouchGrab,
TouchInnerHandle, TouchTarget, UpEvent,
},
},
output::WeakOutput,
utils::{IsAlive, Logical, Point},
};
use super::super::{Data, TilingLayout};
#[derive(Debug, Clone, PartialEq)]
pub struct ResizeForkTarget {
pub node: NodeId,
pub output: WeakOutput,
pub left_up_idx: usize,
pub orientation: Orientation,
}
impl IsAlive for ResizeForkTarget {
fn alive(&self) -> bool {
self.output.upgrade().is_some()
}
}
impl PointerTarget<State> for ResizeForkTarget {
fn enter(&self, seat: &Seat<State>, _data: &mut State, _event: &MotionEvent) {
let user_data = seat.user_data();
let cursor_state = user_data.get::<CursorState>().unwrap();
cursor_state
.lock()
.unwrap()
.set_shape(match self.orientation {
Orientation::Horizontal => CursorIcon::RowResize,
Orientation::Vertical => CursorIcon::ColResize,
});
}
fn leave(
&self,
seat: &Seat<State>,
_data: &mut State,
_serial: smithay::utils::Serial,
_time: InputTime,
) {
let user_data = seat.user_data();
let cursor_state = user_data.get::<CursorState>().unwrap();
cursor_state.lock().unwrap().unset_shape();
}
fn button(&self, seat: &Seat<State>, data: &mut State, event: &ButtonEvent) {
if event.button == 0x110 && event.state == ButtonState::Pressed {
let seat = seat.clone();
let node = self.node.clone();
let output = self.output.clone();
let left_up_idx = self.left_up_idx;
let orientation = self.orientation;
let serial = event.serial;
let button = event.button;
data.common.event_loop_handle.insert_idle(move |state| {
let pointer = seat.get_pointer().unwrap();
let location = pointer.current_location();
pointer.set_grab(
state,
ResizeForkGrab::new(
GrabStartData::Pointer(PointerGrabStartData {
focus: None,
button,
location,
}),
location.as_global(),
node,
left_up_idx,
orientation,
output,
ReleaseMode::NoMouseButtons,
),
serial,
Focus::Clear,
)
});
}
}
fn motion(&self, _seat: &Seat<State>, _data: &mut State, _event: &MotionEvent) {}
fn relative_motion(
&self,
_seat: &Seat<State>,
_data: &mut State,
_event: &RelativeMotionEvent,
) {
}
fn axis(&self, _seat: &Seat<State>, _data: &mut State, _frame: AxisFrame) {}
fn frame(&self, _seat: &Seat<State>, _data: &mut State) {}
fn gesture_swipe_begin(&self, _: &Seat<State>, _: &mut State, _: &GestureSwipeBeginEvent) {}
fn gesture_swipe_update(&self, _: &Seat<State>, _: &mut State, _: &GestureSwipeUpdateEvent) {}
fn gesture_swipe_end(&self, _: &Seat<State>, _: &mut State, _: &GestureSwipeEndEvent) {}
fn gesture_pinch_begin(&self, _: &Seat<State>, _: &mut State, _: &GesturePinchBeginEvent) {}
fn gesture_pinch_update(&self, _: &Seat<State>, _: &mut State, _: &GesturePinchUpdateEvent) {}
fn gesture_pinch_end(&self, _: &Seat<State>, _: &mut State, _: &GesturePinchEndEvent) {}
fn gesture_hold_begin(&self, _: &Seat<State>, _: &mut State, _: &GestureHoldBeginEvent) {}
fn gesture_hold_end(&self, _: &Seat<State>, _: &mut State, _: &GestureHoldEndEvent) {}
}
impl TouchTarget<State> for ResizeForkTarget {
fn down(&self, seat: &Seat<State>, data: &mut State, event: &DownEvent) {
let seat = seat.clone();
let node = self.node.clone();
let output = self.output.clone();
let left_up_idx = self.left_up_idx;
let orientation = self.orientation;
let serial = event.serial;
let slot = event.slot;
let location = event.location;
data.common.event_loop_handle.insert_idle(move |state| {
let touch = seat.get_touch().unwrap();
touch.set_grab(
state,
ResizeForkGrab::new(
GrabStartData::Touch(TouchGrabStartData {
focus: None,
slot,
location,
}),
location.as_global(),
node,
left_up_idx,
orientation,
output,
ReleaseMode::NoMouseButtons,
),
serial,
)
});
}
fn up(&self, _seat: &Seat<State>, _data: &mut State, _event: &UpEvent) {}
fn motion(&self, _seat: &Seat<State>, _data: &mut State, _event: &TouchMotionEvent) {}
fn frame(&self, _seat: &Seat<State>, _data: &mut State, _frame: FrameMarker) {}
fn cancel(&self, _seat: &Seat<State>, _data: &mut State, _frame: FrameMarker) {}
fn shape(&self, _seat: &Seat<State>, _data: &mut State, _event: &ShapeEvent) {}
fn orientation(&self, _seat: &Seat<State>, _data: &mut State, _event: &OrientationEvent) {}
fn last_frame(&self, _seat: &Seat<State>, _data: &mut State) -> Option<FrameMarker> {
None
}
}
pub struct ResizeForkGrab {
start_data: GrabStartData,
last_loc: Point<f64, Global>,
old_tree: Option<Tree<Data>>,
accumulated_delta: f64,
node: NodeId,
output: WeakOutput,
left_up_idx: usize,
orientation: Orientation,
release: ReleaseMode,
}
impl ResizeForkGrab {
pub fn new(
start_data: GrabStartData,
pointer_loc: Point<f64, Global>,
node: NodeId,
idx: usize,
orientation: Orientation,
output: WeakOutput,
release: ReleaseMode,
) -> ResizeForkGrab {
ResizeForkGrab {
start_data,
last_loc: pointer_loc,
old_tree: None,
accumulated_delta: 0.0,
node,
output,
left_up_idx: idx,
orientation,
release,
}
}
}
impl ResizeForkGrab {
// Returns `true` if grab should be unset
fn update_location(
&mut self,
data: &mut State,
location: Point<f64, Logical>,
force: bool,
) -> bool {
let delta = location - self.last_loc.as_logical();
self.last_loc = location.as_global();
if let Some(output) = self.output.upgrade() {
let mut shell = data.common.shell.write();
let Some(workspace) = shell.active_space_mut(&output) else {
return false;
};
let tiling_layer = &mut workspace.tiling_layer;
let gaps = tiling_layer.gaps();
let mut tree = tiling_layer.queue.trees.back().unwrap().0.copy_clone();
match &mut self.old_tree {
Some(old_tree) => {
// it would be so nice to just `zip` here, but `zip` just returns `None` once either returns `None`.
let mut iter_a = old_tree
.root_node_id()
.into_iter()
.flat_map(|root_id| old_tree.traverse_pre_order_ids(root_id).unwrap());
let mut iter_b = tree
.root_node_id()
.into_iter()
.flat_map(|root_id| tree.traverse_pre_order_ids(root_id).unwrap());
// so lets do it manually
let mut equal = true;
let mut a = iter_a.next();
let mut b = iter_b.next();
while a.is_some() || b.is_some() {
equal = a == b;
if !equal {
break;
}
a = iter_a.next();
b = iter_b.next();
}
if !equal {
*old_tree = tree.copy_clone();
self.accumulated_delta = 0.0;
} else {
tree = old_tree.copy_clone();
}
}
x @ None => {
*x = Some(tree.copy_clone());
}
};
if tree.get(&self.node).is_ok() {
let delta = match self.orientation {
Orientation::Vertical => delta.x,
Orientation::Horizontal => delta.y,
}
.round();
self.accumulated_delta += delta;
// check that we are still alive
let mut iter = tree
.children_ids(&self.node)
.unwrap()
.skip(self.left_up_idx);
let first_elem = iter.next();
let second_elem = iter.next();
if first_elem.is_none() || second_elem.is_none() {
return true;
};
if self.accumulated_delta.round() as i32 == 0 {
return false;
}
match tree.get_mut(&self.node).unwrap().data_mut() {
Data::Group {
sizes, orientation, ..
} => {
if sizes[self.left_up_idx] + sizes[self.left_up_idx + 1]
< match orientation {
Orientation::Vertical => 720,
Orientation::Horizontal => 480,
}
{
return false;
};
let old_size = sizes[self.left_up_idx];
sizes[self.left_up_idx] = (old_size
+ self.accumulated_delta.round() as i32)
.max(if self.orientation == Orientation::Vertical {
360
} else {
240
});
let diff = old_size - sizes[self.left_up_idx];
let next_size = sizes[self.left_up_idx + 1] + diff;
sizes[self.left_up_idx + 1] =
next_size.max(if self.orientation == Orientation::Vertical {
360
} else {
240
});
let next_diff = next_size - sizes[self.left_up_idx + 1];
sizes[self.left_up_idx] += next_diff;
}
_ => unreachable!(),
}
let should_configure = force
|| tree
.traverse_pre_order(&self.node)
.unwrap()
.all(|node| match node.data() {
Data::Mapped { mapped, .. } => mapped.latest_size_committed(),
_ => true,
});
if should_configure {
let blocker = TilingLayout::update_positions(&output, &mut tree, gaps);
tiling_layer.queue.push_tree(tree, None, blocker);
}
} else {
return true;
}
}
false
}
pub fn is_touch_grab(&self) -> bool {
match self.start_data {
GrabStartData::Touch(_) => true,
GrabStartData::Pointer(_) => false,
}
}
}
impl PointerGrab<State> for ResizeForkGrab {
fn motion(
&mut self,
data: &mut State,
handle: &mut PointerInnerHandle<'_, State>,
_focus: Option<(PointerFocusTarget, Point<f64, Logical>)>,
event: &MotionEvent,
) {
// While the grab is active, no client has pointer focus
handle.motion(data, None, event);
if self.update_location(data, event.location, false) {
handle.unset_grab(self, data, event.serial, event.time, true);
}
}
fn relative_motion(
&mut self,
state: &mut State,
handle: &mut PointerInnerHandle<'_, State>,
_focus: Option<(PointerFocusTarget, Point<f64, Logical>)>,
event: &RelativeMotionEvent,
) {
// While the grab is active, no client has pointer focus
handle.relative_motion(state, None, event);
}
fn button(
&mut self,
data: &mut State,
handle: &mut PointerInnerHandle<'_, State>,
event: &ButtonEvent,
) {
handle.button(data, event);
match self.release {
ReleaseMode::NoMouseButtons => {
if handle.current_pressed().is_empty() {
handle.unset_grab(self, data, event.serial, event.time, true);
}
}
ReleaseMode::Click => {
if event.state == ButtonState::Pressed {
handle.unset_grab(self, data, event.serial, event.time, true);
}
}
}
}
fn axis(
&mut self,
data: &mut State,
handle: &mut PointerInnerHandle<'_, State>,
details: AxisFrame,
) {
handle.axis(data, details)
}
fn frame(&mut self, data: &mut State, handle: &mut PointerInnerHandle<'_, State>) {
handle.frame(data)
}
fn gesture_swipe_begin(
&mut self,
data: &mut State,
handle: &mut PointerInnerHandle<'_, State>,
event: &GestureSwipeBeginEvent,
) {
handle.gesture_swipe_begin(data, event)
}
fn gesture_swipe_update(
&mut self,
data: &mut State,
handle: &mut PointerInnerHandle<'_, State>,
event: &GestureSwipeUpdateEvent,
) {
handle.gesture_swipe_update(data, event)
}
fn gesture_swipe_end(
&mut self,
data: &mut State,
handle: &mut PointerInnerHandle<'_, State>,
event: &GestureSwipeEndEvent,
) {
handle.gesture_swipe_end(data, event)
}
fn gesture_pinch_begin(
&mut self,
data: &mut State,
handle: &mut PointerInnerHandle<'_, State>,
event: &GesturePinchBeginEvent,
) {
handle.gesture_pinch_begin(data, event)
}
fn gesture_pinch_update(
&mut self,
data: &mut State,
handle: &mut PointerInnerHandle<'_, State>,
event: &GesturePinchUpdateEvent,
) {
handle.gesture_pinch_update(data, event)
}
fn gesture_pinch_end(
&mut self,
data: &mut State,
handle: &mut PointerInnerHandle<'_, State>,
event: &GesturePinchEndEvent,
) {
handle.gesture_pinch_end(data, event)
}
fn gesture_hold_begin(
&mut self,
data: &mut State,
handle: &mut PointerInnerHandle<'_, State>,
event: &GestureHoldBeginEvent,
) {
handle.gesture_hold_begin(data, event)
}
fn gesture_hold_end(
&mut self,
data: &mut State,
handle: &mut PointerInnerHandle<'_, State>,
event: &GestureHoldEndEvent,
) {
handle.gesture_hold_end(data, event)
}
fn start_data(&self) -> &PointerGrabStartData<State> {
match &self.start_data {
GrabStartData::Pointer(start_data) => start_data,
_ => unreachable!(),
}
}
fn unset(&mut self, data: &mut State) {
self.update_location(data, self.last_loc.as_logical(), true);
}
}
impl TouchGrab<State> for ResizeForkGrab {
fn down(
&mut self,
data: &mut State,
handle: &mut TouchInnerHandle<'_, State>,
_focus: Option<(PointerFocusTarget, Point<f64, Logical>)>,
event: &DownEvent,
) {
handle.down(data, None, event)
}
fn up(&mut self, data: &mut State, handle: &mut TouchInnerHandle<'_, State>, event: &UpEvent) {
if event.slot == <Self as TouchGrab<State>>::start_data(self).slot {
handle.unset_grab(self, data);
}
handle.up(data, event);
}
fn motion(
&mut self,
data: &mut State,
handle: &mut TouchInnerHandle<'_, State>,
_focus: Option<(PointerFocusTarget, Point<f64, Logical>)>,
event: &TouchMotionEvent,
) {
if event.slot == <Self as TouchGrab<State>>::start_data(self).slot
&& self.update_location(data, event.location, false)
{
handle.unset_grab(self, data);
}
handle.motion(data, None, event);
}
fn frame(&mut self, data: &mut State, handle: &mut TouchInnerHandle<'_, State>) {
handle.frame(data)
}
fn cancel(&mut self, data: &mut State, handle: &mut TouchInnerHandle<'_, State>) {
handle.unset_grab(self, data);
}
fn shape(
&mut self,
data: &mut State,
handle: &mut TouchInnerHandle<'_, State>,
event: &ShapeEvent,
) {
handle.shape(data, event)
}
fn start_data(&self) -> &TouchGrabStartData<State> {
match &self.start_data {
GrabStartData::Touch(start_data) => start_data,
_ => unreachable!(),
}
}
fn orientation(
&mut self,
data: &mut State,
handle: &mut TouchInnerHandle<'_, State>,
event: &OrientationEvent,
) {
handle.orientation(data, event)
}
fn unset(&mut self, data: &mut State) {
self.update_location(data, self.last_loc.as_logical(), true);
}
}