From 04d448317acd1d240b52e1c8d5de861cac7bdd35 Mon Sep 17 00:00:00 2001 From: Ilia Mirkin Date: Sat, 23 Jan 2021 11:37:22 -0500 Subject: [PATCH 1/2] backend/x11: only send frames when the window is mapped 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 --- backend/x11/backend.c | 24 +++++++++++++++++++++--- backend/x11/output.c | 5 ++++- include/backend/x11.h | 1 + 3 files changed, 26 insertions(+), 4 deletions(-) diff --git a/backend/x11/backend.c b/backend/x11/backend.c index 482524c98..8aebf19b4 100644 --- a/backend/x11/backend.c +++ b/backend/x11/backend.c @@ -116,14 +116,32 @@ static void handle_x11_event(struct wlr_x11_backend *x11, } break; } + case XCB_UNMAP_NOTIFY: { + xcb_unmap_notify_event_t *ev = (xcb_unmap_notify_event_t *)event; + struct wlr_x11_output *output = + get_x11_output_from_window_id(x11, ev->window); + if (output != NULL) { + wlr_log(WLR_DEBUG, "Window unmapped, stopping updates"); + output->mapped = false; + } + break; + } + case XCB_MAP_NOTIFY: { + xcb_unmap_notify_event_t *ev = (xcb_unmap_notify_event_t *)event; + struct wlr_x11_output *output = + get_x11_output_from_window_id(x11, ev->window); + if (output != NULL) { + wlr_log(WLR_DEBUG, "Window mapped, resuming updates"); + output->mapped = true; + wlr_output_send_frame(&output->wlr_output); + } + break; + } case 0: { xcb_value_error_t *ev = (xcb_value_error_t *)event; handle_x11_error(x11, ev); break; } - case XCB_UNMAP_NOTIFY: - case XCB_MAP_NOTIFY: - break; default: handle_x11_unknown_event(x11, event); break; diff --git a/backend/x11/output.c b/backend/x11/output.c index 73723fc4e..68bcc03b0 100644 --- a/backend/x11/output.c +++ b/backend/x11/output.c @@ -600,6 +600,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; + wl_list_insert(&x11->outputs, &output->link); wlr_output_update_enabled(wlr_output, true); @@ -740,7 +742,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) + wlr_output_send_frame(&output->wlr_output); break; default: wlr_log(WLR_DEBUG, "Unhandled Present event %"PRIu16, event->event_type); diff --git a/include/backend/x11.h b/include/backend/x11.h index 3dfe2fe8a..6349bece2 100644 --- a/include/backend/x11.h +++ b/include/backend/x11.h @@ -51,6 +51,7 @@ struct wlr_x11_output { pixman_region32_t exposed; uint64_t last_msc; + bool mapped; struct { struct wlr_swapchain *swapchain; From df8d12fa030f0c1aad3fc27da1fe4f9a1afcaf4f Mon Sep 17 00:00:00 2001 From: Ilia Mirkin Date: Mon, 25 Jan 2021 18:37:46 -0500 Subject: [PATCH 2/2] backend/x11: listen to _NET_WM_STATE changes for determining hidden 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. --- backend/x11/backend.c | 44 +++++++++++++++++++++++++++++++++++++++++-- backend/x11/output.c | 5 +++-- include/backend/x11.h | 3 +++ 3 files changed, 48 insertions(+), 4 deletions(-) diff --git a/backend/x11/backend.c b/backend/x11/backend.c index 8aebf19b4..b48b3f06f 100644 --- a/backend/x11/backend.c +++ b/backend/x11/backend.c @@ -131,9 +131,47 @@ static void handle_x11_event(struct wlr_x11_backend *x11, struct wlr_x11_output *output = get_x11_output_from_window_id(x11, ev->window); if (output != NULL) { - wlr_log(WLR_DEBUG, "Window mapped, resuming updates"); + wlr_log(WLR_DEBUG, "Window mapped%s", !output->hidden ? ", resuming updates" : ""); output->mapped = true; - wlr_output_send_frame(&output->wlr_output); + if (!output->hidden) { + wlr_output_send_frame(&output->wlr_output); + } + } + break; + } + case XCB_PROPERTY_NOTIFY: { + xcb_property_notify_event_t *ev = (xcb_property_notify_event_t *)event; + struct wlr_x11_output *output = + get_x11_output_from_window_id(x11, ev->window); + if (output == NULL) { + break; + } + if (ev->atom == x11->atoms.net_wm_state) { + 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 = + xcb_get_property_reply(x11->xcb, cookie, NULL); + if (reply->type != XCB_ATOM_ATOM) { + free(reply); + break; + } + xcb_atom_t *atoms = xcb_get_property_value(reply); + bool was_hidden = output->hidden; + output->hidden = false; + 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; + } + } + free(reply); + if (was_hidden && !output->hidden) { + wlr_log(WLR_DEBUG, "Window no longer hidden%s", output->mapped ? ", resuming updates" : ""); + if (output->mapped) { + wlr_output_send_frame(&output->wlr_output); + } + } } break; } @@ -432,6 +470,8 @@ struct wlr_backend *wlr_x11_backend_create(struct wl_display *display, } atom[] = { { .name = "WM_PROTOCOLS", .atom = &x11->atoms.wm_protocols }, { .name = "WM_DELETE_WINDOW", .atom = &x11->atoms.wm_delete_window }, + { .name = "_NET_WM_STATE", .atom = &x11->atoms.net_wm_state }, + { .name = "_NET_WM_STATE_HIDDEN", .atom = &x11->atoms.net_wm_state_hidden }, { .name = "_NET_WM_NAME", .atom = &x11->atoms.net_wm_name }, { .name = "UTF8_STRING", .atom = &x11->atoms.utf8_string }, { .name = "_VARIABLE_REFRESH", .atom = &x11->atoms.variable_refresh }, diff --git a/backend/x11/output.c b/backend/x11/output.c index 68bcc03b0..6a1506896 100644 --- a/backend/x11/output.c +++ b/backend/x11/output.c @@ -560,7 +560,8 @@ struct wlr_output *wlr_x11_output_create(struct wlr_backend *backend) { XCB_CW_COLORMAP | XCB_CW_CURSOR; uint32_t values[] = { 0, - XCB_EVENT_MASK_EXPOSURE | XCB_EVENT_MASK_STRUCTURE_NOTIFY, + XCB_EVENT_MASK_EXPOSURE | XCB_EVENT_MASK_STRUCTURE_NOTIFY | + XCB_EVENT_MASK_PROPERTY_CHANGE, x11->colormap, x11->transparent_cursor, }; @@ -742,7 +743,7 @@ void handle_x11_present_event(struct wlr_x11_backend *x11, }; wlr_output_send_present(&output->wlr_output, &present_event); - if (output->mapped) + if (output->mapped && !output->hidden) wlr_output_send_frame(&output->wlr_output); break; default: diff --git a/include/backend/x11.h b/include/backend/x11.h index 6349bece2..8ac262cc4 100644 --- a/include/backend/x11.h +++ b/include/backend/x11.h @@ -52,6 +52,7 @@ struct wlr_x11_output { uint64_t last_msc; bool mapped; + bool hidden; struct { struct wlr_swapchain *swapchain; @@ -99,6 +100,8 @@ struct wlr_x11_backend { xcb_atom_t wm_protocols; xcb_atom_t wm_delete_window; xcb_atom_t net_wm_name; + xcb_atom_t net_wm_state; + xcb_atom_t net_wm_state_hidden; xcb_atom_t utf8_string; xcb_atom_t variable_refresh; } atoms;