Release 1.1.0

- Unify commands and actions. See Issue 4 in Bugs.md
This commit is contained in:
Cagebreak Signing Key 1 2020-04-01 13:41:37 +00:00
commit 40e1313a12
16 changed files with 982 additions and 410 deletions

24
Bugs.md
View file

@ -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...
```

View file

@ -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)

View file

@ -11,7 +11,10 @@
#include "config.h"
#include <fcntl.h>
#include <fontconfig/fontconfig.h>
#include <getopt.h>
#include <pango.h>
#include <pango/pangocairo.h>
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
@ -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;
}

View file

@ -1,11 +1,29 @@
/* This file is used by the fuzzer in order to prevent executing shell commands.
*/
#define _GNU_SOURCE
#include <time.h>
struct tm *(*orig_localtime)(const time_t *timep);
#include "../output.h"
#include <cairo.h>
#include <cairo/cairo.h>
#include <stdlib.h>
#include <wlr/render/wlr_renderer.h>
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;
}

View file

@ -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 <signal.h>
#define _POSIX_C_SOURCE 200812L
#include "config.h"
#include <fcntl.h>
#include <getopt.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/wait.h>
#include <unistd.h>
#include <fontconfig/fontconfig.h>
#include <pango.h>
#include <pango/pangocairo.h>
#include <wayland-server-core.h>
#include <wlr/backend.h>
#include <wlr/render/wlr_renderer.h>
#include <wlr/types/wlr_compositor.h>
#include <wlr/types/wlr_cursor.h>
#include <wlr/types/wlr_data_device.h>
#include <wlr/types/wlr_export_dmabuf_v1.h>
#include <wlr/types/wlr_gamma_control_v1.h>
#include <wlr/types/wlr_idle.h>
#include <wlr/types/wlr_idle_inhibit_v1.h>
#include <wlr/types/wlr_output_damage.h>
#include <wlr/types/wlr_output_layout.h>
#include <wlr/types/wlr_screencopy_v1.h>
#include <wlr/types/wlr_server_decoration.h>
#if CG_HAS_XWAYLAND
#include <wlr/types/wlr_xcursor_manager.h>
#endif
#include <wlr/types/wlr_xdg_decoration_v1.h>
#include <wlr/types/wlr_xdg_output_v1.h>
#include <wlr/types/wlr_xdg_shell.h>
#include <wlr/util/log.h>
#if CG_HAS_XWAYLAND
#include <wlr/xwayland.h>
#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;
}

View file

@ -17,6 +17,7 @@ endif
override_lib = shared_library('execl_override',
[ 'execl_override.c' ],
dependencies: [ pixman,cairo,pango,pangocairo ],
install: false
)

View file

@ -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 "

View file

@ -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 */

View file

@ -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 <key> <keybinding>` is equivalent to
> `definekey root <key> <action>`
> supplied action. `bind <key> <command>` is equivalent to
>> `definekey root <key> <command>`
**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 <mode> <key> <action>`
>> `definekey <mode> <key> <command>`
**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 <mode>`
@ -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 <key>` is equivalent to `definekey top <key> switch_mode root`
**exec**
**exchangedown**
> Executes the supplied shell command using *`sh -c "<command>"`*. Synopsis:
>
>> `exec <command>`
**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 <n>**
> Change to the n-th workspace
**movetoworkspace <n>**
> 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 "<command>"`*. Synopsis:
>
>> `exec <command>`
**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 <n>**
> Switch to tty n
> Split current tile horizontally
**mode <mode>**
> Enter mode "`<mode>`". After a keybinding is processed, return to default mode
**movetonextscreen**
> Move the current window to the next screen
**movetoworkspace <n>**
> 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 <mode>**
> Set the default mode to `<mode>`
**switchvt <n>**
> Switch to tty n
**time**
> Display time
**vsplit**
> Split current tile vertically
**workspace <n>**
> 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:

View file

@ -1,4 +1,4 @@
% CAGEBREAK(1) Version 1.0 | Cagebreak Manual
% CAGEBREAK(1) Version 1.1 | Cagebreak Manual
# NAME

View file

@ -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',

View file

@ -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);

View file

@ -1,4 +1,4 @@
#include "cairo.h"
#include <cairo.h>
#include <cairo/cairo.h>
#include <pango/pangocairo.h>
#include <stdarg.h>

540
parse.c
View file

@ -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;
}

BIN
signatures/1.0.6.sig Normal file

Binary file not shown.

Binary file not shown.