Attached patch modifies virt-viewer so that it acquires the "Send key"
menu contents from an xml file and allows user to load a browsed for
keymap def file while running.
Patch is based on hg tip, changeset: 44:92da1556bbf9
Signed-off-by: Pat Campbell <[email protected]>
diff -r 92da1556bbf9 src/Makefile.am
--- a/src/Makefile.am Fri Nov 28 07:24:56 2008 -0500
+++ b/src/Makefile.am Tue Dec 16 13:34:01 2008 -0700
@@ -4,9 +4,12 @@
gladedir = $(pkgdatadir)/ui
glade_DATA = viewer.glade about.glade auth.glade
-EXTRA_DIST = $(glade_DATA)
+keymapdir = $(pkgdatadir)/keymaps
+keymap_DATA = default.xml
-virt_viewer_SOURCES = main.c viewer.h
+EXTRA_DIST = $(glade_DATA) $(keymap_DATA)
+
+virt_viewer_SOURCES = main.c sendkey.c viewer.h
virt_viewer_LDADD = \
@GTKVNC_LIBS@ \
@GTK2_LIBS@ \
@@ -23,4 +26,5 @@
@LIBVIRT_CFLAGS@ \
@LIBVIRT_GLIB_CFLAGS@ \
@WARN_CFLAGS@ \
- -DGLADE_DIR="\"$(gladedir)\""
+ -DGLADE_DIR="\"$(gladedir)\"" \
+ -DKEYMAPS_DIR="\"$(keymapdir)\""
diff -r 92da1556bbf9 src/default.xml
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/src/default.xml Tue Dec 16 13:34:01 2008 -0700
@@ -0,0 +1,135 @@
+<?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+F_1</label>
+ <keys>
+ <key>Control_L</key>
+ <key>Alt_L</key>
+ <key>F1</key>
+ </keys>
+ </item>
+ <item type="menu">
+ <label>Ctrl+Alt+F_2</label>
+ <keys>
+ <key>Control_L</key>
+ <key>Alt_L</key>
+ <key>F2</key>
+ </keys>
+ </item>
+ <item type="menu">
+ <label>Ctrl+Alt+F_3</label>
+ <keys>
+ <key>Control_L</key>
+ <key>Alt_L</key>
+ <key>F3</key>
+ </keys>
+ </item>
+ <item type="menu">
+ <label>Ctrl+Alt+F_4</label>
+ <keys>
+ <key>Control_L</key>
+ <key>Alt_L</key>
+ <key>F4</key>
+ </keys>
+ </item>
+ <item type="menu">
+ <label>Ctrl+Alt+F_5</label>
+ <keys>
+ <key>Control_L</key>
+ <key>Alt_L</key>
+ <key>F5</key>
+ </keys>
+ </item>
+ <item type="menu">
+ <label>Ctrl+Alt+F_6</label>
+ <keys>
+ <key>Control_L</key>
+ <key>Alt_L</key>
+ <key>F6</key>
+ </keys>
+ </item>
+ <item type="menu">
+ <label>Ctrl+Alt+F_7</label>
+ <keys>
+ <key>Control_L</key>
+ <key>Alt_L</key>
+ <key>F7</key>
+ </keys>
+ </item>
+ <item type="menu">
+ <label>Ctrl+Alt+F_8</label>
+ <keys>
+ <key>Control_L</key>
+ <key>Alt_L</key>
+ <key>F8</key>
+ </keys>
+ </item>
+ <item type="menu">
+ <label>Ctrl+Alt+F_9</label>
+ <keys>
+ <key>Control_L</key>
+ <key>Alt_L</key>
+ <key>F9</key>
+ </keys>
+ </item>
+ <item type="menu">
+ <label>Ctrl+Alt+F1_0</label>
+ <keys>
+ <key>Control_L</key>
+ <key>Alt_L</key>
+ <key>F10</key>
+ </keys>
+ </item>
+ <item type="menu">
+ <label>Ctrl+Alt+F11</label>
+ <keys>
+ <key>Control_L</key>
+ <key>Alt_L</key>
+ <key>F11</key>
+ </keys>
+ </item>
+ <item type="menu">
+ <label>Ctrl+Alt+F12</label>
+ <keys>
+ <key>Control_L</key>
+ <key>Alt_L</key>
+ <key>F12</key>
+ </keys>
+ </item>
+ <item type="separator"/>
+ <item type="menu">
+ <label>_PrintScreen</label>
+ <keys>
+ <key>Print</key>
+ </keys>
+ </item>
+</keymap>
diff -r 92da1556bbf9 src/main.c
--- a/src/main.c Fri Nov 28 07:24:56 2008 -0500
+++ b/src/main.c Tue Dec 16 13:34:01 2008 -0700
@@ -71,35 +71,6 @@
};
-#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"},
- { { GDK_Control_L, GDK_Alt_L, GDK_F5 }, 3, "Ctrl+Alt+F_9"},
- { { GDK_Control_L, GDK_Alt_L, GDK_F6 }, 3, "Ctrl+Alt+F1_0"},
- { { GDK_Control_L, GDK_Alt_L, GDK_F7 }, 3, "Ctrl+Alt+F11"},
- { { GDK_Control_L, GDK_Alt_L, GDK_F8 }, 3, "Ctrl+Alt+F12"},
- { {}, 0, "" },
- { { GDK_Print }, 1, "_PrintScreen"},
-};
-
-
-
typedef struct VirtViewer {
char *uri;
virConnectPtr conn;
@@ -109,6 +80,9 @@
GladeXML *glade;
GtkWidget *window;
GtkWidget *vnc;
+ GtkWidget *menubar;
+ GtkWidget *menu_send;
+ char *menu_send_text;
int active;
gboolean accelEnabled;
@@ -120,6 +94,8 @@
int reconnect;
int direct;
int verbose;
+
+ SendKeyItems sendKeyItems;
} VirtViewer;
typedef struct VirtViewerSize {
@@ -405,12 +381,13 @@
GtkWidget *label = gtk_bin_get_child(GTK_BIN(menu));
const char *text = gtk_label_get_label(GTK_LABEL(label));
DEBUG_LOG("Woo\n");
- for (i = 0 ; i < (sizeof(keyCombos)/sizeof(keyCombos[0])) ; i++) {
- if (!strcmp(text, keyCombos[i].label)) {
+ for (i = 0 ; i < viewer->sendKeyItems.cnt; i++) {
+ struct keyComboDef *kp = viewer->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(viewer->vnc),
- keyCombos[i].keys,
- keyCombos[i].nkeys);
+ kp->keys,
+ kp->nkeys);
return;
}
}
@@ -446,6 +423,83 @@
filename = gtk_file_chooser_get_filename (GTK_FILE_CHOOSER (dialog));
viewer_save_screenshot(viewer->vnc, filename);
+ g_free (filename);
+ }
+
+ gtk_widget_destroy (dialog);
+}
+
+static void viewer_menu_build_sendkey(VirtViewer *viewer, const char *filename)
+{
+ GtkWidget *sendkey;
+ GtkWidget *menu;
+ GtkWidget *label;
+ int i;
+
+ build_keyCombos(&viewer->sendKeyItems, filename, viewer->verbose);
+ gtk_widget_destroy (viewer->menu_send);
+
+ sendkey = gtk_menu_item_new();
+ label = g_object_new(GTK_TYPE_ACCEL_LABEL, NULL);
+ gtk_label_set_text_with_mnemonic(GTK_LABEL(label), viewer->menu_send_text);
+ gtk_misc_set_alignment(GTK_MISC(label), 0.0, 0.5);
+
+ gtk_container_add(GTK_CONTAINER(sendkey), label);
+ gtk_accel_label_set_accel_widget(GTK_ACCEL_LABEL(label), sendkey);
+ gtk_widget_show(label);
+
+ menu = gtk_menu_new();
+ gtk_menu_item_set_submenu(GTK_MENU_ITEM(sendkey), menu);
+
+ for (i = 0; i < viewer->sendKeyItems.cnt; i++) {
+ GtkWidget *key;
+ struct keyComboDef *kp = viewer->sendKeyItems.keyCombos[i];
+
+ if (kp->nkeys) {
+ key = gtk_menu_item_new_with_mnemonic(kp->label);
+ gtk_menu_append(GTK_MENU(menu), key);
+ g_signal_connect(key, "activate", GTK_SIGNAL_FUNC(viewer_menu_send), viewer);
+ } else {
+ gtk_menu_append(GTK_MENU(menu), gtk_separator_menu_item_new());
+ }
+ }
+
+ viewer->menu_send = sendkey;
+ gtk_menu_bar_insert(GTK_MENU_BAR(viewer->menubar), viewer->menu_send, SEND_KEY_MENU);
+ gtk_widget_show_all(viewer->menu_send);
+}
+
+
+static void viewer_menu_file_sendkey(GtkWidget *menu G_GNUC_UNUSED, VirtViewer *viewer G_GNUC_UNUSED)
+{
+ GtkWidget *dialog;
+
+ 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 {
+ viewer_menu_build_sendkey(viewer, filename);
+ }
g_free (filename);
}
@@ -979,12 +1033,14 @@
gboolean waitvm,
gboolean reconnect,
gboolean verbose,
+ const char *keymapfile,
GtkWidget *container)
{
VirtViewer *viewer;
GtkWidget *notebook;
GtkWidget *scroll;
GtkWidget *align;
+ GtkWidget *widget;
viewer = g_new0(VirtViewer, 1);
@@ -1015,6 +1071,8 @@
G_CALLBACK(viewer_menu_file_quit), viewer);
glade_xml_signal_connect_data(viewer->glade, "viewer_menu_file_screenshot",
G_CALLBACK(viewer_menu_file_screenshot), viewer);
+ glade_xml_signal_connect_data(viewer->glade, "viewer_menu_file_sendkey",
+ G_CALLBACK(viewer_menu_file_sendkey), viewer);
glade_xml_signal_connect_data(viewer->glade, "viewer_menu_view_fullscreen",
G_CALLBACK(viewer_menu_view_fullscreen), viewer);
glade_xml_signal_connect_data(viewer->glade, "viewer_menu_view_scale",
@@ -1030,6 +1088,12 @@
vnc_display_set_pointer_grab(VNC_DISPLAY(viewer->vnc), TRUE);
vnc_display_set_force_size(VNC_DISPLAY(viewer->vnc), FALSE);
//vnc_display_set_scaling(VNC_DISPLAY(viewer->vnc), TRUE);
+
+ viewer->menubar = glade_xml_get_widget(viewer->glade,"top-menu");
+ viewer->menu_send = glade_xml_get_widget(viewer->glade,"menu-send");
+ widget = gtk_bin_get_child(GTK_BIN(viewer->menu_send));
+ viewer->menu_send_text = strdup(gtk_label_get_label(GTK_LABEL(widget)));
+ viewer_menu_build_sendkey(viewer, keymapfile);
g_signal_connect(GTK_OBJECT(viewer->vnc), "vnc-connected",
GTK_SIGNAL_FUNC(viewer_connected), viewer);
@@ -1110,6 +1174,7 @@
GError *error = NULL;
int ret;
char *uri = NULL;
+ char *keymapfile = NULL;
gchar **args = NULL;
gboolean print_version = FALSE;
gboolean verbose = FALSE;
@@ -1132,6 +1197,8 @@
"reconnect to domain upon restart", NULL },
{ G_OPTION_REMAINING, '\0', 0, G_OPTION_ARG_STRING_ARRAY, &args,
NULL, "DOMAIN-NAME|ID|UUID" },
+ { "keymap", 'k', 0, G_OPTION_ARG_STRING, &keymapfile,
+ "keymap descriptor file", "filename"},
{ NULL, 0, 0, G_OPTION_ARG_NONE, NULL, NULL, NULL }
};
@@ -1158,7 +1225,7 @@
return 1;
}
- ret = viewer_start (uri, args[0], direct, waitvm, reconnect, verbose, NULL);
+ ret = viewer_start (uri, args[0], direct, waitvm, reconnect, verbose, keymapfile, NULL);
if (ret != 0)
return ret;
diff -r 92da1556bbf9 src/sendkey.c
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/src/sendkey.c Tue Dec 16 13:34:01 2008 -0700
@@ -0,0 +1,417 @@
+/*
+ * sendkey.c: Reads keymap XML definition file populating keyComboDef
+ * structures for send key menu
+ *
+ * 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 <[email protected]>
+ */
+
+#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"
+
+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, "");
+ }
+}
+
+/*
+ * Default keymap when xml definition file can't be found
+ */
+#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"},
+ { { GDK_Control_L, GDK_Alt_L, GDK_F9 }, 3, "Ctrl+Alt+F_9"},
+ { { GDK_Control_L, GDK_Alt_L, GDK_F10 }, 3, "Ctrl+Alt+F1_0"},
+ { { GDK_Control_L, GDK_Alt_L, GDK_F11 }, 3, "Ctrl+Alt+F11"},
+ { { GDK_Control_L, GDK_Alt_L, GDK_F12 }, 3, "Ctrl+Alt+F12"},
+ { {}, 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 = g_strdup_printf("%s/default.xml", KEYMAPS_DIR);
+
+ 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);
+
+}
diff -r 92da1556bbf9 src/viewer.glade
--- a/src/viewer.glade Fri Nov 28 07:24:56 2008 -0500
+++ b/src/viewer.glade Tue Dec 16 13:34:01 2008 -0700
@@ -25,6 +25,14 @@
<property name="label" translatable="yes">Screenshot</property>
<property name="use_underline">True</property>
<signal name="activate" handler="viewer_menu_file_screenshot"/>
+ </widget>
+ </child>
+ <child>
+ <widget class="GtkMenuItem" id="menu-file-sendkey">
+ <property name="visible">True</property>
+ <property name="label" translatable="yes">Send key</property>
+ <property name="use_underline">True</property>
+ <signal name="activate" handler="viewer_menu_file_sendkey"/>
</widget>
</child>
<child>
@@ -78,141 +86,6 @@
<property name="visible">True</property>
<property name="label" translatable="yes">_Send key</property>
<property name="use_underline">True</property>
- <child>
- <widget class="GtkMenu" id="menu5">
- <property name="visible">True</property>
- <child>
- <widget class="GtkMenuItem" id="menu-send-cad">
- <property name="visible">True</property>
- <property name="label" translatable="yes">Ctrl+Alt+_Del</property>
- <property name="use_underline">True</property>
- <signal name="activate" handler="viewer_menu_send" object="cad"/>
- </widget>
- </child>
- <child>
- <widget class="GtkMenuItem" id="menu-send-cab">
- <property name="visible">True</property>
- <property name="label" translatable="yes">Ctrl+Alt+_Backspace</property>
- <property name="use_underline">True</property>
- <signal name="activate" handler="viewer_menu_send" object="cab"/>
- </widget>
- </child>
- <child>
- <widget class="GtkSeparatorMenuItem" id="separatormenuitem2">
- <property name="visible">True</property>
- </widget>
- </child>
- <child>
- <widget class="GtkMenuItem" id="menu-send-caf1">
- <property name="visible">True</property>
- <property name="label" translatable="yes">Ctrl+Alt+F_1</property>
- <property name="use_underline">True</property>
- <signal name="activate" handler="viewer_menu_send" object="caf1"/>
- </widget>
- </child>
- <child>
- <widget class="GtkMenuItem" id="menu-send-caf2">
- <property name="visible">True</property>
- <property name="label" translatable="yes">Ctrl+Alt+F_2</property>
- <property name="use_underline">True</property>
- <signal name="activate" handler="viewer_menu_send" object="caf2"/>
- </widget>
- </child>
- <child>
- <widget class="GtkMenuItem" id="menu-send-caf3">
- <property name="visible">True</property>
- <property name="label" translatable="yes">Ctrl+Alt+F_3</property>
- <property name="use_underline">True</property>
- <signal name="activate" handler="viewer_menu_send" object="caf3"/>
- </widget>
- </child>
- <child>
- <widget class="GtkMenuItem" id="menu-send-caf4">
- <property name="visible">True</property>
- <property name="label" translatable="yes">Ctrl+Alt+F_4</property>
- <property name="use_underline">True</property>
- <signal name="activate" handler="viewer_menu_send" object="caf4"/>
- </widget>
- </child>
- <child>
- <widget class="GtkMenuItem" id="menu-send-caf5">
- <property name="visible">True</property>
- <property name="label" translatable="yes">Ctrl+Alt+F_5</property>
- <property name="use_underline">True</property>
- <signal name="activate" handler="viewer_menu_send" object="caf5"/>
- </widget>
- </child>
- <child>
- <widget class="GtkMenuItem" id="menu-send-caf6">
- <property name="visible">True</property>
- <property name="label" translatable="yes">Ctrl+Alt+F_6</property>
- <property name="use_underline">True</property>
- <signal name="activate" handler="viewer_menu_send"/>
- </widget>
- </child>
- <child>
- <widget class="GtkMenuItem" id="menu-send-caf7">
- <property name="visible">True</property>
- <property name="label" translatable="yes">Ctrl+Alt+F_7</property>
- <property name="use_underline">True</property>
- <signal name="activate" handler="viewer_menu_send" object="caf7"/>
- </widget>
- </child>
- <child>
- <widget class="GtkMenuItem" id="menu-send-caf8">
- <property name="visible">True</property>
- <property name="label" translatable="yes">Ctrl+Alt+F_8</property>
- <property name="use_underline">True</property>
- <signal name="activate" handler="viewer_menu_send" object="caf8"/>
- </widget>
- </child>
- <child>
- <widget class="GtkMenuItem" id="menu-send-caf9">
- <property name="visible">True</property>
- <property name="label" translatable="yes">Ctrl+Alt+F_9</property>
- <property name="use_underline">True</property>
- <signal name="activate" handler="viewer_menu_send" object="caf9"/>
- </widget>
- </child>
- <child>
- <widget class="GtkMenuItem" id="menu-send-caf10">
- <property name="visible">True</property>
- <property name="label" translatable="yes">Ctrl+Alt+F1_0</property>
- <property name="use_underline">True</property>
- <signal name="activate" handler="viewer_menu_send" object="caf10"/>
- </widget>
- </child>
- <child>
- <widget class="GtkMenuItem" id="menu-send-caf11">
- <property name="visible">True</property>
- <property name="label" translatable="yes">Ctrl+Alt+F11</property>
- <property name="use_underline">True</property>
- <signal name="activate" handler="viewer_menu_send" object="caf11"/>
- </widget>
- </child>
- <child>
- <widget class="GtkMenuItem" id="menu-send-caf12">
- <property name="visible">True</property>
- <property name="label" translatable="yes">Ctrl+Alt+F12</property>
- <property name="use_underline">True</property>
- <signal name="activate" handler="viewer_menu_send" object="caf12"/>
- </widget>
- </child>
- <child>
- <widget class="GtkSeparatorMenuItem" id="separatormenuitem3">
- <property name="visible">True</property>
- </widget>
- </child>
- <child>
- <widget class="GtkMenuItem" id="menu-send-print">
- <property name="visible">True</property>
- <property name="label" translatable="yes">_PrintScreen</property>
- <property name="use_underline">True</property>
- <signal name="activate" handler="viewer_menu_send" object="printscreen"/>
- </widget>
- </child>
- </widget>
- </child>
</widget>
</child>
<child>
diff -r 92da1556bbf9 src/viewer.h
--- a/src/viewer.h Fri Nov 28 07:24:56 2008 -0500
+++ b/src/viewer.h Tue Dec 16 13:34:01 2008 -0700
@@ -29,6 +29,21 @@
gboolean waitvm,
gboolean reconnect,
gboolean verbose,
+ const char *keymapfile,
GtkWidget *container);
+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 */
diff -r 92da1556bbf9 virt-viewer.spec.in
--- a/virt-viewer.spec.in Fri Nov 28 07:24:56 2008 -0500
+++ b/virt-viewer.spec.in Tue Dec 16 13:34:01 2008 -0700
@@ -81,6 +81,7 @@
%{_datadir}/%{name}/ui/auth.glade
%{_datadir}/%{name}/ui/about.glade
%{_datadir}/%{name}/ui/viewer.glade
+%{_datadir}/%{name}/keymaps/default.xml
%{_mandir}/man1/%{name}*
%if %{_with_plugin}
_______________________________________________
et-mgmt-tools mailing list
[email protected]
https://www.redhat.com/mailman/listinfo/et-mgmt-tools