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 README.md
Original file line number Diff line number Diff line change
Expand Up @@ -157,7 +157,7 @@ Some Linux systems like CentOS do not have matching dependencies available in th
1. Install the necessary dependencies for CentOS 7

```bash
sudo yum install python3-devel dbus dbus-devel cairo cairo-devel cairomm-devel libjpeg-turbo-devel pango pango-devel pangomm pangomm-devel gobject-introspection-devel cairo-gobject-devel
sudo yum install python3-devel cairo cairo-devel cairomm-devel libjpeg-turbo-devel pango pango-devel pangomm pangomm-devel gobject-introspection-devel cairo-gobject-devel
```

2. Create a virtual environment in your home folder
Expand Down
6 changes: 6 additions & 0 deletions debian/changelog
Original file line number Diff line number Diff line change
@@ -1,3 +1,9 @@
safeeyes (3.0.0) noble; urgency=medium

* Internal fixes for flatpak.

-- Mel Dafert <[email protected]> Sun, 24 Aug 2025 10:30:00 +0000

safeeyes (3.0.0b6) noble; urgency=medium

* Re-release due to broken github action
Expand Down
4 changes: 2 additions & 2 deletions pyproject.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[project]
name = "safeeyes"
version = "3.0.0b6"
version = "3.0.0"
description = "Protect your eyes from eye strain using this continuous breaks reminder."
keywords = ["linux utility health eye-strain safe-eyes"]
readme = "README.md"
Expand Down Expand Up @@ -31,7 +31,7 @@ requires-python = ">=3.10"

[project.urls]
Homepage = "https://github.com/slgobinath/SafeEyes"
Downloads = "https://github.com/slgobinath/SafeEyes/archive/v3.0.0b6.tar.gz"
Downloads = "https://github.com/slgobinath/SafeEyes/archive/v3.0.0.tar.gz"

[project.scripts]
safeeyes = "safeeyes.__main__:main"
Expand Down
2 changes: 1 addition & 1 deletion safeeyes/glade/about_dialog.glade
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,7 @@ along with this program. If not, see &lt;https://www.gnu.org/licenses/&gt;.</pr
<property name="valign">center</property>
<property name="margin-top">10</property>
<property name="margin-bottom">10</property>
<property name="label">Safe Eyes 3.0.0b6</property>
<property name="label">Safe Eyes 3.0.0</property>
<property name="justify">center</property>
<property name="hexpand">1</property>
<property name="vexpand">1</property>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,7 @@
<url type="homepage">https://slgobinath.github.io/SafeEyes/</url>

<releases>
<release version="3.0.0" date="2025-08-24" />
<release version="3.0.0b6" date="2025-08-24" />
<release version="3.0.0b5" date="2025-08-22" />
<release version="3.0.0b4" date="2025-08-22" />
Expand Down
86 changes: 62 additions & 24 deletions safeeyes/plugins/screensaver/plugin.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,31 +22,39 @@

import logging
import os
import typing

import gi

gi.require_version("Gio", "2.0")
from gi.repository import Gio

from safeeyes import utility
from safeeyes.model import TrayAction

context = None
lock_screen = False
is_long_break: bool = False
user_locked_screen = False
lock_screen_command = None
lock_screen_command: typing.Union[list[str], typing.Callable[[], None], None] = None
min_seconds = 0
seconds_passed = 0
tray_icon_path = None
icon_lock_later_path = None


def __lock_screen_command():
def __lock_screen_command() -> typing.Union[list[str], typing.Callable[[], None], None]:
"""Function tries to detect the screensaver command based on the current
envinroment.

Returns either a command to execute or function to call.

Possible results:
Gnome, Unity, Budgie: ['gnome-screensaver-command', '--lock']
Modern GNOME: DBus: org.gnome.ScreenSaver.Lock
Old Gnome, Unity, Budgie: ['gnome-screensaver-command', '--lock']
Cinnamon: ['cinnamon-screensaver-command', '--lock']
Pantheon, LXDE: ['light-locker-command', '--lock']
Mate: ['mate-screensaver-command', '--lock']
KDE: ['qdbus', 'org.freedesktop.ScreenSaver',
'/ScreenSaver', 'Lock']
KDE: DBus: org.freedesktop.ScreenSaver.Lock
XFCE: ['xflock4']
Otherwise: None
"""
Expand All @@ -63,6 +71,7 @@ def __lock_screen_command():
elif desktop_session == "cinnamon" and utility.command_exist(
"cinnamon-screensaver-command"
):
# This calls org.cinnamon.ScreenSaver.Lock internally
return ["cinnamon-screensaver-command", "--lock"]
elif (
desktop_session == "pantheon" or desktop_session.startswith("lubuntu")
Expand All @@ -71,14 +80,23 @@ def __lock_screen_command():
elif desktop_session == "mate" and utility.command_exist(
"mate-screensaver-command"
):
# This calls org.mate.ScreenSaver.Lock internally
# However, it warns not to rely on that
return ["mate-screensaver-command", "--lock"]
elif (
desktop_session == "kde"
or "plasma" in desktop_session
or desktop_session.startswith("kubuntu")
or os.environ.get("KDE_FULL_SESSION") == "true"
):
return ["qdbus", "org.freedesktop.ScreenSaver", "/ScreenSaver", "Lock"]
# Note that this is unfortunately a non-standard KDE extension.
# See https://gitlab.gnome.org/GNOME/gnome-settings-daemon/-/issues/632
# for details.
return lambda: __lock_screen_dbus(
destination="org.freedesktop.ScreenSaver",
path="/ScreenSaver",
method="Lock",
)
elif (
desktop_session in ["gnome", "unity", "budgie-desktop"]
or desktop_session.startswith("ubuntu")
Expand All @@ -87,29 +105,49 @@ def __lock_screen_command():
if utility.command_exist("gnome-screensaver-command"):
return ["gnome-screensaver-command", "--lock"]
# From Gnome 3.8 no gnome-screensaver-command
return [
"dbus-send",
"--type=method_call",
"--dest=org.gnome.ScreenSaver",
"/org/gnome/ScreenSaver",
"org.gnome.ScreenSaver.Lock",
]
elif os.environ.get("GNOME_DESKTOP_SESSION_ID"):
if "deprecated" not in os.environ.get(
"GNOME_DESKTOP_SESSION_ID"
) and utility.command_exist("gnome-screensaver-command"):
return lambda: __lock_screen_dbus(
destination="org.gnome.ScreenSaver",
path="/org/gnome/ScreenSaver",
method="Lock",
)
elif gd_session := os.environ.get("GNOME_DESKTOP_SESSION_ID"):
if "deprecated" not in gd_session and utility.command_exist(
"gnome-screensaver-command"
):
# Gnome 2
return ["gnome-screensaver-command", "--lock"]
return None


def __lock_screen_dbus(destination: str, path: str, method: str) -> None:
"""This assumes that the interface is the same as the destination."""
dbus_proxy = Gio.DBusProxy.new_for_bus_sync(
bus_type=Gio.BusType.SESSION,
flags=Gio.DBusProxyFlags.DO_NOT_LOAD_PROPERTIES,
info=None,
name=destination,
object_path=path,
interface_name=destination,
)

dbus_proxy.call_sync(method, None, Gio.DBusCallFlags.NONE, -1)


def __lock_screen_later():
global user_locked_screen
user_locked_screen = True


def __lock_screen_now() -> None:
utility.execute_command(lock_screen_command)
global lock_screen_command

if lock_screen_command is None:
return

if isinstance(lock_screen_command, list):
utility.execute_command(lock_screen_command)
else:
lock_screen_command()


def init(ctx, safeeyes_config, plugin_config):
Expand All @@ -134,15 +172,15 @@ def init(ctx, safeeyes_config, plugin_config):

def on_start_break(break_obj):
"""Determine the break type and only if it is a long break, enable the
lock_screen flag.
is_long_break flag.
"""
global lock_screen
global is_long_break
global seconds_passed
global user_locked_screen
user_locked_screen = False
seconds_passed = 0
if lock_screen_command:
lock_screen = break_obj.is_long_break()

is_long_break = break_obj.is_long_break()


def on_countdown(countdown, seconds):
Expand All @@ -155,7 +193,7 @@ def on_stop_break():
"""Lock the screen after a long break if the user has not skipped within
min_seconds.
"""
if user_locked_screen or (lock_screen and seconds_passed >= min_seconds):
if user_locked_screen or (is_long_break and seconds_passed >= min_seconds):
__lock_screen_now()


Expand Down