1 | #!/usr/bin/env python |
---|
2 | # Copyright (C) 2006, Red Hat, Inc. |
---|
3 | # Copyright (C) 2009, One Laptop Per Child Association Inc |
---|
4 | # |
---|
5 | # This program is free software; you can redistribute it and/or modify |
---|
6 | # it under the terms of the GNU General Public License as published by |
---|
7 | # the Free Software Foundation; either version 2 of the License, or |
---|
8 | # (at your option) any later version. |
---|
9 | # |
---|
10 | # This program is distributed in the hope that it will be useful, |
---|
11 | # but WITHOUT ANY WARRANTY; without even the implied warranty of |
---|
12 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
---|
13 | # GNU General Public License for more details. |
---|
14 | # |
---|
15 | # You should have received a copy of the GNU General Public License |
---|
16 | # along with this program; if not, write to the Free Software |
---|
17 | # Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA |
---|
18 | |
---|
19 | import os |
---|
20 | import sys |
---|
21 | import time |
---|
22 | import subprocess |
---|
23 | import shutil |
---|
24 | |
---|
25 | if os.environ.get('SUGAR_LOGGER_LEVEL', '') == 'debug': |
---|
26 | print '%r STARTUP: Starting the shell' % time.time() |
---|
27 | sys.stdout.flush() |
---|
28 | |
---|
29 | import gettext |
---|
30 | import logging |
---|
31 | |
---|
32 | import gconf |
---|
33 | import gtk |
---|
34 | import gobject |
---|
35 | import dbus.glib |
---|
36 | import wnck |
---|
37 | import dbus |
---|
38 | |
---|
39 | try: |
---|
40 | import xklavier |
---|
41 | except ImportError: |
---|
42 | logging.debug('Could not load xklavier for keyboard configuration') |
---|
43 | |
---|
44 | gtk.gdk.threads_init() |
---|
45 | dbus.glib.threads_init() |
---|
46 | |
---|
47 | def cleanup_logs(logs_dir): |
---|
48 | """Clean up the log directory, moving old logs into a numbered backup |
---|
49 | directory. We only keep `_MAX_BACKUP_DIRS` of these backup directories |
---|
50 | around; the rest are removed.""" |
---|
51 | if not os.path.isdir(logs_dir): |
---|
52 | os.makedirs(logs_dir) |
---|
53 | |
---|
54 | backup_logs = [] |
---|
55 | backup_dirs = [] |
---|
56 | for f in os.listdir(logs_dir): |
---|
57 | path = os.path.join(logs_dir, f) |
---|
58 | if os.path.isfile(path): |
---|
59 | backup_logs.append(f) |
---|
60 | elif os.path.isdir(path): |
---|
61 | backup_dirs.append(path) |
---|
62 | |
---|
63 | if len(backup_dirs) > 3: |
---|
64 | backup_dirs.sort() |
---|
65 | root = backup_dirs[0] |
---|
66 | for f in os.listdir(root): |
---|
67 | os.remove(os.path.join(root, f)) |
---|
68 | os.rmdir(root) |
---|
69 | |
---|
70 | if len(backup_logs) > 0: |
---|
71 | name = str(int(time.time())) |
---|
72 | backup_dir = os.path.join(logs_dir, name) |
---|
73 | os.mkdir(backup_dir) |
---|
74 | for log in backup_logs: |
---|
75 | source_path = os.path.join(logs_dir, log) |
---|
76 | dest_path = os.path.join(backup_dir, log) |
---|
77 | os.rename(source_path, dest_path) |
---|
78 | |
---|
79 | def show_welcome_screen(): |
---|
80 | path = os.path.expanduser('~/.welcome_screen') |
---|
81 | if os.path.exists(path): |
---|
82 | welcome_file_flag = open(path, 'r') |
---|
83 | welcome_file_command = welcome_file_flag.read() |
---|
84 | welcome_file_flag.close() |
---|
85 | os.remove(path) |
---|
86 | if subprocess.call(welcome_file_command.split()): |
---|
87 | logging.warning('Can not display welcome screen.') |
---|
88 | |
---|
89 | def start_ui_service(): |
---|
90 | from jarabe.view.service import UIService |
---|
91 | |
---|
92 | ui_service = UIService() |
---|
93 | |
---|
94 | def start_session_manager(): |
---|
95 | from jarabe.model.session import get_session_manager |
---|
96 | |
---|
97 | session_manager = get_session_manager() |
---|
98 | session_manager.start() |
---|
99 | |
---|
100 | def unfreeze_dcon_cb(): |
---|
101 | logging.debug('STARTUP: unfreeze_dcon_cb') |
---|
102 | from jarabe.model import screen |
---|
103 | |
---|
104 | screen.set_dcon_freeze(0) |
---|
105 | |
---|
106 | def setup_frame_cb(): |
---|
107 | logging.debug('STARTUP: setup_frame_cb') |
---|
108 | from jarabe import frame |
---|
109 | frame.get_view() |
---|
110 | |
---|
111 | def setup_keyhandler_cb(): |
---|
112 | logging.debug('STARTUP: setup_keyhandler_cb') |
---|
113 | from jarabe.view import keyhandler |
---|
114 | from jarabe import frame |
---|
115 | keyhandler.setup(frame.get_view()) |
---|
116 | |
---|
117 | def setup_journal_cb(): |
---|
118 | logging.debug('STARTUP: setup_journal_cb') |
---|
119 | from jarabe.journal import journalactivity |
---|
120 | journalactivity.start() |
---|
121 | |
---|
122 | def show_software_updates_cb(): |
---|
123 | logging.debug('STARTUP: show_software_updates_cb') |
---|
124 | if os.path.isfile(os.path.expanduser('~/.sugar-update')): |
---|
125 | from jarabe.desktop import homewindow |
---|
126 | home_window = homewindow.get_instance() |
---|
127 | home_window.get_home_box().show_software_updates_alert() |
---|
128 | |
---|
129 | browse_p = None |
---|
130 | |
---|
131 | def browse_check(): |
---|
132 | logging.warning("checking for browse") |
---|
133 | rc = browse_p.poll() |
---|
134 | if rc is None: |
---|
135 | logging.warning("browse still running") |
---|
136 | bus = dbus.SystemBus() |
---|
137 | proxy = bus.get_object('org.freedesktop.login1', '/org/freedesktop/login1') |
---|
138 | pm = dbus.Interface(proxy, 'org.freedesktop.login1.Manager') |
---|
139 | pm.Reboot(True) |
---|
140 | else: |
---|
141 | logging.warning("browse exited with code %s", rc) |
---|
142 | |
---|
143 | def start_browse(): |
---|
144 | logging.warning("starting browse") |
---|
145 | global browse_p |
---|
146 | browse_p = subprocess.Popen("/usr/bin/sugar-launch org.laptop.WebActivity", shell=True) |
---|
147 | gobject.timeout_add(18000, browse_check) |
---|
148 | return False |
---|
149 | |
---|
150 | def setup_notification_service_cb(): |
---|
151 | from jarabe.model import notifications |
---|
152 | notifications.init() |
---|
153 | |
---|
154 | def setup_file_transfer_cb(): |
---|
155 | from jarabe.model import filetransfer |
---|
156 | filetransfer.init() |
---|
157 | |
---|
158 | def setup_keyboard_cb(): |
---|
159 | logging.debug('STARTUP: setup_keyboard_cb') |
---|
160 | |
---|
161 | gconf_client = gconf.client_get_default() |
---|
162 | have_config = False |
---|
163 | |
---|
164 | try: |
---|
165 | display = gtk.gdk.display_get_default() |
---|
166 | if display is not None: |
---|
167 | engine = xklavier.Engine(display) |
---|
168 | else: |
---|
169 | logging.debug('setup_keyboard_cb: Could not get default display.') |
---|
170 | return |
---|
171 | |
---|
172 | configrec = xklavier.ConfigRec() |
---|
173 | configrec.get_from_server(engine) |
---|
174 | |
---|
175 | layouts = gconf_client.get_list(\ |
---|
176 | '/desktop/sugar/peripherals/keyboard/layouts', gconf.VALUE_STRING) |
---|
177 | layouts_list = [] |
---|
178 | variants_list = [] |
---|
179 | for layout in layouts: |
---|
180 | layouts_list.append(layout.split('(')[0]) |
---|
181 | variants_list.append(layout.split('(')[1][:-1]) |
---|
182 | |
---|
183 | if layouts_list and variants_list: |
---|
184 | have_config = True |
---|
185 | configrec.set_layouts(layouts_list) |
---|
186 | configrec.set_variants(variants_list) |
---|
187 | |
---|
188 | model = gconf_client.get_string(\ |
---|
189 | '/desktop/sugar/peripherals/keyboard/model') |
---|
190 | if model: |
---|
191 | have_config = True |
---|
192 | configrec.set_model(model) |
---|
193 | |
---|
194 | options = gconf_client.get_list(\ |
---|
195 | '/desktop/sugar/peripherals/keyboard/options', gconf.VALUE_STRING) |
---|
196 | if options: |
---|
197 | have_config = True |
---|
198 | configrec.set_options(options) |
---|
199 | |
---|
200 | if have_config: |
---|
201 | configrec.activate(engine) |
---|
202 | except Exception: |
---|
203 | logging.exception('Error during keyboard configuration') |
---|
204 | |
---|
205 | def setup_window_manager(): |
---|
206 | logging.debug('STARTUP: window_manager') |
---|
207 | |
---|
208 | # have to reset cursor(metacity sets it on startup) |
---|
209 | if subprocess.call('echo $DISPLAY; xsetroot -cursor_name left_ptr', shell=True): |
---|
210 | logging.warning('Can not reset cursor') |
---|
211 | |
---|
212 | if subprocess.call('metacity-message disable-keybindings', |
---|
213 | shell=True): |
---|
214 | logging.warning('Can not disable metacity keybindings') |
---|
215 | |
---|
216 | if subprocess.call('metacity-message disable-mouse-button-modifiers', |
---|
217 | shell=True): |
---|
218 | logging.warning('Can not disable metacity mouse button modifiers') |
---|
219 | |
---|
220 | def bootstrap(): |
---|
221 | setup_window_manager() |
---|
222 | |
---|
223 | from jarabe.view import launcher |
---|
224 | launcher.setup() |
---|
225 | |
---|
226 | gobject.idle_add(setup_frame_cb) |
---|
227 | gobject.idle_add(setup_keyhandler_cb) |
---|
228 | gobject.idle_add(setup_journal_cb) |
---|
229 | gobject.idle_add(setup_notification_service_cb) |
---|
230 | gobject.idle_add(setup_file_transfer_cb) |
---|
231 | gobject.idle_add(show_software_updates_cb) |
---|
232 | gobject.timeout_add(10000, start_browse) |
---|
233 | |
---|
234 | if sys.modules.has_key('xklavier'): |
---|
235 | gobject.idle_add(setup_keyboard_cb) |
---|
236 | |
---|
237 | def set_fonts(): |
---|
238 | client = gconf.client_get_default() |
---|
239 | face = client.get_string('/desktop/sugar/font/default_face') |
---|
240 | size = client.get_float('/desktop/sugar/font/default_size') |
---|
241 | settings = gtk.settings_get_default() |
---|
242 | settings.set_property("gtk-font-name", "%s %f" % (face, size)) |
---|
243 | |
---|
244 | def main(): |
---|
245 | try: |
---|
246 | from sugar import env |
---|
247 | # Remove temporary files. See http://bugs.sugarlabs.org/ticket/1876 |
---|
248 | data_dir = os.path.join(env.get_profile_path(), 'data') |
---|
249 | shutil.rmtree(data_dir, ignore_errors=True) |
---|
250 | os.makedirs(data_dir) |
---|
251 | cleanup_logs(env.get_logs_path()) |
---|
252 | except OSError, e: |
---|
253 | # logs cleanup is not critical; it should not prevent sugar from |
---|
254 | # starting if (for example) the disk is full or read-only. |
---|
255 | print 'logs cleanup failed: %s' % e |
---|
256 | |
---|
257 | from sugar import logger |
---|
258 | # NOTE: This needs to happen so early because some modules register translatable |
---|
259 | # strings in the module scope. |
---|
260 | from jarabe import config |
---|
261 | gettext.bindtextdomain('sugar', config.locale_path) |
---|
262 | gettext.bindtextdomain('sugar-toolkit', config.locale_path) |
---|
263 | gettext.textdomain('sugar') |
---|
264 | |
---|
265 | from jarabe.desktop import homewindow |
---|
266 | from jarabe.model import sound |
---|
267 | from jarabe import intro |
---|
268 | |
---|
269 | logger.start('shell') |
---|
270 | |
---|
271 | client = gconf.client_get_default() |
---|
272 | client.set_string('/apps/metacity/general/mouse_button_modifier', |
---|
273 | '<Super>') |
---|
274 | |
---|
275 | timezone = client.get_string('/desktop/sugar/date/timezone') |
---|
276 | if timezone is not None and timezone: |
---|
277 | os.environ['TZ'] = timezone |
---|
278 | |
---|
279 | set_fonts() |
---|
280 | |
---|
281 | # this must be added early, so that it executes and unfreezes the screen |
---|
282 | # even when we initially get blocked on the intro screen |
---|
283 | gobject.idle_add(unfreeze_dcon_cb) |
---|
284 | |
---|
285 | # make sure we have the correct cursor in the intro screen |
---|
286 | # TODO #3204 |
---|
287 | if subprocess.call('echo $DISPLAY; xsetroot -cursor_name left_ptr', shell=True): |
---|
288 | logging.warning('Can not reset cursor') |
---|
289 | |
---|
290 | # open welcome screen if booted for the first time |
---|
291 | show_welcome_screen() |
---|
292 | |
---|
293 | intro.check_profile() |
---|
294 | |
---|
295 | start_ui_service() |
---|
296 | start_session_manager() |
---|
297 | |
---|
298 | sound.restore() |
---|
299 | |
---|
300 | sys.path.append(config.ext_path) |
---|
301 | |
---|
302 | icons_path = os.path.join(config.data_path, 'icons') |
---|
303 | gtk.icon_theme_get_default().append_search_path(icons_path) |
---|
304 | |
---|
305 | # open homewindow before window_manager to let desktop appear fast |
---|
306 | home_window = homewindow.get_instance() |
---|
307 | home_window.show() |
---|
308 | |
---|
309 | screen = wnck.screen_get_default() |
---|
310 | screen.connect('window-manager-changed', __window_manager_changed_cb) |
---|
311 | _check_for_window_manager(screen) |
---|
312 | |
---|
313 | try: |
---|
314 | gtk.main() |
---|
315 | except KeyboardInterrupt: |
---|
316 | print 'Ctrl+C pressed, exiting...' |
---|
317 | |
---|
318 | |
---|
319 | def __window_manager_changed_cb(screen): |
---|
320 | _check_for_window_manager(screen) |
---|
321 | |
---|
322 | |
---|
323 | def _check_for_window_manager(screen): |
---|
324 | wm_name = screen.get_window_manager_name() |
---|
325 | if wm_name is not None: |
---|
326 | screen.disconnect_by_func(__window_manager_changed_cb) |
---|
327 | bootstrap() |
---|
328 | |
---|
329 | |
---|
330 | main() |
---|