kdrive/modesetting: Implement hw cursor RandR

Discovered a bug where, when Xmodesetting is started with an initially-rotated
screen, the cursor position is incorectly clamped to the unrotated screen width and height.

This doesn't happen when Xmodesetting is started without any rotations and then rotated with xrandr.
Needs investigating

Signed-off-by: stefan11111 <stefan11111github@gmail.com>
This commit is contained in:
stefan11111 2026-10-04 16:02:23 +03:00 • committed by Enrico Weigelt
commit 5e06d2b3b1
2 changed files with 122 additions and 12 deletions

View file

@ -34,6 +34,9 @@ typedef struct _msPriv {
typedef struct _msCursPriv {
struct gbm_bo *bo;
/* For RandR */
uint32_t *shadow;
int xhot;
int yhot;

View file

@ -14,6 +14,91 @@
static Bool msShowCursor(ScreenPtr pScreen);
/*
* Given a screen coordinate, rotate back to a cursor source coordinate
*/
static void
KdRotateCoord(Rotation rotation,
int width, int height,
int x_dst, int y_dst,
int *x_src, int *y_src)
{
int t;
switch (rotation & 0xf) {
case RR_Rotate_0:
break;
case RR_Rotate_90:
t = x_dst;
x_dst = width - y_dst - 1;
y_dst = t;
break;
case RR_Rotate_180:
x_dst = width - x_dst - 1;
y_dst = height - y_dst - 1;
break;
case RR_Rotate_270:
t = x_dst;
x_dst = y_dst;
y_dst = height - t - 1;
break;
}
if (rotation & RR_Reflect_X)
x_dst = width - x_dst - 1;
if (rotation & RR_Reflect_Y)
y_dst = height - y_dst - 1;
if (x_dst < 0) {
x_dst = 0;
}
if (y_dst < 0) {
y_dst = 0;
}
*x_src = x_dst;
*y_src = y_dst;
}
/*
* Given a cursor source coordinate, rotate to a screen coordinate
*/
static void
KdRotateCoordBack(Rotation rotation,
int width, int height,
int x_dst, int y_dst,
int *x_src, int *y_src)
{
int t;
if (rotation & RR_Reflect_X)
x_dst = width - x_dst - 1;
if (rotation & RR_Reflect_Y)
y_dst = height - y_dst - 1;
switch (rotation & 0xf) {
case RR_Rotate_0:
break;
case RR_Rotate_90:
t = x_dst;
x_dst = y_dst;
y_dst = width - t - 1;
break;
case RR_Rotate_180:
x_dst = width - x_dst - 1;
y_dst = height - y_dst - 1;
break;
case RR_Rotate_270:
t = x_dst;
x_dst = height - y_dst - 1;
y_dst = t;
break;
}
*x_src = x_dst;
*y_src = y_dst;
}
static void
msGetCursorSizes(int fd, int *w, int *h)
{
@ -83,7 +168,8 @@ msLoadCursor(ScreenPtr pScreen, CursorPtr pCursor)
uint32_t width = gbm_bo_get_width(pCurPriv->bo);
uint32_t height = gbm_bo_get_width(pCurPriv->bo);
uint32_t stride = gbm_bo_get_stride(pCurPriv->bo) / sizeof(uint32_t);
uint32_t *ram = gbm_bo_get_map(pCurPriv->bo);
uint32_t *map = gbm_bo_get_map(pCurPriv->bo);
uint32_t *ram = map;
int x, y;
@ -97,7 +183,14 @@ msLoadCursor(ScreenPtr pScreen, CursorPtr pCursor)
h = height;
}
/* TODO: Deal with rotations */
if (scrpriv->randr != RR_Rotate_0) {
if (!pCurPriv->shadow) {
pCurPriv->shadow = calloc(width * stride, sizeof(*ram));
}
if (pCurPriv->shadow) {
ram = pCurPriv->shadow;
}
}
if (bits->argb) {
srcLine = bits->argb;
@ -157,9 +250,24 @@ msLoadCursor(ScreenPtr pScreen, CursorPtr pCursor)
}
}
if (scrpriv->randr != RR_Rotate_0) {
for (y = 0; y < height; y++) {
for (x = 0; x < width; x++) {
int x_in, y_in;
KdRotateCoord(scrpriv->randr,
width, height,
x, y,
&x_in, &y_in);
map[y * stride + x] = ram[y_in * stride + x_in];
}
}
}
/* Save the cursor glyph hotspot */
pCurPriv->xhot = bits->xhot;
pCurPriv->yhot = bits->yhot;
KdRotateCoordBack(scrpriv->randr,
width, height,
bits->xhot, bits->yhot,
&pCurPriv->xhot, &pCurPriv->yhot);
return msShowCursor(pScreen);
}
@ -218,18 +326,15 @@ msMoveCursor(DeviceIntPtr pDev, ScreenPtr pScreen, int x, int y)
int fd = gbm_device_get_fd(priv->gbm);
KdRotateCoordBack(scrpriv->randr,
pScreen->width, pScreen->height,
x, y,
&x, &y);
/* Offset the cursor position, so that the cursor hotspot looks right */
x -= pCurPriv->xhot;
y -= pCurPriv->yhot;
if (x < 0) {
x = 0;
}
if (y < 0) {
y = 0;
}
drmModeMoveCursor(fd, scrpriv->crtc_id, x, y);
}
@ -342,6 +447,8 @@ msCursorFini(ScreenPtr pScreen)
msScrPriv *scrpriv = screen->driver;
msCursPriv *pCurPriv = &scrpriv->cursor;
free(pCurPriv->shadow);
pScreen->QueryBestSize = pCurPriv->QueryBestSize;
gbm_bo_destroy(pCurPriv->bo);