Re: [gtk-vnc-devel] VNC sessions on a 3-d spinning cube



And with the code attached this time....

On Mon, Nov 05, 2007 at 01:17:01AM +0000, Daniel P. Berrange wrote:
> One evil thought, led to another even more evil thought, and thus I ended 
> up writing a VNC viewer program which displays multiple VNC sessions on a 
> spinning cube....
> 
> http://berrange.com/personal/diary/2007/11/vnc-sessions-on-3-d-spinning-cube
> 
> It doesn't work with the current version of GTK-VNC. I needed to add a
> couple of Anthony's patches to propagate mouse events. I also need to add
> an API for sending fake mouse events, and a nnew signal to get notices of
> the VNC region updates so I could refresh the textures. I don't claim it
> is fast/efficient. It is fun though. I think its probably better to write
> it to interact directly with the GVNC class, rather than hooking into the
> VNC DIsplay frontend, since Clutter doesn't technically need to use GTK
> at all - it talks OpenGL & X11 directly. I used the Clutter<->GTK bridge
> for this hack though.

Dan.
-- 
|=- Red Hat, Engineering, Emerging Technologies, Boston.  +1 978 392 2496 -=|
|=-           Perl modules: http://search.cpan.org/~danberr/              -=|
|=-               Projects: http://freshmeat.net/~danielpb/               -=|
|=-  GnuPG: 7D3B9505   F3C9 553F A1DA 4AC2 5648 23C1 B3DF F742 7D3B 9505  -=| 
diff -r 4a821c404746 examples/gvncviewer.c
--- a/examples/gvncviewer.c	Fri Oct 26 09:59:54 2007 -0400
+++ b/examples/gvncviewer.c	Sun Nov 04 20:26:51 2007 -0500
@@ -80,28 +80,28 @@ static void send_caf1(GtkWidget *menu G_
 {
 	guint keys[] = { GDK_Control_L, GDK_Alt_L, GDK_F1 };
 	printf("Sending Ctrl+Alt+F1\n");
-	vnc_display_send_keys(VNC_DISPLAY(vnc), keys, sizeof(keys)/sizeof(keys[0]));
+	vnc_display_send_keys(VNC_DISPLAY(vnc), keys, sizeof(keys)/sizeof(keys[0]), 3);
 }
 
 static void send_caf7(GtkWidget *menu G_GNUC_UNUSED, GtkWidget *vnc)
 {
 	guint keys[] = { GDK_Control_L, GDK_Alt_L, GDK_F7 };
 	printf("Sending Ctrl+Alt+F7\n");
-	vnc_display_send_keys(VNC_DISPLAY(vnc), keys, sizeof(keys)/sizeof(keys[0]));
+	vnc_display_send_keys(VNC_DISPLAY(vnc), keys, sizeof(keys)/sizeof(keys[0]), 3);
 }
 
 static void send_cad(GtkWidget *menu G_GNUC_UNUSED, GtkWidget *vnc)
 {
 	guint keys[] = { GDK_Control_L, GDK_Alt_L, GDK_Delete };
 	printf("Sending Ctrl+Alt+Delete\n");
-	vnc_display_send_keys(VNC_DISPLAY(vnc), keys, sizeof(keys)/sizeof(keys[0]));
+	vnc_display_send_keys(VNC_DISPLAY(vnc), keys, sizeof(keys)/sizeof(keys[0]), 3);
 }
 
 static void send_cab(GtkWidget *menu G_GNUC_UNUSED, GtkWidget *vnc)
 {
 	guint keys[] = { GDK_Control_L, GDK_Alt_L, GDK_BackSpace };
 	printf("Sending Ctrl+Alt+Backspace\n");
-	vnc_display_send_keys(VNC_DISPLAY(vnc), keys, sizeof(keys)/sizeof(keys[0]));
+	vnc_display_send_keys(VNC_DISPLAY(vnc), keys, sizeof(keys)/sizeof(keys[0]), 3);
 }
 
 static void vnc_credential(GtkWidget *vnc, GValueArray *credList)
diff -r 4a821c404746 examples/gvncviewer.py
--- a/examples/gvncviewer.py	Fri Oct 26 09:59:54 2007 -0400
+++ b/examples/gvncviewer.py	Sun Nov 04 20:26:51 2007 -0500
@@ -45,15 +45,15 @@ def vnc_disconnected(src):
 
 def send_caf1(src, vnc):
     print "Send Ctrl+Alt+F1"
-    vnc.send_keys(["Control_L", "Alt_L", "F1"])
+    vnc.send_keys(["Control_L", "Alt_L", "F1"], 3)
 
 def send_caf7(src, vnc):
     print "Send Ctrl+Alt+F7"
-    vnc.send_keys(["Control_L", "Alt_L", "F7"])
+    vnc.send_keys(["Control_L", "Alt_L", "F7"], 3)
 
 def send_cad(src, vnc):
     print "Send Ctrl+Alt+Del"
-    vnc.send_keys(["Control_L", "Alt_L", "Del"])
+    vnc.send_keys(["Control_L", "Alt_L", "Del"], 3)
 
 def send_cab(src, vnc):
     print "Send Ctrl+Alt+BackSpace"
diff -r 4a821c404746 src/libgtk-vnc_sym.version
--- a/src/libgtk-vnc_sym.version	Fri Oct 26 09:59:54 2007 -0400
+++ b/src/libgtk-vnc_sym.version	Sun Nov 04 20:26:51 2007 -0500
@@ -10,6 +10,7 @@
     vnc_display_close;
 
     vnc_display_send_keys;
+    vnc_display_send_pointer;
 
     vnc_display_set_credential;
 
diff -r 4a821c404746 src/vnc.override
--- a/src/vnc.override	Fri Oct 26 09:59:54 2007 -0400
+++ b/src/vnc.override	Sun Nov 04 20:26:51 2007 -0500
@@ -16,18 +16,23 @@ _wrap_vnc_display_send_keys(PyGObject *s
 _wrap_vnc_display_send_keys(PyGObject *self,
                             PyObject *args, PyObject *kwargs) 
 {
-    static char *kwlist[] = {"keys", NULL}; 
+    static char *kwlist[] = {"keys", "flags", NULL}; 
     PyObject *keyList;
     int ret, i, len;
+    int flags;
     guint *keys;
 
     if (!PyArg_ParseTupleAndKeywords(args, kwargs,    
-                                     "O:VncDisplay.send_keys", kwlist,
-                                     &keyList))
-        return;
+                                     "Oi:VncDisplay.send_keys", kwlist,
+                                     &keyList, &flags)) {
+        Py_INCREF(Py_None);
+        return Py_None;
+    }
 
-    if (!PyList_Check(keyList))
-        return;
+    if (!PyList_Check(keyList)) {
+        Py_INCREF(Py_None);
+        return Py_None;
+    }
 
     len = PyList_Size(keyList);
     keys = malloc(sizeof(guint)*len);
@@ -38,11 +43,14 @@ _wrap_vnc_display_send_keys(PyGObject *s
         sym = PyString_AsString(val);
         if (!sym) {
             free(keys);
-            return;
+            Py_INCREF(Py_None);
+	    return Py_None;
         }
         keys[i] = gdk_keyval_from_name(sym);
     }
 
-    vnc_display_send_keys(VNC_DISPLAY(self->obj), keys, len);
+    vnc_display_send_keys(VNC_DISPLAY(self->obj), keys, len, flags);
     free(keys);
+    Py_INCREF(Py_None);
+    return Py_None;
 }
diff -r 4a821c404746 src/vncdisplay.c
--- a/src/vncdisplay.c	Fri Oct 26 09:59:54 2007 -0400
+++ b/src/vncdisplay.c	Sun Nov 04 20:26:51 2007 -0500
@@ -74,6 +74,7 @@ typedef enum
  	VNC_AUTH_CREDENTIAL,
 
 	VNC_DESKTOP_RESIZE,
+	VNC_DESKTOP_UPDATE,
 
 	VNC_AUTH_FAILURE,
 	VNC_AUTH_UNSUPPORTED,
@@ -121,7 +122,7 @@ static gboolean expose_event(GtkWidget *
 	GdkRegion *clear, *copy;
 
 	if (priv->shm_image == NULL)
-		return TRUE;
+		return FALSE;
 
 	x = MIN(expose->area.x, priv->fb.width);
 	y = MIN(expose->area.y, priv->fb.height);
@@ -151,7 +152,7 @@ static gboolean expose_event(GtkWidget *
 	gdk_region_destroy(clear);
 	gdk_region_destroy(copy);
 
-	return TRUE;
+	return FALSE;
 }
 
 static void do_keyboard_grab(VncDisplay *obj, gboolean quiet)
@@ -355,7 +356,7 @@ static gboolean key_event(GtkWidget *wid
 	GdkModifierType consumed;
 
 	if (priv->gvnc == NULL || !gvnc_is_initialized(priv->gvnc))
-		return TRUE;
+		return FALSE;
 
 	/*
 	 * Key handling in VNC is screwy. The event.keyval from GTK is
@@ -393,7 +394,7 @@ static gboolean key_event(GtkWidget *wid
 			do_pointer_grab(VNC_DISPLAY(widget), FALSE);
 	}
 
-	return TRUE;
+	return FALSE;
 }
 
 static gboolean enter_event(GtkWidget *widget, GdkEventCrossing *crossing,
@@ -437,6 +438,10 @@ static gboolean on_update(void *opaque, 
 
 	gtk_widget_queue_draw_area(obj, x, y, w, h);
 
+	g_signal_emit (G_OBJECT (obj),
+		       signals[VNC_DESKTOP_UPDATE],
+		       0,
+		       x, y, w, h);
 	return TRUE;
 }
 
@@ -832,17 +837,28 @@ void vnc_display_close(VncDisplay *obj)
 }
 
 
-void vnc_display_send_keys(VncDisplay *obj, const guint *keyvals, int nkeyvals)
+void vnc_display_send_keys(VncDisplay *obj, const guint *keyvals, int nkeyvals, int flags)
 {
 	int i;
 	if (obj->priv->gvnc == NULL || !gvnc_is_open(obj->priv->gvnc))
 		return;
 
-	for (i = 0 ; i < nkeyvals ; i++)
-		gvnc_key_event(obj->priv->gvnc, 1, keyvals[i]);
-
-	for (i = (nkeyvals-1) ; i >= 0 ; i--)
-		gvnc_key_event(obj->priv->gvnc, 0, keyvals[i]);
+	if (flags & 1)
+		for (i = 0 ; i < nkeyvals ; i++)
+			gvnc_key_event(obj->priv->gvnc, 1, keyvals[i]);
+
+	if (flags & 2)
+		for (i = (nkeyvals-1) ; i >= 0 ; i--)
+			gvnc_key_event(obj->priv->gvnc, 0, keyvals[i]);
+}
+
+
+void vnc_display_send_pointer(VncDisplay *obj, int buttons, int x, int y)
+{
+	if (obj->priv->gvnc == NULL || !gvnc_is_open(obj->priv->gvnc))
+		return;
+
+	gvnc_pointer_event(obj->priv->gvnc, buttons, x, y);
 }
 
 
@@ -980,6 +996,18 @@ static void vnc_display_class_init(VncDi
 			     G_TYPE_NONE,
 			     2,
 			     G_TYPE_INT, G_TYPE_INT);
+
+	signals[VNC_DESKTOP_UPDATE] =
+		g_signal_new("vnc-desktop-update",
+			     G_TYPE_FROM_CLASS(klass),
+			     G_SIGNAL_RUN_LAST | G_SIGNAL_NO_HOOKS,
+			     0,
+			     NULL,
+			     NULL,
+			     g_cclosure_user_marshal_VOID__INT_INT_INT_INT,
+			     G_TYPE_NONE,
+			     4,
+			     G_TYPE_INT, G_TYPE_INT, G_TYPE_INT, G_TYPE_INT);
 
 	signals[VNC_AUTH_FAILURE] =
 		g_signal_new("vnc-auth-failure",
diff -r 4a821c404746 src/vncdisplay.h
--- a/src/vncdisplay.h	Fri Oct 26 09:59:54 2007 -0400
+++ b/src/vncdisplay.h	Sun Nov 04 20:26:51 2007 -0500
@@ -72,7 +72,8 @@ gboolean	vnc_display_is_open(VncDisplay 
 gboolean	vnc_display_is_open(VncDisplay *obj);
 void		vnc_display_close(VncDisplay *obj);
 
-void            vnc_display_send_keys(VncDisplay *obj, const guint *keyvals, int nkeyvals);
+void            vnc_display_send_keys(VncDisplay *obj, const guint *keyvals, int nkeyvals, int down);
+void vnc_display_send_pointer(VncDisplay *obj, int buttons, int x, int y);
 
 gboolean	vnc_display_set_credential(VncDisplay *obj, int type, const gchar *data);
 
diff -r 4a821c404746 src/vncmarshal.txt
--- a/src/vncmarshal.txt	Fri Oct 26 09:59:54 2007 -0400
+++ b/src/vncmarshal.txt	Sun Nov 04 20:26:51 2007 -0500
@@ -1,1 +1,2 @@ VOID:INT,INT
 VOID:INT,INT
+VOID:INT,INT,INT,INT
#!/usr/bin/python
#
# Copyright (C) 2007 Daniel P. Berrange <dan berrange com>
#
# 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., 675 Mass Ave, Cambridge, MA 02139, USA.
#
#
#
# A VNC viewer program displaying multiple VNC sessions
# on a 3-d spinning cube....
#

import sys
import gobject
import gtk
import gtkvnc
import math
import clutter
import clutter.keysyms
import clutter.cluttergtk


if len(sys.argv) < 2:
    print "syntax: gvncclutter.py DISPLAY-1 [DISPLAY-2....]"
    sys.exit(1)

class ClutterVNC:
    def __init__(self, layout, group, host, port, password):
        self.vnc = gtkvnc.Display()
        self.tex = None
        self.group = group

        layout.add(self.vnc)
        self.vnc.realize()
        self.vnc.set_credential(gtkvnc.CREDENTIAL_PASSWORD, password)
        self.vnc.open_host(host, port)

        self.vnc.connect("vnc-desktop-resize", self.reinit_texture)
        self.vnc.connect("vnc-desktop-update", self.refresh_texture)

    def size(self):
        return (self.vnc.get_width(), self.vnc.get_height())

    def show(self):
        self.tex.show()

    def reinit_texture(self, vnc, w, h):
        new = 0
        if self.tex is None:
            self.tex = clutter.Texture()
            new = 1
        self.tex.set_pixbuf(self.vnc.get_pixbuf())
        self.tex.set_size(w, h)
        if new:
            self.group.add(self.tex)

    def refresh_texture(self, vnc, x,y,w,h):
        if self.tex is not None:
            self.tex.set_pixbuf(self.vnc.get_pixbuf())

class ClutterVNCSet:
    def __init__(self, layout):
        self.layout = layout
        self.embed = clutter.cluttergtk.Embed()
        self.embed.set_flags(gtk.CAN_FOCUS)
        self.embed.grab_focus()
        self.stage = self.embed.get_stage()
        self.stage.set_color(clutter.Color(0, 0, 0, 255))
        self.stage.set_size(800,800)
        self.group = clutter.Group()

        self.group.set_position(0,0)
        self.group.set_size(800,800)
        self.stage.add(self.group)
        self.group.show()
        self.vncs = []
        self.current = 0
        self.offset = 0
        self.animate = 0

        self.timeline = clutter.Timeline(30, 30)
        self.timeline.connect("new-frame", self.do_frame)

        self.layout.add(self.embed)
        self.embed.show_all()

        self.group.connect("add", self.child_added)
        self.stage.connect("button-press-event", self.button_event)
        self.stage.connect("button-release-event", self.button_event)
        self.stage.connect("motion-event", self.button_event)
        self.stage.connect("key-press-event", self.key_event)
        self.stage.connect("key-release-event", self.key_event)

    def do_frame(self, timeline, frame):
        if self.animate == 1:
            self.offset = 1-(frame/30.0)
        else:
            self.offset = (frame/30.0)-1
        self.reposition_all()

    def child_added(self, src, child):
        self.reposition_all()


    def reposition_one(self, tex, pos):
        (w,h) = self.stage.get_size()
        (cw,ch) = tex.get_size()

        nsides = len(self.vncs)
        rot = 360/nsides * (pos-self.current+self.offset)
        if nsides > 2:
            depth = (cw/2) / math.tan(math.radians((360/nsides)/2))
        else:
            depth = 0

        # XXX kill when depth sorting is done
        tex.set_opacity(127)

        tex.set_position((w-cw)/2, (h-cw)/2)
        tex.rotate_y(int(rot), (cw/2), int(-depth))

        tex.show()
        #print "Rotate %d %f   at %f depth %f" % (self.current, self.offset, rot, depth)

    def reposition_all(self):
        for i in range(len(self.vncs)):
            if self.vncs[i].tex is not None:
                self.reposition_one(self.vncs[i].tex, i)

        # XXX depth sort


    def button_state(self, ev):
        state = 0
        if ev.get_state() & clutter.BUTTON1_MASK:
            state = state | (1 << 0)
        elif ev.get_state() & clutter.BUTTON2_MASK:
            state = state | (1 << 1)
        elif ev.get_state() & clutter.BUTTON3_MASK:
            state = state | (1 << 2)
        elif ev.get_state() & clutter.BUTTON4_MASK:
            state = state | (1 << 3)
        elif ev.get_state() & clutter.BUTTON5_MASK:
            state = state | (1 << 4)

        if ev.type == clutter.BUTTON_PRESS:
            state = state | (1 << (ev.button-1))
        elif ev.type == clutter.BUTTON_RELEASE:
            state = state & ~(1 << (ev.button-1))
        return state

    def button_event(self, src, ev):
        vnc = self.vncs[self.current]
        (w,h) = self.stage.get_size()
        (cw,ch) = vnc.tex.get_size()
        x = ev.x - ((w-cw)/2)
        y = ev.y - ((h-ch)/2)
        state = self.button_state(ev)
        #print "Button " + str(state) + " " + str(x) + " "  + str(y)
        vnc.vnc.send_pointer(state, x, y)

    def key_event(self, src, ev):
        if ev.type == clutter.KEY_PRESS and \
                (ev.keyval == clutter.keysyms.Page_Up or
                 ev.keyval == clutter.keysyms.Page_Down) and \
                 (ev.get_state() & clutter.CONTROL_MASK) and \
                 (ev.get_state() & clutter.MOD1_MASK):
            if ev.keyval == clutter.keysyms.Page_Up:
                self.animate = -1
                if self.current == 0:
                    self.current = len(self.vncs)-1
                else:
                    self.current = self.current - 1
            else:
                self.animate = 1
                if self.current == (len(self.vncs)-1):
                    self.current = 0
                else:
                    self.current = self.current + 1
            self.timeline.start()
            return

        vnc = self.vncs[self.current]
        kv = gtk.gdk.keyval_name(ev.keyval)
        if ev.type == clutter.KEY_PRESS:
            vnc.vnc.send_keys([kv], 1)
        else:
            vnc.vnc.send_keys([kv], 2)

    def add_vnc(self, host, port, passwd):
        vnc = ClutterVNC(self.layout, self.group, host, port, passwd)
        self.vncs.append(vnc)


def split_host_port(disp):
    offset = disp.find(":")
    if offset != -1:
        return (disp[:offset], str(5900 + int(disp[offset+1:])))
    else:
        return (disp, "5900")

window = gtk.Window()
layout = gtk.VBox()
window.add(layout)
window.show_all()
vncset = ClutterVNCSet(layout)

for i in range(len(sys.argv)-1):
    (host,port) = split_host_port(sys.argv[i+1])
    vncset.add_vnc(host, port, "123456")

window.set_focus(vncset.embed)

gtk.main()


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