🔒 Repository is read-only – file editing is disabled.
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329
use std::{convert::TryInto, process::Command, sync::atomic::Ordering};
use crate::{focus::PointerFocusTarget, shell::FullscreenSurface, AnvilState};
#[cfg(feature = "udev")]
use crate::udev::UdevData;
#[cfg(feature = "udev")]
use smithay::backend::renderer::DebugFlags;
use smithay::{
backend::input::{
self, Axis, AxisSource, Event, InputBackend, InputEvent, KeyState, KeyboardKeyEvent,
PointerAxisEvent, PointerButtonEvent,
},
desktop::{layer_map_for_output, WindowSurfaceType},
input::{
keyboard::{keysyms as xkb, FilterResult, Keysym, ModifiersState},
pointer::{AxisFrame, ButtonEvent, MotionEvent},
},
output::Scale,
reexports::{
wayland_protocols::xdg::decoration::zv1::server::zxdg_toplevel_decoration_v1,
wayland_server::protocol::wl_pointer,
},
utils::{Logical, Point, Serial, Transform, SERIAL_COUNTER as SCOUNTER},
wayland::{
compositor::with_states,
input_method::InputMethodSeat,
keyboard_shortcuts_inhibit::KeyboardShortcutsInhibitorSeat,
shell::wlr_layer::{KeyboardInteractivity, Layer as WlrLayer, LayerSurfaceCachedState},
},
};
#[cfg(any(feature = "winit", feature = "x11", feature = "udev"))]
use smithay::backend::input::AbsolutePositionEvent;
#[cfg(any(feature = "winit", feature = "x11"))]
use smithay::output::Output;
use tracing::{debug, error, info};
use crate::state::Backend;
#[cfg(feature = "udev")]
use smithay::{
backend::{
input::{
Device, DeviceCapability, GestureBeginEvent, GestureEndEvent, GesturePinchUpdateEvent as _,
GestureSwipeUpdateEvent as _, PointerMotionEvent, ProximityState, TabletToolButtonEvent,
TabletToolEvent, TabletToolProximityEvent, TabletToolTipEvent, TabletToolTipState, TouchEvent,
},
session::Session,
},
input::{
pointer::{
GestureHoldBeginEvent, GestureHoldEndEvent, GesturePinchBeginEvent, GesturePinchEndEvent,
GesturePinchUpdateEvent, GestureSwipeBeginEvent, GestureSwipeEndEvent, GestureSwipeUpdateEvent,
RelativeMotionEvent,
},
touch::{DownEvent, UpEvent},
},
reexports::wayland_server::DisplayHandle,
wayland::{
pointer_constraints::{with_pointer_constraint, PointerConstraint},
seat::WaylandFocus,
tablet_manager::{TabletDescriptor, TabletSeatTrait},
},
};
impl<BackendData: Backend> AnvilState<BackendData> {
fn process_common_key_action(&mut self, action: KeyAction) {
match action {
KeyAction::None => (),
KeyAction::Quit => {
info!("Quitting.");
self.running.store(false, Ordering::SeqCst);
}
KeyAction::Run(cmd) => {
info!(cmd, "Starting program");
if let Err(e) = Command::new(&cmd)
.envs(
self.socket_name
.clone()
.map(|v| ("WAYLAND_DISPLAY", v))
.into_iter()
.chain(
#[cfg(feature = "xwayland")]
self.xdisplay.map(|v| ("DISPLAY", format!(":{}", v))),
#[cfg(not(feature = "xwayland"))]
None,
),
)
.spawn()
{
error!(cmd, err = %e, "Failed to start program");
}
}
KeyAction::TogglePreview => {
self.show_window_preview = !self.show_window_preview;
}
KeyAction::ToggleDecorations => {
for element in self.space.elements() {
#[allow(irrefutable_let_patterns)]
if let Some(toplevel) = element.0.toplevel() {
let mode_changed = toplevel.with_pending_state(|state| {
if let Some(current_mode) = state.decoration_mode {
let new_mode =
if current_mode == zxdg_toplevel_decoration_v1::Mode::ClientSide {
zxdg_toplevel_decoration_v1::Mode::ServerSide
} else {
zxdg_toplevel_decoration_v1::Mode::ClientSide
};
state.decoration_mode = Some(new_mode);
true
} else {
false
}
});
if mode_changed && toplevel.is_initial_configure_sent() {
toplevel.send_pending_configure();
}
}
}
}
_ => unreachable!(
"Common key action handler encountered backend specific action {:?}",
action
),
}
}
fn keyboard_key_to_action<B: InputBackend>(&mut self, evt: B::KeyboardKeyEvent) -> KeyAction {
let keycode = evt.key_code();
let state = evt.state();
debug!(?keycode, ?state, "key");
let serial = SCOUNTER.next_serial();
let time = Event::time_msec(&evt);
let mut suppressed_keys = self.suppressed_keys.clone();
let keyboard = self.seat.get_keyboard().unwrap();
for layer in self.layer_shell_state.layer_surfaces().rev() {
let data = with_states(layer.wl_surface(), |states| {
*states.cached_state.get::<LayerSurfaceCachedState>().current()
});
if data.keyboard_interactivity == KeyboardInteractivity::Exclusive
&& (data.layer == WlrLayer::Top || data.layer == WlrLayer::Overlay)
{
let surface = self.space.outputs().find_map(|o| {
let map = layer_map_for_output(o);
let cloned = map.layers().find(|l| l.layer_surface() == &layer).cloned();
cloned
});
if let Some(surface) = surface {
keyboard.set_focus(self, Some(surface.into()), serial);
keyboard.input::<(), _>(self, keycode, state, serial, time, |_, _, _| {
FilterResult::Forward
});
return KeyAction::None;
};
}
}
let inhibited = self
.space
.element_under(self.pointer.current_location())
.and_then(|(window, _)| {
let surface = window.wl_surface()?;
self.seat.keyboard_shortcuts_inhibitor_for_surface(&surface)
})
.map(|inhibitor| inhibitor.is_active())
.unwrap_or(false);
let action = keyboard
.input(self, keycode, state, serial, time, |_, modifiers, handle| {
let keysym = handle.modified_sym();
debug!(
?state,
mods = ?modifiers,
keysym = ::xkbcommon::xkb::keysym_get_name(keysym),
"keysym"
);
// If the key is pressed and triggered a action
// we will not forward the key to the client.
// Additionally add the key to the suppressed keys
// so that we can decide on a release if the key
// should be forwarded to the client or not.
if let KeyState::Pressed = state {
if !inhibited {
let action = process_keyboard_shortcut(*modifiers, keysym);
if action.is_some() {
suppressed_keys.push(keysym);
}
action
.map(FilterResult::Intercept)
.unwrap_or(FilterResult::Forward)
} else {
FilterResult::Forward
}
} else {
let suppressed = suppressed_keys.contains(&keysym);
if suppressed {
suppressed_keys.retain(|k| *k != keysym);
FilterResult::Intercept(KeyAction::None)
} else {
FilterResult::Forward
}
}
})
.unwrap_or(KeyAction::None);
self.suppressed_keys = suppressed_keys;
action
}
fn on_pointer_button<B: InputBackend>(&mut self, evt: B::PointerButtonEvent) {
let serial = SCOUNTER.next_serial();
let button = evt.button_code();
let state = wl_pointer::ButtonState::from(evt.state());
if wl_pointer::ButtonState::Pressed == state {
self.update_keyboard_focus(self.pointer.current_location(), serial);
};
let pointer = self.pointer.clone();
pointer.button(
self,
&ButtonEvent {
button,
state: state.try_into().unwrap(),
serial,
time: evt.time_msec(),
},
);
pointer.frame(self);
}
fn update_keyboard_focus(&mut self, location: Point<f64, Logical>, serial: Serial) {
let keyboard = self.seat.get_keyboard().unwrap();
let touch = self.seat.get_touch();
let input_method = self.seat.input_method();
// 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.
// So for example if a user clicks on a subsurface or popup the toplevel
// will receive the keyboard focus. Directly assigning the focus to the
// matching surface leads to issues with clients dismissing popups and
// subsurface menus (for example firefox-wayland).
// see here for a discussion about that issue:
// https://gitlab.freedesktop.org/wayland/wayland/-/issues/294
if !self.pointer.is_grabbed()
&& (!keyboard.is_grabbed() || input_method.keyboard_grabbed())
&& !touch.map(|touch| touch.is_grabbed()).unwrap_or(false)
{
let output = self.space.output_under(location).next().cloned();
if let Some(output) = output.as_ref() {
let output_geo = self.space.output_geometry(output).unwrap();
if let Some(window) = output
.user_data()
.get::<FullscreenSurface>()
.and_then(|f| f.get())
{
if let Some((_, _)) =
window.surface_under(location - output_geo.loc.to_f64(), WindowSurfaceType::ALL)
{
#[cfg(feature = "xwayland")]
if let Some(surface) = window.0.x11_surface() {
self.xwm.as_mut().unwrap().raise_window(surface).unwrap();
}
keyboard.set_focus(self, Some(window.into()), serial);
return;
}
}
let layers = layer_map_for_output(output);
if let Some(layer) = layers
.layer_under(WlrLayer::Overlay, location - output_geo.loc.to_f64())
.or_else(|| layers.layer_under(WlrLayer::Top, location - output_geo.loc.to_f64()))
{
if layer.can_receive_keyboard_focus() {
if let Some((_, _)) = layer.surface_under(
location
- output_geo.loc.to_f64()
- layers.layer_geometry(layer).unwrap().loc.to_f64(),
WindowSurfaceType::ALL,
) {
keyboard.set_focus(self, Some(layer.clone().into()), serial);
return;
}
}
}
}
if let Some((window, _)) = self.space.element_under(location).map(|(w, p)| (w.clone(), p)) {
self.space.raise_element(&window, true);
#[cfg(feature = "xwayland")]
if let Some(surface) = window.0.x11_surface() {
self.xwm.as_mut().unwrap().raise_window(surface).unwrap();
}
keyboard.set_focus(self, Some(window.into()), serial);
return;
}
if let Some(output) = output.as_ref() {
let output_geo = self.space.output_geometry(output).unwrap();
let layers = layer_map_for_output(output);
if let Some(layer) = layers
.layer_under(WlrLayer::Bottom, location - output_geo.loc.to_f64())
.or_else(|| layers.layer_under(WlrLayer::Background, location - output_geo.loc.to_f64()))
{
if layer.can_receive_keyboard_focus() {
if let Some((_, _)) = layer.surface_under(
location
- output_geo.loc.to_f64()
- layers.layer_geometry(layer).unwrap().loc.to_f64(),
WindowSurfaceType::ALL,
) {
keyboard.set_focus(self, Some(layer.clone().into()), serial);
}
}
}
};
}
}
pub fn surface_under(
&self,
pos: Point<f64, Logical>,
) -> Option<(PointerFocusTarget, Point<f64, Logical>)> {
let output = self.space.outputs().find(|o| {
let geometry = self.space.output_geometry(o).unwrap();
geometry.contains(pos.to_i32_round())
})?;
let output_geo = self.space.output_geometry(output).unwrap();
let layers = layer_map_for_output(output);
let mut under = None;
if let Some((surface, loc)) = output
.user_data()
.get::<FullscreenSurface>()
.and_then(|f| f.get())
.and_then(|w| w.surface_under(pos - output_geo.loc.to_f64(), WindowSurfaceType::ALL))
{
under = Some((surface, loc + output_geo.loc));
} else if let Some(focus) = layers
.layer_under(WlrLayer::Overlay, pos - output_geo.loc.to_f64())
.or_else(|| layers.layer_under(WlrLayer::Top, pos - output_geo.loc.to_f64()))
.and_then(|layer| {
let layer_loc = layers.layer_geometry(layer).unwrap().loc;
layer
.surface_under(
pos - output_geo.loc.to_f64() - layer_loc.to_f64(),
WindowSurfaceType::ALL,
)
.map(|(surface, loc)| {
(
PointerFocusTarget::from(surface),
loc + layer_loc + output_geo.loc,
)
})
})
{
under = Some(focus)
} else if let Some(focus) = self.space.element_under(pos).and_then(|(window, loc)| {
window
.surface_under(pos - loc.to_f64(), WindowSurfaceType::ALL)
.map(|(surface, surf_loc)| (surface, surf_loc + loc))
}) {
under = Some(focus);
} else if let Some(focus) = layers
.layer_under(WlrLayer::Bottom, pos - output_geo.loc.to_f64())
.or_else(|| layers.layer_under(WlrLayer::Background, pos - output_geo.loc.to_f64()))
.and_then(|layer| {
let layer_loc = layers.layer_geometry(layer).unwrap().loc;
layer
.surface_under(
pos - output_geo.loc.to_f64() - layer_loc.to_f64(),
WindowSurfaceType::ALL,
)
.map(|(surface, loc)| {
(
PointerFocusTarget::from(surface),
loc + layer_loc + output_geo.loc,
)
})
})
{
under = Some(focus)
};
under.map(|(s, l)| (s, l.to_f64()))
}
fn on_pointer_axis<B: InputBackend>(&mut self, evt: B::PointerAxisEvent) {
let horizontal_amount = evt
.amount(input::Axis::Horizontal)
.unwrap_or_else(|| evt.amount_v120(input::Axis::Horizontal).unwrap_or(0.0) * 15.0 / 120.);
let vertical_amount = evt
.amount(input::Axis::Vertical)
.unwrap_or_else(|| evt.amount_v120(input::Axis::Vertical).unwrap_or(0.0) * 15.0 / 120.);
let horizontal_amount_discrete = evt.amount_v120(input::Axis::Horizontal);
let vertical_amount_discrete = evt.amount_v120(input::Axis::Vertical);
{
let mut frame = AxisFrame::new(evt.time_msec()).source(evt.source());
if horizontal_amount != 0.0 {
frame = frame.relative_direction(Axis::Horizontal, evt.relative_direction(Axis::Horizontal));
frame = frame.value(Axis::Horizontal, horizontal_amount);
if let Some(discrete) = horizontal_amount_discrete {
frame = frame.v120(Axis::Horizontal, discrete as i32);
}
}
if vertical_amount != 0.0 {
frame = frame.relative_direction(Axis::Vertical, evt.relative_direction(Axis::Vertical));
frame = frame.value(Axis::Vertical, vertical_amount);
if let Some(discrete) = vertical_amount_discrete {
frame = frame.v120(Axis::Vertical, discrete as i32);
}
}
if evt.source() == AxisSource::Finger {
if evt.amount(Axis::Horizontal) == Some(0.0) {
frame = frame.stop(Axis::Horizontal);
}
if evt.amount(Axis::Vertical) == Some(0.0) {
frame = frame.stop(Axis::Vertical);
}
}
let pointer = self.pointer.clone();
pointer.axis(self, frame);
pointer.frame(self);
}
}
}
#[cfg(any(feature = "winit", feature = "x11"))]
impl<BackendData: Backend> AnvilState<BackendData> {
pub fn process_input_event_windowed<B: InputBackend>(&mut self, event: InputEvent<B>, output_name: &str) {
match event {
InputEvent::Keyboard { event } => match self.keyboard_key_to_action::<B>(event) {
KeyAction::ScaleUp => {
let output = self
.space
.outputs()
.find(|o| o.name() == output_name)
.unwrap()
.clone();
let current_scale = output.current_scale().fractional_scale();
let new_scale = current_scale + 0.25;
output.change_current_state(None, None, Some(Scale::Fractional(new_scale)), None);
crate::shell::fixup_positions(&mut self.space, self.pointer.current_location());
self.backend_data.reset_buffers(&output);
}
KeyAction::ScaleDown => {
let output = self
.space
.outputs()
.find(|o| o.name() == output_name)
.unwrap()
.clone();
let current_scale = output.current_scale().fractional_scale();
let new_scale = f64::max(1.0, current_scale - 0.25);
output.change_current_state(None, None, Some(Scale::Fractional(new_scale)), None);
crate::shell::fixup_positions(&mut self.space, self.pointer.current_location());
self.backend_data.reset_buffers(&output);
}
KeyAction::RotateOutput => {
let output = self
.space
.outputs()
.find(|o| o.name() == output_name)
.unwrap()
.clone();
let current_transform = output.current_transform();
let new_transform = match current_transform {
Transform::Normal => Transform::_90,
Transform::_90 => Transform::_180,
Transform::_180 => Transform::_270,
Transform::_270 => Transform::Flipped,
Transform::Flipped => Transform::Flipped90,
Transform::Flipped90 => Transform::Flipped180,
Transform::Flipped180 => Transform::Flipped270,
Transform::Flipped270 => Transform::Normal,
};
tracing::info!(?current_transform, ?new_transform, output = ?output.name(), "changing output transform");
output.change_current_state(None, Some(new_transform), None, None);
crate::shell::fixup_positions(&mut self.space, self.pointer.current_location());
self.backend_data.reset_buffers(&output);
}
action => match action {
KeyAction::None
| KeyAction::Quit
| KeyAction::Run(_)
| KeyAction::TogglePreview
| KeyAction::ToggleDecorations => self.process_common_key_action(action),
_ => tracing::warn!(
?action,
output_name,
"Key action unsupported on on output backend.",
),
},
},
InputEvent::PointerMotionAbsolute { event } => {
let output = self
.space
.outputs()
.find(|o| o.name() == output_name)
.unwrap()
.clone();
self.on_pointer_move_absolute_windowed::<B>(event, &output)
}
InputEvent::PointerButton { event } => self.on_pointer_button::<B>(event),
InputEvent::PointerAxis { event } => self.on_pointer_axis::<B>(event),
_ => (), // other events are not handled in anvil (yet)
}
}
fn on_pointer_move_absolute_windowed<B: InputBackend>(
&mut self,
evt: B::PointerMotionAbsoluteEvent,
output: &Output,
) {
let output_geo = self.space.output_geometry(output).unwrap();
let pos = evt.position_transformed(output_geo.size) + output_geo.loc.to_f64();
let serial = SCOUNTER.next_serial();
let pointer = self.pointer.clone();
let under = self.surface_under(pos);
pointer.motion(
self,
under,
&MotionEvent {
location: pos,
serial,
time: evt.time_msec(),
},
);
pointer.frame(self);
}
pub fn release_all_keys(&mut self) {
let keyboard = self.seat.get_keyboard().unwrap();
for keycode in keyboard.pressed_keys() {
keyboard.input(
self,
keycode,
KeyState::Released,
SCOUNTER.next_serial(),
0,
|_, _, _| FilterResult::Forward::<bool>,
);
}
}
}
#[cfg(feature = "udev")]
impl AnvilState<UdevData> {
pub fn process_input_event<B: InputBackend>(&mut self, dh: &DisplayHandle, event: InputEvent<B>) {
match event {
InputEvent::Keyboard { event, .. } => match self.keyboard_key_to_action::<B>(event) {
#[cfg(feature = "udev")]
KeyAction::VtSwitch(vt) => {
info!(to = vt, "Trying to switch vt");
if let Err(err) = self.backend_data.session.change_vt(vt) {
error!(vt, "Error switching vt: {}", err);
}
}
KeyAction::Screen(num) => {
let geometry = self
.space
.outputs()
.nth(num)
.map(|o| self.space.output_geometry(o).unwrap());
if let Some(geometry) = geometry {
let x = geometry.loc.x as f64 + geometry.size.w as f64 / 2.0;
let y = geometry.size.h as f64 / 2.0;
let location = (x, y).into();
let pointer = self.pointer.clone();
let under = self.surface_under(location);
pointer.motion(
self,
under,
&MotionEvent {
location,
serial: SCOUNTER.next_serial(),
time: self.clock.now().as_millis(),
},
);
pointer.frame(self);
}
}
KeyAction::ScaleUp => {
let pos = self.pointer.current_location().to_i32_round();
let output = self
.space
.outputs()
.find(|o| self.space.output_geometry(o).unwrap().contains(pos))
.cloned();
if let Some(output) = output {
let (output_location, scale) = (
self.space.output_geometry(&output).unwrap().loc,
output.current_scale().fractional_scale(),
);
let new_scale = scale + 0.25;
output.change_current_state(None, None, Some(Scale::Fractional(new_scale)), None);
let rescale = scale / new_scale;
let output_location = output_location.to_f64();
let mut pointer_output_location = self.pointer.current_location() - output_location;
pointer_output_location.x *= rescale;
pointer_output_location.y *= rescale;
let pointer_location = output_location + pointer_output_location;
crate::shell::fixup_positions(&mut self.space, pointer_location);
let pointer = self.pointer.clone();
let under = self.surface_under(pointer_location);
pointer.motion(
self,
under,
&MotionEvent {
location: pointer_location,
serial: SCOUNTER.next_serial(),
time: self.clock.now().as_millis(),
},
);
pointer.frame(self);
self.backend_data.reset_buffers(&output);
}
}
KeyAction::ScaleDown => {
let pos = self.pointer.current_location().to_i32_round();
let output = self
.space
.outputs()
.find(|o| self.space.output_geometry(o).unwrap().contains(pos))
.cloned();
if let Some(output) = output {
let (output_location, scale) = (
self.space.output_geometry(&output).unwrap().loc,
output.current_scale().fractional_scale(),
);
let new_scale = f64::max(1.0, scale - 0.25);
output.change_current_state(None, None, Some(Scale::Fractional(new_scale)), None);
let rescale = scale / new_scale;
let output_location = output_location.to_f64();
let mut pointer_output_location = self.pointer.current_location() - output_location;
pointer_output_location.x *= rescale;
pointer_output_location.y *= rescale;
let pointer_location = output_location + pointer_output_location;
crate::shell::fixup_positions(&mut self.space, pointer_location);
let pointer = self.pointer.clone();
let under = self.surface_under(pointer_location);
pointer.motion(
self,
under,
&MotionEvent {
location: pointer_location,
serial: SCOUNTER.next_serial(),
time: self.clock.now().as_millis(),
},
);
pointer.frame(self);
self.backend_data.reset_buffers(&output);
}
}
KeyAction::RotateOutput => {
let pos = self.pointer.current_location().to_i32_round();
let output = self
.space
.outputs()
.find(|o| self.space.output_geometry(o).unwrap().contains(pos))
.cloned();
if let Some(output) = output {
let current_transform = output.current_transform();
let new_transform = match current_transform {
Transform::Normal => Transform::_90,
Transform::_90 => Transform::_180,
Transform::_180 => Transform::_270,
Transform::_270 => Transform::Flipped,
Transform::Flipped => Transform::Flipped90,
Transform::Flipped90 => Transform::Flipped180,
Transform::Flipped180 => Transform::Flipped270,
Transform::Flipped270 => Transform::Normal,
};
output.change_current_state(None, Some(new_transform), None, None);
crate::shell::fixup_positions(&mut self.space, self.pointer.current_location());
self.backend_data.reset_buffers(&output);
}
}
KeyAction::ToggleTint => {
let mut debug_flags = self.backend_data.debug_flags();
debug_flags.toggle(DebugFlags::TINT);
self.backend_data.set_debug_flags(debug_flags);
}
action => match action {
KeyAction::None
| KeyAction::Quit
| KeyAction::Run(_)
| KeyAction::TogglePreview
| KeyAction::ToggleDecorations => self.process_common_key_action(action),
_ => unreachable!(),
},
},
InputEvent::PointerMotion { event, .. } => self.on_pointer_move::<B>(dh, event),
InputEvent::PointerMotionAbsolute { event, .. } => self.on_pointer_move_absolute::<B>(dh, event),
InputEvent::PointerButton { event, .. } => self.on_pointer_button::<B>(event),
InputEvent::PointerAxis { event, .. } => self.on_pointer_axis::<B>(event),
InputEvent::TabletToolAxis { event, .. } => self.on_tablet_tool_axis::<B>(event),
InputEvent::TabletToolProximity { event, .. } => self.on_tablet_tool_proximity::<B>(dh, event),
InputEvent::TabletToolTip { event, .. } => self.on_tablet_tool_tip::<B>(event),
InputEvent::TabletToolButton { event, .. } => self.on_tablet_button::<B>(event),
InputEvent::GestureSwipeBegin { event, .. } => self.on_gesture_swipe_begin::<B>(event),
InputEvent::GestureSwipeUpdate { event, .. } => self.on_gesture_swipe_update::<B>(event),
InputEvent::GestureSwipeEnd { event, .. } => self.on_gesture_swipe_end::<B>(event),
InputEvent::GesturePinchBegin { event, .. } => self.on_gesture_pinch_begin::<B>(event),
InputEvent::GesturePinchUpdate { event, .. } => self.on_gesture_pinch_update::<B>(event),
InputEvent::GesturePinchEnd { event, .. } => self.on_gesture_pinch_end::<B>(event),
InputEvent::GestureHoldBegin { event, .. } => self.on_gesture_hold_begin::<B>(event),
InputEvent::GestureHoldEnd { event, .. } => self.on_gesture_hold_end::<B>(event),
InputEvent::TouchDown { event } => self.on_touch_down::<B>(event),
InputEvent::TouchUp { event } => self.on_touch_up::<B>(event),
InputEvent::TouchMotion { event } => self.on_touch_motion::<B>(event),
InputEvent::TouchFrame { event } => self.on_touch_frame::<B>(event),
InputEvent::TouchCancel { event } => self.on_touch_cancel::<B>(event),
InputEvent::DeviceAdded { device } => {
if device.has_capability(DeviceCapability::TabletTool) {
self.seat
.tablet_seat()
.add_tablet::<Self>(dh, &TabletDescriptor::from(&device));
}
if device.has_capability(DeviceCapability::Touch) && self.seat.get_touch().is_none() {
self.seat.add_touch();
}
}
InputEvent::DeviceRemoved { device } => {
if device.has_capability(DeviceCapability::TabletTool) {
let tablet_seat = self.seat.tablet_seat();
tablet_seat.remove_tablet(&TabletDescriptor::from(&device));
// If there are no tablets in seat we can remove all tools
if tablet_seat.count_tablets() == 0 {
tablet_seat.clear_tools();
}
}
}
_ => {
// other events are not handled in anvil (yet)
}
}
}
fn on_pointer_move<B: InputBackend>(&mut self, _dh: &DisplayHandle, evt: B::PointerMotionEvent) {
let mut pointer_location = self.pointer.current_location();
let serial = SCOUNTER.next_serial();
let pointer = self.pointer.clone();
let under = self.surface_under(pointer_location);
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, &pointer, |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((pointer_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();
}
}
}
_ => {}
});
}
pointer.relative_motion(
self,
under.clone(),
&RelativeMotionEvent {
delta: evt.delta(),
delta_unaccel: evt.delta_unaccel(),
utime: evt.time(),
},
);
// If pointer is locked, only emit relative motion
if pointer_locked {
pointer.frame(self);
return;
}
pointer_location += evt.delta();
// clamp to screen limits
// this event is never generated by winit
pointer_location = self.clamp_coords(pointer_location);
let new_under = self.surface_under(pointer_location);
// 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() {
pointer.frame(self);
return;
}
if let Some(region) = confine_region {
if !region.contains((pointer_location - *surface_loc).to_i32_round()) {
pointer.frame(self);
return;
}
}
}
}
pointer.motion(
self,
under,
&MotionEvent {
location: pointer_location,
serial,
time: evt.time_msec(),
},
);
pointer.frame(self);
// If pointer is now in a constraint region, activate it
// TODO Anywhere else pointer is moved needs to do this
if let Some((under, surface_location)) =
new_under.and_then(|(target, loc)| Some((target.wl_surface()?.into_owned(), loc)))
{
with_pointer_constraint(&under, &pointer, |constraint| match constraint {
Some(constraint) if !constraint.is_active() => {
let point = (pointer_location - surface_location).to_i32_round();
if constraint.region().map_or(true, |region| region.contains(point)) {
constraint.activate();
}
}
_ => {}
});
}
}
fn on_pointer_move_absolute<B: InputBackend>(
&mut self,
_dh: &DisplayHandle,
evt: B::PointerMotionAbsoluteEvent,
) {
let serial = SCOUNTER.next_serial();
let max_x = self
.space
.outputs()
.fold(0, |acc, o| acc + self.space.output_geometry(o).unwrap().size.w);
let max_h_output = self
.space
.outputs()
.max_by_key(|o| self.space.output_geometry(o).unwrap().size.h)
.unwrap();
let max_y = self.space.output_geometry(max_h_output).unwrap().size.h;
let mut pointer_location = (evt.x_transformed(max_x), evt.y_transformed(max_y)).into();
// clamp to screen limits
pointer_location = self.clamp_coords(pointer_location);
let pointer = self.pointer.clone();
let under = self.surface_under(pointer_location);
pointer.motion(
self,
under,
&MotionEvent {
location: pointer_location,
serial,
time: evt.time_msec(),
},
);
pointer.frame(self);
}
fn on_tablet_tool_axis<B: InputBackend>(&mut self, evt: B::TabletToolAxisEvent) {
let tablet_seat = self.seat.tablet_seat();
if let Some(pointer_location) = self.touch_location_transformed(&evt) {
let pointer = self.pointer.clone();
let under = self.surface_under(pointer_location);
let tablet = tablet_seat.get_tablet(&TabletDescriptor::from(&evt.device()));
let tool = tablet_seat.get_tool(&evt.tool());
pointer.motion(
self,
under.clone(),
&MotionEvent {
location: pointer_location,
serial: SCOUNTER.next_serial(),
time: self.clock.now().as_millis(),
},
);
if let (Some(tablet), Some(tool)) = (tablet, tool) {
if evt.pressure_has_changed() {
tool.pressure(evt.pressure());
}
if evt.distance_has_changed() {
tool.distance(evt.distance());
}
if evt.tilt_has_changed() {
tool.tilt(evt.tilt());
}
if evt.slider_has_changed() {
tool.slider_position(evt.slider_position());
}
if evt.rotation_has_changed() {
tool.rotation(evt.rotation());
}
if evt.wheel_has_changed() {
tool.wheel(evt.wheel_delta(), evt.wheel_delta_discrete());
}
tool.motion(
pointer_location,
under.and_then(|(f, loc)| f.wl_surface().map(|s| (s.into_owned(), loc))),
&tablet,
SCOUNTER.next_serial(),
evt.time_msec(),
);
}
pointer.frame(self);
}
}
fn on_tablet_tool_proximity<B: InputBackend>(
&mut self,
dh: &DisplayHandle,
evt: B::TabletToolProximityEvent,
) {
let tablet_seat = self.seat.tablet_seat();
if let Some(pointer_location) = self.touch_location_transformed(&evt) {
let tool = evt.tool();
tablet_seat.add_tool::<Self>(self, dh, &tool);
let pointer = self.pointer.clone();
let under = self.surface_under(pointer_location);
let tablet = tablet_seat.get_tablet(&TabletDescriptor::from(&evt.device()));
let tool = tablet_seat.get_tool(&tool);
pointer.motion(
self,
under.clone(),
&MotionEvent {
location: pointer_location,
serial: SCOUNTER.next_serial(),
time: evt.time_msec(),
},
);
pointer.frame(self);
if let (Some(under), Some(tablet), Some(tool)) = (
under.and_then(|(f, loc)| f.wl_surface().map(|s| (s.into_owned(), loc))),
tablet,
tool,
) {
match evt.state() {
ProximityState::In => tool.proximity_in(
pointer_location,
under,
&tablet,
SCOUNTER.next_serial(),
evt.time_msec(),
),
ProximityState::Out => tool.proximity_out(evt.time_msec()),
}
}
}
}
fn on_tablet_tool_tip<B: InputBackend>(&mut self, evt: B::TabletToolTipEvent) {
let tool = self.seat.tablet_seat().get_tool(&evt.tool());
if let Some(tool) = tool {
match evt.tip_state() {
TabletToolTipState::Down => {
let serial = SCOUNTER.next_serial();
tool.tip_down(serial, evt.time_msec());
// change the keyboard focus
self.update_keyboard_focus(self.pointer.current_location(), serial);
}
TabletToolTipState::Up => {
tool.tip_up(evt.time_msec());
}
}
}
}
fn on_tablet_button<B: InputBackend>(&mut self, evt: B::TabletToolButtonEvent) {
let tool = self.seat.tablet_seat().get_tool(&evt.tool());
if let Some(tool) = tool {
tool.button(
evt.button(),
evt.button_state(),
SCOUNTER.next_serial(),
evt.time_msec(),
);
}
}
fn on_gesture_swipe_begin<B: InputBackend>(&mut self, evt: B::GestureSwipeBeginEvent) {
let serial = SCOUNTER.next_serial();
let pointer = self.pointer.clone();
pointer.gesture_swipe_begin(
self,
&GestureSwipeBeginEvent {
serial,
time: evt.time_msec(),
fingers: evt.fingers(),
},
);
}
fn on_gesture_swipe_update<B: InputBackend>(&mut self, evt: B::GestureSwipeUpdateEvent) {
let pointer = self.pointer.clone();
pointer.gesture_swipe_update(
self,
&GestureSwipeUpdateEvent {
time: evt.time_msec(),
delta: evt.delta(),
},
);
}
fn on_gesture_swipe_end<B: InputBackend>(&mut self, evt: B::GestureSwipeEndEvent) {
let serial = SCOUNTER.next_serial();
let pointer = self.pointer.clone();
pointer.gesture_swipe_end(
self,
&GestureSwipeEndEvent {
serial,
time: evt.time_msec(),
cancelled: evt.cancelled(),
},
);
}
fn on_gesture_pinch_begin<B: InputBackend>(&mut self, evt: B::GesturePinchBeginEvent) {
let serial = SCOUNTER.next_serial();
let pointer = self.pointer.clone();
pointer.gesture_pinch_begin(
self,
&GesturePinchBeginEvent {
serial,
time: evt.time_msec(),
fingers: evt.fingers(),
},
);
}
fn on_gesture_pinch_update<B: InputBackend>(&mut self, evt: B::GesturePinchUpdateEvent) {
let pointer = self.pointer.clone();
pointer.gesture_pinch_update(
self,
&GesturePinchUpdateEvent {
time: evt.time_msec(),
delta: evt.delta(),
scale: evt.scale(),
rotation: evt.rotation(),
},
);
}
fn on_gesture_pinch_end<B: InputBackend>(&mut self, evt: B::GesturePinchEndEvent) {
let serial = SCOUNTER.next_serial();
let pointer = self.pointer.clone();
pointer.gesture_pinch_end(
self,
&GesturePinchEndEvent {
serial,
time: evt.time_msec(),
cancelled: evt.cancelled(),
},
);
}
fn on_gesture_hold_begin<B: InputBackend>(&mut self, evt: B::GestureHoldBeginEvent) {
let serial = SCOUNTER.next_serial();
let pointer = self.pointer.clone();
pointer.gesture_hold_begin(
self,
&GestureHoldBeginEvent {
serial,
time: evt.time_msec(),
fingers: evt.fingers(),
},
);
}
fn on_gesture_hold_end<B: InputBackend>(&mut self, evt: B::GestureHoldEndEvent) {
let serial = SCOUNTER.next_serial();
let pointer = self.pointer.clone();
pointer.gesture_hold_end(
self,
&GestureHoldEndEvent {
serial,
time: evt.time_msec(),
cancelled: evt.cancelled(),
},
);
}
fn touch_location_transformed<B: InputBackend, E: AbsolutePositionEvent<B>>(
&self,
evt: &E,
) -> Option<Point<f64, Logical>> {
let output = self
.space
.outputs()
.find(|output| output.name().starts_with("eDP"))
.or_else(|| self.space.outputs().next());
let output = output?;
let output_geometry = self.space.output_geometry(output)?;
let transform = output.current_transform();
let size = transform.invert().transform_size(output_geometry.size);
Some(
transform.transform_point_in(evt.position_transformed(size), &size.to_f64())
+ output_geometry.loc.to_f64(),
)
}
fn on_touch_down<B: InputBackend>(&mut self, evt: B::TouchDownEvent) {
let Some(handle) = self.seat.get_touch() else {
return;
};
let Some(touch_location) = self.touch_location_transformed(&evt) else {
return;
};
let serial = SCOUNTER.next_serial();
self.update_keyboard_focus(touch_location, serial);
let under = self.surface_under(touch_location);
handle.down(
self,
under,
&DownEvent {
slot: evt.slot(),
location: touch_location,
serial,
time: evt.time_msec(),
},
);
}
fn on_touch_up<B: InputBackend>(&mut self, evt: B::TouchUpEvent) {
let Some(handle) = self.seat.get_touch() else {
return;
};
let serial = SCOUNTER.next_serial();
handle.up(
self,
&UpEvent {
slot: evt.slot(),
serial,
time: evt.time_msec(),
},
)
}
fn on_touch_motion<B: InputBackend>(&mut self, evt: B::TouchMotionEvent) {
let Some(handle) = self.seat.get_touch() else {
return;
};
let Some(touch_location) = self.touch_location_transformed(&evt) else {
return;
};
let under = self.surface_under(touch_location);
handle.motion(
self,
under,
&smithay::input::touch::MotionEvent {
slot: evt.slot(),
location: touch_location,
time: evt.time_msec(),
},
);
}
fn on_touch_frame<B: InputBackend>(&mut self, _evt: B::TouchFrameEvent) {
let Some(handle) = self.seat.get_touch() else {
return;
};
handle.frame(self);
}
fn on_touch_cancel<B: InputBackend>(&mut self, _evt: B::TouchCancelEvent) {
let Some(handle) = self.seat.get_touch() else {
return;
};
handle.cancel(self);
}
fn clamp_coords(&self, pos: Point<f64, Logical>) -> Point<f64, Logical> {
if self.space.outputs().next().is_none() {
return pos;
}
let (pos_x, pos_y) = pos.into();
let max_x = self
.space
.outputs()
.fold(0, |acc, o| acc + self.space.output_geometry(o).unwrap().size.w);
let clamped_x = pos_x.clamp(0.0, max_x as f64);
let max_y = self
.space
.outputs()
.find(|o| {
let geo = self.space.output_geometry(o).unwrap();
geo.contains((clamped_x as i32, 0))
})
.map(|o| self.space.output_geometry(o).unwrap().size.h);
if let Some(max_y) = max_y {
let clamped_y = pos_y.clamp(0.0, max_y as f64);
(clamped_x, clamped_y).into()
} else {
(clamped_x, pos_y).into()
}
}
}
/// Possible results of a keyboard action
#[allow(dead_code)] // some of these are only read if udev is enabled
#[derive(Debug)]
enum KeyAction {
/// Quit the compositor
Quit,
/// Trigger a vt-switch
VtSwitch(i32),
/// run a command
Run(String),
/// Switch the current screen
Screen(usize),
ScaleUp,
ScaleDown,
TogglePreview,
RotateOutput,
ToggleTint,
ToggleDecorations,
/// Do nothing more
None,
}
fn process_keyboard_shortcut(modifiers: ModifiersState, keysym: Keysym) -> Option<KeyAction> {
if modifiers.ctrl && modifiers.alt && keysym == Keysym::BackSpace || modifiers.logo && keysym == Keysym::q
{
// ctrl+alt+backspace = quit
// logo + q = quit
Some(KeyAction::Quit)
} else if (xkb::KEY_XF86Switch_VT_1..=xkb::KEY_XF86Switch_VT_12).contains(&keysym.raw()) {
// VTSwitch
Some(KeyAction::VtSwitch(
(keysym.raw() - xkb::KEY_XF86Switch_VT_1 + 1) as i32,
))
} else if modifiers.logo && keysym == Keysym::Return {
// run terminal
Some(KeyAction::Run("weston-terminal".into()))
} else if modifiers.logo && (xkb::KEY_1..=xkb::KEY_9).contains(&keysym.raw()) {
Some(KeyAction::Screen((keysym.raw() - xkb::KEY_1) as usize))
} else if modifiers.logo && modifiers.shift && keysym == Keysym::M {
Some(KeyAction::ScaleDown)
} else if modifiers.logo && modifiers.shift && keysym == Keysym::P {
Some(KeyAction::ScaleUp)
} else if modifiers.logo && modifiers.shift && keysym == Keysym::W {
Some(KeyAction::TogglePreview)
} else if modifiers.logo && modifiers.shift && keysym == Keysym::R {
Some(KeyAction::RotateOutput)
} else if modifiers.logo && modifiers.shift && keysym == Keysym::T {
Some(KeyAction::ToggleTint)
} else if modifiers.logo && modifiers.shift && keysym == Keysym::D {
Some(KeyAction::ToggleDecorations)
} else {
None
}
}