From 464a900e4b49c287cc4c577c728127d0483e52a9 Mon Sep 17 00:00:00 2001 From: Victoria Brekenfeld Date: Thu, 22 Sep 2022 18:15:05 +0200 Subject: [PATCH] focus: Track toplevels instead of wl_surfaces --- src/shell/{focus.rs => focus/mod.rs} | 66 +++++---- src/shell/focus/target.rs | 198 +++++++++++++++++++++++++++ 2 files changed, 237 insertions(+), 27 deletions(-) rename src/shell/{focus.rs => focus/mod.rs} (79%) create mode 100644 src/shell/focus/target.rs diff --git a/src/shell/focus.rs b/src/shell/focus/mod.rs similarity index 79% rename from src/shell/focus.rs rename to src/shell/focus/mod.rs index 550929ce..2345d107 100644 --- a/src/shell/focus.rs +++ b/src/shell/focus/mod.rs @@ -6,18 +6,22 @@ use crate::{ }; use indexmap::IndexSet; use smithay::{ - desktop::{PopupUngrabStrategy, Window, WindowSurfaceType}, + desktop::{layer_map_for_output, PopupUngrabStrategy, Window, WindowSurfaceType}, input::Seat, reexports::wayland_server::protocol::wl_surface::WlSurface, utils::{IsAlive, Serial, SERIAL_COUNTER}, - wayland::{compositor::with_states, shell::xdg::XdgToplevelSurfaceRoleAttributes}, + wayland::{ + compositor::get_role, + shell::{wlr_layer::LAYER_SURFACE_ROLE, xdg::XDG_TOPLEVEL_ROLE}, + }, }; use std::{ cell::{Ref, RefCell, RefMut}, collections::HashMap, - sync::Mutex, }; +pub mod target; + #[derive(Debug, serde::Deserialize, Clone, Copy, PartialEq, Eq)] pub enum FocusDirection { Left, @@ -200,40 +204,47 @@ impl Common { pub fn refresh_focus(state: &mut State) { let seats = state.common.seats.clone(); for seat in seats { - let mut fixup = false; let output = active_output(&seat, &state.common); let last_known_focus = ActiveFocus::get(&seat); if let Some(surface) = last_known_focus { if surface.alive() { - let is_toplevel = with_states(&surface, |states| { - states - .data_map - .get::>() - .is_some() - }); - if !is_toplevel { - continue; - } + let is_toplevel = matches!(get_role(&surface), Some(XDG_TOPLEVEL_ROLE)); + let is_layer = matches!(get_role(&surface), Some(LAYER_SURFACE_ROLE)); - let workspace = state.common.shell.active_space(&output); - if let Some(window) = workspace - .space - .window_for_surface(&surface, WindowSurfaceType::ALL) - { - let focus_stack = workspace.focus_stack(&seat); - if focus_stack.last().map(|w| &w != window).unwrap_or(true) { - fixup = true; + if is_layer { + if layer_map_for_output(&output) + .layer_for_surface(&surface, WindowSurfaceType::ALL) + .is_some() + { + continue; // Focus is valid + } + } else if is_toplevel { + let workspace = state.common.shell.active_space(&output); + if let Some(window) = workspace + .space + .window_for_surface(&surface, WindowSurfaceType::ALL) + { + let focus_stack = workspace.focus_stack(&seat); + if !focus_stack.last().map(|w| &w != window).unwrap_or(true) { + continue; // Focus is valid + } else { + slog_scope::debug!("Wrong Window, focus fixup"); + } + } else { + slog_scope::debug!("Different workspaces Window, focus fixup"); } } else { - fixup = true; + // unknown surface type, fixup + slog_scope::debug!("Surface unmapped, focus fixup"); } } else { - fixup = true; + slog_scope::debug!("Surface dead, focus fixup"); } } - if fixup { + // fixup focus + { // also remove popup grabs, if we are switching focus if let Some(mut popup_grab) = seat .user_data() @@ -246,14 +257,15 @@ impl Common { } // update keyboard focus - let surface = state + let surface = dbg!(state .common .shell .active_space(&output) .focus_stack(&seat) - .last() - .map(|w| w.toplevel().wl_surface().clone()); + .last()) + .map(|w| w.toplevel().wl_surface().clone()); if let Some(keyboard) = seat.get_keyboard() { + slog_scope::info!("restoring focus to: {:?}", surface.as_ref()); keyboard.set_focus(state, surface.clone(), SERIAL_COUNTER.next_serial()); ActiveFocus::set(&seat, surface); } diff --git a/src/shell/focus/target.rs b/src/shell/focus/target.rs new file mode 100644 index 00000000..81861424 --- /dev/null +++ b/src/shell/focus/target.rs @@ -0,0 +1,198 @@ +use crate::utils::prelude::*; +pub use smithay::{ + backend::input::KeyState, + desktop::{LayerSurface, PopupKind, Window}, + input::{ + keyboard::{KeyboardTarget, KeysymHandle, ModifiersState}, + pointer::{AxisFrame, ButtonEvent, MotionEvent, PointerTarget}, + Seat, + }, + reexports::wayland_server::{backend::ObjectId, protocol::wl_surface::WlSurface, Resource}, + utils::{IsAlive, Serial}, + wayland::seat::WaylandFocus, +}; + +#[derive(Debug, Clone, PartialEq)] +pub enum FocusTarget { + Window(Window), + LayerSurface(LayerSurface), + Popup(PopupKind), +} + +impl IsAlive for FocusTarget { + fn alive(&self) -> bool { + match self { + FocusTarget::Window(w) => w.alive(), + FocusTarget::LayerSurface(l) => l.alive(), + FocusTarget::Popup(p) => p.alive(), + } + } +} + +impl PointerTarget for FocusTarget { + fn enter(&self, seat: &Seat, data: &mut State, event: &MotionEvent) { + match self { + FocusTarget::Window(w) => { + PointerTarget::enter(w.toplevel().wl_surface(), seat, data, event) + } + FocusTarget::LayerSurface(l) => PointerTarget::enter(l.wl_surface(), seat, data, event), + FocusTarget::Popup(p) => PointerTarget::enter(p.wl_surface(), seat, data, event), + } + } + fn motion(&self, seat: &Seat, data: &mut State, event: &MotionEvent) { + match self { + FocusTarget::Window(w) => { + PointerTarget::motion(w.toplevel().wl_surface(), seat, data, event) + } + FocusTarget::LayerSurface(l) => { + PointerTarget::motion(l.wl_surface(), seat, data, event) + } + FocusTarget::Popup(p) => PointerTarget::motion(p.wl_surface(), seat, data, event), + } + } + fn button(&self, seat: &Seat, data: &mut State, event: &ButtonEvent) { + match self { + FocusTarget::Window(w) => { + PointerTarget::button(w.toplevel().wl_surface(), seat, data, event) + } + FocusTarget::LayerSurface(l) => { + PointerTarget::button(l.wl_surface(), seat, data, event) + } + FocusTarget::Popup(p) => PointerTarget::button(p.wl_surface(), seat, data, event), + } + } + fn axis(&self, seat: &Seat, data: &mut State, frame: AxisFrame) { + match self { + FocusTarget::Window(w) => { + PointerTarget::axis(w.toplevel().wl_surface(), seat, data, frame) + } + FocusTarget::LayerSurface(l) => PointerTarget::axis(l.wl_surface(), seat, data, frame), + FocusTarget::Popup(p) => PointerTarget::axis(p.wl_surface(), seat, data, frame), + } + } + fn leave(&self, seat: &Seat, data: &mut State, serial: Serial, time: u32) { + match self { + FocusTarget::Window(w) => { + PointerTarget::leave(w.toplevel().wl_surface(), seat, data, serial, time) + } + FocusTarget::LayerSurface(l) => { + PointerTarget::leave(l.wl_surface(), seat, data, serial, time) + } + FocusTarget::Popup(p) => PointerTarget::leave(p.wl_surface(), seat, data, serial, time), + } + } +} + +impl KeyboardTarget for FocusTarget { + fn enter( + &self, + seat: &Seat, + data: &mut State, + keys: Vec>, + serial: Serial, + ) { + match self { + FocusTarget::Window(w) => { + KeyboardTarget::enter(w.toplevel().wl_surface(), seat, data, keys, serial) + } + FocusTarget::LayerSurface(l) => { + KeyboardTarget::enter(l.wl_surface(), seat, data, keys, serial) + } + FocusTarget::Popup(p) => { + KeyboardTarget::enter(p.wl_surface(), seat, data, keys, serial) + } + } + } + fn leave(&self, seat: &Seat, data: &mut State, serial: Serial) { + match self { + FocusTarget::Window(w) => { + KeyboardTarget::leave(w.toplevel().wl_surface(), seat, data, serial) + } + FocusTarget::LayerSurface(l) => { + KeyboardTarget::leave(l.wl_surface(), seat, data, serial) + } + FocusTarget::Popup(p) => KeyboardTarget::leave(p.wl_surface(), seat, data, serial), + } + } + fn key( + &self, + seat: &Seat, + data: &mut State, + key: KeysymHandle<'_>, + state: KeyState, + serial: Serial, + time: u32, + ) { + match self { + FocusTarget::Window(w) => KeyboardTarget::key( + w.toplevel().wl_surface(), + seat, + data, + key, + state, + serial, + time, + ), + FocusTarget::LayerSurface(l) => { + KeyboardTarget::key(l.wl_surface(), seat, data, key, state, serial, time) + } + FocusTarget::Popup(p) => { + KeyboardTarget::key(p.wl_surface(), seat, data, key, state, serial, time) + } + } + } + fn modifiers( + &self, + seat: &Seat, + data: &mut State, + modifiers: ModifiersState, + serial: Serial, + ) { + match self { + FocusTarget::Window(w) => { + KeyboardTarget::modifiers(w.toplevel().wl_surface(), seat, data, modifiers, serial) + } + FocusTarget::LayerSurface(l) => { + KeyboardTarget::modifiers(l.wl_surface(), seat, data, modifiers, serial) + } + FocusTarget::Popup(p) => { + KeyboardTarget::modifiers(p.wl_surface(), seat, data, modifiers, serial) + } + } + } +} + +impl WaylandFocus for FocusTarget { + fn wl_surface(&self) -> Option<&WlSurface> { + Some(match self { + FocusTarget::Window(w) => w.toplevel().wl_surface(), + FocusTarget::LayerSurface(l) => l.wl_surface(), + FocusTarget::Popup(p) => p.wl_surface(), + }) + } + fn same_client_as(&self, object_id: &ObjectId) -> bool { + match self { + FocusTarget::Window(w) => w.toplevel().wl_surface().id().same_client_as(object_id), + FocusTarget::LayerSurface(l) => l.wl_surface().id().same_client_as(object_id), + FocusTarget::Popup(p) => p.wl_surface().id().same_client_as(object_id), + } + } +} + +impl From for FocusTarget { + fn from(w: Window) -> Self { + FocusTarget::Window(w) + } +} + +impl From for FocusTarget { + fn from(l: LayerSurface) -> Self { + FocusTarget::LayerSurface(l) + } +} + +impl From for FocusTarget { + fn from(p: PopupKind) -> Self { + FocusTarget::Popup(p) + } +}