Rewrite cursor code to use Xcursor

This commit enables support for color cursors with alpha,
however this raises the requirement of libXcursor which is
not such a big deal.

Fixes issue #86 and #21
This commit is contained in:
Henrik Andersson 2017-09-21 15:59:21 +02:00
parent 2b769148bd
commit 6dd85edc6f
2 changed files with 94 additions and 102 deletions

View File

@ -153,6 +153,22 @@ if test x"$HAVE_XRANDR" = "x1"; then
AC_DEFINE(HAVE_XRANDR)
fi
# Xcursor
if test -n "$PKG_CONFIG"; then
PKG_CHECK_MODULES(XCURSOR, xcursor, [HAVE_XCURSOR=1], [HAVE_XCURSOR=0])
fi
if test x"$HAVE_XCURSOR" = "x1"; then
CFLAGS="$CFLAGS $XCURSOR_CFLAGS"
LIBS="$LIBS $XCURSOR_LIBS"
AC_DEFINE(HAVE_XCURSOR)
else
echo
echo "rdesktop requires libXcursor, install the dependency"
echo
exit 1
fi
dnl Smartcard support
AC_ARG_ENABLE(smartcard, AS_HELP_STRING([--disable-smartcard], [disable support for smartcard]))
AS_IF([test "x$enable_smartcard" != "xno"], [

180
xwin.c
View File

@ -31,6 +31,7 @@
#include <strings.h>
#include "rdesktop.h"
#include "xproto.h"
#include <X11/Xcursor/Xcursor.h>
#ifdef HAVE_XRANDR
#include <X11/extensions/Xrandr.h>
#endif
@ -2906,136 +2907,111 @@ ui_destroy_glyph(RD_HGLYPH glyph)
XFreePixmap(g_display, (Pixmap) glyph);
}
/* convert next pixel to 32 bpp */
static int
get_next_xor_pixel(uint8 * xormask, int bpp, int *k)
#define GET_BIT(ptr, bit) (*(ptr + bit / 8) & (1 << (7 - (bit % 8))))
static uint32
get_pixel(uint32 idx, uint8 * andmask, uint8 * xormask, int bpp)
{
int rv = 0;
PixelColour pc;
uint8 *s8;
uint16 *s16;
uint32 offs;
uint32 argb;
uint8 alpha;
uint8 *pxor;
switch (bpp)
{
case 1:
s8 = xormask + (*k) / 8;
rv = (*s8) & (0x80 >> ((*k) % 8));
rv = rv ? 0xffffff : 0;
(*k) += 1;
break;
case 8:
s8 = xormask + *k;
/* should use colour map */
rv = s8[0];
rv = rv ? 0xffffff : 0;
(*k) += 1;
break;
case 15:
s16 = (uint16 *) xormask;
SPLITCOLOUR15(s16[*k], pc);
rv = (pc.red << 16) | (pc.green << 8) | pc.blue;
(*k) += 1;
break;
case 16:
s16 = (uint16 *) xormask;
SPLITCOLOUR16(s16[*k], pc);
rv = (pc.red << 16) | (pc.green << 8) | pc.blue;
(*k) += 1;
offs = idx;
argb = GET_BIT(xormask, idx);
alpha = GET_BIT(andmask, idx) ? 0x00 : 0xff;
if (!GET_BIT(andmask, idx) == 0x00 && argb)
{
// If we have an xor bit is high and
// andmask bit is low, we should
// render a black pixle due to we can
// not xor blit in X11.
// TODO: add outline to make this
// prettier on black backgrounds.
argb = 0xff000000;
}
else
argb = (alpha << 24) | (argb ? 0xffffff : 0x000000);
break;
case 24:
s8 = xormask + *k;
rv = (s8[0] << 16) | (s8[1] << 8) | s8[2];
(*k) += 3;
offs = idx * 3;
pxor = xormask + offs;
alpha = GET_BIT(andmask, idx) ? 0x00 : 0xff;
argb = (alpha << 24) | (pxor[2] << 16) | (pxor[1] << 8) | pxor[0];
break;
case 32:
s8 = xormask + *k;
rv = (s8[1] << 16) | (s8[2] << 8) | s8[3];
(*k) += 4;
break;
default:
logger(GUI, Warning, "get_next_xor_pixel(), unhandled bpp=%d", bpp);
offs = idx * 4;
pxor = xormask + offs;
argb = (pxor[3] << 24) | (pxor[2] << 16) | (pxor[1] << 8) | pxor[0];
break;
}
return rv;
return argb;
}
RD_HCURSOR
ui_create_cursor(unsigned int x, unsigned int y, int width, int height,
uint8 * andmask, uint8 * xormask, int bpp)
ui_create_cursor(unsigned int xhot, unsigned int yhot, int width,
int height, uint8 * andmask, uint8 * xormask, int bpp)
{
RD_HGLYPH maskglyph, cursorglyph;
XColor bg, fg;
Cursor xcursor;
uint8 *cursor, *pcursor;
uint8 *mask, *pmask;
uint8 nextbit;
int scanline, offset, delta;
int i, j, k;
Cursor cursor;
XcursorPixel *out;
XcursorImage *cimg;
uint32 x, y, oidx, idx, argb;
k = 0;
scanline = (width + 7) / 8;
offset = scanline * height;
logger(GUI, Debug, "ui_create_cursot(): xhot=%d, yhot=%d, width=%d, height=%d, bpp=%d",
xhot, yhot, width, height, bpp);
cursor = (uint8 *) xmalloc(offset);
memset(cursor, 0, offset);
mask = (uint8 *) xmalloc(offset);
memset(mask, 0, offset);
if (bpp == 1)
if (bpp != 1 && bpp != 24 && bpp != 32)
{
offset = 0;
delta = scanline;
logger(GUI, Warning, "ui_create_xcursor_cursor(): Unhandled cursor bit depth %d",
bpp);
return g_null_cursor;
}
else
{
offset = scanline * height - scanline;
delta = -scanline;
}
/* approximate AND and XOR masks with a monochrome X pointer */
for (i = 0; i < height; i++)
{
pcursor = &cursor[offset];
pmask = &mask[offset];
for (j = 0; j < scanline; j++)
cimg = XcursorImageCreate(width, height);
if (!cimg)
{
logger(GUI, Error, "ui_create_xcursor_cursor(): XcursorImageCreate() failed");
return g_null_cursor;
}
cimg->xhot = xhot;
cimg->yhot = yhot;
out = cimg->pixels;
for (y = 0; y < height; y++)
{
for (x = 0; x < width; x++)
{
for (nextbit = 0x80; nextbit != 0; nextbit >>= 1)
oidx = y * width + x;
// Flip cursor on Y axis if color pointer
if (bpp != 1)
{
if (get_next_xor_pixel(xormask, bpp, &k))
{
*pcursor |= (~(*andmask) & nextbit);
*pmask |= nextbit;
}
else
{
*pcursor |= ((*andmask) & nextbit);
*pmask |= (~(*andmask) & nextbit);
}
oidx = (height - 1 - y) * width + x;
}
andmask++;
pcursor++;
pmask++;
idx = y * width + x;
argb = get_pixel(idx, andmask, xormask, bpp);
out[oidx] = argb;
}
offset += delta;
}
fg.red = fg.blue = fg.green = 0xffff;
bg.red = bg.blue = bg.green = 0x0000;
fg.flags = bg.flags = DoRed | DoBlue | DoGreen;
cursor = XcursorImageLoadCursor(g_display, cimg);
if (!cursor)
{
logger(GUI, Error, "ui_create_cursor(): XcursorImageLoadCursor() failed");
return g_null_cursor;
}
cursorglyph = ui_create_glyph(width, height, cursor);
maskglyph = ui_create_glyph(width, height, mask);
xcursor =
XCreatePixmapCursor(g_display, (Pixmap) cursorglyph,
(Pixmap) maskglyph, &fg, &bg, x, y);
ui_destroy_glyph(maskglyph);
ui_destroy_glyph(cursorglyph);
xfree(mask);
xfree(cursor);
return (RD_HCURSOR) xcursor;
return (RD_HCURSOR) cursor;
}
void