Merge pull request #75 from pinnacle-comp/focus

Rework focus and fix xwayland popups not rendering
This commit is contained in:
Ottatop 2023-09-10 02:08:39 -05:00 committed by GitHub
commit 61f341bec6
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
10 changed files with 127 additions and 68 deletions

View file

@ -224,6 +224,7 @@ end
---Get the currently focused window. ---Get the currently focused window.
---@return Window|nil ---@return Window|nil
function window_module.get_focused() function window_module.get_focused()
-- TODO: get focused on output
local windows = window_module.get_all() local windows = window_module.get_all()
for _, w in pairs(windows) do for _, w in pairs(windows) do
@ -238,8 +239,7 @@ end
---Get all windows. ---Get all windows.
---@return Window[] ---@return Window[]
function window_module.get_all() function window_module.get_all()
local window_ids = local window_ids = Request("GetWindows").RequestResponse.response.Windows.window_ids
Request("GetWindows").RequestResponse.response.Windows.window_ids
---@type Window[] ---@type Window[]
local windows = {} local windows = {}
@ -513,8 +513,7 @@ function window_module.fullscreen(win)
window_id = win:id(), window_id = win:id(),
}, },
}) })
local fom = local fom = response.RequestResponse.response.WindowProps.fullscreen_or_maximized
response.RequestResponse.response.WindowProps.fullscreen_or_maximized
return fom == "Fullscreen" return fom == "Fullscreen"
end end
@ -528,8 +527,7 @@ function window_module.maximized(win)
window_id = win:id(), window_id = win:id(),
}, },
}) })
local fom = local fom = response.RequestResponse.response.WindowProps.fullscreen_or_maximized
response.RequestResponse.response.WindowProps.fullscreen_or_maximized
return fom == "Maximized" return fom == "Maximized"
end end

View file

@ -13,7 +13,10 @@ use smithay::{
wayland::seat::WaylandFocus, wayland::seat::WaylandFocus,
}; };
use crate::{state::State, window::WindowElement}; use crate::{
state::{State, WithState},
window::WindowElement,
};
#[derive(Default)] #[derive(Default)]
pub struct FocusState { pub struct FocusState {
@ -21,23 +24,30 @@ pub struct FocusState {
pub focused_output: Option<Output>, pub focused_output: Option<Output>,
} }
impl State {
/// Get the currently focused window on `output`, if any.
pub fn current_focus(&mut self, output: &Output) -> Option<WindowElement> {
self.focus_state.focus_stack.retain(|win| win.alive());
let mut windows = self.focus_state.focus_stack.iter().rev().filter(|win| {
let win_tags = win.with_state(|state| state.tags.clone());
let output_tags =
output.with_state(|state| state.focused_tags().cloned().collect::<Vec<_>>());
win_tags
.iter()
.any(|win_tag| output_tags.iter().any(|op_tag| win_tag == op_tag))
});
windows.next().cloned()
}
}
impl FocusState { impl FocusState {
pub fn new() -> Self { pub fn new() -> Self {
Default::default() Default::default()
} }
// TODO: how does this work with unmapped windows?
/// Get the currently focused window. If there is none, the previous focus is returned.
pub fn current_focus(&mut self) -> Option<WindowElement> {
while let Some(window) = self.focus_stack.last() {
if window.alive() {
return Some(window.clone());
}
self.focus_stack.pop();
}
None
}
/// Set the currently focused window. /// Set the currently focused window.
pub fn set_focus(&mut self, window: WindowElement) { pub fn set_focus(&mut self, window: WindowElement) {
self.focus_stack.retain(|win| win != &window); self.focus_stack.retain(|win| win != &window);
@ -46,8 +56,8 @@ impl FocusState {
/// Fix focus layering for all windows in the `focus_stack`. /// Fix focus layering for all windows in the `focus_stack`.
/// ///
/// This will call `space.map_element` on all windows from front /// This will call `space.raise_element` on all windows from back
/// to back to correct their z locations. /// to front to correct their z locations.
pub fn fix_up_focus(&self, space: &mut Space<WindowElement>) { pub fn fix_up_focus(&self, space: &mut Space<WindowElement>) {
for win in self.focus_stack.iter() { for win in self.focus_stack.iter() {
space.raise_element(win, false); space.raise_element(win, false);

View file

@ -328,10 +328,13 @@ impl SeatHandler for State {
} }
fn focus_changed(&mut self, seat: &Seat<Self>, focused: Option<&Self::KeyboardFocus>) { fn focus_changed(&mut self, seat: &Seat<Self>, focused: Option<&Self::KeyboardFocus>) {
if let Some(focus) = if let Some(win) =
focused.and_then(|focused| self.window_for_surface(&focused.wl_surface()?)) focused.and_then(|focused| self.window_for_surface(&focused.wl_surface()?))
{ {
self.focus_state.set_focus(focus); if let WindowElement::Wayland(win) = &win {
win.set_activated(true);
}
self.focus_state.set_focus(win);
} }
let focus_client = focused.and_then(|foc_target| { let focus_client = focused.and_then(|foc_target| {
self.display_handle self.display_handle

View file

@ -121,16 +121,31 @@ impl XdgShellHandler for State {
.wl_surface() .wl_surface()
.is_some_and(|surf| &surf != surface.wl_surface()) .is_some_and(|surf| &surf != surface.wl_surface())
}); });
if let Some(focused_output) = self.focus_state.focused_output.as_ref().cloned() { self.focus_state.focus_stack.retain(|window| {
self.update_windows(&focused_output); window
.wl_surface()
.is_some_and(|surf| &surf != surface.wl_surface())
});
let Some(window) = self.window_for_surface(surface.wl_surface()) else {
return;
};
if let Some(output) = window.output(self) {
self.update_windows(&output);
let focus = self.current_focus(&output).map(FocusTarget::Window);
if let Some(FocusTarget::Window(win)) = &focus {
tracing::debug!("Focusing on prev win");
self.space.raise_element(win, true);
if let WindowElement::Wayland(win) = &win {
win.toplevel().send_configure();
}
}
self.seat
.get_keyboard()
.expect("Seat had no keyboard")
.set_focus(self, focus, SERIAL_COUNTER.next_serial());
} }
let focus = self.focus_state.current_focus().map(FocusTarget::Window);
self.seat
.get_keyboard()
.expect("Seat had no keyboard")
.set_focus(self, focus, SERIAL_COUNTER.next_serial());
} }
fn new_popup(&mut self, surface: PopupSurface, _positioner: PositionerState) { fn new_popup(&mut self, surface: PopupSurface, _positioner: PositionerState) {

View file

@ -153,17 +153,40 @@ impl XwmHandler for CalloopData {
// } // }
fn mapped_override_redirect_window(&mut self, _xwm: XwmId, window: X11Surface) { fn mapped_override_redirect_window(&mut self, _xwm: XwmId, window: X11Surface) {
tracing::debug!("MAPPED OVERRIDE REDIRECT WINDOW"); tracing::debug!("mapped override redirect window");
let win_type = window.window_type(); let win_type = window.window_type();
tracing::debug!("window type is {win_type:?}"); tracing::debug!("window type is {win_type:?}");
let loc = window.geometry().loc; let loc = window.geometry().loc;
let window = WindowElement::X11(window); let window = WindowElement::X11(window);
window.with_state(|state| {
state.tags = match (
&self.state.focus_state.focused_output,
self.state.space.outputs().next(),
) {
(Some(output), _) | (None, Some(output)) => output.with_state(|state| {
let output_tags = state.focused_tags().cloned().collect::<Vec<_>>();
if !output_tags.is_empty() {
output_tags
} else if let Some(first_tag) = state.tags.first() {
vec![first_tag.clone()]
} else {
vec![]
}
}),
(None, None) => vec![],
};
});
self.state.focus_state.set_focus(window.clone());
// tracing::debug!("mapped_override_redirect_window to loc {loc:?}"); // tracing::debug!("mapped_override_redirect_window to loc {loc:?}");
self.state.space.map_element(window.clone(), loc, true); self.state.space.map_element(window.clone(), loc, true);
} }
fn unmapped_window(&mut self, _xwm: XwmId, window: X11Surface) { fn unmapped_window(&mut self, _xwm: XwmId, window: X11Surface) {
tracing::debug!("UNMAPPED WINDOW"); tracing::debug!("UNMAPPED WINDOW");
self.state.focus_state.focus_stack.retain(|win| {
win.wl_surface()
.is_some_and(|surf| Some(surf) != window.wl_surface())
});
let win = self let win = self
.state .state
.space .space
@ -172,6 +195,22 @@ impl XwmHandler for CalloopData {
.cloned(); .cloned();
if let Some(win) = win { if let Some(win) = win {
self.state.space.unmap_elem(&win); self.state.space.unmap_elem(&win);
if let Some(output) = win.output(&self.state) {
self.state.update_windows(&output);
let focus = self.state.current_focus(&output).map(FocusTarget::Window);
if let Some(FocusTarget::Window(win)) = &focus {
tracing::debug!("Focusing on prev win");
self.state.space.raise_element(win, true);
if let WindowElement::Wayland(win) = &win {
win.toplevel().send_configure();
}
}
self.state
.seat
.get_keyboard()
.expect("Seat had no keyboard")
.set_focus(&mut self.state, focus, SERIAL_COUNTER.next_serial());
}
} }
if !window.is_override_redirect() { if !window.is_override_redirect() {
tracing::debug!("set mapped to false"); tracing::debug!("set mapped to false");
@ -180,6 +219,10 @@ impl XwmHandler for CalloopData {
} }
fn destroyed_window(&mut self, _xwm: XwmId, window: X11Surface) { fn destroyed_window(&mut self, _xwm: XwmId, window: X11Surface) {
self.state.focus_state.focus_stack.retain(|win| {
win.wl_surface()
.is_some_and(|surf| Some(surf) != window.wl_surface())
});
let win = self let win = self
.state .state
.windows .windows
@ -197,6 +240,19 @@ impl XwmHandler for CalloopData {
if let Some(output) = win.output(&self.state) { if let Some(output) = win.output(&self.state) {
self.state.update_windows(&output); self.state.update_windows(&output);
let focus = self.state.current_focus(&output).map(FocusTarget::Window);
if let Some(FocusTarget::Window(win)) = &focus {
tracing::debug!("Focusing on prev win");
self.state.space.raise_element(win, true);
if let WindowElement::Wayland(win) = &win {
win.toplevel().send_configure();
}
}
self.state
.seat
.get_keyboard()
.expect("Seat had no keyboard")
.set_focus(&mut self.state, focus, SERIAL_COUNTER.next_serial());
} }
} }
tracing::debug!("destroyed x11 window"); tracing::debug!("destroyed x11 window");

View file

@ -268,16 +268,10 @@ impl State {
let pointer = self.seat.get_pointer().expect("Seat has no pointer"); // FIXME: handle err let pointer = self.seat.get_pointer().expect("Seat has no pointer"); // FIXME: handle err
let keyboard = self.seat.get_keyboard().expect("Seat has no keyboard"); // FIXME: handle err let keyboard = self.seat.get_keyboard().expect("Seat has no keyboard"); // FIXME: handle err
// A serial is a number sent with a event that is sent back to the
// server by the clients in further requests. This allows the server to
// keep track of which event caused which requests. It is an AtomicU32
// that increments when next_serial is called.
let serial = SERIAL_COUNTER.next_serial(); let serial = SERIAL_COUNTER.next_serial();
// Returns which button on the pointer was used.
let button = event.button_code(); let button = event.button_code();
// The state, either released or pressed.
let button_state = event.state(); let button_state = event.state();
let pointer_loc = pointer.current_location(); let pointer_loc = pointer.current_location();
@ -390,21 +384,7 @@ impl State {
}); });
if let FocusTarget::Window(window) = &focus { if let FocusTarget::Window(window) = &focus {
let focused_name = match &window { tracing::debug!("setting keyboard focus to {:?}", window.class());
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 { } else {

View file

@ -166,7 +166,6 @@ impl State {
// schedule on all idle // schedule on all idle
self.schedule( self.schedule(
move |_dt| { move |_dt| {
tracing::debug!("Waiting for all to be idle");
let all_idle = pending_wins let all_idle = pending_wins
.iter() .iter()
.filter(|(_, win)| win.alive()) .filter(|(_, win)| win.alive())
@ -178,8 +177,6 @@ impl State {
.filter(|(_, win)| !win.with_state(|state| state.loc_request_state.is_idle())) .filter(|(_, win)| !win.with_state(|state| state.loc_request_state.is_idle()))
.count(); .count();
tracing::debug!("{num_not_idle} not idle");
all_idle all_idle
}, },
move |dt| { move |dt| {

View file

@ -63,13 +63,11 @@ where
// let window_bbox = self.bbox(); // let window_bbox = self.bbox();
match self { match self {
WindowElement::Wayland(window) => { WindowElement::Wayland(window) => {
AsRenderElements::<R>::render_elements::<WaylandSurfaceRenderElement<R>>( window.render_elements(renderer, location, scale, alpha)
window, renderer, location, scale, alpha, }
) WindowElement::X11(surface) => {
surface.render_elements(renderer, location, scale, alpha)
} }
WindowElement::X11(surface) => AsRenderElements::<R>::render_elements::<
WaylandSurfaceRenderElement<R>,
>(surface, renderer, location, scale, alpha),
} }
.into_iter() .into_iter()
.map(C::from) .map(C::from)

View file

@ -63,7 +63,7 @@ use smithay::{
socket::ListeningSocketSource, socket::ListeningSocketSource,
viewporter::ViewporterState, viewporter::ViewporterState,
}, },
xwayland::{X11Wm, XWayland, XWaylandEvent}, xwayland::{X11Surface, X11Wm, XWayland, XWaylandEvent},
}; };
use crate::input::InputState; use crate::input::InputState;
@ -138,6 +138,7 @@ pub struct State {
pub xwayland: XWayland, pub xwayland: XWayland,
pub xwm: Option<X11Wm>, pub xwm: Option<X11Wm>,
pub xdisplay: Option<u32>, pub xdisplay: Option<u32>,
pub override_redirect_windows: Vec<X11Surface>,
} }
impl State { impl State {
@ -357,6 +358,7 @@ impl State {
xwayland, xwayland,
xwm: None, xwm: None,
xdisplay: None, xdisplay: None,
override_redirect_windows: vec![],
}) })
} }

View file

@ -271,7 +271,8 @@ impl State {
.api_state .api_state
.stream .stream
.as_ref() .as_ref()
.expect("Stream doesn't exist"); .expect("Stream doesn't exist")
.clone();
let mut stream = stream.lock().expect("Couldn't lock stream"); let mut stream = stream.lock().expect("Couldn't lock stream");
match request { match request {
Request::GetWindows => { Request::GetWindows => {
@ -319,9 +320,8 @@ impl State {
WindowElement::X11(surface) => (Some(surface.class()), Some(surface.title())), WindowElement::X11(surface) => (Some(surface.class()), Some(surface.title())),
}); });
let focused = window.as_ref().and_then(|win| { let focused = window.as_ref().and_then(|win| {
self.focus_state let output = win.output(self)?;
.current_focus() // TODO: actual focus self.current_focus(&output).map(|foc_win| win == &foc_win)
.map(|foc_win| win == &foc_win)
}); });
let floating = window let floating = window
.as_ref() .as_ref()