Wayland++ 1.0.0
C++ Bindings for Wayland
Loading...
Searching...
No Matches
shm.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
29
30#include <stdexcept>
31#include <iostream>
32#include <array>
33#include <memory>
34#include <sstream>
35#include <ctime>
36#include <algorithm>
37#include <random>
38
39#include <wayland-client.hpp>
40#include <wayland-client-protocol-extra.hpp>
41#include <linux/input.h>
42#include <wayland-cursor.hpp>
43
44#include <sys/mman.h>
45#include <sys/stat.h>
46#include <fcntl.h>
47#include <unistd.h>
48
49using namespace wayland;
50
51// helper to create a std::function out of a member function and an object
52template <typename R, typename T, typename... Args>
53std::function<R(Args...)> bind_mem_fn(R(T::* func)(Args...), T *t)
54{
55 return [func, t] (Args... args)
56 {
57 return (t->*func)(args...);
58 };
59}
60
61// shared memory helper class
62class shared_mem_t
63{
64private:
65 std::string name;
66 int fd = 0;
67 size_t len = 0;
68 void *mem = nullptr;
69
70public:
71 shared_mem_t() = default;
72 shared_mem_t(const shared_mem_t&) = delete;
73 shared_mem_t(shared_mem_t&&) noexcept = delete;
74 shared_mem_t& operator=(const shared_mem_t&) = delete;
75 shared_mem_t& operator=(shared_mem_t&&) noexcept = delete;
76
77 shared_mem_t(size_t size)
78 : len(size)
79 {
80 // create random filename
81 std::stringstream ss;
82 std::random_device device;
83 std::default_random_engine engine(device());
84 std::uniform_int_distribution<unsigned int> distribution(0, std::numeric_limits<unsigned int>::max());
85 ss << distribution(engine);
86 name = ss.str();
87
88 // open shared memory file
89 fd = memfd_create(name.c_str(), 0);
90 if(fd < 0)
91 throw std::runtime_error("shm_open failed.");
92
93 // set size
94 if(ftruncate(fd, size) < 0)
95 throw std::runtime_error("ftruncate failed.");
96
97 // map memory
98 mem = mmap(nullptr, len, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
99 if(mem == MAP_FAILED) // NOLINT
100 throw std::runtime_error("mmap failed.");
101 }
102
103 ~shared_mem_t() noexcept
104 {
105 if(fd)
106 {
107 if(munmap(mem, len) < 0)
108 std::cerr << "munmap failed.";
109 if(close(fd) < 0)
110 std::cerr << "close failed.";
111 if(shm_unlink(name.c_str()) < 0)
112 std::cerr << "shm_unlink failed";
113 }
114 }
115
116 int get_fd() const
117 {
118 return fd;
119 }
120
121 void *get_mem()
122 {
123 return mem;
124 }
125};
126
127// example Wayland client
128class example
129{
130private:
131 // global objects
132 display_t display;
133 registry_t registry;
134 compositor_t compositor;
135 shell_t shell;
136 xdg_wm_base_t xdg_wm_base;
137 seat_t seat;
138 shm_t shm;
139
140 // local objects
141 surface_t surface;
142 shell_surface_t shell_surface;
143 xdg_surface_t xdg_surface;
144 xdg_toplevel_t xdg_toplevel;
145 pointer_t pointer;
146 keyboard_t keyboard;
147 callback_t frame_cb;
148 cursor_image_t cursor_image;
149 buffer_t cursor_buffer;
150 surface_t cursor_surface;
151
152 std::shared_ptr<shared_mem_t> shared_mem;
153 std::array<buffer_t, 2> buffer;
154 int cur_buf;
155
156 bool running;
157 bool has_pointer;
158 bool has_keyboard;
159
160 void draw(uint32_t serial = 0)
161 {
162 float h = static_cast<float>((serial >> 4) & 0xFF)/255.0F;
163 float s = 1;
164 float v = 1;
165
166 int hi = static_cast<int>(h*6);
167 float f = h*6 - static_cast<float>(hi);
168 float p = v*(1-s);
169 float q = v*(1-s*f);
170 float t = v*(1-s*(1-f));
171 float r = 0;
172 float g = 0;
173 float b = 0;
174
175 switch(hi)
176 {
177 case 1:
178 r = q; g = v; b = p;
179 break;
180 case 2:
181 r = p; g = v; b = t;
182 break;
183 case 3:
184 r = p; g = q; b = v;
185 break;
186 case 4:
187 r = t; g = p; b = v;
188 break;
189 case 5:
190 r = v; g = p; b = q;
191 break;
192 default: // 0,6
193 r = v; g = t; b = p;
194 break;
195 }
196
197 // draw stuff
198 uint32_t pixel = (0x80 << 24)
199 | (static_cast<uint32_t>(r * 255.0) << 16)
200 | (static_cast<uint32_t>(g * 255.0) << 8)
201 | static_cast<uint32_t>(b * 255.0);
202
203 std::fill_n(static_cast<uint32_t*>(shared_mem->get_mem())+cur_buf*320*240, 320*240, pixel);
204 surface.attach(buffer.at(cur_buf), 0, 0);
205 surface.damage(0, 0, 320, 240);
206 if(!cur_buf)
207 cur_buf = 1;
208 else
209 cur_buf = 0;
210
211 // schedule next draw
212 frame_cb = surface.frame();
213 frame_cb.on_done() = bind_mem_fn(&example::draw, this);
214 surface.commit();
215 }
216
217public:
218 example(const example&) = delete;
219 example(example&&) noexcept = delete;
220 ~example() noexcept = default;
221 example& operator=(const example&) = delete;
222 example& operator=(example&&) noexcept = delete;
223
224 example()
225 {
226 // retrieve global objects
227 registry = display.get_registry();
228 registry.on_global() = [&] (uint32_t name, const std::string& interface, uint32_t version)
229 {
230 if(interface == compositor_t::interface_name)
231 registry.bind(name, compositor, version);
232 else if(interface == shell_t::interface_name)
233 registry.bind(name, shell, version);
234 else if(interface == xdg_wm_base_t::interface_name)
235 registry.bind(name, xdg_wm_base, version);
236 else if(interface == seat_t::interface_name)
237 registry.bind(name, seat, version);
238 else if(interface == shm_t::interface_name)
239 registry.bind(name, shm, version);
240 };
241 display.roundtrip();
242
243 seat.on_capabilities() = [&] (const seat_capability& capability)
244 {
245 has_keyboard = capability & seat_capability::keyboard;
246 has_pointer = capability & seat_capability::pointer;
247 };
248
249 // create a surface
250 surface = compositor.create_surface();
251
252 // create a shell surface
253 if(xdg_wm_base)
254 {
255 xdg_wm_base.on_ping() = [&] (uint32_t serial) { xdg_wm_base.pong(serial); };
256 xdg_surface = xdg_wm_base.get_xdg_surface(surface);
257 xdg_surface.on_configure() = [&] (uint32_t serial) { xdg_surface.ack_configure(serial); };
258 xdg_toplevel = xdg_surface.get_toplevel();
259 xdg_toplevel.set_title("Window");
260 xdg_toplevel.on_close() = [&] () { running = false; };
261 }
262 else
263 {
264 shell_surface = shell.get_shell_surface(surface);
265 shell_surface.on_ping() = [&] (uint32_t serial) { shell_surface.pong(serial); };
266 shell_surface.set_title("Window");
267 shell_surface.set_toplevel();
268 }
269 surface.commit();
270
271 display.roundtrip();
272
273 // Get input devices
274 if(!has_keyboard)
275 throw std::runtime_error("No keyboard found.");
276 if(!has_pointer)
277 throw std::runtime_error("No pointer found.");
278
279 pointer = seat.get_pointer();
280 keyboard = seat.get_keyboard();
281
282 // create shared memory
283 shared_mem = std::make_shared<shared_mem_t>(2*320*240*4);
284 auto pool = shm.create_pool(shared_mem->get_fd(), 2*320*240*4);
285 for(unsigned int c = 0; c < 2; c++)
286 buffer.at(c) = pool.create_buffer(c*320*240*4, 320, 240, 320*4, shm_format::argb8888);
287 cur_buf = 0;
288
289 // load cursor theme
290 cursor_theme_t cursor_theme = cursor_theme_t("default", 16, shm);
291 cursor_t cursor = cursor_theme.get_cursor("cross");
292 cursor_image = cursor.image(0);
293 cursor_buffer = cursor_image.get_buffer();
294
295 // create cursor surface
296 cursor_surface = compositor.create_surface();
297
298 // draw cursor
299 pointer.on_enter() = [&] (uint32_t serial, const surface_t& /*unused*/, int32_t /*unused*/, int32_t /*unused*/)
300 {
301 cursor_surface.attach(cursor_buffer, 0, 0);
302 cursor_surface.damage(0, 0, cursor_image.width(), cursor_image.height());
303 cursor_surface.commit();
304 pointer.set_cursor(serial, cursor_surface, 0, 0);
305 };
306
307 // window movement
308 pointer.on_button() = [&] (uint32_t serial, uint32_t /*unused*/, uint32_t button, pointer_button_state state)
309 {
310 if(button == BTN_LEFT && state == pointer_button_state::pressed)
311 {
312 if(xdg_toplevel)
313 xdg_toplevel.move(seat, serial);
314 else
315 shell_surface.move(seat, serial);
316 }
317 };
318
319 // press 'q' to exit
320 keyboard.on_key() = [&] (uint32_t /*unused*/, uint32_t /*unused*/, uint32_t key, keyboard_key_state state)
321 {
322 if(key == KEY_Q && state == keyboard_key_state::pressed)
323 running = false;
324 };
325
326 // draw stuff
327 draw();
328 }
329
330 void run()
331 {
332 // event loop
333 running = true;
334 while(running)
335 display.dispatch();
336 }
337};
338
339int main()
340{
341 example e;
342 e.run();
343 return 0;
344}
static const wayland::detail::bitfield< 3, 12 > keyboard
the seat has one or more keyboards
static const wayland::detail::bitfield< 3, 12 > pointer
the seat has pointer devices