diff --git a/hw/xwayland/xwayland.c b/hw/xwayland/xwayland.c index 9d795544d..ed6003565 100644 --- a/hw/xwayland/xwayland.c +++ b/hw/xwayland/xwayland.c @@ -27,6 +27,7 @@ #include +#include #include #include #include @@ -34,6 +35,7 @@ #include #include #include +#include #ifdef XF86VIDMODE #include @@ -115,6 +117,94 @@ xwl_screen_get(ScreenPtr screen) return dixLookupPrivate(&screen->devPrivates, &xwl_screen_private_key); } +static void +xwl_window_set_allow_commits(struct xwl_window *xwl_window, Bool allow, + const char *debug_msg) +{ + xwl_window->allow_commits = allow; + DebugF("xwayland: win %d allow_commits = %d (%s)\n", + xwl_window->window->drawable.id, allow, debug_msg); +} + +static void +xwl_window_set_allow_commits_from_property(struct xwl_window *xwl_window, + PropertyPtr prop) +{ + static Bool warned = FALSE; + CARD32 *propdata; + + if (prop->propertyName != xwl_window->xwl_screen->allow_commits_prop) + FatalError("Xwayland internal error: prop mismatch in %s.\n", __func__); + + if (prop->type != XA_CARDINAL || prop->format != 32 || prop->size != 1) { + /* Not properly set, so fall back to safe and glitchy */ + xwl_window_set_allow_commits(xwl_window, TRUE, "WM fault"); + + if (!warned) { + LogMessage(X_WARNING, "Window manager is misusing property %s.\n", + NameForAtom(prop->propertyName)); + warned = TRUE; + } + return; + } + + propdata = prop->data; + xwl_window_set_allow_commits(xwl_window, !!propdata[0], "from property"); +} + +static void +xwl_window_property_allow_commits(struct xwl_window *xwl_window, + PropertyStateRec *propstate) +{ + Bool old_allow_commits = xwl_window->allow_commits; + + switch (propstate->state) { + case PropertyNewValue: + xwl_window_set_allow_commits_from_property(xwl_window, propstate->prop); + break; + + case PropertyDelete: + xwl_window_set_allow_commits(xwl_window, TRUE, "property deleted"); + break; + + default: + break; + } + + /* If allow_commits turned from off to on, discard any frame + * callback we might be waiting for so that a new buffer is posted + * immediately through block_handler() if there is damage to post. + */ + if (!old_allow_commits && xwl_window->allow_commits) { + if (xwl_window->frame_callback) { + wl_callback_destroy(xwl_window->frame_callback); + xwl_window->frame_callback = NULL; + } + } +} + +static void +xwl_property_callback(CallbackListPtr *pcbl, void *closure, + void *calldata) +{ + ScreenPtr screen = closure; + PropertyStateRec *rec = calldata; + struct xwl_screen *xwl_screen; + struct xwl_window *xwl_window; + + if (rec->win->drawable.pScreen != screen) + return; + + xwl_window = xwl_window_get(rec->win); + if (!xwl_window) + return; + + xwl_screen = xwl_screen_get(screen); + + if (rec->prop->propertyName == xwl_screen->allow_commits_prop) + xwl_window_property_allow_commits(xwl_window, rec); +} + static Bool xwl_close_screen(ScreenPtr screen) { @@ -122,6 +212,8 @@ xwl_close_screen(ScreenPtr screen) struct xwl_output *xwl_output, *next_xwl_output; struct xwl_seat *xwl_seat, *next_xwl_seat; + DeleteCallback(&PropertyStateCallback, xwl_property_callback, screen); + xorg_list_for_each_entry_safe(xwl_output, next_xwl_output, &xwl_screen->output_list, link) xwl_output_destroy(xwl_output); @@ -261,6 +353,21 @@ xwl_pixmap_get(PixmapPtr pixmap) return dixLookupPrivate(&pixmap->devPrivates, &xwl_pixmap_private_key); } +static void +xwl_window_init_allow_commits(struct xwl_window *xwl_window) +{ + PropertyPtr prop = NULL; + int ret; + + ret = dixLookupProperty(&prop, xwl_window->window, + xwl_window->xwl_screen->allow_commits_prop, + serverClient, DixReadAccess); + if (ret == Success && prop) + xwl_window_set_allow_commits_from_property(xwl_window, prop); + else + xwl_window_set_allow_commits(xwl_window, TRUE, "no property"); +} + static void send_surface_id_event(struct xwl_window *xwl_window) { @@ -376,6 +483,8 @@ xwl_realize_window(WindowPtr window) dixSetPrivate(&window->devPrivates, &xwl_window_private_key, xwl_window); xorg_list_init(&xwl_window->link_damage); + xwl_window_init_allow_commits(xwl_window); + return ret; err_surf: @@ -505,6 +614,9 @@ xwl_screen_post_damage(struct xwl_screen *xwl_screen) if (xwl_window->frame_callback) continue; + if (!xwl_window->allow_commits) + continue; + xwl_window_post_damage(xwl_window); } } @@ -694,6 +806,7 @@ wm_selection_callback(CallbackListPtr *p, void *data, void *arg) static Bool xwl_screen_init(ScreenPtr pScreen, int argc, char **argv) { + static const char allow_commits[] = "_XWAYLAND_ALLOW_COMMITS"; struct xwl_screen *xwl_screen; Pixel red_mask, blue_mask, green_mask; int ret, bpc, green_bpc, i; @@ -849,6 +962,14 @@ xwl_screen_init(ScreenPtr pScreen, int argc, char **argv) pScreen->CursorWarpedTo = xwl_cursor_warped_to; pScreen->CursorConfinedTo = xwl_cursor_confined_to; + xwl_screen->allow_commits_prop = MakeAtom(allow_commits, + strlen(allow_commits), + TRUE); + if (xwl_screen->allow_commits_prop == BAD_RESOURCE) + return FALSE; + + AddCallback(&PropertyStateCallback, xwl_property_callback, pScreen); + return ret; } diff --git a/hw/xwayland/xwayland.h b/hw/xwayland/xwayland.h index 5e5624be0..91b76200a 100644 --- a/hw/xwayland/xwayland.h +++ b/hw/xwayland/xwayland.h @@ -99,6 +99,8 @@ struct xwl_screen { void *egl_display, *egl_context; struct gbm_device *gbm; struct glamor_context *glamor_ctx; + + Atom allow_commits_prop; }; struct xwl_window { @@ -109,6 +111,7 @@ struct xwl_window { DamagePtr damage; struct xorg_list link_damage; struct wl_callback *frame_callback; + Bool allow_commits; }; #define MODIFIER_META 0x01