1919"""This module contains utility functions for Safe Eyes and its plugins."""
2020
2121import errno
22+ import hashlib
2223import inspect
2324import importlib
2425import json
3839import gi
3940
4041gi .require_version ("Gtk" , "4.0" )
42+ gi .require_version ("Gdk" , "4.0" )
43+
44+ from gi .repository import Gdk
4145from gi .repository import Gtk
4246from gi .repository import GLib
4347from gi .repository import GdkPixbuf
4448from packaging .version import parse
4549
46- gi .require_version ("Gdk" , "4.0" )
47-
4850BIN_DIRECTORY = os .path .dirname (os .path .realpath (__file__ ))
4951HOME_DIRECTORY = os .environ .get ("HOME" ) or os .path .expanduser ("~" )
5052CONFIG_DIRECTORY = os .path .join (
5557CONFIG_FILE_PATH = os .path .join (CONFIG_DIRECTORY , "safeeyes.json" )
5658CONFIG_RESOURCE = os .path .join (CONFIG_DIRECTORY , "resource" )
5759SESSION_FILE_PATH = os .path .join (CONFIG_DIRECTORY , "session.json" )
58- STYLE_SHEET_PATH = os .path .join (STYLE_SHEET_DIRECTORY , "safeeyes_style.css" )
60+ OLD_STYLE_SHEET_PATH = os .path .join (STYLE_SHEET_DIRECTORY , "safeeyes_style.css" )
61+ CUSTOM_STYLE_SHEET_PATH = os .path .join (
62+ STYLE_SHEET_DIRECTORY , "safeeyes_custom_style.css"
63+ )
5964SYSTEM_CONFIG_FILE_PATH = os .path .join (BIN_DIRECTORY , "config/safeeyes.json" )
6065SYSTEM_STYLE_SHEET_PATH = os .path .join (BIN_DIRECTORY , "config/style/safeeyes_style.css" )
6166LOG_FILE_PATH = os .path .join (HOME_DIRECTORY , "safeeyes.log" )
@@ -372,8 +377,32 @@ def merge_configs(new_config, old_config):
372377 return new_config
373378
374379
380+ def sha256sum (filename ):
381+ """Get the sha256 hash of the given file."""
382+ h = hashlib .sha256 ()
383+ b = bytearray (128 * 1024 )
384+ mv = memoryview (b )
385+ with open (filename , "rb" , buffering = 0 ) as f :
386+ for n in iter (lambda : f .readinto (mv ), 0 ):
387+ h .update (mv [:n ])
388+ return h .hexdigest ()
389+
390+
391+ def load_css_file (style_sheet_path , priority , required = True ):
392+ if not os .path .isfile (style_sheet_path ):
393+ if required :
394+ logging .warning ("Failed loading required stylesheet" )
395+ return
396+
397+ css_provider = Gtk .CssProvider ()
398+ css_provider .load_from_path (style_sheet_path )
399+
400+ display = Gdk .Display .get_default ()
401+ Gtk .StyleContext .add_provider_for_display (display , css_provider , priority )
402+
403+
375404def initialize_safeeyes ():
376- """Create the config file and style sheet in XDG_CONFIG_HOME(or
405+ """Create the config file in XDG_CONFIG_HOME(or
377406 ~/.config)/safeeyes directory.
378407 """
379408 logging .info ("Copy the config files to XDG_CONFIG_HOME(or ~/.config)/safeeyes" )
@@ -388,24 +417,45 @@ def initialize_safeeyes():
388417 shutil .copy2 (SYSTEM_CONFIG_FILE_PATH , CONFIG_FILE_PATH )
389418 os .chmod (CONFIG_FILE_PATH , 0o666 )
390419
391- create_user_stylesheet_if_missing ()
392-
393420 # initialize_safeeyes gets called when the configuration file is not present, which
394421 # happens just after installation or manual deletion of
395422 # .config/safeeyes/safeeyes.json file. In these cases, we want to force the creation
396423 # of a startup entry
397424 create_startup_entry (force = True )
398425
399426
400- def create_user_stylesheet_if_missing ():
427+ def cleanup_old_user_stylesheet ():
401428 # Create the XDG_CONFIG_HOME(or ~/.config)/safeeyes/style directory
402429 if not os .path .isdir (STYLE_SHEET_DIRECTORY ):
403430 mkdir (STYLE_SHEET_DIRECTORY )
404431
405- # Copy the new style sheet
406- if not os .path .isfile (STYLE_SHEET_PATH ):
407- shutil .copy2 (SYSTEM_STYLE_SHEET_PATH , STYLE_SHEET_PATH )
408- os .chmod (STYLE_SHEET_PATH , 0o666 )
432+ # Delete the old stylesheet, unless it has customizations
433+ if os .path .isfile (OLD_STYLE_SHEET_PATH ):
434+ hash = sha256sum (OLD_STYLE_SHEET_PATH )
435+ old_default_versions = [
436+ # 2.2.3
437+ "fdc2a305613ae4eeb269650452789d35df3df5bdf1c56eb576cd5ebac70a6f09" ,
438+ # 2.1.0 - 2.2.2
439+ "fbde048fc234db757461971a7542df43a467869035ca3d05ff9b236ca250e4c5" ,
440+ # 2.0.9
441+ "70ca55c12d83ad7a6a4e1c5e7758a38617a43f5d32f28709ede75426d3186713" ,
442+ # 2.0.7 - 2.0.8
443+ "7a15f985e0da6d92c8a62d49ce151781e6d423f87e66d205cc1dc4536e369e19" ,
444+ # 2.0.6 and earlier
445+ "f26621a883e323ca7685a4adba25027e70daa471e0db4a21c261e6c15caaa5ee" ,
446+ ]
447+ if hash in old_default_versions :
448+ logging .info ("Deleting old stylesheet containing default content" )
449+ delete (OLD_STYLE_SHEET_PATH )
450+ else :
451+ # Stylesheet was likely customized, don't delete but warn
452+ logging .warning (
453+ _ (
454+ "Old stylesheet found at '%(old)s', ignoring. For custom styles, "
455+ "create a new stylesheet in '%(new)s' instead." ,
456+ )
457+ % {"old" : OLD_STYLE_SHEET_PATH , "new" : CUSTOM_STYLE_SHEET_PATH }
458+ )
409459
410460
411461def create_startup_entry (force = False ):
@@ -517,26 +567,16 @@ def initialize_platform():
517567def reset_config ():
518568 # Remove the ~/.config/safeeyes/safeeyes.json and safeeyes_style.css
519569 delete (CONFIG_FILE_PATH )
520- delete (STYLE_SHEET_PATH )
521570
522571 # Copy the safeeyes.json and safeeyes_style.css
523572 shutil .copy2 (SYSTEM_CONFIG_FILE_PATH , CONFIG_FILE_PATH )
524- shutil .copy2 (SYSTEM_STYLE_SHEET_PATH , STYLE_SHEET_PATH )
525573
526574 # Add write permission (e.g. if original file was stored in /nix/store)
527575 os .chmod (CONFIG_FILE_PATH , 0o666 )
528- os .chmod (STYLE_SHEET_PATH , 0o666 )
529576
530577 create_startup_entry ()
531578
532579
533- def replace_style_sheet ():
534- """Replace the user style sheet by system style sheet."""
535- delete (STYLE_SHEET_PATH )
536- shutil .copy2 (SYSTEM_STYLE_SHEET_PATH , STYLE_SHEET_PATH )
537- os .chmod (STYLE_SHEET_PATH , 0o666 )
538-
539-
540580def initialize_logging (debug ):
541581 """Initialize the logging framework using the Safe Eyes specific
542582 configurations.
0 commit comments