Add generic toggle button module, and implement caffeine with it

master
Lars Vierbergen 8 years ago
parent 536d14cb37
commit 5e1111727d
  1. 47
      modules/caffeine.py
  2. 61
      modules/toggle.py
  3. 37
      modules/util.py

@ -1,19 +1,16 @@
import subprocess
import logging
import time
from .core import AbstractControl, action
from modules.toggle import ToggleAction, ToggleControl
from .util import process_reaper, backoff
logger = logging.getLogger(__name__)
class CaffeineControl(AbstractControl):
class CaffeineToggleAction(ToggleAction):
def __init__(self):
super().__init__()
self.caffeine_enabled = False
self._activity_proc = None
self._activity_proc_lastrun = 0
super().__init__(False)
def configure(self, argument_parser):
argument_parser.add_argument('--caffeine-timeout',
@ -21,31 +18,17 @@ class CaffeineControl(AbstractControl):
type=int,
default=10)
def respond_to(self, command):
if command == 'caffeine':
self.caffeine_enabled = not self.caffeine_enabled
logger.info("Set caffeine enabled %r", self.caffeine_enabled)
return True
def __str__(self):
return action(self.create_pipe_command('caffeine'), 'C' if self.caffeine_enabled else 'c')
def periodic(self):
if self._activity_proc is not None:
if self._activity_proc.returncode is None:
self._activity_proc.poll()
else:
logger.debug("Reaped subprocess: %s", self._activity_proc)
self._activity_proc = None
if self.caffeine_enabled and self._activity_proc is None:
if self._activity_proc_lastrun + self.args.caffeine_timeout < time.time():
def bind_arguments(self, args):
super().bind_arguments(args)
self.periodic = process_reaper(backoff(self.args.caffeine_timeout)(self.__periodic))
def __periodic(self):
if self.state:
logger.debug("Poking screensaver")
self._activity_proc = subprocess.Popen(['xscreensaver-command', '-deactivate'], stdin=subprocess.DEVNULL, stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL)
self._activity_proc_lastrun = time.time()
return subprocess.Popen(['xscreensaver-command', '-deactivate'],
stdin=subprocess.DEVNULL, stdout=subprocess.DEVNULL,
stderr=subprocess.DEVNULL)
def dump_state(self):
return dict(caffeine_enabled=self.caffeine_enabled)
def load_state(self, state):
self.caffeine_enabled = state['caffeine_enabled']
def CaffeineControl():
return ToggleControl('c', CaffeineToggleAction())

@ -0,0 +1,61 @@
from .core import AbstractControl, WrappingControl, action
import abc
import functools
class ToggleAction(AbstractControl, metaclass=abc.ABCMeta):
def __init__(self, initial_state: bool = False):
super().__init__()
self.__state = initial_state
def dump_state(self):
return {'state': self.__state}
def load_state(self, state):
if 'state' in state:
self.state = state['state']
def toggle(self):
self.state = not self.state
@property
def state(self):
return self.__state
@state.setter
def state(self, state: bool):
if self.__state == state:
return
if state:
self.enable()
else:
self.disable()
self.__state = state
def enable(self):
pass
def disable(self):
pass
class ToggleControl(WrappingControl):
"""Implements a simple toggle button"""
def __init__(self, letter: str, toggle_action: ToggleAction):
super().__init__(toggle_action)
self.__letter = letter
def __get_control_command(self):
return '%s:%s:%s' % (self.__class__.__name__, self.__letter, self.child.__class__.__name__)
def respond_to(self, command):
if command == self.__get_control_command():
self.child.toggle()
return True
def __str__(self):
return action(
self.create_pipe_command(self.__get_control_command()),
self.__letter.upper() if self.child.state else self.__letter.lower()
)

@ -1,5 +1,9 @@
import sys
import os.path
from functools import wraps
import time
from .core import AbstractControl
@ -26,3 +30,36 @@ class ChildReaperControl(AbstractControl):
except:
pass
def backoff(backoff, default=None):
def decorator(fn):
last_called = 0
@wraps(fn)
def wrapper(*a, **kw):
nonlocal last_called
if last_called + backoff > time.time():
return default
last_called = time.time()
return fn(*a, **kw)
return wrapper
return decorator
def process_reaper(fn):
process = None
@wraps(fn)
def wrapper(*a, **kw):
nonlocal process
if process is not None:
process.poll()
if process.returncode is not None:
process = None
if process is None:
process = fn(*a, **kw)
return wrapper

Loading…
Cancel
Save