Merge lp:~raof/mir/waylanding-again into lp:mir
- waylanding-again
- Merge into development-branch
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 |
Related bugs: |
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
Description of the change
Mir CI Bot (mir-ci-bot) wrote : | # |
Mir CI Bot (mir-ci-bot) wrote : | # |
FAILED: Continuous integration, rev:4179
https:/
Executed test runs:
FAILURE: https:/
SUCCESS: https:/
SUCCESS: https:/
SUCCESS: https:/
SUCCESS: https:/
FAILURE: https:/
FAILURE: https:/
FAILURE: https:/
FAILURE: https:/
FAILURE: https:/
FAILURE: https:/
FAILURE: https:/
FAILURE: https:/
Click here to trigger a rebuild:
https:/
Mir CI Bot (mir-ci-bot) wrote : | # |
FAILED: Continuous integration, rev:4180
https:/
Executed test runs:
FAILURE: https:/
SUCCESS: https:/
SUCCESS: https:/
SUCCESS: https:/
SUCCESS: https:/
FAILURE: https:/
FAILURE: https:/
FAILURE: https:/
FAILURE: https:/
FAILURE: https:/
FAILURE: https:/
FAILURE: https:/
FAILURE: https:/
Click here to trigger a rebuild:
https:/
Mir CI Bot (mir-ci-bot) wrote : | # |
FAILED: Continuous integration, rev:4183
https:/
Executed test runs:
FAILURE: https:/
SUCCESS: https:/
SUCCESS: https:/
SUCCESS: https:/
SUCCESS: https:/
FAILURE: https:/
FAILURE: https:/
FAILURE: https:/
FAILURE: https:/
FAILURE: https:/
FAILURE: https:/
FAILURE: https:/
FAILURE: https:/
Click here to trigger a rebuild:
https:/
Mir CI Bot (mir-ci-bot) wrote : | # |
FAILED: Continuous integration, rev:4183
https:/
Executed test runs:
FAILURE: https:/
SUCCESS: https:/
SUCCESS: https:/
SUCCESS: https:/
SUCCESS: https:/
FAILURE: https:/
FAILURE: https:/
FAILURE: https:/
FAILURE: https:/
FAILURE: https:/
FAILURE: https:/
FAILURE: https:/
FAILURE: https:/
Click here to trigger a rebuild:
https:/
Mir CI Bot (mir-ci-bot) wrote : | # |
FAILED: Continuous integration, rev:4185
https:/
Executed test runs:
FAILURE: https:/
SUCCESS: https:/
SUCCESS: https:/
SUCCESS: https:/
SUCCESS: https:/
FAILURE: https:/
FAILURE: https:/
FAILURE: https:/
FAILURE: https:/
FAILURE: https:/
FAILURE: https:/
FAILURE: https:/
FAILURE: https:/
Click here to trigger a rebuild:
https:/
Mir CI Bot (mir-ci-bot) wrote : | # |
FAILED: Continuous integration, rev:4186
https:/
Executed test runs:
FAILURE: https:/
SUCCESS: https:/
SUCCESS: https:/
SUCCESS: https:/
SUCCESS: https:/
FAILURE: https:/
FAILURE: https:/
FAILURE: https:/
FAILURE: https:/
FAILURE: https:/
FAILURE: https:/
FAILURE: https:/
FAILURE: https:/
Click here to trigger a rebuild:
https:/
Mir CI Bot (mir-ci-bot) wrote : | # |
FAILED: Continuous integration, rev:4188
https:/
Executed test runs:
FAILURE: https:/
SUCCESS: https:/
SUCCESS: https:/
SUCCESS: https:/
SUCCESS: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
FAILURE: https:/
FAILURE: https:/
SUCCESS: https:/
deb: https:/
Click here to trigger a rebuild:
https:/
Mir CI Bot (mir-ci-bot) wrote : | # |
FAILED: Continuous integration, rev:4189
https:/
Executed test runs:
FAILURE: https:/
SUCCESS: https:/
SUCCESS: https:/
SUCCESS: https:/
SUCCESS: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
FAILURE: https:/
FAILURE: https:/
SUCCESS: https:/
deb: https:/
Click here to trigger a rebuild:
https:/
Chris Halse Rogers (raof) wrote : | # |
Oh, balls.
Cross-compilation with generated binaries. Sigh.
Gerry Boland (gerboland) wrote : | # |
+class WaylandBuffer :
+ void gl_bind_
+ for (auto&& frame : frames)
+ {
+ auto framer = std::move(frame);
+ executor->spawn(
+ [frame = framer.release(), deleter = framer.
+ {
+ wl_callback_
+ wl_client_
+ 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_
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_
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/
does this file need to change? I only see header files being added.
+++ src/protocol/
Missing licence.
to be continued...
Mir CI Bot (mir-ci-bot) wrote : | # |
FAILED: Continuous integration, rev:4190
https:/
Executed test runs:
FAILURE: https:/
SUCCESS: https:/
SUCCESS: https:/
SUCCESS: https:/
SUCCESS: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
FAILURE: https:/
FAILURE: https:/
SUCCESS: https:/
deb: https:/
Click here to trigger a rebuild:
https:/
Mir CI Bot (mir-ci-bot) wrote : | # |
FAILED: Continuous integration, rev:4190
https:/
Executed test runs:
FAILURE: https:/
SUCCESS: https:/
SUCCESS: https:/
SUCCESS: https:/
SUCCESS: https:/
FAILURE: https:/
FAILURE: https:/
FAILURE: https:/
FAILURE: https:/
FAILURE: https:/
FAILURE: https:/
FAILURE: https:/
FAILURE: https:/
Click here to trigger a rebuild:
https:/
Mir CI Bot (mir-ci-bot) wrote : | # |
FAILED: Continuous integration, rev:4190
https:/
Executed test runs:
FAILURE: https:/
SUCCESS: https:/
SUCCESS: https:/
SUCCESS: https:/
SUCCESS: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
FAILURE: https:/
FAILURE: https:/
SUCCESS: https:/
deb: https:/
Click here to trigger a rebuild:
https:/
Mir CI Bot (mir-ci-bot) wrote : | # |
FAILED: Continuous integration, rev:4190
https:/
Executed test runs:
FAILURE: https:/
SUCCESS: https:/
SUCCESS: https:/
SUCCESS: https:/
SUCCESS: https:/
FAILURE: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
FAILURE: https:/
FAILURE: https:/
SUCCESS: https:/
deb: https:/
Click here to trigger a rebuild:
https:/
Mir CI Bot (mir-ci-bot) wrote : | # |
FAILED: Continuous integration, rev:4192
https:/
Executed test runs:
FAILURE: https:/
SUCCESS: https:/
SUCCESS: https:/
SUCCESS: https:/
SUCCESS: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
FAILURE: https:/
FAILURE: https:/
SUCCESS: https:/
deb: https:/
Click here to trigger a rebuild:
https:/
Mir CI Bot (mir-ci-bot) wrote : | # |
PASSED: Continuous integration, rev:4193
https:/
Executed test runs:
SUCCESS: https:/
SUCCESS: https:/
SUCCESS: https:/
SUCCESS: https:/
SUCCESS: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
Click here to trigger a rebuild:
https:/
Mir CI Bot (mir-ci-bot) wrote : | # |
PASSED: Continuous integration, rev:4195
https:/
Executed test runs:
SUCCESS: https:/
SUCCESS: https:/
SUCCESS: https:/
SUCCESS: https:/
SUCCESS: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
Click here to trigger a rebuild:
https:/
Alan Griffiths (alan-griffiths) wrote : | # |
+ DestructionShim* shim;
+
+ shim = wl_container_
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.
Alan Griffiths (alan-griffiths) wrote : | # |
[terminal 1]
$ bin/miral-shell
[terminal 2]
$ bin/mir_
...
^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 "../../
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 "../../
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/
#include "core_generated
"make refresh-
Chris Halse Rogers (raof) wrote : | # |
Heh. Turns out that doing the not-quite-
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.
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.
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.
Gerry Boland (gerboland) wrote : | # |
Little patch to make it compile on artful: https:/
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.
Mir CI Bot (mir-ci-bot) wrote : | # |
FAILED: Continuous integration, rev:4196
https:/
Executed test runs:
FAILURE: https:/
SUCCESS: https:/
SUCCESS: https:/
SUCCESS: https:/
SUCCESS: https:/
FAILURE: https:/
SUCCESS: https:/
deb: https:/
FAILURE: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
FAILURE: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
Click here to trigger a rebuild:
https:/
Alan Griffiths (alan-griffiths) wrote : | # |
I just merged to trunk and "built" and got...
../libmirshared
/home/alan/
/home/alan/
../libmirshared
/home/alan/
/home/alan/
clang: error: linker command failed with exit code 1 (use -v to see invocation)
src/platforms/
make[2]: *** [lib/server-
CMakeFiles/
Once again "make refresh-
Alan Griffiths (alan-griffiths) wrote : | # |
> I just merged to trunk and "built" and got...
>
> ../libmirshared
> `WaylandBuffer':
> /home/alan/
> 46: undefined reference to `wl_resource_
> /home/alan/
> 65: undefined reference to `wl_resource_
> ../libmirshared
> `~WaylandBuffer':
> /home/alan/
> 05: undefined reference to `wl_resource_
> /home/alan/
> 06: undefined reference to `wl_resource_
> clang: error: linker command failed with exit code 1 (use -v to see
> invocation)
> src/platforms/
> ke:105: recipe for target 'lib/server-
> make[2]: *** [lib/server-
> CMakeFiles/
> 'src/platforms/
> failed
>
> Once again "make refresh-
> quite right.
Loads of tests fail, and (possibly related):
$ cmake-build-
...
wl_global_create: implemented version for 'wl_seat' higher than interface version (6 > 5)
ERROR: /home/alan/
Dynamic exception type: boost::
std::exception:
Segmentation fault (core dumped)
Alan Griffiths (alan-griffiths) wrote : | # |
> I just merged to trunk and "built" and got...
>
> ../libmirshared
> `WaylandBuffer':
> /home/alan/
> 46: undefined reference to `wl_resource_
> /home/alan/
> 65: undefined reference to `wl_resource_
> ../libmirshared
> `~WaylandBuffer':
> /home/alan/
> 05: undefined reference to `wl_resource_
> /home/alan/
> 06: undefined reference to `wl_resource_
> clang: error: linker command failed with exit code 1 (use -v to see
> invocation)
> src/platforms/
> ke:105: recipe for target 'lib/server-
> make[2]: *** [lib/server-
> CMakeFiles/
> 'src/platforms/
> failed
>
> Once again "make refresh-
> quite right.
Loads of tests fail, and (possibly related):
$ cmake-build-
...
wl_global_create: implemented version for 'wl_seat' higher than interface version (6 > 5)
ERROR: /home/alan/
Dynamic exception type: boost::
std::exception:
Segmentation fault (core dumped)
Mir CI Bot (mir-ci-bot) wrote : | # |
FAILED: Continuous integration, rev:4207
https:/
Executed test runs:
FAILURE: https:/
SUCCESS: https:/
SUCCESS: https:/
SUCCESS: https:/
SUCCESS: https:/
FAILURE: https:/
FAILURE: https:/
FAILURE: https:/
FAILURE: https:/
FAILURE: https:/
FAILURE: https:/
SUCCESS: https:/
deb: https:/
FAILURE: https:/
Click here to trigger a rebuild:
https:/
Mir CI Bot (mir-ci-bot) wrote : | # |
PASSED: Continuous integration, rev:4210
https:/
Executed test runs:
SUCCESS: https:/
SUCCESS: https:/
SUCCESS: https:/
SUCCESS: https:/
SUCCESS: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
Click here to trigger a rebuild:
https:/
Alan Griffiths (alan-griffiths) wrote : | # |
Close, just a few nits...
=== modified file 'tests/
--- tests/mir_
+++ tests/mir_
@@ -85,6 +85,7 @@
target_
+ 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://
+ *
+ * Authored by: Christopher James Halse Rogers <email address hidden>
+ */
+
+#include "mir/default_
+#include "wayland_
+
+#include "../../
#include "mir/frontend/
+#include "mir/graphics/
+
+namespace mf = mir::frontend;
+
+std::shared_
+ mir::DefaultSer
+{
+ return wayland_connector(
+ [this]() -> std::shared_
+ {
+ return std::make_
+ the_frontend_
+ *the_mediating_
(Do we really want to avoid sharing ownership? Where's the lifetime guarantee?)
+ the_buffer_
+ });
+}
~~~~~~~
=== added file 'src/server/
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*.
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=
Then in the miral-app terminal:
$ QT_QPA_
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:
what(): Setting null buffer is unimplemented
Mir CI Bot (mir-ci-bot) wrote : | # |
FAILED: Continuous integration, rev:4215
https:/
Executed test runs:
FAILURE: https:/
FAILURE: https:/
FAILURE: https:/
FAILURE: https:/
FAILURE: https:/
FAILURE: https:/
FAILURE: https:/
FAILURE: https:/
FAILURE: https:/
FAILURE: https:/
FAILURE: https:/
FAILURE: https:/
FAILURE: https:/
Click here to trigger a rebuild:
https:/
Mir CI Bot (mir-ci-bot) wrote : | # |
FAILED: Continuous integration, rev:4216
https:/
Executed test runs:
FAILURE: https:/
FAILURE: https:/
FAILURE: https:/
FAILURE: https:/
FAILURE: https:/
FAILURE: https:/
FAILURE: https:/
FAILURE: https:/
FAILURE: https:/
FAILURE: https:/
FAILURE: https:/
FAILURE: https:/
FAILURE: https:/
Click here to trigger a rebuild:
https:/
Mir CI Bot (mir-ci-bot) wrote : | # |
PASSED: Continuous integration, rev:4217
https:/
Executed test runs:
SUCCESS: https:/
SUCCESS: https:/
SUCCESS: https:/
SUCCESS: https:/
SUCCESS: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
Click here to trigger a rebuild:
https:/
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=
>
> Then in the miral-app terminal:
>
> $ QT_QPA_
>
> 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:
> _impl<boost:
> what(): Setting null buffer is unimplemented
Was -r 4216 meant to stop this crash? (It is still happening)
Alan Griffiths (alan-griffiths) wrote : | # |
+#include "mir/default_
+#include "wayland_
+
+#include "../../
#include "mir/frontend/
+#include "mir/graphics/
+
+namespace mf = mir::frontend;
+
+std::shared_
+ mir::DefaultSer
+{
+ return wayland_connector(
+ [this]() -> std::shared_
+ {
+ return std::make_
+ the_frontend_
+ *the_mediating_
(Do we really want to avoid sharing ownership? Where's the lifetime guarantee?)
+ the_buffer_
+ });
+}
Alan Griffiths (alan-griffiths) wrote : | # |
> +#include "mir/default_
> +#include "wayland_
> +
> +#include "../../
>
> #include "mir/frontend/
>
> +#include "mir/graphics/
> +
> +namespace mf = mir::frontend;
> +
> +std::shared_
> + mir::DefaultSer
> +{
> + return wayland_connector(
> + [this]() -> std::shared_
> + {
> + return std::make_
> + the_frontend_
> + *the_mediating_
>
> *the_frontend_
>
> (Do we really want to avoid sharing ownership? Where's the lifetime
> guarantee?)
>
> + the_buffer_
> + });
> +}
nm, let's land this and fixup afterwards.
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=
>>
>> Then in the miral-app terminal:
>>
>> $ QT_QPA_
>>
>> 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:
>>
>> _impl<boost:
>> >'
>> 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.
Chris Halse Rogers (raof) wrote : | # |
On Thu, Sep 7, 2017 at 9:18 PM, Alan Griffiths <email address hidden>
wrote:
> +#include "mir/default_
> +#include "wayland_
> +
> +#include "../../
>
> #include "mir/frontend/
>
> +#include "mir/graphics/
> +
> +namespace mf = mir::frontend;
> +
> +std::shared_
> + mir::DefaultSer
> +{
> + return wayland_connector(
> + [this]() -> std::shared_
> + {
> + return std::make_
> + the_frontend_
> + *the_mediating_
>
> *the_frontend_
>
> (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
1 | === modified file 'CMakeLists.txt' | |||
2 | --- CMakeLists.txt 2017-08-25 11:05:07 +0000 | |||
3 | +++ CMakeLists.txt 2017-09-07 05:58:57 +0000 | |||
4 | @@ -74,6 +74,16 @@ | |||
5 | 74 | set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wno-mismatched-tags") | 74 | set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wno-mismatched-tags") |
6 | 75 | endif() | 75 | endif() |
7 | 76 | 76 | ||
8 | 77 | # GCC 7.1 fixed a bug in the ARM ABI, which results in some std::vector methods | ||
9 | 78 | # (among others) generating this warning. | ||
10 | 79 | # | ||
11 | 80 | # There's nothing we can do about it; everything just needs to be rebuilt with | ||
12 | 81 | # GCC 7.1. | ||
13 | 82 | check_cxx_compiler_flag(-Wpsabi HAS_W_PSABI) | ||
14 | 83 | if(HAS_W_PSABI) | ||
15 | 84 | set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wno-psabi") | ||
16 | 85 | endif() | ||
17 | 86 | |||
18 | 77 | option(MIR_USE_LD_GOLD "Enables the \"gold\" linker." OFF) | 87 | option(MIR_USE_LD_GOLD "Enables the \"gold\" linker." OFF) |
19 | 78 | if(MIR_USE_LD_GOLD) | 88 | if(MIR_USE_LD_GOLD) |
20 | 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") |
21 | @@ -206,6 +216,8 @@ | |||
22 | 206 | 216 | ||
23 | 207 | pkg_check_modules(UDEV REQUIRED libudev) | 217 | pkg_check_modules(UDEV REQUIRED libudev) |
24 | 208 | pkg_check_modules(GLIB REQUIRED glib-2.0) | 218 | pkg_check_modules(GLIB REQUIRED glib-2.0) |
25 | 219 | pkg_check_modules(WAYLAND_SERVER REQUIRED wayland-server) | ||
26 | 220 | pkg_check_modules(WAYLAND_CLIENT REQUIRED wayland-client) | ||
27 | 209 | 221 | ||
28 | 210 | include_directories (SYSTEM ${GLESv2_INCLUDE_DIRS}) | 222 | include_directories (SYSTEM ${GLESv2_INCLUDE_DIRS}) |
29 | 211 | include_directories (SYSTEM ${EGL_INCLUDE_DIRS}) | 223 | include_directories (SYSTEM ${EGL_INCLUDE_DIRS}) |
30 | 212 | 224 | ||
31 | === modified file 'debian/control' | |||
32 | --- debian/control 2017-08-23 11:56:37 +0000 | |||
33 | +++ debian/control 2017-09-07 05:58:57 +0000 | |||
34 | @@ -31,6 +31,7 @@ | |||
35 | 31 | libudev-dev, | 31 | libudev-dev, |
36 | 32 | libgtest-dev, | 32 | libgtest-dev, |
37 | 33 | google-mock (>= 1.6.0+svn437), | 33 | google-mock (>= 1.6.0+svn437), |
38 | 34 | libxml++2.6-dev, | ||
39 | 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: |
40 | 35 | valgrind [amd64 i386 armhf arm64], | 36 | valgrind [amd64 i386 armhf arm64], |
41 | 36 | libglib2.0-dev, | 37 | libglib2.0-dev, |
42 | 37 | 38 | ||
43 | === modified file 'examples/CMakeLists.txt' | |||
44 | --- examples/CMakeLists.txt 2017-08-30 12:38:21 +0000 | |||
45 | +++ examples/CMakeLists.txt 2017-09-07 05:58:57 +0000 | |||
46 | @@ -274,6 +274,15 @@ | |||
47 | 274 | mirclient | 274 | mirclient |
48 | 275 | ) | 275 | ) |
49 | 276 | 276 | ||
50 | 277 | include_directories(${MIR_GENERATED_INCLUDE_DIRECTORIES}) | ||
51 | 278 | |||
52 | 279 | mir_add_wrapped_executable(mir_demo_client_wayland wayland_client.c) | ||
53 | 280 | target_link_libraries(mir_demo_client_wayland | ||
54 | 281 | |||
55 | 282 | ${WAYLAND_CLIENT_LIBRARIES} | ||
56 | 283 | ) | ||
57 | 284 | |||
58 | 277 | mir_add_wrapped_executable(mir_demo_client_screencast screencast.cpp) | 285 | mir_add_wrapped_executable(mir_demo_client_screencast screencast.cpp) |
59 | 278 | 286 | ||
60 | 279 | target_link_libraries(mir_demo_client_screencast mirclient) | 287 | target_link_libraries(mir_demo_client_screencast mirclient) |
61 | 288 | |||
62 | 280 | 289 | ||
63 | === added file 'examples/wayland_client.c' | |||
64 | --- examples/wayland_client.c 1970-01-01 00:00:00 +0000 | |||
65 | +++ examples/wayland_client.c 2017-09-07 05:58:57 +0000 | |||
66 | @@ -0,0 +1,391 @@ | |||
67 | 1 | /* | ||
68 | 2 | * Copyright © 2015 Canonical Ltd. | ||
69 | 3 | * | ||
70 | 4 | * This program is free software: you can redistribute it and/or modify | ||
71 | 5 | * it under the terms of the GNU General Public License version 2 or 3 as | ||
72 | 6 | * published by the Free Software Foundation. | ||
73 | 7 | * | ||
74 | 8 | * This program is distributed in the hope that it will be useful, | ||
75 | 9 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
76 | 10 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
77 | 11 | * GNU General Public License for more details. | ||
78 | 12 | * | ||
79 | 13 | * You should have received a copy of the GNU General Public License | ||
80 | 14 | * along with this program. If not, see <http://www.gnu.org/licenses/>. | ||
81 | 15 | * | ||
82 | 16 | * Author: Christopher James Halse Rogers <christopher.halse.rogers@canonical.com> | ||
83 | 17 | */ | ||
84 | 18 | |||
85 | 19 | #ifndef _GNU_SOURCE | ||
86 | 20 | #define _GNU_SOURCE | ||
87 | 21 | #endif | ||
88 | 22 | |||
89 | 23 | #include <stdlib.h> | ||
90 | 24 | #include <string.h> | ||
91 | 25 | #include <sys/mman.h> | ||
92 | 26 | #include <sys/types.h> | ||
93 | 27 | #include <sys/stat.h> | ||
94 | 28 | #include <fcntl.h> | ||
95 | 29 | #include <unistd.h> | ||
96 | 30 | #include <stdio.h> | ||
97 | 31 | #include <stdbool.h> | ||
98 | 32 | |||
99 | 33 | #include <wayland-client.h> | ||
100 | 34 | #include <wayland-client-core.h> | ||
101 | 35 | |||
102 | 36 | struct globals | ||
103 | 37 | { | ||
104 | 38 | struct wl_compositor* compositor; | ||
105 | 39 | struct wl_shm* shm; | ||
106 | 40 | struct wl_seat* seat; | ||
107 | 41 | struct wl_output* output; | ||
108 | 42 | struct wl_shell* shell; | ||
109 | 43 | }; | ||
110 | 44 | |||
111 | 45 | static void new_global( | ||
112 | 46 | void* data, | ||
113 | 47 | struct wl_registry* registry, | ||
114 | 48 | uint32_t id, | ||
115 | 49 | char const* interface, | ||
116 | 50 | uint32_t version) | ||
117 | 51 | { | ||
118 | 52 | (void)version; | ||
119 | 53 | struct globals* globals = data; | ||
120 | 54 | |||
121 | 55 | if (strcmp(interface, "wl_compositor") == 0) | ||
122 | 56 | { | ||
123 | 57 | globals->compositor = wl_registry_bind(registry, id, &wl_compositor_interface, 3); | ||
124 | 58 | } | ||
125 | 59 | else if (strcmp(interface, "wl_shm") == 0) | ||
126 | 60 | { | ||
127 | 61 | globals->shm = wl_registry_bind(registry, id, &wl_shm_interface, 1); | ||
128 | 62 | // Normally we'd add a listener to pick up the supported formats here | ||
129 | 63 | // As luck would have it, I know that argb8888 is the only format we support :) | ||
130 | 64 | } | ||
131 | 65 | else if (strcmp(interface, "wl_seat") == 0) | ||
132 | 66 | { | ||
133 | 67 | globals->seat = wl_registry_bind(registry, id, &wl_seat_interface, 4); | ||
134 | 68 | } | ||
135 | 69 | else if (strcmp(interface, "wl_output") == 0) | ||
136 | 70 | { | ||
137 | 71 | globals->output = wl_registry_bind(registry, id, &wl_output_interface, 2); | ||
138 | 72 | } | ||
139 | 73 | else if (strcmp(interface, "wl_shell") == 0) | ||
140 | 74 | { | ||
141 | 75 | globals->shell = wl_registry_bind(registry, id, &wl_shell_interface, 1); | ||
142 | 76 | } | ||
143 | 77 | } | ||
144 | 78 | |||
145 | 79 | static void global_remove( | ||
146 | 80 | void* data, | ||
147 | 81 | struct wl_registry* registry, | ||
148 | 82 | uint32_t name) | ||
149 | 83 | { | ||
150 | 84 | (void)data; | ||
151 | 85 | (void)registry; | ||
152 | 86 | (void)name; | ||
153 | 87 | } | ||
154 | 88 | |||
155 | 89 | static struct wl_registry_listener const registry_listener = { | ||
156 | 90 | .global = new_global, | ||
157 | 91 | .global_remove = global_remove | ||
158 | 92 | }; | ||
159 | 93 | |||
160 | 94 | static struct wl_shm_pool* | ||
161 | 95 | make_shm_pool(struct wl_shm* shm, int size, void **data) | ||
162 | 96 | { | ||
163 | 97 | struct wl_shm_pool *pool; | ||
164 | 98 | int fd; | ||
165 | 99 | |||
166 | 100 | fd = open("/dev/shm", O_TMPFILE | O_RDWR | O_EXCL, S_IRWXU); | ||
167 | 101 | if (fd < 0) { | ||
168 | 102 | return NULL; | ||
169 | 103 | } | ||
170 | 104 | |||
171 | 105 | posix_fallocate(fd, 0, size); | ||
172 | 106 | |||
173 | 107 | *data = mmap(NULL, size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0); | ||
174 | 108 | if (*data == MAP_FAILED) { | ||
175 | 109 | close(fd); | ||
176 | 110 | return NULL; | ||
177 | 111 | } | ||
178 | 112 | |||
179 | 113 | pool = wl_shm_create_pool(shm, fd, size); | ||
180 | 114 | |||
181 | 115 | close(fd); | ||
182 | 116 | |||
183 | 117 | return pool; | ||
184 | 118 | } | ||
185 | 119 | |||
186 | 120 | struct draw_context | ||
187 | 121 | { | ||
188 | 122 | void* content_area; | ||
189 | 123 | struct wl_display* display; | ||
190 | 124 | struct wl_surface* surface; | ||
191 | 125 | struct wl_callback* new_frame_signal; | ||
192 | 126 | struct Buffers | ||
193 | 127 | { | ||
194 | 128 | struct wl_buffer* buffer; | ||
195 | 129 | bool available; | ||
196 | 130 | } buffers[4]; | ||
197 | 131 | bool waiting_for_buffer; | ||
198 | 132 | }; | ||
199 | 133 | |||
200 | 134 | static struct wl_buffer* find_free_buffer(struct draw_context* ctx) | ||
201 | 135 | { | ||
202 | 136 | for (int i = 0; i < 4 ; ++i) | ||
203 | 137 | { | ||
204 | 138 | if (ctx->buffers[i].available) | ||
205 | 139 | { | ||
206 | 140 | ctx->buffers[i].available = false; | ||
207 | 141 | return ctx->buffers[i].buffer; | ||
208 | 142 | } | ||
209 | 143 | } | ||
210 | 144 | return NULL; | ||
211 | 145 | } | ||
212 | 146 | |||
213 | 147 | static void draw_new_stuff(void* data, struct wl_callback* callback, uint32_t time); | ||
214 | 148 | |||
215 | 149 | static const struct wl_callback_listener frame_listener = | ||
216 | 150 | { | ||
217 | 151 | .done = &draw_new_stuff | ||
218 | 152 | }; | ||
219 | 153 | |||
220 | 154 | static void update_free_buffers(void* data, struct wl_buffer* buffer) | ||
221 | 155 | { | ||
222 | 156 | struct draw_context* ctx = data; | ||
223 | 157 | for (int i = 0; i < 4 ; ++i) | ||
224 | 158 | { | ||
225 | 159 | if (ctx->buffers[i].buffer == buffer) | ||
226 | 160 | { | ||
227 | 161 | ctx->buffers[i].available = true; | ||
228 | 162 | } | ||
229 | 163 | } | ||
230 | 164 | |||
231 | 165 | if (ctx->waiting_for_buffer) | ||
232 | 166 | { | ||
233 | 167 | struct wl_callback* fake_frame = wl_display_sync(ctx->display); | ||
234 | 168 | wl_callback_add_listener(fake_frame, &frame_listener, ctx); | ||
235 | 169 | } | ||
236 | 170 | |||
237 | 171 | ctx->waiting_for_buffer = false; | ||
238 | 172 | } | ||
239 | 173 | |||
240 | 174 | static void draw_new_stuff( | ||
241 | 175 | void* data, | ||
242 | 176 | struct wl_callback* callback, | ||
243 | 177 | uint32_t time) | ||
244 | 178 | { | ||
245 | 179 | (void)time; | ||
246 | 180 | static unsigned char current_value = 128; | ||
247 | 181 | struct draw_context* ctx = data; | ||
248 | 182 | |||
249 | 183 | wl_callback_destroy(callback); | ||
250 | 184 | |||
251 | 185 | struct wl_buffer* buffer = find_free_buffer(ctx); | ||
252 | 186 | if (!buffer) | ||
253 | 187 | { | ||
254 | 188 | ctx->waiting_for_buffer = false; | ||
255 | 189 | return; | ||
256 | 190 | } | ||
257 | 191 | |||
258 | 192 | memset(ctx->content_area, current_value, 400 * 400 * 4); | ||
259 | 193 | ++current_value; | ||
260 | 194 | |||
261 | 195 | ctx->new_frame_signal = wl_surface_frame(ctx->surface); | ||
262 | 196 | wl_callback_add_listener(ctx->new_frame_signal, &frame_listener, ctx); | ||
263 | 197 | wl_surface_attach(ctx->surface, buffer, 0, 0); | ||
264 | 198 | wl_surface_commit(ctx->surface); | ||
265 | 199 | } | ||
266 | 200 | |||
267 | 201 | static struct wl_buffer_listener const buffer_listener = { | ||
268 | 202 | .release = update_free_buffers | ||
269 | 203 | }; | ||
270 | 204 | |||
271 | 205 | void mouse_enter( | ||
272 | 206 | void *data, | ||
273 | 207 | struct wl_pointer *wl_pointer, | ||
274 | 208 | uint32_t serial, | ||
275 | 209 | struct wl_surface *surface, | ||
276 | 210 | wl_fixed_t surface_x, | ||
277 | 211 | wl_fixed_t surface_y) | ||
278 | 212 | { | ||
279 | 213 | (void)data; | ||
280 | 214 | (void)wl_pointer; | ||
281 | 215 | (void)serial; | ||
282 | 216 | (void)surface; | ||
283 | 217 | printf("Received mouse_enter event: (%f, %f)\n", | ||
284 | 218 | wl_fixed_to_double(surface_x), | ||
285 | 219 | wl_fixed_to_double(surface_y)); | ||
286 | 220 | } | ||
287 | 221 | |||
288 | 222 | void mouse_leave( | ||
289 | 223 | void *data, | ||
290 | 224 | struct wl_pointer *wl_pointer, | ||
291 | 225 | uint32_t serial, | ||
292 | 226 | struct wl_surface *surface) | ||
293 | 227 | { | ||
294 | 228 | (void)data; | ||
295 | 229 | (void)wl_pointer; | ||
296 | 230 | (void)serial; | ||
297 | 231 | (void)surface; | ||
298 | 232 | printf("Received mouse_exit event\n"); | ||
299 | 233 | } | ||
300 | 234 | |||
301 | 235 | void mouse_motion( | ||
302 | 236 | void *data, | ||
303 | 237 | struct wl_pointer *wl_pointer, | ||
304 | 238 | uint32_t time, | ||
305 | 239 | wl_fixed_t surface_x, | ||
306 | 240 | wl_fixed_t surface_y) | ||
307 | 241 | { | ||
308 | 242 | (void)data; | ||
309 | 243 | (void)wl_pointer; | ||
310 | 244 | |||
311 | 245 | printf("Received motion event: (%f, %f) @ %i\n", | ||
312 | 246 | wl_fixed_to_double(surface_x), | ||
313 | 247 | wl_fixed_to_double(surface_y), | ||
314 | 248 | time); | ||
315 | 249 | } | ||
316 | 250 | |||
317 | 251 | void mouse_button( | ||
318 | 252 | void *data, | ||
319 | 253 | struct wl_pointer *wl_pointer, | ||
320 | 254 | uint32_t serial, | ||
321 | 255 | uint32_t time, | ||
322 | 256 | uint32_t button, | ||
323 | 257 | uint32_t state) | ||
324 | 258 | { | ||
325 | 259 | (void)serial; | ||
326 | 260 | (void)data; | ||
327 | 261 | (void)wl_pointer; | ||
328 | 262 | |||
329 | 263 | printf("Received button event: Button %i, state %i @ %i\n", | ||
330 | 264 | button, | ||
331 | 265 | state, | ||
332 | 266 | time); | ||
333 | 267 | } | ||
334 | 268 | |||
335 | 269 | void mouse_axis( | ||
336 | 270 | void *data, | ||
337 | 271 | struct wl_pointer *wl_pointer, | ||
338 | 272 | uint32_t time, | ||
339 | 273 | uint32_t axis, | ||
340 | 274 | wl_fixed_t value) | ||
341 | 275 | { | ||
342 | 276 | (void)data; | ||
343 | 277 | (void)wl_pointer; | ||
344 | 278 | |||
345 | 279 | printf("Received axis event: axis %i, value %f @ %i\n", | ||
346 | 280 | axis, | ||
347 | 281 | wl_fixed_to_double(value), | ||
348 | 282 | time); | ||
349 | 283 | } | ||
350 | 284 | |||
351 | 285 | |||
352 | 286 | |||
353 | 287 | static struct wl_pointer_listener const pointer_listener = { | ||
354 | 288 | .enter = &mouse_enter, | ||
355 | 289 | .leave = &mouse_leave, | ||
356 | 290 | .motion = &mouse_motion, | ||
357 | 291 | .button = &mouse_button, | ||
358 | 292 | .axis = &mouse_axis | ||
359 | 293 | }; | ||
360 | 294 | |||
361 | 295 | static void output_geometry(void *data, | ||
362 | 296 | struct wl_output *wl_output, | ||
363 | 297 | int32_t x, | ||
364 | 298 | int32_t y, | ||
365 | 299 | int32_t physical_width, | ||
366 | 300 | int32_t physical_height, | ||
367 | 301 | int32_t subpixel, | ||
368 | 302 | const char *make, | ||
369 | 303 | const char *model, | ||
370 | 304 | int32_t transform) | ||
371 | 305 | { | ||
372 | 306 | (void)data; | ||
373 | 307 | (void)wl_output; | ||
374 | 308 | (void)subpixel; | ||
375 | 309 | (void)make; | ||
376 | 310 | (void)model; | ||
377 | 311 | (void)transform; | ||
378 | 312 | printf("Got geometry: (%imm × %imm)@(%i, %i)\n", physical_width, physical_height, x, y); | ||
379 | 313 | } | ||
380 | 314 | |||
381 | 315 | static void output_mode(void *data, | ||
382 | 316 | struct wl_output *wl_output, | ||
383 | 317 | uint32_t flags, | ||
384 | 318 | int32_t width, | ||
385 | 319 | int32_t height, | ||
386 | 320 | int32_t refresh) | ||
387 | 321 | { | ||
388 | 322 | (void)data; | ||
389 | 323 | (void)wl_output; | ||
390 | 324 | printf("Got mode: %i×%i@%i (flags: %i)\n", width, height, refresh, flags); | ||
391 | 325 | } | ||
392 | 326 | |||
393 | 327 | static void output_done(void* data, struct wl_output* wl_output) | ||
394 | 328 | { | ||
395 | 329 | (void)data; | ||
396 | 330 | (void)wl_output; | ||
397 | 331 | printf("Output events done\n"); | ||
398 | 332 | } | ||
399 | 333 | |||
400 | 334 | static void output_scale(void* data, struct wl_output* wl_output, int32_t factor) | ||
401 | 335 | { | ||
402 | 336 | (void)data; | ||
403 | 337 | (void)wl_output; | ||
404 | 338 | printf("Output scale: %i\n", factor); | ||
405 | 339 | } | ||
406 | 340 | |||
407 | 341 | static struct wl_output_listener const output_listener = { | ||
408 | 342 | .geometry = &output_geometry, | ||
409 | 343 | .mode = &output_mode, | ||
410 | 344 | .done = &output_done, | ||
411 | 345 | .scale = &output_scale, | ||
412 | 346 | }; | ||
413 | 347 | |||
414 | 348 | int main() | ||
415 | 349 | { | ||
416 | 350 | struct wl_display* display = wl_display_connect(NULL); | ||
417 | 351 | struct globals* globals; | ||
418 | 352 | globals = calloc(sizeof *globals, 1); | ||
419 | 353 | |||
420 | 354 | struct wl_registry* registry = wl_display_get_registry(display); | ||
421 | 355 | |||
422 | 356 | wl_registry_add_listener(registry, ®istry_listener, globals); | ||
423 | 357 | |||
424 | 358 | wl_display_roundtrip(display); | ||
425 | 359 | |||
426 | 360 | struct wl_pointer* pointer = wl_seat_get_pointer(globals->seat); | ||
427 | 361 | wl_pointer_add_listener(pointer, &pointer_listener, NULL); | ||
428 | 362 | |||
429 | 363 | void* pool_data = NULL; | ||
430 | 364 | struct wl_shm_pool* shm_pool = make_shm_pool(globals->shm, 400 * 400 * 4, &pool_data); | ||
431 | 365 | |||
432 | 366 | struct draw_context* ctx = calloc(sizeof *ctx, 1); | ||
433 | 367 | |||
434 | 368 | for (int i = 0; i < 4; ++i) | ||
435 | 369 | { | ||
436 | 370 | ctx->buffers[i].buffer = wl_shm_pool_create_buffer(shm_pool, 0, 400, 400, 400, WL_SHM_FORMAT_ARGB8888); | ||
437 | 371 | ctx->buffers[i].available = true; | ||
438 | 372 | wl_buffer_add_listener(ctx->buffers[i].buffer, &buffer_listener, ctx); | ||
439 | 373 | } | ||
440 | 374 | |||
441 | 375 | ctx->display = display; | ||
442 | 376 | ctx->surface = wl_compositor_create_surface(globals->compositor); | ||
443 | 377 | ctx->content_area = pool_data; | ||
444 | 378 | |||
445 | 379 | struct wl_shell_surface* window = wl_shell_get_shell_surface(globals->shell, ctx->surface); | ||
446 | 380 | wl_shell_surface_set_toplevel(window); | ||
447 | 381 | |||
448 | 382 | struct wl_callback* first_frame = wl_display_sync(display); | ||
449 | 383 | wl_callback_add_listener(first_frame, &frame_listener, ctx); | ||
450 | 384 | |||
451 | 385 | wl_output_add_listener(globals->output, &output_listener, NULL); | ||
452 | 386 | |||
453 | 387 | while (wl_display_dispatch(display)) | ||
454 | 388 | ; | ||
455 | 389 | |||
456 | 390 | return 0; | ||
457 | 391 | } | ||
458 | 0 | 392 | ||
459 | === modified file 'include/platform/mir/graphics/egl_extensions.h' | |||
460 | --- include/platform/mir/graphics/egl_extensions.h 2017-07-28 17:00:43 +0000 | |||
461 | +++ include/platform/mir/graphics/egl_extensions.h 2017-09-07 05:58:57 +0000 | |||
462 | @@ -19,6 +19,8 @@ | |||
463 | 19 | #ifndef MIR_GRAPHICS_EGL_EXTENSIONS_H_ | 19 | #ifndef MIR_GRAPHICS_EGL_EXTENSIONS_H_ |
464 | 20 | #define MIR_GRAPHICS_EGL_EXTENSIONS_H_ | 20 | #define MIR_GRAPHICS_EGL_EXTENSIONS_H_ |
465 | 21 | 21 | ||
466 | 22 | #include <experimental/optional> | ||
467 | 23 | |||
468 | 22 | #define EGL_EGLEXT_PROTOTYPES | 24 | #define EGL_EGLEXT_PROTOTYPES |
469 | 23 | #include <EGL/egl.h> | 25 | #include <EGL/egl.h> |
470 | 24 | #include <EGL/eglext.h> | 26 | #include <EGL/eglext.h> |
471 | @@ -35,6 +37,15 @@ | |||
472 | 35 | PFNEGLCREATEIMAGEKHRPROC const eglCreateImageKHR; | 37 | PFNEGLCREATEIMAGEKHRPROC const eglCreateImageKHR; |
473 | 36 | PFNEGLDESTROYIMAGEKHRPROC const eglDestroyImageKHR; | 38 | PFNEGLDESTROYIMAGEKHRPROC const eglDestroyImageKHR; |
474 | 37 | PFNGLEGLIMAGETARGETTEXTURE2DOESPROC const glEGLImageTargetTexture2DOES; | 39 | PFNGLEGLIMAGETARGETTEXTURE2DOESPROC const glEGLImageTargetTexture2DOES; |
475 | 40 | |||
476 | 41 | struct WaylandExtensions | ||
477 | 42 | { | ||
478 | 43 | WaylandExtensions(); | ||
479 | 44 | |||
480 | 45 | PFNEGLBINDWAYLANDDISPLAYWL const eglBindWaylandDisplayWL; | ||
481 | 46 | PFNEGLQUERYWAYLANDBUFFERWL const eglQueryWaylandBufferWL; | ||
482 | 47 | }; | ||
483 | 48 | std::experimental::optional<WaylandExtensions> const wayland; | ||
484 | 38 | }; | 49 | }; |
485 | 39 | 50 | ||
486 | 40 | } | 51 | } |
487 | 41 | 52 | ||
488 | === added file 'include/platform/mir/graphics/wayland_allocator.h' | |||
489 | --- include/platform/mir/graphics/wayland_allocator.h 1970-01-01 00:00:00 +0000 | |||
490 | +++ include/platform/mir/graphics/wayland_allocator.h 2017-09-07 05:58:57 +0000 | |||
491 | @@ -0,0 +1,45 @@ | |||
492 | 1 | /* | ||
493 | 2 | * Copyright © 2017 Canonical Ltd. | ||
494 | 3 | * | ||
495 | 4 | * This program is free software: you can redistribute it and/or modify it | ||
496 | 5 | * under the terms of the GNU Lesser General Public License version 2 or 3, | ||
497 | 6 | * as published by the Free Software Foundation. | ||
498 | 7 | * | ||
499 | 8 | * This program is distributed in the hope that it will be useful, | ||
500 | 9 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
501 | 10 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
502 | 11 | * GNU Lesser General Public License for more details. | ||
503 | 12 | * | ||
504 | 13 | * You should have received a copy of the GNU Lesser General Public License | ||
505 | 14 | * along with this program. If not, see <http://www.gnu.org/licenses/>. | ||
506 | 15 | * | ||
507 | 16 | * Authored by: | ||
508 | 17 | * Christopher James Halse Rogers <christopher.halse.rogers@canonical.com> | ||
509 | 18 | */ | ||
510 | 19 | |||
511 | 20 | #ifndef MIR_PLATFORM_GRAPHICS_WAYLAND_ALLOCATOR_H_ | ||
512 | 21 | #define MIR_PLATFORM_GRAPHICS_WAYLAND_ALLOCATOR_H_ | ||
513 | 22 | |||
514 | 23 | #include <vector> | ||
515 | 24 | #include <memory> | ||
516 | 25 | |||
517 | 26 | #include <wayland-server-core.h> | ||
518 | 27 | |||
519 | 28 | namespace mir | ||
520 | 29 | { | ||
521 | 30 | namespace graphics | ||
522 | 31 | { | ||
523 | 32 | class Buffer; | ||
524 | 33 | |||
525 | 34 | class WaylandAllocator | ||
526 | 35 | { | ||
527 | 36 | public: | ||
528 | 37 | virtual ~WaylandAllocator() = default; | ||
529 | 38 | |||
530 | 39 | virtual void bind_display(wl_display* display) = 0; | ||
531 | 40 | virtual std::unique_ptr<Buffer> buffer_from_resource (wl_resource* buffer, std::function<void ()>&& on_consumed) = 0; | ||
532 | 41 | }; | ||
533 | 42 | } | ||
534 | 43 | } | ||
535 | 44 | |||
536 | 45 | #endif //MIR_PLATFORM_GRAPHICS_WAYLAND_ALLOCATOR_H_ | ||
537 | 0 | 46 | ||
538 | === modified file 'include/server/mir/frontend/buffer_stream.h' | |||
539 | --- include/server/mir/frontend/buffer_stream.h 2017-08-16 04:04:53 +0000 | |||
540 | +++ include/server/mir/frontend/buffer_stream.h 2017-09-07 05:58:57 +0000 | |||
541 | @@ -45,6 +45,7 @@ | |||
542 | 45 | virtual ~BufferStream() = default; | 45 | virtual ~BufferStream() = default; |
543 | 46 | 46 | ||
544 | 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; |
545 | 48 | virtual void resize(geometry::Size const& size) = 0; | ||
546 | 48 | 49 | ||
547 | 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; |
548 | 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; |
549 | 51 | 52 | ||
550 | === modified file 'include/test/mir/test/doubles/mock_egl.h' | |||
551 | --- include/test/mir/test/doubles/mock_egl.h 2017-07-28 17:00:43 +0000 | |||
552 | +++ include/test/mir/test/doubles/mock_egl.h 2017-09-07 05:58:57 +0000 | |||
553 | @@ -158,6 +158,13 @@ | |||
554 | 158 | int64_t*, int64_t*, | 158 | int64_t*, int64_t*, |
555 | 159 | int64_t*)); | 159 | int64_t*)); |
556 | 160 | 160 | ||
557 | 161 | MOCK_METHOD2(eglBindWaylandDisplayWL, | ||
558 | 162 | EGLBoolean(EGLDisplay, struct wl_display*)); | ||
559 | 163 | MOCK_METHOD2(eglUnbindWaylandDisplayWL, | ||
560 | 164 | EGLBoolean(EGLDisplay, struct wl_display*)); | ||
561 | 165 | MOCK_METHOD4(eglQueryWaylandBufferWL, | ||
562 | 166 | EGLBoolean(EGLDisplay, struct wl_resource*, EGLint, EGLint*)); | ||
563 | 167 | |||
564 | 161 | EGLDisplay const fake_egl_display; | 168 | EGLDisplay const fake_egl_display; |
565 | 162 | EGLConfig const* const fake_configs; | 169 | EGLConfig const* const fake_configs; |
566 | 163 | EGLint const fake_configs_num; | 170 | EGLint const fake_configs_num; |
567 | 164 | 171 | ||
568 | === modified file 'src/CMakeLists.txt' | |||
569 | --- src/CMakeLists.txt 2017-08-21 14:18:55 +0000 | |||
570 | +++ src/CMakeLists.txt 2017-09-07 05:58:57 +0000 | |||
571 | @@ -21,6 +21,7 @@ | |||
572 | 21 | add_subdirectory(capnproto/) | 21 | add_subdirectory(capnproto/) |
573 | 22 | add_subdirectory(common/) | 22 | add_subdirectory(common/) |
574 | 23 | add_subdirectory(protobuf/) | 23 | add_subdirectory(protobuf/) |
575 | 24 | add_subdirectory(protocol/) | ||
576 | 24 | include_directories(${MIR_GENERATED_INCLUDE_DIRECTORIES}) | 25 | include_directories(${MIR_GENERATED_INCLUDE_DIRECTORIES}) |
577 | 25 | 26 | ||
578 | 26 | add_subdirectory(platform/) | 27 | add_subdirectory(platform/) |
579 | 27 | 28 | ||
580 | === modified file 'src/include/server/mir/compositor/buffer_stream.h' | |||
581 | --- src/include/server/mir/compositor/buffer_stream.h 2017-07-28 17:00:43 +0000 | |||
582 | +++ src/include/server/mir/compositor/buffer_stream.h 2017-09-07 05:58:57 +0000 | |||
583 | @@ -45,7 +45,6 @@ | |||
584 | 45 | virtual std::shared_ptr<graphics::Buffer> | 45 | virtual std::shared_ptr<graphics::Buffer> |
585 | 46 | lock_compositor_buffer(void const* user_id) = 0; | 46 | lock_compositor_buffer(void const* user_id) = 0; |
586 | 47 | virtual geometry::Size stream_size() = 0; | 47 | virtual geometry::Size stream_size() = 0; |
587 | 48 | virtual void resize(geometry::Size const& size) = 0; | ||
588 | 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; |
589 | 50 | virtual void drop_old_buffers() = 0; | 49 | virtual void drop_old_buffers() = 0; |
590 | 51 | virtual bool has_submitted_buffer() const = 0; | 50 | virtual bool has_submitted_buffer() const = 0; |
591 | 52 | 51 | ||
592 | === modified file 'src/include/server/mir/default_server_configuration.h' | |||
593 | --- src/include/server/mir/default_server_configuration.h 2017-07-28 17:00:43 +0000 | |||
594 | +++ src/include/server/mir/default_server_configuration.h 2017-09-07 05:58:57 +0000 | |||
595 | @@ -175,6 +175,7 @@ | |||
596 | 175 | * dependencies of DisplayServer on the rest of the Mir | 175 | * dependencies of DisplayServer on the rest of the Mir |
597 | 176 | * @{ */ | 176 | * @{ */ |
598 | 177 | std::shared_ptr<frontend::Connector> the_connector() override; | 177 | std::shared_ptr<frontend::Connector> the_connector() override; |
599 | 178 | std::shared_ptr<frontend::Connector> the_wayland_connector() override; | ||
600 | 178 | std::shared_ptr<frontend::Connector> the_prompt_connector() override; | 179 | std::shared_ptr<frontend::Connector> the_prompt_connector() override; |
601 | 179 | std::shared_ptr<graphics::Display> the_display() override; | 180 | std::shared_ptr<graphics::Display> the_display() override; |
602 | 180 | std::shared_ptr<compositor::Compositor> the_compositor() override; | 181 | std::shared_ptr<compositor::Compositor> the_compositor() override; |
603 | @@ -375,6 +376,7 @@ | |||
604 | 375 | /** @} */ | 376 | /** @} */ |
605 | 376 | 377 | ||
606 | 377 | CachedPtr<frontend::Connector> connector; | 378 | CachedPtr<frontend::Connector> connector; |
607 | 379 | CachedPtr<frontend::Connector> wayland_connector; | ||
608 | 378 | CachedPtr<frontend::Connector> prompt_connector; | 380 | CachedPtr<frontend::Connector> prompt_connector; |
609 | 379 | 381 | ||
610 | 380 | CachedPtr<input::InputReport> input_report; | 382 | CachedPtr<input::InputReport> input_report; |
611 | 381 | 383 | ||
612 | === modified file 'src/include/server/mir/server_configuration.h' | |||
613 | --- src/include/server/mir/server_configuration.h 2017-07-28 17:00:43 +0000 | |||
614 | +++ src/include/server/mir/server_configuration.h 2017-09-07 05:58:57 +0000 | |||
615 | @@ -69,6 +69,7 @@ | |||
616 | 69 | // TODO most of these interfaces are wider DisplayServer needs... | 69 | // TODO most of these interfaces are wider DisplayServer needs... |
617 | 70 | // TODO ...some or all of them need narrowing | 70 | // TODO ...some or all of them need narrowing |
618 | 71 | virtual std::shared_ptr<frontend::Connector> the_connector() = 0; | 71 | virtual std::shared_ptr<frontend::Connector> the_connector() = 0; |
619 | 72 | virtual std::shared_ptr<frontend::Connector> the_wayland_connector() = 0; | ||
620 | 72 | virtual std::shared_ptr<frontend::Connector> the_prompt_connector() = 0; | 73 | virtual std::shared_ptr<frontend::Connector> the_prompt_connector() = 0; |
621 | 73 | virtual std::shared_ptr<graphics::Display> the_display() = 0; | 74 | virtual std::shared_ptr<graphics::Display> the_display() = 0; |
622 | 74 | virtual std::shared_ptr<compositor::Compositor> the_compositor() = 0; | 75 | virtual std::shared_ptr<compositor::Compositor> the_compositor() = 0; |
623 | 75 | 76 | ||
624 | === modified file 'src/platform/graphics/CMakeLists.txt' | |||
625 | --- src/platform/graphics/CMakeLists.txt 2017-05-08 03:04:26 +0000 | |||
626 | +++ src/platform/graphics/CMakeLists.txt 2017-09-07 05:58:57 +0000 | |||
627 | @@ -14,6 +14,7 @@ | |||
628 | 14 | platform_probe.cpp | 14 | platform_probe.cpp |
629 | 15 | atomic_frame.cpp | 15 | atomic_frame.cpp |
630 | 16 | ${PROJECT_SOURCE_DIR}/include/platform/mir/graphics/display.h | 16 | ${PROJECT_SOURCE_DIR}/include/platform/mir/graphics/display.h |
631 | 17 | ${PROJECT_SOURCE_DIR}/include/platform/mir/graphics/wayland_allocator.h | ||
632 | 17 | ) | 18 | ) |
633 | 18 | 19 | ||
634 | 19 | add_library(mirplatformgraphicscommon OBJECT | 20 | add_library(mirplatformgraphicscommon OBJECT |
635 | 20 | 21 | ||
636 | === modified file 'src/platform/graphics/egl_extensions.cpp' | |||
637 | --- src/platform/graphics/egl_extensions.cpp 2017-07-28 17:00:43 +0000 | |||
638 | +++ src/platform/graphics/egl_extensions.cpp 2017-09-07 05:58:57 +0000 | |||
639 | @@ -21,8 +21,26 @@ | |||
640 | 21 | #include <boost/throw_exception.hpp> | 21 | #include <boost/throw_exception.hpp> |
641 | 22 | #include <stdexcept> | 22 | #include <stdexcept> |
642 | 23 | 23 | ||
643 | 24 | #define MIR_LOG_COMPONENT "EGL extensions" | ||
644 | 25 | #include "mir/log.h" | ||
645 | 26 | |||
646 | 24 | namespace mg=mir::graphics; | 27 | namespace mg=mir::graphics; |
647 | 25 | 28 | ||
648 | 29 | namespace | ||
649 | 30 | { | ||
650 | 31 | std::experimental::optional<mg::EGLExtensions::WaylandExtensions> maybe_wayland_ext() | ||
651 | 32 | { | ||
652 | 33 | try | ||
653 | 34 | { | ||
654 | 35 | return mg::EGLExtensions::WaylandExtensions{}; | ||
655 | 36 | } | ||
656 | 37 | catch (std::runtime_error const&) | ||
657 | 38 | { | ||
658 | 39 | return {}; | ||
659 | 40 | } | ||
660 | 41 | } | ||
661 | 42 | } | ||
662 | 43 | |||
663 | 26 | mg::EGLExtensions::EGLExtensions() : | 44 | mg::EGLExtensions::EGLExtensions() : |
664 | 27 | eglCreateImageKHR{ | 45 | eglCreateImageKHR{ |
665 | 28 | reinterpret_cast<PFNEGLCREATEIMAGEKHRPROC>(eglGetProcAddress("eglCreateImageKHR"))}, | 46 | reinterpret_cast<PFNEGLCREATEIMAGEKHRPROC>(eglGetProcAddress("eglCreateImageKHR"))}, |
666 | @@ -36,7 +54,8 @@ | |||
667 | 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. |
668 | 37 | */ | 55 | */ |
669 | 38 | glEGLImageTargetTexture2DOES{ | 56 | glEGLImageTargetTexture2DOES{ |
671 | 39 | reinterpret_cast<PFNGLEGLIMAGETARGETTEXTURE2DOESPROC>(eglGetProcAddress("glEGLImageTargetTexture2DOES"))} | 57 | reinterpret_cast<PFNGLEGLIMAGETARGETTEXTURE2DOESPROC>(eglGetProcAddress("glEGLImageTargetTexture2DOES"))}, |
672 | 58 | wayland{maybe_wayland_ext()} | ||
673 | 40 | { | 59 | { |
674 | 41 | if (!eglCreateImageKHR || !eglDestroyImageKHR) | 60 | if (!eglCreateImageKHR || !eglDestroyImageKHR) |
675 | 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")); |
676 | @@ -44,3 +63,17 @@ | |||
677 | 44 | if (!glEGLImageTargetTexture2DOES) | 63 | if (!glEGLImageTargetTexture2DOES) |
678 | 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")); |
679 | 46 | } | 65 | } |
680 | 66 | |||
681 | 67 | mg::EGLExtensions::WaylandExtensions::WaylandExtensions() : | ||
682 | 68 | eglBindWaylandDisplayWL{ | ||
683 | 69 | reinterpret_cast<PFNEGLBINDWAYLANDDISPLAYWL>(eglGetProcAddress("eglBindWaylandDisplayWL")) | ||
684 | 70 | }, | ||
685 | 71 | eglQueryWaylandBufferWL{ | ||
686 | 72 | reinterpret_cast<PFNEGLQUERYWAYLANDBUFFERWL>(eglGetProcAddress("eglQueryWaylandBufferWL")) | ||
687 | 73 | } | ||
688 | 74 | { | ||
689 | 75 | if (!eglBindWaylandDisplayWL || !eglQueryWaylandBufferWL) | ||
690 | 76 | { | ||
691 | 77 | BOOST_THROW_EXCEPTION(std::runtime_error("EGL implementation doesn't support EGL_WL_bind_wayland_display")); | ||
692 | 78 | } | ||
693 | 79 | } | ||
694 | 47 | \ No newline at end of file | 80 | \ No newline at end of file |
695 | 48 | 81 | ||
696 | === modified file 'src/platforms/mesa/server/CMakeLists.txt' | |||
697 | --- src/platforms/mesa/server/CMakeLists.txt 2017-05-17 04:48:46 +0000 | |||
698 | +++ src/platforms/mesa/server/CMakeLists.txt 2017-09-07 05:58:57 +0000 | |||
699 | @@ -8,6 +8,7 @@ | |||
700 | 8 | include_directories( | 8 | include_directories( |
701 | 9 | ${server_common_include_dirs} | 9 | ${server_common_include_dirs} |
702 | 10 | ${DRM_INCLUDE_DIRS} | 10 | ${DRM_INCLUDE_DIRS} |
703 | 11 | ${WAYLAND_SERVER_INCLUDE_DIRS} | ||
704 | 11 | ) | 12 | ) |
705 | 12 | 13 | ||
706 | 13 | add_library( | 14 | add_library( |
707 | @@ -30,4 +31,5 @@ | |||
708 | 30 | 31 | ||
709 | 31 | server_platform_common | 32 | server_platform_common |
710 | 32 | kms_utils | 33 | kms_utils |
711 | 34 | ${WAYLAND_SERVER_LDFLAGS} ${WAYLAND_SERVER_LIBRARIES} | ||
712 | 33 | ) | 35 | ) |
713 | 34 | 36 | ||
714 | === modified file 'src/platforms/mesa/server/buffer_allocator.cpp' | |||
715 | --- src/platforms/mesa/server/buffer_allocator.cpp 2017-07-28 17:00:43 +0000 | |||
716 | +++ src/platforms/mesa/server/buffer_allocator.cpp 2017-09-07 05:58:57 +0000 | |||
717 | @@ -44,6 +44,12 @@ | |||
718 | 44 | #include <cassert> | 44 | #include <cassert> |
719 | 45 | #include <fcntl.h> | 45 | #include <fcntl.h> |
720 | 46 | 46 | ||
721 | 47 | #include <wayland-server.h> | ||
722 | 48 | |||
723 | 49 | #define MIR_LOG_COMPONENT "mesa-buffer-allocator" | ||
724 | 50 | #include <mir/log.h> | ||
725 | 51 | #include <mutex> | ||
726 | 52 | |||
727 | 47 | namespace mg = mir::graphics; | 53 | namespace mg = mir::graphics; |
728 | 48 | namespace mgm = mg::mesa; | 54 | namespace mgm = mg::mesa; |
729 | 49 | namespace mgc = mg::common; | 55 | namespace mgc = mg::common; |
730 | @@ -317,3 +323,228 @@ | |||
731 | 317 | 323 | ||
732 | 318 | return pixel_formats; | 324 | return pixel_formats; |
733 | 319 | } | 325 | } |
734 | 326 | |||
735 | 327 | namespace | ||
736 | 328 | { | ||
737 | 329 | class WaylandBuffer : | ||
738 | 330 | public mir::graphics::BufferBasic, | ||
739 | 331 | public mir::graphics::NativeBufferBase, | ||
740 | 332 | public mir::renderer::gl::TextureSource | ||
741 | 333 | { | ||
742 | 334 | public: | ||
743 | 335 | WaylandBuffer( | ||
744 | 336 | EGLDisplay dpy, | ||
745 | 337 | wl_resource* buffer, | ||
746 | 338 | std::shared_ptr<mg::EGLExtensions> const& extensions, | ||
747 | 339 | std::function<void()>&& on_consumed) | ||
748 | 340 | : buffer{buffer}, | ||
749 | 341 | dpy{dpy}, | ||
750 | 342 | egl_image{EGL_NO_IMAGE_KHR}, | ||
751 | 343 | extensions{extensions}, | ||
752 | 344 | on_consumed{std::move(on_consumed)} | ||
753 | 345 | { | ||
754 | 346 | if (auto notifier = wl_resource_get_destroy_listener(buffer, &on_buffer_destroyed)) | ||
755 | 347 | { | ||
756 | 348 | DestructionShim* shim; | ||
757 | 349 | |||
758 | 350 | shim = wl_container_of(notifier, shim, destruction_listener); | ||
759 | 351 | |||
760 | 352 | if (shim->associated_buffer) | ||
761 | 353 | BOOST_THROW_EXCEPTION(std::logic_error("Attempt to associate a single wl_buffer with multiple WaylandBuffer wrappers")); | ||
762 | 354 | |||
763 | 355 | shim->associated_buffer = this; | ||
764 | 356 | buffer_mutex = shim->mutex; | ||
765 | 357 | } | ||
766 | 358 | else | ||
767 | 359 | { | ||
768 | 360 | auto shim = new DestructionShim; | ||
769 | 361 | shim->destruction_listener.notify = &on_buffer_destroyed; | ||
770 | 362 | shim->associated_buffer = this; | ||
771 | 363 | buffer_mutex = shim->mutex; | ||
772 | 364 | |||
773 | 365 | wl_resource_add_destroy_listener(buffer, &shim->destruction_listener); | ||
774 | 366 | } | ||
775 | 367 | |||
776 | 368 | if (extensions->wayland->eglQueryWaylandBufferWL(dpy, buffer, EGL_WIDTH, &width) == EGL_FALSE) | ||
777 | 369 | { | ||
778 | 370 | BOOST_THROW_EXCEPTION(mg::egl_error("Failed to query WaylandAllocator buffer width")); | ||
779 | 371 | } | ||
780 | 372 | if (extensions->wayland->eglQueryWaylandBufferWL(dpy, buffer, EGL_HEIGHT, &height) == EGL_FALSE) | ||
781 | 373 | { | ||
782 | 374 | BOOST_THROW_EXCEPTION(mg::egl_error("Failed to query WaylandAllocator buffer height")); | ||
783 | 375 | } | ||
784 | 376 | |||
785 | 377 | EGLint texture_format; | ||
786 | 378 | if (!extensions->wayland->eglQueryWaylandBufferWL(dpy, buffer, EGL_TEXTURE_FORMAT, &texture_format)) | ||
787 | 379 | { | ||
788 | 380 | BOOST_THROW_EXCEPTION(mg::egl_error("Failed to query WL buffer format")); | ||
789 | 381 | } | ||
790 | 382 | |||
791 | 383 | if (texture_format == EGL_TEXTURE_RGB) | ||
792 | 384 | { | ||
793 | 385 | format = mir_pixel_format_xrgb_8888; | ||
794 | 386 | } | ||
795 | 387 | else if (texture_format == EGL_TEXTURE_RGBA) | ||
796 | 388 | { | ||
797 | 389 | format = mir_pixel_format_argb_8888; | ||
798 | 390 | } | ||
799 | 391 | else | ||
800 | 392 | { | ||
801 | 393 | BOOST_THROW_EXCEPTION((std::invalid_argument{"YUV buffers are unimplemented"})); | ||
802 | 394 | } | ||
803 | 395 | } | ||
804 | 396 | |||
805 | 397 | ~WaylandBuffer() | ||
806 | 398 | { | ||
807 | 399 | if (egl_image != EGL_NO_IMAGE_KHR) | ||
808 | 400 | extensions->eglDestroyImageKHR(dpy, egl_image); | ||
809 | 401 | |||
810 | 402 | std::lock_guard<std::mutex> lock{*buffer_mutex}; | ||
811 | 403 | if (buffer) | ||
812 | 404 | { | ||
813 | 405 | wl_resource_queue_event(buffer, WL_BUFFER_RELEASE); | ||
814 | 406 | auto notifier = wl_resource_get_destroy_listener(buffer, &on_buffer_destroyed); | ||
815 | 407 | DestructionShim* shim; | ||
816 | 408 | |||
817 | 409 | shim = wl_container_of(notifier, shim, destruction_listener); | ||
818 | 410 | |||
819 | 411 | shim->associated_buffer = nullptr; | ||
820 | 412 | } | ||
821 | 413 | } | ||
822 | 414 | |||
823 | 415 | void gl_bind_to_texture() override | ||
824 | 416 | { | ||
825 | 417 | std::unique_lock<std::mutex> lock{*buffer_mutex}; | ||
826 | 418 | if (buffer == nullptr) | ||
827 | 419 | { | ||
828 | 420 | mir::log_warning("WaylandBuffer::gl_bind_to_texture() called on a destroyed wl_buffer", this); | ||
829 | 421 | return; | ||
830 | 422 | } | ||
831 | 423 | if (egl_image == EGL_NO_IMAGE_KHR) | ||
832 | 424 | { | ||
833 | 425 | eglBindAPI(MIR_SERVER_EGL_OPENGL_API); | ||
834 | 426 | |||
835 | 427 | const EGLint image_attrs[] = | ||
836 | 428 | { | ||
837 | 429 | EGL_IMAGE_PRESERVED_KHR, EGL_TRUE, | ||
838 | 430 | EGL_NONE | ||
839 | 431 | }; | ||
840 | 432 | |||
841 | 433 | egl_image = extensions->eglCreateImageKHR( | ||
842 | 434 | dpy, | ||
843 | 435 | EGL_NO_CONTEXT, | ||
844 | 436 | EGL_WAYLAND_BUFFER_WL, | ||
845 | 437 | buffer, | ||
846 | 438 | image_attrs); | ||
847 | 439 | |||
848 | 440 | if (egl_image == EGL_NO_IMAGE_KHR) | ||
849 | 441 | BOOST_THROW_EXCEPTION(mg::egl_error("Failed to create EGLImage")); | ||
850 | 442 | |||
851 | 443 | on_consumed(); | ||
852 | 444 | } | ||
853 | 445 | lock.unlock(); | ||
854 | 446 | |||
855 | 447 | extensions->glEGLImageTargetTexture2DOES(GL_TEXTURE_2D, egl_image); | ||
856 | 448 | } | ||
857 | 449 | |||
858 | 450 | void bind() override | ||
859 | 451 | { | ||
860 | 452 | gl_bind_to_texture(); | ||
861 | 453 | } | ||
862 | 454 | |||
863 | 455 | void secure_for_render() override | ||
864 | 456 | { | ||
865 | 457 | } | ||
866 | 458 | |||
867 | 459 | std::shared_ptr<mir::graphics::NativeBuffer> native_buffer_handle() const override | ||
868 | 460 | { | ||
869 | 461 | return nullptr; | ||
870 | 462 | } | ||
871 | 463 | |||
872 | 464 | mir::geometry::Size size() const override | ||
873 | 465 | { | ||
874 | 466 | return mir::geometry::Size{width, height}; | ||
875 | 467 | } | ||
876 | 468 | |||
877 | 469 | MirPixelFormat pixel_format() const override | ||
878 | 470 | { | ||
879 | 471 | return format; | ||
880 | 472 | } | ||
881 | 473 | |||
882 | 474 | mir::graphics::NativeBufferBase *native_buffer_base() override | ||
883 | 475 | { | ||
884 | 476 | return this; | ||
885 | 477 | } | ||
886 | 478 | |||
887 | 479 | private: | ||
888 | 480 | static void on_buffer_destroyed(wl_listener* listener, void*) | ||
889 | 481 | { | ||
890 | 482 | static_assert( | ||
891 | 483 | std::is_standard_layout<DestructionShim>::value, | ||
892 | 484 | "DestructionShim must be Standard Layout for wl_container_of to be defined behaviour"); | ||
893 | 485 | |||
894 | 486 | DestructionShim* shim; | ||
895 | 487 | shim = wl_container_of(listener, shim, destruction_listener); | ||
896 | 488 | |||
897 | 489 | { | ||
898 | 490 | std::lock_guard<std::mutex> lock{*shim->mutex}; | ||
899 | 491 | if (shim->associated_buffer) | ||
900 | 492 | { | ||
901 | 493 | shim->associated_buffer->buffer = nullptr; | ||
902 | 494 | } | ||
903 | 495 | } | ||
904 | 496 | |||
905 | 497 | delete shim; | ||
906 | 498 | } | ||
907 | 499 | |||
908 | 500 | struct DestructionShim | ||
909 | 501 | { | ||
910 | 502 | std::shared_ptr<std::mutex> const mutex = std::make_shared<std::mutex>(); | ||
911 | 503 | WaylandBuffer* associated_buffer; | ||
912 | 504 | wl_listener destruction_listener; | ||
913 | 505 | }; | ||
914 | 506 | |||
915 | 507 | std::shared_ptr<std::mutex> buffer_mutex; | ||
916 | 508 | wl_resource* buffer; | ||
917 | 509 | |||
918 | 510 | EGLDisplay dpy; | ||
919 | 511 | EGLImageKHR egl_image; | ||
920 | 512 | |||
921 | 513 | EGLint width, height; | ||
922 | 514 | MirPixelFormat format; | ||
923 | 515 | |||
924 | 516 | std::shared_ptr<mg::EGLExtensions> const extensions; | ||
925 | 517 | |||
926 | 518 | std::function<void()> on_consumed; | ||
927 | 519 | }; | ||
928 | 520 | } | ||
929 | 521 | |||
930 | 522 | void mgm::BufferAllocator::bind_display(wl_display* display) | ||
931 | 523 | { | ||
932 | 524 | dpy = eglGetCurrentDisplay(); | ||
933 | 525 | |||
934 | 526 | if (dpy == EGL_NO_DISPLAY) | ||
935 | 527 | BOOST_THROW_EXCEPTION((std::logic_error{"WaylandAllocator::bind_display called without an active EGL Display"})); | ||
936 | 528 | |||
937 | 529 | if (!egl_extensions->wayland) | ||
938 | 530 | { | ||
939 | 531 | mir::log_warning("No EGL_WL_bind_wayland_display support"); | ||
940 | 532 | return; | ||
941 | 533 | } | ||
942 | 534 | |||
943 | 535 | if (egl_extensions->wayland->eglBindWaylandDisplayWL(dpy, display) == EGL_FALSE) | ||
944 | 536 | { | ||
945 | 537 | BOOST_THROW_EXCEPTION(mg::egl_error("Failed to bind Wayland display")); | ||
946 | 538 | } | ||
947 | 539 | else | ||
948 | 540 | { | ||
949 | 541 | mir::log_info("Bound WaylandAllocator display"); | ||
950 | 542 | } | ||
951 | 543 | } | ||
952 | 544 | |||
953 | 545 | std::unique_ptr<mg::Buffer> mgm::BufferAllocator::buffer_from_resource (wl_resource* buffer, std::function<void ()>&& on_consumed) | ||
954 | 546 | { | ||
955 | 547 | if (egl_extensions->wayland) | ||
956 | 548 | return std::make_unique<WaylandBuffer>(dpy, buffer, egl_extensions, std::move(on_consumed)); | ||
957 | 549 | return nullptr; | ||
958 | 550 | } | ||
959 | 320 | 551 | ||
960 | === modified file 'src/platforms/mesa/server/buffer_allocator.h' | |||
961 | --- src/platforms/mesa/server/buffer_allocator.h 2017-07-28 17:00:43 +0000 | |||
962 | +++ src/platforms/mesa/server/buffer_allocator.h 2017-09-07 05:58:57 +0000 | |||
963 | @@ -22,6 +22,7 @@ | |||
964 | 22 | #include "platform_common.h" | 22 | #include "platform_common.h" |
965 | 23 | #include "mir/graphics/graphic_buffer_allocator.h" | 23 | #include "mir/graphics/graphic_buffer_allocator.h" |
966 | 24 | #include "mir/graphics/buffer_id.h" | 24 | #include "mir/graphics/buffer_id.h" |
967 | 25 | #include "mir/graphics/wayland_allocator.h" | ||
968 | 25 | #include "mir_toolkit/mir_native_buffer.h" | 26 | #include "mir_toolkit/mir_native_buffer.h" |
969 | 26 | 27 | ||
970 | 27 | #pragma GCC diagnostic push | 28 | #pragma GCC diagnostic push |
971 | @@ -29,6 +30,8 @@ | |||
972 | 29 | #include <gbm.h> | 30 | #include <gbm.h> |
973 | 30 | #pragma GCC diagnostic pop | 31 | #pragma GCC diagnostic pop |
974 | 31 | 32 | ||
975 | 33 | #include <EGL/egl.h> | ||
976 | 34 | |||
977 | 32 | #include <memory> | 35 | #include <memory> |
978 | 33 | 36 | ||
979 | 34 | namespace mir | 37 | namespace mir |
980 | @@ -46,7 +49,9 @@ | |||
981 | 46 | dma_buf | 49 | dma_buf |
982 | 47 | }; | 50 | }; |
983 | 48 | 51 | ||
985 | 49 | class BufferAllocator: public graphics::GraphicBufferAllocator | 52 | class BufferAllocator: |
986 | 53 | public graphics::GraphicBufferAllocator, | ||
987 | 54 | public graphics::WaylandAllocator | ||
988 | 50 | { | 55 | { |
989 | 51 | public: | 56 | public: |
990 | 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); |
991 | @@ -57,10 +62,13 @@ | |||
992 | 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; |
993 | 58 | std::vector<MirPixelFormat> supported_pixel_formats() override; | 63 | std::vector<MirPixelFormat> supported_pixel_formats() override; |
994 | 59 | 64 | ||
995 | 65 | void bind_display(wl_display* display) override; | ||
996 | 66 | std::unique_ptr<Buffer> buffer_from_resource (wl_resource* buffer, std::function<void ()>&& on_consumed) override; | ||
997 | 60 | private: | 67 | private: |
998 | 61 | std::shared_ptr<Buffer> alloc_hardware_buffer( | 68 | std::shared_ptr<Buffer> alloc_hardware_buffer( |
999 | 62 | graphics::BufferProperties const& buffer_properties); | 69 | graphics::BufferProperties const& buffer_properties); |
1000 | 63 | 70 | ||
1001 | 71 | EGLDisplay dpy; | ||
1002 | 64 | gbm_device* const device; | 72 | gbm_device* const device; |
1003 | 65 | std::shared_ptr<EGLExtensions> const egl_extensions; | 73 | std::shared_ptr<EGLExtensions> const egl_extensions; |
1004 | 66 | 74 | ||
1005 | 67 | 75 | ||
1006 | === modified file 'src/platforms/mesa/server/kms/CMakeLists.txt' | |||
1007 | --- src/platforms/mesa/server/kms/CMakeLists.txt 2017-06-09 19:25:54 +0000 | |||
1008 | +++ src/platforms/mesa/server/kms/CMakeLists.txt 2017-09-07 05:58:57 +0000 | |||
1009 | @@ -9,6 +9,7 @@ | |||
1010 | 9 | ${GBM_INCLUDE_DIRS} | 9 | ${GBM_INCLUDE_DIRS} |
1011 | 10 | ${EGL_INCLUDE_DIRS} | 10 | ${EGL_INCLUDE_DIRS} |
1012 | 11 | ${GL_INCLUDE_DIRS} | 11 | ${GL_INCLUDE_DIRS} |
1013 | 12 | ${WAYLAND_SERVER_INCLUDE_DIRS} | ||
1014 | 12 | ${UDEV_INCLUDE_DIRS} | 13 | ${UDEV_INCLUDE_DIRS} |
1015 | 13 | ${PROJECT_SOURCE_DIR}/include/client | 14 | ${PROJECT_SOURCE_DIR}/include/client |
1016 | 14 | ) | 15 | ) |
1017 | @@ -65,6 +66,7 @@ | |||
1018 | 65 | ${GBM_LDFLAGS} ${GBM_LIBRARIES} | 66 | ${GBM_LDFLAGS} ${GBM_LIBRARIES} |
1019 | 66 | ${EGL_LDFLAGS} ${EGL_LIBRARIES} | 67 | ${EGL_LDFLAGS} ${EGL_LIBRARIES} |
1020 | 67 | ${GL_LDFLAGS} ${GL_LIBRARIES} | 68 | ${GL_LDFLAGS} ${GL_LIBRARIES} |
1021 | 69 | ${WAYLAND_SERVER_LDFLAGS} ${WAYLAND_SERVER_LIBRARIES} | ||
1022 | 68 | ) | 70 | ) |
1023 | 69 | 71 | ||
1024 | 70 | set_target_properties( | 72 | set_target_properties( |
1025 | 71 | 73 | ||
1026 | === modified file 'src/platforms/mesa/server/kms/platform.cpp' | |||
1027 | --- src/platforms/mesa/server/kms/platform.cpp 2017-07-28 17:00:43 +0000 | |||
1028 | +++ src/platforms/mesa/server/kms/platform.cpp 2017-09-07 05:58:57 +0000 | |||
1029 | @@ -29,9 +29,23 @@ | |||
1030 | 29 | #include "mir/emergency_cleanup_registry.h" | 29 | #include "mir/emergency_cleanup_registry.h" |
1031 | 30 | #include "mir/udev/wrapper.h" | 30 | #include "mir/udev/wrapper.h" |
1032 | 31 | #include "mesa_extensions.h" | 31 | #include "mesa_extensions.h" |
1033 | 32 | #include "mir/renderer/gl/texture_target.h" | ||
1034 | 33 | #include "mir/graphics/buffer_basic.h" | ||
1035 | 34 | #include "mir/graphics/egl_error.h" | ||
1036 | 35 | |||
1037 | 36 | #include <EGL/egl.h> | ||
1038 | 37 | #include <EGL/eglext.h> | ||
1039 | 38 | #include MIR_SERVER_GL_H | ||
1040 | 39 | #include MIR_SERVER_GLEXT_H | ||
1041 | 40 | |||
1042 | 41 | #define MIR_LOG_COMPONENT "platform-graphics-mesa" | ||
1043 | 42 | #include "mir/log.h" | ||
1044 | 32 | 43 | ||
1045 | 33 | #include <boost/throw_exception.hpp> | 44 | #include <boost/throw_exception.hpp> |
1046 | 34 | #include <stdexcept> | 45 | #include <stdexcept> |
1047 | 46 | #include <mir/renderer/gl/texture_source.h> | ||
1048 | 47 | #include <wayland-server-core.h> | ||
1049 | 48 | #include <wayland-server-protocol.h> | ||
1050 | 35 | 49 | ||
1051 | 36 | namespace mg = mir::graphics; | 50 | namespace mg = mir::graphics; |
1052 | 37 | namespace mgm = mg::mesa; | 51 | namespace mgm = mg::mesa; |
1053 | 38 | 52 | ||
1054 | === added directory 'src/protocol' | |||
1055 | === added file 'src/protocol/CMakeLists.txt' | |||
1056 | --- src/protocol/CMakeLists.txt 1970-01-01 00:00:00 +0000 | |||
1057 | +++ src/protocol/CMakeLists.txt 2017-09-07 05:58:57 +0000 | |||
1058 | @@ -0,0 +1,28 @@ | |||
1059 | 1 | pkg_check_modules(XMLPP libxml++-2.6 REQUIRED) | ||
1060 | 2 | |||
1061 | 3 | include_directories(SYSTEM ${XMLPP_INCLUDE_DIRS}) | ||
1062 | 4 | |||
1063 | 5 | add_executable(wrapper-generator | ||
1064 | 6 | |||
1065 | 7 | wrapper_generator.cpp | ||
1066 | 8 | ) | ||
1067 | 9 | |||
1068 | 10 | target_link_libraries(wrapper-generator | ||
1069 | 11 | |||
1070 | 12 | ${XMLPP_LDFLAGS} ${XMLPP_LIBRARIES} | ||
1071 | 13 | ) | ||
1072 | 14 | |||
1073 | 15 | get_filename_component( | ||
1074 | 16 | GENERATED_HEADER src/server/frontend/wayland/core_generated_interfaces.h | ||
1075 | 17 | ABSOLUTE | ||
1076 | 18 | BASE_DIR ${PROJECT_SOURCE_DIR} | ||
1077 | 19 | ) | ||
1078 | 20 | |||
1079 | 21 | add_custom_target(refresh-wayland-wrapper | ||
1080 | 22 | COMMAND "sh" "-c" "${CMAKE_BINARY_DIR}/bin/wrapper-generator wl_ /usr/share/wayland/wayland.xml >${GENERATED_HEADER}" | ||
1081 | 23 | VERBATIM | ||
1082 | 24 | DEPENDS wrapper-generator | ||
1083 | 25 | DEPENDS /usr/share/wayland/wayland.xml | ||
1084 | 26 | SOURCES ${GENERATED_HEADER} | ||
1085 | 27 | ) | ||
1086 | 28 | |||
1087 | 0 | 29 | ||
1088 | === added file 'src/protocol/wrapper_generator.cpp' | |||
1089 | --- src/protocol/wrapper_generator.cpp 1970-01-01 00:00:00 +0000 | |||
1090 | +++ src/protocol/wrapper_generator.cpp 2017-09-07 05:58:57 +0000 | |||
1091 | @@ -0,0 +1,509 @@ | |||
1092 | 1 | /* | ||
1093 | 2 | * Copyright © 2017 Canonical Ltd. | ||
1094 | 3 | * | ||
1095 | 4 | * This program is free software: you can redistribute it and/or modify it | ||
1096 | 5 | * under the terms of the GNU General Public License version 2 or 3, | ||
1097 | 6 | * as published by the Free Software Foundation. | ||
1098 | 7 | * | ||
1099 | 8 | * This program is distributed in the hope that it will be useful, | ||
1100 | 9 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
1101 | 10 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
1102 | 11 | * GNU General Public License for more details. | ||
1103 | 12 | * | ||
1104 | 13 | * You should have received a copy of the GNU General Public License | ||
1105 | 14 | * along with this program. If not, see <http://www.gnu.org/licenses/>. | ||
1106 | 15 | * | ||
1107 | 16 | * Authored By: Christopher James Halse Rogers <christopher.halse.rogers@canonical.com> | ||
1108 | 17 | */ | ||
1109 | 18 | |||
1110 | 19 | #include <libxml++/libxml++.h> | ||
1111 | 20 | #include <iostream> | ||
1112 | 21 | #include <unordered_map> | ||
1113 | 22 | #include <unordered_set> | ||
1114 | 23 | #include <functional> | ||
1115 | 24 | |||
1116 | 25 | #include <experimental/optional> | ||
1117 | 26 | #include <vector> | ||
1118 | 27 | #include <locale> | ||
1119 | 28 | #include <stdio.h> | ||
1120 | 29 | |||
1121 | 30 | void emit_comment_header(std::ostream& out) | ||
1122 | 31 | { | ||
1123 | 32 | out << "/*" << std::endl; | ||
1124 | 33 | out << " * AUTOGENERATED - DO NOT EDIT" << std::endl; | ||
1125 | 34 | out << " *" << std::endl; | ||
1126 | 35 | out << " * This header is generated by src/protocol/wrapper_generator.cpp" << std::endl; | ||
1127 | 36 | out << " * To regenerate, run the “refresh-wayland-wrapper” target." << std::endl; | ||
1128 | 37 | out << " */" << std::endl; | ||
1129 | 38 | } | ||
1130 | 39 | |||
1131 | 40 | void emit_required_headers() | ||
1132 | 41 | { | ||
1133 | 42 | std::cout << "#include <experimental/optional>" << std::endl; | ||
1134 | 43 | std::cout << "#include <boost/throw_exception.hpp>" << std::endl; | ||
1135 | 44 | std::cout << std::endl; | ||
1136 | 45 | std::cout << "#include <wayland-server.h>" << std::endl; | ||
1137 | 46 | std::cout << "#include <wayland-server-protocol.h>" << std::endl; | ||
1138 | 47 | std::cout << std::endl; | ||
1139 | 48 | std::cout << "#include \"mir/fd.h\"" << std::endl; | ||
1140 | 49 | } | ||
1141 | 50 | |||
1142 | 51 | std::string strip_wl_prefix(std::string const& name) | ||
1143 | 52 | { | ||
1144 | 53 | return name.substr(3); | ||
1145 | 54 | } | ||
1146 | 55 | |||
1147 | 56 | std::string camel_case_string(std::string const& name) | ||
1148 | 57 | { | ||
1149 | 58 | std::string camel_cased_name; | ||
1150 | 59 | camel_cased_name = std::string{std::toupper(name[0], std::locale("C"))} + name.substr(1); | ||
1151 | 60 | auto next_underscore_offset = name.find('_'); | ||
1152 | 61 | while (next_underscore_offset != std::string::npos) | ||
1153 | 62 | { | ||
1154 | 63 | if (next_underscore_offset < camel_cased_name.length()) | ||
1155 | 64 | { | ||
1156 | 65 | camel_cased_name = camel_cased_name.substr(0, next_underscore_offset) + | ||
1157 | 66 | std::toupper(camel_cased_name[next_underscore_offset + 1], std::locale("C")) + | ||
1158 | 67 | camel_cased_name.substr(next_underscore_offset + 2); | ||
1159 | 68 | } | ||
1160 | 69 | next_underscore_offset = camel_cased_name.find('_', next_underscore_offset); | ||
1161 | 70 | } | ||
1162 | 71 | return camel_cased_name; | ||
1163 | 72 | } | ||
1164 | 73 | |||
1165 | 74 | struct ArgumentTypeDescriptor | ||
1166 | 75 | { | ||
1167 | 76 | std::string cpp_type; | ||
1168 | 77 | std::string c_type; | ||
1169 | 78 | std::experimental::optional<std::vector<std::string>> converter; | ||
1170 | 79 | }; | ||
1171 | 80 | |||
1172 | 81 | std::vector<std::string> fd_converter{ | ||
1173 | 82 | "mir::Fd $NAME_resolved{$NAME};" | ||
1174 | 83 | }; | ||
1175 | 84 | |||
1176 | 85 | std::vector<std::string> optional_object_converter{ | ||
1177 | 86 | "std::experimental::optional<struct wl_resource*> $NAME_resolved;", | ||
1178 | 87 | "if ($NAME != nullptr)", | ||
1179 | 88 | "{", | ||
1180 | 89 | " $NAME_resolved = $NAME;", | ||
1181 | 90 | "}" | ||
1182 | 91 | }; | ||
1183 | 92 | |||
1184 | 93 | std::vector<std::string> optional_string_converter{ | ||
1185 | 94 | "std::experimental::optional<std::string> $NAME_resolved;", | ||
1186 | 95 | "if ($NAME != nullptr)", | ||
1187 | 96 | "{", | ||
1188 | 97 | " $NAME_resolved = std::experimental::make_optional<std::string>($NAME);", | ||
1189 | 98 | "}" | ||
1190 | 99 | }; | ||
1191 | 100 | |||
1192 | 101 | std::unordered_map<std::string, ArgumentTypeDescriptor const> type_map = { | ||
1193 | 102 | { "uint", { "uint32_t", "uint32_t", {} }}, | ||
1194 | 103 | { "int", { "int32_t", "int32_t", {} }}, | ||
1195 | 104 | { "fd", { "mir::Fd", "int", { fd_converter }}}, | ||
1196 | 105 | { "object", { "struct wl_resource*", "struct wl_resource*", {} }}, | ||
1197 | 106 | { "string", { "std::string const&", "char const*", {} }}, | ||
1198 | 107 | { "new_id", { "uint32_t", "uint32_t", {} }} | ||
1199 | 108 | }; | ||
1200 | 109 | |||
1201 | 110 | std::unordered_map<std::string, ArgumentTypeDescriptor const> optional_type_map = { | ||
1202 | 111 | { "object", { "std::experimental::optional<struct wl_resource*> const&", "struct wl_resource*", { optional_object_converter }}}, | ||
1203 | 112 | { "string", { "std::experimental::optional<std::string> const&", "char const*", { optional_string_converter} }}, | ||
1204 | 113 | }; | ||
1205 | 114 | |||
1206 | 115 | bool parse_optional(xmlpp::Element const& arg) | ||
1207 | 116 | { | ||
1208 | 117 | if (auto allow_null = arg.get_attribute("allow-null")) | ||
1209 | 118 | { | ||
1210 | 119 | return allow_null->get_value() == "true"; | ||
1211 | 120 | } | ||
1212 | 121 | return false; | ||
1213 | 122 | } | ||
1214 | 123 | |||
1215 | 124 | class Interface; | ||
1216 | 125 | |||
1217 | 126 | class Argument | ||
1218 | 127 | { | ||
1219 | 128 | public: | ||
1220 | 129 | Argument(xmlpp::Element const& node) | ||
1221 | 130 | : name{node.get_attribute_value("name")}, | ||
1222 | 131 | descriptor{parse_optional(node) ? optional_type_map.at(node.get_attribute_value("type")) | ||
1223 | 132 | : type_map.at(node.get_attribute_value("type"))} | ||
1224 | 133 | { | ||
1225 | 134 | } | ||
1226 | 135 | |||
1227 | 136 | void emit_c_prototype(std::ostream& out) const | ||
1228 | 137 | { | ||
1229 | 138 | out << descriptor.c_type << " " << name; | ||
1230 | 139 | } | ||
1231 | 140 | void emit_cpp_prototype(std::ostream& out) const | ||
1232 | 141 | { | ||
1233 | 142 | out << descriptor.cpp_type << " " << name; | ||
1234 | 143 | } | ||
1235 | 144 | void emit_thunk_call_fragment(std::ostream& out) const | ||
1236 | 145 | { | ||
1237 | 146 | out << (descriptor.converter ? (name + "_resolved") : name); | ||
1238 | 147 | } | ||
1239 | 148 | |||
1240 | 149 | void emit_thunk_converter(std::ostream& out, std::string const& indent) const | ||
1241 | 150 | { | ||
1242 | 151 | for (auto const& line : descriptor.converter.value_or(std::vector<std::string>{})) | ||
1243 | 152 | { | ||
1244 | 153 | std::string substituted_line = line; | ||
1245 | 154 | size_t substitution_pos = substituted_line.find("$NAME"); | ||
1246 | 155 | while (substitution_pos != std::string::npos) | ||
1247 | 156 | { | ||
1248 | 157 | substituted_line = substituted_line.replace(substitution_pos, 5, name); | ||
1249 | 158 | substitution_pos = substituted_line.find("$NAME"); | ||
1250 | 159 | } | ||
1251 | 160 | out << indent << substituted_line << std::endl; | ||
1252 | 161 | } | ||
1253 | 162 | } | ||
1254 | 163 | |||
1255 | 164 | private: | ||
1256 | 165 | std::string const name; | ||
1257 | 166 | ArgumentTypeDescriptor const& descriptor; | ||
1258 | 167 | }; | ||
1259 | 168 | |||
1260 | 169 | class Method | ||
1261 | 170 | { | ||
1262 | 171 | public: | ||
1263 | 172 | Method(xmlpp::Element const& node) | ||
1264 | 173 | : name{node.get_attribute_value("name")} | ||
1265 | 174 | { | ||
1266 | 175 | for (auto const& child : node.get_children("arg")) | ||
1267 | 176 | { | ||
1268 | 177 | auto arg_node = dynamic_cast<xmlpp::Element const*>(child); | ||
1269 | 178 | arguments.emplace_back(std::ref(*arg_node)); | ||
1270 | 179 | } | ||
1271 | 180 | } | ||
1272 | 181 | |||
1273 | 182 | // TODO: Decide whether to resolve wl_resource* to wrapped types (ie: Region, Surface, etc). | ||
1274 | 183 | void emit_virtual_prototype(std::ostream& out, std::string const& indent, bool is_global) const | ||
1275 | 184 | { | ||
1276 | 185 | out << indent << "virtual void " << name << "("; | ||
1277 | 186 | if (is_global) | ||
1278 | 187 | { | ||
1279 | 188 | out << "struct wl_client* client, struct wl_resource* resource"; | ||
1280 | 189 | if (!arguments.empty()) | ||
1281 | 190 | { | ||
1282 | 191 | out << ", "; | ||
1283 | 192 | } | ||
1284 | 193 | } | ||
1285 | 194 | for (size_t i = 0 ; i < arguments.size() ; ++i) | ||
1286 | 195 | { | ||
1287 | 196 | arguments[i].emit_cpp_prototype(out); | ||
1288 | 197 | if (i != arguments.size() - 1) | ||
1289 | 198 | { | ||
1290 | 199 | out << ", "; | ||
1291 | 200 | } | ||
1292 | 201 | } | ||
1293 | 202 | out << ") = 0;" << std::endl; | ||
1294 | 203 | } | ||
1295 | 204 | |||
1296 | 205 | // TODO: Decide whether to resolve wl_resource* to wrapped types (ie: Region, Surface, etc). | ||
1297 | 206 | void emit_thunk(std::ostream& out, std::string const& indent, | ||
1298 | 207 | std::string const& interface_type, bool is_global) const | ||
1299 | 208 | { | ||
1300 | 209 | out << indent << "static void " << name << "_thunk(" | ||
1301 | 210 | << "struct wl_client*" << (is_global ? " client" : "") | ||
1302 | 211 | << ", struct wl_resource* resource"; | ||
1303 | 212 | for (auto const& arg : arguments) | ||
1304 | 213 | { | ||
1305 | 214 | out << ", "; | ||
1306 | 215 | arg.emit_c_prototype(out); | ||
1307 | 216 | } | ||
1308 | 217 | out << ")" << std::endl; | ||
1309 | 218 | |||
1310 | 219 | out << indent << "{" << std::endl; | ||
1311 | 220 | out << indent << " auto me = static_cast<" << interface_type << "*>(" | ||
1312 | 221 | << "wl_resource_get_user_data(resource));" << std::endl; | ||
1313 | 222 | for (auto const& arg : arguments) | ||
1314 | 223 | { | ||
1315 | 224 | arg.emit_thunk_converter(out, indent + " "); | ||
1316 | 225 | } | ||
1317 | 226 | |||
1318 | 227 | out << indent << " me->" << name << "("; | ||
1319 | 228 | if (is_global) | ||
1320 | 229 | { | ||
1321 | 230 | out << "client, resource"; | ||
1322 | 231 | if (!arguments.empty()) | ||
1323 | 232 | { | ||
1324 | 233 | out << ", "; | ||
1325 | 234 | } | ||
1326 | 235 | } | ||
1327 | 236 | for (size_t i = 0; i < arguments.size(); ++i) | ||
1328 | 237 | { | ||
1329 | 238 | arguments[i].emit_thunk_call_fragment(out); | ||
1330 | 239 | if (i != arguments.size() - 1) | ||
1331 | 240 | { | ||
1332 | 241 | out << ", "; | ||
1333 | 242 | } | ||
1334 | 243 | } | ||
1335 | 244 | out << ");" << std::endl; | ||
1336 | 245 | |||
1337 | 246 | out << indent << "}" << std::endl; | ||
1338 | 247 | } | ||
1339 | 248 | |||
1340 | 249 | void emit_vtable_initialiser(std::ostream& out, std::string const& indent) const | ||
1341 | 250 | { | ||
1342 | 251 | out << indent << name << "_thunk," << std::endl; | ||
1343 | 252 | } | ||
1344 | 253 | |||
1345 | 254 | private: | ||
1346 | 255 | std::string const name; | ||
1347 | 256 | std::vector<Argument> arguments; | ||
1348 | 257 | }; | ||
1349 | 258 | |||
1350 | 259 | void emit_indented_lines(std::ostream& out, std::string const& indent, | ||
1351 | 260 | std::initializer_list<std::initializer_list<std::string>> lines) | ||
1352 | 261 | { | ||
1353 | 262 | for (auto const& line : lines) | ||
1354 | 263 | { | ||
1355 | 264 | out << indent; | ||
1356 | 265 | for (auto const& fragment : line) | ||
1357 | 266 | { | ||
1358 | 267 | out << fragment; | ||
1359 | 268 | } | ||
1360 | 269 | out << std::endl; | ||
1361 | 270 | } | ||
1362 | 271 | } | ||
1363 | 272 | |||
1364 | 273 | class Interface | ||
1365 | 274 | { | ||
1366 | 275 | public: | ||
1367 | 276 | Interface( | ||
1368 | 277 | xmlpp::Element const& node, | ||
1369 | 278 | std::function<std::string(std::string)> const& name_transform, | ||
1370 | 279 | std::unordered_set<std::string> const& constructable_interfaces) | ||
1371 | 280 | : wl_name{node.get_attribute_value("name")}, | ||
1372 | 281 | generated_name{name_transform(wl_name)}, | ||
1373 | 282 | is_global{constructable_interfaces.count(wl_name) == 0} | ||
1374 | 283 | { | ||
1375 | 284 | for (auto method_node : node.get_children("request")) | ||
1376 | 285 | { | ||
1377 | 286 | auto method = dynamic_cast<xmlpp::Element*>(method_node); | ||
1378 | 287 | methods.emplace_back(std::ref(*method)); | ||
1379 | 288 | } | ||
1380 | 289 | } | ||
1381 | 290 | |||
1382 | 291 | void emit_constructor(std::ostream& out, std::string const& indent, bool has_vtable) | ||
1383 | 292 | { | ||
1384 | 293 | if (is_global) | ||
1385 | 294 | { | ||
1386 | 295 | emit_constructor_for_global(out, indent); | ||
1387 | 296 | } | ||
1388 | 297 | else | ||
1389 | 298 | { | ||
1390 | 299 | emit_constructor_for_regular(out, indent, has_vtable); | ||
1391 | 300 | } | ||
1392 | 301 | } | ||
1393 | 302 | |||
1394 | 303 | void emit_bind(std::ostream& out, std::string const& indent, bool has_vtable) | ||
1395 | 304 | { | ||
1396 | 305 | emit_indented_lines(out, indent, { | ||
1397 | 306 | {"static void bind(struct wl_client* client, void* data, uint32_t version, uint32_t id)"}, | ||
1398 | 307 | {"{"}, | ||
1399 | 308 | }); | ||
1400 | 309 | emit_indented_lines(out, indent + " ", { | ||
1401 | 310 | {"auto me = static_cast<", generated_name, "*>(data);"}, | ||
1402 | 311 | {"auto resource = wl_resource_create(client, &", wl_name, "_interface,"}, | ||
1403 | 312 | {" std::min(version, me->max_version), id);"}, | ||
1404 | 313 | {"if (resource == nullptr)"}, | ||
1405 | 314 | {"{"}, | ||
1406 | 315 | {" wl_client_post_no_memory(client);"}, | ||
1407 | 316 | {" BOOST_THROW_EXCEPTION((std::bad_alloc{}));"}, | ||
1408 | 317 | {"}"}, | ||
1409 | 318 | }); | ||
1410 | 319 | if (has_vtable) | ||
1411 | 320 | { | ||
1412 | 321 | emit_indented_lines(out, indent + " ", | ||
1413 | 322 | {{"wl_resource_set_implementation(resource, &vtable, me, nullptr);"}}); | ||
1414 | 323 | } | ||
1415 | 324 | emit_indented_lines(out, indent, { | ||
1416 | 325 | {"}"} | ||
1417 | 326 | }); | ||
1418 | 327 | } | ||
1419 | 328 | |||
1420 | 329 | void emit_class(std::ostream& out) | ||
1421 | 330 | { | ||
1422 | 331 | out << "class " << generated_name << std::endl; | ||
1423 | 332 | out << "{" << std::endl; | ||
1424 | 333 | out << "protected:" << std::endl; | ||
1425 | 334 | |||
1426 | 335 | emit_constructor(out, " ", !methods.empty()); | ||
1427 | 336 | out << " virtual ~" << generated_name << "() = default;" << std::endl; | ||
1428 | 337 | out << std::endl; | ||
1429 | 338 | |||
1430 | 339 | for (auto const& method : methods) | ||
1431 | 340 | { | ||
1432 | 341 | method.emit_virtual_prototype(out, " ", is_global); | ||
1433 | 342 | } | ||
1434 | 343 | out << std::endl; | ||
1435 | 344 | |||
1436 | 345 | if (!is_global) | ||
1437 | 346 | { | ||
1438 | 347 | emit_indented_lines(out, " ", { | ||
1439 | 348 | { "struct wl_client* const client;" }, | ||
1440 | 349 | { "struct wl_resource* const resource;"} | ||
1441 | 350 | }); | ||
1442 | 351 | out << std::endl; | ||
1443 | 352 | } | ||
1444 | 353 | |||
1445 | 354 | if (!methods.empty()) | ||
1446 | 355 | { | ||
1447 | 356 | out << "private:" << std::endl; | ||
1448 | 357 | } | ||
1449 | 358 | |||
1450 | 359 | for (auto const& method : methods) | ||
1451 | 360 | { | ||
1452 | 361 | method.emit_thunk(out, " ", generated_name, is_global); | ||
1453 | 362 | out << std::endl; | ||
1454 | 363 | } | ||
1455 | 364 | |||
1456 | 365 | if (is_global) | ||
1457 | 366 | { | ||
1458 | 367 | emit_bind(out, " ", !methods.empty()); | ||
1459 | 368 | out << std::endl; | ||
1460 | 369 | emit_indented_lines(out, " ", { | ||
1461 | 370 | { "uint32_t const max_version;" } | ||
1462 | 371 | }); | ||
1463 | 372 | } | ||
1464 | 373 | |||
1465 | 374 | if (!methods.empty()) | ||
1466 | 375 | { | ||
1467 | 376 | emit_indented_lines(out, " ", { | ||
1468 | 377 | { "static struct ", wl_name, "_interface const vtable;" } | ||
1469 | 378 | }); | ||
1470 | 379 | } | ||
1471 | 380 | |||
1472 | 381 | out << "};" << std::endl; | ||
1473 | 382 | |||
1474 | 383 | out << std::endl; | ||
1475 | 384 | |||
1476 | 385 | if (!methods.empty()) | ||
1477 | 386 | { | ||
1478 | 387 | out << "struct " << wl_name << "_interface const " << generated_name << "::vtable = {" << std::endl; | ||
1479 | 388 | for (auto const& method : methods) | ||
1480 | 389 | { | ||
1481 | 390 | method.emit_vtable_initialiser(out, " "); | ||
1482 | 391 | } | ||
1483 | 392 | out << "};" << std::endl; | ||
1484 | 393 | } | ||
1485 | 394 | } | ||
1486 | 395 | |||
1487 | 396 | private: | ||
1488 | 397 | void emit_constructor_for_global(std::ostream& out, std::string const& indent) | ||
1489 | 398 | { | ||
1490 | 399 | out << indent << generated_name << "(struct wl_display* display, uint32_t max_version)" << std::endl; | ||
1491 | 400 | out << indent << " : max_version{max_version}" << std::endl; | ||
1492 | 401 | out << indent << "{" << std::endl; | ||
1493 | 402 | out << indent << " if (!wl_global_create(display, " << std::endl; | ||
1494 | 403 | out << indent << " &" << wl_name << "_interface, max_version," << std::endl; | ||
1495 | 404 | out << indent << " this, &" << generated_name << "::bind))" << std::endl; | ||
1496 | 405 | out << indent << " {" << std::endl; | ||
1497 | 406 | out << indent << " BOOST_THROW_EXCEPTION((std::runtime_error{\"Failed to export " | ||
1498 | 407 | << wl_name << " interface\"}));" << std::endl; | ||
1499 | 408 | out << indent << " }" << std::endl; | ||
1500 | 409 | out << indent << "}" << std::endl; | ||
1501 | 410 | } | ||
1502 | 411 | |||
1503 | 412 | void emit_constructor_for_regular(std::ostream& out, std::string const& indent, bool has_vtable) | ||
1504 | 413 | { | ||
1505 | 414 | emit_indented_lines(out, indent, { | ||
1506 | 415 | { generated_name, "(struct wl_client* client, struct wl_resource* parent, uint32_t id)" }, | ||
1507 | 416 | { " : client{client}," }, | ||
1508 | 417 | { " resource{wl_resource_create(client, &", wl_name, "_interface, wl_resource_get_version(parent), id)}" }, | ||
1509 | 418 | { "{" } | ||
1510 | 419 | }); | ||
1511 | 420 | emit_indented_lines(out, indent + " ", { | ||
1512 | 421 | { "if (resource == nullptr)" }, | ||
1513 | 422 | { "{" }, | ||
1514 | 423 | { " wl_resource_post_no_memory(parent);" }, | ||
1515 | 424 | { " BOOST_THROW_EXCEPTION((std::bad_alloc{}));" }, | ||
1516 | 425 | { "}" }, | ||
1517 | 426 | }); | ||
1518 | 427 | if (has_vtable) | ||
1519 | 428 | { | ||
1520 | 429 | emit_indented_lines(out, indent + " ", | ||
1521 | 430 | {{ "wl_resource_set_implementation(resource, &vtable, this, nullptr);" }}); | ||
1522 | 431 | } | ||
1523 | 432 | emit_indented_lines(out, indent, { | ||
1524 | 433 | { "}" } | ||
1525 | 434 | }); | ||
1526 | 435 | } | ||
1527 | 436 | |||
1528 | 437 | std::string const wl_name; | ||
1529 | 438 | std::string const generated_name; | ||
1530 | 439 | bool const is_global; | ||
1531 | 440 | std::vector<Method> methods; | ||
1532 | 441 | }; | ||
1533 | 442 | |||
1534 | 443 | int main(int argc, char** argv) | ||
1535 | 444 | { | ||
1536 | 445 | if (argc != 3) | ||
1537 | 446 | { | ||
1538 | 447 | exit(1); | ||
1539 | 448 | } | ||
1540 | 449 | |||
1541 | 450 | std::string const prefix{argv[1]}; | ||
1542 | 451 | |||
1543 | 452 | auto name_transform = [prefix](std::string protocol_name) | ||
1544 | 453 | { | ||
1545 | 454 | std::string transformed_name = protocol_name; | ||
1546 | 455 | if (protocol_name.find(prefix) == 0) | ||
1547 | 456 | { | ||
1548 | 457 | transformed_name = protocol_name.substr(prefix.length()); | ||
1549 | 458 | } | ||
1550 | 459 | return camel_case_string(transformed_name); | ||
1551 | 460 | }; | ||
1552 | 461 | |||
1553 | 462 | xmlpp::DomParser parser(argv[2]); | ||
1554 | 463 | |||
1555 | 464 | auto document = parser.get_document(); | ||
1556 | 465 | |||
1557 | 466 | auto root_node = document->get_root_node(); | ||
1558 | 467 | |||
1559 | 468 | auto constructor_nodes = root_node->find("//arg[@type='new_id']"); | ||
1560 | 469 | std::unordered_set<std::string> constructible_interfaces; | ||
1561 | 470 | for (auto const node : constructor_nodes) | ||
1562 | 471 | { | ||
1563 | 472 | auto arg = dynamic_cast<xmlpp::Element const*>(node); | ||
1564 | 473 | constructible_interfaces.insert(arg->get_attribute_value("interface")); | ||
1565 | 474 | } | ||
1566 | 475 | |||
1567 | 476 | emit_comment_header(std::cout); | ||
1568 | 477 | |||
1569 | 478 | std::cout << std::endl; | ||
1570 | 479 | |||
1571 | 480 | emit_required_headers(); | ||
1572 | 481 | |||
1573 | 482 | std::cout << std::endl; | ||
1574 | 483 | |||
1575 | 484 | std::cout << "namespace mir" << std::endl; | ||
1576 | 485 | std::cout << "{" << std::endl; | ||
1577 | 486 | std::cout << "namespace frontend" << std::endl; | ||
1578 | 487 | std::cout << "{" << std::endl; | ||
1579 | 488 | std::cout << "namespace wayland" << std::endl; | ||
1580 | 489 | std::cout << "{" << std::endl; | ||
1581 | 490 | |||
1582 | 491 | for (auto top_level : root_node->get_children("interface")) | ||
1583 | 492 | { | ||
1584 | 493 | auto interface = dynamic_cast<xmlpp::Element*>(top_level); | ||
1585 | 494 | |||
1586 | 495 | if (interface->get_attribute_value("name") == "wl_display" || | ||
1587 | 496 | interface->get_attribute_value("name") == "wl_registry") | ||
1588 | 497 | { | ||
1589 | 498 | // These are special, and don't need binding. | ||
1590 | 499 | continue; | ||
1591 | 500 | } | ||
1592 | 501 | Interface(*interface, name_transform, constructible_interfaces).emit_class(std::cout); | ||
1593 | 502 | |||
1594 | 503 | std::cout << std::endl << std::endl; | ||
1595 | 504 | } | ||
1596 | 505 | std::cout << "}" << std::endl; | ||
1597 | 506 | std::cout << "}" << std::endl; | ||
1598 | 507 | std::cout << "}" << std::endl; | ||
1599 | 508 | return 0; | ||
1600 | 509 | } | ||
1601 | 0 | 510 | ||
1602 | === modified file 'src/server/CMakeLists.txt' | |||
1603 | --- src/server/CMakeLists.txt 2017-08-02 11:07:50 +0000 | |||
1604 | +++ src/server/CMakeLists.txt 2017-09-07 05:58:57 +0000 | |||
1605 | @@ -76,6 +76,7 @@ | |||
1606 | 76 | $<TARGET_OBJECTS:mircompositor> | 76 | $<TARGET_OBJECTS:mircompositor> |
1607 | 77 | $<TARGET_OBJECTS:mirgraphics> | 77 | $<TARGET_OBJECTS:mirgraphics> |
1608 | 78 | $<TARGET_OBJECTS:mirfrontend> | 78 | $<TARGET_OBJECTS:mirfrontend> |
1609 | 79 | $<TARGET_OBJECTS:mirfrontend-wayland> | ||
1610 | 79 | $<TARGET_OBJECTS:mirshell> | 80 | $<TARGET_OBJECTS:mirshell> |
1611 | 80 | $<TARGET_OBJECTS:mirlttng> | 81 | $<TARGET_OBJECTS:mirlttng> |
1612 | 81 | $<TARGET_OBJECTS:mirreport> | 82 | $<TARGET_OBJECTS:mirreport> |
1613 | @@ -120,6 +121,7 @@ | |||
1614 | 120 | ${UDEV_LDFLAGS} ${UDEV_LIBRARIES} | 121 | ${UDEV_LDFLAGS} ${UDEV_LIBRARIES} |
1615 | 121 | ${GLIB_LDFLAGS} ${GLIB_LIBRARIES} | 122 | ${GLIB_LDFLAGS} ${GLIB_LIBRARIES} |
1616 | 122 | ${UUID_LDFLAGS} ${UUID_LIBRARIES} | 123 | ${UUID_LDFLAGS} ${UUID_LIBRARIES} |
1617 | 124 | ${WAYLAND_SERVER_LDFLAGS} ${WAYLAND_SERVER_LIBRARIES} | ||
1618 | 123 | ) | 125 | ) |
1619 | 124 | 126 | ||
1620 | 125 | install(TARGETS mirserver | 127 | install(TARGETS mirserver |
1621 | 126 | 128 | ||
1622 | === modified file 'src/server/display_server.cpp' | |||
1623 | --- src/server/display_server.cpp 2017-09-06 11:14:47 +0000 | |||
1624 | +++ src/server/display_server.cpp 2017-09-07 05:58:57 +0000 | |||
1625 | @@ -50,6 +50,7 @@ | |||
1626 | 50 | input_dispatcher{config.the_input_dispatcher()}, | 50 | input_dispatcher{config.the_input_dispatcher()}, |
1627 | 51 | compositor{config.the_compositor()}, | 51 | compositor{config.the_compositor()}, |
1628 | 52 | connector{config.the_connector()}, | 52 | connector{config.the_connector()}, |
1629 | 53 | wayland_connector{config.the_wayland_connector()}, | ||
1630 | 53 | prompt_connector{config.the_prompt_connector()}, | 54 | prompt_connector{config.the_prompt_connector()}, |
1631 | 54 | input_manager{config.the_input_manager()}, | 55 | input_manager{config.the_input_manager()}, |
1632 | 55 | main_loop{config.the_main_loop()}, | 56 | main_loop{config.the_main_loop()}, |
1633 | @@ -73,9 +74,13 @@ | |||
1634 | 73 | { | 74 | { |
1635 | 74 | auto comm = try_but_revert_if_unwinding( | 75 | auto comm = try_but_revert_if_unwinding( |
1636 | 75 | [this] { connector->stop(); }, | 76 | [this] { connector->stop(); }, |
1640 | 76 | [&, this] { connector->start(); }); | 77 | [this] { connector->start(); }); |
1641 | 77 | 78 | ||
1642 | 78 | auto prompt = try_but_revert_if_unwinding( | 79 | auto wayland = try_but_revert_if_unwinding( |
1643 | 80 | [this] { wayland_connector->stop(); }, | ||
1644 | 81 | [this] { wayland_connector->start(); }); | ||
1645 | 82 | |||
1646 | 83 | auto prompt = try_but_revert_if_unwinding( | ||
1647 | 79 | [this] { prompt_connector->stop(); }, | 84 | [this] { prompt_connector->stop(); }, |
1648 | 80 | [&, this] { prompt_connector->start(); }); | 85 | [&, this] { prompt_connector->start(); }); |
1649 | 81 | 86 | ||
1650 | @@ -161,6 +166,7 @@ | |||
1651 | 161 | std::shared_ptr<mi::InputDispatcher> const input_dispatcher; | 166 | std::shared_ptr<mi::InputDispatcher> const input_dispatcher; |
1652 | 162 | std::shared_ptr<mc::Compositor> const compositor; | 167 | std::shared_ptr<mc::Compositor> const compositor; |
1653 | 163 | std::shared_ptr<mf::Connector> const connector; | 168 | std::shared_ptr<mf::Connector> const connector; |
1654 | 169 | std::shared_ptr<mf::Connector> const wayland_connector; | ||
1655 | 164 | std::shared_ptr<mf::Connector> const prompt_connector; | 170 | std::shared_ptr<mf::Connector> const prompt_connector; |
1656 | 165 | std::shared_ptr<mi::InputManager> const input_manager; | 171 | std::shared_ptr<mi::InputManager> const input_manager; |
1657 | 166 | std::shared_ptr<mir::MainLoop> const main_loop; | 172 | std::shared_ptr<mir::MainLoop> const main_loop; |
1658 | @@ -196,11 +202,13 @@ | |||
1659 | 196 | server.input_dispatcher->start(); | 202 | server.input_dispatcher->start(); |
1660 | 197 | server.prompt_connector->start(); | 203 | server.prompt_connector->start(); |
1661 | 198 | server.connector->start(); | 204 | server.connector->start(); |
1662 | 205 | server.wayland_connector->start(); | ||
1663 | 199 | 206 | ||
1664 | 200 | server.server_status_listener->started(); | 207 | server.server_status_listener->started(); |
1665 | 201 | 208 | ||
1666 | 202 | server.main_loop->run(); | 209 | server.main_loop->run(); |
1667 | 203 | 210 | ||
1668 | 211 | server.wayland_connector->stop(); | ||
1669 | 204 | server.connector->stop(); | 212 | server.connector->stop(); |
1670 | 205 | server.prompt_connector->stop(); | 213 | server.prompt_connector->stop(); |
1671 | 206 | server.input_dispatcher->stop(); | 214 | server.input_dispatcher->stop(); |
1672 | 207 | 215 | ||
1673 | === modified file 'src/server/frontend/CMakeLists.txt' | |||
1674 | --- src/server/frontend/CMakeLists.txt 2017-07-04 04:38:55 +0000 | |||
1675 | +++ src/server/frontend/CMakeLists.txt 2017-09-07 05:58:57 +0000 | |||
1676 | @@ -36,6 +36,8 @@ | |||
1677 | 36 | ${PROJECT_SOURCE_DIR}/src/include/server/mir/frontend/shell.h | 36 | ${PROJECT_SOURCE_DIR}/src/include/server/mir/frontend/shell.h |
1678 | 37 | ) | 37 | ) |
1679 | 38 | 38 | ||
1680 | 39 | add_subdirectory(wayland) | ||
1681 | 40 | |||
1682 | 39 | add_library( | 41 | add_library( |
1683 | 40 | mirfrontend OBJECT | 42 | mirfrontend OBJECT |
1684 | 41 | 43 | ||
1685 | 42 | 44 | ||
1686 | === added directory 'src/server/frontend/wayland' | |||
1687 | === added file 'src/server/frontend/wayland/CMakeLists.txt' | |||
1688 | --- src/server/frontend/wayland/CMakeLists.txt 1970-01-01 00:00:00 +0000 | |||
1689 | +++ src/server/frontend/wayland/CMakeLists.txt 2017-09-07 05:58:57 +0000 | |||
1690 | @@ -0,0 +1,14 @@ | |||
1691 | 1 | set( | ||
1692 | 2 | WAYLAND_SOURCES | ||
1693 | 3 | |||
1694 | 4 | core_generated_interfaces.h | ||
1695 | 5 | wayland_default_configuration.cpp | ||
1696 | 6 | wayland_connector.cpp | ||
1697 | 7 | ) | ||
1698 | 8 | |||
1699 | 9 | add_library( | ||
1700 | 10 | mirfrontend-wayland OBJECT | ||
1701 | 11 | |||
1702 | 12 | ${WAYLAND_SOURCES} | ||
1703 | 13 | ) | ||
1704 | 14 | |||
1705 | 0 | 15 | ||
1706 | === added file 'src/server/frontend/wayland/core_generated_interfaces.h' | |||
1707 | --- src/server/frontend/wayland/core_generated_interfaces.h 1970-01-01 00:00:00 +0000 | |||
1708 | +++ src/server/frontend/wayland/core_generated_interfaces.h 2017-09-07 05:58:57 +0000 | |||
1709 | @@ -0,0 +1,1189 @@ | |||
1710 | 1 | /* | ||
1711 | 2 | * AUTOGENERATED - DO NOT EDIT | ||
1712 | 3 | * | ||
1713 | 4 | * This header is generated by src/protocol/wrapper_generator.cpp | ||
1714 | 5 | * To regenerate, run the “refresh-wayland-wrapper” target. | ||
1715 | 6 | */ | ||
1716 | 7 | |||
1717 | 8 | #include <experimental/optional> | ||
1718 | 9 | #include <boost/throw_exception.hpp> | ||
1719 | 10 | |||
1720 | 11 | #include <wayland-server.h> | ||
1721 | 12 | #include <wayland-server-protocol.h> | ||
1722 | 13 | |||
1723 | 14 | #include "mir/fd.h" | ||
1724 | 15 | |||
1725 | 16 | namespace mir | ||
1726 | 17 | { | ||
1727 | 18 | namespace frontend | ||
1728 | 19 | { | ||
1729 | 20 | namespace wayland | ||
1730 | 21 | { | ||
1731 | 22 | class Callback | ||
1732 | 23 | { | ||
1733 | 24 | protected: | ||
1734 | 25 | Callback(struct wl_client* client, struct wl_resource* parent, uint32_t id) | ||
1735 | 26 | : client{client}, | ||
1736 | 27 | resource{wl_resource_create(client, &wl_callback_interface, wl_resource_get_version(parent), id)} | ||
1737 | 28 | { | ||
1738 | 29 | if (resource == nullptr) | ||
1739 | 30 | { | ||
1740 | 31 | wl_resource_post_no_memory(parent); | ||
1741 | 32 | BOOST_THROW_EXCEPTION((std::bad_alloc{})); | ||
1742 | 33 | } | ||
1743 | 34 | } | ||
1744 | 35 | virtual ~Callback() = default; | ||
1745 | 36 | |||
1746 | 37 | |||
1747 | 38 | struct wl_client* const client; | ||
1748 | 39 | struct wl_resource* const resource; | ||
1749 | 40 | |||
1750 | 41 | }; | ||
1751 | 42 | |||
1752 | 43 | |||
1753 | 44 | |||
1754 | 45 | class Compositor | ||
1755 | 46 | { | ||
1756 | 47 | protected: | ||
1757 | 48 | Compositor(struct wl_display* display, uint32_t max_version) | ||
1758 | 49 | : max_version{max_version} | ||
1759 | 50 | { | ||
1760 | 51 | if (!wl_global_create(display, | ||
1761 | 52 | &wl_compositor_interface, max_version, | ||
1762 | 53 | this, &Compositor::bind)) | ||
1763 | 54 | { | ||
1764 | 55 | BOOST_THROW_EXCEPTION((std::runtime_error{"Failed to export wl_compositor interface"})); | ||
1765 | 56 | } | ||
1766 | 57 | } | ||
1767 | 58 | virtual ~Compositor() = default; | ||
1768 | 59 | |||
1769 | 60 | virtual void create_surface(struct wl_client* client, struct wl_resource* resource, uint32_t id) = 0; | ||
1770 | 61 | virtual void create_region(struct wl_client* client, struct wl_resource* resource, uint32_t id) = 0; | ||
1771 | 62 | |||
1772 | 63 | private: | ||
1773 | 64 | static void create_surface_thunk(struct wl_client* client, struct wl_resource* resource, uint32_t id) | ||
1774 | 65 | { | ||
1775 | 66 | auto me = static_cast<Compositor*>(wl_resource_get_user_data(resource)); | ||
1776 | 67 | me->create_surface(client, resource, id); | ||
1777 | 68 | } | ||
1778 | 69 | |||
1779 | 70 | static void create_region_thunk(struct wl_client* client, struct wl_resource* resource, uint32_t id) | ||
1780 | 71 | { | ||
1781 | 72 | auto me = static_cast<Compositor*>(wl_resource_get_user_data(resource)); | ||
1782 | 73 | me->create_region(client, resource, id); | ||
1783 | 74 | } | ||
1784 | 75 | |||
1785 | 76 | static void bind(struct wl_client* client, void* data, uint32_t version, uint32_t id) | ||
1786 | 77 | { | ||
1787 | 78 | auto me = static_cast<Compositor*>(data); | ||
1788 | 79 | auto resource = wl_resource_create(client, &wl_compositor_interface, | ||
1789 | 80 | std::min(version, me->max_version), id); | ||
1790 | 81 | if (resource == nullptr) | ||
1791 | 82 | { | ||
1792 | 83 | wl_client_post_no_memory(client); | ||
1793 | 84 | BOOST_THROW_EXCEPTION((std::bad_alloc{})); | ||
1794 | 85 | } | ||
1795 | 86 | wl_resource_set_implementation(resource, &vtable, me, nullptr); | ||
1796 | 87 | } | ||
1797 | 88 | |||
1798 | 89 | uint32_t const max_version; | ||
1799 | 90 | static struct wl_compositor_interface const vtable; | ||
1800 | 91 | }; | ||
1801 | 92 | |||
1802 | 93 | struct wl_compositor_interface const Compositor::vtable = { | ||
1803 | 94 | create_surface_thunk, | ||
1804 | 95 | create_region_thunk, | ||
1805 | 96 | }; | ||
1806 | 97 | |||
1807 | 98 | |||
1808 | 99 | class ShmPool | ||
1809 | 100 | { | ||
1810 | 101 | protected: | ||
1811 | 102 | ShmPool(struct wl_client* client, struct wl_resource* parent, uint32_t id) | ||
1812 | 103 | : client{client}, | ||
1813 | 104 | resource{wl_resource_create(client, &wl_shm_pool_interface, wl_resource_get_version(parent), id)} | ||
1814 | 105 | { | ||
1815 | 106 | if (resource == nullptr) | ||
1816 | 107 | { | ||
1817 | 108 | wl_resource_post_no_memory(parent); | ||
1818 | 109 | BOOST_THROW_EXCEPTION((std::bad_alloc{})); | ||
1819 | 110 | } | ||
1820 | 111 | wl_resource_set_implementation(resource, &vtable, this, nullptr); | ||
1821 | 112 | } | ||
1822 | 113 | virtual ~ShmPool() = default; | ||
1823 | 114 | |||
1824 | 115 | virtual void create_buffer(uint32_t id, int32_t offset, int32_t width, int32_t height, int32_t stride, uint32_t format) = 0; | ||
1825 | 116 | virtual void destroy() = 0; | ||
1826 | 117 | virtual void resize(int32_t size) = 0; | ||
1827 | 118 | |||
1828 | 119 | struct wl_client* const client; | ||
1829 | 120 | struct wl_resource* const resource; | ||
1830 | 121 | |||
1831 | 122 | private: | ||
1832 | 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) | ||
1833 | 124 | { | ||
1834 | 125 | auto me = static_cast<ShmPool*>(wl_resource_get_user_data(resource)); | ||
1835 | 126 | me->create_buffer(id, offset, width, height, stride, format); | ||
1836 | 127 | } | ||
1837 | 128 | |||
1838 | 129 | static void destroy_thunk(struct wl_client*, struct wl_resource* resource) | ||
1839 | 130 | { | ||
1840 | 131 | auto me = static_cast<ShmPool*>(wl_resource_get_user_data(resource)); | ||
1841 | 132 | me->destroy(); | ||
1842 | 133 | } | ||
1843 | 134 | |||
1844 | 135 | static void resize_thunk(struct wl_client*, struct wl_resource* resource, int32_t size) | ||
1845 | 136 | { | ||
1846 | 137 | auto me = static_cast<ShmPool*>(wl_resource_get_user_data(resource)); | ||
1847 | 138 | me->resize(size); | ||
1848 | 139 | } | ||
1849 | 140 | |||
1850 | 141 | static struct wl_shm_pool_interface const vtable; | ||
1851 | 142 | }; | ||
1852 | 143 | |||
1853 | 144 | struct wl_shm_pool_interface const ShmPool::vtable = { | ||
1854 | 145 | create_buffer_thunk, | ||
1855 | 146 | destroy_thunk, | ||
1856 | 147 | resize_thunk, | ||
1857 | 148 | }; | ||
1858 | 149 | |||
1859 | 150 | |||
1860 | 151 | class Shm | ||
1861 | 152 | { | ||
1862 | 153 | protected: | ||
1863 | 154 | Shm(struct wl_display* display, uint32_t max_version) | ||
1864 | 155 | : max_version{max_version} | ||
1865 | 156 | { | ||
1866 | 157 | if (!wl_global_create(display, | ||
1867 | 158 | &wl_shm_interface, max_version, | ||
1868 | 159 | this, &Shm::bind)) | ||
1869 | 160 | { | ||
1870 | 161 | BOOST_THROW_EXCEPTION((std::runtime_error{"Failed to export wl_shm interface"})); | ||
1871 | 162 | } | ||
1872 | 163 | } | ||
1873 | 164 | virtual ~Shm() = default; | ||
1874 | 165 | |||
1875 | 166 | virtual void create_pool(struct wl_client* client, struct wl_resource* resource, uint32_t id, mir::Fd fd, int32_t size) = 0; | ||
1876 | 167 | |||
1877 | 168 | private: | ||
1878 | 169 | static void create_pool_thunk(struct wl_client* client, struct wl_resource* resource, uint32_t id, int fd, int32_t size) | ||
1879 | 170 | { | ||
1880 | 171 | auto me = static_cast<Shm*>(wl_resource_get_user_data(resource)); | ||
1881 | 172 | mir::Fd fd_resolved{fd}; | ||
1882 | 173 | me->create_pool(client, resource, id, fd_resolved, size); | ||
1883 | 174 | } | ||
1884 | 175 | |||
1885 | 176 | static void bind(struct wl_client* client, void* data, uint32_t version, uint32_t id) | ||
1886 | 177 | { | ||
1887 | 178 | auto me = static_cast<Shm*>(data); | ||
1888 | 179 | auto resource = wl_resource_create(client, &wl_shm_interface, | ||
1889 | 180 | std::min(version, me->max_version), id); | ||
1890 | 181 | if (resource == nullptr) | ||
1891 | 182 | { | ||
1892 | 183 | wl_client_post_no_memory(client); | ||
1893 | 184 | BOOST_THROW_EXCEPTION((std::bad_alloc{})); | ||
1894 | 185 | } | ||
1895 | 186 | wl_resource_set_implementation(resource, &vtable, me, nullptr); | ||
1896 | 187 | } | ||
1897 | 188 | |||
1898 | 189 | uint32_t const max_version; | ||
1899 | 190 | static struct wl_shm_interface const vtable; | ||
1900 | 191 | }; | ||
1901 | 192 | |||
1902 | 193 | struct wl_shm_interface const Shm::vtable = { | ||
1903 | 194 | create_pool_thunk, | ||
1904 | 195 | }; | ||
1905 | 196 | |||
1906 | 197 | |||
1907 | 198 | class Buffer | ||
1908 | 199 | { | ||
1909 | 200 | protected: | ||
1910 | 201 | Buffer(struct wl_client* client, struct wl_resource* parent, uint32_t id) | ||
1911 | 202 | : client{client}, | ||
1912 | 203 | resource{wl_resource_create(client, &wl_buffer_interface, wl_resource_get_version(parent), id)} | ||
1913 | 204 | { | ||
1914 | 205 | if (resource == nullptr) | ||
1915 | 206 | { | ||
1916 | 207 | wl_resource_post_no_memory(parent); | ||
1917 | 208 | BOOST_THROW_EXCEPTION((std::bad_alloc{})); | ||
1918 | 209 | } | ||
1919 | 210 | wl_resource_set_implementation(resource, &vtable, this, nullptr); | ||
1920 | 211 | } | ||
1921 | 212 | virtual ~Buffer() = default; | ||
1922 | 213 | |||
1923 | 214 | virtual void destroy() = 0; | ||
1924 | 215 | |||
1925 | 216 | struct wl_client* const client; | ||
1926 | 217 | struct wl_resource* const resource; | ||
1927 | 218 | |||
1928 | 219 | private: | ||
1929 | 220 | static void destroy_thunk(struct wl_client*, struct wl_resource* resource) | ||
1930 | 221 | { | ||
1931 | 222 | auto me = static_cast<Buffer*>(wl_resource_get_user_data(resource)); | ||
1932 | 223 | me->destroy(); | ||
1933 | 224 | } | ||
1934 | 225 | |||
1935 | 226 | static struct wl_buffer_interface const vtable; | ||
1936 | 227 | }; | ||
1937 | 228 | |||
1938 | 229 | struct wl_buffer_interface const Buffer::vtable = { | ||
1939 | 230 | destroy_thunk, | ||
1940 | 231 | }; | ||
1941 | 232 | |||
1942 | 233 | |||
1943 | 234 | class DataOffer | ||
1944 | 235 | { | ||
1945 | 236 | protected: | ||
1946 | 237 | DataOffer(struct wl_client* client, struct wl_resource* parent, uint32_t id) | ||
1947 | 238 | : client{client}, | ||
1948 | 239 | resource{wl_resource_create(client, &wl_data_offer_interface, wl_resource_get_version(parent), id)} | ||
1949 | 240 | { | ||
1950 | 241 | if (resource == nullptr) | ||
1951 | 242 | { | ||
1952 | 243 | wl_resource_post_no_memory(parent); | ||
1953 | 244 | BOOST_THROW_EXCEPTION((std::bad_alloc{})); | ||
1954 | 245 | } | ||
1955 | 246 | wl_resource_set_implementation(resource, &vtable, this, nullptr); | ||
1956 | 247 | } | ||
1957 | 248 | virtual ~DataOffer() = default; | ||
1958 | 249 | |||
1959 | 250 | virtual void accept(uint32_t serial, std::experimental::optional<std::string> const& mime_type) = 0; | ||
1960 | 251 | virtual void receive(std::string const& mime_type, mir::Fd fd) = 0; | ||
1961 | 252 | virtual void destroy() = 0; | ||
1962 | 253 | virtual void finish() = 0; | ||
1963 | 254 | virtual void set_actions(uint32_t dnd_actions, uint32_t preferred_action) = 0; | ||
1964 | 255 | |||
1965 | 256 | struct wl_client* const client; | ||
1966 | 257 | struct wl_resource* const resource; | ||
1967 | 258 | |||
1968 | 259 | private: | ||
1969 | 260 | static void accept_thunk(struct wl_client*, struct wl_resource* resource, uint32_t serial, char const* mime_type) | ||
1970 | 261 | { | ||
1971 | 262 | auto me = static_cast<DataOffer*>(wl_resource_get_user_data(resource)); | ||
1972 | 263 | std::experimental::optional<std::string> mime_type_resolved; | ||
1973 | 264 | if (mime_type != nullptr) | ||
1974 | 265 | { | ||
1975 | 266 | mime_type_resolved = std::experimental::make_optional<std::string>(mime_type); | ||
1976 | 267 | } | ||
1977 | 268 | me->accept(serial, mime_type_resolved); | ||
1978 | 269 | } | ||
1979 | 270 | |||
1980 | 271 | static void receive_thunk(struct wl_client*, struct wl_resource* resource, char const* mime_type, int fd) | ||
1981 | 272 | { | ||
1982 | 273 | auto me = static_cast<DataOffer*>(wl_resource_get_user_data(resource)); | ||
1983 | 274 | mir::Fd fd_resolved{fd}; | ||
1984 | 275 | me->receive(mime_type, fd_resolved); | ||
1985 | 276 | } | ||
1986 | 277 | |||
1987 | 278 | static void destroy_thunk(struct wl_client*, struct wl_resource* resource) | ||
1988 | 279 | { | ||
1989 | 280 | auto me = static_cast<DataOffer*>(wl_resource_get_user_data(resource)); | ||
1990 | 281 | me->destroy(); | ||
1991 | 282 | } | ||
1992 | 283 | |||
1993 | 284 | static void finish_thunk(struct wl_client*, struct wl_resource* resource) | ||
1994 | 285 | { | ||
1995 | 286 | auto me = static_cast<DataOffer*>(wl_resource_get_user_data(resource)); | ||
1996 | 287 | me->finish(); | ||
1997 | 288 | } | ||
1998 | 289 | |||
1999 | 290 | static void set_actions_thunk(struct wl_client*, struct wl_resource* resource, uint32_t dnd_actions, uint32_t preferred_action) | ||
2000 | 291 | { | ||
2001 | 292 | auto me = static_cast<DataOffer*>(wl_resource_get_user_data(resource)); | ||
2002 | 293 | me->set_actions(dnd_actions, preferred_action); | ||
2003 | 294 | } | ||
2004 | 295 | |||
2005 | 296 | static struct wl_data_offer_interface const vtable; | ||
2006 | 297 | }; | ||
2007 | 298 | |||
2008 | 299 | struct wl_data_offer_interface const DataOffer::vtable = { | ||
2009 | 300 | accept_thunk, | ||
2010 | 301 | receive_thunk, | ||
2011 | 302 | destroy_thunk, | ||
2012 | 303 | finish_thunk, | ||
2013 | 304 | set_actions_thunk, | ||
2014 | 305 | }; | ||
2015 | 306 | |||
2016 | 307 | |||
2017 | 308 | class DataSource | ||
2018 | 309 | { | ||
2019 | 310 | protected: | ||
2020 | 311 | DataSource(struct wl_client* client, struct wl_resource* parent, uint32_t id) | ||
2021 | 312 | : client{client}, | ||
2022 | 313 | resource{wl_resource_create(client, &wl_data_source_interface, wl_resource_get_version(parent), id)} | ||
2023 | 314 | { | ||
2024 | 315 | if (resource == nullptr) | ||
2025 | 316 | { | ||
2026 | 317 | wl_resource_post_no_memory(parent); | ||
2027 | 318 | BOOST_THROW_EXCEPTION((std::bad_alloc{})); | ||
2028 | 319 | } | ||
2029 | 320 | wl_resource_set_implementation(resource, &vtable, this, nullptr); | ||
2030 | 321 | } | ||
2031 | 322 | virtual ~DataSource() = default; | ||
2032 | 323 | |||
2033 | 324 | virtual void offer(std::string const& mime_type) = 0; | ||
2034 | 325 | virtual void destroy() = 0; | ||
2035 | 326 | virtual void set_actions(uint32_t dnd_actions) = 0; | ||
2036 | 327 | |||
2037 | 328 | struct wl_client* const client; | ||
2038 | 329 | struct wl_resource* const resource; | ||
2039 | 330 | |||
2040 | 331 | private: | ||
2041 | 332 | static void offer_thunk(struct wl_client*, struct wl_resource* resource, char const* mime_type) | ||
2042 | 333 | { | ||
2043 | 334 | auto me = static_cast<DataSource*>(wl_resource_get_user_data(resource)); | ||
2044 | 335 | me->offer(mime_type); | ||
2045 | 336 | } | ||
2046 | 337 | |||
2047 | 338 | static void destroy_thunk(struct wl_client*, struct wl_resource* resource) | ||
2048 | 339 | { | ||
2049 | 340 | auto me = static_cast<DataSource*>(wl_resource_get_user_data(resource)); | ||
2050 | 341 | me->destroy(); | ||
2051 | 342 | } | ||
2052 | 343 | |||
2053 | 344 | static void set_actions_thunk(struct wl_client*, struct wl_resource* resource, uint32_t dnd_actions) | ||
2054 | 345 | { | ||
2055 | 346 | auto me = static_cast<DataSource*>(wl_resource_get_user_data(resource)); | ||
2056 | 347 | me->set_actions(dnd_actions); | ||
2057 | 348 | } | ||
2058 | 349 | |||
2059 | 350 | static struct wl_data_source_interface const vtable; | ||
2060 | 351 | }; | ||
2061 | 352 | |||
2062 | 353 | struct wl_data_source_interface const DataSource::vtable = { | ||
2063 | 354 | offer_thunk, | ||
2064 | 355 | destroy_thunk, | ||
2065 | 356 | set_actions_thunk, | ||
2066 | 357 | }; | ||
2067 | 358 | |||
2068 | 359 | |||
2069 | 360 | class DataDevice | ||
2070 | 361 | { | ||
2071 | 362 | protected: | ||
2072 | 363 | DataDevice(struct wl_client* client, struct wl_resource* parent, uint32_t id) | ||
2073 | 364 | : client{client}, | ||
2074 | 365 | resource{wl_resource_create(client, &wl_data_device_interface, wl_resource_get_version(parent), id)} | ||
2075 | 366 | { | ||
2076 | 367 | if (resource == nullptr) | ||
2077 | 368 | { | ||
2078 | 369 | wl_resource_post_no_memory(parent); | ||
2079 | 370 | BOOST_THROW_EXCEPTION((std::bad_alloc{})); | ||
2080 | 371 | } | ||
2081 | 372 | wl_resource_set_implementation(resource, &vtable, this, nullptr); | ||
2082 | 373 | } | ||
2083 | 374 | virtual ~DataDevice() = default; | ||
2084 | 375 | |||
2085 | 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; | ||
2086 | 377 | virtual void set_selection(std::experimental::optional<struct wl_resource*> const& source, uint32_t serial) = 0; | ||
2087 | 378 | virtual void release() = 0; | ||
2088 | 379 | |||
2089 | 380 | struct wl_client* const client; | ||
2090 | 381 | struct wl_resource* const resource; | ||
2091 | 382 | |||
2092 | 383 | private: | ||
2093 | 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) | ||
2094 | 385 | { | ||
2095 | 386 | auto me = static_cast<DataDevice*>(wl_resource_get_user_data(resource)); | ||
2096 | 387 | std::experimental::optional<struct wl_resource*> source_resolved; | ||
2097 | 388 | if (source != nullptr) | ||
2098 | 389 | { | ||
2099 | 390 | source_resolved = source; | ||
2100 | 391 | } | ||
2101 | 392 | std::experimental::optional<struct wl_resource*> icon_resolved; | ||
2102 | 393 | if (icon != nullptr) | ||
2103 | 394 | { | ||
2104 | 395 | icon_resolved = icon; | ||
2105 | 396 | } | ||
2106 | 397 | me->start_drag(source_resolved, origin, icon_resolved, serial); | ||
2107 | 398 | } | ||
2108 | 399 | |||
2109 | 400 | static void set_selection_thunk(struct wl_client*, struct wl_resource* resource, struct wl_resource* source, uint32_t serial) | ||
2110 | 401 | { | ||
2111 | 402 | auto me = static_cast<DataDevice*>(wl_resource_get_user_data(resource)); | ||
2112 | 403 | std::experimental::optional<struct wl_resource*> source_resolved; | ||
2113 | 404 | if (source != nullptr) | ||
2114 | 405 | { | ||
2115 | 406 | source_resolved = source; | ||
2116 | 407 | } | ||
2117 | 408 | me->set_selection(source_resolved, serial); | ||
2118 | 409 | } | ||
2119 | 410 | |||
2120 | 411 | static void release_thunk(struct wl_client*, struct wl_resource* resource) | ||
2121 | 412 | { | ||
2122 | 413 | auto me = static_cast<DataDevice*>(wl_resource_get_user_data(resource)); | ||
2123 | 414 | me->release(); | ||
2124 | 415 | } | ||
2125 | 416 | |||
2126 | 417 | static struct wl_data_device_interface const vtable; | ||
2127 | 418 | }; | ||
2128 | 419 | |||
2129 | 420 | struct wl_data_device_interface const DataDevice::vtable = { | ||
2130 | 421 | start_drag_thunk, | ||
2131 | 422 | set_selection_thunk, | ||
2132 | 423 | release_thunk, | ||
2133 | 424 | }; | ||
2134 | 425 | |||
2135 | 426 | |||
2136 | 427 | class DataDeviceManager | ||
2137 | 428 | { | ||
2138 | 429 | protected: | ||
2139 | 430 | DataDeviceManager(struct wl_display* display, uint32_t max_version) | ||
2140 | 431 | : max_version{max_version} | ||
2141 | 432 | { | ||
2142 | 433 | if (!wl_global_create(display, | ||
2143 | 434 | &wl_data_device_manager_interface, max_version, | ||
2144 | 435 | this, &DataDeviceManager::bind)) | ||
2145 | 436 | { | ||
2146 | 437 | BOOST_THROW_EXCEPTION((std::runtime_error{"Failed to export wl_data_device_manager interface"})); | ||
2147 | 438 | } | ||
2148 | 439 | } | ||
2149 | 440 | virtual ~DataDeviceManager() = default; | ||
2150 | 441 | |||
2151 | 442 | virtual void create_data_source(struct wl_client* client, struct wl_resource* resource, uint32_t id) = 0; | ||
2152 | 443 | virtual void get_data_device(struct wl_client* client, struct wl_resource* resource, uint32_t id, struct wl_resource* seat) = 0; | ||
2153 | 444 | |||
2154 | 445 | private: | ||
2155 | 446 | static void create_data_source_thunk(struct wl_client* client, struct wl_resource* resource, uint32_t id) | ||
2156 | 447 | { | ||
2157 | 448 | auto me = static_cast<DataDeviceManager*>(wl_resource_get_user_data(resource)); | ||
2158 | 449 | me->create_data_source(client, resource, id); | ||
2159 | 450 | } | ||
2160 | 451 | |||
2161 | 452 | static void get_data_device_thunk(struct wl_client* client, struct wl_resource* resource, uint32_t id, struct wl_resource* seat) | ||
2162 | 453 | { | ||
2163 | 454 | auto me = static_cast<DataDeviceManager*>(wl_resource_get_user_data(resource)); | ||
2164 | 455 | me->get_data_device(client, resource, id, seat); | ||
2165 | 456 | } | ||
2166 | 457 | |||
2167 | 458 | static void bind(struct wl_client* client, void* data, uint32_t version, uint32_t id) | ||
2168 | 459 | { | ||
2169 | 460 | auto me = static_cast<DataDeviceManager*>(data); | ||
2170 | 461 | auto resource = wl_resource_create(client, &wl_data_device_manager_interface, | ||
2171 | 462 | std::min(version, me->max_version), id); | ||
2172 | 463 | if (resource == nullptr) | ||
2173 | 464 | { | ||
2174 | 465 | wl_client_post_no_memory(client); | ||
2175 | 466 | BOOST_THROW_EXCEPTION((std::bad_alloc{})); | ||
2176 | 467 | } | ||
2177 | 468 | wl_resource_set_implementation(resource, &vtable, me, nullptr); | ||
2178 | 469 | } | ||
2179 | 470 | |||
2180 | 471 | uint32_t const max_version; | ||
2181 | 472 | static struct wl_data_device_manager_interface const vtable; | ||
2182 | 473 | }; | ||
2183 | 474 | |||
2184 | 475 | struct wl_data_device_manager_interface const DataDeviceManager::vtable = { | ||
2185 | 476 | create_data_source_thunk, | ||
2186 | 477 | get_data_device_thunk, | ||
2187 | 478 | }; | ||
2188 | 479 | |||
2189 | 480 | |||
2190 | 481 | class Shell | ||
2191 | 482 | { | ||
2192 | 483 | protected: | ||
2193 | 484 | Shell(struct wl_display* display, uint32_t max_version) | ||
2194 | 485 | : max_version{max_version} | ||
2195 | 486 | { | ||
2196 | 487 | if (!wl_global_create(display, | ||
2197 | 488 | &wl_shell_interface, max_version, | ||
2198 | 489 | this, &Shell::bind)) | ||
2199 | 490 | { | ||
2200 | 491 | BOOST_THROW_EXCEPTION((std::runtime_error{"Failed to export wl_shell interface"})); | ||
2201 | 492 | } | ||
2202 | 493 | } | ||
2203 | 494 | virtual ~Shell() = default; | ||
2204 | 495 | |||
2205 | 496 | virtual void get_shell_surface(struct wl_client* client, struct wl_resource* resource, uint32_t id, struct wl_resource* surface) = 0; | ||
2206 | 497 | |||
2207 | 498 | private: | ||
2208 | 499 | static void get_shell_surface_thunk(struct wl_client* client, struct wl_resource* resource, uint32_t id, struct wl_resource* surface) | ||
2209 | 500 | { | ||
2210 | 501 | auto me = static_cast<Shell*>(wl_resource_get_user_data(resource)); | ||
2211 | 502 | me->get_shell_surface(client, resource, id, surface); | ||
2212 | 503 | } | ||
2213 | 504 | |||
2214 | 505 | static void bind(struct wl_client* client, void* data, uint32_t version, uint32_t id) | ||
2215 | 506 | { | ||
2216 | 507 | auto me = static_cast<Shell*>(data); | ||
2217 | 508 | auto resource = wl_resource_create(client, &wl_shell_interface, | ||
2218 | 509 | std::min(version, me->max_version), id); | ||
2219 | 510 | if (resource == nullptr) | ||
2220 | 511 | { | ||
2221 | 512 | wl_client_post_no_memory(client); | ||
2222 | 513 | BOOST_THROW_EXCEPTION((std::bad_alloc{})); | ||
2223 | 514 | } | ||
2224 | 515 | wl_resource_set_implementation(resource, &vtable, me, nullptr); | ||
2225 | 516 | } | ||
2226 | 517 | |||
2227 | 518 | uint32_t const max_version; | ||
2228 | 519 | static struct wl_shell_interface const vtable; | ||
2229 | 520 | }; | ||
2230 | 521 | |||
2231 | 522 | struct wl_shell_interface const Shell::vtable = { | ||
2232 | 523 | get_shell_surface_thunk, | ||
2233 | 524 | }; | ||
2234 | 525 | |||
2235 | 526 | |||
2236 | 527 | class ShellSurface | ||
2237 | 528 | { | ||
2238 | 529 | protected: | ||
2239 | 530 | ShellSurface(struct wl_client* client, struct wl_resource* parent, uint32_t id) | ||
2240 | 531 | : client{client}, | ||
2241 | 532 | resource{wl_resource_create(client, &wl_shell_surface_interface, wl_resource_get_version(parent), id)} | ||
2242 | 533 | { | ||
2243 | 534 | if (resource == nullptr) | ||
2244 | 535 | { | ||
2245 | 536 | wl_resource_post_no_memory(parent); | ||
2246 | 537 | BOOST_THROW_EXCEPTION((std::bad_alloc{})); | ||
2247 | 538 | } | ||
2248 | 539 | wl_resource_set_implementation(resource, &vtable, this, nullptr); | ||
2249 | 540 | } | ||
2250 | 541 | virtual ~ShellSurface() = default; | ||
2251 | 542 | |||
2252 | 543 | virtual void pong(uint32_t serial) = 0; | ||
2253 | 544 | virtual void move(struct wl_resource* seat, uint32_t serial) = 0; | ||
2254 | 545 | virtual void resize(struct wl_resource* seat, uint32_t serial, uint32_t edges) = 0; | ||
2255 | 546 | virtual void set_toplevel() = 0; | ||
2256 | 547 | virtual void set_transient(struct wl_resource* parent, int32_t x, int32_t y, uint32_t flags) = 0; | ||
2257 | 548 | virtual void set_fullscreen(uint32_t method, uint32_t framerate, std::experimental::optional<struct wl_resource*> const& output) = 0; | ||
2258 | 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; | ||
2259 | 550 | virtual void set_maximized(std::experimental::optional<struct wl_resource*> const& output) = 0; | ||
2260 | 551 | virtual void set_title(std::string const& title) = 0; | ||
2261 | 552 | virtual void set_class(std::string const& class_) = 0; | ||
2262 | 553 | |||
2263 | 554 | struct wl_client* const client; | ||
2264 | 555 | struct wl_resource* const resource; | ||
2265 | 556 | |||
2266 | 557 | private: | ||
2267 | 558 | static void pong_thunk(struct wl_client*, struct wl_resource* resource, uint32_t serial) | ||
2268 | 559 | { | ||
2269 | 560 | auto me = static_cast<ShellSurface*>(wl_resource_get_user_data(resource)); | ||
2270 | 561 | me->pong(serial); | ||
2271 | 562 | } | ||
2272 | 563 | |||
2273 | 564 | static void move_thunk(struct wl_client*, struct wl_resource* resource, struct wl_resource* seat, uint32_t serial) | ||
2274 | 565 | { | ||
2275 | 566 | auto me = static_cast<ShellSurface*>(wl_resource_get_user_data(resource)); | ||
2276 | 567 | me->move(seat, serial); | ||
2277 | 568 | } | ||
2278 | 569 | |||
2279 | 570 | static void resize_thunk(struct wl_client*, struct wl_resource* resource, struct wl_resource* seat, uint32_t serial, uint32_t edges) | ||
2280 | 571 | { | ||
2281 | 572 | auto me = static_cast<ShellSurface*>(wl_resource_get_user_data(resource)); | ||
2282 | 573 | me->resize(seat, serial, edges); | ||
2283 | 574 | } | ||
2284 | 575 | |||
2285 | 576 | static void set_toplevel_thunk(struct wl_client*, struct wl_resource* resource) | ||
2286 | 577 | { | ||
2287 | 578 | auto me = static_cast<ShellSurface*>(wl_resource_get_user_data(resource)); | ||
2288 | 579 | me->set_toplevel(); | ||
2289 | 580 | } | ||
2290 | 581 | |||
2291 | 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) | ||
2292 | 583 | { | ||
2293 | 584 | auto me = static_cast<ShellSurface*>(wl_resource_get_user_data(resource)); | ||
2294 | 585 | me->set_transient(parent, x, y, flags); | ||
2295 | 586 | } | ||
2296 | 587 | |||
2297 | 588 | static void set_fullscreen_thunk(struct wl_client*, struct wl_resource* resource, uint32_t method, uint32_t framerate, struct wl_resource* output) | ||
2298 | 589 | { | ||
2299 | 590 | auto me = static_cast<ShellSurface*>(wl_resource_get_user_data(resource)); | ||
2300 | 591 | std::experimental::optional<struct wl_resource*> output_resolved; | ||
2301 | 592 | if (output != nullptr) | ||
2302 | 593 | { | ||
2303 | 594 | output_resolved = output; | ||
2304 | 595 | } | ||
2305 | 596 | me->set_fullscreen(method, framerate, output_resolved); | ||
2306 | 597 | } | ||
2307 | 598 | |||
2308 | 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) | ||
2309 | 600 | { | ||
2310 | 601 | auto me = static_cast<ShellSurface*>(wl_resource_get_user_data(resource)); | ||
2311 | 602 | me->set_popup(seat, serial, parent, x, y, flags); | ||
2312 | 603 | } | ||
2313 | 604 | |||
2314 | 605 | static void set_maximized_thunk(struct wl_client*, struct wl_resource* resource, struct wl_resource* output) | ||
2315 | 606 | { | ||
2316 | 607 | auto me = static_cast<ShellSurface*>(wl_resource_get_user_data(resource)); | ||
2317 | 608 | std::experimental::optional<struct wl_resource*> output_resolved; | ||
2318 | 609 | if (output != nullptr) | ||
2319 | 610 | { | ||
2320 | 611 | output_resolved = output; | ||
2321 | 612 | } | ||
2322 | 613 | me->set_maximized(output_resolved); | ||
2323 | 614 | } | ||
2324 | 615 | |||
2325 | 616 | static void set_title_thunk(struct wl_client*, struct wl_resource* resource, char const* title) | ||
2326 | 617 | { | ||
2327 | 618 | auto me = static_cast<ShellSurface*>(wl_resource_get_user_data(resource)); | ||
2328 | 619 | me->set_title(title); | ||
2329 | 620 | } | ||
2330 | 621 | |||
2331 | 622 | static void set_class_thunk(struct wl_client*, struct wl_resource* resource, char const* class_) | ||
2332 | 623 | { | ||
2333 | 624 | auto me = static_cast<ShellSurface*>(wl_resource_get_user_data(resource)); | ||
2334 | 625 | me->set_class(class_); | ||
2335 | 626 | } | ||
2336 | 627 | |||
2337 | 628 | static struct wl_shell_surface_interface const vtable; | ||
2338 | 629 | }; | ||
2339 | 630 | |||
2340 | 631 | struct wl_shell_surface_interface const ShellSurface::vtable = { | ||
2341 | 632 | pong_thunk, | ||
2342 | 633 | move_thunk, | ||
2343 | 634 | resize_thunk, | ||
2344 | 635 | set_toplevel_thunk, | ||
2345 | 636 | set_transient_thunk, | ||
2346 | 637 | set_fullscreen_thunk, | ||
2347 | 638 | set_popup_thunk, | ||
2348 | 639 | set_maximized_thunk, | ||
2349 | 640 | set_title_thunk, | ||
2350 | 641 | set_class_thunk, | ||
2351 | 642 | }; | ||
2352 | 643 | |||
2353 | 644 | |||
2354 | 645 | class Surface | ||
2355 | 646 | { | ||
2356 | 647 | protected: | ||
2357 | 648 | Surface(struct wl_client* client, struct wl_resource* parent, uint32_t id) | ||
2358 | 649 | : client{client}, | ||
2359 | 650 | resource{wl_resource_create(client, &wl_surface_interface, wl_resource_get_version(parent), id)} | ||
2360 | 651 | { | ||
2361 | 652 | if (resource == nullptr) | ||
2362 | 653 | { | ||
2363 | 654 | wl_resource_post_no_memory(parent); | ||
2364 | 655 | BOOST_THROW_EXCEPTION((std::bad_alloc{})); | ||
2365 | 656 | } | ||
2366 | 657 | wl_resource_set_implementation(resource, &vtable, this, nullptr); | ||
2367 | 658 | } | ||
2368 | 659 | virtual ~Surface() = default; | ||
2369 | 660 | |||
2370 | 661 | virtual void destroy() = 0; | ||
2371 | 662 | virtual void attach(std::experimental::optional<struct wl_resource*> const& buffer, int32_t x, int32_t y) = 0; | ||
2372 | 663 | virtual void damage(int32_t x, int32_t y, int32_t width, int32_t height) = 0; | ||
2373 | 664 | virtual void frame(uint32_t callback) = 0; | ||
2374 | 665 | virtual void set_opaque_region(std::experimental::optional<struct wl_resource*> const& region) = 0; | ||
2375 | 666 | virtual void set_input_region(std::experimental::optional<struct wl_resource*> const& region) = 0; | ||
2376 | 667 | virtual void commit() = 0; | ||
2377 | 668 | virtual void set_buffer_transform(int32_t transform) = 0; | ||
2378 | 669 | virtual void set_buffer_scale(int32_t scale) = 0; | ||
2379 | 670 | virtual void damage_buffer(int32_t x, int32_t y, int32_t width, int32_t height) = 0; | ||
2380 | 671 | |||
2381 | 672 | struct wl_client* const client; | ||
2382 | 673 | struct wl_resource* const resource; | ||
2383 | 674 | |||
2384 | 675 | private: | ||
2385 | 676 | static void destroy_thunk(struct wl_client*, struct wl_resource* resource) | ||
2386 | 677 | { | ||
2387 | 678 | auto me = static_cast<Surface*>(wl_resource_get_user_data(resource)); | ||
2388 | 679 | me->destroy(); | ||
2389 | 680 | } | ||
2390 | 681 | |||
2391 | 682 | static void attach_thunk(struct wl_client*, struct wl_resource* resource, struct wl_resource* buffer, int32_t x, int32_t y) | ||
2392 | 683 | { | ||
2393 | 684 | auto me = static_cast<Surface*>(wl_resource_get_user_data(resource)); | ||
2394 | 685 | std::experimental::optional<struct wl_resource*> buffer_resolved; | ||
2395 | 686 | if (buffer != nullptr) | ||
2396 | 687 | { | ||
2397 | 688 | buffer_resolved = buffer; | ||
2398 | 689 | } | ||
2399 | 690 | me->attach(buffer_resolved, x, y); | ||
2400 | 691 | } | ||
2401 | 692 | |||
2402 | 693 | static void damage_thunk(struct wl_client*, struct wl_resource* resource, int32_t x, int32_t y, int32_t width, int32_t height) | ||
2403 | 694 | { | ||
2404 | 695 | auto me = static_cast<Surface*>(wl_resource_get_user_data(resource)); | ||
2405 | 696 | me->damage(x, y, width, height); | ||
2406 | 697 | } | ||
2407 | 698 | |||
2408 | 699 | static void frame_thunk(struct wl_client*, struct wl_resource* resource, uint32_t callback) | ||
2409 | 700 | { | ||
2410 | 701 | auto me = static_cast<Surface*>(wl_resource_get_user_data(resource)); | ||
2411 | 702 | me->frame(callback); | ||
2412 | 703 | } | ||
2413 | 704 | |||
2414 | 705 | static void set_opaque_region_thunk(struct wl_client*, struct wl_resource* resource, struct wl_resource* region) | ||
2415 | 706 | { | ||
2416 | 707 | auto me = static_cast<Surface*>(wl_resource_get_user_data(resource)); | ||
2417 | 708 | std::experimental::optional<struct wl_resource*> region_resolved; | ||
2418 | 709 | if (region != nullptr) | ||
2419 | 710 | { | ||
2420 | 711 | region_resolved = region; | ||
2421 | 712 | } | ||
2422 | 713 | me->set_opaque_region(region_resolved); | ||
2423 | 714 | } | ||
2424 | 715 | |||
2425 | 716 | static void set_input_region_thunk(struct wl_client*, struct wl_resource* resource, struct wl_resource* region) | ||
2426 | 717 | { | ||
2427 | 718 | auto me = static_cast<Surface*>(wl_resource_get_user_data(resource)); | ||
2428 | 719 | std::experimental::optional<struct wl_resource*> region_resolved; | ||
2429 | 720 | if (region != nullptr) | ||
2430 | 721 | { | ||
2431 | 722 | region_resolved = region; | ||
2432 | 723 | } | ||
2433 | 724 | me->set_input_region(region_resolved); | ||
2434 | 725 | } | ||
2435 | 726 | |||
2436 | 727 | static void commit_thunk(struct wl_client*, struct wl_resource* resource) | ||
2437 | 728 | { | ||
2438 | 729 | auto me = static_cast<Surface*>(wl_resource_get_user_data(resource)); | ||
2439 | 730 | me->commit(); | ||
2440 | 731 | } | ||
2441 | 732 | |||
2442 | 733 | static void set_buffer_transform_thunk(struct wl_client*, struct wl_resource* resource, int32_t transform) | ||
2443 | 734 | { | ||
2444 | 735 | auto me = static_cast<Surface*>(wl_resource_get_user_data(resource)); | ||
2445 | 736 | me->set_buffer_transform(transform); | ||
2446 | 737 | } | ||
2447 | 738 | |||
2448 | 739 | static void set_buffer_scale_thunk(struct wl_client*, struct wl_resource* resource, int32_t scale) | ||
2449 | 740 | { | ||
2450 | 741 | auto me = static_cast<Surface*>(wl_resource_get_user_data(resource)); | ||
2451 | 742 | me->set_buffer_scale(scale); | ||
2452 | 743 | } | ||
2453 | 744 | |||
2454 | 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) | ||
2455 | 746 | { | ||
2456 | 747 | auto me = static_cast<Surface*>(wl_resource_get_user_data(resource)); | ||
2457 | 748 | me->damage_buffer(x, y, width, height); | ||
2458 | 749 | } | ||
2459 | 750 | |||
2460 | 751 | static struct wl_surface_interface const vtable; | ||
2461 | 752 | }; | ||
2462 | 753 | |||
2463 | 754 | struct wl_surface_interface const Surface::vtable = { | ||
2464 | 755 | destroy_thunk, | ||
2465 | 756 | attach_thunk, | ||
2466 | 757 | damage_thunk, | ||
2467 | 758 | frame_thunk, | ||
2468 | 759 | set_opaque_region_thunk, | ||
2469 | 760 | set_input_region_thunk, | ||
2470 | 761 | commit_thunk, | ||
2471 | 762 | set_buffer_transform_thunk, | ||
2472 | 763 | set_buffer_scale_thunk, | ||
2473 | 764 | damage_buffer_thunk, | ||
2474 | 765 | }; | ||
2475 | 766 | |||
2476 | 767 | |||
2477 | 768 | class Seat | ||
2478 | 769 | { | ||
2479 | 770 | protected: | ||
2480 | 771 | Seat(struct wl_display* display, uint32_t max_version) | ||
2481 | 772 | : max_version{max_version} | ||
2482 | 773 | { | ||
2483 | 774 | if (!wl_global_create(display, | ||
2484 | 775 | &wl_seat_interface, max_version, | ||
2485 | 776 | this, &Seat::bind)) | ||
2486 | 777 | { | ||
2487 | 778 | BOOST_THROW_EXCEPTION((std::runtime_error{"Failed to export wl_seat interface"})); | ||
2488 | 779 | } | ||
2489 | 780 | } | ||
2490 | 781 | virtual ~Seat() = default; | ||
2491 | 782 | |||
2492 | 783 | virtual void get_pointer(struct wl_client* client, struct wl_resource* resource, uint32_t id) = 0; | ||
2493 | 784 | virtual void get_keyboard(struct wl_client* client, struct wl_resource* resource, uint32_t id) = 0; | ||
2494 | 785 | virtual void get_touch(struct wl_client* client, struct wl_resource* resource, uint32_t id) = 0; | ||
2495 | 786 | virtual void release(struct wl_client* client, struct wl_resource* resource) = 0; | ||
2496 | 787 | |||
2497 | 788 | private: | ||
2498 | 789 | static void get_pointer_thunk(struct wl_client* client, struct wl_resource* resource, uint32_t id) | ||
2499 | 790 | { | ||
2500 | 791 | auto me = static_cast<Seat*>(wl_resource_get_user_data(resource)); | ||
2501 | 792 | me->get_pointer(client, resource, id); | ||
2502 | 793 | } | ||
2503 | 794 | |||
2504 | 795 | static void get_keyboard_thunk(struct wl_client* client, struct wl_resource* resource, uint32_t id) | ||
2505 | 796 | { | ||
2506 | 797 | auto me = static_cast<Seat*>(wl_resource_get_user_data(resource)); | ||
2507 | 798 | me->get_keyboard(client, resource, id); | ||
2508 | 799 | } | ||
2509 | 800 | |||
2510 | 801 | static void get_touch_thunk(struct wl_client* client, struct wl_resource* resource, uint32_t id) | ||
2511 | 802 | { | ||
2512 | 803 | auto me = static_cast<Seat*>(wl_resource_get_user_data(resource)); | ||
2513 | 804 | me->get_touch(client, resource, id); | ||
2514 | 805 | } | ||
2515 | 806 | |||
2516 | 807 | static void release_thunk(struct wl_client* client, struct wl_resource* resource) | ||
2517 | 808 | { | ||
2518 | 809 | auto me = static_cast<Seat*>(wl_resource_get_user_data(resource)); | ||
2519 | 810 | me->release(client, resource); | ||
2520 | 811 | } | ||
2521 | 812 | |||
2522 | 813 | static void bind(struct wl_client* client, void* data, uint32_t version, uint32_t id) | ||
2523 | 814 | { | ||
2524 | 815 | auto me = static_cast<Seat*>(data); | ||
2525 | 816 | auto resource = wl_resource_create(client, &wl_seat_interface, | ||
2526 | 817 | std::min(version, me->max_version), id); | ||
2527 | 818 | if (resource == nullptr) | ||
2528 | 819 | { | ||
2529 | 820 | wl_client_post_no_memory(client); | ||
2530 | 821 | BOOST_THROW_EXCEPTION((std::bad_alloc{})); | ||
2531 | 822 | } | ||
2532 | 823 | wl_resource_set_implementation(resource, &vtable, me, nullptr); | ||
2533 | 824 | } | ||
2534 | 825 | |||
2535 | 826 | uint32_t const max_version; | ||
2536 | 827 | static struct wl_seat_interface const vtable; | ||
2537 | 828 | }; | ||
2538 | 829 | |||
2539 | 830 | struct wl_seat_interface const Seat::vtable = { | ||
2540 | 831 | get_pointer_thunk, | ||
2541 | 832 | get_keyboard_thunk, | ||
2542 | 833 | get_touch_thunk, | ||
2543 | 834 | release_thunk, | ||
2544 | 835 | }; | ||
2545 | 836 | |||
2546 | 837 | |||
2547 | 838 | class Pointer | ||
2548 | 839 | { | ||
2549 | 840 | protected: | ||
2550 | 841 | Pointer(struct wl_client* client, struct wl_resource* parent, uint32_t id) | ||
2551 | 842 | : client{client}, | ||
2552 | 843 | resource{wl_resource_create(client, &wl_pointer_interface, wl_resource_get_version(parent), id)} | ||
2553 | 844 | { | ||
2554 | 845 | if (resource == nullptr) | ||
2555 | 846 | { | ||
2556 | 847 | wl_resource_post_no_memory(parent); | ||
2557 | 848 | BOOST_THROW_EXCEPTION((std::bad_alloc{})); | ||
2558 | 849 | } | ||
2559 | 850 | wl_resource_set_implementation(resource, &vtable, this, nullptr); | ||
2560 | 851 | } | ||
2561 | 852 | virtual ~Pointer() = default; | ||
2562 | 853 | |||
2563 | 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; | ||
2564 | 855 | virtual void release() = 0; | ||
2565 | 856 | |||
2566 | 857 | struct wl_client* const client; | ||
2567 | 858 | struct wl_resource* const resource; | ||
2568 | 859 | |||
2569 | 860 | private: | ||
2570 | 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) | ||
2571 | 862 | { | ||
2572 | 863 | auto me = static_cast<Pointer*>(wl_resource_get_user_data(resource)); | ||
2573 | 864 | std::experimental::optional<struct wl_resource*> surface_resolved; | ||
2574 | 865 | if (surface != nullptr) | ||
2575 | 866 | { | ||
2576 | 867 | surface_resolved = surface; | ||
2577 | 868 | } | ||
2578 | 869 | me->set_cursor(serial, surface_resolved, hotspot_x, hotspot_y); | ||
2579 | 870 | } | ||
2580 | 871 | |||
2581 | 872 | static void release_thunk(struct wl_client*, struct wl_resource* resource) | ||
2582 | 873 | { | ||
2583 | 874 | auto me = static_cast<Pointer*>(wl_resource_get_user_data(resource)); | ||
2584 | 875 | me->release(); | ||
2585 | 876 | } | ||
2586 | 877 | |||
2587 | 878 | static struct wl_pointer_interface const vtable; | ||
2588 | 879 | }; | ||
2589 | 880 | |||
2590 | 881 | struct wl_pointer_interface const Pointer::vtable = { | ||
2591 | 882 | set_cursor_thunk, | ||
2592 | 883 | release_thunk, | ||
2593 | 884 | }; | ||
2594 | 885 | |||
2595 | 886 | |||
2596 | 887 | class Keyboard | ||
2597 | 888 | { | ||
2598 | 889 | protected: | ||
2599 | 890 | Keyboard(struct wl_client* client, struct wl_resource* parent, uint32_t id) | ||
2600 | 891 | : client{client}, | ||
2601 | 892 | resource{wl_resource_create(client, &wl_keyboard_interface, wl_resource_get_version(parent), id)} | ||
2602 | 893 | { | ||
2603 | 894 | if (resource == nullptr) | ||
2604 | 895 | { | ||
2605 | 896 | wl_resource_post_no_memory(parent); | ||
2606 | 897 | BOOST_THROW_EXCEPTION((std::bad_alloc{})); | ||
2607 | 898 | } | ||
2608 | 899 | wl_resource_set_implementation(resource, &vtable, this, nullptr); | ||
2609 | 900 | } | ||
2610 | 901 | virtual ~Keyboard() = default; | ||
2611 | 902 | |||
2612 | 903 | virtual void release() = 0; | ||
2613 | 904 | |||
2614 | 905 | struct wl_client* const client; | ||
2615 | 906 | struct wl_resource* const resource; | ||
2616 | 907 | |||
2617 | 908 | private: | ||
2618 | 909 | static void release_thunk(struct wl_client*, struct wl_resource* resource) | ||
2619 | 910 | { | ||
2620 | 911 | auto me = static_cast<Keyboard*>(wl_resource_get_user_data(resource)); | ||
2621 | 912 | me->release(); | ||
2622 | 913 | } | ||
2623 | 914 | |||
2624 | 915 | static struct wl_keyboard_interface const vtable; | ||
2625 | 916 | }; | ||
2626 | 917 | |||
2627 | 918 | struct wl_keyboard_interface const Keyboard::vtable = { | ||
2628 | 919 | release_thunk, | ||
2629 | 920 | }; | ||
2630 | 921 | |||
2631 | 922 | |||
2632 | 923 | class Touch | ||
2633 | 924 | { | ||
2634 | 925 | protected: | ||
2635 | 926 | Touch(struct wl_client* client, struct wl_resource* parent, uint32_t id) | ||
2636 | 927 | : client{client}, | ||
2637 | 928 | resource{wl_resource_create(client, &wl_touch_interface, wl_resource_get_version(parent), id)} | ||
2638 | 929 | { | ||
2639 | 930 | if (resource == nullptr) | ||
2640 | 931 | { | ||
2641 | 932 | wl_resource_post_no_memory(parent); | ||
2642 | 933 | BOOST_THROW_EXCEPTION((std::bad_alloc{})); | ||
2643 | 934 | } | ||
2644 | 935 | wl_resource_set_implementation(resource, &vtable, this, nullptr); | ||
2645 | 936 | } | ||
2646 | 937 | virtual ~Touch() = default; | ||
2647 | 938 | |||
2648 | 939 | virtual void release() = 0; | ||
2649 | 940 | |||
2650 | 941 | struct wl_client* const client; | ||
2651 | 942 | struct wl_resource* const resource; | ||
2652 | 943 | |||
2653 | 944 | private: | ||
2654 | 945 | static void release_thunk(struct wl_client*, struct wl_resource* resource) | ||
2655 | 946 | { | ||
2656 | 947 | auto me = static_cast<Touch*>(wl_resource_get_user_data(resource)); | ||
2657 | 948 | me->release(); | ||
2658 | 949 | } | ||
2659 | 950 | |||
2660 | 951 | static struct wl_touch_interface const vtable; | ||
2661 | 952 | }; | ||
2662 | 953 | |||
2663 | 954 | struct wl_touch_interface const Touch::vtable = { | ||
2664 | 955 | release_thunk, | ||
2665 | 956 | }; | ||
2666 | 957 | |||
2667 | 958 | |||
2668 | 959 | class Output | ||
2669 | 960 | { | ||
2670 | 961 | protected: | ||
2671 | 962 | Output(struct wl_display* display, uint32_t max_version) | ||
2672 | 963 | : max_version{max_version} | ||
2673 | 964 | { | ||
2674 | 965 | if (!wl_global_create(display, | ||
2675 | 966 | &wl_output_interface, max_version, | ||
2676 | 967 | this, &Output::bind)) | ||
2677 | 968 | { | ||
2678 | 969 | BOOST_THROW_EXCEPTION((std::runtime_error{"Failed to export wl_output interface"})); | ||
2679 | 970 | } | ||
2680 | 971 | } | ||
2681 | 972 | virtual ~Output() = default; | ||
2682 | 973 | |||
2683 | 974 | virtual void release(struct wl_client* client, struct wl_resource* resource) = 0; | ||
2684 | 975 | |||
2685 | 976 | private: | ||
2686 | 977 | static void release_thunk(struct wl_client* client, struct wl_resource* resource) | ||
2687 | 978 | { | ||
2688 | 979 | auto me = static_cast<Output*>(wl_resource_get_user_data(resource)); | ||
2689 | 980 | me->release(client, resource); | ||
2690 | 981 | } | ||
2691 | 982 | |||
2692 | 983 | static void bind(struct wl_client* client, void* data, uint32_t version, uint32_t id) | ||
2693 | 984 | { | ||
2694 | 985 | auto me = static_cast<Output*>(data); | ||
2695 | 986 | auto resource = wl_resource_create(client, &wl_output_interface, | ||
2696 | 987 | std::min(version, me->max_version), id); | ||
2697 | 988 | if (resource == nullptr) | ||
2698 | 989 | { | ||
2699 | 990 | wl_client_post_no_memory(client); | ||
2700 | 991 | BOOST_THROW_EXCEPTION((std::bad_alloc{})); | ||
2701 | 992 | } | ||
2702 | 993 | wl_resource_set_implementation(resource, &vtable, me, nullptr); | ||
2703 | 994 | } | ||
2704 | 995 | |||
2705 | 996 | uint32_t const max_version; | ||
2706 | 997 | static struct wl_output_interface const vtable; | ||
2707 | 998 | }; | ||
2708 | 999 | |||
2709 | 1000 | struct wl_output_interface const Output::vtable = { | ||
2710 | 1001 | release_thunk, | ||
2711 | 1002 | }; | ||
2712 | 1003 | |||
2713 | 1004 | |||
2714 | 1005 | class Region | ||
2715 | 1006 | { | ||
2716 | 1007 | protected: | ||
2717 | 1008 | Region(struct wl_client* client, struct wl_resource* parent, uint32_t id) | ||
2718 | 1009 | : client{client}, | ||
2719 | 1010 | resource{wl_resource_create(client, &wl_region_interface, wl_resource_get_version(parent), id)} | ||
2720 | 1011 | { | ||
2721 | 1012 | if (resource == nullptr) | ||
2722 | 1013 | { | ||
2723 | 1014 | wl_resource_post_no_memory(parent); | ||
2724 | 1015 | BOOST_THROW_EXCEPTION((std::bad_alloc{})); | ||
2725 | 1016 | } | ||
2726 | 1017 | wl_resource_set_implementation(resource, &vtable, this, nullptr); | ||
2727 | 1018 | } | ||
2728 | 1019 | virtual ~Region() = default; | ||
2729 | 1020 | |||
2730 | 1021 | virtual void destroy() = 0; | ||
2731 | 1022 | virtual void add(int32_t x, int32_t y, int32_t width, int32_t height) = 0; | ||
2732 | 1023 | virtual void subtract(int32_t x, int32_t y, int32_t width, int32_t height) = 0; | ||
2733 | 1024 | |||
2734 | 1025 | struct wl_client* const client; | ||
2735 | 1026 | struct wl_resource* const resource; | ||
2736 | 1027 | |||
2737 | 1028 | private: | ||
2738 | 1029 | static void destroy_thunk(struct wl_client*, struct wl_resource* resource) | ||
2739 | 1030 | { | ||
2740 | 1031 | auto me = static_cast<Region*>(wl_resource_get_user_data(resource)); | ||
2741 | 1032 | me->destroy(); | ||
2742 | 1033 | } | ||
2743 | 1034 | |||
2744 | 1035 | static void add_thunk(struct wl_client*, struct wl_resource* resource, int32_t x, int32_t y, int32_t width, int32_t height) | ||
2745 | 1036 | { | ||
2746 | 1037 | auto me = static_cast<Region*>(wl_resource_get_user_data(resource)); | ||
2747 | 1038 | me->add(x, y, width, height); | ||
2748 | 1039 | } | ||
2749 | 1040 | |||
2750 | 1041 | static void subtract_thunk(struct wl_client*, struct wl_resource* resource, int32_t x, int32_t y, int32_t width, int32_t height) | ||
2751 | 1042 | { | ||
2752 | 1043 | auto me = static_cast<Region*>(wl_resource_get_user_data(resource)); | ||
2753 | 1044 | me->subtract(x, y, width, height); | ||
2754 | 1045 | } | ||
2755 | 1046 | |||
2756 | 1047 | static struct wl_region_interface const vtable; | ||
2757 | 1048 | }; | ||
2758 | 1049 | |||
2759 | 1050 | struct wl_region_interface const Region::vtable = { | ||
2760 | 1051 | destroy_thunk, | ||
2761 | 1052 | add_thunk, | ||
2762 | 1053 | subtract_thunk, | ||
2763 | 1054 | }; | ||
2764 | 1055 | |||
2765 | 1056 | |||
2766 | 1057 | class Subcompositor | ||
2767 | 1058 | { | ||
2768 | 1059 | protected: | ||
2769 | 1060 | Subcompositor(struct wl_display* display, uint32_t max_version) | ||
2770 | 1061 | : max_version{max_version} | ||
2771 | 1062 | { | ||
2772 | 1063 | if (!wl_global_create(display, | ||
2773 | 1064 | &wl_subcompositor_interface, max_version, | ||
2774 | 1065 | this, &Subcompositor::bind)) | ||
2775 | 1066 | { | ||
2776 | 1067 | BOOST_THROW_EXCEPTION((std::runtime_error{"Failed to export wl_subcompositor interface"})); | ||
2777 | 1068 | } | ||
2778 | 1069 | } | ||
2779 | 1070 | virtual ~Subcompositor() = default; | ||
2780 | 1071 | |||
2781 | 1072 | virtual void destroy(struct wl_client* client, struct wl_resource* resource) = 0; | ||
2782 | 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; | ||
2783 | 1074 | |||
2784 | 1075 | private: | ||
2785 | 1076 | static void destroy_thunk(struct wl_client* client, struct wl_resource* resource) | ||
2786 | 1077 | { | ||
2787 | 1078 | auto me = static_cast<Subcompositor*>(wl_resource_get_user_data(resource)); | ||
2788 | 1079 | me->destroy(client, resource); | ||
2789 | 1080 | } | ||
2790 | 1081 | |||
2791 | 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) | ||
2792 | 1083 | { | ||
2793 | 1084 | auto me = static_cast<Subcompositor*>(wl_resource_get_user_data(resource)); | ||
2794 | 1085 | me->get_subsurface(client, resource, id, surface, parent); | ||
2795 | 1086 | } | ||
2796 | 1087 | |||
2797 | 1088 | static void bind(struct wl_client* client, void* data, uint32_t version, uint32_t id) | ||
2798 | 1089 | { | ||
2799 | 1090 | auto me = static_cast<Subcompositor*>(data); | ||
2800 | 1091 | auto resource = wl_resource_create(client, &wl_subcompositor_interface, | ||
2801 | 1092 | std::min(version, me->max_version), id); | ||
2802 | 1093 | if (resource == nullptr) | ||
2803 | 1094 | { | ||
2804 | 1095 | wl_client_post_no_memory(client); | ||
2805 | 1096 | BOOST_THROW_EXCEPTION((std::bad_alloc{})); | ||
2806 | 1097 | } | ||
2807 | 1098 | wl_resource_set_implementation(resource, &vtable, me, nullptr); | ||
2808 | 1099 | } | ||
2809 | 1100 | |||
2810 | 1101 | uint32_t const max_version; | ||
2811 | 1102 | static struct wl_subcompositor_interface const vtable; | ||
2812 | 1103 | }; | ||
2813 | 1104 | |||
2814 | 1105 | struct wl_subcompositor_interface const Subcompositor::vtable = { | ||
2815 | 1106 | destroy_thunk, | ||
2816 | 1107 | get_subsurface_thunk, | ||
2817 | 1108 | }; | ||
2818 | 1109 | |||
2819 | 1110 | |||
2820 | 1111 | class Subsurface | ||
2821 | 1112 | { | ||
2822 | 1113 | protected: | ||
2823 | 1114 | Subsurface(struct wl_client* client, struct wl_resource* parent, uint32_t id) | ||
2824 | 1115 | : client{client}, | ||
2825 | 1116 | resource{wl_resource_create(client, &wl_subsurface_interface, wl_resource_get_version(parent), id)} | ||
2826 | 1117 | { | ||
2827 | 1118 | if (resource == nullptr) | ||
2828 | 1119 | { | ||
2829 | 1120 | wl_resource_post_no_memory(parent); | ||
2830 | 1121 | BOOST_THROW_EXCEPTION((std::bad_alloc{})); | ||
2831 | 1122 | } | ||
2832 | 1123 | wl_resource_set_implementation(resource, &vtable, this, nullptr); | ||
2833 | 1124 | } | ||
2834 | 1125 | virtual ~Subsurface() = default; | ||
2835 | 1126 | |||
2836 | 1127 | virtual void destroy() = 0; | ||
2837 | 1128 | virtual void set_position(int32_t x, int32_t y) = 0; | ||
2838 | 1129 | virtual void place_above(struct wl_resource* sibling) = 0; | ||
2839 | 1130 | virtual void place_below(struct wl_resource* sibling) = 0; | ||
2840 | 1131 | virtual void set_sync() = 0; | ||
2841 | 1132 | virtual void set_desync() = 0; | ||
2842 | 1133 | |||
2843 | 1134 | struct wl_client* const client; | ||
2844 | 1135 | struct wl_resource* const resource; | ||
2845 | 1136 | |||
2846 | 1137 | private: | ||
2847 | 1138 | static void destroy_thunk(struct wl_client*, struct wl_resource* resource) | ||
2848 | 1139 | { | ||
2849 | 1140 | auto me = static_cast<Subsurface*>(wl_resource_get_user_data(resource)); | ||
2850 | 1141 | me->destroy(); | ||
2851 | 1142 | } | ||
2852 | 1143 | |||
2853 | 1144 | static void set_position_thunk(struct wl_client*, struct wl_resource* resource, int32_t x, int32_t y) | ||
2854 | 1145 | { | ||
2855 | 1146 | auto me = static_cast<Subsurface*>(wl_resource_get_user_data(resource)); | ||
2856 | 1147 | me->set_position(x, y); | ||
2857 | 1148 | } | ||
2858 | 1149 | |||
2859 | 1150 | static void place_above_thunk(struct wl_client*, struct wl_resource* resource, struct wl_resource* sibling) | ||
2860 | 1151 | { | ||
2861 | 1152 | auto me = static_cast<Subsurface*>(wl_resource_get_user_data(resource)); | ||
2862 | 1153 | me->place_above(sibling); | ||
2863 | 1154 | } | ||
2864 | 1155 | |||
2865 | 1156 | static void place_below_thunk(struct wl_client*, struct wl_resource* resource, struct wl_resource* sibling) | ||
2866 | 1157 | { | ||
2867 | 1158 | auto me = static_cast<Subsurface*>(wl_resource_get_user_data(resource)); | ||
2868 | 1159 | me->place_below(sibling); | ||
2869 | 1160 | } | ||
2870 | 1161 | |||
2871 | 1162 | static void set_sync_thunk(struct wl_client*, struct wl_resource* resource) | ||
2872 | 1163 | { | ||
2873 | 1164 | auto me = static_cast<Subsurface*>(wl_resource_get_user_data(resource)); | ||
2874 | 1165 | me->set_sync(); | ||
2875 | 1166 | } | ||
2876 | 1167 | |||
2877 | 1168 | static void set_desync_thunk(struct wl_client*, struct wl_resource* resource) | ||
2878 | 1169 | { | ||
2879 | 1170 | auto me = static_cast<Subsurface*>(wl_resource_get_user_data(resource)); | ||
2880 | 1171 | me->set_desync(); | ||
2881 | 1172 | } | ||
2882 | 1173 | |||
2883 | 1174 | static struct wl_subsurface_interface const vtable; | ||
2884 | 1175 | }; | ||
2885 | 1176 | |||
2886 | 1177 | struct wl_subsurface_interface const Subsurface::vtable = { | ||
2887 | 1178 | destroy_thunk, | ||
2888 | 1179 | set_position_thunk, | ||
2889 | 1180 | place_above_thunk, | ||
2890 | 1181 | place_below_thunk, | ||
2891 | 1182 | set_sync_thunk, | ||
2892 | 1183 | set_desync_thunk, | ||
2893 | 1184 | }; | ||
2894 | 1185 | |||
2895 | 1186 | |||
2896 | 1187 | } | ||
2897 | 1188 | } | ||
2898 | 1189 | } | ||
2899 | 0 | 1190 | ||
2900 | === added file 'src/server/frontend/wayland/wayland_connector.cpp' | |||
2901 | --- src/server/frontend/wayland/wayland_connector.cpp 1970-01-01 00:00:00 +0000 | |||
2902 | +++ src/server/frontend/wayland/wayland_connector.cpp 2017-09-07 05:58:57 +0000 | |||
2903 | @@ -0,0 +1,1785 @@ | |||
2904 | 1 | /* | ||
2905 | 2 | * Copyright © 2015 Canonical Ltd. | ||
2906 | 3 | * | ||
2907 | 4 | * This program is free software: you can redistribute it and/or modify it | ||
2908 | 5 | * under the terms of the GNU General Public License version 3, | ||
2909 | 6 | * as published by the Free Software Foundation. | ||
2910 | 7 | * | ||
2911 | 8 | * This program is distributed in the hope that it will be useful, | ||
2912 | 9 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
2913 | 10 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
2914 | 11 | * GNU General Public License for more details. | ||
2915 | 12 | * | ||
2916 | 13 | * You should have received a copy of the GNU General Public License | ||
2917 | 14 | * along with this program. If not, see <http://www.gnu.org/licenses/>. | ||
2918 | 15 | * | ||
2919 | 16 | * Authored by: Christopher James Halse Rogers <christopher.halse.rogers@canonical.com> | ||
2920 | 17 | */ | ||
2921 | 18 | |||
2922 | 19 | #include "wayland_connector.h" | ||
2923 | 20 | |||
2924 | 21 | #include "core_generated_interfaces.h" | ||
2925 | 22 | |||
2926 | 23 | #include "mir/frontend/shell.h" | ||
2927 | 24 | |||
2928 | 25 | #include "mir/compositor/buffer_stream.h" | ||
2929 | 26 | |||
2930 | 27 | #include "mir/frontend/session.h" | ||
2931 | 28 | #include "mir/frontend/event_sink.h" | ||
2932 | 29 | #include "mir/scene/surface_creation_parameters.h" | ||
2933 | 30 | |||
2934 | 31 | #include "mir/graphics/buffer_properties.h" | ||
2935 | 32 | #include "mir/graphics/buffer.h" | ||
2936 | 33 | #include "mir/graphics/display_configuration.h" | ||
2937 | 34 | #include "mir/graphics/graphic_buffer_allocator.h" | ||
2938 | 35 | #include "mir/graphics/wayland_allocator.h" | ||
2939 | 36 | |||
2940 | 37 | #include "mir/renderer/gl/texture_target.h" | ||
2941 | 38 | #include "mir/frontend/buffer_stream_id.h" | ||
2942 | 39 | #include "mir/frontend/display_changer.h" | ||
2943 | 40 | |||
2944 | 41 | #include "mir/executor.h" | ||
2945 | 42 | |||
2946 | 43 | #include <system_error> | ||
2947 | 44 | #include <sys/eventfd.h> | ||
2948 | 45 | #include <wayland-server-core.h> | ||
2949 | 46 | #include <unordered_map> | ||
2950 | 47 | #include <boost/throw_exception.hpp> | ||
2951 | 48 | |||
2952 | 49 | #include <future> | ||
2953 | 50 | #include <functional> | ||
2954 | 51 | #include <type_traits> | ||
2955 | 52 | |||
2956 | 53 | #include <algorithm> | ||
2957 | 54 | #include <iostream> | ||
2958 | 55 | #include <mir/log.h> | ||
2959 | 56 | #include <cstring> | ||
2960 | 57 | #include <deque> | ||
2961 | 58 | #include MIR_SERVER_GL_H | ||
2962 | 59 | #include MIR_SERVER_GLEXT_H | ||
2963 | 60 | |||
2964 | 61 | #include "mir/fd.h" | ||
2965 | 62 | #include "../../../platforms/common/server/shm_buffer.h" | ||
2966 | 63 | |||
2967 | 64 | namespace mf = mir::frontend; | ||
2968 | 65 | namespace mg = mir::graphics; | ||
2969 | 66 | namespace mc = mir::compositor; | ||
2970 | 67 | namespace ms = mir::scene; | ||
2971 | 68 | namespace geom = mir::geometry; | ||
2972 | 69 | |||
2973 | 70 | namespace mir | ||
2974 | 71 | { | ||
2975 | 72 | namespace frontend | ||
2976 | 73 | { | ||
2977 | 74 | |||
2978 | 75 | class WaylandEventSink : public mf::EventSink | ||
2979 | 76 | { | ||
2980 | 77 | public: | ||
2981 | 78 | WaylandEventSink(std::function<void(MirLifecycleState)> const& lifecycle_handler) | ||
2982 | 79 | : lifecycle_handler{lifecycle_handler} | ||
2983 | 80 | { | ||
2984 | 81 | } | ||
2985 | 82 | |||
2986 | 83 | void handle_event(const MirEvent& e) override; | ||
2987 | 84 | void handle_lifecycle_event(MirLifecycleState state) override; | ||
2988 | 85 | void handle_display_config_change(graphics::DisplayConfiguration const& config) override; | ||
2989 | 86 | void send_ping(int32_t serial) override; | ||
2990 | 87 | void send_buffer(BufferStreamId id, graphics::Buffer& buffer, graphics::BufferIpcMsgType type) override; | ||
2991 | 88 | void handle_input_config_change(MirInputConfig const&) override {} | ||
2992 | 89 | void handle_error(ClientVisibleError const&) override {} | ||
2993 | 90 | |||
2994 | 91 | void add_buffer(graphics::Buffer&) override {} | ||
2995 | 92 | void error_buffer(geometry::Size, MirPixelFormat, std::string const& ) override {} | ||
2996 | 93 | void update_buffer(graphics::Buffer&) override {} | ||
2997 | 94 | |||
2998 | 95 | private: | ||
2999 | 96 | std::function<void(MirLifecycleState)> const lifecycle_handler; | ||
3000 | 97 | }; | ||
3001 | 98 | |||
3002 | 99 | namespace | ||
3003 | 100 | { | ||
3004 | 101 | bool get_gl_pixel_format( | ||
3005 | 102 | MirPixelFormat mir_format, | ||
3006 | 103 | GLenum& gl_format, | ||
3007 | 104 | GLenum& gl_type) | ||
3008 | 105 | { | ||
3009 | 106 | #if __BYTE_ORDER == __LITTLE_ENDIAN | ||
3010 | 107 | GLenum const argb = GL_BGRA_EXT; | ||
3011 | 108 | GLenum const abgr = GL_RGBA; | ||
3012 | 109 | #elif __BYTE_ORDER == __BIG_ENDIAN | ||
3013 | 110 | // TODO: Big endian support | ||
3014 | 111 | GLenum const argb = GL_INVALID_ENUM; | ||
3015 | 112 | GLenum const abgr = GL_INVALID_ENUM; | ||
3016 | 113 | //GLenum const rgba = GL_RGBA; | ||
3017 | 114 | //GLenum const bgra = GL_BGRA_EXT; | ||
3018 | 115 | #endif | ||
3019 | 116 | |||
3020 | 117 | static const struct | ||
3021 | 118 | { | ||
3022 | 119 | MirPixelFormat mir_format; | ||
3023 | 120 | GLenum gl_format, gl_type; | ||
3024 | 121 | } mapping[mir_pixel_formats] = | ||
3025 | 122 | { | ||
3026 | 123 | {mir_pixel_format_invalid, GL_INVALID_ENUM, GL_INVALID_ENUM}, | ||
3027 | 124 | {mir_pixel_format_abgr_8888, abgr, GL_UNSIGNED_BYTE}, | ||
3028 | 125 | {mir_pixel_format_xbgr_8888, abgr, GL_UNSIGNED_BYTE}, | ||
3029 | 126 | {mir_pixel_format_argb_8888, argb, GL_UNSIGNED_BYTE}, | ||
3030 | 127 | {mir_pixel_format_xrgb_8888, argb, GL_UNSIGNED_BYTE}, | ||
3031 | 128 | {mir_pixel_format_bgr_888, GL_INVALID_ENUM, GL_INVALID_ENUM}, | ||
3032 | 129 | {mir_pixel_format_rgb_888, GL_RGB, GL_UNSIGNED_BYTE}, | ||
3033 | 130 | {mir_pixel_format_rgb_565, GL_RGB, GL_UNSIGNED_SHORT_5_6_5}, | ||
3034 | 131 | {mir_pixel_format_rgba_5551, GL_RGBA, GL_UNSIGNED_SHORT_5_5_5_1}, | ||
3035 | 132 | {mir_pixel_format_rgba_4444, GL_RGBA, GL_UNSIGNED_SHORT_4_4_4_4}, | ||
3036 | 133 | }; | ||
3037 | 134 | |||
3038 | 135 | if (mir_format > mir_pixel_format_invalid && | ||
3039 | 136 | mir_format < mir_pixel_formats && | ||
3040 | 137 | mapping[mir_format].mir_format == mir_format) // just a sanity check | ||
3041 | 138 | { | ||
3042 | 139 | gl_format = mapping[mir_format].gl_format; | ||
3043 | 140 | gl_type = mapping[mir_format].gl_type; | ||
3044 | 141 | } | ||
3045 | 142 | else | ||
3046 | 143 | { | ||
3047 | 144 | gl_format = GL_INVALID_ENUM; | ||
3048 | 145 | gl_type = GL_INVALID_ENUM; | ||
3049 | 146 | } | ||
3050 | 147 | |||
3051 | 148 | return gl_format != GL_INVALID_ENUM && gl_type != GL_INVALID_ENUM; | ||
3052 | 149 | } | ||
3053 | 150 | |||
3054 | 151 | |||
3055 | 152 | struct ClientPrivate | ||
3056 | 153 | { | ||
3057 | 154 | wl_listener destroy_listener; | ||
3058 | 155 | std::shared_ptr<mf::Session> session; | ||
3059 | 156 | }; | ||
3060 | 157 | |||
3061 | 158 | static_assert( | ||
3062 | 159 | std::is_standard_layout<ClientPrivate>::value, | ||
3063 | 160 | "ClientPrivate must be standard layout for wl_container_of to be defined behaviour"); | ||
3064 | 161 | |||
3065 | 162 | |||
3066 | 163 | ClientPrivate* private_from_listener(wl_listener* listener) | ||
3067 | 164 | { | ||
3068 | 165 | ClientPrivate* userdata; | ||
3069 | 166 | return wl_container_of(listener, userdata, destroy_listener); | ||
3070 | 167 | } | ||
3071 | 168 | |||
3072 | 169 | void cleanup_private(wl_listener* listener, void* /*data*/) | ||
3073 | 170 | { | ||
3074 | 171 | delete private_from_listener(listener); | ||
3075 | 172 | } | ||
3076 | 173 | |||
3077 | 174 | std::shared_ptr<mf::Session> session_for_client(wl_client* client) | ||
3078 | 175 | { | ||
3079 | 176 | auto listener = wl_client_get_destroy_listener(client, &cleanup_private); | ||
3080 | 177 | |||
3081 | 178 | assert(listener && "Client session requested for malformed client"); | ||
3082 | 179 | |||
3083 | 180 | return private_from_listener(listener)->session; | ||
3084 | 181 | } | ||
3085 | 182 | |||
3086 | 183 | struct ClientSessionConstructor | ||
3087 | 184 | { | ||
3088 | 185 | ClientSessionConstructor(std::shared_ptr<mf::Shell> const& shell) | ||
3089 | 186 | : shell{shell} | ||
3090 | 187 | { | ||
3091 | 188 | } | ||
3092 | 189 | |||
3093 | 190 | wl_listener construction_listener; | ||
3094 | 191 | wl_listener destruction_listener; | ||
3095 | 192 | std::shared_ptr<mf::Shell> const shell; | ||
3096 | 193 | }; | ||
3097 | 194 | |||
3098 | 195 | static_assert( | ||
3099 | 196 | std::is_standard_layout<ClientSessionConstructor>::value, | ||
3100 | 197 | "ClientSessionConstructor must be standard layout for wl_container_of to be " | ||
3101 | 198 | "defined behaviour."); | ||
3102 | 199 | |||
3103 | 200 | void create_client_session(wl_listener* listener, void* data) | ||
3104 | 201 | { | ||
3105 | 202 | auto client = reinterpret_cast<wl_client*>(data); | ||
3106 | 203 | |||
3107 | 204 | ClientSessionConstructor* construction_context; | ||
3108 | 205 | construction_context = | ||
3109 | 206 | wl_container_of(listener, construction_context, construction_listener); | ||
3110 | 207 | |||
3111 | 208 | pid_t client_pid; | ||
3112 | 209 | wl_client_get_credentials(client, &client_pid, nullptr, nullptr); | ||
3113 | 210 | |||
3114 | 211 | auto session = construction_context->shell->open_session( | ||
3115 | 212 | client_pid, | ||
3116 | 213 | "", | ||
3117 | 214 | std::make_shared<WaylandEventSink>([](auto){})); | ||
3118 | 215 | |||
3119 | 216 | auto client_context = new ClientPrivate; | ||
3120 | 217 | client_context->destroy_listener.notify = &cleanup_private; | ||
3121 | 218 | client_context->session = session; | ||
3122 | 219 | wl_client_add_destroy_listener(client, &client_context->destroy_listener); | ||
3123 | 220 | } | ||
3124 | 221 | |||
3125 | 222 | void cleanup_client_handler(wl_listener* listener, void*) | ||
3126 | 223 | { | ||
3127 | 224 | ClientSessionConstructor* construction_context; | ||
3128 | 225 | construction_context = wl_container_of(listener, construction_context, destruction_listener); | ||
3129 | 226 | |||
3130 | 227 | delete construction_context; | ||
3131 | 228 | } | ||
3132 | 229 | |||
3133 | 230 | void setup_new_client_handler(wl_display* display, std::shared_ptr<mf::Shell> const& shell) | ||
3134 | 231 | { | ||
3135 | 232 | auto context = new ClientSessionConstructor{shell}; | ||
3136 | 233 | context->construction_listener.notify = &create_client_session; | ||
3137 | 234 | |||
3138 | 235 | wl_display_add_client_created_listener(display, &context->construction_listener); | ||
3139 | 236 | |||
3140 | 237 | context->destruction_listener.notify = &cleanup_client_handler; | ||
3141 | 238 | wl_display_add_destroy_listener(display, &context->destruction_listener); | ||
3142 | 239 | } | ||
3143 | 240 | |||
3144 | 241 | /* | ||
3145 | 242 | std::shared_ptr<mf::BufferStream> create_buffer_stream(mf::Session& session) | ||
3146 | 243 | { | ||
3147 | 244 | mg::BufferProperties const props{ | ||
3148 | 245 | geom::Size{geom::Width{0}, geom::Height{0}}, | ||
3149 | 246 | mir_pixel_format_invalid, | ||
3150 | 247 | mg::BufferUsage::undefined | ||
3151 | 248 | }; | ||
3152 | 249 | |||
3153 | 250 | auto const id = session.create_buffer_stream(props); | ||
3154 | 251 | return session.get_buffer_stream(id); | ||
3155 | 252 | } | ||
3156 | 253 | */ | ||
3157 | 254 | MirPixelFormat wl_format_to_mir_format(uint32_t format) | ||
3158 | 255 | { | ||
3159 | 256 | switch (format) | ||
3160 | 257 | { | ||
3161 | 258 | case WL_SHM_FORMAT_ARGB8888: | ||
3162 | 259 | return mir_pixel_format_argb_8888; | ||
3163 | 260 | case WL_SHM_FORMAT_XRGB8888: | ||
3164 | 261 | return mir_pixel_format_xrgb_8888; | ||
3165 | 262 | case WL_SHM_FORMAT_RGBA4444: | ||
3166 | 263 | return mir_pixel_format_rgba_4444; | ||
3167 | 264 | case WL_SHM_FORMAT_RGBA5551: | ||
3168 | 265 | return mir_pixel_format_rgba_5551; | ||
3169 | 266 | case WL_SHM_FORMAT_RGB565: | ||
3170 | 267 | return mir_pixel_format_rgb_565; | ||
3171 | 268 | case WL_SHM_FORMAT_RGB888: | ||
3172 | 269 | return mir_pixel_format_rgb_888; | ||
3173 | 270 | case WL_SHM_FORMAT_BGR888: | ||
3174 | 271 | return mir_pixel_format_bgr_888; | ||
3175 | 272 | case WL_SHM_FORMAT_XBGR8888: | ||
3176 | 273 | return mir_pixel_format_xbgr_8888; | ||
3177 | 274 | case WL_SHM_FORMAT_ABGR8888: | ||
3178 | 275 | return mir_pixel_format_abgr_8888; | ||
3179 | 276 | default: | ||
3180 | 277 | return mir_pixel_format_invalid; | ||
3181 | 278 | } | ||
3182 | 279 | } | ||
3183 | 280 | |||
3184 | 281 | wl_shm_buffer* shm_buffer_from_resource_checked(wl_resource* resource) | ||
3185 | 282 | { | ||
3186 | 283 | auto const buffer = wl_shm_buffer_get(resource); | ||
3187 | 284 | if (!buffer) | ||
3188 | 285 | { | ||
3189 | 286 | BOOST_THROW_EXCEPTION((std::logic_error{"Tried to create WlShmBuffer from non-shm resource"})); | ||
3190 | 287 | } | ||
3191 | 288 | |||
3192 | 289 | return buffer; | ||
3193 | 290 | } | ||
3194 | 291 | } | ||
3195 | 292 | |||
3196 | 293 | class WlShmBuffer : | ||
3197 | 294 | public mg::BufferBasic, | ||
3198 | 295 | public mg::NativeBufferBase, | ||
3199 | 296 | public mir::renderer::gl::TextureSource, | ||
3200 | 297 | public mir::renderer::software::PixelSource | ||
3201 | 298 | { | ||
3202 | 299 | public: | ||
3203 | 300 | ~WlShmBuffer() | ||
3204 | 301 | { | ||
3205 | 302 | std::lock_guard<std::mutex> lock{*buffer_mutex}; | ||
3206 | 303 | if (buffer) | ||
3207 | 304 | { | ||
3208 | 305 | wl_resource_queue_event(resource, WL_BUFFER_RELEASE); | ||
3209 | 306 | } | ||
3210 | 307 | } | ||
3211 | 308 | |||
3212 | 309 | static std::shared_ptr<graphics::Buffer> mir_buffer_from_wl_buffer( | ||
3213 | 310 | wl_resource* buffer, | ||
3214 | 311 | std::function<void()>&& on_consumed) | ||
3215 | 312 | { | ||
3216 | 313 | std::shared_ptr<WlShmBuffer> mir_buffer; | ||
3217 | 314 | DestructionShim* shim; | ||
3218 | 315 | |||
3219 | 316 | if (auto notifier = wl_resource_get_destroy_listener(buffer, &on_buffer_destroyed)) | ||
3220 | 317 | { | ||
3221 | 318 | // We've already constructed a shim for this buffer, update it. | ||
3222 | 319 | shim = wl_container_of(notifier, shim, destruction_listener); | ||
3223 | 320 | |||
3224 | 321 | if (!(mir_buffer = shim->associated_buffer.lock())) | ||
3225 | 322 | { | ||
3226 | 323 | /* | ||
3227 | 324 | * We've seen this wl_buffer before, but all the WlShmBuffers associated with it | ||
3228 | 325 | * have been destroyed. | ||
3229 | 326 | * | ||
3230 | 327 | * Recreate a new WlShmBuffer to track the new compositor lifetime. | ||
3231 | 328 | */ | ||
3232 | 329 | mir_buffer = std::shared_ptr<WlShmBuffer>{new WlShmBuffer{buffer, std::move(on_consumed)}}; | ||
3233 | 330 | shim->associated_buffer = mir_buffer; | ||
3234 | 331 | } | ||
3235 | 332 | } | ||
3236 | 333 | else | ||
3237 | 334 | { | ||
3238 | 335 | mir_buffer = std::shared_ptr<WlShmBuffer>{new WlShmBuffer{buffer, std::move(on_consumed)}}; | ||
3239 | 336 | shim = new DestructionShim; | ||
3240 | 337 | shim->destruction_listener.notify = &on_buffer_destroyed; | ||
3241 | 338 | shim->associated_buffer = mir_buffer; | ||
3242 | 339 | |||
3243 | 340 | wl_resource_add_destroy_listener(buffer, &shim->destruction_listener); | ||
3244 | 341 | } | ||
3245 | 342 | |||
3246 | 343 | mir_buffer->buffer_mutex = shim->mutex; | ||
3247 | 344 | return mir_buffer; | ||
3248 | 345 | } | ||
3249 | 346 | |||
3250 | 347 | std::shared_ptr<graphics::NativeBuffer> native_buffer_handle() const override | ||
3251 | 348 | { | ||
3252 | 349 | return nullptr; | ||
3253 | 350 | } | ||
3254 | 351 | |||
3255 | 352 | geometry::Size size() const override | ||
3256 | 353 | { | ||
3257 | 354 | return size_; | ||
3258 | 355 | } | ||
3259 | 356 | |||
3260 | 357 | MirPixelFormat pixel_format() const override | ||
3261 | 358 | { | ||
3262 | 359 | return format_; | ||
3263 | 360 | } | ||
3264 | 361 | |||
3265 | 362 | graphics::NativeBufferBase *native_buffer_base() override | ||
3266 | 363 | { | ||
3267 | 364 | return this; | ||
3268 | 365 | } | ||
3269 | 366 | |||
3270 | 367 | void gl_bind_to_texture() override | ||
3271 | 368 | { | ||
3272 | 369 | GLenum format, type; | ||
3273 | 370 | |||
3274 | 371 | if (get_gl_pixel_format( | ||
3275 | 372 | format_, | ||
3276 | 373 | format, | ||
3277 | 374 | type)) | ||
3278 | 375 | { | ||
3279 | 376 | /* | ||
3280 | 377 | * All existing Mir logic assumes that strides are whole multiples of | ||
3281 | 378 | * pixels. And OpenGL defaults to expecting strides are multiples of | ||
3282 | 379 | * 4 bytes. These assumptions used to be compatible when we only had | ||
3283 | 380 | * 4-byte pixels but now we support 2/3-byte pixels we need to be more | ||
3284 | 381 | * careful... | ||
3285 | 382 | */ | ||
3286 | 383 | glPixelStorei(GL_UNPACK_ALIGNMENT, 1); | ||
3287 | 384 | |||
3288 | 385 | read( | ||
3289 | 386 | [this, format, type](unsigned char const* pixels) | ||
3290 | 387 | { | ||
3291 | 388 | auto const size = this->size(); | ||
3292 | 389 | glTexImage2D(GL_TEXTURE_2D, 0, format, | ||
3293 | 390 | size.width.as_int(), size.height.as_int(), | ||
3294 | 391 | 0, format, type, pixels); | ||
3295 | 392 | }); | ||
3296 | 393 | } | ||
3297 | 394 | } | ||
3298 | 395 | |||
3299 | 396 | void bind() override | ||
3300 | 397 | { | ||
3301 | 398 | gl_bind_to_texture(); | ||
3302 | 399 | } | ||
3303 | 400 | |||
3304 | 401 | void secure_for_render() override | ||
3305 | 402 | { | ||
3306 | 403 | } | ||
3307 | 404 | |||
3308 | 405 | void write(unsigned char const *pixels, size_t size) override | ||
3309 | 406 | { | ||
3310 | 407 | std::lock_guard<std::mutex> lock{*buffer_mutex}; | ||
3311 | 408 | wl_shm_buffer_begin_access(buffer); | ||
3312 | 409 | auto data = wl_shm_buffer_get_data(buffer); | ||
3313 | 410 | ::memcpy(data, pixels, size); | ||
3314 | 411 | wl_shm_buffer_end_access(buffer); | ||
3315 | 412 | } | ||
3316 | 413 | |||
3317 | 414 | void read(std::function<void(unsigned char const *)> const &do_with_pixels) override | ||
3318 | 415 | { | ||
3319 | 416 | std::lock_guard<std::mutex> lock{*buffer_mutex}; | ||
3320 | 417 | if (!buffer) | ||
3321 | 418 | { | ||
3322 | 419 | mir::log_warning("Attempt to read from WlShmBuffer after the wl_buffer has been destroyed"); | ||
3323 | 420 | return; | ||
3324 | 421 | } | ||
3325 | 422 | |||
3326 | 423 | if (!consumed) | ||
3327 | 424 | { | ||
3328 | 425 | on_consumed(); | ||
3329 | 426 | consumed = true; | ||
3330 | 427 | } | ||
3331 | 428 | |||
3332 | 429 | wl_shm_buffer_begin_access(buffer); | ||
3333 | 430 | auto data = wl_shm_buffer_get_data(buffer); | ||
3334 | 431 | do_with_pixels(static_cast<unsigned char const*>(data)); | ||
3335 | 432 | wl_shm_buffer_end_access(buffer); | ||
3336 | 433 | } | ||
3337 | 434 | |||
3338 | 435 | geometry::Stride stride() const override | ||
3339 | 436 | { | ||
3340 | 437 | return stride_; | ||
3341 | 438 | } | ||
3342 | 439 | |||
3343 | 440 | private: | ||
3344 | 441 | WlShmBuffer( | ||
3345 | 442 | wl_resource* buffer, | ||
3346 | 443 | std::function<void()>&& on_consumed) | ||
3347 | 444 | : buffer{shm_buffer_from_resource_checked(buffer)}, | ||
3348 | 445 | resource{buffer}, | ||
3349 | 446 | size_{wl_shm_buffer_get_width(this->buffer), wl_shm_buffer_get_height(this->buffer)}, | ||
3350 | 447 | stride_{wl_shm_buffer_get_stride(this->buffer)}, | ||
3351 | 448 | format_{wl_format_to_mir_format(wl_shm_buffer_get_format(this->buffer))}, | ||
3352 | 449 | consumed{false}, | ||
3353 | 450 | on_consumed{std::move(on_consumed)} | ||
3354 | 451 | { | ||
3355 | 452 | } | ||
3356 | 453 | |||
3357 | 454 | static void on_buffer_destroyed(wl_listener* listener, void*) | ||
3358 | 455 | { | ||
3359 | 456 | static_assert( | ||
3360 | 457 | std::is_standard_layout<DestructionShim>::value, | ||
3361 | 458 | "DestructionShim must be Standard Layout for wl_container_of to be defined behaviour"); | ||
3362 | 459 | |||
3363 | 460 | DestructionShim* shim; | ||
3364 | 461 | shim = wl_container_of(listener, shim, destruction_listener); | ||
3365 | 462 | |||
3366 | 463 | { | ||
3367 | 464 | std::lock_guard<std::mutex> lock{*shim->mutex}; | ||
3368 | 465 | if (auto mir_buffer = shim->associated_buffer.lock()) | ||
3369 | 466 | { | ||
3370 | 467 | mir_buffer->buffer = nullptr; | ||
3371 | 468 | } | ||
3372 | 469 | } | ||
3373 | 470 | |||
3374 | 471 | delete shim; | ||
3375 | 472 | } | ||
3376 | 473 | |||
3377 | 474 | struct DestructionShim | ||
3378 | 475 | { | ||
3379 | 476 | std::shared_ptr<std::mutex> const mutex = std::make_shared<std::mutex>(); | ||
3380 | 477 | std::weak_ptr<WlShmBuffer> associated_buffer; | ||
3381 | 478 | wl_listener destruction_listener; | ||
3382 | 479 | }; | ||
3383 | 480 | |||
3384 | 481 | std::shared_ptr<std::mutex> buffer_mutex; | ||
3385 | 482 | |||
3386 | 483 | wl_shm_buffer* buffer; | ||
3387 | 484 | wl_resource* const resource; | ||
3388 | 485 | |||
3389 | 486 | geom::Size const size_; | ||
3390 | 487 | geom::Stride const stride_; | ||
3391 | 488 | MirPixelFormat const format_; | ||
3392 | 489 | |||
3393 | 490 | bool consumed; | ||
3394 | 491 | std::function<void()> on_consumed; | ||
3395 | 492 | }; | ||
3396 | 493 | |||
3397 | 494 | class WlSurface : public wayland::Surface | ||
3398 | 495 | { | ||
3399 | 496 | public: | ||
3400 | 497 | WlSurface( | ||
3401 | 498 | wl_client* client, | ||
3402 | 499 | wl_resource* parent, | ||
3403 | 500 | uint32_t id, | ||
3404 | 501 | std::shared_ptr<mir::Executor> const& executor, | ||
3405 | 502 | std::shared_ptr<mg::WaylandAllocator> const& allocator) | ||
3406 | 503 | : Surface(client, parent, id), | ||
3407 | 504 | allocator{allocator}, | ||
3408 | 505 | executor{executor}, | ||
3409 | 506 | pending_buffer{nullptr}, | ||
3410 | 507 | pending_frames{std::make_shared<std::vector<wl_resource*>>()} | ||
3411 | 508 | { | ||
3412 | 509 | auto session = session_for_client(client); | ||
3413 | 510 | mg::BufferProperties const props{ | ||
3414 | 511 | geom::Size{geom::Width{0}, geom::Height{0}}, | ||
3415 | 512 | mir_pixel_format_invalid, | ||
3416 | 513 | mg::BufferUsage::undefined | ||
3417 | 514 | }; | ||
3418 | 515 | |||
3419 | 516 | stream_id = session->create_buffer_stream(props); | ||
3420 | 517 | stream = session->get_buffer_stream(stream_id); | ||
3421 | 518 | |||
3422 | 519 | // wl_surface is specified to act in mailbox mode | ||
3423 | 520 | stream->allow_framedropping(true); | ||
3424 | 521 | } | ||
3425 | 522 | |||
3426 | 523 | void set_resize_handler(std::function<void(geom::Size)> const& handler) | ||
3427 | 524 | { | ||
3428 | 525 | resize_handler = handler; | ||
3429 | 526 | } | ||
3430 | 527 | |||
3431 | 528 | void set_hide_handler(std::function<void()> const& handler) | ||
3432 | 529 | { | ||
3433 | 530 | hide_handler = handler; | ||
3434 | 531 | } | ||
3435 | 532 | |||
3436 | 533 | mf::BufferStreamId stream_id; | ||
3437 | 534 | std::shared_ptr<mf::BufferStream> stream; | ||
3438 | 535 | private: | ||
3439 | 536 | std::shared_ptr<mg::WaylandAllocator> const allocator; | ||
3440 | 537 | std::shared_ptr<mir::Executor> const executor; | ||
3441 | 538 | |||
3442 | 539 | std::function<void(geom::Size)> resize_handler; | ||
3443 | 540 | std::function<void()> hide_handler; | ||
3444 | 541 | |||
3445 | 542 | wl_resource* pending_buffer; | ||
3446 | 543 | std::shared_ptr<std::vector<wl_resource*>> const pending_frames; | ||
3447 | 544 | |||
3448 | 545 | void destroy(); | ||
3449 | 546 | void attach(std::experimental::optional<wl_resource*> const& buffer, int32_t x, int32_t y); | ||
3450 | 547 | void damage(int32_t x, int32_t y, int32_t width, int32_t height); | ||
3451 | 548 | void frame(uint32_t callback); | ||
3452 | 549 | void set_opaque_region(std::experimental::optional<wl_resource*> const& region); | ||
3453 | 550 | void set_input_region(std::experimental::optional<wl_resource*> const& region); | ||
3454 | 551 | void commit(); | ||
3455 | 552 | void damage_buffer(int32_t x, int32_t y, int32_t width, int32_t height); | ||
3456 | 553 | void set_buffer_transform(int32_t transform); | ||
3457 | 554 | void set_buffer_scale(int32_t scale); | ||
3458 | 555 | }; | ||
3459 | 556 | |||
3460 | 557 | void WlSurface::destroy() | ||
3461 | 558 | { | ||
3462 | 559 | delete this; | ||
3463 | 560 | } | ||
3464 | 561 | |||
3465 | 562 | void WlSurface::attach(std::experimental::optional<wl_resource*> const& buffer, int32_t x, int32_t y) | ||
3466 | 563 | { | ||
3467 | 564 | if (x != 0 || y != 0) | ||
3468 | 565 | { | ||
3469 | 566 | mir::log_warning("Client requested unimplemented non-zero attach offset. Rendering will be incorrect."); | ||
3470 | 567 | } | ||
3471 | 568 | |||
3472 | 569 | if(!buffer && hide_handler) | ||
3473 | 570 | { | ||
3474 | 571 | hide_handler(); | ||
3475 | 572 | } | ||
3476 | 573 | |||
3477 | 574 | pending_buffer = *buffer; | ||
3478 | 575 | } | ||
3479 | 576 | |||
3480 | 577 | void WlSurface::damage(int32_t x, int32_t y, int32_t width, int32_t height) | ||
3481 | 578 | { | ||
3482 | 579 | (void)x; | ||
3483 | 580 | (void)y; | ||
3484 | 581 | (void)width; | ||
3485 | 582 | (void)height; | ||
3486 | 583 | } | ||
3487 | 584 | |||
3488 | 585 | void WlSurface::damage_buffer(int32_t x, int32_t y, int32_t width, int32_t height) | ||
3489 | 586 | { | ||
3490 | 587 | (void)x; | ||
3491 | 588 | (void)y; | ||
3492 | 589 | (void)width; | ||
3493 | 590 | (void)height; | ||
3494 | 591 | } | ||
3495 | 592 | |||
3496 | 593 | void WlSurface::frame(uint32_t callback) | ||
3497 | 594 | { | ||
3498 | 595 | pending_frames->emplace_back( | ||
3499 | 596 | wl_resource_create(client, &wl_callback_interface, 1, callback)); | ||
3500 | 597 | } | ||
3501 | 598 | |||
3502 | 599 | void WlSurface::set_opaque_region(const std::experimental::optional<wl_resource*>& region) | ||
3503 | 600 | { | ||
3504 | 601 | (void)region; | ||
3505 | 602 | } | ||
3506 | 603 | |||
3507 | 604 | void WlSurface::set_input_region(const std::experimental::optional<wl_resource*>& region) | ||
3508 | 605 | { | ||
3509 | 606 | (void)region; | ||
3510 | 607 | } | ||
3511 | 608 | |||
3512 | 609 | void WlSurface::commit() | ||
3513 | 610 | { | ||
3514 | 611 | if (pending_buffer) | ||
3515 | 612 | { | ||
3516 | 613 | std::shared_ptr<mg::Buffer> mir_buffer; | ||
3517 | 614 | auto shm_buffer = wl_shm_buffer_get(pending_buffer); | ||
3518 | 615 | auto send_frame_notifications = | ||
3519 | 616 | [executor = executor, frames = pending_frames]() | ||
3520 | 617 | { | ||
3521 | 618 | executor->spawn( | ||
3522 | 619 | [frames]() | ||
3523 | 620 | { | ||
3524 | 621 | /* | ||
3525 | 622 | * There is no synchronisation required here - | ||
3526 | 623 | * This is run on the WaylandExecutor, and is guaranteed to run on the | ||
3527 | 624 | * wl_event_loop's thread. | ||
3528 | 625 | * | ||
3529 | 626 | * The only other accessors of WlSurface are also on the wl_event_loop, | ||
3530 | 627 | * so this is guaranteed not to be reentrant. | ||
3531 | 628 | */ | ||
3532 | 629 | for (auto frame : *frames) | ||
3533 | 630 | { | ||
3534 | 631 | wl_callback_send_done(frame, 0); | ||
3535 | 632 | wl_resource_destroy(frame); | ||
3536 | 633 | } | ||
3537 | 634 | frames->clear(); | ||
3538 | 635 | }); | ||
3539 | 636 | }; | ||
3540 | 637 | |||
3541 | 638 | if (shm_buffer) | ||
3542 | 639 | { | ||
3543 | 640 | mir_buffer = WlShmBuffer::mir_buffer_from_wl_buffer( | ||
3544 | 641 | pending_buffer, | ||
3545 | 642 | std::move(send_frame_notifications)); | ||
3546 | 643 | } | ||
3547 | 644 | else if ( | ||
3548 | 645 | allocator && | ||
3549 | 646 | (mir_buffer = allocator->buffer_from_resource( | ||
3550 | 647 | pending_buffer, | ||
3551 | 648 | std::move(send_frame_notifications)))) | ||
3552 | 649 | { | ||
3553 | 650 | } | ||
3554 | 651 | else | ||
3555 | 652 | { | ||
3556 | 653 | BOOST_THROW_EXCEPTION((std::runtime_error{"Received unhandled buffer type"})); | ||
3557 | 654 | } | ||
3558 | 655 | |||
3559 | 656 | /* | ||
3560 | 657 | * This is technically incorrect - the resize and submit_buffer *should* be atomic, | ||
3561 | 658 | * but are not, so a client in the process of resizing can have buffers rendered at | ||
3562 | 659 | * an unexpected size. | ||
3563 | 660 | * | ||
3564 | 661 | * It should be good enough for now, though. | ||
3565 | 662 | * | ||
3566 | 663 | * TODO: Provide a mg::Buffer::logical_size() to do this properly. | ||
3567 | 664 | */ | ||
3568 | 665 | stream->resize(mir_buffer->size()); | ||
3569 | 666 | if (resize_handler) | ||
3570 | 667 | { | ||
3571 | 668 | resize_handler(mir_buffer->size()); | ||
3572 | 669 | } | ||
3573 | 670 | stream->submit_buffer(mir_buffer); | ||
3574 | 671 | |||
3575 | 672 | pending_buffer = nullptr; | ||
3576 | 673 | } | ||
3577 | 674 | } | ||
3578 | 675 | |||
3579 | 676 | void WlSurface::set_buffer_transform(int32_t transform) | ||
3580 | 677 | { | ||
3581 | 678 | (void)transform; | ||
3582 | 679 | } | ||
3583 | 680 | |||
3584 | 681 | void WlSurface::set_buffer_scale(int32_t scale) | ||
3585 | 682 | { | ||
3586 | 683 | (void)scale; | ||
3587 | 684 | } | ||
3588 | 685 | |||
3589 | 686 | class WlCompositor : public wayland::Compositor | ||
3590 | 687 | { | ||
3591 | 688 | public: | ||
3592 | 689 | WlCompositor( | ||
3593 | 690 | struct wl_display* display, | ||
3594 | 691 | std::shared_ptr<mir::Executor> const& executor, | ||
3595 | 692 | std::shared_ptr<mg::WaylandAllocator> const& allocator) | ||
3596 | 693 | : Compositor(display, 3), | ||
3597 | 694 | allocator{allocator}, | ||
3598 | 695 | executor{executor} | ||
3599 | 696 | { | ||
3600 | 697 | } | ||
3601 | 698 | |||
3602 | 699 | private: | ||
3603 | 700 | std::shared_ptr<mg::WaylandAllocator> const allocator; | ||
3604 | 701 | std::shared_ptr<mir::Executor> const executor; | ||
3605 | 702 | |||
3606 | 703 | void create_surface(wl_client* client, wl_resource* resource, uint32_t id) override; | ||
3607 | 704 | void create_region(wl_client* client, wl_resource* resource, uint32_t id) override; | ||
3608 | 705 | }; | ||
3609 | 706 | |||
3610 | 707 | void WlCompositor::create_surface(wl_client* client, wl_resource* resource, uint32_t id) | ||
3611 | 708 | { | ||
3612 | 709 | new WlSurface{client, resource, id, executor, allocator}; | ||
3613 | 710 | } | ||
3614 | 711 | |||
3615 | 712 | class Region : public wayland::Region | ||
3616 | 713 | { | ||
3617 | 714 | public: | ||
3618 | 715 | Region(wl_client* client, wl_resource* parent, uint32_t id) | ||
3619 | 716 | : wayland::Region(client, parent, id) | ||
3620 | 717 | { | ||
3621 | 718 | } | ||
3622 | 719 | protected: | ||
3623 | 720 | |||
3624 | 721 | void destroy() override | ||
3625 | 722 | { | ||
3626 | 723 | } | ||
3627 | 724 | void add(int32_t /*x*/, int32_t /*y*/, int32_t /*width*/, int32_t /*height*/) override | ||
3628 | 725 | { | ||
3629 | 726 | } | ||
3630 | 727 | void subtract(int32_t /*x*/, int32_t /*y*/, int32_t /*width*/, int32_t /*height*/) override | ||
3631 | 728 | { | ||
3632 | 729 | } | ||
3633 | 730 | |||
3634 | 731 | }; | ||
3635 | 732 | |||
3636 | 733 | void WlCompositor::create_region(wl_client* client, wl_resource* resource, uint32_t id) | ||
3637 | 734 | { | ||
3638 | 735 | new Region{client, resource, id}; | ||
3639 | 736 | } | ||
3640 | 737 | |||
3641 | 738 | class WlPointer; | ||
3642 | 739 | class WlTouch; | ||
3643 | 740 | |||
3644 | 741 | class WlKeyboard : public wayland::Keyboard | ||
3645 | 742 | { | ||
3646 | 743 | public: | ||
3647 | 744 | WlKeyboard( | ||
3648 | 745 | wl_client* client, | ||
3649 | 746 | wl_resource* parent, | ||
3650 | 747 | uint32_t id, | ||
3651 | 748 | std::shared_ptr<mir::Executor> const& executor) | ||
3652 | 749 | : Keyboard(client, parent, id), | ||
3653 | 750 | executor{executor} | ||
3654 | 751 | { | ||
3655 | 752 | } | ||
3656 | 753 | |||
3657 | 754 | void handle_event(MirInputEvent const* event, wl_resource* /*target*/) | ||
3658 | 755 | { | ||
3659 | 756 | executor->spawn( | ||
3660 | 757 | [ev = mir_event_ref(mir_input_event_get_event(event)), this] () | ||
3661 | 758 | { | ||
3662 | 759 | int const serial = wl_display_next_serial(wl_client_get_display(client)); | ||
3663 | 760 | auto event = mir_event_get_input_event(ev); | ||
3664 | 761 | auto key_event = mir_input_event_get_keyboard_event(event); | ||
3665 | 762 | |||
3666 | 763 | switch (mir_keyboard_event_action(key_event)) | ||
3667 | 764 | { | ||
3668 | 765 | case mir_keyboard_action_up: | ||
3669 | 766 | wl_keyboard_send_key(resource, | ||
3670 | 767 | serial, | ||
3671 | 768 | mir_input_event_get_event_time(event) / 1000, | ||
3672 | 769 | mir_keyboard_event_scan_code(key_event), | ||
3673 | 770 | WL_KEYBOARD_KEY_STATE_RELEASED); | ||
3674 | 771 | break; | ||
3675 | 772 | case mir_keyboard_action_down: | ||
3676 | 773 | wl_keyboard_send_key(resource, | ||
3677 | 774 | serial, | ||
3678 | 775 | mir_input_event_get_event_time(event) / 1000, | ||
3679 | 776 | mir_keyboard_event_scan_code(key_event), | ||
3680 | 777 | WL_KEYBOARD_KEY_STATE_PRESSED); | ||
3681 | 778 | break; | ||
3682 | 779 | default: | ||
3683 | 780 | break; | ||
3684 | 781 | } | ||
3685 | 782 | mir_event_unref(ev); | ||
3686 | 783 | }); | ||
3687 | 784 | } | ||
3688 | 785 | |||
3689 | 786 | private: | ||
3690 | 787 | std::shared_ptr<mir::Executor> const executor; | ||
3691 | 788 | |||
3692 | 789 | void release() override; | ||
3693 | 790 | }; | ||
3694 | 791 | |||
3695 | 792 | void WlKeyboard::release() | ||
3696 | 793 | { | ||
3697 | 794 | } | ||
3698 | 795 | |||
3699 | 796 | namespace | ||
3700 | 797 | { | ||
3701 | 798 | uint32_t calc_button_difference(MirPointerButtons old, MirPointerButtons updated) | ||
3702 | 799 | { | ||
3703 | 800 | switch (old ^ updated) | ||
3704 | 801 | { | ||
3705 | 802 | case mir_pointer_button_primary: | ||
3706 | 803 | return 272; // No, I have *no* idea why GTK expects 271 to be the primary button. | ||
3707 | 804 | case mir_pointer_button_secondary: | ||
3708 | 805 | return 274; | ||
3709 | 806 | case mir_pointer_button_tertiary: | ||
3710 | 807 | return 273; | ||
3711 | 808 | case mir_pointer_button_back: | ||
3712 | 809 | return 275; // I dunno. It's a number, I guess. | ||
3713 | 810 | case mir_pointer_button_forward: | ||
3714 | 811 | return 276; // I dunno. It's a number, I guess. | ||
3715 | 812 | default: | ||
3716 | 813 | throw std::logic_error("Whoops, I misunderstand how Mir pointer events work"); | ||
3717 | 814 | } | ||
3718 | 815 | } | ||
3719 | 816 | } | ||
3720 | 817 | |||
3721 | 818 | class WlPointer : public wayland::Pointer | ||
3722 | 819 | { | ||
3723 | 820 | public: | ||
3724 | 821 | |||
3725 | 822 | WlPointer( | ||
3726 | 823 | wl_client* client, | ||
3727 | 824 | wl_resource* parent, | ||
3728 | 825 | uint32_t id, | ||
3729 | 826 | std::shared_ptr<mir::Executor> const& executor) | ||
3730 | 827 | : Pointer(client, parent, id), | ||
3731 | 828 | display{wl_client_get_display(client)}, | ||
3732 | 829 | executor{executor} | ||
3733 | 830 | { | ||
3734 | 831 | } | ||
3735 | 832 | |||
3736 | 833 | void handle_event(MirInputEvent const* event, wl_resource* target) | ||
3737 | 834 | { | ||
3738 | 835 | executor->spawn( | ||
3739 | 836 | [ev = mir_event_ref(mir_input_event_get_event(event)), target, this]() | ||
3740 | 837 | { | ||
3741 | 838 | auto const serial = wl_display_next_serial(display); | ||
3742 | 839 | auto const event = mir_event_get_input_event(ev); | ||
3743 | 840 | auto const pointer_event = mir_input_event_get_pointer_event(event); | ||
3744 | 841 | |||
3745 | 842 | switch(mir_pointer_event_action(pointer_event)) | ||
3746 | 843 | { | ||
3747 | 844 | case mir_pointer_action_button_down: | ||
3748 | 845 | { | ||
3749 | 846 | auto button = calc_button_difference(last_set, mir_pointer_event_buttons(pointer_event)); | ||
3750 | 847 | wl_pointer_send_button( | ||
3751 | 848 | resource, | ||
3752 | 849 | serial, | ||
3753 | 850 | mir_input_event_get_event_time(event) / 1000, | ||
3754 | 851 | button, | ||
3755 | 852 | WL_POINTER_BUTTON_STATE_PRESSED); | ||
3756 | 853 | last_set = mir_pointer_event_buttons(pointer_event); | ||
3757 | 854 | break; | ||
3758 | 855 | } | ||
3759 | 856 | case mir_pointer_action_button_up: | ||
3760 | 857 | { | ||
3761 | 858 | auto button = calc_button_difference(last_set, mir_pointer_event_buttons(pointer_event)); | ||
3762 | 859 | wl_pointer_send_button( | ||
3763 | 860 | resource, | ||
3764 | 861 | serial, | ||
3765 | 862 | mir_input_event_get_event_time(event) / 1000, | ||
3766 | 863 | button, | ||
3767 | 864 | WL_POINTER_BUTTON_STATE_RELEASED); | ||
3768 | 865 | last_set = mir_pointer_event_buttons(pointer_event); | ||
3769 | 866 | break; | ||
3770 | 867 | } | ||
3771 | 868 | case mir_pointer_action_enter: | ||
3772 | 869 | { | ||
3773 | 870 | wl_pointer_send_enter( | ||
3774 | 871 | resource, | ||
3775 | 872 | serial, | ||
3776 | 873 | target, | ||
3777 | 874 | wl_fixed_from_double(mir_pointer_event_axis_value(pointer_event, mir_pointer_axis_x)), | ||
3778 | 875 | wl_fixed_from_double(mir_pointer_event_axis_value(pointer_event, mir_pointer_axis_y))); | ||
3779 | 876 | break; | ||
3780 | 877 | } | ||
3781 | 878 | case mir_pointer_action_leave: | ||
3782 | 879 | { | ||
3783 | 880 | wl_pointer_send_leave( | ||
3784 | 881 | resource, | ||
3785 | 882 | serial, | ||
3786 | 883 | target); | ||
3787 | 884 | break; | ||
3788 | 885 | } | ||
3789 | 886 | case mir_pointer_action_motion: | ||
3790 | 887 | { | ||
3791 | 888 | auto x = mir_pointer_event_axis_value(pointer_event, mir_pointer_axis_x); | ||
3792 | 889 | auto y = mir_pointer_event_axis_value(pointer_event, mir_pointer_axis_y); | ||
3793 | 890 | auto vscroll = mir_pointer_event_axis_value(pointer_event, mir_pointer_axis_vscroll); | ||
3794 | 891 | auto hscroll = mir_pointer_event_axis_value(pointer_event, mir_pointer_axis_hscroll); | ||
3795 | 892 | |||
3796 | 893 | if ((x != last_x) || (y != last_y)) | ||
3797 | 894 | { | ||
3798 | 895 | wl_pointer_send_motion( | ||
3799 | 896 | resource, | ||
3800 | 897 | mir_input_event_get_event_time(event) / 1000, | ||
3801 | 898 | wl_fixed_from_double(x), | ||
3802 | 899 | wl_fixed_from_double(y)); | ||
3803 | 900 | |||
3804 | 901 | last_x = x; | ||
3805 | 902 | last_y = y; | ||
3806 | 903 | } | ||
3807 | 904 | if (vscroll != last_vscroll) | ||
3808 | 905 | { | ||
3809 | 906 | wl_pointer_send_axis( | ||
3810 | 907 | resource, | ||
3811 | 908 | mir_input_event_get_event_time(event) / 1000, | ||
3812 | 909 | WL_POINTER_AXIS_VERTICAL_SCROLL, | ||
3813 | 910 | wl_fixed_from_double(vscroll)); | ||
3814 | 911 | last_vscroll = vscroll; | ||
3815 | 912 | } | ||
3816 | 913 | if (hscroll != last_hscroll) | ||
3817 | 914 | { | ||
3818 | 915 | wl_pointer_send_axis( | ||
3819 | 916 | resource, | ||
3820 | 917 | mir_input_event_get_event_time(event) / 1000, | ||
3821 | 918 | WL_POINTER_AXIS_HORIZONTAL_SCROLL, | ||
3822 | 919 | wl_fixed_from_double(hscroll)); | ||
3823 | 920 | last_hscroll = hscroll; | ||
3824 | 921 | } | ||
3825 | 922 | break; | ||
3826 | 923 | } | ||
3827 | 924 | case mir_pointer_actions: | ||
3828 | 925 | break; | ||
3829 | 926 | } | ||
3830 | 927 | }); | ||
3831 | 928 | } | ||
3832 | 929 | |||
3833 | 930 | // Pointer interface | ||
3834 | 931 | private: | ||
3835 | 932 | wl_display* const display; | ||
3836 | 933 | std::shared_ptr<mir::Executor> const executor; | ||
3837 | 934 | |||
3838 | 935 | MirPointerButtons last_set{0}; | ||
3839 | 936 | float last_x, last_y, last_vscroll, last_hscroll; | ||
3840 | 937 | |||
3841 | 938 | void set_cursor(uint32_t serial, std::experimental::optional<wl_resource*> const& surface, int32_t hotspot_x, int32_t hotspot_y) override; | ||
3842 | 939 | void release() override; | ||
3843 | 940 | }; | ||
3844 | 941 | |||
3845 | 942 | void WlPointer::set_cursor(uint32_t serial, std::experimental::optional<wl_resource*> const& surface, int32_t hotspot_x, int32_t hotspot_y) | ||
3846 | 943 | { | ||
3847 | 944 | (void)serial; | ||
3848 | 945 | (void)surface; | ||
3849 | 946 | (void)hotspot_x; | ||
3850 | 947 | (void)hotspot_y; | ||
3851 | 948 | } | ||
3852 | 949 | |||
3853 | 950 | void WlPointer::release() | ||
3854 | 951 | { | ||
3855 | 952 | delete this; | ||
3856 | 953 | } | ||
3857 | 954 | |||
3858 | 955 | class WlTouch : public wayland::Touch | ||
3859 | 956 | { | ||
3860 | 957 | public: | ||
3861 | 958 | WlTouch( | ||
3862 | 959 | wl_client* client, | ||
3863 | 960 | wl_resource* parent, | ||
3864 | 961 | uint32_t id, | ||
3865 | 962 | std::shared_ptr<mir::Executor> const& /*executor*/) | ||
3866 | 963 | : Touch(client, parent, id) | ||
3867 | 964 | { | ||
3868 | 965 | } | ||
3869 | 966 | |||
3870 | 967 | void handle_event(MirInputEvent const* /*event*/, wl_resource* /*target*/) | ||
3871 | 968 | { | ||
3872 | 969 | } | ||
3873 | 970 | |||
3874 | 971 | // Touch interface | ||
3875 | 972 | private: | ||
3876 | 973 | void release() override; | ||
3877 | 974 | }; | ||
3878 | 975 | |||
3879 | 976 | void WlTouch::release() | ||
3880 | 977 | { | ||
3881 | 978 | } | ||
3882 | 979 | |||
3883 | 980 | template<class InputInterface> | ||
3884 | 981 | class InputCtx | ||
3885 | 982 | { | ||
3886 | 983 | public: | ||
3887 | 984 | InputCtx() = default; | ||
3888 | 985 | |||
3889 | 986 | InputCtx(InputCtx&&) = delete; | ||
3890 | 987 | InputCtx(InputCtx const&) = delete; | ||
3891 | 988 | InputCtx& operator=(InputCtx const&) = delete; | ||
3892 | 989 | |||
3893 | 990 | void register_listener(std::shared_ptr<InputInterface> const& listener) | ||
3894 | 991 | { | ||
3895 | 992 | listeners.push_back(listener); | ||
3896 | 993 | } | ||
3897 | 994 | |||
3898 | 995 | void unregister_listener(InputInterface const* listener) | ||
3899 | 996 | { | ||
3900 | 997 | std::remove_if( | ||
3901 | 998 | listeners.begin(), | ||
3902 | 999 | listeners.end(), | ||
3903 | 1000 | [listener](auto candidate) | ||
3904 | 1001 | { | ||
3905 | 1002 | return candidate.get() == listener; | ||
3906 | 1003 | }); | ||
3907 | 1004 | } | ||
3908 | 1005 | |||
3909 | 1006 | void handle_event(MirInputEvent const* event, wl_resource* target) const | ||
3910 | 1007 | { | ||
3911 | 1008 | for (auto& listener : listeners) | ||
3912 | 1009 | { | ||
3913 | 1010 | listener->handle_event(event, target); | ||
3914 | 1011 | } | ||
3915 | 1012 | } | ||
3916 | 1013 | |||
3917 | 1014 | private: | ||
3918 | 1015 | std::vector<std::shared_ptr<InputInterface>> listeners; | ||
3919 | 1016 | }; | ||
3920 | 1017 | |||
3921 | 1018 | class WlSeat | ||
3922 | 1019 | { | ||
3923 | 1020 | public: | ||
3924 | 1021 | WlSeat(wl_display* display, std::shared_ptr<mir::Executor> const& executor) | ||
3925 | 1022 | : executor{executor}, | ||
3926 | 1023 | global{wl_global_create( | ||
3927 | 1024 | display, | ||
3928 | 1025 | &wl_seat_interface, | ||
3929 | 1026 | 5, | ||
3930 | 1027 | this, | ||
3931 | 1028 | &WlSeat::bind)} | ||
3932 | 1029 | { | ||
3933 | 1030 | if (!global) | ||
3934 | 1031 | { | ||
3935 | 1032 | BOOST_THROW_EXCEPTION(std::runtime_error("Failed to export wl_seat interface")); | ||
3936 | 1033 | } | ||
3937 | 1034 | } | ||
3938 | 1035 | |||
3939 | 1036 | ~WlSeat() | ||
3940 | 1037 | { | ||
3941 | 1038 | wl_global_destroy(global); | ||
3942 | 1039 | } | ||
3943 | 1040 | |||
3944 | 1041 | InputCtx<WlPointer> const& acquire_pointer_reference(wl_client* client) const; | ||
3945 | 1042 | InputCtx<WlKeyboard> const& acquire_keyboard_reference(wl_client* client) const; | ||
3946 | 1043 | InputCtx<WlTouch> const& acquire_touch_reference(wl_client* client) const; | ||
3947 | 1044 | |||
3948 | 1045 | private: | ||
3949 | 1046 | std::unordered_map<wl_client*, InputCtx<WlPointer>> mutable pointer; | ||
3950 | 1047 | std::unordered_map<wl_client*, InputCtx<WlKeyboard>> mutable keyboard; | ||
3951 | 1048 | std::unordered_map<wl_client*, InputCtx<WlTouch>> mutable touch; | ||
3952 | 1049 | std::shared_ptr<mir::Executor> const executor; | ||
3953 | 1050 | |||
3954 | 1051 | static void bind(struct wl_client* client, void* data, uint32_t version, uint32_t id) | ||
3955 | 1052 | { | ||
3956 | 1053 | auto me = reinterpret_cast<WlSeat*>(data); | ||
3957 | 1054 | auto resource = wl_resource_create(client, &wl_seat_interface, | ||
3958 | 1055 | std::min(version, 6u), id); | ||
3959 | 1056 | if (resource == nullptr) | ||
3960 | 1057 | { | ||
3961 | 1058 | wl_client_post_no_memory(client); | ||
3962 | 1059 | BOOST_THROW_EXCEPTION((std::bad_alloc{})); | ||
3963 | 1060 | } | ||
3964 | 1061 | wl_resource_set_implementation(resource, &vtable, me, nullptr); | ||
3965 | 1062 | |||
3966 | 1063 | /* | ||
3967 | 1064 | * TODO: Read the actual capabilities. Do we have a keyboard? Mouse? Touch? | ||
3968 | 1065 | */ | ||
3969 | 1066 | wl_seat_send_capabilities( | ||
3970 | 1067 | resource, | ||
3971 | 1068 | WL_SEAT_CAPABILITY_POINTER | | ||
3972 | 1069 | WL_SEAT_CAPABILITY_KEYBOARD); | ||
3973 | 1070 | wl_seat_send_name( | ||
3974 | 1071 | resource, | ||
3975 | 1072 | "seat0"); | ||
3976 | 1073 | |||
3977 | 1074 | wl_resource_set_user_data(resource, me); | ||
3978 | 1075 | } | ||
3979 | 1076 | |||
3980 | 1077 | static void get_pointer(wl_client* client, wl_resource* resource, uint32_t id) | ||
3981 | 1078 | { | ||
3982 | 1079 | auto me = reinterpret_cast<WlSeat*>(wl_resource_get_user_data(resource)); | ||
3983 | 1080 | me->pointer[client].register_listener( | ||
3984 | 1081 | std::make_shared<WlPointer>( | ||
3985 | 1082 | client, | ||
3986 | 1083 | resource, | ||
3987 | 1084 | id, | ||
3988 | 1085 | me->executor)); | ||
3989 | 1086 | } | ||
3990 | 1087 | static void get_keyboard(wl_client* client, wl_resource* resource, uint32_t id) | ||
3991 | 1088 | { | ||
3992 | 1089 | auto me = reinterpret_cast<WlSeat*>(wl_resource_get_user_data(resource)); | ||
3993 | 1090 | me->keyboard[client].register_listener( | ||
3994 | 1091 | std::make_shared<WlKeyboard>( | ||
3995 | 1092 | client, | ||
3996 | 1093 | resource, | ||
3997 | 1094 | id, | ||
3998 | 1095 | me->executor)); | ||
3999 | 1096 | } | ||
4000 | 1097 | static void get_touch(wl_client* client, wl_resource* resource, uint32_t id) | ||
4001 | 1098 | { | ||
4002 | 1099 | auto me = reinterpret_cast<WlSeat*>(wl_resource_get_user_data(resource)); | ||
4003 | 1100 | me->touch[client].register_listener( | ||
4004 | 1101 | std::make_shared<WlTouch>( | ||
4005 | 1102 | client, | ||
4006 | 1103 | resource, | ||
4007 | 1104 | id, | ||
4008 | 1105 | me->executor)); | ||
4009 | 1106 | } | ||
4010 | 1107 | static void release(struct wl_client* /*client*/, struct wl_resource* /*resource*/) {} | ||
4011 | 1108 | |||
4012 | 1109 | |||
4013 | 1110 | wl_global* const global; | ||
4014 | 1111 | static struct wl_seat_interface const vtable; | ||
4015 | 1112 | }; | ||
4016 | 1113 | |||
4017 | 1114 | struct wl_seat_interface const WlSeat::vtable = { | ||
4018 | 1115 | WlSeat::get_pointer, | ||
4019 | 1116 | WlSeat::get_keyboard, | ||
4020 | 1117 | WlSeat::get_touch, | ||
4021 | 1118 | WlSeat::release | ||
4022 | 1119 | }; | ||
4023 | 1120 | |||
4024 | 1121 | InputCtx<WlKeyboard> const& WlSeat::acquire_keyboard_reference(wl_client* client) const | ||
4025 | 1122 | { | ||
4026 | 1123 | return keyboard[client]; | ||
4027 | 1124 | } | ||
4028 | 1125 | |||
4029 | 1126 | InputCtx<WlPointer> const& WlSeat::acquire_pointer_reference(wl_client* client) const | ||
4030 | 1127 | { | ||
4031 | 1128 | return pointer[client]; | ||
4032 | 1129 | } | ||
4033 | 1130 | |||
4034 | 1131 | InputCtx<WlTouch> const& WlSeat::acquire_touch_reference(wl_client* client) const | ||
4035 | 1132 | { | ||
4036 | 1133 | return touch[client]; | ||
4037 | 1134 | } | ||
4038 | 1135 | |||
4039 | 1136 | void WaylandEventSink::send_buffer(BufferStreamId /*id*/, graphics::Buffer& /*buffer*/, graphics::BufferIpcMsgType) | ||
4040 | 1137 | { | ||
4041 | 1138 | } | ||
4042 | 1139 | |||
4043 | 1140 | void WaylandEventSink::handle_event(MirEvent const& e) | ||
4044 | 1141 | { | ||
4045 | 1142 | switch(mir_event_get_type(&e)) | ||
4046 | 1143 | { | ||
4047 | 1144 | default: | ||
4048 | 1145 | // Do nothing | ||
4049 | 1146 | break; | ||
4050 | 1147 | } | ||
4051 | 1148 | } | ||
4052 | 1149 | |||
4053 | 1150 | void WaylandEventSink::handle_lifecycle_event(MirLifecycleState state) | ||
4054 | 1151 | { | ||
4055 | 1152 | lifecycle_handler(state); | ||
4056 | 1153 | } | ||
4057 | 1154 | |||
4058 | 1155 | void WaylandEventSink::handle_display_config_change(graphics::DisplayConfiguration const& /*config*/) | ||
4059 | 1156 | { | ||
4060 | 1157 | } | ||
4061 | 1158 | |||
4062 | 1159 | void WaylandEventSink::send_ping(int32_t) | ||
4063 | 1160 | { | ||
4064 | 1161 | } | ||
4065 | 1162 | |||
4066 | 1163 | class SurfaceInputSink : public mf::EventSink | ||
4067 | 1164 | { | ||
4068 | 1165 | public: | ||
4069 | 1166 | SurfaceInputSink(WlSeat* seat, wl_client* client, wl_resource* target) | ||
4070 | 1167 | : seat{seat}, | ||
4071 | 1168 | client{client}, | ||
4072 | 1169 | target{target} | ||
4073 | 1170 | { | ||
4074 | 1171 | } | ||
4075 | 1172 | |||
4076 | 1173 | void handle_event(MirEvent const& e) override; | ||
4077 | 1174 | void handle_lifecycle_event(MirLifecycleState) override {} | ||
4078 | 1175 | void handle_display_config_change(graphics::DisplayConfiguration const&) override {} | ||
4079 | 1176 | void send_ping(int32_t) override {} | ||
4080 | 1177 | void handle_input_config_change(MirInputConfig const&) override {} | ||
4081 | 1178 | void handle_error(ClientVisibleError const&) override {} | ||
4082 | 1179 | |||
4083 | 1180 | void send_buffer(frontend::BufferStreamId, graphics::Buffer&, graphics::BufferIpcMsgType) override {} | ||
4084 | 1181 | void add_buffer(graphics::Buffer&) override {} | ||
4085 | 1182 | void error_buffer(geometry::Size, MirPixelFormat, std::string const& ) override {} | ||
4086 | 1183 | void update_buffer(graphics::Buffer&) override {} | ||
4087 | 1184 | |||
4088 | 1185 | private: | ||
4089 | 1186 | WlSeat* const seat; | ||
4090 | 1187 | wl_client* const client; | ||
4091 | 1188 | wl_resource* const target; | ||
4092 | 1189 | }; | ||
4093 | 1190 | |||
4094 | 1191 | void SurfaceInputSink::handle_event(MirEvent const& event) | ||
4095 | 1192 | { | ||
4096 | 1193 | switch (mir_event_get_type(&event)) | ||
4097 | 1194 | { | ||
4098 | 1195 | case mir_event_type_input: | ||
4099 | 1196 | { | ||
4100 | 1197 | auto input_event = mir_event_get_input_event(&event); | ||
4101 | 1198 | switch (mir_input_event_get_type(input_event)) | ||
4102 | 1199 | { | ||
4103 | 1200 | case mir_input_event_type_key: | ||
4104 | 1201 | seat->acquire_keyboard_reference(client).handle_event(input_event, target); | ||
4105 | 1202 | break; | ||
4106 | 1203 | case mir_input_event_type_pointer: | ||
4107 | 1204 | seat->acquire_pointer_reference(client).handle_event(input_event, target); | ||
4108 | 1205 | break; | ||
4109 | 1206 | case mir_input_event_type_touch: | ||
4110 | 1207 | seat->acquire_touch_reference(client).handle_event(input_event, target); | ||
4111 | 1208 | break; | ||
4112 | 1209 | default: | ||
4113 | 1210 | break; | ||
4114 | 1211 | } | ||
4115 | 1212 | } | ||
4116 | 1213 | default: | ||
4117 | 1214 | break; | ||
4118 | 1215 | } | ||
4119 | 1216 | } | ||
4120 | 1217 | |||
4121 | 1218 | class Output | ||
4122 | 1219 | { | ||
4123 | 1220 | public: | ||
4124 | 1221 | Output( | ||
4125 | 1222 | wl_display* display, | ||
4126 | 1223 | mg::DisplayConfigurationOutput const& initial_configuration) | ||
4127 | 1224 | : output{make_output(display)}, | ||
4128 | 1225 | current_config{initial_configuration} | ||
4129 | 1226 | { | ||
4130 | 1227 | } | ||
4131 | 1228 | |||
4132 | 1229 | ~Output() | ||
4133 | 1230 | { | ||
4134 | 1231 | wl_global_destroy(output); | ||
4135 | 1232 | } | ||
4136 | 1233 | |||
4137 | 1234 | void handle_configuration_changed(mg::DisplayConfigurationOutput const& /*config*/) | ||
4138 | 1235 | { | ||
4139 | 1236 | |||
4140 | 1237 | } | ||
4141 | 1238 | |||
4142 | 1239 | private: | ||
4143 | 1240 | static void send_initial_config( | ||
4144 | 1241 | wl_resource* client_resource, | ||
4145 | 1242 | mg::DisplayConfigurationOutput const& config) | ||
4146 | 1243 | { | ||
4147 | 1244 | wl_output_send_geometry( | ||
4148 | 1245 | client_resource, | ||
4149 | 1246 | config.top_left.x.as_int(), | ||
4150 | 1247 | config.top_left.y.as_int(), | ||
4151 | 1248 | config.physical_size_mm.width.as_int(), | ||
4152 | 1249 | config.physical_size_mm.height.as_int(), | ||
4153 | 1250 | WL_OUTPUT_SUBPIXEL_UNKNOWN, | ||
4154 | 1251 | "Fake manufacturer", | ||
4155 | 1252 | "Fake model", | ||
4156 | 1253 | WL_OUTPUT_TRANSFORM_NORMAL); | ||
4157 | 1254 | for (size_t i = 0; i < config.modes.size(); ++i) | ||
4158 | 1255 | { | ||
4159 | 1256 | auto const& mode = config.modes[i]; | ||
4160 | 1257 | wl_output_send_mode( | ||
4161 | 1258 | client_resource, | ||
4162 | 1259 | ((i == config.preferred_mode_index ? WL_OUTPUT_MODE_PREFERRED : 0) | | ||
4163 | 1260 | (i == config.current_mode_index ? WL_OUTPUT_MODE_CURRENT : 0)), | ||
4164 | 1261 | mode.size.width.as_int(), | ||
4165 | 1262 | mode.size.height.as_int(), | ||
4166 | 1263 | mode.vrefresh_hz * 1000); | ||
4167 | 1264 | } | ||
4168 | 1265 | wl_output_send_scale(client_resource, 1); | ||
4169 | 1266 | wl_output_send_done(client_resource); | ||
4170 | 1267 | } | ||
4171 | 1268 | |||
4172 | 1269 | wl_global* make_output(wl_display* display) | ||
4173 | 1270 | { | ||
4174 | 1271 | return wl_global_create( | ||
4175 | 1272 | display, | ||
4176 | 1273 | &wl_output_interface, | ||
4177 | 1274 | 2, | ||
4178 | 1275 | this, &on_bind); | ||
4179 | 1276 | } | ||
4180 | 1277 | |||
4181 | 1278 | static void on_bind(wl_client* client, void* data, uint32_t version, uint32_t id) | ||
4182 | 1279 | { | ||
4183 | 1280 | auto output = reinterpret_cast<Output*>(data); | ||
4184 | 1281 | auto resource = wl_resource_create(client, &wl_output_interface, | ||
4185 | 1282 | std::min(version, 2u), id); | ||
4186 | 1283 | if (resource == NULL) { | ||
4187 | 1284 | wl_client_post_no_memory(client); | ||
4188 | 1285 | return; | ||
4189 | 1286 | } | ||
4190 | 1287 | |||
4191 | 1288 | output->resource_map[client].push_back(resource); | ||
4192 | 1289 | wl_resource_set_destructor(resource, &resource_destructor); | ||
4193 | 1290 | wl_resource_set_user_data(resource, &(output->resource_map)); | ||
4194 | 1291 | |||
4195 | 1292 | send_initial_config(resource, output->current_config); | ||
4196 | 1293 | } | ||
4197 | 1294 | |||
4198 | 1295 | static void resource_destructor(wl_resource* resource) | ||
4199 | 1296 | { | ||
4200 | 1297 | auto& map = *reinterpret_cast<decltype(resource_map)*>( | ||
4201 | 1298 | wl_resource_get_user_data(resource)); | ||
4202 | 1299 | |||
4203 | 1300 | auto& client_resource_list = map[wl_resource_get_client(resource)]; | ||
4204 | 1301 | std::remove_if( | ||
4205 | 1302 | client_resource_list.begin(), | ||
4206 | 1303 | client_resource_list.end(), | ||
4207 | 1304 | [resource](auto candidate) { return candidate == resource; }); | ||
4208 | 1305 | } | ||
4209 | 1306 | |||
4210 | 1307 | private: | ||
4211 | 1308 | wl_global* const output; | ||
4212 | 1309 | mg::DisplayConfigurationOutput current_config; | ||
4213 | 1310 | std::unordered_map<wl_client*, std::vector<wl_resource*>> resource_map; | ||
4214 | 1311 | }; | ||
4215 | 1312 | |||
4216 | 1313 | class OutputManager | ||
4217 | 1314 | { | ||
4218 | 1315 | public: | ||
4219 | 1316 | OutputManager( | ||
4220 | 1317 | wl_display* display, | ||
4221 | 1318 | mf::DisplayChanger& display_config) | ||
4222 | 1319 | : display{display} | ||
4223 | 1320 | { | ||
4224 | 1321 | // TODO: Also register display configuration listeners | ||
4225 | 1322 | display_config.base_configuration()->for_each_output(std::bind(&OutputManager::create_output, this, std::placeholders::_1)); | ||
4226 | 1323 | } | ||
4227 | 1324 | |||
4228 | 1325 | private: | ||
4229 | 1326 | void create_output(mg::DisplayConfigurationOutput const& initial_config) | ||
4230 | 1327 | { | ||
4231 | 1328 | if (initial_config.used) | ||
4232 | 1329 | { | ||
4233 | 1330 | outputs.emplace( | ||
4234 | 1331 | initial_config.id, | ||
4235 | 1332 | std::make_unique<Output>( | ||
4236 | 1333 | display, | ||
4237 | 1334 | initial_config)); | ||
4238 | 1335 | } | ||
4239 | 1336 | } | ||
4240 | 1337 | |||
4241 | 1338 | void handle_configuration_change(mg::DisplayConfiguration const& config) | ||
4242 | 1339 | { | ||
4243 | 1340 | config.for_each_output([this](mg::DisplayConfigurationOutput const& output_config) | ||
4244 | 1341 | { | ||
4245 | 1342 | auto output_iter = outputs.find(output_config.id); | ||
4246 | 1343 | if (output_iter != outputs.end()) | ||
4247 | 1344 | { | ||
4248 | 1345 | if (output_config.used) | ||
4249 | 1346 | { | ||
4250 | 1347 | output_iter->second->handle_configuration_changed(output_config); | ||
4251 | 1348 | } | ||
4252 | 1349 | else | ||
4253 | 1350 | { | ||
4254 | 1351 | outputs.erase(output_iter); | ||
4255 | 1352 | } | ||
4256 | 1353 | } | ||
4257 | 1354 | else if (output_config.used) | ||
4258 | 1355 | { | ||
4259 | 1356 | outputs[output_config.id] = std::make_unique<Output>(display, output_config); | ||
4260 | 1357 | } | ||
4261 | 1358 | }); | ||
4262 | 1359 | } | ||
4263 | 1360 | |||
4264 | 1361 | wl_display* const display; | ||
4265 | 1362 | std::unordered_map<mg::DisplayConfigurationOutputId, std::unique_ptr<Output>> outputs; | ||
4266 | 1363 | }; | ||
4267 | 1364 | |||
4268 | 1365 | class WlShellSurface : public wayland::ShellSurface | ||
4269 | 1366 | { | ||
4270 | 1367 | public: | ||
4271 | 1368 | WlShellSurface( | ||
4272 | 1369 | wl_client* client, | ||
4273 | 1370 | wl_resource* parent, | ||
4274 | 1371 | uint32_t id, | ||
4275 | 1372 | wl_resource* surface, | ||
4276 | 1373 | std::shared_ptr<mf::Shell> const& shell, | ||
4277 | 1374 | WlSeat& seat) | ||
4278 | 1375 | : ShellSurface(client, parent, id), | ||
4279 | 1376 | shell{shell} | ||
4280 | 1377 | { | ||
4281 | 1378 | auto* tmp = wl_resource_get_user_data(surface); | ||
4282 | 1379 | auto& mir_surface = *static_cast<WlSurface*>(reinterpret_cast<wayland::Surface*>(tmp)); | ||
4283 | 1380 | |||
4284 | 1381 | auto const session = session_for_client(client); | ||
4285 | 1382 | |||
4286 | 1383 | auto params = ms::SurfaceCreationParameters() | ||
4287 | 1384 | .of_type(mir_window_type_freestyle) | ||
4288 | 1385 | .of_size(geom::Size{100, 100}) | ||
4289 | 1386 | .with_buffer_stream(mir_surface.stream_id); | ||
4290 | 1387 | |||
4291 | 1388 | surface_id = shell->create_surface( | ||
4292 | 1389 | session, | ||
4293 | 1390 | params, | ||
4294 | 1391 | std::make_shared<SurfaceInputSink>(&seat, client, surface)); | ||
4295 | 1392 | |||
4296 | 1393 | mir_surface.set_resize_handler( | ||
4297 | 1394 | [shell, session, id = surface_id](geom::Size new_size) | ||
4298 | 1395 | { | ||
4299 | 1396 | shell::SurfaceSpecification new_size_spec; | ||
4300 | 1397 | new_size_spec.width = new_size.width; | ||
4301 | 1398 | new_size_spec.height = new_size.height; | ||
4302 | 1399 | shell->modify_surface(session, id, new_size_spec); | ||
4303 | 1400 | }); | ||
4304 | 1401 | |||
4305 | 1402 | mir_surface.set_hide_handler( | ||
4306 | 1403 | [shell, session, id = surface_id]() | ||
4307 | 1404 | { | ||
4308 | 1405 | shell::SurfaceSpecification hide_spec; | ||
4309 | 1406 | hide_spec.state = mir_window_state_hidden; | ||
4310 | 1407 | shell->modify_surface(session, id, hide_spec); | ||
4311 | 1408 | }); | ||
4312 | 1409 | |||
4313 | 1410 | auto shim = new DestructionShim{session, shell, surface_id}; | ||
4314 | 1411 | shim->destruction_listener.notify = &cleanup_surface; | ||
4315 | 1412 | wl_resource_add_destroy_listener( | ||
4316 | 1413 | resource, | ||
4317 | 1414 | &shim->destruction_listener); | ||
4318 | 1415 | } | ||
4319 | 1416 | |||
4320 | 1417 | ~WlShellSurface() override = default; | ||
4321 | 1418 | protected: | ||
4322 | 1419 | void pong(uint32_t /*serial*/) override | ||
4323 | 1420 | { | ||
4324 | 1421 | } | ||
4325 | 1422 | |||
4326 | 1423 | void move(struct wl_resource* /*seat*/, uint32_t /*serial*/) override | ||
4327 | 1424 | { | ||
4328 | 1425 | } | ||
4329 | 1426 | |||
4330 | 1427 | void resize(struct wl_resource* /*seat*/, uint32_t /*serial*/, uint32_t /*edges*/) override | ||
4331 | 1428 | { | ||
4332 | 1429 | } | ||
4333 | 1430 | |||
4334 | 1431 | void set_toplevel() override | ||
4335 | 1432 | { | ||
4336 | 1433 | } | ||
4337 | 1434 | |||
4338 | 1435 | void set_transient( | ||
4339 | 1436 | struct wl_resource* /*parent*/, | ||
4340 | 1437 | int32_t /*x*/, | ||
4341 | 1438 | int32_t /*y*/, | ||
4342 | 1439 | uint32_t /*flags*/) override | ||
4343 | 1440 | { | ||
4344 | 1441 | } | ||
4345 | 1442 | |||
4346 | 1443 | void set_fullscreen( | ||
4347 | 1444 | uint32_t /*method*/, | ||
4348 | 1445 | uint32_t /*framerate*/, | ||
4349 | 1446 | std::experimental::optional<struct wl_resource*> const& /*output*/) override | ||
4350 | 1447 | { | ||
4351 | 1448 | } | ||
4352 | 1449 | |||
4353 | 1450 | void set_popup( | ||
4354 | 1451 | struct wl_resource* /*seat*/, | ||
4355 | 1452 | uint32_t /*serial*/, | ||
4356 | 1453 | struct wl_resource* /*parent*/, | ||
4357 | 1454 | int32_t /*x*/, | ||
4358 | 1455 | int32_t /*y*/, | ||
4359 | 1456 | uint32_t /*flags*/) override | ||
4360 | 1457 | { | ||
4361 | 1458 | } | ||
4362 | 1459 | |||
4363 | 1460 | void set_maximized(std::experimental::optional<struct wl_resource*> const& /*output*/) override | ||
4364 | 1461 | { | ||
4365 | 1462 | } | ||
4366 | 1463 | |||
4367 | 1464 | void set_title(std::string const& /*title*/) override | ||
4368 | 1465 | { | ||
4369 | 1466 | } | ||
4370 | 1467 | |||
4371 | 1468 | void set_class(std::string const& /*class_*/) override | ||
4372 | 1469 | { | ||
4373 | 1470 | } | ||
4374 | 1471 | private: | ||
4375 | 1472 | struct DestructionShim | ||
4376 | 1473 | { | ||
4377 | 1474 | DestructionShim( | ||
4378 | 1475 | std::shared_ptr<mf::Session> const& session, | ||
4379 | 1476 | std::shared_ptr<mf::Shell> const& shell, | ||
4380 | 1477 | mf::SurfaceId id) | ||
4381 | 1478 | : session{session}, | ||
4382 | 1479 | shell{shell}, | ||
4383 | 1480 | surface_id{id} | ||
4384 | 1481 | { | ||
4385 | 1482 | } | ||
4386 | 1483 | |||
4387 | 1484 | std::shared_ptr<mf::Session> const session; | ||
4388 | 1485 | std::shared_ptr<mf::Shell> const shell; | ||
4389 | 1486 | mf::SurfaceId const surface_id; | ||
4390 | 1487 | wl_listener destruction_listener; | ||
4391 | 1488 | }; | ||
4392 | 1489 | |||
4393 | 1490 | static_assert( | ||
4394 | 1491 | std::is_standard_layout<DestructionShim>::value, | ||
4395 | 1492 | "DestructionShim must be Standard Layout for wl_container_of to be defined behaviour"); | ||
4396 | 1493 | |||
4397 | 1494 | static void cleanup_surface(wl_listener* listener, void*) | ||
4398 | 1495 | { | ||
4399 | 1496 | DestructionShim* shim; | ||
4400 | 1497 | shim = wl_container_of(listener, shim, destruction_listener); | ||
4401 | 1498 | |||
4402 | 1499 | shim->shell->destroy_surface(shim->session, shim->surface_id); | ||
4403 | 1500 | |||
4404 | 1501 | delete shim; | ||
4405 | 1502 | } | ||
4406 | 1503 | |||
4407 | 1504 | std::shared_ptr<mf::Shell> const shell; | ||
4408 | 1505 | mf::SurfaceId surface_id; | ||
4409 | 1506 | }; | ||
4410 | 1507 | |||
4411 | 1508 | class WlShell : public wayland::Shell | ||
4412 | 1509 | { | ||
4413 | 1510 | public: | ||
4414 | 1511 | WlShell( | ||
4415 | 1512 | wl_display* display, | ||
4416 | 1513 | std::shared_ptr<mf::Shell> const& shell, | ||
4417 | 1514 | WlSeat& seat) | ||
4418 | 1515 | : Shell(display, 1), | ||
4419 | 1516 | shell{shell}, | ||
4420 | 1517 | seat{seat} | ||
4421 | 1518 | { | ||
4422 | 1519 | } | ||
4423 | 1520 | |||
4424 | 1521 | void get_shell_surface( | ||
4425 | 1522 | wl_client* client, | ||
4426 | 1523 | wl_resource* resource, | ||
4427 | 1524 | uint32_t id, | ||
4428 | 1525 | wl_resource* surface) override | ||
4429 | 1526 | { | ||
4430 | 1527 | new WlShellSurface(client, resource, id, surface, shell, seat); | ||
4431 | 1528 | } | ||
4432 | 1529 | private: | ||
4433 | 1530 | std::shared_ptr<mf::Shell> const shell; | ||
4434 | 1531 | WlSeat& seat; | ||
4435 | 1532 | }; | ||
4436 | 1533 | } | ||
4437 | 1534 | } | ||
4438 | 1535 | |||
4439 | 1536 | namespace | ||
4440 | 1537 | { | ||
4441 | 1538 | int halt_eventloop(int fd, uint32_t /*mask*/, void* data) | ||
4442 | 1539 | { | ||
4443 | 1540 | auto display = reinterpret_cast<wl_display*>(data); | ||
4444 | 1541 | wl_display_terminate(display); | ||
4445 | 1542 | |||
4446 | 1543 | eventfd_t ignored; | ||
4447 | 1544 | if (eventfd_read(fd, &ignored) < 0) | ||
4448 | 1545 | { | ||
4449 | 1546 | BOOST_THROW_EXCEPTION((std::system_error{ | ||
4450 | 1547 | errno, | ||
4451 | 1548 | std::system_category(), | ||
4452 | 1549 | "Failed to consume pause event notification"})); | ||
4453 | 1550 | } | ||
4454 | 1551 | return 0; | ||
4455 | 1552 | } | ||
4456 | 1553 | } | ||
4457 | 1554 | |||
4458 | 1555 | namespace | ||
4459 | 1556 | { | ||
4460 | 1557 | void cleanup_display(wl_display *display) | ||
4461 | 1558 | { | ||
4462 | 1559 | wl_display_flush_clients(display); | ||
4463 | 1560 | wl_display_destroy(display); | ||
4464 | 1561 | } | ||
4465 | 1562 | |||
4466 | 1563 | class WaylandExecutor : public mir::Executor | ||
4467 | 1564 | { | ||
4468 | 1565 | public: | ||
4469 | 1566 | void spawn (std::function<void ()>&& work) override | ||
4470 | 1567 | { | ||
4471 | 1568 | { | ||
4472 | 1569 | std::lock_guard<std::recursive_mutex> lock{mutex}; | ||
4473 | 1570 | workqueue.emplace_back(std::move(work)); | ||
4474 | 1571 | } | ||
4475 | 1572 | if (auto err = eventfd_write(notify_fd, 1)) | ||
4476 | 1573 | { | ||
4477 | 1574 | BOOST_THROW_EXCEPTION((std::system_error{err, std::system_category(), "eventfd_write failed to notify event loop"})); | ||
4478 | 1575 | } | ||
4479 | 1576 | } | ||
4480 | 1577 | |||
4481 | 1578 | /** | ||
4482 | 1579 | * Get an Executor which dispatches onto a wl_event_loop | ||
4483 | 1580 | * | ||
4484 | 1581 | * \note The executor may outlive the wl_event_loop, but no tasks will be dispatched | ||
4485 | 1582 | * after the wl_event_loop is destroyed. | ||
4486 | 1583 | * | ||
4487 | 1584 | * \param [in] loop The event loop to dispatch on | ||
4488 | 1585 | * \return An Executor that queues onto the wl_event_loop | ||
4489 | 1586 | */ | ||
4490 | 1587 | static std::shared_ptr<mir::Executor> executor_for_event_loop(wl_event_loop* loop) | ||
4491 | 1588 | { | ||
4492 | 1589 | if (auto notifier = wl_event_loop_get_destroy_listener(loop, &on_display_destruction)) | ||
4493 | 1590 | { | ||
4494 | 1591 | DestructionShim* shim; | ||
4495 | 1592 | shim = wl_container_of(notifier, shim, destruction_listener); | ||
4496 | 1593 | |||
4497 | 1594 | return shim->executor; | ||
4498 | 1595 | } | ||
4499 | 1596 | else | ||
4500 | 1597 | { | ||
4501 | 1598 | auto const executor = std::shared_ptr<WaylandExecutor>{new WaylandExecutor{loop}}; | ||
4502 | 1599 | auto shim = std::make_unique<DestructionShim>(executor); | ||
4503 | 1600 | |||
4504 | 1601 | shim->destruction_listener.notify = &on_display_destruction; | ||
4505 | 1602 | wl_event_loop_add_destroy_listener(loop, &(shim.release())->destruction_listener); | ||
4506 | 1603 | |||
4507 | 1604 | return executor; | ||
4508 | 1605 | } | ||
4509 | 1606 | } | ||
4510 | 1607 | |||
4511 | 1608 | private: | ||
4512 | 1609 | WaylandExecutor(wl_event_loop* loop) | ||
4513 | 1610 | : notify_fd{eventfd(0, EFD_CLOEXEC | EFD_SEMAPHORE | EFD_NONBLOCK)}, | ||
4514 | 1611 | notify_source{wl_event_loop_add_fd(loop, notify_fd, WL_EVENT_READABLE, &on_notify, this)} | ||
4515 | 1612 | { | ||
4516 | 1613 | if (notify_fd == mir::Fd::invalid) | ||
4517 | 1614 | { | ||
4518 | 1615 | BOOST_THROW_EXCEPTION((std::system_error{ | ||
4519 | 1616 | errno, | ||
4520 | 1617 | std::system_category(), | ||
4521 | 1618 | "Failed to create IPC pause notification eventfd"})); | ||
4522 | 1619 | } | ||
4523 | 1620 | } | ||
4524 | 1621 | |||
4525 | 1622 | static int on_notify(int fd, uint32_t, void* data) | ||
4526 | 1623 | { | ||
4527 | 1624 | auto executor = static_cast<WaylandExecutor*>(data); | ||
4528 | 1625 | |||
4529 | 1626 | eventfd_t unused; | ||
4530 | 1627 | if (auto err = eventfd_read(fd, &unused)) | ||
4531 | 1628 | { | ||
4532 | 1629 | mir::log_error( | ||
4533 | 1630 | "eventfd_read failed to consume wakeup notification: %s (%i)", | ||
4534 | 1631 | strerror(err), | ||
4535 | 1632 | err); | ||
4536 | 1633 | } | ||
4537 | 1634 | |||
4538 | 1635 | std::lock_guard<std::recursive_mutex> lock{executor->mutex}; | ||
4539 | 1636 | while (!executor->workqueue.empty()) | ||
4540 | 1637 | { | ||
4541 | 1638 | auto work = std::move(executor->workqueue.front()); | ||
4542 | 1639 | work(); | ||
4543 | 1640 | executor->workqueue.pop_front(); | ||
4544 | 1641 | } | ||
4545 | 1642 | |||
4546 | 1643 | return 0; | ||
4547 | 1644 | } | ||
4548 | 1645 | |||
4549 | 1646 | static void on_display_destruction(wl_listener* listener, void*) | ||
4550 | 1647 | { | ||
4551 | 1648 | DestructionShim* shim; | ||
4552 | 1649 | shim = wl_container_of(listener, shim, destruction_listener); | ||
4553 | 1650 | |||
4554 | 1651 | { | ||
4555 | 1652 | std::lock_guard<std::recursive_mutex> lock{shim->executor->mutex}; | ||
4556 | 1653 | wl_event_source_remove(shim->executor->notify_source); | ||
4557 | 1654 | } | ||
4558 | 1655 | delete shim; | ||
4559 | 1656 | } | ||
4560 | 1657 | |||
4561 | 1658 | std::recursive_mutex mutex; | ||
4562 | 1659 | mir::Fd const notify_fd; | ||
4563 | 1660 | std::deque<std::function<void()>> workqueue; | ||
4564 | 1661 | |||
4565 | 1662 | wl_event_source* const notify_source; | ||
4566 | 1663 | |||
4567 | 1664 | struct DestructionShim | ||
4568 | 1665 | { | ||
4569 | 1666 | explicit DestructionShim(std::shared_ptr<WaylandExecutor> const& executor) | ||
4570 | 1667 | : executor{executor} | ||
4571 | 1668 | { | ||
4572 | 1669 | } | ||
4573 | 1670 | |||
4574 | 1671 | std::shared_ptr<WaylandExecutor> const executor; | ||
4575 | 1672 | wl_listener destruction_listener; | ||
4576 | 1673 | }; | ||
4577 | 1674 | static_assert( | ||
4578 | 1675 | std::is_standard_layout<DestructionShim>::value, | ||
4579 | 1676 | "DestructionShim must be Standard Layout for wl_container_of to be defined behaviour"); | ||
4580 | 1677 | }; | ||
4581 | 1678 | } | ||
4582 | 1679 | |||
4583 | 1680 | mf::WaylandConnector::WaylandConnector( | ||
4584 | 1681 | std::shared_ptr<mf::Shell> const& shell, | ||
4585 | 1682 | DisplayChanger& display_config, | ||
4586 | 1683 | std::shared_ptr<mg::GraphicBufferAllocator> const& allocator) | ||
4587 | 1684 | : display{wl_display_create(), &cleanup_display}, | ||
4588 | 1685 | pause_signal{eventfd(0, EFD_CLOEXEC | EFD_SEMAPHORE)}, | ||
4589 | 1686 | allocator{std::dynamic_pointer_cast<mg::WaylandAllocator>(allocator)} | ||
4590 | 1687 | { | ||
4591 | 1688 | if (pause_signal == mir::Fd::invalid) | ||
4592 | 1689 | { | ||
4593 | 1690 | BOOST_THROW_EXCEPTION((std::system_error{ | ||
4594 | 1691 | errno, | ||
4595 | 1692 | std::system_category(), | ||
4596 | 1693 | "Failed to create IPC pause notification eventfd"})); | ||
4597 | 1694 | } | ||
4598 | 1695 | |||
4599 | 1696 | if (!display) | ||
4600 | 1697 | { | ||
4601 | 1698 | BOOST_THROW_EXCEPTION(std::runtime_error{"Failed to create wl_display"}); | ||
4602 | 1699 | } | ||
4603 | 1700 | |||
4604 | 1701 | /* | ||
4605 | 1702 | * Here be Dragons! | ||
4606 | 1703 | * | ||
4607 | 1704 | * Some clients expect a certain order in the publication of globals, and will | ||
4608 | 1705 | * crash with a different order. Yay! | ||
4609 | 1706 | * | ||
4610 | 1707 | * So far I've only found ones which expect wl_compositor before anything else, | ||
4611 | 1708 | * so stick that first. | ||
4612 | 1709 | */ | ||
4613 | 1710 | auto const executor = WaylandExecutor::executor_for_event_loop(wl_display_get_event_loop(display.get())); | ||
4614 | 1711 | |||
4615 | 1712 | compositor_global = std::make_unique<mf::WlCompositor>( | ||
4616 | 1713 | display.get(), | ||
4617 | 1714 | executor, | ||
4618 | 1715 | this->allocator); | ||
4619 | 1716 | seat_global = std::make_unique<mf::WlSeat>(display.get(), executor); | ||
4620 | 1717 | output_manager = std::make_unique<mf::OutputManager>( | ||
4621 | 1718 | display.get(), | ||
4622 | 1719 | display_config); | ||
4623 | 1720 | shell_global = std::make_unique<mf::WlShell>(display.get(), shell, *seat_global); | ||
4624 | 1721 | |||
4625 | 1722 | wl_display_init_shm(display.get()); | ||
4626 | 1723 | |||
4627 | 1724 | if (this->allocator) | ||
4628 | 1725 | { | ||
4629 | 1726 | this->allocator->bind_display(display.get()); | ||
4630 | 1727 | } | ||
4631 | 1728 | else | ||
4632 | 1729 | { | ||
4633 | 1730 | mir::log_warning("No WaylandAllocator EGL support!"); | ||
4634 | 1731 | } | ||
4635 | 1732 | |||
4636 | 1733 | wl_display_add_socket_auto(display.get()); | ||
4637 | 1734 | |||
4638 | 1735 | auto wayland_loop = wl_display_get_event_loop(display.get()); | ||
4639 | 1736 | |||
4640 | 1737 | setup_new_client_handler(display.get(), shell); | ||
4641 | 1738 | |||
4642 | 1739 | pause_source = wl_event_loop_add_fd(wayland_loop, pause_signal, WL_EVENT_READABLE, &halt_eventloop, display.get()); | ||
4643 | 1740 | } | ||
4644 | 1741 | |||
4645 | 1742 | mf::WaylandConnector::~WaylandConnector() | ||
4646 | 1743 | { | ||
4647 | 1744 | if (dispatch_thread.joinable()) | ||
4648 | 1745 | { | ||
4649 | 1746 | stop(); | ||
4650 | 1747 | } | ||
4651 | 1748 | wl_event_source_remove(pause_source); | ||
4652 | 1749 | } | ||
4653 | 1750 | |||
4654 | 1751 | void mf::WaylandConnector::start() | ||
4655 | 1752 | { | ||
4656 | 1753 | dispatch_thread = std::thread{wl_display_run, display.get()}; | ||
4657 | 1754 | } | ||
4658 | 1755 | |||
4659 | 1756 | void mf::WaylandConnector::stop() | ||
4660 | 1757 | { | ||
4661 | 1758 | if (eventfd_write(pause_signal, 1) < 0) | ||
4662 | 1759 | { | ||
4663 | 1760 | BOOST_THROW_EXCEPTION((std::system_error{ | ||
4664 | 1761 | errno, | ||
4665 | 1762 | std::system_category(), | ||
4666 | 1763 | "Failed to send IPC eventloop pause signal"})); | ||
4667 | 1764 | } | ||
4668 | 1765 | if (dispatch_thread.joinable()) | ||
4669 | 1766 | { | ||
4670 | 1767 | dispatch_thread.join(); | ||
4671 | 1768 | dispatch_thread = std::thread{}; | ||
4672 | 1769 | } | ||
4673 | 1770 | else | ||
4674 | 1771 | { | ||
4675 | 1772 | mir::log_warning("WaylandConnector::stop() called on not-running connector?"); | ||
4676 | 1773 | } | ||
4677 | 1774 | } | ||
4678 | 1775 | |||
4679 | 1776 | int mf::WaylandConnector::client_socket_fd() const | ||
4680 | 1777 | { | ||
4681 | 1778 | return -1; | ||
4682 | 1779 | } | ||
4683 | 1780 | |||
4684 | 1781 | int mf::WaylandConnector::client_socket_fd( | ||
4685 | 1782 | std::function<void(std::shared_ptr<Session> const& session)> const& /*connect_handler*/) const | ||
4686 | 1783 | { | ||
4687 | 1784 | return -1; | ||
4688 | 1785 | } | ||
4689 | 0 | 1786 | ||
4690 | === added file 'src/server/frontend/wayland/wayland_connector.h' | |||
4691 | --- src/server/frontend/wayland/wayland_connector.h 1970-01-01 00:00:00 +0000 | |||
4692 | +++ src/server/frontend/wayland/wayland_connector.h 2017-09-07 05:58:57 +0000 | |||
4693 | @@ -0,0 +1,83 @@ | |||
4694 | 1 | /* | ||
4695 | 2 | * Copyright © 2015 Canonical Ltd. | ||
4696 | 3 | * | ||
4697 | 4 | * This program is free software: you can redistribute it and/or modify it | ||
4698 | 5 | * under the terms of the GNU General Public License version 3, | ||
4699 | 6 | * as published by the Free Software Foundation. | ||
4700 | 7 | * | ||
4701 | 8 | * This program is distributed in the hope that it will be useful, | ||
4702 | 9 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
4703 | 10 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
4704 | 11 | * GNU General Public License for more details. | ||
4705 | 12 | * | ||
4706 | 13 | * You should have received a copy of the GNU General Public License | ||
4707 | 14 | * along with this program. If not, see <http://www.gnu.org/licenses/>. | ||
4708 | 15 | * | ||
4709 | 16 | * Authored by: Christopher James Halse Rogers <christopher.halse.rogers@canonical.com> | ||
4710 | 17 | */ | ||
4711 | 18 | |||
4712 | 19 | #ifndef MIR_FRONTEND_WAYLAND_CONNECTOR_H_ | ||
4713 | 20 | #define MIR_FRONTEND_WAYLAND_CONNECTOR_H_ | ||
4714 | 21 | |||
4715 | 22 | #include "mir/frontend/connector.h" | ||
4716 | 23 | #include "mir/fd.h" | ||
4717 | 24 | |||
4718 | 25 | #include <wayland-server-core.h> | ||
4719 | 26 | #include <thread> | ||
4720 | 27 | |||
4721 | 28 | namespace mir | ||
4722 | 29 | { | ||
4723 | 30 | |||
4724 | 31 | |||
4725 | 32 | namespace graphics | ||
4726 | 33 | { | ||
4727 | 34 | class GraphicBufferAllocator; | ||
4728 | 35 | class WaylandAllocator; | ||
4729 | 36 | } | ||
4730 | 37 | |||
4731 | 38 | namespace frontend | ||
4732 | 39 | { | ||
4733 | 40 | class WlCompositor; | ||
4734 | 41 | class WlApplication; | ||
4735 | 42 | class WlShell; | ||
4736 | 43 | class WlSeat; | ||
4737 | 44 | class OutputManager; | ||
4738 | 45 | |||
4739 | 46 | class Shell; | ||
4740 | 47 | class DisplayChanger; | ||
4741 | 48 | |||
4742 | 49 | class WaylandConnector : public Connector | ||
4743 | 50 | { | ||
4744 | 51 | public: | ||
4745 | 52 | WaylandConnector( | ||
4746 | 53 | std::shared_ptr<Shell> const& shell, | ||
4747 | 54 | DisplayChanger& display_config, | ||
4748 | 55 | std::shared_ptr<graphics::GraphicBufferAllocator> const& allocator); | ||
4749 | 56 | |||
4750 | 57 | ~WaylandConnector() override; | ||
4751 | 58 | |||
4752 | 59 | void start() override; | ||
4753 | 60 | void stop() override; | ||
4754 | 61 | |||
4755 | 62 | int client_socket_fd() const override; | ||
4756 | 63 | |||
4757 | 64 | int client_socket_fd( | ||
4758 | 65 | std::function<void(std::shared_ptr<Session> const& session)> const& connect_handler) const override; | ||
4759 | 66 | |||
4760 | 67 | private: | ||
4761 | 68 | std::unique_ptr<wl_display, void(*)(wl_display*)> const display; | ||
4762 | 69 | mir::Fd const pause_signal; | ||
4763 | 70 | std::unique_ptr<WlCompositor> compositor_global; | ||
4764 | 71 | std::unique_ptr<WlSeat> seat_global; | ||
4765 | 72 | std::unique_ptr<OutputManager> output_manager; | ||
4766 | 73 | std::shared_ptr<graphics::WaylandAllocator> const allocator; | ||
4767 | 74 | std::unique_ptr<WlShell> shell_global; | ||
4768 | 75 | std::thread dispatch_thread; | ||
4769 | 76 | wl_event_source* pause_source; | ||
4770 | 77 | }; | ||
4771 | 78 | |||
4772 | 79 | |||
4773 | 80 | } | ||
4774 | 81 | } | ||
4775 | 82 | |||
4776 | 83 | #endif // MIR_FRONTEND_WAYLAND_CONNECTOR_H_ | ||
4777 | 0 | 84 | ||
4778 | === added file 'src/server/frontend/wayland/wayland_default_configuration.cpp' | |||
4779 | --- src/server/frontend/wayland/wayland_default_configuration.cpp 1970-01-01 00:00:00 +0000 | |||
4780 | +++ src/server/frontend/wayland/wayland_default_configuration.cpp 2017-09-07 05:58:57 +0000 | |||
4781 | @@ -0,0 +1,39 @@ | |||
4782 | 1 | /* | ||
4783 | 2 | * Copyright © 2015, 2017 Canonical Ltd. | ||
4784 | 3 | * | ||
4785 | 4 | * This program is free software: you can redistribute it and/or modify it | ||
4786 | 5 | * under the terms of the GNU General Public License version 2 or 3, | ||
4787 | 6 | * as published by the Free Software Foundation. | ||
4788 | 7 | * | ||
4789 | 8 | * This program is distributed in the hope that it will be useful, | ||
4790 | 9 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
4791 | 10 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
4792 | 11 | * GNU General Public License for more details. | ||
4793 | 12 | * | ||
4794 | 13 | * You should have received a copy of the GNU General Public License | ||
4795 | 14 | * along with this program. If not, see <http://www.gnu.org/licenses/>. | ||
4796 | 15 | * | ||
4797 | 16 | * Authored by: Christopher James Halse Rogers <christopher.halse.rogers@canonical.com> | ||
4798 | 17 | */ | ||
4799 | 18 | |||
4800 | 19 | #include "mir/default_server_configuration.h" | ||
4801 | 20 | #include "wayland_connector.h" | ||
4802 | 21 | |||
4803 | 22 | #include "../../scene/mediating_display_changer.h" | ||
4804 | 23 | #include "mir/graphics/platform.h" | ||
4805 | 24 | |||
4806 | 25 | namespace mf = mir::frontend; | ||
4807 | 26 | |||
4808 | 27 | std::shared_ptr<mf::Connector> | ||
4809 | 28 | mir::DefaultServerConfiguration::the_wayland_connector() | ||
4810 | 29 | { | ||
4811 | 30 | return wayland_connector( | ||
4812 | 31 | [this]() -> std::shared_ptr<mf::Connector> | ||
4813 | 32 | { | ||
4814 | 33 | return std::make_shared<mf::WaylandConnector>( | ||
4815 | 34 | the_frontend_shell(), | ||
4816 | 35 | *the_mediating_display_changer(), | ||
4817 | 36 | the_buffer_allocator()); | ||
4818 | 37 | }); | ||
4819 | 38 | } | ||
4820 | 39 | |||
4821 | 0 | 40 | ||
4822 | === modified file 'src/server/symbols.map' | |||
4823 | --- src/server/symbols.map 2017-08-02 11:07:50 +0000 | |||
4824 | +++ src/server/symbols.map 2017-09-07 05:58:57 +0000 | |||
4825 | @@ -913,6 +913,7 @@ | |||
4826 | 913 | mir::DefaultServerConfiguration::the_surface_factory*; | 913 | mir::DefaultServerConfiguration::the_surface_factory*; |
4827 | 914 | mir::DefaultServerConfiguration::the_surface_stack_model*; | 914 | mir::DefaultServerConfiguration::the_surface_stack_model*; |
4828 | 915 | mir::DefaultServerConfiguration::the_touch_visualizer*; | 915 | mir::DefaultServerConfiguration::the_touch_visualizer*; |
4829 | 916 | mir::DefaultServerConfiguration::the_wayland_connector*; | ||
4830 | 916 | mir::DefaultServerConfiguration::the_window_manager_builder*; | 917 | mir::DefaultServerConfiguration::the_window_manager_builder*; |
4831 | 917 | mir::DefaultServerConfiguration::wrap_cursor*; | 918 | mir::DefaultServerConfiguration::wrap_cursor*; |
4832 | 918 | mir::DefaultServerConfiguration::wrap_cursor_listener*; | 919 | mir::DefaultServerConfiguration::wrap_cursor_listener*; |
4833 | 919 | 920 | ||
4834 | === modified file 'tests/mir_test_doubles/mock_egl.cpp' | |||
4835 | --- tests/mir_test_doubles/mock_egl.cpp 2017-07-28 17:00:43 +0000 | |||
4836 | +++ tests/mir_test_doubles/mock_egl.cpp 2017-09-07 05:58:57 +0000 | |||
4837 | @@ -51,6 +51,16 @@ | |||
4838 | 51 | EGLint extension_eglClientWaitSyncKHR(EGLDisplay dpy, EGLSyncKHR sync, EGLint flags, EGLTimeKHR timeout); | 51 | EGLint extension_eglClientWaitSyncKHR(EGLDisplay dpy, EGLSyncKHR sync, EGLint flags, EGLTimeKHR timeout); |
4839 | 52 | EGLBoolean extension_eglGetSyncValuesCHROMIUM(EGLDisplay dpy, | 52 | EGLBoolean extension_eglGetSyncValuesCHROMIUM(EGLDisplay dpy, |
4840 | 53 | EGLSurface surface, int64_t *ust, int64_t *msc, int64_t *sbc); | 53 | EGLSurface surface, int64_t *ust, int64_t *msc, int64_t *sbc); |
4841 | 54 | EGLBoolean extension_eglBindWaylandDisplayWL( | ||
4842 | 55 | EGLDisplay dpy, | ||
4843 | 56 | struct wl_display *display); | ||
4844 | 57 | EGLBoolean extension_eglUnbindWaylandDisplayWL( | ||
4845 | 58 | EGLDisplay dpy, | ||
4846 | 59 | struct wl_display *display); | ||
4847 | 60 | EGLBoolean extension_eglQueryWaylandBufferWL( | ||
4848 | 61 | EGLDisplay dpy, | ||
4849 | 62 | struct wl_resource *buffer, | ||
4850 | 63 | EGLint attribute, EGLint *value); | ||
4851 | 54 | 64 | ||
4852 | 55 | /* EGL{Surface,Display,Config,Context} are all opaque types, so we can put whatever | 65 | /* EGL{Surface,Display,Config,Context} are all opaque types, so we can put whatever |
4853 | 56 | we want in them for testing */ | 66 | we want in them for testing */ |
4854 | @@ -150,13 +160,24 @@ | |||
4855 | 150 | .WillByDefault(Return( | 160 | .WillByDefault(Return( |
4856 | 151 | reinterpret_cast<func_ptr_t>(extension_eglGetSyncValuesCHROMIUM) | 161 | reinterpret_cast<func_ptr_t>(extension_eglGetSyncValuesCHROMIUM) |
4857 | 152 | )); | 162 | )); |
4858 | 163 | ON_CALL(*this, eglGetProcAddress(StrEq("eglQueryWaylandBufferWL"))) | ||
4859 | 164 | .WillByDefault(Return(reinterpret_cast<func_ptr_t>(&extension_eglQueryWaylandBufferWL))); | ||
4860 | 165 | ON_CALL(*this, eglGetProcAddress(StrEq("eglBindWaylandDisplayWL"))) | ||
4861 | 166 | .WillByDefault(Return(reinterpret_cast<func_ptr_t>(&extension_eglBindWaylandDisplayWL))); | ||
4862 | 167 | ON_CALL(*this, eglGetProcAddress(StrEq("eglUnbindWaylandDisplayWL"))) | ||
4863 | 168 | .WillByDefault(Return(reinterpret_cast<func_ptr_t>(&extension_eglUnbindWaylandDisplayWL))); | ||
4864 | 153 | } | 169 | } |
4865 | 154 | 170 | ||
4866 | 155 | void mtd::MockEGL::provide_egl_extensions() | 171 | void mtd::MockEGL::provide_egl_extensions() |
4867 | 156 | { | 172 | { |
4868 | 157 | using namespace testing; | 173 | using namespace testing; |
4869 | 158 | 174 | ||
4871 | 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 = |
4872 | 176 | "EGL_KHR_image " | ||
4873 | 177 | "EGL_KHR_image_base " | ||
4874 | 178 | "EGL_KHR_image_pixmap " | ||
4875 | 179 | "EGL_EXT_image_dma_buf_import " | ||
4876 | 180 | "EGL_WL_bind_wayland_display"; | ||
4877 | 160 | ON_CALL(*this, eglQueryString(_,EGL_EXTENSIONS)) | 181 | ON_CALL(*this, eglQueryString(_,EGL_EXTENSIONS)) |
4878 | 161 | .WillByDefault(Return(egl_exts)); | 182 | .WillByDefault(Return(egl_exts)); |
4879 | 162 | } | 183 | } |
4880 | @@ -430,3 +451,29 @@ | |||
4881 | 430 | return global_mock_egl->eglGetSyncValuesCHROMIUM(dpy, surface, | 451 | return global_mock_egl->eglGetSyncValuesCHROMIUM(dpy, surface, |
4882 | 431 | ust, msc, sbc); | 452 | ust, msc, sbc); |
4883 | 432 | } | 453 | } |
4884 | 454 | |||
4885 | 455 | EGLBoolean extension_eglBindWaylandDisplayWL( | ||
4886 | 456 | EGLDisplay dpy, | ||
4887 | 457 | struct wl_display *display) | ||
4888 | 458 | { | ||
4889 | 459 | CHECK_GLOBAL_MOCK(EGLBoolean); | ||
4890 | 460 | return global_mock_egl->eglBindWaylandDisplayWL(dpy, display); | ||
4891 | 461 | } | ||
4892 | 462 | |||
4893 | 463 | EGLBoolean extension_eglUnbindWaylandDisplayWL( | ||
4894 | 464 | EGLDisplay dpy, | ||
4895 | 465 | struct wl_display *display) | ||
4896 | 466 | { | ||
4897 | 467 | CHECK_GLOBAL_MOCK(EGLBoolean); | ||
4898 | 468 | return global_mock_egl->eglUnbindWaylandDisplayWL(dpy, display); | ||
4899 | 469 | } | ||
4900 | 470 | |||
4901 | 471 | EGLBoolean extension_eglQueryWaylandBufferWL( | ||
4902 | 472 | EGLDisplay dpy, | ||
4903 | 473 | struct wl_resource* buffer, | ||
4904 | 474 | EGLint attribute, EGLint* value) | ||
4905 | 475 | { | ||
4906 | 476 | CHECK_GLOBAL_MOCK(EGLBoolean); | ||
4907 | 477 | return global_mock_egl->eglQueryWaylandBufferWL( | ||
4908 | 478 | dpy, buffer, attribute, value); | ||
4909 | 479 | } | ||
4910 | 433 | 480 | ||
4911 | === modified file 'tests/mir_test_doubles/nested_mock_egl.cpp' | |||
4912 | --- tests/mir_test_doubles/nested_mock_egl.cpp 2017-07-28 17:00:43 +0000 | |||
4913 | +++ tests/mir_test_doubles/nested_mock_egl.cpp 2017-09-07 05:58:57 +0000 | |||
4914 | @@ -30,21 +30,12 @@ | |||
4915 | 30 | EXPECT_CALL(*this, eglTerminate(_)).Times(1); | 30 | EXPECT_CALL(*this, eglTerminate(_)).Times(1); |
4916 | 31 | } | 31 | } |
4917 | 32 | 32 | ||
4918 | 33 | EXPECT_CALL(*this, eglCreateWindowSurface(_, _, _, _)).Times(AnyNumber()); | ||
4919 | 34 | EXPECT_CALL(*this, eglMakeCurrent(_, _, _, _)).Times(AnyNumber()); | ||
4920 | 35 | EXPECT_CALL(*this, eglDestroySurface(_, _)).Times(AnyNumber()); | ||
4921 | 36 | EXPECT_CALL(*this, eglQueryString(_, _)).Times(AnyNumber()); | ||
4922 | 37 | |||
4923 | 38 | provide_egl_extensions(); | 33 | provide_egl_extensions(); |
4924 | 39 | provide_stub_platform_buffer_swapping(); | 34 | provide_stub_platform_buffer_swapping(); |
4925 | 40 | 35 | ||
4927 | 41 | EXPECT_CALL(*this, eglChooseConfig(_, _, _, _, _)).Times(AnyNumber()).WillRepeatedly( | 36 | |
4928 | 37 | ON_CALL(*this, eglChooseConfig(_, _, _, _, _)).WillByDefault( | ||
4929 | 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))); |
4930 | 43 | EXPECT_CALL(*this, eglGetCurrentContext()).Times(AnyNumber()); | ||
4931 | 44 | EXPECT_CALL(*this, eglCreatePbufferSurface(_, _, _)).Times(AnyNumber()); | ||
4932 | 45 | EXPECT_CALL(*this, eglGetProcAddress(StrEq("eglCreateImageKHR"))).Times(AnyNumber()); | ||
4933 | 46 | EXPECT_CALL(*this, eglGetProcAddress(StrEq("eglDestroyImageKHR"))).Times(AnyNumber()); | ||
4934 | 47 | EXPECT_CALL(*this, eglGetProcAddress(StrEq("glEGLImageTargetTexture2DOES"))).Times(AnyNumber()); | ||
4935 | 48 | 39 | ||
4936 | 49 | { | 40 | { |
4937 | 50 | InSequence context_lifecycle; | 41 | InSequence context_lifecycle; |
FAILED: Continuous integration, rev:4177 /mir-jenkins. ubuntu. com/job/ mir-ci/ 3582/ /mir-jenkins. ubuntu. com/job/ build-mir/ 4905/console /mir-jenkins. ubuntu. com/job/ build-0- fetch/5127 /mir-jenkins. ubuntu. com/job/ build-1- sourcepkg/ release= artful/ 5116 /mir-jenkins. ubuntu. com/job/ build-1- sourcepkg/ release= xenial/ 5116 /mir-jenkins. ubuntu. com/job/ build-1- sourcepkg/ release= zesty/5116 /mir-jenkins. ubuntu. com/job/ build-2- binpkg- mir/arch= amd64,compiler= clang,platform= mesa,release= artful/ 4944/console /mir-jenkins. ubuntu. com/job/ build-2- binpkg- mir/arch= amd64,compiler= clang,platform= mesa,release= zesty/4944/ console /mir-jenkins. ubuntu. com/job/ build-2- binpkg- mir/arch= amd64,compiler= gcc,platform= mesa,release= artful/ 4944/console /mir-jenkins. ubuntu. com/job/ build-2- binpkg- mir/arch= amd64,compiler= gcc,platform= mesa,release= xenial/ 4944/console /mir-jenkins. ubuntu. com/job/ build-2- binpkg- mir/arch= amd64,compiler= gcc,platform= mesa,release= zesty/4944/ console /mir-jenkins. ubuntu. com/job/ build-2- binpkg- mir/arch= cross-armhf, compiler= gcc,platform= mesa,release= artful/ 4944/console /mir-jenkins. ubuntu. com/job/ build-2- binpkg- mir/arch= cross-armhf, compiler= gcc,platform= mesa,release= zesty/4944/ console /mir-jenkins. ubuntu. com/job/ build-2- binpkg- mir/arch= i386,compiler= gcc,platform= mesa,release= xenial/ 4944/console
https:/
Executed test runs:
FAILURE: https:/
SUCCESS: https:/
SUCCESS: https:/
SUCCESS: https:/
SUCCESS: https:/
FAILURE: https:/
FAILURE: https:/
FAILURE: https:/
FAILURE: https:/
FAILURE: https:/
FAILURE: https:/
FAILURE: https:/
FAILURE: https:/
Click here to trigger a rebuild: /mir-jenkins. ubuntu. com/job/ mir-ci/ 3582/rebuild
https:/