"""
Hotkeys management.
Requires "gi" package and "Gtk" & "Keybinder" modules.
"""
# pylint: disable=broad-except
import threading
from clay.settings import settings
from clay.eventhook import EventHook
from clay.notifications import notification_area
from clay.log import logger
IS_INIT = False
[docs]def report_error(exc):
"Print an error message to the debug screen"
logger.error("{0}: {1}".format(exc.__class__.__name__, exc))
try:
# pylint: disable=import-error
import gi
gi.require_version('Keybinder', '3.0') # noqa
gi.require_version('Gtk', '3.0') # noqa
from gi.repository import Keybinder, Gtk
# pylint: enable=import-error
except ImportError as error:
report_error(error)
ERROR_MESSAGE = "Couldn't import PyGObject"
except ValueError as error:
report_error(error)
ERROR_MESSAGE = "Couldn't find the Keybinder and/or Gtk modules"
except Exception as error:
report_error(error)
ERROR_MESSAGE = "There was unknown error: '{}'".format(error)
else:
IS_INIT = True
[docs]class _HotkeyManager(object):
"""
Manages configs.
Runs Gtk main loop in a thread.
"""
[docs] def __init__(self):
self._x_hotkeys = {}
self._hotkeys = self._parse_hotkeys()
self.config = None
self.play_pause = EventHook()
self.next = EventHook()
self.prev = EventHook()
if IS_INIT:
Keybinder.init()
self.initialize()
threading.Thread(target=Gtk.main).start()
else:
logger.debug("Not loading the global shortcuts.")
notification_area.notify(
ERROR_MESSAGE +
", this means the global shortcuts will not work.\n" +
"You can check the log for more details."
)
[docs] @staticmethod
def _to_gtk_modifier(key):
"""
Translates the modifies to the way that GTK likes them.
"""
key = key.strip()
if key == "meta":
key = "<alt>"
elif key in ("ctrl", "alt", "shift"):
key = "<" + key + ">"
else:
key = key
return key
[docs] def _parse_x_hotkeys(self):
"""
Reads out them configuration file and parses them into hotkeys readable by GTK.
"""
hotkey_default_config = settings.get_default_config_section('hotkeys', 'x_hotkeys')
mod_key = settings.get('mod_key', 'hotkeys')
hotkeys = {}
for action in hotkey_default_config:
key_seq = settings.get(action, 'hotkeys', 'x_hotkeys')
for key in key_seq.split(', '):
hotkey = key.split(' + ')
if hotkey[0].strip() == 'mod':
hotkey[0] = mod_key
hotkey = [self._to_gtk_modifier(key) for key in hotkey]
hotkeys[action] = ''.join(hotkey)
return hotkeys
[docs] def _parse_hotkeys(self):
"""
Reads out the configuration file and parse them into a hotkeys for urwid.
"""
hotkey_config = settings.get_default_config_section('hotkeys', 'clay_hotkeys')
mod_key = settings.get('mod_key', 'hotkeys')
hotkeys = {}
for hotkey_name, hotkey_dict in hotkey_config.items():
hotkeys[hotkey_name] = {}
for action in hotkey_dict.keys():
key_seq = settings.get(action, 'hotkeys', 'clay_hotkeys', hotkey_name)
for key in key_seq.split(', '):
hotkey = key.split(' + ')
if hotkey[0].strip() == 'mod':
hotkey[0] = mod_key
hotkeys[hotkey_name][' '.join(hotkey)] = action
return hotkeys
[docs] def keypress(self, name, caller, super_, size, key):
"""
Process the pressed key by looking it up in the configuration file
"""
method_name = self._hotkeys[name].get(key)
if method_name:
ret = getattr(caller, method_name)()
elif super_ is not None:
ret = super_.keypress(size, key)
else:
ret = key
return ret
[docs] def initialize(self):
"""
Unbind previous hotkeys, re-read config & bind new hotkeys.
"""
for operation, key in self._x_hotkeys.items():
Keybinder.unbind(key)
self._x_hotkeys = self._parse_x_hotkeys()
for operation, key in self._x_hotkeys.items():
Keybinder.bind(key, self.fire_hook, operation)
[docs] def fire_hook(self, key, operation):
"""
Fire hook by name.
"""
assert key
getattr(self, operation).fire()
hotkey_manager = _HotkeyManager() # pylint: disable=invalid-name