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
=== modified file 'examples/CMakeLists.txt'
--- examples/CMakeLists.txt 2017-08-21 15:58:34 +0000
+++ examples/CMakeLists.txt 2017-08-31 08:02:08 +0000
@@ -9,26 +9,17 @@
9)9)
1010
11add_library(exampleserverconfig STATIC11add_library(exampleserverconfig STATIC
12 server_example_basic_window_manager.cpp
13 server_example_basic_window_manager.h
14 server_example_canonical_window_manager.cpp
15 server_example_display_configuration_policy.cpp
16 server_example_input_device_config.cpp12 server_example_input_device_config.cpp
17 server_example_input_event_filter.cpp13 server_example_input_event_filter.cpp
18 server_example_log_options.cpp14 server_example_log_options.cpp
19 server_example_input_filter.cpp15 server_example_input_filter.cpp
20 server_example_host_lifecycle_event.cpp16 server_example_host_lifecycle_event.cpp
21 server_example_window_management.cpp
22 server_example_custom_compositor.cpp17 server_example_custom_compositor.cpp
23 server_example_adorning_compositor.cpp18 server_example_adorning_compositor.cpp
24 server_example_cursor_images.cpp
25 server_example_cursor_images.h
26 xcursor_loader.cpp
27 xcursor_loader.h
28 xcursor.c
29 xcursor.h
30)19)
3120
21target_link_libraries(exampleserverconfig mirserver)
22
32target_link_libraries(eglapp23target_link_libraries(eglapp
33 mirclient24 mirclient
34 ${EGL_LIBRARIES}25 ${EGL_LIBRARIES}
@@ -175,6 +166,7 @@
175add_library(mirdraw STATIC graphics_utils.cpp)166add_library(mirdraw STATIC graphics_utils.cpp)
176167
177include_directories(168include_directories(
169 ${PROJECT_SOURCE_DIR}/include/miral
178 ${PROJECT_SOURCE_DIR}/include/server170 ${PROJECT_SOURCE_DIR}/include/server
179 ${PROJECT_SOURCE_DIR}/include/client171 ${PROJECT_SOURCE_DIR}/include/client
180 ${PROJECT_SOURCE_DIR}/include/platform172 ${PROJECT_SOURCE_DIR}/include/platform
@@ -190,7 +182,8 @@
190)182)
191183
192target_link_libraries(mir_demo_server_loadable184target_link_libraries(mir_demo_server_loadable
193 mirserver185 miral-shell-lib
186 miral
194 exampleserverconfig187 exampleserverconfig
195 ${GLog_LIBRARY}188 ${GLog_LIBRARY}
196 ${GFlags_LIBRARY}189 ${GFlags_LIBRARY}
@@ -248,15 +241,6 @@
248 endif ()241 endif ()
249endif ()242endif ()
250243
251add_executable(mir_cursor_theme_dump
252 cursor-theme-dump.cpp
253)
254
255target_link_libraries(mir_cursor_theme_dump
256 exampleserverconfig
257 mirserver
258)
259
260mir_add_wrapped_executable(mir_demo_client_multistream244mir_add_wrapped_executable(mir_demo_client_multistream
261 multi_stream.cpp245 multi_stream.cpp
262)246)
263247
=== removed file 'examples/cursor-theme-dump.cpp'
--- examples/cursor-theme-dump.cpp 2017-07-28 17:00:43 +0000
+++ examples/cursor-theme-dump.cpp 1970-01-01 00:00:00 +0000
@@ -1,127 +0,0 @@
1/*
2 * Copyright © 2015 Canonical Ltd.
3 *
4 * This program is free software: you can redistribute it and/or modify it
5 * under the terms of the GNU General Public License version 2 or 3,
6 * as published by the Free Software Foundation.
7 *
8 * This program is distributed in the hope that it will be useful,
9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 * GNU General Public License for more details.
12 *
13 * You should have received a copy of the GNU General Public License
14 * along with this program. If not, see <http://www.gnu.org/licenses/>.
15 *
16 * Authored by: Alan Griffiths <alan@octopull.co.uk>
17 */
18
19#include "xcursor_loader.h"
20
21#include "mir/graphics/cursor_image.h"
22#include <mir_toolkit/cursors.h>
23
24#include <fstream>
25
26using namespace mir::examples;
27using namespace mir::geometry;
28
29namespace
30{
31auto const cursor_names = {
32 mir_arrow_cursor_name,
33 mir_busy_cursor_name,
34 mir_caret_cursor_name,
35 mir_default_cursor_name,
36 mir_pointing_hand_cursor_name,
37 mir_open_hand_cursor_name,
38 mir_closed_hand_cursor_name,
39 mir_horizontal_resize_cursor_name,
40 mir_vertical_resize_cursor_name,
41 mir_diagonal_resize_bottom_to_top_cursor_name,
42 mir_diagonal_resize_top_to_bottom_cursor_name,
43 mir_omnidirectional_resize_cursor_name,
44 mir_vsplit_resize_cursor_name,
45 mir_hsplit_resize_cursor_name,
46 mir_crosshair_cursor_name };
47}
48
49
50int main(int argc, char const* argv[])
51try
52{
53 if (argc != 2)
54 {
55 puts("Usage mir_cursor_theme_dump <cursor theme>\n");
56 exit(-1);
57 }
58
59 auto const theme = argv[1];
60 std::ofstream output(std::string{theme} + "-theme.h");
61
62 output << "#include <initializer_list>\n"
63 "\n"
64 "namespace\n"
65 "{\n"
66 "struct CursorData\n"
67 "{\n"
68 " CursorData(char const* name, unsigned int hotspot_x, unsigned int hotspot_y, char const* pixel_data) :\n"
69 " name(name), hotspot_x(hotspot_x), hotspot_y(hotspot_y), pixel_data(reinterpret_cast<unsigned char const*>(pixel_data)) {}\n"
70 "\n"
71 " unsigned int const width{" << mir::input::default_cursor_size.width.as_int() << "};\n"
72 " unsigned int const height{" << mir::input::default_cursor_size.height.as_int() << "};\n"
73 " char const* const name;\n"
74 " unsigned int const hotspot_x;\n"
75 " unsigned int const hotspot_y;\n"
76 " unsigned char const* const pixel_data;\n"
77 "};\n"
78 "auto const cursor_data = {\n";
79
80 auto const buffer_size = 4*mir::input::default_cursor_size.height.as_int()*mir::input::default_cursor_size.height.as_int();
81
82 auto const xcursor_loader = std::make_shared<XCursorLoader>(theme);
83
84 for (auto cursor : cursor_names)
85 {
86 if (auto const image = xcursor_loader->image(cursor, mir::input::default_cursor_size))
87 {
88 printf("Have image for %s:%s\n", theme, cursor);
89
90 auto const hotspot = image->hotspot();
91 auto const argb_8888 = static_cast<uint8_t const*>(image->as_argb_8888());
92
93 output << "CursorData{\"" << cursor << "\", " << hotspot.dx.as_int() << ", " << hotspot.dy.as_int() << ",\n";
94
95 int chars = 0;
96
97 output << std::oct << " \"";
98
99 for (auto pbyte = argb_8888; pbyte != argb_8888 + buffer_size; ++pbyte)
100 {
101 auto step = (*pbyte < 010) ? 2 : (*pbyte < 0100) ? 3 : 4;
102
103 if ((chars += step) > 80)
104 {
105 output << "\"\n \"";
106 chars = step;
107 }
108
109 output << '\\' << static_cast<unsigned>(*pbyte);
110 }
111
112 output << "\"\n";
113
114 output << "},\n";
115 }
116 else
117 {
118 printf("** WARNING ** No image for %s:%s\n", theme, cursor);
119 }
120 }
121 output << "};\n";
122 output << "}\n";
123}
124catch (std::exception const& error)
125{
126 printf("** ERROR **: %s", error.what());
127}
128\ No newline at end of file0\ No newline at end of file
1291
=== modified file 'examples/miral-shell/CMakeLists.txt'
--- examples/miral-shell/CMakeLists.txt 2017-08-21 15:58:34 +0000
+++ examples/miral-shell/CMakeLists.txt 2017-08-31 08:02:08 +0000
@@ -46,20 +46,24 @@
46 DESTINATION ${CMAKE_INSTALL_PREFIX}/bin46 DESTINATION ${CMAKE_INSTALL_PREFIX}/bin
47)47)
4848
49mir_add_wrapped_executable(miral-shell49add_library(miral-shell-lib STATIC
50 shell_main.cpp
51 tiling_window_manager.cpp tiling_window_manager.h50 tiling_window_manager.cpp tiling_window_manager.h
52 floating_window_manager.cpp floating_window_manager.h51 floating_window_manager.cpp floating_window_manager.h
53 decoration_provider.cpp decoration_provider.h52 decoration_provider.cpp decoration_provider.h
54 titlebar_config.cpp titlebar_config.h53 titlebar_config.cpp titlebar_config.h
55)54)
5655
57pkg_check_modules(FREETYPE freetype2 REQUIRED)56pkg_check_modules(FREETYPE freetype2 REQUIRED)
58target_include_directories(miral-shell PRIVATE ${FREETYPE_INCLUDE_DIRS})57target_include_directories(miral-shell-lib PRIVATE ${FREETYPE_INCLUDE_DIRS})
59target_compile_definitions(miral-shell PRIVATE -DTYPO_SUPPORTS_FREETYPE)58target_compile_definitions(miral-shell-lib PRIVATE -DTYPO_SUPPORTS_FREETYPE)
59target_link_libraries(miral-shell-lib miral-spinner miral ${FREETYPE_LIBRARIES})
60
61mir_add_wrapped_executable(miral-shell
62 shell_main.cpp
63)
64
60target_link_libraries(miral-shell65target_link_libraries(miral-shell
61 miral-spinner66 miral-shell-lib
62 miral67 miral
63 ${FREETYPE_LIBRARIES}
64)68)
6569
6670
=== modified file 'examples/server_example.cpp'
--- examples/server_example.cpp 2017-07-28 17:00:43 +0000
+++ examples/server_example.cpp 2017-08-31 08:02:08 +0000
@@ -1,5 +1,5 @@
1/*1/*
2 * Copyright © 2012-2015 Canonical Ltd.2 * Copyright © 2012-2017 Canonical Ltd.
3 *3 *
4 * This program is free software: you can redistribute it and/or modify4 * This program is free software: you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License version 2 or 3 as5 * it under the terms of the GNU General Public License version 2 or 3 as
@@ -19,19 +19,24 @@
19#include "server_example_log_options.h"19#include "server_example_log_options.h"
20#include "server_example_input_event_filter.h"20#include "server_example_input_event_filter.h"
21#include "server_example_input_filter.h"21#include "server_example_input_filter.h"
22#include "server_example_display_configuration_policy.h"
23#include "server_example_host_lifecycle_event_listener.h"22#include "server_example_host_lifecycle_event_listener.h"
24#include "server_example_window_management.h"
25#include "server_example_custom_compositor.h"23#include "server_example_custom_compositor.h"
26#include "server_example_test_client.h"24#include "server_example_test_client.h"
27#include "server_example_cursor_images.h"
28#include "server_example_input_device_config.h"25#include "server_example_input_device_config.h"
2926
27#include "miral-shell/tiling_window_manager.h"
28#include "miral-shell/floating_window_manager.h"
29#include "miral-shell/titlebar_config.h"
30#include "miral-shell/spinner/splash.h"
31
32#include <miral/cursor_theme.h>
33#include <miral/display_configuration_option.h>
34#include <miral/runner.h>
35#include <miral/window_management_options.h>
36
30#include "mir/abnormal_exit.h"37#include "mir/abnormal_exit.h"
31#include "mir/server.h"38#include "mir/server.h"
32#include "mir/main_loop.h"39#include "mir/main_loop.h"
33#include "mir/fd.h"
34
35#include "mir/report_exception.h"40#include "mir/report_exception.h"
36#include "mir/options/option.h"41#include "mir/options/option.h"
3742
@@ -40,6 +45,8 @@
40#include <chrono>45#include <chrono>
41#include <cstdlib>46#include <cstdlib>
4247
48namespace mir { class AbnormalExit; }
49
43namespace me = mir::examples;50namespace me = mir::examples;
4451
45///\example server_example.cpp52///\example server_example.cpp
@@ -47,51 +54,6 @@
4754
48namespace55namespace
49{56{
50auto connection(int fd) -> std::string
51{
52 char connect_string[64] = {0};
53 // We can't have both the server and the client owning the same fd, since
54 // that will result in a double-close(). We give the client a duplicate which
55 // the client can safely own (and should close when done).
56 sprintf(connect_string, "fd://%d", dup(fd));
57 return connect_string;
58}
59
60void add_launcher_option_to(mir::Server& server)
61{
62 static const char* const launch_child_opt = "launch-client";
63 static const char* const launch_client_descr = "system() command to launch client";
64
65 server.add_configuration_option(launch_child_opt, launch_client_descr, mir::OptionType::string);
66 server.add_init_callback([&]
67 {
68 const auto options = server.get_options();
69 if (options->is_set(launch_child_opt))
70 {
71 unsetenv("DISPLAY"); // Discourage toolkits from using X11
72 setenv("GDK_BACKEND", "mir", true); // configure GTK to use Mir
73 setenv("QT_QPA_PLATFORM", "ubuntumirclient", true); // configure Qt to use Mir
74 unsetenv("QT_QPA_PLATFORMTHEME"); // Discourage Qt from unsupported theme
75 setenv("SDL_VIDEODRIVER", "mir", true); // configure SDL to use Mir
76
77 auto const value = options->get<std::string>(launch_child_opt);
78
79 for (auto i = begin(value); i != end(value); )
80 {
81 auto const j = find(i, end(value), '&');
82
83 auto const cmd ="MIR_SOCKET=" + connection(server.open_client_socket()) + " " +
84 std::string{i, j} + "&";
85
86 auto ignore = std::system(cmd.c_str());
87 (void)(ignore);
88
89 if ((i = j) != end(value)) ++i;
90 }
91 }
92 });
93}
94
95void add_timeout_option_to(mir::Server& server)57void add_timeout_option_to(mir::Server& server)
96{58{
97 static const char* const timeout_opt = "timeout";59 static const char* const timeout_opt = "timeout";
@@ -110,6 +72,23 @@
110 });72 });
111}73}
11274
75
76// Create some input filters (we need to keep them or they deactivate)
77struct InputFilters
78{
79 void operator()(mir::Server& server)
80 {
81 quit_filter = me::make_quit_filter_for(server);
82 printing_filter = me::make_printing_input_filter_for(server);
83 screen_rotation_filter = me::make_screen_rotation_filter_for(server);
84 }
85
86private:
87 std::shared_ptr<mir::input::EventFilter> quit_filter;
88 std::shared_ptr<mir::input::EventFilter> printing_filter;
89 std::shared_ptr<mir::input::EventFilter> screen_rotation_filter;
90};
91
113void exception_handler()92void exception_handler()
114try93try
115{94{
@@ -145,44 +124,48 @@
145int main(int argc, char const* argv[])124int main(int argc, char const* argv[])
146try125try
147{126{
148 mir::Server server;127 miral::MirRunner runner{argc, argv, "mir/mir_demo_server.config"};
149128
150 // Use config options file in e.g. ~/.config/mir/mir_demo_server.config129 runner.set_exception_handler(exception_handler);
151 server.set_config_filename("mir/mir_demo_server.config");130
152131 std::function<void()> shutdown_hook{[]{}};
153 // Add example options for display layout, logging, launching clients and timeout132 runner.add_stop_callback([&] { shutdown_hook(); });
154 me::add_display_configuration_options_to(server);133
155 me::add_log_host_lifecycle_option_to(server);134 SpinnerSplash spinner;
156 me::add_glog_options_to(server);135 miral::InternalClientLauncher launcher;
157 me::add_window_manager_option_to(server);136 miral::ActiveOutputsMonitor outputs_monitor;
158 me::add_custom_compositor_option_to(server);137 miral::WindowManagerOptions window_managers
159 me::add_input_device_configuration_options_to(server);138 {
160 add_launcher_option_to(server);139 miral::add_window_manager_policy<FloatingWindowManagerPolicy>("floating", spinner, launcher, shutdown_hook),
161 add_timeout_option_to(server);140 miral::add_window_manager_policy<TilingWindowManagerPolicy>("tiling", spinner, launcher, outputs_monitor),
162 me::add_x_cursor_images(server);141 };
163142
164 server.set_exception_handler(exception_handler);143 InputFilters input_filters;
165144 me::TestClientRunner test_runner;
166 me::ClientContext context;145
167 me::add_test_client_option_to(server, context);146 bool const server_exited_normally = runner.run_with({
168147 // example options for display layout, logging and timeout
169 // Create some input filters (we need to keep them or they deactivate)148 miral::display_configuration_options,
170 auto const quit_filter = me::make_quit_filter_for(server);149 me::add_log_host_lifecycle_option_to,
171 auto const printing_filter = me::make_printing_input_filter_for(server);150 me::add_glog_options_to,
172 auto const screen_rotation_filter = me::make_screen_rotation_filter_for(server);151 miral::StartupInternalClient{"Intro", spinner},
173152 launcher,
174 // Provide the command line and run the server153 window_managers,
175 server.set_command_line(argc, argv);154 me::add_custom_compositor_option_to,
176 server.apply_settings();155 me::add_input_device_configuration_options_to,
177 server.run();156 add_timeout_option_to,
157 miral::CursorTheme{"default"},
158 input_filters,
159 test_runner
160 });
178161
179 // Propagate any test failure162 // Propagate any test failure
180 if (context.test_failed)163 if (test_runner.test_failed())
181 {164 {
182 return EXIT_FAILURE;165 return EXIT_FAILURE;
183 }166 }
184167
185 return server.exited_normally() ? EXIT_SUCCESS : EXIT_FAILURE;168 return server_exited_normally ? EXIT_SUCCESS : EXIT_FAILURE;
186}169}
187catch (...)170catch (...)
188{171{
189172
=== removed file 'examples/server_example_basic_window_manager.cpp'
--- examples/server_example_basic_window_manager.cpp 2017-07-28 17:00:43 +0000
+++ examples/server_example_basic_window_manager.cpp 1970-01-01 00:00:00 +0000
@@ -1,328 +0,0 @@
1/*
2 * Copyright © 2015 Canonical Ltd.
3 *
4 * This program is free software: you can redistribute it and/or modify it
5 * under the terms of the GNU General Public License version 2 or 3,
6 * as published by the Free Software Foundation.
7 *
8 * This program is distributed in the hope that it will be useful,
9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 * GNU General Public License for more details.
12 *
13 * You should have received a copy of the GNU General Public License
14 * along with this program. If not, see <http://www.gnu.org/licenses/>.
15 *
16 * Authored by: Alan Griffiths <alan@octopull.co.uk>
17 */
18
19#include "server_example_basic_window_manager.h"
20
21#include "mir/scene/session.h"
22#include "mir/scene/surface.h"
23#include "mir/scene/surface_creation_parameters.h"
24
25namespace me = mir::examples;
26
27me::BasicWindowManager::BasicWindowManager(
28 shell::FocusController* focus_controller,
29 std::unique_ptr<WindowManagementPolicy> policy) :
30 focus_controller(focus_controller),
31 policy(std::move(policy))
32{
33}
34
35void me::BasicWindowManager::add_session(std::shared_ptr<scene::Session> const& session)
36{
37 std::lock_guard<decltype(mutex)> lock(mutex);
38 session_info[session] = SessionInfo();
39 policy->handle_session_info_updated(session_info, displays);
40}
41
42void me::BasicWindowManager::remove_session(std::shared_ptr<scene::Session> const& session)
43{
44 std::lock_guard<decltype(mutex)> lock(mutex);
45 session_info.erase(session);
46 policy->handle_session_info_updated(session_info, displays);
47}
48
49auto me::BasicWindowManager::add_surface(
50 std::shared_ptr<scene::Session> const& session,
51 scene::SurfaceCreationParameters const& params,
52 std::function<frontend::SurfaceId(std::shared_ptr<scene::Session> const& session, scene::SurfaceCreationParameters const& params)> const& build)
53-> frontend::SurfaceId
54{
55 std::lock_guard<decltype(mutex)> lock(mutex);
56 scene::SurfaceCreationParameters const placed_params = policy->handle_place_new_surface(session, params);
57 auto const result = build(session, placed_params);
58 auto const surface = session->surface(result);
59 surface_info.emplace(surface, SurfaceInfo{session, surface, placed_params});
60 policy->handle_new_surface(session, surface);
61 policy->generate_decorations_for(session, surface, surface_info, build);
62 return result;
63}
64
65void me::BasicWindowManager::modify_surface(
66 std::shared_ptr<scene::Session> const& session,
67 std::shared_ptr<scene::Surface> const& surface,
68 shell::SurfaceSpecification const& modifications)
69{
70 std::lock_guard<decltype(mutex)> lock(mutex);
71 policy->handle_modify_surface(session, surface, modifications);
72}
73
74void me::BasicWindowManager::remove_surface(
75 std::shared_ptr<scene::Session> const& session,
76 std::weak_ptr<scene::Surface> const& surface)
77{
78 std::lock_guard<decltype(mutex)> lock(mutex);
79 policy->handle_delete_surface(session, surface);
80
81 surface_info.erase(surface);
82}
83
84void me::BasicWindowManager::forget(std::weak_ptr<scene::Surface> const& surface)
85{
86 surface_info.erase(surface);
87}
88
89void me::BasicWindowManager::add_display(geometry::Rectangle const& area)
90{
91 std::lock_guard<decltype(mutex)> lock(mutex);
92 displays.add(area);
93 policy->handle_displays_updated(session_info, displays);
94}
95
96void me::BasicWindowManager::remove_display(geometry::Rectangle const& area)
97{
98 std::lock_guard<decltype(mutex)> lock(mutex);
99 displays.remove(area);
100 policy->handle_displays_updated(session_info, displays);
101}
102
103bool me::BasicWindowManager::handle_keyboard_event(MirKeyboardEvent const* event)
104{
105 std::lock_guard<decltype(mutex)> lock(mutex);
106 update_event_timestamp(event);
107 return policy->handle_keyboard_event(event);
108}
109
110bool me::BasicWindowManager::handle_touch_event(MirTouchEvent const* event)
111{
112 std::lock_guard<decltype(mutex)> lock(mutex);
113 update_event_timestamp(event);
114 return policy->handle_touch_event(event);
115}
116
117bool me::BasicWindowManager::handle_pointer_event(MirPointerEvent const* event)
118{
119 std::lock_guard<decltype(mutex)> lock(mutex);
120 update_event_timestamp(event);
121
122 cursor = {
123 mir_pointer_event_axis_value(event, mir_pointer_axis_x),
124 mir_pointer_event_axis_value(event, mir_pointer_axis_y)};
125
126 return policy->handle_pointer_event(event);
127}
128
129void me::BasicWindowManager::handle_raise_surface(
130 std::shared_ptr<scene::Session> const& session,
131 std::shared_ptr<scene::Surface> const& surface,
132 uint64_t timestamp)
133{
134 std::lock_guard<decltype(mutex)> lock(mutex);
135 if (timestamp >= last_input_event_timestamp)
136 policy->handle_raise_surface(session, surface);
137}
138
139void me::BasicWindowManager::handle_request_drag_and_drop(
140 std::shared_ptr<scene::Session> const& /*session*/,
141 std::shared_ptr<scene::Surface> const& /*surface*/,
142 uint64_t /*timestamp*/)
143{
144 // Not supported in example servers
145}
146
147void me::BasicWindowManager::handle_request_move(
148 std::shared_ptr<scene::Session> const& /*session*/,
149 std::shared_ptr<scene::Surface> const& /*surface*/,
150 uint64_t /*timestamp*/)
151{
152 // Not supported in example servers
153}
154
155int me::BasicWindowManager::set_surface_attribute(
156 std::shared_ptr<scene::Session> const& /*session*/,
157 std::shared_ptr<scene::Surface> const& surface,
158 MirWindowAttrib attrib,
159 int value)
160{
161 std::lock_guard<decltype(mutex)> lock(mutex);
162 switch (attrib)
163 {
164 case mir_window_attrib_state:
165 {
166 auto const state = policy->handle_set_state(surface, MirWindowState(value));
167 return surface->configure(attrib, state);
168 }
169 default:
170 return surface->configure(attrib, value);
171 }
172}
173
174auto me::BasicWindowManager::find_session(std::function<bool(SessionInfo const& info)> const& predicate)
175-> std::shared_ptr<scene::Session>
176 {
177 for(auto& info : session_info)
178 {
179 if (predicate(info.second))
180 {
181 return info.first.lock();
182 }
183 }
184
185 return std::shared_ptr<scene::Session>{};
186 }
187
188auto me::BasicWindowManager::info_for(std::weak_ptr<scene::Session> const& session) const
189-> SessionInfo&
190{
191 return const_cast<SessionInfo&>(session_info.at(session));
192}
193
194auto me::BasicWindowManager::info_for(std::weak_ptr<scene::Surface> const& surface) const
195-> SurfaceInfo&
196{
197 return const_cast<SurfaceInfo&>(surface_info.at(surface));
198}
199
200auto me::BasicWindowManager::focused_session() const
201-> std::shared_ptr<scene::Session>
202{
203 return focus_controller->focused_session();
204}
205
206auto me::BasicWindowManager::focused_surface() const
207->std::shared_ptr<scene::Surface>
208{
209 return focus_controller->focused_surface();
210}
211
212void me::BasicWindowManager::focus_next_session()
213{
214 focus_controller->focus_next_session();
215}
216
217void me::BasicWindowManager::set_focus_to(
218 std::shared_ptr<scene::Session> const& focus,
219 std::shared_ptr<scene::Surface> const& surface)
220{
221 focus_controller->set_focus_to(focus, surface);
222}
223
224auto me::BasicWindowManager::surface_at(geometry::Point cursor) const
225-> std::shared_ptr<scene::Surface>
226{
227 return focus_controller->surface_at(cursor);
228}
229
230auto me::BasicWindowManager::active_display()
231-> geometry::Rectangle const
232{
233 geometry::Rectangle result;
234
235 // 1. If a window has input focus, whichever display contains the largest
236 // proportion of the area of that window.
237 if (auto const surface = focused_surface())
238 {
239 auto const surface_rect = surface->input_bounds();
240 int max_overlap_area = -1;
241
242 for (auto const& display : displays)
243 {
244 auto const intersection = surface_rect.intersection_with(display).size;
245 if (intersection.width.as_int()*intersection.height.as_int() > max_overlap_area)
246 {
247 max_overlap_area = intersection.width.as_int()*intersection.height.as_int();
248 result = display;
249 }
250 }
251 return result;
252 }
253
254 // 2. Otherwise, if any window previously had input focus, for the window that had
255 // it most recently, the display that contained the largest proportion of the
256 // area of that window at the moment it closed, as long as that display is still
257 // available.
258
259 // 3. Otherwise, the display that contains the pointer, if there is one.
260 for (auto const& display : displays)
261 {
262 if (display.contains(cursor))
263 {
264 // Ignore the (unspecified) possiblity of overlapping displays
265 return display;
266 }
267 }
268
269 // 4. Otherwise, the primary display, if there is one (for example, the laptop display).
270
271 // 5. Otherwise, the first display.
272 if (displays.size())
273 result = *displays.begin();
274
275 return result;
276}
277
278void me::BasicWindowManager::raise_tree(std::shared_ptr<scene::Surface> const& root)
279{
280 SurfaceSet surfaces;
281 std::function<void(std::weak_ptr<scene::Surface> const& surface)> const add_children =
282 [&,this](std::weak_ptr<scene::Surface> const& surface)
283 {
284 auto const& info = info_for(surface);
285 surfaces.insert(begin(info.children), end(info.children));
286 for (auto const& child : info.children)
287 add_children(child);
288 };
289
290 surfaces.insert(root);
291 add_children(root);
292
293 focus_controller->raise(surfaces);
294}
295
296void me::BasicWindowManager::update_event_timestamp(MirKeyboardEvent const* kev)
297{
298 auto iev = mir_keyboard_event_input_event(kev);
299 last_input_event_timestamp = mir_input_event_get_event_time(iev);
300}
301
302void me::BasicWindowManager::update_event_timestamp(MirPointerEvent const* pev)
303{
304 auto iev = mir_pointer_event_input_event(pev);
305 auto pointer_action = mir_pointer_event_action(pev);
306
307 if (pointer_action == mir_pointer_action_button_up ||
308 pointer_action == mir_pointer_action_button_down)
309 {
310 last_input_event_timestamp = mir_input_event_get_event_time(iev);
311 }
312}
313
314void me::BasicWindowManager::update_event_timestamp(MirTouchEvent const* tev)
315{
316 auto iev = mir_touch_event_input_event(tev);
317 auto touch_count = mir_touch_event_point_count(tev);
318 for (unsigned i = 0; i < touch_count; i++)
319 {
320 auto touch_action = mir_touch_event_action(tev, i);
321 if (touch_action == mir_touch_action_up ||
322 touch_action == mir_touch_action_down)
323 {
324 last_input_event_timestamp = mir_input_event_get_event_time(iev);
325 break;
326 }
327 }
328}
3290
=== removed file 'examples/server_example_basic_window_manager.h'
--- examples/server_example_basic_window_manager.h 2017-07-28 17:00:43 +0000
+++ examples/server_example_basic_window_manager.h 1970-01-01 00:00:00 +0000
@@ -1,264 +0,0 @@
1/*
2 * Copyright © 2015 Canonical Ltd.
3 *
4 * This program is free software: you can redistribute it and/or modify it
5 * under the terms of the GNU General Public License version 2 or 3,
6 * as published by the Free Software Foundation.
7 *
8 * This program is distributed in the hope that it will be useful,
9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 * GNU General Public License for more details.
12 *
13 * You should have received a copy of the GNU General Public License
14 * along with this program. If not, see <http://www.gnu.org/licenses/>.
15 *
16 * Authored By: Alan Griffiths <alan@octopull.co.uk>
17 */
18
19#ifndef MIR_EXAMPLE_BASIC_WINDOW_MANAGER_H_
20#define MIR_EXAMPLE_BASIC_WINDOW_MANAGER_H_
21
22#include "mir/shell/window_management_info.h"
23
24#include "mir/geometry/rectangles.h"
25#include "mir/shell/abstract_shell.h"
26#include "mir/shell/window_manager.h"
27
28#include <map>
29#include <mutex>
30
31///\example server_example_basic_window_manager.h
32/// A generic policy-based window manager implementation
33
34namespace mir
35{
36namespace examples
37{
38using shell::SurfaceSet;
39using shell::SurfaceInfo;
40using shell::SessionInfo;
41
42/// The interface through which the policy instructs the controller.
43/// These functions assume that the BasicWindowManager data structures can be accessed freely.
44/// I.e. should only be invoked by the policy handle_... methods (where any necessary locks are held).
45class WindowManagerTools
46{
47public:
48 using SurfaceInfoMap = std::map<std::weak_ptr<scene::Surface>, SurfaceInfo, std::owner_less<std::weak_ptr<scene::Surface>>>;
49 using SessionInfoMap = std::map<std::weak_ptr<scene::Session>, SessionInfo, std::owner_less<std::weak_ptr<scene::Session>>>;
50
51 virtual auto find_session(std::function<bool(SessionInfo const& info)> const& predicate)
52 -> std::shared_ptr<scene::Session> = 0;
53
54 virtual auto info_for(std::weak_ptr<scene::Session> const& session) const -> SessionInfo& = 0;
55
56 virtual auto info_for(std::weak_ptr<scene::Surface> const& surface) const -> SurfaceInfo& = 0;
57
58 virtual std::shared_ptr<scene::Session> focused_session() const = 0;
59
60 virtual std::shared_ptr<scene::Surface> focused_surface() const = 0;
61
62 virtual void focus_next_session() = 0;
63
64 virtual void set_focus_to(
65 std::shared_ptr<scene::Session> const& focus,
66 std::shared_ptr<scene::Surface> const& surface) = 0;
67
68 virtual auto surface_at(geometry::Point cursor) const -> std::shared_ptr<scene::Surface> = 0;
69
70 virtual auto active_display() -> geometry::Rectangle const = 0;
71
72 virtual void forget(std::weak_ptr<scene::Surface> const& surface) = 0;
73
74 virtual void raise_tree(std::shared_ptr<scene::Surface> const& root) = 0;
75
76 virtual ~WindowManagerTools() = default;
77 WindowManagerTools() = default;
78 WindowManagerTools(WindowManagerTools const&) = delete;
79 WindowManagerTools& operator=(WindowManagerTools const&) = delete;
80};
81
82class WindowManagementPolicy
83{
84public:
85 using SessionInfoMap = typename WindowManagerTools::SessionInfoMap;
86 using SurfaceInfoMap = typename WindowManagerTools::SurfaceInfoMap;
87
88 virtual void handle_session_info_updated(SessionInfoMap& session_info, geometry::Rectangles const& displays) = 0;
89
90 virtual void handle_displays_updated(SessionInfoMap& session_info, geometry::Rectangles const& displays) = 0;
91
92 virtual auto handle_place_new_surface(
93 std::shared_ptr<scene::Session> const& session,
94 scene::SurfaceCreationParameters const& request_parameters)
95 -> scene::SurfaceCreationParameters = 0;
96
97 virtual void handle_new_surface(std::shared_ptr<scene::Session> const& session, std::shared_ptr<scene::Surface> const& surface) = 0;
98
99 virtual void handle_modify_surface(
100 std::shared_ptr<scene::Session> const& session,
101 std::shared_ptr<scene::Surface> const& surface,
102 shell::SurfaceSpecification const& modifications) = 0;
103
104 virtual void handle_delete_surface(std::shared_ptr<scene::Session> const& session, std::weak_ptr<scene::Surface> const& surface) = 0;
105
106 virtual int handle_set_state(std::shared_ptr<scene::Surface> const& surface, MirWindowState value) = 0;
107
108 virtual void generate_decorations_for(
109 std::shared_ptr<scene::Session> const& session, std::shared_ptr<scene::Surface> const& surface,
110 SurfaceInfoMap& surface_info,
111 std::function<frontend::SurfaceId(std::shared_ptr<scene::Session> const&, scene::SurfaceCreationParameters const&)> const& build) = 0;
112
113 virtual bool handle_keyboard_event(MirKeyboardEvent const* event) = 0;
114
115 virtual bool handle_touch_event(MirTouchEvent const* event) = 0;
116
117 virtual bool handle_pointer_event(MirPointerEvent const* event) = 0;
118
119 virtual void handle_raise_surface(
120 std::shared_ptr<scene::Session> const& session,
121 std::shared_ptr<scene::Surface> const& surface) = 0;
122
123 virtual ~WindowManagementPolicy() = default;
124 WindowManagementPolicy() = default;
125 WindowManagementPolicy(WindowManagementPolicy const&) = delete;
126 WindowManagementPolicy& operator=(WindowManagementPolicy const&) = delete;
127};
128
129/// A policy based window manager.
130/// This takes care of the management of any meta implementation held for the sessions and surfaces.
131class BasicWindowManager : public virtual shell::WindowManager,
132 protected WindowManagerTools
133{
134protected:
135 BasicWindowManager(
136 shell::FocusController* focus_controller,
137 std::unique_ptr<WindowManagementPolicy> policy);
138
139public:
140 using typename WindowManagerTools::SurfaceInfoMap;
141 using typename WindowManagerTools::SessionInfoMap;
142
143 void add_session(std::shared_ptr<scene::Session> const& session) override;
144
145 void remove_session(std::shared_ptr<scene::Session> const& session) override;
146
147 auto add_surface(
148 std::shared_ptr<scene::Session> const& session,
149 scene::SurfaceCreationParameters const& params,
150 std::function<frontend::SurfaceId(std::shared_ptr<scene::Session> const& session, scene::SurfaceCreationParameters const& params)> const& build)
151 -> frontend::SurfaceId override;
152
153 void modify_surface(
154 std::shared_ptr<scene::Session> const& session,
155 std::shared_ptr<scene::Surface> const& surface,
156 shell::SurfaceSpecification const& modifications) override;
157
158 void remove_surface(
159 std::shared_ptr<scene::Session> const& session,
160 std::weak_ptr<scene::Surface> const& surface) override;
161
162 void forget(std::weak_ptr<scene::Surface> const& surface) override;
163
164 void add_display(geometry::Rectangle const& area) override;
165
166 void remove_display(geometry::Rectangle const& area) override;
167
168 bool handle_keyboard_event(MirKeyboardEvent const* event) override;
169
170 bool handle_touch_event(MirTouchEvent const* event) override;
171
172 bool handle_pointer_event(MirPointerEvent const* event) override;
173
174 void handle_raise_surface(
175 std::shared_ptr<scene::Session> const& session,
176 std::shared_ptr<scene::Surface> const& surface,
177 uint64_t timestamp) override;
178
179 void handle_request_drag_and_drop(
180 std::shared_ptr<scene::Session> const& session,
181 std::shared_ptr<scene::Surface> const& surface,
182 uint64_t timestamp) override;
183
184 void handle_request_move(
185 std::shared_ptr<scene::Session> const& session,
186 std::shared_ptr<scene::Surface> const& surface,
187 uint64_t timestamp) override;
188
189 int set_surface_attribute(
190 std::shared_ptr<scene::Session> const& /*session*/,
191 std::shared_ptr<scene::Surface> const& surface,
192 MirWindowAttrib attrib,
193 int value) override;
194
195 auto find_session(std::function<bool(SessionInfo const& info)> const& predicate)
196 -> std::shared_ptr<scene::Session> override;
197
198 auto info_for(std::weak_ptr<scene::Session> const& session) const -> SessionInfo& override;
199
200 auto info_for(std::weak_ptr<scene::Surface> const& surface) const -> SurfaceInfo& override;
201
202 std::shared_ptr<scene::Session> focused_session() const override;
203
204 std::shared_ptr<scene::Surface> focused_surface() const override;
205
206 void focus_next_session() override;
207
208 void set_focus_to(
209 std::shared_ptr<scene::Session> const& focus,
210 std::shared_ptr<scene::Surface> const& surface) override;
211
212 auto surface_at(geometry::Point cursor) const -> std::shared_ptr<scene::Surface> override;
213
214 auto active_display() -> geometry::Rectangle const override;
215
216 void raise_tree(std::shared_ptr<scene::Surface> const& root) override;
217
218private:
219 shell::FocusController* const focus_controller;
220 std::unique_ptr<WindowManagementPolicy> const policy;
221
222 std::mutex mutex;
223 SessionInfoMap session_info;
224 SurfaceInfoMap surface_info;
225 geometry::Rectangles displays;
226 geometry::Point cursor;
227 uint64_t last_input_event_timestamp{0};
228
229 void update_event_timestamp(MirKeyboardEvent const* kev);
230 void update_event_timestamp(MirPointerEvent const* pev);
231 void update_event_timestamp(MirTouchEvent const* tev);
232};
233
234/// A policy based window manager. This exists to initialize BasicWindowManager and
235/// the WMPolicy (in an awkward manner).
236/// TODO revisit this initialization sequence.
237template<typename WMPolicy>
238class WindowManagerBuilder : public BasicWindowManager
239{
240public:
241
242 template <typename... PolicyArgs>
243 WindowManagerBuilder(
244 shell::FocusController* focus_controller,
245 PolicyArgs&&... policy_args) :
246 BasicWindowManager(
247 focus_controller,
248 build_policy(std::forward<PolicyArgs>(policy_args)...))
249 {
250 }
251
252private:
253 template <typename... PolicyArgs>
254 auto build_policy(PolicyArgs&&... policy_args)
255 -> std::unique_ptr<WMPolicy>
256 {
257 return std::unique_ptr<WMPolicy>(
258 new WMPolicy(this, std::forward<PolicyArgs>(policy_args)...));
259 }
260};
261}
262}
263
264#endif /* MIR_EXAMPLE_BASIC_WINDOW_MANAGER_H_ */
2650
=== removed file 'examples/server_example_canonical_window_manager.cpp'
--- examples/server_example_canonical_window_manager.cpp 2017-07-28 17:00:43 +0000
+++ examples/server_example_canonical_window_manager.cpp 1970-01-01 00:00:00 +0000
@@ -1,1007 +0,0 @@
1/*
2 * Copyright © 2015 Canonical Ltd.
3 *
4 * This program is free software: you can redistribute it and/or modify it
5 * under the terms of the GNU General Public License version 2 or 3,
6 * as published by the Free Software Foundation.
7 *
8 * This program is distributed in the hope that it will be useful,
9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 * GNU General Public License for more details.
12 *
13 * You should have received a copy of the GNU General Public License
14 * along with this program. If not, see <http://www.gnu.org/licenses/>.
15 *
16 * Authored By: Alan Griffiths <alan@octopull.co.uk>
17 */
18
19#include "server_example_canonical_window_manager.h"
20
21#include "mir/scene/session.h"
22#include "mir/scene/surface.h"
23#include "mir/scene/surface_creation_parameters.h"
24#include "mir/shell/surface_ready_observer.h"
25#include "mir/shell/display_layout.h"
26
27#include <linux/input.h>
28#include <csignal>
29#include <algorithm>
30
31namespace me = mir::examples;
32namespace ms = mir::scene;
33using namespace mir::geometry;
34
35///\example server_example_canonical_window_manager.cpp
36// Based on "Mir and Unity: Surfaces, input, and displays (v0.3)"
37
38namespace
39{
40int const title_bar_height = 10;
41Size titlebar_size_for_window(Size window_size)
42{
43 return {window_size.width, Height{title_bar_height}};
44}
45
46Point titlebar_position_for_window(Point window_position)
47{
48 return {
49 window_position.x,
50 window_position.y - DeltaY(title_bar_height)
51 };
52}
53}
54
55me::CanonicalWindowManagerPolicyCopy::CanonicalWindowManagerPolicyCopy(
56 WindowManagerTools* const tools,
57 std::shared_ptr<shell::DisplayLayout> const& display_layout,
58 std::shared_ptr<graphics::GraphicBufferAllocator> const& allocator) :
59 tools{tools},
60 display_layout{display_layout},
61 allocator{allocator}
62{
63}
64
65void me::CanonicalWindowManagerPolicyCopy::click(Point cursor)
66{
67 if (auto const surface = tools->surface_at(cursor))
68 select_active_surface(surface);
69}
70
71void me::CanonicalWindowManagerPolicyCopy::handle_session_info_updated(SessionInfoMap& /*session_info*/, Rectangles const& /*displays*/)
72{
73}
74
75void me::CanonicalWindowManagerPolicyCopy::handle_displays_updated(SessionInfoMap& /*session_info*/, Rectangles const& displays)
76{
77 display_area = displays.bounding_rectangle();
78
79 for (auto const weak_surface : fullscreen_surfaces)
80 {
81 if (auto const surface = weak_surface.lock())
82 {
83 auto const& info = tools->info_for(weak_surface);
84 Rectangle rect{surface->top_left(), surface->size()};
85
86 display_layout->place_in_output(info.output_id.value(), rect);
87 surface->move_to(rect.top_left);
88 surface->resize(rect.size);
89 }
90 }
91}
92
93void me::CanonicalWindowManagerPolicyCopy::resize(Point cursor)
94{
95 if (!resizing) select_active_surface(tools->surface_at(old_cursor));
96 resize(active_surface(), cursor, old_cursor, display_area);
97}
98
99auto me::CanonicalWindowManagerPolicyCopy::handle_place_new_surface(
100 std::shared_ptr<ms::Session> const& session,
101 ms::SurfaceCreationParameters const& request_parameters)
102-> ms::SurfaceCreationParameters
103{
104 auto parameters = request_parameters;
105 auto surf_type = parameters.type.is_set() ? parameters.type.value() : mir_window_type_normal;
106 bool const needs_titlebar = SurfaceInfo::needs_titlebar(surf_type);
107
108 if (needs_titlebar)
109 parameters.size.height = parameters.size.height + DeltaY{title_bar_height};
110
111 if (!parameters.state.is_set())
112 parameters.state = mir_window_state_restored;
113
114 auto const active_display = tools->active_display();
115
116 auto const width = parameters.size.width.as_int();
117 auto const height = parameters.size.height.as_int();
118
119 bool positioned = false;
120
121 auto const parent = parameters.parent.lock();
122
123 if (parameters.output_id != mir::graphics::DisplayConfigurationOutputId{0})
124 {
125 Rectangle rect{parameters.top_left, parameters.size};
126 display_layout->place_in_output(parameters.output_id, rect);
127 parameters.top_left = rect.top_left;
128 parameters.size = rect.size;
129 parameters.state = mir_window_state_fullscreen;
130 positioned = true;
131 }
132 else if (!parent) // No parent => client can't suggest positioning
133 {
134 if (auto const default_surface = session->default_surface())
135 {
136 static Displacement const offset{title_bar_height, title_bar_height};
137
138 parameters.top_left = default_surface->top_left() + offset;
139
140 geometry::Rectangle display_for_app{default_surface->top_left(), default_surface->size()};
141
142 display_layout->size_to_output(display_for_app);
143
144 positioned = display_for_app.overlaps(Rectangle{parameters.top_left, parameters.size});
145 }
146 }
147
148 if (parent && parameters.aux_rect.is_set() && parameters.edge_attachment.is_set())
149 {
150 auto const edge_attachment = parameters.edge_attachment.value();
151 auto const aux_rect = parameters.aux_rect.value();
152 auto const parent_top_left = parent->top_left();
153 auto const top_left = aux_rect.top_left -Point{} + parent_top_left;
154 auto const top_right= aux_rect.top_right() -Point{} + parent_top_left;
155 auto const bot_left = aux_rect.bottom_left()-Point{} + parent_top_left;
156
157 if (edge_attachment & mir_edge_attachment_vertical)
158 {
159 if (active_display.contains(top_right + Displacement{width, height}))
160 {
161 parameters.top_left = top_right;
162 positioned = true;
163 }
164 else if (active_display.contains(top_left + Displacement{-width, height}))
165 {
166 parameters.top_left = top_left + Displacement{-width, 0};
167 positioned = true;
168 }
169 }
170
171 if (edge_attachment & mir_edge_attachment_horizontal)
172 {
173 if (active_display.contains(bot_left + Displacement{width, height}))
174 {
175 parameters.top_left = bot_left;
176 positioned = true;
177 }
178 else if (active_display.contains(top_left + Displacement{width, -height}))
179 {
180 parameters.top_left = top_left + Displacement{0, -height};
181 positioned = true;
182 }
183 }
184 }
185 else if (parent)
186 {
187 // o Otherwise, if the dialog is not the same as any previous dialog for the
188 // same parent window, and/or it does not have user-customized position:
189 // o It should be optically centered relative to its parent, unless this
190 // would overlap or cover the title bar of the parent.
191 // o Otherwise, it should be cascaded vertically (but not horizontally)
192 // relative to its parent, unless, this would cause at least part of
193 // it to extend into shell space.
194 auto const parent_top_left = parent->top_left();
195 auto const centred = parent_top_left
196 + 0.5*(as_displacement(parent->size()) - as_displacement(parameters.size))
197 - DeltaY{(parent->size().height.as_int()-height)/6};
198
199 parameters.top_left = centred;
200 positioned = true;
201 }
202
203 if (!positioned)
204 {
205 auto const centred = active_display.top_left
206 + 0.5*(as_displacement(active_display.size) - as_displacement(parameters.size))
207 - DeltaY{(active_display.size.height.as_int()-height)/6};
208
209 switch (parameters.state.value())
210 {
211 case mir_window_state_fullscreen:
212 case mir_window_state_maximized:
213 parameters.top_left = active_display.top_left;
214 parameters.size = active_display.size;
215 break;
216
217 case mir_window_state_vertmaximized:
218 parameters.top_left = centred;
219 parameters.top_left.y = active_display.top_left.y;
220 parameters.size.height = active_display.size.height;
221 break;
222
223 case mir_window_state_horizmaximized:
224 parameters.top_left = centred;
225 parameters.top_left.x = active_display.top_left.x;
226 parameters.size.width = active_display.size.width;
227 break;
228
229 default:
230 parameters.top_left = centred;
231 }
232
233 if (parameters.top_left.y < display_area.top_left.y)
234 parameters.top_left.y = display_area.top_left.y;
235 }
236
237 if (parameters.state != mir_window_state_fullscreen && needs_titlebar)
238 {
239 parameters.top_left.y = parameters.top_left.y + DeltaY{title_bar_height};
240 parameters.size.height = parameters.size.height - DeltaY{title_bar_height};
241 }
242
243 return parameters;
244}
245
246void me::CanonicalWindowManagerPolicyCopy::generate_decorations_for(
247 std::shared_ptr<scene::Session> const& session,
248 std::shared_ptr<scene::Surface> const& surface,
249 SurfaceInfoMap& surface_map,
250 std::function<frontend::SurfaceId(std::shared_ptr<scene::Session> const& session, scene::SurfaceCreationParameters const& params)> const& build)
251{
252 if (!SurfaceInfo::needs_titlebar(surface->type()))
253 return;
254
255 auto format = mir_pixel_format_xrgb_8888;
256 mir::graphics::BufferProperties properties(titlebar_size_for_window(surface->size()),
257 format, mir::graphics::BufferUsage::software);
258 auto stream_id = session->create_buffer_stream(properties);
259 auto params = ms::a_surface()
260 .of_size(titlebar_size_for_window(surface->size()))
261 .of_name("decoration")
262 .of_pixel_format(format)
263 .of_buffer_usage(mir::graphics::BufferUsage::software)
264 .of_position(titlebar_position_for_window(surface->top_left()))
265 .of_type(mir_window_type_gloss)
266 .with_buffer_stream(stream_id);
267 auto id = build(session, params);
268 auto titlebar = session->surface(id);
269 titlebar->set_alpha(0.9);
270
271 auto& surface_info = tools->info_for(surface);
272 surface_info.titlebar = titlebar;
273 surface_info.titlebar_id = id;
274 surface_info.titlebar_stream_id = stream_id;
275 surface_info.children.push_back(titlebar);
276
277 SurfaceInfo& titlebar_info =
278 surface_map.emplace(titlebar, SurfaceInfo{session, titlebar, {}}).first->second;
279 titlebar_info.is_titlebar = true;
280 titlebar_info.parent = surface;
281 titlebar_info.init_titlebar(*allocator, titlebar);
282}
283
284void me::CanonicalWindowManagerPolicyCopy::handle_new_surface(std::shared_ptr<ms::Session> const& session, std::shared_ptr<ms::Surface> const& surface)
285{
286 auto& surface_info = tools->info_for(surface);
287 if (auto const parent = surface_info.parent.lock())
288 {
289 tools->info_for(parent).children.push_back(surface);
290 }
291
292 tools->info_for(session).surfaces.push_back(surface);
293
294 if (surface_info.can_be_active())
295 {
296 surface->add_observer(std::make_shared<shell::SurfaceReadyObserver>(
297 [this](std::shared_ptr<scene::Session> const& /*session*/,
298 std::shared_ptr<scene::Surface> const& surface)
299 {
300 select_active_surface(surface);
301 },
302 session,
303 surface));
304 }
305
306 if (surface_info.state == mir_window_state_fullscreen)
307 fullscreen_surfaces.insert(surface);
308}
309
310void me::CanonicalWindowManagerPolicyCopy::handle_modify_surface(
311 std::shared_ptr<scene::Session> const& session,
312 std::shared_ptr<scene::Surface> const& surface,
313 shell::SurfaceSpecification const& modifications)
314{
315 auto& surface_info_old = tools->info_for(surface);
316
317 auto surface_info = surface_info_old;
318
319 if (modifications.parent.is_set())
320 surface_info.parent = modifications.parent.value();
321
322 if (modifications.type.is_set() &&
323 surface_info.type != modifications.type.value())
324 {
325 auto const new_type = modifications.type.value();
326
327 if (!surface_info.can_morph_to(new_type))
328 {
329 throw std::runtime_error("Unsupported surface type change");
330 }
331
332 surface_info.type = new_type;
333
334 if (surface_info.must_not_have_parent())
335 {
336 if (modifications.parent.is_set())
337 throw std::runtime_error("Target surface type does not support parent");
338
339 surface_info.parent.reset();
340 }
341 else if (surface_info.must_have_parent())
342 {
343 if (!surface_info.parent.lock())
344 throw std::runtime_error("Target surface type requires parent");
345 }
346
347 surface->configure(mir_window_attrib_type, new_type);
348 }
349
350 #define COPY_IF_SET(field)\
351 if (modifications.field.is_set())\
352 surface_info.field = modifications.field.value()
353
354 COPY_IF_SET(min_width);
355 COPY_IF_SET(min_height);
356 COPY_IF_SET(max_width);
357 COPY_IF_SET(max_height);
358 COPY_IF_SET(min_width);
359 COPY_IF_SET(width_inc);
360 COPY_IF_SET(height_inc);
361 COPY_IF_SET(min_aspect);
362 COPY_IF_SET(max_aspect);
363 COPY_IF_SET(output_id);
364
365 #undef COPY_IF_SET
366
367 std::swap(surface_info, surface_info_old);
368
369 if (modifications.name.is_set())
370 surface->rename(modifications.name.value());
371
372 if (modifications.streams.is_set())
373 {
374 auto v = modifications.streams.value();
375 std::vector<shell::StreamSpecification> l (v.begin(), v.end());
376 session->configure_streams(*surface, l);
377 }
378
379 if (modifications.width.is_set() || modifications.height.is_set())
380 {
381 auto new_size = surface->size();
382
383 if (modifications.width.is_set())
384 new_size.width = modifications.width.value();
385
386 if (modifications.height.is_set())
387 new_size.height = modifications.height.value();
388
389 auto top_left = surface->top_left();
390
391 surface_info.constrain_resize(
392 surface,
393 top_left,
394 new_size,
395 false,
396 false,
397 display_area);
398
399 apply_resize(surface, surface_info.titlebar, top_left, new_size);
400 }
401
402 if (modifications.input_shape.is_set())
403 {
404 auto rectangles = modifications.input_shape.value();
405 auto displacement = surface->top_left() - Point{0, 0};
406 for(auto& rect : rectangles)
407 {
408 rect.top_left = rect.top_left + displacement;
409 rect = rect.intersection_with({surface->top_left(), surface->size()});
410 rect.top_left = rect.top_left - displacement;
411 }
412 surface->set_input_region(rectangles);
413 }
414
415
416 if (modifications.state.is_set())
417 {
418 auto const state = handle_set_state(surface, modifications.state.value());
419 surface->configure(mir_window_attrib_state, state);
420 }
421
422 if (modifications.confine_pointer.is_set())
423 {
424 surface->set_confine_pointer_state(modifications.confine_pointer.value());
425 }
426}
427
428void me::CanonicalWindowManagerPolicyCopy::handle_delete_surface(std::shared_ptr<ms::Session> const& session, std::weak_ptr<ms::Surface> const& surface)
429{
430 fullscreen_surfaces.erase(surface);
431 bool const is_active_surface{surface.lock() == active_surface()};
432
433 auto& info = tools->info_for(surface);
434
435 if (auto const parent = info.parent.lock())
436 {
437 auto& siblings = tools->info_for(parent).children;
438
439 for (auto i = begin(siblings); i != end(siblings); ++i)
440 {
441 if (surface.lock() == i->lock())
442 {
443 siblings.erase(i);
444 break;
445 }
446 }
447 }
448
449 session->destroy_surface(surface);
450 if (info.titlebar)
451 {
452 session->destroy_surface(info.titlebar_id);
453 session->destroy_buffer_stream(info.titlebar_stream_id);
454 tools->forget(info.titlebar);
455 }
456
457 auto& surfaces = tools->info_for(session).surfaces;
458
459 for (auto i = begin(surfaces); i != end(surfaces); ++i)
460 {
461 if (surface.lock() == i->lock())
462 {
463 surfaces.erase(i);
464 break;
465 }
466 }
467
468 if (is_active_surface)
469 {
470 active_surface_.reset();
471
472 if (surfaces.empty())
473 {
474 tools->focus_next_session();
475 select_active_surface(tools->focused_surface());
476 }
477 else
478 {
479 select_active_surface(surfaces[0].lock());
480 }
481 }
482}
483
484int me::CanonicalWindowManagerPolicyCopy::handle_set_state(std::shared_ptr<ms::Surface> const& surface, MirWindowState value)
485{
486 auto& info = tools->info_for(surface);
487
488 switch (value)
489 {
490 case mir_window_state_restored:
491 case mir_window_state_maximized:
492 case mir_window_state_vertmaximized:
493 case mir_window_state_horizmaximized:
494 case mir_window_state_fullscreen:
495 case mir_window_state_hidden:
496 case mir_window_state_minimized:
497 break;
498
499 default:
500 return info.state;
501 }
502
503 if (info.state == mir_window_state_restored)
504 {
505 info.restore_rect = {surface->top_left(), surface->size()};
506 }
507
508 if (info.state != mir_window_state_fullscreen)
509 {
510 info.output_id = decltype(info.output_id){};
511 fullscreen_surfaces.erase(surface);
512 }
513 else
514 {
515 fullscreen_surfaces.insert(surface);
516 }
517
518 if (info.state == value)
519 {
520 return info.state;
521 }
522
523 auto const old_pos = surface->top_left();
524 Displacement movement;
525
526 switch (value)
527 {
528 case mir_window_state_restored:
529 movement = info.restore_rect.top_left - old_pos;
530 surface->resize(info.restore_rect.size);
531 if (info.titlebar)
532 {
533 info.titlebar->resize(titlebar_size_for_window(info.restore_rect.size));
534 info.titlebar->show();
535 }
536 break;
537
538 case mir_window_state_maximized:
539 movement = display_area.top_left - old_pos;
540 surface->resize(display_area.size);
541 if (info.titlebar)
542 info.titlebar->hide();
543 break;
544
545 case mir_window_state_horizmaximized:
546 movement = Point{display_area.top_left.x, info.restore_rect.top_left.y} - old_pos;
547 surface->resize({display_area.size.width, info.restore_rect.size.height});
548 if (info.titlebar)
549 {
550 info.titlebar->resize(titlebar_size_for_window({display_area.size.width, info.restore_rect.size.height}));
551 info.titlebar->show();
552 }
553 break;
554
555 case mir_window_state_vertmaximized:
556 movement = Point{info.restore_rect.top_left.x, display_area.top_left.y} - old_pos;
557 surface->resize({info.restore_rect.size.width, display_area.size.height});
558 if (info.titlebar)
559 info.titlebar->hide();
560 break;
561
562 case mir_window_state_fullscreen:
563 {
564 Rectangle rect{old_pos, surface->size()};
565
566 if (info.output_id.is_set())
567 {
568 display_layout->place_in_output(info.output_id.value(), rect);
569 }
570 else
571 {
572 display_layout->size_to_output(rect);
573 }
574
575 movement = rect.top_left - old_pos;
576 surface->resize(rect.size);
577 break;
578 }
579
580 case mir_window_state_hidden:
581 case mir_window_state_minimized:
582 if (info.titlebar)
583 info.titlebar->hide();
584 surface->hide();
585 return info.state = value;
586
587 default:
588 break;
589 }
590
591 // TODO It is rather simplistic to move a tree WRT the top_left of the root
592 // TODO when resizing. But for more sophistication we would need to encode
593 // TODO some sensible layout rules.
594 move_tree(surface, movement);
595
596 info.state = value;
597
598 if (info.is_visible())
599 surface->show();
600
601 return info.state;
602}
603
604void me::CanonicalWindowManagerPolicyCopy::drag(Point cursor)
605{
606 select_active_surface(tools->surface_at(old_cursor));
607 drag(active_surface(), cursor, old_cursor, display_area);
608}
609
610void me::CanonicalWindowManagerPolicyCopy::handle_raise_surface(
611 std::shared_ptr<ms::Session> const& /*session*/,
612 std::shared_ptr<ms::Surface> const& surface)
613{
614 select_active_surface(surface);
615}
616
617bool me::CanonicalWindowManagerPolicyCopy::handle_keyboard_event(MirKeyboardEvent const* event)
618{
619 auto const action = mir_keyboard_event_action(event);
620 auto const scan_code = mir_keyboard_event_scan_code(event);
621 auto const modifiers = mir_keyboard_event_modifiers(event) & modifier_mask;
622
623 if (action == mir_keyboard_action_down && scan_code == KEY_F11)
624 {
625 switch (modifiers)
626 {
627 case mir_input_event_modifier_alt:
628 toggle(mir_window_state_maximized);
629 return true;
630
631 case mir_input_event_modifier_shift:
632 toggle(mir_window_state_vertmaximized);
633 return true;
634
635 case mir_input_event_modifier_ctrl:
636 toggle(mir_window_state_horizmaximized);
637 return true;
638
639 default:
640 break;
641 }
642 }
643 else if (action == mir_keyboard_action_down && scan_code == KEY_F4)
644 {
645 if (auto const session = tools->focused_session())
646 {
647 switch (modifiers)
648 {
649 case mir_input_event_modifier_alt:
650 kill(session->process_id(), SIGTERM);
651 return true;
652
653 case mir_input_event_modifier_ctrl:
654 if (auto const surf = session->default_surface())
655 {
656 surf->request_client_surface_close();
657 return true;
658 }
659
660 default:
661 break;
662 }
663 }
664 }
665 else if (action == mir_keyboard_action_down &&
666 modifiers == mir_input_event_modifier_alt &&
667 scan_code == KEY_TAB)
668 {
669 tools->focus_next_session();
670 if (auto const surface = tools->focused_surface())
671 select_active_surface(surface);
672
673 return true;
674 }
675 else if (action == mir_keyboard_action_down &&
676 modifiers == mir_input_event_modifier_alt &&
677 scan_code == KEY_GRAVE)
678 {
679 if (auto const prev = tools->focused_surface())
680 {
681 if (auto const app = tools->focused_session())
682 select_active_surface(app->surface_after(prev));
683 }
684
685 return true;
686 }
687
688 return false;
689}
690
691bool me::CanonicalWindowManagerPolicyCopy::handle_touch_event(MirTouchEvent const* event)
692{
693 auto const count = mir_touch_event_point_count(event);
694
695 long total_x = 0;
696 long total_y = 0;
697
698 for (auto i = 0U; i != count; ++i)
699 {
700 total_x += mir_touch_event_axis_value(event, i, mir_touch_axis_x);
701 total_y += mir_touch_event_axis_value(event, i, mir_touch_axis_y);
702 }
703
704 Point const cursor{total_x/count, total_y/count};
705
706 bool is_drag = true;
707 for (auto i = 0U; i != count; ++i)
708 {
709 switch (mir_touch_event_action(event, i))
710 {
711 case mir_touch_action_up:
712 return false;
713
714 case mir_touch_action_down:
715 is_drag = false;
716 // fallthrough
717 case mir_touch_action_change:
718 continue;
719
720 case mir_touch_actions:
721 abort();
722 break;
723 }
724 }
725
726 bool consumes_event = false;
727 if (is_drag)
728 {
729 switch (count)
730 {
731 case 4:
732 resize(cursor);
733 consumes_event = true;
734 break;
735
736 case 3:
737 drag(cursor);
738 consumes_event = true;
739 break;
740 }
741 }
742
743 old_cursor = cursor;
744 return consumes_event;
745}
746
747bool me::CanonicalWindowManagerPolicyCopy::handle_pointer_event(MirPointerEvent const* event)
748{
749 auto const action = mir_pointer_event_action(event);
750 auto const modifiers = mir_pointer_event_modifiers(event) & modifier_mask;
751 Point const cursor{
752 mir_pointer_event_axis_value(event, mir_pointer_axis_x),
753 mir_pointer_event_axis_value(event, mir_pointer_axis_y)};
754
755 bool consumes_event = false;
756 bool resize_event = false;
757
758 if (action == mir_pointer_action_button_down)
759 {
760 click(cursor);
761 }
762 else if (action == mir_pointer_action_motion &&
763 modifiers == mir_input_event_modifier_alt)
764 {
765 if (mir_pointer_event_button_state(event, mir_pointer_button_primary))
766 {
767 drag(cursor);
768 consumes_event = true;
769 }
770
771 if (mir_pointer_event_button_state(event, mir_pointer_button_tertiary))
772 {
773 resize(cursor);
774 resize_event = active_surface_.lock().get();
775 consumes_event = true;
776 }
777 }
778 else if (action == mir_pointer_action_motion && !modifiers)
779 {
780 if (mir_pointer_event_button_state(event, mir_pointer_button_primary))
781 {
782 if (auto const possible_titlebar = tools->surface_at(old_cursor))
783 {
784 if (tools->info_for(possible_titlebar).is_titlebar)
785 {
786 drag(cursor);
787 consumes_event = true;
788 }
789 }
790 }
791 }
792
793 resizing = resize_event;
794 old_cursor = cursor;
795 return consumes_event;
796}
797
798void me::CanonicalWindowManagerPolicyCopy::toggle(MirWindowState state)
799{
800 if (auto const surface = active_surface())
801 {
802 auto& info = tools->info_for(surface);
803
804 if (info.state == state)
805 state = mir_window_state_restored;
806
807 auto const value = handle_set_state(surface, MirWindowState(state));
808 surface->configure(mir_window_attrib_state, value);
809 }
810}
811
812void me::CanonicalWindowManagerPolicyCopy::select_active_surface(std::shared_ptr<ms::Surface> const& surface)
813{
814 if (surface == active_surface_.lock())
815 return;
816
817 if (!surface)
818 {
819 if (auto const active_surface = active_surface_.lock())
820 {
821 if (auto const titlebar = tools->info_for(active_surface).titlebar)
822 {
823 tools->info_for(titlebar).paint_titlebar(0x3F);
824 }
825 }
826
827 if (active_surface_.lock())
828 tools->set_focus_to({}, {});
829
830 active_surface_.reset();
831 return;
832 }
833
834 auto const& info_for = tools->info_for(surface);
835
836 if (info_for.can_be_active())
837 {
838 if (auto const active_surface = active_surface_.lock())
839 {
840 if (auto const titlebar = tools->info_for(active_surface).titlebar)
841 {
842 tools->info_for(titlebar).paint_titlebar(0x3F);
843 }
844 }
845 if (auto const titlebar = tools->info_for(surface).titlebar)
846 {
847 tools->info_for(titlebar).paint_titlebar(0xFF);
848 }
849 tools->set_focus_to(info_for.session.lock(), surface);
850 tools->raise_tree(surface);
851 active_surface_ = surface;
852 }
853 else
854 {
855 // Cannot have input focus - try the parent
856 if (auto const parent = info_for.parent.lock())
857 select_active_surface(parent);
858 }
859}
860
861auto me::CanonicalWindowManagerPolicyCopy::active_surface() const
862-> std::shared_ptr<ms::Surface>
863{
864 if (auto const surface = active_surface_.lock())
865 return surface;
866
867 if (auto const session = tools->focused_session())
868 {
869 if (auto const surface = session->default_surface())
870 return surface;
871 }
872
873 return std::shared_ptr<ms::Surface>{};
874}
875
876bool me::CanonicalWindowManagerPolicyCopy::resize(std::shared_ptr<scene::Surface> const& surface, Point cursor, Point old_cursor, Rectangle bounds)
877{
878 if (!surface)
879 return false;
880
881 auto const& surface_info = tools->info_for(surface);
882
883 auto const top_left = surface->top_left();
884 Rectangle const old_pos{top_left, surface->size()};
885
886 if (!resizing)
887 {
888 auto anchor = old_pos.bottom_right();
889
890 for (auto const& corner : {
891 old_pos.top_right(),
892 old_pos.bottom_left(),
893 top_left})
894 {
895 if ((old_cursor - anchor).length_squared() <
896 (old_cursor - corner).length_squared())
897 {
898 anchor = corner;
899 }
900 }
901
902 left_resize = anchor.x != top_left.x;
903 top_resize = anchor.y != top_left.y;
904 }
905
906 int const x_sign = left_resize? -1 : 1;
907 int const y_sign = top_resize? -1 : 1;
908
909 auto delta = cursor-old_cursor;
910
911 auto new_width = old_pos.size.width + x_sign * delta.dx;
912 auto new_height = old_pos.size.height + y_sign * delta.dy;
913
914 auto const min_width = std::max(surface_info.min_width, Width{5});
915 auto const min_height = std::max(surface_info.min_height, Height{5});
916
917 if (new_width < min_width)
918 {
919 new_width = min_width;
920 if (delta.dx > DeltaX{0})
921 delta.dx = DeltaX{0};
922 }
923
924 if (new_height < min_height)
925 {
926 new_height = min_height;
927 if (delta.dy > DeltaY{0})
928 delta.dy = DeltaY{0};
929 }
930
931 Size new_size{new_width, new_height};
932 Point new_pos = top_left + left_resize*delta.dx + top_resize*delta.dy;
933
934
935 surface_info.constrain_resize(surface, new_pos, new_size, left_resize, top_resize, bounds);
936
937 apply_resize(surface, surface_info.titlebar, new_pos, new_size);
938
939 return true;
940}
941
942void me::CanonicalWindowManagerPolicyCopy::apply_resize(
943 std::shared_ptr<ms::Surface> const& surface,
944 std::shared_ptr<ms::Surface> const& titlebar,
945 Point const& new_pos,
946 Size const& new_size) const
947{
948 if (titlebar)
949 titlebar->resize({new_size.width, Height{title_bar_height}});
950
951 surface->resize(new_size);
952
953 move_tree(surface, new_pos-surface->top_left());
954}
955
956bool me::CanonicalWindowManagerPolicyCopy::drag(std::shared_ptr<scene::Surface> surface, Point to, Point from, Rectangle /*bounds*/)
957{
958 if (!surface)
959 return false;
960
961 if (!surface->input_area_contains(from) && !tools->info_for(surface).titlebar)
962 return false;
963
964 auto movement = to - from;
965
966 // placeholder - constrain onscreen
967
968 switch (tools->info_for(surface).state)
969 {
970 case mir_window_state_restored:
971 break;
972
973 // "A vertically maximised surface is anchored to the top and bottom of
974 // the available workspace and can have any width."
975 case mir_window_state_vertmaximized:
976 movement.dy = DeltaY(0);
977 break;
978
979 // "A horizontally maximised surface is anchored to the left and right of
980 // the available workspace and can have any height"
981 case mir_window_state_horizmaximized:
982 movement.dx = DeltaX(0);
983 break;
984
985 // "A maximised surface is anchored to the top, bottom, left and right of the
986 // available workspace. For example, if the launcher is always-visible then
987 // the left-edge of the surface is anchored to the right-edge of the launcher."
988 case mir_window_state_maximized:
989 case mir_window_state_fullscreen:
990 default:
991 return true;
992 }
993
994 move_tree(surface, movement);
995
996 return true;
997}
998
999void me::CanonicalWindowManagerPolicyCopy::move_tree(std::shared_ptr<ms::Surface> const& root, Displacement movement) const
1000{
1001 root->move_to(root->top_left() + movement);
1002
1003 for (auto const& child: tools->info_for(root).children)
1004 {
1005 move_tree(child.lock(), movement);
1006 }
1007}
10080
=== removed file 'examples/server_example_canonical_window_manager.h'
--- examples/server_example_canonical_window_manager.h 2017-07-28 17:00:43 +0000
+++ examples/server_example_canonical_window_manager.h 1970-01-01 00:00:00 +0000
@@ -1,142 +0,0 @@
1/*
2 * Copyright © 2015 Canonical Ltd.
3 *
4 * This program is free software: you can redistribute it and/or modify it
5 * under the terms of the GNU General Public License version 2 or 3,
6 * as published by the Free Software Foundation.
7 *
8 * This program is distributed in the hope that it will be useful,
9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 * GNU General Public License for more details.
12 *
13 * You should have received a copy of the GNU General Public License
14 * along with this program. If not, see <http://www.gnu.org/licenses/>.
15 *
16 * Authored By: Alan Griffiths <alan@octopull.co.uk>
17 */
18
19#ifndef MIR_EXAMPLE_CANONICAL_WINDOW_MANAGER_H_
20#define MIR_EXAMPLE_CANONICAL_WINDOW_MANAGER_H_
21
22#include "server_example_basic_window_manager.h"
23
24#include "mir/geometry/displacement.h"
25
26#include <atomic>
27#include <set>
28
29///\example server_example_canonical_window_manager.h
30// Based on "Mir and Unity: Surfaces, input, and displays (v0.3)"
31
32namespace mir
33{
34namespace shell { class DisplayLayout; }
35namespace graphics
36{
37class GraphicBufferAllocator;
38}
39namespace examples
40{
41// standard window management algorithm:
42// o Switch apps: tap or click on the corresponding tile
43// o Move window: Alt-leftmousebutton drag (three finger drag)
44// o Resize window: Alt-middle_button drag (two finger drag)
45// o Maximize/restore current window (to display size): Alt-F11
46// o Maximize/restore current window (to display height): Shift-F11
47// o Maximize/restore current window (to display width): Ctrl-F11
48// o client requests to maximize, vertically maximize & restore
49class CanonicalWindowManagerPolicyCopy : public WindowManagementPolicy
50{
51public:
52
53 explicit CanonicalWindowManagerPolicyCopy(
54 WindowManagerTools* const tools,
55 std::shared_ptr<shell::DisplayLayout> const& display_layout,
56 std::shared_ptr<graphics::GraphicBufferAllocator> const& allocator);
57
58 void click(geometry::Point cursor);
59
60 void handle_session_info_updated(SessionInfoMap& session_info, geometry::Rectangles const& displays);
61
62 void handle_displays_updated(SessionInfoMap& session_info, geometry::Rectangles const& displays);
63
64 void resize(geometry::Point cursor);
65
66 auto handle_place_new_surface(
67 std::shared_ptr<scene::Session> const& session,
68 scene::SurfaceCreationParameters const& request_parameters)
69 -> scene::SurfaceCreationParameters;
70
71 void handle_new_surface(std::shared_ptr<scene::Session> const& session, std::shared_ptr<scene::Surface> const& surface);
72
73 void handle_modify_surface(
74 std::shared_ptr<scene::Session> const& session,
75 std::shared_ptr<scene::Surface> const& surface,
76 shell::SurfaceSpecification const& modifications);
77
78 void handle_delete_surface(std::shared_ptr<scene::Session> const& session, std::weak_ptr<scene::Surface> const& surface);
79
80 int handle_set_state(std::shared_ptr<scene::Surface> const& surface, MirWindowState value);
81
82 void drag(geometry::Point cursor);
83
84 bool handle_keyboard_event(MirKeyboardEvent const* event);
85
86 bool handle_touch_event(MirTouchEvent const* event);
87
88 bool handle_pointer_event(MirPointerEvent const* event);
89
90 void handle_raise_surface(
91 std::shared_ptr<scene::Session> const& session,
92 std::shared_ptr<scene::Surface> const& surface);
93
94 void generate_decorations_for(
95 std::shared_ptr<scene::Session> const& session,
96 std::shared_ptr<scene::Surface> const& surface,
97 SurfaceInfoMap& surface_map,
98 std::function<frontend::SurfaceId(std::shared_ptr<scene::Session> const& session, scene::SurfaceCreationParameters const& params)> const& build);
99
100private:
101 static const int modifier_mask =
102 mir_input_event_modifier_alt |
103 mir_input_event_modifier_shift |
104 mir_input_event_modifier_sym |
105 mir_input_event_modifier_ctrl |
106 mir_input_event_modifier_meta;
107
108 void toggle(MirWindowState state);
109
110 // "Mir and Unity: Surfaces, input, and displays (v0.3)" talks about active
111 // *window*,but Mir really only understands surfaces
112 void select_active_surface(std::shared_ptr<scene::Surface> const& surface);
113 auto active_surface() const -> std::shared_ptr<scene::Surface>;
114
115 bool resize(std::shared_ptr<scene::Surface> const& surface, geometry::Point cursor, geometry::Point old_cursor, geometry::Rectangle bounds);
116 bool drag(std::shared_ptr<scene::Surface> surface, geometry::Point to, geometry::Point from, geometry::Rectangle bounds);
117 void move_tree(std::shared_ptr<scene::Surface> const& root, geometry::Displacement movement) const;
118 void apply_resize(
119 std::shared_ptr<mir::scene::Surface> const& surface,
120 std::shared_ptr<mir::scene::Surface> const& titlebar,
121 geometry::Point const& new_pos,
122 geometry::Size const& new_size) const;
123
124 WindowManagerTools* const tools;
125 std::shared_ptr<shell::DisplayLayout> const display_layout;
126 std::shared_ptr<graphics::GraphicBufferAllocator> const allocator;
127
128 geometry::Rectangle display_area;
129 geometry::Point old_cursor{};
130 std::weak_ptr<scene::Surface> active_surface_;
131 using FullscreenSurfaces = std::set<std::weak_ptr<scene::Surface>, std::owner_less<std::weak_ptr<scene::Surface>>>;
132
133 FullscreenSurfaces fullscreen_surfaces;
134
135 bool resizing = false;
136 bool left_resize = false;
137 bool top_resize = false;
138};
139}
140}
141
142#endif /* MIR_EXAMPLE_CANONICAL_WINDOW_MANAGER_H_ */
1430
=== removed file 'examples/server_example_cursor_images.cpp'
--- examples/server_example_cursor_images.cpp 2017-07-28 17:00:43 +0000
+++ examples/server_example_cursor_images.cpp 1970-01-01 00:00:00 +0000
@@ -1,57 +0,0 @@
1/*
2 * Copyright © 2015 Canonical Ltd.
3 *
4 * This program is free software: you can redistribute it and/or modify it
5 * under the terms of the GNU General Public License version 2 or 3,
6 * as published by the Free Software Foundation.
7 *
8 * This program is distributed in the hope that it will be useful,
9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 * GNU General Public License for more details.
12 *
13 * You should have received a copy of the GNU General Public License
14 * along with this program. If not, see <http://www.gnu.org/licenses/>.
15 *
16 * Authored by: Alan Griffiths <alan@octopull.co.uk>
17 */
18
19#include "server_example_cursor_images.h"
20#include "xcursor_loader.h"
21
22#include "mir/server.h"
23#include "mir/options/option.h"
24
25#include <mir_toolkit/cursors.h>
26
27namespace mi = mir::input;
28
29namespace
30{
31char const* const xcursor_theme = "x-cursor-theme";
32char const* const xcursor_description = "X Cursor theme to load [default, DMZ-White, DMZ-Black, ...]";
33
34bool has_default_cursor(mi::CursorImages& images)
35{
36 if (images.image(mir_default_cursor_name, mi::default_cursor_size))
37 return true;
38 return false;
39}
40}
41
42void mir::examples::add_x_cursor_images(Server& server)
43{
44 server.add_configuration_option(xcursor_theme, xcursor_description, "default");
45
46 server.override_the_cursor_images([&]
47 {
48 auto const theme = server.get_options()->get<std::string>(xcursor_theme);
49
50 std::shared_ptr<mi::CursorImages> const xcursor_loader{std::make_shared<XCursorLoader>(theme)};
51
52 if (has_default_cursor(*xcursor_loader))
53 return xcursor_loader;
54 else
55 return std::shared_ptr<mi::CursorImages>{};
56 });
57}
580
=== removed file 'examples/server_example_cursor_images.h'
--- examples/server_example_cursor_images.h 2017-07-28 17:00:43 +0000
+++ examples/server_example_cursor_images.h 1970-01-01 00:00:00 +0000
@@ -1,33 +0,0 @@
1/*
2 * Copyright © 2015 Canonical Ltd.
3 *
4 * This program is free software: you can redistribute it and/or modify it
5 * under the terms of the GNU General Public License version 2 or 3,
6 * as published by the Free Software Foundation.
7 *
8 * This program is distributed in the hope that it will be useful,
9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 * GNU General Public License for more details.
12 *
13 * You should have received a copy of the GNU General Public License
14 * along with this program. If not, see <http://www.gnu.org/licenses/>.
15 *
16 * Authored by: Alan Griffiths <alan@octopull.co.uk>
17 */
18
19#ifndef MIR_EXAMPLE_CURSOR_IMAGES_H
20#define MIR_EXAMPLE_CURSOR_IMAGES_H
21
22namespace mir
23{
24class Server;
25
26namespace examples
27{
28void add_x_cursor_images(Server& server);
29}
30} // namespace mir
31
32
33#endif //MIR_EXAMPLE_CURSOR_IMAGES_H
340
=== modified file 'examples/server_example_test_client.cpp'
--- examples/server_example_test_client.cpp 2017-07-28 17:00:43 +0000
+++ examples/server_example_test_client.cpp 2017-08-31 08:02:08 +0000
@@ -1,5 +1,5 @@
1/*1/*
2 * Copyright © 2014-2015 Canonical Ltd.2 * Copyright © 2014-2017 Canonical Ltd.
3 *3 *
4 * This program is free software: you can redistribute it and/or modify it4 * This program is free software: you can redistribute it and/or modify it
5 * under the terms of the GNU General Public License version 2 or 3,5 * under the terms of the GNU General Public License version 2 or 3,
@@ -24,11 +24,12 @@
24#include "mir/log.h"24#include "mir/log.h"
25#include "mir/options/option.h"25#include "mir/options/option.h"
2626
27#include <chrono>
28#include <future>
29
27#include <csignal>30#include <csignal>
28#include <sys/wait.h>31#include <sys/wait.h>
2932
30#include <chrono>
31
32namespace me = mir::examples;33namespace me = mir::examples;
33namespace ml = mir::logging;34namespace ml = mir::logging;
3435
@@ -87,7 +88,16 @@
87}88}
88}89}
8990
90void me::add_test_client_option_to(mir::Server& server, me::ClientContext& context)91struct me::TestClientRunner::Self
92{
93 std::unique_ptr<time::Alarm> client_kill_action;
94 std::unique_ptr<time::Alarm> server_stop_action;
95 std::atomic<bool> test_failed;
96};
97
98me::TestClientRunner::TestClientRunner() : self{std::make_shared<Self>()} {}
99
100void me::TestClientRunner::operator()(mir::Server& server)
91{101{
92 static const char* const test_client_opt = "test-client";102 static const char* const test_client_opt = "test-client";
93 static const char* const test_client_descr = "client executable";103 static const char* const test_client_descr = "client executable";
@@ -95,16 +105,16 @@
95 static const char* const test_timeout_opt = "test-timeout";105 static const char* const test_timeout_opt = "test-timeout";
96 static const char* const test_timeout_descr = "Seconds to run before sending SIGTERM to client";106 static const char* const test_timeout_descr = "Seconds to run before sending SIGTERM to client";
97107
98 server.add_configuration_option(test_client_opt, test_client_descr, mir::OptionType::string);108 server.add_configuration_option(test_client_opt, test_client_descr, OptionType::string);
99 server.add_configuration_option(test_timeout_opt, test_timeout_descr, 10);109 server.add_configuration_option(test_timeout_opt, test_timeout_descr, 10);
100110
101 server.add_init_callback([&server, &context]111 server.add_init_callback([&server, self = self]
102 {112 {
103 const auto options = server.get_options();113 const auto options1 = server.get_options();
104114
105 if (options->is_set(test_client_opt))115 if (options1->is_set(test_client_opt))
106 {116 {
107 context.test_failed = true;117 self->test_failed = true;
108118
109 auto const client_fd = server.open_client_socket();119 auto const client_fd = server.open_client_socket();
110120
@@ -120,29 +130,30 @@
120130
121 setenv("MIR_SOCKET", connect_string, 1);131 setenv("MIR_SOCKET", connect_string, 1);
122132
123 auto const client = options->get<std::string>(test_client_opt);133 auto const client = options1->get<std::string>(test_client_opt);
124 execlp(client.c_str(), client.c_str(), static_cast<char const*>(nullptr));134 execlp(client.c_str(), client.c_str(), static_cast<char const*>(nullptr));
135 // If execl() returns then something is badly wrong
125 log(logging::Severity::critical, "mir::examples",136 log(logging::Severity::critical, "mir::examples",
126 "Failed to execute client (%s) error: %s", client.c_str(), strerror(errno));137 "Failed to execute client (%s) error: %s", client.c_str(), strerror(errno));
127 abort(); // If execl() returns then something is badly wrong138 abort();
128 }139 }
129 else if (pid > 0)140 else if (pid > 0)
130 {141 {
131 context.client_kill_action = server.the_main_loop()->create_alarm(142 self->client_kill_action = server.the_main_loop()->create_alarm(
132 [pid]143 [pid]
133 {144 {
134 kill(pid, SIGTERM);145 kill(pid, SIGTERM);
135 });146 });
136147
137 context.server_stop_action = server.the_main_loop()->create_alarm(148 self->server_stop_action = server.the_main_loop()->create_alarm(
138 [pid, &server, &context]()149 [pid, &server, self]()
139 {150 {
140 context.test_failed = !exit_success(pid);151 self->test_failed = !exit_success(pid);
141 server.stop();152 server.stop();
142 });153 });
143154
144 context.client_kill_action->reschedule_in(std::chrono::seconds(options->get<int>(test_timeout_opt)));155 self->client_kill_action->reschedule_in(std::chrono::seconds(options1->get<int>(test_timeout_opt)));
145 context.server_stop_action->reschedule_in(std::chrono::seconds(options->get<int>(test_timeout_opt)+1));156 self->server_stop_action->reschedule_in(std::chrono::seconds(options1->get<int>(test_timeout_opt) + 1));
146 }157 }
147 else158 else
148 {159 {
@@ -151,7 +162,9 @@
151 }162 }
152 else163 else
153 {164 {
154 context.test_failed = false;165 self->test_failed = false;
155 }166 }
156 });167 });
157}168}
169
170bool me::TestClientRunner::test_failed() const { return self->test_failed; }
158171
=== modified file 'examples/server_example_test_client.h'
--- examples/server_example_test_client.h 2017-07-28 17:00:43 +0000
+++ examples/server_example_test_client.h 2017-08-31 08:02:08 +0000
@@ -20,9 +20,6 @@
20#define MIR_EXAMPLE_TEST_CLIENT_H_20#define MIR_EXAMPLE_TEST_CLIENT_H_
2121
22#include <memory>22#include <memory>
23#include <future>
24
25#include "mir/main_loop.h"
2623
27namespace mir24namespace mir
28{25{
@@ -30,14 +27,21 @@
3027
31namespace examples28namespace examples
32{29{
33struct ClientContext30
31class TestClientRunner
34{32{
35 std::unique_ptr<mir::time::Alarm> client_kill_action;33public:
36 std::unique_ptr<mir::time::Alarm> server_stop_action;34 TestClientRunner();
37 std::atomic<bool> test_failed;35
36 void operator()(mir::Server& server);
37
38 bool test_failed() const;
39
40private:
41 struct Self;
42 std::shared_ptr<Self> self;
38};43};
3944
40void add_test_client_option_to(mir::Server& server, ClientContext& context);
41}45}
42}46}
4347
4448
=== removed file 'examples/server_example_window_management.cpp'
--- examples/server_example_window_management.cpp 2017-07-28 17:00:43 +0000
+++ examples/server_example_window_management.cpp 1970-01-01 00:00:00 +0000
@@ -1,154 +0,0 @@
1/*
2 * Copyright © 2014 Canonical Ltd.
3 *
4 * This program is free software: you can redistribute it and/or modify it
5 * under the terms of the GNU General Public License version 2 or 3,
6 * as published by the Free Software Foundation.
7 *
8 * This program is distributed in the hope that it will be useful,
9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 * GNU General Public License for more details.
12 *
13 * You should have received a copy of the GNU General Public License
14 * along with this program. If not, see <http://www.gnu.org/licenses/>.
15 *
16 * Authored By: Alan Griffiths <alan@octopull.co.uk>
17 */
18
19#include "server_example_window_management.h"
20
21#include "server_example_canonical_window_manager.h"
22
23#include "mir/abnormal_exit.h"
24#include "mir/server.h"
25#include "mir/input/composite_event_filter.h"
26#include "mir/options/option.h"
27#include "mir/scene/session.h"
28#include "mir/scene/surface_creation_parameters.h"
29#include "mir/shell/display_layout.h"
30#include "mir/shell/system_compositor_window_manager.h"
31#include "mir/graphics/platform.h"
32#include "mir/graphics/graphic_buffer_allocator.h"
33
34namespace me = mir::examples;
35namespace mf = mir::frontend;
36namespace mg = mir::graphics;
37namespace mi = mir::input;
38namespace ms = mir::scene;
39namespace msh = mir::shell;
40using namespace mir::geometry;
41
42///\example server_example_window_management.cpp
43/// Demonstrate introducing a window management strategy
44
45namespace
46{
47char const* const wm_option = "window-manager";
48char const* const wm_description = "window management strategy [{canonical|fullscreen|system-compositor}]";
49
50char const* const wm_fullscreen = "fullscreen";
51char const* const wm_canonical = "canonical";
52char const* const wm_system_compositor = "system-compositor";
53
54// Very simple - make every surface fullscreen
55class FullscreenWindowManagerPolicy : public me::WindowManagementPolicy
56{
57public:
58 FullscreenWindowManagerPolicy(me::WindowManagerTools* const /*tools*/, std::shared_ptr<msh::DisplayLayout> const& display_layout) :
59 display_layout{display_layout} {}
60
61 void handle_session_info_updated(SessionInfoMap& /*session_info*/, Rectangles const& /*displays*/) override {}
62
63 void handle_displays_updated(SessionInfoMap& /*session_info*/, Rectangles const& /*displays*/) override {}
64
65 auto handle_place_new_surface(
66 std::shared_ptr<ms::Session> const& /*session*/,
67 ms::SurfaceCreationParameters const& request_parameters)
68 -> ms::SurfaceCreationParameters override
69 {
70 auto placed_parameters = request_parameters;
71
72 Rectangle rect{request_parameters.top_left, request_parameters.size};
73 display_layout->size_to_output(rect);
74 placed_parameters.size = rect.size;
75
76 return placed_parameters;
77 }
78 void handle_modify_surface(
79 std::shared_ptr<ms::Session> const& /*session*/,
80 std::shared_ptr<ms::Surface> const& /*surface*/,
81 msh::SurfaceSpecification const& /*modifications*/) override
82 {
83 }
84
85 void handle_new_surface(std::shared_ptr<ms::Session> const& /*session*/, std::shared_ptr<ms::Surface> const& /*surface*/) override
86 {
87 }
88
89 void handle_delete_surface(std::shared_ptr<ms::Session> const& session, std::weak_ptr<ms::Surface> const& surface) override
90 { session->destroy_surface(surface); }
91
92 int handle_set_state(std::shared_ptr<ms::Surface> const& /*surface*/, MirWindowState value) override
93 { return value; }
94
95 bool handle_keyboard_event(MirKeyboardEvent const* /*event*/) override { return false; }
96
97 bool handle_touch_event(MirTouchEvent const* /*event*/) override { return false; }
98
99 bool handle_pointer_event(MirPointerEvent const* /*event*/) override { return false; }
100
101 void handle_raise_surface(
102 std::shared_ptr<ms::Session> const& /*session*/,
103 std::shared_ptr<ms::Surface> const& /*surface*/) override
104 {
105 }
106
107 void generate_decorations_for(
108 std::shared_ptr<ms::Session> const&,
109 std::shared_ptr<ms::Surface> const&,
110 SurfaceInfoMap&,
111 std::function<mf::SurfaceId(std::shared_ptr<ms::Session> const&, ms::SurfaceCreationParameters const&)> const&) override
112 {
113 }
114private:
115 std::shared_ptr<msh::DisplayLayout> const display_layout;
116};
117
118}
119
120using FullscreenWindowManager = me::WindowManagerBuilder<FullscreenWindowManagerPolicy>;
121using CanonicalWindowManager = me::WindowManagerBuilder<me::CanonicalWindowManagerPolicyCopy>;
122
123void me::add_window_manager_option_to(Server& server)
124{
125 server.add_configuration_option(wm_option, wm_description, wm_canonical);
126
127 server.override_the_window_manager_builder([&server](msh::FocusController* focus_controller)
128 -> std::shared_ptr<msh::WindowManager>
129 {
130 auto const options = server.get_options();
131 auto const selection = options->get<std::string>(wm_option);
132
133 if (selection == wm_fullscreen)
134 {
135 return std::make_shared<FullscreenWindowManager>(focus_controller, server.the_shell_display_layout());
136 }
137 else if (selection == wm_canonical)
138 {
139 return std::make_shared<CanonicalWindowManager>(
140 focus_controller,
141 server.the_shell_display_layout(),
142 server.the_graphics_platform()->create_buffer_allocator());
143 }
144 else if (selection == wm_system_compositor)
145 {
146 return std::make_shared<msh::SystemCompositorWindowManager>(
147 focus_controller,
148 server.the_shell_display_layout(),
149 server.the_session_coordinator());
150 }
151
152 throw mir::AbnormalExit("Unknown window manager: " + selection);
153 });
154}
1550
=== removed file 'examples/server_example_window_management.h'
--- examples/server_example_window_management.h 2017-07-28 17:00:43 +0000
+++ examples/server_example_window_management.h 1970-01-01 00:00:00 +0000
@@ -1,33 +0,0 @@
1/*
2 * Copyright © 2014 Canonical Ltd.
3 *
4 * This program is free software: you can redistribute it and/or modify it
5 * under the terms of the GNU General Public License version 2 or 3,
6 * as published by the Free Software Foundation.
7 *
8 * This program is distributed in the hope that it will be useful,
9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 * GNU General Public License for more details.
12 *
13 * You should have received a copy of the GNU General Public License
14 * along with this program. If not, see <http://www.gnu.org/licenses/>.
15 *
16 * Authored By: Alan Griffiths <alan@octopull.co.uk>
17 */
18
19#ifndef MIR_EXAMPLES_WINDOW_MANAGEMENT_H_
20#define MIR_EXAMPLES_WINDOW_MANAGEMENT_H_
21
22namespace mir
23{
24class Server;
25
26namespace examples
27{
28void add_window_manager_option_to(Server& server);
29}
30} // namespace mir
31
32
33#endif // MIR_EXAMPLES_WINDOW_MANAGEMENT_H_
340
=== removed file 'examples/xcursor.c'
--- examples/xcursor.c 2016-01-29 08:18:22 +0000
+++ examples/xcursor.c 1970-01-01 00:00:00 +0000
@@ -1,973 +0,0 @@
1/*
2 * Copyright © 2002 Keith Packard
3 *
4 * Permission to use, copy, modify, distribute, and sell this software and its
5 * documentation for any purpose is hereby granted without fee, provided that
6 * the above copyright notice appear in all copies and that both that
7 * copyright notice and this permission notice appear in supporting
8 * documentation, and that the name of Keith Packard not be used in
9 * advertising or publicity pertaining to distribution of the software without
10 * specific, written prior permission. Keith Packard makes no
11 * representations about the suitability of this software for any purpose. It
12 * is provided "as is" without express or implied warranty.
13 *
14 * KEITH PACKARD DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
15 * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO
16 * EVENT SHALL KEITH PACKARD BE LIABLE FOR ANY SPECIAL, INDIRECT OR
17 * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE,
18 * DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
19 * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
20 * PERFORMANCE OF THIS SOFTWARE.
21 */
22
23#include "xcursor.h"
24#include <stdio.h>
25#include <stdlib.h>
26#include <string.h>
27#include <dirent.h>
28
29/*
30 * From libXcursor/include/X11/extensions/Xcursor.h
31 */
32
33#define XcursorTrue 1
34#define XcursorFalse 0
35
36/*
37 * Cursor files start with a header. The header
38 * contains a magic number, a version number and a
39 * table of contents which has type and offset information
40 * for the remaining tables in the file.
41 *
42 * File minor versions increment for compatible changes
43 * File major versions increment for incompatible changes (never, we hope)
44 *
45 * Chunks of the same type are always upward compatible. Incompatible
46 * changes are made with new chunk types; the old data can remain under
47 * the old type. Upward compatible changes can add header data as the
48 * header lengths are specified in the file.
49 *
50 * File:
51 * FileHeader
52 * LISTofChunk
53 *
54 * FileHeader:
55 * CARD32 magic magic number
56 * CARD32 header bytes in file header
57 * CARD32 version file version
58 * CARD32 ntoc number of toc entries
59 * LISTofFileToc toc table of contents
60 *
61 * FileToc:
62 * CARD32 type entry type
63 * CARD32 subtype entry subtype (size for images)
64 * CARD32 position absolute file position
65 */
66
67#define XCURSOR_MAGIC 0x72756358 /* "Xcur" LSBFirst */
68
69/*
70 * Current Xcursor version number. Will be substituted by configure
71 * from the version in the libXcursor configure.ac file.
72 */
73
74#define XCURSOR_LIB_MAJOR 1
75#define XCURSOR_LIB_MINOR 1
76#define XCURSOR_LIB_REVISION 13
77#define XCURSOR_LIB_VERSION ((XCURSOR_LIB_MAJOR * 10000) + \
78 (XCURSOR_LIB_MINOR * 100) + \
79 (XCURSOR_LIB_REVISION))
80
81/*
82 * This version number is stored in cursor files; changes to the
83 * file format require updating this version number
84 */
85#define XCURSOR_FILE_MAJOR 1
86#define XCURSOR_FILE_MINOR 0
87#define XCURSOR_FILE_VERSION ((XCURSOR_FILE_MAJOR << 16) | (XCURSOR_FILE_MINOR))
88#define XCURSOR_FILE_HEADER_LEN (4 * 4)
89#define XCURSOR_FILE_TOC_LEN (3 * 4)
90
91typedef struct _XcursorFileToc {
92 XcursorUInt type; /* chunk type */
93 XcursorUInt subtype; /* subtype (size for images) */
94 XcursorUInt position; /* absolute position in file */
95} XcursorFileToc;
96
97typedef struct _XcursorFileHeader {
98 XcursorUInt magic; /* magic number */
99 XcursorUInt header; /* byte length of header */
100 XcursorUInt version; /* file version number */
101 XcursorUInt ntoc; /* number of toc entries */
102 XcursorFileToc *tocs; /* table of contents */
103} XcursorFileHeader;
104
105/*
106 * The rest of the file is a list of chunks, each tagged by type
107 * and version.
108 *
109 * Chunk:
110 * ChunkHeader
111 * <extra type-specific header fields>
112 * <type-specific data>
113 *
114 * ChunkHeader:
115 * CARD32 header bytes in chunk header + type header
116 * CARD32 type chunk type
117 * CARD32 subtype chunk subtype
118 * CARD32 version chunk type version
119 */
120
121#define XCURSOR_CHUNK_HEADER_LEN (4 * 4)
122
123typedef struct _XcursorChunkHeader {
124 XcursorUInt header; /* bytes in chunk header */
125 XcursorUInt type; /* chunk type */
126 XcursorUInt subtype; /* chunk subtype (size for images) */
127 XcursorUInt version; /* version of this type */
128} XcursorChunkHeader;
129
130/*
131 * Here's a list of the known chunk types
132 */
133
134/*
135 * Comments consist of a 4-byte length field followed by
136 * UTF-8 encoded text
137 *
138 * Comment:
139 * ChunkHeader header chunk header
140 * CARD32 length bytes in text
141 * LISTofCARD8 text UTF-8 encoded text
142 */
143
144#define XCURSOR_COMMENT_TYPE 0xfffe0001
145#define XCURSOR_COMMENT_VERSION 1
146#define XCURSOR_COMMENT_HEADER_LEN (XCURSOR_CHUNK_HEADER_LEN + (1 *4))
147#define XCURSOR_COMMENT_COPYRIGHT 1
148#define XCURSOR_COMMENT_LICENSE 2
149#define XCURSOR_COMMENT_OTHER 3
150#define XCURSOR_COMMENT_MAX_LEN 0x100000
151
152typedef struct _XcursorComment {
153 XcursorUInt version;
154 XcursorUInt comment_type;
155 char *comment;
156} XcursorComment;
157
158/*
159 * Each cursor image occupies a separate image chunk.
160 * The length of the image header follows the chunk header
161 * so that future versions can extend the header without
162 * breaking older applications
163 *
164 * Image:
165 * ChunkHeader header chunk header
166 * CARD32 width actual width
167 * CARD32 height actual height
168 * CARD32 xhot hot spot x
169 * CARD32 yhot hot spot y
170 * CARD32 delay animation delay
171 * LISTofCARD32 pixels ARGB pixels
172 */
173
174#define XCURSOR_IMAGE_TYPE 0xfffd0002
175#define XCURSOR_IMAGE_VERSION 1
176#define XCURSOR_IMAGE_HEADER_LEN (XCURSOR_CHUNK_HEADER_LEN + (5*4))
177#define XCURSOR_IMAGE_MAX_SIZE 0x7fff /* 32767x32767 max cursor size */
178
179typedef struct _XcursorFile XcursorFile;
180
181struct _XcursorFile {
182 void *closure;
183 int (*read) (XcursorFile *file, unsigned char *buf, int len);
184 int (*write) (XcursorFile *file, unsigned char *buf, int len);
185 int (*seek) (XcursorFile *file, long offset, int whence);
186};
187
188typedef struct _XcursorComments {
189 int ncomment; /* number of comments */
190 XcursorComment **comments; /* array of XcursorComment pointers */
191} XcursorComments;
192
193/*
194 * From libXcursor/src/file.c
195 */
196
197static XcursorImage *
198XcursorImageCreate (int width, int height)
199{
200 XcursorImage *image;
201
202 image = malloc (sizeof (XcursorImage) +
203 width * height * sizeof (XcursorPixel));
204 if (!image)
205 return NULL;
206 image->version = XCURSOR_IMAGE_VERSION;
207 image->pixels = (XcursorPixel *) (image + 1);
208 image->size = width > height ? width : height;
209 image->width = width;
210 image->height = height;
211 image->delay = 0;
212 return image;
213}
214
215static void
216XcursorImageDestroy (XcursorImage *image)
217{
218 free (image);
219}
220
221static XcursorImages *
222XcursorImagesCreate (int size)
223{
224 XcursorImages *images;
225
226 images = malloc (sizeof (XcursorImages) +
227 size * sizeof (XcursorImage *));
228 if (!images)
229 return NULL;
230 images->nimage = 0;
231 images->images = (XcursorImage **) (images + 1);
232 images->name = NULL;
233 return images;
234}
235
236void
237XcursorImagesDestroy (XcursorImages *images)
238{
239 int n;
240
241 if (!images)
242 return;
243
244 for (n = 0; n < images->nimage; n++)
245 XcursorImageDestroy (images->images[n]);
246 if (images->name)
247 free (images->name);
248 free (images);
249}
250
251static void
252XcursorImagesSetName (XcursorImages *images, const char *name)
253{
254 char *new;
255
256 if (!images || !name)
257 return;
258
259 new = malloc (strlen (name) + 1);
260
261 if (!new)
262 return;
263
264 strcpy (new, name);
265 if (images->name)
266 free (images->name);
267 images->name = new;
268}
269
270static XcursorBool
271_XcursorReadUInt (XcursorFile *file, XcursorUInt *u)
272{
273 unsigned char bytes[4];
274
275 if (!file || !u)
276 return XcursorFalse;
277
278 if ((*file->read) (file, bytes, 4) != 4)
279 return XcursorFalse;
280 *u = (((XcursorUInt)bytes[0] << 0) |
281 ((XcursorUInt)bytes[1] << 8) |
282 ((XcursorUInt)bytes[2] << 16) |
283 ((XcursorUInt)bytes[3] << 24));
284 return XcursorTrue;
285}
286
287static void
288_XcursorFileHeaderDestroy (XcursorFileHeader *fileHeader)
289{
290 free (fileHeader);
291}
292
293static XcursorFileHeader *
294_XcursorFileHeaderCreate (int ntoc)
295{
296 XcursorFileHeader *fileHeader;
297
298 if (ntoc > 0x10000)
299 return NULL;
300 fileHeader = malloc (sizeof (XcursorFileHeader) +
301 ntoc * sizeof (XcursorFileToc));
302 if (!fileHeader)
303 return NULL;
304 fileHeader->magic = XCURSOR_MAGIC;
305 fileHeader->header = XCURSOR_FILE_HEADER_LEN;
306 fileHeader->version = XCURSOR_FILE_VERSION;
307 fileHeader->ntoc = ntoc;
308 fileHeader->tocs = (XcursorFileToc *) (fileHeader + 1);
309 return fileHeader;
310}
311
312static XcursorFileHeader *
313_XcursorReadFileHeader (XcursorFile *file)
314{
315 XcursorFileHeader head, *fileHeader;
316 XcursorUInt skip;
317 unsigned int n;
318
319 if (!file)
320 return NULL;
321
322 if (!_XcursorReadUInt (file, &head.magic))
323 return NULL;
324 if (head.magic != XCURSOR_MAGIC)
325 return NULL;
326 if (!_XcursorReadUInt (file, &head.header))
327 return NULL;
328 if (!_XcursorReadUInt (file, &head.version))
329 return NULL;
330 if (!_XcursorReadUInt (file, &head.ntoc))
331 return NULL;
332 skip = head.header - XCURSOR_FILE_HEADER_LEN;
333 if (skip)
334 if ((*file->seek) (file, skip, SEEK_CUR) == EOF)
335 return NULL;
336 fileHeader = _XcursorFileHeaderCreate (head.ntoc);
337 if (!fileHeader)
338 return NULL;
339 fileHeader->magic = head.magic;
340 fileHeader->header = head.header;
341 fileHeader->version = head.version;
342 fileHeader->ntoc = head.ntoc;
343 for (n = 0; n < fileHeader->ntoc; n++)
344 {
345 if (!_XcursorReadUInt (file, &fileHeader->tocs[n].type))
346 break;
347 if (!_XcursorReadUInt (file, &fileHeader->tocs[n].subtype))
348 break;
349 if (!_XcursorReadUInt (file, &fileHeader->tocs[n].position))
350 break;
351 }
352 if (n != fileHeader->ntoc)
353 {
354 _XcursorFileHeaderDestroy (fileHeader);
355 return NULL;
356 }
357 return fileHeader;
358}
359
360static XcursorBool
361_XcursorSeekToToc (XcursorFile *file,
362 XcursorFileHeader *fileHeader,
363 int toc)
364{
365 if (!file || !fileHeader || \
366 (*file->seek) (file, fileHeader->tocs[toc].position, SEEK_SET) == EOF)
367 return XcursorFalse;
368 return XcursorTrue;
369}
370
371static XcursorBool
372_XcursorFileReadChunkHeader (XcursorFile *file,
373 XcursorFileHeader *fileHeader,
374 int toc,
375 XcursorChunkHeader *chunkHeader)
376{
377 if (!file || !fileHeader || !chunkHeader)
378 return XcursorFalse;
379 if (!_XcursorSeekToToc (file, fileHeader, toc))
380 return XcursorFalse;
381 if (!_XcursorReadUInt (file, &chunkHeader->header))
382 return XcursorFalse;
383 if (!_XcursorReadUInt (file, &chunkHeader->type))
384 return XcursorFalse;
385 if (!_XcursorReadUInt (file, &chunkHeader->subtype))
386 return XcursorFalse;
387 if (!_XcursorReadUInt (file, &chunkHeader->version))
388 return XcursorFalse;
389 /* sanity check */
390 if (chunkHeader->type != fileHeader->tocs[toc].type ||
391 chunkHeader->subtype != fileHeader->tocs[toc].subtype)
392 return XcursorFalse;
393 return XcursorTrue;
394}
395
396#define dist(a,b) ((a) > (b) ? (a) - (b) : (b) - (a))
397
398static XcursorDim
399_XcursorFindBestSize (XcursorFileHeader *fileHeader,
400 XcursorDim size,
401 int *nsizesp)
402{
403 unsigned int n;
404 int nsizes = 0;
405 XcursorDim bestSize = 0;
406 XcursorDim thisSize;
407
408 if (!fileHeader || !nsizesp)
409 return 0;
410
411 for (n = 0; n < fileHeader->ntoc; n++)
412 {
413 if (fileHeader->tocs[n].type != XCURSOR_IMAGE_TYPE)
414 continue;
415 thisSize = fileHeader->tocs[n].subtype;
416 if (!bestSize || dist (thisSize, size) < dist (bestSize, size))
417 {
418 bestSize = thisSize;
419 nsizes = 1;
420 }
421 else if (thisSize == bestSize)
422 nsizes++;
423 }
424 *nsizesp = nsizes;
425 return bestSize;
426}
427
428static int
429_XcursorFindImageToc (XcursorFileHeader *fileHeader,
430 XcursorDim size,
431 int count)
432{
433 unsigned int toc;
434 XcursorDim thisSize;
435
436 if (!fileHeader)
437 return 0;
438
439 for (toc = 0; toc < fileHeader->ntoc; toc++)
440 {
441 if (fileHeader->tocs[toc].type != XCURSOR_IMAGE_TYPE)
442 continue;
443 thisSize = fileHeader->tocs[toc].subtype;
444 if (thisSize != size)
445 continue;
446 if (!count)
447 break;
448 count--;
449 }
450 if (toc == fileHeader->ntoc)
451 return -1;
452 return toc;
453}
454
455static XcursorImage *
456_XcursorReadImage (XcursorFile *file,
457 XcursorFileHeader *fileHeader,
458 int toc)
459{
460 XcursorChunkHeader chunkHeader;
461 XcursorImage head;
462 XcursorImage *image;
463 int n;
464 XcursorPixel *p;
465
466 if (!file || !fileHeader)
467 return NULL;
468
469 if (!_XcursorFileReadChunkHeader (file, fileHeader, toc, &chunkHeader))
470 return NULL;
471 if (!_XcursorReadUInt (file, &head.width))
472 return NULL;
473 if (!_XcursorReadUInt (file, &head.height))
474 return NULL;
475 if (!_XcursorReadUInt (file, &head.xhot))
476 return NULL;
477 if (!_XcursorReadUInt (file, &head.yhot))
478 return NULL;
479 if (!_XcursorReadUInt (file, &head.delay))
480 return NULL;
481 /* sanity check data */
482 if (head.width >= 0x10000 || head.height > 0x10000)
483 return NULL;
484 if (head.width == 0 || head.height == 0)
485 return NULL;
486 if (head.xhot > head.width || head.yhot > head.height)
487 return NULL;
488
489 /* Create the image and initialize it */
490 image = XcursorImageCreate (head.width, head.height);
491 if (image == NULL)
492 return NULL;
493 if (chunkHeader.version < image->version)
494 image->version = chunkHeader.version;
495 image->size = chunkHeader.subtype;
496 image->xhot = head.xhot;
497 image->yhot = head.yhot;
498 image->delay = head.delay;
499 n = image->width * image->height;
500 p = image->pixels;
501 while (n--)
502 {
503 if (!_XcursorReadUInt (file, p))
504 {
505 XcursorImageDestroy (image);
506 return NULL;
507 }
508 p++;
509 }
510 return image;
511}
512
513static XcursorImages *
514XcursorXcFileLoadImages (XcursorFile *file, int size)
515{
516 XcursorFileHeader *fileHeader;
517 XcursorDim bestSize;
518 int nsize;
519 XcursorImages *images;
520 int n;
521 int toc;
522
523 if (!file || size < 0)
524 return NULL;
525 fileHeader = _XcursorReadFileHeader (file);
526 if (!fileHeader)
527 return NULL;
528 bestSize = _XcursorFindBestSize (fileHeader, (XcursorDim) size, &nsize);
529 if (!bestSize)
530 {
531 _XcursorFileHeaderDestroy (fileHeader);
532 return NULL;
533 }
534 images = XcursorImagesCreate (nsize);
535 if (!images)
536 {
537 _XcursorFileHeaderDestroy (fileHeader);
538 return NULL;
539 }
540 for (n = 0; n < nsize; n++)
541 {
542 toc = _XcursorFindImageToc (fileHeader, bestSize, n);
543 if (toc < 0)
544 break;
545 images->images[images->nimage] = _XcursorReadImage (file, fileHeader,
546 toc);
547 if (!images->images[images->nimage])
548 break;
549 images->nimage++;
550 }
551 _XcursorFileHeaderDestroy (fileHeader);
552 if (images->nimage != nsize)
553 {
554 XcursorImagesDestroy (images);
555 images = NULL;
556 }
557 return images;
558}
559
560static int
561_XcursorStdioFileRead (XcursorFile *file, unsigned char *buf, int len)
562{
563 FILE *f = file->closure;
564 return fread (buf, 1, len, f);
565}
566
567static int
568_XcursorStdioFileWrite (XcursorFile *file, unsigned char *buf, int len)
569{
570 FILE *f = file->closure;
571 return fwrite (buf, 1, len, f);
572}
573
574static int
575_XcursorStdioFileSeek (XcursorFile *file, long offset, int whence)
576{
577 FILE *f = file->closure;
578 return fseek (f, offset, whence);
579}
580
581static void
582_XcursorStdioFileInitialize (FILE *stdfile, XcursorFile *file)
583{
584 file->closure = stdfile;
585 file->read = _XcursorStdioFileRead;
586 file->write = _XcursorStdioFileWrite;
587 file->seek = _XcursorStdioFileSeek;
588}
589
590static XcursorImages *
591XcursorFileLoadImages (FILE *file, int size)
592{
593 XcursorFile f;
594
595 if (!file)
596 return NULL;
597
598 _XcursorStdioFileInitialize (file, &f);
599 return XcursorXcFileLoadImages (&f, size);
600}
601
602/*
603 * From libXcursor/src/library.c
604 */
605
606#ifndef ICONDIR
607#define ICONDIR "/usr/X11R6/lib/X11/icons"
608#endif
609
610#ifndef XCURSORPATH
611#define XCURSORPATH "~/.icons:/usr/share/icons:/usr/share/pixmaps:~/.cursors:/usr/share/cursors/xorg-x11:"ICONDIR
612#endif
613
614static const char *
615XcursorLibraryPath (void)
616{
617 static const char *path;
618
619 if (!path)
620 {
621 path = getenv ("XCURSOR_PATH");
622 if (!path)
623 path = XCURSORPATH;
624 }
625 return path;
626}
627
628static void
629_XcursorAddPathElt (char *path, const char *elt, int len)
630{
631 int pathlen = strlen (path);
632
633 /* append / if the path doesn't currently have one */
634 if (path[0] == '\0' || path[pathlen - 1] != '/')
635 {
636 strcat (path, "/");
637 pathlen++;
638 }
639 if (len == -1)
640 len = strlen (elt);
641 /* strip leading slashes */
642 while (len && elt[0] == '/')
643 {
644 elt++;
645 len--;
646 }
647 strncpy (path + pathlen, elt, len);
648 path[pathlen + len] = '\0';
649}
650
651static char *
652_XcursorBuildThemeDir (const char *dir, const char *theme)
653{
654 const char *colon;
655 const char *tcolon;
656 char *full;
657 char *home;
658 int dirlen;
659 int homelen;
660 int themelen;
661 int len;
662
663 if (!dir || !theme)
664 return NULL;
665
666 colon = strchr (dir, ':');
667 if (!colon)
668 colon = dir + strlen (dir);
669
670 dirlen = colon - dir;
671
672 tcolon = strchr (theme, ':');
673 if (!tcolon)
674 tcolon = theme + strlen (theme);
675
676 themelen = tcolon - theme;
677
678 home = NULL;
679 homelen = 0;
680 if (*dir == '~')
681 {
682 home = getenv ("HOME");
683 if (!home)
684 return NULL;
685 homelen = strlen (home);
686 dir++;
687 dirlen--;
688 }
689
690 /*
691 * add space for any needed directory separators, one per component,
692 * and one for the trailing null
693 */
694 len = 1 + homelen + 1 + dirlen + 1 + themelen + 1;
695
696 full = malloc (len);
697 if (!full)
698 return NULL;
699 full[0] = '\0';
700
701 if (home)
702 _XcursorAddPathElt (full, home, -1);
703 _XcursorAddPathElt (full, dir, dirlen);
704 _XcursorAddPathElt (full, theme, themelen);
705 return full;
706}
707
708static char *
709_XcursorBuildFullname (const char *dir, const char *subdir, const char *file)
710{
711 char *full;
712
713 if (!dir || !subdir || !file)
714 return NULL;
715
716 /*
717 * Following the g++5 transition the strlen() in _XcursorAddPathElt() can
718 * trigger valgrind. We add some padding as a workaround.
719 */
720 size_t padding = 4;
721 full = malloc (padding + strlen (dir) + 1 + strlen (subdir) + 1 + strlen (file) + 1);
722 if (!full)
723 return NULL;
724 full[0] = '\0';
725 _XcursorAddPathElt (full, dir, -1);
726 _XcursorAddPathElt (full, subdir, -1);
727 _XcursorAddPathElt (full, file, -1);
728 return full;
729}
730
731static const char *
732_XcursorNextPath (const char *path)
733{
734 char *colon = strchr (path, ':');
735
736 if (!colon)
737 return NULL;
738 return colon + 1;
739}
740
741#define XcursorWhite(c) ((c) == ' ' || (c) == '\t' || (c) == '\n')
742#define XcursorSep(c) ((c) == ';' || (c) == ',')
743
744static char *
745_XcursorThemeInherits (const char *full)
746{
747 char line[8192];
748 char *result = NULL;
749 FILE *f;
750
751 if (!full)
752 return NULL;
753
754 f = fopen (full, "r");
755 if (f)
756 {
757 while (fgets (line, sizeof (line), f))
758 {
759 if (!strncmp (line, "Inherits", 8))
760 {
761 char *l = line + 8;
762 char *r;
763 while (*l == ' ') l++;
764 if (*l != '=') continue;
765 l++;
766 while (*l == ' ') l++;
767 result = malloc (strlen (l) + 1);
768 if (result)
769 {
770 r = result;
771 while (*l)
772 {
773 while (XcursorSep(*l) || XcursorWhite (*l)) l++;
774 if (!*l)
775 break;
776 if (r != result)
777 *r++ = ':';
778 while (*l && !XcursorWhite(*l) &&
779 !XcursorSep(*l))
780 *r++ = *l++;
781 }
782 *r++ = '\0';
783 }
784 break;
785 }
786 }
787 fclose (f);
788 }
789 return result;
790}
791
792static FILE *
793XcursorScanTheme (const char *theme, const char *name)
794{
795 FILE *f = NULL;
796 char *full;
797 char *dir;
798 const char *path;
799 char *inherits = NULL;
800 const char *i;
801
802 if (!theme || !name)
803 return NULL;
804
805 /*
806 * Scan this theme
807 */
808 for (path = XcursorLibraryPath ();
809 path && f == NULL;
810 path = _XcursorNextPath (path))
811 {
812 dir = _XcursorBuildThemeDir (path, theme);
813 if (dir)
814 {
815 full = _XcursorBuildFullname (dir, "cursors", name);
816 if (full)
817 {
818 f = fopen (full, "r");
819 free (full);
820 }
821 if (!f && !inherits)
822 {
823 full = _XcursorBuildFullname (dir, "", "index.theme");
824 if (full)
825 {
826 inherits = _XcursorThemeInherits (full);
827 free (full);
828 }
829 }
830 free (dir);
831 }
832 }
833 /*
834 * Recurse to scan inherited themes
835 */
836 for (i = inherits; i && f == NULL; i = _XcursorNextPath (i))
837 f = XcursorScanTheme (i, name);
838 if (inherits != NULL)
839 free (inherits);
840 return f;
841}
842
843XcursorImages *
844XcursorLibraryLoadImages (const char *file, const char *theme, int size)
845{
846 FILE *f = NULL;
847 XcursorImages *images = NULL;
848
849 if (!file)
850 return NULL;
851
852 if (theme)
853 f = XcursorScanTheme (theme, file);
854 if (!f)
855 f = XcursorScanTheme ("default", file);
856 if (f)
857 {
858 images = XcursorFileLoadImages (f, size);
859 if (images)
860 XcursorImagesSetName (images, file);
861 fclose (f);
862 }
863 return images;
864}
865
866static void
867load_all_cursors_from_dir(const char *path, int size,
868 void (*load_callback)(XcursorImages *, void *),
869 void *user_data)
870{
871 FILE *f;
872 DIR *dir = opendir(path);
873 struct dirent *ent;
874 char *full;
875 XcursorImages *images;
876
877 if (!dir)
878 return;
879
880 for(ent = readdir(dir); ent; ent = readdir(dir)) {
881#ifdef _DIRENT_HAVE_D_TYPE
882 if (ent->d_type != DT_UNKNOWN &&
883 (ent->d_type != DT_REG && ent->d_type != DT_LNK))
884 continue;
885#endif
886
887 full = _XcursorBuildFullname(path, "", ent->d_name);
888 if (!full)
889 continue;
890
891 f = fopen(full, "r");
892 if (!f) {
893 free(full);
894 continue;
895 }
896
897 images = XcursorFileLoadImages(f, size);
898
899 if (images) {
900 XcursorImagesSetName(images, ent->d_name);
901 load_callback(images, user_data);
902 }
903
904 fclose (f);
905 free(full);
906 }
907
908 closedir(dir);
909}
910
911/** Load all the cursor of a theme
912 *
913 * This function loads all the cursor images of a given theme and its
914 * inherited themes. Each cursor is loaded into an XcursorImages object
915 * which is passed to the caller's load callback. If a cursor appears
916 * more than once across all the inherited themes, the load callback
917 * will be called multiple times, with possibly different XcursorImages
918 * object which have the same name. The user is expected to destroy the
919 * XcursorImages objects passed to the callback with
920 * XcursorImagesDestroy().
921 *
922 * \param theme The name of theme that should be loaded
923 * \param size The desired size of the cursor images
924 * \param load_callback A callback function that will be called
925 * for each cursor loaded. The first parameter is the XcursorImages
926 * object representing the loaded cursor and the second is a pointer
927 * to data provided by the user.
928 * \param user_data The data that should be passed to the load callback
929 */
930void
931xcursor_load_theme(const char *theme, int size,
932 void (*load_callback)(XcursorImages *, void *),
933 void *user_data)
934{
935 char *full, *dir;
936 char *inherits = NULL;
937 const char *path, *i;
938
939 if (!theme)
940 theme = "default";
941
942 for (path = XcursorLibraryPath();
943 path;
944 path = _XcursorNextPath(path)) {
945 dir = _XcursorBuildThemeDir(path, theme);
946 if (!dir)
947 continue;
948
949 full = _XcursorBuildFullname(dir, "cursors", "");
950
951 if (full) {
952 load_all_cursors_from_dir(full, size, load_callback,
953 user_data);
954 free(full);
955 }
956
957 if (!inherits) {
958 full = _XcursorBuildFullname(dir, "", "index.theme");
959 if (full) {
960 inherits = _XcursorThemeInherits(full);
961 free(full);
962 }
963 }
964
965 free(dir);
966 }
967
968 for (i = inherits; i; i = _XcursorNextPath(i))
969 xcursor_load_theme(i, size, load_callback, user_data);
970
971 if (inherits)
972 free(inherits);
973}
9740
=== removed file 'examples/xcursor.h'
--- examples/xcursor.h 2016-01-29 08:18:22 +0000
+++ examples/xcursor.h 1970-01-01 00:00:00 +0000
@@ -1,65 +0,0 @@
1/*
2 * Copyright © 2002 Keith Packard
3 *
4 * Permission to use, copy, modify, distribute, and sell this software and its
5 * documentation for any purpose is hereby granted without fee, provided that
6 * the above copyright notice appear in all copies and that both that
7 * copyright notice and this permission notice appear in supporting
8 * documentation, and that the name of Keith Packard not be used in
9 * advertising or publicity pertaining to distribution of the software without
10 * specific, written prior permission. Keith Packard makes no
11 * representations about the suitability of this software for any purpose. It
12 * is provided "as is" without express or implied warranty.
13 *
14 * KEITH PACKARD DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
15 * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO
16 * EVENT SHALL KEITH PACKARD BE LIABLE FOR ANY SPECIAL, INDIRECT OR
17 * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE,
18 * DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
19 * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
20 * PERFORMANCE OF THIS SOFTWARE.
21 */
22
23#ifndef XCURSOR_H
24#define XCURSOR_H
25
26#include <stdint.h>
27
28
29typedef int XcursorBool;
30typedef uint32_t XcursorUInt;
31
32typedef XcursorUInt XcursorDim;
33typedef XcursorUInt XcursorPixel;
34
35typedef struct _XcursorImage {
36 XcursorUInt version; /* version of the image data */
37 XcursorDim size; /* nominal size for matching */
38 XcursorDim width; /* actual width */
39 XcursorDim height; /* actual height */
40 XcursorDim xhot; /* hot spot x (must be inside image) */
41 XcursorDim yhot; /* hot spot y (must be inside image) */
42 XcursorUInt delay; /* animation delay to next frame (ms) */
43 XcursorPixel *pixels; /* pointer to pixels */
44} XcursorImage;
45
46/*
47 * Other data structures exposed by the library API
48 */
49typedef struct _XcursorImages {
50 int nimage; /* number of images */
51 XcursorImage **images; /* array of XcursorImage pointers */
52 char *name; /* name used to load images */
53} XcursorImages;
54
55XcursorImages *
56XcursorLibraryLoadImages (const char *file, const char *theme, int size);
57
58void
59XcursorImagesDestroy (XcursorImages *images);
60
61void
62xcursor_load_theme(const char *theme, int size,
63 void (*load_callback)(XcursorImages *, void *),
64 void *user_data);
65#endif
660
=== removed file 'examples/xcursor_loader.cpp'
--- examples/xcursor_loader.cpp 2017-07-28 17:00:43 +0000
+++ examples/xcursor_loader.cpp 1970-01-01 00:00:00 +0000
@@ -1,228 +0,0 @@
1/*
2 * Copyright © 2014 Canonical Ltd.
3 *
4 * This program is free software: you can redistribute it and/or modify it
5 * under the terms of the GNU General Public License version 2 or 3,
6 * as published by the Free Software Foundation.
7 *
8 * This program is distributed in the hope that it will be useful,
9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 * GNU General Public License for more details.
12 *
13 * You should have received a copy of the GNU General Public License
14 * along with this program. If not, see <http://www.gnu.org/licenses/>.
15 *
16 * Authored by: Robert Carr <robert.carr@canonical.com>
17 */
18
19#include "xcursor_loader.h"
20
21#include "mir/graphics/cursor_image.h"
22
23#include <boost/throw_exception.hpp>
24#include <stdexcept>
25
26#include <string.h>
27
28#include <mir_toolkit/cursors.h>
29
30// Unfortunately this can not be compiled as C++...so we can not namespace
31// these symbols. In order to differentiate from internal symbols
32// we refer to them via their _ prefixed version, i.e. _XcursorImage
33extern "C"
34{
35#include "xcursor.h"
36}
37
38namespace me = mir::examples;
39namespace mg = mir::graphics;
40namespace mi = mir::input;
41namespace geom = mir::geometry;
42
43namespace
44{
45class XCursorImage : public mg::CursorImage
46{
47public:
48 XCursorImage(_XcursorImage *image, std::shared_ptr<_XcursorImages> const& save_resource)
49 : image(image),
50 save_resource(save_resource)
51 {
52 if (image->width != mi::default_cursor_size.width.as_uint32_t() ||
53 image->height != mi::default_cursor_size.height.as_uint32_t())
54 {
55 BOOST_THROW_EXCEPTION(
56 std::runtime_error("Somehow we got a cursor not of the default size (currently only 24x24 supported)"));
57 }
58 }
59
60 ~XCursorImage()
61 {
62 }
63
64 void const* as_argb_8888() const override
65 {
66 return image->pixels;
67 }
68 geom::Size size() const override
69 {
70 return mi::default_cursor_size;
71 }
72 geom::Displacement hotspot() const override
73 {
74 return {image->xhot, image->yhot};
75 }
76
77private:
78 _XcursorImage *image;
79 std::shared_ptr<_XcursorImages> const save_resource;
80};
81
82std::string const
83xcursor_name_for_mir_cursor(std::string const& mir_cursor_name)
84{
85 if (mir_cursor_name == mir_default_cursor_name)
86 {
87 return "arrow";
88 }
89 else if (mir_cursor_name == mir_arrow_cursor_name)
90 {
91 return "arrow";
92 }
93 else if (mir_cursor_name == mir_busy_cursor_name)
94 {
95 return "watch";
96 }
97 else if (mir_cursor_name == mir_caret_cursor_name)
98 {
99 return "xterm"; // Yep
100 }
101 else if (mir_cursor_name == mir_pointing_hand_cursor_name)
102 {
103 return "hand2";
104 }
105 else if (mir_cursor_name == mir_open_hand_cursor_name)
106 {
107 return "hand";
108 }
109 else if (mir_cursor_name == mir_closed_hand_cursor_name)
110 {
111 return "grabbing";
112 }
113 else if (mir_cursor_name == mir_horizontal_resize_cursor_name)
114 {
115 return "h_double_arrow";
116 }
117 else if (mir_cursor_name == mir_vertical_resize_cursor_name)
118 {
119 return "v_double_arrow";
120 }
121 else if (mir_cursor_name == mir_diagonal_resize_bottom_to_top_cursor_name)
122 {
123 return "top_right_corner";
124 }
125 else if (mir_cursor_name == mir_diagonal_resize_top_to_bottom_cursor_name)
126 {
127 return "bottom_right_corner";
128 }
129 else if (mir_cursor_name == mir_omnidirectional_resize_cursor_name)
130 {
131 return "fleur";
132 }
133 else if (mir_cursor_name == mir_vsplit_resize_cursor_name)
134 {
135 return "v_double_arrow";
136 }
137 else if (mir_cursor_name == mir_hsplit_resize_cursor_name)
138 {
139 return "h_double_arrow";
140 }
141 else if (mir_cursor_name == mir_crosshair_cursor_name)
142 {
143 return "crosshair";
144 }
145 else
146 {
147 return mir_cursor_name;
148 }
149}
150}
151
152me::XCursorLoader::XCursorLoader()
153{
154 load_cursor_theme("default");
155}
156
157me::XCursorLoader::XCursorLoader(std::string const& theme)
158{
159 load_cursor_theme(theme);
160}
161
162// Each XcursorImages represents images for the different sizes of a given symbolic cursor.
163void me::XCursorLoader::load_appropriately_sized_image(_XcursorImages *images)
164{
165 // We would rather take this lock in load_cursor_theme but the Xcursor lib style
166 // makes it difficult to use our standard 'pass the lg around to _locked members' pattern
167 std::lock_guard<std::mutex> lg(guard);
168
169 // We have to save all the images as XCursor expects us to free them.
170 // This contains the actual image data though, so we need to ensure they stay alive
171 // with the lifetime of the mg::CursorImage instance which refers to them.
172 auto saved_xcursor_library_resource = std::shared_ptr<_XcursorImages>(images, [](_XcursorImages *images)
173 {
174 XcursorImagesDestroy(images);
175 });
176
177 _XcursorImage *image_of_correct_size = nullptr;
178 for (int i = 0; i < images->nimage; i++)
179 {
180 _XcursorImage *candidate = images->images[i];
181 if (candidate->width == mi::default_cursor_size.width.as_uint32_t() &&
182 candidate->height == mi::default_cursor_size.height.as_uint32_t())
183 {
184 image_of_correct_size = candidate;
185 break;
186 }
187 }
188 if (!image_of_correct_size)
189 return;
190 loaded_images[std::string(images->name)] = std::make_shared<XCursorImage>(image_of_correct_size, saved_xcursor_library_resource);
191}
192
193void me::XCursorLoader::load_cursor_theme(std::string const& theme_name)
194{
195 // Cursors are named by their square dimension...called the nominal size in XCursor terminology, so we just look up by width.
196 // Later we verify the actual size.
197 xcursor_load_theme(theme_name.c_str(), mi::default_cursor_size.width.as_uint32_t(),
198 [](XcursorImages* images, void *this_ptr) -> void
199 {
200 // Can't use lambda capture as this lambda is thunked to a C function ptr
201 auto p = static_cast<me::XCursorLoader*>(this_ptr);
202 p->load_appropriately_sized_image(images);
203 }, this);
204}
205
206std::shared_ptr<mg::CursorImage> me::XCursorLoader::image(
207 std::string const& cursor_name,
208 geom::Size const& size)
209{
210 auto xcursor_name = xcursor_name_for_mir_cursor(cursor_name);
211
212 if (size != mi::default_cursor_size)
213 BOOST_THROW_EXCEPTION(
214 std::logic_error("Only the default cursor size is currently supported (mi::default_cursor_size)"));
215
216 std::lock_guard<std::mutex> lg(guard);
217
218 auto it = loaded_images.find(xcursor_name);
219 if (it != loaded_images.end())
220 return it->second;
221
222 // Fall back
223 it = loaded_images.find("arrow");
224 if (it != loaded_images.end())
225 return it->second;
226
227 return nullptr;
228}
2290
=== removed file 'examples/xcursor_loader.h'
--- examples/xcursor_loader.h 2017-07-28 17:00:43 +0000
+++ examples/xcursor_loader.h 1970-01-01 00:00:00 +0000
@@ -1,74 +0,0 @@
1/*
2 * Copyright © 2014 Canonical Ltd.
3 *
4 * This program is free software: you can redistribute it and/or modify it
5 * under the terms of the GNU General Public License version 2 or 3,
6 * as published by the Free Software Foundation.
7 *
8 * This program is distributed in the hope that it will be useful,
9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 * GNU General Public License for more details.
12 *
13 * You should have received a copy of the GNU General Public License
14 * along with this program. If not, see <http://www.gnu.org/licenses/>.
15 *
16 * Authored by: Robert Carr <robert.carr@canonical.com>
17 */
18
19
20#ifndef MIR_INPUT_XCURSOR_CURSOR_LOADER_H_
21#define MIR_INPUT_XCURSOR_CURSOR_LOADER_H_
22
23#include "mir/input/cursor_images.h"
24
25#include <memory>
26#include <string>
27#include <map>
28#include <mutex>
29#include <functional>
30
31// Unfortunately this library does not compile as C++ so we can not namespace it.
32extern "C"
33{
34struct _XcursorImages;
35}
36
37namespace mir
38{
39namespace graphics
40{
41class CursorImage;
42}
43
44namespace examples
45{
46class XCursorLoader : public input::CursorImages
47{
48public:
49 XCursorLoader();
50
51 explicit XCursorLoader(std::string const& theme);
52
53 virtual ~XCursorLoader() = default;
54
55 std::shared_ptr<graphics::CursorImage> image(std::string const& cursor_name,
56 geometry::Size const& size);
57
58protected:
59 XCursorLoader(XCursorLoader const&) = delete;
60 XCursorLoader& operator=(XCursorLoader const&) = delete;
61
62private:
63 std::mutex guard;
64
65 std::map<std::string, std::shared_ptr<graphics::CursorImage>> loaded_images;
66
67 void load_cursor_theme(std::string const& theme_name);
68 void load_appropriately_sized_image(_XcursorImages *images);
69};
70}
71}
72
73
74#endif /* MIR_INPUT_XCURSOR_CURSOR_LOADER_H_ */
750
=== renamed file 'include/platform/mir/abnormal_exit.h' => 'include/core/mir/abnormal_exit.h'
=== modified file 'playground/CMakeLists.txt'
--- playground/CMakeLists.txt 2017-06-01 13:04:37 +0000
+++ playground/CMakeLists.txt 2017-08-31 08:02:08 +0000
@@ -16,6 +16,7 @@
1616
17add_library(playgroundserverconfig STATIC17add_library(playgroundserverconfig STATIC
18 server_configuration.cpp18 server_configuration.cpp
19 server_example_display_configuration_policy.cpp
19)20)
2021
21add_subdirectory(demo-shell/)22add_subdirectory(demo-shell/)
2223
=== renamed file 'examples/server_example_display_configuration_policy.cpp' => 'playground/server_example_display_configuration_policy.cpp'
=== renamed file 'examples/server_example_display_configuration_policy.h' => 'playground/server_example_display_configuration_policy.h'
=== modified file 'src/miral/process_doxygen_xml.py'
--- src/miral/process_doxygen_xml.py 2017-08-31 08:02:07 +0000
+++ src/miral/process_doxygen_xml.py 2017-08-31 08:02:08 +0000
@@ -1,4 +1,4 @@
1#! /usr/bin/python1#! /usr/bin/python3
2"""This script processes the XML generated by "make doc" and produces summary information2"""This script processes the XML generated by "make doc" and produces summary information
3on symbols that libmiral intends to make public.3on symbols that libmiral intends to make public.
44
55
=== modified file 'tests/unit-tests/input/CMakeLists.txt'
--- tests/unit-tests/input/CMakeLists.txt 2017-05-08 03:04:26 +0000
+++ tests/unit-tests/input/CMakeLists.txt 2017-08-31 08:02:08 +0000
@@ -3,7 +3,6 @@
3list(APPEND UNIT_TEST_SOURCES3list(APPEND UNIT_TEST_SOURCES
4 ${CMAKE_CURRENT_SOURCE_DIR}/test_event_filter_chain_dispatcher.cpp4 ${CMAKE_CURRENT_SOURCE_DIR}/test_event_filter_chain_dispatcher.cpp
5 ${CMAKE_CURRENT_SOURCE_DIR}/test_cursor_controller.cpp5 ${CMAKE_CURRENT_SOURCE_DIR}/test_cursor_controller.cpp
6 ${CMAKE_CURRENT_SOURCE_DIR}/test_xcursor_loader.cpp
7 ${CMAKE_CURRENT_SOURCE_DIR}/test_touchspot_controller.cpp6 ${CMAKE_CURRENT_SOURCE_DIR}/test_touchspot_controller.cpp
8 ${CMAKE_CURRENT_SOURCE_DIR}/test_input_event.cpp7 ${CMAKE_CURRENT_SOURCE_DIR}/test_input_event.cpp
9 ${CMAKE_CURRENT_SOURCE_DIR}/test_config_changer.cpp8 ${CMAKE_CURRENT_SOURCE_DIR}/test_config_changer.cpp
109
=== removed file 'tests/unit-tests/input/test_xcursor_loader.cpp'
--- tests/unit-tests/input/test_xcursor_loader.cpp 2017-07-28 17:00:43 +0000
+++ tests/unit-tests/input/test_xcursor_loader.cpp 1970-01-01 00:00:00 +0000
@@ -1,148 +0,0 @@
1/*
2 * Copyright © 2014 Canonical Ltd.
3 *
4 * This program is free software: you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License version 2 or 3 as
6 * published by the Free Software Foundation.
7 *
8 * This program is distributed in the hope that it will be useful,
9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 * GNU General Public License for more details.
12 *
13 * You should have received a copy of the GNU General Public License
14 * along with this program. If not, see <http://www.gnu.org/licenses/>.
15 *
16 * Authored by: Robert Carr <robert.carr@canonical.com>
17 */
18
19#include "examples/xcursor_loader.h"
20
21#include "mir/graphics/cursor_image.h"
22#include "mir_test_framework/executable_path.h"
23#include "mir_test_framework/temporary_environment_value.h"
24
25#include <mir_toolkit/common.h>
26#include <mir_toolkit/cursors.h>
27
28#include <gtest/gtest.h>
29#include <gmock/gmock.h>
30
31#include <stdexcept>
32
33#include <stdlib.h>
34#include <string.h>
35
36namespace me = mir::examples;
37namespace mi = mir::input;
38namespace mg = mir::graphics;
39namespace mtf = mir_test_framework;
40
41namespace
42{
43std::string const test_cursor_path{mir_test_framework::test_data_path() + std::string("/testing-cursor-theme")};
44}
45
46// Warning, XCURSOR_PATH will only be checked ONCE by libxcursor due to static var
47class XCursorLoaderTest : public ::testing::Test
48{
49public:
50 XCursorLoaderTest()
51 : xcursor_path("XCURSOR_PATH", test_cursor_path.c_str())
52 {
53 }
54
55 mtf::TemporaryEnvironmentValue xcursor_path;
56 me::XCursorLoader loader;
57};
58
59namespace
60{
61bool raw_argb_is_only_pixel(uint32_t const* raw_argb, size_t size, uint32_t pixel)
62{
63 for (unsigned int i = 0; i < size; i++)
64 {
65 if (raw_argb[i] != pixel)
66 {
67 printf("Pixel: %u\n", raw_argb[i]);
68 return false;
69 }
70 }
71 return true;
72}
73bool cursor_image_is_solid_color(std::shared_ptr<mg::CursorImage> const& image, uint32_t pixel)
74{
75 auto raw_argb = static_cast<uint32_t const*>(image->as_argb_8888());
76 size_t size = image->size().width.as_uint32_t() * image->size().height.as_uint32_t();
77
78 return raw_argb_is_only_pixel(raw_argb, size, pixel);
79}
80
81MATCHER(HasLoaded, "cursor image has loaded and is not nullptr."\
82 " Test expects cursor images to be installed to the directory the test is ran from")
83{
84 return arg != nullptr;
85}
86
87MATCHER(IsSolidRed, "")
88{
89 return cursor_image_is_solid_color(arg, 0xffff0000);
90}
91
92MATCHER(IsSolidGreen, "")
93{
94 return cursor_image_is_solid_color(arg, 0xff00ff00);
95}
96
97MATCHER(IsSolidBlue, "")
98{
99 return cursor_image_is_solid_color(arg, 0xff0000ff);
100}
101
102MATCHER(IsSolidBlack, "")
103{
104 return cursor_image_is_solid_color(arg, 0xff000000);
105}
106}
107
108TEST_F(XCursorLoaderTest, loads_cursors_from_testing_theme)
109{
110 auto size = mi::default_cursor_size;
111 auto red_image = loader.image("red", size);
112 auto blue_image = loader.image("blue", size);
113 auto green_image = loader.image("green", size);
114
115 ASSERT_THAT(red_image, HasLoaded());
116 ASSERT_THAT(green_image, HasLoaded());
117 ASSERT_THAT(blue_image, HasLoaded());
118 EXPECT_THAT(red_image, IsSolidRed());
119 EXPECT_THAT(green_image, IsSolidGreen());
120 EXPECT_THAT(blue_image, IsSolidBlue());
121}
122
123TEST_F(XCursorLoaderTest, only_supports_the_default_size)
124{
125 EXPECT_THROW({
126 loader.image("red", {100, 100});
127 }, std::logic_error);
128}
129
130TEST_F(XCursorLoaderTest, default_image_is_arrow_from_xcursor_theme)
131{
132 auto size = mi::default_cursor_size;
133 auto arrow_image = loader.image(mir_default_cursor_name, size);
134
135 // The testing theme uses a solid black image for the "arrow" symbolic
136 // name.
137 ASSERT_THAT(arrow_image, HasLoaded());
138 EXPECT_THAT(arrow_image, IsSolidBlack());
139}
140
141TEST_F(XCursorLoaderTest, symbolic_names_which_are_not_present_resolve_to_default)
142{
143 auto size = mi::default_cursor_size;
144 auto default_image = loader.image(mir_default_cursor_name, size);
145 auto image_with_made_up_name = loader.image("Artickrumbulis", size);
146
147 EXPECT_EQ(default_image, image_with_made_up_name);
148}

Subscribers

People subscribed via source and target branches