Skip to content

Instantly share code, notes, and snippets.

@elliotpotts
Last active January 17, 2019 16:53
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save elliotpotts/b54be8d5435b86cfd135c3742343bdf5 to your computer and use it in GitHub Desktop.
Save elliotpotts/b54be8d5435b86cfd135c3742343bdf5 to your computer and use it in GitHub Desktop.
#include <cstdint>
#include <cerrno>
#include <string>
#include <forward_list>
#include <utility>
#include <memory>
#include <fmt/format.h>
#include <wayland-client.h>
#include <wayland-client-protocol.h>
#include <wayland-egl.h>
#include <boost/interprocess/shared_memory_object.hpp>
#include <boost/interprocess/mapped_region.hpp>
#include <boost/signals2.hpp>
class unique_any {
struct contents {
virtual ~contents() = default;
};
template<typename T>
struct typed_contents : public contents {
T storage;
template<typename... Args>
typed_contents(Args&&... args) : storage{ std::forward<Args>(args)... } {
}
virtual ~typed_contents() = default;
};
std::unique_ptr<contents> storage;
public:
template<typename T, typename... Args>
unique_any(std::in_place_type_t<T>, Args&&... args) :
storage(std::make_unique<typed_contents<T>>(std::forward<Args>(args)...)) {
}
template<typename T>
T* get() {
auto ptr = dynamic_cast<typed_contents<T>*>(storage.get());
if (ptr) {
return &ptr->storage;
} else {
return nullptr;
}
}
};
using namespace std::string_literals;
constexpr int bytes_per_pixel = 4;
constexpr int win_width = 480;
constexpr int win_height = 360;
namespace wl {
class buffer {
wl_buffer* const hnd;
public:
explicit buffer(wl_buffer*);
buffer(const buffer&) = delete;
explicit operator wl_buffer*() const;
};
}
wl::buffer::buffer(wl_buffer* buffer) : hnd{buffer} {
if (!hnd) {
throw std::runtime_error("Can't create buffer from nullptr");
}
}
wl::buffer::operator wl_buffer*() const {
return hnd;
}
namespace wl {
class surface {
wl_surface* const hnd;
static void dispatch_frame(void* data, wl_callback* callback, std::uint32_t callback_data);
wl_callback* frame_callback = nullptr;
wl_callback_listener frame_listener = { dispatch_frame };
public:
explicit surface(wl_surface*);
surface(const surface&) = delete;
explicit operator wl_surface*() const;
void attach(buffer& buf, std::int32_t x, std::int32_t y);
void commit();
boost::signals2::signal<void(std::uint32_t)> on_frame;
};
};
wl::surface::surface(wl_surface* surface) : hnd(surface) {
if (!hnd) {
throw std::runtime_error("Can't create surface from nullptr!");
}
frame_callback = wl_surface_frame(hnd);
wl_callback_add_listener(frame_callback, &frame_listener, this);
}
void wl::surface::dispatch_frame(void* data, wl_callback* callback, std::uint32_t callback_data) {
auto& surface = *reinterpret_cast<wl::surface*>(data);
surface.on_frame(callback_data);
}
wl::surface::operator wl_surface*() const {
return hnd;
}
void wl::surface::attach(wl::buffer& buf, std::int32_t x, std::int32_t y) {
wl_surface_attach(hnd, static_cast<wl_buffer*>(buf), x, y);
}
void wl::surface::commit() {
wl_surface_commit(hnd);
}
namespace wl {
class compositor {
wl_compositor* const hnd;
public:
explicit compositor(wl_compositor*);
compositor(const compositor&) = delete;
surface make_surface();
};
}
wl::compositor::compositor(wl_compositor* compositor) : hnd(compositor) {
if (!hnd) {
throw std::runtime_error("Can't create compositor from nullptr!");
}
}
wl::surface wl::compositor::make_surface() {
return wl::surface{ wl_compositor_create_surface(hnd) };
}
namespace wl {
class shell_surface {
wl_shell_surface* const hnd;
static void handle_ping(void*, wl_shell_surface*, std::uint32_t);
static void handle_configure(void*, wl_shell_surface*, std::uint32_t, std::int32_t, std::int32_t);
static void handle_popup_done(void*, wl_shell_surface*);
const wl_shell_surface_listener listener = {
handle_ping,
handle_configure,
handle_popup_done
};
public:
explicit shell_surface(wl_shell_surface*);
shell_surface(const shell_surface&) = delete;
void set_toplevel();
};
}
wl::shell_surface::shell_surface(wl_shell_surface* surface) : hnd(surface) {
if (!hnd) {
throw std::runtime_error("Can't create shell_surface from nullptr!\n");
}
wl_shell_surface_add_listener(hnd, &listener, nullptr);
}
void wl::shell_surface::handle_ping(void* data, wl_shell_surface* shell_surface, std::uint32_t serial) {
wl_shell_surface_pong(shell_surface, serial);
fmt::print("Pinged and ponged\n");
}
void wl::shell_surface::handle_configure(void*, wl_shell_surface*, std::uint32_t, std::int32_t, std::int32_t) {
}
void wl::shell_surface::handle_popup_done(void*, wl_shell_surface*) {
}
void wl::shell_surface::set_toplevel() {
wl_shell_surface_set_toplevel(hnd);
}
namespace wl {
class shell {
wl_shell* const hnd;
public:
explicit shell(wl_shell*);
shell(const shell&) = delete;
~shell();
explicit operator const wl_shell*() const;
shell_surface make_surface(const surface&);
};
}
wl::shell::shell(wl_shell* shell) : hnd(shell) {
if (!hnd) {
throw std::runtime_error("Can't create shell from nullptr!");
}
}
wl::shell::~shell() {
fmt::print("destroying shell\n");
wl_shell_destroy(hnd);
}
wl::shell_surface wl::shell::make_surface(const surface& surface) {
return wl::shell_surface{ wl_shell_get_shell_surface(hnd, static_cast<wl_surface*>(surface)) };
}
namespace wl {
class shm_pool {
wl_shm_pool* const hnd;
public:
explicit shm_pool(wl_shm_pool*);
shm_pool(const shm_pool&) = delete;
~shm_pool();
explicit operator wl_shm_pool*() const;
buffer make_buffer(std::int32_t offset, std::int32_t width, std::int32_t height, std::int32_t stride, std::int32_t format);
};
}
wl::shm_pool::shm_pool(wl_shm_pool* pool) : hnd(pool) {
if (!hnd) {
throw std::runtime_error("Can't create pool from nullptr!");
}
}
wl::shm_pool::operator wl_shm_pool*() const {
return hnd;
}
wl::buffer wl::shm_pool::make_buffer(std::int32_t offset, std::int32_t width, std::int32_t height, std::int32_t stride, std::int32_t format) {
return wl::buffer{ wl_shm_pool_create_buffer(hnd, offset, width, height, stride, format) };
}
wl::shm_pool::~shm_pool() {
wl_shm_pool_destroy(hnd);
}
namespace wl {
class shm {
wl_shm* const hnd;
static void format(void* data, wl_shm* shm, std::uint32_t format);
wl_shm_listener listener { format };
public:
explicit shm(wl_shm*);
explicit operator wl_shm*() const;
shm_pool make_pool(std::int32_t fd, std::int32_t size);
};
}
wl::shm::shm(wl_shm* shm) : hnd(shm) {
if (!hnd) {
throw std::runtime_error("Can't create shm from nullptr!");
}
wl_shm_add_listener(shm, &listener, nullptr);
}
void wl::shm::format(void* data, wl_shm* shm, std::uint32_t format) {
fmt::print("Format {}\n", format);
}
wl::shm::operator wl_shm*() const {
return hnd;
}
wl::shm_pool wl::shm::make_pool(std::int32_t fd, std::int32_t size) {
return wl::shm_pool { wl_shm_create_pool(hnd, fd, size) };
}
namespace wl {
class registry {
wl_registry* hnd;
std::forward_list<unique_any> objects;
static void handler(void* data, wl_registry* registry, std::uint32_t id, const char* iface_name, std::uint32_t version);
static void remover(void* data, wl_registry* registry, std::uint32_t id);
wl_registry_listener listener { handler, remover };
public:
explicit registry(wl_registry*);
registry(const registry&) = delete;
explicit operator wl_registry*() const;
template<typename T>
T& get() {
for (auto& object : objects) {
T* ptr = object.get<T>();
if (ptr != nullptr) {
return *ptr;
}
}
throw std::runtime_error("No object of given type!\n");
}
};
}
wl::registry::registry(wl_registry* registry) : hnd{registry} {
if (!hnd) {
throw std::runtime_error("Can't create registry from nullptr!");
}
wl_registry_add_listener(hnd, &listener, this);
}
void wl::registry::handler(void* data, wl_registry* registry, std::uint32_t id, const char* iface, std::uint32_t version) {
auto& reg = *reinterpret_cast<wl::registry*>(data);
auto reg_ptr = static_cast<wl_registry*>(reg);
if (iface == "wl_compositor"s) {
reg.objects.emplace_front (
std::in_place_type<compositor>,
static_cast<wl_compositor*> (wl_registry_bind(reg_ptr, id, &wl_compositor_interface, version))
);
} else if (iface == "wl_shell"s) {
reg.objects.emplace_front (
std::in_place_type<shell>,
static_cast<wl_shell*> (wl_registry_bind(reg_ptr, id, &wl_shell_interface, version))
);
} else if (iface == "wl_shm"s) {
reg.objects.emplace_front (
std::in_place_type<shm>,
static_cast<wl_shm*> (wl_registry_bind(reg_ptr, id, &wl_shm_interface, version))
);
}
else {
fmt::print("{:>3}: {}: no handler\n", id, iface);
return;
}
fmt::print("{:>3}: {}: bound successfully\n", id, iface);
}
void wl::registry::remover(void* data, wl_registry* registry, std::uint32_t id) {
fmt::print("Got a registry losing event for id {}\n", id);
}
wl::registry::operator wl_registry*() const {
return hnd;
}
namespace wl {
class display {
wl_display* const hnd;
public:
display();
~display();
registry make_registry();
int dispatch();
void roundtrip();
};
};
wl::display::display() : hnd(wl_display_connect(nullptr)) {
if (!hnd) {
throw std::runtime_error("Can't connect to wayland");
}
}
wl::display::~display() {
fmt::print("!!!!Disconnecting\n");
wl_display_disconnect(hnd);
}
wl::registry wl::display::make_registry() {
return wl::registry{wl_display_get_registry(hnd)};
}
int wl::display::dispatch() {
int count = wl_display_dispatch(hnd);
if (count == -1) {
throw std::runtime_error("Dispatch failed!");
} else {
return count;
}
}
void wl::display::roundtrip() {
wl_display_roundtrip(hnd);
}
int main() {
wl::display my_display;
wl::registry my_registry = my_display.make_registry();
fmt::print("Dispatching\n");
my_display.dispatch(); // dispatch events
my_display.roundtrip(); // wait until they're processed by the server
fmt::print("Getting compositor\n");
auto& my_compositor = my_registry.get<wl::compositor>();
fmt::print("Got compositor\n");
wl::surface my_surface = my_compositor.make_surface();
auto& my_shell = my_registry.get<wl::shell>();
wl::shell_surface my_shell_surface = my_shell.make_surface(my_surface);
my_shell_surface.set_toplevel();
auto& my_shm = my_registry.get<wl::shm>();
//make_window function. eventually abstract surface + buffer (shm, drm) pair
namespace ipc = boost::interprocess;
ipc::shared_memory_object shm_obj {
ipc::open_or_create,
"foobar",
ipc::read_write
};
const int stride = win_width * bytes_per_pixel;
const int size = stride * win_height;
shm_obj.truncate(size);
ipc::mapped_region region {shm_obj, ipc::read_write};
int fd = shm_obj.get_mapping_handle().handle; //WARNING: platform-specific
wl::shm_pool my_pool = my_shm.make_pool(fd, size);
wl::buffer my_buffer = my_pool.make_buffer(0, win_width, win_height, stride, WL_SHM_FORMAT_XRGB8888);
my_surface.attach(my_buffer, 0, 0);
my_surface.commit();
// end make_window
my_surface.on_frame.connect (
[&](std::uint32_t) {
fmt::print("painting\n");
wl_surface_damage(static_cast<wl_surface*>(my_surface), 0, 0, win_width, win_height);
std::fill(
static_cast<int*>(region.get_address()),
static_cast<int*>(region.get_address()) + win_width * win_height,
0xff00ff
);
my_surface.commit();
}
);
while (my_display.dispatch()) {
}
return 0;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment