tiling: Properly handle resizing using a ptr device

This commit is contained in:
Victoria Brekenfeld 2023-07-11 16:33:23 +02:00
parent 92019b4286
commit b818a68a91
8 changed files with 371 additions and 252 deletions

View file

@ -1,7 +1,7 @@
use std::sync::Weak;
use crate::{
shell::{element::CosmicMapped, CosmicSurface},
shell::{element::CosmicMapped, layout::tiling::ResizeForkTarget, CosmicSurface},
utils::prelude::*,
wayland::handlers::xdg_shell::popup::get_popup_toplevel,
};
@ -28,6 +28,7 @@ pub enum PointerFocusTarget {
LayerSurface(LayerSurface),
Popup(PopupKind),
OverrideRedirect(X11Surface),
ResizeFork(ResizeForkTarget),
}
#[derive(Debug, Clone, PartialEq)]
@ -39,6 +40,7 @@ pub enum KeyboardFocusTarget {
Popup(PopupKind),
}
// TODO: This should be TryFrom, but PopupGrab needs to be able to convert. Fix this in smithay
impl From<KeyboardFocusTarget> for PointerFocusTarget {
fn from(target: KeyboardFocusTarget) -> Self {
match target {
@ -51,6 +53,19 @@ impl From<KeyboardFocusTarget> for PointerFocusTarget {
}
}
impl TryFrom<PointerFocusTarget> for KeyboardFocusTarget {
type Error = ();
fn try_from(target: PointerFocusTarget) -> Result<Self, Self::Error> {
match target {
PointerFocusTarget::Element(mapped) => Ok(KeyboardFocusTarget::Element(mapped)),
PointerFocusTarget::Fullscreen(surf) => Ok(KeyboardFocusTarget::Fullscreen(surf)),
PointerFocusTarget::LayerSurface(layer) => Ok(KeyboardFocusTarget::LayerSurface(layer)),
PointerFocusTarget::Popup(popup) => Ok(KeyboardFocusTarget::Popup(popup)),
_ => Err(()),
}
}
}
impl KeyboardFocusTarget {
pub fn toplevel(&self) -> Option<WlSurface> {
match self {
@ -85,6 +100,7 @@ impl IsAlive for PointerFocusTarget {
PointerFocusTarget::LayerSurface(l) => l.alive(),
PointerFocusTarget::Popup(p) => p.alive(),
PointerFocusTarget::OverrideRedirect(s) => s.alive(),
PointerFocusTarget::ResizeFork(f) => f.alive(),
}
}
}
@ -109,6 +125,7 @@ impl PointerTarget<State> for PointerFocusTarget {
PointerFocusTarget::LayerSurface(l) => PointerTarget::enter(l, seat, data, event),
PointerFocusTarget::Popup(p) => PointerTarget::enter(p.wl_surface(), seat, data, event),
PointerFocusTarget::OverrideRedirect(s) => PointerTarget::enter(s, seat, data, event),
PointerFocusTarget::ResizeFork(f) => PointerTarget::enter(f, seat, data, event),
}
}
fn motion(&self, seat: &Seat<State>, data: &mut State, event: &MotionEvent) {
@ -120,6 +137,7 @@ impl PointerTarget<State> for PointerFocusTarget {
PointerTarget::motion(p.wl_surface(), seat, data, event)
}
PointerFocusTarget::OverrideRedirect(s) => PointerTarget::motion(s, seat, data, event),
PointerFocusTarget::ResizeFork(f) => PointerTarget::motion(f, seat, data, event),
}
}
fn relative_motion(&self, seat: &Seat<State>, data: &mut State, event: &RelativeMotionEvent) {
@ -137,6 +155,9 @@ impl PointerTarget<State> for PointerFocusTarget {
PointerFocusTarget::OverrideRedirect(s) => {
PointerTarget::relative_motion(s, seat, data, event)
}
PointerFocusTarget::ResizeFork(f) => {
PointerTarget::relative_motion(f, seat, data, event)
}
}
}
fn button(&self, seat: &Seat<State>, data: &mut State, event: &ButtonEvent) {
@ -148,6 +169,7 @@ impl PointerTarget<State> for PointerFocusTarget {
PointerTarget::button(p.wl_surface(), seat, data, event)
}
PointerFocusTarget::OverrideRedirect(s) => PointerTarget::button(s, seat, data, event),
PointerFocusTarget::ResizeFork(f) => PointerTarget::button(f, seat, data, event),
}
}
fn axis(&self, seat: &Seat<State>, data: &mut State, frame: AxisFrame) {
@ -157,6 +179,7 @@ impl PointerTarget<State> for PointerFocusTarget {
PointerFocusTarget::LayerSurface(l) => PointerTarget::axis(l, seat, data, frame),
PointerFocusTarget::Popup(p) => PointerTarget::axis(p.wl_surface(), seat, data, frame),
PointerFocusTarget::OverrideRedirect(s) => PointerTarget::axis(s, seat, data, frame),
PointerFocusTarget::ResizeFork(f) => PointerTarget::axis(f, seat, data, frame),
}
}
fn leave(&self, seat: &Seat<State>, data: &mut State, serial: Serial, time: u32) {
@ -172,6 +195,7 @@ impl PointerTarget<State> for PointerFocusTarget {
PointerFocusTarget::OverrideRedirect(s) => {
PointerTarget::leave(s, seat, data, serial, time)
}
PointerFocusTarget::ResizeFork(f) => PointerTarget::leave(f, seat, data, serial, time),
}
}
}
@ -290,6 +314,9 @@ impl WaylandFocus for PointerFocusTarget {
PointerFocusTarget::OverrideRedirect(s) => {
return s.wl_surface();
}
PointerFocusTarget::ResizeFork(_) => {
return None;
}
})
}
fn same_client_as(&self, object_id: &ObjectId) -> bool {
@ -299,6 +326,7 @@ impl WaylandFocus for PointerFocusTarget {
PointerFocusTarget::LayerSurface(l) => l.wl_surface().id().same_client_as(object_id),
PointerFocusTarget::Popup(p) => p.wl_surface().id().same_client_as(object_id),
PointerFocusTarget::OverrideRedirect(s) => WaylandFocus::same_client_as(s, object_id),
PointerFocusTarget::ResizeFork(_) => false,
}
}
}
@ -333,6 +361,12 @@ impl From<X11Surface> for PointerFocusTarget {
}
}
impl From<ResizeForkTarget> for PointerFocusTarget {
fn from(f: ResizeForkTarget) -> Self {
PointerFocusTarget::ResizeFork(f)
}
}
impl From<CosmicMapped> for KeyboardFocusTarget {
fn from(w: CosmicMapped) -> Self {
KeyboardFocusTarget::Element(w)

View file

@ -1,52 +1,113 @@
// SPDX-License-Identifier: GPL-3.0-only
use crate::{
backend::render::cursor::{CursorShape, CursorState},
shell::{focus::target::PointerFocusTarget, layout::Orientation},
utils::prelude::*,
};
use id_tree::NodeId;
use smithay::{
input::pointer::{
AxisFrame, ButtonEvent, GrabStartData as PointerGrabStartData, MotionEvent, PointerGrab,
PointerInnerHandle, RelativeMotionEvent,
backend::input::ButtonState,
input::{
pointer::{
AxisFrame, ButtonEvent, Focus, GrabStartData as PointerGrabStartData, MotionEvent,
PointerGrab, PointerInnerHandle, PointerTarget, RelativeMotionEvent,
},
Seat,
},
output::{Output, WeakOutput},
utils::{Logical, Point},
output::WeakOutput,
utils::{IsAlive, Logical, Point},
};
use super::Data;
use 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.set_shape(match self.orientation {
Orientation::Horizontal => CursorShape::RowResize,
Orientation::Vertical => CursorShape::ColResize,
});
}
fn leave(
&self,
seat: &Seat<State>,
_data: &mut State,
_serial: smithay::utils::Serial,
_time: u32,
) {
let user_data = seat.user_data();
let cursor_state = user_data.get::<CursorState>().unwrap();
cursor_state.set_shape(CursorShape::Default)
}
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 |data| {
let pointer = seat.get_pointer().unwrap();
let location = pointer.current_location();
pointer.set_grab(
&mut data.state,
ResizeForkGrab {
start_data: PointerGrabStartData {
focus: None,
button,
location,
},
last_loc: location,
node,
output,
left_up_idx,
orientation,
},
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) {}
}
pub struct ResizeForkGrab {
start_data: PointerGrabStartData<State>,
idx: usize,
initial_size_upleft: i32,
initial_size_downright: i32,
last_loc: Point<f64, Logical>,
node: NodeId,
output: WeakOutput,
}
impl ResizeForkGrab {
pub fn new(
start_data: PointerGrabStartData<State>,
node: NodeId,
output: &Output,
data: &Data,
idx: usize,
) -> ResizeForkGrab {
let sizes = match data {
Data::Group { ref sizes, .. } => sizes,
_ => panic!("Resizing without a group?!?"),
};
ResizeForkGrab {
start_data,
idx,
initial_size_upleft: sizes.iter().take(idx + 1).sum(),
initial_size_downright: sizes.iter().skip(idx + 1).sum(),
node,
output: output.downgrade(),
}
}
left_up_idx: usize,
orientation: Orientation,
}
impl PointerGrab<State> for ResizeForkGrab {
@ -60,131 +121,70 @@ impl PointerGrab<State> for ResizeForkGrab {
// While the grab is active, no client has pointer focus
handle.motion(data, None, event);
let delta = event.location - self.start_data.location;
let delta = event.location - self.last_loc;
if let Some(output) = self.output.upgrade() {
let tiling_layer = &mut data.common.shell.active_space_mut(&output).tiling_layer;
if let Some(queue) = tiling_layer.queues.get_mut(&output) {
let tree = &mut queue.trees.back_mut().unwrap().0;
if tree.get(&self.node).is_ok() {
let orientation = tree.get(&self.node).unwrap().data().orientation();
let delta = match orientation {
let delta = match self.orientation {
Orientation::Vertical => delta.x,
Orientation::Horizontal => delta.y,
}
.round() as i32;
let upleft_node_id =
match tree.children_ids(&self.node).unwrap().skip(self.idx).next() {
Some(elem) => elem,
None => {
return;
}
};
let downright_node_id = match tree
// check that we are still alive
let mut iter = tree
.children_ids(&self.node)
.unwrap()
.skip(self.idx + 1)
.next()
{
Some(elem) => elem,
None => {
return;
}
.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 handle.unset_grab(data, event.serial, event.time);
};
let next_mapped = |mut node| loop {
if let Some(node_id) = node {
match tree.get(&node_id).unwrap().data() {
Data::Group { orientation: o, .. } if o == &orientation => {
node = tree.children_ids(&node_id).unwrap().last().cloned();
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,
}
_ => {
break node_id;
}
}
} else {
unreachable!()
}
};
let upleft_mapped_id = next_mapped(Some(upleft_node_id.clone()));
let downright_mapped_id = next_mapped(Some(downright_node_id.clone()));
let new_upleft_size = self.initial_size_upleft + delta;
let new_downright_size = self.initial_size_downright - delta;
let new_upleft_mapped_size = match orientation {
Orientation::Horizontal => {
tree.get(&upleft_mapped_id)
.unwrap()
.data()
.geometry()
.size
.h
}
Orientation::Vertical => {
tree.get(&upleft_mapped_id)
.unwrap()
.data()
.geometry()
.size
.w
}
} + delta;
let new_downright_mapped_size = match orientation {
Orientation::Horizontal => {
tree.get(&downright_mapped_id)
.unwrap()
.data()
.geometry()
.size
.h
}
Orientation::Vertical => {
tree.get(&downright_mapped_id)
.unwrap()
.data()
.geometry()
.size
.w
}
} - delta;
if new_upleft_mapped_size > 100 && new_downright_mapped_size > 100 {
// lets update
{
let node = tree.get_mut(&self.node).unwrap();
let data = node.data_mut();
match data {
Data::Group { sizes, .. } => {
sizes[self.idx] = new_upleft_size;
sizes[self.idx + 1] = new_downright_size;
}
_ => unreachable!(),
{
return;
};
let old_size = sizes[self.left_up_idx];
sizes[self.left_up_idx] = (old_size + delta).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;
}
for (mapped_id, mapped_size) in &[
(upleft_mapped_id, new_upleft_mapped_size),
(downright_mapped_id, new_downright_mapped_size),
] {
let parent = tree.get(mapped_id).unwrap().parent().cloned().unwrap();
if parent != self.node {
let idx = tree
.children_ids(&parent)
.unwrap()
.position(|id| id == mapped_id)
.unwrap();
let node = tree.get_mut(&parent).unwrap();
let data = node.data_mut();
match data {
Data::Group { sizes, .. } => {
sizes[idx] = *mapped_size;
}
_ => unreachable!(),
};
}
}
return tiling_layer.refresh();
_ => unreachable!(),
}
self.last_loc = event.location;
let blocker = TilingLayout::update_positions(&output, tree, tiling_layer.gaps);
tiling_layer.pending_blockers.extend(blocker);
} else {
handle.unset_grab(data, event.serial, event.time);
}
}
}

View file

@ -10,7 +10,7 @@ use crate::{
CosmicMapped, CosmicMappedRenderElement, CosmicStack, CosmicWindow,
},
focus::{
target::{KeyboardFocusTarget, WindowGroup},
target::{KeyboardFocusTarget, PointerFocusTarget, WindowGroup},
FocusDirection, FocusStackMut,
},
grabs::ResizeEdge,
@ -34,7 +34,7 @@ use smithay::{
ImportAll, ImportMem, Renderer,
},
desktop::{layer_map_for_output, space::SpaceElement, PopupKind},
input::{pointer::GrabStartData as PointerGrabStartData, Seat},
input::Seat,
output::Output,
reexports::wayland_server::Client,
utils::{IsAlive, Logical, Point, Rectangle, Scale},
@ -1389,60 +1389,6 @@ impl TilingLayout {
clients
}
pub fn resize_request(
&self,
mapped: &CosmicMapped,
_seat: &Seat<State>,
start_data: PointerGrabStartData<State>,
edges: ResizeEdge,
) -> Option<ResizeForkGrab> {
let (output, mut node_id) = self.queues.iter().find_map(|(output, queue)| {
let tree = &queue.trees.back().unwrap().0;
let root_id = tree.root_node_id()?;
tree.traverse_pre_order_ids(root_id)
.unwrap()
.find(|id| tree.get(id).unwrap().data().is_mapped(Some(mapped)))
.map(|id| (&output.output, id))
})?;
let queue = self.queues.get(output).unwrap();
let tree = &queue.trees.back().unwrap().0;
while let Some(group_id) = tree.get(&node_id).unwrap().parent() {
let orientation = tree.get(group_id).unwrap().data().orientation();
if !((orientation == Orientation::Vertical
&& (edges.contains(ResizeEdge::LEFT) || edges.contains(ResizeEdge::RIGHT)))
|| (orientation == Orientation::Horizontal
&& (edges.contains(ResizeEdge::TOP) || edges.contains(ResizeEdge::BOTTOM))))
{
node_id = group_id.clone();
continue;
}
let node_idx = tree
.children_ids(group_id)
.unwrap()
.position(|id| id == &node_id)
.unwrap();
let idx = match edges {
x if x.intersects(ResizeEdge::TOP_LEFT) => node_idx.checked_sub(1)?,
_ => node_idx,
};
if idx > tree.get(&group_id).unwrap().data().len() {
return None;
}
return Some(ResizeForkGrab::new(
start_data,
group_id.clone(),
output,
tree.get(&group_id).unwrap().data(),
idx,
));
}
None
}
pub fn possible_resizes(tree: &Tree<Data>, mut node_id: NodeId) -> ResizeEdge {
let mut edges = ResizeEdge::empty();
@ -1535,6 +1481,15 @@ impl TilingLayout {
(other_idx, node_idx)
};
if sizes[shrink_idx] + sizes[grow_idx]
< match orientation {
Orientation::Vertical => 720,
Orientation::Horizontal => 480,
}
{
return true;
};
let old_size = sizes[shrink_idx];
sizes[shrink_idx] =
(old_size - amount).max(if orientation == Orientation::Vertical {
@ -1786,6 +1741,102 @@ impl TilingLayout {
None
}
pub fn element_under(
&self,
location: Point<f64, Logical>,
) -> Option<(PointerFocusTarget, Point<i32, Logical>)> {
self.queues.iter().find_map(|(output_data, queue)| {
let tree = &queue.trees.back().unwrap().0;
let root = tree.root_node_id()?;
let location = (location - output_data.location.to_f64()).to_i32_round();
let mut result = None;
let mut lookup = Some(root.clone());
while let Some(node) = lookup {
let data = tree.get(&node).unwrap().data();
if data.geometry().contains(location) {
result = Some(node.clone());
}
lookup = None;
if result.is_some() && data.is_group() {
for child_id in tree.children_ids(&node).unwrap() {
if tree
.get(child_id)
.unwrap()
.data()
.geometry()
.contains(location)
{
lookup = Some(child_id.clone());
break;
}
}
}
}
match result.map(|id| (id.clone(), tree.get(&id).unwrap().data().clone())) {
Some((
_,
Data::Mapped {
mapped,
last_geometry,
},
)) => {
let test_point = location.to_f64() - last_geometry.loc.to_f64()
+ mapped.geometry().loc.to_f64();
mapped.is_in_input_region(&test_point).then(|| {
(
mapped.clone().into(),
last_geometry.loc - output_data.location - mapped.geometry().loc,
)
})
}
Some((
id,
Data::Group {
orientation,
last_geometry,
..
},
)) => {
let idx = tree
.children(&id)
.unwrap()
.position(|node| {
let data = node.data();
match orientation {
Orientation::Vertical => location.x < data.geometry().loc.x,
Orientation::Horizontal => location.y < data.geometry().loc.y,
}
})
.and_then(|x| x.checked_sub(1))?;
Some((
ResizeForkTarget {
node: id.clone(),
output: output_data.output.downgrade(),
left_up_idx: idx,
orientation,
}
.into(),
last_geometry.loc - output_data.location
+ tree
.children(&id)
.unwrap()
.skip(idx)
.next()
.map(|node| {
let geo = node.data().geometry();
geo.loc + geo.size
})
.unwrap(),
))
}
_ => None,
}
})
}
pub fn mapped(
&self,
) -> impl Iterator<Item = (&Output, &CosmicMapped, Rectangle<i32, Logical>)> {

View file

@ -51,7 +51,10 @@ use super::{
resize_indicator::ResizeIndicator, stack::CosmicStackRenderElement,
window::CosmicWindowRenderElement, CosmicMapped,
},
focus::{target::KeyboardFocusTarget, FocusStack, FocusStackMut},
focus::{
target::{KeyboardFocusTarget, PointerFocusTarget},
FocusStack, FocusStackMut,
},
grabs::{ResizeEdge, ResizeGrab},
CosmicMappedRenderElement, CosmicSurface, ResizeDirection, ResizeMode,
};
@ -200,23 +203,12 @@ impl Workspace {
pub fn element_under(
&self,
location: Point<f64, Logical>,
) -> Option<(&CosmicMapped, Point<i32, Logical>)> {
) -> Option<(PointerFocusTarget, Point<i32, Logical>)> {
self.floating_layer
.space
.element_under(location)
.or_else(|| {
self.tiling_layer.mapped().find_map(|(_, mapped, geo)| {
geo.contains(location.to_i32_round())
.then(|| {
let test_point =
location - geo.loc.to_f64() + mapped.geometry().loc.to_f64();
mapped
.is_in_input_region(&test_point)
.then_some((mapped, geo.loc - mapped.geometry().loc))
})
.flatten()
})
})
.map(|(mapped, p)| (mapped.clone().into(), p))
.or_else(|| self.tiling_layer.element_under(location))
}
pub fn element_geometry(&self, elem: &CosmicMapped) -> Option<Rectangle<i32, Logical>> {
@ -335,10 +327,6 @@ impl Workspace {
self.floating_layer
.resize_request(mapped, seat, start_data.clone(), edges)
.map(Into::into)
} else if self.tiling_layer.mapped().any(|(_, m, _)| m == mapped) {
self.tiling_layer
.resize_request(mapped, seat, start_data, edges)
.map(Into::into)
} else {
None
}