This patch adds a "standalone" subcommand to xpra.

"xpra standalone COMMAND ARGS..." runs the given command so that the X
display is proxied by xpra.  This is useful for testing.  The xpra
client and server are run within the same process, although they still
communicate through a socket.

I refactored xpra.server to use a gdk.Display object instead of using
Gdk's global default display.  That did not work in all cases.  I had
to disable the handler for _NET_WM_ICON, which caused xpra to fail on
gedit (among others).  It was trying to create a pixmap on the wrong
display.  This is a regression for parti, though this is not used in
xpra.

Mark

============================================================
--- wimpiggy/composite.py       9b35416f3062bc9705cd604eb04b079838063097
+++ wimpiggy/composite.py       c586a5d8cced26465c19ce4b705ed54a4c7fb6f8
@@ -85,7 +85,7 @@ class CompositeHelper(AutoPropGObjectMix
                 # we are safe.  (I think.)
                 listening = []
                 win = get_parent(self._window)
-                while win is not gtk.gdk.get_default_root_window():
+                while win.get_parent() is not None:
                     # We have to use a lowlevel function to manipulate the
                     # event selection here, because SubstructureRedirectMask
                     # does not roundtrip through the GDK event mask
============================================================
--- wimpiggy/window.py  844185278c5ff36fbb7f1d01dfaa038537783739
+++ wimpiggy/window.py  58de424d8490f5ca3382dc5341b8ed38a5d9e540
@@ -649,7 +649,8 @@ class WindowModel(BaseWindowModel):
                                              "_NET_WM_ICON", "icon"))
 
         print "icon is now %r" % (self.get_property("icon"),)
-    _property_handlers["_NET_WM_ICON"] = _handle_net_wm_icon
+    # Causes gedit to fail
+    #_property_handlers["_NET_WM_ICON"] = _handle_net_wm_icon
 
     def _read_initial_properties(self):
         # Things that don't change:
============================================================
--- wimpiggy/wm.py      f708add7d38d8a0260a16a8e0981f1078d3fc233
+++ wimpiggy/wm.py      a07296ee7c2c6c9c4854970ded4ea2d4eb17a5df
@@ -163,6 +163,7 @@ class Wm(gobject.GObject):
 
         # Load up our full-screen widget
         self._world_window = WorldWindow()
+        self._world_window.set_screen(self._display.get_default_screen())
         self.notify("toplevel")
         self._world_window.show_all()
 
============================================================
--- xpra/scripts/main.py        96c85995514a2951685364c3fe43a3997282154d
+++ xpra/scripts/main.py        fb6baf809d6a0bf9d9f9e309c8348d12c43ad814
@@ -1,9 +1,11 @@ import os
 import gobject
 import sys
 import os
+import shutil
 import stat
 import socket
 import subprocess
+import tempfile
 from optparse import OptionParser
 import logging
 
@@ -25,7 +27,8 @@ def main(cmdline):
                                  + "\t%prog attach DISPLAY\n"
                                  + "\t%prog stop DISPLAY\n"
                                  + "\t%prog list\n"
-                                 + "\t%prog upgrade DISPLAY"))
+                                 + "\t%prog upgrade DISPLAY\n"
+                                 + "\t%prog standalone COMMAND ARGS..."))
     parser.add_option("--no-daemon", action="store_false",
                       dest="daemon", default=True,
                       help="Don't daemonize when running as a server")
@@ -37,21 +40,23 @@ def main(cmdline):
     if not args:
         parser.error("need a mode")
 
-    mode = args[0]
+    mode = args.pop(0)
     if mode in ("start", "upgrade"):
         nox()
         from xpra.scripts.server import run_server
-        run_server(parser, options, mode, args[1:])
+        run_server(parser, options, mode, args)
     elif mode == "attach":
-        run_client(parser, options, args[1:])
+        run_client(parser, options, args)
     elif mode == "stop":
         nox()
-        run_stop(parser, options, args[1:])
+        run_stop(parser, options, args)
     elif mode == "list":
-        run_list(parser, options, args[1:])
+        run_list(parser, options, args)
     elif mode == "_proxy":
         nox()
-        run_proxy(parser, options, args[1:])
+        run_proxy(parser, options, args)
+    elif mode == "standalone":
+        run_standalone(parser, args)
     else:
         parser.error("invalid mode '%s'" % mode)
 
@@ -140,3 +145,42 @@ def run_list(parser, opts, extra_args):
                 else:
                     sys.stdout.write(" (cleaned up)")
             sys.stdout.write("\n")
+
+def allocate_display():
+    # TODO: This interacts badly with SSH, which listens on TCP only.
+    # It is also racy.
+    number = 20
+    while os.path.exists("/tmp/.X11-unix/X%i" % number):
+        number += 1
+    return ":%i" % number
+
+def run_standalone(parser, args):
+    if len(args) < 1:
+        parser.error("Expected command argument")
+
+    import gtk.gdk
+    import xpra.client
+    import xpra.scripts.server
+    import xpra.server
+
+    new_display = allocate_display()
+    # TODO: could use socketpair() instead of a named socket
+    temp_dir = tempfile.mkdtemp(prefix="xpra-")
+    try:
+        socket_path = os.path.join(temp_dir, "xpra-socket")
+        xvfb_proc = xpra.scripts.server.start_xvfb(new_display)
+        server_display = gtk.gdk.Display(new_display)
+        server = xpra.server.XpraServer(socket_path, server_display,
+                                        clobber=False)
+        client_socket = socket.socket(socket.AF_UNIX)
+        client_socket.connect(socket_path)
+    finally:
+        shutil.rmtree(temp_dir)
+    client = xpra.client.XpraClient(client_socket)
+    environ = os.environ.copy()
+    environ["DISPLAY"] = new_display
+    proc = subprocess.Popen(args, env=environ)
+    # Run forever until being killed with Ctrl-C.
+    server.run()
+    proc.wait()
+    xvfb_proc.wait()
============================================================
--- xpra/scripts/server.py      49e7513a1dcc5501d405823339fc35b07e3219e6
+++ xpra/scripts/server.py      3a6564cd2ba99d728f2293022fa86f2ea80a2c75
@@ -38,6 +38,22 @@ def get_pid():
     return prop_get(gtk.gdk.get_default_root_window(),
                     "_XPRA_SERVER_PID", "u32")
 
+def start_xvfb(display_name):
+    xauthority = os.environ.get("XAUTHORITY",
+                                os.path.expanduser("~/.Xauthority"))
+    xvfb = subprocess.Popen(["Xvfb-for-Xpra", display_name,
+                             "-auth", xauthority,
+                             "+extension", "Composite",
+                             "-screen", "0", "2048x2048x24+32",
+                             "-once"],
+                            executable="Xvfb")
+    raw_cookie = os.urandom(16)
+    baked_cookie = raw_cookie.encode("hex")
+    rc = subprocess.call(["xauth", "add", display_name,
+                          "MIT-MAGIC-COOKIE-1", baked_cookie])
+    assert rc == 0, rc
+    return xvfb
+
 def run_server(parser, opts, mode, extra_args):
     if len(extra_args) != 1:
         parser.error("need exactly 1 extra argument")
@@ -92,21 +108,8 @@ def run_server(parser, opts, mode, extra
         sys.stderr = os.fdopen(2, "w", 1)
 
     if not upgrading:
-        # We need to set up a new server environment
-        xauthority = os.environ.get("XAUTHORITY",
-                                    os.path.expanduser("~/.Xauthority"))
-        xvfb = subprocess.Popen(["Xvfb-for-Xpra", display_name,
-                                 "-auth", xauthority,
-                                 "+extension", "Composite",
-                                 "-screen", "0", "2048x2048x24+32",
-                                 "-once"],
-                                executable="Xvfb")
+        xvfb = start_xvfb(display_name)
 
-        raw_cookie = os.urandom(16)
-        baked_cookie = raw_cookie.encode("hex")
-        assert not subprocess.call(["xauth", "add", display_name,
-                                    "MIT-MAGIC-COOKIE-1", baked_cookie])
-
     # Whether we spawned our server or not, it is now running, and we can
     # connect to it.
     os.environ["DISPLAY"] = display_name
@@ -133,7 +136,7 @@ def run_server(parser, opts, mode, extra
     if xvfb_pid is not None:
         save_pid(xvfb_pid)
 
-    app = XpraServer(sockpath, upgrading)
+    app = XpraServer(sockpath, display, upgrading)
     def cleanup_socket(self):
         print "removing socket"
         try:
============================================================
--- xpra/server.py      861bf7797065219ac2362db1d666661c46ddd8fd
+++ xpra/server.py      2e654a24ed72a6e9ff260a6a6af6e34a1be039a8
@@ -188,16 +188,19 @@ class XpraServer(gobject.GObject):
         "wimpiggy-child-map-event": one_arg_signal,
         }
 
-    def __init__(self, socketpath, clobber):
+    def __init__(self, socketpath, display, clobber):
         gobject.GObject.__init__(self)
         
+        self._display = display
+        self._screen = display.get_default_screen()
         # Do this before creating the Wm object, to avoid clobbering its
         # selecting SubstructureRedirect.
-        root = gtk.gdk.get_default_root_window()
-        root.set_events(root.get_events() | gtk.gdk.SUBSTRUCTURE_MASK)
-        add_event_receiver(root, self)
+        self._root_window = self._screen.get_root_window()
+        self._root_window.set_events(self._root_window.get_events() |
+                                     gtk.gdk.SUBSTRUCTURE_MASK)
+        add_event_receiver(self._root_window, self)
 
-        self._wm = Wm("Xpra", clobber)
+        self._wm = Wm("Xpra", clobber, display)
         self._wm.connect("new-window", self._new_window_signaled)
         self._wm.connect("quit", lambda _: self.quit(True))
 
@@ -216,7 +219,7 @@ class XpraServer(gobject.GObject):
         for window in self._wm.get_property("windows"):
             self._add_new_window(window)
 
-        for window in get_children(root):
+        for window in get_children(self._root_window):
             if (is_override_redirect(window) and is_mapped(window)):
                 self._add_new_or_window(window)
 
@@ -228,11 +231,14 @@ class XpraServer(gobject.GObject):
         gobject.io_add_watch(self._listener, gobject.IO_IN,
                              self._new_connection)
 
-        self._keymap = gtk.gdk.keymap_get_default()
+        self._keymap = gtk.gdk.keymap_get_for_display(self._display)
         self._keymap.connect("keys-changed", self._keys_changed)
         self._keys_changed()
 
-        xmodmap = subprocess.Popen(["xmodmap", "-"], stdin=subprocess.PIPE)
+        environ = os.environ.copy()
+        environ["DISPLAY"] = display.get_name()
+        xmodmap = subprocess.Popen(["xmodmap", "-"], stdin=subprocess.PIPE,
+                                   env=environ)
         xmodmap.communicate("""clear Lock
                                clear Shift
                                clear Control
@@ -284,7 +290,7 @@ class XpraServer(gobject.GObject):
         return True
 
     def _keys_changed(self, *args):
-        self._modifier_map = grok_modifier_map(gtk.gdk.display_get_default())
+        self._modifier_map = grok_modifier_map(self._display)
 
     def _new_window_signaled(self, wm, window):
         self._add_new_window(window)
@@ -363,15 +369,15 @@ class XpraServer(gobject.GObject):
         return self._keymap.get_entries_for_keyval(keyval)[0][0]
 
     def _make_keymask_match(self, modifier_list):
-        (_, _, current_mask) = gtk.gdk.get_default_root_window().get_pointer()
+        (_, _, current_mask) = self._root_window.get_pointer()
         current = set(mask_to_names(current_mask, self._modifier_map))
         wanted = set(modifier_list)
         for modifier in current.difference(wanted):
-            xtest_fake_key(gtk.gdk.display_get_default(),
+            xtest_fake_key(self._display,
                            self._keycode(self._keyname_for_mod[modifier]),
                            False)
         for modifier in wanted.difference(current):
-            xtest_fake_key(gtk.gdk.display_get_default(),
+            xtest_fake_key(self._display,
                            self._keycode(self._keyname_for_mod[modifier]),
                            True)
 
@@ -387,8 +393,7 @@ class XpraServer(gobject.GObject):
 
     def _move_pointer(self, pos):
         (x, y) = pos
-        display = gtk.gdk.display_get_default()
-        display.warp_pointer(display.get_default_screen(), x, y)
+        self._display.warp_pointer(self._screen, x, y)
 
     def _send(self, packet):
         if self._protocol is not None:
@@ -424,8 +429,7 @@ class XpraServer(gobject.GObject):
             if isinstance(window, OverrideRedirectWindowModel):
                 raw_or_to_id[window.get_property("client-window")] = id
         or_stacking = []
-        root = gtk.gdk.get_default_root_window()
-        for raw_window in get_children(root):
+        for raw_window in get_children(self._root_window):
             if raw_window in raw_or_to_id:
                 or_stacking.append(raw_or_to_id[raw_window])
         if or_stacking:
@@ -528,14 +532,13 @@ class XpraServer(gobject.GObject):
         (_, id, keyname, depressed, modifiers) = packet
         self._make_keymask_match(modifiers)
         self._focus(id)
-        xtest_fake_key(gtk.gdk.display_get_default(),
-                       self._keycode(keyname), depressed)
+        xtest_fake_key(self._display, self._keycode(keyname), depressed)
 
     def _process_button_action(self, proto, packet):
         (_, button, depressed, pointer, modifiers) = packet
         self._make_keymask_match(modifiers)
         self._move_pointer(pointer)
-        xtest_fake_button(gtk.gdk.display_get_default(), button, depressed)
+        xtest_fake_button(self._display, button, depressed)
 
     def _process_pointer_position(self, proto, packet):
         (_, pointer, modifiers) = packet

_______________________________________________
Parti-discuss mailing list
[email protected]
http://lists.partiwm.org/cgi-bin/mailman/listinfo/parti-discuss

Reply via email to