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:
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>
+
+