Wayland++  0.2.10
C++ Bindings for Wayland
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 
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 
49 using namespace wayland;
50 
51 // helper to create a std::function out of a member function and an object
52 template <typename R, typename T, typename... Args>
53 std::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
62 class shared_mem_t
63 {
64 private:
65  std::string name;
66  int fd = 0;
67  size_t len = 0;
68  void *mem = nullptr;
69 
70 public:
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
128 class example
129 {
130 private:
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 
217 public:
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 
339 int main()
340 {
341  example e;
342  e.run();
343  return 0;
344 }
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()
Process incoming events.
int roundtrip()
Block until all pending request are processed by the server.
registry_t get_registry()
get global registry object
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
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 detail::bitfield< 3, 12 > keyboard
the seat has one or more keyboards
static const detail::bitfield< 3, 12 > pointer
the seat has pointer devices