cinnamon: Add patches to accelerate settings

Signed-off-by: Andreas Müller <schnitzeltony@gmail.com>
This commit is contained in:
Andreas Müller
2022-05-05 23:37:10 +02:00
parent b863ac9564
commit 41880e3a91
8 changed files with 806 additions and 0 deletions

View File

@@ -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}"

View File

@@ -0,0 +1,101 @@
From 03599325d88f453b464f8c500fea0e758dd6a386 Mon Sep 17 00:00:00 2001
From: okaestne <git@oliver-kaestner.de>
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

View File

@@ -0,0 +1,97 @@
From b20036b87aa78242e11b4e5f023b4f8586cfe1d9 Mon Sep 17 00:00:00 2001
From: okaestne <git@oliver-kaestner.de>
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

View File

@@ -0,0 +1,34 @@
From b7f582e2500959e89c56837675cfe454892846ce Mon Sep 17 00:00:00 2001
From: okaestne <git@oliver-kaestner.de>
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

View File

@@ -0,0 +1,37 @@
From 1358b9df2b18fb7ecb5077cdf54b427cf47e26ac Mon Sep 17 00:00:00 2001
From: okaestne <git@oliver-kaestner.de>
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

View File

@@ -0,0 +1,40 @@
From 0aa0e62dd49e74253d132c7bd9a900af03bbc421 Mon Sep 17 00:00:00 2001
From: okaestne <git@oliver-kaestner.de>
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

View File

@@ -0,0 +1,489 @@
From 7215d33cfb9a4fecc4dcf94ce4fd3d757d1bcf30 Mon Sep 17 00:00:00 2001
From: okaestne <git@oliver-kaestner.de>
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

View File

@@ -0,0 +1,2 @@
Patches taken at 2022-05-05 / see https://github.com/linuxmint/cinnamon/pull/10735