commit a12293fd3a89aa7162ee38b032d215ce0594f090 Author: speckitor Date: Fri Jan 2 22:53:34 2026 +0700 base diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..08ace1e --- /dev/null +++ b/.gitignore @@ -0,0 +1,4 @@ +absinthe +.cache/ +compile_commands.json +xdg-shell-protocol.h diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..f1453d4 --- /dev/null +++ b/Makefile @@ -0,0 +1,9 @@ +all: compile + +proto: + wayland-scanner server-header /usr/share/wayland-protocols/stable/xdg-shell/xdg-shell.xml xdg-shell-protocol.h + +compile: proto + gcc -o absinthe src/* \ + -I./ -I./include -DWLR_USE_UNSTABLE $(shell pkg-config wlroots-0.19 --libs --cflags) -lwayland-server -lxkbcommon \ + -ggdb -Wall -Wextra diff --git a/include/absinthe-toplevel.h b/include/absinthe-toplevel.h new file mode 100644 index 0000000..1c41677 --- /dev/null +++ b/include/absinthe-toplevel.h @@ -0,0 +1,3 @@ +#include "types.h" + +struct absinthe_toplevel *absinthe_toplevel_at(struct absinthe_server *server, double lx, double ly, struct wlr_surface **surface, double *sx, double *sy); diff --git a/include/cursor.h b/include/cursor.h new file mode 100644 index 0000000..2163e36 --- /dev/null +++ b/include/cursor.h @@ -0,0 +1,4 @@ +#include "types.h" + +void reset_cursor_mode(struct absinthe_server *server); +void process_cursor_motion(struct absinthe_server *server, uint32_t time); diff --git a/include/focus.h b/include/focus.h new file mode 100644 index 0000000..0068028 --- /dev/null +++ b/include/focus.h @@ -0,0 +1,5 @@ +#pragma once + +#include "types.h" + +void focus_toplevel(struct absinthe_toplevel *toplevel); diff --git a/include/keyboard.h b/include/keyboard.h new file mode 100644 index 0000000..43b67ee --- /dev/null +++ b/include/keyboard.h @@ -0,0 +1,7 @@ +#pragma once + +#include + +void keyboard_handle_modifiers(struct wl_listener *listener, void *data); +void keyboard_handle_key(struct wl_listener *listener, void *data); +void keyboard_handle_destroy(struct wl_listener *listener, void *data); diff --git a/include/output.h b/include/output.h new file mode 100644 index 0000000..de7b163 --- /dev/null +++ b/include/output.h @@ -0,0 +1,7 @@ +#pragma once + +#include + +void output_frame(struct wl_listener *listener, void *data); +void output_request_state(struct wl_listener *listener, void *data); +void output_destroy(struct wl_listener *listener, void *data); diff --git a/include/seat.h b/include/seat.h new file mode 100644 index 0000000..de441e1 --- /dev/null +++ b/include/seat.h @@ -0,0 +1,7 @@ +#pragma once + +#include + +void seat_request_cursor(struct wl_listener *listener, void *data); +void seat_pointer_focus_change(struct wl_listener *listener, void *data); +void seat_request_set_selection(struct wl_listener *listener, void *data); diff --git a/include/server.h b/include/server.h new file mode 100644 index 0000000..9682c30 --- /dev/null +++ b/include/server.h @@ -0,0 +1,17 @@ +#pragma once + +#include + +void server_new_output(struct wl_listener *listener, void *data); + +void server_new_xdg_toplevel(struct wl_listener *listener, void *data); + +void server_new_xdg_popup(struct wl_listener *listener, void *data); + +void server_cursor_motion(struct wl_listener *listener, void *data); +void server_cursor_motion_absolute(struct wl_listener *listener, void *data); +void server_cursor_button(struct wl_listener *listener, void *data); +void server_cursor_axis(struct wl_listener *listener, void *data); +void server_cursor_frame(struct wl_listener *listener, void *data); + +void server_new_input(struct wl_listener *listener, void *data); diff --git a/include/types.h b/include/types.h new file mode 100644 index 0000000..75ce144 --- /dev/null +++ b/include/types.h @@ -0,0 +1,110 @@ +#pragma once + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define ABSINTHE_CURSOR_MOD WLR_MODIFIER_ALT +#define ABSINTHE_CURSOR_MOVE_BUTTON BTN_LEFT +#define ABSINTHE_CURSOR_RESIZE_BUTTON BTN_RIGHT + +enum absinthe_cursor_mode { + ABSINTHE_CURSOR_PASSTHROUGH, + ABSINTHE_CURSOR_MOVE, + ABSINTHE_CURSOR_RESIZE, +}; + +enum absinthe_cursor_resize_corner { + ABSINTHE_CURSOR_RESIZE_CORNER_TOP_LEFT, + ABSINTHE_CURSOR_RESIZE_CORNER_TOP_RIGHT, + ABSINTHE_CURSOR_RESIZE_CORNER_BOTTOM_LEFT, + ABSINTHE_CURSOR_RESIZE_CORNER_BOTTOM_RIGHT, +}; + +struct absinthe_server { + struct wl_display *display; + struct wlr_backend *backend; + struct wlr_renderer *renderer; + struct wlr_allocator *allocator; + struct wlr_scene *scene; + struct wlr_scene_output_layout *scene_layout; + + struct wlr_xdg_shell *xdg_shell; + struct wl_listener new_xdg_toplevel; + struct wl_listener new_xdg_popup; + struct wl_list toplevels; + + struct wlr_cursor *cursor; + struct wlr_xcursor_manager *cursor_mgr; + struct wl_listener cursor_motion; + struct wl_listener cursor_motion_absolute; + struct wl_listener cursor_button; + struct wl_listener cursor_axis; + struct wl_listener cursor_frame; + + struct wlr_seat *seat; + struct wl_listener new_input; + struct wl_listener request_cursor; + struct wl_listener pointer_focus_change; + struct wl_listener request_set_selection; + struct wl_list keyboards; + enum absinthe_cursor_mode cursor_mode; + struct absinthe_toplevel *grabbed_toplevel; + uint32_t grabbed_toplevel_x, grabbed_toplevel_y; + uint32_t grabbed_toplevel_width, grabbed_toplevel_height; + uint32_t grab_x, grab_y; + enum absinthe_cursor_resize_corner cursor_resize_corner; + + struct wlr_output_layout *output_layout; + struct wl_list outputs; + struct wl_listener new_output; +}; + +struct absinthe_output { + struct wl_list link; + struct absinthe_server *server; + struct wlr_output *wlr_output; + struct wl_listener frame; + struct wl_listener request_state; + struct wl_listener destroy; +}; + +struct absinthe_toplevel { + struct wl_list link; + struct absinthe_server *server; + struct wlr_xdg_toplevel *xdg_toplevel; + struct wlr_scene_tree *scene_tree; + struct wl_listener map; + struct wl_listener unmap; + struct wl_listener commit; + struct wl_listener destroy; + struct wl_listener request_move; + struct wl_listener request_resize; + struct wl_listener request_maximize; + struct wl_listener request_fullscreen; +}; + +struct absinthe_popup { + struct wlr_xdg_popup *xdg_popup; + struct wl_listener commit; + struct wl_listener destroy; +}; + +struct absinthe_keyboard { + struct wl_list link; + struct absinthe_server *server; + struct wlr_keyboard *wlr_keyboard; + struct wl_listener modifiers; + struct wl_listener key; + struct wl_listener destroy; +}; diff --git a/include/xdg-popup.h b/include/xdg-popup.h new file mode 100644 index 0000000..ce8550c --- /dev/null +++ b/include/xdg-popup.h @@ -0,0 +1,6 @@ +#pragma once + +#include + +void xdg_popup_commit(struct wl_listener *listener, void *data); +void xdg_popup_destroy(struct wl_listener *listener, void *data); diff --git a/include/xdg-toplevel.h b/include/xdg-toplevel.h new file mode 100644 index 0000000..1849458 --- /dev/null +++ b/include/xdg-toplevel.h @@ -0,0 +1,12 @@ +#pragma once + +#include + +void xdg_toplevel_map(struct wl_listener *listener, void *data); +void xdg_toplevel_unmap(struct wl_listener *listener, void *data); +void xdg_toplevel_commit(struct wl_listener *listener, void *data); +void xdg_toplevel_destroy(struct wl_listener *listener, void *data); +void xdg_toplevel_request_move(struct wl_listener *listener, void *data); +void xdg_toplevel_request_resize(struct wl_listener *listener, void *data); +void xdg_toplevel_request_maximize(struct wl_listener *listener, void *data); +void xdg_toplevel_request_fullscreen(struct wl_listener *listener, void *data); diff --git a/src/absinthe-toplevel.c b/src/absinthe-toplevel.c new file mode 100644 index 0000000..f74edd5 --- /dev/null +++ b/src/absinthe-toplevel.c @@ -0,0 +1,24 @@ +#include + +#include "types.h" + +struct absinthe_toplevel *absinthe_toplevel_at(struct absinthe_server *server, double lx, double ly, struct wlr_surface **surface, double *sx, double *sy) +{ + struct wlr_scene_node *node = wlr_scene_node_at(&server->scene->tree.node, lx, ly, sx, sy); + if (!node || node->type != WLR_SCENE_NODE_BUFFER) { + return NULL; + } + + struct wlr_scene_buffer *scene_buffer = wlr_scene_buffer_from_node(node); + struct wlr_scene_surface *scene_surface = wlr_scene_surface_try_from_buffer(scene_buffer); + if (!scene_surface) { + return NULL; + } + + *surface = scene_surface->surface; + struct wlr_scene_tree *tree = node->parent; + while (tree != NULL && tree->node.data == NULL) { + tree = tree->node.parent; + } + return tree->node.data; +} diff --git a/src/cursor.c b/src/cursor.c new file mode 100644 index 0000000..9a6385e --- /dev/null +++ b/src/cursor.c @@ -0,0 +1,89 @@ +#include +#include + +#include "types.h" +#include "absinthe-toplevel.h" + +void reset_cursor_mode(struct absinthe_server *server) +{ + server->cursor_mode = ABSINTHE_CURSOR_PASSTHROUGH; + server->grabbed_toplevel = NULL; +} + +static void process_cursor_move(struct absinthe_server *server) { + struct absinthe_toplevel *toplevel = server->grabbed_toplevel; + + if (!toplevel) return; + + uint32_t new_x, new_y; + new_x = server->cursor->x - server->grab_x + server->grabbed_toplevel_x; + new_y = server->cursor->y - server->grab_y + server->grabbed_toplevel_y; + wlr_scene_node_set_position(&toplevel->scene_tree->node, new_x, new_y); +} + +static void process_cursor_resize(struct absinthe_server *server) { + struct absinthe_toplevel *toplevel = server->grabbed_toplevel; + + if (!toplevel) return; + + int32_t new_x, new_y, new_width, new_height; + new_x = server->grabbed_toplevel_x; + new_y = server->grabbed_toplevel_y; + new_width = server->grabbed_toplevel_width; + new_height = server->grabbed_toplevel_height; + + switch (server->cursor_resize_corner) { + case ABSINTHE_CURSOR_RESIZE_CORNER_TOP_LEFT: + new_x += server->cursor->x - server->grab_x; + new_y += server->cursor->y - server->grab_y; + new_width -= server->cursor->x - server->grab_x; + new_height -= server->cursor->y - server->grab_y; + break; + case ABSINTHE_CURSOR_RESIZE_CORNER_TOP_RIGHT: + new_y += server->cursor->y - server->grab_y; + new_width += server->cursor->x - server->grab_x; + new_height -= server->cursor->y - server->grab_y; + break; + case ABSINTHE_CURSOR_RESIZE_CORNER_BOTTOM_LEFT: + new_x += server->cursor->x - server->grab_x; + new_width -= server->cursor->x - server->grab_x; + new_height += server->cursor->y - server->grab_y; + break; + case ABSINTHE_CURSOR_RESIZE_CORNER_BOTTOM_RIGHT: + new_width += server->cursor->x - server->grab_x; + new_height += server->cursor->y - server->grab_y; + break; + default: // unreachable + break; + } + + if (new_width > 0 && new_height > 0) { + wlr_scene_node_set_position(&toplevel->scene_tree->node, new_x, new_y); + wlr_xdg_toplevel_set_size(toplevel->xdg_toplevel, new_width, new_height); + } +} + +void process_cursor_motion(struct absinthe_server *server, uint32_t time) +{ + if (server->cursor_mode == ABSINTHE_CURSOR_MOVE) { + process_cursor_move(server); + return; + } else if (server->cursor_mode == ABSINTHE_CURSOR_RESIZE) { + process_cursor_resize(server); + return; + } + + double sx, sy; + struct wlr_seat *seat = server->seat; + struct wlr_surface *surface = NULL; + struct absinthe_toplevel *toplevel = absinthe_toplevel_at(server, server->cursor->x, server->cursor->y, &surface, &sx, &sy); + if (!toplevel) { + wlr_cursor_set_xcursor(server->cursor, server->cursor_mgr, "default"); + } + 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); + } +} diff --git a/src/focus.c b/src/focus.c new file mode 100644 index 0000000..7693f22 --- /dev/null +++ b/src/focus.c @@ -0,0 +1,34 @@ +#include + +#include "types.h" + +void focus_toplevel(struct absinthe_toplevel *toplevel) +{ + if (!toplevel) { + return; + } + + struct absinthe_server *server = toplevel->server; + struct wlr_seat *seat = server->seat; + struct wlr_surface *prev_surface = seat->keyboard_state.focused_surface; + struct wlr_surface *surface = toplevel->xdg_toplevel->base->surface; + + if (surface == prev_surface) { + return; + } + + if (prev_surface) { + struct wlr_xdg_toplevel *prev_toplevel = wlr_xdg_toplevel_try_from_wlr_surface(prev_surface); + if (prev_toplevel) wlr_xdg_toplevel_set_activated(prev_toplevel, false); + } + + struct wlr_keyboard *keyboard = wlr_seat_get_keyboard(seat); + wlr_scene_node_raise_to_top(&toplevel->scene_tree->node); + wl_list_remove(&toplevel->link); + wl_list_insert(&server->toplevels, &toplevel->link); + wlr_xdg_toplevel_set_activated(toplevel->xdg_toplevel, true); + + if (keyboard) { + wlr_seat_keyboard_notify_enter(seat, surface, keyboard->keycodes, keyboard->num_keycodes, &keyboard->modifiers); + } +} diff --git a/src/keyboard.c b/src/keyboard.c new file mode 100644 index 0000000..efb6b94 --- /dev/null +++ b/src/keyboard.c @@ -0,0 +1,47 @@ +#include + +#include +#include + +#include "types.h" + +void keyboard_handle_modifiers(struct wl_listener *listener, void *data) +{ + struct absinthe_keyboard *keyboard = wl_container_of(listener, keyboard, modifiers); + + wlr_seat_set_keyboard(keyboard->server->seat, keyboard->wlr_keyboard); + wlr_seat_keyboard_notify_modifiers(keyboard->server->seat, &keyboard->wlr_keyboard->modifiers); +} + +static bool keyboard_handle_keybind(struct absinthe_server *server, xkb_keysym_t keysym) +{ + switch (keysym) { + case XKB_KEY_Escape: + wl_display_terminate(server->display); + break; + default: + return false; + } + return true; +} + +void keyboard_handle_key(struct wl_listener *listener, void *data) +{ + struct absinthe_keyboard *keyboard = wl_container_of(listener, keyboard, key); + struct wlr_keyboard_key_event *event = data; + + wlr_seat_set_keyboard(keyboard->server->seat, keyboard->wlr_keyboard); + wlr_seat_keyboard_notify_key(keyboard->server->seat, event->time_msec, event->keycode, event->state); +} + +void keyboard_handle_destroy(struct wl_listener *listener, void *data) +{ + struct absinthe_keyboard *keyboard = wl_container_of(listener, keyboard, modifiers); + + wl_list_remove(&keyboard->modifiers.link); + wl_list_remove(&keyboard->key.link); + wl_list_remove(&keyboard->destroy.link); + wl_list_remove(&keyboard->link); + + free(keyboard); +} diff --git a/src/main.c b/src/main.c new file mode 100644 index 0000000..a1734e5 --- /dev/null +++ b/src/main.c @@ -0,0 +1,139 @@ +#include +#include +#include +#include +#include +#include + +#include "types.h" +#include "server.h" +#include "seat.h" + +int main(int argc, char **argv) +{ + wlr_log_init(WLR_DEBUG, NULL); + + struct absinthe_server server = {0}; + + server.display = wl_display_create(); + if (!server.display) { + wlr_log(WLR_ERROR, "Failed to create wl_display"); + return 1; + } + + server.backend = wlr_backend_autocreate(wl_display_get_event_loop(server.display), NULL); + if (!server.backend) { + wlr_log(WLR_ERROR, "Failed to create wlr_backend"); + return 1; + } + + server.renderer = wlr_renderer_autocreate(server.backend); + if (!server.renderer) { + wlr_log(WLR_ERROR, "Failed to create wlr_renderer"); + return 1; + } + + wlr_renderer_init_wl_display(server.renderer, server.display); + + server.allocator = wlr_allocator_autocreate(server.backend, server.renderer); + if (!server.allocator) { + wlr_log(WLR_ERROR, "Failed to create wlr_allocator"); + return 1; + } + + wlr_compositor_create(server.display, 5, server.renderer); + wlr_subcompositor_create(server.display); + wlr_data_device_manager_create(server.display); + wlr_screencopy_manager_v1_create(server.display); + + server.output_layout = wlr_output_layout_create(server.display); + + wl_list_init(&server.outputs); + server.new_output.notify = server_new_output; + wl_signal_add(&server.backend->events.new_output, &server.new_output); + + server.scene = wlr_scene_create(); + server.scene_layout = wlr_scene_attach_output_layout(server.scene, server.output_layout); + + wl_list_init(&server.toplevels); + server.xdg_shell = wlr_xdg_shell_create(server.display, 3); + server.new_xdg_toplevel.notify = server_new_xdg_toplevel; + wl_signal_add(&server.xdg_shell->events.new_toplevel, &server.new_xdg_toplevel); + server.new_xdg_popup.notify = server_new_xdg_popup; + wl_signal_add(&server.xdg_shell->events.new_popup, &server.new_xdg_popup); + + server.cursor = wlr_cursor_create(); + wlr_cursor_attach_output_layout(server.cursor, server.output_layout); + + server.cursor_mgr = wlr_xcursor_manager_create(NULL, 24); + + server.cursor_mode = ABSINTHE_CURSOR_PASSTHROUGH; + server.cursor_motion.notify = server_cursor_motion; + wl_signal_add(&server.cursor->events.motion, &server.cursor_motion); + server.cursor_motion_absolute.notify = server_cursor_motion_absolute; + wl_signal_add(&server.cursor->events.motion_absolute, &server.cursor_motion_absolute); + server.cursor_button.notify = server_cursor_button; + wl_signal_add(&server.cursor->events.button, &server.cursor_button); + server.cursor_axis.notify = server_cursor_axis; + wl_signal_add(&server.cursor->events.axis, &server.cursor_axis); + server.cursor_frame.notify = server_cursor_frame; + wl_signal_add(&server.cursor->events.frame, &server.cursor_frame); + + wl_list_init(&server.keyboards); + server.new_input.notify = server_new_input; + wl_signal_add(&server.backend->events.new_input, &server.new_input); + server.seat = wlr_seat_create(server.display, "seat0"); + server.request_cursor.notify = seat_request_cursor; + wl_signal_add(&server.seat->events.request_set_cursor, &server.request_cursor); + server.pointer_focus_change.notify = seat_pointer_focus_change; + wl_signal_add(&server.seat->pointer_state.events.focus_change, &server.pointer_focus_change); + server.request_set_selection.notify = seat_request_set_selection; + wl_signal_add(&server.seat->events.request_set_selection, &server.request_set_selection); + + const char *socket = wl_display_add_socket_auto(server.display); + if (!socket) { + wlr_log(WLR_ERROR, "Failed to add socket"); + wlr_backend_destroy(server.backend); + return 1; + } + + if (!wlr_backend_start(server.backend)) { + wlr_log(WLR_ERROR, "Failed to start wlr_backend"); + wlr_backend_destroy(server.backend); + wl_display_destroy(server.display); + return 1; + } + + setenv("WAYLAND_DISPLAY", socket, true); + + wlr_log(WLR_INFO, "Running absinthe on WAYLAND_DISPLAY=%s", socket); + wl_display_run(server.display); + + wl_display_destroy_clients(server.display); + + wl_list_remove(&server.new_xdg_toplevel.link); + wl_list_remove(&server.new_xdg_popup.link); + + wl_list_remove(&server.cursor_motion.link); + wl_list_remove(&server.cursor_motion_absolute.link); + wl_list_remove(&server.cursor_button.link); + wl_list_remove(&server.cursor_axis.link); + wl_list_remove(&server.cursor_frame.link); + + wl_list_remove(&server.new_input.link); + wl_list_remove(&server.request_cursor.link); + wl_list_remove(&server.pointer_focus_change.link); + wl_list_remove(&server.request_set_selection.link); + + wl_list_remove(&server.new_output.link); + + wlr_scene_node_destroy(&server.scene->tree.node); + wlr_xcursor_manager_destroy(server.cursor_mgr); + wlr_cursor_destroy(server.cursor); + wlr_allocator_destroy(server.allocator); + wlr_renderer_destroy(server.renderer); + wlr_backend_destroy(server.backend); + wl_display_destroy(server.display); + + return 0; +} diff --git a/src/output.c b/src/output.c new file mode 100644 index 0000000..6172a31 --- /dev/null +++ b/src/output.c @@ -0,0 +1,35 @@ +#include + +#include "types.h" + +void output_frame(struct wl_listener *listener, void *data) +{ + struct absinthe_output *output = wl_container_of(listener, output, frame); + struct wlr_scene *scene = output->server->scene; + + struct wlr_scene_output *scene_output = wlr_scene_get_scene_output(scene, output->wlr_output); + + wlr_scene_output_commit(scene_output, NULL); + + struct timespec now; + clock_gettime(CLOCK_MONOTONIC, &now); + wlr_scene_output_send_frame_done(scene_output, &now); +} + +void output_request_state(struct wl_listener *listener, void *data) +{ + struct absinthe_output *output = wl_container_of(listener, output, request_state); + const struct wlr_output_event_request_state *event = data; + wlr_output_commit_state(output->wlr_output, event->state); +} + +void output_destroy(struct wl_listener *listener, void *data) +{ + struct absinthe_output *output = wl_container_of(listener, output, request_state); + + wl_list_remove(&output->frame.link); + wl_list_remove(&output->request_state.link); + wl_list_remove(&output->destroy.link); + wl_list_remove(&output->link); + free(output); +} diff --git a/src/seat.c b/src/seat.c new file mode 100644 index 0000000..45b9f67 --- /dev/null +++ b/src/seat.c @@ -0,0 +1,33 @@ +#include +#include + +#include "types.h" + +void seat_request_cursor(struct wl_listener *listener, void *data) +{ + struct absinthe_server *server = wl_container_of(listener, server, request_cursor); + struct wlr_seat_pointer_request_set_cursor_event *event = data; + struct wlr_seat_client *client = server->seat->pointer_state.focused_client; + + if (client == event->seat_client) { + wlr_cursor_set_surface(server->cursor, event->surface, event->hotspot_x, event->hotspot_y); + } +} + +void seat_pointer_focus_change(struct wl_listener *listener, void *data) +{ + struct absinthe_server *server = wl_container_of(listener, server, pointer_focus_change); + struct wlr_seat_pointer_focus_change_event *event = data; + + if (!event->new_surface) { + wlr_cursor_set_xcursor(server->cursor, server->cursor_mgr, "default"); + } +} + +void seat_request_set_selection(struct wl_listener *listener, void *data) +{ + struct absinthe_server *server = wl_container_of(listener, server, request_set_selection); + struct wlr_seat_request_set_selection_event *event = data; + + wlr_seat_set_selection(server->seat, event->source, event->serial); +} diff --git a/src/server.c b/src/server.c new file mode 100644 index 0000000..fc42ac7 --- /dev/null +++ b/src/server.c @@ -0,0 +1,236 @@ +#include +#include + +#include + +#include "types.h" +#include "output.h" +#include "xdg-toplevel.h" +#include "xdg-popup.h" +#include "absinthe-toplevel.h" +#include "focus.h" +#include "keyboard.h" +#include "cursor.h" + +void server_new_output(struct wl_listener *listener, void *data) +{ + struct absinthe_server *server = wl_container_of(listener, server, new_output); + struct wlr_output *wlr_output = data; + + wlr_output_init_render(wlr_output, server->allocator, server->renderer); + + struct wlr_output_state state; + wlr_output_state_init(&state); + wlr_output_state_set_enabled(&state, true); + + struct wlr_output_mode *mode = wlr_output_preferred_mode(wlr_output); + if (mode) { + wlr_output_state_set_mode(&state, mode); + } + + wlr_output_commit_state(wlr_output, &state); + wlr_output_state_finish(&state); + + struct absinthe_output *output = calloc(1, sizeof(*output)); + output->wlr_output = wlr_output; + output->server = server; + + output->frame.notify = output_frame; + wl_signal_add(&wlr_output->events.frame, &output->frame); + + output->request_state.notify = output_request_state; + wl_signal_add(&wlr_output->events.request_state, &output->request_state); + + output->destroy.notify = output_destroy; + wl_signal_add(&wlr_output->events.destroy, &output->destroy); + + struct wlr_output_layout_output *l_layout = wlr_output_layout_add_auto(server->output_layout, output->wlr_output); + struct wlr_scene_output *scene_output = wlr_scene_output_create(server->scene, wlr_output); + wlr_scene_output_layout_add_output(server->scene_layout, l_layout, scene_output); +} + +void server_new_xdg_toplevel(struct wl_listener *listener, void *data) +{ + struct absinthe_server *server = wl_container_of(listener, server, new_xdg_toplevel); + struct wlr_xdg_toplevel *xdg_toplevel = data; + + struct absinthe_toplevel *toplevel = calloc(1, sizeof(*toplevel)); + toplevel->server = server; + toplevel->xdg_toplevel = xdg_toplevel; + toplevel->scene_tree = wlr_scene_xdg_surface_create(&toplevel->server->scene->tree, xdg_toplevel->base); + toplevel->scene_tree->node.data = toplevel; + xdg_toplevel->base->data = toplevel->scene_tree; + + toplevel->map.notify = xdg_toplevel_map; + wl_signal_add(&xdg_toplevel->base->surface->events.map, &toplevel->map); + toplevel->unmap.notify = xdg_toplevel_unmap; + wl_signal_add(&xdg_toplevel->base->surface->events.unmap, &toplevel->unmap); + toplevel->commit.notify = xdg_toplevel_commit; + wl_signal_add(&xdg_toplevel->base->surface->events.commit, &toplevel->commit); + + toplevel->destroy.notify = xdg_toplevel_destroy; + wl_signal_add(&xdg_toplevel->events.destroy, &toplevel->destroy); + + toplevel->request_move.notify = xdg_toplevel_request_move; + wl_signal_add(&xdg_toplevel->events.request_move, &toplevel->request_move); + toplevel->request_resize.notify = xdg_toplevel_request_resize; + wl_signal_add(&xdg_toplevel->events.request_resize, &toplevel->request_resize); + toplevel->request_maximize.notify = xdg_toplevel_request_maximize; + wl_signal_add(&xdg_toplevel->events.request_maximize, &toplevel->request_maximize); + toplevel->request_fullscreen.notify = xdg_toplevel_request_fullscreen; + wl_signal_add(&xdg_toplevel->events.request_fullscreen, &toplevel->request_fullscreen); +} + +void server_new_xdg_popup(struct wl_listener *listener, void *data) +{ + struct absinthe_server *server = wl_container_of(listener, server, new_xdg_popup); + struct wlr_xdg_popup *xdg_popup = data; + + struct absinthe_popup *popup = calloc(1, sizeof(*popup)); + popup->xdg_popup = xdg_popup; + + struct wlr_xdg_surface *parent = wlr_xdg_surface_try_from_wlr_surface(xdg_popup->parent); + assert(parent != NULL); + struct wlr_scene_tree *parent_tree = parent->data; + xdg_popup->base->data = wlr_scene_xdg_surface_create(parent_tree, xdg_popup->base); + + popup->commit.notify = xdg_popup_commit; + wl_signal_add(&xdg_popup->base->surface->events.commit, &popup->commit); + + popup->destroy.notify = xdg_popup_destroy; + wl_signal_add(&xdg_popup->base->surface->events.destroy, &popup->destroy); +} + +void server_cursor_motion(struct wl_listener *listener, void *data) +{ + struct absinthe_server *server = wl_container_of(listener, server, cursor_motion); + struct wlr_pointer_motion_event *event = data; + wlr_cursor_move(server->cursor, &event->pointer->base, event->delta_x, event->delta_y); + process_cursor_motion(server, event->time_msec); +} + +void server_cursor_motion_absolute(struct wl_listener *listener, void *data) +{ + struct absinthe_server *server = wl_container_of(listener, server, cursor_motion_absolute); + struct wlr_pointer_motion_absolute_event *event = data; + wlr_cursor_warp_absolute(server->cursor, &event->pointer->base, event->x, event->y); + process_cursor_motion(server, event->time_msec); +} + +void server_cursor_button(struct wl_listener *listener, void *data) +{ + struct absinthe_server *server = wl_container_of(listener, server, cursor_button); + struct wlr_pointer_button_event *event = data; + + wlr_seat_pointer_notify_button(server->seat, event->time_msec, event->button, event->state); + if (event->state == WL_POINTER_BUTTON_STATE_RELEASED) { + reset_cursor_mode(server); + } else { + double sx, sy; + struct wlr_surface *surface = NULL; + struct absinthe_toplevel *toplevel = absinthe_toplevel_at(server, server->cursor->x, server->cursor->y, &surface, &sx, &sy); + focus_toplevel(toplevel); + + struct wlr_keyboard *keyboard = wlr_seat_get_keyboard(server->seat); + uint32_t mods = wlr_keyboard_get_modifiers(keyboard); + if (mods & ABSINTHE_CURSOR_MOD) { + if (event->button == ABSINTHE_CURSOR_MOVE_BUTTON) { + server->cursor_mode = ABSINTHE_CURSOR_MOVE; + } else if (event->button == ABSINTHE_CURSOR_RESIZE_BUTTON) { + server->cursor_mode = ABSINTHE_CURSOR_RESIZE; + } + } + if (toplevel) { + server->grab_x = server->cursor->x; + server->grab_y = server->cursor->y; + + int lx, ly; + wlr_scene_node_coords(&toplevel->scene_tree->node, &lx, &ly); + server->grabbed_toplevel_x = lx; + server->grabbed_toplevel_y = ly; + server->grabbed_toplevel_width = toplevel->xdg_toplevel->base->geometry.width; + server->grabbed_toplevel_height = toplevel->xdg_toplevel->base->geometry.height; + wlr_log(WLR_DEBUG, "%d, %d", server->grabbed_toplevel_width, server->grabbed_toplevel_width); + server->grabbed_toplevel = toplevel; + + int width = toplevel->xdg_toplevel->base->geometry.width; + int height = toplevel->xdg_toplevel->base->geometry.height; + + if ((int)server->grab_x > (lx + width / 2) && (int)server->grab_y > (ly + height / 2)) { + server->cursor_resize_corner = ABSINTHE_CURSOR_RESIZE_CORNER_BOTTOM_RIGHT; + } else if ((int)server->grab_x < (lx + width / 2) && (int)server->grab_y > (ly + height / 2)) { + server->cursor_resize_corner = ABSINTHE_CURSOR_RESIZE_CORNER_BOTTOM_LEFT; + } else if ((int)server->grab_x > (lx + width / 2) && (int)server->grab_y < (ly + height / 2)) { + server->cursor_resize_corner = ABSINTHE_CURSOR_RESIZE_CORNER_TOP_RIGHT; + } else { + server->cursor_resize_corner = ABSINTHE_CURSOR_RESIZE_CORNER_TOP_LEFT; + } + } + } +} + +void server_cursor_axis(struct wl_listener *listener, void *data) +{ + struct absinthe_server *server = wl_container_of(listener, server, cursor_axis); + struct wlr_pointer_axis_event *event = data; + wlr_seat_pointer_notify_axis(server->seat, event->time_msec, event->orientation, event->delta, event->delta_discrete, event->source, event->relative_direction); +} + +void server_cursor_frame(struct wl_listener *listener, void *data) +{ + struct absinthe_server *server = wl_container_of(listener, server, cursor_frame); + wlr_seat_pointer_notify_frame(server->seat); +} + +static void server_new_keyboard(struct absinthe_server *server, struct wlr_input_device *device) +{ + struct wlr_keyboard *wlr_keyboard = wlr_keyboard_from_input_device(device); + struct absinthe_keyboard *keyboard = calloc(1, sizeof(*keyboard)); + keyboard->server = server; + keyboard->wlr_keyboard = wlr_keyboard; + + 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(wlr_keyboard, keymap); + xkb_keymap_unref(keymap); + xkb_context_unref(context); + wlr_keyboard_set_repeat_info(wlr_keyboard, 25, 600); + + keyboard->modifiers.notify = keyboard_handle_modifiers; + wl_signal_add(&wlr_keyboard->events.modifiers, &keyboard->modifiers); + keyboard->key.notify = keyboard_handle_key; + wl_signal_add(&wlr_keyboard->events.key, &keyboard->key); + keyboard->destroy.notify = keyboard_handle_destroy; + wl_signal_add(&device->events.destroy, &keyboard->destroy); + + wlr_seat_set_keyboard(server->seat, wlr_keyboard); + + wl_list_insert(&server->keyboards, &keyboard->link); +} + +static void server_new_pointer(struct absinthe_server *server, struct wlr_input_device *device) +{ + wlr_cursor_attach_input_device(server->cursor, device); +} + +void server_new_input(struct wl_listener *listener, void *data) +{ + struct absinthe_server *server = wl_container_of(listener, server, new_input); + struct wlr_input_device *device = data; + switch (device->type) { + case WLR_INPUT_DEVICE_KEYBOARD: + server_new_keyboard(server, device); + break; + case WLR_INPUT_DEVICE_POINTER: + server_new_pointer(server, device); + default: + break; + } + + uint32_t caps = WL_SEAT_CAPABILITY_POINTER; + if (!wl_list_empty(&server->keyboards)) { + caps |= WL_SEAT_CAPABILITY_KEYBOARD; + } + wlr_seat_set_capabilities(server->seat, caps); +} diff --git a/src/xdg-popup.c b/src/xdg-popup.c new file mode 100644 index 0000000..9859bf8 --- /dev/null +++ b/src/xdg-popup.c @@ -0,0 +1,24 @@ +#include + +#include + +#include "types.h" + +void xdg_popup_commit(struct wl_listener *listener, void *data) +{ + struct absinthe_popup *popup = wl_container_of(listener, popup, commit); + + if (popup->xdg_popup->base->initial_commit) { + wlr_xdg_surface_schedule_configure(popup->xdg_popup->base); + } +} + +void xdg_popup_destroy(struct wl_listener *listener, void *data) +{ + struct absinthe_popup *popup = wl_container_of(listener, popup, commit); + + wl_list_remove(&popup->commit.link); + wl_list_remove(&popup->destroy.link); + + free(popup); +} diff --git a/src/xdg-toplevel.c b/src/xdg-toplevel.c new file mode 100644 index 0000000..37fb351 --- /dev/null +++ b/src/xdg-toplevel.c @@ -0,0 +1,63 @@ +#include + +#include + +#include "types.h" + +void xdg_toplevel_map(struct wl_listener *listener, void *data) +{ + struct absinthe_toplevel *toplevel = wl_container_of(listener, toplevel, map); + + wl_list_insert(&toplevel->server->toplevels, &toplevel->link); +} + +void xdg_toplevel_unmap(struct wl_listener *listener, void *data) +{ + struct absinthe_toplevel *toplevel = wl_container_of(listener, toplevel, unmap); + + wl_list_remove(&toplevel->link); +} + +void xdg_toplevel_commit(struct wl_listener *listener, void *data) +{ + struct absinthe_toplevel *toplevel = wl_container_of(listener, toplevel, commit); + + if (toplevel->xdg_toplevel->base->initial_commit) { + wlr_xdg_toplevel_set_size(toplevel->xdg_toplevel, 0, 0); + } +} + +void xdg_toplevel_destroy(struct wl_listener *listener, void *data) +{ + struct absinthe_toplevel *toplevel = wl_container_of(listener, toplevel, destroy); + + wl_list_remove(&toplevel->map.link); + wl_list_remove(&toplevel->unmap.link); + wl_list_remove(&toplevel->commit.link); + wl_list_remove(&toplevel->destroy.link); + wl_list_remove(&toplevel->request_move.link); + wl_list_remove(&toplevel->request_resize.link); + wl_list_remove(&toplevel->request_maximize.link); + wl_list_remove(&toplevel->request_fullscreen.link); + + free(toplevel); +} + +void xdg_toplevel_request_move(struct wl_listener *listener, void *data) {} +void xdg_toplevel_request_resize(struct wl_listener *listener, void *data) {} + +void xdg_toplevel_request_maximize(struct wl_listener *listener, void *data) +{ + struct absinthe_toplevel *toplevel = wl_container_of(listener, toplevel, request_maximize); + if (toplevel->xdg_toplevel->base->initialized) { + wlr_xdg_surface_schedule_configure(toplevel->xdg_toplevel->base); + } +} + +void xdg_toplevel_request_fullscreen(struct wl_listener *listener, void *data) +{ + struct absinthe_toplevel *toplevel = wl_container_of(listener, toplevel, request_fullscreen); + if (toplevel->xdg_toplevel->base->initialized) { + wlr_xdg_surface_schedule_configure(toplevel->xdg_toplevel->base); + } +}