Note: This is an ugly hack because there is currently no clean way to do it.

But experiments showed that the X11 agent is the wrong place to do such things, because it is started to late (after login).

Future: Maybe it is possible to extent the linux uinput framework for that purpose?


Index: new/src/vdagent.c
===================================================================
--- new.orig/src/vdagent.c	2013-10-14 14:52:01.000000000 +0200
+++ new/src/vdagent.c	2013-10-15 12:32:42.000000000 +0200
@@ -57,6 +57,12 @@
     struct udscs_message_header *header, uint8_t *data)
 {
     switch (header->type) {
+    case VDAGENTD_KEYSYM_MESSAGE: {
+        VDAgentKeySym *info = (VDAgentKeySym *)data;
+        vdagent_x11_send_keysym(x11, info->keysym, info->flags);
+        free(data);
+        break;
+    }
     case VDAGENTD_MONITORS_CONFIG:
         vdagent_x11_set_monitor_config(x11, (VDAgentMonitorsConfig *)data, 0);
         free(data);
Index: new/src/vdagentd.c
===================================================================
--- new.orig/src/vdagentd.c	2013-10-14 14:52:01.000000000 +0200
+++ new/src/vdagentd.c	2013-10-15 12:34:34.000000000 +0200
@@ -98,6 +98,7 @@
     VD_AGENT_SET_CAPABILITY(caps->caps, VD_AGENT_CAP_CLIPBOARD_SELECTION);
     VD_AGENT_SET_CAPABILITY(caps->caps, VD_AGENT_CAP_SPARSE_MONITORS_CONFIG);
     VD_AGENT_SET_CAPABILITY(caps->caps, VD_AGENT_CAP_GUEST_LINEEND_LF);
+    VD_AGENT_SET_CAPABILITY(caps->caps, VD_AGENT_CAP_KEYSYM);
 
     vdagent_virtio_port_write(vport, VDP_CLIENT_PORT,
                               VD_AGENT_ANNOUNCE_CAPABILITIES, 0,
@@ -232,6 +233,20 @@
                 data, size);
 }
 
+static void do_client_keysym(struct vdagent_virtio_port *vport,
+    VDAgentMessage *message_header, uint8_t *data)
+{
+    if (!active_session_conn) {
+        syslog(LOG_WARNING,
+               "Could not find an agent connection belonging to the "
+               "active session, ignoring client keysym request");
+        return;
+    }
+
+    udscs_write(active_session_conn, VDAGENTD_KEYSYM_MESSAGE, 0, 0,
+                data, message_header->size);
+}
+
 static void cancel_file_xfer(struct vdagent_virtio_port *vport,
                              const char *msg, uint32_t id)
 {
@@ -304,6 +319,10 @@
     }
 
     switch (message_header->type) {
+    case VD_AGENT_KEYSYM_MESSAGE: {
+        do_client_keysym(vport, message_header, data);
+        break;
+    }
     case VD_AGENT_MOUSE_STATE:
         if (message_header->size != sizeof(VDAgentMouseState))
             goto size_error;
Index: new/src/vdagentd-proto.h
===================================================================
--- new.orig/src/vdagentd-proto.h	2013-10-15 07:49:58.000000000 +0200
+++ new/src/vdagentd-proto.h	2013-10-15 07:50:34.000000000 +0200
@@ -40,6 +40,7 @@
     VDAGENTD_FILE_XFER_STATUS,
     VDAGENTD_FILE_XFER_DATA,
     VDAGENTD_CLIENT_DISCONNECTED,  /* daemon -> client */
+    VDAGENTD_KEYSYM_MESSAGE,
     VDAGENTD_NO_MESSAGES /* Must always be last */
 };
 
Index: new/src/vdagent-x11.c
===================================================================
--- new.orig/src/vdagent-x11.c	2013-10-14 14:52:01.000000000 +0200
+++ new/src/vdagent-x11.c	2013-10-15 12:35:32.000000000 +0200
@@ -111,6 +111,56 @@
     return error;
 }
 
+void vdagent_x11_send_keysym(struct vdagent_x11 *x11, uint32_t keysym, uint32_t flags)
+{
+    //syslog(LOG_INFO, "vdagent_x11_send_keysym %08x", keysym);
+
+    /* Scratch space for temporary keycode bindings */
+    int scratch_keycode = 84; // use KEY_KPDOT
+    int keymap_changed = 0;
+
+    static int orig_keysym = 0;
+
+    if (orig_keysym == 0) {
+        int count = 0;
+        KeySym *res = XGetKeyboardMapping(x11->display, scratch_keycode, 1, &count);
+        if (count == 1) {
+            orig_keysym = res[0];
+            XFree(res);
+        } else {
+            orig_keysym = -1;
+        }
+    }
+
+    int kc = XKeysymToKeycode(x11->display, keysym);
+    if (kc && ((keysym >= 0xffe1 && keysym <= 0xffee) ||
+               (keysym >= 0xfe01 && keysym <= 0xfe0f))) { //  never remap Modifiers
+        //syslog(LOG_INFO, "use real keycode %d for modifier %08x", kc, keysym);
+        scratch_keycode = kc;
+    } else {
+        KeySym keysym_list[] = { keysym };
+        keymap_changed = 1;
+        XChangeKeyboardMapping(x11->display, scratch_keycode, 1, keysym_list, 1);
+    }
+
+    if (flags & VD_AGENT_KEYSYM_FLAG_DOWN) {
+        //syslog(LOG_INFO, "vdagent_x11_send_keysym down %08x", keysym);
+        XTestFakeKeyEvent(x11->display, scratch_keycode, True, CurrentTime);
+    }
+
+    if (flags & VD_AGENT_KEYSYM_FLAG_UP) {
+        //syslog(LOG_INFO, "vdagent_x11_send_keysym up %08x", keysym);
+        XTestFakeKeyEvent(x11->display, scratch_keycode, False, CurrentTime);
+    }
+
+    if (keymap_changed && (orig_keysym > 0)) {
+        KeySym keysym_list[] = { orig_keysym };
+        XChangeKeyboardMapping(x11->display, scratch_keycode, 1, keysym_list, 1);
+    }
+
+    XFlush(x11->display);
+}
+
 static void vdagent_x11_get_wm_name(struct vdagent_x11 *x11)
 {
     Atom type_ret;
Index: new/src/vdagent-x11.h
===================================================================
--- new.orig/src/vdagent-x11.h	2013-10-14 14:52:01.000000000 +0200
+++ new/src/vdagent-x11.h	2013-10-15 09:06:44.000000000 +0200
@@ -50,4 +50,6 @@
 
 int vdagent_x11_has_icons_on_desktop(struct vdagent_x11 *x11);
 
+void vdagent_x11_send_keysym(struct vdagent_x11 *x11, uint32_t keysmy, uint32_t flags);
+
 #endif
Index: new/Makefile.am
===================================================================
--- new.orig/Makefile.am	2013-10-14 14:52:01.000000000 +0200
+++ new/Makefile.am	2013-10-15 09:12:45.000000000 +0200
@@ -5,7 +5,7 @@
 sbin_PROGRAMS = src/spice-vdagentd
 
 src_spice_vdagent_CFLAGS = $(X_CFLAGS) $(SPICE_CFLAGS) $(GLIB2_CFLAGS)
-src_spice_vdagent_LDADD = $(X_LIBS) $(SPICE_LIBS) $(GLIB2_LIBS)
+src_spice_vdagent_LDADD = -lXtst $(X_LIBS) $(SPICE_LIBS) $(GLIB2_LIBS)
 src_spice_vdagent_SOURCES = src/vdagent.c src/vdagent-x11.c src/vdagent-x11-randr.c src/vdagent-file-xfers.c src/udscs.c
 
 src_spice_vdagentd_CFLAGS = $(DBUS_CFLAGS) $(LIBSYSTEMD_LOGIN_CFLAGS) \

