Merge lp:~stolowski/unity-2d/numpad-modifier-support into lp:unity-2d

Proposed by Paweł Stołowski
Status: Merged
Approved by: Gerry Boland
Approved revision: 995
Merged at revision: 1001
Proposed branch: lp:~stolowski/unity-2d/numpad-modifier-support
Merge into: lp:unity-2d
Diff against target: 323 lines (+258/-18)
4 files modified
libunity-2d-private/src/hotkey.cpp (+35/-18)
libunity-2d-private/tests/CMakeLists.txt (+3/-0)
libunity-2d-private/tests/hotkeytest.cpp (+218/-0)
shell/app/shellmanager.cpp (+2/-0)
To merge this branch: bzr merge lp:~stolowski/unity-2d/numpad-modifier-support
Reviewer Review Type Date Requested Status
Gerry Boland (community) Approve
Albert Astals Cid Pending
unity-2d-team Pending
Review via email: mp+97931@code.launchpad.net

Commit message

[launcher] Added support for Super + keypad 0..9 hotkeys.
Implemented by adding special handling for Qt::KeypadModifier in Hotkey ctor.

Description of the change

[launcher] Added support for Super + keypad 0..9 hotkeys.
Implemented by adding special handling for Qt::KeypadModifier in Hotkey ctor.

To post a comment you must log in.
Revision history for this message
Gerry Boland (gerboland) wrote :

Works great, thank you Pawel!

review: Approve

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'libunity-2d-private/src/hotkey.cpp'
2--- libunity-2d-private/src/hotkey.cpp 2012-02-03 13:36:16 +0000
3+++ libunity-2d-private/src/hotkey.cpp 2012-03-16 17:21:19 +0000
4@@ -28,6 +28,10 @@
5 #include <X11/extensions/XKB.h>
6 #include <X11/Xproto.h>
7
8+// XK_MISCELLANY is needed for XK_KP_* keysymdef definitions
9+#define XK_MISCELLANY
10+#include <X11/keysymdef.h>
11+
12 #include <debug_p.h>
13
14 static int (*_x_old_errhandler)(Display *, XErrorEvent *);
15@@ -64,24 +68,37 @@
16 if (modifiers.testFlag(Qt::MetaModifier)) {
17 m_x11modifiers |= Mod4Mask;
18 }
19-
20- /* Translate the QT key to X11 keycode */
21-
22- /* QKeySequence can be used to translate a Qt::Key in a format that is
23- understood by XStringToKeysym if the sequence is composed only by the key */
24- QString keyString = QKeySequence(key).toString();
25- KeySym keysym = XStringToKeysym(keyString.toLatin1().data());
26- if (keysym == NoSymbol) {
27- /* XStringToKeysym doesn’t work well with exotic characters (such as
28- 'É'). Note that this fallback code path looks much simpler but doesn’t
29- work for special keys such as the function keys (e.g. F1), which is
30- why the translation with XStringToKeysym is attempted first. */
31- keysym = (ushort) key;
32- }
33- m_x11key = XKeysymToKeycode(QX11Info::display(), keysym);
34- if (m_x11key == 0) {
35- UQ_WARNING << "Could not get keycode for keysym" << keysym
36- << "(" << keyString << ")";
37+ if (modifiers.testFlag(Qt::KeypadModifier)) {
38+ /* Support 0..9 numpad keys only.
39+ If we ever need to support additional numpad keys, then this logic should be extended
40+ in the spirit of Qt's qkeymapper_x11.cpp. */
41+ if (key >= Qt::Key_0 && key <= Qt::Key_9) {
42+ /* Please note that we don't set Mod2Mask (NumLock) modifier. It appears that Mod2 is reported
43+ when it's actually held during numkey key press.
44+ */
45+ m_x11key = XKeysymToKeycode(QX11Info::display(), XK_KP_9 - (Qt::Key_9 - key));
46+ } else {
47+ UQ_WARNING << "Can't map numpad keys other than 0..9";
48+ }
49+ } else {
50+ /* Translate the QT key to X11 keycode */
51+
52+ /* QKeySequence can be used to translate a Qt::Key in a format that is
53+ understood by XStringToKeysym if the sequence is composed only by the key */
54+ QString keyString = QKeySequence(key).toString();
55+ KeySym keysym = XStringToKeysym(keyString.toLatin1().data());
56+ if (keysym == NoSymbol) {
57+ /* XStringToKeysym doesn’t work well with exotic characters (such as
58+ 'É'). Note that this fallback code path looks much simpler but doesn’t
59+ work for special keys such as the function keys (e.g. F1), which is
60+ why the translation with XStringToKeysym is attempted first. */
61+ keysym = (ushort) key;
62+ }
63+ m_x11key = XKeysymToKeycode(QX11Info::display(), keysym);
64+ if (m_x11key == 0) {
65+ UQ_WARNING << "Could not get keycode for keysym" << keysym
66+ << "(" << keyString << ")";
67+ }
68 }
69 }
70
71
72=== modified file 'libunity-2d-private/tests/CMakeLists.txt'
73--- libunity-2d-private/tests/CMakeLists.txt 2012-03-13 18:54:14 +0000
74+++ libunity-2d-private/tests/CMakeLists.txt 2012-03-16 17:21:19 +0000
75@@ -39,9 +39,12 @@
76 qsortfilterproxymodeltest
77 focuspathtest
78 pointerbarriertest
79+ hotkeytest
80 )
81
82 target_link_libraries(pointerbarriertest ${X11_XTest_LIB})
83+
84+target_link_libraries(hotkeytest ${X11_XTest_LIB})
85
86 # unity2dtrtest - FIXME
87 #add_test(NAME unity2dtrtest_check
88
89=== added file 'libunity-2d-private/tests/hotkeytest.cpp'
90--- libunity-2d-private/tests/hotkeytest.cpp 1970-01-01 00:00:00 +0000
91+++ libunity-2d-private/tests/hotkeytest.cpp 2012-03-16 17:21:19 +0000
92@@ -0,0 +1,218 @@
93+/*
94+ * This file is part of unity-2d
95+ *
96+ * Copyright 2012 Canonical Ltd.
97+ *
98+ * Authors:
99+ * - Pawel Stolowski <pawel.stolowski@canonical.com>
100+ *
101+ * This program is free software; you can redistribute it and/or modify
102+ * it under the terms of the GNU General Public License as published by
103+ * the Free Software Foundation; version 3.
104+ *
105+ * This program is distributed in the hope that it will be useful,
106+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
107+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
108+ * GNU General Public License for more details.
109+ *
110+ * You should have received a copy of the GNU General Public License
111+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
112+ */
113+
114+#include <unitytestmacro.h>
115+#include <hotkey.h>
116+#include <hotkeymonitor.h>
117+
118+#include <QApplication>
119+#include <QX11Info>
120+#include <QtTestGui>
121+
122+// XK_MISCELLANY and XK_LATIN1 are needed for XK_* keysymdef definitions
123+#define XK_MISCELLANY
124+#define XK_LATIN1
125+#include <X11/keysymdef.h>
126+#include <X11/extensions/XTest.h>
127+
128+/* Helper class that connects to Hotkey pressed() signal and
129+ records key/modifiers and total number of key presses */
130+class HotKeyPressReceiver: public QObject
131+{
132+ Q_OBJECT
133+public:
134+ HotKeyPressReceiver(): m_count(0) {
135+ }
136+
137+ int count() const { return m_count; }
138+ void reset() { m_count = 0; }
139+ Qt::Key key() const { return m_key; }
140+ Qt::KeyboardModifiers modifiers() const { return m_modifiers; }
141+
142+public Q_SLOTS:
143+ void onKeyPressed() {
144+ Hotkey* hotkey = qobject_cast<Hotkey*>(sender());
145+
146+ m_key = hotkey->key();
147+ m_modifiers = hotkey->modifiers();
148+ ++m_count;
149+ }
150+
151+ private:
152+ Qt::Key m_key;
153+ Qt::KeyboardModifiers m_modifiers;
154+ int m_count;
155+};
156+
157+class HotkeyTest: public QObject
158+{
159+ static void simulateKey(KeySym keysym, KeySym modsym1, KeySym modsym2 = 0) {
160+ Display *disp = QX11Info::display();
161+
162+ KeyCode modcode1, modcode2;
163+ KeyCode keycode = XKeysymToKeycode(disp, keysym);
164+
165+ XTestGrabControl (disp, True);
166+
167+ /* Generate modkey press */
168+ if (modsym1 != 0) {
169+ modcode1 = XKeysymToKeycode(disp, modsym1);
170+ XTestFakeKeyEvent(disp, modcode1, True, 0);
171+ }
172+
173+ if (modsym2 != 0) {
174+ modcode2 = XKeysymToKeycode(disp, modsym2);
175+ XTestFakeKeyEvent(disp, modcode2, True, 1);
176+ }
177+
178+ /* Generate regular key press and release */
179+ XTestFakeKeyEvent(disp, keycode, True, 10);
180+ XTestFakeKeyEvent(disp, keycode, False, 20);
181+
182+ /* Generate modkey release */
183+ if (modsym1 != 0) {
184+ XTestFakeKeyEvent(disp, modcode1, False, 40);
185+ }
186+
187+ if (modsym2 != 0) {
188+ XTestFakeKeyEvent(disp, modcode2, False, 41);
189+ }
190+
191+ XSync(disp, False);
192+ XTestGrabControl(disp, False);
193+
194+ /* Process Qt events. This is crucial since simulated keypress won't automagically trigger Qt signal */
195+ QApplication::processEvents();
196+ }
197+
198+ Q_OBJECT
199+private Q_SLOTS:
200+
201+ void initTestCase()
202+ {
203+ for (Qt::Key key = Qt::Key_0; key <= Qt::Key_9; key = (Qt::Key) (key + 1)) {
204+ Hotkey* hotkey = HotkeyMonitor::instance().getHotkeyFor(key, Qt::MetaModifier);
205+ connect(hotkey, SIGNAL(pressed()), &m_rcv, SLOT(onKeyPressed()));
206+ hotkey = HotkeyMonitor::instance().getHotkeyFor(key, Qt::MetaModifier | Qt::ShiftModifier);
207+ connect(hotkey, SIGNAL(pressed()), &m_shiftrcv, SLOT(onKeyPressed()));
208+ hotkey = HotkeyMonitor::instance().getHotkeyFor(key, Qt::MetaModifier | Qt::KeypadModifier);
209+ connect(hotkey, SIGNAL(pressed()), &m_keypadrcv, SLOT(onKeyPressed()));
210+ }
211+ }
212+
213+ void init()
214+ {
215+ m_rcv.reset();
216+ m_shiftrcv.reset();
217+ m_keypadrcv.reset();
218+ }
219+
220+ /* Test super + number hotkeys */
221+ void testNumericKeypress()
222+ {
223+ QCOMPARE(m_rcv.count(), 0);
224+
225+ simulateKey(XK_1, XK_Super_L);
226+ QCOMPARE(m_rcv.count(), 1);
227+ QCOMPARE(m_keypadrcv.count(), 0);
228+ QCOMPARE(m_shiftrcv.count(), 0);
229+ QCOMPARE(m_rcv.key(), Qt::Key_1);
230+ QCOMPARE(m_rcv.modifiers(), Qt::MetaModifier);
231+
232+ simulateKey(XK_9, XK_Super_L);
233+ QCOMPARE(m_rcv.count(), 2);
234+ QCOMPARE(m_keypadrcv.count(), 0);
235+ QCOMPARE(m_shiftrcv.count(), 0);
236+ QCOMPARE(m_rcv.key(), Qt::Key_9);
237+ QCOMPARE(m_rcv.modifiers(), Qt::MetaModifier);
238+
239+ simulateKey(XK_0, XK_Super_L);
240+ QCOMPARE(m_rcv.count(), 3);
241+ QCOMPARE(m_keypadrcv.count(), 0);
242+ QCOMPARE(m_shiftrcv.count(), 0);
243+ QCOMPARE(m_rcv.key(), Qt::Key_0);
244+ QCOMPARE(m_rcv.modifiers(), Qt::MetaModifier);
245+ }
246+
247+ /* Test for super + shift + number hotkeys */
248+ void testShiftNumericKeypress()
249+ {
250+ QCOMPARE(m_shiftrcv.count(), 0);
251+
252+ simulateKey(XK_1, XK_Super_L, XK_Shift_L);
253+ QCOMPARE(m_shiftrcv.count(), 1);
254+ QCOMPARE(m_rcv.count(), 0);
255+ QCOMPARE(m_keypadrcv.count(), 0);
256+ QCOMPARE(m_shiftrcv.key(), Qt::Key_1);
257+ QCOMPARE(m_shiftrcv.modifiers(), Qt::MetaModifier | Qt::ShiftModifier);
258+
259+ simulateKey(XK_9, XK_Super_L, XK_Shift_L);
260+ QCOMPARE(m_shiftrcv.count(), 2);
261+ QCOMPARE(m_rcv.count(), 0);
262+ QCOMPARE(m_keypadrcv.count(), 0);
263+ QCOMPARE(m_shiftrcv.key(), Qt::Key_9);
264+ QCOMPARE(m_shiftrcv.modifiers(), Qt::MetaModifier | Qt::ShiftModifier);
265+
266+ simulateKey(XK_0, XK_Super_L, XK_Shift_L);
267+ QCOMPARE(m_shiftrcv.count(), 3);
268+ QCOMPARE(m_rcv.count(), 0);
269+ QCOMPARE(m_keypadrcv.count(), 0);
270+ QCOMPARE(m_shiftrcv.key(), Qt::Key_0);
271+ QCOMPARE(m_shiftrcv.modifiers(), Qt::MetaModifier | Qt::ShiftModifier);
272+ }
273+
274+ /* Test for super + numeric keyboard hotkeys */
275+ void testNumpadNumericKeypress()
276+ {
277+ QCOMPARE(m_keypadrcv.count(), 0);
278+
279+ simulateKey(XK_KP_1, XK_Super_L);
280+ QCOMPARE(m_keypadrcv.count(), 1);
281+ QCOMPARE(m_rcv.count(), 0);
282+ QCOMPARE(m_shiftrcv.count(), 0);
283+ QCOMPARE(m_keypadrcv.key(), Qt::Key_1);
284+ QCOMPARE(m_keypadrcv.modifiers(), Qt::MetaModifier | Qt::KeypadModifier);
285+
286+ simulateKey(XK_KP_9, XK_Super_L);
287+ QCOMPARE(m_keypadrcv.count(), 2);
288+ QCOMPARE(m_rcv.count(), 0);
289+ QCOMPARE(m_shiftrcv.count(), 0);
290+ QCOMPARE(m_keypadrcv.key(), Qt::Key_9);
291+ QCOMPARE(m_keypadrcv.modifiers(), Qt::MetaModifier | Qt::KeypadModifier);
292+
293+ simulateKey(XK_KP_0, XK_Super_L);
294+ QCOMPARE(m_keypadrcv.count(), 3);
295+ QCOMPARE(m_rcv.count(), 0);
296+ QCOMPARE(m_shiftrcv.count(), 0);
297+ QCOMPARE(m_keypadrcv.key(), Qt::Key_0);
298+ QCOMPARE(m_keypadrcv.modifiers(), Qt::MetaModifier | Qt::KeypadModifier);
299+ }
300+
301+private:
302+ HotKeyPressReceiver m_rcv; // monitor for meta+number hotkeys
303+ HotKeyPressReceiver m_shiftrcv; //monitor for meta+shift+number hotkeys
304+ HotKeyPressReceiver m_keypadrcv; //monitor for meta+numpad hotkeys
305+};
306+
307+UAPP_TEST_MAIN(HotkeyTest)
308+
309+#include "hotkeytest.moc"
310+
311
312=== modified file 'shell/app/shellmanager.cpp'
313--- shell/app/shellmanager.cpp 2012-03-14 08:52:49 +0000
314+++ shell/app/shellmanager.cpp 2012-03-16 17:21:19 +0000
315@@ -306,6 +306,8 @@
316 connect(hotkey, SIGNAL(pressed()), SLOT(onNumericHotkeyPressed()));
317 hotkey = HotkeyMonitor::instance().getHotkeyFor(key, Qt::MetaModifier | Qt::ShiftModifier);
318 connect(hotkey, SIGNAL(pressed()), SLOT(onNumericHotkeyPressed()));
319+ hotkey = HotkeyMonitor::instance().getHotkeyFor(key, Qt::MetaModifier | Qt::KeypadModifier);
320+ connect(hotkey, SIGNAL(pressed()), SLOT(onNumericHotkeyPressed()));
321 }
322
323 // FIXME: we need to use a queued connection here otherwise QConf will deadlock for some reason

Subscribers

People subscribed via source and target branches