[gtk-vnc-devel] [PATCH][RFC] Support for ExtendedKeyEvent client message



I've included both the gtk-vnc patch and the QEMU patch. I'd like to apply the gtk-vnc one before submitting the QEMU one so please review. I'm pretty sure I've got it right.

Regards,

Anthony Liguori
This patch adds support for the ExtendedKeyEvent client message.  This message
allows a client to send raw scan codes directly to the server.  If the client
and server are using the same keymap, then it's unnecessary to use the '-k'
option with QEMU when this extension is supported.

Index: qemu/vnc.c
===================================================================
--- qemu.orig/vnc.c	2008-01-12 18:36:36.000000000 -0600
+++ qemu/vnc.c	2008-01-12 18:36:43.000000000 -0600
@@ -927,12 +927,8 @@
     kbd_put_keycode(keysym2scancode(vs->kbd_layout, keysym) | 0x80);
 }
 
-static void do_key_event(VncState *vs, int down, uint32_t sym)
+static void do_key_event(VncState *vs, int down, int keycode, int sym)
 {
-    int keycode;
-
-    keycode = keysym2scancode(vs->kbd_layout, sym & 0xFFFF);
-
     /* QEMU console switch */
     switch(keycode) {
     case 0x2a:                          /* Left Shift */
@@ -1033,9 +1029,32 @@
 
 static void key_event(VncState *vs, int down, uint32_t sym)
 {
+    int keycode;
+
     if (sym >= 'A' && sym <= 'Z' && is_graphic_console())
 	sym = sym - 'A' + 'a';
-    do_key_event(vs, down, sym);
+
+    keycode = keysym2scancode(vs->kbd_layout, sym & 0xFFFF);
+    do_key_event(vs, down, keycode, sym);
+}
+
+static void ext_key_event(VncState *vs, int down,
+			  uint32_t sym, uint16_t scancode)
+{
+    int keycode;
+
+    keycode = scancode;
+    if (keycode < 9)
+	keycode = 0;
+    else if (keycode < 97)
+	keycode -= 8;
+    else if (keycode < 212)
+	keycode = _translate_keycode(keycode - 97);
+    else
+	keycode = 0;
+
+    do_key_event(vs, down, keycode, sym);
+
 }
 
 static void framebuffer_update_request(VncState *vs, int incremental,
@@ -1065,6 +1084,15 @@
     }
 }
 
+static void send_ext_key_event_ack(VncState *vs)
+{
+    vnc_write_u8(vs, 0);
+    vnc_write_u8(vs, 0);
+    vnc_write_u16(vs, 1);
+    vnc_framebuffer_update(vs, 0, 0, vs->ds->width, vs->ds->height, -258);
+    vnc_flush(vs);
+}
+
 static void set_encodings(VncState *vs, int32_t *encodings, size_t n_encodings)
 {
     int i;
@@ -1092,6 +1120,9 @@
 	case -257:
 	    vs->has_pointer_type_change = 1;
 	    break;
+	case -258:
+	    send_ext_key_event_ack(vs);
+	    break;
 	default:
 	    break;
 	}
@@ -1245,6 +1276,24 @@
 
 	client_cut_text(vs, read_u32(data, 4), data + 8);
 	break;
+    case 255:
+	if (len == 1)
+	    return 2;
+
+	switch (read_u8(data, 1)) {
+	case 0:
+	    if (len == 2)
+		return 12;
+
+	    ext_key_event(vs, read_u16(data, 2),
+			  read_u32(data, 4), read_u32(data, 8));
+	    break;
+	default:
+	    printf("Msg: %d\n", read_u16(data, 0));
+	    vnc_client_error(vs);
+	    break;
+	}
+	break;
     default:
 	printf("Msg: %d\n", data[0]);
 	vnc_client_error(vs);
This patch implements support for the ExtendKeyEvent client message.  This
message allows scan codes to be sent directly to the server.  This is
particularly useful for virtualization as servers normally have to translate
from VNC key symbols to PC scan codes.  In the event that the host and guest
are using the same keymap, it's unnecessary to explicitly specify the keymap
when creating the guest.

diff -r 492cc3ad13a7 src/gvnc.c
--- a/src/gvnc.c	Fri Jan 11 17:38:00 2008 -0600
+++ b/src/gvnc.c	Sat Jan 12 18:33:05 2008 -0600
@@ -158,6 +158,8 @@ struct gvnc
 
 	uint8_t zrle_pi;
 	int zrle_pi_bits;
+
+	gboolean has_ext_key_event;
 };
 
 #define nibhi(a) (((a) >> 4) & 0x0F)
@@ -815,6 +817,7 @@ gboolean gvnc_set_encodings(struct gvnc 
 	uint8_t pad[1] = {0};
 	int i;
 
+	gvnc->has_ext_key_event = FALSE;
 	gvnc_write_u8(gvnc, 2);
 	gvnc_write(gvnc, pad, 1);
 	gvnc_write_u16(gvnc, n_encoding);
@@ -879,14 +882,31 @@ static void gvnc_buffered_flush(struct g
 	g_io_wakeup(&gvnc->wait);
 }
 
-gboolean gvnc_key_event(struct gvnc *gvnc, uint8_t down_flag, uint32_t key)
+gboolean gvnc_key_event(struct gvnc *gvnc, uint8_t down_flag,
+			uint32_t key, uint16_t scancode)
 {
 	uint8_t pad[2] = {0};
 
-	gvnc_buffered_write_u8(gvnc, 4);
-	gvnc_buffered_write_u8(gvnc, down_flag);
-	gvnc_buffered_write(gvnc, pad, 2);
-	gvnc_buffered_write_u32(gvnc, key);
+	if (gvnc->has_ext_key_event) {
+		gvnc_buffered_write_u8(gvnc, 255);
+		gvnc_buffered_write_u8(gvnc, 0);
+		gvnc_buffered_write_u16(gvnc, down_flag);
+		gvnc_buffered_write_u32(gvnc, key);
+		gvnc_buffered_write_u32(gvnc, scancode);
+	} else {
+		gvnc_buffered_write_u8(gvnc, 4);
+		gvnc_buffered_write_u8(gvnc, down_flag);
+		gvnc_buffered_write(gvnc, pad, 2);
+		gvnc_buffered_write_u32(gvnc, key);
+	}
+
+	gvnc_buffered_flush(gvnc);
+	return !gvnc_has_error(gvnc);
+}
+
+gboolean gvnc_key_event_scancode(struct gvnc *gvnc, uint8_t down_flag,
+				 uint16_t scancode)
+{
 	gvnc_buffered_flush(gvnc);
 	return !gvnc_has_error(gvnc);
 }
@@ -1852,6 +1872,9 @@ static void gvnc_framebuffer_update(stru
 	case GVNC_ENCODING_XCURSOR:
 		gvnc_xcursor(gvnc, x, y, width, height);
 		break;
+	case GVNC_ENCODING_EXT_KEY_EVENT:
+		gvnc->has_ext_key_event = TRUE;
+		break;
 	default:
 		gvnc->has_error = TRUE;
 		break;
diff -r 492cc3ad13a7 src/gvnc.h
--- a/src/gvnc.h	Fri Jan 11 17:38:00 2008 -0600
+++ b/src/gvnc.h	Sat Jan 12 18:33:05 2008 -0600
@@ -89,6 +89,7 @@ typedef enum {
 	GVNC_ENCODING_XCURSOR = -240,
 
 	GVNC_ENCODING_POINTER_CHANGE = -257,
+	GVNC_ENCODING_EXT_KEY_EVENT = -258,
 } gvnc_encoding;
 
 typedef enum {
@@ -149,7 +150,8 @@ gboolean gvnc_pointer_event(struct gvnc 
 gboolean gvnc_pointer_event(struct gvnc *gvnc, uint8_t button_mask,
 			    uint16_t x, uint16_t y);
 
-gboolean gvnc_key_event(struct gvnc *gvnc, uint8_t down_flag, uint32_t key);
+gboolean gvnc_key_event(struct gvnc *gvnc, uint8_t down_flag,
+			uint32_t key, uint16_t scancode);
 
 gboolean gvnc_framebuffer_update_request(struct gvnc *gvnc,
 					 uint8_t incremental,
diff -r 492cc3ad13a7 src/vncdisplay.c
--- a/src/vncdisplay.c	Fri Jan 11 17:38:00 2008 -0600
+++ b/src/vncdisplay.c	Sat Jan 12 18:33:05 2008 -0600
@@ -47,6 +47,7 @@ struct _VncDisplayPrivate
 	gboolean in_keyboard_grab;
 
 	guint down_keyval[16];
+	guint down_scancode[16];
 
 	int button_mask;
 	int last_x;
@@ -418,8 +419,9 @@ static gboolean key_event(GtkWidget *wid
 		for (i = 0 ; i < (int)(sizeof(priv->down_keyval)/sizeof(priv->down_keyval[0])) ; i++) {
 			if (priv->down_keyval[i] == 0) {
 				priv->down_keyval[i] = keyval;
+				priv->down_scancode[i] = key->hardware_keycode;
 				/* Send the actual key event we're dealing with */
-				gvnc_key_event(priv->gvnc, 1, keyval);
+				gvnc_key_event(priv->gvnc, 1, keyval, key->hardware_keycode);
 				break;
 			} else if (priv->down_keyval[i] == keyval) {
 				/* Got an press when we're already pressed ! Why ... ?
@@ -431,9 +433,9 @@ static gboolean key_event(GtkWidget *wid
 				 * them into a sensible stream of press+release pairs
 				 */
 				/* Fake an up event for the previous down event */
-				gvnc_key_event(priv->gvnc, 0, keyval);
+				gvnc_key_event(priv->gvnc, 0, keyval, key->hardware_keycode);
 				/* Now send our actual ldown event */
-				gvnc_key_event(priv->gvnc, 1, keyval);
+				gvnc_key_event(priv->gvnc, 1, keyval, key->hardware_keycode);
 			}
 		}
 	} else {
@@ -442,8 +444,9 @@ static gboolean key_event(GtkWidget *wid
 			/* We were pressed, and now we're released, so... */
 			if (priv->down_keyval[i] == keyval) {
 				priv->down_keyval[i] = 0;
+				priv->down_scancode[i] = 0;
 				/* ..send the key releae event we're dealing with */
-				gvnc_key_event(priv->gvnc, 0, keyval);
+				gvnc_key_event(priv->gvnc, 0, keyval, key->hardware_keycode);
 				break;
 			}
 		}
@@ -509,8 +512,10 @@ static gboolean focus_event(GtkWidget *w
 		/* We are currently pressed so... */
 		if (priv->down_keyval[i] != 0) {
 			/* ..send the fake key releae event to match */
-			gvnc_key_event(priv->gvnc, 0, priv->down_keyval[i]);
+			gvnc_key_event(priv->gvnc, 0,
+				       priv->down_keyval[i], priv->down_scancode[i]);
 			priv->down_keyval[i] = 0;
+			priv->down_scancode[i] = 0;
 		}
 	}
 
@@ -803,6 +808,7 @@ static void *vnc_coroutine(void *opaque)
 	/* this order is extremely important! */
 	int32_t encodings[] = {	GVNC_ENCODING_TIGHT_JPEG5,
 				GVNC_ENCODING_TIGHT,
+				GVNC_ENCODING_EXT_KEY_EVENT,
 				GVNC_ENCODING_DESKTOP_RESIZE,
 				GVNC_ENCODING_RICH_CURSOR,
 				GVNC_ENCODING_XCURSOR,
@@ -971,21 +977,40 @@ void vnc_display_send_keys(VncDisplay *o
 				 nkeyvals, VNC_DISPLAY_KEY_EVENT_CLICK);
 }
 
+static guint get_keycode_from_keyval(guint keyval)
+{
+	guint keycode = 0;
+	GdkKeymapKey *keys = NULL;
+	gint n_keys = 0;
+
+	if (gdk_keymap_get_entries_for_keyval(gdk_keymap_get_default(),
+					      keyval, &keys, &n_keys)) {
+		/* FIXME what about levels? */
+		keycode = keys[0].keycode;
+		g_free(keys);
+	}
+
+	return keycode;
+}
+
 void vnc_display_send_keys_ex(VncDisplay *obj, const guint *keyvals,
 			      int nkeyvals, VncDisplayKeyEvent kind)
 {
 	int i;
+
 	if (obj->priv->gvnc == NULL || !gvnc_is_open(obj->priv->gvnc))
 		return;
 
 	if (kind & VNC_DISPLAY_KEY_EVENT_PRESS) {
 		for (i = 0 ; i < nkeyvals ; i++)
-			gvnc_key_event(obj->priv->gvnc, 1, keyvals[i]);
+			gvnc_key_event(obj->priv->gvnc, 1, keyvals[i],
+				       get_keycode_from_keyval(keyvals[i]));
 	}
 
 	if (kind & VNC_DISPLAY_KEY_EVENT_RELEASE) {
 		for (i = (nkeyvals-1) ; i >= 0 ; i--)
-			gvnc_key_event(obj->priv->gvnc, 0, keyvals[i]);
+			gvnc_key_event(obj->priv->gvnc, 0, keyvals[i],
+				       get_keycode_from_keyval(keyvals[i]));
 	}
 }
 


[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]