diff options
author | Simon McVittie <simon.mcvittie@collabora.co.uk> | 2007-08-11 12:16:13 (GMT) |
---|---|---|
committer | Simon McVittie <simon.mcvittie@collabora.co.uk> | 2007-08-11 12:16:13 (GMT) |
commit | 7368429ad6feadd9e81757433b68b63d9d27c04f (patch) | |
tree | 39090b5e1c19f2d6ba63d9b07477668efff9a7c9 /services | |
parent | df3069a9317fc804b62bd3624d9591fb2e09df0f (diff) | |
parent | dcef110223e312d44955ca4aa1e2f306b9cb9e12 (diff) |
Merge branch 'master' of git+ssh://dev.laptop.org/git/sugar
Diffstat (limited to 'services')
26 files changed, 425 insertions, 137 deletions
diff --git a/services/Makefile.am b/services/Makefile.am index 7d8e351..e230030 100644 --- a/services/Makefile.am +++ b/services/Makefile.am @@ -1 +1 @@ -SUBDIRS = clipboard console +SUBDIRS = shell console diff --git a/services/console/interface/logviewer/logviewer.py b/services/console/interface/logviewer/logviewer.py index 3d90f09..8aaf347 100644 --- a/services/console/interface/logviewer/logviewer.py +++ b/services/console/interface/logviewer/logviewer.py @@ -28,29 +28,29 @@ import pango from sugar import env class MultiLogView(gtk.VBox): - def __init__(self, path): + def __init__(self, path, extra_files): self._active_log = None - self._iters = [] - + self._extra_files = extra_files + # Creating Main treeview with Actitivities list - tv_menu = gtk.TreeView() - tv_menu.connect('cursor-changed', self._load_log) - tv_menu.set_rules_hint(True) + self._tv_menu = gtk.TreeView() + self._tv_menu.connect('cursor-changed', self._load_log) + self._tv_menu.set_rules_hint(True) # Set width box_width = gtk.gdk.screen_width() * 80 / 100 - tv_menu.set_size_request(box_width*25/100, 0) + self._tv_menu.set_size_request(box_width*25/100, 0) - self.store_menu = gtk.TreeStore(str) - tv_menu.set_model(self.store_menu) + self._store_menu = gtk.TreeStore(str) + self._tv_menu.set_model(self._store_menu) - self._add_column(tv_menu, 'Sugar logs', 0) + self._add_column(self._tv_menu, 'Sugar logs', 0) self._logs_path = os.path.join(env.get_profile_path(), 'logs') self._activity = {} # Activities menu self.hbox = gtk.HBox(False, 3) - self.hbox.pack_start(tv_menu, True, True, 0) + self.hbox.pack_start(self._tv_menu, True, True, 0) # Activity log, set width self._view = LogView() @@ -59,52 +59,62 @@ class MultiLogView(gtk.VBox): self.hbox.pack_start(self._view, True, True, 0) self.hbox.show_all() - gobject.timeout_add(1000, self._update, tv_menu) + gobject.timeout_add(1000, self._update) # Load the log information in View (textview) def _load_log(self, treeview): treeselection = treeview.get_selection() - treestore, iter = treeselection.get_selected() - + # Get current selection - act_log = self.store_menu.get_value(iter, 0) - + act_log = self._store_menu.get_value(iter, 0) + # Set buffer and scroll down self._view.textview.set_buffer(self._activity[act_log]) self._view.textview.scroll_to_mark(self._activity[act_log].get_insert(), 0); self._active_log = act_log - - def _update(self, tv_menu): + + def _update(self): # Searching log files for logfile in os.listdir(self._logs_path): full_log_path = os.path.join(self._logs_path, logfile) - - if os.path.isdir(full_log_path): - continue - - if not self._activity.has_key(logfile): - self._add_activity(logfile) - model = LogBuffer(full_log_path) - self._activity[logfile] = model - - self._activity[logfile].update() - written = self._activity[logfile]._written - - # Load the first iter - if self._active_log == None: - self._active_log = logfile - iter = tv_menu.get_model().get_iter_root() - tv_menu.get_selection().select_iter(iter) - self._load_log(tv_menu) - - if written > 0 and self._active_log == logfile: - self._view.textview.scroll_to_mark(self._activity[logfile].get_insert(), 0); + self._add_log_file(full_log_path) + + for ext in self._extra_files: + self._add_log_file(ext) return True - + + def _get_filename_from_path(self, path): + return path.split('/')[-1] + + def _add_log_file(self, path): + if os.path.isdir(path): + return False + + logfile = self._get_filename_from_path(path) + + if not self._activity.has_key(logfile): + self._add_activity(logfile) + model = LogBuffer(path) + self._activity[logfile] = model + + self._activity[logfile].update() + written = self._activity[logfile]._written + + # Load the first iter + if self._active_log == None: + self._active_log = logfile + iter = self._tv_menu.get_model().get_iter_root() + self._tv_menu.get_selection().select_iter(iter) + self._load_log(self._tv_menu) + + if written > 0 and self._active_log == logfile: + self._view.textview.scroll_to_mark(self._activity[logfile].get_insert(), 0) + + def _add_activity(self, name): - self._insert_row(self.store_menu, None, name) + self._insert_row(self._store_menu, None, name) # Add a new column to the main treeview, (code from Memphis) def _add_column(self, treeview, column_name, index): @@ -171,9 +181,20 @@ class LogView(gtk.ScrolledWindow): self.textview.show() class Interface: - def __init__(self): path = None - viewer = MultiLogView(path) + xserver_logfile = self._get_xserver_logfile_path() + + # Aditional files to watch in logviewer + ext_files = [] + ext_files.append(xserver_logfile) + + viewer = MultiLogView(path, ext_files) self.widget = viewer.hbox + # Get the Xorg log file path, we have two ways to get the path: do a system + # call and exec a 'xset -q' or just read directly the file that we know + # always be the right one for a XO machine... + def _get_xserver_logfile_path(self): + path = "/var/log/Xorg.0.log" + return path diff --git a/services/console/interface/memphis/memphis.py b/services/console/interface/memphis/memphis.py index 0dd52fc..5b1ce40 100644 --- a/services/console/interface/memphis/memphis.py +++ b/services/console/interface/memphis/memphis.py @@ -130,15 +130,14 @@ class Data: treeview.set_model(self.store) def _start_memphis(self, button): - # Update information every 1.5 second button.hide() self.interface.button_stop.show() self._running_status = True - gobject.timeout_add(1500, self.load_data, self.treeview) + self._gid = gobject.timeout_add(1500, self.load_data, self.treeview) def _stop_memphis(self, button): - + gobject.source_remove(self._gid) self._running_status = False button.hide() self.interface.button_start.show() diff --git a/services/console/interface/memphis/plugins/Makefile.am b/services/console/interface/memphis/plugins/Makefile.am index a18eafe..d026419 100644 --- a/services/console/interface/memphis/plugins/Makefile.am +++ b/services/console/interface/memphis/plugins/Makefile.am @@ -1,4 +1,4 @@ -SUBDIRS = clean_size cpu dirty_size memphis_init +SUBDIRS = clean_size cpu smaps memphis_init sugardir = $(pkgdatadir)/services/console/interface/memphis/plugins sugar_PYTHON = diff --git a/services/console/interface/memphis/plugins/dirty_size/__init__.py b/services/console/interface/memphis/plugins/dirty_size/__init__.py deleted file mode 100644 index f8e9e0a..0000000 --- a/services/console/interface/memphis/plugins/dirty_size/__init__.py +++ /dev/null @@ -1,17 +0,0 @@ - -import info - - -INTERNALS = { - # Basic information - 'PLGNAME': "Dirty Size", - 'TABNAME': None, # No tabbed plugin - 'AUTHOR': "Eduardo Silva", - 'DESC': "Get dirty size memory usage", - - # Plugin API - 'Plg': None, # Plugin object - - 'top_data': [int], # Top data types needed by memphis core plugin - 'top_cols': ["PDRSS (kb)"] - } diff --git a/services/console/interface/memphis/plugins/memphis_init/info.py b/services/console/interface/memphis/plugins/memphis_init/info.py index 6e524c7..667645c 100644 --- a/services/console/interface/memphis/plugins/memphis_init/info.py +++ b/services/console/interface/memphis/plugins/memphis_init/info.py @@ -11,3 +11,4 @@ def plg_on_top_data_refresh(self, ppinfo): data = [ppinfo['pid'], ppinfo['name'], ppinfo['state_name']] return data + diff --git a/services/console/interface/memphis/plugins/dirty_size/Makefile.am b/services/console/interface/memphis/plugins/smaps/Makefile.am index 1f81dfb..1f81dfb 100644 --- a/services/console/interface/memphis/plugins/dirty_size/Makefile.am +++ b/services/console/interface/memphis/plugins/smaps/Makefile.am diff --git a/services/console/interface/memphis/plugins/dirty_size/README b/services/console/interface/memphis/plugins/smaps/README index ee4d1a5..ee4d1a5 100644 --- a/services/console/interface/memphis/plugins/dirty_size/README +++ b/services/console/interface/memphis/plugins/smaps/README diff --git a/services/console/interface/memphis/plugins/smaps/__init__.py b/services/console/interface/memphis/plugins/smaps/__init__.py new file mode 100644 index 0000000..5977d4b --- /dev/null +++ b/services/console/interface/memphis/plugins/smaps/__init__.py @@ -0,0 +1,17 @@ + +import info + + +INTERNALS = { + # Basic information + 'PLGNAME': "SMaps", + 'TABNAME': None, # No tabbed plugin + 'AUTHOR': "Eduardo Silva", + 'DESC': "Get dirty size and reference memory usage", + + # Plugin API + 'Plg': None, # Plugin object + + 'top_data': [int, int], # Top data types needed by memphis core plugin + 'top_cols': ["PDRSS (kb)", "Referenced (kb)"] + } diff --git a/services/console/interface/memphis/plugins/dirty_size/info.py b/services/console/interface/memphis/plugins/smaps/info.py index 54a2e7e..998a1a2 100644 --- a/services/console/interface/memphis/plugins/dirty_size/info.py +++ b/services/console/interface/memphis/plugins/smaps/info.py @@ -8,13 +8,12 @@ def plg_on_top_data_refresh(self, ppinfo): - - dirty_sizes = get_dirty(self, ppinfo['pid']) + smaps = get_data(self, ppinfo['pid']) - # memhis need an array - return [dirty_sizes['private']] + # memphis need an array + return [smaps['private_dirty'], smaps['referenced']] -def get_dirty(pself, pid): +def get_data(pself, pid): ProcAnalysis = pself.INTERNALS['Plg'].proc_analysis(pid) - return ProcAnalysis.DirtyRSS() + return ProcAnalysis.SMaps() diff --git a/services/console/interface/terminal/terminal.py b/services/console/interface/terminal/terminal.py index 5eebfb3..4aba858 100644 --- a/services/console/interface/terminal/terminal.py +++ b/services/console/interface/terminal/terminal.py @@ -32,7 +32,7 @@ class Terminal(gtk.HBox): self._vte = vte.Terminal() self._configure_vte() - self._vte.set_size(30, 5) + self._vte.set_size(100, 5) self._vte.set_size_request(200, 450) self._vte.show() self.pack_start(self._vte) diff --git a/services/console/lib/procmem/analysis.py b/services/console/lib/procmem/analysis.py index d2a247a..e9d7aec 100644 --- a/services/console/lib/procmem/analysis.py +++ b/services/console/lib/procmem/analysis.py @@ -7,20 +7,22 @@ class Analysis: def __init__(self, pid): self.pid = pid - def DirtyRSS(self): + def SMaps(self): smaps = proc_smaps.ProcSmaps(self.pid) - dirty = [] + private_dirty = 0 + shared_dirty = 0 + referenced = 0 - private = 0 - shared = 0 - for map in smaps.mappings: - private += map.private_dirty - shared += map.shared_dirty + private_dirty += map.private_dirty + shared_dirty += map.shared_dirty + referenced += map.referenced - dirty = {"private": int(private), "shared": int(shared)} + smaps = {"private_dirty": int(private_dirty), \ + "shared_dirty": int(shared_dirty),\ + "referenced": int(referenced)} - return dirty + return smaps def ApproxRealMemoryUsage(self): maps = proc_smaps.ProcMaps(self.pid) diff --git a/services/console/lib/procmem/proc.py b/services/console/lib/procmem/proc.py index adc2f6b..d50242b 100644 --- a/services/console/lib/procmem/proc.py +++ b/services/console/lib/procmem/proc.py @@ -1,4 +1,6 @@ -import sys, os +import os +import re +import sys import string class ProcInfo: @@ -36,10 +38,12 @@ class ProcInfo: return None # Parsing data , check 'man 5 proc' for details - data = infile.read().split() - + stat_data = infile.read() infile.close() - + + process_name = self._get_process_name(stat_data) + data = self._get_safe_split(stat_data) + state_dic = { 'R': 'Running', 'S': 'Sleeping', @@ -48,27 +52,34 @@ class ProcInfo: 'T': 'Traced/Stopped', 'W': 'Paging' } - + # user and group owners pidstat = os.stat(pidfile) - info = { - 'pid': int(data[0]), # Process ID - 'name': data[1].strip('()'), # Process name - 'state': data[2], # Process State, ex: R|S|D|Z|T|W - 'state_name': state_dic[data[2]], # Process State name, ex: Running, sleeping, Zombie, etc - 'ppid': int(data[3]), # Parent process ID - 'utime': int(data[13]), # Used jiffies in user mode - 'stime': int(data[14]), # Used jiffies in kernel mode - 'start_time': int(data[21]), # Process time from system boot (jiffies) - 'vsize': int(data[22]), # Virtual memory size used (bytes) - 'rss': int(data[23])*4, # Resident Set Size (bytes) + 'pid': int(data[0]), # Process ID + 'name': process_name, + 'state': data[2], # Process State, ex: R|S|D|Z|T|W + 'state_name': state_dic[data[2]], # Process State name, ex: Running, sleeping, Zombie, etc + 'ppid': int(data[3]), # Parent process ID + 'utime': int(data[13]), # Used jiffies in user mode + 'stime': int(data[14]), # Used jiffies in kernel mode + 'start_time': int(data[21]), # Process time from system boot (jiffies) + 'vsize': int(data[22]), # Virtual memory size used (bytes) + 'rss': int(data[23])*4, # Resident Set Size (bytes) 'user_id': pidstat.st_uid, # process owner 'group_id': pidstat.st_gid # owner group } - + return info - + + # Return the process name + def _get_process_name(self, data): + m = re.search("\(.*\)", data) + return m.string[m.start()+1:m.end()-1] + + def _get_safe_split(self, data): + new_data = re.sub("\(.*\)", '()', data) + return new_data.split() # Returns the CPU usage expressed in Jiffies def get_CPU_usage(self, cpu_hz, used_jiffies, start_time): diff --git a/services/console/lib/procmem/proc_smaps.py b/services/console/lib/procmem/proc_smaps.py index ce93cb2..422866c 100644 --- a/services/console/lib/procmem/proc_smaps.py +++ b/services/console/lib/procmem/proc_smaps.py @@ -36,7 +36,8 @@ class ProcSmaps: # Shared_Dirty: 0 kB # Private_Clean: 8 kB # Private_Dirty: 0 kB - + # Referenced: 4 kb -> Introduced in kernel 2.6.22 + while num_lines > 0: fields = lines[line_idx].split (" ", 5) if len (fields) == 6: @@ -51,13 +52,20 @@ class ProcSmaps: shared_dirty = self.parse_smaps_size_line (lines[line_idx + 4]) private_clean = self.parse_smaps_size_line (lines[line_idx + 5]) private_dirty = self.parse_smaps_size_line (lines[line_idx + 6]) + referenced = self.parse_smaps_size_line (lines[line_idx + 7]) name = name.strip () - mapping = Mapping (size, rss, shared_clean, shared_dirty, private_clean, private_dirty, permissions, name) + mapping = Mapping (size, rss, shared_clean, shared_dirty, \ + private_clean, private_dirty, referenced, permissions, name) self.mappings.append (mapping) - num_lines -= 7 - line_idx += 7 + num_lines -= 8 + line_idx += 8 + + self._clear_reference(pid) + + def _clear_reference(self, pid): + os.system("echo 1 > /proc/%s/clear_refs" % pid) # Parses a line of the form "foo: 42 kB" and returns an integer for the "42" field def parse_smaps_size_line (self, line): @@ -66,13 +74,15 @@ class ProcSmaps: return int(fields[1]) class Mapping: - def __init__ (self, size, rss, shared_clean, shared_dirty, private_clean, private_dirty, permissions, name): + def __init__ (self, size, rss, shared_clean, shared_dirty, \ + private_clean, private_dirty, referenced, permissions, name): self.size = size self.rss = rss self.shared_clean = shared_clean self.shared_dirty = shared_dirty self.private_clean = private_clean self.private_dirty = private_dirty + self.referenced = referenced self.permissions = permissions self.name = name diff --git a/services/console/sugar-console b/services/console/sugar-console index af709a6..357b7fe 100755 --- a/services/console/sugar-console +++ b/services/console/sugar-console @@ -6,7 +6,15 @@ pygtk.require('2.0') import os import sys from sugar import env +from sugar import util sys.path.append(env.get_service_path('console')) +# change to the user's home directory if it is set +# root if not +os.chdir(os.environ.get('HOME', '/')) + +#set the process title so it shows up as sugar-console not python +util.set_proc_title('sugar-console') + import console diff --git a/services/clipboard/Makefile.am b/services/shell/Makefile.am index e5a03e4..b34b974 100644 --- a/services/clipboard/Makefile.am +++ b/services/shell/Makefile.am @@ -1,29 +1,37 @@ servicedir = $(datadir)/dbus-1/services service_in_files = \ + org.laptop.ActivityRegistry.service.in \ org.laptop.Clipboard.service.in \ org.laptop.ObjectTypeRegistry.service.in service_DATA = \ + org.laptop.ActivityRegistry.service \ org.laptop.Clipboard.service \ org.laptop.ObjectTypeRegistry.service +org.laptop.ActivityRegistry.service: org.laptop.ActivityRegistry.service.in Makefile + @sed -e "s|\@bindir\@|$(bindir)|" $< > $@ + org.laptop.Clipboard.service: org.laptop.Clipboard.service.in Makefile @sed -e "s|\@bindir\@|$(bindir)|" $< > $@ org.laptop.ObjectTypeRegistry.service: org.laptop.ObjectTypeRegistry.service.in Makefile @sed -e "s|\@bindir\@|$(bindir)|" $< > $@ -sugardir = $(pkgdatadir)/services/clipboard +sugardir = $(pkgdatadir)/services/shell -sugar_PYTHON = \ - __init__.py \ - clipboardobject.py \ - clipboardservice.py \ +sugar_PYTHON = \ + __init__.py \ + activityregistryservice.py \ + bundleregistry.py \ + clipboardobject.py \ + clipboardservice.py \ objecttypeservice.py -bin_SCRIPTS = sugar-clipboard +bin_SCRIPTS = sugar-shell-service DISTCLEANFILES = $(service_DATA) EXTRA_DIST = $(service_in_files) $(bin_SCRIPTS) + diff --git a/services/clipboard/__init__.py b/services/shell/__init__.py index 52b82c8..52b82c8 100644 --- a/services/clipboard/__init__.py +++ b/services/shell/__init__.py diff --git a/services/shell/activityregistryservice.py b/services/shell/activityregistryservice.py new file mode 100644 index 0000000..44c9969 --- /dev/null +++ b/services/shell/activityregistryservice.py @@ -0,0 +1,114 @@ +# Copyright (C) 2006-2007 Red Hat, Inc. +# Copyright (C) 2007 One Laptop Per Child +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + +import dbus +import dbus.service + +import bundleregistry + +_ACTIVITY_REGISTRY_SERVICE_NAME = 'org.laptop.ActivityRegistry' +_ACTIVITY_REGISTRY_IFACE = 'org.laptop.ActivityRegistry' +_ACTIVITY_REGISTRY_PATH = '/org/laptop/ActivityRegistry' + +class ActivityRegistry(dbus.service.Object): + def __init__(self): + bus = dbus.SessionBus() + bus_name = dbus.service.BusName(_ACTIVITY_REGISTRY_SERVICE_NAME, bus=bus) + dbus.service.Object.__init__(self, bus_name, _ACTIVITY_REGISTRY_PATH) + + bundle_registry = bundleregistry.get_registry() + bundle_registry.connect('bundle-added', self._bundle_added_cb) + + @dbus.service.method(_ACTIVITY_REGISTRY_IFACE, + in_signature='s', out_signature='b') + def AddBundle(self, bundle_path): + '''Register the activity bundle with the global registry + + bundle_path -- path to the activity bundle's root directory, + that is, the directory with activity/activity.info as a + child of the directory. + + The bundleregistry.BundleRegistry is responsible for setting + up a set of d-bus service mappings for each available activity. + ''' + registry = bundleregistry.get_registry() + return registry.add_bundle(bundle_path) + + @dbus.service.method(_ACTIVITY_REGISTRY_IFACE, + in_signature='', out_signature='aa{sv}') + def GetActivities(self): + result = [] + registry = bundleregistry.get_registry() + for bundle in registry: + result.append(self._bundle_to_dict(bundle)) + return result + + @dbus.service.method(_ACTIVITY_REGISTRY_IFACE, + in_signature='s', out_signature='a{sv}') + def GetActivity(self, service_name): + registry = bundleregistry.get_registry() + bundle = registry.get_bundle(service_name) + if not bundle: + return {} + + return self._bundle_to_dict(bundle) + + @dbus.service.method(_ACTIVITY_REGISTRY_IFACE, + in_signature='s', out_signature='aa{sv}') + def FindActivity(self, name): + result = [] + key = name.lower() + + for bundle in bundleregistry.get_registry(): + name = bundle.get_name().lower() + service_name = bundle.get_service_name().lower() + if name.find(key) != -1 or service_name.find(key) != -1: + result.append(self._bundle_to_dict(bundle)) + + return result + + @dbus.service.method(_ACTIVITY_REGISTRY_IFACE, + in_signature='s', out_signature='aa{sv}') + def GetActivitiesForType(self, mime_type): + result = [] + registry = bundleregistry.get_registry() + for bundle in registry.get_activities_for_type(mime_type): + result.append(self._bundle_to_dict(bundle)) + return result + + @dbus.service.signal(_ACTIVITY_REGISTRY_IFACE, signature='a{sv}') + def ActivityAdded(self, activity_info): + pass + + def _bundle_to_dict(self, bundle): + return {'name': bundle.get_name(), + 'icon': bundle.get_icon(), + 'service_name': bundle.get_service_name(), + 'path': bundle.get_path(), + 'show_launcher': bundle.get_show_launcher()} + + def _bundle_added_cb(self, bundle_registry, bundle): + self.ActivityAdded(self._bundle_to_dict(bundle)) + +_instance = None + +def get_instance(): + global _instance + if not _instance: + _instance = ActivityRegistry() + return _instance + diff --git a/services/shell/bundleregistry.py b/services/shell/bundleregistry.py new file mode 100644 index 0000000..65a2348 --- /dev/null +++ b/services/shell/bundleregistry.py @@ -0,0 +1,125 @@ +# Copyright (C) 2006-2007 Red Hat, Inc. +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + +import os + +import gobject + +from sugar.activity.bundle import Bundle +from sugar import env +from sugar import util + +# http://standards.freedesktop.org/basedir-spec/basedir-spec-0.6.html +def _get_data_dirs(): + if os.environ.has_key('XDG_DATA_DIRS'): + return os.environ['XDG_DATA_DIRS'].split(':') + else: + return [ '/usr/local/share/', '/usr/share/' ] + +class _ServiceManager(object): + """Internal class responsible for creating dbus service files + + DBUS services are defined in files which bind a service name + to the name of an executable which provides the service name. + + In Sugar, the service files are automatically generated from + the activity registry (by this class). When an activity's + dbus launch service is requested, dbus will launch the + specified executable in order to allow it to provide the + requested activity-launching service. + + In the case of activities which provide a "class", instead of + an "exec" attribute in their activity.info, the + sugar-activity-factory script is used with an appropriate + argument to service that bundle. + """ + SERVICE_DIRECTORY = '~/.local/share/dbus-1/services' + def __init__(self): + service_dir = os.path.expanduser(self.SERVICE_DIRECTORY) + if not os.path.isdir(service_dir): + os.makedirs(service_dir) + + self._path = service_dir + + def add(self, bundle): + util.write_service(bundle.get_service_name(), + bundle.get_exec(), self._path) + +class BundleRegistry(gobject.GObject): + """Service that tracks the available activity bundles""" + + __gsignals__ = { + 'bundle-added': (gobject.SIGNAL_RUN_FIRST, gobject.TYPE_NONE, + ([gobject.TYPE_PYOBJECT])) + } + + def __init__(self): + gobject.GObject.__init__(self) + + self._bundles = {} + self._search_path = [] + self._service_manager = _ServiceManager() + + def get_bundle(self, service_name): + """Returns an bundle given his service name""" + if self._bundles.has_key(service_name): + return self._bundles[service_name] + else: + return None + + def add_search_path(self, path): + """Add a directory to the bundles search path""" + self._search_path.append(path) + self._scan_directory(path) + + def __iter__(self): + return self._bundles.values().__iter__() + + def _scan_directory(self, path): + if os.path.isdir(path): + for f in os.listdir(path): + bundle_dir = os.path.join(path, f) + if os.path.isdir(bundle_dir) and \ + bundle_dir.endswith('.activity'): + self.add_bundle(bundle_dir) + + def add_bundle(self, bundle_path): + bundle = Bundle(bundle_path) + if bundle.is_valid(): + self._bundles[bundle.get_service_name()] = bundle + self._service_manager.add(bundle) + self.emit('bundle-added', bundle) + return True + else: + return False + + def get_activities_for_type(self, mime_type): + result = [] + for bundle in self._bundles.values(): + if bundle.get_mime_types() and mime_type in bundle.get_mime_types(): + result.append(bundle) + return result + +def get_registry(): + return _bundle_registry + +_bundle_registry = BundleRegistry() + +for path in _get_data_dirs(): + bundles_path = os.path.join(path, 'activities') + _bundle_registry.add_search_path(bundles_path) + +_bundle_registry.add_search_path(env.get_user_activities_path()) diff --git a/services/clipboard/clipboardobject.py b/services/shell/clipboardobject.py index d751274..bc51f47 100644 --- a/services/clipboard/clipboardobject.py +++ b/services/shell/clipboardobject.py @@ -19,9 +19,9 @@ import logging import urlparse from sugar.objects import mime -from sugar import activity import objecttypeservice +import bundleregistry class ClipboardObject: @@ -66,30 +66,15 @@ class ClipboardObject: return '' def get_activity(self): - logging.debug('get_activity') - mapping = {'text/html' : 'org.laptop.WebActivity', - 'image/jpeg' : 'org.laptop.WebActivity', - 'image/gif' : 'org.laptop.WebActivity', - 'image/png' : 'org.laptop.WebActivity', - 'text/plain' : 'org.laptop.AbiWordActivity', - 'text/rtf' : 'org.laptop.AbiWordActivity', - 'text/richtext' : 'org.laptop.AbiWordActivity', - 'application/pdf' : 'org.laptop.sugar.ReadActivity', - 'application/x-squeak-project' : 'org.vpri.EtoysActivity'} mime = self.get_mime_type() if not mime: return '' - """ - registry = activity.get_registry() + + registry = bundleregistry.get_registry() activities = registry.get_activities_for_type(self.get_mime_type()) # TODO: should we return several activities? if activities: - return activities[0] - else: - return '' - """ - if mapping.has_key(mime): - return mapping[mime] + return activities[0].get_service_name() else: return '' @@ -101,8 +86,6 @@ class ClipboardObject: def add_format(self, format): self._formats[format.get_type()] = format - # We want to get the activity early in order to prevent a DBus lockup. - activity = self.get_activity() def get_formats(self): return self._formats diff --git a/services/clipboard/clipboardservice.py b/services/shell/clipboardservice.py index 639f29c..19958a7 100644 --- a/services/clipboard/clipboardservice.py +++ b/services/shell/clipboardservice.py @@ -74,7 +74,7 @@ class ClipboardService(dbus.service.Object): def add_object_format(self, object_path, format_type, data, on_disk): logging.debug('ClipboardService.add_object_format') cb_object = self._objects[str(object_path)] - + if on_disk and cb_object.get_percent() == 100: new_uri = self._copy_file(data) cb_object.add_format(Format(format_type, new_uri, on_disk)) diff --git a/services/clipboard/objecttypeservice.py b/services/shell/objecttypeservice.py index e12398e..e12398e 100644 --- a/services/clipboard/objecttypeservice.py +++ b/services/shell/objecttypeservice.py diff --git a/services/shell/org.laptop.ActivityRegistry.service.in b/services/shell/org.laptop.ActivityRegistry.service.in new file mode 100644 index 0000000..ab6647c --- /dev/null +++ b/services/shell/org.laptop.ActivityRegistry.service.in @@ -0,0 +1,4 @@ +[D-BUS Service] +Name = org.laptop.ActivityRegistry +Exec = @bindir@/sugar-shell-service + diff --git a/services/clipboard/org.laptop.Clipboard.service.in b/services/shell/org.laptop.Clipboard.service.in index b38bf2b..7ce3f6e 100644 --- a/services/clipboard/org.laptop.Clipboard.service.in +++ b/services/shell/org.laptop.Clipboard.service.in @@ -1,4 +1,4 @@ [D-BUS Service] Name = org.laptop.Clipboard -Exec = @bindir@/sugar-clipboard +Exec = @bindir@/sugar-shell-service diff --git a/services/clipboard/org.laptop.ObjectTypeRegistry.service.in b/services/shell/org.laptop.ObjectTypeRegistry.service.in index 66477eb..563a600 100644 --- a/services/clipboard/org.laptop.ObjectTypeRegistry.service.in +++ b/services/shell/org.laptop.ObjectTypeRegistry.service.in @@ -1,4 +1,4 @@ [D-BUS Service] Name = org.laptop.ObjectTypeRegistry -Exec = @bindir@/sugar-clipboard +Exec = @bindir@/sugar-shell-service diff --git a/services/clipboard/sugar-clipboard b/services/shell/sugar-shell-service index 4cffa33..370c2ea 100755 --- a/services/clipboard/sugar-clipboard +++ b/services/shell/sugar-shell-service @@ -23,28 +23,31 @@ import os import logging from sugar import logger -logger.start('clipboard') +logger.start('shellservice') import gobject import dbus.glib from sugar import env -sys.path.append(env.get_service_path('clipboard')) +sys.path.append(env.get_service_path('shell')) import clipboardservice import objecttypeservice +import activityregistryservice -logging.info('Starting clipboard service.') +logging.info('Starting shell service.') gobject.threads_init() dbus.glib.threads_init() clipboard_service = clipboardservice.get_instance() object_type_registry = objecttypeservice.get_instance() +activity_registry = activityregistryservice.get_instance() loop = gobject.MainLoop() try: loop.run() except KeyboardInterrupt: print 'Ctrl+C pressed, exiting...' + |