Ticket #1659: 0001-sugar-emulator-use-vnc-instead-of-Xephyr-1659.patch
File 0001-sugar-emulator-use-vnc-instead-of-Xephyr-1659.patch, 10.3 KB (added by tomeu, 14 years ago) |
---|
-
src/jarabe/util/emulator.py
From 999584c965f9f4d95f4e44dfd2f4c5d9bbb8669e Mon Sep 17 00:00:00 2001 From: Sascha Silbe <sascha@silbe.org> Date: Tue, 6 Jul 2010 12:09:36 +0200 Subject: [PATCH] sugar-emulator: use vnc instead of Xephyr (#1659) Use VNC4 instead of Xephyr so non-US keyboards work fine. On Ubuntu it requires lsb_release for detecting Ubuntu so it can use a workaround for Ubuntu bug #110263. The workaround breaks other systems so we really need it to be conditional. Tested on: - Debian squeeze - Ubuntu Intrepid - Ubuntu Jaunty - Fedora 12 Signed-off-by: Sascha Silbe <sascha@silbe.org> --- src/jarabe/util/emulator.py | 233 ++++++++++++++++++++++++++++-------------- 1 files changed, 155 insertions(+), 78 deletions(-) diff --git a/src/jarabe/util/emulator.py b/src/jarabe/util/emulator.py index 5a99dbe..31c354c 100644
a b 14 14 # along with this program; if not, write to the Free Software 15 15 # Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA 16 16 17 import errno 17 18 import os 18 19 import signal 19 20 import subprocess … … import time 22 23 from optparse import OptionParser 23 24 24 25 import gtk 25 import gobject26 26 27 27 from sugar import env 28 28 29 DEFAULT_DIMENSIONS = (800, 600) 30 _DEV_NULL = open(os.devnull, 'w') 29 31 30 32 ERROR_NO_DISPLAY = 30 31 33 ERROR_NO_SERVER = 31 32 34 33 34 default_dimensions = (800, 600) 35 def _run_xephyr(display, dpi, dimensions, fullscreen): 36 cmd = [ 'Xephyr' ] 37 cmd.append(':%d' % display) 38 cmd.append('-ac') 39 35 server = None 36 37 def _run_pipe(command, stdin=None): 38 """Run a program with optional input and return output. 39 40 Will raise CalledProcessError if program exits with non-zero return 41 code. 42 """ 43 pipe = subprocess.Popen(command, close_fds=True, stdin=subprocess.PIPE, 44 stdout=subprocess.PIPE) 45 stdout, stderr_ = pipe.communicate(stdin) 46 if pipe.returncode: 47 raise subprocess.CalledProcessError(pipe.returncode, command) 48 49 return stdout 50 51 52 def _get_distro(): 53 """Run lsb_release to get distribution name. 54 Return None if distribution name cannot be determined. 55 """ 56 try: 57 distro = ''.join(_run_pipe(['lsb_release', '-is'])).strip() 58 except subprocess.CalledProcessError: 59 return None 60 61 return distro or None 62 63 64 def _run_xauth(display): 65 """Set up Xauthority file for new display. 66 67 Returns name of Xauthority file.""" 68 # pylint: disable-msg=E1103,W0612 69 xauth_file = os.environ.get('XAUTHORITY', 70 os.path.expanduser('~/.Xauthority')) 71 host = _run_pipe(['uname', '-n']).strip() 72 cookie = _run_pipe(['mcookie']).strip() 73 xauth_pipe = subprocess.Popen(['xauth', '-f', xauth_file], 74 stdin=subprocess.PIPE, close_fds=True) 75 xauth_pipe.communicate('add %(host)s:%(display)s . %(cookie)s\n' 76 'add %(host)s/unix:%(display)s . %(cookie)s\n' % locals()) 77 return xauth_file 78 79 80 def _run_server(display, dpi, dimensions, fullscreen): 81 """Start the X server.""" 40 82 screen_size = (gtk.gdk.screen_width(), gtk.gdk.screen_height()) 41 83 42 84 if (not dimensions) and (fullscreen is None) and \ 43 (screen_size < default_dimensions) : 44 # no forced settings, screen too small => fit screen 45 fullscreen = True 46 elif (not dimensions) : 47 # screen is big enough or user has en/disabled fullscreen manually 48 # => use default size (will get ignored for fullscreen) 49 dimensions = '%dx%d' % default_dimensions 50 51 if not dpi : 85 (screen_size < DEFAULT_DIMENSIONS): 86 dimensions = '%dx%d' % screen_size 87 elif fullscreen: 88 dimensions = '%dx%d' % screen_size 89 elif not dimensions: 90 dimensions = '%dx%d' % DEFAULT_DIMENSIONS 91 92 if not dpi: 52 93 dpi = gtk.settings_get_default().get_property('gtk-xft-dpi') / 1024 53 94 54 if fullscreen : 55 cmd.append('-fullscreen') 56 57 if dimensions : 58 cmd.append('-screen') 59 cmd.append(dimensions) 60 61 if dpi : 62 cmd.append('-dpi') 63 cmd.append('%d' % dpi) 64 65 cmd.append('-noreset') 66 67 try: 68 pipe = subprocess.Popen(cmd) 69 70 except OSError, exc: 71 sys.stderr.write('Error executing server: %s\n' % (exc, )) 72 return None 73 95 xauth_file = _run_xauth(display) 96 command = ['Xvnc', '-DisconnectClients', '-NeverShared', '-localhost', 97 '-SecurityTypes', 'None', '-auth', xauth_file] 98 if dimensions: 99 command.append('-geometry') 100 command.append(dimensions) 101 if dpi: 102 command.append('-dpi') 103 command.append('%d' % dpi) 104 105 if _get_distro() == 'Ubuntu': 106 # workaround for Ubuntu bug #110263 107 command += ['-extension', 'XFIXES'] 108 109 command.append(':%d' % (display, )) 110 pipe = subprocess.Popen(command, close_fds=True) 74 111 return pipe 75 112 76 113 77 def _check_server(display):78 result = subprocess.call(['xdpyinfo', '-display', ':%d' % display],79 stdout=open(os.devnull, "w"),80 stderr=open(os.devnull, "w"))81 return result == 082 83 84 114 def _kill_pipe(pipe): 85 """Terminate and wait for child process ."""115 """Terminate and wait for child process (if any).""" 86 116 try: 87 117 os.kill(pipe.pid, signal.SIGTERM) 88 except OSError: 89 pass 90 91 pipe.wait() 118 except OSError, exception: 119 if exception.errno != errno.ESRCH: 120 raise 92 121 93 94 def _start_xephyr(dpi, dimensions, fullscreen): 122 try: 123 pipe.wait() 124 except OSError, exception: 125 if exception.errno != errno.ECHILD: 126 raise 127 128 129 def _wait_pipe(pipe): 130 """Wait for pipe to finish. 131 132 Retries on EINTR to work around <http://bugs.python.org/issue1068268>. 133 """ 134 while pipe.returncode is None: 135 try: 136 pipe.wait() 137 except OSError, exception: 138 if exception.errno != errno.EINTR: 139 raise 140 141 142 def _start_server(dpi, dimensions, fullscreen): 143 """Try running the X server on a free display.""" 95 144 for display in range(30, 40): 96 145 if not _check_server(display): 97 pipe = _run_ xephyr(display, dpi, dimensions, fullscreen)146 pipe = _run_server(display, dpi, dimensions, fullscreen) 98 147 if pipe is None: 99 148 return None, None 100 149 101 150 for i_ in range(10): 102 151 if _check_server(display): 103 return pipe, display152 return display, pipe 104 153 105 154 time.sleep(0.1) 106 155 … … def _start_xephyr(dpi, dimensions, fullscreen): 109 158 return None, None 110 159 111 160 112 def _start_window_manager(): 113 cmd = ['metacity'] 161 def _start_viewer(display, fullscreen): 162 """Start the VNC viewer.""" 163 command = ['vncviewer'] 164 if fullscreen: 165 command.append('-fullscreen') 166 167 command.append(':%d' % (display, )) 168 pipe = subprocess.Popen(command, close_fds=True) 169 return pipe 170 114 171 115 cmd.extend(['--no-force-fullscreen']) 172 def _check_server(display): 173 """Check the X server on the given display is reachable.""" 174 result = subprocess.call(['xdpyinfo', '-display', ':%d' % (display, )], 175 stdout=_DEV_NULL, stderr=_DEV_NULL) 176 return result == 0 116 177 117 gobject.spawn_async(cmd, flags=gobject.SPAWN_SEARCH_PATH)118 178 119 def _setup_env(display, scaling, emulator_pid): 179 def _start_window_manager(): 180 """Start the window manager inside the new X server.""" 181 command = ['metacity', '--no-force-fullscreen'] 182 pipe_ = subprocess.Popen(command) 183 184 185 def _setup_env(display, scaling): 186 """Set up environment variables for running Sugar inside the new X server. 187 """ 120 188 os.environ['SUGAR_EMULATOR'] = 'yes' 121 189 os.environ['GABBLE_LOGFILE'] = os.path.join( 122 190 env.get_profile_path(), 'logs', 'telepathy-gabble.log') … … def _setup_env(display, scaling, emulator_pid): 125 193 os.environ['STREAM_ENGINE_LOGFILE'] = os.path.join( 126 194 env.get_profile_path(), 'logs', 'telepathy-stream-engine.log') 127 195 os.environ['DISPLAY'] = ":%d" % (display) 128 os.environ['SUGAR_EMULATOR_PID'] = emulator_pid129 196 130 197 if scaling: 131 198 os.environ['SUGAR_SCALING'] = scaling 132 199 133 def main(): 134 """Script-level operations""" 135 200 def _parse_args(): 201 """Parse command line arguments.""" 136 202 parser = OptionParser() 137 203 parser.add_option('-d', '--dpi', dest='dpi', type="int", 138 204 help='Emulator dpi') … … def main(): 146 212 parser.add_option('-F', '--no-fullscreen', dest='fullscreen', 147 213 action='store_false', 148 214 help='Do not run emulator in fullscreen mode') 149 (options, args) =parser.parse_args()215 return parser.parse_args() 150 216 151 if not os.environ.get('DISPLAY'):152 sys.stderr.write('DISPLAY not set, cannot connect to host X server.\n')153 return ERROR_NO_DISPLAY154 217 155 server, display = _start_xephyr(options.dpi, options.dimensions, 156 options.fullscreen) 157 if server is None: 158 sys.stderr.write('Failed to start server. Please check output above' 159 ' for any error message.\n') 160 return ERROR_NO_SERVER 218 def _sigchld_handler(number_, frame_): 219 """Kill server when any (direct) child exits. 161 220 162 _setup_env(display, options.scaling, str(server.pid)) 221 So closing the viewer will close the server as well.""" 222 if server.returncode is not None: 223 return 224 225 signal.signal(signal.SIGCHLD, signal.SIG_DFL) 226 227 print 'sugar-emulator: Child exited, shutting down server' 228 _kill_pipe(server) 229 return 230 231 def main(): 232 """Script-level operations""" 233 global server 234 235 options, args = _parse_args() 236 display, server = _start_server(options.dpi, options.dimensions, 237 options.fullscreen) 238 viewer = _start_viewer(display, options.fullscreen) 239 _setup_env(display, options.scaling) 163 240 164 241 command = ['dbus-launch', '--exit-with-session'] 165 242 … … def main(): 167 244 command.append('sugar') 168 245 else: 169 246 _start_window_manager() 247 command += args 170 248 171 if args[0].endswith('.py'): 172 command.append('python') 173 174 command.append(args[0]) 249 signal.signal(signal.SIGCHLD, _sigchld_handler) 250 session = subprocess.Popen(command, close_fds=True) 251 _wait_pipe(session) 175 252 176 subprocess.call(command)253 _kill_pipe(viewer) 177 254 _kill_pipe(server)