Use wl_keyboard instead of toplevel state for focus

Rare TODO comment actually done.
Fixes #64
This commit is contained in:
Shawn Wallace 2024-10-23 02:27:14 -04:00
parent 8703e243eb
commit dc1f8a753d
4 changed files with 80 additions and 29 deletions

View file

@ -253,15 +253,6 @@ impl SurfaceData {
states, states,
} => { } => {
debug!("configuring toplevel {width}x{height}, {states:?}"); debug!("configuring toplevel {width}x{height}, {states:?}");
let activated = states.contains(&(u32::from(xdg_toplevel::State::Activated) as u8));
if activated {
// Technically this is wrong - activated doesn't necessarily mean focused
// - but it works and no one's complained yet.
// TODO: base focus on keyboard enter instead.
state.to_focus = Some(self.window.unwrap());
}
if let Some(SurfaceRole::Toplevel(Some(toplevel))) = &mut self.role { if let Some(SurfaceRole::Toplevel(Some(toplevel))) = &mut self.role {
let prev_fs = toplevel.fullscreen; let prev_fs = toplevel.fullscreen;
toplevel.fullscreen = toplevel.fullscreen =
@ -539,9 +530,11 @@ impl HandleEvent for Keyboard {
surface, surface,
keys, keys,
} => { } => {
state.last_kb_serial = Some(serial); let key: ObjectKey = surface.data().copied().unwrap();
if let Some(surface_data) = state.get_server_surface_from_client(surface) { if let Some(data) = state.objects.get(key).map(|o| <_ as AsRef<SurfaceData>>::as_ref(o)) {
self.server.enter(serial, surface_data, keys); state.last_kb_serial = Some(serial);
state.to_focus = Some(data.window.unwrap());
self.server.enter(serial, &data.server, keys);
} }
} }
_ => simple_event_shunt! { _ => simple_event_shunt! {

View file

@ -92,7 +92,7 @@ struct Compositor {
compositor: TestObject<WlCompositor>, compositor: TestObject<WlCompositor>,
shm: TestObject<WlShm>, shm: TestObject<WlShm>,
shell: TestObject<XwaylandShellV1>, shell: TestObject<XwaylandShellV1>,
seat: TestObject<WlSeat> seat: TestObject<WlSeat>,
} }
} }
@ -340,6 +340,12 @@ impl TestFixture {
} }
} }
// Activate keyboard for focus.
TestObject::<WlKeyboard>::from_request(
&ret.seat.as_ref().expect("Seat global missing").obj,
wl_seat::Request::GetKeyboard {},
);
ret.into() ret.into()
} }
@ -566,6 +572,7 @@ impl TestFixture {
self.testwl self.testwl
.configure_toplevel(id, 100, 100, vec![xdg_toplevel::State::Activated]); .configure_toplevel(id, 100, 100, vec![xdg_toplevel::State::Activated]);
self.testwl.focus_toplevel(id);
self.run(); self.run();
{ {
@ -1087,7 +1094,6 @@ fn window_group_properties() {
#[test] #[test]
fn copy_from_x11() { fn copy_from_x11() {
let (mut f, comp) = TestFixture::new_with_compositor(); let (mut f, comp) = TestFixture::new_with_compositor();
TestObject::<WlKeyboard>::from_request(&comp.seat.obj, wl_seat::Request::GetKeyboard {});
let win = unsafe { Window::new(1) }; let win = unsafe { Window::new(1) };
let (_surface, _id) = f.create_toplevel(&comp, win); let (_surface, _id) = f.create_toplevel(&comp, win);
@ -1303,7 +1309,8 @@ fn output_offset() {
); );
} }
f.testwl f.testwl
.configure_toplevel(t_id, 100, 100, vec![xdg_toplevel::State::Activated]); .configure_toplevel(t_id, 100, 100, vec![]);
f.testwl.focus_toplevel(t_id);
f.run(); f.run();
{ {

View file

@ -179,6 +179,7 @@ impl Fixture {
self.testwl self.testwl
.configure_toplevel(surface, 100, 100, vec![xdg_toplevel::State::Activated]); .configure_toplevel(surface, 100, 100, vec![xdg_toplevel::State::Activated]);
self.testwl.focus_toplevel(surface);
self.wait_and_dispatch(); self.wait_and_dispatch();
let geometry = connection let geometry = connection
.wait_for_reply(connection.send_request(&x::GetGeometry { .wait_for_reply(connection.send_request(&x::GetGeometry {

View file

@ -161,6 +161,11 @@ struct Output {
xdg: Option<ZxdgOutputV1>, xdg: Option<ZxdgOutputV1>,
} }
struct KeyboardState {
keyboard: WlKeyboard,
current_focus: Option<SurfaceId>,
}
struct State { struct State {
surfaces: HashMap<SurfaceId, SurfaceData>, surfaces: HashMap<SurfaceId, SurfaceData>,
outputs: HashMap<WlOutput, Output>, outputs: HashMap<WlOutput, Output>,
@ -171,7 +176,7 @@ struct State {
last_output: Option<WlOutput>, last_output: Option<WlOutput>,
callbacks: Vec<WlCallback>, callbacks: Vec<WlCallback>,
pointer: Option<WlPointer>, pointer: Option<WlPointer>,
keyboard: Option<WlKeyboard>, keyboard: Option<KeyboardState>,
configure_serial: u32, configure_serial: u32,
selection: Option<WlDataSource>, selection: Option<WlDataSource>,
data_device_man: Option<WlDataDeviceManager>, data_device_man: Option<WlDataDeviceManager>,
@ -209,15 +214,6 @@ impl State {
states: Vec<xdg_toplevel::State>, states: Vec<xdg_toplevel::State>,
) { ) {
let last_serial = self.configure_serial; let last_serial = self.configure_serial;
if states.contains(&xdg_toplevel::State::Activated) {
if let Some(kb) = &self.keyboard {
kb.enter(
last_serial,
&self.surfaces[&surface_id].surface,
Vec::default(),
);
}
}
let toplevel = self.get_toplevel(surface_id); let toplevel = self.get_toplevel(surface_id);
toplevel.states = states.clone(); toplevel.states = states.clone();
let states: Vec<u8> = states let states: Vec<u8> = states
@ -229,6 +225,38 @@ impl State {
self.configure_serial += 1; self.configure_serial += 1;
} }
#[track_caller]
fn focus_toplevel(&mut self, surface_id: SurfaceId) {
let KeyboardState {
keyboard,
current_focus,
} = self.keyboard.as_mut().expect("Keyboard should be created");
if let Some(id) = current_focus {
keyboard.leave(self.configure_serial, &self.surfaces[id].surface);
}
keyboard.enter(
self.configure_serial,
&self.surfaces[&surface_id].surface,
Vec::default(),
);
*current_focus = Some(surface_id);
}
#[track_caller]
fn unfocus_toplevel(&mut self) {
let KeyboardState {
current_focus,
keyboard,
} = self.keyboard.as_mut().expect("Keyboard should be created");
if let Some(id) = current_focus.take() {
keyboard.leave(self.configure_serial, &self.surfaces[&id].surface);
}
}
#[track_caller] #[track_caller]
pub fn configure_popup(&mut self, surface_id: SurfaceId) { pub fn configure_popup(&mut self, surface_id: SurfaceId) {
let surface = self.surfaces.get_mut(&surface_id).unwrap(); let surface = self.surfaces.get_mut(&surface_id).unwrap();
@ -433,6 +461,18 @@ impl Server {
self.display.flush_clients().unwrap(); self.display.flush_clients().unwrap();
} }
#[track_caller]
pub fn focus_toplevel(&mut self, surface_id: SurfaceId) {
self.state.focus_toplevel(surface_id);
self.display.flush_clients().unwrap();
}
#[track_caller]
pub fn unfocus_toplevel(&mut self) {
self.state.unfocus_toplevel();
self.display.flush_clients().unwrap();
}
#[track_caller] #[track_caller]
pub fn configure_popup(&mut self, surface_id: SurfaceId) { pub fn configure_popup(&mut self, surface_id: SurfaceId) {
self.state.configure_popup(surface_id); self.state.configure_popup(surface_id);
@ -825,7 +865,10 @@ impl Dispatch<WlSeat, ()> for State {
state.pointer = Some(data_init.init(id, ())); state.pointer = Some(data_init.init(id, ()));
} }
wl_seat::Request::GetKeyboard { id } => { wl_seat::Request::GetKeyboard { id } => {
state.keyboard = Some(data_init.init(id, ())); state.keyboard = Some(KeyboardState {
keyboard: data_init.init(id, ()),
current_focus: None,
});
} }
wl_seat::Request::Release => {} wl_seat::Request::Release => {}
other => todo!("unhandled request {other:?}"), other => todo!("unhandled request {other:?}"),
@ -1317,9 +1360,16 @@ impl Dispatch<WlSurface, ()> for State {
} }
Commit => {} Commit => {}
Destroy => { Destroy => {
state let id = SurfaceId(resource.id().protocol_id());
.surfaces if let Some(kb) = state
.remove(&SurfaceId(resource.id().protocol_id())); .keyboard
.as_mut()
.filter(|kb| kb.current_focus == Some(id))
{
kb.keyboard.leave(state.configure_serial, resource);
kb.current_focus.take();
}
state.surfaces.remove(&id);
} }
SetInputRegion { .. } => {} SetInputRegion { .. } => {}
SetBufferScale { .. } => {} SetBufferScale { .. } => {}