File keymap.diff of Package virt-viewer

Index: virt-viewer-0.0.3/src/keymap.c
===================================================================
--- /dev/null
+++ virt-viewer-0.0.3/src/keymap.c
@@ -0,0 +1,411 @@
+/*
+ * keymap.c: Reads keymap XML definition file populating keyComboDef
+ *           structures
+ *
+ * Copyright (C) 2008 Novell,
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ *
+ * Author: Pat Campbell <plc@novell.com>
+ */
+
+#include <config.h>
+
+#include <gtk/gtk.h>
+#include <gdk/gdkkeysyms.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <unistd.h>
+#include <libxml/xpath.h>
+
+#include "viewer.h"
+
+#define ITEM_PATH "//keymap/item"
+#define ROOT   "keymap"
+#define ITEM   "item"
+#define TYPE   "type"
+#define MENU   "menu"
+#define SEP    "separator"
+#define LABEL  "label"
+#define KEYS   "keys"
+#define KEY    "key"
+#define DEFAULT_KEY_XML "/usr/share/virt-viewer/keymap.xml"
+
+static int verbose = 0;
+
+static int create_keyCombos(SendKeyItems * sendKeyItems, xmlXPathContextPtr ctxt)
+{
+	xmlXPathObjectPtr obj;
+	xmlNodePtr relnode;
+	SendKeyItems old_sendKeyItems;
+
+	if (ctxt == NULL) {
+		fprintf(stderr,"Invalid parser context to %s", __func__);
+		goto error;
+	}
+	relnode = ctxt->node;
+
+	/*
+	 * Determine number of items
+	 */
+	obj = xmlXPathEval((xmlChar *)ITEM_PATH, ctxt);
+	if ((obj == NULL) || (obj->type != XPATH_NODESET)) {
+		xmlXPathFreeObject(obj);
+		fprintf(stderr, "Error obj NULL or type not a NODESET");
+		goto error;
+	}
+	old_sendKeyItems.keyCombos = sendKeyItems->keyCombos;
+	old_sendKeyItems.cnt = sendKeyItems->cnt;
+
+	sendKeyItems->cnt = xmlXPathNodeSetGetLength(obj->nodesetval);
+	if (sendKeyItems->cnt) {
+		sendKeyItems->keyCombos = calloc(sendKeyItems->cnt, sizeof(struct keyComboDef *));
+		if (!sendKeyItems->keyCombos) {
+			sendKeyItems->keyCombos = old_sendKeyItems.keyCombos;
+			sendKeyItems->cnt = old_sendKeyItems.cnt;
+		}
+		else if (old_sendKeyItems.keyCombos) {
+			int i;
+			for (i = 0; i < old_sendKeyItems.cnt; i++) {
+				free(old_sendKeyItems.keyCombos[i]->keys);
+				free(old_sendKeyItems.keyCombos[i]->label);
+			}
+			free(old_sendKeyItems.keyCombos);
+		}
+	}
+	ctxt->node = relnode;
+	xmlXPathFreeObject(obj);
+error:
+	return(sendKeyItems->cnt);
+}
+
+static void build_keyComboDef(SendKeyItems *sendKeyItems, guint *keys, int  cnt, const char * label)
+{
+	int i;
+	struct keyComboDef *kp;
+
+	kp = (struct keyComboDef*)calloc(sizeof(guint *) + sizeof(guint) + sizeof(char *), 1);
+	if (!kp)
+		return;
+
+	if (keys) {
+		kp->keys = (guint *)malloc((sizeof(guint) * cnt) );
+		if (!kp->keys) {
+			free(kp);
+			return;
+		}
+		for(i=0; i<cnt; i++) {
+			kp->keys[i] = keys[i];
+		}
+	}
+	if (label) {
+		kp->label = strdup(label);
+	}
+	else {
+		kp->label = strdup("Unknown");
+	}
+	kp->nkeys = cnt;
+
+	for (i = 0; i < sendKeyItems->cnt; i++) {
+		if ( sendKeyItems->keyCombos[i] == NULL) {
+			sendKeyItems->keyCombos[i] = kp;
+			break;
+		}
+	}
+}
+
+static char *trim(char *str)
+{
+	char * p = str;
+
+	p += strlen(str);
+
+	/* trim trailing */
+	while(p != str) {
+		if (*p == '\n' || *p == '\t' || *p == ' ' || *p == '\0') {
+			*p = '\0';
+			p--;
+			continue;
+		}
+		break;
+	}
+	/* trim leading */
+	p = str;
+	while(p != '\0') {
+		if (*p == '\n' || *p == '\t' || *p == ' ') {
+			p++;
+			continue;
+		}
+		break;
+	}
+	if (p != str) {
+		memcpy(str, p, strlen(p) + 1);
+	}
+	return(str);
+}
+
+static void process_keys(SendKeyItems *sendKeyItems, xmlDocPtr xml, xmlXPathContextPtr ctxt,
+		xmlNode * node, char *label)
+{
+	xmlNode *n;
+	int cnt = 0;
+	int i = 0;
+	guint * keys;
+	xmlXPathObjectPtr obj;
+	char *str;
+
+	if (!node || !node->children) return;
+
+	/*
+	 * get number of keys
+	 */
+	for(n=node->children; n; n=n->next) {
+		if (n->type != XML_ELEMENT_NODE) continue;
+		cnt++;
+	}
+
+	if (cnt) {
+		keys = malloc(sizeof(guint) * cnt);
+		if (keys) {
+			for(i = 0, n=node->children; n; n=n->next) {
+				if (n->type != XML_ELEMENT_NODE) continue;
+				obj = xmlXPathEval(xmlGetNodePath(n), ctxt);
+				str = (char *)xmlNodeListGetString(xml, obj->nodesetval->nodeTab[0]->xmlChildrenNode, 1);
+				trim(str);
+				keys[i] = gdk_keyval_from_name(str);
+				if (keys[i] == 0) {
+					if (verbose) {
+						fprintf(stderr, "virt-viewer: Could not get keyval for %s:%s, ignoring\n",
+							str, label);
+					}
+					free(keys);
+					sendKeyItems->cnt--;
+					return;
+				}
+				i++;
+			}
+			build_keyComboDef(sendKeyItems, keys, cnt, label);
+		}
+	}
+}
+
+static void process_item(SendKeyItems * sendKeyItems, xmlDocPtr xml,
+		xmlXPathContextPtr ctxt, xmlNode * node)
+{
+	xmlNode *n;
+	xmlXPathObjectPtr obj;
+	xmlChar *type;
+	char *label = NULL;
+
+	if (!node) return;
+
+	type = xmlGetProp(node, (xmlChar*)"type");
+	if (!type) return;
+
+	if (xmlStrncmp((const xmlChar*)MENU, type, strlen(MENU)) == 0) {
+		for(n=node->children; n; n=n->next) {
+			if (n->type != XML_ELEMENT_NODE) continue;
+			if (xmlStrncmp((const xmlChar*)LABEL, n->name, strlen(LABEL)) == 0) {
+				obj = xmlXPathEval(xmlGetNodePath(n), ctxt);
+				if (!obj) return;
+				label = (char *)xmlNodeListGetString(xml, obj->nodesetval->nodeTab[0]->xmlChildrenNode, 1);
+			}
+			else if (xmlStrncmp((const xmlChar*)KEYS, n->name, strlen(KEYS)) == 0) {
+				process_keys(sendKeyItems, xml, ctxt, n, label);
+			}
+		}
+	}
+	else if (xmlStrncmp((const xmlChar*)SEP, type, strlen(SEP)) == 0) {
+		build_keyComboDef(sendKeyItems, NULL, 0, "");
+	}
+}
+
+#define MAX_KEYS 3
+struct  defaultKeyComboDef {
+	guint keys[MAX_KEYS];
+	guint nkeys;
+	const char *label;
+};
+static const struct defaultKeyComboDef defaultKeyCombos[] = {
+	{ { GDK_Control_L, GDK_Alt_L, GDK_Delete }, 3, "Ctrl+Alt+_Del"},
+	{ { GDK_Control_L, GDK_Alt_L, GDK_BackSpace }, 3, "Ctrl+Alt+_Backspace"},
+	{ {}, 0, "" },
+	{ { GDK_Control_L, GDK_Alt_L, GDK_F1 }, 3, "Ctrl+Alt+F_1"},
+	{ { GDK_Control_L, GDK_Alt_L, GDK_F2 }, 3, "Ctrl+Alt+F_2"},
+	{ { GDK_Control_L, GDK_Alt_L, GDK_F3 }, 3, "Ctrl+Alt+F_3"},
+	{ { GDK_Control_L, GDK_Alt_L, GDK_F4 }, 3, "Ctrl+Alt+F_4"},
+	{ { GDK_Control_L, GDK_Alt_L, GDK_F5 }, 3, "Ctrl+Alt+F_5"},
+	{ { GDK_Control_L, GDK_Alt_L, GDK_F6 }, 3, "Ctrl+Alt+F_6"},
+	{ { GDK_Control_L, GDK_Alt_L, GDK_F7 }, 3, "Ctrl+Alt+F_7"},
+	{ { GDK_Control_L, GDK_Alt_L, GDK_F8 }, 3, "Ctrl+Alt+F_8"},
+	{ {}, 0, "" },
+	{ { GDK_Print }, 1, "_PrintScreen"},
+};
+
+static void makeDefaultKeyCombos(SendKeyItems * sendKeyItems)
+{
+	int i;
+	guint *keys;
+
+	sendKeyItems->cnt = (sizeof(defaultKeyCombos)/sizeof(defaultKeyCombos[0]));
+	sendKeyItems->keyCombos = calloc(sendKeyItems->cnt, sizeof(struct keyComboDef *));
+	if (!sendKeyItems->keyCombos) {
+		sendKeyItems->cnt = 0;
+		return;
+	}
+
+	for (i = 0 ; i < (sizeof(defaultKeyCombos)/sizeof(defaultKeyCombos[0])) ; i++) {
+		keys = (guint *)defaultKeyCombos[i].keys;
+		build_keyComboDef(sendKeyItems, keys, defaultKeyCombos[i].nkeys, defaultKeyCombos[i].label);
+	}
+}
+
+
+int build_keyCombos(SendKeyItems * sendKeyItems, const char *filename, int set_verbose)
+{
+	xmlDocPtr xml = NULL;
+	xmlParserCtxtPtr pctxt = NULL;
+	xmlXPathContextPtr ctxt = NULL;
+	xmlNode *root_element = NULL;
+	xmlNode *node;
+	struct stat sbuf;
+
+	verbose = set_verbose;
+	pctxt = xmlNewParserCtxt();
+	if (!pctxt || !pctxt->sax)
+		goto error;
+
+	if (filename == NULL)
+		filename = DEFAULT_KEY_XML;
+
+	if(stat(filename, &sbuf) != 0) {
+		if (verbose)
+			fprintf(stderr, "%s(): could not stat file:%s \n", __FUNCTION__, filename);
+		goto error;
+	}
+
+	xml = xmlCtxtReadFile(pctxt, filename, NULL,
+			     XML_PARSE_NOENT | XML_PARSE_NONET |
+			     XML_PARSE_NOWARNING);
+	if (!xml) {
+		if (verbose)
+			fprintf(stderr, "%s(): could not read file:%s \n", __FUNCTION__, filename);
+		goto error;
+	}
+
+	ctxt = xmlXPathNewContext(xml);
+	if (!ctxt)
+		goto error;
+
+	root_element = xmlDocGetRootElement(xml);
+	if (!root_element) {
+		if (verbose)
+			fprintf(stderr, "%s(): could not locate root element\n", __FUNCTION__);
+		goto error;
+	}
+
+	if (!create_keyCombos(sendKeyItems, ctxt)) {
+		if (verbose)
+			fprintf(stderr, "%s(): could not create XML based keyCombos structure \n", __FUNCTION__);
+		goto error;
+	}
+
+	for (node=root_element; node; node=node->next) {
+		if (node->type != XML_ELEMENT_NODE) continue;
+		if (node->children)
+		{
+			xmlNode *n;
+			for(n=node->children; n; n=n->next) {
+				if (n->type != XML_ELEMENT_NODE) continue;
+				if (xmlStrncmp((const xmlChar*)ITEM, n->name, strlen(ITEM)) == 0) {
+					process_item(sendKeyItems, xml, ctxt, n);
+				}
+			}
+		}
+	}
+
+ error:
+	if (ctxt)
+		xmlXPathFreeContext(ctxt);
+	if (xml)
+		xmlFreeDoc(xml);
+	if (pctxt)
+		xmlFreeParserCtxt(pctxt);
+	if (sendKeyItems->keyCombos == NULL)
+		makeDefaultKeyCombos(sendKeyItems);
+	return(sendKeyItems->cnt);
+}
+
+int sendkey_xml_validate(const char * filename)
+{
+	xmlDocPtr doc = NULL;
+	xmlParserCtxtPtr pctxt = NULL;
+	xmlXPathContextPtr ctxt = NULL;
+	xmlNode *root_element = NULL;
+	int ret = -1;
+
+	pctxt = xmlNewParserCtxt();
+	if (!pctxt || !pctxt->sax) {
+		if (verbose)
+			fprintf(stderr, "%s(): failed to allocate parser context \n", __FUNCTION__);
+		goto error;
+	}
+
+	doc = xmlCtxtReadFile(pctxt, filename, NULL, XML_PARSE_DTDVALID);
+	if (!doc) {
+		if (verbose)
+			fprintf(stderr, "%s(): could not read file:%s \n", __FUNCTION__, filename);
+		goto error;
+	}
+	if (pctxt->valid == 0) {
+		if (verbose)
+			fprintf(stderr, "%s(): Failed to validate :%s \n", __FUNCTION__, filename);
+		goto error;
+	}
+
+	ctxt = xmlXPathNewContext(doc);
+	if (!ctxt) {
+		if (verbose)
+			fprintf(stderr, "%s(): Failed to allocate path context\n", __FUNCTION__);
+		goto error;
+	}
+
+	root_element = xmlDocGetRootElement(doc);
+	if (!root_element) {
+		if (verbose)
+			fprintf(stderr, "%s(): could not locate root element\n", __FUNCTION__);
+		goto error;
+	}
+
+	if (xmlStrncmp((const xmlChar*)ROOT, root_element->name, strlen(ROOT)) != 0) {
+		if (verbose)
+			fprintf(stderr, "%s(): Incorrect root element name:%s\n", __FUNCTION__,
+					root_element->name);
+		goto error;
+	}
+	ret = 0;
+
+error:
+	if (ctxt)
+		xmlXPathFreeContext(ctxt);
+	if (doc)
+		xmlFreeDoc(doc);
+	if (pctxt)
+		xmlFreeParserCtxt(pctxt);
+	return(ret);
+
+}
Index: virt-viewer-0.0.3/src/keymap.xml
===================================================================
--- /dev/null
+++ virt-viewer-0.0.3/src/keymap.xml
@@ -0,0 +1,95 @@
+<?xml version="1.0"?>
+
+<!DOCTYPE keymap [
+<!ELEMENT keymap (item)*>
+<!ELEMENT item (label,keys)*>
+<!ELEMENT label (#PCDATA)>
+<!ELEMENT keys (key)+>
+<!ELEMENT key (#PCDATA)>
+<!ATTLIST keymap id CDATA #REQUIRED>
+<!ATTLIST item type (menu|separator) #REQUIRED>
+]>
+
+<keymap id="virt-viewer, Send Key menu keymap">
+    <!-- See /usr/include/gtk2.0/gdk/gdkkeysyms.h for valid key names -->
+	<item type="menu">
+		<label>Ctrl+Alt+_Del</label>
+        <keys>
+            <key>Control_L</key>
+            <key>Alt_L</key>
+            <key>Delete</key>
+        </keys>
+    </item>
+	<item type="menu">
+		<label>Ctrl+Alt+_BackSpace</label>
+        <keys>
+            <key>Control_L</key>
+            <key>Alt_L</key>
+            <key>BackSpace</key>
+        </keys>
+    </item>
+	<item type="separator"/>
+	<item type="menu">
+		<label>Ctrl+Alt+F1</label>
+        <keys>
+            <key>Control_L</key>
+            <key>Alt_L</key>
+            <key>F1</key>
+        </keys>
+    </item>
+	<item type="menu">
+		<label>Ctrl+Alt+F2</label>
+        <keys>
+            <key>Control_L</key>
+            <key>Alt_L</key>
+            <key>F2</key>
+        </keys>
+    </item>
+    <item type="menu">
+		<label>Ctrl+Alt+F3</label>
+        <keys>
+            <key>Control_L</key>
+            <key>Alt_L</key>
+            <key>F3</key>
+        </keys>
+    </item>
+    <item type="menu">
+		<label>Ctrl+Alt+F4</label>
+        <keys>
+            <key>Control_L</key>
+            <key>Alt_L</key>
+            <key>F4</key>
+        </keys>
+    </item>
+    <item type="menu">
+		<label>Ctrl+Alt+F5</label>
+        <keys>
+            <key>Control_L</key>
+            <key>Alt_L</key>
+            <key>F5</key>
+        </keys>
+    </item>
+    <item type="menu">
+		<label>Ctrl+Alt+F6</label>
+        <keys>
+            <key>Control_L</key>
+            <key>Alt_L</key>
+            <key>F6</key>
+        </keys>
+    </item>
+    <item type="menu">
+		<label>Ctrl+Alt+F7</label>
+        <keys>
+            <key>Control_L</key>
+            <key>Alt_L</key>
+            <key>F7</key>
+        </keys>
+    </item>
+	<item type="separator"/>
+	<item type="menu">
+		<label>_PrintScreen</label>
+        <keys>
+            <key>Print</key>
+        </keys>
+    </item>
+</keymap>
Index: virt-viewer-0.0.3/src/main.c
===================================================================
--- virt-viewer-0.0.3.orig/src/main.c
+++ virt-viewer-0.0.3/src/main.c
@@ -46,28 +46,9 @@
 
 static char *domname = NULL;
 static int verbose = 0;
-#define MAX_KEY_COMBO 3
-struct  keyComboDef {
-	guint keys[MAX_KEY_COMBO];
-	guint nkeys;
-	const char *label;
-};
 
-static const struct keyComboDef keyCombos[] = {
-	{ { GDK_Control_L, GDK_Alt_L, GDK_Delete }, 3, "Ctrl+Alt+_Del"},
-	{ { GDK_Control_L, GDK_Alt_L, GDK_BackSpace }, 3, "Ctrl+Alt+_Backspace"},
-	{ {}, 0, "" },
-	{ { GDK_Control_L, GDK_Alt_L, GDK_F1 }, 3, "Ctrl+Alt+F_1"},
-	{ { GDK_Control_L, GDK_Alt_L, GDK_F2 }, 3, "Ctrl+Alt+F_2"},
-	{ { GDK_Control_L, GDK_Alt_L, GDK_F3 }, 3, "Ctrl+Alt+F_3"},
-	{ { GDK_Control_L, GDK_Alt_L, GDK_F4 }, 3, "Ctrl+Alt+F_4"},
-	{ { GDK_Control_L, GDK_Alt_L, GDK_F5 }, 3, "Ctrl+Alt+F_5"},
-	{ { GDK_Control_L, GDK_Alt_L, GDK_F6 }, 3, "Ctrl+Alt+F_6"},
-	{ { GDK_Control_L, GDK_Alt_L, GDK_F7 }, 3, "Ctrl+Alt+F_7"},
-	{ { GDK_Control_L, GDK_Alt_L, GDK_F8 }, 3, "Ctrl+Alt+F_8"},
-	{ {}, 0, "" },
-	{ { GDK_Print }, 1, "_PrintScreen"},
-};
+SendKeyItems sendKeyItems;
+static GtkWidget *viewer_build_sendkey_menu(VncDisplay *vnc);
 
 enum menuNums {
 	FILE_MENU,
@@ -79,18 +60,27 @@ enum menuNums {
 
 struct menuItem {
 	guint menu;
+	GtkWidget *widget;
 	GtkWidget *label;
 	const char *ungrabbed_text;
 	const char *grabbed_text;
 };
 
 static struct menuItem menuItems[] = {
-	{ FILE_MENU, NULL, "_File", "File" },
-	{ VIEW_MENU, NULL, "_View", "View" },
-	{ SEND_KEY_MENU, NULL, "_Send Key", "Send Key" },
-	{ HELP_MENU, NULL, "_Help", "Help" }
+	{ FILE_MENU, NULL, NULL, "_File", "File" },
+	{ VIEW_MENU, NULL, NULL, "_View", "View" },
+	{ SEND_KEY_MENU, NULL, NULL, "_Send Key", "Send Key" },
+	{ HELP_MENU, NULL, NULL, "_Help", "Help" }
 };
 
+typedef struct {
+	VncDisplay *vnc;
+	GtkWindow *window;
+	GtkWidget *menubar;
+}VirtViewer;
+
+static VirtViewer virtViewer;
+
 static void viewer_set_title(VncDisplay *vnc G_GNUC_UNUSED, GtkWidget *window, gboolean grabbed)
 {
 	char title[1024];
@@ -182,12 +172,13 @@ static void viewer_send_key(GtkWidget *m
 	GtkWidget *label = gtk_bin_get_child(GTK_BIN(menu));
 	const char *text = gtk_label_get_label(GTK_LABEL(label));
 
-	for (i = 0 ; i < (sizeof(keyCombos)/sizeof(keyCombos[0])) ; i++) {
-		if (!strcmp(text, keyCombos[i].label)) {
+	for (i = 0 ; i < sendKeyItems.cnt; i++) {
+		struct keyComboDef *kp = sendKeyItems.keyCombos[i];
+		if (!strcmp(text, kp->label)) {
 			DEBUG_LOG("Sending key combo %s\n", gtk_label_get_text(GTK_LABEL(label)));
 			vnc_display_send_keys(VNC_DISPLAY(vnc),
-					      keyCombos[i].keys,
-					      keyCombos[i].nkeys);
+					      kp->keys,
+					      kp->nkeys);
 			return;
 		}
 	}
@@ -229,6 +220,47 @@ static void viewer_screenshot(GtkWidget
 	gtk_widget_destroy (dialog);
 }
 
+static void viewer_file_sendkey(GtkWidget *menu G_GNUC_UNUSED, VirtViewer *viewer)
+{
+	GtkWidget *dialog;
+	GtkWidget *sendkey;
+
+	dialog = gtk_file_chooser_dialog_new ("Load SendKey",
+					      NULL,
+					      GTK_FILE_CHOOSER_ACTION_OPEN,
+					      GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
+					      GTK_STOCK_OPEN, GTK_RESPONSE_ACCEPT,
+					      NULL);
+again:
+	if (gtk_dialog_run (GTK_DIALOG (dialog)) == GTK_RESPONSE_ACCEPT) {
+		char *filename;
+
+		filename = gtk_file_chooser_get_filename (GTK_FILE_CHOOSER (dialog));
+		if (sendkey_xml_validate(filename) == -1) {
+			GtkWidget *msg_dialog = gtk_message_dialog_new (GTK_WINDOW(dialog),
+					GTK_DIALOG_DESTROY_WITH_PARENT,
+					GTK_MESSAGE_ERROR,
+					GTK_BUTTONS_CLOSE,
+					"Invalid send key XML format: '%s'",
+					filename);
+			gtk_dialog_run (GTK_DIALOG (msg_dialog));
+			gtk_widget_destroy (msg_dialog);
+			g_free (filename);
+			goto again;
+		}
+		else {
+			build_keyCombos(&sendKeyItems, filename, verbose);
+			gtk_widget_destroy (menuItems[SEND_KEY_MENU].widget);
+			sendkey = viewer_build_sendkey_menu(viewer->vnc);
+			gtk_menu_bar_insert(GTK_MENU_BAR(viewer->menubar), sendkey, SEND_KEY_MENU);
+			gtk_widget_show_all(viewer->menubar);
+		}
+		g_free (filename);
+	}
+
+	gtk_widget_destroy (dialog);
+}
+
 
 static void viewer_credential(GtkWidget *vnc, GValueArray *credList)
 {
@@ -386,6 +418,7 @@ static GtkWidget *menu_item_new(int whic
 	gtk_accel_label_set_accel_widget(GTK_ACCEL_LABEL(label), widget);
 	gtk_widget_show(label);
 
+	menuItems[which_menu].widget = widget;
 	menuItems[which_menu].label = label;
 
 	return widget;
@@ -397,6 +430,7 @@ static GtkWidget *viewer_build_file_menu
 	GtkWidget *filemenu;
 	GtkWidget *quit;
 	GtkWidget *screenshot;
+	GtkWidget *sendkey;
 
 	file = menu_item_new(FILE_MENU);
 
@@ -407,6 +441,10 @@ static GtkWidget *viewer_build_file_menu
 	gtk_menu_append(GTK_MENU(filemenu), screenshot);
 	g_signal_connect(screenshot, "activate", GTK_SIGNAL_FUNC(viewer_screenshot), vnc);
 
+	sendkey = gtk_menu_item_new_with_mnemonic("Send _Key");
+	gtk_menu_append(GTK_MENU(filemenu), sendkey);
+	g_signal_connect(sendkey, "activate", GTK_SIGNAL_FUNC(viewer_file_sendkey), &virtViewer);
+
 	gtk_menu_append(GTK_MENU(filemenu), gtk_separator_menu_item_new());
 
 
@@ -453,11 +491,12 @@ static GtkWidget *viewer_build_sendkey_m
 	sendkeymenu = gtk_menu_new();
 	gtk_menu_item_set_submenu(GTK_MENU_ITEM(sendkey), sendkeymenu);
 
-	for (i = 0 ; i < (sizeof(keyCombos)/sizeof(keyCombos[0])) ; i++) {
+	for (i = 0 ; i < sendKeyItems.cnt; i++) {
 		GtkWidget *key;
+		struct keyComboDef *kp = sendKeyItems.keyCombos[i];
 
-		if (keyCombos[i].nkeys) {
-			key = gtk_menu_item_new_with_mnemonic(keyCombos[i].label);
+		if (kp->nkeys) {
+			key = gtk_menu_item_new_with_mnemonic(kp->label);
 			gtk_menu_append(GTK_MENU(sendkeymenu), key);
 			g_signal_connect(key, "activate", GTK_SIGNAL_FUNC(viewer_send_key), vnc);
 		} else {
@@ -495,6 +534,7 @@ static GtkWidget *viewer_build_menu(VncD
 	GtkWidget *help;
 
 	menubar = gtk_menu_bar_new();
+	virtViewer.menubar = menubar;
 
 	file = viewer_build_file_menu(vnc);
 	view = viewer_build_view_menu(vnc, window, composited);
@@ -527,6 +567,9 @@ static GtkWidget *viewer_build_window(Vn
 	window = get_toplevel (data);
 	gtk_window_set_resizable(GTK_WINDOW(window), TRUE);
 
+	virtViewer.vnc = vnc;
+	virtViewer.window = (GTK_WINDOW(window));
+
 	if (with_menubar) {
 		layout = gtk_vbox_new(FALSE, 3);
 		menubar = viewer_build_menu(vnc, window, gtk_widget_is_composited(window));
@@ -886,6 +929,7 @@ static void viewer_help(FILE *out, const
 	fprintf(out, "  -d, --direct            direct connection with no automatic tunnels\n");
 	fprintf(out, "  -c URI, --connect URI   connect to hypervisor URI\n");
 	fprintf(out, "  -w, --wait              wait for domain to start\n");
+	fprintf(out, "  -k file, --keymap file  menu key definition file\n");
 	fprintf(out, "\n");
 }
 
@@ -894,7 +938,7 @@ int main(int argc, char **argv)
 	char *uri = NULL;
 	char *name = NULL;
 	int opt_ind;
-	const char *sopts = "hVc:";
+	const char *sopts = "hvVdc:wk:";
 	static const struct option lopts[] = {
 		{ "help", 0, 0, 'h' },
 		{ "version", 0, 0, 'V' },
@@ -902,6 +946,7 @@ int main(int argc, char **argv)
 		{ "connect", 1, 0, 'c' },
 		{ "wait", 0, 0, 'w' },
 		{ "direct", 0, 0, 'd' },
+		{ "keymap", 1, 0, 'k' },
 		{ 0, 0, 0, 0 }
 	};
 	int ch;
@@ -909,6 +954,7 @@ int main(int argc, char **argv)
 	int waitvnc = 0;
 	int set_verbose = 0;
 	int ret;
+	char *keymap_file = NULL;
 
 	while ((ch = getopt_long(argc, argv, sopts, lopts, &opt_ind)) != -1) {
 		switch (ch) {
@@ -930,6 +976,9 @@ int main(int argc, char **argv)
 		case 'd':
 			direct = 1;
 			break;
+		case 'k':
+			keymap_file = strdup(optarg);
+			break;
 		case '?':
 			viewer_help(stderr, argv[0]);
 			return 1;
@@ -941,6 +990,8 @@ int main(int argc, char **argv)
 		return 1;
 	}
 
+	build_keyCombos(&sendKeyItems, keymap_file, set_verbose);
+
 	gtk_init(&argc, &argv);
 
 	name = argv[optind];
Index: virt-viewer-0.0.3/src/Makefile.am
===================================================================
--- virt-viewer-0.0.3.orig/src/Makefile.am
+++ virt-viewer-0.0.3/src/Makefile.am
@@ -1,6 +1,13 @@
 
 bin_PROGRAMS = virt-viewer
 
-virt_viewer_SOURCES = main.c viewer.h
+virt_viewer_SOURCES = main.c keymap.c viewer.h
 virt_viewer_LDADD = @GTKVNC_LIBS@ @GTK2_LIBS@ @LIBXML2_LIBS@ @LIBVIRT_LIBS@
 virt_viewer_CFLAGS = @GTKVNC_CFLAGS@ @GTK2_CFLAGS@ @LIBXML2_CFLAGS@ @LIBVIRT_CFLAGS@ @WARN_CFLAGS@
+
+install-data-local:
+	mkdir -p $(DESTDIR)$(datadir)/virt-viewer
+	install keymap.xml $(DESTDIR)$(datadir)/virt-viewer
+
+install-data-am: install-data-local
+
Index: virt-viewer-0.0.3/src/Makefile.in
===================================================================
--- virt-viewer-0.0.3.orig/src/Makefile.in
+++ virt-viewer-0.0.3/src/Makefile.in
@@ -46,7 +46,7 @@ CONFIG_CLEAN_FILES =
 am__installdirs = "$(DESTDIR)$(bindir)"
 binPROGRAMS_INSTALL = $(INSTALL_PROGRAM)
 PROGRAMS = $(bin_PROGRAMS)
-am_virt_viewer_OBJECTS = virt_viewer-main.$(OBJEXT)
+am_virt_viewer_OBJECTS = virt_viewer-main.$(OBJEXT) virt_viewer-keymap.$(OBJEXT)
 virt_viewer_OBJECTS = $(am_virt_viewer_OBJECTS)
 virt_viewer_DEPENDENCIES =
 virt_viewer_LINK = $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) \
@@ -191,7 +191,7 @@ sysconfdir = @sysconfdir@
 target_alias = @target_alias@
 top_builddir = @top_builddir@
 top_srcdir = @top_srcdir@
-virt_viewer_SOURCES = main.c viewer.h
+virt_viewer_SOURCES = main.c keymap.c viewer.h
 virt_viewer_LDADD = @GTKVNC_LIBS@ @GTK2_LIBS@ @LIBXML2_LIBS@ @LIBVIRT_LIBS@
 virt_viewer_CFLAGS = @GTKVNC_CFLAGS@ @GTK2_CFLAGS@ @LIBXML2_CFLAGS@ @LIBVIRT_CFLAGS@ @WARN_CFLAGS@
 all: all-am
@@ -266,6 +266,7 @@ distclean-compile:
 	-rm -f *.tab.c
 
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/virt_viewer-main.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/virt_viewer-keymap.Po@am__quote@
 
 .c.o:
 @am__fastdepCC_TRUE@	$(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $<
@@ -295,6 +296,13 @@ virt_viewer-main.o: main.c
 @AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
 @am__fastdepCC_FALSE@	$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(virt_viewer_CFLAGS) $(CFLAGS) -c -o virt_viewer-main.o `test -f 'main.c' || echo '$(srcdir)/'`main.c
 
+virt_viewer-keymap.o: keymap.c
+@am__fastdepCC_TRUE@	$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(virt_viewer_CFLAGS) $(CFLAGS) -MT virt_viewer-keymap.o -MD -MP -MF $(DEPDIR)/virt_viewer-keymap.Tpo -c -o virt_viewer-keymap.o `test -f 'keymap.c' || echo '$(srcdir)/'`keymap.c
+@am__fastdepCC_TRUE@	mv -f $(DEPDIR)/virt_viewer-keymap.Tpo $(DEPDIR)/virt_viewer-keymap.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	source='keymap.c' object='virt_viewer-keymap.o' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@	$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(virt_viewer_CFLAGS) $(CFLAGS) -c -o virt_viewer-keymap.o `test -f 'keymap.c' || echo '$(srcdir)/'`keymap.c
+
 virt_viewer-main.obj: main.c
 @am__fastdepCC_TRUE@	$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(virt_viewer_CFLAGS) $(CFLAGS) -MT virt_viewer-main.obj -MD -MP -MF $(DEPDIR)/virt_viewer-main.Tpo -c -o virt_viewer-main.obj `if test -f 'main.c'; then $(CYGPATH_W) 'main.c'; else $(CYGPATH_W) '$(srcdir)/main.c'; fi`
 @am__fastdepCC_TRUE@	mv -f $(DEPDIR)/virt_viewer-main.Tpo $(DEPDIR)/virt_viewer-main.Po
@@ -433,7 +441,7 @@ info: info-am
 
 info-am:
 
-install-data-am:
+install-data-am: install-data-local
 
 install-dvi: install-dvi-am
 
@@ -487,6 +495,10 @@ uninstall-am: uninstall-binPROGRAMS
 	pdf pdf-am ps ps-am tags uninstall uninstall-am \
 	uninstall-binPROGRAMS
 
+install-data-local:
+	mkdir -p $(DESTDIR)$(datadir)/virt-viewer
+	install keymap.xml $(DESTDIR)$(datadir)/virt-viewer
+
 # Tell versions [3.59,3.63) of GNU make to not export all variables.
 # Otherwise a system limit (for SysV at least) may be exceeded.
 .NOEXPORT:
Index: virt-viewer-0.0.3/src/viewer.h
===================================================================
--- virt-viewer-0.0.3.orig/src/viewer.h
+++ virt-viewer-0.0.3/src/viewer.h
@@ -25,4 +25,18 @@
 
 extern int viewer_start (const char *uri, const char *name, int direct, int waitvnc, int set_verbose, GtkWidget *(*get_toplevel)(void *), void *data, int with_menubar);
 
+typedef struct  keyComboDef {
+	guint *keys;
+	guint nkeys;
+	char *label;
+}KeyComboDef;
+
+typedef struct sendKeyItems {
+	int cnt;
+	struct keyComboDef **keyCombos;
+}SendKeyItems;
+
+int build_keyCombos(SendKeyItems *, const char *, int);
+int sendkey_xml_validate(const char * filename);
+
 #endif /* VIEWER_H */
openSUSE Build Service is sponsored by