fix(input): require mapped layers for keyboard focus (#48)
Co-authored-by: Samuel Onoja <[email protected]>
This commit is contained in:
1 parent
0589f5685b
commit
d73d5f08f9
5 files changed
+357
-16
No files matched your search
@@ -159,9 +159,12 @@ pub fn handle_commit(state: &mut Ferese, surface: &WlSurface) {
|
|||||||
.layer_for_surface(&surface, WindowSurfaceType::ALL)
|
.layer_for_surface(&surface, WindowSurfaceType::ALL)
|
||||||
.is_some_and(|focused| focused == layer)
|
.is_some_and(|focused| focused == layer)
|
||||||
});
|
});
|
||||||
if !state.session_lock.active()
|
state.reconcile_layer_keyboard_focus();
|
||||||
|
if pointer_on_layer
|
||||||
|
&& !state.session_lock.active()
|
||||||
&& !state.input_capture.captures(1)
|
&& !state.input_capture.captures(1)
|
||||||
&& (policy.keyboard_interactivity == KeyboardInteractivity::Exclusive || pointer_on_layer)
|
&& state.layer_keyboard_eligible(&layer)
|
||||||
|
&& state.exclusive_keyboard_layer().is_none()
|
||||||
&& matches!(policy.layer, Layer::Top | Layer::Overlay)
|
&& matches!(policy.layer, Layer::Top | Layer::Overlay)
|
||||||
&& !layer_has_keyboard_focus(state, &layer)
|
&& !layer_has_keyboard_focus(state, &layer)
|
||||||
{
|
{
|
||||||
@@ -170,8 +173,6 @@ pub fn handle_commit(state: &mut Ferese, surface: &WlSurface) {
|
|||||||
Some(layer.wl_surface().clone()),
|
Some(layer.wl_surface().clone()),
|
||||||
smithay::utils::SERIAL_COUNTER.next_serial(),
|
smithay::utils::SERIAL_COUNTER.next_serial(),
|
||||||
);
|
);
|
||||||
} else if !layer.can_receive_keyboard_focus() && layer_has_keyboard_focus(state, &layer) {
|
|
||||||
state.restore_keyboard_focus();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if let Err(error) = state.display_handle.flush_clients() {
|
if let Err(error) = state.display_handle.flush_clients() {
|
||||||
@@ -179,13 +180,99 @@ pub fn handle_commit(state: &mut Ferese, surface: &WlSurface) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl Ferese {
|
||||||
|
/// Layer-map membership tracks the role and output, not buffer mapping.
|
||||||
|
/// Use the renderer state updated by on_commit_buffer_handler, as for windows.
|
||||||
|
pub(crate) fn layer_keyboard_eligible(&self, layer: &DesktopLayerSurface) -> bool {
|
||||||
|
layer.layer_surface().alive()
|
||||||
|
&& layer.can_receive_keyboard_focus()
|
||||||
|
&& smithay::backend::renderer::utils::with_renderer_surface_state(layer.wl_surface(), |state| {
|
||||||
|
state.buffer().is_some()
|
||||||
|
})
|
||||||
|
.unwrap_or(false)
|
||||||
|
&& self.space.outputs().any(|output| {
|
||||||
|
layer_map_for_output(output)
|
||||||
|
.layers()
|
||||||
|
.any(|candidate| candidate == layer)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
fn keyboard_layer_root(&self) -> Option<WlSurface> {
|
||||||
|
let surface = self.seat.get_keyboard()?.current_focus()?;
|
||||||
|
let root = self
|
||||||
|
.popups
|
||||||
|
.find_popup(&surface)
|
||||||
|
.and_then(|popup| smithay::desktop::find_popup_root_surface(&popup).ok())
|
||||||
|
.unwrap_or(surface);
|
||||||
|
(smithay::wayland::compositor::get_role(&root) == Some(smithay::wayland::shell::wlr_layer::LAYER_SURFACE_ROLE))
|
||||||
|
.then_some(root)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) fn keyboard_focused_layer(&self) -> Option<DesktopLayerSurface> {
|
||||||
|
self.space
|
||||||
|
.layer_for_surface(&self.keyboard_layer_root()?, WindowSurfaceType::ALL)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) fn exclusive_keyboard_layer(&self) -> Option<DesktopLayerSurface> {
|
||||||
|
// Overlay outranks Top. Keep the current owner among peers; the protocol
|
||||||
|
// leaves ordering within one layer to the compositor.
|
||||||
|
let focused = self.keyboard_focused_layer();
|
||||||
|
for priority in [Layer::Overlay, Layer::Top] {
|
||||||
|
if let Some(layer) = focused.as_ref().filter(|layer| {
|
||||||
|
layer.layer() == priority
|
||||||
|
&& layer.cached_state().keyboard_interactivity == KeyboardInteractivity::Exclusive
|
||||||
|
&& self.layer_keyboard_eligible(layer)
|
||||||
|
}) {
|
||||||
|
return Some(layer.clone());
|
||||||
|
}
|
||||||
|
for output in self.space.outputs() {
|
||||||
|
let layers = layer_map_for_output(output)
|
||||||
|
.layers_on(priority)
|
||||||
|
.rev()
|
||||||
|
.cloned()
|
||||||
|
.collect::<Vec<_>>();
|
||||||
|
if let Some(layer) = layers.into_iter().find(|layer| {
|
||||||
|
layer.cached_state().keyboard_interactivity == KeyboardInteractivity::Exclusive
|
||||||
|
&& self.layer_keyboard_eligible(layer)
|
||||||
|
}) {
|
||||||
|
return Some(layer.clone());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
None
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) fn reconcile_layer_keyboard_focus(&mut self) {
|
||||||
|
if self.session_lock.active() || self.input_capture.captures(1) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
let keyboard = self.seat.get_keyboard().expect("seat has a keyboard");
|
||||||
|
if let Some(root) = self.keyboard_layer_root().filter(|_| {
|
||||||
|
!self
|
||||||
|
.keyboard_focused_layer()
|
||||||
|
.is_some_and(|layer| self.layer_keyboard_eligible(&layer))
|
||||||
|
}) {
|
||||||
|
// A popup grab cannot keep an ineligible layer alive as input owner.
|
||||||
|
for (popup, _) in smithay::desktop::PopupManager::popups_for_surface(&root) {
|
||||||
|
let _ = smithay::desktop::PopupManager::dismiss_popup(&root, &popup);
|
||||||
|
}
|
||||||
|
keyboard.unset_grab(self);
|
||||||
|
self.restore_keyboard_focus();
|
||||||
|
} else if !keyboard.is_grabbed()
|
||||||
|
&& let Some(layer) = self.exclusive_keyboard_layer()
|
||||||
|
&& !layer_has_keyboard_focus(self, &layer)
|
||||||
|
{
|
||||||
|
keyboard.set_focus(
|
||||||
|
self,
|
||||||
|
Some(layer.wl_surface().clone()),
|
||||||
|
smithay::utils::SERIAL_COUNTER.next_serial(),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fn layer_has_keyboard_focus(state: &Ferese, layer: &DesktopLayerSurface) -> bool {
|
fn layer_has_keyboard_focus(state: &Ferese, layer: &DesktopLayerSurface) -> bool {
|
||||||
state
|
state.keyboard_focused_layer().is_some_and(|focused| focused == *layer)
|
||||||
.seat
|
|
||||||
.get_keyboard()
|
|
||||||
.and_then(|keyboard| keyboard.current_focus())
|
|
||||||
.and_then(|surface| state.space.layer_for_surface(&surface, WindowSurfaceType::ALL))
|
|
||||||
.is_some_and(|focused| focused == *layer)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
|
|||||||
@@ -998,9 +998,13 @@ impl Ferese {
|
|||||||
}
|
}
|
||||||
let keyboard = self.seat.get_keyboard().expect("seat has a keyboard");
|
let keyboard = self.seat.get_keyboard().expect("seat has a keyboard");
|
||||||
self.focus_output_at(position);
|
self.focus_output_at(position);
|
||||||
|
if let Some(layer) = self.exclusive_keyboard_layer() {
|
||||||
|
keyboard.set_focus(self, Some(layer.wl_surface().clone()), serial);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
if let Some((layer, _, _)) = self.layer_under(position) {
|
if let Some((layer, _, _)) = self.layer_under(position) {
|
||||||
if layer.can_receive_keyboard_focus() {
|
if self.layer_keyboard_eligible(&layer) {
|
||||||
keyboard.set_focus(self, Some(layer.wl_surface().clone()), serial);
|
keyboard.set_focus(self, Some(layer.wl_surface().clone()), serial);
|
||||||
} else if raise
|
} else if raise
|
||||||
&& keyboard.current_focus().is_some_and(|surface| {
|
&& keyboard.current_focus().is_some_and(|surface| {
|
||||||
|
|||||||
@@ -12,6 +12,17 @@ use std::os::unix::net::UnixStream;
|
|||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
|
|
||||||
fn layer(state: &mut Ferese, events: &mut EventLoop<'static, Ferese>, output: &Output) -> (LayerSurface, UnixStream) {
|
fn layer(state: &mut Ferese, events: &mut EventLoop<'static, Ferese>, output: &Output) -> (LayerSurface, UnixStream) {
|
||||||
|
layer_with_policy(state, events, output, 0, 2)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn layer_with_policy(
|
||||||
|
state: &mut Ferese,
|
||||||
|
events: &mut EventLoop<'static, Ferese>,
|
||||||
|
output: &Output,
|
||||||
|
interactivity: u32,
|
||||||
|
level: u32,
|
||||||
|
) -> (LayerSurface, UnixStream) {
|
||||||
|
let initial_focus = state.seat.get_keyboard().unwrap().current_focus();
|
||||||
let (server, mut wire) = UnixStream::pair().unwrap();
|
let (server, mut wire) = UnixStream::pair().unwrap();
|
||||||
let client = state
|
let client = state
|
||||||
.display_handle
|
.display_handle
|
||||||
@@ -31,13 +42,19 @@ fn layer(state: &mut Ferese, events: &mut EventLoop<'static, Ferese>, output: &O
|
|||||||
&mut wire,
|
&mut wire,
|
||||||
layers.id().protocol_id(),
|
layers.id().protocol_id(),
|
||||||
0,
|
0,
|
||||||
&[3, 2, 0, 2, 4, u32::from_ne_bytes(*b"bar\0")],
|
&[3, 2, 0, level, 4, u32::from_ne_bytes(*b"bar\0")],
|
||||||
None,
|
None,
|
||||||
);
|
);
|
||||||
request(&mut wire, 3, 0, &[64, 48], None);
|
request(&mut wire, 3, 0, &[64, 48], None);
|
||||||
request(&mut wire, 3, 1, &[1 | 4], None); // Top left.
|
request(&mut wire, 3, 1, &[1 | 4], None); // Top left.
|
||||||
|
request(&mut wire, 3, 4, &[interactivity], None);
|
||||||
request(&mut wire, 2, 6, &[], None);
|
request(&mut wire, 2, 6, &[], None);
|
||||||
dispatch(events, state);
|
dispatch(events, state);
|
||||||
|
assert_eq!(
|
||||||
|
state.seat.get_keyboard().unwrap().current_focus(),
|
||||||
|
initial_focus,
|
||||||
|
"an initially unconfigured layer must not acquire focus"
|
||||||
|
);
|
||||||
|
|
||||||
wire.set_nonblocking(true).unwrap();
|
wire.set_nonblocking(true).unwrap();
|
||||||
let mut bytes = Vec::new();
|
let mut bytes = Vec::new();
|
||||||
@@ -57,6 +74,12 @@ fn layer(state: &mut Ferese, events: &mut EventLoop<'static, Ferese>, output: &O
|
|||||||
offset += (header >> 16) as usize;
|
offset += (header >> 16) as usize;
|
||||||
}
|
}
|
||||||
request(&mut wire, 3, 6, &[serial.expect("layer configure")], None);
|
request(&mut wire, 3, 6, &[serial.expect("layer configure")], None);
|
||||||
|
dispatch(events, state);
|
||||||
|
assert_eq!(
|
||||||
|
state.seat.get_keyboard().unwrap().current_focus(),
|
||||||
|
initial_focus,
|
||||||
|
"acknowledging configure without mapping must not acquire focus"
|
||||||
|
);
|
||||||
let mut file = tempfile::tempfile().unwrap();
|
let mut file = tempfile::tempfile().unwrap();
|
||||||
file.write_all(&0xff112233u32.to_ne_bytes().repeat(64 * 48)).unwrap();
|
file.write_all(&0xff112233u32.to_ne_bytes().repeat(64 * 48)).unwrap();
|
||||||
request(
|
request(
|
||||||
@@ -221,3 +244,225 @@ fn on_demand_layer_focus_follows_opening_and_outside_clicks() {
|
|||||||
"enabling a panel away from the pointer must not steal window focus"
|
"enabling a panel away from the pointer must not steal window focus"
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn fixture() -> (Ferese, EventLoop<'static, Ferese>, Output) {
|
||||||
|
let mut events = EventLoop::try_new().unwrap();
|
||||||
|
let mut config = crate::config::Config::default().runtime_config().unwrap();
|
||||||
|
config.wallpaper.path = None;
|
||||||
|
config.animations_enabled = false;
|
||||||
|
let mut state = Ferese::new(
|
||||||
|
&mut events,
|
||||||
|
smithay::reexports::wayland_server::Display::new().unwrap(),
|
||||||
|
config,
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
let output = Output::new(
|
||||||
|
"layers".into(),
|
||||||
|
PhysicalProperties {
|
||||||
|
size: (0, 0).into(),
|
||||||
|
subpixel: Subpixel::Unknown,
|
||||||
|
make: "test".into(),
|
||||||
|
model: "test".into(),
|
||||||
|
},
|
||||||
|
);
|
||||||
|
output.change_current_state(
|
||||||
|
Some(Mode {
|
||||||
|
size: (800, 600).into(),
|
||||||
|
refresh: 60_000,
|
||||||
|
}),
|
||||||
|
None,
|
||||||
|
None,
|
||||||
|
None,
|
||||||
|
);
|
||||||
|
state.space.map_output(&output, (0, 0));
|
||||||
|
state.register_output(&output, "layers".into());
|
||||||
|
(state, events, output)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn commit_policy(state: &mut Ferese, events: &mut EventLoop<'static, Ferese>, wire: &mut UnixStream, keyboard: u32) {
|
||||||
|
request(wire, 3, 4, &[keyboard], None);
|
||||||
|
request(wire, 2, 6, &[], None);
|
||||||
|
dispatch(events, state);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn null_buffer_commit_revokes_layer_focus_without_destroying_role() {
|
||||||
|
if !crate::startup_tests::private_runtime(
|
||||||
|
"input::layer_focus_tests::null_buffer_commit_revokes_layer_focus_without_destroying_role",
|
||||||
|
) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
let (mut state, mut events, output) = fixture();
|
||||||
|
let (window, _window_wire) = window(&mut state, &mut events, 0xff112233);
|
||||||
|
let id = state.windows.ids()[&window];
|
||||||
|
assert!(state.activate_managed_window(id));
|
||||||
|
let fallback = Some(window.toplevel().unwrap().wl_surface().clone());
|
||||||
|
let keyboard = state.seat.get_keyboard().unwrap();
|
||||||
|
for interactivity in [1, 2] {
|
||||||
|
let (panel, mut wire) = layer(&mut state, &mut events, &output);
|
||||||
|
commit_policy(&mut state, &mut events, &mut wire, interactivity);
|
||||||
|
state.focus_window_at((20., 20.).into(), SERIAL_COUNTER.next_serial(), true);
|
||||||
|
assert_eq!(keyboard.current_focus(), Some(panel.wl_surface().clone()));
|
||||||
|
request(&mut wire, 2, 1, &[0, 0, 0], None); // wl_surface.attach(NULL).
|
||||||
|
request(&mut wire, 2, 6, &[], None);
|
||||||
|
dispatch(&mut events, &mut state);
|
||||||
|
assert!(panel.layer_surface().alive(), "the layer role survives unmapping");
|
||||||
|
assert!(
|
||||||
|
state
|
||||||
|
.space
|
||||||
|
.layer_for_surface(panel.wl_surface(), smithay::desktop::WindowSurfaceType::TOPLEVEL)
|
||||||
|
.is_some()
|
||||||
|
);
|
||||||
|
assert!(
|
||||||
|
smithay::backend::renderer::utils::with_renderer_surface_state(panel.wl_surface(), |s| s
|
||||||
|
.buffer()
|
||||||
|
.is_none())
|
||||||
|
.unwrap()
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
keyboard.current_focus(),
|
||||||
|
fallback,
|
||||||
|
"null commit must revoke keyboard focus for policy {interactivity}"
|
||||||
|
);
|
||||||
|
request(&mut wire, 2, 6, &[], None);
|
||||||
|
dispatch(&mut events, &mut state);
|
||||||
|
assert_eq!(
|
||||||
|
keyboard.current_focus(),
|
||||||
|
fallback,
|
||||||
|
"unmapped commits must not reclaim focus"
|
||||||
|
);
|
||||||
|
// Reconfigure with a changed size before mapping again. This follows
|
||||||
|
// the protocol's bufferless-commit/configure/ack sequence, including
|
||||||
|
// with Smithay 0.7 which retains configure state on null commits.
|
||||||
|
request(&mut wire, 3, 0, &[65, 48], None);
|
||||||
|
request(&mut wire, 2, 6, &[], None);
|
||||||
|
dispatch(&mut events, &mut state);
|
||||||
|
ack_latest_configure(&mut wire);
|
||||||
|
request(&mut wire, 2, 1, &[5, 0, 0], None);
|
||||||
|
request(&mut wire, 2, 6, &[], None);
|
||||||
|
dispatch(&mut events, &mut state);
|
||||||
|
if interactivity == 2 {
|
||||||
|
assert_eq!(
|
||||||
|
keyboard.current_focus(),
|
||||||
|
fallback,
|
||||||
|
"on-demand remap requires interaction"
|
||||||
|
);
|
||||||
|
state.focus_window_at((20., 20.).into(), SERIAL_COUNTER.next_serial(), true);
|
||||||
|
}
|
||||||
|
assert_eq!(
|
||||||
|
keyboard.current_focus(),
|
||||||
|
Some(panel.wl_surface().clone()),
|
||||||
|
"remapped layer remains focusable"
|
||||||
|
);
|
||||||
|
request(&mut wire, 3, 7, &[], None); // Destroy the role.
|
||||||
|
dispatch(&mut events, &mut state);
|
||||||
|
assert_eq!(keyboard.current_focus(), fallback, "destruction restores window focus");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn ack_latest_configure(wire: &mut UnixStream) {
|
||||||
|
wire.set_nonblocking(true).unwrap();
|
||||||
|
let mut bytes = Vec::new();
|
||||||
|
assert_eq!(
|
||||||
|
wire.read_to_end(&mut bytes).unwrap_err().kind(),
|
||||||
|
std::io::ErrorKind::WouldBlock
|
||||||
|
);
|
||||||
|
wire.set_nonblocking(false).unwrap();
|
||||||
|
let mut serial = None;
|
||||||
|
let mut offset = 0;
|
||||||
|
while offset < bytes.len() {
|
||||||
|
let object = u32::from_ne_bytes(bytes[offset..offset + 4].try_into().unwrap());
|
||||||
|
let header = u32::from_ne_bytes(bytes[offset + 4..offset + 8].try_into().unwrap());
|
||||||
|
if object == 3 && header & 0xffff == 0 {
|
||||||
|
serial = Some(u32::from_ne_bytes(bytes[offset + 8..offset + 12].try_into().unwrap()));
|
||||||
|
}
|
||||||
|
offset += (header >> 16) as usize;
|
||||||
|
}
|
||||||
|
request(wire, 3, 6, &[serial.expect("layer configure")], None);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn exclusive_layer_priority_and_output_removal_restore_eligible_focus() {
|
||||||
|
if !crate::startup_tests::private_runtime(
|
||||||
|
"input::layer_focus_tests::exclusive_layer_priority_and_output_removal_restore_eligible_focus",
|
||||||
|
) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
let (mut state, mut events, output) = fixture();
|
||||||
|
let (window, _window_wire) = window(&mut state, &mut events, 0xff112233);
|
||||||
|
assert!(state.activate_managed_window(state.windows.ids()[&window]));
|
||||||
|
let fallback = Some(window.toplevel().unwrap().wl_surface().clone());
|
||||||
|
let keyboard = state.seat.get_keyboard().unwrap();
|
||||||
|
let (top, mut top_wire) = layer_with_policy(&mut state, &mut events, &output, 1, 2);
|
||||||
|
assert_eq!(keyboard.current_focus(), Some(top.wl_surface().clone()));
|
||||||
|
let (overlay, mut overlay_wire) = layer_with_policy(&mut state, &mut events, &output, 1, 3);
|
||||||
|
assert_eq!(keyboard.current_focus(), Some(overlay.wl_surface().clone()));
|
||||||
|
commit_policy(&mut state, &mut events, &mut top_wire, 1);
|
||||||
|
assert_eq!(
|
||||||
|
keyboard.current_focus(),
|
||||||
|
Some(overlay.wl_surface().clone()),
|
||||||
|
"Top commits cannot steal from an exclusive Overlay"
|
||||||
|
);
|
||||||
|
request(&mut overlay_wire, 2, 1, &[0, 0, 0], None);
|
||||||
|
request(&mut overlay_wire, 2, 6, &[], None);
|
||||||
|
dispatch(&mut events, &mut state);
|
||||||
|
assert_eq!(
|
||||||
|
keyboard.current_focus(),
|
||||||
|
Some(top.wl_surface().clone()),
|
||||||
|
"unmapping falls back to another eligible exclusive layer"
|
||||||
|
);
|
||||||
|
commit_policy(&mut state, &mut events, &mut top_wire, 0);
|
||||||
|
assert_eq!(
|
||||||
|
keyboard.current_focus(),
|
||||||
|
fallback,
|
||||||
|
"disabling keyboard interaction restores the window"
|
||||||
|
);
|
||||||
|
commit_policy(&mut state, &mut events, &mut top_wire, 1);
|
||||||
|
assert_eq!(keyboard.current_focus(), Some(top.wl_surface().clone()));
|
||||||
|
state.unregister_output(&output);
|
||||||
|
assert_eq!(
|
||||||
|
keyboard.current_focus(),
|
||||||
|
None,
|
||||||
|
"removed outputs cannot retain layer focus"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn layer_mapping_commits_do_not_override_session_lock() {
|
||||||
|
use smithay::reexports::wayland_protocols::ext::session_lock::v1::server::ext_session_lock_manager_v1::ExtSessionLockManagerV1;
|
||||||
|
if !crate::startup_tests::private_runtime(
|
||||||
|
"input::layer_focus_tests::layer_mapping_commits_do_not_override_session_lock",
|
||||||
|
) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
let (mut state, mut events, output) = fixture();
|
||||||
|
let (panel, mut wire) = layer_with_policy(&mut state, &mut events, &output, 1, 3);
|
||||||
|
let client = panel.wl_surface().client().unwrap();
|
||||||
|
let manager = client
|
||||||
|
.create_resource::<ExtSessionLockManagerV1, (), Ferese>(&state.display_handle, 1, ())
|
||||||
|
.unwrap();
|
||||||
|
request(&mut wire, manager.id().protocol_id(), 1, &[6], None);
|
||||||
|
dispatch(&mut events, &mut state);
|
||||||
|
let keyboard = state.seat.get_keyboard().unwrap();
|
||||||
|
assert_eq!(keyboard.current_focus(), None);
|
||||||
|
commit_policy(&mut state, &mut events, &mut wire, 1);
|
||||||
|
assert_eq!(
|
||||||
|
keyboard.current_focus(),
|
||||||
|
None,
|
||||||
|
"an exclusive layer cannot focus during lock acquisition"
|
||||||
|
);
|
||||||
|
request(&mut wire, 2, 1, &[0, 0, 0], None);
|
||||||
|
request(&mut wire, 2, 6, &[], None);
|
||||||
|
dispatch(&mut events, &mut state);
|
||||||
|
request(&mut wire, 2, 1, &[5, 0, 0], None);
|
||||||
|
request(&mut wire, 2, 6, &[], None);
|
||||||
|
dispatch(&mut events, &mut state);
|
||||||
|
assert_eq!(keyboard.current_focus(), None, "layer remapping cannot bypass the lock");
|
||||||
|
request(&mut wire, 3, 7, &[], None);
|
||||||
|
dispatch(&mut events, &mut state);
|
||||||
|
assert_eq!(
|
||||||
|
keyboard.current_focus(),
|
||||||
|
None,
|
||||||
|
"layer destruction preserves lock-owned focus policy"
|
||||||
|
);
|
||||||
|
}
|
||||||
@@ -713,10 +713,14 @@ impl Ferese {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
let surface = self
|
let surface = self
|
||||||
.focused_window
|
.exclusive_keyboard_layer()
|
||||||
.and_then(|focused| self.windows.window(focused))
|
.map(|layer| layer.wl_surface().clone())
|
||||||
.and_then(|window| window.toplevel())
|
.or_else(|| {
|
||||||
.map(|toplevel| toplevel.wl_surface().clone());
|
self.focused_window
|
||||||
|
.and_then(|focused| self.windows.window(focused))
|
||||||
|
.and_then(|window| window.toplevel())
|
||||||
|
.map(|toplevel| toplevel.wl_surface().clone())
|
||||||
|
});
|
||||||
|
|
||||||
self.seat.get_keyboard().expect("seat has a keyboard").set_focus(
|
self.seat.get_keyboard().expect("seat has a keyboard").set_focus(
|
||||||
self,
|
self,
|
||||||
|
|||||||
@@ -178,6 +178,7 @@ impl Ferese {
|
|||||||
if changes.focus || self.session_lock.active() {
|
if changes.focus || self.session_lock.active() {
|
||||||
self.restore_keyboard_focus();
|
self.restore_keyboard_focus();
|
||||||
}
|
}
|
||||||
|
self.reconcile_layer_keyboard_focus();
|
||||||
self.refresh_lock_outputs();
|
self.refresh_lock_outputs();
|
||||||
self.refresh_idle_inhibition();
|
self.refresh_idle_inhibition();
|
||||||
let batch = self.desktop_transition.take().unwrap();
|
let batch = self.desktop_transition.take().unwrap();
|
||||||
|
|||||||
Reference in new issue
Block a user