Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Smartpause: add support for ext-idle-notify-v1 using pywayland #576

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
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/plugins/smartpause/config.json
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
"version": "0.0.3"
},
"dependencies": {
"python_modules": [],
"python_modules": ["pywayland"],
"shell_commands": [],
"operating_systems": [],
"desktop_environments": [],
Expand Down
5 changes: 5 additions & 0 deletions safeeyes/plugins/smartpause/dependency_checker.py
Original file line number Diff line number Diff line change
Expand Up @@ -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):
Expand Down
93 changes: 93 additions & 0 deletions safeeyes/plugins/smartpause/ext_idle_notify.py
Original file line number Diff line number Diff line change
@@ -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()


39 changes: 39 additions & 0 deletions safeeyes/plugins/smartpause/plugin.py
Original file line number Diff line number Diff line change
Expand Up @@ -46,11 +46,16 @@
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)
Expand Down Expand Up @@ -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.
Expand Down Expand Up @@ -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:
Expand Down Expand Up @@ -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']
Expand All @@ -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():
Expand Down Expand Up @@ -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):
"""
Expand Down
1 change: 1 addition & 0 deletions setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@


requires = [
'pywayland',
'babel',
'psutil',
'croniter',
Expand Down
Loading