Set input focus on windows without WM_HINTS input flag

Also miscellaneous formatting things.
Part of #8
This commit is contained in:
Shawn Wallace 2024-05-20 01:26:06 -04:00
parent 38e58bd11d
commit 2317ebb842
4 changed files with 99 additions and 24 deletions

View file

@ -4,9 +4,9 @@ use std::os::unix::net::UnixStream;
use std::sync::mpsc; use std::sync::mpsc;
use wayland_client::protocol::{ use wayland_client::protocol::{
wl_buffer::WlBuffer, wl_callback::WlCallback, wl_compositor::WlCompositor, wl_buffer::WlBuffer, wl_callback::WlCallback, wl_compositor::WlCompositor,
wl_keyboard::WlKeyboard, wl_output::WlOutput, wl_pointer::WlPointer, wl_registry::WlRegistry, wl_keyboard::WlKeyboard, wl_output::WlOutput, wl_pointer::WlPointer, wl_region::WlRegion,
wl_seat::WlSeat, wl_shm::WlShm, wl_shm_pool::WlShmPool, wl_surface::WlSurface, wl_registry::WlRegistry, wl_seat::WlSeat, wl_shm::WlShm, wl_shm_pool::WlShmPool,
wl_touch::WlTouch, wl_region::WlRegion wl_surface::WlSurface, wl_touch::WlTouch,
}; };
use wayland_client::{delegate_noop, Connection, Dispatch, EventQueue, Proxy, QueueHandle}; use wayland_client::{delegate_noop, Connection, Dispatch, EventQueue, Proxy, QueueHandle};
use wayland_protocols::wp::relative_pointer::zv1::client::{ use wayland_protocols::wp::relative_pointer::zv1::client::{

View file

@ -336,7 +336,7 @@ impl XState {
}, },
title, title,
class, class,
group: wm_hints.map(|h| h.window_group).flatten(), group: wm_hints.and_then(|h| h.window_group),
size_hints, size_hints,
} }
} }
@ -640,6 +640,9 @@ impl From<&[u32]> for WmHints {
let mut ret = Self::default(); let mut ret = Self::default();
let flags = WmHintsFlags::from_bits_truncate(value[0]); let flags = WmHintsFlags::from_bits_truncate(value[0]);
if flags.contains(WmHintsFlags::Input) {
ret.input = Some(value[1] == 1);
}
if flags.contains(WmHintsFlags::WindowGroup) { if flags.contains(WmHintsFlags::WindowGroup) {
let window = unsafe { x::Window::new(value[8]) }; let window = unsafe { x::Window::new(value[8]) };
ret.window_group = Some(window); ret.window_group = Some(window);
@ -713,20 +716,17 @@ impl super::XConnection for Arc<xcb::Connection> {
property: x::ATOM_WM_HINTS, property: x::ATOM_WM_HINTS,
r#type: x::ATOM_WM_HINTS, r#type: x::ATOM_WM_HINTS,
long_offset: 0, long_offset: 0,
long_length: 8, long_length: 9,
})) }))
.unwrap(); .unwrap();
let fields: &[u32] = prop.value(); let set_focus = if prop.r#type() == x::ATOM_NONE {
let mut input = false; true
if !fields.is_empty() { } else {
let flags = fields[0]; WmHints::from(prop.value()).input.unwrap_or(true)
if (flags & 0x1) > 0 { };
input = fields[1] > 0;
}
}
if input { if set_focus {
// might fail if window is not visible but who cares // might fail if window is not visible but who cares
let _ = self.send_and_check_request(&x::SetInputFocus { let _ = self.send_and_check_request(&x::SetInputFocus {
focus: window, focus: window,

View file

@ -12,7 +12,7 @@ use wayland_protocols::xdg::shell::server::xdg_toplevel;
use wayland_server::Resource; use wayland_server::Resource;
use xcb::{x, Xid}; use xcb::{x, Xid};
use xwayland_satellite as xwls; use xwayland_satellite as xwls;
use xwayland_satellite::xstate::WmSizeHintsFlags; use xwayland_satellite::xstate::{WmHintsFlags, WmSizeHintsFlags};
#[derive(Default)] #[derive(Default)]
struct TestDataInner { struct TestDataInner {
@ -300,6 +300,11 @@ impl Connection {
.unwrap(); .unwrap();
} }
fn unmap_window(&self, window: x::Window) {
self.send_and_check_request(&x::UnmapWindow { window })
.unwrap();
}
fn set_property<P: x::PropEl>( fn set_property<P: x::PropEl>(
&self, &self,
window: x::Window, window: x::Window,
@ -423,7 +428,7 @@ fn toplevel_flow() {
#[test] #[test]
fn reparent() { fn reparent() {
let mut f = Fixture::new(); let mut f = Fixture::new();
let connection = Connection::new(&f.display); let mut connection = Connection::new(&f.display);
let parent = connection.new_window(connection.root, 0, 0, 1, 1, false); let parent = connection.new_window(connection.root, 0, 0, 1, 1, false);
let child = connection.new_window(parent, 0, 0, 20, 20, false); let child = connection.new_window(parent, 0, 0, 20, 20, false);
@ -437,9 +442,80 @@ fn reparent() {
}) })
.unwrap(); .unwrap();
connection connection.map_window(child);
.send_and_check_request(&x::MapWindow { window: child })
.unwrap();
f.wait_and_dispatch(); f.wait_and_dispatch();
let surface = f
.testwl
.last_created_surface_id()
.expect("No surface created!");
f.configure_and_verify_new_toplevel(&mut connection, child, surface);
}
#[test]
fn input_focus() {
let mut f = Fixture::new();
let mut connection = Connection::new(&f.display);
fn tst(
f: &mut Fixture,
connection: &mut Connection,
set_props: impl FnOnce(&mut Connection, x::Window),
check: impl FnOnce(/* win: */ x::Window, /* focus: */ x::Window),
) {
let win = connection.new_window(connection.root, 0, 0, 20, 20, false);
set_props(connection, win);
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(connection, win, surface);
let focus = connection
.wait_for_reply(connection.send_request(&x::GetInputFocus {}))
.unwrap()
.focus();
check(win, focus);
f.close_toplevel(connection, win, surface);
}
// Input field unset
tst(
&mut f,
&mut connection,
|_, _| {},
|win, focus| assert_eq!(win, focus),
);
// Input field set to false
tst(
&mut f,
&mut connection,
|connection, win| {
connection.set_property(
win,
x::ATOM_WM_HINTS,
x::ATOM_WM_HINTS,
&[WmHintsFlags::Input.bits(), 0],
);
},
|win, focus| assert_ne!(win, focus),
);
// Input field set to true
tst(
&mut f,
&mut connection,
|connection, win| {
connection.set_property(
win,
x::ATOM_WM_HINTS,
x::ATOM_WM_HINTS,
&[WmHintsFlags::Input.bits(), 1],
);
},
|win, focus| assert_eq!(win, focus),
);
} }

View file

@ -2,8 +2,8 @@
pub mod client { pub mod client {
use wayland_client::{self, protocol::*}; use wayland_client::{self, protocol::*};
pub mod __interfaces { pub mod __interfaces {
use wayland_client::protocol::__interfaces::*;
use wayland_client::backend as wayland_backend; use wayland_client::backend as wayland_backend;
use wayland_client::protocol::__interfaces::*;
wayland_scanner::generate_interfaces!("src/drm.xml"); wayland_scanner::generate_interfaces!("src/drm.xml");
} }
use self::__interfaces::*; use self::__interfaces::*;
@ -11,9 +11,8 @@ pub mod client {
} }
pub mod server { pub mod server {
use wayland_server::{self, protocol::*};
pub use super::client::__interfaces;
use self::__interfaces::*; use self::__interfaces::*;
pub use super::client::__interfaces;
use wayland_server::{self, protocol::*};
wayland_scanner::generate_server_code!("src/drm.xml"); wayland_scanner::generate_server_code!("src/drm.xml");
} }