2 * Copyright (C) 2014-2015 Canonical, Ltd.
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation; version 3.
8 * This program is distributed in the hope that it will be useful,
9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 * GNU General Public License for more details.
13 * You should have received a copy of the GNU General Public License
14 * along with this program. If not, see <http://www.gnu.org/licenses/>.
22 \brief A mapper for the physical keys on the device
24 A mapper to handle events triggered by pressing physical keys on a device.
30 This allows for handling the following events
32 * Volume Decreases/Increases
39 signal powerKeyLongPressed;
40 signal volumeDownTriggered;
41 signal volumeUpTriggered;
42 signal screenshotTriggered;
44 readonly property bool altTabPressed: d.altTabPressed
45 readonly property bool superPressed: d.superPressed
46 readonly property bool superTabPressed: d.superTabPressed
48 property int powerKeyLongPressTime: 2000
50 // For testing. If running windowed (e.g. tryShell), Alt+Tab is taken by the
51 // running desktop, set this to true to use Ctrl+Tab instead.
52 property bool controlInsteadOfAlt: false
53 property bool controlInsteadOfSuper: false
58 property bool volumeDownKeyPressed: false
59 property bool volumeUpKeyPressed: false
60 property bool ignoreVolumeEvents: false
62 property bool altPressed: false
63 property bool altTabPressed: false
65 property bool superPressed: false
66 property bool superTabPressed: false
68 property var powerButtonPressStart: 0
70 // We need to eat ALT presses until we know what they're for (Alt+Tab or going to the app?)
71 // Once we know if an ALT keypress is for the app, we need to re-inject the pressed event for it
72 // but we must only do that once.
73 property bool altPressInjected: false
77 id: inputEventGenerator
80 function onKeyPressed(event, currentEventTimestamp) {
81 if (d.altPressed && !d.altTabPressed && event.key !== Qt.Key_Tab && event.key !== Qt.Key_Alt && !d.altPressInjected) {
82 // ALT is pressed and another key that is not Tab has been received. Re-inject the alt pressed event
83 d.altPressInjected = true;
84 inputEventGenerator.generateKeyEvent(Qt.Key_Alt, true, Qt.NoModifier, currentEventTimestamp - 1, 56);
87 if (event.key == Qt.Key_PowerDown || event.key == Qt.Key_PowerOff) {
88 if (event.isAutoRepeat) {
89 if (d.powerButtonPressStart > 0
90 && currentEventTimestamp - d.powerButtonPressStart >= powerKeyLongPressTime) {
91 d.powerButtonPressStart = 0;
92 root.powerKeyLongPressed();
95 d.powerButtonPressStart = currentEventTimestamp;
97 } else if ((event.key == Qt.Key_MediaTogglePlayPause || event.key == Qt.Key_MediaPlay) && !event.isAutoRepeat) {
98 event.accepted = callManager.handleMediaKey(false);
99 } else if (event.key == Qt.Key_VolumeDown) {
100 if (event.isAutoRepeat && !d.ignoreVolumeEvents) root.volumeDownTriggered();
101 else if (!event.isAutoRepeat) {
102 if (d.volumeUpKeyPressed) {
103 if (Powerd.status === Powerd.On) {
104 root.screenshotTriggered();
106 d.ignoreVolumeEvents = true;
108 d.volumeDownKeyPressed = true;
110 } else if (event.key == Qt.Key_VolumeUp) {
111 if (event.isAutoRepeat && !d.ignoreVolumeEvents) root.volumeUpTriggered();
112 else if (!event.isAutoRepeat) {
113 if (d.volumeDownKeyPressed) {
114 if (Powerd.status === Powerd.On) {
115 root.screenshotTriggered();
117 d.ignoreVolumeEvents = true;
119 d.volumeUpKeyPressed = true;
121 } else if (event.key == Qt.Key_Alt || (root.controlInsteadOfAlt && event.key == Qt.Key_Control)) {
122 if (!d.altPressed || event.isAutoRepeat) {
123 // Only eat it if it's the first time we receive alt pressed (or if it's the autorepeat of the first press)
125 event.accepted = true;
126 d.altPressInjected = false;
129 // Adding MetaModifier here because that's what keyboards do. Pressing Super_L actually gives
130 // Super_L + MetaModifier. This helps to make sure we only invoke superPressed if no other
131 // Modifier is pressed too.
132 } else if (((event.key == Qt.Key_Super_L || event.key == Qt.Key_Super_R) && event.modifiers === Qt.MetaModifier)
133 || (root.controlInsteadOfSuper && event.key == Qt.Key_Control)
135 d.superPressed = true;
136 } else if (event.key == Qt.Key_Tab) {
137 if (d.altPressed && !d.altTabPressed) {
138 d.altTabPressed = true;
139 event.accepted = true;
141 if (d.superPressed && !d.superTabPressed) {
142 d.superTabPressed = true;
143 event.accepted = true;
148 function onKeyReleased(event, currentEventTimestamp) {
149 if (event.key == Qt.Key_PowerDown || event.key == Qt.Key_PowerOff) {
150 d.powerButtonPressStart = 0;
151 event.accepted = true;
152 } else if (event.key == Qt.Key_VolumeDown) {
153 if (!d.ignoreVolumeEvents) root.volumeDownTriggered();
154 d.volumeDownKeyPressed = false;
155 if (!d.volumeUpKeyPressed) d.ignoreVolumeEvents = false;
156 } else if (event.key == Qt.Key_VolumeUp) {
157 if (!d.ignoreVolumeEvents) root.volumeUpTriggered();
158 d.volumeUpKeyPressed = false;
159 if (!d.volumeDownKeyPressed) d.ignoreVolumeEvents = false;
160 } else if (event.key == Qt.Key_Alt || (root.controlInsteadOfAlt && event.key == Qt.Key_Control)) {
161 if (d.altTabPressed) {
162 d.altTabPressed = false;
163 event.accepted = true;
164 } else if (d.altPressed && !d.altPressInjected) {
165 // Alt was released but nothing else. Let's inject a pressed event and also forward the release.
166 d.altPressInjected = true;
167 inputEventGenerator.generateKeyEvent(Qt.Key_Alt, true, Qt.AltModifer, currentEventTimestamp, 56);
168 d.altPressInjected = false;
170 d.altPressed = false;
171 } else if (event.key == Qt.Key_Tab) {
172 if (d.altTabPressed) {
173 event.accepted = true;
175 } else if (event.key == Qt.Key_Super_L || event.key == Qt.Key_Super_R || (root.controlInsteadOfSuper && event.key == Qt.Key_Control)) {
176 d.superPressed = false;
177 if (d.superTabPressed) {
178 d.superTabPressed = false;
179 event.accepted = true;