1 /*
2 * Copyright (c) 2014-2019, 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
26 /** \example egl.cpp
27 * This is an example of how to use the Wayland C++ bindings with EGL and OpenGL.
28 */
29
30 #include <stdexcept>
31 #include <iostream>
32 #include <array>
33 #include <wayland-client.hpp>
34 #include <wayland-client-protocol-extra.hpp>
35 #include <wayland-egl.hpp>
36 #include <GL/gl.h>
37 #include <linux/input.h>
38 #include <wayland-cursor.hpp>
39
40 using namespace wayland;
41
42 // helper to create a std::function out of a member function and an object
43 template <typename R, typename T, typename... Args>
bind_mem_fn(R (T::* func)(Args...),T * t)44 std::function<R(Args...)> bind_mem_fn(R(T::* func)(Args...), T *t)
45 {
46 return [func, t] (Args... args)
47 {
48 return (t->*func)(args...);
49 };
50 }
51
52 // example Wayland client
53 class example
54 {
55 private:
56 // global objects
57 display_t display;
58 registry_t registry;
59 compositor_t compositor;
60 shell_t shell;
61 xdg_wm_base_t xdg_wm_base;
62 seat_t seat;
63 shm_t shm;
64
65 // local objects
66 surface_t surface;
67 shell_surface_t shell_surface;
68 xdg_surface_t xdg_surface;
69 xdg_toplevel_t xdg_toplevel;
70 pointer_t pointer;
71 keyboard_t keyboard;
72 callback_t frame_cb;
73 cursor_image_t cursor_image;
74 buffer_t cursor_buffer;
75 surface_t cursor_surface;
76
77 // EGL
78 egl_window_t egl_window;
79 EGLDisplay egldisplay = nullptr;
80 EGLSurface eglsurface = nullptr;
81 EGLContext eglcontext = nullptr;
82
83 bool running;
84 bool has_pointer;
85 bool has_keyboard;
86
init_egl()87 void init_egl()
88 {
89 egldisplay = eglGetDisplay(display);
90 if(egldisplay == EGL_NO_DISPLAY)
91 throw std::runtime_error("eglGetDisplay");
92
93 EGLint major = 0;
94 EGLint minor = 0;
95 if(eglInitialize(egldisplay, &major, &minor) == EGL_FALSE)
96 throw std::runtime_error("eglInitialize");
97 if(!((major == 1 && minor >= 4) || major >= 2))
98 throw std::runtime_error("EGL version too old");
99
100 if(eglBindAPI(EGL_OPENGL_API) == EGL_FALSE)
101 throw std::runtime_error("eglBindAPI");
102
103 std::array<EGLint, 13> config_attribs = {{
104 EGL_SURFACE_TYPE, EGL_WINDOW_BIT,
105 EGL_RED_SIZE, 8,
106 EGL_GREEN_SIZE, 8,
107 EGL_BLUE_SIZE, 8,
108 EGL_ALPHA_SIZE, 8,
109 EGL_RENDERABLE_TYPE, EGL_OPENGL_BIT,
110 EGL_NONE
111 }};
112
113 EGLConfig config = nullptr;
114 EGLint num = 0;
115 if(eglChooseConfig(egldisplay, config_attribs.data(), &config, 1, &num) == EGL_FALSE || num == 0)
116 throw std::runtime_error("eglChooseConfig");
117
118 std::array<EGLint, 3> context_attribs = {{
119 EGL_CONTEXT_CLIENT_VERSION, 2,
120 EGL_NONE
121 }};
122
123 eglcontext = eglCreateContext(egldisplay, config, EGL_NO_CONTEXT, context_attribs.data());
124 if(eglcontext == EGL_NO_CONTEXT)
125 throw std::runtime_error("eglCreateContext");
126
127 eglsurface = eglCreateWindowSurface(egldisplay, config, egl_window, nullptr);
128 if(eglsurface == EGL_NO_SURFACE)
129 throw std::runtime_error("eglCreateWindowSurface");
130
131 if(eglMakeCurrent(egldisplay, eglsurface, eglsurface, eglcontext) == EGL_FALSE)
132 throw std::runtime_error("eglMakeCurrent");
133 }
134
draw(uint32_t serial=0)135 void draw(uint32_t serial = 0)
136 {
137 float h = static_cast<float>((serial >> 4) & 0xFF)/255.0F;
138 float s = 1;
139 float v = 1;
140
141 int hi = static_cast<int>(h*6);
142 float f = h*6 - static_cast<float>(hi);
143 float p = v*(1-s);
144 float q = v*(1-s*f);
145 float t = v*(1-s*(1-f));
146 float r = 0;
147 float g = 0;
148 float b = 0;
149
150 switch(hi)
151 {
152 case 1:
153 r = q; g = v; b = p;
154 break;
155 case 2:
156 r = p; g = v; b = t;
157 break;
158 case 3:
159 r = p; g = q; b = v;
160 break;
161 case 4:
162 r = t; g = p; b = v;
163 break;
164 case 5:
165 r = v; g = p; b = q;
166 break;
167 default: // 0,6
168 r = v; g = t; b = p;
169 break;
170 }
171
172 // draw stuff
173 glClearColor(r, g, b, 0.5F);
174 glClear(GL_COLOR_BUFFER_BIT);
175
176 // schedule next draw
177 frame_cb = surface.frame();
178 frame_cb.on_done() = bind_mem_fn(&example::draw, this);
179
180 // swap buffers
181 if(eglSwapBuffers(egldisplay, eglsurface) == EGL_FALSE)
182 throw std::runtime_error("eglSwapBuffers");
183 }
184
185 public:
186 example(const example&) = delete;
187 example(example&&) noexcept = delete;
188 example& operator=(const example&) = delete;
189 example& operator=(example&&) noexcept = delete;
190
example()191 example()
192 {
193 // retrieve global objects
194 registry = display.get_registry();
195 registry.on_global() = [&] (uint32_t name, const std::string& interface, uint32_t version)
196 {
197 if(interface == compositor_t::interface_name)
198 registry.bind(name, compositor, version);
199 else if(interface == shell_t::interface_name)
200 registry.bind(name, shell, version);
201 else if(interface == xdg_wm_base_t::interface_name)
202 registry.bind(name, xdg_wm_base, version);
203 else if(interface == seat_t::interface_name)
204 registry.bind(name, seat, version);
205 else if(interface == shm_t::interface_name)
206 registry.bind(name, shm, version);
207 };
208 display.roundtrip();
209
210 seat.on_capabilities() = [&] (const seat_capability& capability)
211 {
212 has_keyboard = capability & seat_capability::keyboard;
213 has_pointer = capability & seat_capability::pointer;
214 };
215
216 // create a surface
217 surface = compositor.create_surface();
218
219 // create a shell surface
220 if(xdg_wm_base)
221 {
222 xdg_wm_base.on_ping() = [&] (uint32_t serial) { xdg_wm_base.pong(serial); };
223 xdg_surface = xdg_wm_base.get_xdg_surface(surface);
224 xdg_surface.on_configure() = [&] (uint32_t serial) { xdg_surface.ack_configure(serial); };
225 xdg_toplevel = xdg_surface.get_toplevel();
226 xdg_toplevel.set_title("Window");
227 xdg_toplevel.on_close() = [&] () { running = false; };
228 }
229 else
230 {
231 shell_surface = shell.get_shell_surface(surface);
232 shell_surface.on_ping() = [&] (uint32_t serial) { shell_surface.pong(serial); };
233 shell_surface.set_title("Window");
234 shell_surface.set_toplevel();
235 }
236 surface.commit();
237
238 display.roundtrip();
239
240 // Get input devices
241 if(!has_keyboard)
242 throw std::runtime_error("No keyboard found.");
243 if(!has_pointer)
244 throw std::runtime_error("No pointer found.");
245
246 pointer = seat.get_pointer();
247 keyboard = seat.get_keyboard();
248
249 // load cursor theme
250 cursor_theme_t cursor_theme = cursor_theme_t("default", 16, shm);
251 cursor_t cursor = cursor_theme.get_cursor("cross");
252 cursor_image = cursor.image(0);
253 cursor_buffer = cursor_image.get_buffer();
254
255 // create cursor surface
256 cursor_surface = compositor.create_surface();
257
258 // draw cursor
259 pointer.on_enter() = [&] (uint32_t serial, const surface_t& /*unused*/, int32_t /*unused*/, int32_t /*unused*/)
260 {
261 cursor_surface.attach(cursor_buffer, 0, 0);
262 cursor_surface.damage(0, 0, cursor_image.width(), cursor_image.height());
263 cursor_surface.commit();
264 pointer.set_cursor(serial, cursor_surface, 0, 0);
265 };
266
267 // window movement
268 pointer.on_button() = [&] (uint32_t serial, uint32_t /*unused*/, uint32_t button, pointer_button_state state)
269 {
270 if(button == BTN_LEFT && state == pointer_button_state::pressed)
271 {
272 if(xdg_toplevel)
273 xdg_toplevel.move(seat, serial);
274 else
275 shell_surface.move(seat, serial);
276 }
277 };
278
279 // press 'q' to exit
280 keyboard.on_key() = [&] (uint32_t /*unused*/, uint32_t /*unused*/, uint32_t key, keyboard_key_state state)
281 {
282 if(key == KEY_Q && state == keyboard_key_state::pressed)
283 running = false;
284 };
285
286 // intitialize egl
287 egl_window = egl_window_t(surface, 320, 240);
288 init_egl();
289
290 // draw stuff
291 draw();
292 }
293
~example()294 ~example() noexcept
295 {
296 // finialize EGL
297 if(eglDestroyContext(egldisplay, eglcontext) == EGL_FALSE)
298 std::cerr << "eglDestroyContext failed.";
299 if(eglTerminate(egldisplay) == EGL_FALSE)
300 std::cerr << "eglTerminate failed.";
301 }
302
run()303 void run()
304 {
305 // event loop
306 running = true;
307 while(running)
308 display.dispatch();
309 }
310 };
311
main()312 int main()
313 {
314 example e;
315 e.run();
316 return 0;
317 }
318