Unfocus window on X11 side when keyboard focus is lost
Closes #69 (nice)
This commit is contained in:
parent
a713cf46cb
commit
b0ee6db9fa
4 changed files with 95 additions and 42 deletions
|
|
@ -525,6 +525,24 @@ impl HandleEvent for Keyboard {
|
||||||
self.server.enter(serial, &data.server, keys);
|
self.server.enter(serial, &data.server, keys);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
client::wl_keyboard::Event::Leave { serial, surface } => {
|
||||||
|
if !surface.is_alive() {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
let key: ObjectKey = surface.data().copied().unwrap();
|
||||||
|
if let Some(data) = state
|
||||||
|
.objects
|
||||||
|
.get(key)
|
||||||
|
.map(|o| <_ as AsRef<SurfaceData>>::as_ref(o))
|
||||||
|
{
|
||||||
|
if state.to_focus == Some(data.window.unwrap()) {
|
||||||
|
state.to_focus.take();
|
||||||
|
} else {
|
||||||
|
state.unfocus = true;
|
||||||
|
}
|
||||||
|
self.server.leave(serial, &data.server);
|
||||||
|
}
|
||||||
|
}
|
||||||
_ => simple_event_shunt! {
|
_ => simple_event_shunt! {
|
||||||
self.server, event: client::wl_keyboard::Event => [
|
self.server, event: client::wl_keyboard::Event => [
|
||||||
Keymap {
|
Keymap {
|
||||||
|
|
@ -532,18 +550,6 @@ impl HandleEvent for Keyboard {
|
||||||
|fd| fd.as_fd(),
|
|fd| fd.as_fd(),
|
||||||
size
|
size
|
||||||
},
|
},
|
||||||
Leave {
|
|
||||||
serial,
|
|
||||||
|surface| {
|
|
||||||
if !surface.is_alive() {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
let Some(surface_data) = state.get_server_surface_from_client(surface) else {
|
|
||||||
return;
|
|
||||||
};
|
|
||||||
surface_data
|
|
||||||
}
|
|
||||||
},
|
|
||||||
Key {
|
Key {
|
||||||
serial,
|
serial,
|
||||||
time,
|
time,
|
||||||
|
|
|
||||||
|
|
@ -479,6 +479,7 @@ pub struct ServerState<C: XConnection> {
|
||||||
qh: ClientQueueHandle,
|
qh: ClientQueueHandle,
|
||||||
client: Option<Client>,
|
client: Option<Client>,
|
||||||
to_focus: Option<x::Window>,
|
to_focus: Option<x::Window>,
|
||||||
|
unfocus: bool,
|
||||||
last_focused_toplevel: Option<x::Window>,
|
last_focused_toplevel: Option<x::Window>,
|
||||||
last_hovered: Option<x::Window>,
|
last_hovered: Option<x::Window>,
|
||||||
connection: Option<C>,
|
connection: Option<C>,
|
||||||
|
|
@ -527,6 +528,7 @@ impl<C: XConnection> ServerState<C> {
|
||||||
qh,
|
qh,
|
||||||
dh,
|
dh,
|
||||||
to_focus: None,
|
to_focus: None,
|
||||||
|
unfocus: false,
|
||||||
last_focused_toplevel: None,
|
last_focused_toplevel: None,
|
||||||
last_hovered: None,
|
last_hovered: None,
|
||||||
connection: None,
|
connection: None,
|
||||||
|
|
@ -832,7 +834,12 @@ impl<C: XConnection> ServerState<C> {
|
||||||
debug!("focusing window {win:?}");
|
debug!("focusing window {win:?}");
|
||||||
conn.focus_window(win, data);
|
conn.focus_window(win, data);
|
||||||
self.last_focused_toplevel = Some(win);
|
self.last_focused_toplevel = Some(win);
|
||||||
|
} else if self.unfocus {
|
||||||
|
let data = C::ExtraData::create(self);
|
||||||
|
let conn = self.connection.as_mut().unwrap();
|
||||||
|
conn.focus_window(x::WINDOW_NONE, data);
|
||||||
}
|
}
|
||||||
|
self.unfocus = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
self.handle_clipboard_events();
|
self.handle_clipboard_events();
|
||||||
|
|
|
||||||
|
|
@ -294,13 +294,6 @@ impl XState {
|
||||||
xcb::Event::X(x::Event::UnmapNotify(e)) => {
|
xcb::Event::X(x::Event::UnmapNotify(e)) => {
|
||||||
trace!("unmap event: {:?}", e.event());
|
trace!("unmap event: {:?}", e.event());
|
||||||
server_state.unmap_window(e.window());
|
server_state.unmap_window(e.window());
|
||||||
unwrap_or_skip_bad_window_cont!(self.connection.send_and_check_request(
|
|
||||||
&x::ChangeWindowAttributes {
|
|
||||||
window: e.window(),
|
|
||||||
value_list: &[x::Cw::EventMask(x::EventMask::empty())],
|
|
||||||
}
|
|
||||||
));
|
|
||||||
|
|
||||||
let active_win = self
|
let active_win = self
|
||||||
.connection
|
.connection
|
||||||
.wait_for_reply(self.get_property_cookie(
|
.wait_for_reply(self.get_property_cookie(
|
||||||
|
|
@ -313,16 +306,19 @@ impl XState {
|
||||||
|
|
||||||
let active_win: &[x::Window] = active_win.value();
|
let active_win: &[x::Window] = active_win.value();
|
||||||
if active_win[0] == e.window() {
|
if active_win[0] == e.window() {
|
||||||
self.connection
|
<_ as super::XConnection>::focus_window(
|
||||||
.send_and_check_request(&x::ChangeProperty {
|
&mut self.connection,
|
||||||
mode: x::PropMode::Replace,
|
x::Window::none(),
|
||||||
window: self.root,
|
self.atoms.clone(),
|
||||||
property: self.atoms.active_win,
|
);
|
||||||
r#type: x::ATOM_WINDOW,
|
|
||||||
data: &[x::Window::none()],
|
|
||||||
})
|
|
||||||
.unwrap();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
unwrap_or_skip_bad_window_cont!(self.connection.send_and_check_request(
|
||||||
|
&x::ChangeWindowAttributes {
|
||||||
|
window: e.window(),
|
||||||
|
value_list: &[x::Cw::EventMask(x::EventMask::empty())],
|
||||||
|
}
|
||||||
|
));
|
||||||
}
|
}
|
||||||
xcb::Event::X(x::Event::DestroyNotify(e)) => {
|
xcb::Event::X(x::Event::DestroyNotify(e)) => {
|
||||||
debug!("destroying window {:?}", e.window());
|
debug!("destroying window {:?}", e.window());
|
||||||
|
|
|
||||||
|
|
@ -234,6 +234,7 @@ impl Fixture {
|
||||||
xcb::atoms_struct! {
|
xcb::atoms_struct! {
|
||||||
struct Atoms {
|
struct Atoms {
|
||||||
wm_protocols => b"WM_PROTOCOLS",
|
wm_protocols => b"WM_PROTOCOLS",
|
||||||
|
net_active_window => b"_NET_ACTIVE_WINDOW",
|
||||||
wm_delete_window => b"WM_DELETE_WINDOW",
|
wm_delete_window => b"WM_DELETE_WINDOW",
|
||||||
clipboard => b"CLIPBOARD",
|
clipboard => b"CLIPBOARD",
|
||||||
targets => b"TARGETS",
|
targets => b"TARGETS",
|
||||||
|
|
@ -489,22 +490,65 @@ fn input_focus() {
|
||||||
let mut f = Fixture::new();
|
let mut f = Fixture::new();
|
||||||
let mut connection = Connection::new(&f.display);
|
let mut connection = Connection::new(&f.display);
|
||||||
|
|
||||||
let win = connection.new_window(connection.root, 0, 0, 20, 20, false);
|
let conn = std::cell::RefCell::new(&mut connection);
|
||||||
connection.map_window(win);
|
let check_focus = |win: x::Window| {
|
||||||
|
let connection = conn.borrow();
|
||||||
|
let focus = connection
|
||||||
|
.wait_for_reply(connection.send_request(&x::GetInputFocus {}))
|
||||||
|
.unwrap()
|
||||||
|
.focus();
|
||||||
|
assert_eq!(win, focus);
|
||||||
|
|
||||||
|
let reply = connection
|
||||||
|
.wait_for_reply(connection.send_request(&x::GetProperty {
|
||||||
|
delete: false,
|
||||||
|
window: connection.root,
|
||||||
|
property: connection.atoms.net_active_window,
|
||||||
|
r#type: x::ATOM_WINDOW,
|
||||||
|
long_offset: 0,
|
||||||
|
long_length: 1,
|
||||||
|
}))
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
assert_eq!(&[win], reply.value::<x::Window>());
|
||||||
|
};
|
||||||
|
|
||||||
|
let mut create_win = || {
|
||||||
|
let mut connection = conn.borrow_mut();
|
||||||
|
let win = connection.new_window(connection.root, 0, 0, 20, 20, false);
|
||||||
|
connection.map_window(win);
|
||||||
|
f.wait_and_dispatch();
|
||||||
|
let surface = f
|
||||||
|
.testwl
|
||||||
|
.last_created_surface_id()
|
||||||
|
.expect("No surface created!");
|
||||||
|
f.configure_and_verify_new_toplevel(&mut connection, win, surface);
|
||||||
|
(win, surface)
|
||||||
|
};
|
||||||
|
|
||||||
|
let (win1, surface1) = create_win();
|
||||||
|
check_focus(win1);
|
||||||
|
let (win2, surface2) = create_win();
|
||||||
|
check_focus(win2);
|
||||||
|
|
||||||
|
f.testwl.focus_toplevel(surface1);
|
||||||
|
// Seems the event doesn't get caught by wait_and_dispatch...
|
||||||
|
std::thread::sleep(std::time::Duration::from_millis(10));
|
||||||
|
check_focus(win1);
|
||||||
|
|
||||||
|
f.testwl.unfocus_toplevel();
|
||||||
|
std::thread::sleep(std::time::Duration::from_millis(10));
|
||||||
|
check_focus(x::WINDOW_NONE);
|
||||||
|
|
||||||
|
f.testwl.focus_toplevel(surface2);
|
||||||
|
std::thread::sleep(std::time::Duration::from_millis(10));
|
||||||
|
check_focus(win2);
|
||||||
|
|
||||||
|
conn.borrow().destroy_window(win2);
|
||||||
f.wait_and_dispatch();
|
f.wait_and_dispatch();
|
||||||
let surface = f
|
check_focus(x::WINDOW_NONE);
|
||||||
.testwl
|
|
||||||
.last_created_surface_id()
|
|
||||||
.expect("No surface created!");
|
|
||||||
f.configure_and_verify_new_toplevel(&mut connection, win, surface);
|
|
||||||
|
|
||||||
let focus = connection
|
f.wm_delete_window(&mut connection, win1, surface1);
|
||||||
.wait_for_reply(connection.send_request(&x::GetInputFocus {}))
|
|
||||||
.unwrap()
|
|
||||||
.focus();
|
|
||||||
assert_eq!(win, focus);
|
|
||||||
|
|
||||||
f.wm_delete_window(&mut connection, win, surface);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue