Wayland++ 1.0.0
C++ Bindings for Wayland
egl.cpp
1/*
2 * Copyright (c) 2014-2022, Nils Christopher Brause, Philipp Kerling, Zsolt Bölöny
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions are met:
7 *
8 * 1. Redistributions of source code must retain the above copyright notice, this
9 * list of conditions and the following disclaimer.
10 * 2. Redistributions in binary form must reproduce the above copyright notice,
11 * this list of conditions and the following disclaimer in the documentation
12 * and/or other materials provided with the distribution.
13 *
14 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
15 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
16 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
17 * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
18 * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
19 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
20 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
21 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
22 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
23 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
24 */
25
30#include <stdexcept>
31#include <iostream>
32#include <array>
33#include <cstdint>
34#include <wayland-client.hpp>
35#include <wayland-client-protocol-extra.hpp>
36#include <wayland-egl.hpp>
37#include <GL/gl.h>
38#include <linux/input.h>
39#include <wayland-cursor.hpp>
40
41using namespace wayland;
42
43// helper to create a std::function out of a member function and an object
44template <typename R, typename T, typename... Args>
45std::function<R(Args...)> bind_mem_fn(R(T::* func)(Args...), T *t)
46{
47 return [func, t] (Args... args)
48 {
49 return (t->*func)(args...);
50 };
51}
52
53// example Wayland client
54class example
55{
56private:
57 // global objects
58 display_t display;
59 registry_t registry;
60 compositor_t compositor;
61 shell_t shell;
62 xdg_wm_base_t xdg_wm_base;
63 seat_t seat;
64 shm_t shm;
65
66 // local objects
67 surface_t surface;
68 shell_surface_t shell_surface;
69 xdg_surface_t xdg_surface;
70 xdg_toplevel_t xdg_toplevel;
71 pointer_t pointer;
72 keyboard_t keyboard;
73 callback_t frame_cb;
74 cursor_image_t cursor_image;
75 buffer_t cursor_buffer;
76 surface_t cursor_surface;
77
78 // EGL
79 egl_window_t egl_window;
80 EGLDisplay egldisplay = nullptr;
81 EGLSurface eglsurface = nullptr;
82 EGLContext eglcontext = nullptr;
83
84 bool running;
85 bool has_pointer;
86 bool has_keyboard;
87
88 void init_egl()
89 {
90 egldisplay = eglGetDisplay(display);
91 if(egldisplay == EGL_NO_DISPLAY)
92 throw std::runtime_error("eglGetDisplay");
93
94 EGLint major = 0;
95 EGLint minor = 0;
96 if(eglInitialize(egldisplay, &major, &minor) == EGL_FALSE)
97 throw std::runtime_error("eglInitialize");
98 if(!((major == 1 && minor >= 4) || major >= 2))
99 throw std::runtime_error("EGL version too old");
100
101 if(eglBindAPI(EGL_OPENGL_API) == EGL_FALSE)
102 throw std::runtime_error("eglBindAPI");
103
104 std::array<EGLint, 13> config_attribs = {{
105 EGL_SURFACE_TYPE, EGL_WINDOW_BIT,
106 EGL_RED_SIZE, 8,
107 EGL_GREEN_SIZE, 8,
108 EGL_BLUE_SIZE, 8,
109 EGL_ALPHA_SIZE, 8,
110 EGL_RENDERABLE_TYPE, EGL_OPENGL_BIT,
111 EGL_NONE
112 }};
113
114 EGLConfig config = nullptr;
115 EGLint num = 0;
116 if(eglChooseConfig(egldisplay, config_attribs.data(), &config, 1, &num) == EGL_FALSE || num == 0)
117 throw std::runtime_error("eglChooseConfig");
118
119 std::array<EGLint, 3> context_attribs = {{
120 EGL_CONTEXT_CLIENT_VERSION, 2,
121 EGL_NONE
122 }};
123
124 eglcontext = eglCreateContext(egldisplay, config, EGL_NO_CONTEXT, context_attribs.data());
125 if(eglcontext == EGL_NO_CONTEXT)
126 throw std::runtime_error("eglCreateContext");
127
128 eglsurface = eglCreateWindowSurface(egldisplay, config, egl_window, nullptr);
129 if(eglsurface == EGL_NO_SURFACE)
130 throw std::runtime_error("eglCreateWindowSurface");
131
132 if(eglMakeCurrent(egldisplay, eglsurface, eglsurface, eglcontext) == EGL_FALSE)
133 throw std::runtime_error("eglMakeCurrent");
134 }
135
136 void draw(uint32_t serial = 0)
137 {
138 float h = static_cast<float>((serial >> 4) & 0xFF)/255.0F;
139 float s = 1;
140 float v = 1;
141
142 int hi = static_cast<int>(h*6);
143 float f = h*6 - static_cast<float>(hi);
144 float p = v*(1-s);
145 float q = v*(1-s*f);
146 float t = v*(1-s*(1-f));
147 float r = 0;
148 float g = 0;
149 float b = 0;
150
151 switch(hi)
152 {
153 case 1:
154 r = q; g = v; b = p;
155 break;
156 case 2:
157 r = p; g = v; b = t;
158 break;
159 case 3:
160 r = p; g = q; b = v;
161 break;
162 case 4:
163 r = t; g = p; b = v;
164 break;
165 case 5:
166 r = v; g = p; b = q;
167 break;
168 default: // 0,6
169 r = v; g = t; b = p;
170 break;
171 }
172
173 // draw stuff
174 glClearColor(r, g, b, 0.5F);
175 glClear(GL_COLOR_BUFFER_BIT);
176
177 // schedule next draw
178 frame_cb = surface.frame();
179 frame_cb.on_done() = bind_mem_fn(&example::draw, this);
180
181 // swap buffers
182 if(eglSwapBuffers(egldisplay, eglsurface) == EGL_FALSE)
183 throw std::runtime_error("eglSwapBuffers");
184 }
185
186public:
187 example(const example&) = delete;
188 example(example&&) noexcept = delete;
189 example& operator=(const example&) = delete;
190 example& operator=(example&&) noexcept = delete;
191
192 example()
193 {
194 // retrieve global objects
195 registry = display.get_registry();
196 registry.on_global() = [&] (uint32_t name, const std::string& interface, uint32_t version)
197 {
198 if(interface == compositor_t::interface_name)
199 registry.bind(name, compositor, version);
200 else if(interface == shell_t::interface_name)
201 registry.bind(name, shell, version);
202 else if(interface == xdg_wm_base_t::interface_name)
203 registry.bind(name, xdg_wm_base, version);
204 else if(interface == seat_t::interface_name)
205 registry.bind(name, seat, version);
206 else if(interface == shm_t::interface_name)
207 registry.bind(name, shm, version);
208 };
209 display.roundtrip();
210
211 seat.on_capabilities() = [&] (const seat_capability& capability)
212 {
213 has_keyboard = capability & seat_capability::keyboard;
214 has_pointer = capability & seat_capability::pointer;
215 };
216
217 // create a surface
218 surface = compositor.create_surface();
219
220 // create a shell surface
221 if(xdg_wm_base)
222 {
223 xdg_wm_base.on_ping() = [&] (uint32_t serial) { xdg_wm_base.pong(serial); };
224 xdg_surface = xdg_wm_base.get_xdg_surface(surface);
225 xdg_surface.on_configure() = [&] (uint32_t serial) { xdg_surface.ack_configure(serial); };
226 xdg_toplevel = xdg_surface.get_toplevel();
227 xdg_toplevel.set_title("Window");
228 xdg_toplevel.on_close() = [&] () { running = false; };
229 }
230 else
231 {
232 shell_surface = shell.get_shell_surface(surface);
233 shell_surface.on_ping() = [&] (uint32_t serial) { shell_surface.pong(serial); };
234 shell_surface.set_title("Window");
235 shell_surface.set_toplevel();
236 }
237 surface.commit();
238
239 display.roundtrip();
240
241 // Get input devices
242 if(!has_keyboard)
243 throw std::runtime_error("No keyboard found.");
244 if(!has_pointer)
245 throw std::runtime_error("No pointer found.");
246
247 pointer = seat.get_pointer();
248 keyboard = seat.get_keyboard();
249
250 // load cursor theme
251 cursor_theme_t cursor_theme = cursor_theme_t("default", 16, shm);
252 cursor_t cursor = cursor_theme.get_cursor("cross");
253 cursor_image = cursor.image(0);
254 cursor_buffer = cursor_image.get_buffer();
255
256 // create cursor surface
257 cursor_surface = compositor.create_surface();
258
259 // draw cursor
260 pointer.on_enter() = [&] (uint32_t serial, const surface_t& /*unused*/, int32_t /*unused*/, int32_t /*unused*/)
261 {
262 cursor_surface.attach(cursor_buffer, 0, 0);
263 cursor_surface.damage(0, 0, cursor_image.width(), cursor_image.height());
264 cursor_surface.commit();
265 pointer.set_cursor(serial, cursor_surface, 0, 0);
266 };
267
268 // window movement
269 pointer.on_button() = [&] (uint32_t serial, uint32_t /*unused*/, uint32_t button, pointer_button_state state)
270 {
271 if(button == BTN_LEFT && state == pointer_button_state::pressed)
272 {
273 if(xdg_toplevel)
274 xdg_toplevel.move(seat, serial);
275 else
276 shell_surface.move(seat, serial);
277 }
278 };
279
280 // press 'q' to exit
281 keyboard.on_key() = [&] (uint32_t /*unused*/, uint32_t /*unused*/, uint32_t key, keyboard_key_state state)
282 {
283 if(key == KEY_Q && state == keyboard_key_state::pressed)
284 running = false;
285 };
286
287 // intitialize egl
288 egl_window = egl_window_t(surface, 320, 240);
289 init_egl();
290
291 // draw stuff
292 draw();
293 }
294
295 ~example() noexcept
296 {
297 // finialize EGL
298 if(eglDestroyContext(egldisplay, eglcontext) == EGL_FALSE)
299 std::cerr << "eglDestroyContext failed.";
300 if(eglTerminate(egldisplay) == EGL_FALSE)
301 std::cerr << "eglTerminate failed.";
302 }
303
304 void run()
305 {
306 // event loop
307 running = true;
308 while(running)
309 display.dispatch();
310 }
311};
312
313int main()
314{
315 example e;
316 e.run();
317 return 0;
318}
content for a wl_surface
std::function< void(uint32_t)> & on_done()
done event
the compositor singleton
surface_t create_surface()
create new surface
Represents a connection to the compositor and acts as a proxy to the display singleton object.
int dispatch() const
Process incoming events.
registry_t get_registry()
get global registry object
int roundtrip() const
Block until all pending request are processed by the server.
Native EGL window.
Definition: wayland-egl.hpp:43
std::function< void(uint32_t, uint32_t, uint32_t, keyboard_key_state)> & on_key()
key event
std::function< void(uint32_t, surface_t, double, double)> & on_enter()
enter event
std::function< void(uint32_t, uint32_t, uint32_t, pointer_button_state)> & on_button()
pointer button event
void set_cursor(uint32_t serial, surface_t const &surface, int32_t hotspot_x, int32_t hotspot_y)
set the pointer surface
global registry object
std::function< void(uint32_t, std::string, uint32_t)> & on_global()
announce global object
proxy_t bind(uint32_t name, proxy_t &interface, uint32_t version)
bind an object to the display
group of input devices
keyboard_t get_keyboard()
return keyboard object
pointer_t get_pointer()
return pointer object
std::function< void(seat_capability)> & on_capabilities()
seat capabilities changed
desktop-style metadata interface
void move(seat_t const &seat, uint32_t serial)
start an interactive move
void set_toplevel()
make the surface a toplevel surface
std::function< void(uint32_t)> & on_ping()
ping client
void pong(uint32_t serial)
respond to a ping event
void set_title(std::string const &title)
set surface title
create desktop-style surfaces
shell_surface_t get_shell_surface(surface_t const &surface)
create a shell surface from a surface
shared memory support
void commit()
commit pending surface state
void attach(buffer_t const &buffer, int32_t x, int32_t y)
set the surface contents
callback_t frame()
request a frame throttling hint
void damage(int32_t x, int32_t y, int32_t width, int32_t height)
mark part of the surface damaged
desktop user interface surface base interface
std::function< void(uint32_t)> & on_configure()
suggest a surface change
xdg_toplevel_t get_toplevel()
assign the xdg_toplevel surface role
void ack_configure(uint32_t serial)
ack a configure event
void set_title(std::string const &title)
set surface title
std::function< void()> & on_close()
surface wants to be closed
void move(seat_t const &seat, uint32_t serial)
start an interactive move
create desktop-style surfaces
std::function< void(uint32_t)> & on_ping()
check if the client is alive
void pong(uint32_t serial)
respond to a ping event
xdg_surface_t get_xdg_surface(surface_t const &surface)
create a shell surface from a surface