Mir

Merge lp:~vanvugt/mir/surface-states into lp:~mir-team/mir/trunk

Proposed by Daniel van Vugt
Status: Superseded
Proposed branch: lp:~vanvugt/mir/surface-states
Merge into: lp:~mir-team/mir/trunk
Diff against target: 1787 lines (+956/-50)
45 files modified
include/client/mir_toolkit/mir_client_library.h (+16/-0)
include/server/mir/event_queue.h (+40/-0)
include/server/mir/frontend/protobuf_ipc_factory.h (+2/-5)
include/server/mir/frontend/server.h (+45/-0)
include/server/mir/frontend/session.h (+3/-0)
include/server/mir/frontend/session_mediator.h (+2/-1)
include/server/mir/shell/application_session.h (+5/-0)
include/server/mir/shell/surface.h (+14/-0)
include/shared/mir/event_sink.h (+39/-0)
include/shared/mir_toolkit/common.h (+27/-1)
include/shared/mir_toolkit/event.h (+16/-4)
include/test/mir_test/stub_server_tool.h (+2/-2)
include/test/mir_test/test_protobuf_server.h (+1/-1)
include/test/mir_test_doubles/mock_session.h (+2/-0)
include/test/mir_test_doubles/stub_ipc_factory.h (+3/-3)
include/test/mir_test_doubles/stub_session.h (+3/-0)
src/client/mir_client_library.cpp (+39/-9)
src/client/mir_connection.cpp (+44/-1)
src/client/mir_connection.h (+11/-2)
src/client/mir_socket_rpc_channel.cpp (+68/-3)
src/client/mir_socket_rpc_channel.h (+6/-0)
src/client/mir_surface.cpp (+22/-5)
src/client/mir_surface.h (+1/-0)
src/server/CMakeLists.txt (+3/-0)
src/server/default_server_configuration.cpp (+1/-1)
src/server/event_queue.cpp (+37/-0)
src/server/frontend/CMakeLists.txt (+1/-1)
src/server/frontend/protobuf_message_processor.cpp (+27/-0)
src/server/frontend/protobuf_message_processor.h (+7/-1)
src/server/frontend/protobuf_socket_communicator.cpp (+9/-4)
src/server/frontend/server.cpp (+30/-0)
src/server/frontend/session_mediator.cpp (+1/-0)
src/server/shell/application_session.cpp (+11/-0)
src/server/shell/surface.cpp (+63/-1)
src/shared/protobuf/mir_protobuf.proto (+8/-0)
src/shared/protobuf/mir_protobuf_wire.proto (+2/-1)
tests/acceptance-tests/test_client_library.cpp (+166/-0)
tests/integration-tests/test_error_reporting.cpp (+2/-1)
tests/mir_test_doubles/test_protobuf_socket_server.cpp (+1/-1)
tests/unit-tests/CMakeLists.txt (+1/-1)
tests/unit-tests/client/test_client_mir_surface.cpp (+19/-1)
tests/unit-tests/event/CMakeLists.txt (+5/-0)
tests/unit-tests/event/test_event_queue.cpp (+115/-0)
tests/unit-tests/shell/test_single_visibility_focus_mechanism.cpp (+1/-0)
tests/unit-tests/shell/test_surface.cpp (+35/-0)
To merge this branch: bzr merge lp:~vanvugt/mir/surface-states
Reviewer Review Type Date Requested Status
PS Jenkins bot (community) continuous-integration Approve
Alan Griffiths Needs Fixing
Robert Ancell Approve
Alexandros Frantzis (community) Needs Information
Review via email: mp+158289@code.launchpad.net

This proposal has been superseded by a proposal from 2013-04-18.

Commit message

Add support for surface states.

Actually, most of this branch is about adding generic support for events. And
changes to the state of a surface are one good example of an event originating
on the server that the client needs to be told about. This is because most
state changes will come from the shell/user. Not the client. We use events
to notify the client of such changes.

To post a comment you must log in.
Revision history for this message
Daniel van Vugt (vanvugt) wrote :

This will conflict slightly with the unique-id branch. I will fix that if and when it becomes a problem. But unique-id is not a prerequisite for this.

Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Approve (continuous-integration)
Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Approve (continuous-integration)
Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Approve (continuous-integration)
Revision history for this message
Alexandros Frantzis (afrantzis) wrote :

I don't see the utility of EventQueue as thing stand currently. I think a msh::Surface only needs an EventSink (i.e. an event handler). If we need an EventQueue in the future, it should be derived from EventSink. The surface doesn't need to know anything about the details of how its events are handled.

Also, I would argue that since the id and the event handler are not really optional for the correct functioning of mir, nor there is any need to reset them, they should be taken at construction time:

msh::Surface(..., id, event_sink, ...)

msh::SurfaceFactory::create_surface(params, id/id_generator, EventSink)

908 + // A dynamic cast is not ideal. But the alternatives all seem to require
909 + // non-trivial architectural changes. Another day...

I think these changes should be discussed in this MP, or alternatively we can split out the communications part to a different MP, leaving this with only the implementation of the internal event propagation and handling mechanisms on the server and client side.

Marking "Needs Information" => "Needs discussion"

review: Needs Information
Revision history for this message
Kevin DuBois (kdub) wrote :

> I don't see the utility of EventQueue as thing stand currently. I think a
> msh::Surface only needs an EventSink (i.e. an event handler). If we need an
> EventQueue in the future, it should be derived from EventSink. The surface
> doesn't need to know anything about the details of how its events are handled.
>
> Also, I would argue that since the id and the event handler are not really
> optional for the correct functioning of mir, nor there is any need to reset
> them, they should be taken at construction time:
>
> msh::Surface(..., id, event_sink, ...)
>
> msh::SurfaceFactory::create_surface(params, id/id_generator, EventSink)
that makes sense to me too.

1399 +TEST(EventQueue, events_get_handled)
we test that the same thing (but with different arguments) happens 3 times. We could test that one event gets handled and cover the same bit of code. (same sort of thing with ShellSurface.states)

The acceptance test names don't really capture what they're doing.
Its good to see a new acceptance test though :) I've been meaning to complain that the #of unit tests are expanding a lot faster than our integration/acceptance.

Revision history for this message
Daniel van Vugt (vanvugt) wrote :

Alexandros:
It might be possible to merge EventQueue into EventSink. Although I would be inclined not to because:
  1. If EventQueue adds threading and background queuing in future then I don't want to have to touch any event sources. So the extra abstraction is useful.
  2. If EventQueue adds support for multiple simultaneous sinks (e.g. in-server, logging, or none like you can now), then you need the EventQueue class. You can't let your event sources know how many, if any, sinks there are.
  3. The class relationships might make it very difficult to implement that way. Not sure.

Although, if we're talking about unused functionality then the "EventSequence" message is a better example. It's presently always length 1. :)

I did think about passing things in to constructors, but decided against it in the name of:
  simplicity - no long constructor arg lists to steepen the learning curve and trigger changes to lots of test code; and
  flexibility - it's more clear this way that id and event_sink are optional things, and that the Surface class does not require them to remain fixed or be present at all.

Kevin:
Testing something three times is a good thing. It means you're verifying that the class does not stop functioning for some reason after the first use (a common programming mistake). That's a very useful test for event queuing.

Acceptance test names; I disagree they always need to be verbose. Sometimes if you're writing a test that covers _all_ the functionality of some minor feature (surface_states) then it makes sense to name the test after that feature. But I've now renamed the acceptance tests to make them more verbose. I know other people like it that way too.

Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Approve (continuous-integration)
Revision history for this message
Alan Griffiths (alan-griffiths) wrote :

267 + * MirSurfaceState: "minimized" or "minimised"?
268 + * A review of 7 leading dictionaries shows "z" is the most common spelling
269 + * (listed in all dictionaries), and "s" only appears in 4. And those that list
270 + * both "z" and "s" usually point out that "z" is the main spelling. Only 2
271 + * dictionaries say that "s" is preferred, and even still all dictionaries seem
272 + * to agree that "s" is only used in the UK.
273 + * The same is true for "maximized" vs "maximised".

All very interesting - but do we need it? Surely Canonical has decided which dialect of English to use?

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

719 + assert(configure_result.has_error());

I'm not keen on assert() - which has different behavior according to the build options.

In any case, if we check there is an error shouldn't we be doing something with it? (Write it to log->debug() perhaps?)

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

363 + // Allocate a non-zero unique ID. We reserve zero for special use.

What special use? Oh, I see elsewhere:

591 + if (result.id() == 0) // It's an event sequence

This seems like unnecessary coupling to me. There wouldn't be a need for this special case handling in "on the wire" code if the client library made blocking "get events" calls to the server (these would simply complete like any other call when the server provides some events).

~~~~

I agree with Alexandros that the relationship between EventQueue and EventSink seems wrong. They are both classes that handle/post events which is commonality that I'd expect to see in the design. E.g.

class EventHandler ...

class EventSink : public EventHandler ...

class EventQueue : public EventHandler ...

As Alexandros says a surface only needs an EventHandler - it doesn't need to know if it is a queue or a sink.

review: Needs Information
Revision history for this message
Robert Ancell (robert-ancell) wrote :

> 267 + * MirSurfaceState: "minimized" or "minimised"?
>
> All very interesting - but do we need it? Surely Canonical has decided which
> dialect of English to use?

Yeah, this is a bit over the top - we should only support one set of names, and the convention would be to use the US dialect - minimized.

Revision history for this message
Robert Ancell (robert-ancell) wrote :

Approving on the grounds this is an improvement on what we currently have. I can imagine some refactoring could be done in the future, but the client API seems appropriate and this can be done transparently.

review: Approve
Revision history for this message
Daniel van Vugt (vanvugt) wrote :

"All very interesting - but do we need it? Surely Canonical has decided which dialect of English to use?"

Actually no one has decided. Our code like most APIs uses a lot of American spelling ("color"). And our design docs use the (rare) UK spelling of minimise/maximise. So the confusion already exists.

AFAIK, most existing toolkits use American spelling for minimize/maximize. And those forms are valid to UK English too. So if you had to choose one then choose "z" (despite conflicting with the design docs). Otherwise, I recommend keeping both forms. It does not hurt to keep both.

Revision history for this message
Daniel van Vugt (vanvugt) wrote :

> Asserts...
Yeah there is only one assert. It is correct and it is harmless. It might not be compiled in for release builds and that's fine. Because correct behaviour in that region of code is to ignore error responses (for now). I could happily remove the assert completely if it's bothersome.

> "There wouldn't be a need for this special case handling in "on the wire" code if the client library made blocking "get events" calls to the server (these would simply complete like any other call when the server provides some events)."
I thought exactly the same in the beginning, wanting to reuse the query/response model. However I then realized that the client-side MirSurface needs to receive and handle events regardless of whether the client app is interested in events. This is so the client API can have up-to-date state information cached for each surface, and almost never needs to query the server when mir_surface_get_state is called. No blocking, no round-trips, and no requirement than an event-handling client app needs to be threaded. And of course, this approach will conceivably be useful for other things later.

A second reason for asynchronous event delivery is that it makes little sense to impose a new restriction; that your client has to be designed to block and wait for the next event, if you can generalize with a non-blocking approach. The blocking approach can still be emulated by adding new client API functions later if desired.

Revision history for this message
Daniel van Vugt (vanvugt) wrote :

> 363 + // Allocate a non-zero unique ID. We reserve zero for special use.
> What special use? Oh, I see elsewhere:
> 591 + if (result.id() == 0) // It's an event sequence

This is related to the unique-id branch, which is a nicer approach.

Revision history for this message
Daniel van Vugt (vanvugt) wrote :

Decoupled shell::Surface from a concrete EventQueue. Now shell::Surface only gets and uses the EventSink interface.

Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Approve (continuous-integration)
Revision history for this message
Alan Griffiths (alan-griffiths) wrote :

> > "There wouldn't be a need for this special case handling in "on the wire"
> code if the client library made blocking "get events" calls to the server
> (these would simply complete like any other call when the server provides some
> events)."
> I thought exactly the same in the beginning, wanting to reuse the
> query/response model. However I then realized that the client-side MirSurface
> needs to receive and handle events regardless of whether the client app is
> interested in events. This is so the client API can have up-to-date state
> information cached for each surface, and almost never needs to query the
> server when mir_surface_get_state is called. No blocking, no round-trips, and
> no requirement than an event-handling client app needs to be threaded. And of
> course, this approach will conceivably be useful for other things later.
>
> A second reason for asynchronous event delivery is that it makes little sense
> to impose a new restriction; that your client has to be designed to block and
> wait for the next event, if you can generalize with a non-blocking approach.
> The blocking approach can still be emulated by adding new client API functions
> later if desired.

To clarify I meant the calls could originate *in* the library, not through the API. (Still thinking about whether the proposed approach is good enough - will update vote shortly.)

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

237 +class EventSink
238 +{
239 +public:
240 + virtual ~EventSink() {}
241 + virtual void handle_event(MirEvent const& e) = 0;
242 +};

Destructor should be nothrow. CopyAssign should be disabled.

~~~~

state_value is effectively atomic on Intel, but as it is can be accessed by different threads we should make that explicit with "std::atomic<MirSurfaceState> state_value;" etc.

review: Needs Fixing
Revision history for this message
Daniel van Vugt (vanvugt) wrote :

Mir's unclear use of threads scares me. If state_value needs to be atomic then so does type_value. That's an existing problem which pre-dates this proposal. If it is a problem at all...

Please document Mir's thread usage and which classes may be accessed concurrently.

I'm not going to fix the atomicity of state_value while adjacent members are not atomic, and I'm not entirely sure or convinced that concurrent access is required yet.

Revision history for this message
Daniel van Vugt (vanvugt) wrote :

Fixed EventSink destructor and assignment.

Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Approve (continuous-integration)
Revision history for this message
Alan Griffiths (alan-griffiths) wrote :

612 + if (event_handler)
613 + {
...
647 + } // else ignore incoming events

I think that this is rather a big chunk of logic to be buried in the middle of MirSocketRpcChannel::read_message() - I'd be happier to see a separate handle_event() member function. (I'm still not totally convinced that MirSocketRpcChannel is the right place for this logic but PendingCallCache doesn't seem any better - although a rename might fix that.)

Actually, if we're going to stick event handling into the "wire" decoding logic I'd rather be changing wire::Result to have an optional event field instead of have a special value in the id field and reusing the response field.

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

> I'm not going to fix the atomicity of state_value while adjacent members are
> not atomic, and I'm not entirely sure or convinced that concurrent access is
> required yet.

OK as state() isn't called yet I guess concurrent access isn't needed yet.

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

88 + virtual void set_event_sink(std::weak_ptr<EventSink> const&) {}

Session is an interface, it should only have pure virtual functions.

~~~~

The chain of set_event_sink() calls feels a bit odd - they fight against the system architecture. Especially:

SessionMediator is just an implementation of the mir::protobuf::DisplayServer interface. But with this MP it introduces an additional public set_event_sink() member function to the interface. This is a bad idea as client code (which, correctly, uses the interface and has no access to the function). This leads to:

927 + // A dynamic cast is not ideal. But the alternatives all seem to require
928 + // non-trivial architectural changes. Another day...
929 + mf::SessionMediator *sm = dynamic_cast<mf::SessionMediator*>(ds.get());
930 + if (sm)
931 + sm->set_event_sink(proc);

Which we can all agree is horrid. (Especially the silently different behavior when another type is supplied.)

~~~~

I can understand that adding an EventSink interface to ProtobufMessageProcessor minimizes the size of the diff, but it dilutes the purpose of the class. It would be better to separate out a ProtobufResponseSender and use that both in the ProtobufMessageProcessor and in a new ProtobufEventSink.

review: Needs Fixing
Revision history for this message
Daniel van Vugt (vanvugt) wrote :

Alan,

612 + if (event_handler)
I thought of moving it too. But I prefer the cohesion of having code in the one and only place where it is used. And it's easier for the reader to comprehend when there is one fewer function name to look up and find. Should that logic ever be reused then definitely separate it, sure. It's not like that block of code is too big to fit on anyone's screen.

My first attempt (weeks ago) did work on the wire protocol as you say, and did not piggyback on Result. However there was a major problem with that -- Protobuf cannot tell you what kind of message you are receiving before you try to decode it. You just construct an object of every possible message type and try to parse the data for it. Half the time you will get an error (wrong message type) and waste CPU time in doing so. The simple and fast solution is to guarantee that incoming messages are of a type that you know in advance (Result). So you can parse them correctly first time. Protobuf won't log any errors and you don't waste CPU time.

The set_event_sink/casting stuff was a big challenge. I spent days (weeks actually) figuring out the class relationships and how to make them talk in a way that makes sense. Reworking it all could take days for me. Maybe simpler for you since you understand these classes. So I would appreciate merges proposed if you'd like it structured differently. I am rapidly running out of time and won't be around to do major reworks until June.

Revision history for this message
Daniel van Vugt (vanvugt) wrote :

Re:
    virtual void set_event_sink(std::weak_ptr<EventSink> const&) {}

I know it's an interface so usually consists of pure virtual methods. But there's already a concrete inline destructor:
    virtual ~Session() {}

It's not that much different.

Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Needs Fixing (continuous-integration)
Revision history for this message
Daniel van Vugt (vanvugt) wrote :

Fixed most of Alan's concerns. Except the dynamic_cast. I will look into it again over the next few days.

Revision history for this message
Daniel van Vugt (vanvugt) wrote :

Dear Jenkins,

Rebuild!

Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Approve (continuous-integration)

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'include/client/mir_toolkit/mir_client_library.h'
2--- include/client/mir_toolkit/mir_client_library.h 2013-04-17 07:31:40 +0000
3+++ include/client/mir_toolkit/mir_client_library.h 2013-04-18 07:33:25 +0000
4@@ -265,6 +265,22 @@
5 */
6 MirSurfaceType mir_surface_get_type(MirSurface *surface);
7
8+/**
9+ * Change the state of a surface.
10+ * \param [in] surface The surface to operate on
11+ * \param [in] state The new state of the surface
12+ * \return A wait handle that can be passed to mir_wait_for
13+ */
14+MirWaitHandle* mir_surface_set_state(MirSurface *surface,
15+ MirSurfaceState state);
16+
17+/**
18+ * Get the current state of a surface.
19+ * \param [in] surface The surface to query
20+ * \return The state of the surface
21+ */
22+MirSurfaceState mir_surface_get_state(MirSurface *surface);
23+
24 #ifdef __cplusplus
25 }
26 /**@}*/
27
28=== added file 'include/server/mir/event_queue.h'
29--- include/server/mir/event_queue.h 1970-01-01 00:00:00 +0000
30+++ include/server/mir/event_queue.h 2013-04-18 07:33:25 +0000
31@@ -0,0 +1,40 @@
32+/*
33+ * Copyright © 2013 Canonical Ltd.
34+ *
35+ * This program is free software: you can redistribute it and/or modify
36+ * it under the terms of the GNU General Public License version 3 as
37+ * published by the Free Software Foundation.
38+ *
39+ * This program is distributed in the hope that it will be useful,
40+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
41+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
42+ * GNU General Public License for more details.
43+ *
44+ * You should have received a copy of the GNU General Public License
45+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
46+ *
47+ * Authored by: Daniel van Vugt <daniel.van.vugt@canonical.com>
48+ */
49+
50+#ifndef MIR_EVENT_QUEUE_H_
51+#define MIR_EVENT_QUEUE_H_
52+
53+#include "mir_toolkit/event.h"
54+#include "mir/event_sink.h"
55+#include <memory>
56+
57+namespace mir
58+{
59+class EventQueue : public EventSink
60+{
61+public:
62+ void set_target(std::weak_ptr<EventSink> const& s);
63+ void handle_event(MirEvent const& e) override;
64+
65+private:
66+ std::weak_ptr<EventSink> target;
67+};
68+
69+} // namespace mir
70+
71+#endif
72
73=== modified file 'include/server/mir/frontend/protobuf_ipc_factory.h'
74--- include/server/mir/frontend/protobuf_ipc_factory.h 2013-04-16 09:08:29 +0000
75+++ include/server/mir/frontend/protobuf_ipc_factory.h 2013-04-18 07:33:25 +0000
76@@ -23,19 +23,16 @@
77
78 namespace mir
79 {
80-namespace protobuf
81-{
82-class DisplayServer;
83-}
84 namespace frontend
85 {
86+class Server;
87 class ResourceCache;
88 class MessageProcessorReport;
89
90 class ProtobufIpcFactory
91 {
92 public:
93- virtual std::shared_ptr<protobuf::DisplayServer> make_ipc_server() = 0;
94+ virtual std::shared_ptr<Server> make_ipc_server() = 0;
95 virtual std::shared_ptr<ResourceCache> resource_cache() = 0;
96 virtual std::shared_ptr<MessageProcessorReport> report() = 0;
97
98
99=== added file 'include/server/mir/frontend/server.h'
100--- include/server/mir/frontend/server.h 1970-01-01 00:00:00 +0000
101+++ include/server/mir/frontend/server.h 2013-04-18 07:33:25 +0000
102@@ -0,0 +1,45 @@
103+/*
104+ * Copyright © 2013 Canonical Ltd.
105+ *
106+ * This program is free software: you can redistribute it and/or modify it
107+ * under the terms of the GNU General Public License version 3,
108+ * as published by the Free Software Foundation.
109+ *
110+ * This program is distributed in the hope that it will be useful,
111+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
112+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
113+ * GNU General Public License for more details.
114+ *
115+ * You should have received a copy of the GNU General Public License
116+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
117+ *
118+ * Authored by: Daniel van Vugt <daniel.van.vugt@canonical.com>
119+ */
120+
121+#ifndef MIR_FRONTEND_SERVER_H_
122+#define MIR_FRONTEND_SERVER_H_
123+
124+#include "mir_protobuf.pb.h"
125+#include <memory>
126+
127+namespace mir
128+{
129+class EventSink;
130+
131+namespace frontend
132+{
133+
134+class Server : public mir::protobuf::DisplayServer
135+{
136+public:
137+ virtual ~Server() noexcept;
138+ void set_event_sink(std::weak_ptr<EventSink> const& sink);
139+
140+protected:
141+ std::weak_ptr<EventSink> event_sink;
142+};
143+
144+} // namespace frontent
145+} // namespace mir
146+
147+#endif
148
149=== modified file 'include/server/mir/frontend/session.h'
150--- include/server/mir/frontend/session.h 2013-04-16 09:08:29 +0000
151+++ include/server/mir/frontend/session.h 2013-04-18 07:33:25 +0000
152@@ -29,6 +29,7 @@
153
154 namespace mir
155 {
156+class EventSink;
157
158 namespace frontend
159 {
160@@ -52,6 +53,8 @@
161
162 virtual int configure_surface(SurfaceId id, MirSurfaceAttrib attrib, int value) = 0;
163
164+ virtual void set_event_sink(std::weak_ptr<EventSink> const&) = 0;
165+
166 protected:
167 Session() = default;
168 Session(Session const&) = delete;
169
170=== modified file 'include/server/mir/frontend/session_mediator.h'
171--- include/server/mir/frontend/session_mediator.h 2013-04-16 09:08:29 +0000
172+++ include/server/mir/frontend/session_mediator.h 2013-04-18 07:33:25 +0000
173@@ -21,6 +21,7 @@
174 #define MIR_FRONTEND_SESSION_MEDIATOR_H_
175
176 #include "mir_protobuf.pb.h"
177+#include "mir/frontend/server.h"
178
179 #include <map>
180 #include <memory>
181@@ -50,7 +51,7 @@
182 class ClientBufferTracker;
183
184 // SessionMediator relays requests from the client process into the server.
185-class SessionMediator : public mir::protobuf::DisplayServer
186+class SessionMediator : public mir::frontend::Server
187 {
188 public:
189
190
191=== modified file 'include/server/mir/shell/application_session.h'
192--- include/server/mir/shell/application_session.h 2013-04-16 09:08:29 +0000
193+++ include/server/mir/shell/application_session.h 2013-04-18 07:33:25 +0000
194@@ -20,11 +20,13 @@
195 #define MIR_SHELL_APPLICATION_SESSION_H_
196
197 #include "mir/shell/session.h"
198+#include "mir/event_queue.h"
199
200 #include <map>
201
202 namespace mir
203 {
204+class EventSink;
205
206 namespace shell
207 {
208@@ -52,12 +54,15 @@
209
210 int configure_surface(frontend::SurfaceId id, MirSurfaceAttrib attrib, int value);
211
212+ void set_event_sink(std::weak_ptr<mir::EventSink> const& sink);
213+
214 protected:
215 ApplicationSession(ApplicationSession const&) = delete;
216 ApplicationSession& operator=(ApplicationSession const&) = delete;
217
218 private:
219 std::shared_ptr<SurfaceFactory> const surface_factory;
220+ std::shared_ptr<EventQueue> event_queue;
221 std::string const session_name;
222
223 frontend::SurfaceId next_id();
224
225=== modified file 'include/server/mir/shell/surface.h'
226--- include/server/mir/shell/surface.h 2013-04-16 09:08:29 +0000
227+++ include/server/mir/shell/surface.h 2013-04-18 07:33:25 +0000
228@@ -21,6 +21,7 @@
229 #define MIR_SHELL_SURFACE_H_
230
231 #include "mir/frontend/surface.h"
232+#include "mir/frontend/surface_id.h"
233 #include "mir/surfaces/surface.h"
234 #include "mir/input/surface_target.h"
235
236@@ -30,6 +31,9 @@
237
238 namespace mir
239 {
240+
241+class EventSink;
242+
243 namespace frontend
244 {
245 struct SurfaceCreationParameters;
246@@ -76,15 +80,25 @@
247
248 virtual int configure(MirSurfaceAttrib attrib, int value);
249 virtual MirSurfaceType type() const;
250+ virtual MirSurfaceState state() const;
251+
252+ void set_id(frontend::SurfaceId i);
253+ void set_event_target(std::shared_ptr<EventSink> const& sink);
254
255 private:
256 bool set_type(MirSurfaceType t); // Use configure() to make public changes
257+ bool set_state(MirSurfaceState s);
258+ void notify_change(MirSurfaceAttrib attrib, int value);
259
260 std::shared_ptr<SurfaceBuilder> const builder;
261 std::shared_ptr<mir::input::InputChannel> const input_channel;
262 std::weak_ptr<mir::surfaces::Surface> const surface;
263
264+ std::shared_ptr<EventSink> event_sink;
265+ frontend::SurfaceId id;
266+
267 MirSurfaceType type_value;
268+ MirSurfaceState state_value;
269 };
270 }
271 }
272
273=== added file 'include/shared/mir/event_sink.h'
274--- include/shared/mir/event_sink.h 1970-01-01 00:00:00 +0000
275+++ include/shared/mir/event_sink.h 2013-04-18 07:33:25 +0000
276@@ -0,0 +1,39 @@
277+/*
278+ * Copyright © 2013 Canonical Ltd.
279+ *
280+ * This program is free software: you can redistribute it and/or modify
281+ * it under the terms of the GNU Lesser General Public License version 3 as
282+ * published by the Free Software Foundation.
283+ *
284+ * This program is distributed in the hope that it will be useful,
285+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
286+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
287+ * GNU Lesser General Public License for more details.
288+ *
289+ * You should have received a copy of the GNU Lesser General Public License
290+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
291+ *
292+ * Authored by: Daniel van Vugt <daniel.van.vugt@canonical.com>
293+ */
294+
295+#ifndef MIR_EVENT_SINK_H_
296+#define MIR_EVENT_SINK_H_
297+
298+#include "mir_toolkit/event.h"
299+
300+namespace mir
301+{
302+
303+class EventSink
304+{
305+public:
306+ virtual ~EventSink() noexcept {}
307+ virtual void handle_event(MirEvent const& e) = 0;
308+
309+ EventSink& operator=(EventSink const&) = delete;
310+};
311+
312+} // namespace mir
313+
314+#endif
315+
316
317=== modified file 'include/shared/mir_toolkit/common.h'
318--- include/shared/mir_toolkit/common.h 2013-03-29 11:39:30 +0000
319+++ include/shared/mir_toolkit/common.h 2013-04-18 07:33:25 +0000
320@@ -34,6 +34,7 @@
321 typedef enum MirSurfaceAttrib
322 {
323 mir_surface_attrib_type,
324+ mir_surface_attrib_state,
325 mir_surface_attrib_arraysize_
326 } MirSurfaceAttrib;
327
328@@ -48,7 +49,32 @@
329 mir_surface_type_arraysize_
330 } MirSurfaceType;
331
332-/* TODO: Surface states here */
333+/*
334+ * MirSurfaceState: "minimized" or "minimised"?
335+ * A review of 7 leading dictionaries shows "z" is the most common spelling
336+ * (listed in all dictionaries), and "s" only appears in 4. And those that list
337+ * both "z" and "s" usually point out that "z" is the main spelling. Only 2
338+ * dictionaries say that "s" is preferred, and even still all dictionaries seem
339+ * to agree that "s" is only used in the UK.
340+ * The same is true for "maximized" vs "maximised".
341+ */
342+typedef enum MirSurfaceState
343+{
344+ mir_surface_state_unknown,
345+ mir_surface_state_restored,
346+ mir_surface_state_minimized,
347+ mir_surface_state_minimised = mir_surface_state_minimized,
348+ mir_surface_state_maximized,
349+ mir_surface_state_maximised = mir_surface_state_maximized,
350+ mir_surface_state_vertmaximized,
351+ mir_surface_state_vertmaximised = mir_surface_state_vertmaximized,
352+ /* mir_surface_state_semimaximized,
353+ Omitted for now, since it's functionally a subset of vertmaximized and
354+ differs only in the X coordinate. */
355+ mir_surface_state_fullscreen,
356+ mir_surface_state_arraysize_
357+} MirSurfaceState;
358+
359 /**@}*/
360
361 #endif
362
363=== modified file 'include/shared/mir_toolkit/event.h'
364--- include/shared/mir_toolkit/event.h 2013-04-11 22:47:09 +0000
365+++ include/shared/mir_toolkit/event.h 2013-04-18 07:33:25 +0000
366@@ -21,6 +21,7 @@
367
368 #include <stddef.h>
369 #include <stdint.h>
370+#include "mir_toolkit/common.h"
371
372 #ifdef __cplusplus
373 /**
374@@ -37,7 +38,8 @@
375 typedef enum
376 {
377 mir_event_type_key,
378- mir_event_type_motion
379+ mir_event_type_motion,
380+ mir_event_type_surface
381 } MirEventType;
382
383 typedef struct
384@@ -91,11 +93,21 @@
385 } pointer_coordinates[MIR_INPUT_EVENT_MAX_POINTER_COUNT];
386 } MirMotionEvent;
387
388+typedef struct
389+{
390+ MirEventType type;
391+
392+ int id;
393+ MirSurfaceAttrib attrib;
394+ int value;
395+} MirSurfaceEvent;
396+
397 typedef union
398 {
399- MirEventType type;
400- MirKeyEvent key;
401- MirMotionEvent motion;
402+ MirEventType type;
403+ MirKeyEvent key;
404+ MirMotionEvent motion;
405+ MirSurfaceEvent surface;
406 } MirEvent;
407
408 #ifdef __cplusplus
409
410=== modified file 'include/test/mir_test/stub_server_tool.h'
411--- include/test/mir_test/stub_server_tool.h 2013-04-16 09:08:29 +0000
412+++ include/test/mir_test/stub_server_tool.h 2013-04-18 07:33:25 +0000
413@@ -20,7 +20,7 @@
414 #ifndef MIR_TEST_STUB_SERVER_TOOL_H_
415 #define MIR_TEST_STUB_SERVER_TOOL_H_
416
417-#include "mir_protobuf.pb.h"
418+#include "mir/frontend/server.h"
419 #include <condition_variable>
420 #include <mutex>
421
422@@ -29,7 +29,7 @@
423 namespace test
424 {
425
426-struct StubServerTool : mir::protobuf::DisplayServer
427+struct StubServerTool : frontend::Server
428 {
429 StubServerTool()
430 : drm_magic{0}
431
432=== modified file 'include/test/mir_test/test_protobuf_server.h'
433--- include/test/mir_test/test_protobuf_server.h 2013-04-16 09:08:29 +0000
434+++ include/test/mir_test/test_protobuf_server.h 2013-04-18 07:33:25 +0000
435@@ -31,7 +31,7 @@
436 struct TestProtobufServer
437 {
438 TestProtobufServer(std::string socket_name,
439- const std::shared_ptr<protobuf::DisplayServer>& tool);
440+ const std::shared_ptr<frontend::Server>& tool);
441
442 std::shared_ptr<frontend::Communicator> make_communicator(
443 const std::string& socket_name,
444
445=== modified file 'include/test/mir_test_doubles/mock_session.h'
446--- include/test/mir_test_doubles/mock_session.h 2013-04-16 09:08:29 +0000
447+++ include/test/mir_test_doubles/mock_session.h 2013-04-18 07:33:25 +0000
448@@ -43,6 +43,8 @@
449 MOCK_METHOD0(show, void());
450
451 MOCK_METHOD3(configure_surface, int(frontend::SurfaceId, MirSurfaceAttrib, int));
452+
453+ MOCK_METHOD1(set_event_sink, void(std::weak_ptr<mir::EventSink> const&));
454 };
455
456 }
457
458=== modified file 'include/test/mir_test_doubles/stub_ipc_factory.h'
459--- include/test/mir_test_doubles/stub_ipc_factory.h 2013-04-16 09:08:29 +0000
460+++ include/test/mir_test_doubles/stub_ipc_factory.h 2013-04-18 07:33:25 +0000
461@@ -35,13 +35,13 @@
462 class StubIpcFactory : public frontend::ProtobufIpcFactory
463 {
464 public:
465- StubIpcFactory(protobuf::DisplayServer& server) :
466+ StubIpcFactory(frontend::Server& server) :
467 server(fake_shared(server)),
468 cache(std::make_shared<frontend::ResourceCache>())
469 {
470 }
471
472- std::shared_ptr<protobuf::DisplayServer> make_ipc_server()
473+ std::shared_ptr<frontend::Server> make_ipc_server()
474 {
475 return server;
476 }
477@@ -57,7 +57,7 @@
478 return std::make_shared<frontend::NullMessageProcessorReport>();
479 }
480
481- std::shared_ptr<protobuf::DisplayServer> server;
482+ std::shared_ptr<frontend::Server> server;
483 std::shared_ptr<frontend::ResourceCache> const cache;
484 };
485
486
487=== modified file 'include/test/mir_test_doubles/stub_session.h'
488--- include/test/mir_test_doubles/stub_session.h 2013-04-16 09:08:29 +0000
489+++ include/test/mir_test_doubles/stub_session.h 2013-04-18 07:33:25 +0000
490@@ -58,6 +58,9 @@
491 {
492 return 0;
493 }
494+ void set_event_sink(std::weak_ptr<EventSink> const&)
495+ {
496+ }
497 };
498
499 }
500
501=== modified file 'src/client/mir_client_library.cpp'
502--- src/client/mir_client_library.cpp 2013-04-17 07:31:40 +0000
503+++ src/client/mir_client_library.cpp 2013-04-18 07:33:25 +0000
504@@ -25,7 +25,7 @@
505 #include "native_client_platform_factory.h"
506 #include "egl_native_display_container.h"
507 #include "mir_logger.h"
508-#include "make_rpc_channel.h"
509+#include "mir_socket_rpc_channel.h"
510
511 #include <set>
512 #include <unordered_set>
513@@ -57,10 +57,12 @@
514 auto log = std::make_shared<mcl::ConsoleLogger>();
515 auto client_platform_factory = std::make_shared<mcl::NativeClientPlatformFactory>();
516
517- MirConnection* connection = new MirConnection(
518- mcl::make_rpc_channel(socket_file, log),
519- log,
520- client_platform_factory);
521+ auto rpc = std::make_shared<mcl::MirSocketRpcChannel>(socket_file, log);
522+
523+ MirConnection* connection = new MirConnection(rpc, log,
524+ client_platform_factory);
525+
526+ rpc->set_event_handler(connection);
527
528 return connection->connect(name, callback, context);
529 }
530@@ -253,10 +255,12 @@
531 auto log = std::make_shared<mcl::ConsoleLogger>();
532 auto client_platform_factory = std::make_shared<mcl::NativeClientPlatformFactory>();
533
534- MirConnection* connection = new MirConnection(
535- mcl::make_rpc_channel(server, log),
536- log,
537- client_platform_factory);
538+ auto rpc = std::make_shared<mcl::MirSocketRpcChannel>(server, log);
539+
540+ MirConnection* connection = new MirConnection(rpc, log,
541+ client_platform_factory);
542+
543+ rpc->set_event_handler(connection);
544
545 return connection->connect(lightdm_id, app_name, callback, client_context);
546 }
547@@ -298,3 +302,29 @@
548
549 return type;
550 }
551+
552+MirWaitHandle* mir_surface_set_state(MirSurface *surf, MirSurfaceState state)
553+{
554+ return surf ? surf->configure(mir_surface_attrib_state, state) : NULL;
555+}
556+
557+MirSurfaceState mir_surface_get_state(MirSurface *surf)
558+{
559+ MirSurfaceState state = mir_surface_state_unknown;
560+
561+ if (surf)
562+ {
563+ int s = surf->attrib(mir_surface_attrib_state);
564+
565+ if (s == mir_surface_state_unknown)
566+ {
567+ surf->configure(mir_surface_attrib_state,
568+ mir_surface_state_unknown)->wait_for_result();
569+ s = surf->attrib(mir_surface_attrib_state);
570+ }
571+
572+ state = static_cast<MirSurfaceState>(s);
573+ }
574+
575+ return state;
576+}
577
578=== modified file 'src/client/mir_connection.cpp'
579--- src/client/mir_connection.cpp 2013-04-11 22:47:09 +0000
580+++ src/client/mir_connection.cpp 2013-04-18 07:33:25 +0000
581@@ -59,7 +59,7 @@
582 connect_result.set_error("connect not called");
583 }
584
585-MirConnection::~MirConnection()
586+MirConnection::~MirConnection() noexcept
587 {
588 std::lock_guard<std::mutex> lock(connection_guard);
589 valid_connections.erase(this);
590@@ -120,6 +120,8 @@
591
592 SurfaceRelease surf_release{surface, new_wait_handle, callback, context};
593
594+ valid_surfaces.erase(surface->id());
595+
596 mir::protobuf::SurfaceId message;
597 message.set_value(surface->id());
598 server.release_surface(0, &message, &void_response,
599@@ -312,3 +314,44 @@
600 {
601 return *native_display;
602 }
603+
604+void MirConnection::on_surface_created(int id, MirSurface* surface)
605+{
606+ valid_surfaces[id] = surface;
607+}
608+
609+void MirConnection::handle_event(MirEvent const& e)
610+{
611+ switch (e.type)
612+ {
613+ case mir_event_type_surface:
614+ {
615+ int id = e.surface.id;
616+ SurfaceMap::iterator it = valid_surfaces.find(id);
617+ if (it != valid_surfaces.end())
618+ {
619+ MirSurface *surface = it->second;
620+ surface->handle_event(e);
621+ }
622+ else
623+ {
624+ log->error() << __PRETTY_FUNCTION__
625+ << ": mir_event_type_surface "
626+ << "received for non-existent surface ID "
627+ << id
628+ << ".\n";
629+ }
630+ }
631+ break;
632+ default:
633+ // Don't worry. This function only gets called for events from the
634+ // RPC channel (not input). So you will never see this error unless
635+ // you make a mistake.
636+
637+ log->error() << __PRETTY_FUNCTION__
638+ << ": Unsupported event type "
639+ << e.type
640+ << " received from server.\n";
641+ break;
642+ }
643+}
644
645=== modified file 'src/client/mir_connection.h'
646--- src/client/mir_connection.h 2013-04-11 22:47:09 +0000
647+++ src/client/mir_connection.h 2013-04-18 07:33:25 +0000
648@@ -21,6 +21,7 @@
649 #include <string>
650 #include <memory>
651 #include <unordered_set>
652+#include <unordered_map>
653
654 #include <mutex>
655
656@@ -33,6 +34,7 @@
657 #include "client_context.h"
658
659 #include "mir_wait_handle.h"
660+#include "mir/event_sink.h"
661
662 namespace mir
663 {
664@@ -49,7 +51,8 @@
665 }
666 }
667
668-struct MirConnection : public mir::client::ClientContext
669+struct MirConnection : public mir::client::ClientContext,
670+ public mir::EventSink
671 {
672 public:
673 MirConnection();
674@@ -57,7 +60,7 @@
675 MirConnection(std::shared_ptr<google::protobuf::RpcChannel> const& channel,
676 std::shared_ptr<mir::client::Logger> const & log,
677 std::shared_ptr<mir::client::ClientPlatformFactory> const& client_platform_factory);
678- ~MirConnection();
679+ ~MirConnection() noexcept;
680
681 MirConnection(MirConnection const &) = delete;
682 MirConnection& operator=(MirConnection const &) = delete;
683@@ -104,6 +107,9 @@
684
685 EGLNativeDisplayType egl_native_display();
686
687+ void on_surface_created(int id, MirSurface* surface);
688+ void handle_event(MirEvent const&);
689+
690 private:
691 std::shared_ptr<google::protobuf::RpcChannel> channel;
692 mir::protobuf::DisplayServer::Stub server;
693@@ -132,6 +138,9 @@
694 static std::mutex connection_guard;
695 static std::unordered_set<MirConnection*> valid_connections;
696
697+ typedef std::unordered_map<int, MirSurface*> SurfaceMap;
698+ SurfaceMap valid_surfaces;
699+
700 struct SurfaceRelease;
701
702 void done_disconnect();
703
704=== modified file 'src/client/mir_socket_rpc_channel.cpp'
705--- src/client/mir_socket_rpc_channel.cpp 2013-04-11 22:47:09 +0000
706+++ src/client/mir_socket_rpc_channel.cpp 2013-04-18 07:33:25 +0000
707@@ -27,6 +27,7 @@
708 #include "ancillary.h"
709
710 #include <boost/bind.hpp>
711+#include <cstring>
712
713 namespace mcl = mir::client;
714 namespace mcld = mir::client::detail;
715@@ -36,8 +37,15 @@
716 {
717 }
718
719-mcl::MirSocketRpcChannel::MirSocketRpcChannel(std::string const& endpoint, std::shared_ptr<Logger> const& log) :
720- log(log), pending_calls(log), work(io_service), endpoint(endpoint), socket(io_service)
721+mcl::MirSocketRpcChannel::MirSocketRpcChannel(
722+ std::string const& endpoint,
723+ std::shared_ptr<Logger> const& log) :
724+ log(log),
725+ pending_calls(log),
726+ work(io_service),
727+ endpoint(endpoint),
728+ socket(io_service),
729+ event_handler(nullptr)
730 {
731 socket.connect(endpoint);
732
733@@ -245,7 +253,14 @@
734
735 log->debug() << __PRETTY_FUNCTION__ << " result.id():" << result.id() << std::endl;
736
737- pending_calls.complete_response(result);
738+ if (!result.has_id()) // It's an event sequence
739+ {
740+ process_event_sequence(result);
741+ }
742+ else
743+ {
744+ pending_calls.complete_response(result);
745+ }
746 }
747 catch (std::exception const& x)
748 {
749@@ -254,6 +269,46 @@
750 }
751 }
752
753+void mcl::MirSocketRpcChannel::process_event_sequence(
754+ mir::protobuf::wire::Result const& result)
755+{
756+ if (!event_handler)
757+ return;
758+
759+ mir::protobuf::EventSequence seq;
760+ if (seq.ParseFromString(result.response()))
761+ {
762+ int const nevents = seq.event_size();
763+ for (int i = 0; i < nevents; i++)
764+ {
765+ mir::protobuf::Event const& event = seq.event(i);
766+ if (event.has_raw())
767+ {
768+ std::string const& raw_event = event.raw();
769+
770+ // In future, events might be compressed where possible.
771+ // But that's a job for later...
772+ if (raw_event.size() == sizeof(MirEvent))
773+ {
774+ MirEvent e;
775+
776+ // Make a copy to ensure integer fields get correct memory
777+ // alignment, which is critical on many non-x86
778+ // architectures.
779+ memcpy(&e, raw_event.data(), sizeof e);
780+ event_handler->handle_event(e);
781+ }
782+ else
783+ {
784+ log->error() << __PRETTY_FUNCTION__
785+ << " Received MirEvent of an unexpected size."
786+ << std::endl;
787+ }
788+ }
789+ }
790+ } // else protobuf will log an error
791+}
792+
793 size_t mcl::MirSocketRpcChannel::read_message_header()
794 {
795 const size_t body_size = (header_bytes[0] << 8) + header_bytes[1];
796@@ -275,3 +330,13 @@
797 result.ParseFromIstream(&in);
798 return result;
799 }
800+
801+void mcl::MirSocketRpcChannel::set_event_handler(mir::EventSink *sink)
802+{
803+ /*
804+ * Yes, these have to be regular pointers. Because ownership of the object
805+ * (which is actually a MirConnection) is the responsibility of the calling
806+ * client. So out of our control.
807+ */
808+ event_handler = sink;
809+}
810
811=== modified file 'src/client/mir_socket_rpc_channel.h'
812--- src/client/mir_socket_rpc_channel.h 2013-04-11 22:47:09 +0000
813+++ src/client/mir_socket_rpc_channel.h 2013-04-18 07:33:25 +0000
814@@ -22,6 +22,7 @@
815
816 #include "mir_basic_rpc_channel.h"
817 #include "mir_logger.h"
818+#include "mir/event_sink.h"
819
820 #include <boost/asio.hpp>
821
822@@ -51,6 +52,8 @@
823 MirSocketRpcChannel(const std::string& endpoint, const std::shared_ptr<Logger>& log);
824 ~MirSocketRpcChannel();
825
826+ void set_event_handler(EventSink *sink);
827+
828 private:
829 virtual void CallMethod(const google::protobuf::MethodDescriptor* method, google::protobuf::RpcController*,
830 const google::protobuf::Message* parameters, google::protobuf::Message* response,
831@@ -72,10 +75,13 @@
832 void on_header_read(const boost::system::error_code& error);
833
834 void read_message();
835+ void process_event_sequence(mir::protobuf::wire::Result const& result);
836
837 size_t read_message_header();
838
839 mir::protobuf::wire::Result read_message_body(const size_t body_size);
840+
841+ EventSink *event_handler;
842 };
843
844 }
845
846=== modified file 'src/client/mir_surface.cpp'
847--- src/client/mir_surface.cpp 2013-04-11 22:47:09 +0000
848+++ src/client/mir_surface.cpp 2013-04-18 07:33:25 +0000
849@@ -59,6 +59,7 @@
850 for (int i = 0; i < mir_surface_attrib_arraysize_; i++)
851 attrib_cache[i] = -1;
852 attrib_cache[mir_surface_attrib_type] = mir_surface_type_normal;
853+ attrib_cache[mir_surface_attrib_state] = mir_surface_state_unknown;
854 }
855
856 MirSurface::~MirSurface()
857@@ -176,6 +177,7 @@
858 auto platform = connection->get_client_platform();
859 accelerated_window = platform->create_egl_native_window(this);
860
861+ connection->on_surface_created(id(), this);
862 callback(this, context);
863
864 create_wait_handle.result_received();
865@@ -261,14 +263,16 @@
866 configure_result.surfaceid().value() == surface.id().value() &&
867 configure_result.has_attrib())
868 {
869- switch (configure_result.attrib())
870+ int a = configure_result.attrib();
871+
872+ switch (a)
873 {
874 case mir_surface_attrib_type:
875+ case mir_surface_attrib_state:
876 if (configure_result.has_ivalue())
877- {
878- int t = configure_result.ivalue();
879- attrib_cache[mir_surface_attrib_type] = t;
880- } // else error is probably set due to an unsupported attrib/value
881+ attrib_cache[a] = configure_result.ivalue();
882+ else
883+ assert(configure_result.has_error());
884 break;
885 default:
886 assert(false);
887@@ -307,3 +311,16 @@
888 }
889 }
890 }
891+
892+void MirSurface::handle_event(MirEvent const& e)
893+{
894+ if (e.type == mir_event_type_surface)
895+ {
896+ MirSurfaceAttrib a = e.surface.attrib;
897+ if (a < mir_surface_attrib_arraysize_)
898+ attrib_cache[a] = e.surface.value;
899+ }
900+
901+ if (handle_event_callback)
902+ handle_event_callback(&e);
903+}
904
905=== modified file 'src/client/mir_surface.h'
906--- src/client/mir_surface.h 2013-04-11 22:47:09 +0000
907+++ src/client/mir_surface.h 2013-04-18 07:33:25 +0000
908@@ -85,6 +85,7 @@
909 int attrib(MirSurfaceAttrib a) const;
910
911 void set_event_handler(MirEventDelegate const* delegate);
912+ void handle_event(MirEvent const& e);
913
914 private:
915 void on_configured();
916
917=== modified file 'src/server/CMakeLists.txt'
918--- src/server/CMakeLists.txt 2013-04-10 16:23:54 +0000
919+++ src/server/CMakeLists.txt 2013-04-18 07:33:25 +0000
920@@ -15,6 +15,8 @@
921 set(LIBDIR "${CMAKE_INSTALL_PREFIX}/lib")
922 set(INCLUDEDIR "${CMAKE_INSTALL_PREFIX}/include/mirserver")
923
924+add_library(mireventqueue STATIC event_queue.cpp)
925+
926 configure_file(
927 ${CMAKE_CURRENT_SOURCE_DIR}/mirserver.pc.in
928 ${CMAKE_CURRENT_BINARY_DIR}/mirserver.pc
929@@ -51,6 +53,7 @@
930 mirsharedinput
931 mirsurfaces
932 mirtime
933+ mireventqueue
934 )
935
936 list(APPEND MIRSERVER_LINKS
937
938=== modified file 'src/server/default_server_configuration.cpp'
939--- src/server/default_server_configuration.cpp 2013-04-17 16:57:53 +0000
940+++ src/server/default_server_configuration.cpp 2013-04-18 07:33:25 +0000
941@@ -106,7 +106,7 @@
942 std::shared_ptr<mg::ViewableArea> const graphics_display;
943 std::shared_ptr<mc::GraphicBufferAllocator> const buffer_allocator;
944
945- virtual std::shared_ptr<mir::protobuf::DisplayServer> make_ipc_server()
946+ virtual std::shared_ptr<mf::Server> make_ipc_server()
947 {
948 return std::make_shared<mf::SessionMediator>(
949 shell,
950
951=== added file 'src/server/event_queue.cpp'
952--- src/server/event_queue.cpp 1970-01-01 00:00:00 +0000
953+++ src/server/event_queue.cpp 2013-04-18 07:33:25 +0000
954@@ -0,0 +1,37 @@
955+/*
956+ * Copyright © 2013 Canonical Ltd.
957+ *
958+ * This program is free software: you can redistribute it and/or modify
959+ * it under the terms of the GNU General Public License version 3 as
960+ * published by the Free Software Foundation.
961+ *
962+ * This program is distributed in the hope that it will be useful,
963+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
964+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
965+ * GNU General Public License for more details.
966+ *
967+ * You should have received a copy of the GNU General Public License
968+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
969+ *
970+ * Authored by: Daniel van Vugt <daniel.van.vugt@canonical.com>
971+ */
972+
973+#include "mir/event_queue.h"
974+#include "mir/event_sink.h"
975+
976+using namespace mir;
977+
978+void EventQueue::set_target(std::weak_ptr<EventSink> const& s)
979+{
980+ target = s;
981+}
982+
983+void EventQueue::handle_event(MirEvent const& e)
984+{
985+ // In future, post might put e on a queue and wait for some background
986+ // thread to push it through to sink. But that's not required right now.
987+
988+ std::shared_ptr<EventSink> p = target.lock();
989+ if (p)
990+ p->handle_event(e);
991+}
992
993=== modified file 'src/server/frontend/CMakeLists.txt'
994--- src/server/frontend/CMakeLists.txt 2013-04-02 03:03:51 +0000
995+++ src/server/frontend/CMakeLists.txt 2013-04-18 07:33:25 +0000
996@@ -8,7 +8,7 @@
997 protobuf_message_processor.cpp
998 null_message_processor.cpp
999 surface_creation_parameters.cpp
1000-
1001+ server.cpp
1002 resource_cache.cpp
1003 ${PROTO_HDRS}
1004 )
1005
1006=== modified file 'src/server/frontend/protobuf_message_processor.cpp'
1007--- src/server/frontend/protobuf_message_processor.cpp 2013-04-16 09:08:29 +0000
1008+++ src/server/frontend/protobuf_message_processor.cpp 2013-04-18 07:33:25 +0000
1009@@ -16,6 +16,7 @@
1010 * Authored by: Alan Griffiths <alan@octopull.co.uk>
1011 */
1012
1013+#include "mir_toolkit/event.h"
1014 #include "protobuf_message_processor.h"
1015 #include "mir/frontend/message_processor_report.h"
1016 #include "mir/frontend/resource_cache.h"
1017@@ -143,6 +144,32 @@
1018 sender->send(buffer);
1019 }
1020
1021+void mfd::ProtobufMessageProcessor::send_event(MirEvent const& e)
1022+{
1023+ mir::protobuf::EventSequence seq;
1024+ mir::protobuf::Event *ev = seq.add_event();
1025+ ev->set_raw(&e, sizeof(MirEvent));
1026+
1027+ std::string buffer;
1028+ seq.SerializeToString(&buffer);
1029+
1030+ mir::protobuf::wire::Result result;
1031+ result.set_response(buffer);
1032+
1033+ result.SerializeToString(&buffer);
1034+
1035+ sender->send(buffer);
1036+}
1037+
1038+void mfd::ProtobufMessageProcessor::handle_event(MirEvent const& e)
1039+{
1040+ // Limit the types of events we wish to send over protobuf, for now.
1041+ if (e.type == mir_event_type_surface)
1042+ {
1043+ send_event(e);
1044+ }
1045+}
1046+
1047 bool mfd::ProtobufMessageProcessor::dispatch(mir::protobuf::wire::Invocation const& invocation)
1048 {
1049 report->received_invocation(display_server.get(), invocation.id(), invocation.method_name());
1050
1051=== modified file 'src/server/frontend/protobuf_message_processor.h'
1052--- src/server/frontend/protobuf_message_processor.h 2013-04-16 09:08:29 +0000
1053+++ src/server/frontend/protobuf_message_processor.h 2013-04-18 07:33:25 +0000
1054@@ -24,6 +24,7 @@
1055
1056 #include "mir_protobuf.pb.h"
1057 #include "mir_protobuf_wire.pb.h"
1058+#include "mir/event_sink.h"
1059
1060 #include <vector>
1061 #include <memory>
1062@@ -42,7 +43,8 @@
1063 namespace detail
1064 {
1065
1066-struct ProtobufMessageProcessor : MessageProcessor
1067+struct ProtobufMessageProcessor : MessageProcessor,
1068+ public EventSink
1069 {
1070 ProtobufMessageProcessor(
1071 MessageSender* sender,
1072@@ -72,6 +74,10 @@
1073 // OTOH until we have a real requirement it is hard to see how best to generalise.
1074 void send_response(::google::protobuf::uint32 id, mir::protobuf::Surface* response);
1075
1076+ void send_event(MirEvent const& e);
1077+
1078+ void handle_event(MirEvent const& e);
1079+
1080 template<class Response>
1081 std::vector<int32_t> extract_fds_from(Response* response);
1082
1083
1084=== modified file 'src/server/frontend/protobuf_socket_communicator.cpp'
1085--- src/server/frontend/protobuf_socket_communicator.cpp 2013-04-16 09:08:29 +0000
1086+++ src/server/frontend/protobuf_socket_communicator.cpp 2013-04-18 07:33:25 +0000
1087@@ -22,6 +22,7 @@
1088
1089 #include "mir/frontend/protobuf_ipc_factory.h"
1090 #include "mir/protobuf/google_protobuf_guard.h"
1091+#include "mir/frontend/session_mediator.h"
1092
1093 #include <boost/signals2.hpp>
1094
1095@@ -52,13 +53,17 @@
1096 next_id(),
1097 connected_sessions);
1098
1099- auto session = std::make_shared<detail::ProtobufMessageProcessor>(
1100+ std::shared_ptr<mir::frontend::Server> server =
1101+ ipc_factory->make_ipc_server();
1102+
1103+ auto proc = std::make_shared<detail::ProtobufMessageProcessor>(
1104 socket_session.get(),
1105- ipc_factory->make_ipc_server(),
1106+ server,
1107 ipc_factory->resource_cache(),
1108 ipc_factory->report());
1109-
1110- socket_session->set_processor(session);
1111+
1112+ server->set_event_sink(proc);
1113+ socket_session->set_processor(proc);
1114
1115 acceptor.async_accept(
1116 socket_session->get_socket(),
1117
1118=== added file 'src/server/frontend/server.cpp'
1119--- src/server/frontend/server.cpp 1970-01-01 00:00:00 +0000
1120+++ src/server/frontend/server.cpp 2013-04-18 07:33:25 +0000
1121@@ -0,0 +1,30 @@
1122+/*
1123+ * Copyright © 2013 Canonical Ltd.
1124+ *
1125+ * This program is free software: you can redistribute it and/or modify it
1126+ * under the terms of the GNU General Public License version 3,
1127+ * as published by the Free Software Foundation.
1128+ *
1129+ * This program is distributed in the hope that it will be useful,
1130+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
1131+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
1132+ * GNU General Public License for more details.
1133+ *
1134+ * You should have received a copy of the GNU General Public License
1135+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
1136+ *
1137+ * Authored by: Daniel van Vugt <daniel.van.vugt@canonical.com>
1138+ */
1139+
1140+#include "mir/frontend/server.h"
1141+
1142+using namespace mir::frontend;
1143+
1144+Server::~Server() noexcept
1145+{
1146+}
1147+
1148+void Server::set_event_sink(std::weak_ptr<mir::EventSink> const& sink)
1149+{
1150+ event_sink = sink;
1151+}
1152
1153=== modified file 'src/server/frontend/session_mediator.cpp'
1154--- src/server/frontend/session_mediator.cpp 2013-04-16 09:08:29 +0000
1155+++ src/server/frontend/session_mediator.cpp 2013-04-18 07:33:25 +0000
1156@@ -64,6 +64,7 @@
1157 report->session_connect_called(request->application_name());
1158
1159 session = shell->open_session(request->application_name());
1160+ session->set_event_sink(event_sink);
1161
1162 auto ipc_package = graphics_platform->get_ipc_package();
1163 auto platform = response->mutable_platform();
1164
1165=== modified file 'src/server/shell/application_session.cpp'
1166--- src/server/shell/application_session.cpp 2013-04-16 09:08:29 +0000
1167+++ src/server/shell/application_session.cpp 2013-04-18 07:33:25 +0000
1168@@ -35,6 +35,7 @@
1169 std::shared_ptr<SurfaceFactory> const& surface_factory,
1170 std::string const& session_name) :
1171 surface_factory(surface_factory),
1172+ event_queue(std::make_shared<EventQueue>()),
1173 session_name(session_name),
1174 next_surface_id(0)
1175 {
1176@@ -50,6 +51,12 @@
1177 }
1178 }
1179
1180+void msh::ApplicationSession::set_event_sink(
1181+ std::weak_ptr<mir::EventSink> const& sink)
1182+{
1183+ event_queue->set_target(sink);
1184+}
1185+
1186 mf::SurfaceId msh::ApplicationSession::next_id()
1187 {
1188 return mf::SurfaceId(next_surface_id.fetch_add(1));
1189@@ -60,6 +67,10 @@
1190 auto surf = surface_factory->create_surface(params);
1191 auto const id = next_id();
1192
1193+ std::shared_ptr<EventSink> q(event_queue);
1194+ surf->set_id(id);
1195+ surf->set_event_target(q);
1196+
1197 std::unique_lock<std::mutex> lock(surfaces_mutex);
1198 surfaces[id] = surf;
1199 return id;
1200
1201=== modified file 'src/server/shell/surface.cpp'
1202--- src/server/shell/surface.cpp 2013-04-16 09:08:29 +0000
1203+++ src/server/shell/surface.cpp 2013-04-18 07:33:25 +0000
1204@@ -19,10 +19,13 @@
1205 #include "mir/shell/surface.h"
1206 #include "mir/shell/surface_builder.h"
1207 #include "mir/input/input_channel.h"
1208+#include "mir_toolkit/event.h"
1209+#include "mir/event_sink.h"
1210
1211 #include <boost/throw_exception.hpp>
1212
1213 #include <stdexcept>
1214+#include <cstring>
1215
1216 namespace msh = mir::shell;
1217 namespace mc = mir::compositor;
1218@@ -35,7 +38,9 @@
1219 : builder(builder),
1220 input_channel(input_channel),
1221 surface(builder->create_surface(params)),
1222- type_value(mir_surface_type_normal)
1223+ id(0),
1224+ type_value(mir_surface_type_normal),
1225+ state_value(mir_surface_state_restored)
1226 {
1227 }
1228
1229@@ -47,6 +52,16 @@
1230 }
1231 }
1232
1233+void msh::Surface::set_id(mir::frontend::SurfaceId i)
1234+{
1235+ id = i;
1236+}
1237+
1238+void msh::Surface::set_event_target(std::shared_ptr<EventSink> const& sink)
1239+{
1240+ event_sink = sink;
1241+}
1242+
1243 void msh::Surface::hide()
1244 {
1245 if (auto const& s = surface.lock())
1246@@ -169,6 +184,12 @@
1247 "type."));
1248 result = type();
1249 break;
1250+ case mir_surface_attrib_state:
1251+ if (value != mir_surface_state_unknown &&
1252+ !set_state(static_cast<MirSurfaceState>(value)))
1253+ BOOST_THROW_EXCEPTION(std::logic_error("Invalid surface state."));
1254+ result = state();
1255+ break;
1256 default:
1257 BOOST_THROW_EXCEPTION(std::logic_error("Invalid surface "
1258 "attribute."));
1259@@ -195,3 +216,44 @@
1260
1261 return valid;
1262 }
1263+
1264+MirSurfaceState msh::Surface::state() const
1265+{
1266+ return state_value;
1267+}
1268+
1269+bool msh::Surface::set_state(MirSurfaceState s)
1270+{
1271+ bool valid = false;
1272+
1273+ if (s > mir_surface_state_unknown &&
1274+ s < mir_surface_state_arraysize_)
1275+ {
1276+ state_value = s;
1277+ valid = true;
1278+
1279+ notify_change(mir_surface_attrib_state, s);
1280+ }
1281+
1282+ return valid;
1283+}
1284+
1285+void msh::Surface::notify_change(MirSurfaceAttrib attrib, int value)
1286+{
1287+ if (event_sink)
1288+ {
1289+ MirEvent e;
1290+
1291+ // This memset is not really required. However it does avoid some
1292+ // harmless uninitialized memory reads that valgrind will complain
1293+ // about, due to gaps in MirEvent.
1294+ memset(&e, 0, sizeof e);
1295+
1296+ e.type = mir_event_type_surface;
1297+ e.surface.id = id.as_value();
1298+ e.surface.attrib = attrib;
1299+ e.surface.value = value;
1300+
1301+ event_sink->handle_event(e);
1302+ }
1303+}
1304
1305=== modified file 'src/shared/protobuf/mir_protobuf.proto'
1306--- src/shared/protobuf/mir_protobuf.proto 2013-03-21 03:32:59 +0000
1307+++ src/shared/protobuf/mir_protobuf.proto 2013-04-18 07:33:25 +0000
1308@@ -122,6 +122,14 @@
1309 optional string error = 127;
1310 }
1311
1312+message Event {
1313+ optional bytes raw = 1; // MirEvent structure
1314+}
1315+
1316+message EventSequence {
1317+ repeated Event event = 1;
1318+}
1319+
1320 service DisplayServer {
1321 // Platform independent requests
1322 rpc connect(ConnectParameters) returns (Connection);
1323
1324=== modified file 'src/shared/protobuf/mir_protobuf_wire.proto'
1325--- src/shared/protobuf/mir_protobuf_wire.proto 2012-09-14 12:03:52 +0000
1326+++ src/shared/protobuf/mir_protobuf_wire.proto 2013-04-18 07:33:25 +0000
1327@@ -7,6 +7,7 @@
1328 }
1329
1330 message Result {
1331- required uint32 id = 1;
1332+ // Invocation results have id and response. Events only have response.
1333+ optional uint32 id = 1;
1334 optional bytes response = 2;
1335 }
1336
1337=== modified file 'tests/acceptance-tests/test_client_library.cpp'
1338--- tests/acceptance-tests/test_client_library.cpp 2013-04-17 07:31:40 +0000
1339+++ tests/acceptance-tests/test_client_library.cpp 2013-04-18 07:33:25 +0000
1340@@ -29,6 +29,7 @@
1341
1342 #include <gtest/gtest.h>
1343 #include <chrono>
1344+#include <cstring>
1345
1346 namespace mf = mir::frontend;
1347 namespace mc = mir::compositor;
1348@@ -279,6 +280,171 @@
1349 launch_client_process(client_config);
1350 }
1351
1352+TEST_F(DefaultDisplayServerTestFixture, client_can_set_surface_state)
1353+{
1354+ struct ClientConfig : ClientConfigCommon
1355+ {
1356+ void exec()
1357+ {
1358+ connection = mir_connect_sync(mir_test_socket, __PRETTY_FUNCTION__);
1359+ ASSERT_TRUE(connection != NULL);
1360+ EXPECT_TRUE(mir_connection_is_valid(connection));
1361+ EXPECT_STREQ(mir_connection_get_error_message(connection), "");
1362+
1363+ MirSurfaceParameters const request_params =
1364+ {
1365+ __PRETTY_FUNCTION__,
1366+ 640, 480,
1367+ mir_pixel_format_abgr_8888,
1368+ mir_buffer_usage_hardware
1369+ };
1370+
1371+ surface = mir_connection_create_surface_sync(connection,
1372+ &request_params);
1373+ ASSERT_TRUE(surface != NULL);
1374+ EXPECT_TRUE(mir_surface_is_valid(surface));
1375+ EXPECT_STREQ(mir_surface_get_error_message(surface), "");
1376+
1377+ EXPECT_EQ(mir_surface_state_restored,
1378+ mir_surface_get_state(surface));
1379+
1380+ mir_wait_for(mir_surface_set_state(surface,
1381+ mir_surface_state_fullscreen));
1382+ EXPECT_EQ(mir_surface_state_fullscreen,
1383+ mir_surface_get_state(surface));
1384+
1385+ mir_wait_for(mir_surface_set_state(surface,
1386+ static_cast<MirSurfaceState>(999)));
1387+ EXPECT_EQ(mir_surface_state_fullscreen,
1388+ mir_surface_get_state(surface));
1389+
1390+ mir_wait_for(mir_surface_set_state(surface,
1391+ mir_surface_state_minimized));
1392+ EXPECT_EQ(mir_surface_state_minimised,
1393+ mir_surface_get_state(surface));
1394+
1395+ mir_wait_for(mir_surface_set_state(surface,
1396+ static_cast<MirSurfaceState>(888)));
1397+ EXPECT_EQ(mir_surface_state_minimised,
1398+ mir_surface_get_state(surface));
1399+
1400+ // Stress-test synchronization logic with some flooding
1401+ for (int i = 0; i < 100; i++)
1402+ {
1403+ mir_surface_set_state(surface, mir_surface_state_restored);
1404+ mir_wait_for(mir_surface_set_state(surface,
1405+ mir_surface_state_fullscreen));
1406+ ASSERT_EQ(mir_surface_state_fullscreen,
1407+ mir_surface_get_state(surface));
1408+ }
1409+
1410+ mir_surface_release_sync(surface);
1411+ mir_connection_release(connection);
1412+ }
1413+ } client_config;
1414+
1415+ launch_client_process(client_config);
1416+}
1417+
1418+TEST_F(DefaultDisplayServerTestFixture, client_receives_surface_state_events)
1419+{
1420+ struct ClientConfig : ClientConfigCommon
1421+ {
1422+ static void event_callback(MirSurface* surface, MirEvent const* event,
1423+ void* ctx)
1424+ {
1425+ ClientConfig* self = static_cast<ClientConfig*>(ctx);
1426+ self->last_event = *event;
1427+ self->last_event_surface = surface;
1428+ }
1429+
1430+ void exec()
1431+ {
1432+ connection = mir_connect_sync(mir_test_socket, __PRETTY_FUNCTION__);
1433+ ASSERT_TRUE(connection != NULL);
1434+ ASSERT_TRUE(mir_connection_is_valid(connection));
1435+
1436+ MirSurfaceParameters const request_params =
1437+ {
1438+ __PRETTY_FUNCTION__,
1439+ 640, 480,
1440+ mir_pixel_format_abgr_8888,
1441+ mir_buffer_usage_hardware
1442+ };
1443+
1444+ memset(&last_event, 0, sizeof last_event);
1445+ last_event_surface = nullptr;
1446+
1447+ MirEventDelegate delegate{&event_callback, this};
1448+ MirSurface* other_surface =
1449+ mir_connection_create_surface_sync(connection, &request_params);
1450+ ASSERT_TRUE(other_surface != NULL);
1451+ ASSERT_TRUE(mir_surface_is_valid(other_surface));
1452+ mir_surface_set_event_handler(other_surface, nullptr);
1453+
1454+ surface =
1455+ mir_connection_create_surface_sync(connection, &request_params);
1456+ ASSERT_TRUE(surface != NULL);
1457+ ASSERT_TRUE(mir_surface_is_valid(surface));
1458+
1459+ mir_surface_set_event_handler(surface, &delegate);
1460+
1461+ int surface_id = mir_surface_get_id(surface);
1462+
1463+ mir_wait_for(mir_surface_set_state(surface,
1464+ mir_surface_state_fullscreen));
1465+ mir_wait_for(mir_surface_set_state(other_surface,
1466+ mir_surface_state_minimized));
1467+ EXPECT_EQ(surface, last_event_surface);
1468+ EXPECT_EQ(mir_event_type_surface, last_event.type);
1469+ EXPECT_EQ(surface_id, last_event.surface.id);
1470+ EXPECT_EQ(mir_surface_attrib_state, last_event.surface.attrib);
1471+ EXPECT_EQ(mir_surface_state_fullscreen, last_event.surface.value);
1472+
1473+ mir_wait_for(mir_surface_set_state(surface,
1474+ static_cast<MirSurfaceState>(999)));
1475+ EXPECT_EQ(surface, last_event_surface);
1476+ EXPECT_EQ(mir_event_type_surface, last_event.type);
1477+ EXPECT_EQ(surface_id, last_event.surface.id);
1478+ EXPECT_EQ(mir_surface_attrib_state, last_event.surface.attrib);
1479+ EXPECT_EQ(mir_surface_state_fullscreen, last_event.surface.value);
1480+
1481+ memset(&last_event, 0, sizeof last_event);
1482+ last_event_surface = nullptr;
1483+
1484+ mir_wait_for(mir_surface_set_state(surface,
1485+ mir_surface_state_minimized));
1486+ EXPECT_EQ(surface, last_event_surface);
1487+ EXPECT_EQ(mir_event_type_surface, last_event.type);
1488+ EXPECT_EQ(surface_id, last_event.surface.id);
1489+ EXPECT_EQ(mir_surface_attrib_state, last_event.surface.attrib);
1490+ EXPECT_EQ(mir_surface_state_minimized, last_event.surface.value);
1491+
1492+ memset(&last_event, 0, sizeof last_event);
1493+ last_event_surface = nullptr;
1494+
1495+ mir_wait_for(mir_surface_set_state(surface,
1496+ static_cast<MirSurfaceState>(777)));
1497+ mir_wait_for(mir_surface_set_state(other_surface,
1498+ mir_surface_state_maximized));
1499+ EXPECT_EQ(0, last_event_surface);
1500+ EXPECT_EQ(0, last_event.type);
1501+ EXPECT_EQ(0, last_event.surface.id);
1502+ EXPECT_EQ(0, last_event.surface.attrib);
1503+ EXPECT_EQ(0, last_event.surface.value);
1504+
1505+ mir_surface_release_sync(surface);
1506+ mir_surface_release_sync(other_surface);
1507+ mir_connection_release(connection);
1508+ }
1509+
1510+ MirEvent last_event;
1511+ MirSurface* last_event_surface;
1512+ } client_config;
1513+
1514+ launch_client_process(client_config);
1515+}
1516+
1517 TEST_F(DefaultDisplayServerTestFixture, client_library_creates_multiple_surfaces)
1518 {
1519 int const n_surfaces = 13;
1520
1521=== modified file 'tests/integration-tests/test_error_reporting.cpp'
1522--- tests/integration-tests/test_error_reporting.cpp 2013-04-17 07:31:40 +0000
1523+++ tests/integration-tests/test_error_reporting.cpp 2013-04-18 07:33:25 +0000
1524@@ -23,6 +23,7 @@
1525 #include "mir/frontend/protobuf_ipc_factory.h"
1526 #include "mir/frontend/resource_cache.h"
1527 #include "mir/frontend/communicator.h"
1528+#include "mir/frontend/server.h"
1529
1530 #include "mir_protobuf.pb.h"
1531
1532@@ -47,7 +48,7 @@
1533 {
1534 char const* const mir_test_socket = mtf::test_socket_file().c_str();
1535
1536-struct ErrorServer : mir::protobuf::DisplayServer
1537+struct ErrorServer : mf::Server
1538 {
1539 static std::string const test_exception_text;
1540
1541
1542=== modified file 'tests/mir_test_doubles/test_protobuf_socket_server.cpp'
1543--- tests/mir_test_doubles/test_protobuf_socket_server.cpp 2013-03-13 08:09:52 +0000
1544+++ tests/mir_test_doubles/test_protobuf_socket_server.cpp 2013-04-18 07:33:25 +0000
1545@@ -26,7 +26,7 @@
1546
1547 mt::TestProtobufServer::TestProtobufServer(
1548 std::string socket_name,
1549- const std::shared_ptr<protobuf::DisplayServer>& tool) :
1550+ const std::shared_ptr<frontend::Server>& tool) :
1551 factory(std::make_shared<mtd::StubIpcFactory>(*tool)),
1552 comm(make_communicator(socket_name, factory))
1553 {
1554
1555=== modified file 'tests/unit-tests/CMakeLists.txt'
1556--- tests/unit-tests/CMakeLists.txt 2013-04-08 16:20:19 +0000
1557+++ tests/unit-tests/CMakeLists.txt 2013-04-18 07:33:25 +0000
1558@@ -20,7 +20,7 @@
1559 add_subdirectory(android_input/)
1560 add_subdirectory(surfaces/)
1561 add_subdirectory(draw/)
1562-
1563+add_subdirectory(event/)
1564
1565 add_executable(unit-tests ${UNIT_TEST_SOURCES})
1566 uses_android_input(unit-tests)
1567
1568=== modified file 'tests/unit-tests/client/test_client_mir_surface.cpp'
1569--- tests/unit-tests/client/test_client_mir_surface.cpp 2013-04-09 08:50:40 +0000
1570+++ tests/unit-tests/client/test_client_mir_surface.cpp 2013-04-18 07:33:25 +0000
1571@@ -63,7 +63,7 @@
1572
1573 input_fd = open("/dev/null", O_APPEND);
1574 }
1575- ~MockServerPackageGenerator()
1576+ ~MockServerPackageGenerator() noexcept
1577 {
1578 close(input_fd);
1579 for (int i = 0; i < server_package.fd_items; i++)
1580@@ -540,3 +540,21 @@
1581 EXPECT_EQ(mir_surface_type_normal,
1582 surface->attrib(mir_surface_attrib_type));
1583 }
1584+
1585+TEST_F(MirClientSurfaceTest, default_surface_state)
1586+{
1587+ auto surface = std::make_shared<MirSurface> (connection.get(),
1588+ *client_comm_channel,
1589+ logger,
1590+ mock_buffer_factory,
1591+ input_platform,
1592+ params,
1593+ &empty_callback,
1594+ nullptr);
1595+ surface->get_create_wait_handle()->wait_for_result();
1596+
1597+ // Test the default cached state value. It is always unknown until we
1598+ // get a real answer from the server.
1599+ EXPECT_EQ(mir_surface_state_unknown,
1600+ surface->attrib(mir_surface_attrib_state));
1601+}
1602
1603=== added directory 'tests/unit-tests/event'
1604=== added file 'tests/unit-tests/event/CMakeLists.txt'
1605--- tests/unit-tests/event/CMakeLists.txt 1970-01-01 00:00:00 +0000
1606+++ tests/unit-tests/event/CMakeLists.txt 2013-04-18 07:33:25 +0000
1607@@ -0,0 +1,5 @@
1608+set (UNIT_TEST_SOURCES
1609+ ${UNIT_TEST_SOURCES}
1610+ ${CMAKE_CURRENT_SOURCE_DIR}/test_event_queue.cpp
1611+ PARENT_SCOPE
1612+)
1613
1614=== added file 'tests/unit-tests/event/test_event_queue.cpp'
1615--- tests/unit-tests/event/test_event_queue.cpp 1970-01-01 00:00:00 +0000
1616+++ tests/unit-tests/event/test_event_queue.cpp 2013-04-18 07:33:25 +0000
1617@@ -0,0 +1,115 @@
1618+/*
1619+ * Copyright © 2013 Canonical Ltd.
1620+ *
1621+ * This program is free software: you can redistribute it and/or modify
1622+ * it under the terms of the GNU General Public License version 3 as
1623+ * published by the Free Software Foundation.
1624+ *
1625+ * This program is distributed in the hope that it will be useful,
1626+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
1627+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
1628+ * GNU General Public License for more details.
1629+ *
1630+ * You should have received a copy of the GNU General Public License
1631+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
1632+ *
1633+ * Authored by: Daniel van Vugt <daniel.van.vugt@canonical.com>
1634+ */
1635+
1636+#include <gtest/gtest.h>
1637+#include "mir/event_queue.h"
1638+#include "mir/event_sink.h"
1639+#include <cstring>
1640+
1641+TEST(EventQueue, no_sink)
1642+{
1643+ EXPECT_NO_FATAL_FAILURE({
1644+ mir::EventQueue q;
1645+ MirEvent e;
1646+
1647+ e.type = mir_event_type_key;
1648+ q.handle_event(e);
1649+
1650+ e.type = mir_event_type_motion;
1651+ q.handle_event(e);
1652+
1653+ e.type = mir_event_type_surface;
1654+ q.handle_event(e);
1655+ });
1656+}
1657+
1658+namespace
1659+{
1660+ class TestSink : public mir::EventSink
1661+ {
1662+ public:
1663+ TestSink()
1664+ {
1665+ memset(&last_event, 0, sizeof last_event);
1666+ }
1667+
1668+ void handle_event(MirEvent const& e)
1669+ {
1670+ last_event = e;
1671+ }
1672+
1673+ MirEvent const& last_event_handled() const
1674+ {
1675+ return last_event;
1676+ }
1677+
1678+ private:
1679+ MirEvent last_event;
1680+ };
1681+}
1682+
1683+TEST(EventQueue, events_get_handled)
1684+{
1685+ mir::EventQueue q;
1686+ std::shared_ptr<TestSink> s(new TestSink);
1687+
1688+ q.set_target(s);
1689+
1690+ MirEvent e;
1691+ memset(&e, 0, sizeof e);
1692+
1693+ e.type = mir_event_type_key;
1694+ q.handle_event(e);
1695+ EXPECT_EQ(mir_event_type_key, s->last_event_handled().type);
1696+
1697+ e.type = mir_event_type_motion;
1698+ q.handle_event(e);
1699+ EXPECT_EQ(mir_event_type_motion, s->last_event_handled().type);
1700+
1701+ e.type = mir_event_type_surface;
1702+ q.handle_event(e);
1703+ EXPECT_EQ(mir_event_type_surface, s->last_event_handled().type);
1704+}
1705+
1706+TEST(EventQueue, sink_is_changeable)
1707+{
1708+ mir::EventQueue q;
1709+ std::shared_ptr<TestSink> a(new TestSink);
1710+ std::shared_ptr<TestSink> b(new TestSink);
1711+
1712+ q.set_target(a);
1713+
1714+ MirEvent e;
1715+ memset(&e, 0, sizeof e);
1716+
1717+ e.type = mir_event_type_motion;
1718+ q.handle_event(e);
1719+ EXPECT_EQ(mir_event_type_motion, a->last_event_handled().type);
1720+
1721+ q.set_target(b);
1722+
1723+ e.type = mir_event_type_surface;
1724+ q.handle_event(e);
1725+ EXPECT_EQ(mir_event_type_surface, b->last_event_handled().type);
1726+ EXPECT_EQ(mir_event_type_motion, a->last_event_handled().type);
1727+
1728+ e.type = mir_event_type_key;
1729+ q.handle_event(e);
1730+ EXPECT_EQ(mir_event_type_key, b->last_event_handled().type);
1731+ EXPECT_EQ(mir_event_type_motion, a->last_event_handled().type);
1732+}
1733
1734=== modified file 'tests/unit-tests/shell/test_single_visibility_focus_mechanism.cpp'
1735--- tests/unit-tests/shell/test_single_visibility_focus_mechanism.cpp 2013-03-29 16:51:35 +0000
1736+++ tests/unit-tests/shell/test_single_visibility_focus_mechanism.cpp 2013-04-18 07:33:25 +0000
1737@@ -58,6 +58,7 @@
1738 MOCK_METHOD0(show, void());
1739
1740 MOCK_METHOD3(configure_surface, int(mf::SurfaceId, MirSurfaceAttrib, int));
1741+ MOCK_METHOD1(set_event_sink, void(std::weak_ptr<mir::EventSink> const&));
1742 };
1743
1744 TEST(SingleVisibilityFocusMechanism, mechanism_sets_visibility)
1745
1746=== modified file 'tests/unit-tests/shell/test_surface.cpp'
1747--- tests/unit-tests/shell/test_surface.cpp 2013-04-01 16:11:03 +0000
1748+++ tests/unit-tests/shell/test_surface.cpp 2013-04-18 07:33:25 +0000
1749@@ -415,3 +415,38 @@
1750 mir_surface_type_freestyle));
1751 EXPECT_EQ(mir_surface_type_freestyle, surf.type());
1752 }
1753+
1754+TEST_F(ShellSurface, states)
1755+{
1756+ using namespace testing;
1757+
1758+ msh::Surface surf(
1759+ mt::fake_shared(surface_builder),
1760+ mf::a_surface(),
1761+ null_input_channel);
1762+
1763+ EXPECT_EQ(mir_surface_state_restored, surf.state());
1764+
1765+ EXPECT_EQ(mir_surface_state_vertmaximized,
1766+ surf.configure(mir_surface_attrib_state,
1767+ mir_surface_state_vertmaximized));
1768+ EXPECT_EQ(mir_surface_state_vertmaximized, surf.state());
1769+
1770+ EXPECT_THROW({
1771+ surf.configure(mir_surface_attrib_state, 999);
1772+ }, std::logic_error);
1773+ EXPECT_THROW({
1774+ surf.configure(mir_surface_attrib_state, -1);
1775+ }, std::logic_error);
1776+ EXPECT_EQ(mir_surface_state_vertmaximized, surf.state());
1777+
1778+ EXPECT_EQ(mir_surface_state_minimized,
1779+ surf.configure(mir_surface_attrib_state,
1780+ mir_surface_state_minimized));
1781+ EXPECT_EQ(mir_surface_state_minimized, surf.state());
1782+
1783+ EXPECT_EQ(mir_surface_state_fullscreen,
1784+ surf.configure(mir_surface_attrib_state,
1785+ mir_surface_state_fullscreen));
1786+ EXPECT_EQ(mir_surface_state_fullscreen, surf.state());
1787+}

Subscribers

People subscribed via source and target branches