Wayland++  0.2.6
C++ Bindings for Wayland
shm.cpp
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 
30 #include <stdexcept>
31 #include <iostream>
32 #include <array>
33 #include <memory>
34 #include <sstream>
35 #include <ctime>
36 #include <algorithm>
37 
38 #include <wayland-client.hpp>
39 #include <wayland-client-protocol-extra.hpp>
40 #include <linux/input.h>
41 #include <wayland-cursor.hpp>
42 
43 #include <sys/mman.h>
44 #include <sys/stat.h>
45 #include <fcntl.h>
46 #include <unistd.h>
47 
48 using namespace wayland;
49 
50 // helper to create a std::function out of a member function and an object
51 template <typename R, typename T, typename... Args>
52 std::function<R(Args...)> bind_mem_fn(R(T::* func)(Args...), T *t)
53 {
54  return [func, t] (Args... args)
55  {
56  return (t->*func)(args...);
57  };
58 }
59 
60 // shared memory helper class
61 class shared_mem_t
62 {
63 private:
64  std::string name;
65  int fd = 0;
66  size_t len = 0;
67  void *mem = nullptr;
68 
69 public:
70  shared_mem_t()
71  {
72  }
73 
74  shared_mem_t(size_t size)
75  : len(size)
76  {
77  // Very simple shared memory wrapper - do not use this in production code!
78  // The generated memory regions are visible in the file system and can be
79  // stolen by other running processes.
80  // Linux code should use memfd_create when possible (ommited here for
81  // simplicity).
82 
83  // create random filename
84  std::stringstream ss;
85  std::srand(std::time(nullptr));
86  ss << '/' << std::rand();
87  name = ss.str();
88 
89  // open shared memory file
90  fd = shm_open(name.c_str(), O_RDWR | O_CREAT | O_EXCL, 0600);
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)
101  throw std::runtime_error("mmap failed.");
102  }
103 
104  ~shared_mem_t() noexcept(false)
105  {
106  if(fd)
107  {
108  if(munmap(mem, len) < 0)
109  throw std::runtime_error("munmap failed.");
110  if(close(fd) < 0)
111  throw std::runtime_error("close failed.");
112  if(shm_unlink(name.c_str()) < 0)
113  throw std::runtime_error("shm_unlink failed");
114  }
115  }
116 
117  int get_fd()
118  {
119  return fd;
120  }
121 
122  void *get_mem()
123  {
124  return mem;
125  }
126 };
127 
128 // example Wayland client
129 class example
130 {
131 private:
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 = ((serial >> 4) & 0xFF)/255.0;
164  float s = 1, v = 1;
165 
166  int hi = static_cast<int>(h*6);
167  float f = h*6 - 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, g, b;
172 
173  switch(hi)
174  {
175  case 1:
176  r = q; g = v; b = p;
177  break;
178  case 2:
179  r = p; g = v; b = t;
180  break;
181  case 3:
182  r = p; g = q; b = v;
183  break;
184  case 4:
185  r = t; g = p; b = v;
186  break;
187  case 5:
188  r = v; g = p; b = q;
189  break;
190  default: // 0,6
191  r = v; g = t; b = p;
192  break;
193  }
194 
195  // draw stuff
196  uint32_t pixel = (0x80 << 24)
197  | (static_cast<uint32_t>(r * 255.0) << 16)
198  | (static_cast<uint32_t>(g * 255.0) << 8)
199  | static_cast<uint32_t>(b * 255.0);
200 
201  std::fill_n(static_cast<uint32_t*>(shared_mem->get_mem())+cur_buf*320*240, 320*240, pixel);
202  surface.attach(buffer.at(cur_buf), 0, 0);
203  surface.damage(0, 0, 320, 240);
204  if(!cur_buf)
205  cur_buf = 1;
206  else
207  cur_buf = 0;
208 
209  // schedule next draw
210  frame_cb = surface.frame();
211  frame_cb.on_done() = bind_mem_fn(&example::draw, this);
212  surface.commit();
213  }
214 
215 public:
216  example()
217  {
218  // retrieve global objects
219  registry = display.get_registry();
220  registry.on_global() = [&] (uint32_t name, std::string interface, uint32_t version)
221  {
222  if(interface == compositor_t::interface_name)
223  registry.bind(name, compositor, version);
224  else if(interface == shell_t::interface_name)
225  registry.bind(name, shell, version);
226  else if(interface == xdg_wm_base_t::interface_name)
227  registry.bind(name, xdg_wm_base, version);
228  else if(interface == seat_t::interface_name)
229  registry.bind(name, seat, version);
230  else if(interface == shm_t::interface_name)
231  registry.bind(name, shm, version);
232  };
233  display.roundtrip();
234 
235  seat.on_capabilities() = [&] (seat_capability capability)
236  {
237  has_keyboard = capability & seat_capability::keyboard;
238  has_pointer = capability & seat_capability::pointer;
239  };
240 
241  // create a surface
242  surface = compositor.create_surface();
243 
244  // create a shell surface
245  if(xdg_wm_base)
246  {
247  xdg_wm_base.on_ping() = [&] (uint32_t serial) { xdg_wm_base.pong(serial); };
248  xdg_surface = xdg_wm_base.get_xdg_surface(surface);
249  xdg_surface.on_configure() = [&] (uint32_t serial) { xdg_surface.ack_configure(serial); };
250  xdg_toplevel = xdg_surface.get_toplevel();
251  xdg_toplevel.set_title("Window");
252  xdg_toplevel.on_close() = [&] () { running = false; };
253  }
254  else
255  {
256  shell_surface = shell.get_shell_surface(surface);
257  shell_surface.on_ping() = [&] (uint32_t serial) { shell_surface.pong(serial); };
258  shell_surface.set_title("Window");
259  shell_surface.set_toplevel();
260  }
261  surface.commit();
262 
263  display.roundtrip();
264 
265  // Get input devices
266  if(!has_keyboard)
267  throw std::runtime_error("No keyboard found.");
268  if(!has_pointer)
269  throw std::runtime_error("No pointer found.");
270 
271  pointer = seat.get_pointer();
272  keyboard = seat.get_keyboard();
273 
274  // create shared memory
275  shared_mem = std::make_shared<shared_mem_t>(2*320*240*4);
276  auto pool = shm.create_pool(shared_mem->get_fd(), 2*320*240*4);
277  for(unsigned int c = 0; c < 2; c++)
278  buffer.at(c) = pool.create_buffer(c*320*240*4, 320, 240, 320*4, shm_format::argb8888);
279  cur_buf = 0;
280 
281  // load cursor theme
282  cursor_theme_t cursor_theme = cursor_theme_t("default", 16, shm);
283  cursor_t cursor = cursor_theme.get_cursor("cross");
284  cursor_image = cursor.image(0);
285  cursor_buffer = cursor_image.get_buffer();
286 
287  // create cursor surface
288  cursor_surface = compositor.create_surface();
289 
290  // draw cursor
291  pointer.on_enter() = [&] (uint32_t serial, surface_t, int32_t, int32_t)
292  {
293  cursor_surface.attach(cursor_buffer, 0, 0);
294  cursor_surface.damage(0, 0, cursor_image.width(), cursor_image.height());
295  cursor_surface.commit();
296  pointer.set_cursor(serial, cursor_surface, 0, 0);
297  };
298 
299  // window movement
300  pointer.on_button() = [&] (uint32_t serial, uint32_t time, uint32_t button, pointer_button_state state)
301  {
302  if(button == BTN_LEFT && state == pointer_button_state::pressed)
303  {
304  if(xdg_toplevel)
305  xdg_toplevel.move(seat, serial);
306  else
307  shell_surface.move(seat, serial);
308  }
309  };
310 
311  // press 'q' to exit
312  keyboard.on_key() = [&] (uint32_t, uint32_t, uint32_t key, keyboard_key_state state)
313  {
314  if(key == KEY_Q && state == keyboard_key_state::pressed)
315  running = false;
316  };
317 
318  // draw stuff
319  draw();
320  }
321 
322  ~example()
323  {
324  }
325 
326  void run()
327  {
328  // event loop
329  running = true;
330  while(running)
331  display.dispatch();
332  }
333 };
334 
335 int main()
336 {
337  example e;
338  e.run();
339  return 0;
340 }
wayland::xdg_wm_base_t::pong
void pong(uint32_t serial)
respond to a ping event
Definition: wayland-client-protocol-extra.cpp:968
wayland::surface_t::frame
callback_t frame()
request a frame throttling hint
Definition: wayland-client-protocol.cpp:2633
wayland::display_t
Represents a connection to the compositor and acts as a proxy to the display singleton object.
Definition: wayland-client.hpp:463
wayland::xdg_wm_base_t::get_xdg_surface
xdg_surface_t get_xdg_surface(surface_t surface)
create a shell surface from a surface
Definition: wayland-client-protocol-extra.cpp:962
wayland::pointer_t::on_button
std::function< void(uint32_t, uint32_t, uint32_t, pointer_button_state)> & on_button()
pointer button event
Definition: wayland-client-protocol.cpp:2890
wayland::display_t::dispatch
int dispatch()
Process incoming events.
wayland::display_t::get_registry
registry_t get_registry()
get global registry object
wayland::pointer_t::on_enter
std::function< void(uint32_t, surface_t, double, double)> & on_enter()
enter event
Definition: wayland-client-protocol.cpp:2875
wayland::shm_t::create_pool
shm_pool_t create_pool(int fd, int32_t size)
create a shm pool
Definition: wayland-client-protocol.cpp:1840
wayland::shell_t
create desktop-style surfaces
Definition: wayland-client-protocol.hpp:1419
wayland::shell_surface_t::pong
void pong(uint32_t serial)
respond to a ping event
Definition: wayland-client-protocol.cpp:2476
wayland::pointer_t::set_cursor
void set_cursor(uint32_t serial, surface_t surface, int32_t hotspot_x, int32_t hotspot_y)
set the pointer surface
Definition: wayland-client-protocol.cpp:2861
wayland::callback_t::on_done
std::function< void(uint32_t)> & on_done()
done event
Definition: wayland-client-protocol.cpp:1641
wayland::xdg_surface_t
desktop user interface surface base interface
Definition: wayland-client-protocol-extra.hpp:959
wayland::buffer_t
content for a wl_surface
Definition: wayland-client-protocol.hpp:602
wayland::shm_t
shared memory support
Definition: wayland-client-protocol.hpp:391
wayland::surface_t::commit
void commit()
commit pending surface state
Definition: wayland-client-protocol.cpp:2649
wayland::keyboard_t::on_key
std::function< void(uint32_t, uint32_t, uint32_t, keyboard_key_state)> & on_key()
key event
Definition: wayland-client-protocol.cpp:3033
wayland::shell_surface_t::set_title
void set_title(std::string title)
set surface title
Definition: wayland-client-protocol.cpp:2516
wayland::surface_t::attach
void attach(buffer_t buffer, int32_t x, int32_t y)
set the surface contents
Definition: wayland-client-protocol.cpp:2623
wayland::surface_t
an onscreen surface
Definition: wayland-client-protocol.hpp:1898
wayland::seat_capability::pointer
static const detail::bitfield< 3, 12 > pointer
the seat has pointer devices
Definition: wayland-client-protocol.hpp:2485
wayland::xdg_surface_t::on_configure
std::function< void(uint32_t)> & on_configure()
suggest a surface change
Definition: wayland-client-protocol-extra.cpp:1161
wayland::surface_t::damage
void damage(int32_t x, int32_t y, int32_t width, int32_t height)
mark part of the surface damaged
Definition: wayland-client-protocol.cpp:2628
wayland::seat_capability
seat capability bitmask
Definition: wayland-client-protocol.hpp:2478
wayland::xdg_toplevel_t::on_close
std::function< void()> & on_close()
surface wants to be closed
Definition: wayland-client-protocol-extra.cpp:1300
wayland::shell_surface_t
desktop-style metadata interface
Definition: wayland-client-protocol.hpp:1485
wayland::xdg_toplevel_t::set_title
void set_title(std::string title)
set surface title
Definition: wayland-client-protocol-extra.cpp:1235
wayland::xdg_surface_t::get_toplevel
xdg_toplevel_t get_toplevel()
assign the xdg_toplevel surface role
Definition: wayland-client-protocol-extra.cpp:1139
wayland::shell_surface_t::move
void move(seat_t seat, uint32_t serial)
start an interactive move
Definition: wayland-client-protocol.cpp:2481
wayland::xdg_toplevel_t::move
void move(seat_t seat, uint32_t serial)
start an interactive move
Definition: wayland-client-protocol-extra.cpp:1250
wayland::xdg_toplevel_t
toplevel surface
Definition: wayland-client-protocol-extra.hpp:1143
wayland::registry_t
global registry object
Definition: wayland-client-protocol.hpp:130
wayland::registry_t::bind
proxy_t bind(uint32_t name, proxy_t &interface, uint32_t version)
bind an object to the display
Definition: wayland-client-protocol.cpp:1560
wayland::seat_t
group of input devices
Definition: wayland-client-protocol.hpp:2334
wayland::compositor_t::create_surface
surface_t create_surface()
create new surface
Definition: wayland-client-protocol.cpp:1707
wayland::shell_t::get_shell_surface
shell_surface_t get_shell_surface(surface_t surface)
create a shell surface from a surface
Definition: wayland-client-protocol.cpp:2415
wayland::display_t::roundtrip
int roundtrip()
Block until all pending request are processed by the server.
wayland::shell_surface_t::on_ping
std::function< void(uint32_t)> & on_ping()
ping client
Definition: wayland-client-protocol.cpp:2526
wayland::seat_t::get_pointer
pointer_t get_pointer()
return pointer object
Definition: wayland-client-protocol.cpp:2756
wayland::compositor_t
the compositor singleton
Definition: wayland-client-protocol.hpp:251
wayland::xdg_surface_t::ack_configure
void ack_configure(uint32_t serial)
ack a configure event
Definition: wayland-client-protocol-extra.cpp:1156
wayland::xdg_wm_base_t
create desktop-style surfaces
Definition: wayland-client-protocol-extra.hpp:564
wayland::xdg_wm_base_t::on_ping
std::function< void(uint32_t)> & on_ping()
check if the client is alive
Definition: wayland-client-protocol-extra.cpp:973
wayland::seat_t::get_keyboard
keyboard_t get_keyboard()
return keyboard object
Definition: wayland-client-protocol.cpp:2762
wayland::callback_t
callback object
Definition: wayland-client-protocol.hpp:210
wayland::shell_surface_t::set_toplevel
void set_toplevel()
make the surface a toplevel surface
Definition: wayland-client-protocol.cpp:2491
wayland::registry_t::on_global
std::function< void(uint32_t, std::string, uint32_t)> & on_global()
announce global object
Definition: wayland-client-protocol.cpp:1567
wayland::seat_t::on_capabilities
std::function< void(seat_capability)> & on_capabilities()
seat capabilities changed
Definition: wayland-client-protocol.cpp:2783
wayland::seat_capability::keyboard
static const detail::bitfield< 3, 12 > keyboard
the seat has one or more keyboards
Definition: wayland-client-protocol.hpp:2487
wayland::keyboard_t
keyboard input device
Definition: wayland-client-protocol.hpp:2896
wayland-client.hpp
wayland::pointer_t
pointer input device
Definition: wayland-client-protocol.hpp:2505