This is an example of how to use the Wayland C++ bindings with SHM.
#include <stdexcept>
#include <iostream>
#include <array>
#include <memory>
#include <sstream>
#include <ctime>
#include <algorithm>
#include <random>
#include <wayland-client-protocol-extra.hpp>
#include <linux/input.h>
#include <wayland-cursor.hpp>
#include <sys/mman.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
using namespace wayland;
template <typename R, typename T, typename... Args>
std::function<R(Args...)> bind_mem_fn(R(T::* func)(Args...), T *t)
{
return [func, t] (Args... args)
{
return (t->*func)(args...);
};
}
class shared_mem_t
{
private:
std::string name;
int fd = 0;
size_t len = 0;
void *mem = nullptr;
public:
shared_mem_t() = default;
shared_mem_t(const shared_mem_t&) = delete;
shared_mem_t(shared_mem_t&&) noexcept = delete;
shared_mem_t& operator=(const shared_mem_t&) = delete;
shared_mem_t& operator=(shared_mem_t&&) noexcept = delete;
shared_mem_t(size_t size)
: len(size)
{
std::stringstream ss;
std::random_device device;
std::default_random_engine engine(device());
std::uniform_int_distribution<unsigned int> distribution(0, std::numeric_limits<unsigned int>::max());
ss << distribution(engine);
name = ss.str();
fd = memfd_create(name.c_str(), 0);
if(fd < 0)
throw std::runtime_error("shm_open failed.");
if(ftruncate(fd, size) < 0)
throw std::runtime_error("ftruncate failed.");
mem = mmap(nullptr, len, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
if(mem == MAP_FAILED)
throw std::runtime_error("mmap failed.");
}
~shared_mem_t() noexcept
{
if(fd)
{
if(munmap(mem, len) < 0)
std::cerr << "munmap failed.";
if(close(fd) < 0)
std::cerr << "close failed.";
if(shm_unlink(name.c_str()) < 0)
std::cerr << "shm_unlink failed";
}
}
int get_fd() const
{
return fd;
}
void *get_mem()
{
return mem;
}
};
class example
{
private:
cursor_image_t cursor_image;
std::shared_ptr<shared_mem_t> shared_mem;
std::array<buffer_t, 2> buffer;
int cur_buf;
bool running;
bool has_pointer;
bool has_keyboard;
void draw(uint32_t serial = 0)
{
float h = static_cast<float>((serial >> 4) & 0xFF)/255.0F;
float s = 1;
float v = 1;
int hi = static_cast<int>(h*6);
float f = h*6 - static_cast<float>(hi);
float p = v*(1-s);
float q = v*(1-s*f);
float t = v*(1-s*(1-f));
float r = 0;
float g = 0;
float b = 0;
switch(hi)
{
case 1:
r = q; g = v; b = p;
break;
case 2:
r = p; g = v; b = t;
break;
case 3:
r = p; g = q; b = v;
break;
case 4:
r = t; g = p; b = v;
break;
case 5:
r = v; g = p; b = q;
break;
default:
r = v; g = t; b = p;
break;
}
uint32_t pixel = (0x80 << 24)
| (static_cast<uint32_t>(r * 255.0) << 16)
| (static_cast<uint32_t>(g * 255.0) << 8)
| static_cast<uint32_t>(b * 255.0);
std::fill_n(static_cast<uint32_t*>(shared_mem->get_mem())+cur_buf*320*240, 320*240, pixel);
surface.
attach(buffer.at(cur_buf), 0, 0);
surface.
damage(0, 0, 320, 240);
if(!cur_buf)
cur_buf = 1;
else
cur_buf = 0;
frame_cb = surface.
frame();
frame_cb.
on_done() = bind_mem_fn(&example::draw,
this);
}
public:
example(const example&) = delete;
example(example&&) noexcept = delete;
~example() noexcept = default;
example& operator=(const example&) = delete;
example& operator=(example&&) noexcept = delete;
example()
{
registry.
on_global() = [&] (uint32_t name,
const std::string& interface, uint32_t version)
{
if(interface == compositor_t::interface_name)
registry.
bind(name, compositor, version);
else if(interface == shell_t::interface_name)
registry.
bind(name, shell, version);
else if(interface == xdg_wm_base_t::interface_name)
registry.
bind(name, xdg_wm_base, version);
else if(interface == seat_t::interface_name)
registry.
bind(name, seat, version);
else if(interface == shm_t::interface_name)
registry.
bind(name, shm, version);
};
{
has_keyboard = capability & seat_capability::keyboard;
has_pointer = capability & seat_capability::pointer;
};
if(xdg_wm_base)
{
xdg_wm_base.
on_ping() = [&] (uint32_t serial) { xdg_wm_base.
pong(serial); };
xdg_toplevel.
on_close() = [&] () { running =
false; };
}
else
{
shell_surface.
on_ping() = [&] (uint32_t serial) { shell_surface.
pong(serial); };
}
if(!has_keyboard)
throw std::runtime_error("No keyboard found.");
if(!has_pointer)
throw std::runtime_error("No pointer found.");
shared_mem = std::make_shared<shared_mem_t>(2*320*240*4);
auto pool = shm.
create_pool(shared_mem->get_fd(), 2*320*240*4);
for(unsigned int c = 0; c < 2; c++)
buffer.at(c) = pool.create_buffer(c*320*240*4, 320, 240, 320*4, shm_format::argb8888);
cur_buf = 0;
cursor_theme_t cursor_theme = cursor_theme_t("default", 16, shm);
cursor_t cursor = cursor_theme.
get_cursor(
"cross");
cursor_image = cursor.
image(0);
cursor_buffer = cursor_image.
get_buffer();
{
cursor_surface.
attach(cursor_buffer, 0, 0);
cursor_surface.
damage(0, 0, cursor_image.
width(), cursor_image.
height());
};
pointer.
on_button() = [&] (uint32_t serial, uint32_t , uint32_t button, pointer_button_state state)
{
if(button == BTN_LEFT && state == pointer_button_state::pressed)
{
if(xdg_toplevel)
xdg_toplevel.
move(seat, serial);
else
shell_surface.
move(seat, serial);
}
};
keyboard.
on_key() = [&] (uint32_t , uint32_t , uint32_t key, keyboard_key_state state)
{
if(key == KEY_Q && state == keyboard_key_state::pressed)
running = false;
};
draw();
}
void run()
{
running = true;
while(running)
}
};
int main()
{
example e;
return 0;
}
std::function< void(uint32_t)> & on_done()
done event
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
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
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