Steam big picture gets no keyboard input
Testing on Anvil on all backends available.
If you launch steam as steam -tenfoot or launch normally and switch to big-picture mode the window doesn't accept any keypresses.
Log shows keys are registered by Anvil and all other apps tested work as expected
Per this issue https://github.com/ValveSoftware/steam-for-linux/issues/4924, this is probably an issue with XWayland?
This issue is not across all XWayland, does not happen on wayfire via XWayland.
This issue is not across all XWayland, does not happen on wayfire via XWayland.
XWayland in this case means the smithay integration with XWayland if it was a little confusing :)
I can confirm this is an issue, recorded anvil using obs in the x11 backend
https://user-images.githubusercontent.com/30619168/143513205-4da486cc-f714-4f1c-92a2-3a147dc2f447.mp4
I try typing into the big picture browser at 1 min, there are also a few other bugs I find on the path to get into big picture by just trying to do things.
@psychon probably understands xwayland in smithay a lot better than I do.
Well, the code in anvil/src/xwayland/mod.rs basically "does not do anything". For this case: Nothing ever does anything with the x11 input focus, so things end up in the default focus-follows-mouse mode. I do not know specifically why this case doesn't work with steam and I am not going to install steam to try to figure this out (could someone run xprop in a terminal, click on the steam fullscreen window and fetch the xprop output? Any long _NET_WM_ICON output can be skipped. I am specifically curious about WM_HINTS and WM_PROTOCOLS. (No idea how to run something in a terminal while another window is fullscreen. sleep 10 ; xprop?))
X11 being x11, there are four different "input models", where two of them expect help from the WM to get the input focus: https://tronche.com/gui/x/icccm/sec-4.html#s-4.1.7 [At first I typed an explanation of the four models here, but then deleted it again because no one cares]
(Oh and: Nothing ever does anything with the stacking order, so it could happen that another window is "on top" in X11 than what anvil actually renders on top).
Basically: Something needs to do/be an actual window manager for lots of things to work properly. I can do the X11 parts (or help someone in filling them in), but sadly I do not really have the time to do much. The current WM in anvil doesn't even keep any state, so it does not know which windows exist! And AFAIR the user cannot move/resize windows.
_NET_WM_USER_TIME(CARDINAL) = 630306
STEAM_BIGPICTURE(CARDINAL) = 1
_NET_WM_ICON(CARDINAL) = Icon (48 x 48):
(not shown)
_NET_WM_STATE(ATOM) = _NET_WM_STATE_FULLSCREEN, _NET_WM_STATE_FOCUSED
WM_STATE(WM_STATE):
window state: Normal
icon window: 0x0
XdndAware(ATOM) = BITMAP
_NET_WM_NAME(UTF8_STRING) = "Steam"
WM_NAME(UTF8_STRING) = "Steam"
WM_PROTOCOLS(ATOM): protocols WM_DELETE_WINDOW, WM_TAKE_FOCUS, _NET_WM_PING
_NET_WM_WINDOW_TYPE(ATOM) = _NET_WM_WINDOW_TYPE_NORMAL
_NET_WM_PID(CARDINAL) = 8677
WM_LOCALE_NAME(STRING) = "en_US.UTF-8"
WM_CLASS(STRING) = "steam", "steam"
WM_HINTS(WM_HINTS):
Client accepts input or input focus: True
window id # of group leader: 0x57e77205
WM_NORMAL_HINTS(WM_SIZE_HINTS):
program specified minimum aspect ratio: 1066/600
program specified maximum aspect ratio: 1066/600
WM_CLIENT_MACHINE(STRING) = "triggtop"
_MOTIF_WM_HINTS(_MOTIF_WM_HINTS) = 0x2, 0x0, 0x0, 0x0, 0x0
(Oh and: Nothing ever does anything with the stacking order, so it could happen that another window is "on top" in X11 than what anvil actually renders on top).
In my case this shouldn't be the problem as I only ever draw the first window in the list,
but sadly I do not really have the time to do much
I completely understand that and thanks for looking into this!
WM_PROTOCOLS(ATOM): protocols WM_DELETE_WINDOW, WM_TAKE_FOCUS, _NET_WM_PING
Yup, this window expects a WM_TAKE_FOCUS message from the WM to get the input focus.
WM_HINTS(WM_HINTS): Client accepts input or input focus: True
It also expects the WM to focus the window "by itself". This part might actually already work (more or less) through the default "focus follows mouse" mode, but this will likely break as sooner or later (as soon as something actually sets the input focus and WM_TAKE_FOCUS allows clients to do this).
The WM_TAKE_FOCUS part "just" needs a call similar to this: https://github.com/psychon/x11rb/blob/d661b0ca04033b806dcb1f38619776ad34f281df/examples/simple_window_manager.rs#L359-L366
(Plus, in the context of anvil, a conn.flush()? afterwards might be necessary.)
But instead of using WM_DELETE_WINDOW, this part of ICCCM needs to be implemented: https://tronche.com/gui/x/icccm/sec-4.html#s-4.1.7
Windows with the atom WM_TAKE_FOCUS in their WM_PROTOCOLS property may receive a ClientMessage event from the window manager (as described in section 4.2.8) with WM_TAKE_FOCUS in its data[0] field and a valid timestamp (i.e. not CurrentTime ) in its data[1] field.
I know that the spec forbids CurrentTime, but for a first draft, just using CurrentTime aka x11rb::CURRENT_TIME aka 0 might actually be enough. Doing this properly is a tiny bit more complicated, because "time" is more like "monotonic counter that the X11 server manages and that has nothing to do with actual time".
So, untested draft that just needs someone to figure out how to find the right X11 window id for the call (aka "the hard part is missing" since anvil's WM does not keep any state about windows currently):
let wm_protocols = conn.intern_atom(false, b"WM_PROTOCOLS")?.reply()?.atom;
let wm_delete_window = conn.intern_atom(false, b"WM_DELETE_WINDOW")?.reply()?.atom;
let event = ClientMessageEvent::new(32, window, wm_protocols, [wm_delete_window, 0, 0, 0, 0]);
conn.send_event(false, window, EventMask::NO_EVENT, &event)?;
conn.flush();
For doing this properly, I would recommend using https://docs.rs/x11rb/0.9.0/x11rb/macro.atom_manager.html instead of doing needless round-trips for interning these atoms when they are needed, but for a first draft the above would be enough.
The proper way to find the window to focus, would be to keep some map between Wayland surface ID and X11 window ID and use that. A quick first hack might be to just focus the window that was opened last. And to send it a WM_TAKE_FOCUS unconditionally, no matter what input mode it actually has. That would be done here (after mapping the window): https://github.com/Smithay/smithay/blob/a8bc2f4a50eebbd95fa5578aa92510f6ea884825/anvil/src/xwayland/mod.rs#L163
Edit: And if someone wants to actually check whether a window has WM_TAKE_FOCUS in its WM_PROTOCOLS property, a quick hack would be something like this condition that checks whether WM_TAKE_FOCUS appears in WM_PROTOCOLS:
conn
.get_property(false, window, the_atom_for_WM_PROTOCOLS, x11rb::protocol::xproto::AtomEnum::ATOM, 0, 1234)?
.reply()?
.value32()
.map(|iter| iter.any(|atom| atom == the_atom_for_WM_TAKE_FOCUS)) == Ok(true)
(Edit: Perhaps better u32::max instead of 1234? Doesn't really matter..)
should this issue be closed? all prs addressing the issue are closed or merged. (I could be wrong, though)
My apologies I've not been keeping up with my bug reports lately.
This works in all my tests. Thanks!