From c17a41794507d6d04c850e9e1bc04fc60c31de18 Mon Sep 17 00:00:00 2001 From: Keith Packard Date: Sat, 4 Jun 2016 19:55:07 -0700 Subject: [PATCH] ephyr: Process queued X events before blocking [v2] If we end up reading all pending X events in the course of other server execution, then our notify FD callback won't get invoked and we won't process them. Fix this by noting that there are queued events in the block handler, setting the poll timeout to zero and queuing a work proc to clear the event queue. v2: use a work proc to clear the event queue rather than doing it in the block handler directly. Signed-off-by: Keith Packard Reviewed-by: Adam Jackson --- hw/kdrive/ephyr/ephyr.c | 52 ++++++++++++++++++++++++++--------------- hw/kdrive/ephyr/hostx.c | 26 +++++++++++++++++++++ hw/kdrive/ephyr/hostx.h | 6 +++++ 3 files changed, 65 insertions(+), 19 deletions(-) diff --git a/hw/kdrive/ephyr/ephyr.c b/hw/kdrive/ephyr/ephyr.c index e5f188334..015aef53d 100644 --- a/hw/kdrive/ephyr/ephyr.c +++ b/hw/kdrive/ephyr/ephyr.c @@ -336,6 +336,16 @@ ephyrInternalDamageRedisplay(ScreenPtr pScreen) } } +static void +ephyrXcbProcessEvents(Bool queued_only); + +static Bool +ephyrEventWorkProc(ClientPtr client, void *closure) +{ + ephyrXcbProcessEvents(TRUE); + return TRUE; +} + static void ephyrScreenBlockHandler(ScreenPtr pScreen, void *timeout, void *pRead) { @@ -345,20 +355,16 @@ ephyrScreenBlockHandler(ScreenPtr pScreen, void *timeout, void *pRead) pScreen->BlockHandler = scrpriv->BlockHandler; (*pScreen->BlockHandler)(pScreen, timeout, pRead); + scrpriv->BlockHandler = pScreen->BlockHandler; + pScreen->BlockHandler = ephyrScreenBlockHandler; - if (scrpriv->pDamage) { - - /* Re-wrap if we're still tracking damage - */ - scrpriv->BlockHandler = pScreen->BlockHandler; - pScreen->BlockHandler = ephyrScreenBlockHandler; + if (scrpriv->pDamage) ephyrInternalDamageRedisplay(pScreen); - } else { - /* Done tracking damage, note that we've left - * the block handler unwrapped - */ - scrpriv->BlockHandler = NULL; + if (hostx_has_queued_event()) { + if (!QueueWorkProc(ephyrEventWorkProc, NULL, NULL)) + FatalError("cannot queue event processing in ephyr block handler"); + AdjustWaitForDelay(timeout, 0); } } @@ -374,12 +380,6 @@ ephyrSetInternalDamage(ScreenPtr pScreen) (DamageDestroyFunc) 0, DamageReportNone, TRUE, pScreen, pScreen); - /* Wrap only once */ - if (scrpriv->BlockHandler == NULL) { - scrpriv->BlockHandler = pScreen->BlockHandler; - pScreen->BlockHandler = ephyrScreenBlockHandler; - } - pPixmap = (*pScreen->GetScreenPixmap) (pScreen); DamageRegister(&pPixmap->drawable, scrpriv->pDamage); @@ -682,6 +682,10 @@ ephyrInitScreen(ScreenPtr pScreen) Bool ephyrFinishInitScreen(ScreenPtr pScreen) { + KdScreenPriv(pScreen); + KdScreenInfo *screen = pScreenPriv->screen; + EphyrScrPriv *scrpriv = screen->driver; + /* FIXME: Calling this even if not using shadow. * Seems harmless enough. But may be safer elsewhere. */ @@ -693,6 +697,9 @@ ephyrFinishInitScreen(ScreenPtr pScreen) return FALSE; #endif + scrpriv->BlockHandler = pScreen->BlockHandler; + pScreen->BlockHandler = ephyrScreenBlockHandler; + return TRUE; } @@ -1131,12 +1138,13 @@ ephyrProcessConfigureNotify(xcb_generic_event_t *xev) } static void -ephyrXcbNotify(int fd, int ready, void *data) +ephyrXcbProcessEvents(Bool queued_only) { xcb_connection_t *conn = hostx_get_xcbconn(); while (TRUE) { - xcb_generic_event_t *xev = xcb_poll_for_event(conn); + xcb_generic_event_t *xev = hostx_get_event(queued_only); + if (!xev) { /* If our XCB connection has died (for example, our window was * closed), exit now. @@ -1191,6 +1199,12 @@ ephyrXcbNotify(int fd, int ready, void *data) } } +static void +ephyrXcbNotify(int fd, int ready, void *data) +{ + ephyrXcbProcessEvents(FALSE); +} + void ephyrCardFini(KdCardInfo * card) { diff --git a/hw/kdrive/ephyr/hostx.c b/hw/kdrive/ephyr/hostx.c index abe6edaf0..887f6544c 100644 --- a/hw/kdrive/ephyr/hostx.c +++ b/hw/kdrive/ephyr/hostx.c @@ -70,6 +70,7 @@ struct EphyrHostXVars { xcb_gcontext_t gc; xcb_render_pictformat_t argb_format; xcb_cursor_t empty_cursor; + xcb_generic_event_t *saved_event; int depth; Bool use_sw_cursor; Bool use_fullscreen; @@ -1227,6 +1228,31 @@ hostx_get_xcbconn(void) return HostX.conn; } +xcb_generic_event_t * +hostx_get_event(Bool queued_only) +{ + xcb_generic_event_t *xev; + + if (HostX.saved_event) { + xev = HostX.saved_event; + HostX.saved_event = NULL; + } else { + if (queued_only) + xev = xcb_poll_for_queued_event(HostX.conn); + else + xev = xcb_poll_for_event(HostX.conn); + } + return xev; +} + +Bool +hostx_has_queued_event(void) +{ + if (!HostX.saved_event) + HostX.saved_event = xcb_poll_for_queued_event(HostX.conn); + return HostX.saved_event != NULL; +} + int hostx_get_screen(void) { diff --git a/hw/kdrive/ephyr/hostx.h b/hw/kdrive/ephyr/hostx.h index 6e0b07ba0..d0f30119c 100644 --- a/hw/kdrive/ephyr/hostx.h +++ b/hw/kdrive/ephyr/hostx.h @@ -157,6 +157,12 @@ hostx_size_set_from_configure(Bool); xcb_connection_t * hostx_get_xcbconn(void); +xcb_generic_event_t * +hostx_get_event(Bool queued_only); + +Bool +hostx_has_queued_event(void); + int hostx_get_screen(void);