Permalink
sugar-toolkit-gtk3/src/sugar3/activity/activityinstance.py
Newer
100644
234 lines (183 sloc)
8.1 KB
1
# Copyright (C) 2006-2008, Red Hat, Inc.
2
#
3
# This program is free software; you can redistribute it and/or modify
4
# it under the terms of the GNU General Public License as published by
5
# the Free Software Foundation; either version 2 of the License, or
6
# (at your option) any later version.
7
#
8
# This program is distributed in the hope that it will be useful,
9
# but WITHOUT ANY WARRANTY; without even the implied warranty of
10
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11
# GNU General Public License for more details.
12
#
13
# You should have received a copy of the GNU General Public License
14
# along with this program; if not, write to the Free Software
15
# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
16
17
import os
18
import sys
19
import six
20
import logging
21
22
# Change the default encoding to avoid UnicodeDecodeError
23
# http://lists.sugarlabs.org/archive/sugar-devel/2012-August/038928.html
24
if six.PY2:
25
reload(sys)
26
sys.setdefaultencoding('utf-8')
27
28
import gettext
29
from argparse import ArgumentParser
30
31
import dbus
32
import dbus.service
33
from dbus.mainloop.glib import DBusGMainLoop
34
DBusGMainLoop(set_as_default=True)
35
36
from sugar3.activity import activityhandle
37
from sugar3 import config
38
from sugar3.bundle.activitybundle import ActivityBundle
39
from sugar3 import logger
40
41
from sugar3.bundle.bundle import MalformedBundleException
42
45
import time
46
import hashlib
47
import random
48
49
50
def _makedirs(path):
51
try:
52
os.makedirs(path)
53
except OSError as e:
54
if e.errno != EEXIST:
55
raise e
56
57
58
def create_activity_instance(constructor, handle):
59
activity = constructor(handle)
60
activity.show()
61
return activity
62
63
64
def get_single_process_name(bundle_id):
65
return bundle_id
66
67
68
def get_single_process_path(bundle_id):
69
return '/' + bundle_id.replace('.', '/')
70
71
72
class SingleProcess(dbus.service.Object):
73
74
def __init__(self, name_service, constructor):
75
self.constructor = constructor
76
77
bus = dbus.SessionBus()
78
bus_name = dbus.service.BusName(name_service, bus=bus)
79
object_path = get_single_process_path(name_service)
80
dbus.service.Object.__init__(self, bus_name, object_path)
81
82
@dbus.service.method('org.laptop.SingleProcess', in_signature='a{sv}')
83
def create(self, handle_dict):
84
handle = activityhandle.create_from_dict(handle_dict)
85
create_activity_instance(self.constructor, handle)
86
87
88
def main():
89
usage = 'usage: %prog [options] [activity dir] [python class]'
90
epilog = 'If you are running from a directory containing an Activity, ' \
91
'the argument may be omitted. Otherwise please provide either '\
92
'a directory containing a Sugar Activity [activity dir], a '\
93
'[python_class], or both.'
94
95
parser = ArgumentParser(usage=usage, epilog=epilog)
96
parser.add_argument('-b', '--bundle-id', dest='bundle_id',
97
help='identifier of the activity bundle')
98
parser.add_argument('-a', '--activity-id', dest='activity_id',
99
help='identifier of the activity instance')
100
parser.add_argument('-o', '--object-id', dest='object_id',
101
help='identifier of the associated datastore object')
102
parser.add_argument('-u', '--uri', dest='uri',
103
help='URI to load')
104
parser.add_argument('-s', '--single-process', dest='single_process',
105
action='store_true',
106
help='start all the instances in the same process')
107
parser.add_argument('-i', '--invited', dest='invited',
108
action='store_true', default=False,
109
help='the activity is being launched for handling an '
110
'invite from the network')
111
112
options, args = parser.parse_known_args()
113
114
logger.start()
115
116
activity_class = None
117
if len(args) == 2:
118
activity_class = args[1]
119
os.chdir(args[0])
120
elif len(args) == 1:
121
if os.path.isdir(args[0]):
122
os.chdir(args[0])
123
else:
124
activity_class = args[0]
125
126
bundle_path = os.path.abspath(os.curdir)
127
sys.path.insert(0, bundle_path)
128
129
try:
130
bundle = ActivityBundle(bundle_path)
131
except MalformedBundleException:
132
parser.print_help()
133
exit(0)
134
135
if not activity_class:
136
command = bundle.get_command()
137
if command.startswith('sugar-activity'):
138
if not command.startswith('sugar-activity3'):
139
logging.warning("Activity written for Python 2,"
140
" consider porting to Python 3.")
143
# when an activity is started outside sugar,
144
# activityfactory.get_environment has not executed in parent
145
# process, so parts of get_environment must happen here.
146
if 'SUGAR_BUNDLE_PATH' not in os.environ:
147
profile_id = os.environ.get('SUGAR_PROFILE', 'default')
148
home_dir = os.environ.get('SUGAR_HOME', os.path.expanduser('~/.sugar'))
149
base = os.path.join(home_dir, profile_id)
150
activity_root = os.path.join(base, bundle.get_bundle_id())
151
152
instance_dir = os.path.join(activity_root, 'instance')
154
155
data_dir = os.path.join(activity_root, 'data')
161
os.environ['SUGAR_BUNDLE_PATH'] = bundle_path
162
os.environ['SUGAR_BUNDLE_ID'] = bundle.get_bundle_id()
163
os.environ['SUGAR_ACTIVITY_ROOT'] = activity_root
164
165
os.environ['SUGAR_BUNDLE_NAME'] = bundle.get_name()
166
os.environ['SUGAR_BUNDLE_VERSION'] = str(bundle.get_activity_version())
167
168
# must be done early, some activities set translations globally, SL #3654
169
activity_locale_path = os.environ.get("SUGAR_LOCALEDIR",
170
config.locale_path)
171
172
gettext.bindtextdomain(bundle.get_bundle_id(), activity_locale_path)
173
gettext.bindtextdomain('sugar-toolkit-gtk3', config.locale_path)
174
gettext.textdomain(bundle.get_bundle_id())
175
176
splitted_module = activity_class.rsplit('.', 1)
177
module_name = splitted_module[0]
178
class_name = splitted_module[1]
179
180
module = __import__(module_name)
181
for comp in module_name.split('.')[1:]:
182
module = getattr(module, comp)
183
184
activity_constructor = getattr(module, class_name)
185
186
if not options.activity_id:
187
# Generate random hash
188
data = '%s%s' % (time.time(), random.randint(10000, 100000))
189
random_hash = hashlib.sha1(data.encode()).hexdigest()
190
options.activity_id = random_hash
191
options.bundle_id = bundle.get_bundle_id()
192
193
activity_handle = activityhandle.ActivityHandle(
194
activity_id=options.activity_id,
195
object_id=options.object_id, uri=options.uri,
196
invited=options.invited)
197
198
if options.single_process is True:
199
sessionbus = dbus.SessionBus()
200
201
service_name = get_single_process_name(options.bundle_id)
202
service_path = get_single_process_path(options.bundle_id)
203
204
bus_object = sessionbus.get_object(
205
'org.freedesktop.DBus', '/org/freedesktop/DBus')
206
try:
207
name = bus_object.GetNameOwner(
208
service_name, dbus_interface='org.freedesktop.DBus')
209
except dbus.DBusException:
210
name = None
211
212
if not name:
213
SingleProcess(service_name, activity_constructor)
214
else:
215
try:
216
single_process = sessionbus.get_object(service_name,
217
service_path)
218
single_process.create(
219
activity_handle.get_dict(),
220
dbus_interface='org.laptop.SingleProcess')
221
222
print('Created %s in a single process.' % service_name)
223
sys.exit(0)
224
except (TypeError, dbus.DBusException):
225
print('Could not communicate with the instance process,'
226
'launching a new process')
227
228
if hasattr(module, 'start'):
229
module.start()
230
231
instance = create_activity_instance(activity_constructor, activity_handle)
232
233
if hasattr(instance, 'run_main_loop'):
234
instance.run_main_loop()