NEDM/keybinding.c

2245 lines
66 KiB
C

// Copyright 2020 - 2025, project-repo and the NEDM contributors
// SPDX-License-Identifier: MIT
#define _POSIX_C_SOURCE 200809L
#include <signal.h>
#include <string.h>
#include <sys/wait.h>
#include <unistd.h>
#include <wayland-server-core.h>
#include <wlr/backend/multi.h>
#include <wlr/backend/session.h>
#include <wlr/types/wlr_cursor.h>
#include <wlr/types/wlr_keyboard_group.h>
#include <wlr/types/wlr_output_layout.h>
#include <wlr/types/wlr_scene.h>
#include <wlr/types/wlr_xcursor_manager.h>
#include <wlr/util/log.h>
#include "input.h"
#include "input_manager.h"
#include "keybinding.h"
#include "message.h"
#include "output.h"
#include "seat.h"
#include "server.h"
#include "util.h"
#include "view.h"
#include "workspace.h"
char *keybinding_action_string[] = {FOREACH_KEYBINDING(GENERATE_STRING)};
int
keybinding_resize(struct keybinding_list *list) {
if(list->length == list->capacity) {
list->capacity *= 2;
struct keybinding **new_list =
realloc(list->keybindings, sizeof(void *) * list->capacity);
if(new_list == NULL) {
return -1;
} else {
list->keybindings = new_list;
return 0;
}
}
return 0;
}
struct keybinding **
find_keybinding(const struct keybinding_list *list,
const struct keybinding *keybinding) {
struct keybinding **it = list->keybindings;
for(size_t i = 0; i < list->length; ++i, ++it) {
if(!(keybinding->modifiers ^ (*it)->modifiers) &&
keybinding->mode == (*it)->mode && keybinding->key == (*it)->key) {
return it;
}
}
return NULL;
}
void
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;
case KEYBINDING_CONFIGURE_OUTPUT:
free(keybinding->data.o_cfg->output_name);
free(keybinding->data.o_cfg);
break;
case KEYBINDING_CONFIGURE_INPUT:
free(keybinding->data.i_cfg->identifier);
if(keybinding->data.i_cfg->mapped_from_region) {
free(keybinding->data.i_cfg->mapped_from_region);
}
if(keybinding->data.i_cfg->mapped_to_output) {
free(keybinding->data.i_cfg->mapped_to_output);
}
free(keybinding->data.o_cfg);
break;
case KEYBINDING_CONFIGURE_MESSAGE:
if(keybinding->data.m_cfg->font != NULL) {
free(keybinding->data.m_cfg->font);
}
free(keybinding->data.m_cfg);
break;
case KEYBINDING_DISPLAY_MESSAGE:
if(keybinding->data.c != NULL) {
free(keybinding->data.c);
}
break;
case KEYBINDING_SEND_CUSTOM_EVENT:
if(keybinding->data.c != NULL) {
free(keybinding->data.c);
}
break;
case KEYBINDING_SETMODECURSOR:
if(keybinding->data.cs[0] != NULL) {
free(keybinding->data.cs[0]);
}
if(keybinding->data.cs[1] != NULL) {
free(keybinding->data.cs[1]);
}
default:
break;
}
free(keybinding);
}
int
keybinding_list_push(struct keybinding_list *list,
struct keybinding *keybinding) {
/* Error resizing list */
if(keybinding_resize(list) != 0) {
return -1;
}
/*Maintain that only a single keybinding for a key, modifier and mode may
* exist*/
struct keybinding **found_keybinding = find_keybinding(list, keybinding);
if(found_keybinding != NULL) {
keybinding_free(*found_keybinding, true);
*found_keybinding = keybinding;
wlr_log(WLR_DEBUG, "A keybinding was found twice in the config file.");
} else {
list->keybindings[list->length] = keybinding;
++list->length;
}
return 0;
}
struct keybinding_list *
keybinding_list_init(void) {
struct keybinding_list *list = malloc(sizeof(struct keybinding_list));
list->keybindings = malloc(sizeof(struct keybinding *));
list->capacity = 1;
list->length = 0;
return list;
}
void
keybinding_list_free(struct keybinding_list *list) {
if(!list) {
return;
}
for(unsigned int i = 0; i < list->length; ++i) {
keybinding_free(list->keybindings[i], true);
}
free(list->keybindings);
free(list);
}
// Returns whether x is between a and b (a exclusive, b exclusive) where
// a<b
bool
is_between_strict(int a, int b, int x) {
return a < x && x < b;
}
struct nedm_tile *
tile_from_id(struct nedm_server *server, uint32_t id) {
struct nedm_tile *tile = NULL;
struct nedm_output *outp_it;
wl_list_for_each(outp_it, &server->outputs, link) {
for(unsigned int i = 0; i < server->nws && tile == NULL; ++i) {
bool first = true;
for(struct nedm_tile *tile_it = outp_it->workspaces[i]->focused_tile;
first || tile_it != outp_it->workspaces[i]->focused_tile;
tile_it = tile_it->next) {
first = false;
if(id == tile_it->id) {
tile = tile_it;
break;
}
}
}
if(tile != NULL) {
break;
}
}
return tile;
}
struct nedm_view *
view_from_id(struct nedm_server *server, uint32_t id) {
struct nedm_view *view = NULL;
struct nedm_output *outp_it;
wl_list_for_each(outp_it, &server->outputs, link) {
for(unsigned int i = 0; i < server->nws && view == NULL; ++i) {
struct nedm_view *view_it;
wl_list_for_each(view_it, &outp_it->workspaces[i]->views, link) {
if(id == view_it->id) {
view = view_it;
break;
}
}
}
if(view != NULL) {
break;
}
}
return view;
}
struct nedm_output *
output_from_num(struct nedm_server *server, int num) {
struct nedm_output *it;
int count = 1;
wl_list_for_each(it, &server->outputs, link) {
if(count == num) {
return it;
}
++count;
}
return NULL;
}
struct nedm_tile *
find_right_tile(const struct nedm_tile *tile) {
struct nedm_tile *it = tile->next;
int center = tile->tile.y + tile->tile.height / 2;
while(it != tile) {
if(it->tile.x == tile->tile.x + tile->tile.width) {
if(is_between_strict(it->tile.y, it->tile.y + it->tile.height,
center) ||
it->tile.y + it->tile.height == center) {
return it;
}
}
it = it->next;
}
return NULL;
}
struct nedm_tile *
find_left_tile(const struct nedm_tile *tile) {
struct nedm_tile *it = tile->next;
int center = tile->tile.y + tile->tile.height / 2;
while(it != tile) {
if(it->tile.x + it->tile.width == tile->tile.x) {
if(is_between_strict(it->tile.y, it->tile.y + it->tile.height,
center) ||
it->tile.y + it->tile.height == center) {
return it;
}
}
it = it->next;
}
return NULL;
}
struct nedm_tile *
find_top_tile(const struct nedm_tile *tile) {
struct nedm_tile *it = tile->next;
int center = tile->tile.x + tile->tile.width / 2;
while(it != tile) {
if(it->tile.y + it->tile.height == tile->tile.y) {
if(is_between_strict(it->tile.x, it->tile.x + it->tile.width,
center) ||
it->tile.x + it->tile.width == center) {
return it;
}
}
it = it->next;
}
return NULL;
}
struct nedm_tile *
find_bottom_tile(const struct nedm_tile *tile) {
struct nedm_tile *it = tile->next;
int center = tile->tile.x + tile->tile.width / 2;
while(it != tile) {
if(it->tile.y == tile->tile.y + tile->tile.height) {
if(is_between_strict(it->tile.x, it->tile.x + it->tile.width,
center) ||
it->tile.x + it->tile.width == center) {
return it;
}
}
it = it->next;
}
return NULL;
}
int *
get_compl_coord(struct nedm_tile *tile, int *(*get_coord)(struct nedm_tile *tile)) {
if(get_coord(tile) == &tile->tile.x) {
return &tile->tile.y;
} else {
return &tile->tile.x;
}
}
int *
get_compl_dim(struct nedm_tile *tile, int *(*get_dim)(struct nedm_tile *tile)) {
if(get_dim(tile) == &tile->tile.height) {
return &tile->tile.width;
} else {
return &tile->tile.height;
}
}
/* find_tile is the direction in which to search, get_dim returns the dimension
* which must be equal for merging to be possible and get_coord returns the
* coordinate which must be equal for merging to be possible. */
void
merge_tile(struct nedm_tile *tile,
struct nedm_tile *(*find_tile)(const struct nedm_tile *),
int *(*get_dim)(struct nedm_tile *tile),
int *(*get_coord)(struct nedm_tile *tile)) {
struct nedm_tile *merge_tile = find_tile(tile);
if(merge_tile == NULL || merge_tile == tile ||
*get_dim(tile) != *get_dim(merge_tile) ||
*get_coord(tile) != *get_coord(merge_tile)) {
return;
}
// There are at least two tiles once we reach this point, since merge_tile
// != tile
int merge_tile_id = merge_tile->id;
workspace_tile_update_view(merge_tile, NULL);
merge_tile->prev->next = merge_tile->next;
merge_tile->next->prev = merge_tile->prev;
if(merge_tile->workspace->focused_tile == merge_tile) {
merge_tile->workspace->focused_tile = tile;
}
*get_compl_dim(tile, get_dim) =
*get_compl_dim(merge_tile, get_dim) + *get_compl_dim(tile, get_dim);
*get_compl_coord(tile, get_coord) =
fmin(*get_compl_coord(merge_tile, get_coord),
*get_compl_coord(tile, get_coord));
if(tile->workspace->server->seat->cursor_tile == merge_tile) {
tile->workspace->server->seat->cursor_tile = tile;
}
free(merge_tile);
if(tile->view != NULL) {
view_maximize(tile->view, tile);
}
ipc_send_event(
tile->workspace->output->server,
"{\"event_name\":\"merge_tile\",\"tile_id\":%d,\"merge_tile_id\":%d,"
"\"workspace\":%d,\"output\":\"%s\",\"output_id\":%d}",
tile->id, merge_tile_id, tile->workspace->num + 1,
tile->workspace->output->name, output_get_num(tile->workspace->output));
}
void
keybinding_focus_tile(struct nedm_server *server, uint32_t tile_id);
void
swap_tiles(struct nedm_tile *tile, struct nedm_tile *swap_tile, bool follow) {
if(swap_tile == NULL || tile == NULL || swap_tile == tile) {
return;
}
struct nedm_server *server = tile->workspace->server;
struct nedm_view *tmp_view = tile->view;
struct nedm_view *tmp_swap_view = swap_tile->view;
workspace_tile_update_view(tile, NULL);
workspace_tile_update_view(swap_tile, tmp_view);
workspace_tile_update_view(tile, tmp_swap_view);
if(follow) {
keybinding_focus_tile(server, swap_tile->id);
} else {
seat_set_focus(
server->seat,
server->curr_output->workspaces[server->curr_output->curr_workspace]
->focused_tile->view);
}
ipc_send_event(
tile->workspace->output->server,
"{\"event_name\":\"swap_tile\",\"tile_id\":%d,\"swap_"
"tile_id\":%d,\"workspace\":%d,\"output\":\"%s\",\"output_id\":%d}",
tile->id, swap_tile->id, tile->workspace->num + 1,
tile->workspace->output->name, output_get_num(tile->workspace->output));
}
void
swap_tile(struct nedm_server *server, uint32_t tile_id,
struct nedm_tile *(*find_tile)(const struct nedm_tile *), bool follow) {
struct nedm_tile *tile = NULL;
if(tile_id == 0) {
tile =
server->curr_output->workspaces[server->curr_output->curr_workspace]
->focused_tile;
} else {
tile = tile_from_id(server, tile_id);
}
if(tile != NULL) {
struct nedm_tile *swap_tile = find_tile(tile);
swap_tiles(tile, swap_tile, follow);
}
}
int *
get_width(struct nedm_tile *tile) {
return &tile->tile.width;
}
int *
get_height(struct nedm_tile *tile) {
return &tile->tile.height;
}
int *
get_x(struct nedm_tile *tile) {
return &tile->tile.x;
}
int *
get_y(struct nedm_tile *tile) {
return &tile->tile.y;
}
void
merge_tile_left(struct nedm_tile *tile) {
merge_tile(tile, find_left_tile, get_height, get_y);
}
void
merge_tile_right(struct nedm_tile *tile) {
merge_tile(tile, find_right_tile, get_height, get_y);
}
void
merge_tile_top(struct nedm_tile *tile) {
merge_tile(tile, find_top_tile, get_width, get_x);
}
void
merge_tile_bottom(struct nedm_tile *tile) {
merge_tile(tile, find_bottom_tile, get_width, get_x);
}
void
swap_tile_left(struct nedm_server *server, uint32_t tile_id, bool follow) {
swap_tile(server, tile_id, find_left_tile, follow);
}
void
swap_tile_right(struct nedm_server *server, uint32_t tile_id, bool follow) {
swap_tile(server, tile_id, find_right_tile, follow);
}
void
swap_tile_top(struct nedm_server *server, uint32_t tile_id, bool follow) {
swap_tile(server, tile_id, find_top_tile, follow);
}
void
swap_tile_bottom(struct nedm_server *server, uint32_t tile_id, bool follow) {
swap_tile(server, tile_id, find_bottom_tile, follow);
}
void
focus_tile(struct nedm_tile *tile,
struct nedm_tile *(*find_tile)(const struct nedm_tile *tile)) {
struct nedm_tile *new_tile = find_tile(tile);
if(new_tile == NULL) {
return;
}
struct nedm_server *server = new_tile->workspace->server;
workspace_focus_tile(tile->workspace, new_tile);
seat_set_focus(server->seat, new_tile->view);
}
void
focus_tile_left(struct nedm_tile *tile) {
focus_tile(tile, find_left_tile);
}
void
focus_tile_right(struct nedm_tile *tile) {
focus_tile(tile, find_right_tile);
}
void
focus_tile_top(struct nedm_tile *tile) {
focus_tile(tile, find_top_tile);
}
void
focus_tile_bottom(struct nedm_tile *tile) {
focus_tile(tile, find_bottom_tile);
}
bool
intervalls_intersect(int x1, int x2, int y1, int y2) {
return y2 > x1 && y1 < x2;
}
bool
resize_allowed(struct nedm_tile *tile, const struct nedm_tile *parent,
int coord_offset, int dim_offset,
int *(*get_coord)(struct nedm_tile *tile),
int *(*get_dim)(struct nedm_tile *tile), struct nedm_tile *orig) {
if(coord_offset == 0 && dim_offset == 0) {
return true;
} else if(*get_dim(tile) + dim_offset <= 0) {
return false;
}
for(struct nedm_tile *it = tile->next; it != tile && it != NULL;
it = it->next) {
if(it == parent || it == orig) {
continue;
}
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;
}
}
}
}
return true;
}
void
resize(struct nedm_tile *tile, const struct nedm_tile *parent, int coord_offset,
int dim_offset, int *(*get_coord)(struct nedm_tile *tile),
int *(*get_dim)(struct nedm_tile *tile), struct nedm_tile *orig) {
if(coord_offset == 0 && dim_offset == 0) {
return;
}
for(struct nedm_tile *it = tile->next; it != tile && it != NULL;
it = it->next) {
if(it == parent || it == orig) {
continue;
}
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);
}
}
}
int old_x = tile->tile.x, old_y = tile->tile.y,
old_height = tile->tile.height, old_width = tile->tile.width;
*get_coord(tile) += coord_offset;
*get_dim(tile) += dim_offset;
if(tile->view != NULL) {
view_maximize(tile->view, tile);
}
ipc_send_event(tile->workspace->output->server,
"{\"event_name\":\"resize_tile\",\"tile_id\":%d,\"old_"
"dims\":\"[%d,%d,%d,%d]\",\"new_dims\":\"[%d,%d,%d,%d]\","
"\"workspace\":%d,\"output\":\"%s\",\"output_id\":%d}",
tile->id, old_x, old_y, old_height, old_width, tile->tile.x,
tile->tile.y, tile->tile.height, tile->tile.width,
tile->workspace->num + 1, tile->workspace->output->name,
output_get_num(tile->workspace->output));
}
bool
resize_allowed_horizontal(struct nedm_tile *tile, struct nedm_tile *parent,
int x_offset, int width_offset) {
return resize_allowed(tile, parent, x_offset, width_offset, get_x,
get_width, tile);
}
bool
resize_allowed_vertical(struct nedm_tile *tile, struct nedm_tile *parent,
int y_offset, int height_offset) {
return resize_allowed(tile, parent, y_offset, height_offset, get_y,
get_height, tile);
}
void
resize_horizontal(struct nedm_tile *tile, struct nedm_tile *parent, int x_offset,
int width_offset) {
resize(tile, parent, x_offset, width_offset, get_x, get_width, tile);
}
void
resize_vertical(struct nedm_tile *tile, struct nedm_tile *parent, int y_offset,
int height_offset) {
resize(tile, parent, y_offset, height_offset, get_y, get_height, tile);
}
/* hpixs: positiv -> right, negative -> left; vpixs: positiv -> down, negative
* -> up */
void
resize_tile(struct nedm_server *server, int hpixs, int vpixs, int tile_id) {
struct nedm_output *output = server->curr_output;
struct nedm_tile *tile =
output->workspaces[output->curr_workspace]->focused_tile;
if(tile_id != 0) {
tile = tile_from_id(server, tile_id);
}
if(tile == NULL) {
return;
}
/* First do the horizontal adjustment */
if(hpixs != 0 && tile->tile.width < output_get_layout_box(output).width &&
is_between_strict(0, output_get_layout_box(output).width,
tile->tile.width + hpixs)) {
int x_offset = 0;
/* In case we are on the total right, move the left edge of the tile */
if(tile->tile.x + tile->tile.width ==
output_get_layout_box(output).width) {
x_offset = -hpixs;
}
bool resize_allowed =
resize_allowed_horizontal(tile, NULL, x_offset, hpixs);
if(resize_allowed) {
resize_horizontal(tile, NULL, x_offset, hpixs);
}
}
/* Repeat for vertical */
if(vpixs != 0 && tile->tile.height < output_get_layout_box(output).height &&
is_between_strict(0, output_get_layout_box(output).height,
tile->tile.height + vpixs)) {
int y_offset = 0;
if(tile->tile.y + tile->tile.height ==
output_get_layout_box(output).height) {
y_offset = -vpixs;
}
bool resize_allowed =
resize_allowed_vertical(tile, NULL, y_offset, vpixs);
if(resize_allowed) {
resize_vertical(tile, NULL, y_offset, vpixs);
}
}
}
void
keybinding_workspace_fullscreen(struct nedm_server *server, uint32_t screen,
uint32_t workspace) {
struct nedm_output *output = server->curr_output;
uint32_t ws = output->curr_workspace;
if(screen != 0) {
output = output_from_num(server, screen);
ws = workspace;
}
if(output == NULL || ws >= server->nws) {
return;
}
output_make_workspace_fullscreen(output, ws);
ipc_send_event(server,
"{\"event_name\":\"fullscreen\",\"tile_id\":%d,"
"\"workspace\":%d,\"output\":\"%s\",\"output_id\":%d}",
output->workspaces[ws]->focused_tile->id,
output->workspaces[ws]->num + 1, output->name,
output_get_num(output));
}
// Switch to a differerent virtual terminal
static int
keybinding_switch_vt(struct nedm_server *server, unsigned int vt) {
if(wlr_backend_is_multi(server->backend)) {
if(server->session) {
wlr_session_change_vt(server->session, vt);
}
return 0;
}
return -1;
}
/* Split screen (vertical or horizontal)
* Important: Do not attempt to perform mathematical simplifications in this
* function without taking rounding errors into account. */
static void
keybinding_split_output(struct nedm_output *output, bool vertical,
float percentage) {
struct nedm_view *original_view = seat_get_focus(output->server->seat);
struct nedm_workspace *curr_workspace =
output->workspaces[output->curr_workspace];
int32_t width = curr_workspace->focused_tile->tile.width;
int32_t height = curr_workspace->focused_tile->tile.height;
int32_t x = curr_workspace->focused_tile->tile.x;
int32_t y = curr_workspace->focused_tile->tile.y;
int32_t new_width, new_height;
if(vertical) {
new_width = (int)(((float)width) * percentage);
new_height = height;
} else {
new_width = width;
new_height = (int)(((float)height) * percentage);
}
if(new_width < 1 || new_height < 1) {
return;
}
struct nedm_view *next_view = NULL;
struct nedm_view *it;
wl_list_for_each(it, &output->workspaces[output->curr_workspace]->views,
link) {
if(!view_is_visible(it) && original_view != it) {
next_view = it;
break;
}
}
int32_t new_x, new_y;
if(vertical) {
new_x = x + new_width;
new_y = y;
} else {
new_x = x;
new_y = y + new_height;
}
struct nedm_tile *new_tile = calloc(1, sizeof(struct nedm_tile));
if(!new_tile) {
wlr_log(WLR_ERROR, "Failed to allocate new tile for splitting");
return;
}
new_tile->id = output->server->tiles_curr_id;
++output->server->tiles_curr_id;
new_tile->tile.x = new_x;
new_tile->tile.y = new_y;
new_tile->tile.width = x + width - new_x;
new_tile->tile.height = y + height - new_y;
new_tile->prev = curr_workspace->focused_tile;
new_tile->next = curr_workspace->focused_tile->next;
workspace_tile_update_view(new_tile, next_view);
new_tile->workspace = curr_workspace;
curr_workspace->focused_tile->next->prev = new_tile;
curr_workspace->focused_tile->next = new_tile;
curr_workspace->focused_tile->tile.width = new_width;
curr_workspace->focused_tile->tile.height = new_height;
workspace_focus_tile(curr_workspace, curr_workspace->focused_tile);
if(next_view != NULL) {
view_maximize(next_view, new_tile);
}
if(original_view != NULL) {
view_maximize(original_view, curr_workspace->focused_tile);
}
ipc_send_event(
output->server,
"{\"event_name\":\"split\",\"tile_id\":%d,\"new_tile_id\":%d,"
"\"workspace\":%d,\"output\":\"%s\",\"output_id\":%d,\"vertical\":%d}",
curr_workspace->focused_tile->id, new_tile->id, curr_workspace->num + 1,
curr_workspace->output->name, output_get_num(curr_workspace->output),
vertical);
}
static void
keybinding_close_view(struct nedm_view *view) {
if(view == NULL) {
return;
}
struct nedm_output *outp = view->workspace->output;
uint32_t view_id = view->id;
uint32_t view_pid = view->impl->get_pid(view);
uint32_t tile_id = view->id;
uint32_t ws = view->workspace->num;
view->impl->close(view);
ipc_send_event(
outp->server,
"{\"event_name\":\"close\",\"view_id\":%d,\"view_pid\":%d,\"tile_id\":"
"%d,\"workspace\":%d,\"output\":\"%s\",\"output_id\":%d}",
view_id, view_pid, tile_id, ws + 1, outp->name, output_get_num(outp));
}
static void
keybinding_split_vertical(struct nedm_server *server, float percentage) {
keybinding_split_output(server->curr_output, true, percentage);
}
static void
keybinding_split_horizontal(struct nedm_server *server, float percentage) {
keybinding_split_output(server->curr_output, false, percentage);
}
static void
into_process(const char *command) {
execlp("sh", "sh", "-c", command, (char *)NULL);
_exit(1);
}
void
set_output(struct nedm_server *server, struct nedm_output *output) {
server->curr_output = output;
seat_set_focus(
server->seat,
server->curr_output->workspaces[server->curr_output->curr_workspace]
->focused_tile->view);
message_printf(server->curr_output, "Current Output");
}
void
keybinding_cycle_outputs(struct nedm_server *server, bool reverse,
bool trigger_event) {
struct nedm_output *output = NULL;
struct nedm_output *old_output = server->curr_output;
if(reverse) {
output = wl_container_of(server->curr_output->link.prev,
server->curr_output, link);
} else {
output = wl_container_of(server->curr_output->link.next,
server->curr_output, link);
}
if(&output->link == &server->outputs) {
if(reverse) {
output = wl_container_of(output->link.prev, output, link);
} else {
output = wl_container_of(output->link.next, output, link);
}
}
set_output(server, output);
if(trigger_event) {
ipc_send_event(
output->server,
"{\"event_name\":\"cycle_outputs\",\"old_output\":\"%s\",\"old_"
"output_id\":%d,"
"\"new_output\":\"%s\",\"new_output_id\":%d,\"reverse\":%d}",
old_output->name, output_get_num(old_output), output->name,
output_get_num(output), reverse);
}
}
/* Cycle through views, whereby the workspace does not change */
void
keybinding_cycle_views(struct nedm_server *server, struct nedm_tile *tile,
uint32_t view_id, bool reverse, bool ipc) {
if(tile == NULL) {
tile =
server->curr_output->workspaces[server->curr_output->curr_workspace]
->focused_tile;
}
struct nedm_view *current_view = tile->view;
struct nedm_workspace *ws = tile->workspace;
struct nedm_view *it_view, *next_view = NULL;
if(view_id == 0) {
if(reverse) {
wl_list_for_each(it_view, &ws->views, link) {
if(!view_is_visible(it_view)) {
next_view = it_view;
break;
}
}
} else {
wl_list_for_each_reverse(it_view, &ws->views, link) {
if(!view_is_visible(it_view)) {
next_view = it_view;
break;
}
}
}
} else {
next_view = view_from_id(server, view_id);
if(next_view == NULL || view_is_visible(next_view)) {
return;
}
}
if(next_view == NULL) {
return;
}
workspace_tile_update_view(tile, next_view);
if(tile ==
server->curr_output->workspaces[server->curr_output->curr_workspace]
->focused_tile) {
seat_set_focus(server->seat, next_view);
}
if(ipc) {
int curr_id = -1;
int curr_pid = -1;
if(current_view != NULL && current_view->link.next != ws->views.next) {
curr_id = current_view->id;
curr_pid = current_view->impl->get_pid(current_view);
}
ipc_send_event(
ws->output->server,
"{\"event_name\":\"cycle_views\",\"old_view_id\":%d,\"old_view_"
"pid\":%d,"
"\"new_view_id\":%d,\"new_view_pid\":%d,\"tile_id\":%d,"
"\"workspace\":%d,\"output\":\"%s\",\"output_id\":%d}",
curr_id, curr_pid, next_view == NULL ? -1 : (int)next_view->id,
next_view == NULL ? -1 : (int)next_view->impl->get_pid(next_view),
next_view->tile->id, ws->num + 1, ws->output->name,
output_get_num(ws->output));
}
}
int
keybinding_switch_ws(struct nedm_server *server, uint32_t ws) {
if(ws >= server->nws) {
wlr_log(WLR_ERROR,
"Requested workspace %u, but only have %u workspaces.", ws + 1,
server->nws);
return -1;
}
struct nedm_output *output = server->curr_output;
uint32_t old_ws = server->curr_output->curr_workspace;
workspace_focus(output, ws);
seat_set_focus(server->seat,
server->curr_output->workspaces[ws]->focused_tile->view);
message_printf(server->curr_output, "Workspace %d", ws + 1);
ipc_send_event(output->server,
"{\"event_name\":\"switch_ws\",\"old_workspace\":%d,"
"\"new_workspace\":%d,\"output\":\"%s\",\"output_id\":%d}",
old_ws + 1, ws + 1, output->name, output_get_num(output));
return 0;
}
void
keybinding_focus_tile(struct nedm_server *server, uint32_t tile_id) {
struct nedm_output *output = server->curr_output;
struct nedm_workspace *workspace = output->workspaces[output->curr_workspace];
struct nedm_tile *old_tile = workspace->focused_tile;
struct nedm_tile *tile = tile_from_id(server, tile_id);
if(tile == NULL) {
return;
}
if(server->curr_output != tile->workspace->output) {
set_output(server, tile->workspace->output);
}
if(server->curr_output->curr_workspace != (int)tile->workspace->num) {
keybinding_switch_ws(server, tile->workspace->num);
}
workspace_focus_tile(tile->workspace, tile);
struct nedm_view *next_view = tile->workspace->focused_tile->view;
seat_set_focus(server->seat, next_view);
char *view_title = "";
if (next_view && next_view->impl && next_view->impl->get_title) {
char *title = next_view->impl->get_title(next_view);
view_title = title ? title : "";
}
ipc_send_event(
output->server,
"{\"event_name\":\"focus_tile\",\"old_tile_id\":%d,\"new_tile_"
"id\":%d,\"old_workspace\":%d,\"new_workspace\":%d,\"old_output\":\"%"
"s\",\"old_output_id\":%d,\"output\":\"%s\",\"output_id\":%d,\"view_title\":\"%s\"}",
old_tile->id, tile->workspace->focused_tile->id, workspace->num + 1,
tile->workspace->num + 1, output->name, output_get_num(output),
tile->workspace->output->name, output_get_num(tile->workspace->output), view_title);
}
void
keybinding_cycle_tiles(struct nedm_server *server, bool reverse) {
struct nedm_output *output = server->curr_output;
struct nedm_workspace *workspace = output->workspaces[output->curr_workspace];
if(reverse) {
keybinding_focus_tile(server, workspace->focused_tile->prev->id);
} else {
keybinding_focus_tile(server, workspace->focused_tile->next->id);
}
}
void
keybinding_show_time(struct nedm_server *server) {
char *msg, *tmp;
time_t timep;
timep = time(NULL);
tmp = ctime(&timep);
msg = strdup(tmp);
msg[strcspn(msg, "\n")] = '\0'; /* Remove the newline */
message_printf(server->curr_output, "%s", msg);
free(msg);
}
struct dyn_str {
uint32_t len;
uint32_t cur_pos;
char **str_arr;
};
/*Note that inp is in an invalid state after calling the function * and should
* no longer be used.*/
char *
dyn_str_to_str(struct dyn_str *inp) {
char *outp = calloc(inp->len + 1, sizeof(char));
if(outp == NULL) {
return NULL;
}
outp[inp->len] = '\0';
uint32_t tmp_pos = 0;
for(uint32_t i = 0; i < inp->cur_pos; ++i) {
strcat(outp + tmp_pos, inp->str_arr[i]);
tmp_pos += strlen(inp->str_arr[i]);
free(inp->str_arr[i]);
}
free(inp->str_arr);
return outp;
}
int
print_str(struct dyn_str *outp, const char *fmt, ...) {
va_list args;
va_start(args, fmt);
char *ret = malloc_vsprintf_va_list(fmt, args);
if(ret == NULL) {
return -1;
}
outp->str_arr[outp->cur_pos] = ret;
++outp->cur_pos;
outp->len += strlen(ret);
return 0;
}
char *
print_message_conf(struct nedm_message_config *config) {
struct dyn_str outp_str;
outp_str.len = 0;
outp_str.cur_pos = 0;
uint32_t nmemb = 8;
outp_str.str_arr = calloc(nmemb, sizeof(char *));
print_str(&outp_str, "\"message_config\": {");
print_str(&outp_str, "\"font\": \"%s\",\n", config->font);
print_str(&outp_str, "\"display_time\": %d,\n", config->display_time);
print_str(&outp_str, "\"bg_color\": [%f,%f,%f,%f],\n", config->bg_color[0],
config->bg_color[1], config->bg_color[2], config->bg_color[3]);
print_str(&outp_str, "\"fg_color\": [%f,%f,%f,%f],\n", config->fg_color[0],
config->fg_color[1], config->fg_color[2], config->fg_color[3]);
print_str(&outp_str, "\"enabled\": %d,\n", config->enabled == 1);
switch(config->anchor) {
case NEDM_MESSAGE_TOP_LEFT:
print_str(&outp_str, "\"anchor\": \"top_left\"\n", config->font);
break;
case NEDM_MESSAGE_TOP_CENTER:
print_str(&outp_str, "\"anchor\": \"top_center\"\n", config->font);
break;
case NEDM_MESSAGE_TOP_RIGHT:
print_str(&outp_str, "\"anchor\": \"top_right\"\n", config->font);
break;
case NEDM_MESSAGE_BOTTOM_LEFT:
print_str(&outp_str, "\"anchor\": \"bottom_left\"\n", config->font);
break;
case NEDM_MESSAGE_BOTTOM_CENTER:
print_str(&outp_str, "\"anchor\": \"bottom_center\"\n", config->font);
break;
case NEDM_MESSAGE_BOTTOM_RIGHT:
print_str(&outp_str, "\"anchor\": \"bottom_right\"\n", config->font);
break;
case NEDM_MESSAGE_CENTER:
print_str(&outp_str, "\"anchor\": \"center\"\n", config->font);
break;
case NEDM_MESSAGE_NOPT: // This should actually never occur
print_str(&outp_str, "\"anchor\": \"no_op\"\n", config->font);
break;
}
print_str(&outp_str, "}");
return dyn_str_to_str(&outp_str);
}
void
print_modes(struct dyn_str *str, char **modes) {
uint32_t len = 0;
char **tmp = modes;
uint32_t nmemb = 0;
while(*tmp != NULL) {
len += strlen(*tmp);
++nmemb;
++tmp;
}
if(nmemb == 0) {
wlr_log(WLR_ERROR,
"This is a bug: Cagebreak has no valid modes. This should not "
"occur, since default modes are defined on startup.");
return;
}
/* We are assuming here that we have at least one mode, which is given by
* the initialization of cagebreak */
char *modes_str = calloc(len + 3 * (nmemb - 1) + 1, sizeof(char));
modes_str[len + nmemb - 1] = '\0';
uint32_t tmp_pos = 0;
for(uint32_t i = 0; i < nmemb; ++i) {
if(i != 0) {
strcat(modes_str + tmp_pos, "\",\"");
tmp_pos += 3;
}
strcat(modes_str + tmp_pos, modes[i]);
tmp_pos += strlen(modes[i]);
}
print_str(str, "\"modes\":[\"%s\"],\n", modes_str);
free(modes_str);
}
char *
print_view(struct nedm_view *view) {
struct dyn_str outp_str;
outp_str.len = 0;
outp_str.cur_pos = 0;
uint32_t nmemb = 5;
outp_str.str_arr = calloc(nmemb, sizeof(char *));
print_str(&outp_str, "\"id\": %d,\n", view->id);
print_str(&outp_str, "\"pid\": %d,\n", view->impl->get_pid(view));
if(view->server->bs == true) {
char *title_str = view->impl->get_title(view);
print_str(&outp_str, "\"title\": \"%s\",\n",
title_str == NULL ? "" : title_str);
}
print_str(&outp_str, "\"coords\": {\"x\":%d,\"y\":%d},\n", view->ox,
view->oy);
#if NEDM_HAS_XWAYLAND
print_str(&outp_str, "\"type\": \"%s\"\n",
view->type == NEDM_XWAYLAND_VIEW ? "xwayland" : "xdg");
#else
print_str(&outp_str, "\"type\": \"xdg\"\n");
#endif
return dyn_str_to_str(&outp_str);
}
char *
print_tile(struct nedm_tile *tile) {
struct dyn_str outp_str;
outp_str.len = 0;
outp_str.cur_pos = 0;
uint32_t nmemb = 4;
outp_str.str_arr = calloc(nmemb, sizeof(char *));
print_str(&outp_str, "\"id\": %d,\n", tile->id);
print_str(&outp_str, "\"coords\": {\"x\":%d,\"y\":%d},\n", tile->tile.x,
tile->tile.y);
print_str(&outp_str, "\"size\": {\"width\":%d,\"height\":%d},\n",
tile->tile.width, tile->tile.height);
print_str(&outp_str, "\"view_id\": %d\n",
tile->view == NULL ? -1 : (int)tile->view->id);
return dyn_str_to_str(&outp_str);
}
char *
print_views(struct nedm_workspace *ws) {
struct dyn_str outp_str;
outp_str.len = 0;
outp_str.cur_pos = 0;
uint32_t nviews = wl_list_length(&ws->views);
if(nviews == 0) {
return strdup("{}");
}
outp_str.str_arr = calloc(2 * nviews - 1, sizeof(char *));
struct nedm_view *it;
uint32_t count = 0;
wl_list_for_each(it, &ws->views, link) {
if(count != 0) {
print_str(&outp_str, ",");
}
++count;
char *view_str = print_view(it);
if(view_str != NULL) {
print_str(&outp_str, "{\n%s\n}", view_str);
free(view_str);
}
}
return dyn_str_to_str(&outp_str);
}
char *
print_tiles(struct nedm_workspace *ws) {
struct dyn_str outp_str;
outp_str.len = 0;
outp_str.cur_pos = 0;
uint32_t ntiles = 0;
bool first = true;
for(struct nedm_tile *tile = ws->focused_tile;
first || tile != ws->focused_tile; tile = tile->next) {
first = false;
++ntiles;
}
if(ntiles == 0) {
return strdup("{}");
}
outp_str.str_arr = calloc(2 * ntiles - 1, sizeof(char *));
first = true;
for(struct nedm_tile *tile = ws->focused_tile;
first || tile != ws->focused_tile; tile = tile->next) {
if(first == false) {
print_str(&outp_str, ",");
}
first = false;
char *tile_str = print_tile(tile);
if(tile_str != NULL) {
print_str(&outp_str, "{\n%s\n}", tile_str);
free(tile_str);
}
}
return dyn_str_to_str(&outp_str);
}
char *
print_workspace(struct nedm_workspace *ws) {
struct dyn_str outp_str;
outp_str.len = 0;
outp_str.cur_pos = 0;
uint32_t nmemb = 6;
outp_str.str_arr = calloc(nmemb, sizeof(char *));
print_str(&outp_str, "\"views\": [");
char *views_str = print_views(ws);
if(views_str != NULL) {
print_str(&outp_str, "%s", views_str);
free(views_str);
}
print_str(&outp_str, "],");
print_str(&outp_str, "\"tiles\": [");
char *tiles_str = print_tiles(ws);
if(tiles_str != NULL) {
print_str(&outp_str, "%s", tiles_str);
free(tiles_str);
}
print_str(&outp_str, "]");
return dyn_str_to_str(&outp_str);
}
char *
print_workspaces(struct nedm_output *outp) {
struct dyn_str outp_str;
outp_str.len = 0;
outp_str.cur_pos = 0;
outp_str.str_arr = calloc((2 * outp->server->nws - 1) + 2, sizeof(char *));
print_str(&outp_str, "\"workspaces\": [");
for(int i = 0; i < outp->server->nws; ++i) {
if(i != 0) {
print_str(&outp_str, ",");
}
char *ws = print_workspace(outp->workspaces[i]);
if(ws != NULL) {
print_str(&outp_str, "{%s}", ws);
free(ws);
}
}
print_str(&outp_str, "]\n");
return dyn_str_to_str(&outp_str);
}
char *
print_output(struct nedm_output *outp) {
struct dyn_str outp_str;
outp_str.len = 0;
outp_str.cur_pos = 0;
uint32_t nmemb = 10;
outp_str.str_arr = calloc(nmemb, sizeof(char *));
print_str(&outp_str, "\"%s\": {\n", outp->name);
print_str(&outp_str, "\"priority\": %d,\n", outp->priority);
print_str(&outp_str, "\"coords\": {\"x\":%d,\"y\":%d},\n",
output_get_layout_box(outp).x, output_get_layout_box(outp).y);
print_str(&outp_str, "\"size\": {\"width\":%d,\"height\":%d},\n",
outp->wlr_output->width, outp->wlr_output->height);
print_str(&outp_str, "\"refresh_rate\": %f,\n",
(float)outp->wlr_output->refresh / 1000.0);
print_str(&outp_str, "\"permanent\": %d,\n",
outp->role == OUTPUT_ROLE_PERMANENT);
print_str(&outp_str, "\"active\": %d,\n", !outp->destroyed);
print_str(&outp_str, "\"curr_workspace\": %d,\n", outp->curr_workspace + 1);
char *workspaces_str = print_workspaces(outp);
if(workspaces_str != NULL) {
print_str(&outp_str, "%s", workspaces_str);
free(workspaces_str);
}
print_str(&outp_str, "}");
return dyn_str_to_str(&outp_str);
}
char *
print_outputs(struct nedm_server *server) {
uint32_t noutps = wl_list_length(&server->outputs);
struct dyn_str outp_str;
outp_str.len = 0;
outp_str.cur_pos = 0;
outp_str.str_arr = calloc((2 * noutps - 1) + 2, sizeof(char *));
print_str(&outp_str, "\"outputs\": {");
struct nedm_output *it;
uint32_t count = 0;
wl_list_for_each(it, &server->outputs, link) {
if(count != 0) {
print_str(&outp_str, ",");
}
++count;
char *outp = print_output(it);
if(outp == NULL) {
continue;
}
print_str(&outp_str, "%s", outp);
free(outp);
}
print_str(&outp_str, "}\n");
return dyn_str_to_str(&outp_str);
}
char *
print_keyboard_group(struct nedm_keyboard_group *grp) {
struct dyn_str outp_str;
outp_str.len = 0;
outp_str.cur_pos = 0;
uint32_t nmemb = 5;
outp_str.str_arr = calloc(nmemb, sizeof(char *));
if(grp->identifier != NULL) {
print_str(&outp_str, "\"%s\": {\n", grp->identifier);
} else {
print_str(&outp_str, "\"NULL\": {\n");
}
print_str(&outp_str, "\"commands_enabled\": %d,\n",
grp->enable_keybindings);
print_str(&outp_str, "\"repeat_delay\": %d,\n",
grp->wlr_group->keyboard.repeat_info.delay);
print_str(&outp_str, "\"repeat_rate\": %d\n",
grp->wlr_group->keyboard.repeat_info.rate);
print_str(&outp_str, "}");
return dyn_str_to_str(&outp_str);
}
char *
print_keyboard_groups(struct nedm_server *server) {
uint32_t ninps = wl_list_length(&server->seat->keyboard_groups);
struct dyn_str outp_str;
outp_str.len = 0;
outp_str.cur_pos = 0;
outp_str.str_arr = calloc((2 * ninps - 1) + 3, sizeof(char *));
print_str(&outp_str, "\"keyboards\": {");
struct nedm_keyboard_group *it;
uint32_t count = 0;
wl_list_for_each(it, &server->seat->keyboard_groups, link) {
if(count != 0) {
print_str(&outp_str, ",");
}
++count;
char *outp = print_keyboard_group(it);
if(outp == NULL) {
continue;
}
print_str(&outp_str, "%s", outp);
free(outp);
}
print_str(&outp_str, "}\n");
return dyn_str_to_str(&outp_str);
}
char *
print_input_device(struct nedm_input_device *dev) {
struct dyn_str outp_str;
outp_str.len = 0;
outp_str.cur_pos = 0;
uint32_t nmemb = 4;
outp_str.str_arr = calloc(nmemb, sizeof(char *));
if(dev->identifier != NULL) {
print_str(&outp_str, "\"%s\": {\n", dev->identifier);
} else {
print_str(&outp_str, "\"NULL\": {\n");
}
print_str(&outp_str, "\"is_virtual\": %d,\n", dev->is_virtual);
print_str(&outp_str, "\"type\": \"%s\"\n",
dev->wlr_device->type == WLR_INPUT_DEVICE_POINTER ? "pointer"
: dev->wlr_device->type == WLR_INPUT_DEVICE_SWITCH ? "switch"
: dev->wlr_device->type == WLR_INPUT_DEVICE_TABLET_PAD
? "tablet pad"
: dev->wlr_device->type == WLR_INPUT_DEVICE_TABLET ? "tablet"
: dev->wlr_device->type == WLR_INPUT_DEVICE_TOUCH ? "touch"
: dev->wlr_device->type == WLR_INPUT_DEVICE_KEYBOARD ? "keyboard"
: "unknown");
print_str(&outp_str, "}");
return dyn_str_to_str(&outp_str);
}
char *
print_input_devices(struct nedm_server *server) {
uint32_t ninps = wl_list_length(&server->input->devices);
struct dyn_str outp_str;
outp_str.len = 0;
outp_str.cur_pos = 0;
outp_str.str_arr = calloc((2 * ninps - 1) + 3, sizeof(char *));
print_str(&outp_str, "\"input_devices\": {");
struct nedm_input_device *it;
uint32_t count = 0;
wl_list_for_each(it, &server->input->devices, link) {
if(count != 0) {
print_str(&outp_str, ",");
}
++count;
char *outp = print_input_device(it);
if(outp == NULL) {
continue;
}
print_str(&outp_str, "%s", outp);
free(outp);
}
print_str(&outp_str, "}\n");
return dyn_str_to_str(&outp_str);
}
char *
get_mode_name(char **modes, unsigned int mode_nr) {
unsigned int i;
for(i = 0; i < mode_nr && modes[i] != NULL; ++i) {
}
if(modes[i] != NULL) {
return modes[i];
} else {
return "NULL";
}
}
void
keybinding_dump(struct nedm_server *server) {
struct dyn_str str;
str.len = 0;
str.cur_pos = 0;
uint32_t nmemb = 14;
str.str_arr = calloc(nmemb, sizeof(char *));
print_str(&str, "{\"event_name\":\"dump\",");
print_str(&str, "\"nws\":%d,\n", server->nws);
print_str(&str, "\"bg_color\":[%f,%f,%f],\n", server->bg_color[0],
server->bg_color[1], server->bg_color[2]);
struct nedm_view *focused_view = seat_get_focus(server->seat);
int curr_view_id = -1, curr_tile_id = -1;
if(focused_view != NULL) {
curr_view_id = focused_view->id;
if(focused_view->tile != NULL) {
curr_tile_id = focused_view->tile->id;
}
}
print_str(&str, "\"views_curr_id\":%d,\n", curr_view_id);
print_str(&str, "\"tiles_curr_id\":%d,\n", curr_tile_id);
print_str(&str, "\"curr_output\":\"%s\",\n", server->curr_output->name);
print_str(&str, "\"default_mode\":\"%s\",\n",
get_mode_name(server->modes, server->seat->default_mode));
print_modes(&str, server->modes);
char *message_string = print_message_conf(&server->message_config);
if(message_string != NULL) {
print_str(&str, "%s,", message_string);
free(message_string);
}
char *outps_str = print_outputs(server);
if(outps_str != NULL) {
print_str(&str, "%s,", outps_str);
free(outps_str);
}
char *keyboards_str = print_keyboard_groups(server);
if(keyboards_str != NULL) {
print_str(&str, "%s,", keyboards_str);
free(keyboards_str);
}
char *input_dev_str = print_input_devices(server);
if(input_dev_str != NULL) {
print_str(&str, "%s,", input_dev_str);
free(input_dev_str);
}
print_str(&str, "\"cursor_coords\":{\"x\":%f,\"y\":%f}\n",
server->seat->cursor->x, server->seat->cursor->y);
print_str(&str, "}");
char *send_str = dyn_str_to_str(&str);
if(send_str == NULL) {
wlr_log(WLR_ERROR, "Unable to create output string for \"dump\".");
}
ipc_send_event(server, send_str);
free(send_str);
}
void
keybinding_show_info(struct nedm_server *server) {
char *msg = server_show_info(server);
if(!msg) {
return;
}
message_printf(server->curr_output, "%s", msg);
free(msg);
}
void
keybinding_display_message(struct nedm_server *server, char *msg) {
message_printf(server->curr_output, "%s", msg);
}
void
keybinding_send_custom_event(struct nedm_server *server, char *msg) {
ipc_send_event(server,
"{\"event_name\":\"custom_event\",\"message\":\"%s\"}", msg);
}
void
keybinding_move_view_to_cycle_output(struct nedm_server *server, bool reverse) {
if(wl_list_length(&server->outputs) <= 1) {
return;
}
struct nedm_output *old_outp = server->curr_output;
struct nedm_view *view =
server->curr_output->workspaces[server->curr_output->curr_workspace]
->focused_tile->view;
if(view != NULL) {
wl_list_remove(&view->link);
server->curr_output->workspaces[server->curr_output->curr_workspace]
->focused_tile->view = NULL;
keybinding_cycle_views(server, NULL, 0, false, false);
if(server->curr_output->workspaces[server->curr_output->curr_workspace]
->focused_tile->view == NULL) {
seat_set_focus(server->seat, NULL);
}
}
keybinding_cycle_outputs(server, reverse, false);
if(view != NULL) {
struct nedm_workspace *ws =
server->curr_output
->workspaces[server->curr_output->curr_workspace];
wl_list_insert(&ws->views, &view->link);
wlr_scene_node_reparent(&view->scene_tree->node, ws->scene);
workspace_tile_update_view(ws->focused_tile, view);
view->workspace = ws;
seat_set_focus(server->seat, view);
}
int id = -1;
int pid = -1;
if(view != NULL) {
id = view->id;
pid = view->impl->get_pid(view);
}
ipc_send_event(
server,
"{\"event_name\":\"move_view_to_cycle_output\",\"view_id\":%d,\"view_"
"pid\":%d,\"old_output\":\"%s\",\"old_output_id\":%d,\"new_output\":\"%"
"s\",\"new_output_id\":%d,\"old_tile_id\":%d,\"new_tile_id\":%d}",
id, pid, old_outp->name, output_get_num(old_outp),
server->curr_output->name, output_get_num(server->curr_output),
old_outp->workspaces[old_outp->curr_workspace]->focused_tile->id,
server->curr_output->workspaces[server->curr_output->curr_workspace]
->focused_tile->id);
}
void
keybinding_set_nws(struct nedm_server *server, int nws) {
struct nedm_output *output;
unsigned int old_nws = server->nws;
server->nws = nws;
wl_list_for_each(output, &server->outputs, link) {
for(unsigned int i = nws; i < old_nws; ++i) {
struct nedm_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 nedm_workspace **new_workspaces =
realloc(output->workspaces, nws * sizeof(struct nedm_workspace *));
if(new_workspaces == NULL) {
wlr_log(WLR_ERROR, "Error reallocating memory for workspaces.");
return;
}
output->workspaces = new_workspaces;
for(int i = old_nws; i < nws; ++i) {
output->workspaces[i] = full_screen_workspace(output);
output->workspaces[i]->num = i;
if(!output->workspaces[i]) {
wlr_log(WLR_ERROR, "Failed to allocate additional workspaces");
return;
}
wl_list_init(&output->workspaces[i]->views);
wl_list_init(&output->workspaces[i]->unmanaged_views);
}
if(output->curr_workspace >= nws) {
output->curr_workspace = 0;
workspace_focus(output, nws - 1);
}
}
seat_set_focus(
server->seat,
server->curr_output->workspaces[server->curr_output->curr_workspace]
->focused_tile->view);
ipc_send_event(server,
"{\"event_name\":\"set_nws\",\"old_nws\":%d,\"new_nws\":%d}",
old_nws, server->nws);
}
void
keybinding_set_gap(struct nedm_server *server, int gap_size) {
uint32_t old_gap = server->gap_size;
server->gap_size = gap_size >= 0 ? gap_size : 0;
// Re-position all windows with new gap settings
struct nedm_output *output;
wl_list_for_each(output, &server->outputs, link) {
for(unsigned int i = 0; i < server->nws; ++i) {
struct nedm_tile *tile = output->workspaces[i]->focused_tile;
if(tile) {
struct nedm_tile *curr_tile = tile;
do {
if(curr_tile->view) {
view_maximize(curr_tile->view, curr_tile);
}
curr_tile = curr_tile->next;
} while(curr_tile != tile);
}
}
}
ipc_send_event(server,
"{\"event_name\":\"set_gap\",\"old_gap\":%d,\"new_gap\":%d}",
old_gap, server->gap_size);
}
void
keybinding_definemode(struct nedm_server *server, char *mode) {
int length = 0;
while(server->modes[length++] != NULL)
;
char **tmp = realloc(server->modes, (length + 1) * sizeof(char *));
char **tmp2 = realloc(server->modecursors, (length + 1) * sizeof(char *));
if(tmp == NULL || tmp2 == NULL) {
if(tmp != NULL) {
free(tmp);
}
if(tmp2 != NULL) {
free(tmp2);
}
wlr_log(WLR_ERROR, "Could not allocate memory for storing modes.");
return;
}
server->modes = tmp;
server->modecursors = tmp2;
server->modes[length] = NULL;
server->modecursors[length] = NULL;
server->modes[length - 1] = strdup(mode);
ipc_send_event(server, "{\"event_name\":\"definemode\",\"mode\":\"%s\"}",
mode);
}
void
keybinding_definekey(struct nedm_server *server, struct keybinding *kb) {
keybinding_list_push(server->keybindings, kb);
ipc_send_event(server,
"{\"event_name\":\"definekey\",\"modifiers\":%d,\"key\":"
"%d,\"command\":\"%s\"}",
kb->modifiers, kb->key,
keybinding_action_string[kb->action]);
}
void
keybinding_set_background(struct nedm_server *server, float *bg) {
ipc_send_event(server,
"{\"event_name\":\"background\",\"old_bg\":[%f,%f,%f],"
"\"new_bg\":[%f,%f,%f]}",
server->bg_color[0], server->bg_color[1],
server->bg_color[2], bg[0], bg[1], bg[2]);
server->bg_color[0] = bg[0];
server->bg_color[1] = bg[1];
server->bg_color[2] = bg[2];
struct nedm_output *it = NULL;
wl_list_for_each(it, &server->outputs, link) {
wlr_scene_rect_set_color(it->bg, server->bg_color);
}
wl_list_for_each(it, &server->disabled_outputs, link) {
wlr_scene_rect_set_color(it->bg, server->bg_color);
}
}
void
keybinding_switch_output(struct nedm_server *server, int output) {
struct nedm_output *old_outp = server->curr_output;
struct nedm_output *new_outp = output_from_num(server, output);
if(new_outp != NULL) {
set_output(server, new_outp);
ipc_send_event(server,
"{\"event_name\":\"switch_output\",\"old_output\":"
"\"%s\",\"old_output_id\":%d,\"new_output\":\"%s\","
"\"new_output_id\":%d}",
old_outp->name, output_get_num(old_outp), new_outp->name,
output_get_num(new_outp));
return;
}
message_printf(server->curr_output, "Output %d does not exist", output);
return;
}
void
keybinding_move_view_to_tile(struct nedm_server *server, uint32_t view_id,
uint32_t tile_id, bool follow) {
struct nedm_view *view = view_from_id(server, view_id);
struct nedm_tile *tile = tile_from_id(server, tile_id);
struct nedm_tile *old_tile = view ? view->tile : NULL;
struct nedm_output *old_outp = view ? view->workspace->output : NULL;
int old_workspace = view ? (int)view->workspace->num : -1;
if(tile == NULL) {
return;
}
if(view != NULL) {
if(old_tile != NULL) {
workspace_tile_update_view(old_tile, NULL);
wl_list_remove(&view->link);
keybinding_cycle_views(server, old_tile, 0, false, false);
if(old_tile->view == NULL &&
old_tile == server->curr_output
->workspaces[server->curr_output->curr_workspace]
->focused_tile) {
seat_set_focus(server->seat, NULL);
}
} else {
wl_list_remove(&view->link);
}
}
if(view != NULL) {
struct nedm_workspace *ws = tile->workspace;
view->workspace = ws;
wl_list_insert(&ws->views, &view->link);
wlr_scene_node_reparent(&view->scene_tree->node, ws->scene);
view->tile = tile;
tile->view = view;
if(tile ==
server->curr_output->workspaces[server->curr_output->curr_workspace]
->focused_tile) {
seat_set_focus(server->seat, view);
}
} else {
if(tile->view) {
tile->view->tile = NULL;
tile->view = NULL;
}
}
if(follow) {
keybinding_focus_tile(server, tile->id);
} else {
if(tile->view != NULL) {
workspace_tile_update_view(tile, tile->view);
}
}
ipc_send_event(
server,
"{\"event_name\":\"move_view\",\"view_id\":%d,\"old_output\":\"%s\","
"\"old_workspace\":\"%d\",\"old_tile\":\"%d\",\"new_output\":\"%s\","
"\"new_workspace\":\"%d\",\"new_tile\":\"%d\"}",
view_id, old_outp ? old_outp->name : "", old_workspace,
old_tile ? (int)old_tile->id : -1, server->curr_output->name,
tile->workspace->num, tile->id);
}
void
keybinding_move_view_to_output(struct nedm_server *server, int view_id,
int output_num, bool follow) {
struct nedm_output *outp = output_from_num(server, output_num);
if(outp != NULL) {
keybinding_move_view_to_tile(
server, view_id,
outp->workspaces[outp->curr_workspace]->focused_tile->id, follow);
} else {
message_printf(server->curr_output, "Output number %d not found.",
output_num);
}
}
void
keybinding_move_view_to_workspace(struct nedm_server *server, int view_id,
uint32_t ws, bool follow) {
if(ws >= server->nws) {
message_printf(server->curr_output,
"Attempting to move view to workspace %d, but there are "
"only %d workspaces.",
ws + 1, server->nws);
return;
}
keybinding_move_view_to_tile(
server, view_id, server->curr_output->workspaces[ws]->focused_tile->id,
follow);
}
void
merge_config(struct nedm_output_config *config_new,
struct nedm_output_config *config_old) {
if(config_new->status == OUTPUT_DEFAULT) {
config_new->status = config_old->status;
}
if(config_new->pos.x == -1) {
config_new->pos = config_old->pos;
}
if(config_new->refresh_rate == 0) {
config_new->refresh_rate = config_old->refresh_rate;
}
if(config_new->priority == -1) {
config_new->priority = config_old->priority;
}
}
void
keybinding_configure_output(struct nedm_server *server,
struct nedm_output_config *cfg) {
struct nedm_output_config *config;
config = malloc(sizeof(struct nedm_output_config));
if(config == NULL) {
wlr_log(WLR_ERROR,
"Could not allocate memory for server configuration.");
return;
}
*config = *cfg;
config->output_name = strdup(cfg->output_name);
struct nedm_output_config *it, *tmp;
wl_list_for_each_safe(it, tmp, &server->output_config, link) {
if(strcmp(config->output_name, it->output_name) == 0) {
wl_list_remove(&it->link);
merge_config(config, it);
free(it->output_name);
free(it);
}
}
wl_list_insert(&server->output_config, &config->link);
struct nedm_output *output, *tmp_output;
wl_list_for_each_safe(output, tmp_output, &server->outputs, link) {
if(strcmp(config->output_name, output->name) == 0) {
int output_num = output_get_num(output);
output_configure(server, output);
ipc_send_event(server,
"{\"event_name\":\"configure_output\",\"output\":\"%"
"s\",\"output_id\":%d}",
cfg->output_name, output_num);
return;
}
}
wl_list_for_each_safe(output, tmp_output, &server->disabled_outputs, link) {
if(strcmp(config->output_name, output->name) == 0) {
output_configure(server, output);
ipc_send_event(
output->server,
"{\"event_name\":\"configure_output\",\"output\":\"%s\"}",
cfg->output_name);
return;
}
}
}
void
keybinding_configure_input(struct nedm_server *server,
struct nedm_input_config *cfg) {
struct nedm_input_config *tcfg = input_manager_create_empty_input_config();
if(tcfg == NULL) {
wlr_log(WLR_ERROR,
"Could not allocate temporary empty input configuration.");
return;
}
struct nedm_input_config *ocfg = input_manager_merge_input_configs(cfg, tcfg);
free(tcfg);
if(ocfg == NULL) {
wlr_log(WLR_ERROR,
"Could not allocate input configuration for merging.");
return;
}
wl_list_insert(&server->input_config, &ocfg->link);
nedm_input_manager_configure(server);
ipc_send_event(server,
"{\"event_name\":\"configure_input\",\"input\":\"%s\"}",
cfg->identifier);
}
void
keybinding_configure_message(struct nedm_server *server,
struct nedm_message_config *config) {
if(config->font != NULL) {
free(server->message_config.font);
server->message_config.font = strdup(config->font);
}
if(config->display_time != -1) {
server->message_config.display_time = config->display_time;
}
if(config->bg_color[0] != -1) {
server->message_config.bg_color[0] = config->bg_color[0];
server->message_config.bg_color[1] = config->bg_color[1];
server->message_config.bg_color[2] = config->bg_color[2];
server->message_config.bg_color[3] = config->bg_color[3];
}
if(config->fg_color[0] != -1) {
server->message_config.fg_color[0] = config->fg_color[0];
server->message_config.fg_color[1] = config->fg_color[1];
server->message_config.fg_color[2] = config->fg_color[2];
server->message_config.fg_color[3] = config->fg_color[3];
}
if(config->anchor != NEDM_MESSAGE_NOPT) {
server->message_config.anchor = config->anchor;
}
if(config->enabled != -1) {
server->message_config.enabled = config->enabled;
}
ipc_send_event(server, "{\"event_name\":\"configure_message\"}");
}
void
set_cursor(bool enabled, struct nedm_seat *seat) {
if(enabled == true) {
seat->enable_cursor = true;
wlr_cursor_set_xcursor(seat->cursor, seat->xcursor_manager,
DEFAULT_XCURSOR);
} else {
seat->enable_cursor = false;
wlr_cursor_unset_image(seat->cursor);
}
}
/* Hint: see keybinding.h for details on "data" */
int
run_action(enum keybinding_action action, struct nedm_server *server,
union keybinding_params data) {
switch(action) {
case KEYBINDING_QUIT:
display_terminate(server);
server->running = false;
break;
case KEYBINDING_CHANGE_TTY:
return keybinding_switch_vt(server, data.u);
case KEYBINDING_CURSOR:
set_cursor(data.i, server->seat);
break;
case KEYBINDING_LAYOUT_FULLSCREEN:
keybinding_workspace_fullscreen(server, data.us[0], data.us[1]);
break;
case KEYBINDING_SPLIT_HORIZONTAL:
keybinding_split_horizontal(server, data.f);
break;
case KEYBINDING_SPLIT_VERTICAL:
keybinding_split_vertical(server, data.f);
break;
case KEYBINDING_RUN_COMMAND: {
int pid;
if((pid = fork()) == 0) {
setsid();
sigset_t set;
sigemptyset(&set);
sigprocmask(SIG_SETMASK, &set, NULL);
if(fork() == 0) {
into_process(data.c);
}
_exit(0);
} else if(pid > 0) {
waitpid(pid, NULL, 0);
}
} break;
case KEYBINDING_CYCLE_VIEWS:
keybinding_cycle_views(server, NULL, data.us[1], data.us[0], true);
break;
case KEYBINDING_CYCLE_TILES:
if(data.us[1] == 0) {
keybinding_cycle_tiles(server, data.us[0]);
} else {
keybinding_focus_tile(server, data.us[1]);
}
break;
case KEYBINDING_CYCLE_OUTPUT:
keybinding_cycle_outputs(server, data.b, true);
break;
case KEYBINDING_SWITCH_WORKSPACE:
keybinding_switch_ws(server, data.u);
break;
case KEYBINDING_SWITCH_OUTPUT:
keybinding_switch_output(server, data.u);
break;
case KEYBINDING_SWITCH_MODE:
uint32_t n_modes = 0;
while(server->modes[n_modes] != NULL) {
++n_modes;
}
if(data.u != server->seat->default_mode && data.u < n_modes &&
server->seat->num_pointers > 0) {
wlr_seat_pointer_notify_clear_focus(server->seat->seat);
if(server->seat->enable_cursor == true &&
server->modecursors[data.u] != NULL) {
wlr_cursor_set_xcursor(server->seat->cursor,
server->seat->xcursor_manager,
server->modecursors[data.u]);
}
}
server->seat->mode = data.u;
break;
case KEYBINDING_SWITCH_DEFAULT_MODE:
ipc_send_event(server,
"{\"event_name\":\"switch_default_mode\",\"old_mode\":"
"\"%s\",\"mode\":\"%s\"}",
get_mode_name(server->modes, server->seat->default_mode),
get_mode_name(server->modes, data.u));
uint32_t n_modes2 = 0;
while(server->modes[n_modes2] != NULL) {
++n_modes2;
}
if(data.u != server->seat->default_mode && data.u < n_modes2) {
wlr_seat_pointer_notify_clear_focus(server->seat->seat);
if(server->seat->enable_cursor == true &&
server->seat->num_pointers > 0) {
if(server->modecursors[data.u] != NULL) {
wlr_cursor_set_xcursor(server->seat->cursor,
server->seat->xcursor_manager,
server->modecursors[data.u]);
} else {
wlr_cursor_set_xcursor(server->seat->cursor,
server->seat->xcursor_manager,
DEFAULT_XCURSOR);
}
}
}
server->seat->mode = data.u;
server->seat->default_mode = data.u;
break;
case KEYBINDING_NOOP:
break;
case KEYBINDING_SHOW_TIME:
keybinding_show_time(server);
break;
case KEYBINDING_DUMP:
keybinding_dump(server);
break;
case KEYBINDING_SHOW_INFO:
keybinding_show_info(server);
break;
case KEYBINDING_DISPLAY_MESSAGE:
keybinding_display_message(server, data.c);
break;
case KEYBINDING_SEND_CUSTOM_EVENT:
keybinding_send_custom_event(server, data.c);
break;
case KEYBINDING_RESIZE_TILE_HORIZONTAL:
resize_tile(server, data.is[0], 0, data.is[1]);
break;
case KEYBINDING_RESIZE_TILE_VERTICAL:
resize_tile(server, 0, data.is[0], data.is[1]);
break;
case KEYBINDING_MOVE_TO_TILE: {
struct nedm_view *view =
server->curr_output->workspaces[server->curr_output->curr_workspace]
->focused_tile->view;
keybinding_move_view_to_tile(server, view ? (int)view->id : -1,
data.us[0], data.us[1] > 0);
break;
}
case KEYBINDING_MOVE_TO_WORKSPACE: {
struct nedm_view *view =
server->curr_output->workspaces[server->curr_output->curr_workspace]
->focused_tile->view;
keybinding_move_view_to_workspace(server, view ? (int)view->id : -1,
data.us[0], data.us[1] > 0);
break;
}
case KEYBINDING_MOVE_TO_OUTPUT: {
struct nedm_view *view =
server->curr_output->workspaces[server->curr_output->curr_workspace]
->focused_tile->view;
keybinding_move_view_to_output(server, view ? (int)view->id : -1,
data.us[0], data.us[1] > 0);
break;
}
case KEYBINDING_MOVE_VIEW_TO_TILE: {
keybinding_move_view_to_tile(server, data.us[0], data.us[1],
data.us[2] > 0);
break;
}
case KEYBINDING_MOVE_VIEW_TO_WORKSPACE: {
keybinding_move_view_to_workspace(server, data.us[0], data.us[1],
data.us[2] > 0);
break;
}
case KEYBINDING_MOVE_VIEW_TO_OUTPUT: {
keybinding_move_view_to_output(server, data.us[0], data.us[1],
data.us[2] > 0);
break;
}
case KEYBINDING_MERGE_LEFT: {
struct nedm_tile *tile =
server->curr_output->workspaces[server->curr_output->curr_workspace]
->focused_tile;
if(data.u != 0) {
tile = tile_from_id(server, data.u);
}
if(tile != NULL) {
merge_tile_left(tile);
}
break;
}
case KEYBINDING_MERGE_RIGHT: {
struct nedm_tile *tile =
server->curr_output->workspaces[server->curr_output->curr_workspace]
->focused_tile;
if(data.u != 0) {
tile = tile_from_id(server, data.u);
}
if(tile != NULL) {
merge_tile_right(tile);
}
break;
}
case KEYBINDING_MERGE_TOP: {
struct nedm_tile *tile =
server->curr_output->workspaces[server->curr_output->curr_workspace]
->focused_tile;
if(data.u != 0) {
tile = tile_from_id(server, data.u);
}
if(tile != NULL) {
merge_tile_top(tile);
}
break;
}
case KEYBINDING_MERGE_BOTTOM: {
struct nedm_tile *tile =
server->curr_output->workspaces[server->curr_output->curr_workspace]
->focused_tile;
if(data.u != 0) {
tile = tile_from_id(server, data.u);
}
if(tile != NULL) {
merge_tile_bottom(tile);
}
break;
}
case KEYBINDING_SWAP_LEFT: {
swap_tile_left(server, data.us[0], data.us[1] == 1);
break;
}
case KEYBINDING_SWAP_RIGHT: {
swap_tile_right(server, data.us[0], data.us[1] == 1);
break;
}
case KEYBINDING_SWAP_TOP: {
swap_tile_top(server, data.us[0], data.us[1] == 1);
break;
}
case KEYBINDING_SWAP_BOTTOM: {
swap_tile_bottom(server, data.us[0], data.us[1] == 1);
break;
}
case KEYBINDING_SWAP: {
swap_tiles(tile_from_id(server, data.us[0]),
tile_from_id(server, data.us[1]), data.us[2] == 1);
break;
}
case KEYBINDING_FOCUS_LEFT: {
focus_tile_left(
server->curr_output->workspaces[server->curr_output->curr_workspace]
->focused_tile);
break;
}
case KEYBINDING_FOCUS_RIGHT: {
focus_tile_right(
server->curr_output->workspaces[server->curr_output->curr_workspace]
->focused_tile);
break;
}
case KEYBINDING_FOCUS_TOP: {
focus_tile_top(
server->curr_output->workspaces[server->curr_output->curr_workspace]
->focused_tile);
break;
}
case KEYBINDING_FOCUS_BOTTOM: {
focus_tile_bottom(
server->curr_output->workspaces[server->curr_output->curr_workspace]
->focused_tile);
break;
}
case KEYBINDING_MOVE_VIEW_TO_CYCLE_OUTPUT: {
keybinding_move_view_to_cycle_output(server, data.b);
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;
case KEYBINDING_GAP:
keybinding_set_gap(server, data.i);
break;
case KEYBINDING_CONFIGURE_OUTPUT:
keybinding_configure_output(server, data.o_cfg);
break;
case KEYBINDING_CONFIGURE_MESSAGE:
keybinding_configure_message(server, data.m_cfg);
break;
case KEYBINDING_CONFIGURE_INPUT:
keybinding_configure_input(server, data.i_cfg);
break;
case KEYBINDING_CLOSE_VIEW:
keybinding_close_view(
server->curr_output->workspaces[server->curr_output->curr_workspace]
->focused_tile->view);
break;
case KEYBINDING_SETMODECURSOR:
for(int i = 0; server->modes[i] != NULL; ++i) {
if(strcmp(server->modes[i], data.cs[0]) == 0) {
if(server->modecursors[i] != NULL) {
free(server->modecursors[i]);
}
server->modecursors[i] = strdup(data.cs[1]);
}
}
break;
default: {
wlr_log(WLR_ERROR,
"run_action was called with a value not present in \"enum "
"keybinding_action\". This should not happen.");
return -1;
}
}
return 0;
}