This commit is contained in:
deltragon 2024-09-30 20:06:48 -04:00 committed by GitHub
commit b8b1f9f6a5
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
5 changed files with 139 additions and 1 deletions

View File

@ -5,7 +5,7 @@
"version": "0.0.3"
},
"dependencies": {
"python_modules": [],
"python_modules": ["pywayland"],
"shell_commands": [],
"operating_systems": [],
"desktop_environments": [],

View File

@ -25,6 +25,11 @@ def validate(plugin_config, plugin_settings):
command = "dbus-send"
elif utility.DESKTOP_ENVIRONMENT == "sway":
command = "swayidle"
elif utility.IS_WAYLAND:
if not utility.module_exist("pywayland"):
return _("Please install the Python module '%s'") % "pywayland"
# no command needed with pywayland
return None
else:
command = "xprintidle"
if not utility.command_exist(command):

View File

@ -0,0 +1,93 @@
# Safe Eyes is a utility to remind you to take break frequently
# to protect your eyes from eye strain.
# Copyright (C) 2017 Gobinath
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
# This file is heavily inspired by https://github.com/juienpro/easyland/blob/efc26a0b22d7bdbb0f8436183428f7036da4662a/src/easyland/idle.py
import logging
import threading
import datetime
from pywayland.client import Display
from pywayland.protocol.wayland.wl_seat import WlSeat
from pywayland.protocol.ext_idle_notify_v1 import (
ExtIdleNotificationV1,
ExtIdleNotifierV1
)
class ExtIdleNotify:
_idle_notifier = None
_seat = None
_notification = None
_notifier_set = False
_running = True
_thread = None
_idle_since = None
def __init__(self):
self._display = Display()
self._display.connect()
def stop(self):
self._running = False
self._notification.destroy()
self._notification = None
self._seat = None
self._thread.join()
def run(self):
self._thread = threading.Thread(target=self._run, name="ExtIdleNotify", daemon=False)
self._thread.start()
def _run(self):
reg = self._display.get_registry()
reg.dispatcher['global'] = self._global_handler
while self._running:
self._display.dispatch(block=True)
self._display.disconnect()
def _global_handler(self, reg, id_num, iface_name, version):
if iface_name == 'wl_seat':
self._seat = reg.bind(id_num, WlSeat, version)
if iface_name == "ext_idle_notifier_v1":
self._idle_notifier = reg.bind(id_num, ExtIdleNotifierV1, version)
if self._idle_notifier and self._seat and not self._notifier_set:
self._notifier_set = True
timeout_sec = 1
self._notification = self._idle_notifier.get_idle_notification(timeout_sec * 1000, self._seat)
self._notification.dispatcher['idled'] = self._idle_notifier_handler
self._notification.dispatcher['resumed'] = self._idle_notifier_resume_handler
def _idle_notifier_handler(self, notification):
self._idle_since = datetime.datetime.now()
def _idle_notifier_resume_handler(self, notification):
self._idle_since = None
def get_idle_time_seconds(self):
if self._idle_since is None:
return 0
result = datetime.datetime.now() - self._idle_since
return result.total_seconds()

View File

@ -46,11 +46,16 @@ waiting_time = 2
is_wayland_and_gnome = False
use_swayidle = False
use_ext_idle_notify = False
swayidle_process = None
swayidle_lock = threading.Lock()
swayidle_idle = 0
swayidle_active = 0
ext_idle_notify_lock = threading.Lock()
ext_idle_notification_obj = None
# swayidle
def __swayidle_running():
return (swayidle_process is not None and
swayidle_process.poll() is None)
@ -88,6 +93,33 @@ def __swayidle_idle_time():
return idle_time
return 0
# ext idle
def __start_ext_idle_monitor():
global ext_idle_notification_obj
from .ext_idle_notify import ExtIdleNotify
ext_idle_notification_obj = ExtIdleNotify()
ext_idle_notification_obj.run()
def __stop_ext_idle_monitor():
global ext_idle_notification_obj
with ext_idle_notify_lock:
if ext_idle_notification_obj is not None:
ext_idle_notification_obj.stop()
ext_idle_notification_obj = None;
def __ext_idle_idle_time():
global ext_idle_notification_obj
with ext_idle_notify_lock:
if ext_idle_notification_obj is None:
__start_ext_idle_monitor()
else:
return ext_idle_notification_obj.get_idle_time_seconds()
return 0
# gnome
def __gnome_wayland_idle_time():
"""
Determine system idle time in seconds, specifically for gnome with wayland.
@ -119,6 +151,8 @@ def __system_idle_time():
return __gnome_wayland_idle_time()
elif use_swayidle:
return __swayidle_idle_time()
elif use_ext_idle_notify:
return __ext_idle_idle_time()
# Convert to seconds
return int(subprocess.check_output(['xprintidle']).decode('utf-8')) / 1000
except BaseException:
@ -159,6 +193,7 @@ def init(ctx, safeeyes_config, plugin_config):
global postpone_if_active
global is_wayland_and_gnome
global use_swayidle
global use_ext_idle_notify
logging.debug('Initialize Smart Pause plugin')
context = ctx
enable_safeeyes = context['api']['enable_safeeyes']
@ -172,6 +207,7 @@ def init(ctx, safeeyes_config, plugin_config):
waiting_time = min(2, idle_time) # If idle time is 1 sec, wait only 1 sec
is_wayland_and_gnome = context['desktop'] == 'gnome' and context['is_wayland']
use_swayidle = context['desktop'] == 'sway'
use_ext_idle_notify = context['is_wayland'] and not use_swayidle and not is_wayland_and_gnome
def __start_idle_monitor():
@ -245,6 +281,9 @@ def on_stop():
idle_condition.notify_all()
idle_condition.release()
if use_ext_idle_notify:
__stop_ext_idle_monitor()
def update_next_break(break_obj, dateTime):
"""

View File

@ -4,6 +4,7 @@ import setuptools
requires = [
'pywayland',
'babel',
'psutil',
'croniter',