-
Notifications
You must be signed in to change notification settings - Fork 341
backend/x11: only send frames when the window is visible #2682
base: master
Are you sure you want to change the base?
Conversation
Hm. It seems like the UnmapNotify event doesn't happen on i3. Instead, it seems like i3 uses https://github.com/i3/i3/blob/dcd6079c9bb5e06716a53b90f8539715bb194eeb/src/x.c#L751 Does WindowMaker do this as well? |
backend/x11/output.c
Outdated
@@ -579,7 +579,8 @@ void handle_x11_present_event(struct wlr_x11_backend *x11, | |||
}; | |||
wlr_output_send_present(&output->wlr_output, &present_event); | |||
|
|||
wlr_output_send_frame(&output->wlr_output); | |||
if (!output->hidden) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Style nit: braces are mandatory
According to the code, yes. But I don't see it in x11trace, perhaps I haven't enabled something. Here's what the ICCCM spec says:
So we should probably listen to both. That said, I can't quite figure out how to listen to _NET_WM_STATE stuff. Will require some digging into X APIs. |
Have you added |
Ah I see. So then I get _NET_WM_STATE notifications, and then I have to run XGetWindowProperty to get the hidden state? Delightful... |
OK, so experiments suggest that _NET_WM_STATE_HIDDEN is only set when miniaturizing the window in WindowMaker. When switching workspaces, I only see UnmapNotify. When "rolling up" the window, I do see a _NET_WM_STATE change, but HIDDEN is not set. Happy to include this logic though. |
Looks like we have to keep track of the Map/Unmap state separate from the STATE_HIDDEN state, and "and" them. In the roll-up case, I get an unmap followed by some sort of _NET_WM_STATE change which does not include the HIDDEN atom. So we have to be careful not to decide to start displaying stuff... |
Tested with WindowMaker. When switching to another workspace, minimizing, or rolling up the window, we get an UnmapNotify event, and a corresponding MapNotify event when the window appears on screen again. Fixes swaywm#2675
Some window managers don't unmap windows when they're hidden. Also check the _NET_WM_STATE property for _NET_WM_STATE_HIDDEN. This way if the window is hidden or unmapped, then we won't send updates.
I believe this should work correctly now. Mapped and hidden state are now kept track of separately. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Logic looks good to me, here are a few comments.
@@ -740,7 +743,8 @@ void handle_x11_present_event(struct wlr_x11_backend *x11, | |||
}; | |||
wlr_output_send_present(&output->wlr_output, &present_event); | |||
|
|||
wlr_output_send_frame(&output->wlr_output); | |||
if (output->mapped && !output->hidden) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nit: braces are mandatory
@@ -600,6 +601,8 @@ struct wlr_output *wlr_x11_output_create(struct wlr_backend *backend) { | |||
xcb_map_window(x11->xcb, output->win); | |||
xcb_flush(x11->xcb); | |||
|
|||
output->mapped = true; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I wonder if we should wait for MapNotify
here?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We won't get it. We only insert the window into the list after this happens.
ev->atom, XCB_ATOM_ATOM, 0, 32); | ||
xcb_get_property_reply_t *reply = | ||
xcb_get_property_reply(x11->xcb, cookie, NULL); | ||
if (reply->type != XCB_ATOM_ATOM) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We should probably check reply != NULL
too. And log an error message if something goes wrong, to make sure we don't miss these kind of errors.
xcb_get_property_cookie_t cookie = | ||
xcb_get_property(x11->xcb, false, ev->window, | ||
ev->atom, XCB_ATOM_ATOM, 0, 32); | ||
xcb_get_property_reply_t *reply = |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The spec says:
The Window Manager should remove the property whenever a window is withdrawn
So we probably need to handle the case where the property is removed, and reset hidden
to false. xcb_property_notify_event_t.state
indicates whether a property is deleted.
for (int i = 0; i < xcb_get_property_value_length(reply); i++) { | ||
if (atoms[i] == x11->atoms.net_wm_state_hidden) { | ||
wlr_log(WLR_DEBUG, "Window hidden, stopping updates"); | ||
output->hidden = true; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nit: can add a break
here.
Gentle ping: do you have plans to update this PR? |
To be honest, I don't know -- this is all getting into the territory of stuff I can't really test. My WM doesn't really do these things. I'd actually rather go back to just doing UnmapNotify/MapNotify and leaving the WM state stuff to someone who can really test that out properly. What do you think? |
Might I suggest using These events contain a window and one of "Unobscured", "PartiallyObscured" or "FullyObscured". You need to select for VisibilityChange on the window to get these events. Relevant names for xcb seem to be:
|
That sounds nice in theory. In practice, I don't get these. See #2675 (comment) . |
Hm, okay... I only found https://gitlab.freedesktop.org/xorg/xserver/-/issues/922 which is about "when a compositor is running". I guess this does not apply to your case (or are you running WindowManager plus a compositing manager?) |
I'm only running WindowMaker. No compositors, no funny business. Same setup as I had in ~1998 :) |
I suspect there's no VisibilityNotify since the window is just plain unmapped. It's not obscured -- it's just gone. |
This looks fine to me! |
I won't (and can't) stop you, but... in a reparenting WM, I wouldn't expect any I just tried running
(That I tried finding something relevant in ICCCM, but... yeah. i3 violates § 4.2.5 by not unmapping the window when going to iconic state:
§ 4.1.3.1 seems to say that a window in Normal state and in Withdrawn state can still be visible / be able to redraw itself:
TL;DR: I don't really have anything useful to say and X11 is complicated. |
Without commenting on how window managers are supposed to work, here is how WindowMaker works on e.g. a workspace change: https://repo.or.cz/wmaker-crm.git/blob/HEAD:/src/workspace.c#l510 As you can see it unmaps all the current workspace's windows. (And wWindowUnmap is what you'd expect - just a XUnmapWindow wrapper.) |
Sorry :-( I took another look at Xorg's source code and I think I found some missing piece of information: if (pChild->viewable) {
oldVis = pChild->visibility;
if (oldVis != (pChild->visibility = VisibilityFullyObscured) &&
((pChild->
eventMask | wOtherEventMasks(pChild)) & VisibilityChangeMask))
SendVisibilityNotify(pChild); This code completely ignores windows where
This would explain the behaviour you are seeing.
This is also fine with me. If you |
So X11 clients are supposed to check 3 different pieces of information (map state, visibility, _net_wm_hidden) to figure out whether they need to continue rendering new frames? |
wlroots has migrated to gitlab.freedesktop.org. This pull request has been moved to: https://gitlab.freedesktop.org/wlroots/wlroots/-/merge_requests/2682 |
Tested with WindowMaker. When switching to another workspace,
minimizing, or rolling up the window, we get an UnmapNotify event, and a
corresponding MapNotify event when the window appears on screen again.
Fixes #2675