Mir

Merge lp:~raof/mir/waylanding-again into lp:mir

Proposed by Chris Halse Rogers
Status: Merged
Approved by: Alan Griffiths
Approved revision: no longer in the source branch.
Merged at revision: 4244
Proposed branch: lp:~raof/mir/waylanding-again
Merge into: lp:mir
Diff against target: 4937 lines (+4487/-18)
32 files modified
CMakeLists.txt (+12/-0)
debian/control (+1/-0)
examples/CMakeLists.txt (+9/-0)
examples/wayland_client.c (+391/-0)
include/platform/mir/graphics/egl_extensions.h (+11/-0)
include/platform/mir/graphics/wayland_allocator.h (+45/-0)
include/server/mir/frontend/buffer_stream.h (+1/-0)
include/test/mir/test/doubles/mock_egl.h (+7/-0)
src/CMakeLists.txt (+1/-0)
src/include/server/mir/compositor/buffer_stream.h (+0/-1)
src/include/server/mir/default_server_configuration.h (+2/-0)
src/include/server/mir/server_configuration.h (+1/-0)
src/platform/graphics/CMakeLists.txt (+1/-0)
src/platform/graphics/egl_extensions.cpp (+34/-1)
src/platforms/mesa/server/CMakeLists.txt (+2/-0)
src/platforms/mesa/server/buffer_allocator.cpp (+231/-0)
src/platforms/mesa/server/buffer_allocator.h (+9/-1)
src/platforms/mesa/server/kms/CMakeLists.txt (+2/-0)
src/platforms/mesa/server/kms/platform.cpp (+14/-0)
src/protocol/CMakeLists.txt (+28/-0)
src/protocol/wrapper_generator.cpp (+509/-0)
src/server/CMakeLists.txt (+2/-0)
src/server/display_server.cpp (+11/-3)
src/server/frontend/CMakeLists.txt (+2/-0)
src/server/frontend/wayland/CMakeLists.txt (+14/-0)
src/server/frontend/wayland/core_generated_interfaces.h (+1189/-0)
src/server/frontend/wayland/wayland_connector.cpp (+1785/-0)
src/server/frontend/wayland/wayland_connector.h (+83/-0)
src/server/frontend/wayland/wayland_default_configuration.cpp (+39/-0)
src/server/symbols.map (+1/-0)
tests/mir_test_doubles/mock_egl.cpp (+48/-1)
tests/mir_test_doubles/nested_mock_egl.cpp (+2/-11)
To merge this branch: bzr merge lp:~raof/mir/waylanding-again
Reviewer Review Type Date Requested Status
Alan Griffiths Approve
Mir CI Bot continuous-integration Approve
Review via email: mp+329566@code.launchpad.net

Commit message

First round of Wayland support

This is sufficient to
a) Not break unrelated tests, and
b) Provide enough protocol to run es2gears_wayland
c) Provide input support to Wayland clients

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

FAILED: Continuous integration, rev:4177
https://mir-jenkins.ubuntu.com/job/mir-ci/3582/
Executed test runs:
    FAILURE: https://mir-jenkins.ubuntu.com/job/build-mir/4905/console
    SUCCESS: https://mir-jenkins.ubuntu.com/job/build-0-fetch/5127
    SUCCESS: https://mir-jenkins.ubuntu.com/job/build-1-sourcepkg/release=artful/5116
    SUCCESS: https://mir-jenkins.ubuntu.com/job/build-1-sourcepkg/release=xenial/5116
    SUCCESS: https://mir-jenkins.ubuntu.com/job/build-1-sourcepkg/release=zesty/5116
    FAILURE: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-mir/arch=amd64,compiler=clang,platform=mesa,release=artful/4944/console
    FAILURE: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-mir/arch=amd64,compiler=clang,platform=mesa,release=zesty/4944/console
    FAILURE: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-mir/arch=amd64,compiler=gcc,platform=mesa,release=artful/4944/console
    FAILURE: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-mir/arch=amd64,compiler=gcc,platform=mesa,release=xenial/4944/console
    FAILURE: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-mir/arch=amd64,compiler=gcc,platform=mesa,release=zesty/4944/console
    FAILURE: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-mir/arch=cross-armhf,compiler=gcc,platform=mesa,release=artful/4944/console
    FAILURE: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-mir/arch=cross-armhf,compiler=gcc,platform=mesa,release=zesty/4944/console
    FAILURE: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-mir/arch=i386,compiler=gcc,platform=mesa,release=xenial/4944/console

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

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

FAILED: Continuous integration, rev:4179
https://mir-jenkins.ubuntu.com/job/mir-ci/3583/
Executed test runs:
    FAILURE: https://mir-jenkins.ubuntu.com/job/build-mir/4906/console
    SUCCESS: https://mir-jenkins.ubuntu.com/job/build-0-fetch/5128
    SUCCESS: https://mir-jenkins.ubuntu.com/job/build-1-sourcepkg/release=artful/5117
    SUCCESS: https://mir-jenkins.ubuntu.com/job/build-1-sourcepkg/release=xenial/5117
    SUCCESS: https://mir-jenkins.ubuntu.com/job/build-1-sourcepkg/release=zesty/5117
    FAILURE: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-mir/arch=amd64,compiler=clang,platform=mesa,release=artful/4945/console
    FAILURE: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-mir/arch=amd64,compiler=clang,platform=mesa,release=zesty/4945/console
    FAILURE: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-mir/arch=amd64,compiler=gcc,platform=mesa,release=artful/4945/console
    FAILURE: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-mir/arch=amd64,compiler=gcc,platform=mesa,release=xenial/4945/console
    FAILURE: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-mir/arch=amd64,compiler=gcc,platform=mesa,release=zesty/4945/console
    FAILURE: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-mir/arch=cross-armhf,compiler=gcc,platform=mesa,release=artful/4945/console
    FAILURE: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-mir/arch=cross-armhf,compiler=gcc,platform=mesa,release=zesty/4945/console
    FAILURE: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-mir/arch=i386,compiler=gcc,platform=mesa,release=xenial/4945/console

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

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

FAILED: Continuous integration, rev:4180
https://mir-jenkins.ubuntu.com/job/mir-ci/3584/
Executed test runs:
    FAILURE: https://mir-jenkins.ubuntu.com/job/build-mir/4907/console
    SUCCESS: https://mir-jenkins.ubuntu.com/job/build-0-fetch/5129
    SUCCESS: https://mir-jenkins.ubuntu.com/job/build-1-sourcepkg/release=artful/5118
    SUCCESS: https://mir-jenkins.ubuntu.com/job/build-1-sourcepkg/release=xenial/5118
    SUCCESS: https://mir-jenkins.ubuntu.com/job/build-1-sourcepkg/release=zesty/5118
    FAILURE: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-mir/arch=amd64,compiler=clang,platform=mesa,release=artful/4946/console
    FAILURE: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-mir/arch=amd64,compiler=clang,platform=mesa,release=zesty/4946/console
    FAILURE: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-mir/arch=amd64,compiler=gcc,platform=mesa,release=artful/4946/console
    FAILURE: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-mir/arch=amd64,compiler=gcc,platform=mesa,release=xenial/4946/console
    FAILURE: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-mir/arch=amd64,compiler=gcc,platform=mesa,release=zesty/4946/console
    FAILURE: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-mir/arch=cross-armhf,compiler=gcc,platform=mesa,release=artful/4946/console
    FAILURE: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-mir/arch=cross-armhf,compiler=gcc,platform=mesa,release=zesty/4946/console
    FAILURE: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-mir/arch=i386,compiler=gcc,platform=mesa,release=xenial/4946/console

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

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

FAILED: Continuous integration, rev:4183
https://mir-jenkins.ubuntu.com/job/mir-ci/3588/
Executed test runs:
    FAILURE: https://mir-jenkins.ubuntu.com/job/build-mir/4911/console
    SUCCESS: https://mir-jenkins.ubuntu.com/job/build-0-fetch/5136
    SUCCESS: https://mir-jenkins.ubuntu.com/job/build-1-sourcepkg/release=artful/5125
    SUCCESS: https://mir-jenkins.ubuntu.com/job/build-1-sourcepkg/release=xenial/5125
    SUCCESS: https://mir-jenkins.ubuntu.com/job/build-1-sourcepkg/release=zesty/5125
    FAILURE: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-mir/arch=amd64,compiler=clang,platform=mesa,release=artful/4950/console
    FAILURE: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-mir/arch=amd64,compiler=clang,platform=mesa,release=zesty/4950/console
    FAILURE: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-mir/arch=amd64,compiler=gcc,platform=mesa,release=artful/4950/console
    FAILURE: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-mir/arch=amd64,compiler=gcc,platform=mesa,release=xenial/4950/console
    FAILURE: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-mir/arch=amd64,compiler=gcc,platform=mesa,release=zesty/4950/console
    FAILURE: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-mir/arch=cross-armhf,compiler=gcc,platform=mesa,release=artful/4950/console
    FAILURE: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-mir/arch=cross-armhf,compiler=gcc,platform=mesa,release=zesty/4950/console
    FAILURE: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-mir/arch=i386,compiler=gcc,platform=mesa,release=xenial/4950/console

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

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

FAILED: Continuous integration, rev:4183
https://mir-jenkins.ubuntu.com/job/mir-ci/3589/
Executed test runs:
    FAILURE: https://mir-jenkins.ubuntu.com/job/build-mir/4912/console
    SUCCESS: https://mir-jenkins.ubuntu.com/job/build-0-fetch/5137
    SUCCESS: https://mir-jenkins.ubuntu.com/job/build-1-sourcepkg/release=artful/5126
    SUCCESS: https://mir-jenkins.ubuntu.com/job/build-1-sourcepkg/release=xenial/5126
    SUCCESS: https://mir-jenkins.ubuntu.com/job/build-1-sourcepkg/release=zesty/5126
    FAILURE: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-mir/arch=amd64,compiler=clang,platform=mesa,release=artful/4951/console
    FAILURE: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-mir/arch=amd64,compiler=clang,platform=mesa,release=zesty/4951/console
    FAILURE: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-mir/arch=amd64,compiler=gcc,platform=mesa,release=artful/4951/console
    FAILURE: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-mir/arch=amd64,compiler=gcc,platform=mesa,release=xenial/4951/console
    FAILURE: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-mir/arch=amd64,compiler=gcc,platform=mesa,release=zesty/4951/console
    FAILURE: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-mir/arch=cross-armhf,compiler=gcc,platform=mesa,release=artful/4951/console
    FAILURE: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-mir/arch=cross-armhf,compiler=gcc,platform=mesa,release=zesty/4951/console
    FAILURE: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-mir/arch=i386,compiler=gcc,platform=mesa,release=xenial/4951/console

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

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

FAILED: Continuous integration, rev:4185
https://mir-jenkins.ubuntu.com/job/mir-ci/3590/
Executed test runs:
    FAILURE: https://mir-jenkins.ubuntu.com/job/build-mir/4913/console
    SUCCESS: https://mir-jenkins.ubuntu.com/job/build-0-fetch/5138
    SUCCESS: https://mir-jenkins.ubuntu.com/job/build-1-sourcepkg/release=artful/5127
    SUCCESS: https://mir-jenkins.ubuntu.com/job/build-1-sourcepkg/release=xenial/5127
    SUCCESS: https://mir-jenkins.ubuntu.com/job/build-1-sourcepkg/release=zesty/5127
    FAILURE: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-mir/arch=amd64,compiler=clang,platform=mesa,release=artful/4952/console
    FAILURE: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-mir/arch=amd64,compiler=clang,platform=mesa,release=zesty/4952/console
    FAILURE: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-mir/arch=amd64,compiler=gcc,platform=mesa,release=artful/4952/console
    FAILURE: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-mir/arch=amd64,compiler=gcc,platform=mesa,release=xenial/4952/console
    FAILURE: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-mir/arch=amd64,compiler=gcc,platform=mesa,release=zesty/4952/console
    FAILURE: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-mir/arch=cross-armhf,compiler=gcc,platform=mesa,release=artful/4952/console
    FAILURE: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-mir/arch=cross-armhf,compiler=gcc,platform=mesa,release=zesty/4952/console
    FAILURE: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-mir/arch=i386,compiler=gcc,platform=mesa,release=xenial/4952/console

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

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

FAILED: Continuous integration, rev:4186
https://mir-jenkins.ubuntu.com/job/mir-ci/3591/
Executed test runs:
    FAILURE: https://mir-jenkins.ubuntu.com/job/build-mir/4915/console
    SUCCESS: https://mir-jenkins.ubuntu.com/job/build-0-fetch/5140
    SUCCESS: https://mir-jenkins.ubuntu.com/job/build-1-sourcepkg/release=artful/5129
    SUCCESS: https://mir-jenkins.ubuntu.com/job/build-1-sourcepkg/release=xenial/5129
    SUCCESS: https://mir-jenkins.ubuntu.com/job/build-1-sourcepkg/release=zesty/5129
    FAILURE: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-mir/arch=amd64,compiler=clang,platform=mesa,release=artful/4954/console
    FAILURE: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-mir/arch=amd64,compiler=clang,platform=mesa,release=zesty/4954/console
    FAILURE: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-mir/arch=amd64,compiler=gcc,platform=mesa,release=artful/4954/console
    FAILURE: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-mir/arch=amd64,compiler=gcc,platform=mesa,release=xenial/4954/console
    FAILURE: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-mir/arch=amd64,compiler=gcc,platform=mesa,release=zesty/4954/console
    FAILURE: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-mir/arch=cross-armhf,compiler=gcc,platform=mesa,release=artful/4954/console
    FAILURE: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-mir/arch=cross-armhf,compiler=gcc,platform=mesa,release=zesty/4954/console
    FAILURE: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-mir/arch=i386,compiler=gcc,platform=mesa,release=xenial/4954/console

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

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

FAILED: Continuous integration, rev:4188
https://mir-jenkins.ubuntu.com/job/mir-ci/3593/
Executed test runs:
    FAILURE: https://mir-jenkins.ubuntu.com/job/build-mir/4917/console
    SUCCESS: https://mir-jenkins.ubuntu.com/job/build-0-fetch/5142
    SUCCESS: https://mir-jenkins.ubuntu.com/job/build-1-sourcepkg/release=artful/5131
    SUCCESS: https://mir-jenkins.ubuntu.com/job/build-1-sourcepkg/release=xenial/5131
    SUCCESS: https://mir-jenkins.ubuntu.com/job/build-1-sourcepkg/release=zesty/5131
    SUCCESS: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-mir/arch=amd64,compiler=clang,platform=mesa,release=artful/4956
        deb: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-mir/arch=amd64,compiler=clang,platform=mesa,release=artful/4956/artifact/output/*zip*/output.zip
    SUCCESS: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-mir/arch=amd64,compiler=clang,platform=mesa,release=zesty/4956
        deb: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-mir/arch=amd64,compiler=clang,platform=mesa,release=zesty/4956/artifact/output/*zip*/output.zip
    SUCCESS: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-mir/arch=amd64,compiler=gcc,platform=mesa,release=artful/4956
        deb: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-mir/arch=amd64,compiler=gcc,platform=mesa,release=artful/4956/artifact/output/*zip*/output.zip
    SUCCESS: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-mir/arch=amd64,compiler=gcc,platform=mesa,release=xenial/4956
        deb: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-mir/arch=amd64,compiler=gcc,platform=mesa,release=xenial/4956/artifact/output/*zip*/output.zip
    SUCCESS: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-mir/arch=amd64,compiler=gcc,platform=mesa,release=zesty/4956
        deb: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-mir/arch=amd64,compiler=gcc,platform=mesa,release=zesty/4956/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/4956/console
    FAILURE: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-mir/arch=cross-armhf,compiler=gcc,platform=mesa,release=zesty/4956/console
    SUCCESS: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-mir/arch=i386,compiler=gcc,platform=mesa,release=xenial/4956
        deb: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-mir/arch=i386,compiler=gcc,platform=mesa,release=xenial/4956/artifact/output/*zip*/output.zip

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

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

FAILED: Continuous integration, rev:4189
https://mir-jenkins.ubuntu.com/job/mir-ci/3594/
Executed test runs:
    FAILURE: https://mir-jenkins.ubuntu.com/job/build-mir/4918/console
    SUCCESS: https://mir-jenkins.ubuntu.com/job/build-0-fetch/5143
    SUCCESS: https://mir-jenkins.ubuntu.com/job/build-1-sourcepkg/release=artful/5132
    SUCCESS: https://mir-jenkins.ubuntu.com/job/build-1-sourcepkg/release=xenial/5132
    SUCCESS: https://mir-jenkins.ubuntu.com/job/build-1-sourcepkg/release=zesty/5132
    SUCCESS: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-mir/arch=amd64,compiler=clang,platform=mesa,release=artful/4957
        deb: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-mir/arch=amd64,compiler=clang,platform=mesa,release=artful/4957/artifact/output/*zip*/output.zip
    SUCCESS: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-mir/arch=amd64,compiler=clang,platform=mesa,release=zesty/4957
        deb: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-mir/arch=amd64,compiler=clang,platform=mesa,release=zesty/4957/artifact/output/*zip*/output.zip
    SUCCESS: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-mir/arch=amd64,compiler=gcc,platform=mesa,release=artful/4957
        deb: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-mir/arch=amd64,compiler=gcc,platform=mesa,release=artful/4957/artifact/output/*zip*/output.zip
    SUCCESS: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-mir/arch=amd64,compiler=gcc,platform=mesa,release=xenial/4957
        deb: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-mir/arch=amd64,compiler=gcc,platform=mesa,release=xenial/4957/artifact/output/*zip*/output.zip
    SUCCESS: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-mir/arch=amd64,compiler=gcc,platform=mesa,release=zesty/4957
        deb: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-mir/arch=amd64,compiler=gcc,platform=mesa,release=zesty/4957/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/4957/console
    FAILURE: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-mir/arch=cross-armhf,compiler=gcc,platform=mesa,release=zesty/4957/console
    SUCCESS: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-mir/arch=i386,compiler=gcc,platform=mesa,release=xenial/4957
        deb: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-mir/arch=i386,compiler=gcc,platform=mesa,release=xenial/4957/artifact/output/*zip*/output.zip

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

review: Needs Fixing (continuous-integration)
Revision history for this message
Chris Halse Rogers (raof) wrote :

Oh, balls.

Cross-compilation with generated binaries. Sigh.

Revision history for this message
Gerry Boland (gerboland) wrote :

+class WaylandBuffer :
+ void gl_bind_to_texture() override
+ for (auto&& frame : frames)
+ {
+ auto framer = std::move(frame);
+ executor->spawn(
+ [frame = framer.release(), deleter = framer.get_deleter()]()
+ {
+ wl_callback_send_done(frame, 0);
+ wl_client_flush(wl_resource_get_client(frame));
+ deleter(frame);
+ });
+ }
+ frames.clear();

More a question of intent: if a client has no new frame to draw, are we doing wrong to delete the frame on the server? I would expect we only delete a frame when we have got a new one from the client.

Or instead do we tell the client, compositor has released the frame, and client can then specify that buffer be re-used? That seems a bit wasteful. /me trying to interpret the buffer exchange design from wayland.xml

It's the fact you need the Executor just to deal with frame release, that made me bring this up.

+ struct DestructionShim
+ {
+ std::shared_ptr<std::mutex> const mutex = std::make_shared<std::mutex>();
The way you're sharing this mutex between this and the WaylandBuffer is a bit peculiar looking, but I suspect unavoidable, as you need the mutex for the on_buffer_destroyed callback. You *could* use the void*, but what you're doing is indeed cleaner.

+ if (dpy == EGL_NO_DISPLAY)
+ BOOST_THROW_EXCEPTION((std::runtime_error{"Fuck"}));
Mir has been rated PG13 due to its low expletive, violence and sexual content, please don't jeopardise that. The kids are where its at for all that sweet merchandising profit.

+++ src/platforms/mesa/server/kms/platform.cpp
does this file need to change? I only see header files being added.

+++ src/protocol/wrapper_generator.cpp
Missing licence.

to be continued...

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

FAILED: Continuous integration, rev:4190
https://mir-jenkins.ubuntu.com/job/mir-ci/3602/
Executed test runs:
    FAILURE: https://mir-jenkins.ubuntu.com/job/build-mir/4926/console
    SUCCESS: https://mir-jenkins.ubuntu.com/job/build-0-fetch/5151
    SUCCESS: https://mir-jenkins.ubuntu.com/job/build-1-sourcepkg/release=artful/5141
    SUCCESS: https://mir-jenkins.ubuntu.com/job/build-1-sourcepkg/release=xenial/5141
    SUCCESS: https://mir-jenkins.ubuntu.com/job/build-1-sourcepkg/release=zesty/5141
    SUCCESS: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-mir/arch=amd64,compiler=clang,platform=mesa,release=artful/4965
        deb: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-mir/arch=amd64,compiler=clang,platform=mesa,release=artful/4965/artifact/output/*zip*/output.zip
    SUCCESS: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-mir/arch=amd64,compiler=clang,platform=mesa,release=zesty/4965
        deb: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-mir/arch=amd64,compiler=clang,platform=mesa,release=zesty/4965/artifact/output/*zip*/output.zip
    SUCCESS: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-mir/arch=amd64,compiler=gcc,platform=mesa,release=artful/4965
        deb: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-mir/arch=amd64,compiler=gcc,platform=mesa,release=artful/4965/artifact/output/*zip*/output.zip
    SUCCESS: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-mir/arch=amd64,compiler=gcc,platform=mesa,release=xenial/4965
        deb: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-mir/arch=amd64,compiler=gcc,platform=mesa,release=xenial/4965/artifact/output/*zip*/output.zip
    SUCCESS: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-mir/arch=amd64,compiler=gcc,platform=mesa,release=zesty/4965
        deb: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-mir/arch=amd64,compiler=gcc,platform=mesa,release=zesty/4965/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/4965/console
    FAILURE: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-mir/arch=cross-armhf,compiler=gcc,platform=mesa,release=zesty/4965/console
    SUCCESS: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-mir/arch=i386,compiler=gcc,platform=mesa,release=xenial/4965
        deb: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-mir/arch=i386,compiler=gcc,platform=mesa,release=xenial/4965/artifact/output/*zip*/output.zip

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

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

FAILED: Continuous integration, rev:4190
https://mir-jenkins.ubuntu.com/job/mir-ci/3603/
Executed test runs:
    FAILURE: https://mir-jenkins.ubuntu.com/job/build-mir/4927/console
    SUCCESS: https://mir-jenkins.ubuntu.com/job/build-0-fetch/5152
    SUCCESS: https://mir-jenkins.ubuntu.com/job/build-1-sourcepkg/release=artful/5142
    SUCCESS: https://mir-jenkins.ubuntu.com/job/build-1-sourcepkg/release=xenial/5142
    SUCCESS: https://mir-jenkins.ubuntu.com/job/build-1-sourcepkg/release=zesty/5142
    FAILURE: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-mir/arch=amd64,compiler=clang,platform=mesa,release=artful/4966/console
    FAILURE: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-mir/arch=amd64,compiler=clang,platform=mesa,release=zesty/4966/console
    FAILURE: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-mir/arch=amd64,compiler=gcc,platform=mesa,release=artful/4966/console
    FAILURE: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-mir/arch=amd64,compiler=gcc,platform=mesa,release=xenial/4966/console
    FAILURE: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-mir/arch=amd64,compiler=gcc,platform=mesa,release=zesty/4966/console
    FAILURE: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-mir/arch=cross-armhf,compiler=gcc,platform=mesa,release=artful/4966/console
    FAILURE: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-mir/arch=cross-armhf,compiler=gcc,platform=mesa,release=zesty/4966/console
    FAILURE: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-mir/arch=i386,compiler=gcc,platform=mesa,release=xenial/4966/console

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

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

FAILED: Continuous integration, rev:4190
https://mir-jenkins.ubuntu.com/job/mir-ci/3604/
Executed test runs:
    FAILURE: https://mir-jenkins.ubuntu.com/job/build-mir/4928/console
    SUCCESS: https://mir-jenkins.ubuntu.com/job/build-0-fetch/5153
    SUCCESS: https://mir-jenkins.ubuntu.com/job/build-1-sourcepkg/release=artful/5143
    SUCCESS: https://mir-jenkins.ubuntu.com/job/build-1-sourcepkg/release=xenial/5143
    SUCCESS: https://mir-jenkins.ubuntu.com/job/build-1-sourcepkg/release=zesty/5143
    SUCCESS: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-mir/arch=amd64,compiler=clang,platform=mesa,release=artful/4967
        deb: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-mir/arch=amd64,compiler=clang,platform=mesa,release=artful/4967/artifact/output/*zip*/output.zip
    SUCCESS: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-mir/arch=amd64,compiler=clang,platform=mesa,release=zesty/4967
        deb: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-mir/arch=amd64,compiler=clang,platform=mesa,release=zesty/4967/artifact/output/*zip*/output.zip
    SUCCESS: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-mir/arch=amd64,compiler=gcc,platform=mesa,release=artful/4967
        deb: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-mir/arch=amd64,compiler=gcc,platform=mesa,release=artful/4967/artifact/output/*zip*/output.zip
    SUCCESS: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-mir/arch=amd64,compiler=gcc,platform=mesa,release=xenial/4967
        deb: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-mir/arch=amd64,compiler=gcc,platform=mesa,release=xenial/4967/artifact/output/*zip*/output.zip
    SUCCESS: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-mir/arch=amd64,compiler=gcc,platform=mesa,release=zesty/4967
        deb: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-mir/arch=amd64,compiler=gcc,platform=mesa,release=zesty/4967/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/4967/console
    FAILURE: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-mir/arch=cross-armhf,compiler=gcc,platform=mesa,release=zesty/4967/console
    SUCCESS: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-mir/arch=i386,compiler=gcc,platform=mesa,release=xenial/4967
        deb: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-mir/arch=i386,compiler=gcc,platform=mesa,release=xenial/4967/artifact/output/*zip*/output.zip

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

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

FAILED: Continuous integration, rev:4190
https://mir-jenkins.ubuntu.com/job/mir-ci/3605/
Executed test runs:
    FAILURE: https://mir-jenkins.ubuntu.com/job/build-mir/4929/console
    SUCCESS: https://mir-jenkins.ubuntu.com/job/build-0-fetch/5154
    SUCCESS: https://mir-jenkins.ubuntu.com/job/build-1-sourcepkg/release=artful/5144
    SUCCESS: https://mir-jenkins.ubuntu.com/job/build-1-sourcepkg/release=xenial/5144
    SUCCESS: https://mir-jenkins.ubuntu.com/job/build-1-sourcepkg/release=zesty/5144
    FAILURE: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-mir/arch=amd64,compiler=clang,platform=mesa,release=artful/4968/console
    SUCCESS: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-mir/arch=amd64,compiler=clang,platform=mesa,release=zesty/4968
        deb: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-mir/arch=amd64,compiler=clang,platform=mesa,release=zesty/4968/artifact/output/*zip*/output.zip
    SUCCESS: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-mir/arch=amd64,compiler=gcc,platform=mesa,release=artful/4968
        deb: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-mir/arch=amd64,compiler=gcc,platform=mesa,release=artful/4968/artifact/output/*zip*/output.zip
    SUCCESS: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-mir/arch=amd64,compiler=gcc,platform=mesa,release=xenial/4968
        deb: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-mir/arch=amd64,compiler=gcc,platform=mesa,release=xenial/4968/artifact/output/*zip*/output.zip
    SUCCESS: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-mir/arch=amd64,compiler=gcc,platform=mesa,release=zesty/4968
        deb: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-mir/arch=amd64,compiler=gcc,platform=mesa,release=zesty/4968/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/4968/console
    FAILURE: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-mir/arch=cross-armhf,compiler=gcc,platform=mesa,release=zesty/4968/console
    SUCCESS: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-mir/arch=i386,compiler=gcc,platform=mesa,release=xenial/4968
        deb: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-mir/arch=i386,compiler=gcc,platform=mesa,release=xenial/4968/artifact/output/*zip*/output.zip

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

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

FAILED: Continuous integration, rev:4192
https://mir-jenkins.ubuntu.com/job/mir-ci/3606/
Executed test runs:
    FAILURE: https://mir-jenkins.ubuntu.com/job/build-mir/4930/console
    SUCCESS: https://mir-jenkins.ubuntu.com/job/build-0-fetch/5155
    SUCCESS: https://mir-jenkins.ubuntu.com/job/build-1-sourcepkg/release=artful/5145
    SUCCESS: https://mir-jenkins.ubuntu.com/job/build-1-sourcepkg/release=xenial/5145
    SUCCESS: https://mir-jenkins.ubuntu.com/job/build-1-sourcepkg/release=zesty/5145
    SUCCESS: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-mir/arch=amd64,compiler=clang,platform=mesa,release=artful/4969
        deb: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-mir/arch=amd64,compiler=clang,platform=mesa,release=artful/4969/artifact/output/*zip*/output.zip
    SUCCESS: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-mir/arch=amd64,compiler=clang,platform=mesa,release=zesty/4969
        deb: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-mir/arch=amd64,compiler=clang,platform=mesa,release=zesty/4969/artifact/output/*zip*/output.zip
    SUCCESS: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-mir/arch=amd64,compiler=gcc,platform=mesa,release=artful/4969
        deb: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-mir/arch=amd64,compiler=gcc,platform=mesa,release=artful/4969/artifact/output/*zip*/output.zip
    SUCCESS: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-mir/arch=amd64,compiler=gcc,platform=mesa,release=xenial/4969
        deb: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-mir/arch=amd64,compiler=gcc,platform=mesa,release=xenial/4969/artifact/output/*zip*/output.zip
    SUCCESS: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-mir/arch=amd64,compiler=gcc,platform=mesa,release=zesty/4969
        deb: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-mir/arch=amd64,compiler=gcc,platform=mesa,release=zesty/4969/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/4969/console
    FAILURE: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-mir/arch=cross-armhf,compiler=gcc,platform=mesa,release=zesty/4969/console
    SUCCESS: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-mir/arch=i386,compiler=gcc,platform=mesa,release=xenial/4969
        deb: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-mir/arch=i386,compiler=gcc,platform=mesa,release=xenial/4969/artifact/output/*zip*/output.zip

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

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

PASSED: Continuous integration, rev:4193
https://mir-jenkins.ubuntu.com/job/mir-ci/3615/
Executed test runs:
    SUCCESS: https://mir-jenkins.ubuntu.com/job/build-mir/4942
    SUCCESS: https://mir-jenkins.ubuntu.com/job/build-0-fetch/5167
    SUCCESS: https://mir-jenkins.ubuntu.com/job/build-1-sourcepkg/release=artful/5157
    SUCCESS: https://mir-jenkins.ubuntu.com/job/build-1-sourcepkg/release=xenial/5157
    SUCCESS: https://mir-jenkins.ubuntu.com/job/build-1-sourcepkg/release=zesty/5157
    SUCCESS: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-mir/arch=amd64,compiler=clang,platform=mesa,release=artful/4981
        deb: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-mir/arch=amd64,compiler=clang,platform=mesa,release=artful/4981/artifact/output/*zip*/output.zip
    SUCCESS: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-mir/arch=amd64,compiler=clang,platform=mesa,release=zesty/4981
        deb: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-mir/arch=amd64,compiler=clang,platform=mesa,release=zesty/4981/artifact/output/*zip*/output.zip
    SUCCESS: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-mir/arch=amd64,compiler=gcc,platform=mesa,release=artful/4981
        deb: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-mir/arch=amd64,compiler=gcc,platform=mesa,release=artful/4981/artifact/output/*zip*/output.zip
    SUCCESS: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-mir/arch=amd64,compiler=gcc,platform=mesa,release=xenial/4981
        deb: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-mir/arch=amd64,compiler=gcc,platform=mesa,release=xenial/4981/artifact/output/*zip*/output.zip
    SUCCESS: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-mir/arch=amd64,compiler=gcc,platform=mesa,release=zesty/4981
        deb: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-mir/arch=amd64,compiler=gcc,platform=mesa,release=zesty/4981/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/4981
        deb: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-mir/arch=cross-armhf,compiler=gcc,platform=mesa,release=artful/4981/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/4981
        deb: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-mir/arch=cross-armhf,compiler=gcc,platform=mesa,release=zesty/4981/artifact/output/*zip*/output.zip
    SUCCESS: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-mir/arch=i386,compiler=gcc,platform=mesa,release=xenial/4981
        deb: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-mir/arch=i386,compiler=gcc,platform=mesa,release=xenial/4981/artifact/output/*zip*/output.zip

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

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

PASSED: Continuous integration, rev:4195
https://mir-jenkins.ubuntu.com/job/mir-ci/3616/
Executed test runs:
    SUCCESS: https://mir-jenkins.ubuntu.com/job/build-mir/4943
    SUCCESS: https://mir-jenkins.ubuntu.com/job/build-0-fetch/5168
    SUCCESS: https://mir-jenkins.ubuntu.com/job/build-1-sourcepkg/release=artful/5158
    SUCCESS: https://mir-jenkins.ubuntu.com/job/build-1-sourcepkg/release=xenial/5158
    SUCCESS: https://mir-jenkins.ubuntu.com/job/build-1-sourcepkg/release=zesty/5158
    SUCCESS: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-mir/arch=amd64,compiler=clang,platform=mesa,release=artful/4982
        deb: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-mir/arch=amd64,compiler=clang,platform=mesa,release=artful/4982/artifact/output/*zip*/output.zip
    SUCCESS: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-mir/arch=amd64,compiler=clang,platform=mesa,release=zesty/4982
        deb: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-mir/arch=amd64,compiler=clang,platform=mesa,release=zesty/4982/artifact/output/*zip*/output.zip
    SUCCESS: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-mir/arch=amd64,compiler=gcc,platform=mesa,release=artful/4982
        deb: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-mir/arch=amd64,compiler=gcc,platform=mesa,release=artful/4982/artifact/output/*zip*/output.zip
    SUCCESS: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-mir/arch=amd64,compiler=gcc,platform=mesa,release=xenial/4982
        deb: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-mir/arch=amd64,compiler=gcc,platform=mesa,release=xenial/4982/artifact/output/*zip*/output.zip
    SUCCESS: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-mir/arch=amd64,compiler=gcc,platform=mesa,release=zesty/4982
        deb: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-mir/arch=amd64,compiler=gcc,platform=mesa,release=zesty/4982/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/4982
        deb: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-mir/arch=cross-armhf,compiler=gcc,platform=mesa,release=artful/4982/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/4982
        deb: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-mir/arch=cross-armhf,compiler=gcc,platform=mesa,release=zesty/4982/artifact/output/*zip*/output.zip
    SUCCESS: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-mir/arch=i386,compiler=gcc,platform=mesa,release=xenial/4982
        deb: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-mir/arch=i386,compiler=gcc,platform=mesa,release=xenial/4982/artifact/output/*zip*/output.zip

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

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

+ DestructionShim* shim;
+
+ shim = wl_container_of(notifier, shim, destruction_listener);

I thought we were passing an uninitialized value to a function, but no, its a nasty lowercase MACRO imitating a function, but doing something a function can't!!

I guess this isn't something we can fix.

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

[terminal 1]
$ bin/miral-shell

[terminal 2]
$ bin/mir_demo_client_wayland
...
^C

The server crashes (this doesn't happen with using Shift-Alt-F4 to send SIGTERM).

~~~~

+ * Copyright © 2015 Canonical LTD

Really 2015? We use "Ltd." elsewhere.

~~~~

+ GNU General Public License version 3

2 or 3

~~~~

+#include "../../../../include/server/mir/executor.h"

1. Not the approved way to reach a header.
2. Platforms shouldn't be depending on mirserver (yes I realize it's an interface)

~~~~

+#include "../../scene/mediating_display_changer.h"

1. Not the approved way to reach a header.
2. The frontend shouldn't be dependent on scene internals. (It should define an abstraction that scene can implement.)

~~~~

I just merged to trunk and "built" and got...

/home/alan/display_server/mir1/src/server/frontend/wayland/wayland_connector.cpp:21:10: fatal error: 'core_generated_interfaces.h' file not found
#include "core_generated_interfaces.h"
         ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~

"make refresh-wayland-wrapper" fixes that, but something isn't quite right.

review: Needs Fixing
Revision history for this message
Chris Halse Rogers (raof) wrote :

Heh. Turns out that doing the not-quite-correct-but-near-enough thing is *easier* than I thought; this now also renders at the right size.

Revision history for this message
Gerry Boland (gerboland) wrote :

Nice work so far, I can bring up wayland glmark and Qt apps using wayland, and they render ok.

Input is problematic. Seems Qt apps not getting input events. And moving mouse over glmark quickly causes server lockup.

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

I'm off work until Tuesday, so won't be reviewing in detail until then. But looks like my "Needs Fixings" are being fixed.

review: Abstain
Revision history for this message
Chris Halse Rogers (raof) wrote :

Hah! The reason why input is problematic is that the *window* is only set to 100x100! You only get input in the top-left-hand corner.

That took altogether too long to figure out.

Revision history for this message
Gerry Boland (gerboland) wrote :

Little patch to make it compile on artful: https://pastebin.ubuntu.com/25464701/

Input is more stable now, but something broke Qt app rendering - colours are there, but each line offset by a little, as if window width not quite right.

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

FAILED: Continuous integration, rev:4196
https://mir-jenkins.ubuntu.com/job/mir-ci/3628/
Executed test runs:
    FAILURE: https://mir-jenkins.ubuntu.com/job/build-mir/4957/console
    SUCCESS: https://mir-jenkins.ubuntu.com/job/build-0-fetch/5182
    SUCCESS: https://mir-jenkins.ubuntu.com/job/build-1-sourcepkg/release=artful/5172
    SUCCESS: https://mir-jenkins.ubuntu.com/job/build-1-sourcepkg/release=xenial/5172
    SUCCESS: https://mir-jenkins.ubuntu.com/job/build-1-sourcepkg/release=zesty/5172
    FAILURE: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-mir/arch=amd64,compiler=clang,platform=mesa,release=artful/4996/console
    SUCCESS: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-mir/arch=amd64,compiler=clang,platform=mesa,release=zesty/4996
        deb: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-mir/arch=amd64,compiler=clang,platform=mesa,release=zesty/4996/artifact/output/*zip*/output.zip
    FAILURE: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-mir/arch=amd64,compiler=gcc,platform=mesa,release=artful/4996/console
    SUCCESS: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-mir/arch=amd64,compiler=gcc,platform=mesa,release=xenial/4996
        deb: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-mir/arch=amd64,compiler=gcc,platform=mesa,release=xenial/4996/artifact/output/*zip*/output.zip
    SUCCESS: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-mir/arch=amd64,compiler=gcc,platform=mesa,release=zesty/4996
        deb: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-mir/arch=amd64,compiler=gcc,platform=mesa,release=zesty/4996/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/4996/console
    SUCCESS: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-mir/arch=cross-armhf,compiler=gcc,platform=mesa,release=zesty/4996
        deb: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-mir/arch=cross-armhf,compiler=gcc,platform=mesa,release=zesty/4996/artifact/output/*zip*/output.zip
    SUCCESS: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-mir/arch=i386,compiler=gcc,platform=mesa,release=xenial/4996
        deb: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-mir/arch=i386,compiler=gcc,platform=mesa,release=xenial/4996/artifact/output/*zip*/output.zip

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

review: Needs Fixing (continuous-integration)
Revision history for this message
Alan Griffiths (alan-griffiths) wrote :

I just merged to trunk and "built" and got...

../libmirsharedmesaservercommon-static.a(buffer_allocator.cpp.o): In function `WaylandBuffer':
/home/alan/display_server/mir/src/platforms/mesa/server/buffer_allocator.cpp:346: undefined reference to `wl_resource_get_destroy_listener'
/home/alan/display_server/mir/src/platforms/mesa/server/buffer_allocator.cpp:365: undefined reference to `wl_resource_add_destroy_listener'
../libmirsharedmesaservercommon-static.a(buffer_allocator.cpp.o): In function `~WaylandBuffer':
/home/alan/display_server/mir/src/platforms/mesa/server/buffer_allocator.cpp:405: undefined reference to `wl_resource_queue_event'
/home/alan/display_server/mir/src/platforms/mesa/server/buffer_allocator.cpp:406: undefined reference to `wl_resource_get_destroy_listener'
clang: error: linker command failed with exit code 1 (use -v to see invocation)
src/platforms/mesa/server/x11/CMakeFiles/mirplatformservermesax11.dir/build.make:105: recipe for target 'lib/server-modules/server-mesa-x11.so.13' failed
make[2]: *** [lib/server-modules/server-mesa-x11.so.13] Error 1
CMakeFiles/Makefile2:1823: recipe for target 'src/platforms/mesa/server/x11/CMakeFiles/mirplatformservermesax11.dir/all' failed

Once again "make refresh-wayland-wrapper" fixes that, but something isn't quite right.

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

> I just merged to trunk and "built" and got...
>
> ../libmirsharedmesaservercommon-static.a(buffer_allocator.cpp.o): In function
> `WaylandBuffer':
> /home/alan/display_server/mir/src/platforms/mesa/server/buffer_allocator.cpp:3
> 46: undefined reference to `wl_resource_get_destroy_listener'
> /home/alan/display_server/mir/src/platforms/mesa/server/buffer_allocator.cpp:3
> 65: undefined reference to `wl_resource_add_destroy_listener'
> ../libmirsharedmesaservercommon-static.a(buffer_allocator.cpp.o): In function
> `~WaylandBuffer':
> /home/alan/display_server/mir/src/platforms/mesa/server/buffer_allocator.cpp:4
> 05: undefined reference to `wl_resource_queue_event'
> /home/alan/display_server/mir/src/platforms/mesa/server/buffer_allocator.cpp:4
> 06: undefined reference to `wl_resource_get_destroy_listener'
> clang: error: linker command failed with exit code 1 (use -v to see
> invocation)
> src/platforms/mesa/server/x11/CMakeFiles/mirplatformservermesax11.dir/build.ma
> ke:105: recipe for target 'lib/server-modules/server-mesa-x11.so.13' failed
> make[2]: *** [lib/server-modules/server-mesa-x11.so.13] Error 1
> CMakeFiles/Makefile2:1823: recipe for target
> 'src/platforms/mesa/server/x11/CMakeFiles/mirplatformservermesax11.dir/all'
> failed
>
> Once again "make refresh-wayland-wrapper" fixes that, but something isn't
> quite right.

Loads of tests fail, and (possibly related):

$ cmake-build-debug/bin/miral-shell
...
wl_global_create: implemented version for 'wl_seat' higher than interface version (6 > 5)
ERROR: /home/alan/display_server/mir/src/server/frontend/wayland/wayland_connector.cpp(1026): Throw in function mir::frontend::WlSeat::WlSeat(wl_display *, const std::shared_ptr<mir::Executor> &)
Dynamic exception type: boost::exception_detail::clone_impl<boost::exception_detail::error_info_injector<std::runtime_error> >
std::exception::what: Failed to export wl_seat interface

Segmentation fault (core dumped)

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

> I just merged to trunk and "built" and got...
>
> ../libmirsharedmesaservercommon-static.a(buffer_allocator.cpp.o): In function
> `WaylandBuffer':
> /home/alan/display_server/mir/src/platforms/mesa/server/buffer_allocator.cpp:3
> 46: undefined reference to `wl_resource_get_destroy_listener'
> /home/alan/display_server/mir/src/platforms/mesa/server/buffer_allocator.cpp:3
> 65: undefined reference to `wl_resource_add_destroy_listener'
> ../libmirsharedmesaservercommon-static.a(buffer_allocator.cpp.o): In function
> `~WaylandBuffer':
> /home/alan/display_server/mir/src/platforms/mesa/server/buffer_allocator.cpp:4
> 05: undefined reference to `wl_resource_queue_event'
> /home/alan/display_server/mir/src/platforms/mesa/server/buffer_allocator.cpp:4
> 06: undefined reference to `wl_resource_get_destroy_listener'
> clang: error: linker command failed with exit code 1 (use -v to see
> invocation)
> src/platforms/mesa/server/x11/CMakeFiles/mirplatformservermesax11.dir/build.ma
> ke:105: recipe for target 'lib/server-modules/server-mesa-x11.so.13' failed
> make[2]: *** [lib/server-modules/server-mesa-x11.so.13] Error 1
> CMakeFiles/Makefile2:1823: recipe for target
> 'src/platforms/mesa/server/x11/CMakeFiles/mirplatformservermesax11.dir/all'
> failed
>
> Once again "make refresh-wayland-wrapper" fixes that, but something isn't
> quite right.

Loads of tests fail, and (possibly related):

$ cmake-build-debug/bin/miral-shell
...
wl_global_create: implemented version for 'wl_seat' higher than interface version (6 > 5)
ERROR: /home/alan/display_server/mir/src/server/frontend/wayland/wayland_connector.cpp(1026): Throw in function mir::frontend::WlSeat::WlSeat(wl_display *, const std::shared_ptr<mir::Executor> &)
Dynamic exception type: boost::exception_detail::clone_impl<boost::exception_detail::error_info_injector<std::runtime_error> >
std::exception::what: Failed to export wl_seat interface

Segmentation fault (core dumped)

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

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

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

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

PASSED: Continuous integration, rev:4210
https://mir-jenkins.ubuntu.com/job/mir-ci/3631/
Executed test runs:
    SUCCESS: https://mir-jenkins.ubuntu.com/job/build-mir/4967
    SUCCESS: https://mir-jenkins.ubuntu.com/job/build-0-fetch/5195
    SUCCESS: https://mir-jenkins.ubuntu.com/job/build-1-sourcepkg/release=artful/5183
    SUCCESS: https://mir-jenkins.ubuntu.com/job/build-1-sourcepkg/release=xenial/5183
    SUCCESS: https://mir-jenkins.ubuntu.com/job/build-1-sourcepkg/release=zesty/5183
    SUCCESS: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-mir/arch=amd64,compiler=clang,platform=mesa,release=artful/5006
        deb: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-mir/arch=amd64,compiler=clang,platform=mesa,release=artful/5006/artifact/output/*zip*/output.zip
    SUCCESS: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-mir/arch=amd64,compiler=clang,platform=mesa,release=zesty/5006
        deb: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-mir/arch=amd64,compiler=clang,platform=mesa,release=zesty/5006/artifact/output/*zip*/output.zip
    SUCCESS: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-mir/arch=amd64,compiler=gcc,platform=mesa,release=artful/5006
        deb: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-mir/arch=amd64,compiler=gcc,platform=mesa,release=artful/5006/artifact/output/*zip*/output.zip
    SUCCESS: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-mir/arch=amd64,compiler=gcc,platform=mesa,release=xenial/5006
        deb: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-mir/arch=amd64,compiler=gcc,platform=mesa,release=xenial/5006/artifact/output/*zip*/output.zip
    SUCCESS: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-mir/arch=amd64,compiler=gcc,platform=mesa,release=zesty/5006
        deb: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-mir/arch=amd64,compiler=gcc,platform=mesa,release=zesty/5006/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/5006
        deb: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-mir/arch=cross-armhf,compiler=gcc,platform=mesa,release=artful/5006/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/5006
        deb: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-mir/arch=cross-armhf,compiler=gcc,platform=mesa,release=zesty/5006/artifact/output/*zip*/output.zip
    SUCCESS: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-mir/arch=i386,compiler=gcc,platform=mesa,release=xenial/5006
        deb: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-mir/arch=i386,compiler=gcc,platform=mesa,release=xenial/5006/artifact/output/*zip*/output.zip

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

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

Close, just a few nits...

=== modified file 'tests/mir_test_framework/CMakeLists.txt'
--- tests/mir_test_framework/CMakeLists.txt 2017-08-30 11:59:08 +0000
+++ tests/mir_test_framework/CMakeLists.txt 2017-09-06 06:18:29 +0000
@@ -85,6 +85,7 @@

 target_link_libraries(mir-test-framework-static

+ mirserver

Not needed

~~~~~~~~~~~~~~~~~~~~~~~~~~~~

+/*
+ * Copyright © 2015 Canonical Ltd.

Probably want 2017 too.

+ *
+ * This program is free software: you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 3,

2 or 3

+ * as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * Authored by: Christopher James Halse Rogers <email address hidden>
+ */
+
+#include "mir/default_server_configuration.h"
+#include "wayland_connector.h"
+
+#include "../../scene/mediating_display_changer.h"

#include "mir/frontend/display_changer.h"

+#include "mir/graphics/platform.h"
+
+namespace mf = mir::frontend;
+
+std::shared_ptr<mf::Connector>
+ mir::DefaultServerConfiguration::the_wayland_connector()
+{
+ return wayland_connector(
+ [this]() -> std::shared_ptr<mf::Connector>
+ {
+ return std::make_shared<mf::WaylandConnector>(
+ the_frontend_shell(),
+ *the_mediating_display_changer(),

                *the_frontend_display_changer(),

(Do we really want to avoid sharing ownership? Where's the lifetime guarantee?)

+ the_buffer_allocator());
+ });
+}

~~~~~~~~~~~~~~~~~~~~~~~~~~~~

=== added file 'src/server/frontend/wayland/core_generated_interfaces.h'

Could benefit from a header comment to say it is generated.

The generated code uses a lot of reinterpret_cast<> where static_cast<> is all that is needed to downcast from void*.

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

I guess a server crash might be enough to block landing, but this is beyond what we decided was minimal viable feature...

$ bin/miral-app --x11-displays=1200x900 --keymap=gb

Then in the miral-app terminal:

$ QT_QPA_PLATFORM=wayland kate& kate&

Use the mouse to switch to the wayland instance of kate, pull down the file menu then click on the editor panel.

Expect: menu disappears
Actual: server crashes:
terminate called after throwing an instance of 'boost::exception_detail::clone_impl<boost::exception_detail::error_info_injector<std::runtime_error> >'
  what(): Setting null buffer is unimplemented

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

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

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

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

FAILED: Continuous integration, rev:4216
https://mir-jenkins.ubuntu.com/job/mir-ci/3638/
Executed test runs:
    FAILURE: https://mir-jenkins.ubuntu.com/job/build-mir/4976/console
    FAILURE: https://mir-jenkins.ubuntu.com/job/build-0-fetch/5204/console
    FAILURE: https://mir-jenkins.ubuntu.com/job/build-1-sourcepkg/release=artful/5192/console
    FAILURE: https://mir-jenkins.ubuntu.com/job/build-1-sourcepkg/release=xenial/5192/console
    FAILURE: https://mir-jenkins.ubuntu.com/job/build-1-sourcepkg/release=zesty/5192/console
    FAILURE: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-mir/arch=amd64,compiler=clang,platform=mesa,release=artful/5015/console
    FAILURE: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-mir/arch=amd64,compiler=clang,platform=mesa,release=zesty/5015/console
    FAILURE: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-mir/arch=amd64,compiler=gcc,platform=mesa,release=artful/5015/console
    FAILURE: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-mir/arch=amd64,compiler=gcc,platform=mesa,release=xenial/5015/console
    FAILURE: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-mir/arch=amd64,compiler=gcc,platform=mesa,release=zesty/5015/console
    FAILURE: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-mir/arch=cross-armhf,compiler=gcc,platform=mesa,release=artful/5015/console
    FAILURE: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-mir/arch=cross-armhf,compiler=gcc,platform=mesa,release=zesty/5015/console
    FAILURE: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-mir/arch=i386,compiler=gcc,platform=mesa,release=xenial/5015/console

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

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

PASSED: Continuous integration, rev:4217
https://mir-jenkins.ubuntu.com/job/mir-ci/3639/
Executed test runs:
    SUCCESS: https://mir-jenkins.ubuntu.com/job/build-mir/4977
    SUCCESS: https://mir-jenkins.ubuntu.com/job/build-0-fetch/5205
    SUCCESS: https://mir-jenkins.ubuntu.com/job/build-1-sourcepkg/release=artful/5193
    SUCCESS: https://mir-jenkins.ubuntu.com/job/build-1-sourcepkg/release=xenial/5193
    SUCCESS: https://mir-jenkins.ubuntu.com/job/build-1-sourcepkg/release=zesty/5193
    SUCCESS: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-mir/arch=amd64,compiler=clang,platform=mesa,release=artful/5016
        deb: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-mir/arch=amd64,compiler=clang,platform=mesa,release=artful/5016/artifact/output/*zip*/output.zip
    SUCCESS: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-mir/arch=amd64,compiler=clang,platform=mesa,release=zesty/5016
        deb: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-mir/arch=amd64,compiler=clang,platform=mesa,release=zesty/5016/artifact/output/*zip*/output.zip
    SUCCESS: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-mir/arch=amd64,compiler=gcc,platform=mesa,release=artful/5016
        deb: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-mir/arch=amd64,compiler=gcc,platform=mesa,release=artful/5016/artifact/output/*zip*/output.zip
    SUCCESS: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-mir/arch=amd64,compiler=gcc,platform=mesa,release=xenial/5016
        deb: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-mir/arch=amd64,compiler=gcc,platform=mesa,release=xenial/5016/artifact/output/*zip*/output.zip
    SUCCESS: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-mir/arch=amd64,compiler=gcc,platform=mesa,release=zesty/5016
        deb: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-mir/arch=amd64,compiler=gcc,platform=mesa,release=zesty/5016/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/5016
        deb: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-mir/arch=cross-armhf,compiler=gcc,platform=mesa,release=artful/5016/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/5016
        deb: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-mir/arch=cross-armhf,compiler=gcc,platform=mesa,release=zesty/5016/artifact/output/*zip*/output.zip
    SUCCESS: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-mir/arch=i386,compiler=gcc,platform=mesa,release=xenial/5016
        deb: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-mir/arch=i386,compiler=gcc,platform=mesa,release=xenial/5016/artifact/output/*zip*/output.zip

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

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

> I guess a server crash might be enough to block landing, but this is beyond
> what we decided was minimal viable feature...
>
> $ bin/miral-app --x11-displays=1200x900 --keymap=gb
>
> Then in the miral-app terminal:
>
> $ QT_QPA_PLATFORM=wayland kate& kate&
>
> Use the mouse to switch to the wayland instance of kate, pull down the file
> menu then click on the editor panel.
>
> Expect: menu disappears
> Actual: server crashes:
> terminate called after throwing an instance of 'boost::exception_detail::clone
> _impl<boost::exception_detail::error_info_injector<std::runtime_error> >'
> what(): Setting null buffer is unimplemented

Was -r 4216 meant to stop this crash? (It is still happening)

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

+#include "mir/default_server_configuration.h"
+#include "wayland_connector.h"
+
+#include "../../scene/mediating_display_changer.h"

#include "mir/frontend/display_changer.h"

+#include "mir/graphics/platform.h"
+
+namespace mf = mir::frontend;
+
+std::shared_ptr<mf::Connector>
+ mir::DefaultServerConfiguration::the_wayland_connector()
+{
+ return wayland_connector(
+ [this]() -> std::shared_ptr<mf::Connector>
+ {
+ return std::make_shared<mf::WaylandConnector>(
+ the_frontend_shell(),
+ *the_mediating_display_changer(),

                *the_frontend_display_changer(),

(Do we really want to avoid sharing ownership? Where's the lifetime guarantee?)

+ the_buffer_allocator());
+ });
+}

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

> +#include "mir/default_server_configuration.h"
> +#include "wayland_connector.h"
> +
> +#include "../../scene/mediating_display_changer.h"
>
> #include "mir/frontend/display_changer.h"
>
> +#include "mir/graphics/platform.h"
> +
> +namespace mf = mir::frontend;
> +
> +std::shared_ptr<mf::Connector>
> + mir::DefaultServerConfiguration::the_wayland_connector()
> +{
> + return wayland_connector(
> + [this]() -> std::shared_ptr<mf::Connector>
> + {
> + return std::make_shared<mf::WaylandConnector>(
> + the_frontend_shell(),
> + *the_mediating_display_changer(),
>
> *the_frontend_display_changer(),
>
> (Do we really want to avoid sharing ownership? Where's the lifetime
> guarantee?)
>
> + the_buffer_allocator());
> + });
> +}

nm, let's land this and fixup afterwards.

review: Approve
Revision history for this message
Chris Halse Rogers (raof) wrote :

On Thu, Sep 7, 2017 at 9:08 PM, Alan Griffiths <email address hidden>
wrote:
>> I guess a server crash might be enough to block landing, but this
>> is beyond
>> what we decided was minimal viable feature...
>>
>> $ bin/miral-app --x11-displays=1200x900 --keymap=gb
>>
>> Then in the miral-app terminal:
>>
>> $ QT_QPA_PLATFORM=wayland kate& kate&
>>
>> Use the mouse to switch to the wayland instance of kate, pull down
>> the file
>> menu then click on the editor panel.
>>
>> Expect: menu disappears
>> Actual: server crashes:
>> terminate called after throwing an instance of
>> 'boost::exception_detail::clone
>>
>> _impl<boost::exception_detail::error_info_injector<std::runtime_error>
>> >'
>> what(): Setting null buffer is unimplemented
>
> Was -r 4216 meant to stop this crash? (It is still happening)

Huh. That particular crash *definitely* cannot happen, and I could
happily switch between Kate instances, open menus, and such without
anything crashing.

I wonder what's up.

Revision history for this message
Chris Halse Rogers (raof) wrote :

On Thu, Sep 7, 2017 at 9:18 PM, Alan Griffiths <email address hidden>
wrote:
> +#include "mir/default_server_configuration.h"
> +#include "wayland_connector.h"
> +
> +#include "../../scene/mediating_display_changer.h"
>
> #include "mir/frontend/display_changer.h"
>
> +#include "mir/graphics/platform.h"
> +
> +namespace mf = mir::frontend;
> +
> +std::shared_ptr<mf::Connector>
> + mir::DefaultServerConfiguration::the_wayland_connector()
> +{
> + return wayland_connector(
> + [this]() -> std::shared_ptr<mf::Connector>
> + {
> + return std::make_shared<mf::WaylandConnector>(
> + the_frontend_shell(),
> + *the_mediating_display_changer(),
>
> *the_frontend_display_changer(),
>
> (Do we really want to avoid sharing ownership? Where's the lifetime
> guarantee?)

The lifetime guarantee is that WaylandConnector does not store any
reference to the mf::DisplayChanger; it's purely used at initialisation
time.

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
=== modified file 'CMakeLists.txt'
--- CMakeLists.txt 2017-08-25 11:05:07 +0000
+++ CMakeLists.txt 2017-09-07 05:58:57 +0000
@@ -74,6 +74,16 @@
74 set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wno-mismatched-tags")74 set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wno-mismatched-tags")
75endif()75endif()
7676
77# GCC 7.1 fixed a bug in the ARM ABI, which results in some std::vector methods
78# (among others) generating this warning.
79#
80# There's nothing we can do about it; everything just needs to be rebuilt with
81# GCC 7.1.
82check_cxx_compiler_flag(-Wpsabi HAS_W_PSABI)
83if(HAS_W_PSABI)
84 set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wno-psabi")
85endif()
86
77option(MIR_USE_LD_GOLD "Enables the \"gold\" linker." OFF)87option(MIR_USE_LD_GOLD "Enables the \"gold\" linker." OFF)
78if(MIR_USE_LD_GOLD)88if(MIR_USE_LD_GOLD)
79 set (CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} -fuse-ld=gold")89 set (CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} -fuse-ld=gold")
@@ -206,6 +216,8 @@
206216
207pkg_check_modules(UDEV REQUIRED libudev)217pkg_check_modules(UDEV REQUIRED libudev)
208pkg_check_modules(GLIB REQUIRED glib-2.0)218pkg_check_modules(GLIB REQUIRED glib-2.0)
219pkg_check_modules(WAYLAND_SERVER REQUIRED wayland-server)
220pkg_check_modules(WAYLAND_CLIENT REQUIRED wayland-client)
209221
210include_directories (SYSTEM ${GLESv2_INCLUDE_DIRS})222include_directories (SYSTEM ${GLESv2_INCLUDE_DIRS})
211include_directories (SYSTEM ${EGL_INCLUDE_DIRS})223include_directories (SYSTEM ${EGL_INCLUDE_DIRS})
212224
=== modified file 'debian/control'
--- debian/control 2017-08-23 11:56:37 +0000
+++ debian/control 2017-09-07 05:58:57 +0000
@@ -31,6 +31,7 @@
31 libudev-dev,31 libudev-dev,
32 libgtest-dev,32 libgtest-dev,
33 google-mock (>= 1.6.0+svn437),33 google-mock (>= 1.6.0+svn437),
34 libxml++2.6-dev,
34# only enable valgrind once it's been tested to work on each architecture:35# only enable valgrind once it's been tested to work on each architecture:
35 valgrind [amd64 i386 armhf arm64],36 valgrind [amd64 i386 armhf arm64],
36 libglib2.0-dev,37 libglib2.0-dev,
3738
=== modified file 'examples/CMakeLists.txt'
--- examples/CMakeLists.txt 2017-08-30 12:38:21 +0000
+++ examples/CMakeLists.txt 2017-09-07 05:58:57 +0000
@@ -274,6 +274,15 @@
274 mirclient274 mirclient
275)275)
276276
277include_directories(${MIR_GENERATED_INCLUDE_DIRECTORIES})
278
279mir_add_wrapped_executable(mir_demo_client_wayland wayland_client.c)
280target_link_libraries(mir_demo_client_wayland
281
282 ${WAYLAND_CLIENT_LIBRARIES}
283)
284
277mir_add_wrapped_executable(mir_demo_client_screencast screencast.cpp)285mir_add_wrapped_executable(mir_demo_client_screencast screencast.cpp)
278286
279target_link_libraries(mir_demo_client_screencast mirclient)287target_link_libraries(mir_demo_client_screencast mirclient)
288
280289
=== added file 'examples/wayland_client.c'
--- examples/wayland_client.c 1970-01-01 00:00:00 +0000
+++ examples/wayland_client.c 2017-09-07 05:58:57 +0000
@@ -0,0 +1,391 @@
1/*
2 * Copyright © 2015 Canonical Ltd.
3 *
4 * This program is free software: you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License version 2 or 3 as
6 * published by the Free Software Foundation.
7 *
8 * This program is distributed in the hope that it will be useful,
9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 * GNU General Public License for more details.
12 *
13 * You should have received a copy of the GNU General Public License
14 * along with this program. If not, see <http://www.gnu.org/licenses/>.
15 *
16 * Author: Christopher James Halse Rogers <christopher.halse.rogers@canonical.com>
17 */
18
19#ifndef _GNU_SOURCE
20#define _GNU_SOURCE
21#endif
22
23#include <stdlib.h>
24#include <string.h>
25#include <sys/mman.h>
26#include <sys/types.h>
27#include <sys/stat.h>
28#include <fcntl.h>
29#include <unistd.h>
30#include <stdio.h>
31#include <stdbool.h>
32
33#include <wayland-client.h>
34#include <wayland-client-core.h>
35
36struct globals
37{
38 struct wl_compositor* compositor;
39 struct wl_shm* shm;
40 struct wl_seat* seat;
41 struct wl_output* output;
42 struct wl_shell* shell;
43};
44
45static void new_global(
46 void* data,
47 struct wl_registry* registry,
48 uint32_t id,
49 char const* interface,
50 uint32_t version)
51{
52 (void)version;
53 struct globals* globals = data;
54
55 if (strcmp(interface, "wl_compositor") == 0)
56 {
57 globals->compositor = wl_registry_bind(registry, id, &wl_compositor_interface, 3);
58 }
59 else if (strcmp(interface, "wl_shm") == 0)
60 {
61 globals->shm = wl_registry_bind(registry, id, &wl_shm_interface, 1);
62 // Normally we'd add a listener to pick up the supported formats here
63 // As luck would have it, I know that argb8888 is the only format we support :)
64 }
65 else if (strcmp(interface, "wl_seat") == 0)
66 {
67 globals->seat = wl_registry_bind(registry, id, &wl_seat_interface, 4);
68 }
69 else if (strcmp(interface, "wl_output") == 0)
70 {
71 globals->output = wl_registry_bind(registry, id, &wl_output_interface, 2);
72 }
73 else if (strcmp(interface, "wl_shell") == 0)
74 {
75 globals->shell = wl_registry_bind(registry, id, &wl_shell_interface, 1);
76 }
77}
78
79static void global_remove(
80 void* data,
81 struct wl_registry* registry,
82 uint32_t name)
83{
84 (void)data;
85 (void)registry;
86 (void)name;
87}
88
89static struct wl_registry_listener const registry_listener = {
90 .global = new_global,
91 .global_remove = global_remove
92};
93
94static struct wl_shm_pool*
95make_shm_pool(struct wl_shm* shm, int size, void **data)
96{
97 struct wl_shm_pool *pool;
98 int fd;
99
100 fd = open("/dev/shm", O_TMPFILE | O_RDWR | O_EXCL, S_IRWXU);
101 if (fd < 0) {
102 return NULL;
103 }
104
105 posix_fallocate(fd, 0, size);
106
107 *data = mmap(NULL, size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
108 if (*data == MAP_FAILED) {
109 close(fd);
110 return NULL;
111 }
112
113 pool = wl_shm_create_pool(shm, fd, size);
114
115 close(fd);
116
117 return pool;
118}
119
120struct draw_context
121{
122 void* content_area;
123 struct wl_display* display;
124 struct wl_surface* surface;
125 struct wl_callback* new_frame_signal;
126 struct Buffers
127 {
128 struct wl_buffer* buffer;
129 bool available;
130 } buffers[4];
131 bool waiting_for_buffer;
132};
133
134static struct wl_buffer* find_free_buffer(struct draw_context* ctx)
135{
136 for (int i = 0; i < 4 ; ++i)
137 {
138 if (ctx->buffers[i].available)
139 {
140 ctx->buffers[i].available = false;
141 return ctx->buffers[i].buffer;
142 }
143 }
144 return NULL;
145}
146
147static void draw_new_stuff(void* data, struct wl_callback* callback, uint32_t time);
148
149static const struct wl_callback_listener frame_listener =
150{
151 .done = &draw_new_stuff
152};
153
154static void update_free_buffers(void* data, struct wl_buffer* buffer)
155{
156 struct draw_context* ctx = data;
157 for (int i = 0; i < 4 ; ++i)
158 {
159 if (ctx->buffers[i].buffer == buffer)
160 {
161 ctx->buffers[i].available = true;
162 }
163 }
164
165 if (ctx->waiting_for_buffer)
166 {
167 struct wl_callback* fake_frame = wl_display_sync(ctx->display);
168 wl_callback_add_listener(fake_frame, &frame_listener, ctx);
169 }
170
171 ctx->waiting_for_buffer = false;
172}
173
174static void draw_new_stuff(
175 void* data,
176 struct wl_callback* callback,
177 uint32_t time)
178{
179 (void)time;
180 static unsigned char current_value = 128;
181 struct draw_context* ctx = data;
182
183 wl_callback_destroy(callback);
184
185 struct wl_buffer* buffer = find_free_buffer(ctx);
186 if (!buffer)
187 {
188 ctx->waiting_for_buffer = false;
189 return;
190 }
191
192 memset(ctx->content_area, current_value, 400 * 400 * 4);
193 ++current_value;
194
195 ctx->new_frame_signal = wl_surface_frame(ctx->surface);
196 wl_callback_add_listener(ctx->new_frame_signal, &frame_listener, ctx);
197 wl_surface_attach(ctx->surface, buffer, 0, 0);
198 wl_surface_commit(ctx->surface);
199}
200
201static struct wl_buffer_listener const buffer_listener = {
202 .release = update_free_buffers
203};
204
205void mouse_enter(
206 void *data,
207 struct wl_pointer *wl_pointer,
208 uint32_t serial,
209 struct wl_surface *surface,
210 wl_fixed_t surface_x,
211 wl_fixed_t surface_y)
212{
213 (void)data;
214 (void)wl_pointer;
215 (void)serial;
216 (void)surface;
217 printf("Received mouse_enter event: (%f, %f)\n",
218 wl_fixed_to_double(surface_x),
219 wl_fixed_to_double(surface_y));
220}
221
222void mouse_leave(
223 void *data,
224 struct wl_pointer *wl_pointer,
225 uint32_t serial,
226 struct wl_surface *surface)
227{
228 (void)data;
229 (void)wl_pointer;
230 (void)serial;
231 (void)surface;
232 printf("Received mouse_exit event\n");
233}
234
235void mouse_motion(
236 void *data,
237 struct wl_pointer *wl_pointer,
238 uint32_t time,
239 wl_fixed_t surface_x,
240 wl_fixed_t surface_y)
241{
242 (void)data;
243 (void)wl_pointer;
244
245 printf("Received motion event: (%f, %f) @ %i\n",
246 wl_fixed_to_double(surface_x),
247 wl_fixed_to_double(surface_y),
248 time);
249}
250
251void mouse_button(
252 void *data,
253 struct wl_pointer *wl_pointer,
254 uint32_t serial,
255 uint32_t time,
256 uint32_t button,
257 uint32_t state)
258{
259 (void)serial;
260 (void)data;
261 (void)wl_pointer;
262
263 printf("Received button event: Button %i, state %i @ %i\n",
264 button,
265 state,
266 time);
267}
268
269void mouse_axis(
270 void *data,
271 struct wl_pointer *wl_pointer,
272 uint32_t time,
273 uint32_t axis,
274 wl_fixed_t value)
275{
276 (void)data;
277 (void)wl_pointer;
278
279 printf("Received axis event: axis %i, value %f @ %i\n",
280 axis,
281 wl_fixed_to_double(value),
282 time);
283}
284
285
286
287static struct wl_pointer_listener const pointer_listener = {
288 .enter = &mouse_enter,
289 .leave = &mouse_leave,
290 .motion = &mouse_motion,
291 .button = &mouse_button,
292 .axis = &mouse_axis
293};
294
295static void output_geometry(void *data,
296 struct wl_output *wl_output,
297 int32_t x,
298 int32_t y,
299 int32_t physical_width,
300 int32_t physical_height,
301 int32_t subpixel,
302 const char *make,
303 const char *model,
304 int32_t transform)
305{
306 (void)data;
307 (void)wl_output;
308 (void)subpixel;
309 (void)make;
310 (void)model;
311 (void)transform;
312 printf("Got geometry: (%imm × %imm)@(%i, %i)\n", physical_width, physical_height, x, y);
313}
314
315static void output_mode(void *data,
316 struct wl_output *wl_output,
317 uint32_t flags,
318 int32_t width,
319 int32_t height,
320 int32_t refresh)
321{
322 (void)data;
323 (void)wl_output;
324 printf("Got mode: %i×%i@%i (flags: %i)\n", width, height, refresh, flags);
325}
326
327static void output_done(void* data, struct wl_output* wl_output)
328{
329 (void)data;
330 (void)wl_output;
331 printf("Output events done\n");
332}
333
334static void output_scale(void* data, struct wl_output* wl_output, int32_t factor)
335{
336 (void)data;
337 (void)wl_output;
338 printf("Output scale: %i\n", factor);
339}
340
341static struct wl_output_listener const output_listener = {
342 .geometry = &output_geometry,
343 .mode = &output_mode,
344 .done = &output_done,
345 .scale = &output_scale,
346};
347
348int main()
349{
350 struct wl_display* display = wl_display_connect(NULL);
351 struct globals* globals;
352 globals = calloc(sizeof *globals, 1);
353
354 struct wl_registry* registry = wl_display_get_registry(display);
355
356 wl_registry_add_listener(registry, &registry_listener, globals);
357
358 wl_display_roundtrip(display);
359
360 struct wl_pointer* pointer = wl_seat_get_pointer(globals->seat);
361 wl_pointer_add_listener(pointer, &pointer_listener, NULL);
362
363 void* pool_data = NULL;
364 struct wl_shm_pool* shm_pool = make_shm_pool(globals->shm, 400 * 400 * 4, &pool_data);
365
366 struct draw_context* ctx = calloc(sizeof *ctx, 1);
367
368 for (int i = 0; i < 4; ++i)
369 {
370 ctx->buffers[i].buffer = wl_shm_pool_create_buffer(shm_pool, 0, 400, 400, 400, WL_SHM_FORMAT_ARGB8888);
371 ctx->buffers[i].available = true;
372 wl_buffer_add_listener(ctx->buffers[i].buffer, &buffer_listener, ctx);
373 }
374
375 ctx->display = display;
376 ctx->surface = wl_compositor_create_surface(globals->compositor);
377 ctx->content_area = pool_data;
378
379 struct wl_shell_surface* window = wl_shell_get_shell_surface(globals->shell, ctx->surface);
380 wl_shell_surface_set_toplevel(window);
381
382 struct wl_callback* first_frame = wl_display_sync(display);
383 wl_callback_add_listener(first_frame, &frame_listener, ctx);
384
385 wl_output_add_listener(globals->output, &output_listener, NULL);
386
387 while (wl_display_dispatch(display))
388 ;
389
390 return 0;
391}
0392
=== modified file 'include/platform/mir/graphics/egl_extensions.h'
--- include/platform/mir/graphics/egl_extensions.h 2017-07-28 17:00:43 +0000
+++ include/platform/mir/graphics/egl_extensions.h 2017-09-07 05:58:57 +0000
@@ -19,6 +19,8 @@
19#ifndef MIR_GRAPHICS_EGL_EXTENSIONS_H_19#ifndef MIR_GRAPHICS_EGL_EXTENSIONS_H_
20#define MIR_GRAPHICS_EGL_EXTENSIONS_H_20#define MIR_GRAPHICS_EGL_EXTENSIONS_H_
2121
22#include <experimental/optional>
23
22#define EGL_EGLEXT_PROTOTYPES24#define EGL_EGLEXT_PROTOTYPES
23#include <EGL/egl.h>25#include <EGL/egl.h>
24#include <EGL/eglext.h>26#include <EGL/eglext.h>
@@ -35,6 +37,15 @@
35 PFNEGLCREATEIMAGEKHRPROC const eglCreateImageKHR;37 PFNEGLCREATEIMAGEKHRPROC const eglCreateImageKHR;
36 PFNEGLDESTROYIMAGEKHRPROC const eglDestroyImageKHR;38 PFNEGLDESTROYIMAGEKHRPROC const eglDestroyImageKHR;
37 PFNGLEGLIMAGETARGETTEXTURE2DOESPROC const glEGLImageTargetTexture2DOES;39 PFNGLEGLIMAGETARGETTEXTURE2DOESPROC const glEGLImageTargetTexture2DOES;
40
41 struct WaylandExtensions
42 {
43 WaylandExtensions();
44
45 PFNEGLBINDWAYLANDDISPLAYWL const eglBindWaylandDisplayWL;
46 PFNEGLQUERYWAYLANDBUFFERWL const eglQueryWaylandBufferWL;
47 };
48 std::experimental::optional<WaylandExtensions> const wayland;
38};49};
3950
40}51}
4152
=== added file 'include/platform/mir/graphics/wayland_allocator.h'
--- include/platform/mir/graphics/wayland_allocator.h 1970-01-01 00:00:00 +0000
+++ include/platform/mir/graphics/wayland_allocator.h 2017-09-07 05:58:57 +0000
@@ -0,0 +1,45 @@
1/*
2 * Copyright © 2017 Canonical Ltd.
3 *
4 * This program is free software: you can redistribute it and/or modify it
5 * under the terms of the GNU Lesser 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 Lesser General Public License for more details.
12 *
13 * You should have received a copy of the GNU Lesser General Public License
14 * along with this program. If not, see <http://www.gnu.org/licenses/>.
15 *
16 * Authored by:
17 * Christopher James Halse Rogers <christopher.halse.rogers@canonical.com>
18 */
19
20#ifndef MIR_PLATFORM_GRAPHICS_WAYLAND_ALLOCATOR_H_
21#define MIR_PLATFORM_GRAPHICS_WAYLAND_ALLOCATOR_H_
22
23#include <vector>
24#include <memory>
25
26#include <wayland-server-core.h>
27
28namespace mir
29{
30namespace graphics
31{
32class Buffer;
33
34class WaylandAllocator
35{
36public:
37 virtual ~WaylandAllocator() = default;
38
39 virtual void bind_display(wl_display* display) = 0;
40 virtual std::unique_ptr<Buffer> buffer_from_resource (wl_resource* buffer, std::function<void ()>&& on_consumed) = 0;
41};
42}
43}
44
45#endif //MIR_PLATFORM_GRAPHICS_WAYLAND_ALLOCATOR_H_
046
=== modified file 'include/server/mir/frontend/buffer_stream.h'
--- include/server/mir/frontend/buffer_stream.h 2017-08-16 04:04:53 +0000
+++ include/server/mir/frontend/buffer_stream.h 2017-09-07 05:58:57 +0000
@@ -45,6 +45,7 @@
45 virtual ~BufferStream() = default;45 virtual ~BufferStream() = default;
46 46
47 virtual void submit_buffer(std::shared_ptr<graphics::Buffer> const& buffer) = 0;47 virtual void submit_buffer(std::shared_ptr<graphics::Buffer> const& buffer) = 0;
48 virtual void resize(geometry::Size const& size) = 0;
4849
49 virtual void add_observer(std::shared_ptr<scene::SurfaceObserver> const& observer) = 0;50 virtual void add_observer(std::shared_ptr<scene::SurfaceObserver> const& observer) = 0;
50 virtual void remove_observer(std::weak_ptr<scene::SurfaceObserver> const& observer) = 0;51 virtual void remove_observer(std::weak_ptr<scene::SurfaceObserver> const& observer) = 0;
5152
=== modified file 'include/test/mir/test/doubles/mock_egl.h'
--- include/test/mir/test/doubles/mock_egl.h 2017-07-28 17:00:43 +0000
+++ include/test/mir/test/doubles/mock_egl.h 2017-09-07 05:58:57 +0000
@@ -158,6 +158,13 @@
158 int64_t*, int64_t*,158 int64_t*, int64_t*,
159 int64_t*));159 int64_t*));
160160
161 MOCK_METHOD2(eglBindWaylandDisplayWL,
162 EGLBoolean(EGLDisplay, struct wl_display*));
163 MOCK_METHOD2(eglUnbindWaylandDisplayWL,
164 EGLBoolean(EGLDisplay, struct wl_display*));
165 MOCK_METHOD4(eglQueryWaylandBufferWL,
166 EGLBoolean(EGLDisplay, struct wl_resource*, EGLint, EGLint*));
167
161 EGLDisplay const fake_egl_display;168 EGLDisplay const fake_egl_display;
162 EGLConfig const* const fake_configs;169 EGLConfig const* const fake_configs;
163 EGLint const fake_configs_num;170 EGLint const fake_configs_num;
164171
=== modified file 'src/CMakeLists.txt'
--- src/CMakeLists.txt 2017-08-21 14:18:55 +0000
+++ src/CMakeLists.txt 2017-09-07 05:58:57 +0000
@@ -21,6 +21,7 @@
21add_subdirectory(capnproto/)21add_subdirectory(capnproto/)
22add_subdirectory(common/)22add_subdirectory(common/)
23add_subdirectory(protobuf/)23add_subdirectory(protobuf/)
24add_subdirectory(protocol/)
24include_directories(${MIR_GENERATED_INCLUDE_DIRECTORIES})25include_directories(${MIR_GENERATED_INCLUDE_DIRECTORIES})
2526
26add_subdirectory(platform/)27add_subdirectory(platform/)
2728
=== modified file 'src/include/server/mir/compositor/buffer_stream.h'
--- src/include/server/mir/compositor/buffer_stream.h 2017-07-28 17:00:43 +0000
+++ src/include/server/mir/compositor/buffer_stream.h 2017-09-07 05:58:57 +0000
@@ -45,7 +45,6 @@
45 virtual std::shared_ptr<graphics::Buffer>45 virtual std::shared_ptr<graphics::Buffer>
46 lock_compositor_buffer(void const* user_id) = 0;46 lock_compositor_buffer(void const* user_id) = 0;
47 virtual geometry::Size stream_size() = 0;47 virtual geometry::Size stream_size() = 0;
48 virtual void resize(geometry::Size const& size) = 0;
49 virtual int buffers_ready_for_compositor(void const* user_id) const = 0;48 virtual int buffers_ready_for_compositor(void const* user_id) const = 0;
50 virtual void drop_old_buffers() = 0;49 virtual void drop_old_buffers() = 0;
51 virtual bool has_submitted_buffer() const = 0;50 virtual bool has_submitted_buffer() const = 0;
5251
=== modified file 'src/include/server/mir/default_server_configuration.h'
--- src/include/server/mir/default_server_configuration.h 2017-07-28 17:00:43 +0000
+++ src/include/server/mir/default_server_configuration.h 2017-09-07 05:58:57 +0000
@@ -175,6 +175,7 @@
175 * dependencies of DisplayServer on the rest of the Mir175 * dependencies of DisplayServer on the rest of the Mir
176 * @{ */176 * @{ */
177 std::shared_ptr<frontend::Connector> the_connector() override;177 std::shared_ptr<frontend::Connector> the_connector() override;
178 std::shared_ptr<frontend::Connector> the_wayland_connector() override;
178 std::shared_ptr<frontend::Connector> the_prompt_connector() override;179 std::shared_ptr<frontend::Connector> the_prompt_connector() override;
179 std::shared_ptr<graphics::Display> the_display() override;180 std::shared_ptr<graphics::Display> the_display() override;
180 std::shared_ptr<compositor::Compositor> the_compositor() override;181 std::shared_ptr<compositor::Compositor> the_compositor() override;
@@ -375,6 +376,7 @@
375/** @} */376/** @} */
376377
377 CachedPtr<frontend::Connector> connector;378 CachedPtr<frontend::Connector> connector;
379 CachedPtr<frontend::Connector> wayland_connector;
378 CachedPtr<frontend::Connector> prompt_connector;380 CachedPtr<frontend::Connector> prompt_connector;
379381
380 CachedPtr<input::InputReport> input_report;382 CachedPtr<input::InputReport> input_report;
381383
=== modified file 'src/include/server/mir/server_configuration.h'
--- src/include/server/mir/server_configuration.h 2017-07-28 17:00:43 +0000
+++ src/include/server/mir/server_configuration.h 2017-09-07 05:58:57 +0000
@@ -69,6 +69,7 @@
69 // TODO most of these interfaces are wider DisplayServer needs...69 // TODO most of these interfaces are wider DisplayServer needs...
70 // TODO ...some or all of them need narrowing70 // TODO ...some or all of them need narrowing
71 virtual std::shared_ptr<frontend::Connector> the_connector() = 0;71 virtual std::shared_ptr<frontend::Connector> the_connector() = 0;
72 virtual std::shared_ptr<frontend::Connector> the_wayland_connector() = 0;
72 virtual std::shared_ptr<frontend::Connector> the_prompt_connector() = 0;73 virtual std::shared_ptr<frontend::Connector> the_prompt_connector() = 0;
73 virtual std::shared_ptr<graphics::Display> the_display() = 0;74 virtual std::shared_ptr<graphics::Display> the_display() = 0;
74 virtual std::shared_ptr<compositor::Compositor> the_compositor() = 0;75 virtual std::shared_ptr<compositor::Compositor> the_compositor() = 0;
7576
=== modified file 'src/platform/graphics/CMakeLists.txt'
--- src/platform/graphics/CMakeLists.txt 2017-05-08 03:04:26 +0000
+++ src/platform/graphics/CMakeLists.txt 2017-09-07 05:58:57 +0000
@@ -14,6 +14,7 @@
14 platform_probe.cpp14 platform_probe.cpp
15 atomic_frame.cpp15 atomic_frame.cpp
16 ${PROJECT_SOURCE_DIR}/include/platform/mir/graphics/display.h16 ${PROJECT_SOURCE_DIR}/include/platform/mir/graphics/display.h
17 ${PROJECT_SOURCE_DIR}/include/platform/mir/graphics/wayland_allocator.h
17)18)
1819
19add_library(mirplatformgraphicscommon OBJECT20add_library(mirplatformgraphicscommon OBJECT
2021
=== modified file 'src/platform/graphics/egl_extensions.cpp'
--- src/platform/graphics/egl_extensions.cpp 2017-07-28 17:00:43 +0000
+++ src/platform/graphics/egl_extensions.cpp 2017-09-07 05:58:57 +0000
@@ -21,8 +21,26 @@
21#include <boost/throw_exception.hpp>21#include <boost/throw_exception.hpp>
22#include <stdexcept>22#include <stdexcept>
2323
24#define MIR_LOG_COMPONENT "EGL extensions"
25#include "mir/log.h"
26
24namespace mg=mir::graphics;27namespace mg=mir::graphics;
2528
29namespace
30{
31std::experimental::optional<mg::EGLExtensions::WaylandExtensions> maybe_wayland_ext()
32{
33 try
34 {
35 return mg::EGLExtensions::WaylandExtensions{};
36 }
37 catch (std::runtime_error const&)
38 {
39 return {};
40 }
41}
42}
43
26mg::EGLExtensions::EGLExtensions() :44mg::EGLExtensions::EGLExtensions() :
27 eglCreateImageKHR{45 eglCreateImageKHR{
28 reinterpret_cast<PFNEGLCREATEIMAGEKHRPROC>(eglGetProcAddress("eglCreateImageKHR"))},46 reinterpret_cast<PFNEGLCREATEIMAGEKHRPROC>(eglGetProcAddress("eglCreateImageKHR"))},
@@ -36,7 +54,8 @@
36 * mix ES and GL code. But other drivers won't be so lenient.54 * mix ES and GL code. But other drivers won't be so lenient.
37 */55 */
38 glEGLImageTargetTexture2DOES{56 glEGLImageTargetTexture2DOES{
39 reinterpret_cast<PFNGLEGLIMAGETARGETTEXTURE2DOESPROC>(eglGetProcAddress("glEGLImageTargetTexture2DOES"))}57 reinterpret_cast<PFNGLEGLIMAGETARGETTEXTURE2DOESPROC>(eglGetProcAddress("glEGLImageTargetTexture2DOES"))},
58 wayland{maybe_wayland_ext()}
40{59{
41 if (!eglCreateImageKHR || !eglDestroyImageKHR)60 if (!eglCreateImageKHR || !eglDestroyImageKHR)
42 BOOST_THROW_EXCEPTION(std::runtime_error("EGL implementation doesn't support EGLImage"));61 BOOST_THROW_EXCEPTION(std::runtime_error("EGL implementation doesn't support EGLImage"));
@@ -44,3 +63,17 @@
44 if (!glEGLImageTargetTexture2DOES)63 if (!glEGLImageTargetTexture2DOES)
45 BOOST_THROW_EXCEPTION(std::runtime_error("GLES2 implementation doesn't support updating a texture from an EGLImage"));64 BOOST_THROW_EXCEPTION(std::runtime_error("GLES2 implementation doesn't support updating a texture from an EGLImage"));
46}65}
66
67mg::EGLExtensions::WaylandExtensions::WaylandExtensions() :
68 eglBindWaylandDisplayWL{
69 reinterpret_cast<PFNEGLBINDWAYLANDDISPLAYWL>(eglGetProcAddress("eglBindWaylandDisplayWL"))
70 },
71 eglQueryWaylandBufferWL{
72 reinterpret_cast<PFNEGLQUERYWAYLANDBUFFERWL>(eglGetProcAddress("eglQueryWaylandBufferWL"))
73 }
74{
75 if (!eglBindWaylandDisplayWL || !eglQueryWaylandBufferWL)
76 {
77 BOOST_THROW_EXCEPTION(std::runtime_error("EGL implementation doesn't support EGL_WL_bind_wayland_display"));
78 }
79}
47\ No newline at end of file80\ No newline at end of file
4881
=== modified file 'src/platforms/mesa/server/CMakeLists.txt'
--- src/platforms/mesa/server/CMakeLists.txt 2017-05-17 04:48:46 +0000
+++ src/platforms/mesa/server/CMakeLists.txt 2017-09-07 05:58:57 +0000
@@ -8,6 +8,7 @@
8include_directories(8include_directories(
9 ${server_common_include_dirs}9 ${server_common_include_dirs}
10 ${DRM_INCLUDE_DIRS}10 ${DRM_INCLUDE_DIRS}
11 ${WAYLAND_SERVER_INCLUDE_DIRS}
11)12)
1213
13add_library(14add_library(
@@ -30,4 +31,5 @@
3031
31 server_platform_common32 server_platform_common
32 kms_utils33 kms_utils
34 ${WAYLAND_SERVER_LDFLAGS} ${WAYLAND_SERVER_LIBRARIES}
33)35)
3436
=== modified file 'src/platforms/mesa/server/buffer_allocator.cpp'
--- src/platforms/mesa/server/buffer_allocator.cpp 2017-07-28 17:00:43 +0000
+++ src/platforms/mesa/server/buffer_allocator.cpp 2017-09-07 05:58:57 +0000
@@ -44,6 +44,12 @@
44#include <cassert>44#include <cassert>
45#include <fcntl.h>45#include <fcntl.h>
4646
47#include <wayland-server.h>
48
49#define MIR_LOG_COMPONENT "mesa-buffer-allocator"
50#include <mir/log.h>
51#include <mutex>
52
47namespace mg = mir::graphics;53namespace mg = mir::graphics;
48namespace mgm = mg::mesa;54namespace mgm = mg::mesa;
49namespace mgc = mg::common;55namespace mgc = mg::common;
@@ -317,3 +323,228 @@
317323
318 return pixel_formats;324 return pixel_formats;
319}325}
326
327namespace
328{
329class WaylandBuffer :
330 public mir::graphics::BufferBasic,
331 public mir::graphics::NativeBufferBase,
332 public mir::renderer::gl::TextureSource
333{
334public:
335 WaylandBuffer(
336 EGLDisplay dpy,
337 wl_resource* buffer,
338 std::shared_ptr<mg::EGLExtensions> const& extensions,
339 std::function<void()>&& on_consumed)
340 : buffer{buffer},
341 dpy{dpy},
342 egl_image{EGL_NO_IMAGE_KHR},
343 extensions{extensions},
344 on_consumed{std::move(on_consumed)}
345 {
346 if (auto notifier = wl_resource_get_destroy_listener(buffer, &on_buffer_destroyed))
347 {
348 DestructionShim* shim;
349
350 shim = wl_container_of(notifier, shim, destruction_listener);
351
352 if (shim->associated_buffer)
353 BOOST_THROW_EXCEPTION(std::logic_error("Attempt to associate a single wl_buffer with multiple WaylandBuffer wrappers"));
354
355 shim->associated_buffer = this;
356 buffer_mutex = shim->mutex;
357 }
358 else
359 {
360 auto shim = new DestructionShim;
361 shim->destruction_listener.notify = &on_buffer_destroyed;
362 shim->associated_buffer = this;
363 buffer_mutex = shim->mutex;
364
365 wl_resource_add_destroy_listener(buffer, &shim->destruction_listener);
366 }
367
368 if (extensions->wayland->eglQueryWaylandBufferWL(dpy, buffer, EGL_WIDTH, &width) == EGL_FALSE)
369 {
370 BOOST_THROW_EXCEPTION(mg::egl_error("Failed to query WaylandAllocator buffer width"));
371 }
372 if (extensions->wayland->eglQueryWaylandBufferWL(dpy, buffer, EGL_HEIGHT, &height) == EGL_FALSE)
373 {
374 BOOST_THROW_EXCEPTION(mg::egl_error("Failed to query WaylandAllocator buffer height"));
375 }
376
377 EGLint texture_format;
378 if (!extensions->wayland->eglQueryWaylandBufferWL(dpy, buffer, EGL_TEXTURE_FORMAT, &texture_format))
379 {
380 BOOST_THROW_EXCEPTION(mg::egl_error("Failed to query WL buffer format"));
381 }
382
383 if (texture_format == EGL_TEXTURE_RGB)
384 {
385 format = mir_pixel_format_xrgb_8888;
386 }
387 else if (texture_format == EGL_TEXTURE_RGBA)
388 {
389 format = mir_pixel_format_argb_8888;
390 }
391 else
392 {
393 BOOST_THROW_EXCEPTION((std::invalid_argument{"YUV buffers are unimplemented"}));
394 }
395 }
396
397 ~WaylandBuffer()
398 {
399 if (egl_image != EGL_NO_IMAGE_KHR)
400 extensions->eglDestroyImageKHR(dpy, egl_image);
401
402 std::lock_guard<std::mutex> lock{*buffer_mutex};
403 if (buffer)
404 {
405 wl_resource_queue_event(buffer, WL_BUFFER_RELEASE);
406 auto notifier = wl_resource_get_destroy_listener(buffer, &on_buffer_destroyed);
407 DestructionShim* shim;
408
409 shim = wl_container_of(notifier, shim, destruction_listener);
410
411 shim->associated_buffer = nullptr;
412 }
413 }
414
415 void gl_bind_to_texture() override
416 {
417 std::unique_lock<std::mutex> lock{*buffer_mutex};
418 if (buffer == nullptr)
419 {
420 mir::log_warning("WaylandBuffer::gl_bind_to_texture() called on a destroyed wl_buffer", this);
421 return;
422 }
423 if (egl_image == EGL_NO_IMAGE_KHR)
424 {
425 eglBindAPI(MIR_SERVER_EGL_OPENGL_API);
426
427 const EGLint image_attrs[] =
428 {
429 EGL_IMAGE_PRESERVED_KHR, EGL_TRUE,
430 EGL_NONE
431 };
432
433 egl_image = extensions->eglCreateImageKHR(
434 dpy,
435 EGL_NO_CONTEXT,
436 EGL_WAYLAND_BUFFER_WL,
437 buffer,
438 image_attrs);
439
440 if (egl_image == EGL_NO_IMAGE_KHR)
441 BOOST_THROW_EXCEPTION(mg::egl_error("Failed to create EGLImage"));
442
443 on_consumed();
444 }
445 lock.unlock();
446
447 extensions->glEGLImageTargetTexture2DOES(GL_TEXTURE_2D, egl_image);
448 }
449
450 void bind() override
451 {
452 gl_bind_to_texture();
453 }
454
455 void secure_for_render() override
456 {
457 }
458
459 std::shared_ptr<mir::graphics::NativeBuffer> native_buffer_handle() const override
460 {
461 return nullptr;
462 }
463
464 mir::geometry::Size size() const override
465 {
466 return mir::geometry::Size{width, height};
467 }
468
469 MirPixelFormat pixel_format() const override
470 {
471 return format;
472 }
473
474 mir::graphics::NativeBufferBase *native_buffer_base() override
475 {
476 return this;
477 }
478
479private:
480 static void on_buffer_destroyed(wl_listener* listener, void*)
481 {
482 static_assert(
483 std::is_standard_layout<DestructionShim>::value,
484 "DestructionShim must be Standard Layout for wl_container_of to be defined behaviour");
485
486 DestructionShim* shim;
487 shim = wl_container_of(listener, shim, destruction_listener);
488
489 {
490 std::lock_guard<std::mutex> lock{*shim->mutex};
491 if (shim->associated_buffer)
492 {
493 shim->associated_buffer->buffer = nullptr;
494 }
495 }
496
497 delete shim;
498 }
499
500 struct DestructionShim
501 {
502 std::shared_ptr<std::mutex> const mutex = std::make_shared<std::mutex>();
503 WaylandBuffer* associated_buffer;
504 wl_listener destruction_listener;
505 };
506
507 std::shared_ptr<std::mutex> buffer_mutex;
508 wl_resource* buffer;
509
510 EGLDisplay dpy;
511 EGLImageKHR egl_image;
512
513 EGLint width, height;
514 MirPixelFormat format;
515
516 std::shared_ptr<mg::EGLExtensions> const extensions;
517
518 std::function<void()> on_consumed;
519};
520}
521
522void mgm::BufferAllocator::bind_display(wl_display* display)
523{
524 dpy = eglGetCurrentDisplay();
525
526 if (dpy == EGL_NO_DISPLAY)
527 BOOST_THROW_EXCEPTION((std::logic_error{"WaylandAllocator::bind_display called without an active EGL Display"}));
528
529 if (!egl_extensions->wayland)
530 {
531 mir::log_warning("No EGL_WL_bind_wayland_display support");
532 return;
533 }
534
535 if (egl_extensions->wayland->eglBindWaylandDisplayWL(dpy, display) == EGL_FALSE)
536 {
537 BOOST_THROW_EXCEPTION(mg::egl_error("Failed to bind Wayland display"));
538 }
539 else
540 {
541 mir::log_info("Bound WaylandAllocator display");
542 }
543}
544
545std::unique_ptr<mg::Buffer> mgm::BufferAllocator::buffer_from_resource (wl_resource* buffer, std::function<void ()>&& on_consumed)
546{
547 if (egl_extensions->wayland)
548 return std::make_unique<WaylandBuffer>(dpy, buffer, egl_extensions, std::move(on_consumed));
549 return nullptr;
550}
320551
=== modified file 'src/platforms/mesa/server/buffer_allocator.h'
--- src/platforms/mesa/server/buffer_allocator.h 2017-07-28 17:00:43 +0000
+++ src/platforms/mesa/server/buffer_allocator.h 2017-09-07 05:58:57 +0000
@@ -22,6 +22,7 @@
22#include "platform_common.h"22#include "platform_common.h"
23#include "mir/graphics/graphic_buffer_allocator.h"23#include "mir/graphics/graphic_buffer_allocator.h"
24#include "mir/graphics/buffer_id.h"24#include "mir/graphics/buffer_id.h"
25#include "mir/graphics/wayland_allocator.h"
25#include "mir_toolkit/mir_native_buffer.h"26#include "mir_toolkit/mir_native_buffer.h"
2627
27#pragma GCC diagnostic push28#pragma GCC diagnostic push
@@ -29,6 +30,8 @@
29#include <gbm.h>30#include <gbm.h>
30#pragma GCC diagnostic pop31#pragma GCC diagnostic pop
3132
33#include <EGL/egl.h>
34
32#include <memory>35#include <memory>
3336
34namespace mir37namespace mir
@@ -46,7 +49,9 @@
46 dma_buf49 dma_buf
47};50};
4851
49class BufferAllocator: public graphics::GraphicBufferAllocator52class BufferAllocator:
53 public graphics::GraphicBufferAllocator,
54 public graphics::WaylandAllocator
50{55{
51public:56public:
52 BufferAllocator(gbm_device* device, BypassOption bypass_option, BufferImportMethod const buffer_import_method);57 BufferAllocator(gbm_device* device, BypassOption bypass_option, BufferImportMethod const buffer_import_method);
@@ -57,10 +62,13 @@
57 std::shared_ptr<Buffer> alloc_buffer(graphics::BufferProperties const& buffer_properties) override;62 std::shared_ptr<Buffer> alloc_buffer(graphics::BufferProperties const& buffer_properties) override;
58 std::vector<MirPixelFormat> supported_pixel_formats() override;63 std::vector<MirPixelFormat> supported_pixel_formats() override;
5964
65 void bind_display(wl_display* display) override;
66 std::unique_ptr<Buffer> buffer_from_resource (wl_resource* buffer, std::function<void ()>&& on_consumed) override;
60private:67private:
61 std::shared_ptr<Buffer> alloc_hardware_buffer(68 std::shared_ptr<Buffer> alloc_hardware_buffer(
62 graphics::BufferProperties const& buffer_properties);69 graphics::BufferProperties const& buffer_properties);
6370
71 EGLDisplay dpy;
64 gbm_device* const device;72 gbm_device* const device;
65 std::shared_ptr<EGLExtensions> const egl_extensions;73 std::shared_ptr<EGLExtensions> const egl_extensions;
6674
6775
=== modified file 'src/platforms/mesa/server/kms/CMakeLists.txt'
--- src/platforms/mesa/server/kms/CMakeLists.txt 2017-06-09 19:25:54 +0000
+++ src/platforms/mesa/server/kms/CMakeLists.txt 2017-09-07 05:58:57 +0000
@@ -9,6 +9,7 @@
9 ${GBM_INCLUDE_DIRS}9 ${GBM_INCLUDE_DIRS}
10 ${EGL_INCLUDE_DIRS}10 ${EGL_INCLUDE_DIRS}
11 ${GL_INCLUDE_DIRS}11 ${GL_INCLUDE_DIRS}
12 ${WAYLAND_SERVER_INCLUDE_DIRS}
12 ${UDEV_INCLUDE_DIRS}13 ${UDEV_INCLUDE_DIRS}
13 ${PROJECT_SOURCE_DIR}/include/client14 ${PROJECT_SOURCE_DIR}/include/client
14)15)
@@ -65,6 +66,7 @@
65 ${GBM_LDFLAGS} ${GBM_LIBRARIES}66 ${GBM_LDFLAGS} ${GBM_LIBRARIES}
66 ${EGL_LDFLAGS} ${EGL_LIBRARIES}67 ${EGL_LDFLAGS} ${EGL_LIBRARIES}
67 ${GL_LDFLAGS} ${GL_LIBRARIES}68 ${GL_LDFLAGS} ${GL_LIBRARIES}
69 ${WAYLAND_SERVER_LDFLAGS} ${WAYLAND_SERVER_LIBRARIES}
68)70)
6971
70set_target_properties(72set_target_properties(
7173
=== modified file 'src/platforms/mesa/server/kms/platform.cpp'
--- src/platforms/mesa/server/kms/platform.cpp 2017-07-28 17:00:43 +0000
+++ src/platforms/mesa/server/kms/platform.cpp 2017-09-07 05:58:57 +0000
@@ -29,9 +29,23 @@
29#include "mir/emergency_cleanup_registry.h"29#include "mir/emergency_cleanup_registry.h"
30#include "mir/udev/wrapper.h"30#include "mir/udev/wrapper.h"
31#include "mesa_extensions.h"31#include "mesa_extensions.h"
32#include "mir/renderer/gl/texture_target.h"
33#include "mir/graphics/buffer_basic.h"
34#include "mir/graphics/egl_error.h"
35
36#include <EGL/egl.h>
37#include <EGL/eglext.h>
38#include MIR_SERVER_GL_H
39#include MIR_SERVER_GLEXT_H
40
41#define MIR_LOG_COMPONENT "platform-graphics-mesa"
42#include "mir/log.h"
3243
33#include <boost/throw_exception.hpp>44#include <boost/throw_exception.hpp>
34#include <stdexcept>45#include <stdexcept>
46#include <mir/renderer/gl/texture_source.h>
47#include <wayland-server-core.h>
48#include <wayland-server-protocol.h>
3549
36namespace mg = mir::graphics;50namespace mg = mir::graphics;
37namespace mgm = mg::mesa;51namespace mgm = mg::mesa;
3852
=== added directory 'src/protocol'
=== added file 'src/protocol/CMakeLists.txt'
--- src/protocol/CMakeLists.txt 1970-01-01 00:00:00 +0000
+++ src/protocol/CMakeLists.txt 2017-09-07 05:58:57 +0000
@@ -0,0 +1,28 @@
1pkg_check_modules(XMLPP libxml++-2.6 REQUIRED)
2
3include_directories(SYSTEM ${XMLPP_INCLUDE_DIRS})
4
5add_executable(wrapper-generator
6
7 wrapper_generator.cpp
8)
9
10target_link_libraries(wrapper-generator
11
12 ${XMLPP_LDFLAGS} ${XMLPP_LIBRARIES}
13)
14
15get_filename_component(
16 GENERATED_HEADER src/server/frontend/wayland/core_generated_interfaces.h
17 ABSOLUTE
18 BASE_DIR ${PROJECT_SOURCE_DIR}
19)
20
21add_custom_target(refresh-wayland-wrapper
22 COMMAND "sh" "-c" "${CMAKE_BINARY_DIR}/bin/wrapper-generator wl_ /usr/share/wayland/wayland.xml >${GENERATED_HEADER}"
23 VERBATIM
24 DEPENDS wrapper-generator
25 DEPENDS /usr/share/wayland/wayland.xml
26 SOURCES ${GENERATED_HEADER}
27)
28
029
=== added file 'src/protocol/wrapper_generator.cpp'
--- src/protocol/wrapper_generator.cpp 1970-01-01 00:00:00 +0000
+++ src/protocol/wrapper_generator.cpp 2017-09-07 05:58:57 +0000
@@ -0,0 +1,509 @@
1/*
2 * Copyright © 2017 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: Christopher James Halse Rogers <christopher.halse.rogers@canonical.com>
17 */
18
19#include <libxml++/libxml++.h>
20#include <iostream>
21#include <unordered_map>
22#include <unordered_set>
23#include <functional>
24
25#include <experimental/optional>
26#include <vector>
27#include <locale>
28#include <stdio.h>
29
30void emit_comment_header(std::ostream& out)
31{
32 out << "/*" << std::endl;
33 out << " * AUTOGENERATED - DO NOT EDIT" << std::endl;
34 out << " *" << std::endl;
35 out << " * This header is generated by src/protocol/wrapper_generator.cpp" << std::endl;
36 out << " * To regenerate, run the “refresh-wayland-wrapper” target." << std::endl;
37 out << " */" << std::endl;
38}
39
40void emit_required_headers()
41{
42 std::cout << "#include <experimental/optional>" << std::endl;
43 std::cout << "#include <boost/throw_exception.hpp>" << std::endl;
44 std::cout << std::endl;
45 std::cout << "#include <wayland-server.h>" << std::endl;
46 std::cout << "#include <wayland-server-protocol.h>" << std::endl;
47 std::cout << std::endl;
48 std::cout << "#include \"mir/fd.h\"" << std::endl;
49}
50
51std::string strip_wl_prefix(std::string const& name)
52{
53 return name.substr(3);
54}
55
56std::string camel_case_string(std::string const& name)
57{
58 std::string camel_cased_name;
59 camel_cased_name = std::string{std::toupper(name[0], std::locale("C"))} + name.substr(1);
60 auto next_underscore_offset = name.find('_');
61 while (next_underscore_offset != std::string::npos)
62 {
63 if (next_underscore_offset < camel_cased_name.length())
64 {
65 camel_cased_name = camel_cased_name.substr(0, next_underscore_offset) +
66 std::toupper(camel_cased_name[next_underscore_offset + 1], std::locale("C")) +
67 camel_cased_name.substr(next_underscore_offset + 2);
68 }
69 next_underscore_offset = camel_cased_name.find('_', next_underscore_offset);
70 }
71 return camel_cased_name;
72}
73
74struct ArgumentTypeDescriptor
75{
76 std::string cpp_type;
77 std::string c_type;
78 std::experimental::optional<std::vector<std::string>> converter;
79};
80
81std::vector<std::string> fd_converter{
82 "mir::Fd $NAME_resolved{$NAME};"
83};
84
85std::vector<std::string> optional_object_converter{
86 "std::experimental::optional<struct wl_resource*> $NAME_resolved;",
87 "if ($NAME != nullptr)",
88 "{",
89 " $NAME_resolved = $NAME;",
90 "}"
91};
92
93std::vector<std::string> optional_string_converter{
94 "std::experimental::optional<std::string> $NAME_resolved;",
95 "if ($NAME != nullptr)",
96 "{",
97 " $NAME_resolved = std::experimental::make_optional<std::string>($NAME);",
98 "}"
99};
100
101std::unordered_map<std::string, ArgumentTypeDescriptor const> type_map = {
102 { "uint", { "uint32_t", "uint32_t", {} }},
103 { "int", { "int32_t", "int32_t", {} }},
104 { "fd", { "mir::Fd", "int", { fd_converter }}},
105 { "object", { "struct wl_resource*", "struct wl_resource*", {} }},
106 { "string", { "std::string const&", "char const*", {} }},
107 { "new_id", { "uint32_t", "uint32_t", {} }}
108};
109
110std::unordered_map<std::string, ArgumentTypeDescriptor const> optional_type_map = {
111 { "object", { "std::experimental::optional<struct wl_resource*> const&", "struct wl_resource*", { optional_object_converter }}},
112 { "string", { "std::experimental::optional<std::string> const&", "char const*", { optional_string_converter} }},
113};
114
115bool parse_optional(xmlpp::Element const& arg)
116{
117 if (auto allow_null = arg.get_attribute("allow-null"))
118 {
119 return allow_null->get_value() == "true";
120 }
121 return false;
122}
123
124class Interface;
125
126class Argument
127{
128public:
129 Argument(xmlpp::Element const& node)
130 : name{node.get_attribute_value("name")},
131 descriptor{parse_optional(node) ? optional_type_map.at(node.get_attribute_value("type"))
132 : type_map.at(node.get_attribute_value("type"))}
133 {
134 }
135
136 void emit_c_prototype(std::ostream& out) const
137 {
138 out << descriptor.c_type << " " << name;
139 }
140 void emit_cpp_prototype(std::ostream& out) const
141 {
142 out << descriptor.cpp_type << " " << name;
143 }
144 void emit_thunk_call_fragment(std::ostream& out) const
145 {
146 out << (descriptor.converter ? (name + "_resolved") : name);
147 }
148
149 void emit_thunk_converter(std::ostream& out, std::string const& indent) const
150 {
151 for (auto const& line : descriptor.converter.value_or(std::vector<std::string>{}))
152 {
153 std::string substituted_line = line;
154 size_t substitution_pos = substituted_line.find("$NAME");
155 while (substitution_pos != std::string::npos)
156 {
157 substituted_line = substituted_line.replace(substitution_pos, 5, name);
158 substitution_pos = substituted_line.find("$NAME");
159 }
160 out << indent << substituted_line << std::endl;
161 }
162 }
163
164private:
165 std::string const name;
166 ArgumentTypeDescriptor const& descriptor;
167};
168
169class Method
170{
171public:
172 Method(xmlpp::Element const& node)
173 : name{node.get_attribute_value("name")}
174 {
175 for (auto const& child : node.get_children("arg"))
176 {
177 auto arg_node = dynamic_cast<xmlpp::Element const*>(child);
178 arguments.emplace_back(std::ref(*arg_node));
179 }
180 }
181
182 // TODO: Decide whether to resolve wl_resource* to wrapped types (ie: Region, Surface, etc).
183 void emit_virtual_prototype(std::ostream& out, std::string const& indent, bool is_global) const
184 {
185 out << indent << "virtual void " << name << "(";
186 if (is_global)
187 {
188 out << "struct wl_client* client, struct wl_resource* resource";
189 if (!arguments.empty())
190 {
191 out << ", ";
192 }
193 }
194 for (size_t i = 0 ; i < arguments.size() ; ++i)
195 {
196 arguments[i].emit_cpp_prototype(out);
197 if (i != arguments.size() - 1)
198 {
199 out << ", ";
200 }
201 }
202 out << ") = 0;" << std::endl;
203 }
204
205 // TODO: Decide whether to resolve wl_resource* to wrapped types (ie: Region, Surface, etc).
206 void emit_thunk(std::ostream& out, std::string const& indent,
207 std::string const& interface_type, bool is_global) const
208 {
209 out << indent << "static void " << name << "_thunk("
210 << "struct wl_client*" << (is_global ? " client" : "")
211 << ", struct wl_resource* resource";
212 for (auto const& arg : arguments)
213 {
214 out << ", ";
215 arg.emit_c_prototype(out);
216 }
217 out << ")" << std::endl;
218
219 out << indent << "{" << std::endl;
220 out << indent << " auto me = static_cast<" << interface_type << "*>("
221 << "wl_resource_get_user_data(resource));" << std::endl;
222 for (auto const& arg : arguments)
223 {
224 arg.emit_thunk_converter(out, indent + " ");
225 }
226
227 out << indent << " me->" << name << "(";
228 if (is_global)
229 {
230 out << "client, resource";
231 if (!arguments.empty())
232 {
233 out << ", ";
234 }
235 }
236 for (size_t i = 0; i < arguments.size(); ++i)
237 {
238 arguments[i].emit_thunk_call_fragment(out);
239 if (i != arguments.size() - 1)
240 {
241 out << ", ";
242 }
243 }
244 out << ");" << std::endl;
245
246 out << indent << "}" << std::endl;
247 }
248
249 void emit_vtable_initialiser(std::ostream& out, std::string const& indent) const
250 {
251 out << indent << name << "_thunk," << std::endl;
252 }
253
254private:
255 std::string const name;
256 std::vector<Argument> arguments;
257};
258
259void emit_indented_lines(std::ostream& out, std::string const& indent,
260 std::initializer_list<std::initializer_list<std::string>> lines)
261{
262 for (auto const& line : lines)
263 {
264 out << indent;
265 for (auto const& fragment : line)
266 {
267 out << fragment;
268 }
269 out << std::endl;
270 }
271}
272
273class Interface
274{
275public:
276 Interface(
277 xmlpp::Element const& node,
278 std::function<std::string(std::string)> const& name_transform,
279 std::unordered_set<std::string> const& constructable_interfaces)
280 : wl_name{node.get_attribute_value("name")},
281 generated_name{name_transform(wl_name)},
282 is_global{constructable_interfaces.count(wl_name) == 0}
283 {
284 for (auto method_node : node.get_children("request"))
285 {
286 auto method = dynamic_cast<xmlpp::Element*>(method_node);
287 methods.emplace_back(std::ref(*method));
288 }
289 }
290
291 void emit_constructor(std::ostream& out, std::string const& indent, bool has_vtable)
292 {
293 if (is_global)
294 {
295 emit_constructor_for_global(out, indent);
296 }
297 else
298 {
299 emit_constructor_for_regular(out, indent, has_vtable);
300 }
301 }
302
303 void emit_bind(std::ostream& out, std::string const& indent, bool has_vtable)
304 {
305 emit_indented_lines(out, indent, {
306 {"static void bind(struct wl_client* client, void* data, uint32_t version, uint32_t id)"},
307 {"{"},
308 });
309 emit_indented_lines(out, indent + " ", {
310 {"auto me = static_cast<", generated_name, "*>(data);"},
311 {"auto resource = wl_resource_create(client, &", wl_name, "_interface,"},
312 {" std::min(version, me->max_version), id);"},
313 {"if (resource == nullptr)"},
314 {"{"},
315 {" wl_client_post_no_memory(client);"},
316 {" BOOST_THROW_EXCEPTION((std::bad_alloc{}));"},
317 {"}"},
318 });
319 if (has_vtable)
320 {
321 emit_indented_lines(out, indent + " ",
322 {{"wl_resource_set_implementation(resource, &vtable, me, nullptr);"}});
323 }
324 emit_indented_lines(out, indent, {
325 {"}"}
326 });
327 }
328
329 void emit_class(std::ostream& out)
330 {
331 out << "class " << generated_name << std::endl;
332 out << "{" << std::endl;
333 out << "protected:" << std::endl;
334
335 emit_constructor(out, " ", !methods.empty());
336 out << " virtual ~" << generated_name << "() = default;" << std::endl;
337 out << std::endl;
338
339 for (auto const& method : methods)
340 {
341 method.emit_virtual_prototype(out, " ", is_global);
342 }
343 out << std::endl;
344
345 if (!is_global)
346 {
347 emit_indented_lines(out, " ", {
348 { "struct wl_client* const client;" },
349 { "struct wl_resource* const resource;"}
350 });
351 out << std::endl;
352 }
353
354 if (!methods.empty())
355 {
356 out << "private:" << std::endl;
357 }
358
359 for (auto const& method : methods)
360 {
361 method.emit_thunk(out, " ", generated_name, is_global);
362 out << std::endl;
363 }
364
365 if (is_global)
366 {
367 emit_bind(out, " ", !methods.empty());
368 out << std::endl;
369 emit_indented_lines(out, " ", {
370 { "uint32_t const max_version;" }
371 });
372 }
373
374 if (!methods.empty())
375 {
376 emit_indented_lines(out, " ", {
377 { "static struct ", wl_name, "_interface const vtable;" }
378 });
379 }
380
381 out << "};" << std::endl;
382
383 out << std::endl;
384
385 if (!methods.empty())
386 {
387 out << "struct " << wl_name << "_interface const " << generated_name << "::vtable = {" << std::endl;
388 for (auto const& method : methods)
389 {
390 method.emit_vtable_initialiser(out, " ");
391 }
392 out << "};" << std::endl;
393 }
394 }
395
396private:
397 void emit_constructor_for_global(std::ostream& out, std::string const& indent)
398 {
399 out << indent << generated_name << "(struct wl_display* display, uint32_t max_version)" << std::endl;
400 out << indent << " : max_version{max_version}" << std::endl;
401 out << indent << "{" << std::endl;
402 out << indent << " if (!wl_global_create(display, " << std::endl;
403 out << indent << " &" << wl_name << "_interface, max_version," << std::endl;
404 out << indent << " this, &" << generated_name << "::bind))" << std::endl;
405 out << indent << " {" << std::endl;
406 out << indent << " BOOST_THROW_EXCEPTION((std::runtime_error{\"Failed to export "
407 << wl_name << " interface\"}));" << std::endl;
408 out << indent << " }" << std::endl;
409 out << indent << "}" << std::endl;
410 }
411
412 void emit_constructor_for_regular(std::ostream& out, std::string const& indent, bool has_vtable)
413 {
414 emit_indented_lines(out, indent, {
415 { generated_name, "(struct wl_client* client, struct wl_resource* parent, uint32_t id)" },
416 { " : client{client}," },
417 { " resource{wl_resource_create(client, &", wl_name, "_interface, wl_resource_get_version(parent), id)}" },
418 { "{" }
419 });
420 emit_indented_lines(out, indent + " ", {
421 { "if (resource == nullptr)" },
422 { "{" },
423 { " wl_resource_post_no_memory(parent);" },
424 { " BOOST_THROW_EXCEPTION((std::bad_alloc{}));" },
425 { "}" },
426 });
427 if (has_vtable)
428 {
429 emit_indented_lines(out, indent + " ",
430 {{ "wl_resource_set_implementation(resource, &vtable, this, nullptr);" }});
431 }
432 emit_indented_lines(out, indent, {
433 { "}" }
434 });
435 }
436
437 std::string const wl_name;
438 std::string const generated_name;
439 bool const is_global;
440 std::vector<Method> methods;
441};
442
443int main(int argc, char** argv)
444{
445 if (argc != 3)
446 {
447 exit(1);
448 }
449
450 std::string const prefix{argv[1]};
451
452 auto name_transform = [prefix](std::string protocol_name)
453 {
454 std::string transformed_name = protocol_name;
455 if (protocol_name.find(prefix) == 0)
456 {
457 transformed_name = protocol_name.substr(prefix.length());
458 }
459 return camel_case_string(transformed_name);
460 };
461
462 xmlpp::DomParser parser(argv[2]);
463
464 auto document = parser.get_document();
465
466 auto root_node = document->get_root_node();
467
468 auto constructor_nodes = root_node->find("//arg[@type='new_id']");
469 std::unordered_set<std::string> constructible_interfaces;
470 for (auto const node : constructor_nodes)
471 {
472 auto arg = dynamic_cast<xmlpp::Element const*>(node);
473 constructible_interfaces.insert(arg->get_attribute_value("interface"));
474 }
475
476 emit_comment_header(std::cout);
477
478 std::cout << std::endl;
479
480 emit_required_headers();
481
482 std::cout << std::endl;
483
484 std::cout << "namespace mir" << std::endl;
485 std::cout << "{" << std::endl;
486 std::cout << "namespace frontend" << std::endl;
487 std::cout << "{" << std::endl;
488 std::cout << "namespace wayland" << std::endl;
489 std::cout << "{" << std::endl;
490
491 for (auto top_level : root_node->get_children("interface"))
492 {
493 auto interface = dynamic_cast<xmlpp::Element*>(top_level);
494
495 if (interface->get_attribute_value("name") == "wl_display" ||
496 interface->get_attribute_value("name") == "wl_registry")
497 {
498 // These are special, and don't need binding.
499 continue;
500 }
501 Interface(*interface, name_transform, constructible_interfaces).emit_class(std::cout);
502
503 std::cout << std::endl << std::endl;
504 }
505 std::cout << "}" << std::endl;
506 std::cout << "}" << std::endl;
507 std::cout << "}" << std::endl;
508 return 0;
509}
0510
=== modified file 'src/server/CMakeLists.txt'
--- src/server/CMakeLists.txt 2017-08-02 11:07:50 +0000
+++ src/server/CMakeLists.txt 2017-09-07 05:58:57 +0000
@@ -76,6 +76,7 @@
76 $<TARGET_OBJECTS:mircompositor>76 $<TARGET_OBJECTS:mircompositor>
77 $<TARGET_OBJECTS:mirgraphics>77 $<TARGET_OBJECTS:mirgraphics>
78 $<TARGET_OBJECTS:mirfrontend>78 $<TARGET_OBJECTS:mirfrontend>
79 $<TARGET_OBJECTS:mirfrontend-wayland>
79 $<TARGET_OBJECTS:mirshell>80 $<TARGET_OBJECTS:mirshell>
80 $<TARGET_OBJECTS:mirlttng>81 $<TARGET_OBJECTS:mirlttng>
81 $<TARGET_OBJECTS:mirreport>82 $<TARGET_OBJECTS:mirreport>
@@ -120,6 +121,7 @@
120 ${UDEV_LDFLAGS} ${UDEV_LIBRARIES}121 ${UDEV_LDFLAGS} ${UDEV_LIBRARIES}
121 ${GLIB_LDFLAGS} ${GLIB_LIBRARIES}122 ${GLIB_LDFLAGS} ${GLIB_LIBRARIES}
122 ${UUID_LDFLAGS} ${UUID_LIBRARIES}123 ${UUID_LDFLAGS} ${UUID_LIBRARIES}
124 ${WAYLAND_SERVER_LDFLAGS} ${WAYLAND_SERVER_LIBRARIES}
123)125)
124126
125install(TARGETS mirserver127install(TARGETS mirserver
126128
=== modified file 'src/server/display_server.cpp'
--- src/server/display_server.cpp 2017-09-06 11:14:47 +0000
+++ src/server/display_server.cpp 2017-09-07 05:58:57 +0000
@@ -50,6 +50,7 @@
50 input_dispatcher{config.the_input_dispatcher()},50 input_dispatcher{config.the_input_dispatcher()},
51 compositor{config.the_compositor()},51 compositor{config.the_compositor()},
52 connector{config.the_connector()},52 connector{config.the_connector()},
53 wayland_connector{config.the_wayland_connector()},
53 prompt_connector{config.the_prompt_connector()},54 prompt_connector{config.the_prompt_connector()},
54 input_manager{config.the_input_manager()},55 input_manager{config.the_input_manager()},
55 main_loop{config.the_main_loop()},56 main_loop{config.the_main_loop()},
@@ -73,9 +74,13 @@
73 {74 {
74 auto comm = try_but_revert_if_unwinding(75 auto comm = try_but_revert_if_unwinding(
75 [this] { connector->stop(); },76 [this] { connector->stop(); },
76 [&, this] { connector->start(); });77 [this] { connector->start(); });
7778
78 auto prompt = try_but_revert_if_unwinding(79 auto wayland = try_but_revert_if_unwinding(
80 [this] { wayland_connector->stop(); },
81 [this] { wayland_connector->start(); });
82
83 auto prompt = try_but_revert_if_unwinding(
79 [this] { prompt_connector->stop(); },84 [this] { prompt_connector->stop(); },
80 [&, this] { prompt_connector->start(); });85 [&, this] { prompt_connector->start(); });
8186
@@ -161,6 +166,7 @@
161 std::shared_ptr<mi::InputDispatcher> const input_dispatcher;166 std::shared_ptr<mi::InputDispatcher> const input_dispatcher;
162 std::shared_ptr<mc::Compositor> const compositor;167 std::shared_ptr<mc::Compositor> const compositor;
163 std::shared_ptr<mf::Connector> const connector;168 std::shared_ptr<mf::Connector> const connector;
169 std::shared_ptr<mf::Connector> const wayland_connector;
164 std::shared_ptr<mf::Connector> const prompt_connector;170 std::shared_ptr<mf::Connector> const prompt_connector;
165 std::shared_ptr<mi::InputManager> const input_manager;171 std::shared_ptr<mi::InputManager> const input_manager;
166 std::shared_ptr<mir::MainLoop> const main_loop;172 std::shared_ptr<mir::MainLoop> const main_loop;
@@ -196,11 +202,13 @@
196 server.input_dispatcher->start();202 server.input_dispatcher->start();
197 server.prompt_connector->start();203 server.prompt_connector->start();
198 server.connector->start();204 server.connector->start();
205 server.wayland_connector->start();
199206
200 server.server_status_listener->started();207 server.server_status_listener->started();
201208
202 server.main_loop->run();209 server.main_loop->run();
203210
211 server.wayland_connector->stop();
204 server.connector->stop();212 server.connector->stop();
205 server.prompt_connector->stop();213 server.prompt_connector->stop();
206 server.input_dispatcher->stop();214 server.input_dispatcher->stop();
207215
=== modified file 'src/server/frontend/CMakeLists.txt'
--- src/server/frontend/CMakeLists.txt 2017-07-04 04:38:55 +0000
+++ src/server/frontend/CMakeLists.txt 2017-09-07 05:58:57 +0000
@@ -36,6 +36,8 @@
36 ${PROJECT_SOURCE_DIR}/src/include/server/mir/frontend/shell.h36 ${PROJECT_SOURCE_DIR}/src/include/server/mir/frontend/shell.h
37)37)
3838
39add_subdirectory(wayland)
40
39add_library(41add_library(
40 mirfrontend OBJECT42 mirfrontend OBJECT
4143
4244
=== added directory 'src/server/frontend/wayland'
=== added file 'src/server/frontend/wayland/CMakeLists.txt'
--- src/server/frontend/wayland/CMakeLists.txt 1970-01-01 00:00:00 +0000
+++ src/server/frontend/wayland/CMakeLists.txt 2017-09-07 05:58:57 +0000
@@ -0,0 +1,14 @@
1set(
2 WAYLAND_SOURCES
3
4 core_generated_interfaces.h
5 wayland_default_configuration.cpp
6 wayland_connector.cpp
7)
8
9add_library(
10 mirfrontend-wayland OBJECT
11
12 ${WAYLAND_SOURCES}
13)
14
015
=== added file 'src/server/frontend/wayland/core_generated_interfaces.h'
--- src/server/frontend/wayland/core_generated_interfaces.h 1970-01-01 00:00:00 +0000
+++ src/server/frontend/wayland/core_generated_interfaces.h 2017-09-07 05:58:57 +0000
@@ -0,0 +1,1189 @@
1/*
2 * AUTOGENERATED - DO NOT EDIT
3 *
4 * This header is generated by src/protocol/wrapper_generator.cpp
5 * To regenerate, run the “refresh-wayland-wrapper” target.
6 */
7
8#include <experimental/optional>
9#include <boost/throw_exception.hpp>
10
11#include <wayland-server.h>
12#include <wayland-server-protocol.h>
13
14#include "mir/fd.h"
15
16namespace mir
17{
18namespace frontend
19{
20namespace wayland
21{
22class Callback
23{
24protected:
25 Callback(struct wl_client* client, struct wl_resource* parent, uint32_t id)
26 : client{client},
27 resource{wl_resource_create(client, &wl_callback_interface, wl_resource_get_version(parent), id)}
28 {
29 if (resource == nullptr)
30 {
31 wl_resource_post_no_memory(parent);
32 BOOST_THROW_EXCEPTION((std::bad_alloc{}));
33 }
34 }
35 virtual ~Callback() = default;
36
37
38 struct wl_client* const client;
39 struct wl_resource* const resource;
40
41};
42
43
44
45class Compositor
46{
47protected:
48 Compositor(struct wl_display* display, uint32_t max_version)
49 : max_version{max_version}
50 {
51 if (!wl_global_create(display,
52 &wl_compositor_interface, max_version,
53 this, &Compositor::bind))
54 {
55 BOOST_THROW_EXCEPTION((std::runtime_error{"Failed to export wl_compositor interface"}));
56 }
57 }
58 virtual ~Compositor() = default;
59
60 virtual void create_surface(struct wl_client* client, struct wl_resource* resource, uint32_t id) = 0;
61 virtual void create_region(struct wl_client* client, struct wl_resource* resource, uint32_t id) = 0;
62
63private:
64 static void create_surface_thunk(struct wl_client* client, struct wl_resource* resource, uint32_t id)
65 {
66 auto me = static_cast<Compositor*>(wl_resource_get_user_data(resource));
67 me->create_surface(client, resource, id);
68 }
69
70 static void create_region_thunk(struct wl_client* client, struct wl_resource* resource, uint32_t id)
71 {
72 auto me = static_cast<Compositor*>(wl_resource_get_user_data(resource));
73 me->create_region(client, resource, id);
74 }
75
76 static void bind(struct wl_client* client, void* data, uint32_t version, uint32_t id)
77 {
78 auto me = static_cast<Compositor*>(data);
79 auto resource = wl_resource_create(client, &wl_compositor_interface,
80 std::min(version, me->max_version), id);
81 if (resource == nullptr)
82 {
83 wl_client_post_no_memory(client);
84 BOOST_THROW_EXCEPTION((std::bad_alloc{}));
85 }
86 wl_resource_set_implementation(resource, &vtable, me, nullptr);
87 }
88
89 uint32_t const max_version;
90 static struct wl_compositor_interface const vtable;
91};
92
93struct wl_compositor_interface const Compositor::vtable = {
94 create_surface_thunk,
95 create_region_thunk,
96};
97
98
99class ShmPool
100{
101protected:
102 ShmPool(struct wl_client* client, struct wl_resource* parent, uint32_t id)
103 : client{client},
104 resource{wl_resource_create(client, &wl_shm_pool_interface, wl_resource_get_version(parent), id)}
105 {
106 if (resource == nullptr)
107 {
108 wl_resource_post_no_memory(parent);
109 BOOST_THROW_EXCEPTION((std::bad_alloc{}));
110 }
111 wl_resource_set_implementation(resource, &vtable, this, nullptr);
112 }
113 virtual ~ShmPool() = default;
114
115 virtual void create_buffer(uint32_t id, int32_t offset, int32_t width, int32_t height, int32_t stride, uint32_t format) = 0;
116 virtual void destroy() = 0;
117 virtual void resize(int32_t size) = 0;
118
119 struct wl_client* const client;
120 struct wl_resource* const resource;
121
122private:
123 static void create_buffer_thunk(struct wl_client*, struct wl_resource* resource, uint32_t id, int32_t offset, int32_t width, int32_t height, int32_t stride, uint32_t format)
124 {
125 auto me = static_cast<ShmPool*>(wl_resource_get_user_data(resource));
126 me->create_buffer(id, offset, width, height, stride, format);
127 }
128
129 static void destroy_thunk(struct wl_client*, struct wl_resource* resource)
130 {
131 auto me = static_cast<ShmPool*>(wl_resource_get_user_data(resource));
132 me->destroy();
133 }
134
135 static void resize_thunk(struct wl_client*, struct wl_resource* resource, int32_t size)
136 {
137 auto me = static_cast<ShmPool*>(wl_resource_get_user_data(resource));
138 me->resize(size);
139 }
140
141 static struct wl_shm_pool_interface const vtable;
142};
143
144struct wl_shm_pool_interface const ShmPool::vtable = {
145 create_buffer_thunk,
146 destroy_thunk,
147 resize_thunk,
148};
149
150
151class Shm
152{
153protected:
154 Shm(struct wl_display* display, uint32_t max_version)
155 : max_version{max_version}
156 {
157 if (!wl_global_create(display,
158 &wl_shm_interface, max_version,
159 this, &Shm::bind))
160 {
161 BOOST_THROW_EXCEPTION((std::runtime_error{"Failed to export wl_shm interface"}));
162 }
163 }
164 virtual ~Shm() = default;
165
166 virtual void create_pool(struct wl_client* client, struct wl_resource* resource, uint32_t id, mir::Fd fd, int32_t size) = 0;
167
168private:
169 static void create_pool_thunk(struct wl_client* client, struct wl_resource* resource, uint32_t id, int fd, int32_t size)
170 {
171 auto me = static_cast<Shm*>(wl_resource_get_user_data(resource));
172 mir::Fd fd_resolved{fd};
173 me->create_pool(client, resource, id, fd_resolved, size);
174 }
175
176 static void bind(struct wl_client* client, void* data, uint32_t version, uint32_t id)
177 {
178 auto me = static_cast<Shm*>(data);
179 auto resource = wl_resource_create(client, &wl_shm_interface,
180 std::min(version, me->max_version), id);
181 if (resource == nullptr)
182 {
183 wl_client_post_no_memory(client);
184 BOOST_THROW_EXCEPTION((std::bad_alloc{}));
185 }
186 wl_resource_set_implementation(resource, &vtable, me, nullptr);
187 }
188
189 uint32_t const max_version;
190 static struct wl_shm_interface const vtable;
191};
192
193struct wl_shm_interface const Shm::vtable = {
194 create_pool_thunk,
195};
196
197
198class Buffer
199{
200protected:
201 Buffer(struct wl_client* client, struct wl_resource* parent, uint32_t id)
202 : client{client},
203 resource{wl_resource_create(client, &wl_buffer_interface, wl_resource_get_version(parent), id)}
204 {
205 if (resource == nullptr)
206 {
207 wl_resource_post_no_memory(parent);
208 BOOST_THROW_EXCEPTION((std::bad_alloc{}));
209 }
210 wl_resource_set_implementation(resource, &vtable, this, nullptr);
211 }
212 virtual ~Buffer() = default;
213
214 virtual void destroy() = 0;
215
216 struct wl_client* const client;
217 struct wl_resource* const resource;
218
219private:
220 static void destroy_thunk(struct wl_client*, struct wl_resource* resource)
221 {
222 auto me = static_cast<Buffer*>(wl_resource_get_user_data(resource));
223 me->destroy();
224 }
225
226 static struct wl_buffer_interface const vtable;
227};
228
229struct wl_buffer_interface const Buffer::vtable = {
230 destroy_thunk,
231};
232
233
234class DataOffer
235{
236protected:
237 DataOffer(struct wl_client* client, struct wl_resource* parent, uint32_t id)
238 : client{client},
239 resource{wl_resource_create(client, &wl_data_offer_interface, wl_resource_get_version(parent), id)}
240 {
241 if (resource == nullptr)
242 {
243 wl_resource_post_no_memory(parent);
244 BOOST_THROW_EXCEPTION((std::bad_alloc{}));
245 }
246 wl_resource_set_implementation(resource, &vtable, this, nullptr);
247 }
248 virtual ~DataOffer() = default;
249
250 virtual void accept(uint32_t serial, std::experimental::optional<std::string> const& mime_type) = 0;
251 virtual void receive(std::string const& mime_type, mir::Fd fd) = 0;
252 virtual void destroy() = 0;
253 virtual void finish() = 0;
254 virtual void set_actions(uint32_t dnd_actions, uint32_t preferred_action) = 0;
255
256 struct wl_client* const client;
257 struct wl_resource* const resource;
258
259private:
260 static void accept_thunk(struct wl_client*, struct wl_resource* resource, uint32_t serial, char const* mime_type)
261 {
262 auto me = static_cast<DataOffer*>(wl_resource_get_user_data(resource));
263 std::experimental::optional<std::string> mime_type_resolved;
264 if (mime_type != nullptr)
265 {
266 mime_type_resolved = std::experimental::make_optional<std::string>(mime_type);
267 }
268 me->accept(serial, mime_type_resolved);
269 }
270
271 static void receive_thunk(struct wl_client*, struct wl_resource* resource, char const* mime_type, int fd)
272 {
273 auto me = static_cast<DataOffer*>(wl_resource_get_user_data(resource));
274 mir::Fd fd_resolved{fd};
275 me->receive(mime_type, fd_resolved);
276 }
277
278 static void destroy_thunk(struct wl_client*, struct wl_resource* resource)
279 {
280 auto me = static_cast<DataOffer*>(wl_resource_get_user_data(resource));
281 me->destroy();
282 }
283
284 static void finish_thunk(struct wl_client*, struct wl_resource* resource)
285 {
286 auto me = static_cast<DataOffer*>(wl_resource_get_user_data(resource));
287 me->finish();
288 }
289
290 static void set_actions_thunk(struct wl_client*, struct wl_resource* resource, uint32_t dnd_actions, uint32_t preferred_action)
291 {
292 auto me = static_cast<DataOffer*>(wl_resource_get_user_data(resource));
293 me->set_actions(dnd_actions, preferred_action);
294 }
295
296 static struct wl_data_offer_interface const vtable;
297};
298
299struct wl_data_offer_interface const DataOffer::vtable = {
300 accept_thunk,
301 receive_thunk,
302 destroy_thunk,
303 finish_thunk,
304 set_actions_thunk,
305};
306
307
308class DataSource
309{
310protected:
311 DataSource(struct wl_client* client, struct wl_resource* parent, uint32_t id)
312 : client{client},
313 resource{wl_resource_create(client, &wl_data_source_interface, wl_resource_get_version(parent), id)}
314 {
315 if (resource == nullptr)
316 {
317 wl_resource_post_no_memory(parent);
318 BOOST_THROW_EXCEPTION((std::bad_alloc{}));
319 }
320 wl_resource_set_implementation(resource, &vtable, this, nullptr);
321 }
322 virtual ~DataSource() = default;
323
324 virtual void offer(std::string const& mime_type) = 0;
325 virtual void destroy() = 0;
326 virtual void set_actions(uint32_t dnd_actions) = 0;
327
328 struct wl_client* const client;
329 struct wl_resource* const resource;
330
331private:
332 static void offer_thunk(struct wl_client*, struct wl_resource* resource, char const* mime_type)
333 {
334 auto me = static_cast<DataSource*>(wl_resource_get_user_data(resource));
335 me->offer(mime_type);
336 }
337
338 static void destroy_thunk(struct wl_client*, struct wl_resource* resource)
339 {
340 auto me = static_cast<DataSource*>(wl_resource_get_user_data(resource));
341 me->destroy();
342 }
343
344 static void set_actions_thunk(struct wl_client*, struct wl_resource* resource, uint32_t dnd_actions)
345 {
346 auto me = static_cast<DataSource*>(wl_resource_get_user_data(resource));
347 me->set_actions(dnd_actions);
348 }
349
350 static struct wl_data_source_interface const vtable;
351};
352
353struct wl_data_source_interface const DataSource::vtable = {
354 offer_thunk,
355 destroy_thunk,
356 set_actions_thunk,
357};
358
359
360class DataDevice
361{
362protected:
363 DataDevice(struct wl_client* client, struct wl_resource* parent, uint32_t id)
364 : client{client},
365 resource{wl_resource_create(client, &wl_data_device_interface, wl_resource_get_version(parent), id)}
366 {
367 if (resource == nullptr)
368 {
369 wl_resource_post_no_memory(parent);
370 BOOST_THROW_EXCEPTION((std::bad_alloc{}));
371 }
372 wl_resource_set_implementation(resource, &vtable, this, nullptr);
373 }
374 virtual ~DataDevice() = default;
375
376 virtual void start_drag(std::experimental::optional<struct wl_resource*> const& source, struct wl_resource* origin, std::experimental::optional<struct wl_resource*> const& icon, uint32_t serial) = 0;
377 virtual void set_selection(std::experimental::optional<struct wl_resource*> const& source, uint32_t serial) = 0;
378 virtual void release() = 0;
379
380 struct wl_client* const client;
381 struct wl_resource* const resource;
382
383private:
384 static void start_drag_thunk(struct wl_client*, struct wl_resource* resource, struct wl_resource* source, struct wl_resource* origin, struct wl_resource* icon, uint32_t serial)
385 {
386 auto me = static_cast<DataDevice*>(wl_resource_get_user_data(resource));
387 std::experimental::optional<struct wl_resource*> source_resolved;
388 if (source != nullptr)
389 {
390 source_resolved = source;
391 }
392 std::experimental::optional<struct wl_resource*> icon_resolved;
393 if (icon != nullptr)
394 {
395 icon_resolved = icon;
396 }
397 me->start_drag(source_resolved, origin, icon_resolved, serial);
398 }
399
400 static void set_selection_thunk(struct wl_client*, struct wl_resource* resource, struct wl_resource* source, uint32_t serial)
401 {
402 auto me = static_cast<DataDevice*>(wl_resource_get_user_data(resource));
403 std::experimental::optional<struct wl_resource*> source_resolved;
404 if (source != nullptr)
405 {
406 source_resolved = source;
407 }
408 me->set_selection(source_resolved, serial);
409 }
410
411 static void release_thunk(struct wl_client*, struct wl_resource* resource)
412 {
413 auto me = static_cast<DataDevice*>(wl_resource_get_user_data(resource));
414 me->release();
415 }
416
417 static struct wl_data_device_interface const vtable;
418};
419
420struct wl_data_device_interface const DataDevice::vtable = {
421 start_drag_thunk,
422 set_selection_thunk,
423 release_thunk,
424};
425
426
427class DataDeviceManager
428{
429protected:
430 DataDeviceManager(struct wl_display* display, uint32_t max_version)
431 : max_version{max_version}
432 {
433 if (!wl_global_create(display,
434 &wl_data_device_manager_interface, max_version,
435 this, &DataDeviceManager::bind))
436 {
437 BOOST_THROW_EXCEPTION((std::runtime_error{"Failed to export wl_data_device_manager interface"}));
438 }
439 }
440 virtual ~DataDeviceManager() = default;
441
442 virtual void create_data_source(struct wl_client* client, struct wl_resource* resource, uint32_t id) = 0;
443 virtual void get_data_device(struct wl_client* client, struct wl_resource* resource, uint32_t id, struct wl_resource* seat) = 0;
444
445private:
446 static void create_data_source_thunk(struct wl_client* client, struct wl_resource* resource, uint32_t id)
447 {
448 auto me = static_cast<DataDeviceManager*>(wl_resource_get_user_data(resource));
449 me->create_data_source(client, resource, id);
450 }
451
452 static void get_data_device_thunk(struct wl_client* client, struct wl_resource* resource, uint32_t id, struct wl_resource* seat)
453 {
454 auto me = static_cast<DataDeviceManager*>(wl_resource_get_user_data(resource));
455 me->get_data_device(client, resource, id, seat);
456 }
457
458 static void bind(struct wl_client* client, void* data, uint32_t version, uint32_t id)
459 {
460 auto me = static_cast<DataDeviceManager*>(data);
461 auto resource = wl_resource_create(client, &wl_data_device_manager_interface,
462 std::min(version, me->max_version), id);
463 if (resource == nullptr)
464 {
465 wl_client_post_no_memory(client);
466 BOOST_THROW_EXCEPTION((std::bad_alloc{}));
467 }
468 wl_resource_set_implementation(resource, &vtable, me, nullptr);
469 }
470
471 uint32_t const max_version;
472 static struct wl_data_device_manager_interface const vtable;
473};
474
475struct wl_data_device_manager_interface const DataDeviceManager::vtable = {
476 create_data_source_thunk,
477 get_data_device_thunk,
478};
479
480
481class Shell
482{
483protected:
484 Shell(struct wl_display* display, uint32_t max_version)
485 : max_version{max_version}
486 {
487 if (!wl_global_create(display,
488 &wl_shell_interface, max_version,
489 this, &Shell::bind))
490 {
491 BOOST_THROW_EXCEPTION((std::runtime_error{"Failed to export wl_shell interface"}));
492 }
493 }
494 virtual ~Shell() = default;
495
496 virtual void get_shell_surface(struct wl_client* client, struct wl_resource* resource, uint32_t id, struct wl_resource* surface) = 0;
497
498private:
499 static void get_shell_surface_thunk(struct wl_client* client, struct wl_resource* resource, uint32_t id, struct wl_resource* surface)
500 {
501 auto me = static_cast<Shell*>(wl_resource_get_user_data(resource));
502 me->get_shell_surface(client, resource, id, surface);
503 }
504
505 static void bind(struct wl_client* client, void* data, uint32_t version, uint32_t id)
506 {
507 auto me = static_cast<Shell*>(data);
508 auto resource = wl_resource_create(client, &wl_shell_interface,
509 std::min(version, me->max_version), id);
510 if (resource == nullptr)
511 {
512 wl_client_post_no_memory(client);
513 BOOST_THROW_EXCEPTION((std::bad_alloc{}));
514 }
515 wl_resource_set_implementation(resource, &vtable, me, nullptr);
516 }
517
518 uint32_t const max_version;
519 static struct wl_shell_interface const vtable;
520};
521
522struct wl_shell_interface const Shell::vtable = {
523 get_shell_surface_thunk,
524};
525
526
527class ShellSurface
528{
529protected:
530 ShellSurface(struct wl_client* client, struct wl_resource* parent, uint32_t id)
531 : client{client},
532 resource{wl_resource_create(client, &wl_shell_surface_interface, wl_resource_get_version(parent), id)}
533 {
534 if (resource == nullptr)
535 {
536 wl_resource_post_no_memory(parent);
537 BOOST_THROW_EXCEPTION((std::bad_alloc{}));
538 }
539 wl_resource_set_implementation(resource, &vtable, this, nullptr);
540 }
541 virtual ~ShellSurface() = default;
542
543 virtual void pong(uint32_t serial) = 0;
544 virtual void move(struct wl_resource* seat, uint32_t serial) = 0;
545 virtual void resize(struct wl_resource* seat, uint32_t serial, uint32_t edges) = 0;
546 virtual void set_toplevel() = 0;
547 virtual void set_transient(struct wl_resource* parent, int32_t x, int32_t y, uint32_t flags) = 0;
548 virtual void set_fullscreen(uint32_t method, uint32_t framerate, std::experimental::optional<struct wl_resource*> const& output) = 0;
549 virtual void set_popup(struct wl_resource* seat, uint32_t serial, struct wl_resource* parent, int32_t x, int32_t y, uint32_t flags) = 0;
550 virtual void set_maximized(std::experimental::optional<struct wl_resource*> const& output) = 0;
551 virtual void set_title(std::string const& title) = 0;
552 virtual void set_class(std::string const& class_) = 0;
553
554 struct wl_client* const client;
555 struct wl_resource* const resource;
556
557private:
558 static void pong_thunk(struct wl_client*, struct wl_resource* resource, uint32_t serial)
559 {
560 auto me = static_cast<ShellSurface*>(wl_resource_get_user_data(resource));
561 me->pong(serial);
562 }
563
564 static void move_thunk(struct wl_client*, struct wl_resource* resource, struct wl_resource* seat, uint32_t serial)
565 {
566 auto me = static_cast<ShellSurface*>(wl_resource_get_user_data(resource));
567 me->move(seat, serial);
568 }
569
570 static void resize_thunk(struct wl_client*, struct wl_resource* resource, struct wl_resource* seat, uint32_t serial, uint32_t edges)
571 {
572 auto me = static_cast<ShellSurface*>(wl_resource_get_user_data(resource));
573 me->resize(seat, serial, edges);
574 }
575
576 static void set_toplevel_thunk(struct wl_client*, struct wl_resource* resource)
577 {
578 auto me = static_cast<ShellSurface*>(wl_resource_get_user_data(resource));
579 me->set_toplevel();
580 }
581
582 static void set_transient_thunk(struct wl_client*, struct wl_resource* resource, struct wl_resource* parent, int32_t x, int32_t y, uint32_t flags)
583 {
584 auto me = static_cast<ShellSurface*>(wl_resource_get_user_data(resource));
585 me->set_transient(parent, x, y, flags);
586 }
587
588 static void set_fullscreen_thunk(struct wl_client*, struct wl_resource* resource, uint32_t method, uint32_t framerate, struct wl_resource* output)
589 {
590 auto me = static_cast<ShellSurface*>(wl_resource_get_user_data(resource));
591 std::experimental::optional<struct wl_resource*> output_resolved;
592 if (output != nullptr)
593 {
594 output_resolved = output;
595 }
596 me->set_fullscreen(method, framerate, output_resolved);
597 }
598
599 static void set_popup_thunk(struct wl_client*, struct wl_resource* resource, struct wl_resource* seat, uint32_t serial, struct wl_resource* parent, int32_t x, int32_t y, uint32_t flags)
600 {
601 auto me = static_cast<ShellSurface*>(wl_resource_get_user_data(resource));
602 me->set_popup(seat, serial, parent, x, y, flags);
603 }
604
605 static void set_maximized_thunk(struct wl_client*, struct wl_resource* resource, struct wl_resource* output)
606 {
607 auto me = static_cast<ShellSurface*>(wl_resource_get_user_data(resource));
608 std::experimental::optional<struct wl_resource*> output_resolved;
609 if (output != nullptr)
610 {
611 output_resolved = output;
612 }
613 me->set_maximized(output_resolved);
614 }
615
616 static void set_title_thunk(struct wl_client*, struct wl_resource* resource, char const* title)
617 {
618 auto me = static_cast<ShellSurface*>(wl_resource_get_user_data(resource));
619 me->set_title(title);
620 }
621
622 static void set_class_thunk(struct wl_client*, struct wl_resource* resource, char const* class_)
623 {
624 auto me = static_cast<ShellSurface*>(wl_resource_get_user_data(resource));
625 me->set_class(class_);
626 }
627
628 static struct wl_shell_surface_interface const vtable;
629};
630
631struct wl_shell_surface_interface const ShellSurface::vtable = {
632 pong_thunk,
633 move_thunk,
634 resize_thunk,
635 set_toplevel_thunk,
636 set_transient_thunk,
637 set_fullscreen_thunk,
638 set_popup_thunk,
639 set_maximized_thunk,
640 set_title_thunk,
641 set_class_thunk,
642};
643
644
645class Surface
646{
647protected:
648 Surface(struct wl_client* client, struct wl_resource* parent, uint32_t id)
649 : client{client},
650 resource{wl_resource_create(client, &wl_surface_interface, wl_resource_get_version(parent), id)}
651 {
652 if (resource == nullptr)
653 {
654 wl_resource_post_no_memory(parent);
655 BOOST_THROW_EXCEPTION((std::bad_alloc{}));
656 }
657 wl_resource_set_implementation(resource, &vtable, this, nullptr);
658 }
659 virtual ~Surface() = default;
660
661 virtual void destroy() = 0;
662 virtual void attach(std::experimental::optional<struct wl_resource*> const& buffer, int32_t x, int32_t y) = 0;
663 virtual void damage(int32_t x, int32_t y, int32_t width, int32_t height) = 0;
664 virtual void frame(uint32_t callback) = 0;
665 virtual void set_opaque_region(std::experimental::optional<struct wl_resource*> const& region) = 0;
666 virtual void set_input_region(std::experimental::optional<struct wl_resource*> const& region) = 0;
667 virtual void commit() = 0;
668 virtual void set_buffer_transform(int32_t transform) = 0;
669 virtual void set_buffer_scale(int32_t scale) = 0;
670 virtual void damage_buffer(int32_t x, int32_t y, int32_t width, int32_t height) = 0;
671
672 struct wl_client* const client;
673 struct wl_resource* const resource;
674
675private:
676 static void destroy_thunk(struct wl_client*, struct wl_resource* resource)
677 {
678 auto me = static_cast<Surface*>(wl_resource_get_user_data(resource));
679 me->destroy();
680 }
681
682 static void attach_thunk(struct wl_client*, struct wl_resource* resource, struct wl_resource* buffer, int32_t x, int32_t y)
683 {
684 auto me = static_cast<Surface*>(wl_resource_get_user_data(resource));
685 std::experimental::optional<struct wl_resource*> buffer_resolved;
686 if (buffer != nullptr)
687 {
688 buffer_resolved = buffer;
689 }
690 me->attach(buffer_resolved, x, y);
691 }
692
693 static void damage_thunk(struct wl_client*, struct wl_resource* resource, int32_t x, int32_t y, int32_t width, int32_t height)
694 {
695 auto me = static_cast<Surface*>(wl_resource_get_user_data(resource));
696 me->damage(x, y, width, height);
697 }
698
699 static void frame_thunk(struct wl_client*, struct wl_resource* resource, uint32_t callback)
700 {
701 auto me = static_cast<Surface*>(wl_resource_get_user_data(resource));
702 me->frame(callback);
703 }
704
705 static void set_opaque_region_thunk(struct wl_client*, struct wl_resource* resource, struct wl_resource* region)
706 {
707 auto me = static_cast<Surface*>(wl_resource_get_user_data(resource));
708 std::experimental::optional<struct wl_resource*> region_resolved;
709 if (region != nullptr)
710 {
711 region_resolved = region;
712 }
713 me->set_opaque_region(region_resolved);
714 }
715
716 static void set_input_region_thunk(struct wl_client*, struct wl_resource* resource, struct wl_resource* region)
717 {
718 auto me = static_cast<Surface*>(wl_resource_get_user_data(resource));
719 std::experimental::optional<struct wl_resource*> region_resolved;
720 if (region != nullptr)
721 {
722 region_resolved = region;
723 }
724 me->set_input_region(region_resolved);
725 }
726
727 static void commit_thunk(struct wl_client*, struct wl_resource* resource)
728 {
729 auto me = static_cast<Surface*>(wl_resource_get_user_data(resource));
730 me->commit();
731 }
732
733 static void set_buffer_transform_thunk(struct wl_client*, struct wl_resource* resource, int32_t transform)
734 {
735 auto me = static_cast<Surface*>(wl_resource_get_user_data(resource));
736 me->set_buffer_transform(transform);
737 }
738
739 static void set_buffer_scale_thunk(struct wl_client*, struct wl_resource* resource, int32_t scale)
740 {
741 auto me = static_cast<Surface*>(wl_resource_get_user_data(resource));
742 me->set_buffer_scale(scale);
743 }
744
745 static void damage_buffer_thunk(struct wl_client*, struct wl_resource* resource, int32_t x, int32_t y, int32_t width, int32_t height)
746 {
747 auto me = static_cast<Surface*>(wl_resource_get_user_data(resource));
748 me->damage_buffer(x, y, width, height);
749 }
750
751 static struct wl_surface_interface const vtable;
752};
753
754struct wl_surface_interface const Surface::vtable = {
755 destroy_thunk,
756 attach_thunk,
757 damage_thunk,
758 frame_thunk,
759 set_opaque_region_thunk,
760 set_input_region_thunk,
761 commit_thunk,
762 set_buffer_transform_thunk,
763 set_buffer_scale_thunk,
764 damage_buffer_thunk,
765};
766
767
768class Seat
769{
770protected:
771 Seat(struct wl_display* display, uint32_t max_version)
772 : max_version{max_version}
773 {
774 if (!wl_global_create(display,
775 &wl_seat_interface, max_version,
776 this, &Seat::bind))
777 {
778 BOOST_THROW_EXCEPTION((std::runtime_error{"Failed to export wl_seat interface"}));
779 }
780 }
781 virtual ~Seat() = default;
782
783 virtual void get_pointer(struct wl_client* client, struct wl_resource* resource, uint32_t id) = 0;
784 virtual void get_keyboard(struct wl_client* client, struct wl_resource* resource, uint32_t id) = 0;
785 virtual void get_touch(struct wl_client* client, struct wl_resource* resource, uint32_t id) = 0;
786 virtual void release(struct wl_client* client, struct wl_resource* resource) = 0;
787
788private:
789 static void get_pointer_thunk(struct wl_client* client, struct wl_resource* resource, uint32_t id)
790 {
791 auto me = static_cast<Seat*>(wl_resource_get_user_data(resource));
792 me->get_pointer(client, resource, id);
793 }
794
795 static void get_keyboard_thunk(struct wl_client* client, struct wl_resource* resource, uint32_t id)
796 {
797 auto me = static_cast<Seat*>(wl_resource_get_user_data(resource));
798 me->get_keyboard(client, resource, id);
799 }
800
801 static void get_touch_thunk(struct wl_client* client, struct wl_resource* resource, uint32_t id)
802 {
803 auto me = static_cast<Seat*>(wl_resource_get_user_data(resource));
804 me->get_touch(client, resource, id);
805 }
806
807 static void release_thunk(struct wl_client* client, struct wl_resource* resource)
808 {
809 auto me = static_cast<Seat*>(wl_resource_get_user_data(resource));
810 me->release(client, resource);
811 }
812
813 static void bind(struct wl_client* client, void* data, uint32_t version, uint32_t id)
814 {
815 auto me = static_cast<Seat*>(data);
816 auto resource = wl_resource_create(client, &wl_seat_interface,
817 std::min(version, me->max_version), id);
818 if (resource == nullptr)
819 {
820 wl_client_post_no_memory(client);
821 BOOST_THROW_EXCEPTION((std::bad_alloc{}));
822 }
823 wl_resource_set_implementation(resource, &vtable, me, nullptr);
824 }
825
826 uint32_t const max_version;
827 static struct wl_seat_interface const vtable;
828};
829
830struct wl_seat_interface const Seat::vtable = {
831 get_pointer_thunk,
832 get_keyboard_thunk,
833 get_touch_thunk,
834 release_thunk,
835};
836
837
838class Pointer
839{
840protected:
841 Pointer(struct wl_client* client, struct wl_resource* parent, uint32_t id)
842 : client{client},
843 resource{wl_resource_create(client, &wl_pointer_interface, wl_resource_get_version(parent), id)}
844 {
845 if (resource == nullptr)
846 {
847 wl_resource_post_no_memory(parent);
848 BOOST_THROW_EXCEPTION((std::bad_alloc{}));
849 }
850 wl_resource_set_implementation(resource, &vtable, this, nullptr);
851 }
852 virtual ~Pointer() = default;
853
854 virtual void set_cursor(uint32_t serial, std::experimental::optional<struct wl_resource*> const& surface, int32_t hotspot_x, int32_t hotspot_y) = 0;
855 virtual void release() = 0;
856
857 struct wl_client* const client;
858 struct wl_resource* const resource;
859
860private:
861 static void set_cursor_thunk(struct wl_client*, struct wl_resource* resource, uint32_t serial, struct wl_resource* surface, int32_t hotspot_x, int32_t hotspot_y)
862 {
863 auto me = static_cast<Pointer*>(wl_resource_get_user_data(resource));
864 std::experimental::optional<struct wl_resource*> surface_resolved;
865 if (surface != nullptr)
866 {
867 surface_resolved = surface;
868 }
869 me->set_cursor(serial, surface_resolved, hotspot_x, hotspot_y);
870 }
871
872 static void release_thunk(struct wl_client*, struct wl_resource* resource)
873 {
874 auto me = static_cast<Pointer*>(wl_resource_get_user_data(resource));
875 me->release();
876 }
877
878 static struct wl_pointer_interface const vtable;
879};
880
881struct wl_pointer_interface const Pointer::vtable = {
882 set_cursor_thunk,
883 release_thunk,
884};
885
886
887class Keyboard
888{
889protected:
890 Keyboard(struct wl_client* client, struct wl_resource* parent, uint32_t id)
891 : client{client},
892 resource{wl_resource_create(client, &wl_keyboard_interface, wl_resource_get_version(parent), id)}
893 {
894 if (resource == nullptr)
895 {
896 wl_resource_post_no_memory(parent);
897 BOOST_THROW_EXCEPTION((std::bad_alloc{}));
898 }
899 wl_resource_set_implementation(resource, &vtable, this, nullptr);
900 }
901 virtual ~Keyboard() = default;
902
903 virtual void release() = 0;
904
905 struct wl_client* const client;
906 struct wl_resource* const resource;
907
908private:
909 static void release_thunk(struct wl_client*, struct wl_resource* resource)
910 {
911 auto me = static_cast<Keyboard*>(wl_resource_get_user_data(resource));
912 me->release();
913 }
914
915 static struct wl_keyboard_interface const vtable;
916};
917
918struct wl_keyboard_interface const Keyboard::vtable = {
919 release_thunk,
920};
921
922
923class Touch
924{
925protected:
926 Touch(struct wl_client* client, struct wl_resource* parent, uint32_t id)
927 : client{client},
928 resource{wl_resource_create(client, &wl_touch_interface, wl_resource_get_version(parent), id)}
929 {
930 if (resource == nullptr)
931 {
932 wl_resource_post_no_memory(parent);
933 BOOST_THROW_EXCEPTION((std::bad_alloc{}));
934 }
935 wl_resource_set_implementation(resource, &vtable, this, nullptr);
936 }
937 virtual ~Touch() = default;
938
939 virtual void release() = 0;
940
941 struct wl_client* const client;
942 struct wl_resource* const resource;
943
944private:
945 static void release_thunk(struct wl_client*, struct wl_resource* resource)
946 {
947 auto me = static_cast<Touch*>(wl_resource_get_user_data(resource));
948 me->release();
949 }
950
951 static struct wl_touch_interface const vtable;
952};
953
954struct wl_touch_interface const Touch::vtable = {
955 release_thunk,
956};
957
958
959class Output
960{
961protected:
962 Output(struct wl_display* display, uint32_t max_version)
963 : max_version{max_version}
964 {
965 if (!wl_global_create(display,
966 &wl_output_interface, max_version,
967 this, &Output::bind))
968 {
969 BOOST_THROW_EXCEPTION((std::runtime_error{"Failed to export wl_output interface"}));
970 }
971 }
972 virtual ~Output() = default;
973
974 virtual void release(struct wl_client* client, struct wl_resource* resource) = 0;
975
976private:
977 static void release_thunk(struct wl_client* client, struct wl_resource* resource)
978 {
979 auto me = static_cast<Output*>(wl_resource_get_user_data(resource));
980 me->release(client, resource);
981 }
982
983 static void bind(struct wl_client* client, void* data, uint32_t version, uint32_t id)
984 {
985 auto me = static_cast<Output*>(data);
986 auto resource = wl_resource_create(client, &wl_output_interface,
987 std::min(version, me->max_version), id);
988 if (resource == nullptr)
989 {
990 wl_client_post_no_memory(client);
991 BOOST_THROW_EXCEPTION((std::bad_alloc{}));
992 }
993 wl_resource_set_implementation(resource, &vtable, me, nullptr);
994 }
995
996 uint32_t const max_version;
997 static struct wl_output_interface const vtable;
998};
999
1000struct wl_output_interface const Output::vtable = {
1001 release_thunk,
1002};
1003
1004
1005class Region
1006{
1007protected:
1008 Region(struct wl_client* client, struct wl_resource* parent, uint32_t id)
1009 : client{client},
1010 resource{wl_resource_create(client, &wl_region_interface, wl_resource_get_version(parent), id)}
1011 {
1012 if (resource == nullptr)
1013 {
1014 wl_resource_post_no_memory(parent);
1015 BOOST_THROW_EXCEPTION((std::bad_alloc{}));
1016 }
1017 wl_resource_set_implementation(resource, &vtable, this, nullptr);
1018 }
1019 virtual ~Region() = default;
1020
1021 virtual void destroy() = 0;
1022 virtual void add(int32_t x, int32_t y, int32_t width, int32_t height) = 0;
1023 virtual void subtract(int32_t x, int32_t y, int32_t width, int32_t height) = 0;
1024
1025 struct wl_client* const client;
1026 struct wl_resource* const resource;
1027
1028private:
1029 static void destroy_thunk(struct wl_client*, struct wl_resource* resource)
1030 {
1031 auto me = static_cast<Region*>(wl_resource_get_user_data(resource));
1032 me->destroy();
1033 }
1034
1035 static void add_thunk(struct wl_client*, struct wl_resource* resource, int32_t x, int32_t y, int32_t width, int32_t height)
1036 {
1037 auto me = static_cast<Region*>(wl_resource_get_user_data(resource));
1038 me->add(x, y, width, height);
1039 }
1040
1041 static void subtract_thunk(struct wl_client*, struct wl_resource* resource, int32_t x, int32_t y, int32_t width, int32_t height)
1042 {
1043 auto me = static_cast<Region*>(wl_resource_get_user_data(resource));
1044 me->subtract(x, y, width, height);
1045 }
1046
1047 static struct wl_region_interface const vtable;
1048};
1049
1050struct wl_region_interface const Region::vtable = {
1051 destroy_thunk,
1052 add_thunk,
1053 subtract_thunk,
1054};
1055
1056
1057class Subcompositor
1058{
1059protected:
1060 Subcompositor(struct wl_display* display, uint32_t max_version)
1061 : max_version{max_version}
1062 {
1063 if (!wl_global_create(display,
1064 &wl_subcompositor_interface, max_version,
1065 this, &Subcompositor::bind))
1066 {
1067 BOOST_THROW_EXCEPTION((std::runtime_error{"Failed to export wl_subcompositor interface"}));
1068 }
1069 }
1070 virtual ~Subcompositor() = default;
1071
1072 virtual void destroy(struct wl_client* client, struct wl_resource* resource) = 0;
1073 virtual void get_subsurface(struct wl_client* client, struct wl_resource* resource, uint32_t id, struct wl_resource* surface, struct wl_resource* parent) = 0;
1074
1075private:
1076 static void destroy_thunk(struct wl_client* client, struct wl_resource* resource)
1077 {
1078 auto me = static_cast<Subcompositor*>(wl_resource_get_user_data(resource));
1079 me->destroy(client, resource);
1080 }
1081
1082 static void get_subsurface_thunk(struct wl_client* client, struct wl_resource* resource, uint32_t id, struct wl_resource* surface, struct wl_resource* parent)
1083 {
1084 auto me = static_cast<Subcompositor*>(wl_resource_get_user_data(resource));
1085 me->get_subsurface(client, resource, id, surface, parent);
1086 }
1087
1088 static void bind(struct wl_client* client, void* data, uint32_t version, uint32_t id)
1089 {
1090 auto me = static_cast<Subcompositor*>(data);
1091 auto resource = wl_resource_create(client, &wl_subcompositor_interface,
1092 std::min(version, me->max_version), id);
1093 if (resource == nullptr)
1094 {
1095 wl_client_post_no_memory(client);
1096 BOOST_THROW_EXCEPTION((std::bad_alloc{}));
1097 }
1098 wl_resource_set_implementation(resource, &vtable, me, nullptr);
1099 }
1100
1101 uint32_t const max_version;
1102 static struct wl_subcompositor_interface const vtable;
1103};
1104
1105struct wl_subcompositor_interface const Subcompositor::vtable = {
1106 destroy_thunk,
1107 get_subsurface_thunk,
1108};
1109
1110
1111class Subsurface
1112{
1113protected:
1114 Subsurface(struct wl_client* client, struct wl_resource* parent, uint32_t id)
1115 : client{client},
1116 resource{wl_resource_create(client, &wl_subsurface_interface, wl_resource_get_version(parent), id)}
1117 {
1118 if (resource == nullptr)
1119 {
1120 wl_resource_post_no_memory(parent);
1121 BOOST_THROW_EXCEPTION((std::bad_alloc{}));
1122 }
1123 wl_resource_set_implementation(resource, &vtable, this, nullptr);
1124 }
1125 virtual ~Subsurface() = default;
1126
1127 virtual void destroy() = 0;
1128 virtual void set_position(int32_t x, int32_t y) = 0;
1129 virtual void place_above(struct wl_resource* sibling) = 0;
1130 virtual void place_below(struct wl_resource* sibling) = 0;
1131 virtual void set_sync() = 0;
1132 virtual void set_desync() = 0;
1133
1134 struct wl_client* const client;
1135 struct wl_resource* const resource;
1136
1137private:
1138 static void destroy_thunk(struct wl_client*, struct wl_resource* resource)
1139 {
1140 auto me = static_cast<Subsurface*>(wl_resource_get_user_data(resource));
1141 me->destroy();
1142 }
1143
1144 static void set_position_thunk(struct wl_client*, struct wl_resource* resource, int32_t x, int32_t y)
1145 {
1146 auto me = static_cast<Subsurface*>(wl_resource_get_user_data(resource));
1147 me->set_position(x, y);
1148 }
1149
1150 static void place_above_thunk(struct wl_client*, struct wl_resource* resource, struct wl_resource* sibling)
1151 {
1152 auto me = static_cast<Subsurface*>(wl_resource_get_user_data(resource));
1153 me->place_above(sibling);
1154 }
1155
1156 static void place_below_thunk(struct wl_client*, struct wl_resource* resource, struct wl_resource* sibling)
1157 {
1158 auto me = static_cast<Subsurface*>(wl_resource_get_user_data(resource));
1159 me->place_below(sibling);
1160 }
1161
1162 static void set_sync_thunk(struct wl_client*, struct wl_resource* resource)
1163 {
1164 auto me = static_cast<Subsurface*>(wl_resource_get_user_data(resource));
1165 me->set_sync();
1166 }
1167
1168 static void set_desync_thunk(struct wl_client*, struct wl_resource* resource)
1169 {
1170 auto me = static_cast<Subsurface*>(wl_resource_get_user_data(resource));
1171 me->set_desync();
1172 }
1173
1174 static struct wl_subsurface_interface const vtable;
1175};
1176
1177struct wl_subsurface_interface const Subsurface::vtable = {
1178 destroy_thunk,
1179 set_position_thunk,
1180 place_above_thunk,
1181 place_below_thunk,
1182 set_sync_thunk,
1183 set_desync_thunk,
1184};
1185
1186
1187}
1188}
1189}
01190
=== added file 'src/server/frontend/wayland/wayland_connector.cpp'
--- src/server/frontend/wayland/wayland_connector.cpp 1970-01-01 00:00:00 +0000
+++ src/server/frontend/wayland/wayland_connector.cpp 2017-09-07 05:58:57 +0000
@@ -0,0 +1,1785 @@
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 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: Christopher James Halse Rogers <christopher.halse.rogers@canonical.com>
17 */
18
19#include "wayland_connector.h"
20
21#include "core_generated_interfaces.h"
22
23#include "mir/frontend/shell.h"
24
25#include "mir/compositor/buffer_stream.h"
26
27#include "mir/frontend/session.h"
28#include "mir/frontend/event_sink.h"
29#include "mir/scene/surface_creation_parameters.h"
30
31#include "mir/graphics/buffer_properties.h"
32#include "mir/graphics/buffer.h"
33#include "mir/graphics/display_configuration.h"
34#include "mir/graphics/graphic_buffer_allocator.h"
35#include "mir/graphics/wayland_allocator.h"
36
37#include "mir/renderer/gl/texture_target.h"
38#include "mir/frontend/buffer_stream_id.h"
39#include "mir/frontend/display_changer.h"
40
41#include "mir/executor.h"
42
43#include <system_error>
44#include <sys/eventfd.h>
45#include <wayland-server-core.h>
46#include <unordered_map>
47#include <boost/throw_exception.hpp>
48
49#include <future>
50#include <functional>
51#include <type_traits>
52
53#include <algorithm>
54#include <iostream>
55#include <mir/log.h>
56#include <cstring>
57#include <deque>
58#include MIR_SERVER_GL_H
59#include MIR_SERVER_GLEXT_H
60
61#include "mir/fd.h"
62#include "../../../platforms/common/server/shm_buffer.h"
63
64namespace mf = mir::frontend;
65namespace mg = mir::graphics;
66namespace mc = mir::compositor;
67namespace ms = mir::scene;
68namespace geom = mir::geometry;
69
70namespace mir
71{
72namespace frontend
73{
74
75class WaylandEventSink : public mf::EventSink
76{
77public:
78 WaylandEventSink(std::function<void(MirLifecycleState)> const& lifecycle_handler)
79 : lifecycle_handler{lifecycle_handler}
80 {
81 }
82
83 void handle_event(const MirEvent& e) override;
84 void handle_lifecycle_event(MirLifecycleState state) override;
85 void handle_display_config_change(graphics::DisplayConfiguration const& config) override;
86 void send_ping(int32_t serial) override;
87 void send_buffer(BufferStreamId id, graphics::Buffer& buffer, graphics::BufferIpcMsgType type) override;
88 void handle_input_config_change(MirInputConfig const&) override {}
89 void handle_error(ClientVisibleError const&) override {}
90
91 void add_buffer(graphics::Buffer&) override {}
92 void error_buffer(geometry::Size, MirPixelFormat, std::string const& ) override {}
93 void update_buffer(graphics::Buffer&) override {}
94
95private:
96 std::function<void(MirLifecycleState)> const lifecycle_handler;
97};
98
99namespace
100{
101bool get_gl_pixel_format(
102 MirPixelFormat mir_format,
103 GLenum& gl_format,
104 GLenum& gl_type)
105{
106#if __BYTE_ORDER == __LITTLE_ENDIAN
107 GLenum const argb = GL_BGRA_EXT;
108 GLenum const abgr = GL_RGBA;
109#elif __BYTE_ORDER == __BIG_ENDIAN
110 // TODO: Big endian support
111GLenum const argb = GL_INVALID_ENUM;
112GLenum const abgr = GL_INVALID_ENUM;
113//GLenum const rgba = GL_RGBA;
114//GLenum const bgra = GL_BGRA_EXT;
115#endif
116
117 static const struct
118 {
119 MirPixelFormat mir_format;
120 GLenum gl_format, gl_type;
121 } mapping[mir_pixel_formats] =
122 {
123 {mir_pixel_format_invalid, GL_INVALID_ENUM, GL_INVALID_ENUM},
124 {mir_pixel_format_abgr_8888, abgr, GL_UNSIGNED_BYTE},
125 {mir_pixel_format_xbgr_8888, abgr, GL_UNSIGNED_BYTE},
126 {mir_pixel_format_argb_8888, argb, GL_UNSIGNED_BYTE},
127 {mir_pixel_format_xrgb_8888, argb, GL_UNSIGNED_BYTE},
128 {mir_pixel_format_bgr_888, GL_INVALID_ENUM, GL_INVALID_ENUM},
129 {mir_pixel_format_rgb_888, GL_RGB, GL_UNSIGNED_BYTE},
130 {mir_pixel_format_rgb_565, GL_RGB, GL_UNSIGNED_SHORT_5_6_5},
131 {mir_pixel_format_rgba_5551, GL_RGBA, GL_UNSIGNED_SHORT_5_5_5_1},
132 {mir_pixel_format_rgba_4444, GL_RGBA, GL_UNSIGNED_SHORT_4_4_4_4},
133 };
134
135 if (mir_format > mir_pixel_format_invalid &&
136 mir_format < mir_pixel_formats &&
137 mapping[mir_format].mir_format == mir_format) // just a sanity check
138 {
139 gl_format = mapping[mir_format].gl_format;
140 gl_type = mapping[mir_format].gl_type;
141 }
142 else
143 {
144 gl_format = GL_INVALID_ENUM;
145 gl_type = GL_INVALID_ENUM;
146 }
147
148 return gl_format != GL_INVALID_ENUM && gl_type != GL_INVALID_ENUM;
149}
150
151
152struct ClientPrivate
153{
154 wl_listener destroy_listener;
155 std::shared_ptr<mf::Session> session;
156};
157
158static_assert(
159 std::is_standard_layout<ClientPrivate>::value,
160 "ClientPrivate must be standard layout for wl_container_of to be defined behaviour");
161
162
163ClientPrivate* private_from_listener(wl_listener* listener)
164{
165 ClientPrivate* userdata;
166 return wl_container_of(listener, userdata, destroy_listener);
167}
168
169void cleanup_private(wl_listener* listener, void* /*data*/)
170{
171 delete private_from_listener(listener);
172}
173
174std::shared_ptr<mf::Session> session_for_client(wl_client* client)
175{
176 auto listener = wl_client_get_destroy_listener(client, &cleanup_private);
177
178 assert(listener && "Client session requested for malformed client");
179
180 return private_from_listener(listener)->session;
181}
182
183struct ClientSessionConstructor
184{
185 ClientSessionConstructor(std::shared_ptr<mf::Shell> const& shell)
186 : shell{shell}
187 {
188 }
189
190 wl_listener construction_listener;
191 wl_listener destruction_listener;
192 std::shared_ptr<mf::Shell> const shell;
193};
194
195static_assert(
196 std::is_standard_layout<ClientSessionConstructor>::value,
197 "ClientSessionConstructor must be standard layout for wl_container_of to be "
198 "defined behaviour.");
199
200void create_client_session(wl_listener* listener, void* data)
201{
202 auto client = reinterpret_cast<wl_client*>(data);
203
204 ClientSessionConstructor* construction_context;
205 construction_context =
206 wl_container_of(listener, construction_context, construction_listener);
207
208 pid_t client_pid;
209 wl_client_get_credentials(client, &client_pid, nullptr, nullptr);
210
211 auto session = construction_context->shell->open_session(
212 client_pid,
213 "",
214 std::make_shared<WaylandEventSink>([](auto){}));
215
216 auto client_context = new ClientPrivate;
217 client_context->destroy_listener.notify = &cleanup_private;
218 client_context->session = session;
219 wl_client_add_destroy_listener(client, &client_context->destroy_listener);
220}
221
222void cleanup_client_handler(wl_listener* listener, void*)
223{
224 ClientSessionConstructor* construction_context;
225 construction_context = wl_container_of(listener, construction_context, destruction_listener);
226
227 delete construction_context;
228}
229
230void setup_new_client_handler(wl_display* display, std::shared_ptr<mf::Shell> const& shell)
231{
232 auto context = new ClientSessionConstructor{shell};
233 context->construction_listener.notify = &create_client_session;
234
235 wl_display_add_client_created_listener(display, &context->construction_listener);
236
237 context->destruction_listener.notify = &cleanup_client_handler;
238 wl_display_add_destroy_listener(display, &context->destruction_listener);
239}
240
241/*
242std::shared_ptr<mf::BufferStream> create_buffer_stream(mf::Session& session)
243{
244 mg::BufferProperties const props{
245 geom::Size{geom::Width{0}, geom::Height{0}},
246 mir_pixel_format_invalid,
247 mg::BufferUsage::undefined
248 };
249
250 auto const id = session.create_buffer_stream(props);
251 return session.get_buffer_stream(id);
252}
253*/
254MirPixelFormat wl_format_to_mir_format(uint32_t format)
255{
256 switch (format)
257 {
258 case WL_SHM_FORMAT_ARGB8888:
259 return mir_pixel_format_argb_8888;
260 case WL_SHM_FORMAT_XRGB8888:
261 return mir_pixel_format_xrgb_8888;
262 case WL_SHM_FORMAT_RGBA4444:
263 return mir_pixel_format_rgba_4444;
264 case WL_SHM_FORMAT_RGBA5551:
265 return mir_pixel_format_rgba_5551;
266 case WL_SHM_FORMAT_RGB565:
267 return mir_pixel_format_rgb_565;
268 case WL_SHM_FORMAT_RGB888:
269 return mir_pixel_format_rgb_888;
270 case WL_SHM_FORMAT_BGR888:
271 return mir_pixel_format_bgr_888;
272 case WL_SHM_FORMAT_XBGR8888:
273 return mir_pixel_format_xbgr_8888;
274 case WL_SHM_FORMAT_ABGR8888:
275 return mir_pixel_format_abgr_8888;
276 default:
277 return mir_pixel_format_invalid;
278 }
279}
280
281wl_shm_buffer* shm_buffer_from_resource_checked(wl_resource* resource)
282{
283 auto const buffer = wl_shm_buffer_get(resource);
284 if (!buffer)
285 {
286 BOOST_THROW_EXCEPTION((std::logic_error{"Tried to create WlShmBuffer from non-shm resource"}));
287 }
288
289 return buffer;
290}
291}
292
293class WlShmBuffer :
294 public mg::BufferBasic,
295 public mg::NativeBufferBase,
296 public mir::renderer::gl::TextureSource,
297 public mir::renderer::software::PixelSource
298{
299public:
300 ~WlShmBuffer()
301 {
302 std::lock_guard<std::mutex> lock{*buffer_mutex};
303 if (buffer)
304 {
305 wl_resource_queue_event(resource, WL_BUFFER_RELEASE);
306 }
307 }
308
309 static std::shared_ptr<graphics::Buffer> mir_buffer_from_wl_buffer(
310 wl_resource* buffer,
311 std::function<void()>&& on_consumed)
312 {
313 std::shared_ptr<WlShmBuffer> mir_buffer;
314 DestructionShim* shim;
315
316 if (auto notifier = wl_resource_get_destroy_listener(buffer, &on_buffer_destroyed))
317 {
318 // We've already constructed a shim for this buffer, update it.
319 shim = wl_container_of(notifier, shim, destruction_listener);
320
321 if (!(mir_buffer = shim->associated_buffer.lock()))
322 {
323 /*
324 * We've seen this wl_buffer before, but all the WlShmBuffers associated with it
325 * have been destroyed.
326 *
327 * Recreate a new WlShmBuffer to track the new compositor lifetime.
328 */
329 mir_buffer = std::shared_ptr<WlShmBuffer>{new WlShmBuffer{buffer, std::move(on_consumed)}};
330 shim->associated_buffer = mir_buffer;
331 }
332 }
333 else
334 {
335 mir_buffer = std::shared_ptr<WlShmBuffer>{new WlShmBuffer{buffer, std::move(on_consumed)}};
336 shim = new DestructionShim;
337 shim->destruction_listener.notify = &on_buffer_destroyed;
338 shim->associated_buffer = mir_buffer;
339
340 wl_resource_add_destroy_listener(buffer, &shim->destruction_listener);
341 }
342
343 mir_buffer->buffer_mutex = shim->mutex;
344 return mir_buffer;
345 }
346
347 std::shared_ptr<graphics::NativeBuffer> native_buffer_handle() const override
348 {
349 return nullptr;
350 }
351
352 geometry::Size size() const override
353 {
354 return size_;
355 }
356
357 MirPixelFormat pixel_format() const override
358 {
359 return format_;
360 }
361
362 graphics::NativeBufferBase *native_buffer_base() override
363 {
364 return this;
365 }
366
367 void gl_bind_to_texture() override
368 {
369 GLenum format, type;
370
371 if (get_gl_pixel_format(
372 format_,
373 format,
374 type))
375 {
376 /*
377 * All existing Mir logic assumes that strides are whole multiples of
378 * pixels. And OpenGL defaults to expecting strides are multiples of
379 * 4 bytes. These assumptions used to be compatible when we only had
380 * 4-byte pixels but now we support 2/3-byte pixels we need to be more
381 * careful...
382 */
383 glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
384
385 read(
386 [this, format, type](unsigned char const* pixels)
387 {
388 auto const size = this->size();
389 glTexImage2D(GL_TEXTURE_2D, 0, format,
390 size.width.as_int(), size.height.as_int(),
391 0, format, type, pixels);
392 });
393 }
394 }
395
396 void bind() override
397 {
398 gl_bind_to_texture();
399 }
400
401 void secure_for_render() override
402 {
403 }
404
405 void write(unsigned char const *pixels, size_t size) override
406 {
407 std::lock_guard<std::mutex> lock{*buffer_mutex};
408 wl_shm_buffer_begin_access(buffer);
409 auto data = wl_shm_buffer_get_data(buffer);
410 ::memcpy(data, pixels, size);
411 wl_shm_buffer_end_access(buffer);
412 }
413
414 void read(std::function<void(unsigned char const *)> const &do_with_pixels) override
415 {
416 std::lock_guard<std::mutex> lock{*buffer_mutex};
417 if (!buffer)
418 {
419 mir::log_warning("Attempt to read from WlShmBuffer after the wl_buffer has been destroyed");
420 return;
421 }
422
423 if (!consumed)
424 {
425 on_consumed();
426 consumed = true;
427 }
428
429 wl_shm_buffer_begin_access(buffer);
430 auto data = wl_shm_buffer_get_data(buffer);
431 do_with_pixels(static_cast<unsigned char const*>(data));
432 wl_shm_buffer_end_access(buffer);
433 }
434
435 geometry::Stride stride() const override
436 {
437 return stride_;
438 }
439
440private:
441 WlShmBuffer(
442 wl_resource* buffer,
443 std::function<void()>&& on_consumed)
444 : buffer{shm_buffer_from_resource_checked(buffer)},
445 resource{buffer},
446 size_{wl_shm_buffer_get_width(this->buffer), wl_shm_buffer_get_height(this->buffer)},
447 stride_{wl_shm_buffer_get_stride(this->buffer)},
448 format_{wl_format_to_mir_format(wl_shm_buffer_get_format(this->buffer))},
449 consumed{false},
450 on_consumed{std::move(on_consumed)}
451 {
452 }
453
454 static void on_buffer_destroyed(wl_listener* listener, void*)
455 {
456 static_assert(
457 std::is_standard_layout<DestructionShim>::value,
458 "DestructionShim must be Standard Layout for wl_container_of to be defined behaviour");
459
460 DestructionShim* shim;
461 shim = wl_container_of(listener, shim, destruction_listener);
462
463 {
464 std::lock_guard<std::mutex> lock{*shim->mutex};
465 if (auto mir_buffer = shim->associated_buffer.lock())
466 {
467 mir_buffer->buffer = nullptr;
468 }
469 }
470
471 delete shim;
472 }
473
474 struct DestructionShim
475 {
476 std::shared_ptr<std::mutex> const mutex = std::make_shared<std::mutex>();
477 std::weak_ptr<WlShmBuffer> associated_buffer;
478 wl_listener destruction_listener;
479 };
480
481 std::shared_ptr<std::mutex> buffer_mutex;
482
483 wl_shm_buffer* buffer;
484 wl_resource* const resource;
485
486 geom::Size const size_;
487 geom::Stride const stride_;
488 MirPixelFormat const format_;
489
490 bool consumed;
491 std::function<void()> on_consumed;
492};
493
494class WlSurface : public wayland::Surface
495{
496public:
497 WlSurface(
498 wl_client* client,
499 wl_resource* parent,
500 uint32_t id,
501 std::shared_ptr<mir::Executor> const& executor,
502 std::shared_ptr<mg::WaylandAllocator> const& allocator)
503 : Surface(client, parent, id),
504 allocator{allocator},
505 executor{executor},
506 pending_buffer{nullptr},
507 pending_frames{std::make_shared<std::vector<wl_resource*>>()}
508 {
509 auto session = session_for_client(client);
510 mg::BufferProperties const props{
511 geom::Size{geom::Width{0}, geom::Height{0}},
512 mir_pixel_format_invalid,
513 mg::BufferUsage::undefined
514 };
515
516 stream_id = session->create_buffer_stream(props);
517 stream = session->get_buffer_stream(stream_id);
518
519 // wl_surface is specified to act in mailbox mode
520 stream->allow_framedropping(true);
521 }
522
523 void set_resize_handler(std::function<void(geom::Size)> const& handler)
524 {
525 resize_handler = handler;
526 }
527
528 void set_hide_handler(std::function<void()> const& handler)
529 {
530 hide_handler = handler;
531 }
532
533 mf::BufferStreamId stream_id;
534 std::shared_ptr<mf::BufferStream> stream;
535private:
536 std::shared_ptr<mg::WaylandAllocator> const allocator;
537 std::shared_ptr<mir::Executor> const executor;
538
539 std::function<void(geom::Size)> resize_handler;
540 std::function<void()> hide_handler;
541
542 wl_resource* pending_buffer;
543 std::shared_ptr<std::vector<wl_resource*>> const pending_frames;
544
545 void destroy();
546 void attach(std::experimental::optional<wl_resource*> const& buffer, int32_t x, int32_t y);
547 void damage(int32_t x, int32_t y, int32_t width, int32_t height);
548 void frame(uint32_t callback);
549 void set_opaque_region(std::experimental::optional<wl_resource*> const& region);
550 void set_input_region(std::experimental::optional<wl_resource*> const& region);
551 void commit();
552 void damage_buffer(int32_t x, int32_t y, int32_t width, int32_t height);
553 void set_buffer_transform(int32_t transform);
554 void set_buffer_scale(int32_t scale);
555};
556
557void WlSurface::destroy()
558{
559 delete this;
560}
561
562void WlSurface::attach(std::experimental::optional<wl_resource*> const& buffer, int32_t x, int32_t y)
563{
564 if (x != 0 || y != 0)
565 {
566 mir::log_warning("Client requested unimplemented non-zero attach offset. Rendering will be incorrect.");
567 }
568
569 if(!buffer && hide_handler)
570 {
571 hide_handler();
572 }
573
574 pending_buffer = *buffer;
575}
576
577void WlSurface::damage(int32_t x, int32_t y, int32_t width, int32_t height)
578{
579 (void)x;
580 (void)y;
581 (void)width;
582 (void)height;
583}
584
585void WlSurface::damage_buffer(int32_t x, int32_t y, int32_t width, int32_t height)
586{
587 (void)x;
588 (void)y;
589 (void)width;
590 (void)height;
591}
592
593void WlSurface::frame(uint32_t callback)
594{
595 pending_frames->emplace_back(
596 wl_resource_create(client, &wl_callback_interface, 1, callback));
597}
598
599void WlSurface::set_opaque_region(const std::experimental::optional<wl_resource*>& region)
600{
601 (void)region;
602}
603
604void WlSurface::set_input_region(const std::experimental::optional<wl_resource*>& region)
605{
606 (void)region;
607}
608
609void WlSurface::commit()
610{
611 if (pending_buffer)
612 {
613 std::shared_ptr<mg::Buffer> mir_buffer;
614 auto shm_buffer = wl_shm_buffer_get(pending_buffer);
615 auto send_frame_notifications =
616 [executor = executor, frames = pending_frames]()
617 {
618 executor->spawn(
619 [frames]()
620 {
621 /*
622 * There is no synchronisation required here -
623 * This is run on the WaylandExecutor, and is guaranteed to run on the
624 * wl_event_loop's thread.
625 *
626 * The only other accessors of WlSurface are also on the wl_event_loop,
627 * so this is guaranteed not to be reentrant.
628 */
629 for (auto frame : *frames)
630 {
631 wl_callback_send_done(frame, 0);
632 wl_resource_destroy(frame);
633 }
634 frames->clear();
635 });
636 };
637
638 if (shm_buffer)
639 {
640 mir_buffer = WlShmBuffer::mir_buffer_from_wl_buffer(
641 pending_buffer,
642 std::move(send_frame_notifications));
643 }
644 else if (
645 allocator &&
646 (mir_buffer = allocator->buffer_from_resource(
647 pending_buffer,
648 std::move(send_frame_notifications))))
649 {
650 }
651 else
652 {
653 BOOST_THROW_EXCEPTION((std::runtime_error{"Received unhandled buffer type"}));
654 }
655
656 /*
657 * This is technically incorrect - the resize and submit_buffer *should* be atomic,
658 * but are not, so a client in the process of resizing can have buffers rendered at
659 * an unexpected size.
660 *
661 * It should be good enough for now, though.
662 *
663 * TODO: Provide a mg::Buffer::logical_size() to do this properly.
664 */
665 stream->resize(mir_buffer->size());
666 if (resize_handler)
667 {
668 resize_handler(mir_buffer->size());
669 }
670 stream->submit_buffer(mir_buffer);
671
672 pending_buffer = nullptr;
673 }
674}
675
676void WlSurface::set_buffer_transform(int32_t transform)
677{
678 (void)transform;
679}
680
681void WlSurface::set_buffer_scale(int32_t scale)
682{
683 (void)scale;
684}
685
686class WlCompositor : public wayland::Compositor
687{
688public:
689 WlCompositor(
690 struct wl_display* display,
691 std::shared_ptr<mir::Executor> const& executor,
692 std::shared_ptr<mg::WaylandAllocator> const& allocator)
693 : Compositor(display, 3),
694 allocator{allocator},
695 executor{executor}
696 {
697 }
698
699private:
700 std::shared_ptr<mg::WaylandAllocator> const allocator;
701 std::shared_ptr<mir::Executor> const executor;
702
703 void create_surface(wl_client* client, wl_resource* resource, uint32_t id) override;
704 void create_region(wl_client* client, wl_resource* resource, uint32_t id) override;
705};
706
707void WlCompositor::create_surface(wl_client* client, wl_resource* resource, uint32_t id)
708{
709 new WlSurface{client, resource, id, executor, allocator};
710}
711
712class Region : public wayland::Region
713{
714public:
715 Region(wl_client* client, wl_resource* parent, uint32_t id)
716 : wayland::Region(client, parent, id)
717 {
718 }
719protected:
720
721 void destroy() override
722 {
723 }
724 void add(int32_t /*x*/, int32_t /*y*/, int32_t /*width*/, int32_t /*height*/) override
725 {
726 }
727 void subtract(int32_t /*x*/, int32_t /*y*/, int32_t /*width*/, int32_t /*height*/) override
728 {
729 }
730
731};
732
733void WlCompositor::create_region(wl_client* client, wl_resource* resource, uint32_t id)
734{
735 new Region{client, resource, id};
736}
737
738class WlPointer;
739class WlTouch;
740
741class WlKeyboard : public wayland::Keyboard
742{
743public:
744 WlKeyboard(
745 wl_client* client,
746 wl_resource* parent,
747 uint32_t id,
748 std::shared_ptr<mir::Executor> const& executor)
749 : Keyboard(client, parent, id),
750 executor{executor}
751 {
752 }
753
754 void handle_event(MirInputEvent const* event, wl_resource* /*target*/)
755 {
756 executor->spawn(
757 [ev = mir_event_ref(mir_input_event_get_event(event)), this] ()
758 {
759 int const serial = wl_display_next_serial(wl_client_get_display(client));
760 auto event = mir_event_get_input_event(ev);
761 auto key_event = mir_input_event_get_keyboard_event(event);
762
763 switch (mir_keyboard_event_action(key_event))
764 {
765 case mir_keyboard_action_up:
766 wl_keyboard_send_key(resource,
767 serial,
768 mir_input_event_get_event_time(event) / 1000,
769 mir_keyboard_event_scan_code(key_event),
770 WL_KEYBOARD_KEY_STATE_RELEASED);
771 break;
772 case mir_keyboard_action_down:
773 wl_keyboard_send_key(resource,
774 serial,
775 mir_input_event_get_event_time(event) / 1000,
776 mir_keyboard_event_scan_code(key_event),
777 WL_KEYBOARD_KEY_STATE_PRESSED);
778 break;
779 default:
780 break;
781 }
782 mir_event_unref(ev);
783 });
784 }
785
786private:
787 std::shared_ptr<mir::Executor> const executor;
788
789 void release() override;
790};
791
792void WlKeyboard::release()
793{
794}
795
796namespace
797{
798uint32_t calc_button_difference(MirPointerButtons old, MirPointerButtons updated)
799{
800 switch (old ^ updated)
801 {
802 case mir_pointer_button_primary:
803 return 272; // No, I have *no* idea why GTK expects 271 to be the primary button.
804 case mir_pointer_button_secondary:
805 return 274;
806 case mir_pointer_button_tertiary:
807 return 273;
808 case mir_pointer_button_back:
809 return 275; // I dunno. It's a number, I guess.
810 case mir_pointer_button_forward:
811 return 276; // I dunno. It's a number, I guess.
812 default:
813 throw std::logic_error("Whoops, I misunderstand how Mir pointer events work");
814 }
815}
816}
817
818class WlPointer : public wayland::Pointer
819{
820public:
821
822 WlPointer(
823 wl_client* client,
824 wl_resource* parent,
825 uint32_t id,
826 std::shared_ptr<mir::Executor> const& executor)
827 : Pointer(client, parent, id),
828 display{wl_client_get_display(client)},
829 executor{executor}
830 {
831 }
832
833 void handle_event(MirInputEvent const* event, wl_resource* target)
834 {
835 executor->spawn(
836 [ev = mir_event_ref(mir_input_event_get_event(event)), target, this]()
837 {
838 auto const serial = wl_display_next_serial(display);
839 auto const event = mir_event_get_input_event(ev);
840 auto const pointer_event = mir_input_event_get_pointer_event(event);
841
842 switch(mir_pointer_event_action(pointer_event))
843 {
844 case mir_pointer_action_button_down:
845 {
846 auto button = calc_button_difference(last_set, mir_pointer_event_buttons(pointer_event));
847 wl_pointer_send_button(
848 resource,
849 serial,
850 mir_input_event_get_event_time(event) / 1000,
851 button,
852 WL_POINTER_BUTTON_STATE_PRESSED);
853 last_set = mir_pointer_event_buttons(pointer_event);
854 break;
855 }
856 case mir_pointer_action_button_up:
857 {
858 auto button = calc_button_difference(last_set, mir_pointer_event_buttons(pointer_event));
859 wl_pointer_send_button(
860 resource,
861 serial,
862 mir_input_event_get_event_time(event) / 1000,
863 button,
864 WL_POINTER_BUTTON_STATE_RELEASED);
865 last_set = mir_pointer_event_buttons(pointer_event);
866 break;
867 }
868 case mir_pointer_action_enter:
869 {
870 wl_pointer_send_enter(
871 resource,
872 serial,
873 target,
874 wl_fixed_from_double(mir_pointer_event_axis_value(pointer_event, mir_pointer_axis_x)),
875 wl_fixed_from_double(mir_pointer_event_axis_value(pointer_event, mir_pointer_axis_y)));
876 break;
877 }
878 case mir_pointer_action_leave:
879 {
880 wl_pointer_send_leave(
881 resource,
882 serial,
883 target);
884 break;
885 }
886 case mir_pointer_action_motion:
887 {
888 auto x = mir_pointer_event_axis_value(pointer_event, mir_pointer_axis_x);
889 auto y = mir_pointer_event_axis_value(pointer_event, mir_pointer_axis_y);
890 auto vscroll = mir_pointer_event_axis_value(pointer_event, mir_pointer_axis_vscroll);
891 auto hscroll = mir_pointer_event_axis_value(pointer_event, mir_pointer_axis_hscroll);
892
893 if ((x != last_x) || (y != last_y))
894 {
895 wl_pointer_send_motion(
896 resource,
897 mir_input_event_get_event_time(event) / 1000,
898 wl_fixed_from_double(x),
899 wl_fixed_from_double(y));
900
901 last_x = x;
902 last_y = y;
903 }
904 if (vscroll != last_vscroll)
905 {
906 wl_pointer_send_axis(
907 resource,
908 mir_input_event_get_event_time(event) / 1000,
909 WL_POINTER_AXIS_VERTICAL_SCROLL,
910 wl_fixed_from_double(vscroll));
911 last_vscroll = vscroll;
912 }
913 if (hscroll != last_hscroll)
914 {
915 wl_pointer_send_axis(
916 resource,
917 mir_input_event_get_event_time(event) / 1000,
918 WL_POINTER_AXIS_HORIZONTAL_SCROLL,
919 wl_fixed_from_double(hscroll));
920 last_hscroll = hscroll;
921 }
922 break;
923 }
924 case mir_pointer_actions:
925 break;
926 }
927 });
928 }
929
930 // Pointer interface
931private:
932 wl_display* const display;
933 std::shared_ptr<mir::Executor> const executor;
934
935 MirPointerButtons last_set{0};
936 float last_x, last_y, last_vscroll, last_hscroll;
937
938 void set_cursor(uint32_t serial, std::experimental::optional<wl_resource*> const& surface, int32_t hotspot_x, int32_t hotspot_y) override;
939 void release() override;
940};
941
942void WlPointer::set_cursor(uint32_t serial, std::experimental::optional<wl_resource*> const& surface, int32_t hotspot_x, int32_t hotspot_y)
943{
944 (void)serial;
945 (void)surface;
946 (void)hotspot_x;
947 (void)hotspot_y;
948}
949
950void WlPointer::release()
951{
952 delete this;
953}
954
955class WlTouch : public wayland::Touch
956{
957public:
958 WlTouch(
959 wl_client* client,
960 wl_resource* parent,
961 uint32_t id,
962 std::shared_ptr<mir::Executor> const& /*executor*/)
963 : Touch(client, parent, id)
964 {
965 }
966
967 void handle_event(MirInputEvent const* /*event*/, wl_resource* /*target*/)
968 {
969 }
970
971 // Touch interface
972private:
973 void release() override;
974};
975
976void WlTouch::release()
977{
978}
979
980template<class InputInterface>
981class InputCtx
982{
983public:
984 InputCtx() = default;
985
986 InputCtx(InputCtx&&) = delete;
987 InputCtx(InputCtx const&) = delete;
988 InputCtx& operator=(InputCtx const&) = delete;
989
990 void register_listener(std::shared_ptr<InputInterface> const& listener)
991 {
992 listeners.push_back(listener);
993 }
994
995 void unregister_listener(InputInterface const* listener)
996 {
997 std::remove_if(
998 listeners.begin(),
999 listeners.end(),
1000 [listener](auto candidate)
1001 {
1002 return candidate.get() == listener;
1003 });
1004 }
1005
1006 void handle_event(MirInputEvent const* event, wl_resource* target) const
1007 {
1008 for (auto& listener : listeners)
1009 {
1010 listener->handle_event(event, target);
1011 }
1012 }
1013
1014private:
1015 std::vector<std::shared_ptr<InputInterface>> listeners;
1016};
1017
1018class WlSeat
1019{
1020public:
1021 WlSeat(wl_display* display, std::shared_ptr<mir::Executor> const& executor)
1022 : executor{executor},
1023 global{wl_global_create(
1024 display,
1025 &wl_seat_interface,
1026 5,
1027 this,
1028 &WlSeat::bind)}
1029 {
1030 if (!global)
1031 {
1032 BOOST_THROW_EXCEPTION(std::runtime_error("Failed to export wl_seat interface"));
1033 }
1034 }
1035
1036 ~WlSeat()
1037 {
1038 wl_global_destroy(global);
1039 }
1040
1041 InputCtx<WlPointer> const& acquire_pointer_reference(wl_client* client) const;
1042 InputCtx<WlKeyboard> const& acquire_keyboard_reference(wl_client* client) const;
1043 InputCtx<WlTouch> const& acquire_touch_reference(wl_client* client) const;
1044
1045private:
1046 std::unordered_map<wl_client*, InputCtx<WlPointer>> mutable pointer;
1047 std::unordered_map<wl_client*, InputCtx<WlKeyboard>> mutable keyboard;
1048 std::unordered_map<wl_client*, InputCtx<WlTouch>> mutable touch;
1049 std::shared_ptr<mir::Executor> const executor;
1050
1051 static void bind(struct wl_client* client, void* data, uint32_t version, uint32_t id)
1052 {
1053 auto me = reinterpret_cast<WlSeat*>(data);
1054 auto resource = wl_resource_create(client, &wl_seat_interface,
1055 std::min(version, 6u), id);
1056 if (resource == nullptr)
1057 {
1058 wl_client_post_no_memory(client);
1059 BOOST_THROW_EXCEPTION((std::bad_alloc{}));
1060 }
1061 wl_resource_set_implementation(resource, &vtable, me, nullptr);
1062
1063 /*
1064 * TODO: Read the actual capabilities. Do we have a keyboard? Mouse? Touch?
1065 */
1066 wl_seat_send_capabilities(
1067 resource,
1068 WL_SEAT_CAPABILITY_POINTER |
1069 WL_SEAT_CAPABILITY_KEYBOARD);
1070 wl_seat_send_name(
1071 resource,
1072 "seat0");
1073
1074 wl_resource_set_user_data(resource, me);
1075 }
1076
1077 static void get_pointer(wl_client* client, wl_resource* resource, uint32_t id)
1078 {
1079 auto me = reinterpret_cast<WlSeat*>(wl_resource_get_user_data(resource));
1080 me->pointer[client].register_listener(
1081 std::make_shared<WlPointer>(
1082 client,
1083 resource,
1084 id,
1085 me->executor));
1086 }
1087 static void get_keyboard(wl_client* client, wl_resource* resource, uint32_t id)
1088 {
1089 auto me = reinterpret_cast<WlSeat*>(wl_resource_get_user_data(resource));
1090 me->keyboard[client].register_listener(
1091 std::make_shared<WlKeyboard>(
1092 client,
1093 resource,
1094 id,
1095 me->executor));
1096 }
1097 static void get_touch(wl_client* client, wl_resource* resource, uint32_t id)
1098 {
1099 auto me = reinterpret_cast<WlSeat*>(wl_resource_get_user_data(resource));
1100 me->touch[client].register_listener(
1101 std::make_shared<WlTouch>(
1102 client,
1103 resource,
1104 id,
1105 me->executor));
1106 }
1107 static void release(struct wl_client* /*client*/, struct wl_resource* /*resource*/) {}
1108
1109
1110 wl_global* const global;
1111 static struct wl_seat_interface const vtable;
1112};
1113
1114struct wl_seat_interface const WlSeat::vtable = {
1115 WlSeat::get_pointer,
1116 WlSeat::get_keyboard,
1117 WlSeat::get_touch,
1118 WlSeat::release
1119};
1120
1121InputCtx<WlKeyboard> const& WlSeat::acquire_keyboard_reference(wl_client* client) const
1122{
1123 return keyboard[client];
1124}
1125
1126InputCtx<WlPointer> const& WlSeat::acquire_pointer_reference(wl_client* client) const
1127{
1128 return pointer[client];
1129}
1130
1131InputCtx<WlTouch> const& WlSeat::acquire_touch_reference(wl_client* client) const
1132{
1133 return touch[client];
1134}
1135
1136void WaylandEventSink::send_buffer(BufferStreamId /*id*/, graphics::Buffer& /*buffer*/, graphics::BufferIpcMsgType)
1137{
1138}
1139
1140void WaylandEventSink::handle_event(MirEvent const& e)
1141{
1142 switch(mir_event_get_type(&e))
1143 {
1144 default:
1145 // Do nothing
1146 break;
1147 }
1148}
1149
1150void WaylandEventSink::handle_lifecycle_event(MirLifecycleState state)
1151{
1152 lifecycle_handler(state);
1153}
1154
1155void WaylandEventSink::handle_display_config_change(graphics::DisplayConfiguration const& /*config*/)
1156{
1157}
1158
1159void WaylandEventSink::send_ping(int32_t)
1160{
1161}
1162
1163class SurfaceInputSink : public mf::EventSink
1164{
1165public:
1166 SurfaceInputSink(WlSeat* seat, wl_client* client, wl_resource* target)
1167 : seat{seat},
1168 client{client},
1169 target{target}
1170 {
1171 }
1172
1173 void handle_event(MirEvent const& e) override;
1174 void handle_lifecycle_event(MirLifecycleState) override {}
1175 void handle_display_config_change(graphics::DisplayConfiguration const&) override {}
1176 void send_ping(int32_t) override {}
1177 void handle_input_config_change(MirInputConfig const&) override {}
1178 void handle_error(ClientVisibleError const&) override {}
1179
1180 void send_buffer(frontend::BufferStreamId, graphics::Buffer&, graphics::BufferIpcMsgType) override {}
1181 void add_buffer(graphics::Buffer&) override {}
1182 void error_buffer(geometry::Size, MirPixelFormat, std::string const& ) override {}
1183 void update_buffer(graphics::Buffer&) override {}
1184
1185private:
1186 WlSeat* const seat;
1187 wl_client* const client;
1188 wl_resource* const target;
1189};
1190
1191void SurfaceInputSink::handle_event(MirEvent const& event)
1192{
1193 switch (mir_event_get_type(&event))
1194 {
1195 case mir_event_type_input:
1196 {
1197 auto input_event = mir_event_get_input_event(&event);
1198 switch (mir_input_event_get_type(input_event))
1199 {
1200 case mir_input_event_type_key:
1201 seat->acquire_keyboard_reference(client).handle_event(input_event, target);
1202 break;
1203 case mir_input_event_type_pointer:
1204 seat->acquire_pointer_reference(client).handle_event(input_event, target);
1205 break;
1206 case mir_input_event_type_touch:
1207 seat->acquire_touch_reference(client).handle_event(input_event, target);
1208 break;
1209 default:
1210 break;
1211 }
1212 }
1213 default:
1214 break;
1215 }
1216}
1217
1218class Output
1219{
1220public:
1221 Output(
1222 wl_display* display,
1223 mg::DisplayConfigurationOutput const& initial_configuration)
1224 : output{make_output(display)},
1225 current_config{initial_configuration}
1226 {
1227 }
1228
1229 ~Output()
1230 {
1231 wl_global_destroy(output);
1232 }
1233
1234 void handle_configuration_changed(mg::DisplayConfigurationOutput const& /*config*/)
1235 {
1236
1237 }
1238
1239private:
1240 static void send_initial_config(
1241 wl_resource* client_resource,
1242 mg::DisplayConfigurationOutput const& config)
1243 {
1244 wl_output_send_geometry(
1245 client_resource,
1246 config.top_left.x.as_int(),
1247 config.top_left.y.as_int(),
1248 config.physical_size_mm.width.as_int(),
1249 config.physical_size_mm.height.as_int(),
1250 WL_OUTPUT_SUBPIXEL_UNKNOWN,
1251 "Fake manufacturer",
1252 "Fake model",
1253 WL_OUTPUT_TRANSFORM_NORMAL);
1254 for (size_t i = 0; i < config.modes.size(); ++i)
1255 {
1256 auto const& mode = config.modes[i];
1257 wl_output_send_mode(
1258 client_resource,
1259 ((i == config.preferred_mode_index ? WL_OUTPUT_MODE_PREFERRED : 0) |
1260 (i == config.current_mode_index ? WL_OUTPUT_MODE_CURRENT : 0)),
1261 mode.size.width.as_int(),
1262 mode.size.height.as_int(),
1263 mode.vrefresh_hz * 1000);
1264 }
1265 wl_output_send_scale(client_resource, 1);
1266 wl_output_send_done(client_resource);
1267 }
1268
1269 wl_global* make_output(wl_display* display)
1270 {
1271 return wl_global_create(
1272 display,
1273 &wl_output_interface,
1274 2,
1275 this, &on_bind);
1276 }
1277
1278 static void on_bind(wl_client* client, void* data, uint32_t version, uint32_t id)
1279 {
1280 auto output = reinterpret_cast<Output*>(data);
1281 auto resource = wl_resource_create(client, &wl_output_interface,
1282 std::min(version, 2u), id);
1283 if (resource == NULL) {
1284 wl_client_post_no_memory(client);
1285 return;
1286 }
1287
1288 output->resource_map[client].push_back(resource);
1289 wl_resource_set_destructor(resource, &resource_destructor);
1290 wl_resource_set_user_data(resource, &(output->resource_map));
1291
1292 send_initial_config(resource, output->current_config);
1293 }
1294
1295 static void resource_destructor(wl_resource* resource)
1296 {
1297 auto& map = *reinterpret_cast<decltype(resource_map)*>(
1298 wl_resource_get_user_data(resource));
1299
1300 auto& client_resource_list = map[wl_resource_get_client(resource)];
1301 std::remove_if(
1302 client_resource_list.begin(),
1303 client_resource_list.end(),
1304 [resource](auto candidate) { return candidate == resource; });
1305 }
1306
1307private:
1308 wl_global* const output;
1309 mg::DisplayConfigurationOutput current_config;
1310 std::unordered_map<wl_client*, std::vector<wl_resource*>> resource_map;
1311};
1312
1313class OutputManager
1314{
1315public:
1316 OutputManager(
1317 wl_display* display,
1318 mf::DisplayChanger& display_config)
1319 : display{display}
1320 {
1321 // TODO: Also register display configuration listeners
1322 display_config.base_configuration()->for_each_output(std::bind(&OutputManager::create_output, this, std::placeholders::_1));
1323 }
1324
1325private:
1326 void create_output(mg::DisplayConfigurationOutput const& initial_config)
1327 {
1328 if (initial_config.used)
1329 {
1330 outputs.emplace(
1331 initial_config.id,
1332 std::make_unique<Output>(
1333 display,
1334 initial_config));
1335 }
1336 }
1337
1338 void handle_configuration_change(mg::DisplayConfiguration const& config)
1339 {
1340 config.for_each_output([this](mg::DisplayConfigurationOutput const& output_config)
1341 {
1342 auto output_iter = outputs.find(output_config.id);
1343 if (output_iter != outputs.end())
1344 {
1345 if (output_config.used)
1346 {
1347 output_iter->second->handle_configuration_changed(output_config);
1348 }
1349 else
1350 {
1351 outputs.erase(output_iter);
1352 }
1353 }
1354 else if (output_config.used)
1355 {
1356 outputs[output_config.id] = std::make_unique<Output>(display, output_config);
1357 }
1358 });
1359 }
1360
1361 wl_display* const display;
1362 std::unordered_map<mg::DisplayConfigurationOutputId, std::unique_ptr<Output>> outputs;
1363};
1364
1365class WlShellSurface : public wayland::ShellSurface
1366{
1367public:
1368 WlShellSurface(
1369 wl_client* client,
1370 wl_resource* parent,
1371 uint32_t id,
1372 wl_resource* surface,
1373 std::shared_ptr<mf::Shell> const& shell,
1374 WlSeat& seat)
1375 : ShellSurface(client, parent, id),
1376 shell{shell}
1377 {
1378 auto* tmp = wl_resource_get_user_data(surface);
1379 auto& mir_surface = *static_cast<WlSurface*>(reinterpret_cast<wayland::Surface*>(tmp));
1380
1381 auto const session = session_for_client(client);
1382
1383 auto params = ms::SurfaceCreationParameters()
1384 .of_type(mir_window_type_freestyle)
1385 .of_size(geom::Size{100, 100})
1386 .with_buffer_stream(mir_surface.stream_id);
1387
1388 surface_id = shell->create_surface(
1389 session,
1390 params,
1391 std::make_shared<SurfaceInputSink>(&seat, client, surface));
1392
1393 mir_surface.set_resize_handler(
1394 [shell, session, id = surface_id](geom::Size new_size)
1395 {
1396 shell::SurfaceSpecification new_size_spec;
1397 new_size_spec.width = new_size.width;
1398 new_size_spec.height = new_size.height;
1399 shell->modify_surface(session, id, new_size_spec);
1400 });
1401
1402 mir_surface.set_hide_handler(
1403 [shell, session, id = surface_id]()
1404 {
1405 shell::SurfaceSpecification hide_spec;
1406 hide_spec.state = mir_window_state_hidden;
1407 shell->modify_surface(session, id, hide_spec);
1408 });
1409
1410 auto shim = new DestructionShim{session, shell, surface_id};
1411 shim->destruction_listener.notify = &cleanup_surface;
1412 wl_resource_add_destroy_listener(
1413 resource,
1414 &shim->destruction_listener);
1415 }
1416
1417 ~WlShellSurface() override = default;
1418protected:
1419 void pong(uint32_t /*serial*/) override
1420 {
1421 }
1422
1423 void move(struct wl_resource* /*seat*/, uint32_t /*serial*/) override
1424 {
1425 }
1426
1427 void resize(struct wl_resource* /*seat*/, uint32_t /*serial*/, uint32_t /*edges*/) override
1428 {
1429 }
1430
1431 void set_toplevel() override
1432 {
1433 }
1434
1435 void set_transient(
1436 struct wl_resource* /*parent*/,
1437 int32_t /*x*/,
1438 int32_t /*y*/,
1439 uint32_t /*flags*/) override
1440 {
1441 }
1442
1443 void set_fullscreen(
1444 uint32_t /*method*/,
1445 uint32_t /*framerate*/,
1446 std::experimental::optional<struct wl_resource*> const& /*output*/) override
1447 {
1448 }
1449
1450 void set_popup(
1451 struct wl_resource* /*seat*/,
1452 uint32_t /*serial*/,
1453 struct wl_resource* /*parent*/,
1454 int32_t /*x*/,
1455 int32_t /*y*/,
1456 uint32_t /*flags*/) override
1457 {
1458 }
1459
1460 void set_maximized(std::experimental::optional<struct wl_resource*> const& /*output*/) override
1461 {
1462 }
1463
1464 void set_title(std::string const& /*title*/) override
1465 {
1466 }
1467
1468 void set_class(std::string const& /*class_*/) override
1469 {
1470 }
1471private:
1472 struct DestructionShim
1473 {
1474 DestructionShim(
1475 std::shared_ptr<mf::Session> const& session,
1476 std::shared_ptr<mf::Shell> const& shell,
1477 mf::SurfaceId id)
1478 : session{session},
1479 shell{shell},
1480 surface_id{id}
1481 {
1482 }
1483
1484 std::shared_ptr<mf::Session> const session;
1485 std::shared_ptr<mf::Shell> const shell;
1486 mf::SurfaceId const surface_id;
1487 wl_listener destruction_listener;
1488 };
1489
1490 static_assert(
1491 std::is_standard_layout<DestructionShim>::value,
1492 "DestructionShim must be Standard Layout for wl_container_of to be defined behaviour");
1493
1494 static void cleanup_surface(wl_listener* listener, void*)
1495 {
1496 DestructionShim* shim;
1497 shim = wl_container_of(listener, shim, destruction_listener);
1498
1499 shim->shell->destroy_surface(shim->session, shim->surface_id);
1500
1501 delete shim;
1502 }
1503
1504 std::shared_ptr<mf::Shell> const shell;
1505 mf::SurfaceId surface_id;
1506};
1507
1508class WlShell : public wayland::Shell
1509{
1510public:
1511 WlShell(
1512 wl_display* display,
1513 std::shared_ptr<mf::Shell> const& shell,
1514 WlSeat& seat)
1515 : Shell(display, 1),
1516 shell{shell},
1517 seat{seat}
1518 {
1519 }
1520
1521 void get_shell_surface(
1522 wl_client* client,
1523 wl_resource* resource,
1524 uint32_t id,
1525 wl_resource* surface) override
1526 {
1527 new WlShellSurface(client, resource, id, surface, shell, seat);
1528 }
1529private:
1530 std::shared_ptr<mf::Shell> const shell;
1531 WlSeat& seat;
1532};
1533}
1534}
1535
1536namespace
1537{
1538int halt_eventloop(int fd, uint32_t /*mask*/, void* data)
1539{
1540 auto display = reinterpret_cast<wl_display*>(data);
1541 wl_display_terminate(display);
1542
1543 eventfd_t ignored;
1544 if (eventfd_read(fd, &ignored) < 0)
1545 {
1546 BOOST_THROW_EXCEPTION((std::system_error{
1547 errno,
1548 std::system_category(),
1549 "Failed to consume pause event notification"}));
1550 }
1551 return 0;
1552}
1553}
1554
1555namespace
1556{
1557void cleanup_display(wl_display *display)
1558{
1559 wl_display_flush_clients(display);
1560 wl_display_destroy(display);
1561}
1562
1563class WaylandExecutor : public mir::Executor
1564{
1565public:
1566 void spawn (std::function<void ()>&& work) override
1567 {
1568 {
1569 std::lock_guard<std::recursive_mutex> lock{mutex};
1570 workqueue.emplace_back(std::move(work));
1571 }
1572 if (auto err = eventfd_write(notify_fd, 1))
1573 {
1574 BOOST_THROW_EXCEPTION((std::system_error{err, std::system_category(), "eventfd_write failed to notify event loop"}));
1575 }
1576 }
1577
1578 /**
1579 * Get an Executor which dispatches onto a wl_event_loop
1580 *
1581 * \note The executor may outlive the wl_event_loop, but no tasks will be dispatched
1582 * after the wl_event_loop is destroyed.
1583 *
1584 * \param [in] loop The event loop to dispatch on
1585 * \return An Executor that queues onto the wl_event_loop
1586 */
1587 static std::shared_ptr<mir::Executor> executor_for_event_loop(wl_event_loop* loop)
1588 {
1589 if (auto notifier = wl_event_loop_get_destroy_listener(loop, &on_display_destruction))
1590 {
1591 DestructionShim* shim;
1592 shim = wl_container_of(notifier, shim, destruction_listener);
1593
1594 return shim->executor;
1595 }
1596 else
1597 {
1598 auto const executor = std::shared_ptr<WaylandExecutor>{new WaylandExecutor{loop}};
1599 auto shim = std::make_unique<DestructionShim>(executor);
1600
1601 shim->destruction_listener.notify = &on_display_destruction;
1602 wl_event_loop_add_destroy_listener(loop, &(shim.release())->destruction_listener);
1603
1604 return executor;
1605 }
1606 }
1607
1608private:
1609 WaylandExecutor(wl_event_loop* loop)
1610 : notify_fd{eventfd(0, EFD_CLOEXEC | EFD_SEMAPHORE | EFD_NONBLOCK)},
1611 notify_source{wl_event_loop_add_fd(loop, notify_fd, WL_EVENT_READABLE, &on_notify, this)}
1612 {
1613 if (notify_fd == mir::Fd::invalid)
1614 {
1615 BOOST_THROW_EXCEPTION((std::system_error{
1616 errno,
1617 std::system_category(),
1618 "Failed to create IPC pause notification eventfd"}));
1619 }
1620 }
1621
1622 static int on_notify(int fd, uint32_t, void* data)
1623 {
1624 auto executor = static_cast<WaylandExecutor*>(data);
1625
1626 eventfd_t unused;
1627 if (auto err = eventfd_read(fd, &unused))
1628 {
1629 mir::log_error(
1630 "eventfd_read failed to consume wakeup notification: %s (%i)",
1631 strerror(err),
1632 err);
1633 }
1634
1635 std::lock_guard<std::recursive_mutex> lock{executor->mutex};
1636 while (!executor->workqueue.empty())
1637 {
1638 auto work = std::move(executor->workqueue.front());
1639 work();
1640 executor->workqueue.pop_front();
1641 }
1642
1643 return 0;
1644 }
1645
1646 static void on_display_destruction(wl_listener* listener, void*)
1647 {
1648 DestructionShim* shim;
1649 shim = wl_container_of(listener, shim, destruction_listener);
1650
1651 {
1652 std::lock_guard<std::recursive_mutex> lock{shim->executor->mutex};
1653 wl_event_source_remove(shim->executor->notify_source);
1654 }
1655 delete shim;
1656 }
1657
1658 std::recursive_mutex mutex;
1659 mir::Fd const notify_fd;
1660 std::deque<std::function<void()>> workqueue;
1661
1662 wl_event_source* const notify_source;
1663
1664 struct DestructionShim
1665 {
1666 explicit DestructionShim(std::shared_ptr<WaylandExecutor> const& executor)
1667 : executor{executor}
1668 {
1669 }
1670
1671 std::shared_ptr<WaylandExecutor> const executor;
1672 wl_listener destruction_listener;
1673 };
1674 static_assert(
1675 std::is_standard_layout<DestructionShim>::value,
1676 "DestructionShim must be Standard Layout for wl_container_of to be defined behaviour");
1677};
1678}
1679
1680mf::WaylandConnector::WaylandConnector(
1681 std::shared_ptr<mf::Shell> const& shell,
1682 DisplayChanger& display_config,
1683 std::shared_ptr<mg::GraphicBufferAllocator> const& allocator)
1684 : display{wl_display_create(), &cleanup_display},
1685 pause_signal{eventfd(0, EFD_CLOEXEC | EFD_SEMAPHORE)},
1686 allocator{std::dynamic_pointer_cast<mg::WaylandAllocator>(allocator)}
1687{
1688 if (pause_signal == mir::Fd::invalid)
1689 {
1690 BOOST_THROW_EXCEPTION((std::system_error{
1691 errno,
1692 std::system_category(),
1693 "Failed to create IPC pause notification eventfd"}));
1694 }
1695
1696 if (!display)
1697 {
1698 BOOST_THROW_EXCEPTION(std::runtime_error{"Failed to create wl_display"});
1699 }
1700
1701 /*
1702 * Here be Dragons!
1703 *
1704 * Some clients expect a certain order in the publication of globals, and will
1705 * crash with a different order. Yay!
1706 *
1707 * So far I've only found ones which expect wl_compositor before anything else,
1708 * so stick that first.
1709 */
1710 auto const executor = WaylandExecutor::executor_for_event_loop(wl_display_get_event_loop(display.get()));
1711
1712 compositor_global = std::make_unique<mf::WlCompositor>(
1713 display.get(),
1714 executor,
1715 this->allocator);
1716 seat_global = std::make_unique<mf::WlSeat>(display.get(), executor);
1717 output_manager = std::make_unique<mf::OutputManager>(
1718 display.get(),
1719 display_config);
1720 shell_global = std::make_unique<mf::WlShell>(display.get(), shell, *seat_global);
1721
1722 wl_display_init_shm(display.get());
1723
1724 if (this->allocator)
1725 {
1726 this->allocator->bind_display(display.get());
1727 }
1728 else
1729 {
1730 mir::log_warning("No WaylandAllocator EGL support!");
1731 }
1732
1733 wl_display_add_socket_auto(display.get());
1734
1735 auto wayland_loop = wl_display_get_event_loop(display.get());
1736
1737 setup_new_client_handler(display.get(), shell);
1738
1739 pause_source = wl_event_loop_add_fd(wayland_loop, pause_signal, WL_EVENT_READABLE, &halt_eventloop, display.get());
1740}
1741
1742mf::WaylandConnector::~WaylandConnector()
1743{
1744 if (dispatch_thread.joinable())
1745 {
1746 stop();
1747 }
1748 wl_event_source_remove(pause_source);
1749}
1750
1751void mf::WaylandConnector::start()
1752{
1753 dispatch_thread = std::thread{wl_display_run, display.get()};
1754}
1755
1756void mf::WaylandConnector::stop()
1757{
1758 if (eventfd_write(pause_signal, 1) < 0)
1759 {
1760 BOOST_THROW_EXCEPTION((std::system_error{
1761 errno,
1762 std::system_category(),
1763 "Failed to send IPC eventloop pause signal"}));
1764 }
1765 if (dispatch_thread.joinable())
1766 {
1767 dispatch_thread.join();
1768 dispatch_thread = std::thread{};
1769 }
1770 else
1771 {
1772 mir::log_warning("WaylandConnector::stop() called on not-running connector?");
1773 }
1774}
1775
1776int mf::WaylandConnector::client_socket_fd() const
1777{
1778 return -1;
1779}
1780
1781int mf::WaylandConnector::client_socket_fd(
1782 std::function<void(std::shared_ptr<Session> const& session)> const& /*connect_handler*/) const
1783{
1784 return -1;
1785}
01786
=== added file 'src/server/frontend/wayland/wayland_connector.h'
--- src/server/frontend/wayland/wayland_connector.h 1970-01-01 00:00:00 +0000
+++ src/server/frontend/wayland/wayland_connector.h 2017-09-07 05:58:57 +0000
@@ -0,0 +1,83 @@
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 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: Christopher James Halse Rogers <christopher.halse.rogers@canonical.com>
17 */
18
19#ifndef MIR_FRONTEND_WAYLAND_CONNECTOR_H_
20#define MIR_FRONTEND_WAYLAND_CONNECTOR_H_
21
22#include "mir/frontend/connector.h"
23#include "mir/fd.h"
24
25#include <wayland-server-core.h>
26#include <thread>
27
28namespace mir
29{
30
31
32namespace graphics
33{
34class GraphicBufferAllocator;
35class WaylandAllocator;
36}
37
38namespace frontend
39{
40class WlCompositor;
41class WlApplication;
42class WlShell;
43class WlSeat;
44class OutputManager;
45
46class Shell;
47class DisplayChanger;
48
49class WaylandConnector : public Connector
50{
51public:
52 WaylandConnector(
53 std::shared_ptr<Shell> const& shell,
54 DisplayChanger& display_config,
55 std::shared_ptr<graphics::GraphicBufferAllocator> const& allocator);
56
57 ~WaylandConnector() override;
58
59 void start() override;
60 void stop() override;
61
62 int client_socket_fd() const override;
63
64 int client_socket_fd(
65 std::function<void(std::shared_ptr<Session> const& session)> const& connect_handler) const override;
66
67private:
68 std::unique_ptr<wl_display, void(*)(wl_display*)> const display;
69 mir::Fd const pause_signal;
70 std::unique_ptr<WlCompositor> compositor_global;
71 std::unique_ptr<WlSeat> seat_global;
72 std::unique_ptr<OutputManager> output_manager;
73 std::shared_ptr<graphics::WaylandAllocator> const allocator;
74 std::unique_ptr<WlShell> shell_global;
75 std::thread dispatch_thread;
76 wl_event_source* pause_source;
77};
78
79
80}
81}
82
83#endif // MIR_FRONTEND_WAYLAND_CONNECTOR_H_
084
=== added file 'src/server/frontend/wayland/wayland_default_configuration.cpp'
--- src/server/frontend/wayland/wayland_default_configuration.cpp 1970-01-01 00:00:00 +0000
+++ src/server/frontend/wayland/wayland_default_configuration.cpp 2017-09-07 05:58:57 +0000
@@ -0,0 +1,39 @@
1/*
2 * Copyright © 2015, 2017 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: Christopher James Halse Rogers <christopher.halse.rogers@canonical.com>
17 */
18
19#include "mir/default_server_configuration.h"
20#include "wayland_connector.h"
21
22#include "../../scene/mediating_display_changer.h"
23#include "mir/graphics/platform.h"
24
25namespace mf = mir::frontend;
26
27std::shared_ptr<mf::Connector>
28 mir::DefaultServerConfiguration::the_wayland_connector()
29{
30 return wayland_connector(
31 [this]() -> std::shared_ptr<mf::Connector>
32 {
33 return std::make_shared<mf::WaylandConnector>(
34 the_frontend_shell(),
35 *the_mediating_display_changer(),
36 the_buffer_allocator());
37 });
38}
39
040
=== modified file 'src/server/symbols.map'
--- src/server/symbols.map 2017-08-02 11:07:50 +0000
+++ src/server/symbols.map 2017-09-07 05:58:57 +0000
@@ -913,6 +913,7 @@
913 mir::DefaultServerConfiguration::the_surface_factory*;913 mir::DefaultServerConfiguration::the_surface_factory*;
914 mir::DefaultServerConfiguration::the_surface_stack_model*;914 mir::DefaultServerConfiguration::the_surface_stack_model*;
915 mir::DefaultServerConfiguration::the_touch_visualizer*;915 mir::DefaultServerConfiguration::the_touch_visualizer*;
916 mir::DefaultServerConfiguration::the_wayland_connector*;
916 mir::DefaultServerConfiguration::the_window_manager_builder*;917 mir::DefaultServerConfiguration::the_window_manager_builder*;
917 mir::DefaultServerConfiguration::wrap_cursor*;918 mir::DefaultServerConfiguration::wrap_cursor*;
918 mir::DefaultServerConfiguration::wrap_cursor_listener*;919 mir::DefaultServerConfiguration::wrap_cursor_listener*;
919920
=== modified file 'tests/mir_test_doubles/mock_egl.cpp'
--- tests/mir_test_doubles/mock_egl.cpp 2017-07-28 17:00:43 +0000
+++ tests/mir_test_doubles/mock_egl.cpp 2017-09-07 05:58:57 +0000
@@ -51,6 +51,16 @@
51EGLint extension_eglClientWaitSyncKHR(EGLDisplay dpy, EGLSyncKHR sync, EGLint flags, EGLTimeKHR timeout);51EGLint extension_eglClientWaitSyncKHR(EGLDisplay dpy, EGLSyncKHR sync, EGLint flags, EGLTimeKHR timeout);
52EGLBoolean extension_eglGetSyncValuesCHROMIUM(EGLDisplay dpy,52EGLBoolean extension_eglGetSyncValuesCHROMIUM(EGLDisplay dpy,
53 EGLSurface surface, int64_t *ust, int64_t *msc, int64_t *sbc);53 EGLSurface surface, int64_t *ust, int64_t *msc, int64_t *sbc);
54EGLBoolean extension_eglBindWaylandDisplayWL(
55 EGLDisplay dpy,
56 struct wl_display *display);
57EGLBoolean extension_eglUnbindWaylandDisplayWL(
58 EGLDisplay dpy,
59 struct wl_display *display);
60EGLBoolean extension_eglQueryWaylandBufferWL(
61 EGLDisplay dpy,
62 struct wl_resource *buffer,
63 EGLint attribute, EGLint *value);
5464
55/* EGL{Surface,Display,Config,Context} are all opaque types, so we can put whatever65/* EGL{Surface,Display,Config,Context} are all opaque types, so we can put whatever
56 we want in them for testing */66 we want in them for testing */
@@ -150,13 +160,24 @@
150 .WillByDefault(Return(160 .WillByDefault(Return(
151 reinterpret_cast<func_ptr_t>(extension_eglGetSyncValuesCHROMIUM)161 reinterpret_cast<func_ptr_t>(extension_eglGetSyncValuesCHROMIUM)
152 ));162 ));
163 ON_CALL(*this, eglGetProcAddress(StrEq("eglQueryWaylandBufferWL")))
164 .WillByDefault(Return(reinterpret_cast<func_ptr_t>(&extension_eglQueryWaylandBufferWL)));
165 ON_CALL(*this, eglGetProcAddress(StrEq("eglBindWaylandDisplayWL")))
166 .WillByDefault(Return(reinterpret_cast<func_ptr_t>(&extension_eglBindWaylandDisplayWL)));
167 ON_CALL(*this, eglGetProcAddress(StrEq("eglUnbindWaylandDisplayWL")))
168 .WillByDefault(Return(reinterpret_cast<func_ptr_t>(&extension_eglUnbindWaylandDisplayWL)));
153}169}
154170
155void mtd::MockEGL::provide_egl_extensions()171void mtd::MockEGL::provide_egl_extensions()
156{172{
157 using namespace testing;173 using namespace testing;
158174
159 const char* egl_exts = "EGL_KHR_image EGL_KHR_image_base EGL_KHR_image_pixmap EGL_EXT_image_dma_buf_import";175 const char* egl_exts =
176 "EGL_KHR_image "
177 "EGL_KHR_image_base "
178 "EGL_KHR_image_pixmap "
179 "EGL_EXT_image_dma_buf_import "
180 "EGL_WL_bind_wayland_display";
160 ON_CALL(*this, eglQueryString(_,EGL_EXTENSIONS))181 ON_CALL(*this, eglQueryString(_,EGL_EXTENSIONS))
161 .WillByDefault(Return(egl_exts));182 .WillByDefault(Return(egl_exts));
162}183}
@@ -430,3 +451,29 @@
430 return global_mock_egl->eglGetSyncValuesCHROMIUM(dpy, surface,451 return global_mock_egl->eglGetSyncValuesCHROMIUM(dpy, surface,
431 ust, msc, sbc);452 ust, msc, sbc);
432}453}
454
455EGLBoolean extension_eglBindWaylandDisplayWL(
456 EGLDisplay dpy,
457 struct wl_display *display)
458{
459 CHECK_GLOBAL_MOCK(EGLBoolean);
460 return global_mock_egl->eglBindWaylandDisplayWL(dpy, display);
461}
462
463EGLBoolean extension_eglUnbindWaylandDisplayWL(
464 EGLDisplay dpy,
465 struct wl_display *display)
466{
467 CHECK_GLOBAL_MOCK(EGLBoolean);
468 return global_mock_egl->eglUnbindWaylandDisplayWL(dpy, display);
469}
470
471EGLBoolean extension_eglQueryWaylandBufferWL(
472 EGLDisplay dpy,
473 struct wl_resource* buffer,
474 EGLint attribute, EGLint* value)
475{
476 CHECK_GLOBAL_MOCK(EGLBoolean);
477 return global_mock_egl->eglQueryWaylandBufferWL(
478 dpy, buffer, attribute, value);
479}
433480
=== modified file 'tests/mir_test_doubles/nested_mock_egl.cpp'
--- tests/mir_test_doubles/nested_mock_egl.cpp 2017-07-28 17:00:43 +0000
+++ tests/mir_test_doubles/nested_mock_egl.cpp 2017-09-07 05:58:57 +0000
@@ -30,21 +30,12 @@
30 EXPECT_CALL(*this, eglTerminate(_)).Times(1);30 EXPECT_CALL(*this, eglTerminate(_)).Times(1);
31 }31 }
3232
33 EXPECT_CALL(*this, eglCreateWindowSurface(_, _, _, _)).Times(AnyNumber());
34 EXPECT_CALL(*this, eglMakeCurrent(_, _, _, _)).Times(AnyNumber());
35 EXPECT_CALL(*this, eglDestroySurface(_, _)).Times(AnyNumber());
36 EXPECT_CALL(*this, eglQueryString(_, _)).Times(AnyNumber());
37
38 provide_egl_extensions();33 provide_egl_extensions();
39 provide_stub_platform_buffer_swapping();34 provide_stub_platform_buffer_swapping();
4035
41 EXPECT_CALL(*this, eglChooseConfig(_, _, _, _, _)).Times(AnyNumber()).WillRepeatedly(36
37 ON_CALL(*this, eglChooseConfig(_, _, _, _, _)).WillByDefault(
42 DoAll(WithArgs<2, 4>(Invoke(this, &NestedMockEGL::egl_choose_config)), Return(EGL_TRUE)));38 DoAll(WithArgs<2, 4>(Invoke(this, &NestedMockEGL::egl_choose_config)), Return(EGL_TRUE)));
43 EXPECT_CALL(*this, eglGetCurrentContext()).Times(AnyNumber());
44 EXPECT_CALL(*this, eglCreatePbufferSurface(_, _, _)).Times(AnyNumber());
45 EXPECT_CALL(*this, eglGetProcAddress(StrEq("eglCreateImageKHR"))).Times(AnyNumber());
46 EXPECT_CALL(*this, eglGetProcAddress(StrEq("eglDestroyImageKHR"))).Times(AnyNumber());
47 EXPECT_CALL(*this, eglGetProcAddress(StrEq("glEGLImageTargetTexture2DOES"))).Times(AnyNumber());
4839
49 {40 {
50 InSequence context_lifecycle;41 InSequence context_lifecycle;

Subscribers

People subscribed via source and target branches