Source code for sugar4.activity.activityinstance

# Copyright (C) 2006-2008, Red Hat, Inc.
# Copyright (C) 2025 MostlyK
#
# 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 gettext
import logging
import os
import sys
from argparse import ArgumentParser

import dbus
import dbus.service
from dbus.mainloop.glib import DBusGMainLoop

DBusGMainLoop(set_as_default=True)

import gi

gi.require_version("Gtk", "4.0")
import hashlib
import random
import time
from errno import EEXIST

from gi.repository import Gtk

from sugar4 import config, logger
from sugar4.activity import activityhandle
from sugar4.bundle.activitybundle import ActivityBundle
from sugar4.bundle.bundle import MalformedBundleException


def _makedirs(path):
    try:
        os.makedirs(path)
    except OSError as e:
        if e.errno != EEXIST:
            raise e


[docs] def get_single_process_name(bundle_id): return bundle_id
[docs] def get_single_process_path(bundle_id): return "/" + bundle_id.replace(".", "/")
[docs] class SingleProcess(dbus.service.Object): def __init__(self, name_service, constructor):
[docs] self.constructor = constructor
bus = dbus.SessionBus() bus_name = dbus.service.BusName(name_service, bus=bus) object_path = get_single_process_path(name_service) dbus.service.Object.__init__(self, bus_name, object_path) @dbus.service.method("org.laptop.SingleProcess", in_signature="a{sv}")
[docs] def create(self, handle_dict): # handle = activityhandle.create_from_dict(handle_dict) # create_activity_instance(self.constructor, handle) # Don't create instance here, GTK4 uses application model pass
[docs] def main(): usage = "%(prog)s [options] [activity dir] [python class]" epilog = ( "If you are running from a directory containing an Activity, " "the argument may be omitted. Otherwise please provide either " "a directory containing a Sugar Activity [activity dir], a " "[python_class], or both." ) parser = ArgumentParser(usage=usage, epilog=epilog) parser.add_argument( "-b", "--bundle-id", dest="bundle_id", help="identifier of the activity bundle" ) parser.add_argument( "-a", "--activity-id", dest="activity_id", help="identifier of the activity instance", ) parser.add_argument( "-o", "--object-id", dest="object_id", help="identifier of the associated datastore object", ) parser.add_argument("-u", "--uri", dest="uri", help="URI to load") parser.add_argument( "-s", "--single-process", dest="single_process", action="store_true", help="start all the instances in the same process", ) parser.add_argument( "-i", "--invited", dest="invited", action="store_true", default=False, help="the activity is being launched for handling an invite from the network", ) options, args = parser.parse_known_args() logger.start() activity_class = None if len(args) == 2: activity_class = args[1] os.chdir(args[0]) elif len(args) == 1: if os.path.isdir(args[0]): os.chdir(args[0]) else: activity_class = args[0] bundle_path = os.path.abspath(os.curdir) sys.path.insert(0, bundle_path) try: bundle = ActivityBundle(bundle_path) except MalformedBundleException: parser.print_help() exit(0) if not activity_class: command = bundle.get_command() if command.startswith("sugar-activity"): activity_class = command.split(" ")[1] # when an activity is started outside sugar, # activityfactory.get_environment has not executed in parent # process, so parts of get_environment must happen here. if "SUGAR_BUNDLE_PATH" not in os.environ: profile_id = os.environ.get("SUGAR_PROFILE", "default") home_dir = os.environ.get("SUGAR_HOME", os.path.expanduser("~/.sugar")) base = os.path.join(home_dir, profile_id) activity_root = os.path.join(base, bundle.get_bundle_id()) instance_dir = os.path.join(activity_root, "instance") _makedirs(instance_dir) data_dir = os.path.join(activity_root, "data") _makedirs(data_dir) tmp_dir = os.path.join(activity_root, "tmp") _makedirs(tmp_dir) os.environ["SUGAR_BUNDLE_PATH"] = bundle_path os.environ["SUGAR_BUNDLE_ID"] = bundle.get_bundle_id() os.environ["SUGAR_ACTIVITY_ROOT"] = activity_root os.environ["SUGAR_BUNDLE_NAME"] = bundle.get_name() os.environ["SUGAR_BUNDLE_VERSION"] = str(bundle.get_activity_version()) # must be done early, some activities set translations globally, SL #3654 activity_locale_path = os.environ.get("SUGAR_LOCALEDIR", config.locale_path) gettext.bindtextdomain(bundle.get_bundle_id(), activity_locale_path) gettext.bindtextdomain("sugar-toolkit-gtk4", config.locale_path) gettext.textdomain(bundle.get_bundle_id()) splitted_module = activity_class.rsplit(".", 1) module_name = splitted_module[0] class_name = splitted_module[1] module = __import__(module_name) for comp in module_name.split(".")[1:]: module = getattr(module, comp) activity_constructor = getattr(module, class_name) if not options.activity_id: # Generate random hash data = "%s%s" % (time.time(), random.randint(10000, 100000)) random_hash = hashlib.sha1(data.encode()).hexdigest() options.activity_id = random_hash options.bundle_id = bundle.get_bundle_id() activity_handle = activityhandle.ActivityHandle( activity_id=options.activity_id, object_id=options.object_id, uri=options.uri, invited=options.invited, ) # GTK4: Gtk.Application model app = Gtk.Application(application_id=bundle.get_bundle_id()) def on_activate(app): """Create and show the activity when the application is activated.""" activity = activity_constructor(activity_handle) app.add_window(activity) activity.show() # This calls present() internally app.connect("activate", on_activate) if hasattr(module, "start"): module.start() # Run the GTK application sys.exit(app.run(sys.argv))