From 40e1313a12fcc5fed5bf5d293247e132ff34c459 Mon Sep 17 00:00:00 2001 From: Cagebreak Signing Key 1 Date: Wed, 1 Apr 2020 13:41:37 +0000 Subject: [PATCH] Release 1.1.0 - Unify commands and actions. See Issue 4 in Bugs.md --- Bugs.md | 24 ++ README.md | 36 ++- cagebreak.c | 8 + fuzz/execl_override.c | 28 +- fuzz/fuzz-parse.c | 445 ++++++++++++++++++++++++++++--- fuzz/meson.build | 1 + keybinding.c | 180 +++++++++++-- keybinding.h | 9 + man/cagebreak-config.5.md | 204 +++++++------- man/cagebreak.1.md | 2 +- meson.build | 5 +- message.c | 6 + pango.c | 2 +- parse.c | 540 +++++++++++++++++++------------------- signatures/1.0.6.sig | Bin 0 -> 566 bytes signatures/cagebreak.sig | Bin 566 -> 566 bytes 16 files changed, 1031 insertions(+), 459 deletions(-) create mode 100644 signatures/1.0.6.sig diff --git a/Bugs.md b/Bugs.md index 491d2c6..b34f4f6 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.1.0 + +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/README.md b/README.md index c793ac0..619fd75 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,7 @@ # Cagebreak: A Wayland Tiling Compositor Inspired by Ratpoison This is Cagebreak, a Wayland tiling compositor. The goal of this project is to -provide a successor to ratpoison for wayland users. However, this is +provide a successor to ratpoison for Wayland users. However, this is no reimplementation of ratpoison. Should you like to know if a feature will be implemented, open an issue or get in touch with the development team. @@ -83,7 +83,7 @@ git tag -v version git push --tags origin master ``` -and a log message roughly describing the features is added in the commit. +and a log message roughly describing the features added in the commit. In the past, our git history did not always reflect this scheme. @@ -94,8 +94,9 @@ Release checklist * [ ] Cursory testing * [ ] libfuzzer testing * [ ] Version Number - * [ ] -v flag + * [ ] meson.build * [ ] git tag + * [ ] man pages * [ ] Relevant Documentation * [ ] New features documented * [ ] man page @@ -131,7 +132,7 @@ The full public keys can be found in `keys/` along with any revocation certifica Cagebreak offers reproducible builds given the exact library versions specified in `meson.build`. Should a version mismatch occur, a warning will be emitted. We have decided on this compromise to allow flexibility and security. In general we will -adapt the versions to the packages available under archlinux at the time of +adapt the versions to the packages available under arch linux at the time of release. #### Reproducible Build Instructions @@ -147,6 +148,11 @@ ninja -C build For every release after 1.0.5, hashes will be provided. +1.1.0 + + * sha 256: fc393e225c549f893b9e21e8b904e546d5857bac1d905b3d26334c8f8a1cda11 + * sha 512: 8b94b1069e767202bcab087cb592eadc42f0453d17ee119d48760be00bc66278cf0fa60bb09308d30cb551df6a7a3e26e2ab8b29c04f0e953cc6906542cd2d6f + 1.0.6 * sha 256: 712ae9a8f17a9e589e108f0d503da203cc5eaf1c4a6ca6efb5b4c83b432ce0b8 @@ -163,12 +169,28 @@ will be named after their release version. Along with the project source code, a fuzzing framework based on `libfuzzer` is supplied. This allows for the testing of the parsing code responsible for reading -the `cagebreak` configuration file. When the `libfuzzer` is available (please +the `cagebreak` configuration file. When `libfuzzer` is available (please use the `clang` compiler to enable it), building the fuzz-testing software can be enabled by passing `-Dfuzz=true` to meson. This generates a `build/fuzz/fuzz-parse` binary according to the `libfuzzer` specifications. Further documentation on how to run this binary can be found [here](https://llvm.org/docs/LibFuzzer.html). +Here is an example workflow: + +``` +rm -rf build +CC=clang meson build -Dfuzz=true -Db_sanitize=address,undefined -Db_lundef=false +ninja -C build/ +mkdir build/fuzz_corpus +cp examples/config build/fuzz_corpus/ +WLR_BACKENDS=headless ./build/fuzz/fuzz-parse -jobs=12 -max_len=50000 -close_fd_mask=3 build/fuzz_corpus/ +``` + +You may want to tweak `-jobs` or add other options depending on your own setup. +We have found code path discovery to increase rapidly when the fuzzer is supplied +with an initial config file. We are working on improving our fuzzing coverage to +find bugs in other areas of the code. + ## Bugs For any bug, please [create an @@ -184,6 +206,10 @@ independent of github, in case this service is unavailable. Adds basic tiling window manager functionality to cage. +### Release 1.1.0 + +Unifies commands and actions. See Issue 4 in Bugs.md. + ## License Please see [LICENSE](https://github.com/project-repo/cagebreak/blob/master/LICENSE) 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..e5b75cb 100644 --- a/fuzz/fuzz-parse.c +++ b/fuzz/fuzz-parse.c @@ -1,75 +1,412 @@ -#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; +#ifdef DEBUG + server->debug_damage_tracking = false; +#endif + 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 +415,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/man/cagebreak-config.5.md b/man/cagebreak-config.5.md index bbf3096..dffc8b3 100644 --- a/man/cagebreak-config.5.md +++ b/man/cagebreak-config.5.md @@ -1,4 +1,4 @@ -% CAGEBREAK-CONFIG(1) Version 1.0 | Cagebreak Manual +% CAGEBREAK-CONFIG(1) Version 1.1 | Cagebreak Manual # NAME @@ -18,19 +18,23 @@ by prepending a line with the # symbol. ## COMMANDS +**abort** + +> Return to the default mode without running any command + **background - Set background color** > Set the background color. This command expects three floating point numbers -> between 0 and 1, specifying the r,g and b values respectively. +> between 0 and 1, specifying the r, g and b values respectively. > (e.g. "background 1.0 0.0 0.0" sets to background color to red) > There is no support for specifying a background image. -**bind - Bind key to action in command mode** +**bind - Bind key to command in root mode** -> This command requires a key (see **KEY DEFINITIONS**) and an action (see **ACTIONS**) as an argument. +> This command requires a key (see **KEY DEFINITIONS**) and a command (see **COMMANDS**) as an argument. > Subsequently, pressing this key while in command mode executes the -> supplied action. `bind ` is equivalent to -> `definekey root ` +> supplied action. `bind ` is equivalent to +>> `definekey root ` **definekey - Bind key to action in arbitrary mode** @@ -38,13 +42,13 @@ by prepending a line with the # symbol. > difference that the mode in which the keybinding is activated is > specified by the user. A call to this function is to be structured as follows: > ->> `definekey ` +>> `definekey ` **definemode** > This command requires a single argument; the name of the mode to be defined. > Subsequent to a call to this function, the defined mode may be used along with -> the definekey command to create a custom keymapping. Synopsis: +> the definekey command to create a custom key mapping. Synopsis: > >> `definemode ` @@ -53,90 +57,9 @@ by prepending a line with the # symbol. > Defines the key with which the current mode can be changed to "root". > `escape ` is equivalent to `definekey top switch_mode root` -**exec** +**exchangedown** -> Executes the supplied shell command using *`sh -c ""`*. Synopsis: -> ->> `exec ` - -**workspaces** - -> Requires a single integer larger than 1 and less than 30 as an argument. Sets the number of -> workspaces to the supplied number - -# ACTIONS - -**vsplit** - -> Split current tile vertically - -**hsplit** - -> Split current tile horizontally - -**quit** - -> Exit cagebreak - -**focus** - -> Focus next tile - -**focusprev** - -> Focus previous tile - -**next** - -> Focus next window in current tile - -**prev** - -> Focus previous window in current tile - -**only** - -> Remove all splits and make the current window fill the entire screen - -**abort** - -> Return to the default mode without running any action - -**time** - -> Display time - -**nextscreen** - -> Focus the next screen - -**prevscreen** - -> Focus the previous screen - -**resizeleft** - -> Resize the current tile towards the left - -**resizeright** - -> Resize the current tile towards the right - -**resizedown** - -> Resize the current tile towards the bottom - -**resizeup** - -> Resize the current tile towards the top - -**workspace ** - -> Change to the n-th workspace - -**movetoworkspace ** - -> Move the currently focused window to the n-th workspace +> Exchange the current window with the window in the tile to the bottom **exchangeleft** @@ -150,14 +73,28 @@ by prepending a line with the # symbol. > Exchange the current window with the window in the tile to the top -**exchangedown** +**exec** -> Exchange the current window with the window in the tile to the bottom +> Executes the supplied shell command using *`sh -c ""`*. Synopsis: +> +>> `exec ` + +**focus** + +> Focus next tile + +**focusdown** + +> Focus the tile to the bottom **focusleft** > Focus the tile to the left +**focusprev** + +> Focus previous tile + **focusright** > Focus the tile to the right @@ -166,26 +103,87 @@ by prepending a line with the # symbol. > Focus the tile to the top -**focusdown** +**hsplit** -> Focus the tile to the bottom - -**movetonextscreen** - -> Move the current window to the next screen - -**switchvt ** - -> Switch to tty n +> Split current tile horizontally **mode ** > Enter mode "``". After a keybinding is processed, return to default mode +**movetonextscreen** + +> Move the current window to the next screen + +**movetoworkspace ** + +> Move the currently focused window to the n-th workspace + +**next** + +> Focus next window in current tile + +**nextscreen** + +> Focus the next screen + +**only** + +> Remove all splits and make the current window fill the entire screen + +**prev** + +> Focus previous window in current tile + +**prevscreen** + +> Focus the previous screen + +**quit** + +> Exit cagebreak + +**resizedown** + +> Resize the current tile towards the bottom + +**resizeleft** + +> Resize the current tile towards the left + +**resizeright** + +> Resize the current tile towards the right + +**resizeup** + +> Resize the current tile towards the top + **setmode ** > Set the default mode to `` +**switchvt ** + +> Switch to tty n + +**time** + +> Display time + +**vsplit** + +> Split current tile vertically + +**workspace ** + +> Change to the n-th workspace + +**workspaces** + +> Requires a single integer larger than 1 and less than 30 as an argument. Sets the number of +> workspaces to the supplied number + # MODES By default, three modes are defined: diff --git a/man/cagebreak.1.md b/man/cagebreak.1.md index 6d5b051..cacd1c2 100644 --- a/man/cagebreak.1.md +++ b/man/cagebreak.1.md @@ -1,4 +1,4 @@ -% CAGEBREAK(1) Version 1.0 | Cagebreak Manual +% CAGEBREAK(1) Version 1.1 | Cagebreak Manual # NAME diff --git a/meson.build b/meson.build index 6b2e509..959d35f 100644 --- a/meson.build +++ b/meson.build @@ -1,5 +1,5 @@ project('cagebreak', 'c', - version: '1.0.6', + version: '1.1.0', license: 'MIT', default_options: [ 'c_std=c11', @@ -165,7 +165,6 @@ endforeach foreach header : cagebreak_header_strings cagebreak_headers += files(header) endforeach - cagebreak_dependencies_dict = { 'server_protos': server_protos, 'wayland_server': wayland_server, @@ -182,7 +181,7 @@ cagebreak_dependencies_dict = { } reproducible_build_versions = { - 'server_protos': '1.0.6', + 'server_protos': '-1', 'wayland_server': '1.18.0', 'wayland_client': '1.18.0', 'wayland_cursor': '1.18.0', 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; } diff --git a/signatures/1.0.6.sig b/signatures/1.0.6.sig new file mode 100644 index 0000000000000000000000000000000000000000..3a641bee171da79f5d93fc627d5230d0786eb715 GIT binary patch literal 566 zcmV-60?GY}0y6{v0SEvc79j-ZpKYEIH7WG5|K!!g`E=I|>?~Ua0$zY^*8mC$5XAX( z*9`0|Tc$z}|64$gG`MHKZ=AWaCuyalWsk4kO22O@Vk;D@>@jJuMc1+zpA&G+@{_qV zY9~dDUAwMOK2VHMa&iLO!uOvyglM zhBjpy;1P1#FHLT#7v>lpVVi6Z)1C)>q}@RqNpe^BE8D&&DY2=Mik-G~dS%Sl z0UXfHXw_6n^tAebKouvI4 z_=BR(cP)Ly>xoA^o*f2+Q=qp@z(YPty4N-AOu~Yv{8k&L$Lf7F?L)YbTwZlvADEF0 E$r~dTVE_OC literal 0 HcmV?d00001 diff --git a/signatures/cagebreak.sig b/signatures/cagebreak.sig index 3a641bee171da79f5d93fc627d5230d0786eb715..ac5a0e800ff52d34973b17b4349461fee72fbd32 100644 GIT binary patch delta 542 zcmV+(0^$9(1hxc_BY%W>z5ogd5XAX(*9`0|TO2eH0JT8XB2TlbW_=Xa#kN@qTWL1V z>FRsd$`XXjWfmfAwn0iW4MZ|YvHCHnh{!wl=LkCb z!a;8f2`&#+FV^Soqxb2JJIA*(up!gv{cSXU35ns!zYyvwnOtY5T>#3G-F&Cc<|X|w z$%6AuK3avHEq_3_6Mwsb@F5hc3AdMS5mcuu{q1OTyS}?O3p!=U1sDb^O0DWDg4Jv> z88bcfG+>0J?N1H&%YV7lqsomoxoTVEqR{@|j?eq+ zf_Wszus)TC);~$H9CeLJWCm>7Ssiobp&>ao;G33i?%Kke3Sv?%2suKd!K7J+77HoU z&jO)2-!K8RgC50dKWg-iA^OEiO}_fo5@jJuMc1+zpA&G+@{_qVO`Y09k!O{I7iHTUnPT zkIYpcNNuceS3$Os?#EXLAyy`{?}uNjVHyd*;iJ~`1b>hBSmWUVf{UFQ>F9NW=K#IT@oo||J1{+Y@VaDU&9OEld zP{GeM)oL;krO_f@kyJ9bTNNg&f~=06r2QHAgQCuNEq%o6iAOx19R`F`ptnrGLq1Bn g*EQ@+!WV+4{8k&L$Lf7F?L)YbTwZlvADEF0$t#x(F8}}l