From 0763f5bc42836089027f578d43701be2e2df98d1 Mon Sep 17 00:00:00 2001 From: Dan Winship Date: Mon, 23 Feb 2009 14:52:49 -0500 Subject: [PATCH] Replace start-in-Xephyr/start-replace with a new single "gnome-shell" script The new script runs in "replace" mode if you pass --replace, or if there isn't already a panel running, and in "Xephyr" mode otherwise. --- scripts/launcher.py | 136 --------------------- scripts/start-in-Xephyr | 63 ---------- scripts/start-replace | 61 ---------- src/gnome-shell | 256 ++++++++++++++++++++++++++++++++++++++++ 4 files changed, 256 insertions(+), 260 deletions(-) delete mode 100644 scripts/launcher.py delete mode 100755 scripts/start-in-Xephyr delete mode 100755 scripts/start-replace create mode 100755 src/gnome-shell diff --git a/scripts/launcher.py b/scripts/launcher.py deleted file mode 100644 index 55b31d85f..000000000 --- a/scripts/launcher.py +++ /dev/null @@ -1,136 +0,0 @@ -from optparse import OptionParser -import os -import re -import subprocess -import sys - -GLXINFO_RE = re.compile(r"^(\S.*):\s*\n((?:^\s+.*\n)*)", re.MULTILINE) - -def _get_glx_extensions(): - """Return a tuple of server, client, and effective GLX extensions""" - - glxinfo = subprocess.Popen(["glxinfo"], stdout=subprocess.PIPE) - glxinfo_output = glxinfo.communicate()[0] - glxinfo.wait() - - glxinfo_map = {} - for m in GLXINFO_RE.finditer(glxinfo_output): - glxinfo_map[m.group(1)] = m.group(2) - - server_glx_extensions = set(re.split("\s*,\s*", glxinfo_map['server glx extensions'].strip())) - client_glx_extensions = set(re.split("\s*,\s*", glxinfo_map['client glx extensions'].strip())) - glx_extensions = set(re.split("\s*,\s*", glxinfo_map['GLX extensions'].strip())) - - return (server_glx_extensions, client_glx_extensions, glx_extensions) - -class Launcher: - def __init__(self, use_tfp=True, accept_geometry=False): - self.use_tfp = use_tfp - - # Figure out the path to the plugin when uninstalled - scripts_dir = os.path.dirname(os.path.abspath(sys.argv[0])) - top_dir = os.path.dirname(scripts_dir) - self.plugin_dir = os.path.join(top_dir, "src") - self.js_dir = os.path.join(top_dir, "js") - self.data_dir = os.path.join(top_dir, "data") - - parser = OptionParser() - parser.add_option("-g", "--debug", action="store_true", - help="Run under a debugger") - parser.add_option("", "--debug-command", metavar="COMMAND", - help="Command to use for debugging (defaults to 'gdb --args')") - parser.add_option("-v", "--verbose", action="store_true") - parser.add_option("" "--sync", action="store_true") - if accept_geometry: - parser.add_option("", "--geometry", metavar="GEOMETRY", - help="Specify screen geometry", - default="1024x768"); - parser.add_option("-w", "--wide", action="store_true", - help="Use widescreen (1280x800)") - - self.options, args = parser.parse_args() - - if args: - parser.print_usage() - sys.exit(1) - - if self.options.debug_command: - self.options.debug = True - self.debug_command = self.options.debug_command.split() - else: - self.debug_command = ["gdb", "--args"] - - if accept_geometry and self.options.wide: - self.options.geometry = "1280x800" - - def start_shell(self): - """Starts gnome-shell. Returns a subprocess.Popen object""" - - use_tfp = self.use_tfp - force_indirect = False - - # Allow disabling usage of the EXT_texture_for_pixmap extension. - # FIXME: Move this to ClutterGlxPixmap like - # CLUTTER_PIXMAP_TEXTURE_RECTANGLE=disable. - if 'GNOME_SHELL_DISABLE_TFP' in os.environ and \ - os.environ['GNOME_SHELL_DISABLE_TFP'] != '': - use_tfp = False - - if use_tfp: - # Decide if we need to set LIBGL_ALWAYS_INDIRECT=1 to get the texture_from_pixmap - # extension; we take having the extension be supported on both the client and - # server but not in the list of effective extensions as a signal of needing - # to force indirect rendering. - # - # (The Xepyhr DRI support confuses this heuristic but we disable TFP in - # start-in-Xepyhr) - # - (server_glx_extensions, client_glx_extensions, glx_extensions) = _get_glx_extensions() - - if ("GLX_EXT_texture_from_pixmap" in server_glx_extensions and - "GLX_EXT_texture_from_pixmap" in client_glx_extensions and - (not "GLX_EXT_texture_from_pixmap" in glx_extensions)): - - force_indirect = True - - # Now launch metacity-clutter with our plugin - env=dict(os.environ) - env.update({'GNOME_SHELL_JS' : self.js_dir, - 'GNOME_SHELL_DATADIR' : self.data_dir, - 'GI_TYPELIB_PATH' : self.plugin_dir, - 'PATH' : os.environ.get('PATH', '') + ':' + self.plugin_dir, - 'LD_LIBRARY_PATH' : os.environ.get('LD_LIBRARY_PATH', '') + ':' + self.plugin_dir, - 'GNOME_DISABLE_CRASH_DIALOG' : '1'}) - - if force_indirect: - if self.options.verbose: - print "Forcing indirect GL" - - # This is Mesa specific; the NVIDIA proprietary drivers drivers use - # __GL_FORCE_INDIRECT=1 instead. But we don't need to force indirect - # rendering for NVIDIA. - env['LIBGL_ALWAYS_INDIRECT'] = '1' - - if not self.options.verbose: - # Unless verbose() is specified, only let gjs show errors and things that are - # explicitly logged via log() from javascript - env['GJS_DEBUG_TOPICS'] = 'JS ERROR;JS LOG' - - if self.options.debug: - args = list(self.debug_command) - else: - args = [] - - plugin = os.path.join(self.plugin_dir, "libgnome-shell.la") - args.extend(['metacity', '--mutter-plugins=' + plugin, '--replace']) - if self.options.sync: - args.append('--sync') - return subprocess.Popen(args, env=env) - - def is_verbose(self): - """Returns whether the Launcher was started in verbose mode""" - return self.options.verbose - - def get_geometry(self): - """Returns the command-line specified geometry""" - return self.options.geometry diff --git a/scripts/start-in-Xephyr b/scripts/start-in-Xephyr deleted file mode 100755 index 8a04d9c02..000000000 --- a/scripts/start-in-Xephyr +++ /dev/null @@ -1,63 +0,0 @@ -#!/usr/bin/python - -import os -import random -import shutil -import signal -import subprocess -import sys -import tempfile -import time - -from launcher import Launcher - -# GL_EXT_texture_from_pixmap doesn't work in Xepyhr -launcher = Launcher(use_tfp=False, accept_geometry=True) - -# Temporary directory to hold our X credentials -tmpdir = tempfile.mkdtemp("", "gnome-shell.") -try: - display = ":" + str(random.randint(10, 99)) - xauth_file = os.path.join(tmpdir, "database") - - # Create a random 128-byte key and format it as hex - f = open("/dev/urandom", "r") - key = f.read(16) - f.close() - hexkey = "".join(("%02x" % ord(byte) for byte in key)) - - # Store that in an xauthority file as the key for connecting to our Xephyr - retcode = subprocess.call(["xauth", - "-f", xauth_file, - "add", display, "MIT-MAGIC-COOKIE-1", hexkey]) - if retcode != 0: - raise RuntimeError("xauth failed") - - # Launch Xephyr - xephyr = subprocess.Popen(["Xephyr", display, - "-auth", xauth_file, - "-screen", launcher.get_geometry(), - "-host-cursor"]) - os.environ['DISPLAY'] = display - os.environ['XAUTHORITY'] = xauth_file - - # Wait for server to get going: LAME - time.sleep(1) - - # Start some windows in our session. Specify explicit geometries - # so we don't end up with the title bars underneath the panel - subprocess.Popen(["xterm", "-geometry", "+30+30"]) - subprocess.Popen(["xlogo", "-geometry", "-0-0"]) - subprocess.Popen(["xeyes", "-geometry", "-0+30"]) - - # Now launch metacity-clutter with our plugin - shell = launcher.start_shell() - - # Wait for Xephyr to exit - try: - retcode = xephyr.wait() - except KeyboardInterrupt, e: - os.kill(xephyr.pid, signal.SIGKILL) -finally: - shutil.rmtree(tmpdir) - diff --git a/scripts/start-replace b/scripts/start-replace deleted file mode 100755 index af914aa2a..000000000 --- a/scripts/start-replace +++ /dev/null @@ -1,61 +0,0 @@ -#!/usr/bin/python - -import os -import subprocess -import signal - -from launcher import Launcher - -launcher = Launcher() - -def find_cmd (cmd_list): - """ - Takes a list of command candidates and returns the first one that exists. - Raises a system exit if none of the commands exist. - """ - for cmd in cmd_list: - if os.path.exists(cmd): - return cmd - - raise SystemExit("None of the commands %s exist" % cmd_list) - -try: - # Kill gnome-panel in a way that it won't autorespawn - pidof_cmd = find_cmd(["/sbin/pidof", "/bin/pidof", "/usr/bin/pidof"]) - - pidof = subprocess.Popen([pidof_cmd, "gnome-panel"], stdout=subprocess.PIPE) - pids = pidof.communicate()[0].split() - pidof.wait() - devnull = open("/dev/null", "w") - for pid in pids: - if launcher.is_verbose(): - print "Terminating panel process %s" % pid - subprocess.call(["gdb", "-batch-silent", - "-ex", "call panel_session_do_not_restart()", - "-ex", "call exit(0)", - "-p", pid], stdout=devnull, stderr=devnull) - devnull.close() - - if launcher.is_verbose(): - print "Starting shell" - shell = launcher.start_shell() - - # Wait for shell to exit - try: - if launcher.is_verbose(): - print "Waiting for shell to exit" - shell.wait() - if launcher.is_verbose(): - print "Shell is dead" - except KeyboardInterrupt, e: - os.kill(shell.pid, signal.SIGKILL) - shell.wait() - if launcher.is_verbose(): - print "Shell killed" -finally: - # Restart gnome-panel and window manager - if launcher.is_verbose(): - print "Restarting Metacity and Gnome Panel" - - subprocess.Popen(["/usr/bin/metacity"]) - subprocess.Popen(["/usr/bin/gnome-panel"]) diff --git a/src/gnome-shell b/src/gnome-shell new file mode 100755 index 000000000..3da8748f1 --- /dev/null +++ b/src/gnome-shell @@ -0,0 +1,256 @@ +#!/usr/bin/python + +import atexit +import optparse +import os +import random +import re +import shutil +import signal +import subprocess +import sys +import tempfile +import time + +def find_cmd (cmd_list): + """ + Takes a list of command candidates and returns the first one that exists. + Raises a system exit if none of the commands exist. + """ + for cmd in cmd_list: + if os.path.exists(cmd): + return cmd + + raise SystemExit("None of the commands %s exist" % cmd_list) + +def pidof(command): + pidof_cmd = find_cmd(["/sbin/pidof", "/bin/pidof", "/usr/bin/pidof"]) + pidof = subprocess.Popen([pidof_cmd, command], stdout=subprocess.PIPE) + pids = pidof.communicate()[0].split() + pidof.wait() + + # pidof doesn't have a "current user only" option, so we may have + # gotten the pids of other users' processes. Fix that. + for pid in pids: + try: + os.kill(int(pid), 0) + return pid + except Exception, e: + pass + return None + +def kill_gnome_panel(pid): + if options.verbose: + print "Terminating panel process %s" % pid + devnull = open("/dev/null", "w") + subprocess.call(["gdb", "-batch-silent", + "-ex", "call panel_session_do_not_restart()", + "-ex", "call exit(0)", + "-p", pid], stdout=devnull, stderr=devnull) + devnull.close() + +def start_xephyr(): + tmpdir = tempfile.mkdtemp("", "gnome-shell.") + atexit.register(shutil.rmtree, tmpdir) + + display = ":" + str(random.randint(10, 99)) + xauth_file = os.path.join(tmpdir, "database") + + # Create a random 128-bit key and format it as hex + f = open("/dev/urandom", "r") + key = f.read(16) + f.close() + hexkey = "".join(("%02x" % ord(byte) for byte in key)) + + # Store that in an xauthority file as the key for connecting to our Xephyr + retcode = subprocess.call(["xauth", + "-f", xauth_file, + "add", display, "MIT-MAGIC-COOKIE-1", hexkey]) + if retcode != 0: + raise RuntimeError("xauth failed") + + # Launch Xephyr + xephyr = subprocess.Popen(["Xephyr", display, + "-auth", xauth_file, + "-screen", options.geometry, + "-host-cursor"]) + os.environ['DISPLAY'] = display + os.environ['XAUTHORITY'] = xauth_file + + # Wait for server to get going: LAME + time.sleep(1) + + # Start some windows in our session. + subprocess.Popen(["xterm", "-geometry", "+30+30"]) + subprocess.Popen(["xlogo", "-geometry", "-0-0"]) + subprocess.Popen(["xeyes", "-geometry", "-0+30"]) + + return xephyr; + +GLXINFO_RE = re.compile(r"^(\S.*):\s*\n((?:^\s+.*\n)*)", re.MULTILINE) + +def _get_glx_extensions(): + """Return a tuple of server, client, and effective GLX extensions""" + + glxinfo = subprocess.Popen(["glxinfo"], stdout=subprocess.PIPE) + glxinfo_output = glxinfo.communicate()[0] + glxinfo.wait() + + glxinfo_map = {} + for m in GLXINFO_RE.finditer(glxinfo_output): + glxinfo_map[m.group(1)] = m.group(2) + + server_glx_extensions = set(re.split("\s*,\s*", glxinfo_map['server glx extensions'].strip())) + client_glx_extensions = set(re.split("\s*,\s*", glxinfo_map['client glx extensions'].strip())) + glx_extensions = set(re.split("\s*,\s*", glxinfo_map['GLX extensions'].strip())) + + return (server_glx_extensions, client_glx_extensions, glx_extensions) + +def start_shell(): + # Figure out the path to the plugin when uninstalled + src_dir = os.path.dirname(os.path.abspath(sys.argv[0])) + top_dir = os.path.dirname(src_dir) + plugin_dir = os.path.join(top_dir, "src") + js_dir = os.path.join(top_dir, "js") + data_dir = os.path.join(top_dir, "data") + + force_indirect = False + if use_tfp: + # Decide if we need to set LIBGL_ALWAYS_INDIRECT=1 to get the + # texture_from_pixmap extension; we take having the extension + # be supported on both the client and server but not in the + # list of effective extensions as a signal of needing to force + # indirect rendering. + # + # Note that this check would give the wrong answer for Xephyr, + # but since we force !use_tfp there anyway, it doesn't matter. + (server_glx_extensions, client_glx_extensions, glx_extensions) = _get_glx_extensions() + + if ("GLX_EXT_texture_from_pixmap" in server_glx_extensions and + "GLX_EXT_texture_from_pixmap" in client_glx_extensions and + (not "GLX_EXT_texture_from_pixmap" in glx_extensions)): + if options.verbose: + print "Forcing indirect GL" + force_indirect = True + + # Now launch metacity-clutter with our plugin + env = dict(os.environ) + env.update({'GNOME_SHELL_JS' : js_dir, + 'GNOME_SHELL_DATADIR' : data_dir, + 'GI_TYPELIB_PATH' : plugin_dir, + 'PATH' : os.environ.get('PATH', '') + ':' + plugin_dir, + 'LD_LIBRARY_PATH' : os.environ.get('LD_LIBRARY_PATH', '') + ':' + plugin_dir, + 'GNOME_DISABLE_CRASH_DIALOG' : '1'}) + + if force_indirect: + if options.verbose: + # This is Mesa specific; the NVIDIA proprietary drivers + # drivers use __GL_FORCE_INDIRECT=1 instead. But we don't + # need to force indirect rendering for NVIDIA. + env['LIBGL_ALWAYS_INDIRECT'] = '1' + + if not options.verbose: + # Unless verbose() is specified, only let gjs show errors and + # things that are explicitly logged via log() from javascript + env['GJS_DEBUG_TOPICS'] = 'JS ERROR;JS LOG' + + if options.debug: + debug_command = options.debug_command.split() + args = list(debug_command) + else: + args = [] + + plugin = os.path.join(plugin_dir, "libgnome-shell.la") + args.extend(['metacity', '--mutter-plugins=' + plugin, '--replace']) + if options.sync: + args.append('--sync') + return subprocess.Popen(args, env=env) + + +# Main program + +parser = optparse.OptionParser() +parser.add_option("-r", "--replace", action="store_true", + help="Replace the running metacity/gnome-panel") +parser.add_option("-g", "--debug", action="store_true", + help="Run under a debugger") +parser.add_option("", "--debug-command", metavar="COMMAND", + help="Command to use for debugging (defaults to 'gdb --args')") +parser.add_option("-v", "--verbose", action="store_true") +parser.add_option("", "--sync", action="store_true") +parser.add_option("", "--geometry", metavar="GEOMETRY", + help="Specify Xephyr screen geometry", + default="1024x768"); +parser.add_option("-w", "--wide", action="store_true", + help="Use widescreen (1280x800) with Xephyr") + +options, args = parser.parse_args() + +if args: + parser.print_usage() + sys.exit(1) + +if options.debug_command: + options.debug = True +elif options.debug: + options.debug_command = "gdb --args" + +if options.wide: + options.geometry = "1280x800" + +metacity_pid = pidof("metacity") +gnome_panel_pid = pidof("gnome-panel") + +# Run in Xephyr if gnome-panel is already running and the user didn't +# specify --replace. Otherwise, run fullscreen +if options.replace: + run_in_xephyr = False +else: + run_in_xephyr = (metacity_pid != None or gnome_panel_pid != None) + +# Figure out whether or not to use GL_EXT_texture_from_pixmap. By default +# we use it iff we aren't running Xephyr, but we allow the user to +# explicitly disable it. +# FIXME: Move this to ClutterGlxPixmap like +# CLUTTER_PIXMAP_TEXTURE_RECTANGLE=disable. +if 'GNOME_SHELL_DISABLE_TFP' in os.environ and \ + os.environ['GNOME_SHELL_DISABLE_TFP'] != '': + use_tfp = False +else: + # tfp does not work correctly in Xephyr + use_tfp = not run_in_xephyr + +if options.verbose: + print "Starting shell" + +try: + if run_in_xephyr: + shell = start_xephyr() + start_shell() + else: + kill_gnome_panel(gnome_panel_pid) + shell = start_shell() + + # Wait for shell to exit + if options.verbose: + print "Waiting for shell to exit" + shell.wait() + if options.verbose: + print "Shell is dead" + +except KeyboardInterrupt, e: + os.kill(shell.pid, signal.SIGKILL) + shell.wait() + if options.verbose: + print "Shell killed" + +finally: + if metacity_pid or gnome_panel_pid: + # Restart gnome-panel and window manager + if options.verbose: + print "Restarting Metacity and Gnome Panel" + + if metacity_pid: + subprocess.Popen(["/usr/bin/metacity"]) + if gnome_panel_pid: + subprocess.Popen(["/usr/bin/gnome-panel"])