xwayland: Allow eavesdropping on certain keyboard/pointer events
This commit is contained in:
parent
23f51eb150
commit
cbc4ad6fc2
6 changed files with 330 additions and 24 deletions
|
|
@ -46,6 +46,8 @@ pub struct CosmicCompConfig {
|
||||||
pub focus_follows_cursor_delay: u64,
|
pub focus_follows_cursor_delay: u64,
|
||||||
/// Let X11 applications scale themselves
|
/// Let X11 applications scale themselves
|
||||||
pub descale_xwayland: bool,
|
pub descale_xwayland: bool,
|
||||||
|
/// Let X11 applications snoop on certain key-presses to allow for global shortcuts
|
||||||
|
pub xwayland_eavesdropping: XwaylandEavesdropping,
|
||||||
/// The threshold before windows snap themselves to output edges
|
/// The threshold before windows snap themselves to output edges
|
||||||
pub edge_snap_threshold: u32,
|
pub edge_snap_threshold: u32,
|
||||||
pub accessibility_zoom: ZoomConfig,
|
pub accessibility_zoom: ZoomConfig,
|
||||||
|
|
@ -79,6 +81,7 @@ impl Default for CosmicCompConfig {
|
||||||
cursor_follows_focus: false,
|
cursor_follows_focus: false,
|
||||||
focus_follows_cursor_delay: 250,
|
focus_follows_cursor_delay: 250,
|
||||||
descale_xwayland: false,
|
descale_xwayland: false,
|
||||||
|
xwayland_eavesdropping: XwaylandEavesdropping::default(),
|
||||||
edge_snap_threshold: 0,
|
edge_snap_threshold: 0,
|
||||||
accessibility_zoom: ZoomConfig::default(),
|
accessibility_zoom: ZoomConfig::default(),
|
||||||
}
|
}
|
||||||
|
|
@ -154,3 +157,18 @@ pub enum ZoomMovement {
|
||||||
Centered,
|
Centered,
|
||||||
Continuously,
|
Continuously,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Default, Clone, Copy, PartialEq, Eq, Deserialize, Serialize)]
|
||||||
|
pub struct XwaylandEavesdropping {
|
||||||
|
pub keyboard: EavesdroppingKeyboardMode,
|
||||||
|
pub pointer: bool,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Default, Clone, Copy, PartialEq, Eq, Deserialize, Serialize)]
|
||||||
|
pub enum EavesdroppingKeyboardMode {
|
||||||
|
None,
|
||||||
|
Modifiers,
|
||||||
|
#[default]
|
||||||
|
Combinations,
|
||||||
|
All,
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -45,7 +45,7 @@ pub use self::types::*;
|
||||||
use cosmic::config::CosmicTk;
|
use cosmic::config::CosmicTk;
|
||||||
use cosmic_comp_config::{
|
use cosmic_comp_config::{
|
||||||
input::InputConfig, workspace::WorkspaceConfig, CosmicCompConfig, KeyboardConfig, TileBehavior,
|
input::InputConfig, workspace::WorkspaceConfig, CosmicCompConfig, KeyboardConfig, TileBehavior,
|
||||||
XkbConfig, ZoomConfig,
|
XkbConfig, XwaylandEavesdropping, ZoomConfig,
|
||||||
};
|
};
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
|
|
@ -923,6 +923,15 @@ fn config_changed(config: cosmic_config::Config, keys: Vec<String>, state: &mut
|
||||||
state.common.update_xwayland_scale();
|
state.common.update_xwayland_scale();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
"xwayland_eavesdropping" => {
|
||||||
|
let new = get_config::<XwaylandEavesdropping>(&config, "xwayland_eavesdropping");
|
||||||
|
if new != state.common.config.cosmic_conf.xwayland_eavesdropping {
|
||||||
|
state.common.config.cosmic_conf.xwayland_eavesdropping = new;
|
||||||
|
state
|
||||||
|
.common
|
||||||
|
.xwayland_reset_eavesdropping(SERIAL_COUNTER.next_serial());
|
||||||
|
}
|
||||||
|
}
|
||||||
"focus_follows_cursor" => {
|
"focus_follows_cursor" => {
|
||||||
let new = get_config::<bool>(&config, "focus_follows_cursor");
|
let new = get_config::<bool>(&config, "focus_follows_cursor");
|
||||||
if new != state.common.config.cosmic_conf.focus_follows_cursor {
|
if new != state.common.config.cosmic_conf.focus_follows_cursor {
|
||||||
|
|
|
||||||
|
|
@ -237,9 +237,40 @@ impl State {
|
||||||
.lock()
|
.lock()
|
||||||
.unwrap() = Some(serial);
|
.unwrap() = Some(serial);
|
||||||
}
|
}
|
||||||
Self::filter_keyboard_input(
|
|
||||||
|
let current_focus = seat.get_keyboard().unwrap().current_focus();
|
||||||
|
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)
|
||||||
|
});
|
||||||
|
let sym = handle.modified_sym();
|
||||||
|
|
||||||
|
let result = Self::filter_keyboard_input(
|
||||||
data, &event, &seat, modifiers, handle, serial,
|
data, &event, &seat, modifiers, handle, serial,
|
||||||
)
|
);
|
||||||
|
|
||||||
|
if (matches!(result, FilterResult::Forward)
|
||||||
|
&& !seat.get_keyboard().unwrap().is_grabbed()
|
||||||
|
&& !shortcuts_inhibited
|
||||||
|
&& !matches!(
|
||||||
|
current_focus,
|
||||||
|
Some(KeyboardFocusTarget::LockSurface(_))
|
||||||
|
))
|
||||||
|
// we don't want to accidentally leave any keys pressed
|
||||||
|
// and do more filtering in `xwayland_notify_key_event`
|
||||||
|
// for released keys
|
||||||
|
|| state == KeyState::Released
|
||||||
|
{
|
||||||
|
data.common.xwayland_notify_key_event(
|
||||||
|
sym, keycode, state, serial, time,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
result
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
.flatten()
|
.flatten()
|
||||||
|
|
@ -652,7 +683,7 @@ impl State {
|
||||||
self.common.idle_notifier_state.notify_activity(&seat);
|
self.common.idle_notifier_state.notify_activity(&seat);
|
||||||
|
|
||||||
let current_focus = seat.get_keyboard().unwrap().current_focus();
|
let current_focus = seat.get_keyboard().unwrap().current_focus();
|
||||||
let shortcuts_inhibited = current_focus.is_some_and(|f| {
|
let shortcuts_inhibited = current_focus.as_ref().is_some_and(|f| {
|
||||||
f.wl_surface()
|
f.wl_surface()
|
||||||
.and_then(|surface| {
|
.and_then(|surface| {
|
||||||
seat.keyboard_shortcuts_inhibitor_for_surface(&surface)
|
seat.keyboard_shortcuts_inhibitor_for_surface(&surface)
|
||||||
|
|
@ -663,6 +694,7 @@ impl State {
|
||||||
|
|
||||||
let serial = SERIAL_COUNTER.next_serial();
|
let serial = SERIAL_COUNTER.next_serial();
|
||||||
let button = event.button_code();
|
let button = event.button_code();
|
||||||
|
|
||||||
let mut pass_event = !seat.supressed_buttons().remove(button);
|
let mut pass_event = !seat.supressed_buttons().remove(button);
|
||||||
if event.state() == ButtonState::Pressed {
|
if event.state() == ButtonState::Pressed {
|
||||||
// change the keyboard focus unless the pointer is grabbed
|
// change the keyboard focus unless the pointer is grabbed
|
||||||
|
|
@ -810,6 +842,18 @@ impl State {
|
||||||
std::mem::drop(shell);
|
std::mem::drop(shell);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
if pass_event
|
||||||
|
&& !matches!(current_focus, Some(KeyboardFocusTarget::LockSurface(_)))
|
||||||
|
&& !shortcuts_inhibited
|
||||||
|
{
|
||||||
|
self.common.xwayland_notify_pointer_button_event(
|
||||||
|
button,
|
||||||
|
event.state(),
|
||||||
|
serial,
|
||||||
|
event.time_msec(),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
let ptr = seat.get_pointer().unwrap();
|
let ptr = seat.get_pointer().unwrap();
|
||||||
if pass_event {
|
if pass_event {
|
||||||
ptr.button(
|
ptr.button(
|
||||||
|
|
|
||||||
|
|
@ -274,12 +274,12 @@ fn update_focus_state(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let serial = serial.unwrap_or_else(|| SERIAL_COUNTER.next_serial());
|
||||||
|
state
|
||||||
|
.common
|
||||||
|
.xwayland_notify_focus_change(target.cloned(), serial);
|
||||||
ActiveFocus::set(seat, target.cloned());
|
ActiveFocus::set(seat, target.cloned());
|
||||||
keyboard.set_focus(
|
keyboard.set_focus(state, target.cloned(), serial);
|
||||||
state,
|
|
||||||
target.cloned(),
|
|
||||||
serial.unwrap_or_else(|| SERIAL_COUNTER.next_serial()),
|
|
||||||
);
|
|
||||||
std::mem::drop(keyboard);
|
std::mem::drop(keyboard);
|
||||||
|
|
||||||
//update the focused output or set it to the active output
|
//update the focused output or set it to the active output
|
||||||
|
|
|
||||||
|
|
@ -28,9 +28,12 @@ use smithay::{
|
||||||
},
|
},
|
||||||
Seat,
|
Seat,
|
||||||
},
|
},
|
||||||
reexports::wayland_server::{backend::ObjectId, protocol::wl_surface::WlSurface, Resource},
|
reexports::wayland_server::{
|
||||||
|
backend::ObjectId, protocol::wl_surface::WlSurface, Client, Resource,
|
||||||
|
},
|
||||||
utils::{IsAlive, Logical, Point, Serial, Transform},
|
utils::{IsAlive, Logical, Point, Serial, Transform},
|
||||||
wayland::{seat::WaylandFocus, session_lock::LockSurface},
|
wayland::{seat::WaylandFocus, session_lock::LockSurface},
|
||||||
|
xwayland::xwm::XwmId,
|
||||||
};
|
};
|
||||||
|
|
||||||
#[derive(Debug, Clone, PartialEq)]
|
#[derive(Debug, Clone, PartialEq)]
|
||||||
|
|
@ -155,6 +158,15 @@ impl PointerFocusTarget {
|
||||||
_ => None,
|
_ => None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn is_client(&self, client: &Client) -> bool {
|
||||||
|
match self {
|
||||||
|
PointerFocusTarget::WlSurface { surface, .. } => {
|
||||||
|
surface.client().is_some_and(|c| c == *client)
|
||||||
|
}
|
||||||
|
_ => false,
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl KeyboardFocusTarget {
|
impl KeyboardFocusTarget {
|
||||||
|
|
@ -167,6 +179,24 @@ impl KeyboardFocusTarget {
|
||||||
_ => None,
|
_ => None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn is_xwm(&self, xwm: XwmId) -> bool {
|
||||||
|
match self {
|
||||||
|
KeyboardFocusTarget::Element(mapped) => {
|
||||||
|
if let Some(surface) = mapped.active_window().x11_surface() {
|
||||||
|
return surface.xwm_id().unwrap() == xwm;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
KeyboardFocusTarget::Fullscreen(surface) => {
|
||||||
|
if let Some(surface) = surface.x11_surface() {
|
||||||
|
return surface.xwm_id().unwrap() == xwm;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_ => {}
|
||||||
|
}
|
||||||
|
|
||||||
|
false
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
|
|
|
||||||
233
src/xwayland.rs
233
src/xwayland.rs
|
|
@ -11,12 +11,16 @@ use crate::{
|
||||||
toplevel_management::minimize_rectangle, xdg_activation::ActivationContext,
|
toplevel_management::minimize_rectangle, xdg_activation::ActivationContext,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
use cosmic_comp_config::EavesdroppingKeyboardMode;
|
||||||
use smithay::{
|
use smithay::{
|
||||||
backend::drm::DrmNode,
|
backend::{
|
||||||
|
drm::DrmNode,
|
||||||
|
input::{ButtonState, KeyState, Keycode},
|
||||||
|
},
|
||||||
desktop::space::SpaceElement,
|
desktop::space::SpaceElement,
|
||||||
input::pointer::CursorIcon,
|
input::{keyboard::ModifiersState, pointer::CursorIcon},
|
||||||
reexports::{wayland_server::Client, x11rb::protocol::xproto::Window as X11Window},
|
reexports::{wayland_server::Client, x11rb::protocol::xproto::Window as X11Window},
|
||||||
utils::{Logical, Point, Rectangle, Size, SERIAL_COUNTER},
|
utils::{Logical, Point, Rectangle, Serial, Size, SERIAL_COUNTER},
|
||||||
wayland::{
|
wayland::{
|
||||||
selection::{
|
selection::{
|
||||||
data_device::{
|
data_device::{
|
||||||
|
|
@ -37,12 +41,16 @@ use smithay::{
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
use tracing::{error, trace, warn};
|
use tracing::{error, trace, warn};
|
||||||
|
use xkbcommon::xkb::Keysym;
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct XWaylandState {
|
pub struct XWaylandState {
|
||||||
pub client: Client,
|
pub client: Client,
|
||||||
pub xwm: Option<X11Wm>,
|
pub xwm: Option<X11Wm>,
|
||||||
pub display: u32,
|
pub display: u32,
|
||||||
|
pub pressed_keys: Vec<Keycode>,
|
||||||
|
pub pressed_buttons: Vec<u32>,
|
||||||
|
pub last_modifier_state: Option<ModifiersState>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl State {
|
impl State {
|
||||||
|
|
@ -84,6 +92,9 @@ impl State {
|
||||||
client: client.clone(),
|
client: client.clone(),
|
||||||
xwm: None,
|
xwm: None,
|
||||||
display: display_number,
|
display: display_number,
|
||||||
|
pressed_keys: Vec::new(),
|
||||||
|
pressed_buttons: Vec::new(),
|
||||||
|
last_modifier_state: None,
|
||||||
});
|
});
|
||||||
|
|
||||||
let mut wm = match X11Wm::start_wm(
|
let mut wm = match X11Wm::start_wm(
|
||||||
|
|
@ -137,31 +148,225 @@ impl State {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Common {
|
impl Common {
|
||||||
fn is_x_focused(&self, xwm: XwmId) -> bool {
|
fn has_x_keyboard_focus(&self, xwmid: XwmId) -> bool {
|
||||||
if let Some(keyboard) = self
|
let keyboard = self
|
||||||
.shell
|
.shell
|
||||||
.read()
|
.read()
|
||||||
.unwrap()
|
.unwrap()
|
||||||
.seats
|
.seats
|
||||||
.last_active()
|
.last_active()
|
||||||
.get_keyboard()
|
.get_keyboard()
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
keyboard
|
||||||
|
.current_focus()
|
||||||
|
.is_some_and(|target| target.is_xwm(xwmid))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn has_x_pointer_focus(&self, xwmid: XwmId) -> bool {
|
||||||
|
let pointer = self
|
||||||
|
.shell
|
||||||
|
.read()
|
||||||
|
.unwrap()
|
||||||
|
.seats
|
||||||
|
.last_active()
|
||||||
|
.get_pointer()
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
if let Some(x_client) = self.xwayland_state.as_ref().and_then(|xstate| {
|
||||||
|
xstate
|
||||||
|
.xwm
|
||||||
|
.as_ref()
|
||||||
|
.is_some_and(|xwm| xwm.id() == xwmid)
|
||||||
|
.then_some(&xstate.client)
|
||||||
|
}) {
|
||||||
|
pointer
|
||||||
|
.current_focus()
|
||||||
|
.is_some_and(|target| target.is_client(x_client))
|
||||||
|
} else {
|
||||||
|
false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn xwayland_notify_focus_change(
|
||||||
|
&mut self,
|
||||||
|
target: Option<KeyboardFocusTarget>,
|
||||||
|
serial: Serial,
|
||||||
|
) {
|
||||||
|
if let Some(xwm_id) = self
|
||||||
|
.xwayland_state
|
||||||
|
.as_ref()
|
||||||
|
.and_then(|xstate| xstate.xwm.as_ref())
|
||||||
|
.map(|xwm| xwm.id())
|
||||||
{
|
{
|
||||||
match keyboard.current_focus() {
|
if target
|
||||||
Some(KeyboardFocusTarget::Element(mapped)) => {
|
.as_ref()
|
||||||
if let Some(surface) = mapped.active_window().x11_surface() {
|
.is_some_and(|target| matches!(target, KeyboardFocusTarget::LockSurface(_)))
|
||||||
return surface.xwm_id().unwrap() == xwm;
|
|| (!self.has_x_keyboard_focus(xwm_id)
|
||||||
|
&& target.as_ref().is_some_and(|target| target.is_xwm(xwm_id)))
|
||||||
|
{
|
||||||
|
self.xwayland_reset_eavesdropping(serial);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn xwayland_reset_eavesdropping(&mut self, serial: Serial) {
|
||||||
|
let seat = self.shell.read().unwrap().seats.last_active().clone();
|
||||||
|
let keyboard = seat.get_keyboard().unwrap();
|
||||||
|
let pointer = seat.get_pointer().unwrap();
|
||||||
|
|
||||||
|
let xstate = self.xwayland_state.as_mut().unwrap();
|
||||||
|
xstate.last_modifier_state.take();
|
||||||
|
for key in xstate.pressed_keys.drain(..).rev() {
|
||||||
|
for wl_keyboard in keyboard.client_keyboards(&xstate.client) {
|
||||||
|
wl_keyboard.key(serial.into(), 0, key.raw() - 8, KeyState::Released.into());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for button in xstate.pressed_buttons.drain(..).rev() {
|
||||||
|
for wl_pointer in pointer.client_pointers(&xstate.client) {
|
||||||
|
wl_pointer.button(serial.into(), 0, button, ButtonState::Released.into());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn xwayland_notify_key_event(
|
||||||
|
&mut self,
|
||||||
|
sym: Keysym,
|
||||||
|
code: Keycode,
|
||||||
|
state: KeyState,
|
||||||
|
serial: Serial,
|
||||||
|
time: u32,
|
||||||
|
) {
|
||||||
|
let config = self.config.cosmic_conf.xwayland_eavesdropping.keyboard;
|
||||||
|
if config == EavesdroppingKeyboardMode::None {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if self.xwayland_state.as_ref().is_none_or(|xstate| {
|
||||||
|
xstate
|
||||||
|
.xwm
|
||||||
|
.as_ref()
|
||||||
|
.is_none_or(|xwm| self.has_x_keyboard_focus(xwm.id()))
|
||||||
|
}) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
let keyboard = self
|
||||||
|
.shell
|
||||||
|
.read()
|
||||||
|
.unwrap()
|
||||||
|
.seats
|
||||||
|
.last_active()
|
||||||
|
.get_keyboard()
|
||||||
|
.unwrap();
|
||||||
|
let modifiers = keyboard.modifier_state();
|
||||||
|
let is_modifier = sym.is_modifier_key();
|
||||||
|
|
||||||
|
let xstate = self.xwayland_state.as_mut().unwrap();
|
||||||
|
if state == KeyState::Pressed {
|
||||||
|
match config {
|
||||||
|
EavesdroppingKeyboardMode::Modifiers => {
|
||||||
|
if !is_modifier {
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Some(KeyboardFocusTarget::Fullscreen(surface)) => {
|
EavesdroppingKeyboardMode::Combinations => {
|
||||||
if let Some(surface) = surface.x11_surface() {
|
// don't forward alpha-numeric keys, just because shift is held, but forward shift itself
|
||||||
return surface.xwm_id().unwrap() == xwm;
|
if !is_modifier && !(modifiers.alt || modifiers.ctrl || modifiers.logo) {
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
_ => {}
|
_ => {}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
xstate.pressed_keys.push(code);
|
||||||
|
} else {
|
||||||
|
let mut removed = false;
|
||||||
|
xstate.pressed_keys.retain(|c| {
|
||||||
|
if *c == code {
|
||||||
|
removed = true;
|
||||||
|
false
|
||||||
|
} else {
|
||||||
|
true
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
if !removed {
|
||||||
|
// Don't forward released events, we don't have a record off.
|
||||||
|
return;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
false
|
tracing::trace!("Forwaring key {} {:?} to xwayland", code.raw() - 8, state);
|
||||||
|
for wl_keyboard in keyboard.client_keyboards(&xstate.client) {
|
||||||
|
wl_keyboard.key(serial.into(), time, code.raw() - 8, state.into());
|
||||||
|
if xstate.last_modifier_state != Some(modifiers) {
|
||||||
|
xstate.last_modifier_state = Some(modifiers);
|
||||||
|
wl_keyboard.modifiers(
|
||||||
|
serial.into(),
|
||||||
|
modifiers.serialized.depressed,
|
||||||
|
modifiers.serialized.latched,
|
||||||
|
modifiers.serialized.locked,
|
||||||
|
modifiers.serialized.layout_effective,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn xwayland_notify_pointer_button_event(
|
||||||
|
&mut self,
|
||||||
|
button: u32,
|
||||||
|
state: ButtonState,
|
||||||
|
serial: Serial,
|
||||||
|
time: u32,
|
||||||
|
) {
|
||||||
|
if !self.config.cosmic_conf.xwayland_eavesdropping.pointer {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
let pointer = self
|
||||||
|
.shell
|
||||||
|
.read()
|
||||||
|
.unwrap()
|
||||||
|
.seats
|
||||||
|
.last_active()
|
||||||
|
.get_pointer()
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
if self.xwayland_state.as_ref().is_none_or(|xstate| {
|
||||||
|
xstate
|
||||||
|
.xwm
|
||||||
|
.as_ref()
|
||||||
|
.is_none_or(|xwm| self.has_x_pointer_focus(xwm.id()))
|
||||||
|
}) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
let xstate = self.xwayland_state.as_mut().unwrap();
|
||||||
|
if state == ButtonState::Pressed {
|
||||||
|
xstate.pressed_buttons.push(button);
|
||||||
|
} else {
|
||||||
|
let mut removed = false;
|
||||||
|
xstate.pressed_buttons.retain(|b| {
|
||||||
|
if *b == button {
|
||||||
|
removed = true;
|
||||||
|
false
|
||||||
|
} else {
|
||||||
|
true
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
if !removed {
|
||||||
|
// Don't forward released events, we don't have a record off.
|
||||||
|
// This can happen if `xwayland_reset_eavesdropping` was called in between
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
tracing::trace!("Forwaring ptr button {} {:?} to Xwayland", button, state);
|
||||||
|
for wl_pointer in pointer.client_pointers(&xstate.client) {
|
||||||
|
wl_pointer.button(serial.into(), time, button, state.into());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn update_x11_stacking_order(&mut self) {
|
pub fn update_x11_stacking_order(&mut self) {
|
||||||
|
|
@ -756,7 +961,7 @@ impl XwmHandler for State {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn allow_selection_access(&mut self, xwm: XwmId, _selection: SelectionTarget) -> bool {
|
fn allow_selection_access(&mut self, xwm: XwmId, _selection: SelectionTarget) -> bool {
|
||||||
self.common.is_x_focused(xwm)
|
self.common.has_x_keyboard_focus(xwm)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn new_selection(&mut self, xwm: XwmId, selection: SelectionTarget, mime_types: Vec<String>) {
|
fn new_selection(&mut self, xwm: XwmId, selection: SelectionTarget, mime_types: Vec<String>) {
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue