Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion safeeyes/__main__.py
Original file line number Diff line number Diff line change
Expand Up @@ -130,7 +130,7 @@ def main():
utility.initialize_logging(args.debug)
utility.initialize_platform()
config = Config()
utility.create_user_stylesheet_if_missing()
utility.cleanup_old_user_stylesheet()

if __running():
logging.info("Safe Eyes is already running")
Expand Down
4 changes: 4 additions & 0 deletions safeeyes/config/locale/safeeyes.pot
Original file line number Diff line number Diff line change
Expand Up @@ -557,3 +557,7 @@ msgstr ""

msgid "License:"
msgstr ""

#, python-format
msgid "Old stylesheet found at '%(old)s', ignoring. For custom styles, create a new stylesheet in '%(new)s' instead."
msgstr ""
2 changes: 0 additions & 2 deletions safeeyes/model.py
Original file line number Diff line number Diff line change
Expand Up @@ -327,8 +327,6 @@ def __init__(self, init=True):
self.__user_config, self.__system_config
)
self.__user_config = self.__system_config
# Update the style sheet
utility.replace_style_sheet()

utility.merge_plugins(self.__user_config)
self.save()
Expand Down
19 changes: 14 additions & 5 deletions safeeyes/safeeyes.py
Original file line number Diff line number Diff line change
Expand Up @@ -104,12 +104,11 @@ def do_startup(self):
else:
self.context["session"] = {"plugin": {}}

# Initialize the theme
self._initialize_styles()

self.break_screen = BreakScreen(
self,
self.context,
self.on_skipped,
self.on_postponed,
utility.STYLE_SHEET_PATH,
self, self.context, self.on_skipped, self.on_postponed
)
self.break_screen.initialize(self.config)
self.plugins_manager = PluginManager()
Expand Down Expand Up @@ -166,6 +165,16 @@ def do_activate(self):
elif self.cli_args.take_break:
self.take_break()

def _initialize_styles(self):
utility.load_css_file(
utility.SYSTEM_STYLE_SHEET_PATH, Gtk.STYLE_PROVIDER_PRIORITY_APPLICATION
)
utility.load_css_file(
utility.CUSTOM_STYLE_SHEET_PATH,
Gtk.STYLE_PROVIDER_PRIORITY_USER,
required=False,
)

def _retry_errored_plugins(self):
if not self.plugins_manager.needs_retry():
return
Expand Down
13 changes: 1 addition & 12 deletions safeeyes/ui/break_screen.py
Original file line number Diff line number Diff line change
Expand Up @@ -43,9 +43,7 @@ class BreakScreen:
interface.
"""

def __init__(
self, application, context, on_skipped, on_postponed, style_sheet_path
):
def __init__(self, application, context, on_skipped, on_postponed):
self.application = application
self.context = context
self.count_labels = []
Expand All @@ -64,15 +62,6 @@ def __init__(
if not self.context["is_wayland"]:
self.x11_display = Display()

# Initialize the theme
css_provider = Gtk.CssProvider()
css_provider.load_from_path(style_sheet_path)

display = Gdk.Display.get_default()
Gtk.StyleContext.add_provider_for_display(
display, css_provider, Gtk.STYLE_PROVIDER_PRIORITY_APPLICATION
)

def initialize(self, config):
"""Initialize the internal properties from configuration."""
logging.info("Initialize the break screen")
Expand Down
82 changes: 61 additions & 21 deletions safeeyes/utility.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
"""This module contains utility functions for Safe Eyes and its plugins."""

import errno
import hashlib
import inspect
import importlib
import json
Expand All @@ -38,13 +39,14 @@
import gi

gi.require_version("Gtk", "4.0")
gi.require_version("Gdk", "4.0")

from gi.repository import Gdk
from gi.repository import Gtk
from gi.repository import GLib
from gi.repository import GdkPixbuf
from packaging.version import parse

gi.require_version("Gdk", "4.0")

BIN_DIRECTORY = os.path.dirname(os.path.realpath(__file__))
HOME_DIRECTORY = os.environ.get("HOME") or os.path.expanduser("~")
CONFIG_DIRECTORY = os.path.join(
Expand All @@ -55,7 +57,10 @@
CONFIG_FILE_PATH = os.path.join(CONFIG_DIRECTORY, "safeeyes.json")
CONFIG_RESOURCE = os.path.join(CONFIG_DIRECTORY, "resource")
SESSION_FILE_PATH = os.path.join(CONFIG_DIRECTORY, "session.json")
STYLE_SHEET_PATH = os.path.join(STYLE_SHEET_DIRECTORY, "safeeyes_style.css")
OLD_STYLE_SHEET_PATH = os.path.join(STYLE_SHEET_DIRECTORY, "safeeyes_style.css")
CUSTOM_STYLE_SHEET_PATH = os.path.join(
STYLE_SHEET_DIRECTORY, "safeeyes_custom_style.css"
)
SYSTEM_CONFIG_FILE_PATH = os.path.join(BIN_DIRECTORY, "config/safeeyes.json")
SYSTEM_STYLE_SHEET_PATH = os.path.join(BIN_DIRECTORY, "config/style/safeeyes_style.css")
LOG_FILE_PATH = os.path.join(HOME_DIRECTORY, "safeeyes.log")
Expand Down Expand Up @@ -372,8 +377,32 @@ def merge_configs(new_config, old_config):
return new_config


def sha256sum(filename):
"""Get the sha256 hash of the given file."""
h = hashlib.sha256()
b = bytearray(128 * 1024)
mv = memoryview(b)
with open(filename, "rb", buffering=0) as f:
for n in iter(lambda: f.readinto(mv), 0):
h.update(mv[:n])
return h.hexdigest()


def load_css_file(style_sheet_path, priority, required=True):
if not os.path.isfile(style_sheet_path):
if required:
logging.warning("Failed loading required stylesheet")
return

css_provider = Gtk.CssProvider()
css_provider.load_from_path(style_sheet_path)

display = Gdk.Display.get_default()
Gtk.StyleContext.add_provider_for_display(display, css_provider, priority)


def initialize_safeeyes():
"""Create the config file and style sheet in XDG_CONFIG_HOME(or
"""Create the config file in XDG_CONFIG_HOME(or
~/.config)/safeeyes directory.
"""
logging.info("Copy the config files to XDG_CONFIG_HOME(or ~/.config)/safeeyes")
Expand All @@ -388,24 +417,45 @@ def initialize_safeeyes():
shutil.copy2(SYSTEM_CONFIG_FILE_PATH, CONFIG_FILE_PATH)
os.chmod(CONFIG_FILE_PATH, 0o666)

create_user_stylesheet_if_missing()

# initialize_safeeyes gets called when the configuration file is not present, which
# happens just after installation or manual deletion of
# .config/safeeyes/safeeyes.json file. In these cases, we want to force the creation
# of a startup entry
create_startup_entry(force=True)


def create_user_stylesheet_if_missing():
def cleanup_old_user_stylesheet():
# Create the XDG_CONFIG_HOME(or ~/.config)/safeeyes/style directory
if not os.path.isdir(STYLE_SHEET_DIRECTORY):
mkdir(STYLE_SHEET_DIRECTORY)

# Copy the new style sheet
if not os.path.isfile(STYLE_SHEET_PATH):
shutil.copy2(SYSTEM_STYLE_SHEET_PATH, STYLE_SHEET_PATH)
os.chmod(STYLE_SHEET_PATH, 0o666)
# Delete the old stylesheet, unless it has customizations
if os.path.isfile(OLD_STYLE_SHEET_PATH):
hash = sha256sum(OLD_STYLE_SHEET_PATH)
old_default_versions = [
# 2.2.3
"fdc2a305613ae4eeb269650452789d35df3df5bdf1c56eb576cd5ebac70a6f09",
# 2.1.0 - 2.2.2
"fbde048fc234db757461971a7542df43a467869035ca3d05ff9b236ca250e4c5",
# 2.0.9
"70ca55c12d83ad7a6a4e1c5e7758a38617a43f5d32f28709ede75426d3186713",
# 2.0.7 - 2.0.8
"7a15f985e0da6d92c8a62d49ce151781e6d423f87e66d205cc1dc4536e369e19",
# 2.0.6 and earlier
"f26621a883e323ca7685a4adba25027e70daa471e0db4a21c261e6c15caaa5ee",
]
if hash in old_default_versions:
logging.info("Deleting old stylesheet containing default content")
delete(OLD_STYLE_SHEET_PATH)
else:
# Stylesheet was likely customized, don't delete but warn
logging.warning(
_(
"Old stylesheet found at '%(old)s', ignoring. For custom styles, "
"create a new stylesheet in '%(new)s' instead.",
)
% {"old": OLD_STYLE_SHEET_PATH, "new": CUSTOM_STYLE_SHEET_PATH}
)


def create_startup_entry(force=False):
Expand Down Expand Up @@ -517,26 +567,16 @@ def initialize_platform():
def reset_config():
# Remove the ~/.config/safeeyes/safeeyes.json and safeeyes_style.css
delete(CONFIG_FILE_PATH)
delete(STYLE_SHEET_PATH)

# Copy the safeeyes.json and safeeyes_style.css
shutil.copy2(SYSTEM_CONFIG_FILE_PATH, CONFIG_FILE_PATH)
shutil.copy2(SYSTEM_STYLE_SHEET_PATH, STYLE_SHEET_PATH)

# Add write permission (e.g. if original file was stored in /nix/store)
os.chmod(CONFIG_FILE_PATH, 0o666)
os.chmod(STYLE_SHEET_PATH, 0o666)

create_startup_entry()


def replace_style_sheet():
"""Replace the user style sheet by system style sheet."""
delete(STYLE_SHEET_PATH)
shutil.copy2(SYSTEM_STYLE_SHEET_PATH, STYLE_SHEET_PATH)
os.chmod(STYLE_SHEET_PATH, 0o666)


def initialize_logging(debug):
"""Initialize the logging framework using the Safe Eyes specific
configurations.
Expand Down