cosmic-comp/src/input/mod.rs

1279 lines
56 KiB
Rust
Raw Normal View History

2021-12-21 18:57:09 +01:00
// SPDX-License-Identifier: GPL-3.0-only
2022-07-08 19:15:56 +02:00
use crate::{
2023-05-25 00:10:24 +02:00
config::{Action, Config, KeyModifiers, WorkspaceLayout},
2022-11-10 18:42:11 +01:00
shell::{
focus::{target::PointerFocusTarget, FocusDirection},
layout::{
floating::SeatMoveGrabState,
tiling::{Direction, FocusResult},
},
OverviewMode, Workspace,
2022-11-10 18:42:11 +01:00
}, // shell::grabs::SeatMoveGrabState
2022-08-31 13:01:23 +02:00
state::Common,
2022-07-08 19:15:56 +02:00
utils::prelude::*,
2022-11-03 18:51:27 +01:00
wayland::{handlers::screencopy::ScreencopySessions, protocols::screencopy::Session},
2022-07-08 19:15:56 +02:00
};
2022-11-03 18:51:27 +01:00
use cosmic_protocols::screencopy::v1::server::zcosmic_screencopy_session_v1::InputType;
2021-12-21 18:57:09 +01:00
use smithay::{
backend::input::{
Axis, AxisSource, Device, DeviceCapability, InputBackend, InputEvent, KeyState,
PointerAxisEvent,
2022-08-31 13:01:23 +02:00
},
desktop::{layer_map_for_output, space::SpaceElement, WindowSurfaceType},
2022-08-31 13:01:23 +02:00
input::{
keyboard::{keysyms, FilterResult, KeysymHandle, XkbConfig},
pointer::{AxisFrame, ButtonEvent, CursorImageStatus, MotionEvent, RelativeMotionEvent},
2022-08-31 13:01:23 +02:00
Seat, SeatState,
2021-12-21 18:57:09 +01:00
},
output::Output,
2022-09-28 12:01:29 +02:00
reexports::wayland_server::DisplayHandle,
2022-11-22 10:10:08 +01:00
utils::{Logical, Point, Rectangle, Serial, SERIAL_COUNTER},
wayland::{
2022-09-28 12:01:29 +02:00
keyboard_shortcuts_inhibit::KeyboardShortcutsInhibitorSeat, seat::WaylandFocus,
shell::wlr_layer::Layer as WlrLayer,
},
xwayland::X11Surface,
2021-12-21 18:57:09 +01:00
};
#[cfg(not(feature = "debug"))]
use tracing::info;
use tracing::{error, trace, warn};
2022-08-31 13:01:23 +02:00
2021-12-21 18:57:09 +01:00
use std::{cell::RefCell, collections::HashMap};
2022-05-03 13:37:51 +02:00
use xkbcommon::xkb::KEY_XF86Switch_VT_12;
2021-12-21 18:57:09 +01:00
2022-07-08 14:00:13 +02:00
crate::utils::id_gen!(next_seat_id, SEAT_ID, SEAT_IDS);
#[repr(transparent)]
pub struct SeatId(pub usize);
2021-12-21 18:57:09 +01:00
pub struct ActiveOutput(pub RefCell<Output>);
#[derive(Default)]
2022-01-11 17:22:23 +01:00
pub struct SupressedKeys(RefCell<Vec<u32>>);
#[derive(Default)]
2021-12-22 20:14:09 +01:00
pub struct Devices(RefCell<HashMap<String, Vec<DeviceCapability>>>);
2022-07-08 14:00:13 +02:00
impl Default for SeatId {
fn default() -> SeatId {
SeatId(next_seat_id())
}
}
impl Drop for SeatId {
fn drop(&mut self) {
SEAT_IDS.lock().unwrap().remove(&self.0);
}
}
2022-01-11 17:22:23 +01:00
impl SupressedKeys {
fn add(&self, keysym: &KeysymHandle) {
self.0.borrow_mut().push(keysym.raw_code());
}
fn filter(&self, keysym: &KeysymHandle) -> bool {
let mut keys = self.0.borrow_mut();
if let Some(i) = keys.iter().position(|x| *x == keysym.raw_code()) {
keys.remove(i);
true
} else {
false
}
}
}
2021-12-22 20:14:09 +01:00
impl Devices {
fn add_device<D: Device>(&self, device: &D) -> Vec<DeviceCapability> {
let id = device.id();
let mut map = self.0.borrow_mut();
let caps = [DeviceCapability::Keyboard, DeviceCapability::Pointer]
.iter()
.cloned()
.filter(|c| device.has_capability(*c))
.collect::<Vec<_>>();
let new_caps = caps
.iter()
.cloned()
.filter(|c| map.values().flatten().all(|has| *c != *has))
.collect::<Vec<_>>();
map.insert(id, caps);
new_caps
}
pub fn has_device<D: Device>(&self, device: &D) -> bool {
self.0.borrow().contains_key(&device.id())
}
fn remove_device<D: Device>(&self, device: &D) -> Vec<DeviceCapability> {
let id = device.id();
let mut map = self.0.borrow_mut();
map.remove(&id)
.unwrap_or(Vec::new())
.into_iter()
.filter(|c| map.values().flatten().all(|has| *c != *has))
.collect()
}
}
2022-08-31 13:01:23 +02:00
pub fn add_seat(
dh: &DisplayHandle,
seat_state: &mut SeatState<State>,
2022-09-28 12:01:29 +02:00
output: &Output,
2022-08-31 13:01:23 +02:00
config: &Config,
name: String,
) -> Seat<State> {
let mut seat = seat_state.new_wl_seat(dh, name);
2021-12-22 20:14:09 +01:00
let userdata = seat.user_data();
2022-07-08 14:00:13 +02:00
userdata.insert_if_missing(SeatId::default);
userdata.insert_if_missing(Devices::default);
userdata.insert_if_missing(SupressedKeys::default);
userdata.insert_if_missing(SeatMoveGrabState::default);
2022-09-28 12:01:29 +02:00
userdata.insert_if_missing(|| ActiveOutput(RefCell::new(output.clone())));
2022-03-24 20:32:31 +01:00
userdata.insert_if_missing(|| RefCell::new(CursorImageStatus::Default));
// A lot of clients bind keyboard and pointer unconditionally once on launch..
// Initial clients might race the compositor on adding periheral and
// end up in a state, where they are not able to receive input.
// Additionally a lot of clients don't handle keyboards/pointer objects being
// removed very well either and we don't want to crash applications, because the
// user is replugging their keyboard or mouse.
//
// So instead of doing the right thing (and initialize these capabilities as matching
// devices appear), we have to surrender to reality and just always expose a keyboard and pointer.
2022-07-29 14:23:23 +02:00
let conf = config.xkb_config();
if let Err(err) = seat.add_keyboard((&conf).into(), 200, 25) {
warn!(
?err,
"Failed to load provided xkb config. Trying default...",
);
seat.add_keyboard(XkbConfig::default(), 200, 25)
.expect("Failed to load xkb configuration files");
}
seat.add_pointer();
2021-12-21 18:57:09 +01:00
seat
}
2022-04-25 12:35:55 +02:00
impl State {
2022-08-31 13:01:23 +02:00
pub fn process_input_event<B: InputBackend>(&mut self, event: InputEvent<B>) {
2021-12-22 20:14:09 +01:00
use smithay::backend::input::Event;
match event {
InputEvent::DeviceAdded { device } => {
2022-09-28 12:01:29 +02:00
let seat = &mut self.common.last_active_seat();
2021-12-22 20:14:09 +01:00
let userdata = seat.user_data();
let devices = userdata.get::<Devices>().unwrap();
for cap in devices.add_device(&device) {
match cap {
// TODO: Handle touch, tablet
2021-12-22 20:14:09 +01:00
_ => {}
}
}
2023-03-06 18:48:52 +01:00
#[cfg(feature = "debug")]
{
self.common.egui.state.handle_device_added(&device);
}
2021-12-22 20:14:09 +01:00
}
InputEvent::DeviceRemoved { device } => {
2022-09-28 12:01:29 +02:00
for seat in &mut self.common.seats() {
2021-12-22 20:14:09 +01:00
let userdata = seat.user_data();
let devices = userdata.get::<Devices>().unwrap();
if devices.has_device(&device) {
for cap in devices.remove_device(&device) {
match cap {
// TODO: Handle touch, tablet
2022-08-30 13:28:36 +02:00
_ => {}
2021-12-22 20:14:09 +01:00
}
}
break;
}
}
2023-03-06 18:48:52 +01:00
#[cfg(feature = "debug")]
{
self.common.egui.state.handle_device_removed(&device);
}
2021-12-22 20:14:09 +01:00
}
InputEvent::Keyboard { event, .. } => {
use smithay::backend::input::KeyboardKeyEvent;
let device = event.device();
2022-09-28 12:01:29 +02:00
for seat in self.common.seats().cloned().collect::<Vec<_>>().iter() {
let current_output = seat.active_output();
let workspace = self.common.shell.active_space_mut(&current_output);
let shortcuts_inhibited = workspace
2022-09-28 12:01:29 +02:00
.focus_stack
.get(seat)
.last()
.and_then(|window| {
2022-09-28 12:01:29 +02:00
window.wl_surface().and_then(|surface| {
seat.keyboard_shortcuts_inhibitor_for_surface(&surface)
})
})
.map(|inhibitor| inhibitor.is_active())
.unwrap_or(false);
2021-12-22 20:14:09 +01:00
let userdata = seat.user_data();
let devices = userdata.get::<Devices>().unwrap();
if devices.has_device(&device) {
let keycode = event.key_code();
let state = event.state();
trace!(?keycode, ?state, "key");
2021-12-22 20:14:09 +01:00
let serial = SERIAL_COUNTER.next_serial();
let time = Event::time_msec(&event);
if let Some((action, mods)) = seat
2022-03-30 23:24:29 +02:00
.get_keyboard()
.unwrap()
2022-08-31 13:01:23 +02:00
.input(
self,
keycode,
state,
serial,
time,
|data, modifiers, handle| {
if let OverviewMode::Started(action_modifiers, _) =
data.common.shell.overview_mode()
{
if (!action_modifiers.ctrl || modifiers.ctrl)
&& (!action_modifiers.alt || modifiers.alt)
&& (!action_modifiers.logo || modifiers.logo)
&& (!action_modifiers.shift || modifiers.shift)
{
data.common.shell.set_overview_mode(None);
}
}
2022-08-31 13:01:23 +02:00
if state == KeyState::Released
&& userdata.get::<SupressedKeys>().unwrap().filter(&handle)
2022-01-11 17:22:23 +01:00
{
2022-08-31 13:01:23 +02:00
return FilterResult::Intercept(None);
2022-01-11 17:22:23 +01:00
}
2022-04-25 12:35:55 +02:00
2023-03-06 18:48:52 +01:00
#[cfg(feature = "debug")]
{
if data.common.seats().position(|x| x == seat).unwrap() == 0
&& data.common.egui.active
{
if data.common.egui.state.wants_keyboard() {
data.common.egui.state.handle_keyboard(
&handle,
state == KeyState::Pressed,
modifiers.clone(),
);
userdata
.get::<SupressedKeys>()
.unwrap()
.add(&handle);
return FilterResult::Intercept(None);
}
}
}
2022-03-28 23:45:30 +02:00
if state == KeyState::Pressed
2022-08-31 13:01:23 +02:00
&& (keysyms::KEY_XF86Switch_VT_1..=KEY_XF86Switch_VT_12)
.contains(&handle.modified_sym())
2022-03-28 23:45:30 +02:00
{
2022-08-31 13:01:23 +02:00
if let Err(err) = data.backend.kms().switch_vt(
(handle.modified_sym() - keysyms::KEY_XF86Switch_VT_1
+ 1)
as i32,
) {
error!(?err, "Failed switching virtual terminal.");
2022-08-31 13:01:23 +02:00
}
2022-03-30 23:24:29 +02:00
userdata.get::<SupressedKeys>().unwrap().add(&handle);
2022-08-31 13:01:23 +02:00
return FilterResult::Intercept(None);
}
// here we can handle global shortcuts and the like
if !shortcuts_inhibited {
for (binding, action) in
data.common.config.static_conf.key_bindings.iter()
2022-08-31 13:01:23 +02:00
{
if state == KeyState::Pressed
&& binding.modifiers == *modifiers
&& handle.raw_syms().contains(&binding.key)
{
userdata
.get::<SupressedKeys>()
.unwrap()
.add(&handle);
return FilterResult::Intercept(Some((
action.clone(),
binding.modifiers.clone(),
)));
}
2022-08-31 13:01:23 +02:00
}
2022-03-28 23:45:30 +02:00
}
2022-01-11 17:22:23 +01:00
2022-08-31 13:01:23 +02:00
FilterResult::Forward
},
)
2022-03-30 23:24:29 +02:00
.flatten()
{
self.handle_action(action, seat, serial, time, mods, None)
2022-03-30 23:24:29 +02:00
}
2021-12-22 20:14:09 +01:00
break;
}
}
}
InputEvent::PointerMotion { event, .. } => {
use smithay::backend::input::PointerMotionEvent;
let device = event.device();
2022-09-28 12:01:29 +02:00
for seat in self.common.seats().cloned().collect::<Vec<_>>().iter() {
2021-12-22 20:14:09 +01:00
let userdata = seat.user_data();
let devices = userdata.get::<Devices>().unwrap();
if devices.has_device(&device) {
2022-09-28 12:01:29 +02:00
let current_output = seat.active_output();
2021-12-22 20:14:09 +01:00
let mut position = seat.get_pointer().unwrap().current_location();
position += event.delta();
let output = self
2022-04-25 12:35:55 +02:00
.common
2022-03-24 20:32:31 +01:00
.shell
2021-12-22 20:14:09 +01:00
.outputs()
2022-07-04 16:00:29 +02:00
.find(|output| output.geometry().to_f64().contains(position))
2021-12-28 16:23:12 +01:00
.cloned()
.unwrap_or(current_output.clone());
if output != current_output {
2022-11-03 18:51:27 +01:00
for session in sessions_for_output(&self.common, &current_output) {
session.cursor_leave(seat, InputType::Pointer);
}
for session in sessions_for_output(&self.common, &output) {
session.cursor_enter(seat, InputType::Pointer);
}
2022-09-28 12:01:29 +02:00
seat.set_active_output(&output);
2021-12-22 20:14:09 +01:00
}
let output_geometry = output.geometry();
2021-12-22 20:14:09 +01:00
position.x = (output_geometry.loc.x as f64)
2021-12-22 20:14:09 +01:00
.max(position.x)
.min((output_geometry.loc.x + output_geometry.size.w) as f64);
position.y = (output_geometry.loc.y as f64)
2021-12-22 20:14:09 +01:00
.max(position.y)
.min((output_geometry.loc.y + output_geometry.size.h) as f64);
let serial = SERIAL_COUNTER.next_serial();
2022-09-28 12:01:29 +02:00
let relative_pos = self.common.shell.map_global_to_space(position, &output);
let workspace = self.common.shell.active_space(&output);
2022-04-25 12:35:55 +02:00
let under = State::surface_under(
position,
relative_pos,
&output,
output_geometry,
&self.common.shell.override_redirect_windows,
2022-04-22 15:18:28 +02:00
&workspace,
);
2022-11-03 18:51:27 +01:00
for session in sessions_for_output(&self.common, &output) {
if let Some((geometry, offset)) = seat.cursor_geometry(
position.to_buffer(
output.current_scale().fractional_scale(),
output.current_transform(),
&output.geometry().size.to_f64(),
),
2022-11-17 20:32:54 +01:00
self.common.clock.now(),
2022-11-03 18:51:27 +01:00
) {
session.cursor_info(seat, InputType::Pointer, geometry, offset);
}
}
let ptr = seat.get_pointer().unwrap();
ptr.motion(
2022-07-04 16:00:29 +02:00
self,
under.clone(),
2022-07-04 16:00:29 +02:00
&MotionEvent {
location: position,
serial,
time: event.time_msec(),
},
);
ptr.relative_motion(
self,
under,
&RelativeMotionEvent {
delta: event.delta(),
delta_unaccel: event.delta_unaccel(),
utime: event.time(),
2022-07-04 16:00:29 +02:00
},
);
2023-03-06 18:48:52 +01:00
#[cfg(feature = "debug")]
if self.common.seats().position(|x| x == seat).unwrap() == 0 {
let location = if let Some(output) = self.common.shell.outputs.first() {
self.common
.shell
.map_global_to_space(position, output)
.to_i32_round()
} else {
position.to_i32_round()
};
self.common.egui.state.handle_pointer_motion(location);
}
2021-12-22 20:14:09 +01:00
break;
}
}
}
InputEvent::PointerMotionAbsolute { event, .. } => {
let device = event.device();
2022-09-28 12:01:29 +02:00
for seat in self.common.seats().cloned().collect::<Vec<_>>().iter() {
2021-12-22 20:14:09 +01:00
let userdata = seat.user_data();
let devices = userdata.get::<Devices>().unwrap();
if devices.has_device(&device) {
2022-09-28 12:01:29 +02:00
let output = seat.active_output();
let geometry = output.geometry();
2022-11-17 20:32:54 +01:00
let position = geometry.loc.to_f64()
+ smithay::backend::input::AbsolutePositionEvent::position_transformed(
&event,
geometry.size,
);
2022-09-28 12:01:29 +02:00
let relative_pos = self.common.shell.map_global_to_space(position, &output);
let workspace = self.common.shell.active_space(&output);
2021-12-22 20:14:09 +01:00
let serial = SERIAL_COUNTER.next_serial();
2022-04-25 12:35:55 +02:00
let under = State::surface_under(
position,
relative_pos,
&output,
geometry,
&self.common.shell.override_redirect_windows,
2022-04-22 15:18:28 +02:00
&workspace,
);
for session in sessions_for_output(&self.common, &output) {
if let Some((geometry, offset)) = seat.cursor_geometry(
position.to_buffer(
output.current_scale().fractional_scale(),
output.current_transform(),
&output.geometry().size.to_f64(),
),
self.common.clock.now(),
) {
session.cursor_info(seat, InputType::Pointer, geometry, offset);
}
}
2022-07-04 16:00:29 +02:00
seat.get_pointer().unwrap().motion(
self,
2022-08-31 13:01:23 +02:00
under,
2022-07-04 16:00:29 +02:00
&MotionEvent {
location: position,
serial,
time: event.time_msec(),
2022-07-04 16:00:29 +02:00
},
);
2023-03-06 18:48:52 +01:00
#[cfg(feature = "debug")]
if self.common.seats().position(|x| x == seat).unwrap() == 0 {
let location = if let Some(output) = self.common.shell.outputs.first() {
self.common
.shell
.map_global_to_space(position, output)
.to_i32_round()
} else {
position.to_i32_round()
};
self.common.egui.state.handle_pointer_motion(location);
}
2022-01-11 17:22:23 +01:00
2021-12-22 20:14:09 +01:00
break;
}
}
}
InputEvent::PointerButton { event, .. } => {
2022-08-31 13:01:23 +02:00
use smithay::backend::input::{ButtonState, PointerButtonEvent};
2021-12-22 20:14:09 +01:00
let device = event.device();
2022-09-28 12:01:29 +02:00
for seat in self.common.seats().cloned().collect::<Vec<_>>().iter() {
2021-12-22 20:14:09 +01:00
let userdata = seat.user_data();
let devices = userdata.get::<Devices>().unwrap();
if devices.has_device(&device) {
2023-03-06 18:48:52 +01:00
#[cfg(feature = "debug")]
if self.common.seats().position(|x| x == seat).unwrap() == 0
&& self.common.egui.active
{
if self.common.egui.state.wants_pointer() {
if let Some(button) = event.button() {
self.common.egui.state.handle_pointer_button(
button,
event.state() == ButtonState::Pressed,
);
}
break;
}
}
2021-12-22 20:14:09 +01:00
let serial = SERIAL_COUNTER.next_serial();
let button = event.button_code();
2022-08-31 13:01:23 +02:00
if event.state() == ButtonState::Pressed {
// change the keyboard focus unless the pointer or keyboard 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()
&& !seat.get_keyboard().map(|k| k.is_grabbed()).unwrap_or(false)
{
2022-09-28 12:01:29 +02:00
let output = seat.active_output();
2022-08-31 13:01:23 +02:00
let pos = seat.get_pointer().unwrap().current_location();
2022-09-28 12:01:29 +02:00
let relative_pos =
self.common.shell.map_global_to_space(pos, &output);
2022-08-31 13:01:23 +02:00
let workspace = self.common.shell.active_space_mut(&output);
let layers = layer_map_for_output(&output);
let mut under = None;
if let Some(window) = workspace.get_fullscreen(&output) {
if let Some(layer) =
layers.layer_under(WlrLayer::Overlay, relative_pos)
{
let layer_loc = layers.layer_geometry(layer).unwrap().loc;
if layer.can_receive_keyboard_focus()
&& layer
.surface_under(
relative_pos - layer_loc.to_f64(),
WindowSurfaceType::ALL,
)
.is_some()
{
2022-09-28 12:01:29 +02:00
under = Some(layer.clone().into());
2022-04-22 15:18:28 +02:00
}
} else {
2022-09-28 12:01:29 +02:00
under = Some(window.clone().into());
2022-04-22 15:18:28 +02:00
}
2022-08-31 13:01:23 +02:00
} else {
if let Some(layer) = layers
.layer_under(WlrLayer::Overlay, relative_pos)
.or_else(|| layers.layer_under(WlrLayer::Top, relative_pos))
{
let layer_loc = layers.layer_geometry(layer).unwrap().loc;
if layer.can_receive_keyboard_focus()
&& layer
.surface_under(
relative_pos - layer_loc.to_f64(),
WindowSurfaceType::ALL,
)
.is_some()
{
2022-09-28 12:01:29 +02:00
under = Some(layer.clone().into());
2022-08-31 13:01:23 +02:00
}
2022-09-28 12:01:29 +02:00
} else if let Some((window, _)) =
workspace.element_under(relative_pos)
2022-08-31 13:01:23 +02:00
{
2022-09-28 12:01:29 +02:00
under = Some(window.clone().into());
2022-08-31 13:01:23 +02:00
} else if let Some(layer) = layers
.layer_under(WlrLayer::Bottom, pos)
.or_else(|| layers.layer_under(WlrLayer::Background, pos))
{
let layer_loc = layers.layer_geometry(layer).unwrap().loc;
if layer.can_receive_keyboard_focus()
&& layer
.surface_under(
relative_pos - layer_loc.to_f64(),
WindowSurfaceType::ALL,
)
.is_some()
{
2022-09-28 12:01:29 +02:00
under = Some(layer.clone().into());
2022-08-31 13:01:23 +02:00
}
};
2021-12-22 20:14:09 +01:00
}
2022-08-31 13:01:23 +02:00
Common::set_focus(self, under.as_ref(), seat, Some(serial));
2021-12-22 20:14:09 +01:00
}
};
2022-07-04 16:00:29 +02:00
seat.get_pointer().unwrap().button(
self,
&ButtonEvent {
button,
2022-08-31 13:01:23 +02:00
state: event.state(),
serial,
time: event.time_msec(),
2022-07-04 16:00:29 +02:00
},
);
2021-12-22 20:14:09 +01:00
break;
}
}
}
InputEvent::PointerAxis { event, .. } => {
let device = event.device();
2022-09-28 12:01:29 +02:00
for seat in self.common.seats().cloned().collect::<Vec<_>>().iter() {
2023-03-06 18:48:52 +01:00
#[cfg(feature = "debug")]
if self.common.seats().position(|x| x == seat).unwrap() == 0
&& self.common.egui.active
{
if self.common.egui.state.wants_pointer() {
self.common.egui.state.handle_pointer_axis(
event
.amount_discrete(Axis::Horizontal)
.or_else(|| event.amount(Axis::Horizontal).map(|x| x * 3.0))
.unwrap_or(0.0),
event
.amount_discrete(Axis::Vertical)
.or_else(|| event.amount(Axis::Vertical).map(|x| x * 3.0))
.unwrap_or(0.0),
);
break;
}
}
2021-12-22 20:14:09 +01:00
let userdata = seat.user_data();
let devices = userdata.get::<Devices>().unwrap();
if devices.has_device(&device) {
let horizontal_amount =
event.amount(Axis::Horizontal).unwrap_or_else(|| {
event.amount_discrete(Axis::Horizontal).unwrap_or(0.0) * 3.0
2021-12-22 20:14:09 +01:00
});
let vertical_amount = event.amount(Axis::Vertical).unwrap_or_else(|| {
event.amount_discrete(Axis::Vertical).unwrap_or(0.0) * 3.0
2021-12-22 20:14:09 +01:00
});
let horizontal_amount_discrete = event.amount_discrete(Axis::Horizontal);
let vertical_amount_discrete = event.amount_discrete(Axis::Vertical);
{
let mut frame =
AxisFrame::new(event.time_msec()).source(event.source());
2021-12-22 20:14:09 +01:00
if horizontal_amount != 0.0 {
2022-08-31 13:01:23 +02:00
frame = frame.value(Axis::Horizontal, horizontal_amount);
2021-12-22 20:14:09 +01:00
if let Some(discrete) = horizontal_amount_discrete {
2022-08-31 13:01:23 +02:00
frame = frame.discrete(Axis::Horizontal, discrete as i32);
2021-12-22 20:14:09 +01:00
}
2022-08-31 13:01:23 +02:00
} else if event.source() == AxisSource::Finger {
frame = frame.stop(Axis::Horizontal);
2021-12-22 20:14:09 +01:00
}
if vertical_amount != 0.0 {
2022-08-31 13:01:23 +02:00
frame = frame.value(Axis::Vertical, vertical_amount);
2021-12-22 20:14:09 +01:00
if let Some(discrete) = vertical_amount_discrete {
2022-08-31 13:01:23 +02:00
frame = frame.discrete(Axis::Vertical, discrete as i32);
2021-12-22 20:14:09 +01:00
}
2022-08-31 13:01:23 +02:00
} else if event.source() == AxisSource::Finger {
frame = frame.stop(Axis::Vertical);
2021-12-22 20:14:09 +01:00
}
2022-08-31 13:01:23 +02:00
seat.get_pointer().unwrap().axis(self, frame);
2021-12-22 20:14:09 +01:00
}
break;
}
}
}
_ => { /* TODO e.g. tablet or touch events */ }
}
}
fn handle_action(
&mut self,
action: Action,
seat: &Seat<State>,
serial: Serial,
time: u32,
mods: KeyModifiers,
direction: Option<Direction>,
) {
2022-11-10 18:42:11 +01:00
match action {
Action::Terminate => {
self.common.should_stop = true;
}
#[cfg(feature = "debug")]
Action::Debug => {
self.common.egui.active = !self.common.egui.active;
puffin::set_scopes_on(self.common.egui.active);
for mapped in self
.common
.shell
.workspaces
.spaces()
.flat_map(|w| w.mapped())
{
mapped.set_debug(self.common.egui.active);
}
2022-11-10 18:42:11 +01:00
}
#[cfg(not(feature = "debug"))]
Action::Debug => {
info!("Debug overlay not included in this build.")
2022-11-10 18:42:11 +01:00
}
Action::Close => {
let current_output = seat.active_output();
let workspace = self.common.shell.active_space_mut(&current_output);
if let Some(window) = workspace.focus_stack.get(seat).last() {
window.send_close();
}
}
Action::Workspace(key_num) => {
let current_output = seat.active_output();
let workspace = match key_num {
0 => 9,
x => x - 1,
};
2022-11-22 10:10:08 +01:00
let _ = self
2022-11-10 18:42:11 +01:00
.common
.shell
2022-11-22 10:10:08 +01:00
.activate(&current_output, workspace as usize);
2022-11-10 18:42:11 +01:00
}
Action::NextWorkspace => {
let current_output = seat.active_output();
let workspace = self
.common
.shell
.workspaces
.active_num(&current_output)
.1
2022-11-10 18:42:11 +01:00
.saturating_add(1);
2023-05-25 00:10:24 +02:00
if self
.common
.shell
.activate(&current_output, workspace)
.is_err()
{
self.handle_action(Action::NextOutput, seat, serial, time, mods, direction);
2023-05-25 00:10:24 +02:00
}
2022-11-10 18:42:11 +01:00
}
Action::PreviousWorkspace => {
let current_output = seat.active_output();
let workspace = self
.common
.shell
.workspaces
.active_num(&current_output)
.1
2022-11-10 18:42:11 +01:00
.saturating_sub(1);
2023-05-25 00:10:24 +02:00
if self
.common
.shell
.activate(&current_output, workspace)
.is_err()
{
self.handle_action(Action::PreviousOutput, seat, serial, time, mods, direction);
2023-05-25 00:10:24 +02:00
}
2022-11-10 18:42:11 +01:00
}
Action::LastWorkspace => {
let current_output = seat.active_output();
let workspace = self
.common
.shell
.workspaces
.len(&current_output)
.saturating_sub(1);
2022-11-22 10:10:08 +01:00
let _ = self.common.shell.activate(&current_output, workspace);
2022-11-10 18:42:11 +01:00
}
2023-01-24 19:22:00 +01:00
x @ Action::MoveToWorkspace(_) | x @ Action::SendToWorkspace(_) => {
2022-11-10 18:42:11 +01:00
let current_output = seat.active_output();
2023-01-24 19:22:00 +01:00
let follow = matches!(x, Action::MoveToWorkspace(_));
let workspace = match x {
Action::MoveToWorkspace(0) | Action::SendToWorkspace(0) => 9,
Action::MoveToWorkspace(x) | Action::SendToWorkspace(x) => x - 1,
_ => unreachable!(),
2022-11-10 18:42:11 +01:00
};
2023-05-25 00:10:24 +02:00
let _ = Shell::move_current_window(
2022-11-10 18:42:11 +01:00
self,
seat,
&current_output,
(&current_output, Some(workspace as usize)),
2023-01-24 19:22:00 +01:00
follow,
None,
2022-11-10 18:42:11 +01:00
);
}
2023-01-24 19:22:00 +01:00
x @ Action::MoveToNextWorkspace | x @ Action::SendToNextWorkspace => {
2022-11-10 18:42:11 +01:00
let current_output = seat.active_output();
let workspace = self
.common
.shell
.workspaces
.active_num(&current_output)
.1
2022-11-10 18:42:11 +01:00
.saturating_add(1);
2023-05-25 00:10:24 +02:00
if Shell::move_current_window(
2022-11-10 18:42:11 +01:00
self,
seat,
&current_output,
(&current_output, Some(workspace as usize)),
2023-01-24 19:22:00 +01:00
matches!(x, Action::MoveToNextWorkspace),
direction,
2023-05-25 00:10:24 +02:00
)
.is_err()
{
self.handle_action(
if matches!(x, Action::MoveToNextWorkspace) {
Action::MoveToNextOutput
} else {
Action::SendToNextOutput
},
seat,
serial,
time,
mods,
direction,
2023-05-25 00:10:24 +02:00
)
}
2022-11-10 18:42:11 +01:00
}
2023-01-24 19:22:00 +01:00
x @ Action::MoveToPreviousWorkspace | x @ Action::SendToPreviousWorkspace => {
2022-11-10 18:42:11 +01:00
let current_output = seat.active_output();
let workspace = self
.common
.shell
.workspaces
.active_num(&current_output)
.1
2022-11-10 18:42:11 +01:00
.saturating_sub(1);
// TODO: Possibly move to prev output, if idx < 0
2023-05-25 00:10:24 +02:00
if Shell::move_current_window(
2022-11-10 18:42:11 +01:00
self,
seat,
&current_output,
(&current_output, Some(workspace as usize)),
2023-01-24 19:22:00 +01:00
matches!(x, Action::MoveToPreviousWorkspace),
direction,
2023-05-25 00:10:24 +02:00
)
.is_err()
{
self.handle_action(
if matches!(x, Action::MoveToNextWorkspace) {
Action::MoveToPreviousOutput
} else {
Action::SendToPreviousOutput
},
seat,
serial,
time,
mods,
direction,
2023-05-25 00:10:24 +02:00
)
}
2022-11-10 18:42:11 +01:00
}
2023-01-24 19:22:00 +01:00
x @ Action::MoveToLastWorkspace | x @ Action::SendToLastWorkspace => {
2022-11-10 18:42:11 +01:00
let current_output = seat.active_output();
let workspace = self
.common
.shell
.workspaces
.len(&current_output)
.saturating_sub(1);
2023-05-25 00:10:24 +02:00
let _ = Shell::move_current_window(
2022-11-10 18:42:11 +01:00
self,
seat,
&current_output,
(&current_output, Some(workspace as usize)),
2023-01-24 19:22:00 +01:00
matches!(x, Action::MoveToLastWorkspace),
None,
2022-11-10 18:42:11 +01:00
);
}
Action::NextOutput => {
let current_output = seat.active_output();
if let Some(next_output) = self
.common
.shell
.outputs
.iter()
.skip_while(|o| *o != &current_output)
.skip(1)
.next()
.cloned()
{
let idx = self.common.shell.workspaces.active_num(&next_output).1;
2023-05-25 00:10:24 +02:00
if let Ok(Some(new_pos)) = self.common.shell.activate(&next_output, idx) {
2022-11-22 10:10:08 +01:00
seat.set_active_output(&next_output);
2022-11-10 18:42:11 +01:00
if let Some(ptr) = seat.get_pointer() {
2022-11-22 10:10:08 +01:00
ptr.motion(
self,
None,
&MotionEvent {
location: new_pos.to_f64(),
serial,
time,
},
);
2022-11-10 18:42:11 +01:00
}
}
}
}
Action::PreviousOutput => {
let current_output = seat.active_output();
if let Some(prev_output) = self
.common
.shell
.outputs
.iter()
.rev()
.skip_while(|o| *o != &current_output)
.skip(1)
.next()
.cloned()
{
let idx = self.common.shell.workspaces.active_num(&prev_output).1;
2023-05-25 00:10:24 +02:00
if let Ok(Some(new_pos)) = self.common.shell.activate(&prev_output, idx) {
2022-11-22 10:10:08 +01:00
seat.set_active_output(&prev_output);
2022-11-10 18:42:11 +01:00
if let Some(ptr) = seat.get_pointer() {
2022-11-22 10:10:08 +01:00
ptr.motion(
self,
None,
&MotionEvent {
location: new_pos.to_f64(),
serial,
time,
},
);
2022-11-10 18:42:11 +01:00
}
}
}
}
2023-05-25 00:10:24 +02:00
x @ Action::MoveToNextOutput | x @ Action::SendToNextOutput => {
2022-11-10 18:42:11 +01:00
let current_output = seat.active_output();
if let Some(next_output) = self
.common
.shell
.outputs
.iter()
.skip_while(|o| *o != &current_output)
.skip(1)
.next()
.cloned()
{
2023-05-25 00:10:24 +02:00
if let Ok(Some(new_pos)) = Shell::move_current_window(
2022-11-22 10:10:08 +01:00
self,
seat,
&current_output,
(&next_output, None),
2023-05-25 00:10:24 +02:00
matches!(x, Action::MoveToNextOutput),
direction,
2022-11-22 10:10:08 +01:00
) {
if let Some(ptr) = seat.get_pointer() {
ptr.motion(
self,
None,
&MotionEvent {
location: new_pos.to_f64(),
serial,
time,
},
);
}
}
2022-11-10 18:42:11 +01:00
}
}
2023-05-25 00:10:24 +02:00
x @ Action::MoveToPreviousOutput | x @ Action::SendToPreviousOutput => {
2022-11-10 18:42:11 +01:00
let current_output = seat.active_output();
if let Some(prev_output) = self
.common
.shell
.outputs
.iter()
.rev()
.skip_while(|o| *o != &current_output)
.skip(1)
.next()
.cloned()
{
2023-05-25 00:10:24 +02:00
if let Ok(Some(new_pos)) = Shell::move_current_window(
2022-11-22 10:10:08 +01:00
self,
seat,
&current_output,
(&prev_output, None),
2023-05-25 00:10:24 +02:00
matches!(x, Action::MoveToPreviousOutput),
direction,
2022-11-22 10:10:08 +01:00
) {
if let Some(ptr) = seat.get_pointer() {
ptr.motion(
self,
None,
&MotionEvent {
location: new_pos.to_f64(),
serial,
time,
},
);
}
}
2022-11-10 18:42:11 +01:00
}
}
Action::Focus(focus) => {
let current_output = seat.active_output();
let workspace = self.common.shell.active_space_mut(&current_output);
let focus_stack = workspace.focus_stack.get(seat);
let mut result = workspace
2022-11-10 18:42:11 +01:00
.tiling_layer
.next_focus(focus, seat, focus_stack.iter());
if workspace.get_fullscreen(&current_output).is_some() {
result = FocusResult::None;
}
match result {
2022-11-10 18:42:11 +01:00
FocusResult::None => {
2023-05-25 00:10:24 +02:00
match (focus, self.common.config.static_conf.workspace_layout) {
(FocusDirection::Left, WorkspaceLayout::Horizontal)
| (FocusDirection::Up, WorkspaceLayout::Vertical) => self
.handle_action(
Action::PreviousWorkspace,
seat,
serial,
time,
mods,
direction,
),
2023-05-25 00:10:24 +02:00
(FocusDirection::Right, WorkspaceLayout::Horizontal)
| (FocusDirection::Down, WorkspaceLayout::Vertical) => self
.handle_action(
Action::NextWorkspace,
seat,
serial,
time,
mods,
direction,
),
2023-05-25 00:10:24 +02:00
(FocusDirection::Left, WorkspaceLayout::Vertical)
| (FocusDirection::Up, WorkspaceLayout::Horizontal) => self
.handle_action(
Action::PreviousOutput,
seat,
serial,
time,
mods,
direction,
),
2023-05-25 00:10:24 +02:00
(FocusDirection::Right, WorkspaceLayout::Vertical)
| (FocusDirection::Down, WorkspaceLayout::Horizontal) => self
.handle_action(
Action::NextOutput,
seat,
serial,
time,
mods,
direction,
),
2022-11-10 18:42:11 +01:00
_ => {}
}
}
FocusResult::Handled => {}
FocusResult::Some(target) => {
std::mem::drop(focus_stack);
Common::set_focus(self, Some(&target), seat, None);
}
}
}
Action::Move(direction) => {
let current_output = seat.active_output();
let workspace = self.common.shell.active_space_mut(&current_output);
if workspace.get_fullscreen(&current_output).is_some() {
return; // TODO, is this what we want? How do we indicate the switch?
}
2022-11-10 18:42:11 +01:00
if let Some(_move_further) =
workspace.tiling_layer.move_current_window(direction, seat)
{
// TODO: Being able to move Groups (move_further should be KeyboardFocusTarget instead)
2023-05-25 00:10:24 +02:00
match (direction, self.common.config.static_conf.workspace_layout) {
(Direction::Left, WorkspaceLayout::Horizontal)
| (Direction::Up, WorkspaceLayout::Vertical) => self.handle_action(
Action::MoveToPreviousWorkspace,
seat,
serial,
time,
mods,
Some(direction),
),
2023-05-25 00:10:24 +02:00
(Direction::Right, WorkspaceLayout::Horizontal)
| (Direction::Down, WorkspaceLayout::Vertical) => self.handle_action(
Action::MoveToNextWorkspace,
seat,
serial,
time,
mods,
Some(direction),
),
2023-05-25 00:10:24 +02:00
(Direction::Left, WorkspaceLayout::Vertical)
| (Direction::Up, WorkspaceLayout::Horizontal) => self.handle_action(
Action::MoveToPreviousOutput,
seat,
serial,
time,
mods,
Some(direction),
),
2023-05-25 00:10:24 +02:00
(Direction::Right, WorkspaceLayout::Vertical)
| (Direction::Down, WorkspaceLayout::Horizontal) => self.handle_action(
Action::MoveToNextOutput,
seat,
serial,
time,
mods,
Some(direction),
),
}
} else {
let focus_stack = workspace.focus_stack.get(seat);
if let Some(focused_window) = focus_stack.last() {
if workspace.is_tiled(focused_window) {
self.common.shell.set_overview_mode(Some(mods));
2022-11-10 18:42:11 +01:00
}
}
}
}
Action::Maximize => {
let current_output = seat.active_output();
let workspace = self.common.shell.active_space_mut(&current_output);
let focus_stack = workspace.focus_stack.get(seat);
let focused_window = focus_stack.last();
if let Some(window) = focused_window.map(|f| f.active_window()) {
workspace.maximize_toggle(&window, &current_output);
}
}
Action::ToggleOrientation => {
let output = seat.active_output();
let workspace = self.common.shell.active_space_mut(&output);
let focus_stack = workspace.focus_stack.get(seat);
workspace
.tiling_layer
.update_orientation(None, &seat, focus_stack.iter());
}
Action::Orientation(orientation) => {
let output = seat.active_output();
let workspace = self.common.shell.active_space_mut(&output);
let focus_stack = workspace.focus_stack.get(seat);
workspace.tiling_layer.update_orientation(
Some(orientation),
&seat,
focus_stack.iter(),
);
}
Action::ToggleTiling => {
let output = seat.active_output();
let workspace = self.common.shell.active_space_mut(&output);
workspace.toggle_tiling(seat);
}
Action::ToggleWindowFloating => {
let output = seat.active_output();
let workspace = self.common.shell.active_space_mut(&output);
workspace.toggle_floating_window(seat);
}
Action::Spawn(command) => {
let wayland_display = self.common.socket.clone();
let display = self
.common
.xwayland_state
.as_ref()
.map(|s| format!(":{}", s.display))
.unwrap_or_default();
std::thread::spawn(move || {
let mut cmd = std::process::Command::new("/bin/sh");
cmd.arg("-c")
.arg(command.clone())
.env("WAYLAND_DISPLAY", &wayland_display)
.env("DISPLAY", &display)
.env_remove("COSMIC_SESSION_SOCK");
match cmd.spawn() {
Ok(mut child) => {
let _res = child.wait();
}
Err(err) => {
tracing::warn!(?err, "Failed to spawn \"{}\"", command);
}
}
});
2022-11-10 18:42:11 +01:00
}
}
}
2021-12-22 20:14:09 +01:00
pub fn surface_under(
global_pos: Point<f64, Logical>,
relative_pos: Point<f64, Logical>,
2021-12-22 20:14:09 +01:00
output: &Output,
output_geo: Rectangle<i32, Logical>,
override_redirect_windows: &[X11Surface],
2022-04-22 15:18:28 +02:00
workspace: &Workspace,
2022-09-28 12:01:29 +02:00
) -> Option<(PointerFocusTarget, Point<i32, Logical>)> {
2021-12-22 20:14:09 +01:00
let layers = layer_map_for_output(output);
2022-04-22 15:18:28 +02:00
if let Some(window) = workspace.get_fullscreen(output) {
if let Some(layer) = layers.layer_under(WlrLayer::Overlay, relative_pos) {
2022-04-22 15:18:28 +02:00
let layer_loc = layers.layer_geometry(layer).unwrap().loc;
if layer
.surface_under(relative_pos - layer_loc.to_f64(), WindowSurfaceType::ALL)
.is_some()
{
return Some((layer.clone().into(), output_geo.loc + layer_loc));
}
2022-04-22 15:18:28 +02:00
}
if let Some(or) = override_redirect_windows
.iter()
.find(|or| or.is_in_input_region(&(global_pos - or.geometry().loc.to_f64())))
{
return Some((or.clone().into(), or.geometry().loc));
}
Some((window.clone().into(), output_geo.loc))
2021-12-22 20:14:09 +01:00
} else {
2022-04-22 15:18:28 +02:00
if let Some(layer) = layers
.layer_under(WlrLayer::Overlay, relative_pos)
.or_else(|| layers.layer_under(WlrLayer::Top, relative_pos))
{
let layer_loc = layers.layer_geometry(layer).unwrap().loc;
if layer
.surface_under(relative_pos - layer_loc.to_f64(), WindowSurfaceType::ALL)
.is_some()
{
return Some((layer.clone().into(), output_geo.loc + layer_loc));
}
}
if let Some(or) = override_redirect_windows
.iter()
.find(|or| or.is_in_input_region(&(global_pos - or.geometry().loc.to_f64())))
{
return Some((or.clone().into(), or.geometry().loc));
}
if let Some((mapped, loc)) = workspace.element_under(relative_pos) {
return Some((
2022-09-28 12:01:29 +02:00
mapped.clone().into(),
loc + (global_pos - relative_pos).to_i32_round(),
));
}
if let Some(layer) = layers
2022-04-22 15:18:28 +02:00
.layer_under(WlrLayer::Bottom, relative_pos)
.or_else(|| layers.layer_under(WlrLayer::Background, relative_pos))
{
let layer_loc = layers.layer_geometry(layer).unwrap().loc;
if layer
.surface_under(relative_pos - layer_loc.to_f64(), WindowSurfaceType::ALL)
.is_some()
{
return Some((layer.clone().into(), output_geo.loc + layer_loc));
}
2022-04-22 15:18:28 +02:00
}
None
2021-12-22 20:14:09 +01:00
}
}
}
2022-11-03 18:51:27 +01:00
fn sessions_for_output(state: &Common, output: &Output) -> impl Iterator<Item = Session> {
let workspace = state.shell.active_space(&output);
let maybe_fullscreen = workspace.get_fullscreen(&output);
workspace
.screencopy_sessions
.iter()
.map(|s| (&**s).clone())
.chain(
maybe_fullscreen
.and_then(|w| w.user_data().get::<ScreencopySessions>())
.map(|sessions| {
sessions
.0
.borrow()
.iter()
.map(|s| (&**s).clone())
.collect::<Vec<_>>()
})
.into_iter()
.flatten(),
)
.chain(
output
.user_data()
.get::<ScreencopySessions>()
.map(|sessions| {
sessions
.0
.borrow()
.iter()
.map(|s| (&**s).clone())
.collect::<Vec<_>>()
})
.into_iter()
.into_iter()
.flatten(),
)
.collect::<Vec<_>>()
.into_iter()
}