Signed-off-by: Dietmar Maurer <dietmar@proxmox.com>

Index: new/gtk/channel-inputs.c
===================================================================
--- new.orig/gtk/channel-inputs.c	2013-10-16 09:53:10.000000000 +0200
+++ new/gtk/channel-inputs.c	2013-10-16 10:00:11.000000000 +0200
@@ -451,6 +451,21 @@
     spice_msg_out_send(msg);
 }
 
+static void spice_inputs_key_utf8(SpiceInputsChannel *channel, gunichar uc)
+{
+    SpiceMsgOut *msg;
+
+    g_assert(uc != 0);
+
+    uint8_t data[32];
+    uint32_t size = g_unichar_to_utf8(uc, (gchar*)data);
+
+    msg = spice_msg_out_new(SPICE_CHANNEL(channel), SPICE_MSGC_INPUTS_KEY_UTF8);
+    uint8_t *buf = (guint8*)spice_marshaller_reserve_space(msg->marshaller, size);
+    memcpy(buf, data, size);
+    spice_msg_out_send(msg);
+}
+
 /**
  * spice_inputs_key_press:
  * @channel:
@@ -458,7 +473,7 @@
  *
  * Press a key.
  **/
-void spice_inputs_key_press(SpiceInputsChannel *channel, guint scancode)
+void spice_inputs_key_press(SpiceInputsChannel *channel, guint scancode, gunichar uc)
 {
     SpiceMsgcKeyDown down;
     SpiceMsgOut *msg;
@@ -474,6 +489,10 @@
     msg = spice_msg_out_new(SPICE_CHANNEL(channel), SPICE_MSGC_INPUTS_KEY_DOWN);
     msg->marshallers->msgc_inputs_key_down(msg->marshaller, &down);
     spice_msg_out_send(msg);
+
+    if (uc && spice_channel_test_capability(SPICE_CHANNEL(channel), SPICE_INPUTS_CAP_KEY_UTF8)) {
+        spice_inputs_key_utf8(channel, uc);
+    }
 }
 
 /**
@@ -510,7 +529,7 @@
  *
  * Since: 0.13
  **/
-void spice_inputs_key_press_and_release(SpiceInputsChannel *input_channel, guint scancode)
+void spice_inputs_key_press_and_release(SpiceInputsChannel *input_channel, guint scancode, gunichar uc)
 {
     SpiceChannel *channel = SPICE_CHANNEL(input_channel);
 
@@ -544,9 +563,13 @@
         spice_msg_out_send(msg);
     } else {
         CHANNEL_DEBUG(channel, "The server doesn't support atomic press and release");
-        spice_inputs_key_press(input_channel, scancode);
+        spice_inputs_key_press(input_channel, scancode, uc);
         spice_inputs_key_release(input_channel, scancode);
     }
+
+    if (uc && spice_channel_test_capability(channel, SPICE_INPUTS_CAP_KEY_UTF8)) {
+        spice_inputs_key_utf8(input_channel, uc);
+    }
 }
 
 /* main or coroutine context */
Index: new/gtk/channel-inputs.h
===================================================================
--- new.orig/gtk/channel-inputs.h	2013-10-16 09:53:10.000000000 +0200
+++ new/gtk/channel-inputs.h	2013-10-16 09:53:16.000000000 +0200
@@ -79,10 +79,10 @@
                                gint button_state);
 void spice_inputs_button_release(SpiceInputsChannel *channel, gint button,
                                  gint button_state);
-void spice_inputs_key_press(SpiceInputsChannel *channel, guint scancode);
+void spice_inputs_key_press(SpiceInputsChannel *channel, guint scancode, gunichar unicode);
 void spice_inputs_key_release(SpiceInputsChannel *channel, guint scancode);
 void spice_inputs_set_key_locks(SpiceInputsChannel *channel, guint locks);
-void spice_inputs_key_press_and_release(SpiceInputsChannel *channel, guint scancode);
+void spice_inputs_key_press_and_release(SpiceInputsChannel *channel, guint scancode, gunichar unicode);
 
 G_END_DECLS
 
Index: new/gtk/spice-widget-priv.h
===================================================================
--- new.orig/gtk/spice-widget-priv.h	2013-10-16 09:53:10.000000000 +0200
+++ new/gtk/spice-widget-priv.h	2013-10-16 09:53:16.000000000 +0200
@@ -108,6 +108,7 @@
     size_t                  keycode_maplen;
     uint32_t                key_state[512 / 32];
     int                     key_delayed_scancode;
+    guint                   key_delayed_keysym;
     guint                   key_delayed_id;
     SpiceGrabSequence         *grabseq; /* the configured key sequence */
     gboolean                *activeseq; /* the currently pressed keys */
Index: new/gtk/spice-widget.c
===================================================================
--- new.orig/gtk/spice-widget.c	2013-10-16 09:53:10.000000000 +0200
+++ new/gtk/spice-widget.c	2013-10-16 09:53:16.000000000 +0200
@@ -1153,8 +1153,10 @@
     if (d->key_delayed_scancode == 0)
         return;
 
-    spice_inputs_key_press_and_release(d->inputs, d->key_delayed_scancode);
+    spice_inputs_key_press_and_release(d->inputs, d->key_delayed_scancode,
+                                       gdk_keyval_to_unicode(d->key_delayed_keysym));
     d->key_delayed_scancode = 0;
+    d->key_delayed_keysym = 0;
 
     if (d->key_delayed_id) {
         g_source_remove(d->key_delayed_id);
@@ -1169,8 +1171,10 @@
     if (d->key_delayed_scancode == 0)
         return FALSE;
 
-    spice_inputs_key_press(d->inputs, d->key_delayed_scancode);
+    spice_inputs_key_press(d->inputs, d->key_delayed_scancode,
+                           gdk_keyval_to_unicode(d->key_delayed_keysym));
     d->key_delayed_scancode = 0;
+    d->key_delayed_keysym = 0;
 
     if (d->key_delayed_id) {
         g_source_remove(d->key_delayed_id);
@@ -1180,7 +1184,7 @@
     return FALSE;
 }
 
-static void send_key(SpiceDisplay *display, int scancode, SendKeyType type, gboolean press_delayed)
+static void send_key(SpiceDisplay *display, int scancode, guint keysym, SendKeyType type, gboolean press_delayed)
 {
     SpiceDisplayPrivate *d = SPICE_DISPLAY_GET_PRIVATE(display);
     uint32_t i, b, m;
@@ -1209,8 +1213,9 @@
             g_warn_if_fail(d->key_delayed_id == 0);
             d->key_delayed_id = g_timeout_add(d->keypress_delay, key_press_delayed, display);
             d->key_delayed_scancode = scancode;
+            d->key_delayed_keysym = keysym;
         } else
-            spice_inputs_key_press(d->inputs, scancode);
+            spice_inputs_key_press(d->inputs, scancode, gdk_keyval_to_unicode(keysym));
 
         d->key_state[i] |= m;
         break;
@@ -1246,7 +1251,8 @@
             continue;
         }
         for (b = 0; b < 32; b++) {
-            send_key(display, i * 32 + b, SEND_KEY_RELEASE, FALSE);
+            // note: using keysym 0 here, because we do not have this information
+            send_key(display, i * 32 + b, 0, SEND_KEY_RELEASE, FALSE);
         }
     }
 }
@@ -1337,10 +1343,10 @@
 
     switch (key->type) {
     case GDK_KEY_PRESS:
-        send_key(display, scancode, SEND_KEY_PRESS, !key->is_modifier);
+        send_key(display, scancode, key->keyval, SEND_KEY_PRESS, !key->is_modifier);
         break;
     case GDK_KEY_RELEASE:
-        send_key(display, scancode, SEND_KEY_RELEASE, !key->is_modifier);
+        send_key(display, scancode, key->keyval, SEND_KEY_RELEASE, !key->is_modifier);
         break;
     default:
         g_warn_if_reached();
@@ -1393,12 +1399,12 @@
 
     if (kind & SPICE_DISPLAY_KEY_EVENT_PRESS) {
         for (i = 0 ; i < nkeyvals ; i++)
-            send_key(display, get_scancode_from_keyval(display, keyvals[i]), SEND_KEY_PRESS, FALSE);
+            send_key(display, get_scancode_from_keyval(display, keyvals[i]), keyvals[i], SEND_KEY_PRESS, FALSE);
     }
 
     if (kind & SPICE_DISPLAY_KEY_EVENT_RELEASE) {
         for (i = (nkeyvals-1) ; i >= 0 ; i--)
-            send_key(display, get_scancode_from_keyval(display, keyvals[i]), SEND_KEY_RELEASE, FALSE);
+            send_key(display, get_scancode_from_keyval(display, keyvals[i]), keyvals[i], SEND_KEY_RELEASE, FALSE);
     }
 }
 

