Merge lp:~alan-griffiths/mir/move-miral-to-mir-cleanup into lp:mir
- move-miral-to-mir-cleanup
- Merge into development-branch
Status: | Merged |
---|---|
Approved by: | Alan Griffiths |
Approved revision: | no longer in the source branch. |
Merged at revision: | 4238 |
Proposed branch: | lp:~alan-griffiths/mir/move-miral-to-mir-cleanup |
Merge into: | lp:mir |
Prerequisite: | lp:~alan-griffiths/mir/mive-miral-to-mir-regeneration |
Diff against target: |
4207 lines (+127/-3772) 22 files modified
examples/CMakeLists.txt (+5/-21) examples/cursor-theme-dump.cpp (+0/-127) examples/miral-shell/CMakeLists.txt (+11/-7) examples/server_example.cpp (+66/-83) examples/server_example_basic_window_manager.cpp (+0/-328) examples/server_example_basic_window_manager.h (+0/-264) examples/server_example_canonical_window_manager.cpp (+0/-1007) examples/server_example_canonical_window_manager.h (+0/-142) examples/server_example_cursor_images.cpp (+0/-57) examples/server_example_cursor_images.h (+0/-33) examples/server_example_test_client.cpp (+31/-18) examples/server_example_test_client.h (+12/-8) examples/server_example_window_management.cpp (+0/-154) examples/server_example_window_management.h (+0/-33) examples/xcursor.c (+0/-973) examples/xcursor.h (+0/-65) examples/xcursor_loader.cpp (+0/-228) examples/xcursor_loader.h (+0/-74) playground/CMakeLists.txt (+1/-0) src/miral/process_doxygen_xml.py (+1/-1) tests/unit-tests/input/CMakeLists.txt (+0/-1) tests/unit-tests/input/test_xcursor_loader.cpp (+0/-148) |
To merge this branch: | bzr merge lp:~alan-griffiths/mir/move-miral-to-mir-cleanup |
Related bugs: |
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
Mir CI Bot | continuous-integration | Approve | |
Gerry Boland (community) | Approve | ||
Review via email: mp+329622@code.launchpad.net |
Commit message
rework mir_demo_server to use miral (and parts of miral-shell)
Description of the change
Incorporate miral project into mir source tree
This is a initial pass at removing code obsoleted by MirAL
There's no reworking of the generated docs to include miral (yet)
Mir CI Bot (mir-ci-bot) wrote : | # |
Mir CI Bot (mir-ci-bot) wrote : | # |
FAILED: Continuous integration, rev:4264
https:/
Executed test runs:
FAILURE: https:/
SUCCESS: https:/
SUCCESS: https:/
SUCCESS: https:/
SUCCESS: https:/
FAILURE: https:/
SUCCESS: https:/
deb: https:/
FAILURE: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
FAILURE: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
Click here to trigger a rebuild:
https:/
Mir CI Bot (mir-ci-bot) wrote : | # |
PASSED: Continuous integration, rev:4264
https:/
Executed test runs:
SUCCESS: https:/
SUCCESS: https:/
SUCCESS: https:/
SUCCESS: https:/
SUCCESS: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
Click here to trigger a rebuild:
https:/
Gerry Boland (gerboland) wrote : | # |
- auto const client = options-
+ auto const client = options1-
Interesting, necessary?
Gerry Boland (gerboland) wrote : | # |
Rest looks fine, tests ok. I'm not too familar with the features of mir_demo_server, but those I recall this new refactoring does support.
Alan Griffiths (alan-griffiths) wrote : | # |
> - auto const client =
> options-
> + auto const client = options1-
> Interesting, necessary?
No. CLion "improving" code without me noticing.
Good catch!
Mir CI Bot (mir-ci-bot) wrote : | # |
FAILED: Continuous integration, rev:4265
https:/
Executed test runs:
FAILURE: https:/
FAILURE: https:/
FAILURE: https:/
FAILURE: https:/
FAILURE: https:/
FAILURE: https:/
FAILURE: https:/
FAILURE: https:/
FAILURE: https:/
FAILURE: https:/
FAILURE: https:/
FAILURE: https:/
FAILURE: https:/
Click here to trigger a rebuild:
https:/
Mir CI Bot (mir-ci-bot) wrote : | # |
FAILED: Continuous integration, rev:4266
https:/
Executed test runs:
FAILURE: https:/
FAILURE: https:/
FAILURE: https:/
FAILURE: https:/
FAILURE: https:/
FAILURE: https:/
FAILURE: https:/
FAILURE: https:/
FAILURE: https:/
FAILURE: https:/
FAILURE: https:/
FAILURE: https:/
FAILURE: https:/
Click here to trigger a rebuild:
https:/
Mir CI Bot (mir-ci-bot) wrote : | # |
PASSED: Continuous integration, rev:4267
https:/
Executed test runs:
SUCCESS: https:/
SUCCESS: https:/
SUCCESS: https:/
SUCCESS: https:/
SUCCESS: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
Click here to trigger a rebuild:
https:/
Preview Diff
1 | === modified file 'examples/CMakeLists.txt' | |||
2 | --- examples/CMakeLists.txt 2017-08-21 15:58:34 +0000 | |||
3 | +++ examples/CMakeLists.txt 2017-08-31 08:02:08 +0000 | |||
4 | @@ -9,26 +9,17 @@ | |||
5 | 9 | ) | 9 | ) |
6 | 10 | 10 | ||
7 | 11 | add_library(exampleserverconfig STATIC | 11 | add_library(exampleserverconfig STATIC |
8 | 12 | server_example_basic_window_manager.cpp | ||
9 | 13 | server_example_basic_window_manager.h | ||
10 | 14 | server_example_canonical_window_manager.cpp | ||
11 | 15 | server_example_display_configuration_policy.cpp | ||
12 | 16 | server_example_input_device_config.cpp | 12 | server_example_input_device_config.cpp |
13 | 17 | server_example_input_event_filter.cpp | 13 | server_example_input_event_filter.cpp |
14 | 18 | server_example_log_options.cpp | 14 | server_example_log_options.cpp |
15 | 19 | server_example_input_filter.cpp | 15 | server_example_input_filter.cpp |
16 | 20 | server_example_host_lifecycle_event.cpp | 16 | server_example_host_lifecycle_event.cpp |
17 | 21 | server_example_window_management.cpp | ||
18 | 22 | server_example_custom_compositor.cpp | 17 | server_example_custom_compositor.cpp |
19 | 23 | server_example_adorning_compositor.cpp | 18 | server_example_adorning_compositor.cpp |
20 | 24 | server_example_cursor_images.cpp | ||
21 | 25 | server_example_cursor_images.h | ||
22 | 26 | xcursor_loader.cpp | ||
23 | 27 | xcursor_loader.h | ||
24 | 28 | xcursor.c | ||
25 | 29 | xcursor.h | ||
26 | 30 | ) | 19 | ) |
27 | 31 | 20 | ||
28 | 21 | target_link_libraries(exampleserverconfig mirserver) | ||
29 | 22 | |||
30 | 32 | target_link_libraries(eglapp | 23 | target_link_libraries(eglapp |
31 | 33 | mirclient | 24 | mirclient |
32 | 34 | ${EGL_LIBRARIES} | 25 | ${EGL_LIBRARIES} |
33 | @@ -175,6 +166,7 @@ | |||
34 | 175 | add_library(mirdraw STATIC graphics_utils.cpp) | 166 | add_library(mirdraw STATIC graphics_utils.cpp) |
35 | 176 | 167 | ||
36 | 177 | include_directories( | 168 | include_directories( |
37 | 169 | ${PROJECT_SOURCE_DIR}/include/miral | ||
38 | 178 | ${PROJECT_SOURCE_DIR}/include/server | 170 | ${PROJECT_SOURCE_DIR}/include/server |
39 | 179 | ${PROJECT_SOURCE_DIR}/include/client | 171 | ${PROJECT_SOURCE_DIR}/include/client |
40 | 180 | ${PROJECT_SOURCE_DIR}/include/platform | 172 | ${PROJECT_SOURCE_DIR}/include/platform |
41 | @@ -190,7 +182,8 @@ | |||
42 | 190 | ) | 182 | ) |
43 | 191 | 183 | ||
44 | 192 | target_link_libraries(mir_demo_server_loadable | 184 | target_link_libraries(mir_demo_server_loadable |
46 | 193 | mirserver | 185 | miral-shell-lib |
47 | 186 | miral | ||
48 | 194 | exampleserverconfig | 187 | exampleserverconfig |
49 | 195 | ${GLog_LIBRARY} | 188 | ${GLog_LIBRARY} |
50 | 196 | ${GFlags_LIBRARY} | 189 | ${GFlags_LIBRARY} |
51 | @@ -248,15 +241,6 @@ | |||
52 | 248 | endif () | 241 | endif () |
53 | 249 | endif () | 242 | endif () |
54 | 250 | 243 | ||
55 | 251 | add_executable(mir_cursor_theme_dump | ||
56 | 252 | cursor-theme-dump.cpp | ||
57 | 253 | ) | ||
58 | 254 | |||
59 | 255 | target_link_libraries(mir_cursor_theme_dump | ||
60 | 256 | exampleserverconfig | ||
61 | 257 | mirserver | ||
62 | 258 | ) | ||
63 | 259 | |||
64 | 260 | mir_add_wrapped_executable(mir_demo_client_multistream | 244 | mir_add_wrapped_executable(mir_demo_client_multistream |
65 | 261 | multi_stream.cpp | 245 | multi_stream.cpp |
66 | 262 | ) | 246 | ) |
67 | 263 | 247 | ||
68 | === removed file 'examples/cursor-theme-dump.cpp' | |||
69 | --- examples/cursor-theme-dump.cpp 2017-07-28 17:00:43 +0000 | |||
70 | +++ examples/cursor-theme-dump.cpp 1970-01-01 00:00:00 +0000 | |||
71 | @@ -1,127 +0,0 @@ | |||
72 | 1 | /* | ||
73 | 2 | * Copyright © 2015 Canonical Ltd. | ||
74 | 3 | * | ||
75 | 4 | * This program is free software: you can redistribute it and/or modify it | ||
76 | 5 | * under the terms of the GNU General Public License version 2 or 3, | ||
77 | 6 | * as published by the Free Software Foundation. | ||
78 | 7 | * | ||
79 | 8 | * This program is distributed in the hope that it will be useful, | ||
80 | 9 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
81 | 10 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
82 | 11 | * GNU General Public License for more details. | ||
83 | 12 | * | ||
84 | 13 | * You should have received a copy of the GNU General Public License | ||
85 | 14 | * along with this program. If not, see <http://www.gnu.org/licenses/>. | ||
86 | 15 | * | ||
87 | 16 | * Authored by: Alan Griffiths <alan@octopull.co.uk> | ||
88 | 17 | */ | ||
89 | 18 | |||
90 | 19 | #include "xcursor_loader.h" | ||
91 | 20 | |||
92 | 21 | #include "mir/graphics/cursor_image.h" | ||
93 | 22 | #include <mir_toolkit/cursors.h> | ||
94 | 23 | |||
95 | 24 | #include <fstream> | ||
96 | 25 | |||
97 | 26 | using namespace mir::examples; | ||
98 | 27 | using namespace mir::geometry; | ||
99 | 28 | |||
100 | 29 | namespace | ||
101 | 30 | { | ||
102 | 31 | auto const cursor_names = { | ||
103 | 32 | mir_arrow_cursor_name, | ||
104 | 33 | mir_busy_cursor_name, | ||
105 | 34 | mir_caret_cursor_name, | ||
106 | 35 | mir_default_cursor_name, | ||
107 | 36 | mir_pointing_hand_cursor_name, | ||
108 | 37 | mir_open_hand_cursor_name, | ||
109 | 38 | mir_closed_hand_cursor_name, | ||
110 | 39 | mir_horizontal_resize_cursor_name, | ||
111 | 40 | mir_vertical_resize_cursor_name, | ||
112 | 41 | mir_diagonal_resize_bottom_to_top_cursor_name, | ||
113 | 42 | mir_diagonal_resize_top_to_bottom_cursor_name, | ||
114 | 43 | mir_omnidirectional_resize_cursor_name, | ||
115 | 44 | mir_vsplit_resize_cursor_name, | ||
116 | 45 | mir_hsplit_resize_cursor_name, | ||
117 | 46 | mir_crosshair_cursor_name }; | ||
118 | 47 | } | ||
119 | 48 | |||
120 | 49 | |||
121 | 50 | int main(int argc, char const* argv[]) | ||
122 | 51 | try | ||
123 | 52 | { | ||
124 | 53 | if (argc != 2) | ||
125 | 54 | { | ||
126 | 55 | puts("Usage mir_cursor_theme_dump <cursor theme>\n"); | ||
127 | 56 | exit(-1); | ||
128 | 57 | } | ||
129 | 58 | |||
130 | 59 | auto const theme = argv[1]; | ||
131 | 60 | std::ofstream output(std::string{theme} + "-theme.h"); | ||
132 | 61 | |||
133 | 62 | output << "#include <initializer_list>\n" | ||
134 | 63 | "\n" | ||
135 | 64 | "namespace\n" | ||
136 | 65 | "{\n" | ||
137 | 66 | "struct CursorData\n" | ||
138 | 67 | "{\n" | ||
139 | 68 | " CursorData(char const* name, unsigned int hotspot_x, unsigned int hotspot_y, char const* pixel_data) :\n" | ||
140 | 69 | " name(name), hotspot_x(hotspot_x), hotspot_y(hotspot_y), pixel_data(reinterpret_cast<unsigned char const*>(pixel_data)) {}\n" | ||
141 | 70 | "\n" | ||
142 | 71 | " unsigned int const width{" << mir::input::default_cursor_size.width.as_int() << "};\n" | ||
143 | 72 | " unsigned int const height{" << mir::input::default_cursor_size.height.as_int() << "};\n" | ||
144 | 73 | " char const* const name;\n" | ||
145 | 74 | " unsigned int const hotspot_x;\n" | ||
146 | 75 | " unsigned int const hotspot_y;\n" | ||
147 | 76 | " unsigned char const* const pixel_data;\n" | ||
148 | 77 | "};\n" | ||
149 | 78 | "auto const cursor_data = {\n"; | ||
150 | 79 | |||
151 | 80 | auto const buffer_size = 4*mir::input::default_cursor_size.height.as_int()*mir::input::default_cursor_size.height.as_int(); | ||
152 | 81 | |||
153 | 82 | auto const xcursor_loader = std::make_shared<XCursorLoader>(theme); | ||
154 | 83 | |||
155 | 84 | for (auto cursor : cursor_names) | ||
156 | 85 | { | ||
157 | 86 | if (auto const image = xcursor_loader->image(cursor, mir::input::default_cursor_size)) | ||
158 | 87 | { | ||
159 | 88 | printf("Have image for %s:%s\n", theme, cursor); | ||
160 | 89 | |||
161 | 90 | auto const hotspot = image->hotspot(); | ||
162 | 91 | auto const argb_8888 = static_cast<uint8_t const*>(image->as_argb_8888()); | ||
163 | 92 | |||
164 | 93 | output << "CursorData{\"" << cursor << "\", " << hotspot.dx.as_int() << ", " << hotspot.dy.as_int() << ",\n"; | ||
165 | 94 | |||
166 | 95 | int chars = 0; | ||
167 | 96 | |||
168 | 97 | output << std::oct << " \""; | ||
169 | 98 | |||
170 | 99 | for (auto pbyte = argb_8888; pbyte != argb_8888 + buffer_size; ++pbyte) | ||
171 | 100 | { | ||
172 | 101 | auto step = (*pbyte < 010) ? 2 : (*pbyte < 0100) ? 3 : 4; | ||
173 | 102 | |||
174 | 103 | if ((chars += step) > 80) | ||
175 | 104 | { | ||
176 | 105 | output << "\"\n \""; | ||
177 | 106 | chars = step; | ||
178 | 107 | } | ||
179 | 108 | |||
180 | 109 | output << '\\' << static_cast<unsigned>(*pbyte); | ||
181 | 110 | } | ||
182 | 111 | |||
183 | 112 | output << "\"\n"; | ||
184 | 113 | |||
185 | 114 | output << "},\n"; | ||
186 | 115 | } | ||
187 | 116 | else | ||
188 | 117 | { | ||
189 | 118 | printf("** WARNING ** No image for %s:%s\n", theme, cursor); | ||
190 | 119 | } | ||
191 | 120 | } | ||
192 | 121 | output << "};\n"; | ||
193 | 122 | output << "}\n"; | ||
194 | 123 | } | ||
195 | 124 | catch (std::exception const& error) | ||
196 | 125 | { | ||
197 | 126 | printf("** ERROR **: %s", error.what()); | ||
198 | 127 | } | ||
199 | 128 | \ No newline at end of file | 0 | \ No newline at end of file |
200 | 129 | 1 | ||
201 | === modified file 'examples/miral-shell/CMakeLists.txt' | |||
202 | --- examples/miral-shell/CMakeLists.txt 2017-08-21 15:58:34 +0000 | |||
203 | +++ examples/miral-shell/CMakeLists.txt 2017-08-31 08:02:08 +0000 | |||
204 | @@ -46,20 +46,24 @@ | |||
205 | 46 | DESTINATION ${CMAKE_INSTALL_PREFIX}/bin | 46 | DESTINATION ${CMAKE_INSTALL_PREFIX}/bin |
206 | 47 | ) | 47 | ) |
207 | 48 | 48 | ||
210 | 49 | mir_add_wrapped_executable(miral-shell | 49 | add_library(miral-shell-lib STATIC |
209 | 50 | shell_main.cpp | ||
211 | 51 | tiling_window_manager.cpp tiling_window_manager.h | 50 | tiling_window_manager.cpp tiling_window_manager.h |
213 | 52 | floating_window_manager.cpp floating_window_manager.h | 51 | floating_window_manager.cpp floating_window_manager.h |
214 | 53 | decoration_provider.cpp decoration_provider.h | 52 | decoration_provider.cpp decoration_provider.h |
215 | 54 | titlebar_config.cpp titlebar_config.h | 53 | titlebar_config.cpp titlebar_config.h |
216 | 55 | ) | 54 | ) |
217 | 56 | 55 | ||
218 | 57 | pkg_check_modules(FREETYPE freetype2 REQUIRED) | 56 | pkg_check_modules(FREETYPE freetype2 REQUIRED) |
221 | 58 | target_include_directories(miral-shell PRIVATE ${FREETYPE_INCLUDE_DIRS}) | 57 | target_include_directories(miral-shell-lib PRIVATE ${FREETYPE_INCLUDE_DIRS}) |
222 | 59 | target_compile_definitions(miral-shell PRIVATE -DTYPO_SUPPORTS_FREETYPE) | 58 | target_compile_definitions(miral-shell-lib PRIVATE -DTYPO_SUPPORTS_FREETYPE) |
223 | 59 | target_link_libraries(miral-shell-lib miral-spinner miral ${FREETYPE_LIBRARIES}) | ||
224 | 60 | |||
225 | 61 | mir_add_wrapped_executable(miral-shell | ||
226 | 62 | shell_main.cpp | ||
227 | 63 | ) | ||
228 | 64 | |||
229 | 60 | target_link_libraries(miral-shell | 65 | target_link_libraries(miral-shell |
231 | 61 | miral-spinner | 66 | miral-shell-lib |
232 | 62 | miral | 67 | miral |
233 | 63 | ${FREETYPE_LIBRARIES} | ||
234 | 64 | ) | 68 | ) |
235 | 65 | 69 | ||
236 | 66 | 70 | ||
237 | === modified file 'examples/server_example.cpp' | |||
238 | --- examples/server_example.cpp 2017-07-28 17:00:43 +0000 | |||
239 | +++ examples/server_example.cpp 2017-08-31 08:02:08 +0000 | |||
240 | @@ -1,5 +1,5 @@ | |||
241 | 1 | /* | 1 | /* |
243 | 2 | * Copyright © 2012-2015 Canonical Ltd. | 2 | * Copyright © 2012-2017 Canonical Ltd. |
244 | 3 | * | 3 | * |
245 | 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 |
246 | 5 | * it under the terms of the GNU General Public License version 2 or 3 as | 5 | * it under the terms of the GNU General Public License version 2 or 3 as |
247 | @@ -19,19 +19,24 @@ | |||
248 | 19 | #include "server_example_log_options.h" | 19 | #include "server_example_log_options.h" |
249 | 20 | #include "server_example_input_event_filter.h" | 20 | #include "server_example_input_event_filter.h" |
250 | 21 | #include "server_example_input_filter.h" | 21 | #include "server_example_input_filter.h" |
251 | 22 | #include "server_example_display_configuration_policy.h" | ||
252 | 23 | #include "server_example_host_lifecycle_event_listener.h" | 22 | #include "server_example_host_lifecycle_event_listener.h" |
253 | 24 | #include "server_example_window_management.h" | ||
254 | 25 | #include "server_example_custom_compositor.h" | 23 | #include "server_example_custom_compositor.h" |
255 | 26 | #include "server_example_test_client.h" | 24 | #include "server_example_test_client.h" |
256 | 27 | #include "server_example_cursor_images.h" | ||
257 | 28 | #include "server_example_input_device_config.h" | 25 | #include "server_example_input_device_config.h" |
258 | 29 | 26 | ||
259 | 27 | #include "miral-shell/tiling_window_manager.h" | ||
260 | 28 | #include "miral-shell/floating_window_manager.h" | ||
261 | 29 | #include "miral-shell/titlebar_config.h" | ||
262 | 30 | #include "miral-shell/spinner/splash.h" | ||
263 | 31 | |||
264 | 32 | #include <miral/cursor_theme.h> | ||
265 | 33 | #include <miral/display_configuration_option.h> | ||
266 | 34 | #include <miral/runner.h> | ||
267 | 35 | #include <miral/window_management_options.h> | ||
268 | 36 | |||
269 | 30 | #include "mir/abnormal_exit.h" | 37 | #include "mir/abnormal_exit.h" |
270 | 31 | #include "mir/server.h" | 38 | #include "mir/server.h" |
271 | 32 | #include "mir/main_loop.h" | 39 | #include "mir/main_loop.h" |
272 | 33 | #include "mir/fd.h" | ||
273 | 34 | |||
274 | 35 | #include "mir/report_exception.h" | 40 | #include "mir/report_exception.h" |
275 | 36 | #include "mir/options/option.h" | 41 | #include "mir/options/option.h" |
276 | 37 | 42 | ||
277 | @@ -40,6 +45,8 @@ | |||
278 | 40 | #include <chrono> | 45 | #include <chrono> |
279 | 41 | #include <cstdlib> | 46 | #include <cstdlib> |
280 | 42 | 47 | ||
281 | 48 | namespace mir { class AbnormalExit; } | ||
282 | 49 | |||
283 | 43 | namespace me = mir::examples; | 50 | namespace me = mir::examples; |
284 | 44 | 51 | ||
285 | 45 | ///\example server_example.cpp | 52 | ///\example server_example.cpp |
286 | @@ -47,51 +54,6 @@ | |||
287 | 47 | 54 | ||
288 | 48 | namespace | 55 | namespace |
289 | 49 | { | 56 | { |
290 | 50 | auto connection(int fd) -> std::string | ||
291 | 51 | { | ||
292 | 52 | char connect_string[64] = {0}; | ||
293 | 53 | // We can't have both the server and the client owning the same fd, since | ||
294 | 54 | // that will result in a double-close(). We give the client a duplicate which | ||
295 | 55 | // the client can safely own (and should close when done). | ||
296 | 56 | sprintf(connect_string, "fd://%d", dup(fd)); | ||
297 | 57 | return connect_string; | ||
298 | 58 | } | ||
299 | 59 | |||
300 | 60 | void add_launcher_option_to(mir::Server& server) | ||
301 | 61 | { | ||
302 | 62 | static const char* const launch_child_opt = "launch-client"; | ||
303 | 63 | static const char* const launch_client_descr = "system() command to launch client"; | ||
304 | 64 | |||
305 | 65 | server.add_configuration_option(launch_child_opt, launch_client_descr, mir::OptionType::string); | ||
306 | 66 | server.add_init_callback([&] | ||
307 | 67 | { | ||
308 | 68 | const auto options = server.get_options(); | ||
309 | 69 | if (options->is_set(launch_child_opt)) | ||
310 | 70 | { | ||
311 | 71 | unsetenv("DISPLAY"); // Discourage toolkits from using X11 | ||
312 | 72 | setenv("GDK_BACKEND", "mir", true); // configure GTK to use Mir | ||
313 | 73 | setenv("QT_QPA_PLATFORM", "ubuntumirclient", true); // configure Qt to use Mir | ||
314 | 74 | unsetenv("QT_QPA_PLATFORMTHEME"); // Discourage Qt from unsupported theme | ||
315 | 75 | setenv("SDL_VIDEODRIVER", "mir", true); // configure SDL to use Mir | ||
316 | 76 | |||
317 | 77 | auto const value = options->get<std::string>(launch_child_opt); | ||
318 | 78 | |||
319 | 79 | for (auto i = begin(value); i != end(value); ) | ||
320 | 80 | { | ||
321 | 81 | auto const j = find(i, end(value), '&'); | ||
322 | 82 | |||
323 | 83 | auto const cmd ="MIR_SOCKET=" + connection(server.open_client_socket()) + " " + | ||
324 | 84 | std::string{i, j} + "&"; | ||
325 | 85 | |||
326 | 86 | auto ignore = std::system(cmd.c_str()); | ||
327 | 87 | (void)(ignore); | ||
328 | 88 | |||
329 | 89 | if ((i = j) != end(value)) ++i; | ||
330 | 90 | } | ||
331 | 91 | } | ||
332 | 92 | }); | ||
333 | 93 | } | ||
334 | 94 | |||
335 | 95 | void add_timeout_option_to(mir::Server& server) | 57 | void add_timeout_option_to(mir::Server& server) |
336 | 96 | { | 58 | { |
337 | 97 | static const char* const timeout_opt = "timeout"; | 59 | static const char* const timeout_opt = "timeout"; |
338 | @@ -110,6 +72,23 @@ | |||
339 | 110 | }); | 72 | }); |
340 | 111 | } | 73 | } |
341 | 112 | 74 | ||
342 | 75 | |||
343 | 76 | // Create some input filters (we need to keep them or they deactivate) | ||
344 | 77 | struct InputFilters | ||
345 | 78 | { | ||
346 | 79 | void operator()(mir::Server& server) | ||
347 | 80 | { | ||
348 | 81 | quit_filter = me::make_quit_filter_for(server); | ||
349 | 82 | printing_filter = me::make_printing_input_filter_for(server); | ||
350 | 83 | screen_rotation_filter = me::make_screen_rotation_filter_for(server); | ||
351 | 84 | } | ||
352 | 85 | |||
353 | 86 | private: | ||
354 | 87 | std::shared_ptr<mir::input::EventFilter> quit_filter; | ||
355 | 88 | std::shared_ptr<mir::input::EventFilter> printing_filter; | ||
356 | 89 | std::shared_ptr<mir::input::EventFilter> screen_rotation_filter; | ||
357 | 90 | }; | ||
358 | 91 | |||
359 | 113 | void exception_handler() | 92 | void exception_handler() |
360 | 114 | try | 93 | try |
361 | 115 | { | 94 | { |
362 | @@ -145,44 +124,48 @@ | |||
363 | 145 | int main(int argc, char const* argv[]) | 124 | int main(int argc, char const* argv[]) |
364 | 146 | try | 125 | try |
365 | 147 | { | 126 | { |
396 | 148 | mir::Server server; | 127 | miral::MirRunner runner{argc, argv, "mir/mir_demo_server.config"}; |
397 | 149 | 128 | ||
398 | 150 | // Use config options file in e.g. ~/.config/mir/mir_demo_server.config | 129 | runner.set_exception_handler(exception_handler); |
399 | 151 | server.set_config_filename("mir/mir_demo_server.config"); | 130 | |
400 | 152 | 131 | std::function<void()> shutdown_hook{[]{}}; | |
401 | 153 | // Add example options for display layout, logging, launching clients and timeout | 132 | runner.add_stop_callback([&] { shutdown_hook(); }); |
402 | 154 | me::add_display_configuration_options_to(server); | 133 | |
403 | 155 | me::add_log_host_lifecycle_option_to(server); | 134 | SpinnerSplash spinner; |
404 | 156 | me::add_glog_options_to(server); | 135 | miral::InternalClientLauncher launcher; |
405 | 157 | me::add_window_manager_option_to(server); | 136 | miral::ActiveOutputsMonitor outputs_monitor; |
406 | 158 | me::add_custom_compositor_option_to(server); | 137 | miral::WindowManagerOptions window_managers |
407 | 159 | me::add_input_device_configuration_options_to(server); | 138 | { |
408 | 160 | add_launcher_option_to(server); | 139 | miral::add_window_manager_policy<FloatingWindowManagerPolicy>("floating", spinner, launcher, shutdown_hook), |
409 | 161 | add_timeout_option_to(server); | 140 | miral::add_window_manager_policy<TilingWindowManagerPolicy>("tiling", spinner, launcher, outputs_monitor), |
410 | 162 | me::add_x_cursor_images(server); | 141 | }; |
411 | 163 | 142 | ||
412 | 164 | server.set_exception_handler(exception_handler); | 143 | InputFilters input_filters; |
413 | 165 | 144 | me::TestClientRunner test_runner; | |
414 | 166 | me::ClientContext context; | 145 | |
415 | 167 | me::add_test_client_option_to(server, context); | 146 | bool const server_exited_normally = runner.run_with({ |
416 | 168 | 147 | // example options for display layout, logging and timeout | |
417 | 169 | // Create some input filters (we need to keep them or they deactivate) | 148 | miral::display_configuration_options, |
418 | 170 | auto const quit_filter = me::make_quit_filter_for(server); | 149 | me::add_log_host_lifecycle_option_to, |
419 | 171 | auto const printing_filter = me::make_printing_input_filter_for(server); | 150 | me::add_glog_options_to, |
420 | 172 | auto const screen_rotation_filter = me::make_screen_rotation_filter_for(server); | 151 | miral::StartupInternalClient{"Intro", spinner}, |
421 | 173 | 152 | launcher, | |
422 | 174 | // Provide the command line and run the server | 153 | window_managers, |
423 | 175 | server.set_command_line(argc, argv); | 154 | me::add_custom_compositor_option_to, |
424 | 176 | server.apply_settings(); | 155 | me::add_input_device_configuration_options_to, |
425 | 177 | server.run(); | 156 | add_timeout_option_to, |
426 | 157 | miral::CursorTheme{"default"}, | ||
427 | 158 | input_filters, | ||
428 | 159 | test_runner | ||
429 | 160 | }); | ||
430 | 178 | 161 | ||
431 | 179 | // Propagate any test failure | 162 | // Propagate any test failure |
433 | 180 | if (context.test_failed) | 163 | if (test_runner.test_failed()) |
434 | 181 | { | 164 | { |
435 | 182 | return EXIT_FAILURE; | 165 | return EXIT_FAILURE; |
436 | 183 | } | 166 | } |
437 | 184 | 167 | ||
439 | 185 | return server.exited_normally() ? EXIT_SUCCESS : EXIT_FAILURE; | 168 | return server_exited_normally ? EXIT_SUCCESS : EXIT_FAILURE; |
440 | 186 | } | 169 | } |
441 | 187 | catch (...) | 170 | catch (...) |
442 | 188 | { | 171 | { |
443 | 189 | 172 | ||
444 | === removed file 'examples/server_example_basic_window_manager.cpp' | |||
445 | --- examples/server_example_basic_window_manager.cpp 2017-07-28 17:00:43 +0000 | |||
446 | +++ examples/server_example_basic_window_manager.cpp 1970-01-01 00:00:00 +0000 | |||
447 | @@ -1,328 +0,0 @@ | |||
448 | 1 | /* | ||
449 | 2 | * Copyright © 2015 Canonical Ltd. | ||
450 | 3 | * | ||
451 | 4 | * This program is free software: you can redistribute it and/or modify it | ||
452 | 5 | * under the terms of the GNU General Public License version 2 or 3, | ||
453 | 6 | * as published by the Free Software Foundation. | ||
454 | 7 | * | ||
455 | 8 | * This program is distributed in the hope that it will be useful, | ||
456 | 9 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
457 | 10 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
458 | 11 | * GNU General Public License for more details. | ||
459 | 12 | * | ||
460 | 13 | * You should have received a copy of the GNU General Public License | ||
461 | 14 | * along with this program. If not, see <http://www.gnu.org/licenses/>. | ||
462 | 15 | * | ||
463 | 16 | * Authored by: Alan Griffiths <alan@octopull.co.uk> | ||
464 | 17 | */ | ||
465 | 18 | |||
466 | 19 | #include "server_example_basic_window_manager.h" | ||
467 | 20 | |||
468 | 21 | #include "mir/scene/session.h" | ||
469 | 22 | #include "mir/scene/surface.h" | ||
470 | 23 | #include "mir/scene/surface_creation_parameters.h" | ||
471 | 24 | |||
472 | 25 | namespace me = mir::examples; | ||
473 | 26 | |||
474 | 27 | me::BasicWindowManager::BasicWindowManager( | ||
475 | 28 | shell::FocusController* focus_controller, | ||
476 | 29 | std::unique_ptr<WindowManagementPolicy> policy) : | ||
477 | 30 | focus_controller(focus_controller), | ||
478 | 31 | policy(std::move(policy)) | ||
479 | 32 | { | ||
480 | 33 | } | ||
481 | 34 | |||
482 | 35 | void me::BasicWindowManager::add_session(std::shared_ptr<scene::Session> const& session) | ||
483 | 36 | { | ||
484 | 37 | std::lock_guard<decltype(mutex)> lock(mutex); | ||
485 | 38 | session_info[session] = SessionInfo(); | ||
486 | 39 | policy->handle_session_info_updated(session_info, displays); | ||
487 | 40 | } | ||
488 | 41 | |||
489 | 42 | void me::BasicWindowManager::remove_session(std::shared_ptr<scene::Session> const& session) | ||
490 | 43 | { | ||
491 | 44 | std::lock_guard<decltype(mutex)> lock(mutex); | ||
492 | 45 | session_info.erase(session); | ||
493 | 46 | policy->handle_session_info_updated(session_info, displays); | ||
494 | 47 | } | ||
495 | 48 | |||
496 | 49 | auto me::BasicWindowManager::add_surface( | ||
497 | 50 | std::shared_ptr<scene::Session> const& session, | ||
498 | 51 | scene::SurfaceCreationParameters const& params, | ||
499 | 52 | std::function<frontend::SurfaceId(std::shared_ptr<scene::Session> const& session, scene::SurfaceCreationParameters const& params)> const& build) | ||
500 | 53 | -> frontend::SurfaceId | ||
501 | 54 | { | ||
502 | 55 | std::lock_guard<decltype(mutex)> lock(mutex); | ||
503 | 56 | scene::SurfaceCreationParameters const placed_params = policy->handle_place_new_surface(session, params); | ||
504 | 57 | auto const result = build(session, placed_params); | ||
505 | 58 | auto const surface = session->surface(result); | ||
506 | 59 | surface_info.emplace(surface, SurfaceInfo{session, surface, placed_params}); | ||
507 | 60 | policy->handle_new_surface(session, surface); | ||
508 | 61 | policy->generate_decorations_for(session, surface, surface_info, build); | ||
509 | 62 | return result; | ||
510 | 63 | } | ||
511 | 64 | |||
512 | 65 | void me::BasicWindowManager::modify_surface( | ||
513 | 66 | std::shared_ptr<scene::Session> const& session, | ||
514 | 67 | std::shared_ptr<scene::Surface> const& surface, | ||
515 | 68 | shell::SurfaceSpecification const& modifications) | ||
516 | 69 | { | ||
517 | 70 | std::lock_guard<decltype(mutex)> lock(mutex); | ||
518 | 71 | policy->handle_modify_surface(session, surface, modifications); | ||
519 | 72 | } | ||
520 | 73 | |||
521 | 74 | void me::BasicWindowManager::remove_surface( | ||
522 | 75 | std::shared_ptr<scene::Session> const& session, | ||
523 | 76 | std::weak_ptr<scene::Surface> const& surface) | ||
524 | 77 | { | ||
525 | 78 | std::lock_guard<decltype(mutex)> lock(mutex); | ||
526 | 79 | policy->handle_delete_surface(session, surface); | ||
527 | 80 | |||
528 | 81 | surface_info.erase(surface); | ||
529 | 82 | } | ||
530 | 83 | |||
531 | 84 | void me::BasicWindowManager::forget(std::weak_ptr<scene::Surface> const& surface) | ||
532 | 85 | { | ||
533 | 86 | surface_info.erase(surface); | ||
534 | 87 | } | ||
535 | 88 | |||
536 | 89 | void me::BasicWindowManager::add_display(geometry::Rectangle const& area) | ||
537 | 90 | { | ||
538 | 91 | std::lock_guard<decltype(mutex)> lock(mutex); | ||
539 | 92 | displays.add(area); | ||
540 | 93 | policy->handle_displays_updated(session_info, displays); | ||
541 | 94 | } | ||
542 | 95 | |||
543 | 96 | void me::BasicWindowManager::remove_display(geometry::Rectangle const& area) | ||
544 | 97 | { | ||
545 | 98 | std::lock_guard<decltype(mutex)> lock(mutex); | ||
546 | 99 | displays.remove(area); | ||
547 | 100 | policy->handle_displays_updated(session_info, displays); | ||
548 | 101 | } | ||
549 | 102 | |||
550 | 103 | bool me::BasicWindowManager::handle_keyboard_event(MirKeyboardEvent const* event) | ||
551 | 104 | { | ||
552 | 105 | std::lock_guard<decltype(mutex)> lock(mutex); | ||
553 | 106 | update_event_timestamp(event); | ||
554 | 107 | return policy->handle_keyboard_event(event); | ||
555 | 108 | } | ||
556 | 109 | |||
557 | 110 | bool me::BasicWindowManager::handle_touch_event(MirTouchEvent const* event) | ||
558 | 111 | { | ||
559 | 112 | std::lock_guard<decltype(mutex)> lock(mutex); | ||
560 | 113 | update_event_timestamp(event); | ||
561 | 114 | return policy->handle_touch_event(event); | ||
562 | 115 | } | ||
563 | 116 | |||
564 | 117 | bool me::BasicWindowManager::handle_pointer_event(MirPointerEvent const* event) | ||
565 | 118 | { | ||
566 | 119 | std::lock_guard<decltype(mutex)> lock(mutex); | ||
567 | 120 | update_event_timestamp(event); | ||
568 | 121 | |||
569 | 122 | cursor = { | ||
570 | 123 | mir_pointer_event_axis_value(event, mir_pointer_axis_x), | ||
571 | 124 | mir_pointer_event_axis_value(event, mir_pointer_axis_y)}; | ||
572 | 125 | |||
573 | 126 | return policy->handle_pointer_event(event); | ||
574 | 127 | } | ||
575 | 128 | |||
576 | 129 | void me::BasicWindowManager::handle_raise_surface( | ||
577 | 130 | std::shared_ptr<scene::Session> const& session, | ||
578 | 131 | std::shared_ptr<scene::Surface> const& surface, | ||
579 | 132 | uint64_t timestamp) | ||
580 | 133 | { | ||
581 | 134 | std::lock_guard<decltype(mutex)> lock(mutex); | ||
582 | 135 | if (timestamp >= last_input_event_timestamp) | ||
583 | 136 | policy->handle_raise_surface(session, surface); | ||
584 | 137 | } | ||
585 | 138 | |||
586 | 139 | void me::BasicWindowManager::handle_request_drag_and_drop( | ||
587 | 140 | std::shared_ptr<scene::Session> const& /*session*/, | ||
588 | 141 | std::shared_ptr<scene::Surface> const& /*surface*/, | ||
589 | 142 | uint64_t /*timestamp*/) | ||
590 | 143 | { | ||
591 | 144 | // Not supported in example servers | ||
592 | 145 | } | ||
593 | 146 | |||
594 | 147 | void me::BasicWindowManager::handle_request_move( | ||
595 | 148 | std::shared_ptr<scene::Session> const& /*session*/, | ||
596 | 149 | std::shared_ptr<scene::Surface> const& /*surface*/, | ||
597 | 150 | uint64_t /*timestamp*/) | ||
598 | 151 | { | ||
599 | 152 | // Not supported in example servers | ||
600 | 153 | } | ||
601 | 154 | |||
602 | 155 | int me::BasicWindowManager::set_surface_attribute( | ||
603 | 156 | std::shared_ptr<scene::Session> const& /*session*/, | ||
604 | 157 | std::shared_ptr<scene::Surface> const& surface, | ||
605 | 158 | MirWindowAttrib attrib, | ||
606 | 159 | int value) | ||
607 | 160 | { | ||
608 | 161 | std::lock_guard<decltype(mutex)> lock(mutex); | ||
609 | 162 | switch (attrib) | ||
610 | 163 | { | ||
611 | 164 | case mir_window_attrib_state: | ||
612 | 165 | { | ||
613 | 166 | auto const state = policy->handle_set_state(surface, MirWindowState(value)); | ||
614 | 167 | return surface->configure(attrib, state); | ||
615 | 168 | } | ||
616 | 169 | default: | ||
617 | 170 | return surface->configure(attrib, value); | ||
618 | 171 | } | ||
619 | 172 | } | ||
620 | 173 | |||
621 | 174 | auto me::BasicWindowManager::find_session(std::function<bool(SessionInfo const& info)> const& predicate) | ||
622 | 175 | -> std::shared_ptr<scene::Session> | ||
623 | 176 | { | ||
624 | 177 | for(auto& info : session_info) | ||
625 | 178 | { | ||
626 | 179 | if (predicate(info.second)) | ||
627 | 180 | { | ||
628 | 181 | return info.first.lock(); | ||
629 | 182 | } | ||
630 | 183 | } | ||
631 | 184 | |||
632 | 185 | return std::shared_ptr<scene::Session>{}; | ||
633 | 186 | } | ||
634 | 187 | |||
635 | 188 | auto me::BasicWindowManager::info_for(std::weak_ptr<scene::Session> const& session) const | ||
636 | 189 | -> SessionInfo& | ||
637 | 190 | { | ||
638 | 191 | return const_cast<SessionInfo&>(session_info.at(session)); | ||
639 | 192 | } | ||
640 | 193 | |||
641 | 194 | auto me::BasicWindowManager::info_for(std::weak_ptr<scene::Surface> const& surface) const | ||
642 | 195 | -> SurfaceInfo& | ||
643 | 196 | { | ||
644 | 197 | return const_cast<SurfaceInfo&>(surface_info.at(surface)); | ||
645 | 198 | } | ||
646 | 199 | |||
647 | 200 | auto me::BasicWindowManager::focused_session() const | ||
648 | 201 | -> std::shared_ptr<scene::Session> | ||
649 | 202 | { | ||
650 | 203 | return focus_controller->focused_session(); | ||
651 | 204 | } | ||
652 | 205 | |||
653 | 206 | auto me::BasicWindowManager::focused_surface() const | ||
654 | 207 | ->std::shared_ptr<scene::Surface> | ||
655 | 208 | { | ||
656 | 209 | return focus_controller->focused_surface(); | ||
657 | 210 | } | ||
658 | 211 | |||
659 | 212 | void me::BasicWindowManager::focus_next_session() | ||
660 | 213 | { | ||
661 | 214 | focus_controller->focus_next_session(); | ||
662 | 215 | } | ||
663 | 216 | |||
664 | 217 | void me::BasicWindowManager::set_focus_to( | ||
665 | 218 | std::shared_ptr<scene::Session> const& focus, | ||
666 | 219 | std::shared_ptr<scene::Surface> const& surface) | ||
667 | 220 | { | ||
668 | 221 | focus_controller->set_focus_to(focus, surface); | ||
669 | 222 | } | ||
670 | 223 | |||
671 | 224 | auto me::BasicWindowManager::surface_at(geometry::Point cursor) const | ||
672 | 225 | -> std::shared_ptr<scene::Surface> | ||
673 | 226 | { | ||
674 | 227 | return focus_controller->surface_at(cursor); | ||
675 | 228 | } | ||
676 | 229 | |||
677 | 230 | auto me::BasicWindowManager::active_display() | ||
678 | 231 | -> geometry::Rectangle const | ||
679 | 232 | { | ||
680 | 233 | geometry::Rectangle result; | ||
681 | 234 | |||
682 | 235 | // 1. If a window has input focus, whichever display contains the largest | ||
683 | 236 | // proportion of the area of that window. | ||
684 | 237 | if (auto const surface = focused_surface()) | ||
685 | 238 | { | ||
686 | 239 | auto const surface_rect = surface->input_bounds(); | ||
687 | 240 | int max_overlap_area = -1; | ||
688 | 241 | |||
689 | 242 | for (auto const& display : displays) | ||
690 | 243 | { | ||
691 | 244 | auto const intersection = surface_rect.intersection_with(display).size; | ||
692 | 245 | if (intersection.width.as_int()*intersection.height.as_int() > max_overlap_area) | ||
693 | 246 | { | ||
694 | 247 | max_overlap_area = intersection.width.as_int()*intersection.height.as_int(); | ||
695 | 248 | result = display; | ||
696 | 249 | } | ||
697 | 250 | } | ||
698 | 251 | return result; | ||
699 | 252 | } | ||
700 | 253 | |||
701 | 254 | // 2. Otherwise, if any window previously had input focus, for the window that had | ||
702 | 255 | // it most recently, the display that contained the largest proportion of the | ||
703 | 256 | // area of that window at the moment it closed, as long as that display is still | ||
704 | 257 | // available. | ||
705 | 258 | |||
706 | 259 | // 3. Otherwise, the display that contains the pointer, if there is one. | ||
707 | 260 | for (auto const& display : displays) | ||
708 | 261 | { | ||
709 | 262 | if (display.contains(cursor)) | ||
710 | 263 | { | ||
711 | 264 | // Ignore the (unspecified) possiblity of overlapping displays | ||
712 | 265 | return display; | ||
713 | 266 | } | ||
714 | 267 | } | ||
715 | 268 | |||
716 | 269 | // 4. Otherwise, the primary display, if there is one (for example, the laptop display). | ||
717 | 270 | |||
718 | 271 | // 5. Otherwise, the first display. | ||
719 | 272 | if (displays.size()) | ||
720 | 273 | result = *displays.begin(); | ||
721 | 274 | |||
722 | 275 | return result; | ||
723 | 276 | } | ||
724 | 277 | |||
725 | 278 | void me::BasicWindowManager::raise_tree(std::shared_ptr<scene::Surface> const& root) | ||
726 | 279 | { | ||
727 | 280 | SurfaceSet surfaces; | ||
728 | 281 | std::function<void(std::weak_ptr<scene::Surface> const& surface)> const add_children = | ||
729 | 282 | [&,this](std::weak_ptr<scene::Surface> const& surface) | ||
730 | 283 | { | ||
731 | 284 | auto const& info = info_for(surface); | ||
732 | 285 | surfaces.insert(begin(info.children), end(info.children)); | ||
733 | 286 | for (auto const& child : info.children) | ||
734 | 287 | add_children(child); | ||
735 | 288 | }; | ||
736 | 289 | |||
737 | 290 | surfaces.insert(root); | ||
738 | 291 | add_children(root); | ||
739 | 292 | |||
740 | 293 | focus_controller->raise(surfaces); | ||
741 | 294 | } | ||
742 | 295 | |||
743 | 296 | void me::BasicWindowManager::update_event_timestamp(MirKeyboardEvent const* kev) | ||
744 | 297 | { | ||
745 | 298 | auto iev = mir_keyboard_event_input_event(kev); | ||
746 | 299 | last_input_event_timestamp = mir_input_event_get_event_time(iev); | ||
747 | 300 | } | ||
748 | 301 | |||
749 | 302 | void me::BasicWindowManager::update_event_timestamp(MirPointerEvent const* pev) | ||
750 | 303 | { | ||
751 | 304 | auto iev = mir_pointer_event_input_event(pev); | ||
752 | 305 | auto pointer_action = mir_pointer_event_action(pev); | ||
753 | 306 | |||
754 | 307 | if (pointer_action == mir_pointer_action_button_up || | ||
755 | 308 | pointer_action == mir_pointer_action_button_down) | ||
756 | 309 | { | ||
757 | 310 | last_input_event_timestamp = mir_input_event_get_event_time(iev); | ||
758 | 311 | } | ||
759 | 312 | } | ||
760 | 313 | |||
761 | 314 | void me::BasicWindowManager::update_event_timestamp(MirTouchEvent const* tev) | ||
762 | 315 | { | ||
763 | 316 | auto iev = mir_touch_event_input_event(tev); | ||
764 | 317 | auto touch_count = mir_touch_event_point_count(tev); | ||
765 | 318 | for (unsigned i = 0; i < touch_count; i++) | ||
766 | 319 | { | ||
767 | 320 | auto touch_action = mir_touch_event_action(tev, i); | ||
768 | 321 | if (touch_action == mir_touch_action_up || | ||
769 | 322 | touch_action == mir_touch_action_down) | ||
770 | 323 | { | ||
771 | 324 | last_input_event_timestamp = mir_input_event_get_event_time(iev); | ||
772 | 325 | break; | ||
773 | 326 | } | ||
774 | 327 | } | ||
775 | 328 | } | ||
776 | 329 | 0 | ||
777 | === removed file 'examples/server_example_basic_window_manager.h' | |||
778 | --- examples/server_example_basic_window_manager.h 2017-07-28 17:00:43 +0000 | |||
779 | +++ examples/server_example_basic_window_manager.h 1970-01-01 00:00:00 +0000 | |||
780 | @@ -1,264 +0,0 @@ | |||
781 | 1 | /* | ||
782 | 2 | * Copyright © 2015 Canonical Ltd. | ||
783 | 3 | * | ||
784 | 4 | * This program is free software: you can redistribute it and/or modify it | ||
785 | 5 | * under the terms of the GNU General Public License version 2 or 3, | ||
786 | 6 | * as published by the Free Software Foundation. | ||
787 | 7 | * | ||
788 | 8 | * This program is distributed in the hope that it will be useful, | ||
789 | 9 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
790 | 10 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
791 | 11 | * GNU General Public License for more details. | ||
792 | 12 | * | ||
793 | 13 | * You should have received a copy of the GNU General Public License | ||
794 | 14 | * along with this program. If not, see <http://www.gnu.org/licenses/>. | ||
795 | 15 | * | ||
796 | 16 | * Authored By: Alan Griffiths <alan@octopull.co.uk> | ||
797 | 17 | */ | ||
798 | 18 | |||
799 | 19 | #ifndef MIR_EXAMPLE_BASIC_WINDOW_MANAGER_H_ | ||
800 | 20 | #define MIR_EXAMPLE_BASIC_WINDOW_MANAGER_H_ | ||
801 | 21 | |||
802 | 22 | #include "mir/shell/window_management_info.h" | ||
803 | 23 | |||
804 | 24 | #include "mir/geometry/rectangles.h" | ||
805 | 25 | #include "mir/shell/abstract_shell.h" | ||
806 | 26 | #include "mir/shell/window_manager.h" | ||
807 | 27 | |||
808 | 28 | #include <map> | ||
809 | 29 | #include <mutex> | ||
810 | 30 | |||
811 | 31 | ///\example server_example_basic_window_manager.h | ||
812 | 32 | /// A generic policy-based window manager implementation | ||
813 | 33 | |||
814 | 34 | namespace mir | ||
815 | 35 | { | ||
816 | 36 | namespace examples | ||
817 | 37 | { | ||
818 | 38 | using shell::SurfaceSet; | ||
819 | 39 | using shell::SurfaceInfo; | ||
820 | 40 | using shell::SessionInfo; | ||
821 | 41 | |||
822 | 42 | /// The interface through which the policy instructs the controller. | ||
823 | 43 | /// These functions assume that the BasicWindowManager data structures can be accessed freely. | ||
824 | 44 | /// I.e. should only be invoked by the policy handle_... methods (where any necessary locks are held). | ||
825 | 45 | class WindowManagerTools | ||
826 | 46 | { | ||
827 | 47 | public: | ||
828 | 48 | using SurfaceInfoMap = std::map<std::weak_ptr<scene::Surface>, SurfaceInfo, std::owner_less<std::weak_ptr<scene::Surface>>>; | ||
829 | 49 | using SessionInfoMap = std::map<std::weak_ptr<scene::Session>, SessionInfo, std::owner_less<std::weak_ptr<scene::Session>>>; | ||
830 | 50 | |||
831 | 51 | virtual auto find_session(std::function<bool(SessionInfo const& info)> const& predicate) | ||
832 | 52 | -> std::shared_ptr<scene::Session> = 0; | ||
833 | 53 | |||
834 | 54 | virtual auto info_for(std::weak_ptr<scene::Session> const& session) const -> SessionInfo& = 0; | ||
835 | 55 | |||
836 | 56 | virtual auto info_for(std::weak_ptr<scene::Surface> const& surface) const -> SurfaceInfo& = 0; | ||
837 | 57 | |||
838 | 58 | virtual std::shared_ptr<scene::Session> focused_session() const = 0; | ||
839 | 59 | |||
840 | 60 | virtual std::shared_ptr<scene::Surface> focused_surface() const = 0; | ||
841 | 61 | |||
842 | 62 | virtual void focus_next_session() = 0; | ||
843 | 63 | |||
844 | 64 | virtual void set_focus_to( | ||
845 | 65 | std::shared_ptr<scene::Session> const& focus, | ||
846 | 66 | std::shared_ptr<scene::Surface> const& surface) = 0; | ||
847 | 67 | |||
848 | 68 | virtual auto surface_at(geometry::Point cursor) const -> std::shared_ptr<scene::Surface> = 0; | ||
849 | 69 | |||
850 | 70 | virtual auto active_display() -> geometry::Rectangle const = 0; | ||
851 | 71 | |||
852 | 72 | virtual void forget(std::weak_ptr<scene::Surface> const& surface) = 0; | ||
853 | 73 | |||
854 | 74 | virtual void raise_tree(std::shared_ptr<scene::Surface> const& root) = 0; | ||
855 | 75 | |||
856 | 76 | virtual ~WindowManagerTools() = default; | ||
857 | 77 | WindowManagerTools() = default; | ||
858 | 78 | WindowManagerTools(WindowManagerTools const&) = delete; | ||
859 | 79 | WindowManagerTools& operator=(WindowManagerTools const&) = delete; | ||
860 | 80 | }; | ||
861 | 81 | |||
862 | 82 | class WindowManagementPolicy | ||
863 | 83 | { | ||
864 | 84 | public: | ||
865 | 85 | using SessionInfoMap = typename WindowManagerTools::SessionInfoMap; | ||
866 | 86 | using SurfaceInfoMap = typename WindowManagerTools::SurfaceInfoMap; | ||
867 | 87 | |||
868 | 88 | virtual void handle_session_info_updated(SessionInfoMap& session_info, geometry::Rectangles const& displays) = 0; | ||
869 | 89 | |||
870 | 90 | virtual void handle_displays_updated(SessionInfoMap& session_info, geometry::Rectangles const& displays) = 0; | ||
871 | 91 | |||
872 | 92 | virtual auto handle_place_new_surface( | ||
873 | 93 | std::shared_ptr<scene::Session> const& session, | ||
874 | 94 | scene::SurfaceCreationParameters const& request_parameters) | ||
875 | 95 | -> scene::SurfaceCreationParameters = 0; | ||
876 | 96 | |||
877 | 97 | virtual void handle_new_surface(std::shared_ptr<scene::Session> const& session, std::shared_ptr<scene::Surface> const& surface) = 0; | ||
878 | 98 | |||
879 | 99 | virtual void handle_modify_surface( | ||
880 | 100 | std::shared_ptr<scene::Session> const& session, | ||
881 | 101 | std::shared_ptr<scene::Surface> const& surface, | ||
882 | 102 | shell::SurfaceSpecification const& modifications) = 0; | ||
883 | 103 | |||
884 | 104 | virtual void handle_delete_surface(std::shared_ptr<scene::Session> const& session, std::weak_ptr<scene::Surface> const& surface) = 0; | ||
885 | 105 | |||
886 | 106 | virtual int handle_set_state(std::shared_ptr<scene::Surface> const& surface, MirWindowState value) = 0; | ||
887 | 107 | |||
888 | 108 | virtual void generate_decorations_for( | ||
889 | 109 | std::shared_ptr<scene::Session> const& session, std::shared_ptr<scene::Surface> const& surface, | ||
890 | 110 | SurfaceInfoMap& surface_info, | ||
891 | 111 | std::function<frontend::SurfaceId(std::shared_ptr<scene::Session> const&, scene::SurfaceCreationParameters const&)> const& build) = 0; | ||
892 | 112 | |||
893 | 113 | virtual bool handle_keyboard_event(MirKeyboardEvent const* event) = 0; | ||
894 | 114 | |||
895 | 115 | virtual bool handle_touch_event(MirTouchEvent const* event) = 0; | ||
896 | 116 | |||
897 | 117 | virtual bool handle_pointer_event(MirPointerEvent const* event) = 0; | ||
898 | 118 | |||
899 | 119 | virtual void handle_raise_surface( | ||
900 | 120 | std::shared_ptr<scene::Session> const& session, | ||
901 | 121 | std::shared_ptr<scene::Surface> const& surface) = 0; | ||
902 | 122 | |||
903 | 123 | virtual ~WindowManagementPolicy() = default; | ||
904 | 124 | WindowManagementPolicy() = default; | ||
905 | 125 | WindowManagementPolicy(WindowManagementPolicy const&) = delete; | ||
906 | 126 | WindowManagementPolicy& operator=(WindowManagementPolicy const&) = delete; | ||
907 | 127 | }; | ||
908 | 128 | |||
909 | 129 | /// A policy based window manager. | ||
910 | 130 | /// This takes care of the management of any meta implementation held for the sessions and surfaces. | ||
911 | 131 | class BasicWindowManager : public virtual shell::WindowManager, | ||
912 | 132 | protected WindowManagerTools | ||
913 | 133 | { | ||
914 | 134 | protected: | ||
915 | 135 | BasicWindowManager( | ||
916 | 136 | shell::FocusController* focus_controller, | ||
917 | 137 | std::unique_ptr<WindowManagementPolicy> policy); | ||
918 | 138 | |||
919 | 139 | public: | ||
920 | 140 | using typename WindowManagerTools::SurfaceInfoMap; | ||
921 | 141 | using typename WindowManagerTools::SessionInfoMap; | ||
922 | 142 | |||
923 | 143 | void add_session(std::shared_ptr<scene::Session> const& session) override; | ||
924 | 144 | |||
925 | 145 | void remove_session(std::shared_ptr<scene::Session> const& session) override; | ||
926 | 146 | |||
927 | 147 | auto add_surface( | ||
928 | 148 | std::shared_ptr<scene::Session> const& session, | ||
929 | 149 | scene::SurfaceCreationParameters const& params, | ||
930 | 150 | std::function<frontend::SurfaceId(std::shared_ptr<scene::Session> const& session, scene::SurfaceCreationParameters const& params)> const& build) | ||
931 | 151 | -> frontend::SurfaceId override; | ||
932 | 152 | |||
933 | 153 | void modify_surface( | ||
934 | 154 | std::shared_ptr<scene::Session> const& session, | ||
935 | 155 | std::shared_ptr<scene::Surface> const& surface, | ||
936 | 156 | shell::SurfaceSpecification const& modifications) override; | ||
937 | 157 | |||
938 | 158 | void remove_surface( | ||
939 | 159 | std::shared_ptr<scene::Session> const& session, | ||
940 | 160 | std::weak_ptr<scene::Surface> const& surface) override; | ||
941 | 161 | |||
942 | 162 | void forget(std::weak_ptr<scene::Surface> const& surface) override; | ||
943 | 163 | |||
944 | 164 | void add_display(geometry::Rectangle const& area) override; | ||
945 | 165 | |||
946 | 166 | void remove_display(geometry::Rectangle const& area) override; | ||
947 | 167 | |||
948 | 168 | bool handle_keyboard_event(MirKeyboardEvent const* event) override; | ||
949 | 169 | |||
950 | 170 | bool handle_touch_event(MirTouchEvent const* event) override; | ||
951 | 171 | |||
952 | 172 | bool handle_pointer_event(MirPointerEvent const* event) override; | ||
953 | 173 | |||
954 | 174 | void handle_raise_surface( | ||
955 | 175 | std::shared_ptr<scene::Session> const& session, | ||
956 | 176 | std::shared_ptr<scene::Surface> const& surface, | ||
957 | 177 | uint64_t timestamp) override; | ||
958 | 178 | |||
959 | 179 | void handle_request_drag_and_drop( | ||
960 | 180 | std::shared_ptr<scene::Session> const& session, | ||
961 | 181 | std::shared_ptr<scene::Surface> const& surface, | ||
962 | 182 | uint64_t timestamp) override; | ||
963 | 183 | |||
964 | 184 | void handle_request_move( | ||
965 | 185 | std::shared_ptr<scene::Session> const& session, | ||
966 | 186 | std::shared_ptr<scene::Surface> const& surface, | ||
967 | 187 | uint64_t timestamp) override; | ||
968 | 188 | |||
969 | 189 | int set_surface_attribute( | ||
970 | 190 | std::shared_ptr<scene::Session> const& /*session*/, | ||
971 | 191 | std::shared_ptr<scene::Surface> const& surface, | ||
972 | 192 | MirWindowAttrib attrib, | ||
973 | 193 | int value) override; | ||
974 | 194 | |||
975 | 195 | auto find_session(std::function<bool(SessionInfo const& info)> const& predicate) | ||
976 | 196 | -> std::shared_ptr<scene::Session> override; | ||
977 | 197 | |||
978 | 198 | auto info_for(std::weak_ptr<scene::Session> const& session) const -> SessionInfo& override; | ||
979 | 199 | |||
980 | 200 | auto info_for(std::weak_ptr<scene::Surface> const& surface) const -> SurfaceInfo& override; | ||
981 | 201 | |||
982 | 202 | std::shared_ptr<scene::Session> focused_session() const override; | ||
983 | 203 | |||
984 | 204 | std::shared_ptr<scene::Surface> focused_surface() const override; | ||
985 | 205 | |||
986 | 206 | void focus_next_session() override; | ||
987 | 207 | |||
988 | 208 | void set_focus_to( | ||
989 | 209 | std::shared_ptr<scene::Session> const& focus, | ||
990 | 210 | std::shared_ptr<scene::Surface> const& surface) override; | ||
991 | 211 | |||
992 | 212 | auto surface_at(geometry::Point cursor) const -> std::shared_ptr<scene::Surface> override; | ||
993 | 213 | |||
994 | 214 | auto active_display() -> geometry::Rectangle const override; | ||
995 | 215 | |||
996 | 216 | void raise_tree(std::shared_ptr<scene::Surface> const& root) override; | ||
997 | 217 | |||
998 | 218 | private: | ||
999 | 219 | shell::FocusController* const focus_controller; | ||
1000 | 220 | std::unique_ptr<WindowManagementPolicy> const policy; | ||
1001 | 221 | |||
1002 | 222 | std::mutex mutex; | ||
1003 | 223 | SessionInfoMap session_info; | ||
1004 | 224 | SurfaceInfoMap surface_info; | ||
1005 | 225 | geometry::Rectangles displays; | ||
1006 | 226 | geometry::Point cursor; | ||
1007 | 227 | uint64_t last_input_event_timestamp{0}; | ||
1008 | 228 | |||
1009 | 229 | void update_event_timestamp(MirKeyboardEvent const* kev); | ||
1010 | 230 | void update_event_timestamp(MirPointerEvent const* pev); | ||
1011 | 231 | void update_event_timestamp(MirTouchEvent const* tev); | ||
1012 | 232 | }; | ||
1013 | 233 | |||
1014 | 234 | /// A policy based window manager. This exists to initialize BasicWindowManager and | ||
1015 | 235 | /// the WMPolicy (in an awkward manner). | ||
1016 | 236 | /// TODO revisit this initialization sequence. | ||
1017 | 237 | template<typename WMPolicy> | ||
1018 | 238 | class WindowManagerBuilder : public BasicWindowManager | ||
1019 | 239 | { | ||
1020 | 240 | public: | ||
1021 | 241 | |||
1022 | 242 | template <typename... PolicyArgs> | ||
1023 | 243 | WindowManagerBuilder( | ||
1024 | 244 | shell::FocusController* focus_controller, | ||
1025 | 245 | PolicyArgs&&... policy_args) : | ||
1026 | 246 | BasicWindowManager( | ||
1027 | 247 | focus_controller, | ||
1028 | 248 | build_policy(std::forward<PolicyArgs>(policy_args)...)) | ||
1029 | 249 | { | ||
1030 | 250 | } | ||
1031 | 251 | |||
1032 | 252 | private: | ||
1033 | 253 | template <typename... PolicyArgs> | ||
1034 | 254 | auto build_policy(PolicyArgs&&... policy_args) | ||
1035 | 255 | -> std::unique_ptr<WMPolicy> | ||
1036 | 256 | { | ||
1037 | 257 | return std::unique_ptr<WMPolicy>( | ||
1038 | 258 | new WMPolicy(this, std::forward<PolicyArgs>(policy_args)...)); | ||
1039 | 259 | } | ||
1040 | 260 | }; | ||
1041 | 261 | } | ||
1042 | 262 | } | ||
1043 | 263 | |||
1044 | 264 | #endif /* MIR_EXAMPLE_BASIC_WINDOW_MANAGER_H_ */ | ||
1045 | 265 | 0 | ||
1046 | === removed file 'examples/server_example_canonical_window_manager.cpp' | |||
1047 | --- examples/server_example_canonical_window_manager.cpp 2017-07-28 17:00:43 +0000 | |||
1048 | +++ examples/server_example_canonical_window_manager.cpp 1970-01-01 00:00:00 +0000 | |||
1049 | @@ -1,1007 +0,0 @@ | |||
1050 | 1 | /* | ||
1051 | 2 | * Copyright © 2015 Canonical Ltd. | ||
1052 | 3 | * | ||
1053 | 4 | * This program is free software: you can redistribute it and/or modify it | ||
1054 | 5 | * under the terms of the GNU General Public License version 2 or 3, | ||
1055 | 6 | * as published by the Free Software Foundation. | ||
1056 | 7 | * | ||
1057 | 8 | * This program is distributed in the hope that it will be useful, | ||
1058 | 9 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
1059 | 10 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
1060 | 11 | * GNU General Public License for more details. | ||
1061 | 12 | * | ||
1062 | 13 | * You should have received a copy of the GNU General Public License | ||
1063 | 14 | * along with this program. If not, see <http://www.gnu.org/licenses/>. | ||
1064 | 15 | * | ||
1065 | 16 | * Authored By: Alan Griffiths <alan@octopull.co.uk> | ||
1066 | 17 | */ | ||
1067 | 18 | |||
1068 | 19 | #include "server_example_canonical_window_manager.h" | ||
1069 | 20 | |||
1070 | 21 | #include "mir/scene/session.h" | ||
1071 | 22 | #include "mir/scene/surface.h" | ||
1072 | 23 | #include "mir/scene/surface_creation_parameters.h" | ||
1073 | 24 | #include "mir/shell/surface_ready_observer.h" | ||
1074 | 25 | #include "mir/shell/display_layout.h" | ||
1075 | 26 | |||
1076 | 27 | #include <linux/input.h> | ||
1077 | 28 | #include <csignal> | ||
1078 | 29 | #include <algorithm> | ||
1079 | 30 | |||
1080 | 31 | namespace me = mir::examples; | ||
1081 | 32 | namespace ms = mir::scene; | ||
1082 | 33 | using namespace mir::geometry; | ||
1083 | 34 | |||
1084 | 35 | ///\example server_example_canonical_window_manager.cpp | ||
1085 | 36 | // Based on "Mir and Unity: Surfaces, input, and displays (v0.3)" | ||
1086 | 37 | |||
1087 | 38 | namespace | ||
1088 | 39 | { | ||
1089 | 40 | int const title_bar_height = 10; | ||
1090 | 41 | Size titlebar_size_for_window(Size window_size) | ||
1091 | 42 | { | ||
1092 | 43 | return {window_size.width, Height{title_bar_height}}; | ||
1093 | 44 | } | ||
1094 | 45 | |||
1095 | 46 | Point titlebar_position_for_window(Point window_position) | ||
1096 | 47 | { | ||
1097 | 48 | return { | ||
1098 | 49 | window_position.x, | ||
1099 | 50 | window_position.y - DeltaY(title_bar_height) | ||
1100 | 51 | }; | ||
1101 | 52 | } | ||
1102 | 53 | } | ||
1103 | 54 | |||
1104 | 55 | me::CanonicalWindowManagerPolicyCopy::CanonicalWindowManagerPolicyCopy( | ||
1105 | 56 | WindowManagerTools* const tools, | ||
1106 | 57 | std::shared_ptr<shell::DisplayLayout> const& display_layout, | ||
1107 | 58 | std::shared_ptr<graphics::GraphicBufferAllocator> const& allocator) : | ||
1108 | 59 | tools{tools}, | ||
1109 | 60 | display_layout{display_layout}, | ||
1110 | 61 | allocator{allocator} | ||
1111 | 62 | { | ||
1112 | 63 | } | ||
1113 | 64 | |||
1114 | 65 | void me::CanonicalWindowManagerPolicyCopy::click(Point cursor) | ||
1115 | 66 | { | ||
1116 | 67 | if (auto const surface = tools->surface_at(cursor)) | ||
1117 | 68 | select_active_surface(surface); | ||
1118 | 69 | } | ||
1119 | 70 | |||
1120 | 71 | void me::CanonicalWindowManagerPolicyCopy::handle_session_info_updated(SessionInfoMap& /*session_info*/, Rectangles const& /*displays*/) | ||
1121 | 72 | { | ||
1122 | 73 | } | ||
1123 | 74 | |||
1124 | 75 | void me::CanonicalWindowManagerPolicyCopy::handle_displays_updated(SessionInfoMap& /*session_info*/, Rectangles const& displays) | ||
1125 | 76 | { | ||
1126 | 77 | display_area = displays.bounding_rectangle(); | ||
1127 | 78 | |||
1128 | 79 | for (auto const weak_surface : fullscreen_surfaces) | ||
1129 | 80 | { | ||
1130 | 81 | if (auto const surface = weak_surface.lock()) | ||
1131 | 82 | { | ||
1132 | 83 | auto const& info = tools->info_for(weak_surface); | ||
1133 | 84 | Rectangle rect{surface->top_left(), surface->size()}; | ||
1134 | 85 | |||
1135 | 86 | display_layout->place_in_output(info.output_id.value(), rect); | ||
1136 | 87 | surface->move_to(rect.top_left); | ||
1137 | 88 | surface->resize(rect.size); | ||
1138 | 89 | } | ||
1139 | 90 | } | ||
1140 | 91 | } | ||
1141 | 92 | |||
1142 | 93 | void me::CanonicalWindowManagerPolicyCopy::resize(Point cursor) | ||
1143 | 94 | { | ||
1144 | 95 | if (!resizing) select_active_surface(tools->surface_at(old_cursor)); | ||
1145 | 96 | resize(active_surface(), cursor, old_cursor, display_area); | ||
1146 | 97 | } | ||
1147 | 98 | |||
1148 | 99 | auto me::CanonicalWindowManagerPolicyCopy::handle_place_new_surface( | ||
1149 | 100 | std::shared_ptr<ms::Session> const& session, | ||
1150 | 101 | ms::SurfaceCreationParameters const& request_parameters) | ||
1151 | 102 | -> ms::SurfaceCreationParameters | ||
1152 | 103 | { | ||
1153 | 104 | auto parameters = request_parameters; | ||
1154 | 105 | auto surf_type = parameters.type.is_set() ? parameters.type.value() : mir_window_type_normal; | ||
1155 | 106 | bool const needs_titlebar = SurfaceInfo::needs_titlebar(surf_type); | ||
1156 | 107 | |||
1157 | 108 | if (needs_titlebar) | ||
1158 | 109 | parameters.size.height = parameters.size.height + DeltaY{title_bar_height}; | ||
1159 | 110 | |||
1160 | 111 | if (!parameters.state.is_set()) | ||
1161 | 112 | parameters.state = mir_window_state_restored; | ||
1162 | 113 | |||
1163 | 114 | auto const active_display = tools->active_display(); | ||
1164 | 115 | |||
1165 | 116 | auto const width = parameters.size.width.as_int(); | ||
1166 | 117 | auto const height = parameters.size.height.as_int(); | ||
1167 | 118 | |||
1168 | 119 | bool positioned = false; | ||
1169 | 120 | |||
1170 | 121 | auto const parent = parameters.parent.lock(); | ||
1171 | 122 | |||
1172 | 123 | if (parameters.output_id != mir::graphics::DisplayConfigurationOutputId{0}) | ||
1173 | 124 | { | ||
1174 | 125 | Rectangle rect{parameters.top_left, parameters.size}; | ||
1175 | 126 | display_layout->place_in_output(parameters.output_id, rect); | ||
1176 | 127 | parameters.top_left = rect.top_left; | ||
1177 | 128 | parameters.size = rect.size; | ||
1178 | 129 | parameters.state = mir_window_state_fullscreen; | ||
1179 | 130 | positioned = true; | ||
1180 | 131 | } | ||
1181 | 132 | else if (!parent) // No parent => client can't suggest positioning | ||
1182 | 133 | { | ||
1183 | 134 | if (auto const default_surface = session->default_surface()) | ||
1184 | 135 | { | ||
1185 | 136 | static Displacement const offset{title_bar_height, title_bar_height}; | ||
1186 | 137 | |||
1187 | 138 | parameters.top_left = default_surface->top_left() + offset; | ||
1188 | 139 | |||
1189 | 140 | geometry::Rectangle display_for_app{default_surface->top_left(), default_surface->size()}; | ||
1190 | 141 | |||
1191 | 142 | display_layout->size_to_output(display_for_app); | ||
1192 | 143 | |||
1193 | 144 | positioned = display_for_app.overlaps(Rectangle{parameters.top_left, parameters.size}); | ||
1194 | 145 | } | ||
1195 | 146 | } | ||
1196 | 147 | |||
1197 | 148 | if (parent && parameters.aux_rect.is_set() && parameters.edge_attachment.is_set()) | ||
1198 | 149 | { | ||
1199 | 150 | auto const edge_attachment = parameters.edge_attachment.value(); | ||
1200 | 151 | auto const aux_rect = parameters.aux_rect.value(); | ||
1201 | 152 | auto const parent_top_left = parent->top_left(); | ||
1202 | 153 | auto const top_left = aux_rect.top_left -Point{} + parent_top_left; | ||
1203 | 154 | auto const top_right= aux_rect.top_right() -Point{} + parent_top_left; | ||
1204 | 155 | auto const bot_left = aux_rect.bottom_left()-Point{} + parent_top_left; | ||
1205 | 156 | |||
1206 | 157 | if (edge_attachment & mir_edge_attachment_vertical) | ||
1207 | 158 | { | ||
1208 | 159 | if (active_display.contains(top_right + Displacement{width, height})) | ||
1209 | 160 | { | ||
1210 | 161 | parameters.top_left = top_right; | ||
1211 | 162 | positioned = true; | ||
1212 | 163 | } | ||
1213 | 164 | else if (active_display.contains(top_left + Displacement{-width, height})) | ||
1214 | 165 | { | ||
1215 | 166 | parameters.top_left = top_left + Displacement{-width, 0}; | ||
1216 | 167 | positioned = true; | ||
1217 | 168 | } | ||
1218 | 169 | } | ||
1219 | 170 | |||
1220 | 171 | if (edge_attachment & mir_edge_attachment_horizontal) | ||
1221 | 172 | { | ||
1222 | 173 | if (active_display.contains(bot_left + Displacement{width, height})) | ||
1223 | 174 | { | ||
1224 | 175 | parameters.top_left = bot_left; | ||
1225 | 176 | positioned = true; | ||
1226 | 177 | } | ||
1227 | 178 | else if (active_display.contains(top_left + Displacement{width, -height})) | ||
1228 | 179 | { | ||
1229 | 180 | parameters.top_left = top_left + Displacement{0, -height}; | ||
1230 | 181 | positioned = true; | ||
1231 | 182 | } | ||
1232 | 183 | } | ||
1233 | 184 | } | ||
1234 | 185 | else if (parent) | ||
1235 | 186 | { | ||
1236 | 187 | // o Otherwise, if the dialog is not the same as any previous dialog for the | ||
1237 | 188 | // same parent window, and/or it does not have user-customized position: | ||
1238 | 189 | // o It should be optically centered relative to its parent, unless this | ||
1239 | 190 | // would overlap or cover the title bar of the parent. | ||
1240 | 191 | // o Otherwise, it should be cascaded vertically (but not horizontally) | ||
1241 | 192 | // relative to its parent, unless, this would cause at least part of | ||
1242 | 193 | // it to extend into shell space. | ||
1243 | 194 | auto const parent_top_left = parent->top_left(); | ||
1244 | 195 | auto const centred = parent_top_left | ||
1245 | 196 | + 0.5*(as_displacement(parent->size()) - as_displacement(parameters.size)) | ||
1246 | 197 | - DeltaY{(parent->size().height.as_int()-height)/6}; | ||
1247 | 198 | |||
1248 | 199 | parameters.top_left = centred; | ||
1249 | 200 | positioned = true; | ||
1250 | 201 | } | ||
1251 | 202 | |||
1252 | 203 | if (!positioned) | ||
1253 | 204 | { | ||
1254 | 205 | auto const centred = active_display.top_left | ||
1255 | 206 | + 0.5*(as_displacement(active_display.size) - as_displacement(parameters.size)) | ||
1256 | 207 | - DeltaY{(active_display.size.height.as_int()-height)/6}; | ||
1257 | 208 | |||
1258 | 209 | switch (parameters.state.value()) | ||
1259 | 210 | { | ||
1260 | 211 | case mir_window_state_fullscreen: | ||
1261 | 212 | case mir_window_state_maximized: | ||
1262 | 213 | parameters.top_left = active_display.top_left; | ||
1263 | 214 | parameters.size = active_display.size; | ||
1264 | 215 | break; | ||
1265 | 216 | |||
1266 | 217 | case mir_window_state_vertmaximized: | ||
1267 | 218 | parameters.top_left = centred; | ||
1268 | 219 | parameters.top_left.y = active_display.top_left.y; | ||
1269 | 220 | parameters.size.height = active_display.size.height; | ||
1270 | 221 | break; | ||
1271 | 222 | |||
1272 | 223 | case mir_window_state_horizmaximized: | ||
1273 | 224 | parameters.top_left = centred; | ||
1274 | 225 | parameters.top_left.x = active_display.top_left.x; | ||
1275 | 226 | parameters.size.width = active_display.size.width; | ||
1276 | 227 | break; | ||
1277 | 228 | |||
1278 | 229 | default: | ||
1279 | 230 | parameters.top_left = centred; | ||
1280 | 231 | } | ||
1281 | 232 | |||
1282 | 233 | if (parameters.top_left.y < display_area.top_left.y) | ||
1283 | 234 | parameters.top_left.y = display_area.top_left.y; | ||
1284 | 235 | } | ||
1285 | 236 | |||
1286 | 237 | if (parameters.state != mir_window_state_fullscreen && needs_titlebar) | ||
1287 | 238 | { | ||
1288 | 239 | parameters.top_left.y = parameters.top_left.y + DeltaY{title_bar_height}; | ||
1289 | 240 | parameters.size.height = parameters.size.height - DeltaY{title_bar_height}; | ||
1290 | 241 | } | ||
1291 | 242 | |||
1292 | 243 | return parameters; | ||
1293 | 244 | } | ||
1294 | 245 | |||
1295 | 246 | void me::CanonicalWindowManagerPolicyCopy::generate_decorations_for( | ||
1296 | 247 | std::shared_ptr<scene::Session> const& session, | ||
1297 | 248 | std::shared_ptr<scene::Surface> const& surface, | ||
1298 | 249 | SurfaceInfoMap& surface_map, | ||
1299 | 250 | std::function<frontend::SurfaceId(std::shared_ptr<scene::Session> const& session, scene::SurfaceCreationParameters const& params)> const& build) | ||
1300 | 251 | { | ||
1301 | 252 | if (!SurfaceInfo::needs_titlebar(surface->type())) | ||
1302 | 253 | return; | ||
1303 | 254 | |||
1304 | 255 | auto format = mir_pixel_format_xrgb_8888; | ||
1305 | 256 | mir::graphics::BufferProperties properties(titlebar_size_for_window(surface->size()), | ||
1306 | 257 | format, mir::graphics::BufferUsage::software); | ||
1307 | 258 | auto stream_id = session->create_buffer_stream(properties); | ||
1308 | 259 | auto params = ms::a_surface() | ||
1309 | 260 | .of_size(titlebar_size_for_window(surface->size())) | ||
1310 | 261 | .of_name("decoration") | ||
1311 | 262 | .of_pixel_format(format) | ||
1312 | 263 | .of_buffer_usage(mir::graphics::BufferUsage::software) | ||
1313 | 264 | .of_position(titlebar_position_for_window(surface->top_left())) | ||
1314 | 265 | .of_type(mir_window_type_gloss) | ||
1315 | 266 | .with_buffer_stream(stream_id); | ||
1316 | 267 | auto id = build(session, params); | ||
1317 | 268 | auto titlebar = session->surface(id); | ||
1318 | 269 | titlebar->set_alpha(0.9); | ||
1319 | 270 | |||
1320 | 271 | auto& surface_info = tools->info_for(surface); | ||
1321 | 272 | surface_info.titlebar = titlebar; | ||
1322 | 273 | surface_info.titlebar_id = id; | ||
1323 | 274 | surface_info.titlebar_stream_id = stream_id; | ||
1324 | 275 | surface_info.children.push_back(titlebar); | ||
1325 | 276 | |||
1326 | 277 | SurfaceInfo& titlebar_info = | ||
1327 | 278 | surface_map.emplace(titlebar, SurfaceInfo{session, titlebar, {}}).first->second; | ||
1328 | 279 | titlebar_info.is_titlebar = true; | ||
1329 | 280 | titlebar_info.parent = surface; | ||
1330 | 281 | titlebar_info.init_titlebar(*allocator, titlebar); | ||
1331 | 282 | } | ||
1332 | 283 | |||
1333 | 284 | void me::CanonicalWindowManagerPolicyCopy::handle_new_surface(std::shared_ptr<ms::Session> const& session, std::shared_ptr<ms::Surface> const& surface) | ||
1334 | 285 | { | ||
1335 | 286 | auto& surface_info = tools->info_for(surface); | ||
1336 | 287 | if (auto const parent = surface_info.parent.lock()) | ||
1337 | 288 | { | ||
1338 | 289 | tools->info_for(parent).children.push_back(surface); | ||
1339 | 290 | } | ||
1340 | 291 | |||
1341 | 292 | tools->info_for(session).surfaces.push_back(surface); | ||
1342 | 293 | |||
1343 | 294 | if (surface_info.can_be_active()) | ||
1344 | 295 | { | ||
1345 | 296 | surface->add_observer(std::make_shared<shell::SurfaceReadyObserver>( | ||
1346 | 297 | [this](std::shared_ptr<scene::Session> const& /*session*/, | ||
1347 | 298 | std::shared_ptr<scene::Surface> const& surface) | ||
1348 | 299 | { | ||
1349 | 300 | select_active_surface(surface); | ||
1350 | 301 | }, | ||
1351 | 302 | session, | ||
1352 | 303 | surface)); | ||
1353 | 304 | } | ||
1354 | 305 | |||
1355 | 306 | if (surface_info.state == mir_window_state_fullscreen) | ||
1356 | 307 | fullscreen_surfaces.insert(surface); | ||
1357 | 308 | } | ||
1358 | 309 | |||
1359 | 310 | void me::CanonicalWindowManagerPolicyCopy::handle_modify_surface( | ||
1360 | 311 | std::shared_ptr<scene::Session> const& session, | ||
1361 | 312 | std::shared_ptr<scene::Surface> const& surface, | ||
1362 | 313 | shell::SurfaceSpecification const& modifications) | ||
1363 | 314 | { | ||
1364 | 315 | auto& surface_info_old = tools->info_for(surface); | ||
1365 | 316 | |||
1366 | 317 | auto surface_info = surface_info_old; | ||
1367 | 318 | |||
1368 | 319 | if (modifications.parent.is_set()) | ||
1369 | 320 | surface_info.parent = modifications.parent.value(); | ||
1370 | 321 | |||
1371 | 322 | if (modifications.type.is_set() && | ||
1372 | 323 | surface_info.type != modifications.type.value()) | ||
1373 | 324 | { | ||
1374 | 325 | auto const new_type = modifications.type.value(); | ||
1375 | 326 | |||
1376 | 327 | if (!surface_info.can_morph_to(new_type)) | ||
1377 | 328 | { | ||
1378 | 329 | throw std::runtime_error("Unsupported surface type change"); | ||
1379 | 330 | } | ||
1380 | 331 | |||
1381 | 332 | surface_info.type = new_type; | ||
1382 | 333 | |||
1383 | 334 | if (surface_info.must_not_have_parent()) | ||
1384 | 335 | { | ||
1385 | 336 | if (modifications.parent.is_set()) | ||
1386 | 337 | throw std::runtime_error("Target surface type does not support parent"); | ||
1387 | 338 | |||
1388 | 339 | surface_info.parent.reset(); | ||
1389 | 340 | } | ||
1390 | 341 | else if (surface_info.must_have_parent()) | ||
1391 | 342 | { | ||
1392 | 343 | if (!surface_info.parent.lock()) | ||
1393 | 344 | throw std::runtime_error("Target surface type requires parent"); | ||
1394 | 345 | } | ||
1395 | 346 | |||
1396 | 347 | surface->configure(mir_window_attrib_type, new_type); | ||
1397 | 348 | } | ||
1398 | 349 | |||
1399 | 350 | #define COPY_IF_SET(field)\ | ||
1400 | 351 | if (modifications.field.is_set())\ | ||
1401 | 352 | surface_info.field = modifications.field.value() | ||
1402 | 353 | |||
1403 | 354 | COPY_IF_SET(min_width); | ||
1404 | 355 | COPY_IF_SET(min_height); | ||
1405 | 356 | COPY_IF_SET(max_width); | ||
1406 | 357 | COPY_IF_SET(max_height); | ||
1407 | 358 | COPY_IF_SET(min_width); | ||
1408 | 359 | COPY_IF_SET(width_inc); | ||
1409 | 360 | COPY_IF_SET(height_inc); | ||
1410 | 361 | COPY_IF_SET(min_aspect); | ||
1411 | 362 | COPY_IF_SET(max_aspect); | ||
1412 | 363 | COPY_IF_SET(output_id); | ||
1413 | 364 | |||
1414 | 365 | #undef COPY_IF_SET | ||
1415 | 366 | |||
1416 | 367 | std::swap(surface_info, surface_info_old); | ||
1417 | 368 | |||
1418 | 369 | if (modifications.name.is_set()) | ||
1419 | 370 | surface->rename(modifications.name.value()); | ||
1420 | 371 | |||
1421 | 372 | if (modifications.streams.is_set()) | ||
1422 | 373 | { | ||
1423 | 374 | auto v = modifications.streams.value(); | ||
1424 | 375 | std::vector<shell::StreamSpecification> l (v.begin(), v.end()); | ||
1425 | 376 | session->configure_streams(*surface, l); | ||
1426 | 377 | } | ||
1427 | 378 | |||
1428 | 379 | if (modifications.width.is_set() || modifications.height.is_set()) | ||
1429 | 380 | { | ||
1430 | 381 | auto new_size = surface->size(); | ||
1431 | 382 | |||
1432 | 383 | if (modifications.width.is_set()) | ||
1433 | 384 | new_size.width = modifications.width.value(); | ||
1434 | 385 | |||
1435 | 386 | if (modifications.height.is_set()) | ||
1436 | 387 | new_size.height = modifications.height.value(); | ||
1437 | 388 | |||
1438 | 389 | auto top_left = surface->top_left(); | ||
1439 | 390 | |||
1440 | 391 | surface_info.constrain_resize( | ||
1441 | 392 | surface, | ||
1442 | 393 | top_left, | ||
1443 | 394 | new_size, | ||
1444 | 395 | false, | ||
1445 | 396 | false, | ||
1446 | 397 | display_area); | ||
1447 | 398 | |||
1448 | 399 | apply_resize(surface, surface_info.titlebar, top_left, new_size); | ||
1449 | 400 | } | ||
1450 | 401 | |||
1451 | 402 | if (modifications.input_shape.is_set()) | ||
1452 | 403 | { | ||
1453 | 404 | auto rectangles = modifications.input_shape.value(); | ||
1454 | 405 | auto displacement = surface->top_left() - Point{0, 0}; | ||
1455 | 406 | for(auto& rect : rectangles) | ||
1456 | 407 | { | ||
1457 | 408 | rect.top_left = rect.top_left + displacement; | ||
1458 | 409 | rect = rect.intersection_with({surface->top_left(), surface->size()}); | ||
1459 | 410 | rect.top_left = rect.top_left - displacement; | ||
1460 | 411 | } | ||
1461 | 412 | surface->set_input_region(rectangles); | ||
1462 | 413 | } | ||
1463 | 414 | |||
1464 | 415 | |||
1465 | 416 | if (modifications.state.is_set()) | ||
1466 | 417 | { | ||
1467 | 418 | auto const state = handle_set_state(surface, modifications.state.value()); | ||
1468 | 419 | surface->configure(mir_window_attrib_state, state); | ||
1469 | 420 | } | ||
1470 | 421 | |||
1471 | 422 | if (modifications.confine_pointer.is_set()) | ||
1472 | 423 | { | ||
1473 | 424 | surface->set_confine_pointer_state(modifications.confine_pointer.value()); | ||
1474 | 425 | } | ||
1475 | 426 | } | ||
1476 | 427 | |||
1477 | 428 | void me::CanonicalWindowManagerPolicyCopy::handle_delete_surface(std::shared_ptr<ms::Session> const& session, std::weak_ptr<ms::Surface> const& surface) | ||
1478 | 429 | { | ||
1479 | 430 | fullscreen_surfaces.erase(surface); | ||
1480 | 431 | bool const is_active_surface{surface.lock() == active_surface()}; | ||
1481 | 432 | |||
1482 | 433 | auto& info = tools->info_for(surface); | ||
1483 | 434 | |||
1484 | 435 | if (auto const parent = info.parent.lock()) | ||
1485 | 436 | { | ||
1486 | 437 | auto& siblings = tools->info_for(parent).children; | ||
1487 | 438 | |||
1488 | 439 | for (auto i = begin(siblings); i != end(siblings); ++i) | ||
1489 | 440 | { | ||
1490 | 441 | if (surface.lock() == i->lock()) | ||
1491 | 442 | { | ||
1492 | 443 | siblings.erase(i); | ||
1493 | 444 | break; | ||
1494 | 445 | } | ||
1495 | 446 | } | ||
1496 | 447 | } | ||
1497 | 448 | |||
1498 | 449 | session->destroy_surface(surface); | ||
1499 | 450 | if (info.titlebar) | ||
1500 | 451 | { | ||
1501 | 452 | session->destroy_surface(info.titlebar_id); | ||
1502 | 453 | session->destroy_buffer_stream(info.titlebar_stream_id); | ||
1503 | 454 | tools->forget(info.titlebar); | ||
1504 | 455 | } | ||
1505 | 456 | |||
1506 | 457 | auto& surfaces = tools->info_for(session).surfaces; | ||
1507 | 458 | |||
1508 | 459 | for (auto i = begin(surfaces); i != end(surfaces); ++i) | ||
1509 | 460 | { | ||
1510 | 461 | if (surface.lock() == i->lock()) | ||
1511 | 462 | { | ||
1512 | 463 | surfaces.erase(i); | ||
1513 | 464 | break; | ||
1514 | 465 | } | ||
1515 | 466 | } | ||
1516 | 467 | |||
1517 | 468 | if (is_active_surface) | ||
1518 | 469 | { | ||
1519 | 470 | active_surface_.reset(); | ||
1520 | 471 | |||
1521 | 472 | if (surfaces.empty()) | ||
1522 | 473 | { | ||
1523 | 474 | tools->focus_next_session(); | ||
1524 | 475 | select_active_surface(tools->focused_surface()); | ||
1525 | 476 | } | ||
1526 | 477 | else | ||
1527 | 478 | { | ||
1528 | 479 | select_active_surface(surfaces[0].lock()); | ||
1529 | 480 | } | ||
1530 | 481 | } | ||
1531 | 482 | } | ||
1532 | 483 | |||
1533 | 484 | int me::CanonicalWindowManagerPolicyCopy::handle_set_state(std::shared_ptr<ms::Surface> const& surface, MirWindowState value) | ||
1534 | 485 | { | ||
1535 | 486 | auto& info = tools->info_for(surface); | ||
1536 | 487 | |||
1537 | 488 | switch (value) | ||
1538 | 489 | { | ||
1539 | 490 | case mir_window_state_restored: | ||
1540 | 491 | case mir_window_state_maximized: | ||
1541 | 492 | case mir_window_state_vertmaximized: | ||
1542 | 493 | case mir_window_state_horizmaximized: | ||
1543 | 494 | case mir_window_state_fullscreen: | ||
1544 | 495 | case mir_window_state_hidden: | ||
1545 | 496 | case mir_window_state_minimized: | ||
1546 | 497 | break; | ||
1547 | 498 | |||
1548 | 499 | default: | ||
1549 | 500 | return info.state; | ||
1550 | 501 | } | ||
1551 | 502 | |||
1552 | 503 | if (info.state == mir_window_state_restored) | ||
1553 | 504 | { | ||
1554 | 505 | info.restore_rect = {surface->top_left(), surface->size()}; | ||
1555 | 506 | } | ||
1556 | 507 | |||
1557 | 508 | if (info.state != mir_window_state_fullscreen) | ||
1558 | 509 | { | ||
1559 | 510 | info.output_id = decltype(info.output_id){}; | ||
1560 | 511 | fullscreen_surfaces.erase(surface); | ||
1561 | 512 | } | ||
1562 | 513 | else | ||
1563 | 514 | { | ||
1564 | 515 | fullscreen_surfaces.insert(surface); | ||
1565 | 516 | } | ||
1566 | 517 | |||
1567 | 518 | if (info.state == value) | ||
1568 | 519 | { | ||
1569 | 520 | return info.state; | ||
1570 | 521 | } | ||
1571 | 522 | |||
1572 | 523 | auto const old_pos = surface->top_left(); | ||
1573 | 524 | Displacement movement; | ||
1574 | 525 | |||
1575 | 526 | switch (value) | ||
1576 | 527 | { | ||
1577 | 528 | case mir_window_state_restored: | ||
1578 | 529 | movement = info.restore_rect.top_left - old_pos; | ||
1579 | 530 | surface->resize(info.restore_rect.size); | ||
1580 | 531 | if (info.titlebar) | ||
1581 | 532 | { | ||
1582 | 533 | info.titlebar->resize(titlebar_size_for_window(info.restore_rect.size)); | ||
1583 | 534 | info.titlebar->show(); | ||
1584 | 535 | } | ||
1585 | 536 | break; | ||
1586 | 537 | |||
1587 | 538 | case mir_window_state_maximized: | ||
1588 | 539 | movement = display_area.top_left - old_pos; | ||
1589 | 540 | surface->resize(display_area.size); | ||
1590 | 541 | if (info.titlebar) | ||
1591 | 542 | info.titlebar->hide(); | ||
1592 | 543 | break; | ||
1593 | 544 | |||
1594 | 545 | case mir_window_state_horizmaximized: | ||
1595 | 546 | movement = Point{display_area.top_left.x, info.restore_rect.top_left.y} - old_pos; | ||
1596 | 547 | surface->resize({display_area.size.width, info.restore_rect.size.height}); | ||
1597 | 548 | if (info.titlebar) | ||
1598 | 549 | { | ||
1599 | 550 | info.titlebar->resize(titlebar_size_for_window({display_area.size.width, info.restore_rect.size.height})); | ||
1600 | 551 | info.titlebar->show(); | ||
1601 | 552 | } | ||
1602 | 553 | break; | ||
1603 | 554 | |||
1604 | 555 | case mir_window_state_vertmaximized: | ||
1605 | 556 | movement = Point{info.restore_rect.top_left.x, display_area.top_left.y} - old_pos; | ||
1606 | 557 | surface->resize({info.restore_rect.size.width, display_area.size.height}); | ||
1607 | 558 | if (info.titlebar) | ||
1608 | 559 | info.titlebar->hide(); | ||
1609 | 560 | break; | ||
1610 | 561 | |||
1611 | 562 | case mir_window_state_fullscreen: | ||
1612 | 563 | { | ||
1613 | 564 | Rectangle rect{old_pos, surface->size()}; | ||
1614 | 565 | |||
1615 | 566 | if (info.output_id.is_set()) | ||
1616 | 567 | { | ||
1617 | 568 | display_layout->place_in_output(info.output_id.value(), rect); | ||
1618 | 569 | } | ||
1619 | 570 | else | ||
1620 | 571 | { | ||
1621 | 572 | display_layout->size_to_output(rect); | ||
1622 | 573 | } | ||
1623 | 574 | |||
1624 | 575 | movement = rect.top_left - old_pos; | ||
1625 | 576 | surface->resize(rect.size); | ||
1626 | 577 | break; | ||
1627 | 578 | } | ||
1628 | 579 | |||
1629 | 580 | case mir_window_state_hidden: | ||
1630 | 581 | case mir_window_state_minimized: | ||
1631 | 582 | if (info.titlebar) | ||
1632 | 583 | info.titlebar->hide(); | ||
1633 | 584 | surface->hide(); | ||
1634 | 585 | return info.state = value; | ||
1635 | 586 | |||
1636 | 587 | default: | ||
1637 | 588 | break; | ||
1638 | 589 | } | ||
1639 | 590 | |||
1640 | 591 | // TODO It is rather simplistic to move a tree WRT the top_left of the root | ||
1641 | 592 | // TODO when resizing. But for more sophistication we would need to encode | ||
1642 | 593 | // TODO some sensible layout rules. | ||
1643 | 594 | move_tree(surface, movement); | ||
1644 | 595 | |||
1645 | 596 | info.state = value; | ||
1646 | 597 | |||
1647 | 598 | if (info.is_visible()) | ||
1648 | 599 | surface->show(); | ||
1649 | 600 | |||
1650 | 601 | return info.state; | ||
1651 | 602 | } | ||
1652 | 603 | |||
1653 | 604 | void me::CanonicalWindowManagerPolicyCopy::drag(Point cursor) | ||
1654 | 605 | { | ||
1655 | 606 | select_active_surface(tools->surface_at(old_cursor)); | ||
1656 | 607 | drag(active_surface(), cursor, old_cursor, display_area); | ||
1657 | 608 | } | ||
1658 | 609 | |||
1659 | 610 | void me::CanonicalWindowManagerPolicyCopy::handle_raise_surface( | ||
1660 | 611 | std::shared_ptr<ms::Session> const& /*session*/, | ||
1661 | 612 | std::shared_ptr<ms::Surface> const& surface) | ||
1662 | 613 | { | ||
1663 | 614 | select_active_surface(surface); | ||
1664 | 615 | } | ||
1665 | 616 | |||
1666 | 617 | bool me::CanonicalWindowManagerPolicyCopy::handle_keyboard_event(MirKeyboardEvent const* event) | ||
1667 | 618 | { | ||
1668 | 619 | auto const action = mir_keyboard_event_action(event); | ||
1669 | 620 | auto const scan_code = mir_keyboard_event_scan_code(event); | ||
1670 | 621 | auto const modifiers = mir_keyboard_event_modifiers(event) & modifier_mask; | ||
1671 | 622 | |||
1672 | 623 | if (action == mir_keyboard_action_down && scan_code == KEY_F11) | ||
1673 | 624 | { | ||
1674 | 625 | switch (modifiers) | ||
1675 | 626 | { | ||
1676 | 627 | case mir_input_event_modifier_alt: | ||
1677 | 628 | toggle(mir_window_state_maximized); | ||
1678 | 629 | return true; | ||
1679 | 630 | |||
1680 | 631 | case mir_input_event_modifier_shift: | ||
1681 | 632 | toggle(mir_window_state_vertmaximized); | ||
1682 | 633 | return true; | ||
1683 | 634 | |||
1684 | 635 | case mir_input_event_modifier_ctrl: | ||
1685 | 636 | toggle(mir_window_state_horizmaximized); | ||
1686 | 637 | return true; | ||
1687 | 638 | |||
1688 | 639 | default: | ||
1689 | 640 | break; | ||
1690 | 641 | } | ||
1691 | 642 | } | ||
1692 | 643 | else if (action == mir_keyboard_action_down && scan_code == KEY_F4) | ||
1693 | 644 | { | ||
1694 | 645 | if (auto const session = tools->focused_session()) | ||
1695 | 646 | { | ||
1696 | 647 | switch (modifiers) | ||
1697 | 648 | { | ||
1698 | 649 | case mir_input_event_modifier_alt: | ||
1699 | 650 | kill(session->process_id(), SIGTERM); | ||
1700 | 651 | return true; | ||
1701 | 652 | |||
1702 | 653 | case mir_input_event_modifier_ctrl: | ||
1703 | 654 | if (auto const surf = session->default_surface()) | ||
1704 | 655 | { | ||
1705 | 656 | surf->request_client_surface_close(); | ||
1706 | 657 | return true; | ||
1707 | 658 | } | ||
1708 | 659 | |||
1709 | 660 | default: | ||
1710 | 661 | break; | ||
1711 | 662 | } | ||
1712 | 663 | } | ||
1713 | 664 | } | ||
1714 | 665 | else if (action == mir_keyboard_action_down && | ||
1715 | 666 | modifiers == mir_input_event_modifier_alt && | ||
1716 | 667 | scan_code == KEY_TAB) | ||
1717 | 668 | { | ||
1718 | 669 | tools->focus_next_session(); | ||
1719 | 670 | if (auto const surface = tools->focused_surface()) | ||
1720 | 671 | select_active_surface(surface); | ||
1721 | 672 | |||
1722 | 673 | return true; | ||
1723 | 674 | } | ||
1724 | 675 | else if (action == mir_keyboard_action_down && | ||
1725 | 676 | modifiers == mir_input_event_modifier_alt && | ||
1726 | 677 | scan_code == KEY_GRAVE) | ||
1727 | 678 | { | ||
1728 | 679 | if (auto const prev = tools->focused_surface()) | ||
1729 | 680 | { | ||
1730 | 681 | if (auto const app = tools->focused_session()) | ||
1731 | 682 | select_active_surface(app->surface_after(prev)); | ||
1732 | 683 | } | ||
1733 | 684 | |||
1734 | 685 | return true; | ||
1735 | 686 | } | ||
1736 | 687 | |||
1737 | 688 | return false; | ||
1738 | 689 | } | ||
1739 | 690 | |||
1740 | 691 | bool me::CanonicalWindowManagerPolicyCopy::handle_touch_event(MirTouchEvent const* event) | ||
1741 | 692 | { | ||
1742 | 693 | auto const count = mir_touch_event_point_count(event); | ||
1743 | 694 | |||
1744 | 695 | long total_x = 0; | ||
1745 | 696 | long total_y = 0; | ||
1746 | 697 | |||
1747 | 698 | for (auto i = 0U; i != count; ++i) | ||
1748 | 699 | { | ||
1749 | 700 | total_x += mir_touch_event_axis_value(event, i, mir_touch_axis_x); | ||
1750 | 701 | total_y += mir_touch_event_axis_value(event, i, mir_touch_axis_y); | ||
1751 | 702 | } | ||
1752 | 703 | |||
1753 | 704 | Point const cursor{total_x/count, total_y/count}; | ||
1754 | 705 | |||
1755 | 706 | bool is_drag = true; | ||
1756 | 707 | for (auto i = 0U; i != count; ++i) | ||
1757 | 708 | { | ||
1758 | 709 | switch (mir_touch_event_action(event, i)) | ||
1759 | 710 | { | ||
1760 | 711 | case mir_touch_action_up: | ||
1761 | 712 | return false; | ||
1762 | 713 | |||
1763 | 714 | case mir_touch_action_down: | ||
1764 | 715 | is_drag = false; | ||
1765 | 716 | // fallthrough | ||
1766 | 717 | case mir_touch_action_change: | ||
1767 | 718 | continue; | ||
1768 | 719 | |||
1769 | 720 | case mir_touch_actions: | ||
1770 | 721 | abort(); | ||
1771 | 722 | break; | ||
1772 | 723 | } | ||
1773 | 724 | } | ||
1774 | 725 | |||
1775 | 726 | bool consumes_event = false; | ||
1776 | 727 | if (is_drag) | ||
1777 | 728 | { | ||
1778 | 729 | switch (count) | ||
1779 | 730 | { | ||
1780 | 731 | case 4: | ||
1781 | 732 | resize(cursor); | ||
1782 | 733 | consumes_event = true; | ||
1783 | 734 | break; | ||
1784 | 735 | |||
1785 | 736 | case 3: | ||
1786 | 737 | drag(cursor); | ||
1787 | 738 | consumes_event = true; | ||
1788 | 739 | break; | ||
1789 | 740 | } | ||
1790 | 741 | } | ||
1791 | 742 | |||
1792 | 743 | old_cursor = cursor; | ||
1793 | 744 | return consumes_event; | ||
1794 | 745 | } | ||
1795 | 746 | |||
1796 | 747 | bool me::CanonicalWindowManagerPolicyCopy::handle_pointer_event(MirPointerEvent const* event) | ||
1797 | 748 | { | ||
1798 | 749 | auto const action = mir_pointer_event_action(event); | ||
1799 | 750 | auto const modifiers = mir_pointer_event_modifiers(event) & modifier_mask; | ||
1800 | 751 | Point const cursor{ | ||
1801 | 752 | mir_pointer_event_axis_value(event, mir_pointer_axis_x), | ||
1802 | 753 | mir_pointer_event_axis_value(event, mir_pointer_axis_y)}; | ||
1803 | 754 | |||
1804 | 755 | bool consumes_event = false; | ||
1805 | 756 | bool resize_event = false; | ||
1806 | 757 | |||
1807 | 758 | if (action == mir_pointer_action_button_down) | ||
1808 | 759 | { | ||
1809 | 760 | click(cursor); | ||
1810 | 761 | } | ||
1811 | 762 | else if (action == mir_pointer_action_motion && | ||
1812 | 763 | modifiers == mir_input_event_modifier_alt) | ||
1813 | 764 | { | ||
1814 | 765 | if (mir_pointer_event_button_state(event, mir_pointer_button_primary)) | ||
1815 | 766 | { | ||
1816 | 767 | drag(cursor); | ||
1817 | 768 | consumes_event = true; | ||
1818 | 769 | } | ||
1819 | 770 | |||
1820 | 771 | if (mir_pointer_event_button_state(event, mir_pointer_button_tertiary)) | ||
1821 | 772 | { | ||
1822 | 773 | resize(cursor); | ||
1823 | 774 | resize_event = active_surface_.lock().get(); | ||
1824 | 775 | consumes_event = true; | ||
1825 | 776 | } | ||
1826 | 777 | } | ||
1827 | 778 | else if (action == mir_pointer_action_motion && !modifiers) | ||
1828 | 779 | { | ||
1829 | 780 | if (mir_pointer_event_button_state(event, mir_pointer_button_primary)) | ||
1830 | 781 | { | ||
1831 | 782 | if (auto const possible_titlebar = tools->surface_at(old_cursor)) | ||
1832 | 783 | { | ||
1833 | 784 | if (tools->info_for(possible_titlebar).is_titlebar) | ||
1834 | 785 | { | ||
1835 | 786 | drag(cursor); | ||
1836 | 787 | consumes_event = true; | ||
1837 | 788 | } | ||
1838 | 789 | } | ||
1839 | 790 | } | ||
1840 | 791 | } | ||
1841 | 792 | |||
1842 | 793 | resizing = resize_event; | ||
1843 | 794 | old_cursor = cursor; | ||
1844 | 795 | return consumes_event; | ||
1845 | 796 | } | ||
1846 | 797 | |||
1847 | 798 | void me::CanonicalWindowManagerPolicyCopy::toggle(MirWindowState state) | ||
1848 | 799 | { | ||
1849 | 800 | if (auto const surface = active_surface()) | ||
1850 | 801 | { | ||
1851 | 802 | auto& info = tools->info_for(surface); | ||
1852 | 803 | |||
1853 | 804 | if (info.state == state) | ||
1854 | 805 | state = mir_window_state_restored; | ||
1855 | 806 | |||
1856 | 807 | auto const value = handle_set_state(surface, MirWindowState(state)); | ||
1857 | 808 | surface->configure(mir_window_attrib_state, value); | ||
1858 | 809 | } | ||
1859 | 810 | } | ||
1860 | 811 | |||
1861 | 812 | void me::CanonicalWindowManagerPolicyCopy::select_active_surface(std::shared_ptr<ms::Surface> const& surface) | ||
1862 | 813 | { | ||
1863 | 814 | if (surface == active_surface_.lock()) | ||
1864 | 815 | return; | ||
1865 | 816 | |||
1866 | 817 | if (!surface) | ||
1867 | 818 | { | ||
1868 | 819 | if (auto const active_surface = active_surface_.lock()) | ||
1869 | 820 | { | ||
1870 | 821 | if (auto const titlebar = tools->info_for(active_surface).titlebar) | ||
1871 | 822 | { | ||
1872 | 823 | tools->info_for(titlebar).paint_titlebar(0x3F); | ||
1873 | 824 | } | ||
1874 | 825 | } | ||
1875 | 826 | |||
1876 | 827 | if (active_surface_.lock()) | ||
1877 | 828 | tools->set_focus_to({}, {}); | ||
1878 | 829 | |||
1879 | 830 | active_surface_.reset(); | ||
1880 | 831 | return; | ||
1881 | 832 | } | ||
1882 | 833 | |||
1883 | 834 | auto const& info_for = tools->info_for(surface); | ||
1884 | 835 | |||
1885 | 836 | if (info_for.can_be_active()) | ||
1886 | 837 | { | ||
1887 | 838 | if (auto const active_surface = active_surface_.lock()) | ||
1888 | 839 | { | ||
1889 | 840 | if (auto const titlebar = tools->info_for(active_surface).titlebar) | ||
1890 | 841 | { | ||
1891 | 842 | tools->info_for(titlebar).paint_titlebar(0x3F); | ||
1892 | 843 | } | ||
1893 | 844 | } | ||
1894 | 845 | if (auto const titlebar = tools->info_for(surface).titlebar) | ||
1895 | 846 | { | ||
1896 | 847 | tools->info_for(titlebar).paint_titlebar(0xFF); | ||
1897 | 848 | } | ||
1898 | 849 | tools->set_focus_to(info_for.session.lock(), surface); | ||
1899 | 850 | tools->raise_tree(surface); | ||
1900 | 851 | active_surface_ = surface; | ||
1901 | 852 | } | ||
1902 | 853 | else | ||
1903 | 854 | { | ||
1904 | 855 | // Cannot have input focus - try the parent | ||
1905 | 856 | if (auto const parent = info_for.parent.lock()) | ||
1906 | 857 | select_active_surface(parent); | ||
1907 | 858 | } | ||
1908 | 859 | } | ||
1909 | 860 | |||
1910 | 861 | auto me::CanonicalWindowManagerPolicyCopy::active_surface() const | ||
1911 | 862 | -> std::shared_ptr<ms::Surface> | ||
1912 | 863 | { | ||
1913 | 864 | if (auto const surface = active_surface_.lock()) | ||
1914 | 865 | return surface; | ||
1915 | 866 | |||
1916 | 867 | if (auto const session = tools->focused_session()) | ||
1917 | 868 | { | ||
1918 | 869 | if (auto const surface = session->default_surface()) | ||
1919 | 870 | return surface; | ||
1920 | 871 | } | ||
1921 | 872 | |||
1922 | 873 | return std::shared_ptr<ms::Surface>{}; | ||
1923 | 874 | } | ||
1924 | 875 | |||
1925 | 876 | bool me::CanonicalWindowManagerPolicyCopy::resize(std::shared_ptr<scene::Surface> const& surface, Point cursor, Point old_cursor, Rectangle bounds) | ||
1926 | 877 | { | ||
1927 | 878 | if (!surface) | ||
1928 | 879 | return false; | ||
1929 | 880 | |||
1930 | 881 | auto const& surface_info = tools->info_for(surface); | ||
1931 | 882 | |||
1932 | 883 | auto const top_left = surface->top_left(); | ||
1933 | 884 | Rectangle const old_pos{top_left, surface->size()}; | ||
1934 | 885 | |||
1935 | 886 | if (!resizing) | ||
1936 | 887 | { | ||
1937 | 888 | auto anchor = old_pos.bottom_right(); | ||
1938 | 889 | |||
1939 | 890 | for (auto const& corner : { | ||
1940 | 891 | old_pos.top_right(), | ||
1941 | 892 | old_pos.bottom_left(), | ||
1942 | 893 | top_left}) | ||
1943 | 894 | { | ||
1944 | 895 | if ((old_cursor - anchor).length_squared() < | ||
1945 | 896 | (old_cursor - corner).length_squared()) | ||
1946 | 897 | { | ||
1947 | 898 | anchor = corner; | ||
1948 | 899 | } | ||
1949 | 900 | } | ||
1950 | 901 | |||
1951 | 902 | left_resize = anchor.x != top_left.x; | ||
1952 | 903 | top_resize = anchor.y != top_left.y; | ||
1953 | 904 | } | ||
1954 | 905 | |||
1955 | 906 | int const x_sign = left_resize? -1 : 1; | ||
1956 | 907 | int const y_sign = top_resize? -1 : 1; | ||
1957 | 908 | |||
1958 | 909 | auto delta = cursor-old_cursor; | ||
1959 | 910 | |||
1960 | 911 | auto new_width = old_pos.size.width + x_sign * delta.dx; | ||
1961 | 912 | auto new_height = old_pos.size.height + y_sign * delta.dy; | ||
1962 | 913 | |||
1963 | 914 | auto const min_width = std::max(surface_info.min_width, Width{5}); | ||
1964 | 915 | auto const min_height = std::max(surface_info.min_height, Height{5}); | ||
1965 | 916 | |||
1966 | 917 | if (new_width < min_width) | ||
1967 | 918 | { | ||
1968 | 919 | new_width = min_width; | ||
1969 | 920 | if (delta.dx > DeltaX{0}) | ||
1970 | 921 | delta.dx = DeltaX{0}; | ||
1971 | 922 | } | ||
1972 | 923 | |||
1973 | 924 | if (new_height < min_height) | ||
1974 | 925 | { | ||
1975 | 926 | new_height = min_height; | ||
1976 | 927 | if (delta.dy > DeltaY{0}) | ||
1977 | 928 | delta.dy = DeltaY{0}; | ||
1978 | 929 | } | ||
1979 | 930 | |||
1980 | 931 | Size new_size{new_width, new_height}; | ||
1981 | 932 | Point new_pos = top_left + left_resize*delta.dx + top_resize*delta.dy; | ||
1982 | 933 | |||
1983 | 934 | |||
1984 | 935 | surface_info.constrain_resize(surface, new_pos, new_size, left_resize, top_resize, bounds); | ||
1985 | 936 | |||
1986 | 937 | apply_resize(surface, surface_info.titlebar, new_pos, new_size); | ||
1987 | 938 | |||
1988 | 939 | return true; | ||
1989 | 940 | } | ||
1990 | 941 | |||
1991 | 942 | void me::CanonicalWindowManagerPolicyCopy::apply_resize( | ||
1992 | 943 | std::shared_ptr<ms::Surface> const& surface, | ||
1993 | 944 | std::shared_ptr<ms::Surface> const& titlebar, | ||
1994 | 945 | Point const& new_pos, | ||
1995 | 946 | Size const& new_size) const | ||
1996 | 947 | { | ||
1997 | 948 | if (titlebar) | ||
1998 | 949 | titlebar->resize({new_size.width, Height{title_bar_height}}); | ||
1999 | 950 | |||
2000 | 951 | surface->resize(new_size); | ||
2001 | 952 | |||
2002 | 953 | move_tree(surface, new_pos-surface->top_left()); | ||
2003 | 954 | } | ||
2004 | 955 | |||
2005 | 956 | bool me::CanonicalWindowManagerPolicyCopy::drag(std::shared_ptr<scene::Surface> surface, Point to, Point from, Rectangle /*bounds*/) | ||
2006 | 957 | { | ||
2007 | 958 | if (!surface) | ||
2008 | 959 | return false; | ||
2009 | 960 | |||
2010 | 961 | if (!surface->input_area_contains(from) && !tools->info_for(surface).titlebar) | ||
2011 | 962 | return false; | ||
2012 | 963 | |||
2013 | 964 | auto movement = to - from; | ||
2014 | 965 | |||
2015 | 966 | // placeholder - constrain onscreen | ||
2016 | 967 | |||
2017 | 968 | switch (tools->info_for(surface).state) | ||
2018 | 969 | { | ||
2019 | 970 | case mir_window_state_restored: | ||
2020 | 971 | break; | ||
2021 | 972 | |||
2022 | 973 | // "A vertically maximised surface is anchored to the top and bottom of | ||
2023 | 974 | // the available workspace and can have any width." | ||
2024 | 975 | case mir_window_state_vertmaximized: | ||
2025 | 976 | movement.dy = DeltaY(0); | ||
2026 | 977 | break; | ||
2027 | 978 | |||
2028 | 979 | // "A horizontally maximised surface is anchored to the left and right of | ||
2029 | 980 | // the available workspace and can have any height" | ||
2030 | 981 | case mir_window_state_horizmaximized: | ||
2031 | 982 | movement.dx = DeltaX(0); | ||
2032 | 983 | break; | ||
2033 | 984 | |||
2034 | 985 | // "A maximised surface is anchored to the top, bottom, left and right of the | ||
2035 | 986 | // available workspace. For example, if the launcher is always-visible then | ||
2036 | 987 | // the left-edge of the surface is anchored to the right-edge of the launcher." | ||
2037 | 988 | case mir_window_state_maximized: | ||
2038 | 989 | case mir_window_state_fullscreen: | ||
2039 | 990 | default: | ||
2040 | 991 | return true; | ||
2041 | 992 | } | ||
2042 | 993 | |||
2043 | 994 | move_tree(surface, movement); | ||
2044 | 995 | |||
2045 | 996 | return true; | ||
2046 | 997 | } | ||
2047 | 998 | |||
2048 | 999 | void me::CanonicalWindowManagerPolicyCopy::move_tree(std::shared_ptr<ms::Surface> const& root, Displacement movement) const | ||
2049 | 1000 | { | ||
2050 | 1001 | root->move_to(root->top_left() + movement); | ||
2051 | 1002 | |||
2052 | 1003 | for (auto const& child: tools->info_for(root).children) | ||
2053 | 1004 | { | ||
2054 | 1005 | move_tree(child.lock(), movement); | ||
2055 | 1006 | } | ||
2056 | 1007 | } | ||
2057 | 1008 | 0 | ||
2058 | === removed file 'examples/server_example_canonical_window_manager.h' | |||
2059 | --- examples/server_example_canonical_window_manager.h 2017-07-28 17:00:43 +0000 | |||
2060 | +++ examples/server_example_canonical_window_manager.h 1970-01-01 00:00:00 +0000 | |||
2061 | @@ -1,142 +0,0 @@ | |||
2062 | 1 | /* | ||
2063 | 2 | * Copyright © 2015 Canonical Ltd. | ||
2064 | 3 | * | ||
2065 | 4 | * This program is free software: you can redistribute it and/or modify it | ||
2066 | 5 | * under the terms of the GNU General Public License version 2 or 3, | ||
2067 | 6 | * as published by the Free Software Foundation. | ||
2068 | 7 | * | ||
2069 | 8 | * This program is distributed in the hope that it will be useful, | ||
2070 | 9 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
2071 | 10 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
2072 | 11 | * GNU General Public License for more details. | ||
2073 | 12 | * | ||
2074 | 13 | * You should have received a copy of the GNU General Public License | ||
2075 | 14 | * along with this program. If not, see <http://www.gnu.org/licenses/>. | ||
2076 | 15 | * | ||
2077 | 16 | * Authored By: Alan Griffiths <alan@octopull.co.uk> | ||
2078 | 17 | */ | ||
2079 | 18 | |||
2080 | 19 | #ifndef MIR_EXAMPLE_CANONICAL_WINDOW_MANAGER_H_ | ||
2081 | 20 | #define MIR_EXAMPLE_CANONICAL_WINDOW_MANAGER_H_ | ||
2082 | 21 | |||
2083 | 22 | #include "server_example_basic_window_manager.h" | ||
2084 | 23 | |||
2085 | 24 | #include "mir/geometry/displacement.h" | ||
2086 | 25 | |||
2087 | 26 | #include <atomic> | ||
2088 | 27 | #include <set> | ||
2089 | 28 | |||
2090 | 29 | ///\example server_example_canonical_window_manager.h | ||
2091 | 30 | // Based on "Mir and Unity: Surfaces, input, and displays (v0.3)" | ||
2092 | 31 | |||
2093 | 32 | namespace mir | ||
2094 | 33 | { | ||
2095 | 34 | namespace shell { class DisplayLayout; } | ||
2096 | 35 | namespace graphics | ||
2097 | 36 | { | ||
2098 | 37 | class GraphicBufferAllocator; | ||
2099 | 38 | } | ||
2100 | 39 | namespace examples | ||
2101 | 40 | { | ||
2102 | 41 | // standard window management algorithm: | ||
2103 | 42 | // o Switch apps: tap or click on the corresponding tile | ||
2104 | 43 | // o Move window: Alt-leftmousebutton drag (three finger drag) | ||
2105 | 44 | // o Resize window: Alt-middle_button drag (two finger drag) | ||
2106 | 45 | // o Maximize/restore current window (to display size): Alt-F11 | ||
2107 | 46 | // o Maximize/restore current window (to display height): Shift-F11 | ||
2108 | 47 | // o Maximize/restore current window (to display width): Ctrl-F11 | ||
2109 | 48 | // o client requests to maximize, vertically maximize & restore | ||
2110 | 49 | class CanonicalWindowManagerPolicyCopy : public WindowManagementPolicy | ||
2111 | 50 | { | ||
2112 | 51 | public: | ||
2113 | 52 | |||
2114 | 53 | explicit CanonicalWindowManagerPolicyCopy( | ||
2115 | 54 | WindowManagerTools* const tools, | ||
2116 | 55 | std::shared_ptr<shell::DisplayLayout> const& display_layout, | ||
2117 | 56 | std::shared_ptr<graphics::GraphicBufferAllocator> const& allocator); | ||
2118 | 57 | |||
2119 | 58 | void click(geometry::Point cursor); | ||
2120 | 59 | |||
2121 | 60 | void handle_session_info_updated(SessionInfoMap& session_info, geometry::Rectangles const& displays); | ||
2122 | 61 | |||
2123 | 62 | void handle_displays_updated(SessionInfoMap& session_info, geometry::Rectangles const& displays); | ||
2124 | 63 | |||
2125 | 64 | void resize(geometry::Point cursor); | ||
2126 | 65 | |||
2127 | 66 | auto handle_place_new_surface( | ||
2128 | 67 | std::shared_ptr<scene::Session> const& session, | ||
2129 | 68 | scene::SurfaceCreationParameters const& request_parameters) | ||
2130 | 69 | -> scene::SurfaceCreationParameters; | ||
2131 | 70 | |||
2132 | 71 | void handle_new_surface(std::shared_ptr<scene::Session> const& session, std::shared_ptr<scene::Surface> const& surface); | ||
2133 | 72 | |||
2134 | 73 | void handle_modify_surface( | ||
2135 | 74 | std::shared_ptr<scene::Session> const& session, | ||
2136 | 75 | std::shared_ptr<scene::Surface> const& surface, | ||
2137 | 76 | shell::SurfaceSpecification const& modifications); | ||
2138 | 77 | |||
2139 | 78 | void handle_delete_surface(std::shared_ptr<scene::Session> const& session, std::weak_ptr<scene::Surface> const& surface); | ||
2140 | 79 | |||
2141 | 80 | int handle_set_state(std::shared_ptr<scene::Surface> const& surface, MirWindowState value); | ||
2142 | 81 | |||
2143 | 82 | void drag(geometry::Point cursor); | ||
2144 | 83 | |||
2145 | 84 | bool handle_keyboard_event(MirKeyboardEvent const* event); | ||
2146 | 85 | |||
2147 | 86 | bool handle_touch_event(MirTouchEvent const* event); | ||
2148 | 87 | |||
2149 | 88 | bool handle_pointer_event(MirPointerEvent const* event); | ||
2150 | 89 | |||
2151 | 90 | void handle_raise_surface( | ||
2152 | 91 | std::shared_ptr<scene::Session> const& session, | ||
2153 | 92 | std::shared_ptr<scene::Surface> const& surface); | ||
2154 | 93 | |||
2155 | 94 | void generate_decorations_for( | ||
2156 | 95 | std::shared_ptr<scene::Session> const& session, | ||
2157 | 96 | std::shared_ptr<scene::Surface> const& surface, | ||
2158 | 97 | SurfaceInfoMap& surface_map, | ||
2159 | 98 | std::function<frontend::SurfaceId(std::shared_ptr<scene::Session> const& session, scene::SurfaceCreationParameters const& params)> const& build); | ||
2160 | 99 | |||
2161 | 100 | private: | ||
2162 | 101 | static const int modifier_mask = | ||
2163 | 102 | mir_input_event_modifier_alt | | ||
2164 | 103 | mir_input_event_modifier_shift | | ||
2165 | 104 | mir_input_event_modifier_sym | | ||
2166 | 105 | mir_input_event_modifier_ctrl | | ||
2167 | 106 | mir_input_event_modifier_meta; | ||
2168 | 107 | |||
2169 | 108 | void toggle(MirWindowState state); | ||
2170 | 109 | |||
2171 | 110 | // "Mir and Unity: Surfaces, input, and displays (v0.3)" talks about active | ||
2172 | 111 | // *window*,but Mir really only understands surfaces | ||
2173 | 112 | void select_active_surface(std::shared_ptr<scene::Surface> const& surface); | ||
2174 | 113 | auto active_surface() const -> std::shared_ptr<scene::Surface>; | ||
2175 | 114 | |||
2176 | 115 | bool resize(std::shared_ptr<scene::Surface> const& surface, geometry::Point cursor, geometry::Point old_cursor, geometry::Rectangle bounds); | ||
2177 | 116 | bool drag(std::shared_ptr<scene::Surface> surface, geometry::Point to, geometry::Point from, geometry::Rectangle bounds); | ||
2178 | 117 | void move_tree(std::shared_ptr<scene::Surface> const& root, geometry::Displacement movement) const; | ||
2179 | 118 | void apply_resize( | ||
2180 | 119 | std::shared_ptr<mir::scene::Surface> const& surface, | ||
2181 | 120 | std::shared_ptr<mir::scene::Surface> const& titlebar, | ||
2182 | 121 | geometry::Point const& new_pos, | ||
2183 | 122 | geometry::Size const& new_size) const; | ||
2184 | 123 | |||
2185 | 124 | WindowManagerTools* const tools; | ||
2186 | 125 | std::shared_ptr<shell::DisplayLayout> const display_layout; | ||
2187 | 126 | std::shared_ptr<graphics::GraphicBufferAllocator> const allocator; | ||
2188 | 127 | |||
2189 | 128 | geometry::Rectangle display_area; | ||
2190 | 129 | geometry::Point old_cursor{}; | ||
2191 | 130 | std::weak_ptr<scene::Surface> active_surface_; | ||
2192 | 131 | using FullscreenSurfaces = std::set<std::weak_ptr<scene::Surface>, std::owner_less<std::weak_ptr<scene::Surface>>>; | ||
2193 | 132 | |||
2194 | 133 | FullscreenSurfaces fullscreen_surfaces; | ||
2195 | 134 | |||
2196 | 135 | bool resizing = false; | ||
2197 | 136 | bool left_resize = false; | ||
2198 | 137 | bool top_resize = false; | ||
2199 | 138 | }; | ||
2200 | 139 | } | ||
2201 | 140 | } | ||
2202 | 141 | |||
2203 | 142 | #endif /* MIR_EXAMPLE_CANONICAL_WINDOW_MANAGER_H_ */ | ||
2204 | 143 | 0 | ||
2205 | === removed file 'examples/server_example_cursor_images.cpp' | |||
2206 | --- examples/server_example_cursor_images.cpp 2017-07-28 17:00:43 +0000 | |||
2207 | +++ examples/server_example_cursor_images.cpp 1970-01-01 00:00:00 +0000 | |||
2208 | @@ -1,57 +0,0 @@ | |||
2209 | 1 | /* | ||
2210 | 2 | * Copyright © 2015 Canonical Ltd. | ||
2211 | 3 | * | ||
2212 | 4 | * This program is free software: you can redistribute it and/or modify it | ||
2213 | 5 | * under the terms of the GNU General Public License version 2 or 3, | ||
2214 | 6 | * as published by the Free Software Foundation. | ||
2215 | 7 | * | ||
2216 | 8 | * This program is distributed in the hope that it will be useful, | ||
2217 | 9 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
2218 | 10 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
2219 | 11 | * GNU General Public License for more details. | ||
2220 | 12 | * | ||
2221 | 13 | * You should have received a copy of the GNU General Public License | ||
2222 | 14 | * along with this program. If not, see <http://www.gnu.org/licenses/>. | ||
2223 | 15 | * | ||
2224 | 16 | * Authored by: Alan Griffiths <alan@octopull.co.uk> | ||
2225 | 17 | */ | ||
2226 | 18 | |||
2227 | 19 | #include "server_example_cursor_images.h" | ||
2228 | 20 | #include "xcursor_loader.h" | ||
2229 | 21 | |||
2230 | 22 | #include "mir/server.h" | ||
2231 | 23 | #include "mir/options/option.h" | ||
2232 | 24 | |||
2233 | 25 | #include <mir_toolkit/cursors.h> | ||
2234 | 26 | |||
2235 | 27 | namespace mi = mir::input; | ||
2236 | 28 | |||
2237 | 29 | namespace | ||
2238 | 30 | { | ||
2239 | 31 | char const* const xcursor_theme = "x-cursor-theme"; | ||
2240 | 32 | char const* const xcursor_description = "X Cursor theme to load [default, DMZ-White, DMZ-Black, ...]"; | ||
2241 | 33 | |||
2242 | 34 | bool has_default_cursor(mi::CursorImages& images) | ||
2243 | 35 | { | ||
2244 | 36 | if (images.image(mir_default_cursor_name, mi::default_cursor_size)) | ||
2245 | 37 | return true; | ||
2246 | 38 | return false; | ||
2247 | 39 | } | ||
2248 | 40 | } | ||
2249 | 41 | |||
2250 | 42 | void mir::examples::add_x_cursor_images(Server& server) | ||
2251 | 43 | { | ||
2252 | 44 | server.add_configuration_option(xcursor_theme, xcursor_description, "default"); | ||
2253 | 45 | |||
2254 | 46 | server.override_the_cursor_images([&] | ||
2255 | 47 | { | ||
2256 | 48 | auto const theme = server.get_options()->get<std::string>(xcursor_theme); | ||
2257 | 49 | |||
2258 | 50 | std::shared_ptr<mi::CursorImages> const xcursor_loader{std::make_shared<XCursorLoader>(theme)}; | ||
2259 | 51 | |||
2260 | 52 | if (has_default_cursor(*xcursor_loader)) | ||
2261 | 53 | return xcursor_loader; | ||
2262 | 54 | else | ||
2263 | 55 | return std::shared_ptr<mi::CursorImages>{}; | ||
2264 | 56 | }); | ||
2265 | 57 | } | ||
2266 | 58 | 0 | ||
2267 | === removed file 'examples/server_example_cursor_images.h' | |||
2268 | --- examples/server_example_cursor_images.h 2017-07-28 17:00:43 +0000 | |||
2269 | +++ examples/server_example_cursor_images.h 1970-01-01 00:00:00 +0000 | |||
2270 | @@ -1,33 +0,0 @@ | |||
2271 | 1 | /* | ||
2272 | 2 | * Copyright © 2015 Canonical Ltd. | ||
2273 | 3 | * | ||
2274 | 4 | * This program is free software: you can redistribute it and/or modify it | ||
2275 | 5 | * under the terms of the GNU General Public License version 2 or 3, | ||
2276 | 6 | * as published by the Free Software Foundation. | ||
2277 | 7 | * | ||
2278 | 8 | * This program is distributed in the hope that it will be useful, | ||
2279 | 9 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
2280 | 10 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
2281 | 11 | * GNU General Public License for more details. | ||
2282 | 12 | * | ||
2283 | 13 | * You should have received a copy of the GNU General Public License | ||
2284 | 14 | * along with this program. If not, see <http://www.gnu.org/licenses/>. | ||
2285 | 15 | * | ||
2286 | 16 | * Authored by: Alan Griffiths <alan@octopull.co.uk> | ||
2287 | 17 | */ | ||
2288 | 18 | |||
2289 | 19 | #ifndef MIR_EXAMPLE_CURSOR_IMAGES_H | ||
2290 | 20 | #define MIR_EXAMPLE_CURSOR_IMAGES_H | ||
2291 | 21 | |||
2292 | 22 | namespace mir | ||
2293 | 23 | { | ||
2294 | 24 | class Server; | ||
2295 | 25 | |||
2296 | 26 | namespace examples | ||
2297 | 27 | { | ||
2298 | 28 | void add_x_cursor_images(Server& server); | ||
2299 | 29 | } | ||
2300 | 30 | } // namespace mir | ||
2301 | 31 | |||
2302 | 32 | |||
2303 | 33 | #endif //MIR_EXAMPLE_CURSOR_IMAGES_H | ||
2304 | 34 | 0 | ||
2305 | === modified file 'examples/server_example_test_client.cpp' | |||
2306 | --- examples/server_example_test_client.cpp 2017-07-28 17:00:43 +0000 | |||
2307 | +++ examples/server_example_test_client.cpp 2017-08-31 08:02:08 +0000 | |||
2308 | @@ -1,5 +1,5 @@ | |||
2309 | 1 | /* | 1 | /* |
2311 | 2 | * Copyright © 2014-2015 Canonical Ltd. | 2 | * Copyright © 2014-2017 Canonical Ltd. |
2312 | 3 | * | 3 | * |
2313 | 4 | * This program is free software: you can redistribute it and/or modify it | 4 | * This program is free software: you can redistribute it and/or modify it |
2314 | 5 | * under the terms of the GNU General Public License version 2 or 3, | 5 | * under the terms of the GNU General Public License version 2 or 3, |
2315 | @@ -24,11 +24,12 @@ | |||
2316 | 24 | #include "mir/log.h" | 24 | #include "mir/log.h" |
2317 | 25 | #include "mir/options/option.h" | 25 | #include "mir/options/option.h" |
2318 | 26 | 26 | ||
2319 | 27 | #include <chrono> | ||
2320 | 28 | #include <future> | ||
2321 | 29 | |||
2322 | 27 | #include <csignal> | 30 | #include <csignal> |
2323 | 28 | #include <sys/wait.h> | 31 | #include <sys/wait.h> |
2324 | 29 | 32 | ||
2325 | 30 | #include <chrono> | ||
2326 | 31 | |||
2327 | 32 | namespace me = mir::examples; | 33 | namespace me = mir::examples; |
2328 | 33 | namespace ml = mir::logging; | 34 | namespace ml = mir::logging; |
2329 | 34 | 35 | ||
2330 | @@ -87,7 +88,16 @@ | |||
2331 | 87 | } | 88 | } |
2332 | 88 | } | 89 | } |
2333 | 89 | 90 | ||
2335 | 90 | void me::add_test_client_option_to(mir::Server& server, me::ClientContext& context) | 91 | struct me::TestClientRunner::Self |
2336 | 92 | { | ||
2337 | 93 | std::unique_ptr<time::Alarm> client_kill_action; | ||
2338 | 94 | std::unique_ptr<time::Alarm> server_stop_action; | ||
2339 | 95 | std::atomic<bool> test_failed; | ||
2340 | 96 | }; | ||
2341 | 97 | |||
2342 | 98 | me::TestClientRunner::TestClientRunner() : self{std::make_shared<Self>()} {} | ||
2343 | 99 | |||
2344 | 100 | void me::TestClientRunner::operator()(mir::Server& server) | ||
2345 | 91 | { | 101 | { |
2346 | 92 | static const char* const test_client_opt = "test-client"; | 102 | static const char* const test_client_opt = "test-client"; |
2347 | 93 | static const char* const test_client_descr = "client executable"; | 103 | static const char* const test_client_descr = "client executable"; |
2348 | @@ -95,16 +105,16 @@ | |||
2349 | 95 | static const char* const test_timeout_opt = "test-timeout"; | 105 | static const char* const test_timeout_opt = "test-timeout"; |
2350 | 96 | static const char* const test_timeout_descr = "Seconds to run before sending SIGTERM to client"; | 106 | static const char* const test_timeout_descr = "Seconds to run before sending SIGTERM to client"; |
2351 | 97 | 107 | ||
2353 | 98 | server.add_configuration_option(test_client_opt, test_client_descr, mir::OptionType::string); | 108 | server.add_configuration_option(test_client_opt, test_client_descr, OptionType::string); |
2354 | 99 | server.add_configuration_option(test_timeout_opt, test_timeout_descr, 10); | 109 | server.add_configuration_option(test_timeout_opt, test_timeout_descr, 10); |
2355 | 100 | 110 | ||
2357 | 101 | server.add_init_callback([&server, &context] | 111 | server.add_init_callback([&server, self = self] |
2358 | 102 | { | 112 | { |
2360 | 103 | const auto options = server.get_options(); | 113 | const auto options1 = server.get_options(); |
2361 | 104 | 114 | ||
2363 | 105 | if (options->is_set(test_client_opt)) | 115 | if (options1->is_set(test_client_opt)) |
2364 | 106 | { | 116 | { |
2366 | 107 | context.test_failed = true; | 117 | self->test_failed = true; |
2367 | 108 | 118 | ||
2368 | 109 | auto const client_fd = server.open_client_socket(); | 119 | auto const client_fd = server.open_client_socket(); |
2369 | 110 | 120 | ||
2370 | @@ -120,29 +130,30 @@ | |||
2371 | 120 | 130 | ||
2372 | 121 | setenv("MIR_SOCKET", connect_string, 1); | 131 | setenv("MIR_SOCKET", connect_string, 1); |
2373 | 122 | 132 | ||
2375 | 123 | auto const client = options->get<std::string>(test_client_opt); | 133 | auto const client = options1->get<std::string>(test_client_opt); |
2376 | 124 | execlp(client.c_str(), client.c_str(), static_cast<char const*>(nullptr)); | 134 | execlp(client.c_str(), client.c_str(), static_cast<char const*>(nullptr)); |
2377 | 135 | // If execl() returns then something is badly wrong | ||
2378 | 125 | log(logging::Severity::critical, "mir::examples", | 136 | log(logging::Severity::critical, "mir::examples", |
2379 | 126 | "Failed to execute client (%s) error: %s", client.c_str(), strerror(errno)); | 137 | "Failed to execute client (%s) error: %s", client.c_str(), strerror(errno)); |
2381 | 127 | abort(); // If execl() returns then something is badly wrong | 138 | abort(); |
2382 | 128 | } | 139 | } |
2383 | 129 | else if (pid > 0) | 140 | else if (pid > 0) |
2384 | 130 | { | 141 | { |
2386 | 131 | context.client_kill_action = server.the_main_loop()->create_alarm( | 142 | self->client_kill_action = server.the_main_loop()->create_alarm( |
2387 | 132 | [pid] | 143 | [pid] |
2388 | 133 | { | 144 | { |
2389 | 134 | kill(pid, SIGTERM); | 145 | kill(pid, SIGTERM); |
2390 | 135 | }); | 146 | }); |
2391 | 136 | 147 | ||
2394 | 137 | context.server_stop_action = server.the_main_loop()->create_alarm( | 148 | self->server_stop_action = server.the_main_loop()->create_alarm( |
2395 | 138 | [pid, &server, &context]() | 149 | [pid, &server, self]() |
2396 | 139 | { | 150 | { |
2398 | 140 | context.test_failed = !exit_success(pid); | 151 | self->test_failed = !exit_success(pid); |
2399 | 141 | server.stop(); | 152 | server.stop(); |
2400 | 142 | }); | 153 | }); |
2401 | 143 | 154 | ||
2404 | 144 | context.client_kill_action->reschedule_in(std::chrono::seconds(options->get<int>(test_timeout_opt))); | 155 | self->client_kill_action->reschedule_in(std::chrono::seconds(options1->get<int>(test_timeout_opt))); |
2405 | 145 | context.server_stop_action->reschedule_in(std::chrono::seconds(options->get<int>(test_timeout_opt)+1)); | 156 | self->server_stop_action->reschedule_in(std::chrono::seconds(options1->get<int>(test_timeout_opt) + 1)); |
2406 | 146 | } | 157 | } |
2407 | 147 | else | 158 | else |
2408 | 148 | { | 159 | { |
2409 | @@ -151,7 +162,9 @@ | |||
2410 | 151 | } | 162 | } |
2411 | 152 | else | 163 | else |
2412 | 153 | { | 164 | { |
2414 | 154 | context.test_failed = false; | 165 | self->test_failed = false; |
2415 | 155 | } | 166 | } |
2416 | 156 | }); | 167 | }); |
2417 | 157 | } | 168 | } |
2418 | 169 | |||
2419 | 170 | bool me::TestClientRunner::test_failed() const { return self->test_failed; } | ||
2420 | 158 | 171 | ||
2421 | === modified file 'examples/server_example_test_client.h' | |||
2422 | --- examples/server_example_test_client.h 2017-07-28 17:00:43 +0000 | |||
2423 | +++ examples/server_example_test_client.h 2017-08-31 08:02:08 +0000 | |||
2424 | @@ -20,9 +20,6 @@ | |||
2425 | 20 | #define MIR_EXAMPLE_TEST_CLIENT_H_ | 20 | #define MIR_EXAMPLE_TEST_CLIENT_H_ |
2426 | 21 | 21 | ||
2427 | 22 | #include <memory> | 22 | #include <memory> |
2428 | 23 | #include <future> | ||
2429 | 24 | |||
2430 | 25 | #include "mir/main_loop.h" | ||
2431 | 26 | 23 | ||
2432 | 27 | namespace mir | 24 | namespace mir |
2433 | 28 | { | 25 | { |
2434 | @@ -30,14 +27,21 @@ | |||
2435 | 30 | 27 | ||
2436 | 31 | namespace examples | 28 | namespace examples |
2437 | 32 | { | 29 | { |
2439 | 33 | struct ClientContext | 30 | |
2440 | 31 | class TestClientRunner | ||
2441 | 34 | { | 32 | { |
2445 | 35 | std::unique_ptr<mir::time::Alarm> client_kill_action; | 33 | public: |
2446 | 36 | std::unique_ptr<mir::time::Alarm> server_stop_action; | 34 | TestClientRunner(); |
2447 | 37 | std::atomic<bool> test_failed; | 35 | |
2448 | 36 | void operator()(mir::Server& server); | ||
2449 | 37 | |||
2450 | 38 | bool test_failed() const; | ||
2451 | 39 | |||
2452 | 40 | private: | ||
2453 | 41 | struct Self; | ||
2454 | 42 | std::shared_ptr<Self> self; | ||
2455 | 38 | }; | 43 | }; |
2456 | 39 | 44 | ||
2457 | 40 | void add_test_client_option_to(mir::Server& server, ClientContext& context); | ||
2458 | 41 | } | 45 | } |
2459 | 42 | } | 46 | } |
2460 | 43 | 47 | ||
2461 | 44 | 48 | ||
2462 | === removed file 'examples/server_example_window_management.cpp' | |||
2463 | --- examples/server_example_window_management.cpp 2017-07-28 17:00:43 +0000 | |||
2464 | +++ examples/server_example_window_management.cpp 1970-01-01 00:00:00 +0000 | |||
2465 | @@ -1,154 +0,0 @@ | |||
2466 | 1 | /* | ||
2467 | 2 | * Copyright © 2014 Canonical Ltd. | ||
2468 | 3 | * | ||
2469 | 4 | * This program is free software: you can redistribute it and/or modify it | ||
2470 | 5 | * under the terms of the GNU General Public License version 2 or 3, | ||
2471 | 6 | * as published by the Free Software Foundation. | ||
2472 | 7 | * | ||
2473 | 8 | * This program is distributed in the hope that it will be useful, | ||
2474 | 9 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
2475 | 10 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
2476 | 11 | * GNU General Public License for more details. | ||
2477 | 12 | * | ||
2478 | 13 | * You should have received a copy of the GNU General Public License | ||
2479 | 14 | * along with this program. If not, see <http://www.gnu.org/licenses/>. | ||
2480 | 15 | * | ||
2481 | 16 | * Authored By: Alan Griffiths <alan@octopull.co.uk> | ||
2482 | 17 | */ | ||
2483 | 18 | |||
2484 | 19 | #include "server_example_window_management.h" | ||
2485 | 20 | |||
2486 | 21 | #include "server_example_canonical_window_manager.h" | ||
2487 | 22 | |||
2488 | 23 | #include "mir/abnormal_exit.h" | ||
2489 | 24 | #include "mir/server.h" | ||
2490 | 25 | #include "mir/input/composite_event_filter.h" | ||
2491 | 26 | #include "mir/options/option.h" | ||
2492 | 27 | #include "mir/scene/session.h" | ||
2493 | 28 | #include "mir/scene/surface_creation_parameters.h" | ||
2494 | 29 | #include "mir/shell/display_layout.h" | ||
2495 | 30 | #include "mir/shell/system_compositor_window_manager.h" | ||
2496 | 31 | #include "mir/graphics/platform.h" | ||
2497 | 32 | #include "mir/graphics/graphic_buffer_allocator.h" | ||
2498 | 33 | |||
2499 | 34 | namespace me = mir::examples; | ||
2500 | 35 | namespace mf = mir::frontend; | ||
2501 | 36 | namespace mg = mir::graphics; | ||
2502 | 37 | namespace mi = mir::input; | ||
2503 | 38 | namespace ms = mir::scene; | ||
2504 | 39 | namespace msh = mir::shell; | ||
2505 | 40 | using namespace mir::geometry; | ||
2506 | 41 | |||
2507 | 42 | ///\example server_example_window_management.cpp | ||
2508 | 43 | /// Demonstrate introducing a window management strategy | ||
2509 | 44 | |||
2510 | 45 | namespace | ||
2511 | 46 | { | ||
2512 | 47 | char const* const wm_option = "window-manager"; | ||
2513 | 48 | char const* const wm_description = "window management strategy [{canonical|fullscreen|system-compositor}]"; | ||
2514 | 49 | |||
2515 | 50 | char const* const wm_fullscreen = "fullscreen"; | ||
2516 | 51 | char const* const wm_canonical = "canonical"; | ||
2517 | 52 | char const* const wm_system_compositor = "system-compositor"; | ||
2518 | 53 | |||
2519 | 54 | // Very simple - make every surface fullscreen | ||
2520 | 55 | class FullscreenWindowManagerPolicy : public me::WindowManagementPolicy | ||
2521 | 56 | { | ||
2522 | 57 | public: | ||
2523 | 58 | FullscreenWindowManagerPolicy(me::WindowManagerTools* const /*tools*/, std::shared_ptr<msh::DisplayLayout> const& display_layout) : | ||
2524 | 59 | display_layout{display_layout} {} | ||
2525 | 60 | |||
2526 | 61 | void handle_session_info_updated(SessionInfoMap& /*session_info*/, Rectangles const& /*displays*/) override {} | ||
2527 | 62 | |||
2528 | 63 | void handle_displays_updated(SessionInfoMap& /*session_info*/, Rectangles const& /*displays*/) override {} | ||
2529 | 64 | |||
2530 | 65 | auto handle_place_new_surface( | ||
2531 | 66 | std::shared_ptr<ms::Session> const& /*session*/, | ||
2532 | 67 | ms::SurfaceCreationParameters const& request_parameters) | ||
2533 | 68 | -> ms::SurfaceCreationParameters override | ||
2534 | 69 | { | ||
2535 | 70 | auto placed_parameters = request_parameters; | ||
2536 | 71 | |||
2537 | 72 | Rectangle rect{request_parameters.top_left, request_parameters.size}; | ||
2538 | 73 | display_layout->size_to_output(rect); | ||
2539 | 74 | placed_parameters.size = rect.size; | ||
2540 | 75 | |||
2541 | 76 | return placed_parameters; | ||
2542 | 77 | } | ||
2543 | 78 | void handle_modify_surface( | ||
2544 | 79 | std::shared_ptr<ms::Session> const& /*session*/, | ||
2545 | 80 | std::shared_ptr<ms::Surface> const& /*surface*/, | ||
2546 | 81 | msh::SurfaceSpecification const& /*modifications*/) override | ||
2547 | 82 | { | ||
2548 | 83 | } | ||
2549 | 84 | |||
2550 | 85 | void handle_new_surface(std::shared_ptr<ms::Session> const& /*session*/, std::shared_ptr<ms::Surface> const& /*surface*/) override | ||
2551 | 86 | { | ||
2552 | 87 | } | ||
2553 | 88 | |||
2554 | 89 | void handle_delete_surface(std::shared_ptr<ms::Session> const& session, std::weak_ptr<ms::Surface> const& surface) override | ||
2555 | 90 | { session->destroy_surface(surface); } | ||
2556 | 91 | |||
2557 | 92 | int handle_set_state(std::shared_ptr<ms::Surface> const& /*surface*/, MirWindowState value) override | ||
2558 | 93 | { return value; } | ||
2559 | 94 | |||
2560 | 95 | bool handle_keyboard_event(MirKeyboardEvent const* /*event*/) override { return false; } | ||
2561 | 96 | |||
2562 | 97 | bool handle_touch_event(MirTouchEvent const* /*event*/) override { return false; } | ||
2563 | 98 | |||
2564 | 99 | bool handle_pointer_event(MirPointerEvent const* /*event*/) override { return false; } | ||
2565 | 100 | |||
2566 | 101 | void handle_raise_surface( | ||
2567 | 102 | std::shared_ptr<ms::Session> const& /*session*/, | ||
2568 | 103 | std::shared_ptr<ms::Surface> const& /*surface*/) override | ||
2569 | 104 | { | ||
2570 | 105 | } | ||
2571 | 106 | |||
2572 | 107 | void generate_decorations_for( | ||
2573 | 108 | std::shared_ptr<ms::Session> const&, | ||
2574 | 109 | std::shared_ptr<ms::Surface> const&, | ||
2575 | 110 | SurfaceInfoMap&, | ||
2576 | 111 | std::function<mf::SurfaceId(std::shared_ptr<ms::Session> const&, ms::SurfaceCreationParameters const&)> const&) override | ||
2577 | 112 | { | ||
2578 | 113 | } | ||
2579 | 114 | private: | ||
2580 | 115 | std::shared_ptr<msh::DisplayLayout> const display_layout; | ||
2581 | 116 | }; | ||
2582 | 117 | |||
2583 | 118 | } | ||
2584 | 119 | |||
2585 | 120 | using FullscreenWindowManager = me::WindowManagerBuilder<FullscreenWindowManagerPolicy>; | ||
2586 | 121 | using CanonicalWindowManager = me::WindowManagerBuilder<me::CanonicalWindowManagerPolicyCopy>; | ||
2587 | 122 | |||
2588 | 123 | void me::add_window_manager_option_to(Server& server) | ||
2589 | 124 | { | ||
2590 | 125 | server.add_configuration_option(wm_option, wm_description, wm_canonical); | ||
2591 | 126 | |||
2592 | 127 | server.override_the_window_manager_builder([&server](msh::FocusController* focus_controller) | ||
2593 | 128 | -> std::shared_ptr<msh::WindowManager> | ||
2594 | 129 | { | ||
2595 | 130 | auto const options = server.get_options(); | ||
2596 | 131 | auto const selection = options->get<std::string>(wm_option); | ||
2597 | 132 | |||
2598 | 133 | if (selection == wm_fullscreen) | ||
2599 | 134 | { | ||
2600 | 135 | return std::make_shared<FullscreenWindowManager>(focus_controller, server.the_shell_display_layout()); | ||
2601 | 136 | } | ||
2602 | 137 | else if (selection == wm_canonical) | ||
2603 | 138 | { | ||
2604 | 139 | return std::make_shared<CanonicalWindowManager>( | ||
2605 | 140 | focus_controller, | ||
2606 | 141 | server.the_shell_display_layout(), | ||
2607 | 142 | server.the_graphics_platform()->create_buffer_allocator()); | ||
2608 | 143 | } | ||
2609 | 144 | else if (selection == wm_system_compositor) | ||
2610 | 145 | { | ||
2611 | 146 | return std::make_shared<msh::SystemCompositorWindowManager>( | ||
2612 | 147 | focus_controller, | ||
2613 | 148 | server.the_shell_display_layout(), | ||
2614 | 149 | server.the_session_coordinator()); | ||
2615 | 150 | } | ||
2616 | 151 | |||
2617 | 152 | throw mir::AbnormalExit("Unknown window manager: " + selection); | ||
2618 | 153 | }); | ||
2619 | 154 | } | ||
2620 | 155 | 0 | ||
2621 | === removed file 'examples/server_example_window_management.h' | |||
2622 | --- examples/server_example_window_management.h 2017-07-28 17:00:43 +0000 | |||
2623 | +++ examples/server_example_window_management.h 1970-01-01 00:00:00 +0000 | |||
2624 | @@ -1,33 +0,0 @@ | |||
2625 | 1 | /* | ||
2626 | 2 | * Copyright © 2014 Canonical Ltd. | ||
2627 | 3 | * | ||
2628 | 4 | * This program is free software: you can redistribute it and/or modify it | ||
2629 | 5 | * under the terms of the GNU General Public License version 2 or 3, | ||
2630 | 6 | * as published by the Free Software Foundation. | ||
2631 | 7 | * | ||
2632 | 8 | * This program is distributed in the hope that it will be useful, | ||
2633 | 9 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
2634 | 10 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
2635 | 11 | * GNU General Public License for more details. | ||
2636 | 12 | * | ||
2637 | 13 | * You should have received a copy of the GNU General Public License | ||
2638 | 14 | * along with this program. If not, see <http://www.gnu.org/licenses/>. | ||
2639 | 15 | * | ||
2640 | 16 | * Authored By: Alan Griffiths <alan@octopull.co.uk> | ||
2641 | 17 | */ | ||
2642 | 18 | |||
2643 | 19 | #ifndef MIR_EXAMPLES_WINDOW_MANAGEMENT_H_ | ||
2644 | 20 | #define MIR_EXAMPLES_WINDOW_MANAGEMENT_H_ | ||
2645 | 21 | |||
2646 | 22 | namespace mir | ||
2647 | 23 | { | ||
2648 | 24 | class Server; | ||
2649 | 25 | |||
2650 | 26 | namespace examples | ||
2651 | 27 | { | ||
2652 | 28 | void add_window_manager_option_to(Server& server); | ||
2653 | 29 | } | ||
2654 | 30 | } // namespace mir | ||
2655 | 31 | |||
2656 | 32 | |||
2657 | 33 | #endif // MIR_EXAMPLES_WINDOW_MANAGEMENT_H_ | ||
2658 | 34 | 0 | ||
2659 | === removed file 'examples/xcursor.c' | |||
2660 | --- examples/xcursor.c 2016-01-29 08:18:22 +0000 | |||
2661 | +++ examples/xcursor.c 1970-01-01 00:00:00 +0000 | |||
2662 | @@ -1,973 +0,0 @@ | |||
2663 | 1 | /* | ||
2664 | 2 | * Copyright © 2002 Keith Packard | ||
2665 | 3 | * | ||
2666 | 4 | * Permission to use, copy, modify, distribute, and sell this software and its | ||
2667 | 5 | * documentation for any purpose is hereby granted without fee, provided that | ||
2668 | 6 | * the above copyright notice appear in all copies and that both that | ||
2669 | 7 | * copyright notice and this permission notice appear in supporting | ||
2670 | 8 | * documentation, and that the name of Keith Packard not be used in | ||
2671 | 9 | * advertising or publicity pertaining to distribution of the software without | ||
2672 | 10 | * specific, written prior permission. Keith Packard makes no | ||
2673 | 11 | * representations about the suitability of this software for any purpose. It | ||
2674 | 12 | * is provided "as is" without express or implied warranty. | ||
2675 | 13 | * | ||
2676 | 14 | * KEITH PACKARD DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, | ||
2677 | 15 | * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO | ||
2678 | 16 | * EVENT SHALL KEITH PACKARD BE LIABLE FOR ANY SPECIAL, INDIRECT OR | ||
2679 | 17 | * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, | ||
2680 | 18 | * DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER | ||
2681 | 19 | * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR | ||
2682 | 20 | * PERFORMANCE OF THIS SOFTWARE. | ||
2683 | 21 | */ | ||
2684 | 22 | |||
2685 | 23 | #include "xcursor.h" | ||
2686 | 24 | #include <stdio.h> | ||
2687 | 25 | #include <stdlib.h> | ||
2688 | 26 | #include <string.h> | ||
2689 | 27 | #include <dirent.h> | ||
2690 | 28 | |||
2691 | 29 | /* | ||
2692 | 30 | * From libXcursor/include/X11/extensions/Xcursor.h | ||
2693 | 31 | */ | ||
2694 | 32 | |||
2695 | 33 | #define XcursorTrue 1 | ||
2696 | 34 | #define XcursorFalse 0 | ||
2697 | 35 | |||
2698 | 36 | /* | ||
2699 | 37 | * Cursor files start with a header. The header | ||
2700 | 38 | * contains a magic number, a version number and a | ||
2701 | 39 | * table of contents which has type and offset information | ||
2702 | 40 | * for the remaining tables in the file. | ||
2703 | 41 | * | ||
2704 | 42 | * File minor versions increment for compatible changes | ||
2705 | 43 | * File major versions increment for incompatible changes (never, we hope) | ||
2706 | 44 | * | ||
2707 | 45 | * Chunks of the same type are always upward compatible. Incompatible | ||
2708 | 46 | * changes are made with new chunk types; the old data can remain under | ||
2709 | 47 | * the old type. Upward compatible changes can add header data as the | ||
2710 | 48 | * header lengths are specified in the file. | ||
2711 | 49 | * | ||
2712 | 50 | * File: | ||
2713 | 51 | * FileHeader | ||
2714 | 52 | * LISTofChunk | ||
2715 | 53 | * | ||
2716 | 54 | * FileHeader: | ||
2717 | 55 | * CARD32 magic magic number | ||
2718 | 56 | * CARD32 header bytes in file header | ||
2719 | 57 | * CARD32 version file version | ||
2720 | 58 | * CARD32 ntoc number of toc entries | ||
2721 | 59 | * LISTofFileToc toc table of contents | ||
2722 | 60 | * | ||
2723 | 61 | * FileToc: | ||
2724 | 62 | * CARD32 type entry type | ||
2725 | 63 | * CARD32 subtype entry subtype (size for images) | ||
2726 | 64 | * CARD32 position absolute file position | ||
2727 | 65 | */ | ||
2728 | 66 | |||
2729 | 67 | #define XCURSOR_MAGIC 0x72756358 /* "Xcur" LSBFirst */ | ||
2730 | 68 | |||
2731 | 69 | /* | ||
2732 | 70 | * Current Xcursor version number. Will be substituted by configure | ||
2733 | 71 | * from the version in the libXcursor configure.ac file. | ||
2734 | 72 | */ | ||
2735 | 73 | |||
2736 | 74 | #define XCURSOR_LIB_MAJOR 1 | ||
2737 | 75 | #define XCURSOR_LIB_MINOR 1 | ||
2738 | 76 | #define XCURSOR_LIB_REVISION 13 | ||
2739 | 77 | #define XCURSOR_LIB_VERSION ((XCURSOR_LIB_MAJOR * 10000) + \ | ||
2740 | 78 | (XCURSOR_LIB_MINOR * 100) + \ | ||
2741 | 79 | (XCURSOR_LIB_REVISION)) | ||
2742 | 80 | |||
2743 | 81 | /* | ||
2744 | 82 | * This version number is stored in cursor files; changes to the | ||
2745 | 83 | * file format require updating this version number | ||
2746 | 84 | */ | ||
2747 | 85 | #define XCURSOR_FILE_MAJOR 1 | ||
2748 | 86 | #define XCURSOR_FILE_MINOR 0 | ||
2749 | 87 | #define XCURSOR_FILE_VERSION ((XCURSOR_FILE_MAJOR << 16) | (XCURSOR_FILE_MINOR)) | ||
2750 | 88 | #define XCURSOR_FILE_HEADER_LEN (4 * 4) | ||
2751 | 89 | #define XCURSOR_FILE_TOC_LEN (3 * 4) | ||
2752 | 90 | |||
2753 | 91 | typedef struct _XcursorFileToc { | ||
2754 | 92 | XcursorUInt type; /* chunk type */ | ||
2755 | 93 | XcursorUInt subtype; /* subtype (size for images) */ | ||
2756 | 94 | XcursorUInt position; /* absolute position in file */ | ||
2757 | 95 | } XcursorFileToc; | ||
2758 | 96 | |||
2759 | 97 | typedef struct _XcursorFileHeader { | ||
2760 | 98 | XcursorUInt magic; /* magic number */ | ||
2761 | 99 | XcursorUInt header; /* byte length of header */ | ||
2762 | 100 | XcursorUInt version; /* file version number */ | ||
2763 | 101 | XcursorUInt ntoc; /* number of toc entries */ | ||
2764 | 102 | XcursorFileToc *tocs; /* table of contents */ | ||
2765 | 103 | } XcursorFileHeader; | ||
2766 | 104 | |||
2767 | 105 | /* | ||
2768 | 106 | * The rest of the file is a list of chunks, each tagged by type | ||
2769 | 107 | * and version. | ||
2770 | 108 | * | ||
2771 | 109 | * Chunk: | ||
2772 | 110 | * ChunkHeader | ||
2773 | 111 | * <extra type-specific header fields> | ||
2774 | 112 | * <type-specific data> | ||
2775 | 113 | * | ||
2776 | 114 | * ChunkHeader: | ||
2777 | 115 | * CARD32 header bytes in chunk header + type header | ||
2778 | 116 | * CARD32 type chunk type | ||
2779 | 117 | * CARD32 subtype chunk subtype | ||
2780 | 118 | * CARD32 version chunk type version | ||
2781 | 119 | */ | ||
2782 | 120 | |||
2783 | 121 | #define XCURSOR_CHUNK_HEADER_LEN (4 * 4) | ||
2784 | 122 | |||
2785 | 123 | typedef struct _XcursorChunkHeader { | ||
2786 | 124 | XcursorUInt header; /* bytes in chunk header */ | ||
2787 | 125 | XcursorUInt type; /* chunk type */ | ||
2788 | 126 | XcursorUInt subtype; /* chunk subtype (size for images) */ | ||
2789 | 127 | XcursorUInt version; /* version of this type */ | ||
2790 | 128 | } XcursorChunkHeader; | ||
2791 | 129 | |||
2792 | 130 | /* | ||
2793 | 131 | * Here's a list of the known chunk types | ||
2794 | 132 | */ | ||
2795 | 133 | |||
2796 | 134 | /* | ||
2797 | 135 | * Comments consist of a 4-byte length field followed by | ||
2798 | 136 | * UTF-8 encoded text | ||
2799 | 137 | * | ||
2800 | 138 | * Comment: | ||
2801 | 139 | * ChunkHeader header chunk header | ||
2802 | 140 | * CARD32 length bytes in text | ||
2803 | 141 | * LISTofCARD8 text UTF-8 encoded text | ||
2804 | 142 | */ | ||
2805 | 143 | |||
2806 | 144 | #define XCURSOR_COMMENT_TYPE 0xfffe0001 | ||
2807 | 145 | #define XCURSOR_COMMENT_VERSION 1 | ||
2808 | 146 | #define XCURSOR_COMMENT_HEADER_LEN (XCURSOR_CHUNK_HEADER_LEN + (1 *4)) | ||
2809 | 147 | #define XCURSOR_COMMENT_COPYRIGHT 1 | ||
2810 | 148 | #define XCURSOR_COMMENT_LICENSE 2 | ||
2811 | 149 | #define XCURSOR_COMMENT_OTHER 3 | ||
2812 | 150 | #define XCURSOR_COMMENT_MAX_LEN 0x100000 | ||
2813 | 151 | |||
2814 | 152 | typedef struct _XcursorComment { | ||
2815 | 153 | XcursorUInt version; | ||
2816 | 154 | XcursorUInt comment_type; | ||
2817 | 155 | char *comment; | ||
2818 | 156 | } XcursorComment; | ||
2819 | 157 | |||
2820 | 158 | /* | ||
2821 | 159 | * Each cursor image occupies a separate image chunk. | ||
2822 | 160 | * The length of the image header follows the chunk header | ||
2823 | 161 | * so that future versions can extend the header without | ||
2824 | 162 | * breaking older applications | ||
2825 | 163 | * | ||
2826 | 164 | * Image: | ||
2827 | 165 | * ChunkHeader header chunk header | ||
2828 | 166 | * CARD32 width actual width | ||
2829 | 167 | * CARD32 height actual height | ||
2830 | 168 | * CARD32 xhot hot spot x | ||
2831 | 169 | * CARD32 yhot hot spot y | ||
2832 | 170 | * CARD32 delay animation delay | ||
2833 | 171 | * LISTofCARD32 pixels ARGB pixels | ||
2834 | 172 | */ | ||
2835 | 173 | |||
2836 | 174 | #define XCURSOR_IMAGE_TYPE 0xfffd0002 | ||
2837 | 175 | #define XCURSOR_IMAGE_VERSION 1 | ||
2838 | 176 | #define XCURSOR_IMAGE_HEADER_LEN (XCURSOR_CHUNK_HEADER_LEN + (5*4)) | ||
2839 | 177 | #define XCURSOR_IMAGE_MAX_SIZE 0x7fff /* 32767x32767 max cursor size */ | ||
2840 | 178 | |||
2841 | 179 | typedef struct _XcursorFile XcursorFile; | ||
2842 | 180 | |||
2843 | 181 | struct _XcursorFile { | ||
2844 | 182 | void *closure; | ||
2845 | 183 | int (*read) (XcursorFile *file, unsigned char *buf, int len); | ||
2846 | 184 | int (*write) (XcursorFile *file, unsigned char *buf, int len); | ||
2847 | 185 | int (*seek) (XcursorFile *file, long offset, int whence); | ||
2848 | 186 | }; | ||
2849 | 187 | |||
2850 | 188 | typedef struct _XcursorComments { | ||
2851 | 189 | int ncomment; /* number of comments */ | ||
2852 | 190 | XcursorComment **comments; /* array of XcursorComment pointers */ | ||
2853 | 191 | } XcursorComments; | ||
2854 | 192 | |||
2855 | 193 | /* | ||
2856 | 194 | * From libXcursor/src/file.c | ||
2857 | 195 | */ | ||
2858 | 196 | |||
2859 | 197 | static XcursorImage * | ||
2860 | 198 | XcursorImageCreate (int width, int height) | ||
2861 | 199 | { | ||
2862 | 200 | XcursorImage *image; | ||
2863 | 201 | |||
2864 | 202 | image = malloc (sizeof (XcursorImage) + | ||
2865 | 203 | width * height * sizeof (XcursorPixel)); | ||
2866 | 204 | if (!image) | ||
2867 | 205 | return NULL; | ||
2868 | 206 | image->version = XCURSOR_IMAGE_VERSION; | ||
2869 | 207 | image->pixels = (XcursorPixel *) (image + 1); | ||
2870 | 208 | image->size = width > height ? width : height; | ||
2871 | 209 | image->width = width; | ||
2872 | 210 | image->height = height; | ||
2873 | 211 | image->delay = 0; | ||
2874 | 212 | return image; | ||
2875 | 213 | } | ||
2876 | 214 | |||
2877 | 215 | static void | ||
2878 | 216 | XcursorImageDestroy (XcursorImage *image) | ||
2879 | 217 | { | ||
2880 | 218 | free (image); | ||
2881 | 219 | } | ||
2882 | 220 | |||
2883 | 221 | static XcursorImages * | ||
2884 | 222 | XcursorImagesCreate (int size) | ||
2885 | 223 | { | ||
2886 | 224 | XcursorImages *images; | ||
2887 | 225 | |||
2888 | 226 | images = malloc (sizeof (XcursorImages) + | ||
2889 | 227 | size * sizeof (XcursorImage *)); | ||
2890 | 228 | if (!images) | ||
2891 | 229 | return NULL; | ||
2892 | 230 | images->nimage = 0; | ||
2893 | 231 | images->images = (XcursorImage **) (images + 1); | ||
2894 | 232 | images->name = NULL; | ||
2895 | 233 | return images; | ||
2896 | 234 | } | ||
2897 | 235 | |||
2898 | 236 | void | ||
2899 | 237 | XcursorImagesDestroy (XcursorImages *images) | ||
2900 | 238 | { | ||
2901 | 239 | int n; | ||
2902 | 240 | |||
2903 | 241 | if (!images) | ||
2904 | 242 | return; | ||
2905 | 243 | |||
2906 | 244 | for (n = 0; n < images->nimage; n++) | ||
2907 | 245 | XcursorImageDestroy (images->images[n]); | ||
2908 | 246 | if (images->name) | ||
2909 | 247 | free (images->name); | ||
2910 | 248 | free (images); | ||
2911 | 249 | } | ||
2912 | 250 | |||
2913 | 251 | static void | ||
2914 | 252 | XcursorImagesSetName (XcursorImages *images, const char *name) | ||
2915 | 253 | { | ||
2916 | 254 | char *new; | ||
2917 | 255 | |||
2918 | 256 | if (!images || !name) | ||
2919 | 257 | return; | ||
2920 | 258 | |||
2921 | 259 | new = malloc (strlen (name) + 1); | ||
2922 | 260 | |||
2923 | 261 | if (!new) | ||
2924 | 262 | return; | ||
2925 | 263 | |||
2926 | 264 | strcpy (new, name); | ||
2927 | 265 | if (images->name) | ||
2928 | 266 | free (images->name); | ||
2929 | 267 | images->name = new; | ||
2930 | 268 | } | ||
2931 | 269 | |||
2932 | 270 | static XcursorBool | ||
2933 | 271 | _XcursorReadUInt (XcursorFile *file, XcursorUInt *u) | ||
2934 | 272 | { | ||
2935 | 273 | unsigned char bytes[4]; | ||
2936 | 274 | |||
2937 | 275 | if (!file || !u) | ||
2938 | 276 | return XcursorFalse; | ||
2939 | 277 | |||
2940 | 278 | if ((*file->read) (file, bytes, 4) != 4) | ||
2941 | 279 | return XcursorFalse; | ||
2942 | 280 | *u = (((XcursorUInt)bytes[0] << 0) | | ||
2943 | 281 | ((XcursorUInt)bytes[1] << 8) | | ||
2944 | 282 | ((XcursorUInt)bytes[2] << 16) | | ||
2945 | 283 | ((XcursorUInt)bytes[3] << 24)); | ||
2946 | 284 | return XcursorTrue; | ||
2947 | 285 | } | ||
2948 | 286 | |||
2949 | 287 | static void | ||
2950 | 288 | _XcursorFileHeaderDestroy (XcursorFileHeader *fileHeader) | ||
2951 | 289 | { | ||
2952 | 290 | free (fileHeader); | ||
2953 | 291 | } | ||
2954 | 292 | |||
2955 | 293 | static XcursorFileHeader * | ||
2956 | 294 | _XcursorFileHeaderCreate (int ntoc) | ||
2957 | 295 | { | ||
2958 | 296 | XcursorFileHeader *fileHeader; | ||
2959 | 297 | |||
2960 | 298 | if (ntoc > 0x10000) | ||
2961 | 299 | return NULL; | ||
2962 | 300 | fileHeader = malloc (sizeof (XcursorFileHeader) + | ||
2963 | 301 | ntoc * sizeof (XcursorFileToc)); | ||
2964 | 302 | if (!fileHeader) | ||
2965 | 303 | return NULL; | ||
2966 | 304 | fileHeader->magic = XCURSOR_MAGIC; | ||
2967 | 305 | fileHeader->header = XCURSOR_FILE_HEADER_LEN; | ||
2968 | 306 | fileHeader->version = XCURSOR_FILE_VERSION; | ||
2969 | 307 | fileHeader->ntoc = ntoc; | ||
2970 | 308 | fileHeader->tocs = (XcursorFileToc *) (fileHeader + 1); | ||
2971 | 309 | return fileHeader; | ||
2972 | 310 | } | ||
2973 | 311 | |||
2974 | 312 | static XcursorFileHeader * | ||
2975 | 313 | _XcursorReadFileHeader (XcursorFile *file) | ||
2976 | 314 | { | ||
2977 | 315 | XcursorFileHeader head, *fileHeader; | ||
2978 | 316 | XcursorUInt skip; | ||
2979 | 317 | unsigned int n; | ||
2980 | 318 | |||
2981 | 319 | if (!file) | ||
2982 | 320 | return NULL; | ||
2983 | 321 | |||
2984 | 322 | if (!_XcursorReadUInt (file, &head.magic)) | ||
2985 | 323 | return NULL; | ||
2986 | 324 | if (head.magic != XCURSOR_MAGIC) | ||
2987 | 325 | return NULL; | ||
2988 | 326 | if (!_XcursorReadUInt (file, &head.header)) | ||
2989 | 327 | return NULL; | ||
2990 | 328 | if (!_XcursorReadUInt (file, &head.version)) | ||
2991 | 329 | return NULL; | ||
2992 | 330 | if (!_XcursorReadUInt (file, &head.ntoc)) | ||
2993 | 331 | return NULL; | ||
2994 | 332 | skip = head.header - XCURSOR_FILE_HEADER_LEN; | ||
2995 | 333 | if (skip) | ||
2996 | 334 | if ((*file->seek) (file, skip, SEEK_CUR) == EOF) | ||
2997 | 335 | return NULL; | ||
2998 | 336 | fileHeader = _XcursorFileHeaderCreate (head.ntoc); | ||
2999 | 337 | if (!fileHeader) | ||
3000 | 338 | return NULL; | ||
3001 | 339 | fileHeader->magic = head.magic; | ||
3002 | 340 | fileHeader->header = head.header; | ||
3003 | 341 | fileHeader->version = head.version; | ||
3004 | 342 | fileHeader->ntoc = head.ntoc; | ||
3005 | 343 | for (n = 0; n < fileHeader->ntoc; n++) | ||
3006 | 344 | { | ||
3007 | 345 | if (!_XcursorReadUInt (file, &fileHeader->tocs[n].type)) | ||
3008 | 346 | break; | ||
3009 | 347 | if (!_XcursorReadUInt (file, &fileHeader->tocs[n].subtype)) | ||
3010 | 348 | break; | ||
3011 | 349 | if (!_XcursorReadUInt (file, &fileHeader->tocs[n].position)) | ||
3012 | 350 | break; | ||
3013 | 351 | } | ||
3014 | 352 | if (n != fileHeader->ntoc) | ||
3015 | 353 | { | ||
3016 | 354 | _XcursorFileHeaderDestroy (fileHeader); | ||
3017 | 355 | return NULL; | ||
3018 | 356 | } | ||
3019 | 357 | return fileHeader; | ||
3020 | 358 | } | ||
3021 | 359 | |||
3022 | 360 | static XcursorBool | ||
3023 | 361 | _XcursorSeekToToc (XcursorFile *file, | ||
3024 | 362 | XcursorFileHeader *fileHeader, | ||
3025 | 363 | int toc) | ||
3026 | 364 | { | ||
3027 | 365 | if (!file || !fileHeader || \ | ||
3028 | 366 | (*file->seek) (file, fileHeader->tocs[toc].position, SEEK_SET) == EOF) | ||
3029 | 367 | return XcursorFalse; | ||
3030 | 368 | return XcursorTrue; | ||
3031 | 369 | } | ||
3032 | 370 | |||
3033 | 371 | static XcursorBool | ||
3034 | 372 | _XcursorFileReadChunkHeader (XcursorFile *file, | ||
3035 | 373 | XcursorFileHeader *fileHeader, | ||
3036 | 374 | int toc, | ||
3037 | 375 | XcursorChunkHeader *chunkHeader) | ||
3038 | 376 | { | ||
3039 | 377 | if (!file || !fileHeader || !chunkHeader) | ||
3040 | 378 | return XcursorFalse; | ||
3041 | 379 | if (!_XcursorSeekToToc (file, fileHeader, toc)) | ||
3042 | 380 | return XcursorFalse; | ||
3043 | 381 | if (!_XcursorReadUInt (file, &chunkHeader->header)) | ||
3044 | 382 | return XcursorFalse; | ||
3045 | 383 | if (!_XcursorReadUInt (file, &chunkHeader->type)) | ||
3046 | 384 | return XcursorFalse; | ||
3047 | 385 | if (!_XcursorReadUInt (file, &chunkHeader->subtype)) | ||
3048 | 386 | return XcursorFalse; | ||
3049 | 387 | if (!_XcursorReadUInt (file, &chunkHeader->version)) | ||
3050 | 388 | return XcursorFalse; | ||
3051 | 389 | /* sanity check */ | ||
3052 | 390 | if (chunkHeader->type != fileHeader->tocs[toc].type || | ||
3053 | 391 | chunkHeader->subtype != fileHeader->tocs[toc].subtype) | ||
3054 | 392 | return XcursorFalse; | ||
3055 | 393 | return XcursorTrue; | ||
3056 | 394 | } | ||
3057 | 395 | |||
3058 | 396 | #define dist(a,b) ((a) > (b) ? (a) - (b) : (b) - (a)) | ||
3059 | 397 | |||
3060 | 398 | static XcursorDim | ||
3061 | 399 | _XcursorFindBestSize (XcursorFileHeader *fileHeader, | ||
3062 | 400 | XcursorDim size, | ||
3063 | 401 | int *nsizesp) | ||
3064 | 402 | { | ||
3065 | 403 | unsigned int n; | ||
3066 | 404 | int nsizes = 0; | ||
3067 | 405 | XcursorDim bestSize = 0; | ||
3068 | 406 | XcursorDim thisSize; | ||
3069 | 407 | |||
3070 | 408 | if (!fileHeader || !nsizesp) | ||
3071 | 409 | return 0; | ||
3072 | 410 | |||
3073 | 411 | for (n = 0; n < fileHeader->ntoc; n++) | ||
3074 | 412 | { | ||
3075 | 413 | if (fileHeader->tocs[n].type != XCURSOR_IMAGE_TYPE) | ||
3076 | 414 | continue; | ||
3077 | 415 | thisSize = fileHeader->tocs[n].subtype; | ||
3078 | 416 | if (!bestSize || dist (thisSize, size) < dist (bestSize, size)) | ||
3079 | 417 | { | ||
3080 | 418 | bestSize = thisSize; | ||
3081 | 419 | nsizes = 1; | ||
3082 | 420 | } | ||
3083 | 421 | else if (thisSize == bestSize) | ||
3084 | 422 | nsizes++; | ||
3085 | 423 | } | ||
3086 | 424 | *nsizesp = nsizes; | ||
3087 | 425 | return bestSize; | ||
3088 | 426 | } | ||
3089 | 427 | |||
3090 | 428 | static int | ||
3091 | 429 | _XcursorFindImageToc (XcursorFileHeader *fileHeader, | ||
3092 | 430 | XcursorDim size, | ||
3093 | 431 | int count) | ||
3094 | 432 | { | ||
3095 | 433 | unsigned int toc; | ||
3096 | 434 | XcursorDim thisSize; | ||
3097 | 435 | |||
3098 | 436 | if (!fileHeader) | ||
3099 | 437 | return 0; | ||
3100 | 438 | |||
3101 | 439 | for (toc = 0; toc < fileHeader->ntoc; toc++) | ||
3102 | 440 | { | ||
3103 | 441 | if (fileHeader->tocs[toc].type != XCURSOR_IMAGE_TYPE) | ||
3104 | 442 | continue; | ||
3105 | 443 | thisSize = fileHeader->tocs[toc].subtype; | ||
3106 | 444 | if (thisSize != size) | ||
3107 | 445 | continue; | ||
3108 | 446 | if (!count) | ||
3109 | 447 | break; | ||
3110 | 448 | count--; | ||
3111 | 449 | } | ||
3112 | 450 | if (toc == fileHeader->ntoc) | ||
3113 | 451 | return -1; | ||
3114 | 452 | return toc; | ||
3115 | 453 | } | ||
3116 | 454 | |||
3117 | 455 | static XcursorImage * | ||
3118 | 456 | _XcursorReadImage (XcursorFile *file, | ||
3119 | 457 | XcursorFileHeader *fileHeader, | ||
3120 | 458 | int toc) | ||
3121 | 459 | { | ||
3122 | 460 | XcursorChunkHeader chunkHeader; | ||
3123 | 461 | XcursorImage head; | ||
3124 | 462 | XcursorImage *image; | ||
3125 | 463 | int n; | ||
3126 | 464 | XcursorPixel *p; | ||
3127 | 465 | |||
3128 | 466 | if (!file || !fileHeader) | ||
3129 | 467 | return NULL; | ||
3130 | 468 | |||
3131 | 469 | if (!_XcursorFileReadChunkHeader (file, fileHeader, toc, &chunkHeader)) | ||
3132 | 470 | return NULL; | ||
3133 | 471 | if (!_XcursorReadUInt (file, &head.width)) | ||
3134 | 472 | return NULL; | ||
3135 | 473 | if (!_XcursorReadUInt (file, &head.height)) | ||
3136 | 474 | return NULL; | ||
3137 | 475 | if (!_XcursorReadUInt (file, &head.xhot)) | ||
3138 | 476 | return NULL; | ||
3139 | 477 | if (!_XcursorReadUInt (file, &head.yhot)) | ||
3140 | 478 | return NULL; | ||
3141 | 479 | if (!_XcursorReadUInt (file, &head.delay)) | ||
3142 | 480 | return NULL; | ||
3143 | 481 | /* sanity check data */ | ||
3144 | 482 | if (head.width >= 0x10000 || head.height > 0x10000) | ||
3145 | 483 | return NULL; | ||
3146 | 484 | if (head.width == 0 || head.height == 0) | ||
3147 | 485 | return NULL; | ||
3148 | 486 | if (head.xhot > head.width || head.yhot > head.height) | ||
3149 | 487 | return NULL; | ||
3150 | 488 | |||
3151 | 489 | /* Create the image and initialize it */ | ||
3152 | 490 | image = XcursorImageCreate (head.width, head.height); | ||
3153 | 491 | if (image == NULL) | ||
3154 | 492 | return NULL; | ||
3155 | 493 | if (chunkHeader.version < image->version) | ||
3156 | 494 | image->version = chunkHeader.version; | ||
3157 | 495 | image->size = chunkHeader.subtype; | ||
3158 | 496 | image->xhot = head.xhot; | ||
3159 | 497 | image->yhot = head.yhot; | ||
3160 | 498 | image->delay = head.delay; | ||
3161 | 499 | n = image->width * image->height; | ||
3162 | 500 | p = image->pixels; | ||
3163 | 501 | while (n--) | ||
3164 | 502 | { | ||
3165 | 503 | if (!_XcursorReadUInt (file, p)) | ||
3166 | 504 | { | ||
3167 | 505 | XcursorImageDestroy (image); | ||
3168 | 506 | return NULL; | ||
3169 | 507 | } | ||
3170 | 508 | p++; | ||
3171 | 509 | } | ||
3172 | 510 | return image; | ||
3173 | 511 | } | ||
3174 | 512 | |||
3175 | 513 | static XcursorImages * | ||
3176 | 514 | XcursorXcFileLoadImages (XcursorFile *file, int size) | ||
3177 | 515 | { | ||
3178 | 516 | XcursorFileHeader *fileHeader; | ||
3179 | 517 | XcursorDim bestSize; | ||
3180 | 518 | int nsize; | ||
3181 | 519 | XcursorImages *images; | ||
3182 | 520 | int n; | ||
3183 | 521 | int toc; | ||
3184 | 522 | |||
3185 | 523 | if (!file || size < 0) | ||
3186 | 524 | return NULL; | ||
3187 | 525 | fileHeader = _XcursorReadFileHeader (file); | ||
3188 | 526 | if (!fileHeader) | ||
3189 | 527 | return NULL; | ||
3190 | 528 | bestSize = _XcursorFindBestSize (fileHeader, (XcursorDim) size, &nsize); | ||
3191 | 529 | if (!bestSize) | ||
3192 | 530 | { | ||
3193 | 531 | _XcursorFileHeaderDestroy (fileHeader); | ||
3194 | 532 | return NULL; | ||
3195 | 533 | } | ||
3196 | 534 | images = XcursorImagesCreate (nsize); | ||
3197 | 535 | if (!images) | ||
3198 | 536 | { | ||
3199 | 537 | _XcursorFileHeaderDestroy (fileHeader); | ||
3200 | 538 | return NULL; | ||
3201 | 539 | } | ||
3202 | 540 | for (n = 0; n < nsize; n++) | ||
3203 | 541 | { | ||
3204 | 542 | toc = _XcursorFindImageToc (fileHeader, bestSize, n); | ||
3205 | 543 | if (toc < 0) | ||
3206 | 544 | break; | ||
3207 | 545 | images->images[images->nimage] = _XcursorReadImage (file, fileHeader, | ||
3208 | 546 | toc); | ||
3209 | 547 | if (!images->images[images->nimage]) | ||
3210 | 548 | break; | ||
3211 | 549 | images->nimage++; | ||
3212 | 550 | } | ||
3213 | 551 | _XcursorFileHeaderDestroy (fileHeader); | ||
3214 | 552 | if (images->nimage != nsize) | ||
3215 | 553 | { | ||
3216 | 554 | XcursorImagesDestroy (images); | ||
3217 | 555 | images = NULL; | ||
3218 | 556 | } | ||
3219 | 557 | return images; | ||
3220 | 558 | } | ||
3221 | 559 | |||
3222 | 560 | static int | ||
3223 | 561 | _XcursorStdioFileRead (XcursorFile *file, unsigned char *buf, int len) | ||
3224 | 562 | { | ||
3225 | 563 | FILE *f = file->closure; | ||
3226 | 564 | return fread (buf, 1, len, f); | ||
3227 | 565 | } | ||
3228 | 566 | |||
3229 | 567 | static int | ||
3230 | 568 | _XcursorStdioFileWrite (XcursorFile *file, unsigned char *buf, int len) | ||
3231 | 569 | { | ||
3232 | 570 | FILE *f = file->closure; | ||
3233 | 571 | return fwrite (buf, 1, len, f); | ||
3234 | 572 | } | ||
3235 | 573 | |||
3236 | 574 | static int | ||
3237 | 575 | _XcursorStdioFileSeek (XcursorFile *file, long offset, int whence) | ||
3238 | 576 | { | ||
3239 | 577 | FILE *f = file->closure; | ||
3240 | 578 | return fseek (f, offset, whence); | ||
3241 | 579 | } | ||
3242 | 580 | |||
3243 | 581 | static void | ||
3244 | 582 | _XcursorStdioFileInitialize (FILE *stdfile, XcursorFile *file) | ||
3245 | 583 | { | ||
3246 | 584 | file->closure = stdfile; | ||
3247 | 585 | file->read = _XcursorStdioFileRead; | ||
3248 | 586 | file->write = _XcursorStdioFileWrite; | ||
3249 | 587 | file->seek = _XcursorStdioFileSeek; | ||
3250 | 588 | } | ||
3251 | 589 | |||
3252 | 590 | static XcursorImages * | ||
3253 | 591 | XcursorFileLoadImages (FILE *file, int size) | ||
3254 | 592 | { | ||
3255 | 593 | XcursorFile f; | ||
3256 | 594 | |||
3257 | 595 | if (!file) | ||
3258 | 596 | return NULL; | ||
3259 | 597 | |||
3260 | 598 | _XcursorStdioFileInitialize (file, &f); | ||
3261 | 599 | return XcursorXcFileLoadImages (&f, size); | ||
3262 | 600 | } | ||
3263 | 601 | |||
3264 | 602 | /* | ||
3265 | 603 | * From libXcursor/src/library.c | ||
3266 | 604 | */ | ||
3267 | 605 | |||
3268 | 606 | #ifndef ICONDIR | ||
3269 | 607 | #define ICONDIR "/usr/X11R6/lib/X11/icons" | ||
3270 | 608 | #endif | ||
3271 | 609 | |||
3272 | 610 | #ifndef XCURSORPATH | ||
3273 | 611 | #define XCURSORPATH "~/.icons:/usr/share/icons:/usr/share/pixmaps:~/.cursors:/usr/share/cursors/xorg-x11:"ICONDIR | ||
3274 | 612 | #endif | ||
3275 | 613 | |||
3276 | 614 | static const char * | ||
3277 | 615 | XcursorLibraryPath (void) | ||
3278 | 616 | { | ||
3279 | 617 | static const char *path; | ||
3280 | 618 | |||
3281 | 619 | if (!path) | ||
3282 | 620 | { | ||
3283 | 621 | path = getenv ("XCURSOR_PATH"); | ||
3284 | 622 | if (!path) | ||
3285 | 623 | path = XCURSORPATH; | ||
3286 | 624 | } | ||
3287 | 625 | return path; | ||
3288 | 626 | } | ||
3289 | 627 | |||
3290 | 628 | static void | ||
3291 | 629 | _XcursorAddPathElt (char *path, const char *elt, int len) | ||
3292 | 630 | { | ||
3293 | 631 | int pathlen = strlen (path); | ||
3294 | 632 | |||
3295 | 633 | /* append / if the path doesn't currently have one */ | ||
3296 | 634 | if (path[0] == '\0' || path[pathlen - 1] != '/') | ||
3297 | 635 | { | ||
3298 | 636 | strcat (path, "/"); | ||
3299 | 637 | pathlen++; | ||
3300 | 638 | } | ||
3301 | 639 | if (len == -1) | ||
3302 | 640 | len = strlen (elt); | ||
3303 | 641 | /* strip leading slashes */ | ||
3304 | 642 | while (len && elt[0] == '/') | ||
3305 | 643 | { | ||
3306 | 644 | elt++; | ||
3307 | 645 | len--; | ||
3308 | 646 | } | ||
3309 | 647 | strncpy (path + pathlen, elt, len); | ||
3310 | 648 | path[pathlen + len] = '\0'; | ||
3311 | 649 | } | ||
3312 | 650 | |||
3313 | 651 | static char * | ||
3314 | 652 | _XcursorBuildThemeDir (const char *dir, const char *theme) | ||
3315 | 653 | { | ||
3316 | 654 | const char *colon; | ||
3317 | 655 | const char *tcolon; | ||
3318 | 656 | char *full; | ||
3319 | 657 | char *home; | ||
3320 | 658 | int dirlen; | ||
3321 | 659 | int homelen; | ||
3322 | 660 | int themelen; | ||
3323 | 661 | int len; | ||
3324 | 662 | |||
3325 | 663 | if (!dir || !theme) | ||
3326 | 664 | return NULL; | ||
3327 | 665 | |||
3328 | 666 | colon = strchr (dir, ':'); | ||
3329 | 667 | if (!colon) | ||
3330 | 668 | colon = dir + strlen (dir); | ||
3331 | 669 | |||
3332 | 670 | dirlen = colon - dir; | ||
3333 | 671 | |||
3334 | 672 | tcolon = strchr (theme, ':'); | ||
3335 | 673 | if (!tcolon) | ||
3336 | 674 | tcolon = theme + strlen (theme); | ||
3337 | 675 | |||
3338 | 676 | themelen = tcolon - theme; | ||
3339 | 677 | |||
3340 | 678 | home = NULL; | ||
3341 | 679 | homelen = 0; | ||
3342 | 680 | if (*dir == '~') | ||
3343 | 681 | { | ||
3344 | 682 | home = getenv ("HOME"); | ||
3345 | 683 | if (!home) | ||
3346 | 684 | return NULL; | ||
3347 | 685 | homelen = strlen (home); | ||
3348 | 686 | dir++; | ||
3349 | 687 | dirlen--; | ||
3350 | 688 | } | ||
3351 | 689 | |||
3352 | 690 | /* | ||
3353 | 691 | * add space for any needed directory separators, one per component, | ||
3354 | 692 | * and one for the trailing null | ||
3355 | 693 | */ | ||
3356 | 694 | len = 1 + homelen + 1 + dirlen + 1 + themelen + 1; | ||
3357 | 695 | |||
3358 | 696 | full = malloc (len); | ||
3359 | 697 | if (!full) | ||
3360 | 698 | return NULL; | ||
3361 | 699 | full[0] = '\0'; | ||
3362 | 700 | |||
3363 | 701 | if (home) | ||
3364 | 702 | _XcursorAddPathElt (full, home, -1); | ||
3365 | 703 | _XcursorAddPathElt (full, dir, dirlen); | ||
3366 | 704 | _XcursorAddPathElt (full, theme, themelen); | ||
3367 | 705 | return full; | ||
3368 | 706 | } | ||
3369 | 707 | |||
3370 | 708 | static char * | ||
3371 | 709 | _XcursorBuildFullname (const char *dir, const char *subdir, const char *file) | ||
3372 | 710 | { | ||
3373 | 711 | char *full; | ||
3374 | 712 | |||
3375 | 713 | if (!dir || !subdir || !file) | ||
3376 | 714 | return NULL; | ||
3377 | 715 | |||
3378 | 716 | /* | ||
3379 | 717 | * Following the g++5 transition the strlen() in _XcursorAddPathElt() can | ||
3380 | 718 | * trigger valgrind. We add some padding as a workaround. | ||
3381 | 719 | */ | ||
3382 | 720 | size_t padding = 4; | ||
3383 | 721 | full = malloc (padding + strlen (dir) + 1 + strlen (subdir) + 1 + strlen (file) + 1); | ||
3384 | 722 | if (!full) | ||
3385 | 723 | return NULL; | ||
3386 | 724 | full[0] = '\0'; | ||
3387 | 725 | _XcursorAddPathElt (full, dir, -1); | ||
3388 | 726 | _XcursorAddPathElt (full, subdir, -1); | ||
3389 | 727 | _XcursorAddPathElt (full, file, -1); | ||
3390 | 728 | return full; | ||
3391 | 729 | } | ||
3392 | 730 | |||
3393 | 731 | static const char * | ||
3394 | 732 | _XcursorNextPath (const char *path) | ||
3395 | 733 | { | ||
3396 | 734 | char *colon = strchr (path, ':'); | ||
3397 | 735 | |||
3398 | 736 | if (!colon) | ||
3399 | 737 | return NULL; | ||
3400 | 738 | return colon + 1; | ||
3401 | 739 | } | ||
3402 | 740 | |||
3403 | 741 | #define XcursorWhite(c) ((c) == ' ' || (c) == '\t' || (c) == '\n') | ||
3404 | 742 | #define XcursorSep(c) ((c) == ';' || (c) == ',') | ||
3405 | 743 | |||
3406 | 744 | static char * | ||
3407 | 745 | _XcursorThemeInherits (const char *full) | ||
3408 | 746 | { | ||
3409 | 747 | char line[8192]; | ||
3410 | 748 | char *result = NULL; | ||
3411 | 749 | FILE *f; | ||
3412 | 750 | |||
3413 | 751 | if (!full) | ||
3414 | 752 | return NULL; | ||
3415 | 753 | |||
3416 | 754 | f = fopen (full, "r"); | ||
3417 | 755 | if (f) | ||
3418 | 756 | { | ||
3419 | 757 | while (fgets (line, sizeof (line), f)) | ||
3420 | 758 | { | ||
3421 | 759 | if (!strncmp (line, "Inherits", 8)) | ||
3422 | 760 | { | ||
3423 | 761 | char *l = line + 8; | ||
3424 | 762 | char *r; | ||
3425 | 763 | while (*l == ' ') l++; | ||
3426 | 764 | if (*l != '=') continue; | ||
3427 | 765 | l++; | ||
3428 | 766 | while (*l == ' ') l++; | ||
3429 | 767 | result = malloc (strlen (l) + 1); | ||
3430 | 768 | if (result) | ||
3431 | 769 | { | ||
3432 | 770 | r = result; | ||
3433 | 771 | while (*l) | ||
3434 | 772 | { | ||
3435 | 773 | while (XcursorSep(*l) || XcursorWhite (*l)) l++; | ||
3436 | 774 | if (!*l) | ||
3437 | 775 | break; | ||
3438 | 776 | if (r != result) | ||
3439 | 777 | *r++ = ':'; | ||
3440 | 778 | while (*l && !XcursorWhite(*l) && | ||
3441 | 779 | !XcursorSep(*l)) | ||
3442 | 780 | *r++ = *l++; | ||
3443 | 781 | } | ||
3444 | 782 | *r++ = '\0'; | ||
3445 | 783 | } | ||
3446 | 784 | break; | ||
3447 | 785 | } | ||
3448 | 786 | } | ||
3449 | 787 | fclose (f); | ||
3450 | 788 | } | ||
3451 | 789 | return result; | ||
3452 | 790 | } | ||
3453 | 791 | |||
3454 | 792 | static FILE * | ||
3455 | 793 | XcursorScanTheme (const char *theme, const char *name) | ||
3456 | 794 | { | ||
3457 | 795 | FILE *f = NULL; | ||
3458 | 796 | char *full; | ||
3459 | 797 | char *dir; | ||
3460 | 798 | const char *path; | ||
3461 | 799 | char *inherits = NULL; | ||
3462 | 800 | const char *i; | ||
3463 | 801 | |||
3464 | 802 | if (!theme || !name) | ||
3465 | 803 | return NULL; | ||
3466 | 804 | |||
3467 | 805 | /* | ||
3468 | 806 | * Scan this theme | ||
3469 | 807 | */ | ||
3470 | 808 | for (path = XcursorLibraryPath (); | ||
3471 | 809 | path && f == NULL; | ||
3472 | 810 | path = _XcursorNextPath (path)) | ||
3473 | 811 | { | ||
3474 | 812 | dir = _XcursorBuildThemeDir (path, theme); | ||
3475 | 813 | if (dir) | ||
3476 | 814 | { | ||
3477 | 815 | full = _XcursorBuildFullname (dir, "cursors", name); | ||
3478 | 816 | if (full) | ||
3479 | 817 | { | ||
3480 | 818 | f = fopen (full, "r"); | ||
3481 | 819 | free (full); | ||
3482 | 820 | } | ||
3483 | 821 | if (!f && !inherits) | ||
3484 | 822 | { | ||
3485 | 823 | full = _XcursorBuildFullname (dir, "", "index.theme"); | ||
3486 | 824 | if (full) | ||
3487 | 825 | { | ||
3488 | 826 | inherits = _XcursorThemeInherits (full); | ||
3489 | 827 | free (full); | ||
3490 | 828 | } | ||
3491 | 829 | } | ||
3492 | 830 | free (dir); | ||
3493 | 831 | } | ||
3494 | 832 | } | ||
3495 | 833 | /* | ||
3496 | 834 | * Recurse to scan inherited themes | ||
3497 | 835 | */ | ||
3498 | 836 | for (i = inherits; i && f == NULL; i = _XcursorNextPath (i)) | ||
3499 | 837 | f = XcursorScanTheme (i, name); | ||
3500 | 838 | if (inherits != NULL) | ||
3501 | 839 | free (inherits); | ||
3502 | 840 | return f; | ||
3503 | 841 | } | ||
3504 | 842 | |||
3505 | 843 | XcursorImages * | ||
3506 | 844 | XcursorLibraryLoadImages (const char *file, const char *theme, int size) | ||
3507 | 845 | { | ||
3508 | 846 | FILE *f = NULL; | ||
3509 | 847 | XcursorImages *images = NULL; | ||
3510 | 848 | |||
3511 | 849 | if (!file) | ||
3512 | 850 | return NULL; | ||
3513 | 851 | |||
3514 | 852 | if (theme) | ||
3515 | 853 | f = XcursorScanTheme (theme, file); | ||
3516 | 854 | if (!f) | ||
3517 | 855 | f = XcursorScanTheme ("default", file); | ||
3518 | 856 | if (f) | ||
3519 | 857 | { | ||
3520 | 858 | images = XcursorFileLoadImages (f, size); | ||
3521 | 859 | if (images) | ||
3522 | 860 | XcursorImagesSetName (images, file); | ||
3523 | 861 | fclose (f); | ||
3524 | 862 | } | ||
3525 | 863 | return images; | ||
3526 | 864 | } | ||
3527 | 865 | |||
3528 | 866 | static void | ||
3529 | 867 | load_all_cursors_from_dir(const char *path, int size, | ||
3530 | 868 | void (*load_callback)(XcursorImages *, void *), | ||
3531 | 869 | void *user_data) | ||
3532 | 870 | { | ||
3533 | 871 | FILE *f; | ||
3534 | 872 | DIR *dir = opendir(path); | ||
3535 | 873 | struct dirent *ent; | ||
3536 | 874 | char *full; | ||
3537 | 875 | XcursorImages *images; | ||
3538 | 876 | |||
3539 | 877 | if (!dir) | ||
3540 | 878 | return; | ||
3541 | 879 | |||
3542 | 880 | for(ent = readdir(dir); ent; ent = readdir(dir)) { | ||
3543 | 881 | #ifdef _DIRENT_HAVE_D_TYPE | ||
3544 | 882 | if (ent->d_type != DT_UNKNOWN && | ||
3545 | 883 | (ent->d_type != DT_REG && ent->d_type != DT_LNK)) | ||
3546 | 884 | continue; | ||
3547 | 885 | #endif | ||
3548 | 886 | |||
3549 | 887 | full = _XcursorBuildFullname(path, "", ent->d_name); | ||
3550 | 888 | if (!full) | ||
3551 | 889 | continue; | ||
3552 | 890 | |||
3553 | 891 | f = fopen(full, "r"); | ||
3554 | 892 | if (!f) { | ||
3555 | 893 | free(full); | ||
3556 | 894 | continue; | ||
3557 | 895 | } | ||
3558 | 896 | |||
3559 | 897 | images = XcursorFileLoadImages(f, size); | ||
3560 | 898 | |||
3561 | 899 | if (images) { | ||
3562 | 900 | XcursorImagesSetName(images, ent->d_name); | ||
3563 | 901 | load_callback(images, user_data); | ||
3564 | 902 | } | ||
3565 | 903 | |||
3566 | 904 | fclose (f); | ||
3567 | 905 | free(full); | ||
3568 | 906 | } | ||
3569 | 907 | |||
3570 | 908 | closedir(dir); | ||
3571 | 909 | } | ||
3572 | 910 | |||
3573 | 911 | /** Load all the cursor of a theme | ||
3574 | 912 | * | ||
3575 | 913 | * This function loads all the cursor images of a given theme and its | ||
3576 | 914 | * inherited themes. Each cursor is loaded into an XcursorImages object | ||
3577 | 915 | * which is passed to the caller's load callback. If a cursor appears | ||
3578 | 916 | * more than once across all the inherited themes, the load callback | ||
3579 | 917 | * will be called multiple times, with possibly different XcursorImages | ||
3580 | 918 | * object which have the same name. The user is expected to destroy the | ||
3581 | 919 | * XcursorImages objects passed to the callback with | ||
3582 | 920 | * XcursorImagesDestroy(). | ||
3583 | 921 | * | ||
3584 | 922 | * \param theme The name of theme that should be loaded | ||
3585 | 923 | * \param size The desired size of the cursor images | ||
3586 | 924 | * \param load_callback A callback function that will be called | ||
3587 | 925 | * for each cursor loaded. The first parameter is the XcursorImages | ||
3588 | 926 | * object representing the loaded cursor and the second is a pointer | ||
3589 | 927 | * to data provided by the user. | ||
3590 | 928 | * \param user_data The data that should be passed to the load callback | ||
3591 | 929 | */ | ||
3592 | 930 | void | ||
3593 | 931 | xcursor_load_theme(const char *theme, int size, | ||
3594 | 932 | void (*load_callback)(XcursorImages *, void *), | ||
3595 | 933 | void *user_data) | ||
3596 | 934 | { | ||
3597 | 935 | char *full, *dir; | ||
3598 | 936 | char *inherits = NULL; | ||
3599 | 937 | const char *path, *i; | ||
3600 | 938 | |||
3601 | 939 | if (!theme) | ||
3602 | 940 | theme = "default"; | ||
3603 | 941 | |||
3604 | 942 | for (path = XcursorLibraryPath(); | ||
3605 | 943 | path; | ||
3606 | 944 | path = _XcursorNextPath(path)) { | ||
3607 | 945 | dir = _XcursorBuildThemeDir(path, theme); | ||
3608 | 946 | if (!dir) | ||
3609 | 947 | continue; | ||
3610 | 948 | |||
3611 | 949 | full = _XcursorBuildFullname(dir, "cursors", ""); | ||
3612 | 950 | |||
3613 | 951 | if (full) { | ||
3614 | 952 | load_all_cursors_from_dir(full, size, load_callback, | ||
3615 | 953 | user_data); | ||
3616 | 954 | free(full); | ||
3617 | 955 | } | ||
3618 | 956 | |||
3619 | 957 | if (!inherits) { | ||
3620 | 958 | full = _XcursorBuildFullname(dir, "", "index.theme"); | ||
3621 | 959 | if (full) { | ||
3622 | 960 | inherits = _XcursorThemeInherits(full); | ||
3623 | 961 | free(full); | ||
3624 | 962 | } | ||
3625 | 963 | } | ||
3626 | 964 | |||
3627 | 965 | free(dir); | ||
3628 | 966 | } | ||
3629 | 967 | |||
3630 | 968 | for (i = inherits; i; i = _XcursorNextPath(i)) | ||
3631 | 969 | xcursor_load_theme(i, size, load_callback, user_data); | ||
3632 | 970 | |||
3633 | 971 | if (inherits) | ||
3634 | 972 | free(inherits); | ||
3635 | 973 | } | ||
3636 | 974 | 0 | ||
3637 | === removed file 'examples/xcursor.h' | |||
3638 | --- examples/xcursor.h 2016-01-29 08:18:22 +0000 | |||
3639 | +++ examples/xcursor.h 1970-01-01 00:00:00 +0000 | |||
3640 | @@ -1,65 +0,0 @@ | |||
3641 | 1 | /* | ||
3642 | 2 | * Copyright © 2002 Keith Packard | ||
3643 | 3 | * | ||
3644 | 4 | * Permission to use, copy, modify, distribute, and sell this software and its | ||
3645 | 5 | * documentation for any purpose is hereby granted without fee, provided that | ||
3646 | 6 | * the above copyright notice appear in all copies and that both that | ||
3647 | 7 | * copyright notice and this permission notice appear in supporting | ||
3648 | 8 | * documentation, and that the name of Keith Packard not be used in | ||
3649 | 9 | * advertising or publicity pertaining to distribution of the software without | ||
3650 | 10 | * specific, written prior permission. Keith Packard makes no | ||
3651 | 11 | * representations about the suitability of this software for any purpose. It | ||
3652 | 12 | * is provided "as is" without express or implied warranty. | ||
3653 | 13 | * | ||
3654 | 14 | * KEITH PACKARD DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, | ||
3655 | 15 | * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO | ||
3656 | 16 | * EVENT SHALL KEITH PACKARD BE LIABLE FOR ANY SPECIAL, INDIRECT OR | ||
3657 | 17 | * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, | ||
3658 | 18 | * DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER | ||
3659 | 19 | * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR | ||
3660 | 20 | * PERFORMANCE OF THIS SOFTWARE. | ||
3661 | 21 | */ | ||
3662 | 22 | |||
3663 | 23 | #ifndef XCURSOR_H | ||
3664 | 24 | #define XCURSOR_H | ||
3665 | 25 | |||
3666 | 26 | #include <stdint.h> | ||
3667 | 27 | |||
3668 | 28 | |||
3669 | 29 | typedef int XcursorBool; | ||
3670 | 30 | typedef uint32_t XcursorUInt; | ||
3671 | 31 | |||
3672 | 32 | typedef XcursorUInt XcursorDim; | ||
3673 | 33 | typedef XcursorUInt XcursorPixel; | ||
3674 | 34 | |||
3675 | 35 | typedef struct _XcursorImage { | ||
3676 | 36 | XcursorUInt version; /* version of the image data */ | ||
3677 | 37 | XcursorDim size; /* nominal size for matching */ | ||
3678 | 38 | XcursorDim width; /* actual width */ | ||
3679 | 39 | XcursorDim height; /* actual height */ | ||
3680 | 40 | XcursorDim xhot; /* hot spot x (must be inside image) */ | ||
3681 | 41 | XcursorDim yhot; /* hot spot y (must be inside image) */ | ||
3682 | 42 | XcursorUInt delay; /* animation delay to next frame (ms) */ | ||
3683 | 43 | XcursorPixel *pixels; /* pointer to pixels */ | ||
3684 | 44 | } XcursorImage; | ||
3685 | 45 | |||
3686 | 46 | /* | ||
3687 | 47 | * Other data structures exposed by the library API | ||
3688 | 48 | */ | ||
3689 | 49 | typedef struct _XcursorImages { | ||
3690 | 50 | int nimage; /* number of images */ | ||
3691 | 51 | XcursorImage **images; /* array of XcursorImage pointers */ | ||
3692 | 52 | char *name; /* name used to load images */ | ||
3693 | 53 | } XcursorImages; | ||
3694 | 54 | |||
3695 | 55 | XcursorImages * | ||
3696 | 56 | XcursorLibraryLoadImages (const char *file, const char *theme, int size); | ||
3697 | 57 | |||
3698 | 58 | void | ||
3699 | 59 | XcursorImagesDestroy (XcursorImages *images); | ||
3700 | 60 | |||
3701 | 61 | void | ||
3702 | 62 | xcursor_load_theme(const char *theme, int size, | ||
3703 | 63 | void (*load_callback)(XcursorImages *, void *), | ||
3704 | 64 | void *user_data); | ||
3705 | 65 | #endif | ||
3706 | 66 | 0 | ||
3707 | === removed file 'examples/xcursor_loader.cpp' | |||
3708 | --- examples/xcursor_loader.cpp 2017-07-28 17:00:43 +0000 | |||
3709 | +++ examples/xcursor_loader.cpp 1970-01-01 00:00:00 +0000 | |||
3710 | @@ -1,228 +0,0 @@ | |||
3711 | 1 | /* | ||
3712 | 2 | * Copyright © 2014 Canonical Ltd. | ||
3713 | 3 | * | ||
3714 | 4 | * This program is free software: you can redistribute it and/or modify it | ||
3715 | 5 | * under the terms of the GNU General Public License version 2 or 3, | ||
3716 | 6 | * as published by the Free Software Foundation. | ||
3717 | 7 | * | ||
3718 | 8 | * This program is distributed in the hope that it will be useful, | ||
3719 | 9 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
3720 | 10 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
3721 | 11 | * GNU General Public License for more details. | ||
3722 | 12 | * | ||
3723 | 13 | * You should have received a copy of the GNU General Public License | ||
3724 | 14 | * along with this program. If not, see <http://www.gnu.org/licenses/>. | ||
3725 | 15 | * | ||
3726 | 16 | * Authored by: Robert Carr <robert.carr@canonical.com> | ||
3727 | 17 | */ | ||
3728 | 18 | |||
3729 | 19 | #include "xcursor_loader.h" | ||
3730 | 20 | |||
3731 | 21 | #include "mir/graphics/cursor_image.h" | ||
3732 | 22 | |||
3733 | 23 | #include <boost/throw_exception.hpp> | ||
3734 | 24 | #include <stdexcept> | ||
3735 | 25 | |||
3736 | 26 | #include <string.h> | ||
3737 | 27 | |||
3738 | 28 | #include <mir_toolkit/cursors.h> | ||
3739 | 29 | |||
3740 | 30 | // Unfortunately this can not be compiled as C++...so we can not namespace | ||
3741 | 31 | // these symbols. In order to differentiate from internal symbols | ||
3742 | 32 | // we refer to them via their _ prefixed version, i.e. _XcursorImage | ||
3743 | 33 | extern "C" | ||
3744 | 34 | { | ||
3745 | 35 | #include "xcursor.h" | ||
3746 | 36 | } | ||
3747 | 37 | |||
3748 | 38 | namespace me = mir::examples; | ||
3749 | 39 | namespace mg = mir::graphics; | ||
3750 | 40 | namespace mi = mir::input; | ||
3751 | 41 | namespace geom = mir::geometry; | ||
3752 | 42 | |||
3753 | 43 | namespace | ||
3754 | 44 | { | ||
3755 | 45 | class XCursorImage : public mg::CursorImage | ||
3756 | 46 | { | ||
3757 | 47 | public: | ||
3758 | 48 | XCursorImage(_XcursorImage *image, std::shared_ptr<_XcursorImages> const& save_resource) | ||
3759 | 49 | : image(image), | ||
3760 | 50 | save_resource(save_resource) | ||
3761 | 51 | { | ||
3762 | 52 | if (image->width != mi::default_cursor_size.width.as_uint32_t() || | ||
3763 | 53 | image->height != mi::default_cursor_size.height.as_uint32_t()) | ||
3764 | 54 | { | ||
3765 | 55 | BOOST_THROW_EXCEPTION( | ||
3766 | 56 | std::runtime_error("Somehow we got a cursor not of the default size (currently only 24x24 supported)")); | ||
3767 | 57 | } | ||
3768 | 58 | } | ||
3769 | 59 | |||
3770 | 60 | ~XCursorImage() | ||
3771 | 61 | { | ||
3772 | 62 | } | ||
3773 | 63 | |||
3774 | 64 | void const* as_argb_8888() const override | ||
3775 | 65 | { | ||
3776 | 66 | return image->pixels; | ||
3777 | 67 | } | ||
3778 | 68 | geom::Size size() const override | ||
3779 | 69 | { | ||
3780 | 70 | return mi::default_cursor_size; | ||
3781 | 71 | } | ||
3782 | 72 | geom::Displacement hotspot() const override | ||
3783 | 73 | { | ||
3784 | 74 | return {image->xhot, image->yhot}; | ||
3785 | 75 | } | ||
3786 | 76 | |||
3787 | 77 | private: | ||
3788 | 78 | _XcursorImage *image; | ||
3789 | 79 | std::shared_ptr<_XcursorImages> const save_resource; | ||
3790 | 80 | }; | ||
3791 | 81 | |||
3792 | 82 | std::string const | ||
3793 | 83 | xcursor_name_for_mir_cursor(std::string const& mir_cursor_name) | ||
3794 | 84 | { | ||
3795 | 85 | if (mir_cursor_name == mir_default_cursor_name) | ||
3796 | 86 | { | ||
3797 | 87 | return "arrow"; | ||
3798 | 88 | } | ||
3799 | 89 | else if (mir_cursor_name == mir_arrow_cursor_name) | ||
3800 | 90 | { | ||
3801 | 91 | return "arrow"; | ||
3802 | 92 | } | ||
3803 | 93 | else if (mir_cursor_name == mir_busy_cursor_name) | ||
3804 | 94 | { | ||
3805 | 95 | return "watch"; | ||
3806 | 96 | } | ||
3807 | 97 | else if (mir_cursor_name == mir_caret_cursor_name) | ||
3808 | 98 | { | ||
3809 | 99 | return "xterm"; // Yep | ||
3810 | 100 | } | ||
3811 | 101 | else if (mir_cursor_name == mir_pointing_hand_cursor_name) | ||
3812 | 102 | { | ||
3813 | 103 | return "hand2"; | ||
3814 | 104 | } | ||
3815 | 105 | else if (mir_cursor_name == mir_open_hand_cursor_name) | ||
3816 | 106 | { | ||
3817 | 107 | return "hand"; | ||
3818 | 108 | } | ||
3819 | 109 | else if (mir_cursor_name == mir_closed_hand_cursor_name) | ||
3820 | 110 | { | ||
3821 | 111 | return "grabbing"; | ||
3822 | 112 | } | ||
3823 | 113 | else if (mir_cursor_name == mir_horizontal_resize_cursor_name) | ||
3824 | 114 | { | ||
3825 | 115 | return "h_double_arrow"; | ||
3826 | 116 | } | ||
3827 | 117 | else if (mir_cursor_name == mir_vertical_resize_cursor_name) | ||
3828 | 118 | { | ||
3829 | 119 | return "v_double_arrow"; | ||
3830 | 120 | } | ||
3831 | 121 | else if (mir_cursor_name == mir_diagonal_resize_bottom_to_top_cursor_name) | ||
3832 | 122 | { | ||
3833 | 123 | return "top_right_corner"; | ||
3834 | 124 | } | ||
3835 | 125 | else if (mir_cursor_name == mir_diagonal_resize_top_to_bottom_cursor_name) | ||
3836 | 126 | { | ||
3837 | 127 | return "bottom_right_corner"; | ||
3838 | 128 | } | ||
3839 | 129 | else if (mir_cursor_name == mir_omnidirectional_resize_cursor_name) | ||
3840 | 130 | { | ||
3841 | 131 | return "fleur"; | ||
3842 | 132 | } | ||
3843 | 133 | else if (mir_cursor_name == mir_vsplit_resize_cursor_name) | ||
3844 | 134 | { | ||
3845 | 135 | return "v_double_arrow"; | ||
3846 | 136 | } | ||
3847 | 137 | else if (mir_cursor_name == mir_hsplit_resize_cursor_name) | ||
3848 | 138 | { | ||
3849 | 139 | return "h_double_arrow"; | ||
3850 | 140 | } | ||
3851 | 141 | else if (mir_cursor_name == mir_crosshair_cursor_name) | ||
3852 | 142 | { | ||
3853 | 143 | return "crosshair"; | ||
3854 | 144 | } | ||
3855 | 145 | else | ||
3856 | 146 | { | ||
3857 | 147 | return mir_cursor_name; | ||
3858 | 148 | } | ||
3859 | 149 | } | ||
3860 | 150 | } | ||
3861 | 151 | |||
3862 | 152 | me::XCursorLoader::XCursorLoader() | ||
3863 | 153 | { | ||
3864 | 154 | load_cursor_theme("default"); | ||
3865 | 155 | } | ||
3866 | 156 | |||
3867 | 157 | me::XCursorLoader::XCursorLoader(std::string const& theme) | ||
3868 | 158 | { | ||
3869 | 159 | load_cursor_theme(theme); | ||
3870 | 160 | } | ||
3871 | 161 | |||
3872 | 162 | // Each XcursorImages represents images for the different sizes of a given symbolic cursor. | ||
3873 | 163 | void me::XCursorLoader::load_appropriately_sized_image(_XcursorImages *images) | ||
3874 | 164 | { | ||
3875 | 165 | // We would rather take this lock in load_cursor_theme but the Xcursor lib style | ||
3876 | 166 | // makes it difficult to use our standard 'pass the lg around to _locked members' pattern | ||
3877 | 167 | std::lock_guard<std::mutex> lg(guard); | ||
3878 | 168 | |||
3879 | 169 | // We have to save all the images as XCursor expects us to free them. | ||
3880 | 170 | // This contains the actual image data though, so we need to ensure they stay alive | ||
3881 | 171 | // with the lifetime of the mg::CursorImage instance which refers to them. | ||
3882 | 172 | auto saved_xcursor_library_resource = std::shared_ptr<_XcursorImages>(images, [](_XcursorImages *images) | ||
3883 | 173 | { | ||
3884 | 174 | XcursorImagesDestroy(images); | ||
3885 | 175 | }); | ||
3886 | 176 | |||
3887 | 177 | _XcursorImage *image_of_correct_size = nullptr; | ||
3888 | 178 | for (int i = 0; i < images->nimage; i++) | ||
3889 | 179 | { | ||
3890 | 180 | _XcursorImage *candidate = images->images[i]; | ||
3891 | 181 | if (candidate->width == mi::default_cursor_size.width.as_uint32_t() && | ||
3892 | 182 | candidate->height == mi::default_cursor_size.height.as_uint32_t()) | ||
3893 | 183 | { | ||
3894 | 184 | image_of_correct_size = candidate; | ||
3895 | 185 | break; | ||
3896 | 186 | } | ||
3897 | 187 | } | ||
3898 | 188 | if (!image_of_correct_size) | ||
3899 | 189 | return; | ||
3900 | 190 | loaded_images[std::string(images->name)] = std::make_shared<XCursorImage>(image_of_correct_size, saved_xcursor_library_resource); | ||
3901 | 191 | } | ||
3902 | 192 | |||
3903 | 193 | void me::XCursorLoader::load_cursor_theme(std::string const& theme_name) | ||
3904 | 194 | { | ||
3905 | 195 | // Cursors are named by their square dimension...called the nominal size in XCursor terminology, so we just look up by width. | ||
3906 | 196 | // Later we verify the actual size. | ||
3907 | 197 | xcursor_load_theme(theme_name.c_str(), mi::default_cursor_size.width.as_uint32_t(), | ||
3908 | 198 | [](XcursorImages* images, void *this_ptr) -> void | ||
3909 | 199 | { | ||
3910 | 200 | // Can't use lambda capture as this lambda is thunked to a C function ptr | ||
3911 | 201 | auto p = static_cast<me::XCursorLoader*>(this_ptr); | ||
3912 | 202 | p->load_appropriately_sized_image(images); | ||
3913 | 203 | }, this); | ||
3914 | 204 | } | ||
3915 | 205 | |||
3916 | 206 | std::shared_ptr<mg::CursorImage> me::XCursorLoader::image( | ||
3917 | 207 | std::string const& cursor_name, | ||
3918 | 208 | geom::Size const& size) | ||
3919 | 209 | { | ||
3920 | 210 | auto xcursor_name = xcursor_name_for_mir_cursor(cursor_name); | ||
3921 | 211 | |||
3922 | 212 | if (size != mi::default_cursor_size) | ||
3923 | 213 | BOOST_THROW_EXCEPTION( | ||
3924 | 214 | std::logic_error("Only the default cursor size is currently supported (mi::default_cursor_size)")); | ||
3925 | 215 | |||
3926 | 216 | std::lock_guard<std::mutex> lg(guard); | ||
3927 | 217 | |||
3928 | 218 | auto it = loaded_images.find(xcursor_name); | ||
3929 | 219 | if (it != loaded_images.end()) | ||
3930 | 220 | return it->second; | ||
3931 | 221 | |||
3932 | 222 | // Fall back | ||
3933 | 223 | it = loaded_images.find("arrow"); | ||
3934 | 224 | if (it != loaded_images.end()) | ||
3935 | 225 | return it->second; | ||
3936 | 226 | |||
3937 | 227 | return nullptr; | ||
3938 | 228 | } | ||
3939 | 229 | 0 | ||
3940 | === removed file 'examples/xcursor_loader.h' | |||
3941 | --- examples/xcursor_loader.h 2017-07-28 17:00:43 +0000 | |||
3942 | +++ examples/xcursor_loader.h 1970-01-01 00:00:00 +0000 | |||
3943 | @@ -1,74 +0,0 @@ | |||
3944 | 1 | /* | ||
3945 | 2 | * Copyright © 2014 Canonical Ltd. | ||
3946 | 3 | * | ||
3947 | 4 | * This program is free software: you can redistribute it and/or modify it | ||
3948 | 5 | * under the terms of the GNU General Public License version 2 or 3, | ||
3949 | 6 | * as published by the Free Software Foundation. | ||
3950 | 7 | * | ||
3951 | 8 | * This program is distributed in the hope that it will be useful, | ||
3952 | 9 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
3953 | 10 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
3954 | 11 | * GNU General Public License for more details. | ||
3955 | 12 | * | ||
3956 | 13 | * You should have received a copy of the GNU General Public License | ||
3957 | 14 | * along with this program. If not, see <http://www.gnu.org/licenses/>. | ||
3958 | 15 | * | ||
3959 | 16 | * Authored by: Robert Carr <robert.carr@canonical.com> | ||
3960 | 17 | */ | ||
3961 | 18 | |||
3962 | 19 | |||
3963 | 20 | #ifndef MIR_INPUT_XCURSOR_CURSOR_LOADER_H_ | ||
3964 | 21 | #define MIR_INPUT_XCURSOR_CURSOR_LOADER_H_ | ||
3965 | 22 | |||
3966 | 23 | #include "mir/input/cursor_images.h" | ||
3967 | 24 | |||
3968 | 25 | #include <memory> | ||
3969 | 26 | #include <string> | ||
3970 | 27 | #include <map> | ||
3971 | 28 | #include <mutex> | ||
3972 | 29 | #include <functional> | ||
3973 | 30 | |||
3974 | 31 | // Unfortunately this library does not compile as C++ so we can not namespace it. | ||
3975 | 32 | extern "C" | ||
3976 | 33 | { | ||
3977 | 34 | struct _XcursorImages; | ||
3978 | 35 | } | ||
3979 | 36 | |||
3980 | 37 | namespace mir | ||
3981 | 38 | { | ||
3982 | 39 | namespace graphics | ||
3983 | 40 | { | ||
3984 | 41 | class CursorImage; | ||
3985 | 42 | } | ||
3986 | 43 | |||
3987 | 44 | namespace examples | ||
3988 | 45 | { | ||
3989 | 46 | class XCursorLoader : public input::CursorImages | ||
3990 | 47 | { | ||
3991 | 48 | public: | ||
3992 | 49 | XCursorLoader(); | ||
3993 | 50 | |||
3994 | 51 | explicit XCursorLoader(std::string const& theme); | ||
3995 | 52 | |||
3996 | 53 | virtual ~XCursorLoader() = default; | ||
3997 | 54 | |||
3998 | 55 | std::shared_ptr<graphics::CursorImage> image(std::string const& cursor_name, | ||
3999 | 56 | geometry::Size const& size); | ||
4000 | 57 | |||
4001 | 58 | protected: | ||
4002 | 59 | XCursorLoader(XCursorLoader const&) = delete; | ||
4003 | 60 | XCursorLoader& operator=(XCursorLoader const&) = delete; | ||
4004 | 61 | |||
4005 | 62 | private: | ||
4006 | 63 | std::mutex guard; | ||
4007 | 64 | |||
4008 | 65 | std::map<std::string, std::shared_ptr<graphics::CursorImage>> loaded_images; | ||
4009 | 66 | |||
4010 | 67 | void load_cursor_theme(std::string const& theme_name); | ||
4011 | 68 | void load_appropriately_sized_image(_XcursorImages *images); | ||
4012 | 69 | }; | ||
4013 | 70 | } | ||
4014 | 71 | } | ||
4015 | 72 | |||
4016 | 73 | |||
4017 | 74 | #endif /* MIR_INPUT_XCURSOR_CURSOR_LOADER_H_ */ | ||
4018 | 75 | 0 | ||
4019 | === renamed file 'include/platform/mir/abnormal_exit.h' => 'include/core/mir/abnormal_exit.h' | |||
4020 | === modified file 'playground/CMakeLists.txt' | |||
4021 | --- playground/CMakeLists.txt 2017-06-01 13:04:37 +0000 | |||
4022 | +++ playground/CMakeLists.txt 2017-08-31 08:02:08 +0000 | |||
4023 | @@ -16,6 +16,7 @@ | |||
4024 | 16 | 16 | ||
4025 | 17 | add_library(playgroundserverconfig STATIC | 17 | add_library(playgroundserverconfig STATIC |
4026 | 18 | server_configuration.cpp | 18 | server_configuration.cpp |
4027 | 19 | server_example_display_configuration_policy.cpp | ||
4028 | 19 | ) | 20 | ) |
4029 | 20 | 21 | ||
4030 | 21 | add_subdirectory(demo-shell/) | 22 | add_subdirectory(demo-shell/) |
4031 | 22 | 23 | ||
4032 | === renamed file 'examples/server_example_display_configuration_policy.cpp' => 'playground/server_example_display_configuration_policy.cpp' | |||
4033 | === renamed file 'examples/server_example_display_configuration_policy.h' => 'playground/server_example_display_configuration_policy.h' | |||
4034 | === modified file 'src/miral/process_doxygen_xml.py' | |||
4035 | --- src/miral/process_doxygen_xml.py 2017-08-31 08:02:07 +0000 | |||
4036 | +++ src/miral/process_doxygen_xml.py 2017-08-31 08:02:08 +0000 | |||
4037 | @@ -1,4 +1,4 @@ | |||
4039 | 1 | #! /usr/bin/python | 1 | #! /usr/bin/python3 |
4040 | 2 | """This script processes the XML generated by "make doc" and produces summary information | 2 | """This script processes the XML generated by "make doc" and produces summary information |
4041 | 3 | on symbols that libmiral intends to make public. | 3 | on symbols that libmiral intends to make public. |
4042 | 4 | 4 | ||
4043 | 5 | 5 | ||
4044 | === modified file 'tests/unit-tests/input/CMakeLists.txt' | |||
4045 | --- tests/unit-tests/input/CMakeLists.txt 2017-05-08 03:04:26 +0000 | |||
4046 | +++ tests/unit-tests/input/CMakeLists.txt 2017-08-31 08:02:08 +0000 | |||
4047 | @@ -3,7 +3,6 @@ | |||
4048 | 3 | list(APPEND UNIT_TEST_SOURCES | 3 | list(APPEND UNIT_TEST_SOURCES |
4049 | 4 | ${CMAKE_CURRENT_SOURCE_DIR}/test_event_filter_chain_dispatcher.cpp | 4 | ${CMAKE_CURRENT_SOURCE_DIR}/test_event_filter_chain_dispatcher.cpp |
4050 | 5 | ${CMAKE_CURRENT_SOURCE_DIR}/test_cursor_controller.cpp | 5 | ${CMAKE_CURRENT_SOURCE_DIR}/test_cursor_controller.cpp |
4051 | 6 | ${CMAKE_CURRENT_SOURCE_DIR}/test_xcursor_loader.cpp | ||
4052 | 7 | ${CMAKE_CURRENT_SOURCE_DIR}/test_touchspot_controller.cpp | 6 | ${CMAKE_CURRENT_SOURCE_DIR}/test_touchspot_controller.cpp |
4053 | 8 | ${CMAKE_CURRENT_SOURCE_DIR}/test_input_event.cpp | 7 | ${CMAKE_CURRENT_SOURCE_DIR}/test_input_event.cpp |
4054 | 9 | ${CMAKE_CURRENT_SOURCE_DIR}/test_config_changer.cpp | 8 | ${CMAKE_CURRENT_SOURCE_DIR}/test_config_changer.cpp |
4055 | 10 | 9 | ||
4056 | === removed file 'tests/unit-tests/input/test_xcursor_loader.cpp' | |||
4057 | --- tests/unit-tests/input/test_xcursor_loader.cpp 2017-07-28 17:00:43 +0000 | |||
4058 | +++ tests/unit-tests/input/test_xcursor_loader.cpp 1970-01-01 00:00:00 +0000 | |||
4059 | @@ -1,148 +0,0 @@ | |||
4060 | 1 | /* | ||
4061 | 2 | * Copyright © 2014 Canonical Ltd. | ||
4062 | 3 | * | ||
4063 | 4 | * This program is free software: you can redistribute it and/or modify | ||
4064 | 5 | * it under the terms of the GNU General Public License version 2 or 3 as | ||
4065 | 6 | * published by the Free Software Foundation. | ||
4066 | 7 | * | ||
4067 | 8 | * This program is distributed in the hope that it will be useful, | ||
4068 | 9 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
4069 | 10 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
4070 | 11 | * GNU General Public License for more details. | ||
4071 | 12 | * | ||
4072 | 13 | * You should have received a copy of the GNU General Public License | ||
4073 | 14 | * along with this program. If not, see <http://www.gnu.org/licenses/>. | ||
4074 | 15 | * | ||
4075 | 16 | * Authored by: Robert Carr <robert.carr@canonical.com> | ||
4076 | 17 | */ | ||
4077 | 18 | |||
4078 | 19 | #include "examples/xcursor_loader.h" | ||
4079 | 20 | |||
4080 | 21 | #include "mir/graphics/cursor_image.h" | ||
4081 | 22 | #include "mir_test_framework/executable_path.h" | ||
4082 | 23 | #include "mir_test_framework/temporary_environment_value.h" | ||
4083 | 24 | |||
4084 | 25 | #include <mir_toolkit/common.h> | ||
4085 | 26 | #include <mir_toolkit/cursors.h> | ||
4086 | 27 | |||
4087 | 28 | #include <gtest/gtest.h> | ||
4088 | 29 | #include <gmock/gmock.h> | ||
4089 | 30 | |||
4090 | 31 | #include <stdexcept> | ||
4091 | 32 | |||
4092 | 33 | #include <stdlib.h> | ||
4093 | 34 | #include <string.h> | ||
4094 | 35 | |||
4095 | 36 | namespace me = mir::examples; | ||
4096 | 37 | namespace mi = mir::input; | ||
4097 | 38 | namespace mg = mir::graphics; | ||
4098 | 39 | namespace mtf = mir_test_framework; | ||
4099 | 40 | |||
4100 | 41 | namespace | ||
4101 | 42 | { | ||
4102 | 43 | std::string const test_cursor_path{mir_test_framework::test_data_path() + std::string("/testing-cursor-theme")}; | ||
4103 | 44 | } | ||
4104 | 45 | |||
4105 | 46 | // Warning, XCURSOR_PATH will only be checked ONCE by libxcursor due to static var | ||
4106 | 47 | class XCursorLoaderTest : public ::testing::Test | ||
4107 | 48 | { | ||
4108 | 49 | public: | ||
4109 | 50 | XCursorLoaderTest() | ||
4110 | 51 | : xcursor_path("XCURSOR_PATH", test_cursor_path.c_str()) | ||
4111 | 52 | { | ||
4112 | 53 | } | ||
4113 | 54 | |||
4114 | 55 | mtf::TemporaryEnvironmentValue xcursor_path; | ||
4115 | 56 | me::XCursorLoader loader; | ||
4116 | 57 | }; | ||
4117 | 58 | |||
4118 | 59 | namespace | ||
4119 | 60 | { | ||
4120 | 61 | bool raw_argb_is_only_pixel(uint32_t const* raw_argb, size_t size, uint32_t pixel) | ||
4121 | 62 | { | ||
4122 | 63 | for (unsigned int i = 0; i < size; i++) | ||
4123 | 64 | { | ||
4124 | 65 | if (raw_argb[i] != pixel) | ||
4125 | 66 | { | ||
4126 | 67 | printf("Pixel: %u\n", raw_argb[i]); | ||
4127 | 68 | return false; | ||
4128 | 69 | } | ||
4129 | 70 | } | ||
4130 | 71 | return true; | ||
4131 | 72 | } | ||
4132 | 73 | bool cursor_image_is_solid_color(std::shared_ptr<mg::CursorImage> const& image, uint32_t pixel) | ||
4133 | 74 | { | ||
4134 | 75 | auto raw_argb = static_cast<uint32_t const*>(image->as_argb_8888()); | ||
4135 | 76 | size_t size = image->size().width.as_uint32_t() * image->size().height.as_uint32_t(); | ||
4136 | 77 | |||
4137 | 78 | return raw_argb_is_only_pixel(raw_argb, size, pixel); | ||
4138 | 79 | } | ||
4139 | 80 | |||
4140 | 81 | MATCHER(HasLoaded, "cursor image has loaded and is not nullptr."\ | ||
4141 | 82 | " Test expects cursor images to be installed to the directory the test is ran from") | ||
4142 | 83 | { | ||
4143 | 84 | return arg != nullptr; | ||
4144 | 85 | } | ||
4145 | 86 | |||
4146 | 87 | MATCHER(IsSolidRed, "") | ||
4147 | 88 | { | ||
4148 | 89 | return cursor_image_is_solid_color(arg, 0xffff0000); | ||
4149 | 90 | } | ||
4150 | 91 | |||
4151 | 92 | MATCHER(IsSolidGreen, "") | ||
4152 | 93 | { | ||
4153 | 94 | return cursor_image_is_solid_color(arg, 0xff00ff00); | ||
4154 | 95 | } | ||
4155 | 96 | |||
4156 | 97 | MATCHER(IsSolidBlue, "") | ||
4157 | 98 | { | ||
4158 | 99 | return cursor_image_is_solid_color(arg, 0xff0000ff); | ||
4159 | 100 | } | ||
4160 | 101 | |||
4161 | 102 | MATCHER(IsSolidBlack, "") | ||
4162 | 103 | { | ||
4163 | 104 | return cursor_image_is_solid_color(arg, 0xff000000); | ||
4164 | 105 | } | ||
4165 | 106 | } | ||
4166 | 107 | |||
4167 | 108 | TEST_F(XCursorLoaderTest, loads_cursors_from_testing_theme) | ||
4168 | 109 | { | ||
4169 | 110 | auto size = mi::default_cursor_size; | ||
4170 | 111 | auto red_image = loader.image("red", size); | ||
4171 | 112 | auto blue_image = loader.image("blue", size); | ||
4172 | 113 | auto green_image = loader.image("green", size); | ||
4173 | 114 | |||
4174 | 115 | ASSERT_THAT(red_image, HasLoaded()); | ||
4175 | 116 | ASSERT_THAT(green_image, HasLoaded()); | ||
4176 | 117 | ASSERT_THAT(blue_image, HasLoaded()); | ||
4177 | 118 | EXPECT_THAT(red_image, IsSolidRed()); | ||
4178 | 119 | EXPECT_THAT(green_image, IsSolidGreen()); | ||
4179 | 120 | EXPECT_THAT(blue_image, IsSolidBlue()); | ||
4180 | 121 | } | ||
4181 | 122 | |||
4182 | 123 | TEST_F(XCursorLoaderTest, only_supports_the_default_size) | ||
4183 | 124 | { | ||
4184 | 125 | EXPECT_THROW({ | ||
4185 | 126 | loader.image("red", {100, 100}); | ||
4186 | 127 | }, std::logic_error); | ||
4187 | 128 | } | ||
4188 | 129 | |||
4189 | 130 | TEST_F(XCursorLoaderTest, default_image_is_arrow_from_xcursor_theme) | ||
4190 | 131 | { | ||
4191 | 132 | auto size = mi::default_cursor_size; | ||
4192 | 133 | auto arrow_image = loader.image(mir_default_cursor_name, size); | ||
4193 | 134 | |||
4194 | 135 | // The testing theme uses a solid black image for the "arrow" symbolic | ||
4195 | 136 | // name. | ||
4196 | 137 | ASSERT_THAT(arrow_image, HasLoaded()); | ||
4197 | 138 | EXPECT_THAT(arrow_image, IsSolidBlack()); | ||
4198 | 139 | } | ||
4199 | 140 | |||
4200 | 141 | TEST_F(XCursorLoaderTest, symbolic_names_which_are_not_present_resolve_to_default) | ||
4201 | 142 | { | ||
4202 | 143 | auto size = mi::default_cursor_size; | ||
4203 | 144 | auto default_image = loader.image(mir_default_cursor_name, size); | ||
4204 | 145 | auto image_with_made_up_name = loader.image("Artickrumbulis", size); | ||
4205 | 146 | |||
4206 | 147 | EXPECT_EQ(default_image, image_with_made_up_name); | ||
4207 | 148 | } |
FAILED: Continuous integration, rev:4263 /mir-jenkins. ubuntu. com/job/ mir-ci/ 3586/ /mir-jenkins. ubuntu. com/job/ build-mir/ 4909/console /mir-jenkins. ubuntu. com/job/ build-0- fetch/5134 /mir-jenkins. ubuntu. com/job/ build-1- sourcepkg/ release= artful/ 5123 /mir-jenkins. ubuntu. com/job/ build-1- sourcepkg/ release= xenial/ 5123 /mir-jenkins. ubuntu. com/job/ build-1- sourcepkg/ release= zesty/5123 /mir-jenkins. ubuntu. com/job/ build-2- binpkg- mir/arch= amd64,compiler= clang,platform= mesa,release= artful/ 4948/console /mir-jenkins. ubuntu. com/job/ build-2- binpkg- mir/arch= amd64,compiler= clang,platform= mesa,release= zesty/4948/ console /mir-jenkins. ubuntu. com/job/ build-2- binpkg- mir/arch= amd64,compiler= gcc,platform= mesa,release= artful/ 4948/console /mir-jenkins. ubuntu. com/job/ build-2- binpkg- mir/arch= amd64,compiler= gcc,platform= mesa,release= xenial/ 4948/console /mir-jenkins. ubuntu. com/job/ build-2- binpkg- mir/arch= amd64,compiler= gcc,platform= mesa,release= zesty/4948/ console /mir-jenkins. ubuntu. com/job/ build-2- binpkg- mir/arch= cross-armhf, compiler= gcc,platform= mesa,release= artful/ 4948/console /mir-jenkins. ubuntu. com/job/ build-2- binpkg- mir/arch= cross-armhf, compiler= gcc,platform= mesa,release= zesty/4948/ console /mir-jenkins. ubuntu. com/job/ build-2- binpkg- mir/arch= i386,compiler= gcc,platform= mesa,release= xenial/ 4948/console
https:/
Executed test runs:
FAILURE: https:/
SUCCESS: https:/
SUCCESS: https:/
SUCCESS: https:/
SUCCESS: https:/
FAILURE: https:/
FAILURE: https:/
FAILURE: https:/
FAILURE: https:/
FAILURE: https:/
FAILURE: https:/
FAILURE: https:/
FAILURE: https:/
Click here to trigger a rebuild: /mir-jenkins. ubuntu. com/job/ mir-ci/ 3586/rebuild
https:/