Mir

Merge lp:~alan-griffiths/mir/move-miral-to-mir-cleanup into lp:mir

Proposed by Alan Griffiths
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
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)

To post a comment you must log in.
Revision history for this message
Mir CI Bot (mir-ci-bot) wrote :

FAILED: Continuous integration, rev:4263
https://mir-jenkins.ubuntu.com/job/mir-ci/3586/
Executed test runs:
    FAILURE: https://mir-jenkins.ubuntu.com/job/build-mir/4909/console
    SUCCESS: https://mir-jenkins.ubuntu.com/job/build-0-fetch/5134
    SUCCESS: https://mir-jenkins.ubuntu.com/job/build-1-sourcepkg/release=artful/5123
    SUCCESS: https://mir-jenkins.ubuntu.com/job/build-1-sourcepkg/release=xenial/5123
    SUCCESS: https://mir-jenkins.ubuntu.com/job/build-1-sourcepkg/release=zesty/5123
    FAILURE: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-mir/arch=amd64,compiler=clang,platform=mesa,release=artful/4948/console
    FAILURE: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-mir/arch=amd64,compiler=clang,platform=mesa,release=zesty/4948/console
    FAILURE: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-mir/arch=amd64,compiler=gcc,platform=mesa,release=artful/4948/console
    FAILURE: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-mir/arch=amd64,compiler=gcc,platform=mesa,release=xenial/4948/console
    FAILURE: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-mir/arch=amd64,compiler=gcc,platform=mesa,release=zesty/4948/console
    FAILURE: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-mir/arch=cross-armhf,compiler=gcc,platform=mesa,release=artful/4948/console
    FAILURE: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-mir/arch=cross-armhf,compiler=gcc,platform=mesa,release=zesty/4948/console
    FAILURE: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-mir/arch=i386,compiler=gcc,platform=mesa,release=xenial/4948/console

Click here to trigger a rebuild:
https://mir-jenkins.ubuntu.com/job/mir-ci/3586/rebuild

review: Needs Fixing (continuous-integration)
Revision history for this message
Mir CI Bot (mir-ci-bot) wrote :

FAILED: Continuous integration, rev:4264
https://mir-jenkins.ubuntu.com/job/mir-ci/3587/
Executed test runs:
    FAILURE: https://mir-jenkins.ubuntu.com/job/build-mir/4910/console
    SUCCESS: https://mir-jenkins.ubuntu.com/job/build-0-fetch/5135
    SUCCESS: https://mir-jenkins.ubuntu.com/job/build-1-sourcepkg/release=artful/5124
    SUCCESS: https://mir-jenkins.ubuntu.com/job/build-1-sourcepkg/release=xenial/5124
    SUCCESS: https://mir-jenkins.ubuntu.com/job/build-1-sourcepkg/release=zesty/5124
    FAILURE: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-mir/arch=amd64,compiler=clang,platform=mesa,release=artful/4949/console
    SUCCESS: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-mir/arch=amd64,compiler=clang,platform=mesa,release=zesty/4949
        deb: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-mir/arch=amd64,compiler=clang,platform=mesa,release=zesty/4949/artifact/output/*zip*/output.zip
    FAILURE: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-mir/arch=amd64,compiler=gcc,platform=mesa,release=artful/4949/console
    SUCCESS: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-mir/arch=amd64,compiler=gcc,platform=mesa,release=xenial/4949
        deb: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-mir/arch=amd64,compiler=gcc,platform=mesa,release=xenial/4949/artifact/output/*zip*/output.zip
    SUCCESS: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-mir/arch=amd64,compiler=gcc,platform=mesa,release=zesty/4949
        deb: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-mir/arch=amd64,compiler=gcc,platform=mesa,release=zesty/4949/artifact/output/*zip*/output.zip
    FAILURE: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-mir/arch=cross-armhf,compiler=gcc,platform=mesa,release=artful/4949/console
    SUCCESS: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-mir/arch=cross-armhf,compiler=gcc,platform=mesa,release=zesty/4949
        deb: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-mir/arch=cross-armhf,compiler=gcc,platform=mesa,release=zesty/4949/artifact/output/*zip*/output.zip
    SUCCESS: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-mir/arch=i386,compiler=gcc,platform=mesa,release=xenial/4949
        deb: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-mir/arch=i386,compiler=gcc,platform=mesa,release=xenial/4949/artifact/output/*zip*/output.zip

Click here to trigger a rebuild:
https://mir-jenkins.ubuntu.com/job/mir-ci/3587/rebuild

review: Needs Fixing (continuous-integration)
Revision history for this message
Mir CI Bot (mir-ci-bot) wrote :

PASSED: Continuous integration, rev:4264
https://mir-jenkins.ubuntu.com/job/mir-ci/3592/
Executed test runs:
    SUCCESS: https://mir-jenkins.ubuntu.com/job/build-mir/4916
    SUCCESS: https://mir-jenkins.ubuntu.com/job/build-0-fetch/5141
    SUCCESS: https://mir-jenkins.ubuntu.com/job/build-1-sourcepkg/release=artful/5130
    SUCCESS: https://mir-jenkins.ubuntu.com/job/build-1-sourcepkg/release=xenial/5130
    SUCCESS: https://mir-jenkins.ubuntu.com/job/build-1-sourcepkg/release=zesty/5130
    SUCCESS: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-mir/arch=amd64,compiler=clang,platform=mesa,release=artful/4955
        deb: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-mir/arch=amd64,compiler=clang,platform=mesa,release=artful/4955/artifact/output/*zip*/output.zip
    SUCCESS: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-mir/arch=amd64,compiler=clang,platform=mesa,release=zesty/4955
        deb: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-mir/arch=amd64,compiler=clang,platform=mesa,release=zesty/4955/artifact/output/*zip*/output.zip
    SUCCESS: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-mir/arch=amd64,compiler=gcc,platform=mesa,release=artful/4955
        deb: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-mir/arch=amd64,compiler=gcc,platform=mesa,release=artful/4955/artifact/output/*zip*/output.zip
    SUCCESS: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-mir/arch=amd64,compiler=gcc,platform=mesa,release=xenial/4955
        deb: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-mir/arch=amd64,compiler=gcc,platform=mesa,release=xenial/4955/artifact/output/*zip*/output.zip
    SUCCESS: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-mir/arch=amd64,compiler=gcc,platform=mesa,release=zesty/4955
        deb: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-mir/arch=amd64,compiler=gcc,platform=mesa,release=zesty/4955/artifact/output/*zip*/output.zip
    SUCCESS: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-mir/arch=cross-armhf,compiler=gcc,platform=mesa,release=artful/4955
        deb: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-mir/arch=cross-armhf,compiler=gcc,platform=mesa,release=artful/4955/artifact/output/*zip*/output.zip
    SUCCESS: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-mir/arch=cross-armhf,compiler=gcc,platform=mesa,release=zesty/4955
        deb: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-mir/arch=cross-armhf,compiler=gcc,platform=mesa,release=zesty/4955/artifact/output/*zip*/output.zip
    SUCCESS: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-mir/arch=i386,compiler=gcc,platform=mesa,release=xenial/4955
        deb: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-mir/arch=i386,compiler=gcc,platform=mesa,release=xenial/4955/artifact/output/*zip*/output.zip

Click here to trigger a rebuild:
https://mir-jenkins.ubuntu.com/job/mir-ci/3592/rebuild

review: Approve (continuous-integration)
Revision history for this message
Gerry Boland (gerboland) wrote :

- auto const client = options->get<std::string>(test_client_opt);
+ auto const client = options1->get<std::__cxx11::string>(test_client_opt);
Interesting, necessary?

Revision history for this message
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.

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

> - auto const client =
> options->get<std::string>(test_client_opt);
> + auto const client = options1->get<std::__cxx11::string>(test_client_opt);
> Interesting, necessary?

No. CLion "improving" code without me noticing.

Good catch!

Revision history for this message
Mir CI Bot (mir-ci-bot) wrote :

FAILED: Continuous integration, rev:4265
https://mir-jenkins.ubuntu.com/job/mir-ci/3610/
Executed test runs:
    FAILURE: https://mir-jenkins.ubuntu.com/job/build-mir/4936/console
    FAILURE: https://mir-jenkins.ubuntu.com/job/build-0-fetch/5161/console
    FAILURE: https://mir-jenkins.ubuntu.com/job/build-1-sourcepkg/release=artful/5151/console
    FAILURE: https://mir-jenkins.ubuntu.com/job/build-1-sourcepkg/release=xenial/5151/console
    FAILURE: https://mir-jenkins.ubuntu.com/job/build-1-sourcepkg/release=zesty/5151/console
    FAILURE: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-mir/arch=amd64,compiler=clang,platform=mesa,release=artful/4975/console
    FAILURE: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-mir/arch=amd64,compiler=clang,platform=mesa,release=zesty/4975/console
    FAILURE: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-mir/arch=amd64,compiler=gcc,platform=mesa,release=artful/4975/console
    FAILURE: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-mir/arch=amd64,compiler=gcc,platform=mesa,release=xenial/4975/console
    FAILURE: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-mir/arch=amd64,compiler=gcc,platform=mesa,release=zesty/4975/console
    FAILURE: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-mir/arch=cross-armhf,compiler=gcc,platform=mesa,release=artful/4975/console
    FAILURE: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-mir/arch=cross-armhf,compiler=gcc,platform=mesa,release=zesty/4975/console
    FAILURE: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-mir/arch=i386,compiler=gcc,platform=mesa,release=xenial/4975/console

Click here to trigger a rebuild:
https://mir-jenkins.ubuntu.com/job/mir-ci/3610/rebuild

review: Needs Fixing (continuous-integration)
Revision history for this message
Mir CI Bot (mir-ci-bot) wrote :

FAILED: Continuous integration, rev:4266
https://mir-jenkins.ubuntu.com/job/mir-ci/3612/
Executed test runs:
    FAILURE: https://mir-jenkins.ubuntu.com/job/build-mir/4939/console
    FAILURE: https://mir-jenkins.ubuntu.com/job/build-0-fetch/5164/console
    FAILURE: https://mir-jenkins.ubuntu.com/job/build-1-sourcepkg/release=artful/5154/console
    FAILURE: https://mir-jenkins.ubuntu.com/job/build-1-sourcepkg/release=xenial/5154/console
    FAILURE: https://mir-jenkins.ubuntu.com/job/build-1-sourcepkg/release=zesty/5154/console
    FAILURE: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-mir/arch=amd64,compiler=clang,platform=mesa,release=artful/4978/console
    FAILURE: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-mir/arch=amd64,compiler=clang,platform=mesa,release=zesty/4978/console
    FAILURE: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-mir/arch=amd64,compiler=gcc,platform=mesa,release=artful/4978/console
    FAILURE: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-mir/arch=amd64,compiler=gcc,platform=mesa,release=xenial/4978/console
    FAILURE: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-mir/arch=amd64,compiler=gcc,platform=mesa,release=zesty/4978/console
    FAILURE: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-mir/arch=cross-armhf,compiler=gcc,platform=mesa,release=artful/4978/console
    FAILURE: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-mir/arch=cross-armhf,compiler=gcc,platform=mesa,release=zesty/4978/console
    FAILURE: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-mir/arch=i386,compiler=gcc,platform=mesa,release=xenial/4978/console

Click here to trigger a rebuild:
https://mir-jenkins.ubuntu.com/job/mir-ci/3612/rebuild

review: Needs Fixing (continuous-integration)
Revision history for this message
Mir CI Bot (mir-ci-bot) wrote :

PASSED: Continuous integration, rev:4267
https://mir-jenkins.ubuntu.com/job/mir-ci/3618/
Executed test runs:
    SUCCESS: https://mir-jenkins.ubuntu.com/job/build-mir/4945
    SUCCESS: https://mir-jenkins.ubuntu.com/job/build-0-fetch/5170
    SUCCESS: https://mir-jenkins.ubuntu.com/job/build-1-sourcepkg/release=artful/5160
    SUCCESS: https://mir-jenkins.ubuntu.com/job/build-1-sourcepkg/release=xenial/5160
    SUCCESS: https://mir-jenkins.ubuntu.com/job/build-1-sourcepkg/release=zesty/5160
    SUCCESS: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-mir/arch=amd64,compiler=clang,platform=mesa,release=artful/4984
        deb: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-mir/arch=amd64,compiler=clang,platform=mesa,release=artful/4984/artifact/output/*zip*/output.zip
    SUCCESS: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-mir/arch=amd64,compiler=clang,platform=mesa,release=zesty/4984
        deb: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-mir/arch=amd64,compiler=clang,platform=mesa,release=zesty/4984/artifact/output/*zip*/output.zip
    SUCCESS: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-mir/arch=amd64,compiler=gcc,platform=mesa,release=artful/4984
        deb: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-mir/arch=amd64,compiler=gcc,platform=mesa,release=artful/4984/artifact/output/*zip*/output.zip
    SUCCESS: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-mir/arch=amd64,compiler=gcc,platform=mesa,release=xenial/4984
        deb: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-mir/arch=amd64,compiler=gcc,platform=mesa,release=xenial/4984/artifact/output/*zip*/output.zip
    SUCCESS: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-mir/arch=amd64,compiler=gcc,platform=mesa,release=zesty/4984
        deb: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-mir/arch=amd64,compiler=gcc,platform=mesa,release=zesty/4984/artifact/output/*zip*/output.zip
    SUCCESS: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-mir/arch=cross-armhf,compiler=gcc,platform=mesa,release=artful/4984
        deb: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-mir/arch=cross-armhf,compiler=gcc,platform=mesa,release=artful/4984/artifact/output/*zip*/output.zip
    SUCCESS: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-mir/arch=cross-armhf,compiler=gcc,platform=mesa,release=zesty/4984
        deb: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-mir/arch=cross-armhf,compiler=gcc,platform=mesa,release=zesty/4984/artifact/output/*zip*/output.zip
    SUCCESS: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-mir/arch=i386,compiler=gcc,platform=mesa,release=xenial/4984
        deb: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-mir/arch=i386,compiler=gcc,platform=mesa,release=xenial/4984/artifact/output/*zip*/output.zip

Click here to trigger a rebuild:
https://mir-jenkins.ubuntu.com/job/mir-ci/3618/rebuild

review: Approve (continuous-integration)

Preview Diff

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

Subscribers

People subscribed via source and target branches