From f97aa49c8467820896bbca0abebd92aaef652376 Mon Sep 17 00:00:00 2001 From: Vincent Vanlaer Date: Sat, 26 May 2018 09:56:16 +0200 Subject: [PATCH] Add relative layouts to rootston In order to properly manage relative layouts in rootston, the 'wlr_output_layout' has been encapsulated in 'roots_layout'. This is primarily to deal with outputs not being available to reference. This lives in rootston since compositors might want to deal differently with missing outputs. The behavior of rootston in the case of missign outputs, is to mark rules whose relative output is not available as unconfigured. When an output is unconfigured it will be placed to the right of any other outputs. This way, no gaps will occur in the layout, unless the user specifically configures them with fixed output. This commit adds the possibility to switch layouts at runtime trough the commnd 'next_layout'. --- include/rootston/config.h | 16 ++- include/rootston/desktop.h | 3 +- include/rootston/layout.h | 31 ++++ rootston/config.c | 100 ++++++++++++- rootston/desktop.c | 43 +++--- rootston/keyboard.c | 17 +++ rootston/layer_shell.c | 4 +- rootston/layout.c | 257 ++++++++++++++++++++++++++++++++++ rootston/meson.build | 1 + rootston/output.c | 34 +++-- rootston/rootston.ini.example | 16 ++- rootston/seat.c | 4 +- rootston/xdg_shell.c | 2 +- rootston/xdg_shell_v6.c | 2 +- 14 files changed, 482 insertions(+), 48 deletions(-) create mode 100644 include/rootston/layout.h create mode 100644 rootston/layout.c diff --git a/include/rootston/config.h b/include/rootston/config.h index 97a8baab2e..dd0e738448 100644 --- a/include/rootston/config.h +++ b/include/rootston/config.h @@ -10,7 +10,6 @@ struct roots_output_config { char *name; bool enable; enum wl_output_transform transform; - int x, y; float scale; struct wl_list link; struct { @@ -19,6 +18,20 @@ struct roots_output_config { } mode; }; +struct roots_layout_config { + char *name; + struct wl_list link; + struct wl_list rules; +}; + +struct roots_layout_rule_config { + char *output_name; + struct wl_list link; + enum wlr_output_layout_output_configuration configuration; + char *reference_output; + int x, y; +}; + struct roots_device_config { char *name; char *seat; @@ -67,6 +80,7 @@ struct roots_config { struct wl_list bindings; struct wl_list keyboards; struct wl_list cursors; + struct wl_list layouts; char *config_path; char *startup_cmd; diff --git a/include/rootston/desktop.h b/include/rootston/desktop.h index 83ff2ea811..f3826ec696 100644 --- a/include/rootston/desktop.h +++ b/include/rootston/desktop.h @@ -25,6 +25,7 @@ #include "rootston/config.h" #include "rootston/output.h" #include "rootston/view.h" +#include "rootston/layout.h" struct roots_desktop { struct wl_list views; // roots_view::link @@ -34,8 +35,8 @@ struct roots_desktop { struct roots_server *server; struct roots_config *config; + struct roots_layout *layout; - struct wlr_output_layout *layout; struct wlr_xcursor_manager *xcursor_manager; struct wlr_compositor *compositor; diff --git a/include/rootston/layout.h b/include/rootston/layout.h new file mode 100644 index 0000000000..680cdf9b0c --- /dev/null +++ b/include/rootston/layout.h @@ -0,0 +1,31 @@ +#ifndef ROOTSTON_LAYOUT_H +#define ROOTSTON_LAYOUT_H + +#include +#include "rootston/config.h" +#include "rootston/output.h" + +struct roots_layout_rule { + struct roots_layout_rule_config *config; + struct roots_output *output; + struct wl_list link; + bool configured; +}; + +struct roots_layout { + struct wlr_output_layout *wlr_layout; + struct roots_layout_config *current_config; + struct wl_list rules; +}; + +struct roots_layout* roots_layout_create(struct roots_layout_config *config); +void roots_layout_destroy(struct roots_layout *layout); +void roots_layout_add_output(struct roots_layout *layout, + struct roots_output *output); +void roots_layout_remove_output(struct roots_layout *layout, + struct roots_output *output); +void roots_layout_reflow(struct roots_layout *layout); +void roots_layout_reconfigure(struct roots_layout *layout, + struct roots_layout_config *config); + +#endif diff --git a/rootston/config.c b/rootston/config.c index 9a4d77fd89..5ae299b26d 100644 --- a/rootston/config.c +++ b/rootston/config.c @@ -228,10 +228,79 @@ static void config_handle_keyboard(struct roots_config *config, } } +static void config_handle_layout(struct roots_config *config, + const char *layout_name, const char *name, const char *value) { + struct roots_layout_config *lc; + bool found = false; + wl_list_for_each(lc, &config->layouts, link) { + if (strcmp(lc->name, layout_name) == 0) { + found = true; + break; + } + } + + if (!found) { + lc = calloc(1, sizeof(struct roots_layout_config)); + lc->name = strdup(layout_name); + wl_list_insert(&config->layouts, &lc->link); + wl_list_init(&lc->rules); + } + + char *mod_value = strdup(value); + char *strstart = mod_value; + char *saveptr; + mod_value = strtok_r(mod_value, " ", &saveptr); + + struct roots_layout_rule_config *rule = + calloc(1, sizeof(struct roots_layout_rule_config)); + wl_list_insert(&lc->rules, &rule->link); + rule->output_name = strdup(name); + + if (strcmp(mod_value, "fixed") == 0) { + mod_value = strtok_r(NULL, " ", &saveptr); + rule->x = strtol(mod_value, NULL, 10); + mod_value = strtok_r(NULL, " ", &saveptr); + rule->y = strtol(mod_value, NULL, 10); + rule->configuration = + WLR_OUTPUT_LAYOUT_OUTPUT_CONFIGURATION_FIXED; + } else if (strcmp(mod_value, "left-of") == 0) { + mod_value = strtok_r(NULL, " ", &saveptr); + rule->reference_output = strdup(mod_value); + rule->configuration = + WLR_OUTPUT_LAYOUT_OUTPUT_CONFIGURATION_RELATIVE_LEFT_OF; + } else if (strcmp(mod_value, "right-of") == 0) { + mod_value = strtok_r(NULL, " ", &saveptr); + rule->reference_output = strdup(mod_value); + rule->configuration = + WLR_OUTPUT_LAYOUT_OUTPUT_CONFIGURATION_RELATIVE_RIGHT_OF; + } else if (strcmp(mod_value, "below") == 0) { + mod_value = strtok_r(NULL, " ", &saveptr); + rule->reference_output = strdup(mod_value); + rule->configuration = + WLR_OUTPUT_LAYOUT_OUTPUT_CONFIGURATION_RELATIVE_BELOW; + } else if (strcmp(mod_value, "above") == 0) { + mod_value = strtok_r(NULL, " ", &saveptr); + rule->reference_output = strdup(mod_value); + rule->configuration = + WLR_OUTPUT_LAYOUT_OUTPUT_CONFIGURATION_RELATIVE_ABOVE; + } else if (strcmp(mod_value, "same-as") == 0) { + mod_value = strtok_r(NULL, " ", &saveptr); + rule->reference_output = strdup(mod_value); + rule->configuration = + WLR_OUTPUT_LAYOUT_OUTPUT_CONFIGURATION_RELATIVE_SAME_AS; + } else { + wlr_log(L_ERROR, "got invalid input for layout %s: %s = %s", + layout_name, name, value); + } + + free(strstart); +} + static const char *output_prefix = "output:"; static const char *device_prefix = "device:"; static const char *keyboard_prefix = "keyboard:"; static const char *cursor_prefix = "cursor:"; +static const char *layout_prefix = "layout:"; static int config_ini_handler(void *user, const char *section, const char *name, const char *value) { @@ -280,10 +349,6 @@ static int config_ini_handler(void *user, const char *section, const char *name, } else { wlr_log(L_ERROR, "got invalid output enable value: %s", value); } - } else if (strcmp(name, "x") == 0) { - oc->x = strtol(value, NULL, 10); - } else if (strcmp(name, "y") == 0) { - oc->y = strtol(value, NULL, 10); } else if (strcmp(name, "scale") == 0) { oc->scale = strtof(value, NULL); assert(oc->scale > 0); @@ -376,6 +441,12 @@ static int config_ini_handler(void *user, const char *section, const char *name, section, strlen(keyboard_prefix)) == 0) { const char *device_name = section + strlen(keyboard_prefix); config_handle_keyboard(config, device_name, name, value); + } else if (strcmp(section, "layout") == 0) { + config_handle_layout(config, "", name, value); + } else if (strncmp(layout_prefix, + section, strlen(layout_prefix)) == 0) { + const char *layout_name = section + strlen(layout_prefix); + config_handle_layout(config, layout_name, name, value); } else if (strcmp(section, "bindings") == 0) { add_binding_config(&config->bindings, name, value); } else { @@ -398,6 +469,7 @@ struct roots_config *roots_config_create_from_args(int argc, char *argv[]) { wl_list_init(&config->keyboards); wl_list_init(&config->cursors); wl_list_init(&config->bindings); + wl_list_init(&config->layouts); int c; while ((c = getopt(argc, argv, "C:E:hD")) != -1) { @@ -453,6 +525,14 @@ struct roots_config *roots_config_create_from_args(int argc, char *argv[]) { exit(1); } + if (wl_list_empty(&config->layouts)) { + struct roots_layout_config *layout = + calloc(1, sizeof(struct roots_layout_config)); + layout->name = strdup("default"); + wl_list_init(&layout->rules); + wl_list_insert(&config->layouts, &layout->link); + } + return config; } @@ -500,6 +580,18 @@ void roots_config_destroy(struct roots_config *config) { free(bc); } + struct roots_layout_config *lc, *ltmp = NULL; + struct roots_layout_rule_config *lrc, *lrtmp = NULL; + wl_list_for_each_safe(lc, ltmp, &config->layouts, link) { + wl_list_for_each_safe(lrc, lrtmp, &lc->rules, link) { + free(lrc->output_name); + free(lrc->reference_output); + free(lrc); + } + free(lc->name); + free(kc); + } + free(config->config_path); free(config); } diff --git a/rootston/desktop.c b/rootston/desktop.c index 178a975a1c..26ef89ba9c 100644 --- a/rootston/desktop.c +++ b/rootston/desktop.c @@ -113,9 +113,9 @@ static void view_update_output(const struct roots_view *view, struct roots_output *output; wl_list_for_each(output, &desktop->outputs, link) { bool intersected = before != NULL && wlr_output_layout_intersects( - desktop->layout, output->wlr_output, before); - bool intersects = wlr_output_layout_intersects(desktop->layout, - output->wlr_output, &box); + desktop->layout->wlr_layout, output->wlr_output, before); + bool intersects = wlr_output_layout_intersects( + desktop->layout->wlr_layout, output->wlr_output, &box); if (intersected && !intersects) { wlr_surface_send_leave(view->wlr_surface, output->wlr_output); } @@ -184,12 +184,12 @@ static struct wlr_output *view_get_output(struct roots_view *view) { view_get_box(view, &view_box); double output_x, output_y; - wlr_output_layout_closest_point(view->desktop->layout, NULL, + wlr_output_layout_closest_point(view->desktop->layout->wlr_layout, NULL, view->x + (double)view_box.width/2, view->y + (double)view_box.height/2, &output_x, &output_y); - return wlr_output_layout_output_at(view->desktop->layout, output_x, - output_y); + return wlr_output_layout_output_at(view->desktop->layout->wlr_layout, + output_x, output_y); } void view_arrange_maximized(struct roots_view *view) { @@ -199,7 +199,7 @@ void view_arrange_maximized(struct roots_view *view) { struct wlr_output *output = view_get_output(view); struct roots_output *roots_output = output->data; struct wlr_box *output_box = - wlr_output_layout_get_box(view->desktop->layout, output); + wlr_output_layout_get_box(view->desktop->layout->wlr_layout, output); struct wlr_box usable_area; memcpy(&usable_area, &roots_output->usable_area, sizeof(struct wlr_box)); @@ -274,7 +274,7 @@ void view_set_fullscreen(struct roots_view *view, bool fullscreen, view->saved.height = view_box.height; struct wlr_box *output_box = - wlr_output_layout_get_box(view->desktop->layout, output); + wlr_output_layout_get_box(view->desktop->layout->wlr_layout, output); view_move_resize(view, output_box->x, output_box->y, output_box->width, output_box->height); view_rotate(view, 0); @@ -332,7 +332,7 @@ bool view_center(struct roots_view *view) { } struct wlr_output *output = - wlr_output_layout_output_at(desktop->layout, + wlr_output_layout_output_at(desktop->layout->wlr_layout, seat->cursor->cursor->x, seat->cursor->cursor->y); if (!output) { @@ -341,7 +341,7 @@ bool view_center(struct roots_view *view) { } const struct wlr_output_layout_output *l_output = - wlr_output_layout_get(desktop->layout, output); + wlr_output_layout_get(desktop->layout->wlr_layout, output); int width, height; wlr_output_effective_resolution(output, &width, &height); @@ -615,7 +615,7 @@ static struct roots_view *desktop_view_at(struct roots_desktop *desktop, double lx, double ly, struct wlr_surface **surface, double *sx, double *sy) { struct wlr_output *wlr_output = - wlr_output_layout_output_at(desktop->layout, lx, ly); + wlr_output_layout_output_at(desktop->layout->wlr_layout, lx, ly); if (wlr_output != NULL) { struct roots_output *output = desktop_output_from_wlr_output(desktop, wlr_output); @@ -660,7 +660,7 @@ struct wlr_surface *desktop_surface_at(struct roots_desktop *desktop, struct roots_view **view) { struct wlr_surface *surface = NULL; struct wlr_output *wlr_output = - wlr_output_layout_output_at(desktop->layout, lx, ly); + wlr_output_layout_output_at(desktop->layout->wlr_layout, lx, ly); struct roots_output *roots_output = NULL; double ox = lx, oy = ly; if (view) { @@ -669,7 +669,7 @@ struct wlr_surface *desktop_surface_at(struct roots_desktop *desktop, if (wlr_output) { roots_output = wlr_output->data; - wlr_output_layout_output_coords(desktop->layout, wlr_output, &ox, &oy); + wlr_output_layout_output_coords(desktop->layout->wlr_layout, wlr_output, &ox, &oy); if ((surface = layer_surface_at(roots_output, &roots_output->layers[ZWLR_LAYER_SHELL_V1_LAYER_OVERLAY], @@ -711,13 +711,13 @@ static void handle_layout_change(struct wl_listener *listener, void *data) { wl_container_of(listener, desktop, layout_change); struct wlr_output *center_output = - wlr_output_layout_get_center_output(desktop->layout); + wlr_output_layout_get_center_output(desktop->layout->wlr_layout); if (center_output == NULL) { return; } struct wlr_box *center_output_box = - wlr_output_layout_get_box(desktop->layout, center_output); + wlr_output_layout_get_box(desktop->layout->wlr_layout, center_output); double center_x = center_output_box->x + center_output_box->width/2; double center_y = center_output_box->y + center_output_box->height/2; @@ -726,7 +726,8 @@ static void handle_layout_change(struct wl_listener *listener, void *data) { struct wlr_box box; view_get_box(view, &box); - if (wlr_output_layout_intersects(desktop->layout, NULL, &box)) { + if (wlr_output_layout_intersects(desktop->layout->wlr_layout, NULL, + &box)) { continue; } @@ -771,10 +772,14 @@ struct roots_desktop *desktop_create(struct roots_server *server, desktop->server = server; desktop->config = config; - desktop->layout = wlr_output_layout_create(); - wlr_xdg_output_manager_create(server->wl_display, desktop->layout); + struct roots_layout_config *default_layout; + + wl_list_for_each(default_layout, &config->layouts, link) { break; } + + desktop->layout = roots_layout_create(default_layout); + wlr_xdg_output_manager_create(server->wl_display, desktop->layout->wlr_layout); desktop->layout_change.notify = handle_layout_change; - wl_signal_add(&desktop->layout->events.change, &desktop->layout_change); + wl_signal_add(&desktop->layout->wlr_layout->events.change, &desktop->layout_change); desktop->compositor = wlr_compositor_create(server->wl_display, server->renderer); diff --git a/rootston/keyboard.c b/rootston/keyboard.c index b5dac51c8a..11eeeb06fb 100644 --- a/rootston/keyboard.c +++ b/rootston/keyboard.c @@ -13,6 +13,7 @@ #include "rootston/input.h" #include "rootston/keyboard.h" #include "rootston/seat.h" +#include "rootston/layout.h" static ssize_t pressed_keysyms_index(xkb_keysym_t *pressed_keysyms, xkb_keysym_t keysym) { @@ -133,6 +134,22 @@ static void keyboard_binding_execute(struct roots_keyboard *keyboard, wl_list_for_each(output, &keyboard->input->server->desktop->outputs, link) { wlr_output_enable(output->wlr_output, outputs_enabled); } + } else if (strcmp(command, "next_layout") == 0) { + struct roots_desktop *desktop = keyboard->input->server->desktop; + struct wl_list *next = desktop->layout->current_config->link.next; + + if (next == &desktop->config->layouts) { + next = next->next; + } + + struct roots_layout_config *config = + wl_container_of(next, desktop->layout->current_config, link); + + wlr_log(L_DEBUG, "Switching to layout %s", config->name); + + roots_layout_reconfigure(desktop->layout, config); + roots_layout_reflow(desktop->layout); + } else { wlr_log(L_ERROR, "unknown binding command: %s", command); } diff --git a/rootston/layer_shell.c b/rootston/layer_shell.c index db0aeb596b..3ba02d18a9 100644 --- a/rootston/layer_shell.c +++ b/rootston/layer_shell.c @@ -392,14 +392,14 @@ void handle_layer_shell_surface(struct wl_listener *listener, void *data) { struct roots_seat *seat = input_last_active_seat(input); assert(seat); // Technically speaking we should handle this case struct wlr_output *output = - wlr_output_layout_output_at(desktop->layout, + wlr_output_layout_output_at(desktop->layout->wlr_layout, seat->cursor->cursor->x, seat->cursor->cursor->y); if (!output) { wlr_log(L_ERROR, "Couldn't find output at (%.0f,%.0f)", seat->cursor->cursor->x, seat->cursor->cursor->y); - output = wlr_output_layout_get_center_output(desktop->layout); + output = wlr_output_layout_get_center_output(desktop->layout->wlr_layout); } if (output) { layer_surface->output = output; diff --git a/rootston/layout.c b/rootston/layout.c new file mode 100644 index 0000000000..639b29d526 --- /dev/null +++ b/rootston/layout.c @@ -0,0 +1,257 @@ +#include +#include +#include +#include "rootston/layout.h" + +static struct roots_layout_rule *get_rule(struct roots_layout *layout, char *name) { + struct roots_layout_rule *rule; + wl_list_for_each(rule, &layout->rules, link) { + if (strcmp(name, rule->output->wlr_output->name) == 0) { + return rule; + } + } + + return NULL; +} + +struct roots_layout* roots_layout_create(struct roots_layout_config *config) { + struct roots_layout *layout = calloc(1, sizeof(struct roots_layout)); + + if (!layout) { + return NULL; + } + + wl_list_init(&layout->rules); + layout->wlr_layout = wlr_output_layout_create(); + layout->current_config = config; + + return layout; +} + +void roots_layout_destroy(struct roots_layout *layout) { + if (layout) { + struct roots_layout_rule *rule, *tmp; + wl_list_for_each_safe(rule, tmp, &layout->rules, link) { + wl_list_remove(&rule->link); + free(rule); + } + + free(layout); + } +} + +static void apply_rule(struct roots_layout *layout, + struct roots_layout_rule *rule) { + + rule->configured = false; + + if (rule->config) { + if (rule->config->configuration != + WLR_OUTPUT_LAYOUT_OUTPUT_CONFIGURATION_FIXED) { + struct roots_layout_rule *ref = + get_rule(layout, rule->config->reference_output); + if (ref) { + wlr_output_layout_add_relative(layout->wlr_layout, + rule->output->wlr_output, ref->output->wlr_output, + rule->config->configuration); + rule->configured = true; + } else { + wlr_output_layout_add(layout->wlr_layout, + rule->output->wlr_output, 0, 0); + } + } else { + wlr_output_layout_add(layout->wlr_layout, rule->output->wlr_output, + rule->config->x, rule->config->y); + rule->configured = true; + } + } else { + wlr_output_layout_add(layout->wlr_layout, + rule->output->wlr_output, 0, 0); + } + + struct roots_layout_rule *ref_rule; + wl_list_for_each(ref_rule, &layout->rules, link) { + if (!ref_rule->configured && ref_rule->config) { + if (strcmp(rule->output->wlr_output->name, + ref_rule->config->reference_output) == 0) { + wlr_output_layout_add_relative(layout->wlr_layout, + ref_rule->output->wlr_output, rule->output->wlr_output, + ref_rule->config->configuration); + ref_rule->configured = true; + } + } + } +} + +void roots_layout_add_output(struct roots_layout *layout, + struct roots_output *output) { + + struct roots_layout_rule *new_rule; + + wl_list_for_each(new_rule, &layout->rules, link) { + if (new_rule->output == output) { + return; + } + } + + new_rule = calloc(1, sizeof(struct roots_layout_rule)); + + if (!new_rule) { + wlr_log(L_ERROR, "Could not allocate layout rule"); + return; + } + + new_rule->output = output; + + struct roots_layout_rule_config *c_rule; + wl_list_for_each(c_rule, &layout->current_config->rules, link) { + if (strcmp(output->wlr_output->name, c_rule->output_name) == 0) { + new_rule->config = c_rule; + break; + } + } + + wl_list_insert(&layout->rules, &new_rule->link); + apply_rule(layout, new_rule); +} + +void roots_layout_remove_output(struct roots_layout *layout, + struct roots_output *output) { + struct roots_layout_rule *rule, *tmp; + + wl_list_for_each_safe(rule, tmp, &layout->rules, link) { + if (rule->output == output) { + wl_list_remove(&rule->link); + free(rule); + } else if (rule->config && rule->config->reference_output && + strcmp(output->wlr_output->name, + rule->config->reference_output) == 0) { + rule->configured = false; + } + } +} + +void roots_layout_reflow(struct roots_layout *layout) { + int max_x = INT_MIN; + int max_x_y = 0; + bool configured; + + struct wlr_output_layout_output *l_output; + wl_list_for_each(l_output, &layout->wlr_layout->outputs, link) { + if (l_output->configuration == + WLR_OUTPUT_LAYOUT_OUTPUT_CONFIGURATION_FIXED) { + struct roots_layout_rule *rule; + wl_list_for_each(rule, &layout->rules, link) { + if (rule->output->wlr_output == l_output->output) { + configured = rule->configured; + break; + } + } + } + + if (configured) { + struct wlr_box *box = + wlr_output_layout_get_box(layout->wlr_layout, l_output->output); + if (max_x < box->x + box->width) { + max_x = box->x + box->width; + max_x_y = box->y; + } + } + } + + if (max_x == INT_MIN) { // In case there are no configured layouts + max_x = 0; + } + + struct wlr_output *unconfigured_output = NULL; + int auto_min_x = INT_MAX; + int auto_min_x_y = 0; + int auto_max_x = INT_MIN; + int auto_max_x_y = 0; + + wl_list_for_each(l_output, &layout->wlr_layout->outputs, link) { + if (l_output->configuration == + WLR_OUTPUT_LAYOUT_OUTPUT_CONFIGURATION_FIXED) { + if (unconfigured_output) { + struct wlr_box *box = + wlr_output_layout_get_box(layout->wlr_layout, + unconfigured_output); + wlr_output_layout_add(layout->wlr_layout, unconfigured_output, + box->x - auto_min_x + max_x, + box->y - auto_min_x_y + max_x_y); + max_x += auto_max_x - auto_min_x; + max_x_y += auto_max_x_y - auto_min_x_y; + auto_min_x = INT_MAX; + auto_min_x_y = 0; + auto_max_x = INT_MIN; + auto_max_x_y = 0; + unconfigured_output = NULL; + } + struct roots_layout_rule *rule; + wl_list_for_each(rule, &layout->rules, link) { + if (rule->output->wlr_output == l_output->output) { + configured = rule->configured; + if (!configured) { + unconfigured_output = l_output->output; + } + break; + } + } + } + + if (!configured) { + struct wlr_box *box = + wlr_output_layout_get_box(layout->wlr_layout, l_output->output); + if (auto_max_x < box->x + box->width) { + auto_max_x = box->x + box->width; + auto_max_x_y = box->y; + } + + if (auto_min_x > box->x) { + auto_min_x = box->x; + auto_min_x_y = box->y; + } + } + } + + if (unconfigured_output) { + struct wlr_box *box = + wlr_output_layout_get_box(layout->wlr_layout, + unconfigured_output); + wlr_output_layout_add(layout->wlr_layout, unconfigured_output, + box->x - auto_min_x + max_x, + box->y - auto_min_x_y + max_x_y); + max_x += auto_max_x - auto_min_x; + max_x_y += auto_max_x_y - auto_min_x_y; + } + + struct roots_layout_rule *rule; + wl_list_for_each(rule, &layout->rules, link) { + output_damage_whole(rule->output); + } +} + +void roots_layout_reconfigure(struct roots_layout *layout, + struct roots_layout_config *config) { + layout->current_config = config; + + struct roots_layout_rule *rule; + wl_list_for_each(rule, &layout->rules, link) { + struct roots_layout_rule_config *c_rule; + bool found = false; + + wl_list_for_each(c_rule, &layout->current_config->rules, link) { + if (strcmp(rule->output->wlr_output->name, c_rule->output_name) == 0) { + rule->config = c_rule; + found = true; + break; + } + } + + if (!found) { + rule->config = NULL; + } + + apply_rule(layout, rule); + } +} diff --git a/rootston/meson.build b/rootston/meson.build index 53a4635de2..ce02578bf2 100644 --- a/rootston/meson.build +++ b/rootston/meson.build @@ -12,6 +12,7 @@ sources = [ 'wl_shell.c', 'xdg_shell_v6.c', 'xdg_shell.c', + 'layout.c' ] if get_option('enable-xwayland') sources += ['xwayland.c'] diff --git a/rootston/output.c b/rootston/output.c index faa808d176..9cb9c2bb7c 100644 --- a/rootston/output.c +++ b/rootston/output.c @@ -201,8 +201,9 @@ static void render_surface(struct wlr_surface *surface, int sx, int sy, get_layout_position(&data->layout, &lx, &ly, surface, sx, sy); struct wlr_box box; - bool intersects = surface_intersect_output(surface, output->desktop->layout, - output->wlr_output, lx, ly, rotation, &box); + bool intersects = surface_intersect_output(surface, + output->desktop->layout->wlr_layout, output->wlr_output, + lx, ly, rotation, &box); if (!intersects) { return; } @@ -252,7 +253,8 @@ static void get_decoration_box(struct roots_view *view, double x = sx + view->x; double y = sy + view->y; - wlr_output_layout_output_coords(output->desktop->layout, wlr_output, &x, &y); + wlr_output_layout_output_coords(output->desktop->layout->wlr_layout, + wlr_output, &x, &y); box->x = x * wlr_output->scale; box->y = y * wlr_output->scale; @@ -346,7 +348,7 @@ static void surface_send_frame_done(struct wlr_surface *surface, int sx, int sy, double lx, ly; get_layout_position(&data->layout, &lx, &ly, surface, sx, sy); - if (!surface_intersect_output(surface, output->desktop->layout, + if (!surface_intersect_output(surface, output->desktop->layout->wlr_layout, output->wlr_output, lx, ly, rotation, NULL)) { return; } @@ -403,7 +405,7 @@ static void render_output(struct roots_output *output) { float clear_color[] = {0.25f, 0.25f, 0.25f, 1.0f}; const struct wlr_box *output_box = - wlr_output_layout_get_box(desktop->layout, wlr_output); + wlr_output_layout_get_box(desktop->layout->wlr_layout, wlr_output); // Check if we can delegate the fullscreen surface to the output if (output->fullscreen_view != NULL && @@ -608,8 +610,9 @@ static void damage_whole_surface(struct wlr_surface *surface, int sx, int sy, wlr_output_transformed_resolution(output->wlr_output, &ow, &oh); struct wlr_box box; - bool intersects = surface_intersect_output(surface, output->desktop->layout, - output->wlr_output, lx, ly, rotation, &box); + bool intersects = surface_intersect_output(surface, + output->desktop->layout->wlr_layout, output->wlr_output, + lx, ly, rotation, &box); if (!intersects) { return; } @@ -622,7 +625,7 @@ static void damage_whole_surface(struct wlr_surface *surface, int sx, int sy, void output_damage_whole_local_surface(struct roots_output *output, struct wlr_surface *surface, double ox, double oy, float rotation) { struct wlr_output_layout_output *layout = wlr_output_layout_get( - output->desktop->layout, output->wlr_output); + output->desktop->layout->wlr_layout, output->wlr_output); struct damage_data data = { .output = output }; surface_for_each_surface(surface, ox + layout->x, oy + layout->y, 0, &data.layout, damage_whole_surface, &data); @@ -679,7 +682,7 @@ static void damage_from_surface(struct wlr_surface *surface, int sx, int sy, wlr_output_transformed_resolution(wlr_output, &ow, &oh); struct wlr_box box; - surface_intersect_output(surface, output->desktop->layout, + surface_intersect_output(surface, output->desktop->layout->wlr_layout, wlr_output, lx, ly, rotation, &box); int center_x = box.x + box.width/2; @@ -704,7 +707,7 @@ static void damage_from_surface(struct wlr_surface *surface, int sx, int sy, void output_damage_from_local_surface(struct roots_output *output, struct wlr_surface *surface, double ox, double oy, float rotation) { struct wlr_output_layout_output *layout = wlr_output_layout_get( - output->desktop->layout, output->wlr_output); + output->desktop->layout->wlr_layout, output->wlr_output); struct damage_data data = { .output = output }; surface_for_each_surface(surface, ox + layout->x, oy + layout->y, 0, &data.layout, damage_from_surface, &data); @@ -752,6 +755,8 @@ static void output_destroy(struct roots_output *output) { // TODO: cursor //example_config_configure_cursor(sample->config, sample->cursor, // sample->compositor); + roots_layout_remove_output(output->desktop->layout, output); + roots_layout_reflow(output->desktop->layout); wl_list_remove(&output->link); wl_list_remove(&output->destroy.link); @@ -784,12 +789,14 @@ static void output_damage_handle_destroy(struct wl_listener *listener, static void output_handle_mode(struct wl_listener *listener, void *data) { struct roots_output *output = wl_container_of(listener, output, mode); + roots_layout_reflow(output->desktop->layout); arrange_layers(output); } static void output_handle_transform(struct wl_listener *listener, void *data) { struct roots_output *output = wl_container_of(listener, output, transform); + roots_layout_reflow(output->desktop->layout); arrange_layers(output); } @@ -846,13 +853,14 @@ void handle_new_output(struct wl_listener *listener, void *data) { } wlr_output_set_scale(wlr_output, output_config->scale); wlr_output_set_transform(wlr_output, output_config->transform); - wlr_output_layout_add(desktop->layout, wlr_output, output_config->x, - output_config->y); + roots_layout_add_output(desktop->layout, output); + roots_layout_reflow(desktop->layout); } else { wlr_output_enable(wlr_output, false); } } else { - wlr_output_layout_add_auto(desktop->layout, wlr_output); + roots_layout_add_output(desktop->layout, output); + roots_layout_reflow(desktop->layout); } struct roots_seat *seat; diff --git a/rootston/rootston.ini.example b/rootston/rootston.ini.example index 556cbefba5..e84d871f9b 100644 --- a/rootston/rootston.ini.example +++ b/rootston/rootston.ini.example @@ -7,10 +7,6 @@ xwayland=false # Single output configuration. String after colon must match output's name. [output:VGA-1] -# Set logical (layout) coordinates for this screen -x = 1920 -y = 0 - # Screen transformation # possible values are: # '90', '180' or '270' - rotate output by specified angle clockwise @@ -19,6 +15,16 @@ y = 0 # and rotate by specified angle rotate = 90 +# Output position configuration +# When multiple layout sections are available, they can be cycled with the +# command next_layout +[layout:l1] +# Put the output with name VGA-1 at (0,0) +VGA-1 = fixed 0 0 +# Put the output with name VGA-2 to the left of the output with name VGA-1 +VGA-2 = left-of VGA-1 +# Other possibilities include : right-of, above, below, mirror + [cursor] # Restrict cursor movements to single output map-to-output = VGA-1 @@ -44,11 +50,13 @@ meta-key = Logo # - "exit" to stop the compositor # - "exec" to execute a shell command # - "close" to close the current view +# - "next_layout" to cycle through layouts # - "next_window" to cycle through windows # - "alpha" to cycle a window's alpha channel [bindings] Logo+Shift+e = exit Logo+q = close Logo+m = maximize +Logo+l = next_layout Alt+Tab = next_window Ctrl+Shift+a = alpha diff --git a/rootston/seat.c b/rootston/seat.c index b137ff1185..0173b3dcda 100644 --- a/rootston/seat.c +++ b/rootston/seat.c @@ -215,7 +215,7 @@ static void roots_seat_init_cursor(struct roots_seat *seat) { seat->cursor->seat = seat; struct wlr_cursor *wlr_cursor = seat->cursor->cursor; struct roots_desktop *desktop = seat->input->server->desktop; - wlr_cursor_attach_output_layout(wlr_cursor, desktop->layout); + wlr_cursor_attach_output_layout(wlr_cursor, desktop->layout->wlr_layout); roots_seat_configure_cursor(seat); roots_seat_configure_xcursor(seat); @@ -762,7 +762,7 @@ void roots_seat_set_focus(struct roots_seat *seat, struct roots_view *view) { if (output->fullscreen_view && output->fullscreen_view != view && wlr_output_layout_intersects( - desktop->layout, + desktop->layout->wlr_layout, output->wlr_output, &box)) { view_set_fullscreen(output->fullscreen_view, false, NULL); diff --git a/rootston/xdg_shell.c b/rootston/xdg_shell.c index 83a1caf086..4e0b49084a 100644 --- a/rootston/xdg_shell.c +++ b/rootston/xdg_shell.c @@ -61,7 +61,7 @@ static void popup_unconstrain(struct roots_xdg_popup *popup) { } struct roots_view *view = popup->view_child.view; - struct wlr_output_layout *layout = view->desktop->layout; + struct wlr_output_layout *layout = view->desktop->layout->wlr_layout; struct wlr_xdg_popup *wlr_popup = popup->wlr_popup; int anchor_lx, anchor_ly; diff --git a/rootston/xdg_shell_v6.c b/rootston/xdg_shell_v6.c index 5a829f5da5..79ecee3673 100644 --- a/rootston/xdg_shell_v6.c +++ b/rootston/xdg_shell_v6.c @@ -63,7 +63,7 @@ static void popup_unconstrain(struct roots_xdg_popup_v6 *popup) { } struct roots_view *view = popup->view_child.view; - struct wlr_output_layout *layout = view->desktop->layout; + struct wlr_output_layout *layout = view->desktop->layout->wlr_layout; struct wlr_xdg_popup_v6 *wlr_popup = popup->wlr_popup; int anchor_lx, anchor_ly;