Merge lp:~afrantzis/unity-system-compositor/grand-refactoring-first-steps into lp:unity-system-compositor
- grand-refactoring-first-steps
- Merge into trunk
Status: | Merged | ||||
---|---|---|---|---|---|
Approved by: | Alexandros Frantzis | ||||
Approved revision: | 156 | ||||
Merged at revision: | 157 | ||||
Proposed branch: | lp:~afrantzis/unity-system-compositor/grand-refactoring-first-steps | ||||
Merge into: | lp:unity-system-compositor | ||||
Diff against target: |
3292 lines (+2068/-857) 24 files modified
CMakeLists.txt (+10/-0) cmake/FindGtest.cmake (+53/-0) debian/control (+1/-0) src/CMakeLists.txt (+22/-13) src/asio_dm_connection.cpp (+42/-19) src/asio_dm_connection.h (+73/-0) src/dm_connection.h (+22/-45) src/external_spinner.cpp (+54/-0) src/external_spinner.h (+49/-0) src/main.cpp (+8/-3) src/server_configuration.cpp (+196/-0) src/server_configuration.h (+119/-0) src/session_coordinator.cpp (+117/-0) src/session_coordinator.h (+54/-0) src/session_switcher.cpp (+190/-0) src/session_switcher.h (+98/-0) src/spinner.h (+44/-0) src/surface_coordinator.cpp (+86/-0) src/surface_coordinator.h (+47/-0) src/system_compositor.cpp (+45/-748) src/system_compositor.h (+26/-29) tests/CMakeLists.txt (+17/-0) tests/unit-tests/CMakeLists.txt (+39/-0) tests/unit-tests/test_session_switcher.cpp (+656/-0) |
||||
To merge this branch: | bzr merge lp:~afrantzis/unity-system-compositor/grand-refactoring-first-steps | ||||
Related bugs: |
|
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
Alberto Aguirre (community) | Approve | ||
Alan Griffiths | Approve | ||
Michael Terry (community) | Approve | ||
PS Jenkins bot (community) | continuous-integration | Approve | |
Review via email: mp+226000@code.launchpad.net |
Commit message
First steps of refactoring to improve code comprehensibility and testability
Description of the change
First steps of refactoring to improve code comprehensibility and testability
There is still a lot of work to be done, mainly to allow acceptance and integration testing, but it's a start. In this MP I focused on refactoring the high-level wiring of the application components
and on our session switching algorithm (rewritten test-first from scratch).
Tested on the latest N4 image.
Alexandros Frantzis (afrantzis) wrote : | # |
PS Jenkins bot (ps-jenkins) wrote : | # |
FAILED: Continuous integration, rev:151
http://
Executed test runs:
SUCCESS: http://
FAILURE: http://
Click here to trigger a rebuild:
http://
Alexandros Frantzis (afrantzis) wrote : | # |
> FAILURE: http://
Some kind of build dependency failure (libgtest.a not built in proper order). Will take a look tomorrow, but feel free to review the code in the meantime.
Michael Terry (mterry) wrote : | # |
Thanks for this work, it's great. Comments inline.
Alexandros Frantzis (afrantzis) wrote : | # |
> I noticed I messed up/forgot copyrights in some cases. Will address that tomorrow along with other review comments.
Fixed.
> Some kind of build dependency failure (libgtest.a not built in proper order).
Fixed.
> > + google-mock,
>
> Guh, why not nicely sorted?
Fixed.
> > + surface_
>
> You sort the new stuff but not the old stuff. I know it wasn't sorted before you got there,
> but it's a good chance to fix that.
Fixed.
> > # Link against libmirserver
> > +qt5_use_
>
> This separates the comment above it from the code it comments.
> Though really, you could probably delete the comment, as it doesn't add much
> to the line it comments.
Fixed.
> > === added file 'src/asio_
>
> Please do a "bzr mv" instead of a delete and add for this file. That way, we keep bzr history.
Fixed.
> > +TEST_F(
>
> We shouldn't display a next session without a ready active session.
Fixed. Updated test name is "does_not_
> > +TEST_F(
>
> This test probably wants an active session configured too.
> Because without one, next should never be shown anyway.
> But I think you have that same test below.
Fixed. Kept test (renamed to "does_not_
> > +TEST_F(
>
> Do you even need a next session for this test?
Fixed.
> > +TEST_F(
>
> No, if the active session is not set, we should show spinner (or nothing, if no spinner).
Fixed. "does_not_
> > +TEST_F(
>
> Do we need spinner in this test?
Updated test name to better communicate its intent (and why we need a spinner): "displays_
> > +TEST_F(
>
> Might also want to test that we handle it being resurrected under a different pid,
> which is more common.
Added: "can_handle_
> > +TEST_F(
>
> And again, might want to add test for a process that uses a dead spinner's pid.
Added: "is_not_
PS Jenkins bot (ps-jenkins) wrote : | # |
PASSED: Continuous integration, rev:152
http://
Executed test runs:
SUCCESS: http://
SUCCESS: http://
Click here to trigger a rebuild:
http://
Michael Terry (mterry) wrote : | # |
Excellent, thanks Alexandros!
Alan Griffiths (alan-griffiths) wrote : | # |
Not a complete review but what I've followed looks like an improvement. (And makes my fingers itch to fix more.)
Alexandros Frantzis (afrantzis) wrote : | # |
* Did you test your feature/code change/bug fix ? what device(s) ?
YES, Desktop and N4
* Did you break mir server API or ABI and have the relevant bumps to .so and debian docs been made ?
No (Not applicable)
* Did you break mir client API or ABI and have you followed up with the known clients & announced on mir-devel mailing list ?
No (Not applicable)
Alberto Aguirre (albaguirre) wrote : | # |
1507 + std::thread{
Neat! :)
Looks good, now I need to add some tests for all the display state/inactivity policy stuff :)
Preview Diff
1 | === modified file 'CMakeLists.txt' | |||
2 | --- CMakeLists.txt 2014-07-03 17:52:36 +0000 | |||
3 | +++ CMakeLists.txt 2014-07-18 09:49:03 +0000 | |||
4 | @@ -52,3 +52,13 @@ | |||
5 | 52 | 52 | ||
6 | 53 | add_subdirectory(spinner/) | 53 | add_subdirectory(spinner/) |
7 | 54 | add_subdirectory(src/) | 54 | add_subdirectory(src/) |
8 | 55 | |||
9 | 56 | enable_testing() | ||
10 | 57 | |||
11 | 58 | option(MIR_ENABLE_TESTS "Build tests" ON) | ||
12 | 59 | |||
13 | 60 | if (MIR_ENABLE_TESTS) | ||
14 | 61 | find_package(Gtest REQUIRED) | ||
15 | 62 | include_directories(${GMOCK_INCLUDE_DIR} ${GTEST_INCLUDE_DIR}) | ||
16 | 63 | add_subdirectory(tests/) | ||
17 | 64 | endif () | ||
18 | 55 | 65 | ||
19 | === added file 'cmake/FindGtest.cmake' | |||
20 | --- cmake/FindGtest.cmake 1970-01-01 00:00:00 +0000 | |||
21 | +++ cmake/FindGtest.cmake 2014-07-18 09:49:03 +0000 | |||
22 | @@ -0,0 +1,53 @@ | |||
23 | 1 | include(ExternalProject) | ||
24 | 2 | include(FindPackageHandleStandardArgs) | ||
25 | 3 | |||
26 | 4 | #gtest | ||
27 | 5 | set(GTEST_INSTALL_DIR /usr/src/gmock/gtest/include) | ||
28 | 6 | find_path(GTEST_INCLUDE_DIR gtest/gtest.h | ||
29 | 7 | HINTS ${GTEST_INSTALL_DIR}) | ||
30 | 8 | |||
31 | 9 | #gmock | ||
32 | 10 | find_path(GMOCK_INSTALL_DIR gmock/CMakeLists.txt | ||
33 | 11 | HINTS /usr/src) | ||
34 | 12 | if(${GMOCK_INSTALL_DIR} STREQUAL "GMOCK_INSTALL_DIR-NOTFOUND") | ||
35 | 13 | message(FATAL_ERROR "google-mock package not found") | ||
36 | 14 | endif() | ||
37 | 15 | |||
38 | 16 | set(GMOCK_INSTALL_DIR ${GMOCK_INSTALL_DIR}/gmock) | ||
39 | 17 | find_path(GMOCK_INCLUDE_DIR gmock/gmock.h) | ||
40 | 18 | |||
41 | 19 | set(GMOCK_PREFIX gmock) | ||
42 | 20 | set(GMOCK_BINARY_DIR ${CMAKE_BINARY_DIR}/${GMOCK_PREFIX}/libs) | ||
43 | 21 | set(GTEST_BINARY_DIR ${GMOCK_BINARY_DIR}/gtest) | ||
44 | 22 | |||
45 | 23 | set(GTEST_CMAKE_ARGS "") | ||
46 | 24 | if (${CMAKE_CROSSCOMPILING}) | ||
47 | 25 | set(GTEST_CMAKE_ARGS | ||
48 | 26 | -DCMAKE_TOOLCHAIN_FILE=${CMAKE_MODULE_PATH}/LinuxCrossCompile.cmake) | ||
49 | 27 | endif() | ||
50 | 28 | |||
51 | 29 | ExternalProject_Add( | ||
52 | 30 | GMock | ||
53 | 31 | #where to build in source tree | ||
54 | 32 | PREFIX ${GMOCK_PREFIX} | ||
55 | 33 | #where the source is external to the project | ||
56 | 34 | SOURCE_DIR ${GMOCK_INSTALL_DIR} | ||
57 | 35 | #forward the compilers to the subproject so cross-arch builds work | ||
58 | 36 | CMAKE_ARGS ${GTEST_CMAKE_ARGS} | ||
59 | 37 | BINARY_DIR ${GMOCK_BINARY_DIR} | ||
60 | 38 | |||
61 | 39 | #we don't need to install, so skip | ||
62 | 40 | INSTALL_COMMAND "" | ||
63 | 41 | ) | ||
64 | 42 | |||
65 | 43 | set(GMOCK_LIBRARY ${GMOCK_BINARY_DIR}/libgmock.a) | ||
66 | 44 | set(GMOCK_MAIN_LIBRARY ${GMOCK_BINARY_DIR}/libgmock_main.a) | ||
67 | 45 | set(GMOCK_BOTH_LIBRARIES ${GMOCK_LIBRARY} ${GMOCK_MAIN_LIBRARY}) | ||
68 | 46 | set(GTEST_LIBRARY ${GTEST_BINARY_DIR}/libgtest.a) | ||
69 | 47 | set(GTEST_MAIN_LIBRARY ${GTEST_BINARY_DIR}/libgtest_main.a) | ||
70 | 48 | set(GTEST_BOTH_LIBRARIES ${GTEST_LIBRARY} ${GTEST_MAIN_LIBRARY}) | ||
71 | 49 | set(GTEST_ALL_LIBRARIES ${GTEST_BOTH_LIBRARIES} ${GMOCK_BOTH_LIBRARIES}) | ||
72 | 50 | |||
73 | 51 | find_package_handle_standard_args(GTest DEFAULT_MSG | ||
74 | 52 | GMOCK_INCLUDE_DIR | ||
75 | 53 | GTEST_INCLUDE_DIR) | ||
76 | 0 | 54 | ||
77 | === modified file 'debian/control' | |||
78 | --- debian/control 2014-07-15 13:50:07 +0000 | |||
79 | +++ debian/control 2014-07-18 09:49:03 +0000 | |||
80 | @@ -5,6 +5,7 @@ | |||
81 | 5 | Build-Depends: cmake, | 5 | Build-Depends: cmake, |
82 | 6 | cmake-data, | 6 | cmake-data, |
83 | 7 | debhelper (>= 9), | 7 | debhelper (>= 9), |
84 | 8 | google-mock, | ||
85 | 8 | libandroid-properties-dev [i386 amd64 armhf], | 9 | libandroid-properties-dev [i386 amd64 armhf], |
86 | 9 | libboost-chrono-dev, | 10 | libboost-chrono-dev, |
87 | 10 | libboost-date-time-dev, | 11 | libboost-date-time-dev, |
88 | 11 | 12 | ||
89 | === modified file 'src/CMakeLists.txt' | |||
90 | --- src/CMakeLists.txt 2014-06-16 16:53:55 +0000 | |||
91 | +++ src/CMakeLists.txt 2014-07-18 09:49:03 +0000 | |||
92 | @@ -15,18 +15,17 @@ | |||
93 | 15 | # Authored by: Robert Ancell <robert.ancell@canonical.com> | 15 | # Authored by: Robert Ancell <robert.ancell@canonical.com> |
94 | 16 | 16 | ||
95 | 17 | set(USC_SRCS | 17 | set(USC_SRCS |
97 | 18 | main.cpp | 18 | asio_dm_connection.cpp |
98 | 19 | dbus_screen.cpp | 19 | dbus_screen.cpp |
100 | 20 | dbus_screen.h | 20 | external_spinner.cpp |
101 | 21 | powerd_mediator.cpp | 21 | powerd_mediator.cpp |
104 | 22 | dm_connection.cpp | 22 | powerkey_handler.cpp |
105 | 23 | dm_connection.h | 23 | screen_state_handler.cpp |
106 | 24 | server_configuration.cpp | ||
107 | 25 | session_coordinator.cpp | ||
108 | 26 | session_switcher.cpp | ||
109 | 27 | surface_coordinator.cpp | ||
110 | 24 | system_compositor.cpp | 28 | system_compositor.cpp |
111 | 25 | system_compositor.h | ||
112 | 26 | screen_state_handler.cpp | ||
113 | 27 | screen_state_handler.h | ||
114 | 28 | powerkey_handler.cpp | ||
115 | 29 | powerkey_handler.h | ||
116 | 30 | ) | 29 | ) |
117 | 31 | 30 | ||
118 | 32 | qt5_generate_dbus_interface(dbus_screen.h com.canonical.Unity.Screen.xml) | 31 | qt5_generate_dbus_interface(dbus_screen.h com.canonical.Unity.Screen.xml) |
119 | @@ -36,9 +35,14 @@ | |||
120 | 36 | dbus_screen_adaptor DBusScreenAdaptor) | 35 | dbus_screen_adaptor DBusScreenAdaptor) |
121 | 37 | 36 | ||
122 | 38 | # Compile system compositor | 37 | # Compile system compositor |
123 | 38 | add_library( | ||
124 | 39 | usc STATIC | ||
125 | 40 | ${USC_SRCS} | ||
126 | 41 | ) | ||
127 | 42 | |||
128 | 39 | add_executable( | 43 | add_executable( |
129 | 40 | unity-system-compositor | 44 | unity-system-compositor |
131 | 41 | ${USC_SRCS} | 45 | main.cpp |
132 | 42 | ) | 46 | ) |
133 | 43 | 47 | ||
134 | 44 | include_directories( | 48 | include_directories( |
135 | @@ -52,15 +56,20 @@ | |||
136 | 52 | -DDEFAULT_SPINNER="${CMAKE_INSTALL_FULL_BINDIR}/unity-system-compositor-spinner" | 56 | -DDEFAULT_SPINNER="${CMAKE_INSTALL_FULL_BINDIR}/unity-system-compositor-spinner" |
137 | 53 | ) | 57 | ) |
138 | 54 | 58 | ||
140 | 55 | # Link against libmirserver | 59 | qt5_use_modules(usc Core DBus) |
141 | 60 | |||
142 | 56 | link_directories(${MIRSERVER_LIBRARY_DIRS}) | 61 | link_directories(${MIRSERVER_LIBRARY_DIRS}) |
144 | 57 | target_link_libraries(unity-system-compositor | 62 | |
145 | 63 | target_link_libraries(usc | ||
146 | 58 | ${MIRSERVER_LDFLAGS} | 64 | ${MIRSERVER_LDFLAGS} |
147 | 59 | pthread | 65 | pthread |
148 | 60 | ${Boost_LIBRARIES} | 66 | ${Boost_LIBRARIES} |
149 | 61 | ${GLESv2_LIBRARIES} | 67 | ${GLESv2_LIBRARIES} |
150 | 62 | ) | 68 | ) |
152 | 63 | qt5_use_modules(unity-system-compositor Core DBus) | 69 | |
153 | 70 | target_link_libraries(unity-system-compositor | ||
154 | 71 | usc | ||
155 | 72 | ) | ||
156 | 64 | 73 | ||
157 | 65 | # Install into bin directory | 74 | # Install into bin directory |
158 | 66 | install(TARGETS unity-system-compositor | 75 | install(TARGETS unity-system-compositor |
159 | 67 | 76 | ||
160 | === renamed file 'src/dm_connection.cpp' => 'src/asio_dm_connection.cpp' | |||
161 | --- src/dm_connection.cpp 2013-07-09 19:18:59 +0000 | |||
162 | +++ src/asio_dm_connection.cpp 2014-07-18 09:49:03 +0000 | |||
163 | @@ -1,5 +1,5 @@ | |||
164 | 1 | /* | 1 | /* |
166 | 2 | * Copyright © 2013 Canonical Ltd. | 2 | * Copyright © 2013-2014 Canonical Ltd. |
167 | 3 | * | 3 | * |
168 | 4 | * This program is free software: you can redistribute it and/or modify | 4 | * This program is free software: you can redistribute it and/or modify |
169 | 5 | * it under the terms of the GNU General Public License version 3 as | 5 | * it under the terms of the GNU General Public License version 3 as |
170 | @@ -14,37 +14,62 @@ | |||
171 | 14 | * along with this program. If not, see <http://www.gnu.org/licenses/>. | 14 | * along with this program. If not, see <http://www.gnu.org/licenses/>. |
172 | 15 | * | 15 | * |
173 | 16 | * Authored by: Robert Ancell <robert.ancell@canonical.com> | 16 | * Authored by: Robert Ancell <robert.ancell@canonical.com> |
174 | 17 | * Alexandros Frantzis <alexandros.frantzis@canonical.com> | ||
175 | 17 | */ | 18 | */ |
176 | 18 | 19 | ||
178 | 19 | #include "dm_connection.h" | 20 | #include "asio_dm_connection.h" |
179 | 20 | 21 | ||
180 | 21 | #include <boost/signals2.hpp> | ||
181 | 22 | #include <iostream> | 22 | #include <iostream> |
182 | 23 | #include <thread> | ||
183 | 23 | 24 | ||
184 | 24 | namespace ba = boost::asio; | 25 | namespace ba = boost::asio; |
185 | 25 | namespace bs = boost::system; | 26 | namespace bs = boost::system; |
186 | 26 | 27 | ||
188 | 27 | void DMConnection::start() | 28 | usc::AsioDMConnection::AsioDMConnection( |
189 | 29 | int from_dm_fd, int to_dm_fd, | ||
190 | 30 | std::shared_ptr<DMMessageHandler> const& dm_message_handler) | ||
191 | 31 | : from_dm_pipe{io_service, from_dm_fd}, | ||
192 | 32 | to_dm_pipe{io_service, to_dm_fd}, | ||
193 | 33 | dm_message_handler{dm_message_handler} | ||
194 | 34 | { | ||
195 | 35 | } | ||
196 | 36 | |||
197 | 37 | usc::AsioDMConnection::~AsioDMConnection() | ||
198 | 38 | { | ||
199 | 39 | io_service.stop(); | ||
200 | 40 | if (io_thread.joinable()) | ||
201 | 41 | io_thread.join(); | ||
202 | 42 | } | ||
203 | 43 | |||
204 | 44 | void usc::AsioDMConnection::start() | ||
205 | 28 | { | 45 | { |
206 | 29 | std::cerr << "dm_connection_start" << std::endl; | 46 | std::cerr << "dm_connection_start" << std::endl; |
207 | 47 | |||
208 | 48 | send_ready(); | ||
209 | 30 | read_header(); | 49 | read_header(); |
210 | 50 | |||
211 | 51 | io_thread = std::thread{ | ||
212 | 52 | [this] | ||
213 | 53 | { | ||
214 | 54 | io_service.run(); | ||
215 | 55 | }}; | ||
216 | 31 | } | 56 | } |
217 | 32 | 57 | ||
219 | 33 | void DMConnection::send_ready() | 58 | void usc::AsioDMConnection::send_ready() |
220 | 34 | { | 59 | { |
221 | 35 | send(USCMessageID::ready, ""); | 60 | send(USCMessageID::ready, ""); |
222 | 36 | } | 61 | } |
223 | 37 | 62 | ||
225 | 38 | void DMConnection::read_header() | 63 | void usc::AsioDMConnection::read_header() |
226 | 39 | { | 64 | { |
227 | 40 | ba::async_read(from_dm_pipe, | 65 | ba::async_read(from_dm_pipe, |
228 | 41 | ba::buffer(message_header_bytes), | 66 | ba::buffer(message_header_bytes), |
232 | 42 | boost::bind(&DMConnection::on_read_header, | 67 | std::bind(&AsioDMConnection::on_read_header, |
233 | 43 | this, | 68 | this, |
234 | 44 | ba::placeholders::error)); | 69 | std::placeholders::_1)); |
235 | 45 | } | 70 | } |
236 | 46 | 71 | ||
238 | 47 | void DMConnection::on_read_header(const bs::error_code& ec) | 72 | void usc::AsioDMConnection::on_read_header(bs::error_code const& ec) |
239 | 48 | { | 73 | { |
240 | 49 | if (!ec) | 74 | if (!ec) |
241 | 50 | { | 75 | { |
242 | @@ -52,15 +77,15 @@ | |||
243 | 52 | ba::async_read(from_dm_pipe, | 77 | ba::async_read(from_dm_pipe, |
244 | 53 | message_payload_buffer, | 78 | message_payload_buffer, |
245 | 54 | ba::transfer_exactly(payload_length), | 79 | ba::transfer_exactly(payload_length), |
249 | 55 | boost::bind(&DMConnection::on_read_payload, | 80 | std::bind(&AsioDMConnection::on_read_payload, |
250 | 56 | this, | 81 | this, |
251 | 57 | ba::placeholders::error)); | 82 | std::placeholders::_1)); |
252 | 58 | } | 83 | } |
253 | 59 | else | 84 | else |
254 | 60 | std::cerr << "Failed to read header" << std::endl; | 85 | std::cerr << "Failed to read header" << std::endl; |
255 | 61 | } | 86 | } |
256 | 62 | 87 | ||
258 | 63 | void DMConnection::on_read_payload(const bs::error_code& ec) | 88 | void usc::AsioDMConnection::on_read_payload(const bs::error_code& ec) |
259 | 64 | { | 89 | { |
260 | 65 | if (!ec) | 90 | if (!ec) |
261 | 66 | { | 91 | { |
262 | @@ -86,8 +111,7 @@ | |||
263 | 86 | ss << &message_payload_buffer; | 111 | ss << &message_payload_buffer; |
264 | 87 | auto client_name = ss.str(); | 112 | auto client_name = ss.str(); |
265 | 88 | std::cerr << "set_active_session '" << client_name << "'" << std::endl; | 113 | std::cerr << "set_active_session '" << client_name << "'" << std::endl; |
268 | 89 | if (handler) | 114 | dm_message_handler->set_active_session(client_name); |
267 | 90 | handler->set_active_session(client_name); | ||
269 | 91 | break; | 115 | break; |
270 | 92 | } | 116 | } |
271 | 93 | case USCMessageID::set_next_session: | 117 | case USCMessageID::set_next_session: |
272 | @@ -96,8 +120,7 @@ | |||
273 | 96 | ss << &message_payload_buffer; | 120 | ss << &message_payload_buffer; |
274 | 97 | auto client_name = ss.str(); | 121 | auto client_name = ss.str(); |
275 | 98 | std::cerr << "set_next_session '" << client_name << "'" << std::endl; | 122 | std::cerr << "set_next_session '" << client_name << "'" << std::endl; |
278 | 99 | if (handler) | 123 | dm_message_handler->set_next_session(client_name); |
277 | 100 | handler->set_next_session(client_name); | ||
279 | 101 | break; | 124 | break; |
280 | 102 | } | 125 | } |
281 | 103 | default: | 126 | default: |
282 | @@ -111,7 +134,7 @@ | |||
283 | 111 | read_header(); | 134 | read_header(); |
284 | 112 | } | 135 | } |
285 | 113 | 136 | ||
287 | 114 | void DMConnection::send(USCMessageID id, std::string const& body) | 137 | void usc::AsioDMConnection::send(USCMessageID id, std::string const& body) |
288 | 115 | { | 138 | { |
289 | 116 | const size_t size = body.size(); | 139 | const size_t size = body.size(); |
290 | 117 | const uint16_t _id = (uint16_t) id; | 140 | const uint16_t _id = (uint16_t) id; |
291 | 118 | 141 | ||
292 | === added file 'src/asio_dm_connection.h' | |||
293 | --- src/asio_dm_connection.h 1970-01-01 00:00:00 +0000 | |||
294 | +++ src/asio_dm_connection.h 2014-07-18 09:49:03 +0000 | |||
295 | @@ -0,0 +1,73 @@ | |||
296 | 1 | /* | ||
297 | 2 | * Copyright © 2013-2014 Canonical Ltd. | ||
298 | 3 | * | ||
299 | 4 | * This program is free software: you can redistribute it and/or modify | ||
300 | 5 | * it under the terms of the GNU General Public License version 3 as | ||
301 | 6 | * published by the Free Software Foundation. | ||
302 | 7 | * | ||
303 | 8 | * This program is distributed in the hope that it will be useful, | ||
304 | 9 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
305 | 10 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
306 | 11 | * GNU General Public License for more details. | ||
307 | 12 | * | ||
308 | 13 | * You should have received a copy of the GNU General Public License | ||
309 | 14 | * along with this program. If not, see <http://www.gnu.org/licenses/>. | ||
310 | 15 | * | ||
311 | 16 | * Authored by: Robert Ancell <robert.ancell@canonical.com> | ||
312 | 17 | * Alexandros Frantzis <alexandros.frantzis@canonical.com> | ||
313 | 18 | */ | ||
314 | 19 | |||
315 | 20 | #ifndef USC_ASIO_DM_CONNECTION_H_ | ||
316 | 21 | #define USC_ASIO_DM_CONNECTION_H_ | ||
317 | 22 | |||
318 | 23 | #include "dm_connection.h" | ||
319 | 24 | |||
320 | 25 | #include <boost/asio.hpp> | ||
321 | 26 | #include <thread> | ||
322 | 27 | |||
323 | 28 | namespace usc | ||
324 | 29 | { | ||
325 | 30 | |||
326 | 31 | class AsioDMConnection : public DMConnection | ||
327 | 32 | { | ||
328 | 33 | public: | ||
329 | 34 | AsioDMConnection( | ||
330 | 35 | int from_dm_fd, int to_dm_fd, | ||
331 | 36 | std::shared_ptr<DMMessageHandler> const& dm_message_handler); | ||
332 | 37 | ~AsioDMConnection(); | ||
333 | 38 | |||
334 | 39 | void start() override; | ||
335 | 40 | |||
336 | 41 | private: | ||
337 | 42 | enum class USCMessageID | ||
338 | 43 | { | ||
339 | 44 | ping = 0, | ||
340 | 45 | pong = 1, | ||
341 | 46 | ready = 2, | ||
342 | 47 | session_connected = 3, | ||
343 | 48 | set_active_session = 4, | ||
344 | 49 | set_next_session = 5, | ||
345 | 50 | }; | ||
346 | 51 | |||
347 | 52 | void send_ready(); | ||
348 | 53 | void read_header(); | ||
349 | 54 | void on_read_header(const boost::system::error_code& ec); | ||
350 | 55 | void on_read_payload(const boost::system::error_code& ec); | ||
351 | 56 | void send(USCMessageID id, std::string const& body); | ||
352 | 57 | |||
353 | 58 | boost::asio::io_service io_service; | ||
354 | 59 | boost::asio::posix::stream_descriptor from_dm_pipe; | ||
355 | 60 | boost::asio::posix::stream_descriptor to_dm_pipe; | ||
356 | 61 | std::thread io_thread; | ||
357 | 62 | std::shared_ptr<DMMessageHandler> const dm_message_handler; | ||
358 | 63 | |||
359 | 64 | static size_t const size_of_header = 4; | ||
360 | 65 | unsigned char message_header_bytes[size_of_header]; | ||
361 | 66 | boost::asio::streambuf message_payload_buffer; | ||
362 | 67 | std::vector<char> write_buffer; | ||
363 | 68 | |||
364 | 69 | }; | ||
365 | 70 | |||
366 | 71 | } | ||
367 | 72 | |||
368 | 73 | #endif /* USC_ASIO_DM_CONNECTION_H_ */ | ||
369 | 0 | 74 | ||
370 | === modified file 'src/dm_connection.h' | |||
371 | --- src/dm_connection.h 2013-11-13 13:53:51 +0000 | |||
372 | +++ src/dm_connection.h 2014-07-18 09:49:03 +0000 | |||
373 | @@ -1,5 +1,5 @@ | |||
374 | 1 | /* | 1 | /* |
376 | 2 | * Copyright © 2013 Canonical Ltd. | 2 | * Copyright © 2014 Canonical Ltd. |
377 | 3 | * | 3 | * |
378 | 4 | * This program is free software: you can redistribute it and/or modify | 4 | * This program is free software: you can redistribute it and/or modify |
379 | 5 | * it under the terms of the GNU General Public License version 3 as | 5 | * it under the terms of the GNU General Public License version 3 as |
380 | @@ -13,60 +13,37 @@ | |||
381 | 13 | * You should have received a copy of the GNU General Public License | 13 | * You should have received a copy of the GNU General Public License |
382 | 14 | * along with this program. If not, see <http://www.gnu.org/licenses/>. | 14 | * along with this program. If not, see <http://www.gnu.org/licenses/>. |
383 | 15 | * | 15 | * |
385 | 16 | * Authored by: Robert Ancell <robert.ancell@canonical.com> | 16 | * Authored by: Alexandros Frantzis <alexandros.frantzis@canonical.com> |
386 | 17 | */ | 17 | */ |
387 | 18 | 18 | ||
392 | 19 | #ifndef DM_CONNECTION_H_ | 19 | #ifndef USC_DM_CONNECTION_H_ |
393 | 20 | #define DM_CONNECTION_H_ | 20 | #define USC_DM_CONNECTION_H_ |
394 | 21 | 21 | ||
395 | 22 | #include <boost/asio.hpp> | 22 | #include <string> |
396 | 23 | |||
397 | 24 | namespace usc | ||
398 | 25 | { | ||
399 | 23 | 26 | ||
400 | 24 | class DMMessageHandler | 27 | class DMMessageHandler |
401 | 25 | { | 28 | { |
402 | 26 | public: | 29 | public: |
415 | 27 | virtual void set_active_session(std::string client_name) = 0; | 30 | virtual void set_active_session(std::string const& client_name) = 0; |
416 | 28 | virtual void set_next_session(std::string client_name) = 0; | 31 | virtual void set_next_session(std::string const& client_name) = 0; |
405 | 29 | }; | ||
406 | 30 | |||
407 | 31 | enum class USCMessageID | ||
408 | 32 | { | ||
409 | 33 | ping = 0, | ||
410 | 34 | pong = 1, | ||
411 | 35 | ready = 2, | ||
412 | 36 | session_connected = 3, | ||
413 | 37 | set_active_session = 4, | ||
414 | 38 | set_next_session = 5, | ||
417 | 39 | }; | 32 | }; |
418 | 40 | 33 | ||
419 | 41 | class DMConnection | 34 | class DMConnection |
420 | 42 | { | 35 | { |
421 | 43 | public: | 36 | public: |
448 | 44 | DMConnection(boost::asio::io_service& io_service, int from_dm_fd, int to_dm_fd) : | 37 | virtual ~DMConnection() = default; |
449 | 45 | from_dm_pipe(io_service, from_dm_fd), | 38 | |
450 | 46 | to_dm_pipe(io_service, to_dm_fd) {}; | 39 | virtual void start() = 0; |
451 | 47 | 40 | ||
452 | 48 | void set_handler(DMMessageHandler *handler) | 41 | protected: |
453 | 49 | { | 42 | DMConnection() = default; |
454 | 50 | this->handler = handler; | 43 | DMConnection(DMConnection const&) = delete; |
455 | 51 | } | 44 | DMConnection& operator=(DMConnection const&) = delete; |
430 | 52 | |||
431 | 53 | void start(); | ||
432 | 54 | |||
433 | 55 | void send_ready(); | ||
434 | 56 | |||
435 | 57 | private: | ||
436 | 58 | DMMessageHandler *handler; | ||
437 | 59 | boost::asio::posix::stream_descriptor from_dm_pipe; | ||
438 | 60 | boost::asio::posix::stream_descriptor to_dm_pipe; | ||
439 | 61 | static size_t const size_of_header = 4; | ||
440 | 62 | unsigned char message_header_bytes[size_of_header]; | ||
441 | 63 | boost::asio::streambuf message_payload_buffer; | ||
442 | 64 | std::vector<char> write_buffer; | ||
443 | 65 | |||
444 | 66 | void read_header(); | ||
445 | 67 | void on_read_header(const boost::system::error_code& ec); | ||
446 | 68 | void on_read_payload(const boost::system::error_code& ec); | ||
447 | 69 | void send(USCMessageID id, std::string const& body); | ||
456 | 70 | }; | 45 | }; |
457 | 71 | 46 | ||
459 | 72 | #endif /* DM_CONNECTION_H_ */ | 47 | } |
460 | 48 | |||
461 | 49 | #endif /* USC_DM_CONNECTION_H_ */ | ||
462 | 73 | 50 | ||
463 | === added file 'src/external_spinner.cpp' | |||
464 | --- src/external_spinner.cpp 1970-01-01 00:00:00 +0000 | |||
465 | +++ src/external_spinner.cpp 2014-07-18 09:49:03 +0000 | |||
466 | @@ -0,0 +1,54 @@ | |||
467 | 1 | /* | ||
468 | 2 | * Copyright © 2014 Canonical Ltd. | ||
469 | 3 | * | ||
470 | 4 | * This program is free software: you can redistribute it and/or modify | ||
471 | 5 | * it under the terms of the GNU General Public License version 3 as | ||
472 | 6 | * published by the Free Software Foundation. | ||
473 | 7 | * | ||
474 | 8 | * This program is distributed in the hope that it will be useful, | ||
475 | 9 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
476 | 10 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
477 | 11 | * GNU General Public License for more details. | ||
478 | 12 | * | ||
479 | 13 | * You should have received a copy of the GNU General Public License | ||
480 | 14 | * along with this program. If not, see <http://www.gnu.org/licenses/>. | ||
481 | 15 | * | ||
482 | 16 | * Authored by: Alexandros Frantzis <alexandros.frantzis@canonical.com> | ||
483 | 17 | */ | ||
484 | 18 | |||
485 | 19 | #include "external_spinner.h" | ||
486 | 20 | |||
487 | 21 | usc::ExternalSpinner::ExternalSpinner( | ||
488 | 22 | std::string const& executable, | ||
489 | 23 | std::string const& mir_socket) | ||
490 | 24 | : executable{executable}, | ||
491 | 25 | mir_socket{mir_socket} | ||
492 | 26 | { | ||
493 | 27 | } | ||
494 | 28 | |||
495 | 29 | usc::ExternalSpinner::~ExternalSpinner() | ||
496 | 30 | { | ||
497 | 31 | kill(); | ||
498 | 32 | } | ||
499 | 33 | |||
500 | 34 | void usc::ExternalSpinner::ensure_running() | ||
501 | 35 | { | ||
502 | 36 | if (executable.empty() || process.state() != QProcess::NotRunning) | ||
503 | 37 | return; | ||
504 | 38 | |||
505 | 39 | // Launch spinner process to provide default background when a session isn't ready | ||
506 | 40 | QStringList env = QProcess::systemEnvironment(); | ||
507 | 41 | env << "MIR_SOCKET=" + QString::fromStdString(mir_socket); | ||
508 | 42 | process.setEnvironment(env); | ||
509 | 43 | process.start(executable.c_str()); | ||
510 | 44 | } | ||
511 | 45 | |||
512 | 46 | void usc::ExternalSpinner::kill() | ||
513 | 47 | { | ||
514 | 48 | process.close(); | ||
515 | 49 | } | ||
516 | 50 | |||
517 | 51 | pid_t usc::ExternalSpinner::pid() | ||
518 | 52 | { | ||
519 | 53 | return process.processId(); | ||
520 | 54 | } | ||
521 | 0 | 55 | ||
522 | === added file 'src/external_spinner.h' | |||
523 | --- src/external_spinner.h 1970-01-01 00:00:00 +0000 | |||
524 | +++ src/external_spinner.h 2014-07-18 09:49:03 +0000 | |||
525 | @@ -0,0 +1,49 @@ | |||
526 | 1 | /* | ||
527 | 2 | * Copyright © 2014 Canonical Ltd. | ||
528 | 3 | * | ||
529 | 4 | * This program is free software: you can redistribute it and/or modify | ||
530 | 5 | * it under the terms of the GNU General Public License version 3 as | ||
531 | 6 | * published by the Free Software Foundation. | ||
532 | 7 | * | ||
533 | 8 | * This program is distributed in the hope that it will be useful, | ||
534 | 9 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
535 | 10 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
536 | 11 | * GNU General Public License for more details. | ||
537 | 12 | * | ||
538 | 13 | * You should have received a copy of the GNU General Public License | ||
539 | 14 | * along with this program. If not, see <http://www.gnu.org/licenses/>. | ||
540 | 15 | * | ||
541 | 16 | * Authored by: Alexandros Frantzis <alexandros.frantzis@canonical.com> | ||
542 | 17 | */ | ||
543 | 18 | |||
544 | 19 | #ifndef USC_EXTERNAL_SPINNER_H_ | ||
545 | 20 | #define USC_EXTERNAL_SPINNER_H_ | ||
546 | 21 | |||
547 | 22 | #include "spinner.h" | ||
548 | 23 | |||
549 | 24 | #include <QProcess> | ||
550 | 25 | #include <string> | ||
551 | 26 | |||
552 | 27 | namespace usc | ||
553 | 28 | { | ||
554 | 29 | |||
555 | 30 | class ExternalSpinner : public Spinner | ||
556 | 31 | { | ||
557 | 32 | public: | ||
558 | 33 | ExternalSpinner(std::string const& executable, | ||
559 | 34 | std::string const& mir_socket); | ||
560 | 35 | ~ExternalSpinner(); | ||
561 | 36 | |||
562 | 37 | void ensure_running() override; | ||
563 | 38 | void kill() override; | ||
564 | 39 | pid_t pid() override; | ||
565 | 40 | |||
566 | 41 | private: | ||
567 | 42 | std::string const executable; | ||
568 | 43 | std::string const mir_socket; | ||
569 | 44 | QProcess process; | ||
570 | 45 | }; | ||
571 | 46 | |||
572 | 47 | } | ||
573 | 48 | |||
574 | 49 | #endif | ||
575 | 0 | 50 | ||
576 | === modified file 'src/main.cpp' | |||
577 | --- src/main.cpp 2013-10-31 22:00:19 +0000 | |||
578 | +++ src/main.cpp 2014-07-18 09:49:03 +0000 | |||
579 | @@ -1,5 +1,5 @@ | |||
580 | 1 | /* | 1 | /* |
582 | 2 | * Copyright © 2013 Canonical Ltd. | 2 | * Copyright © 2013-2014 Canonical Ltd. |
583 | 3 | * | 3 | * |
584 | 4 | * This program is free software: you can redistribute it and/or modify | 4 | * This program is free software: you can redistribute it and/or modify |
585 | 5 | * it under the terms of the GNU General Public License version 3 as | 5 | * it under the terms of the GNU General Public License version 3 as |
586 | @@ -14,17 +14,22 @@ | |||
587 | 14 | * along with this program. If not, see <http://www.gnu.org/licenses/>. | 14 | * along with this program. If not, see <http://www.gnu.org/licenses/>. |
588 | 15 | * | 15 | * |
589 | 16 | * Authored by: Robert Ancell <robert.ancell@canonical.com> | 16 | * Authored by: Robert Ancell <robert.ancell@canonical.com> |
590 | 17 | * Alexandros Frantzis <alexandros.frantzis@canonical.com> | ||
591 | 17 | */ | 18 | */ |
592 | 18 | 19 | ||
593 | 19 | #include "system_compositor.h" | 20 | #include "system_compositor.h" |
594 | 21 | #include "server_configuration.h" | ||
595 | 22 | |||
596 | 20 | #include <mir/report_exception.h> | 23 | #include <mir/report_exception.h> |
597 | 21 | #include <iostream> | 24 | #include <iostream> |
598 | 22 | 25 | ||
599 | 23 | int main(int argc, char *argv[]) | 26 | int main(int argc, char *argv[]) |
600 | 24 | try | 27 | try |
601 | 25 | { | 28 | { |
604 | 26 | SystemCompositor system_compositor; | 29 | auto const config = std::make_shared<usc::ServerConfiguration>(argc, argv); |
605 | 27 | system_compositor.run(argc, argv); | 30 | |
606 | 31 | usc::SystemCompositor system_compositor{config}; | ||
607 | 32 | system_compositor.run(); | ||
608 | 28 | 33 | ||
609 | 29 | return 0; | 34 | return 0; |
610 | 30 | } | 35 | } |
611 | 31 | 36 | ||
612 | === added file 'src/server_configuration.cpp' | |||
613 | --- src/server_configuration.cpp 1970-01-01 00:00:00 +0000 | |||
614 | +++ src/server_configuration.cpp 2014-07-18 09:49:03 +0000 | |||
615 | @@ -0,0 +1,196 @@ | |||
616 | 1 | /* | ||
617 | 2 | * Copyright © 2014 Canonical Ltd. | ||
618 | 3 | * | ||
619 | 4 | * This program is free software: you can redistribute it and/or modify | ||
620 | 5 | * it under the terms of the GNU General Public License version 3 as | ||
621 | 6 | * published by the Free Software Foundation. | ||
622 | 7 | * | ||
623 | 8 | * This program is distributed in the hope that it will be useful, | ||
624 | 9 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
625 | 10 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
626 | 11 | * GNU General Public License for more details. | ||
627 | 12 | * | ||
628 | 13 | * You should have received a copy of the GNU General Public License | ||
629 | 14 | * along with this program. If not, see <http://www.gnu.org/licenses/>. | ||
630 | 15 | * | ||
631 | 16 | * Authored by: Alexandros Frantzis <alexandros.frantzis@canonical.com> | ||
632 | 17 | */ | ||
633 | 18 | |||
634 | 19 | #include "server_configuration.h" | ||
635 | 20 | #include "external_spinner.h" | ||
636 | 21 | #include "session_coordinator.h" | ||
637 | 22 | #include "surface_coordinator.h" | ||
638 | 23 | #include "asio_dm_connection.h" | ||
639 | 24 | #include "session_switcher.h" | ||
640 | 25 | |||
641 | 26 | #include <mir/options/default_configuration.h> | ||
642 | 27 | #include <mir/input/cursor_listener.h> | ||
643 | 28 | #include <mir/server_status_listener.h> | ||
644 | 29 | #include <mir/shell/focus_controller.h> | ||
645 | 30 | #include <mir/scene/session.h> | ||
646 | 31 | |||
647 | 32 | #include <boost/program_options.hpp> | ||
648 | 33 | |||
649 | 34 | namespace msh = mir::shell; | ||
650 | 35 | namespace ms = mir::scene; | ||
651 | 36 | namespace mf = mir::frontend; | ||
652 | 37 | namespace mi = mir::input; | ||
653 | 38 | namespace mo = mir::options; | ||
654 | 39 | namespace po = boost::program_options; | ||
655 | 40 | |||
656 | 41 | namespace | ||
657 | 42 | { | ||
658 | 43 | |||
659 | 44 | class ConfigurationOptions : public mo::DefaultConfiguration | ||
660 | 45 | { | ||
661 | 46 | public: | ||
662 | 47 | ConfigurationOptions(int argc, char const* argv[]) : | ||
663 | 48 | DefaultConfiguration(argc, argv) | ||
664 | 49 | { | ||
665 | 50 | add_options() | ||
666 | 51 | ("from-dm-fd", po::value<int>(), "File descriptor of read end of pipe from display manager [int]") | ||
667 | 52 | ("to-dm-fd", po::value<int>(), "File descriptor of write end of pipe to display manager [int]") | ||
668 | 53 | ("blacklist", po::value<std::string>(), "Video blacklist regex to use") | ||
669 | 54 | ("version", "Show version of Unity System Compositor") | ||
670 | 55 | ("spinner", po::value<std::string>(), "Path to spinner executable") | ||
671 | 56 | ("public-socket", po::value<bool>(), "Make the socket file publicly writable") | ||
672 | 57 | ("enable-hardware-cursor", po::value<bool>(), "Enable the hardware cursor (disabled by default)") | ||
673 | 58 | ("inactivity-display-off-timeout", po::value<int>(), "The time in seconds before the screen is turned off when there are no active sessions") | ||
674 | 59 | ("inactivity-display-dim-timeout", po::value<int>(), "The time in seconds before the screen is dimmed when there are no active sessions") | ||
675 | 60 | ("shutdown-timeout", po::value<int>(), "The time in milli-seconds the power key must be held to initiate a clean system shutdown") | ||
676 | 61 | ("power-key-ignore-timeout", po::value<int>(), "The time in milli-seconds the power key must be held to ignore - must be less than shutdown-timeout") | ||
677 | 62 | ("disable-inactivity-policy", po::value<bool>(), "Disables handling user inactivity and power key"); | ||
678 | 63 | } | ||
679 | 64 | |||
680 | 65 | void parse_config_file( | ||
681 | 66 | boost::program_options::options_description& options_description, | ||
682 | 67 | mo::ProgramOption& options) const override | ||
683 | 68 | { | ||
684 | 69 | options.parse_file(options_description, "unity-system-compositor.conf"); | ||
685 | 70 | } | ||
686 | 71 | }; | ||
687 | 72 | |||
688 | 73 | } | ||
689 | 74 | |||
690 | 75 | usc::ServerConfiguration::ServerConfiguration(int argc, char** argv) | ||
691 | 76 | : mir::DefaultServerConfiguration( | ||
692 | 77 | std::make_shared<ConfigurationOptions>(argc, const_cast<char const **>(argv))) | ||
693 | 78 | { | ||
694 | 79 | } | ||
695 | 80 | |||
696 | 81 | std::shared_ptr<mi::CursorListener> | ||
697 | 82 | usc::ServerConfiguration::the_cursor_listener() | ||
698 | 83 | { | ||
699 | 84 | struct NullCursorListener : public mi::CursorListener | ||
700 | 85 | { | ||
701 | 86 | void cursor_moved_to(float, float) override | ||
702 | 87 | { | ||
703 | 88 | } | ||
704 | 89 | }; | ||
705 | 90 | |||
706 | 91 | // This is a workaround for u8 desktop preview in 14.04 for the lack of client cursor API. | ||
707 | 92 | // We need to disable the cursor for XMir but leave it on for the desktop preview. | ||
708 | 93 | // Luckily as it stands they run inside seperate instances of USC. ~racarr | ||
709 | 94 | if (enable_hardware_cursor()) | ||
710 | 95 | return mir::DefaultServerConfiguration::the_cursor_listener(); | ||
711 | 96 | else | ||
712 | 97 | return std::make_shared<NullCursorListener>(); | ||
713 | 98 | } | ||
714 | 99 | |||
715 | 100 | std::shared_ptr<mir::ServerStatusListener> | ||
716 | 101 | usc::ServerConfiguration::the_server_status_listener() | ||
717 | 102 | { | ||
718 | 103 | struct ServerStatusListener : public mir::ServerStatusListener | ||
719 | 104 | { | ||
720 | 105 | ServerStatusListener( | ||
721 | 106 | std::shared_ptr<msh::FocusController> const& focus_controller) | ||
722 | 107 | : focus_controller{focus_controller} | ||
723 | 108 | { | ||
724 | 109 | } | ||
725 | 110 | |||
726 | 111 | void paused() override | ||
727 | 112 | { | ||
728 | 113 | std::cerr << "pause" << std::endl; | ||
729 | 114 | |||
730 | 115 | if (auto active_session = weak_active_session().lock()) | ||
731 | 116 | active_session->set_lifecycle_state(mir_lifecycle_state_will_suspend); | ||
732 | 117 | } | ||
733 | 118 | |||
734 | 119 | void resumed() override | ||
735 | 120 | { | ||
736 | 121 | std::cerr << "resume" << std::endl; | ||
737 | 122 | |||
738 | 123 | if (auto active_session = weak_active_session().lock()) | ||
739 | 124 | active_session->set_lifecycle_state(mir_lifecycle_state_resumed); | ||
740 | 125 | } | ||
741 | 126 | |||
742 | 127 | void started() override | ||
743 | 128 | { | ||
744 | 129 | } | ||
745 | 130 | |||
746 | 131 | std::weak_ptr<ms::Session> weak_active_session() | ||
747 | 132 | { | ||
748 | 133 | return focus_controller->focussed_application(); | ||
749 | 134 | } | ||
750 | 135 | |||
751 | 136 | std::shared_ptr<msh::FocusController> const focus_controller; | ||
752 | 137 | }; | ||
753 | 138 | |||
754 | 139 | return std::make_shared<ServerStatusListener>(the_focus_controller()); | ||
755 | 140 | } | ||
756 | 141 | |||
757 | 142 | std::shared_ptr<mir::scene::SessionCoordinator> | ||
758 | 143 | usc::ServerConfiguration::wrap_session_coordinator( | ||
759 | 144 | std::shared_ptr<ms::SessionCoordinator> const& wrapped) | ||
760 | 145 | { | ||
761 | 146 | return std::make_shared<SessionCoordinator>( | ||
762 | 147 | wrapped, | ||
763 | 148 | the_session_switcher()); | ||
764 | 149 | } | ||
765 | 150 | |||
766 | 151 | std::shared_ptr<mir::scene::SurfaceCoordinator> | ||
767 | 152 | usc::ServerConfiguration::wrap_surface_coordinator( | ||
768 | 153 | std::shared_ptr<ms::SurfaceCoordinator> const& wrapped) | ||
769 | 154 | { | ||
770 | 155 | return std::make_shared<SurfaceCoordinator>( | ||
771 | 156 | wrapped, | ||
772 | 157 | the_session_switcher()); | ||
773 | 158 | } | ||
774 | 159 | |||
775 | 160 | std::shared_ptr<usc::Spinner> usc::ServerConfiguration::the_spinner() | ||
776 | 161 | { | ||
777 | 162 | return spinner( | ||
778 | 163 | [this] | ||
779 | 164 | { | ||
780 | 165 | return std::make_shared<ExternalSpinner>( | ||
781 | 166 | spinner_executable(), | ||
782 | 167 | get_socket_file()); | ||
783 | 168 | }); | ||
784 | 169 | } | ||
785 | 170 | |||
786 | 171 | std::shared_ptr<usc::SessionSwitcher> usc::ServerConfiguration::the_session_switcher() | ||
787 | 172 | { | ||
788 | 173 | return session_switcher( | ||
789 | 174 | [this] | ||
790 | 175 | { | ||
791 | 176 | return std::make_shared<SessionSwitcher>( | ||
792 | 177 | the_spinner()); | ||
793 | 178 | }); | ||
794 | 179 | } | ||
795 | 180 | |||
796 | 181 | std::shared_ptr<usc::DMMessageHandler> usc::ServerConfiguration::the_dm_message_handler() | ||
797 | 182 | { | ||
798 | 183 | return the_session_switcher(); | ||
799 | 184 | } | ||
800 | 185 | |||
801 | 186 | std::shared_ptr<usc::DMConnection> usc::ServerConfiguration::the_dm_connection() | ||
802 | 187 | { | ||
803 | 188 | return dm_connection( | ||
804 | 189 | [this] | ||
805 | 190 | { | ||
806 | 191 | return std::make_shared<AsioDMConnection>( | ||
807 | 192 | the_options()->get("from-dm-fd", -1), | ||
808 | 193 | the_options()->get("to-dm-fd", -1), | ||
809 | 194 | the_dm_message_handler()); | ||
810 | 195 | }); | ||
811 | 196 | } | ||
812 | 0 | 197 | ||
813 | === added file 'src/server_configuration.h' | |||
814 | --- src/server_configuration.h 1970-01-01 00:00:00 +0000 | |||
815 | +++ src/server_configuration.h 2014-07-18 09:49:03 +0000 | |||
816 | @@ -0,0 +1,119 @@ | |||
817 | 1 | /* | ||
818 | 2 | * Copyright © 2014 Canonical Ltd. | ||
819 | 3 | * | ||
820 | 4 | * This program is free software: you can redistribute it and/or modify | ||
821 | 5 | * it under the terms of the GNU General Public License version 3 as | ||
822 | 6 | * published by the Free Software Foundation. | ||
823 | 7 | * | ||
824 | 8 | * This program is distributed in the hope that it will be useful, | ||
825 | 9 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
826 | 10 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
827 | 11 | * GNU General Public License for more details. | ||
828 | 12 | * | ||
829 | 13 | * You should have received a copy of the GNU General Public License | ||
830 | 14 | * along with this program. If not, see <http://www.gnu.org/licenses/>. | ||
831 | 15 | * | ||
832 | 16 | * Authored by: Alexandros Frantzis <alexandros.frantzis@canonical.com> | ||
833 | 17 | */ | ||
834 | 18 | |||
835 | 19 | #ifndef USC_SERVER_CONFIGURATION_H_ | ||
836 | 20 | #define USC_SERVER_CONFIGURATION_H_ | ||
837 | 21 | |||
838 | 22 | #include <mir/default_server_configuration.h> | ||
839 | 23 | #include <mir/options/option.h> | ||
840 | 24 | |||
841 | 25 | namespace usc | ||
842 | 26 | { | ||
843 | 27 | class Spinner; | ||
844 | 28 | class SessionSwitcher; | ||
845 | 29 | class DMMessageHandler; | ||
846 | 30 | class DMConnection; | ||
847 | 31 | |||
848 | 32 | class ServerConfiguration : public mir::DefaultServerConfiguration | ||
849 | 33 | { | ||
850 | 34 | public: | ||
851 | 35 | ServerConfiguration(int argc, char** argv); | ||
852 | 36 | |||
853 | 37 | virtual std::shared_ptr<Spinner> the_spinner(); | ||
854 | 38 | virtual std::shared_ptr<DMMessageHandler> the_dm_message_handler(); | ||
855 | 39 | virtual std::shared_ptr<DMConnection> the_dm_connection(); | ||
856 | 40 | |||
857 | 41 | bool show_version() | ||
858 | 42 | { | ||
859 | 43 | return the_options()->is_set("version"); | ||
860 | 44 | } | ||
861 | 45 | |||
862 | 46 | int inactivity_display_off_timeout() | ||
863 | 47 | { | ||
864 | 48 | return the_options()->get("inactivity-display-off-timeout", 60); | ||
865 | 49 | } | ||
866 | 50 | |||
867 | 51 | int inactivity_display_dim_timeout() | ||
868 | 52 | { | ||
869 | 53 | return the_options()->get("inactivity-display-dim-timeout", 45); | ||
870 | 54 | } | ||
871 | 55 | |||
872 | 56 | int shutdown_timeout() | ||
873 | 57 | { | ||
874 | 58 | return the_options()->get("shutdown-timeout", 5000); | ||
875 | 59 | } | ||
876 | 60 | |||
877 | 61 | int power_key_ignore_timeout() | ||
878 | 62 | { | ||
879 | 63 | return the_options()->get("power-key-ignore-timeout", 1500); | ||
880 | 64 | } | ||
881 | 65 | |||
882 | 66 | bool enable_hardware_cursor() | ||
883 | 67 | { | ||
884 | 68 | return the_options()->get("enable-hardware-cursor", false); | ||
885 | 69 | } | ||
886 | 70 | |||
887 | 71 | bool disable_inactivity_policy() | ||
888 | 72 | { | ||
889 | 73 | return the_options()->get("disable-inactivity-policy", false); | ||
890 | 74 | } | ||
891 | 75 | |||
892 | 76 | std::string blacklist() | ||
893 | 77 | { | ||
894 | 78 | auto x = the_options()->get("blacklist", ""); | ||
895 | 79 | //boost::trim(x); | ||
896 | 80 | return x; | ||
897 | 81 | } | ||
898 | 82 | |||
899 | 83 | std::string spinner_executable() | ||
900 | 84 | { | ||
901 | 85 | // TODO: once our default spinner is ready for use everywhere, replace | ||
902 | 86 | // default value with DEFAULT_SPINNER instead of the empty string. | ||
903 | 87 | auto x = the_options()->get("spinner", ""); | ||
904 | 88 | //boost::trim(x); | ||
905 | 89 | return x; | ||
906 | 90 | } | ||
907 | 91 | |||
908 | 92 | bool public_socket() | ||
909 | 93 | { | ||
910 | 94 | return !the_options()->is_set("no-file") && the_options()->get("public-socket", true); | ||
911 | 95 | } | ||
912 | 96 | |||
913 | 97 | std::string get_socket_file() | ||
914 | 98 | { | ||
915 | 99 | // the_socket_file is private, so we have to re-implement it here | ||
916 | 100 | return the_options()->get("file", "/tmp/mir_socket"); | ||
917 | 101 | } | ||
918 | 102 | |||
919 | 103 | protected: | ||
920 | 104 | virtual std::shared_ptr<SessionSwitcher> the_session_switcher(); | ||
921 | 105 | std::shared_ptr<mir::input::CursorListener> the_cursor_listener() override; | ||
922 | 106 | std::shared_ptr<mir::ServerStatusListener> the_server_status_listener() override; | ||
923 | 107 | std::shared_ptr<mir::scene::SessionCoordinator> wrap_session_coordinator( | ||
924 | 108 | std::shared_ptr<mir::scene::SessionCoordinator> const& wrapped) override; | ||
925 | 109 | std::shared_ptr<mir::scene::SurfaceCoordinator> wrap_surface_coordinator( | ||
926 | 110 | std::shared_ptr<mir::scene::SurfaceCoordinator> const& wrapped) override; | ||
927 | 111 | |||
928 | 112 | mir::CachedPtr<Spinner> spinner; | ||
929 | 113 | mir::CachedPtr<DMConnection> dm_connection; | ||
930 | 114 | mir::CachedPtr<SessionSwitcher> session_switcher; | ||
931 | 115 | }; | ||
932 | 116 | |||
933 | 117 | } | ||
934 | 118 | |||
935 | 119 | #endif | ||
936 | 0 | 120 | ||
937 | === added file 'src/session_coordinator.cpp' | |||
938 | --- src/session_coordinator.cpp 1970-01-01 00:00:00 +0000 | |||
939 | +++ src/session_coordinator.cpp 2014-07-18 09:49:03 +0000 | |||
940 | @@ -0,0 +1,117 @@ | |||
941 | 1 | /* | ||
942 | 2 | * Copyright © 2014 Canonical Ltd. | ||
943 | 3 | * | ||
944 | 4 | * This program is free software: you can redistribute it and/or modify | ||
945 | 5 | * it under the terms of the GNU General Public License version 3 as | ||
946 | 6 | * published by the Free Software Foundation. | ||
947 | 7 | * | ||
948 | 8 | * This program is distributed in the hope that it will be useful, | ||
949 | 9 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
950 | 10 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
951 | 11 | * GNU General Public License for more details. | ||
952 | 12 | * | ||
953 | 13 | * You should have received a copy of the GNU General Public License | ||
954 | 14 | * along with this program. If not, see <http://www.gnu.org/licenses/>. | ||
955 | 15 | * | ||
956 | 16 | * Authored by: Alexandros Frantzis <alexandros.frantzis@canonical.com> | ||
957 | 17 | */ | ||
958 | 18 | |||
959 | 19 | #include "session_coordinator.h" | ||
960 | 20 | #include "session_switcher.h" | ||
961 | 21 | |||
962 | 22 | #include <mir/scene/session.h> | ||
963 | 23 | |||
964 | 24 | #include <iostream> | ||
965 | 25 | |||
966 | 26 | namespace msh = mir::shell; | ||
967 | 27 | namespace ms = mir::scene; | ||
968 | 28 | namespace mf = mir::frontend; | ||
969 | 29 | |||
970 | 30 | namespace | ||
971 | 31 | { | ||
972 | 32 | |||
973 | 33 | class UscSession : public usc::Session | ||
974 | 34 | { | ||
975 | 35 | public: | ||
976 | 36 | UscSession( | ||
977 | 37 | std::shared_ptr<ms::Session> const& scene_session, | ||
978 | 38 | msh::FocusController& focus_controller) | ||
979 | 39 | : scene_session{scene_session}, | ||
980 | 40 | focus_controller(focus_controller) | ||
981 | 41 | { | ||
982 | 42 | } | ||
983 | 43 | |||
984 | 44 | std::string name() | ||
985 | 45 | { | ||
986 | 46 | return scene_session->name(); | ||
987 | 47 | } | ||
988 | 48 | |||
989 | 49 | void show() override | ||
990 | 50 | { | ||
991 | 51 | scene_session->show(); | ||
992 | 52 | } | ||
993 | 53 | |||
994 | 54 | void hide() override | ||
995 | 55 | { | ||
996 | 56 | scene_session->hide(); | ||
997 | 57 | } | ||
998 | 58 | |||
999 | 59 | void raise_and_focus() override | ||
1000 | 60 | { | ||
1001 | 61 | focus_controller.set_focus_to(scene_session); | ||
1002 | 62 | } | ||
1003 | 63 | |||
1004 | 64 | bool corresponds_to(mir::scene::Session const* s) override | ||
1005 | 65 | { | ||
1006 | 66 | return scene_session.get() == s; | ||
1007 | 67 | } | ||
1008 | 68 | |||
1009 | 69 | std::shared_ptr<ms::Session> const scene_session; | ||
1010 | 70 | msh::FocusController& focus_controller; | ||
1011 | 71 | }; | ||
1012 | 72 | |||
1013 | 73 | } | ||
1014 | 74 | |||
1015 | 75 | usc::SessionCoordinator::SessionCoordinator( | ||
1016 | 76 | std::shared_ptr<ms::SessionCoordinator> const& wrapped, | ||
1017 | 77 | std::shared_ptr<SessionSwitcher> const& session_switcher) | ||
1018 | 78 | : msh::SessionCoordinatorWrapper{wrapped}, | ||
1019 | 79 | session_switcher{session_switcher} | ||
1020 | 80 | { | ||
1021 | 81 | } | ||
1022 | 82 | |||
1023 | 83 | std::shared_ptr<mf::Session> | ||
1024 | 84 | usc::SessionCoordinator::open_session( | ||
1025 | 85 | pid_t client_pid, | ||
1026 | 86 | std::string const& name, | ||
1027 | 87 | std::shared_ptr<mf::EventSink> const& sink) | ||
1028 | 88 | { | ||
1029 | 89 | std::cerr << "Opening session " << name << std::endl; | ||
1030 | 90 | |||
1031 | 91 | // We need ms::Session objects because that is what the focus controller | ||
1032 | 92 | // works with. But the mf::SessionCoordinator interface deals with mf::Session objects. | ||
1033 | 93 | // So we cast here since in practice, these objects are also ms::Sessions. | ||
1034 | 94 | auto orig = std::dynamic_pointer_cast<ms::Session>( | ||
1035 | 95 | msh::SessionCoordinatorWrapper::open_session(client_pid, name, sink)); | ||
1036 | 96 | if (!orig) | ||
1037 | 97 | { | ||
1038 | 98 | std::cerr << "Unexpected non-shell session" << std::endl; | ||
1039 | 99 | return std::shared_ptr<mf::Session>(); | ||
1040 | 100 | } | ||
1041 | 101 | |||
1042 | 102 | auto const usc_session = std::make_shared<UscSession>(orig, *this); | ||
1043 | 103 | |||
1044 | 104 | session_switcher->add(usc_session, client_pid); | ||
1045 | 105 | |||
1046 | 106 | return orig; | ||
1047 | 107 | } | ||
1048 | 108 | |||
1049 | 109 | void usc::SessionCoordinator::close_session( | ||
1050 | 110 | std::shared_ptr<mf::Session> const& session) | ||
1051 | 111 | { | ||
1052 | 112 | std::cerr << "Closing session " << session->name() << std::endl; | ||
1053 | 113 | |||
1054 | 114 | msh::SessionCoordinatorWrapper::close_session(session); | ||
1055 | 115 | |||
1056 | 116 | session_switcher->remove(session->name()); | ||
1057 | 117 | } | ||
1058 | 0 | 118 | ||
1059 | === added file 'src/session_coordinator.h' | |||
1060 | --- src/session_coordinator.h 1970-01-01 00:00:00 +0000 | |||
1061 | +++ src/session_coordinator.h 2014-07-18 09:49:03 +0000 | |||
1062 | @@ -0,0 +1,54 @@ | |||
1063 | 1 | /* | ||
1064 | 2 | * Copyright © 2014 Canonical Ltd. | ||
1065 | 3 | * | ||
1066 | 4 | * This program is free software: you can redistribute it and/or modify | ||
1067 | 5 | * it under the terms of the GNU General Public License version 3 as | ||
1068 | 6 | * published by the Free Software Foundation. | ||
1069 | 7 | * | ||
1070 | 8 | * This program is distributed in the hope that it will be useful, | ||
1071 | 9 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
1072 | 10 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
1073 | 11 | * GNU General Public License for more details. | ||
1074 | 12 | * | ||
1075 | 13 | * You should have received a copy of the GNU General Public License | ||
1076 | 14 | * along with this program. If not, see <http://www.gnu.org/licenses/>. | ||
1077 | 15 | * | ||
1078 | 16 | * Authored by: Alexandros Frantzis <alexandros.frantzis@canonical.com> | ||
1079 | 17 | */ | ||
1080 | 18 | |||
1081 | 19 | #ifndef USC_SESSION_COORDINATOR_H_ | ||
1082 | 20 | #define USC_SESSION_COORDINATOR_H_ | ||
1083 | 21 | |||
1084 | 22 | #include <mir/shell/session_coordinator_wrapper.h> | ||
1085 | 23 | |||
1086 | 24 | #include <memory> | ||
1087 | 25 | |||
1088 | 26 | namespace mir | ||
1089 | 27 | { | ||
1090 | 28 | namespace scene { class SurfaceCoordinator; } | ||
1091 | 29 | } | ||
1092 | 30 | |||
1093 | 31 | namespace usc | ||
1094 | 32 | { | ||
1095 | 33 | class SessionSwitcher; | ||
1096 | 34 | |||
1097 | 35 | class SessionCoordinator : public mir::shell::SessionCoordinatorWrapper | ||
1098 | 36 | { | ||
1099 | 37 | public: | ||
1100 | 38 | SessionCoordinator( | ||
1101 | 39 | std::shared_ptr<mir::scene::SessionCoordinator> const& wrapped, | ||
1102 | 40 | std::shared_ptr<SessionSwitcher> const& session_switcher); | ||
1103 | 41 | |||
1104 | 42 | private: | ||
1105 | 43 | std::shared_ptr<mir::frontend::Session> open_session( | ||
1106 | 44 | pid_t client_pid, | ||
1107 | 45 | std::string const& name, | ||
1108 | 46 | std::shared_ptr<mir::frontend::EventSink> const& sink) override; | ||
1109 | 47 | void close_session(std::shared_ptr<mir::frontend::Session> const& session) override; | ||
1110 | 48 | |||
1111 | 49 | std::shared_ptr<SessionSwitcher> const session_switcher; | ||
1112 | 50 | }; | ||
1113 | 51 | |||
1114 | 52 | } | ||
1115 | 53 | |||
1116 | 54 | #endif | ||
1117 | 0 | 55 | ||
1118 | === added file 'src/session_switcher.cpp' | |||
1119 | --- src/session_switcher.cpp 1970-01-01 00:00:00 +0000 | |||
1120 | +++ src/session_switcher.cpp 2014-07-18 09:49:03 +0000 | |||
1121 | @@ -0,0 +1,190 @@ | |||
1122 | 1 | /* | ||
1123 | 2 | * Copyright © 2014 Canonical Ltd. | ||
1124 | 3 | * | ||
1125 | 4 | * This program is free software: you can redistribute it and/or modify | ||
1126 | 5 | * it under the terms of the GNU General Public License version 3 as | ||
1127 | 6 | * published by the Free Software Foundation. | ||
1128 | 7 | * | ||
1129 | 8 | * This program is distributed in the hope that it will be useful, | ||
1130 | 9 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
1131 | 10 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
1132 | 11 | * GNU General Public License for more details. | ||
1133 | 12 | * | ||
1134 | 13 | * You should have received a copy of the GNU General Public License | ||
1135 | 14 | * along with this program. If not, see <http://www.gnu.org/licenses/>. | ||
1136 | 15 | * | ||
1137 | 16 | * Authored by: Alexandros Frantzis <alexandros.frantzis@canonical.com> | ||
1138 | 17 | */ | ||
1139 | 18 | |||
1140 | 19 | #include "session_switcher.h" | ||
1141 | 20 | #include "spinner.h" | ||
1142 | 21 | |||
1143 | 22 | usc::SessionSwitcher::SessionSwitcher(std::shared_ptr<Spinner> const& spinner) | ||
1144 | 23 | : spinner_process{spinner}, | ||
1145 | 24 | booting{true} | ||
1146 | 25 | { | ||
1147 | 26 | } | ||
1148 | 27 | |||
1149 | 28 | void usc::SessionSwitcher::add(std::shared_ptr<Session> const& session, pid_t pid) | ||
1150 | 29 | { | ||
1151 | 30 | std::lock_guard<std::mutex> lock{mutex}; | ||
1152 | 31 | |||
1153 | 32 | if (pid == spinner_process->pid()) | ||
1154 | 33 | spinner_name = session->name(); | ||
1155 | 34 | |||
1156 | 35 | sessions[session->name()] = SessionInfo(session); | ||
1157 | 36 | update_displayed_sessions(); | ||
1158 | 37 | } | ||
1159 | 38 | |||
1160 | 39 | void usc::SessionSwitcher::remove(std::string const& name) | ||
1161 | 40 | { | ||
1162 | 41 | std::lock_guard<std::mutex> lock{mutex}; | ||
1163 | 42 | |||
1164 | 43 | if (name == spinner_name) | ||
1165 | 44 | spinner_name = ""; | ||
1166 | 45 | |||
1167 | 46 | sessions.erase(name); | ||
1168 | 47 | update_displayed_sessions(); | ||
1169 | 48 | } | ||
1170 | 49 | |||
1171 | 50 | void usc::SessionSwitcher::set_active_session(std::string const& name) | ||
1172 | 51 | { | ||
1173 | 52 | std::lock_guard<std::mutex> lock{mutex}; | ||
1174 | 53 | |||
1175 | 54 | active_name = name; | ||
1176 | 55 | update_displayed_sessions(); | ||
1177 | 56 | } | ||
1178 | 57 | |||
1179 | 58 | void usc::SessionSwitcher::set_next_session(std::string const& name) | ||
1180 | 59 | { | ||
1181 | 60 | std::lock_guard<std::mutex> lock{mutex}; | ||
1182 | 61 | |||
1183 | 62 | next_name = name; | ||
1184 | 63 | update_displayed_sessions(); | ||
1185 | 64 | } | ||
1186 | 65 | |||
1187 | 66 | void usc::SessionSwitcher::mark_ready(mir::scene::Session const* scene_session) | ||
1188 | 67 | { | ||
1189 | 68 | std::lock_guard<std::mutex> lock{mutex}; | ||
1190 | 69 | |||
1191 | 70 | for (auto& pair : sessions) | ||
1192 | 71 | { | ||
1193 | 72 | if (pair.second.session && pair.second.session->corresponds_to(scene_session)) | ||
1194 | 73 | pair.second.ready = true; | ||
1195 | 74 | } | ||
1196 | 75 | |||
1197 | 76 | update_displayed_sessions(); | ||
1198 | 77 | } | ||
1199 | 78 | |||
1200 | 79 | void usc::SessionSwitcher::update_displayed_sessions() | ||
1201 | 80 | { | ||
1202 | 81 | hide_uninteresting_sessions(); | ||
1203 | 82 | |||
1204 | 83 | bool show_spinner = false; | ||
1205 | 84 | ShowMode show_spinner_mode{ShowMode::as_next}; | ||
1206 | 85 | bool const allowed_to_display_active = | ||
1207 | 86 | is_session_ready_for_display(next_name) || | ||
1208 | 87 | !is_session_expected_to_become_ready(next_name) || | ||
1209 | 88 | !booting; | ||
1210 | 89 | bool show_active = false; | ||
1211 | 90 | |||
1212 | 91 | if (allowed_to_display_active && is_session_ready_for_display(active_name)) | ||
1213 | 92 | { | ||
1214 | 93 | show_session(active_name, ShowMode::as_active); | ||
1215 | 94 | show_active = true; | ||
1216 | 95 | booting = false; | ||
1217 | 96 | } | ||
1218 | 97 | else if (is_session_expected_to_become_ready(active_name)) | ||
1219 | 98 | { | ||
1220 | 99 | show_spinner = true; | ||
1221 | 100 | show_spinner_mode = ShowMode::as_active; | ||
1222 | 101 | } | ||
1223 | 102 | |||
1224 | 103 | bool const allowed_to_display_next = !show_spinner && show_active; | ||
1225 | 104 | |||
1226 | 105 | if (allowed_to_display_next) | ||
1227 | 106 | { | ||
1228 | 107 | if (is_session_ready_for_display(next_name)) | ||
1229 | 108 | { | ||
1230 | 109 | show_session(next_name, ShowMode::as_next); | ||
1231 | 110 | } | ||
1232 | 111 | else if (is_session_expected_to_become_ready(next_name)) | ||
1233 | 112 | { | ||
1234 | 113 | show_spinner = true; | ||
1235 | 114 | show_spinner_mode = ShowMode::as_next; | ||
1236 | 115 | } | ||
1237 | 116 | } | ||
1238 | 117 | else if (is_session_ready_for_display(next_name)) | ||
1239 | 118 | { | ||
1240 | 119 | hide_session(next_name); | ||
1241 | 120 | } | ||
1242 | 121 | |||
1243 | 122 | if (show_spinner) | ||
1244 | 123 | ensure_spinner_will_be_shown(show_spinner_mode); | ||
1245 | 124 | else | ||
1246 | 125 | ensure_spinner_is_not_running(); | ||
1247 | 126 | } | ||
1248 | 127 | |||
1249 | 128 | void usc::SessionSwitcher::hide_uninteresting_sessions() | ||
1250 | 129 | { | ||
1251 | 130 | for (auto const& pair : sessions) | ||
1252 | 131 | { | ||
1253 | 132 | if (pair.second.session->name() != active_name && | ||
1254 | 133 | pair.second.session->name() != next_name) | ||
1255 | 134 | { | ||
1256 | 135 | pair.second.session->hide(); | ||
1257 | 136 | } | ||
1258 | 137 | } | ||
1259 | 138 | } | ||
1260 | 139 | |||
1261 | 140 | bool usc::SessionSwitcher::is_session_ready_for_display(std::string const& name) | ||
1262 | 141 | { | ||
1263 | 142 | auto const iter = sessions.find(name); | ||
1264 | 143 | if (iter == sessions.end()) | ||
1265 | 144 | return false; | ||
1266 | 145 | |||
1267 | 146 | return iter->second.session && iter->second.ready; | ||
1268 | 147 | } | ||
1269 | 148 | |||
1270 | 149 | bool usc::SessionSwitcher::is_session_expected_to_become_ready(std::string const& name) | ||
1271 | 150 | { | ||
1272 | 151 | return !name.empty(); | ||
1273 | 152 | } | ||
1274 | 153 | |||
1275 | 154 | void usc::SessionSwitcher::show_session( | ||
1276 | 155 | std::string const& name, | ||
1277 | 156 | ShowMode show_mode) | ||
1278 | 157 | { | ||
1279 | 158 | auto& session = sessions[name].session; | ||
1280 | 159 | |||
1281 | 160 | if (show_mode == ShowMode::as_active) | ||
1282 | 161 | session->raise_and_focus(); | ||
1283 | 162 | |||
1284 | 163 | session->show(); | ||
1285 | 164 | } | ||
1286 | 165 | |||
1287 | 166 | void usc::SessionSwitcher::hide_session(std::string const& name) | ||
1288 | 167 | { | ||
1289 | 168 | auto& session = sessions[name].session; | ||
1290 | 169 | session->hide(); | ||
1291 | 170 | } | ||
1292 | 171 | |||
1293 | 172 | void usc::SessionSwitcher::ensure_spinner_will_be_shown(ShowMode show_mode) | ||
1294 | 173 | { | ||
1295 | 174 | auto const iter = sessions.find(spinner_name); | ||
1296 | 175 | if (iter == sessions.end()) | ||
1297 | 176 | { | ||
1298 | 177 | spinner_process->ensure_running(); | ||
1299 | 178 | } | ||
1300 | 179 | else | ||
1301 | 180 | { | ||
1302 | 181 | if (show_mode == ShowMode::as_active) | ||
1303 | 182 | iter->second.session->raise_and_focus(); | ||
1304 | 183 | iter->second.session->show(); | ||
1305 | 184 | } | ||
1306 | 185 | } | ||
1307 | 186 | |||
1308 | 187 | void usc::SessionSwitcher::ensure_spinner_is_not_running() | ||
1309 | 188 | { | ||
1310 | 189 | spinner_process->kill(); | ||
1311 | 190 | } | ||
1312 | 0 | 191 | ||
1313 | === added file 'src/session_switcher.h' | |||
1314 | --- src/session_switcher.h 1970-01-01 00:00:00 +0000 | |||
1315 | +++ src/session_switcher.h 2014-07-18 09:49:03 +0000 | |||
1316 | @@ -0,0 +1,98 @@ | |||
1317 | 1 | /* | ||
1318 | 2 | * Copyright © 2014 Canonical Ltd. | ||
1319 | 3 | * | ||
1320 | 4 | * This program is free software: you can redistribute it and/or modify | ||
1321 | 5 | * it under the terms of the GNU General Public License version 3 as | ||
1322 | 6 | * published by the Free Software Foundation. | ||
1323 | 7 | * | ||
1324 | 8 | * This program is distributed in the hope that it will be useful, | ||
1325 | 9 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
1326 | 10 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
1327 | 11 | * GNU General Public License for more details. | ||
1328 | 12 | * | ||
1329 | 13 | * You should have received a copy of the GNU General Public License | ||
1330 | 14 | * along with this program. If not, see <http://www.gnu.org/licenses/>. | ||
1331 | 15 | * | ||
1332 | 16 | * Authored by: Alexandros Frantzis <alexandros.frantzis@canonical.com> | ||
1333 | 17 | */ | ||
1334 | 18 | |||
1335 | 19 | #ifndef USC_SESSION_SWITCHER_H_ | ||
1336 | 20 | #define USC_SESSION_SWITCHER_H_ | ||
1337 | 21 | |||
1338 | 22 | #include "dm_connection.h" | ||
1339 | 23 | |||
1340 | 24 | #include <map> | ||
1341 | 25 | #include <memory> | ||
1342 | 26 | #include <mutex> | ||
1343 | 27 | |||
1344 | 28 | namespace mir { namespace scene { class Session; }}; | ||
1345 | 29 | namespace usc | ||
1346 | 30 | { | ||
1347 | 31 | class Spinner; | ||
1348 | 32 | |||
1349 | 33 | class Session | ||
1350 | 34 | { | ||
1351 | 35 | public: | ||
1352 | 36 | virtual ~Session() = default; | ||
1353 | 37 | |||
1354 | 38 | virtual std::string name() = 0; | ||
1355 | 39 | virtual void show() = 0; | ||
1356 | 40 | virtual void hide() = 0; | ||
1357 | 41 | virtual void raise_and_focus() = 0; | ||
1358 | 42 | virtual bool corresponds_to(mir::scene::Session const*) = 0; | ||
1359 | 43 | |||
1360 | 44 | protected: | ||
1361 | 45 | Session() = default; | ||
1362 | 46 | Session(Session const&) = delete; | ||
1363 | 47 | Session& operator=(Session const&) = delete; | ||
1364 | 48 | }; | ||
1365 | 49 | |||
1366 | 50 | class SessionSwitcher : public DMMessageHandler | ||
1367 | 51 | { | ||
1368 | 52 | public: | ||
1369 | 53 | SessionSwitcher(std::shared_ptr<Spinner> const& spinner); | ||
1370 | 54 | |||
1371 | 55 | void add(std::shared_ptr<Session> const& session, pid_t pid); | ||
1372 | 56 | void remove(std::string const& name); | ||
1373 | 57 | void mark_ready(mir::scene::Session const*); | ||
1374 | 58 | |||
1375 | 59 | /* From DMMessageHandler */ | ||
1376 | 60 | void set_active_session(std::string const& name) override; | ||
1377 | 61 | void set_next_session(std::string const& name) override; | ||
1378 | 62 | |||
1379 | 63 | private: | ||
1380 | 64 | enum class ShowMode { as_active, as_next }; | ||
1381 | 65 | |||
1382 | 66 | void update_displayed_sessions(); | ||
1383 | 67 | void hide_uninteresting_sessions(); | ||
1384 | 68 | bool is_session_ready_for_display(std::string const& name); | ||
1385 | 69 | bool is_session_expected_to_become_ready(std::string const& name); | ||
1386 | 70 | void show_session(std::string const& name, ShowMode show_mode); | ||
1387 | 71 | void hide_session(std::string const& name); | ||
1388 | 72 | void ensure_spinner_will_be_shown(ShowMode show_mode); | ||
1389 | 73 | void ensure_spinner_is_not_running(); | ||
1390 | 74 | |||
1391 | 75 | struct SessionInfo | ||
1392 | 76 | { | ||
1393 | 77 | SessionInfo() = default; | ||
1394 | 78 | SessionInfo(std::shared_ptr<Session> session) | ||
1395 | 79 | : session{session} | ||
1396 | 80 | { | ||
1397 | 81 | } | ||
1398 | 82 | std::shared_ptr<Session> session; | ||
1399 | 83 | bool ready = false; | ||
1400 | 84 | }; | ||
1401 | 85 | |||
1402 | 86 | std::mutex mutex; | ||
1403 | 87 | std::shared_ptr<Spinner> const spinner_process; | ||
1404 | 88 | std::map<std::string, SessionInfo> sessions; | ||
1405 | 89 | std::string active_name; | ||
1406 | 90 | std::string next_name; | ||
1407 | 91 | std::string spinner_name; | ||
1408 | 92 | bool booting; | ||
1409 | 93 | }; | ||
1410 | 94 | |||
1411 | 95 | } | ||
1412 | 96 | |||
1413 | 97 | #endif | ||
1414 | 98 | |||
1415 | 0 | 99 | ||
1416 | === added file 'src/spinner.h' | |||
1417 | --- src/spinner.h 1970-01-01 00:00:00 +0000 | |||
1418 | +++ src/spinner.h 2014-07-18 09:49:03 +0000 | |||
1419 | @@ -0,0 +1,44 @@ | |||
1420 | 1 | /* | ||
1421 | 2 | * Copyright © 2014 Canonical Ltd. | ||
1422 | 3 | * | ||
1423 | 4 | * This program is free software: you can redistribute it and/or modify | ||
1424 | 5 | * it under the terms of the GNU General Public License version 3 as | ||
1425 | 6 | * published by the Free Software Foundation. | ||
1426 | 7 | * | ||
1427 | 8 | * This program is distributed in the hope that it will be useful, | ||
1428 | 9 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
1429 | 10 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
1430 | 11 | * GNU General Public License for more details. | ||
1431 | 12 | * | ||
1432 | 13 | * You should have received a copy of the GNU General Public License | ||
1433 | 14 | * along with this program. If not, see <http://www.gnu.org/licenses/>. | ||
1434 | 15 | * | ||
1435 | 16 | * Authored by: Alexandros Frantzis <alexandros.frantzis@canonical.com> | ||
1436 | 17 | */ | ||
1437 | 18 | |||
1438 | 19 | #ifndef USC_SPINNER_H_ | ||
1439 | 20 | #define USC_SPINNER_H_ | ||
1440 | 21 | |||
1441 | 22 | #include <sys/types.h> | ||
1442 | 23 | |||
1443 | 24 | namespace usc | ||
1444 | 25 | { | ||
1445 | 26 | |||
1446 | 27 | class Spinner | ||
1447 | 28 | { | ||
1448 | 29 | public: | ||
1449 | 30 | virtual ~Spinner() = default; | ||
1450 | 31 | |||
1451 | 32 | virtual void ensure_running() = 0; | ||
1452 | 33 | virtual void kill() = 0; | ||
1453 | 34 | virtual pid_t pid() = 0; | ||
1454 | 35 | |||
1455 | 36 | protected: | ||
1456 | 37 | Spinner() = default; | ||
1457 | 38 | Spinner(Spinner const&) = delete; | ||
1458 | 39 | Spinner& operator=(Spinner const&) = delete; | ||
1459 | 40 | }; | ||
1460 | 41 | |||
1461 | 42 | } | ||
1462 | 43 | |||
1463 | 44 | #endif | ||
1464 | 0 | 45 | ||
1465 | === added file 'src/surface_coordinator.cpp' | |||
1466 | --- src/surface_coordinator.cpp 1970-01-01 00:00:00 +0000 | |||
1467 | +++ src/surface_coordinator.cpp 2014-07-18 09:49:03 +0000 | |||
1468 | @@ -0,0 +1,86 @@ | |||
1469 | 1 | /* | ||
1470 | 2 | * Copyright © 2014 Canonical Ltd. | ||
1471 | 3 | * | ||
1472 | 4 | * This program is free software: you can redistribute it and/or modify | ||
1473 | 5 | * it under the terms of the GNU General Public License version 3 as | ||
1474 | 6 | * published by the Free Software Foundation. | ||
1475 | 7 | * | ||
1476 | 8 | * This program is distributed in the hope that it will be useful, | ||
1477 | 9 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
1478 | 10 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
1479 | 11 | * GNU General Public License for more details. | ||
1480 | 12 | * | ||
1481 | 13 | * You should have received a copy of the GNU General Public License | ||
1482 | 14 | * along with this program. If not, see <http://www.gnu.org/licenses/>. | ||
1483 | 15 | * | ||
1484 | 16 | * Authored by: Alexandros Frantzis <alexandros.frantzis@canonical.com> | ||
1485 | 17 | */ | ||
1486 | 18 | |||
1487 | 19 | #include "surface_coordinator.h" | ||
1488 | 20 | #include "session_switcher.h" | ||
1489 | 21 | |||
1490 | 22 | #include <mir/scene/null_surface_observer.h> | ||
1491 | 23 | #include <mir/scene/surface.h> | ||
1492 | 24 | |||
1493 | 25 | namespace ms = mir::scene; | ||
1494 | 26 | namespace msh = mir::shell; | ||
1495 | 27 | |||
1496 | 28 | namespace | ||
1497 | 29 | { | ||
1498 | 30 | |||
1499 | 31 | struct SessionReadyObserver : ms::NullSurfaceObserver, | ||
1500 | 32 | std::enable_shared_from_this<SessionReadyObserver> | ||
1501 | 33 | { | ||
1502 | 34 | SessionReadyObserver( | ||
1503 | 35 | std::shared_ptr<usc::SessionSwitcher> const& switcher, | ||
1504 | 36 | std::shared_ptr<ms::Surface> const& surface, | ||
1505 | 37 | ms::Session const* session) | ||
1506 | 38 | : switcher{switcher}, | ||
1507 | 39 | surface{surface}, | ||
1508 | 40 | session{session} | ||
1509 | 41 | { | ||
1510 | 42 | } | ||
1511 | 43 | |||
1512 | 44 | void frame_posted(int) override | ||
1513 | 45 | { | ||
1514 | 46 | ++num_frames_posted; | ||
1515 | 47 | if (num_frames_posted == num_frames_for_session_ready) | ||
1516 | 48 | { | ||
1517 | 49 | switcher->mark_ready(session); | ||
1518 | 50 | surface->remove_observer(shared_from_this()); | ||
1519 | 51 | } | ||
1520 | 52 | } | ||
1521 | 53 | |||
1522 | 54 | std::shared_ptr<usc::SessionSwitcher> const switcher; | ||
1523 | 55 | std::shared_ptr<ms::Surface> const surface; | ||
1524 | 56 | ms::Session const* const session; | ||
1525 | 57 | // We need to wait for the second frame before marking the session | ||
1526 | 58 | // as ready. The first frame posted from sessions is a blank frame. | ||
1527 | 59 | // TODO: Solve this issue at its root and remove this workaround | ||
1528 | 60 | int const num_frames_for_session_ready{2}; | ||
1529 | 61 | int num_frames_posted{0}; | ||
1530 | 62 | }; | ||
1531 | 63 | |||
1532 | 64 | } | ||
1533 | 65 | |||
1534 | 66 | usc::SurfaceCoordinator::SurfaceCoordinator( | ||
1535 | 67 | std::shared_ptr<ms::SurfaceCoordinator> const& wrapped, | ||
1536 | 68 | std::shared_ptr<SessionSwitcher> const& session_switcher) | ||
1537 | 69 | : msh::SurfaceCoordinatorWrapper{wrapped}, | ||
1538 | 70 | session_switcher{session_switcher} | ||
1539 | 71 | { | ||
1540 | 72 | } | ||
1541 | 73 | |||
1542 | 74 | std::shared_ptr<ms::Surface> usc::SurfaceCoordinator::add_surface( | ||
1543 | 75 | ms::SurfaceCreationParameters const& params, | ||
1544 | 76 | ms::Session* session) | ||
1545 | 77 | { | ||
1546 | 78 | auto const surface = msh::SurfaceCoordinatorWrapper::add_surface(params, session); | ||
1547 | 79 | |||
1548 | 80 | auto const session_ready_observer = std::make_shared<SessionReadyObserver>( | ||
1549 | 81 | session_switcher, surface, session); | ||
1550 | 82 | |||
1551 | 83 | surface->add_observer(session_ready_observer); | ||
1552 | 84 | |||
1553 | 85 | return surface; | ||
1554 | 86 | } | ||
1555 | 0 | 87 | ||
1556 | === added file 'src/surface_coordinator.h' | |||
1557 | --- src/surface_coordinator.h 1970-01-01 00:00:00 +0000 | |||
1558 | +++ src/surface_coordinator.h 2014-07-18 09:49:03 +0000 | |||
1559 | @@ -0,0 +1,47 @@ | |||
1560 | 1 | /* | ||
1561 | 2 | * Copyright © 2014 Canonical Ltd. | ||
1562 | 3 | * | ||
1563 | 4 | * This program is free software: you can redistribute it and/or modify | ||
1564 | 5 | * it under the terms of the GNU General Public License version 3 as | ||
1565 | 6 | * published by the Free Software Foundation. | ||
1566 | 7 | * | ||
1567 | 8 | * This program is distributed in the hope that it will be useful, | ||
1568 | 9 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
1569 | 10 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
1570 | 11 | * GNU General Public License for more details. | ||
1571 | 12 | * | ||
1572 | 13 | * You should have received a copy of the GNU General Public License | ||
1573 | 14 | * along with this program. If not, see <http://www.gnu.org/licenses/>. | ||
1574 | 15 | * | ||
1575 | 16 | * Authored by: Alexandros Frantzis <alexandros.frantzis@canonical.com> | ||
1576 | 17 | */ | ||
1577 | 18 | |||
1578 | 19 | #ifndef USC_SURFACE_COORDINATOR_H_ | ||
1579 | 20 | #define USC_SURFACE_COORDINATOR_H_ | ||
1580 | 21 | |||
1581 | 22 | #include <mir/shell/surface_coordinator_wrapper.h> | ||
1582 | 23 | |||
1583 | 24 | #include <memory> | ||
1584 | 25 | |||
1585 | 26 | namespace usc | ||
1586 | 27 | { | ||
1587 | 28 | class SessionSwitcher; | ||
1588 | 29 | |||
1589 | 30 | class SurfaceCoordinator : public mir::shell::SurfaceCoordinatorWrapper | ||
1590 | 31 | { | ||
1591 | 32 | public: | ||
1592 | 33 | SurfaceCoordinator( | ||
1593 | 34 | std::shared_ptr<mir::scene::SurfaceCoordinator> const& wrapped, | ||
1594 | 35 | std::shared_ptr<SessionSwitcher> const& session_switcher); | ||
1595 | 36 | |||
1596 | 37 | private: | ||
1597 | 38 | std::shared_ptr<mir::scene::Surface> add_surface( | ||
1598 | 39 | mir::scene::SurfaceCreationParameters const& params, | ||
1599 | 40 | mir::scene::Session* session) override; | ||
1600 | 41 | |||
1601 | 42 | std::shared_ptr<SessionSwitcher> const session_switcher; | ||
1602 | 43 | }; | ||
1603 | 44 | |||
1604 | 45 | } | ||
1605 | 46 | |||
1606 | 47 | #endif | ||
1607 | 0 | 48 | ||
1608 | === modified file 'src/system_compositor.cpp' | |||
1609 | --- src/system_compositor.cpp 2014-07-15 19:26:10 +0000 | |||
1610 | +++ src/system_compositor.cpp 2014-07-18 09:49:03 +0000 | |||
1611 | @@ -1,5 +1,5 @@ | |||
1612 | 1 | /* | 1 | /* |
1614 | 2 | * Copyright © 2013 Canonical Ltd. | 2 | * Copyright © 2013-2014 Canonical Ltd. |
1615 | 3 | * | 3 | * |
1616 | 4 | * This program is free software: you can redistribute it and/or modify | 4 | * This program is free software: you can redistribute it and/or modify |
1617 | 5 | * it under the terms of the GNU General Public License version 3 as | 5 | * it under the terms of the GNU General Public License version 3 as |
1618 | @@ -14,10 +14,14 @@ | |||
1619 | 14 | * along with this program. If not, see <http://www.gnu.org/licenses/>. | 14 | * along with this program. If not, see <http://www.gnu.org/licenses/>. |
1620 | 15 | * | 15 | * |
1621 | 16 | * Authored by: Robert Ancell <robert.ancell@canonical.com> | 16 | * Authored by: Robert Ancell <robert.ancell@canonical.com> |
1622 | 17 | * Alexandros Frantzis <alexandros.frantzis@canonical.com> | ||
1623 | 17 | */ | 18 | */ |
1624 | 18 | 19 | ||
1625 | 19 | 20 | ||
1626 | 20 | #include "system_compositor.h" | 21 | #include "system_compositor.h" |
1627 | 22 | #include "server_configuration.h" | ||
1628 | 23 | #include "dm_connection.h" | ||
1629 | 24 | #include "spinner.h" | ||
1630 | 21 | #include "screen_state_handler.h" | 25 | #include "screen_state_handler.h" |
1631 | 22 | #include "powerkey_handler.h" | 26 | #include "powerkey_handler.h" |
1632 | 23 | 27 | ||
1633 | @@ -26,21 +30,9 @@ | |||
1634 | 26 | // method declarations | 30 | // method declarations |
1635 | 27 | #undef signals | 31 | #undef signals |
1636 | 28 | 32 | ||
1637 | 33 | #include <mir/input/composite_event_filter.h> | ||
1638 | 29 | #include <mir/run_mir.h> | 34 | #include <mir/run_mir.h> |
1639 | 30 | #include <mir/abnormal_exit.h> | 35 | #include <mir/abnormal_exit.h> |
1640 | 31 | #include <mir/compositor/compositor_id.h> | ||
1641 | 32 | #include <mir/compositor/scene.h> | ||
1642 | 33 | #include <mir/compositor/scene_element.h> | ||
1643 | 34 | #include <mir/default_server_configuration.h> | ||
1644 | 35 | #include <mir/options/default_configuration.h> | ||
1645 | 36 | #include <mir/frontend/shell.h> | ||
1646 | 37 | #include <mir/scene/surface.h> | ||
1647 | 38 | #include <mir/scene/surface_coordinator.h> | ||
1648 | 39 | #include <mir/scene/session.h> | ||
1649 | 40 | #include <mir/server_status_listener.h> | ||
1650 | 41 | #include <mir/shell/focus_controller.h> | ||
1651 | 42 | #include <mir/input/cursor_listener.h> | ||
1652 | 43 | #include <mir/input/composite_event_filter.h> | ||
1653 | 44 | #include <mir/main_loop.h> | 36 | #include <mir/main_loop.h> |
1654 | 45 | 37 | ||
1655 | 46 | #include <cerrno> | 38 | #include <cerrno> |
1656 | @@ -49,664 +41,16 @@ | |||
1657 | 49 | #include <thread> | 41 | #include <thread> |
1658 | 50 | #include <regex.h> | 42 | #include <regex.h> |
1659 | 51 | #include <GLES2/gl2.h> | 43 | #include <GLES2/gl2.h> |
1660 | 52 | #include <boost/algorithm/string.hpp> | ||
1661 | 53 | #include <QCoreApplication> | 44 | #include <QCoreApplication> |
1662 | 54 | 45 | ||
2318 | 55 | namespace geom = mir::geometry; | 46 | namespace |
2319 | 56 | namespace mc = mir::compositor; | 47 | { |
2320 | 57 | namespace msh = mir::shell; | 48 | |
2321 | 58 | namespace msc = mir::scene; | 49 | bool check_blacklist( |
2322 | 59 | namespace mf = mir::frontend; | 50 | std::string const& blacklist, |
2323 | 60 | namespace mg = mir::graphics; | 51 | const char *vendor, |
2324 | 61 | namespace mi = mir::input; | 52 | const char *renderer, |
2325 | 62 | namespace mo = mir::options; | 53 | const char *version) |
1671 | 63 | namespace po = boost::program_options; | ||
1672 | 64 | |||
1673 | 65 | class SystemCompositorSurface; | ||
1674 | 66 | |||
1675 | 67 | class SystemCompositorSession : public msc::Session | ||
1676 | 68 | { | ||
1677 | 69 | public: | ||
1678 | 70 | SystemCompositorSession(std::shared_ptr<msc::Session> const& self, | ||
1679 | 71 | SystemCompositorShell *shell) | ||
1680 | 72 | : self{self}, shell{shell}, ready{false} {} | ||
1681 | 73 | |||
1682 | 74 | // These are defined below, since they reference methods defined in other classes | ||
1683 | 75 | void mark_ready(); | ||
1684 | 76 | void raise(std::shared_ptr<msc::SurfaceCoordinator> const& coordinator); | ||
1685 | 77 | std::shared_ptr<mf::Surface> get_surface(mf::SurfaceId surface) const override; | ||
1686 | 78 | mf::SurfaceId create_surface(msc::SurfaceCreationParameters const& params) override; | ||
1687 | 79 | |||
1688 | 80 | bool is_ready() const | ||
1689 | 81 | { | ||
1690 | 82 | return ready; | ||
1691 | 83 | } | ||
1692 | 84 | |||
1693 | 85 | std::shared_ptr<msc::Session> get_orig() | ||
1694 | 86 | { | ||
1695 | 87 | return self; | ||
1696 | 88 | } | ||
1697 | 89 | |||
1698 | 90 | void destroy_surface(mf::SurfaceId surface) override | ||
1699 | 91 | { | ||
1700 | 92 | surfaces.erase(surface); | ||
1701 | 93 | self->destroy_surface(surface); | ||
1702 | 94 | } | ||
1703 | 95 | |||
1704 | 96 | // This is just for convience of USC | ||
1705 | 97 | std::vector<std::shared_ptr<SystemCompositorSurface>> get_surfaces() | ||
1706 | 98 | { | ||
1707 | 99 | std::vector<std::shared_ptr<SystemCompositorSurface>> vector; | ||
1708 | 100 | for(auto surface: surfaces) | ||
1709 | 101 | vector.push_back(surface.second); | ||
1710 | 102 | return vector; | ||
1711 | 103 | } | ||
1712 | 104 | |||
1713 | 105 | std::string name() const override {return self->name();} | ||
1714 | 106 | void hide() override {self->hide();} | ||
1715 | 107 | void show() override {self->show();} | ||
1716 | 108 | void send_display_config(mg::DisplayConfiguration const&config) override {self->send_display_config(config);} | ||
1717 | 109 | pid_t process_id() const override {return self->process_id();} | ||
1718 | 110 | void force_requests_to_complete() override {self->force_requests_to_complete();} | ||
1719 | 111 | void take_snapshot(msc::SnapshotCallback const& snapshot_taken) override {self->take_snapshot(snapshot_taken);} | ||
1720 | 112 | std::shared_ptr<msc::Surface> default_surface() const override {return self->default_surface();} | ||
1721 | 113 | void set_lifecycle_state(MirLifecycleState state) override {self->set_lifecycle_state(state);} | ||
1722 | 114 | |||
1723 | 115 | void start_prompt_session() override { self->start_prompt_session(); } | ||
1724 | 116 | void stop_prompt_session() override { self->stop_prompt_session(); } | ||
1725 | 117 | |||
1726 | 118 | private: | ||
1727 | 119 | std::shared_ptr<msc::Session> const self; | ||
1728 | 120 | SystemCompositorShell *shell; | ||
1729 | 121 | std::map<mf::SurfaceId, std::shared_ptr<SystemCompositorSurface>> surfaces; | ||
1730 | 122 | bool ready; | ||
1731 | 123 | }; | ||
1732 | 124 | |||
1733 | 125 | class SystemCompositorSurface : public msc::Surface | ||
1734 | 126 | { | ||
1735 | 127 | public: | ||
1736 | 128 | SystemCompositorSurface(std::shared_ptr<msc::Surface> const& self, | ||
1737 | 129 | SystemCompositorSession *session) | ||
1738 | 130 | : self{self}, session{session}, buffer_count{0} {} | ||
1739 | 131 | |||
1740 | 132 | std::shared_ptr<msc::Surface> get_orig() | ||
1741 | 133 | { | ||
1742 | 134 | return self; | ||
1743 | 135 | } | ||
1744 | 136 | |||
1745 | 137 | void swap_buffers(mg::Buffer* old_buffer, std::function<void(mg::Buffer* new_buffer)> complete) override | ||
1746 | 138 | { | ||
1747 | 139 | self->swap_buffers(old_buffer, complete); | ||
1748 | 140 | // Mark it available only after some content has been rendered | ||
1749 | 141 | if (old_buffer != NULL && !session->is_ready() && buffer_count++ == 1) | ||
1750 | 142 | session->mark_ready(); | ||
1751 | 143 | } | ||
1752 | 144 | |||
1753 | 145 | // mf::Surface methods | ||
1754 | 146 | void force_requests_to_complete() override {self->force_requests_to_complete();} | ||
1755 | 147 | geom::Size size() const override {return self->size();} | ||
1756 | 148 | MirPixelFormat pixel_format() const override {return self->pixel_format();} | ||
1757 | 149 | bool supports_input() const override {return self->supports_input();} | ||
1758 | 150 | int client_input_fd() const override {return self->client_input_fd();} | ||
1759 | 151 | int configure(MirSurfaceAttrib attrib, int value) override {return self->configure(attrib, value);} | ||
1760 | 152 | |||
1761 | 153 | // msc::Surface methods | ||
1762 | 154 | std::string name() const override {return self->name();} | ||
1763 | 155 | geom::Size client_size() const override { return self->client_size(); }; | ||
1764 | 156 | geom::Rectangle input_bounds() const override { return self->input_bounds(); }; | ||
1765 | 157 | |||
1766 | 158 | MirSurfaceType type() const override {return self->type();} | ||
1767 | 159 | MirSurfaceState state() const override {return self->state();} | ||
1768 | 160 | void hide() override {self->hide();} | ||
1769 | 161 | void show() override {self->show();} | ||
1770 | 162 | void move_to(geom::Point const& top_left) override {self->move_to(top_left);} | ||
1771 | 163 | geom::Point top_left() const override {return self->top_left();} | ||
1772 | 164 | void take_input_focus(std::shared_ptr<msh::InputTargeter> const& targeter) override {self->take_input_focus(targeter);} | ||
1773 | 165 | void set_input_region(std::vector<geom::Rectangle> const& region) override {self->set_input_region(region);} | ||
1774 | 166 | void allow_framedropping(bool allow) override {self->allow_framedropping(allow);} | ||
1775 | 167 | void resize(geom::Size const& size) override {self->resize(size);} | ||
1776 | 168 | void set_transformation(glm::mat4 const& t) override {self->set_transformation(t);} | ||
1777 | 169 | float alpha() const override {return self->alpha();} | ||
1778 | 170 | void set_alpha(float alpha) override {self->set_alpha(alpha);} | ||
1779 | 171 | void with_most_recent_buffer_do(std::function<void(mg::Buffer&)> const& exec) override {self->with_most_recent_buffer_do(exec);} | ||
1780 | 172 | |||
1781 | 173 | // msc::Surface methods | ||
1782 | 174 | std::shared_ptr<mi::InputChannel> input_channel() const override {return self->input_channel();} | ||
1783 | 175 | void set_reception_mode(mi::InputReceptionMode mode) override { self->set_reception_mode(mode); } | ||
1784 | 176 | void add_observer(std::shared_ptr<msc::SurfaceObserver> const& observer) override {self->add_observer(observer);} | ||
1785 | 177 | void remove_observer(std::weak_ptr<msc::SurfaceObserver> const& observer) override {self->remove_observer(observer);} | ||
1786 | 178 | void consume(MirEvent const& event) override {self->consume(event);} | ||
1787 | 179 | void set_orientation(MirOrientation orientation) override {self->set_orientation(orientation);} | ||
1788 | 180 | |||
1789 | 181 | // mi::Surface methods | ||
1790 | 182 | bool input_area_contains(geom::Point const& point) const override {return self->input_area_contains(point);} | ||
1791 | 183 | mi::InputReceptionMode reception_mode() const override { return self->reception_mode(); } | ||
1792 | 184 | |||
1793 | 185 | // mg::Renderable methods | ||
1794 | 186 | std::unique_ptr<mg::Renderable> compositor_snapshot(void const* compositor_id) const override { return self->compositor_snapshot(compositor_id); } | ||
1795 | 187 | |||
1796 | 188 | void set_cursor_image(std::shared_ptr<mg::CursorImage> const& image) override { self->set_cursor_image(image); } | ||
1797 | 189 | std::shared_ptr<mg::CursorImage> cursor_image() const override { return self->cursor_image(); } | ||
1798 | 190 | |||
1799 | 191 | private: | ||
1800 | 192 | std::shared_ptr<msc::Surface> const self; | ||
1801 | 193 | SystemCompositorSession *session; | ||
1802 | 194 | int buffer_count; | ||
1803 | 195 | }; | ||
1804 | 196 | |||
1805 | 197 | class SystemCompositorShell : public mf::Shell | ||
1806 | 198 | { | ||
1807 | 199 | public: | ||
1808 | 200 | SystemCompositorShell(SystemCompositor *compositor, | ||
1809 | 201 | std::shared_ptr<mf::Shell> const& self, | ||
1810 | 202 | std::shared_ptr<msh::FocusController> const& focus_controller, | ||
1811 | 203 | std::shared_ptr<msc::SurfaceCoordinator> const& surface_coordinator) | ||
1812 | 204 | : compositor{compositor}, self(self), focus_controller{focus_controller}, surface_coordinator{surface_coordinator}, active_ever_used{false} {} | ||
1813 | 205 | |||
1814 | 206 | std::shared_ptr<mf::Session> session_named(std::string const& name) | ||
1815 | 207 | { | ||
1816 | 208 | return sessions[name]; | ||
1817 | 209 | } | ||
1818 | 210 | |||
1819 | 211 | void set_active_session(std::string const& name) | ||
1820 | 212 | { | ||
1821 | 213 | active_name = name; | ||
1822 | 214 | update_session_focus(); | ||
1823 | 215 | } | ||
1824 | 216 | |||
1825 | 217 | void set_next_session(std::string const& name) | ||
1826 | 218 | { | ||
1827 | 219 | next_name = name; | ||
1828 | 220 | update_session_focus(); | ||
1829 | 221 | } | ||
1830 | 222 | |||
1831 | 223 | std::shared_ptr<SystemCompositorSession> get_active_session() | ||
1832 | 224 | { | ||
1833 | 225 | return active_session; | ||
1834 | 226 | } | ||
1835 | 227 | |||
1836 | 228 | std::shared_ptr<SystemCompositorSession> get_next_session() | ||
1837 | 229 | { | ||
1838 | 230 | return next_session; | ||
1839 | 231 | } | ||
1840 | 232 | |||
1841 | 233 | void update_session_focus() | ||
1842 | 234 | { | ||
1843 | 235 | auto spinner = sessions[spinner_name]; | ||
1844 | 236 | auto next = sessions[next_name]; | ||
1845 | 237 | auto active = sessions[active_name]; | ||
1846 | 238 | bool need_spinner = false; | ||
1847 | 239 | |||
1848 | 240 | if (spinner) | ||
1849 | 241 | spinner->hide(); | ||
1850 | 242 | |||
1851 | 243 | if (next && next->is_ready()) | ||
1852 | 244 | { | ||
1853 | 245 | std::cerr << "Setting next focus to session " << next_name; | ||
1854 | 246 | next->hide(); | ||
1855 | 247 | next->raise(surface_coordinator); | ||
1856 | 248 | next_session = next; | ||
1857 | 249 | } | ||
1858 | 250 | else if (!next_name.empty() && spinner) | ||
1859 | 251 | { | ||
1860 | 252 | std::cerr << "Setting next focus to spinner"; | ||
1861 | 253 | spinner->raise(surface_coordinator); | ||
1862 | 254 | next_session = spinner; | ||
1863 | 255 | need_spinner = true; | ||
1864 | 256 | } | ||
1865 | 257 | else | ||
1866 | 258 | { | ||
1867 | 259 | need_spinner = !next_name.empty(); | ||
1868 | 260 | std::cerr << "Setting no next focus"; | ||
1869 | 261 | next_session.reset(); | ||
1870 | 262 | } | ||
1871 | 263 | |||
1872 | 264 | // If we are booting, we want to wait for next session to be ready to | ||
1873 | 265 | // go (it's a smoother experience if user is able to immediately swipe | ||
1874 | 266 | // greeter out of way -- enough that it's worth the tiny wait). So | ||
1875 | 267 | // check here to see if next is all ready for us (or we've already | ||
1876 | 268 | // focused the active before in which case we're not booting anymore). | ||
1877 | 269 | bool next_all_set = next_name.empty() || (next && next->is_ready()); | ||
1878 | 270 | if (active && active->is_ready() && (next_all_set || active_ever_used)) | ||
1879 | 271 | { | ||
1880 | 272 | std::cerr << "; active focus to session " << active_name; | ||
1881 | 273 | focus_controller->set_focus_to(active); // raises and focuses | ||
1882 | 274 | active_ever_used = true; | ||
1883 | 275 | active_session = active; | ||
1884 | 276 | if (active_session == next_session) | ||
1885 | 277 | next_session.reset(); | ||
1886 | 278 | } | ||
1887 | 279 | else if (!active_name.empty() && spinner) | ||
1888 | 280 | { | ||
1889 | 281 | std::cerr << "; active focus to spinner"; | ||
1890 | 282 | focus_controller->set_focus_to(spinner); // raises and focuses | ||
1891 | 283 | active_session = spinner; | ||
1892 | 284 | next_session.reset(); | ||
1893 | 285 | need_spinner = true; | ||
1894 | 286 | } | ||
1895 | 287 | else | ||
1896 | 288 | { | ||
1897 | 289 | need_spinner = need_spinner || !active_name.empty(); | ||
1898 | 290 | std::cerr << "; no active focus"; | ||
1899 | 291 | active_session.reset(); | ||
1900 | 292 | next_session.reset(); | ||
1901 | 293 | } | ||
1902 | 294 | |||
1903 | 295 | if (active_session) | ||
1904 | 296 | active_session->show(); | ||
1905 | 297 | if (next_session) | ||
1906 | 298 | next_session->show(); | ||
1907 | 299 | |||
1908 | 300 | if (need_spinner) | ||
1909 | 301 | compositor->ensure_spinner(); | ||
1910 | 302 | else | ||
1911 | 303 | compositor->kill_spinner(); | ||
1912 | 304 | |||
1913 | 305 | std::cerr << std::endl; | ||
1914 | 306 | } | ||
1915 | 307 | |||
1916 | 308 | std::shared_ptr<mf::PromptSession> start_prompt_session_for( | ||
1917 | 309 | std::shared_ptr<mf::Session> const& session, | ||
1918 | 310 | msc::PromptSessionCreationParameters const& params) override | ||
1919 | 311 | { | ||
1920 | 312 | return self->start_prompt_session_for(session, params); | ||
1921 | 313 | } | ||
1922 | 314 | |||
1923 | 315 | void add_prompt_provider_for( | ||
1924 | 316 | std::shared_ptr<mf::PromptSession> const& prompt_session, | ||
1925 | 317 | std::shared_ptr<mf::Session> const& session) override | ||
1926 | 318 | { | ||
1927 | 319 | self->add_prompt_provider_for(prompt_session, session); | ||
1928 | 320 | } | ||
1929 | 321 | |||
1930 | 322 | void stop_prompt_session(std::shared_ptr<mf::PromptSession> const& prompt_session) override | ||
1931 | 323 | { | ||
1932 | 324 | self->stop_prompt_session(prompt_session); | ||
1933 | 325 | } | ||
1934 | 326 | |||
1935 | 327 | private: | ||
1936 | 328 | std::shared_ptr<mf::Session> open_session( | ||
1937 | 329 | pid_t client_pid, | ||
1938 | 330 | std::string const& name, | ||
1939 | 331 | std::shared_ptr<mf::EventSink> const& sink) override | ||
1940 | 332 | { | ||
1941 | 333 | std::cerr << "Opening session " << name << std::endl; | ||
1942 | 334 | |||
1943 | 335 | // We need msc::Session objects because that is what the focus controller | ||
1944 | 336 | // works with. But the mf::Shell interface deals with mf::Session objects. | ||
1945 | 337 | // So we cast here since in practice, these objects are also msc::Sessions. | ||
1946 | 338 | auto orig = std::dynamic_pointer_cast<msc::Session>(self->open_session(client_pid, name, sink)); | ||
1947 | 339 | if (!orig) | ||
1948 | 340 | { | ||
1949 | 341 | std::cerr << "Unexpected non-shell session" << std::endl; | ||
1950 | 342 | return std::shared_ptr<mf::Session>(); | ||
1951 | 343 | } | ||
1952 | 344 | |||
1953 | 345 | auto result = std::make_shared<SystemCompositorSession>(orig, this); | ||
1954 | 346 | sessions[name] = result; | ||
1955 | 347 | |||
1956 | 348 | if (client_pid == compositor->get_spinner_pid()) | ||
1957 | 349 | spinner_name = name; | ||
1958 | 350 | |||
1959 | 351 | return result; | ||
1960 | 352 | } | ||
1961 | 353 | |||
1962 | 354 | void close_session(std::shared_ptr<mf::Session> const& session_in) override | ||
1963 | 355 | { | ||
1964 | 356 | std::cerr << "Closing session " << session_in->name() << std::endl; | ||
1965 | 357 | |||
1966 | 358 | auto session = std::dynamic_pointer_cast<SystemCompositorSession>(session_in); | ||
1967 | 359 | if (!session) | ||
1968 | 360 | return; // shouldn't happen | ||
1969 | 361 | |||
1970 | 362 | if (session->name() == spinner_name) | ||
1971 | 363 | spinner_name = ""; | ||
1972 | 364 | |||
1973 | 365 | self->close_session(session->get_orig()); | ||
1974 | 366 | sessions.erase(session->name()); | ||
1975 | 367 | } | ||
1976 | 368 | |||
1977 | 369 | mf::SurfaceId create_surface_for( | ||
1978 | 370 | std::shared_ptr<mf::Session> const& session, | ||
1979 | 371 | msc::SurfaceCreationParameters const& params) override | ||
1980 | 372 | { | ||
1981 | 373 | return self->create_surface_for(session, params); | ||
1982 | 374 | } | ||
1983 | 375 | |||
1984 | 376 | void handle_surface_created(std::shared_ptr<mf::Session> const& session) override | ||
1985 | 377 | { | ||
1986 | 378 | self->handle_surface_created(session); | ||
1987 | 379 | |||
1988 | 380 | // Opening a new surface will steal focus from our active surface, so | ||
1989 | 381 | // restore the focus if needed. | ||
1990 | 382 | update_session_focus(); | ||
1991 | 383 | } | ||
1992 | 384 | |||
1993 | 385 | SystemCompositor *compositor; | ||
1994 | 386 | std::shared_ptr<mf::Shell> const self; | ||
1995 | 387 | std::shared_ptr<msh::FocusController> const focus_controller; | ||
1996 | 388 | std::shared_ptr<msc::SurfaceCoordinator> const surface_coordinator; | ||
1997 | 389 | std::map<std::string, std::shared_ptr<SystemCompositorSession>> sessions; | ||
1998 | 390 | std::string active_name; | ||
1999 | 391 | std::string next_name; | ||
2000 | 392 | std::string spinner_name; | ||
2001 | 393 | std::shared_ptr<SystemCompositorSession> active_session; | ||
2002 | 394 | std::shared_ptr<SystemCompositorSession> next_session; | ||
2003 | 395 | bool active_ever_used; | ||
2004 | 396 | }; | ||
2005 | 397 | |||
2006 | 398 | class SurfaceSceneElement : public mc::SceneElement | ||
2007 | 399 | { | ||
2008 | 400 | public: | ||
2009 | 401 | SurfaceSceneElement(std::shared_ptr<mg::Renderable> renderable) | ||
2010 | 402 | : renderable_{renderable} | ||
2011 | 403 | { | ||
2012 | 404 | } | ||
2013 | 405 | |||
2014 | 406 | std::shared_ptr<mg::Renderable> renderable() const override | ||
2015 | 407 | { | ||
2016 | 408 | return renderable_; | ||
2017 | 409 | } | ||
2018 | 410 | |||
2019 | 411 | void rendered_in(mc::CompositorID) override {} | ||
2020 | 412 | void occluded_in(mc::CompositorID) override {} | ||
2021 | 413 | |||
2022 | 414 | private: | ||
2023 | 415 | std::shared_ptr<mg::Renderable> const renderable_; | ||
2024 | 416 | }; | ||
2025 | 417 | |||
2026 | 418 | class SystemCompositorScene : public mc::Scene | ||
2027 | 419 | { | ||
2028 | 420 | public: | ||
2029 | 421 | SystemCompositorScene(std::shared_ptr<mc::Scene> const& self) | ||
2030 | 422 | : self{self}, shell{nullptr} {} | ||
2031 | 423 | |||
2032 | 424 | void set_shell(std::shared_ptr<SystemCompositorShell> const& the_shell) | ||
2033 | 425 | { | ||
2034 | 426 | shell = the_shell; | ||
2035 | 427 | } | ||
2036 | 428 | |||
2037 | 429 | mc::SceneElementSequence scene_elements_for(mc::CompositorID id) override | ||
2038 | 430 | { | ||
2039 | 431 | if (shell == nullptr) | ||
2040 | 432 | return self->scene_elements_for(id); | ||
2041 | 433 | |||
2042 | 434 | mc::SceneElementSequence elements; | ||
2043 | 435 | std::shared_ptr<SystemCompositorSession> session; | ||
2044 | 436 | |||
2045 | 437 | session = shell->get_next_session(); | ||
2046 | 438 | if (session) | ||
2047 | 439 | { | ||
2048 | 440 | for (auto const& surface : session->get_surfaces()) { | ||
2049 | 441 | // An open question is whether this memory allocation overhead (as this method is | ||
2050 | 442 | // called for every rendered frame) is perceivable/significant at all to warrant | ||
2051 | 443 | // an optimization here. | ||
2052 | 444 | auto element = std::make_shared<SurfaceSceneElement>(surface->compositor_snapshot(id)); | ||
2053 | 445 | elements.emplace_back(element); | ||
2054 | 446 | } | ||
2055 | 447 | } | ||
2056 | 448 | |||
2057 | 449 | session = shell->get_active_session(); | ||
2058 | 450 | if (session) | ||
2059 | 451 | { | ||
2060 | 452 | for (auto const& surface : session->get_surfaces()) { | ||
2061 | 453 | // see comment previous comment | ||
2062 | 454 | auto element = std::make_shared<SurfaceSceneElement>(surface->compositor_snapshot(id)); | ||
2063 | 455 | elements.emplace_back(element); | ||
2064 | 456 | } | ||
2065 | 457 | } | ||
2066 | 458 | |||
2067 | 459 | return elements; | ||
2068 | 460 | } | ||
2069 | 461 | |||
2070 | 462 | void add_observer(std::shared_ptr<msc::Observer> const& observer) override { self->add_observer(observer); } | ||
2071 | 463 | void remove_observer(std::weak_ptr<msc::Observer> const& observer) override { self->remove_observer(observer); } | ||
2072 | 464 | |||
2073 | 465 | void register_compositor(mc::CompositorID id) override { self->register_compositor(id); } | ||
2074 | 466 | void unregister_compositor(mc::CompositorID id) override { self->unregister_compositor(id); } | ||
2075 | 467 | |||
2076 | 468 | |||
2077 | 469 | private: | ||
2078 | 470 | std::shared_ptr<mc::Scene> const self; | ||
2079 | 471 | std::shared_ptr<SystemCompositorShell> shell; | ||
2080 | 472 | }; | ||
2081 | 473 | |||
2082 | 474 | |||
2083 | 475 | void SystemCompositorSession::mark_ready() | ||
2084 | 476 | { | ||
2085 | 477 | if (!ready) | ||
2086 | 478 | { | ||
2087 | 479 | ready = true; | ||
2088 | 480 | shell->update_session_focus(); | ||
2089 | 481 | } | ||
2090 | 482 | } | ||
2091 | 483 | |||
2092 | 484 | void SystemCompositorSession::raise(std::shared_ptr<msc::SurfaceCoordinator> const& coordinator) | ||
2093 | 485 | { | ||
2094 | 486 | std::map<mf::SurfaceId, std::shared_ptr<SystemCompositorSurface>>::iterator iter; | ||
2095 | 487 | for (iter = surfaces.begin(); iter != surfaces.end(); ++iter) | ||
2096 | 488 | { | ||
2097 | 489 | // This will iterate by creation order, which is fine. New surfaces on top | ||
2098 | 490 | coordinator->raise(iter->second->get_orig()); | ||
2099 | 491 | } | ||
2100 | 492 | } | ||
2101 | 493 | |||
2102 | 494 | std::shared_ptr<mf::Surface> SystemCompositorSession::get_surface(mf::SurfaceId surface) const | ||
2103 | 495 | { | ||
2104 | 496 | return surfaces.at(surface); | ||
2105 | 497 | } | ||
2106 | 498 | |||
2107 | 499 | mf::SurfaceId SystemCompositorSession::create_surface(msc::SurfaceCreationParameters const& params) | ||
2108 | 500 | { | ||
2109 | 501 | mf::SurfaceId id = self->create_surface(params); | ||
2110 | 502 | std::shared_ptr<mf::Surface> mf_surface = self->get_surface(id); | ||
2111 | 503 | |||
2112 | 504 | auto surface = std::dynamic_pointer_cast<msc::Surface>(mf_surface); | ||
2113 | 505 | if (!surface) | ||
2114 | 506 | { | ||
2115 | 507 | std::cerr << "Unexpected non-scene surface" << std::endl; | ||
2116 | 508 | self->destroy_surface(id); | ||
2117 | 509 | return mf::SurfaceId(0); | ||
2118 | 510 | } | ||
2119 | 511 | |||
2120 | 512 | surfaces[id] = std::make_shared<SystemCompositorSurface>(surface, this); | ||
2121 | 513 | return id; | ||
2122 | 514 | } | ||
2123 | 515 | |||
2124 | 516 | class SystemCompositorServerConfiguration : public mir::DefaultServerConfiguration | ||
2125 | 517 | { | ||
2126 | 518 | public: | ||
2127 | 519 | SystemCompositorServerConfiguration(SystemCompositor *compositor, std::shared_ptr<mo::Configuration> options) | ||
2128 | 520 | : mir::DefaultServerConfiguration(options), compositor{compositor} | ||
2129 | 521 | { | ||
2130 | 522 | } | ||
2131 | 523 | |||
2132 | 524 | int from_dm_fd() | ||
2133 | 525 | { | ||
2134 | 526 | return the_options()->get("from-dm-fd", -1); | ||
2135 | 527 | } | ||
2136 | 528 | |||
2137 | 529 | int to_dm_fd() | ||
2138 | 530 | { | ||
2139 | 531 | return the_options()->get("to-dm-fd", -1); | ||
2140 | 532 | } | ||
2141 | 533 | |||
2142 | 534 | bool show_version() | ||
2143 | 535 | { | ||
2144 | 536 | return the_options()->is_set("version"); | ||
2145 | 537 | } | ||
2146 | 538 | |||
2147 | 539 | int inactivity_display_off_timeout() | ||
2148 | 540 | { | ||
2149 | 541 | return the_options()->get("inactivity-display-off-timeout", 60); | ||
2150 | 542 | } | ||
2151 | 543 | |||
2152 | 544 | int inactivity_display_dim_timeout() | ||
2153 | 545 | { | ||
2154 | 546 | return the_options()->get("inactivity-display-dim-timeout", 45); | ||
2155 | 547 | } | ||
2156 | 548 | |||
2157 | 549 | int shutdown_timeout() | ||
2158 | 550 | { | ||
2159 | 551 | return the_options()->get("shutdown-timeout", 5000); | ||
2160 | 552 | } | ||
2161 | 553 | |||
2162 | 554 | int power_key_ignore_timeout() | ||
2163 | 555 | { | ||
2164 | 556 | return the_options()->get("power-key-ignore-timeout", 1500); | ||
2165 | 557 | } | ||
2166 | 558 | |||
2167 | 559 | bool enable_hardware_cursor() | ||
2168 | 560 | { | ||
2169 | 561 | return the_options()->get("enable-hardware-cursor", false); | ||
2170 | 562 | } | ||
2171 | 563 | |||
2172 | 564 | bool disable_inactivity_policy() | ||
2173 | 565 | { | ||
2174 | 566 | return the_options()->get("disable-inactivity-policy", false); | ||
2175 | 567 | } | ||
2176 | 568 | |||
2177 | 569 | std::string blacklist() | ||
2178 | 570 | { | ||
2179 | 571 | auto x = the_options()->get("blacklist", ""); | ||
2180 | 572 | boost::trim(x); | ||
2181 | 573 | return x; | ||
2182 | 574 | } | ||
2183 | 575 | |||
2184 | 576 | std::string spinner() | ||
2185 | 577 | { | ||
2186 | 578 | // TODO: once our default spinner is ready for use everywhere, replace | ||
2187 | 579 | // default value with DEFAULT_SPINNER instead of the empty string. | ||
2188 | 580 | auto x = the_options()->get("spinner", ""); | ||
2189 | 581 | boost::trim(x); | ||
2190 | 582 | return x; | ||
2191 | 583 | } | ||
2192 | 584 | |||
2193 | 585 | bool public_socket() | ||
2194 | 586 | { | ||
2195 | 587 | return !the_options()->is_set("no-file") && the_options()->get("public-socket", true); | ||
2196 | 588 | } | ||
2197 | 589 | |||
2198 | 590 | std::shared_ptr<mi::CursorListener> the_cursor_listener() override | ||
2199 | 591 | { | ||
2200 | 592 | struct NullCursorListener : public mi::CursorListener | ||
2201 | 593 | { | ||
2202 | 594 | void cursor_moved_to(float, float) override | ||
2203 | 595 | { | ||
2204 | 596 | } | ||
2205 | 597 | }; | ||
2206 | 598 | |||
2207 | 599 | // This is a workaround for u8 desktop preview in 14.04 for the lack of client cursor API. | ||
2208 | 600 | // We need to disable the cursor for XMir but leave it on for the desktop preview. | ||
2209 | 601 | // Luckily as it stands they run inside seperate instances of USC. ~racarr | ||
2210 | 602 | if (enable_hardware_cursor()) | ||
2211 | 603 | return mir::DefaultServerConfiguration::the_cursor_listener(); | ||
2212 | 604 | else | ||
2213 | 605 | return std::make_shared<NullCursorListener>(); | ||
2214 | 606 | } | ||
2215 | 607 | |||
2216 | 608 | std::shared_ptr<mir::ServerStatusListener> the_server_status_listener() override | ||
2217 | 609 | { | ||
2218 | 610 | struct ServerStatusListener : public mir::ServerStatusListener | ||
2219 | 611 | { | ||
2220 | 612 | ServerStatusListener (SystemCompositor *compositor) : compositor{compositor} {} | ||
2221 | 613 | |||
2222 | 614 | void paused() override | ||
2223 | 615 | { | ||
2224 | 616 | compositor->pause(); | ||
2225 | 617 | } | ||
2226 | 618 | |||
2227 | 619 | void resumed() override | ||
2228 | 620 | { | ||
2229 | 621 | compositor->resume(); | ||
2230 | 622 | } | ||
2231 | 623 | |||
2232 | 624 | void started() override | ||
2233 | 625 | { | ||
2234 | 626 | } | ||
2235 | 627 | |||
2236 | 628 | SystemCompositor *compositor; | ||
2237 | 629 | }; | ||
2238 | 630 | return std::make_shared<ServerStatusListener>(compositor); | ||
2239 | 631 | } | ||
2240 | 632 | |||
2241 | 633 | std::string get_socket_file() | ||
2242 | 634 | { | ||
2243 | 635 | // the_socket_file is private, so we have to re-implement it here | ||
2244 | 636 | return the_options()->get("file", "/tmp/mir_socket"); | ||
2245 | 637 | } | ||
2246 | 638 | |||
2247 | 639 | std::shared_ptr<SystemCompositorShell> the_system_compositor_shell() | ||
2248 | 640 | { | ||
2249 | 641 | auto shell = sc_shell([this] | ||
2250 | 642 | { | ||
2251 | 643 | return std::make_shared<SystemCompositorShell>( | ||
2252 | 644 | compositor, | ||
2253 | 645 | mir::DefaultServerConfiguration::the_frontend_shell(), | ||
2254 | 646 | the_focus_controller(), | ||
2255 | 647 | the_surface_coordinator()); | ||
2256 | 648 | }); | ||
2257 | 649 | |||
2258 | 650 | the_system_compositor_scene()->set_shell(shell); | ||
2259 | 651 | return shell; | ||
2260 | 652 | } | ||
2261 | 653 | |||
2262 | 654 | std::shared_ptr<SystemCompositorScene> the_system_compositor_scene() | ||
2263 | 655 | { | ||
2264 | 656 | return sc_scene([this] | ||
2265 | 657 | { | ||
2266 | 658 | return std::make_shared<SystemCompositorScene>( | ||
2267 | 659 | mir::DefaultServerConfiguration::the_scene()); | ||
2268 | 660 | }); | ||
2269 | 661 | } | ||
2270 | 662 | |||
2271 | 663 | std::shared_ptr<mc::Scene> the_scene() | ||
2272 | 664 | { | ||
2273 | 665 | return the_system_compositor_scene(); | ||
2274 | 666 | } | ||
2275 | 667 | |||
2276 | 668 | private: | ||
2277 | 669 | mir::CachedPtr<SystemCompositorShell> sc_shell; | ||
2278 | 670 | mir::CachedPtr<SystemCompositorScene> sc_scene; | ||
2279 | 671 | |||
2280 | 672 | std::shared_ptr<mf::Shell> the_frontend_shell() override | ||
2281 | 673 | { | ||
2282 | 674 | return the_system_compositor_shell(); | ||
2283 | 675 | } | ||
2284 | 676 | |||
2285 | 677 | SystemCompositor *compositor; | ||
2286 | 678 | }; | ||
2287 | 679 | |||
2288 | 680 | class SystemCompositorConfigurationOptions : public mo::DefaultConfiguration | ||
2289 | 681 | { | ||
2290 | 682 | public: | ||
2291 | 683 | SystemCompositorConfigurationOptions(int argc, char const* argv[]) : | ||
2292 | 684 | DefaultConfiguration(argc, argv) | ||
2293 | 685 | { | ||
2294 | 686 | add_options() | ||
2295 | 687 | ("from-dm-fd", po::value<int>(), "File descriptor of read end of pipe from display manager [int]") | ||
2296 | 688 | ("to-dm-fd", po::value<int>(), "File descriptor of write end of pipe to display manager [int]") | ||
2297 | 689 | ("blacklist", po::value<std::string>(), "Video blacklist regex to use") | ||
2298 | 690 | ("version", "Show version of Unity System Compositor") | ||
2299 | 691 | ("spinner", po::value<std::string>(), "Path to spinner executable") | ||
2300 | 692 | ("public-socket", po::value<bool>(), "Make the socket file publicly writable") | ||
2301 | 693 | ("enable-hardware-cursor", po::value<bool>(), "Enable the hardware cursor (disabled by default)") | ||
2302 | 694 | ("inactivity-display-off-timeout", po::value<int>(), "The time in seconds before the screen is turned off when there are no active sessions") | ||
2303 | 695 | ("inactivity-display-dim-timeout", po::value<int>(), "The time in seconds before the screen is dimmed when there are no active sessions") | ||
2304 | 696 | ("shutdown-timeout", po::value<int>(), "The time in milli-seconds the power key must be held to initiate a clean system shutdown") | ||
2305 | 697 | ("power-key-ignore-timeout", po::value<int>(), "The time in milli-seconds the power key must be held to ignore - must be less than shutdown-timeout") | ||
2306 | 698 | ("disable-inactivity-policy", po::value<bool>(), "Disables handling user inactivity and power key"); | ||
2307 | 699 | } | ||
2308 | 700 | |||
2309 | 701 | void parse_config_file( | ||
2310 | 702 | boost::program_options::options_description& options_description, | ||
2311 | 703 | mo::ProgramOption& options) const override | ||
2312 | 704 | { | ||
2313 | 705 | options.parse_file(options_description, "unity-system-compositor.conf"); | ||
2314 | 706 | } | ||
2315 | 707 | }; | ||
2316 | 708 | |||
2317 | 709 | bool check_blacklist(std::string blacklist, const char *vendor, const char *renderer, const char *version) | ||
2326 | 710 | { | 54 | { |
2327 | 711 | if (blacklist.empty()) | 55 | if (blacklist.empty()) |
2328 | 712 | return true; | 56 | return true; |
2329 | @@ -739,35 +83,37 @@ | |||
2330 | 739 | return true; | 83 | return true; |
2331 | 740 | } | 84 | } |
2332 | 741 | 85 | ||
2338 | 742 | void SystemCompositor::run(int argc, char **argv) | 86 | } |
2339 | 743 | { | 87 | |
2340 | 744 | auto const options = std::make_shared<SystemCompositorConfigurationOptions>(argc, const_cast<char const **>(argv)); | 88 | usc::SystemCompositor::SystemCompositor( |
2341 | 745 | config = std::make_shared<SystemCompositorServerConfiguration>(this, options); | 89 | std::shared_ptr<ServerConfiguration> const& config) |
2342 | 746 | 90 | : config{config}, | |
2343 | 91 | dm_connection{config->the_dm_connection()}, | ||
2344 | 92 | spinner{config->the_spinner()} | ||
2345 | 93 | { | ||
2346 | 94 | } | ||
2347 | 95 | |||
2348 | 96 | void usc::SystemCompositor::run() | ||
2349 | 97 | { | ||
2350 | 747 | if (config->show_version()) | 98 | if (config->show_version()) |
2351 | 748 | { | 99 | { |
2352 | 749 | std::cerr << "unity-system-compositor " << USC_VERSION << std::endl; | 100 | std::cerr << "unity-system-compositor " << USC_VERSION << std::endl; |
2353 | 750 | return; | 101 | return; |
2354 | 751 | } | 102 | } |
2355 | 752 | 103 | ||
2356 | 753 | dm_connection = std::make_shared<DMConnection>(io_service, config->from_dm_fd(), config->to_dm_fd()); | ||
2357 | 754 | |||
2358 | 755 | struct ScopeGuard | 104 | struct ScopeGuard |
2359 | 756 | { | 105 | { |
2360 | 757 | explicit ScopeGuard(boost::asio::io_service& io_service) : io_service(io_service) {} | ||
2361 | 758 | ~ScopeGuard() | 106 | ~ScopeGuard() |
2362 | 759 | { | 107 | { |
2363 | 760 | io_service.stop(); | ||
2364 | 761 | if (io_thread.joinable()) | ||
2365 | 762 | io_thread.join(); | ||
2366 | 763 | if (qt_thread.joinable()) | 108 | if (qt_thread.joinable()) |
2367 | 109 | { | ||
2368 | 110 | QCoreApplication::quit(); | ||
2369 | 764 | qt_thread.join(); | 111 | qt_thread.join(); |
2370 | 112 | } | ||
2371 | 765 | } | 113 | } |
2372 | 766 | 114 | ||
2373 | 767 | boost::asio::io_service& io_service; | ||
2374 | 768 | std::thread io_thread; | ||
2375 | 769 | std::thread qt_thread; | 115 | std::thread qt_thread; |
2377 | 770 | } guard(io_service); | 116 | } guard; |
2378 | 771 | 117 | ||
2379 | 772 | mir::run_mir(*config, [&](mir::DisplayServer&) | 118 | mir::run_mir(*config, [&](mir::DisplayServer&) |
2380 | 773 | { | 119 | { |
2381 | @@ -781,46 +127,12 @@ | |||
2382 | 781 | if (!check_blacklist(config->blacklist(), vendor, renderer, version)) | 127 | if (!check_blacklist(config->blacklist(), vendor, renderer, version)) |
2383 | 782 | throw mir::AbnormalExit ("Video driver is blacklisted, exiting"); | 128 | throw mir::AbnormalExit ("Video driver is blacklisted, exiting"); |
2384 | 783 | 129 | ||
2388 | 784 | shell = config->the_system_compositor_shell(); | 130 | main(); |
2389 | 785 | guard.io_thread = std::thread(&SystemCompositor::main, this); | 131 | guard.qt_thread = std::thread(&SystemCompositor::qt_main, this); |
2387 | 786 | guard.qt_thread = std::thread(&SystemCompositor::qt_main, this, argc, argv); | ||
2390 | 787 | }); | 132 | }); |
2391 | 788 | } | 133 | } |
2392 | 789 | 134 | ||
2427 | 790 | void SystemCompositor::pause() | 135 | void usc::SystemCompositor::main() |
2394 | 791 | { | ||
2395 | 792 | std::cerr << "pause" << std::endl; | ||
2396 | 793 | |||
2397 | 794 | if (auto active_session = config->the_focus_controller()->focussed_application().lock()) | ||
2398 | 795 | active_session->set_lifecycle_state(mir_lifecycle_state_will_suspend); | ||
2399 | 796 | } | ||
2400 | 797 | |||
2401 | 798 | void SystemCompositor::resume() | ||
2402 | 799 | { | ||
2403 | 800 | std::cerr << "resume" << std::endl; | ||
2404 | 801 | |||
2405 | 802 | if (auto active_session = config->the_focus_controller()->focussed_application().lock()) | ||
2406 | 803 | active_session->set_lifecycle_state(mir_lifecycle_state_resumed); | ||
2407 | 804 | } | ||
2408 | 805 | |||
2409 | 806 | pid_t SystemCompositor::get_spinner_pid() const | ||
2410 | 807 | { | ||
2411 | 808 | return spinner_process.pid(); | ||
2412 | 809 | } | ||
2413 | 810 | |||
2414 | 811 | void SystemCompositor::set_active_session(std::string client_name) | ||
2415 | 812 | { | ||
2416 | 813 | std::cerr << "set_active_session" << std::endl; | ||
2417 | 814 | shell->set_active_session(client_name); | ||
2418 | 815 | } | ||
2419 | 816 | |||
2420 | 817 | void SystemCompositor::set_next_session(std::string client_name) | ||
2421 | 818 | { | ||
2422 | 819 | std::cerr << "set_next_session" << std::endl; | ||
2423 | 820 | shell->set_next_session(client_name); | ||
2424 | 821 | } | ||
2425 | 822 | |||
2426 | 823 | void SystemCompositor::main() | ||
2428 | 824 | { | 136 | { |
2429 | 825 | // Make socket world-writable, since users need to talk to us. No worries | 137 | // Make socket world-writable, since users need to talk to us. No worries |
2430 | 826 | // about race condition, since we are adding permissions, not restricting | 138 | // about race condition, since we are adding permissions, not restricting |
2431 | @@ -828,33 +140,13 @@ | |||
2432 | 828 | if (config->public_socket() && chmod(config->get_socket_file().c_str(), 0777) == -1) | 140 | if (config->public_socket() && chmod(config->get_socket_file().c_str(), 0777) == -1) |
2433 | 829 | std::cerr << "Unable to chmod socket file " << config->get_socket_file() << ": " << strerror(errno) << std::endl; | 141 | std::cerr << "Unable to chmod socket file " << config->get_socket_file() << ": " << strerror(errno) << std::endl; |
2434 | 830 | 142 | ||
2435 | 831 | dm_connection->set_handler(this); | ||
2436 | 832 | dm_connection->start(); | 143 | dm_connection->start(); |
2462 | 833 | dm_connection->send_ready(); | 144 | } |
2463 | 834 | 145 | ||
2464 | 835 | io_service.run(); | 146 | void usc::SystemCompositor::qt_main() |
2465 | 836 | } | 147 | { |
2466 | 837 | 148 | int argc{0}; | |
2467 | 838 | void SystemCompositor::ensure_spinner() | 149 | QCoreApplication app(argc, nullptr); |
2443 | 839 | { | ||
2444 | 840 | if (config->spinner().empty() || spinner_process.state() != QProcess::NotRunning) | ||
2445 | 841 | return; | ||
2446 | 842 | |||
2447 | 843 | // Launch spinner process to provide default background when a session isn't ready | ||
2448 | 844 | QStringList env = QProcess::systemEnvironment(); | ||
2449 | 845 | env << "MIR_SOCKET=" + QString(config->get_socket_file().c_str()); | ||
2450 | 846 | spinner_process.setEnvironment(env); | ||
2451 | 847 | spinner_process.start(config->spinner().c_str()); | ||
2452 | 848 | } | ||
2453 | 849 | |||
2454 | 850 | void SystemCompositor::kill_spinner() | ||
2455 | 851 | { | ||
2456 | 852 | spinner_process.close(); | ||
2457 | 853 | } | ||
2458 | 854 | |||
2459 | 855 | void SystemCompositor::qt_main(int argc, char **argv) | ||
2460 | 856 | { | ||
2461 | 857 | QCoreApplication app(argc, argv); | ||
2468 | 858 | 150 | ||
2469 | 859 | if (!config->disable_inactivity_policy()) | 151 | if (!config->disable_inactivity_policy()) |
2470 | 860 | { | 152 | { |
2471 | @@ -877,6 +169,11 @@ | |||
2472 | 877 | composite_filter->append(power_key_handler); | 169 | composite_filter->append(power_key_handler); |
2473 | 878 | } | 170 | } |
2474 | 879 | 171 | ||
2475 | 880 | ensure_spinner(); | ||
2476 | 881 | app.exec(); | 172 | app.exec(); |
2477 | 173 | |||
2478 | 174 | // Destroy components that depend on Qt event handling inside the Qt thread, | ||
2479 | 175 | // to silence warnings during shutdown | ||
2480 | 176 | |||
2481 | 177 | // ScreenStateHandler uses the Qt DBus infrastructure | ||
2482 | 178 | screen_state_handler.reset(); | ||
2483 | 882 | } | 179 | } |
2484 | 883 | 180 | ||
2485 | === modified file 'src/system_compositor.h' | |||
2486 | --- src/system_compositor.h 2014-06-20 16:35:15 +0000 | |||
2487 | +++ src/system_compositor.h 2014-07-18 09:49:03 +0000 | |||
2488 | @@ -1,5 +1,5 @@ | |||
2489 | 1 | /* | 1 | /* |
2491 | 2 | * Copyright © 2013 Canonical Ltd. | 2 | * Copyright © 2013-2014 Canonical Ltd. |
2492 | 3 | * | 3 | * |
2493 | 4 | * This program is free software: you can redistribute it and/or modify | 4 | * This program is free software: you can redistribute it and/or modify |
2494 | 5 | * it under the terms of the GNU General Public License version 3 as | 5 | * it under the terms of the GNU General Public License version 3 as |
2495 | @@ -14,44 +14,41 @@ | |||
2496 | 14 | * along with this program. If not, see <http://www.gnu.org/licenses/>. | 14 | * along with this program. If not, see <http://www.gnu.org/licenses/>. |
2497 | 15 | * | 15 | * |
2498 | 16 | * Authored by: Robert Ancell <robert.ancell@canonical.com> | 16 | * Authored by: Robert Ancell <robert.ancell@canonical.com> |
2499 | 17 | * Alexandros Frantzis <alexandros.frantzis@canonical.com> | ||
2500 | 17 | */ | 18 | */ |
2501 | 18 | 19 | ||
2512 | 19 | #ifndef SYSTEM_COMPOSITOR_H_ | 20 | #ifndef USC_SYSTEM_COMPOSITOR_H_ |
2513 | 20 | #define SYSTEM_COMPOSITOR_H_ | 21 | #define USC_SYSTEM_COMPOSITOR_H_ |
2514 | 21 | 22 | ||
2515 | 22 | #include "dm_connection.h" | 23 | #include <memory> |
2516 | 23 | #include <QProcess> | 24 | |
2507 | 24 | |||
2508 | 25 | namespace mir { namespace scene { class Session; } } | ||
2509 | 26 | |||
2510 | 27 | class SystemCompositorShell; | ||
2511 | 28 | class SystemCompositorServerConfiguration; | ||
2517 | 29 | class ScreenStateHandler; | 25 | class ScreenStateHandler; |
2518 | 30 | class PowerKeyHandler; | 26 | class PowerKeyHandler; |
2519 | 31 | 27 | ||
2521 | 32 | class SystemCompositor : public DMMessageHandler | 28 | namespace usc |
2522 | 29 | { | ||
2523 | 30 | |||
2524 | 31 | class ServerConfiguration; | ||
2525 | 32 | class DMConnection; | ||
2526 | 33 | class Spinner; | ||
2527 | 34 | |||
2528 | 35 | class SystemCompositor | ||
2529 | 33 | { | 36 | { |
2530 | 34 | public: | 37 | public: |
2537 | 35 | void run(int argc, char **argv); | 38 | SystemCompositor(std::shared_ptr<ServerConfiguration> const& config); |
2538 | 36 | void pause(); | 39 | void run(); |
2533 | 37 | void resume(); | ||
2534 | 38 | pid_t get_spinner_pid() const; | ||
2535 | 39 | void ensure_spinner(); | ||
2536 | 40 | void kill_spinner(); | ||
2539 | 41 | 40 | ||
2540 | 42 | private: | 41 | private: |
2545 | 43 | std::shared_ptr<SystemCompositorServerConfiguration> config; | 42 | void main(); |
2546 | 44 | std::shared_ptr<SystemCompositorShell> shell; | 43 | void qt_main(); |
2547 | 45 | boost::asio::io_service io_service; | 44 | |
2548 | 46 | std::shared_ptr<DMConnection> dm_connection; | 45 | std::shared_ptr<ServerConfiguration> const config; |
2549 | 46 | std::shared_ptr<DMConnection> const dm_connection; | ||
2550 | 47 | std::shared_ptr<Spinner> const spinner; | ||
2551 | 47 | std::shared_ptr<ScreenStateHandler> screen_state_handler; | 48 | std::shared_ptr<ScreenStateHandler> screen_state_handler; |
2552 | 48 | std::shared_ptr<PowerKeyHandler> power_key_handler; | 49 | std::shared_ptr<PowerKeyHandler> power_key_handler; |
2553 | 49 | QProcess spinner_process; | ||
2554 | 50 | |||
2555 | 51 | void set_active_session(std::string client_name); | ||
2556 | 52 | void set_next_session(std::string client_name); | ||
2557 | 53 | void main(); | ||
2558 | 54 | void qt_main(int argc, char **argv); | ||
2559 | 55 | }; | 50 | }; |
2560 | 56 | 51 | ||
2562 | 57 | #endif /* SYSTEM_COMPOSITOR_H_ */ | 52 | } |
2563 | 53 | |||
2564 | 54 | #endif /* USC_SYSTEM_COMPOSITOR_H_ */ | ||
2565 | 58 | 55 | ||
2566 | === added file 'tests/CMakeLists.txt' | |||
2567 | --- tests/CMakeLists.txt 1970-01-01 00:00:00 +0000 | |||
2568 | +++ tests/CMakeLists.txt 2014-07-18 09:49:03 +0000 | |||
2569 | @@ -0,0 +1,17 @@ | |||
2570 | 1 | # Copyright © 2014 Canonical Ltd. | ||
2571 | 2 | # | ||
2572 | 3 | # This program is free software: you can redistribute it and/or modify | ||
2573 | 4 | # it under the terms of the GNU General Public License version 3 as | ||
2574 | 5 | # published by the Free Software Foundation. | ||
2575 | 6 | # | ||
2576 | 7 | # This program is distributed in the hope that it will be useful, | ||
2577 | 8 | # but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
2578 | 9 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
2579 | 10 | # GNU General Public License for more details. | ||
2580 | 11 | # | ||
2581 | 12 | # You should have received a copy of the GNU General Public License | ||
2582 | 13 | # along with this program. If not, see <http://www.gnu.org/licenses/>. | ||
2583 | 14 | # | ||
2584 | 15 | # Authored by: Alexandros Frantzis <alexandros.frantzis@canonical.com> | ||
2585 | 16 | |||
2586 | 17 | add_subdirectory(unit-tests/) | ||
2587 | 0 | 18 | ||
2588 | === added directory 'tests/unit-tests' | |||
2589 | === added file 'tests/unit-tests/CMakeLists.txt' | |||
2590 | --- tests/unit-tests/CMakeLists.txt 1970-01-01 00:00:00 +0000 | |||
2591 | +++ tests/unit-tests/CMakeLists.txt 2014-07-18 09:49:03 +0000 | |||
2592 | @@ -0,0 +1,39 @@ | |||
2593 | 1 | # Copyright © 2014 Canonical Ltd. | ||
2594 | 2 | # | ||
2595 | 3 | # This program is free software: you can redistribute it and/or modify | ||
2596 | 4 | # it under the terms of the GNU General Public License version 3 as | ||
2597 | 5 | # published by the Free Software Foundation. | ||
2598 | 6 | # | ||
2599 | 7 | # This program is distributed in the hope that it will be useful, | ||
2600 | 8 | # but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
2601 | 9 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
2602 | 10 | # GNU General Public License for more details. | ||
2603 | 11 | # | ||
2604 | 12 | # You should have received a copy of the GNU General Public License | ||
2605 | 13 | # along with this program. If not, see <http://www.gnu.org/licenses/>. | ||
2606 | 14 | # | ||
2607 | 15 | # Authored by: Alexandros Frantzis <alexandros.frantzis@canonical.com> | ||
2608 | 16 | |||
2609 | 17 | include_directories( | ||
2610 | 18 | ${CMAKE_SOURCE_DIR} | ||
2611 | 19 | ${MIRSERVER_INCLUDE_DIRS} | ||
2612 | 20 | ) | ||
2613 | 21 | |||
2614 | 22 | add_executable( | ||
2615 | 23 | usc_unit_tests | ||
2616 | 24 | |||
2617 | 25 | test_session_switcher.cpp | ||
2618 | 26 | ) | ||
2619 | 27 | |||
2620 | 28 | target_link_libraries( | ||
2621 | 29 | usc_unit_tests | ||
2622 | 30 | |||
2623 | 31 | usc | ||
2624 | 32 | ${GTEST_BOTH_LIBRARIES} | ||
2625 | 33 | ${GMOCK_LIBRARY} | ||
2626 | 34 | ${GMOCK_MAIN_LIBRARY} | ||
2627 | 35 | ) | ||
2628 | 36 | |||
2629 | 37 | add_test(usc_unit_tests ${EXECUTABLE_OUTPUT_PATH}/usc_unit_tests) | ||
2630 | 38 | |||
2631 | 39 | add_dependencies(usc_unit_tests GMock) | ||
2632 | 0 | 40 | ||
2633 | === added file 'tests/unit-tests/test_session_switcher.cpp' | |||
2634 | --- tests/unit-tests/test_session_switcher.cpp 1970-01-01 00:00:00 +0000 | |||
2635 | +++ tests/unit-tests/test_session_switcher.cpp 2014-07-18 09:49:03 +0000 | |||
2636 | @@ -0,0 +1,656 @@ | |||
2637 | 1 | /* | ||
2638 | 2 | * Copyright © 2014 Canonical Ltd. | ||
2639 | 3 | * | ||
2640 | 4 | * This program is free software: you can redistribute it and/or modify | ||
2641 | 5 | * it under the terms of the GNU General Public License version 3 as | ||
2642 | 6 | * published by the Free Software Foundation. | ||
2643 | 7 | * | ||
2644 | 8 | * This program is distributed in the hope that it will be useful, | ||
2645 | 9 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
2646 | 10 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
2647 | 11 | * GNU General Public License for more details. | ||
2648 | 12 | * | ||
2649 | 13 | * You should have received a copy of the GNU General Public License | ||
2650 | 14 | * along with this program. If not, see <http://www.gnu.org/licenses/>. | ||
2651 | 15 | * | ||
2652 | 16 | * Authored by: Alexandros Frantzis <alexandros.frantzis@canonical.com> | ||
2653 | 17 | */ | ||
2654 | 18 | |||
2655 | 19 | #include "src/session_switcher.h" | ||
2656 | 20 | #include "src/spinner.h" | ||
2657 | 21 | |||
2658 | 22 | #include <gtest/gtest.h> | ||
2659 | 23 | #include <gmock/gmock.h> | ||
2660 | 24 | |||
2661 | 25 | #include <memory> | ||
2662 | 26 | #include <vector> | ||
2663 | 27 | #include <tuple> | ||
2664 | 28 | |||
2665 | 29 | namespace | ||
2666 | 30 | { | ||
2667 | 31 | |||
2668 | 32 | class FakeScene | ||
2669 | 33 | { | ||
2670 | 34 | public: | ||
2671 | 35 | void add(usc::Session* session) | ||
2672 | 36 | { | ||
2673 | 37 | sessions.emplace_back(session, true); | ||
2674 | 38 | } | ||
2675 | 39 | |||
2676 | 40 | void remove(usc::Session* session) | ||
2677 | 41 | { | ||
2678 | 42 | sessions.erase(find(session)); | ||
2679 | 43 | } | ||
2680 | 44 | |||
2681 | 45 | void show(usc::Session* session) | ||
2682 | 46 | { | ||
2683 | 47 | find(session)->second = true; | ||
2684 | 48 | } | ||
2685 | 49 | |||
2686 | 50 | void hide(usc::Session* session) | ||
2687 | 51 | { | ||
2688 | 52 | find(session)->second = false; | ||
2689 | 53 | } | ||
2690 | 54 | |||
2691 | 55 | void raise(usc::Session* session) | ||
2692 | 56 | { | ||
2693 | 57 | auto const iter = find(session); | ||
2694 | 58 | auto const pair = *iter; | ||
2695 | 59 | sessions.erase(iter); | ||
2696 | 60 | sessions.push_back(pair); | ||
2697 | 61 | } | ||
2698 | 62 | |||
2699 | 63 | std::vector<std::string> displayed_sessions() | ||
2700 | 64 | { | ||
2701 | 65 | std::vector<std::string> ret; | ||
2702 | 66 | for (auto const& pair : sessions) | ||
2703 | 67 | { | ||
2704 | 68 | bool session_visible = pair.second; | ||
2705 | 69 | if (session_visible) | ||
2706 | 70 | ret.push_back(pair.first->name()); | ||
2707 | 71 | } | ||
2708 | 72 | |||
2709 | 73 | return ret; | ||
2710 | 74 | } | ||
2711 | 75 | |||
2712 | 76 | private: | ||
2713 | 77 | std::vector<std::pair<usc::Session*,bool>> sessions; | ||
2714 | 78 | |||
2715 | 79 | decltype(sessions)::iterator find(usc::Session* session) | ||
2716 | 80 | { | ||
2717 | 81 | return std::find_if( | ||
2718 | 82 | sessions.begin(), sessions.end(), | ||
2719 | 83 | [session] (decltype(sessions)::value_type const& p) | ||
2720 | 84 | { | ||
2721 | 85 | return p.first == session; | ||
2722 | 86 | }); | ||
2723 | 87 | } | ||
2724 | 88 | }; | ||
2725 | 89 | |||
2726 | 90 | class StubSession : public usc::Session | ||
2727 | 91 | { | ||
2728 | 92 | public: | ||
2729 | 93 | StubSession(FakeScene& fake_scene, std::string const& name) | ||
2730 | 94 | : fake_scene(fake_scene), | ||
2731 | 95 | name_{name} | ||
2732 | 96 | { | ||
2733 | 97 | fake_scene.add(this); | ||
2734 | 98 | } | ||
2735 | 99 | |||
2736 | 100 | ~StubSession() | ||
2737 | 101 | { | ||
2738 | 102 | fake_scene.remove(this); | ||
2739 | 103 | } | ||
2740 | 104 | |||
2741 | 105 | std::string name() override | ||
2742 | 106 | { | ||
2743 | 107 | return name_; | ||
2744 | 108 | } | ||
2745 | 109 | |||
2746 | 110 | void show() override | ||
2747 | 111 | { | ||
2748 | 112 | fake_scene.show(this); | ||
2749 | 113 | } | ||
2750 | 114 | |||
2751 | 115 | void hide() override | ||
2752 | 116 | { | ||
2753 | 117 | fake_scene.hide(this); | ||
2754 | 118 | } | ||
2755 | 119 | |||
2756 | 120 | void raise_and_focus() override | ||
2757 | 121 | { | ||
2758 | 122 | fake_scene.raise(this); | ||
2759 | 123 | } | ||
2760 | 124 | |||
2761 | 125 | bool corresponds_to(mir::scene::Session const* s) override | ||
2762 | 126 | { | ||
2763 | 127 | return s == corresponding_scene_session(); | ||
2764 | 128 | } | ||
2765 | 129 | |||
2766 | 130 | mir::scene::Session const* corresponding_scene_session() | ||
2767 | 131 | { | ||
2768 | 132 | return reinterpret_cast<mir::scene::Session const*>(this); | ||
2769 | 133 | } | ||
2770 | 134 | |||
2771 | 135 | private: | ||
2772 | 136 | FakeScene& fake_scene; | ||
2773 | 137 | std::string const name_; | ||
2774 | 138 | }; | ||
2775 | 139 | |||
2776 | 140 | struct StubSpinner : usc::Spinner | ||
2777 | 141 | { | ||
2778 | 142 | void ensure_running() override { is_running_ = true; } | ||
2779 | 143 | void kill() override { is_running_ = false; } | ||
2780 | 144 | pid_t pid() override { return pid_; } | ||
2781 | 145 | |||
2782 | 146 | void set_pid(pid_t new_pid) { pid_ = new_pid; } | ||
2783 | 147 | bool is_running() { return is_running_; } | ||
2784 | 148 | |||
2785 | 149 | private: | ||
2786 | 150 | bool is_running_ = false; | ||
2787 | 151 | pid_t pid_ = 666; | ||
2788 | 152 | }; | ||
2789 | 153 | |||
2790 | 154 | struct ASessionSwitcher : testing::Test | ||
2791 | 155 | { | ||
2792 | 156 | std::shared_ptr<StubSession> create_stub_session(std::string const& name) | ||
2793 | 157 | { | ||
2794 | 158 | return std::make_shared<StubSession>(fake_scene, name); | ||
2795 | 159 | } | ||
2796 | 160 | |||
2797 | 161 | std::tuple<std::string,std::string> boot() | ||
2798 | 162 | { | ||
2799 | 163 | std::string const boot_active_name{"boot_active"}; | ||
2800 | 164 | std::string const boot_next_name{"boot_next"}; | ||
2801 | 165 | |||
2802 | 166 | auto const boot_active = create_stub_session(boot_active_name); | ||
2803 | 167 | auto const boot_next = create_stub_session(boot_next_name); | ||
2804 | 168 | |||
2805 | 169 | switcher.add(boot_active, active_pid); | ||
2806 | 170 | switcher.add(boot_next, next_pid); | ||
2807 | 171 | |||
2808 | 172 | switcher.set_next_session(boot_next_name); | ||
2809 | 173 | switcher.set_active_session(boot_active_name); | ||
2810 | 174 | switcher.mark_ready(boot_active->corresponding_scene_session()); | ||
2811 | 175 | switcher.mark_ready(boot_next->corresponding_scene_session()); | ||
2812 | 176 | |||
2813 | 177 | return std::make_tuple(boot_active_name, boot_next_name); | ||
2814 | 178 | } | ||
2815 | 179 | |||
2816 | 180 | FakeScene fake_scene; | ||
2817 | 181 | std::shared_ptr<StubSpinner> const stub_spinner{std::make_shared<StubSpinner>()}; | ||
2818 | 182 | usc::SessionSwitcher switcher{stub_spinner}; | ||
2819 | 183 | std::string const active_name{"active"}; | ||
2820 | 184 | std::string const next_name{"next"}; | ||
2821 | 185 | std::string const spinner_name{"spinner"}; | ||
2822 | 186 | pid_t const invalid_pid{0}; | ||
2823 | 187 | pid_t const active_pid{1000}; | ||
2824 | 188 | pid_t const next_pid{1001}; | ||
2825 | 189 | pid_t const other_pid{1002}; | ||
2826 | 190 | }; | ||
2827 | 191 | |||
2828 | 192 | } | ||
2829 | 193 | |||
2830 | 194 | TEST_F(ASessionSwitcher, does_not_display_any_session_if_active_and_next_not_set) | ||
2831 | 195 | { | ||
2832 | 196 | using namespace testing; | ||
2833 | 197 | |||
2834 | 198 | switcher.add(create_stub_session("s1"), invalid_pid); | ||
2835 | 199 | switcher.add(create_stub_session("s2"), invalid_pid); | ||
2836 | 200 | |||
2837 | 201 | EXPECT_THAT(fake_scene.displayed_sessions(), IsEmpty()); | ||
2838 | 202 | } | ||
2839 | 203 | |||
2840 | 204 | TEST_F(ASessionSwitcher, does_not_display_not_ready_active_session) | ||
2841 | 205 | { | ||
2842 | 206 | using namespace testing; | ||
2843 | 207 | |||
2844 | 208 | auto const active = create_stub_session(active_name); | ||
2845 | 209 | |||
2846 | 210 | switcher.add(active, active_pid); | ||
2847 | 211 | switcher.set_active_session(active_name); | ||
2848 | 212 | |||
2849 | 213 | EXPECT_THAT(fake_scene.displayed_sessions(), IsEmpty()); | ||
2850 | 214 | } | ||
2851 | 215 | |||
2852 | 216 | TEST_F(ASessionSwitcher, does_not_display_not_ready_next_session) | ||
2853 | 217 | { | ||
2854 | 218 | using namespace testing; | ||
2855 | 219 | |||
2856 | 220 | auto const next = create_stub_session(next_name); | ||
2857 | 221 | |||
2858 | 222 | switcher.add(next, next_pid); | ||
2859 | 223 | switcher.set_next_session(next_name); | ||
2860 | 224 | |||
2861 | 225 | EXPECT_THAT(fake_scene.displayed_sessions(), IsEmpty()); | ||
2862 | 226 | } | ||
2863 | 227 | |||
2864 | 228 | TEST_F(ASessionSwitcher, does_not_display_ready_next_session_without_ready_active_session) | ||
2865 | 229 | { | ||
2866 | 230 | using namespace testing; | ||
2867 | 231 | |||
2868 | 232 | auto const next = create_stub_session(next_name); | ||
2869 | 233 | |||
2870 | 234 | switcher.add(next, next_pid); | ||
2871 | 235 | switcher.set_next_session(next_name); | ||
2872 | 236 | switcher.mark_ready(next->corresponding_scene_session()); | ||
2873 | 237 | |||
2874 | 238 | EXPECT_THAT(fake_scene.displayed_sessions(), IsEmpty()); | ||
2875 | 239 | } | ||
2876 | 240 | |||
2877 | 241 | TEST_F(ASessionSwitcher, | ||
2878 | 242 | does_not_display_any_session_on_boot_if_not_both_active_and_next_are_ready) | ||
2879 | 243 | { | ||
2880 | 244 | using namespace testing; | ||
2881 | 245 | |||
2882 | 246 | auto const active = create_stub_session(active_name); | ||
2883 | 247 | auto const next = create_stub_session(next_name); | ||
2884 | 248 | |||
2885 | 249 | switcher.add(active, active_pid); | ||
2886 | 250 | switcher.add(next, next_pid); | ||
2887 | 251 | |||
2888 | 252 | switcher.set_active_session(active_name); | ||
2889 | 253 | switcher.set_next_session(next_name); | ||
2890 | 254 | switcher.mark_ready(active->corresponding_scene_session()); | ||
2891 | 255 | |||
2892 | 256 | EXPECT_THAT(fake_scene.displayed_sessions(), IsEmpty()); | ||
2893 | 257 | } | ||
2894 | 258 | |||
2895 | 259 | TEST_F(ASessionSwitcher, | ||
2896 | 260 | displays_the_active_session_on_boot_if_it_is_ready_and_there_is_no_next_session) | ||
2897 | 261 | { | ||
2898 | 262 | using namespace testing; | ||
2899 | 263 | |||
2900 | 264 | auto const active = create_stub_session(active_name); | ||
2901 | 265 | auto const spinner = create_stub_session(spinner_name); | ||
2902 | 266 | |||
2903 | 267 | switcher.add(active, active_pid); | ||
2904 | 268 | switcher.add(spinner, stub_spinner->pid()); | ||
2905 | 269 | |||
2906 | 270 | switcher.set_active_session(active_name); | ||
2907 | 271 | switcher.mark_ready(active->corresponding_scene_session()); | ||
2908 | 272 | |||
2909 | 273 | EXPECT_THAT(fake_scene.displayed_sessions(), | ||
2910 | 274 | ElementsAre(active_name)); | ||
2911 | 275 | } | ||
2912 | 276 | |||
2913 | 277 | TEST_F(ASessionSwitcher, displays_the_active_session_after_boot_if_it_is_ready) | ||
2914 | 278 | { | ||
2915 | 279 | using namespace testing; | ||
2916 | 280 | |||
2917 | 281 | boot(); | ||
2918 | 282 | |||
2919 | 283 | auto const active = create_stub_session(active_name); | ||
2920 | 284 | auto const next = create_stub_session(next_name); | ||
2921 | 285 | |||
2922 | 286 | switcher.add(active, active_pid); | ||
2923 | 287 | switcher.add(next, next_pid); | ||
2924 | 288 | |||
2925 | 289 | switcher.set_next_session(next_name); | ||
2926 | 290 | switcher.set_active_session(active_name); | ||
2927 | 291 | switcher.mark_ready(active->corresponding_scene_session()); | ||
2928 | 292 | |||
2929 | 293 | EXPECT_THAT(fake_scene.displayed_sessions(), | ||
2930 | 294 | ElementsAre(active_name)); | ||
2931 | 295 | } | ||
2932 | 296 | |||
2933 | 297 | TEST_F(ASessionSwitcher, displays_active_over_next_if_both_are_ready) | ||
2934 | 298 | { | ||
2935 | 299 | using namespace testing; | ||
2936 | 300 | |||
2937 | 301 | auto const active = create_stub_session(active_name); | ||
2938 | 302 | auto const next = create_stub_session(next_name); | ||
2939 | 303 | |||
2940 | 304 | switcher.add(active, active_pid); | ||
2941 | 305 | switcher.add(next, next_pid); | ||
2942 | 306 | |||
2943 | 307 | switcher.set_next_session(next_name); | ||
2944 | 308 | switcher.set_active_session(active_name); | ||
2945 | 309 | switcher.mark_ready(active->corresponding_scene_session()); | ||
2946 | 310 | switcher.mark_ready(next->corresponding_scene_session()); | ||
2947 | 311 | |||
2948 | 312 | EXPECT_THAT(fake_scene.displayed_sessions(), | ||
2949 | 313 | ElementsAre(next_name, active_name)); | ||
2950 | 314 | } | ||
2951 | 315 | |||
2952 | 316 | |||
2953 | 317 | TEST_F(ASessionSwitcher, displays_only_active_if_next_equals_active) | ||
2954 | 318 | { | ||
2955 | 319 | using namespace testing; | ||
2956 | 320 | |||
2957 | 321 | auto const active = create_stub_session(active_name); | ||
2958 | 322 | |||
2959 | 323 | switcher.add(active, active_pid); | ||
2960 | 324 | |||
2961 | 325 | switcher.set_next_session(active_name); | ||
2962 | 326 | switcher.set_active_session(active_name); | ||
2963 | 327 | switcher.mark_ready(active->corresponding_scene_session()); | ||
2964 | 328 | |||
2965 | 329 | EXPECT_THAT(fake_scene.displayed_sessions(), | ||
2966 | 330 | ElementsAre(active_name)); | ||
2967 | 331 | } | ||
2968 | 332 | |||
2969 | 333 | TEST_F(ASessionSwitcher, displays_only_active_and_next_sessions) | ||
2970 | 334 | { | ||
2971 | 335 | using namespace testing; | ||
2972 | 336 | |||
2973 | 337 | auto const active = create_stub_session(active_name); | ||
2974 | 338 | auto const next = create_stub_session(next_name); | ||
2975 | 339 | auto const other = create_stub_session("other"); | ||
2976 | 340 | |||
2977 | 341 | switcher.add(active, active_pid); | ||
2978 | 342 | switcher.add(next, next_pid); | ||
2979 | 343 | switcher.add(other, other_pid); | ||
2980 | 344 | |||
2981 | 345 | switcher.set_next_session(next_name); | ||
2982 | 346 | switcher.set_active_session(active_name); | ||
2983 | 347 | switcher.mark_ready(active->corresponding_scene_session()); | ||
2984 | 348 | switcher.mark_ready(next->corresponding_scene_session()); | ||
2985 | 349 | switcher.mark_ready(other->corresponding_scene_session()); | ||
2986 | 350 | |||
2987 | 351 | EXPECT_THAT(fake_scene.displayed_sessions(), | ||
2988 | 352 | ElementsAre(next_name, active_name)); | ||
2989 | 353 | } | ||
2990 | 354 | |||
2991 | 355 | TEST_F(ASessionSwitcher, displays_spinner_if_active_is_not_ready) | ||
2992 | 356 | { | ||
2993 | 357 | using namespace testing; | ||
2994 | 358 | |||
2995 | 359 | auto const active = create_stub_session(active_name); | ||
2996 | 360 | auto const spinner = create_stub_session(spinner_name); | ||
2997 | 361 | |||
2998 | 362 | switcher.add(active, active_pid); | ||
2999 | 363 | switcher.add(spinner, stub_spinner->pid()); | ||
3000 | 364 | |||
3001 | 365 | switcher.set_active_session(active_name); | ||
3002 | 366 | |||
3003 | 367 | EXPECT_THAT(fake_scene.displayed_sessions(), | ||
3004 | 368 | ElementsAre(spinner_name)); | ||
3005 | 369 | } | ||
3006 | 370 | |||
3007 | 371 | TEST_F(ASessionSwitcher, does_not_display_spinner_if_next_is_not_ready) | ||
3008 | 372 | { | ||
3009 | 373 | using namespace testing; | ||
3010 | 374 | |||
3011 | 375 | auto const next = create_stub_session(next_name); | ||
3012 | 376 | auto const spinner = create_stub_session(spinner_name); | ||
3013 | 377 | |||
3014 | 378 | switcher.add(next, next_pid); | ||
3015 | 379 | switcher.add(spinner, stub_spinner->pid()); | ||
3016 | 380 | |||
3017 | 381 | switcher.set_next_session(next_name); | ||
3018 | 382 | |||
3019 | 383 | EXPECT_THAT(fake_scene.displayed_sessions(), IsEmpty()); | ||
3020 | 384 | } | ||
3021 | 385 | |||
3022 | 386 | TEST_F(ASessionSwitcher, displays_only_spinner_when_active_is_not_ready_but_next_is_ready) | ||
3023 | 387 | { | ||
3024 | 388 | using namespace testing; | ||
3025 | 389 | |||
3026 | 390 | auto const active = create_stub_session(active_name); | ||
3027 | 391 | auto const next = create_stub_session(next_name); | ||
3028 | 392 | auto const spinner = create_stub_session(spinner_name); | ||
3029 | 393 | |||
3030 | 394 | switcher.add(active, active_pid); | ||
3031 | 395 | switcher.add(next, next_pid); | ||
3032 | 396 | switcher.add(spinner, stub_spinner->pid()); | ||
3033 | 397 | |||
3034 | 398 | switcher.set_active_session(active_name); | ||
3035 | 399 | switcher.set_next_session(next_name); | ||
3036 | 400 | switcher.mark_ready(next->corresponding_scene_session()); | ||
3037 | 401 | |||
3038 | 402 | EXPECT_THAT(fake_scene.displayed_sessions(), | ||
3039 | 403 | ElementsAre(spinner_name)); | ||
3040 | 404 | } | ||
3041 | 405 | |||
3042 | 406 | TEST_F(ASessionSwitcher, | ||
3043 | 407 | displays_only_spinner_when_booting_if_not_both_active_and_next_are_ready) | ||
3044 | 408 | { | ||
3045 | 409 | using namespace testing; | ||
3046 | 410 | |||
3047 | 411 | auto const active = create_stub_session(active_name); | ||
3048 | 412 | auto const next = create_stub_session(next_name); | ||
3049 | 413 | auto const spinner = create_stub_session(spinner_name); | ||
3050 | 414 | |||
3051 | 415 | switcher.add(active, active_pid); | ||
3052 | 416 | switcher.add(next, next_pid); | ||
3053 | 417 | switcher.add(spinner, stub_spinner->pid()); | ||
3054 | 418 | |||
3055 | 419 | switcher.set_active_session(active_name); | ||
3056 | 420 | switcher.set_next_session(next_name); | ||
3057 | 421 | switcher.mark_ready(active->corresponding_scene_session()); | ||
3058 | 422 | |||
3059 | 423 | EXPECT_THAT(fake_scene.displayed_sessions(), | ||
3060 | 424 | ElementsAre(spinner_name)); | ||
3061 | 425 | } | ||
3062 | 426 | |||
3063 | 427 | TEST_F(ASessionSwitcher, | ||
3064 | 428 | displays_spinner_behind_active_after_boot_if_active_is_ready_but_next_is_not_ready) | ||
3065 | 429 | { | ||
3066 | 430 | using namespace testing; | ||
3067 | 431 | |||
3068 | 432 | boot(); | ||
3069 | 433 | |||
3070 | 434 | auto const active = create_stub_session(active_name); | ||
3071 | 435 | auto const next = create_stub_session(next_name); | ||
3072 | 436 | auto const spinner = create_stub_session(spinner_name); | ||
3073 | 437 | |||
3074 | 438 | switcher.add(active, active_pid); | ||
3075 | 439 | switcher.add(next, next_pid); | ||
3076 | 440 | switcher.add(spinner, stub_spinner->pid()); | ||
3077 | 441 | |||
3078 | 442 | switcher.set_active_session(active_name); | ||
3079 | 443 | switcher.set_next_session(next_name); | ||
3080 | 444 | switcher.mark_ready(active->corresponding_scene_session()); | ||
3081 | 445 | |||
3082 | 446 | EXPECT_THAT(fake_scene.displayed_sessions(), | ||
3083 | 447 | ElementsAre(spinner_name, active_name)); | ||
3084 | 448 | } | ||
3085 | 449 | |||
3086 | 450 | TEST_F(ASessionSwitcher, starts_and_stops_spinner_as_needed) | ||
3087 | 451 | { | ||
3088 | 452 | using namespace testing; | ||
3089 | 453 | |||
3090 | 454 | auto const active = create_stub_session(active_name); | ||
3091 | 455 | |||
3092 | 456 | switcher.add(active, active_pid); | ||
3093 | 457 | switcher.set_active_session(active_name); | ||
3094 | 458 | |||
3095 | 459 | EXPECT_TRUE(stub_spinner->is_running()); | ||
3096 | 460 | |||
3097 | 461 | switcher.mark_ready(active->corresponding_scene_session()); | ||
3098 | 462 | |||
3099 | 463 | EXPECT_FALSE(stub_spinner->is_running()); | ||
3100 | 464 | } | ||
3101 | 465 | |||
3102 | 466 | TEST_F(ASessionSwitcher, does_not_display_next_when_active_is_removed) | ||
3103 | 467 | { | ||
3104 | 468 | using namespace testing; | ||
3105 | 469 | |||
3106 | 470 | std::string const no_session_name; | ||
3107 | 471 | |||
3108 | 472 | std::string boot_active_name; | ||
3109 | 473 | std::string boot_next_name; | ||
3110 | 474 | std::tie(boot_active_name, boot_next_name) = boot(); | ||
3111 | 475 | |||
3112 | 476 | auto const spinner = create_stub_session(spinner_name); | ||
3113 | 477 | switcher.add(spinner, stub_spinner->pid()); | ||
3114 | 478 | |||
3115 | 479 | switcher.remove(boot_active_name); | ||
3116 | 480 | switcher.set_active_session(no_session_name); | ||
3117 | 481 | |||
3118 | 482 | EXPECT_THAT(fake_scene.displayed_sessions(), IsEmpty()); | ||
3119 | 483 | } | ||
3120 | 484 | |||
3121 | 485 | TEST_F(ASessionSwitcher, displays_only_active_not_spinner_when_next_is_removed) | ||
3122 | 486 | { | ||
3123 | 487 | using namespace testing; | ||
3124 | 488 | |||
3125 | 489 | std::string const no_session_name; | ||
3126 | 490 | |||
3127 | 491 | std::string boot_active_name; | ||
3128 | 492 | std::string boot_next_name; | ||
3129 | 493 | std::tie(boot_active_name, boot_next_name) = boot(); | ||
3130 | 494 | |||
3131 | 495 | auto const spinner = create_stub_session(spinner_name); | ||
3132 | 496 | switcher.add(spinner, stub_spinner->pid()); | ||
3133 | 497 | |||
3134 | 498 | switcher.remove(boot_next_name); | ||
3135 | 499 | switcher.set_next_session(no_session_name); | ||
3136 | 500 | |||
3137 | 501 | EXPECT_THAT(fake_scene.displayed_sessions(), | ||
3138 | 502 | ElementsAre(boot_active_name)); | ||
3139 | 503 | } | ||
3140 | 504 | |||
3141 | 505 | TEST_F(ASessionSwitcher, displays_spinner_when_active_is_removed_unexpectedly) | ||
3142 | 506 | { | ||
3143 | 507 | using namespace testing; | ||
3144 | 508 | |||
3145 | 509 | std::string const no_session_name; | ||
3146 | 510 | |||
3147 | 511 | std::string boot_active_name; | ||
3148 | 512 | std::string boot_next_name; | ||
3149 | 513 | std::tie(boot_active_name, boot_next_name) = boot(); | ||
3150 | 514 | |||
3151 | 515 | auto const spinner = create_stub_session(spinner_name); | ||
3152 | 516 | switcher.add(spinner, stub_spinner->pid()); | ||
3153 | 517 | |||
3154 | 518 | switcher.remove(boot_active_name); | ||
3155 | 519 | |||
3156 | 520 | EXPECT_THAT(fake_scene.displayed_sessions(), | ||
3157 | 521 | ElementsAre(spinner_name)); | ||
3158 | 522 | } | ||
3159 | 523 | |||
3160 | 524 | TEST_F(ASessionSwitcher, displays_spinner_under_active_if_next_is_removed_unexpectedly) | ||
3161 | 525 | { | ||
3162 | 526 | using namespace testing; | ||
3163 | 527 | |||
3164 | 528 | std::string const no_session_name; | ||
3165 | 529 | |||
3166 | 530 | std::string boot_active_name; | ||
3167 | 531 | std::string boot_next_name; | ||
3168 | 532 | std::tie(boot_active_name, boot_next_name) = boot(); | ||
3169 | 533 | |||
3170 | 534 | auto const spinner = create_stub_session(spinner_name); | ||
3171 | 535 | switcher.add(spinner, stub_spinner->pid()); | ||
3172 | 536 | |||
3173 | 537 | switcher.remove(boot_next_name); | ||
3174 | 538 | |||
3175 | 539 | EXPECT_THAT(fake_scene.displayed_sessions(), | ||
3176 | 540 | ElementsAre(spinner_name, boot_active_name)); | ||
3177 | 541 | } | ||
3178 | 542 | |||
3179 | 543 | TEST_F(ASessionSwitcher, | ||
3180 | 544 | does_not_display_any_session_when_spinner_is_removed_and_no_sessions_are_ready) | ||
3181 | 545 | { | ||
3182 | 546 | using namespace testing; | ||
3183 | 547 | |||
3184 | 548 | auto const active = create_stub_session(active_name); | ||
3185 | 549 | auto spinner = create_stub_session(spinner_name); | ||
3186 | 550 | |||
3187 | 551 | switcher.add(active, active_pid); | ||
3188 | 552 | switcher.add(spinner, stub_spinner->pid()); | ||
3189 | 553 | |||
3190 | 554 | switcher.set_active_session(active_name); | ||
3191 | 555 | |||
3192 | 556 | EXPECT_THAT(fake_scene.displayed_sessions(), ElementsAre(spinner_name)); | ||
3193 | 557 | |||
3194 | 558 | spinner.reset(); | ||
3195 | 559 | switcher.remove(spinner_name); | ||
3196 | 560 | |||
3197 | 561 | EXPECT_THAT(fake_scene.displayed_sessions(), IsEmpty()); | ||
3198 | 562 | } | ||
3199 | 563 | |||
3200 | 564 | TEST_F(ASessionSwitcher, can_handle_spinner_resurrection_under_different_name) | ||
3201 | 565 | { | ||
3202 | 566 | using namespace testing; | ||
3203 | 567 | |||
3204 | 568 | auto const active = create_stub_session(active_name); | ||
3205 | 569 | auto spinner = create_stub_session(spinner_name); | ||
3206 | 570 | |||
3207 | 571 | switcher.add(active, active_pid); | ||
3208 | 572 | switcher.add(spinner, stub_spinner->pid()); | ||
3209 | 573 | |||
3210 | 574 | switcher.set_active_session(active_name); | ||
3211 | 575 | |||
3212 | 576 | EXPECT_THAT(fake_scene.displayed_sessions(), ElementsAre(spinner_name)); | ||
3213 | 577 | |||
3214 | 578 | spinner.reset(); | ||
3215 | 579 | switcher.remove(spinner_name); | ||
3216 | 580 | |||
3217 | 581 | std::string const new_spinner_name{"new_spinner_name"}; | ||
3218 | 582 | spinner = create_stub_session(new_spinner_name); | ||
3219 | 583 | switcher.add(spinner, stub_spinner->pid()); | ||
3220 | 584 | |||
3221 | 585 | EXPECT_THAT(fake_scene.displayed_sessions(), ElementsAre(new_spinner_name)); | ||
3222 | 586 | } | ||
3223 | 587 | |||
3224 | 588 | TEST_F(ASessionSwitcher, can_handle_spinner_resurrection_under_different_pid) | ||
3225 | 589 | { | ||
3226 | 590 | using namespace testing; | ||
3227 | 591 | |||
3228 | 592 | auto const active = create_stub_session(active_name); | ||
3229 | 593 | auto spinner = create_stub_session(spinner_name); | ||
3230 | 594 | |||
3231 | 595 | switcher.add(active, active_pid); | ||
3232 | 596 | switcher.add(spinner, stub_spinner->pid()); | ||
3233 | 597 | switcher.set_active_session(active_name); | ||
3234 | 598 | |||
3235 | 599 | EXPECT_THAT(fake_scene.displayed_sessions(), ElementsAre(spinner_name)); | ||
3236 | 600 | |||
3237 | 601 | spinner.reset(); | ||
3238 | 602 | switcher.remove(spinner_name); | ||
3239 | 603 | |||
3240 | 604 | pid_t const new_pid{1234}; | ||
3241 | 605 | stub_spinner->set_pid(new_pid); | ||
3242 | 606 | |||
3243 | 607 | spinner = create_stub_session(spinner_name); | ||
3244 | 608 | switcher.add(spinner, stub_spinner->pid()); | ||
3245 | 609 | |||
3246 | 610 | EXPECT_THAT(fake_scene.displayed_sessions(), ElementsAre(spinner_name)); | ||
3247 | 611 | } | ||
3248 | 612 | |||
3249 | 613 | TEST_F(ASessionSwitcher, is_not_confused_by_other_session_with_name_of_dead_spinner) | ||
3250 | 614 | { | ||
3251 | 615 | using namespace testing; | ||
3252 | 616 | |||
3253 | 617 | auto const active = create_stub_session(active_name); | ||
3254 | 618 | auto spinner = create_stub_session(spinner_name); | ||
3255 | 619 | |||
3256 | 620 | switcher.add(active, active_pid); | ||
3257 | 621 | switcher.add(spinner, stub_spinner->pid()); | ||
3258 | 622 | |||
3259 | 623 | switcher.set_active_session(active_name); | ||
3260 | 624 | |||
3261 | 625 | spinner.reset(); | ||
3262 | 626 | switcher.remove(spinner_name); | ||
3263 | 627 | stub_spinner->set_pid(invalid_pid); | ||
3264 | 628 | |||
3265 | 629 | auto const other = create_stub_session(spinner_name); | ||
3266 | 630 | switcher.add(other, other_pid); | ||
3267 | 631 | |||
3268 | 632 | EXPECT_THAT(fake_scene.displayed_sessions(), IsEmpty()); | ||
3269 | 633 | } | ||
3270 | 634 | |||
3271 | 635 | TEST_F(ASessionSwitcher, is_not_confused_by_other_session_with_pid_of_dead_spinner) | ||
3272 | 636 | { | ||
3273 | 637 | using namespace testing; | ||
3274 | 638 | |||
3275 | 639 | auto const active = create_stub_session(active_name); | ||
3276 | 640 | auto spinner = create_stub_session(spinner_name); | ||
3277 | 641 | |||
3278 | 642 | switcher.add(active, active_pid); | ||
3279 | 643 | switcher.add(spinner, stub_spinner->pid()); | ||
3280 | 644 | |||
3281 | 645 | switcher.set_active_session(active_name); | ||
3282 | 646 | |||
3283 | 647 | auto const old_spinner_pid = stub_spinner->pid(); | ||
3284 | 648 | stub_spinner->set_pid(invalid_pid); | ||
3285 | 649 | spinner.reset(); | ||
3286 | 650 | switcher.remove(spinner_name); | ||
3287 | 651 | |||
3288 | 652 | auto const other = create_stub_session(spinner_name); | ||
3289 | 653 | switcher.add(other, old_spinner_pid); | ||
3290 | 654 | |||
3291 | 655 | EXPECT_THAT(fake_scene.displayed_sessions(), IsEmpty()); | ||
3292 | 656 | } |
I noticed I messed up/forgot copyrights in some cases. Will address that tomorrow along with other review comments.