Merge lp:~vanvugt/mir/surface-states into lp:~mir-team/mir/trunk
- surface-states
- Merge into trunk
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 |
Related bugs: | |
Related blueprints: |
Mir on the Phone (iteration 0)
(Undefined)
|
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.
Description of the change
Daniel van Vugt (vanvugt) wrote : | # |
PS Jenkins bot (ps-jenkins) wrote : | # |
PASSED: Continuous integration, rev:622
http://
Executed test runs:
SUCCESS: http://
SUCCESS: http://
SUCCESS: http://
deb: http://
Click here to trigger a rebuild:
http://
PS Jenkins bot (ps-jenkins) wrote : | # |
PASSED: Continuous integration, rev:625
http://
Executed test runs:
SUCCESS: http://
SUCCESS: http://
SUCCESS: http://
deb: http://
SUCCESS: http://
Click here to trigger a rebuild:
http://
PS Jenkins bot (ps-jenkins) wrote : | # |
PASSED: Continuous integration, rev:626
http://
Executed test runs:
SUCCESS: http://
SUCCESS: http://
SUCCESS: http://
deb: http://
SUCCESS: http://
Click here to trigger a rebuild:
http://
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::SurfaceFac
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"
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::SurfaceFac
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.
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/
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.
PS Jenkins bot (ps-jenkins) wrote : | # |
PASSED: Continuous integration, rev:628
http://
Executed test runs:
SUCCESS: http://
SUCCESS: http://
SUCCESS: http://
deb: http://
Click here to trigger a rebuild:
http://
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?
Alan Griffiths (alan-griffiths) wrote : | # |
719 + assert(
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?)
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.
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.
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.
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.
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_
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.
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.
Daniel van Vugt (vanvugt) wrote : | # |
Decoupled shell::Surface from a concrete EventQueue. Now shell::Surface only gets and uses the EventSink interface.
PS Jenkins bot (ps-jenkins) wrote : | # |
PASSED: Continuous integration, rev:631
http://
Executed test runs:
SUCCESS: http://
SUCCESS: http://
SUCCESS: http://
deb: http://
SUCCESS: http://
Click here to trigger a rebuild:
http://
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_
> 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.)
Alan Griffiths (alan-griffiths) wrote : | # |
237 +class EventSink
238 +{
239 +public:
240 + virtual ~EventSink() {}
241 + virtual void handle_
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<
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.
Daniel van Vugt (vanvugt) wrote : | # |
Fixed EventSink destructor and assignment.
PS Jenkins bot (ps-jenkins) wrote : | # |
PASSED: Continuous integration, rev:634
http://
Executed test runs:
SUCCESS: http://
SUCCESS: http://
SUCCESS: http://
deb: http://
Click here to trigger a rebuild:
http://
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 MirSocketRpcCha
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.
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.
Alan Griffiths (alan-griffiths) wrote : | # |
88 + virtual void set_event_
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:
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_
930 + if (sm)
931 + sm->set_
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 ProtobufMessage
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_
Daniel van Vugt (vanvugt) wrote : | # |
Re:
virtual void set_event_
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.
PS Jenkins bot (ps-jenkins) wrote : | # |
FAILED: Continuous integration, rev:637
http://
Executed test runs:
FAILURE: http://
FAILURE: http://
FAILURE: http://
SUCCESS: http://
Click here to trigger a rebuild:
http://
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.
Daniel van Vugt (vanvugt) wrote : | # |
Dear Jenkins,
Rebuild!
PS Jenkins bot (ps-jenkins) wrote : | # |
PASSED: Continuous integration, rev:640
http://
Executed test runs:
SUCCESS: http://
SUCCESS: http://
SUCCESS: http://
deb: http://
SUCCESS: http://
Click here to trigger a rebuild:
http://
Preview Diff
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 | +} |
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.