Re: [gtk-vnc-devel] PATCH: Implement cursor encoding



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]