From ebcea16e71cd9c037351f2dc4527ca696346c8a6 Mon Sep 17 00:00:00 2001 From: Jon Turney Date: Mon, 3 Aug 2015 16:47:38 +0100 Subject: [PATCH] hw/xwin: A simpleminded attempt at composition MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Rather than drawing the window contents from the shadow framebuffer, use Composite extension redirection to cause the server to maintain a bitmap image of each top-level X window, and draw the window contents from that, so that window contents which are occluded in the framebuffer show correctly in the task bar and task switcher previews. v2: Fix incorrect use of memset() found by gcc5 hw/xwin/winshadgdi.c: In function ‘winBltExposedWindowRegionShadowGDI’: hw/xwin/winshadgdi.c:861:9: warning: ‘memset’ used with constant zero length parameter; this could be due to transposed parameters [-Wmemset-transposed-args] v3: Turn on -compositewm by default v4: Ignore -swcursor if -compositewm -swcursor is not compatible with -compositewm (because the window contents are drawn from an off-screen pixmap, not from the screen pixmap, where the software cursor will be drawn). v5: Update meson.build also Add -compositewm option to help output Update CI to install prerequisites --- .appveyor.yml | 1 + configure.ac | 2 +- hw/xwin/InitOutput.c | 5 +++ hw/xwin/man/XWin.man | 7 ++++ hw/xwin/meson.build | 1 + hw/xwin/win.h | 1 + hw/xwin/winmultiwindowwm.c | 50 ++++++++++++++++++++++++++--- hw/xwin/winprocarg.c | 20 ++++++++++++ hw/xwin/winscrinit.c | 8 ++++- hw/xwin/winshadgdi.c | 65 ++++++++++++++++++++++++++++++++++++++ hw/xwin/winvalargs.c | 8 +++++ hw/xwin/winwindow.h | 2 +- 12 files changed, 162 insertions(+), 8 deletions(-) diff --git a/.appveyor.yml b/.appveyor.yml index dd244b8db..50072acd4 100644 --- a/.appveyor.yml +++ b/.appveyor.yml @@ -50,6 +50,7 @@ libXmu-devel,\ libXpm-devel,\ libXrender-devel,\ libXtst-devel,\ +libxcb-composite-devel,\ libxcb-ewmh-devel,\ libxcb-icccm-devel,\ libxcb-image-devel,\ diff --git a/configure.ac b/configure.ac index 23c0ac285..55bb6942a 100644 --- a/configure.ac +++ b/configure.ac @@ -2081,7 +2081,7 @@ if test "x$XWIN" = xyes; then AC_DEFINE_UNQUOTED(__VENDORDWEBSUPPORT__, ["$VENDOR_WEB"], [Vendor web address for support]) AC_CHECK_TOOL(WINDRES, windres) - PKG_CHECK_MODULES([XWINMODULES],[x11 xdmcp xau xfixes x11-xcb xcb-aux xcb-image xcb-ewmh xcb-icccm]) + PKG_CHECK_MODULES([XWINMODULES],[x11 xau xdmcp xfixes x11-xcb xcb-aux xcb-composite xcb-image xcb-ewmh xcb-icccm]) if test "x$WINDOWSDRI" = xauto; then PKG_CHECK_EXISTS([windowsdriproto], [WINDOWSDRI=yes], [WINDOWSDRI=no]) diff --git a/hw/xwin/InitOutput.c b/hw/xwin/InitOutput.c index ddbf04253..00053beb0 100644 --- a/hw/xwin/InitOutput.c +++ b/hw/xwin/InitOutput.c @@ -716,6 +716,11 @@ winUseMsg(void) "\tthe updated region when num_boxes, or more, are in the\n" "\tupdated region.\n"); + ErrorF("-[no]compositewm\n" + "\tUse the Composite extension to keep a bitmap image of each top-level\n" + "\tX window, so window contents which are occluded show correctly in\n" + "\ttask bar and task switcher previews.\n"); + #ifdef XWIN_XF86CONFIG ErrorF("-config\n" "\tSpecify a configuration file.\n"); diff --git a/hw/xwin/man/XWin.man b/hw/xwin/man/XWin.man index 2401f9f09..e61589e8e 100644 --- a/hw/xwin/man/XWin.man +++ b/hw/xwin/man/XWin.man @@ -170,6 +170,12 @@ on its own is equivalent to \fB\-resize=randr\fP Add the host name to the window title for X applications which are running on remote hosts, when that information is available and it's useful to do so. The default is enabled. +.TP 8 +.B \-[no]compositewm +Use Composite extension redirection to maintain a bitmap image of each top-level +X window, so window contents which are occluded show correctly in task bar and +task switcher previews. +The default is enabled. .SH OPTIONS CONTROLLING WINDOWS INTEGRATION .TP 8 @@ -206,6 +212,7 @@ The default is enabled. .TP 8 .B \-swcursor Disable the usage of the \fIWindows\fP cursor and use the X11 software cursor instead. +This option is ignored if \fB-compositewm\fP is also enabled. .TP 8 .B \-[no]trayicon Do not create a tray icon. Default is to create one diff --git a/hw/xwin/meson.build b/hw/xwin/meson.build index 3ec809fef..3462c396d 100644 --- a/hw/xwin/meson.build +++ b/hw/xwin/meson.build @@ -137,6 +137,7 @@ xwin_dep = [ dependency('xcb-image'), dependency('xcb-ewmh'), dependency('xcb-icccm'), + dependency('xcb-composite'), ] executable( diff --git a/hw/xwin/win.h b/hw/xwin/win.h index 18f918d29..b667ceb66 100644 --- a/hw/xwin/win.h +++ b/hw/xwin/win.h @@ -391,6 +391,7 @@ typedef struct { Bool fDecoration; Bool fRootless; Bool fMultiWindow; + Bool fCompositeWM; Bool fMultiMonitorOverride; Bool fMultipleMonitors; Bool fLessPointer; diff --git a/hw/xwin/winmultiwindowwm.c b/hw/xwin/winmultiwindowwm.c index 32bc722e2..0734006fc 100644 --- a/hw/xwin/winmultiwindowwm.c +++ b/hw/xwin/winmultiwindowwm.c @@ -49,6 +49,7 @@ #include #include #include +#include #include @@ -116,6 +117,7 @@ typedef struct _WMInfo { xcb_atom_t atmUtf8String; xcb_atom_t atmNetWmName; xcb_ewmh_connection_t ewmh; + Bool fCompositeWM; } WMInfoRec, *WMInfoPtr; typedef struct _WMProcArgRec { @@ -1038,6 +1040,8 @@ winMultiWindowXMsgProc(void *pArg) xcb_atom_t atmWindowState, atmMotifWmHints, atmWindowType, atmNormalHints; int iReturn; xcb_auth_info_t *auth_info; + xcb_screen_t *root_screen; + xcb_window_t root_window_id; winDebug("winMultiWindowXMsgProc - Hello\n"); @@ -1110,11 +1114,11 @@ winMultiWindowXMsgProc(void *pArg) pthread_exit(NULL); } - { - /* Get root window id */ - xcb_screen_t *root_screen = xcb_aux_get_screen(pProcArg->conn, pProcArg->dwScreen); - xcb_window_t root_window_id = root_screen->root; + /* Get root window id */ + root_screen = xcb_aux_get_screen(pProcArg->conn, pProcArg->dwScreen); + root_window_id = root_screen->root; + { /* Set WM_ICON_SIZE property indicating desired icon sizes */ typedef struct { uint32_t min_width, min_height; @@ -1152,6 +1156,41 @@ winMultiWindowXMsgProc(void *pArg) */ intern_atom(pProcArg->conn, "WM_STATE"); + /* + Enable Composite extension and redirect subwindows of the root window + */ + if (pProcArg->pWMInfo->fCompositeWM) { + const char *extension_name = "Composite"; + xcb_query_extension_cookie_t cookie; + xcb_query_extension_reply_t *reply; + + cookie = xcb_query_extension(pProcArg->conn, strlen(extension_name), extension_name); + reply = xcb_query_extension_reply(pProcArg->conn, cookie, NULL); + + if (reply && (reply->present)) { + xcb_composite_redirect_subwindows(pProcArg->conn, + root_window_id, + XCB_COMPOSITE_REDIRECT_AUTOMATIC); + + /* + We use automatic updating of the root window for two + reasons: + + 1) redirected window contents are mirrored to the root + window so that the root window draws correctly when shown. + + 2) updating the root window causes damage against the + shadow framebuffer, which ultimately causes WM_PAINT to be + sent to the affected window(s) to cause the damage regions + to be redrawn. + */ + + ErrorF("Using Composite redirection\n"); + + free(reply); + } + } + /* Loop until we explicitly break out */ while (1) { xcb_generic_event_t *event; @@ -1351,7 +1390,7 @@ winInitWM(void **ppWMInfo, pthread_t * ptWMProc, pthread_t * ptXMsgProc, pthread_mutex_t * ppmServerStarted, - int dwScreen, HWND hwndScreen) + int dwScreen, HWND hwndScreen, Bool compositeWM) { WMProcArgPtr pArg = malloc(sizeof(WMProcArgRec)); WMInfoPtr pWMInfo = malloc(sizeof(WMInfoRec)); @@ -1373,6 +1412,7 @@ winInitWM(void **ppWMInfo, /* Set a return pointer to the Window Manager info structure */ *ppWMInfo = pWMInfo; + pWMInfo->fCompositeWM = compositeWM; /* Setup the argument structure for the thread function */ pArg->dwScreen = dwScreen; diff --git a/hw/xwin/winprocarg.c b/hw/xwin/winprocarg.c index 5dd878c30..e849cbc97 100644 --- a/hw/xwin/winprocarg.c +++ b/hw/xwin/winprocarg.c @@ -128,6 +128,7 @@ winInitializeScreenDefaults(void) defaultScreenInfo.fDecoration = TRUE; defaultScreenInfo.fRootless = FALSE; defaultScreenInfo.fMultiWindow = FALSE; + defaultScreenInfo.fCompositeWM = TRUE; defaultScreenInfo.fMultiMonitorOverride = FALSE; defaultScreenInfo.fMultipleMonitors = FALSE; defaultScreenInfo.fLessPointer = FALSE; @@ -571,6 +572,25 @@ ddxProcessArgument(int argc, char *argv[], int i) return 1; } + /* + * Look for the '-compositewm' argument + */ + if (IS_OPTION("-compositewm")) { + screenInfoPtr->fCompositeWM = TRUE; + + /* Indicate that we have processed this argument */ + return 1; + } + /* + * Look for the '-nocompositewm' argument + */ + if (IS_OPTION("-nocompositewm")) { + screenInfoPtr->fCompositeWM = FALSE; + + /* Indicate that we have processed this argument */ + return 1; + } + /* * Look for the '-multiplemonitors' argument */ diff --git a/hw/xwin/winscrinit.c b/hw/xwin/winscrinit.c index d6a1fedc7..fdaa1d6b3 100644 --- a/hw/xwin/winscrinit.c +++ b/hw/xwin/winscrinit.c @@ -461,6 +461,11 @@ winFinishScreenInitFB(int i, ScreenPtr pScreen, int argc, char **argv) if (pScreenInfo->fMultiWindow) { + if ((pScreenInfo->dwBPP == 8) && (pScreenInfo->fCompositeWM)) { + ErrorF("-compositewm disabled due to 8bpp depth\n"); + pScreenInfo->fCompositeWM = FALSE; + } + #if CYGDEBUG || YES winDebug("winFinishScreenInitFB - Calling winInitWM.\n"); #endif @@ -471,7 +476,8 @@ winFinishScreenInitFB(int i, ScreenPtr pScreen, int argc, char **argv) &pScreenPriv->ptXMsgProc, &pScreenPriv->pmServerStarted, pScreenInfo->dwScreen, - (HWND) &pScreenPriv->hwndScreen)) { + (HWND) &pScreenPriv->hwndScreen, + pScreenInfo->fCompositeWM)) { ErrorF("winFinishScreenInitFB - winInitWM () failed.\n"); return FALSE; } diff --git a/hw/xwin/winshadgdi.c b/hw/xwin/winshadgdi.c index 7c3e880dc..0235d1b2c 100644 --- a/hw/xwin/winshadgdi.c +++ b/hw/xwin/winshadgdi.c @@ -826,6 +826,70 @@ winBltExposedWindowRegionShadowGDI(ScreenPtr pScreen, WindowPtr pWin) return 0; } +#ifdef COMPOSITE + if (pWin->redirectDraw != RedirectDrawNone) { + HBITMAP hBitmap; + HDC hdcPixmap; + PixmapPtr pPixmap = (*pScreen->GetWindowPixmap) (pWin); + + /* + This is kind of clunky, and possibly not very efficient. + + Would it be more efficient to only create the DIB bitmap when the + composite bitmap is realloced and store it in a window private? + + But we still end up copying and converting all the bits from the + window pixmap into a DDB for every update. + + Perhaps better still would be to wrap the screen CreatePixmap routine + so it uses CreateDIBSection()? + */ + + BITMAPV4HEADER bmih; + memset(&bmih, 0, sizeof(bmih)); + bmih.bV4Size = sizeof(BITMAPV4HEADER); + bmih.bV4Width = pPixmap->drawable.width; + bmih.bV4Height = -pPixmap->drawable.height; /* top-down bitmap */ + bmih.bV4Planes = 1; + bmih.bV4BitCount = pPixmap->drawable.bitsPerPixel; + bmih.bV4SizeImage = 0; + /* window pixmap format is the same as the screen pixmap */ + assert(pPixmap->drawable.bitsPerPixel > 8); + bmih.bV4V4Compression = BI_BITFIELDS; + bmih.bV4RedMask = pScreenPriv->dwRedMask; + bmih.bV4GreenMask = pScreenPriv->dwGreenMask; + bmih.bV4BlueMask = pScreenPriv->dwBlueMask; + bmih.bV4AlphaMask = 0; + + /* Create the window bitmap from the pixmap */ + hBitmap = CreateDIBitmap(pScreenPriv->hdcScreen, + (BITMAPINFOHEADER *)&bmih, CBM_INIT, + pPixmap->devPrivate.ptr, (BITMAPINFO *)&bmih, + DIB_RGB_COLORS); + + /* Select the window bitmap into a screen-compatible DC */ + hdcPixmap = CreateCompatibleDC(pScreenPriv->hdcScreen); + SelectObject(hdcPixmap, hBitmap); + + /* Blt from the window bitmap to the invalidated region */ + if (!BitBlt(hdcUpdate, + ps.rcPaint.left, ps.rcPaint.top, + ps.rcPaint.right - ps.rcPaint.left, + ps.rcPaint.bottom - ps.rcPaint.top, + hdcPixmap, + ps.rcPaint.left + pWin->borderWidth, + ps.rcPaint.top + pWin->borderWidth, + SRCCOPY)) + ErrorF("winBltExposedWindowRegionShadowGDI - BitBlt failed: 0x%08x\n", + GetLastError()); + + /* Release */ + DeleteDC(hdcPixmap); + DeleteObject(hBitmap); + } + else +#endif + { /* Try to copy from the shadow buffer to the invalidated region */ if (!BitBlt(hdcUpdate, ps.rcPaint.left, ps.rcPaint.top, @@ -850,6 +914,7 @@ winBltExposedWindowRegionShadowGDI(ScreenPtr pScreen, WindowPtr pWin) (LPSTR) lpMsgBuf); LocalFree(lpMsgBuf); } + } /* EndPaint frees the DC */ EndPaint(hWnd, &ps); diff --git a/hw/xwin/winvalargs.c b/hw/xwin/winvalargs.c index 585544eab..008c111a6 100644 --- a/hw/xwin/winvalargs.c +++ b/hw/xwin/winvalargs.c @@ -155,6 +155,14 @@ winValidateArgs(void) "-scrollbars, -resize, -nodecoration, or -lesspointer.\n"); return FALSE; } + + /* Ignore -swcursor if -multiwindow -compositewm is requested */ + if (g_ScreenInfo[i].fMultiWindow && g_ScreenInfo[i].fCompositeWM) { + if (g_fSoftwareCursor) { + g_fSoftwareCursor = FALSE; + winMsg(X_WARNING, "Ignoring -swcursor due to -compositewm\n"); + } + } } winDebug("winValidateArgs - Returning.\n"); diff --git a/hw/xwin/winwindow.h b/hw/xwin/winwindow.h index 959ce152a..402b9e63c 100644 --- a/hw/xwin/winwindow.h +++ b/hw/xwin/winwindow.h @@ -144,7 +144,7 @@ winInitWM(void **ppWMInfo, pthread_t * ptWMProc, pthread_t * ptXMsgProc, pthread_mutex_t * ppmServerStarted, - int dwScreen, HWND hwndScreen); + int dwScreen, HWND hwndScreen, Bool compositeWM); void winDeinitMultiWindowWM(void);