cosmic-comp/src/input/mod.rs
Ian Douglas Scott 96c931a0c3 Split off KeyboardHandle::filter callback to separate method
This saves 7 levels of indentation, which seems like a plus.

The `InputEvent::Keyboard` branch of the `match` now fits clearly in one
screen. The `filter_keyboard_input()` method could probably still be
broken up more.

The method needs a few arguments, but it seems nice to make what it
depends on explicit.
2024-09-19 16:23:25 +02:00

2021 lines
91 KiB
Rust

// SPDX-License-Identifier: GPL-3.0-only
use crate::{
config::{
key_bindings::{
cosmic_keystate_from_smithay, cosmic_modifiers_eq_smithay,
cosmic_modifiers_from_smithay,
},
Action, Config, PrivateAction,
},
input::gestures::{GestureState, SwipeAction},
shell::{
focus::target::{KeyboardFocusTarget, PointerFocusTarget},
grabs::{ReleaseMode, ResizeEdge},
layout::{floating::ResizeGrabMarker, tiling::TilingLayout},
SeatExt, Trigger,
},
utils::{prelude::*, quirks::workspace_overview_is_open},
wayland::{
handlers::screencopy::SessionHolder,
protocols::screencopy::{BufferConstraints, CursorSession},
},
};
use calloop::{
timer::{TimeoutAction, Timer},
RegistrationToken,
};
use cosmic_comp_config::workspace::WorkspaceLayout;
use cosmic_settings_config::shortcuts;
use cosmic_settings_config::shortcuts::action::{Direction, ResizeDirection};
use smithay::{
backend::input::{
AbsolutePositionEvent, Axis, AxisSource, Device, DeviceCapability, GestureBeginEvent,
GestureEndEvent, GesturePinchUpdateEvent as _, GestureSwipeUpdateEvent as _, InputBackend,
InputEvent, KeyState, KeyboardKeyEvent, PointerAxisEvent, ProximityState,
TabletToolButtonEvent, TabletToolEvent, TabletToolProximityEvent, TabletToolTipEvent,
TabletToolTipState, TouchEvent,
},
desktop::{
layer_map_for_output, space::SpaceElement, utils::under_from_surface_tree,
WindowSurfaceType,
},
input::{
keyboard::{FilterResult, KeysymHandle, ModifiersState},
pointer::{
AxisFrame, ButtonEvent, GestureHoldBeginEvent, GestureHoldEndEvent,
GesturePinchBeginEvent, GesturePinchEndEvent, GesturePinchUpdateEvent,
GestureSwipeBeginEvent, GestureSwipeEndEvent, GestureSwipeUpdateEvent, MotionEvent,
PointerGrab, RelativeMotionEvent,
},
touch::{DownEvent, MotionEvent as TouchMotionEvent, UpEvent},
Seat,
},
output::Output,
reexports::{
input::Device as InputDevice, wayland_server::protocol::wl_shm::Format as ShmFormat,
},
utils::{Point, Serial, SERIAL_COUNTER},
wayland::{
keyboard_shortcuts_inhibit::KeyboardShortcutsInhibitorSeat,
pointer_constraints::{with_pointer_constraint, PointerConstraint},
seat::WaylandFocus,
shell::wlr_layer::Layer as WlrLayer,
tablet_manager::{TabletDescriptor, TabletSeatTrait},
},
xwayland::X11Surface,
};
use tracing::{error, trace};
use xkbcommon::xkb::{Keycode, Keysym};
use std::{
any::Any,
borrow::Cow,
cell::RefCell,
collections::HashSet,
time::{Duration, Instant},
};
pub mod actions;
pub mod gestures;
/// Used for debouncing focus updates due to pointer motion, if after the focus change is
/// triggered the event will cancel if the pointer moves to the original target
#[derive(Debug)]
pub struct PointerFocusState {
//the window under the cursor prior to it's movement
originally_focused_window: Option<KeyboardFocusTarget>,
//the window under the cursor after it's movement
scheduled_focused_window: Option<KeyboardFocusTarget>,
token: RegistrationToken,
}
#[derive(Default)]
pub struct SupressedKeys(RefCell<Vec<(Keycode, Option<RegistrationToken>)>>);
#[derive(Default)]
pub struct SupressedButtons(RefCell<HashSet<u32>>);
#[derive(Default, Debug)]
pub struct ModifiersShortcutQueue(RefCell<Option<shortcuts::Binding>>);
impl SupressedKeys {
fn add(&self, keysym: &KeysymHandle, token: impl Into<Option<RegistrationToken>>) {
self.0.borrow_mut().push((keysym.raw_code(), token.into()));
}
fn filter(&self, keysym: &KeysymHandle) -> Option<Vec<RegistrationToken>> {
let mut keys = self.0.borrow_mut();
let (removed, remaining) = keys
.drain(..)
.partition(|(key, _)| *key == keysym.raw_code());
*keys = remaining;
if removed.is_empty() {
return None;
}
Some(
removed
.into_iter()
.map(|(_, token)| token)
.flatten()
.collect::<Vec<_>>(),
)
}
}
impl SupressedButtons {
fn add(&self, button: u32) {
self.0.borrow_mut().insert(button);
}
fn remove(&self, button: u32) -> bool {
self.0.borrow_mut().remove(&button)
}
}
impl ModifiersShortcutQueue {
pub fn set(&self, binding: shortcuts::Binding) {
let mut set = self.0.borrow_mut();
*set = Some(binding);
}
pub fn take(&self, binding: &shortcuts::Binding) -> bool {
let mut set = self.0.borrow_mut();
if set.is_some() && set.as_ref().unwrap() == binding {
*set = None;
true
} else {
false
}
}
pub fn clear(&self) {
let mut set = self.0.borrow_mut();
*set = None;
}
}
impl State {
pub fn process_input_event<B: InputBackend>(
&mut self,
event: InputEvent<B>,
needs_key_repetition: bool,
) where
<B as InputBackend>::Device: 'static,
{
use smithay::backend::input::Event;
match event {
InputEvent::DeviceAdded { device } => {
let shell = self.common.shell.read().unwrap();
let seat = shell.seats.last_active();
seat.devices().add_device(&device);
if device.has_capability(DeviceCapability::TabletTool) {
seat.tablet_seat().add_tablet::<Self>(
&self.common.display_handle,
&TabletDescriptor::from(&device),
);
}
}
InputEvent::DeviceRemoved { device } => {
for seat in &mut self.common.shell.read().unwrap().seats.iter() {
let devices = seat.devices();
if devices.has_device(&device) {
devices.remove_device(&device);
if device.has_capability(DeviceCapability::TabletTool) {
seat.tablet_seat()
.remove_tablet(&TabletDescriptor::from(&device));
if seat.tablet_seat().count_tablets() == 0 {
seat.tablet_seat().clear_tools();
}
}
break;
}
}
}
InputEvent::Keyboard { event, .. } => {
use smithay::backend::input::KeyboardKeyEvent;
let maybe_seat = self
.common
.shell
.read()
.unwrap()
.seats
.for_device(&event.device())
.cloned();
if let Some(seat) = maybe_seat {
self.common.idle_notifier_state.notify_activity(&seat);
let keycode = event.key_code();
let state = event.state();
trace!(?keycode, ?state, "key");
let serial = SERIAL_COUNTER.next_serial();
let time = Event::time_msec(&event);
let keyboard = seat.get_keyboard().unwrap();
if let Some((action, pattern)) = keyboard
.input(
self,
keycode,
state,
serial,
time,
|data, modifiers, handle| {
Self::filter_keyboard_input(
data,
&event,
&seat,
modifiers,
handle,
serial,
needs_key_repetition,
)
},
)
.flatten()
{
if pattern.key.is_none() && state == KeyState::Released {
// we still want to send release-events and not have apps stuck on some modifiers.
keyboard.input(self, keycode, state, serial, time, |_, _, _| {
FilterResult::<()>::Forward
});
}
self.handle_action(action, &seat, serial, time, pattern, None, true)
}
}
}
InputEvent::PointerMotion { event, .. } => {
use smithay::backend::input::PointerMotionEvent;
let mut shell = self.common.shell.write().unwrap();
if let Some(seat) = shell.seats.for_device(&event.device()).cloned() {
self.common.idle_notifier_state.notify_activity(&seat);
let current_output = seat.active_output();
let mut position = seat.get_pointer().unwrap().current_location().as_global();
let under = State::surface_under(position, &current_output, &mut *shell)
.map(|(target, pos)| (target, pos.as_logical()));
let ptr = seat.get_pointer().unwrap();
let mut pointer_locked = false;
let mut pointer_confined = false;
let mut confine_region = None;
if let Some((surface, surface_loc)) = under
.as_ref()
.and_then(|(target, l)| Some((target.wl_surface()?, l)))
{
with_pointer_constraint(&surface, &ptr, |constraint| match constraint {
Some(constraint) if constraint.is_active() => {
// Constraint does not apply if not within region
if !constraint.region().map_or(true, |x| {
x.contains(
(ptr.current_location() - *surface_loc).to_i32_round(),
)
}) {
return;
}
match &*constraint {
PointerConstraint::Locked(_locked) => {
pointer_locked = true;
}
PointerConstraint::Confined(confine) => {
pointer_confined = true;
confine_region = confine.region().cloned();
}
}
}
_ => {}
});
}
let original_position = position;
position += event.delta().as_global();
let output = shell
.outputs()
.find(|output| output.geometry().to_f64().contains(position))
.cloned()
.unwrap_or(current_output.clone());
let new_under = State::surface_under(position, &output, &mut *shell)
.map(|(target, pos)| (target, pos.as_logical()));
std::mem::drop(shell);
ptr.relative_motion(
self,
under.clone(),
&RelativeMotionEvent {
delta: event.delta(),
delta_unaccel: event.delta_unaccel(),
utime: event.time(),
},
);
if pointer_locked {
ptr.frame(self);
return;
}
if ptr.is_grabbed() {
if seat
.user_data()
.get::<ResizeGrabMarker>()
.map(|marker| marker.get())
.unwrap_or(false)
{
if output != current_output {
ptr.frame(self);
return;
}
}
//If the pointer isn't grabbed, we should check if the focused element should be updated
} else if self.common.config.cosmic_conf.focus_follows_cursor {
let shell = self.common.shell.read().unwrap();
let old_keyboard_target =
shell.keyboard_target_from_position(original_position, &current_output);
let new_keyboard_target =
shell.keyboard_target_from_position(position, &output);
if old_keyboard_target != new_keyboard_target
&& new_keyboard_target.is_some()
{
let create_source = if self.common.pointer_focus_state.is_none() {
true
} else {
let PointerFocusState {
originally_focused_window,
scheduled_focused_window,
token,
} = self.common.pointer_focus_state.as_ref().unwrap();
if &new_keyboard_target == originally_focused_window {
//if we moved to the original window, just cancel the event
self.common.event_loop_handle.remove(*token);
//clear the state
self.common.pointer_focus_state = None;
false
} else if &new_keyboard_target != scheduled_focused_window {
//if we moved to a new window, update the scheduled focus
self.common.event_loop_handle.remove(*token);
true
} else {
//the state doesn't need to be updated or cleared
false
}
};
if create_source {
// prevent popups from being unfocusable if there is a gap between them and their parent
let delay = calloop::timer::Timer::from_duration(
//default to 250ms
std::time::Duration::from_millis(
self.common.config.cosmic_conf.focus_follows_cursor_delay,
),
);
let seat = seat.clone();
let token = self
.common
.event_loop_handle
.insert_source(delay, move |_, _, state| {
let target = state
.common
.pointer_focus_state
.as_ref()
.unwrap()
.scheduled_focused_window
.clone();
//clear it prior in case the user twitches in the microsecond it
//takes this function to run
state.common.pointer_focus_state = None;
Shell::set_focus(
state,
target.as_ref(),
&seat,
Some(SERIAL_COUNTER.next_serial()),
false,
);
TimeoutAction::Drop
})
.ok();
if token.is_some() {
let originally_focused_window =
if self.common.pointer_focus_state.is_none() {
old_keyboard_target
} else {
// In this case, the pointer has moved to a new window (neither original, nor scheduled)
// so we should preserve the original window for the focus state
self.common
.pointer_focus_state
.as_ref()
.unwrap()
.originally_focused_window
.clone()
};
self.common.pointer_focus_state = Some(PointerFocusState {
originally_focused_window,
scheduled_focused_window: new_keyboard_target,
token: token.unwrap(),
});
}
}
}
}
let output_geometry = output.geometry();
position.x = position.x.clamp(
output_geometry.loc.x as f64,
((output_geometry.loc.x + output_geometry.size.w) as f64).next_lower(), // FIXME: Replace with f64::next_down when stable
);
position.y = position.y.clamp(
output_geometry.loc.y as f64,
((output_geometry.loc.y + output_geometry.size.h) as f64).next_lower(), // FIXME: Replace with f64::next_down when stable
);
// If confined, don't move pointer if it would go outside surface or region
if pointer_confined {
if let Some((surface, surface_loc)) = &under {
if new_under.as_ref().and_then(|(under, _)| under.wl_surface())
!= surface.wl_surface()
{
ptr.frame(self);
return;
}
if let PointerFocusTarget::WlSurface { surface, .. } = surface {
if under_from_surface_tree(
surface,
position.as_logical() - surface_loc.to_f64(),
(0, 0),
WindowSurfaceType::ALL,
)
.is_none()
{
ptr.frame(self);
return;
}
}
if let Some(region) = confine_region {
if !region
.contains((position.as_logical() - *surface_loc).to_i32_round())
{
ptr.frame(self);
return;
}
}
}
}
let serial = SERIAL_COUNTER.next_serial();
ptr.motion(
self,
under,
&MotionEvent {
location: position.as_logical(),
serial,
time: event.time_msec(),
},
);
ptr.frame(self);
// If pointer is now in a constraint region, activate it
if let Some((under, surface_location)) = new_under
.and_then(|(target, loc)| Some((target.wl_surface()?.into_owned(), loc)))
{
with_pointer_constraint(&under, &ptr, |constraint| match constraint {
Some(constraint) if !constraint.is_active() => {
let region = match &*constraint {
PointerConstraint::Locked(locked) => locked.region(),
PointerConstraint::Confined(confined) => confined.region(),
};
let point =
(ptr.current_location() - surface_location).to_i32_round();
if region.map_or(true, |region| region.contains(point)) {
constraint.activate();
}
}
_ => {}
});
}
let shell = self.common.shell.read().unwrap();
if output != current_output {
for session in cursor_sessions_for_output(&*shell, &current_output) {
session.set_cursor_pos(None);
}
seat.set_active_output(&output);
}
for session in cursor_sessions_for_output(&shell, &output) {
if let Some((geometry, offset)) = seat.cursor_geometry(
position.as_logical().to_buffer(
output.current_scale().fractional_scale(),
output.current_transform(),
&output_geometry.size.to_f64().as_logical(),
),
self.common.clock.now(),
) {
if session
.current_constraints()
.map(|constraint| constraint.size != geometry.size)
.unwrap_or(true)
{
session.update_constraints(BufferConstraints {
size: geometry.size,
shm: vec![ShmFormat::Argb8888],
dma: None,
});
}
session.set_cursor_hotspot(offset);
session.set_cursor_pos(Some(geometry.loc));
}
}
}
}
InputEvent::PointerMotionAbsolute { event, .. } => {
let maybe_seat = self
.common
.shell
.read()
.unwrap()
.seats
.for_device(&event.device())
.cloned();
if let Some(seat) = maybe_seat {
self.common.idle_notifier_state.notify_activity(&seat);
let output = seat.active_output();
let geometry = output.geometry();
let position = geometry.loc.to_f64()
+ smithay::backend::input::AbsolutePositionEvent::position_transformed(
&event,
geometry.size.as_logical(),
)
.as_global();
let serial = SERIAL_COUNTER.next_serial();
let under = State::surface_under(
position,
&output,
&mut *self.common.shell.write().unwrap(),
)
.map(|(target, pos)| (target, pos.as_logical()));
let ptr = seat.get_pointer().unwrap();
ptr.motion(
self,
under,
&MotionEvent {
location: position.as_logical(),
serial,
time: event.time_msec(),
},
);
ptr.frame(self);
let shell = self.common.shell.read().unwrap();
for session in cursor_sessions_for_output(&*shell, &output) {
if let Some((geometry, offset)) = seat.cursor_geometry(
position.as_logical().to_buffer(
output.current_scale().fractional_scale(),
output.current_transform(),
&geometry.size.to_f64().as_logical(),
),
self.common.clock.now(),
) {
if session
.current_constraints()
.map(|constraint| constraint.size != geometry.size)
.unwrap_or(true)
{
session.update_constraints(BufferConstraints {
size: geometry.size,
shm: vec![ShmFormat::Argb8888],
dma: None,
});
}
session.set_cursor_hotspot(offset);
session.set_cursor_pos(Some(geometry.loc));
}
}
}
}
InputEvent::PointerButton { event, .. } => {
use smithay::backend::input::{ButtonState, PointerButtonEvent};
//
let Some(seat) = self
.common
.shell
.read()
.unwrap()
.seats
.for_device(&event.device())
.cloned()
else {
return;
};
self.common.idle_notifier_state.notify_activity(&seat);
let serial = SERIAL_COUNTER.next_serial();
let button = event.button_code();
let mut pass_event = !seat.supressed_buttons().remove(button);
if event.state() == ButtonState::Pressed {
// change the keyboard focus unless the pointer is grabbed
// We test for any matching surface type here but always use the root
// (in case of a window the toplevel) surface for the focus.
// see: https://gitlab.freedesktop.org/wayland/wayland/-/issues/294
if !seat.get_pointer().unwrap().is_grabbed() {
let output = seat.active_output();
let global_position =
seat.get_pointer().unwrap().current_location().as_global();
let shell = self.common.shell.write().unwrap();
let under = shell.keyboard_target_from_position(global_position, &output);
// Don't check override redirect windows, because we don't set keyboard focus to them explicitly.
// These cases are handled by the XwaylandKeyboardGrab.
if let Some(target) = shell.element_under(global_position, &output) {
if seat.get_keyboard().unwrap().modifier_state().logo {
if let Some(surface) = target.toplevel().map(Cow::into_owned) {
let seat_clone = seat.clone();
let mouse_button = PointerButtonEvent::button(&event);
let mut supress_button = || {
// If the logo is held then the pointer event is
// aimed at the compositor and shouldn't be passed
// to the application.
pass_event = false;
seat.supressed_buttons().add(button);
};
fn dispatch_grab<G: PointerGrab<State> + 'static>(
grab: Option<(G, smithay::input::pointer::Focus)>,
seat: Seat<State>,
serial: Serial,
state: &mut State,
) {
if let Some((target, focus)) = grab {
seat.modifiers_shortcut_queue().clear();
seat.get_pointer()
.unwrap()
.set_grab(state, target, serial, focus);
}
}
if let Some(mouse_button) = mouse_button {
match mouse_button {
smithay::backend::input::MouseButton::Left => {
supress_button();
self.common.event_loop_handle.insert_idle(
move |state| {
let mut shell =
state.common.shell.write().unwrap();
let res = shell.move_request(
&surface,
&seat_clone,
serial,
ReleaseMode::NoMouseButtons,
false,
&state.common.config,
&state.common.event_loop_handle,
&state.common.xdg_activation_state,
false,
);
drop(shell);
dispatch_grab(
res, seat_clone, serial, state,
);
},
);
}
smithay::backend::input::MouseButton::Right => {
supress_button();
self.common.event_loop_handle.insert_idle(
move |state| {
let mut shell =
state.common.shell.write().unwrap();
let Some(target_elem) =
shell.element_for_surface(&surface)
else {
return;
};
let Some(geom) =
shell.space_for(target_elem).and_then(
|f| f.element_geometry(target_elem),
)
else {
return;
};
let geom = geom.to_f64();
let center =
geom.loc + geom.size.downscale(2.0);
let offset = center.to_global(&output)
- global_position;
let edge = match (
offset.x > 0.0,
offset.y > 0.0,
) {
(true, true) => ResizeEdge::TOP_LEFT,
(false, true) => ResizeEdge::TOP_RIGHT,
(true, false) => {
ResizeEdge::BOTTOM_LEFT
}
(false, false) => {
ResizeEdge::BOTTOM_RIGHT
}
};
let res = shell.resize_request(
&surface,
&seat_clone,
serial,
edge,
false,
);
drop(shell);
dispatch_grab(
res, seat_clone, serial, state,
);
},
);
}
_ => {}
}
}
}
}
}
std::mem::drop(shell);
Shell::set_focus(self, under.as_ref(), &seat, Some(serial), false);
}
} else {
let mut shell = self.common.shell.write().unwrap();
if let Some(Trigger::Pointer(action_button)) =
shell.overview_mode().0.active_trigger()
{
if *action_button == button {
shell.set_overview_mode(None, self.common.event_loop_handle.clone());
}
}
std::mem::drop(shell);
};
let ptr = seat.get_pointer().unwrap();
if pass_event {
ptr.button(
self,
&ButtonEvent {
button,
state: event.state(),
serial,
time: event.time_msec(),
},
);
ptr.frame(self);
} else if event.state() == ButtonState::Released {
ptr.unset_grab(self, serial, event.time_msec())
}
}
InputEvent::PointerAxis { event, .. } => {
let scroll_factor =
if let Some(device) = <dyn Any>::downcast_ref::<InputDevice>(&event.device()) {
self.common.config.scroll_factor(device)
} else {
1.0
};
let maybe_seat = self
.common
.shell
.read()
.unwrap()
.seats
.for_device(&event.device())
.cloned();
if let Some(seat) = maybe_seat {
self.common.idle_notifier_state.notify_activity(&seat);
let mut frame = AxisFrame::new(event.time_msec()).source(event.source());
if let Some(horizontal_amount) = event.amount(Axis::Horizontal) {
if horizontal_amount != 0.0 {
frame =
frame.value(Axis::Horizontal, scroll_factor * horizontal_amount);
if let Some(discrete) = event.amount_v120(Axis::Horizontal) {
frame = frame.v120(
Axis::Horizontal,
(discrete * scroll_factor).round() as i32,
);
}
} else if event.source() == AxisSource::Finger {
frame = frame.stop(Axis::Horizontal);
}
}
if let Some(vertical_amount) = event.amount(Axis::Vertical) {
if vertical_amount != 0.0 {
frame = frame.value(Axis::Vertical, scroll_factor * vertical_amount);
if let Some(discrete) = event.amount_v120(Axis::Vertical) {
frame = frame.v120(
Axis::Vertical,
(discrete * scroll_factor).round() as i32,
);
}
} else if event.source() == AxisSource::Finger {
frame = frame.stop(Axis::Vertical);
}
}
let ptr = seat.get_pointer().unwrap();
ptr.axis(self, frame);
ptr.frame(self);
}
}
InputEvent::GestureSwipeBegin { event, .. } => {
let maybe_seat = self
.common
.shell
.read()
.unwrap()
.seats
.for_device(&event.device())
.cloned();
if let Some(seat) = maybe_seat {
self.common.idle_notifier_state.notify_activity(&seat);
if event.fingers() >= 3 && !workspace_overview_is_open(&seat.active_output()) {
self.common.gesture_state = Some(GestureState::new(event.fingers()));
} else {
let serial = SERIAL_COUNTER.next_serial();
let pointer = seat.get_pointer().unwrap();
pointer.gesture_swipe_begin(
self,
&GestureSwipeBeginEvent {
serial,
time: event.time_msec(),
fingers: event.fingers(),
},
);
}
}
}
InputEvent::GestureSwipeUpdate { event, .. } => {
let maybe_seat = self
.common
.shell
.read()
.unwrap()
.seats
.for_device(&event.device())
.cloned();
if let Some(seat) = maybe_seat {
self.common.idle_notifier_state.notify_activity(&seat);
let mut activate_action: Option<SwipeAction> = None;
if let Some(ref mut gesture_state) = self.common.gesture_state {
let first_update = gesture_state.update(
event.delta(),
Duration::from_millis(event.time_msec() as u64),
);
// Decide on action if first update
if first_update {
let mut natural_scroll = false;
if let Some(scroll_config) =
&self.common.config.cosmic_conf.input_touchpad.scroll_config
{
if let Some(natural) = scroll_config.natural_scroll {
natural_scroll = natural;
}
}
activate_action = match gesture_state.fingers {
3 => None, // TODO: 3 finger gestures
4 => {
if self.common.config.cosmic_conf.workspaces.workspace_layout
== WorkspaceLayout::Horizontal
{
match gesture_state.direction {
Some(Direction::Left) => {
if natural_scroll {
Some(SwipeAction::NextWorkspace)
} else {
Some(SwipeAction::PrevWorkspace)
}
}
Some(Direction::Right) => {
if natural_scroll {
Some(SwipeAction::PrevWorkspace)
} else {
Some(SwipeAction::NextWorkspace)
}
}
_ => None, // TODO: Other actions
}
} else {
match gesture_state.direction {
Some(Direction::Up) => {
if natural_scroll {
Some(SwipeAction::NextWorkspace)
} else {
Some(SwipeAction::PrevWorkspace)
}
}
Some(Direction::Down) => {
if natural_scroll {
Some(SwipeAction::PrevWorkspace)
} else {
Some(SwipeAction::NextWorkspace)
}
}
_ => None, // TODO: Other actions
}
}
}
_ => None,
};
gesture_state.action = activate_action;
}
match gesture_state.action {
Some(SwipeAction::NextWorkspace) | Some(SwipeAction::PrevWorkspace) => {
self.common.shell.write().unwrap().update_workspace_delta(
&seat.active_output(),
gesture_state.delta,
)
}
_ => {}
}
} else {
let pointer = seat.get_pointer().unwrap();
pointer.gesture_swipe_update(
self,
&GestureSwipeUpdateEvent {
time: event.time_msec(),
delta: event.delta(),
},
);
}
if let Some(action) = activate_action {
self.handle_swipe_action(action, &seat);
}
}
}
InputEvent::GestureSwipeEnd { event, .. } => {
let maybe_seat = self
.common
.shell
.read()
.unwrap()
.seats
.for_device(&event.device())
.cloned();
if let Some(seat) = maybe_seat {
self.common.idle_notifier_state.notify_activity(&seat);
if let Some(ref gesture_state) = self.common.gesture_state {
match gesture_state.action {
Some(SwipeAction::NextWorkspace) | Some(SwipeAction::PrevWorkspace) => {
let velocity = gesture_state.velocity();
let norm_velocity =
if self.common.config.cosmic_conf.workspaces.workspace_layout
== WorkspaceLayout::Horizontal
{
velocity / seat.active_output().geometry().size.w as f64
} else {
velocity / seat.active_output().geometry().size.h as f64
};
let _ = self.common.shell.write().unwrap().end_workspace_swipe(
&seat.active_output(),
norm_velocity,
&mut self.common.workspace_state.update(),
);
}
_ => {}
}
self.common.gesture_state = None;
} else {
let serial = SERIAL_COUNTER.next_serial();
let pointer = seat.get_pointer().unwrap();
pointer.gesture_swipe_end(
self,
&GestureSwipeEndEvent {
serial,
time: event.time_msec(),
cancelled: event.cancelled(),
},
);
}
}
}
InputEvent::GesturePinchBegin { event, .. } => {
let maybe_seat = self
.common
.shell
.read()
.unwrap()
.seats
.for_device(&event.device())
.cloned();
if let Some(seat) = maybe_seat {
self.common.idle_notifier_state.notify_activity(&seat);
let serial = SERIAL_COUNTER.next_serial();
let pointer = seat.get_pointer().unwrap();
pointer.gesture_pinch_begin(
self,
&GesturePinchBeginEvent {
serial,
time: event.time_msec(),
fingers: event.fingers(),
},
);
}
}
InputEvent::GesturePinchUpdate { event, .. } => {
let maybe_seat = self
.common
.shell
.read()
.unwrap()
.seats
.for_device(&event.device())
.cloned();
if let Some(seat) = maybe_seat {
self.common.idle_notifier_state.notify_activity(&seat);
let pointer = seat.get_pointer().unwrap();
pointer.gesture_pinch_update(
self,
&GesturePinchUpdateEvent {
time: event.time_msec(),
delta: event.delta(),
scale: event.scale(),
rotation: event.rotation(),
},
);
}
}
InputEvent::GesturePinchEnd { event, .. } => {
let maybe_seat = self
.common
.shell
.read()
.unwrap()
.seats
.for_device(&event.device())
.cloned();
if let Some(seat) = maybe_seat {
self.common.idle_notifier_state.notify_activity(&seat);
let serial = SERIAL_COUNTER.next_serial();
let pointer = seat.get_pointer().unwrap();
pointer.gesture_pinch_end(
self,
&GesturePinchEndEvent {
serial,
time: event.time_msec(),
cancelled: event.cancelled(),
},
);
}
}
InputEvent::GestureHoldBegin { event, .. } => {
let maybe_seat = self
.common
.shell
.read()
.unwrap()
.seats
.for_device(&event.device())
.cloned();
if let Some(seat) = maybe_seat {
self.common.idle_notifier_state.notify_activity(&seat);
let serial = SERIAL_COUNTER.next_serial();
let pointer = seat.get_pointer().unwrap();
pointer.gesture_hold_begin(
self,
&GestureHoldBeginEvent {
serial,
time: event.time_msec(),
fingers: event.fingers(),
},
);
}
}
InputEvent::GestureHoldEnd { event, .. } => {
let maybe_seat = self
.common
.shell
.read()
.unwrap()
.seats
.for_device(&event.device())
.cloned();
if let Some(seat) = maybe_seat {
self.common.idle_notifier_state.notify_activity(&seat);
let serial = SERIAL_COUNTER.next_serial();
let pointer = seat.get_pointer().unwrap();
pointer.gesture_hold_end(
self,
&GestureHoldEndEvent {
serial,
time: event.time_msec(),
cancelled: event.cancelled(),
},
);
}
}
InputEvent::TouchDown { event, .. } => {
let mut shell = self.common.shell.write().unwrap();
if let Some(seat) = shell.seats.for_device(&event.device()).cloned() {
self.common.idle_notifier_state.notify_activity(&seat);
let Some(output) =
mapped_output_for_device(&self.common.config, &*shell, &event.device())
.cloned()
else {
return;
};
let geometry = output.geometry();
let position = geometry.loc.to_f64()
+ event
.position_transformed(geometry.size.as_logical())
.as_global();
let under = State::surface_under(position, &output, &mut *shell)
.map(|(target, pos)| (target, pos.as_logical()));
std::mem::drop(shell);
let serial = SERIAL_COUNTER.next_serial();
let touch = seat.get_touch().unwrap();
touch.down(
self,
under,
&DownEvent {
slot: event.slot(),
location: position.as_logical(),
serial,
time: event.time_msec(),
},
);
}
}
InputEvent::TouchMotion { event, .. } => {
let mut shell = self.common.shell.write().unwrap();
if let Some(seat) = shell.seats.for_device(&event.device()).cloned() {
self.common.idle_notifier_state.notify_activity(&seat);
let Some(output) =
mapped_output_for_device(&self.common.config, &*shell, &event.device())
.cloned()
else {
return;
};
let geometry = output.geometry();
let position = geometry.loc.to_f64()
+ event
.position_transformed(geometry.size.as_logical())
.as_global();
let under = State::surface_under(position, &output, &mut *shell)
.map(|(target, pos)| (target, pos.as_logical()));
std::mem::drop(shell);
let touch = seat.get_touch().unwrap();
touch.motion(
self,
under,
&TouchMotionEvent {
slot: event.slot(),
location: position.as_logical(),
time: event.time_msec(),
},
);
}
}
InputEvent::TouchUp { event, .. } => {
let mut shell = self.common.shell.write().unwrap();
if let Some(Trigger::Touch(slot)) = shell.overview_mode().0.active_trigger() {
if *slot == event.slot() {
shell.set_overview_mode(None, self.common.event_loop_handle.clone());
}
}
let maybe_seat = shell.seats.for_device(&event.device()).cloned();
if let Some(seat) = maybe_seat {
self.common.idle_notifier_state.notify_activity(&seat);
std::mem::drop(shell);
let serial = SERIAL_COUNTER.next_serial();
let touch = seat.get_touch().unwrap();
touch.up(
self,
&UpEvent {
slot: event.slot(),
time: event.time_msec(),
serial,
},
);
}
}
InputEvent::TouchCancel { event, .. } => {
let maybe_seat = self
.common
.shell
.read()
.unwrap()
.seats
.for_device(&event.device())
.cloned();
if let Some(seat) = maybe_seat {
self.common.idle_notifier_state.notify_activity(&seat);
let touch = seat.get_touch().unwrap();
touch.cancel(self);
}
}
InputEvent::TouchFrame { event, .. } => {
let maybe_seat = self
.common
.shell
.read()
.unwrap()
.seats
.for_device(&event.device())
.cloned();
if let Some(seat) = maybe_seat {
self.common.idle_notifier_state.notify_activity(&seat);
let touch = seat.get_touch().unwrap();
touch.frame(self);
}
}
InputEvent::TabletToolAxis { event, .. } => {
let mut shell = self.common.shell.write().unwrap();
if let Some(seat) = shell.seats.for_device(&event.device()).cloned() {
self.common.idle_notifier_state.notify_activity(&seat);
let Some(output) =
mapped_output_for_device(&self.common.config, &shell, &event.device())
.cloned()
else {
return;
};
let geometry = output.geometry();
let position = event
.position_transformed(geometry.size.as_logical())
.as_global()
+ geometry.loc.to_f64();
let under = State::surface_under(position, &output, &mut *shell)
.map(|(target, pos)| (target, pos.as_logical()));
std::mem::drop(shell);
let pointer = seat.get_pointer().unwrap();
pointer.motion(
self,
under.clone(),
&MotionEvent {
location: position.as_logical(),
serial: SERIAL_COUNTER.next_serial(),
time: self.common.clock.now().as_millis(),
},
);
let tablet_seat = seat.tablet_seat();
let tablet = tablet_seat.get_tablet(&TabletDescriptor::from(&event.device()));
let tool = tablet_seat.get_tool(&event.tool());
if let (Some(tablet), Some(tool)) = (tablet, tool) {
if event.pressure_has_changed() {
tool.pressure(event.pressure());
}
if event.distance_has_changed() {
tool.distance(event.distance());
}
if event.tilt_has_changed() {
tool.tilt(event.tilt());
}
if event.slider_has_changed() {
tool.slider_position(event.slider_position());
}
if event.rotation_has_changed() {
tool.rotation(event.rotation());
}
if event.wheel_has_changed() {
tool.wheel(event.wheel_delta(), event.wheel_delta_discrete());
}
tool.motion(
position.as_logical(),
under
.and_then(|(f, loc)| f.wl_surface().map(|s| (s.into_owned(), loc))),
&tablet,
SERIAL_COUNTER.next_serial(),
event.time_msec(),
);
}
}
}
InputEvent::TabletToolProximity { event, .. } => {
let mut shell = self.common.shell.write().unwrap();
if let Some(seat) = shell.seats.for_device(&event.device()).cloned() {
self.common.idle_notifier_state.notify_activity(&seat);
let Some(output) =
mapped_output_for_device(&self.common.config, &shell, &event.device())
.cloned()
else {
return;
};
let geometry = output.geometry();
let position = event
.position_transformed(geometry.size.as_logical())
.as_global()
+ geometry.loc.to_f64();
let under = State::surface_under(position, &output, &mut *shell)
.map(|(target, pos)| (target, pos.as_logical()));
std::mem::drop(shell);
let pointer = seat.get_pointer().unwrap();
pointer.motion(
self,
under.clone(),
&MotionEvent {
location: position.as_logical(),
serial: SERIAL_COUNTER.next_serial(),
time: self.common.clock.now().as_millis(),
},
);
let tablet_seat = seat.tablet_seat();
let tablet = tablet_seat.get_tablet(&TabletDescriptor::from(&event.device()));
let dh = self.common.display_handle.clone();
let tool = tablet_seat.add_tool::<Self>(self, &dh, &event.tool());
if let Some(tablet) = tablet {
match event.state() {
ProximityState::In => {
if let Some(under) = under.and_then(|(f, loc)| {
f.wl_surface().map(|s| (s.into_owned(), loc))
}) {
tool.proximity_in(
position.as_logical(),
under,
&tablet,
SERIAL_COUNTER.next_serial(),
event.time_msec(),
)
}
}
ProximityState::Out => tool.proximity_out(event.time_msec()),
}
}
}
}
InputEvent::TabletToolTip { event, .. } => {
let maybe_seat = self
.common
.shell
.read()
.unwrap()
.seats
.for_device(&event.device())
.cloned();
if let Some(seat) = maybe_seat {
self.common.idle_notifier_state.notify_activity(&seat);
if let Some(tool) = seat.tablet_seat().get_tool(&event.tool()) {
match event.tip_state() {
TabletToolTipState::Down => {
tool.tip_down(SERIAL_COUNTER.next_serial(), event.time_msec());
}
TabletToolTipState::Up => {
tool.tip_up(event.time_msec());
}
}
}
}
}
InputEvent::TabletToolButton { event, .. } => {
let maybe_seat = self
.common
.shell
.read()
.unwrap()
.seats
.for_device(&event.device())
.cloned();
if let Some(seat) = maybe_seat {
self.common.idle_notifier_state.notify_activity(&seat);
if let Some(tool) = seat.tablet_seat().get_tool(&event.tool()) {
tool.button(
event.button(),
event.button_state(),
SERIAL_COUNTER.next_serial(),
event.time_msec(),
);
}
}
}
InputEvent::Special(_) => {}
InputEvent::SwitchToggle { event: _ } => {}
}
}
/// Determine is key event should be intercepted as a key binding, or forwarded to surface
pub fn filter_keyboard_input<B: InputBackend, E: KeyboardKeyEvent<B>>(
&mut self,
event: &E,
seat: &Seat<Self>,
modifiers: &ModifiersState,
handle: KeysymHandle<'_>,
serial: Serial,
needs_key_repetition: bool,
) -> FilterResult<Option<(Action, shortcuts::Binding)>> {
let mut shell = self.common.shell.write().unwrap();
let keyboard = seat.get_keyboard().unwrap();
let pointer = seat.get_pointer().unwrap();
let is_grabbed = keyboard.is_grabbed() || pointer.is_grabbed();
let current_focus = keyboard.current_focus();
//this should fall back to active output since there may not be a focused output
let focused_output = seat.focused_or_active_output();
let shortcuts_inhibited = current_focus.as_ref().is_some_and(|f| {
f.wl_surface()
.and_then(|surface| {
seat.keyboard_shortcuts_inhibitor_for_surface(&surface)
.map(|inhibitor| inhibitor.is_active())
})
.unwrap_or(false)
});
// Leave move overview mode, if any modifier was released
if let Some(Trigger::KeyboardMove(action_modifiers)) =
shell.overview_mode().0.active_trigger()
{
if (action_modifiers.ctrl && !modifiers.ctrl)
|| (action_modifiers.alt && !modifiers.alt)
|| (action_modifiers.logo && !modifiers.logo)
|| (action_modifiers.shift && !modifiers.shift)
{
shell.set_overview_mode(None, self.common.event_loop_handle.clone());
}
}
// Leave swap overview mode, if any key was released
if let Some(Trigger::KeyboardSwap(action_pattern, old_descriptor)) =
shell.overview_mode().0.active_trigger()
{
if (action_pattern.modifiers.ctrl && !modifiers.ctrl)
|| (action_pattern.modifiers.alt && !modifiers.alt)
|| (action_pattern.modifiers.logo && !modifiers.logo)
|| (action_pattern.modifiers.shift && !modifiers.shift)
|| (action_pattern.key.is_some()
&& handle.raw_syms().contains(&action_pattern.key.unwrap())
&& event.state() == KeyState::Released)
{
shell.set_overview_mode(None, self.common.event_loop_handle.clone());
if let Some(focus) = current_focus {
if let Some(new_descriptor) =
shell.workspaces.active(&focused_output).1.node_desc(focus)
{
let mut spaces = shell.workspaces.spaces_mut();
if old_descriptor.handle != new_descriptor.handle {
let (mut old_w, mut other_w) = spaces
.partition::<Vec<_>, _>(|w| w.handle == old_descriptor.handle);
if let Some(old_workspace) = old_w.get_mut(0) {
if let Some(new_workspace) = other_w
.iter_mut()
.find(|w| w.handle == new_descriptor.handle)
{
{
let mut stack = new_workspace.focus_stack.get_mut(&seat);
for elem in
old_descriptor.focus_stack.iter().flat_map(|node_id| {
old_workspace.tiling_layer.element_for_node(node_id)
})
{
stack.append(elem);
}
}
{
let mut stack = old_workspace.focus_stack.get_mut(&seat);
for elem in
new_descriptor.focus_stack.iter().flat_map(|node_id| {
new_workspace.tiling_layer.element_for_node(node_id)
})
{
stack.append(elem);
}
}
if let Some(focus) = TilingLayout::swap_trees(
&mut old_workspace.tiling_layer,
Some(&mut new_workspace.tiling_layer),
&old_descriptor,
&new_descriptor,
) {
let seat = seat.clone();
self.common.event_loop_handle.insert_idle(move |state| {
Shell::set_focus(
state,
Some(&focus),
&seat,
None,
true,
);
});
}
old_workspace.refresh_focus_stack();
new_workspace.refresh_focus_stack();
}
}
} else {
if let Some(workspace) =
spaces.find(|w| w.handle == new_descriptor.handle)
{
if let Some(focus) = TilingLayout::swap_trees(
&mut workspace.tiling_layer,
None,
&old_descriptor,
&new_descriptor,
) {
std::mem::drop(spaces);
let seat = seat.clone();
self.common.event_loop_handle.insert_idle(move |state| {
Shell::set_focus(state, Some(&focus), &seat, None, true);
});
}
workspace.refresh_focus_stack();
}
}
}
} else {
let new_workspace = shell.workspaces.active(&focused_output).1.handle;
if new_workspace != old_descriptor.handle {
let spaces = shell.workspaces.spaces_mut();
let (mut old_w, mut other_w) =
spaces.partition::<Vec<_>, _>(|w| w.handle == old_descriptor.handle);
if let Some(old_workspace) = old_w.get_mut(0) {
if let Some(new_workspace) =
other_w.iter_mut().find(|w| w.handle == new_workspace)
{
if new_workspace.tiling_layer.windows().next().is_none() {
{
let mut stack = new_workspace.focus_stack.get_mut(&seat);
for elem in
old_descriptor.focus_stack.iter().flat_map(|node_id| {
old_workspace.tiling_layer.element_for_node(node_id)
})
{
stack.append(elem);
}
}
if let Some(focus) = TilingLayout::move_tree(
&mut old_workspace.tiling_layer,
&mut new_workspace.tiling_layer,
&new_workspace.handle,
&seat,
new_workspace.focus_stack.get(&seat).iter(),
old_descriptor.clone(),
None,
) {
let seat = seat.clone();
self.common.event_loop_handle.insert_idle(move |state| {
Shell::set_focus(
state,
Some(&focus),
&seat,
None,
true,
);
});
}
old_workspace.refresh_focus_stack();
}
}
}
}
}
}
}
// Leave or update resize mode, if modifiers changed or initial key was released
if let Some(action_pattern) = shell.resize_mode().0.active_binding() {
if action_pattern.key.is_some()
&& event.state() == KeyState::Released
&& handle.raw_syms().contains(&action_pattern.key.unwrap())
{
shell.set_resize_mode(
None,
&self.common.config,
self.common.event_loop_handle.clone(),
);
} else if !cosmic_modifiers_eq_smithay(&action_pattern.modifiers, modifiers) {
let mut new_pattern = action_pattern.clone();
new_pattern.modifiers = cosmic_modifiers_from_smithay(modifiers.clone());
let enabled =
self.common
.config
.shortcuts
.iter()
.find_map(move |(binding, action)| {
if binding == &new_pattern
&& matches!(action, shortcuts::Action::Resizing(_))
{
let shortcuts::Action::Resizing(direction) = action else {
unreachable!()
};
Some((new_pattern.clone(), *direction))
} else {
None
}
});
shell.set_resize_mode(
enabled,
&self.common.config,
self.common.event_loop_handle.clone(),
);
}
}
// Special case resizing with regards to arrow keys
if let Some(direction) = shell.resize_mode().0.active_direction() {
let resize_edge = match handle.modified_sym() {
Keysym::Left | Keysym::h | Keysym::H => Some(ResizeEdge::LEFT),
Keysym::Down | Keysym::j | Keysym::J => Some(ResizeEdge::BOTTOM),
Keysym::Up | Keysym::k | Keysym::K => Some(ResizeEdge::TOP),
Keysym::Right | Keysym::l | Keysym::L => Some(ResizeEdge::RIGHT),
_ => None,
};
if let Some(mut edge) = resize_edge {
if direction == ResizeDirection::Inwards {
edge.flip_direction();
}
let action = Action::Private(PrivateAction::Resizing(
direction,
edge.into(),
cosmic_keystate_from_smithay(event.state()),
));
let key_pattern = shortcuts::Binding {
modifiers: cosmic_modifiers_from_smithay(modifiers.clone()),
key: Some(handle.modified_sym()),
description: None,
};
if event.state() == KeyState::Released {
if let Some(tokens) = seat.supressed_keys().filter(&handle) {
for token in tokens {
self.common.event_loop_handle.remove(token);
}
}
} else {
let token = if needs_key_repetition {
let seat_clone = seat.clone();
let action_clone = action.clone();
let key_pattern_clone = key_pattern.clone();
let start = Instant::now();
let time = event.time_msec();
self.common
.event_loop_handle
.insert_source(
Timer::from_duration(Duration::from_millis(200)),
move |current, _, state| {
let duration = current.duration_since(start).as_millis();
state.handle_action(
action_clone.clone(),
&seat_clone,
serial,
time.overflowing_add(duration as u32).0,
key_pattern_clone.clone(),
None,
true,
);
calloop::timer::TimeoutAction::ToDuration(
Duration::from_millis(25),
)
},
)
.ok()
} else {
None
};
seat.supressed_keys().add(&handle, token);
}
return FilterResult::Intercept(Some((action, key_pattern)));
}
}
std::mem::drop(shell);
// cancel grabs
if is_grabbed
&& handle.modified_sym() == Keysym::Escape
&& event.state() == KeyState::Pressed
&& !modifiers.alt
&& !modifiers.ctrl
&& !modifiers.logo
&& !modifiers.shift
{
seat.supressed_keys().add(&handle, None);
return FilterResult::Intercept(Some((
Action::Private(PrivateAction::Escape),
shortcuts::Binding {
modifiers: shortcuts::Modifiers::default(),
key: Some(Keysym::Escape),
description: None,
},
)));
}
// Skip released events for initially surpressed keys
if event.state() == KeyState::Released {
if let Some(tokens) = seat.supressed_keys().filter(&handle) {
for token in tokens {
self.common.event_loop_handle.remove(token);
}
return FilterResult::Intercept(None);
}
}
// Handle VT switches
if event.state() == KeyState::Pressed
&& (Keysym::XF86_Switch_VT_1.raw()..=Keysym::XF86_Switch_VT_12.raw())
.contains(&handle.modified_sym().raw())
{
if let Err(err) = self.backend.kms().switch_vt(
(handle.modified_sym().raw() - Keysym::XF86_Switch_VT_1.raw() + 1) as i32,
) {
error!(?err, "Failed switching virtual terminal.");
}
seat.supressed_keys().add(&handle, None);
return FilterResult::Intercept(None);
}
// handle the rest of the global shortcuts
let mut clear_queue = true;
if !shortcuts_inhibited {
let modifiers_queue = seat.modifiers_shortcut_queue();
for (binding, action) in self.common.config.shortcuts.iter() {
if *action == shortcuts::Action::Disable {
continue;
}
// is this a released (triggered) modifier-only binding?
if binding.key.is_none()
&& event.state() == KeyState::Released
&& !cosmic_modifiers_eq_smithay(&binding.modifiers, modifiers)
&& modifiers_queue.take(binding)
{
modifiers_queue.clear();
return FilterResult::Intercept(Some((
Action::Shortcut(action.clone()),
binding.clone(),
)));
}
// could this potentially become a modifier-only binding?
if binding.key.is_none()
&& event.state() == KeyState::Pressed
&& cosmic_modifiers_eq_smithay(&binding.modifiers, modifiers)
{
modifiers_queue.set(binding.clone());
clear_queue = false;
}
// is this a normal binding?
if binding.key.is_some()
&& event.state() == KeyState::Pressed
&& handle.raw_syms().contains(&binding.key.unwrap())
&& cosmic_modifiers_eq_smithay(&binding.modifiers, modifiers)
{
modifiers_queue.clear();
seat.supressed_keys().add(&handle, None);
return FilterResult::Intercept(Some((
Action::Shortcut(action.clone()),
binding.clone(),
)));
}
}
}
// no binding
if clear_queue {
seat.modifiers_shortcut_queue().clear();
}
// keys are passed through to apps
FilterResult::Forward
}
// TODO: Try to get rid of the *mutable* Shell references (needed for hovered_stack in floating_layout)
pub fn surface_under(
global_pos: Point<f64, Global>,
output: &Output,
shell: &mut Shell,
) -> Option<(PointerFocusTarget, Point<f64, Global>)> {
let session_lock = shell.session_lock.as_ref();
let relative_pos = global_pos.to_local(output);
let output_geo = output.geometry();
if let Some(session_lock) = session_lock {
return session_lock.surfaces.get(output).map(|surface| {
(
PointerFocusTarget::WlSurface {
surface: surface.wl_surface().clone(),
toplevel: None,
},
output_geo.loc.to_f64(),
)
});
}
if let Some(window) = shell.workspaces.active(output).1.get_fullscreen() {
let layers = layer_map_for_output(output);
if let Some(layer) = layers.layer_under(WlrLayer::Overlay, relative_pos.as_logical()) {
let layer_loc = layers.layer_geometry(layer).unwrap().loc;
if let Some((wl_surface, surface_loc)) = layer.surface_under(
relative_pos.as_logical() - layer_loc.to_f64(),
WindowSurfaceType::ALL,
) {
return Some((
PointerFocusTarget::WlSurface {
surface: wl_surface,
toplevel: None,
},
(output_geo.loc + layer_loc.as_global() + surface_loc.as_global()).to_f64(),
));
}
}
if let Some((surface, geo)) = shell
.override_redirect_windows
.iter()
.find(|or| {
or.is_in_input_region(
&(global_pos.as_logical() - X11Surface::geometry(*or).loc.to_f64()),
)
})
.and_then(|or| {
or.wl_surface()
.map(|surface| (surface, X11Surface::geometry(or).loc.as_global().to_f64()))
})
{
return Some((
PointerFocusTarget::WlSurface {
surface,
toplevel: None,
},
geo,
));
}
PointerFocusTarget::under_surface(window, relative_pos.as_logical()).map(
|(target, surface_loc)| {
(target, (output_geo.loc + surface_loc.as_global()).to_f64())
},
)
} else {
{
let layers = layer_map_for_output(output);
if let Some(layer) = layers
.layer_under(WlrLayer::Overlay, relative_pos.as_logical())
.or_else(|| layers.layer_under(WlrLayer::Top, relative_pos.as_logical()))
{
let layer_loc = layers.layer_geometry(layer).unwrap().loc;
if let Some((wl_surface, surface_loc)) = layer.surface_under(
relative_pos.as_logical() - layer_loc.to_f64(),
WindowSurfaceType::ALL,
) {
return Some((
PointerFocusTarget::WlSurface {
surface: wl_surface,
toplevel: None,
},
(output_geo.loc + layer_loc.as_global() + surface_loc.as_global())
.to_f64(),
));
}
}
}
if let Some((surface, geo)) = shell
.override_redirect_windows
.iter()
.find(|or| {
or.is_in_input_region(
&(global_pos.as_logical() - X11Surface::geometry(*or).loc.to_f64()),
)
})
.and_then(|or| {
or.wl_surface()
.map(|surface| (surface, X11Surface::geometry(or).loc.as_global().to_f64()))
})
{
return Some((
PointerFocusTarget::WlSurface {
surface,
toplevel: None,
},
geo,
));
}
if let Some((target, loc)) = shell.surface_under(global_pos, output) {
return Some((target, loc));
}
{
let layers = layer_map_for_output(output);
if let Some(layer) = layers
.layer_under(WlrLayer::Bottom, relative_pos.as_logical())
.or_else(|| layers.layer_under(WlrLayer::Background, relative_pos.as_logical()))
{
let layer_loc = layers.layer_geometry(layer).unwrap().loc;
if let Some((wl_surface, surface_loc)) = layer.surface_under(
relative_pos.as_logical() - layer_loc.to_f64(),
WindowSurfaceType::ALL,
) {
return Some((
PointerFocusTarget::WlSurface {
surface: wl_surface,
toplevel: None,
},
(output_geo.loc + layer_loc.as_global() + surface_loc.as_global())
.to_f64(),
));
}
}
}
None
}
}
}
fn cursor_sessions_for_output(
shell: &Shell,
output: &Output,
) -> impl Iterator<Item = CursorSession> {
let workspace = shell.active_space(&output);
let maybe_fullscreen = workspace.get_fullscreen();
workspace
.cursor_sessions()
.into_iter()
.chain(
maybe_fullscreen
.map(|w| w.cursor_sessions())
.into_iter()
.flatten(),
)
.chain(output.cursor_sessions().into_iter())
}
// TODO Is it possible to determine mapping for external touchscreen?
// Support map_to_region like sway?
fn mapped_output_for_device<'a, D: Device + 'static>(
config: &Config,
shell: &'a Shell,
device: &D,
) -> Option<&'a Output> {
let map_to_output = if let Some(device) = <dyn Any>::downcast_ref::<InputDevice>(device) {
config
.map_to_output(device)
.and_then(|name| shell.outputs().find(|output| output.name() == name))
} else {
None
};
map_to_output.or_else(|| shell.builtin_output())
}
// FIXME: When f64::next_down reaches stable rust, use that instead
trait NextDown {
fn next_lower(self) -> Self;
}
impl NextDown for f64 {
fn next_lower(self) -> Self {
// We must use strictly integer arithmetic to prevent denormals from
// flushing to zero after an arithmetic operation on some platforms.
const NEG_TINY_BITS: u64 = 0x8000_0000_0000_0001; // Smallest (in magnitude) negative f64.
const CLEAR_SIGN_MASK: u64 = 0x7fff_ffff_ffff_ffff;
let bits = self.to_bits();
if self.is_nan() || bits == Self::NEG_INFINITY.to_bits() {
return self;
}
let abs = bits & CLEAR_SIGN_MASK;
let next_bits = if abs == 0 {
NEG_TINY_BITS
} else if bits == abs {
bits - 1
} else {
bits + 1
};
Self::from_bits(next_bits)
}
}