diff --git a/recipes-cinnamon/cinnamon/cinnamon.bb b/recipes-cinnamon/cinnamon/cinnamon.bb index 6cfc12a..6fca45e 100644 --- a/recipes-cinnamon/cinnamon/cinnamon.bb +++ b/recipes-cinnamon/cinnamon/cinnamon.bb @@ -29,6 +29,12 @@ inherit meson pkgconfig gobject-introspection gtk-icon-cache gsettings gtk-doc g SRC_URI = " \ git://github.com/linuxmint/cinnamon.git;branch=master;protocol=https \ file://0001-Do-not-crash-on-systemd-reporting-Univeral-timezone.patch \ + file://okaestne-settings-performance/0001-ExtensionCore-defer-loading-of-cinnamon-version-fix-.patch \ + file://okaestne-settings-performance/0002-cs_privacy-defer-init-of-NM.Client.patch \ + file://okaestne-settings-performance/0003-cs_backgrounds-defer-import-of-imtools-module.patch \ + file://okaestne-settings-performance/0004-Spices-defer-import-of-requests-module.patch \ + file://okaestne-settings-performance/0005-cs-fix-print_timing-remove-stale-touch-function.patch \ + file://okaestne-settings-performance/0006-cs-lazy-load-python-modules-when-passed-as-arg.patch \ " SRCREV = "037b17248b176c7f3dd5c9848f8c6738105c4cc2" PV = "5.2.7+git${SRCPV}" diff --git a/recipes-cinnamon/cinnamon/cinnamon/okaestne-settings-performance/0001-ExtensionCore-defer-loading-of-cinnamon-version-fix-.patch b/recipes-cinnamon/cinnamon/cinnamon/okaestne-settings-performance/0001-ExtensionCore-defer-loading-of-cinnamon-version-fix-.patch new file mode 100644 index 0000000..0903a81 --- /dev/null +++ b/recipes-cinnamon/cinnamon/cinnamon/okaestne-settings-performance/0001-ExtensionCore-defer-loading-of-cinnamon-version-fix-.patch @@ -0,0 +1,101 @@ +From 03599325d88f453b464f8c500fea0e758dd6a386 Mon Sep 17 00:00:00 2001 +From: okaestne +Date: Sun, 3 Apr 2022 15:39:36 +0200 +Subject: [PATCH 1/6] ExtensionCore: defer loading of cinnamon version & fix + comparison + +* saves ~15ms at import time +* fix: comparsion was able to fail because of string comparision + e.g `['5', '8'] > ['5', '10']` is `True` +--- + .../cinnamon-settings/bin/ExtensionCore.py | 34 ++++++++++--------- + 1 file changed, 18 insertions(+), 16 deletions(-) + +diff --git a/files/usr/share/cinnamon/cinnamon-settings/bin/ExtensionCore.py b/files/usr/share/cinnamon/cinnamon-settings/bin/ExtensionCore.py +index 72d124c28..e616b08e1 100644 +--- a/files/usr/share/cinnamon/cinnamon-settings/bin/ExtensionCore.py ++++ b/files/usr/share/cinnamon/cinnamon-settings/bin/ExtensionCore.py +@@ -1,5 +1,6 @@ + #!/usr/bin/python3 + ++from functools import lru_cache + import os + import re + import html +@@ -30,20 +31,22 @@ ROW_SIZE = 32 + + UNSAFE_ITEMS = ['spawn_sync', 'spawn_command_line_sync', 'GTop', 'get_file_contents_utf8_sync'] + +-curr_ver = subprocess.check_output(['cinnamon', '--version']).decode("utf-8").splitlines()[0].split(' ')[1] +-curr_ver_elements = curr_ver.split(".") +-curr_ver_major = int(curr_ver_elements[0]) +-curr_ver_minor = int(curr_ver_elements[1]) +- + LANGUAGE_CODE = "C" + try: + LANGUAGE_CODE = locale.getlocale()[0].split("_")[0] + except: + pass + ++ ++@lru_cache(maxsize=None) # fetch only once ++def get_cinnamon_version(): ++ version_str = subprocess.check_output(['cinnamon', '--version'], encoding="utf-8").split()[1] ++ return [int(part) for part in version_str.split(".")] ++ ++ + def find_extension_subdir(directory): +- largest = ['0'] +- curr_a = curr_ver.split('.') ++ largest = [0] ++ curr_a = get_cinnamon_version() + + for subdir in os.listdir(directory): + if not os.path.isdir(os.path.join(directory, subdir)): +@@ -52,15 +55,15 @@ def find_extension_subdir(directory): + if not re.match(r'^[1-9][0-9]*\.[0-9]+(\.[0-9]+)?$', subdir): + continue + +- subdir_a = subdir.split(".") ++ subdir_a = [int(part) for part in subdir.split(".")] + +- if subdir_a < curr_a and largest < subdir_a: ++ if subdir_a <= curr_a and largest < subdir_a: + largest = subdir_a + +- if len(largest) == 1: ++ if largest == [0]: + return directory + else: +- return os.path.join(directory, ".".join(largest)) ++ return os.path.join(directory, ".".join(map(str, largest))) + + translations = {} + +@@ -315,11 +318,11 @@ class ManageSpicesRow(Gtk.ListBoxRow): + try: + # Treat "cinnamon-version" as a list of minimum required versions + # if any version in there is lower than our Cinnamon version, then the spice is compatible. ++ curr_ver = get_cinnamon_version() ++ + for version in self.metadata['cinnamon-version']: +- elements = version.split(".") +- major = int(elements[0]) +- minor = int(elements[1]) +- if curr_ver_major > major or (curr_ver_major == major and curr_ver_minor >= minor): ++ spice_ver = [int(part) for part in version.split(".")] ++ if spice_ver[:2] <= curr_ver: + # The version is OK, check that we can find the right .js file in the appropriate subdir + path = os.path.join(self.metadata['path'], self.extension_type + ".js") + if os.path.exists(path): +@@ -327,7 +330,6 @@ class ManageSpicesRow(Gtk.ListBoxRow): + else: + print ("The %s %s is not properly structured. Path not found: '%s'" % (self.uuid, self.extension_type, path)) + return False +- return True + print ("The %s %s is not compatible with this version of Cinnamon." % (self.uuid, self.extension_type)) + return False + except: +-- +2.34.1 + diff --git a/recipes-cinnamon/cinnamon/cinnamon/okaestne-settings-performance/0002-cs_privacy-defer-init-of-NM.Client.patch b/recipes-cinnamon/cinnamon/cinnamon/okaestne-settings-performance/0002-cs_privacy-defer-init-of-NM.Client.patch new file mode 100644 index 0000000..06dabdf --- /dev/null +++ b/recipes-cinnamon/cinnamon/cinnamon/okaestne-settings-performance/0002-cs_privacy-defer-init-of-NM.Client.patch @@ -0,0 +1,97 @@ +From b20036b87aa78242e11b4e5f023b4f8586cfe1d9 Mon Sep 17 00:00:00 2001 +From: okaestne +Date: Sun, 3 Apr 2022 17:49:43 +0200 +Subject: [PATCH 2/6] cs_privacy: defer init of NM.Client + +saves ~10ms import time + +also fix some imports +--- + .../cinnamon-settings/modules/cs_privacy.py | 43 +++++++++++-------- + 1 file changed, 24 insertions(+), 19 deletions(-) + +diff --git a/files/usr/share/cinnamon/cinnamon-settings/modules/cs_privacy.py b/files/usr/share/cinnamon/cinnamon-settings/modules/cs_privacy.py +index a726b0776..26693b230 100755 +--- a/files/usr/share/cinnamon/cinnamon-settings/modules/cs_privacy.py ++++ b/files/usr/share/cinnamon/cinnamon-settings/modules/cs_privacy.py +@@ -1,26 +1,16 @@ + #!/usr/bin/python3 +- +-nm_client = None +-try: +- import gi +- gi.require_version('NM', '1.0') +- from gi.repository import NM +- nm_client = NM.Client.new(None) +- # call connectivity_check_get_available to make +- # sure it's available (it's new in libnm 1.10) +- # if it's not, we catch the exception and set +- # the client to None +- nm_client.connectivity_check_get_available() +-except: +- nm_client = None ++import gi ++gi.require_version('Gtk', '3.0') ++from gi.repository import Gio, Gtk + + from SettingsWidgets import SidePage +-from xapp.GSettingsWidgets import * ++from xapp.GSettingsWidgets import GSettingsSwitch, SettingsLabel, SettingsPage, SettingsRevealer, SettingsWidget, Switch + + PRIVACY_SCHEMA = "org.cinnamon.desktop.privacy" + GTK_RECENT_ENABLE_KEY = "remember-recent-files" + GTK_RECENT_MAX_AGE = "recent-files-max-age" + ++ + class Module: + name = "privacy" + comment = _("Cinnamon privacy settings") +@@ -31,6 +21,19 @@ class Module: + sidePage = SidePage(_("Privacy"), "cs-privacy", keywords, content_box, module=self) + self.sidePage = sidePage + self.settings = Gio.Settings(schema=PRIVACY_SCHEMA) ++ self.nm_client = None ++ ++ def _init_nm_client(self): ++ try: ++ gi.require_version('NM', '1.0') ++ from gi.repository import NM ++ nm_client = NM.Client.new() ++ ++ # we need libnm >=1.10 ++ if hasattr(nm_client, 'connectivity_check_get_available'): ++ self.nm_client = nm_client ++ except ValueError: ++ pass + + def on_module_selected(self): + if not self.loaded: +@@ -77,14 +80,16 @@ class Module: + else: + self.indefinite_switch.content_widget.set_active(False) + self.revealer.set_reveal_child(True) +- if start_age == 0: # Shouldn't happen, unless someone manually sets the value ++ if start_age == 0: # Shouldn't happen, unless someone manually sets the value + self.settings.set_int(GTK_RECENT_MAX_AGE, 30) + self.bind_spinner() + +- if nm_client is not None and nm_client.connectivity_check_get_available(): ++ self._init_nm_client() ++ ++ if self.nm_client is not None and self.nm_client.connectivity_check_get_available(): + section = page.add_section(_("Internet connectivity")) + connectivity_switch = Switch(_("Check connectivity")) +- connectivity_switch.content_widget.set_active(nm_client.connectivity_check_get_enabled()) ++ connectivity_switch.content_widget.set_active(self.nm_client.connectivity_check_get_enabled()) + connectivity_switch.content_widget.connect("notify::active", self.on_connectivity_toggled) + section.add_row(connectivity_switch) + section.add_note(_("Check that network connections can reach the Internet. This makes it possible to detect captive portals, but also generates periodic network traffic.")) +@@ -117,4 +122,4 @@ class Module: + + def on_connectivity_toggled(self, widget, gparam): + active = widget.get_active() +- nm_client.connectivity_check_set_enabled(active) ++ self.nm_client.connectivity_check_set_enabled(active) +-- +2.34.1 + diff --git a/recipes-cinnamon/cinnamon/cinnamon/okaestne-settings-performance/0003-cs_backgrounds-defer-import-of-imtools-module.patch b/recipes-cinnamon/cinnamon/cinnamon/okaestne-settings-performance/0003-cs_backgrounds-defer-import-of-imtools-module.patch new file mode 100644 index 0000000..94a7902 --- /dev/null +++ b/recipes-cinnamon/cinnamon/cinnamon/okaestne-settings-performance/0003-cs_backgrounds-defer-import-of-imtools-module.patch @@ -0,0 +1,34 @@ +From b7f582e2500959e89c56837675cfe454892846ce Mon Sep 17 00:00:00 2001 +From: okaestne +Date: Sun, 3 Apr 2022 23:59:22 +0200 +Subject: [PATCH 3/6] cs_backgrounds: defer import of imtools module + +saves ~55ms import time +--- + .../share/cinnamon/cinnamon-settings/modules/cs_backgrounds.py | 3 ++- + 1 file changed, 2 insertions(+), 1 deletion(-) + +diff --git a/files/usr/share/cinnamon/cinnamon-settings/modules/cs_backgrounds.py b/files/usr/share/cinnamon/cinnamon-settings/modules/cs_backgrounds.py +index c462177b7..5d772191b 100755 +--- a/files/usr/share/cinnamon/cinnamon-settings/modules/cs_backgrounds.py ++++ b/files/usr/share/cinnamon/cinnamon-settings/modules/cs_backgrounds.py +@@ -1,7 +1,6 @@ + #!/usr/bin/python3 + + import os +-import imtools + import gettext + import _thread as thread + import subprocess +@@ -623,6 +622,8 @@ class PixCache(object): + img = img.convert("RGB") + if size: + img.thumbnail((size, size), Image.ANTIALIAS) ++ ++ import imtools + img = imtools.round_image(img, {}, False, None, 3, 255) + img = imtools.drop_shadow(img, 4, 4, background_color=(255, 255, 255, 0), + shadow_color=0x444444, border=8, shadow_blur=3, +-- +2.34.1 + diff --git a/recipes-cinnamon/cinnamon/cinnamon/okaestne-settings-performance/0004-Spices-defer-import-of-requests-module.patch b/recipes-cinnamon/cinnamon/cinnamon/okaestne-settings-performance/0004-Spices-defer-import-of-requests-module.patch new file mode 100644 index 0000000..64d82ee --- /dev/null +++ b/recipes-cinnamon/cinnamon/cinnamon/okaestne-settings-performance/0004-Spices-defer-import-of-requests-module.patch @@ -0,0 +1,37 @@ +From 1358b9df2b18fb7ecb5077cdf54b427cf47e26ac Mon Sep 17 00:00:00 2001 +From: okaestne +Date: Mon, 4 Apr 2022 00:55:26 +0200 +Subject: [PATCH 4/6] Spices: defer import of requests module + +saves ~25ms import time +--- + files/usr/share/cinnamon/cinnamon-settings/bin/Spices.py | 5 +++-- + 1 file changed, 3 insertions(+), 2 deletions(-) + +diff --git a/files/usr/share/cinnamon/cinnamon-settings/bin/Spices.py b/files/usr/share/cinnamon/cinnamon-settings/bin/Spices.py +index f345243f3..348d9488d 100644 +--- a/files/usr/share/cinnamon/cinnamon-settings/bin/Spices.py ++++ b/files/usr/share/cinnamon/cinnamon-settings/bin/Spices.py +@@ -12,9 +12,7 @@ try: + import threading + from PIL import Image + import datetime +- import proxygsettings + import time +- import requests + except Exception as detail: + print(detail) + sys.exit(1) +@@ -382,6 +380,9 @@ class Spice_Harvester(GObject.Object): + #Like the one in urllib. Unlike urllib.retrieve url_retrieve + #can be interrupted. KeyboardInterrupt exception is raised when + #interrupted. ++ import proxygsettings ++ import requests ++ + count = 0 + blockSize = 1024 * 8 + proxy_info = proxygsettings.get_proxy_settings() +-- +2.34.1 + diff --git a/recipes-cinnamon/cinnamon/cinnamon/okaestne-settings-performance/0005-cs-fix-print_timing-remove-stale-touch-function.patch b/recipes-cinnamon/cinnamon/cinnamon/okaestne-settings-performance/0005-cs-fix-print_timing-remove-stale-touch-function.patch new file mode 100644 index 0000000..7914a9c --- /dev/null +++ b/recipes-cinnamon/cinnamon/cinnamon/okaestne-settings-performance/0005-cs-fix-print_timing-remove-stale-touch-function.patch @@ -0,0 +1,40 @@ +From 0aa0e62dd49e74253d132c7bd9a900af03bbc421 Mon Sep 17 00:00:00 2001 +From: okaestne +Date: Sat, 9 Apr 2022 18:50:32 +0200 +Subject: [PATCH 5/6] cs: fix print_timing; remove stale touch function + +--- + .../cinnamon/cinnamon-settings/cinnamon-settings.py | 11 +++-------- + 1 file changed, 3 insertions(+), 8 deletions(-) + +diff --git a/files/usr/share/cinnamon/cinnamon-settings/cinnamon-settings.py b/files/usr/share/cinnamon/cinnamon-settings/cinnamon-settings.py +index 65d4a3028..7dee496e0 100755 +--- a/files/usr/share/cinnamon/cinnamon-settings/cinnamon-settings.py ++++ b/files/usr/share/cinnamon/cinnamon-settings/cinnamon-settings.py +@@ -152,20 +152,15 @@ ARG_REWRITE = { + + def print_timing(func): + # decorate functions with @print_timing to output how long they take to run. +- def wrapper(*arg): ++ def wrapper(*args, **kwargs): + t1 = time.time() +- res = func(*arg) ++ res = func(*args, **kwargs) + t2 = time.time() +- print('%s took %0.3f ms' % (func.func_name, (t2-t1)*1000.0)) ++ print('%s took %0.3f ms' % (func.__name__, (t2-t1)*1000.0)) + return res + return wrapper + + +-def touch(fname, times=None): +- with open(fname, 'a'): +- os.utime(fname, times) +- +- + class MainWindow(Gio.Application): + # Change pages + def side_view_nav(self, side_view, path, cat): +-- +2.34.1 + diff --git a/recipes-cinnamon/cinnamon/cinnamon/okaestne-settings-performance/0006-cs-lazy-load-python-modules-when-passed-as-arg.patch b/recipes-cinnamon/cinnamon/cinnamon/okaestne-settings-performance/0006-cs-lazy-load-python-modules-when-passed-as-arg.patch new file mode 100644 index 0000000..96b96de --- /dev/null +++ b/recipes-cinnamon/cinnamon/cinnamon/okaestne-settings-performance/0006-cs-lazy-load-python-modules-when-passed-as-arg.patch @@ -0,0 +1,489 @@ +From 7215d33cfb9a4fecc4dcf94ce4fd3d757d1bcf30 Mon Sep 17 00:00:00 2001 +From: okaestne +Date: Sat, 9 Apr 2022 22:52:05 +0200 +Subject: [PATCH 6/6] cs: lazy load python modules, when passed as arg + +--- + .../cinnamon-settings/cinnamon-settings.py | 358 ++++++++++-------- + 1 file changed, 204 insertions(+), 154 deletions(-) + +diff --git a/files/usr/share/cinnamon/cinnamon-settings/cinnamon-settings.py b/files/usr/share/cinnamon/cinnamon-settings/cinnamon-settings.py +index 7dee496e0..41746c16f 100755 +--- a/files/usr/share/cinnamon/cinnamon-settings/cinnamon-settings.py ++++ b/files/usr/share/cinnamon/cinnamon-settings/cinnamon-settings.py +@@ -1,56 +1,47 @@ + #!/usr/bin/python3 +- +-import getopt +-import sys +- + from bin import util + util.strip_syspath_locals() + +-import os +-import glob ++from functools import cmp_to_key ++import getopt + import gettext ++import glob ++import locale ++import os ++from setproctitle import setproctitle ++import sys + import time + import traceback +-import locale +-import urllib.request as urllib +-from functools import cmp_to_key ++import typing + import unicodedata +-import config +-from setproctitle import setproctitle ++import urllib.request as urllib + + import gi + gi.require_version('Gtk', '3.0') + gi.require_version('XApp', '1.0') + from gi.repository import Gio, Gtk, Pango, Gdk, XApp + +-sys.path.append(config.currentPath + "/modules") +-sys.path.append(config.currentPath + "/bin") +-import capi +-import proxygsettings +-import SettingsWidgets ++import config ++sys.path.append(os.path.join(config.currentPath, "bin")) ++sys.path.append(os.path.join(config.currentPath, "modules")) ++from bin import capi ++from bin import proxygsettings ++from bin import SettingsWidgets + + # i18n + gettext.install("cinnamon", "/usr/share/locale", names=["ngettext"]) + +-# Standard setting pages... this can be expanded to include applet dirs maybe? +-mod_files = glob.glob(config.currentPath + "/modules/*.py") +-mod_files.sort() +-if len(mod_files) == 0: +- print("No settings modules found!!") +- sys.exit(1) +- +-mod_files = [x.split('/')[-1].split('.')[0] for x in mod_files] +- +-for mod_file in mod_files: +- if mod_file[0:3] != "cs_": +- raise Exception("Settings modules must have a prefix of 'cs_' !!") +- +-modules = map(__import__, mod_files) +- + # i18n for menu item + menuName = _("System Settings") + menuComment = _("Control Center") + ++ ++class SidePageData(typing.NamedTuple): ++ sp: SettingsWidgets.SidePage ++ name: str ++ cat: str ++ ++ + WIN_WIDTH = 800 + WIN_HEIGHT = 600 + WIN_H_PADDING = 20 +@@ -169,7 +160,9 @@ class MainWindow(Gio.Application): + self.deselect(cat) + filtered_path = side_view.get_model().convert_path_to_child_path(selected_items[0]) + if filtered_path is not None: +- self.go_to_sidepage(cat, filtered_path, user_action=True) ++ iterator = self.store_by_cat[cat].get_iter(filtered_path) ++ sidePage = self.store_by_cat[cat].get_value(iterator, 2) ++ self.go_to_sidepage(sidePage, user_action=True) + + def _on_sidepage_hide_stack(self): + self.stack_switcher.set_opacity(0) +@@ -177,57 +170,56 @@ class MainWindow(Gio.Application): + def _on_sidepage_show_stack(self): + self.stack_switcher.set_opacity(1) + +- def go_to_sidepage(self, cat, path, user_action=True): +- iterator = self.store[cat].get_iter(path) +- sidePage = self.store[cat].get_value(iterator, 2) +- if not sidePage.is_standalone: +- if not user_action: +- self.window.set_title(sidePage.name) +- self.window.set_icon_name(sidePage.icon) +- sidePage.build() +- if sidePage.stack: +- self.stack_switcher.set_stack(sidePage.stack) +- l = sidePage.stack.get_children() +- if len(l) > 0: +- if self.tab in range(len(l)): +- sidePage.stack.set_visible_child(l[self.tab]) +- visible_child = sidePage.stack.get_visible_child() +- if self.tab == 1 \ +- and hasattr(visible_child, 'sort_combo') \ +- and self.sort in range(5): +- visible_child.sort_combo.set_active(self.sort) +- visible_child.sort_changed() +- else: +- sidePage.stack.set_visible_child(l[0]) +- if sidePage.stack.get_visible(): +- self.stack_switcher.set_opacity(1) +- else: +- self.stack_switcher.set_opacity(0) +- if hasattr(sidePage, "connect_proxy"): +- sidePage.connect_proxy("hide_stack", self._on_sidepage_hide_stack) +- sidePage.connect_proxy("show_stack", self._on_sidepage_show_stack) ++ def go_to_sidepage(self, sidePage: SettingsWidgets.SidePage, user_action=True): ++ sidePage.build() ++ ++ if sidePage.is_standalone: ++ return # we're done ++ ++ if not user_action: ++ self.window.set_title(sidePage.name) ++ self.window.set_icon_name(sidePage.icon) ++ ++ if sidePage.stack: ++ self.stack_switcher.set_stack(sidePage.stack) ++ l = sidePage.stack.get_children() ++ if len(l) > 0: ++ if self.tab in range(len(l)): ++ sidePage.stack.set_visible_child(l[self.tab]) ++ visible_child = sidePage.stack.get_visible_child() ++ if self.tab == 1 \ ++ and hasattr(visible_child, 'sort_combo') \ ++ and self.sort in range(5): ++ visible_child.sort_combo.set_active(self.sort) ++ visible_child.sort_changed() ++ else: ++ sidePage.stack.set_visible_child(l[0]) ++ if sidePage.stack.get_visible(): ++ self.stack_switcher.set_opacity(1) + else: + self.stack_switcher.set_opacity(0) ++ if hasattr(sidePage, "connect_proxy"): ++ sidePage.connect_proxy("hide_stack", self._on_sidepage_hide_stack) ++ sidePage.connect_proxy("show_stack", self._on_sidepage_show_stack) + else: + self.stack_switcher.set_opacity(0) ++ else: ++ self.stack_switcher.set_opacity(0) + +- if user_action: +- self.main_stack.set_visible_child_name("content_box_page") +- self.header_stack.set_visible_child_name("content_box") +- +- else: +- self.main_stack.set_visible_child_full("content_box_page", Gtk.StackTransitionType.NONE) +- self.header_stack.set_visible_child_full("content_box", Gtk.StackTransitionType.NONE) +- +- self.current_sidepage = sidePage +- width = 0 +- for widget in self.top_bar: +- m, n = widget.get_preferred_width() +- width += n +- self.top_bar.set_size_request(width + 20, -1) +- self.maybe_resize(sidePage) ++ if user_action: ++ self.main_stack.set_visible_child_name("content_box_page") ++ self.header_stack.set_visible_child_name("content_box") + else: +- sidePage.build() ++ self.main_stack.set_visible_child_full("content_box_page", Gtk.StackTransitionType.NONE) ++ self.header_stack.set_visible_child_full("content_box", Gtk.StackTransitionType.NONE) ++ ++ self.current_sidepage = sidePage ++ width = 0 ++ for widget in self.top_bar: ++ m, n = widget.get_preferred_width() ++ width += n ++ self.top_bar.set_size_request(width + 20, -1) ++ self.maybe_resize(sidePage) + + def maybe_resize(self, sidePage): + m, n = self.content_box.get_preferred_size() +@@ -260,7 +252,7 @@ class MainWindow(Gio.Application): + flags=Gio.ApplicationFlags.NON_UNIQUE | Gio.ApplicationFlags.HANDLES_OPEN) + self.builder = Gtk.Builder() + self.builder.set_translation_domain('cinnamon') # let it translate! +- self.builder.add_from_file(config.currentPath + "/cinnamon-settings.ui") ++ self.builder.add_from_file(os.path.join(config.currentPath, "cinnamon-settings.ui")) + self.window = XApp.GtkWindow(window_position=Gtk.WindowPosition.CENTER, + default_width=800, default_height=600) + +@@ -298,8 +290,7 @@ class MainWindow(Gio.Application): + self.window.connect("destroy", self._quit) + + self.builder.connect_signals(self) +- self.unsortedSidePages = [] +- self.sidePages = [] ++ self.sidePages: typing.List[SidePageData] = [] + self.settings = Gio.Settings.new("org.cinnamon") + self.current_cat_widget = None + +@@ -308,58 +299,61 @@ class MainWindow(Gio.Application): + self.content_box.c_manager = self.c_manager + self.bar_heights = 0 + +- for module in modules: +- try: +- mod = module.Module(self.content_box) +- if self.loadCheck(mod) and self.setParentRefs(mod): +- self.unsortedSidePages.append((mod.sidePage, mod.name, mod.category)) +- except: +- print("Failed to load module %s" % module) +- traceback.print_exc() ++ self.tab = 0 # open 'manage' tab by default ++ self.sort = 1 # sorted by 'score' by default + +- for item in CONTROL_CENTER_MODULES: +- ccmodule = SettingsWidgets.CCModule(item[0], item[1], item[2], item[3], item[4], self.content_box) +- if ccmodule.process(self.c_manager): +- self.unsortedSidePages.append((ccmodule.sidePage, ccmodule.name, ccmodule.category)) ++ self.store_by_cat: typing.Dict[str, Gtk.ListStore] = {} ++ self.storeFilter = {} + +- for item in STANDALONE_MODULES: +- samodule = SettingsWidgets.SAModule(item[0], item[1], item[2], item[3], item[4], self.content_box) +- if samodule.process(): +- self.unsortedSidePages.append((samodule.sidePage, samodule.name, samodule.category)) ++ # load CCC and standalone modules, but not python modules yet ++ self.load_ccc_modules() ++ self.load_standalone_modules() + +- # sort the modules alphabetically according to the current locale ++ # if a certain sidepage is given via arguments, try to load only it ++ if len(sys.argv) > 1: ++ if self.load_sidepage_as_standalone(): ++ return ++ ++ self.init_settings_overview() ++ ++ def init_settings_overview(self): ++ """Load the system settings overview (default) ++ ++ This requires to initialize all settings modules. ++ """ ++ # 1. load all python modules ++ self.load_python_modules() ++ ++ # 2. sort the modules alphabetically according to the current locale + localeStrKey = cmp_to_key(locale.strcoll) + # Apply locale key to the field name of each side page. + sidePagesKey = lambda m: localeStrKey(m[0].name) +- self.sidePages = sorted(self.unsortedSidePages, key=sidePagesKey) ++ self.sidePages = sorted(self.sidePages, key=sidePagesKey) + +- # create the backing stores for the side nav-view. +- sidePagesIters = {} +- self.store = {} +- self.storeFilter = {} ++ # 3. create the backing stores for the side nav-view. + for sidepage in self.sidePages: + sp, sp_id, sp_cat = sidepage +- if sp_cat not in self.store: # Label Icon sidePage Category +- self.store[sidepage[2]] = Gtk.ListStore(str, str, object, str) ++ if sidepage.cat not in self.store_by_cat: ++ self.store_by_cat[sidepage.cat] = Gtk.ListStore(str, str, object, str) # Label, Icon, sidePage, Category + for category in CATEGORIES: +- if category["id"] == sp_cat: ++ if category["id"] == sidepage.cat: + category["show"] = True + + # Don't allow item names (and their translations) to be more than 30 chars long. It looks ugly and it creates huge gaps in the icon views + name = sp.name + if len(name) > 30: + name = "%s..." % name[:30] +- sidePagesIters[sp_id] = (self.store[sp_cat].append([name, sp.icon, sp, sp_cat]), sp_cat) ++ self.store_by_cat[sp_cat].append([name, sp.icon, sp, sp_cat]) + + self.min_label_length = 0 + self.min_pix_length = 0 + +- for key in self.store: +- char, pix = self.get_label_min_width(self.store[key]) ++ for cat in self.store_by_cat: ++ char, pix = self.get_label_min_width(self.store_by_cat[cat]) + self.min_label_length = max(char, self.min_label_length) + self.min_pix_length = max(pix, self.min_pix_length) +- self.storeFilter[key] = self.store[key].filter_new() +- self.storeFilter[key].set_visible_func(self.filter_visible_function) ++ self.storeFilter[cat] = self.store_by_cat[cat].filter_new() ++ self.storeFilter[cat].set_visible_func(self.filter_visible_function) + + self.min_label_length += 2 + self.min_pix_length += 4 +@@ -378,38 +372,55 @@ class MainWindow(Gio.Application): + + self.calculate_bar_heights() + +- self.tab = 0 # open 'manage' tab by default +- self.sort = 1 # sorted by 'score' by default ++ self.search_entry.grab_focus() ++ self.window.connect("key-press-event", self.on_keypress) ++ self.window.connect("button-press-event", self.on_buttonpress) ++ ++ self.window.show() ++ ++ def load_sidepage_as_standalone(self) -> bool: ++ """ ++ When an explicit sidepage is given as an argument, ++ try load only this module to save much startup time. ++ ++ Analyses arguments to know the tab to open ++ and the sort to apply if the tab is the 'more' one. ++ ++ Examples: ++ ``` ++ cinnamon-settings.py applets --tab=more --sort=date ++ cinnamon-settings.py applets --tab=1 --sort=2 ++ cinnamon-settings.py applets --tab=more --sort=date ++ cinnamon-settings.py applets --tab=1 -s 2 ++ cinnamon-settings.py applets -t 1 -s installed ++ cinnamon-settings.py desklets -t 2 ++ ``` ++ Please note that useless or wrong arguments are ignored. ++ ++ :return: True if sidepage was loaded successfully, False otherwise ++ """ ++ if sys.argv == 1: ++ return False + +- # Select the first sidePage ++ # (1) get the settings sidepage name and rewrite it if necessary ++ sidepage_name = ARG_REWRITE.get(sys.argv[1], sys.argv[1]) ++ # pop the arg once we consume it so we don't pass it go Gio.application.run ++ sys.argv.pop(1) ++ ++ # (2) Try to load a matching python module. ++ # Note: the requested module could also be a CCC or SA module (which are always loaded by __init__()) ++ self.load_python_modules(only_module=sidepage_name) ++ ++ # (3) set tab to show and/or spices sorting if specified via args + if len(sys.argv) > 1: +- arg1 = sys.argv[1] +- if arg1 in ARG_REWRITE.keys(): +- arg1 = ARG_REWRITE[arg1] +- if len(sys.argv) > 1 and arg1 in sidePagesIters: +- # Analyses arguments to know the tab to open +- # and the sort to apply if the tab is the 'more' one. +- # Examples: +- # cinnamon-settings.py applets --tab=more --sort=date +- # cinnamon-settings.py applets --tab=1 --sort=2 +- # cinnamon-settings.py applets --tab=more --sort=date +- # cinnamon-settings.py applets --tab=1 -s 2 +- # cinnamon-settings.py applets -t 1 -s installed +- # cinnamon-settings.py desklets -t 2 +- # Please note that useless or wrong arguments are ignored. + opts = [] + sorts_literal = {"name":0, "score":1, "date":2, "installed":3, "update":4} +- tabs_literal = {"default":0} +- if arg1 in TABS.keys(): +- tabs_literal = TABS[arg1] ++ tabs_literal = TABS.get(sidepage_name, {"default": 0}) + + try: +- if len(sys.argv) > 2: +- opts = getopt.getopt(sys.argv[2:], "t:s:", ["tab=", "sort="])[0] ++ opts = getopt.getopt(sys.argv[1:], "t:s:", ["tab=", "sort="])[0] + except getopt.GetoptError: +- pass +- # pop the arg once we consume it so we don't pass it go Gio.application.run +- sys.argv.pop(1) ++ pass # ignore unknown args + + for opt, arg in opts: + if opt in ("-t", "--tab"): +@@ -426,30 +437,69 @@ class MainWindow(Gio.Application): + sys.argv.remove(opt) + sys.argv.remove(arg) + +- # If we're launching a module directly, set the WM class so GWL +- # can consider it as a standalone app and give it its own +- # group. +- wm_class = "cinnamon-settings %s" % arg1 +- self.window.set_wmclass(wm_class, wm_class) +- self.button_back.hide() +- (iter, cat) = sidePagesIters[arg1] +- path = self.store[cat].get_path(iter) +- if path: +- self.go_to_sidepage(cat, path, user_action=False) +- self.window.show() +- if arg1 in ("mintlocale", "blueberry", "system-config-printer", "mintlocale-im", "nvidia-settings"): ++ # (4) set the WM class so GWL can consider it as a standalone app and give it its own group. ++ wm_class = f"cinnamon-settings {sidepage_name}" ++ self.window.set_wmclass(wm_class, wm_class) ++ self.button_back.hide() ++ ++ # (5) find and show it ++ for sp_data in self.sidePages: ++ if sp_data.name == sidepage_name: ++ self.go_to_sidepage(sp_data.sp, user_action=False) ++ if sp_data.sp.is_standalone: + # These modules do not need to leave the System Settings window open, + # when selected by command line argument. + self.window.close() ++ else: ++ self.window.show() ++ return True ++ print(f"warning: settings module {sidepage_name} not found.") ++ return False ++ ++ def load_ccc_modules(self): ++ """Loads all Cinnamon Control Center settings modules.""" ++ for item in CONTROL_CENTER_MODULES: ++ ccmodule = SettingsWidgets.CCModule(item[0], item[1], item[2], item[3], item[4], self.content_box) ++ if ccmodule.process(self.c_manager): ++ self.sidePages.append(SidePageData(ccmodule.sidePage, ccmodule.name, ccmodule.category)) + else: +- self.search_entry.grab_focus() +- self.window.show() +- else: +- self.search_entry.grab_focus() +- self.window.connect("key-press-event", self.on_keypress) +- self.window.connect("button-press-event", self.on_buttonpress) ++ print("warning: failed to process CCC module", item[1]) ++ ++ def load_standalone_modules(self): ++ """Loads all standalone settings modules.""" ++ for item in STANDALONE_MODULES: ++ samodule = SettingsWidgets.SAModule(item[0], item[1], item[2], item[3], item[4], self.content_box) ++ if samodule.process(): ++ self.sidePages.append(SidePageData(samodule.sidePage, samodule.name, samodule.category)) ++ # else: ++ # print(f"note: skipped standalone module {samodule.name} (not found in PATH).") ++ ++ def load_python_modules(self, only_module: str = None) -> bool: ++ """Loads all or only a given settings module(s) written in python. ++ ++ :param only_module: (optional) module name to be loaded exclusively ++ :return: True if successful, False otherwise ++ """ ++ # Standard setting pages... this can be expanded to include applet dirs maybe? ++ mod_files = glob.glob(os.path.join(config.currentPath, 'modules', 'cs_*.py')) ++ if len(mod_files) == 0: ++ print("warning: no python settings modules found!!", file=sys.stderr) ++ return False ++ ++ to_import = [os.path.splitext(os.path.basename(x))[0] for x in mod_files] ++ ++ if only_module is not None: ++ to_import = filter(lambda mod: only_module in mod, to_import) + +- self.window.show() ++ for module in map(__import__, to_import): ++ try: ++ mod = module.Module(self.content_box) ++ if self.loadCheck(mod) and self.setParentRefs(mod): ++ self.sidePages.append(SidePageData(mod.sidePage, mod.name, mod.category)) ++ except: ++ print(f"failed to load python module {module}", file=sys.stderr) ++ traceback.print_exc() ++ return True + + # If there are no arguments, do_active() is called, otherwise do_open(). + def do_activate(self): +-- +2.34.1 + diff --git a/recipes-cinnamon/cinnamon/cinnamon/okaestne-settings-performance/README b/recipes-cinnamon/cinnamon/cinnamon/okaestne-settings-performance/README new file mode 100644 index 0000000..b811f59 --- /dev/null +++ b/recipes-cinnamon/cinnamon/cinnamon/okaestne-settings-performance/README @@ -0,0 +1,2 @@ +Patches taken at 2022-05-05 / see https://github.com/linuxmint/cinnamon/pull/10735 +