Fix keyboard input to Steam games on winit

This commit is contained in:
Ottatop 2023-07-28 11:11:35 -05:00
parent 78e53abf95
commit 43d6cde926
5 changed files with 263 additions and 26 deletions

View file

@ -1,8 +1,19 @@
// SPDX-License-Identifier: GPL-3.0-or-later
use smithay::{output::Output, utils::IsAlive};
use smithay::{
desktop::PopupKind,
input::{
keyboard::KeyboardTarget,
pointer::{MotionEvent, PointerTarget},
Seat,
},
output::Output,
reexports::wayland_server::{protocol::wl_surface::WlSurface, Resource},
utils::IsAlive,
wayland::seat::WaylandFocus,
};
use crate::window::WindowElement;
use crate::{backend::Backend, state::State, window::WindowElement};
#[derive(Default)]
pub struct FocusState {
@ -33,3 +44,196 @@ impl FocusState {
self.focus_stack.push(window);
}
}
#[derive(Debug, Clone, PartialEq)]
pub enum FocusTarget {
Window(WindowElement),
Popup(PopupKind),
// TODO: LayerSurface
}
impl IsAlive for FocusTarget {
fn alive(&self) -> bool {
match self {
FocusTarget::Window(window) => window.alive(),
FocusTarget::Popup(popup) => popup.alive(),
}
}
}
impl From<FocusTarget> for WlSurface {
fn from(value: FocusTarget) -> Self {
value.wl_surface().expect("no wl_surface")
}
}
impl<B: Backend> PointerTarget<State<B>> for FocusTarget {
fn enter(&self, seat: &Seat<State<B>>, data: &mut State<B>, event: &MotionEvent) {
match self {
FocusTarget::Window(window) => PointerTarget::enter(window, seat, data, event),
FocusTarget::Popup(popup) => {
PointerTarget::enter(popup.wl_surface(), seat, data, event);
}
}
}
fn motion(&self, seat: &Seat<State<B>>, data: &mut State<B>, event: &MotionEvent) {
match self {
FocusTarget::Window(window) => PointerTarget::motion(window, seat, data, event),
FocusTarget::Popup(popup) => {
PointerTarget::motion(popup.wl_surface(), seat, data, event);
}
}
}
fn relative_motion(
&self,
seat: &Seat<State<B>>,
data: &mut State<B>,
event: &smithay::input::pointer::RelativeMotionEvent,
) {
match self {
FocusTarget::Window(window) => {
PointerTarget::relative_motion(window, seat, data, event);
}
FocusTarget::Popup(popup) => {
PointerTarget::relative_motion(popup.wl_surface(), seat, data, event);
}
}
}
fn button(
&self,
seat: &Seat<State<B>>,
data: &mut State<B>,
event: &smithay::input::pointer::ButtonEvent,
) {
match self {
FocusTarget::Window(window) => PointerTarget::button(window, seat, data, event),
FocusTarget::Popup(popup) => {
PointerTarget::button(popup.wl_surface(), seat, data, event);
}
}
}
fn axis(
&self,
seat: &Seat<State<B>>,
data: &mut State<B>,
frame: smithay::input::pointer::AxisFrame,
) {
match self {
FocusTarget::Window(window) => PointerTarget::axis(window, seat, data, frame),
FocusTarget::Popup(popup) => PointerTarget::axis(popup.wl_surface(), seat, data, frame),
}
}
fn leave(
&self,
seat: &Seat<State<B>>,
data: &mut State<B>,
serial: smithay::utils::Serial,
time: u32,
) {
match self {
FocusTarget::Window(window) => PointerTarget::leave(window, seat, data, serial, time),
FocusTarget::Popup(popup) => {
PointerTarget::leave(popup.wl_surface(), seat, data, serial, time);
}
}
}
}
impl<B: Backend> KeyboardTarget<State<B>> for FocusTarget {
fn enter(
&self,
seat: &Seat<State<B>>,
data: &mut State<B>,
keys: Vec<smithay::input::keyboard::KeysymHandle<'_>>,
serial: smithay::utils::Serial,
) {
match self {
FocusTarget::Window(window) => KeyboardTarget::enter(window, seat, data, keys, serial),
FocusTarget::Popup(popup) => {
KeyboardTarget::enter(popup.wl_surface(), seat, data, keys, serial);
}
}
}
fn leave(&self, seat: &Seat<State<B>>, data: &mut State<B>, serial: smithay::utils::Serial) {
match self {
FocusTarget::Window(window) => KeyboardTarget::leave(window, seat, data, serial),
FocusTarget::Popup(popup) => {
KeyboardTarget::leave(popup.wl_surface(), seat, data, serial);
}
}
}
fn key(
&self,
seat: &Seat<State<B>>,
data: &mut State<B>,
key: smithay::input::keyboard::KeysymHandle<'_>,
state: smithay::backend::input::KeyState,
serial: smithay::utils::Serial,
time: u32,
) {
match self {
FocusTarget::Window(window) => {
KeyboardTarget::key(window, seat, data, key, state, serial, time);
}
FocusTarget::Popup(popup) => {
KeyboardTarget::key(popup.wl_surface(), seat, data, key, state, serial, time);
}
}
}
fn modifiers(
&self,
seat: &Seat<State<B>>,
data: &mut State<B>,
modifiers: smithay::input::keyboard::ModifiersState,
serial: smithay::utils::Serial,
) {
match self {
FocusTarget::Window(window) => {
KeyboardTarget::modifiers(window, seat, data, modifiers, serial);
}
FocusTarget::Popup(popup) => {
KeyboardTarget::modifiers(popup.wl_surface(), seat, data, modifiers, serial);
}
}
}
}
impl WaylandFocus for FocusTarget {
fn wl_surface(&self) -> Option<WlSurface> {
match self {
FocusTarget::Window(window) => window.wl_surface(),
FocusTarget::Popup(popup) => Some(popup.wl_surface().clone()),
}
}
fn same_client_as(
&self,
object_id: &smithay::reexports::wayland_server::backend::ObjectId,
) -> bool {
match self {
FocusTarget::Window(WindowElement::Wayland(window)) => window.same_client_as(object_id),
FocusTarget::Window(WindowElement::X11(surface)) => surface.same_client_as(object_id),
FocusTarget::Popup(popup) => popup.wl_surface().id().same_client_as(object_id),
}
}
}
impl From<WindowElement> for FocusTarget {
fn from(value: WindowElement) -> Self {
FocusTarget::Window(value)
}
}
impl From<PopupKind> for FocusTarget {
fn from(value: PopupKind) -> Self {
FocusTarget::Popup(value)
}
}

View file

@ -38,6 +38,7 @@ use smithay::{
},
dmabuf,
fractional_scale::{self, FractionalScaleHandler},
seat::WaylandFocus,
shell::xdg::{
Configure, PopupSurface, PositionerState, ToplevelSurface, XdgPopupSurfaceData,
XdgShellHandler, XdgShellState, XdgToplevelSurfaceData,
@ -49,6 +50,7 @@ use smithay::{
use crate::{
backend::Backend,
focus::FocusTarget,
state::{CalloopData, ClientState, State, WithState},
window::{window_state::WindowResizeState, WindowBlocker, WindowElement, BLOCKER_COUNTER},
};
@ -206,8 +208,8 @@ impl<B: Backend> DataDeviceHandler for State<B> {
delegate_data_device!(@<B: Backend> State<B>);
impl<B: Backend> SeatHandler for State<B> {
type KeyboardFocus = WlSurface;
type PointerFocus = WlSurface;
type KeyboardFocus = FocusTarget;
type PointerFocus = FocusTarget;
fn seat_state(&mut self) -> &mut SeatState<Self> {
&mut self.seat_state
@ -219,14 +221,13 @@ impl<B: Backend> SeatHandler for State<B> {
}
fn focus_changed(&mut self, seat: &Seat<Self>, focused: Option<&Self::KeyboardFocus>) {
if let Some(wl_surface) = focused {
if let Some(window) = self.window_for_surface(wl_surface) {
if let Some(focus) = focused.and_then(|focus| focus.wl_surface()) {
if let Some(window) = self.window_for_surface(&focus) {
self.focus_state.set_focus(window);
// let focus = focused.and_then(|surf| self.display_handle.get_client(surf.id()).ok());
// set_data_device_focus(&self.display_handle, seat, focus);
}
}
let focus = focused.and_then(|surf| self.display_handle.get_client(surf.id()).ok());
set_data_device_focus(&self.display_handle, seat, focus);
}
}
delegate_seat!(@<B: Backend> State<B>);
@ -347,7 +348,7 @@ impl<B: Backend> XdgShellHandler for State<B> {
.expect("Seat had no keyboard") // FIXME: actually handle error
.set_focus(
&mut data.state,
window.wl_surface(),
Some(FocusTarget::Window(window)),
SERIAL_COUNTER.next_serial(),
);
});
@ -377,10 +378,7 @@ impl<B: Backend> XdgShellHandler for State<B> {
// let mut windows: Vec<Window> = self.space.elements().cloned().collect();
// windows.retain(|window| window.toplevel() != &surface);
// Layouts::master_stack(self, windows, crate::layout::Direction::Left);
let focus = self
.focus_state
.current_focus()
.and_then(|win| win.wl_surface());
let focus = self.focus_state.current_focus().map(FocusTarget::Window);
self.seat
.get_keyboard()
.expect("Seat had no keyboard")
@ -440,10 +438,9 @@ impl<B: Backend> XdgShellHandler for State<B> {
.ok()
.and_then(|root| self.window_for_surface(&root))
{
let Some(wl_surface) = root.wl_surface() else { return };
if let Ok(mut grab) = self
.popup_manager
.grab_popup(wl_surface, popup_kind, &seat, serial)
if let Ok(mut grab) =
self.popup_manager
.grab_popup(FocusTarget::Window(root), popup_kind, &seat, serial)
{
if let Some(keyboard) = seat.get_keyboard() {
if keyboard.is_grabbed()

View file

@ -22,7 +22,7 @@ use crate::{
backend::Backend,
grab::resize_grab::{ResizeSurfaceGrab, ResizeSurfaceState},
state::{CalloopData, WithState},
window::{WindowBlocker, WindowElement, BLOCKER_COUNTER, window_state::Float},
window::{WindowBlocker, WindowElement, BLOCKER_COUNTER, window_state::Float}, focus::FocusTarget,
};
impl<B: Backend> XwmHandler for CalloopData<B> {
@ -38,7 +38,17 @@ impl<B: Backend> XwmHandler for CalloopData<B> {
tracing::debug!("-----MAP WINDOW REQUEST");
// tracing::debug!("new x11 window from map_window_request");
// tracing::debug!("window popup is {}", window.is_popup());
//
// TODO: TOMORROW: figure out why keyboard input isn't going to games (prolly you never
// | change keyboard focus)
if window.is_override_redirect() {
let loc = window.geometry().loc;
let window = WindowElement::X11(window);
// tracing::debug!("mapped_override_redirect_window to loc {loc:?}");
self.state.space.map_element(window, loc, true);
return;
}
window.set_mapped(true).expect("failed to map x11 window");
let window = WindowElement::X11(window);
self.state.space.map_element(window.clone(), (0, 0), true);
@ -174,7 +184,7 @@ impl<B: Backend> XwmHandler for CalloopData<B> {
.expect("Seat had no keyboard") // FIXME: actually handle error
.set_focus(
&mut data.state,
window.wl_surface(),
Some(FocusTarget::Window(window)),
SERIAL_COUNTER.next_serial(),
);
});

View file

@ -4,6 +4,7 @@ use std::collections::HashMap;
use crate::{
api::msg::{CallbackId, Modifier, ModifierMask, OutgoingMsg},
focus::FocusTarget,
window::WindowElement,
};
use smithay::{
@ -18,6 +19,7 @@ use smithay::{
},
reexports::wayland_protocols::xdg::shell::server::xdg_toplevel::ResizeEdge,
utils::{Logical, Point, SERIAL_COUNTER},
wayland::compositor,
};
use crate::{
@ -144,15 +146,36 @@ impl<B: Backend> State<B> {
.expect("no xwm")
.raise_window(surface)
.expect("failed to raise x11 win");
surface.set_activated(true).unwrap();
}
keyboard.set_focus(self, window.wl_surface(), serial);
tracing::debug!(
"wl_surface focus is some? {}",
window.wl_surface().is_some()
);
keyboard.set_focus(self, Some(FocusTarget::Window(window.clone())), serial);
self.space.elements().for_each(|window| {
if let WindowElement::Wayland(window) = window {
window.toplevel().send_configure();
}
});
let focused_name = match &window {
WindowElement::Wayland(win) => {
compositor::with_states(win.toplevel().wl_surface(), |states| {
let lock = states
.data_map
.get::<smithay::wayland::shell::xdg::XdgToplevelSurfaceData>()
.expect("XdgToplevelSurfaceData wasn't in surface's data map")
.lock()
.expect("failed to acquire lock");
lock.app_id.clone().unwrap_or_default()
})
}
WindowElement::X11(surf) => surf.class(),
};
tracing::debug!("setting keyboard focus to {focused_name}");
}
} else {
self.space.elements().for_each(|window| match window {
@ -361,7 +384,7 @@ impl State<WinitData> {
.and_then(|(window, location)| {
window
.surface_under(pointer_loc - location.to_f64(), WindowSurfaceType::ALL)
.map(|(s, p)| (s, p + location))
.map(|(_s, p)| (FocusTarget::Window(window.clone()), p + location))
});
pointer.motion(
@ -418,7 +441,7 @@ impl State<UdevData> {
let surface_under = self
.surface_under(self.pointer_location)
.and_then(|(window, loc)| window.wl_surface().map(|surface| (surface, loc)));
.map(|(window, loc)| (FocusTarget::Window(window), loc));
// tracing::info!("{:?}", self.pointer_location);
if let Some(ptr) = self.seat.get_pointer() {
@ -485,7 +508,7 @@ impl State<UdevData> {
let surface_under = self
.surface_under(self.pointer_location)
.and_then(|(window, loc)| window.wl_surface().map(|surface| (surface, loc)));
.map(|(window, loc)| (FocusTarget::Window(window), loc));
if let Some(ptr) = self.seat.get_pointer() {
ptr.motion(

View file

@ -7,8 +7,11 @@ use smithay::{
},
reexports::wayland_server::{protocol::wl_surface::WlSurface, Resource},
utils::Serial,
wayland::seat::WaylandFocus,
};
use crate::focus::FocusTarget;
/// Returns the [GrabStartData] from a pointer grab, if any.
pub fn pointer_grab_start_data<S>(
pointer: &PointerHandle<S>,
@ -16,7 +19,7 @@ pub fn pointer_grab_start_data<S>(
serial: Serial,
) -> Option<GrabStartData<S>>
where
S: SeatHandler<PointerFocus = WlSurface> + 'static,
S: SeatHandler<PointerFocus = FocusTarget> + 'static,
{
println!("start of pointer_grab_start_data");
if !pointer.has_grab(serial) {
@ -28,7 +31,7 @@ where
let (focus_surface, _point) = start_data.focus.as_ref()?;
if !focus_surface.id().same_client_as(&surface.id()) {
if !focus_surface.same_client_as(&surface.id()) {
println!("surface isn't the same");
return None;
}