shell: Generalize move grab
This commit is contained in:
parent
9b70372f44
commit
a8a5ee466a
9 changed files with 73 additions and 53 deletions
159
src/shell/grabs/mod.rs
Normal file
159
src/shell/grabs/mod.rs
Normal file
|
|
@ -0,0 +1,159 @@
|
|||
use smithay::{
|
||||
input::pointer::{
|
||||
AxisFrame, ButtonEvent, GrabStartData as PointerGrabStartData, MotionEvent, PointerGrab,
|
||||
PointerInnerHandle, RelativeMotionEvent,
|
||||
},
|
||||
reexports::wayland_protocols::xdg::shell::server::xdg_toplevel,
|
||||
utils::{Logical, Point},
|
||||
xwayland::xwm,
|
||||
};
|
||||
|
||||
use crate::state::State;
|
||||
|
||||
use super::{
|
||||
focus::target::PointerFocusTarget,
|
||||
layout::{floating::ResizeSurfaceGrab, tiling::ResizeForkGrab},
|
||||
};
|
||||
|
||||
mod moving;
|
||||
pub use self::moving::*;
|
||||
|
||||
bitflags::bitflags! {
|
||||
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 ResizeEdge {
|
||||
pub fn flip_direction(&mut self) {
|
||||
let mut new_edge = ResizeEdge::empty();
|
||||
if self.contains(ResizeEdge::TOP) {
|
||||
new_edge.insert(ResizeEdge::BOTTOM);
|
||||
}
|
||||
if self.contains(ResizeEdge::BOTTOM) {
|
||||
new_edge.insert(ResizeEdge::TOP);
|
||||
}
|
||||
if self.contains(ResizeEdge::LEFT) {
|
||||
new_edge.insert(ResizeEdge::RIGHT);
|
||||
}
|
||||
if self.contains(ResizeEdge::RIGHT) {
|
||||
new_edge.insert(ResizeEdge::LEFT);
|
||||
}
|
||||
*self = new_edge;
|
||||
}
|
||||
}
|
||||
|
||||
impl From<xdg_toplevel::ResizeEdge> for ResizeEdge {
|
||||
#[inline]
|
||||
fn from(x: xdg_toplevel::ResizeEdge) -> Self {
|
||||
Self::from_bits(x.into()).unwrap()
|
||||
}
|
||||
}
|
||||
|
||||
impl From<ResizeEdge> for xdg_toplevel::ResizeEdge {
|
||||
#[inline]
|
||||
fn from(x: ResizeEdge) -> Self {
|
||||
Self::try_from(x.bits()).unwrap()
|
||||
}
|
||||
}
|
||||
|
||||
impl From<xwm::ResizeEdge> for ResizeEdge {
|
||||
#[inline]
|
||||
fn from(x: xwm::ResizeEdge) -> Self {
|
||||
match x {
|
||||
xwm::ResizeEdge::Top => ResizeEdge::TOP,
|
||||
xwm::ResizeEdge::Bottom => ResizeEdge::BOTTOM,
|
||||
xwm::ResizeEdge::Left => ResizeEdge::LEFT,
|
||||
xwm::ResizeEdge::Right => ResizeEdge::RIGHT,
|
||||
xwm::ResizeEdge::TopLeft => ResizeEdge::TOP_LEFT,
|
||||
xwm::ResizeEdge::BottomLeft => ResizeEdge::BOTTOM_LEFT,
|
||||
xwm::ResizeEdge::TopRight => ResizeEdge::TOP_RIGHT,
|
||||
xwm::ResizeEdge::BottomRight => ResizeEdge::BOTTOM_RIGHT,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub enum ResizeGrab {
|
||||
Floating(ResizeSurfaceGrab),
|
||||
Tiling(ResizeForkGrab),
|
||||
}
|
||||
|
||||
impl From<ResizeSurfaceGrab> for ResizeGrab {
|
||||
fn from(grab: ResizeSurfaceGrab) -> Self {
|
||||
ResizeGrab::Floating(grab)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<ResizeForkGrab> for ResizeGrab {
|
||||
fn from(grab: ResizeForkGrab) -> Self {
|
||||
ResizeGrab::Tiling(grab)
|
||||
}
|
||||
}
|
||||
|
||||
impl PointerGrab<State> for ResizeGrab {
|
||||
fn motion(
|
||||
&mut self,
|
||||
data: &mut State,
|
||||
handle: &mut PointerInnerHandle<'_, State>,
|
||||
focus: Option<(PointerFocusTarget, Point<i32, Logical>)>,
|
||||
event: &MotionEvent,
|
||||
) {
|
||||
match self {
|
||||
ResizeGrab::Floating(grab) => grab.motion(data, handle, focus, event),
|
||||
ResizeGrab::Tiling(grab) => grab.motion(data, handle, focus, event),
|
||||
}
|
||||
}
|
||||
|
||||
fn relative_motion(
|
||||
&mut self,
|
||||
data: &mut State,
|
||||
handle: &mut PointerInnerHandle<'_, State>,
|
||||
focus: Option<(PointerFocusTarget, Point<i32, Logical>)>,
|
||||
event: &RelativeMotionEvent,
|
||||
) {
|
||||
match self {
|
||||
ResizeGrab::Floating(grab) => grab.relative_motion(data, handle, focus, event),
|
||||
ResizeGrab::Tiling(grab) => grab.relative_motion(data, handle, focus, event),
|
||||
}
|
||||
}
|
||||
|
||||
fn button(
|
||||
&mut self,
|
||||
data: &mut State,
|
||||
handle: &mut PointerInnerHandle<'_, State>,
|
||||
event: &ButtonEvent,
|
||||
) {
|
||||
match self {
|
||||
ResizeGrab::Floating(grab) => grab.button(data, handle, event),
|
||||
ResizeGrab::Tiling(grab) => grab.button(data, handle, event),
|
||||
}
|
||||
}
|
||||
|
||||
fn axis(
|
||||
&mut self,
|
||||
data: &mut State,
|
||||
handle: &mut PointerInnerHandle<'_, State>,
|
||||
details: AxisFrame,
|
||||
) {
|
||||
match self {
|
||||
ResizeGrab::Floating(grab) => grab.axis(data, handle, details),
|
||||
ResizeGrab::Tiling(grab) => grab.axis(data, handle, details),
|
||||
}
|
||||
}
|
||||
|
||||
fn start_data(&self) -> &PointerGrabStartData<State> {
|
||||
match self {
|
||||
ResizeGrab::Floating(grab) => grab.start_data(),
|
||||
ResizeGrab::Tiling(grab) => grab.start_data(),
|
||||
}
|
||||
}
|
||||
}
|
||||
329
src/shell/grabs/moving.rs
Normal file
329
src/shell/grabs/moving.rs
Normal file
|
|
@ -0,0 +1,329 @@
|
|||
// SPDX-License-Identifier: GPL-3.0-only
|
||||
|
||||
use crate::{
|
||||
backend::render::{element::AsGlowRenderer, IndicatorShader},
|
||||
shell::{
|
||||
element::CosmicMappedRenderElement,
|
||||
focus::target::{KeyboardFocusTarget, PointerFocusTarget},
|
||||
CosmicMapped, CosmicSurface,
|
||||
},
|
||||
utils::prelude::*,
|
||||
};
|
||||
|
||||
use smithay::{
|
||||
backend::renderer::{element::RenderElement, ImportAll, ImportMem, Renderer},
|
||||
desktop::space::SpaceElement,
|
||||
input::{
|
||||
pointer::{
|
||||
AxisFrame, ButtonEvent, GrabStartData as PointerGrabStartData, MotionEvent,
|
||||
PointerGrab, PointerInnerHandle, RelativeMotionEvent,
|
||||
},
|
||||
Seat,
|
||||
},
|
||||
output::Output,
|
||||
reexports::wayland_server::protocol::wl_surface::WlSurface,
|
||||
utils::{IsAlive, Logical, Point, Rectangle, Serial},
|
||||
wayland::compositor::SurfaceData,
|
||||
};
|
||||
use std::{cell::RefCell, collections::HashSet, time::Duration};
|
||||
|
||||
pub type SeatMoveGrabState = RefCell<Option<MoveGrabState>>;
|
||||
|
||||
pub struct MoveGrabState {
|
||||
window: CosmicMapped,
|
||||
window_offset: Point<i32, Logical>,
|
||||
indicator_thickness: u8,
|
||||
}
|
||||
|
||||
impl MoveGrabState {
|
||||
pub fn render<I, R>(&self, renderer: &mut R, seat: &Seat<State>, output: &Output) -> Vec<I>
|
||||
where
|
||||
R: Renderer + ImportAll + ImportMem + AsGlowRenderer,
|
||||
<R as Renderer>::TextureId: 'static,
|
||||
CosmicMappedRenderElement<R>: RenderElement<R>,
|
||||
I: From<CosmicMappedRenderElement<R>>,
|
||||
{
|
||||
#[cfg(feature = "debug")]
|
||||
puffin::profile_function!();
|
||||
|
||||
let cursor_at = seat.get_pointer().unwrap().current_location();
|
||||
|
||||
let mut window_geo = self.window.geometry();
|
||||
window_geo.loc += cursor_at.to_i32_round() + self.window_offset;
|
||||
if !output.geometry().intersection(window_geo).is_some() {
|
||||
return Vec::new();
|
||||
}
|
||||
|
||||
let scale = output.current_scale().fractional_scale().into();
|
||||
let render_location = cursor_at.to_i32_round() - output.geometry().loc + self.window_offset;
|
||||
|
||||
let focus_element = if self.indicator_thickness > 0 {
|
||||
Some(
|
||||
CosmicMappedRenderElement::from(IndicatorShader::focus_element(
|
||||
renderer,
|
||||
self.window.clone(),
|
||||
Rectangle::from_loc_and_size(render_location, self.window.geometry().size),
|
||||
self.indicator_thickness,
|
||||
1.0,
|
||||
))
|
||||
.into(),
|
||||
)
|
||||
} else {
|
||||
None
|
||||
};
|
||||
|
||||
let (window_elements, popup_elements) = self.window.split_render_elements::<R, I>(
|
||||
renderer,
|
||||
(render_location - self.window.geometry().loc).to_physical_precise_round(scale),
|
||||
scale,
|
||||
1.0,
|
||||
);
|
||||
|
||||
popup_elements
|
||||
.into_iter()
|
||||
.chain(focus_element)
|
||||
.chain(window_elements)
|
||||
.collect()
|
||||
}
|
||||
|
||||
pub fn send_frames(
|
||||
&self,
|
||||
output: &Output,
|
||||
time: impl Into<Duration>,
|
||||
throttle: Option<Duration>,
|
||||
primary_scan_out_output: impl FnMut(&WlSurface, &SurfaceData) -> Option<Output> + Copy,
|
||||
) {
|
||||
self.window
|
||||
.active_window()
|
||||
.send_frame(output, time, throttle, primary_scan_out_output)
|
||||
}
|
||||
|
||||
pub fn window(&self) -> CosmicSurface {
|
||||
self.window.active_window()
|
||||
}
|
||||
}
|
||||
|
||||
pub struct MoveGrab {
|
||||
window: CosmicMapped,
|
||||
start_data: PointerGrabStartData<State>,
|
||||
seat: Seat<State>,
|
||||
outputs: HashSet<Output>,
|
||||
tiling: bool,
|
||||
}
|
||||
|
||||
impl PointerGrab<State> for MoveGrab {
|
||||
fn motion(
|
||||
&mut self,
|
||||
state: &mut State,
|
||||
handle: &mut PointerInnerHandle<'_, State>,
|
||||
_focus: Option<(PointerFocusTarget, Point<i32, Logical>)>,
|
||||
event: &MotionEvent,
|
||||
) {
|
||||
let borrow = self
|
||||
.seat
|
||||
.user_data()
|
||||
.get::<SeatMoveGrabState>()
|
||||
.map(|s| s.borrow());
|
||||
if let Some(grab_state) = borrow.as_ref().and_then(|s| s.as_ref()) {
|
||||
let mut window_geo = self.window.geometry();
|
||||
window_geo.loc += event.location.to_i32_round() + grab_state.window_offset;
|
||||
for output in state.common.shell.outputs() {
|
||||
if let Some(overlap) = output.geometry().intersection(window_geo) {
|
||||
if self.outputs.insert(output.clone()) {
|
||||
self.window.output_enter(output, overlap);
|
||||
}
|
||||
} else if self.outputs.remove(&output) {
|
||||
self.window.output_leave(output);
|
||||
}
|
||||
}
|
||||
}
|
||||
drop(borrow);
|
||||
|
||||
// While the grab is active, no client has pointer focus
|
||||
handle.motion(state, None, event);
|
||||
if !self.window.alive() {
|
||||
self.ungrab(state, handle, event.serial, event.time);
|
||||
}
|
||||
}
|
||||
|
||||
fn relative_motion(
|
||||
&mut self,
|
||||
state: &mut State,
|
||||
handle: &mut PointerInnerHandle<'_, State>,
|
||||
_focus: Option<(PointerFocusTarget, Point<i32, Logical>)>,
|
||||
event: &RelativeMotionEvent,
|
||||
) {
|
||||
// While the grab is active, no client has pointer focus
|
||||
handle.relative_motion(state, None, event);
|
||||
}
|
||||
|
||||
fn button(
|
||||
&mut self,
|
||||
state: &mut State,
|
||||
handle: &mut PointerInnerHandle<'_, State>,
|
||||
event: &ButtonEvent,
|
||||
) {
|
||||
handle.button(state, event);
|
||||
if handle.current_pressed().is_empty() {
|
||||
self.ungrab(state, handle, event.serial, event.time);
|
||||
}
|
||||
}
|
||||
|
||||
fn axis(
|
||||
&mut self,
|
||||
state: &mut State,
|
||||
handle: &mut PointerInnerHandle<'_, State>,
|
||||
details: AxisFrame,
|
||||
) {
|
||||
handle.axis(state, details);
|
||||
}
|
||||
|
||||
fn start_data(&self) -> &PointerGrabStartData<State> {
|
||||
&self.start_data
|
||||
}
|
||||
}
|
||||
|
||||
impl MoveGrab {
|
||||
pub fn new(
|
||||
start_data: PointerGrabStartData<State>,
|
||||
window: CosmicMapped,
|
||||
seat: &Seat<State>,
|
||||
initial_cursor_location: Point<f64, Logical>,
|
||||
initial_window_location: Point<i32, Logical>,
|
||||
indicator_thickness: u8,
|
||||
was_tiled: bool,
|
||||
) -> MoveGrab {
|
||||
let output = seat.active_output();
|
||||
let mut outputs = HashSet::new();
|
||||
outputs.insert(output.clone());
|
||||
window.output_enter(&output, window.geometry()); // not accurate but...
|
||||
|
||||
let grab_state = MoveGrabState {
|
||||
window: window.clone(),
|
||||
window_offset: initial_window_location - initial_cursor_location.to_i32_round(),
|
||||
indicator_thickness,
|
||||
};
|
||||
|
||||
*seat
|
||||
.user_data()
|
||||
.get::<SeatMoveGrabState>()
|
||||
.unwrap()
|
||||
.borrow_mut() = Some(grab_state);
|
||||
|
||||
MoveGrab {
|
||||
window,
|
||||
start_data,
|
||||
seat: seat.clone(),
|
||||
outputs,
|
||||
tiling: was_tiled,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn is_tiling_grab(&self) -> bool {
|
||||
self.tiling
|
||||
}
|
||||
|
||||
fn ungrab(
|
||||
&mut self,
|
||||
state: &mut State,
|
||||
handle: &mut PointerInnerHandle<'_, State>,
|
||||
serial: Serial,
|
||||
time: u32,
|
||||
) {
|
||||
// No more buttons are pressed, release the grab.
|
||||
let output = self.seat.active_output();
|
||||
|
||||
let position = if let Some(grab_state) = self
|
||||
.seat
|
||||
.user_data()
|
||||
.get::<SeatMoveGrabState>()
|
||||
.and_then(|s| s.borrow_mut().take())
|
||||
{
|
||||
if grab_state.window.alive() {
|
||||
let window_location = handle.current_location().to_i32_round()
|
||||
- output.geometry().loc
|
||||
+ grab_state.window_offset;
|
||||
|
||||
let workspace_handle = state.common.shell.active_space(&output).handle;
|
||||
for old_output in self.outputs.iter().filter(|o| *o != &output) {
|
||||
grab_state.window.output_leave(old_output);
|
||||
}
|
||||
for (window, _) in grab_state.window.windows() {
|
||||
state
|
||||
.common
|
||||
.shell
|
||||
.toplevel_info_state
|
||||
.toplevel_enter_workspace(&window, &workspace_handle);
|
||||
state
|
||||
.common
|
||||
.shell
|
||||
.toplevel_info_state
|
||||
.toplevel_enter_output(&window, &output);
|
||||
}
|
||||
|
||||
if self.tiling {
|
||||
Some(
|
||||
state
|
||||
.common
|
||||
.shell
|
||||
.active_space_mut(&output)
|
||||
.tiling_layer
|
||||
.drop_window(grab_state.window, &output, handle.current_location()),
|
||||
)
|
||||
} else {
|
||||
let offset = state
|
||||
.common
|
||||
.shell
|
||||
.active_space(&output)
|
||||
.floating_layer
|
||||
.space
|
||||
.output_geometry(&output)
|
||||
.unwrap()
|
||||
.loc;
|
||||
grab_state.window.set_geometry(Rectangle::from_loc_and_size(
|
||||
window_location + offset,
|
||||
grab_state.window.geometry().size,
|
||||
));
|
||||
state
|
||||
.common
|
||||
.shell
|
||||
.active_space_mut(&output)
|
||||
.floating_layer
|
||||
.map_internal(grab_state.window, &output, Some(window_location + offset));
|
||||
|
||||
let pointer_pos = handle.current_location();
|
||||
let relative_pos = state.common.shell.map_global_to_space(pointer_pos, &output);
|
||||
Some(window_location + offset + (pointer_pos - relative_pos).to_i32_round())
|
||||
}
|
||||
} else {
|
||||
None
|
||||
}
|
||||
} else {
|
||||
None
|
||||
};
|
||||
|
||||
handle.unset_grab(state, serial, time);
|
||||
if self.window.alive() {
|
||||
if let Some(position) = position {
|
||||
handle.motion(
|
||||
state,
|
||||
Some((
|
||||
PointerFocusTarget::from(self.window.clone()),
|
||||
position - self.window.geometry().loc,
|
||||
)),
|
||||
&MotionEvent {
|
||||
location: handle.current_location(),
|
||||
serial: serial,
|
||||
time: time,
|
||||
},
|
||||
);
|
||||
}
|
||||
Common::set_focus(
|
||||
state,
|
||||
Some(&KeyboardFocusTarget::from(self.window.clone())),
|
||||
&self.seat,
|
||||
Some(serial),
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue