[PATCH v2] Implement custom grab key combination definition



 Hi all,
this is the second version of my patch to set your own key combination to grab the window. Now I'd like to mentioned that VncGrabSequence structure which carries the information about the pressed keys. There are 2 instances of this structure allocated, one to carry the grab key combination and second one to to carry the current keys pressed in the VNC window. This structure is one of the input parameters for vnc_display_set_grab_keys() function and the return value of vnc_display_get_grab_keys() function call. Since the structure itself can't be printed as string there's also one more function defined, called vnc_display_translate_grab_keys(), which basically translates the VncGrabSequence using the gdk_keyval_name() function. This also introduced an issue with python bindings and therefore the vnc.override file contents have changed to define a the wrappers for
 both getting and setting the grab key combination.

The python binding syntax is as follows (vnc is a vncdisplay object):

vnc.set_grab_keys(list)

where list is a comma delimited list of symbol strings, like "Control_L" or "Alt_L".

The get_grab_keys() method returns a string translated using the translate_grab_keys()
function as declared in vncdisplay.c.

The example in both C language and Python are having both of the functions used and the key recording popup window was introduced in the C example. Both examples and libvirt based tools on GTK (virt-viewer and virt-manager) were tested with the patch
and working fine on Fedora-13 (Goddard).

If any suggestions please write both to my e-mail with CC to the gtk-vnc list.

Michal

--
Michal Novotny<minovotn redhat com>, RHCE
Virtualization Team (xen userspace), Red Hat

>From 55a1cc81f9e38b9977a4772f20f7672a7024b54c Mon Sep 17 00:00:00 2001
From: Michal Novotny <minovotn redhat com>
Date: Tue, 8 Jun 2010 18:27:59 +0200
Subject: [PATCH v2] Implement custom grab key combination definition

 Hi all,
 this is the second version of my patch to set your own key combination to grab the window.
 Now I'd like to mentioned that VncGrabSequence structure which carries the information
 about the pressed keys. There are 2 instances of this structure allocated, one to carry
 the grab key combination and second one to to carry the current keys pressed in the VNC
 window. This structure is one of the input parameters for vnc_display_set_grab_keys()
 function and the return value of vnc_display_get_grab_keys() function call. Since the
 structure itself can't be printed as string there's also one more function defined,
 called vnc_display_translate_grab_keys(), which basically translates the VncGrabSequence
 using the gdk_keyval_name() function. This also introduced an issue with python bindings
 and therefore the vnc.override file contents have changed to define a the wrappers for
 both getting and setting the grab key combination.

The python binding syntax is as follows (vnc is a vncdisplay object):

vnc.set_grab_keys(list)

where list is a comma delimited list of symbol strings, like "Control_L" or "Alt_L".

The get_grab_keys() method returns a string translated using the translate_grab_keys()
function as declared in vncdisplay.c.

The example in both C language and Python are having both of the functions used and
the key recording popup window was introduced in the C example. Both examples and
libvirt based tools on GTK (virt-viewer and virt-manager) were tested with the patch
and working fine on Fedora-13 (Goddard).

If any suggestions please write both to my e-mail with CC to the gtk-vnc list.

Thanks,
Michal
---
 examples/gvncviewer.c      |  167 +++++++++++++++++++++++++++++++++++++++++++-
 examples/gvncviewer.py     |    7 ++-
 src/libgtk-vnc_sym.version |    5 ++
 src/vnc.override           |   53 ++++++++++++++
 src/vncdisplay.c           |  109 ++++++++++++++++++++++++++++-
 src/vncdisplay.h           |    8 ++
 6 files changed, 343 insertions(+), 6 deletions(-)
 mode change 100644 => 100755 examples/gvncviewer.py

diff --git a/examples/gvncviewer.c b/examples/gvncviewer.c
index c3fbd74..dba3006 100644
--- a/examples/gvncviewer.c
+++ b/examples/gvncviewer.c
@@ -43,6 +43,14 @@ static const GOptionEntry options [] =
 
 static GtkWidget *vnc;
 
+typedef struct {
+	GtkWidget *label;
+	guint curkeys;
+	guint numkeys;
+	guint *keysyms;
+	gboolean set;
+} VncGrabDefs;
+
 static void set_title(VncDisplay *vncdisplay, GtkWidget *window,
 	gboolean grabbed)
 {
@@ -51,7 +59,9 @@ static void set_title(VncDisplay *vncdisplay, GtkWidget *window,
 	const char *subtitle;
 
 	if (grabbed)
-		subtitle = "(Press Ctrl+Alt to release pointer) ";
+		subtitle = g_strdup_printf("(Press %s to release pointer) ",
+			vnc_display_translate_grab_keys( vncdisplay,
+				vnc_display_get_grab_keys( vncdisplay)));
 	else
 		subtitle = "";
 
@@ -216,6 +226,146 @@ static void do_scaling(GtkWidget *menu, GtkWidget *vncdisplay)
 		vnc_display_set_scaling(VNC_DISPLAY(vncdisplay), FALSE);
 }
 
+static void dialog_update_keysyms(GtkWidget *window, guint *keysyms, guint numsyms)
+{
+	gchar *keys;
+	int i;
+
+	keys = g_strdup_printf("");
+	for (i = 0; i < numsyms; i++)
+	{
+		keys = g_strdup_printf("%s%s%s", keys,
+			(strlen(keys) > 0) ? "+" : " ", gdk_keyval_name(keysyms[i]));
+	}
+
+	gtk_label_set_text( GTK_LABEL(window), keys);
+}
+
+static gboolean dialog_key_ignore(int keyval)
+{
+	switch (keyval) {
+		case GDK_Return:
+		case GDK_Escape:
+			return TRUE;
+	}
+
+	return FALSE;
+}
+
+static gboolean dialog_key_press(GtkWidget *window G_GNUC_UNUSED,
+        GdkEvent *ev, VncGrabDefs *defs)
+{
+	gboolean keySymExists;
+	int i;
+
+	if (dialog_key_ignore(ev->key.keyval))
+		return FALSE;
+
+	if (defs->set && defs->curkeys)
+		return FALSE;
+
+	/* Check whether we already have keysym in array - i.e. it was handler by something else */
+	keySymExists = FALSE;
+	for (i = 0; i < defs->curkeys; i++) {
+		if (defs->keysyms[i] == ev->key.keyval)
+			keySymExists = TRUE;
+	}
+
+	if (!keySymExists) {
+		defs->keysyms = g_renew(guint, defs->keysyms, defs->curkeys + 1);
+		defs->keysyms[defs->curkeys] = ev->key.keyval;
+		defs->curkeys++;
+	}
+
+	dialog_update_keysyms(defs->label, defs->keysyms, defs->curkeys);
+
+	if (!ev->key.is_modifier) {
+		defs->set = TRUE;
+		defs->numkeys = defs->curkeys;
+		defs->curkeys--;
+	}
+
+	return FALSE;
+}
+
+static gboolean dialog_key_release(GtkWidget *window G_GNUC_UNUSED,
+        GdkEvent *ev, VncGrabDefs *defs)
+{
+	int i;
+
+	if (dialog_key_ignore(ev->key.keyval))
+		return FALSE;
+
+	if (defs->set) {
+		if (defs->curkeys == 0)
+			defs->set = FALSE;
+		if (defs->curkeys)
+			defs->curkeys--;
+		return FALSE;
+	}
+
+	for (i = 0; i < defs->curkeys; i++)
+	{
+		if (defs->keysyms[i] == ev->key.keyval)
+		{
+			defs->keysyms[i] = defs->keysyms[defs->curkeys - 1];
+			defs->curkeys--;
+			defs->keysyms = g_renew(guint, defs->keysyms, defs->curkeys);
+		}
+	}
+
+	dialog_update_keysyms(defs->label, defs->keysyms, defs->curkeys);
+
+	return FALSE;
+}
+
+static void do_set_grab_keys(GtkWidget *menu G_GNUC_UNUSED, GtkWidget *window)
+{
+	VncGrabDefs *defs;
+	VncGrabSequence seq;
+	GtkWidget *dialog, *content_area, *label;
+	gint result;
+
+	dialog = gtk_dialog_new_with_buttons ("Key recorder",
+						GTK_WINDOW(window),
+						GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT,
+						GTK_STOCK_OK,
+						GTK_RESPONSE_ACCEPT,
+						GTK_STOCK_CANCEL,
+						GTK_RESPONSE_REJECT,
+						NULL);
+
+	label = gtk_label_new("Please press desired grab key combination");
+	defs = g_new(VncGrabDefs, 1);
+	defs->label = label;
+	defs->keysyms = 0;
+	defs->numkeys = 0;
+	defs->curkeys = 0;
+	defs->set = FALSE;
+	g_signal_connect(dialog, "key-press-event",
+			G_CALLBACK(dialog_key_press), defs);
+	g_signal_connect(dialog, "key-release-event",
+			G_CALLBACK(dialog_key_release), defs);
+	gtk_widget_set_size_request(dialog, 300, 100);
+	content_area = gtk_dialog_get_content_area( GTK_DIALOG(dialog) );
+	gtk_container_add( GTK_CONTAINER(content_area), label);
+	gtk_widget_show_all(dialog);
+
+	result = gtk_dialog_run(GTK_DIALOG(dialog));
+	if (result == GTK_RESPONSE_ACCEPT) {
+		/* Accepted so we make a grab sequence from it */
+		seq.numkeys = defs->numkeys;
+		seq.keysyms = g_new(guint, seq.numkeys);
+		for (int i = 0; i < defs->numkeys; i++)
+			seq.keysyms[i] = defs->keysyms[i];
+
+		vnc_display_set_grab_keys( VNC_DISPLAY(vnc), seq );
+		set_title(VNC_DISPLAY(vnc), window, FALSE);
+	}
+	g_free(defs);
+	gtk_widget_destroy(dialog);
+}
+
 static void vnc_credential(GtkWidget *vncdisplay, GValueArray *credList)
 {
 	GtkWidget *dialog = NULL;
@@ -353,7 +503,7 @@ int main(int argc, char **argv)
 	GtkWidget *window;
 	GtkWidget *layout;
 	GtkWidget *menubar;
-	GtkWidget *sendkey, *view;
+	GtkWidget *sendkey, *view, *settings;
 	GtkWidget *submenu;
 	GtkWidget *caf1;
 	GtkWidget *caf2;
@@ -367,6 +517,7 @@ int main(int argc, char **argv)
 	GtkWidget *cab;
 	GtkWidget *fullscreen;
 	GtkWidget *scaling;
+	GtkWidget *showgrabkeydlg;
 	const char *help_msg = "Run 'gvncviewer --help' to see a full list of available command line options";
 
 	/* Setup command line options */
@@ -441,6 +592,16 @@ int main(int argc, char **argv)
 
 	gtk_menu_item_set_submenu(GTK_MENU_ITEM(view), submenu);
 
+	settings = gtk_menu_item_new_with_mnemonic("_Settings");
+	gtk_menu_shell_append(GTK_MENU_SHELL(menubar), settings);
+
+	submenu = gtk_menu_new();
+
+	showgrabkeydlg = gtk_menu_item_new_with_mnemonic("_Set grab keys");
+	gtk_menu_shell_append(GTK_MENU_SHELL(submenu), showgrabkeydlg);
+
+	gtk_menu_item_set_submenu(GTK_MENU_ITEM(settings), submenu);
+
 #if WITH_LIBVIEW
 	ViewAutoDrawer_SetActive(VIEW_AUTODRAWER(layout), FALSE);
 	ViewOvBox_SetOver(VIEW_OV_BOX(layout), menubar);
@@ -516,6 +677,8 @@ int main(int argc, char **argv)
 			 G_CALLBACK(send_cad), vnc);
 	g_signal_connect(cab, "activate",
 			 G_CALLBACK(send_cab), vnc);
+	g_signal_connect(showgrabkeydlg, "activate",
+			 G_CALLBACK(do_set_grab_keys), window);
 	g_signal_connect(fullscreen, "toggled",
 			 G_CALLBACK(do_fullscreen), window);
 	g_signal_connect(scaling, "toggled",
diff --git a/examples/gvncviewer.py b/examples/gvncviewer.py
old mode 100644
new mode 100755
index 9a74268..9339635
--- a/examples/gvncviewer.py
+++ b/examples/gvncviewer.py
@@ -29,7 +29,7 @@ if len(sys.argv) != 2 and len(sys.argv) != 3:
 def set_title(vnc, window, grabbed):
     name = vnc.get_name()
     if grabbed:
-        subtitle = "(Press Ctrl+Alt to release pointer) "
+        subtitle = "(Press %s to release pointer) " % vnc.get_grab_keys()
     else:
         subtitle = ""
 
@@ -173,6 +173,11 @@ layout.add(vnc)
 vnc.realize()
 vnc.set_pointer_grab(True)
 vnc.set_keyboard_grab(True)
+
+# Example to change grab key combination to Ctrl+Alt+g
+grab_keys = [ "Control_L", "Alt_L", "g" ]
+vnc.set_grab_keys(grab_keys)
+
 #v.set_pointer_local(True)
 
 if len(sys.argv) == 3:
diff --git a/src/libgtk-vnc_sym.version b/src/libgtk-vnc_sym.version
index b6f5347..16a1716 100644
--- a/src/libgtk-vnc_sym.version
+++ b/src/libgtk-vnc_sym.version
@@ -68,6 +68,11 @@
     vnc_image_framebuffer_new;
     vnc_image_framebuffer_get_image;
 
+# grab key settings support
+    vnc_display_set_grab_keys;
+    vnc_display_get_grab_keys;
+    vnc_display_translate_grab_keys;
+
   local:
       *;
 };
diff --git a/src/vnc.override b/src/vnc.override
index dfc346e..60dd3a9 100644
--- a/src/vnc.override
+++ b/src/vnc.override
@@ -52,3 +52,56 @@ _wrap_vnc_display_send_keys(PyGObject *self,
     Py_INCREF(Py_None);
     return Py_None;
 }
+%%
+override vnc_display_get_grab_keys kwargs
+static PyObject*
+_wrap_vnc_display_get_grab_keys(PyGObject *self,
+                            PyObject *args, PyObject *kwargs)
+{
+    gchar* keys;
+    VncGrabSequence seq;
+
+    keys = vnc_display_translate_grab_keys( VNC_DISPLAY(self->obj),
+               vnc_display_get_grab_keys(VNC_DISPLAY(self->obj)) );
+
+    return PyString_FromString(keys);
+}
+%%
+override vnc_display_set_grab_keys kwargs
+static PyObject*
+_wrap_vnc_display_set_grab_keys(PyGObject *self,
+                            PyObject *args, PyObject *kwargs)
+{
+    static char *kwlist[] = {"keys", NULL};
+    PyObject *keyList;
+    int i, len;
+    VncGrabSequence seq;
+
+    if (!PyArg_ParseTupleAndKeywords(args, kwargs,
+                                     "O|I:VncDisplay.set_grab_keys", kwlist,
+                                     &keyList))
+        return NULL;
+
+    if (!PyList_Check(keyList))
+        return NULL;
+
+    len = PyList_Size(keyList);
+    seq.numkeys = len;
+    seq.keysyms = g_new(guint, len);
+
+    for (i = 0 ; i < len ; i++) {
+        PyObject *val;
+        char *sym;
+        val = PyList_GetItem(keyList, i);
+        sym = PyString_AsString(val);
+        if (!sym) {
+            return NULL;
+        }
+        seq.keysyms[i] = (guint)gdk_keyval_from_name(sym);
+    }
+
+    vnc_display_set_grab_keys(VNC_DISPLAY(self->obj), seq);
+
+    Py_INCREF(Py_None);
+    return Py_None;
+}
diff --git a/src/vncdisplay.c b/src/vncdisplay.c
index a031125..024b4d4 100644
--- a/src/vncdisplay.c
+++ b/src/vncdisplay.c
@@ -85,6 +85,11 @@ struct _VncDisplayPrivate
 
 	GSList *preferable_auths;
 	const guint8 const *keycode_map;
+
+	/* vnckeyseq contains the current vnc key sequence */
+	VncGrabSequence *vnckeyseq;
+	/* vncgrabseq contains the key sequence for grabbing */
+	VncGrabSequence vncgrabseq;
 };
 
 G_DEFINE_TYPE(VncDisplay, vnc_display, GTK_TYPE_DRAWING_AREA)
@@ -673,6 +678,70 @@ static gboolean motion_event(GtkWidget *widget, GdkEventMotion *motion)
 	return TRUE;
 }
 
+static gboolean grab_key_ignore(int keyval)
+{
+	switch (keyval) {
+		case GDK_Return:
+		case GDK_Escape:
+			return TRUE;
+	}
+
+	return FALSE;
+}
+
+static void grab_key_press(VncGrabSequence *seq, guint keyval)
+{
+	if (grab_key_ignore(keyval))
+		return;
+
+	seq->keysyms = g_renew(guint, seq->keysyms, seq->numkeys + 1);
+	seq->keysyms[seq->numkeys] = keyval;
+	seq->numkeys++;
+}
+
+static void grab_key_release(VncGrabSequence *seq, guint keyval)
+{
+	int i;
+
+	if (grab_key_ignore(keyval))
+		return;
+
+	for (i = 0; i < seq->numkeys; i++)
+	{
+		if (seq->keysyms[i] == keyval)
+		{
+			seq->keysyms[i] = seq->keysyms[seq->numkeys - 1];
+			seq->numkeys--;
+			seq->keysyms = g_renew(guint, seq->keysyms, seq->numkeys);
+		}
+	}
+}
+
+static gboolean check_for_grab_key(GtkWidget *widget, int type, int keyval)
+{
+	VncDisplayPrivate *priv = VNC_DISPLAY(widget)->priv;
+	VncGrabSequence *cseq = priv->vnckeyseq;
+	VncGrabSequence seq = priv->vncgrabseq;
+	int i, ii, matches;
+
+	if (type == GDK_KEY_RELEASE)
+		grab_key_release(cseq, keyval);
+	else
+	if (type == GDK_KEY_PRESS)
+		grab_key_press(cseq, keyval);
+
+	matches = 0;
+	for (i = 0; i < cseq->numkeys; i++)
+		for (ii = 0; ii < seq.numkeys; ii++)
+			if (seq.keysyms[ii] == cseq->keysyms[i])
+				matches++;
+
+	if ((type == GDK_KEY_PRESS) && (matches == seq.numkeys))
+            return TRUE;
+
+        return FALSE;
+}
+
 static gboolean key_event(GtkWidget *widget, GdkEventKey *key)
 {
 	VncDisplayPrivate *priv = VNC_DISPLAY(widget)->priv;
@@ -752,9 +821,7 @@ static gboolean key_event(GtkWidget *widget, GdkEventKey *key)
 		}
 	}
 
-	if (key->type == GDK_KEY_PRESS &&
-	    ((keyval == GDK_Control_L && (key->state & GDK_MOD1_MASK)) ||
-	     (keyval == GDK_Alt_L && (key->state & GDK_CONTROL_MASK)))) {
+	if (check_for_grab_key(widget, key->type, key->keyval)) {
 		if (priv->in_pointer_grab)
 			do_pointer_ungrab(VNC_DISPLAY(widget), FALSE);
 		else if (!priv->grab_keyboard || !priv->absolute)
@@ -1816,6 +1883,16 @@ static void vnc_display_init(VncDisplay *display)
 	priv->local_pointer = FALSE;
 	priv->shared_flag = FALSE;
 	priv->force_size = TRUE;
+	priv->vnckeyseq = g_new(VncGrabSequence, 1);
+	memset(priv->vnckeyseq, 0, sizeof(VncGrabSequence));
+
+	VncGrabSequence seq;
+	seq.keysyms = g_new(guint, 2);
+	seq.keysyms[0] = GDK_Control_L;
+	seq.keysyms[1] = GDK_Alt_L;
+	seq.numkeys = 2,
+
+	vnc_display_set_grab_keys(display, seq);
 
 	/*
 	 * Both these two provide TLS based auth, and can layer
@@ -1898,6 +1975,32 @@ void vnc_display_set_pointer_grab(VncDisplay *obj, gboolean enable)
 		do_pointer_ungrab(obj, FALSE);
 }
 
+void vnc_display_set_grab_keys(VncDisplay *obj, VncGrabSequence seq)
+{
+	obj->priv->vncgrabseq = seq;
+}
+
+VncGrabSequence vnc_display_get_grab_keys(VncDisplay *obj)
+{
+	return obj->priv->vncgrabseq;
+}
+
+const gchar *vnc_display_translate_grab_keys(VncDisplay *obj G_GNUC_UNUSED, VncGrabSequence seq)
+{
+	gchar *keys;
+	int i;
+
+	keys = g_strdup_printf("");
+	for (i = 0; i < seq.numkeys; i++)
+	{
+		keys = g_strdup_printf("%s%s%s", keys,
+			(strlen(keys) > 0) ? "+" : "",
+			gdk_keyval_name(seq.keysyms[i]));
+	}
+
+	return keys;
+}
+
 void vnc_display_set_keyboard_grab(VncDisplay *obj, gboolean enable)
 {
 	VncDisplayPrivate *priv = obj->priv;
diff --git a/src/vncdisplay.h b/src/vncdisplay.h
index 1b5b297..bd8dc67 100644
--- a/src/vncdisplay.h
+++ b/src/vncdisplay.h
@@ -80,6 +80,11 @@ typedef enum
 	VNC_DISPLAY_DEPTH_COLOR_ULTRA_LOW
 } VncDisplayDepthColor;
 
+typedef struct {
+	guint *keysyms;
+	guint numkeys;
+} VncGrabSequence;
+
 GType		vnc_display_get_type(void);
 GtkWidget *	vnc_display_new(void);
 
@@ -94,6 +99,9 @@ void            vnc_display_send_keys_ex(VncDisplay *obj, const guint *keyvals,
 					 int nkeyvals, VncDisplayKeyEvent kind);
 
 void		vnc_display_send_pointer(VncDisplay *obj, gint x, gint y, int button_mask);
+void		vnc_display_set_grab_keys(VncDisplay *obj, VncGrabSequence seq);
+VncGrabSequence	vnc_display_get_grab_keys(VncDisplay *obj);
+const gchar*	vnc_display_translate_grab_keys(VncDisplay *obj, VncGrabSequence seq);
 
 gboolean	vnc_display_set_credential(VncDisplay *obj, int type, const gchar *data);
 
-- 
1.7.0.1



[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]