Compare commits
4 Commits
fc33569edd
...
911e32aae1
Author | SHA1 | Date |
---|---|---|
deltragon | 911e32aae1 | |
aerowolf | 00c33908a3 | |
deltragon | 3b792db159 | |
deltragon | 8b5007c1c1 |
|
@ -67,7 +67,7 @@ sudo apt-get install safeeyes
|
||||||
### Fedora
|
### Fedora
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
sudo dnf install libappindicator-gtk3 python3-psutil cairo-devel python3-devel gobject-introspection-devel cairo-gobject-devel
|
sudo dnf install python3-psutil cairo-devel python3-devel gobject-introspection-devel cairo-gobject-devel
|
||||||
sudo pip3 install safeeyes
|
sudo pip3 install safeeyes
|
||||||
sudo gtk-update-icon-cache /usr/share/icons/hicolor
|
sudo gtk-update-icon-cache /usr/share/icons/hicolor
|
||||||
```
|
```
|
||||||
|
@ -95,9 +95,7 @@ flatpak install flathub io.github.slgobinath.SafeEyes
|
||||||
|
|
||||||
Ensure to meet the following dependencies:
|
Ensure to meet the following dependencies:
|
||||||
|
|
||||||
- gir1.2-appindicator3-0.1 or gir1.2-ayatanaappindicator3-0.1
|
|
||||||
- gir1.2-notify-0.7
|
- gir1.2-notify-0.7
|
||||||
- libappindicator-gtk3
|
|
||||||
- python3-psutil
|
- python3-psutil
|
||||||
- xprintidle (optional)
|
- xprintidle (optional)
|
||||||
- wlrctl (wayland optional)
|
- wlrctl (wayland optional)
|
||||||
|
@ -140,7 +138,7 @@ Some Linux systems like Cent OS do not have matching dependencies available in t
|
||||||
pip3 install virtualenv --user
|
pip3 install virtualenv --user
|
||||||
virtualenv --no-site-packages venv
|
virtualenv --no-site-packages venv
|
||||||
source venv/bin/activate
|
source venv/bin/activate
|
||||||
pip3 install dbus-python safeeyes
|
pip3 install safeeyes
|
||||||
```
|
```
|
||||||
|
|
||||||
3. Start Safe Eyes from terminal
|
3. Start Safe Eyes from terminal
|
||||||
|
|
|
@ -9,7 +9,7 @@ Homepage: https://github.com/slgobinath/SafeEyes/
|
||||||
|
|
||||||
Package: safeeyes
|
Package: safeeyes
|
||||||
Architecture: all
|
Architecture: all
|
||||||
Depends: ${misc:Depends}, ${python3:Depends}, gir1.2-ayatanaappindicator3-0.1, python3 (>= 3.5.0), python3-xlib, python3-dbus, gir1.2-notify-0.7, python3-babel, x11-utils, xprintidle, alsa-utils, python3-psutil, python3-croniter
|
Depends: ${misc:Depends}, ${python3:Depends}, python3 (>= 3.5.0), python3-xlib, gir1.2-notify-0.7, python3-babel, x11-utils, xprintidle, alsa-utils, python3-psutil, python3-croniter
|
||||||
Description: Safe Eyes
|
Description: Safe Eyes
|
||||||
Safe Eyes is a simple tool to remind you to take periodic breaks for your eyes. This is essential for anyone spending more time on the computer to avoid eye strain and other physical problems.
|
Safe Eyes is a simple tool to remind you to take periodic breaks for your eyes. This is essential for anyone spending more time on the computer to avoid eye strain and other physical problems.
|
||||||
.
|
.
|
||||||
|
|
|
@ -6,8 +6,8 @@ msgid ""
|
||||||
msgstr ""
|
msgstr ""
|
||||||
"Project-Id-Version: \n"
|
"Project-Id-Version: \n"
|
||||||
"POT-Creation-Date: \n"
|
"POT-Creation-Date: \n"
|
||||||
"PO-Revision-Date: 2021-10-10 05:05+0000\n"
|
"PO-Revision-Date: 2024-02-21 10:01+0000\n"
|
||||||
"Last-Translator: Frank.wu <me@wuzhiping.top>\n"
|
"Last-Translator: aerowolf <aerowolf@tom.com>\n"
|
||||||
"Language-Team: Chinese (Simplified) <https://hosted.weblate.org/projects/"
|
"Language-Team: Chinese (Simplified) <https://hosted.weblate.org/projects/"
|
||||||
"safe-eyes/translations/zh_Hans/>\n"
|
"safe-eyes/translations/zh_Hans/>\n"
|
||||||
"Language: zh_CN\n"
|
"Language: zh_CN\n"
|
||||||
|
@ -15,23 +15,23 @@ msgstr ""
|
||||||
"Content-Type: text/plain; charset=UTF-8\n"
|
"Content-Type: text/plain; charset=UTF-8\n"
|
||||||
"Content-Transfer-Encoding: 8bit\n"
|
"Content-Transfer-Encoding: 8bit\n"
|
||||||
"Plural-Forms: nplurals=1; plural=0;\n"
|
"Plural-Forms: nplurals=1; plural=0;\n"
|
||||||
"X-Generator: Weblate 4.9-dev\n"
|
"X-Generator: Weblate 5.5-dev\n"
|
||||||
|
|
||||||
# Short break
|
# Short break
|
||||||
msgid "Tightly close your eyes"
|
msgid "Tightly close your eyes"
|
||||||
msgstr "闭上您的眼睛休息一会"
|
msgstr "闭上眼睛休息一下"
|
||||||
|
|
||||||
# Short break
|
# Short break
|
||||||
msgid "Roll your eyes a few times to each side"
|
msgid "Roll your eyes a few times to each side"
|
||||||
msgstr "向两边各翻几下眼睛"
|
msgstr "左右转动眼球"
|
||||||
|
|
||||||
# Short break
|
# Short break
|
||||||
msgid "Rotate your eyes in clockwise direction"
|
msgid "Rotate your eyes in clockwise direction"
|
||||||
msgstr "按顺时针方向旋转你的眼球"
|
msgstr "顺时针方向转动眼球"
|
||||||
|
|
||||||
# Short break
|
# Short break
|
||||||
msgid "Rotate your eyes in counterclockwise direction"
|
msgid "Rotate your eyes in counterclockwise direction"
|
||||||
msgstr "按逆时针方向旋转你的眼球"
|
msgstr "逆时针方向转动眼球"
|
||||||
|
|
||||||
# Short break
|
# Short break
|
||||||
msgid "Blink your eyes"
|
msgid "Blink your eyes"
|
||||||
|
@ -39,11 +39,11 @@ msgstr "眨眨眼"
|
||||||
|
|
||||||
# Short break
|
# Short break
|
||||||
msgid "Focus on a point in the far distance"
|
msgid "Focus on a point in the far distance"
|
||||||
msgstr "望远:将视线聚焦到远处物体上"
|
msgstr "远眺:看看尽可能远的远处"
|
||||||
|
|
||||||
# Short break
|
# Short break
|
||||||
msgid "Have some water"
|
msgid "Have some water"
|
||||||
msgstr "喝一点水"
|
msgstr "喝点水吧"
|
||||||
|
|
||||||
# Long break
|
# Long break
|
||||||
msgid "Walk for a while"
|
msgid "Walk for a while"
|
||||||
|
@ -51,7 +51,7 @@ msgstr "站起来走走"
|
||||||
|
|
||||||
# Long break
|
# Long break
|
||||||
msgid "Lean back at your seat and relax"
|
msgid "Lean back at your seat and relax"
|
||||||
msgstr "靠在椅背上休息一会"
|
msgstr "靠在椅背上休息一下"
|
||||||
|
|
||||||
# Commandline arg description
|
# Commandline arg description
|
||||||
msgid "show the about dialog"
|
msgid "show the about dialog"
|
||||||
|
@ -59,15 +59,15 @@ msgstr "显示“关于”对话框"
|
||||||
|
|
||||||
# Commandline arg description
|
# Commandline arg description
|
||||||
msgid "disable the currently running safeeyes instance"
|
msgid "disable the currently running safeeyes instance"
|
||||||
msgstr "禁用当前正在运行的Safe Eyes"
|
msgstr "暂停当前正在运行的Safe Eyes"
|
||||||
|
|
||||||
# Commandline arg description
|
# Commandline arg description
|
||||||
msgid "enable the currently running safeeyes instance"
|
msgid "enable the currently running safeeyes instance"
|
||||||
msgstr "启用当前正在运行的Safe Eyes"
|
msgstr "恢复当前正在运行的Safe Eyes"
|
||||||
|
|
||||||
# Commandline arg description
|
# Commandline arg description
|
||||||
msgid "quit the running safeeyes instance and exit"
|
msgid "quit the running safeeyes instance and exit"
|
||||||
msgstr "退出正在运行的Safe Eeyes"
|
msgstr "退出Safe Eeyes"
|
||||||
|
|
||||||
# Commandline arg description
|
# Commandline arg description
|
||||||
msgid "show the settings dialog"
|
msgid "show the settings dialog"
|
||||||
|
@ -79,17 +79,17 @@ msgstr "以调试模式运行Safe Eyes"
|
||||||
|
|
||||||
# Commandline arg description
|
# Commandline arg description
|
||||||
msgid "print the status of running safeeyes instance and exit"
|
msgid "print the status of running safeeyes instance and exit"
|
||||||
msgstr "打印Safe Eyes运行状态后退出"
|
msgstr "显示Safe Eyes运行状态后退出"
|
||||||
|
|
||||||
# Status message
|
# Status message
|
||||||
msgid "Safe Eyes is not running"
|
msgid "Safe Eyes is not running"
|
||||||
msgstr "Safe Eyes 尚未运行"
|
msgstr "Safe Eyes 没有运行"
|
||||||
|
|
||||||
# RPC not enabled message
|
# RPC not enabled message
|
||||||
msgid ""
|
msgid ""
|
||||||
"Safe Eyes is running without an RPC server. Turn it on to use command-line "
|
"Safe Eyes is running without an RPC server. Turn it on to use command-line "
|
||||||
"arguments."
|
"arguments."
|
||||||
msgstr "Safe Eyes 正在没有RPC服务器的情况下运行。打开它以使用命令行参数。"
|
msgstr "Safe Eyes 正在没有RPC服务器的情况下运行。打开RPC服务器可以使用命令行参数。"
|
||||||
|
|
||||||
# About dialog
|
# About dialog
|
||||||
msgid "Close"
|
msgid "Close"
|
||||||
|
@ -106,7 +106,7 @@ msgstr ""
|
||||||
|
|
||||||
# About dialog
|
# About dialog
|
||||||
msgid "License"
|
msgid "License"
|
||||||
msgstr "许可证"
|
msgstr "许可"
|
||||||
|
|
||||||
# Break screen
|
# Break screen
|
||||||
msgid "Skip"
|
msgid "Skip"
|
||||||
|
@ -142,7 +142,7 @@ msgstr "以随机顺序显示休息"
|
||||||
|
|
||||||
# Settings dialog
|
# Settings dialog
|
||||||
msgid "Strict break (No way to skip breaks)"
|
msgid "Strict break (No way to skip breaks)"
|
||||||
msgstr "坚持休息(无法跳过)"
|
msgstr "坚持休息(不允许跳过)"
|
||||||
|
|
||||||
# Settings dialog
|
# Settings dialog
|
||||||
msgid "Allow postponing breaks"
|
msgid "Allow postponing breaks"
|
||||||
|
@ -150,7 +150,7 @@ msgstr "允许推迟休息"
|
||||||
|
|
||||||
# Settings dialog
|
# Settings dialog
|
||||||
msgid "Persist the internal state"
|
msgid "Persist the internal state"
|
||||||
msgstr "维持内部状态"
|
msgstr "支持内部状态"
|
||||||
|
|
||||||
# Settings dialog
|
# Settings dialog
|
||||||
msgid "Use RPC server to receive runtime commands"
|
msgid "Use RPC server to receive runtime commands"
|
||||||
|
@ -414,11 +414,11 @@ msgstr "在通知区域显示托盘图标"
|
||||||
|
|
||||||
#: plugins/trayicon
|
#: plugins/trayicon
|
||||||
msgid "Show next break time in tray icon"
|
msgid "Show next break time in tray icon"
|
||||||
msgstr "在托盘图标上显示下次休息时间"
|
msgstr "在托盘图标中显示下次休息时间"
|
||||||
|
|
||||||
#: plugins/trayicon
|
#: plugins/trayicon
|
||||||
msgid "Allow disabling Safe Eyes"
|
msgid "Allow disabling Safe Eyes"
|
||||||
msgstr "允许禁用Safe Eyes"
|
msgstr "允许暂停Safe Eyes"
|
||||||
|
|
||||||
#: plugins/trayicon
|
#: plugins/trayicon
|
||||||
msgid "About"
|
msgid "About"
|
||||||
|
@ -426,19 +426,19 @@ msgstr "关于"
|
||||||
|
|
||||||
#: plugins/trayicon
|
#: plugins/trayicon
|
||||||
msgid "Disable Safe Eyes"
|
msgid "Disable Safe Eyes"
|
||||||
msgstr "禁用 Safe Eyes"
|
msgstr "暂停 Safe Eyes"
|
||||||
|
|
||||||
#: plugins/trayicon
|
#: plugins/trayicon
|
||||||
msgid "Disabled until %s"
|
msgid "Disabled until %s"
|
||||||
msgstr "禁用软件直到 %s"
|
msgstr "暂停,直到 %s"
|
||||||
|
|
||||||
#: plugins/trayicon
|
#: plugins/trayicon
|
||||||
msgid "Disabled until restart"
|
msgid "Disabled until restart"
|
||||||
msgstr "禁用软件直到重启"
|
msgstr "暂停,直到重启"
|
||||||
|
|
||||||
#: plugins/trayicon
|
#: plugins/trayicon
|
||||||
msgid "Enable Safe Eyes"
|
msgid "Enable Safe Eyes"
|
||||||
msgstr "启用 Safe Eyes"
|
msgstr "恢复 Safe Eyes"
|
||||||
|
|
||||||
#: plugins/trayicon
|
#: plugins/trayicon
|
||||||
msgid "For %d Hour"
|
msgid "For %d Hour"
|
||||||
|
@ -457,7 +457,7 @@ msgstr[0] "%d 秒"
|
||||||
|
|
||||||
#: plugins/trayicon
|
#: plugins/trayicon
|
||||||
msgid "Next break at %s"
|
msgid "Next break at %s"
|
||||||
msgstr "下次休息在 %s"
|
msgstr "下次休息将在 %s"
|
||||||
|
|
||||||
#: plugins/trayicon
|
#: plugins/trayicon
|
||||||
msgid "No Breaks Available"
|
msgid "No Breaks Available"
|
||||||
|
|
|
@ -5,7 +5,7 @@
|
||||||
"version": "0.0.1"
|
"version": "0.0.1"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"python_modules": ["dbus"],
|
"python_modules": [],
|
||||||
"shell_commands": [],
|
"shell_commands": [],
|
||||||
"operating_systems": [],
|
"operating_systems": [],
|
||||||
"desktop_environments": [],
|
"desktop_environments": [],
|
||||||
|
|
|
@ -22,12 +22,11 @@ Media Control plugin lets users to pause currently playing media player from the
|
||||||
|
|
||||||
import logging
|
import logging
|
||||||
import os
|
import os
|
||||||
import dbus
|
|
||||||
import re
|
import re
|
||||||
import gi
|
import gi
|
||||||
from safeeyes.model import TrayAction
|
from safeeyes.model import TrayAction
|
||||||
gi.require_version('Gtk', '3.0')
|
gi.require_version('Gtk', '3.0')
|
||||||
from gi.repository import Gtk
|
from gi.repository import Gtk, Gio
|
||||||
|
|
||||||
tray_icon_path = None
|
tray_icon_path = None
|
||||||
|
|
||||||
|
@ -37,13 +36,31 @@ def __active_players():
|
||||||
List of all media players which are playing now.
|
List of all media players which are playing now.
|
||||||
"""
|
"""
|
||||||
players = []
|
players = []
|
||||||
bus = dbus.SessionBus()
|
|
||||||
|
|
||||||
for service in bus.list_names():
|
dbus_proxy = Gio.DBusProxy.new_for_bus_sync(
|
||||||
|
bus_type=Gio.BusType.SESSION,
|
||||||
|
flags=Gio.DBusProxyFlags.DO_NOT_LOAD_PROPERTIES,
|
||||||
|
info=None,
|
||||||
|
name='org.freedesktop.DBus',
|
||||||
|
object_path='/org/freedesktop/DBus',
|
||||||
|
interface_name='org.freedesktop.DBus',
|
||||||
|
cancellable=None,
|
||||||
|
)
|
||||||
|
|
||||||
|
for service in dbus_proxy.ListNames():
|
||||||
if re.match('org.mpris.MediaPlayer2.', service):
|
if re.match('org.mpris.MediaPlayer2.', service):
|
||||||
player = bus.get_object(service, "/org/mpris/MediaPlayer2")
|
player = Gio.DBusProxy.new_for_bus_sync(
|
||||||
interface = dbus.Interface(player, 'org.freedesktop.DBus.Properties')
|
bus_type=Gio.BusType.SESSION,
|
||||||
status = str(interface.Get('org.mpris.MediaPlayer2.Player', 'PlaybackStatus')).lower()
|
flags=Gio.DBusProxyFlags.NONE,
|
||||||
|
info=None,
|
||||||
|
name=service,
|
||||||
|
object_path='/org/mpris/MediaPlayer2',
|
||||||
|
interface_name='org.mpris.MediaPlayer2.Player',
|
||||||
|
cancellable=None,
|
||||||
|
)
|
||||||
|
|
||||||
|
status = player.get_cached_property('PlaybackStatus').unpack().lower()
|
||||||
|
|
||||||
if status == "playing":
|
if status == "playing":
|
||||||
players.append(player)
|
players.append(player)
|
||||||
return players
|
return players
|
||||||
|
@ -54,8 +71,7 @@ def __pause_players(players):
|
||||||
Pause all playing media players using dbus.
|
Pause all playing media players using dbus.
|
||||||
"""
|
"""
|
||||||
for player in players:
|
for player in players:
|
||||||
interface = dbus.Interface(player, dbus_interface='org.mpris.MediaPlayer2.Player')
|
player.Pause()
|
||||||
interface.Pause()
|
|
||||||
|
|
||||||
|
|
||||||
def init(ctx, safeeyes_config, plugin_config):
|
def init(ctx, safeeyes_config, plugin_config):
|
||||||
|
|
|
@ -20,14 +20,7 @@ import datetime
|
||||||
from safeeyes.model import BreakType
|
from safeeyes.model import BreakType
|
||||||
import gi
|
import gi
|
||||||
gi.require_version('Gtk', '3.0')
|
gi.require_version('Gtk', '3.0')
|
||||||
try:
|
from gi.repository import Gio, GLib
|
||||||
gi.require_version('AppIndicator3', '0.1')
|
|
||||||
from gi.repository import AppIndicator3 as appindicator
|
|
||||||
except:
|
|
||||||
#fall back to Ayatana
|
|
||||||
gi.require_version('AyatanaAppIndicator3', '0.1')
|
|
||||||
from gi.repository import AyatanaAppIndicator3 as appindicator
|
|
||||||
from gi.repository import Gtk
|
|
||||||
import logging
|
import logging
|
||||||
from safeeyes import utility
|
from safeeyes import utility
|
||||||
import threading
|
import threading
|
||||||
|
@ -37,11 +30,291 @@ import time
|
||||||
Safe Eyes tray icon plugin
|
Safe Eyes tray icon plugin
|
||||||
"""
|
"""
|
||||||
|
|
||||||
APPINDICATOR_ID = 'safeeyes_2'
|
|
||||||
context = None
|
context = None
|
||||||
tray_icon = None
|
tray_icon = None
|
||||||
safeeyes_config = None
|
safeeyes_config = None
|
||||||
|
|
||||||
|
SNI_NODE_INFO = Gio.DBusNodeInfo.new_for_xml("""
|
||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<node>
|
||||||
|
<interface name="org.kde.StatusNotifierItem">
|
||||||
|
<property name="Category" type="s" access="read"/>
|
||||||
|
<property name="Id" type="s" access="read"/>
|
||||||
|
<property name="Title" type="s" access="read"/>
|
||||||
|
<property name="ToolTip" type="(sa(iiay)ss)" access="read"/>
|
||||||
|
<property name="Menu" type="o" access="read"/>
|
||||||
|
<property name="ItemIsMenu" type="b" access="read"/>
|
||||||
|
<property name="IconName" type="s" access="read"/>
|
||||||
|
<property name="IconThemePath" type="s" access="read"/>
|
||||||
|
<property name="Status" type="s" access="read"/>
|
||||||
|
<signal name="NewIcon"/>
|
||||||
|
<signal name="NewTooltip"/>
|
||||||
|
</interface>
|
||||||
|
</node>""").interfaces[0]
|
||||||
|
|
||||||
|
MENU_NODE_INFO = Gio.DBusNodeInfo.new_for_xml("""
|
||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<node>
|
||||||
|
<interface name="com.canonical.dbusmenu">
|
||||||
|
<method name="GetLayout">
|
||||||
|
<arg type="i" direction="in"/>
|
||||||
|
<arg type="i" direction="in"/>
|
||||||
|
<arg type="as" direction="in"/>
|
||||||
|
<arg type="u" direction="out"/>
|
||||||
|
<arg type="(ia{sv}av)" direction="out"/>
|
||||||
|
</method>
|
||||||
|
<method name="Event">
|
||||||
|
<arg type="i" direction="in"/>
|
||||||
|
<arg type="s" direction="in"/>
|
||||||
|
<arg type="v" direction="in"/>
|
||||||
|
<arg type="u" direction="in"/>
|
||||||
|
</method>
|
||||||
|
<method name="AboutToShow">
|
||||||
|
<arg type="i" direction="in"/>
|
||||||
|
<arg type="b" direction="out"/>
|
||||||
|
</method>
|
||||||
|
<signal name="LayoutUpdated">
|
||||||
|
<arg type="u"/>
|
||||||
|
<arg type="i"/>
|
||||||
|
</signal>
|
||||||
|
</interface>
|
||||||
|
</node>""").interfaces[0]
|
||||||
|
|
||||||
|
class DBusService:
|
||||||
|
def __init__(self, interface_info, object_path, bus):
|
||||||
|
self.interface_info = interface_info
|
||||||
|
self.bus = bus
|
||||||
|
self.object_path = object_path
|
||||||
|
self.registration_id = None
|
||||||
|
|
||||||
|
def register(self):
|
||||||
|
self.registration_id = self.bus.register_object(
|
||||||
|
object_path=self.object_path,
|
||||||
|
interface_info=self.interface_info,
|
||||||
|
method_call_closure=self.on_method_call,
|
||||||
|
get_property_closure=self.on_get_property
|
||||||
|
)
|
||||||
|
|
||||||
|
if not self.registration_id:
|
||||||
|
raise GLib.Error(f"Failed to register object with path {self.object_path}")
|
||||||
|
|
||||||
|
self.interface_info.cache_build()
|
||||||
|
|
||||||
|
def unregister(self):
|
||||||
|
self.interface_info.cache_release()
|
||||||
|
|
||||||
|
if self.registration_id is not None:
|
||||||
|
self.bus.unregister_object(self.registration_id)
|
||||||
|
self.registration_id = None
|
||||||
|
|
||||||
|
def on_method_call(self, _connection, _sender, _path, _interface_name, method_name, parameters, invocation):
|
||||||
|
method_info = self.interface_info.lookup_method(method_name)
|
||||||
|
method = getattr(self, method_name)
|
||||||
|
result = method(*parameters.unpack())
|
||||||
|
out_arg_types = "".join([arg.signature for arg in method_info.out_args])
|
||||||
|
return_value = None
|
||||||
|
|
||||||
|
if method_info.out_args:
|
||||||
|
return_value = GLib.Variant(f"({out_arg_types})", result)
|
||||||
|
|
||||||
|
invocation.return_value(return_value)
|
||||||
|
|
||||||
|
def on_get_property(self, _connection, _sender, _path, _interface_name, property_name):
|
||||||
|
property_info = self.interface_info.lookup_property(property_name)
|
||||||
|
return GLib.Variant(property_info.signature, getattr(self, property_name))
|
||||||
|
|
||||||
|
def emit_signal(self, signal_name, args = None):
|
||||||
|
signal_info = self.interface_info.lookup_signal(signal_name)
|
||||||
|
if len(signal_info.args) == 0:
|
||||||
|
parameters = None
|
||||||
|
else:
|
||||||
|
arg_types = "".join([arg.signature for arg in signal_info.args])
|
||||||
|
parameters = GLib.Variant(f"({arg_types})", args)
|
||||||
|
self.bus.emit_signal(
|
||||||
|
destination_bus_name=None,
|
||||||
|
object_path=self.object_path,
|
||||||
|
interface_name=self.interface_info.name,
|
||||||
|
signal_name=signal_name,
|
||||||
|
parameters=parameters
|
||||||
|
)
|
||||||
|
|
||||||
|
class DBusMenuService(DBusService):
|
||||||
|
DBUS_SERVICE_PATH = '/io/github/slgobinath/SafeEyes/Menu'
|
||||||
|
|
||||||
|
revision = 0
|
||||||
|
|
||||||
|
items = []
|
||||||
|
idToCallback = {}
|
||||||
|
|
||||||
|
def __init__(self, session_bus, context, items):
|
||||||
|
super().__init__(
|
||||||
|
interface_info=MENU_NODE_INFO,
|
||||||
|
object_path=self.DBUS_SERVICE_PATH,
|
||||||
|
bus=session_bus
|
||||||
|
)
|
||||||
|
|
||||||
|
self.set_items(items)
|
||||||
|
|
||||||
|
def set_items(self, items):
|
||||||
|
self.items = items
|
||||||
|
|
||||||
|
self.idToCallback = self.getItemCallbacks(items, {})
|
||||||
|
|
||||||
|
self.revision += 1
|
||||||
|
|
||||||
|
self.LayoutUpdated(self.revision, 0)
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def getItemCallbacks(items, idToCallback):
|
||||||
|
for item in items:
|
||||||
|
if item.get('hidden', False) == True:
|
||||||
|
continue
|
||||||
|
if 'callback' in item:
|
||||||
|
idToCallback[item['id']] = item['callback']
|
||||||
|
if 'children' in item:
|
||||||
|
idToCallback = DBusMenuService.getItemCallbacks(item['children'], idToCallback)
|
||||||
|
|
||||||
|
return idToCallback
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def itemToDbus(item, recursion_depth):
|
||||||
|
result = {}
|
||||||
|
|
||||||
|
if item.get('hidden', False) == True:
|
||||||
|
return None
|
||||||
|
|
||||||
|
string_props = ['label', 'icon-name', 'type', 'children-display']
|
||||||
|
for key in string_props:
|
||||||
|
if key in item:
|
||||||
|
result[key] = GLib.Variant('s', item[key])
|
||||||
|
|
||||||
|
bool_props = ['enabled']
|
||||||
|
for key in bool_props:
|
||||||
|
if key in item:
|
||||||
|
result[key] = GLib.Variant('b', item[key])
|
||||||
|
|
||||||
|
children = []
|
||||||
|
if recursion_depth > 1 or recursion_depth == -1:
|
||||||
|
if "children" in item:
|
||||||
|
children = [DBusMenuService.itemToDbus(item, recursion_depth - 1) for item in item['children']]
|
||||||
|
children = [i for i in children if i is not None]
|
||||||
|
|
||||||
|
return GLib.Variant("(ia{sv}av)", (item['id'], result, children))
|
||||||
|
|
||||||
|
def findItemsWithParent(self, parent_id, items):
|
||||||
|
for item in items:
|
||||||
|
if item.get('hidden', False) == True:
|
||||||
|
continue
|
||||||
|
if 'children' in item:
|
||||||
|
if item['id'] == parent_id:
|
||||||
|
return item['children']
|
||||||
|
else:
|
||||||
|
ret = self.findItemsWithParent(parent_id, item['children'])
|
||||||
|
if ret is not None:
|
||||||
|
return ret
|
||||||
|
return None
|
||||||
|
|
||||||
|
def GetLayout(self, parent_id, recursion_depth, property_names):
|
||||||
|
children = []
|
||||||
|
|
||||||
|
if parent_id == 0:
|
||||||
|
children = self.items
|
||||||
|
else:
|
||||||
|
children = self.findItemsWithParent(parent_id, self.items)
|
||||||
|
if children is None:
|
||||||
|
children = []
|
||||||
|
|
||||||
|
children = [self.itemToDbus(item, recursion_depth) for item in children]
|
||||||
|
children = [i for i in children if i is not None]
|
||||||
|
|
||||||
|
ret = (
|
||||||
|
self.revision,
|
||||||
|
(
|
||||||
|
0,
|
||||||
|
{ 'children-display': GLib.Variant('s', 'submenu') },
|
||||||
|
children
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
return ret
|
||||||
|
|
||||||
|
def Event(self, idx, event_id, data, timestamp):
|
||||||
|
if event_id != "clicked":
|
||||||
|
return
|
||||||
|
|
||||||
|
if idx in self.idToCallback:
|
||||||
|
self.idToCallback[idx]()
|
||||||
|
|
||||||
|
def AboutToShow(self, item_id):
|
||||||
|
return (False,)
|
||||||
|
|
||||||
|
def LayoutUpdated(self, revision, parent):
|
||||||
|
self.emit_signal(
|
||||||
|
'LayoutUpdated',
|
||||||
|
(revision, parent)
|
||||||
|
)
|
||||||
|
|
||||||
|
class StatusNotifierItemService(DBusService):
|
||||||
|
DBUS_SERVICE_PATH = '/io/github/slgobinath/SafeEyes'
|
||||||
|
|
||||||
|
Category = 'ApplicationStatus'
|
||||||
|
Id = 'io.github.slgobinath.SafeEyes'
|
||||||
|
Title = _('Safe Eyes')
|
||||||
|
Status = 'Active'
|
||||||
|
IconName = 'io.github.slgobinath.SafeEyes-enabled'
|
||||||
|
IconThemePath = ''
|
||||||
|
ToolTip = ('', [], _('Safe Eyes'), '')
|
||||||
|
ItemIsMenu = True
|
||||||
|
Menu = None
|
||||||
|
|
||||||
|
def __init__(self, session_bus, context, menu_items):
|
||||||
|
super().__init__(
|
||||||
|
interface_info=SNI_NODE_INFO,
|
||||||
|
object_path=self.DBUS_SERVICE_PATH,
|
||||||
|
bus=session_bus
|
||||||
|
)
|
||||||
|
|
||||||
|
self.bus = session_bus
|
||||||
|
|
||||||
|
self._menu = DBusMenuService(session_bus, context, menu_items)
|
||||||
|
self.Menu = self._menu.DBUS_SERVICE_PATH
|
||||||
|
|
||||||
|
def register(self):
|
||||||
|
self._menu.register()
|
||||||
|
super().register()
|
||||||
|
|
||||||
|
watcher = Gio.DBusProxy.new_sync(
|
||||||
|
connection=self.bus,
|
||||||
|
flags=Gio.DBusProxyFlags.DO_NOT_LOAD_PROPERTIES,
|
||||||
|
info=None,
|
||||||
|
name='org.kde.StatusNotifierWatcher',
|
||||||
|
object_path='/StatusNotifierWatcher',
|
||||||
|
interface_name='org.kde.StatusNotifierWatcher',
|
||||||
|
cancellable=None,
|
||||||
|
)
|
||||||
|
|
||||||
|
watcher.RegisterStatusNotifierItem('(s)', self.DBUS_SERVICE_PATH)
|
||||||
|
|
||||||
|
def unregister(self):
|
||||||
|
super().unregister()
|
||||||
|
self._menu.unregister()
|
||||||
|
|
||||||
|
def set_items(self, items):
|
||||||
|
self._menu.set_items(items)
|
||||||
|
|
||||||
|
def set_icon(self, icon):
|
||||||
|
self.IconName = icon
|
||||||
|
|
||||||
|
self.emit_signal(
|
||||||
|
'NewIcon'
|
||||||
|
)
|
||||||
|
|
||||||
|
def set_tooltip(self, title, description):
|
||||||
|
self.ToolTip = ('', [], title, description)
|
||||||
|
|
||||||
|
self.emit_signal(
|
||||||
|
'NewTooltip'
|
||||||
|
)
|
||||||
|
|
||||||
class TrayIcon:
|
class TrayIcon:
|
||||||
"""
|
"""
|
||||||
|
@ -66,33 +339,61 @@ class TrayIcon:
|
||||||
self.lock = threading.Lock()
|
self.lock = threading.Lock()
|
||||||
self.allow_disabling = plugin_config['allow_disabling']
|
self.allow_disabling = plugin_config['allow_disabling']
|
||||||
self.animate = False
|
self.animate = False
|
||||||
|
self.menu_locked = False
|
||||||
|
|
||||||
# Construct the tray icon
|
session_bus = Gio.bus_get_sync(Gio.BusType.SESSION)
|
||||||
self.indicator = appindicator.Indicator.new(
|
|
||||||
APPINDICATOR_ID, "io.github.slgobinath.SafeEyes-enabled", appindicator.IndicatorCategory.APPLICATION_STATUS)
|
|
||||||
self.indicator.set_status(appindicator.IndicatorStatus.ACTIVE)
|
|
||||||
|
|
||||||
# Construct the context menu
|
self.sni_service = StatusNotifierItemService(
|
||||||
self.menu = Gtk.Menu()
|
session_bus,
|
||||||
|
context,
|
||||||
|
menu_items = self.get_items()
|
||||||
|
)
|
||||||
|
self.sni_service.register()
|
||||||
|
|
||||||
# Next break info menu item
|
self.update_tooltip()
|
||||||
self.item_info = Gtk.ImageMenuItem()
|
|
||||||
img_timer = Gtk.Image()
|
|
||||||
img_timer.set_from_icon_name("io.github.slgobinath.SafeEyes-timer", 16)
|
|
||||||
self.item_info.set_image(img_timer)
|
|
||||||
|
|
||||||
self.item_separator = Gtk.SeparatorMenuItem()
|
def initialize(self, plugin_config):
|
||||||
|
"""
|
||||||
|
Initialize the tray icon by setting the config.
|
||||||
|
"""
|
||||||
|
self.plugin_config = plugin_config
|
||||||
|
self.allow_disabling = plugin_config['allow_disabling']
|
||||||
|
|
||||||
self.item_enable = Gtk.MenuItem()
|
self.update_menu()
|
||||||
self.item_enable.connect('activate', self.on_enable_clicked)
|
self.update_tooltip()
|
||||||
|
|
||||||
self.item_disable = Gtk.MenuItem()
|
def get_items(self):
|
||||||
self.sub_menu_disable = Gtk.Menu()
|
breaks_found = self.has_breaks()
|
||||||
self.sub_menu_disable_items = []
|
|
||||||
|
|
||||||
# Read disable options and build the sub menu
|
info_message = _('No breaks available')
|
||||||
for disable_option in plugin_config['disable_options']:
|
|
||||||
time_in_minutes = disable_option['time']
|
if breaks_found:
|
||||||
|
if self.active:
|
||||||
|
next_break = self.get_next_break_time()
|
||||||
|
|
||||||
|
if next_break is not None:
|
||||||
|
(next_time, next_long_time, next_is_long) = next_break
|
||||||
|
|
||||||
|
if next_long_time:
|
||||||
|
if next_is_long:
|
||||||
|
info_message = _('Next long break at %s') % (next_long_time)
|
||||||
|
else:
|
||||||
|
info_message = _('Next breaks at %s/%s') % (next_time, next_long_time)
|
||||||
|
else:
|
||||||
|
info_message = _('Next break at %s') % (next_time)
|
||||||
|
else:
|
||||||
|
if self.wakeup_time:
|
||||||
|
info_message = _('Disabled until %s') % utility.format_time(self.wakeup_time)
|
||||||
|
else:
|
||||||
|
info_message = _('Disabled until restart')
|
||||||
|
|
||||||
|
disable_items = []
|
||||||
|
|
||||||
|
if self.allow_disabling:
|
||||||
|
disable_option_dynamic_id = 13
|
||||||
|
|
||||||
|
for disable_option in self.plugin_config['disable_options']:
|
||||||
|
time_in_minutes = time_in_x = disable_option['time']
|
||||||
label = []
|
label = []
|
||||||
# Validate time value
|
# Validate time value
|
||||||
if not isinstance(time_in_minutes, int) or time_in_minutes <= 0:
|
if not isinstance(time_in_minutes, int) or time_in_minutes <= 0:
|
||||||
|
@ -113,138 +414,110 @@ class TrayIcon:
|
||||||
logging.error('Invalid unit in disable option: ' + str(disable_option))
|
logging.error('Invalid unit in disable option: ' + str(disable_option))
|
||||||
continue
|
continue
|
||||||
|
|
||||||
# Create submenu
|
label = self.context['locale'].ngettext(label[0], label[1], time_in_x) % time_in_x
|
||||||
sub_menu_item = Gtk.MenuItem()
|
|
||||||
sub_menu_item.connect('activate', self.on_disable_clicked, time_in_minutes)
|
|
||||||
self.sub_menu_disable_items.append([sub_menu_item, label, disable_option['time']])
|
|
||||||
self.sub_menu_disable.append(sub_menu_item)
|
|
||||||
|
|
||||||
# Disable until restart submenu
|
disable_items.append({
|
||||||
self.sub_menu_item_until_restart = Gtk.MenuItem()
|
'id': disable_option_dynamic_id,
|
||||||
self.sub_menu_item_until_restart.connect('activate', self.on_disable_clicked, -1)
|
'label': label,
|
||||||
self.sub_menu_disable.append(self.sub_menu_item_until_restart)
|
'callback': lambda: self.on_disable_clicked(time_in_minutes),
|
||||||
|
})
|
||||||
|
|
||||||
# Add the sub menu to the enable/disable menu
|
disable_option_dynamic_id += 1
|
||||||
self.item_disable.set_submenu(self.sub_menu_disable)
|
|
||||||
|
|
||||||
# Manual break menu item
|
disable_items.append({
|
||||||
self.item_manual_break = Gtk.MenuItem()
|
'id': 12,
|
||||||
|
'label': _('Until restart'),
|
||||||
|
'callback': lambda: self.on_disable_clicked(-1),
|
||||||
|
})
|
||||||
|
|
||||||
self.sub_menu_manual_next_break = Gtk.MenuItem()
|
return [
|
||||||
self.sub_menu_manual_next_break.connect('activate', self.on_manual_break_clicked, None)
|
{
|
||||||
self.sub_menu_manual_next_short_break = Gtk.MenuItem()
|
'id': 1,
|
||||||
self.sub_menu_manual_next_short_break.connect('activate', self.on_manual_break_clicked, BreakType.SHORT_BREAK)
|
'label': info_message,
|
||||||
self.sub_menu_manual_next_long_break = Gtk.MenuItem()
|
'icon-name': "io.github.slgobinath.SafeEyes-timer",
|
||||||
self.sub_menu_manual_next_long_break.connect('activate', self.on_manual_break_clicked, BreakType.LONG_BREAK)
|
'enabled': breaks_found and self.active,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
'id': 2,
|
||||||
|
'type': "separator",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
'id': 3,
|
||||||
|
'label': _("Enable Safe Eyes"),
|
||||||
|
'enabled': breaks_found and not self.active,
|
||||||
|
'callback': self.on_enable_clicked,
|
||||||
|
'hidden': not self.allow_disabling,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
'id': 4,
|
||||||
|
'label': _("Disable Safe Eyes"),
|
||||||
|
'enabled': breaks_found and self.active and not self.menu_locked,
|
||||||
|
'children-display': 'submenu',
|
||||||
|
'children': disable_items,
|
||||||
|
'hidden': not self.allow_disabling,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
'id': 5,
|
||||||
|
'label': _('Take a break now'),
|
||||||
|
'enabled': breaks_found and self.active and not self.menu_locked,
|
||||||
|
'children-display': 'submenu',
|
||||||
|
'children': [
|
||||||
|
{
|
||||||
|
'id': 9,
|
||||||
|
'label': _('Any Break'),
|
||||||
|
'callback': lambda: self.on_manual_break_clicked(None),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
'id': 10,
|
||||||
|
'label': _('Short Break'),
|
||||||
|
'callback': lambda: self.on_manual_break_clicked(BreakType.SHORT_BREAK),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
'id': 11,
|
||||||
|
'label': _('Long Break'),
|
||||||
|
'callback': lambda: self.on_manual_break_clicked(BreakType.LONG_BREAK),
|
||||||
|
},
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
'id': 6,
|
||||||
|
'label': _('Settings'),
|
||||||
|
'enabled': not self.menu_locked,
|
||||||
|
'callback': self.show_settings,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
'id': 7,
|
||||||
|
'label': _('About'),
|
||||||
|
'callback': self.show_about,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
'id': 8,
|
||||||
|
'label': _('Quit'),
|
||||||
|
'enabled': not self.menu_locked,
|
||||||
|
'callback': self.quit_safe_eyes,
|
||||||
|
'hidden': not self.allow_disabling,
|
||||||
|
},
|
||||||
|
]
|
||||||
|
|
||||||
self.sub_menu_manual_break = Gtk.Menu()
|
def update_menu(self):
|
||||||
self.sub_menu_manual_break.append(self.sub_menu_manual_next_break)
|
self.sni_service.set_items(self.get_items())
|
||||||
self.sub_menu_manual_break.append(self.sub_menu_manual_next_short_break)
|
|
||||||
self.sub_menu_manual_break.append(self.sub_menu_manual_next_long_break)
|
|
||||||
self.item_manual_break.set_submenu(self.sub_menu_manual_break)
|
|
||||||
|
|
||||||
# Settings menu item
|
def update_tooltip(self):
|
||||||
self.item_settings = Gtk.MenuItem()
|
next_break = self.get_next_break_time()
|
||||||
self.item_settings.connect('activate', self.show_settings)
|
|
||||||
|
|
||||||
# About menu item
|
if next_break is not None and self.plugin_config.get('show_time_in_tray', False):
|
||||||
self.item_about = Gtk.MenuItem()
|
(next_time, next_long_time, _next_is_long) = next_break
|
||||||
self.item_about.connect('activate', self.show_about)
|
|
||||||
|
|
||||||
# Quit menu item
|
if next_long_time and self.plugin_config.get('show_long_time_in_tray', False):
|
||||||
self.item_quit = Gtk.MenuItem()
|
description = next_long_time
|
||||||
self.item_quit.connect('activate', self.quit_safe_eyes)
|
|
||||||
|
|
||||||
self.set_labels()
|
|
||||||
|
|
||||||
# At startup, no need for activate menu
|
|
||||||
self.item_enable.set_sensitive(False)
|
|
||||||
|
|
||||||
# Append all menu items and show the menu
|
|
||||||
self.menu.append(self.item_info)
|
|
||||||
self.menu.append(self.item_separator)
|
|
||||||
self.menu.append(self.item_enable)
|
|
||||||
self.menu.append(self.item_disable)
|
|
||||||
self.menu.append(self.item_manual_break)
|
|
||||||
self.menu.append(self.item_settings)
|
|
||||||
self.menu.append(self.item_about)
|
|
||||||
self.menu.append(self.item_quit)
|
|
||||||
self.menu.show_all()
|
|
||||||
|
|
||||||
self.item_enable.set_visible(self.allow_disabling)
|
|
||||||
self.item_disable.set_visible(self.allow_disabling)
|
|
||||||
self.item_quit.set_visible(self.allow_disabling)
|
|
||||||
self.item_quit.set_visible(self.allow_disabling)
|
|
||||||
|
|
||||||
self.indicator.set_menu(self.menu)
|
|
||||||
|
|
||||||
def initialize(self, plugin_config):
|
|
||||||
"""
|
|
||||||
Initialize the tray icon by setting the config.
|
|
||||||
"""
|
|
||||||
self.plugin_config = plugin_config
|
|
||||||
self.set_labels()
|
|
||||||
self.allow_disabling = plugin_config['allow_disabling']
|
|
||||||
self.item_enable.set_visible(self.allow_disabling)
|
|
||||||
self.item_disable.set_visible(self.allow_disabling)
|
|
||||||
self.item_quit.set_visible(self.allow_disabling)
|
|
||||||
self.item_quit.set_visible(self.allow_disabling)
|
|
||||||
|
|
||||||
def set_labels(self):
|
|
||||||
"""
|
|
||||||
Update the text of menu items based on the selected language.
|
|
||||||
"""
|
|
||||||
for entry in self.sub_menu_disable_items:
|
|
||||||
# print(self.context['locale'].ngettext('For %d Hour', 'For %d Hours', 1) % 1)
|
|
||||||
entry[0].set_label(self.context['locale'].ngettext(entry[1][0], entry[1][1], entry[2]) % entry[2])
|
|
||||||
|
|
||||||
self.sub_menu_item_until_restart.set_label(_('Until restart'))
|
|
||||||
self.item_enable.set_label(_('Enable Safe Eyes'))
|
|
||||||
self.item_disable.set_label(_('Disable Safe Eyes'))
|
|
||||||
|
|
||||||
breaks_found = self.has_breaks()
|
|
||||||
if breaks_found:
|
|
||||||
if self.active:
|
|
||||||
if self.date_time:
|
|
||||||
self.__set_next_break_info()
|
|
||||||
self.indicator.set_icon("io.github.slgobinath.SafeEyes-enabled")
|
|
||||||
else:
|
else:
|
||||||
if self.wakeup_time:
|
description = next_time
|
||||||
self.item_info.set_label(_('Disabled until %s') % utility.format_time(self.wakeup_time))
|
|
||||||
else:
|
else:
|
||||||
self.item_info.set_label(_('Disabled until restart'))
|
description = ''
|
||||||
self.indicator.set_label('', '')
|
|
||||||
self.indicator.set_icon("io.github.slgobinath.SafeEyes-disabled")
|
|
||||||
else:
|
|
||||||
self.item_info.set_label(_('No breaks available'))
|
|
||||||
self.indicator.set_label('', '')
|
|
||||||
self.indicator.set_icon("io.github.slgobinath.SafeEyes-disabled")
|
|
||||||
self.item_info.set_sensitive(breaks_found and self.active)
|
|
||||||
self.item_enable.set_sensitive(breaks_found and not self.active)
|
|
||||||
self.item_disable.set_sensitive(breaks_found and self.active)
|
|
||||||
self.item_manual_break.set_sensitive(breaks_found and self.active)
|
|
||||||
|
|
||||||
self.item_manual_break.set_label(_('Take a break now'))
|
self.sni_service.set_tooltip(_('Safe Eyes'), description)
|
||||||
self.sub_menu_manual_next_break.set_label(_('Any break'))
|
|
||||||
self.sub_menu_manual_next_short_break.set_label(_('Short break'))
|
|
||||||
self.sub_menu_manual_next_long_break.set_label(_('Long break'))
|
|
||||||
self.item_settings.set_label(_('Settings'))
|
|
||||||
self.item_about.set_label(_('About'))
|
|
||||||
self.item_quit.set_label(_('Quit'))
|
|
||||||
|
|
||||||
def show_icon(self):
|
def quit_safe_eyes(self):
|
||||||
"""
|
|
||||||
Show the tray icon.
|
|
||||||
"""
|
|
||||||
self.indicator.set_status(appindicator.IndicatorStatus.ACTIVE)
|
|
||||||
|
|
||||||
def hide_icon(self):
|
|
||||||
"""
|
|
||||||
Hide the tray icon.
|
|
||||||
"""
|
|
||||||
self.indicator.set_status(appindicator.IndicatorStatus.PASSIVE)
|
|
||||||
|
|
||||||
def quit_safe_eyes(self, *args):
|
|
||||||
"""
|
"""
|
||||||
Handle Quit menu action.
|
Handle Quit menu action.
|
||||||
This action terminates the application.
|
This action terminates the application.
|
||||||
|
@ -257,14 +530,14 @@ class TrayIcon:
|
||||||
self.idle_condition.release()
|
self.idle_condition.release()
|
||||||
self.quit()
|
self.quit()
|
||||||
|
|
||||||
def show_settings(self, *args):
|
def show_settings(self):
|
||||||
"""
|
"""
|
||||||
Handle Settings menu action.
|
Handle Settings menu action.
|
||||||
This action shows the Settings dialog.
|
This action shows the Settings dialog.
|
||||||
"""
|
"""
|
||||||
self.on_show_settings()
|
self.on_show_settings()
|
||||||
|
|
||||||
def show_about(self, *args):
|
def show_about(self):
|
||||||
"""
|
"""
|
||||||
Handle About menu action.
|
Handle About menu action.
|
||||||
This action shows the About dialog.
|
This action shows the About dialog.
|
||||||
|
@ -277,50 +550,37 @@ class TrayIcon:
|
||||||
"""
|
"""
|
||||||
logging.info("Update next break information")
|
logging.info("Update next break information")
|
||||||
self.date_time = dateTime
|
self.date_time = dateTime
|
||||||
self.__set_next_break_info()
|
self.update_menu()
|
||||||
|
self.update_tooltip()
|
||||||
|
|
||||||
|
def get_next_break_time(self):
|
||||||
|
if not (self.has_breaks() and self.active and self.date_time):
|
||||||
|
return None
|
||||||
|
|
||||||
def __set_next_break_info(self):
|
|
||||||
"""
|
|
||||||
A private method to be called within this class to update the next break information using self.dateTime.
|
|
||||||
"""
|
|
||||||
formatted_time = utility.format_time(self.get_break_time())
|
formatted_time = utility.format_time(self.get_break_time())
|
||||||
long_time = self.get_break_time(BreakType.LONG_BREAK)
|
long_time = self.get_break_time(BreakType.LONG_BREAK)
|
||||||
|
|
||||||
if long_time:
|
if long_time:
|
||||||
long_time = utility.format_time(long_time)
|
long_time = utility.format_time(long_time)
|
||||||
if long_time == formatted_time:
|
if long_time == formatted_time:
|
||||||
message = _('Next long break at %s') % (long_time)
|
return (long_time, long_time, True)
|
||||||
else:
|
else:
|
||||||
message = _('Next breaks at %s/%s') % (formatted_time, long_time)
|
return (formatted_time, long_time, False)
|
||||||
else:
|
|
||||||
message = _('Next break at %s') % (formatted_time)
|
|
||||||
|
|
||||||
# Update the menu item label
|
return (formatted_time, None, False)
|
||||||
utility.execute_main_thread(self.item_info.set_label, message)
|
|
||||||
|
|
||||||
# Update the tray icon label
|
|
||||||
if self.plugin_config.get('show_time_in_tray', False):
|
|
||||||
show_long = long_time and self.plugin_config.get('show_long_time_in_tray', False)
|
|
||||||
self.indicator.set_label(long_time if show_long else formatted_time, '')
|
|
||||||
else:
|
|
||||||
self.indicator.set_label('', '')
|
|
||||||
|
|
||||||
def on_manual_break_clicked(self, *args):
|
def on_manual_break_clicked(self, break_type):
|
||||||
"""
|
"""
|
||||||
Trigger a break manually.
|
Trigger a break manually.
|
||||||
"""
|
"""
|
||||||
if len(args) > 1:
|
|
||||||
break_type = args[1]
|
|
||||||
self.take_break(break_type)
|
self.take_break(break_type)
|
||||||
else:
|
|
||||||
self.take_break()
|
|
||||||
|
|
||||||
def on_enable_clicked(self, *args):
|
def on_enable_clicked(self):
|
||||||
"""
|
"""
|
||||||
Handle 'Enable Safe Eyes' menu action.
|
Handle 'Enable Safe Eyes' menu action.
|
||||||
This action enables the application if it is currently disabled.
|
This action enables the application if it is currently disabled.
|
||||||
"""
|
"""
|
||||||
# active = self.item_enable.get_active()
|
|
||||||
if not self.active:
|
if not self.active:
|
||||||
with self.lock:
|
with self.lock:
|
||||||
self.enable_ui()
|
self.enable_ui()
|
||||||
|
@ -330,41 +590,40 @@ class TrayIcon:
|
||||||
self.idle_condition.notify_all()
|
self.idle_condition.notify_all()
|
||||||
self.idle_condition.release()
|
self.idle_condition.release()
|
||||||
|
|
||||||
def on_disable_clicked(self, *args):
|
def on_disable_clicked(self, time_to_wait):
|
||||||
"""
|
"""
|
||||||
Handle the menu actions of all the sub menus of 'Disable Safe Eyes'.
|
Handle the menu actions of all the sub menus of 'Disable Safe Eyes'.
|
||||||
This action disables the application if it is currently active.
|
This action disables the application if it is currently active.
|
||||||
"""
|
"""
|
||||||
# active = self.item_enable.get_active()
|
if self.active:
|
||||||
if self.active and len(args) > 1:
|
|
||||||
self.disable_ui()
|
self.disable_ui()
|
||||||
|
|
||||||
time_to_wait = args[1]
|
|
||||||
if time_to_wait <= 0:
|
if time_to_wait <= 0:
|
||||||
info = _('Disabled until restart')
|
info = _('Disabled until restart')
|
||||||
self.disable_safeeyes(info)
|
self.disable_safeeyes(info)
|
||||||
self.wakeup_time = None
|
self.wakeup_time = None
|
||||||
self.item_info.set_label(info)
|
|
||||||
else:
|
else:
|
||||||
self.wakeup_time = datetime.datetime.now() + datetime.timedelta(minutes=time_to_wait)
|
self.wakeup_time = datetime.datetime.now() + datetime.timedelta(minutes=time_to_wait)
|
||||||
info = _('Disabled until %s') % utility.format_time(self.wakeup_time)
|
info = _('Disabled until %s') % utility.format_time(self.wakeup_time)
|
||||||
self.disable_safeeyes(info)
|
self.disable_safeeyes(info)
|
||||||
self.item_info.set_label(info)
|
|
||||||
utility.start_thread(self.__schedule_resume, time_minutes=time_to_wait)
|
utility.start_thread(self.__schedule_resume, time_minutes=time_to_wait)
|
||||||
|
self.update_menu()
|
||||||
|
|
||||||
def lock_menu(self):
|
def lock_menu(self):
|
||||||
"""
|
"""
|
||||||
This method is called by the core to prevent user from disabling Safe Eyes after the notification.
|
This method is called by the core to prevent user from disabling Safe Eyes after the notification.
|
||||||
"""
|
"""
|
||||||
if self.active:
|
if self.active:
|
||||||
self.menu.set_sensitive(False)
|
self.menu_locked = True
|
||||||
|
self.update_menu()
|
||||||
|
|
||||||
def unlock_menu(self):
|
def unlock_menu(self):
|
||||||
"""
|
"""
|
||||||
This method is called by the core to activate the menu after the the break.
|
This method is called by the core to activate the menu after the the break.
|
||||||
"""
|
"""
|
||||||
if self.active:
|
if self.active:
|
||||||
self.menu.set_sensitive(True)
|
self.menu_locked = False
|
||||||
|
self.update_menu()
|
||||||
|
|
||||||
def disable_ui(self):
|
def disable_ui(self):
|
||||||
"""
|
"""
|
||||||
|
@ -373,13 +632,9 @@ class TrayIcon:
|
||||||
if self.active:
|
if self.active:
|
||||||
logging.info('Disable Safe Eyes')
|
logging.info('Disable Safe Eyes')
|
||||||
self.active = False
|
self.active = False
|
||||||
self.indicator.set_icon("io.github.slgobinath.SafeEyes-disabled")
|
|
||||||
self.item_info.set_label(_('Disabled until restart'))
|
self.sni_service.set_icon("io.github.slgobinath.SafeEyes-disabled")
|
||||||
self.indicator.set_label('', '')
|
self.update_menu()
|
||||||
self.item_info.set_sensitive(False)
|
|
||||||
self.item_enable.set_sensitive(True)
|
|
||||||
self.item_disable.set_sensitive(False)
|
|
||||||
self.item_manual_break.set_sensitive(False)
|
|
||||||
|
|
||||||
def enable_ui(self):
|
def enable_ui(self):
|
||||||
"""
|
"""
|
||||||
|
@ -388,11 +643,9 @@ class TrayIcon:
|
||||||
if not self.active:
|
if not self.active:
|
||||||
logging.info('Enable Safe Eyes')
|
logging.info('Enable Safe Eyes')
|
||||||
self.active = True
|
self.active = True
|
||||||
self.indicator.set_icon("io.github.slgobinath.SafeEyes-enabled")
|
|
||||||
self.item_info.set_sensitive(True)
|
self.sni_service.set_icon("io.github.slgobinath.SafeEyes-enabled")
|
||||||
self.item_enable.set_sensitive(False)
|
self.update_menu()
|
||||||
self.item_disable.set_sensitive(True)
|
|
||||||
self.item_manual_break.set_sensitive(True)
|
|
||||||
|
|
||||||
def __schedule_resume(self, time_minutes):
|
def __schedule_resume(self, time_minutes):
|
||||||
"""
|
"""
|
||||||
|
@ -404,14 +657,14 @@ class TrayIcon:
|
||||||
|
|
||||||
with self.lock:
|
with self.lock:
|
||||||
if not self.active:
|
if not self.active:
|
||||||
utility.execute_main_thread(self.item_enable.activate)
|
utility.execute_main_thread(self.on_enable_clicked)
|
||||||
|
|
||||||
def start_animation(self):
|
def start_animation(self):
|
||||||
if not self.active or not self.animate:
|
if not self.active or not self.animate:
|
||||||
return
|
return
|
||||||
utility.execute_main_thread(lambda: self.indicator.set_icon("io.github.slgobinath.SafeEyes-disabled"))
|
utility.execute_main_thread(lambda: self.sni_service.set_icon("io.github.slgobinath.SafeEyes-disabled"))
|
||||||
time.sleep(0.5)
|
time.sleep(0.5)
|
||||||
utility.execute_main_thread(lambda: self.indicator.set_icon("io.github.slgobinath.SafeEyes-enabled"))
|
utility.execute_main_thread(lambda: self.sni_service.set_icon("io.github.slgobinath.SafeEyes-enabled"))
|
||||||
if self.animate and self.active:
|
if self.animate and self.active:
|
||||||
time.sleep(0.5)
|
time.sleep(0.5)
|
||||||
if self.animate and self.active:
|
if self.animate and self.active:
|
||||||
|
@ -420,9 +673,9 @@ class TrayIcon:
|
||||||
def stop_animation(self):
|
def stop_animation(self):
|
||||||
self.animate = False
|
self.animate = False
|
||||||
if self.active:
|
if self.active:
|
||||||
utility.execute_main_thread(lambda: self.indicator.set_icon("io.github.slgobinath.SafeEyes-enabled"))
|
utility.execute_main_thread(lambda: self.sni_service.set_icon("io.github.slgobinath.SafeEyes-enabled"))
|
||||||
else:
|
else:
|
||||||
utility.execute_main_thread(lambda: self.indicator.set_icon("io.github.slgobinath.SafeEyes-disabled"))
|
utility.execute_main_thread(lambda: self.sni_service.set_icon("io.github.slgobinath.SafeEyes-disabled"))
|
||||||
|
|
||||||
def init(ctx, safeeyes_cfg, plugin_config):
|
def init(ctx, safeeyes_cfg, plugin_config):
|
||||||
"""
|
"""
|
||||||
|
|
|
@ -25,9 +25,7 @@ import logging
|
||||||
import os
|
import os
|
||||||
from threading import Timer
|
from threading import Timer
|
||||||
|
|
||||||
import dbus
|
|
||||||
import gi
|
import gi
|
||||||
from dbus.mainloop.glib import DBusGMainLoop
|
|
||||||
from safeeyes import utility
|
from safeeyes import utility
|
||||||
from safeeyes.ui.about_dialog import AboutDialog
|
from safeeyes.ui.about_dialog import AboutDialog
|
||||||
from safeeyes.ui.break_screen import BreakScreen
|
from safeeyes.ui.break_screen import BreakScreen
|
||||||
|
@ -38,7 +36,7 @@ from safeeyes.core import SafeEyesCore
|
||||||
from safeeyes.ui.settings_dialog import SettingsDialog
|
from safeeyes.ui.settings_dialog import SettingsDialog
|
||||||
|
|
||||||
gi.require_version('Gtk', '3.0')
|
gi.require_version('Gtk', '3.0')
|
||||||
from gi.repository import Gtk
|
from gi.repository import Gtk, Gio
|
||||||
|
|
||||||
SAFE_EYES_VERSION = "2.1.6"
|
SAFE_EYES_VERSION = "2.1.6"
|
||||||
|
|
||||||
|
@ -166,14 +164,28 @@ class SafeEyes:
|
||||||
self.plugins_manager.start()
|
self.plugins_manager.start()
|
||||||
self.safe_eyes_core.start()
|
self.safe_eyes_core.start()
|
||||||
|
|
||||||
|
def handle_suspend_signal(self, proxy, sender, signal, parameters):
|
||||||
|
if signal != "PrepareForSleep":
|
||||||
|
return
|
||||||
|
|
||||||
|
(sleeping, ) = parameters
|
||||||
|
|
||||||
|
self.handle_suspend_callback(sleeping)
|
||||||
|
|
||||||
def handle_system_suspend(self):
|
def handle_system_suspend(self):
|
||||||
"""
|
"""
|
||||||
Setup system suspend listener.
|
Setup system suspend listener.
|
||||||
"""
|
"""
|
||||||
DBusGMainLoop(set_as_default=True)
|
self.suspend_proxy = Gio.DBusProxy.new_for_bus_sync(
|
||||||
bus = dbus.SystemBus()
|
bus_type=Gio.BusType.SYSTEM,
|
||||||
bus.add_signal_receiver(self.handle_suspend_callback, 'PrepareForSleep',
|
flags=Gio.DBusProxyFlags.DO_NOT_LOAD_PROPERTIES,
|
||||||
'org.freedesktop.login1.Manager', 'org.freedesktop.login1')
|
info=None,
|
||||||
|
name='org.freedesktop.login1',
|
||||||
|
object_path='/org/freedesktop/login1',
|
||||||
|
interface_name='org.freedesktop.login1.Manager',
|
||||||
|
cancellable=None,
|
||||||
|
)
|
||||||
|
self.suspend_proxy.connect('g-signal', self.handle_suspend_signal)
|
||||||
|
|
||||||
def on_skipped(self):
|
def on_skipped(self):
|
||||||
"""
|
"""
|
||||||
|
|
Loading…
Reference in New Issue