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