From 43445d6a39c1c350e604b795aa78adac7374124c Mon Sep 17 00:00:00 2001 From: bens Date: Wed, 09 Dec 2009 17:38:04 +0000 Subject: Initial Import --- diff --git a/MAINTAINERS b/MAINTAINERS new file mode 100644 index 0000000..7d1f84e --- /dev/null +++ b/MAINTAINERS @@ -0,0 +1 @@ +Ben Schwartz (bens@alum.mit.edu) diff --git a/NEWS b/NEWS new file mode 100644 index 0000000..b61c9e7 --- /dev/null +++ b/NEWS @@ -0,0 +1,4 @@ +v2: Include gtk-vnc in the bundle using the shim layer. Bundled versions are: +gtk-vnc-0.3.8-2.fc9.i386.rpm +gtk-vnc-python-0.3.8-2.fc9.i386.rpm +http://downloads.sourceforge.net/x11vnc/x11vnc-0.9.8_i386-none-linux diff --git a/README b/README new file mode 100644 index 0000000..9589107 --- /dev/null +++ b/README @@ -0,0 +1,11 @@ +Watch Me lets you share a view of your screen with anyone. It does this by running +a VNC server (x11vnc) on the initiator machine, and running a VNC client (based on +gtk-vnc-python) on the client. + +Watch Me depends on gtk-vnc-python and x11vnc. Copies of these programs are included +in the bundle; however, for maximum compatibility, you should install these programs +using your system's package manager. + +To build a Watch Me bundle including binaries, it is necessary to install gtk-vnc, + gtk-vnc-python, and x11vnc. Current bundles are constructed from RPMs of the first two +and a static binary of the third, using the process described in README.ldshim diff --git a/README.ldshim b/README.ldshim new file mode 100644 index 0000000..9779623 --- /dev/null +++ b/README.ldshim @@ -0,0 +1,18 @@ +Normally, one would add libraries and python modules to a bundle using LD_LIBRARY_PATH +and PYTHONPATH. This gives them priority over the systemwide versions of these items. +However, this is a dangerous approach, as if the included versions are incompatible with +the system, the program will not run. ldshim.py creates a safer alternative, by adding +the bundled binaries to the _end_ of the search path, rather than the beginning, so that +systemwide binaries take precedence. + +To produce a bundle from RPMs, one must download them into the bundle path +and then execute + +cat [name].rpm | rpm2cpio | cpio -di + +for each RPM. This will produce a new root directory tree based on the +current path. One may also need to add other binaries to the tree manually +in directories like usr/bin/. + +If adding python modules, it is important to ensure that every directory +recursively containing the python module is world-readable and executable. diff --git a/activity/activity-watchme.svg b/activity/activity-watchme.svg new file mode 100644 index 0000000..da2bafd --- /dev/null +++ b/activity/activity-watchme.svg @@ -0,0 +1,102 @@ + + + +]> +image/svg+xml + + + + + + + + + diff --git a/activity/activity.info b/activity/activity.info new file mode 100644 index 0000000..fd325cf --- /dev/null +++ b/activity/activity.info @@ -0,0 +1,9 @@ +[Activity] +name = Watch Me +activity_version = 2 +service_name = org.sugarlabs.Watchme +exec = ./ldshim.py sugar-activity watchme.WatchMeActivity +icon = activity-watchme +mime_types = +license = GPLv2 + diff --git a/ldshim.py b/ldshim.py new file mode 100755 index 0000000..a219522 --- /dev/null +++ b/ldshim.py @@ -0,0 +1,53 @@ +#!/usr/bin/env python +def parseconf(confpath): + import os.path + confdir = os.path.dirname(confpath) + f = open(confpath) + dirs = [] + for line in f: + f = line.find('#') #strip comments + if f != -1: + line = line[:f] + f = line.find('=') #I don't know what = means, but binutils strips it + if f != -1: + line = line[:f] + line = line.strip() #strip whitespace + if line.startswith('include ') or line.startswith('include\t'): + includeglob = line[8:].strip() + if not includeglob.startswith(os.path.sep): + includeglob = os.path.join(confdir,includeglob) + import glob + for fname in glob.glob(includeglob): + dirs.extend(parseconf(fname)) + elif line.startswith(os.path.sep): + dirs.append(line) + else: + dirs.append(os.path.join(confdir,line)) + return dirs + +def getlinuxdirs(): + dirs = ['/lib','/usr/lib'] + dirs.extend(parseconf('/etc/ld.so.conf')) + return dirs + +def makepathstring(L): + return ':'.join(L) + +def addldpaths(dirs): + import os + paths = getlinuxdirs() + paths.extend(dirs) + os.environ['LD_LIBRARY_PATH'] = makepathstring(paths) + +if __name__ == "__main__": + import os + import sys + base = os.environ['SUGAR_BUNDLE_PATH'] + print("The base path is %s" % base) + sys.path.append(os.path.join(base,'usr/lib/python2.5/site-packages')) + addldpaths([os.path.join(base,'usr/lib')]) #Sets LD_LIBRARY_PATH + os.environ['PYTHONPATH'] = ':'.join(sys.path) #Sets PYTHONPATH + os.environ['PATH'] += ":%s" % os.path.join(base,'usr/bin') #Extend PATH + + import subprocess + subprocess.Popen(sys.argv[1:]) #Inherits the environment variables diff --git a/ldshim_example.py b/ldshim_example.py new file mode 100755 index 0000000..6ee0213 --- /dev/null +++ b/ldshim_example.py @@ -0,0 +1,3 @@ +#!/usr/bin/env python +import gtkvnc +print gtkvnc.__file__ diff --git a/setup.py b/setup.py new file mode 100755 index 0000000..876cd3f --- /dev/null +++ b/setup.py @@ -0,0 +1,22 @@ +#!/usr/bin/python + +# Copyright (C) 2006, Red Hat, Inc. +# +# 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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + +from sugar.activity import bundlebuilder + +bundlebuilder.start() + diff --git a/watchme.py b/watchme.py new file mode 100644 index 0000000..38ecd8a --- /dev/null +++ b/watchme.py @@ -0,0 +1,182 @@ +# Copyright (C) 2007, Eduardo Silva . +# Copyright (C) 2008, One Laptop Per Child +# Copyright (C) 2009, Ben Schwartz +# +# 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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + +import os + +import logging +from gettext import gettext + +import gtk +import dbus + +from sugar.activity import activity +import os.path + +import telepathy +import subprocess + +import signal + +SERVICE = 'org.sugarlabs.WatchMe' +IFACE = SERVICE +PATH = '/org/sugarlabs/WatchMe' + +class WatchMeActivity(activity.Activity): + + def __init__(self, handle): + activity.Activity.__init__(self, handle) + + self._logger = logging.getLogger('watchme-activity') + self._logger.debug('Starting the WatchMe activity') + + self.set_title(gettext('WatchMe Activity')) + + self._vncdaemon = None + self._vncviewer = None + + if not self._shared_activity: #I am the initiator + toolbox = activity.ActivityToolbox(self) + activity_toolbar = toolbox.get_activity_toolbar() + activity_toolbar.keep.props.visible = False + + self.set_toolbox(toolbox) + toolbox.show() + + label = gtk.Label(gettext("If you want people to be able to see everything on your screen, invite them to this activity")) + + self._connected = True + # The initiator is "connected" without actually connecting to the VNC + # server, because they are looking at the X server. + self.connect('shared', self._shared_cb) + else: # I am joining + #A GUI is not created for a joiner. Hopefully, the vncviewer window + #will be the first window created by the activity, and so will get + #a working icon, etc. + label = gtk.Label(gettext("Please wait while you are connected to the shared session")) + self._connected = False + if self.get_shared(): #Already joined for some reason + self._joined_cb() + else: + self.connect('joined', self._joined_cb) + self.set_canvas(label) + self.show_all() + + def _sharing_setup(self): + params = {} #could be used in the future to indicate a reflector + + bundle_path = activity.get_bundle_path() + port = 5900 + # Start a VNC daemon at an automatically located free TCP port, + # scaling down by a factor of 2 with no blending (for efficiency) + # allowing viewers to view only, not control the cursor or keyboard + # allowing multiple viewers to join + # continue running indefinitely + # allow connections only from localhost + self._vncdaemon = subprocess.Popen(['x11vnc', #search the PATH + '-autoport',str(port), + '-viewonly', + '-shared', + '-forever', + '-localhost'], stdout=subprocess.PIPE) + + # When run with -autoport, x11vnc finds an open port, opens it, and + # prints a line of the form + # PORT=5972 + # to stdout. + x = self._vncdaemon.stdout.readline() + success = 'PORT=' + if x[:len(success)] == success: + port = int(x[len(success):]) + logging.debug('started VNC daemon successfully on port %d' % port) + else: + self._vncdaemon.terminate() + self._vncdaemon.wait() + self._logger.error('unable to find an open port!') + self.close() + return (params, port) + + def _shared_cb(self, activity): + self._logger.debug('My activity was shared') + self.initiating = True + (params, port) = self._sharing_setup() + + self._logger.debug('This is my activity: making a tube...') + + address = ('127.0.0.1', dbus.UInt16(port)) + + tubes_chan = self._shared_activity.telepathy_tubes_chan + id = tubes_chan[telepathy.CHANNEL_TYPE_TUBES].OfferStreamTube( + SERVICE, params, telepathy.SOCKET_ADDRESS_TYPE_IPV4, address, + telepathy.SOCKET_ACCESS_CONTROL_LOCALHOST, 0) + + def _joined_cb(self, also_self): + tubes_chan = self._shared_activity.telepathy_tubes_chan + + tubes_chan[telepathy.CHANNEL_TYPE_TUBES].connect_to_signal('NewTube', + self._new_tube_cb) + tubes_chan[telepathy.CHANNEL_TYPE_TUBES].ListTubes( + reply_handler=self._list_tubes_reply_cb, + error_handler=self._list_tubes_error_cb) + + def _new_tube_cb(self, tube_id, initiator, tube_type, service, params, state): + self._logger.debug('New Tube') + if ((tube_type == telepathy.TUBE_TYPE_STREAM) and + (service == SERVICE) and (not self._connected)): + tubes_chan = self._shared_activity.telepathy_tubes_chan + iface = tubes_chan[telepathy.CHANNEL_TYPE_TUBES] + addr = iface.AcceptStreamTube(tube_id, + telepathy.SOCKET_ADDRESS_TYPE_IPV4, + telepathy.SOCKET_ACCESS_CONTROL_LOCALHOST, 0) + + port = int(addr[1]) + # additional properties are available from params + + ## Start a VNC viewer pointed at the appropriate port. + #self._vncviewer = subprocess.Popen(['/usr/bin/vncviewer', + # 'localhost::%d' % port, + # '-ViewOnly', + # '-Shared']) + import gtkvnc + vncwidget = gtkvnc.Display() + self.set_canvas(vncwidget) + vncwidget.realize() # I don't know what this does + vncwidget.open_host('localhost',str(port)) + self._connected = True + self.show_all() + + def can_close(self): + #if self._vncviewer is not None: + # try: + # os.kill(self._vncviewer.pid,signal.SIGTERM) + # except: + # pass + # #self._vncviewer.terminate() #requires python 2.6 + if self._vncdaemon is not None: + try: + os.kill(self._vncdaemon.pid,signal.SIGTERM) + except: + pass + #self._vncdaemon.terminate() #requires python 2.6 + return True + + def _list_tubes_reply_cb(self, tubes): + for tube_info in tubes: + self._new_tube_cb(*tube_info) + + def _list_tubes_error_cb(self, e): + self._logger.error('ListTubes() failed: %s' % e) -- cgit v0.9.1