Last active
January 17, 2019 16:53
-
-
Save elliotpotts/b54be8d5435b86cfd135c3742343bdf5 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#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