Mir

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

Proposed by Alan Griffiths on 2017-08-25
Status: Merged
Approved by: Alan Griffiths on 2017-08-31
Approved revision: 4267
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 on 2017-08-31
Gerry Boland 2017-08-25 Approve on 2017-08-30
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.
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)
4264. By Alan Griffiths on 2017-08-27

Find freetype before using it

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)
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)
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?

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
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!

4265. By Alan Griffiths on 2017-08-30

Revert unnecessary change

4266. By Alan Griffiths on 2017-08-30

merge --weave lp:~alan-griffiths/mir/mive-miral-to-mir-regeneration

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)
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)
4267. By Alan Griffiths on 2017-08-31

merge lp:~alan-griffiths/mir/mive-miral-to-mir-regeneration

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 }