1#!/usr/local/bin/python3.8
2
3import gi
4gi.require_version('CDesktopEnums', '3.0')
5from gi.repository import Gtk, GObject, Gdk, Gio, CinnamonDesktop
6from gi.repository.CDesktopEnums import MediaKeyType as MK
7
8import status
9import singletons
10from util import settings
11
12ALLOWED_ACTIONS = [MK.MUTE,
13                   MK.VOLUME_UP,
14                   MK.VOLUME_UP_QUIET,
15                   MK.VOLUME_DOWN,
16                   MK.VOLUME_DOWN_QUIET,
17                   MK.MIC_MUTE,
18                   MK.EJECT,
19                   MK.PLAY,
20                   MK.PAUSE,
21                   MK.STOP,
22                   MK.PREVIOUS,
23                   MK.NEXT,
24                   MK.REWIND,
25                   MK.FORWARD,
26                   MK.REPEAT,
27                   MK.RANDOM,
28                   MK.TOUCHPAD,
29                   MK.TOUCHPAD_ON,
30                   MK.TOUCHPAD_OFF,
31                   MK.SHUTDOWN,
32                   MK.SUSPEND,
33                   MK.HIBERNATE,
34                   MK.SCREEN_BRIGHTNESS_UP,
35                   MK.SCREEN_BRIGHTNESS_DOWN,
36                   MK.ROTATE_VIDEO,
37                   MK.KEYBOARD_BRIGHTNESS_UP,
38                   MK.KEYBOARD_BRIGHTNESS_DOWN,
39                   MK.KEYBOARD_BRIGHTNESS_TOGGLE]
40
41class ShortcutAction(GObject.GObject):
42    """
43    A class representing a group of keybindings for a specific
44    media key action.
45    """
46    def __init__(self, action, bindings):
47        super(ShortcutAction, self).__init__()
48
49        self.action = action
50        self.bindings = bindings
51
52        self.parsed = []
53
54        for binding in self.bindings:
55            key, codes, mods = Gtk.accelerator_parse_with_keycode(binding)
56
57            self.parsed.append((key, codes, mods))
58
59    def activate(self, key, keycode, mods):
60        for binding in self.parsed:
61            if (key == binding[0] or keycode in binding[1]) and mods == binding[2]:
62                return self.action
63
64        return -1
65
66class KeyBindings(GObject.GObject):
67    """
68    Receives keystrokes from the EventHandler - it checks for media key
69    activation, as well as special keys like Escape or Tab, and performs the
70    appropriate actions.
71    """
72    def __init__(self, manager):
73        super(KeyBindings, self).__init__()
74
75        self.manager = manager
76
77        self.client = singletons.KeybindingHandlerClient
78
79        self.keymap = Gdk.Keymap.get_default()
80
81        self.media_key_settings = Gio.Settings(schema_id="org.cinnamon.desktop.keybindings.media-keys")
82        self.shortcut_actions = []
83
84        self.load_bindings()
85
86    def load_bindings(self):
87        self.shortcut_actions = []
88
89        for action_id in ALLOWED_ACTIONS:
90            bindings = self.media_key_settings.get_strv(CinnamonDesktop.desktop_get_media_key_string(action_id))
91
92            action = ShortcutAction(action_id, bindings)
93
94            self.shortcut_actions.append(action)
95
96    def maybe_handle_event(self, event):
97        if event.type != Gdk.EventType.KEY_PRESS:
98            return False
99
100        filtered_state = Gdk.ModifierType(event.state & ~(Gdk.ModifierType.MOD2_MASK | Gdk.ModifierType.LOCK_MASK))
101
102        if filtered_state == 0 and event.keyval == Gdk.KEY_Escape:
103            if status.Awake:
104                self.manager.cancel_unlock_widget()
105                return True
106
107        if event.keyval == Gdk.KEY_Menu:
108            return True
109
110        if status.Awake:
111            if (event.keyval in (Gdk.KEY_Tab, Gdk.KEY_ISO_Left_Tab)):
112                if event.keyval == Gdk.KEY_ISO_Left_Tab:
113                    self.manager.propagate_tab_event(True)
114                else:
115                    self.manager.propagate_tab_event(False)
116                return True
117            elif event.keyval == Gdk.KEY_space and isinstance(self.manager.get_focused_widget(), Gtk.Button):
118                self.manager.propagate_activation()
119                return True
120
121        if settings.get_allow_shortcuts():
122            for entry in self.shortcut_actions:
123                res = entry.activate(event.keyval, event.hardware_keycode, filtered_state)
124
125                if res == -1:
126                    continue
127                else:
128                    self.client.handle_keybinding(res)
129                    return True
130
131        return False
132