kranewl

A wlroots-based dynamic Wayland compositor, written in C++, configurable with Lua
git clone git://git.deurzen.net/kranewl
Log | Files | Refs | LICENSE

commit bc69f6b67c67d48358266f754d823cb48e460528
parent 1d4813bf87f8d493e62211d5aa9b951911061807
Author: deurzen <m.deurzen@tum.de>
Date:   Wed, 18 May 2022 00:52:38 +0200

adds initial server setup and signalling

Diffstat:
Dinclude/kranewl/conf/state.hh | 3---
Minclude/kranewl/input/keyboard.hh | 12++++++++++++
Minclude/kranewl/model.hh | 27++++-----------------------
Minclude/kranewl/server.hh | 88+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++--------
Ainclude/kranewl/tree/output.hh | 13+++++++++++++
Minclude/kranewl/tree/view.hh | 16++++++++++++++++
Minclude/version.hh | 4++--
Msrc/kranewl/conf/config.cc | 35+++++++++++++++++++++++++++++++----
Dsrc/kranewl/conf/state.cc | 3---
Msrc/kranewl/main.cc | 2+-
Msrc/kranewl/model.cc | 27+++++++++++++++++++++++++++
Msrc/kranewl/server.cc | 441+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/kranewl/tree/output.cc | 3+++
13 files changed, 630 insertions(+), 44 deletions(-)

diff --git a/include/kranewl/conf/state.hh b/include/kranewl/conf/state.hh @@ -1,3 +0,0 @@ -#pragma once - - diff --git a/include/kranewl/input/keyboard.hh b/include/kranewl/input/keyboard.hh @@ -1,3 +1,15 @@ #pragma once +extern "C" { +#include <wlr/backend.h> +} +class Server; +struct Keyboard { + struct wl_list link; + Server* server; + struct wlr_input_device* device; + + struct wl_listener modifiers; + struct wl_listener key; +}; diff --git a/include/kranewl/model.hh b/include/kranewl/model.hh @@ -1,34 +1,15 @@ #pragma once -#include <kranewl/conf/config.hh> -#include <kranewl/exec.hh> -#include <kranewl/server.hh> - -#include <spdlog/spdlog.h> - #include <optional> #include <string> +class Server; +class Config; class Model final { public: - Model( - Server& server, - Config const& config, - [[maybe_unused]] std::optional<std::string> autostart - ) - : m_server(server), - m_config(config) - { -#ifdef NDEBUG - if (autostart) { - spdlog::info("Executing autostart at " + config_path); - exec_external(*autostart); - } -#endif - } - - ~Model() {} + Model(Server&, Config const&, std::optional<std::string>); + ~Model(); void run(); diff --git a/include/kranewl/server.hh b/include/kranewl/server.hh @@ -1,21 +1,93 @@ #pragma once -#include <kranewl/conf/state.hh> - -#include <wayland-server.h> -#include <wayland-util.h> +extern "C" { +#include <wlr/backend.h> +#include <wlr/util/box.h> +} +#include <cstdint> #include <string> +struct View; class Server final { + enum class CursorMode { + Passthrough, + Move, + Resize, + }; + public: - Server(std::string&& wm_name) - : m_wm_name(wm_name) {} + Server(); + ~Server(); - ~Server() {}; + void start() noexcept; private: - std::string m_wm_name; + static void new_output(struct wl_listener*, void*); + static void new_xdg_surface(struct wl_listener*, void*); + static void new_input(struct wl_listener*, void*); + static void new_pointer(Server*, struct wlr_input_device*); + static void new_keyboard(Server*, struct wlr_input_device*); + + static void cursor_motion(struct wl_listener*, void*); + static void cursor_motion_absolute(struct wl_listener*, void*); + static void cursor_axis(struct wl_listener*, void*); + static void cursor_button(struct wl_listener*, void*); + static void cursor_frame(struct wl_listener*, void*); + static void cursor_process_motion(Server*, uint32_t); + static void cursor_process_move(Server*, uint32_t); + static void cursor_process_resize(Server*, uint32_t); + + static void keyboard_handle_modifiers(struct wl_listener*, void*); + static void keyboard_handle_key(struct wl_listener*, void*); + + static void seat_request_cursor(struct wl_listener*, void*); + static void seat_request_set_selection(struct wl_listener*, void*); + + static void output_frame(struct wl_listener*, void*); + static View* desktop_view_at(Server*, double, double, struct wlr_surface**, double*, double*); + static void focus_view(View*, struct wlr_surface*); + + static void xdg_toplevel_map(struct wl_listener*, void*); + static void xdg_toplevel_unmap(struct wl_listener*, void*); + static void xdg_toplevel_destroy(struct wl_listener*, void*); + static void xdg_toplevel_request_move(struct wl_listener*, void*); + static void xdg_toplevel_request_resize(struct wl_listener*, void*); + + struct wl_display* m_display; + struct wlr_backend* m_backend; + struct wlr_renderer* m_renderer; + struct wlr_allocator* m_allocator; + struct wlr_scene* m_scene; + + struct wlr_xdg_shell* m_xdg_shell; + struct wl_listener m_new_xdg_surface; + struct wl_list m_views; + + struct wlr_cursor* m_cursor; + struct wlr_xcursor_manager* m_cursor_mgr; + struct wl_listener m_cursor_motion; + struct wl_listener m_cursor_motion_absolute; + struct wl_listener m_cursor_button; + struct wl_listener m_cursor_axis; + struct wl_listener m_cursor_frame; + + struct wlr_seat* m_seat; + struct wl_listener m_new_input; + struct wl_listener m_request_cursor; + struct wl_listener m_request_set_selection; + struct wl_list m_keyboards; + CursorMode m_cursor_mode; + struct tinywl_view* m_grabbed_view; + double m_grab_x, m_grab_y; + struct wlr_box m_grab_geobox; + uint32_t m_resize_edges; + + struct wlr_output_layout* m_output_layout; + struct wl_list m_outputs; + struct wl_listener m_new_output; + + const std::string m_socket; }; diff --git a/include/kranewl/tree/output.hh b/include/kranewl/tree/output.hh @@ -0,0 +1,13 @@ +#pragma once + +extern "C" { +#include <wlr/backend.h> +} + +class Server; +struct Output { + struct wl_list link; + Server* server; + struct wlr_output* wlr_output; + struct wl_listener frame; +}; diff --git a/include/kranewl/tree/view.hh b/include/kranewl/tree/view.hh @@ -1,3 +1,19 @@ #pragma once +extern "C" { +#include <wlr/backend.h> +} +class Server; +struct View { + struct wl_list link; + Server* server; + struct wlr_xdg_surface* xdg_surface; + struct wlr_scene_node* scene_node; + struct wl_listener map; + struct wl_listener unmap; + struct wl_listener destroy; + struct wl_listener request_move; + struct wl_listener request_resize; + int x, y; +}; diff --git a/include/version.hh b/include/version.hh @@ -1 +1 @@ -#define VERSION "master/c8d6fb9+" -\ No newline at end of file +#define VERSION "master/dfeb751+" +\ No newline at end of file diff --git a/src/kranewl/conf/config.cc b/src/kranewl/conf/config.cc @@ -8,6 +8,29 @@ extern "C" { #include <lua5.4/lualib.h> } +#include <linux/input-event-codes.h> +#include <wlr/types/wlr_keyboard.h> + +static void +print_table(lua_State* L) +{ + lua_pushnil(L); + + while(lua_next(L, -2) != 0) + { + if(lua_isstring(L, -1)) + printf("%s = %s\n", lua_tostring(L, -2), lua_tostring(L, -1)); + else if(lua_isnumber(L, -1)) + printf("%s = %f\n", lua_tostring(L, -2), lua_tonumber(L, -1)); + else if(lua_istable(L, -1)) { + printf("%s:\n", lua_tostring(L, -2)); + print_table(L); + } + + lua_pop(L, 1); + } +} + Config ConfigParser::generate_config() const noexcept { @@ -32,11 +55,12 @@ ConfigParser::generate_config() const noexcept bool ConfigParser::parse_decorations(Config& config) const noexcept { + // TODO static_cast<void>(config); lua_getglobal(m_state.get(), "decorations"); if (lua_istable(m_state.get(), -1)) - ; // TODO + print_table(m_state.get()); return true; } @@ -44,11 +68,12 @@ ConfigParser::parse_decorations(Config& config) const noexcept bool ConfigParser::parse_outputs(Config& config) const noexcept { + // TODO static_cast<void>(config); lua_getglobal(m_state.get(), "outputs"); if (lua_istable(m_state.get(), -1)) - ; // TODO + print_table(m_state.get()); return true; } @@ -56,11 +81,12 @@ ConfigParser::parse_outputs(Config& config) const noexcept bool ConfigParser::parse_commands(Config& config) const noexcept { + // TODO static_cast<void>(config); lua_getglobal(m_state.get(), "commands"); if (lua_istable(m_state.get(), -1)) - ; // TODO + print_table(m_state.get()); return true; } @@ -68,11 +94,12 @@ ConfigParser::parse_commands(Config& config) const noexcept bool ConfigParser::parse_bindings(Config& config) const noexcept { + // TODO static_cast<void>(config); lua_getglobal(m_state.get(), "bindings"); if (lua_istable(m_state.get(), -1)) - ; // TODO + print_table(m_state.get()); return true; } diff --git a/src/kranewl/conf/state.cc b/src/kranewl/conf/state.cc @@ -1,3 +0,0 @@ -#include <kranewl/conf/state.hh> - - diff --git a/src/kranewl/main.cc b/src/kranewl/main.cc @@ -22,7 +22,7 @@ main(int argc, char** argv) const ConfigParser config_parser{options.config_path}; const Config config = config_parser.generate_config(); - Server server{"kranewl"}; + Server server; Model{server, config, options.autostart_path}.run(); return EXIT_SUCCESS; diff --git a/src/kranewl/model.cc b/src/kranewl/model.cc @@ -1,5 +1,32 @@ #include <kranewl/model.hh> +#include <kranewl/conf/config.hh> +#include <kranewl/exec.hh> +#include <kranewl/server.hh> + +#include <spdlog/spdlog.h> + +Model::Model( + Server& server, + Config const& config, + [[maybe_unused]] std::optional<std::string> autostart_path +) + : m_server(server), + m_config(config) +{ +#ifdef NDEBUG + if (autostart_path) { + spdlog::info("Executing autostart file at " + *autostart_path); + exec_external(*autostart_path); + } +#endif + + m_server.start(); +} + +Model::~Model() +{} + void Model::run() { diff --git a/src/kranewl/server.cc b/src/kranewl/server.cc @@ -1 +1,442 @@ #include <kranewl/server.hh> + +#include <kranewl/input/keyboard.hh> +#include <kranewl/tree/output.hh> +#include <kranewl/tree/view.hh> + +#include <spdlog/spdlog.h> + +#include <wayland-server-core.h> +#include <wayland-util.h> + +// https://github.com/swaywm/wlroots/issues/682 +#define class class_ +#define namespace namespace_ +#define static +extern "C" { +#include <wlr/backend.h> +#include <wlr/render/allocator.h> +#include <wlr/render/wlr_renderer.h> +#include <wlr/types/wlr_compositor.h> +#include <wlr/types/wlr_cursor.h> +#include <wlr/types/wlr_data_device.h> +#include <wlr/types/wlr_input_device.h> +#include <wlr/types/wlr_keyboard.h> +#include <wlr/types/wlr_output.h> +#include <wlr/types/wlr_output_layout.h> +#include <wlr/types/wlr_pointer.h> +#include <wlr/types/wlr_scene.h> +#include <wlr/types/wlr_seat.h> +#include <wlr/types/wlr_xcursor_manager.h> +#include <wlr/types/wlr_xdg_shell.h> +#include <wlr/util/box.h> +#include <wlr/util/log.h> +#ifdef WLR_HAS_XWAYLAND +#include <wlr/xwayland.h> +#endif +} +#undef static +#undef class +#undef namespace + +extern "C" { +#include <xkbcommon/xkbcommon.h> +} + +#include <cstdlib> + +Server::Server() + : m_display(wl_display_create()), + m_backend(wlr_backend_autocreate(m_display)), + m_renderer([](struct wl_display* display, struct wlr_backend* backend) { + struct wlr_renderer* renderer = wlr_renderer_autocreate(backend); + wlr_renderer_init_wl_display(renderer, display); + + return renderer; + }(m_display, m_backend)), + m_allocator(wlr_allocator_autocreate(m_backend, m_renderer)), + m_socket(wl_display_add_socket_auto(m_display)) +{ + wlr_log_init(WLR_DEBUG, NULL); + + wlr_compositor_create(m_display, m_renderer); + wlr_data_device_manager_create(m_display); + + m_output_layout = wlr_output_layout_create(); + + wl_list_init(&m_outputs); + m_new_output.notify = Server::new_output; + wl_signal_add(&m_backend->events.new_output, &m_new_output); + + m_scene = wlr_scene_create(); + wlr_scene_attach_output_layout(m_scene, m_output_layout); + + wl_list_init(&m_views); + m_xdg_shell = wlr_xdg_shell_create(m_display); + m_new_xdg_surface.notify = Server::new_xdg_surface; + wl_signal_add(&m_xdg_shell->events.new_surface, &m_new_xdg_surface); + + m_cursor = wlr_cursor_create(); + wlr_cursor_attach_output_layout(m_cursor, m_output_layout); + + m_cursor_mgr = wlr_xcursor_manager_create(NULL, 24); + wlr_xcursor_manager_load(m_cursor_mgr, 1); + + { // sets up cursor signals + m_cursor_motion.notify = Server::cursor_motion; + wl_signal_add(&m_cursor->events.motion, &m_cursor_motion); + m_cursor_motion_absolute.notify = Server::cursor_motion_absolute; + wl_signal_add(&m_cursor->events.motion_absolute, &m_cursor_motion_absolute); + m_cursor_button.notify = Server::cursor_button; + wl_signal_add(&m_cursor->events.button, &m_cursor_button); + m_cursor_axis.notify = Server::cursor_axis; + wl_signal_add(&m_cursor->events.axis, &m_cursor_axis); + m_cursor_frame.notify = Server::cursor_frame; + wl_signal_add(&m_cursor->events.frame, &m_cursor_frame); + } + + { // sets up keyboard signals + wl_list_init(&m_keyboards); + m_new_input.notify = Server::new_input; + wl_signal_add(&m_backend->events.new_input, &m_new_input); + m_seat = wlr_seat_create(m_display, "seat0"); + m_request_cursor.notify = Server::seat_request_cursor; + wl_signal_add(&m_seat->events.request_set_cursor, &m_request_cursor); + m_request_set_selection.notify = Server::seat_request_set_selection; + wl_signal_add(&m_seat->events.request_set_selection, &m_request_set_selection); + } + + if (m_socket.empty()) { + wlr_backend_destroy(m_backend); + exit(1); + return; + } + + if (!wlr_backend_start(m_backend)) { + wlr_backend_destroy(m_backend); + wl_display_destroy(m_display); + exit(1); + return; + } + + setenv("WAYLAND_DISPLAY", m_socket.c_str(), true); + spdlog::info("Server initiated on WAYLAND_DISPLAY=" + m_socket); +} + +Server::~Server() +{ + wl_display_destroy_clients(m_display); + wl_display_destroy(m_display); +} + +void +Server::start() noexcept +{ + wl_display_run(m_display); +} + +void +Server::new_output(struct wl_listener* listener, void* data) +{ + Server* server = wl_container_of(listener, server, m_new_output); + struct wlr_output* wlr_output = reinterpret_cast<struct wlr_output*>(data); + + wlr_output_init_render(wlr_output, server->m_allocator, server->m_renderer); + + if (!wl_list_empty(&wlr_output->modes)) { + struct wlr_output_mode* mode = wlr_output_preferred_mode(wlr_output); + wlr_output_set_mode(wlr_output, mode); + wlr_output_enable(wlr_output, true); + if (!wlr_output_commit(wlr_output)) { + return; + } + } + + Output* output = reinterpret_cast<Output*>(calloc(1, sizeof(Output))); + output->wlr_output = wlr_output; + output->server = server; + output->frame.notify = Server::output_frame; + wl_signal_add(&wlr_output->events.frame, &output->frame); + wl_list_insert(&server->m_outputs, &output->link); + + wlr_output_layout_add_auto(server->m_output_layout, wlr_output); +} + +void +Server::new_xdg_surface(struct wl_listener* listener, void* data) +{ + Server* server = wl_container_of(listener, server, m_new_xdg_surface); + struct wlr_xdg_surface* xdg_surface = reinterpret_cast<struct wlr_xdg_surface*>(data); + + if (xdg_surface->role == WLR_XDG_SURFACE_ROLE_POPUP) { + struct wlr_xdg_surface* parent + = wlr_xdg_surface_from_wlr_surface(xdg_surface->popup->parent); + + struct wlr_scene_node* parent_node + = reinterpret_cast<struct wlr_scene_node*>(parent->data); + + xdg_surface->data = wlr_scene_xdg_surface_create(parent_node, xdg_surface); + return; + } + assert(xdg_surface->role == WLR_XDG_SURFACE_ROLE_TOPLEVEL); + + View* view = reinterpret_cast<View*>(calloc(1, sizeof(View))); + view->server = server; + view->xdg_surface = xdg_surface; + view->scene_node = wlr_scene_xdg_surface_create( + &view->server->m_scene->node, + view->xdg_surface + ); + view->scene_node->data = view; + xdg_surface->data = view->scene_node; + + view->map.notify = xdg_toplevel_map; + wl_signal_add(&xdg_surface->events.map, &view->map); + view->unmap.notify = xdg_toplevel_unmap; + wl_signal_add(&xdg_surface->events.unmap, &view->unmap); + view->destroy.notify = xdg_toplevel_destroy; + wl_signal_add(&xdg_surface->events.destroy, &view->destroy); + + struct wlr_xdg_toplevel* toplevel = xdg_surface->toplevel; + view->request_move.notify = xdg_toplevel_request_move; + wl_signal_add(&toplevel->events.request_move, &view->request_move); + view->request_resize.notify = xdg_toplevel_request_resize; + wl_signal_add(&toplevel->events.request_resize, &view->request_resize); +} + +void +Server::new_input(struct wl_listener* listener, void* data) +{ + Server* server = wl_container_of(listener, server, m_new_input); + struct wlr_input_device* device = reinterpret_cast<struct wlr_input_device*>(data); + + switch (device->type) { + case WLR_INPUT_DEVICE_KEYBOARD: + new_keyboard(server, device); + break; + case WLR_INPUT_DEVICE_POINTER: + new_pointer(server, device); + break; + default: + break; + } + + uint32_t caps = WL_SEAT_CAPABILITY_POINTER; + if (!wl_list_empty(&server->m_keyboards)) + caps |= WL_SEAT_CAPABILITY_KEYBOARD; + + wlr_seat_set_capabilities(server->m_seat, caps); +} + +void +Server::new_pointer(Server* server, struct wlr_input_device* device) +{ + wlr_cursor_attach_input_device(server->m_cursor, device); +} + +void +Server::new_keyboard(Server* server, struct wlr_input_device* device) +{ + Keyboard* keyboard = reinterpret_cast<Keyboard*>(calloc(1, sizeof(Keyboard))); + keyboard->server = server; + keyboard->device = device; + + struct xkb_context* context = xkb_context_new(XKB_CONTEXT_NO_FLAGS); + struct xkb_keymap* keymap = xkb_keymap_new_from_names( + context, + NULL, + XKB_KEYMAP_COMPILE_NO_FLAGS + ); + + wlr_keyboard_set_keymap(device->keyboard, keymap); + xkb_keymap_unref(keymap); + xkb_context_unref(context); + wlr_keyboard_set_repeat_info(device->keyboard, 25, 600); + + keyboard->modifiers.notify = keyboard_handle_modifiers; + wl_signal_add(&device->keyboard->events.modifiers, &keyboard->modifiers); + keyboard->key.notify = keyboard_handle_key; + wl_signal_add(&device->keyboard->events.key, &keyboard->key); + + wlr_seat_set_keyboard(server->m_seat, device); + wl_list_insert(&server->m_keyboards, &keyboard->link); +} + +void +Server::cursor_motion(struct wl_listener* listener, void* data) +{ + Server* server = wl_container_of(listener, server, m_cursor_motion); + struct wlr_event_pointer_motion* event + = reinterpret_cast<wlr_event_pointer_motion*>(data); + + wlr_cursor_move(server->m_cursor, event->device, event->delta_x, event->delta_y); + cursor_process_motion(server, event->time_msec); +} + +void +Server::cursor_motion_absolute(struct wl_listener* listener, void* data) +{ + Server* server = wl_container_of(listener, server, m_cursor_motion_absolute); + struct wlr_event_pointer_motion_absolute* event + = reinterpret_cast<wlr_event_pointer_motion_absolute*>(data); + + wlr_cursor_warp_absolute(server->m_cursor, event->device, event->x, event->y); + cursor_process_motion(server, event->time_msec); +} + +void +Server::cursor_button(struct wl_listener* listener, void* data) +{ + Server* server = wl_container_of(listener, server, m_cursor_button); + struct wlr_event_pointer_button* event + = reinterpret_cast<wlr_event_pointer_button*>(data); + + wlr_seat_pointer_notify_button( + server->m_seat, + event->time_msec, + event->button, + event->state + ); + + double sx, sy; + struct wlr_surface* surface = NULL; + + View* view = desktop_view_at( + server, + server->m_cursor->x, + server->m_cursor->y, + &surface, + &sx, + &sy + ); + + if (event->state == WLR_BUTTON_RELEASED) + server->m_cursor_mode = Server::CursorMode::Passthrough; + else + focus_view(view, surface); +} + +void +Server::cursor_axis(struct wl_listener* listener, void* data) +{ + Server* server = wl_container_of(listener, server, m_cursor_axis); + struct wlr_event_pointer_axis* event = reinterpret_cast<wlr_event_pointer_axis*>(data); + + wlr_seat_pointer_notify_axis( + server->m_seat, + event->time_msec, + event->orientation, + event->delta, + event->delta_discrete, + event->source + ); +} + +void +Server::cursor_frame(struct wl_listener* listener, void* data) +{ + Server* server = wl_container_of(listener, server, m_cursor_frame); + wlr_seat_pointer_notify_frame(server->m_seat); +} + +void +Server::cursor_process_motion(Server* server, uint32_t time) +{ + switch (server->m_cursor_mode) { + case Server::CursorMode::Move: cursor_process_move(server, time); return; + case Server::CursorMode::Resize: cursor_process_resize(server, time); return; + default: return; + } + + double sx, sy; + struct wlr_seat* seat = server->m_seat; + struct wlr_surface* surface = NULL; + View* view = desktop_view_at( + server, + server->m_cursor->x, + server->m_cursor->y, + &surface, + &sx, + &sy + ); + + if (!view) + wlr_xcursor_manager_set_cursor_image( + server->m_cursor_mgr, + "left_ptr", + server->m_cursor + ); + + if (surface) { + wlr_seat_pointer_notify_enter(seat, surface, sx, sy); + wlr_seat_pointer_notify_motion(seat, time, sx, sy); + } else + wlr_seat_pointer_clear_focus(seat); +} + +void +Server::cursor_process_move(Server* server, uint32_t time) +{} + +void +Server::cursor_process_resize(Server* server, uint32_t time) +{} + +void +Server::keyboard_handle_modifiers(struct wl_listener* listener, void* data) +{} + +void +Server::keyboard_handle_key(struct wl_listener* listener, void* data) +{} + +void +Server::seat_request_cursor(struct wl_listener* listener, void* data) +{} + +void +Server::seat_request_set_selection(struct wl_listener* listener, void* data) +{} + +void +Server::output_frame(struct wl_listener* listener, void* data) +{} + +View* +Server::desktop_view_at( + Server* server, + double lx, + double ly, + struct wlr_surface** surface, + double* sx, + double* sy +) +{ + +} + +void +Server::focus_view(View* view, struct wlr_surface* surface) +{ + +} + +void +Server::xdg_toplevel_map(struct wl_listener* listener, void* data) +{} + +void +Server::xdg_toplevel_unmap(struct wl_listener* listener, void* data) +{} + +void +Server::xdg_toplevel_destroy(struct wl_listener* listener, void* data) +{} + +void +Server::xdg_toplevel_request_move(struct wl_listener* listener, void* data) +{} + +void +Server::xdg_toplevel_request_resize(struct wl_listener* listener, void* data) +{} diff --git a/src/kranewl/tree/output.cc b/src/kranewl/tree/output.cc @@ -0,0 +1,3 @@ +#include <kranewl/tree/output.hh> + +