X11: Scale cursor when SmartSizing is on
authorNathan Loewen <loewen.nathan@gmail.com>
Tue, 8 Sep 2020 15:12:27 +0000 (10:12 -0500)
committerakallabeth <akallabeth@users.noreply.github.com>
Thu, 25 Feb 2021 08:51:41 +0000 (09:51 +0100)
(cherry picked from commit f4f795f3233c56fe7f4cd579777115b77f49da1e)

client/X11/xf_graphics.c
client/X11/xfreerdp.h

index b63b20e..76fbef5 100644 (file)
@@ -226,13 +226,129 @@ static BOOL xf_Bitmap_SetSurface(rdpContext* context, rdpBitmap* bitmap, BOOL pr
        return TRUE;
 }
 
+static BOOL _xf_Pointer_GetCursorForCurrentScale(rdpContext* context, const rdpPointer* pointer,
+                                                 Cursor* cursor)
+{
+#ifdef WITH_XCURSOR
+       UINT32 CursorFormat;
+       xfContext* xfc = (xfContext*)context;
+       xfPointer* xpointer = (xfPointer*)pointer;
+       XcursorImage ci;
+       rdpSettings* settings;
+       UINT32 xTargetSize;
+       UINT32 yTargetSize;
+       double xscale;
+       double yscale;
+       size_t size;
+       int cursorIndex = -1;
+
+       if (!context || !pointer || !context->gdi)
+               return FALSE;
+
+       settings = xfc->context.settings;
+
+       if (!settings)
+               return FALSE;
+
+       xscale = (settings->SmartSizing ? xfc->scaledWidth / (double)settings->DesktopWidth : 1);
+       yscale = (settings->SmartSizing ? xfc->scaledHeight / (double)settings->DesktopHeight : 1);
+       xTargetSize = pointer->width * xscale;
+       yTargetSize = pointer->height * yscale;
+
+       for (int i = 0; i < xpointer->nCursors; i++)
+       {
+               if (xpointer->cursorWidths[i] == xTargetSize && xpointer->cursorHeights[i] == yTargetSize)
+               {
+                       cursorIndex = i;
+               }
+       }
+
+       if (cursorIndex == -1)
+       {
+               xf_lock_x11(xfc);
+
+               if (!xfc->invert)
+                       CursorFormat = (!xfc->big_endian) ? PIXEL_FORMAT_RGBA32 : PIXEL_FORMAT_ABGR32;
+               else
+                       CursorFormat = (!xfc->big_endian) ? PIXEL_FORMAT_BGRA32 : PIXEL_FORMAT_ARGB32;
+
+               if (xpointer->nCursors == xpointer->mCursors)
+               {
+                       xpointer->mCursors = (xpointer->mCursors == 0 ? 1 : xpointer->mCursors * 2);
+
+                       if (!(xpointer->cursorWidths = (UINT32*)realloc(xpointer->cursorWidths,
+                                                                       sizeof(UINT32) * xpointer->mCursors)))
+                       {
+                               xf_unlock_x11(xfc);
+                               return FALSE;
+                       }
+                       if (!(xpointer->cursorHeights = (UINT32*)realloc(xpointer->cursorHeights,
+                                                                        sizeof(UINT32) * xpointer->mCursors)))
+                       {
+                               xf_unlock_x11(xfc);
+                               return FALSE;
+                       }
+                       if (!(xpointer->cursors =
+                                 (Cursor*)realloc(xpointer->cursors, sizeof(Cursor) * xpointer->mCursors)))
+                       {
+                               xf_unlock_x11(xfc);
+                               return FALSE;
+                       }
+               }
+
+               ZeroMemory(&ci, sizeof(ci));
+               ci.version = XCURSOR_IMAGE_VERSION;
+               ci.size = sizeof(ci);
+               ci.width = xTargetSize;
+               ci.height = yTargetSize;
+               ci.xhot = pointer->xPos * xscale;
+               ci.yhot = pointer->yPos * yscale;
+               size = ci.height * ci.width * GetBytesPerPixel(CursorFormat);
+
+               if (!(ci.pixels = (XcursorPixel*)_aligned_malloc(size, 16)))
+               {
+                       xf_unlock_x11(xfc);
+                       return FALSE;
+               }
+
+               if (xscale != 1 || yscale != 1)
+               {
+                       if (!freerdp_image_scale((BYTE*)ci.pixels, CursorFormat, 0, 0, 0, ci.width, ci.height,
+                                                (BYTE*)xpointer->cursorPixels, CursorFormat, 0, 0, 0,
+                                                pointer->width, pointer->height))
+                       {
+                               _aligned_free(ci.pixels);
+                               xf_unlock_x11(xfc);
+                               return FALSE;
+                       }
+               }
+               else
+               {
+                       ci.pixels = xpointer->cursorPixels;
+               }
+
+               cursorIndex = xpointer->nCursors;
+               xpointer->cursorWidths[cursorIndex] = ci.width;
+               xpointer->cursorHeights[cursorIndex] = ci.height;
+               xpointer->cursors[cursorIndex] = XcursorImageLoadCursor(xfc->display, &ci);
+               xpointer->nCursors += 1;
+               if (xscale != 1 || yscale != 1)
+                       _aligned_free(ci.pixels);
+
+               xf_unlock_x11(xfc);
+       }
+
+       cursor[0] = xpointer->cursors[cursorIndex];
+#endif
+       return TRUE;
+}
+
 /* Pointer Class */
 static BOOL xf_Pointer_New(rdpContext* context, rdpPointer* pointer)
 {
 #ifdef WITH_XCURSOR
        UINT32 CursorFormat;
        size_t size;
-       XcursorImage ci;
        xfContext* xfc = (xfContext*)context;
        xfPointer* xpointer = (xfPointer*)pointer;
 
@@ -244,35 +360,25 @@ static BOOL xf_Pointer_New(rdpContext* context, rdpPointer* pointer)
        else
                CursorFormat = (!xfc->big_endian) ? PIXEL_FORMAT_BGRA32 : PIXEL_FORMAT_ARGB32;
 
-       xf_lock_x11(xfc);
-       ZeroMemory(&ci, sizeof(ci));
-       ci.version = XCURSOR_IMAGE_VERSION;
-       ci.size = sizeof(ci);
-       ci.width = pointer->width;
-       ci.height = pointer->height;
-       ci.xhot = pointer->xPos;
-       ci.yhot = pointer->yPos;
-       size = ci.height * ci.width * GetBytesPerPixel(CursorFormat);
-
-       if (!(ci.pixels = (XcursorPixel*)_aligned_malloc(size, 16)))
-       {
-               xf_unlock_x11(xfc);
+       xpointer->nCursors = 0;
+       xpointer->mCursors = 0;
+
+       size = pointer->height * pointer->width * GetBytesPerPixel(CursorFormat);
+
+       if (!(xpointer->cursorPixels = (XcursorPixel*)_aligned_malloc(size, 16)))
                return FALSE;
-       }
 
        if (!freerdp_image_copy_from_pointer_data(
-               (BYTE*)ci.pixels, CursorFormat, 0, 0, 0, pointer->width, pointer->height,
+               (BYTE*)xpointer->cursorPixels, CursorFormat, 0, 0, 0, pointer->width, pointer->height,
                pointer->xorMaskData, pointer->lengthXorMask, pointer->andMaskData,
                pointer->lengthAndMask, pointer->xorBpp, &context->gdi->palette))
        {
-               _aligned_free(ci.pixels);
-               xf_unlock_x11(xfc);
+               _aligned_free(xpointer->cursorPixels);
                return FALSE;
        }
 
-       xpointer->cursor = XcursorImageLoadCursor(xfc->display, &ci);
-       _aligned_free(ci.pixels);
-       xf_unlock_x11(xfc);
+       if (!_xf_Pointer_GetCursorForCurrentScale(context, pointer, &(xpointer->cursor)))
+               return FALSE;
 #endif
        return TRUE;
 }
@@ -281,10 +387,22 @@ static void xf_Pointer_Free(rdpContext* context, rdpPointer* pointer)
 {
 #ifdef WITH_XCURSOR
        xfContext* xfc = (xfContext*)context;
+       xfPointer* xpointer = (xfPointer*)pointer;
+
        xf_lock_x11(xfc);
 
-       if (((xfPointer*)pointer)->cursor)
-               XFreeCursor(xfc->display, ((xfPointer*)pointer)->cursor);
+       _aligned_free(xpointer->cursorPixels);
+       free(xpointer->cursorWidths);
+       free(xpointer->cursorHeights);
+
+       for (int i = 0; i < xpointer->nCursors; i++)
+       {
+               XFreeCursor(xfc->display, xpointer->cursors[i]);
+       }
+
+       free(xpointer->cursors);
+       xpointer->nCursors = 0;
+       xpointer->mCursors = 0;
 
        xf_unlock_x11(xfc);
 #endif
@@ -294,15 +412,18 @@ static BOOL xf_Pointer_Set(rdpContext* context, const rdpPointer* pointer)
 {
 #ifdef WITH_XCURSOR
        xfContext* xfc = (xfContext*)context;
-       xf_lock_x11(xfc);
        xfc->pointer = (xfPointer*)pointer;
 
        /* in RemoteApp mode, window can be null if none has had focus */
 
        if (xfc->window)
+       {
+               if (!_xf_Pointer_GetCursorForCurrentScale(context, pointer, &(xfc->pointer->cursor)))
+                       return FALSE;
+               xf_lock_x11(xfc);
                XDefineCursor(xfc->display, xfc->window->handle, xfc->pointer->cursor);
-
-       xf_unlock_x11(xfc);
+               xf_unlock_x11(xfc);
+       }
 #endif
        return TRUE;
 }
index 0ac91d6..94592a4 100644 (file)
 
 typedef struct xf_context xfContext;
 
+#ifdef WITH_XCURSOR
+#include <X11/Xcursor/Xcursor.h>
+#endif
+
 #include <freerdp/api.h>
 
 #include "xf_window.h"
@@ -48,6 +52,14 @@ typedef struct xf_context xfContext;
 #include <freerdp/codec/progressive.h>
 #include <freerdp/codec/region.h>
 
+#if !defined(XcursorUInt)
+typedef unsigned int XcursorUInt;
+#endif
+
+#if !defined(XcursorPixel)
+typedef XcursorUInt XcursorPixel;
+#endif
+
 struct xf_FullscreenMonitors
 {
        UINT32 top;
@@ -69,6 +81,12 @@ typedef struct xf_WorkArea xfWorkArea;
 struct xf_pointer
 {
        rdpPointer pointer;
+       XcursorPixel* cursorPixels;
+       UINT32 nCursors;
+       UINT32 mCursors;
+       UINT32* cursorWidths;
+       UINT32* cursorHeights;
+       Cursor* cursors;
        Cursor cursor;
 };
 typedef struct xf_pointer xfPointer;