#ifndef __CONFIG_H_ #define __CONFIG_H_ #include "keybinds.h" #include "types.h" #define CURSOR_MOD WLR_MODIFIER_ALT #define CURSOR_MOVE_BUTTON BTN_LEFT #define CURSOR_RESIZE_BUTTON BTN_RIGHT #define TOPLEVEL_BW 2 static const float bgcolor[4] = {0.0, 0.0, 0.0, 1.0}; static const float focused_bc[4] = {1.0, 0.4, 0.6, 1.0}; static const float urgent_bc[4] = {1.0, 0.0, 0.0, 1.0}; static const float unfocused_bc[4] = {0.28, 0.28, 0.28, 1.0}; #define STACK_COUNT 1 #define STACK_SIZE 0.5 #define OUTPUT_GAP 10 #define LAYOUT_GAP 5 #define ALT WLR_MODIFIER_ALT #define CTRL WLR_MODIFIER_CTRL #define SHIFT WLR_MODIFIER_SHIFT #define LOGO WLR_MODIFIER_LOGO static const char *workspaces[] = { "1", "2", "3", "4", "5", "6", "7", "8", "9", "10", }; static const absn_keybind keybinds[] = { {ALT, XKB_KEY_Escape, quit, {0}}, {ALT, XKB_KEY_Return, &run, {.v = "alacritty"}}, {ALT, XKB_KEY_q, kill_focus, {0}}, {ALT, XKB_KEY_j, cycle_focus, {.i = +1}}, {ALT, XKB_KEY_k, cycle_focus, {.i = -1}}, {ALT | SHIFT, XKB_KEY_j, swap_focus, {.i = +1}}, {ALT | SHIFT, XKB_KEY_k, swap_focus, {.i = -1}}, {ALT, XKB_KEY_f, toggle_fullscreen, {0}}, {ALT, XKB_KEY_v, toggle_floating, {0}}, {ALT, XKB_KEY_h, increase_master_count, {.i = +1}}, {ALT, XKB_KEY_l, increase_master_count, {.i = -1}}, {ALT | SHIFT, XKB_KEY_h, increase_master_width, {.f = -0.05}}, {ALT | SHIFT, XKB_KEY_l, increase_master_width, {.f = +0.05}}, {ALT, XKB_KEY_t, set_layout, {.i = LAYOUT_TILE}}, {ALT, XKB_KEY_r, set_layout, {.i = LAYOUT_TILELEFT}}, {ALT, XKB_KEY_m, set_layout, {.i = LAYOUT_MONOCLE}}, {ALT, XKB_KEY_1, switch_workspace, {.v = "1"}}, {ALT, XKB_KEY_2, switch_workspace, {.v = "2"}}, {ALT, XKB_KEY_3, switch_workspace, {.v = "3"}}, {ALT, XKB_KEY_4, switch_workspace, {.v = "4"}}, {ALT, XKB_KEY_5, switch_workspace, {.v = "5"}}, {ALT, XKB_KEY_6, switch_workspace, {.v = "6"}}, {ALT, XKB_KEY_7, switch_workspace, {.v = "7"}}, {ALT, XKB_KEY_8, switch_workspace, {.v = "8"}}, {ALT, XKB_KEY_9, switch_workspace, {.v = "9"}}, {ALT, XKB_KEY_0, switch_workspace, {.v = "10"}}, {ALT | SHIFT, XKB_KEY_exclam, move_focus_to_workspace, {.v = "1"}}, {ALT | SHIFT, XKB_KEY_at, move_focus_to_workspace, {.v = "2"}}, {ALT | SHIFT, XKB_KEY_numbersign, move_focus_to_workspace, {.v = "3"}}, {ALT | SHIFT, XKB_KEY_dollar, move_focus_to_workspace, {.v = "4"}}, {ALT | SHIFT, XKB_KEY_percent, move_focus_to_workspace, {.v = "5"}}, {ALT | SHIFT, XKB_KEY_asciicircum, move_focus_to_workspace, {.v = "6"}}, {ALT | SHIFT, XKB_KEY_ampersand, move_focus_to_workspace, {.v = "7"}}, {ALT | SHIFT, XKB_KEY_asterisk, move_focus_to_workspace, {.v = "8"}}, {ALT | SHIFT, XKB_KEY_parenleft, move_focus_to_workspace, {.v = "9"}}, {ALT | SHIFT, XKB_KEY_parenright, move_focus_to_workspace, {.v = "10"}}, }; #endif #include "types.h" void reset_cursor_mode(absn_server *server); void process_cursor_motion(absn_server *server, uint32_t time); #ifndef __FOCUS_H_ #define __FOCUS_H_ #include "types.h" void unfocus_toplevel(absn_toplevel *toplevel); void focus_toplevel(absn_toplevel *toplevel); absn_toplevel *focus_get_topmost(absn_server *server); #endif #ifndef __KEYBINDS_CALLBACKS_H #define __KEYBINDS_CALLBACKS_H #include "types.h" void run(absn_server *server, const absn_arg *arg); void kill_focus(absn_server *server, const absn_arg *arg); void cycle_focus(absn_server *server, const absn_arg *arg); void swap_focus(absn_server *server, const absn_arg *arg); void toggle_fullscreen(absn_server *server, const absn_arg *arg); void toggle_floating(absn_server *server, const absn_arg *arg); void increase_master_width(absn_server *server, const absn_arg *arg); void increase_master_count(absn_server *server, const absn_arg *arg); void switch_workspace(absn_server *server, const absn_arg *arg); void move_focus_to_workspace(absn_server *server, const absn_arg *arg); void set_layout(absn_server *server, const absn_arg *arg); void quit(absn_server *server, const absn_arg *arg); #endif #ifndef __KEYBOARD_H_ #define __KEYBOARD_H_ #include void handle_modifiers(struct wl_listener *listener, void *data); void handle_key(struct wl_listener *listener, void *data); void handle_destroy(struct wl_listener *listener, void *data); #endif #ifndef __LAYER_ARRANGE_H_ #define __LAYER_ARRANGE_H_ #include "output.h" #include "types.h" void layers_arrange(absn_output *output); #endif #ifndef __LAYER_SURFACE_H_ #define __LAYER_SURFACE_H_ #include void layer_surface_unmap(struct wl_listener *listener, void *data); void layer_surface_commit(struct wl_listener *listener, void *data); void layer_surface_new_popup(struct wl_listener *listener, void *data); void layer_surface_destroy(struct wl_listener *listener, void *data); #endif #ifndef __LAYOUT_H_ #define __LAYOUT_H_ #include "types.h" void layout_arrange(absn_output *output); #endif #ifndef __OUTPUT_H_ #define __OUTPUT_H_ #include #include "types.h" 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); void output_layout_change(struct wl_listener *listener, void *data); void update_focused_output(absn_server *server); void output_arrange(absn_output *output); #endif #ifndef __SEAT_H_ #define __SEAT_H_ #include void request_cursor(struct wl_listener *listener, void *data); void pointer_focus_change(struct wl_listener *listener, void *data); void request_set_selection(struct wl_listener *listener, void *data); #endif #ifndef __SERVER_H_ #define __SERVER_H_ #include void new_output(struct wl_listener *listener, void *data); void new_xdg_toplevel(struct wl_listener *listener, void *data); void new_xdg_popup(struct wl_listener *listener, void *data); void new_xdg_decoration(struct wl_listener *listener, void *data); void new_layer_surface(struct wl_listener *listener, void *data); #ifdef XWAYLAND void xwayland_ready(struct wl_listener *listener, void *data); void xwayland_new_surface(struct wl_listener *listener, void *data); #endif void cursor_motion(struct wl_listener *listener, void *data); void cursor_motion_abs(struct wl_listener *listener, void *data); void cursor_button(struct wl_listener *listener, void *data); void cursor_axis(struct wl_listener *listener, void *data); void cursor_frame(struct wl_listener *listener, void *data); void new_input(struct wl_listener *listener, void *data); #endif #ifndef __TOPLEVEL_H_ #define __TOPLEVEL_H_ #include "types.h" absn_toplevel *toplevel_at(absn_server *server, double lx, double ly, struct wlr_surface **surface, double *sx, double *sy); bool toplevel_is_unmanaged(absn_toplevel *toplevel); void toplevel_get_geom(absn_toplevel *toplevel); void toplevel_set_pos(absn_toplevel *toplevel, int32_t x, int32_t y); void toplevel_set_size(absn_toplevel *toplevel, int32_t width, int32_t height); void toplevel_set_geom(absn_toplevel *toplevel, struct wlr_box *geom); void toplevel_set_floating(absn_toplevel *toplevel, bool floating); void toplevel_set_fullscreen(absn_toplevel *toplevel, bool fullscreen); void toplevel_set_border_color(absn_toplevel *toplevel, const float color[4]); #endif #ifndef __TOPLEVLE_HANDLERS_H_ #define __TOPLEVLE_HANDLERS_H_ #include void toplevel_map(struct wl_listener *listener, void *data); void toplevel_unmap(struct wl_listener *listener, void *data); void toplevel_destroy(struct wl_listener *listener, void *data); void toplevel_request_move(struct wl_listener *listener, void *data); void toplevel_request_resize(struct wl_listener *listener, void *data); void toplevel_request_maximize(struct wl_listener *listener, void *data); void toplevel_request_fullscreen(struct wl_listener *listener, void *data); #endif #ifndef __TYPES_H_ #define __TYPES_H_ #include #include #include #include #include #include #include #include #include #include #include #include #include #ifdef XWAYLAND #include #include #include #endif #define BTN_LEFT 0x110 #define BTN_RIGHT 0x111 #define BTN_MIDDLE 0x112 #define MAX(A, B) (A) > (B) ? (A) : (B) #define MIN(A, B) (A) < (B) ? (A) : (B) /* macro for adding listener for event */ #define LISTEN(L, C, E) \ do { \ (L).notify = (C); \ wl_signal_add(&(E), &(L)); \ } while (0); #define UNUSED(X) (void)(X) #define CLEANMASK(M) (M & ~WLR_MODIFIER_CAPS) /* cursor mode */ enum { CURSOR_PASSTHROUGH, CURSOR_MOVE, CURSOR_RESIZE, }; /* resizing corners */ enum { TOP_LEFT, TOP_RIGHT, BOTTOM_LEFT, BOTTOM_RIGHT, }; /* toplevel types */ enum { TOPLEVEL_XDG, TOPLEVEL_X11, LAYER_SURFACE, }; /* layers for wlr layer shell */ enum { LAYER_BACKGROUND, LAYER_BOTTOM, LAYER_TILE, LAYER_FLOAT, LAYER_FULLSCREEN, LAYER_TOP, LAYER_OVERLAY, LAYERS_COUNT, }; static const int layermap[] = { LAYER_BACKGROUND, LAYER_BOTTOM, LAYER_TOP, LAYER_OVERLAY, }; enum { LAYOUT_TILE, LAYOUT_TILELEFT, LAYOUT_TILETOP, LAYOUT_TILEBOTTOM, LAYOUT_MONOCLE, LAYOUT_SPIRAL, LAYOUT_DWINDLE, LAYOUT_VGRID, LAYOUT_HGRID, }; typedef struct absn_output absn_output; typedef struct absn_toplevel absn_toplevel; typedef struct { int layout; const char *name; absn_output *output; int count; float size; } absn_workspace; typedef struct { struct wl_display *display; struct wlr_backend *backend; struct wlr_renderer *renderer; struct wlr_allocator *allocator; struct wlr_compositor *compositor; struct wlr_scene *scene; struct wlr_scene_output_layout *scene_layout; struct wlr_scene_rect *bg; struct wlr_xdg_shell *xdg_shell; struct wl_listener new_xdg_toplevel; struct wl_listener new_xdg_popup; struct wlr_xdg_decoration_manager_v1 *xdg_deco_mgr; struct wl_listener new_xdg_deco; struct wlr_layer_shell_v1 *layer_shell; struct wl_listener new_layer_surface; struct wlr_scene_tree *layers[LAYERS_COUNT]; #ifdef XWAYLAND struct wlr_xwayland *xwayland; struct wl_listener xw_new_surface; struct wl_listener xw_ready; #endif struct wlr_cursor *cursor; struct wlr_xcursor_manager *cursor_mgr; struct wl_listener cursor_motion; struct wl_listener cursor_motion_abs; 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; int cursor_mode; int resize_corner; struct wlr_box grab_geom; int32_t grab_x, grab_y; struct wl_list toplevels; struct wl_list focus_stack; absn_toplevel *focused_toplevel; absn_output *focused_output; struct wl_list outputs; struct wl_listener new_output; struct wlr_output_layout *output_layout; struct wl_listener layout_change; struct wlr_output_manager_v1 *output_mgr; struct wl_listener mgr_apply; struct wl_listener mgr_test; int workspaces_count; absn_workspace *workspaces; } absn_server; struct absn_output { struct wl_list link; absn_server *server; absn_workspace *workspace; struct wlr_box geom; struct wlr_box usable_area; struct wlr_output *wlr; struct wl_listener frame; struct wl_listener request_state; struct wl_listener destroy; struct wl_list layers[4]; }; typedef struct { struct wl_list link; absn_server *server; absn_output *output; bool mapped; struct wlr_scene_tree *scene_tree; struct wlr_scene_layer_surface_v1 *scene_layer; struct wlr_scene_tree *popups; struct wlr_layer_surface_v1 *wlr; struct wl_listener map; struct wl_listener unmap; struct wl_listener commit; struct wl_listener new_popup; struct wl_listener destroy; } absn_layer_surface; struct absn_toplevel { struct wl_list link; struct wl_list flink; /* link for focus stack */ uint32_t tag; absn_server *server; absn_output *output; absn_workspace *workspace; struct wlr_scene_tree *scene_tree; struct wlr_scene_tree *scene_surface; int32_t bw; struct wlr_scene_rect *border[4]; struct wlr_xdg_toplevel_decoration_v1 *deco; bool floating; bool fullscreen; bool urgent; uint32_t resizing; struct wlr_box geom; struct wlr_box prev_geom; int type; union { struct wlr_xdg_toplevel *xdg; struct wlr_xwayland_surface *xw; }; 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 wl_listener deco_request_mode; struct wl_listener deco_destroy; #ifdef XWAYLAND struct wl_listener xw_activate; struct wl_listener xw_associate; struct wl_listener xw_dissociate; struct wl_listener xw_configure; struct wl_listener xw_set_hints; #endif }; typedef struct { struct wlr_xdg_popup *wlr; int parent_type; void *parent; struct wl_listener commit; } absn_popup; typedef struct { struct wl_list link; absn_server *server; struct wlr_keyboard *wlr; struct wl_listener modifiers; struct wl_listener key; struct wl_listener destroy; } absn_keyboard; typedef struct { int i; float f; const void *v; } absn_arg; typedef struct { uint32_t mods; xkb_keysym_t keysym; void (*cb)(absn_server *, const absn_arg *); const absn_arg arg; } absn_keybind; #endif /* __TYPES_H_ */ #ifndef __XDG_DECORATION_H_ #define __XDG_DECORATION_H_ #include void deco_request_mode(struct wl_listener *listener, void *data); void deco_destroy(struct wl_listener *listener, void *data); #endif #ifndef __XDG_POPUP_H #define __XDG_POPUP_H void xdg_popup_commit(struct wl_listener *listener, void *data); #endif #ifndef __XDG_TOPLEVEL_H_ #define __XDG_TOPLEVEL_H_ #include void toplevel_commit(struct wl_listener *listener, void *data); #endif #ifndef __XWAYLAND_H_ #define __XWAYLAND_H_ #include void xwayland_activate(struct wl_listener *listener, void *data); void xwayland_associate(struct wl_listener *listener, void *data); void xwayland_dissociate(struct wl_listener *listener, void *data); void xwayland_configure(struct wl_listener *listener, void *data); void xwayland_set_hints(struct wl_listener *listener, void *data); #endif #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "config.h" #include "output.h" #include "seat.h" #include "server.h" #include "types.h" static int setup(absn_server *server) { wlr_log_init(WLR_DEBUG, NULL); 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_shm(server->renderer, server->display); server->scene = wlr_scene_create(); if (wlr_renderer_get_texture_formats(server->renderer, WLR_BUFFER_CAP_DMABUF)) { wlr_drm_create(server->display, server->renderer); wlr_scene_set_linux_dmabuf_v1(server->scene, wlr_linux_dmabuf_v1_create_with_renderer(server->display, 5, server->renderer)); } server->bg = wlr_scene_rect_create(&server->scene->tree, 0, 0, bgcolor); server->allocator = wlr_allocator_autocreate(server->backend, server->renderer); if (!server->allocator) { wlr_log(WLR_ERROR, "Failed to create wlr_allocator"); return 1; } /* wlroots managers */ server->compositor = wlr_compositor_create(server->display, 6, server->renderer); wlr_subcompositor_create(server->display); wlr_data_device_manager_create(server->display); wlr_screencopy_manager_v1_create(server->display); wlr_data_control_manager_v1_create(server->display); wlr_viewporter_create(server->display); wlr_single_pixel_buffer_manager_v1_create(server->display); wlr_fractional_scale_manager_v1_create(server->display, 1); wlr_presentation_create(server->display, server->backend, 2); wlr_alpha_modifier_v1_create(server->display); wlr_export_dmabuf_manager_v1_create(server->display); wlr_ext_foreign_toplevel_list_v1_create(server->display, 1); wlr_server_decoration_manager_set_default_mode(wlr_server_decoration_manager_create(server->display), WLR_SERVER_DECORATION_MANAGER_MODE_SERVER); server->xdg_deco_mgr = wlr_xdg_decoration_manager_v1_create(server->display); LISTEN(server->new_xdg_deco, new_xdg_decoration, server->xdg_deco_mgr->events.new_toplevel_decoration); /* toplevels lists */ wl_list_init(&server->toplevels); wl_list_init(&server->focus_stack); /* output */ wl_list_init(&server->outputs); LISTEN(server->new_output, new_output, server->backend->events.new_output); server->output_layout = wlr_output_layout_create(server->display); LISTEN(server->layout_change, output_layout_change, server->output_layout->events.change); server->output_mgr = wlr_output_manager_v1_create(server->display); wlr_xdg_output_manager_v1_create(server->display, server->output_layout); server->scene_layout = wlr_scene_attach_output_layout(server->scene, server->output_layout); /* xdg_shell */ server->xdg_shell = wlr_xdg_shell_create(server->display, 6); LISTEN(server->new_xdg_toplevel, new_xdg_toplevel, server->xdg_shell->events.new_toplevel); LISTEN(server->new_xdg_popup, new_xdg_popup, server->xdg_shell->events.new_popup); /* cursor */ 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 = CURSOR_PASSTHROUGH; LISTEN(server->cursor_motion, cursor_motion, server->cursor->events.motion); LISTEN(server->cursor_motion_abs, cursor_motion_abs, server->cursor->events.motion_absolute); LISTEN(server->cursor_button, cursor_button, server->cursor->events.button); LISTEN(server->cursor_axis, cursor_axis, server->cursor->events.axis); LISTEN(server->cursor_frame, cursor_frame, server->cursor->events.frame); /* keyboard */ wl_list_init(&server->keyboards); server->seat = wlr_seat_create(server->display, "seat0"); LISTEN(server->new_input, new_input, server->backend->events.new_input); LISTEN(server->request_cursor, request_cursor, server->seat->events.request_set_cursor); LISTEN(server->pointer_focus_change, pointer_focus_change, server->seat->pointer_state.events.focus_change); LISTEN(server->request_set_selection, request_cursor, server->seat->events.request_set_selection); /* layer shell */ server->layer_shell = wlr_layer_shell_v1_create(server->display, 1); for (int i = 0; i < LAYERS_COUNT; ++i) { server->layers[i] = wlr_scene_tree_create(&server->scene->tree); } /* workspaces */ int workspaces_count = sizeof(workspaces) / sizeof(workspaces[0]); server->workspaces_count = workspaces_count; server->workspaces = calloc(workspaces_count, sizeof(absn_workspace)); for (int i = 0; i < workspaces_count; ++i) { server->workspaces[i].layout = LAYOUT_TILE; server->workspaces[i].name = workspaces[i]; server->workspaces[i].count = STACK_COUNT; server->workspaces[i].size = STACK_SIZE; server->workspaces[i].output = NULL; } unsetenv("DISPLAY"); #ifdef XWAYLAND if ((server->xwayland = wlr_xwayland_create(server->display, server->compositor, 1))) { LISTEN(server->xw_ready, xwayland_ready, server->xwayland->events.ready); LISTEN(server->xw_new_surface, xwayland_new_surface, server->xwayland->events.new_surface); setenv("DISPLAY", server->xwayland->display_name, 1); wlr_log(WLR_INFO, "Running XWayland, DISPLAY=%s", server->xwayland->display_name); } else { wlr_log(WLR_ERROR, "Failed to setup XWayland, continuing without it"); } #endif 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); return 0; } static void cleanup(absn_server *server) { 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->new_xdg_deco.link); wl_list_remove(&server->cursor_motion.link); wl_list_remove(&server->cursor_motion_abs.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); wl_list_remove(&server->new_layer_surface.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); } int main(void) { absn_server server = {0}; int err = setup(&server); if (err) { return EXIT_FAILURE; } wl_display_run(server.display); cleanup(&server); return EXIT_SUCCESS; } #include #include #include "layout.h" #include "toplevel.h" #include "types.h" void reset_cursor_mode(absn_server *server) { server->cursor_mode = CURSOR_PASSTHROUGH; wlr_cursor_set_xcursor(server->cursor, server->cursor_mgr, "default"); if (server->focused_toplevel) { wlr_xdg_toplevel_set_resizing(server->focused_toplevel->xdg, false); } } static void process_cursor_move(absn_server *server) { struct absn_toplevel *toplevel = server->focused_toplevel; if (!toplevel) { return; } if (toplevel->fullscreen) { toplevel->prev_geom = toplevel->geom; toplevel_set_fullscreen(toplevel, false); } uint32_t new_x, new_y; new_x = server->cursor->x - server->grab_x + server->grab_geom.x; new_y = server->cursor->y - server->grab_y + server->grab_geom.y; toplevel_set_pos(toplevel, new_x, new_y); if (!toplevel->floating) { toplevel_set_floating(toplevel, true); } } static void apply_resize(absn_toplevel *toplevel, struct wlr_box *new_geom) { if (toplevel->type == TOPLEVEL_XDG) { int32_t min_width = toplevel->xdg->current.min_width; int32_t min_height = toplevel->xdg->current.min_height; int32_t max_width = toplevel->xdg->current.max_width; int32_t max_height = toplevel->xdg->current.max_height; if (max_width == 0) { max_width = 10000; } if (max_height == 0) { max_height = 10000; } if (!(new_geom->width >= min_width && new_geom->width <= max_width)) { new_geom->width = toplevel->geom.width; new_geom->x = toplevel->geom.x; } if (!(new_geom->height >= min_height && new_geom->height <= max_height)) { new_geom->height = toplevel->geom.height; new_geom->y = toplevel->geom.y; } } toplevel_set_geom(toplevel, new_geom); } static void process_cursor_resize(absn_server *server) { struct absn_toplevel *toplevel = server->focused_toplevel; if (!toplevel) { return; } if (toplevel->resizing) { return; } if (toplevel->fullscreen) { toplevel_set_fullscreen(toplevel, false); } if (!toplevel->floating) { toplevel_set_floating(toplevel, true); } int32_t new_x, new_y, new_width, new_height; new_x = server->grab_geom.x; new_y = server->grab_geom.y; new_width = server->grab_geom.width; new_height = server->grab_geom.height; int32_t dx = server->cursor->x - server->grab_x; int32_t dy = server->cursor->y - server->grab_y; if (dx == 0 && dy == 0) { return; } switch (server->resize_corner) { case TOP_LEFT: new_x += dx; new_y += dy; new_width -= dx; new_height -= dy; break; case TOP_RIGHT: new_y += dy; new_width += dx; new_height -= dy; break; case BOTTOM_LEFT: new_x += dx; new_width -= dx; new_height += dy; break; case BOTTOM_RIGHT: new_width += dx; new_height += dy; break; default: // unreachable break; } if (new_width > 0 && new_height > 0) { struct wlr_box new_geometry = { .x = new_x, .y = new_y, .width = new_width, .height = new_height, }; apply_resize(server->focused_toplevel, &new_geometry); } } void process_cursor_motion(absn_server *server, uint32_t time) { double sx, sy; struct wlr_surface *surface = NULL; toplevel_at(server, server->cursor->x, server->cursor->y, &surface, &sx, &sy); struct wlr_seat *seat = server->seat; if (server->cursor_mode == CURSOR_MOVE) { process_cursor_move(server); return; } else if (server->cursor_mode == CURSOR_RESIZE) { process_cursor_resize(server); return; } 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); } } #include #include #include "config.h" #include "focus.h" #include "toplevel.h" #include "types.h" void unfocus_toplevel(absn_toplevel *toplevel) { if (!toplevel) { return; } if (toplevel->type == TOPLEVEL_XDG) { wlr_xdg_toplevel_set_activated(toplevel->xdg, false); } toplevel_set_border_color(toplevel, unfocused_bc); } void focus_toplevel(absn_toplevel *toplevel) { if (!toplevel) { return; } absn_server *server = toplevel->server; struct wlr_seat *seat = server->seat; struct wlr_surface *prev_surface = seat->keyboard_state.focused_surface; struct wlr_surface *surface; #ifdef XWAYLAND if (toplevel->type == TOPLEVEL_X11) { surface = toplevel->xw->surface; } else #endif surface = toplevel->xdg->base->surface; if (surface == prev_surface) { return; } if (prev_surface) { unfocus_toplevel(prev_surface->data); } toplevel->server->focused_toplevel = toplevel; struct wlr_keyboard *keyboard = wlr_seat_get_keyboard(seat); wlr_scene_node_raise_to_top(&toplevel->scene_tree->node); wl_list_remove(&toplevel->flink); wl_list_insert(&server->focus_stack, &toplevel->flink); if (toplevel->type != TOPLEVEL_X11) { wlr_xdg_toplevel_set_activated(toplevel->xdg, true); } toplevel_set_border_color(toplevel, focused_bc); if (keyboard) { wlr_seat_keyboard_notify_enter(seat, surface, keyboard->keycodes, keyboard->num_keycodes, &keyboard->modifiers); } } /* * get first toplevel on monitor * (last focused toplevel) */ absn_toplevel *focus_get_topmost(absn_server *server) { absn_toplevel *toplevel; wl_list_for_each(toplevel, &server->focus_stack, flink) { if (toplevel && toplevel->workspace == server->focused_output->workspace) { return toplevel; } } return NULL; } #include #include #include #include #include "focus.h" #include "layout.h" #include "output.h" #include "toplevel.h" #include "types.h" void run(absn_server *server, const absn_arg *arg) { UNUSED(server); if (fork() == 0) { setsid(); execl("/bin/sh", "sh", "-c", arg->v, NULL); } } void kill_focus(absn_server *server, const absn_arg *arg) { UNUSED(arg); if (!server->focused_toplevel) { return; } absn_toplevel *focus = server->focused_toplevel; #ifdef XWAYLAND if (focus->type == TOPLEVEL_X11) { wlr_xwayland_surface_close(focus->xw); } else #endif { wlr_xdg_toplevel_send_close(focus->xdg); } } void cycle_focus(absn_server *server, const absn_arg *arg) { absn_toplevel *toplevel = focus_get_topmost(server); if (!toplevel || toplevel->fullscreen) { return; } absn_toplevel *new_focus; if (arg->i > 0) { wl_list_for_each(new_focus, &toplevel->link, link) { if (toplevel->workspace == new_focus->workspace) { break; } } } else { wl_list_for_each_reverse(new_focus, &toplevel->link, link) { if (toplevel->workspace == new_focus->workspace) { break; } } } focus_toplevel(new_focus); } void swap_focus(absn_server *server, const absn_arg *arg) { absn_toplevel *toplevel = focus_get_topmost(server); if (!toplevel || toplevel->fullscreen || toplevel->floating) { return; } absn_toplevel *temp = NULL; absn_toplevel *first = NULL; absn_toplevel *last = NULL; wl_list_for_each(temp, &server->toplevels, link) { if (temp->workspace == toplevel->workspace) { last = temp; } if (!first && temp->workspace == toplevel->workspace) { first = temp; } } if (first == last) { return; } absn_toplevel *swap = NULL; if (arg->i > 0) { if (toplevel == last) { wl_list_remove(&toplevel->link); wl_list_insert(&server->toplevels, &toplevel->link); goto arrange; } wl_list_for_each(swap, &toplevel->link, link) { if (toplevel->workspace == swap->workspace) { break; } } wl_list_remove(&toplevel->link); wl_list_insert(&swap->link, &toplevel->link); } else { if (toplevel == first) { wl_list_remove(&toplevel->link); wl_list_insert(server->toplevels.prev, &toplevel->link); goto arrange; } wl_list_for_each_reverse(swap, &toplevel->link, link) { if (toplevel->workspace == swap->workspace) { break; } } wl_list_remove(&swap->link); wl_list_insert(&toplevel->link, &swap->link); } arrange: layout_arrange(toplevel->output); } void toggle_fullscreen(absn_server *server, const absn_arg *arg) { UNUSED(arg); if (!server->focused_toplevel) { return; } absn_toplevel *focus = server->focused_toplevel; toplevel_set_fullscreen(focus, !focus->fullscreen); } void toggle_floating(absn_server *server, const absn_arg *arg) { UNUSED(arg); if (!server->focused_toplevel) { return; } absn_toplevel *focus = server->focused_toplevel; toplevel_set_floating(focus, !focus->floating); if (focus->floating) { int32_t new_x = focus->output->geom.x + (focus->output->geom.width / 2 - focus->geom.width / 2); int32_t new_y = focus->output->geom.y + (focus->output->geom.height / 2 - focus->geom.height / 2); toplevel_set_pos(focus, new_x, new_y); } } void increase_master_width(absn_server *server, const absn_arg *arg) { absn_workspace *workspace = server->focused_output->workspace; if (workspace->size + arg->f >= 1.0 || workspace->size + arg->f <= 0.0) { return; } workspace->size += arg->f; layout_arrange(workspace->output); } void increase_master_count(absn_server *server, const absn_arg *arg) { absn_workspace *workspace = server->focused_output->workspace; if (workspace->count + arg->i <= 0) { return; } workspace->count += arg->i; layout_arrange(workspace->output); } void switch_workspace(absn_server *server, const absn_arg *arg) { int i; for (i = 0; i < server->workspaces_count; ++i) { if (arg->v == server->workspaces[i].name) { break; /* found it */ } } if (&server->workspaces[i] == server->focused_output->workspace) { return; } int toplevels_count = 0; absn_toplevel *toplevel; wl_list_for_each(toplevel, &server->toplevels, link) { if (toplevel->workspace == server->focused_output->workspace) { toplevels_count++; } } if (toplevels_count == 0) { server->focused_output->workspace->output = NULL; } if (!server->workspaces[i].output) { server->workspaces[i].output = server->focused_output; } else if (server->focused_output == server->workspaces[i].output) { server->focused_output->workspace = &server->workspaces[i]; } else { server->focused_output = server->workspaces[i].output; struct wlr_box *geom = &server->focused_output->geom; int32_t new_x = geom->x + (geom->width / 2); int32_t new_y = geom->y + (geom->height / 2); wlr_cursor_warp(server->cursor, NULL, new_x, new_y); } server->focused_output->workspace = &server->workspaces[i]; absn_toplevel *focus = NULL; wl_list_for_each(toplevel, &server->toplevels, link) { if (!focus && toplevel && toplevel->workspace == &server->workspaces[i]) { focus = toplevel; /* found it */ } if (toplevel && toplevel->workspace == &server->workspaces[i] && toplevel->fullscreen) { /* overwrite if there is a fullscreen window */ focus = toplevel; break; } } unfocus_toplevel(server->focused_toplevel); if (focus) { /* crazy way to make toplevel focus properly if it was focused before */ struct wlr_surface *surface; #ifdef XWAYLAND if (focus->type == TOPLEVEL_X11) { surface = focus->xw->surface; } else #endif surface = focus->xdg->base->surface; if (surface == server->seat->keyboard_state.focused_surface) { server->seat->keyboard_state.focused_surface = NULL; } focus_toplevel(focus); } layout_arrange(server->focused_output); } void move_focus_to_workspace(absn_server *server, const absn_arg *arg) { if (!server->focused_toplevel) { return; } if (server->focused_toplevel->fullscreen) { toplevel_set_fullscreen(server->focused_toplevel, false); } int i; for (i = 0; i < server->workspaces_count; ++i) { if (arg->v == server->workspaces[i].name) { break; /* found it */ } } if (&server->workspaces[i] == server->focused_toplevel->workspace) { return; } if (!server->workspaces[i].output) { server->workspaces[i].output = server->focused_output; } server->focused_toplevel->output = server->workspaces[i].output; server->focused_toplevel->workspace = &server->workspaces[i]; focus_toplevel(focus_get_topmost(server)); layout_arrange(server->focused_output); layout_arrange(server->focused_toplevel->output); } void set_layout(absn_server *server, const absn_arg *arg) { server->focused_output->workspace->layout = arg->i; layout_arrange(server->focused_output); } noreturn void quit(absn_server *server, const absn_arg *arg) { UNUSED(arg); wl_display_terminate(server->display); exit(EXIT_SUCCESS); } #include #include #include #include #include #include #include "config.h" #include "types.h" void handle_modifiers(struct wl_listener *listener, void *data) { UNUSED(data); absn_keyboard *keyboard = wl_container_of(listener, keyboard, modifiers); wlr_seat_set_keyboard(keyboard->server->seat, keyboard->wlr); wlr_seat_keyboard_notify_modifiers(keyboard->server->seat, &keyboard->wlr->modifiers); } static bool handle_keybind(absn_server *server, uint32_t mods, xkb_keysym_t keysym) { int nkeyb = sizeof(keybinds) / sizeof(keybinds[0]); for (int i = 0; i < nkeyb; ++i) { if (CLEANMASK(keybinds[i].mods) == CLEANMASK(mods) && xkb_keysym_to_lower(keybinds[i].keysym) == xkb_keysym_to_lower(keysym)) { keybinds[i].cb(server, &keybinds[i].arg); return true; } } return false; } void handle_key(struct wl_listener *listener, void *data) { absn_keyboard *keyboard = wl_container_of(listener, keyboard, key); struct wlr_keyboard_key_event *event = data; /* translate keycode to xkbcommon */ uint32_t code = event->keycode + 8; uint32_t mods = wlr_keyboard_get_modifiers(keyboard->wlr); const xkb_keysym_t *syms; int nsyms = xkb_state_key_get_syms(keyboard->wlr->xkb_state, code, &syms); bool handled = false; if (event->state == WL_KEYBOARD_KEY_STATE_PRESSED) { for (int i = 0; i < nsyms; ++i) { handled = handle_keybind(keyboard->server, mods, syms[i]) || handled; } } if (handled) { return; } wlr_seat_set_keyboard(keyboard->server->seat, keyboard->wlr); wlr_seat_keyboard_notify_key(keyboard->server->seat, event->time_msec, event->keycode, event->state); } void handle_destroy(struct wl_listener *listener, void *data) { UNUSED(data); absn_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); } #include #include "layout.h" #include "output.h" #include "types.h" /* arrange layers logic from dwl */ static void layer_arrange(absn_output *output, struct wl_list *list, struct wlr_box *usable_area, int exclusive) { absn_layer_surface *layer_surface; wl_list_for_each(layer_surface, list, link) { struct wlr_layer_surface_v1 *surface = layer_surface->wlr; if (!surface->initialized) { continue; } if (exclusive != (surface->current.exclusive_zone > 0)) { continue; } wlr_scene_layer_surface_v1_configure(layer_surface->scene_layer, &output->geom, usable_area); wlr_scene_node_set_position(&layer_surface->popups->node, layer_surface->scene_tree->node.x, layer_surface->scene_tree->node.y); } } void layers_arrange(absn_output *output) { if (!output->wlr->enabled) { return; } struct wlr_box usable_area = output->usable_area; /* Arrange non-exclusive surfaces from top->bottom */ for (int i = 3; i >= 0; --i) { layer_arrange(output, &output->layers[i], &usable_area, 1); } if (!wlr_box_equal(&output->usable_area, &usable_area)) { output->usable_area = usable_area; output_arrange(output); } /* Arrange non-exclusive surfaces from top->bottom */ for (int i = 3; i >= 0; --i) { layer_arrange(output, &output->layers[i], &usable_area, 0); } /* disabled for now uint32_t layers_above_shell[] = { ZWLR_LAYER_SHELL_V1_LAYER_OVERLAY, ZWLR_LAYER_SHELL_V1_LAYER_TOP, }; int layers_above_shell_count = sizeof(layers_above_shell) / sizeof(layers_above_shell[0]); absn_layer_surface *layer_surface; for (int i = 0; i < layers_above_shell_count; ++i) { wl_list_for_each_reverse(layer_surface, &output->layers[layers_above_shell[i]], link) { if (!layer_surface->wlr->current.keyboard_interactive || !layer_surface->mapped) continue; unfocus_toplevel(output->server->focused_toplevel); output->server->exclusive_focus = l; client_notify_enter(l->layer_surface->surface, wlr_seat_get_keyboard(seat)); return; } } */ } #include #include "focus.h" #include "layers.h" #include "xdg-popup.h" #include "types.h" void layer_surface_unmap(struct wl_listener *listener, void *data) { UNUSED(data); absn_layer_surface *layer_surface = wl_container_of(listener, layer_surface, unmap); if (layer_surface->wlr->surface == layer_surface->server->seat->keyboard_state.focused_surface) { focus_toplevel(focus_get_topmost(layer_surface->server)); } layer_surface->mapped = false; } void layer_surface_commit(struct wl_listener *listener, void *data) { UNUSED(data); absn_layer_surface *layer_surface = wl_container_of(listener, layer_surface, commit); struct wlr_layer_surface_v1 *surface = layer_surface->wlr; struct wlr_scene_tree *scene_layer = layer_surface->server->layers[layermap[surface->current.layer]]; if (surface->initial_commit) { struct wlr_layer_surface_v1_state old_state = surface->current; surface->current = surface->pending; layers_arrange(layer_surface->output); surface->current = old_state; return; } if (surface->current.committed == 0 || layer_surface->mapped == surface->surface->mapped) { return; } layer_surface->mapped = surface->surface->mapped; if (scene_layer != layer_surface->scene_tree->node.parent) { wlr_scene_node_reparent(&layer_surface->scene_tree->node, scene_layer); wl_list_remove(&layer_surface->link); wl_list_insert(&layer_surface->output->layers[surface->current.layer], &layer_surface->link); bool on_top_layer = surface->current.layer == ZWLR_LAYER_SHELL_V1_LAYER_TOP; if (on_top_layer) { wlr_scene_node_reparent(&layer_surface->popups->node, layer_surface->output->server->layers[LAYER_TOP]); } else { wlr_scene_node_reparent(&layer_surface->popups->node, scene_layer); } } layers_arrange(layer_surface->output); } void layer_surface_new_popup(struct wl_listener *listener, void *data) { UNUSED(data); absn_layer_surface *layer_surface = wl_container_of(listener, layer_surface, new_popup); struct wlr_xdg_popup *xdg_popup = data; wlr_scene_xdg_surface_create(layer_surface->popups, xdg_popup->base); absn_popup *popup = calloc(1, sizeof(*popup)); popup->wlr = xdg_popup; popup->parent_type = LAYER_SURFACE; popup->parent = layer_surface; LISTEN(popup->commit, xdg_popup_commit, xdg_popup->base->surface->events.commit); } void layer_surface_destroy(struct wl_listener *listener, void *data) { UNUSED(data); absn_layer_surface *layer_surface = wl_container_of(listener, layer_surface, destroy); wl_list_remove(&layer_surface->link); wl_list_remove(&layer_surface->map.link); wl_list_remove(&layer_surface->unmap.link); wl_list_remove(&layer_surface->commit.link); wl_list_remove(&layer_surface->new_popup.link); wl_list_remove(&layer_surface->destroy.link); wlr_scene_node_destroy(&layer_surface->scene_tree->node); free(layer_surface); } #include #include "config.h" #include "output.h" #include "toplevel.h" #include "types.h" /* returns number of toplevels on workspace */ static int prepare_output(struct absn_output *output) { int res = 0; absn_toplevel *toplevel; wl_list_for_each(toplevel, &output->server->toplevels, link) { if (toplevel->workspace == output->workspace) { wlr_scene_node_set_enabled(&toplevel->scene_tree->node, true); if (!toplevel->floating && !toplevel->fullscreen) { res++; } } else if (toplevel->output == output && toplevel->workspace != output->workspace) { wlr_scene_node_set_enabled(&toplevel->scene_tree->node, false); } } return res; } static void tile(struct absn_output *output) { int toplevels_count = prepare_output(output); if (toplevels_count < 1) { return; } int32_t og = OUTPUT_GAP; struct wlr_box new_geom; absn_toplevel *toplevel; if (toplevels_count == 1) { wl_list_for_each(toplevel, &output->server->toplevels, link) { if (toplevel->workspace == output->workspace && !toplevel->floating && !toplevel->fullscreen) { break; } } new_geom.x = output->usable_area.x + og, new_geom.y = output->usable_area.y + og, new_geom.width = output->usable_area.width - 2 * og, new_geom.height = output->usable_area.height - 2 * og, toplevel_set_geom(toplevel, &new_geom); return; } int32_t lg = LAYOUT_GAP; int32_t total_lg; int mcount = output->workspace->count; float msize = output->workspace->size; int32_t main_stack_width = (toplevels_count <= mcount) ? output->usable_area.width - 2 * og : msize * (output->usable_area.width - 2 * og); int32_t w = output->usable_area.width - main_stack_width - 2 * og - lg; int32_t pure_h; int32_t h; int32_t cur_h; int32_t r; int32_t dy = og; int i = 0; if (toplevels_count <= mcount) { total_lg = (toplevels_count - 1) * lg; pure_h = output->usable_area.height - 2 * og - total_lg; h = pure_h / toplevels_count; r = pure_h % toplevels_count; wl_list_for_each(toplevel, &output->server->toplevels, link) { if (toplevel->workspace != output->workspace || toplevel->floating || toplevel->fullscreen) { continue; } cur_h = h + (i < r ? 1 : 0); new_geom.x = output->usable_area.x + og; new_geom.y = output->usable_area.y + dy; new_geom.width = main_stack_width; new_geom.height = cur_h; toplevel_set_geom(toplevel, &new_geom); dy += cur_h + lg; i++; } return; } wl_list_for_each(toplevel, &output->server->toplevels, link) { if (toplevel->workspace != output->workspace || toplevel->floating || toplevel->fullscreen) { continue; } if (i < mcount) { total_lg = (mcount - 1) * lg; pure_h = output->usable_area.height - 2 * og - total_lg; h = pure_h / mcount; r = pure_h % mcount; cur_h = h + (i < r ? 1 : 0); new_geom.x = output->usable_area.x + og; new_geom.y = output->usable_area.y + dy; new_geom.width = main_stack_width; new_geom.height = cur_h; toplevel_set_geom(toplevel, &new_geom); dy += cur_h + lg; } else { if (i == mcount) { dy = og; } int32_t stack_count = toplevels_count - mcount; total_lg = (stack_count - 1) * lg; pure_h = output->usable_area.height - 2 * og - total_lg; h = pure_h / stack_count; r = pure_h % stack_count; cur_h = h + (i - mcount < r ? 1 : 0); new_geom.x = output->usable_area.x + main_stack_width + lg + og; new_geom.y = output->usable_area.y + dy; new_geom.width = w; new_geom.height = cur_h; toplevel_set_geom(toplevel, &new_geom); dy += cur_h + lg; } i++; } } static void tile_left(struct absn_output *output) { int toplevels_count = prepare_output(output); if (toplevels_count < 1) { return; } int32_t og = OUTPUT_GAP; struct wlr_box new_geom; absn_toplevel *toplevel; if (toplevels_count == 1) { wl_list_for_each(toplevel, &output->server->toplevels, link) { if (toplevel->workspace == output->workspace && !toplevel->floating && !toplevel->fullscreen) { break; } } new_geom.x = output->usable_area.x + og, new_geom.y = output->usable_area.y + og, new_geom.width = output->usable_area.width - 2 * og, new_geom.height = output->usable_area.height - 2 * og, toplevel_set_geom(toplevel, &new_geom); return; } int32_t lg = LAYOUT_GAP; int32_t total_lg; int mcount = output->workspace->count; float msize = output->workspace->size; int32_t main_stack_width = (toplevels_count <= mcount) ? output->usable_area.width - 2 * og : msize * (output->usable_area.width - 2 * og); int32_t w = output->usable_area.width - main_stack_width - 2 * og - lg; int32_t pure_h; int32_t h; int32_t cur_h; int32_t r; int32_t dy = og; int i = 0; if (toplevels_count <= mcount) { total_lg = (toplevels_count - 1) * lg; pure_h = output->usable_area.height - 2 * og - total_lg; h = pure_h / toplevels_count; r = pure_h % toplevels_count; wl_list_for_each(toplevel, &output->server->toplevels, link) { if (toplevel->workspace != output->workspace || toplevel->floating || toplevel->fullscreen) { continue; } cur_h = h + (i < r ? 1 : 0); new_geom.x = output->usable_area.x + og; new_geom.y = output->usable_area.y + dy; new_geom.width = main_stack_width; new_geom.height = cur_h; toplevel_set_geom(toplevel, &new_geom); dy += cur_h + lg; i++; } return; } wl_list_for_each(toplevel, &output->server->toplevels, link) { if (toplevel->workspace != output->workspace || toplevel->floating || toplevel->fullscreen) { continue; } if (i < mcount) { total_lg = (mcount - 1) * lg; pure_h = output->usable_area.height - 2 * og - total_lg; h = pure_h / mcount; r = pure_h % mcount; cur_h = h + (i < r ? 1 : 0); new_geom.x = output->usable_area.x + output->usable_area.width - og - main_stack_width; new_geom.y = output->usable_area.y + dy; new_geom.width = main_stack_width; new_geom.height = cur_h; toplevel_set_geom(toplevel, &new_geom); dy += cur_h + lg; } else { if (i == mcount) { dy = og; } int32_t stack_count = toplevels_count - mcount; total_lg = (stack_count - 1) * lg; pure_h = output->usable_area.height - 2 * og - total_lg; h = pure_h / stack_count; r = pure_h % stack_count; cur_h = h + (i - mcount < r ? 1 : 0); new_geom.x = output->usable_area.x + og; new_geom.y = output->usable_area.y + dy; new_geom.width = w; new_geom.height = cur_h; toplevel_set_geom(toplevel, &new_geom); dy += cur_h + lg; } i++; } } static void monocle(struct absn_output *output) { int toplevels_count = prepare_output(output); if (toplevels_count < 1) { return; } int32_t og = OUTPUT_GAP; struct wlr_box new_geom = { .x = output->usable_area.x + og, .y = output->usable_area.y + og, .width = output->usable_area.width - 2 * og, .height = output->usable_area.height - 2 * og, }; absn_toplevel *toplevel; wl_list_for_each(toplevel, &output->server->toplevels, link) { if (toplevel->workspace != output->workspace || toplevel->floating || toplevel->fullscreen) { continue; } toplevel_set_geom(toplevel, &new_geom); } } void layout_arrange(struct absn_output *output) { if (!output) { return; } switch (output->workspace->layout) { case LAYOUT_TILE: tile(output); break; case LAYOUT_TILELEFT: tile_left(output); break; case LAYOUT_MONOCLE: monocle(output); break; default: /* currently not implemented layouts */ tile(output); break; } } #include #include #include "focus.h" #include "layout.h" #include "toplevel.h" #include "types.h" void output_frame(struct wl_listener *listener, void *data) { UNUSED(data); struct timespec now; absn_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); // absn_toplevel *toplevel; // wl_list_for_each(toplevel, &output->server->toplevels, link) // { // if (toplevel->resizing && toplevel->output == output) { // goto skip; // } // } wlr_scene_output_commit(scene_output, NULL); skip: 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 absn_output *output = wl_container_of(listener, output, request_state); const struct wlr_output_event_request_state *event = data; wlr_output_commit_state(output->wlr, event->state); } void output_destroy(struct wl_listener *listener, void *data) { UNUSED(data); struct absn_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); } void output_layout_change(struct wl_listener *listener, void *data) { UNUSED(data); absn_server *server = wl_container_of(listener, server, layout_change); struct wlr_output_configuration_v1 *config = wlr_output_configuration_v1_create(); absn_toplevel *toplevel = NULL; absn_output *output; struct wlr_output_configuration_head_v1 *config_head; wl_list_for_each(output, &server->outputs, link) { if (output->wlr->enabled) { continue; } config_head = wlr_output_configuration_head_v1_create(config, output->wlr); config_head->state.enabled = 0; wlr_output_layout_remove(server->output_layout, output->wlr); // closemon(m); output->geom = output->usable_area = (struct wlr_box){0}; } wl_list_for_each(output, &server->outputs, link) { if (output->wlr->enabled && !wlr_output_layout_get(server->output_layout, output->wlr)) { wlr_output_layout_add_auto(server->output_layout, toplevel->output->wlr); } } struct wlr_box layout_geom; wlr_output_layout_get_box(server->output_layout, NULL, &layout_geom); wlr_scene_node_set_position(&server->bg->node, layout_geom.x, layout_geom.y); wlr_scene_rect_set_size(server->bg, layout_geom.width, layout_geom.height); wl_list_for_each(output, &server->outputs, link) { if (!output->wlr->enabled) { continue; } config_head = wlr_output_configuration_head_v1_create(config, output->wlr); wlr_output_layout_get_box(server->output_layout, output->wlr, &output->geom); output->usable_area = output->geom; if ((toplevel = focus_get_topmost(server)) && toplevel->fullscreen) { toplevel_set_size(toplevel, output->geom.width, output->geom.height); } config_head->state.x = output->geom.x; config_head->state.y = output->geom.y; if (!server->focused_output) { server->focused_output = output; } } if (server->focused_output && server->focused_output->wlr->enabled) { wl_list_for_each(toplevel, &server->toplevels, link) { if (!toplevel->output) { toplevel->output = server->focused_output; } } layout_arrange(server->focused_output); } focus_toplevel(focus_get_topmost(server)); wlr_cursor_move(server->cursor, NULL, 0, 0); wlr_output_manager_v1_set_configuration(server->output_mgr, config); } void update_focused_output(absn_server *server) { absn_output *output; int32_t cursor_x = server->cursor->x; int32_t cursor_y = server->cursor->y; wl_list_for_each(output, &server->outputs, link) { bool cursor_in_output_x = cursor_x >= output->geom.x && cursor_x <= output->geom.x + output->geom.width; bool cursor_in_output_y = cursor_y >= output->geom.y && cursor_y <= output->geom.y + output->geom.height; if (cursor_in_output_x && cursor_in_output_y) { server->focused_output = output; break; } } } void output_arrange(absn_output *output) { if (!output->wlr->enabled) return; absn_server *server = output->server; bool enabled; absn_toplevel *toplevel; wl_list_for_each(toplevel, &server->toplevels, link) { if (toplevel->output == output) { enabled = output->workspace == toplevel->workspace; wlr_scene_node_set_enabled(&toplevel->scene_tree->node, enabled); } } wl_list_for_each(toplevel, &server->toplevels, link) { if (toplevel->output != output || toplevel->scene_tree->node.parent == server->layers[LAYER_FULLSCREEN]) { continue; } wlr_scene_node_reparent(&toplevel->scene_tree->node, toplevel->scene_tree->node.parent); } } #include #include #include "types.h" void request_cursor(struct wl_listener *listener, void *data) { absn_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 pointer_focus_change(struct wl_listener *listener, void *data) { absn_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 request_set_selection(struct wl_listener *listener, void *data) { absn_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); } #include #include #include #include #include #include "config.h" #include "cursor.h" #include "focus.h" #include "keyboard.h" #include "layers.h" #include "layer-surface.h" #include "output.h" #include "xdg-popup.h" #include "toplevel-handlers.h" #include "toplevel.h" #include "types.h" #include "xdg-decoration.h" #include "xdg-toplevel.h" #ifdef XWAYLAND #include #include "xwayland.h" #endif void new_output(struct wl_listener *listener, void *data) { absn_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 absn_output *output = calloc(1, sizeof(*output)); output->wlr = wlr_output; output->server = server; for (int i = 0; i < server->workspaces_count; ++i) { if (!server->workspaces[i].output) { server->workspaces[i].output = output; output->workspace = &server->workspaces[i]; break; } } LISTEN(output->frame, output_frame, wlr_output->events.frame); LISTEN(output->request_state, output_request_state, wlr_output->events.request_state); LISTEN(output->destroy, output_destroy, wlr_output->events.destroy); wl_list_insert(&server->outputs, &output->link); for (int i = 0; i < 4; ++i) { wl_list_init(&output->layers[i]); } struct wlr_output_layout_output *l_layout = wlr_output_layout_add_auto(server->output_layout, output->wlr); 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); wlr_output_layout_get_box(server->output_layout, output->wlr, &output->geom); output->usable_area = output->geom; } void new_xdg_toplevel(struct wl_listener *listener, void *data) { absn_server *server = wl_container_of(listener, server, new_xdg_toplevel); struct wlr_xdg_toplevel *xdg_toplevel = data; absn_toplevel *toplevel = calloc(1, sizeof(*toplevel)); toplevel->type = TOPLEVEL_XDG; toplevel->server = server; toplevel->xdg = xdg_toplevel; toplevel->xdg->base->data = toplevel; toplevel->xdg->base->surface->data = toplevel; wlr_surface_set_preferred_buffer_scale(toplevel->xdg->base->surface, 1); LISTEN(toplevel->commit, toplevel_commit, xdg_toplevel->base->surface->events.commit); LISTEN(toplevel->map, toplevel_map, xdg_toplevel->base->surface->events.map); LISTEN(toplevel->unmap, toplevel_unmap, xdg_toplevel->base->surface->events.unmap); LISTEN(toplevel->destroy, toplevel_destroy, xdg_toplevel->events.destroy); LISTEN(toplevel->request_move, toplevel_request_move, xdg_toplevel->events.request_move); LISTEN(toplevel->request_resize, toplevel_request_resize, xdg_toplevel->events.request_resize); LISTEN(toplevel->request_maximize, toplevel_request_maximize, xdg_toplevel->events.request_maximize); LISTEN(toplevel->request_fullscreen, toplevel_request_fullscreen, xdg_toplevel->events.request_fullscreen); } void new_xdg_popup(struct wl_listener *listener, void *data) { absn_server *server = wl_container_of(listener, server, new_xdg_popup); struct wlr_xdg_popup *xdg_popup = data; struct wlr_xdg_surface *parent = wlr_xdg_surface_try_from_wlr_surface(xdg_popup->parent); if (!parent) { return; } absn_toplevel *toplevel = parent->data; wlr_scene_xdg_surface_create(toplevel->scene_tree, xdg_popup->base); absn_popup *popup = calloc(1, sizeof(*popup)); popup->wlr = xdg_popup; popup->parent_type = TOPLEVEL_XDG; popup->parent = toplevel; LISTEN(popup->commit, xdg_popup_commit, xdg_popup->base->surface->events.commit); } void new_xdg_decoration(struct wl_listener *listener, void *data) { UNUSED(listener); struct wlr_xdg_toplevel_decoration_v1 *deco = data; absn_toplevel *toplevel = deco->toplevel->base->data; toplevel->deco = deco; LISTEN(toplevel->deco_request_mode, deco_request_mode, deco->events.request_mode); LISTEN(toplevel->deco_destroy, deco_destroy, deco->events.destroy); /* Forse server side decoration mode */ deco_request_mode(&toplevel->deco_request_mode, toplevel->deco); } void new_layer_surface(struct wl_listener *listener, void *data) { absn_server *server = wl_container_of(listener, server, new_layer_surface); struct wlr_layer_surface_v1 *surface = data; if (!surface->output && !(surface->output = server->focused_output->wlr)) { wlr_layer_surface_v1_destroy(surface); return; } absn_layer_surface *layer_surface = calloc(1, sizeof(*layer_surface)); LISTEN(layer_surface->unmap, layer_surface_unmap, surface->surface->events.unmap); LISTEN(layer_surface->commit, layer_surface_commit, surface->surface->events.commit); LISTEN(layer_surface->new_popup, layer_surface_new_popup, surface->events.new_popup); LISTEN(layer_surface->destroy, layer_surface_destroy, surface->surface->events.destroy); layer_surface->wlr = surface; layer_surface->output = surface->output->data; struct wlr_scene_tree *scene_layer = server->layers[layermap[surface->pending.layer]]; layer_surface->scene_layer = wlr_scene_layer_surface_v1_create(scene_layer, surface); layer_surface->scene_tree = layer_surface->scene_layer->tree; layer_surface->popups = wlr_scene_tree_create(surface->current.layer < ZWLR_LAYER_SHELL_V1_LAYER_TOP ? server->layers[LAYER_TOP] : scene_layer); wl_list_insert(&layer_surface->output->layers[surface->pending.layer], &layer_surface->link); wlr_surface_send_enter(surface->surface, surface->output); } #ifdef XWAYLAND void xwayland_ready(struct wl_listener *listener, void *data) { UNUSED(data); absn_server *server = wl_container_of(listener, server, xw_ready); wlr_xwayland_set_seat(server->xwayland, server->seat); struct wlr_xcursor *xcursor; if ((xcursor = wlr_xcursor_manager_get_xcursor(server->cursor_mgr, "default", 1))) { struct wlr_buffer *buffer = wlr_xcursor_image_get_buffer(xcursor->images[0]); wlr_xwayland_set_cursor(server->xwayland, buffer, xcursor->images[0]->hotspot_x, xcursor->images[0]->hotspot_y); } } void xwayland_new_surface(struct wl_listener *listener, void *data) { absn_server *server = wl_container_of(listener, server, xw_new_surface); struct wlr_xwayland_surface *surface = data; absn_toplevel *toplevel = calloc(1, sizeof(*toplevel)); toplevel->type = TOPLEVEL_X11; toplevel->server = server; toplevel->xw = surface; toplevel->bw = toplevel_is_unmanaged(toplevel) ? 0 : TOPLEVEL_BW; surface->data = toplevel; LISTEN(toplevel->destroy, toplevel_destroy, surface->events.destroy); LISTEN(toplevel->request_maximize, toplevel_request_maximize, surface->events.request_maximize); LISTEN(toplevel->request_fullscreen, toplevel_request_fullscreen, surface->events.request_fullscreen); LISTEN(toplevel->xw_activate, xwayland_activate, surface->events.request_activate); LISTEN(toplevel->xw_associate, xwayland_associate, surface->events.associate); LISTEN(toplevel->xw_dissociate, xwayland_dissociate, surface->events.dissociate); LISTEN(toplevel->xw_configure, xwayland_configure, surface->events.request_configure); LISTEN(toplevel->xw_set_hints, xwayland_set_hints, surface->events.set_hints); } #endif void cursor_motion(struct wl_listener *listener, void *data) { absn_server *server = wl_container_of(listener, server, cursor_motion); struct wlr_pointer_motion_event *event = data; update_focused_output(server); wlr_cursor_move(server->cursor, &event->pointer->base, event->delta_x, event->delta_y); process_cursor_motion(server, event->time_msec); } void cursor_motion_abs(struct wl_listener *listener, void *data) { absn_server *server = wl_container_of(listener, server, cursor_motion_abs); struct wlr_pointer_motion_absolute_event *event = data; update_focused_output(server); wlr_cursor_warp_absolute(server->cursor, &event->pointer->base, event->x, event->y); process_cursor_motion(server, event->time_msec); } void cursor_button(struct wl_listener *listener, void *data) { absn_server *server = wl_container_of(listener, server, cursor_button); struct wlr_pointer_button_event *event = data; bool handled = false; if (event->state == WL_POINTER_BUTTON_STATE_RELEASED) { reset_cursor_mode(server); } else { double sx, sy; struct wlr_surface *surface = NULL; absn_toplevel *toplevel = toplevel_at(server, server->cursor->x, server->cursor->y, &surface, &sx, &sy); if (!toplevel) { goto handle; } struct wlr_keyboard *keyboard = wlr_seat_get_keyboard(server->seat); uint32_t mods = wlr_keyboard_get_modifiers(keyboard); if (mods & CURSOR_MOD) { if (event->button == CURSOR_MOVE_BUTTON) { server->cursor_mode = CURSOR_MOVE; wlr_cursor_set_xcursor(server->cursor, server->cursor_mgr, "all-scroll"); handled = true; } else if (event->button == CURSOR_RESIZE_BUTTON) { server->cursor_mode = CURSOR_RESIZE; handled = true; } } focus_toplevel(toplevel); server->grab_x = server->cursor->x; server->grab_y = server->cursor->y; int32_t lx, ly; wlr_scene_node_coords(&toplevel->scene_tree->node, &lx, &ly); server->grab_geom.x = lx; server->grab_geom.y = ly; server->grab_geom.width = toplevel->geom.width; server->grab_geom.height = toplevel->geom.height; if (server->cursor_mode != CURSOR_RESIZE) { goto handle; } int32_t width = toplevel->xdg->base->geometry.width; int32_t height = toplevel->xdg->base->geometry.height; if (server->grab_x > (lx + width / 2) && server->grab_y > (ly + height / 2)) { server->resize_corner = BOTTOM_RIGHT; wlr_cursor_set_xcursor(server->cursor, server->cursor_mgr, "se-resize"); } else if (server->grab_x < (lx + width / 2) && server->grab_y > (ly + height / 2)) { server->resize_corner = BOTTOM_LEFT; wlr_cursor_set_xcursor(server->cursor, server->cursor_mgr, "sw-resize"); } else if (server->grab_x > (lx + width / 2) && server->grab_y < (ly + height / 2)) { server->resize_corner = TOP_RIGHT; wlr_cursor_set_xcursor(server->cursor, server->cursor_mgr, "ne-resize"); } else { server->resize_corner = TOP_LEFT; wlr_cursor_set_xcursor(server->cursor, server->cursor_mgr, "nw-resize"); } wlr_xdg_toplevel_set_resizing(toplevel->xdg, true); } handle: if (!handled) { wlr_seat_pointer_notify_button(server->seat, event->time_msec, event->button, event->state); } } void cursor_axis(struct wl_listener *listener, void *data) { absn_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 cursor_frame(struct wl_listener *listener, void *data) { UNUSED(data); absn_server *server = wl_container_of(listener, server, cursor_frame); wlr_seat_pointer_notify_frame(server->seat); } static void new_keyboard(absn_server *server, struct wlr_input_device *device) { struct wlr_keyboard *wlr_keyboard = wlr_keyboard_from_input_device(device); absn_keyboard *keyboard = calloc(1, sizeof(*keyboard)); keyboard->server = server; keyboard->wlr = 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); LISTEN(keyboard->modifiers, handle_modifiers, wlr_keyboard->events.modifiers); LISTEN(keyboard->key, handle_key, wlr_keyboard->events.key); LISTEN(keyboard->destroy, handle_destroy, wlr_keyboard->base.events.destroy); wlr_seat_set_keyboard(server->seat, wlr_keyboard); wl_list_insert(&server->keyboards, &keyboard->link); } static void new_pointer(absn_server *server, struct wlr_input_device *device) { wlr_cursor_attach_input_device(server->cursor, device); } void new_input(struct wl_listener *listener, void *data) { absn_server *server = wl_container_of(listener, server, new_input); struct wlr_input_device *device = data; switch (device->type) { case WLR_INPUT_DEVICE_KEYBOARD: new_keyboard(server, device); break; case WLR_INPUT_DEVICE_POINTER: 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); } #include #include #include "config.h" #include "layout.h" #include "toplevel.h" #include "types.h" #include "xdg-shell-protocol.h" /* * returns toplevel at given cursor coordinates, * its surface and coordinates inside of it * to process input event */ absn_toplevel *toplevel_at(absn_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) { return NULL; } struct wlr_scene_buffer *scene_buffer = NULL; struct wlr_scene_surface *scene_surface = NULL; switch (node->type) { case WLR_SCENE_NODE_BUFFER: scene_buffer = wlr_scene_buffer_from_node(node); 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; break; case WLR_SCENE_NODE_RECT: return node->data; break; default: return NULL; } } /* * if toplevel is unmanaged we should not * move or resize it and just draw it * where it wants */ bool toplevel_is_unmanaged(absn_toplevel *toplevel) { #ifdef XWAYLAND if (toplevel->type == TOPLEVEL_X11) { return toplevel->xw->override_redirect; } #endif return false; } /* used only to get initial window size */ void toplevel_get_geom(absn_toplevel *toplevel) { #ifdef XWAYLAND if (toplevel->type == TOPLEVEL_X11) { toplevel->geom.x = toplevel->xw->x; toplevel->geom.y = toplevel->xw->y; toplevel->geom.width = toplevel->xw->width; toplevel->geom.height = toplevel->xw->height; } else #endif { toplevel->geom = toplevel->xdg->base->geometry; } } static void toplevel_update_borders_geom(absn_toplevel *toplevel) { int32_t bw = toplevel->bw; if (toplevel->geom.width - 2 * bw < 0 || toplevel->geom.height - 2 * bw < 0) { return; } wlr_scene_node_set_position(&toplevel->scene_tree->node, toplevel->geom.x, toplevel->geom.y); wlr_scene_node_set_position(&toplevel->scene_surface->node, bw, bw); wlr_scene_rect_set_size(toplevel->border[0], toplevel->geom.width - 2 * bw, bw); wlr_scene_rect_set_size(toplevel->border[1], toplevel->geom.width - 2 * bw, bw); wlr_scene_rect_set_size(toplevel->border[2], bw, toplevel->geom.height); wlr_scene_rect_set_size(toplevel->border[3], bw, toplevel->geom.height); wlr_scene_node_set_position(&toplevel->border[0]->node, bw, 0); wlr_scene_node_set_position(&toplevel->border[1]->node, bw, toplevel->geom.height - bw); wlr_scene_node_set_position(&toplevel->border[2]->node, 0, 0); wlr_scene_node_set_position(&toplevel->border[3]->node, toplevel->geom.width - bw, 0); } void toplevel_set_pos(absn_toplevel *toplevel, int32_t x, int32_t y) { toplevel->geom.x = x; toplevel->geom.y = y; wlr_scene_node_set_position(&toplevel->scene_tree->node, x, y); } void toplevel_set_size(absn_toplevel *toplevel, int32_t width, int32_t height) { if (width <= 2 * toplevel->bw || height <= 2 * toplevel->bw || (width == toplevel->geom.width && height == toplevel->geom.height)) { return; } toplevel->geom.width = width; toplevel->geom.height = height; toplevel_update_borders_geom(toplevel); int32_t bw = toplevel->bw; struct wlr_box clip = { .x = 0, .y = 0, .width = width - bw, .height = height - bw, }; if (toplevel->type == TOPLEVEL_XDG) { if (wl_resource_get_version(toplevel->xdg->resource) >= XDG_TOPLEVEL_CONFIGURE_BOUNDS_SINCE_VERSION) { wlr_xdg_toplevel_set_bounds(toplevel->xdg, width, height); } toplevel->resizing = wlr_xdg_toplevel_set_size(toplevel->xdg, width - 2 * bw, height - 2 * bw); } #ifdef XWAYLAND else if (toplevel->type == TOPLEVEL_X11) { wlr_xwayland_surface_configure(toplevel->xw, toplevel->geom.x, toplevel->geom.y, width - 2 * bw, height - 2 * toplevel->bw); /* manually update position */ toplevel_set_pos(toplevel, toplevel->geom.x, toplevel->geom.y); } #endif wlr_scene_subsurface_tree_set_clip(&toplevel->scene_surface->node, &clip); } void toplevel_set_geom(absn_toplevel *toplevel, struct wlr_box *geom) { toplevel_set_pos(toplevel, geom->x, geom->y); toplevel_set_size(toplevel, geom->width, geom->height); } void toplevel_set_floating(absn_toplevel *toplevel, bool floating) { if (!toplevel || toplevel->floating == floating) { return; } toplevel->floating = floating; if (floating) { wlr_scene_node_reparent(&toplevel->scene_tree->node, toplevel->server->layers[LAYER_FLOAT]); } else { wlr_scene_node_reparent(&toplevel->scene_tree->node, toplevel->server->layers[LAYER_TILE]); } layout_arrange(toplevel->output); } void toplevel_set_fullscreen(absn_toplevel *toplevel, bool fullscreen) { if (!toplevel || toplevel->fullscreen == fullscreen) { return; } absn_output *output = toplevel->server->focused_output; toplevel->fullscreen = fullscreen; wlr_xdg_toplevel_set_fullscreen(toplevel->xdg, fullscreen); if (fullscreen) { toplevel->prev_geom = toplevel->geom; toplevel->bw = 0; toplevel_set_geom(toplevel, &output->geom); wlr_scene_node_reparent(&toplevel->scene_tree->node, toplevel->server->layers[LAYER_FULLSCREEN]); } else { toplevel->bw = toplevel_is_unmanaged(toplevel) ? 0 : TOPLEVEL_BW; toplevel_set_geom(toplevel, &toplevel->prev_geom); if (toplevel->floating) { wlr_scene_node_reparent(&toplevel->scene_tree->node, toplevel->server->layers[LAYER_FLOAT]); } else { wlr_scene_node_reparent(&toplevel->scene_tree->node, toplevel->server->layers[LAYER_TILE]); } } toplevel_update_borders_geom(toplevel); layout_arrange(toplevel->output); } void toplevel_set_border_color(absn_toplevel *toplevel, const float color[4]) { if (!toplevel) { return; } for (int i = 0; i < 4; ++i) { wlr_scene_rect_set_color(toplevel->border[i], color); } } #include #include #include "config.h" #include "focus.h" #include "layout.h" #include "output.h" #include "toplevel.h" void toplevel_map(struct wl_listener *listener, void *data) { UNUSED(data); absn_toplevel *toplevel = wl_container_of(listener, toplevel, map); absn_server *server = toplevel->server; toplevel->scene_tree = wlr_scene_tree_create(server->layers[LAYER_TILE]); toplevel->scene_tree->node.data = toplevel; wlr_scene_node_set_enabled(&toplevel->scene_tree->node, toplevel_is_unmanaged(toplevel)); if (toplevel->type != TOPLEVEL_X11 && wl_resource_get_version(toplevel->xdg->resource) >= XDG_TOPLEVEL_STATE_TILED_RIGHT_SINCE_VERSION) { wlr_xdg_toplevel_set_tiled(toplevel->xdg, WLR_EDGE_TOP | WLR_EDGE_BOTTOM | WLR_EDGE_LEFT | WLR_EDGE_RIGHT); } else { wlr_xdg_toplevel_set_maximized(toplevel->xdg, true); } toplevel->bw = toplevel_is_unmanaged(toplevel) ? 0 : TOPLEVEL_BW; #ifdef XWAYLAND if (toplevel->type == TOPLEVEL_X11) { toplevel->scene_surface = wlr_scene_subsurface_tree_create(toplevel->scene_tree, toplevel->xw->surface); } else #endif { toplevel->scene_surface = wlr_scene_xdg_surface_create(toplevel->scene_tree, toplevel->xdg->base); } toplevel->scene_surface->node.data = toplevel; toplevel_get_geom(toplevel); toplevel->bw = toplevel_is_unmanaged(toplevel) ? 0 : TOPLEVEL_BW; for (int i = 0; i < 4; ++i) { toplevel->border[i] = wlr_scene_rect_create(toplevel->scene_tree, 0, 0, unfocused_bc); toplevel->border[i]->node.data = toplevel; } update_focused_output(toplevel->server); toplevel->output = toplevel->server->focused_output; toplevel->workspace = toplevel->output->workspace; toplevel->fullscreen = false; wl_list_insert(&toplevel->server->toplevels, &toplevel->link); wl_list_insert(&toplevel->server->focus_stack, &toplevel->flink); layout_arrange(toplevel->output); focus_toplevel(focus_get_topmost(toplevel->server)); } void toplevel_unmap(struct wl_listener *listener, void *data) { UNUSED(data); absn_toplevel *toplevel = wl_container_of(listener, toplevel, unmap); if (toplevel == toplevel->server->focused_toplevel) { toplevel->server->focused_toplevel = NULL; toplevel->server->seat->keyboard_state.focused_surface = NULL; } wl_list_remove(&toplevel->link); wl_list_remove(&toplevel->flink); layout_arrange(toplevel->output); focus_toplevel(focus_get_topmost(toplevel->server)); wlr_scene_node_destroy(&toplevel->scene_tree->node); } void toplevel_destroy(struct wl_listener *listener, void *data) { UNUSED(data); absn_toplevel *toplevel = wl_container_of(listener, toplevel, destroy); #ifdef XWAYLAND if (toplevel->type == TOPLEVEL_X11) { wl_list_remove(&toplevel->xw_activate.link); wl_list_remove(&toplevel->xw_associate.link); wl_list_remove(&toplevel->xw_dissociate.link); wl_list_remove(&toplevel->xw_configure.link); wl_list_remove(&toplevel->xw_set_hints.link); } else #endif { wl_list_remove(&toplevel->map.link); wl_list_remove(&toplevel->unmap.link); wl_list_remove(&toplevel->commit.link); wl_list_remove(&toplevel->request_move.link); wl_list_remove(&toplevel->request_resize.link); } wl_list_remove(&toplevel->destroy.link); wl_list_remove(&toplevel->request_maximize.link); wl_list_remove(&toplevel->request_fullscreen.link); free(toplevel); } void toplevel_request_move(struct wl_listener *listener, void *data) { UNUSED(data); absn_toplevel *toplevel = wl_container_of(listener, toplevel, request_move); if (toplevel->xdg->base->initialized) { wlr_xdg_surface_schedule_configure(toplevel->xdg->base); } } void toplevel_request_resize(struct wl_listener *listener, void *data) { UNUSED(data); absn_toplevel *toplevel = wl_container_of(listener, toplevel, request_resize); if (toplevel->xdg->base->initialized) { wlr_xdg_surface_schedule_configure(toplevel->xdg->base); } } void toplevel_request_maximize(struct wl_listener *listener, void *data) { UNUSED(data); absn_toplevel *toplevel = wl_container_of(listener, toplevel, request_maximize); if (toplevel->xdg->base->initialized) { wlr_xdg_surface_schedule_configure(toplevel->xdg->base); } } void toplevel_request_fullscreen(struct wl_listener *listener, void *data) { UNUSED(data); absn_toplevel *toplevel = wl_container_of(listener, toplevel, request_fullscreen); toplevel_set_fullscreen(toplevel, toplevel->xdg->requested.fullscreen); } #include #include "types.h" void deco_request_mode(struct wl_listener *listener, void *data) { UNUSED(data); absn_toplevel *toplevel = wl_container_of(listener, toplevel, deco_request_mode); if (toplevel->xdg->base->initialized) { wlr_xdg_toplevel_decoration_v1_set_mode(toplevel->deco, WLR_XDG_TOPLEVEL_DECORATION_V1_MODE_SERVER_SIDE); } } void deco_destroy(struct wl_listener *listener, void *data) { UNUSED(data); absn_toplevel *toplevel = wl_container_of(listener, toplevel, deco_destroy); wl_list_remove(&toplevel->deco_request_mode.link); wl_list_remove(&toplevel->deco_destroy.link); } #include "types.h" void xdg_popup_commit(struct wl_listener *listener, void *data) { UNUSED(data); absn_popup *popup = wl_container_of(listener, popup, commit); struct wlr_box box; if (popup->parent_type == TOPLEVEL_XDG) { absn_toplevel *toplevel = popup->parent; box = toplevel->output->usable_area; box.x -= toplevel->geom.x; box.y -= toplevel->geom.y; } else { absn_layer_surface *layer_surface = popup->parent; box = layer_surface->output->geom; box.x -= layer_surface->popups->node.x; box.y -= layer_surface->popups->node.y; } wlr_xdg_popup_unconstrain_from_box(popup->wlr, &box); wl_list_remove(&popup->commit.link); free(popup); } #include #include #include #include "types.h" #include "xdg-decoration.h" void toplevel_commit(struct wl_listener *listener, void *data) { UNUSED(data); absn_toplevel *toplevel = wl_container_of(listener, toplevel, commit); if (toplevel->xdg->base->current.configure_serial < toplevel->resizing) { return; } if (toplevel->xdg->base->initial_commit) { wlr_xdg_toplevel_set_activated(toplevel->xdg, false); /* forse server side decoration mode */ if (toplevel->deco) { deco_request_mode(&toplevel->deco_request_mode, toplevel->deco); } /* let client set initial size */ toplevel->resizing = wlr_xdg_toplevel_set_size(toplevel->xdg, 0, 0); return; } struct wlr_box clip = { .x = toplevel->xdg->base->geometry.x, .y = toplevel->xdg->base->geometry.y, .width = toplevel->geom.width - toplevel->bw, .height = toplevel->geom.height - toplevel->bw, }; wlr_scene_subsurface_tree_set_clip(&toplevel->scene_surface->node, &clip); toplevel->resizing = 0; } #ifdef XWAYLAND #include "xwayland.h" #include "toplevel-handlers.h" #include "toplevel.h" #include "types.h" void xwayland_activate(struct wl_listener *listener, void *data) { UNUSED(data); absn_toplevel *toplevel = wl_container_of(listener, toplevel, xw_activate); if (!toplevel_is_unmanaged(toplevel)) { wlr_xwayland_surface_activate(toplevel->xw, 1); } } void xwayland_associate(struct wl_listener *listener, void *data) { UNUSED(data); absn_toplevel *toplevel = wl_container_of(listener, toplevel, xw_associate); LISTEN(toplevel->map, toplevel_map, toplevel->xw->surface->events.map); LISTEN(toplevel->unmap, toplevel_unmap, toplevel->xw->surface->events.unmap); } void xwayland_dissociate(struct wl_listener *listener, void *data) { UNUSED(data); absn_toplevel *toplevel = wl_container_of(listener, toplevel, xw_dissociate); wl_list_remove(&toplevel->map.link); wl_list_remove(&toplevel->unmap.link); } void xwayland_configure(struct wl_listener *listener, void *data) { UNUSED(data); absn_toplevel *toplevel = wl_container_of(listener, toplevel, xw_configure); struct wlr_xwayland_surface_configure_event *event = data; if (!toplevel->xw->surface || !toplevel->xw->surface->mapped) { wlr_xwayland_surface_configure(toplevel->xw, event->x, event->y, event->width, event->height); return; } if (toplevel_is_unmanaged(toplevel)) { wlr_scene_node_set_position(&toplevel->scene_tree->node, event->x, event->y); wlr_xwayland_surface_configure(toplevel->xw, event->x, event->y, event->width, event->height); return; } } void xwayland_set_hints(struct wl_listener *listener, void *data) { UNUSED(listener); UNUSED(data); } #endif