diff --git a/src/Application.cpp b/src/Application.cpp index afd9a29..3d039be 100644 --- a/src/Application.cpp +++ b/src/Application.cpp @@ -539,6 +539,8 @@ Application::Application() init_input(); + init_wayland(); + if (m_backend == Backend::SDL) mouse_captured(true); @@ -647,6 +649,15 @@ auto Application::init_test_meshes() -> void m_test_meshes = std::move(*meshes); } +auto Application::init_wayland() -> void +{ + // TODO: Replace with new name, we might have conflicts! + auto const *WAYLAND_SOCKET_NAME { "wayland-5" }; + m_wayland.display.add_socket(WAYLAND_SOCKET_NAME); + assert(setenv("WAYLAND_DISPLAY", WAYLAND_SOCKET_NAME, true) == 0); + m_logger.info("Started Wayland display socket on {}", WAYLAND_SOCKET_NAME); +} + auto Application::run() -> void { diff --git a/src/Application.h b/src/Application.h index f9b35ee..79efed8 100644 --- a/src/Application.h +++ b/src/Application.h @@ -5,7 +5,6 @@ #include #include #include -#include #include #include @@ -19,6 +18,7 @@ #include "Logger.h" #include "Skybox.h" #include "Types.h" +#include "wayland/Display.h" struct libinput; struct libinput_event_keyboard; @@ -55,6 +55,7 @@ private: auto init_input() -> void; auto init_test_meshes() -> void; + auto init_wayland() -> void; auto asset_directory() -> std::filesystem::path; auto shutdown_input() -> void; auto process_libinput_events() -> void; @@ -79,6 +80,10 @@ private: auto render_hands( VulkanRenderer::GL &gl, smath::Mat4 const &view_projection) -> void; + struct { + Wayland::Display display; + } m_wayland; + SDL_Window *m_window { nullptr }; Backend m_backend { Backend::SDL }; Logger m_logger { "Lunar" }; diff --git a/src/wayland/Client.h b/src/wayland/Client.h new file mode 100644 index 0000000..07bdf1d --- /dev/null +++ b/src/wayland/Client.h @@ -0,0 +1,112 @@ +#pragma once + +#include +#include +#include +#include +#include + +#include + +#include "Display.h" + +namespace Lunar::Wayland { + +struct Client { + Client(wl_client *client) + : m_client { std::move(client) } + { + assert(m_client); + } + ~Client() = default; + + inline auto c_ptr() const -> wl_client * { return m_client; } + + static auto from_link(wl_list *link) -> Client + { + return Client { wl_client_from_link(link) }; + } + + auto flush() { wl_client_flush(m_client); } + + auto get_display() -> Display + { + return Display { wl_client_get_display(m_client) }; + } + + auto get_credentials() noexcept -> std::tuple + { + std::tuple ret {}; + auto &[pid, uid, gid] { ret }; + wl_client_get_credentials(m_client, &pid, &uid, &gid); + return ret; + } + + auto get_fd() noexcept -> int { return wl_client_get_fd(m_client); } + + auto get_object(uint32_t id) -> std::optional + { + if (auto *res = wl_client_get_object(m_client, id); res != NULL) { + return res; + } else { + return {}; + } + } + + auto post_implementation_error(std::string_view string) + { + wl_client_post_implementation_error( + m_client, "%.*s", static_cast(string.size()), string.data()); + } + + template + auto post_implementation_error( + std::format_string fmt, Args &&...args) + { + post_implementation_error( + std::format(fmt, std::forward(args)...)); + } + + auto add_destroy_listener(wl_listener *listener) + { + wl_client_add_destroy_listener(m_client, listener); + } + + auto add_destroy_late_listener(wl_listener *listener) + { + wl_client_add_destroy_late_listener(m_client, listener); + } + + auto get_link() -> List { return wl_client_get_link(m_client); } + + auto add_resource_created_listener(wl_listener *listener) + { + wl_client_add_resource_created_listener(m_client, listener); + } + + auto for_each_resource( + std::function const &fn) -> void + { + wl_client_for_each_resource( + m_client, + (wl_client_for_each_resource_iterator_func_t)[]( + wl_resource * res, void *user_data) + ->wl_iterator_result { + auto *f = static_cast< + std::function *>( + user_data); + return (*f)(res); + }, + &fn); + } + + auto set_max_buffer_size(size_t max_buffer_size) + { + wl_client_set_max_buffer_size(m_client, max_buffer_size); + } + +private: + wl_client *m_client {}; +}; + +} // namespace Lunar::Wayland diff --git a/src/wayland/Display.h b/src/wayland/Display.h new file mode 100644 index 0000000..d9905cc --- /dev/null +++ b/src/wayland/Display.h @@ -0,0 +1,103 @@ +#pragma once + +#include +#include +#include +#include + +#include + +namespace Lunar::Wayland { + +struct Display { + Display() + : m_display(wl_display_create()) + { + } + Display(wl_display *display) + : m_display { std::move(display) } + , m_should_cleanup { false } + { + } + ~Display() + { + if (!m_should_cleanup) + return; + wl_display_destroy_clients(m_display); + wl_display_destroy(m_display); + } + + inline auto c_ptr() const -> wl_display * { return m_display; } + + auto set_global_filter( + wl_display_global_filter_func_t filter, void *data) noexcept + { + wl_display_set_global_filter(m_display, filter, data); + } + + auto next_serial() noexcept -> uint32_t + { + return wl_display_next_serial(m_display); + } + + auto set_default_max_buffer_size(size_t max_buffer_size) noexcept + { + wl_display_set_default_max_buffer_size(m_display, max_buffer_size); + } + + auto add_socket_fd(int fd) + { + if (wl_display_add_socket_fd(m_display, fd) == -1) { + throw std::runtime_error( + "Failed to add socket fd to Wayland display"); + } + } + + auto add_socket(char const *name) + { + if (wl_display_add_socket(m_display, name) == -1) { + throw std::runtime_error(std::format( + "Failed to add socket `{}` to Wayland display", name)); + } + } + + auto add_protocol_logger(wl_protocol_logger_func_t func, void *user_data) + -> wl_protocol_logger * + { + if (auto *logger + = wl_display_add_protocol_logger(m_display, func, user_data); + logger != NULL) { + return logger; + } else { + throw std::runtime_error( + "Failed to add protocol logger to Wayland display"); + } + } + + auto add_shm_format(uint32_t format) -> uint32_t * + { + if (auto *fmt = wl_display_add_shm_format(m_display, format); + fmt != NULL) { + return fmt; + } else { + throw std::runtime_error( + "Failed to add SHM format to Wayland display"); + } + } + + auto get_client_list() -> std::vector + { + std::vector ret {}; + auto const list { wl_display_get_client_list(m_display) }; + assert(list); + wl_client *client {}; + wl_client_for_each(client, list) { ret.push_back(client); } + return ret; + } + +private: + wl_display *m_display {}; + bool m_should_cleanup { true }; +}; + +} // namespace Lunar::Wayland diff --git a/src/wayland/Global.h b/src/wayland/Global.h new file mode 100644 index 0000000..281062c --- /dev/null +++ b/src/wayland/Global.h @@ -0,0 +1,71 @@ +#pragma once + +#include +#include +#include + +#include + +#include "Client.h" +#include "Display.h" + +namespace Lunar::Wayland { + +struct Global { + Global() = delete; + + explicit Global(Display &display, wl_interface const *interface, + int version, void *data, wl_global_bind_func_t bind) + : m_global { + wl_global_create(display.c_ptr(), interface, version, data, bind), + } + { + } + + Global(wl_global *global) + : m_global { std::move(global) } + , m_should_cleanup { false } + { + assert(m_global); + } + + ~Global() + { + if (!m_should_cleanup) + return; + wl_global_destroy(m_global); + } + + inline auto c_ptr() const -> wl_global * { return m_global; } + + auto get_name(Client &client) const -> std::optional + { + if (auto const ret = wl_global_get_name(m_global, client.c_ptr()); + ret != 0) { + return ret; + } else { + return {}; + } + } + + inline auto get_version() const -> uint32_t + { + return wl_global_get_version(m_global); + } + + inline auto get_display() const -> Display + { + return wl_global_get_display(m_global); + } + + inline auto get_interface() const -> wl_interface const * + { + return wl_global_get_interface(m_global); + } + +private: + wl_global *m_global {}; + bool m_should_cleanup { true }; +}; + +} // namespace Lunar::Wayland diff --git a/src/wayland/List.h b/src/wayland/List.h new file mode 100644 index 0000000..4c97d64 --- /dev/null +++ b/src/wayland/List.h @@ -0,0 +1,286 @@ +#pragma once + +#include +#include + +#include + +namespace Lunar::Wayland { + +namespace detail { + +template +constexpr std::ptrdiff_t member_offset() noexcept +{ + return reinterpret_cast( + &(reinterpret_cast(0)->*Member)); +} + +template +inline T *container_of(wl_list *node) noexcept +{ + auto *p = reinterpret_cast(node) - member_offset(); + return reinterpret_cast(p); +} + +template +inline T const *container_of(wl_list const *node) noexcept +{ + auto *p = reinterpret_cast(node) + - member_offset(); + return reinterpret_cast(p); +} + +} // namespace detail + +template struct List { + struct Iterator { + using iterator_category = std::bidirectional_iterator_tag; + using value_type = T; + using difference_type = std::ptrdiff_t; + using pointer = T *; + using reference = T &; + + Iterator() = default; + Iterator(wl_list *cur, wl_list *head) + : m_cur(cur) + , m_head(head) + { + } + + auto operator*() const -> reference + { + return *detail::container_of(m_cur); + } + auto operator->() const -> pointer + { + return detail::container_of(m_cur); + } + + auto operator++() -> Iterator & + { + m_cur = m_cur->next; + return *this; + } + auto operator++(int) -> Iterator + { + auto t = *this; + ++(*this); + return t; + } + + auto operator--() -> Iterator & + { + m_cur = (m_cur == m_head) ? m_head->prev : m_cur->prev; + return *this; + } + auto operator--(int) -> Iterator + { + auto t = *this; + --(*this); + return t; + } + + friend auto operator==(Iterator a, Iterator b) -> bool + { + return a.m_cur == b.m_cur; + } + friend auto operator!=(Iterator a, Iterator b) -> bool + { + return !(a == b); + } + + private: + wl_list *m_cur { nullptr }; + wl_list *m_head { nullptr }; + }; + + struct ConstIterator { + using iterator_category = std::bidirectional_iterator_tag; + using value_type = T const; + using difference_type = std::ptrdiff_t; + using pointer = T const *; + using reference = T const &; + + ConstIterator() = default; + ConstIterator(wl_list const *cur, wl_list const *head) + : m_cur(cur) + , m_head(head) + { + } + + auto operator*() const -> reference + { + return *detail::container_of(m_cur); + } + auto operator->() const -> pointer + { + return detail::container_of(m_cur); + } + + auto operator++() -> ConstIterator & + { + m_cur = m_cur->next; + return *this; + } + auto operator++(int) -> ConstIterator + { + auto t = *this; + ++(*this); + return t; + } + + auto operator--() -> ConstIterator & + { + m_cur = (m_cur == m_head) ? m_head->prev : m_cur->prev; + return *this; + } + auto operator--(int) -> ConstIterator + { + auto t = *this; + --(*this); + return t; + } + + friend auto operator==(ConstIterator a, ConstIterator b) -> bool + { + return a.m_cur == b.m_cur; + } + friend auto operator!=(ConstIterator a, ConstIterator b) -> bool + { + return !(a == b); + } + + private: + wl_list const *m_cur { nullptr }; + wl_list const *m_head { nullptr }; + }; + + List() + : m_head {} + , m_external_head { nullptr } + , m_should_cleanup { true } + { + wl_list_init(&m_head); + } + + explicit List( + wl_list *existing_head, bool should_cleanup = false, bool init = false) + : m_head {} + , m_external_head { existing_head } + , m_should_cleanup { should_cleanup } + { + if (init && m_external_head) + wl_list_init(m_external_head); + } + + ~List() + { + if (!m_should_cleanup) + return; + + clear(); + if (auto *h = head_ptr(); h) + wl_list_init(h); + } + + List(List const &) = delete; + auto operator=(List const &) -> List & = delete; + + List(List &&other) noexcept { move_from(other); } + + auto operator=(List &&other) noexcept -> List & + { + if (this != &other) { + this->~List(); + move_from(other); + } + return *this; + } + + inline auto c_ptr() -> wl_list * { return head_ptr(); } + inline auto c_ptr() const -> wl_list const * { return head_ptr(); } + + auto empty() const noexcept -> bool { return wl_list_empty(head_ptr()); } + auto length() const noexcept -> int { return wl_list_length(head_ptr()); } + + auto push_front(T *elem) noexcept -> void + { + wl_list_insert(head_ptr(), &(elem->*Member)); + } + + auto push_back(T *elem) noexcept -> void + { + auto *h = head_ptr(); + wl_list_insert(h->prev, &(elem->*Member)); + } + + auto remove(T *elem) noexcept -> void + { + wl_list_remove(&(elem->*Member)); + wl_list_init(&(elem->*Member)); + } + + auto clear() noexcept -> void + { + auto *h = head_ptr(); + while (!wl_list_empty(h)) { + auto *node = h->next; + wl_list_remove(node); + wl_list_init(node); + } + } + + auto begin() noexcept -> Iterator + { + auto *h = head_ptr(); + return Iterator(h->next, h); + } + + auto end() noexcept -> Iterator + { + auto *h = head_ptr(); + return Iterator(h, h); + } + + auto begin() const noexcept -> ConstIterator + { + auto const *h = head_ptr(); + return ConstIterator(h->next, h); + } + + auto end() const noexcept -> ConstIterator + { + auto const *h = head_ptr(); + return ConstIterator(h, h); + } + +private: + auto head_ptr() noexcept -> wl_list * + { + return m_external_head ? m_external_head : &m_head; + } + + auto head_ptr() const noexcept -> wl_list const * + { + return m_external_head ? m_external_head : &m_head; + } + + auto move_from(List &other) noexcept -> void + { + m_head = other.m_head; + m_external_head = other.m_external_head; + m_should_cleanup = other.m_should_cleanup; + + other.m_external_head = nullptr; + other.m_should_cleanup = false; + wl_list_init(&other.m_head); + } + +private: + wl_list m_head {}; + wl_list *m_external_head { nullptr }; + bool m_should_cleanup { true }; +}; + +} // namespace Lunar::Wayland diff --git a/src/wayland/Signal.h b/src/wayland/Signal.h new file mode 100644 index 0000000..e54d990 --- /dev/null +++ b/src/wayland/Signal.h @@ -0,0 +1,28 @@ +#pragma once + +#include + +#include + +namespace Lunar::Wayland { + +struct Signal { + Signal(wl_signal *signal) + : m_signal { std::move(signal) } + { + assert(m_signal); + } + ~Signal() = default; + + inline auto c_ptr() const -> wl_signal * { return m_signal; } + + template auto flush(T *data) + { + wl_signal_emit_mutable(m_signal, (void *)data); + } + +private: + wl_signal *m_signal {}; +}; + +} // namespace Lunar::Wayland