Mir

Merge lp:~andreas-pokorny/mir/fix-1588237 into lp:mir

Proposed by Andreas Pokorny
Status: Merged
Approved by: Andreas Pokorny
Approved revision: no longer in the source branch.
Merged at revision: 4016
Proposed branch: lp:~andreas-pokorny/mir/fix-1588237
Merge into: lp:mir
Prerequisite: lp:~andreas-pokorny/mir/send-input-device-state-event
Diff against target: 498 lines (+269/-14)
7 files modified
src/client/input/android/android_input_receiver.cpp (+22/-6)
src/client/input/xkb_mapper.cpp (+59/-4)
src/include/common/mir/input/key_mapper.h (+1/-0)
src/include/common/mir/input/xkb_mapper.h (+7/-0)
src/server/input/seat_input_device_tracker.cpp (+39/-2)
tests/acceptance-tests/test_client_input.cpp (+140/-2)
tests/include/mir/test/doubles/mock_key_mapper.h (+1/-0)
To merge this branch: bzr merge lp:~andreas-pokorny/mir/fix-1588237
Reviewer Review Type Date Requested Status
Alexandros Frantzis (community) Approve
Mir CI Bot continuous-integration Approve
Review via email: mp+299353@code.launchpad.net

Commit message

Fix behavior of numlock and related modifier keys (LP: #1588237)

Corrects the tracking of modifiers that can be toggled. The input device state event now transfers the state of the toggled modifiers via double entries in the scan code array. Finally this change adds some missing wiring to forward the input device state event to the client side XkbMapper.

Description of the change

Three small changes that add all missing pieces to fix client side key mapping.
First of all the modifier tracking for toggling modifiers had to be fixed. Then those modifiers need special treatment to transfer them from the server to the upcoming focused client. The client needs that status to feed that back into xkb. This is done via scan codes - with that we also handle the case that the client keymap configuration remapped that toggling modifier.

When and where is that relevant?
If we do all scan code to key mapping in the server this change is not really necessary. The server keeps the key state separate from the currently focused window. So the modifier state stays correct when input focus is switched between surfaces. Because of Xmir/X11 we still have to support client side key mapping. So clients have to be informed about modifier state beyond the list of depressed keys.

Note on testing:
In mir/miral the default is to do server side key mapping only.
But his can be easily changed:
Add
  xkb_mapper->set_keymap_for_all_devices(mir::input::Keymap{});
to the constructor of android_input_receiver.cpp

Launch "gedit -s" twice, and change num lock or caps lock in one gedit then type in the other.

To post a comment you must log in.
Revision history for this message
Mir CI Bot (mir-ci-bot) wrote :

FAILED: Continuous integration, rev:3534
https://mir-jenkins.ubuntu.com/job/mir-ci/1236/
Executed test runs:
    FAILURE: https://mir-jenkins.ubuntu.com/job/build-mir/1438/console
    SUCCESS: https://mir-jenkins.ubuntu.com/job/build-0-fetch/1490
    SUCCESS: https://mir-jenkins.ubuntu.com/job/build-1-sourcepkg/release=vivid+overlay/1481
    SUCCESS: https://mir-jenkins.ubuntu.com/job/build-1-sourcepkg/release=xenial/1481
    SUCCESS: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-mir/arch=amd64,compiler=clang,platform=mesa,release=vivid+overlay/1452
        deb: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-mir/arch=amd64,compiler=clang,platform=mesa,release=vivid+overlay/1452/artifact/output/*zip*/output.zip
    FAILURE: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-mir/arch=amd64,compiler=gcc,platform=mesa,release=xenial/1452/console
    SUCCESS: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-mir/arch=cross-armhf,compiler=gcc,platform=android,release=vivid+overlay/1452
        deb: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-mir/arch=cross-armhf,compiler=gcc,platform=android,release=vivid+overlay/1452/artifact/output/*zip*/output.zip
    SUCCESS: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-mir/arch=i386,compiler=gcc,platform=android,release=vivid+overlay/1452
        deb: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-mir/arch=i386,compiler=gcc,platform=android,release=vivid+overlay/1452/artifact/output/*zip*/output.zip
    FAILURE: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-mir/arch=i386,compiler=gcc,platform=mesa,release=xenial/1452/console

Click here to trigger a rebuild:
https://mir-jenkins.ubuntu.com/job/mir-ci/1236/rebuild

review: Needs Fixing (continuous-integration)
Revision history for this message
Mir CI Bot (mir-ci-bot) wrote :

FAILED: Continuous integration, rev:3535
https://mir-jenkins.ubuntu.com/job/mir-ci/1240/
Executed test runs:
    FAILURE: https://mir-jenkins.ubuntu.com/job/build-mir/1442/console
    SUCCESS: https://mir-jenkins.ubuntu.com/job/build-0-fetch/1494
    SUCCESS: https://mir-jenkins.ubuntu.com/job/build-1-sourcepkg/release=vivid+overlay/1485
    SUCCESS: https://mir-jenkins.ubuntu.com/job/build-1-sourcepkg/release=xenial/1485
    SUCCESS: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-mir/arch=amd64,compiler=clang,platform=mesa,release=vivid+overlay/1456
        deb: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-mir/arch=amd64,compiler=clang,platform=mesa,release=vivid+overlay/1456/artifact/output/*zip*/output.zip
    SUCCESS: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-mir/arch=amd64,compiler=gcc,platform=mesa,release=xenial/1456
        deb: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-mir/arch=amd64,compiler=gcc,platform=mesa,release=xenial/1456/artifact/output/*zip*/output.zip
    SUCCESS: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-mir/arch=cross-armhf,compiler=gcc,platform=android,release=vivid+overlay/1456
        deb: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-mir/arch=cross-armhf,compiler=gcc,platform=android,release=vivid+overlay/1456/artifact/output/*zip*/output.zip
    SUCCESS: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-mir/arch=i386,compiler=gcc,platform=android,release=vivid+overlay/1456
        deb: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-mir/arch=i386,compiler=gcc,platform=android,release=vivid+overlay/1456/artifact/output/*zip*/output.zip
    FAILURE: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-mir/arch=i386,compiler=gcc,platform=mesa,release=xenial/1456/console

Click here to trigger a rebuild:
https://mir-jenkins.ubuntu.com/job/mir-ci/1240/rebuild

review: Needs Fixing (continuous-integration)
Revision history for this message
Alexandros Frantzis (afrantzis) wrote :

[ FAILED ] TestClientInput.reestablishes_num_lock_state_in_client_with_surface_keymap (1450 ms)

review: Needs Fixing
Revision history for this message
Andreas Pokorny (andreas-pokorny) wrote :

still working on gtk and now also systemd to make a fix.. so putting that down for a week or so

Revision history for this message
Mir CI Bot (mir-ci-bot) wrote :

FAILED: Continuous integration, rev:3537
https://mir-jenkins.ubuntu.com/job/mir-ci/2877/
Executed test runs:
    FAILURE: https://mir-jenkins.ubuntu.com/job/build-mir/3793/console
    SUCCESS: https://mir-jenkins.ubuntu.com/job/build-0-fetch/3872
    SUCCESS: https://mir-jenkins.ubuntu.com/job/build-1-sourcepkg/release=vivid+overlay/3862
    SUCCESS: https://mir-jenkins.ubuntu.com/job/build-1-sourcepkg/release=xenial+overlay/3862
    SUCCESS: https://mir-jenkins.ubuntu.com/job/build-1-sourcepkg/release=zesty/3862
    SUCCESS: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-mir/arch=amd64,compiler=clang,platform=mesa,release=zesty/3820
        deb: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-mir/arch=amd64,compiler=clang,platform=mesa,release=zesty/3820/artifact/output/*zip*/output.zip
    SUCCESS: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-mir/arch=amd64,compiler=gcc,platform=mesa,release=xenial+overlay/3820
        deb: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-mir/arch=amd64,compiler=gcc,platform=mesa,release=xenial+overlay/3820/artifact/output/*zip*/output.zip
    FAILURE: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-mir/arch=amd64,compiler=gcc,platform=mesa,release=zesty/3820/console
    SUCCESS: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-mir/arch=cross-armhf,compiler=gcc,platform=android,release=vivid+overlay/3820
        deb: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-mir/arch=cross-armhf,compiler=gcc,platform=android,release=vivid+overlay/3820/artifact/output/*zip*/output.zip
    SUCCESS: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-mir/arch=i386,compiler=gcc,platform=android,release=vivid+overlay/3820
        deb: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-mir/arch=i386,compiler=gcc,platform=android,release=vivid+overlay/3820/artifact/output/*zip*/output.zip
    FAILURE: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-mir/arch=i386,compiler=gcc,platform=mesa,release=xenial+overlay/3820/console

Click here to trigger a rebuild:
https://mir-jenkins.ubuntu.com/job/mir-ci/2877/rebuild

review: Needs Fixing (continuous-integration)
Revision history for this message
Mir CI Bot (mir-ci-bot) wrote :

FAILED: Continuous integration, rev:3538
https://mir-jenkins.ubuntu.com/job/mir-ci/2878/
Executed test runs:
    FAILURE: https://mir-jenkins.ubuntu.com/job/build-mir/3795/console
    SUCCESS: https://mir-jenkins.ubuntu.com/job/build-0-fetch/3874
    SUCCESS: https://mir-jenkins.ubuntu.com/job/build-1-sourcepkg/release=vivid+overlay/3864
    SUCCESS: https://mir-jenkins.ubuntu.com/job/build-1-sourcepkg/release=xenial+overlay/3864
    SUCCESS: https://mir-jenkins.ubuntu.com/job/build-1-sourcepkg/release=zesty/3864
    SUCCESS: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-mir/arch=amd64,compiler=clang,platform=mesa,release=zesty/3822
        deb: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-mir/arch=amd64,compiler=clang,platform=mesa,release=zesty/3822/artifact/output/*zip*/output.zip
    FAILURE: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-mir/arch=amd64,compiler=gcc,platform=mesa,release=xenial+overlay/3822/console
    SUCCESS: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-mir/arch=amd64,compiler=gcc,platform=mesa,release=zesty/3822
        deb: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-mir/arch=amd64,compiler=gcc,platform=mesa,release=zesty/3822/artifact/output/*zip*/output.zip
    SUCCESS: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-mir/arch=cross-armhf,compiler=gcc,platform=android,release=vivid+overlay/3822
        deb: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-mir/arch=cross-armhf,compiler=gcc,platform=android,release=vivid+overlay/3822/artifact/output/*zip*/output.zip
    SUCCESS: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-mir/arch=i386,compiler=gcc,platform=android,release=vivid+overlay/3822
        deb: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-mir/arch=i386,compiler=gcc,platform=android,release=vivid+overlay/3822/artifact/output/*zip*/output.zip
    SUCCESS: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-mir/arch=i386,compiler=gcc,platform=mesa,release=xenial+overlay/3822
        deb: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-mir/arch=i386,compiler=gcc,platform=mesa,release=xenial+overlay/3822/artifact/output/*zip*/output.zip

Click here to trigger a rebuild:
https://mir-jenkins.ubuntu.com/job/mir-ci/2878/rebuild

review: Needs Fixing (continuous-integration)
Revision history for this message
Andreas Pokorny (andreas-pokorny) wrote :

Ok still the same ordering problem.. the device state event occurs to soon and lacks the pressed keys..

Revision history for this message
Mir CI Bot (mir-ci-bot) wrote :

FAILED: Continuous integration, rev:3540
https://mir-jenkins.ubuntu.com/job/mir-ci/2898/
Executed test runs:
    FAILURE: https://mir-jenkins.ubuntu.com/job/build-mir/3830/console
    SUCCESS: https://mir-jenkins.ubuntu.com/job/build-0-fetch/3909
    SUCCESS: https://mir-jenkins.ubuntu.com/job/build-1-sourcepkg/release=vivid+overlay/3899
    SUCCESS: https://mir-jenkins.ubuntu.com/job/build-1-sourcepkg/release=xenial+overlay/3899
    SUCCESS: https://mir-jenkins.ubuntu.com/job/build-1-sourcepkg/release=zesty/3899
    SUCCESS: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-mir/arch=amd64,compiler=clang,platform=mesa,release=zesty/3857
        deb: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-mir/arch=amd64,compiler=clang,platform=mesa,release=zesty/3857/artifact/output/*zip*/output.zip
    SUCCESS: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-mir/arch=amd64,compiler=gcc,platform=mesa,release=xenial+overlay/3857
        deb: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-mir/arch=amd64,compiler=gcc,platform=mesa,release=xenial+overlay/3857/artifact/output/*zip*/output.zip
    FAILURE: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-mir/arch=amd64,compiler=gcc,platform=mesa,release=zesty/3857/console
    SUCCESS: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-mir/arch=cross-armhf,compiler=gcc,platform=android,release=vivid+overlay/3857
        deb: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-mir/arch=cross-armhf,compiler=gcc,platform=android,release=vivid+overlay/3857/artifact/output/*zip*/output.zip
    SUCCESS: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-mir/arch=i386,compiler=gcc,platform=android,release=vivid+overlay/3857
        deb: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-mir/arch=i386,compiler=gcc,platform=android,release=vivid+overlay/3857/artifact/output/*zip*/output.zip
    SUCCESS: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-mir/arch=i386,compiler=gcc,platform=mesa,release=xenial+overlay/3857
        deb: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-mir/arch=i386,compiler=gcc,platform=mesa,release=xenial+overlay/3857/artifact/output/*zip*/output.zip

Click here to trigger a rebuild:
https://mir-jenkins.ubuntu.com/job/mir-ci/2898/rebuild

review: Needs Fixing (continuous-integration)
Revision history for this message
Mir CI Bot (mir-ci-bot) wrote :

FAILED: Continuous integration, rev:3541
https://mir-jenkins.ubuntu.com/job/mir-ci/2899/
Executed test runs:
    FAILURE: https://mir-jenkins.ubuntu.com/job/build-mir/3831/console
    SUCCESS: https://mir-jenkins.ubuntu.com/job/build-0-fetch/3910
    SUCCESS: https://mir-jenkins.ubuntu.com/job/build-1-sourcepkg/release=vivid+overlay/3900
    SUCCESS: https://mir-jenkins.ubuntu.com/job/build-1-sourcepkg/release=xenial+overlay/3900
    SUCCESS: https://mir-jenkins.ubuntu.com/job/build-1-sourcepkg/release=zesty/3900
    SUCCESS: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-mir/arch=amd64,compiler=clang,platform=mesa,release=zesty/3858
        deb: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-mir/arch=amd64,compiler=clang,platform=mesa,release=zesty/3858/artifact/output/*zip*/output.zip
    SUCCESS: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-mir/arch=amd64,compiler=gcc,platform=mesa,release=xenial+overlay/3858
        deb: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-mir/arch=amd64,compiler=gcc,platform=mesa,release=xenial+overlay/3858/artifact/output/*zip*/output.zip
    SUCCESS: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-mir/arch=amd64,compiler=gcc,platform=mesa,release=zesty/3858
        deb: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-mir/arch=amd64,compiler=gcc,platform=mesa,release=zesty/3858/artifact/output/*zip*/output.zip
    SUCCESS: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-mir/arch=cross-armhf,compiler=gcc,platform=android,release=vivid+overlay/3858
        deb: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-mir/arch=cross-armhf,compiler=gcc,platform=android,release=vivid+overlay/3858/artifact/output/*zip*/output.zip
    SUCCESS: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-mir/arch=i386,compiler=gcc,platform=android,release=vivid+overlay/3858
        deb: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-mir/arch=i386,compiler=gcc,platform=android,release=vivid+overlay/3858/artifact/output/*zip*/output.zip
    FAILURE: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-mir/arch=i386,compiler=gcc,platform=mesa,release=xenial+overlay/3858/console

Click here to trigger a rebuild:
https://mir-jenkins.ubuntu.com/job/mir-ci/2899/rebuild

review: Needs Fixing (continuous-integration)
Revision history for this message
Andreas Pokorny (andreas-pokorny) wrote :

NestedInput.* and in the last one there were two ClientLatency.* failures.

Revision history for this message
Mir CI Bot (mir-ci-bot) wrote :

PASSED: Continuous integration, rev:3542
https://mir-jenkins.ubuntu.com/job/mir-ci/2945/
Executed test runs:
    SUCCESS: https://mir-jenkins.ubuntu.com/job/build-mir/3902
    SUCCESS: https://mir-jenkins.ubuntu.com/job/build-0-fetch/3981
    SUCCESS: https://mir-jenkins.ubuntu.com/job/build-1-sourcepkg/release=vivid+overlay/3971
    SUCCESS: https://mir-jenkins.ubuntu.com/job/build-1-sourcepkg/release=xenial+overlay/3971
    SUCCESS: https://mir-jenkins.ubuntu.com/job/build-1-sourcepkg/release=zesty/3971
    SUCCESS: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-mir/arch=amd64,compiler=clang,platform=mesa,release=zesty/3929
        deb: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-mir/arch=amd64,compiler=clang,platform=mesa,release=zesty/3929/artifact/output/*zip*/output.zip
    SUCCESS: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-mir/arch=amd64,compiler=gcc,platform=mesa,release=xenial+overlay/3929
        deb: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-mir/arch=amd64,compiler=gcc,platform=mesa,release=xenial+overlay/3929/artifact/output/*zip*/output.zip
    SUCCESS: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-mir/arch=amd64,compiler=gcc,platform=mesa,release=zesty/3929
        deb: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-mir/arch=amd64,compiler=gcc,platform=mesa,release=zesty/3929/artifact/output/*zip*/output.zip
    SUCCESS: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-mir/arch=cross-armhf,compiler=gcc,platform=android,release=vivid+overlay/3929
        deb: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-mir/arch=cross-armhf,compiler=gcc,platform=android,release=vivid+overlay/3929/artifact/output/*zip*/output.zip
    SUCCESS: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-mir/arch=i386,compiler=gcc,platform=android,release=vivid+overlay/3929
        deb: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-mir/arch=i386,compiler=gcc,platform=android,release=vivid+overlay/3929/artifact/output/*zip*/output.zip
    SUCCESS: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-mir/arch=i386,compiler=gcc,platform=mesa,release=xenial+overlay/3929
        deb: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-mir/arch=i386,compiler=gcc,platform=mesa,release=xenial+overlay/3929/artifact/output/*zip*/output.zip

Click here to trigger a rebuild:
https://mir-jenkins.ubuntu.com/job/mir-ci/2945/rebuild

review: Approve (continuous-integration)
Revision history for this message
Alexandros Frantzis (afrantzis) wrote :

Looks good.

review: Approve

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'src/client/input/android/android_input_receiver.cpp'
2--- src/client/input/android/android_input_receiver.cpp 2017-01-18 02:29:37 +0000
3+++ src/client/input/android/android_input_receiver.cpp 2017-02-06 09:42:51 +0000
4@@ -103,12 +103,28 @@
5
6 static void map_key_event(std::shared_ptr<mircv::XKBMapper> const& xkb_mapper, MirEvent &ev)
7 {
8- // TODO: As XKBMapper is used to track modifier state we need to use a seperate instance
9- // of XKBMapper per device id (or modify XKBMapper semantics)
10- if (mir_event_get_type(&ev) != mir_event_type_input)
11- return;
12-
13- xkb_mapper->map_event(ev);
14+ auto const type = mir_event_get_type(&ev);
15+ if (type == mir_event_type_input)
16+ {
17+ xkb_mapper->map_event(ev);
18+ }
19+ else if (type == mir_event_type_input_device_state)
20+ {
21+ auto device_state = mir_event_get_input_device_state_event(&ev);
22+ std::vector<uint32_t> keys;
23+
24+ for (size_t index = 0, end_index = mir_input_device_state_event_device_count(device_state);
25+ index != end_index; ++index)
26+ {
27+ auto device_id = mir_input_device_state_event_device_id(device_state, index);
28+ auto key_count = mir_input_device_state_event_device_pressed_keys_count(device_state, index);
29+ for (decltype(key_count) i = 0; i != key_count; ++i)
30+ keys.push_back(
31+ mir_input_device_state_event_device_pressed_keys_for_index(device_state, index, i));
32+
33+ xkb_mapper->set_key_state(device_id, keys);
34+ }
35+ }
36 }
37
38 }
39
40=== modified file 'src/client/input/xkb_mapper.cpp'
41--- src/client/input/xkb_mapper.cpp 2017-01-18 02:29:37 +0000
42+++ src/client/input/xkb_mapper.cpp 2017-02-06 09:42:51 +0000
43@@ -25,6 +25,7 @@
44
45 #include <sstream>
46 #include <boost/throw_exception.hpp>
47+#include <unordered_set>
48
49 namespace mi = mir::input;
50 namespace mev = mir::events;
51@@ -59,10 +60,18 @@
52 case XKB_KEY_Super_R: return mir_input_event_modifier_meta_right;
53 case XKB_KEY_Caps_Lock: return mir_input_event_modifier_caps_lock;
54 case XKB_KEY_Scroll_Lock: return mir_input_event_modifier_scroll_lock;
55+ case XKB_KEY_Num_Lock: return mir_input_event_modifier_num_lock;
56 default: return MirInputEventModifiers{0};
57 }
58 }
59
60+bool is_toggle_modifier(MirInputEventModifiers key)
61+{
62+ return key == mir_input_event_modifier_caps_lock ||
63+ key == mir_input_event_modifier_scroll_lock ||
64+ key == mir_input_event_modifier_num_lock;
65+}
66+
67 MirInputEventModifiers expand_modifiers(MirInputEventModifiers modifiers)
68 {
69 if (modifiers == 0)
70@@ -167,7 +176,7 @@
71 MirInputEventModifiers new_modifier = 0;
72 for (auto const& mapping_state : device_mapping)
73 {
74- new_modifier |= mapping_state.second->modifier_state;
75+ new_modifier |= mapping_state.second->modifiers();
76 }
77
78 modifier_state = new_modifier;
79@@ -284,6 +293,16 @@
80 return mir_input_event_modifier_none;
81 }
82
83+MirInputEventModifiers mircv::XKBMapper::device_modifiers(MirInputDeviceId id) const
84+{
85+ std::lock_guard<std::mutex> lg(guard);
86+
87+ auto it = device_mapping.find(id);
88+ if (it == end(device_mapping))
89+ return mir_input_event_modifier_none;
90+ return expand_modifiers(it->second->modifiers());
91+}
92+
93 mircv::XKBMapper::XkbMappingState::XkbMappingState(std::shared_ptr<xkb_keymap> const& keymap)
94 : keymap{keymap}, state{make_unique_state(this->keymap.get())}
95 {
96@@ -293,8 +312,20 @@
97 {
98 state = make_unique_state(keymap.get());
99 modifier_state = mir_input_event_modifier_none;
100+ std::unordered_set<uint32_t> pressed_codes;
101 for (uint32_t scan_code : key_state)
102- update_state(to_xkb_scan_code(scan_code), mir_keyboard_action_down, nullptr);
103+ {
104+ bool already_pressed = pressed_codes.count(scan_code) > 0;
105+
106+ update_state(to_xkb_scan_code(scan_code),
107+ (already_pressed) ? mir_keyboard_action_up : mir_keyboard_action_down,
108+ nullptr);
109+
110+ if (already_pressed)
111+ pressed_codes.erase(scan_code);
112+ else
113+ pressed_codes.insert(scan_code);
114+ }
115 }
116
117 bool mircv::XKBMapper::XkbMappingState::update_and_map(MirEvent& event, mircv::XKBMapper::ComposeState* compose_state)
118@@ -322,17 +353,26 @@
119 if (action == mir_keyboard_action_up)
120 {
121 xkb_state_update_key(state.get(), scan_code, XKB_KEY_UP);
122- modifier_state = modifier_state & ~mod_change;
123+ // TODO get the modifier state from xkbcommon and apply it
124+ // for all other modifiers manually track them here:
125+ release_modifier(mod_change);
126 }
127 else if (action == mir_keyboard_action_down)
128 {
129 xkb_state_update_key(state.get(), scan_code, XKB_KEY_DOWN);
130- modifier_state = modifier_state | mod_change;
131+ // TODO get the modifier state from xkbcommon and apply it
132+ // for all other modifiers manually track them here:
133+ press_modifier(mod_change);
134 }
135
136 return key_sym;
137 }
138
139+MirInputEventModifiers mircv::XKBMapper::XkbMappingState::modifiers() const
140+{
141+ return modifier_state;
142+}
143+
144 mircv::XKBMapper::ComposeState* mircv::XKBMapper::get_compose_state(MirInputDeviceId id)
145 {
146 auto dev_compose_state = device_composing.find(id);
147@@ -410,3 +450,18 @@
148
149 return mapped_key;
150 }
151+
152+
153+void mircv::XKBMapper::XkbMappingState::press_modifier(MirInputEventModifiers mod)
154+{
155+ if (is_toggle_modifier(mod))
156+ modifier_state ^= mod;
157+ else
158+ modifier_state |= mod;
159+}
160+
161+void mircv::XKBMapper::XkbMappingState::release_modifier(MirInputEventModifiers mod)
162+{
163+ if (!is_toggle_modifier(mod))
164+ modifier_state &= ~mod;
165+}
166
167=== modified file 'src/include/common/mir/input/key_mapper.h'
168--- src/include/common/mir/input/key_mapper.h 2017-01-18 02:29:37 +0000
169+++ src/include/common/mir/input/key_mapper.h 2017-02-06 09:42:51 +0000
170@@ -88,6 +88,7 @@
171 */
172 virtual void map_event(MirEvent& event) = 0;
173 virtual MirInputEventModifiers modifiers() const = 0;
174+ virtual MirInputEventModifiers device_modifiers(MirInputDeviceId id) const = 0;
175
176 protected:
177 KeyMapper(KeyMapper const&) = delete;
178
179=== modified file 'src/include/common/mir/input/xkb_mapper.h'
180--- src/include/common/mir/input/xkb_mapper.h 2017-01-18 02:29:37 +0000
181+++ src/include/common/mir/input/xkb_mapper.h 2017-02-06 09:42:51 +0000
182@@ -63,6 +63,7 @@
183 void clear_all_keymaps() override;
184 void map_event(MirEvent& event) override;
185 MirInputEventModifiers modifiers() const override;
186+ MirInputEventModifiers device_modifiers(MirInputDeviceId di) const override;
187
188 protected:
189 XKBMapper(XKBMapper const&) = delete;
190@@ -89,8 +90,14 @@
191 {
192 explicit XkbMappingState(std::shared_ptr<xkb_keymap> const& keymap);
193 void set_key_state(std::vector<uint32_t> const& key_state);
194+
195 bool update_and_map(MirEvent& event, ComposeState* compose_state);
196 xkb_keysym_t update_state(uint32_t scan_code, MirKeyboardAction direction, ComposeState* compose_state);
197+ MirInputEventModifiers modifiers() const;
198+ private:
199+ void press_modifier(MirInputEventModifiers mod);
200+ void release_modifier(MirInputEventModifiers mod);
201+
202 std::shared_ptr<xkb_keymap> const keymap;
203 XKBStatePtr state;
204 MirInputEventModifiers modifier_state{0};
205
206=== modified file 'src/server/input/seat_input_device_tracker.cpp'
207--- src/server/input/seat_input_device_tracker.cpp 2017-01-18 02:29:37 +0000
208+++ src/server/input/seat_input_device_tracker.cpp 2017-02-06 09:42:51 +0000
209@@ -31,6 +31,7 @@
210 #include "input_modifier_utils.h"
211
212 #include <boost/throw_exception.hpp>
213+#include <linux/input.h>
214
215 #include <stdexcept>
216 #include <algorithm>
217@@ -115,7 +116,7 @@
218 if (stored_data == end(device_data))
219 return true;
220
221- return !stored_data->second.allowed_scan_code_action(mir_input_event_get_keyboard_event(event));
222+ return !stored_data->second.allowed_scan_code_action(mir_input_event_get_keyboard_event(event));;
223 }
224 return false;
225 }
226@@ -251,8 +252,44 @@
227 std::vector<mev::InputDeviceState> devices;
228 devices.reserve(device_data.size());
229 for (auto const& item : device_data)
230+ {
231 devices.push_back({item.first, item.second.scan_codes, item.second.buttons});
232-
233+ auto lock_state = key_mapper->device_modifiers(item.first);
234+
235+ bool caps_lock_active = (lock_state & mir_input_event_modifier_caps_lock);
236+ bool scroll_lock_active = (lock_state & mir_input_event_modifier_scroll_lock);
237+ bool num_lock_active = (lock_state & mir_input_event_modifier_num_lock);
238+
239+ bool contains_caps_lock_pressed = false;
240+ bool contains_scroll_lock_pressed = false;
241+ bool contains_num_lock_pressed = false;
242+
243+ for (uint32_t scan_code : item.second.scan_codes)
244+ {
245+ if (scan_code == KEY_CAPSLOCK)
246+ contains_caps_lock_pressed = true;
247+ else if (scan_code == KEY_SCROLLLOCK)
248+ contains_scroll_lock_pressed = true;
249+ else if (scan_code == KEY_NUMLOCK)
250+ contains_num_lock_pressed = true;
251+ }
252+
253+ if (caps_lock_active && !contains_caps_lock_pressed)
254+ {
255+ devices.back().pressed_keys.push_back(KEY_CAPSLOCK);
256+ devices.back().pressed_keys.push_back(KEY_CAPSLOCK);
257+ }
258+ if (num_lock_active && !contains_num_lock_pressed)
259+ {
260+ devices.back().pressed_keys.push_back(KEY_NUMLOCK);
261+ devices.back().pressed_keys.push_back(KEY_NUMLOCK);
262+ }
263+ if (scroll_lock_active && !contains_scroll_lock_pressed)
264+ {
265+ devices.back().pressed_keys.push_back(KEY_SCROLLLOCK);
266+ devices.back().pressed_keys.push_back(KEY_SCROLLLOCK);
267+ }
268+ }
269 auto out_ev = mev::make_event(
270 clock->now().time_since_epoch(),
271 buttons,
272
273=== modified file 'tests/acceptance-tests/test_client_input.cpp'
274--- tests/acceptance-tests/test_client_input.cpp 2017-02-06 03:54:54 +0000
275+++ tests/acceptance-tests/test_client_input.cpp 2017-02-06 09:42:51 +0000
276@@ -18,7 +18,10 @@
277
278 #include "mir/input/input_device_info.h"
279 #include "mir/input/event_filter.h"
280+#include "mir/input/keymap.h"
281 #include "mir/input/composite_event_filter.h"
282+#include "mir/scene/session.h"
283+#include "mir/scene/surface.h"
284 #include "mir/input/mir_touchpad_config.h"
285 #include "mir/input/mir_input_config.h"
286
287@@ -45,6 +48,7 @@
288 #include <linux/input.h>
289
290 #include <condition_variable>
291+#include <unordered_map>
292 #include <chrono>
293 #include <atomic>
294 #include <mutex>
295@@ -98,12 +102,52 @@
296 {
297 }
298
299+struct SurfaceTrackingShell : mir::shell::ShellWrapper
300+{
301+ SurfaceTrackingShell(
302+ std::shared_ptr<mir::shell::Shell> wrapped_shell)
303+ : ShellWrapper{wrapped_shell}, wrapped_shell{wrapped_shell}
304+ {}
305+
306+ mir::frontend::SurfaceId create_surface(
307+ std::shared_ptr<mir::scene::Session> const& session,
308+ mir::scene::SurfaceCreationParameters const& params,
309+ std::shared_ptr<mir::frontend::EventSink> const& sink) override
310+ {
311+ auto surface_id = wrapped_shell->create_surface(session, params, sink);
312+
313+ tracked_surfaces[session->name()] = TrackedSurface{session, surface_id};
314+
315+ return surface_id;
316+ }
317+
318+ std::shared_ptr<mir::scene::Surface> get_surface(std::string const& session_name)
319+ {
320+ if (end(tracked_surfaces) == tracked_surfaces.find(session_name))
321+ return nullptr;
322+ TrackedSurface & tracked_surface = tracked_surfaces[session_name];
323+ auto session = tracked_surface.session.lock();
324+ if (!session)
325+ return nullptr;
326+ return session->surface(tracked_surface.surface);
327+ }
328+
329+ struct TrackedSurface
330+ {
331+ std::weak_ptr<mir::scene::Session> session;
332+ mir::frontend::SurfaceId surface;
333+ };
334+ std::unordered_map<std::string, TrackedSurface> tracked_surfaces;
335+ std::shared_ptr<mir::shell::Shell> wrapped_shell;
336+};
337+
338 struct Client
339 {
340 MirWindow* window{nullptr};
341
342 MOCK_METHOD1(handle_input, void(MirEvent const*));
343 MOCK_METHOD1(handle_keymap, void(MirEvent const*));
344+ MOCK_METHOD1(handle_input_device_state, void(MirEvent const*));
345
346 Client(std::string const& con, std::string const& name)
347 {
348@@ -146,7 +190,12 @@
349 mir_window_focus_state_focused == value)
350 focused = true;
351
352- if (exposed && focused)
353+ test_and_raise();
354+ }
355+
356+ void test_and_raise()
357+ {
358+ if (exposed && focused && input_device_state_received)
359 ready_to_accept_events.raise();
360 }
361
362@@ -164,6 +213,12 @@
363 client->handle_input(ev);
364 if (type == mir_event_type_keymap)
365 client->handle_keymap(ev);
366+ if (type == mir_event_type_input_device_state)
367+ {
368+ client->input_device_state_received = true;
369+ client->test_and_raise();
370+ client->handle_input_device_state(ev);
371+ }
372 }
373 ~Client()
374 {
375@@ -181,6 +236,7 @@
376 mir::test::Signal all_events_received;
377 bool exposed = false;
378 bool focused = false;
379+ bool input_device_state_received = false;
380 };
381
382 struct DeviceCounter : mi::InputDeviceObserver
383@@ -216,7 +272,8 @@
384 [this](std::shared_ptr<mir::shell::Shell> const& wrapped)
385 {
386 shell = std::make_shared<mtf::PlacementApplyingShell>(wrapped, input_regions, positions);
387- return shell;
388+ surfaces = std::make_shared<SurfaceTrackingShell>(shell);
389+ return surfaces;
390 });
391 server.override_the_session_authorizer([this] { return mt::fake_shared(stub_authorizer); });
392
393@@ -225,7 +282,13 @@
394 positions[first] = geom::Rectangle{{0,0}, {surface_width, surface_height}};
395 }
396
397+ std::shared_ptr<mir::scene::Surface> get_surface(std::string const& name)
398+ {
399+ return surfaces->get_surface(name);
400+ }
401+
402 std::shared_ptr<mtf::PlacementApplyingShell> shell;
403+ std::shared_ptr<SurfaceTrackingShell> surfaces;
404 std::string const keyboard_name = "keyboard";
405 std::string const keyboard_unique_id = "keyboard-uid";
406 std::string const mouse_name = "mouse";
407@@ -939,6 +1002,81 @@
408 mir_input_config_release(config);
409 }
410
411+TEST_F(TestClientInput, num_lock_is_off_on_startup)
412+{
413+ Client a_client(new_connection(), first);
414+
415+ EXPECT_CALL(a_client, handle_input(mt::KeyOfSymbol(XKB_KEY_KP_Left)))
416+ .WillOnce(mt::WakeUp(&a_client.all_events_received));
417+
418+ fake_keyboard->emit_event(mis::a_key_down_event().of_scancode(KEY_KP4));
419+ a_client.all_events_received.wait_for(10s);
420+}
421+
422+TEST_F(TestClientInput, keeps_num_lock_state_after_focus_change)
423+{
424+ Client first_client(new_connection(), first);
425+
426+ {
427+ Client second_client(new_connection(), second);
428+ EXPECT_CALL(second_client, handle_input(mt::KeyDownEvent()));
429+ EXPECT_CALL(second_client, handle_input(mt::KeyUpEvent()))
430+ .WillOnce(mt::WakeUp(&second_client.all_events_received));
431+
432+ fake_keyboard->emit_event(mis::a_key_down_event().of_scancode(KEY_NUMLOCK));
433+ fake_keyboard->emit_event(mis::a_key_up_event().of_scancode(KEY_NUMLOCK));
434+
435+ second_client.all_events_received.wait_for(10s);
436+ }
437+
438+ EXPECT_CALL(first_client, handle_input(mt::KeyOfSymbol(XKB_KEY_KP_4)))
439+ .WillOnce(mt::WakeUp(&first_client.all_events_received));
440+ fake_keyboard->emit_event(mis::a_key_down_event().of_scancode(KEY_KP4));
441+ first_client.all_events_received.wait_for(10s);
442+}
443+
444+TEST_F(TestClientInput, reestablishes_num_lock_state_in_client_with_surface_keymap)
445+{
446+ Client a_client_with_keymap(new_connection(), first);
447+
448+ mir::test::Signal keymap_received;
449+ mir::test::Signal device_state_received;
450+
451+ EXPECT_CALL(a_client_with_keymap, handle_keymap(_))
452+ .WillOnce(mt::WakeUp(&keymap_received));
453+ EXPECT_CALL(a_client_with_keymap,
454+ handle_input_device_state(
455+ mt::DeviceStateWithPressedKeys(std::vector<uint32_t>{KEY_NUMLOCK, KEY_NUMLOCK})))
456+ .WillOnce(mt::WakeUp(&device_state_received));
457+
458+ get_surface(first)->set_keymap(MirInputDeviceId{0}, "pc105", "de", "", "");
459+ keymap_received.wait_for(4s);
460+
461+ {
462+ Client a_client(new_connection(), second);
463+
464+ EXPECT_CALL(a_client, handle_input(mt::KeyDownEvent()));
465+ EXPECT_CALL(a_client, handle_input(mt::KeyUpEvent()));
466+ EXPECT_CALL(a_client, handle_input(AllOf(mt::KeyDownEvent(),mt::KeyOfSymbol(XKB_KEY_KP_4))));
467+ EXPECT_CALL(a_client, handle_input(AllOf(mt::KeyUpEvent(),mt::KeyOfSymbol(XKB_KEY_KP_4))))
468+ .WillOnce(mt::WakeUp(&a_client.all_events_received));
469+
470+ fake_keyboard->emit_event(mis::a_key_down_event().of_scancode(KEY_NUMLOCK));
471+ fake_keyboard->emit_event(mis::a_key_up_event().of_scancode(KEY_NUMLOCK));
472+ fake_keyboard->emit_event(mis::a_key_down_event().of_scancode(KEY_KP4));
473+ fake_keyboard->emit_event(mis::a_key_up_event().of_scancode(KEY_KP4));
474+
475+ a_client.all_events_received.wait_for(10s);
476+ }
477+ device_state_received.wait_for(4s);
478+ EXPECT_CALL(a_client_with_keymap, handle_input(mt::KeyOfSymbol(XKB_KEY_KP_4)))
479+ .WillOnce(mt::WakeUp(&a_client_with_keymap.all_events_received));
480+
481+ fake_keyboard->emit_event(mis::a_key_down_event().of_scancode(KEY_KP4));
482+
483+ a_client_with_keymap.all_events_received.wait_for(10s);
484+}
485+
486 TEST_F(TestClientInput, initial_mouse_configuration_can_be_querried)
487 {
488 wait_for_input_devices();
489
490=== modified file 'tests/include/mir/test/doubles/mock_key_mapper.h'
491--- tests/include/mir/test/doubles/mock_key_mapper.h 2017-01-18 02:29:37 +0000
492+++ tests/include/mir/test/doubles/mock_key_mapper.h 2017-02-06 09:42:51 +0000
493@@ -41,6 +41,7 @@
494 MOCK_METHOD0(clear_all_keymaps, void());
495 MOCK_METHOD1(map_event, void(MirEvent& event));
496 MOCK_CONST_METHOD0(modifiers, MirInputEventModifiers());
497+ MOCK_CONST_METHOD1(device_modifiers, MirInputEventModifiers(MirInputDeviceId));
498 };
499
500

Subscribers

People subscribed via source and target branches