Mir

Merge lp:~afrantzis/mir/always-consume-input-events-in-clients into lp:mir

Proposed by Alexandros Frantzis
Status: Rejected
Rejected by: Alan Griffiths
Proposed branch: lp:~afrantzis/mir/always-consume-input-events-in-clients
Merge into: lp:mir
Diff against target: 248 lines (+113/-82)
2 files modified
src/client/mir_surface.cpp (+13/-6)
tests/unit-tests/client/test_client_mir_surface.cpp (+100/-76)
To merge this branch: bzr merge lp:~afrantzis/mir/always-consume-input-events-in-clients
Reviewer Review Type Date Requested Status
Alan Griffiths Needs Information
PS Jenkins bot (community) continuous-integration Approve
Review via email: mp+209313@code.launchpad.net

Commit message

client: Process input events even in clients that don't handle input

Description of the change

client: Process input events even in clients that don't handle input

This is one alternative solution for bug 1213804. See comments in bug 1213804 for more information and pointers to other options.

Currently, focused client surfaces that don't handle input events don't process them at all. Such clients never acknowledge receiving input events, causing the input system to retry sending the events.

If in the interval between two send attempts another app gains the focus, the input system sends the event to the newly focused app/surface, leading to bug 1213804 (i.e. the target of events is always the currently focused surfaced, not a particular surface).

Ideally, the input system should be able to handle this situation better, e.g., by resending the event only if the focused surface the event was originally targeted at hasn't changed. However, the input subsystem is full of intricacies and I am not familiar enough with it to tell if this behavior is a bug or has a particular reason for being there.

If we decide that the proposed solution/workaround is not acceptable, I can dive more deeply into the abyss known as the input subsystem.

To post a comment you must log in.
Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Approve (continuous-integration)
Revision history for this message
Daniel van Vugt (vanvugt) wrote :

It definitely sounds like a workaround more than a fix. It also leads to clients being woken up by events they should never receive at all. But we possibly already had that problem...

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

> It definitely sounds like a workaround more than a fix. It also leads to
> clients being woken up by events they should never receive at all. But we
> possibly already had that problem...

I guess toolkits will always have event handles set?

Revision history for this message
Alan Griffiths (alan-griffiths) wrote :

It sounds to me as though the real problem is in the server - that if a client fails to consume an event then "bad stuff happens". I'd rather fix the "bad stuff happens" - but if that is a lot of work then this attempt to mitigate the problem is OK (at least for now).

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

InputDispatcher.cpp has its own app switching logic which does a lot of interesting things, as there is something like a dedicated app switch key... Maybe we have to drop all key events when focused surface changes?

Revision history for this message
Alexandros Frantzis (afrantzis) wrote :

> I'd rather fix the "bad stuff happens" - but if that is a lot of work then this attempt to mitigate the problem is OK (at least for now).

> Maybe we have to drop all key events when focused surface changes?

I am looking more into the input subsystem to see if there is a clean and safe way we can handle the issue there.

Unmerged revisions

1445. By Alexandros Frantzis

client: Process input events even in clients that don't handle input

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'src/client/mir_surface.cpp'
2--- src/client/mir_surface.cpp 2014-02-10 09:07:48 +0000
3+++ src/client/mir_surface.cpp 2014-03-04 17:39:59 +0000
4@@ -213,6 +213,8 @@
5 accelerated_window = platform->create_egl_native_window(this);
6 }
7
8+ set_event_handler(nullptr);
9+
10 connection->on_surface_created(id(), this);
11 callback(this, context);
12 create_wait_handle.result_received();
13@@ -366,13 +368,18 @@
14 handle_event_callback = std::bind(delegate->callback, this,
15 std::placeholders::_1,
16 delegate->context);
17+ }
18+ else
19+ {
20+ handle_event_callback = [](MirEvent const*) {};
21+ }
22
23- if (surface.fd_size() > 0 && handle_event_callback)
24- {
25- input_thread = input_platform->create_input_thread(surface.fd(0),
26- handle_event_callback);
27- input_thread->start();
28- }
29+ if (surface.fd_size() > 0)
30+ {
31+ input_thread =
32+ input_platform->create_input_thread(surface.fd(0),
33+ handle_event_callback);
34+ input_thread->start();
35 }
36 }
37
38
39=== modified file 'tests/unit-tests/client/test_client_mir_surface.cpp'
40--- tests/unit-tests/client/test_client_mir_surface.cpp 2014-02-11 03:04:50 +0000
41+++ tests/unit-tests/client/test_client_mir_surface.cpp 2014-03-04 17:39:59 +0000
42@@ -248,9 +248,16 @@
43
44 struct StubClientInputPlatform : public mircv::InputPlatform
45 {
46- std::shared_ptr<mircv::InputReceiverThread> create_input_thread(int /* fd */, std::function<void(MirEvent*)> const& /* callback */)
47+ std::shared_ptr<mircv::InputReceiverThread> create_input_thread(
48+ int /* fd */, std::function<void(MirEvent*)> const& /* callback */)
49 {
50- return std::shared_ptr<mircv::InputReceiverThread>();
51+ struct NullInputReceiverThread : public mircv::InputReceiverThread
52+ {
53+ void start() override {}
54+ void stop() override {}
55+ void join() override {}
56+ };
57+ return std::make_shared<NullInputReceiverThread>();
58 }
59 };
60
61@@ -493,38 +500,106 @@
62 static void null_event_callback(MirSurface*, MirEvent const*, void*)
63 {
64 }
65+
66+class StubBuffer : public mcl::ClientBuffer
67+{
68+public:
69+ StubBuffer(geom::Size size, geom::Stride stride, MirPixelFormat pf)
70+ : size_{size}, stride_{stride}, pf_{pf}
71+ {
72+ }
73+
74+ std::shared_ptr<mcl::MemoryRegion> secure_for_cpu_write() override
75+ {
76+ auto raw = new mcl::MemoryRegion{size_.width,
77+ size_.height,
78+ stride_,
79+ pf_,
80+ nullptr};
81+
82+ return std::shared_ptr<mcl::MemoryRegion>(raw);
83+ }
84+
85+ geom::Size size() const override { return size_; }
86+ geom::Stride stride() const override { return stride_; }
87+ MirPixelFormat pixel_format() const override { return pf_; }
88+ uint32_t age() const override { return 0; }
89+ void increment_age() override {}
90+ void mark_as_submitted() override {}
91+
92+ std::shared_ptr<mg::NativeBuffer> native_buffer_handle() const override
93+ {
94+ return std::shared_ptr<mg::NativeBuffer>();
95+ }
96+
97+private:
98+ geom::Size const size_;
99+ geom::Stride const stride_;
100+ MirPixelFormat const pf_;
101+};
102+
103+struct StubClientBufferFactory : public mcl::ClientBufferFactory
104+{
105+ std::shared_ptr<mcl::ClientBuffer> create_buffer(
106+ std::shared_ptr<MirBufferPackage> const& package,
107+ geom::Size size, MirPixelFormat pf)
108+ {
109+ return std::make_shared<StubBuffer>(size,
110+ geom::Stride{package->stride},
111+ pf);
112+ }
113+};
114 }
115
116-TEST_F(MirClientSurfaceTest, input_fd_used_to_create_input_thread_when_delegate_specified)
117+TEST_F(MirClientSurfaceTest, creates_input_thread_at_construction)
118 {
119 using namespace ::testing;
120
121- auto mock_input_platform = std::make_shared<mt::MockClientInputPlatform>();
122- auto mock_input_thread = std::make_shared<NiceMock<mt::MockInputReceiverThread>>();
123- MirEventDelegate delegate = {null_event_callback, nullptr};
124-
125- EXPECT_CALL(*mock_buffer_factory, create_buffer(_,_,_)).Times(2);
126-
127- EXPECT_CALL(*mock_input_platform, create_input_thread(_, _)).Times(1)
128+ auto const mock_input_platform = std::make_shared<mt::MockClientInputPlatform>();
129+ auto const mock_input_thread = std::make_shared<NiceMock<mt::MockInputReceiverThread>>();
130+
131+ EXPECT_CALL(*mock_input_platform, create_input_thread(_, _))
132 .WillOnce(Return(mock_input_thread));
133 EXPECT_CALL(*mock_input_thread, start()).Times(1);
134 EXPECT_CALL(*mock_input_thread, stop()).Times(1);
135
136- {
137- auto surface = std::make_shared<MirSurface> (connection.get(), *client_comm_channel,
138- mock_buffer_factory, mock_input_platform, params, &empty_callback, nullptr);
139- auto wait_handle = surface->get_create_wait_handle();
140- wait_handle->wait_for_all();
141- surface->set_event_handler(&delegate);
142- }
143-
144- {
145- // This surface should not trigger a call to the input platform as no input delegate is specified.
146- auto surface = std::make_shared<MirSurface> (connection.get(), *client_comm_channel,
147- mock_buffer_factory, mock_input_platform, params, &empty_callback, nullptr);
148- auto wait_handle = surface->get_create_wait_handle();
149- wait_handle->wait_for_all();
150- }
151+ MirSurface mir_surface(connection.get(), *client_comm_channel,
152+ std::make_shared<StubClientBufferFactory>(),
153+ mock_input_platform, params, &empty_callback, nullptr);
154+
155+ mir_surface.get_create_wait_handle()->wait_for_all();
156+}
157+
158+TEST_F(MirClientSurfaceTest, recreates_input_thread_when_setting_event_handler)
159+{
160+ using namespace ::testing;
161+
162+ auto const mock_input_platform = std::make_shared<mt::MockClientInputPlatform>();
163+ auto const mock_input_thread1 = std::make_shared<NiceMock<mt::MockInputReceiverThread>>();
164+
165+ EXPECT_CALL(*mock_input_platform, create_input_thread(_, _))
166+ .WillOnce(Return(mock_input_thread1));
167+ EXPECT_CALL(*mock_input_thread1, start()).Times(1);
168+
169+ MirSurface mir_surface(connection.get(), *client_comm_channel,
170+ std::make_shared<StubClientBufferFactory>(),
171+ mock_input_platform, params, &empty_callback, nullptr);
172+
173+ mir_surface.get_create_wait_handle()->wait_for_all();
174+
175+ Mock::VerifyAndClearExpectations(mock_input_platform.get());
176+ Mock::VerifyAndClearExpectations(mock_input_thread1.get());
177+
178+ auto const mock_input_thread2 = std::make_shared<NiceMock<mt::MockInputReceiverThread>>();
179+
180+ EXPECT_CALL(*mock_input_thread1, stop()).Times(1);
181+ EXPECT_CALL(*mock_input_platform, create_input_thread(_, _))
182+ .WillOnce(Return(mock_input_thread2));
183+ EXPECT_CALL(*mock_input_thread2, start()).Times(1);
184+ EXPECT_CALL(*mock_input_thread2, stop()).Times(1);
185+
186+ MirEventDelegate const delegate = {null_event_callback, nullptr};
187+ mir_surface.set_event_handler(&delegate);
188 }
189
190 TEST_F(MirClientSurfaceTest, get_buffer_returns_last_received_buffer_package)
191@@ -633,57 +708,6 @@
192 surface->attrib(mir_surface_attrib_state));
193 }
194
195-namespace
196-{
197-
198-struct StubBuffer : public mcl::ClientBuffer
199-{
200- StubBuffer(geom::Size size, geom::Stride stride, MirPixelFormat pf)
201- : size_{size}, stride_{stride}, pf_{pf}
202- {
203- }
204-
205- std::shared_ptr<mcl::MemoryRegion> secure_for_cpu_write()
206- {
207- auto raw = new mcl::MemoryRegion{size_.width,
208- size_.height,
209- stride_,
210- pf_,
211- nullptr};
212-
213- return std::shared_ptr<mcl::MemoryRegion>(raw);
214- }
215-
216- geom::Size size() const { return size_; }
217- geom::Stride stride() const { return stride_; }
218- MirPixelFormat pixel_format() const { return pf_; }
219- uint32_t age() const { return 0; }
220- void increment_age() {}
221- void mark_as_submitted() {}
222-
223- std::shared_ptr<mg::NativeBuffer> native_buffer_handle() const
224- {
225- return std::shared_ptr<mg::NativeBuffer>();
226- }
227-
228- geom::Size size_;
229- geom::Stride stride_;
230- MirPixelFormat pf_;
231-};
232-
233-struct StubClientBufferFactory : public mcl::ClientBufferFactory
234-{
235- std::shared_ptr<mcl::ClientBuffer> create_buffer(
236- std::shared_ptr<MirBufferPackage> const& package,
237- geom::Size size, MirPixelFormat pf)
238- {
239- return std::make_shared<StubBuffer>(size,
240- geom::Stride{package->stride},
241- pf);
242- }
243-};
244-
245-}
246 TEST_F(MirClientSurfaceTest, get_cpu_region_returns_correct_data)
247 {
248 using namespace testing;

Subscribers

People subscribed via source and target branches