From 95ac3962557fe023e3ac1983d5ef3702fb53bc3f Mon Sep 17 00:00:00 2001 From: Zhiyi Zhang Date: Fri, 15 Oct 2021 09:56:51 +0800 Subject: [PATCH 49/51] WIP: winex11.drv: Implement DPI unawared scaling. To: wine-devel@winehq.org Signed-off-by: Zhiyi Zhang --- dlls/winex11.drv/display.c | 30 ++++ dlls/winex11.drv/event.c | 22 ++- dlls/winex11.drv/init.c | 5 +- dlls/winex11.drv/mouse.c | 9 +- dlls/winex11.drv/systray.c | 4 +- dlls/winex11.drv/window.c | 281 ++++++++++++++++++++++++++++++--- dlls/winex11.drv/x11drv.h | 17 +- dlls/winex11.drv/x11drv_main.c | 3 + dlls/winex11.drv/xdamage.c | 77 +++++++++ 9 files changed, 411 insertions(+), 37 deletions(-) diff --git a/dlls/winex11.drv/display.c b/dlls/winex11.drv/display.c index 67cffaf5e64..1ad921f5c61 100644 --- a/dlls/winex11.drv/display.c +++ b/dlls/winex11.drv/display.c @@ -66,6 +66,36 @@ POINT root_to_virtual_screen(INT x, INT y) return pt; } +POINT dpi_unaware_virtual_screen_to_root(INT x, INT y) +{ + RECT virtual = NtUserGetVirtualScreenRect(); + POINT pt; + + pt.x = x - virtual.left; + pt.y = y - virtual.top; + + if (is_dpi_unaware_scaling_required()) + pt = map_dpi_point(pt, USER_DEFAULT_SCREEN_DPI, get_effective_dpi()); + + return pt; +} + +POINT dpi_unaware_root_to_virtual_screen(INT x, INT y) +{ + RECT virtual = NtUserGetVirtualScreenRect(); + POINT pt; + + pt.x = x; + pt.y = y; + + if (is_dpi_unaware_scaling_required()) + pt = map_dpi_point(pt, get_effective_dpi(), USER_DEFAULT_SCREEN_DPI); + + pt.x += virtual.left; + pt.y += virtual.top; + return pt; +} + /* Get virtual screen rectangle in DPI per-monitor aware context */ RECT get_virtual_screen_rect(void) { diff --git a/dlls/winex11.drv/event.c b/dlls/winex11.drv/event.c index df8803854e7..87a23e42e4a 100644 --- a/dlls/winex11.drv/event.c +++ b/dlls/winex11.drv/event.c @@ -388,7 +388,8 @@ static inline BOOL call_event_handler( Display *display, XEvent *event ) #endif if (XFindContext( display, event->xany.window, winContext, (char **)&hwnd ) != 0) hwnd = 0; /* not for a registered window */ - if (!hwnd && event->xany.window == root_window) hwnd = NtUserGetDesktopWindow(); + if (!hwnd && (event->xany.window == root_window || event->xany.window == root_window_drawable)) + hwnd = NtUserGetDesktopWindow(); TRACE( "%lu %s for hwnd/window %p/%lx\n", event->xany.serial, dbgstr_event( event->type ), hwnd, event->xany.window ); @@ -922,7 +923,7 @@ static BOOL X11DRV_Expose( HWND hwnd, XEvent *xev ) pos.x = event->x; pos.y = event->y; } - else pos = root_to_virtual_screen( event->x, event->y ); + else pos = dpi_unaware_root_to_virtual_screen( event->x, event->y ); if (!(data = get_win_data( hwnd ))) return FALSE; @@ -1122,10 +1123,17 @@ static BOOL X11DRV_ConfigureNotify( HWND hwnd, XEvent *xev ) pos.x = x; pos.y = y; } - else pos = root_to_virtual_screen( x, y ); + else pos = dpi_unaware_root_to_virtual_screen( x, y ); - X11DRV_X_to_window_rect( data, &rect, pos.x, pos.y, event->width, event->height ); - if (root_coords) NtUserMapWindowPoints( 0, parent, (POINT *)&rect, 2 ); + cx = event->width; + cy = event->height; + if (is_window_scaling_enabled(data)) + { + cx = MulDiv( cx, USER_DEFAULT_SCREEN_DPI, get_effective_dpi() ); + cy = MulDiv( cy, USER_DEFAULT_SCREEN_DPI, get_effective_dpi() ); + } + X11DRV_X_to_window_rect( data, &rect, pos.x, pos.y, cx, cy ); + if (root_coords) MapWindowPoints( 0, parent, (POINT *)&rect, 2 ); TRACE( "win %p/%lx new X rect %d,%d,%dx%d (event %d,%d,%dx%d)\n", hwnd, data->whole_window, rect.left, rect.top, rect.right-rect.left, rect.bottom-rect.top, @@ -1505,7 +1513,7 @@ static void EVENT_DropFromOffiX( HWND hWnd, XClientMessageEvent *event ) XQueryPointer( event->display, win, &w_aux_root, &w_aux_child, &x, &y, &dummy, &dummy, (unsigned int*)&aux_long); - pt = root_to_virtual_screen( x, y ); + pt = dpi_unaware_root_to_virtual_screen( x, y ); /* find out drop point and drop window */ if (pt.x < 0 || pt.y < 0 || pt.x > cx || pt.y > cy) @@ -1625,7 +1633,7 @@ static void EVENT_DropURLs( HWND hWnd, XClientMessageEvent *event ) if( drop_len && drop_len < 65535 ) { XQueryPointer( event->display, root_window, &u.w_aux, &u.w_aux, &x, &y, &u.i, &u.i, &u.u); - pos = root_to_virtual_screen( x, y ); + pos = dpi_unaware_root_to_virtual_screen( x, y ); drop_len += sizeof(DROPFILES) + 1; hDrop = GlobalAlloc( GMEM_SHARE, drop_len ); diff --git a/dlls/winex11.drv/init.c b/dlls/winex11.drv/init.c index 16f82c364b3..397d4eedf40 100644 --- a/dlls/winex11.drv/init.c +++ b/dlls/winex11.drv/init.c @@ -95,13 +95,14 @@ static X11DRV_PDEVICE *create_x11_physdev( Drawable drawable ) static BOOL CDECL X11DRV_CreateDC( PHYSDEV *pdev, LPCWSTR device, LPCWSTR output, const DEVMODEW* initData ) { - X11DRV_PDEVICE *physDev = create_x11_physdev( root_window ); + Drawable drawable = X11DRV_get_root_window_drawable(); + X11DRV_PDEVICE *physDev = create_x11_physdev( drawable ); if (!physDev) return FALSE; physDev->depth = default_visual.depth; physDev->color_shifts = &X11DRV_PALETTE_default_shifts; - physDev->dc_rect = get_virtual_screen_rect(); + physDev->dc_rect = NtUserGetVirtualScreenRect(); OffsetRect( &physDev->dc_rect, -physDev->dc_rect.left, -physDev->dc_rect.top ); push_dc_driver( pdev, &physDev->dev, &x11drv_funcs.dc_funcs ); if (xrender_funcs && !xrender_funcs->pCreateDC( pdev, device, output, initData )) return FALSE; diff --git a/dlls/winex11.drv/mouse.c b/dlls/winex11.drv/mouse.c index ad0e1884cb3..7d8fc718d8e 100644 --- a/dlls/winex11.drv/mouse.c +++ b/dlls/winex11.drv/mouse.c @@ -414,7 +414,7 @@ static BOOL grab_clipping_window( const RECT *clip ) TRACE( "clipping to %s win %lx\n", wine_dbgstr_rect(clip), clip_window ); if (!data->clip_hwnd) XUnmapWindow( data->display, clip_window ); - pos = virtual_screen_to_root( clip->left, clip->top ); + pos = dpi_unaware_virtual_screen_to_root( clip->left, clip->top ); XMoveResizeWindow( data->display, clip_window, pos.x, pos.y, max( 1, clip->right - clip->left ), max( 1, clip->bottom - clip->top ) ); XMapWindow( data->display, clip_window ); @@ -615,6 +615,9 @@ static void map_event_coords( HWND hwnd, Window window, Window event_root, int x { if (window == data->whole_window) { + if (is_dpi_unaware_scaling_required()) + pt = map_dpi_point(pt, get_effective_dpi(), USER_DEFAULT_SCREEN_DPI); + pt.x += data->whole_rect.left - data->client_rect.left; pt.y += data->whole_rect.top - data->client_rect.top; } @@ -622,6 +625,8 @@ static void map_event_coords( HWND hwnd, Window window, Window event_root, int x if (NtUserGetWindowLongW( hwnd, GWL_EXSTYLE ) & WS_EX_LAYOUTRTL) pt.x = data->client_rect.right - data->client_rect.left - 1 - pt.x; NtUserMapWindowPoints( hwnd, 0, &pt, 1 ); + if (is_dpi_unaware_scaling_required()) + pt = map_dpi_point(pt, USER_DEFAULT_SCREEN_DPI, get_effective_dpi()); } release_win_data( data ); } @@ -1618,7 +1623,7 @@ void move_resize_window( HWND hwnd, int dir ) if (!(win = X11DRV_get_whole_window( hwnd ))) return; pt = NtUserGetThreadInfo()->message_pos; - pos = virtual_screen_to_root( (short)LOWORD( pt ), (short)HIWORD( pt ) ); + pos = dpi_unaware_virtual_screen_to_root( (short)LOWORD( pt ), (short)HIWORD( pt ) ); if (NtUserGetKeyState( VK_LBUTTON ) & 0x8000) button = 1; else if (NtUserGetKeyState( VK_MBUTTON ) & 0x8000) button = 2; diff --git a/dlls/winex11.drv/systray.c b/dlls/winex11.drv/systray.c index c6d12b21c28..b68ac967bc8 100644 --- a/dlls/winex11.drv/systray.c +++ b/dlls/winex11.drv/systray.c @@ -676,6 +676,8 @@ static void dock_systray_icon( Display *display, struct tray_icon *icon, Window attr.background_pixmap = ParentRelative; attr.bit_gravity = ForgetGravity; XChangeWindowAttributes( display, window, CWBackPixmap | CWBitGravity, &attr ); + if (data->whole_drawable != data->whole_window) + XChangeWindowAttributes( display, data->whole_drawable, CWBackPixmap | CWBitGravity, &attr ); } else repaint_tray_icon( icon ); } @@ -758,7 +760,7 @@ static BOOL modify_icon( struct tray_icon *icon, NOTIFYICONDATAW *nid ) else if (icon->layered) repaint_tray_icon( icon ); else { - Window win = X11DRV_get_whole_window( icon->window ); + Window win = X11DRV_get_whole_drawable( icon->window ); if (win) XClearArea( gdi_display, win, 0, 0, 0, 0, True ); } } diff --git a/dlls/winex11.drv/window.c b/dlls/winex11.drv/window.c index 0b076d8329f..2a28e02f69b 100644 --- a/dlls/winex11.drv/window.c +++ b/dlls/winex11.drv/window.c @@ -49,6 +49,9 @@ #include "wine/unicode.h" #include "x11drv.h" +#include "xcomposite.h" +#include "xdamage.h" +#include "xrender.h" #include "wine/debug.h" #include "wine/server.h" #include "mwm.h" @@ -100,6 +103,8 @@ static const WCHAR foreign_window_prop[] = {'_','_','w','i','n','e','_','x','1','1','_','f','o','r','e','i','g','n','_','w','i','n','d','o','w',0}; static const WCHAR whole_window_prop[] = {'_','_','w','i','n','e','_','x','1','1','_','w','h','o','l','e','_','w','i','n','d','o','w',0}; +static const WCHAR whole_drawable_prop[] = + {'_','_','w','i','n','e','_','x','1','1','_','w','h','o','l','e','_','d','r','a','w','a','b','l','e',0}; static const WCHAR clip_window_prop[] = {'_','_','w','i','n','e','_','x','1','1','_','c','l','i','p','_','w','i','n','d','o','w',0}; @@ -355,6 +360,11 @@ static void sync_window_style( struct x11drv_win_data *data ) int mask = get_window_attributes( data, &attr ); XChangeWindowAttributes( data->display, data->whole_window, mask, &attr ); + if (data->whole_drawable != data->whole_window) + { + mask &= CWColormap | CWBorderPixel | CWBitGravity; + XChangeWindowAttributes( data->display, data->whole_drawable, mask, &attr ); + } } } @@ -375,7 +385,7 @@ static void sync_window_region( struct x11drv_win_data *data, HRGN win_region ) if (IsRectEmpty( &data->window_rect )) /* set an empty shape */ { static XRectangle empty_rect; - XShapeCombineRectangles( data->display, data->whole_window, ShapeBounding, 0, 0, + XShapeCombineRectangles( data->display, data->whole_drawable, ShapeBounding, 0, 0, &empty_rect, 1, ShapeSet, YXBanded ); return; } @@ -392,7 +402,7 @@ static void sync_window_region( struct x11drv_win_data *data, HRGN win_region ) if (!hrgn) { - XShapeCombineMask( data->display, data->whole_window, ShapeBounding, 0, 0, None, ShapeSet ); + XShapeCombineMask( data->display, data->whole_drawable, ShapeBounding, 0, 0, None, ShapeSet ); } else { @@ -402,7 +412,7 @@ static void sync_window_region( struct x11drv_win_data *data, HRGN win_region ) NtUserMirrorRgn( data->hwnd, hrgn ); if ((pRegionData = X11DRV_GetRegionData( hrgn, 0 ))) { - XShapeCombineRectangles( data->display, data->whole_window, ShapeBounding, + XShapeCombineRectangles( data->display, data->whole_drawable, ShapeBounding, data->window_rect.left - data->whole_rect.left, data->window_rect.top - data->whole_rect.top, (XRectangle *)pRegionData->Buffer, @@ -696,6 +706,12 @@ static void set_size_hints( struct x11drv_win_data *data, DWORD style ) { size_hints->x = data->whole_rect.left; size_hints->y = data->whole_rect.top; + if (is_window_scaling_enabled(data)) + { + size_hints->x = MulDiv(size_hints->x, get_effective_dpi(), 96); + size_hints->y = MulDiv(size_hints->y, get_effective_dpi(), 96); + } + size_hints->flags |= PPosition; } else size_hints->win_gravity = NorthWestGravity; @@ -706,6 +722,13 @@ static void set_size_hints( struct x11drv_win_data *data, DWORD style ) size_hints->max_height = data->whole_rect.bottom - data->whole_rect.top; if (size_hints->max_width <= 0 ||size_hints->max_height <= 0) size_hints->max_width = size_hints->max_height = 1; + + if (is_window_scaling_enabled(data)) + { + size_hints->max_width = MulDiv(size_hints->max_width, get_effective_dpi(), 96); + size_hints->max_height = MulDiv(size_hints->max_height, get_effective_dpi(), 96); + } + size_hints->min_width = size_hints->max_width; size_hints->min_height = size_hints->max_height; size_hints->flags |= PMinSize | PMaxSize; @@ -1284,7 +1307,6 @@ void X11DRV_X_to_window_rect( struct x11drv_win_data *data, RECT *rect, int x, i SetRect( rect, x, y, x + cx, y + cy ); } - /*********************************************************************** * sync_window_position * @@ -1306,6 +1328,11 @@ static void sync_window_position( struct x11drv_win_data *data, { changes.width = data->whole_rect.right - data->whole_rect.left; changes.height = data->whole_rect.bottom - data->whole_rect.top; + if (is_dpi_unaware_scaling_required()) + { + changes.width = MulDiv(changes.width, get_effective_dpi(), USER_DEFAULT_SCREEN_DPI); + changes.height = MulDiv(changes.height, get_effective_dpi(), USER_DEFAULT_SCREEN_DPI); + } /* if window rect is empty force size to 1x1 */ if (changes.width <= 0 || changes.height <= 0) changes.width = changes.height = 1; if (changes.width > 65535) changes.width = 65535; @@ -1316,7 +1343,7 @@ static void sync_window_position( struct x11drv_win_data *data, /* only the size is allowed to change for the desktop window */ if (data->whole_window != root_window) { - POINT pt = virtual_screen_to_root( data->whole_rect.left, data->whole_rect.top ); + POINT pt = dpi_unaware_virtual_screen_to_root( data->whole_rect.left, data->whole_rect.top ); changes.x = pt.x; changes.y = pt.y; mask |= CWX | CWY; @@ -1346,6 +1373,12 @@ static void sync_window_position( struct x11drv_win_data *data, update_net_wm_states( data, TRUE ); data->configure_serial = NextRequest( data->display ); XReconfigureWMWindow( data->display, data->whole_window, data->vis.screen, mask, &changes ); + if (is_window_scaling_enabled(data) && (mask & (CWWidth | CWHeight))) + { + changes.width = MulDiv(changes.width, USER_DEFAULT_SCREEN_DPI, get_effective_dpi()); + changes.height = MulDiv(changes.height, USER_DEFAULT_SCREEN_DPI, get_effective_dpi()); + XReconfigureWMWindow(data->display, data->whole_drawable, data->vis.screen, mask, &changes); + } #ifdef HAVE_LIBXSHAPE if (IsRectEmpty( old_window_rect ) != IsRectEmpty( &data->window_rect )) sync_window_region( data, (HRGN)1 ); @@ -1356,7 +1389,7 @@ static void sync_window_position( struct x11drv_win_data *data, int new_x_offset = data->window_rect.left - data->whole_rect.left; int new_y_offset = data->window_rect.top - data->whole_rect.top; if (old_x_offset != new_x_offset || old_y_offset != new_y_offset) - XShapeOffsetShape( data->display, data->whole_window, ShapeBounding, + XShapeOffsetShape( data->display, data->whole_drawable, ShapeBounding, new_x_offset - old_x_offset, new_y_offset - old_y_offset ); } #endif @@ -1564,7 +1597,7 @@ Window create_client_window( HWND hwnd, const XVisualInfo *visual ) cy = min( max( 1, data->client_rect.bottom - data->client_rect.top ), 65535 ); ret = data->client_window = XCreateWindow( gdi_display, - data->whole_window ? data->whole_window : dummy_parent, + data->whole_drawable ? data->whole_drawable : dummy_parent, x, y, cx, cy, 0, default_visual.depth, InputOutput, visual->visual, CWBitGravity | CWWinGravity | CWBackingStore | CWColormap | CWBorderPixel, &attr ); @@ -1580,6 +1613,15 @@ Window create_client_window( HWND hwnd, const XVisualInfo *visual ) return ret; } +static CRITICAL_SECTION dpi_unaware_section; +static CRITICAL_SECTION_DEBUG dpi_unaware_critsect_debug = +{ + 0, 0, &dpi_unaware_section, + {&dpi_unaware_critsect_debug.ProcessLocksList, &dpi_unaware_critsect_debug.ProcessLocksList}, + 0, 0, {(DWORD_PTR)(__FILE__ ": dpi_unaware_section")} +}; +static CRITICAL_SECTION dpi_unaware_section = {&dpi_unaware_critsect_debug, -1, 0, 0, 0, 0}; + POINT map_dpi_point( POINT pt, UINT dpi_from, UINT dpi_to ) { if (dpi_from && dpi_to && dpi_from != dpi_to) @@ -1590,6 +1632,16 @@ POINT map_dpi_point( POINT pt, UINT dpi_from, UINT dpi_to ) return pt; } +SIZE map_dpi_size( SIZE size, UINT dpi_from, UINT dpi_to ) +{ + if (dpi_from && dpi_to && dpi_from != dpi_to) + { + size.cx = MulDiv( size.cx, dpi_to, dpi_from ); + size.cy = MulDiv( size.cy, dpi_to, dpi_from ); + } + return size; +} + RECT map_dpi_rect( RECT rect, UINT dpi_from, UINT dpi_to ) { if (dpi_from && dpi_to && dpi_from != dpi_to) @@ -1602,7 +1654,7 @@ RECT map_dpi_rect( RECT rect, UINT dpi_from, UINT dpi_to ) return rect; } -unsigned int get_window_effective_dpi(void) +unsigned int get_effective_dpi(void) { DPI_AWARENESS_CONTEXT context; UINT dpi; @@ -1614,6 +1666,112 @@ unsigned int get_window_effective_dpi(void) return dpi; } +BOOL is_window_scaling_enabled(const struct x11drv_win_data *data) +{ + return data->whole_drawable != data->whole_window; +} + +BOOL is_dpi_unaware_scaling_required(void) +{ + if (!usexcomposite || !usexdamage || !usexrender) + return FALSE; + + if (GetAwarenessFromDpiAwarenessContext(GetThreadDpiAwarenessContext()) != DPI_AWARENESS_UNAWARE) + return FALSE; + + return get_effective_dpi() > 96; +} + +Drawable X11DRV_get_whole_drawable( HWND hwnd ) +{ + struct x11drv_win_data *data = get_win_data( hwnd ); + Window ret; + + if (!data) + { + if (hwnd == NtUserGetDesktopWindow()) + return root_window; + return (Window)NtUserGetProp(hwnd, whole_drawable_prop); + } + + ret = data->whole_drawable; + release_win_data( data ); + return ret; +} + +static Window create_invisible_root_window(void) +{ + Window virtual_root; + Atom atoms[3]; + RECT rect; + + rect = get_virtual_screen_rect(); + virtual_root = XCreateSimpleWindow(gdi_display, DefaultRootWindow(gdi_display), 0, 0, + rect.right - rect.left, rect.bottom - rect.top, 0, 0, 0); + + atoms[0] = x11drv_atom(_NET_WM_STATE_HIDDEN); + atoms[1] = x11drv_atom(_NET_WM_STATE_SKIP_PAGER); + atoms[2] = x11drv_atom(_NET_WM_STATE_SKIP_TASKBAR); + XChangeProperty(gdi_display, virtual_root, x11drv_atom(_NET_WM_STATE), XA_ATOM, 32, + PropModeReplace, (unsigned char *)atoms, ARRAY_SIZE(atoms)); + XMapWindow(gdi_display, virtual_root); + XFlush(gdi_display); + return virtual_root; +} + +Window get_invisible_root_window(void) +{ + EnterCriticalSection(&dpi_unaware_section); + if (invisible_root_window) + { + LeaveCriticalSection(&dpi_unaware_section); + return invisible_root_window; + } + + invisible_root_window = create_invisible_root_window(); + LeaveCriticalSection(&dpi_unaware_section); + return invisible_root_window; +} + +Drawable X11DRV_get_root_window_drawable(void) +{ + Display *display = thread_init_display(); + unsigned int effective_dpi; + XSetWindowAttributes swa; + XWindowAttributes wa; + + if (!is_dpi_unaware_scaling_required()) + return root_window; + + /* !! What about virtual screen */ + if (is_virtual_desktop()) + return root_window; + + EnterCriticalSection(&dpi_unaware_section); + if (root_window_drawable) + { + LeaveCriticalSection(&dpi_unaware_section); + return root_window_drawable; + } + + effective_dpi = get_effective_dpi(); + XGetWindowAttributes(display, root_window, &wa); + wa.width = MulDiv(wa.width, 96, effective_dpi); + wa.height = MulDiv(wa.height, 96, effective_dpi); + swa.colormap = wa.colormap; + swa.bit_gravity = wa.bit_gravity; + root_window_drawable = XCreateWindow(display, get_invisible_root_window(), wa.x, wa.y, wa.width, wa.height, 0, + wa.depth, InputOutput, wa.visual, + CWColormap | CWBitGravity, &swa); + + pXCompositeRedirectWindow(display, root_window_drawable, CompositeRedirectAutomatic); + XMapWindow(display, root_window_drawable); + + root_window_damage = pXDamageCreate(display, root_window_drawable, XDamageReportNonEmpty); + LeaveCriticalSection(&dpi_unaware_section); + return root_window_drawable; +} + /********************************************************************** * create_whole_window * @@ -1654,17 +1812,57 @@ static void create_whole_window( struct x11drv_win_data *data ) if (!(cy = data->whole_rect.bottom - data->whole_rect.top)) cy = 1; else if (cy > 65535) cy = 65535; - pos = virtual_screen_to_root( data->whole_rect.left, data->whole_rect.top ); - data->whole_window = XCreateWindow( data->display, root_window, pos.x, pos.y, - cx, cy, 0, data->vis.depth, InputOutput, - data->vis.visual, mask, &attr ); - if (!data->whole_window) goto done; + if (is_dpi_unaware_scaling_required()) + { + pos = dpi_unaware_virtual_screen_to_root( data->whole_rect.left, data->whole_rect.top ); + data->whole_drawable = XCreateWindow( data->display, get_invisible_root_window(), pos.x, pos.y, + cx, cy, 0, data->vis.depth, InputOutput, + data->vis.visual, mask & (CWColormap | CWBitGravity), &attr ); + if (!data->whole_drawable) goto done; + + pXCompositeRedirectWindow( data->display, data->whole_drawable, CompositeRedirectAutomatic ); + + /* Use scaled window for display */ + pos = dpi_unaware_virtual_screen_to_root( data->whole_rect.left, data->whole_rect.top ); + if (is_dpi_unaware_scaling_required()) + { + UINT effective_dpi = get_effective_dpi(); + cx = MulDiv(cx, effective_dpi, USER_DEFAULT_SCREEN_DPI); + cy = MulDiv(cy, effective_dpi, USER_DEFAULT_SCREEN_DPI); + } + data->whole_window = XCreateWindow( data->display, root_window, pos.x, pos.y, cx, cy, 0, + data->vis.depth, InputOutput, data->vis.visual, mask, + &attr); + if (!data->whole_window) + { + XDestroyWindow( data->display, data->whole_drawable ); + goto done; + } + + /* Set X context so that events for data->whole_drawable are working */ + XSaveContext( data->display, data->whole_drawable, winContext, (char *)data->hwnd ); + /* whole_drawable has to be mapped for X damage to work */ + XMapWindow(data->display, data->whole_drawable); + + data->damage = pXDamageCreate( data->display, data->whole_drawable, XDamageReportNonEmpty ); + } + else + { + pos = virtual_screen_to_root( data->whole_rect.left, data->whole_rect.top ); + data->whole_drawable = XCreateWindow( data->display, root_window, pos.x, pos.y, + cx, cy, 0, data->vis.depth, InputOutput, + data->vis.visual, mask, &attr ); + if (!data->whole_drawable) goto done; + + data->whole_window = data->whole_drawable; + } set_initial_wm_hints( data->display, data->whole_window ); set_wm_hints( data ); XSaveContext( data->display, data->whole_window, winContext, (char *)data->hwnd ); NtUserSetProp( data->hwnd, whole_window_prop, (HANDLE)data->whole_window ); + NtUserSetProp( data->hwnd, whole_drawable_prop, (HANDLE)data->whole_drawable ); /* set the window text */ if (!NtUserInternalGetWindowText( data->hwnd, text, ARRAY_SIZE( text ))) text[0] = 0; @@ -1720,7 +1918,23 @@ static void destroy_whole_window( struct x11drv_win_data *data, BOOL already_des XSync( data->display, False ); } XDeleteContext( data->display, data->whole_window, winContext ); - if (!already_destroyed) XDestroyWindow( data->display, data->whole_window ); + if (!already_destroyed) + { + /* Destroy damage first */ + if (data->damage) + { + pXDamageDestroy( data->display, data->damage ); + data->damage = 0; + } + if (data->whole_drawable != data->whole_window) + { + pXCompositeUnredirectWindow(data->display, data->whole_drawable, + CompositeRedirectAutomatic); + XDestroyWindow(data->display, data->whole_drawable); + data->whole_drawable = 0; + } + XDestroyWindow( data->display, data->whole_window ); + } } if (data->whole_colormap) XFreeColormap( data->display, data->whole_colormap ); data->whole_window = data->client_window = 0; @@ -1739,6 +1953,7 @@ static void destroy_whole_window( struct x11drv_win_data *data, BOOL already_des if (data->surface) window_surface_release( data->surface ); data->surface = NULL; NtUserRemoveProp( data->hwnd, whole_window_prop ); + NtUserRemoveProp( data->hwnd, whole_drawable_prop ); } @@ -1760,11 +1975,24 @@ void set_window_visual( struct x11drv_win_data *data, const XVisualInfo *vis, BO if (data->vis.visualid == vis->visualid) return; data->client_window = 0; destroy_whole_window( data, client_window != 0 /* don't destroy whole_window until reparented */ ); + /* Destroy damage first */ + if (data->damage) + { + pXDamageDestroy( data->display, data->damage ); + data->damage = 0; + } + if (data->whole_drawable != whole_window) + { + pXCompositeUnredirectWindow(data->display, data->whole_drawable, + CompositeRedirectAutomatic); + XDestroyWindow(data->display, data->whole_drawable); + data->whole_drawable = 0; + } data->vis = *vis; create_whole_window( data ); if (!client_window) return; /* move the client to the new parent */ - XReparentWindow( data->display, client_window, data->whole_window, + XReparentWindow( data->display, client_window, data->whole_drawable, data->client_rect.left - data->whole_rect.left, data->client_rect.top - data->whole_rect.top ); data->client_window = client_window; @@ -2127,7 +2355,7 @@ HWND create_foreign_window( Display *display, Window xwin ) { parent = NtUserGetDesktopWindow(); style |= WS_POPUP; - pos = root_to_virtual_screen( attr.x, attr.y ); + pos = dpi_unaware_root_to_virtual_screen( attr.x, attr.y ); } else { @@ -2228,7 +2456,7 @@ void X11DRV_GetDC( HDC hdc, HWND hwnd, HWND top, const RECT *win_rect, { struct x11drv_win_data *data = get_win_data( hwnd ); - escape.drawable = data ? data->whole_window : X11DRV_get_whole_window( hwnd ); + escape.drawable = data ? data->whole_drawable : X11DRV_get_whole_drawable( hwnd ); /* special case: when repainting the root window, clip out top-level windows */ if (data && data->whole_window == root_window) escape.mode = ClipByChildren; @@ -2238,7 +2466,7 @@ void X11DRV_GetDC( HDC hdc, HWND hwnd, HWND top, const RECT *win_rect, { /* find the first ancestor that has a drawable */ for (parent = hwnd; parent && parent != top; parent = NtUserGetAncestor( parent, GA_PARENT )) - if ((escape.drawable = X11DRV_get_whole_window( parent ))) break; + if ((escape.drawable = X11DRV_get_whole_drawable( parent ))) break; if (escape.drawable) { @@ -2248,7 +2476,7 @@ void X11DRV_GetDC( HDC hdc, HWND hwnd, HWND top, const RECT *win_rect, OffsetRect( &escape.dc_rect, pt.x, pt.y ); if (flags & DCX_CLIPCHILDREN) escape.mode = ClipByChildren; } - else escape.drawable = X11DRV_get_whole_window( top ); + else escape.drawable = X11DRV_get_whole_drawable( top ); } NtGdiExtEscape( hdc, NULL, 0, X11DRV_ESCAPE, sizeof(escape), (LPSTR)&escape, 0, NULL ); @@ -2265,7 +2493,7 @@ void X11DRV_ReleaseDC( HWND hwnd, HDC hdc ) escape.code = X11DRV_SET_DRAWABLE; escape.drawable = root_window; escape.mode = IncludeInferiors; - escape.dc_rect = get_virtual_screen_rect(); + escape.dc_rect = NtUserGetVirtualScreenRect(); OffsetRect( &escape.dc_rect, -2 * escape.dc_rect.left, -2 * escape.dc_rect.top ); NtGdiExtEscape( hdc, NULL, 0, X11DRV_ESCAPE, sizeof(escape), (LPSTR)&escape, 0, NULL ); } @@ -2380,7 +2608,7 @@ done: static inline BOOL get_surface_rect( const RECT *visible_rect, RECT *surface_rect ) { - *surface_rect = get_virtual_screen_rect(); + *surface_rect = NtUserGetVirtualScreenRect(); if (!IntersectRect( surface_rect, surface_rect, visible_rect )) return FALSE; OffsetRect( surface_rect, -visible_rect->left, -visible_rect->top ); @@ -2450,7 +2678,7 @@ BOOL X11DRV_WindowPosChanging( HWND hwnd, HWND insert_after, UINT swp_flags, if (!layered || !NtUserGetLayeredWindowAttributes( hwnd, &key, NULL, &flags ) || !(flags & LWA_COLORKEY)) key = CLR_INVALID; - *surface = create_surface( hwnd, data->whole_window, &data->vis, &surface_rect, key, FALSE ); + *surface = create_surface( hwnd, data->whole_drawable, &data->vis, &surface_rect, key, FALSE ); done: release_win_data( data ); @@ -2672,7 +2900,12 @@ UINT X11DRV_ShowWindow( HWND hwnd, INT cmd, RECT *rect, UINT swp ) XGetGeometry( thread_data->display, data->whole_window, &root, &x, &y, &width, &height, &border, &depth ); XTranslateCoordinates( thread_data->display, data->whole_window, root, 0, 0, &x, &y, &top ); - pos = root_to_virtual_screen( x, y ); + pos = dpi_unaware_root_to_virtual_screen( x, y ); + if (is_window_scaling_enabled(data)) + { + width = MulDiv( width, USER_DEFAULT_SCREEN_DPI, get_effective_dpi() ); + height = MulDiv( height, USER_DEFAULT_SCREEN_DPI, get_effective_dpi() ); + } X11DRV_X_to_window_rect( data, rect, pos.x, pos.y, width, height ); swp &= ~(SWP_NOMOVE | SWP_NOCLIENTMOVE | SWP_NOSIZE | SWP_NOCLIENTSIZE); @@ -2812,7 +3045,7 @@ BOOL X11DRV_UpdateLayeredWindow( HWND hwnd, const UPDATELAYEREDWINDOWINFO *info, surface = data->surface; if (!surface || !EqualRect( &surface->rect, &rect )) { - data->surface = create_surface( hwnd, data->whole_window, &data->vis, &rect, + data->surface = create_surface( hwnd, data->whole_drawable, &data->vis, &rect, color_key, data->use_alpha ); if (surface) window_surface_release( surface ); surface = data->surface; diff --git a/dlls/winex11.drv/x11drv.h b/dlls/winex11.drv/x11drv.h index 5f02487c74f..74c9683a9e7 100644 --- a/dlls/winex11.drv/x11drv.h +++ b/dlls/winex11.drv/x11drv.h @@ -428,6 +428,9 @@ extern XVisualInfo argb_visual DECLSPEC_HIDDEN; extern Colormap default_colormap DECLSPEC_HIDDEN; extern XPixmapFormatValues **pixmap_formats DECLSPEC_HIDDEN; extern Window root_window DECLSPEC_HIDDEN; +extern Window invisible_root_window DECLSPEC_HIDDEN; +extern Drawable root_window_drawable DECLSPEC_HIDDEN; +extern XID root_window_damage DECLSPEC_HIDDEN; extern BOOL clipping_cursor DECLSPEC_HIDDEN; extern BOOL keyboard_grabbed DECLSPEC_HIDDEN; extern unsigned int screen_bpp DECLSPEC_HIDDEN; @@ -564,6 +567,7 @@ extern void X11DRV_register_event_handler( int type, x11drv_event_handler handle extern BOOL X11DRV_ButtonPress( HWND hwnd, XEvent *event ) DECLSPEC_HIDDEN; extern BOOL X11DRV_ButtonRelease( HWND hwnd, XEvent *event ) DECLSPEC_HIDDEN; +extern BOOL X11DRV_DamageNotify( HWND hwnd, XEvent *event ) DECLSPEC_HIDDEN; extern BOOL X11DRV_MotionNotify( HWND hwnd, XEvent *event ) DECLSPEC_HIDDEN; extern BOOL X11DRV_EnterNotify( HWND hwnd, XEvent *event ) DECLSPEC_HIDDEN; extern BOOL X11DRV_KeyEvent( HWND hwnd, XEvent *event ) DECLSPEC_HIDDEN; @@ -614,6 +618,8 @@ struct x11drv_win_data Colormap client_colormap; /* colormap for the client window */ HWND hwnd; /* hwnd that this private data belongs to */ Window whole_window; /* X window for the complete window */ + Drawable whole_drawable; /* X drawable for the complete window. Same as whole_window if not scaled */ + XID damage; /* Damage for syncing whole_drawable to whole_window */ Window client_window; /* X window for the client area */ RECT window_rect; /* USER window rectangle relative to win32 parent window client area */ RECT whole_rect; /* X window rectangle for the whole window relative to win32 parent window client area */ @@ -713,9 +719,18 @@ extern RECT get_virtual_screen_rect(void) DECLSPEC_HIDDEN; /* DPI unaware scaling helpers */ extern RECT dpi_unaware_get_virtual_screen_rect(void) DECLSPEC_HIDDEN; -extern unsigned int get_window_effective_dpi( void ) DECLSPEC_HIDDEN; +extern RECT dpi_unaware_get_primary_monitor_rect(void) DECLSPEC_HIDDEN; +extern POINT dpi_unaware_virtual_screen_to_root( INT x, INT y ) DECLSPEC_HIDDEN; +extern POINT dpi_unaware_root_to_virtual_screen( INT x, INT y ) DECLSPEC_HIDDEN; +extern BOOL is_window_scaling_enabled( const struct x11drv_win_data *data ) DECLSPEC_HIDDEN; +extern BOOL is_dpi_unaware_scaling_required( void ) DECLSPEC_HIDDEN; +extern unsigned int get_effective_dpi( void ) DECLSPEC_HIDDEN; extern POINT map_dpi_point( POINT pt, UINT dpi_from, UINT dpi_to ) DECLSPEC_HIDDEN; +extern SIZE map_dpi_size( SIZE size, UINT dpi_from, UINT dpi_to ) DECLSPEC_HIDDEN; extern RECT map_dpi_rect( RECT rect, UINT dpi_from, UINT dpi_to ) DECLSPEC_HIDDEN; +extern Drawable X11DRV_get_whole_drawable( HWND hwnd ) DECLSPEC_HIDDEN; +extern Drawable X11DRV_get_root_window_drawable( void ) DECLSPEC_HIDDEN; +extern Window get_invisible_root_window(void) DECLSPEC_HIDDEN; #define DEPTH_COUNT 3 extern const unsigned int *depths DECLSPEC_HIDDEN; diff --git a/dlls/winex11.drv/x11drv_main.c b/dlls/winex11.drv/x11drv_main.c index 6569faf637e..94373c26817 100644 --- a/dlls/winex11.drv/x11drv_main.c +++ b/dlls/winex11.drv/x11drv_main.c @@ -63,6 +63,9 @@ Colormap default_colormap = None; XPixmapFormatValues **pixmap_formats; unsigned int screen_bpp; Window root_window; +Window invisible_root_window = None; +Drawable root_window_drawable = None; +XID root_window_damage = None; BOOL usexvidmode = TRUE; BOOL usexrandr = TRUE; BOOL usexcomposite = TRUE; diff --git a/dlls/winex11.drv/xdamage.c b/dlls/winex11.drv/xdamage.c index 17f2225208b..17d82dc1589 100644 --- a/dlls/winex11.drv/xdamage.c +++ b/dlls/winex11.drv/xdamage.c @@ -27,6 +27,7 @@ #include "x11drv.h" #include "xdamage.h" +#include "xrender.h" #ifdef SONAME_LIBXDAMAGE @@ -43,6 +44,81 @@ MAKE_FUNCPTR(XDamageSubtract) #undef MAKE_FUNCPTR +BOOL X11DRV_DamageNotify( HWND hwnd, XEvent *xev ) +{ + Drawable src_drawable, dst_drawable; + struct x11drv_win_data *data = NULL; + Picture src_picture, dst_picture; + XRenderPictureAttributes pa; + XTransform transform = {0}; + XRenderPictFormat *format; + XWindowAttributes wa; + Display *display; + unsigned int dpi; + Damage damage; + double scale; + + TRACE("hwnd %p, event %p.\n", hwnd, xev); + + if (hwnd == GetDesktopWindow()) + { + display = thread_display(); + damage = root_window_damage; + src_drawable = root_window_drawable; + dst_drawable = root_window; + } + else + { + if (!(data = get_win_data(hwnd))) + return FALSE; + + display = data->display; + damage = data->damage; + src_drawable = data->whole_drawable; + dst_drawable = data->whole_window; + } + + if (!damage) + { + /* Damage was destroyed before event arrival */ + if (data) + release_win_data( data ); + return FALSE; + } + + pXDamageSubtract(display, damage, None, None); + + pa.subwindow_mode = IncludeInferiors; + XGetWindowAttributes(display, src_drawable, &wa); + format = pXRenderFindVisualFormat(display, wa.visual); + src_picture = pXRenderCreatePicture(display, src_drawable, format, CPSubwindowMode, &pa); + dst_picture = pXRenderCreatePicture(display, dst_drawable, format, 0, &pa); + + dpi = get_effective_dpi(); + scale = (double)dpi / USER_DEFAULT_SCREEN_DPI; + + /* scaling matrix */ + transform.matrix[0][0] = XDoubleToFixed(1); + transform.matrix[1][1] = XDoubleToFixed(1); + transform.matrix[2][2] = XDoubleToFixed(scale); + pXRenderSetPictureTransform(display, src_picture, &transform); + + /* !! COPY only changed parts */ +// pXRenderComposite(data->display, PictOpSrc, src_picture, None, dst_picture, 0, 0, 0, 0, 0, 0, +// wa.width, wa.height); + + pXRenderComposite(display, PictOpSrc, src_picture, None, dst_picture, 0, 0, 0, 0, 0, 0, + MulDiv(wa.width, dpi, USER_DEFAULT_SCREEN_DPI), + MulDiv(wa.height, dpi, USER_DEFAULT_SCREEN_DPI)); + + pXRenderFreePicture(display, src_picture); + pXRenderFreePicture(display, dst_picture); + + if (data) + release_win_data( data ); + return TRUE; +} + void X11DRV_XDamage_Init(void) { int event_base, error_base; @@ -72,6 +148,7 @@ void X11DRV_XDamage_Init(void) goto failed; } TRACE("XDamage is up, event base %d, error_base %d.\n", event_base, error_base); + X11DRV_register_event_handler(event_base + XDamageNotify, X11DRV_DamageNotify, "XDamageNotify"); usexdamage = TRUE; return; -- 2.32.0