[PATCH] Implement paste from clipboard functionality



Hi,
this is the patch to implement paste from clipboard to the VNC Window
functionality. This is the first version of the patch, tested mainly
to copy & paste URL addresses to the virtual machines to be used as
an installation source (e.g. in anaconda when using network install).
The core functionality is done by the vnc_display_clipboard_paste()
functionality that has been inspired by xsel source code to grab the
clipboard contents. Also, a new function to send multiple scancodes
per character, called the vnc_display_send_character() has been
introduced since vnc_display_send_keys_ex() was having issue when
sending multiple characters at once. Mainly special characters like
slashes, backslashes, commas, semicolons, exclamation marks etc.
were having issues when used in vnc_display_send_keys_ex() so the
implementation of vnc_display_send_character() was necessary for
this purpose.

Michal

Signed-off-by: Michal Novotny <minovotn redhat com>

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

>From c7edd42700c3b0d99fe060742197d1201bf7e49e Mon Sep 17 00:00:00 2001
From: Michal Novotny <minovotn redhat com>
Date: Wed, 15 Dec 2010 18:56:42 +0100
Subject: [PATCH] Implement paste from clipboard functionality

Hi,
this is the patch to implement paste from clipboard to the VNC Window
functionality. This is the first version of the patch, tested mainly
to copy & paste URL addresses to the virtual machines to be used as
an installation source (e.g. in anaconda when using network install).
The core functionality is done by the vnc_display_clipboard_paste()
functionality that has been inspired by xsel source code to grab the
clipboard contents. Also, a new function to send multiple scancodes
per character, called the vnc_display_send_character() has been
introduced since vnc_display_send_keys_ex() was having issue when
sending multiple characters at once. Mainly special characters like
slashes, backslashes, commas, semicolons, exclamation marks etc.

Michal

Signed-off-by: Michal Novotny <minovotn redhat com>
---
 examples/gvncviewer.c      |   10 +++
 examples/gvncviewer.py     |    8 ++-
 src/libgtk-vnc_sym.version |    2 +
 src/vncdisplay.c           |  147 ++++++++++++++++++++++++++++++++++++++++++++
 src/vncdisplay.h           |    2 +
 5 files changed, 168 insertions(+), 1 deletions(-)

diff --git a/examples/gvncviewer.c b/examples/gvncviewer.c
index 84f36e0..a156ff5 100644
--- a/examples/gvncviewer.c
+++ b/examples/gvncviewer.c
@@ -186,6 +186,11 @@ static void send_caf1(GtkWidget *menu G_GNUC_UNUSED, GtkWidget *vncdisplay)
 		sizeof(keys)/sizeof(keys[0]));
 }
 
+static void send_clipboard(GtkWidget *menu G_GNUC_UNUSED, GtkWidget *vncdisplay)
+{
+	vnc_display_clipboard_paste(VNC_DISPLAY(vncdisplay));
+}
+
 static void send_caf2(GtkWidget *menu G_GNUC_UNUSED, GtkWidget *vncdisplay)
 {
 	guint keys[] = { GDK_Control_L, GDK_Alt_L, GDK_F2 };
@@ -563,6 +568,7 @@ int main(int argc, char **argv)
 	GtkWidget *caf8;
 	GtkWidget *cad;
 	GtkWidget *cab;
+	GtkWidget *clipboard;
 	GtkWidget *fullscreen;
 	GtkWidget *scaling;
 	GtkWidget *showgrabkeydlg;
@@ -616,6 +622,7 @@ int main(int argc, char **argv)
 	caf8 = gtk_menu_item_new_with_mnemonic("Ctrl+Alt+F_8");
 	cad = gtk_menu_item_new_with_mnemonic("Ctrl+Alt+_Del");
 	cab = gtk_menu_item_new_with_mnemonic("Ctrl+Alt+_Backspace");
+	clipboard = gtk_menu_item_new_with_mnemonic("Clipboard text");
 
 	gtk_menu_shell_append(GTK_MENU_SHELL(submenu), caf1);
 	gtk_menu_shell_append(GTK_MENU_SHELL(submenu), caf2);
@@ -627,6 +634,7 @@ int main(int argc, char **argv)
 	gtk_menu_shell_append(GTK_MENU_SHELL(submenu), caf8);
 	gtk_menu_shell_append(GTK_MENU_SHELL(submenu), cad);
 	gtk_menu_shell_append(GTK_MENU_SHELL(submenu), cab);
+	gtk_menu_shell_append(GTK_MENU_SHELL(submenu), clipboard);
 
 	gtk_menu_item_set_submenu(GTK_MENU_ITEM(sendkey), submenu);
 
@@ -728,6 +736,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(clipboard, "activate",
+			 G_CALLBACK(send_clipboard), vnc);
 	g_signal_connect(showgrabkeydlg, "activate",
 			 G_CALLBACK(do_set_grab_keys), window);
 	g_signal_connect(fullscreen, "toggled",
diff --git a/examples/gvncviewer.py b/examples/gvncviewer.py
old mode 100644
new mode 100755
index c5769f3..a227f59
--- a/examples/gvncviewer.py
+++ b/examples/gvncviewer.py
@@ -84,6 +84,10 @@ def send_cab(src, vnc):
     print "Send Ctrl+Alt+BackSpace"
     vnc.send_keys(["Control_L", "Alt_L", "BackSpace"])
 
+def send_clipboard(src, vnc):
+    print "Send clipboard"
+    vnc.clipboard_paste()
+
 def vnc_auth_cred(src, credList):
     prompt = 0
     data = []
@@ -160,19 +164,21 @@ caf1 = gtk.MenuItem("Ctrl+Alt+F_1")
 caf7 = gtk.MenuItem("Ctrl+Alt+F_7")
 cad = gtk.MenuItem("Ctrl+Alt+_Del")
 cab = gtk.MenuItem("Ctrl+Alt+_Backspace")
+clip = gtk.MenuItem("Clipboard text")
 
 submenu = gtk.Menu()
 submenu.append(caf1)
 submenu.append(caf7)
 submenu.append(cad)
 submenu.append(cab)
+submenu.append(clip)
 sendkeys.set_submenu(submenu)
 
 caf1.connect("activate", send_caf1, vnc)
 caf7.connect("activate", send_caf7, vnc)
 cad.connect("activate", send_cad, vnc)
 cab.connect("activate", send_cab, vnc)
-
+clip.connect("activate", send_clipboard, vnc)
 
 layout.add(menubar)
 layout.add(vnc)
diff --git a/src/libgtk-vnc_sym.version b/src/libgtk-vnc_sym.version
index 7b641ec..a409a6e 100644
--- a/src/libgtk-vnc_sym.version
+++ b/src/libgtk-vnc_sym.version
@@ -13,6 +13,8 @@
     vnc_display_send_pointer;
     vnc_display_send_keys;
     vnc_display_send_keys_ex;
+    vnc_display_clipboard_paste;
+    vnc_display_send_character;
 
     vnc_display_set_credential;
 
diff --git a/src/vncdisplay.c b/src/vncdisplay.c
index deab4d8..80aa786 100644
--- a/src/vncdisplay.c
+++ b/src/vncdisplay.c
@@ -39,6 +39,10 @@
 #include <sys/types.h>
 #include <sys/stat.h>
 #include <unistd.h>
+#include <X11/StringDefs.h>
+#include <X11/Xlib.h>
+#include <X11/Intrinsic.h>
+#include <X11/Xatom.h>
 
 #define VNC_DISPLAY_GET_PRIVATE(obj) \
       (G_TYPE_INSTANCE_GET_PRIVATE((obj), VNC_TYPE_DISPLAY, VncDisplayPrivate))
@@ -1479,6 +1483,149 @@ static guint get_scancode_from_keyval(VncDisplay *obj, guint keyval)
 	return vnc_display_keymap_gdk2rfb(priv->keycode_map, priv->keycode_maplen, keycode);
 }
 
+/* Function get_scancodes_from_keyval():
+ * 	Gets multiple scancodes from one keyval specified and overrides some characters of the mapping
+ * 	to fix the character handling. Used for vnc_display_send_character() function.
+ * Parameters:
+ * 	obj:		VncDisplay object instance
+ * 	keyval:		character numeric value of which we need to get it's scancodes
+ * 	keycode_array:	pointer to array of scancodes to be filled
+ * 	max:		maximum number of scancodes in the structure (since it's allocated before the call)
+ * Output:
+ * 	number of scancodes set in the keycode_array pointer
+ */
+static guint get_scancodes_from_keyval(VncDisplay *obj, guint keyval, guint *keycode_array, guint max)
+{
+	VncDisplayPrivate *priv = obj->priv;
+	guint keycode = 0, i;
+	gboolean modShift = FALSE;
+	GdkKeymapKey *keys = NULL;
+	gint n_keys = 0;
+	guint modShiftExtra[256] = { 0, [58] = 1, [95] = 1, [34] = 1, [63] = 1, [123] = 1, [125] = 1 };
+	guint mapOverride[256] = { 0, [61] = 13, [43] = 78, [47] = 53, [39] = 40, [34] = 40, [92] = 43, [63] = 53, [91] = 26, [93] = 27, [123] = 26, [125] = 27 };
+
+	if (gdk_keymap_get_entries_for_keyval(gdk_keymap_get_default(),
+					      keyval, &keys, &n_keys)) {
+		keycode = keys[0].keycode;
+		if (n_keys > 2)
+			modShift = TRUE;
+		g_free(keys);
+	}
+
+	i = 0;
+	if (mapOverride[keyval]) {
+		if (modShiftExtra[keyval] == 1) {
+			if (i > max - 1)
+				return 0;
+			keycode_array[i++] = vnc_display_keymap_gdk2rfb(priv->keycode_map, priv->keycode_maplen, 50);
+		}
+
+		if (i > max - 1)
+			return 0;
+
+		keycode_array[i++] = mapOverride[keyval];
+	}
+	else {
+		if ((modShift) || (modShiftExtra[keyval] == 1)) {
+			if (i > max - 1)
+				return 0;
+
+			keycode_array[i++] = vnc_display_keymap_gdk2rfb(priv->keycode_map, priv->keycode_maplen, 50);
+		}
+
+		if (i > max - 1)
+			return 0;
+
+		keycode_array[i++] = vnc_display_keymap_gdk2rfb(priv->keycode_map, priv->keycode_maplen, keycode);
+	}
+
+	return i;
+}
+
+/*
+ * Function vnc_display_send_character():
+ * 	Sends the character to VNC window with ability to use multiple scancodes per
+ * 	each character. Used by vnc_display_clipboard_paste() defined below.
+ * Parameters:
+ * 	obj:	VncDisplay object instance
+ * 	keyval:	numeric value of character to be sent
+ * Output:
+ * 	None
+ */
+void vnc_display_send_character(VncDisplay *obj, guint keyval)
+{
+	guint codes = 10;	/* 10 scancodes per character should be more than enough */
+	gint i = 0, numCodes;
+	guint *keycodes = NULL;
+
+	keycodes = malloc( codes * sizeof(guint) );
+	memset(keycodes, 0, codes);
+
+	numCodes = get_scancodes_from_keyval(obj, keyval, keycodes, codes);
+        for (i = 0; i < numCodes; i++)
+		vnc_connection_key_event(obj->priv->conn, 1, keyval,
+				keycodes[i]);
+	for (i = (numCodes-1); i >= 0; i--)
+		vnc_connection_key_event(obj->priv->conn, 0, keyval,
+				keycodes[i]);
+	free(keycodes);
+}
+
+/*
+ * Function vnc_display_clipboard_paste():
+ * 	Reads the X-window clipboard text, inspired by xsel source code
+ * Parameters:
+ * 	obj:	VncDisplay object instance
+ * Outputs:
+ * 	TRUE if clipboard text has been grabbed successfully
+ * 	FALSE if error occured (e.g. if it cannot open X Display)
+ */
+gboolean vnc_display_clipboard_paste(VncDisplay *obj)
+{
+	Display *display;
+	Window root, window;
+	Atom selection = XA_PRIMARY;
+	Atom data;
+	int black;
+	XEvent event;
+	Atom target;
+	int format;
+	unsigned long bytesafter, length;
+	unsigned char *value = NULL;
+	guint i;
+
+	display = XOpenDisplay(NULL);
+	if (display == NULL)
+		return FALSE;
+	root = XDefaultRootWindow(display);
+
+	black = BlackPixel (display, DefaultScreen (display));
+	window = XCreateSimpleWindow (display, root, 0, 0, 1, 1, 0, black, black);
+
+	data = XInternAtom(display, "CLIPBOARD_DATA", False);
+	if (!data)
+		return FALSE;
+	if (!XConvertSelection(display, selection, XA_STRING, data, window,
+				CurrentTime))
+		return FALSE;
+	XSync(display, False);
+
+	XNextEvent (display, &event);
+	if (XGetWindowProperty (event.xselection.display,
+			event.xselection.requestor,
+			event.xselection.property, 0L, 1000000,
+			False, XA_STRING, &target,
+			&format, &length, &bytesafter, &value))
+		return FALSE;
+
+	XDestroyWindow(display, window);
+
+	for (i = 0; i < length; i++)
+		vnc_display_send_character(obj, value[i]);
+
+	return TRUE;
+}
+
 void vnc_display_send_keys_ex(VncDisplay *obj, const guint *keyvals,
 			      int nkeyvals, VncDisplayKeyEvent kind)
 {
diff --git a/src/vncdisplay.h b/src/vncdisplay.h
index 76f44d4..aa1349b 100644
--- a/src/vncdisplay.h
+++ b/src/vncdisplay.h
@@ -97,6 +97,8 @@ void            vnc_display_send_keys(VncDisplay *obj, const guint *keyvals, int
 /* FIXME: can we just eliminate the old send_keys interface? */
 void            vnc_display_send_keys_ex(VncDisplay *obj, const guint *keyvals,
 					 int nkeyvals, VncDisplayKeyEvent kind);
+gboolean	vnc_display_clipboard_paste(VncDisplay *obj);
+void		vnc_display_send_character(VncDisplay *obj, guint keyval);
 
 void		vnc_display_send_pointer(VncDisplay *obj, gint x, gint y, int button_mask);
 void		vnc_display_set_grab_keys(VncDisplay *obj, VncGrabSequence *seq);
-- 
1.7.3.2



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