Re: [gtk-vnc-devel] PATCH: Implement cursor encoding
- From: "Daniel P. Berrange" <berrange redhat com>
- To: Jonh Wendell <jwendell gnome org>
- Cc: gtk-vnc-devel List <gtk-vnc-devel lists sourceforge net>
- Subject: Re: [gtk-vnc-devel] PATCH: Implement cursor encoding
- Date: Fri, 31 Aug 2007 02:43:15 +0100
On Fri, Aug 31, 2007 at 02:42:17AM +0100, Daniel P. Berrange wrote:
> On Thu, Aug 30, 2007 at 10:50:42PM +0100, Daniel P. Berrange wrote:
> > On Thu, Aug 30, 2007 at 05:09:37PM -0300, Jonh Wendell wrote:
> > > Em Qui, 2007-08-30 às 20:50 +0100, Daniel P. Berrange escreveu:
> > > > The OSX-VNC server violates the RFB spec by never drawing a remote cursor
> > > > itself. So if you connect to it with GTK-VNC you never see a cursor on the
> > > > remote framebuffer. The server stupidly assumes that all clients will
> > > > implement the rich cursor extension, even if the client doesn't advertise
> > > > this fact. I can't find anyway around this, so I implemented the rich
> > > > cursor extension
> > > >
> > > > Dan.
> > >
> > > Well, the patch did not work to me.
> > > I connect into a UltraVNC server on windows xp and get no cursor after
> > > applying this patch.
> >
> > The VNC cursor extensions are fun in that there are two different ones.
> > One call Rich Cursor, the other called XCursor. I implemented the former,
> > and I'd guess UltraVNC wants the latter. Sucks. Guess I'll have to set
> > up a virtual machine runnings Windows & UltraVNC to test.
>
> Try this new patch - it implements both rich cursor and xcursor extensions.
> I discovered TightVNC server does both on Linux, so tested against that &
> it looks like it works.
And this time with the patch attached.
Dan.
--
|=- Red Hat, Engineering, Emerging Technologies, Boston. +1 978 392 2496 -=|
|=- Perl modules: http://search.cpan.org/~danberr/ -=|
|=- Projects: http://freshmeat.net/~danielpb/ -=|
|=- GnuPG: 7D3B9505 F3C9 553F A1DA 4AC2 5648 23C1 B3DF F742 7D3B 9505 -=|
diff -r b1c48ddc01d9 src/gvnc.c
--- a/src/gvnc.c Wed Aug 22 15:10:12 2007 -0400
+++ b/src/gvnc.c Thu Aug 30 21:33:56 2007 -0400
@@ -1057,6 +1057,119 @@ static void gvnc_shared_memory_rmid(stru
if (!gvnc->ops.shared_memory_rmid(gvnc->ops_data, shmid))
gvnc->has_error = TRUE;
}
+
+#define RICH_CURSOR_BLIT(gvnc, pixbuf, image, mask, pitch, width, height, src_pixel_t) \
+ do { \
+ int x, y; \
+ uint8_t *src = image; \
+ uint32_t *dst = (uint32_t*)pixbuf; \
+ uint8_t *alpha = mask; \
+ for (y = 0; y < height; y++) { \
+ src_pixel_t *sp = (src_pixel_t *)src; \
+ uint8_t *mp = alpha; \
+ for (x = 0; x < width; x++) { \
+ *dst++ = (((mp[x/8] >> (7-(x % 8))) &1) ? (255 << 24) : 0) \
+ | (((*sp >> gvnc->fmt.red_shift) & (gvnc->fmt.red_max)) << 16) \
+ | (((*sp >> gvnc->fmt.green_shift) & (gvnc->fmt.green_max)) << 8) \
+ | (((*sp >> gvnc->fmt.blue_shift) & (gvnc->fmt.blue_max)) << 0); \
+ sp++; \
+ } \
+ src += pitch; \
+ alpha += ((width+7)/8); \
+ } \
+ } while(0)
+
+static void gvnc_rich_cursor(struct gvnc *gvnc, int x, int y, int width, int height)
+{
+ uint8_t *pixbuf = NULL;
+
+ if (width && height) {
+ uint8_t *image, *mask;
+ int imagelen, masklen;
+
+ imagelen = width * height * (gvnc->fmt.bits_per_pixel / 8);
+ masklen = ((width + 7)/8) * height;
+
+ image = malloc(imagelen);
+ mask = malloc(masklen);
+ pixbuf = malloc(width * height * 4); /* RGB-A 8bit */
+ gvnc_read(gvnc, image, imagelen);
+ gvnc_read(gvnc, mask, masklen);
+
+ if (gvnc->fmt.bits_per_pixel == 8) {
+ RICH_CURSOR_BLIT(gvnc, pixbuf, image, mask, width * (gvnc->fmt.bits_per_pixel/8), width, height, uint8_t);
+ } else if (gvnc->fmt.bits_per_pixel == 16) {
+ RICH_CURSOR_BLIT(gvnc, pixbuf, image, mask, width * (gvnc->fmt.bits_per_pixel/8), width, height, uint16_t);
+ } else if (gvnc->fmt.bits_per_pixel == 24 || gvnc->fmt.bits_per_pixel == 32) {
+ RICH_CURSOR_BLIT(gvnc, pixbuf, image, mask, width * (gvnc->fmt.bits_per_pixel/8), width, height, uint32_t);
+ }
+ free(image);
+ free(mask);
+ }
+
+ if (gvnc->has_error || !gvnc->ops.local_cursor)
+ return;
+ if (!gvnc->ops.local_cursor(gvnc->ops_data, x, y, width, height, pixbuf))
+ gvnc->has_error = TRUE;
+
+ free(pixbuf);
+}
+
+
+static void gvnc_xcursor(struct gvnc *gvnc, int x, int y, int width, int height)
+{
+ uint8_t *pixbuf = NULL;
+
+ if (width && height) {
+ uint8_t *data, *mask, *datap, *maskp;
+ uint32_t *pixp;
+ int rowlen;
+ int x1, y1;
+ uint8_t fgrgb[3], bgrgb[3];
+ uint32_t fg, bg;
+ gvnc_read(gvnc, fgrgb, 3);
+ gvnc_read(gvnc, bgrgb, 3);
+ fg = (255 << 24) | (fgrgb[0] << 16) | (fgrgb[1] << 8) | fgrgb[2];
+ bg = (255 << 24) | (bgrgb[0] << 16) | (bgrgb[1] << 8) | bgrgb[2];
+
+ rowlen = ((width + 7)/8);
+ if (!(data = malloc(rowlen*height))) {
+ gvnc->has_error = TRUE;
+ return;
+ }
+ if (!(mask = malloc(rowlen*height))) {
+ free(data);
+ gvnc->has_error = TRUE;
+ return;
+ }
+ pixbuf = malloc(width * height * 4); /* RGB-A 8bit */
+ gvnc_read(gvnc, data, rowlen*height);
+ gvnc_read(gvnc, mask, rowlen*height);
+ datap = data;
+ maskp = mask;
+ pixp = (uint32_t*)pixbuf;
+ for (y1 = 0; y1 < height; y1++) {
+ for (x1 = 0; x1 < width; x1++) {
+ *pixp++ = ((maskp[x1 / 8] >> (7-(x1 % 8))) & 1) ?
+ (((datap[x1 / 8] >> (7-(x1 % 8))) & 1) ? fg : bg) : 0;
+ }
+ datap += rowlen;
+ maskp += rowlen;
+ }
+ free(data);
+ free(mask);
+ }
+
+
+
+ if (gvnc->has_error || !gvnc->ops.local_cursor)
+ return;
+ if (!gvnc->ops.local_cursor(gvnc->ops_data, x, y, width, height, pixbuf))
+ gvnc->has_error = TRUE;
+
+ free(pixbuf);
+}
+
static void gvnc_framebuffer_update(struct gvnc *gvnc, int32_t etype,
uint16_t x, uint16_t y,
@@ -1096,6 +1209,12 @@ static void gvnc_framebuffer_update(stru
break;
}
break;
+ case GVNC_ENCODING_RICH_CURSOR:
+ gvnc_rich_cursor(gvnc, x, y, width, height);
+ break;
+ case GVNC_ENCODING_XCURSOR:
+ gvnc_xcursor(gvnc, x, y, width, height);
+ break;
default:
gvnc->has_error = TRUE;
break;
diff -r b1c48ddc01d9 src/gvnc.h
--- a/src/gvnc.h Wed Aug 22 15:10:12 2007 -0400
+++ b/src/gvnc.h Thu Aug 30 21:26:43 2007 -0400
@@ -18,6 +18,7 @@ struct gvnc_ops
gboolean (*resize)(void *, int, int);
gboolean (*pointer_type_change)(void *, int);
gboolean (*shared_memory_rmid)(void *, int);
+ gboolean (*local_cursor)(void *, int, int, int, int, uint8_t *);
};
struct gvnc_pixel_format
@@ -68,7 +69,7 @@ typedef enum {
GVNC_ENCODING_DESKTOP_RESIZE = -223,
GVNC_ENCODING_CURSOR_POS = -232,
GVNC_ENCODING_RICH_CURSOR = -239,
- GVNC_ENCODING_XCUSOR = -240,
+ GVNC_ENCODING_XCURSOR = -240,
GVNC_ENCODING_POINTER_CHANGE = -257,
GVNC_ENCODING_SHARED_MEMORY = -258,
diff -r b1c48ddc01d9 src/vncdisplay.c
--- a/src/vncdisplay.c Wed Aug 22 15:10:12 2007 -0400
+++ b/src/vncdisplay.c Thu Aug 30 21:32:29 2007 -0400
@@ -35,6 +35,7 @@ struct _VncDisplayPrivate
GdkGC *gc;
VncShmImage *shm_image;
GdkCursor *null_cursor;
+ GdkCursor *remote_cursor;
struct gvnc_framebuffer fb;
struct coroutine coroutine;
@@ -140,7 +141,7 @@ static gboolean expose_event(GtkWidget *
return TRUE;
}
-static void do_keyboard_grab(VncDisplay *obj)
+static void do_keyboard_grab(VncDisplay *obj, gboolean quiet)
{
VncDisplayPrivate *priv = obj->priv;
@@ -148,27 +149,29 @@ static void do_keyboard_grab(VncDisplay
FALSE,
GDK_CURRENT_TIME);
priv->in_keyboard_grab = TRUE;
- g_signal_emit(obj, signals[VNC_KEYBOARD_GRAB], 0);
-}
-
-
-static void do_keyboard_ungrab(VncDisplay *obj)
+ if (!quiet)
+ g_signal_emit(obj, signals[VNC_KEYBOARD_GRAB], 0);
+}
+
+
+static void do_keyboard_ungrab(VncDisplay *obj, gboolean quiet)
{
VncDisplayPrivate *priv = obj->priv;
gdk_keyboard_ungrab(GDK_CURRENT_TIME);
priv->in_keyboard_grab = FALSE;
- g_signal_emit(obj, signals[VNC_KEYBOARD_UNGRAB], 0);
-}
-
-
-static void do_pointer_grab(VncDisplay *obj)
+ if (!quiet)
+ g_signal_emit(obj, signals[VNC_KEYBOARD_UNGRAB], 0);
+}
+
+
+static void do_pointer_grab(VncDisplay *obj, gboolean quiet)
{
VncDisplayPrivate *priv = obj->priv;
/* If we're not already grabbing keyboard, grab it now */
if (!priv->grab_keyboard)
- do_keyboard_grab(obj);
+ do_keyboard_grab(obj, quiet);
gdk_pointer_grab(GTK_WIDGET(obj)->window,
TRUE,
@@ -178,36 +181,39 @@ static void do_pointer_grab(VncDisplay *
GDK_BUTTON_MOTION_MASK |
GDK_SCROLL_MASK,
GTK_WIDGET(obj)->window,
- priv->null_cursor,
+ priv->remote_cursor ? priv->remote_cursor : priv->null_cursor,
GDK_CURRENT_TIME);
priv->in_pointer_grab = TRUE;
- g_signal_emit(obj, signals[VNC_POINTER_GRAB], 0);
-}
-
-static void do_pointer_ungrab(VncDisplay *obj)
+ if (!quiet)
+ g_signal_emit(obj, signals[VNC_POINTER_GRAB], 0);
+}
+
+static void do_pointer_ungrab(VncDisplay *obj, gboolean quiet)
{
VncDisplayPrivate *priv = obj->priv;
/* If we grabed keyboard upon pointer grab, then ungrab it now */
if (!priv->grab_keyboard)
- do_keyboard_ungrab(obj);
+ do_keyboard_ungrab(obj, quiet);
gdk_pointer_ungrab(GDK_CURRENT_TIME);
priv->in_pointer_grab = FALSE;
- g_signal_emit(obj, signals[VNC_POINTER_UNGRAB], 0);
+ if (!quiet)
+ g_signal_emit(obj, signals[VNC_POINTER_UNGRAB], 0);
}
static void do_pointer_hide(VncDisplay *obj)
{
VncDisplayPrivate *priv = obj->priv;
gdk_window_set_cursor(GTK_WIDGET(obj)->window,
- priv->null_cursor);
+ priv->remote_cursor ? priv->remote_cursor : priv->null_cursor);
}
static void do_pointer_show(VncDisplay *obj)
{
+ VncDisplayPrivate *priv = obj->priv;
gdk_window_set_cursor(GTK_WIDGET(obj)->window,
- NULL);
+ priv->remote_cursor);
}
@@ -223,7 +229,7 @@ static gboolean button_event(GtkWidget *
if ((priv->grab_pointer || !priv->absolute) &&
!priv->in_pointer_grab &&
button->button == 1 && button->type == GDK_BUTTON_PRESS)
- do_pointer_grab(VNC_DISPLAY(widget));
+ do_pointer_grab(VNC_DISPLAY(widget), FALSE);
n = 1 << (button->button - 1);
if (button->type == GDK_BUTTON_PRESS)
@@ -369,9 +375,9 @@ static gboolean key_event(GtkWidget *wid
((keyval == GDK_Control_L && (key->state & GDK_MOD1_MASK)) ||
(keyval == GDK_Alt_L && (key->state & GDK_CONTROL_MASK)))) {
if (priv->in_pointer_grab)
- do_pointer_ungrab(VNC_DISPLAY(widget));
+ do_pointer_ungrab(VNC_DISPLAY(widget), FALSE);
else
- do_pointer_grab(VNC_DISPLAY(widget));
+ do_pointer_grab(VNC_DISPLAY(widget), FALSE);
}
return TRUE;
@@ -389,7 +395,7 @@ static gboolean enter_event(GtkWidget *w
return TRUE;
if (priv->grab_keyboard)
- do_keyboard_grab(VNC_DISPLAY(widget));
+ do_keyboard_grab(VNC_DISPLAY(widget), FALSE);
return TRUE;
}
@@ -406,7 +412,7 @@ static gboolean leave_event(GtkWidget *w
return TRUE;
if (priv->grab_keyboard)
- do_keyboard_ungrab(VNC_DISPLAY(widget));
+ do_keyboard_ungrab(VNC_DISPLAY(widget), FALSE);
return TRUE;
}
@@ -482,7 +488,7 @@ static gboolean on_pointer_type_change(v
VncDisplayPrivate *priv = obj->priv;
if (absolute && priv->in_pointer_grab && !priv->grab_pointer)
- do_pointer_ungrab(obj);
+ do_pointer_ungrab(obj, FALSE);
priv->absolute = absolute;
return TRUE;
@@ -570,6 +576,35 @@ static gboolean on_auth_subtype(void *op
gvnc_set_auth_subtype(priv->gvnc, types[0]);
return TRUE;
+}
+
+static gboolean on_local_cursor(void *opaque, int x, int y, int width, int height, uint8_t *image)
+{
+ VncDisplay *obj = VNC_DISPLAY(opaque);
+ VncDisplayPrivate *priv = obj->priv;
+
+ if (priv->remote_cursor) {
+ gdk_cursor_unref(priv->remote_cursor);
+ priv->remote_cursor = NULL;
+ }
+
+ if (width && height) {
+ GdkDisplay *display = gdk_drawable_get_display(GDK_DRAWABLE(GTK_WIDGET(obj)->window));
+ GdkPixbuf *pixbuf = gdk_pixbuf_new_from_data(image, GDK_COLORSPACE_RGB,
+ TRUE, 8, width, height,
+ width * 4, NULL, NULL);
+ priv->remote_cursor = gdk_cursor_new_from_pixbuf(display,
+ pixbuf,
+ x, y);
+ gdk_pixbuf_unref(pixbuf);
+ }
+
+ if (priv->in_pointer_grab) {
+ do_pointer_ungrab(obj, TRUE);
+ do_pointer_grab(obj, TRUE);
+ } else {
+ do_pointer_hide(obj);
+ }
}
@@ -581,6 +616,7 @@ static const struct gvnc_ops vnc_display
.resize = on_resize,
.pointer_type_change = on_pointer_type_change,
.shared_memory_rmid = on_shared_memory_rmid,
+ .local_cursor = on_local_cursor,
};
static void *vnc_coroutine(void *opaque)
@@ -588,6 +624,8 @@ static void *vnc_coroutine(void *opaque)
VncDisplay *obj = VNC_DISPLAY(opaque);
VncDisplayPrivate *priv = obj->priv;
int32_t encodings[] = { GVNC_ENCODING_DESKTOP_RESIZE,
+ GVNC_ENCODING_RICH_CURSOR,
+ GVNC_ENCODING_XCURSOR,
GVNC_ENCODING_SHARED_MEMORY,
GVNC_ENCODING_POINTER_CHANGE,
GVNC_ENCODING_HEXTILE,
@@ -966,7 +1004,7 @@ void vnc_display_set_pointer_grab(VncDis
priv->grab_pointer = enable;
if (!enable && priv->absolute && priv->in_pointer_grab)
- do_pointer_ungrab(obj);
+ do_pointer_ungrab(obj, FALSE);
}
void vnc_display_set_keyboard_grab(VncDisplay *obj, gboolean enable)
@@ -975,7 +1013,7 @@ void vnc_display_set_keyboard_grab(VncDi
priv->grab_keyboard = enable;
if (!enable && priv->in_keyboard_grab && !priv->in_pointer_grab)
- do_keyboard_ungrab(obj);
+ do_keyboard_ungrab(obj, FALSE);
}
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]