Unity 8
PhysicalKeysMapper.qml
1 /*
2  * Copyright (C) 2014-2015 Canonical, Ltd.
3  *
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.
7  *
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.
12  *
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/>.
15  */
16 
17 import QtQuick 2.4
18 import Powerd 0.1
19 import Utils 0.1
20 
21 /*!
22  \brief A mapper for the physical keys on the device
23 
24  A mapper to handle events triggered by pressing physical keys on a device.
25  Keys included are
26  * Volume Decrease
27  * Volume Increase
28  * Power
29 
30  This allows for handling the following events
31  * Power dialog
32  * Volume Decreases/Increases
33  * Screenshots
34 */
35 
36 Item {
37  id: root
38 
39  signal powerKeyLongPressed;
40  signal volumeDownTriggered;
41  signal volumeUpTriggered;
42  signal screenshotTriggered;
43 
44  readonly property bool altTabPressed: d.altTabPressed
45  readonly property bool superPressed: d.superPressed
46  readonly property bool superTabPressed: d.superTabPressed
47 
48  property int powerKeyLongPressTime: 2000
49 
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
54 
55  QtObject {
56  id: d
57 
58  property bool volumeDownKeyPressed: false
59  property bool volumeUpKeyPressed: false
60  property bool ignoreVolumeEvents: false
61 
62  property bool altPressed: false
63  property bool altTabPressed: false
64 
65  property bool superPressed: false
66  property bool superTabPressed: false
67 
68  property var powerButtonPressStart: 0
69 
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
74  }
75 
76  InputEventGenerator {
77  id: inputEventGenerator
78  }
79 
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);
85  }
86 
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();
93  }
94  } else {
95  d.powerButtonPressStart = currentEventTimestamp;
96  }
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();
105  }
106  d.ignoreVolumeEvents = true;
107  }
108  d.volumeDownKeyPressed = true;
109  }
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();
116  }
117  d.ignoreVolumeEvents = true;
118  }
119  d.volumeUpKeyPressed = true;
120  }
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)
124  d.altPressed = true;
125  event.accepted = true;
126  d.altPressInjected = false;
127  }
128 
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)
134  ) {
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;
140  }
141  if (d.superPressed && !d.superTabPressed) {
142  d.superTabPressed = true;
143  event.accepted = true;
144  }
145  }
146  }
147 
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;
169  }
170  d.altPressed = false;
171  } else if (event.key == Qt.Key_Tab) {
172  if (d.altTabPressed) {
173  event.accepted = true;
174  }
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;
180  }
181  }
182  }
183 }