diff --git a/Bugs.md b/Bugs.md index 491d2c6..835fc94 100644 --- a/Bugs.md +++ b/Bugs.md @@ -55,3 +55,27 @@ Steps to reproduce: ``` (EE) failed to read Wayland events: Broken pipe ``` + +### Issue 4 + + github issue number: #1 + Fixed: 1.0.7 + +This issue is code duplication in `parse.c`. + +Github issue text: + +``` +As of right now, the actions which can be run in the config file and the +actions which can be run as a keybinding are parsed separately in `parse.c`. +This leads to a lot of code duplication. Furthermore, unifying these +functionalities would enable a more versatile configuration. For instance, it +would enable the user to write `hsplit` into the configuration file to split +the output on startup and workspace 2 to set the default workspace to +workspace 2. Therefore, this change would simplify the code base, while at +the same time increasing the feature set. + +PS: As a side effect, this would allow quirky statements such as +`bind dbind r hsplit` which would bind the d key to binding the r key to +split the output... +``` diff --git a/cagebreak.c b/cagebreak.c index 4a71480..ba359e5 100644 --- a/cagebreak.c +++ b/cagebreak.c @@ -11,7 +11,10 @@ #include "config.h" #include +#include #include +#include +#include #include #include #include @@ -533,5 +536,10 @@ end: with a proper wl_display. */ wl_display_destroy(server.wl_display); wlr_output_layout_destroy(server.output_layout); + + pango_cairo_font_map_set_default(NULL); + cairo_debug_reset_static_data(); + FcFini(); + return ret; } diff --git a/fuzz/execl_override.c b/fuzz/execl_override.c index 281282f..316cf1e 100644 --- a/fuzz/execl_override.c +++ b/fuzz/execl_override.c @@ -1,11 +1,29 @@ /* This file is used by the fuzzer in order to prevent executing shell commands. */ #define _GNU_SOURCE -#include - -struct tm *(*orig_localtime)(const time_t *timep); +#include "../output.h" +#include +#include +#include +#include int -execl(const char *pathname, const char *arg, ...) { - return 0; +fork() { + return 1; +} + +void +wlr_texture_get_size(struct wlr_texture *texture, int *width, int *height) { + if(width != NULL) { + *width = 0; + } + + if(height != NULL) { + *height = 0; + } +} + +cairo_surface_t * +cairo_image_surface_create(cairo_format_t fmt, int width, int height) { + return NULL; } diff --git a/fuzz/fuzz-parse.c b/fuzz/fuzz-parse.c index 257f289..24b15cf 100644 --- a/fuzz/fuzz-parse.c +++ b/fuzz/fuzz-parse.c @@ -1,75 +1,410 @@ -#define _POSIX_C_SOURCE 200812L -#define FUZZING +/* + * Cagebreak: A Wayland tiling compositor. + * + * Copyright (C) 2018-2020 Jente Hidskes + * + * See the LICENSE file accompanying this file. + */ -#include "../keybinding.h" -#include "../parse.h" -#include "../seat.h" -#include "../server.h" -#include +#define _POSIX_C_SOURCE 200812L + +#include "config.h" + +#include +#include #include +#include #include #include #include #include + +#include +#include +#include #include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#if CG_HAS_XWAYLAND +#include +#endif +#include +#include +#include +#include +#if CG_HAS_XWAYLAND +#include +#endif + +#include "idle_inhibit_v1.h" +#include "keybinding.h" +#include "message.h" +#include "output.h" +#include "parse.h" +#include "seat.h" +#include "server.h" +#include "view.h" +#include "workspace.h" +#include "xdg_shell.h" +#if CG_HAS_XWAYLAND +#include "xwayland.h" +#endif #ifndef WAIT_ANY #define WAIT_ANY -1 #endif -void -set_sig_handler(int sig) { - struct sigaction sa; - sa.sa_handler = SIG_IGN; // handle signal by ignoring - sigemptyset(&sa.sa_mask); - sa.sa_flags = 0; - if(sigaction(SIGCHLD, &sa, 0) == -1) { - perror(0); - exit(1); +static bool +drop_permissions(void) { + if(getuid() != geteuid() || getgid() != getegid()) { + if(setuid(getuid()) != 0 || setgid(getgid()) != 0) { + wlr_log(WLR_ERROR, "Unable to drop root, refusing to start"); + return false; + } } + + if(setuid(0) != -1) { + wlr_log(WLR_ERROR, "Unable to drop root (we shouldn't be able to " + "restore it after setuid), refusing to start"); + return false; + } + + return true; } -struct cg_server server; +static bool +parse_args(struct cg_server *server, int argc, char *argv[]) { + server->output_transform = WL_OUTPUT_TRANSFORM_NORMAL; + server->debug_damage_tracking = false; + return true; +} + +struct cg_server server = {0}; +struct wlr_xwayland *xwayland = NULL; +#if CG_HAS_XWAYLAND +struct wlr_xcursor_manager *xcursor_manager = NULL; +#endif + +void +cleanup() { + server.running = false; +#if CG_HAS_XWAYLAND + if(xwayland != NULL) { + wlr_xwayland_destroy(xwayland); + } + if(xcursor_manager != NULL) { + wlr_xcursor_manager_destroy(xcursor_manager); + } +#endif + wl_display_destroy_clients(server.wl_display); + + for(unsigned int i = 0; server.modes[i] != NULL; ++i) { + free(server.modes[i]); + } + free(server.modes); + + keybinding_list_free(server.keybindings); + + seat_destroy(server.seat); + /* This function is not null-safe, but we only ever get here + with a proper wl_display. */ + wl_display_destroy(server.wl_display); + wlr_output_layout_destroy(server.output_layout); +} int LLVMFuzzerInitialize(int *argc, char ***argv) { - set_sig_handler(SIGCHLD); + struct wl_event_loop *event_loop = NULL; + struct wlr_backend *backend = NULL; + struct wlr_renderer *renderer = NULL; + struct wlr_compositor *compositor = NULL; + struct wlr_data_device_manager *data_device_manager = NULL; + struct wlr_server_decoration_manager *server_decoration_manager = NULL; + struct wlr_xdg_decoration_manager_v1 *xdg_decoration_manager = NULL; + struct wlr_export_dmabuf_manager_v1 *export_dmabuf_manager = NULL; + struct wlr_screencopy_manager_v1 *screencopy_manager = NULL; + struct wlr_xdg_output_manager_v1 *output_manager = NULL; + struct wlr_gamma_control_manager_v1 *gamma_control_manager = NULL; + struct wlr_xdg_shell *xdg_shell = NULL; + int ret = 0; - server.wl_display = NULL; - server.event_loop = NULL; + if(!parse_args(&server, *argc, *argv)) { + return 1; + } - server.seat = malloc(sizeof(struct cg_seat)); - server.seat->mode = 0; - server.seat->default_mode = 0; +#ifdef DEBUG + wlr_log_init(WLR_DEBUG, NULL); +#else + wlr_log_init(WLR_ERROR, NULL); +#endif - server.idle = NULL; - server.idle_inhibit_v1 = NULL; - /* new_idle_inhibitor_v1 */ - wl_list_init(&server.inhibitors); + /* Wayland requires XDG_RUNTIME_DIR to be set. */ + if(!getenv("XDG_RUNTIME_DIR")) { + wlr_log(WLR_ERROR, "XDG_RUNTIME_DIR is not set in the environment"); + return 1; + } - server.output_layout = NULL; - wl_list_init(&server.outputs); - server.curr_output = NULL; - - /* new_output */ - - /* xdg_toplevel_decoration */ - /* new_xdg_shell_surface */ - /* new_xwayland_surface */ - server.keybindings = keybinding_list_init(); - server.output_transform = 0; + server.wl_display = wl_display_create(); + if(!server.wl_display) { + wlr_log(WLR_ERROR, "Cannot allocate a Wayland display"); + return 1; + } server.running = true; - server.modes = malloc(sizeof(char *)); - server.modes[0] = NULL; + + server.modes = malloc(4 * sizeof(char *)); + server.modes[0] = strdup("top"); + server.modes[1] = strdup("root"); + server.modes[2] = strdup("resize"); + server.modes[3] = NULL; + server.nws = 1; - server.message_timeout = 0; + server.message_timeout = 2; + + event_loop = wl_display_get_event_loop(server.wl_display); + server.event_loop = event_loop; + + backend = wlr_backend_autocreate(server.wl_display, NULL); + if(!backend) { + wlr_log(WLR_ERROR, "Unable to create the wlroots backend"); + ret = 1; + goto end; + } + server.backend = backend; + + if(!drop_permissions()) { + ret = 1; + goto end; + } + + server.keybindings = keybinding_list_init(); + if(server.keybindings == NULL || server.keybindings->keybindings == NULL) { + wlr_log(WLR_ERROR, "Unable to allocate keybindings"); + ret = 1; + goto end; + } + + renderer = wlr_backend_get_renderer(backend); + wlr_renderer_init_wl_display(renderer, server.wl_display); + server.bg_color = malloc(4 * sizeof(float)); server.bg_color[0] = 0; server.bg_color[1] = 0; server.bg_color[2] = 0; server.bg_color[3] = 1; + wl_list_init(&server.outputs); + server.output_layout = wlr_output_layout_create(); + if(!server.output_layout) { + wlr_log(WLR_ERROR, "Unable to create output layout"); + ret = 1; + goto end; + } + + compositor = wlr_compositor_create(server.wl_display, renderer); + if(!compositor) { + wlr_log(WLR_ERROR, "Unable to create the wlroots compositor"); + ret = 1; + goto end; + } + + data_device_manager = wlr_data_device_manager_create(server.wl_display); + if(!data_device_manager) { + wlr_log(WLR_ERROR, "Unable to create the data device manager"); + ret = 1; + goto end; + } + + /* Configure a listener to be notified when new outputs are + * available on the backend. We use this only to detect the + * first output and ignore subsequent outputs. */ + server.new_output.notify = handle_new_output; + wl_signal_add(&backend->events.new_output, &server.new_output); + + server.seat = seat_create(&server, backend); + if(!server.seat) { + wlr_log(WLR_ERROR, "Unable to create the seat"); + ret = 1; + goto end; + } + + server.idle = wlr_idle_create(server.wl_display); + if(!server.idle) { + wlr_log(WLR_ERROR, "Unable to create the idle tracker"); + ret = 1; + goto end; + } + + server.idle_inhibit_v1 = wlr_idle_inhibit_v1_create(server.wl_display); + if(!server.idle_inhibit_v1) { + wlr_log(WLR_ERROR, "Cannot create the idle inhibitor"); + ret = 1; + goto end; + } + server.new_idle_inhibitor_v1.notify = handle_idle_inhibitor_v1_new; + wl_signal_add(&server.idle_inhibit_v1->events.new_inhibitor, + &server.new_idle_inhibitor_v1); + wl_list_init(&server.inhibitors); + + xdg_shell = wlr_xdg_shell_create(server.wl_display); + if(!xdg_shell) { + wlr_log(WLR_ERROR, "Unable to create the XDG shell interface"); + ret = 1; + goto end; + } + server.new_xdg_shell_surface.notify = handle_xdg_shell_surface_new; + wl_signal_add(&xdg_shell->events.new_surface, + &server.new_xdg_shell_surface); + + xdg_decoration_manager = + wlr_xdg_decoration_manager_v1_create(server.wl_display); + if(!xdg_decoration_manager) { + wlr_log(WLR_ERROR, "Unable to create the XDG decoration manager"); + ret = 1; + goto end; + } + wl_signal_add(&xdg_decoration_manager->events.new_toplevel_decoration, + &server.xdg_toplevel_decoration); + server.xdg_toplevel_decoration.notify = handle_xdg_toplevel_decoration; + + server_decoration_manager = + wlr_server_decoration_manager_create(server.wl_display); + if(!server_decoration_manager) { + wlr_log(WLR_ERROR, "Unable to create the server decoration manager"); + ret = 1; + goto end; + } + wlr_server_decoration_manager_set_default_mode( + server_decoration_manager, WLR_SERVER_DECORATION_MANAGER_MODE_SERVER); + + export_dmabuf_manager = + wlr_export_dmabuf_manager_v1_create(server.wl_display); + if(!export_dmabuf_manager) { + wlr_log(WLR_ERROR, "Unable to create the export DMABUF manager"); + ret = 1; + goto end; + } + + screencopy_manager = wlr_screencopy_manager_v1_create(server.wl_display); + if(!screencopy_manager) { + wlr_log(WLR_ERROR, "Unable to create the screencopy manager"); + ret = 1; + goto end; + } + + output_manager = wlr_xdg_output_manager_v1_create(server.wl_display, + server.output_layout); + if(!output_manager) { + wlr_log(WLR_ERROR, "Unable to create the output manager"); + ret = 1; + goto end; + } + + gamma_control_manager = + wlr_gamma_control_manager_v1_create(server.wl_display); + if(!gamma_control_manager) { + wlr_log(WLR_ERROR, "Unable to create the gamma control manager"); + ret = 1; + goto end; + } + +#if CG_HAS_XWAYLAND + xwayland = wlr_xwayland_create(server.wl_display, compositor, true); + if(!xwayland) { + wlr_log(WLR_ERROR, "Cannot create XWayland server"); + ret = 1; + goto end; + } + server.new_xwayland_surface.notify = handle_xwayland_surface_new; + wl_signal_add(&xwayland->events.new_surface, &server.new_xwayland_surface); + + xcursor_manager = wlr_xcursor_manager_create(DEFAULT_XCURSOR, XCURSOR_SIZE); + if(!xcursor_manager) { + wlr_log(WLR_ERROR, "Cannot create XWayland XCursor manager"); + ret = 1; + goto end; + } + + if(setenv("DISPLAY", xwayland->display_name, true) < 0) { + wlr_log_errno(WLR_ERROR, "Unable to set DISPLAY for XWayland.", + "Clients may not be able to connect"); + } else { + wlr_log(WLR_DEBUG, "XWayland is running on display %s", + xwayland->display_name); + } + + if(wlr_xcursor_manager_load(xcursor_manager, 1)) { + wlr_log(WLR_ERROR, "Cannot load XWayland XCursor theme"); + } + struct wlr_xcursor *xcursor = + wlr_xcursor_manager_get_xcursor(xcursor_manager, DEFAULT_XCURSOR, 1); + if(xcursor) { + struct wlr_xcursor_image *image = xcursor->images[0]; + wlr_xwayland_set_cursor(xwayland, image->buffer, image->width * 4, + image->width, image->height, image->hotspot_x, + image->hotspot_y); + } +#endif + + const char *socket = wl_display_add_socket_auto(server.wl_display); + if(!socket) { + wlr_log_errno(WLR_ERROR, "Unable to open Wayland socket"); + ret = 1; + goto end; + } + + if(!wlr_backend_start(backend)) { + wlr_log(WLR_ERROR, "Unable to start the wlroots backend"); + ret = 1; + goto end; + } + + if(setenv("WAYLAND_DISPLAY", socket, true) < 0) { + wlr_log_errno(WLR_ERROR, "Unable to set WAYLAND_DISPLAY.", + "Clients may not be able to connect"); + } else { + wlr_log(WLR_DEBUG, + "Cagebreak " CG_VERSION " is running on Wayland display %s", + socket); + } + +#if CG_HAS_XWAYLAND + wlr_xwayland_set_seat(xwayland, server.seat->seat); +#endif + + /* Place the cursor to the topl left of the output layout. */ + wlr_cursor_warp(server.seat->cursor, NULL, 0, 0); + atexit(cleanup); + return 0; +end: + cleanup(); + return ret; +} + +/* Parse config file. Lines longer than "max_line_size" are ignored */ +int +set_configuration(struct cg_server *server, char *content) { + char *line; + for(unsigned int line_num = 1; + (line = strtok_r(NULL, "\n", &content)) != NULL; ++line_num) { + line[strcspn(line, "\n")] = '\0'; + if(*line != '\0' && *line != '#') { + if(parse_rc_line(server, line) != 0) { + return -1; + } + } + } return 0; } @@ -78,9 +413,33 @@ LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) { char *str = malloc(size * sizeof(char) + 1); strncpy(str, (char *)data, size); str[size] = 0; - parse_rc_line(&server, str); + set_configuration(&server, str); free(str); keybinding_list_free(server.keybindings); server.keybindings = keybinding_list_init(); + run_action(KEYBINDING_WORKSPACES, &server, + (union keybinding_params){.i = 1}); + run_action(KEYBINDING_LAYOUT_FULLSCREEN, &server, + (union keybinding_params){.c = NULL}); + wl_display_flush_clients(server.wl_display); + wl_display_destroy_clients(server.wl_display); + struct cg_output *output; + wl_list_for_each(output, &server.outputs, link) { + message_clear(output); + struct cg_view *view; + wl_list_for_each(view, &(*output->workspaces)->views, link) { + view_unmap(view); + view_destroy(view); + } + wl_list_for_each(view, &(*output->workspaces)->unmanaged_views, link) { + view_unmap(view); + view_destroy(view); + } + } + for(unsigned int i = 3; server.modes[i] != NULL; ++i) { + free(server.modes[i]); + } + server.modes[3] = NULL; + server.modes = realloc(server.modes, 4 * sizeof(char *)); return 0; } diff --git a/fuzz/meson.build b/fuzz/meson.build index 0266717..8ff982a 100644 --- a/fuzz/meson.build +++ b/fuzz/meson.build @@ -17,6 +17,7 @@ endif override_lib = shared_library('execl_override', [ 'execl_override.c' ], + dependencies: [ pixman,cairo,pango,pangocairo ], install: false ) diff --git a/keybinding.c b/keybinding.c index 2d5abdf..f6f5eaf 100644 --- a/keybinding.c +++ b/keybinding.c @@ -54,12 +54,19 @@ find_keybinding(const struct keybinding_list *list, } void -keybinding_free(struct keybinding *keybinding) { +keybinding_free(struct keybinding *keybinding, bool recursive) { switch(keybinding->action) { + case KEYBINDING_DEFINEMODE: case KEYBINDING_RUN_COMMAND: if(keybinding->data.c != NULL) { free(keybinding->data.c); } + break; + case KEYBINDING_DEFINEKEY: + if(keybinding->data.kb != NULL && recursive) { + keybinding_free(keybinding->data.kb, true); + } + break; default: break; } @@ -79,7 +86,7 @@ keybinding_list_push(struct keybinding_list *list, * exist*/ struct keybinding **found_keybinding = find_keybinding(list, keybinding); if(found_keybinding != NULL) { - keybinding_free(*found_keybinding); + keybinding_free(*found_keybinding, true); *found_keybinding = keybinding; wlr_log(WLR_DEBUG, "A keybinding was found twice in the config file."); } else { @@ -101,7 +108,7 @@ keybinding_list_init() { void keybinding_list_free(struct keybinding_list *list) { for(unsigned int i = 0; i < list->length; ++i) { - keybinding_free(list->keybindings[i]); + keybinding_free(list->keybindings[i], true); } free(list->keybindings); free(list); @@ -248,10 +255,27 @@ is_between_strict(int a, int b, int x) { return a < x && x < b; } +int +get_compl_coord(struct cg_tile *tile, int *(*get_coord)(struct cg_tile *tile)) { + return tile->tile.x + tile->tile.y - *get_coord(tile); +} + +int +get_compl_dim(struct cg_tile *tile, int *(*get_dim)(struct cg_tile *tile)) { + return tile->tile.width + tile->tile.height - *get_dim(tile); +} + bool -resize_allowed(struct cg_tile *tile, struct cg_tile *parent, int coord_offset, - int dim_offset, int *(*get_coord)(struct cg_tile *tile), - int *(*get_dim)(struct cg_tile *tile)) { +intervalls_intersect(int x1, int x2, int y1, int y2) { + return y2 > x1 && y1 < x2; +} + +bool +resize_allowed(struct cg_tile *tile, const struct cg_tile *parent, + int coord_offset, int dim_offset, + int *(*get_coord)(struct cg_tile *tile), + int *(*get_dim)(struct cg_tile *tile), struct cg_tile *orig) { + if(coord_offset == 0 && dim_offset == 0) { return true; } else if(*get_dim(tile) - coord_offset + dim_offset <= 0) { @@ -260,17 +284,25 @@ resize_allowed(struct cg_tile *tile, struct cg_tile *parent, int coord_offset, for(struct cg_tile *it = tile->next; it != tile && it != NULL; it = it->next) { - if(it == parent) { + if(it == parent || it == orig) { continue; } - if(it->tile.x == tile->tile.x + tile->tile.width) { - if(!resize_allowed(it, tile, dim_offset, -dim_offset, get_coord, - get_dim)) { - return false; - } - } else if(it->tile.x + it->tile.width == tile->tile.x) { - if(!resize_allowed(it, tile, 0, coord_offset, get_coord, get_dim)) { - return false; + if(intervalls_intersect( + get_compl_coord(tile, get_coord), + get_compl_coord(tile, get_coord) + get_compl_dim(tile, get_dim), + get_compl_coord(it, get_coord), + get_compl_coord(it, get_coord) + get_compl_dim(it, get_dim))) { + if(*get_coord(it) == *get_coord(tile) + *get_dim(tile)) { + if(!resize_allowed(it, tile, dim_offset + coord_offset, + -dim_offset - coord_offset, get_coord, + get_dim, orig)) { + return false; + } + } else if(*get_coord(it) + *get_dim(it) == *get_coord(tile)) { + if(!resize_allowed(it, tile, 0, coord_offset, get_coord, + get_dim, orig)) { + return false; + } } } } @@ -280,20 +312,27 @@ resize_allowed(struct cg_tile *tile, struct cg_tile *parent, int coord_offset, void resize(struct cg_tile *tile, const struct cg_tile *parent, int coord_offset, int dim_offset, int *(*get_coord)(struct cg_tile *tile), - int *(*get_dim)(struct cg_tile *tile)) { + int *(*get_dim)(struct cg_tile *tile), struct cg_tile *orig) { if(coord_offset == 0 && dim_offset == 0) { return; } for(struct cg_tile *it = tile->next; it != tile && it != NULL; it = it->next) { - if(it == parent) { + if(it == parent || it == orig) { continue; } - if(*get_coord(it) == *get_coord(tile) + *get_dim(tile)) { - resize(it, tile, dim_offset, -dim_offset, get_coord, get_dim); - } else if(*get_coord(it) + *get_dim(it) == *get_coord(tile)) { - resize(it, tile, 0, coord_offset, get_coord, get_dim); + if(intervalls_intersect( + get_compl_coord(tile, get_coord), + get_compl_coord(tile, get_coord) + get_compl_dim(tile, get_dim), + get_compl_coord(it, get_coord), + get_compl_coord(it, get_coord) + get_compl_dim(it, get_dim))) { + if(*get_coord(it) == *get_coord(tile) + *get_dim(tile)) { + resize(it, tile, dim_offset + coord_offset, + -dim_offset - coord_offset, get_coord, get_dim, orig); + } else if(*get_coord(it) + *get_dim(it) == *get_coord(tile)) { + resize(it, tile, 0, coord_offset, get_coord, get_dim, orig); + } } } @@ -335,26 +374,26 @@ bool resize_allowed_horizontal(struct cg_tile *tile, struct cg_tile *parent, int x_offset, int width_offset) { return resize_allowed(tile, parent, x_offset, width_offset, get_x, - get_width); + get_width, tile); } bool resize_allowed_vertical(struct cg_tile *tile, struct cg_tile *parent, int y_offset, int height_offset) { return resize_allowed(tile, parent, y_offset, height_offset, get_y, - get_height); + get_height, tile); } void resize_horizontal(struct cg_tile *tile, struct cg_tile *parent, int x_offset, int width_offset) { - resize(tile, parent, x_offset, width_offset, get_x, get_width); + resize(tile, parent, x_offset, width_offset, get_x, get_width, tile); } void resize_vertical(struct cg_tile *tile, struct cg_tile *parent, int y_offset, int height_offset) { - resize(tile, parent, y_offset, height_offset, get_y, get_height); + resize(tile, parent, y_offset, height_offset, get_y, get_height, tile); } /* hpixs: positiv -> right, negative -> left; vpixs: positiv -> down, negative @@ -608,9 +647,9 @@ keybinding_cycle_tiles(struct cg_server *server, bool reverse) { int keybinding_switch_ws(struct cg_server *server, uint32_t ws) { - if(ws > server->nws) { + if(ws >= server->nws) { wlr_log(WLR_ERROR, - "Requested workspace %u, but only have %u workspaces.", ws, + "Requested workspace %u, but only have %u workspaces.", ws + 1, server->nws); return -1; } @@ -634,6 +673,7 @@ keybinding_show_time(struct cg_server *server) { msg[strcspn(msg, "\n")] = '\0'; /* Remove the newline */ message_printf(server->curr_output, "%s", msg); + free(msg); } void @@ -670,6 +710,80 @@ keybinding_move_view_to_next_output(struct cg_server *server) { } } +void +keybinding_set_nws(struct cg_server *server, int nws) { + struct cg_output *output; + wl_list_for_each(output, &server->outputs, link) { + for(unsigned int i = nws; i < server->nws; ++i) { + struct cg_view *view, *tmp; + wl_list_for_each_safe(view, tmp, &output->workspaces[i]->views, + link) { + wl_list_remove(&view->link); + wl_list_insert(&output->workspaces[nws - 1]->views, + &view->link); + view->workspace = output->workspaces[nws - 1]; + } + wl_list_for_each_safe( + view, tmp, &output->workspaces[i]->unmanaged_views, link) { + wl_list_remove(&view->link); + wl_list_insert(&output->workspaces[nws - 1]->unmanaged_views, + &view->link); + view->workspace = output->workspaces[nws - 1]; + } + workspace_free(output->workspaces[i]); + } + struct cg_workspace **new_workspaces = + realloc(output->workspaces, nws * sizeof(struct cg_workspace *)); + if(new_workspaces == NULL) { + wlr_log(WLR_ERROR, "Error reallocating memory for workspaces."); + return; + } + output->workspaces = new_workspaces; + for(int i = server->nws; i < nws; ++i) { + output->workspaces[i] = full_screen_workspace(output); + wl_list_init(&output->workspaces[i]->views); + wl_list_init(&output->workspaces[i]->unmanaged_views); + } + + if(output->curr_workspace >= nws) { + output->curr_workspace = nws - 1; + } + } + server->nws = nws; + seat_set_focus( + server->seat, + server->curr_output->workspaces[server->curr_output->curr_workspace] + ->focused_tile->view); +} + +void +keybinding_definemode(struct cg_server *server, char *mode) { + int length = 0; + while(server->modes[length++] != NULL) + ; + char **tmp = realloc(server->modes, (length + 1) * sizeof(char *)); + if(tmp == NULL) { + wlr_log(WLR_ERROR, "Could not allocate memory for storing modes."); + return; + } + server->modes = tmp; + server->modes[length] = NULL; + + server->modes[length - 1] = strdup(mode); +} + +void +keybinding_definekey(struct cg_server *server, struct keybinding *kb) { + keybinding_list_push(server->keybindings, kb); +} + +void +keybinding_set_background(struct cg_server *server, float *bg) { + server->bg_color[0] = bg[0]; + server->bg_color[1] = bg[1]; + server->bg_color[2] = bg[2]; +} + void keybinding_move_view_to_workspace(struct cg_server *server, uint32_t ws) { struct cg_view *view = @@ -808,6 +922,18 @@ run_action(enum keybinding_action action, struct cg_server *server, keybinding_move_view_to_next_output(server); break; } + case KEYBINDING_DEFINEKEY: + keybinding_definekey(server, data.kb); + break; + case KEYBINDING_BACKGROUND: + keybinding_set_background(server, data.color); + break; + case KEYBINDING_DEFINEMODE: + keybinding_definemode(server, data.c); + break; + case KEYBINDING_WORKSPACES: + keybinding_set_nws(server, data.i); + break; default: { wlr_log(WLR_ERROR, "run_action was called with a value not present in \"enum " diff --git a/keybinding.h b/keybinding.h index d06bbbb..24f8af2 100644 --- a/keybinding.h +++ b/keybinding.h @@ -43,6 +43,11 @@ enum keybinding_action { KEYBINDING_FOCUS_RIGHT, KEYBINDING_FOCUS_TOP, KEYBINDING_FOCUS_BOTTOM, + + KEYBINDING_DEFINEKEY, // data.kb is the keybinding definition + KEYBINDING_BACKGROUND, // data.color is the background color + KEYBINDING_DEFINEMODE, // data.c is the mode name + KEYBINDING_WORKSPACES, // data.i is the number of workspaces }; union keybinding_params { @@ -50,6 +55,8 @@ union keybinding_params { uint32_t u; int32_t i; bool b; + float color[3]; + struct keybinding *kb; }; struct keybinding { @@ -82,5 +89,7 @@ keybinding_list_init(); int run_action(enum keybinding_action action, struct cg_server *server, union keybinding_params data); +void +keybinding_free(struct keybinding *keybinding, bool recursive); #endif /* end of include guard KEYBINDINGS_H */ diff --git a/meson.build b/meson.build index 6b2e509..8f26c17 100644 --- a/meson.build +++ b/meson.build @@ -1,5 +1,5 @@ project('cagebreak', 'c', - version: '1.0.6', + version: '1.0.7', license: 'MIT', default_options: [ 'c_std=c11', @@ -165,6 +165,41 @@ endforeach foreach header : cagebreak_header_strings cagebreak_headers += files(header) endforeach +cagebreak_dependencies_dict = { + 'server_protos': server_protos, + 'wayland_server': wayland_server, + 'wayland_client': wayland_client, + 'wayland_cursor': wayland_cursor, + 'wlroots': wlroots, + 'xkbcommon': xkbcommon, + 'fontconfig': fontconfig, + 'pixman': pixman, + 'pango': pango, + 'cairo': cairo, + 'pangocairo': pangocairo, + 'math': math +} + +reproducible_build_versions = { + 'server_protos': '1.0.6', + 'wayland_server': '1.18.0', + 'wayland_client': '1.18.0', + 'wayland_cursor': '1.18.0', + 'wlroots': '0.10.1', + 'xkbcommon': '0.10.0', + 'fontconfig': '2.13.91', + 'pixman': '0.38.4', + 'pango': '1.44.7', + 'cairo': '1.17.3', + 'pangocairo': '1.44.7', + 'math': '-1' +} + +cagebreak_dependencies = [] + +foreach name, dep : cagebreak_dependencies_dict + cagebreak_dependencies += dep +endforeach cagebreak_dependencies_dict = { 'server_protos': server_protos, diff --git a/message.c b/message.c index 66e09dd..40ac033 100644 --- a/message.c +++ b/message.c @@ -45,7 +45,13 @@ create_message_texture(const char *string, const struct cg_output *output) { // Therefore, we cannot use cairo_create(NULL). cairo_surface_t *dummy_surface = cairo_image_surface_create(CAIRO_FORMAT_ARGB32, 0, 0); + // This occurs when we are fuzzing. In that case, do nothing + if(dummy_surface == NULL) { + return NULL; + } + cairo_t *c = cairo_create(dummy_surface); + cairo_set_antialias(c, CAIRO_ANTIALIAS_BEST); cairo_font_options_t *fo = cairo_font_options_create(); cairo_font_options_set_hint_style(fo, CAIRO_HINT_STYLE_FULL); diff --git a/pango.c b/pango.c index 035ac9b..ef3df10 100644 --- a/pango.c +++ b/pango.c @@ -1,4 +1,4 @@ -#include "cairo.h" +#include #include #include #include diff --git a/parse.c b/parse.c index e8c7b15..cb6abe3 100644 --- a/parse.c +++ b/parse.c @@ -10,167 +10,6 @@ #include "server.h" #include "workspace.h" -int -parse_action(struct cg_server *server, struct keybinding *keybinding, - char **saveptr) { - char *action = strtok_r(NULL, " ", saveptr); - if(action == NULL) { - wlr_log( - WLR_ERROR, - "Not enough parameters to \"bind\". Expected action to execute"); - return -1; - } - keybinding->data = (union keybinding_params){.c = NULL}; - if(strcmp(action, "vsplit") == 0) { - keybinding->action = KEYBINDING_SPLIT_VERTICAL; - } else if(strcmp(action, "hsplit") == 0) { - keybinding->action = KEYBINDING_SPLIT_HORIZONTAL; - } else if(strcmp(action, "quit") == 0) { - keybinding->action = KEYBINDING_QUIT; - } else if(strcmp(action, "focus") == 0) { - keybinding->action = KEYBINDING_CYCLE_TILES; - keybinding->data.b = false; - } else if(strcmp(action, "focusprev") == 0) { - keybinding->action = KEYBINDING_CYCLE_TILES; - keybinding->data.b = true; - } else if(strcmp(action, "next") == 0) { - keybinding->action = KEYBINDING_CYCLE_VIEWS; - keybinding->data.b = false; - } else if(strcmp(action, "prev") == 0) { - keybinding->action = KEYBINDING_CYCLE_VIEWS; - keybinding->data.b = true; - } else if(strcmp(action, "only") == 0) { - keybinding->action = KEYBINDING_LAYOUT_FULLSCREEN; - } else if(strcmp(action, "abort") == 0) { - keybinding->action = KEYBINDING_NOOP; - } else if(strcmp(action, "time") == 0) { - keybinding->action = KEYBINDING_SHOW_TIME; - } else if(strcmp(action, "nextscreen") == 0) { - keybinding->action = KEYBINDING_CYCLE_OUTPUT; - keybinding->data.b = false; - } else if(strcmp(action, "prevscreen") == 0) { - keybinding->action = KEYBINDING_CYCLE_OUTPUT; - keybinding->data.b = true; - } else if(strcmp(action, "exec") == 0) { - keybinding->action = KEYBINDING_RUN_COMMAND; - if(*saveptr == NULL) { - wlr_log(WLR_ERROR, "Not enough paramaters to \"exec\". Expected " - "string to execute."); - return -1; - } - keybinding->data.c = strdup(*saveptr); - } else if(strcmp(action, "resizeleft") == 0) { - keybinding->action = KEYBINDING_RESIZE_TILE_HORIZONTAL; - keybinding->data.i = -10; - } else if(strcmp(action, "resizeright") == 0) { - keybinding->action = KEYBINDING_RESIZE_TILE_HORIZONTAL; - keybinding->data.i = 10; - } else if(strcmp(action, "resizedown") == 0) { - keybinding->action = KEYBINDING_RESIZE_TILE_VERTICAL; - keybinding->data.i = 10; - } else if(strcmp(action, "resizeup") == 0) { - keybinding->action = KEYBINDING_RESIZE_TILE_VERTICAL; - keybinding->data.i = -10; - } else if(strcmp(action, "workspace") == 0) { - keybinding->action = KEYBINDING_SWITCH_WORKSPACE; - char *nws_str = strtok_r(NULL, " ", saveptr); - if(nws_str == NULL) { - wlr_log(WLR_ERROR, - "Expected argument for \"workspace\" action, got none."); - return -1; - } - - long ws = strtol(nws_str, NULL, 10); - if(!(1 <= ws && ws <= server->nws)) { - wlr_log(WLR_ERROR, - "Requested binding for workspace %li, but have %u", ws, - server->nws); - return -1; - } - keybinding->data.u = ws - 1; - } else if(strcmp(action, "movetoworkspace") == 0) { - keybinding->action = KEYBINDING_MOVE_VIEW_TO_WORKSPACE; - char *nws_str = strtok_r(NULL, " ", saveptr); - if(nws_str == NULL) { - wlr_log(WLR_ERROR, - "Expected argument for \"workspace\" action, got none."); - return -1; - } - - long ws = strtol(nws_str, NULL, 10); - if(!(1 <= ws && ws <= server->nws)) { - wlr_log( - WLR_ERROR, - "Requested binding for moving to workspace %li, but have %u", - ws, server->nws); - return -1; - } - keybinding->data.u = ws - 1; - } else if(strcmp(action, "exchangeleft") == 0) { - keybinding->action = KEYBINDING_SWAP_LEFT; - } else if(strcmp(action, "exchangeright") == 0) { - keybinding->action = KEYBINDING_SWAP_RIGHT; - } else if(strcmp(action, "exchangeup") == 0) { - keybinding->action = KEYBINDING_SWAP_TOP; - } else if(strcmp(action, "exchangedown") == 0) { - keybinding->action = KEYBINDING_SWAP_BOTTOM; - } else if(strcmp(action, "focusleft") == 0) { - keybinding->action = KEYBINDING_FOCUS_LEFT; - } else if(strcmp(action, "focusright") == 0) { - keybinding->action = KEYBINDING_FOCUS_RIGHT; - } else if(strcmp(action, "focusup") == 0) { - keybinding->action = KEYBINDING_FOCUS_TOP; - } else if(strcmp(action, "focusdown") == 0) { - keybinding->action = KEYBINDING_FOCUS_BOTTOM; - } else if(strcmp(action, "movetonextscreen") == 0) { - keybinding->action = KEYBINDING_MOVE_VIEW_TO_NEXT_OUTPUT; - } else if(strcmp(action, "switchvt") == 0) { - keybinding->action = KEYBINDING_CHANGE_TTY; - char *ntty = strtok_r(NULL, " ", saveptr); - if(ntty == NULL) { - wlr_log(WLR_ERROR, - "Expected argument for \"switchvt\" command, got none."); - return -1; - } - long tty = strtol(ntty, NULL, 10); - keybinding->data.u = tty; - } else if(strcmp(action, "mode") == 0) { - keybinding->action = KEYBINDING_SWITCH_MODE; - char *mode = strtok_r(NULL, " ", saveptr); - if(mode == NULL) { - wlr_log(WLR_ERROR, - "Expected mode after \"switch_mode\". Got nothing."); - return -1; - } - int mode_idx = get_mode_index_from_name(server->modes, mode); - if(mode_idx == -1) { - wlr_log(WLR_ERROR, "Unknown mode \"%s\" for switch_mode", mode); - return -1; - } - keybinding->data.u = (unsigned int)mode_idx; - } else if(strcmp(action, "setmode") == 0) { - keybinding->action = KEYBINDING_SWITCH_DEFAULT_MODE; - char *mode = strtok_r(NULL, " ", saveptr); - if(mode == NULL) { - wlr_log( - WLR_ERROR, - "Expected mode after \"switch_default_mode\". Got nothing."); - return -1; - } - int mode_idx = get_mode_index_from_name(server->modes, mode); - if(mode_idx == -1) { - wlr_log(WLR_ERROR, "Unknown mode \"%s\" for switch_default_mode", - mode); - return -1; - } - keybinding->data.u = (unsigned int)mode_idx; - } else { - wlr_log(WLR_ERROR, "Error, unsupported action \"%s\".", action); - return -1; - } - return 0; -} - /* parses a key definition (e.g. "S-Tab") and sets key and modifiers in * keybinding respectivly */ int @@ -218,6 +57,10 @@ parse_key(struct keybinding *keybinding, const char *key_def) { return 0; } +int +parse_command(struct cg_server *server, struct keybinding *keybinding, + char *saveptr); + /* Parse a keybinding definition and return it if successful, else return NULL */ struct keybinding * @@ -229,188 +72,341 @@ parse_keybinding(struct cg_server *server, char **saveptr) { free(keybinding); return NULL; } - if(parse_action(server, keybinding, saveptr) != 0) { + if(parse_command(server, keybinding, *saveptr) != 0) { free(keybinding); return NULL; } return keybinding; } -int -parse_bind(struct cg_server *server, struct keybinding_list *list, - char **saveptr) { +struct keybinding * +parse_bind(struct cg_server *server, char **saveptr) { struct keybinding *keybinding = parse_keybinding(server, saveptr); if(keybinding == NULL) { wlr_log(WLR_ERROR, "Could not parse keybinding for \"bind\"."); - return -1; + return NULL; } keybinding->mode = 1; - keybinding_list_push(list, keybinding); - return 0; + return keybinding; } -int -parse_definekey(struct cg_server *server, struct keybinding_list *list, - char **saveptr) { +struct keybinding * +parse_definekey(struct cg_server *server, char **saveptr) { char *mode = strtok_r(NULL, " ", saveptr); if(mode == NULL) { wlr_log(WLR_ERROR, "Too few arguments to \"definekey\". Expected mode"); - return -1; + return NULL; } int mode_idx = get_mode_index_from_name(server->modes, mode); if(mode_idx == -1) { wlr_log(WLR_ERROR, "Unknown mode \"%s\"", mode); - return -1; + return NULL; } struct keybinding *keybinding = parse_keybinding(server, saveptr); if(keybinding == NULL) { wlr_log(WLR_ERROR, "Could not parse keybinding for \"definekey\""); - return -1; + return NULL; } keybinding->mode = mode_idx; - keybinding_list_push(list, keybinding); - return 0; + return keybinding; } int -parse_and_run_exec(char **saveptr) { - return run_action(KEYBINDING_RUN_COMMAND, NULL, - (union keybinding_params){.c = *saveptr}); -} - -int -parse_background(struct cg_server *server, char **saveptr) { +parse_background(struct cg_server *server, float *color, char **saveptr) { /* Read rgb numbers */ for(unsigned int i = 0; i < 3; ++i) { - char *nstr = strtok_r(NULL, " ", saveptr); - if(nstr == NULL) { + char *nstr = strtok_r(NULL, " \n", saveptr); + int nstrlen; + if(nstr == NULL || (nstrlen = strlen(nstr)) == 0) { wlr_log(WLR_ERROR, "Expected three space-separated numbers (rgb) for " "background color setting. Got %d.", i); return -1; } + if(nstr[nstrlen - 1] == '\n') { + nstr[nstrlen - 1] = '\0'; + --nstrlen; + } char *endptr = NULL; float nval = strtof(nstr, &endptr); - if(endptr == nstr) { + if(endptr != nstr + nstrlen) { wlr_log( WLR_ERROR, - "Could not parse number \"%s\" für background color setting.", + "Could not parse number \"%s\" for background color setting.", nstr); return -1; } - server->bg_color[i] = nval; + if(nval < 0 || nval > 1) { + wlr_log(WLR_ERROR, + "Expected a number between 0 and 1 for setting of " + "background color. Got %f.", + nval); + return -1; + } + color[i] = nval; } return 0; } -int -parse_escape(struct keybinding_list *list, char **saveptr) { +struct keybinding * +parse_escape(char **saveptr) { struct keybinding *keybinding = malloc(sizeof(struct keybinding)); char *key = strtok_r(NULL, " ", saveptr); if(parse_key(keybinding, key) != 0) { wlr_log(WLR_ERROR, "Could not parse key definition \"%s\" for \"escape\"", key); free(keybinding); - return -1; + return NULL; } keybinding->mode = 0; //"top" mode keybinding->action = KEYBINDING_SWITCH_MODE; keybinding->data.u = 1; //"root" mode - keybinding_list_push(list, keybinding); - return 0; + return keybinding; } -int -parse_definemode(char ***modes, char **saveptr) { - int length = 0; - while((*modes)[length++] != NULL) - ; - char **tmp = realloc(*modes, (length + 1) * sizeof(char *)); - if(tmp == NULL) { - wlr_log(WLR_ERROR, "Could not allocate memory for storing modes."); - return -1; - } - *modes = tmp; - (*modes)[length] = NULL; - +char * +parse_definemode(char **saveptr) { char *mode = strtok_r(NULL, " ", saveptr); if(mode == NULL) { wlr_log(WLR_ERROR, "Expected mode to succeed \"definemode\" keyword."); + return NULL; + } + return strdup(mode); +} + +int +parse_workspaces(char **saveptr) { + char *nws_str = strtok_r(NULL, " ", saveptr); + if(nws_str == NULL) { + wlr_log(WLR_ERROR, + "Expected argument for \"workspaces\" command, got none."); + return -1; + } + long nws = strtol(nws_str, NULL, 10); + if(!(1 <= nws && nws <= 30)) { + wlr_log(WLR_ERROR, + "More than 30 workspaces are not supported. Received %li", nws); + return -1; + } + return nws; +} + +int +parse_command(struct cg_server *server, struct keybinding *keybinding, + char *saveptr) { + char *action = strtok_r(NULL, " ", &saveptr); + if(action == NULL) { + wlr_log(WLR_ERROR, "Expected an action to parse, got none."); + return -1; + } + keybinding->data = (union keybinding_params){.c = NULL}; + if(strcmp(action, "vsplit") == 0) { + keybinding->action = KEYBINDING_SPLIT_VERTICAL; + } else if(strcmp(action, "hsplit") == 0) { + keybinding->action = KEYBINDING_SPLIT_HORIZONTAL; + } else if(strcmp(action, "quit") == 0) { + keybinding->action = KEYBINDING_QUIT; + } else if(strcmp(action, "focus") == 0) { + keybinding->action = KEYBINDING_CYCLE_TILES; + keybinding->data.b = false; + } else if(strcmp(action, "focusprev") == 0) { + keybinding->action = KEYBINDING_CYCLE_TILES; + keybinding->data.b = true; + } else if(strcmp(action, "next") == 0) { + keybinding->action = KEYBINDING_CYCLE_VIEWS; + keybinding->data.b = false; + } else if(strcmp(action, "prev") == 0) { + keybinding->action = KEYBINDING_CYCLE_VIEWS; + keybinding->data.b = true; + } else if(strcmp(action, "only") == 0) { + keybinding->action = KEYBINDING_LAYOUT_FULLSCREEN; + } else if(strcmp(action, "abort") == 0) { + keybinding->action = KEYBINDING_NOOP; + } else if(strcmp(action, "time") == 0) { + keybinding->action = KEYBINDING_SHOW_TIME; + } else if(strcmp(action, "nextscreen") == 0) { + keybinding->action = KEYBINDING_CYCLE_OUTPUT; + keybinding->data.b = false; + } else if(strcmp(action, "prevscreen") == 0) { + keybinding->action = KEYBINDING_CYCLE_OUTPUT; + keybinding->data.b = true; + } else if(strcmp(action, "exec") == 0) { + keybinding->action = KEYBINDING_RUN_COMMAND; + if(saveptr == NULL) { + wlr_log(WLR_ERROR, "Not enough paramaters to \"exec\". Expected " + "string to execute."); + return -1; + } + keybinding->data.c = strdup(saveptr); + } else if(strcmp(action, "resizeleft") == 0) { + keybinding->action = KEYBINDING_RESIZE_TILE_HORIZONTAL; + keybinding->data.i = -10; + } else if(strcmp(action, "resizeright") == 0) { + keybinding->action = KEYBINDING_RESIZE_TILE_HORIZONTAL; + keybinding->data.i = 10; + } else if(strcmp(action, "resizedown") == 0) { + keybinding->action = KEYBINDING_RESIZE_TILE_VERTICAL; + keybinding->data.i = 10; + } else if(strcmp(action, "resizeup") == 0) { + keybinding->action = KEYBINDING_RESIZE_TILE_VERTICAL; + keybinding->data.i = -10; + } else if(strcmp(action, "workspace") == 0) { + keybinding->action = KEYBINDING_SWITCH_WORKSPACE; + char *nws_str = strtok_r(NULL, " ", &saveptr); + if(nws_str == NULL) { + wlr_log(WLR_ERROR, + "Expected argument for \"workspace\" action, got none."); + return -1; + } + + long ws = strtol(nws_str, NULL, 10); + if(ws < 1) { + wlr_log(WLR_ERROR, + "Workspace number must be a integer number larger or equal " + "to 1. Got %ld", + ws); + return -1; + } + keybinding->data.u = ws - 1; + } else if(strcmp(action, "movetoworkspace") == 0) { + keybinding->action = KEYBINDING_MOVE_VIEW_TO_WORKSPACE; + char *nws_str = strtok_r(NULL, " ", &saveptr); + if(nws_str == NULL) { + wlr_log(WLR_ERROR, + "Expected argument for \"workspace\" action, got none."); + return -1; + } + + long ws = strtol(nws_str, NULL, 10); + if(ws < 1) { + wlr_log(WLR_ERROR, + "Workspace number must be an integer larger or equal to 1. " + "Got %ld", + ws); + return -1; + } + keybinding->data.u = ws - 1; + } else if(strcmp(action, "exchangeleft") == 0) { + keybinding->action = KEYBINDING_SWAP_LEFT; + } else if(strcmp(action, "exchangeright") == 0) { + keybinding->action = KEYBINDING_SWAP_RIGHT; + } else if(strcmp(action, "exchangeup") == 0) { + keybinding->action = KEYBINDING_SWAP_TOP; + } else if(strcmp(action, "exchangedown") == 0) { + keybinding->action = KEYBINDING_SWAP_BOTTOM; + } else if(strcmp(action, "focusleft") == 0) { + keybinding->action = KEYBINDING_FOCUS_LEFT; + } else if(strcmp(action, "focusright") == 0) { + keybinding->action = KEYBINDING_FOCUS_RIGHT; + } else if(strcmp(action, "focusup") == 0) { + keybinding->action = KEYBINDING_FOCUS_TOP; + } else if(strcmp(action, "focusdown") == 0) { + keybinding->action = KEYBINDING_FOCUS_BOTTOM; + } else if(strcmp(action, "movetonextscreen") == 0) { + keybinding->action = KEYBINDING_MOVE_VIEW_TO_NEXT_OUTPUT; + } else if(strcmp(action, "switchvt") == 0) { + keybinding->action = KEYBINDING_CHANGE_TTY; + char *ntty = strtok_r(NULL, " ", &saveptr); + if(ntty == NULL) { + wlr_log(WLR_ERROR, + "Expected argument for \"switchvt\" command, got none."); + return -1; + } + long tty = strtol(ntty, NULL, 10); + keybinding->data.u = tty; + } else if(strcmp(action, "mode") == 0) { + keybinding->action = KEYBINDING_SWITCH_MODE; + char *mode = strtok_r(NULL, " ", &saveptr); + if(mode == NULL) { + wlr_log(WLR_ERROR, + "Expected mode after \"switch_mode\". Got nothing."); + return -1; + } + int mode_idx = get_mode_index_from_name(server->modes, mode); + if(mode_idx == -1) { + wlr_log(WLR_ERROR, "Unknown mode \"%s\" for switch_mode", mode); + return -1; + } + keybinding->data.u = (unsigned int)mode_idx; + } else if(strcmp(action, "setmode") == 0) { + keybinding->action = KEYBINDING_SWITCH_DEFAULT_MODE; + char *mode = strtok_r(NULL, " ", &saveptr); + if(mode == NULL) { + wlr_log( + WLR_ERROR, + "Expected mode after \"switch_default_mode\". Got nothing."); + return -1; + } + int mode_idx = get_mode_index_from_name(server->modes, mode); + if(mode_idx == -1) { + wlr_log(WLR_ERROR, "Unknown mode \"%s\" for switch_default_mode", + mode); + return -1; + } + keybinding->data.u = (unsigned int)mode_idx; + } else if(strcmp(action, "bind") == 0) { + keybinding->action = KEYBINDING_DEFINEKEY; + keybinding->data.kb = parse_bind(server, &saveptr); + if(keybinding->data.kb == NULL) { + return -1; + } + } else if(strcmp(action, "definekey") == 0) { + keybinding->action = KEYBINDING_DEFINEKEY; + keybinding->data.kb = parse_definekey(server, &saveptr); + if(keybinding->data.kb == NULL) { + return -1; + } + } else if(strcmp(action, "background") == 0) { + keybinding->action = KEYBINDING_BACKGROUND; + if(parse_background(server, keybinding->data.color, &saveptr) != 0) { + return -1; + } + } else if(strcmp(action, "escape") == 0) { + keybinding->action = KEYBINDING_DEFINEKEY; + keybinding->data.kb = parse_escape(&saveptr); + if(keybinding->data.kb == NULL) { + return -1; + } + } else if(strcmp(action, "definemode") == 0) { + keybinding->action = KEYBINDING_DEFINEMODE; + keybinding->data.c = parse_definemode(&saveptr); + if(keybinding->data.c == NULL) { + return -1; + } + } else if(strcmp(action, "workspaces") == 0) { + keybinding->action = KEYBINDING_WORKSPACES; + keybinding->data.i = parse_workspaces(&saveptr); + if(keybinding->data.i < 0) { + return -1; + } + } else { + wlr_log(WLR_ERROR, "Error, unsupported action \"%s\".", action); return -1; } - (*modes)[length - 1] = strdup(mode); return 0; } int parse_rc_line(struct cg_server *server, char *line) { - char *saveptr = NULL; // Used internally by strtok_r - char *command = strtok_r(line, " ", &saveptr); - if(command == NULL) { - return 0; - } - if(strcmp(command, "bind") == 0) { - if(parse_bind(server, server->keybindings, &saveptr) != 0) { - return -1; - } - } else if(strcmp(command, "definekey") == 0) { - if(parse_definekey(server, server->keybindings, &saveptr) != 0) { - return -1; - } - } else if(strcmp(command, "background") == 0) { - if(parse_background(server, &saveptr) != 0) { - return -1; - } - } else if(strcmp(command, "escape") == 0) { - if(parse_escape(server->keybindings, &saveptr) != 0) { - return -1; - } - } else if(strcmp(command, "definemode") == 0) { - if(parse_definemode(&server->modes, &saveptr) != 0) { - return -1; - } - } else if(strcmp(command, "exec") == 0) { - if(parse_and_run_exec(&saveptr) != 0) { - return -1; - } - } else if(strcmp(command, "workspaces") == 0) { - char *nws_str = strtok_r(NULL, " ", &saveptr); - if(nws_str == NULL) { - wlr_log(WLR_ERROR, - "Expected argument for \"workspaces\" command, got none."); - return -1; - } - long nws = strtol(nws_str, NULL, 10); - if(!(1 <= nws && nws <= 30)) { - wlr_log(WLR_ERROR, - "More than 30 workspaces are not supported. Received %li", - nws); - return -1; - } - struct cg_output *output; - wl_list_for_each(output, &server->outputs, link) { - for(unsigned int i = nws; i < server->nws; ++i) { - free(output->workspaces[i]); - } - struct cg_workspace **new_workspaces = realloc( - output->workspaces, nws * sizeof(struct cg_workspace *)); - if(new_workspaces == NULL) { - wlr_log(WLR_ERROR, "Error reallocating memory for workspaces."); - return -1; - } - output->workspaces = new_workspaces; - for(unsigned int i = server->nws; i < nws; ++i) { - output->workspaces[i] = full_screen_workspace(output); - wl_list_init(&output->workspaces[i]->views); - wl_list_init(&output->workspaces[i]->unmanaged_views); - } - } - server->nws = nws; - } else { - wlr_log(WLR_ERROR, "Unsupported command \"%s\" in config file", - command); + char *saveptr = strdup(line); // Used internally by strtok_r + + struct keybinding *keybinding = malloc(sizeof(struct keybinding)); + if(keybinding == NULL) { + wlr_log(WLR_ERROR, + "Failed to allocate memory for temporary keybinding struct."); + free(keybinding); + free(saveptr); return -1; } + if(parse_command(server, keybinding, saveptr) != 0) { + wlr_log(WLR_ERROR, "Error parsing config file."); + free(keybinding); + free(saveptr); + return -1; + } + run_action(keybinding->action, server, keybinding->data); + keybinding_free(keybinding, false); + free(saveptr); return 0; }