Mir

Merge lp:~mir-team/mir/wayland-fix-keymaps-re-attempt into lp:mir

Proposed by Brandon Schaefer
Status: Merged
Approved by: Alan Griffiths
Approved revision: no longer in the source branch.
Merged at revision: 4284
Proposed branch: lp:~mir-team/mir/wayland-fix-keymaps-re-attempt
Merge into: lp:mir
Prerequisite: lp:~raof/mir/fixish-wayland-keyboard
Diff against target: 599 lines (+302/-27)
12 files modified
include/core/mir/optional_value.h (+5/-0)
include/server/mir/server_action_queue.h (+10/-0)
src/include/server/mir/glib_main_loop.h (+5/-0)
src/server/frontend/wayland/wayland_connector.cpp (+130/-18)
src/server/frontend/wayland/wayland_connector.h (+5/-1)
src/server/frontend/wayland/wayland_default_configuration.cpp (+1/-0)
src/server/glib_main_loop.cpp (+55/-1)
src/server/input/default_input_device_hub.cpp (+8/-7)
tests/include/mir/test/doubles/triggered_main_loop.h (+1/-0)
tests/mir_test_doubles/triggered_main_loop.cpp (+6/-0)
tests/unit-tests/scene/test_mediating_display_changer.cpp (+6/-0)
tests/unit-tests/test_glib_main_loop.cpp (+70/-0)
To merge this branch: bzr merge lp:~mir-team/mir/wayland-fix-keymaps-re-attempt
Reviewer Review Type Date Requested Status
Alan Griffiths Approve
Mir CI Bot continuous-integration Approve
Review via email: mp+331793@code.launchpad.net

Commit message

Wayland: Handle keymaps vaguely appropriately.

This will work for the common case of one system-wide keymap and no per-surface keymaps.

It does not handle having multiple keyboards with different keymaps, or per-surface keymaps. This is left as an exercise for the reader.

Description of the change

Re proposing https://code.launchpad.net/~mir-team/mir/wayland-fix-keymaps/+merge/331695

Since it didnt want to update its diffs or anything.

Wayland: Handle keymaps vaguely appropriately.

This will work for the common case of one system-wide keymap and no per-surface keymaps.

It does not handle having multiple keyboards with different keymaps, or per-surface keymaps. This is left as an exercise for the reader.

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

FAILED: Continuous integration, rev:4292
https://mir-jenkins.ubuntu.com/job/mir-ci/3714/
Executed test runs:
    FAILURE: https://mir-jenkins.ubuntu.com/job/build-mir/5095/console
    SUCCESS: https://mir-jenkins.ubuntu.com/job/build-0-fetch/5333
    SUCCESS: https://mir-jenkins.ubuntu.com/job/build-1-sourcepkg/release=artful/5320
    SUCCESS: https://mir-jenkins.ubuntu.com/job/build-1-sourcepkg/release=xenial/5320
    SUCCESS: https://mir-jenkins.ubuntu.com/job/build-1-sourcepkg/release=zesty/5320
    FAILURE: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-mir/arch=amd64,compiler=clang,platform=mesa,release=artful/5139/console
    FAILURE: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-mir/arch=amd64,compiler=clang,platform=mesa,release=zesty/5139/console
    FAILURE: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-mir/arch=amd64,compiler=gcc,platform=mesa,release=artful/5139/console
    FAILURE: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-mir/arch=amd64,compiler=gcc,platform=mesa,release=xenial/5139/console
    FAILURE: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-mir/arch=amd64,compiler=gcc,platform=mesa,release=zesty/5139/console
    SUCCESS: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-mir/arch=cross-armhf,compiler=gcc,platform=mesa,release=artful/5139
        deb: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-mir/arch=cross-armhf,compiler=gcc,platform=mesa,release=artful/5139/artifact/output/*zip*/output.zip
    SUCCESS: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-mir/arch=cross-armhf,compiler=gcc,platform=mesa,release=zesty/5139
        deb: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-mir/arch=cross-armhf,compiler=gcc,platform=mesa,release=zesty/5139/artifact/output/*zip*/output.zip
    FAILURE: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-mir/arch=i386,compiler=gcc,platform=mesa,release=xenial/5139/console

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

review: Needs Fixing (continuous-integration)
Revision history for this message
MichaƂ Sawicz (saviq) wrote :

I'm afraid CI is quite insistent on the test failures :/

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

FAILED: Continuous integration, rev:4292
https://mir-jenkins.ubuntu.com/job/mir-ci/3715/
Executed test runs:
    FAILURE: https://mir-jenkins.ubuntu.com/job/build-mir/5098/console
    SUCCESS: https://mir-jenkins.ubuntu.com/job/build-0-fetch/5336
    SUCCESS: https://mir-jenkins.ubuntu.com/job/build-1-sourcepkg/release=artful/5323
    SUCCESS: https://mir-jenkins.ubuntu.com/job/build-1-sourcepkg/release=xenial/5323
    SUCCESS: https://mir-jenkins.ubuntu.com/job/build-1-sourcepkg/release=zesty/5323
    ABORTED: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-mir/arch=amd64,compiler=clang,platform=mesa,release=artful/5142/console
    FAILURE: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-mir/arch=amd64,compiler=clang,platform=mesa,release=zesty/5142/console
    FAILURE: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-mir/arch=amd64,compiler=gcc,platform=mesa,release=artful/5142/console
    FAILURE: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-mir/arch=amd64,compiler=gcc,platform=mesa,release=xenial/5142/console
    FAILURE: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-mir/arch=amd64,compiler=gcc,platform=mesa,release=zesty/5142/console
    SUCCESS: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-mir/arch=cross-armhf,compiler=gcc,platform=mesa,release=artful/5142
        deb: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-mir/arch=cross-armhf,compiler=gcc,platform=mesa,release=artful/5142/artifact/output/*zip*/output.zip
    SUCCESS: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-mir/arch=cross-armhf,compiler=gcc,platform=mesa,release=zesty/5142
        deb: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-mir/arch=cross-armhf,compiler=gcc,platform=mesa,release=zesty/5142/artifact/output/*zip*/output.zip
    FAILURE: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-mir/arch=i386,compiler=gcc,platform=mesa,release=xenial/5142/console

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

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

FAILED: Continuous integration, rev:4292
https://mir-jenkins.ubuntu.com/job/mir-ci/3717/
Executed test runs:
    FAILURE: https://mir-jenkins.ubuntu.com/job/build-mir/5100/console
    SUCCESS: https://mir-jenkins.ubuntu.com/job/build-0-fetch/5338
    SUCCESS: https://mir-jenkins.ubuntu.com/job/build-1-sourcepkg/release=artful/5325
    SUCCESS: https://mir-jenkins.ubuntu.com/job/build-1-sourcepkg/release=xenial/5325
    SUCCESS: https://mir-jenkins.ubuntu.com/job/build-1-sourcepkg/release=zesty/5325
    FAILURE: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-mir/arch=amd64,compiler=clang,platform=mesa,release=artful/5144/console
    SUCCESS: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-mir/arch=amd64,compiler=clang,platform=mesa,release=zesty/5144
        deb: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-mir/arch=amd64,compiler=clang,platform=mesa,release=zesty/5144/artifact/output/*zip*/output.zip
    SUCCESS: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-mir/arch=amd64,compiler=gcc,platform=mesa,release=artful/5144
        deb: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-mir/arch=amd64,compiler=gcc,platform=mesa,release=artful/5144/artifact/output/*zip*/output.zip
    SUCCESS: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-mir/arch=amd64,compiler=gcc,platform=mesa,release=xenial/5144
        deb: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-mir/arch=amd64,compiler=gcc,platform=mesa,release=xenial/5144/artifact/output/*zip*/output.zip
    SUCCESS: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-mir/arch=amd64,compiler=gcc,platform=mesa,release=zesty/5144
        deb: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-mir/arch=amd64,compiler=gcc,platform=mesa,release=zesty/5144/artifact/output/*zip*/output.zip
    FAILURE: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-mir/arch=cross-armhf,compiler=gcc,platform=mesa,release=artful/5144/console
    SUCCESS: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-mir/arch=cross-armhf,compiler=gcc,platform=mesa,release=zesty/5144
        deb: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-mir/arch=cross-armhf,compiler=gcc,platform=mesa,release=zesty/5144/artifact/output/*zip*/output.zip
    SUCCESS: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-mir/arch=i386,compiler=gcc,platform=mesa,release=xenial/5144
        deb: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-mir/arch=i386,compiler=gcc,platform=mesa,release=xenial/5144/artifact/output/*zip*/output.zip

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

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

PASSED: Continuous integration, rev:4292
https://mir-jenkins.ubuntu.com/job/mir-ci/3718/
Executed test runs:
    SUCCESS: https://mir-jenkins.ubuntu.com/job/build-mir/5101
    SUCCESS: https://mir-jenkins.ubuntu.com/job/build-0-fetch/5339
    SUCCESS: https://mir-jenkins.ubuntu.com/job/build-1-sourcepkg/release=artful/5326
    SUCCESS: https://mir-jenkins.ubuntu.com/job/build-1-sourcepkg/release=xenial/5326
    SUCCESS: https://mir-jenkins.ubuntu.com/job/build-1-sourcepkg/release=zesty/5326
    SUCCESS: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-mir/arch=amd64,compiler=clang,platform=mesa,release=artful/5145
        deb: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-mir/arch=amd64,compiler=clang,platform=mesa,release=artful/5145/artifact/output/*zip*/output.zip
    SUCCESS: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-mir/arch=amd64,compiler=clang,platform=mesa,release=zesty/5145
        deb: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-mir/arch=amd64,compiler=clang,platform=mesa,release=zesty/5145/artifact/output/*zip*/output.zip
    SUCCESS: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-mir/arch=amd64,compiler=gcc,platform=mesa,release=artful/5145
        deb: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-mir/arch=amd64,compiler=gcc,platform=mesa,release=artful/5145/artifact/output/*zip*/output.zip
    SUCCESS: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-mir/arch=amd64,compiler=gcc,platform=mesa,release=xenial/5145
        deb: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-mir/arch=amd64,compiler=gcc,platform=mesa,release=xenial/5145/artifact/output/*zip*/output.zip
    SUCCESS: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-mir/arch=amd64,compiler=gcc,platform=mesa,release=zesty/5145
        deb: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-mir/arch=amd64,compiler=gcc,platform=mesa,release=zesty/5145/artifact/output/*zip*/output.zip
    SUCCESS: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-mir/arch=cross-armhf,compiler=gcc,platform=mesa,release=artful/5145
        deb: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-mir/arch=cross-armhf,compiler=gcc,platform=mesa,release=artful/5145/artifact/output/*zip*/output.zip
    SUCCESS: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-mir/arch=cross-armhf,compiler=gcc,platform=mesa,release=zesty/5145
        deb: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-mir/arch=cross-armhf,compiler=gcc,platform=mesa,release=zesty/5145/artifact/output/*zip*/output.zip
    SUCCESS: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-mir/arch=i386,compiler=gcc,platform=mesa,release=xenial/5145
        deb: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-mir/arch=i386,compiler=gcc,platform=mesa,release=xenial/5145/artifact/output/*zip*/output.zip

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

review: Approve (continuous-integration)
Revision history for this message
Alan Griffiths (alan-griffiths) wrote :

Nit:

+ void device_added (std::shared_ptr<input::Device> const& device) override;
+ void device_changed (std::shared_ptr<input::Device> const& device) override;
+ void device_removed (std::shared_ptr<input::Device> const& device) override;
+ void changes_complete () override;

We don't leave a space before function call parentheses.

Otherwise looks plausible and works for me

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

PASSED: Continuous integration, rev:4283
https://mir-jenkins.ubuntu.com/job/mir-ci/3719/
Executed test runs:
    SUCCESS: https://mir-jenkins.ubuntu.com/job/build-mir/5104
    SUCCESS: https://mir-jenkins.ubuntu.com/job/build-0-fetch/5342
    SUCCESS: https://mir-jenkins.ubuntu.com/job/build-1-sourcepkg/release=artful/5329
    SUCCESS: https://mir-jenkins.ubuntu.com/job/build-1-sourcepkg/release=xenial/5329
    SUCCESS: https://mir-jenkins.ubuntu.com/job/build-1-sourcepkg/release=zesty/5329
    SUCCESS: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-mir/arch=amd64,compiler=clang,platform=mesa,release=artful/5148
        deb: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-mir/arch=amd64,compiler=clang,platform=mesa,release=artful/5148/artifact/output/*zip*/output.zip
    SUCCESS: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-mir/arch=amd64,compiler=clang,platform=mesa,release=zesty/5148
        deb: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-mir/arch=amd64,compiler=clang,platform=mesa,release=zesty/5148/artifact/output/*zip*/output.zip
    SUCCESS: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-mir/arch=amd64,compiler=gcc,platform=mesa,release=artful/5148
        deb: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-mir/arch=amd64,compiler=gcc,platform=mesa,release=artful/5148/artifact/output/*zip*/output.zip
    SUCCESS: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-mir/arch=amd64,compiler=gcc,platform=mesa,release=xenial/5148
        deb: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-mir/arch=amd64,compiler=gcc,platform=mesa,release=xenial/5148/artifact/output/*zip*/output.zip
    SUCCESS: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-mir/arch=amd64,compiler=gcc,platform=mesa,release=zesty/5148
        deb: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-mir/arch=amd64,compiler=gcc,platform=mesa,release=zesty/5148/artifact/output/*zip*/output.zip
    SUCCESS: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-mir/arch=cross-armhf,compiler=gcc,platform=mesa,release=artful/5148
        deb: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-mir/arch=cross-armhf,compiler=gcc,platform=mesa,release=artful/5148/artifact/output/*zip*/output.zip
    SUCCESS: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-mir/arch=cross-armhf,compiler=gcc,platform=mesa,release=zesty/5148
        deb: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-mir/arch=cross-armhf,compiler=gcc,platform=mesa,release=zesty/5148/artifact/output/*zip*/output.zip
    SUCCESS: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-mir/arch=i386,compiler=gcc,platform=mesa,release=xenial/5148
        deb: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-mir/arch=i386,compiler=gcc,platform=mesa,release=xenial/5148/artifact/output/*zip*/output.zip

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

review: Approve (continuous-integration)
Revision history for this message
Alan Griffiths (alan-griffiths) wrote :

Fixed

review: Approve

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'include/core/mir/optional_value.h'
2--- include/core/mir/optional_value.h 2017-07-28 17:00:43 +0000
3+++ include/core/mir/optional_value.h 2017-10-09 17:50:36 +0000
4@@ -59,6 +59,11 @@
5 return std::move(value_);
6 }
7
8+ operator bool() const
9+ {
10+ return is_set();
11+ }
12+
13 private:
14 void die_if_unset() const
15 {
16
17=== modified file 'include/server/mir/server_action_queue.h'
18--- include/server/mir/server_action_queue.h 2017-07-28 17:00:43 +0000
19+++ include/server/mir/server_action_queue.h 2017-10-09 17:50:36 +0000
20@@ -32,6 +32,16 @@
21 virtual ~ServerActionQueue() = default;
22
23 virtual void enqueue(void const* owner, ServerAction const& action) = 0;
24+ /**
25+ * Enqueue an action to be run, guaranteeing that it *will* be run.
26+ *
27+ * The action will be run even if the queue is not normally being drained
28+ * (for example, if the main loop is suspended). If running on the queue is
29+ * not possible, the action will be run on the caller's thread.
30+ *
31+ * \param [in] action Functor to invoke.
32+ */
33+ virtual void enqueue_with_guaranteed_execution(ServerAction const& action) = 0;
34 virtual void pause_processing_for(void const* owner) = 0;
35 virtual void resume_processing_for(void const* owner) = 0;
36
37
38=== modified file 'src/include/server/mir/glib_main_loop.h'
39--- src/include/server/mir/glib_main_loop.h 2017-07-28 17:00:43 +0000
40+++ src/include/server/mir/glib_main_loop.h 2017-10-09 17:50:36 +0000
41@@ -28,6 +28,7 @@
42 #include <exception>
43
44 #include <glib.h>
45+#include <deque>
46
47 namespace mir
48 {
49@@ -76,6 +77,8 @@
50 void unregister_fd_handler(void const* owner) override;
51
52 void enqueue(void const* owner, ServerAction const& action) override;
53+ void enqueue_with_guaranteed_execution(ServerAction const& action) override;
54+
55 void pause_processing_for(void const* owner) override;
56 void resume_processing_for(void const* owner) override;
57
58@@ -100,6 +103,8 @@
59 detail::SignalSources signal_sources;
60 std::mutex do_not_process_mutex;
61 std::vector<void const*> do_not_process;
62+ std::mutex run_on_halt_mutex;
63+ std::deque<ServerAction> run_on_halt_queue;
64 std::function<void()> before_iteration_hook;
65 std::exception_ptr main_loop_exception;
66 };
67
68=== modified file 'src/server/frontend/wayland/wayland_connector.cpp'
69--- src/server/frontend/wayland/wayland_connector.cpp 2017-10-08 02:12:58 +0000
70+++ src/server/frontend/wayland/wayland_connector.cpp 2017-10-09 17:50:36 +0000
71@@ -43,6 +43,11 @@
72
73 #include "mir/client/event.h"
74
75+#include "mir/input/device.h"
76+#include "mir/input/mir_keyboard_config.h"
77+#include "mir/input/input_device_hub.h"
78+#include "mir/input/input_device_observer.h"
79+
80 #include <system_error>
81 #include <sys/eventfd.h>
82 #include <wayland-server-core.h>
83@@ -75,6 +80,7 @@
84 namespace ms = mir::scene;
85 namespace geom = mir::geometry;
86 namespace mcl = mir::client;
87+namespace mi = mir::input;
88
89 namespace mir
90 {
91@@ -798,6 +804,7 @@
92 wl_client* client,
93 wl_resource* parent,
94 uint32_t id,
95+ mir::input::Keymap const& initial_keymap,
96 std::function<void(WlKeyboard*)> const& on_destroy,
97 std::shared_ptr<mir::Executor> const& executor)
98 : Keyboard(client, parent, id),
99@@ -811,19 +818,14 @@
100 // TODO: We should really grab the keymap for the focused surface when
101 // we receive focus.
102
103- xkb_rule_names default_rules;
104- memset(&default_rules, 0, sizeof(default_rules));
105-
106- keymap = decltype(keymap){
107- xkb_keymap_new_from_names(
108- context.get(),
109- &default_rules,
110- XKB_KEYMAP_COMPILE_NO_FLAGS),
111- &xkb_keymap_unref};
112-
113- state = decltype(state){
114- xkb_state_new(keymap.get()),
115- &xkb_state_unref};
116+ // TODO: Maintain per-device keymaps, and send the appropriate map before
117+ // sending an event from a keyboard with a different map.
118+
119+ /* The wayland::Keyboard constructor has already run, creating the keyboard
120+ * resource. It is thus safe to send a keymap event to it; the client will receive
121+ * the keyboard object before this event.
122+ */
123+ set_keymap(initial_keymap);
124 }
125
126 ~WlKeyboard()
127@@ -966,6 +968,38 @@
128 state = decltype(state)(xkb_state_new(keymap.get()), &xkb_state_unref);
129 }
130
131+ void set_keymap(mir::input::Keymap const& new_keymap)
132+ {
133+ xkb_rule_names const names = {
134+ "evdev",
135+ new_keymap.model.c_str(),
136+ new_keymap.layout.c_str(),
137+ new_keymap.variant.c_str(),
138+ new_keymap.options.c_str()
139+ };
140+ keymap = decltype(keymap){
141+ xkb_keymap_new_from_names(
142+ context.get(),
143+ &names,
144+ XKB_KEYMAP_COMPILE_NO_FLAGS),
145+ &xkb_keymap_unref};
146+
147+ // TODO: We might need to copy across the existing depressed keys?
148+ state = decltype(state)(xkb_state_new(keymap.get()), &xkb_state_unref);
149+
150+ auto buffer = xkb_keymap_get_as_string(keymap.get(), XKB_KEYMAP_FORMAT_TEXT_V1);
151+ auto length = strlen(buffer);
152+
153+ mir::AnonymousShmFile shm_buffer{length};
154+ memcpy(shm_buffer.base_ptr(), buffer, length);
155+
156+ wl_keyboard_send_keymap(
157+ resource,
158+ WL_KEYBOARD_KEYMAP_FORMAT_XKB_V1,
159+ shm_buffer.fd(),
160+ length);
161+ }
162+
163 private:
164 std::unique_ptr<xkb_keymap, decltype(&xkb_keymap_unref)> keymap;
165 std::unique_ptr<xkb_state, decltype(&xkb_state_unref)> state;
166@@ -1320,8 +1354,20 @@
167 class WlSeat
168 {
169 public:
170- WlSeat(wl_display* display, std::shared_ptr<mir::Executor> const& executor)
171- : executor{executor},
172+ WlSeat(
173+ wl_display* display,
174+ std::shared_ptr<mi::InputDeviceHub> const& input_hub,
175+ std::shared_ptr<mir::Executor> const& executor)
176+ : config_observer{
177+ std::make_shared<ConfigObserver>(
178+ keymap,
179+ [this](mir::input::Keymap const& new_keymap)
180+ {
181+ keymap = new_keymap;
182+
183+ })},
184+ input_hub{input_hub},
185+ executor{executor},
186 global{wl_global_create(
187 display,
188 &wl_seat_interface,
189@@ -1333,10 +1379,12 @@
190 {
191 BOOST_THROW_EXCEPTION(std::runtime_error("Failed to export wl_seat interface"));
192 }
193+ input_hub->add_observer(config_observer);
194 }
195
196 ~WlSeat()
197 {
198+ input_hub->remove_observer(config_observer);
199 wl_global_destroy(global);
200 }
201
202@@ -1350,9 +1398,38 @@
203 }
204
205 private:
206+ class ConfigObserver : public mi::InputDeviceObserver
207+ {
208+ public:
209+ ConfigObserver(
210+ mir::input::Keymap const& keymap,
211+ std::function<void(mir::input::Keymap const&)> const& on_keymap_commit)
212+ : current_keymap{keymap},
213+ on_keymap_commit{on_keymap_commit}
214+ {
215+ }
216+
217+ void device_added(std::shared_ptr<input::Device> const& device) override;
218+ void device_changed(std::shared_ptr<input::Device> const& device) override;
219+ void device_removed(std::shared_ptr<input::Device> const& device) override;
220+ void changes_complete() override;
221+
222+ private:
223+ mir::input::Keymap const& current_keymap;
224+ mir::input::Keymap pending_keymap;
225+ std::function<void(mir::input::Keymap const&)> const on_keymap_commit;
226+ };
227+
228+ mir::input::Keymap keymap;
229+ std::shared_ptr<ConfigObserver> const config_observer;
230+
231 std::unordered_map<wl_client*, InputCtx<WlPointer>> mutable pointer;
232 std::unordered_map<wl_client*, InputCtx<WlKeyboard>> mutable keyboard;
233 std::unordered_map<wl_client*, InputCtx<WlTouch>> mutable touch;
234+
235+ std::shared_ptr<mi::InputDeviceHub> const input_hub;
236+
237+
238 std::shared_ptr<mir::Executor> const executor;
239
240 static void bind(struct wl_client* client, void* data, uint32_t version, uint32_t id)
241@@ -1413,6 +1490,7 @@
242 client,
243 resource,
244 id,
245+ me->keymap,
246 [&input_ctx](WlKeyboard* listener)
247 {
248 input_ctx.unregister_listener(listener);
249@@ -1435,8 +1513,10 @@
250 },
251 me->executor});
252 }
253- static void release(struct wl_client* /*client*/, struct wl_resource* /*resource*/) {}
254-
255+ static void release(struct wl_client* /*client*/, struct wl_resource* us)
256+ {
257+ wl_resource_destroy(us);
258+ }
259
260 wl_global* const global;
261 static struct wl_seat_interface const vtable;
262@@ -1464,6 +1544,37 @@
263 return touch[client];
264 }
265
266+void WlSeat::ConfigObserver::device_added(std::shared_ptr<input::Device> const& device)
267+{
268+ if (auto keyboard_config = device->keyboard_configuration())
269+ {
270+ if (current_keymap != keyboard_config.value().device_keymap())
271+ {
272+ pending_keymap = keyboard_config.value().device_keymap();
273+ }
274+ }
275+}
276+
277+void WlSeat::ConfigObserver::device_changed(std::shared_ptr<input::Device> const& device)
278+{
279+ if (auto keyboard_config = device->keyboard_configuration())
280+ {
281+ if (current_keymap != keyboard_config.value().device_keymap())
282+ {
283+ pending_keymap = keyboard_config.value().device_keymap();
284+ }
285+ }
286+}
287+
288+void WlSeat::ConfigObserver::device_removed(std::shared_ptr<input::Device> const& /*device*/)
289+{
290+}
291+
292+void WlSeat::ConfigObserver::changes_complete()
293+{
294+ on_keymap_commit(pending_keymap);
295+}
296+
297 void WaylandEventSink::send_buffer(BufferStreamId /*id*/, graphics::Buffer& /*buffer*/, graphics::BufferIpcMsgType)
298 {
299 }
300@@ -2055,6 +2166,7 @@
301 optional_value<std::string> const& display_name,
302 std::shared_ptr<mf::Shell> const& shell,
303 DisplayChanger& display_config,
304+ std::shared_ptr<mi::InputDeviceHub> const& input_hub,
305 std::shared_ptr<mg::GraphicBufferAllocator> const& allocator,
306 bool arw_socket)
307 : display{wl_display_create(), &cleanup_display},
308@@ -2089,7 +2201,7 @@
309 display.get(),
310 executor,
311 this->allocator);
312- seat_global = std::make_unique<mf::WlSeat>(display.get(), executor);
313+ seat_global = std::make_unique<mf::WlSeat>(display.get(), input_hub, executor);
314 output_manager = std::make_unique<mf::OutputManager>(
315 display.get(),
316 display_config);
317
318=== modified file 'src/server/frontend/wayland/wayland_connector.h'
319--- src/server/frontend/wayland/wayland_connector.h 2017-09-15 16:34:13 +0000
320+++ src/server/frontend/wayland/wayland_connector.h 2017-10-09 17:50:36 +0000
321@@ -29,7 +29,10 @@
322 namespace mir
323 {
324
325-
326+namespace input
327+{
328+class InputDeviceHub;
329+}
330 namespace graphics
331 {
332 class GraphicBufferAllocator;
333@@ -54,6 +57,7 @@
334 optional_value<std::string> const& display_name,
335 std::shared_ptr<Shell> const& shell,
336 DisplayChanger& display_config,
337+ std::shared_ptr<input::InputDeviceHub> const& input_hub,
338 std::shared_ptr<graphics::GraphicBufferAllocator> const& allocator,
339 bool arw_socket);
340
341
342=== modified file 'src/server/frontend/wayland/wayland_default_configuration.cpp'
343--- src/server/frontend/wayland/wayland_default_configuration.cpp 2017-09-15 16:34:13 +0000
344+++ src/server/frontend/wayland/wayland_default_configuration.cpp 2017-10-09 17:50:36 +0000
345@@ -42,6 +42,7 @@
346 display_name,
347 the_frontend_shell(),
348 *the_frontend_display_changer(),
349+ the_input_device_hub(),
350 the_buffer_allocator(),
351 arw_socket);
352 });
353
354=== modified file 'src/server/glib_main_loop.cpp'
355--- src/server/glib_main_loop.cpp 2017-07-28 17:00:43 +0000
356+++ src/server/glib_main_loop.cpp 2017-10-09 17:50:36 +0000
357@@ -154,7 +154,19 @@
358 detail::add_idle_gsource(main_context, G_PRIORITY_HIGH,
359 [this]
360 {
361- running = false;
362+ {
363+ std::lock_guard<std::mutex> lock{run_on_halt_mutex};
364+ running = false;
365+ }
366+ // We know any other thread sees running == false here, so don't need
367+ // to lock run_on_halt_queue.
368+ for (auto& action : run_on_halt_queue)
369+ {
370+ try { action(); }
371+ catch (...) { handle_exception(std::current_exception()); }
372+ }
373+ run_on_halt_queue.clear();
374+
375 g_main_context_wakeup(main_context);
376 });
377 }
378@@ -246,6 +258,48 @@
379 });
380 }
381
382+
383+void mir::GLibMainLoop::enqueue_with_guaranteed_execution(mir::ServerAction const& action)
384+{
385+ auto const action_with_exception_handling =
386+ [this]
387+ {
388+ try
389+ {
390+ mir::ServerAction action;
391+ {
392+ std::lock_guard<std::mutex> lock{run_on_halt_mutex};
393+ action = run_on_halt_queue.front();
394+ run_on_halt_queue.pop_front();
395+ }
396+ action();
397+ }
398+ catch (...)
399+ {
400+ handle_exception(std::current_exception());
401+ }
402+ };
403+
404+ {
405+ std::lock_guard<std::mutex> lock{run_on_halt_mutex};
406+
407+ if (!running)
408+ {
409+ action();
410+ return;
411+ }
412+ else
413+ {
414+ run_on_halt_queue.push_back(action);
415+ }
416+ }
417+
418+ detail::add_idle_gsource(
419+ main_context,
420+ G_PRIORITY_DEFAULT,
421+ action_with_exception_handling);
422+}
423+
424 void mir::GLibMainLoop::pause_processing_for(void const* owner)
425 {
426 std::lock_guard<std::mutex> lock{do_not_process_mutex};
427
428=== modified file 'src/server/input/default_input_device_hub.cpp'
429--- src/server/input/default_input_device_hub.cpp 2017-07-28 17:00:43 +0000
430+++ src/server/input/default_input_device_hub.cpp 2017-10-09 17:50:36 +0000
431@@ -88,25 +88,26 @@
432 auto observer = obs.lock();
433 if (observer)
434 {
435- std::mutex mutex;
436 std::condition_variable cv;
437- std::unique_lock<decltype(mutex)> lock{mutex};
438- bool removed{false};
439+ std::atomic<bool> removed{false};
440
441- data->observer_queue->enqueue(
442- data.get(),
443+ data->observer_queue->enqueue_with_guaranteed_execution(
444 [&,this]
445 {
446 // Unless remove() is on the same thread as add() external synchronization would be needed
447 data->observers.remove(observer);
448
449- std::lock_guard<decltype(mutex)> lock{mutex};
450+ // We do *not* take a lock and instead rely on removed being atomic,
451+ // as enqueue_with_guaranteed_execution may run on our stack.
452 removed = true;
453 cv.notify_one();
454 });
455
456+ std::mutex mutex;
457+ std::unique_lock<decltype(mutex)> lock{mutex};
458+
459 // Before returning wait for the remove - otherwise notifications can still happen
460- cv.wait(lock, [&] { return removed; });
461+ cv.wait(lock, [&] { return removed.load(); });
462 }
463 }
464
465
466=== modified file 'tests/include/mir/test/doubles/triggered_main_loop.h'
467--- tests/include/mir/test/doubles/triggered_main_loop.h 2017-07-28 17:00:43 +0000
468+++ tests/include/mir/test/doubles/triggered_main_loop.h 2017-10-09 17:50:36 +0000
469@@ -41,6 +41,7 @@
470 void unregister_fd_handler(void const* owner) override;
471 std::unique_ptr<mir::time::Alarm> create_alarm(callback const& call) override;
472 void enqueue(void const* owner, ServerAction const& action) override;
473+ void enqueue_with_guaranteed_execution(ServerAction const& action) override;
474
475 void spawn(std::function<void()>&& work) override;
476
477
478=== modified file 'tests/mir_test_doubles/triggered_main_loop.cpp'
479--- tests/mir_test_doubles/triggered_main_loop.cpp 2017-07-28 17:00:43 +0000
480+++ tests/mir_test_doubles/triggered_main_loop.cpp 2017-10-09 17:50:36 +0000
481@@ -81,6 +81,12 @@
482 actions.push_back(action);
483 }
484
485+void mtd::TriggeredMainLoop::enqueue_with_guaranteed_execution(ServerAction const& action)
486+{
487+ base::enqueue(nullptr, action);
488+ actions.push_back(action);
489+}
490+
491 void mtd::TriggeredMainLoop::trigger_server_actions()
492 {
493 for (auto const& action : actions)
494
495=== modified file 'tests/unit-tests/scene/test_mediating_display_changer.cpp'
496--- tests/unit-tests/scene/test_mediating_display_changer.cpp 2017-08-09 11:30:59 +0000
497+++ tests/unit-tests/scene/test_mediating_display_changer.cpp 2017-10-09 17:50:36 +0000
498@@ -100,6 +100,10 @@
499 {
500 action();
501 }
502+ void enqueue_with_guaranteed_execution(mir::ServerAction const& action) override
503+ {
504+ action();
505+ }
506
507 void pause_processing_for(void const* /*owner*/) override {}
508 void resume_processing_for(void const* /*owner*/) override {}
509@@ -110,8 +114,10 @@
510 MockServerActionQueue()
511 {
512 ON_CALL(*this, enqueue(_, _)).WillByDefault(InvokeArgument<1>());
513+ ON_CALL(*this, enqueue_with_guaranteed_execution(_)).WillByDefault(InvokeArgument<0>());
514 }
515 MOCK_METHOD2(enqueue, void(void const*, mir::ServerAction const&));
516+ MOCK_METHOD1(enqueue_with_guaranteed_execution, void(mir::ServerAction const&));
517 MOCK_METHOD1(pause_processing_for, void(void const*));
518 MOCK_METHOD1(resume_processing_for, void(void const*));
519 };
520
521=== modified file 'tests/unit-tests/test_glib_main_loop.cpp'
522--- tests/unit-tests/test_glib_main_loop.cpp 2017-07-28 17:00:43 +0000
523+++ tests/unit-tests/test_glib_main_loop.cpp 2017-10-09 17:50:36 +0000
524@@ -833,6 +833,76 @@
525 destroy_glib_main_loop);
526 }
527
528+TEST_F(GLibMainLoopTest, enqueue_with_guaranteed_execution_executes_before_run)
529+{
530+ using namespace testing;
531+
532+ int num_actions{0};
533+
534+ ml.enqueue_with_guaranteed_execution(
535+ [&num_actions]
536+ {
537+ ++num_actions;
538+ });
539+
540+ EXPECT_THAT(num_actions, Eq(1));
541+}
542+
543+TEST_F(GLibMainLoopTest, enqueue_with_guaranteed_execution_executes_after_stop)
544+{
545+ using namespace testing;
546+
547+ int num_actions{0};
548+
549+ ml.enqueue(
550+ nullptr,
551+ [this]()
552+ {
553+ ml.stop();
554+ });
555+
556+ ml.run();
557+
558+ ml.enqueue_with_guaranteed_execution(
559+ [&num_actions]
560+ {
561+ ++num_actions;
562+ });
563+
564+ EXPECT_THAT(num_actions, Eq(1));
565+}
566+
567+TEST_F(GLibMainLoopTest, enqueue_with_guaranteed_execution_executes_on_mainloop)
568+{
569+ using namespace testing;
570+
571+ mt::Signal loop_running;
572+ mt::Signal loop_finished;
573+
574+ ml.enqueue(
575+ nullptr,
576+ [&loop_running]() { loop_running.raise(); });
577+
578+ mt::AutoJoinThread t{
579+ [&]
580+ {
581+ ml.run();
582+ loop_finished.raise();
583+ }};
584+
585+ ASSERT_TRUE(loop_running.wait_for(std::chrono::seconds{5}));
586+
587+ ml.enqueue_with_guaranteed_execution(
588+ [main_thread = std::this_thread::get_id()]()
589+ {
590+ EXPECT_THAT(std::this_thread::get_id(), Ne(main_thread));
591+ });
592+
593+ ml.stop();
594+
595+ EXPECT_TRUE(loop_finished.wait_for(std::chrono::seconds{30}));
596+}
597+
598 namespace
599 {
600

Subscribers

People subscribed via source and target branches