2017-10-07 15:10:31 +02:00
|
|
|
#!/usr/bin/env python
|
|
|
|
# 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/>.
|
|
|
|
"""
|
|
|
|
Skip Fullscreen plugin skips the break if the active window is fullscreen.
|
2021-01-27 04:55:59 +01:00
|
|
|
NOTE: Do not remove the unused import 'GdkX11' because it is required in Ubuntu 14.04
|
2017-10-07 15:10:31 +02:00
|
|
|
"""
|
|
|
|
|
2018-06-22 21:11:09 +02:00
|
|
|
import os
|
2017-10-07 15:10:31 +02:00
|
|
|
import logging
|
|
|
|
import re
|
|
|
|
import subprocess
|
|
|
|
|
|
|
|
import gi
|
|
|
|
gi.require_version('Gdk', '3.0')
|
|
|
|
from gi.repository import Gdk
|
2017-11-11 16:53:24 +01:00
|
|
|
from gi.repository import GdkX11 # noqa F401
|
2021-04-18 21:47:53 +02:00
|
|
|
from safeeyes import utility
|
2017-10-07 15:10:31 +02:00
|
|
|
|
|
|
|
context = None
|
|
|
|
skip_break_window_classes = []
|
|
|
|
take_break_window_classes = []
|
|
|
|
unfullscreen_allowed = True
|
2018-06-23 00:22:14 +02:00
|
|
|
dnd_while_on_battery = False
|
2017-10-07 15:10:31 +02:00
|
|
|
|
2018-06-23 13:38:13 +02:00
|
|
|
|
2021-04-18 21:47:53 +02:00
|
|
|
def is_active_window_skipped_wayland(pre_break):
|
|
|
|
cmdlist = ['wlrctl', 'toplevel', 'find', 'state:fullscreen']
|
|
|
|
try:
|
|
|
|
process = subprocess.Popen(cmdlist, stdout=subprocess.PIPE)
|
|
|
|
process.communicate()[0]
|
|
|
|
if process.returncode == 0:
|
|
|
|
return True
|
|
|
|
elif process.returncode == 1:
|
|
|
|
return False
|
|
|
|
elif process.returncode == 127:
|
|
|
|
logging.warning('Could not find wlrctl needed to detect fullscreen under wayland')
|
|
|
|
return False
|
|
|
|
except subprocess.CalledProcessError:
|
|
|
|
logging.warning('Error in finding full-screen application')
|
|
|
|
return False
|
|
|
|
|
|
|
|
|
|
|
|
def is_active_window_skipped_xorg(pre_break):
|
2017-10-07 15:10:31 +02:00
|
|
|
"""
|
|
|
|
Check for full-screen applications.
|
2018-06-22 21:11:09 +02:00
|
|
|
This method must be executed by the main thread. If not, it will cause random failure.
|
2017-10-07 15:10:31 +02:00
|
|
|
"""
|
|
|
|
logging.info('Searching for full-screen application')
|
|
|
|
screen = Gdk.Screen.get_default()
|
|
|
|
|
|
|
|
active_window = screen.get_active_window()
|
|
|
|
if active_window:
|
|
|
|
active_xid = str(active_window.get_xid())
|
2018-06-22 21:11:09 +02:00
|
|
|
cmdlist = ['xprop', '-root', '-notype', '-id',
|
|
|
|
active_xid, 'WM_CLASS', '_NET_WM_STATE']
|
2017-10-07 15:10:31 +02:00
|
|
|
|
|
|
|
try:
|
|
|
|
stdout = subprocess.check_output(cmdlist).decode('utf-8')
|
|
|
|
except subprocess.CalledProcessError:
|
|
|
|
logging.warning('Error in finding full-screen application')
|
|
|
|
else:
|
|
|
|
if stdout:
|
|
|
|
is_fullscreen = 'FULLSCREEN' in stdout
|
|
|
|
# Extract the process name
|
|
|
|
process_names = re.findall('"(.+?)"', stdout)
|
|
|
|
if process_names:
|
|
|
|
process = process_names[1].lower()
|
|
|
|
if process in skip_break_window_classes:
|
|
|
|
return True
|
|
|
|
elif process in take_break_window_classes:
|
2018-05-15 12:47:25 +02:00
|
|
|
if is_fullscreen and unfullscreen_allowed and not pre_break:
|
2017-10-07 15:10:31 +02:00
|
|
|
try:
|
|
|
|
active_window.unfullscreen()
|
|
|
|
except BaseException:
|
2018-06-22 21:11:09 +02:00
|
|
|
logging.error(
|
|
|
|
'Error in unfullscreen the window ' + process)
|
2017-10-07 15:10:31 +02:00
|
|
|
return False
|
|
|
|
|
|
|
|
return is_fullscreen
|
|
|
|
|
|
|
|
return False
|
|
|
|
|
|
|
|
|
2018-06-22 21:11:09 +02:00
|
|
|
def is_on_battery():
|
|
|
|
"""
|
|
|
|
Check if the computer is running on battery.
|
|
|
|
"""
|
2018-06-24 13:52:34 +02:00
|
|
|
on_battery = False
|
2018-06-22 21:11:09 +02:00
|
|
|
available_power_sources = os.listdir('/sys/class/power_supply')
|
2018-06-23 13:38:13 +02:00
|
|
|
logging.info('Looking for battery status in available power sources: %s' % str(
|
|
|
|
available_power_sources))
|
2018-06-22 21:11:09 +02:00
|
|
|
for power_source in available_power_sources:
|
|
|
|
if 'BAT' in power_source:
|
|
|
|
# Found battery
|
|
|
|
battery_status = os.path.join(
|
|
|
|
'/sys/class/power_supply', power_source, 'status')
|
|
|
|
if os.path.isfile(battery_status):
|
|
|
|
# Additional check to confirm that the status file exists
|
|
|
|
try:
|
|
|
|
with open(battery_status, 'r') as status_file:
|
|
|
|
status = status_file.read()
|
|
|
|
if status:
|
2018-06-24 13:52:34 +02:00
|
|
|
on_battery = 'discharging' in status.lower()
|
2018-06-22 21:11:09 +02:00
|
|
|
except BaseException:
|
|
|
|
logging.error('Failed to read %s' % battery_status)
|
|
|
|
break
|
2018-06-24 13:52:34 +02:00
|
|
|
return on_battery
|
2018-06-22 21:11:09 +02:00
|
|
|
|
|
|
|
|
2017-10-07 15:10:31 +02:00
|
|
|
def init(ctx, safeeyes_config, plugin_config):
|
|
|
|
global context
|
|
|
|
global skip_break_window_classes
|
|
|
|
global take_break_window_classes
|
|
|
|
global unfullscreen_allowed
|
2018-06-22 21:11:09 +02:00
|
|
|
global dnd_while_on_battery
|
2017-10-07 15:10:31 +02:00
|
|
|
logging.debug('Initialize Skip Fullscreen plugin')
|
|
|
|
context = ctx
|
|
|
|
skip_break_window_classes = plugin_config['skip_break_windows'].split()
|
|
|
|
take_break_window_classes = plugin_config['take_break_windows'].split()
|
|
|
|
unfullscreen_allowed = plugin_config['unfullscreen']
|
2018-06-22 21:11:09 +02:00
|
|
|
dnd_while_on_battery = plugin_config['while_on_battery']
|
2017-10-07 15:10:31 +02:00
|
|
|
|
|
|
|
|
2018-05-15 12:47:25 +02:00
|
|
|
def on_pre_break(break_obj):
|
|
|
|
"""
|
2018-06-22 21:11:09 +02:00
|
|
|
Lifecycle method executes before the pre-break period.
|
2018-05-15 12:47:25 +02:00
|
|
|
"""
|
2021-04-18 21:47:53 +02:00
|
|
|
if utility.IS_WAYLAND:
|
|
|
|
skip_break = is_active_window_skipped_wayland(True)
|
|
|
|
else:
|
|
|
|
skip_break = is_active_window_skipped_xorg(True)
|
2018-06-22 21:11:09 +02:00
|
|
|
if dnd_while_on_battery and not skip_break:
|
|
|
|
skip_break = is_on_battery()
|
|
|
|
return skip_break
|
2018-05-15 12:47:25 +02:00
|
|
|
|
|
|
|
|
2017-10-07 15:10:31 +02:00
|
|
|
def on_start_break(break_obj):
|
|
|
|
"""
|
2018-06-22 21:11:09 +02:00
|
|
|
Lifecycle method executes just before the break.
|
2017-10-07 15:10:31 +02:00
|
|
|
"""
|
2021-04-18 21:47:53 +02:00
|
|
|
if utility.IS_WAYLAND:
|
|
|
|
skip_break = is_active_window_skipped_wayland(True)
|
|
|
|
else:
|
|
|
|
skip_break = is_active_window_skipped_xorg(True)
|
2018-06-22 21:11:09 +02:00
|
|
|
if dnd_while_on_battery and not skip_break:
|
|
|
|
skip_break = is_on_battery()
|
|
|
|
return skip_break
|