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