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 <plc@xxxxxxxxxx>
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 <plc@xxxxxxxxxx> + */ + +#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 et-mgmt-tools@xxxxxxxxxx https://www.redhat.com/mailman/listinfo/et-mgmt-tools