Web   ·   Wiki   ·   Activities   ·   Blog   ·   Lists   ·   Chat   ·   Meeting   ·   Bugs   ·   Git   ·   Translate   ·   Archive   ·   People   ·   Donate
summaryrefslogtreecommitdiffstats
path: root/services
diff options
context:
space:
mode:
authorSimon 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)
commit7368429ad6feadd9e81757433b68b63d9d27c04f (patch)
tree39090b5e1c19f2d6ba63d9b07477668efff9a7c9 /services
parentdf3069a9317fc804b62bd3624d9591fb2e09df0f (diff)
parentdcef110223e312d44955ca4aa1e2f306b9cb9e12 (diff)
Merge branch 'master' of git+ssh://dev.laptop.org/git/sugar
Diffstat (limited to 'services')
-rw-r--r--services/Makefile.am2
-rw-r--r--services/console/interface/logviewer/logviewer.py107
-rw-r--r--services/console/interface/memphis/memphis.py5
-rw-r--r--services/console/interface/memphis/plugins/Makefile.am2
-rw-r--r--services/console/interface/memphis/plugins/dirty_size/__init__.py17
-rw-r--r--services/console/interface/memphis/plugins/memphis_init/info.py1
-rw-r--r--services/console/interface/memphis/plugins/smaps/Makefile.am (renamed from services/console/interface/memphis/plugins/dirty_size/Makefile.am)0
-rw-r--r--services/console/interface/memphis/plugins/smaps/README (renamed from services/console/interface/memphis/plugins/dirty_size/README)0
-rw-r--r--services/console/interface/memphis/plugins/smaps/__init__.py17
-rw-r--r--services/console/interface/memphis/plugins/smaps/info.py (renamed from services/console/interface/memphis/plugins/dirty_size/info.py)11
-rw-r--r--services/console/interface/terminal/terminal.py2
-rw-r--r--services/console/lib/procmem/analysis.py20
-rw-r--r--services/console/lib/procmem/proc.py47
-rw-r--r--services/console/lib/procmem/proc_smaps.py20
-rwxr-xr-xservices/console/sugar-console8
-rw-r--r--services/shell/Makefile.am (renamed from services/clipboard/Makefile.am)20
-rw-r--r--services/shell/__init__.py (renamed from services/clipboard/__init__.py)0
-rw-r--r--services/shell/activityregistryservice.py114
-rw-r--r--services/shell/bundleregistry.py125
-rw-r--r--services/shell/clipboardobject.py (renamed from services/clipboard/clipboardobject.py)25
-rw-r--r--services/shell/clipboardservice.py (renamed from services/clipboard/clipboardservice.py)2
-rw-r--r--services/shell/objecttypeservice.py (renamed from services/clipboard/objecttypeservice.py)0
-rw-r--r--services/shell/org.laptop.ActivityRegistry.service.in4
-rw-r--r--services/shell/org.laptop.Clipboard.service.in (renamed from services/clipboard/org.laptop.Clipboard.service.in)2
-rw-r--r--services/shell/org.laptop.ObjectTypeRegistry.service.in (renamed from services/clipboard/org.laptop.ObjectTypeRegistry.service.in)2
-rwxr-xr-xservices/shell/sugar-shell-service (renamed from services/clipboard/sugar-clipboard)9
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...'
+