From acd79e1505c06089e4fb9fb6c0c6e1d351ba9176 Mon Sep 17 00:00:00 2001 From: Drew DeVault Date: Tue, 26 Jun 2018 20:32:09 -0400 Subject: [PATCH 001/148] Implement pid->workspace tracking When you spawn a process with the exec command, sway now notes the workspace you had focused and the pid of the child process, then assigns that workspace to the child when its window appears. Some of this is carried over from sway 0.15, but with some major refactoring and centralization of state. --- config.in | 3 +- include/sway/config.h | 11 ---- include/sway/tree/workspace.h | 5 ++ sway/commands/exec_always.c | 2 +- sway/config.c | 2 - sway/desktop/xdg_shell.c | 3 - sway/desktop/xdg_shell_v6.c | 3 - sway/desktop/xwayland.c | 3 - sway/tree/view.c | 43 ++++++++++--- sway/tree/workspace.c | 112 ++++++++++++++++++++++++++++++++++ 10 files changed, 154 insertions(+), 33 deletions(-) diff --git a/config.in b/config.in index 4a11762a..41f53461 100644 --- a/config.in +++ b/config.in @@ -16,7 +16,8 @@ set $right l # Your preferred terminal emulator set $term urxvt # Your preferred application launcher -set $menu dmenu_run +# Note: it's recommended that you pass the final command to sway +set $menu dmenu_path | dmenu | xargs swaymsg exec ### Output configuration # diff --git a/include/sway/config.h b/include/sway/config.h index e75b0664..9b583323 100644 --- a/include/sway/config.h +++ b/include/sway/config.h @@ -1,6 +1,5 @@ #ifndef _SWAY_CONFIG_H #define _SWAY_CONFIG_H -#define PID_WORKSPACE_TIMEOUT 60 #include #include #include @@ -143,12 +142,6 @@ struct workspace_output { char *workspace; }; -struct pid_workspace { - pid_t *pid; - char *workspace; - time_t *time_added; -}; - struct bar_config { /** * One of "dock", "hide", "invisible" @@ -300,7 +293,6 @@ struct sway_config { list_t *bars; list_t *cmd_queue; list_t *workspace_outputs; - list_t *pid_workspaces; list_t *output_configs; list_t *input_configs; list_t *seat_configs; @@ -384,9 +376,6 @@ struct sway_config { } handler_context; }; -void pid_workspace_add(struct pid_workspace *pw); -void free_pid_workspace(struct pid_workspace *pw); - /** * Loads the main config from the given path. is_active should be true when * reloading the config. diff --git a/include/sway/tree/workspace.h b/include/sway/tree/workspace.h index c72a4ac0..d84e4a02 100644 --- a/include/sway/tree/workspace.h +++ b/include/sway/tree/workspace.h @@ -42,4 +42,9 @@ void workspace_output_add_priority(struct sway_container *workspace, struct sway_container *workspace_output_get_highest_available( struct sway_container *ws, struct sway_container *exclude); + +struct sway_container *workspace_for_pid(pid_t pid); + +void workspace_record_pid(pid_t pid); + #endif diff --git a/sway/commands/exec_always.c b/sway/commands/exec_always.c index 682d195e..abd52e59 100644 --- a/sway/commands/exec_always.c +++ b/sway/commands/exec_always.c @@ -76,7 +76,7 @@ struct cmd_results *cmd_exec_always(int argc, char **argv) { waitpid(pid, NULL, 0); if (*child > 0) { wlr_log(L_DEBUG, "Child process created with pid %d", *child); - // TODO: add PID to active workspace + workspace_record_pid(*child); } else { free(child); } diff --git a/sway/config.c b/sway/config.c index 12a02163..512cab31 100644 --- a/sway/config.c +++ b/sway/config.c @@ -86,7 +86,6 @@ void free_config(struct sway_config *config) { } list_free(config->cmd_queue); list_free(config->workspace_outputs); - list_free(config->pid_workspaces); list_free(config->output_configs); if (config->input_configs) { for (int i = 0; i < config->input_configs->length; i++) { @@ -145,7 +144,6 @@ static void config_defaults(struct sway_config *config) { if (!(config->modes = create_list())) goto cleanup; if (!(config->bars = create_list())) goto cleanup; if (!(config->workspace_outputs = create_list())) goto cleanup; - if (!(config->pid_workspaces = create_list())) goto cleanup; if (!(config->criteria = create_list())) goto cleanup; if (!(config->no_focus = create_list())) goto cleanup; if (!(config->input_configs = create_list())) goto cleanup; diff --git a/sway/desktop/xdg_shell.c b/sway/desktop/xdg_shell.c index 82db4076..47604c31 100644 --- a/sway/desktop/xdg_shell.c +++ b/sway/desktop/xdg_shell.c @@ -321,9 +321,6 @@ void handle_xdg_shell_surface(struct wl_listener *listener, void *data) { view_init(&xdg_shell_view->view, SWAY_VIEW_XDG_SHELL, &view_impl); xdg_shell_view->view.wlr_xdg_surface = xdg_surface; - // TODO: - // - Look up pid and open on appropriate workspace - xdg_shell_view->map.notify = handle_map; wl_signal_add(&xdg_surface->events.map, &xdg_shell_view->map); diff --git a/sway/desktop/xdg_shell_v6.c b/sway/desktop/xdg_shell_v6.c index 0d3c1644..b28c4b9c 100644 --- a/sway/desktop/xdg_shell_v6.c +++ b/sway/desktop/xdg_shell_v6.c @@ -312,9 +312,6 @@ void handle_xdg_shell_v6_surface(struct wl_listener *listener, void *data) { view_init(&xdg_shell_v6_view->view, SWAY_VIEW_XDG_SHELL_V6, &view_impl); xdg_shell_v6_view->view.wlr_xdg_surface_v6 = xdg_surface; - // TODO: - // - Look up pid and open on appropriate workspace - xdg_shell_v6_view->map.notify = handle_map; wl_signal_add(&xdg_surface->events.map, &xdg_shell_v6_view->map); diff --git a/sway/desktop/xwayland.c b/sway/desktop/xwayland.c index 4bb35f60..b3b1473d 100644 --- a/sway/desktop/xwayland.c +++ b/sway/desktop/xwayland.c @@ -446,9 +446,6 @@ void handle_xwayland_surface(struct wl_listener *listener, void *data) { view_init(&xwayland_view->view, SWAY_VIEW_XWAYLAND, &view_impl); xwayland_view->view.wlr_xwayland_surface = xsurface; - // TODO: - // - Look up pid and open on appropriate workspace - wl_signal_add(&xsurface->events.destroy, &xwayland_view->destroy); xwayland_view->destroy.notify = handle_destroy; diff --git a/sway/tree/view.c b/sway/tree/view.c index 06e9edc5..24fb6864 100644 --- a/sway/tree/view.c +++ b/sway/tree/view.c @@ -3,6 +3,7 @@ #include #include #include +#include #include "list.h" #include "log.h" #include "sway/criteria.h" @@ -492,9 +493,21 @@ void view_map(struct sway_view *view, struct wlr_surface *wlr_surface) { return; } + pid_t pid; + if (view->type == SWAY_VIEW_XWAYLAND) { + struct wlr_xwayland_surface *surf = + wlr_xwayland_surface_from_wlr_surface(wlr_surface); + pid = surf->pid; + } else { + struct wl_client *client = + wl_resource_get_client(wlr_surface->resource); + wl_client_get_credentials(client, &pid, NULL, NULL); + } + struct sway_seat *seat = input_manager_current_seat(input_manager); - struct sway_container *focus = + struct sway_container *target_sibling = seat_get_focus_inactive(seat, &root_container); + struct sway_container *prev_focus = target_sibling; struct sway_container *cont = NULL; // Check if there's any `assign` criteria for the view @@ -508,18 +521,31 @@ void view_map(struct sway_view *view, struct wlr_surface *wlr_surface) { if (!workspace) { workspace = workspace_create(NULL, criteria->target); } - focus = seat_get_focus_inactive(seat, workspace); + prev_focus = target_sibling; + target_sibling = seat_get_focus_inactive(seat, workspace); } else { // TODO: CT_ASSIGN_OUTPUT } } + list_free(criterias); + + if (!workspace) { + workspace = workspace_for_pid(pid); + if (workspace) { + prev_focus = target_sibling; + target_sibling = seat_get_focus_inactive(seat, workspace); + } + } // If we're about to launch the view into the floating container, then // launch it as a tiled view in the root of the workspace instead. - if (container_is_floating(focus)) { - focus = focus->parent->parent; + if (container_is_floating(target_sibling)) { + if (prev_focus == target_sibling) { + prev_focus = target_sibling->parent->parent; + } + target_sibling = target_sibling->parent->parent; } - free(criterias); - cont = container_view_create(focus, view); + + cont = container_view_create(target_sibling, view); view->surface = wlr_surface; view->swayc = cont; @@ -538,9 +564,8 @@ void view_map(struct sway_view *view, struct wlr_surface *wlr_surface) { container_set_floating(view->swayc, true); } - input_manager_set_focus(input_manager, cont); - if (workspace) { - workspace_switch(workspace); + if (prev_focus == target_sibling) { + input_manager_set_focus(input_manager, cont); } view_update_title(view, false); diff --git a/sway/tree/workspace.c b/sway/tree/workspace.c index 5eb4be0f..651cc011 100644 --- a/sway/tree/workspace.c +++ b/sway/tree/workspace.c @@ -9,6 +9,7 @@ #include "sway/input/input-manager.h" #include "sway/input/seat.h" #include "sway/ipc-server.h" +#include "sway/output.h" #include "sway/tree/arrange.h" #include "sway/tree/container.h" #include "sway/tree/workspace.h" @@ -516,3 +517,114 @@ struct sway_container *workspace_output_get_highest_available( return NULL; } + +struct pid_workspace { + pid_t pid; + char *workspace; + struct timespec time_added; + + struct sway_container *output; + struct wl_listener output_destroy; + + struct wl_list link; +}; + +static struct wl_list pid_workspaces; + +struct sway_container *workspace_for_pid(pid_t pid) { + if (!pid_workspaces.prev && !pid_workspaces.next) { + wl_list_init(&pid_workspaces); + return NULL; + } + + struct sway_container *ws = NULL; + struct pid_workspace *pw = NULL; + + wlr_log(L_DEBUG, "Looking up workspace for pid %d", pid); + + do { + struct pid_workspace *_pw = NULL; + wl_list_for_each(_pw, &pid_workspaces, link) { + if (pid == _pw->pid) { + pw = _pw; + wlr_log(L_DEBUG, + "found pid_workspace for pid %d, workspace %s", + pid, pw->workspace); + goto found; + } + } + pid = get_parent_pid(pid); + } while (pid > 1); +found: + + if (pw && pw->workspace) { + ws = workspace_by_name(pw->workspace); + + if (!ws) { + wlr_log(L_DEBUG, + "Creating workspace %s for pid %d because it disappeared", + pw->workspace, pid); + ws = workspace_create(pw->output, pw->workspace); + } + + wl_list_remove(&pw->output_destroy.link); + wl_list_remove(&pw->link); + free(pw->workspace); + free(pw); + } + + return ws; +} + +static void pw_handle_output_destroy(struct wl_listener *listener, void *data) { + struct pid_workspace *pw = wl_container_of(listener, pw, output_destroy); + pw->output = NULL; +} + +void workspace_record_pid(pid_t pid) { + wlr_log(L_DEBUG, "Recording workspace for process %d", pid); + if (!pid_workspaces.prev && !pid_workspaces.next) { + wl_list_init(&pid_workspaces); + } + + struct sway_seat *seat = input_manager_current_seat(input_manager); + struct sway_container *ws = + seat_get_focus_inactive(seat, &root_container); + if (ws && ws->type != C_WORKSPACE) { + ws = container_parent(ws, C_WORKSPACE); + } + if (!ws) { + wlr_log(L_DEBUG, "Bailing out, no workspace"); + return; + } + struct sway_container *output = ws->parent; + if (!output) { + wlr_log(L_DEBUG, "Bailing out, no output"); + return; + } + + struct timespec now; + clock_gettime(CLOCK_MONOTONIC, &now); + + // Remove expired entries + static const int timeout = 60; + struct pid_workspace *old, *_old; + wl_list_for_each_safe(old, _old, &pid_workspaces, link) { + if (now.tv_sec - old->time_added.tv_sec >= timeout) { + wl_list_remove(&old->output_destroy.link); + wl_list_remove(&old->link); + free(old->workspace); + free(old); + } + } + + struct pid_workspace *pw = calloc(1, sizeof(struct pid_workspace)); + pw->workspace = strdup(ws->name); + pw->output = output; + pw->pid = pid; + memcpy(&pw->time_added, &now, sizeof(struct timespec)); + pw->output_destroy.notify = pw_handle_output_destroy; + wl_signal_add(&output->sway_output->wlr_output->events.destroy, + &pw->output_destroy); + wl_list_insert(&pid_workspaces, &pw->link); +} From 9fbe13b9be18c732b58033a57a22a299af91a170 Mon Sep 17 00:00:00 2001 From: Ryan Dwyer Date: Wed, 18 Jul 2018 16:13:28 +1000 Subject: [PATCH 002/148] Implement floating_modifier and mouse operations for floating views This implements the following: * `floating_modifier` configuration directive * Drag a floating window by its title bar * Hold mod + drag a floating window from anywhere * Resize a floating view by dragging the border * Resize a floating view by holding mod and right clicking anywhere on the view * Resize a floating view and keep aspect ratio by holding shift while resizing using either method * Mouse cursor turns into resize when hovering floating border or corner --- include/sway/commands.h | 2 +- include/sway/input/seat.h | 16 ++ include/sway/tree/container.h | 12 ++ include/sway/tree/layout.h | 9 +- sway/commands.c | 1 + sway/commands/floating_modifier.c | 30 +++ sway/input/cursor.c | 322 +++++++++++++++++++++++++++++- sway/meson.build | 1 + sway/tree/container.c | 16 +- sway/tree/layout.c | 1 + sway/tree/view.c | 2 + 11 files changed, 395 insertions(+), 17 deletions(-) create mode 100644 sway/commands/floating_modifier.c diff --git a/include/sway/commands.h b/include/sway/commands.h index e71a7228..f53d335a 100644 --- a/include/sway/commands.h +++ b/include/sway/commands.h @@ -106,7 +106,7 @@ sway_cmd cmd_exit; sway_cmd cmd_floating; sway_cmd cmd_floating_maximum_size; sway_cmd cmd_floating_minimum_size; -sway_cmd cmd_floating_mod; +sway_cmd cmd_floating_modifier; sway_cmd cmd_floating_scroll; sway_cmd cmd_focus; sway_cmd cmd_focus_follows_mouse; diff --git a/include/sway/input/seat.h b/include/sway/input/seat.h index eac1626b..be1f3610 100644 --- a/include/sway/input/seat.h +++ b/include/sway/input/seat.h @@ -34,6 +34,8 @@ struct sway_drag_icon { struct wl_listener destroy; }; +enum resize_edge; + struct sway_seat { struct wlr_seat *wlr_seat; struct sway_cursor *cursor; @@ -52,6 +54,20 @@ struct sway_seat { int32_t touch_id; double touch_x, touch_y; + // Operations (drag and resize) + enum { + OP_NONE, + OP_DRAG, + OP_RESIZE, + } operation; + struct sway_container *op_container; + enum resize_edge op_resize_edge; + uint32_t op_button; + bool op_resize_preserve_ratio; + double op_ref_lx, op_ref_ly; // cursor's x/y at start of op + double op_ref_width, op_ref_height; // container's size at start of op + double op_ref_con_lx, op_ref_con_ly; // container's x/y at start of op + struct wl_listener focus_destroy; struct wl_listener new_container; struct wl_listener new_drag_icon; diff --git a/include/sway/tree/container.h b/include/sway/tree/container.h index ca7a3288..59c5b4c7 100644 --- a/include/sway/tree/container.h +++ b/include/sway/tree/container.h @@ -304,6 +304,12 @@ bool container_is_floating(struct sway_container *container); */ void container_get_box(struct sway_container *container, struct wlr_box *box); +/** + * Move a floating container by the specified amount. + */ +void container_floating_translate(struct sway_container *con, + double x_amount, double y_amount); + /** * Move a floating container to a new layout-local position. */ @@ -318,4 +324,10 @@ void container_set_dirty(struct sway_container *container); bool container_has_urgent_child(struct sway_container *container); +/** + * If the container is involved in a drag or resize operation via a mouse, this + * ends the operation. + */ +void container_end_mouse_operation(struct sway_container *container); + #endif diff --git a/include/sway/tree/layout.h b/include/sway/tree/layout.h index ba265623..5a78fd58 100644 --- a/include/sway/tree/layout.h +++ b/include/sway/tree/layout.h @@ -14,10 +14,11 @@ enum movement_direction { }; enum resize_edge { - RESIZE_EDGE_LEFT, - RESIZE_EDGE_RIGHT, - RESIZE_EDGE_TOP, - RESIZE_EDGE_BOTTOM, + RESIZE_EDGE_NONE = 0, + RESIZE_EDGE_LEFT = 1, + RESIZE_EDGE_RIGHT = 2, + RESIZE_EDGE_TOP = 4, + RESIZE_EDGE_BOTTOM = 8, }; struct sway_container; diff --git a/sway/commands.c b/sway/commands.c index f1f03574..f40f0e9d 100644 --- a/sway/commands.c +++ b/sway/commands.c @@ -103,6 +103,7 @@ static struct cmd_handler handlers[] = { { "exec_always", cmd_exec_always }, { "floating_maximum_size", cmd_floating_maximum_size }, { "floating_minimum_size", cmd_floating_minimum_size }, + { "floating_modifier", cmd_floating_modifier }, { "focus", cmd_focus }, { "focus_follows_mouse", cmd_focus_follows_mouse }, { "focus_wrapping", cmd_focus_wrapping }, diff --git a/sway/commands/floating_modifier.c b/sway/commands/floating_modifier.c new file mode 100644 index 00000000..1ced50af --- /dev/null +++ b/sway/commands/floating_modifier.c @@ -0,0 +1,30 @@ +#ifdef __linux__ +#include +#elif __FreeBSD__ +#include +#endif +#include +#include +#include +#include "sway/commands.h" +#include "sway/config.h" +#include "list.h" +#include "log.h" +#include "util.h" + +struct cmd_results *cmd_floating_modifier(int argc, char **argv) { + struct cmd_results *error = NULL; + if ((error = checkarg(argc, "floating_modifier", EXPECTED_EQUAL_TO, 1))) { + return error; + } + + uint32_t mod = get_modifier_mask_by_name(argv[0]); + if (!mod) { + return cmd_results_new(CMD_INVALID, "floating_modifier", + "Invalid modifier"); + } + + config->floating_mod = mod; + + return cmd_results_new(CMD_SUCCESS, NULL, NULL); +} diff --git a/sway/input/cursor.c b/sway/input/cursor.c index c76c20b3..6ad214b5 100644 --- a/sway/input/cursor.c +++ b/sway/input/cursor.c @@ -5,15 +5,19 @@ #elif __FreeBSD__ #include #endif +#include #include #include #include #include "list.h" #include "log.h" +#include "sway/desktop.h" #include "sway/desktop/transaction.h" #include "sway/input/cursor.h" +#include "sway/input/keyboard.h" #include "sway/layers.h" #include "sway/output.h" +#include "sway/tree/arrange.h" #include "sway/tree/view.h" #include "sway/tree/workspace.h" #include "wlr-layer-shell-unstable-v1-protocol.h" @@ -127,7 +131,7 @@ static struct sway_container *container_at_coords( return ws; } - c = seat_get_focus_inactive(seat, output->swayc); + c = seat_get_active_child(seat, output->swayc); if (c) { return c; } @@ -139,6 +143,173 @@ static struct sway_container *container_at_coords( return output->swayc; } +static enum resize_edge find_resize_edge(struct sway_container *cont, + struct sway_cursor *cursor) { + if (cont->type != C_VIEW) { + return RESIZE_EDGE_NONE; + } + struct sway_view *view = cont->sway_view; + if (view->border == B_NONE || !view->border_thickness || view->using_csd) { + return RESIZE_EDGE_NONE; + } + + enum resize_edge edge = 0; + if (cursor->cursor->x < cont->x + view->border_thickness) { + edge |= RESIZE_EDGE_LEFT; + } + if (cursor->cursor->y < cont->y + view->border_thickness) { + edge |= RESIZE_EDGE_TOP; + } + if (cursor->cursor->x >= cont->x + cont->width - view->border_thickness) { + edge |= RESIZE_EDGE_RIGHT; + } + if (cursor->cursor->y >= cont->y + cont->height - view->border_thickness) { + edge |= RESIZE_EDGE_BOTTOM; + } + return edge; +} + +static void handle_drag_motion(struct sway_seat *seat, + struct sway_cursor *cursor) { + struct sway_container *con = seat->op_container; + desktop_damage_whole_container(con); + container_floating_translate(con, + cursor->cursor->x - cursor->previous.x, + cursor->cursor->y - cursor->previous.y); + desktop_damage_whole_container(con); +} + +static void calculate_floating_constraints(struct sway_container *con, + int *min_width, int *max_width, int *min_height, int *max_height) { + if (config->floating_minimum_width == -1) { // no minimum + *min_width = 0; + } else if (config->floating_minimum_width == 0) { // automatic + *min_width = 75; + } else { + *min_width = config->floating_minimum_width; + } + + if (config->floating_minimum_height == -1) { // no minimum + *min_height = 0; + } else if (config->floating_minimum_height == 0) { // automatic + *min_height = 50; + } else { + *min_height = config->floating_minimum_height; + } + + if (config->floating_maximum_width == -1) { // no maximum + *max_width = INT_MAX; + } else if (config->floating_maximum_width == 0) { // automatic + struct sway_container *ws = container_parent(con, C_WORKSPACE); + *max_width = ws->width; + } else { + *max_width = config->floating_maximum_width; + } + + if (config->floating_maximum_height == -1) { // no maximum + *max_height = INT_MAX; + } else if (config->floating_maximum_height == 0) { // automatic + struct sway_container *ws = container_parent(con, C_WORKSPACE); + *max_height = ws->height; + } else { + *max_height = config->floating_maximum_height; + } +} +static void handle_resize_motion(struct sway_seat *seat, + struct sway_cursor *cursor) { + struct sway_container *con = seat->op_container; + double center_lx = con->x + con->width / 2; + double center_ly = con->y + con->height / 2; + enum resize_edge edge = seat->op_resize_edge; + + // The amount the mouse has moved since the start of the resize operation + // Positive is down/right + double mouse_move_x = cursor->cursor->x - seat->op_ref_lx; + double mouse_move_y = cursor->cursor->y - seat->op_ref_ly; + + if (edge == RESIZE_EDGE_TOP || edge == RESIZE_EDGE_BOTTOM) { + mouse_move_x = 0; + } + if (edge == RESIZE_EDGE_LEFT || edge == RESIZE_EDGE_RIGHT) { + mouse_move_y = 0; + } + + double grow_width = seat->op_ref_lx > center_lx ? + mouse_move_x : -mouse_move_x; + double grow_height = seat->op_ref_ly > center_ly ? + mouse_move_y : -mouse_move_y; + + if (seat->op_resize_preserve_ratio) { + double x_multiplier = grow_width / seat->op_ref_width; + double y_multiplier = grow_height / seat->op_ref_height; + double avg_multiplier = (x_multiplier + y_multiplier) / 2; + grow_width = seat->op_ref_width * avg_multiplier; + grow_height = seat->op_ref_height * avg_multiplier; + } + + // If we're resizing from the center (mod + right click), we need to double + // the amount we're growing because we're doing it in both directions. + if (edge == RESIZE_EDGE_NONE) { + grow_width *= 2; + grow_height *= 2; + } + + // Determine new width/height, and accommodate for min/max values + double width = seat->op_ref_width + grow_width; + double height = seat->op_ref_height + grow_height; + int min_width, max_width, min_height, max_height; + calculate_floating_constraints(con, &min_width, &max_width, + &min_height, &max_height); + width = fmax(min_width, fmin(width, max_width)); + height = fmax(min_height, fmin(height, max_height)); + + // Recalculate these, in case we hit a min/max limit + grow_width = width - seat->op_ref_width; + grow_height = height - seat->op_ref_height; + + // Determine grow x/y values - these are relative to the container's x/y at + // the start of the resize operation. + double grow_x = 0, grow_y = 0; + if (edge & RESIZE_EDGE_LEFT) { + grow_x = -grow_width; + } else if (edge & RESIZE_EDGE_RIGHT) { + grow_x = 0; + } else { + grow_x = -grow_width / 2; + } + if (edge & RESIZE_EDGE_TOP) { + grow_y = -grow_height; + } else if (edge & RESIZE_EDGE_BOTTOM) { + grow_y = 0; + } else { + grow_y = -grow_height / 2; + } + + // Determine the amounts we need to bump everything relative to the current + // size. + int relative_grow_width = width - con->width; + int relative_grow_height = height - con->height; + int relative_grow_x = (seat->op_ref_con_lx + grow_x) - con->x; + int relative_grow_y = (seat->op_ref_con_ly + grow_y) - con->y; + + // Actually resize stuff + con->x += relative_grow_x; + con->y += relative_grow_y; + con->width += relative_grow_width; + con->height += relative_grow_height; + + if (con->type == C_VIEW) { + struct sway_view *view = con->sway_view; + view->x += relative_grow_x; + view->y += relative_grow_y; + view->width += relative_grow_width; + view->height += relative_grow_height; + } + + arrange_windows(con); + transaction_commit_dirty(); +} + void cursor_send_pointer_motion(struct sway_cursor *cursor, uint32_t time_msec, bool allow_refocusing) { if (time_msec == 0) { @@ -146,6 +317,18 @@ void cursor_send_pointer_motion(struct sway_cursor *cursor, uint32_t time_msec, } struct sway_seat *seat = cursor->seat; + + if (seat->operation != OP_NONE) { + if (seat->operation == OP_DRAG) { + handle_drag_motion(seat, cursor); + } else { + handle_resize_motion(seat, cursor); + } + cursor->previous.x = cursor->cursor->x; + cursor->previous.y = cursor->cursor->y; + return; + } + struct wlr_seat *wlr_seat = seat->wlr_seat; struct wlr_surface *surface = NULL; double sx, sy; @@ -194,15 +377,54 @@ void cursor_send_pointer_motion(struct sway_cursor *cursor, uint32_t time_msec, } } - // reset cursor if switching between clients - struct wl_client *client = NULL; - if (surface != NULL) { - client = wl_resource_get_client(surface->resource); - } - if (client != cursor->image_client) { + // Handle cursor image + if (surface) { + // Reset cursor if switching between clients + struct wl_client *client = wl_resource_get_client(surface->resource); + if (client != cursor->image_client) { + wlr_xcursor_manager_set_cursor_image(cursor->xcursor_manager, + "left_ptr", cursor->cursor); + cursor->image_client = client; + } + } else if (c && container_is_floating(c)) { + // Try a floating container's resize edge + enum resize_edge edge = find_resize_edge(c, cursor); + if (edge == RESIZE_EDGE_NONE) { + wlr_xcursor_manager_set_cursor_image(cursor->xcursor_manager, + "left_ptr", cursor->cursor); + } else if (edge == RESIZE_EDGE_TOP) { + wlr_xcursor_manager_set_cursor_image(cursor->xcursor_manager, + "top_side", cursor->cursor); + } else if (edge == RESIZE_EDGE_RIGHT) { + wlr_xcursor_manager_set_cursor_image(cursor->xcursor_manager, + "right_side", cursor->cursor); + } else if (edge == RESIZE_EDGE_BOTTOM) { + wlr_xcursor_manager_set_cursor_image(cursor->xcursor_manager, + "bottom_side", cursor->cursor); + } else if (edge == RESIZE_EDGE_LEFT) { + wlr_xcursor_manager_set_cursor_image(cursor->xcursor_manager, + "left_side", cursor->cursor); + } else if (edge == (RESIZE_EDGE_TOP | RESIZE_EDGE_LEFT)) { + wlr_xcursor_manager_set_cursor_image(cursor->xcursor_manager, + "top_left_corner", cursor->cursor); + } else if (edge == (RESIZE_EDGE_TOP | RESIZE_EDGE_RIGHT)) { + wlr_xcursor_manager_set_cursor_image(cursor->xcursor_manager, + "top_right_corner", cursor->cursor); + } else if (edge == (RESIZE_EDGE_BOTTOM | RESIZE_EDGE_LEFT)) { + wlr_xcursor_manager_set_cursor_image(cursor->xcursor_manager, + "bottom_left_corner", cursor->cursor); + } else if (edge == (RESIZE_EDGE_BOTTOM | RESIZE_EDGE_RIGHT)) { + wlr_xcursor_manager_set_cursor_image(cursor->xcursor_manager, + "bottom_right_corner", cursor->cursor); + } else { + wlr_xcursor_manager_set_cursor_image(cursor->xcursor_manager, + "left_ptr", cursor->cursor); + } + cursor->image_client = NULL; + } else { wlr_xcursor_manager_set_cursor_image(cursor->xcursor_manager, - "left_ptr", cursor->cursor); - cursor->image_client = client; + "left_ptr", cursor->cursor); + cursor->image_client = NULL; } // send pointer enter/leave @@ -243,8 +465,79 @@ static void handle_cursor_motion_absolute( transaction_commit_dirty(); } +static void handle_end_operation(struct sway_seat *seat) { + if (seat->operation == OP_DRAG) { + // We "move" the container to its own location so it discovers its + // output again. + struct sway_container *con = seat->op_container; + container_floating_move_to(con, con->x, con->y); + seat->operation = OP_NONE; + seat->op_container = NULL; + } else { + // OP_RESIZE + seat->operation = OP_NONE; + seat->op_container = NULL; + } +} + +static void dispatch_cursor_button_floating(struct sway_cursor *cursor, + uint32_t time_msec, uint32_t button, enum wlr_button_state state, + struct wlr_surface *surface, double sx, double sy, + struct sway_container *cont) { + struct sway_seat *seat = cursor->seat; + + // Deny dragging or resizing a fullscreen view + if (cont->type == C_VIEW && cont->sway_view->is_fullscreen) { + wlr_seat_pointer_notify_button(seat->wlr_seat, time_msec, button, state); + return; + } + + struct wlr_keyboard *keyboard = wlr_seat_get_keyboard(seat->wlr_seat); + bool mod_pressed = keyboard && + (keyboard->modifiers.depressed & config->floating_mod); + enum resize_edge edge = find_resize_edge(cont, cursor); + bool over_title = edge == RESIZE_EDGE_NONE && !surface; + + // Check for beginning drag + if (button == BTN_LEFT && state == WLR_BUTTON_PRESSED && + (mod_pressed || over_title)) { + seat->operation = OP_DRAG; + seat->op_container = cont; + seat->op_button = button; + return; + } + + // Check for beginning resize + bool resizing_via_border = button == BTN_LEFT && edge; + bool resizing_via_mod = button == BTN_RIGHT && mod_pressed; + if ((resizing_via_border || resizing_via_mod) && + state == WLR_BUTTON_PRESSED) { + seat->operation = OP_RESIZE; + seat->op_container = cont; + seat->op_resize_preserve_ratio = keyboard && + (keyboard->modifiers.depressed & 1); // Shift + seat->op_resize_edge = edge; + seat->op_button = button; + seat->op_ref_lx = cursor->cursor->x; + seat->op_ref_ly = cursor->cursor->y; + seat->op_ref_con_lx = cont->x; + seat->op_ref_con_ly = cont->y; + seat->op_ref_width = cont->width; + seat->op_ref_height = cont->height; + return; + } + + // Send event to surface + wlr_seat_pointer_notify_button(seat->wlr_seat, time_msec, button, state); +} + void dispatch_cursor_button(struct sway_cursor *cursor, uint32_t time_msec, uint32_t button, enum wlr_button_state state) { + if (cursor->seat->operation != OP_NONE && + button == cursor->seat->op_button && state == WLR_BUTTON_RELEASED) { + handle_end_operation(cursor->seat); + return; + } if (time_msec == 0) { time_msec = get_current_time_msec(); } @@ -259,6 +552,11 @@ void dispatch_cursor_button(struct sway_cursor *cursor, if (layer->current.keyboard_interactive) { seat_set_focus_layer(cursor->seat, layer); } + wlr_seat_pointer_notify_button(cursor->seat->wlr_seat, + time_msec, button, state); + } else if (cont && container_is_floating(cont)) { + dispatch_cursor_button_floating(cursor, time_msec, button, state, + surface, sx, sy, cont); } else if (surface && cont && cont->type != C_VIEW) { // Avoid moving keyboard focus from a surface that accepts it to one // that does not unless the change would move us to a new workspace. @@ -275,12 +573,14 @@ void dispatch_cursor_button(struct sway_cursor *cursor, if (new_ws != old_ws) { seat_set_focus(cursor->seat, cont); } + wlr_seat_pointer_notify_button(cursor->seat->wlr_seat, + time_msec, button, state); } else if (cont) { seat_set_focus(cursor->seat, cont); + wlr_seat_pointer_notify_button(cursor->seat->wlr_seat, + time_msec, button, state); } - wlr_seat_pointer_notify_button(cursor->seat->wlr_seat, - time_msec, button, state); transaction_commit_dirty(); } diff --git a/sway/meson.build b/sway/meson.build index 09bc40b8..f4fdc8ea 100644 --- a/sway/meson.build +++ b/sway/meson.build @@ -42,6 +42,7 @@ sway_sources = files( 'commands/exec_always.c', 'commands/floating.c', 'commands/floating_minmax_size.c', + 'commands/floating_modifier.c', 'commands/focus.c', 'commands/focus_follows_mouse.c', 'commands/focus_wrapping.c', diff --git a/sway/tree/container.c b/sway/tree/container.c index 4dbfbb29..ba4af352 100644 --- a/sway/tree/container.c +++ b/sway/tree/container.c @@ -323,6 +323,8 @@ static struct sway_container *container_destroy_noreaping( } } + container_end_mouse_operation(con); + con->destroying = true; container_set_dirty(con); @@ -964,6 +966,8 @@ void container_set_floating(struct sway_container *container, bool enable) { container_reap_empty_recursive(workspace->sway_workspace->floating); } + container_end_mouse_operation(container); + ipc_event_window(container, "floating"); } @@ -1009,7 +1013,7 @@ void container_get_box(struct sway_container *container, struct wlr_box *box) { /** * Translate the container's position as well as all children. */ -static void container_floating_translate(struct sway_container *con, +void container_floating_translate(struct sway_container *con, double x_amount, double y_amount) { con->x += x_amount; con->y += y_amount; @@ -1105,3 +1109,13 @@ static bool find_urgent_iterator(struct sway_container *con, bool container_has_urgent_child(struct sway_container *container) { return container_find(container, find_urgent_iterator, NULL); } + +void container_end_mouse_operation(struct sway_container *container) { + struct sway_seat *seat; + wl_list_for_each(seat, &input_manager->seats, link) { + if (seat->op_container == container) { + seat->op_container = NULL; + seat->operation = OP_NONE; + } + } +} diff --git a/sway/tree/layout.c b/sway/tree/layout.c index 1f898f8a..533906fa 100644 --- a/sway/tree/layout.c +++ b/sway/tree/layout.c @@ -562,6 +562,7 @@ void container_move(struct sway_container *container, workspace_detect_urgent(last_ws); workspace_detect_urgent(next_ws); } + container_end_mouse_operation(container); } enum sway_container_layout container_get_default_layout( diff --git a/sway/tree/view.c b/sway/tree/view.c index 7881e6d7..24594950 100644 --- a/sway/tree/view.c +++ b/sway/tree/view.c @@ -387,6 +387,8 @@ void view_set_fullscreen(struct sway_view *view, bool fullscreen) { } } + container_end_mouse_operation(view->swayc); + ipc_event_window(view->swayc, "fullscreen_mode"); } From 3aadf944ae0ad08439d2651d37eb3c6c686d2709 Mon Sep 17 00:00:00 2001 From: Ryan Dwyer Date: Wed, 18 Jul 2018 17:40:53 +1000 Subject: [PATCH 003/148] Use WLR_MODIFIER_SHIFT --- sway/input/cursor.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sway/input/cursor.c b/sway/input/cursor.c index 6ad214b5..3c27a7f6 100644 --- a/sway/input/cursor.c +++ b/sway/input/cursor.c @@ -515,7 +515,7 @@ static void dispatch_cursor_button_floating(struct sway_cursor *cursor, seat->operation = OP_RESIZE; seat->op_container = cont; seat->op_resize_preserve_ratio = keyboard && - (keyboard->modifiers.depressed & 1); // Shift + (keyboard->modifiers.depressed & WLR_MODIFIER_SHIFT); seat->op_resize_edge = edge; seat->op_button = button; seat->op_ref_lx = cursor->cursor->x; From 2c6616050a924a356b9bebbe16c9c7b8661b5d80 Mon Sep 17 00:00:00 2001 From: Ryan Dwyer Date: Thu, 19 Jul 2018 13:17:20 +1000 Subject: [PATCH 004/148] Make mod + resize do it from the top left corner --- sway/input/cursor.c | 20 +++++--------------- 1 file changed, 5 insertions(+), 15 deletions(-) diff --git a/sway/input/cursor.c b/sway/input/cursor.c index 3c27a7f6..e5631f5b 100644 --- a/sway/input/cursor.c +++ b/sway/input/cursor.c @@ -218,8 +218,6 @@ static void calculate_floating_constraints(struct sway_container *con, static void handle_resize_motion(struct sway_seat *seat, struct sway_cursor *cursor) { struct sway_container *con = seat->op_container; - double center_lx = con->x + con->width / 2; - double center_ly = con->y + con->height / 2; enum resize_edge edge = seat->op_resize_edge; // The amount the mouse has moved since the start of the resize operation @@ -234,10 +232,8 @@ static void handle_resize_motion(struct sway_seat *seat, mouse_move_y = 0; } - double grow_width = seat->op_ref_lx > center_lx ? - mouse_move_x : -mouse_move_x; - double grow_height = seat->op_ref_ly > center_ly ? - mouse_move_y : -mouse_move_y; + double grow_width = edge & RESIZE_EDGE_LEFT ? -mouse_move_x : mouse_move_x; + double grow_height = edge & RESIZE_EDGE_TOP ? -mouse_move_y : mouse_move_y; if (seat->op_resize_preserve_ratio) { double x_multiplier = grow_width / seat->op_ref_width; @@ -247,13 +243,6 @@ static void handle_resize_motion(struct sway_seat *seat, grow_height = seat->op_ref_height * avg_multiplier; } - // If we're resizing from the center (mod + right click), we need to double - // the amount we're growing because we're doing it in both directions. - if (edge == RESIZE_EDGE_NONE) { - grow_width *= 2; - grow_height *= 2; - } - // Determine new width/height, and accommodate for min/max values double width = seat->op_ref_width + grow_width; double height = seat->op_ref_height + grow_height; @@ -508,7 +497,7 @@ static void dispatch_cursor_button_floating(struct sway_cursor *cursor, } // Check for beginning resize - bool resizing_via_border = button == BTN_LEFT && edge; + bool resizing_via_border = button == BTN_LEFT && edge != RESIZE_EDGE_NONE; bool resizing_via_mod = button == BTN_RIGHT && mod_pressed; if ((resizing_via_border || resizing_via_mod) && state == WLR_BUTTON_PRESSED) { @@ -516,7 +505,8 @@ static void dispatch_cursor_button_floating(struct sway_cursor *cursor, seat->op_container = cont; seat->op_resize_preserve_ratio = keyboard && (keyboard->modifiers.depressed & WLR_MODIFIER_SHIFT); - seat->op_resize_edge = edge; + seat->op_resize_edge = resizing_via_mod ? + RESIZE_EDGE_BOTTOM | RESIZE_EDGE_RIGHT : edge; seat->op_button = button; seat->op_ref_lx = cursor->cursor->x; seat->op_ref_ly = cursor->cursor->y; From f9491c9584d2c1fb789eee9c9e21fd6c274f4579 Mon Sep 17 00:00:00 2001 From: Ryan Dwyer Date: Thu, 19 Jul 2018 13:18:04 +1000 Subject: [PATCH 005/148] Fix damage issue when moving and resizing --- sway/desktop/output.c | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/sway/desktop/output.c b/sway/desktop/output.c index a9808406..a206ac6b 100644 --- a/sway/desktop/output.c +++ b/sway/desktop/output.c @@ -463,11 +463,12 @@ static void output_damage_whole_container_iterator(struct sway_container *con, void output_damage_whole_container(struct sway_output *output, struct sway_container *con) { + // Pad the box by 1px, because the width is a double and might be a fraction struct wlr_box box = { - .x = con->current.swayc_x - output->wlr_output->lx, - .y = con->current.swayc_y - output->wlr_output->ly, - .width = con->current.swayc_width, - .height = con->current.swayc_height, + .x = con->current.swayc_x - output->wlr_output->lx - 1, + .y = con->current.swayc_y - output->wlr_output->ly - 1, + .width = con->current.swayc_width + 2, + .height = con->current.swayc_height + 2, }; scale_box(&box, output->wlr_output->scale); wlr_output_damage_add_box(output->damage, &box); From c299b6b5cd72ce186baa9a0a1cc09a4293431d74 Mon Sep 17 00:00:00 2001 From: Ryan Dwyer Date: Thu, 19 Jul 2018 13:42:43 +1000 Subject: [PATCH 006/148] Use max multiplier when resizing while preserving ratio --- sway/input/cursor.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/sway/input/cursor.c b/sway/input/cursor.c index e5631f5b..a24a7de8 100644 --- a/sway/input/cursor.c +++ b/sway/input/cursor.c @@ -238,9 +238,9 @@ static void handle_resize_motion(struct sway_seat *seat, if (seat->op_resize_preserve_ratio) { double x_multiplier = grow_width / seat->op_ref_width; double y_multiplier = grow_height / seat->op_ref_height; - double avg_multiplier = (x_multiplier + y_multiplier) / 2; - grow_width = seat->op_ref_width * avg_multiplier; - grow_height = seat->op_ref_height * avg_multiplier; + double max_multiplier = fmax(x_multiplier, y_multiplier); + grow_width = seat->op_ref_width * max_multiplier; + grow_height = seat->op_ref_height * max_multiplier; } // Determine new width/height, and accommodate for min/max values From 350e9ea9293a4c185734ecda9d77ee2fd13502d2 Mon Sep 17 00:00:00 2001 From: Ryan Dwyer Date: Thu, 19 Jul 2018 15:20:43 +1000 Subject: [PATCH 007/148] Fix clicking xwayland menus --- sway/input/cursor.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/sway/input/cursor.c b/sway/input/cursor.c index a24a7de8..7a06cf9c 100644 --- a/sway/input/cursor.c +++ b/sway/input/cursor.c @@ -569,6 +569,9 @@ void dispatch_cursor_button(struct sway_cursor *cursor, seat_set_focus(cursor->seat, cont); wlr_seat_pointer_notify_button(cursor->seat->wlr_seat, time_msec, button, state); + } else { + wlr_seat_pointer_notify_button(cursor->seat->wlr_seat, + time_msec, button, state); } transaction_commit_dirty(); From 31f91bd483797feb411077da0e351ccfae9ecc10 Mon Sep 17 00:00:00 2001 From: Ryan Dwyer Date: Thu, 19 Jul 2018 15:37:09 +1000 Subject: [PATCH 008/148] Improve resize performance by partially flushing the transaction queue When interactively resizing some views (eg. Nautilus), new transactions are added to the queue faster than the client can process them. Previously, we would wait for the entire queue to be ready before applying any of them, but in this case the transactions would time out, giving the client choppy performance. This changes the queue handling so it applies the transactions up to the first waiting transaction, without waiting for the entire queue to be ready. --- sway/desktop/transaction.c | 14 +++----------- 1 file changed, 3 insertions(+), 11 deletions(-) diff --git a/sway/desktop/transaction.c b/sway/desktop/transaction.c index 19f41efc..2a89880a 100644 --- a/sway/desktop/transaction.c +++ b/sway/desktop/transaction.c @@ -222,24 +222,16 @@ static void transaction_apply(struct sway_transaction *transaction) { } } -/** - * For simplicity, we only progress the queue if it can be completely flushed. - */ static void transaction_progress_queue() { - // We iterate this list in reverse because we're more likely to find a - // waiting transactions at the end of the list. - for (int i = server.transactions->length - 1; i >= 0; --i) { - struct sway_transaction *transaction = server.transactions->items[i]; + while (server.transactions->length) { + struct sway_transaction *transaction = server.transactions->items[0]; if (transaction->num_waiting) { return; } - } - for (int i = 0; i < server.transactions->length; ++i) { - struct sway_transaction *transaction = server.transactions->items[i]; transaction_apply(transaction); transaction_destroy(transaction); + list_del(server.transactions, 0); } - server.transactions->length = 0; idle_inhibit_v1_check_active(server.idle_inhibit_manager_v1); } From dd337127d80ce93ba556691c4a4565a19babba0e Mon Sep 17 00:00:00 2001 From: Ryan Dwyer Date: Thu, 19 Jul 2018 21:29:21 +1000 Subject: [PATCH 009/148] Use separate function for choosing edge cursor --- sway/input/cursor.c | 59 +++++++++++++++++++++------------------------ 1 file changed, 28 insertions(+), 31 deletions(-) diff --git a/sway/input/cursor.c b/sway/input/cursor.c index 7a06cf9c..dfb5603e 100644 --- a/sway/input/cursor.c +++ b/sway/input/cursor.c @@ -299,6 +299,31 @@ static void handle_resize_motion(struct sway_seat *seat, transaction_commit_dirty(); } +static const char *edge_to_image_name(enum resize_edge edge) { + switch (edge) { + case RESIZE_EDGE_NONE: + return "left_ptr"; + case RESIZE_EDGE_TOP: + return "top_side"; + case RESIZE_EDGE_RIGHT: + return "right_side"; + case RESIZE_EDGE_BOTTOM: + return "bottom_side"; + case RESIZE_EDGE_LEFT: + return "left_side"; + } + if (edge == (RESIZE_EDGE_TOP | RESIZE_EDGE_LEFT)) { + return "top_left_corner"; + } else if (edge == (RESIZE_EDGE_TOP | RESIZE_EDGE_RIGHT)) { + return "top_right_corner"; + } else if (edge == (RESIZE_EDGE_BOTTOM | RESIZE_EDGE_LEFT)) { + return "bottom_left_corner"; + } else if (edge == (RESIZE_EDGE_BOTTOM | RESIZE_EDGE_RIGHT)) { + return "bottom_right_corner"; + } + return "left_ptr"; +} + void cursor_send_pointer_motion(struct sway_cursor *cursor, uint32_t time_msec, bool allow_refocusing) { if (time_msec == 0) { @@ -378,37 +403,9 @@ void cursor_send_pointer_motion(struct sway_cursor *cursor, uint32_t time_msec, } else if (c && container_is_floating(c)) { // Try a floating container's resize edge enum resize_edge edge = find_resize_edge(c, cursor); - if (edge == RESIZE_EDGE_NONE) { - wlr_xcursor_manager_set_cursor_image(cursor->xcursor_manager, - "left_ptr", cursor->cursor); - } else if (edge == RESIZE_EDGE_TOP) { - wlr_xcursor_manager_set_cursor_image(cursor->xcursor_manager, - "top_side", cursor->cursor); - } else if (edge == RESIZE_EDGE_RIGHT) { - wlr_xcursor_manager_set_cursor_image(cursor->xcursor_manager, - "right_side", cursor->cursor); - } else if (edge == RESIZE_EDGE_BOTTOM) { - wlr_xcursor_manager_set_cursor_image(cursor->xcursor_manager, - "bottom_side", cursor->cursor); - } else if (edge == RESIZE_EDGE_LEFT) { - wlr_xcursor_manager_set_cursor_image(cursor->xcursor_manager, - "left_side", cursor->cursor); - } else if (edge == (RESIZE_EDGE_TOP | RESIZE_EDGE_LEFT)) { - wlr_xcursor_manager_set_cursor_image(cursor->xcursor_manager, - "top_left_corner", cursor->cursor); - } else if (edge == (RESIZE_EDGE_TOP | RESIZE_EDGE_RIGHT)) { - wlr_xcursor_manager_set_cursor_image(cursor->xcursor_manager, - "top_right_corner", cursor->cursor); - } else if (edge == (RESIZE_EDGE_BOTTOM | RESIZE_EDGE_LEFT)) { - wlr_xcursor_manager_set_cursor_image(cursor->xcursor_manager, - "bottom_left_corner", cursor->cursor); - } else if (edge == (RESIZE_EDGE_BOTTOM | RESIZE_EDGE_RIGHT)) { - wlr_xcursor_manager_set_cursor_image(cursor->xcursor_manager, - "bottom_right_corner", cursor->cursor); - } else { - wlr_xcursor_manager_set_cursor_image(cursor->xcursor_manager, - "left_ptr", cursor->cursor); - } + const char *image = edge_to_image_name(edge); + wlr_xcursor_manager_set_cursor_image(cursor->xcursor_manager, image, + cursor->cursor); cursor->image_client = NULL; } else { wlr_xcursor_manager_set_cursor_image(cursor->xcursor_manager, From 1b3b75a09499972f6fdfb43185f0c9f84c16791e Mon Sep 17 00:00:00 2001 From: Ryan Dwyer Date: Thu, 19 Jul 2018 21:31:10 +1000 Subject: [PATCH 010/148] Use wlr_keyboard_get_modifiers --- sway/input/cursor.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/sway/input/cursor.c b/sway/input/cursor.c index dfb5603e..8723e2b4 100644 --- a/sway/input/cursor.c +++ b/sway/input/cursor.c @@ -480,7 +480,7 @@ static void dispatch_cursor_button_floating(struct sway_cursor *cursor, struct wlr_keyboard *keyboard = wlr_seat_get_keyboard(seat->wlr_seat); bool mod_pressed = keyboard && - (keyboard->modifiers.depressed & config->floating_mod); + (wlr_keyboard_get_modifiers(keyboard) & config->floating_mod); enum resize_edge edge = find_resize_edge(cont, cursor); bool over_title = edge == RESIZE_EDGE_NONE && !surface; @@ -501,7 +501,7 @@ static void dispatch_cursor_button_floating(struct sway_cursor *cursor, seat->operation = OP_RESIZE; seat->op_container = cont; seat->op_resize_preserve_ratio = keyboard && - (keyboard->modifiers.depressed & WLR_MODIFIER_SHIFT); + (wlr_keyboard_get_modifiers(keyboard) & WLR_MODIFIER_SHIFT); seat->op_resize_edge = resizing_via_mod ? RESIZE_EDGE_BOTTOM | RESIZE_EDGE_RIGHT : edge; seat->op_button = button; From ff445cc85597ee6bfae01f03d3c246e2326f3981 Mon Sep 17 00:00:00 2001 From: Ryan Dwyer Date: Fri, 20 Jul 2018 09:28:22 +1000 Subject: [PATCH 011/148] Implement xdg shell request_move and request_resize events Also does a few other related things: * Now uses enum wlr_edges instead of our own enum resize_edge * Now uses wlr_xcursor_get_resize_name and removes our own find_resize_edge_name * Renames drag to move for consistency --- include/sway/input/seat.h | 13 +++-- sway/desktop/xdg_shell.c | 33 ++++++++++++ sway/desktop/xdg_shell_v6.c | 33 ++++++++++++ sway/input/cursor.c | 100 +++++++++++------------------------- sway/input/seat.c | 38 ++++++++++++++ 5 files changed, 144 insertions(+), 73 deletions(-) diff --git a/include/sway/input/seat.h b/include/sway/input/seat.h index be1f3610..35a965ee 100644 --- a/include/sway/input/seat.h +++ b/include/sway/input/seat.h @@ -3,6 +3,7 @@ #include #include +#include #include "sway/input/input-manager.h" struct sway_seat_device { @@ -34,8 +35,6 @@ struct sway_drag_icon { struct wl_listener destroy; }; -enum resize_edge; - struct sway_seat { struct wlr_seat *wlr_seat; struct sway_cursor *cursor; @@ -57,11 +56,12 @@ struct sway_seat { // Operations (drag and resize) enum { OP_NONE, - OP_DRAG, + OP_MOVE, OP_RESIZE, } operation; + struct sway_container *op_container; - enum resize_edge op_resize_edge; + enum wlr_edges op_resize_edge; uint32_t op_button; bool op_resize_preserve_ratio; double op_ref_lx, op_ref_ly; // cursor's x/y at start of op @@ -150,4 +150,9 @@ bool seat_is_input_allowed(struct sway_seat *seat, struct wlr_surface *surface); void drag_icon_update_position(struct sway_drag_icon *icon); +void seat_begin_move(struct sway_seat *seat, struct sway_container *con); + +void seat_begin_resize(struct sway_seat *seat, struct sway_container *con, + uint32_t button, enum wlr_edges edge); + #endif diff --git a/sway/desktop/xdg_shell.c b/sway/desktop/xdg_shell.c index 98c16faf..d6c3a9a7 100644 --- a/sway/desktop/xdg_shell.c +++ b/sway/desktop/xdg_shell.c @@ -1,4 +1,9 @@ #define _POSIX_C_SOURCE 199309L +#ifdef __linux__ +#include +#elif __FreeBSD__ +#include +#endif #include #include #include @@ -248,6 +253,24 @@ static void handle_request_fullscreen(struct wl_listener *listener, void *data) transaction_commit_dirty(); } +static void handle_request_move(struct wl_listener *listener, void *data) { + struct sway_xdg_shell_view *xdg_shell_view = + wl_container_of(listener, xdg_shell_view, request_move); + struct sway_view *view = &xdg_shell_view->view; + struct wlr_xdg_toplevel_move_event *e = data; + struct sway_seat *seat = e->seat->seat->data; + seat_begin_move(seat, view->swayc); +} + +static void handle_request_resize(struct wl_listener *listener, void *data) { + struct sway_xdg_shell_view *xdg_shell_view = + wl_container_of(listener, xdg_shell_view, request_resize); + struct sway_view *view = &xdg_shell_view->view; + struct wlr_xdg_toplevel_resize_event *e = data; + struct sway_seat *seat = e->seat->seat->data; + seat_begin_resize(seat, view->swayc, BTN_LEFT, e->edges); +} + static void handle_unmap(struct wl_listener *listener, void *data) { struct sway_xdg_shell_view *xdg_shell_view = wl_container_of(listener, xdg_shell_view, unmap); @@ -262,6 +285,8 @@ static void handle_unmap(struct wl_listener *listener, void *data) { wl_list_remove(&xdg_shell_view->commit.link); wl_list_remove(&xdg_shell_view->new_popup.link); wl_list_remove(&xdg_shell_view->request_fullscreen.link); + wl_list_remove(&xdg_shell_view->request_move.link); + wl_list_remove(&xdg_shell_view->request_resize.link); } static void handle_map(struct wl_listener *listener, void *data) { @@ -299,6 +324,14 @@ static void handle_map(struct wl_listener *listener, void *data) { xdg_shell_view->request_fullscreen.notify = handle_request_fullscreen; wl_signal_add(&xdg_surface->toplevel->events.request_fullscreen, &xdg_shell_view->request_fullscreen); + + xdg_shell_view->request_move.notify = handle_request_move; + wl_signal_add(&xdg_surface->toplevel->events.request_move, + &xdg_shell_view->request_move); + + xdg_shell_view->request_resize.notify = handle_request_resize; + wl_signal_add(&xdg_surface->toplevel->events.request_resize, + &xdg_shell_view->request_resize); } static void handle_destroy(struct wl_listener *listener, void *data) { diff --git a/sway/desktop/xdg_shell_v6.c b/sway/desktop/xdg_shell_v6.c index 4d76f0a7..241bd9b0 100644 --- a/sway/desktop/xdg_shell_v6.c +++ b/sway/desktop/xdg_shell_v6.c @@ -1,4 +1,9 @@ #define _POSIX_C_SOURCE 199309L +#ifdef __linux__ +#include +#elif __FreeBSD__ +#include +#endif #include #include #include @@ -243,6 +248,24 @@ static void handle_request_fullscreen(struct wl_listener *listener, void *data) transaction_commit_dirty(); } +static void handle_request_move(struct wl_listener *listener, void *data) { + struct sway_xdg_shell_v6_view *xdg_shell_v6_view = + wl_container_of(listener, xdg_shell_v6_view, request_move); + struct sway_view *view = &xdg_shell_v6_view->view; + struct wlr_xdg_toplevel_v6_move_event *e = data; + struct sway_seat *seat = e->seat->seat->data; + seat_begin_move(seat, view->swayc); +} + +static void handle_request_resize(struct wl_listener *listener, void *data) { + struct sway_xdg_shell_v6_view *xdg_shell_v6_view = + wl_container_of(listener, xdg_shell_v6_view, request_resize); + struct sway_view *view = &xdg_shell_v6_view->view; + struct wlr_xdg_toplevel_v6_resize_event *e = data; + struct sway_seat *seat = e->seat->seat->data; + seat_begin_resize(seat, view->swayc, BTN_LEFT, e->edges); +} + static void handle_unmap(struct wl_listener *listener, void *data) { struct sway_xdg_shell_v6_view *xdg_shell_v6_view = wl_container_of(listener, xdg_shell_v6_view, unmap); @@ -257,6 +280,8 @@ static void handle_unmap(struct wl_listener *listener, void *data) { wl_list_remove(&xdg_shell_v6_view->commit.link); wl_list_remove(&xdg_shell_v6_view->new_popup.link); wl_list_remove(&xdg_shell_v6_view->request_fullscreen.link); + wl_list_remove(&xdg_shell_v6_view->request_move.link); + wl_list_remove(&xdg_shell_v6_view->request_resize.link); } static void handle_map(struct wl_listener *listener, void *data) { @@ -294,6 +319,14 @@ static void handle_map(struct wl_listener *listener, void *data) { xdg_shell_v6_view->request_fullscreen.notify = handle_request_fullscreen; wl_signal_add(&xdg_surface->toplevel->events.request_fullscreen, &xdg_shell_v6_view->request_fullscreen); + + xdg_shell_v6_view->request_move.notify = handle_request_move; + wl_signal_add(&xdg_surface->toplevel->events.request_move, + &xdg_shell_v6_view->request_move); + + xdg_shell_v6_view->request_resize.notify = handle_request_resize; + wl_signal_add(&xdg_surface->toplevel->events.request_resize, + &xdg_shell_v6_view->request_resize); } static void handle_destroy(struct wl_listener *listener, void *data) { diff --git a/sway/input/cursor.c b/sway/input/cursor.c index 8723e2b4..8b9208c6 100644 --- a/sway/input/cursor.c +++ b/sway/input/cursor.c @@ -143,33 +143,33 @@ static struct sway_container *container_at_coords( return output->swayc; } -static enum resize_edge find_resize_edge(struct sway_container *cont, +static enum wlr_edges find_resize_edge(struct sway_container *cont, struct sway_cursor *cursor) { if (cont->type != C_VIEW) { - return RESIZE_EDGE_NONE; + return WLR_EDGE_NONE; } struct sway_view *view = cont->sway_view; if (view->border == B_NONE || !view->border_thickness || view->using_csd) { - return RESIZE_EDGE_NONE; + return WLR_EDGE_NONE; } - enum resize_edge edge = 0; + enum wlr_edges edge = 0; if (cursor->cursor->x < cont->x + view->border_thickness) { - edge |= RESIZE_EDGE_LEFT; + edge |= WLR_EDGE_LEFT; } if (cursor->cursor->y < cont->y + view->border_thickness) { - edge |= RESIZE_EDGE_TOP; + edge |= WLR_EDGE_TOP; } if (cursor->cursor->x >= cont->x + cont->width - view->border_thickness) { - edge |= RESIZE_EDGE_RIGHT; + edge |= WLR_EDGE_RIGHT; } if (cursor->cursor->y >= cont->y + cont->height - view->border_thickness) { - edge |= RESIZE_EDGE_BOTTOM; + edge |= WLR_EDGE_BOTTOM; } return edge; } -static void handle_drag_motion(struct sway_seat *seat, +static void handle_move_motion(struct sway_seat *seat, struct sway_cursor *cursor) { struct sway_container *con = seat->op_container; desktop_damage_whole_container(con); @@ -218,22 +218,22 @@ static void calculate_floating_constraints(struct sway_container *con, static void handle_resize_motion(struct sway_seat *seat, struct sway_cursor *cursor) { struct sway_container *con = seat->op_container; - enum resize_edge edge = seat->op_resize_edge; + enum wlr_edges edge = seat->op_resize_edge; // The amount the mouse has moved since the start of the resize operation // Positive is down/right double mouse_move_x = cursor->cursor->x - seat->op_ref_lx; double mouse_move_y = cursor->cursor->y - seat->op_ref_ly; - if (edge == RESIZE_EDGE_TOP || edge == RESIZE_EDGE_BOTTOM) { + if (edge == WLR_EDGE_TOP || edge == WLR_EDGE_BOTTOM) { mouse_move_x = 0; } - if (edge == RESIZE_EDGE_LEFT || edge == RESIZE_EDGE_RIGHT) { + if (edge == WLR_EDGE_LEFT || edge == WLR_EDGE_RIGHT) { mouse_move_y = 0; } - double grow_width = edge & RESIZE_EDGE_LEFT ? -mouse_move_x : mouse_move_x; - double grow_height = edge & RESIZE_EDGE_TOP ? -mouse_move_y : mouse_move_y; + double grow_width = edge & WLR_EDGE_LEFT ? -mouse_move_x : mouse_move_x; + double grow_height = edge & WLR_EDGE_TOP ? -mouse_move_y : mouse_move_y; if (seat->op_resize_preserve_ratio) { double x_multiplier = grow_width / seat->op_ref_width; @@ -259,16 +259,16 @@ static void handle_resize_motion(struct sway_seat *seat, // Determine grow x/y values - these are relative to the container's x/y at // the start of the resize operation. double grow_x = 0, grow_y = 0; - if (edge & RESIZE_EDGE_LEFT) { + if (edge & WLR_EDGE_LEFT) { grow_x = -grow_width; - } else if (edge & RESIZE_EDGE_RIGHT) { + } else if (edge & WLR_EDGE_RIGHT) { grow_x = 0; } else { grow_x = -grow_width / 2; } - if (edge & RESIZE_EDGE_TOP) { + if (edge & WLR_EDGE_TOP) { grow_y = -grow_height; - } else if (edge & RESIZE_EDGE_BOTTOM) { + } else if (edge & WLR_EDGE_BOTTOM) { grow_y = 0; } else { grow_y = -grow_height / 2; @@ -299,31 +299,6 @@ static void handle_resize_motion(struct sway_seat *seat, transaction_commit_dirty(); } -static const char *edge_to_image_name(enum resize_edge edge) { - switch (edge) { - case RESIZE_EDGE_NONE: - return "left_ptr"; - case RESIZE_EDGE_TOP: - return "top_side"; - case RESIZE_EDGE_RIGHT: - return "right_side"; - case RESIZE_EDGE_BOTTOM: - return "bottom_side"; - case RESIZE_EDGE_LEFT: - return "left_side"; - } - if (edge == (RESIZE_EDGE_TOP | RESIZE_EDGE_LEFT)) { - return "top_left_corner"; - } else if (edge == (RESIZE_EDGE_TOP | RESIZE_EDGE_RIGHT)) { - return "top_right_corner"; - } else if (edge == (RESIZE_EDGE_BOTTOM | RESIZE_EDGE_LEFT)) { - return "bottom_left_corner"; - } else if (edge == (RESIZE_EDGE_BOTTOM | RESIZE_EDGE_RIGHT)) { - return "bottom_right_corner"; - } - return "left_ptr"; -} - void cursor_send_pointer_motion(struct sway_cursor *cursor, uint32_t time_msec, bool allow_refocusing) { if (time_msec == 0) { @@ -333,8 +308,8 @@ void cursor_send_pointer_motion(struct sway_cursor *cursor, uint32_t time_msec, struct sway_seat *seat = cursor->seat; if (seat->operation != OP_NONE) { - if (seat->operation == OP_DRAG) { - handle_drag_motion(seat, cursor); + if (seat->operation == OP_MOVE) { + handle_move_motion(seat, cursor); } else { handle_resize_motion(seat, cursor); } @@ -402,8 +377,9 @@ void cursor_send_pointer_motion(struct sway_cursor *cursor, uint32_t time_msec, } } else if (c && container_is_floating(c)) { // Try a floating container's resize edge - enum resize_edge edge = find_resize_edge(c, cursor); - const char *image = edge_to_image_name(edge); + enum wlr_edges edge = find_resize_edge(c, cursor); + const char *image = edge == WLR_EDGE_NONE ? + "left_ptr" : wlr_xcursor_get_resize_name(edge); wlr_xcursor_manager_set_cursor_image(cursor->xcursor_manager, image, cursor->cursor); cursor->image_client = NULL; @@ -452,7 +428,7 @@ static void handle_cursor_motion_absolute( } static void handle_end_operation(struct sway_seat *seat) { - if (seat->operation == OP_DRAG) { + if (seat->operation == OP_MOVE) { // We "move" the container to its own location so it discovers its // output again. struct sway_container *con = seat->op_container; @@ -472,7 +448,7 @@ static void dispatch_cursor_button_floating(struct sway_cursor *cursor, struct sway_container *cont) { struct sway_seat *seat = cursor->seat; - // Deny dragging or resizing a fullscreen view + // Deny moving or resizing a fullscreen view if (cont->type == C_VIEW && cont->sway_view->is_fullscreen) { wlr_seat_pointer_notify_button(seat->wlr_seat, time_msec, button, state); return; @@ -481,36 +457,22 @@ static void dispatch_cursor_button_floating(struct sway_cursor *cursor, struct wlr_keyboard *keyboard = wlr_seat_get_keyboard(seat->wlr_seat); bool mod_pressed = keyboard && (wlr_keyboard_get_modifiers(keyboard) & config->floating_mod); - enum resize_edge edge = find_resize_edge(cont, cursor); - bool over_title = edge == RESIZE_EDGE_NONE && !surface; + enum wlr_edges edge = find_resize_edge(cont, cursor); + bool over_title = edge == WLR_EDGE_NONE && !surface; - // Check for beginning drag + // Check for beginning move if (button == BTN_LEFT && state == WLR_BUTTON_PRESSED && (mod_pressed || over_title)) { - seat->operation = OP_DRAG; - seat->op_container = cont; - seat->op_button = button; + seat_begin_move(seat, cont); return; } // Check for beginning resize - bool resizing_via_border = button == BTN_LEFT && edge != RESIZE_EDGE_NONE; + bool resizing_via_border = button == BTN_LEFT && edge != WLR_EDGE_NONE; bool resizing_via_mod = button == BTN_RIGHT && mod_pressed; if ((resizing_via_border || resizing_via_mod) && state == WLR_BUTTON_PRESSED) { - seat->operation = OP_RESIZE; - seat->op_container = cont; - seat->op_resize_preserve_ratio = keyboard && - (wlr_keyboard_get_modifiers(keyboard) & WLR_MODIFIER_SHIFT); - seat->op_resize_edge = resizing_via_mod ? - RESIZE_EDGE_BOTTOM | RESIZE_EDGE_RIGHT : edge; - seat->op_button = button; - seat->op_ref_lx = cursor->cursor->x; - seat->op_ref_ly = cursor->cursor->y; - seat->op_ref_con_lx = cont->x; - seat->op_ref_con_ly = cont->y; - seat->op_ref_width = cont->width; - seat->op_ref_height = cont->height; + seat_begin_resize(seat, cont, button, edge); return; } diff --git a/sway/input/seat.c b/sway/input/seat.c index e77d88a8..cc5b2e0f 100644 --- a/sway/input/seat.c +++ b/sway/input/seat.c @@ -1,6 +1,11 @@ #define _XOPEN_SOURCE 700 #define _POSIX_C_SOURCE 199309L #include +#ifdef __linux__ +#include +#elif __FreeBSD__ +#include +#endif #include #include #include @@ -348,6 +353,7 @@ struct sway_seat *seat_create(struct sway_input_manager *input, free(seat); return NULL; } + seat->wlr_seat->data = seat; seat->cursor = sway_cursor_create(seat); if (!seat->cursor) { @@ -894,3 +900,35 @@ struct seat_config *seat_get_config(struct sway_seat *seat) { return NULL; } + +void seat_begin_move(struct sway_seat *seat, struct sway_container *con) { + if (!seat->cursor) { + wlr_log(WLR_DEBUG, "Ignoring move request due to no cursor device"); + return; + } + seat->operation = OP_MOVE; + seat->op_container = con; + seat->op_button = BTN_LEFT; +} + +void seat_begin_resize(struct sway_seat *seat, struct sway_container *con, + uint32_t button, enum wlr_edges edge) { + if (!seat->cursor) { + wlr_log(WLR_DEBUG, "Ignoring resize request due to no cursor device"); + return; + } + struct wlr_keyboard *keyboard = wlr_seat_get_keyboard(seat->wlr_seat); + seat->operation = OP_RESIZE; + seat->op_container = con; + seat->op_resize_preserve_ratio = keyboard && + (wlr_keyboard_get_modifiers(keyboard) & WLR_MODIFIER_SHIFT); + seat->op_resize_edge = edge == WLR_EDGE_NONE ? + RESIZE_EDGE_BOTTOM | RESIZE_EDGE_RIGHT : edge; + seat->op_button = button; + seat->op_ref_lx = seat->cursor->cursor->x; + seat->op_ref_ly = seat->cursor->cursor->y; + seat->op_ref_con_lx = con->x; + seat->op_ref_con_ly = con->y; + seat->op_ref_width = con->width; + seat->op_ref_height = con->height; +} From 86f55315113556eaa58f8b06231a89d67b1201ba Mon Sep 17 00:00:00 2001 From: Ryan Dwyer Date: Sat, 21 Jul 2018 10:35:16 +1000 Subject: [PATCH 012/148] Remove unnecessary includes --- sway/commands/floating_modifier.c | 10 ---------- 1 file changed, 10 deletions(-) diff --git a/sway/commands/floating_modifier.c b/sway/commands/floating_modifier.c index 1ced50af..9432c9f1 100644 --- a/sway/commands/floating_modifier.c +++ b/sway/commands/floating_modifier.c @@ -1,15 +1,5 @@ -#ifdef __linux__ -#include -#elif __FreeBSD__ -#include -#endif -#include -#include -#include #include "sway/commands.h" #include "sway/config.h" -#include "list.h" -#include "log.h" #include "util.h" struct cmd_results *cmd_floating_modifier(int argc, char **argv) { From 0c87bff5d1ce686afdc25d37eee4bdd8f08abdd3 Mon Sep 17 00:00:00 2001 From: Ryan Dwyer Date: Sat, 21 Jul 2018 10:40:12 +1000 Subject: [PATCH 013/148] Replace static handle_end_operation with seat_end_mouse_operation --- include/sway/input/seat.h | 2 ++ sway/input/cursor.c | 17 +---------------- sway/input/seat.c | 19 +++++++++++++++++++ sway/tree/container.c | 3 +-- 4 files changed, 23 insertions(+), 18 deletions(-) diff --git a/include/sway/input/seat.h b/include/sway/input/seat.h index 35a965ee..cd36ef5a 100644 --- a/include/sway/input/seat.h +++ b/include/sway/input/seat.h @@ -155,4 +155,6 @@ void seat_begin_move(struct sway_seat *seat, struct sway_container *con); void seat_begin_resize(struct sway_seat *seat, struct sway_container *con, uint32_t button, enum wlr_edges edge); +void seat_end_mouse_operation(struct sway_seat *seat); + #endif diff --git a/sway/input/cursor.c b/sway/input/cursor.c index 8b9208c6..ec83746e 100644 --- a/sway/input/cursor.c +++ b/sway/input/cursor.c @@ -427,21 +427,6 @@ static void handle_cursor_motion_absolute( transaction_commit_dirty(); } -static void handle_end_operation(struct sway_seat *seat) { - if (seat->operation == OP_MOVE) { - // We "move" the container to its own location so it discovers its - // output again. - struct sway_container *con = seat->op_container; - container_floating_move_to(con, con->x, con->y); - seat->operation = OP_NONE; - seat->op_container = NULL; - } else { - // OP_RESIZE - seat->operation = OP_NONE; - seat->op_container = NULL; - } -} - static void dispatch_cursor_button_floating(struct sway_cursor *cursor, uint32_t time_msec, uint32_t button, enum wlr_button_state state, struct wlr_surface *surface, double sx, double sy, @@ -484,7 +469,7 @@ void dispatch_cursor_button(struct sway_cursor *cursor, uint32_t time_msec, uint32_t button, enum wlr_button_state state) { if (cursor->seat->operation != OP_NONE && button == cursor->seat->op_button && state == WLR_BUTTON_RELEASED) { - handle_end_operation(cursor->seat); + seat_end_mouse_operation(cursor->seat); return; } if (time_msec == 0) { diff --git a/sway/input/seat.c b/sway/input/seat.c index cc5b2e0f..3a3350e1 100644 --- a/sway/input/seat.c +++ b/sway/input/seat.c @@ -932,3 +932,22 @@ void seat_begin_resize(struct sway_seat *seat, struct sway_container *con, seat->op_ref_width = con->width; seat->op_ref_height = con->height; } + +void seat_end_mouse_operation(struct sway_seat *seat) { + switch (seat->operation) { + case OP_MOVE: + { + // We "move" the container to its own location so it discovers its + // output again. + struct sway_container *con = seat->op_container; + container_floating_move_to(con, con->x, con->y); + } + case OP_RESIZE: + // Don't need to do anything here. + break; + case OP_NONE: + break; + } + seat->operation = OP_NONE; + seat->op_container = NULL; +} diff --git a/sway/tree/container.c b/sway/tree/container.c index ba4af352..42c1d024 100644 --- a/sway/tree/container.c +++ b/sway/tree/container.c @@ -1114,8 +1114,7 @@ void container_end_mouse_operation(struct sway_container *container) { struct sway_seat *seat; wl_list_for_each(seat, &input_manager->seats, link) { if (seat->op_container == container) { - seat->op_container = NULL; - seat->operation = OP_NONE; + seat_end_mouse_operation(seat); } } } From 6767d8a593723a9b69d018eed13d24a789be9516 Mon Sep 17 00:00:00 2001 From: Ryan Dwyer Date: Sat, 21 Jul 2018 10:57:14 +1000 Subject: [PATCH 014/148] Prevent re-uploading the same cursor image multiple times --- include/sway/input/cursor.h | 4 ++++ sway/input/cursor.c | 22 +++++++++++++--------- 2 files changed, 17 insertions(+), 9 deletions(-) diff --git a/include/sway/input/cursor.h b/include/sway/input/cursor.h index 5dd109ca..b0a3a7c5 100644 --- a/include/sway/input/cursor.h +++ b/include/sway/input/cursor.h @@ -11,6 +11,7 @@ struct sway_cursor { } previous; struct wlr_xcursor_manager *xcursor_manager; + const char *image; struct wl_client *image_client; struct wl_listener motion; @@ -37,4 +38,7 @@ void cursor_send_pointer_motion(struct sway_cursor *cursor, uint32_t time_msec, void dispatch_cursor_button(struct sway_cursor *cursor, uint32_t time_msec, uint32_t button, enum wlr_button_state state); +void cursor_set_image(struct sway_cursor *cursor, const char *image, + struct wl_client *client); + #endif diff --git a/sway/input/cursor.c b/sway/input/cursor.c index ec83746e..43721d28 100644 --- a/sway/input/cursor.c +++ b/sway/input/cursor.c @@ -371,22 +371,16 @@ void cursor_send_pointer_motion(struct sway_cursor *cursor, uint32_t time_msec, // Reset cursor if switching between clients struct wl_client *client = wl_resource_get_client(surface->resource); if (client != cursor->image_client) { - wlr_xcursor_manager_set_cursor_image(cursor->xcursor_manager, - "left_ptr", cursor->cursor); - cursor->image_client = client; + cursor_set_image(cursor, "left_ptr", client); } } else if (c && container_is_floating(c)) { // Try a floating container's resize edge enum wlr_edges edge = find_resize_edge(c, cursor); const char *image = edge == WLR_EDGE_NONE ? "left_ptr" : wlr_xcursor_get_resize_name(edge); - wlr_xcursor_manager_set_cursor_image(cursor->xcursor_manager, image, - cursor->cursor); - cursor->image_client = NULL; + cursor_set_image(cursor, image, NULL); } else { - wlr_xcursor_manager_set_cursor_image(cursor->xcursor_manager, - "left_ptr", cursor->cursor); - cursor->image_client = NULL; + cursor_set_image(cursor, "left_ptr", NULL); } // send pointer enter/leave @@ -725,6 +719,16 @@ static void handle_request_set_cursor(struct wl_listener *listener, cursor->image_client = focused_client; } +void cursor_set_image(struct sway_cursor *cursor, const char *image, + struct wl_client *client) { + if (!cursor->image || strcmp(cursor->image, image) != 0) { + wlr_xcursor_manager_set_cursor_image(cursor->xcursor_manager, image, + cursor->cursor); + cursor->image = image; + } + cursor->image_client = client; +} + void sway_cursor_destroy(struct sway_cursor *cursor) { if (!cursor) { return; From 9df660ee3188386c907d8feb999636ce8d61d095 Mon Sep 17 00:00:00 2001 From: Ryan Dwyer Date: Sat, 21 Jul 2018 11:23:48 +1000 Subject: [PATCH 015/148] Store last button and use it when views request to move or resize --- include/sway/input/seat.h | 9 ++++++++- sway/desktop/xdg_shell.c | 13 ++++++------- sway/desktop/xdg_shell_v6.c | 13 ++++++------- sway/input/cursor.c | 18 +++++++----------- sway/input/seat.c | 12 ++++++++++-- 5 files changed, 37 insertions(+), 28 deletions(-) diff --git a/include/sway/input/seat.h b/include/sway/input/seat.h index cd36ef5a..ab25788f 100644 --- a/include/sway/input/seat.h +++ b/include/sway/input/seat.h @@ -68,6 +68,9 @@ struct sway_seat { double op_ref_width, op_ref_height; // container's size at start of op double op_ref_con_lx, op_ref_con_ly; // container's x/y at start of op + uint32_t last_button; + uint32_t last_button_serial; + struct wl_listener focus_destroy; struct wl_listener new_container; struct wl_listener new_drag_icon; @@ -150,11 +153,15 @@ bool seat_is_input_allowed(struct sway_seat *seat, struct wlr_surface *surface); void drag_icon_update_position(struct sway_drag_icon *icon); -void seat_begin_move(struct sway_seat *seat, struct sway_container *con); +void seat_begin_move(struct sway_seat *seat, struct sway_container *con, + uint32_t button); void seat_begin_resize(struct sway_seat *seat, struct sway_container *con, uint32_t button, enum wlr_edges edge); void seat_end_mouse_operation(struct sway_seat *seat); +void seat_pointer_notify_button(struct sway_seat *seat, uint32_t time_msec, + uint32_t button, enum wlr_button_state state); + #endif diff --git a/sway/desktop/xdg_shell.c b/sway/desktop/xdg_shell.c index d6c3a9a7..c5d53d1d 100644 --- a/sway/desktop/xdg_shell.c +++ b/sway/desktop/xdg_shell.c @@ -1,9 +1,4 @@ #define _POSIX_C_SOURCE 199309L -#ifdef __linux__ -#include -#elif __FreeBSD__ -#include -#endif #include #include #include @@ -259,7 +254,9 @@ static void handle_request_move(struct wl_listener *listener, void *data) { struct sway_view *view = &xdg_shell_view->view; struct wlr_xdg_toplevel_move_event *e = data; struct sway_seat *seat = e->seat->seat->data; - seat_begin_move(seat, view->swayc); + if (e->serial == seat->last_button_serial) { + seat_begin_move(seat, view->swayc, seat->last_button); + } } static void handle_request_resize(struct wl_listener *listener, void *data) { @@ -268,7 +265,9 @@ static void handle_request_resize(struct wl_listener *listener, void *data) { struct sway_view *view = &xdg_shell_view->view; struct wlr_xdg_toplevel_resize_event *e = data; struct sway_seat *seat = e->seat->seat->data; - seat_begin_resize(seat, view->swayc, BTN_LEFT, e->edges); + if (e->serial == seat->last_button_serial) { + seat_begin_resize(seat, view->swayc, seat->last_button, e->edges); + } } static void handle_unmap(struct wl_listener *listener, void *data) { diff --git a/sway/desktop/xdg_shell_v6.c b/sway/desktop/xdg_shell_v6.c index 241bd9b0..4bd6af5e 100644 --- a/sway/desktop/xdg_shell_v6.c +++ b/sway/desktop/xdg_shell_v6.c @@ -1,9 +1,4 @@ #define _POSIX_C_SOURCE 199309L -#ifdef __linux__ -#include -#elif __FreeBSD__ -#include -#endif #include #include #include @@ -254,7 +249,9 @@ static void handle_request_move(struct wl_listener *listener, void *data) { struct sway_view *view = &xdg_shell_v6_view->view; struct wlr_xdg_toplevel_v6_move_event *e = data; struct sway_seat *seat = e->seat->seat->data; - seat_begin_move(seat, view->swayc); + if (e->serial == seat->last_button_serial) { + seat_begin_move(seat, view->swayc, seat->last_button); + } } static void handle_request_resize(struct wl_listener *listener, void *data) { @@ -263,7 +260,9 @@ static void handle_request_resize(struct wl_listener *listener, void *data) { struct sway_view *view = &xdg_shell_v6_view->view; struct wlr_xdg_toplevel_v6_resize_event *e = data; struct sway_seat *seat = e->seat->seat->data; - seat_begin_resize(seat, view->swayc, BTN_LEFT, e->edges); + if (e->serial == seat->last_button_serial) { + seat_begin_resize(seat, view->swayc, seat->last_button, e->edges); + } } static void handle_unmap(struct wl_listener *listener, void *data) { diff --git a/sway/input/cursor.c b/sway/input/cursor.c index 43721d28..ad0ceb94 100644 --- a/sway/input/cursor.c +++ b/sway/input/cursor.c @@ -429,7 +429,7 @@ static void dispatch_cursor_button_floating(struct sway_cursor *cursor, // Deny moving or resizing a fullscreen view if (cont->type == C_VIEW && cont->sway_view->is_fullscreen) { - wlr_seat_pointer_notify_button(seat->wlr_seat, time_msec, button, state); + seat_pointer_notify_button(seat, time_msec, button, state); return; } @@ -442,7 +442,7 @@ static void dispatch_cursor_button_floating(struct sway_cursor *cursor, // Check for beginning move if (button == BTN_LEFT && state == WLR_BUTTON_PRESSED && (mod_pressed || over_title)) { - seat_begin_move(seat, cont); + seat_begin_move(seat, cont, BTN_LEFT); return; } @@ -456,7 +456,7 @@ static void dispatch_cursor_button_floating(struct sway_cursor *cursor, } // Send event to surface - wlr_seat_pointer_notify_button(seat->wlr_seat, time_msec, button, state); + seat_pointer_notify_button(seat, time_msec, button, state); } void dispatch_cursor_button(struct sway_cursor *cursor, @@ -480,8 +480,7 @@ void dispatch_cursor_button(struct sway_cursor *cursor, if (layer->current.keyboard_interactive) { seat_set_focus_layer(cursor->seat, layer); } - wlr_seat_pointer_notify_button(cursor->seat->wlr_seat, - time_msec, button, state); + seat_pointer_notify_button(cursor->seat, time_msec, button, state); } else if (cont && container_is_floating(cont)) { dispatch_cursor_button_floating(cursor, time_msec, button, state, surface, sx, sy, cont); @@ -501,15 +500,12 @@ void dispatch_cursor_button(struct sway_cursor *cursor, if (new_ws != old_ws) { seat_set_focus(cursor->seat, cont); } - wlr_seat_pointer_notify_button(cursor->seat->wlr_seat, - time_msec, button, state); + seat_pointer_notify_button(cursor->seat, time_msec, button, state); } else if (cont) { seat_set_focus(cursor->seat, cont); - wlr_seat_pointer_notify_button(cursor->seat->wlr_seat, - time_msec, button, state); + seat_pointer_notify_button(cursor->seat, time_msec, button, state); } else { - wlr_seat_pointer_notify_button(cursor->seat->wlr_seat, - time_msec, button, state); + seat_pointer_notify_button(cursor->seat, time_msec, button, state); } transaction_commit_dirty(); diff --git a/sway/input/seat.c b/sway/input/seat.c index 3a3350e1..4e803efd 100644 --- a/sway/input/seat.c +++ b/sway/input/seat.c @@ -901,14 +901,15 @@ struct seat_config *seat_get_config(struct sway_seat *seat) { return NULL; } -void seat_begin_move(struct sway_seat *seat, struct sway_container *con) { +void seat_begin_move(struct sway_seat *seat, struct sway_container *con, + uint32_t button) { if (!seat->cursor) { wlr_log(WLR_DEBUG, "Ignoring move request due to no cursor device"); return; } seat->operation = OP_MOVE; seat->op_container = con; - seat->op_button = BTN_LEFT; + seat->op_button = button; } void seat_begin_resize(struct sway_seat *seat, struct sway_container *con, @@ -951,3 +952,10 @@ void seat_end_mouse_operation(struct sway_seat *seat) { seat->operation = OP_NONE; seat->op_container = NULL; } + +void seat_pointer_notify_button(struct sway_seat *seat, uint32_t time_msec, + uint32_t button, enum wlr_button_state state) { + seat->last_button = button; + seat->last_button_serial = wlr_seat_pointer_notify_button(seat->wlr_seat, + time_msec, button, state); +} From 011d1ebfa4219eb666487529a5a5e7189c14fd40 Mon Sep 17 00:00:00 2001 From: Ryan Dwyer Date: Sat, 21 Jul 2018 12:13:00 +1000 Subject: [PATCH 016/148] Consider view's min/max sizes when resizing --- include/sway/tree/view.h | 5 +++++ sway/desktop/xdg_shell.c | 12 ++++++++++++ sway/desktop/xdg_shell_v6.c | 12 ++++++++++++ sway/input/cursor.c | 11 ++++++++++- sway/tree/view.c | 13 +++++++++++++ 5 files changed, 52 insertions(+), 1 deletion(-) diff --git a/include/sway/tree/view.h b/include/sway/tree/view.h index 068d92c6..1dfb218b 100644 --- a/include/sway/tree/view.h +++ b/include/sway/tree/view.h @@ -26,6 +26,8 @@ enum sway_view_prop { }; struct sway_view_impl { + void (*get_constraints)(struct sway_view *view, double *min_width, + double *max_width, double *min_height, double *max_height); const char *(*get_string_prop)(struct sway_view *view, enum sway_view_prop prop); uint32_t (*get_int_prop)(struct sway_view *view, enum sway_view_prop prop); @@ -215,6 +217,9 @@ uint32_t view_get_window_type(struct sway_view *view); const char *view_get_shell(struct sway_view *view); +void view_get_constraints(struct sway_view *view, double *min_width, + double *max_width, double *min_height, double *max_height); + uint32_t view_configure(struct sway_view *view, double lx, double ly, int width, int height); diff --git a/sway/desktop/xdg_shell.c b/sway/desktop/xdg_shell.c index c5d53d1d..76fe72ea 100644 --- a/sway/desktop/xdg_shell.c +++ b/sway/desktop/xdg_shell.c @@ -1,4 +1,5 @@ #define _POSIX_C_SOURCE 199309L +#include #include #include #include @@ -95,6 +96,16 @@ static struct sway_xdg_shell_view *xdg_shell_view_from_view( return (struct sway_xdg_shell_view *)view; } +static void get_constraints(struct sway_view *view, double *min_width, + double *max_width, double *min_height, double *max_height) { + struct wlr_xdg_toplevel_state *state = + &view->wlr_xdg_surface->toplevel->current; + *min_width = state->min_width > 0 ? state->min_width : DBL_MIN; + *max_width = state->max_width > 0 ? state->max_width : DBL_MAX; + *min_height = state->min_height > 0 ? state->min_height : DBL_MIN; + *max_height = state->max_height > 0 ? state->max_height : DBL_MAX; +} + static const char *get_string_prop(struct sway_view *view, enum sway_view_prop prop) { if (xdg_shell_view_from_view(view) == NULL) { return NULL; @@ -188,6 +199,7 @@ static void destroy(struct sway_view *view) { } static const struct sway_view_impl view_impl = { + .get_constraints = get_constraints, .get_string_prop = get_string_prop, .configure = configure, .set_activated = set_activated, diff --git a/sway/desktop/xdg_shell_v6.c b/sway/desktop/xdg_shell_v6.c index 4bd6af5e..57b51908 100644 --- a/sway/desktop/xdg_shell_v6.c +++ b/sway/desktop/xdg_shell_v6.c @@ -1,4 +1,5 @@ #define _POSIX_C_SOURCE 199309L +#include #include #include #include @@ -94,6 +95,16 @@ static struct sway_xdg_shell_v6_view *xdg_shell_v6_view_from_view( return (struct sway_xdg_shell_v6_view *)view; } +static void get_constraints(struct sway_view *view, double *min_width, + double *max_width, double *min_height, double *max_height) { + struct wlr_xdg_toplevel_v6_state *state = + &view->wlr_xdg_surface_v6->toplevel->current; + *min_width = state->min_width > 0 ? state->min_width : DBL_MIN; + *max_width = state->max_width > 0 ? state->max_width : DBL_MAX; + *min_height = state->min_height > 0 ? state->min_height : DBL_MIN; + *max_height = state->max_height > 0 ? state->max_height : DBL_MAX; +} + static const char *get_string_prop(struct sway_view *view, enum sway_view_prop prop) { if (xdg_shell_v6_view_from_view(view) == NULL) { return NULL; @@ -184,6 +195,7 @@ static void destroy(struct sway_view *view) { } static const struct sway_view_impl view_impl = { + .get_constraints = get_constraints, .get_string_prop = get_string_prop, .configure = configure, .set_activated = set_activated, diff --git a/sway/input/cursor.c b/sway/input/cursor.c index ad0ceb94..7deb2b19 100644 --- a/sway/input/cursor.c +++ b/sway/input/cursor.c @@ -243,7 +243,7 @@ static void handle_resize_motion(struct sway_seat *seat, grow_height = seat->op_ref_height * max_multiplier; } - // Determine new width/height, and accommodate for min/max values + // Determine new width/height, and accommodate for floating min/max values double width = seat->op_ref_width + grow_width; double height = seat->op_ref_height + grow_height; int min_width, max_width, min_height, max_height; @@ -252,6 +252,15 @@ static void handle_resize_motion(struct sway_seat *seat, width = fmax(min_width, fmin(width, max_width)); height = fmax(min_height, fmin(height, max_height)); + // Apply the view's min/max size + if (con->type == C_VIEW) { + double view_min_width, view_max_width, view_min_height, view_max_height; + view_get_constraints(con->sway_view, &view_min_width, &view_max_width, + &view_min_height, &view_max_height); + width = fmax(view_min_width, fmin(width, view_max_width)); + height = fmax(view_min_height, fmin(height, view_max_height)); + } + // Recalculate these, in case we hit a min/max limit grow_width = width - seat->op_ref_width; grow_height = height - seat->op_ref_height; diff --git a/sway/tree/view.c b/sway/tree/view.c index 24594950..89150a69 100644 --- a/sway/tree/view.c +++ b/sway/tree/view.c @@ -141,6 +141,19 @@ const char *view_get_shell(struct sway_view *view) { return "unknown"; } +void view_get_constraints(struct sway_view *view, double *min_width, + double *max_width, double *min_height, double *max_height) { + if (view->impl->get_constraints) { + view->impl->get_constraints(view, + min_width, max_width, min_height, max_height); + } else { + *min_width = DBL_MIN; + *max_width = DBL_MAX; + *min_height = DBL_MIN; + *max_height = DBL_MAX; + } +} + uint32_t view_configure(struct sway_view *view, double lx, double ly, int width, int height) { if (view->impl->configure) { From cf5f5eaf8c67c9d06c491a82b0b235aa27bbfe5c Mon Sep 17 00:00:00 2001 From: Ryan Dwyer Date: Sun, 22 Jul 2018 21:45:01 +1000 Subject: [PATCH 017/148] Deny move/resize events for tiled xdg shell views --- sway/desktop/xdg_shell.c | 6 ++++++ sway/desktop/xdg_shell_v6.c | 6 ++++++ 2 files changed, 12 insertions(+) diff --git a/sway/desktop/xdg_shell.c b/sway/desktop/xdg_shell.c index 76fe72ea..706b35c3 100644 --- a/sway/desktop/xdg_shell.c +++ b/sway/desktop/xdg_shell.c @@ -264,6 +264,9 @@ static void handle_request_move(struct wl_listener *listener, void *data) { struct sway_xdg_shell_view *xdg_shell_view = wl_container_of(listener, xdg_shell_view, request_move); struct sway_view *view = &xdg_shell_view->view; + if (!container_is_floating(view->swayc)) { + return; + } struct wlr_xdg_toplevel_move_event *e = data; struct sway_seat *seat = e->seat->seat->data; if (e->serial == seat->last_button_serial) { @@ -275,6 +278,9 @@ static void handle_request_resize(struct wl_listener *listener, void *data) { struct sway_xdg_shell_view *xdg_shell_view = wl_container_of(listener, xdg_shell_view, request_resize); struct sway_view *view = &xdg_shell_view->view; + if (!container_is_floating(view->swayc)) { + return; + } struct wlr_xdg_toplevel_resize_event *e = data; struct sway_seat *seat = e->seat->seat->data; if (e->serial == seat->last_button_serial) { diff --git a/sway/desktop/xdg_shell_v6.c b/sway/desktop/xdg_shell_v6.c index 57b51908..201b5b1e 100644 --- a/sway/desktop/xdg_shell_v6.c +++ b/sway/desktop/xdg_shell_v6.c @@ -259,6 +259,9 @@ static void handle_request_move(struct wl_listener *listener, void *data) { struct sway_xdg_shell_v6_view *xdg_shell_v6_view = wl_container_of(listener, xdg_shell_v6_view, request_move); struct sway_view *view = &xdg_shell_v6_view->view; + if (!container_is_floating(view->swayc)) { + return; + } struct wlr_xdg_toplevel_v6_move_event *e = data; struct sway_seat *seat = e->seat->seat->data; if (e->serial == seat->last_button_serial) { @@ -270,6 +273,9 @@ static void handle_request_resize(struct wl_listener *listener, void *data) { struct sway_xdg_shell_v6_view *xdg_shell_v6_view = wl_container_of(listener, xdg_shell_v6_view, request_resize); struct sway_view *view = &xdg_shell_v6_view->view; + if (!container_is_floating(view->swayc)) { + return; + } struct wlr_xdg_toplevel_v6_resize_event *e = data; struct sway_seat *seat = e->seat->seat->data; if (e->serial == seat->last_button_serial) { From 3faceadffe9c9b334d22cad3a348b82078b542b5 Mon Sep 17 00:00:00 2001 From: Ryan Dwyer Date: Sun, 22 Jul 2018 21:50:35 +1000 Subject: [PATCH 018/148] Fix focus bug with floating containers --- sway/input/cursor.c | 1 + 1 file changed, 1 insertion(+) diff --git a/sway/input/cursor.c b/sway/input/cursor.c index 7deb2b19..54cd3bf7 100644 --- a/sway/input/cursor.c +++ b/sway/input/cursor.c @@ -465,6 +465,7 @@ static void dispatch_cursor_button_floating(struct sway_cursor *cursor, } // Send event to surface + seat_set_focus(seat, cont); seat_pointer_notify_button(seat, time_msec, button, state); } From 5ba2ae9c6a4372cbf6f8867b711bb55ef6937cb4 Mon Sep 17 00:00:00 2001 From: Ryan Dwyer Date: Sun, 22 Jul 2018 22:14:36 +1000 Subject: [PATCH 019/148] Implement request_move and request_resize for xwayland views I discovered we have to send a click event when ending the move or resize operation to make xwayland's requests work correctly. --- sway/desktop/xwayland.c | 41 +++++++++++++++++++++++++++++++++++++++++ sway/input/cursor.c | 1 + 2 files changed, 42 insertions(+) diff --git a/sway/desktop/xwayland.c b/sway/desktop/xwayland.c index bce0a37b..2546168b 100644 --- a/sway/desktop/xwayland.c +++ b/sway/desktop/xwayland.c @@ -305,6 +305,8 @@ static void handle_destroy(struct wl_listener *listener, void *data) { wl_list_remove(&xwayland_view->destroy.link); wl_list_remove(&xwayland_view->request_configure.link); wl_list_remove(&xwayland_view->request_fullscreen.link); + wl_list_remove(&xwayland_view->request_move.link); + wl_list_remove(&xwayland_view->request_resize.link); wl_list_remove(&xwayland_view->set_title.link); wl_list_remove(&xwayland_view->set_class.link); wl_list_remove(&xwayland_view->set_window_type.link); @@ -400,6 +402,37 @@ static void handle_request_fullscreen(struct wl_listener *listener, void *data) transaction_commit_dirty(); } +static void handle_request_move(struct wl_listener *listener, void *data) { + struct sway_xwayland_view *xwayland_view = + wl_container_of(listener, xwayland_view, request_move); + struct sway_view *view = &xwayland_view->view; + struct wlr_xwayland_surface *xsurface = view->wlr_xwayland_surface; + if (!xsurface->mapped) { + return; + } + if (!container_is_floating(view->swayc)) { + return; + } + struct sway_seat *seat = input_manager_current_seat(input_manager); + seat_begin_move(seat, view->swayc, seat->last_button); +} + +static void handle_request_resize(struct wl_listener *listener, void *data) { + struct sway_xwayland_view *xwayland_view = + wl_container_of(listener, xwayland_view, request_resize); + struct sway_view *view = &xwayland_view->view; + struct wlr_xwayland_surface *xsurface = view->wlr_xwayland_surface; + if (!xsurface->mapped) { + return; + } + if (!container_is_floating(view->swayc)) { + return; + } + struct wlr_xwayland_resize_event *e = data; + struct sway_seat *seat = input_manager_current_seat(input_manager); + seat_begin_resize(seat, view->swayc, seat->last_button, e->edges); +} + static void handle_set_title(struct wl_listener *listener, void *data) { struct sway_xwayland_view *xwayland_view = wl_container_of(listener, xwayland_view, set_title); @@ -495,6 +528,14 @@ void handle_xwayland_surface(struct wl_listener *listener, void *data) { &xwayland_view->request_fullscreen); xwayland_view->request_fullscreen.notify = handle_request_fullscreen; + wl_signal_add(&xsurface->events.request_move, + &xwayland_view->request_move); + xwayland_view->request_move.notify = handle_request_move; + + wl_signal_add(&xsurface->events.request_resize, + &xwayland_view->request_resize); + xwayland_view->request_resize.notify = handle_request_resize; + wl_signal_add(&xsurface->events.set_title, &xwayland_view->set_title); xwayland_view->set_title.notify = handle_set_title; diff --git a/sway/input/cursor.c b/sway/input/cursor.c index 54cd3bf7..f9b59fcc 100644 --- a/sway/input/cursor.c +++ b/sway/input/cursor.c @@ -474,6 +474,7 @@ void dispatch_cursor_button(struct sway_cursor *cursor, if (cursor->seat->operation != OP_NONE && button == cursor->seat->op_button && state == WLR_BUTTON_RELEASED) { seat_end_mouse_operation(cursor->seat); + seat_pointer_notify_button(cursor->seat, time_msec, button, state); return; } if (time_msec == 0) { From 009b42602460076f96073166ce647aa59177b961 Mon Sep 17 00:00:00 2001 From: Ryan Dwyer Date: Sun, 22 Jul 2018 22:44:09 +1000 Subject: [PATCH 020/148] Fix damage when shrinking a floating view using cursor --- sway/input/cursor.c | 1 - 1 file changed, 1 deletion(-) diff --git a/sway/input/cursor.c b/sway/input/cursor.c index f9b59fcc..ee50dcde 100644 --- a/sway/input/cursor.c +++ b/sway/input/cursor.c @@ -305,7 +305,6 @@ static void handle_resize_motion(struct sway_seat *seat, } arrange_windows(con); - transaction_commit_dirty(); } void cursor_send_pointer_motion(struct sway_cursor *cursor, uint32_t time_msec, From d21d4b83a3effc56b177f5d505aedfdf2ddbbbd5 Mon Sep 17 00:00:00 2001 From: Ryan Dwyer Date: Sun, 22 Jul 2018 23:10:06 +1000 Subject: [PATCH 021/148] Set cursor when beginning resize and move operations --- sway/input/cursor.c | 4 ++++ sway/input/seat.c | 6 ++++++ 2 files changed, 10 insertions(+) diff --git a/sway/input/cursor.c b/sway/input/cursor.c index ee50dcde..771ad01d 100644 --- a/sway/input/cursor.c +++ b/sway/input/cursor.c @@ -215,6 +215,7 @@ static void calculate_floating_constraints(struct sway_container *con, *max_height = config->floating_maximum_height; } } + static void handle_resize_motion(struct sway_seat *seat, struct sway_cursor *cursor) { struct sway_container *con = seat->op_container; @@ -704,6 +705,9 @@ static void handle_request_set_cursor(struct wl_listener *listener, void *data) { struct sway_cursor *cursor = wl_container_of(listener, cursor, request_set_cursor); + if (cursor->seat->operation != OP_NONE) { + return; + } struct wlr_seat_pointer_request_set_cursor_event *event = data; struct wl_client *focused_client = NULL; diff --git a/sway/input/seat.c b/sway/input/seat.c index 4e803efd..273223db 100644 --- a/sway/input/seat.c +++ b/sway/input/seat.c @@ -910,6 +910,7 @@ void seat_begin_move(struct sway_seat *seat, struct sway_container *con, seat->operation = OP_MOVE; seat->op_container = con; seat->op_button = button; + cursor_set_image(seat->cursor, "grab", NULL); } void seat_begin_resize(struct sway_seat *seat, struct sway_container *con, @@ -932,6 +933,10 @@ void seat_begin_resize(struct sway_seat *seat, struct sway_container *con, seat->op_ref_con_ly = con->y; seat->op_ref_width = con->width; seat->op_ref_height = con->height; + + const char *image = edge == WLR_EDGE_NONE ? + "se-resize" : wlr_xcursor_get_resize_name(edge); + cursor_set_image(seat->cursor, image, NULL); } void seat_end_mouse_operation(struct sway_seat *seat) { @@ -951,6 +956,7 @@ void seat_end_mouse_operation(struct sway_seat *seat) { } seat->operation = OP_NONE; seat->op_container = NULL; + cursor_set_image(seat->cursor, "left_ptr", NULL); } void seat_pointer_notify_button(struct sway_seat *seat, uint32_t time_msec, From 238c8afc74241efdc44d1cf88322d322ce1226d9 Mon Sep 17 00:00:00 2001 From: emersion Date: Sun, 22 Jul 2018 22:20:07 +0100 Subject: [PATCH 022/148] Handle set_{title,app_id} for xdg-shell and zxdg-shell-v6 This allows to update the title even if the view doesn't commit. This is useful e.g. when a terminal sets its toplevel title to the currently running command and when the view isn't visible. --- include/sway/tree/view.h | 4 ++++ sway/desktop/xdg_shell.c | 26 +++++++++++++++++++++++++- sway/desktop/xdg_shell_v6.c | 26 +++++++++++++++++++++++++- 3 files changed, 54 insertions(+), 2 deletions(-) diff --git a/include/sway/tree/view.h b/include/sway/tree/view.h index 1dfb218b..3bdfe252 100644 --- a/include/sway/tree/view.h +++ b/include/sway/tree/view.h @@ -110,6 +110,8 @@ struct sway_xdg_shell_v6_view { struct wl_listener request_resize; struct wl_listener request_maximize; struct wl_listener request_fullscreen; + struct wl_listener set_title; + struct wl_listener set_app_id; struct wl_listener new_popup; struct wl_listener map; struct wl_listener unmap; @@ -124,6 +126,8 @@ struct sway_xdg_shell_view { struct wl_listener request_resize; struct wl_listener request_maximize; struct wl_listener request_fullscreen; + struct wl_listener set_title; + struct wl_listener set_app_id; struct wl_listener new_popup; struct wl_listener map; struct wl_listener unmap; diff --git a/sway/desktop/xdg_shell.c b/sway/desktop/xdg_shell.c index 706b35c3..62c3abc8 100644 --- a/sway/desktop/xdg_shell.c +++ b/sway/desktop/xdg_shell.c @@ -225,10 +225,24 @@ static void handle_commit(struct wl_listener *listener, void *data) { transaction_notify_view_ready(view, xdg_surface->configure_serial); } - view_update_title(view, false); view_damage_from(view); } +static void handle_set_title(struct wl_listener *listener, void *data) { + struct sway_xdg_shell_view *xdg_shell_view = + wl_container_of(listener, xdg_shell_view, set_title); + struct sway_view *view = &xdg_shell_view->view; + view_update_title(view, false); + view_execute_criteria(view); +} + +static void handle_set_app_id(struct wl_listener *listener, void *data) { + struct sway_xdg_shell_view *xdg_shell_view = + wl_container_of(listener, xdg_shell_view, set_app_id); + struct sway_view *view = &xdg_shell_view->view; + view_execute_criteria(view); +} + static void handle_new_popup(struct wl_listener *listener, void *data) { struct sway_xdg_shell_view *xdg_shell_view = wl_container_of(listener, xdg_shell_view, new_popup); @@ -304,6 +318,8 @@ static void handle_unmap(struct wl_listener *listener, void *data) { wl_list_remove(&xdg_shell_view->request_fullscreen.link); wl_list_remove(&xdg_shell_view->request_move.link); wl_list_remove(&xdg_shell_view->request_resize.link); + wl_list_remove(&xdg_shell_view->set_title.link); + wl_list_remove(&xdg_shell_view->set_app_id.link); } static void handle_map(struct wl_listener *listener, void *data) { @@ -349,6 +365,14 @@ static void handle_map(struct wl_listener *listener, void *data) { xdg_shell_view->request_resize.notify = handle_request_resize; wl_signal_add(&xdg_surface->toplevel->events.request_resize, &xdg_shell_view->request_resize); + + xdg_shell_view->set_title.notify = handle_set_title; + wl_signal_add(&xdg_surface->toplevel->events.set_title, + &xdg_shell_view->set_title); + + xdg_shell_view->set_app_id.notify = handle_set_app_id; + wl_signal_add(&xdg_surface->toplevel->events.set_app_id, + &xdg_shell_view->set_app_id); } static void handle_destroy(struct wl_listener *listener, void *data) { diff --git a/sway/desktop/xdg_shell_v6.c b/sway/desktop/xdg_shell_v6.c index 201b5b1e..7fb85410 100644 --- a/sway/desktop/xdg_shell_v6.c +++ b/sway/desktop/xdg_shell_v6.c @@ -220,10 +220,24 @@ static void handle_commit(struct wl_listener *listener, void *data) { transaction_notify_view_ready(view, xdg_surface_v6->configure_serial); } - view_update_title(view, false); view_damage_from(view); } +static void handle_set_title(struct wl_listener *listener, void *data) { + struct sway_xdg_shell_v6_view *xdg_shell_v6_view = + wl_container_of(listener, xdg_shell_v6_view, set_title); + struct sway_view *view = &xdg_shell_v6_view->view; + view_update_title(view, false); + view_execute_criteria(view); +} + +static void handle_set_app_id(struct wl_listener *listener, void *data) { + struct sway_xdg_shell_v6_view *xdg_shell_v6_view = + wl_container_of(listener, xdg_shell_v6_view, set_app_id); + struct sway_view *view = &xdg_shell_v6_view->view; + view_execute_criteria(view); +} + static void handle_new_popup(struct wl_listener *listener, void *data) { struct sway_xdg_shell_v6_view *xdg_shell_v6_view = wl_container_of(listener, xdg_shell_v6_view, new_popup); @@ -299,6 +313,8 @@ static void handle_unmap(struct wl_listener *listener, void *data) { wl_list_remove(&xdg_shell_v6_view->request_fullscreen.link); wl_list_remove(&xdg_shell_v6_view->request_move.link); wl_list_remove(&xdg_shell_v6_view->request_resize.link); + wl_list_remove(&xdg_shell_v6_view->set_title.link); + wl_list_remove(&xdg_shell_v6_view->set_app_id.link); } static void handle_map(struct wl_listener *listener, void *data) { @@ -344,6 +360,14 @@ static void handle_map(struct wl_listener *listener, void *data) { xdg_shell_v6_view->request_resize.notify = handle_request_resize; wl_signal_add(&xdg_surface->toplevel->events.request_resize, &xdg_shell_v6_view->request_resize); + + xdg_shell_v6_view->set_title.notify = handle_set_title; + wl_signal_add(&xdg_surface->toplevel->events.set_title, + &xdg_shell_v6_view->set_title); + + xdg_shell_v6_view->set_app_id.notify = handle_set_app_id; + wl_signal_add(&xdg_surface->toplevel->events.set_app_id, + &xdg_shell_v6_view->set_app_id); } static void handle_destroy(struct wl_listener *listener, void *data) { From 81e8f31cc6f284b54ab206e14af7ecbc1a9ed1bb Mon Sep 17 00:00:00 2001 From: Ryan Dwyer Date: Sun, 22 Jul 2018 14:10:40 +1000 Subject: [PATCH 023/148] Implement scratchpad Implements the following commands: * move scratchpad * scratchpad show * [criteria] scratchpad show Also fixes these: * Fix memory leak when executing command with criteria (use `list_free(views)` instead of `free(views)`) * Fix crash when running `move to` with no further arguments --- include/sway/scratchpad.h | 26 +++++ include/sway/server.h | 2 + include/sway/tree/container.h | 5 + sway/commands.c | 3 +- sway/commands/move.c | 19 +++- sway/commands/scratchpad.c | 37 ++++++++ sway/criteria.c | 8 ++ sway/meson.build | 2 + sway/scratchpad.c | 173 ++++++++++++++++++++++++++++++++++ sway/server.c | 3 + sway/tree/container.c | 8 ++ sway/tree/layout.c | 7 ++ sway/tree/view.c | 2 +- 13 files changed, 290 insertions(+), 5 deletions(-) create mode 100644 include/sway/scratchpad.h create mode 100644 sway/commands/scratchpad.c create mode 100644 sway/scratchpad.c diff --git a/include/sway/scratchpad.h b/include/sway/scratchpad.h new file mode 100644 index 00000000..5af5256f --- /dev/null +++ b/include/sway/scratchpad.h @@ -0,0 +1,26 @@ +#ifndef _SWAY_SCRATCHPAD_H +#define _SWAY_SCRATCHPAD_H + +#include "tree/container.h" + +/** + * Move a container to the scratchpad. + */ +void scratchpad_add_container(struct sway_container *con); + +/** + * Remove a container from the scratchpad. + */ +void scratchpad_remove_container(struct sway_container *con); + +/** + * Show or hide the next container on the scratchpad. + */ +void scratchpad_toggle_auto(void); + +/** + * Show or hide a specific container on the scratchpad. + */ +void scratchpad_toggle_container(struct sway_container *con); + +#endif diff --git a/include/sway/server.h b/include/sway/server.h index 70bde6d4..6cef2e58 100644 --- a/include/sway/server.h +++ b/include/sway/server.h @@ -48,6 +48,8 @@ struct sway_server { list_t *transactions; list_t *dirty_containers; + + list_t *scratchpad; // struct sway_container }; struct sway_server server; diff --git a/include/sway/tree/container.h b/include/sway/tree/container.h index 59c5b4c7..2a4be18c 100644 --- a/include/sway/tree/container.h +++ b/include/sway/tree/container.h @@ -135,6 +135,11 @@ struct sway_container { struct sway_container *parent; + // Indicates that the container is a scratchpad container. + // Both hidden and visible scratchpad containers have scratchpad=true. + // Hidden scratchpad containers have a NULL parent. + bool scratchpad; + float alpha; struct wlr_texture *title_focused; diff --git a/sway/commands.c b/sway/commands.c index f40f0e9d..fdae1961 100644 --- a/sway/commands.c +++ b/sway/commands.c @@ -149,6 +149,7 @@ static struct cmd_handler command_handlers[] = { { "reload", cmd_reload }, { "rename", cmd_rename }, { "resize", cmd_resize }, + { "scratchpad", cmd_scratchpad }, { "split", cmd_split }, { "splith", cmd_splith }, { "splitt", cmd_splitt }, @@ -326,7 +327,7 @@ struct cmd_results *execute_command(char *_exec, struct sway_seat *seat) { } while(head); cleanup: free(exec); - free(views); + list_free(views); if (!results) { results = cmd_results_new(CMD_SUCCESS, NULL, NULL); } diff --git a/sway/commands/move.c b/sway/commands/move.c index 6ec050a8..1940043d 100644 --- a/sway/commands/move.c +++ b/sway/commands/move.c @@ -9,6 +9,7 @@ #include "sway/input/cursor.h" #include "sway/input/seat.h" #include "sway/output.h" +#include "sway/scratchpad.h" #include "sway/tree/arrange.h" #include "sway/tree/container.h" #include "sway/tree/layout.h" @@ -296,6 +297,19 @@ static struct cmd_results *move_to_position(struct sway_container *container, return cmd_results_new(CMD_SUCCESS, NULL, NULL); } +static struct cmd_results *move_to_scratchpad(struct sway_container *con) { + if (con->type != C_CONTAINER && con->type != C_VIEW) { + return cmd_results_new(CMD_INVALID, "move", + "Only views and containers can be moved to the scratchpad"); + } + if (con->scratchpad) { + return cmd_results_new(CMD_INVALID, "move", + "Container is already in the scratchpad"); + } + scratchpad_add_container(con); + return cmd_results_new(CMD_SUCCESS, NULL, NULL); +} + struct cmd_results *cmd_move(int argc, char **argv) { struct cmd_results *error = NULL; if ((error = checkarg(argc, "move", EXPECTED_AT_LEAST, 1))) { @@ -317,10 +331,9 @@ struct cmd_results *cmd_move(int argc, char **argv) { } else if (strcasecmp(argv[0], "workspace") == 0) { return cmd_move_workspace(current, argc, argv); } else if (strcasecmp(argv[0], "scratchpad") == 0 - || (strcasecmp(argv[0], "to") == 0 + || (strcasecmp(argv[0], "to") == 0 && argc == 2 && strcasecmp(argv[1], "scratchpad") == 0)) { - // TODO: scratchpad - return cmd_results_new(CMD_FAILURE, "move", "Unimplemented"); + return move_to_scratchpad(current); } else if (strcasecmp(argv[0], "position") == 0) { return move_to_position(current, argc, argv); } else if (strcasecmp(argv[0], "absolute") == 0) { diff --git a/sway/commands/scratchpad.c b/sway/commands/scratchpad.c new file mode 100644 index 00000000..8a529cb4 --- /dev/null +++ b/sway/commands/scratchpad.c @@ -0,0 +1,37 @@ +#include "log.h" +#include "sway/commands.h" +#include "sway/config.h" +#include "sway/scratchpad.h" +#include "sway/server.h" +#include "sway/tree/container.h" + +struct cmd_results *cmd_scratchpad(int argc, char **argv) { + struct cmd_results *error = NULL; + if ((error = checkarg(argc, "scratchpad", EXPECTED_EQUAL_TO, 1))) { + return error; + } + if (strcmp(argv[0], "show") != 0) { + return cmd_results_new(CMD_INVALID, "scratchpad", + "Expected 'scratchpad show'"); + } + if (!server.scratchpad->length) { + return cmd_results_new(CMD_INVALID, "scratchpad", + "Scratchpad is empty"); + } + + if (config->handler_context.using_criteria) { + // If using criteria, this command is executed for every container which + // matches the criteria. If this container isn't in the scratchpad, + // we'll just silently return a success. + struct sway_container *con = config->handler_context.current_container; + wlr_log(WLR_INFO, "cmd_scratchpad(%s)", con->name); + if (!con->scratchpad) { + return cmd_results_new(CMD_SUCCESS, NULL, NULL); + } + scratchpad_toggle_container(con); + } else { + scratchpad_toggle_auto(); + } + + return cmd_results_new(CMD_SUCCESS, NULL, NULL); +} diff --git a/sway/criteria.c b/sway/criteria.c index e2b248de..6af97d5b 100644 --- a/sway/criteria.c +++ b/sway/criteria.c @@ -225,6 +225,14 @@ list_t *criteria_get_views(struct criteria *criteria) { }; container_for_each_descendant_dfs(&root_container, criteria_get_views_iterator, &data); + + // Scratchpad items which are hidden are not in the tree. + for (int i = 0; i < server.scratchpad->length; ++i) { + struct sway_container *con = server.scratchpad->items[i]; + if (!con->parent) { + criteria_get_views_iterator(con, &data); + } + } return matches; } diff --git a/sway/meson.build b/sway/meson.build index f4fdc8ea..30c848e2 100644 --- a/sway/meson.build +++ b/sway/meson.build @@ -7,6 +7,7 @@ sway_sources = files( 'debug-tree.c', 'ipc-json.c', 'ipc-server.c', + 'scratchpad.c', 'security.c', 'desktop/desktop.c', @@ -67,6 +68,7 @@ sway_sources = files( 'commands/reload.c', 'commands/rename.c', 'commands/resize.c', + 'commands/scratchpad.c', 'commands/seat.c', 'commands/seat/attach.c', 'commands/seat/cursor.c', diff --git a/sway/scratchpad.c b/sway/scratchpad.c new file mode 100644 index 00000000..e1f931a4 --- /dev/null +++ b/sway/scratchpad.c @@ -0,0 +1,173 @@ +#define _XOPEN_SOURCE 700 +#include +#include +#include +#include "sway/scratchpad.h" +#include "sway/input/seat.h" +#include "sway/tree/arrange.h" +#include "sway/tree/container.h" +#include "sway/tree/view.h" +#include "sway/tree/workspace.h" +#include "list.h" +#include "log.h" + +void scratchpad_add_container(struct sway_container *con) { + if (!sway_assert(!con->scratchpad, "Container is already in scratchpad")) { + return; + } + con->scratchpad = true; + list_add(server.scratchpad, con); + + struct sway_container *parent = con->parent; + container_set_floating(con, true); + container_remove_child(con); + arrange_windows(parent); + + struct sway_seat *seat = input_manager_current_seat(input_manager); + seat_set_focus(seat, seat_get_focus_inactive(seat, parent)); +} + +void scratchpad_remove_container(struct sway_container *con) { + if (!sway_assert(con->scratchpad, "Container is not in scratchpad")) { + return; + } + con->scratchpad = false; + for (int i = 0; i < server.scratchpad->length; ++i) { + if (server.scratchpad->items[i] == con) { + list_del(server.scratchpad, i); + break; + } + } +} + +/** + * Show a single scratchpad container. + * The container might be visible on another workspace already. + */ +static void scratchpad_show(struct sway_container *con) { + struct sway_seat *seat = input_manager_current_seat(input_manager); + struct sway_container *ws = seat_get_focus(seat); + if (ws->type != C_WORKSPACE) { + ws = container_parent(ws, C_WORKSPACE); + } + + // If the current con or any of its parents are in fullscreen mode, we + // first need to disable it before showing the scratchpad con. + if (ws->sway_workspace->fullscreen) { + view_set_fullscreen(ws->sway_workspace->fullscreen, false); + } + + // Show the container + if (con->parent) { + container_remove_child(con); + } + container_add_child(ws->sway_workspace->floating, con); + + // Make sure the container's center point overlaps this workspace + double center_lx = con->x + con->width / 2; + double center_ly = con->y + con->height / 2; + + struct wlr_box workspace_box; + container_get_box(ws, &workspace_box); + if (!wlr_box_contains_point(&workspace_box, center_lx, center_ly)) { + // Maybe resize it + if (con->width > ws->width || con->height > ws->height) { + // TODO: Do this properly once we can float C_CONTAINERs + if (con->type == C_VIEW) { + view_init_floating(con->sway_view); + arrange_windows(con); + } + } + + // Center it + double new_lx = ws->x + (ws->width - con->width) / 2; + double new_ly = ws->y + (ws->height - con->height) / 2; + container_floating_move_to(con, new_lx, new_ly); + } + + seat_set_focus(seat, con); + + container_set_dirty(con->parent); +} + +/** + * Hide a single scratchpad container. + * The container might not be the focused container (eg. when using criteria). + */ +static void scratchpad_hide(struct sway_container *con) { + struct sway_seat *seat = input_manager_current_seat(input_manager); + struct sway_container *focus = seat_get_focus(seat); + struct sway_container *ws = container_parent(con, C_WORKSPACE); + + container_remove_child(con); + arrange_windows(ws); + if (con == focus) { + seat_set_focus(seat, seat_get_focus_inactive(seat, ws)); + } + list_move_to_end(server.scratchpad, con); +} + +void scratchpad_toggle_auto(void) { + struct sway_seat *seat = input_manager_current_seat(input_manager); + struct sway_container *focus = seat_get_focus(seat); + struct sway_container *ws = focus->type == C_WORKSPACE ? + focus : container_parent(focus, C_WORKSPACE); + + // Check if the currently focused window is a scratchpad window and should + // be hidden again. + if (focus->scratchpad) { + wlr_log(WLR_DEBUG, "Focus is a scratchpad window - hiding %s", + focus->name); + scratchpad_hide(focus); + return; + } + + // Check if there is an unfocused scratchpad window on the current workspace + // and focus it. + for (int i = 0; i < ws->sway_workspace->floating->children->length; ++i) { + struct sway_container *floater = + ws->sway_workspace->floating->children->items[i]; + if (floater->scratchpad && focus != floater) { + wlr_log(WLR_DEBUG, + "Focusing other scratchpad window (%s) in this workspace", + floater->name); + scratchpad_show(floater); + return; + } + } + + // Check if there is a visible scratchpad window on another workspace. + // In this case we move it to the current workspace. + for (int i = 0; i < server.scratchpad->length; ++i) { + struct sway_container *con = server.scratchpad->items[i]; + if (con->parent) { + wlr_log(WLR_DEBUG, + "Moving a visible scratchpad window (%s) to this workspace", + con->name); + scratchpad_show(con); + return; + } + } + + // Take the container at the bottom of the scratchpad list + if (!sway_assert(server.scratchpad->length, "Scratchpad is empty")) { + return; + } + struct sway_container *con = server.scratchpad->items[0]; + wlr_log(WLR_DEBUG, "Showing %s from list", con->name); + scratchpad_show(con); +} + +void scratchpad_toggle_container(struct sway_container *con) { + if (!sway_assert(con->scratchpad, "Container isn't in the scratchpad")) { + return; + } + + // Check if it matches a currently visible scratchpad window and hide it. + if (con->parent) { + scratchpad_hide(con); + return; + } + + scratchpad_show(con); +} diff --git a/sway/server.c b/sway/server.c index 89dfbf8c..916e6b71 100644 --- a/sway/server.c +++ b/sway/server.c @@ -126,6 +126,8 @@ bool server_init(struct sway_server *server) { server->dirty_containers = create_list(); server->transactions = create_list(); + server->scratchpad = create_list(); + input_manager = input_manager_create(server); return true; } @@ -135,6 +137,7 @@ void server_fini(struct sway_server *server) { wl_display_destroy(server->wl_display); list_free(server->dirty_containers); list_free(server->transactions); + list_free(server->scratchpad); } bool server_start_backend(struct sway_server *server) { diff --git a/sway/tree/container.c b/sway/tree/container.c index 42c1d024..4f743c40 100644 --- a/sway/tree/container.c +++ b/sway/tree/container.c @@ -17,6 +17,7 @@ #include "sway/input/seat.h" #include "sway/ipc-server.h" #include "sway/output.h" +#include "sway/scratchpad.h" #include "sway/server.h" #include "sway/tree/arrange.h" #include "sway/tree/layout.h" @@ -328,6 +329,10 @@ static struct sway_container *container_destroy_noreaping( con->destroying = true; container_set_dirty(con); + if (con->scratchpad) { + scratchpad_remove_container(con); + } + if (!con->parent) { return NULL; } @@ -955,6 +960,9 @@ void container_set_floating(struct sway_container *container, bool enable) { container_reap_empty_recursive(workspace); } else { // Returning to tiled + if (container->scratchpad) { + scratchpad_remove_container(container); + } container_remove_child(container); container_add_child(workspace, container); container->width = container->parent->width; diff --git a/sway/tree/layout.c b/sway/tree/layout.c index 533906fa..af37611f 100644 --- a/sway/tree/layout.c +++ b/sway/tree/layout.c @@ -135,6 +135,10 @@ void container_add_child(struct sway_container *parent, list_add(parent->children, child); child->parent = parent; container_handle_fullscreen_reparent(child, old_parent); + if (old_parent) { + container_set_dirty(old_parent); + } + container_set_dirty(child); } struct sway_container *container_remove_child(struct sway_container *child) { @@ -153,6 +157,9 @@ struct sway_container *container_remove_child(struct sway_container *child) { child->parent = NULL; container_notify_subtree_changed(parent); + container_set_dirty(parent); + container_set_dirty(child); + return parent; } diff --git a/sway/tree/view.c b/sway/tree/view.c index 89150a69..9d88d7aa 100644 --- a/sway/tree/view.c +++ b/sway/tree/view.c @@ -1065,7 +1065,7 @@ void view_update_marks_textures(struct sway_view *view) { } bool view_is_visible(struct sway_view *view) { - if (!view->swayc || view->swayc->destroying) { + if (!view->swayc || view->swayc->destroying || !view->swayc->parent) { return false; } struct sway_container *workspace = From 12e90fa6006b2cf17a5b5983b5a6e2e70cda58d3 Mon Sep 17 00:00:00 2001 From: Ryan Dwyer Date: Sun, 22 Jul 2018 22:28:20 +1000 Subject: [PATCH 024/148] Store scratchpad list in sway_root instead of server --- include/sway/server.h | 2 -- include/sway/tree/layout.h | 2 ++ sway/commands/scratchpad.c | 3 +-- sway/criteria.c | 5 +++-- sway/scratchpad.c | 20 +++++++++++--------- sway/server.c | 3 --- sway/tree/layout.c | 1 + 7 files changed, 18 insertions(+), 18 deletions(-) diff --git a/include/sway/server.h b/include/sway/server.h index 6cef2e58..70bde6d4 100644 --- a/include/sway/server.h +++ b/include/sway/server.h @@ -48,8 +48,6 @@ struct sway_server { list_t *transactions; list_t *dirty_containers; - - list_t *scratchpad; // struct sway_container }; struct sway_server server; diff --git a/include/sway/tree/layout.h b/include/sway/tree/layout.h index 5a78fd58..7d7da2d7 100644 --- a/include/sway/tree/layout.h +++ b/include/sway/tree/layout.h @@ -35,6 +35,8 @@ struct sway_root { struct wl_list outputs; // sway_output::link + list_t *scratchpad; // struct sway_container + struct { struct wl_signal new_container; } events; diff --git a/sway/commands/scratchpad.c b/sway/commands/scratchpad.c index 8a529cb4..ccc07c87 100644 --- a/sway/commands/scratchpad.c +++ b/sway/commands/scratchpad.c @@ -2,7 +2,6 @@ #include "sway/commands.h" #include "sway/config.h" #include "sway/scratchpad.h" -#include "sway/server.h" #include "sway/tree/container.h" struct cmd_results *cmd_scratchpad(int argc, char **argv) { @@ -14,7 +13,7 @@ struct cmd_results *cmd_scratchpad(int argc, char **argv) { return cmd_results_new(CMD_INVALID, "scratchpad", "Expected 'scratchpad show'"); } - if (!server.scratchpad->length) { + if (!root_container.sway_root->scratchpad->length) { return cmd_results_new(CMD_INVALID, "scratchpad", "Scratchpad is empty"); } diff --git a/sway/criteria.c b/sway/criteria.c index 6af97d5b..c2e9c07e 100644 --- a/sway/criteria.c +++ b/sway/criteria.c @@ -227,8 +227,9 @@ list_t *criteria_get_views(struct criteria *criteria) { criteria_get_views_iterator, &data); // Scratchpad items which are hidden are not in the tree. - for (int i = 0; i < server.scratchpad->length; ++i) { - struct sway_container *con = server.scratchpad->items[i]; + for (int i = 0; i < root_container.sway_root->scratchpad->length; ++i) { + struct sway_container *con = + root_container.sway_root->scratchpad->items[i]; if (!con->parent) { criteria_get_views_iterator(con, &data); } diff --git a/sway/scratchpad.c b/sway/scratchpad.c index e1f931a4..1e836e7d 100644 --- a/sway/scratchpad.c +++ b/sway/scratchpad.c @@ -16,7 +16,7 @@ void scratchpad_add_container(struct sway_container *con) { return; } con->scratchpad = true; - list_add(server.scratchpad, con); + list_add(root_container.sway_root->scratchpad, con); struct sway_container *parent = con->parent; container_set_floating(con, true); @@ -32,9 +32,9 @@ void scratchpad_remove_container(struct sway_container *con) { return; } con->scratchpad = false; - for (int i = 0; i < server.scratchpad->length; ++i) { - if (server.scratchpad->items[i] == con) { - list_del(server.scratchpad, i); + for (int i = 0; i < root_container.sway_root->scratchpad->length; ++i) { + if (root_container.sway_root->scratchpad->items[i] == con) { + list_del(root_container.sway_root->scratchpad, i); break; } } @@ -104,7 +104,7 @@ static void scratchpad_hide(struct sway_container *con) { if (con == focus) { seat_set_focus(seat, seat_get_focus_inactive(seat, ws)); } - list_move_to_end(server.scratchpad, con); + list_move_to_end(root_container.sway_root->scratchpad, con); } void scratchpad_toggle_auto(void) { @@ -138,8 +138,9 @@ void scratchpad_toggle_auto(void) { // Check if there is a visible scratchpad window on another workspace. // In this case we move it to the current workspace. - for (int i = 0; i < server.scratchpad->length; ++i) { - struct sway_container *con = server.scratchpad->items[i]; + for (int i = 0; i < root_container.sway_root->scratchpad->length; ++i) { + struct sway_container *con = + root_container.sway_root->scratchpad->items[i]; if (con->parent) { wlr_log(WLR_DEBUG, "Moving a visible scratchpad window (%s) to this workspace", @@ -150,10 +151,11 @@ void scratchpad_toggle_auto(void) { } // Take the container at the bottom of the scratchpad list - if (!sway_assert(server.scratchpad->length, "Scratchpad is empty")) { + if (!sway_assert(root_container.sway_root->scratchpad->length, + "Scratchpad is empty")) { return; } - struct sway_container *con = server.scratchpad->items[0]; + struct sway_container *con = root_container.sway_root->scratchpad->items[0]; wlr_log(WLR_DEBUG, "Showing %s from list", con->name); scratchpad_show(con); } diff --git a/sway/server.c b/sway/server.c index 916e6b71..89dfbf8c 100644 --- a/sway/server.c +++ b/sway/server.c @@ -126,8 +126,6 @@ bool server_init(struct sway_server *server) { server->dirty_containers = create_list(); server->transactions = create_list(); - server->scratchpad = create_list(); - input_manager = input_manager_create(server); return true; } @@ -137,7 +135,6 @@ void server_fini(struct sway_server *server) { wl_display_destroy(server->wl_display); list_free(server->dirty_containers); list_free(server->transactions); - list_free(server->scratchpad); } bool server_start_backend(struct sway_server *server) { diff --git a/sway/tree/layout.c b/sway/tree/layout.c index af37611f..a2be0ef3 100644 --- a/sway/tree/layout.c +++ b/sway/tree/layout.c @@ -42,6 +42,7 @@ void layout_init(void) { wl_list_init(&root_container.sway_root->xwayland_unmanaged); wl_list_init(&root_container.sway_root->drag_icons); wl_signal_init(&root_container.sway_root->events.new_container); + root_container.sway_root->scratchpad = create_list(); root_container.sway_root->output_layout_change.notify = output_layout_handle_change; From 7ead2e85a7816a65e87bb7fded7414f571d56619 Mon Sep 17 00:00:00 2001 From: somdoron Date: Mon, 23 Jul 2018 19:29:32 +0300 Subject: [PATCH 025/148] fix crash on new output while swaylock is running --- sway/desktop/render.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sway/desktop/render.c b/sway/desktop/render.c index 4c85e516..7da54594 100644 --- a/sway/desktop/render.c +++ b/sway/desktop/render.c @@ -838,7 +838,7 @@ void output_render(struct sway_output *output, struct timespec *when, struct sway_view *fullscreen_view = workspace->current.ws_fullscreen; struct sway_seat *seat = input_manager_current_seat(input_manager); - if (output_has_opaque_lockscreen(output, seat)) { + if (output_has_opaque_lockscreen(output, seat) && seat->focused_layer) { struct wlr_layer_surface *wlr_layer_surface = seat->focused_layer; struct sway_layer_surface *sway_layer_surface = layer_from_wlr_layer_surface(seat->focused_layer); From 44e218574f96d46f36f6bd6e0b7625ae3c97593f Mon Sep 17 00:00:00 2001 From: Brian Ashworth Date: Mon, 23 Jul 2018 15:40:05 -0400 Subject: [PATCH 026/148] Add missing checks to fix 2339 --- sway/input/seat.c | 1 + 1 file changed, 1 insertion(+) diff --git a/sway/input/seat.c b/sway/input/seat.c index 273223db..fc9e54b6 100644 --- a/sway/input/seat.c +++ b/sway/input/seat.c @@ -628,6 +628,7 @@ void seat_set_focus_warp(struct sway_seat *seat, if (last_workspace && last_workspace == new_workspace && last_workspace->sway_workspace->fullscreen + && container && container->type == C_VIEW && !container->sway_view->is_fullscreen) { return; } From 0db6f2ef58fe81375219f9cc2e650661f6bd02ce Mon Sep 17 00:00:00 2001 From: Ryan Dwyer Date: Tue, 24 Jul 2018 09:09:49 +1000 Subject: [PATCH 027/148] Fix some cases where the cursor doesn't update cursor_set_image only uploads the named image if it doesn't match the previous named image. This means when setting the cursor image to a surface as given by a client, we have to clear the currently stored image. --- sway/input/cursor.c | 1 + 1 file changed, 1 insertion(+) diff --git a/sway/input/cursor.c b/sway/input/cursor.c index 771ad01d..65d04cac 100644 --- a/sway/input/cursor.c +++ b/sway/input/cursor.c @@ -726,6 +726,7 @@ static void handle_request_set_cursor(struct wl_listener *listener, wlr_cursor_set_surface(cursor->cursor, event->surface, event->hotspot_x, event->hotspot_y); + cursor->image = NULL; cursor->image_client = focused_client; } From 754372c3de1f5b357850cb58cc8ddc1aee596bd6 Mon Sep 17 00:00:00 2001 From: frsfnrrg Date: Tue, 17 Jul 2018 22:01:06 -0400 Subject: [PATCH 028/148] Parse mouse binding options First, the existing sway_binding structure is given an enumerated type code. As all flags to bindsym/bindcode are boolean, a single uint32 is used to hold all flags. The _BORDER, _CONTENTS, _TITLEBAR flags, when active, indicate in which part of a container the binding can trigger; to localize complexity, they do not overlap with the command line arguments, which center around _TITLEBAR being set by default. The keyboard handling code is adjusted for this change, as is binding_key_compare; note that BINDING_LOCKED is *not* part of the key portion of the binding. Next, list of mouse bindings is introduced and cleaned up. Finally, the binding command parsing code is extended to handle the case where bindsym is used to describe a mouse binding rather than a keysym binding; the difference between the two may be detected as late as when the first key/button is parsed, or as early as the first flag. As bindings can have multiple keycodes/keysyms/buttons, mixed keysym/button sequences are prohibited. --- include/sway/config.h | 21 +++++- sway/commands/bind.c | 152 +++++++++++++++++++++++++++++------------- sway/config.c | 7 ++ sway/input/keyboard.c | 6 +- 4 files changed, 134 insertions(+), 52 deletions(-) diff --git a/include/sway/config.h b/include/sway/config.h index b8da29c5..92536d10 100644 --- a/include/sway/config.h +++ b/include/sway/config.h @@ -22,14 +22,28 @@ struct sway_variable { char *value; }; + +enum binding_input_type { + BINDING_KEYCODE, + BINDING_KEYSYM, + BINDING_MOUSE, +}; + +enum binding_flags { + BINDING_RELEASE=1, + BINDING_LOCKED=2, // keyboard only + BINDING_BORDER=4, // mouse only; trigger on container border + BINDING_CONTENTS=8, // mouse only; trigger on container contents + BINDING_TITLEBAR=16 // mouse only; trigger on container titlebar +}; + /** * A key binding and an associated command. */ struct sway_binding { + enum binding_input_type type; int order; - bool release; - bool locked; - bool bindcode; + uint32_t flags; list_t *keys; // sorted in ascending order uint32_t modifiers; char *command; @@ -50,6 +64,7 @@ struct sway_mode { char *name; list_t *keysym_bindings; list_t *keycode_bindings; + list_t *mouse_bindings; bool pango; }; diff --git a/sway/commands/bind.c b/sway/commands/bind.c index 83e9e432..6910237f 100644 --- a/sway/commands/bind.c +++ b/sway/commands/bind.c @@ -34,11 +34,14 @@ void free_sway_binding(struct sway_binding *binding) { */ static bool binding_key_compare(struct sway_binding *binding_a, struct sway_binding *binding_b) { - if (binding_a->release != binding_b->release) { + if (binding_a->type != binding_b->type) { return false; } - if (binding_a->bindcode != binding_b->bindcode) { + uint32_t conflict_generating_flags = BINDING_RELEASE | BINDING_BORDER + | BINDING_CONTENTS | BINDING_TITLEBAR; + if ((binding_a->flags & conflict_generating_flags) != + (binding_b->flags & conflict_generating_flags)) { return false; } @@ -69,6 +72,66 @@ static int key_qsort_cmp(const void *keyp_a, const void *keyp_b) { return (key_a < key_b) ? -1 : ((key_a > key_b) ? 1 : 0); } + +/** + * From a keycode, bindcode, or bindsym name and the most likely binding type, + * identify the appropriate numeric value corresponding to the key. Return NULL + * and set *key_val if successful, otherwise return a specific error. Change + * the value of *type if the initial type guess was incorrect and if this + * was the first identified key. + */ +static struct cmd_results *identify_key(const char* name, bool first_key, + uint32_t* key_val, enum binding_input_type* type) { + if (*type == BINDING_KEYCODE) { + // check for keycode + xkb_keycode_t keycode = strtol(name, NULL, 10); + if (!xkb_keycode_is_legal_ext(keycode)) { + return cmd_results_new(CMD_INVALID, "bindcode", + "Invalid keycode '%s'", name); + } + *key_val = keycode; + } else { + // check for keysym + xkb_keysym_t keysym = xkb_keysym_from_name(name, + XKB_KEYSYM_CASE_INSENSITIVE); + + // Check for mouse binding + uint32_t button = 0; + if (strncasecmp(name, "button", strlen("button")) == 0 && + strlen(name) == strlen("button0")) { + button = name[strlen("button")] - '1' + BTN_LEFT; + } + + if (*type == BINDING_KEYSYM) { + if (button) { + if (first_key) { + *type = BINDING_MOUSE; + *key_val = button; + } else { + return cmd_results_new(CMD_INVALID, "bindsym", + "Mixed button '%s' into key sequence", name); + } + } else if (keysym) { + *key_val = keysym; + } else { + return cmd_results_new(CMD_INVALID, "bindsym", + "Unknown key '%s'", name); + } + } else { + if (button) { + *key_val = button; + } else if (keysym) { + return cmd_results_new(CMD_INVALID, "bindsym", + "Mixed keysym '%s' into button sequence", name); + } else { + return cmd_results_new(CMD_INVALID, "bindsym", + "Unknown button '%s'", name); + } + } + } + return NULL; +} + static struct cmd_results *cmd_bindsym_or_bindcode(int argc, char **argv, bool bindcode) { const char *bindtype = bindcode ? "bindcode" : "bindsym"; @@ -85,22 +148,34 @@ static struct cmd_results *cmd_bindsym_or_bindcode(int argc, char **argv, } binding->keys = create_list(); binding->modifiers = 0; - binding->release = false; - binding->locked = false; - binding->bindcode = bindcode; + binding->flags = 0; + binding->type = bindcode ? BINDING_KEYCODE : BINDING_KEYSYM; + + bool exclude_titlebar = false; // Handle --release and --locked while (argc > 0) { if (strcmp("--release", argv[0]) == 0) { - binding->release = true; + binding->flags |= BINDING_RELEASE; } else if (strcmp("--locked", argv[0]) == 0) { - binding->locked = true; + binding->flags |= BINDING_LOCKED; + } else if (strcmp("--whole-window", argv[0]) == 0) { + binding->flags |= BINDING_BORDER | BINDING_CONTENTS | BINDING_TITLEBAR; + } else if (strcmp("--border", argv[0]) == 0) { + binding->flags |= BINDING_BORDER; + } else if (strcmp("--exclude-titlebar", argv[0]) == 0) { + exclude_titlebar = true; } else { break; } argv++; argc--; } + if (binding->flags & (BINDING_BORDER | BINDING_CONTENTS | BINDING_TITLEBAR) + || exclude_titlebar) { + binding->type = BINDING_MOUSE; + } + if (argc < 2) { free_sway_binding(binding); return cmd_results_new(CMD_FAILURE, bindtype, @@ -119,64 +194,47 @@ static struct cmd_results *cmd_bindsym_or_bindcode(int argc, char **argv, continue; } - xkb_keycode_t keycode; - xkb_keysym_t keysym; - if (bindcode) { - // parse keycode - keycode = (int)strtol(split->items[i], NULL, 10); - if (!xkb_keycode_is_legal_ext(keycode)) { - error = - cmd_results_new(CMD_INVALID, "bindcode", - "Invalid keycode '%s'", (char *)split->items[i]); - free_sway_binding(binding); - list_free(split); - return error; - } - } else { - // Check for xkb key - keysym = xkb_keysym_from_name(split->items[i], - XKB_KEYSYM_CASE_INSENSITIVE); - - // Check for mouse binding - if (strncasecmp(split->items[i], "button", strlen("button")) == 0 && - strlen(split->items[i]) == strlen("button0")) { - keysym = ((char *)split->items[i])[strlen("button")] - '1' + BTN_LEFT; - } - if (!keysym) { - struct cmd_results *ret = cmd_results_new(CMD_INVALID, "bindsym", - "Unknown key '%s'", (char *)split->items[i]); - free_sway_binding(binding); - free_flat_list(split); - return ret; - } + // Identify the key and possibly change binding->type + uint32_t key_val = 0; + error = identify_key(split->items[i], binding->keys->length == 0, + &key_val, &binding->type); + if (error) { + free_sway_binding(binding); + list_free(split); + return error; } + uint32_t *key = calloc(1, sizeof(uint32_t)); if (!key) { free_sway_binding(binding); free_flat_list(split); return cmd_results_new(CMD_FAILURE, bindtype, - "Unable to allocate binding"); + "Unable to allocate binding key"); } - - if (bindcode) { - *key = (uint32_t)keycode; - } else { - *key = (uint32_t)keysym; - } - + *key = key_val; list_add(binding->keys, key); } free_flat_list(split); binding->order = binding_order++; + // refine region of interest for mouse binding once we are certain + // that this is one + if (exclude_titlebar) { + binding->flags &= ~BINDING_TITLEBAR; + } else if (binding->type == BINDING_MOUSE) { + binding->flags |= BINDING_TITLEBAR; + } + // sort ascending list_qsort(binding->keys, key_qsort_cmp); list_t *mode_bindings; - if (bindcode) { + if (binding->type == BINDING_KEYCODE) { mode_bindings = config->current_mode->keycode_bindings; - } else { + } else if (binding->type == BINDING_KEYSYM) { mode_bindings = config->current_mode->keysym_bindings; + } else { + mode_bindings = config->current_mode->mouse_bindings; } // overwrite the binding if it already exists diff --git a/sway/config.c b/sway/config.c index ed624bfa..c2310ff7 100644 --- a/sway/config.c +++ b/sway/config.c @@ -56,6 +56,12 @@ static void free_mode(struct sway_mode *mode) { } list_free(mode->keycode_bindings); } + if (mode->mouse_bindings) { + for (i = 0; i < mode->mouse_bindings->length; i++) { + free_sway_binding(mode->mouse_bindings->items[i]); + } + list_free(mode->mouse_bindings); + } free(mode); } @@ -172,6 +178,7 @@ static void config_defaults(struct sway_config *config) { strcpy(config->current_mode->name, "default"); if (!(config->current_mode->keysym_bindings = create_list())) goto cleanup; if (!(config->current_mode->keycode_bindings = create_list())) goto cleanup; + if (!(config->current_mode->mouse_bindings = create_list())) goto cleanup; list_add(config->modes, config->current_mode); config->floating_mod = 0; diff --git a/sway/input/keyboard.c b/sway/input/keyboard.c index ede38519..e6c5c335 100644 --- a/sway/input/keyboard.c +++ b/sway/input/keyboard.c @@ -88,11 +88,13 @@ static void get_active_binding(const struct sway_shortcut_state *state, uint32_t modifiers, bool release, bool locked) { for (int i = 0; i < bindings->length; ++i) { struct sway_binding *binding = bindings->items[i]; + bool binding_locked = binding->flags | BINDING_LOCKED; + bool binding_release = binding->flags | BINDING_RELEASE; if (modifiers ^ binding->modifiers || state->npressed != (size_t)binding->keys->length || - locked > binding->locked || - release != binding->release) { + release != binding_release || + locked > binding_locked) { continue; } From 863914ec9574eb58cb0746d59f216997c4863cdf Mon Sep 17 00:00:00 2001 From: Brian Ashworth Date: Mon, 23 Jul 2018 15:04:46 -0400 Subject: [PATCH 029/148] Switch to using a function to parse booleans --- common/util.c | 15 +++++++++++++++ include/util.h | 8 ++++++++ sway/commands/focus_follows_mouse.c | 4 +++- sway/commands/focus_wrapping.c | 12 +++++------- sway/commands/force_focus_wrapping.c | 8 +++----- sway/commands/fullscreen.c | 14 ++++---------- sway/commands/input/drag_lock.c | 9 +++------ sway/commands/input/dwt.c | 9 +++------ sway/commands/input/left_handed.c | 11 ++--------- sway/commands/input/middle_emulation.c | 9 +++------ sway/commands/input/natural_scroll.c | 11 ++--------- sway/commands/input/tap.c | 9 +++------ sway/commands/output/dpms.c | 8 +++----- sway/commands/show_marks.c | 10 ++-------- sway/commands/urgent.c | 10 +++------- 15 files changed, 62 insertions(+), 85 deletions(-) diff --git a/common/util.c b/common/util.c index e8a88772..33a0c77e 100644 --- a/common/util.c +++ b/common/util.c @@ -123,6 +123,21 @@ uint32_t parse_color(const char *color) { return res; } +bool parse_boolean(const char *boolean, const bool current) { + if (strcmp(boolean, "1") == 0 + || strcmp(boolean, "yes") == 0 + || strcmp(boolean, "on") == 0 + || strcmp(boolean, "true") == 0 + || strcmp(boolean, "enable") == 0 + || strcmp(boolean, "enabled") == 0 + || strcmp(boolean, "active") == 0) { + return true; + } else if (strcmp(boolean, "toggle") == 0) { + return !current; + } + return false; +} + char* resolve_path(const char* path) { struct stat sb; ssize_t r; diff --git a/include/util.h b/include/util.h index f68deae8..40f2c7b1 100644 --- a/include/util.h +++ b/include/util.h @@ -50,6 +50,14 @@ pid_t get_parent_pid(pid_t pid); */ uint32_t parse_color(const char *color); +/** + * Given a string that represents a boolean, return the boolean value. This + * function also takes in the current boolean value to support toggling. If + * toggling is not desired, pass in true for current so that toggling values + * get parsed as not true. + */ +bool parse_boolean(const char *boolean, const bool current); + /** * Given a path string, recurseively resolves any symlinks to their targets * (which may be a file, directory) and returns the result. diff --git a/sway/commands/focus_follows_mouse.c b/sway/commands/focus_follows_mouse.c index 661e7852..0b0e334c 100644 --- a/sway/commands/focus_follows_mouse.c +++ b/sway/commands/focus_follows_mouse.c @@ -1,12 +1,14 @@ #include #include #include "sway/commands.h" +#include "util.h" struct cmd_results *cmd_focus_follows_mouse(int argc, char **argv) { struct cmd_results *error = NULL; if ((error = checkarg(argc, "focus_follows_mouse", EXPECTED_EQUAL_TO, 1))) { return error; } - config->focus_follows_mouse = !strcasecmp(argv[0], "yes"); + config->focus_follows_mouse = + parse_boolean(argv[0], config->focus_follows_mouse); return cmd_results_new(CMD_SUCCESS, NULL, NULL); } diff --git a/sway/commands/focus_wrapping.c b/sway/commands/focus_wrapping.c index 0a9e0bf2..15704228 100644 --- a/sway/commands/focus_wrapping.c +++ b/sway/commands/focus_wrapping.c @@ -1,6 +1,7 @@ #include #include "sway/commands.h" #include "sway/config.h" +#include "util.h" struct cmd_results *cmd_focus_wrapping(int argc, char **argv) { struct cmd_results *error = NULL; @@ -8,15 +9,12 @@ struct cmd_results *cmd_focus_wrapping(int argc, char **argv) { return error; } - if (strcasecmp(argv[0], "no") == 0) { - config->focus_wrapping = WRAP_NO; - } else if (strcasecmp(argv[0], "yes") == 0) { - config->focus_wrapping = WRAP_YES; - } else if (strcasecmp(argv[0], "force") == 0) { + if (strcmp(argv[0], "force") == 0) { config->focus_wrapping = WRAP_FORCE; + } else if (parse_boolean(argv[0], config->focus_wrapping == WRAP_YES)) { + config->focus_wrapping = WRAP_YES; } else { - return cmd_results_new(CMD_INVALID, "focus_wrapping", - "Expected 'focus_wrapping yes|no|force'"); + config->focus_wrapping = WRAP_NO; } return cmd_results_new(CMD_SUCCESS, NULL, NULL); diff --git a/sway/commands/force_focus_wrapping.c b/sway/commands/force_focus_wrapping.c index bc1d067f..0892d9e9 100644 --- a/sway/commands/force_focus_wrapping.c +++ b/sway/commands/force_focus_wrapping.c @@ -1,6 +1,7 @@ #include #include "sway/commands.h" #include "sway/config.h" +#include "util.h" struct cmd_results *cmd_force_focus_wrapping(int argc, char **argv) { struct cmd_results *error = @@ -9,13 +10,10 @@ struct cmd_results *cmd_force_focus_wrapping(int argc, char **argv) { return error; } - if (strcasecmp(argv[0], "no") == 0) { - config->focus_wrapping = WRAP_YES; - } else if (strcasecmp(argv[0], "yes") == 0) { + if (parse_boolean(argv[0], config->focus_wrapping == WRAP_FORCE)) { config->focus_wrapping = WRAP_FORCE; } else { - return cmd_results_new(CMD_INVALID, "force_focus_wrapping", - "Expected 'force_focus_wrapping yes|no'"); + config->focus_wrapping = WRAP_YES; } return cmd_results_new(CMD_SUCCESS, NULL, NULL); diff --git a/sway/commands/fullscreen.c b/sway/commands/fullscreen.c index 0b5beaa2..b423fd23 100644 --- a/sway/commands/fullscreen.c +++ b/sway/commands/fullscreen.c @@ -5,6 +5,7 @@ #include "sway/tree/container.h" #include "sway/tree/view.h" #include "sway/tree/layout.h" +#include "util.h" struct cmd_results *cmd_fullscreen(int argc, char **argv) { struct cmd_results *error = NULL; @@ -18,17 +19,10 @@ struct cmd_results *cmd_fullscreen(int argc, char **argv) { "Only views can fullscreen"); } struct sway_view *view = container->sway_view; - bool wants_fullscreen; + bool wants_fullscreen = !view->is_fullscreen; - if (argc == 0 || strcmp(argv[0], "toggle") == 0) { - wants_fullscreen = !view->is_fullscreen; - } else if (strcmp(argv[0], "enable") == 0) { - wants_fullscreen = true; - } else if (strcmp(argv[0], "disable") == 0) { - wants_fullscreen = false; - } else { - return cmd_results_new(CMD_INVALID, "fullscreen", - "Expected 'fullscreen' or 'fullscreen '"); + if (argc) { + wants_fullscreen = parse_boolean(argv[0], view->is_fullscreen); } view_set_fullscreen(view, wants_fullscreen); diff --git a/sway/commands/input/drag_lock.c b/sway/commands/input/drag_lock.c index 9e32816f..f9ddeef2 100644 --- a/sway/commands/input/drag_lock.c +++ b/sway/commands/input/drag_lock.c @@ -3,6 +3,7 @@ #include "sway/config.h" #include "sway/commands.h" #include "sway/input/input-manager.h" +#include "util.h" struct cmd_results *input_cmd_drag_lock(int argc, char **argv) { struct cmd_results *error = NULL; @@ -18,14 +19,10 @@ struct cmd_results *input_cmd_drag_lock(int argc, char **argv) { struct input_config *new_config = new_input_config(current_input_config->identifier); - if (strcasecmp(argv[0], "enabled") == 0) { + if (parse_boolean(argv[0], true)) { new_config->drag_lock = LIBINPUT_CONFIG_DRAG_LOCK_ENABLED; - } else if (strcasecmp(argv[0], "disabled") == 0) { - new_config->drag_lock = LIBINPUT_CONFIG_DRAG_LOCK_DISABLED; } else { - free_input_config(new_config); - return cmd_results_new(CMD_INVALID, "drag_lock", - "Expected 'drag_lock '"); + new_config->drag_lock = LIBINPUT_CONFIG_DRAG_LOCK_DISABLED; } apply_input_config(new_config); diff --git a/sway/commands/input/dwt.c b/sway/commands/input/dwt.c index 73937507..15134268 100644 --- a/sway/commands/input/dwt.c +++ b/sway/commands/input/dwt.c @@ -3,6 +3,7 @@ #include "sway/config.h" #include "sway/commands.h" #include "sway/input/input-manager.h" +#include "util.h" struct cmd_results *input_cmd_dwt(int argc, char **argv) { struct cmd_results *error = NULL; @@ -17,14 +18,10 @@ struct cmd_results *input_cmd_dwt(int argc, char **argv) { struct input_config *new_config = new_input_config(current_input_config->identifier); - if (strcasecmp(argv[0], "enabled") == 0) { + if (parse_boolean(argv[0], true)) { new_config->dwt = LIBINPUT_CONFIG_DWT_ENABLED; - } else if (strcasecmp(argv[0], "disabled") == 0) { - new_config->dwt = LIBINPUT_CONFIG_DWT_DISABLED; } else { - free_input_config(new_config); - return cmd_results_new(CMD_INVALID, "dwt", - "Expected 'dwt '"); + new_config->dwt = LIBINPUT_CONFIG_DWT_DISABLED; } apply_input_config(new_config); diff --git a/sway/commands/input/left_handed.c b/sway/commands/input/left_handed.c index 769ce98c..e770043a 100644 --- a/sway/commands/input/left_handed.c +++ b/sway/commands/input/left_handed.c @@ -3,6 +3,7 @@ #include "sway/config.h" #include "sway/commands.h" #include "sway/input/input-manager.h" +#include "util.h" struct cmd_results *input_cmd_left_handed(int argc, char **argv) { struct cmd_results *error = NULL; @@ -18,15 +19,7 @@ struct cmd_results *input_cmd_left_handed(int argc, char **argv) { struct input_config *new_config = new_input_config(current_input_config->identifier); - if (strcasecmp(argv[0], "enabled") == 0) { - new_config->left_handed = 1; - } else if (strcasecmp(argv[0], "disabled") == 0) { - new_config->left_handed = 0; - } else { - free_input_config(new_config); - return cmd_results_new(CMD_INVALID, "left_handed", - "Expected 'left_handed '"); - } + new_config->left_handed = parse_boolean(argv[0], true); apply_input_config(new_config); return cmd_results_new(CMD_SUCCESS, NULL, NULL); diff --git a/sway/commands/input/middle_emulation.c b/sway/commands/input/middle_emulation.c index 7ca01629..414d4d2b 100644 --- a/sway/commands/input/middle_emulation.c +++ b/sway/commands/input/middle_emulation.c @@ -3,6 +3,7 @@ #include "sway/config.h" #include "sway/commands.h" #include "sway/input/input-manager.h" +#include "util.h" struct cmd_results *input_cmd_middle_emulation(int argc, char **argv) { struct cmd_results *error = NULL; @@ -18,15 +19,11 @@ struct cmd_results *input_cmd_middle_emulation(int argc, char **argv) { struct input_config *new_config = new_input_config(current_input_config->identifier); - if (strcasecmp(argv[0], "enabled") == 0) { + if (parse_boolean(argv[0], true)) { new_config->middle_emulation = LIBINPUT_CONFIG_MIDDLE_EMULATION_ENABLED; - } else if (strcasecmp(argv[0], "disabled") == 0) { + } else { new_config->middle_emulation = LIBINPUT_CONFIG_MIDDLE_EMULATION_DISABLED; - } else { - free_input_config(new_config); - return cmd_results_new(CMD_INVALID, "middle_emulation", - "Expected 'middle_emulation '"); } apply_input_config(new_config); diff --git a/sway/commands/input/natural_scroll.c b/sway/commands/input/natural_scroll.c index 55236790..77c3ff00 100644 --- a/sway/commands/input/natural_scroll.c +++ b/sway/commands/input/natural_scroll.c @@ -3,6 +3,7 @@ #include "sway/config.h" #include "sway/commands.h" #include "sway/input/input-manager.h" +#include "util.h" struct cmd_results *input_cmd_natural_scroll(int argc, char **argv) { struct cmd_results *error = NULL; @@ -18,15 +19,7 @@ struct cmd_results *input_cmd_natural_scroll(int argc, char **argv) { struct input_config *new_config = new_input_config(current_input_config->identifier); - if (strcasecmp(argv[0], "enabled") == 0) { - new_config->natural_scroll = 1; - } else if (strcasecmp(argv[0], "disabled") == 0) { - new_config->natural_scroll = 0; - } else { - free_input_config(new_config); - return cmd_results_new(CMD_INVALID, "natural_scroll", - "Expected 'natural_scroll '"); - } + new_config->natural_scroll = parse_boolean(argv[0], true); apply_input_config(new_config); return cmd_results_new(CMD_SUCCESS, NULL, NULL); diff --git a/sway/commands/input/tap.c b/sway/commands/input/tap.c index a8d1a10c..ac3b8237 100644 --- a/sway/commands/input/tap.c +++ b/sway/commands/input/tap.c @@ -4,6 +4,7 @@ #include "sway/commands.h" #include "sway/input/input-manager.h" #include "log.h" +#include "util.h" struct cmd_results *input_cmd_tap(int argc, char **argv) { struct cmd_results *error = NULL; @@ -18,14 +19,10 @@ struct cmd_results *input_cmd_tap(int argc, char **argv) { struct input_config *new_config = new_input_config(current_input_config->identifier); - if (strcasecmp(argv[0], "enabled") == 0) { + if (parse_boolean(argv[0], true)) { new_config->tap = LIBINPUT_CONFIG_TAP_ENABLED; - } else if (strcasecmp(argv[0], "disabled") == 0) { - new_config->tap = LIBINPUT_CONFIG_TAP_DISABLED; } else { - free_input_config(new_config); - return cmd_results_new(CMD_INVALID, "tap", - "Expected 'tap '"); + new_config->tap = LIBINPUT_CONFIG_TAP_DISABLED; } wlr_log(WLR_DEBUG, "apply-tap for device: %s", diff --git a/sway/commands/output/dpms.c b/sway/commands/output/dpms.c index 0959ea6b..3492061e 100644 --- a/sway/commands/output/dpms.c +++ b/sway/commands/output/dpms.c @@ -1,5 +1,6 @@ #include "sway/commands.h" #include "sway/config.h" +#include "util.h" struct cmd_results *output_cmd_dpms(int argc, char **argv) { if (!config->handler_context.output_config) { @@ -9,13 +10,10 @@ struct cmd_results *output_cmd_dpms(int argc, char **argv) { return cmd_results_new(CMD_INVALID, "output", "Missing dpms argument."); } - if (strcmp(*argv, "on") == 0) { + if (parse_boolean(argv[0], true)) { config->handler_context.output_config->dpms_state = DPMS_ON; - } else if (strcmp(*argv, "off") == 0) { - config->handler_context.output_config->dpms_state = DPMS_OFF; } else { - return cmd_results_new(CMD_INVALID, "output", - "Invalid dpms state, valid states are on/off."); + config->handler_context.output_config->dpms_state = DPMS_OFF; } config->handler_context.leftovers.argc = argc - 1; diff --git a/sway/commands/show_marks.c b/sway/commands/show_marks.c index c7fdc538..434a0e27 100644 --- a/sway/commands/show_marks.c +++ b/sway/commands/show_marks.c @@ -7,6 +7,7 @@ #include "list.h" #include "log.h" #include "stringop.h" +#include "util.h" static void rebuild_marks_iterator(struct sway_container *con, void *data) { if (con->type == C_VIEW) { @@ -20,14 +21,7 @@ struct cmd_results *cmd_show_marks(int argc, char **argv) { return error; } - if (strcmp(*argv, "yes") == 0) { - config->show_marks = true; - } else if (strcmp(*argv, "no") == 0) { - config->show_marks = false; - } else { - return cmd_results_new(CMD_INVALID, "show_marks", - "Expected 'show_marks '"); - } + config->show_marks = parse_boolean(argv[0], config->show_marks); if (config->show_marks) { container_for_each_descendant_dfs(&root_container, diff --git a/sway/commands/urgent.c b/sway/commands/urgent.c index d199858a..51c497c4 100644 --- a/sway/commands/urgent.c +++ b/sway/commands/urgent.c @@ -5,6 +5,7 @@ #include "sway/tree/container.h" #include "sway/tree/view.h" #include "sway/tree/layout.h" +#include "util.h" struct cmd_results *cmd_urgent(int argc, char **argv) { struct cmd_results *error = NULL; @@ -19,17 +20,12 @@ struct cmd_results *cmd_urgent(int argc, char **argv) { } struct sway_view *view = container->sway_view; - if (strcmp(argv[0], "enable") == 0) { - view_set_urgent(view, true); - } else if (strcmp(argv[0], "disable") == 0) { - view_set_urgent(view, false); - } else if (strcmp(argv[0], "allow") == 0) { + if (strcmp(argv[0], "allow") == 0) { view->allow_request_urgent = true; } else if (strcmp(argv[0], "deny") == 0) { view->allow_request_urgent = false; } else { - return cmd_results_new(CMD_INVALID, "urgent", - "Expected 'urgent '"); + view_set_urgent(view, parse_boolean(argv[0], view_is_urgent(view))); } return cmd_results_new(CMD_SUCCESS, NULL, NULL); From d56d62c1c0b504d6c414f4b970a4a996cdadd879 Mon Sep 17 00:00:00 2001 From: Brian Ashworth Date: Mon, 23 Jul 2018 16:22:09 -0400 Subject: [PATCH 030/148] Remove unneeded const --- common/util.c | 2 +- include/util.h | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/common/util.c b/common/util.c index 33a0c77e..3fa0c03f 100644 --- a/common/util.c +++ b/common/util.c @@ -123,7 +123,7 @@ uint32_t parse_color(const char *color) { return res; } -bool parse_boolean(const char *boolean, const bool current) { +bool parse_boolean(const char *boolean, bool current) { if (strcmp(boolean, "1") == 0 || strcmp(boolean, "yes") == 0 || strcmp(boolean, "on") == 0 diff --git a/include/util.h b/include/util.h index 40f2c7b1..bda941ce 100644 --- a/include/util.h +++ b/include/util.h @@ -56,7 +56,7 @@ uint32_t parse_color(const char *color); * toggling is not desired, pass in true for current so that toggling values * get parsed as not true. */ -bool parse_boolean(const char *boolean, const bool current); +bool parse_boolean(const char *boolean, bool current); /** * Given a path string, recurseively resolves any symlinks to their targets From 9ec1d6cf79e6f9c3233f577c6fddeaeb21bb1bfc Mon Sep 17 00:00:00 2001 From: Brian Ashworth Date: Mon, 23 Jul 2018 21:37:53 -0400 Subject: [PATCH 031/148] Address review comments on parse_boolean --- common/util.c | 17 +++++++++-------- sway/commands/focus_wrapping.c | 2 +- 2 files changed, 10 insertions(+), 9 deletions(-) diff --git a/common/util.c b/common/util.c index 3fa0c03f..467aa4b5 100644 --- a/common/util.c +++ b/common/util.c @@ -124,17 +124,18 @@ uint32_t parse_color(const char *color) { } bool parse_boolean(const char *boolean, bool current) { - if (strcmp(boolean, "1") == 0 - || strcmp(boolean, "yes") == 0 - || strcmp(boolean, "on") == 0 - || strcmp(boolean, "true") == 0 - || strcmp(boolean, "enable") == 0 - || strcmp(boolean, "enabled") == 0 - || strcmp(boolean, "active") == 0) { + if (strcasecmp(boolean, "1") == 0 + || strcasecmp(boolean, "yes") == 0 + || strcasecmp(boolean, "on") == 0 + || strcasecmp(boolean, "true") == 0 + || strcasecmp(boolean, "enable") == 0 + || strcasecmp(boolean, "enabled") == 0 + || strcasecmp(boolean, "active") == 0) { return true; - } else if (strcmp(boolean, "toggle") == 0) { + } else if (strcasecmp(boolean, "toggle") == 0) { return !current; } + // All other values are false to match i3 return false; } diff --git a/sway/commands/focus_wrapping.c b/sway/commands/focus_wrapping.c index 15704228..562ee4f9 100644 --- a/sway/commands/focus_wrapping.c +++ b/sway/commands/focus_wrapping.c @@ -9,7 +9,7 @@ struct cmd_results *cmd_focus_wrapping(int argc, char **argv) { return error; } - if (strcmp(argv[0], "force") == 0) { + if (strcasecmp(argv[0], "force") == 0) { config->focus_wrapping = WRAP_FORCE; } else if (parse_boolean(argv[0], config->focus_wrapping == WRAP_YES)) { config->focus_wrapping = WRAP_YES; From 94dd8823a0081f7983dce368d5d093d1d3eeaefe Mon Sep 17 00:00:00 2001 From: frsfnrrg Date: Mon, 23 Jul 2018 21:38:29 -0400 Subject: [PATCH 032/148] Invoke mouse bindings The mouse binding logic is inspired/copied from the keyboard binding logic; we store a sorted list of the currently pressed buttons, and trigger a binding when the currently pressed (or just recently pressed, in the case of a release binding) buttons, as well as modifiers/container region, match those of a given binding. As the code to execute a binding is not very keyboard specific, keyboard_execute_command is renamed to seat_execute_command and moved to where the other binding handling functions are. The call to transaction_commit_dirty has been lifted out. --- include/sway/config.h | 2 + include/sway/input/cursor.h | 6 +++ sway/commands/bind.c | 16 ++++++ sway/input/cursor.c | 102 ++++++++++++++++++++++++++++++++++++ sway/input/keyboard.c | 42 +++++---------- 5 files changed, 140 insertions(+), 28 deletions(-) diff --git a/include/sway/config.h b/include/sway/config.h index 92536d10..bcd503a4 100644 --- a/include/sway/config.h +++ b/include/sway/config.h @@ -497,6 +497,8 @@ void free_sway_binding(struct sway_binding *sb); struct sway_binding *sway_binding_dup(struct sway_binding *sb); +void seat_execute_command(struct sway_seat *seat, struct sway_binding *binding); + void load_swaybars(); void invoke_swaybar(struct bar_config *bar); diff --git a/include/sway/input/cursor.h b/include/sway/input/cursor.h index b0a3a7c5..7ec45120 100644 --- a/include/sway/input/cursor.h +++ b/include/sway/input/cursor.h @@ -3,6 +3,8 @@ #include #include "sway/input/seat.h" +#define SWAY_CURSOR_PRESSED_BUTTONS_CAP 32 + struct sway_cursor { struct sway_seat *seat; struct wlr_cursor *cursor; @@ -29,6 +31,10 @@ struct sway_cursor { uint32_t tool_buttons; struct wl_listener request_set_cursor; + + // Mouse binding state + uint32_t pressed_buttons[SWAY_CURSOR_PRESSED_BUTTONS_CAP]; + size_t pressed_button_count; }; void sway_cursor_destroy(struct sway_cursor *cursor); diff --git a/sway/commands/bind.c b/sway/commands/bind.c index 6910237f..133fd089 100644 --- a/sway/commands/bind.c +++ b/sway/commands/bind.c @@ -267,3 +267,19 @@ struct cmd_results *cmd_bindsym(int argc, char **argv) { struct cmd_results *cmd_bindcode(int argc, char **argv) { return cmd_bindsym_or_bindcode(argc, argv, true); } + + +/** + * Execute the command associated to a binding + */ +void seat_execute_command(struct sway_seat *seat, struct sway_binding *binding) { + wlr_log(WLR_DEBUG, "running command for binding: %s", + binding->command); + config->handler_context.seat = seat; + struct cmd_results *results = execute_command(binding->command, NULL); + if (results->status != CMD_SUCCESS) { + wlr_log(WLR_DEBUG, "could not run command for binding: %s (%s)", + binding->command, results->error); + } + free_cmd_results(results); +} diff --git a/sway/input/cursor.c b/sway/input/cursor.c index 65d04cac..f1481936 100644 --- a/sway/input/cursor.c +++ b/sway/input/cursor.c @@ -469,6 +469,83 @@ static void dispatch_cursor_button_floating(struct sway_cursor *cursor, seat_pointer_notify_button(seat, time_msec, button, state); } +/** + * Remove a button (and duplicates) to the sorted list of currently pressed buttons + */ +static void state_erase_button(struct sway_cursor *cursor, uint32_t button) { + size_t j = 0; + for (size_t i = 0; i < cursor->pressed_button_count; ++i) { + if (i > j) { + cursor->pressed_buttons[j] = cursor->pressed_buttons[i]; + } + if (cursor->pressed_buttons[i] != button) { + ++j; + } + } + while (cursor->pressed_button_count > j) { + --cursor->pressed_button_count; + cursor->pressed_buttons[cursor->pressed_button_count] = 0; + } +} + +/** + * Add a button to the sorted list of currently pressed buttons, if there + * is space. + */ +static void state_add_button(struct sway_cursor *cursor, uint32_t button) { + if (cursor->pressed_button_count >= SWAY_CURSOR_PRESSED_BUTTONS_CAP) { + return; + } + size_t i = 0; + while (i < cursor->pressed_button_count && cursor->pressed_buttons[i] < button) { + ++i; + } + size_t j = cursor->pressed_button_count; + while (j > i) { + cursor->pressed_buttons[j] = cursor->pressed_buttons[j - 1]; + --j; + } + cursor->pressed_buttons[i] = button; + cursor->pressed_button_count++; +} + +/** + * Return the mouse binding which matches modifier, click location, release, + * and pressed button state, otherwise return null. + */ +static struct sway_binding* get_active_mouse_binding(const struct sway_cursor *cursor, + list_t *bindings, uint32_t modifiers, bool release, bool on_titlebar, + bool on_border, bool on_content) { + uint32_t click_region = (on_titlebar ? BINDING_TITLEBAR : 0) | + (on_border ? BINDING_BORDER : 0) | + (on_content ? BINDING_CONTENTS : 0); + + for (int i = 0; i < bindings->length; ++i) { + struct sway_binding *binding = bindings->items[i]; + if (modifiers ^ binding->modifiers || + cursor->pressed_button_count != (size_t)binding->keys->length || + release != (binding->flags & BINDING_RELEASE) || + !(click_region & binding->flags)) { + continue; + } + + bool match = true; + for (size_t j = 0; j < cursor->pressed_button_count; j++) { + uint32_t key = *(uint32_t *)binding->keys->items[j]; + if (key != cursor->pressed_buttons[j]) { + match = false; + break; + } + } + if (!match) { + continue; + } + + return binding; + } + return NULL; +} + void dispatch_cursor_button(struct sway_cursor *cursor, uint32_t time_msec, uint32_t button, enum wlr_button_state state) { if (cursor->seat->operation != OP_NONE && @@ -485,6 +562,31 @@ void dispatch_cursor_button(struct sway_cursor *cursor, double sx, sy; struct sway_container *cont = container_at_coords(cursor->seat, cursor->cursor->x, cursor->cursor->y, &surface, &sx, &sy); + + // Handle mouse bindings + bool on_border = find_resize_edge(cont, cursor) != WLR_EDGE_NONE; + bool on_contents = !on_border && surface; + bool on_titlebar = !on_border && !surface; + struct wlr_keyboard *keyboard = wlr_seat_get_keyboard(cursor->seat->wlr_seat); + uint32_t modifiers = keyboard ? wlr_keyboard_get_modifiers(keyboard) : 0; + + struct sway_binding *binding = NULL; + if (state == WLR_BUTTON_PRESSED) { + state_add_button(cursor, button); + binding = get_active_mouse_binding(cursor, + config->current_mode->mouse_bindings, modifiers, false, + on_titlebar, on_border, on_contents); + } else { + binding = get_active_mouse_binding(cursor, + config->current_mode->mouse_bindings, modifiers, true, + on_titlebar, on_border, on_contents); + state_erase_button(cursor, button); + } + if (binding) { + seat_execute_command(cursor->seat, binding); + // TODO: do we want to pass on the event? + } + if (surface && wlr_surface_is_layer_surface(surface)) { struct wlr_layer_surface *layer = wlr_layer_surface_from_wlr_surface(surface); diff --git a/sway/input/keyboard.c b/sway/input/keyboard.c index e6c5c335..49241db8 100644 --- a/sway/input/keyboard.c +++ b/sway/input/keyboard.c @@ -3,11 +3,11 @@ #include #include #include -#include "sway/desktop/transaction.h" -#include "sway/input/seat.h" -#include "sway/input/keyboard.h" -#include "sway/input/input-manager.h" #include "sway/commands.h" +#include "sway/desktop/transaction.h" +#include "sway/input/input-manager.h" +#include "sway/input/keyboard.h" +#include "sway/input/seat.h" #include "log.h" /** @@ -88,8 +88,8 @@ static void get_active_binding(const struct sway_shortcut_state *state, uint32_t modifiers, bool release, bool locked) { for (int i = 0; i < bindings->length; ++i) { struct sway_binding *binding = bindings->items[i]; - bool binding_locked = binding->flags | BINDING_LOCKED; - bool binding_release = binding->flags | BINDING_RELEASE; + bool binding_locked = binding->flags & BINDING_LOCKED; + bool binding_release = binding->flags & BINDING_RELEASE; if (modifiers ^ binding->modifiers || state->npressed != (size_t)binding->keys->length || @@ -120,23 +120,6 @@ static void get_active_binding(const struct sway_shortcut_state *state, } } -/** - * Execute the command associated to a binding - */ -static void keyboard_execute_command(struct sway_keyboard *keyboard, - struct sway_binding *binding) { - wlr_log(WLR_DEBUG, "running command for binding: %s", - binding->command); - config->handler_context.seat = keyboard->seat_device->sway_seat; - struct cmd_results *results = execute_command(binding->command, NULL); - transaction_commit_dirty(); - if (results->status != CMD_SUCCESS) { - wlr_log(WLR_DEBUG, "could not run command for binding: %s (%s)", - binding->command, results->error); - } - free_cmd_results(results); -} - /** * Execute a built-in, hardcoded compositor binding. These are triggered from a * single keysym. @@ -213,12 +196,13 @@ static size_t keyboard_keysyms_raw(struct sway_keyboard *keyboard, static void handle_keyboard_key(struct wl_listener *listener, void *data) { struct sway_keyboard *keyboard = wl_container_of(listener, keyboard, keyboard_key); - struct wlr_seat *wlr_seat = keyboard->seat_device->sway_seat->wlr_seat; + struct sway_seat* seat = keyboard->seat_device->sway_seat; + struct wlr_seat *wlr_seat = seat->wlr_seat; struct wlr_input_device *wlr_device = keyboard->seat_device->input_device->wlr_device; - wlr_idle_notify_activity(keyboard->seat_device->sway_seat->input->server->idle, wlr_seat); + wlr_idle_notify_activity(seat->input->server->idle, wlr_seat); struct wlr_event_keyboard_key *event = data; - bool input_inhibited = keyboard->seat_device->sway_seat->exclusive_client != NULL; + bool input_inhibited = seat->exclusive_client != NULL; // Identify new keycode, raw keysym(s), and translated keysym(s) xkb_keycode_t keycode = event->keycode + 8; @@ -268,7 +252,7 @@ static void handle_keyboard_key(struct wl_listener *listener, void *data) { // Execute stored release binding once no longer active if (keyboard->held_binding && binding_released != keyboard->held_binding && event->state == WLR_KEY_RELEASED) { - keyboard_execute_command(keyboard, keyboard->held_binding); + seat_execute_command(seat, keyboard->held_binding); handled = true; } if (binding_released != keyboard->held_binding) { @@ -292,7 +276,7 @@ static void handle_keyboard_key(struct wl_listener *listener, void *data) { raw_modifiers, false, input_inhibited); if (binding_pressed) { - keyboard_execute_command(keyboard, binding_pressed); + seat_execute_command(seat, binding_pressed); handled = true; } } @@ -314,6 +298,8 @@ static void handle_keyboard_key(struct wl_listener *listener, void *data) { wlr_seat_keyboard_notify_key(wlr_seat, event->time_msec, event->keycode, event->state); } + + transaction_commit_dirty(); } static void handle_keyboard_modifiers(struct wl_listener *listener, From ab0efebc3e74aeae529cfdf085ea0b7ea33dbfab Mon Sep 17 00:00:00 2001 From: frsfnrrg Date: Sun, 15 Jul 2018 10:35:56 -0400 Subject: [PATCH 033/148] Also extract first workspace name from bindcodes --- sway/tree/workspace.c | 167 ++++++++++++++++++++++-------------------- 1 file changed, 87 insertions(+), 80 deletions(-) diff --git a/sway/tree/workspace.c b/sway/tree/workspace.c index 622f01ec..e450b87f 100644 --- a/sway/tree/workspace.c +++ b/sway/tree/workspace.c @@ -107,6 +107,87 @@ static bool workspace_valid_on_output(const char *output_name, return true; } +static void workspace_name_from_binding(const struct sway_binding * binding, + const char* output_name, int *min_order, char **earliest_name) { + char *cmdlist = strdup(binding->command); + char *dup = cmdlist; + char *name = NULL; + + // workspace n + char *cmd = argsep(&cmdlist, " "); + if (cmdlist) { + name = argsep(&cmdlist, ",;"); + } + + if (strcmp("workspace", cmd) == 0 && name) { + char *_target = strdup(name); + _target = do_var_replacement(_target); + strip_quotes(_target); + while (isspace(*_target)) { + memmove(_target, _target+1, strlen(_target+1)); + } + wlr_log(WLR_DEBUG, "Got valid workspace command for target: '%s'", + _target); + + // Make sure that the command references an actual workspace + // not a command about workspaces + if (strcmp(_target, "next") == 0 || + strcmp(_target, "prev") == 0 || + strcmp(_target, "next_on_output") == 0 || + strcmp(_target, "prev_on_output") == 0 || + strcmp(_target, "number") == 0 || + strcmp(_target, "back_and_forth") == 0 || + strcmp(_target, "current") == 0) { + free(_target); + free(dup); + return; + } + + // If the command is workspace number , isolate the name + if (strncmp(_target, "number ", strlen("number ")) == 0) { + size_t length = strlen(_target) - strlen("number ") + 1; + char *temp = malloc(length); + strncpy(temp, _target + strlen("number "), length - 1); + temp[length - 1] = '\0'; + free(_target); + _target = temp; + wlr_log(WLR_DEBUG, "Isolated name from workspace number: '%s'", _target); + + // Make sure the workspace number doesn't already exist + if (workspace_by_number(_target)) { + free(_target); + free(dup); + return; + } + } + + // Make sure that the workspace doesn't already exist + if (workspace_by_name(_target)) { + free(_target); + free(dup); + return; + } + + // make sure that the workspace can appear on the given + // output + if (!workspace_valid_on_output(output_name, _target)) { + free(_target); + free(dup); + return; + } + + if (binding->order < *min_order) { + *min_order = binding->order; + free(*earliest_name); + *earliest_name = _target; + wlr_log(WLR_DEBUG, "Workspace: Found free name %s", _target); + } else { + free(_target); + } + } + free(dup); +} + char *workspace_next_name(const char *output_name) { wlr_log(WLR_DEBUG, "Workspace: Generating new workspace name for output %s", output_name); @@ -114,89 +195,15 @@ char *workspace_next_name(const char *output_name) { // if none are found/available then default to a number struct sway_mode *mode = config->current_mode; - // TODO: iterate over keycode bindings too int order = INT_MAX; char *target = NULL; for (int i = 0; i < mode->keysym_bindings->length; ++i) { - struct sway_binding *binding = mode->keysym_bindings->items[i]; - char *cmdlist = strdup(binding->command); - char *dup = cmdlist; - char *name = NULL; - - // workspace n - char *cmd = argsep(&cmdlist, " "); - if (cmdlist) { - name = argsep(&cmdlist, ",;"); - } - - if (strcmp("workspace", cmd) == 0 && name) { - char *_target = strdup(name); - _target = do_var_replacement(_target); - strip_quotes(_target); - while (isspace(*_target)) { - memmove(_target, _target+1, strlen(_target+1)); - } - wlr_log(WLR_DEBUG, "Got valid workspace command for target: '%s'", - _target); - - // Make sure that the command references an actual workspace - // not a command about workspaces - if (strcmp(_target, "next") == 0 || - strcmp(_target, "prev") == 0 || - strcmp(_target, "next_on_output") == 0 || - strcmp(_target, "prev_on_output") == 0 || - strcmp(_target, "number") == 0 || - strcmp(_target, "back_and_forth") == 0 || - strcmp(_target, "current") == 0) - { - free(_target); - free(dup); - continue; - } - - // If the command is workspace number , isolate the name - if (strncmp(_target, "number ", strlen("number ")) == 0) { - size_t length = strlen(_target) - strlen("number ") + 1; - char *temp = malloc(length); - strncpy(temp, _target + strlen("number "), length - 1); - temp[length - 1] = '\0'; - free(_target); - _target = temp; - wlr_log(WLR_DEBUG, "Isolated name from workspace number: '%s'", _target); - - // Make sure the workspace number doesn't already exist - if (workspace_by_number(_target)) { - free(_target); - free(dup); - continue; - } - } - - // Make sure that the workspace doesn't already exist - if (workspace_by_name(_target)) { - free(_target); - free(dup); - continue; - } - - // make sure that the workspace can appear on the given - // output - if (!workspace_valid_on_output(output_name, _target)) { - free(_target); - free(dup); - continue; - } - - if (binding->order < order) { - order = binding->order; - free(target); - target = _target; - wlr_log(WLR_DEBUG, "Workspace: Found free name %s", _target); - } else { - free(_target); - } - } - free(dup); + workspace_name_from_binding(mode->keysym_bindings->items[i], + output_name, &order, &target); + } + for (int i = 0; i < mode->keycode_bindings->length; ++i) { + workspace_name_from_binding(mode->keycode_bindings->items[i], + output_name, &order, &target); } if (target != NULL) { return target; From e7c10f1871ba64c234ff1df22828ca84ad28c67e Mon Sep 17 00:00:00 2001 From: Ryan Dwyer Date: Tue, 24 Jul 2018 20:16:54 +1000 Subject: [PATCH 034/148] Make mod+resize work in any direction This makes it so if you hold mod and right click on a surface to resize it, the resize direction is chosen based on which quarter of the surface you've clicked. The previous implementation only resized towards the bottom right. --- sway/input/cursor.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/sway/input/cursor.c b/sway/input/cursor.c index f1481936..8a6299cf 100644 --- a/sway/input/cursor.c +++ b/sway/input/cursor.c @@ -460,6 +460,12 @@ static void dispatch_cursor_button_floating(struct sway_cursor *cursor, bool resizing_via_mod = button == BTN_RIGHT && mod_pressed; if ((resizing_via_border || resizing_via_mod) && state == WLR_BUTTON_PRESSED) { + if (edge == WLR_EDGE_NONE) { + edge |= cursor->cursor->x > cont->x + cont->width / 2 ? + WLR_EDGE_RIGHT : WLR_EDGE_LEFT; + edge |= cursor->cursor->y > cont->y + cont->height / 2 ? + WLR_EDGE_BOTTOM : WLR_EDGE_TOP; + } seat_begin_resize(seat, cont, button, edge); return; } From 2f1b84a0f3fb9e765530878e08a25d3194a831bb Mon Sep 17 00:00:00 2001 From: Ryan Dwyer Date: Tue, 24 Jul 2018 21:12:24 +1000 Subject: [PATCH 035/148] Fix crash when clicking certain surfaces cont was NULL. --- sway/input/cursor.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sway/input/cursor.c b/sway/input/cursor.c index 8a6299cf..27597640 100644 --- a/sway/input/cursor.c +++ b/sway/input/cursor.c @@ -570,7 +570,7 @@ void dispatch_cursor_button(struct sway_cursor *cursor, cursor->cursor->x, cursor->cursor->y, &surface, &sx, &sy); // Handle mouse bindings - bool on_border = find_resize_edge(cont, cursor) != WLR_EDGE_NONE; + bool on_border = cont && (find_resize_edge(cont, cursor) != WLR_EDGE_NONE); bool on_contents = !on_border && surface; bool on_titlebar = !on_border && !surface; struct wlr_keyboard *keyboard = wlr_seat_get_keyboard(cursor->seat->wlr_seat); From c80258c3b394781c42cd4f2c161c705b2558c485 Mon Sep 17 00:00:00 2001 From: Drew DeVault Date: Tue, 24 Jul 2018 14:31:38 -0400 Subject: [PATCH 036/148] Address @emersion's review comments --- sway/tree/workspace.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/sway/tree/workspace.c b/sway/tree/workspace.c index a93d9f44..b1082e4f 100644 --- a/sway/tree/workspace.c +++ b/sway/tree/workspace.c @@ -592,6 +592,8 @@ found: static void pw_handle_output_destroy(struct wl_listener *listener, void *data) { struct pid_workspace *pw = wl_container_of(listener, pw, output_destroy); pw->output = NULL; + wl_list_remove(&pw->output_destroy.link); + wl_list_init(&pw->output_destroy.link); } void workspace_record_pid(pid_t pid) { From 24ad1c3983192b47345566fd876e26b45160d68e Mon Sep 17 00:00:00 2001 From: Pascal Pascher Date: Tue, 24 Jul 2018 22:16:06 +0200 Subject: [PATCH 037/148] Added meson option "enable_xwayland" (default: true) to enable/disable xwayland support --- include/sway/criteria.h | 2 ++ include/sway/server.h | 9 ++++++--- include/sway/tree/layout.h | 3 ++- include/sway/tree/view.h | 15 ++++++++++++--- include/sway/xwayland.h | 2 ++ meson.build | 6 ++++++ meson_options.txt | 1 + sway/commands/swap.c | 6 ++++++ sway/criteria.c | 12 ++++++++++++ sway/desktop/output.c | 15 +++++++++------ sway/desktop/render.c | 10 ++++++---- sway/desktop/xwayland.c | 2 ++ sway/input/cursor.c | 3 ++- sway/input/seat.c | 2 ++ sway/server.c | 4 ++++ sway/tree/container.c | 2 ++ sway/tree/layout.c | 2 ++ sway/tree/view.c | 16 ++++++++++++++-- 18 files changed, 92 insertions(+), 20 deletions(-) diff --git a/include/sway/criteria.h b/include/sway/criteria.h index 6a8337c5..5b5c0f58 100644 --- a/include/sway/criteria.h +++ b/include/sway/criteria.h @@ -25,7 +25,9 @@ struct criteria { pcre *instance; pcre *con_mark; uint32_t con_id; // internal ID + #ifdef HAVE_XWAYLAND uint32_t id; // X11 window ID + #endif pcre *window_role; uint32_t window_type; bool floating; diff --git a/include/sway/server.h b/include/sway/server.h index 70bde6d4..fb22125f 100644 --- a/include/sway/server.h +++ b/include/sway/server.h @@ -12,7 +12,9 @@ #include // TODO WLR: make Xwayland optional #include "list.h" +#ifdef HAVE_XWAYLAND #include "sway/xwayland.h" +#endif struct sway_server { struct wl_display *wl_display; @@ -39,11 +41,11 @@ struct sway_server { struct wlr_xdg_shell *xdg_shell; struct wl_listener xdg_shell_surface; - + #ifdef HAVE_XWAYLAND struct sway_xwayland xwayland; struct wl_listener xwayland_surface; struct wl_listener xwayland_ready; - + #endif bool debug_txn_timings; list_t *transactions; @@ -65,6 +67,7 @@ void handle_idle_inhibitor_v1(struct wl_listener *listener, void *data); void handle_layer_shell_surface(struct wl_listener *listener, void *data); void handle_xdg_shell_v6_surface(struct wl_listener *listener, void *data); void handle_xdg_shell_surface(struct wl_listener *listener, void *data); +#ifdef HAVE_XWAYLAND void handle_xwayland_surface(struct wl_listener *listener, void *data); - +#endif #endif diff --git a/include/sway/tree/layout.h b/include/sway/tree/layout.h index 7d7da2d7..da74e205 100644 --- a/include/sway/tree/layout.h +++ b/include/sway/tree/layout.h @@ -27,8 +27,9 @@ struct sway_root { struct wlr_output_layout *output_layout; struct wl_listener output_layout_change; - + #ifdef HAVE_XWAYLAND struct wl_list xwayland_unmanaged; // sway_xwayland_unmanaged::link + #endif struct wl_list drag_icons; // sway_drag_icon::link struct wlr_texture *debug_tree; diff --git a/include/sway/tree/view.h b/include/sway/tree/view.h index 3bdfe252..af12cf88 100644 --- a/include/sway/tree/view.h +++ b/include/sway/tree/view.h @@ -3,7 +3,9 @@ #include #include #include +#ifdef HAVE_XWAYLAND #include +#endif #include "sway/input/input-manager.h" #include "sway/input/seat.h" @@ -12,7 +14,9 @@ struct sway_container; enum sway_view_type { SWAY_VIEW_XDG_SHELL_V6, SWAY_VIEW_XDG_SHELL, + #ifdef HAVE_XWAYLAND SWAY_VIEW_XWAYLAND, + #endif }; enum sway_view_prop { @@ -22,7 +26,9 @@ enum sway_view_prop { VIEW_PROP_INSTANCE, VIEW_PROP_WINDOW_TYPE, VIEW_PROP_WINDOW_ROLE, + #ifdef HAVE_XWAYLAND VIEW_PROP_X11_WINDOW_ID, + #endif }; struct sway_view_impl { @@ -90,7 +96,9 @@ struct sway_view { union { struct wlr_xdg_surface_v6 *wlr_xdg_surface_v6; struct wlr_xdg_surface *wlr_xdg_surface; + #ifdef HAVE_XWAYLAND struct wlr_xwayland_surface *wlr_xwayland_surface; + #endif struct wlr_wl_shell_surface *wlr_wl_shell_surface; }; @@ -133,7 +141,7 @@ struct sway_xdg_shell_view { struct wl_listener unmap; struct wl_listener destroy; }; - +#ifdef HAVE_XWAYLAND struct sway_xwayland_view { struct sway_view view; @@ -165,7 +173,7 @@ struct sway_xwayland_unmanaged { struct wl_listener unmap; struct wl_listener destroy; }; - +#endif struct sway_view_child; struct sway_view_child_impl { @@ -281,9 +289,10 @@ struct sway_view *view_from_wlr_xdg_surface( struct wlr_xdg_surface *xdg_surface); struct sway_view *view_from_wlr_xdg_surface_v6( struct wlr_xdg_surface_v6 *xdg_surface_v6); +#ifdef HAVE_XWAYLAND struct sway_view *view_from_wlr_xwayland_surface( struct wlr_xwayland_surface *xsurface); - +#endif struct sway_view *view_from_wlr_surface(struct wlr_surface *surface); /** diff --git a/include/sway/xwayland.h b/include/sway/xwayland.h index 78d1053b..e6572ae9 100644 --- a/include/sway/xwayland.h +++ b/include/sway/xwayland.h @@ -1,3 +1,4 @@ +#ifdef HAVE_XWAYLAND #ifndef SWAY_XWAYLAND_H #define SWAY_XWAYLAND_H @@ -23,3 +24,4 @@ struct sway_xwayland { void handle_xwayland_ready(struct wl_listener *listener, void *data); #endif +#endif diff --git a/meson.build b/meson.build index 1d40581a..a2def755 100644 --- a/meson.build +++ b/meson.build @@ -48,6 +48,12 @@ git = find_program('git', required: false) conf_data = configuration_data() +if get_option('enable-xwayland') + conf_data.set('HAVE_XWAYLAND', true) +else + conf_data.set('HAVE_XWAYLAND', false) +endif + if gdk_pixbuf.found() conf_data.set('HAVE_GDK_PIXBUF', true) endif diff --git a/meson_options.txt b/meson_options.txt index 541ccf13..1897cba6 100644 --- a/meson_options.txt +++ b/meson_options.txt @@ -1,3 +1,4 @@ option('sway_version', type : 'string', description: 'The version string reported in `sway --version`.') option('default_wallpaper', type: 'boolean', value: true, description: 'Install the default wallpaper.') option('zsh_completions', type: 'boolean', value: true, description: 'Install zsh shell completions.') +option('enable-xwayland', type: 'boolean', value: false, description: 'Enable support X11 applications') diff --git a/sway/commands/swap.c b/sway/commands/swap.c index 2fc88308..3563cdd9 100644 --- a/sway/commands/swap.c +++ b/sway/commands/swap.c @@ -14,10 +14,14 @@ static bool test_con_id(struct sway_container *container, void *con_id) { } static bool test_id(struct sway_container *container, void *id) { + #ifdef HAVE_XWAYLAND xcb_window_t *wid = id; return (container->type == C_VIEW && container->sway_view->type == SWAY_VIEW_XWAYLAND && container->sway_view->wlr_xwayland_surface->window_id == *wid); + #else + return false; + #endif } static bool test_mark(struct sway_container *container, void *mark) { @@ -43,8 +47,10 @@ struct cmd_results *cmd_swap(int argc, char **argv) { char *value = join_args(argv + 3, argc - 3); if (strcasecmp(argv[2], "id") == 0) { + #ifdef HAVE_XWAYLAND xcb_window_t id = strtol(value, NULL, 0); other = container_find(&root_container, test_id, (void *)&id); + #endif } else if (strcasecmp(argv[2], "con_id") == 0) { size_t con_id = atoi(value); other = container_find(&root_container, test_con_id, (void *)con_id); diff --git a/sway/criteria.c b/sway/criteria.c index c2e9c07e..b2c6edf9 100644 --- a/sway/criteria.c +++ b/sway/criteria.c @@ -19,7 +19,9 @@ bool criteria_is_empty(struct criteria *criteria) { && !criteria->instance && !criteria->con_mark && !criteria->con_id + #ifdef HAVE_XWAYLAND && !criteria->id + #endif && !criteria->window_role && !criteria->window_type && !criteria->floating @@ -127,12 +129,14 @@ static bool criteria_matches_view(struct criteria *criteria, } } + #ifdef HAVE_XWAYLAND if (criteria->id) { // X11 window ID uint32_t x11_window_id = view_get_x11_window_id(view); if (!x11_window_id || x11_window_id != criteria->id) { return false; } } + #endif if (criteria->window_role) { // TODO @@ -265,7 +269,9 @@ enum criteria_token { T_CON_ID, T_CON_MARK, T_FLOATING, + #ifdef HAVE_XWAYLAND T_ID, + #endif T_INSTANCE, T_SHELL, T_TILING, @@ -287,8 +293,10 @@ static enum criteria_token token_from_name(char *name) { return T_CON_ID; } else if (strcmp(name, "con_mark") == 0) { return T_CON_MARK; + #ifdef HAVE_XWAYLAND } else if (strcmp(name, "id") == 0) { return T_ID; + #endif } else if (strcmp(name, "instance") == 0) { return T_INSTANCE; } else if (strcmp(name, "shell") == 0) { @@ -355,7 +363,9 @@ static char *get_focused_prop(enum criteria_token token) { case T_CON_ID: // These do not support __focused__ case T_CON_MARK: case T_FLOATING: + #ifdef HAVE_XWAYLAND case T_ID: + #endif case T_TILING: case T_URGENT: case T_WINDOW_TYPE: @@ -426,12 +436,14 @@ static bool parse_token(struct criteria *criteria, char *name, char *value) { case T_WINDOW_TYPE: // TODO: This is a string but will be stored as an enum or integer break; + #ifdef HAVE_XWAYLAND case T_ID: criteria->id = strtoul(effective_value, &endptr, 10); if (*endptr != 0) { error = strdup("The value for 'id' should be numeric"); } break; + #endif case T_FLOATING: criteria->floating = true; break; diff --git a/sway/desktop/output.c b/sway/desktop/output.c index a206ac6b..7cca23bd 100644 --- a/sway/desktop/output.c +++ b/sway/desktop/output.c @@ -128,7 +128,7 @@ void output_layer_for_each_surface(struct wl_list *layer_surfaces, user_data); } } - +#ifdef HAVE_XWAYLAND void output_unmanaged_for_each_surface(struct wl_list *unmanaged, struct sway_output *output, struct root_geometry *geo, wlr_surface_iterator_func_t iterator, void *user_data) { @@ -143,7 +143,7 @@ void output_unmanaged_for_each_surface(struct wl_list *unmanaged, iterator, user_data); } } - +#endif void output_drag_icons_for_each_surface(struct wl_list *drag_icons, struct sway_output *output, struct root_geometry *geo, wlr_surface_iterator_func_t iterator, void *user_data) { @@ -244,13 +244,13 @@ static void send_frame_done_layer(struct send_frame_done_data *data, output_layer_for_each_surface(layer_surfaces, &data->root_geo, send_frame_done_iterator, data); } - +#ifdef HAVE_XWAYLAND static void send_frame_done_unmanaged(struct send_frame_done_data *data, struct wl_list *unmanaged) { output_unmanaged_for_each_surface(unmanaged, data->output, &data->root_geo, send_frame_done_iterator, data); } - +#endif static void send_frame_done_drag_icons(struct send_frame_done_data *data, struct wl_list *drag_icons) { output_drag_icons_for_each_surface(drag_icons, data->output, &data->root_geo, @@ -291,11 +291,12 @@ static void send_frame_done(struct sway_output *output, struct timespec *when) { if (workspace->current.ws_fullscreen) { send_frame_done_container_iterator( workspace->current.ws_fullscreen->swayc, &data); - + #ifdef HAVE_XWAYLAND if (workspace->current.ws_fullscreen->type == SWAY_VIEW_XWAYLAND) { send_frame_done_unmanaged(&data, &root_container.sway_root->xwayland_unmanaged); } + #endif } else { send_frame_done_layer(&data, &output->layers[ZWLR_LAYER_SHELL_V1_LAYER_BACKGROUND]); @@ -304,9 +305,11 @@ static void send_frame_done(struct sway_output *output, struct timespec *when) { send_frame_done_container(&data, workspace); send_frame_done_container(&data, workspace->sway_workspace->floating); - + + #ifdef HAVE_XWAYLAND send_frame_done_unmanaged(&data, &root_container.sway_root->xwayland_unmanaged); + #endif send_frame_done_layer(&data, &output->layers[ZWLR_LAYER_SHELL_V1_LAYER_TOP]); } diff --git a/sway/desktop/render.c b/sway/desktop/render.c index 7da54594..46bfec6b 100644 --- a/sway/desktop/render.c +++ b/sway/desktop/render.c @@ -132,7 +132,7 @@ static void render_layer(struct sway_output *output, output_layer_for_each_surface(layer_surfaces, &data.root_geo, render_surface_iterator, &data); } - +#ifdef HAVE_XWAYLAND static void render_unmanaged(struct sway_output *output, pixman_region32_t *damage, struct wl_list *unmanaged) { struct render_data data = { @@ -143,7 +143,7 @@ static void render_unmanaged(struct sway_output *output, output_unmanaged_for_each_surface(unmanaged, output, &data.root_geo, render_surface_iterator, &data); } - +#endif static void render_drag_icons(struct sway_output *output, pixman_region32_t *damage, struct wl_list *drag_icons) { struct render_data data = { @@ -866,11 +866,12 @@ void output_render(struct sway_output *output, struct timespec *when, } else { render_view_surfaces(fullscreen_view, output, damage, 1.0f); } - + #ifdef HAVE_XWAYLAND if (fullscreen_view->type == SWAY_VIEW_XWAYLAND) { render_unmanaged(output, damage, &root_container.sway_root->xwayland_unmanaged); } + #endif } else { float clear_color[] = {0.25f, 0.25f, 0.25f, 1.0f}; @@ -888,9 +889,10 @@ void output_render(struct sway_output *output, struct timespec *when, render_container(output, damage, workspace, workspace->current.focused); render_floating(output, damage); - + #ifdef HAVE_XWAYLAND render_unmanaged(output, damage, &root_container.sway_root->xwayland_unmanaged); + #endif render_layer(output, damage, &output->layers[ZWLR_LAYER_SHELL_V1_LAYER_TOP]); } diff --git a/sway/desktop/xwayland.c b/sway/desktop/xwayland.c index 65d4fcd4..d940d47b 100644 --- a/sway/desktop/xwayland.c +++ b/sway/desktop/xwayland.c @@ -1,4 +1,5 @@ #define _POSIX_C_SOURCE 199309L +#ifdef HAVE_XWAYLAND #include #include #include @@ -591,3 +592,4 @@ void handle_xwayland_ready(struct wl_listener *listener, void *data) { xcb_disconnect(xcb_conn); } +#endif diff --git a/sway/input/cursor.c b/sway/input/cursor.c index 27597640..c873a20e 100644 --- a/sway/input/cursor.c +++ b/sway/input/cursor.c @@ -54,6 +54,7 @@ static struct sway_container *container_at_coords( struct sway_seat *seat, double lx, double ly, struct wlr_surface **surface, double *sx, double *sy) { // check for unmanaged views first + #ifdef HAVE_XWAYLAND struct wl_list *unmanaged = &root_container.sway_root->xwayland_unmanaged; struct sway_xwayland_unmanaged *unmanaged_surface; wl_list_for_each_reverse(unmanaged_surface, unmanaged, link) { @@ -69,7 +70,7 @@ static struct sway_container *container_at_coords( return NULL; } } - + #endif // find the output the cursor is on struct wlr_output_layout *output_layout = root_container.sway_root->output_layout; diff --git a/sway/input/seat.c b/sway/input/seat.c index fc9e54b6..66d11eea 100644 --- a/sway/input/seat.c +++ b/sway/input/seat.c @@ -103,11 +103,13 @@ static void seat_send_focus(struct sway_container *con, if (con->type == C_VIEW && seat_is_input_allowed(seat, con->sway_view->surface)) { + #ifdef HAVE_XWAYLAND if (con->sway_view->type == SWAY_VIEW_XWAYLAND) { struct wlr_xwayland *xwayland = seat->input->server->xwayland.wlr_xwayland; wlr_xwayland_set_seat(xwayland, seat->wlr_seat); } + #endif struct wlr_keyboard *keyboard = wlr_seat_get_keyboard(seat->wlr_seat); if (keyboard) { wlr_seat_keyboard_notify_enter(seat->wlr_seat, diff --git a/sway/server.c b/sway/server.c index 89dfbf8c..90f25dbc 100644 --- a/sway/server.c +++ b/sway/server.c @@ -25,7 +25,9 @@ #include "sway/input/input-manager.h" #include "sway/server.h" #include "sway/tree/layout.h" +#ifdef HAVE_XWAYLAND #include "sway/xwayland.h" +#endif bool server_privileged_prepare(struct sway_server *server) { wlr_log(WLR_DEBUG, "Preparing Wayland server initialization"); @@ -81,6 +83,7 @@ bool server_init(struct sway_server *server) { server->xdg_shell_surface.notify = handle_xdg_shell_surface; // TODO make xwayland optional + #ifdef HAVE_XWAYLAND server->xwayland.wlr_xwayland = wlr_xwayland_create(server->wl_display, server->compositor, true); wl_signal_add(&server->xwayland.wlr_xwayland->events.new_surface, @@ -101,6 +104,7 @@ bool server_init(struct sway_server *server) { image->width * 4, image->width, image->height, image->hotspot_x, image->hotspot_y); } + #endif // TODO: Integration with sway borders struct wlr_server_decoration_manager *deco_manager = diff --git a/sway/tree/container.c b/sway/tree/container.c index 4f743c40..0a655db5 100644 --- a/sway/tree/container.c +++ b/sway/tree/container.c @@ -527,10 +527,12 @@ static struct sway_container *container_at_view(struct sway_container *swayc, double _sx, _sy; struct wlr_surface *_surface = NULL; switch (sview->type) { + #ifdef HAVE_XWAYLAND case SWAY_VIEW_XWAYLAND: _surface = wlr_surface_surface_at(sview->surface, view_sx, view_sy, &_sx, &_sy); break; + #endif case SWAY_VIEW_XDG_SHELL_V6: _surface = wlr_xdg_surface_v6_surface_at( sview->wlr_xdg_surface_v6, diff --git a/sway/tree/layout.c b/sway/tree/layout.c index a2be0ef3..91350c3f 100644 --- a/sway/tree/layout.c +++ b/sway/tree/layout.c @@ -39,7 +39,9 @@ void layout_init(void) { root_container.sway_root = calloc(1, sizeof(*root_container.sway_root)); root_container.sway_root->output_layout = wlr_output_layout_create(); wl_list_init(&root_container.sway_root->outputs); + #ifdef HAVE_XWAYLAND wl_list_init(&root_container.sway_root->xwayland_unmanaged); + #endif wl_list_init(&root_container.sway_root->drag_icons); wl_signal_init(&root_container.sway_root->events.new_container); root_container.sway_root->scratchpad = create_list(); diff --git a/sway/tree/view.c b/sway/tree/view.c index a55c8a29..e99b938e 100644 --- a/sway/tree/view.c +++ b/sway/tree/view.c @@ -3,7 +3,9 @@ #include #include #include +#ifdef HAVE_XWAYLAND #include +#endif #include "list.h" #include "log.h" #include "sway/criteria.h" @@ -108,14 +110,14 @@ const char *view_get_instance(struct sway_view *view) { } return NULL; } - +#ifdef HAVE_XWAYLAND uint32_t view_get_x11_window_id(struct sway_view *view) { if (view->impl->get_int_prop) { return view->impl->get_int_prop(view, VIEW_PROP_X11_WINDOW_ID); } return 0; } - +#endif const char *view_get_window_role(struct sway_view *view) { if (view->impl->get_string_prop) { return view->impl->get_string_prop(view, VIEW_PROP_WINDOW_ROLE); @@ -136,8 +138,10 @@ const char *view_get_shell(struct sway_view *view) { return "xdg_shell_v6"; case SWAY_VIEW_XDG_SHELL: return "xdg_shell"; + #ifdef HAVE_XWAYLAND case SWAY_VIEW_XWAYLAND: return "xwayland"; + #endif } return "unknown"; } @@ -563,6 +567,7 @@ void view_map(struct sway_view *view, struct wlr_surface *wlr_surface) { } pid_t pid; + #ifdef HAVE_XWAYLAND if (view->type == SWAY_VIEW_XWAYLAND) { struct wlr_xwayland_surface *surf = wlr_xwayland_surface_from_wlr_surface(wlr_surface); @@ -572,6 +577,11 @@ void view_map(struct sway_view *view, struct wlr_surface *wlr_surface) { wl_resource_get_client(wlr_surface->resource); wl_client_get_credentials(client, &pid, NULL, NULL); } + #else + struct wl_client *client = + wl_resource_get_client(wlr_surface->resource); + wl_client_get_credentials(client, &pid, NULL, NULL); + #endif struct sway_seat *seat = input_manager_current_seat(input_manager); struct sway_container *target_sibling = @@ -825,11 +835,13 @@ struct sway_view *view_from_wlr_surface(struct wlr_surface *wlr_surface) { wlr_xdg_surface_v6_from_wlr_surface(wlr_surface); return view_from_wlr_xdg_surface_v6(xdg_surface_v6); } + #ifdef HAVE_XWAYLAND if (wlr_surface_is_xwayland_surface(wlr_surface)) { struct wlr_xwayland_surface *xsurface = wlr_xwayland_surface_from_wlr_surface(wlr_surface); return view_from_wlr_xwayland_surface(xsurface); } + #endif if (wlr_surface_is_subsurface(wlr_surface)) { struct wlr_subsurface *subsurface = wlr_subsurface_from_wlr_surface(wlr_surface); From 2bf893248a94c7f70a9557aad4a8228731041eeb Mon Sep 17 00:00:00 2001 From: Pascal Pascher Date: Tue, 24 Jul 2018 23:37:41 +0200 Subject: [PATCH 038/148] style fixes, exclude sway/desctop/xwayland.c when enable_xwayland: false --- include/sway/criteria.h | 4 ++-- include/sway/server.h | 4 ++-- include/sway/tree/layout.h | 4 ++-- include/sway/tree/view.h | 12 ++++++------ meson.build | 4 ++-- meson_options.txt | 2 +- sway/commands/swap.c | 12 ++++++------ sway/desktop/output.c | 10 +++++----- sway/desktop/render.c | 8 ++++---- sway/desktop/xwayland.c | 2 -- sway/input/cursor.c | 4 ++-- sway/input/seat.c | 4 ++-- sway/meson.build | 5 ++++- sway/server.c | 4 ++-- sway/tree/container.c | 4 ++-- sway/tree/layout.c | 4 ++-- sway/tree/view.c | 14 +++++++------- 17 files changed, 51 insertions(+), 50 deletions(-) diff --git a/include/sway/criteria.h b/include/sway/criteria.h index 5b5c0f58..23efe9d4 100644 --- a/include/sway/criteria.h +++ b/include/sway/criteria.h @@ -25,9 +25,9 @@ struct criteria { pcre *instance; pcre *con_mark; uint32_t con_id; // internal ID - #ifdef HAVE_XWAYLAND +#ifdef HAVE_XWAYLAND uint32_t id; // X11 window ID - #endif +#endif pcre *window_role; uint32_t window_type; bool floating; diff --git a/include/sway/server.h b/include/sway/server.h index fb22125f..b6a6bde3 100644 --- a/include/sway/server.h +++ b/include/sway/server.h @@ -41,11 +41,11 @@ struct sway_server { struct wlr_xdg_shell *xdg_shell; struct wl_listener xdg_shell_surface; - #ifdef HAVE_XWAYLAND +#ifdef HAVE_XWAYLAND struct sway_xwayland xwayland; struct wl_listener xwayland_surface; struct wl_listener xwayland_ready; - #endif +#endif bool debug_txn_timings; list_t *transactions; diff --git a/include/sway/tree/layout.h b/include/sway/tree/layout.h index da74e205..580acd16 100644 --- a/include/sway/tree/layout.h +++ b/include/sway/tree/layout.h @@ -27,9 +27,9 @@ struct sway_root { struct wlr_output_layout *output_layout; struct wl_listener output_layout_change; - #ifdef HAVE_XWAYLAND +#ifdef HAVE_XWAYLAND struct wl_list xwayland_unmanaged; // sway_xwayland_unmanaged::link - #endif +#endif struct wl_list drag_icons; // sway_drag_icon::link struct wlr_texture *debug_tree; diff --git a/include/sway/tree/view.h b/include/sway/tree/view.h index af12cf88..d4416dd3 100644 --- a/include/sway/tree/view.h +++ b/include/sway/tree/view.h @@ -14,9 +14,9 @@ struct sway_container; enum sway_view_type { SWAY_VIEW_XDG_SHELL_V6, SWAY_VIEW_XDG_SHELL, - #ifdef HAVE_XWAYLAND +#ifdef HAVE_XWAYLAND SWAY_VIEW_XWAYLAND, - #endif +#endif }; enum sway_view_prop { @@ -26,9 +26,9 @@ enum sway_view_prop { VIEW_PROP_INSTANCE, VIEW_PROP_WINDOW_TYPE, VIEW_PROP_WINDOW_ROLE, - #ifdef HAVE_XWAYLAND +#ifdef HAVE_XWAYLAND VIEW_PROP_X11_WINDOW_ID, - #endif +#endif }; struct sway_view_impl { @@ -96,9 +96,9 @@ struct sway_view { union { struct wlr_xdg_surface_v6 *wlr_xdg_surface_v6; struct wlr_xdg_surface *wlr_xdg_surface; - #ifdef HAVE_XWAYLAND +#ifdef HAVE_XWAYLAND struct wlr_xwayland_surface *wlr_xwayland_surface; - #endif +#endif struct wlr_wl_shell_surface *wlr_wl_shell_surface; }; diff --git a/meson.build b/meson.build index a2def755..06299618 100644 --- a/meson.build +++ b/meson.build @@ -49,9 +49,9 @@ git = find_program('git', required: false) conf_data = configuration_data() if get_option('enable-xwayland') - conf_data.set('HAVE_XWAYLAND', true) + conf_data.set('HAVE_XWAYLAND', true) else - conf_data.set('HAVE_XWAYLAND', false) + conf_data.set('HAVE_XWAYLAND', false) endif if gdk_pixbuf.found() diff --git a/meson_options.txt b/meson_options.txt index 1897cba6..6c7f241d 100644 --- a/meson_options.txt +++ b/meson_options.txt @@ -1,4 +1,4 @@ option('sway_version', type : 'string', description: 'The version string reported in `sway --version`.') option('default_wallpaper', type: 'boolean', value: true, description: 'Install the default wallpaper.') option('zsh_completions', type: 'boolean', value: true, description: 'Install zsh shell completions.') -option('enable-xwayland', type: 'boolean', value: false, description: 'Enable support X11 applications') +option('enable-xwayland', type: 'boolean', value: true, description: 'Enable support for X11 applications') diff --git a/sway/commands/swap.c b/sway/commands/swap.c index 3563cdd9..325adc38 100644 --- a/sway/commands/swap.c +++ b/sway/commands/swap.c @@ -14,14 +14,14 @@ static bool test_con_id(struct sway_container *container, void *con_id) { } static bool test_id(struct sway_container *container, void *id) { - #ifdef HAVE_XWAYLAND +#ifdef HAVE_XWAYLAND xcb_window_t *wid = id; return (container->type == C_VIEW && container->sway_view->type == SWAY_VIEW_XWAYLAND && container->sway_view->wlr_xwayland_surface->window_id == *wid); - #else - return false; - #endif +#else + return false; +#endif } static bool test_mark(struct sway_container *container, void *mark) { @@ -47,10 +47,10 @@ struct cmd_results *cmd_swap(int argc, char **argv) { char *value = join_args(argv + 3, argc - 3); if (strcasecmp(argv[2], "id") == 0) { - #ifdef HAVE_XWAYLAND +#ifdef HAVE_XWAYLAND xcb_window_t id = strtol(value, NULL, 0); other = container_find(&root_container, test_id, (void *)&id); - #endif +#endif } else if (strcasecmp(argv[2], "con_id") == 0) { size_t con_id = atoi(value); other = container_find(&root_container, test_con_id, (void *)con_id); diff --git a/sway/desktop/output.c b/sway/desktop/output.c index 7cca23bd..05daad7b 100644 --- a/sway/desktop/output.c +++ b/sway/desktop/output.c @@ -291,12 +291,12 @@ static void send_frame_done(struct sway_output *output, struct timespec *when) { if (workspace->current.ws_fullscreen) { send_frame_done_container_iterator( workspace->current.ws_fullscreen->swayc, &data); - #ifdef HAVE_XWAYLAND +#ifdef HAVE_XWAYLAND if (workspace->current.ws_fullscreen->type == SWAY_VIEW_XWAYLAND) { send_frame_done_unmanaged(&data, &root_container.sway_root->xwayland_unmanaged); } - #endif +#endif } else { send_frame_done_layer(&data, &output->layers[ZWLR_LAYER_SHELL_V1_LAYER_BACKGROUND]); @@ -305,11 +305,11 @@ static void send_frame_done(struct sway_output *output, struct timespec *when) { send_frame_done_container(&data, workspace); send_frame_done_container(&data, workspace->sway_workspace->floating); - - #ifdef HAVE_XWAYLAND + +#ifdef HAVE_XWAYLAND send_frame_done_unmanaged(&data, &root_container.sway_root->xwayland_unmanaged); - #endif +#endif send_frame_done_layer(&data, &output->layers[ZWLR_LAYER_SHELL_V1_LAYER_TOP]); } diff --git a/sway/desktop/render.c b/sway/desktop/render.c index 46bfec6b..2e6b6649 100644 --- a/sway/desktop/render.c +++ b/sway/desktop/render.c @@ -866,12 +866,12 @@ void output_render(struct sway_output *output, struct timespec *when, } else { render_view_surfaces(fullscreen_view, output, damage, 1.0f); } - #ifdef HAVE_XWAYLAND +#ifdef HAVE_XWAYLAND if (fullscreen_view->type == SWAY_VIEW_XWAYLAND) { render_unmanaged(output, damage, &root_container.sway_root->xwayland_unmanaged); } - #endif +#endif } else { float clear_color[] = {0.25f, 0.25f, 0.25f, 1.0f}; @@ -889,10 +889,10 @@ void output_render(struct sway_output *output, struct timespec *when, render_container(output, damage, workspace, workspace->current.focused); render_floating(output, damage); - #ifdef HAVE_XWAYLAND +#ifdef HAVE_XWAYLAND render_unmanaged(output, damage, &root_container.sway_root->xwayland_unmanaged); - #endif +#endif render_layer(output, damage, &output->layers[ZWLR_LAYER_SHELL_V1_LAYER_TOP]); } diff --git a/sway/desktop/xwayland.c b/sway/desktop/xwayland.c index d940d47b..65d4fcd4 100644 --- a/sway/desktop/xwayland.c +++ b/sway/desktop/xwayland.c @@ -1,5 +1,4 @@ #define _POSIX_C_SOURCE 199309L -#ifdef HAVE_XWAYLAND #include #include #include @@ -592,4 +591,3 @@ void handle_xwayland_ready(struct wl_listener *listener, void *data) { xcb_disconnect(xcb_conn); } -#endif diff --git a/sway/input/cursor.c b/sway/input/cursor.c index c873a20e..33eebf97 100644 --- a/sway/input/cursor.c +++ b/sway/input/cursor.c @@ -54,7 +54,7 @@ static struct sway_container *container_at_coords( struct sway_seat *seat, double lx, double ly, struct wlr_surface **surface, double *sx, double *sy) { // check for unmanaged views first - #ifdef HAVE_XWAYLAND +#ifdef HAVE_XWAYLAND struct wl_list *unmanaged = &root_container.sway_root->xwayland_unmanaged; struct sway_xwayland_unmanaged *unmanaged_surface; wl_list_for_each_reverse(unmanaged_surface, unmanaged, link) { @@ -70,7 +70,7 @@ static struct sway_container *container_at_coords( return NULL; } } - #endif +#endif // find the output the cursor is on struct wlr_output_layout *output_layout = root_container.sway_root->output_layout; diff --git a/sway/input/seat.c b/sway/input/seat.c index 66d11eea..6b4e5f2e 100644 --- a/sway/input/seat.c +++ b/sway/input/seat.c @@ -103,13 +103,13 @@ static void seat_send_focus(struct sway_container *con, if (con->type == C_VIEW && seat_is_input_allowed(seat, con->sway_view->surface)) { - #ifdef HAVE_XWAYLAND +#ifdef HAVE_XWAYLAND if (con->sway_view->type == SWAY_VIEW_XWAYLAND) { struct wlr_xwayland *xwayland = seat->input->server->xwayland.wlr_xwayland; wlr_xwayland_set_seat(xwayland, seat->wlr_seat); } - #endif +#endif struct wlr_keyboard *keyboard = wlr_seat_get_keyboard(seat->wlr_seat); if (keyboard) { wlr_seat_keyboard_notify_enter(seat->wlr_seat, diff --git a/sway/meson.build b/sway/meson.build index 30c848e2..649a3ac2 100644 --- a/sway/meson.build +++ b/sway/meson.build @@ -18,7 +18,6 @@ sway_sources = files( 'desktop/transaction.c', 'desktop/xdg_shell_v6.c', 'desktop/xdg_shell.c', - 'desktop/xwayland.c', 'input/input-manager.c', 'input/seat.c', @@ -152,6 +151,10 @@ sway_sources = files( 'tree/output.c', ) +if get_option('enable-xwayland') + sway_sources += 'desktop/xwayland.c' +endif + sway_deps = [ cairo, gdk_pixbuf, diff --git a/sway/server.c b/sway/server.c index 90f25dbc..1521597f 100644 --- a/sway/server.c +++ b/sway/server.c @@ -83,7 +83,7 @@ bool server_init(struct sway_server *server) { server->xdg_shell_surface.notify = handle_xdg_shell_surface; // TODO make xwayland optional - #ifdef HAVE_XWAYLAND +#ifdef HAVE_XWAYLAND server->xwayland.wlr_xwayland = wlr_xwayland_create(server->wl_display, server->compositor, true); wl_signal_add(&server->xwayland.wlr_xwayland->events.new_surface, @@ -104,7 +104,7 @@ bool server_init(struct sway_server *server) { image->width * 4, image->width, image->height, image->hotspot_x, image->hotspot_y); } - #endif +#endif // TODO: Integration with sway borders struct wlr_server_decoration_manager *deco_manager = diff --git a/sway/tree/container.c b/sway/tree/container.c index 0a655db5..b56b4e87 100644 --- a/sway/tree/container.c +++ b/sway/tree/container.c @@ -527,12 +527,12 @@ static struct sway_container *container_at_view(struct sway_container *swayc, double _sx, _sy; struct wlr_surface *_surface = NULL; switch (sview->type) { - #ifdef HAVE_XWAYLAND +#ifdef HAVE_XWAYLAND case SWAY_VIEW_XWAYLAND: _surface = wlr_surface_surface_at(sview->surface, view_sx, view_sy, &_sx, &_sy); break; - #endif +#endif case SWAY_VIEW_XDG_SHELL_V6: _surface = wlr_xdg_surface_v6_surface_at( sview->wlr_xdg_surface_v6, diff --git a/sway/tree/layout.c b/sway/tree/layout.c index 91350c3f..3d361ea5 100644 --- a/sway/tree/layout.c +++ b/sway/tree/layout.c @@ -39,9 +39,9 @@ void layout_init(void) { root_container.sway_root = calloc(1, sizeof(*root_container.sway_root)); root_container.sway_root->output_layout = wlr_output_layout_create(); wl_list_init(&root_container.sway_root->outputs); - #ifdef HAVE_XWAYLAND +#ifdef HAVE_XWAYLAND wl_list_init(&root_container.sway_root->xwayland_unmanaged); - #endif +#endif wl_list_init(&root_container.sway_root->drag_icons); wl_signal_init(&root_container.sway_root->events.new_container); root_container.sway_root->scratchpad = create_list(); diff --git a/sway/tree/view.c b/sway/tree/view.c index e99b938e..2eff80a8 100644 --- a/sway/tree/view.c +++ b/sway/tree/view.c @@ -138,10 +138,10 @@ const char *view_get_shell(struct sway_view *view) { return "xdg_shell_v6"; case SWAY_VIEW_XDG_SHELL: return "xdg_shell"; - #ifdef HAVE_XWAYLAND +#ifdef HAVE_XWAYLAND case SWAY_VIEW_XWAYLAND: return "xwayland"; - #endif +#endif } return "unknown"; } @@ -567,7 +567,7 @@ void view_map(struct sway_view *view, struct wlr_surface *wlr_surface) { } pid_t pid; - #ifdef HAVE_XWAYLAND +#ifdef HAVE_XWAYLAND if (view->type == SWAY_VIEW_XWAYLAND) { struct wlr_xwayland_surface *surf = wlr_xwayland_surface_from_wlr_surface(wlr_surface); @@ -577,11 +577,11 @@ void view_map(struct sway_view *view, struct wlr_surface *wlr_surface) { wl_resource_get_client(wlr_surface->resource); wl_client_get_credentials(client, &pid, NULL, NULL); } - #else +#else struct wl_client *client = wl_resource_get_client(wlr_surface->resource); wl_client_get_credentials(client, &pid, NULL, NULL); - #endif +#endif struct sway_seat *seat = input_manager_current_seat(input_manager); struct sway_container *target_sibling = @@ -835,13 +835,13 @@ struct sway_view *view_from_wlr_surface(struct wlr_surface *wlr_surface) { wlr_xdg_surface_v6_from_wlr_surface(wlr_surface); return view_from_wlr_xdg_surface_v6(xdg_surface_v6); } - #ifdef HAVE_XWAYLAND +#ifdef HAVE_XWAYLAND if (wlr_surface_is_xwayland_surface(wlr_surface)) { struct wlr_xwayland_surface *xsurface = wlr_xwayland_surface_from_wlr_surface(wlr_surface); return view_from_wlr_xwayland_surface(xsurface); } - #endif +#endif if (wlr_surface_is_subsurface(wlr_surface)) { struct wlr_subsurface *subsurface = wlr_subsurface_from_wlr_surface(wlr_surface); From dca02944ce91feae625e68d897d4caee025f7002 Mon Sep 17 00:00:00 2001 From: Brian Ashworth Date: Tue, 24 Jul 2018 18:41:08 -0400 Subject: [PATCH 039/148] Implement floating_modifier [inverse|normal] --- include/sway/config.h | 1 + sway/commands/floating_modifier.c | 12 +++++++++++- sway/config.c | 1 + sway/input/cursor.c | 8 +++++--- 4 files changed, 18 insertions(+), 4 deletions(-) diff --git a/include/sway/config.h b/include/sway/config.h index 032f4196..4a6bb780 100644 --- a/include/sway/config.h +++ b/include/sway/config.h @@ -320,6 +320,7 @@ struct sway_config { struct bar_config *current_bar; char *swaybg_command; uint32_t floating_mod; + bool floating_mod_inverse; uint32_t dragging_key; uint32_t resizing_key; char *floating_scroll_up_cmd; diff --git a/sway/commands/floating_modifier.c b/sway/commands/floating_modifier.c index 9432c9f1..f5d2b3fe 100644 --- a/sway/commands/floating_modifier.c +++ b/sway/commands/floating_modifier.c @@ -1,10 +1,11 @@ +#include "strings.h" #include "sway/commands.h" #include "sway/config.h" #include "util.h" struct cmd_results *cmd_floating_modifier(int argc, char **argv) { struct cmd_results *error = NULL; - if ((error = checkarg(argc, "floating_modifier", EXPECTED_EQUAL_TO, 1))) { + if ((error = checkarg(argc, "floating_modifier", EXPECTED_AT_LEAST, 1))) { return error; } @@ -14,6 +15,15 @@ struct cmd_results *cmd_floating_modifier(int argc, char **argv) { "Invalid modifier"); } + if (argc == 1 || strcasecmp(argv[1], "normal") == 0) { + config->floating_mod_inverse = false; + } else if (strcasecmp(argv[1], "inverse") == 0) { + config->floating_mod_inverse = true; + } else { + return cmd_results_new(CMD_INVALID, "floating_modifier", + "Usage: floating_modifier [inverse|normal]"); + } + config->floating_mod = mod; return cmd_results_new(CMD_SUCCESS, NULL, NULL); diff --git a/sway/config.c b/sway/config.c index 90dfb9a9..2afffab1 100644 --- a/sway/config.c +++ b/sway/config.c @@ -180,6 +180,7 @@ static void config_defaults(struct sway_config *config) { list_add(config->modes, config->current_mode); config->floating_mod = 0; + config->floating_mod_inverse = false; config->dragging_key = BTN_LEFT; config->resizing_key = BTN_RIGHT; diff --git a/sway/input/cursor.c b/sway/input/cursor.c index 27597640..9eb11de1 100644 --- a/sway/input/cursor.c +++ b/sway/input/cursor.c @@ -449,15 +449,17 @@ static void dispatch_cursor_button_floating(struct sway_cursor *cursor, bool over_title = edge == WLR_EDGE_NONE && !surface; // Check for beginning move - if (button == BTN_LEFT && state == WLR_BUTTON_PRESSED && + uint32_t btn_move = config->floating_mod_inverse ? BTN_RIGHT : BTN_LEFT; + if (button == btn_move && state == WLR_BUTTON_PRESSED && (mod_pressed || over_title)) { - seat_begin_move(seat, cont, BTN_LEFT); + seat_begin_move(seat, cont, btn_move); return; } // Check for beginning resize bool resizing_via_border = button == BTN_LEFT && edge != WLR_EDGE_NONE; - bool resizing_via_mod = button == BTN_RIGHT && mod_pressed; + uint32_t btn_resize = config->floating_mod_inverse ? BTN_LEFT : BTN_RIGHT; + bool resizing_via_mod = button == btn_resize && mod_pressed; if ((resizing_via_border || resizing_via_mod) && state == WLR_BUTTON_PRESSED) { if (edge == WLR_EDGE_NONE) { From 3a75bb7f3290b33872d4c61a131bb0eec876f3ae Mon Sep 17 00:00:00 2001 From: Brian Ashworth Date: Tue, 24 Jul 2018 18:52:54 -0400 Subject: [PATCH 040/148] Change button var passed to seat_begin_move --- sway/input/cursor.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sway/input/cursor.c b/sway/input/cursor.c index 9eb11de1..d94a707c 100644 --- a/sway/input/cursor.c +++ b/sway/input/cursor.c @@ -452,7 +452,7 @@ static void dispatch_cursor_button_floating(struct sway_cursor *cursor, uint32_t btn_move = config->floating_mod_inverse ? BTN_RIGHT : BTN_LEFT; if (button == btn_move && state == WLR_BUTTON_PRESSED && (mod_pressed || over_title)) { - seat_begin_move(seat, cont, btn_move); + seat_begin_move(seat, cont, button); return; } From 0e79b2114c0bd374c6b4a37fc2ee0e672b8fbb38 Mon Sep 17 00:00:00 2001 From: emersion Date: Wed, 25 Jul 2018 08:50:06 +0100 Subject: [PATCH 041/148] Improve rendering with a fullscreen opaque overlay surface The rendering code doesn't use the exclusive input surface at all anymore to decide to skip rendering of shell surfaces. This fixes a weird situation in which a client requests exclusive input but isn't an overlay layer surface. The renderer also renders all overlay surfaces in this situation, not just one. This simplifies the code and fixes rendering when there are more than one overlay surfaces (e.g. for a virtual keyboard to type the lockscreen password). --- include/sway/output.h | 3 +-- sway/desktop/output.c | 26 ++++++++------------------ sway/desktop/render.c | 21 +++++++-------------- 3 files changed, 16 insertions(+), 34 deletions(-) diff --git a/include/sway/output.h b/include/sway/output.h index b6cda83c..c225e541 100644 --- a/include/sway/output.h +++ b/include/sway/output.h @@ -65,8 +65,7 @@ struct sway_container *output_by_name(const char *name); void output_enable(struct sway_output *output); -bool output_has_opaque_lockscreen(struct sway_output *output, - struct sway_seat *seat); +bool output_has_opaque_overlay_layer_surface(struct sway_output *output); struct sway_container *output_get_active_workspace(struct sway_output *output); diff --git a/sway/desktop/output.c b/sway/desktop/output.c index a206ac6b..cbd6ef86 100644 --- a/sway/desktop/output.c +++ b/sway/desktop/output.c @@ -181,21 +181,14 @@ struct sway_container *output_get_active_workspace(struct sway_output *output) { return workspace; } -bool output_has_opaque_lockscreen(struct sway_output *output, - struct sway_seat *seat) { - if (!seat->exclusive_client) { - return false; - } - +bool output_has_opaque_overlay_layer_surface(struct sway_output *output) { struct wlr_layer_surface *wlr_layer_surface; wl_list_for_each(wlr_layer_surface, &server.layer_shell->surfaces, link) { - if (wlr_layer_surface->output != output->wlr_output) { + if (wlr_layer_surface->output != output->wlr_output || + wlr_layer_surface->layer != ZWLR_LAYER_SHELL_V1_LAYER_OVERLAY) { continue; } struct wlr_surface *wlr_surface = wlr_layer_surface->surface; - if (wlr_surface->resource->client != seat->exclusive_client) { - continue; - } struct sway_layer_surface *sway_layer_surface = layer_from_wlr_layer_surface(wlr_layer_surface); pixman_box32_t output_box = { @@ -221,16 +214,11 @@ struct send_frame_done_data { struct root_geometry root_geo; struct sway_output *output; struct timespec *when; - struct wl_client *exclusive_client; }; static void send_frame_done_iterator(struct wlr_surface *surface, int sx, int sy, void *_data) { struct send_frame_done_data *data = _data; - if (data->exclusive_client && - data->exclusive_client != surface->resource->client) { - return; - } bool intersects = output_get_surface_box(&data->root_geo, data->output, surface, sx, sy, NULL); @@ -279,14 +267,15 @@ static void send_frame_done_container(struct send_frame_done_data *data, } static void send_frame_done(struct sway_output *output, struct timespec *when) { - struct sway_seat *seat = input_manager_current_seat(input_manager); struct send_frame_done_data data = { .output = output, .when = when, - .exclusive_client = output_has_opaque_lockscreen(output, seat) ? - seat->exclusive_client : NULL, }; + if (output_has_opaque_overlay_layer_surface(output)) { + goto send_frame_overlay; + } + struct sway_container *workspace = output_get_active_workspace(output); if (workspace->current.ws_fullscreen) { send_frame_done_container_iterator( @@ -311,6 +300,7 @@ static void send_frame_done(struct sway_output *output, struct timespec *when) { &output->layers[ZWLR_LAYER_SHELL_V1_LAYER_TOP]); } +send_frame_overlay: send_frame_done_layer(&data, &output->layers[ZWLR_LAYER_SHELL_V1_LAYER_OVERLAY]); send_frame_done_drag_icons(&data, &root_container.sway_root->drag_icons); diff --git a/sway/desktop/render.c b/sway/desktop/render.c index 7da54594..d6c3fa8c 100644 --- a/sway/desktop/render.c +++ b/sway/desktop/render.c @@ -836,21 +836,12 @@ void output_render(struct sway_output *output, struct timespec *when, struct sway_container *workspace = output_get_active_workspace(output); struct sway_view *fullscreen_view = workspace->current.ws_fullscreen; - struct sway_seat *seat = input_manager_current_seat(input_manager); - if (output_has_opaque_lockscreen(output, seat) && seat->focused_layer) { - struct wlr_layer_surface *wlr_layer_surface = seat->focused_layer; - struct sway_layer_surface *sway_layer_surface = - layer_from_wlr_layer_surface(seat->focused_layer); - struct render_data data = { - .output = output, - .damage = damage, - .alpha = 1.0f, - }; - output_surface_for_each_surface(wlr_layer_surface->surface, - sway_layer_surface->geo.x, sway_layer_surface->geo.y, - &data.root_geo, render_surface_iterator, &data); - } else if (fullscreen_view) { + if (output_has_opaque_overlay_layer_surface(output)) { + goto render_overlay; + } + + if (fullscreen_view) { float clear_color[] = {0.0f, 0.0f, 0.0f, 1.0f}; int nrects; @@ -894,6 +885,8 @@ void output_render(struct sway_output *output, struct timespec *when, render_layer(output, damage, &output->layers[ZWLR_LAYER_SHELL_V1_LAYER_TOP]); } + +render_overlay: render_layer(output, damage, &output->layers[ZWLR_LAYER_SHELL_V1_LAYER_OVERLAY]); render_drag_icons(output, damage, &root_container.sway_root->drag_icons); From b14bd1b0b1536039e4f46fe94515c7c44e7afc61 Mon Sep 17 00:00:00 2001 From: Ryan Dwyer Date: Wed, 25 Jul 2018 19:26:12 +1000 Subject: [PATCH 042/148] Fix crash when closing last child of a tabbed container The crash only occurs if the mouse cursor is above the tabbed container when the last child is closed. Introduced in 03d49490ccff3c5c81bea73622c8616fa61eb3dd, over a week ago and unnoticed until now :O The above commit changes the behaviour of a focus change. When you change focus, it sends pointer motion which makes the client set a new cursor image. We already had this behaviour for workspace switching, but this commit adds it for view switching too, such as in a tabbed container or when closing a view. The sequence of events that leads to the crash is: * The last child of a tabbed container unmaps, which triggers a `destroy` event before we've cleaned up the child or reaped the tabbed container. * The seat code listens to the `destroy` event and removes the seat container from the focus stack. As part of this, it decides to set focus to the parent (my fix alters this decision). * When setting focus to the new parent, the container motion is sent as per the previously mentioned commit. * The motion code uses `container_at`, which encounters the tabbed container and its child in a half destroyed state, and everything blows up from there. `con->parent` is needed because scratchpad containers don't have parents if they're hidden, so this probably fixes a crash when a hidden scratchpad container closes too. The `con->parent->children->length > 1` check should catch any cases where the parent is about to be reaped. --- sway/input/seat.c | 1 + 1 file changed, 1 insertion(+) diff --git a/sway/input/seat.c b/sway/input/seat.c index fc9e54b6..8c634e5f 100644 --- a/sway/input/seat.c +++ b/sway/input/seat.c @@ -181,6 +181,7 @@ static void handle_seat_container_destroy(struct wl_listener *listener, bool set_focus = focus != NULL && (focus == con || container_has_child(con, focus)) && + con->parent && con->parent->children->length > 1 && con->type != C_WORKSPACE; seat_container_destroy(seat_con); From 79a45d4a4049e3b9235915cd308ed2995623f833 Mon Sep 17 00:00:00 2001 From: Pascal Pascher Date: Wed, 25 Jul 2018 12:17:10 +0200 Subject: [PATCH 043/148] more style fixes, included "sway/config.h" where needed --- include/sway/server.h | 1 + include/sway/xwayland.h | 2 -- sway/commands/swap.c | 1 + sway/criteria.c | 21 +++++++++++---------- sway/input/cursor.c | 1 + sway/input/seat.c | 1 + sway/tree/layout.c | 1 + sway/tree/view.c | 1 + 8 files changed, 17 insertions(+), 12 deletions(-) diff --git a/include/sway/server.h b/include/sway/server.h index b6a6bde3..a3782f91 100644 --- a/include/sway/server.h +++ b/include/sway/server.h @@ -12,6 +12,7 @@ #include // TODO WLR: make Xwayland optional #include "list.h" +#include "config.h" #ifdef HAVE_XWAYLAND #include "sway/xwayland.h" #endif diff --git a/include/sway/xwayland.h b/include/sway/xwayland.h index e6572ae9..78d1053b 100644 --- a/include/sway/xwayland.h +++ b/include/sway/xwayland.h @@ -1,4 +1,3 @@ -#ifdef HAVE_XWAYLAND #ifndef SWAY_XWAYLAND_H #define SWAY_XWAYLAND_H @@ -24,4 +23,3 @@ struct sway_xwayland { void handle_xwayland_ready(struct wl_listener *listener, void *data); #endif -#endif diff --git a/sway/commands/swap.c b/sway/commands/swap.c index 325adc38..8b8e9d79 100644 --- a/sway/commands/swap.c +++ b/sway/commands/swap.c @@ -1,5 +1,6 @@ #include #include +#include "sway/config.h" #include "sway/commands.h" #include "sway/tree/arrange.h" #include "sway/tree/layout.h" diff --git a/sway/criteria.c b/sway/criteria.c index b2c6edf9..48899125 100644 --- a/sway/criteria.c +++ b/sway/criteria.c @@ -10,6 +10,7 @@ #include "stringop.h" #include "list.h" #include "log.h" +#include "config.h" bool criteria_is_empty(struct criteria *criteria) { return !criteria->title @@ -19,9 +20,9 @@ bool criteria_is_empty(struct criteria *criteria) { && !criteria->instance && !criteria->con_mark && !criteria->con_id - #ifdef HAVE_XWAYLAND +#ifdef HAVE_XWAYLAND && !criteria->id - #endif +#endif && !criteria->window_role && !criteria->window_type && !criteria->floating @@ -129,14 +130,14 @@ static bool criteria_matches_view(struct criteria *criteria, } } - #ifdef HAVE_XWAYLAND +#ifdef HAVE_XWAYLAND if (criteria->id) { // X11 window ID uint32_t x11_window_id = view_get_x11_window_id(view); if (!x11_window_id || x11_window_id != criteria->id) { return false; } } - #endif +#endif if (criteria->window_role) { // TODO @@ -293,10 +294,10 @@ static enum criteria_token token_from_name(char *name) { return T_CON_ID; } else if (strcmp(name, "con_mark") == 0) { return T_CON_MARK; - #ifdef HAVE_XWAYLAND +#ifdef HAVE_XWAYLAND } else if (strcmp(name, "id") == 0) { return T_ID; - #endif +#endif } else if (strcmp(name, "instance") == 0) { return T_INSTANCE; } else if (strcmp(name, "shell") == 0) { @@ -363,9 +364,9 @@ static char *get_focused_prop(enum criteria_token token) { case T_CON_ID: // These do not support __focused__ case T_CON_MARK: case T_FLOATING: - #ifdef HAVE_XWAYLAND +#ifdef HAVE_XWAYLAND case T_ID: - #endif +#endif case T_TILING: case T_URGENT: case T_WINDOW_TYPE: @@ -436,14 +437,14 @@ static bool parse_token(struct criteria *criteria, char *name, char *value) { case T_WINDOW_TYPE: // TODO: This is a string but will be stored as an enum or integer break; - #ifdef HAVE_XWAYLAND +#ifdef HAVE_XWAYLAND case T_ID: criteria->id = strtoul(effective_value, &endptr, 10); if (*endptr != 0) { error = strdup("The value for 'id' should be numeric"); } break; - #endif +#endif case T_FLOATING: criteria->floating = true; break; diff --git a/sway/input/cursor.c b/sway/input/cursor.c index 33eebf97..3de36e1c 100644 --- a/sway/input/cursor.c +++ b/sway/input/cursor.c @@ -11,6 +11,7 @@ #include #include "list.h" #include "log.h" +#include "sway/config.h" #include "sway/desktop.h" #include "sway/desktop/transaction.h" #include "sway/input/cursor.h" diff --git a/sway/input/seat.c b/sway/input/seat.c index 6b4e5f2e..2d62b101 100644 --- a/sway/input/seat.c +++ b/sway/input/seat.c @@ -12,6 +12,7 @@ #include #include #include "log.h" +#include "sway/config.h" #include "sway/debug.h" #include "sway/desktop.h" #include "sway/input/cursor.h" diff --git a/sway/tree/layout.c b/sway/tree/layout.c index 3d361ea5..91f6accd 100644 --- a/sway/tree/layout.c +++ b/sway/tree/layout.c @@ -6,6 +6,7 @@ #include #include #include +#include "sway/config.h" #include "sway/debug.h" #include "sway/tree/arrange.h" #include "sway/tree/container.h" diff --git a/sway/tree/view.c b/sway/tree/view.c index 2eff80a8..7cc15ae3 100644 --- a/sway/tree/view.c +++ b/sway/tree/view.c @@ -3,6 +3,7 @@ #include #include #include +#include "sway/config.h" #ifdef HAVE_XWAYLAND #include #endif From f95cb9a7c17251c43b1d1058092e0a15d1fe8dd9 Mon Sep 17 00:00:00 2001 From: Pascal Pascher Date: Wed, 25 Jul 2018 13:32:20 +0200 Subject: [PATCH 044/148] reverted includes of "sway/config.h" and replaced with "config.h" from meson build --- include/sway/criteria.h | 1 + include/sway/tree/layout.h | 1 + include/sway/tree/view.h | 1 + sway/commands/swap.c | 2 +- sway/desktop/output.c | 1 + sway/desktop/render.c | 1 + sway/input/cursor.c | 2 +- sway/input/seat.c | 2 +- sway/server.c | 1 + sway/tree/layout.c | 2 +- sway/tree/view.c | 2 +- 11 files changed, 11 insertions(+), 5 deletions(-) diff --git a/include/sway/criteria.h b/include/sway/criteria.h index 23efe9d4..b4ff7d49 100644 --- a/include/sway/criteria.h +++ b/include/sway/criteria.h @@ -2,6 +2,7 @@ #define _SWAY_CRITERIA_H #include +#include "config.h" #include "list.h" #include "tree/view.h" diff --git a/include/sway/tree/layout.h b/include/sway/tree/layout.h index 580acd16..a4c31bf6 100644 --- a/include/sway/tree/layout.h +++ b/include/sway/tree/layout.h @@ -3,6 +3,7 @@ #include #include #include "sway/tree/container.h" +#include "config.h" enum movement_direction { MOVE_LEFT, diff --git a/include/sway/tree/view.h b/include/sway/tree/view.h index d4416dd3..1972447b 100644 --- a/include/sway/tree/view.h +++ b/include/sway/tree/view.h @@ -3,6 +3,7 @@ #include #include #include +#include "config.h" #ifdef HAVE_XWAYLAND #include #endif diff --git a/sway/commands/swap.c b/sway/commands/swap.c index 8b8e9d79..4e3a9cce 100644 --- a/sway/commands/swap.c +++ b/sway/commands/swap.c @@ -1,6 +1,6 @@ #include #include -#include "sway/config.h" +#include "config.h" #include "sway/commands.h" #include "sway/tree/arrange.h" #include "sway/tree/layout.h" diff --git a/sway/desktop/output.c b/sway/desktop/output.c index 05daad7b..1512408e 100644 --- a/sway/desktop/output.c +++ b/sway/desktop/output.c @@ -14,6 +14,7 @@ #include #include #include "log.h" +#include "config.h" #include "sway/config.h" #include "sway/input/input-manager.h" #include "sway/input/seat.h" diff --git a/sway/desktop/render.c b/sway/desktop/render.c index 2e6b6649..3e7bd94b 100644 --- a/sway/desktop/render.c +++ b/sway/desktop/render.c @@ -14,6 +14,7 @@ #include #include #include "log.h" +#include "config.h" #include "sway/config.h" #include "sway/debug.h" #include "sway/input/input-manager.h" diff --git a/sway/input/cursor.c b/sway/input/cursor.c index 3de36e1c..2468a341 100644 --- a/sway/input/cursor.c +++ b/sway/input/cursor.c @@ -11,7 +11,7 @@ #include #include "list.h" #include "log.h" -#include "sway/config.h" +#include "config.h" #include "sway/desktop.h" #include "sway/desktop/transaction.h" #include "sway/input/cursor.h" diff --git a/sway/input/seat.c b/sway/input/seat.c index 2d62b101..cc0b16cf 100644 --- a/sway/input/seat.c +++ b/sway/input/seat.c @@ -12,7 +12,7 @@ #include #include #include "log.h" -#include "sway/config.h" +#include "config.h" #include "sway/debug.h" #include "sway/desktop.h" #include "sway/input/cursor.h" diff --git a/sway/server.c b/sway/server.c index 1521597f..10ca9614 100644 --- a/sway/server.c +++ b/sway/server.c @@ -25,6 +25,7 @@ #include "sway/input/input-manager.h" #include "sway/server.h" #include "sway/tree/layout.h" +#include "config.h" #ifdef HAVE_XWAYLAND #include "sway/xwayland.h" #endif diff --git a/sway/tree/layout.c b/sway/tree/layout.c index 91f6accd..2b3263f8 100644 --- a/sway/tree/layout.c +++ b/sway/tree/layout.c @@ -6,7 +6,7 @@ #include #include #include -#include "sway/config.h" +#include "config.h" #include "sway/debug.h" #include "sway/tree/arrange.h" #include "sway/tree/container.h" diff --git a/sway/tree/view.c b/sway/tree/view.c index 7cc15ae3..f672417e 100644 --- a/sway/tree/view.c +++ b/sway/tree/view.c @@ -3,12 +3,12 @@ #include #include #include -#include "sway/config.h" #ifdef HAVE_XWAYLAND #include #endif #include "list.h" #include "log.h" +#include "config.h" #include "sway/criteria.h" #include "sway/commands.h" #include "sway/ipc-server.h" From 384c55c0b465d6b9a9c4cb88d8752435ac5cf708 Mon Sep 17 00:00:00 2001 From: Pascal Pascher Date: Wed, 25 Jul 2018 13:43:21 +0200 Subject: [PATCH 045/148] more style fixes, reorder config.h include --- sway/criteria.c | 4 ++-- sway/tree/view.c | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/sway/criteria.c b/sway/criteria.c index 48899125..39d300ea 100644 --- a/sway/criteria.c +++ b/sway/criteria.c @@ -270,9 +270,9 @@ enum criteria_token { T_CON_ID, T_CON_MARK, T_FLOATING, - #ifdef HAVE_XWAYLAND +#ifdef HAVE_XWAYLAND T_ID, - #endif +#endif T_INSTANCE, T_SHELL, T_TILING, diff --git a/sway/tree/view.c b/sway/tree/view.c index f672417e..beeb8144 100644 --- a/sway/tree/view.c +++ b/sway/tree/view.c @@ -3,12 +3,12 @@ #include #include #include +#include "config.h" #ifdef HAVE_XWAYLAND #include #endif #include "list.h" #include "log.h" -#include "config.h" #include "sway/criteria.h" #include "sway/commands.h" #include "sway/ipc-server.h" From 445a09575ceffbb05b9045ecd0ce89a6c2e173dc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=C3=A9dric=20Cabessa?= Date: Wed, 25 Jul 2018 17:06:50 +0200 Subject: [PATCH 046/148] fix #2355: "move workspace to output up" crashes when using 2 display, if scaling is different `container_update_textures_recursive` is called when moving workspace on different display. We need to call `container_update_title_textures` only for container of type "CONTAINER" or "VIEW" in order to be consistent with the assert in `update_title_texture`. --- sway/tree/container.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/sway/tree/container.c b/sway/tree/container.c index b56b4e87..237e1a35 100644 --- a/sway/tree/container.c +++ b/sway/tree/container.c @@ -68,7 +68,9 @@ void container_create_notify(struct sway_container *container) { } static void container_update_textures_recursive(struct sway_container *con) { - container_update_title_textures(con); + if (con->type == C_CONTAINER || con->type == C_VIEW) { + container_update_title_textures(con); + } if (con->type == C_VIEW) { view_update_marks_textures(con->sway_view); From 2166dbe2e49fc5eeb345d744424db360ee63f508 Mon Sep 17 00:00:00 2001 From: ProgAndy Date: Sun, 22 Jul 2018 21:16:19 +0200 Subject: [PATCH 047/148] Implement setting NumLock and CapsLock status After setting the keymap, try to enable NumLock and disable CapsLock. This only works if sway has the xkb master state and controls the keyboard. Prepare configuration settings for later use as well. --- include/sway/config.h | 3 +++ sway/config/input.c | 8 ++++++++ sway/input/keyboard.c | 18 ++++++++++++++++++ 3 files changed, 29 insertions(+) diff --git a/include/sway/config.h b/include/sway/config.h index 4a6bb780..0f74b439 100644 --- a/include/sway/config.h +++ b/include/sway/config.h @@ -101,6 +101,9 @@ struct input_config { char *xkb_rules; char *xkb_variant; + int xkb_numlock; + int xkb_capslock; + struct input_config_mapped_from_region *mapped_from_region; char *mapped_to_output; diff --git a/sway/config/input.c b/sway/config/input.c index 8d687a6d..9885e85c 100644 --- a/sway/config/input.c +++ b/sway/config/input.c @@ -33,6 +33,8 @@ struct input_config *new_input_config(const char* identifier) { input->left_handed = INT_MIN; input->repeat_delay = INT_MIN; input->repeat_rate = INT_MIN; + input->xkb_numlock = INT_MIN; + input->xkb_capslock = INT_MIN; return input; } @@ -104,6 +106,12 @@ void merge_input_config(struct input_config *dst, struct input_config *src) { free(dst->xkb_variant); dst->xkb_variant = strdup(src->xkb_variant); } + if (src->xkb_numlock != INT_MIN) { + dst->xkb_numlock = src->xkb_numlock; + } + if (src->xkb_capslock != INT_MIN) { + dst->xkb_capslock = src->xkb_capslock; + } if (src->mapped_from_region) { free(dst->mapped_from_region); dst->mapped_from_region = diff --git a/sway/input/keyboard.c b/sway/input/keyboard.c index 49241db8..36c5a064 100644 --- a/sway/input/keyboard.c +++ b/sway/input/keyboard.c @@ -3,6 +3,7 @@ #include #include #include +#include #include "sway/commands.h" #include "sway/desktop/transaction.h" #include "sway/input/input-manager.h" @@ -385,6 +386,23 @@ void sway_keyboard_configure(struct sway_keyboard *keyboard) { keyboard->keymap = keymap; wlr_keyboard_set_keymap(wlr_device->keyboard, keyboard->keymap); + xkb_mod_mask_t locked_mods = 0; + if (!input_config || input_config->xkb_numlock != 0) { + xkb_mod_index_t mod_index = xkb_map_mod_get_index(keymap, XKB_MOD_NAME_NUM); + if (mod_index != XKB_MOD_INVALID) { + locked_mods |= (uint32_t)1 << mod_index; + } + } + if (input_config && input_config->xkb_capslock > 0) { + xkb_mod_index_t mod_index = xkb_map_mod_get_index(keymap, XKB_MOD_NAME_CAPS); + if (mod_index != XKB_MOD_INVALID) { + locked_mods |= (uint32_t)1 << mod_index; + } + } + if (locked_mods) { + wlr_keyboard_notify_modifiers(wlr_device->keyboard, 0, 0, locked_mods, 0); + } + if (input_config && input_config->repeat_delay != INT_MIN && input_config->repeat_rate != INT_MIN) { wlr_keyboard_set_repeat_info(wlr_device->keyboard, From c7a3a03115aa672b8abd1155e7e93e6328512326 Mon Sep 17 00:00:00 2001 From: ProgAndy Date: Sun, 22 Jul 2018 22:22:16 +0200 Subject: [PATCH 048/148] Add xkb_numlock/xkb_capslock commands (#2311) --- include/sway/commands.h | 2 ++ sway/commands/input.c | 2 ++ sway/commands/input/xkb_capslock.c | 33 ++++++++++++++++++++++++++++++ sway/commands/input/xkb_numlock.c | 33 ++++++++++++++++++++++++++++++ sway/meson.build | 2 ++ 5 files changed, 72 insertions(+) create mode 100644 sway/commands/input/xkb_capslock.c create mode 100644 sway/commands/input/xkb_numlock.c diff --git a/include/sway/commands.h b/include/sway/commands.h index f53d335a..41858ccc 100644 --- a/include/sway/commands.h +++ b/include/sway/commands.h @@ -213,8 +213,10 @@ sway_cmd input_cmd_scroll_button; sway_cmd input_cmd_scroll_method; sway_cmd input_cmd_tap; sway_cmd input_cmd_tap_button_map; +sway_cmd input_cmd_xkb_capslock; sway_cmd input_cmd_xkb_layout; sway_cmd input_cmd_xkb_model; +sway_cmd input_cmd_xkb_numlock; sway_cmd input_cmd_xkb_options; sway_cmd input_cmd_xkb_rules; sway_cmd input_cmd_xkb_variant; diff --git a/sway/commands/input.c b/sway/commands/input.c index 5b203ea0..7f927073 100644 --- a/sway/commands/input.c +++ b/sway/commands/input.c @@ -24,8 +24,10 @@ static struct cmd_handler input_handlers[] = { { "scroll_method", input_cmd_scroll_method }, { "tap", input_cmd_tap }, { "tap_button_map", input_cmd_tap_button_map }, + { "xkb_capslock", input_cmd_xkb_capslock }, { "xkb_layout", input_cmd_xkb_layout }, { "xkb_model", input_cmd_xkb_model }, + { "xkb_numlock", input_cmd_xkb_numlock }, { "xkb_options", input_cmd_xkb_options }, { "xkb_rules", input_cmd_xkb_rules }, { "xkb_variant", input_cmd_xkb_variant }, diff --git a/sway/commands/input/xkb_capslock.c b/sway/commands/input/xkb_capslock.c new file mode 100644 index 00000000..5442c463 --- /dev/null +++ b/sway/commands/input/xkb_capslock.c @@ -0,0 +1,33 @@ +#include +#include +#include "sway/config.h" +#include "sway/commands.h" +#include "sway/input/input-manager.h" + +struct cmd_results *input_cmd_xkb_capslock(int argc, char **argv) { + struct cmd_results *error = NULL; + if ((error = checkarg(argc, "xkb_capslock", EXPECTED_AT_LEAST, 1))) { + return error; + } + struct input_config *current_input_config = + config->handler_context.input_config; + if (!current_input_config) { + return cmd_results_new(CMD_FAILURE, "xkb_capslock", + "No input device defined."); + } + struct input_config *new_config = + new_input_config(current_input_config->identifier); + + if (strcasecmp(argv[0], "enabled") == 0) { + new_config->xkb_capslock = 1; + } else if (strcasecmp(argv[0], "disabled") == 0) { + new_config->xkb_capslock = 0; + } else { + free_input_config(new_config); + return cmd_results_new(CMD_INVALID, "xkb_capslock", + "Expected 'xkb_capslock '"); + } + + apply_input_config(new_config); + return cmd_results_new(CMD_SUCCESS, NULL, NULL); +} diff --git a/sway/commands/input/xkb_numlock.c b/sway/commands/input/xkb_numlock.c new file mode 100644 index 00000000..39675366 --- /dev/null +++ b/sway/commands/input/xkb_numlock.c @@ -0,0 +1,33 @@ +#include +#include +#include "sway/config.h" +#include "sway/commands.h" +#include "sway/input/input-manager.h" + +struct cmd_results *input_cmd_xkb_numlock(int argc, char **argv) { + struct cmd_results *error = NULL; + if ((error = checkarg(argc, "xkb_numlock", EXPECTED_AT_LEAST, 1))) { + return error; + } + struct input_config *current_input_config = + config->handler_context.input_config; + if (!current_input_config) { + return cmd_results_new(CMD_FAILURE, "xkb_numlock", + "No input device defined."); + } + struct input_config *new_config = + new_input_config(current_input_config->identifier); + + if (strcasecmp(argv[0], "enabled") == 0) { + new_config->xkb_numlock = 1; + } else if (strcasecmp(argv[0], "disabled") == 0) { + new_config->xkb_numlock = 0; + } else { + free_input_config(new_config); + return cmd_results_new(CMD_INVALID, "xkb_numlock", + "Expected 'xkb_numlock '"); + } + + apply_input_config(new_config); + return cmd_results_new(CMD_SUCCESS, NULL, NULL); +} diff --git a/sway/meson.build b/sway/meson.build index 649a3ac2..d92bb905 100644 --- a/sway/meson.build +++ b/sway/meson.build @@ -128,8 +128,10 @@ sway_sources = files( 'commands/input/scroll_method.c', 'commands/input/tap.c', 'commands/input/tap_button_map.c', + 'commands/input/xkb_capslock.c', 'commands/input/xkb_layout.c', 'commands/input/xkb_model.c', + 'commands/input/xkb_numlock.c', 'commands/input/xkb_options.c', 'commands/input/xkb_rules.c', 'commands/input/xkb_variant.c', From d07463d53b2677ba0662b2b91aad484f67fc6405 Mon Sep 17 00:00:00 2001 From: ProgAndy Date: Mon, 23 Jul 2018 14:41:11 +0200 Subject: [PATCH 049/148] Add documentation for xkb_capslock/xkb_numlock --- sway/sway-input.5.scd | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/sway/sway-input.5.scd b/sway/sway-input.5.scd index b6391431..372ca697 100644 --- a/sway/sway-input.5.scd +++ b/sway/sway-input.5.scd @@ -33,6 +33,14 @@ For more information on these xkb configuration options, see *input* xkb\_variant Sets the variant of the keyboard like _dvorak_ or _colemak_. +*input* xkb\_capslock enabled|disabled + Enables or disables CapsLock on sway startup, the default is disabled. + Only meaningful if sway controls the keyboard. + +*input* xkb\_numlock enabled|disabled + Enables or disables NumLock on sway startup, the default is enabled. + Only meaningful if sway controls the keyboard. + ## MAPPING CONFIGURATION *input* map\_to\_output From 0ba52458abb8fb0414319d2084e6d0d0214ae304 Mon Sep 17 00:00:00 2001 From: ProgAndy Date: Wed, 25 Jul 2018 17:08:47 +0200 Subject: [PATCH 050/148] Restrict CapsLock and NumLock commands to the configuration file --- sway/commands/input.c | 23 ++++++++++++++++++++--- sway/sway-input.5.scd | 8 ++++---- 2 files changed, 24 insertions(+), 7 deletions(-) diff --git a/sway/commands/input.c b/sway/commands/input.c index 7f927073..84888fbb 100644 --- a/sway/commands/input.c +++ b/sway/commands/input.c @@ -24,15 +24,19 @@ static struct cmd_handler input_handlers[] = { { "scroll_method", input_cmd_scroll_method }, { "tap", input_cmd_tap }, { "tap_button_map", input_cmd_tap_button_map }, - { "xkb_capslock", input_cmd_xkb_capslock }, { "xkb_layout", input_cmd_xkb_layout }, { "xkb_model", input_cmd_xkb_model }, - { "xkb_numlock", input_cmd_xkb_numlock }, { "xkb_options", input_cmd_xkb_options }, { "xkb_rules", input_cmd_xkb_rules }, { "xkb_variant", input_cmd_xkb_variant }, }; +// must be in order for the bsearch +static struct cmd_handler input_config_handlers[] = { + { "xkb_capslock", input_cmd_xkb_capslock }, + { "xkb_numlock", input_cmd_xkb_numlock }, +}; + struct cmd_results *cmd_input(int argc, char **argv) { struct cmd_results *error = NULL; if ((error = checkarg(argc, "input", EXPECTED_AT_LEAST, 2))) { @@ -46,8 +50,21 @@ struct cmd_results *cmd_input(int argc, char **argv) { return cmd_results_new(CMD_FAILURE, NULL, "Couldn't allocate config"); } - struct cmd_results *res = config_subcommand(argv + 1, argc - 1, + struct cmd_results *res; + + if (find_handler(argv[1], input_config_handlers, + sizeof(input_config_handlers))) { + if (config->reading) { + res = config_subcommand(argv + 1, argc - 1, + input_config_handlers, sizeof(input_config_handlers)); + } else { + res = cmd_results_new(CMD_FAILURE, "input", + "Can only be used in config file."); + } + } else { + res = config_subcommand(argv + 1, argc - 1, input_handlers, sizeof(input_handlers)); + } free_input_config(config->handler_context.input_config); config->handler_context.input_config = NULL; diff --git a/sway/sway-input.5.scd b/sway/sway-input.5.scd index 372ca697..fa311971 100644 --- a/sway/sway-input.5.scd +++ b/sway/sway-input.5.scd @@ -33,13 +33,13 @@ For more information on these xkb configuration options, see *input* xkb\_variant Sets the variant of the keyboard like _dvorak_ or _colemak_. +The following commands may only be used in the configuration file. + *input* xkb\_capslock enabled|disabled - Enables or disables CapsLock on sway startup, the default is disabled. - Only meaningful if sway controls the keyboard. + Initially enables or disables CapsLock, the default is disabled. *input* xkb\_numlock enabled|disabled - Enables or disables NumLock on sway startup, the default is enabled. - Only meaningful if sway controls the keyboard. + Initially enables or disables NumLock, the default is enabled. ## MAPPING CONFIGURATION From 76ece5788bffe27b32c85968886f3738d244abf9 Mon Sep 17 00:00:00 2001 From: ProgAndy Date: Wed, 25 Jul 2018 17:09:34 +0200 Subject: [PATCH 051/148] Fix LEDs for configured modifier states --- sway/input/keyboard.c | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/sway/input/keyboard.c b/sway/input/keyboard.c index 36c5a064..643ff510 100644 --- a/sway/input/keyboard.c +++ b/sway/input/keyboard.c @@ -401,6 +401,14 @@ void sway_keyboard_configure(struct sway_keyboard *keyboard) { } if (locked_mods) { wlr_keyboard_notify_modifiers(wlr_device->keyboard, 0, 0, locked_mods, 0); + uint32_t leds = 0; + for (uint32_t i = 0; i < WLR_LED_COUNT; ++i) { + if (xkb_state_led_index_is_active(wlr_device->keyboard->xkb_state, + wlr_device->keyboard->led_indexes[i])) { + leds |= (1 << i); + } + } + wlr_keyboard_led_update(wlr_device->keyboard, leds); } if (input_config && input_config->repeat_delay != INT_MIN From 27a20a488465468511de9b2307941ac1bc4db8bf Mon Sep 17 00:00:00 2001 From: Ryan Dwyer Date: Wed, 25 Jul 2018 20:56:23 +1000 Subject: [PATCH 052/148] Allow containers to be fullscreen --- include/sway/tree/container.h | 23 ++++++++- include/sway/tree/view.h | 6 --- include/sway/tree/workspace.h | 2 +- sway/commands/fullscreen.c | 18 +++++--- sway/commands/move.c | 2 +- sway/desktop/output.c | 14 +++--- sway/desktop/render.c | 23 +++++---- sway/desktop/transaction.c | 2 +- sway/desktop/xdg_shell.c | 4 +- sway/desktop/xdg_shell_v6.c | 4 +- sway/desktop/xwayland.c | 4 +- sway/input/cursor.c | 14 ++---- sway/input/seat.c | 3 +- sway/scratchpad.c | 2 +- sway/tree/arrange.c | 18 +++++++- sway/tree/container.c | 87 +++++++++++++++++++++++++++++++++++ sway/tree/layout.c | 83 +++++++++++++++++++-------------- sway/tree/view.c | 73 ++--------------------------- 18 files changed, 226 insertions(+), 156 deletions(-) diff --git a/include/sway/tree/container.h b/include/sway/tree/container.h index 2a4be18c..c584cd92 100644 --- a/include/sway/tree/container.h +++ b/include/sway/tree/container.h @@ -60,6 +60,8 @@ struct sway_container_state { double swayc_x, swayc_y; double swayc_width, swayc_height; + bool is_fullscreen; + bool has_gaps; double current_gaps; double gaps_inner; @@ -74,7 +76,6 @@ struct sway_container_state { // View properties double view_x, view_y; double view_width, view_height; - bool is_fullscreen; enum sway_container_border border; int border_thickness; @@ -84,7 +85,7 @@ struct sway_container_state { bool border_right; // Workspace properties - struct sway_view *ws_fullscreen; + struct sway_container *ws_fullscreen; struct sway_container *ws_floating; }; @@ -124,6 +125,8 @@ struct sway_container { double saved_x, saved_y; double saved_width, saved_height; + bool is_fullscreen; + // The gaps currently applied to the container. double current_gaps; @@ -335,4 +338,20 @@ bool container_has_urgent_child(struct sway_container *container); */ void container_end_mouse_operation(struct sway_container *container); +void container_set_fullscreen(struct sway_container *container, bool enable); + +/** + * Return true if the container is fullscreen, or a child of a fullscreen split + * container. + */ +bool container_is_fullscreen_or_child(struct sway_container *container); + +/** + * Wrap the children of parent in a new container. The new container will be the + * only child of parent. + * + * The new container is returned. + */ +struct sway_container *container_wrap_children(struct sway_container *parent); + #endif diff --git a/include/sway/tree/view.h b/include/sway/tree/view.h index 1972447b..7086314f 100644 --- a/include/sway/tree/view.h +++ b/include/sway/tree/view.h @@ -69,8 +69,6 @@ struct sway_view { // Used when changing a view from tiled to floating. int natural_width, natural_height; - bool is_fullscreen; - char *title_format; enum sway_container_border border; int border_thickness; @@ -251,10 +249,6 @@ void view_set_activated(struct sway_view *view, bool activated); void view_set_tiled(struct sway_view *view, bool tiled); -void view_set_fullscreen_raw(struct sway_view *view, bool fullscreen); - -void view_set_fullscreen(struct sway_view *view, bool fullscreen); - void view_close(struct sway_view *view); void view_damage_from(struct sway_view *view); diff --git a/include/sway/tree/workspace.h b/include/sway/tree/workspace.h index ff66da6b..5ae0ae3a 100644 --- a/include/sway/tree/workspace.h +++ b/include/sway/tree/workspace.h @@ -7,7 +7,7 @@ struct sway_view; struct sway_workspace { struct sway_container *swayc; - struct sway_view *fullscreen; + struct sway_container *fullscreen; struct sway_container *floating; list_t *output_priority; bool urgent; diff --git a/sway/commands/fullscreen.c b/sway/commands/fullscreen.c index b423fd23..5ad06e40 100644 --- a/sway/commands/fullscreen.c +++ b/sway/commands/fullscreen.c @@ -14,18 +14,24 @@ struct cmd_results *cmd_fullscreen(int argc, char **argv) { } struct sway_container *container = config->handler_context.current_container; - if (container->type != C_VIEW) { + if (container->type == C_WORKSPACE && container->children->length == 0) { return cmd_results_new(CMD_INVALID, "fullscreen", - "Only views can fullscreen"); + "Can't fullscreen an empty workspace"); } - struct sway_view *view = container->sway_view; - bool wants_fullscreen = !view->is_fullscreen; + if (container->type == C_WORKSPACE) { + // Wrap the workspace's children in a container so we can fullscreen it + struct sway_container *workspace = container; + container = container_wrap_children(container); + workspace->layout = L_HORIZ; + seat_set_focus(config->handler_context.seat, container); + } + bool enable = !container->is_fullscreen; if (argc) { - wants_fullscreen = parse_boolean(argv[0], view->is_fullscreen); + enable = parse_boolean(argv[0], container->is_fullscreen); } - view_set_fullscreen(view, wants_fullscreen); + container_set_fullscreen(container, enable); struct sway_container *workspace = container_parent(container, C_WORKSPACE); arrange_windows(workspace->parent); diff --git a/sway/commands/move.c b/sway/commands/move.c index 1940043d..aede3d6c 100644 --- a/sway/commands/move.c +++ b/sway/commands/move.c @@ -196,7 +196,7 @@ static struct cmd_results *move_in_direction(struct sway_container *container, "Cannot move workspaces in a direction"); } if (container_is_floating(container)) { - if (container->type == C_VIEW && container->sway_view->is_fullscreen) { + if (container->is_fullscreen) { return cmd_results_new(CMD_FAILURE, "move", "Cannot move fullscreen floating container"); } diff --git a/sway/desktop/output.c b/sway/desktop/output.c index 1764b4e3..cecd300a 100644 --- a/sway/desktop/output.c +++ b/sway/desktop/output.c @@ -279,13 +279,15 @@ static void send_frame_done(struct sway_output *output, struct timespec *when) { struct sway_container *workspace = output_get_active_workspace(output); if (workspace->current.ws_fullscreen) { - send_frame_done_container_iterator( - workspace->current.ws_fullscreen->swayc, &data); -#ifdef HAVE_XWAYLAND - if (workspace->current.ws_fullscreen->type == SWAY_VIEW_XWAYLAND) { - send_frame_done_unmanaged(&data, - &root_container.sway_root->xwayland_unmanaged); + if (workspace->current.ws_fullscreen->type == C_VIEW) { + send_frame_done_container_iterator( + workspace->current.ws_fullscreen, &data); + } else { + send_frame_done_container(&data, workspace->current.ws_fullscreen); } +#ifdef HAVE_XWAYLAND + send_frame_done_unmanaged(&data, + &root_container.sway_root->xwayland_unmanaged); #endif } else { send_frame_done_layer(&data, diff --git a/sway/desktop/render.c b/sway/desktop/render.c index 15c5b94c..3e7b1a62 100644 --- a/sway/desktop/render.c +++ b/sway/desktop/render.c @@ -836,13 +836,13 @@ void output_render(struct sway_output *output, struct timespec *when, } struct sway_container *workspace = output_get_active_workspace(output); - struct sway_view *fullscreen_view = workspace->current.ws_fullscreen; + struct sway_container *fullscreen_con = workspace->current.ws_fullscreen; if (output_has_opaque_overlay_layer_surface(output)) { goto render_overlay; } - if (fullscreen_view) { + if (fullscreen_con) { float clear_color[] = {0.0f, 0.0f, 0.0f, 1.0f}; int nrects; @@ -853,16 +853,21 @@ void output_render(struct sway_output *output, struct timespec *when, } // TODO: handle views smaller than the output - if (fullscreen_view->swayc->instructions->length) { - render_saved_view(fullscreen_view, output, damage, 1.0f); + if (fullscreen_con->type == C_VIEW) { + if (fullscreen_con->instructions->length) { + render_saved_view(fullscreen_con->sway_view, + output, damage, 1.0f); } else { - render_view_surfaces(fullscreen_view, output, damage, 1.0f); + render_view_surfaces(fullscreen_con->sway_view, + output, damage, 1.0f); + } + } else { + render_container(output, damage, fullscreen_con, + fullscreen_con->current.focused); } #ifdef HAVE_XWAYLAND - if (fullscreen_view->type == SWAY_VIEW_XWAYLAND) { - render_unmanaged(output, damage, - &root_container.sway_root->xwayland_unmanaged); - } + render_unmanaged(output, damage, + &root_container.sway_root->xwayland_unmanaged); #endif } else { float clear_color[] = {0.25f, 0.25f, 0.25f, 1.0f}; diff --git a/sway/desktop/transaction.c b/sway/desktop/transaction.c index 2a89880a..ee7a0704 100644 --- a/sway/desktop/transaction.c +++ b/sway/desktop/transaction.c @@ -110,6 +110,7 @@ static void copy_pending_state(struct sway_container *container, state->swayc_y = container->y; state->swayc_width = container->width; state->swayc_height = container->height; + state->is_fullscreen = container->is_fullscreen; state->has_gaps = container->has_gaps; state->current_gaps = container->current_gaps; state->gaps_inner = container->gaps_inner; @@ -122,7 +123,6 @@ static void copy_pending_state(struct sway_container *container, state->view_y = view->y; state->view_width = view->width; state->view_height = view->height; - state->is_fullscreen = view->is_fullscreen; state->border = view->border; state->border_thickness = view->border_thickness; state->border_top = view->border_top; diff --git a/sway/desktop/xdg_shell.c b/sway/desktop/xdg_shell.c index f3e4fef8..e6e1527e 100644 --- a/sway/desktop/xdg_shell.c +++ b/sway/desktop/xdg_shell.c @@ -267,7 +267,7 @@ static void handle_request_fullscreen(struct wl_listener *listener, void *data) return; } - view_set_fullscreen(view, e->fullscreen); + container_set_fullscreen(view->swayc, e->fullscreen); struct sway_container *output = container_parent(view->swayc, C_OUTPUT); arrange_windows(output); @@ -338,7 +338,7 @@ static void handle_map(struct wl_listener *listener, void *data) { view_map(view, view->wlr_xdg_surface->surface); if (xdg_surface->toplevel->client_pending.fullscreen) { - view_set_fullscreen(view, true); + container_set_fullscreen(view->swayc, true); struct sway_container *ws = container_parent(view->swayc, C_WORKSPACE); arrange_windows(ws); } else { diff --git a/sway/desktop/xdg_shell_v6.c b/sway/desktop/xdg_shell_v6.c index 46fd4769..5feee3e4 100644 --- a/sway/desktop/xdg_shell_v6.c +++ b/sway/desktop/xdg_shell_v6.c @@ -262,7 +262,7 @@ static void handle_request_fullscreen(struct wl_listener *listener, void *data) return; } - view_set_fullscreen(view, e->fullscreen); + container_set_fullscreen(view->swayc, e->fullscreen); struct sway_container *output = container_parent(view->swayc, C_OUTPUT); arrange_windows(output); @@ -333,7 +333,7 @@ static void handle_map(struct wl_listener *listener, void *data) { view_map(view, view->wlr_xdg_surface_v6->surface); if (xdg_surface->toplevel->client_pending.fullscreen) { - view_set_fullscreen(view, true); + container_set_fullscreen(view->swayc, true); struct sway_container *ws = container_parent(view->swayc, C_WORKSPACE); arrange_windows(ws); } else { diff --git a/sway/desktop/xwayland.c b/sway/desktop/xwayland.c index 65d4fcd4..390ca580 100644 --- a/sway/desktop/xwayland.c +++ b/sway/desktop/xwayland.c @@ -357,7 +357,7 @@ static void handle_map(struct wl_listener *listener, void *data) { view_map(view, xsurface->surface); if (xsurface->fullscreen) { - view_set_fullscreen(view, true); + container_set_fullscreen(view->swayc, true); struct sway_container *ws = container_parent(view->swayc, C_WORKSPACE); arrange_windows(ws); } else { @@ -395,7 +395,7 @@ static void handle_request_fullscreen(struct wl_listener *listener, void *data) if (!xsurface->mapped) { return; } - view_set_fullscreen(view, xsurface->fullscreen); + container_set_fullscreen(view->swayc, xsurface->fullscreen); struct sway_container *output = container_parent(view->swayc, C_OUTPUT); arrange_windows(output); diff --git a/sway/input/cursor.c b/sway/input/cursor.c index 02bd2239..cc0dbe99 100644 --- a/sway/input/cursor.c +++ b/sway/input/cursor.c @@ -99,14 +99,8 @@ static struct sway_container *container_at_coords( return ws; } if (ws->sway_workspace->fullscreen) { - struct wlr_surface *wlr_surface = ws->sway_workspace->fullscreen->surface; - if (wlr_surface_point_accepts_input(wlr_surface, ox, oy)) { - *sx = ox; - *sy = oy; - *surface = wlr_surface; - return ws->sway_workspace->fullscreen->swayc; - } - return NULL; + return container_at(ws->sway_workspace->fullscreen, lx, ly, + surface, sx, sy); } if ((*surface = layer_surface_at(output, &output->layers[ZWLR_LAYER_SHELL_V1_LAYER_TOP], @@ -438,8 +432,8 @@ static void dispatch_cursor_button_floating(struct sway_cursor *cursor, struct sway_container *cont) { struct sway_seat *seat = cursor->seat; - // Deny moving or resizing a fullscreen view - if (cont->type == C_VIEW && cont->sway_view->is_fullscreen) { + // Deny moving or resizing a fullscreen container + if (container_is_fullscreen_or_child(cont)) { seat_pointer_notify_button(seat, time_msec, button, state); return; } diff --git a/sway/input/seat.c b/sway/input/seat.c index 8698d69e..e7b6e0c5 100644 --- a/sway/input/seat.c +++ b/sway/input/seat.c @@ -632,8 +632,7 @@ void seat_set_focus_warp(struct sway_seat *seat, if (last_workspace && last_workspace == new_workspace && last_workspace->sway_workspace->fullscreen - && container && container->type == C_VIEW - && !container->sway_view->is_fullscreen) { + && container && !container_is_fullscreen_or_child(container)) { return; } diff --git a/sway/scratchpad.c b/sway/scratchpad.c index 1e836e7d..64636c77 100644 --- a/sway/scratchpad.c +++ b/sway/scratchpad.c @@ -54,7 +54,7 @@ static void scratchpad_show(struct sway_container *con) { // If the current con or any of its parents are in fullscreen mode, we // first need to disable it before showing the scratchpad con. if (ws->sway_workspace->fullscreen) { - view_set_fullscreen(ws->sway_workspace->fullscreen, false); + container_set_fullscreen(ws->sway_workspace->fullscreen, false); } // Show the container diff --git a/sway/tree/arrange.c b/sway/tree/arrange.c index 533cf71c..5452b13c 100644 --- a/sway/tree/arrange.c +++ b/sway/tree/arrange.c @@ -220,8 +220,22 @@ static void arrange_workspace(struct sway_container *workspace) { container_set_dirty(workspace); wlr_log(WLR_DEBUG, "Arranging workspace '%s' at %f, %f", workspace->name, workspace->x, workspace->y); - arrange_floating(workspace->sway_workspace->floating); - arrange_children_of(workspace); + if (workspace->sway_workspace->fullscreen) { + struct sway_container *fs = workspace->sway_workspace->fullscreen; + fs->x = workspace->parent->x; + fs->y = workspace->parent->y; + fs->width = workspace->parent->width; + fs->height = workspace->parent->height; + if (fs->type == C_VIEW) { + view_autoconfigure(fs->sway_view); + } else { + arrange_children_of(fs); + } + container_set_dirty(fs); + } else { + arrange_floating(workspace->sway_workspace->floating); + arrange_children_of(workspace); + } } static void arrange_output(struct sway_container *output) { diff --git a/sway/tree/container.c b/sway/tree/container.c index 237e1a35..6ebf2653 100644 --- a/sway/tree/container.c +++ b/sway/tree/container.c @@ -1130,3 +1130,90 @@ void container_end_mouse_operation(struct sway_container *container) { } } } + +static void set_fullscreen_iterator(struct sway_container *con, void *data) { + if (con->type != C_VIEW) { + return; + } + if (con->sway_view->impl->set_fullscreen) { + bool *enable = data; + con->sway_view->impl->set_fullscreen(con->sway_view, *enable); + } +} + +void container_set_fullscreen(struct sway_container *container, bool enable) { + if (container->is_fullscreen == enable) { + return; + } + + struct sway_container *workspace = container_parent(container, C_WORKSPACE); + if (enable && workspace->sway_workspace->fullscreen) { + container_set_fullscreen(workspace->sway_workspace->fullscreen, false); + } + + container_for_each_descendant_dfs(container, + set_fullscreen_iterator, &enable); + + container->is_fullscreen = enable; + + if (enable) { + workspace->sway_workspace->fullscreen = container; + container->saved_x = container->x; + container->saved_y = container->y; + container->saved_width = container->width; + container->saved_height = container->height; + + struct sway_seat *seat; + struct sway_container *focus, *focus_ws; + wl_list_for_each(seat, &input_manager->seats, link) { + focus = seat_get_focus(seat); + if (focus) { + focus_ws = focus; + if (focus_ws->type != C_WORKSPACE) { + focus_ws = container_parent(focus_ws, C_WORKSPACE); + } + if (focus_ws == workspace) { + seat_set_focus(seat, container); + } + } + } + } else { + workspace->sway_workspace->fullscreen = NULL; + if (container_is_floating(container)) { + container->x = container->saved_x; + container->y = container->saved_y; + container->width = container->saved_width; + container->height = container->saved_height; + } else { + container->width = container->saved_width; + container->height = container->saved_height; + } + } + + container_end_mouse_operation(container); + + ipc_event_window(container, "fullscreen_mode"); +} + +bool container_is_fullscreen_or_child(struct sway_container *container) { + do { + if (container->is_fullscreen) { + return true; + } + container = container->parent; + } while (container && container->type != C_WORKSPACE); + + return false; +} + +struct sway_container *container_wrap_children(struct sway_container *parent) { + struct sway_container *middle = container_create(C_CONTAINER); + middle->layout = parent->layout; + while (parent->children->length) { + struct sway_container *child = parent->children->items[0]; + container_remove_child(child); + container_add_child(middle, child); + } + container_add_child(parent, middle); + return middle; +} diff --git a/sway/tree/layout.c b/sway/tree/layout.c index 2b3263f8..ab5acc16 100644 --- a/sway/tree/layout.c +++ b/sway/tree/layout.c @@ -66,10 +66,9 @@ static int index_child(const struct sway_container *child) { static void container_handle_fullscreen_reparent(struct sway_container *con, struct sway_container *old_parent) { - if (con->type != C_VIEW || !con->sway_view->is_fullscreen) { + if (!con->is_fullscreen) { return; } - struct sway_view *view = con->sway_view; struct sway_container *old_workspace = old_parent; if (old_workspace && old_workspace->type != C_WORKSPACE) { old_workspace = container_parent(old_workspace, C_WORKSPACE); @@ -85,19 +84,27 @@ static void container_handle_fullscreen_reparent(struct sway_container *con, // Mark the new workspace as fullscreen if (new_workspace->sway_workspace->fullscreen) { - view_set_fullscreen(new_workspace->sway_workspace->fullscreen, false); + container_set_fullscreen( + new_workspace->sway_workspace->fullscreen, false); } - new_workspace->sway_workspace->fullscreen = view; - // Resize view to new output dimensions + new_workspace->sway_workspace->fullscreen = con; + + // Resize container to new output dimensions struct sway_container *output = new_workspace->parent; - view->x = output->x; - view->y = output->y; - view->width = output->width; - view->height = output->height; con->x = output->x; con->y = output->y; con->width = output->width; con->height = output->height; + + if (con->type == C_VIEW) { + struct sway_view *view = con->sway_view; + view->x = output->x; + view->y = output->y; + view->width = output->width; + view->height = output->height; + } else { + arrange_windows(new_workspace); + } } void container_insert_child(struct sway_container *parent, @@ -146,7 +153,7 @@ void container_add_child(struct sway_container *parent, } struct sway_container *container_remove_child(struct sway_container *child) { - if (child->type == C_VIEW && child->sway_view->is_fullscreen) { + if (child->is_fullscreen) { struct sway_container *workspace = container_parent(child, C_WORKSPACE); workspace->sway_workspace->fullscreen = NULL; } @@ -229,10 +236,10 @@ void container_move_to(struct sway_container *container, if (focus_ws->type != C_WORKSPACE) { focus_ws = container_parent(focus_ws, C_WORKSPACE); } - seat_set_focus(seat, - new_workspace->sway_workspace->fullscreen->swayc); - if (focus_ws != new_workspace) { - seat_set_focus(seat, focus); + if (focus_ws == new_workspace) { + struct sway_container *new_focus = seat_get_focus_inactive(seat, + new_workspace->sway_workspace->fullscreen); + seat_set_focus(seat, new_focus); } } } @@ -375,10 +382,16 @@ void container_move(struct sway_container *container, struct sway_container *sibling = NULL; struct sway_container *current = container; struct sway_container *parent = current->parent; + struct sway_container *top = &root_container; // If moving a fullscreen view, only consider outputs - if (container->type == C_VIEW && container->sway_view->is_fullscreen) { + if (container->is_fullscreen) { current = container_parent(container, C_OUTPUT); + } else if (container_is_fullscreen_or_child(container)) { + // If we've fullscreened a split container, only allow the child to move + // around within the fullscreen parent. + struct sway_container *ws = container_parent(container, C_WORKSPACE); + top = ws->sway_workspace->fullscreen; } struct sway_container *new_parent = container_flatten(parent); @@ -388,7 +401,7 @@ void container_move(struct sway_container *container, } while (!sibling) { - if (current->type == C_ROOT) { + if (current == top) { return; } @@ -452,8 +465,9 @@ void container_move(struct sway_container *container, if ((index == parent->children->length - 1 && offs > 0) || (index == 0 && offs < 0)) { if (current->parent == container->parent) { - if (parent->layout == L_TABBED - || parent->layout == L_STACKED) { + if (!parent->is_fullscreen && + (parent->layout == L_TABBED || + parent->layout == L_STACKED)) { move_out_of_tabs_stacks(container, current, move_dir, offs); return; @@ -474,8 +488,8 @@ void container_move(struct sway_container *container, sibling = parent->children->items[index + offs]; wlr_log(WLR_DEBUG, "Selecting sibling id:%zd", sibling->id); } - } else if (parent->layout == L_TABBED - || parent->layout == L_STACKED) { + } else if (!parent->is_fullscreen && (parent->layout == L_TABBED || + parent->layout == L_STACKED)) { move_out_of_tabs_stacks(container, current, move_dir, offs); return; } else { @@ -707,16 +721,16 @@ struct sway_container *container_get_in_direction( return NULL; } - if (container->type == C_VIEW && container->sway_view->is_fullscreen) { - if (dir == MOVE_PARENT || dir == MOVE_CHILD) { + if (dir == MOVE_CHILD) { + return seat_get_focus_inactive(seat, container); + } + if (container->is_fullscreen) { + if (dir == MOVE_PARENT) { return NULL; } container = container_parent(container, C_OUTPUT); parent = container->parent; } else { - if (dir == MOVE_CHILD) { - return seat_get_focus_inactive(seat, container); - } if (dir == MOVE_PARENT) { if (parent->type == C_OUTPUT) { return NULL; @@ -767,7 +781,8 @@ struct sway_container *container_get_in_direction( } sway_assert(next_workspace, "Next container has no workspace"); if (next_workspace->sway_workspace->fullscreen) { - return next_workspace->sway_workspace->fullscreen->swayc; + return seat_get_focus_inactive(seat, + next_workspace->sway_workspace->fullscreen); } if (next->children && next->children->length) { // TODO consider floating children as well @@ -1014,13 +1029,13 @@ void container_swap(struct sway_container *con1, struct sway_container *con2) { wlr_log(WLR_DEBUG, "Swapping containers %zu and %zu", con1->id, con2->id); - int fs1 = con1->type == C_VIEW && con1->sway_view->is_fullscreen; - int fs2 = con2->type == C_VIEW && con2->sway_view->is_fullscreen; + int fs1 = con1->is_fullscreen; + int fs2 = con2->is_fullscreen; if (fs1) { - view_set_fullscreen(con1->sway_view, false); + container_set_fullscreen(con1, false); } if (fs2) { - view_set_fullscreen(con2->sway_view, false); + container_set_fullscreen(con2, false); } struct sway_seat *seat = input_manager_get_default_seat(input_manager); @@ -1053,10 +1068,10 @@ void container_swap(struct sway_container *con1, struct sway_container *con2) { prev_workspace_name = stored_prev_name; } - if (fs1 && con2->type == C_VIEW) { - view_set_fullscreen(con2->sway_view, true); + if (fs1) { + container_set_fullscreen(con2, true); } - if (fs2 && con1->type == C_VIEW) { - view_set_fullscreen(con1->sway_view, true); + if (fs2) { + container_set_fullscreen(con1, true); } } diff --git a/sway/tree/view.c b/sway/tree/view.c index beeb8144..82c3ad4a 100644 --- a/sway/tree/view.c +++ b/sway/tree/view.c @@ -225,7 +225,7 @@ void view_autoconfigure(struct sway_view *view) { struct sway_container *output = container_parent(view->swayc, C_OUTPUT); - if (view->is_fullscreen) { + if (view->swayc->is_fullscreen) { view->x = output->x; view->y = output->y; view->width = output->width; @@ -233,10 +233,6 @@ void view_autoconfigure(struct sway_view *view) { return; } - if (container_is_floating(view->swayc)) { - return; - } - struct sway_container *ws = container_parent(view->swayc, C_WORKSPACE); int other_views = 0; @@ -349,68 +345,6 @@ void view_set_tiled(struct sway_view *view, bool tiled) { } } -void view_set_fullscreen(struct sway_view *view, bool fullscreen) { - if (view->is_fullscreen == fullscreen) { - return; - } - - struct sway_container *workspace = - container_parent(view->swayc, C_WORKSPACE); - - if (view->impl->set_fullscreen) { - view->impl->set_fullscreen(view, fullscreen); - } - - view->is_fullscreen = fullscreen; - - if (fullscreen) { - if (workspace->sway_workspace->fullscreen) { - view_set_fullscreen(workspace->sway_workspace->fullscreen, false); - } - workspace->sway_workspace->fullscreen = view; - view->saved_x = view->x; - view->saved_y = view->y; - view->saved_width = view->width; - view->saved_height = view->height; - view->swayc->saved_x = view->swayc->x; - view->swayc->saved_y = view->swayc->y; - view->swayc->saved_width = view->swayc->width; - view->swayc->saved_height = view->swayc->height; - - struct sway_seat *seat; - struct sway_container *focus, *focus_ws; - wl_list_for_each(seat, &input_manager->seats, link) { - focus = seat_get_focus(seat); - if (focus) { - focus_ws = focus; - if (focus && focus_ws->type != C_WORKSPACE) { - focus_ws = container_parent(focus_ws, C_WORKSPACE); - } - seat_set_focus(seat, view->swayc); - if (focus_ws != workspace) { - seat_set_focus(seat, focus); - } - } - } - } else { - workspace->sway_workspace->fullscreen = NULL; - if (container_is_floating(view->swayc)) { - view->x = view->saved_x; - view->y = view->saved_y; - view->width = view->saved_width; - view->height = view->saved_height; - container_set_geometry_from_floating_view(view->swayc); - } else { - view->swayc->width = view->swayc->saved_width; - view->swayc->height = view->swayc->saved_height; - } - } - - container_end_mouse_operation(view->swayc); - - ipc_event_window(view->swayc, "fullscreen_mode"); -} - void view_close(struct sway_view *view) { if (view->impl->close) { view->impl->close(view); @@ -680,7 +614,7 @@ void view_unmap(struct sway_view *view) { struct sway_container *ws = container_parent(view->swayc, C_WORKSPACE); struct sway_container *parent; - if (view->is_fullscreen) { + if (view->swayc->is_fullscreen) { ws->sway_workspace->fullscreen = NULL; parent = container_destroy(view->swayc); @@ -1133,7 +1067,8 @@ bool view_is_visible(struct sway_view *view) { container = container->parent; } // Check view isn't hidden by another fullscreen view - if (workspace->sway_workspace->fullscreen && !view->is_fullscreen) { + if (workspace->sway_workspace->fullscreen && + !container_is_fullscreen_or_child(view->swayc)) { return false; } // Check the workspace is visible From 868dcde14ef2116fcbd5c2c66badf2bcc514bba3 Mon Sep 17 00:00:00 2001 From: Ryan Dwyer Date: Thu, 26 Jul 2018 08:28:05 +1000 Subject: [PATCH 053/148] Fix indent --- sway/desktop/render.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sway/desktop/render.c b/sway/desktop/render.c index 3e7b1a62..d2f1c9f2 100644 --- a/sway/desktop/render.c +++ b/sway/desktop/render.c @@ -857,7 +857,7 @@ void output_render(struct sway_output *output, struct timespec *when, if (fullscreen_con->instructions->length) { render_saved_view(fullscreen_con->sway_view, output, damage, 1.0f); - } else { + } else { render_view_surfaces(fullscreen_con->sway_view, output, damage, 1.0f); } From a90f2dbde9866a34e91b242bb06071e471d32eb3 Mon Sep 17 00:00:00 2001 From: Ryan Dwyer Date: Wed, 25 Jul 2018 23:34:00 +1000 Subject: [PATCH 054/148] Refactor view_map * Move workspace selection into separate function * Instead of keeping a `prev_focus` variable, do the check in `should_focus` (ie. views can only take focus if they're mapped into the active workspace) * Fix assign-to-output - it previously set `prev_focus` but should be `target_sibling` * Remove call to `workspace_switch` as we'll only ever focus the view if it's in the active workspace --- sway/tree/view.c | 142 +++++++++++++++++++++++++---------------------- 1 file changed, 77 insertions(+), 65 deletions(-) diff --git a/sway/tree/view.c b/sway/tree/view.c index 82c3ad4a..fec628e1 100644 --- a/sway/tree/view.c +++ b/sway/tree/view.c @@ -476,12 +476,81 @@ void view_execute_criteria(struct sway_view *view) { seat_set_focus(seat, prior_focus); } +static struct sway_container *select_workspace(struct sway_view *view) { + struct sway_seat *seat = input_manager_current_seat(input_manager); + + // Check if there's any `assign` criteria for the view + list_t *criterias = criteria_for_view(view, + CT_ASSIGN_WORKSPACE | CT_ASSIGN_OUTPUT); + struct sway_container *ws = NULL; + for (int i = 0; i < criterias->length; ++i) { + struct criteria *criteria = criterias->items[i]; + if (criteria->type == CT_ASSIGN_WORKSPACE) { + struct sway_container *ws = workspace_by_name(criteria->target); + if (!ws) { + ws = workspace_create(NULL, criteria->target); + } + break; + } else { + // CT_ASSIGN_OUTPUT + struct sway_container *output = output_by_name(criteria->target); + if (output) { + ws = seat_get_active_child(seat, output); + break; + } + } + } + list_free(criterias); + if (ws) { + return ws; + } + + // Check if there's a PID mapping + pid_t pid; +#ifdef HAVE_XWAYLAND + if (view->type == SWAY_VIEW_XWAYLAND) { + struct wlr_xwayland_surface *surf = + wlr_xwayland_surface_from_wlr_surface(view->surface); + pid = surf->pid; + } else { + struct wl_client *client = + wl_resource_get_client(view->surface->resource); + wl_client_get_credentials(client, &pid, NULL, NULL); + } +#else + struct wl_client *client = + wl_resource_get_client(view->surface->resource); + wl_client_get_credentials(client, &pid, NULL, NULL); +#endif + ws = workspace_for_pid(pid); + if (ws) { + return ws; + } + + // Use the focused workspace + ws = seat_get_focus(seat); + if (ws->type != C_WORKSPACE) { + ws = container_parent(ws, C_WORKSPACE); + } + return ws; +} + static bool should_focus(struct sway_view *view) { + struct sway_seat *seat = input_manager_current_seat(input_manager); + struct sway_container *prev_focus = seat_get_focus(seat); + struct sway_container *prev_ws = prev_focus->type == C_WORKSPACE ? + prev_focus : container_parent(prev_focus, C_WORKSPACE); + struct sway_container *map_ws = container_parent(view->swayc, C_WORKSPACE); + + // Views can only take focus if they are mapped into the active workspace + if (prev_ws != map_ws) { + return false; + } + // If the view is the only one in the focused workspace, it'll get focus // regardless of any no_focus criteria. struct sway_container *parent = view->swayc->parent; - struct sway_seat *seat = input_manager_current_seat(input_manager); - if (parent->type == C_WORKSPACE && seat_get_focus(seat) == parent) { + if (parent->type == C_WORKSPACE && prev_focus == parent) { size_t num_children = parent->children->length + parent->sway_workspace->floating->children->length; if (num_children == 1) { @@ -500,73 +569,19 @@ void view_map(struct sway_view *view, struct wlr_surface *wlr_surface) { if (!sway_assert(view->surface == NULL, "cannot map mapped view")) { return; } - - pid_t pid; -#ifdef HAVE_XWAYLAND - if (view->type == SWAY_VIEW_XWAYLAND) { - struct wlr_xwayland_surface *surf = - wlr_xwayland_surface_from_wlr_surface(wlr_surface); - pid = surf->pid; - } else { - struct wl_client *client = - wl_resource_get_client(wlr_surface->resource); - wl_client_get_credentials(client, &pid, NULL, NULL); - } -#else - struct wl_client *client = - wl_resource_get_client(wlr_surface->resource); - wl_client_get_credentials(client, &pid, NULL, NULL); -#endif + view->surface = wlr_surface; struct sway_seat *seat = input_manager_current_seat(input_manager); - struct sway_container *target_sibling = - seat_get_focus_inactive(seat, &root_container); - struct sway_container *prev_focus = target_sibling; - struct sway_container *cont = NULL; + struct sway_container *ws = select_workspace(view); + struct sway_container *target_sibling = seat_get_focus_inactive(seat, ws); - // Check if there's any `assign` criteria for the view - list_t *criterias = criteria_for_view(view, - CT_ASSIGN_WORKSPACE | CT_ASSIGN_OUTPUT); - struct sway_container *workspace = NULL; - if (criterias->length) { - struct criteria *criteria = criterias->items[0]; - if (criteria->type == CT_ASSIGN_WORKSPACE) { - workspace = workspace_by_name(criteria->target); - if (!workspace) { - workspace = workspace_create(NULL, criteria->target); - } - prev_focus = target_sibling; - target_sibling = seat_get_focus_inactive(seat, workspace); - } else { - // CT_ASSIGN_OUTPUT - struct sway_container *output = output_by_name(criteria->target); - if (output) { - prev_focus = seat_get_focus_inactive(seat, output); - } - } - } - list_free(criterias); - - if (!workspace) { - workspace = workspace_for_pid(pid); - if (workspace) { - prev_focus = target_sibling; - target_sibling = seat_get_focus_inactive(seat, workspace); - } - } // If we're about to launch the view into the floating container, then // launch it as a tiled view in the root of the workspace instead. if (container_is_floating(target_sibling)) { - if (prev_focus == target_sibling) { - prev_focus = target_sibling->parent->parent; - } target_sibling = target_sibling->parent->parent; } - cont = container_view_create(target_sibling, view); - - view->surface = wlr_surface; - view->swayc = cont; + view->swayc = container_view_create(target_sibling, view); view_init_subsurfaces(view, wlr_surface); wl_signal_add(&wlr_surface->events.new_subsurface, @@ -586,11 +601,8 @@ void view_map(struct sway_view *view, struct wlr_surface *wlr_surface) { view_set_tiled(view, true); } - if (should_focus(view) && prev_focus == target_sibling) { - input_manager_set_focus(input_manager, cont); - if (workspace) { - workspace_switch(workspace); - } + if (should_focus(view)) { + input_manager_set_focus(input_manager, view->swayc); } view_update_title(view, false); From 6ceb1d33fd9038640f6adf1aabf48b6931dc0fdb Mon Sep 17 00:00:00 2001 From: Ryan Dwyer Date: Fri, 27 Jul 2018 09:21:55 +1000 Subject: [PATCH 055/148] Fix assign to workspace --- sway/tree/view.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sway/tree/view.c b/sway/tree/view.c index fec628e1..e641544e 100644 --- a/sway/tree/view.c +++ b/sway/tree/view.c @@ -486,7 +486,7 @@ static struct sway_container *select_workspace(struct sway_view *view) { for (int i = 0; i < criterias->length; ++i) { struct criteria *criteria = criterias->items[i]; if (criteria->type == CT_ASSIGN_WORKSPACE) { - struct sway_container *ws = workspace_by_name(criteria->target); + ws = workspace_by_name(criteria->target); if (!ws) { ws = workspace_create(NULL, criteria->target); } From d37c94d1b666ba92562efbcd4bda2cb0c3ca4916 Mon Sep 17 00:00:00 2001 From: Mihai Coman Date: Sat, 28 Jul 2018 00:18:52 +0000 Subject: [PATCH 056/148] Preserve workspace name --- sway/tree/workspace.c | 3 --- 1 file changed, 3 deletions(-) diff --git a/sway/tree/workspace.c b/sway/tree/workspace.c index 62974cd7..588e2aae 100644 --- a/sway/tree/workspace.c +++ b/sway/tree/workspace.c @@ -124,9 +124,6 @@ static void workspace_name_from_binding(const struct sway_binding * binding, char *_target = strdup(name); _target = do_var_replacement(_target); strip_quotes(_target); - while (isspace(*_target)) { - memmove(_target, _target+1, strlen(_target+1)); - } wlr_log(WLR_DEBUG, "Got valid workspace command for target: '%s'", _target); From 073ac425d5bf6f6393eb91d9b5f84e3caa68f511 Mon Sep 17 00:00:00 2001 From: Ryan Dwyer Date: Sat, 28 Jul 2018 15:19:14 +1000 Subject: [PATCH 057/148] Fix use after free in transactions In set_instructions_ready, calling set_instruction_ready may cause any number of transactions to get applied, which removes them from the list being iterated. The iteration variables need to be adjusted accordingly. --- sway/desktop/transaction.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/sway/desktop/transaction.c b/sway/desktop/transaction.c index ee7a0704..0a24c4fc 100644 --- a/sway/desktop/transaction.c +++ b/sway/desktop/transaction.c @@ -364,7 +364,13 @@ static void set_instructions_ready(struct sway_view *view, int index) { struct sway_transaction_instruction *instruction = view->swayc->instructions->items[i]; if (!instruction->ready) { + // set_instruction_ready can remove instructions from the list we're + // iterating + size_t length = view->swayc->instructions->length; set_instruction_ready(instruction); + size_t num_removed = length - view->swayc->instructions->length; + i -= num_removed; + index -= num_removed; } } } From 52cf410d3cfcf0cae81b47c90097867c4e4d8564 Mon Sep 17 00:00:00 2001 From: Ryan Dwyer Date: Sat, 28 Jul 2018 17:43:18 +1000 Subject: [PATCH 058/148] Second attempt at fixing transaction use-after-free The solution used in 073ac425d5bf6f6393eb91d9b5f84e3caa68f511 doesn't work in all cases because the freed instruction might be ahead in the list, not necessarily behind. The new solution delays running the queue until after the loop has finished iterating, thus avoiding the problem completely. --- sway/desktop/transaction.c | 9 +-------- 1 file changed, 1 insertion(+), 8 deletions(-) diff --git a/sway/desktop/transaction.c b/sway/desktop/transaction.c index 0a24c4fc..ccda1963 100644 --- a/sway/desktop/transaction.c +++ b/sway/desktop/transaction.c @@ -344,13 +344,11 @@ static void set_instruction_ready( } - // If all views are ready, apply the transaction. // If the transaction has timed out then its num_waiting will be 0 already. if (transaction->num_waiting > 0 && --transaction->num_waiting == 0) { if (!txn_debug) { wlr_log(WLR_DEBUG, "Transaction %p is ready", transaction); wl_event_source_timer_update(transaction->timer, 0); - transaction_progress_queue(); } } } @@ -364,15 +362,10 @@ static void set_instructions_ready(struct sway_view *view, int index) { struct sway_transaction_instruction *instruction = view->swayc->instructions->items[i]; if (!instruction->ready) { - // set_instruction_ready can remove instructions from the list we're - // iterating - size_t length = view->swayc->instructions->length; set_instruction_ready(instruction); - size_t num_removed = length - view->swayc->instructions->length; - i -= num_removed; - index -= num_removed; } } + transaction_progress_queue(); } void transaction_notify_view_ready(struct sway_view *view, uint32_t serial) { From 32663b7b013e9c0fd37c1c86d6c26bc3156e1c3a Mon Sep 17 00:00:00 2001 From: Ryan Dwyer Date: Sat, 28 Jul 2018 09:22:37 +1000 Subject: [PATCH 059/148] Handle out-of-fd situations gracefully for transaction and urgent timers --- sway/desktop/transaction.c | 9 ++++++++- sway/input/seat.c | 10 ++++++++-- 2 files changed, 16 insertions(+), 3 deletions(-) diff --git a/sway/desktop/transaction.c b/sway/desktop/transaction.c index ccda1963..a9c9cb58 100644 --- a/sway/desktop/transaction.c +++ b/sway/desktop/transaction.c @@ -316,7 +316,14 @@ static void transaction_commit(struct sway_transaction *transaction) { // Set up a timer which the views must respond within transaction->timer = wl_event_loop_add_timer(server.wl_event_loop, handle_timeout, transaction); - wl_event_source_timer_update(transaction->timer, txn_timeout_ms); + if (transaction->timer) { + wl_event_source_timer_update(transaction->timer, txn_timeout_ms); + } else { + wlr_log(WLR_ERROR, "Unable to create transaction timer. " + "There might not be any available file descriptors. " + "Some imperfect frames might be rendered."); + handle_timeout(transaction); + } } // The debug tree shows the pending/live tree. Here is a good place to diff --git a/sway/input/seat.c b/sway/input/seat.c index e7b6e0c5..ab07c03c 100644 --- a/sway/input/seat.c +++ b/sway/input/seat.c @@ -696,8 +696,14 @@ void seat_set_focus_warp(struct sway_seat *seat, config->urgent_timeout > 0) { view->urgent_timer = wl_event_loop_add_timer(server.wl_event_loop, handle_urgent_timeout, view); - wl_event_source_timer_update(view->urgent_timer, - config->urgent_timeout); + if (view->urgent_timer) { + wl_event_source_timer_update(view->urgent_timer, + config->urgent_timeout); + } else { + wlr_log(WLR_ERROR, "Unable to create urgency timer. " + "There might not be any available file descriptors."); + handle_urgent_timeout(view); + } } else { view_set_urgent(view, false); } From d6daf10cad540f7581e9a325a041333d1113cae5 Mon Sep 17 00:00:00 2001 From: Ryan Dwyer Date: Sat, 28 Jul 2018 10:00:04 +1000 Subject: [PATCH 060/148] Show errno description in log --- sway/desktop/transaction.c | 6 +++--- sway/input/seat.c | 4 ++-- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/sway/desktop/transaction.c b/sway/desktop/transaction.c index a9c9cb58..17e3f467 100644 --- a/sway/desktop/transaction.c +++ b/sway/desktop/transaction.c @@ -319,9 +319,9 @@ static void transaction_commit(struct sway_transaction *transaction) { if (transaction->timer) { wl_event_source_timer_update(transaction->timer, txn_timeout_ms); } else { - wlr_log(WLR_ERROR, "Unable to create transaction timer. " - "There might not be any available file descriptors. " - "Some imperfect frames might be rendered."); + wlr_log(WLR_ERROR, "Unable to create transaction timer (%s). " + "Some imperfect frames might be rendered.", + strerror(errno)); handle_timeout(transaction); } } diff --git a/sway/input/seat.c b/sway/input/seat.c index ab07c03c..ce2e0936 100644 --- a/sway/input/seat.c +++ b/sway/input/seat.c @@ -700,8 +700,8 @@ void seat_set_focus_warp(struct sway_seat *seat, wl_event_source_timer_update(view->urgent_timer, config->urgent_timeout); } else { - wlr_log(WLR_ERROR, "Unable to create urgency timer. " - "There might not be any available file descriptors."); + wlr_log(WLR_ERROR, "Unable to create urgency timer (%s)", + strerror(errno)); handle_urgent_timeout(view); } } else { From a4bcddcfdc67ef64edf3737342a99c4e41cae6d4 Mon Sep 17 00:00:00 2001 From: Ryan Dwyer Date: Sat, 28 Jul 2018 10:24:04 +1000 Subject: [PATCH 061/148] Include errno.h --- sway/desktop/transaction.c | 1 + sway/input/seat.c | 1 + 2 files changed, 2 insertions(+) diff --git a/sway/desktop/transaction.c b/sway/desktop/transaction.c index 17e3f467..7975366e 100644 --- a/sway/desktop/transaction.c +++ b/sway/desktop/transaction.c @@ -1,4 +1,5 @@ #define _POSIX_C_SOURCE 200809L +#include #include #include #include diff --git a/sway/input/seat.c b/sway/input/seat.c index ce2e0936..b783a84f 100644 --- a/sway/input/seat.c +++ b/sway/input/seat.c @@ -1,6 +1,7 @@ #define _XOPEN_SOURCE 700 #define _POSIX_C_SOURCE 199309L #include +#include #ifdef __linux__ #include #elif __FreeBSD__ From 08cfba2192f5770d975c5fe70789a81aaee4dc7e Mon Sep 17 00:00:00 2001 From: Ryan Dwyer Date: Thu, 26 Jul 2018 18:36:46 +1000 Subject: [PATCH 062/148] Allow containers to float Things worth noting: * When a fullscreen view unmaps, the check to unset fullscreen on the workspace has been moved out of view_unmap and into container_destroy, because containers can be fullscreen too * The calls to `container_reap_empty_recursive(workspace)` have been removed from `container_set_floating`. That function reaps upwards so it wouldn't do anything. I'm probably the one who originally added it... * My fix (b14bd1b0b1536039e4f46fe94515c7c44e7afc61) for the tabbed child crash has a side effect where when you close a floating container, focus is not given to the tiled container again. I've removed my fix and removed the call to `send_cursor_motion` from `seat_set_focus_warp`. We should consider calling it from somewhere earlier in the call stack. --- include/sway/tree/container.h | 11 +++++ include/sway/tree/view.h | 5 --- sway/commands/floating.c | 13 ++++-- sway/commands/move.c | 10 ++++- sway/desktop/render.c | 2 +- sway/input/cursor.c | 5 ++- sway/input/seat.c | 5 --- sway/scratchpad.c | 7 +-- sway/tree/container.c | 82 ++++++++++++++++++++++++++++++++--- sway/tree/layout.c | 19 +++++--- sway/tree/view.c | 53 +--------------------- 11 files changed, 126 insertions(+), 86 deletions(-) diff --git a/include/sway/tree/container.h b/include/sway/tree/container.h index c584cd92..d91b3bf1 100644 --- a/include/sway/tree/container.h +++ b/include/sway/tree/container.h @@ -297,6 +297,11 @@ void container_notify_subtree_changed(struct sway_container *container); */ size_t container_titlebar_height(void); +/** + * Resize and center the container in its workspace. + */ +void container_init_floating(struct sway_container *container); + void container_set_floating(struct sway_container *container, bool enable); void container_set_geometry_from_floating_view(struct sway_container *con); @@ -340,6 +345,12 @@ void container_end_mouse_operation(struct sway_container *container); void container_set_fullscreen(struct sway_container *container, bool enable); +/** + * Return true if the container is floating, or a child of a floating split + * container. + */ +bool container_is_floating_or_child(struct sway_container *container); + /** * Return true if the container is fullscreen, or a child of a fullscreen split * container. diff --git a/include/sway/tree/view.h b/include/sway/tree/view.h index 7086314f..0152ed55 100644 --- a/include/sway/tree/view.h +++ b/include/sway/tree/view.h @@ -234,11 +234,6 @@ void view_get_constraints(struct sway_view *view, double *min_width, uint32_t view_configure(struct sway_view *view, double lx, double ly, int width, int height); -/** - * Center the view in its workspace and build the swayc decorations around it. - */ -void view_init_floating(struct sway_view *view); - /** * Configure the view's position and size based on the swayc's position and * size, taking borders into consideration. diff --git a/sway/commands/floating.c b/sway/commands/floating.c index 6ab56c3b..b67e736f 100644 --- a/sway/commands/floating.c +++ b/sway/commands/floating.c @@ -17,9 +17,16 @@ struct cmd_results *cmd_floating(int argc, char **argv) { } struct sway_container *container = config->handler_context.current_container; - if (container->type != C_VIEW) { - // TODO: This doesn't strictly speaking have to be true - return cmd_results_new(CMD_INVALID, "float", "Only views can float"); + if (container->type == C_WORKSPACE && container->children->length == 0) { + return cmd_results_new(CMD_INVALID, "floating", + "Can't float an empty workspace"); + } + if (container->type == C_WORKSPACE) { + // Wrap the workspace's children in a container so we can float it + struct sway_container *workspace = container; + container = container_wrap_children(container); + workspace->layout = L_HORIZ; + seat_set_focus(config->handler_context.seat, container); } bool wants_floating; diff --git a/sway/commands/move.c b/sway/commands/move.c index aede3d6c..b127c89f 100644 --- a/sway/commands/move.c +++ b/sway/commands/move.c @@ -298,9 +298,15 @@ static struct cmd_results *move_to_position(struct sway_container *container, } static struct cmd_results *move_to_scratchpad(struct sway_container *con) { - if (con->type != C_CONTAINER && con->type != C_VIEW) { + if (con->type == C_WORKSPACE && con->children->length == 0) { return cmd_results_new(CMD_INVALID, "move", - "Only views and containers can be moved to the scratchpad"); + "Can't move an empty workspace to the scratchpad"); + } + if (con->type == C_WORKSPACE) { + // Wrap the workspace's children in a container + struct sway_container *workspace = con; + con = container_wrap_children(con); + workspace->layout = L_HORIZ; } if (con->scratchpad) { return cmd_results_new(CMD_INVALID, "move", diff --git a/sway/desktop/render.c b/sway/desktop/render.c index d2f1c9f2..c9fdfd95 100644 --- a/sway/desktop/render.c +++ b/sway/desktop/render.c @@ -779,7 +779,7 @@ static void render_floating_container(struct sway_output *soutput, } render_view(soutput, damage, con, colors); } else { - render_container(soutput, damage, con, false); + render_container(soutput, damage, con, con->current.focused); } } diff --git a/sway/input/cursor.c b/sway/input/cursor.c index cc0dbe99..d1347198 100644 --- a/sway/input/cursor.c +++ b/sway/input/cursor.c @@ -598,7 +598,10 @@ void dispatch_cursor_button(struct sway_cursor *cursor, seat_set_focus_layer(cursor->seat, layer); } seat_pointer_notify_button(cursor->seat, time_msec, button, state); - } else if (cont && container_is_floating(cont)) { + } else if (cont && container_is_floating_or_child(cont)) { + while (cont->parent->layout != L_FLOATING) { + cont = cont->parent; + } dispatch_cursor_button_floating(cursor, time_msec, button, state, surface, sx, sy, cont); } else if (surface && cont && cont->type != C_VIEW) { diff --git a/sway/input/seat.c b/sway/input/seat.c index e7b6e0c5..877a93c6 100644 --- a/sway/input/seat.c +++ b/sway/input/seat.c @@ -184,7 +184,6 @@ static void handle_seat_container_destroy(struct wl_listener *listener, bool set_focus = focus != NULL && (focus == con || container_has_child(con, focus)) && - con->parent && con->parent->children->length > 1 && con->type != C_WORKSPACE; seat_container_destroy(seat_con); @@ -754,10 +753,6 @@ void seat_set_focus_warp(struct sway_seat *seat, } } - if (last_focus != NULL) { - cursor_send_pointer_motion(seat->cursor, 0, true); - } - seat->has_focus = (container != NULL); update_debug_tree(); diff --git a/sway/scratchpad.c b/sway/scratchpad.c index 64636c77..181d9b3b 100644 --- a/sway/scratchpad.c +++ b/sway/scratchpad.c @@ -72,11 +72,7 @@ static void scratchpad_show(struct sway_container *con) { if (!wlr_box_contains_point(&workspace_box, center_lx, center_ly)) { // Maybe resize it if (con->width > ws->width || con->height > ws->height) { - // TODO: Do this properly once we can float C_CONTAINERs - if (con->type == C_VIEW) { - view_init_floating(con->sway_view); - arrange_windows(con); - } + container_init_floating(con); } // Center it @@ -85,6 +81,7 @@ static void scratchpad_show(struct sway_container *con) { container_floating_move_to(con, new_lx, new_ly); } + arrange_windows(ws); seat_set_focus(seat, con); container_set_dirty(con->parent); diff --git a/sway/tree/container.c b/sway/tree/container.c index 6ebf2653..566432b1 100644 --- a/sway/tree/container.c +++ b/sway/tree/container.c @@ -407,6 +407,10 @@ struct sway_container *container_flatten(struct sway_container *container) { * This function just wraps container_destroy_noreaping(), then does reaping. */ struct sway_container *container_destroy(struct sway_container *con) { + if (con->is_fullscreen) { + struct sway_container *ws = container_parent(con, C_WORKSPACE); + ws->sway_workspace->fullscreen = NULL; + } struct sway_container *parent = container_destroy_noreaping(con); if (!parent) { @@ -945,23 +949,81 @@ size_t container_titlebar_height() { return config->font_height + TITLEBAR_V_PADDING * 2; } +void container_init_floating(struct sway_container *con) { + if (!sway_assert(con->type == C_VIEW || con->type == C_CONTAINER, + "Expected a view or container")) { + return; + } + struct sway_container *ws = container_parent(con, C_WORKSPACE); + int min_width, min_height; + int max_width, max_height; + + if (config->floating_minimum_width == -1) { // no minimum + min_width = 0; + } else if (config->floating_minimum_width == 0) { // automatic + min_width = 75; + } else { + min_width = config->floating_minimum_width; + } + + if (config->floating_minimum_height == -1) { // no minimum + min_height = 0; + } else if (config->floating_minimum_height == 0) { // automatic + min_height = 50; + } else { + min_height = config->floating_minimum_height; + } + + if (config->floating_maximum_width == -1) { // no maximum + max_width = INT_MAX; + } else if (config->floating_maximum_width == 0) { // automatic + max_width = ws->width * 0.6666; + } else { + max_width = config->floating_maximum_width; + } + + if (config->floating_maximum_height == -1) { // no maximum + max_height = INT_MAX; + } else if (config->floating_maximum_height == 0) { // automatic + max_height = ws->height * 0.6666; + } else { + max_height = config->floating_maximum_height; + } + + if (con->type == C_CONTAINER) { + con->width = max_width; + con->height = max_height; + con->x = ws->x + (ws->width - con->width) / 2; + con->y = ws->y + (ws->height - con->height) / 2; + } else { + struct sway_view *view = con->sway_view; + view->width = fmax(min_width, fmin(view->natural_width, max_width)); + view->height = fmax(min_height, fmin(view->natural_height, max_height)); + view->x = ws->x + (ws->width - view->width) / 2; + view->y = ws->y + (ws->height - view->height) / 2; + + // If the view's border is B_NONE then these properties are ignored. + view->border_top = view->border_bottom = true; + view->border_left = view->border_right = true; + + container_set_geometry_from_floating_view(view->swayc); + } +} + void container_set_floating(struct sway_container *container, bool enable) { if (container_is_floating(container) == enable) { return; } struct sway_container *workspace = container_parent(container, C_WORKSPACE); - struct sway_seat *seat = input_manager_current_seat(input_manager); if (enable) { container_remove_child(container); container_add_child(workspace->sway_workspace->floating, container); + container_init_floating(container); if (container->type == C_VIEW) { - view_init_floating(container->sway_view); view_set_tiled(container->sway_view, false); } - seat_set_focus(seat, seat_get_focus_inactive(seat, container)); - container_reap_empty_recursive(workspace); } else { // Returning to tiled if (container->scratchpad) { @@ -975,7 +1037,6 @@ void container_set_floating(struct sway_container *container, bool enable) { view_set_tiled(container->sway_view, true); } container->is_sticky = false; - container_reap_empty_recursive(workspace->sway_workspace->floating); } container_end_mouse_operation(container); @@ -1195,6 +1256,17 @@ void container_set_fullscreen(struct sway_container *container, bool enable) { ipc_event_window(container, "fullscreen_mode"); } +bool container_is_floating_or_child(struct sway_container *container) { + do { + if (container->parent && container->parent->layout == L_FLOATING) { + return true; + } + container = container->parent; + } while (container && container->type != C_WORKSPACE); + + return false; +} + bool container_is_fullscreen_or_child(struct sway_container *container) { do { if (container->is_fullscreen) { diff --git a/sway/tree/layout.c b/sway/tree/layout.c index ab5acc16..a0764a54 100644 --- a/sway/tree/layout.c +++ b/sway/tree/layout.c @@ -387,9 +387,11 @@ void container_move(struct sway_container *container, // If moving a fullscreen view, only consider outputs if (container->is_fullscreen) { current = container_parent(container, C_OUTPUT); - } else if (container_is_fullscreen_or_child(container)) { + } else if (container_is_fullscreen_or_child(container) || + container_is_floating_or_child(container)) { // If we've fullscreened a split container, only allow the child to move // around within the fullscreen parent. + // Same with floating a split container. struct sway_container *ws = container_parent(container, C_WORKSPACE); top = ws->sway_workspace->fullscreen; } @@ -465,6 +467,9 @@ void container_move(struct sway_container *container, if ((index == parent->children->length - 1 && offs > 0) || (index == 0 && offs < 0)) { if (current->parent == container->parent) { + if (parent->parent->layout == L_FLOATING) { + return; + } if (!parent->is_fullscreen && (parent->layout == L_TABBED || parent->layout == L_STACKED)) { @@ -488,10 +493,14 @@ void container_move(struct sway_container *container, sibling = parent->children->items[index + offs]; wlr_log(WLR_DEBUG, "Selecting sibling id:%zd", sibling->id); } - } else if (!parent->is_fullscreen && (parent->layout == L_TABBED || + } else if (!parent->is_fullscreen && + parent->parent->layout != L_FLOATING && + (parent->layout == L_TABBED || parent->layout == L_STACKED)) { move_out_of_tabs_stacks(container, current, move_dir, offs); return; + } else if (parent->parent->layout == L_FLOATING) { + return; } else { wlr_log(WLR_DEBUG, "Moving up to find a parallel container"); current = current->parent; @@ -717,10 +726,6 @@ struct sway_container *container_get_in_direction( enum movement_direction dir) { struct sway_container *parent = container->parent; - if (container_is_floating(container)) { - return NULL; - } - if (dir == MOVE_CHILD) { return seat_get_focus_inactive(seat, container); } @@ -732,7 +737,7 @@ struct sway_container *container_get_in_direction( parent = container->parent; } else { if (dir == MOVE_PARENT) { - if (parent->type == C_OUTPUT) { + if (parent->type == C_OUTPUT || container_is_floating(container)) { return NULL; } else { return parent; diff --git a/sway/tree/view.c b/sway/tree/view.c index e641544e..253f3001 100644 --- a/sway/tree/view.c +++ b/sway/tree/view.c @@ -168,55 +168,6 @@ uint32_t view_configure(struct sway_view *view, double lx, double ly, int width, return 0; } -void view_init_floating(struct sway_view *view) { - struct sway_container *ws = container_parent(view->swayc, C_WORKSPACE); - int min_width, min_height; - int max_width, max_height; - - if (config->floating_minimum_width == -1) { // no minimum - min_width = 0; - } else if (config->floating_minimum_width == 0) { // automatic - min_width = 75; - } else { - min_width = config->floating_minimum_width; - } - - if (config->floating_minimum_height == -1) { // no minimum - min_height = 0; - } else if (config->floating_minimum_height == 0) { // automatic - min_height = 50; - } else { - min_height = config->floating_minimum_height; - } - - if (config->floating_maximum_width == -1) { // no maximum - max_width = INT_MAX; - } else if (config->floating_maximum_width == 0) { // automatic - max_width = ws->width * 0.6666; - } else { - max_width = config->floating_maximum_width; - } - - if (config->floating_maximum_height == -1) { // no maximum - max_height = INT_MAX; - } else if (config->floating_maximum_height == 0) { // automatic - max_height = ws->height * 0.6666; - } else { - max_height = config->floating_maximum_height; - } - - view->width = fmax(min_width, fmin(view->natural_width, max_width)); - view->height = fmax(min_height, fmin(view->natural_height, max_height)); - view->x = ws->x + (ws->width - view->width) / 2; - view->y = ws->y + (ws->height - view->height) / 2; - - // If the view's border is B_NONE then these properties are ignored. - view->border_top = view->border_bottom = true; - view->border_left = view->border_right = true; - - container_set_geometry_from_floating_view(view->swayc); -} - void view_autoconfigure(struct sway_view *view) { if (!sway_assert(view->swayc, "Called view_autoconfigure() on a view without a swayc")) { @@ -626,10 +577,8 @@ void view_unmap(struct sway_view *view) { struct sway_container *ws = container_parent(view->swayc, C_WORKSPACE); struct sway_container *parent; - if (view->swayc->is_fullscreen) { - ws->sway_workspace->fullscreen = NULL; + if (container_is_fullscreen_or_child(view->swayc)) { parent = container_destroy(view->swayc); - arrange_windows(ws->parent); } else { parent = container_destroy(view->swayc); From 0b6b6716e28b97213c8f4a3c9e65aeba6409987e Mon Sep 17 00:00:00 2001 From: Ryan Dwyer Date: Thu, 26 Jul 2018 19:10:53 +1000 Subject: [PATCH 063/148] Fix clicking a floating split container It would focus the split container rather than the child. This commit makes it track the child and the split container separately and send the surface click to the child. --- sway/input/cursor.c | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/sway/input/cursor.c b/sway/input/cursor.c index d1347198..96ac7b33 100644 --- a/sway/input/cursor.c +++ b/sway/input/cursor.c @@ -437,18 +437,22 @@ static void dispatch_cursor_button_floating(struct sway_cursor *cursor, seat_pointer_notify_button(seat, time_msec, button, state); return; } + struct sway_container *floater = cont; + while (floater->parent->layout != L_FLOATING) { + floater = floater->parent; + } struct wlr_keyboard *keyboard = wlr_seat_get_keyboard(seat->wlr_seat); bool mod_pressed = keyboard && (wlr_keyboard_get_modifiers(keyboard) & config->floating_mod); - enum wlr_edges edge = find_resize_edge(cont, cursor); + enum wlr_edges edge = find_resize_edge(floater, cursor); bool over_title = edge == WLR_EDGE_NONE && !surface; // Check for beginning move uint32_t btn_move = config->floating_mod_inverse ? BTN_RIGHT : BTN_LEFT; if (button == btn_move && state == WLR_BUTTON_PRESSED && (mod_pressed || over_title)) { - seat_begin_move(seat, cont, button); + seat_begin_move(seat, floater, button); return; } @@ -459,12 +463,12 @@ static void dispatch_cursor_button_floating(struct sway_cursor *cursor, if ((resizing_via_border || resizing_via_mod) && state == WLR_BUTTON_PRESSED) { if (edge == WLR_EDGE_NONE) { - edge |= cursor->cursor->x > cont->x + cont->width / 2 ? + edge |= cursor->cursor->x > floater->x + floater->width / 2 ? WLR_EDGE_RIGHT : WLR_EDGE_LEFT; - edge |= cursor->cursor->y > cont->y + cont->height / 2 ? + edge |= cursor->cursor->y > floater->y + floater->height / 2 ? WLR_EDGE_BOTTOM : WLR_EDGE_TOP; } - seat_begin_resize(seat, cont, button, edge); + seat_begin_resize(seat, floater, button, edge); return; } @@ -599,9 +603,6 @@ void dispatch_cursor_button(struct sway_cursor *cursor, } seat_pointer_notify_button(cursor->seat, time_msec, button, state); } else if (cont && container_is_floating_or_child(cont)) { - while (cont->parent->layout != L_FLOATING) { - cont = cont->parent; - } dispatch_cursor_button_floating(cursor, time_msec, button, state, surface, sx, sy, cont); } else if (surface && cont && cont->type != C_VIEW) { From 902a1402ba35be3fbc3c054cd7af5a8bda5f8fd6 Mon Sep 17 00:00:00 2001 From: Ryan Dwyer Date: Thu, 26 Jul 2018 22:27:42 +1000 Subject: [PATCH 064/148] Fix crash when a child of a floating container is in the scratchpad --- sway/tree/view.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/sway/tree/view.c b/sway/tree/view.c index 253f3001..8f54cc11 100644 --- a/sway/tree/view.c +++ b/sway/tree/view.c @@ -999,11 +999,14 @@ void view_update_marks_textures(struct sway_view *view) { } bool view_is_visible(struct sway_view *view) { - if (!view->swayc || view->swayc->destroying || !view->swayc->parent) { + if (!view->swayc || view->swayc->destroying) { return false; } struct sway_container *workspace = container_parent(view->swayc, C_WORKSPACE); + if (!workspace) { + return false; + } // Determine if view is nested inside a floating container which is sticky. // A simple floating view will have this ancestry: // C_VIEW -> floating -> workspace From 936168e740067a8658c7097ff76588b070faa71f Mon Sep 17 00:00:00 2001 From: Ryan Dwyer Date: Thu, 26 Jul 2018 22:42:58 +1000 Subject: [PATCH 065/148] Operate on floating split container when a child is focused --- sway/commands/floating.c | 8 ++++++++ sway/commands/scratchpad.c | 12 ++++++++++-- sway/scratchpad.c | 9 +++++++++ 3 files changed, 27 insertions(+), 2 deletions(-) diff --git a/sway/commands/floating.c b/sway/commands/floating.c index b67e736f..31de5ec3 100644 --- a/sway/commands/floating.c +++ b/sway/commands/floating.c @@ -29,6 +29,14 @@ struct cmd_results *cmd_floating(int argc, char **argv) { seat_set_focus(config->handler_context.seat, container); } + // If the container is in a floating split container, + // operate on the split container instead of the child. + if (container_is_floating_or_child(container)) { + while (container->parent->layout != L_FLOATING) { + container = container->parent; + } + } + bool wants_floating; if (strcasecmp(argv[0], "enable") == 0) { wants_floating = true; diff --git a/sway/commands/scratchpad.c b/sway/commands/scratchpad.c index ccc07c87..01a91d65 100644 --- a/sway/commands/scratchpad.c +++ b/sway/commands/scratchpad.c @@ -19,11 +19,19 @@ struct cmd_results *cmd_scratchpad(int argc, char **argv) { } if (config->handler_context.using_criteria) { + struct sway_container *con = config->handler_context.current_container; + + // If the container is in a floating split container, + // operate on the split container instead of the child. + if (container_is_floating_or_child(con)) { + while (con->parent->layout != L_FLOATING) { + con = con->parent; + } + } + // If using criteria, this command is executed for every container which // matches the criteria. If this container isn't in the scratchpad, // we'll just silently return a success. - struct sway_container *con = config->handler_context.current_container; - wlr_log(WLR_INFO, "cmd_scratchpad(%s)", con->name); if (!con->scratchpad) { return cmd_results_new(CMD_SUCCESS, NULL, NULL); } diff --git a/sway/scratchpad.c b/sway/scratchpad.c index 181d9b3b..787af6e1 100644 --- a/sway/scratchpad.c +++ b/sway/scratchpad.c @@ -110,6 +110,15 @@ void scratchpad_toggle_auto(void) { struct sway_container *ws = focus->type == C_WORKSPACE ? focus : container_parent(focus, C_WORKSPACE); + // If the focus is in a floating split container, + // operate on the split container instead of the child. + if (container_is_floating_or_child(focus)) { + while (focus->parent->layout != L_FLOATING) { + focus = focus->parent; + } + } + + // Check if the currently focused window is a scratchpad window and should // be hidden again. if (focus->scratchpad) { From da2a87f6c71bfe90a4d77542bfc7ed22899f67be Mon Sep 17 00:00:00 2001 From: Ryan Dwyer Date: Thu, 26 Jul 2018 22:58:42 +1000 Subject: [PATCH 066/148] When unfloating, return container to previously focused tiled container This introduces seat_get_focus_inactive_tiling and updates `focus mode_toggle` to use it instead, because the previous method wasn't guaranteed to return a tiling view. --- include/sway/input/seat.h | 3 +++ sway/commands/focus.c | 14 ++++++++------ sway/input/seat.c | 23 +++++++++++++++++------ sway/tree/container.c | 5 ++++- 4 files changed, 32 insertions(+), 13 deletions(-) diff --git a/include/sway/input/seat.h b/include/sway/input/seat.h index ab25788f..07febe2c 100644 --- a/include/sway/input/seat.h +++ b/include/sway/input/seat.h @@ -124,6 +124,9 @@ struct sway_container *seat_get_focus(struct sway_seat *seat); struct sway_container *seat_get_focus_inactive(struct sway_seat *seat, struct sway_container *container); +struct sway_container *seat_get_focus_inactive_tiling(struct sway_seat *seat, + struct sway_container *container); + /** * Descend into the focus stack to find the focus-inactive view. Useful for * container placement when they change position in the tree. diff --git a/sway/commands/focus.c b/sway/commands/focus.c index 9cd8bfae..ce3d032f 100644 --- a/sway/commands/focus.c +++ b/sway/commands/focus.c @@ -35,14 +35,16 @@ static struct cmd_results *focus_mode(struct sway_container *con, struct sway_seat *seat, bool floating) { struct sway_container *ws = con->type == C_WORKSPACE ? con : container_parent(con, C_WORKSPACE); - struct sway_container *new_focus = ws; + struct sway_container *new_focus = NULL; if (floating) { - new_focus = ws->sway_workspace->floating; - if (new_focus->children->length == 0) { - return cmd_results_new(CMD_SUCCESS, NULL, NULL); - } + new_focus = seat_get_focus_inactive(seat, ws->sway_workspace->floating); + } else { + new_focus = seat_get_focus_inactive_tiling(seat, ws); } - seat_set_focus(seat, seat_get_active_child(seat, new_focus)); + if (!new_focus) { + new_focus = ws; + } + seat_set_focus(seat, new_focus); return cmd_results_new(CMD_SUCCESS, NULL, NULL); } diff --git a/sway/input/seat.c b/sway/input/seat.c index 877a93c6..18d5591d 100644 --- a/sway/input/seat.c +++ b/sway/input/seat.c @@ -124,12 +124,14 @@ static void seat_send_focus(struct sway_container *con, } static struct sway_container *seat_get_focus_by_type(struct sway_seat *seat, - struct sway_container *container, enum sway_container_type type) { + struct sway_container *container, enum sway_container_type type, + bool only_tiling) { if (container->type == C_VIEW) { return container; } - struct sway_container *floating = container->type == C_WORKSPACE ? + struct sway_container *floating = + container->type == C_WORKSPACE && !only_tiling ? container->sway_workspace->floating : NULL; if (container->children->length == 0 && (!floating || floating->children->length == 0)) { @@ -143,6 +145,10 @@ static struct sway_container *seat_get_focus_by_type(struct sway_seat *seat, } if (container_has_child(container, current->container)) { + if (only_tiling && + container_is_floating_or_child(current->container)) { + continue; + } return current->container; } if (floating && container_has_child(floating, current->container)) { @@ -169,7 +175,7 @@ void seat_focus_inactive_children_for_each(struct sway_seat *seat, struct sway_container *seat_get_focus_inactive_view(struct sway_seat *seat, struct sway_container *container) { - return seat_get_focus_by_type(seat, container, C_VIEW); + return seat_get_focus_by_type(seat, container, C_VIEW, false); } static void handle_seat_container_destroy(struct wl_listener *listener, @@ -191,7 +197,7 @@ static void handle_seat_container_destroy(struct wl_listener *listener, if (set_focus) { struct sway_container *next_focus = NULL; while (next_focus == NULL) { - next_focus = seat_get_focus_by_type(seat, parent, C_VIEW); + next_focus = seat_get_focus_by_type(seat, parent, C_VIEW, false); if (next_focus == NULL && parent->type == C_WORKSPACE) { next_focus = parent; @@ -648,7 +654,7 @@ void seat_set_focus_warp(struct sway_seat *seat, struct sway_container *new_output_last_ws = NULL; if (last_output && new_output && last_output != new_output) { new_output_last_ws = - seat_get_focus_by_type(seat, new_output, C_WORKSPACE); + seat_get_focus_by_type(seat, new_output, C_WORKSPACE, false); } if (container && container->parent) { @@ -853,7 +859,12 @@ void seat_set_exclusive_client(struct sway_seat *seat, struct sway_container *seat_get_focus_inactive(struct sway_seat *seat, struct sway_container *container) { - return seat_get_focus_by_type(seat, container, C_TYPES); + return seat_get_focus_by_type(seat, container, C_TYPES, false); +} + +struct sway_container *seat_get_focus_inactive_tiling(struct sway_seat *seat, + struct sway_container *container) { + return seat_get_focus_by_type(seat, container, C_TYPES, true); } struct sway_container *seat_get_active_child(struct sway_seat *seat, diff --git a/sway/tree/container.c b/sway/tree/container.c index 566432b1..b8ff87e1 100644 --- a/sway/tree/container.c +++ b/sway/tree/container.c @@ -1015,6 +1015,7 @@ void container_set_floating(struct sway_container *container, bool enable) { return; } + struct sway_seat *seat = input_manager_current_seat(input_manager); struct sway_container *workspace = container_parent(container, C_WORKSPACE); if (enable) { @@ -1029,8 +1030,10 @@ void container_set_floating(struct sway_container *container, bool enable) { if (container->scratchpad) { scratchpad_remove_container(container); } + struct sway_container *sibling = + seat_get_focus_inactive_tiling(seat, workspace); container_remove_child(container); - container_add_child(workspace, container); + container_add_child(sibling, container); container->width = container->parent->width; container->height = container->parent->height; if (container->type == C_VIEW) { From c95847c9e17cf42a8ff23f791232c0ddd671ba6e Mon Sep 17 00:00:00 2001 From: Ryan Dwyer Date: Thu, 26 Jul 2018 23:00:58 +1000 Subject: [PATCH 067/148] Allow splitting a floating view --- sway/commands/split.c | 4 ---- 1 file changed, 4 deletions(-) diff --git a/sway/commands/split.c b/sway/commands/split.c index 313799da..a8eddf54 100644 --- a/sway/commands/split.c +++ b/sway/commands/split.c @@ -10,10 +10,6 @@ static struct cmd_results *do_split(int layout) { struct sway_container *con = config->handler_context.current_container; - if (container_is_floating(con)) { - return cmd_results_new(CMD_FAILURE, "split", - "Can't split a floating view"); - } struct sway_container *parent = container_split(con, layout); container_create_notify(parent); arrange_windows(parent->parent); From 538e083f61c363ef1127636d8fac1b7e4872e4c4 Mon Sep 17 00:00:00 2001 From: Ryan Dwyer Date: Fri, 27 Jul 2018 09:10:10 +1000 Subject: [PATCH 068/148] Fix focus mode_toggle from a child of a floating container Also fixes a crash when unfloating a window. It needs to add it back to the tiling tree as a sibling rather than a child, because the reference container might be a view. --- sway/commands/focus.c | 11 ++++++++++- sway/tree/container.c | 2 +- 2 files changed, 11 insertions(+), 2 deletions(-) diff --git a/sway/commands/focus.c b/sway/commands/focus.c index ce3d032f..76d3f1dc 100644 --- a/sway/commands/focus.c +++ b/sway/commands/focus.c @@ -35,6 +35,15 @@ static struct cmd_results *focus_mode(struct sway_container *con, struct sway_seat *seat, bool floating) { struct sway_container *ws = con->type == C_WORKSPACE ? con : container_parent(con, C_WORKSPACE); + + // If the container is in a floating split container, + // operate on the split container instead of the child. + if (container_is_floating_or_child(con)) { + while (con->parent->layout != L_FLOATING) { + con = con->parent; + } + } + struct sway_container *new_focus = NULL; if (floating) { new_focus = seat_get_focus_inactive(seat, ws->sway_workspace->floating); @@ -99,7 +108,7 @@ struct cmd_results *cmd_focus(int argc, char **argv) { } else if (strcmp(argv[0], "tiling") == 0) { return focus_mode(con, seat, false); } else if (strcmp(argv[0], "mode_toggle") == 0) { - return focus_mode(con, seat, !container_is_floating(con)); + return focus_mode(con, seat, !container_is_floating_or_child(con)); } if (strcmp(argv[0], "output") == 0) { diff --git a/sway/tree/container.c b/sway/tree/container.c index b8ff87e1..71babe5c 100644 --- a/sway/tree/container.c +++ b/sway/tree/container.c @@ -1033,7 +1033,7 @@ void container_set_floating(struct sway_container *container, bool enable) { struct sway_container *sibling = seat_get_focus_inactive_tiling(seat, workspace); container_remove_child(container); - container_add_child(sibling, container); + container_add_sibling(sibling, container); container->width = container->parent->width; container->height = container->parent->height; if (container->type == C_VIEW) { From 9d1f6e095b08252585e6279cd32391a23a877998 Mon Sep 17 00:00:00 2001 From: Ryan Dwyer Date: Fri, 27 Jul 2018 09:14:12 +1000 Subject: [PATCH 069/148] Send floating container to scratchpad when a child is focused --- sway/commands/move.c | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/sway/commands/move.c b/sway/commands/move.c index b127c89f..1aae3838 100644 --- a/sway/commands/move.c +++ b/sway/commands/move.c @@ -308,6 +308,15 @@ static struct cmd_results *move_to_scratchpad(struct sway_container *con) { con = container_wrap_children(con); workspace->layout = L_HORIZ; } + + // If the container is in a floating split container, + // operate on the split container instead of the child. + if (container_is_floating_or_child(con)) { + while (con->parent->layout != L_FLOATING) { + con = con->parent; + } + } + if (con->scratchpad) { return cmd_results_new(CMD_INVALID, "move", "Container is already in the scratchpad"); From debb8e85b2808e4b5e8ff79a702ffc48efc36389 Mon Sep 17 00:00:00 2001 From: Ryan Dwyer Date: Fri, 27 Jul 2018 13:46:01 +1000 Subject: [PATCH 070/148] Fix crash when unfloating child of container into empty workspace --- sway/tree/container.c | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/sway/tree/container.c b/sway/tree/container.c index 71babe5c..2332d81f 100644 --- a/sway/tree/container.c +++ b/sway/tree/container.c @@ -1030,10 +1030,13 @@ void container_set_floating(struct sway_container *container, bool enable) { if (container->scratchpad) { scratchpad_remove_container(container); } - struct sway_container *sibling = - seat_get_focus_inactive_tiling(seat, workspace); container_remove_child(container); - container_add_sibling(sibling, container); + struct sway_container *reference = + seat_get_focus_inactive_tiling(seat, workspace); + if (reference->type == C_VIEW) { + reference = reference->parent; + } + container_add_child(reference, container); container->width = container->parent->width; container->height = container->parent->height; if (container->type == C_VIEW) { From 0337609667ad1d01e0e0dc19231373df3fbf7c87 Mon Sep 17 00:00:00 2001 From: Ryan Dwyer Date: Fri, 27 Jul 2018 13:48:20 +1000 Subject: [PATCH 071/148] Focus child when showing a scratchpad container --- sway/scratchpad.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sway/scratchpad.c b/sway/scratchpad.c index 787af6e1..b7d6fd99 100644 --- a/sway/scratchpad.c +++ b/sway/scratchpad.c @@ -82,7 +82,7 @@ static void scratchpad_show(struct sway_container *con) { } arrange_windows(ws); - seat_set_focus(seat, con); + seat_set_focus(seat, seat_get_focus_inactive(seat, con)); container_set_dirty(con->parent); } From 84cd22c8cb722daaa9250a792da0f44930accfae Mon Sep 17 00:00:00 2001 From: Ryan Dwyer Date: Sat, 28 Jul 2018 22:13:13 +1000 Subject: [PATCH 072/148] Fix crash when a deferred command destroys a workspace Example config that produces the crash (with a single output): workspace 1 workspace 2 Prior to this commit, container_workspace_free would manually mark the L_FLOATING container as destroying and free it. This assumed the L_FLOATING container would never be involved in a transaction. This was a safe assumption when it was implemented, but became an incorrect assumption once parent/child relationships became transactionised. This commit removes the L_FLOATING free from container_workspace_free. When the workspace is destroyed, it starts the normal destroy process on the L_FLOATING container so it can be freed via transactions. --- sway/tree/container.c | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/sway/tree/container.c b/sway/tree/container.c index 6ebf2653..01387636 100644 --- a/sway/tree/container.c +++ b/sway/tree/container.c @@ -142,8 +142,6 @@ struct sway_container *container_create(enum sway_container_type type) { static void container_workspace_free(struct sway_workspace *ws) { list_foreach(ws->output_priority, free); list_free(ws->output_priority); - ws->floating->destroying = true; - container_free(ws->floating); free(ws); } @@ -196,6 +194,9 @@ void container_free(struct sway_container *cont) { free(cont); } +static struct sway_container *container_destroy_noreaping( + struct sway_container *con); + static struct sway_container *container_workspace_destroy( struct sway_container *workspace) { if (!sway_assert(workspace, "cannot destroy null workspace")) { @@ -240,6 +241,8 @@ static struct sway_container *container_workspace_destroy( } } + container_destroy_noreaping(workspace->sway_workspace->floating); + return output; } From 30fee94354b61c2604431a53f4d1a1daf6c0a67a Mon Sep 17 00:00:00 2001 From: Geoff Greer Date: Fri, 27 Jul 2018 22:42:50 -0700 Subject: [PATCH 073/148] Don't enable numlock by default. This fixes an annoying issue where laptop keyboards would have 'numlock mode' enabled, remapping parts of the alphabet to numbers. --- sway/input/keyboard.c | 2 +- sway/sway-input.5.scd | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/sway/input/keyboard.c b/sway/input/keyboard.c index 643ff510..8dc8239c 100644 --- a/sway/input/keyboard.c +++ b/sway/input/keyboard.c @@ -387,7 +387,7 @@ void sway_keyboard_configure(struct sway_keyboard *keyboard) { wlr_keyboard_set_keymap(wlr_device->keyboard, keyboard->keymap); xkb_mod_mask_t locked_mods = 0; - if (!input_config || input_config->xkb_numlock != 0) { + if (input_config && input_config->xkb_numlock > 0) { xkb_mod_index_t mod_index = xkb_map_mod_get_index(keymap, XKB_MOD_NAME_NUM); if (mod_index != XKB_MOD_INVALID) { locked_mods |= (uint32_t)1 << mod_index; diff --git a/sway/sway-input.5.scd b/sway/sway-input.5.scd index fa311971..b2395273 100644 --- a/sway/sway-input.5.scd +++ b/sway/sway-input.5.scd @@ -39,7 +39,7 @@ The following commands may only be used in the configuration file. Initially enables or disables CapsLock, the default is disabled. *input* xkb\_numlock enabled|disabled - Initially enables or disables NumLock, the default is enabled. + Initially enables or disables NumLock, the default is disabled. ## MAPPING CONFIGURATION From 1e65439a5448ac203fa0476d410ad303e0766552 Mon Sep 17 00:00:00 2001 From: Drew DeVault Date: Sat, 28 Jul 2018 20:47:56 -0400 Subject: [PATCH 074/148] Add virtual keyboard protocol Ref #2373 --- include/sway/input/input-manager.h | 3 +++ sway/input/input-manager.c | 36 ++++++++++++++++++++++++++++++ 2 files changed, 39 insertions(+) diff --git a/include/sway/input/input-manager.h b/include/sway/input/input-manager.h index 89a3ac71..aa2f6f19 100644 --- a/include/sway/input/input-manager.h +++ b/include/sway/input/input-manager.h @@ -2,6 +2,7 @@ #define _SWAY_INPUT_INPUT_MANAGER_H #include #include +#include #include "sway/server.h" #include "sway/config.h" #include "list.h" @@ -25,10 +26,12 @@ struct sway_input_manager { struct wl_list seats; struct wlr_input_inhibit_manager *inhibit; + struct wlr_virtual_keyboard_manager_v1 *virtual_keyboard; struct wl_listener new_input; struct wl_listener inhibit_activate; struct wl_listener inhibit_deactivate; + struct wl_listener virtual_keyboard_new; }; struct sway_input_manager *input_manager_create(struct sway_server *server); diff --git a/sway/input/input-manager.c b/sway/input/input-manager.c index 0b7cb766..c820e032 100644 --- a/sway/input/input-manager.c +++ b/sway/input/input-manager.c @@ -8,6 +8,7 @@ #include #include #include +#include #include "sway/config.h" #include "sway/input/input-manager.h" #include "sway/input/seat.h" @@ -303,6 +304,35 @@ static void handle_inhibit_deactivate(struct wl_listener *listener, void *data) } } +void handle_virtual_keyboard(struct wl_listener *listener, void *data) { + struct sway_input_manager *input_manager = + wl_container_of(listener, input_manager, virtual_keyboard_new); + struct wlr_virtual_keyboard_v1 *keyboard = data; + struct wlr_input_device *device = &keyboard->input_device; + + struct sway_seat *seat = input_manager_get_default_seat(input_manager); + + // TODO: The user might want this on a different seat + struct sway_input_device *input_device = + calloc(1, sizeof(struct sway_input_device)); + if (!sway_assert(input_device, "could not allocate input device")) { + return; + } + device->data = input_device; + + input_device->wlr_device = device; + input_device->identifier = get_device_identifier(device); + wl_list_insert(&input_manager->devices, &input_device->link); + + wlr_log(WLR_DEBUG, "adding virtual keyboard: '%s'", + input_device->identifier); + + wl_signal_add(&device->events.destroy, &input_device->device_destroy); + input_device->device_destroy.notify = handle_device_destroy; + + seat_add_device(seat, input_device); +} + struct sway_input_manager *input_manager_create( struct sway_server *server) { struct sway_input_manager *input = @@ -321,6 +351,12 @@ struct sway_input_manager *input_manager_create( input->new_input.notify = handle_new_input; wl_signal_add(&server->backend->events.new_input, &input->new_input); + input->virtual_keyboard = wlr_virtual_keyboard_manager_v1_create( + server->wl_display); + wl_signal_add(&input->virtual_keyboard->events.new_virtual_keyboard, + &input->virtual_keyboard_new); + input->virtual_keyboard_new.notify = handle_virtual_keyboard; + input->inhibit = wlr_input_inhibit_manager_create(server->wl_display); input->inhibit_activate.notify = handle_inhibit_activate; wl_signal_add(&input->inhibit->events.activate, From 7b9aee5451a08f63d9f9effed286cdb0339b4e5b Mon Sep 17 00:00:00 2001 From: Marius Orcsik Date: Sun, 29 Jul 2018 14:47:53 +0200 Subject: [PATCH 075/148] Escaping underscores in tap_button_map --- sway/sway-input.5.scd | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sway/sway-input.5.scd b/sway/sway-input.5.scd index b2395273..707c36af 100644 --- a/sway/sway-input.5.scd +++ b/sway/sway-input.5.scd @@ -108,7 +108,7 @@ The following commands may only be used in the configuration file. *input* tap enabled|disabled Enables or disables tap for specified input device. -*input* tap_button_map lrm|lmr +*input* tap\_button\_map lrm|lmr Specifies which button mapping to use for tapping. _lrm_ treats 1 finger as left click, 2 fingers as right click, and 3 fingers as middle click. _lmr_ treats 1 finger as left click, 2 fingers as middle click, and 3 fingers as From d2172bd331937ab406175a6b4c5a76db6f406fbe Mon Sep 17 00:00:00 2001 From: emersion Date: Fri, 27 Jul 2018 18:17:47 +0100 Subject: [PATCH 076/148] wip: redesign output_unmanaged_for_each_surface iterator --- include/sway/output.h | 17 ++++++++-- sway/desktop/output.c | 73 +++++++++++++++++++++++++++++++++++++++---- sway/desktop/render.c | 31 ++++++++++++++++-- 3 files changed, 110 insertions(+), 11 deletions(-) diff --git a/include/sway/output.h b/include/sway/output.h index c225e541..99b0bcc6 100644 --- a/include/sway/output.h +++ b/include/sway/output.h @@ -5,6 +5,7 @@ #include #include #include +#include "config.h" #include "sway/tree/view.h" struct sway_server; @@ -48,6 +49,10 @@ struct root_geometry { float rotation; }; +typedef void (*sway_surface_iterator_func_t)(struct sway_output *output, + struct wlr_surface *surface, struct wlr_box *box, float rotation, + void *user_data); + void output_damage_whole(struct sway_output *output); void output_damage_surface(struct sway_output *output, double ox, double oy, @@ -80,6 +85,10 @@ void output_surface_for_each_surface(struct wlr_surface *surface, double ox, double oy, struct root_geometry *geo, wlr_surface_iterator_func_t iterator, void *user_data); +void output_surface_for_each_surface2(struct sway_output *output, + struct wlr_surface *surface, double ox, double oy, float rotation, + sway_surface_iterator_func_t iterator, void *user_data); + void output_view_for_each_surface(struct sway_view *view, struct sway_output *output, struct root_geometry *geo, wlr_surface_iterator_func_t iterator, void *user_data); @@ -88,9 +97,11 @@ void output_layer_for_each_surface(struct wl_list *layer_surfaces, struct root_geometry *geo, wlr_surface_iterator_func_t iterator, void *user_data); -void output_unmanaged_for_each_surface(struct wl_list *unmanaged, - struct sway_output *output, struct root_geometry *geo, - wlr_surface_iterator_func_t iterator, void *user_data); +#ifdef HAVE_XWAYLAND +void output_unmanaged_for_each_surface(struct sway_output *output, + struct wl_list *unmanaged, sway_surface_iterator_func_t iterator, + void *user_data); +#endif void output_drag_icons_for_each_surface(struct wl_list *drag_icons, struct sway_output *output, struct root_geometry *geo, diff --git a/sway/desktop/output.c b/sway/desktop/output.c index cecd300a..9c2a1a07 100644 --- a/sway/desktop/output.c +++ b/sway/desktop/output.c @@ -105,6 +105,56 @@ void output_surface_for_each_surface(struct wlr_surface *surface, wlr_surface_for_each_surface(surface, iterator, user_data); } +struct surface_iterator_data { + sway_surface_iterator_func_t user_iterator; + void *user_data; + + struct sway_output *output; + double ox, oy; + int width, height; + float rotation; +}; + +void output_surface_for_each_surface2_iterator(struct wlr_surface *surface, + int sx, int sy, void *_data) { + struct surface_iterator_data *data = _data; + + struct root_geometry geo = { + .x = data->ox, + .y = data->oy, + .width = data->width, + .height = data->height, + .rotation = data->rotation, + }; + struct wlr_box box; + bool intersects = output_get_surface_box(&geo, data->output, + surface, sx, sy, &box); + if (!intersects) { + return; + } + + data->user_iterator(data->output, surface, &box, data->rotation, + data->user_data); +} + +void output_surface_for_each_surface2(struct sway_output *output, + struct wlr_surface *surface, double ox, double oy, float rotation, + sway_surface_iterator_func_t iterator, void *user_data) { + struct surface_iterator_data data = { + .user_iterator = iterator, + .user_data = user_data, + .output = output, + .ox = ox, + .oy = oy, + .width = surface->current.width, + .height = surface->current.height, + .rotation = rotation, + }; + + wlr_surface_for_each_surface(surface, + output_surface_for_each_surface2_iterator, &data); +} + void output_view_for_each_surface(struct sway_view *view, struct sway_output *output, struct root_geometry *geo, wlr_surface_iterator_func_t iterator, void *user_data) { @@ -129,10 +179,11 @@ void output_layer_for_each_surface(struct wl_list *layer_surfaces, user_data); } } + #ifdef HAVE_XWAYLAND -void output_unmanaged_for_each_surface(struct wl_list *unmanaged, - struct sway_output *output, struct root_geometry *geo, - wlr_surface_iterator_func_t iterator, void *user_data) { +void output_unmanaged_for_each_surface(struct sway_output *output, + struct wl_list *unmanaged, sway_surface_iterator_func_t iterator, + void *user_data) { struct sway_xwayland_unmanaged *unmanaged_surface; wl_list_for_each(unmanaged_surface, unmanaged, link) { struct wlr_xwayland_surface *xsurface = @@ -140,11 +191,12 @@ void output_unmanaged_for_each_surface(struct wl_list *unmanaged, double ox = unmanaged_surface->lx - output->swayc->current.swayc_x; double oy = unmanaged_surface->ly - output->swayc->current.swayc_y; - output_surface_for_each_surface(xsurface->surface, ox, oy, geo, + output_surface_for_each_surface2(output, xsurface->surface, ox, oy, 0, iterator, user_data); } } #endif + void output_drag_icons_for_each_surface(struct wl_list *drag_icons, struct sway_output *output, struct root_geometry *geo, wlr_surface_iterator_func_t iterator, void *user_data) { @@ -228,18 +280,27 @@ static void send_frame_done_iterator(struct wlr_surface *surface, } } +static void send_frame_done_iterator2(struct sway_output *output, + struct wlr_surface *surface, struct wlr_box *box, float rotation, + void *_data) { + struct send_frame_done_data *data = _data; + wlr_surface_send_frame_done(surface, data->when); +} + static void send_frame_done_layer(struct send_frame_done_data *data, struct wl_list *layer_surfaces) { output_layer_for_each_surface(layer_surfaces, &data->root_geo, send_frame_done_iterator, data); } + #ifdef HAVE_XWAYLAND static void send_frame_done_unmanaged(struct send_frame_done_data *data, struct wl_list *unmanaged) { - output_unmanaged_for_each_surface(unmanaged, data->output, &data->root_geo, - send_frame_done_iterator, data); + output_unmanaged_for_each_surface(data->output, unmanaged, + send_frame_done_iterator2, data); } #endif + static void send_frame_done_drag_icons(struct send_frame_done_data *data, struct wl_list *drag_icons) { output_drag_icons_for_each_surface(drag_icons, data->output, &data->root_geo, diff --git a/sway/desktop/render.c b/sway/desktop/render.c index c9fdfd95..8eea19e0 100644 --- a/sway/desktop/render.c +++ b/sway/desktop/render.c @@ -123,6 +123,31 @@ static void render_surface_iterator(struct wlr_surface *surface, int sx, int sy, render_texture(wlr_output, output_damage, texture, &box, matrix, alpha); } +static void render_surface_iterator2(struct sway_output *output, + struct wlr_surface *surface, struct wlr_box *_box, float rotation, + void *_data) { + struct render_data *data = _data; + struct wlr_output *wlr_output = output->wlr_output; + pixman_region32_t *output_damage = data->damage; + float alpha = data->alpha; + + struct wlr_texture *texture = wlr_surface_get_texture(surface); + if (!texture) { + return; + } + + struct wlr_box box = *_box; + scale_box(&box, wlr_output->scale); + + float matrix[9]; + enum wl_output_transform transform = + wlr_output_transform_invert(surface->current.transform); + wlr_matrix_project_box(matrix, &box, transform, rotation, + wlr_output->transform_matrix); + + render_texture(wlr_output, output_damage, texture, &box, matrix, alpha); +} + static void render_layer(struct sway_output *output, pixman_region32_t *damage, struct wl_list *layer_surfaces) { struct render_data data = { @@ -133,6 +158,7 @@ static void render_layer(struct sway_output *output, output_layer_for_each_surface(layer_surfaces, &data.root_geo, render_surface_iterator, &data); } + #ifdef HAVE_XWAYLAND static void render_unmanaged(struct sway_output *output, pixman_region32_t *damage, struct wl_list *unmanaged) { @@ -141,10 +167,11 @@ static void render_unmanaged(struct sway_output *output, .damage = damage, .alpha = 1.0f, }; - output_unmanaged_for_each_surface(unmanaged, output, &data.root_geo, - render_surface_iterator, &data); + output_unmanaged_for_each_surface(output, unmanaged, + render_surface_iterator2, &data); } #endif + static void render_drag_icons(struct sway_output *output, pixman_region32_t *damage, struct wl_list *drag_icons) { struct render_data data = { From dbf6dd0daec9e712a002c3e01accbd0fee769fac Mon Sep 17 00:00:00 2001 From: emersion Date: Fri, 27 Jul 2018 18:44:36 +0100 Subject: [PATCH 077/148] wip: redesign output_drag_icons_for_each_surface iterator --- include/sway/output.h | 6 +++--- sway/desktop/output.c | 15 ++++++++------- sway/desktop/render.c | 4 ++-- 3 files changed, 13 insertions(+), 12 deletions(-) diff --git a/include/sway/output.h b/include/sway/output.h index 99b0bcc6..10a14207 100644 --- a/include/sway/output.h +++ b/include/sway/output.h @@ -103,8 +103,8 @@ void output_unmanaged_for_each_surface(struct sway_output *output, void *user_data); #endif -void output_drag_icons_for_each_surface(struct wl_list *drag_icons, - struct sway_output *output, struct root_geometry *geo, - wlr_surface_iterator_func_t iterator, void *user_data); +void output_drag_icons_for_each_surface(struct sway_output *output, + struct wl_list *drag_icons, sway_surface_iterator_func_t iterator, + void *user_data); #endif diff --git a/sway/desktop/output.c b/sway/desktop/output.c index 9c2a1a07..e83a9a3d 100644 --- a/sway/desktop/output.c +++ b/sway/desktop/output.c @@ -197,17 +197,18 @@ void output_unmanaged_for_each_surface(struct sway_output *output, } #endif -void output_drag_icons_for_each_surface(struct wl_list *drag_icons, - struct sway_output *output, struct root_geometry *geo, - wlr_surface_iterator_func_t iterator, void *user_data) { +void output_drag_icons_for_each_surface(struct sway_output *output, + struct wl_list *drag_icons, sway_surface_iterator_func_t iterator, + void *user_data) { struct sway_drag_icon *drag_icon; wl_list_for_each(drag_icon, drag_icons, link) { double ox = drag_icon->x - output->swayc->x; double oy = drag_icon->y - output->swayc->y; if (drag_icon->wlr_drag_icon->mapped) { - output_surface_for_each_surface(drag_icon->wlr_drag_icon->surface, - ox, oy, geo, iterator, user_data); + output_surface_for_each_surface2(output, + drag_icon->wlr_drag_icon->surface, ox, oy, 0, + iterator, user_data); } } } @@ -303,8 +304,8 @@ static void send_frame_done_unmanaged(struct send_frame_done_data *data, static void send_frame_done_drag_icons(struct send_frame_done_data *data, struct wl_list *drag_icons) { - output_drag_icons_for_each_surface(drag_icons, data->output, &data->root_geo, - send_frame_done_iterator, data); + output_drag_icons_for_each_surface(data->output, drag_icons, + send_frame_done_iterator2, data); } static void send_frame_done_container_iterator(struct sway_container *con, diff --git a/sway/desktop/render.c b/sway/desktop/render.c index 8eea19e0..1270b7cf 100644 --- a/sway/desktop/render.c +++ b/sway/desktop/render.c @@ -179,8 +179,8 @@ static void render_drag_icons(struct sway_output *output, .damage = damage, .alpha = 1.0f, }; - output_drag_icons_for_each_surface(drag_icons, output, &data.root_geo, - render_surface_iterator, &data); + output_drag_icons_for_each_surface(output, drag_icons, + render_surface_iterator2, &data); } static void render_rect(struct wlr_output *wlr_output, From a0dd9776172796f65b0fce0a8e5cfb66bdb2e2e5 Mon Sep 17 00:00:00 2001 From: emersion Date: Fri, 27 Jul 2018 18:53:15 +0100 Subject: [PATCH 078/148] wip: redesign output_layer_for_each_surface iterator --- include/sway/output.h | 4 ++-- sway/desktop/output.c | 32 ++++++++++++++++---------------- sway/desktop/render.c | 4 ++-- 3 files changed, 20 insertions(+), 20 deletions(-) diff --git a/include/sway/output.h b/include/sway/output.h index 10a14207..c1763b26 100644 --- a/include/sway/output.h +++ b/include/sway/output.h @@ -93,8 +93,8 @@ void output_view_for_each_surface(struct sway_view *view, struct sway_output *output, struct root_geometry *geo, wlr_surface_iterator_func_t iterator, void *user_data); -void output_layer_for_each_surface(struct wl_list *layer_surfaces, - struct root_geometry *geo, wlr_surface_iterator_func_t iterator, +void output_layer_for_each_surface(struct sway_output *output, + struct wl_list *layer_surfaces, sway_surface_iterator_func_t iterator, void *user_data); #ifdef HAVE_XWAYLAND diff --git a/sway/desktop/output.c b/sway/desktop/output.c index e83a9a3d..5e309250 100644 --- a/sway/desktop/output.c +++ b/sway/desktop/output.c @@ -167,15 +167,15 @@ void output_view_for_each_surface(struct sway_view *view, view_for_each_surface(view, iterator, user_data); } -void output_layer_for_each_surface(struct wl_list *layer_surfaces, - struct root_geometry *geo, wlr_surface_iterator_func_t iterator, +void output_layer_for_each_surface(struct sway_output *output, + struct wl_list *layer_surfaces, sway_surface_iterator_func_t iterator, void *user_data) { struct sway_layer_surface *layer_surface; wl_list_for_each(layer_surface, layer_surfaces, link) { struct wlr_layer_surface *wlr_layer_surface = layer_surface->layer_surface; - output_surface_for_each_surface(wlr_layer_surface->surface, - layer_surface->geo.x, layer_surface->geo.y, geo, iterator, + output_surface_for_each_surface2(output, wlr_layer_surface->surface, + layer_surface->geo.x, layer_surface->geo.y, 0, iterator, user_data); } } @@ -288,10 +288,10 @@ static void send_frame_done_iterator2(struct sway_output *output, wlr_surface_send_frame_done(surface, data->when); } -static void send_frame_done_layer(struct send_frame_done_data *data, - struct wl_list *layer_surfaces) { - output_layer_for_each_surface(layer_surfaces, &data->root_geo, - send_frame_done_iterator, data); +static void send_frame_done_layer(struct sway_output *output, + struct wl_list *layer_surfaces, struct send_frame_done_data *data) { + output_layer_for_each_surface(output, layer_surfaces, + send_frame_done_iterator2, data); } #ifdef HAVE_XWAYLAND @@ -352,10 +352,10 @@ static void send_frame_done(struct sway_output *output, struct timespec *when) { &root_container.sway_root->xwayland_unmanaged); #endif } else { - send_frame_done_layer(&data, - &output->layers[ZWLR_LAYER_SHELL_V1_LAYER_BACKGROUND]); - send_frame_done_layer(&data, - &output->layers[ZWLR_LAYER_SHELL_V1_LAYER_BOTTOM]); + send_frame_done_layer(output, + &output->layers[ZWLR_LAYER_SHELL_V1_LAYER_BACKGROUND], &data); + send_frame_done_layer(output, + &output->layers[ZWLR_LAYER_SHELL_V1_LAYER_BOTTOM], &data); send_frame_done_container(&data, workspace); send_frame_done_container(&data, workspace->sway_workspace->floating); @@ -364,13 +364,13 @@ static void send_frame_done(struct sway_output *output, struct timespec *when) { send_frame_done_unmanaged(&data, &root_container.sway_root->xwayland_unmanaged); #endif - send_frame_done_layer(&data, - &output->layers[ZWLR_LAYER_SHELL_V1_LAYER_TOP]); + send_frame_done_layer(output, + &output->layers[ZWLR_LAYER_SHELL_V1_LAYER_TOP], &data); } send_frame_overlay: - send_frame_done_layer(&data, - &output->layers[ZWLR_LAYER_SHELL_V1_LAYER_OVERLAY]); + send_frame_done_layer(output, + &output->layers[ZWLR_LAYER_SHELL_V1_LAYER_OVERLAY], &data); send_frame_done_drag_icons(&data, &root_container.sway_root->drag_icons); } diff --git a/sway/desktop/render.c b/sway/desktop/render.c index 1270b7cf..35dc3edc 100644 --- a/sway/desktop/render.c +++ b/sway/desktop/render.c @@ -155,8 +155,8 @@ static void render_layer(struct sway_output *output, .damage = damage, .alpha = 1.0f, }; - output_layer_for_each_surface(layer_surfaces, &data.root_geo, - render_surface_iterator, &data); + output_layer_for_each_surface(output, layer_surfaces, + render_surface_iterator2, &data); } #ifdef HAVE_XWAYLAND From e9d674cfd294f13a32893dd584826ed7481e05e3 Mon Sep 17 00:00:00 2001 From: emersion Date: Fri, 27 Jul 2018 18:59:14 +0100 Subject: [PATCH 079/148] wip: redesign output_view_for_each_surface iterator --- include/sway/output.h | 6 ++-- sway/desktop/output.c | 75 +++++++++++++++++++++++++++++++++++-------- sway/desktop/render.c | 3 +- 3 files changed, 66 insertions(+), 18 deletions(-) diff --git a/include/sway/output.h b/include/sway/output.h index c1763b26..7a458a84 100644 --- a/include/sway/output.h +++ b/include/sway/output.h @@ -89,9 +89,9 @@ void output_surface_for_each_surface2(struct sway_output *output, struct wlr_surface *surface, double ox, double oy, float rotation, sway_surface_iterator_func_t iterator, void *user_data); -void output_view_for_each_surface(struct sway_view *view, - struct sway_output *output, struct root_geometry *geo, - wlr_surface_iterator_func_t iterator, void *user_data); +void output_view_for_each_surface(struct sway_output *output, + struct sway_view *view, sway_surface_iterator_func_t iterator, + void *user_data); void output_layer_for_each_surface(struct sway_output *output, struct wl_list *layer_surfaces, sway_surface_iterator_func_t iterator, diff --git a/sway/desktop/output.c b/sway/desktop/output.c index 5e309250..31033ee3 100644 --- a/sway/desktop/output.c +++ b/sway/desktop/output.c @@ -155,16 +155,22 @@ void output_surface_for_each_surface2(struct sway_output *output, output_surface_for_each_surface2_iterator, &data); } -void output_view_for_each_surface(struct sway_view *view, - struct sway_output *output, struct root_geometry *geo, - wlr_surface_iterator_func_t iterator, void *user_data) { - geo->x = view->swayc->current.view_x - output->swayc->current.swayc_x; - geo->y = view->swayc->current.view_y - output->swayc->current.swayc_y; - geo->width = view->swayc->current.view_width; - geo->height = view->swayc->current.view_height; - geo->rotation = 0; // TODO +void output_view_for_each_surface(struct sway_output *output, + struct sway_view *view, sway_surface_iterator_func_t iterator, + void *user_data) { + struct surface_iterator_data data = { + .user_iterator = iterator, + .user_data = user_data, + .output = output, + .ox = view->swayc->current.view_x - output->swayc->current.swayc_x, + .oy = view->swayc->current.view_y - output->swayc->current.swayc_y, + .width = view->swayc->current.view_width, + .height = view->swayc->current.view_height, + .rotation = 0, // TODO + }; - view_for_each_surface(view, iterator, user_data); + view_for_each_surface(view, + output_surface_for_each_surface2_iterator, &data); } void output_layer_for_each_surface(struct sway_output *output, @@ -319,8 +325,8 @@ static void send_frame_done_container_iterator(struct sway_container *con, return; } - output_view_for_each_surface(con->sway_view, data->output, &data->root_geo, - send_frame_done_iterator, data); + output_view_for_each_surface(data->output, con->sway_view, + send_frame_done_iterator2, data); } static void send_frame_done_container(struct send_frame_done_data *data, @@ -463,6 +469,50 @@ static void damage_surface_iterator(struct wlr_surface *surface, int sx, int sy, wlr_output_schedule_frame(output->wlr_output); } +static void damage_surface_iterator2(struct sway_output *output, + struct wlr_surface *surface, struct wlr_box *_box, float rotation, + void *_data) { + struct damage_data *data = _data; + bool whole = data->whole; + + struct wlr_box box = *_box; + scale_box(&box, output->wlr_output->scale); + + int center_x = box.x + box.width/2; + int center_y = box.y + box.height/2; + + if (pixman_region32_not_empty(&surface->buffer_damage)) { + enum wl_output_transform transform = + wlr_output_transform_invert(surface->current.transform); + + pixman_region32_t damage; + pixman_region32_init(&damage); + pixman_region32_copy(&damage, &surface->buffer_damage); + wlr_region_transform(&damage, &damage, transform, + surface->current.buffer_width, surface->current.buffer_height); + wlr_region_scale(&damage, &damage, + output->wlr_output->scale / (float)surface->current.scale); + if (ceil(output->wlr_output->scale) > surface->current.scale) { + // When scaling up a surface, it'll become blurry so we need to + // expand the damage region + wlr_region_expand(&damage, &damage, + ceil(output->wlr_output->scale) - surface->current.scale); + } + pixman_region32_translate(&damage, box.x, box.y); + wlr_region_rotated_bounds(&damage, &damage, rotation, + center_x, center_y); + wlr_output_damage_add(output->damage, &damage); + pixman_region32_fini(&damage); + } + + if (whole) { + wlr_box_rotated_bounds(&box, rotation, &box); + wlr_output_damage_add_box(output->damage, &box); + } + + wlr_output_schedule_frame(output->wlr_output); +} + void output_damage_surface(struct sway_output *output, double ox, double oy, struct wlr_surface *surface, bool whole) { struct damage_data data = { @@ -489,8 +539,7 @@ static void output_damage_view(struct sway_output *output, .whole = whole, }; - output_view_for_each_surface(view, output, &data.root_geo, - damage_surface_iterator, &data); + output_view_for_each_surface(output, view, damage_surface_iterator2, &data); } void output_damage_from_view(struct sway_output *output, diff --git a/sway/desktop/render.c b/sway/desktop/render.c index 35dc3edc..18d076df 100644 --- a/sway/desktop/render.c +++ b/sway/desktop/render.c @@ -231,8 +231,7 @@ static void render_view_surfaces(struct sway_view *view, .view = view, .alpha = alpha, }; - output_view_for_each_surface(view, output, &data.root_geo, - render_surface_iterator, &data); + output_view_for_each_surface(output, view, render_surface_iterator2, &data); } static void render_saved_view(struct sway_view *view, From 8d5cc8625ce04c657eab6bd5f242a02e97ddd647 Mon Sep 17 00:00:00 2001 From: emersion Date: Fri, 27 Jul 2018 19:16:36 +0100 Subject: [PATCH 080/148] Completely switch over to new iterators --- include/sway/output.h | 18 --- sway/desktop/output.c | 281 ++++++++++++++---------------------------- sway/desktop/render.c | 49 +------- 3 files changed, 98 insertions(+), 250 deletions(-) diff --git a/include/sway/output.h b/include/sway/output.h index 7a458a84..70f631a0 100644 --- a/include/sway/output.h +++ b/include/sway/output.h @@ -39,16 +39,6 @@ struct sway_output { } events; }; -/** - * Contains a surface's root geometry information. For instance, when rendering - * a popup, this will contain the parent view's position and size. - */ -struct root_geometry { - double x, y; - int width, height; - float rotation; -}; - typedef void (*sway_surface_iterator_func_t)(struct sway_output *output, struct wlr_surface *surface, struct wlr_box *box, float rotation, void *user_data); @@ -77,14 +67,6 @@ struct sway_container *output_get_active_workspace(struct sway_output *output); void output_render(struct sway_output *output, struct timespec *when, pixman_region32_t *damage); -bool output_get_surface_box(struct root_geometry *geo, - struct sway_output *output, struct wlr_surface *surface, int sx, int sy, - struct wlr_box *surface_box); - -void output_surface_for_each_surface(struct wlr_surface *surface, - double ox, double oy, struct root_geometry *geo, - wlr_surface_iterator_func_t iterator, void *user_data); - void output_surface_for_each_surface2(struct sway_output *output, struct wlr_surface *surface, double ox, double oy, float rotation, sway_surface_iterator_func_t iterator, void *user_data); diff --git a/sway/desktop/output.c b/sway/desktop/output.c index 31033ee3..e1d85b10 100644 --- a/sway/desktop/output.c +++ b/sway/desktop/output.c @@ -57,54 +57,6 @@ static void rotate_child_position(double *sx, double *sy, double sw, double sh, *sy = ry + ph/2 - sh/2; } -bool output_get_surface_box(struct root_geometry *geo, - struct sway_output *output, struct wlr_surface *surface, int sx, int sy, - struct wlr_box *surface_box) { - if (!wlr_surface_has_buffer(surface)) { - return false; - } - - int sw = surface->current.width; - int sh = surface->current.height; - - double _sx = sx, _sy = sy; - rotate_child_position(&_sx, &_sy, sw, sh, geo->width, geo->height, - geo->rotation); - - struct wlr_box box = { - .x = geo->x + _sx, - .y = geo->y + _sy, - .width = sw, - .height = sh, - }; - if (surface_box != NULL) { - memcpy(surface_box, &box, sizeof(struct wlr_box)); - } - - struct wlr_box rotated_box; - wlr_box_rotated_bounds(&box, geo->rotation, &rotated_box); - - struct wlr_box output_box = { - .width = output->swayc->current.swayc_width, - .height = output->swayc->current.swayc_height, - }; - - struct wlr_box intersection; - return wlr_box_intersection(&output_box, &rotated_box, &intersection); -} - -void output_surface_for_each_surface(struct wlr_surface *surface, - double ox, double oy, struct root_geometry *geo, - wlr_surface_iterator_func_t iterator, void *user_data) { - geo->x = ox; - geo->y = oy; - geo->width = surface->current.width; - geo->height = surface->current.height; - geo->rotation = 0; - - wlr_surface_for_each_surface(surface, iterator, user_data); -} - struct surface_iterator_data { sway_surface_iterator_func_t user_iterator; void *user_data; @@ -115,20 +67,50 @@ struct surface_iterator_data { float rotation; }; -void output_surface_for_each_surface2_iterator(struct wlr_surface *surface, +static bool get_surface_box(struct surface_iterator_data *data, + struct wlr_surface *surface, int sx, int sy, + struct wlr_box *surface_box) { + struct sway_output *output = data->output; + + if (!wlr_surface_has_buffer(surface)) { + return false; + } + + int sw = surface->current.width; + int sh = surface->current.height; + + double _sx = sx, _sy = sy; + rotate_child_position(&_sx, &_sy, sw, sh, data->width, data->height, + data->rotation); + + struct wlr_box box = { + .x = data->ox + _sx, + .y = data->oy + _sy, + .width = sw, + .height = sh, + }; + if (surface_box != NULL) { + memcpy(surface_box, &box, sizeof(struct wlr_box)); + } + + struct wlr_box rotated_box; + wlr_box_rotated_bounds(&box, data->rotation, &rotated_box); + + struct wlr_box output_box = { + .width = output->swayc->current.swayc_width, + .height = output->swayc->current.swayc_height, + }; + + struct wlr_box intersection; + return wlr_box_intersection(&output_box, &rotated_box, &intersection); +} + +void output_surface_for_each_surface_iterator(struct wlr_surface *surface, int sx, int sy, void *_data) { struct surface_iterator_data *data = _data; - struct root_geometry geo = { - .x = data->ox, - .y = data->oy, - .width = data->width, - .height = data->height, - .rotation = data->rotation, - }; struct wlr_box box; - bool intersects = output_get_surface_box(&geo, data->output, - surface, sx, sy, &box); + bool intersects = get_surface_box(data, surface, sx, sy, &box); if (!intersects) { return; } @@ -137,7 +119,7 @@ void output_surface_for_each_surface2_iterator(struct wlr_surface *surface, data->user_data); } -void output_surface_for_each_surface2(struct sway_output *output, +void output_surface_for_each_surface(struct sway_output *output, struct wlr_surface *surface, double ox, double oy, float rotation, sway_surface_iterator_func_t iterator, void *user_data) { struct surface_iterator_data data = { @@ -152,7 +134,7 @@ void output_surface_for_each_surface2(struct sway_output *output, }; wlr_surface_for_each_surface(surface, - output_surface_for_each_surface2_iterator, &data); + output_surface_for_each_surface_iterator, &data); } void output_view_for_each_surface(struct sway_output *output, @@ -170,7 +152,7 @@ void output_view_for_each_surface(struct sway_output *output, }; view_for_each_surface(view, - output_surface_for_each_surface2_iterator, &data); + output_surface_for_each_surface_iterator, &data); } void output_layer_for_each_surface(struct sway_output *output, @@ -180,7 +162,7 @@ void output_layer_for_each_surface(struct sway_output *output, wl_list_for_each(layer_surface, layer_surfaces, link) { struct wlr_layer_surface *wlr_layer_surface = layer_surface->layer_surface; - output_surface_for_each_surface2(output, wlr_layer_surface->surface, + output_surface_for_each_surface(output, wlr_layer_surface->surface, layer_surface->geo.x, layer_surface->geo.y, 0, iterator, user_data); } @@ -197,7 +179,7 @@ void output_unmanaged_for_each_surface(struct sway_output *output, double ox = unmanaged_surface->lx - output->swayc->current.swayc_x; double oy = unmanaged_surface->ly - output->swayc->current.swayc_y; - output_surface_for_each_surface2(output, xsurface->surface, ox, oy, 0, + output_surface_for_each_surface(output, xsurface->surface, ox, oy, 0, iterator, user_data); } } @@ -212,7 +194,7 @@ void output_drag_icons_for_each_surface(struct sway_output *output, double oy = drag_icon->y - output->swayc->y; if (drag_icon->wlr_drag_icon->mapped) { - output_surface_for_each_surface2(output, + output_surface_for_each_surface(output, drag_icon->wlr_drag_icon->surface, ox, oy, 0, iterator, user_data); } @@ -270,50 +252,38 @@ bool output_has_opaque_overlay_layer_surface(struct sway_output *output) { return false; } -struct send_frame_done_data { - struct root_geometry root_geo; - struct sway_output *output; - struct timespec *when; -}; - -static void send_frame_done_iterator(struct wlr_surface *surface, - int sx, int sy, void *_data) { - struct send_frame_done_data *data = _data; - - bool intersects = output_get_surface_box(&data->root_geo, data->output, surface, - sx, sy, NULL); - if (intersects) { - wlr_surface_send_frame_done(surface, data->when); - } -} - -static void send_frame_done_iterator2(struct sway_output *output, +static void send_frame_done_iterator(struct sway_output *output, struct wlr_surface *surface, struct wlr_box *box, float rotation, void *_data) { - struct send_frame_done_data *data = _data; - wlr_surface_send_frame_done(surface, data->when); + struct timespec *when = _data; + wlr_surface_send_frame_done(surface, when); } static void send_frame_done_layer(struct sway_output *output, - struct wl_list *layer_surfaces, struct send_frame_done_data *data) { + struct wl_list *layer_surfaces, struct timespec *when) { output_layer_for_each_surface(output, layer_surfaces, - send_frame_done_iterator2, data); + send_frame_done_iterator, when); } #ifdef HAVE_XWAYLAND -static void send_frame_done_unmanaged(struct send_frame_done_data *data, - struct wl_list *unmanaged) { - output_unmanaged_for_each_surface(data->output, unmanaged, - send_frame_done_iterator2, data); +static void send_frame_done_unmanaged(struct sway_output *output, + struct wl_list *unmanaged, struct timespec *when) { + output_unmanaged_for_each_surface(output, unmanaged, + send_frame_done_iterator, when); } #endif -static void send_frame_done_drag_icons(struct send_frame_done_data *data, - struct wl_list *drag_icons) { - output_drag_icons_for_each_surface(data->output, drag_icons, - send_frame_done_iterator2, data); +static void send_frame_done_drag_icons(struct sway_output *output, + struct wl_list *drag_icons, struct timespec *when) { + output_drag_icons_for_each_surface(output, drag_icons, + send_frame_done_iterator, when); } +struct send_frame_done_data { + struct sway_output *output; + struct timespec *when; +}; + static void send_frame_done_container_iterator(struct sway_container *con, void *_data) { struct send_frame_done_data *data = _data; @@ -326,21 +296,20 @@ static void send_frame_done_container_iterator(struct sway_container *con, } output_view_for_each_surface(data->output, con->sway_view, - send_frame_done_iterator2, data); + send_frame_done_iterator, data->when); } -static void send_frame_done_container(struct send_frame_done_data *data, - struct sway_container *con) { - container_descendants(con, C_VIEW, - send_frame_done_container_iterator, data); -} - -static void send_frame_done(struct sway_output *output, struct timespec *when) { +static void send_frame_done_container(struct sway_output *output, + struct sway_container *con, struct timespec *when) { struct send_frame_done_data data = { .output = output, .when = when, }; + container_descendants(con, C_VIEW, + send_frame_done_container_iterator, &data); +} +static void send_frame_done(struct sway_output *output, struct timespec *when) { if (output_has_opaque_overlay_layer_surface(output)) { goto send_frame_overlay; } @@ -349,35 +318,38 @@ static void send_frame_done(struct sway_output *output, struct timespec *when) { if (workspace->current.ws_fullscreen) { if (workspace->current.ws_fullscreen->type == C_VIEW) { send_frame_done_container_iterator( - workspace->current.ws_fullscreen, &data); + workspace->current.ws_fullscreen, when); } else { - send_frame_done_container(&data, workspace->current.ws_fullscreen); + send_frame_done_container(output, workspace->current.ws_fullscreen, + when); } #ifdef HAVE_XWAYLAND - send_frame_done_unmanaged(&data, - &root_container.sway_root->xwayland_unmanaged); + send_frame_done_unmanaged(output, + &root_container.sway_root->xwayland_unmanaged, when); #endif } else { send_frame_done_layer(output, - &output->layers[ZWLR_LAYER_SHELL_V1_LAYER_BACKGROUND], &data); + &output->layers[ZWLR_LAYER_SHELL_V1_LAYER_BACKGROUND], when); send_frame_done_layer(output, - &output->layers[ZWLR_LAYER_SHELL_V1_LAYER_BOTTOM], &data); + &output->layers[ZWLR_LAYER_SHELL_V1_LAYER_BOTTOM], when); - send_frame_done_container(&data, workspace); - send_frame_done_container(&data, workspace->sway_workspace->floating); + send_frame_done_container(output, workspace, when); + send_frame_done_container(output, workspace->sway_workspace->floating, + when); #ifdef HAVE_XWAYLAND - send_frame_done_unmanaged(&data, - &root_container.sway_root->xwayland_unmanaged); + send_frame_done_unmanaged(output, + &root_container.sway_root->xwayland_unmanaged, when); #endif send_frame_done_layer(output, - &output->layers[ZWLR_LAYER_SHELL_V1_LAYER_TOP], &data); + &output->layers[ZWLR_LAYER_SHELL_V1_LAYER_TOP], when); } send_frame_overlay: send_frame_done_layer(output, - &output->layers[ZWLR_LAYER_SHELL_V1_LAYER_OVERLAY], &data); - send_frame_done_drag_icons(&data, &root_container.sway_root->drag_icons); + &output->layers[ZWLR_LAYER_SHELL_V1_LAYER_OVERLAY], when); + send_frame_done_drag_icons(output, &root_container.sway_root->drag_icons, + when); } static void damage_handle_frame(struct wl_listener *listener, void *data) { @@ -412,68 +384,11 @@ void output_damage_whole(struct sway_output *output) { wlr_output_damage_add_whole(output->damage); } -struct damage_data { - struct root_geometry root_geo; - struct sway_output *output; - bool whole; -}; - -static void damage_surface_iterator(struct wlr_surface *surface, int sx, int sy, - void *_data) { - struct damage_data *data = _data; - struct sway_output *output = data->output; - float rotation = data->root_geo.rotation; - bool whole = data->whole; - - struct wlr_box box; - bool intersects = output_get_surface_box(&data->root_geo, data->output, surface, - sx, sy, &box); - if (!intersects) { - return; - } - - scale_box(&box, output->wlr_output->scale); - - int center_x = box.x + box.width/2; - int center_y = box.y + box.height/2; - - if (pixman_region32_not_empty(&surface->buffer_damage)) { - enum wl_output_transform transform = - wlr_output_transform_invert(surface->current.transform); - - pixman_region32_t damage; - pixman_region32_init(&damage); - pixman_region32_copy(&damage, &surface->buffer_damage); - wlr_region_transform(&damage, &damage, transform, - surface->current.buffer_width, surface->current.buffer_height); - wlr_region_scale(&damage, &damage, - output->wlr_output->scale / (float)surface->current.scale); - if (ceil(output->wlr_output->scale) > surface->current.scale) { - // When scaling up a surface, it'll become blurry so we need to - // expand the damage region - wlr_region_expand(&damage, &damage, - ceil(output->wlr_output->scale) - surface->current.scale); - } - pixman_region32_translate(&damage, box.x, box.y); - wlr_region_rotated_bounds(&damage, &damage, rotation, - center_x, center_y); - wlr_output_damage_add(output->damage, &damage); - pixman_region32_fini(&damage); - } - - if (whole) { - wlr_box_rotated_bounds(&box, rotation, &box); - wlr_output_damage_add_box(output->damage, &box); - } - - wlr_output_schedule_frame(output->wlr_output); -} - -static void damage_surface_iterator2(struct sway_output *output, +static void damage_surface_iterator(struct sway_output *output, struct wlr_surface *surface, struct wlr_box *_box, float rotation, void *_data) { - struct damage_data *data = _data; - bool whole = data->whole; + bool *data = _data; + bool whole = *data; struct wlr_box box = *_box; scale_box(&box, output->wlr_output->scale); @@ -515,13 +430,8 @@ static void damage_surface_iterator2(struct sway_output *output, void output_damage_surface(struct sway_output *output, double ox, double oy, struct wlr_surface *surface, bool whole) { - struct damage_data data = { - .output = output, - .whole = whole, - }; - - output_surface_for_each_surface(surface, ox, oy, &data.root_geo, - damage_surface_iterator, &data); + output_surface_for_each_surface(output, surface, ox, oy, 0, + damage_surface_iterator, &whole); } static void output_damage_view(struct sway_output *output, @@ -534,12 +444,7 @@ static void output_damage_view(struct sway_output *output, return; } - struct damage_data data = { - .output = output, - .whole = whole, - }; - - output_view_for_each_surface(output, view, damage_surface_iterator2, &data); + output_view_for_each_surface(output, view, damage_surface_iterator, &whole); } void output_damage_from_view(struct sway_output *output, diff --git a/sway/desktop/render.c b/sway/desktop/render.c index 18d076df..f25055b8 100644 --- a/sway/desktop/render.c +++ b/sway/desktop/render.c @@ -29,10 +29,7 @@ #include "sway/tree/workspace.h" struct render_data { - struct root_geometry root_geo; - struct sway_output *output; pixman_region32_t *damage; - struct sway_view *view; float alpha; }; @@ -92,38 +89,7 @@ damage_finish: pixman_region32_fini(&damage); } -static void render_surface_iterator(struct wlr_surface *surface, int sx, int sy, - void *_data) { - struct render_data *data = _data; - struct wlr_output *wlr_output = data->output->wlr_output; - float rotation = data->root_geo.rotation; - pixman_region32_t *output_damage = data->damage; - float alpha = data->alpha; - - struct wlr_texture *texture = wlr_surface_get_texture(surface); - if (!texture) { - return; - } - - struct wlr_box box; - bool intersects = output_get_surface_box(&data->root_geo, data->output, - surface, sx, sy, &box); - if (!intersects) { - return; - } - - scale_box(&box, wlr_output->scale); - - float matrix[9]; - enum wl_output_transform transform = - wlr_output_transform_invert(surface->current.transform); - wlr_matrix_project_box(matrix, &box, transform, rotation, - wlr_output->transform_matrix); - - render_texture(wlr_output, output_damage, texture, &box, matrix, alpha); -} - -static void render_surface_iterator2(struct sway_output *output, +static void render_surface_iterator(struct sway_output *output, struct wlr_surface *surface, struct wlr_box *_box, float rotation, void *_data) { struct render_data *data = _data; @@ -151,36 +117,33 @@ static void render_surface_iterator2(struct sway_output *output, static void render_layer(struct sway_output *output, pixman_region32_t *damage, struct wl_list *layer_surfaces) { struct render_data data = { - .output = output, .damage = damage, .alpha = 1.0f, }; output_layer_for_each_surface(output, layer_surfaces, - render_surface_iterator2, &data); + render_surface_iterator, &data); } #ifdef HAVE_XWAYLAND static void render_unmanaged(struct sway_output *output, pixman_region32_t *damage, struct wl_list *unmanaged) { struct render_data data = { - .output = output, .damage = damage, .alpha = 1.0f, }; output_unmanaged_for_each_surface(output, unmanaged, - render_surface_iterator2, &data); + render_surface_iterator, &data); } #endif static void render_drag_icons(struct sway_output *output, pixman_region32_t *damage, struct wl_list *drag_icons) { struct render_data data = { - .output = output, .damage = damage, .alpha = 1.0f, }; output_drag_icons_for_each_surface(output, drag_icons, - render_surface_iterator2, &data); + render_surface_iterator, &data); } static void render_rect(struct wlr_output *wlr_output, @@ -226,12 +189,10 @@ static void premultiply_alpha(float color[4], float opacity) { static void render_view_surfaces(struct sway_view *view, struct sway_output *output, pixman_region32_t *damage, float alpha) { struct render_data data = { - .output = output, .damage = damage, - .view = view, .alpha = alpha, }; - output_view_for_each_surface(output, view, render_surface_iterator2, &data); + output_view_for_each_surface(output, view, render_surface_iterator, &data); } static void render_saved_view(struct sway_view *view, From fe0750fec107f692be4f47659b446ed3d8d8f3b6 Mon Sep 17 00:00:00 2001 From: emersion Date: Fri, 27 Jul 2018 19:19:30 +0100 Subject: [PATCH 081/148] Remove output_surface_for_each_surface from header --- include/sway/output.h | 4 ---- sway/desktop/output.c | 14 +++++++------- 2 files changed, 7 insertions(+), 11 deletions(-) diff --git a/include/sway/output.h b/include/sway/output.h index 70f631a0..6283db68 100644 --- a/include/sway/output.h +++ b/include/sway/output.h @@ -67,10 +67,6 @@ struct sway_container *output_get_active_workspace(struct sway_output *output); void output_render(struct sway_output *output, struct timespec *when, pixman_region32_t *damage); -void output_surface_for_each_surface2(struct sway_output *output, - struct wlr_surface *surface, double ox, double oy, float rotation, - sway_surface_iterator_func_t iterator, void *user_data); - void output_view_for_each_surface(struct sway_output *output, struct sway_view *view, sway_surface_iterator_func_t iterator, void *user_data); diff --git a/sway/desktop/output.c b/sway/desktop/output.c index e1d85b10..8272b35b 100644 --- a/sway/desktop/output.c +++ b/sway/desktop/output.c @@ -119,8 +119,8 @@ void output_surface_for_each_surface_iterator(struct wlr_surface *surface, data->user_data); } -void output_surface_for_each_surface(struct sway_output *output, - struct wlr_surface *surface, double ox, double oy, float rotation, +static void output_surface_for_each_surface(struct sway_output *output, + struct wlr_surface *surface, double ox, double oy, sway_surface_iterator_func_t iterator, void *user_data) { struct surface_iterator_data data = { .user_iterator = iterator, @@ -130,7 +130,7 @@ void output_surface_for_each_surface(struct sway_output *output, .oy = oy, .width = surface->current.width, .height = surface->current.height, - .rotation = rotation, + .rotation = 0, }; wlr_surface_for_each_surface(surface, @@ -163,7 +163,7 @@ void output_layer_for_each_surface(struct sway_output *output, struct wlr_layer_surface *wlr_layer_surface = layer_surface->layer_surface; output_surface_for_each_surface(output, wlr_layer_surface->surface, - layer_surface->geo.x, layer_surface->geo.y, 0, iterator, + layer_surface->geo.x, layer_surface->geo.y, iterator, user_data); } } @@ -179,7 +179,7 @@ void output_unmanaged_for_each_surface(struct sway_output *output, double ox = unmanaged_surface->lx - output->swayc->current.swayc_x; double oy = unmanaged_surface->ly - output->swayc->current.swayc_y; - output_surface_for_each_surface(output, xsurface->surface, ox, oy, 0, + output_surface_for_each_surface(output, xsurface->surface, ox, oy, iterator, user_data); } } @@ -195,7 +195,7 @@ void output_drag_icons_for_each_surface(struct sway_output *output, if (drag_icon->wlr_drag_icon->mapped) { output_surface_for_each_surface(output, - drag_icon->wlr_drag_icon->surface, ox, oy, 0, + drag_icon->wlr_drag_icon->surface, ox, oy, iterator, user_data); } } @@ -430,7 +430,7 @@ static void damage_surface_iterator(struct sway_output *output, void output_damage_surface(struct sway_output *output, double ox, double oy, struct wlr_surface *surface, bool whole) { - output_surface_for_each_surface(output, surface, ox, oy, 0, + output_surface_for_each_surface(output, surface, ox, oy, damage_surface_iterator, &whole); } From 9951b2ec334545f636c892742ed034d917146558 Mon Sep 17 00:00:00 2001 From: emersion Date: Fri, 27 Jul 2018 19:23:38 +0100 Subject: [PATCH 082/148] Remove unused output_from_wlr_output --- sway/desktop/output.c | 4 ---- 1 file changed, 4 deletions(-) diff --git a/sway/desktop/output.c b/sway/desktop/output.c index 8272b35b..fc767cca 100644 --- a/sway/desktop/output.c +++ b/sway/desktop/output.c @@ -534,10 +534,6 @@ static void handle_scale(struct wl_listener *listener, void *data) { transaction_commit_dirty(); } -struct sway_output *output_from_wlr_output(struct wlr_output *wlr_output) { - return wlr_output->data; -} - void handle_new_output(struct wl_listener *listener, void *data) { struct sway_server *server = wl_container_of(listener, server, new_output); struct wlr_output *wlr_output = data; From 0ad865f0b7db2407e151796253b632036a240c7f Mon Sep 17 00:00:00 2001 From: emersion Date: Sun, 29 Jul 2018 14:34:48 +0100 Subject: [PATCH 083/148] Make output_surface_for_each_surface_iterator static --- sway/desktop/output.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/sway/desktop/output.c b/sway/desktop/output.c index fc767cca..d9ccb00d 100644 --- a/sway/desktop/output.c +++ b/sway/desktop/output.c @@ -105,7 +105,7 @@ static bool get_surface_box(struct surface_iterator_data *data, return wlr_box_intersection(&output_box, &rotated_box, &intersection); } -void output_surface_for_each_surface_iterator(struct wlr_surface *surface, +static void output_for_each_surface_iterator(struct wlr_surface *surface, int sx, int sy, void *_data) { struct surface_iterator_data *data = _data; @@ -134,7 +134,7 @@ static void output_surface_for_each_surface(struct sway_output *output, }; wlr_surface_for_each_surface(surface, - output_surface_for_each_surface_iterator, &data); + output_for_each_surface_iterator, &data); } void output_view_for_each_surface(struct sway_output *output, @@ -152,7 +152,7 @@ void output_view_for_each_surface(struct sway_output *output, }; view_for_each_surface(view, - output_surface_for_each_surface_iterator, &data); + output_for_each_surface_iterator, &data); } void output_layer_for_each_surface(struct sway_output *output, From 9a7e436fad96e9a99f1c06fecedba93828bf4dc1 Mon Sep 17 00:00:00 2001 From: emersion Date: Sun, 29 Jul 2018 11:05:11 +0100 Subject: [PATCH 084/148] Don't give focus if xwayland window has a type hint --- sway/desktop/xwayland.c | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/sway/desktop/xwayland.c b/sway/desktop/xwayland.c index 390ca580..398446f8 100644 --- a/sway/desktop/xwayland.c +++ b/sway/desktop/xwayland.c @@ -69,11 +69,13 @@ static void unmanaged_handle_map(struct wl_listener *listener, void *data) { surface->ly = xsurface->y; desktop_damage_surface(xsurface->surface, surface->lx, surface->ly, true); - struct sway_seat *seat = input_manager_current_seat(input_manager); - struct wlr_xwayland *xwayland = - seat->input->server->xwayland.wlr_xwayland; - wlr_xwayland_set_seat(xwayland, seat->wlr_seat); - seat_set_focus_surface(seat, xsurface->surface, false); + if (wlr_xwayland_or_surface_wants_focus(xsurface)) { + struct sway_seat *seat = input_manager_current_seat(input_manager); + struct wlr_xwayland *xwayland = + seat->input->server->xwayland.wlr_xwayland; + wlr_xwayland_set_seat(xwayland, seat->wlr_seat); + seat_set_focus_surface(seat, xsurface->surface, false); + } } static void unmanaged_handle_unmap(struct wl_listener *listener, void *data) { From e33dfbfa758fb899c276135d06f25359ceee0002 Mon Sep 17 00:00:00 2001 From: frsfnrrg Date: Sun, 29 Jul 2018 16:25:43 -0400 Subject: [PATCH 085/148] Implement key repeat for pressed key bindings Each sway_keyboard is provided with a wayland timer event source. When a valid keypress binding has been found, a callback to handle_keyboard_repeat is set. Any key event will either clear the callback or (if the new key event is a valid keypress binding) delay the callback again. --- include/sway/input/keyboard.h | 5 +++++ sway/input/keyboard.c | 38 +++++++++++++++++++++++++++++++++++ 2 files changed, 43 insertions(+) diff --git a/include/sway/input/keyboard.h b/include/sway/input/keyboard.h index 6713398e..660cdc9c 100644 --- a/include/sway/input/keyboard.h +++ b/include/sway/input/keyboard.h @@ -38,6 +38,11 @@ struct sway_keyboard { struct sway_shortcut_state state_keysyms_raw; struct sway_shortcut_state state_keycodes; struct sway_binding *held_binding; + + struct wl_event_source *key_repeat_source; + struct sway_binding *repeat_binding; + int key_repeat_initial_delay; // first key repeat event delay, in ms + int key_repeat_step_delay; // subsequent repeat delay, in ms }; struct sway_keyboard *sway_keyboard_create(struct sway_seat *seat, diff --git a/sway/input/keyboard.c b/sway/input/keyboard.c index 8dc8239c..be000fb9 100644 --- a/sway/input/keyboard.c +++ b/sway/input/keyboard.c @@ -264,6 +264,7 @@ static void handle_keyboard_key(struct wl_listener *listener, void *data) { } // Identify and execute active pressed binding + struct sway_binding *next_repeat_binding = NULL; if (event->state == WLR_KEY_PRESSED) { struct sway_binding *binding_pressed = NULL; get_active_binding(&keyboard->state_keycodes, @@ -279,6 +280,21 @@ static void handle_keyboard_key(struct wl_listener *listener, void *data) { if (binding_pressed) { seat_execute_command(seat, binding_pressed); handled = true; + next_repeat_binding = binding_pressed; + } + } + + // Set up (or clear) keyboard repeat for a pressed binding + if (next_repeat_binding) { + keyboard->repeat_binding = next_repeat_binding; + if (wl_event_source_timer_update(keyboard->key_repeat_source, + keyboard->key_repeat_initial_delay) < 0) { + wlr_log(WLR_DEBUG, "failed to set key repeat timer"); + } + } else if (keyboard->repeat_binding) { + keyboard->repeat_binding = NULL; + if (wl_event_source_timer_update(keyboard->key_repeat_source, 0) < 0) { + wlr_log(WLR_DEBUG, "failed to disarm key repeat timer"); } } @@ -303,6 +319,22 @@ static void handle_keyboard_key(struct wl_listener *listener, void *data) { transaction_commit_dirty(); } +static int handle_keyboard_repeat(void *data) { + struct sway_keyboard *keyboard = (struct sway_keyboard *)data; + if (keyboard->repeat_binding) { + // We queue the next event first, as the command might cancel it + if (wl_event_source_timer_update(keyboard->key_repeat_source, + keyboard->key_repeat_step_delay) < 0) { + wlr_log(WLR_DEBUG, "failed to update key repeat timer"); + } + + seat_execute_command(keyboard->seat_device->sway_seat, + keyboard->repeat_binding); + transaction_commit_dirty(); + } + return 0; +} + static void handle_keyboard_modifiers(struct wl_listener *listener, void *data) { struct sway_keyboard *keyboard = @@ -328,6 +360,11 @@ struct sway_keyboard *sway_keyboard_create(struct sway_seat *seat, wl_list_init(&keyboard->keyboard_key.link); wl_list_init(&keyboard->keyboard_modifiers.link); + keyboard->key_repeat_source = wl_event_loop_add_timer(server.wl_event_loop, + handle_keyboard_repeat, keyboard); + keyboard->key_repeat_initial_delay = 660; + keyboard->key_repeat_step_delay = 40; + return keyboard; } @@ -441,5 +478,6 @@ void sway_keyboard_destroy(struct sway_keyboard *keyboard) { } wl_list_remove(&keyboard->keyboard_key.link); wl_list_remove(&keyboard->keyboard_modifiers.link); + wl_event_source_remove(keyboard->key_repeat_source); free(keyboard); } From 8dbbfa5965ca8bfe4bc023100387dd657bafe48e Mon Sep 17 00:00:00 2001 From: frsfnrrg Date: Sun, 29 Jul 2018 18:50:20 -0400 Subject: [PATCH 086/148] Bindings use advised keyboard repeat parameters Now 'repeat_delay' and 'repeat_rate' control the initial delay and rate (per second) of repeated binding invocations. If the repeat delay is zero, binding repetition is disabled. When the repeat rate is zero, the binding is repeated exactly once, assuming no other key events intervene. --- include/sway/input/keyboard.h | 2 -- sway/input/keyboard.c | 18 ++++++++++-------- 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/include/sway/input/keyboard.h b/include/sway/input/keyboard.h index 660cdc9c..6d28454c 100644 --- a/include/sway/input/keyboard.h +++ b/include/sway/input/keyboard.h @@ -41,8 +41,6 @@ struct sway_keyboard { struct wl_event_source *key_repeat_source; struct sway_binding *repeat_binding; - int key_repeat_initial_delay; // first key repeat event delay, in ms - int key_repeat_step_delay; // subsequent repeat delay, in ms }; struct sway_keyboard *sway_keyboard_create(struct sway_seat *seat, diff --git a/sway/input/keyboard.c b/sway/input/keyboard.c index be000fb9..160ef10b 100644 --- a/sway/input/keyboard.c +++ b/sway/input/keyboard.c @@ -285,10 +285,10 @@ static void handle_keyboard_key(struct wl_listener *listener, void *data) { } // Set up (or clear) keyboard repeat for a pressed binding - if (next_repeat_binding) { + if (next_repeat_binding && wlr_device->keyboard->repeat_info.delay > 0) { keyboard->repeat_binding = next_repeat_binding; if (wl_event_source_timer_update(keyboard->key_repeat_source, - keyboard->key_repeat_initial_delay) < 0) { + wlr_device->keyboard->repeat_info.delay) < 0) { wlr_log(WLR_DEBUG, "failed to set key repeat timer"); } } else if (keyboard->repeat_binding) { @@ -321,11 +321,15 @@ static void handle_keyboard_key(struct wl_listener *listener, void *data) { static int handle_keyboard_repeat(void *data) { struct sway_keyboard *keyboard = (struct sway_keyboard *)data; + struct wlr_keyboard *wlr_device = + keyboard->seat_device->input_device->wlr_device->keyboard; if (keyboard->repeat_binding) { - // We queue the next event first, as the command might cancel it - if (wl_event_source_timer_update(keyboard->key_repeat_source, - keyboard->key_repeat_step_delay) < 0) { - wlr_log(WLR_DEBUG, "failed to update key repeat timer"); + if (wlr_device->repeat_info.rate > 0) { + // We queue the next event first, as the command might cancel it + if (wl_event_source_timer_update(keyboard->key_repeat_source, + 1000 / wlr_device->repeat_info.rate) < 0) { + wlr_log(WLR_DEBUG, "failed to update key repeat timer"); + } } seat_execute_command(keyboard->seat_device->sway_seat, @@ -362,8 +366,6 @@ struct sway_keyboard *sway_keyboard_create(struct sway_seat *seat, keyboard->key_repeat_source = wl_event_loop_add_timer(server.wl_event_loop, handle_keyboard_repeat, keyboard); - keyboard->key_repeat_initial_delay = 660; - keyboard->key_repeat_step_delay = 40; return keyboard; } From 8dacd0639cb68675f7fd6fb762a0f71dc6148cb7 Mon Sep 17 00:00:00 2001 From: Drew DeVault Date: Sun, 29 Jul 2018 19:33:54 -0400 Subject: [PATCH 087/148] Enable unstable wlroots features --- meson.build | 1 + 1 file changed, 1 insertion(+) diff --git a/meson.build b/meson.build index 06299618..1cb40ade 100644 --- a/meson.build +++ b/meson.build @@ -12,6 +12,7 @@ project( add_project_arguments('-Wno-unused-parameter', language: 'c') add_project_arguments('-Wno-unused-function', language: 'c') add_project_arguments('-Wno-unused-result', language: 'c') +add_project_arguments('-DWLR_USE_UNSTABLE', language: 'c') cc = meson.get_compiler('c') From b8efdeae298d59bf7015a9821e3d29cc4e9852e1 Mon Sep 17 00:00:00 2001 From: Brian Ashworth Date: Mon, 30 Jul 2018 01:59:20 -0400 Subject: [PATCH 088/148] Fix title textures on scale change --- include/sway/tree/container.h | 2 ++ sway/desktop/output.c | 6 +----- sway/tree/container.c | 6 +++++- 3 files changed, 8 insertions(+), 6 deletions(-) diff --git a/include/sway/tree/container.h b/include/sway/tree/container.h index d91b3bf1..d4a42a71 100644 --- a/include/sway/tree/container.h +++ b/include/sway/tree/container.h @@ -270,6 +270,8 @@ int container_count_descendants_of_type(struct sway_container *con, void container_create_notify(struct sway_container *container); +void container_update_textures_recursive(struct sway_container *con); + void container_damage_whole(struct sway_container *container); bool container_reap_empty(struct sway_container *con); diff --git a/sway/desktop/output.c b/sway/desktop/output.c index cecd300a..fb52e7b1 100644 --- a/sway/desktop/output.c +++ b/sway/desktop/output.c @@ -506,14 +506,10 @@ static void handle_transform(struct wl_listener *listener, void *data) { transaction_commit_dirty(); } -static void handle_scale_iterator(struct sway_container *view, void *data) { - view_update_marks_textures(view->sway_view); -} - static void handle_scale(struct wl_listener *listener, void *data) { struct sway_output *output = wl_container_of(listener, output, scale); arrange_layers(output); - container_descendants(output->swayc, C_VIEW, handle_scale_iterator, NULL); + container_update_textures_recursive(output->swayc); arrange_windows(output->swayc); transaction_commit_dirty(); } diff --git a/sway/tree/container.c b/sway/tree/container.c index b7442002..4e85021d 100644 --- a/sway/tree/container.c +++ b/sway/tree/container.c @@ -67,7 +67,7 @@ void container_create_notify(struct sway_container *container) { } } -static void container_update_textures_recursive(struct sway_container *con) { +void container_update_textures_recursive(struct sway_container *con) { if (con->type == C_CONTAINER || con->type == C_VIEW) { container_update_title_textures(con); } @@ -79,6 +79,10 @@ static void container_update_textures_recursive(struct sway_container *con) { struct sway_container *child = con->children->items[i]; container_update_textures_recursive(child); } + + if (con->type == C_WORKSPACE) { + container_update_textures_recursive(con->sway_workspace->floating); + } } } From c6a6faaeae9e7337c61b3b05af852da864c6a1a5 Mon Sep 17 00:00:00 2001 From: Eric Engestrom Date: Mon, 30 Jul 2018 13:03:46 +0100 Subject: [PATCH 089/148] util.h: add missing include parse_boolean() takes and returns a `bool` --- include/util.h | 1 + 1 file changed, 1 insertion(+) diff --git a/include/util.h b/include/util.h index bda941ce..9277fa6e 100644 --- a/include/util.h +++ b/include/util.h @@ -2,6 +2,7 @@ #define _SWAY_UTIL_H #include +#include #include #include #include From 9f598859284ca72d08230301ddabda2fa537cb6d Mon Sep 17 00:00:00 2001 From: Eric Engestrom Date: Thu, 26 Jul 2018 17:25:20 +0100 Subject: [PATCH 090/148] zsh: fix sway completion --- completions/zsh/_sway | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/completions/zsh/_sway b/completions/zsh/_sway index bab90fbf..05112002 100644 --- a/completions/zsh/_sway +++ b/completions/zsh/_sway @@ -18,5 +18,5 @@ _arguments -s \ '(-c --config)'{-c,--config}'[Specify a config file]:files:_files' \ '(-C --validate)'{-C,--validate}'[Check validity of the config file, then exit]' \ '(-d --debug)'{-d,--debug}'[Enables full logging, including debug information]' \ - '(-v --verbose)'{-v,--verbose}'[Enables more verbose logging]' \ + '(-V --verbose)'{-V,--verbose}'[Enables more verbose logging]' \ '(--get-socketpath)'--get-socketpath'[Gets the IPC socket path and prints it, then exits]' From 27e89cf61dab7b92edd905a50df3429d51c65e79 Mon Sep 17 00:00:00 2001 From: Eric Engestrom Date: Fri, 27 Jul 2018 16:31:04 +0100 Subject: [PATCH 091/148] bash: add completion for sway --- completions/bash/sway | 46 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 46 insertions(+) create mode 100644 completions/bash/sway diff --git a/completions/bash/sway b/completions/bash/sway new file mode 100644 index 00000000..edd752cd --- /dev/null +++ b/completions/bash/sway @@ -0,0 +1,46 @@ +# sway(1) completion + +_sway() +{ + local cur prev + _get_comp_words_by_ref cur prev + + short=( + -h + -c + -C + -d + -v + -V + ) + + long=( + --help + --config + --validate + --debug + --version + --verbose + --get-socketpath + ) + + case $prev in + -c|--config) + _filedir + return + ;; + esac + + if [[ $cur == --* ]]; then + COMPREPLY=($(compgen -W "${long[*]}" -- "$cur")) + elif [[ $cur == -* ]]; then + COMPREPLY=($(compgen -W "${short[*]}" -- "$cur")) + COMPREPLY+=($(compgen -W "${long[*]}" -- "$cur")) + else + COMPREPLY=($(compgen -W "${short[*]}" -- "$cur")) + COMPREPLY+=($(compgen -W "${long[*]}" -- "$cur")) + COMPREPLY+=($(compgen -c -- "$cur")) + fi + +} && +complete -F _sway sway From 21609f8af26b51872ef045ec75cb543835961ead Mon Sep 17 00:00:00 2001 From: Eric Engestrom Date: Fri, 27 Jul 2018 16:31:18 +0100 Subject: [PATCH 092/148] bash: add completion for swaymsg --- completions/bash/swaymsg | 57 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 57 insertions(+) create mode 100644 completions/bash/swaymsg diff --git a/completions/bash/swaymsg b/completions/bash/swaymsg new file mode 100644 index 00000000..8ec90b6f --- /dev/null +++ b/completions/bash/swaymsg @@ -0,0 +1,57 @@ +# swaymsg(1) completion + +_swaymsg() +{ + local cur prev + _get_comp_words_by_ref cur prev + + types=( + 'get_workspaces' + 'get_seats' + 'get_inputs' + 'get_outputs' + 'get_tree' + 'get_marks' + 'get_bar_config' + 'get_version' + 'get_clipboard' + ) + + short=( + -h + -q + -r + -s + -t + -v + ) + + long=( + --help + --quiet + --raw + --socket + --type + --verbose + ) + + case $prev in + -s|--socket) + _filedir + return + ;; + -t|--type) + COMPREPLY=($(compgen -W "${types[*]}" -- "$cur")) + return + ;; + esac + + if [[ $cur == --* ]]; then + COMPREPLY=($(compgen -W "${long[*]}" -- "$cur")) + else + COMPREPLY=($(compgen -W "${short[*]}" -- "$cur")) + COMPREPLY+=($(compgen -W "${long[*]}" -- "$cur")) + fi + +} && +complete -F _swaymsg swaymsg From 4a8a19d21f91a3126d83ac42d6a96900f42ba64c Mon Sep 17 00:00:00 2001 From: Eric Engestrom Date: Fri, 27 Jul 2018 16:31:30 +0100 Subject: [PATCH 093/148] bash: add completion for swaylock --- completions/bash/swaylock | 66 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 66 insertions(+) create mode 100644 completions/bash/swaylock diff --git a/completions/bash/swaylock b/completions/bash/swaylock new file mode 100644 index 00000000..33925480 --- /dev/null +++ b/completions/bash/swaylock @@ -0,0 +1,66 @@ +# swaylock(1) completion + +_swaylock() +{ + local cur prev + _get_comp_words_by_ref -n : cur prev + + short=( + -h + -c + -s + -t + -v + -i + -u + -f + ) + + long=( + --help + --color + --scaling + --tiling + --version + --image + --no-unlock-indicator + --daemonize + ) + + scaling=( + 'stretch' + 'fill' + 'fit' + 'center' + 'tile' + ) + + case $prev in + -c|--color) + return + ;; + --scaling) + COMPREPLY=($(compgen -W "${scaling[*]}" -- "$cur")) + return + ;; + -i|--image) + if grep -q : <<< "$cur"; then + output="${cur%%:*}:" + cur="${cur#*:}" + else + output= + fi + COMPREPLY=($(compgen -f -- "$cur")) + return + ;; + esac + + if [[ $cur == --* ]]; then + COMPREPLY=($(compgen -W "${long[*]}" -- "$cur")) + else + COMPREPLY=($(compgen -W "${short[*]}" -- "$cur")) + COMPREPLY+=($(compgen -W "${long[*]}" -- "$cur")) + fi + +} && +complete -F _swaylock swaylock From c4b4da5dc4ba9c68ff28ba2a01566b760adf1f55 Mon Sep 17 00:00:00 2001 From: Eric Engestrom Date: Fri, 27 Jul 2018 16:31:39 +0100 Subject: [PATCH 094/148] bash: add completion for swayidle --- completions/bash/swayidle | 48 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 48 insertions(+) create mode 100644 completions/bash/swayidle diff --git a/completions/bash/swayidle b/completions/bash/swayidle new file mode 100644 index 00000000..a0cdc8b2 --- /dev/null +++ b/completions/bash/swayidle @@ -0,0 +1,48 @@ +# swaymsg(1) completion + +_swayidle() +{ + local cur prev + _get_comp_words_by_ref -n : cur prev + local prev2=${COMP_WORDS[COMP_CWORD-2]} + local prev3=${COMP_WORDS[COMP_CWORD-3]} + + events=( + 'timeout' + 'before-sleep' + ) + + short=( + -h + -d + ) + + if [ "$prev" = timeout ]; then + # timeout + return + elif [ "$prev2" = timeout ]; then + # timeout + COMPREPLY=($(compgen -c -- "$cur")) + return + elif [ "$prev3" = timeout ]; then + # timeout [resume ] + COMPREPLY=(resume) + # optional argument; no return here as user may skip 'resume' + fi + + case "$prev" in + resume) + COMPREPLY=($(compgen -c -- "$cur")) + return + ;; + before-sleep) + COMPREPLY=($(compgen -c -- "$cur")) + return + ;; + esac + + COMPREPLY+=($(compgen -W "${events[*]}" -- "$cur")) + COMPREPLY+=($(compgen -W "${short[*]}" -- "$cur")) + +} && +complete -F _swayidle swayidle From b9d531b3181a0877ea3b94d5f9aa5877bbddeb1b Mon Sep 17 00:00:00 2001 From: Eric Engestrom Date: Fri, 27 Jul 2018 17:50:33 +0100 Subject: [PATCH 095/148] meson: install bash completion --- meson.build | 12 ++++++++++++ meson_options.txt | 1 + 2 files changed, 13 insertions(+) diff --git a/meson.build b/meson.build index 1cb40ade..5be1f8d2 100644 --- a/meson.build +++ b/meson.build @@ -191,3 +191,15 @@ if (get_option('zsh_completions')) install_data(zsh_files, install_dir: zsh_install_dir) endif + +if (get_option('bash_completions')) + bash_files = files( + 'completions/bash/sway', + 'completions/bash/swayidle', + 'completions/bash/swaylock', + 'completions/bash/swaymsg', + ) + bash_install_dir = datadir + '/bash-completion/completions' + + install_data(bash_files, install_dir: bash_install_dir) +endif diff --git a/meson_options.txt b/meson_options.txt index 6c7f241d..7a23c206 100644 --- a/meson_options.txt +++ b/meson_options.txt @@ -1,4 +1,5 @@ option('sway_version', type : 'string', description: 'The version string reported in `sway --version`.') option('default_wallpaper', type: 'boolean', value: true, description: 'Install the default wallpaper.') option('zsh_completions', type: 'boolean', value: true, description: 'Install zsh shell completions.') +option('bash_completions', type: 'boolean', value: true, description: 'Install bash shell completions.') option('enable-xwayland', type: 'boolean', value: true, description: 'Enable support for X11 applications') From 52a27f1529973b7058427feddb5de32f23b1c64f Mon Sep 17 00:00:00 2001 From: Eric Engestrom Date: Mon, 30 Jul 2018 16:24:46 +0100 Subject: [PATCH 096/148] delete references to swaygrab --- README.de.md | 2 -- README.el.md | 2 -- README.fr.md | 2 -- README.it.md | 2 -- README.ja.md | 2 -- README.md | 2 -- README.pt.md | 2 -- README.ru.md | 2 -- README.uk.md | 2 -- completions/zsh/_swaygrab | 23 ----------------------- include/swaygrab/json.h | 10 ---------- meson.build | 1 - security.d/00-defaults.in | 6 ------ sway/sway.1.scd | 2 +- swayidle/swayidle.1.scd | 2 +- 15 files changed, 2 insertions(+), 60 deletions(-) delete mode 100644 completions/zsh/_swaygrab delete mode 100644 include/swaygrab/json.h diff --git a/README.de.md b/README.de.md index 206a1040..a872e888 100644 --- a/README.de.md +++ b/README.de.md @@ -64,8 +64,6 @@ Abhängigkeiten: * cairo * gdk-pixbuf2 * * pam ** -* imagemagick (erforderlich für Bildaufnahme mit swaygrab) -* ffmpeg (erforderlich für Videoaufnahme swaygrab) * [scdoc](https://git.sr.ht/~sircmpwn/scdoc) (erforderlich für man pages) _\*Nur erforderlich für swaybar, swaybg, und swaylock_ diff --git a/README.el.md b/README.el.md index 5c70beff..6887fe8e 100644 --- a/README.el.md +++ b/README.el.md @@ -57,8 +57,6 @@ To username μου στο Freenode είναι kon14 και θα με βρείτ * cairo * gdk-pixbuf2 * * pam ** -* imagemagick (αναγκαίο για καταγραφή εικόνας μέσω του swaygrab) -* ffmpeg (αναγκαίο για καταγραφή video μέσω του swaygrab) * [scdoc](https://git.sr.ht/~sircmpwn/scdoc) (required for man pages) _\*Απαιτείται μόνο για swaybar, swaybg, and swaylock_ diff --git a/README.fr.md b/README.fr.md index 0d2573f9..6ea4d14c 100644 --- a/README.fr.md +++ b/README.fr.md @@ -59,8 +59,6 @@ Installez les dépendances : * cairo * gdk-pixbuf2 * * pam ** -* imagemagick (requis pour la capture d'image avec swaygrab) -* ffmpeg (requis pour la capture vidéo avec swaygrab) * [scdoc](https://git.sr.ht/~sircmpwn/scdoc) (requis pour les pages man) _\*Uniquement requis pour swaybar, swaybg, and swaylock_ diff --git a/README.it.md b/README.it.md index 0d81ea54..3b1b1ebc 100644 --- a/README.it.md +++ b/README.it.md @@ -60,8 +60,6 @@ Installa queste dipendenze: * cairo * gdk-pixbuf2 * * pam ** -* imagemagick (richiesto per catturare immagini con swaygrab) -* ffmpeg (rrichiesto per catturare video con swaygrab) * [scdoc](https://git.sr.ht/~sircmpwn/scdoc) (rrichiesto per man pages) _\*Richiesto solo per swaybar, swaybg, e swaylock_ diff --git a/README.ja.md b/README.ja.md index 476d7472..7b437966 100644 --- a/README.ja.md +++ b/README.ja.md @@ -50,8 +50,6 @@ Swayは沢山のディストリビューションで提供されています。" * cairo * gdk-pixbuf2 * * pam ** -* imagemagick (swaygrabでスクリーンショットを撮るのに必要です) -* ffmpeg (swaygrabで画面を録画するのに必要です) * [scdoc](https://git.sr.ht/~sircmpwn/scdoc) (manで必要です) _\*swaybar,swaybg,swaylockでのみ必要です_ diff --git a/README.md b/README.md index 49140fb8..ce753265 100644 --- a/README.md +++ b/README.md @@ -58,8 +58,6 @@ Install dependencies: * gdk-pixbuf2 * * pam ** * dbus >= 1.10 *** -* imagemagick (required for image capture with swaygrab) -* ffmpeg (required for video capture with swaygrab) * [scdoc](https://git.sr.ht/~sircmpwn/scdoc) (required for man pages) _\*Only required for swaybar, swaybg, and swaylock_ diff --git a/README.pt.md b/README.pt.md index d1ef245f..9089c8c6 100644 --- a/README.pt.md +++ b/README.pt.md @@ -66,8 +66,6 @@ Antes de iniciar a compilação, instale as dependências: * cairo * gdk-pixbuf2 * * pam ** -* imagemagick (capturar imagem com o swaygrab) -* ffmpeg (capturar vídeo com o swaygrab) * [scdoc](https://git.sr.ht/~sircmpwn/scdoc) (man pages) _\*Dependência apenas de swaybar, swaybg, e swaylock_ diff --git a/README.ru.md b/README.ru.md index 3b3de19a..68675db3 100644 --- a/README.ru.md +++ b/README.ru.md @@ -62,8 +62,6 @@ Sway доступен во многих дистрибутивах и наход * gdk-pixbuf2 * * pam ** * dbus >= 1.10 *** -* imagemagick (требуется для захвата изображений через swaygrab) -* ffmpeg (требуется для захвата видео через swaygrab) * [scdoc](https://git.sr.ht/~sircmpwn/scdoc) (required for man pages) _\*Требуется только для swaybar, swaybg и swaylock_ diff --git a/README.uk.md b/README.uk.md index 55698487..c31a3ea9 100644 --- a/README.uk.md +++ b/README.uk.md @@ -66,8 +66,6 @@ Sway доступний у багатьох дистрибутивах Linux (а * cairo * gdk-pixbuf2 * * pam ** -* imagemagick (для захоплення зображень за допомогою swaygrab) -* ffmpeg (для захоплення відео за допомогою swaygrab) * [scdoc](https://git.sr.ht/~sircmpwn/scdoc) (required for man pages) _\*Лише для swaybar, swaybg та swaylock_ diff --git a/completions/zsh/_swaygrab b/completions/zsh/_swaygrab deleted file mode 100644 index 0f9846f4..00000000 --- a/completions/zsh/_swaygrab +++ /dev/null @@ -1,23 +0,0 @@ -#compdef swaygrab -#----------------- -# Description -# ----------- -# -# Completion script for swaygrab in sway wm (http://swaywm.org) -# -# ----------------------------------------------------- -# Author -# ------ -# -# * Seth Barberee -# -# ------------------------------------------ - -_arguments -s \ - '(-h --help)'{-h,--help}'[Shows help message]' \ - '(-c --capture)'{-c,--capture}'[Captures multiple frames as video and passes them to ffmpeg]' \ - '(-o --output)'{-o,--output}'[Use the specified output. If not specified then current focused output will be used]' \ - '(-v --version)'{-v,--version}'[Print the version (of swaymsg) and quit]' \ - '(-s --socket)'{-s,--socket}'[Use the specified socket path.]:files:_files' \ - '(-r --rate)'{-r,--rate}'[Specify a framerate (in fps). Used in combination with -c. Default is 30 and must be an integer]' \ - '(--raw)--raw[Instead of ImageMagick or ffmpeg, dump raw rgba data to stdout]' diff --git a/include/swaygrab/json.h b/include/swaygrab/json.h deleted file mode 100644 index c1093ef1..00000000 --- a/include/swaygrab/json.h +++ /dev/null @@ -1,10 +0,0 @@ -#include -#include "wlc/wlc.h" - -void init_json_tree(int socketfd); -void free_json_tree(); -char *get_focused_output(); -char *create_payload(const char *output, struct wlc_geometry *g); -struct wlc_geometry *get_container_geometry(json_object *container); -json_object *get_focused_container(); -json_object *get_output_container(const char *output); diff --git a/meson.build b/meson.build index 1cb40ade..1131f1c9 100644 --- a/meson.build +++ b/meson.build @@ -183,7 +183,6 @@ endif if (get_option('zsh_completions')) zsh_files = files( 'completions/zsh/_sway', - 'completions/zsh/_swaygrab', 'completions/zsh/_swaylock', 'completions/zsh/_swaymsg', ) diff --git a/security.d/00-defaults.in b/security.d/00-defaults.in index e4626477..be7b9d06 100644 --- a/security.d/00-defaults.in +++ b/security.d/00-defaults.in @@ -12,7 +12,6 @@ permit * fullscreen keyboard mouse permit @prefix@/bin/swaylock lock permit @prefix@/bin/swaybg background -permit @prefix@/bin/swaygrab screenshot permit @prefix@/bin/swaybar panel # Configures enabled IPC features for specific programs @@ -36,11 +35,6 @@ ipc @prefix@/bin/swaybar { } } -ipc @prefix@/bin/swaygrab { - outputs enabled - tree enabled -} - ipc @prefix@/bin/swaylock { outputs enabled } diff --git a/sway/sway.1.scd b/sway/sway.1.scd index 5b770cce..0c2ee974 100644 --- a/sway/sway.1.scd +++ b/sway/sway.1.scd @@ -92,4 +92,4 @@ source contributors. For more information about sway development, see # SEE ALSO -*sway*(5) *swaymsg*(1) *swaygrab*(1) *sway-input*(5) *sway-bar*(5) +*sway*(5) *swaymsg*(1) *sway-input*(5) *sway-bar*(5) diff --git a/swayidle/swayidle.1.scd b/swayidle/swayidle.1.scd index 5cd4a7fd..7c1b138a 100644 --- a/swayidle/swayidle.1.scd +++ b/swayidle/swayidle.1.scd @@ -58,4 +58,4 @@ https://github.com/swaywm/sway. # SEE ALSO -*sway*(5) *swaymsg*(1) *swaygrab*(1) *sway-input*(5) *sway-bar*(5) +*sway*(5) *swaymsg*(1) *sway-input*(5) *sway-bar*(5) From 15c19572cf97d3386ed3b2f9154dd752ce0b0b49 Mon Sep 17 00:00:00 2001 From: emersion Date: Mon, 30 Jul 2018 19:44:07 +0100 Subject: [PATCH 097/148] Fix segfault with fullscreen --- sway/desktop/output.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/sway/desktop/output.c b/sway/desktop/output.c index 6b2eb0c2..31b53213 100644 --- a/sway/desktop/output.c +++ b/sway/desktop/output.c @@ -317,8 +317,9 @@ static void send_frame_done(struct sway_output *output, struct timespec *when) { struct sway_container *workspace = output_get_active_workspace(output); if (workspace->current.ws_fullscreen) { if (workspace->current.ws_fullscreen->type == C_VIEW) { - send_frame_done_container_iterator( - workspace->current.ws_fullscreen, when); + output_view_for_each_surface(output, + workspace->current.ws_fullscreen->sway_view, + send_frame_done_iterator, when); } else { send_frame_done_container(output, workspace->current.ws_fullscreen, when); From de86d65627e96cffe77f4abf11c4a0b982326ff9 Mon Sep 17 00:00:00 2001 From: Ryan Dwyer Date: Tue, 31 Jul 2018 18:41:30 +1000 Subject: [PATCH 098/148] Fix popups Fixes the render and container_at order for popups. Fixes #2210 For rendering: * render_view_surfaces has been renamed to render_view_toplevels * render_view_toplevels now uses output_surface_for_each_surface (which is now public), as that function uses wlr_surface_for_each_surface which doesn't descend into popups * Views now have a for_each_popup iterator, which is used by the renderer to render the focused view's popups * When rendering a popup, toplevels (xdg subsurfaces) of that popup are also rendered For sending frame done, the logic has been updated to match the rendering logic: * send_frame_done_container no longer descends into popups * for_each_popup is used to send frame done to the focused view's popups and their child toplevels For container_at: * floating_container_at is now static, which means it had to be moved higher in the file. * container_at now considers popups for the focused view before checking containers. * tiling_container_at has been introduced, so that it doesn't call container_at recursively (it would check popups recursively if it did) --- include/sway/output.h | 8 ++ include/sway/tree/container.h | 11 +-- include/sway/tree/view.h | 11 +++ sway/desktop/output.c | 52 ++++++++++++- sway/desktop/render.c | 37 +++++++++- sway/desktop/xdg_shell.c | 9 +++ sway/desktop/xdg_shell_v6.c | 10 +++ sway/input/cursor.c | 3 - sway/tree/container.c | 134 ++++++++++++++++++++++++---------- sway/tree/view.c | 10 +++ 10 files changed, 228 insertions(+), 57 deletions(-) diff --git a/include/sway/output.h b/include/sway/output.h index 6283db68..80dcd37b 100644 --- a/include/sway/output.h +++ b/include/sway/output.h @@ -67,10 +67,18 @@ struct sway_container *output_get_active_workspace(struct sway_output *output); void output_render(struct sway_output *output, struct timespec *when, pixman_region32_t *damage); +void output_surface_for_each_surface(struct sway_output *output, + struct wlr_surface *surface, double ox, double oy, + sway_surface_iterator_func_t iterator, void *user_data); + void output_view_for_each_surface(struct sway_output *output, struct sway_view *view, sway_surface_iterator_func_t iterator, void *user_data); +void output_view_for_each_popup(struct sway_output *output, + struct sway_view *view, sway_surface_iterator_func_t iterator, + void *user_data); + void output_layer_for_each_surface(struct sway_output *output, struct wl_list *layer_surfaces, sway_surface_iterator_func_t iterator, void *user_data); diff --git a/include/sway/tree/container.h b/include/sway/tree/container.h index d4a42a71..12ff8a5a 100644 --- a/include/sway/tree/container.h +++ b/include/sway/tree/container.h @@ -230,17 +230,10 @@ struct sway_container *container_parent(struct sway_container *container, * surface-local coordinates of the given layout coordinates if the container * is a view and the view contains a surface at those coordinates. */ -struct sway_container *container_at(struct sway_container *container, - double ox, double oy, struct wlr_surface **surface, +struct sway_container *container_at(struct sway_container *workspace, + double lx, double ly, struct wlr_surface **surface, double *sx, double *sy); -/** - * Same as container_at, but only checks floating views and expects coordinates - * to be layout coordinates, as that's what floating views use. - */ -struct sway_container *floating_container_at(double lx, double ly, - struct wlr_surface **surface, double *sx, double *sy); - /** * Apply the function for each descendant of the container breadth first. */ diff --git a/include/sway/tree/view.h b/include/sway/tree/view.h index 0152ed55..9f6d36fe 100644 --- a/include/sway/tree/view.h +++ b/include/sway/tree/view.h @@ -47,6 +47,8 @@ struct sway_view_impl { bool (*has_client_side_decorations)(struct sway_view *view); void (*for_each_surface)(struct sway_view *view, wlr_surface_iterator_func_t iterator, void *user_data); + void (*for_each_popup)(struct sway_view *view, + wlr_surface_iterator_func_t iterator, void *user_data); void (*close)(struct sway_view *view); void (*destroy)(struct sway_view *view); }; @@ -248,9 +250,18 @@ void view_close(struct sway_view *view); void view_damage_from(struct sway_view *view); +/** + * Iterate all surfaces of a view (toplevels + popups). + */ void view_for_each_surface(struct sway_view *view, wlr_surface_iterator_func_t iterator, void *user_data); +/** + * Iterate all popups recursively. + */ +void view_for_each_popup(struct sway_view *view, + wlr_surface_iterator_func_t iterator, void *user_data); + // view implementation void view_init(struct sway_view *view, enum sway_view_type type, diff --git a/sway/desktop/output.c b/sway/desktop/output.c index 31b53213..4c9d978c 100644 --- a/sway/desktop/output.c +++ b/sway/desktop/output.c @@ -119,7 +119,7 @@ static void output_for_each_surface_iterator(struct wlr_surface *surface, data->user_data); } -static void output_surface_for_each_surface(struct sway_output *output, +void output_surface_for_each_surface(struct sway_output *output, struct wlr_surface *surface, double ox, double oy, sway_surface_iterator_func_t iterator, void *user_data) { struct surface_iterator_data data = { @@ -155,6 +155,23 @@ void output_view_for_each_surface(struct sway_output *output, output_for_each_surface_iterator, &data); } +void output_view_for_each_popup(struct sway_output *output, + struct sway_view *view, sway_surface_iterator_func_t iterator, + void *user_data) { + struct surface_iterator_data data = { + .user_iterator = iterator, + .user_data = user_data, + .output = output, + .ox = view->swayc->current.view_x - output->swayc->current.swayc_x, + .oy = view->swayc->current.view_y - output->swayc->current.swayc_y, + .width = view->swayc->current.view_width, + .height = view->swayc->current.view_height, + .rotation = 0, // TODO + }; + + view_for_each_popup(view, output_for_each_surface_iterator, &data); +} + void output_layer_for_each_surface(struct sway_output *output, struct wl_list *layer_surfaces, sway_surface_iterator_func_t iterator, void *user_data) { @@ -295,8 +312,9 @@ static void send_frame_done_container_iterator(struct sway_container *con, return; } - output_view_for_each_surface(data->output, con->sway_view, - send_frame_done_iterator, data->when); + // Toplevels only + output_surface_for_each_surface(data->output, con->sway_view->surface, + con->x, con->y, send_frame_done_iterator, data->when); } static void send_frame_done_container(struct sway_output *output, @@ -309,6 +327,27 @@ static void send_frame_done_container(struct sway_output *output, send_frame_done_container_iterator, &data); } +static void send_frame_done_popup_iterator(struct sway_output *output, + struct wlr_surface *surface, struct wlr_box *box, float rotation, + void *data) { + // Send frame done to this popup's surface + send_frame_done_iterator(output, surface, box, rotation, data); + + // Send frame done to this popup's child toplevels + output_surface_for_each_surface(output, surface, box->x, box->y, + send_frame_done_iterator, data); +} + +static void send_frame_done_popups(struct sway_output *output, + struct sway_view *view, struct timespec *when) { + struct send_frame_done_data data = { + .output = output, + .when = when, + }; + output_view_for_each_popup(output, view, + send_frame_done_popup_iterator, &data); +} + static void send_frame_done(struct sway_output *output, struct timespec *when) { if (output_has_opaque_overlay_layer_surface(output)) { goto send_frame_overlay; @@ -346,6 +385,13 @@ static void send_frame_done(struct sway_output *output, struct timespec *when) { &output->layers[ZWLR_LAYER_SHELL_V1_LAYER_TOP], when); } + struct sway_seat *seat = input_manager_current_seat(input_manager); + struct sway_container *focus = seat_get_focus(seat); + if (focus && focus->type == C_VIEW) { + send_frame_done_popups(output, focus->sway_view, when); + } + + send_frame_overlay: send_frame_done_layer(output, &output->layers[ZWLR_LAYER_SHELL_V1_LAYER_OVERLAY], when); diff --git a/sway/desktop/render.c b/sway/desktop/render.c index f25055b8..ea4361f2 100644 --- a/sway/desktop/render.c +++ b/sway/desktop/render.c @@ -186,13 +186,36 @@ static void premultiply_alpha(float color[4], float opacity) { color[2] *= color[3]; } -static void render_view_surfaces(struct sway_view *view, +static void render_view_toplevels(struct sway_view *view, struct sway_output *output, pixman_region32_t *damage, float alpha) { struct render_data data = { .damage = damage, .alpha = alpha, }; - output_view_for_each_surface(output, view, render_surface_iterator, &data); + // Render all toplevels without descending into popups + output_surface_for_each_surface(output, view->surface, + view->swayc->current.view_x, view->swayc->current.view_y, + render_surface_iterator, &data); +} + +static void render_popup_iterator(struct sway_output *output, + struct wlr_surface *surface, struct wlr_box *box, float rotation, + void *data) { + // Render this popup's surface + render_surface_iterator(output, surface, box, rotation, data); + + // Render this popup's child toplevels + output_surface_for_each_surface(output, surface, box->x, box->y, + render_surface_iterator, data); +} + +static void render_view_popups(struct sway_view *view, + struct sway_output *output, pixman_region32_t *damage, float alpha) { + struct render_data data = { + .damage = damage, + .alpha = alpha, + }; + output_view_for_each_popup(output, view, render_popup_iterator, &data); } static void render_saved_view(struct sway_view *view, @@ -241,7 +264,7 @@ static void render_view(struct sway_output *output, pixman_region32_t *damage, if (view->swayc->instructions->length) { render_saved_view(view, output, damage, view->swayc->alpha); } else { - render_view_surfaces(view, output, damage, view->swayc->alpha); + render_view_toplevels(view, output, damage, view->swayc->alpha); } if (view->using_csd) { @@ -845,7 +868,7 @@ void output_render(struct sway_output *output, struct timespec *when, render_saved_view(fullscreen_con->sway_view, output, damage, 1.0f); } else { - render_view_surfaces(fullscreen_con->sway_view, + render_view_toplevels(fullscreen_con->sway_view, output, damage, 1.0f); } } else { @@ -881,6 +904,12 @@ void output_render(struct sway_output *output, struct timespec *when, &output->layers[ZWLR_LAYER_SHELL_V1_LAYER_TOP]); } + struct sway_seat *seat = input_manager_current_seat(input_manager); + struct sway_container *focus = seat_get_focus(seat); + if (focus && focus->type == C_VIEW) { + render_view_popups(focus->sway_view, output, damage, focus->alpha); + } + render_overlay: render_layer(output, damage, &output->layers[ZWLR_LAYER_SHELL_V1_LAYER_OVERLAY]); diff --git a/sway/desktop/xdg_shell.c b/sway/desktop/xdg_shell.c index e6e1527e..9f94bd74 100644 --- a/sway/desktop/xdg_shell.c +++ b/sway/desktop/xdg_shell.c @@ -179,6 +179,14 @@ static void for_each_surface(struct sway_view *view, user_data); } +static void for_each_popup(struct sway_view *view, + wlr_surface_iterator_func_t iterator, void *user_data) { + if (xdg_shell_view_from_view(view) == NULL) { + return; + } + wlr_xdg_surface_for_each_popup(view->wlr_xdg_surface, iterator, user_data); +} + static void _close(struct sway_view *view) { if (xdg_shell_view_from_view(view) == NULL) { return; @@ -207,6 +215,7 @@ static const struct sway_view_impl view_impl = { .set_fullscreen = set_fullscreen, .wants_floating = wants_floating, .for_each_surface = for_each_surface, + .for_each_popup = for_each_popup, .close = _close, .destroy = destroy, }; diff --git a/sway/desktop/xdg_shell_v6.c b/sway/desktop/xdg_shell_v6.c index 5feee3e4..4502c386 100644 --- a/sway/desktop/xdg_shell_v6.c +++ b/sway/desktop/xdg_shell_v6.c @@ -175,6 +175,15 @@ static void for_each_surface(struct sway_view *view, user_data); } +static void for_each_popup(struct sway_view *view, + wlr_surface_iterator_func_t iterator, void *user_data) { + if (xdg_shell_v6_view_from_view(view) == NULL) { + return; + } + wlr_xdg_surface_v6_for_each_popup(view->wlr_xdg_surface_v6, iterator, + user_data); +} + static void _close(struct sway_view *view) { if (xdg_shell_v6_view_from_view(view) == NULL) { return; @@ -203,6 +212,7 @@ static const struct sway_view_impl view_impl = { .set_fullscreen = set_fullscreen, .wants_floating = wants_floating, .for_each_surface = for_each_surface, + .for_each_popup = for_each_popup, .close = _close, .destroy = destroy, }; diff --git a/sway/input/cursor.c b/sway/input/cursor.c index 96ac7b33..ad4b1718 100644 --- a/sway/input/cursor.c +++ b/sway/input/cursor.c @@ -109,9 +109,6 @@ static struct sway_container *container_at_coords( } struct sway_container *c; - if ((c = floating_container_at(lx, ly, surface, sx, sy))) { - return c; - } if ((c = container_at(ws, lx, ly, surface, sx, sy))) { return c; } diff --git a/sway/tree/container.c b/sway/tree/container.c index 4e85021d..b5fefd17 100644 --- a/sway/tree/container.c +++ b/sway/tree/container.c @@ -561,10 +561,15 @@ static struct sway_container *container_at_view(struct sway_container *swayc, *sx = _sx; *sy = _sy; *surface = _surface; + return swayc; } - return swayc; + return NULL; } +static struct sway_container *tiling_container_at( + struct sway_container *con, double lx, double ly, + struct wlr_surface **surface, double *sx, double *sy); + /** * container_at for a container with layout L_TABBED. */ @@ -591,7 +596,7 @@ static struct sway_container *container_at_tabbed(struct sway_container *parent, // Surfaces struct sway_container *current = seat_get_active_child(seat, parent); - return container_at(current, lx, ly, surface, sx, sy); + return tiling_container_at(current, lx, ly, surface, sx, sy); } /** @@ -616,7 +621,7 @@ static struct sway_container *container_at_stacked( // Surfaces struct sway_container *current = seat_get_active_child(seat, parent); - return container_at(current, lx, ly, surface, sx, sy); + return tiling_container_at(current, lx, ly, surface, sx, sy); } /** @@ -634,45 +639,13 @@ static struct sway_container *container_at_linear(struct sway_container *parent, .height = child->height, }; if (wlr_box_contains_point(&box, lx, ly)) { - return container_at(child, lx, ly, surface, sx, sy); + return tiling_container_at(child, lx, ly, surface, sx, sy); } } return NULL; } -struct sway_container *container_at(struct sway_container *parent, - double lx, double ly, - struct wlr_surface **surface, double *sx, double *sy) { - if (!sway_assert(parent->type >= C_WORKSPACE, - "Expected workspace or deeper")) { - return NULL; - } - if (parent->type == C_VIEW) { - return container_at_view(parent, lx, ly, surface, sx, sy); - } - if (!parent->children->length) { - return NULL; - } - - switch (parent->layout) { - case L_HORIZ: - case L_VERT: - return container_at_linear(parent, lx, ly, surface, sx, sy); - case L_TABBED: - return container_at_tabbed(parent, lx, ly, surface, sx, sy); - case L_STACKED: - return container_at_stacked(parent, lx, ly, surface, sx, sy); - case L_FLOATING: - sway_assert(false, "Didn't expect to see floating here"); - return NULL; - case L_NONE: - return NULL; - } - - return NULL; -} - -struct sway_container *floating_container_at(double lx, double ly, +static struct sway_container *floating_container_at(double lx, double ly, struct wlr_surface **surface, double *sx, double *sy) { for (int i = 0; i < root_container.children->length; ++i) { struct sway_container *output = root_container.children->items[i]; @@ -694,7 +667,8 @@ struct sway_container *floating_container_at(double lx, double ly, .height = floater->height, }; if (wlr_box_contains_point(&box, lx, ly)) { - return container_at(floater, lx, ly, surface, sx, sy); + return tiling_container_at(floater, lx, ly, + surface, sx, sy); } } } @@ -702,6 +676,90 @@ struct sway_container *floating_container_at(double lx, double ly, return NULL; } +static struct sway_container *tiling_container_at( + struct sway_container *con, double lx, double ly, + struct wlr_surface **surface, double *sx, double *sy) { + if (con->type == C_VIEW) { + return container_at_view(con, lx, ly, surface, sx, sy); + } + if (!con->children->length) { + return NULL; + } + + switch (con->layout) { + case L_HORIZ: + case L_VERT: + return container_at_linear(con, lx, ly, surface, sx, sy); + case L_TABBED: + return container_at_tabbed(con, lx, ly, surface, sx, sy); + case L_STACKED: + return container_at_stacked(con, lx, ly, surface, sx, sy); + case L_FLOATING: + sway_assert(false, "Didn't expect to see floating here"); + return NULL; + case L_NONE: + return NULL; + } + return NULL; +} + +static bool surface_is_popup(struct wlr_surface *surface) { + if (wlr_surface_is_xdg_surface(surface)) { + struct wlr_xdg_surface *xdg_surface = + wlr_xdg_surface_from_wlr_surface(surface); + while (xdg_surface) { + if (xdg_surface->role == WLR_XDG_SURFACE_ROLE_POPUP) { + return true; + } + xdg_surface = xdg_surface->toplevel->parent; + } + return false; + } + + if (wlr_surface_is_xdg_surface_v6(surface)) { + struct wlr_xdg_surface_v6 *xdg_surface_v6 = + wlr_xdg_surface_v6_from_wlr_surface(surface); + while (xdg_surface_v6) { + if (xdg_surface_v6->role == WLR_XDG_SURFACE_V6_ROLE_POPUP) { + return true; + } + xdg_surface_v6 = xdg_surface_v6->toplevel->parent; + } + return false; + } + + return false; +} + +struct sway_container *container_at(struct sway_container *workspace, + double lx, double ly, + struct wlr_surface **surface, double *sx, double *sy) { + if (!sway_assert(workspace->type == C_WORKSPACE, "Expected a workspace")) { + return NULL; + } + struct sway_container *c; + // Focused view's popups + struct sway_seat *seat = input_manager_current_seat(input_manager); + struct sway_container *focus = + seat_get_focus_inactive(seat, &root_container); + if (focus && focus->type == C_VIEW) { + container_at_view(focus, lx, ly, surface, sx, sy); + if (*surface && surface_is_popup(*surface)) { + return focus; + } + *surface = NULL; + } + // Floating + if ((c = floating_container_at(lx, ly, surface, sx, sy))) { + return c; + } + // Tiling + if ((c = tiling_container_at(workspace, lx, ly, surface, sx, sy))) { + return c; + } + return NULL; +} + void container_for_each_descendant_dfs(struct sway_container *container, void (*f)(struct sway_container *container, void *data), void *data) { diff --git a/sway/tree/view.c b/sway/tree/view.c index 8f54cc11..c1207821 100644 --- a/sway/tree/view.c +++ b/sway/tree/view.c @@ -332,6 +332,16 @@ void view_for_each_surface(struct sway_view *view, } } +void view_for_each_popup(struct sway_view *view, + wlr_surface_iterator_func_t iterator, void *user_data) { + if (!view->surface) { + return; + } + if (view->impl->for_each_popup) { + view->impl->for_each_popup(view, iterator, user_data); + } +} + static void view_subsurface_create(struct sway_view *view, struct wlr_subsurface *subsurface); From 7a59508da467a3b793e355e28ae67ce04633761c Mon Sep 17 00:00:00 2001 From: Ryan Dwyer Date: Tue, 31 Jul 2018 19:58:34 +1000 Subject: [PATCH 099/148] Close popups when changing focus Also reverts the send frame done changes from the previous commit. --- include/sway/tree/view.h | 3 +++ sway/desktop/output.c | 33 ++------------------------------- sway/desktop/xdg_shell.c | 13 +++++++++++++ sway/desktop/xdg_shell_v6.c | 13 +++++++++++++ sway/input/seat.c | 7 +++++++ sway/tree/view.c | 6 ++++++ 6 files changed, 44 insertions(+), 31 deletions(-) diff --git a/include/sway/tree/view.h b/include/sway/tree/view.h index 9f6d36fe..e722ca5e 100644 --- a/include/sway/tree/view.h +++ b/include/sway/tree/view.h @@ -50,6 +50,7 @@ struct sway_view_impl { void (*for_each_popup)(struct sway_view *view, wlr_surface_iterator_func_t iterator, void *user_data); void (*close)(struct sway_view *view); + void (*close_popups)(struct sway_view *view); void (*destroy)(struct sway_view *view); }; @@ -248,6 +249,8 @@ void view_set_tiled(struct sway_view *view, bool tiled); void view_close(struct sway_view *view); +void view_close_popups(struct sway_view *view); + void view_damage_from(struct sway_view *view); /** diff --git a/sway/desktop/output.c b/sway/desktop/output.c index 4c9d978c..66747a3f 100644 --- a/sway/desktop/output.c +++ b/sway/desktop/output.c @@ -312,9 +312,8 @@ static void send_frame_done_container_iterator(struct sway_container *con, return; } - // Toplevels only - output_surface_for_each_surface(data->output, con->sway_view->surface, - con->x, con->y, send_frame_done_iterator, data->when); + output_view_for_each_surface(data->output, con->sway_view, + send_frame_done_iterator, data->when); } static void send_frame_done_container(struct sway_output *output, @@ -327,27 +326,6 @@ static void send_frame_done_container(struct sway_output *output, send_frame_done_container_iterator, &data); } -static void send_frame_done_popup_iterator(struct sway_output *output, - struct wlr_surface *surface, struct wlr_box *box, float rotation, - void *data) { - // Send frame done to this popup's surface - send_frame_done_iterator(output, surface, box, rotation, data); - - // Send frame done to this popup's child toplevels - output_surface_for_each_surface(output, surface, box->x, box->y, - send_frame_done_iterator, data); -} - -static void send_frame_done_popups(struct sway_output *output, - struct sway_view *view, struct timespec *when) { - struct send_frame_done_data data = { - .output = output, - .when = when, - }; - output_view_for_each_popup(output, view, - send_frame_done_popup_iterator, &data); -} - static void send_frame_done(struct sway_output *output, struct timespec *when) { if (output_has_opaque_overlay_layer_surface(output)) { goto send_frame_overlay; @@ -385,13 +363,6 @@ static void send_frame_done(struct sway_output *output, struct timespec *when) { &output->layers[ZWLR_LAYER_SHELL_V1_LAYER_TOP], when); } - struct sway_seat *seat = input_manager_current_seat(input_manager); - struct sway_container *focus = seat_get_focus(seat); - if (focus && focus->type == C_VIEW) { - send_frame_done_popups(output, focus->sway_view, when); - } - - send_frame_overlay: send_frame_done_layer(output, &output->layers[ZWLR_LAYER_SHELL_V1_LAYER_OVERLAY], when); diff --git a/sway/desktop/xdg_shell.c b/sway/desktop/xdg_shell.c index 9f94bd74..b364663d 100644 --- a/sway/desktop/xdg_shell.c +++ b/sway/desktop/xdg_shell.c @@ -197,6 +197,18 @@ static void _close(struct sway_view *view) { } } +static void close_popups_iterator(struct wlr_surface *surface, + int sx, int sy, void *data) { + struct wlr_xdg_surface *xdg_surface = + wlr_xdg_surface_from_wlr_surface(surface); + wlr_xdg_surface_send_close(xdg_surface); +} + +static void close_popups(struct sway_view *view) { + wlr_xdg_surface_for_each_popup(view->wlr_xdg_surface, + close_popups_iterator, NULL); +} + static void destroy(struct sway_view *view) { struct sway_xdg_shell_view *xdg_shell_view = xdg_shell_view_from_view(view); @@ -217,6 +229,7 @@ static const struct sway_view_impl view_impl = { .for_each_surface = for_each_surface, .for_each_popup = for_each_popup, .close = _close, + .close_popups = close_popups, .destroy = destroy, }; diff --git a/sway/desktop/xdg_shell_v6.c b/sway/desktop/xdg_shell_v6.c index 4502c386..ffea03ad 100644 --- a/sway/desktop/xdg_shell_v6.c +++ b/sway/desktop/xdg_shell_v6.c @@ -194,6 +194,18 @@ static void _close(struct sway_view *view) { } } +static void close_popups_iterator(struct wlr_surface *surface, + int sx, int sy, void *data) { + struct wlr_xdg_surface_v6 *xdg_surface_v6 = + wlr_xdg_surface_v6_from_wlr_surface(surface); + wlr_xdg_surface_v6_send_close(xdg_surface_v6); +} + +static void close_popups(struct sway_view *view) { + wlr_xdg_surface_v6_for_each_popup(view->wlr_xdg_surface_v6, + close_popups_iterator, NULL); +} + static void destroy(struct sway_view *view) { struct sway_xdg_shell_v6_view *xdg_shell_v6_view = xdg_shell_v6_view_from_view(view); @@ -214,6 +226,7 @@ static const struct sway_view_impl view_impl = { .for_each_surface = for_each_surface, .for_each_popup = for_each_popup, .close = _close, + .close_popups = close_popups, .destroy = destroy, }; diff --git a/sway/input/seat.c b/sway/input/seat.c index 53a92989..8ed0dce2 100644 --- a/sway/input/seat.c +++ b/sway/input/seat.c @@ -737,6 +737,13 @@ void seat_set_focus_warp(struct sway_seat *seat, } } + // Close any popups on the old focus + if (last_focus && last_focus != container) { + if (last_focus->type == C_VIEW) { + view_close_popups(last_focus->sway_view); + } + } + if (last_focus) { if (last_workspace) { ipc_event_workspace(last_workspace, container, "focus"); diff --git a/sway/tree/view.c b/sway/tree/view.c index c1207821..5d9b625f 100644 --- a/sway/tree/view.c +++ b/sway/tree/view.c @@ -302,6 +302,12 @@ void view_close(struct sway_view *view) { } } +void view_close_popups(struct sway_view *view) { + if (view->impl->close_popups) { + view->impl->close_popups(view); + } +} + void view_damage_from(struct sway_view *view) { for (int i = 0; i < root_container.children->length; ++i) { struct sway_container *cont = root_container.children->items[i]; From 00a00aacef7e9995d4cd1177ce1258a96f56d5c9 Mon Sep 17 00:00:00 2001 From: Brian Ashworth Date: Tue, 31 Jul 2018 23:49:42 -0400 Subject: [PATCH 100/148] Create list for mouse binds when creating new mode --- sway/commands/mode.c | 1 + 1 file changed, 1 insertion(+) diff --git a/sway/commands/mode.c b/sway/commands/mode.c index b460fcb5..637ca45e 100644 --- a/sway/commands/mode.c +++ b/sway/commands/mode.c @@ -56,6 +56,7 @@ struct cmd_results *cmd_mode(int argc, char **argv) { mode->name = strdup(mode_name); mode->keysym_bindings = create_list(); mode->keycode_bindings = create_list(); + mode->mouse_bindings = create_list(); mode->pango = pango; list_add(config->modes, mode); } From d10ccc1eb144e4de2477398f6b11753f6b7df70b Mon Sep 17 00:00:00 2001 From: Ryan Dwyer Date: Wed, 1 Aug 2018 16:23:11 +1000 Subject: [PATCH 101/148] Correctly track saved surfaces during multiple transactions Fixes #2364. Suppose a view is 600px wide, and we tell it to resize to 601px during a resize operation. We create a transaction, save the 600px buffer and send the configure. This buffer is saved into the associated instruction, and is rendered while we wait for the view to commit a 601px buffer. Before the view commits the 601px buffer, suppose we tell it to resize to 602px. The new transaction will also save the buffer, but it's still the 600px buffer because we haven't received a new one yet. Then suppose the view commits its original 601px buffer. This completes the first transaction, so we apply the 601px width to the container. There's still the second (now only) transaction remaining, so we render the saved buffer from that. But this is still the 600px buffer, and we believe it's 601px. Whoops. The problem here is we can't stack buffers like this. So this commit removes the saved buffer from the instructions, places it in the view instead, and re-saves the latest buffer every time the view completes a transaction and still has further pending transactions. As saved buffers are now specific to views rather than instructions, the functions for saving and removing the saved buffer have been moved to view.c. The calls to save and restore the buffer have been relocated to more appropriate functions too, favouring transaction_commit and transaction_apply rather than transaction_add_container and transaction_destroy. --- include/sway/desktop/transaction.h | 13 -------- include/sway/tree/view.h | 7 ++++ sway/desktop/render.c | 16 ++++------ sway/desktop/transaction.c | 51 +++++++----------------------- sway/tree/view.c | 20 ++++++++++++ 5 files changed, 46 insertions(+), 61 deletions(-) diff --git a/include/sway/desktop/transaction.h b/include/sway/desktop/transaction.h index cee4afed..56361d94 100644 --- a/include/sway/desktop/transaction.h +++ b/include/sway/desktop/transaction.h @@ -42,17 +42,4 @@ void transaction_notify_view_ready(struct sway_view *view, uint32_t serial); void transaction_notify_view_ready_by_size(struct sway_view *view, int width, int height); -/** - * Get the saved texture that should be rendered for a view. - * - * The addresses pointed at by the width and height pointers will be populated - * with the surface's dimensions, which may be different to the texture's - * dimensions if output scaling is used. - * - * This function should only be called if it is known that the view has - * instructions. - */ -struct wlr_texture *transaction_get_saved_texture(struct sway_view *view, - int *width, int *height); - #endif diff --git a/include/sway/tree/view.h b/include/sway/tree/view.h index 0152ed55..0f9b0bb2 100644 --- a/include/sway/tree/view.h +++ b/include/sway/tree/view.h @@ -82,6 +82,9 @@ struct sway_view { bool allow_request_urgent; struct wl_event_source *urgent_timer; + struct wlr_buffer *saved_buffer; + int saved_buffer_width, saved_buffer_height; + bool destroying; list_t *executed_criteria; // struct criteria * @@ -323,4 +326,8 @@ void view_set_urgent(struct sway_view *view, bool enable); bool view_is_urgent(struct sway_view *view); +void view_remove_saved_buffer(struct sway_view *view); + +void view_save_buffer(struct sway_view *view); + #endif diff --git a/sway/desktop/render.c b/sway/desktop/render.c index f25055b8..f0e47c95 100644 --- a/sway/desktop/render.c +++ b/sway/desktop/render.c @@ -199,17 +199,14 @@ static void render_saved_view(struct sway_view *view, struct sway_output *output, pixman_region32_t *damage, float alpha) { struct wlr_output *wlr_output = output->wlr_output; - int width, height; - struct wlr_texture *texture = - transaction_get_saved_texture(view, &width, &height); - if (!texture) { + if (!view->saved_buffer || !view->saved_buffer->texture) { return; } struct wlr_box box = { .x = view->swayc->current.view_x - output->swayc->current.swayc_x, .y = view->swayc->current.view_y - output->swayc->current.swayc_y, - .width = width, - .height = height, + .width = view->saved_buffer_width, + .height = view->saved_buffer_height, }; struct wlr_box output_box = { @@ -229,7 +226,8 @@ static void render_saved_view(struct sway_view *view, wlr_matrix_project_box(matrix, &box, WL_OUTPUT_TRANSFORM_NORMAL, 0, wlr_output->transform_matrix); - render_texture(wlr_output, damage, texture, &box, matrix, alpha); + render_texture(wlr_output, damage, view->saved_buffer->texture, + &box, matrix, alpha); } /** @@ -238,7 +236,7 @@ static void render_saved_view(struct sway_view *view, static void render_view(struct sway_output *output, pixman_region32_t *damage, struct sway_container *con, struct border_colors *colors) { struct sway_view *view = con->sway_view; - if (view->swayc->instructions->length) { + if (view->saved_buffer) { render_saved_view(view, output, damage, view->swayc->alpha); } else { render_view_surfaces(view, output, damage, view->swayc->alpha); @@ -841,7 +839,7 @@ void output_render(struct sway_output *output, struct timespec *when, // TODO: handle views smaller than the output if (fullscreen_con->type == C_VIEW) { - if (fullscreen_con->instructions->length) { + if (fullscreen_con->sway_view->saved_buffer) { render_saved_view(fullscreen_con->sway_view, output, damage, 1.0f); } else { diff --git a/sway/desktop/transaction.c b/sway/desktop/transaction.c index 7975366e..94070363 100644 --- a/sway/desktop/transaction.c +++ b/sway/desktop/transaction.c @@ -41,8 +41,6 @@ struct sway_transaction_instruction { struct sway_transaction *transaction; struct sway_container *container; struct sway_container_state state; - struct wlr_buffer *saved_buffer; - int saved_buffer_width, saved_buffer_height; uint32_t serial; bool ready; }; @@ -57,27 +55,6 @@ static struct sway_transaction *transaction_create() { return transaction; } -static void remove_saved_view_buffer( - struct sway_transaction_instruction *instruction) { - if (instruction->saved_buffer) { - wlr_buffer_unref(instruction->saved_buffer); - instruction->saved_buffer = NULL; - } -} - -static void save_view_buffer(struct sway_view *view, - struct sway_transaction_instruction *instruction) { - if (!sway_assert(instruction->saved_buffer == NULL, - "Didn't expect instruction to have a saved buffer already")) { - remove_saved_view_buffer(instruction); - } - if (view->surface && wlr_surface_has_buffer(view->surface)) { - instruction->saved_buffer = wlr_buffer_ref(view->surface->buffer); - instruction->saved_buffer_width = view->surface->current.width; - instruction->saved_buffer_height = view->surface->current.height; - } -} - static void transaction_destroy(struct sway_transaction *transaction) { // Free instructions for (int i = 0; i < transaction->instructions->length; ++i) { @@ -93,7 +70,6 @@ static void transaction_destroy(struct sway_transaction *transaction) { if (con->destroying && !con->instructions->length) { container_free(con); } - remove_saved_view_buffer(instruction); free(instruction); } list_free(transaction->instructions); @@ -158,9 +134,6 @@ static void transaction_add_container(struct sway_transaction *transaction, copy_pending_state(container, &instruction->state); - if (container->type == C_VIEW) { - save_view_buffer(container->sway_view, instruction); - } list_add(transaction->instructions, instruction); } @@ -220,6 +193,15 @@ static void transaction_apply(struct sway_transaction *transaction) { memcpy(&container->current, &instruction->state, sizeof(struct sway_container_state)); + + if (container->type == C_VIEW) { + if (container->sway_view->saved_buffer) { + view_remove_saved_buffer(container->sway_view); + } + if (container->instructions->length > 1) { + view_save_buffer(container->sway_view); + } + } } } @@ -294,6 +276,9 @@ static void transaction_commit(struct sway_transaction *transaction) { // mapping and its default geometry doesn't intersect an output. struct timespec when; wlr_surface_send_frame_done(con->sway_view->surface, &when); + if (!con->sway_view->saved_buffer) { + view_save_buffer(con->sway_view); + } } list_add(con->instructions, instruction); } @@ -400,18 +385,6 @@ void transaction_notify_view_ready_by_size(struct sway_view *view, } } -struct wlr_texture *transaction_get_saved_texture(struct sway_view *view, - int *width, int *height) { - struct sway_transaction_instruction *instruction = - view->swayc->instructions->items[0]; - if (!instruction->saved_buffer) { - return NULL; - } - *width = instruction->saved_buffer_width; - *height = instruction->saved_buffer_height; - return instruction->saved_buffer->texture; -} - void transaction_commit_dirty(void) { if (!server.dirty_containers->length) { return; diff --git a/sway/tree/view.c b/sway/tree/view.c index 8f54cc11..0dbd3812 100644 --- a/sway/tree/view.c +++ b/sway/tree/view.c @@ -2,6 +2,7 @@ #include #include #include +#include #include #include "config.h" #ifdef HAVE_XWAYLAND @@ -1070,3 +1071,22 @@ void view_set_urgent(struct sway_view *view, bool enable) { bool view_is_urgent(struct sway_view *view) { return view->urgent.tv_sec || view->urgent.tv_nsec; } + +void view_remove_saved_buffer(struct sway_view *view) { + if (!sway_assert(view->saved_buffer, "Expected a saved buffer")) { + return; + } + wlr_buffer_unref(view->saved_buffer); + view->saved_buffer = NULL; +} + +void view_save_buffer(struct sway_view *view) { + if (!sway_assert(!view->saved_buffer, "Didn't expect saved buffer")) { + view_remove_saved_buffer(view); + } + if (view->surface && wlr_surface_has_buffer(view->surface)) { + view->saved_buffer = wlr_buffer_ref(view->surface->buffer); + view->saved_buffer_width = view->surface->current.width; + view->saved_buffer_height = view->surface->current.height; + } +} From 77d74dd34fd7126d6159234c883b50a5a021e24a Mon Sep 17 00:00:00 2001 From: chr0me Date: Wed, 1 Aug 2018 19:48:43 +0800 Subject: [PATCH 102/148] XCursor is not configured if no pointer device is available --- sway/input/seat.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sway/input/seat.c b/sway/input/seat.c index 53a92989..a4a449e4 100644 --- a/sway/input/seat.c +++ b/sway/input/seat.c @@ -393,7 +393,6 @@ struct sway_seat *seat_create(struct sway_input_manager *input, WL_SEAT_CAPABILITY_POINTER | WL_SEAT_CAPABILITY_TOUCH); - seat_configure_xcursor(seat); wl_list_insert(&input->seats, &seat->link); @@ -438,6 +437,7 @@ static void seat_apply_input_config(struct sway_seat *seat, static void seat_configure_pointer(struct sway_seat *seat, struct sway_seat_device *sway_device) { + seat_configure_xcursor(seat); wlr_cursor_attach_input_device(seat->cursor->cursor, sway_device->input_device->wlr_device); seat_apply_input_config(seat, sway_device); From e8b179e313632c150ff7775f4f740296465cb58a Mon Sep 17 00:00:00 2001 From: Ian Fan Date: Fri, 13 Jul 2018 16:58:45 +0100 Subject: [PATCH 103/148] ipc: add shutdown event --- include/ipc.h | 5 +++-- include/sway/ipc-server.h | 1 + sway/ipc-server.c | 16 ++++++++++++++++ sway/main.c | 1 + 4 files changed, 21 insertions(+), 2 deletions(-) diff --git a/include/ipc.h b/include/ipc.h index 0010718b..ffc57d1b 100644 --- a/include/ipc.h +++ b/include/ipc.h @@ -27,8 +27,9 @@ enum ipc_command_type { IPC_EVENT_WINDOW = ((1<<31) | 3), IPC_EVENT_BARCONFIG_UPDATE = ((1<<31) | 4), IPC_EVENT_BINDING = ((1<<31) | 5), - IPC_EVENT_MODIFIER = ((1<<31) | 6), - IPC_EVENT_INPUT = ((1<<31) | 7), + IPC_EVENT_SHUTDOWN = ((1<<31) | 6), + IPC_EVENT_MODIFIER = ((1<<31) | 16), + IPC_EVENT_INPUT = ((1<<31) | 17), }; #endif diff --git a/include/sway/ipc-server.h b/include/sway/ipc-server.h index 6469f097..a11735cf 100644 --- a/include/sway/ipc-server.h +++ b/include/sway/ipc-server.h @@ -16,5 +16,6 @@ void ipc_event_workspace(struct sway_container *old, void ipc_event_window(struct sway_container *window, const char *change); void ipc_event_barconfig_update(struct bar_config *bar); void ipc_event_mode(const char *mode, bool pango); +void ipc_event_shutdown(const char *reason); #endif diff --git a/sway/ipc-server.c b/sway/ipc-server.c index be703915..72031e24 100644 --- a/sway/ipc-server.c +++ b/sway/ipc-server.c @@ -353,6 +353,20 @@ void ipc_event_mode(const char *mode, bool pango) { json_object_put(obj); } +void ipc_event_shutdown(const char *reason) { + if (!ipc_has_event_listeners(IPC_EVENT_SHUTDOWN)) { + return; + } + wlr_log(WLR_DEBUG, "Sending shutdown::%s event", reason); + + json_object *json = json_object_new_object(); + json_object_object_add(json, "change", json_object_new_string(reason)); + + const char *json_string = json_object_to_json_string(json); + ipc_send_event(json_string, IPC_EVENT_SHUTDOWN); + json_object_put(json); +} + int ipc_client_handle_writable(int client_fd, uint32_t mask, void *data) { struct ipc_client *client = data; @@ -549,6 +563,8 @@ void ipc_client_handle_command(struct ipc_client *client) { client->subscribed_events |= event_mask(IPC_EVENT_BARCONFIG_UPDATE); } else if (strcmp(event_type, "mode") == 0) { client->subscribed_events |= event_mask(IPC_EVENT_MODE); + } else if (strcmp(event_type, "shutdown") == 0) { + client->subscribed_events |= event_mask(IPC_EVENT_SHUTDOWN); } else if (strcmp(event_type, "window") == 0) { client->subscribed_events |= event_mask(IPC_EVENT_WINDOW); } else if (strcmp(event_type, "modifier") == 0) { diff --git a/sway/main.c b/sway/main.c index a20f1dac..477ffa5a 100644 --- a/sway/main.c +++ b/sway/main.c @@ -36,6 +36,7 @@ struct sway_server server; void sway_terminate(int exit_code) { terminate_request = true; exit_value = exit_code; + ipc_event_shutdown("exit"); wl_display_terminate(server.wl_display); } From 87ccf189646cfc79db791ef9b81b5b3f0c2040b7 Mon Sep 17 00:00:00 2001 From: Ian Fan Date: Fri, 13 Jul 2018 17:13:25 +0100 Subject: [PATCH 104/148] ipc: add workspace::init event --- sway/tree/container.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/sway/tree/container.c b/sway/tree/container.c index 4e85021d..f082e8b1 100644 --- a/sway/tree/container.c +++ b/sway/tree/container.c @@ -64,6 +64,8 @@ void container_create_notify(struct sway_container *container) { if (container->type == C_VIEW || container->type == C_CONTAINER) { ipc_event_window(container, "new"); + } else if (container->type == C_WORKSPACE) { + ipc_event_workspace(NULL, container, "init"); } } From f0310933c89ba062a13df0b319d65f0574445ab8 Mon Sep 17 00:00:00 2001 From: Ian Fan Date: Fri, 13 Jul 2018 17:15:52 +0100 Subject: [PATCH 105/148] ipc: add window::close event --- sway/tree/container.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/sway/tree/container.c b/sway/tree/container.c index f082e8b1..47855052 100644 --- a/sway/tree/container.c +++ b/sway/tree/container.c @@ -321,7 +321,11 @@ static struct sway_container *container_destroy_noreaping( } wl_signal_emit(&con->events.destroy, con); - ipc_event_window(con, "close"); + + // emit IPC event + if (con->type == C_VIEW) { + ipc_event_window(con, "close"); + } // The below functions move their children to somewhere else. if (con->type == C_OUTPUT) { From dd1d6255f0e558f2d62e03f701440a57f65254b2 Mon Sep 17 00:00:00 2001 From: Ian Fan Date: Fri, 13 Jul 2018 17:16:32 +0100 Subject: [PATCH 106/148] ipc: add window::focus event --- sway/input/seat.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/sway/input/seat.c b/sway/input/seat.c index a4a449e4..c85e9242 100644 --- a/sway/input/seat.c +++ b/sway/input/seat.c @@ -766,6 +766,10 @@ void seat_set_focus_warp(struct sway_seat *seat, } } + if (container->type == C_VIEW) { + ipc_event_window(container, "focus"); + } + seat->has_focus = (container != NULL); update_debug_tree(); From 4f8f363dda2495810e419be6e79c3869bb7f17d8 Mon Sep 17 00:00:00 2001 From: Ian Fan Date: Fri, 13 Jul 2018 17:19:16 +0100 Subject: [PATCH 107/148] ipc: add window::title event --- sway/tree/view.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/sway/tree/view.c b/sway/tree/view.c index 8f54cc11..7300f207 100644 --- a/sway/tree/view.c +++ b/sway/tree/view.c @@ -864,6 +864,8 @@ void view_update_title(struct sway_view *view, bool force) { // Update title after the global font height is updated container_update_title_textures(view->swayc); + + ipc_event_window(view->swayc, "title"); } static bool find_by_mark_iterator(struct sway_container *con, From 686c084cec0b1143c877e8c0088732fad5229ce4 Mon Sep 17 00:00:00 2001 From: Ian Fan Date: Fri, 13 Jul 2018 21:55:04 +0100 Subject: [PATCH 108/148] ipc: add workspace::empty event --- sway/tree/container.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/sway/tree/container.c b/sway/tree/container.c index 47855052..3b3c5e2e 100644 --- a/sway/tree/container.c +++ b/sway/tree/container.c @@ -325,6 +325,8 @@ static struct sway_container *container_destroy_noreaping( // emit IPC event if (con->type == C_VIEW) { ipc_event_window(con, "close"); + } else if (con->type == C_WORKSPACE) { + ipc_event_workspace(NULL, con, "empty"); } // The below functions move their children to somewhere else. From dd6debf367420d0771cc0326ecb2511c1ce05ac1 Mon Sep 17 00:00:00 2001 From: Ian Fan Date: Fri, 13 Jul 2018 23:22:34 +0100 Subject: [PATCH 109/148] ipc: add barconfig_update event on config reload --- sway/commands/reload.c | 28 ++++++++++++++++++++++++++++ 1 file changed, 28 insertions(+) diff --git a/sway/commands/reload.c b/sway/commands/reload.c index cea6a94b..36c6acce 100644 --- a/sway/commands/reload.c +++ b/sway/commands/reload.c @@ -1,17 +1,45 @@ +#define _XOPEN_SOURCE 500 +#include #include "sway/commands.h" #include "sway/config.h" +#include "sway/ipc-server.h" #include "sway/tree/arrange.h" +#include "list.h" struct cmd_results *cmd_reload(int argc, char **argv) { struct cmd_results *error = NULL; if ((error = checkarg(argc, "reload", EXPECTED_EQUAL_TO, 0))) { return error; } + + // store bar ids to check against new bars for barconfig_update events + list_t *bar_ids = create_list(); + for (int i = 0; i < config->bars->length; ++i) { + struct bar_config *bar = config->bars->items[i]; + list_add(bar_ids, strdup(bar->id)); + } + if (!load_main_config(config->current_config_path, true)) { return cmd_results_new(CMD_FAILURE, "reload", "Error(s) reloading config."); } load_swaybars(); + + for (int i = 0; i < config->bars->length; ++i) { + struct bar_config *bar = config->bars->items[i]; + for (int j = 0; j < bar_ids->length; ++j) { + if (strcmp(bar->id, bar_ids->items[j]) == 0) { + ipc_event_barconfig_update(bar); + break; + } + } + } + + for (int i = 0; i < bar_ids->length; ++i) { + free(bar_ids->items[i]); + } + list_free(bar_ids); + arrange_windows(&root_container); return cmd_results_new(CMD_SUCCESS, NULL, NULL); } From 317217f2c87aba4463806e211c22296ac9230b6c Mon Sep 17 00:00:00 2001 From: Ian Fan Date: Sat, 14 Jul 2018 11:10:36 +0100 Subject: [PATCH 110/148] ipc: add window::mark event --- include/sway/tree/view.h | 2 ++ sway/commands/mark.c | 2 +- sway/ipc-json.c | 9 +++++++++ sway/tree/view.c | 13 +++++++++---- 4 files changed, 21 insertions(+), 5 deletions(-) diff --git a/include/sway/tree/view.h b/include/sway/tree/view.h index 0152ed55..800df073 100644 --- a/include/sway/tree/view.h +++ b/include/sway/tree/view.h @@ -311,6 +311,8 @@ void view_clear_marks(struct sway_view *view); bool view_has_mark(struct sway_view *view, char *mark); +void view_add_mark(struct sway_view *view, char *mark); + void view_update_marks_textures(struct sway_view *view); /** diff --git a/sway/commands/mark.c b/sway/commands/mark.c index 5a897e69..9ea8c301 100644 --- a/sway/commands/mark.c +++ b/sway/commands/mark.c @@ -58,7 +58,7 @@ struct cmd_results *cmd_mark(int argc, char **argv) { view_find_and_unmark(mark); if (!toggle || !had_mark) { - list_add(view->marks, strdup(mark)); + view_add_mark(view, mark); } free(mark); diff --git a/sway/ipc-json.c b/sway/ipc-json.c index c49ea47e..4c2bcc98 100644 --- a/sway/ipc-json.c +++ b/sway/ipc-json.c @@ -201,6 +201,15 @@ static void ipc_json_describe_view(struct sway_container *c, json_object *object bool urgent = c->type == C_VIEW ? view_is_urgent(c->sway_view) : container_has_urgent_child(c); json_object_object_add(object, "urgent", json_object_new_boolean(urgent)); + + if (c->type == C_VIEW) { + json_object *marks = json_object_new_array(); + list_t *view_marks = c->sway_view->marks; + for (int i = 0; i < view_marks->length; ++i) { + json_object_array_add(marks, json_object_new_string(view_marks->items[i])); + } + json_object_object_add(object, "marks", marks); + } } static void focus_inactive_children_iterator(struct sway_container *c, void *data) { diff --git a/sway/tree/view.c b/sway/tree/view.c index 7300f207..48b39e80 100644 --- a/sway/tree/view.c +++ b/sway/tree/view.c @@ -888,6 +888,7 @@ bool view_find_and_unmark(char *mark) { free(view_mark); list_del(view->marks, i); view_update_marks_textures(view); + ipc_event_window(container, "mark"); return true; } } @@ -895,11 +896,10 @@ bool view_find_and_unmark(char *mark) { } void view_clear_marks(struct sway_view *view) { - for (int i = 0; i < view->marks->length; ++i) { - free(view->marks->items[i]); + while (view->marks->length) { + list_del(view->marks, 0); + ipc_event_window(view->swayc, "mark"); } - list_free(view->marks); - view->marks = create_list(); } bool view_has_mark(struct sway_view *view, char *mark) { @@ -912,6 +912,11 @@ bool view_has_mark(struct sway_view *view, char *mark) { return false; } +void view_add_mark(struct sway_view *view, char *mark) { + list_add(view->marks, strdup(mark)); + ipc_event_window(view->swayc, "mark"); +} + static void update_marks_texture(struct sway_view *view, struct wlr_texture **texture, struct border_colors *class) { struct sway_container *output = container_parent(view->swayc, C_OUTPUT); From 07101a570750f47fa5d4fb81739a40168b6c8ea6 Mon Sep 17 00:00:00 2001 From: Ian Fan Date: Mon, 16 Jul 2018 14:01:35 +0100 Subject: [PATCH 111/148] ipc: only emit window::create event for views --- sway/tree/container.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sway/tree/container.c b/sway/tree/container.c index 3b3c5e2e..92d9ce06 100644 --- a/sway/tree/container.c +++ b/sway/tree/container.c @@ -62,7 +62,7 @@ void container_create_notify(struct sway_container *container) { // TODO send ipc event type based on the container type wl_signal_emit(&root_container.sway_root->events.new_container, container); - if (container->type == C_VIEW || container->type == C_CONTAINER) { + if (container->type == C_VIEW) { ipc_event_window(container, "new"); } else if (container->type == C_WORKSPACE) { ipc_event_workspace(NULL, container, "init"); From 4bf253855f1946ffca5a41085b8ae3eb79ca62c4 Mon Sep 17 00:00:00 2001 From: Ian Fan Date: Wed, 18 Jul 2018 10:47:44 +0100 Subject: [PATCH 112/148] ipc: fix workspace::move calls argument order --- sway/tree/container.c | 2 +- sway/tree/layout.c | 2 +- sway/tree/output.c | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/sway/tree/container.c b/sway/tree/container.c index 92d9ce06..b6ff4d30 100644 --- a/sway/tree/container.c +++ b/sway/tree/container.c @@ -283,7 +283,7 @@ static struct sway_container *container_output_destroy( container_remove_child(workspace); if (!workspace_is_empty(workspace)) { container_add_child(new_output, workspace); - ipc_event_workspace(workspace, NULL, "move"); + ipc_event_workspace(NULL, workspace, "move"); } else { container_destroy(workspace); } diff --git a/sway/tree/layout.c b/sway/tree/layout.c index a0764a54..b833e8e1 100644 --- a/sway/tree/layout.c +++ b/sway/tree/layout.c @@ -217,7 +217,7 @@ void container_move_to(struct sway_container *container, container_sort_workspaces(new_parent); seat_set_focus(seat, new_parent); workspace_output_raise_priority(container, old_parent, new_parent); - ipc_event_workspace(container, NULL, "move"); + ipc_event_workspace(NULL, container, "move"); } container_notify_subtree_changed(old_parent); container_notify_subtree_changed(new_parent); diff --git a/sway/tree/output.c b/sway/tree/output.c index da535c18..31e3bf9b 100644 --- a/sway/tree/output.c +++ b/sway/tree/output.c @@ -22,7 +22,7 @@ static void restore_workspaces(struct sway_container *output) { if (highest == output) { container_remove_child(ws); container_add_child(output, ws); - ipc_event_workspace(ws, NULL, "move"); + ipc_event_workspace(NULL, ws, "move"); j--; } } From e0e638281e193103b44105cb1689b2ec25cae7e8 Mon Sep 17 00:00:00 2001 From: Ian Fan Date: Wed, 18 Jul 2018 10:50:48 +0100 Subject: [PATCH 113/148] ipc: add window::move events --- sway/tree/layout.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/sway/tree/layout.c b/sway/tree/layout.c index b833e8e1..a0f9b6de 100644 --- a/sway/tree/layout.c +++ b/sway/tree/layout.c @@ -218,6 +218,8 @@ void container_move_to(struct sway_container *container, seat_set_focus(seat, new_parent); workspace_output_raise_priority(container, old_parent, new_parent); ipc_event_workspace(NULL, container, "move"); + } else if (container->type == C_VIEW) { + ipc_event_window(container, "move"); } container_notify_subtree_changed(old_parent); container_notify_subtree_changed(new_parent); @@ -578,6 +580,10 @@ void container_move(struct sway_container *container, container_notify_subtree_changed(old_parent); container_notify_subtree_changed(container->parent); + if (container->type == C_VIEW) { + ipc_event_window(container, "move"); + } + if (old_parent) { seat_set_focus(config->handler_context.seat, old_parent); seat_set_focus(config->handler_context.seat, container); From 6865b8aae9736e73a2153bce89d979737f8162cc Mon Sep 17 00:00:00 2001 From: Ian Fan Date: Wed, 18 Jul 2018 11:37:50 +0100 Subject: [PATCH 114/148] ipc: add binding event --- include/sway/config.h | 2 -- include/sway/ipc-server.h | 1 + sway/commands/bind.c | 52 ++++++++++++++++++++++++++- sway/ipc-server.c | 76 +++++++++++++++++++++++++++++++++++++++ 4 files changed, 128 insertions(+), 3 deletions(-) diff --git a/include/sway/config.h b/include/sway/config.h index 0f74b439..909b6827 100644 --- a/include/sway/config.h +++ b/include/sway/config.h @@ -488,8 +488,6 @@ int sway_binding_cmp_keys(const void *a, const void *b); void free_sway_binding(struct sway_binding *sb); -struct sway_binding *sway_binding_dup(struct sway_binding *sb); - void seat_execute_command(struct sway_seat *seat, struct sway_binding *binding); void load_swaybars(); diff --git a/include/sway/ipc-server.h b/include/sway/ipc-server.h index a11735cf..4b6d0e25 100644 --- a/include/sway/ipc-server.h +++ b/include/sway/ipc-server.h @@ -17,5 +17,6 @@ void ipc_event_window(struct sway_container *window, const char *change); void ipc_event_barconfig_update(struct bar_config *bar); void ipc_event_mode(const char *mode, bool pango); void ipc_event_shutdown(const char *reason); +void ipc_event_binding(struct sway_binding *binding); #endif diff --git a/sway/commands/bind.c b/sway/commands/bind.c index 133fd089..8270b958 100644 --- a/sway/commands/bind.c +++ b/sway/commands/bind.c @@ -1,3 +1,4 @@ +#define _XOPEN_SOURCE 500 #ifdef __linux__ #include #elif __FreeBSD__ @@ -5,9 +6,11 @@ #endif #include #include +#include #include #include "sway/commands.h" #include "sway/config.h" +#include "sway/ipc-server.h" #include "list.h" #include "log.h" #include "stringop.h" @@ -27,6 +30,33 @@ void free_sway_binding(struct sway_binding *binding) { free(binding); } +static struct sway_binding *sway_binding_dup(struct sway_binding *sb) { + struct sway_binding *new_sb = calloc(1, sizeof(struct sway_binding)); + if (!new_sb) { + return NULL; + } + + new_sb->type = sb->type; + new_sb->order = sb->order; + new_sb->flags = sb->flags; + new_sb->modifiers = sb->modifiers; + new_sb->command = strdup(sb->command); + + new_sb->keys = create_list(); + int i; + for (i = 0; i < sb->keys->length; ++i) { + xkb_keysym_t *key = malloc(sizeof(xkb_keysym_t)); + if (!key) { + free_sway_binding(new_sb); + return NULL; + } + *key = *(xkb_keysym_t *)sb->keys->items[i]; + list_add(new_sb->keys, key); + } + + return new_sb; +} + /** * Returns true if the bindings have the same key and modifier combinations. * Note that keyboard layout is not considered, so the bindings might actually @@ -275,11 +305,31 @@ struct cmd_results *cmd_bindcode(int argc, char **argv) { void seat_execute_command(struct sway_seat *seat, struct sway_binding *binding) { wlr_log(WLR_DEBUG, "running command for binding: %s", binding->command); + + struct sway_binding *binding_copy = binding; + bool reload = false; + // if this is a reload command we need to make a duplicate of the + // binding since it will be gone after the reload has completed. + if (strcasecmp(binding->command, "reload") == 0) { + reload = true; + binding_copy = sway_binding_dup(binding); + if (!binding_copy) { + wlr_log(WLR_ERROR, "Failed to duplicate binding during reload"); + return; + } + } + config->handler_context.seat = seat; struct cmd_results *results = execute_command(binding->command, NULL); - if (results->status != CMD_SUCCESS) { + if (results->status == CMD_SUCCESS) { + ipc_event_binding(binding_copy); + } else { wlr_log(WLR_DEBUG, "could not run command for binding: %s (%s)", binding->command, results->error); } + + if (reload) { // free the binding if we made a copy + free_sway_binding(binding_copy); + } free_cmd_results(results); } diff --git a/sway/ipc-server.c b/sway/ipc-server.c index 72031e24..8fceafa2 100644 --- a/sway/ipc-server.c +++ b/sway/ipc-server.c @@ -3,12 +3,18 @@ // Any value will hide SOCK_CLOEXEC on FreeBSD (__BSD_VISIBLE=0) #define _XOPEN_SOURCE 700 #endif +#ifdef __linux__ +#include +#elif __FreeBSD__ +#include +#endif #include #include #include #include #include #include +#include #include #include #include @@ -28,6 +34,7 @@ #include "sway/tree/view.h" #include "list.h" #include "log.h" +#include "util.h" static int ipc_socket = -1; static struct wl_event_source *ipc_event_source = NULL; @@ -367,6 +374,75 @@ void ipc_event_shutdown(const char *reason) { json_object_put(json); } +void ipc_event_binding(struct sway_binding *binding) { + if (!ipc_has_event_listeners(IPC_EVENT_BINDING)) { + return; + } + wlr_log(WLR_DEBUG, "Sending binding event"); + + json_object *json_binding = json_object_new_object(); + json_object_object_add(json_binding, "command", json_object_new_string(binding->command)); + + const char *names[10]; + int len = get_modifier_names(names, binding->modifiers); + json_object *modifiers = json_object_new_array(); + for (int i = 0; i < len; ++i) { + json_object_array_add(modifiers, json_object_new_string(names[i])); + } + json_object_object_add(json_binding, "event_state_mask", modifiers); + + json_object *input_codes = json_object_new_array(); + int input_code = 0; + json_object *symbols = json_object_new_array(); + json_object *symbol = NULL; + + if (binding->type == BINDING_KEYCODE) { // bindcode: populate input_codes + uint32_t keycode; + for (int i = 0; i < binding->keys->length; ++i) { + keycode = *(uint32_t *)binding->keys->items[i]; + json_object_array_add(input_codes, json_object_new_int(keycode)); + if (i == 0) { + input_code = keycode; + } + } + } else { // bindsym/mouse: populate symbols + uint32_t keysym; + char buffer[64]; + for (int i = 0; i < binding->keys->length; ++i) { + keysym = *(uint32_t *)binding->keys->items[i]; + if (keysym >= BTN_LEFT && keysym <= BTN_LEFT + 8) { + snprintf(buffer, 64, "button%u", keysym - BTN_LEFT + 1); + } else if (xkb_keysym_get_name(keysym, buffer, 64) < 0) { + continue; + } + + json_object *str = json_object_new_string(buffer); + if (i == 0) { + // str is owned by both symbol and symbols. Make sure + // to bump the ref count. + json_object_array_add(symbols, json_object_get(str)); + symbol = str; + } else { + json_object_array_add(symbols, str); + } + } + } + + json_object_object_add(json_binding, "input_codes", input_codes); + json_object_object_add(json_binding, "input_code", json_object_new_int(input_code)); + json_object_object_add(json_binding, "symbols", symbols); + json_object_object_add(json_binding, "symbol", symbol); + json_object_object_add(json_binding, "input_type", binding->type == BINDING_MOUSE ? + json_object_new_string("mouse") : json_object_new_string("keyboard")); + + json_object *json = json_object_new_object(); + json_object_object_add(json, "change", json_object_new_string("run")); + json_object_object_add(json, "binding", json_binding); + const char *json_string = json_object_to_json_string(json); + ipc_send_event(json_string, IPC_EVENT_BINDING); + json_object_put(json); +} + int ipc_client_handle_writable(int client_fd, uint32_t mask, void *data) { struct ipc_client *client = data; From d898e035ba539005a8d7fa8283191f4c4054612e Mon Sep 17 00:00:00 2001 From: Ian Fan Date: Wed, 18 Jul 2018 11:38:06 +0100 Subject: [PATCH 115/148] ipc: add workspace::reload event --- sway/commands/reload.c | 1 + 1 file changed, 1 insertion(+) diff --git a/sway/commands/reload.c b/sway/commands/reload.c index 36c6acce..5c1b19b4 100644 --- a/sway/commands/reload.c +++ b/sway/commands/reload.c @@ -22,6 +22,7 @@ struct cmd_results *cmd_reload(int argc, char **argv) { if (!load_main_config(config->current_config_path, true)) { return cmd_results_new(CMD_FAILURE, "reload", "Error(s) reloading config."); } + ipc_event_workspace(NULL, NULL, "reload"); load_swaybars(); From 75aba004603e35c80eda397c2e987f2c3e75cbf5 Mon Sep 17 00:00:00 2001 From: Ian Fan Date: Wed, 18 Jul 2018 11:40:16 +0100 Subject: [PATCH 116/148] ipc: always include old property in workspace events --- sway/ipc-server.c | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/sway/ipc-server.c b/sway/ipc-server.c index 8fceafa2..dc6b353b 100644 --- a/sway/ipc-server.c +++ b/sway/ipc-server.c @@ -298,13 +298,11 @@ void ipc_event_workspace(struct sway_container *old, wlr_log(WLR_DEBUG, "Sending workspace::%s event", change); json_object *obj = json_object_new_object(); json_object_object_add(obj, "change", json_object_new_string(change)); - if (strcmp("focus", change) == 0) { - if (old) { - json_object_object_add(obj, "old", - ipc_json_describe_container_recursive(old)); - } else { - json_object_object_add(obj, "old", NULL); - } + if (old) { + json_object_object_add(obj, "old", + ipc_json_describe_container_recursive(old)); + } else { + json_object_object_add(obj, "old", NULL); } if (new) { From 33433c64347eb0bfd7c1465a3b60579fa88ef724 Mon Sep 17 00:00:00 2001 From: Ian Fan Date: Wed, 18 Jul 2018 11:52:29 +0100 Subject: [PATCH 117/148] Add missing swaymsg completions --- completions/bash/swaymsg | 3 ++- completions/zsh/_swaymsg | 2 ++ 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/completions/bash/swaymsg b/completions/bash/swaymsg index 8ec90b6f..e4b2c1b7 100644 --- a/completions/bash/swaymsg +++ b/completions/bash/swaymsg @@ -14,7 +14,8 @@ _swaymsg() 'get_marks' 'get_bar_config' 'get_version' - 'get_clipboard' + 'get_binding_modes' + 'get_config' ) short=( diff --git a/completions/zsh/_swaymsg b/completions/zsh/_swaymsg index 2e39deb6..28de474d 100644 --- a/completions/zsh/_swaymsg +++ b/completions/zsh/_swaymsg @@ -22,6 +22,8 @@ types=( 'get_marks' 'get_bar_config' 'get_version' +'get_binding_modes' +'get_config' ) _arguments -s \ From 3edaf2ce2a8a4753c162491329a7dfa492ff7e77 Mon Sep 17 00:00:00 2001 From: Ian Fan Date: Wed, 18 Jul 2018 12:30:39 +0100 Subject: [PATCH 118/148] ipc: add tick event --- completions/bash/swaymsg | 1 + completions/zsh/_swaymsg | 1 + include/ipc.h | 2 ++ sway/ipc-server.c | 30 ++++++++++++++++++++++++++++++ swaymsg/main.c | 8 +++++++- swaymsg/swaymsg.1.scd | 3 +++ 6 files changed, 44 insertions(+), 1 deletion(-) diff --git a/completions/bash/swaymsg b/completions/bash/swaymsg index e4b2c1b7..20092bdc 100644 --- a/completions/bash/swaymsg +++ b/completions/bash/swaymsg @@ -16,6 +16,7 @@ _swaymsg() 'get_version' 'get_binding_modes' 'get_config' + 'send_tick' ) short=( diff --git a/completions/zsh/_swaymsg b/completions/zsh/_swaymsg index 28de474d..a7a1c8e0 100644 --- a/completions/zsh/_swaymsg +++ b/completions/zsh/_swaymsg @@ -24,6 +24,7 @@ types=( 'get_version' 'get_binding_modes' 'get_config' +'send_tick' ) _arguments -s \ diff --git a/include/ipc.h b/include/ipc.h index ffc57d1b..2138d3fa 100644 --- a/include/ipc.h +++ b/include/ipc.h @@ -15,6 +15,7 @@ enum ipc_command_type { IPC_GET_VERSION = 7, IPC_GET_BINDING_MODES = 8, IPC_GET_CONFIG = 9, + IPC_SEND_TICK = 10, // sway-specific command types IPC_GET_INPUTS = 100, @@ -28,6 +29,7 @@ enum ipc_command_type { IPC_EVENT_BARCONFIG_UPDATE = ((1<<31) | 4), IPC_EVENT_BINDING = ((1<<31) | 5), IPC_EVENT_SHUTDOWN = ((1<<31) | 6), + IPC_EVENT_TICK = ((1<<31) | 7), IPC_EVENT_MODIFIER = ((1<<31) | 16), IPC_EVENT_INPUT = ((1<<31) | 17), }; diff --git a/sway/ipc-server.c b/sway/ipc-server.c index dc6b353b..63633567 100644 --- a/sway/ipc-server.c +++ b/sway/ipc-server.c @@ -441,6 +441,21 @@ void ipc_event_binding(struct sway_binding *binding) { json_object_put(json); } +static void ipc_event_tick(const char *payload) { + if (!ipc_has_event_listeners(IPC_EVENT_TICK)) { + return; + } + wlr_log(WLR_DEBUG, "Sending tick event"); + + json_object *json = json_object_new_object(); + json_object_object_add(json, "first", json_object_new_boolean(false)); + json_object_object_add(json, "payload", json_object_new_string(payload)); + + const char *json_string = json_object_to_json_string(json); + ipc_send_event(json_string, IPC_EVENT_TICK); + json_object_put(json); +} + int ipc_client_handle_writable(int client_fd, uint32_t mask, void *data) { struct ipc_client *client = data; @@ -582,6 +597,13 @@ void ipc_client_handle_command(struct ipc_client *client) { goto exit_cleanup; } + case IPC_SEND_TICK: + { + ipc_event_tick(buf); + ipc_send_reply(client, "{\"success\": true}", 17); + goto exit_cleanup; + } + case IPC_GET_OUTPUTS: { json_object *outputs = json_object_new_array(); @@ -628,6 +650,7 @@ void ipc_client_handle_command(struct ipc_client *client) { goto exit_cleanup; } + bool is_tick = false; // parse requested event types for (size_t i = 0; i < json_object_array_length(request); i++) { const char *event_type = json_object_get_string(json_object_array_get_idx(request, i)); @@ -645,6 +668,9 @@ void ipc_client_handle_command(struct ipc_client *client) { client->subscribed_events |= event_mask(IPC_EVENT_MODIFIER); } else if (strcmp(event_type, "binding") == 0) { client->subscribed_events |= event_mask(IPC_EVENT_BINDING); + } else if (strcmp(event_type, "tick") == 0) { + client->subscribed_events |= event_mask(IPC_EVENT_TICK); + is_tick = true; } else { client_valid = ipc_send_reply(client, "{\"success\": false}", 18); @@ -656,6 +682,10 @@ void ipc_client_handle_command(struct ipc_client *client) { json_object_put(request); client_valid = ipc_send_reply(client, "{\"success\": true}", 17); + if (is_tick) { + client->current_command = IPC_EVENT_TICK; + ipc_send_reply(client, "{\"first\": true, \"payload\": \"\"}", 30); + } goto exit_cleanup; } diff --git a/swaymsg/main.c b/swaymsg/main.c index c4141ca5..3767daf3 100644 --- a/swaymsg/main.c +++ b/swaymsg/main.c @@ -250,12 +250,16 @@ static void pretty_print(int type, json_object *resp) { if (type != IPC_COMMAND && type != IPC_GET_WORKSPACES && type != IPC_GET_INPUTS && type != IPC_GET_OUTPUTS && type != IPC_GET_VERSION && type != IPC_GET_SEATS && - type != IPC_GET_CONFIG) { + type != IPC_GET_CONFIG && type != IPC_SEND_TICK) { printf("%s\n", json_object_to_json_string_ext(resp, JSON_C_TO_STRING_PRETTY | JSON_C_TO_STRING_SPACED)); return; } + if (type == IPC_SEND_TICK) { + return; + } + if (type == IPC_GET_VERSION) { pretty_print_version(resp); return; @@ -384,6 +388,8 @@ int main(int argc, char **argv) { type = IPC_GET_BINDING_MODES; } else if (strcasecmp(cmdtype, "get_config") == 0) { type = IPC_GET_CONFIG; + } else if (strcasecmp(cmdtype, "send_tick") == 0) { + type = IPC_SEND_TICK; } else { sway_abort("Unknown message type %s", cmdtype); } diff --git a/swaymsg/swaymsg.1.scd b/swaymsg/swaymsg.1.scd index a6e279da..8cf1b222 100644 --- a/swaymsg/swaymsg.1.scd +++ b/swaymsg/swaymsg.1.scd @@ -64,3 +64,6 @@ _swaymsg_ [options...] [message] *get\_config* Gets a JSON-encoded copy of the current configuration. + +*send\_tick* + Sends a tick event to all subscribed clients. From b2ac234569ff98de583d9e7755526cadf960f772 Mon Sep 17 00:00:00 2001 From: Ian Fan Date: Wed, 18 Jul 2018 21:52:15 +0100 Subject: [PATCH 119/148] ipc: fix workspace::focus event behaviour --- sway/input/seat.c | 4 +++- sway/tree/layout.c | 2 +- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/sway/input/seat.c b/sway/input/seat.c index c85e9242..76050aa9 100644 --- a/sway/input/seat.c +++ b/sway/input/seat.c @@ -739,7 +739,9 @@ void seat_set_focus_warp(struct sway_seat *seat, if (last_focus) { if (last_workspace) { - ipc_event_workspace(last_workspace, container, "focus"); + if (last_workspace != new_workspace) { + ipc_event_workspace(last_workspace, new_workspace, "focus"); + } if (!workspace_is_visible(last_workspace) && workspace_is_empty(last_workspace)) { if (last_workspace == last_focus) { diff --git a/sway/tree/layout.c b/sway/tree/layout.c index a0f9b6de..9fbbccaf 100644 --- a/sway/tree/layout.c +++ b/sway/tree/layout.c @@ -598,7 +598,7 @@ void container_move(struct sway_container *container, next_ws = container_parent(next_ws, C_WORKSPACE); } if (last_ws && next_ws && last_ws != next_ws) { - ipc_event_workspace(last_ws, container, "focus"); + ipc_event_workspace(last_ws, next_ws, "focus"); workspace_detect_urgent(last_ws); workspace_detect_urgent(next_ws); } From 03eaf444a4a432e5712d40f93d849b51d2028b63 Mon Sep 17 00:00:00 2001 From: Ian Fan Date: Wed, 18 Jul 2018 21:55:14 +0100 Subject: [PATCH 120/148] ipc: prevent emitting a workspace::focus event when moving a container to a different workspace or output When a container is moved from, say, workspace 1 to workspace 2, workspace 2 is focused in order to arrange the windows before focus is moved back to workspace 1, which caused a workspace:focus event from workspace 2 to workspace 1 to be emitted. This commit inhibits that event. --- include/sway/input/seat.h | 2 +- sway/commands/move.c | 4 ++-- sway/input/cursor.c | 6 +++--- sway/input/seat.c | 6 +++--- sway/tree/layout.c | 4 ++-- 5 files changed, 11 insertions(+), 11 deletions(-) diff --git a/include/sway/input/seat.h b/include/sway/input/seat.h index 07febe2c..92387601 100644 --- a/include/sway/input/seat.h +++ b/include/sway/input/seat.h @@ -99,7 +99,7 @@ void seat_configure_xcursor(struct sway_seat *seat); void seat_set_focus(struct sway_seat *seat, struct sway_container *container); void seat_set_focus_warp(struct sway_seat *seat, - struct sway_container *container, bool warp); + struct sway_container *container, bool warp, bool notify); void seat_set_focus_surface(struct sway_seat *seat, struct wlr_surface *surface, bool unfocus); diff --git a/sway/commands/move.c b/sway/commands/move.c index 1aae3838..46ebcd83 100644 --- a/sway/commands/move.c +++ b/sway/commands/move.c @@ -98,7 +98,7 @@ static struct cmd_results *cmd_move_container(struct sway_container *current, container_move_to(current, destination); struct sway_container *focus = seat_get_focus_inactive( config->handler_context.seat, old_parent); - seat_set_focus(config->handler_context.seat, focus); + seat_set_focus_warp(config->handler_context.seat, focus, true, false); container_reap_empty(old_parent); container_reap_empty(destination->parent); @@ -135,7 +135,7 @@ static struct cmd_results *cmd_move_container(struct sway_container *current, struct sway_container *old_parent = current->parent; struct sway_container *old_ws = container_parent(current, C_WORKSPACE); container_move_to(current, focus); - seat_set_focus(config->handler_context.seat, old_parent); + seat_set_focus_warp(config->handler_context.seat, old_parent, true, false); container_reap_empty(old_parent); container_reap_empty(focus->parent); diff --git a/sway/input/cursor.c b/sway/input/cursor.c index 96ac7b33..d6fdc1da 100644 --- a/sway/input/cursor.c +++ b/sway/input/cursor.c @@ -349,7 +349,7 @@ void cursor_send_pointer_motion(struct sway_cursor *cursor, uint32_t time_msec, output = container_parent(c, C_OUTPUT); } if (output != focus) { - seat_set_focus_warp(seat, c, false); + seat_set_focus_warp(seat, c, false, true); } } else if (c->type == C_VIEW) { // Focus c if the following are true: @@ -359,13 +359,13 @@ void cursor_send_pointer_motion(struct sway_cursor *cursor, uint32_t time_msec, if (!wlr_seat_keyboard_has_grab(cursor->seat->wlr_seat) && c != prev_c && view_is_visible(c->sway_view)) { - seat_set_focus_warp(seat, c, false); + seat_set_focus_warp(seat, c, false, true); } else { struct sway_container *next_focus = seat_get_focus_inactive(seat, &root_container); if (next_focus && next_focus->type == C_VIEW && view_is_visible(next_focus->sway_view)) { - seat_set_focus_warp(seat, next_focus, false); + seat_set_focus_warp(seat, next_focus, false, true); } } } diff --git a/sway/input/seat.c b/sway/input/seat.c index 76050aa9..fe3cbc53 100644 --- a/sway/input/seat.c +++ b/sway/input/seat.c @@ -617,7 +617,7 @@ static int handle_urgent_timeout(void *data) { } void seat_set_focus_warp(struct sway_seat *seat, - struct sway_container *container, bool warp) { + struct sway_container *container, bool warp, bool notify) { if (seat->focused_layer) { return; } @@ -739,7 +739,7 @@ void seat_set_focus_warp(struct sway_seat *seat, if (last_focus) { if (last_workspace) { - if (last_workspace != new_workspace) { + if (notify && last_workspace != new_workspace) { ipc_event_workspace(last_workspace, new_workspace, "focus"); } if (!workspace_is_visible(last_workspace) @@ -779,7 +779,7 @@ void seat_set_focus_warp(struct sway_seat *seat, void seat_set_focus(struct sway_seat *seat, struct sway_container *container) { - seat_set_focus_warp(seat, container, true); + seat_set_focus_warp(seat, container, true, true); } void seat_set_focus_surface(struct sway_seat *seat, diff --git a/sway/tree/layout.c b/sway/tree/layout.c index 9fbbccaf..1f82e534 100644 --- a/sway/tree/layout.c +++ b/sway/tree/layout.c @@ -1001,13 +1001,13 @@ static void swap_focus(struct sway_container *con1, if (focus == con1 && (con2->parent->layout == L_TABBED || con2->parent->layout == L_STACKED)) { if (workspace_is_visible(ws2)) { - seat_set_focus_warp(seat, con2, false); + seat_set_focus_warp(seat, con2, false, true); } seat_set_focus(seat, ws1 != ws2 ? con2 : con1); } else if (focus == con2 && (con1->parent->layout == L_TABBED || con1->parent->layout == L_STACKED)) { if (workspace_is_visible(ws1)) { - seat_set_focus_warp(seat, con1, false); + seat_set_focus_warp(seat, con1, false, true); } seat_set_focus(seat, ws1 != ws2 ? con1 : con2); } else if (ws1 != ws2) { From 46cfa8ff56acff0139b2e24300cbc3ea19da723f Mon Sep 17 00:00:00 2001 From: Ian Fan Date: Wed, 1 Aug 2018 16:22:06 +0100 Subject: [PATCH 121/148] ipc: remove extraneous values Removes IPC_EVENT_MODIFIER and IPC_EVENT_INPUT, which were sway-specific and unused --- include/ipc.h | 2 -- sway/ipc-server.c | 2 -- 2 files changed, 4 deletions(-) diff --git a/include/ipc.h b/include/ipc.h index 2138d3fa..a3f60e19 100644 --- a/include/ipc.h +++ b/include/ipc.h @@ -30,8 +30,6 @@ enum ipc_command_type { IPC_EVENT_BINDING = ((1<<31) | 5), IPC_EVENT_SHUTDOWN = ((1<<31) | 6), IPC_EVENT_TICK = ((1<<31) | 7), - IPC_EVENT_MODIFIER = ((1<<31) | 16), - IPC_EVENT_INPUT = ((1<<31) | 17), }; #endif diff --git a/sway/ipc-server.c b/sway/ipc-server.c index 63633567..7d2d8969 100644 --- a/sway/ipc-server.c +++ b/sway/ipc-server.c @@ -664,8 +664,6 @@ void ipc_client_handle_command(struct ipc_client *client) { client->subscribed_events |= event_mask(IPC_EVENT_SHUTDOWN); } else if (strcmp(event_type, "window") == 0) { client->subscribed_events |= event_mask(IPC_EVENT_WINDOW); - } else if (strcmp(event_type, "modifier") == 0) { - client->subscribed_events |= event_mask(IPC_EVENT_MODIFIER); } else if (strcmp(event_type, "binding") == 0) { client->subscribed_events |= event_mask(IPC_EVENT_BINDING); } else if (strcmp(event_type, "tick") == 0) { From abf33468c12df258b8135089d99e7bd4f10e4f28 Mon Sep 17 00:00:00 2001 From: Brian Ashworth Date: Sun, 22 Jul 2018 23:43:45 -0400 Subject: [PATCH 122/148] Arrange output in arrange_layers and commit dirty --- sway/desktop/layer_shell.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/sway/desktop/layer_shell.c b/sway/desktop/layer_shell.c index a7d96717..71a0163c 100644 --- a/sway/desktop/layer_shell.c +++ b/sway/desktop/layer_shell.c @@ -7,11 +7,13 @@ #include #include #include +#include "sway/desktop/transaction.h" #include "sway/input/input-manager.h" #include "sway/input/seat.h" #include "sway/layers.h" #include "sway/output.h" #include "sway/server.h" +#include "sway/tree/arrange.h" #include "sway/tree/layout.h" #include "log.h" @@ -212,6 +214,9 @@ void arrange_layers(struct sway_output *output) { wl_list_for_each(seat, &input_manager->seats, link) { seat_set_focus_layer(seat, topmost ? topmost->layer_surface : NULL); } + + arrange_windows(output->swayc); + transaction_commit_dirty(); } static void handle_output_destroy(struct wl_listener *listener, void *data) { From 88bc4b528ef3e1af3598b513dd5c1572dd09ec23 Mon Sep 17 00:00:00 2001 From: Brian Ashworth Date: Wed, 25 Jul 2018 21:57:19 -0400 Subject: [PATCH 123/148] Implements swaynagbar --- include/swaynagbar/nagbar.h | 88 +++++++++ include/swaynagbar/render.h | 6 + meson.build | 2 + swaynagbar/main.c | 184 ++++++++++++++++++ swaynagbar/meson.build | 20 ++ swaynagbar/nagbar.c | 364 ++++++++++++++++++++++++++++++++++++ swaynagbar/render.c | 152 +++++++++++++++ swaynagbar/swaynagbar.1.scd | 41 ++++ 8 files changed, 857 insertions(+) create mode 100644 include/swaynagbar/nagbar.h create mode 100644 include/swaynagbar/render.h create mode 100644 swaynagbar/main.c create mode 100644 swaynagbar/meson.build create mode 100644 swaynagbar/nagbar.c create mode 100644 swaynagbar/render.c create mode 100644 swaynagbar/swaynagbar.1.scd diff --git a/include/swaynagbar/nagbar.h b/include/swaynagbar/nagbar.h new file mode 100644 index 00000000..07a0d51e --- /dev/null +++ b/include/swaynagbar/nagbar.h @@ -0,0 +1,88 @@ +#ifndef _SWAY_NAGBAR_NAGBAR_H +#define _SWAY_NAGBAR_NAGNAR_H +#include +#include "list.h" +#include "pool-buffer.h" +#include "xdg-output-unstable-v1-client-protocol.h" + +#define NAGBAR_BAR_BORDER_THICKNESS 2 +#define NAGBAR_MESSAGE_PADDING 8 +#define NAGBAR_BUTTON_BORDER_THICKNESS 3 +#define NAGBAR_BUTTON_GAP 20 +#define NAGBAR_BUTTON_GAP_CLOSE 15 +#define NAGBAR_BUTTON_MARGIN_RIGHT 2 +#define NAGBAR_BUTTON_PADDING 3 + +enum sway_nagbar_type { + NAGBAR_ERROR, + NAGBAR_WARNING, +}; + +struct sway_nagbar_colors { + uint32_t button_background; + uint32_t background; + uint32_t text; + uint32_t border; + uint32_t border_bottom; +}; + +struct sway_nagbar_pointer { + struct wl_pointer *pointer; + struct wl_cursor_theme *cursor_theme; + struct wl_cursor_image *cursor_image; + struct wl_surface *cursor_surface; + int x; + int y; +}; + +struct sway_nagbar_output { + char *name; + struct wl_output *wl_output; + uint32_t wl_name; +}; + +struct sway_nagbar_button { + char *text; + char *action; + int x; + int y; + int width; + int height; +}; + +struct sway_nagbar { + bool run_display; + int querying_outputs; + + struct wl_display *display; + struct wl_compositor *compositor; + struct wl_seat *seat; + struct wl_shm *shm; + struct sway_nagbar_pointer pointer; + struct zxdg_output_manager_v1 *xdg_output_manager; + struct sway_nagbar_output output; + struct zwlr_layer_shell_v1 *layer_shell; + struct zwlr_layer_surface_v1 *layer_surface; + struct wl_surface *surface; + + uint32_t width; + uint32_t height; + int32_t scale; + struct pool_buffer buffers[2]; + struct pool_buffer *current_buffer; + + enum sway_nagbar_type type; + struct sway_nagbar_colors colors; + uint32_t anchors; + char *message; + char *font; + list_t *buttons; +}; + +void nagbar_setup(struct sway_nagbar *nagbar); + +void nagbar_run(struct sway_nagbar *nagbar); + +void nagbar_destroy(struct sway_nagbar *nagbar); + +#endif diff --git a/include/swaynagbar/render.h b/include/swaynagbar/render.h new file mode 100644 index 00000000..d9429f7f --- /dev/null +++ b/include/swaynagbar/render.h @@ -0,0 +1,6 @@ +#ifndef _SWAY_NAGBAR_RENDER_H +#define _SWAY_NAGBAR_RENDER_H + +void render_frame(struct sway_nagbar *nagbar); + +#endif diff --git a/meson.build b/meson.build index 05d334d2..26d36e06 100644 --- a/meson.build +++ b/meson.build @@ -82,6 +82,7 @@ if scdoc.found() 'swaylock/swaylock.1.scd', 'swaymsg/swaymsg.1.scd', 'swayidle/swayidle.1.scd', + 'swaynagbar/swaynagbar.1.scd', ] foreach filename : man_files topic = filename.split('.')[-3].split('/')[-1] @@ -130,6 +131,7 @@ subdir('swaybg') subdir('swaybar') subdir('swaylock') subdir('swayidle') +subdir('swaynagbar') config = configuration_data() config.set('sysconfdir', join_paths(prefix, sysconfdir)) diff --git a/swaynagbar/main.c b/swaynagbar/main.c new file mode 100644 index 00000000..f5ed064e --- /dev/null +++ b/swaynagbar/main.c @@ -0,0 +1,184 @@ +#define _XOPEN_SOURCE 500 +#include +#include +#include "log.h" +#include "list.h" +#include "swaynagbar/nagbar.h" +#include "wlr-layer-shell-unstable-v1-client-protocol.h" + +static struct sway_nagbar nagbar; + +void sig_handler(int signal) { + nagbar_destroy(&nagbar); + exit(EXIT_FAILURE); +} + +void sway_terminate(int code) { + nagbar_destroy(&nagbar); + exit(code); +} + +static void set_nagbar_colors() { + if (nagbar.type == NAGBAR_ERROR) { + nagbar.colors.button_background = 0x680A0AFF; + nagbar.colors.background = 0x900000FF; + nagbar.colors.text = 0xFFFFFFFF; + nagbar.colors.border = 0xD92424FF; + nagbar.colors.border_bottom = 0x470909FF; + } else if (nagbar.type == NAGBAR_WARNING) { + nagbar.colors.button_background = 0xFFC100FF; + nagbar.colors.background = 0xFFA800FF; + nagbar.colors.text = 0x000000FF; + nagbar.colors.border = 0xAB7100FF; + nagbar.colors.border_bottom = 0xAB7100FF; + } +} + +int main(int argc, char **argv) { + int exit_code = EXIT_SUCCESS; + bool debug = false; + + memset(&nagbar, 0, sizeof(nagbar)); + nagbar.anchors = ZWLR_LAYER_SURFACE_V1_ANCHOR_TOP + | ZWLR_LAYER_SURFACE_V1_ANCHOR_LEFT + | ZWLR_LAYER_SURFACE_V1_ANCHOR_RIGHT; + nagbar.type = NAGBAR_ERROR; + set_nagbar_colors(); + nagbar.font = strdup("pango:monospace 8"); + nagbar.buttons = create_list(); + + struct sway_nagbar_button *button_close = + calloc(sizeof(struct sway_nagbar_button), 1); + button_close->text = strdup("X"); + button_close->action = NULL; + list_add(nagbar.buttons, button_close); + + static struct option long_options[] = { + {"button", required_argument, NULL, 'b'}, + {"debug", no_argument, NULL, 'd'}, + {"edge", required_argument, NULL, 'e'}, + {"font", required_argument, NULL, 'f'}, + {"help", no_argument, NULL, 'h'}, + {"message", required_argument, NULL, 'm'}, + {"output", required_argument, NULL, 'o'}, + {"type", required_argument, NULL, 't'}, + {"version", no_argument, NULL, 'v'}, + {0, 0, 0, 0} + }; + + const char *usage = + "Usage: swaynagbar [options...]\n" + "\n" + " -b, --button Create a button with text that " + "executes action when pressed. Multiple buttons can be defined.\n" + " -d, --debug Enable debugging.\n" + " -e, --edge top|bottom Set the edge to use.\n" + " -f, --font Set the font to use.\n" + " -h, --help Show help message and quit.\n" + " -m, --message Set the message text.\n" + " -o, --output Set the output to use.\n" + " -t, --type error|warning Set the message type.\n" + " -v, --version Show the version number and quit.\n"; + + while (1) { + int c = getopt_long(argc, argv, "b:de:f:hm:o:t:v", long_options, NULL); + if (c == -1) { + break; + } + switch (c) { + case 'b': // Button + if (optind >= argc) { + fprintf(stderr, "Missing action for button %s", optarg); + exit_code = EXIT_FAILURE; + goto cleanup; + } + struct sway_nagbar_button *button; + button = calloc(sizeof(struct sway_nagbar_button), 1); + button->text = strdup(optarg); + button->action = strdup(argv[optind]); + optind++; + list_add(nagbar.buttons, button); + break; + case 'd': // Debug + debug = true; + break; + case 'e': // Edge + if (strcmp(optarg, "top") == 0) { + nagbar.anchors = ZWLR_LAYER_SURFACE_V1_ANCHOR_TOP + | ZWLR_LAYER_SURFACE_V1_ANCHOR_LEFT + | ZWLR_LAYER_SURFACE_V1_ANCHOR_RIGHT; + } else if (strcmp(optarg, "bottom") == 0) { + nagbar.anchors = ZWLR_LAYER_SURFACE_V1_ANCHOR_BOTTOM + | ZWLR_LAYER_SURFACE_V1_ANCHOR_LEFT + | ZWLR_LAYER_SURFACE_V1_ANCHOR_RIGHT; + } else { + fprintf(stderr, "Invalid edge: %s\n", optarg); + exit_code = EXIT_FAILURE; + goto cleanup; + } + break; + case 'f': // Font + free(nagbar.font); + nagbar.font = strdup(optarg); + break; + case 'm': // Message + free(nagbar.message); + nagbar.message = strdup(optarg); + break; + case 'o': // Output + free(nagbar.output.name); + nagbar.output.name = strdup(optarg); + break; + case 't': // Type + if (strcmp(optarg, "error") == 0) { + nagbar.type = NAGBAR_ERROR; + } else if (strcmp(optarg, "warning") == 0) { + nagbar.type = NAGBAR_WARNING; + } else { + fprintf(stderr, "Type must be either 'error' or 'warning'"); + exit_code = EXIT_FAILURE; + goto cleanup; + } + set_nagbar_colors(); + break; + case 'v': // Version + fprintf(stdout, "sway version " SWAY_VERSION "\n"); + exit_code = EXIT_SUCCESS; + goto cleanup; + default: // Help or unknown flag + fprintf(c == 'h' ? stdout : stderr, "%s", usage); + exit_code = c == 'h' ? EXIT_SUCCESS : EXIT_FAILURE; + goto cleanup; + } + } + + wlr_log_init(debug ? WLR_DEBUG : WLR_ERROR, NULL); + + if (!nagbar.message) { + wlr_log(WLR_ERROR, "No message passed. Please provide --message/-m"); + exit_code = EXIT_FAILURE; + goto cleanup; + } + + wlr_log(WLR_DEBUG, "Output: %s", nagbar.output.name); + wlr_log(WLR_DEBUG, "Anchors: %d", nagbar.anchors); + wlr_log(WLR_DEBUG, "Type: %d", nagbar.type); + wlr_log(WLR_DEBUG, "Message: %s", nagbar.message); + wlr_log(WLR_DEBUG, "Font: %s", nagbar.font); + wlr_log(WLR_DEBUG, "Buttons"); + for (int i = 0; i < nagbar.buttons->length; i++) { + struct sway_nagbar_button *button = nagbar.buttons->items[i]; + wlr_log(WLR_DEBUG, "\t[%s] `%s`", button->text, button->action); + } + + signal(SIGTERM, sig_handler); + + nagbar_setup(&nagbar); + nagbar_run(&nagbar); + return exit_code; + +cleanup: + nagbar_destroy(&nagbar); + return exit_code; +} + diff --git a/swaynagbar/meson.build b/swaynagbar/meson.build new file mode 100644 index 00000000..254462f2 --- /dev/null +++ b/swaynagbar/meson.build @@ -0,0 +1,20 @@ +executable( + 'swaynagbar', [ + 'main.c', + 'nagbar.c', + 'render.c', + ], + include_directories: [sway_inc], + dependencies: [ + cairo, + client_protos, + gdk_pixbuf, + pango, + pangocairo, + wayland_client, + wayland_cursor, + wlroots, + ], + link_with: [lib_sway_common, lib_sway_client], + install: true +) diff --git a/swaynagbar/nagbar.c b/swaynagbar/nagbar.c new file mode 100644 index 00000000..22e5aff4 --- /dev/null +++ b/swaynagbar/nagbar.c @@ -0,0 +1,364 @@ +#define _XOPEN_SOURCE 500 +#include +#include +#include +#include +#include +#include "log.h" +#include "list.h" +#include "swaynagbar/nagbar.h" +#include "swaynagbar/render.h" +#include "wlr-layer-shell-unstable-v1-client-protocol.h" + +static void nop() { + // Intentionally left blank +} + +static bool terminal_execute(char *terminal, char *command) { + char fname[] = "/tmp/swaynagbarXXXXXX"; + FILE *tmp= fdopen(mkstemp(fname), "w"); + if (!tmp) { + wlr_log(WLR_ERROR, "Failed to create temp script"); + return false; + } + wlr_log(WLR_DEBUG, "Created temp script: %s", fname); + fprintf(tmp, "#!/bin/sh\nrm %s\n%s", fname, command); + fclose(tmp); + chmod(fname, S_IRUSR | S_IWUSR | S_IXUSR); + char cmd[strlen(terminal) + strlen(" -e ") + strlen(fname) + 1]; + sprintf(cmd, "%s -e %s", terminal, fname); + execl("/bin/sh", "/bin/sh", "-c", cmd, NULL); + return true; +} + +static void nagbar_button_execute(struct sway_nagbar *nagbar, + struct sway_nagbar_button *button) { + wlr_log(WLR_DEBUG, "Executing [%s]: %s", button->text, button->action); + if (!button->action) { + nagbar->run_display = false; + } else { + if (fork() == 0) { + // Child process. Will be used to prevent zombie processes + setsid(); + if (fork() == 0) { + // Child of the child. Will be reparented to the init process + char *terminal = getenv("TERMINAL"); + if (terminal && strlen(terminal)) { + wlr_log(WLR_DEBUG, "Found $TERMINAL: %s", terminal); + if (!terminal_execute(terminal, button->action)) { + nagbar_destroy(nagbar); + exit(EXIT_FAILURE); + } + } else { + wlr_log(WLR_DEBUG, "$TERMINAL not found. Running directly"); + execl("/bin/sh", "/bin/sh", "-c", button->action, NULL); + } + } + exit(EXIT_SUCCESS); + } + } + wait(0); +} + +static void layer_surface_configure(void *data, + struct zwlr_layer_surface_v1 *surface, + uint32_t serial, uint32_t width, uint32_t height) { + struct sway_nagbar *nagbar = data; + nagbar->width = width; + nagbar->height = height; + zwlr_layer_surface_v1_ack_configure(surface, serial); + render_frame(nagbar); +} + +static void layer_surface_closed(void *data, + struct zwlr_layer_surface_v1 *surface) { + struct sway_nagbar *nagbar = data; + nagbar_destroy(nagbar); +} + +static struct zwlr_layer_surface_v1_listener layer_surface_listener = { + .configure = layer_surface_configure, + .closed = layer_surface_closed, +}; + +static void wl_pointer_enter(void *data, struct wl_pointer *wl_pointer, + uint32_t serial, struct wl_surface *surface, + wl_fixed_t surface_x, wl_fixed_t surface_y) { + struct sway_nagbar *nagbar = data; + struct sway_nagbar_pointer *pointer = &nagbar->pointer; + wl_surface_set_buffer_scale(pointer->cursor_surface, nagbar->scale); + wl_surface_attach(pointer->cursor_surface, + wl_cursor_image_get_buffer(pointer->cursor_image), 0, 0); + wl_pointer_set_cursor(wl_pointer, serial, pointer->cursor_surface, + pointer->cursor_image->hotspot_x / nagbar->scale, + pointer->cursor_image->hotspot_y / nagbar->scale); + wl_surface_commit(pointer->cursor_surface); +} + +static void wl_pointer_motion(void *data, struct wl_pointer *wl_pointer, + uint32_t time, wl_fixed_t surface_x, wl_fixed_t surface_y) { + struct sway_nagbar *nagbar = data; + nagbar->pointer.x = wl_fixed_to_int(surface_x); + nagbar->pointer.y = wl_fixed_to_int(surface_y); +} + +static void wl_pointer_button(void *data, struct wl_pointer *wl_pointer, + uint32_t serial, uint32_t time, uint32_t button, uint32_t state) { + struct sway_nagbar *nagbar = data; + + if (state != WL_POINTER_BUTTON_STATE_PRESSED) { + return; + } + + double x = nagbar->pointer.x * nagbar->scale; + double y = nagbar->pointer.y * nagbar->scale; + for (int i = 0; i < nagbar->buttons->length; i++) { + struct sway_nagbar_button *nagbutton = nagbar->buttons->items[i]; + if (x >= nagbutton->x + && y >= nagbutton->y + && x < nagbutton->x + nagbutton->width + && y < nagbutton->y + nagbutton->height) { + nagbar_button_execute(nagbar, nagbutton); + } + } +} + +static struct wl_pointer_listener pointer_listener = { + .enter = wl_pointer_enter, + .leave = nop, + .motion = wl_pointer_motion, + .button = wl_pointer_button, + .axis = nop, + .frame = nop, + .axis_source = nop, + .axis_stop = nop, + .axis_discrete = nop, +}; + +static void seat_handle_capabilities(void *data, struct wl_seat *wl_seat, + enum wl_seat_capability caps) { + struct sway_nagbar *nagbar = data; + if ((caps & WL_SEAT_CAPABILITY_POINTER)) { + nagbar->pointer.pointer = wl_seat_get_pointer(wl_seat); + wl_pointer_add_listener(nagbar->pointer.pointer, &pointer_listener, + nagbar); + } +} + +const struct wl_seat_listener seat_listener = { + .capabilities = seat_handle_capabilities, + .name = nop, +}; + +static void output_scale(void *data, struct wl_output *output, + int32_t factor) { + struct sway_nagbar *nagbar = data; + nagbar->scale = factor; + render_frame(nagbar); +} + +static struct wl_output_listener output_listener = { + .geometry = nop, + .mode = nop, + .done = nop, + .scale = output_scale, +}; + +struct output_state { + struct wl_output *wl_output; + uint32_t wl_name; + struct zxdg_output_v1 *xdg_output; + struct sway_nagbar *nagbar; +}; + +static void xdg_output_handle_name(void *data, + struct zxdg_output_v1 *xdg_output, const char *name) { + struct output_state *state = data; + char *outname = state->nagbar->output.name; + wlr_log(WLR_DEBUG, "Checking against output %s for %s", name, outname); + if ((!outname && !state->nagbar->output.wl_output) + || (name && outname && strcmp(name, outname) == 0)) { + wlr_log(WLR_DEBUG, "Using output %s", name); + state->nagbar->output.wl_output = state->wl_output; + state->nagbar->output.wl_name = state->wl_name; + wl_output_add_listener(state->nagbar->output.wl_output, + &output_listener, state->nagbar); + wl_display_roundtrip(state->nagbar->display); + zxdg_output_v1_destroy(state->xdg_output); + } else { + zxdg_output_v1_destroy(state->xdg_output); + wl_output_destroy(state->wl_output); + } + state->nagbar->querying_outputs--; + free(state); +} + +static struct zxdg_output_v1_listener xdg_output_listener = { + .logical_position = nop, + .logical_size = nop, + .done = nop, + .name = xdg_output_handle_name, + .description = nop, +}; + +static void handle_global(void *data, struct wl_registry *registry, + uint32_t name, const char *interface, uint32_t version) { + struct sway_nagbar *nagbar = data; + if (strcmp(interface, wl_compositor_interface.name) == 0) { + nagbar->compositor = wl_registry_bind(registry, name, + &wl_compositor_interface, 3); + } else if (strcmp(interface, wl_seat_interface.name) == 0) { + nagbar->seat = wl_registry_bind(registry, name, &wl_seat_interface, 1); + wl_seat_add_listener(nagbar->seat, &seat_listener, nagbar); + } else if (strcmp(interface, wl_shm_interface.name) == 0) { + nagbar->shm = wl_registry_bind(registry, name, &wl_shm_interface, 1); + } else if (strcmp(interface, wl_output_interface.name) == 0) { + if (!nagbar->output.wl_output && nagbar->xdg_output_manager) { + nagbar->querying_outputs++; + struct output_state *state = + calloc(1, sizeof(struct output_state)); + state->nagbar = nagbar; + state->wl_output = wl_registry_bind(registry, name, + &wl_output_interface, 3); + state->wl_name = name; + state->xdg_output = zxdg_output_manager_v1_get_xdg_output( + nagbar->xdg_output_manager, state->wl_output); + zxdg_output_v1_add_listener(state->xdg_output, + &xdg_output_listener, state); + } else if (!nagbar->output.wl_output && !nagbar->xdg_output_manager) { + wlr_log(WLR_ERROR, "Warning: zxdg_output_manager_v1 not supported." + " Falling back to first detected output"); + nagbar->output.wl_output = wl_registry_bind(registry, name, + &wl_output_interface, 3); + nagbar->output.wl_name = name; + wl_output_add_listener(nagbar->output.wl_output, + &output_listener, nagbar); + } + } else if (strcmp(interface, zwlr_layer_shell_v1_interface.name) == 0) { + nagbar->layer_shell = wl_registry_bind( + registry, name, &zwlr_layer_shell_v1_interface, 1); + } else if (strcmp(interface, zxdg_output_manager_v1_interface.name) == 0 + && version >= ZXDG_OUTPUT_V1_NAME_SINCE_VERSION) { + nagbar->xdg_output_manager = wl_registry_bind(registry, name, + &zxdg_output_manager_v1_interface, + ZXDG_OUTPUT_V1_NAME_SINCE_VERSION); + } +} + +static void handle_global_remove(void *data, struct wl_registry *registry, + uint32_t name) { + struct sway_nagbar *nagbar = data; + if (nagbar->output.wl_name == name) { + nagbar->run_display = false; + } +} + +static const struct wl_registry_listener registry_listener = { + .global = handle_global, + .global_remove = handle_global_remove, +}; + +void nagbar_setup(struct sway_nagbar *nagbar) { + nagbar->display = wl_display_connect(NULL); + assert(nagbar->display); + + nagbar->scale = 1; + + struct wl_registry *registry = wl_display_get_registry(nagbar->display); + wl_registry_add_listener(registry, ®istry_listener, nagbar); + wl_display_roundtrip(nagbar->display); + assert(nagbar->compositor && nagbar->layer_shell && nagbar->shm); + + while (nagbar->querying_outputs > 0) { + wl_display_roundtrip(nagbar->display); + } + + if (!nagbar->output.wl_output) { + if (nagbar->output.name) { + wlr_log(WLR_ERROR, "Output '%s' not found", nagbar->output.name); + } else { + wlr_log(WLR_ERROR, "No outputs detected"); + } + nagbar_destroy(nagbar); + exit(EXIT_FAILURE); + } + + struct sway_nagbar_pointer *pointer = &nagbar->pointer; + int scale = nagbar->scale < 1 ? 1 : nagbar->scale; + pointer->cursor_theme = wl_cursor_theme_load( + NULL, 24 * scale, nagbar->shm); + assert(pointer->cursor_theme); + struct wl_cursor *cursor = + wl_cursor_theme_get_cursor(pointer->cursor_theme, "left_ptr"); + assert(cursor); + pointer->cursor_image = cursor->images[0]; + pointer->cursor_surface = wl_compositor_create_surface(nagbar->compositor); + assert(pointer->cursor_surface); + + nagbar->surface = wl_compositor_create_surface(nagbar->compositor); + assert(nagbar->surface); + nagbar->layer_surface = zwlr_layer_shell_v1_get_layer_surface( + nagbar->layer_shell, nagbar->surface, nagbar->output.wl_output, + ZWLR_LAYER_SHELL_V1_LAYER_BOTTOM, "nagbar"); + assert(nagbar->layer_surface); + zwlr_layer_surface_v1_add_listener(nagbar->layer_surface, + &layer_surface_listener, nagbar); + zwlr_layer_surface_v1_set_anchor(nagbar->layer_surface, nagbar->anchors); + + wl_registry_destroy(registry); +} + +void nagbar_run(struct sway_nagbar *nagbar) { + nagbar->run_display = true; + render_frame(nagbar); + while (nagbar->run_display && wl_display_dispatch(nagbar->display) != -1) { + // This is intentionally left blank + } +} + +void nagbar_destroy(struct sway_nagbar *nagbar) { + nagbar->run_display = false; + + free(nagbar->message); + free(nagbar->font); + while (nagbar->buttons->length) { + struct sway_nagbar_button *button = nagbar->buttons->items[0]; + list_del(nagbar->buttons, 0); + free(button->text); + free(button->action); + free(button); + } + list_free(nagbar->buttons); + + if (nagbar->layer_surface) { + zwlr_layer_surface_v1_destroy(nagbar->layer_surface); + } + + if (nagbar->surface) { + wl_surface_destroy(nagbar->surface); + } + + if (nagbar->output.wl_output) { + wl_output_destroy(nagbar->output.wl_output); + } + + if (&nagbar->buffers[0]) { + destroy_buffer(&nagbar->buffers[0]); + } + + if (&nagbar->buffers[1]) { + destroy_buffer(&nagbar->buffers[1]); + } + + if (nagbar->compositor) { + wl_compositor_destroy(nagbar->compositor); + } + + if (nagbar->shm) { + wl_shm_destroy(nagbar->shm); + } + + if (nagbar->display) { + wl_display_disconnect(nagbar->display); + } +} diff --git a/swaynagbar/render.c b/swaynagbar/render.c new file mode 100644 index 00000000..c0f59298 --- /dev/null +++ b/swaynagbar/render.c @@ -0,0 +1,152 @@ +#include +#include "cairo.h" +#include "log.h" +#include "pango.h" +#include "pool-buffer.h" +#include "swaynagbar/nagbar.h" +#include "wlr-layer-shell-unstable-v1-client-protocol.h" + +static uint32_t render_message(cairo_t *cairo, struct sway_nagbar *nagbar) { + uint32_t height = nagbar->height * nagbar->scale; + height -= NAGBAR_BAR_BORDER_THICKNESS * nagbar->scale; + + int text_width, text_height; + get_text_size(cairo, nagbar->font, &text_width, &text_height, + nagbar->scale, true, "%s", nagbar->message); + + int padding = NAGBAR_MESSAGE_PADDING * nagbar->scale; + + uint32_t ideal_height = text_height + padding * 2; + uint32_t ideal_surface_height = ideal_height / nagbar->scale; + if (nagbar->height < ideal_surface_height) { + return ideal_surface_height; + } + + cairo_set_source_u32(cairo, nagbar->colors.text); + cairo_move_to(cairo, padding, (int)(height / 2.0 - text_height / 2.0)); + pango_printf(cairo, nagbar->font, nagbar->scale, true, "%s", + nagbar->message); + + return nagbar->height; +} + +static uint32_t render_button(cairo_t *cairo, struct sway_nagbar *nagbar, + int button_index, int *x) { + uint32_t height = nagbar->height * nagbar->scale; + height -= NAGBAR_BAR_BORDER_THICKNESS * nagbar->scale; + struct sway_nagbar_button *button = nagbar->buttons->items[button_index]; + + int text_width, text_height; + get_text_size(cairo, nagbar->font, &text_width, &text_height, + nagbar->scale, true, "%s", button->text); + + int border = NAGBAR_BUTTON_BORDER_THICKNESS * nagbar->scale; + int padding = NAGBAR_BUTTON_PADDING * nagbar->scale; + + uint32_t ideal_height = text_height + padding * 2 + border * 2; + uint32_t ideal_surface_height = ideal_height / nagbar->scale; + if (nagbar->height < ideal_surface_height) { + return ideal_surface_height; + } + + button->x = *x - border - text_width - padding * 2; + button->y = (int)(height / 2.0 - text_height / 2.0) - padding; + button->width = text_width + padding * 2; + button->height = text_height + padding * 2; + + cairo_set_source_u32(cairo, nagbar->colors.border); + cairo_rectangle(cairo, button->x - border, button->y - border, + button->width + border * 2, button->height + border * 2); + cairo_fill(cairo); + + cairo_set_source_u32(cairo, nagbar->colors.button_background); + cairo_rectangle(cairo, button->x, button->y, + button->width, button->height); + cairo_fill(cairo); + + cairo_set_source_u32(cairo, nagbar->colors.text); + cairo_move_to(cairo, button->x + padding, button->y + padding); + pango_printf(cairo, nagbar->font, nagbar->scale, true, "%s", button->text); + + *x = button->x - border; + + return nagbar->height; +} + +static uint32_t render_to_cairo(cairo_t *cairo, struct sway_nagbar *nagbar) { + uint32_t max_height = 0; + + cairo_set_operator(cairo, CAIRO_OPERATOR_SOURCE); + cairo_set_source_u32(cairo, nagbar->colors.background); + cairo_paint(cairo); + + uint32_t h = render_message(cairo, nagbar); + max_height = h > max_height ? h : max_height; + + int x = (nagbar->width - NAGBAR_BUTTON_MARGIN_RIGHT) * nagbar->scale; + for (int i = 0; i < nagbar->buttons->length; i++) { + h = render_button(cairo, nagbar, i, &x); + max_height = h > max_height ? h : max_height; + x -= NAGBAR_BUTTON_GAP * nagbar->scale; + if (i == 0) { + x -= NAGBAR_BUTTON_GAP_CLOSE * nagbar->scale; + } + } + + int border = NAGBAR_BAR_BORDER_THICKNESS * nagbar->scale; + if (max_height > nagbar->height) { + max_height += border; + } + cairo_set_source_u32(cairo, nagbar->colors.border_bottom); + cairo_rectangle(cairo, 0, nagbar->height * nagbar->scale - border, + nagbar->width * nagbar->scale, border); + cairo_fill(cairo); + + return max_height > nagbar->height ? max_height : nagbar->height; +} + +void render_frame(struct sway_nagbar *nagbar) { + if (!nagbar->run_display) { + return; + } + + cairo_surface_t *recorder = cairo_recording_surface_create( + CAIRO_CONTENT_COLOR_ALPHA, NULL); + cairo_t *cairo = cairo_create(recorder); + cairo_save(cairo); + cairo_set_operator(cairo, CAIRO_OPERATOR_CLEAR); + cairo_paint(cairo); + cairo_restore(cairo); + uint32_t height = render_to_cairo(cairo, nagbar); + if (height != nagbar->height) { + zwlr_layer_surface_v1_set_size(nagbar->layer_surface, 0, height); + zwlr_layer_surface_v1_set_exclusive_zone(nagbar->layer_surface, + height); + wl_surface_commit(nagbar->surface); + wl_display_roundtrip(nagbar->display); + } else { + nagbar->current_buffer = get_next_buffer(nagbar->shm, + nagbar->buffers, + nagbar->width * nagbar->scale, + nagbar->height * nagbar->scale); + cairo_t *shm = nagbar->current_buffer->cairo; + + cairo_save(shm); + cairo_set_operator(shm, CAIRO_OPERATOR_CLEAR); + cairo_paint(shm); + cairo_restore(shm); + + cairo_set_source_surface(shm, recorder, 0.0, 0.0); + cairo_paint(shm); + + wl_surface_set_buffer_scale(nagbar->surface, nagbar->scale); + wl_surface_attach(nagbar->surface, + nagbar->current_buffer->buffer, 0, 0); + wl_surface_damage(nagbar->surface, 0, 0, + nagbar->width, nagbar->height); + wl_surface_commit(nagbar->surface); + wl_display_roundtrip(nagbar->display); + } + cairo_surface_destroy(recorder); + cairo_destroy(cairo); +} diff --git a/swaynagbar/swaynagbar.1.scd b/swaynagbar/swaynagbar.1.scd new file mode 100644 index 00000000..4235e2ea --- /dev/null +++ b/swaynagbar/swaynagbar.1.scd @@ -0,0 +1,41 @@ +swaynagbar(1) + +# NAME + +swaynagbar - Show a warning or error message with buttons + +# SYNOPSIS + +_swaynagbar_ [options...] + +# OPTIONS +*-b, --button* + Create a button with the text _text_ that executes _action_ when pressed. + Multiple buttons can be defined by providing the flag multiple times. + +*-d, --debug* + Enable debugging. + +*-e, --edge top|bottom* + Set the edge to use. + +*-f, --font * + Set the font to use. + +*-h, --help* + Show help message and quit. + +*-m, --message * + Set the message text. + +*-o, --output * + Set the output to use. This should be the name of a _xdg\_output_. If + _xdg\_output\_manager_ is not supported, then the first detected output + will be used + +*-t, --type error|warning* + Set the message type. + +*-v, --version + Show the version number and quit. + From 72db10c2f1a1a216c50f68461a840eea3948e878 Mon Sep 17 00:00:00 2001 From: Brian Ashworth Date: Fri, 27 Jul 2018 01:30:35 -0400 Subject: [PATCH 124/148] Support a detailed message in swaynagbar --- include/swaynagbar/nagbar.h | 27 ++++++ swaynagbar/main.c | 76 +++++++++++++++-- swaynagbar/nagbar.c | 60 +++++++++++++- swaynagbar/render.c | 160 ++++++++++++++++++++++++++++++++++-- swaynagbar/swaynagbar.1.scd | 12 +++ 5 files changed, 321 insertions(+), 14 deletions(-) diff --git a/include/swaynagbar/nagbar.h b/include/swaynagbar/nagbar.h index 07a0d51e..8b55e4fa 100644 --- a/include/swaynagbar/nagbar.h +++ b/include/swaynagbar/nagbar.h @@ -7,17 +7,26 @@ #define NAGBAR_BAR_BORDER_THICKNESS 2 #define NAGBAR_MESSAGE_PADDING 8 +#define NAGBAR_DETAILS_BORDER_THICKNESS 3 #define NAGBAR_BUTTON_BORDER_THICKNESS 3 #define NAGBAR_BUTTON_GAP 20 #define NAGBAR_BUTTON_GAP_CLOSE 15 #define NAGBAR_BUTTON_MARGIN_RIGHT 2 #define NAGBAR_BUTTON_PADDING 3 +#define NAGBAR_MAX_HEIGHT 500 + enum sway_nagbar_type { NAGBAR_ERROR, NAGBAR_WARNING, }; +enum sway_nagbar_action_type { + NAGBAR_ACTION_DISMISS, + NAGBAR_ACTION_EXPAND, + NAGBAR_ACTION_COMMAND, +}; + struct sway_nagbar_colors { uint32_t button_background; uint32_t background; @@ -43,6 +52,7 @@ struct sway_nagbar_output { struct sway_nagbar_button { char *text; + enum sway_nagbar_action_type type; char *action; int x; int y; @@ -50,6 +60,22 @@ struct sway_nagbar_button { int height; }; +struct sway_nagbar_details { + bool visible; + char *message; + + int x; + int y; + int width; + int height; + + int offset; + int visible_lines; + int total_lines; + struct sway_nagbar_button button_up; + struct sway_nagbar_button button_down; +}; + struct sway_nagbar { bool run_display; int querying_outputs; @@ -77,6 +103,7 @@ struct sway_nagbar { char *message; char *font; list_t *buttons; + struct sway_nagbar_details details; }; void nagbar_setup(struct sway_nagbar *nagbar); diff --git a/swaynagbar/main.c b/swaynagbar/main.c index f5ed064e..389118c6 100644 --- a/swaynagbar/main.c +++ b/swaynagbar/main.c @@ -3,6 +3,7 @@ #include #include "log.h" #include "list.h" +#include "readline.h" #include "swaynagbar/nagbar.h" #include "wlr-layer-shell-unstable-v1-client-protocol.h" @@ -34,6 +35,32 @@ static void set_nagbar_colors() { } } +static char *read_from_stdin() { + char *buffer = NULL; + while (!feof(stdin)) { + char *line = read_line(stdin); + if (!line) { + continue; + } + + if (!buffer) { + buffer = strdup(line); + } else { + buffer = realloc(buffer, strlen(buffer) + strlen(line) + 2); + strcat(buffer, line); + strcat(buffer, "\n"); + } + + free(line); + } + + if (buffer && buffer[strlen(buffer) - 1] == '\n') { + buffer[strlen(buffer) - 1] = '\0'; + } + + return buffer; +} + int main(int argc, char **argv) { int exit_code = EXIT_SUCCESS; bool debug = false; @@ -50,17 +77,25 @@ int main(int argc, char **argv) { struct sway_nagbar_button *button_close = calloc(sizeof(struct sway_nagbar_button), 1); button_close->text = strdup("X"); - button_close->action = NULL; + button_close->type = NAGBAR_ACTION_DISMISS; list_add(nagbar.buttons, button_close); - static struct option long_options[] = { + struct sway_nagbar_button *button_details = + calloc(sizeof(struct sway_nagbar_button), 1); + button_details->text = strdup("Toggle Details"); + button_details->type = NAGBAR_ACTION_EXPAND; + + static struct option opts[] = { {"button", required_argument, NULL, 'b'}, {"debug", no_argument, NULL, 'd'}, {"edge", required_argument, NULL, 'e'}, {"font", required_argument, NULL, 'f'}, {"help", no_argument, NULL, 'h'}, + {"detailed-message", required_argument, NULL, 'l'}, + {"detailed-button", required_argument, NULL, 'L'}, {"message", required_argument, NULL, 'm'}, {"output", required_argument, NULL, 'o'}, + {"dismiss-button", required_argument, NULL, 's'}, {"type", required_argument, NULL, 't'}, {"version", no_argument, NULL, 'v'}, {0, 0, 0, 0} @@ -75,26 +110,30 @@ int main(int argc, char **argv) { " -e, --edge top|bottom Set the edge to use.\n" " -f, --font Set the font to use.\n" " -h, --help Show help message and quit.\n" + " -l, --detailed-message Set a detailed message.\n" + " -L, --detailed-button Set the text of the detail button.\n" " -m, --message Set the message text.\n" " -o, --output Set the output to use.\n" + " -s, --dismiss-button Set the dismiss button text.\n" " -t, --type error|warning Set the message type.\n" " -v, --version Show the version number and quit.\n"; while (1) { - int c = getopt_long(argc, argv, "b:de:f:hm:o:t:v", long_options, NULL); + int c = getopt_long(argc, argv, "b:de:f:hl:L:m:o:s:t:v", opts, NULL); if (c == -1) { break; } switch (c) { case 'b': // Button if (optind >= argc) { - fprintf(stderr, "Missing action for button %s", optarg); + fprintf(stderr, "Missing action for button %s\n", optarg); exit_code = EXIT_FAILURE; goto cleanup; } struct sway_nagbar_button *button; button = calloc(sizeof(struct sway_nagbar_button), 1); button->text = strdup(optarg); + button->type = NAGBAR_ACTION_COMMAND; button->action = strdup(argv[optind]); optind++; list_add(nagbar.buttons, button); @@ -121,6 +160,20 @@ int main(int argc, char **argv) { free(nagbar.font); nagbar.font = strdup(optarg); break; + case 'l': // Detailed Message + free(nagbar.details.message); + if (strcmp(optarg, "-") == 0) { + nagbar.details.message = read_from_stdin(); + } else { + nagbar.details.message = strdup(optarg); + } + nagbar.details.button_up.text = strdup("▲"); + nagbar.details.button_down.text = strdup("▼"); + break; + case 'L': // Detailed Button Text + free(button_details->text); + button_details->text = strdup(optarg); + break; case 'm': // Message free(nagbar.message); nagbar.message = strdup(optarg); @@ -129,13 +182,17 @@ int main(int argc, char **argv) { free(nagbar.output.name); nagbar.output.name = strdup(optarg); break; + case 's': // Dismiss Button Text + free(button_close->text); + button_close->text = strdup(optarg); + break; case 't': // Type if (strcmp(optarg, "error") == 0) { nagbar.type = NAGBAR_ERROR; } else if (strcmp(optarg, "warning") == 0) { nagbar.type = NAGBAR_WARNING; } else { - fprintf(stderr, "Type must be either 'error' or 'warning'"); + fprintf(stderr, "Type must be either 'error' or 'warning'\n"); exit_code = EXIT_FAILURE; goto cleanup; } @@ -160,6 +217,13 @@ int main(int argc, char **argv) { goto cleanup; } + if (nagbar.details.message) { + list_add(nagbar.buttons, button_details); + } else { + free(button_details->text); + free(button_details); + } + wlr_log(WLR_DEBUG, "Output: %s", nagbar.output.name); wlr_log(WLR_DEBUG, "Anchors: %d", nagbar.anchors); wlr_log(WLR_DEBUG, "Type: %d", nagbar.type); @@ -178,6 +242,8 @@ int main(int argc, char **argv) { return exit_code; cleanup: + free(button_details->text); + free(button_details); nagbar_destroy(&nagbar); return exit_code; } diff --git a/swaynagbar/nagbar.c b/swaynagbar/nagbar.c index 22e5aff4..9e8bbb4b 100644 --- a/swaynagbar/nagbar.c +++ b/swaynagbar/nagbar.c @@ -34,8 +34,11 @@ static bool terminal_execute(char *terminal, char *command) { static void nagbar_button_execute(struct sway_nagbar *nagbar, struct sway_nagbar_button *button) { wlr_log(WLR_DEBUG, "Executing [%s]: %s", button->text, button->action); - if (!button->action) { + if (button->type == NAGBAR_ACTION_DISMISS) { nagbar->run_display = false; + } else if (button->type == NAGBAR_ACTION_EXPAND) { + nagbar->details.visible = !nagbar->details.visible; + render_frame(nagbar); } else { if (fork() == 0) { // Child process. Will be used to prevent zombie processes @@ -119,8 +122,58 @@ static void wl_pointer_button(void *data, struct wl_pointer *wl_pointer, && x < nagbutton->x + nagbutton->width && y < nagbutton->y + nagbutton->height) { nagbar_button_execute(nagbar, nagbutton); + return; } } + + if (nagbar->details.visible && + nagbar->details.total_lines != nagbar->details.visible_lines) { + struct sway_nagbar_button button_up = nagbar->details.button_up; + if (x >= button_up.x + && y >= button_up.y + && x < button_up.x + button_up.width + && y < button_up.y + button_up.height + && nagbar->details.offset > 0) { + nagbar->details.offset--; + render_frame(nagbar); + return; + } + + struct sway_nagbar_button button_down = nagbar->details.button_down; + int bot = nagbar->details.total_lines - nagbar->details.visible_lines; + if (x >= button_down.x + && y >= button_down.y + && x < button_down.x + button_down.width + && y < button_down.y + button_down.height + && nagbar->details.offset < bot) { + nagbar->details.offset++; + render_frame(nagbar); + return; + } + } +} + +static void wl_pointer_axis(void *data, struct wl_pointer *wl_pointer, + uint32_t time, uint32_t axis, wl_fixed_t value) { + struct sway_nagbar *nagbar = data; + if (!nagbar->details.visible + || nagbar->pointer.x < nagbar->details.x + || nagbar->pointer.y < nagbar->details.y + || nagbar->pointer.x >= nagbar->details.x + nagbar->details.width + || nagbar->pointer.y >= nagbar->details.y + nagbar->details.height + || nagbar->details.total_lines == nagbar->details.visible_lines) { + return; + } + + int direction = wl_fixed_to_int(value); + int bot = nagbar->details.total_lines - nagbar->details.visible_lines; + if (direction < 0 && nagbar->details.offset > 0) { + nagbar->details.offset--; + } else if (direction > 0 && nagbar->details.offset < bot) { + nagbar->details.offset++; + } + + render_frame(nagbar); } static struct wl_pointer_listener pointer_listener = { @@ -128,7 +181,7 @@ static struct wl_pointer_listener pointer_listener = { .leave = nop, .motion = wl_pointer_motion, .button = wl_pointer_button, - .axis = nop, + .axis = wl_pointer_axis, .frame = nop, .axis_source = nop, .axis_stop = nop, @@ -329,6 +382,9 @@ void nagbar_destroy(struct sway_nagbar *nagbar) { free(button); } list_free(nagbar->buttons); + free(nagbar->details.message); + free(nagbar->details.button_up.text); + free(nagbar->details.button_down.text); if (nagbar->layer_surface) { zwlr_layer_surface_v1_destroy(nagbar->layer_surface); diff --git a/swaynagbar/render.c b/swaynagbar/render.c index c0f59298..7bc2961e 100644 --- a/swaynagbar/render.c +++ b/swaynagbar/render.c @@ -23,11 +23,149 @@ static uint32_t render_message(cairo_t *cairo, struct sway_nagbar *nagbar) { } cairo_set_source_u32(cairo, nagbar->colors.text); - cairo_move_to(cairo, padding, (int)(height / 2.0 - text_height / 2.0)); + cairo_move_to(cairo, padding, (int)(ideal_height - text_height) / 2); pango_printf(cairo, nagbar->font, nagbar->scale, true, "%s", nagbar->message); - return nagbar->height; + return ideal_height; +} + +static void render_details_scroll_button(cairo_t *cairo, + struct sway_nagbar *nagbar, struct sway_nagbar_button *button) { + int text_width, text_height; + get_text_size(cairo, nagbar->font, &text_width, &text_height, + nagbar->scale, true, "%s", button->text); + + int border = NAGBAR_BUTTON_BORDER_THICKNESS * nagbar->scale; + int padding = NAGBAR_BUTTON_PADDING * nagbar->scale; + + cairo_set_source_u32(cairo, nagbar->colors.border); + cairo_rectangle(cairo, button->x, button->y, + button->width, button->height); + cairo_fill(cairo); + + cairo_set_source_u32(cairo, nagbar->colors.button_background); + cairo_rectangle(cairo, button->x + border, button->y + border, + button->width - (border * 2), button->height - (border * 2)); + cairo_fill(cairo); + + cairo_set_source_u32(cairo, nagbar->colors.text); + cairo_move_to(cairo, button->x + border + padding, + button->y + border + (button->height - text_height) / 2); + pango_printf(cairo, nagbar->font, nagbar->scale, true, "%s", button->text); +} + +static int get_detailed_scroll_button_width(cairo_t *cairo, + struct sway_nagbar *nagbar) { + int up_width, down_width, temp_height; + get_text_size(cairo, nagbar->font, &up_width, &temp_height, + nagbar->scale, true, "%s", nagbar->details.button_up.text); + get_text_size(cairo, nagbar->font, &down_width, &temp_height, + nagbar->scale, true, "%s", nagbar->details.button_down.text); + + int text_width = up_width > down_width ? up_width : down_width; + int border = NAGBAR_BUTTON_BORDER_THICKNESS * nagbar->scale; + int padding = NAGBAR_BUTTON_PADDING * nagbar->scale; + + return text_width + border * 2 + padding * 2; +} + +static uint32_t render_detailed(cairo_t *cairo, struct sway_nagbar *nagbar, + uint32_t y) { + uint32_t width = nagbar->width * nagbar->scale; + uint32_t height = nagbar->height * nagbar->scale; + height -= NAGBAR_BAR_BORDER_THICKNESS * nagbar->scale; + + int border = NAGBAR_DETAILS_BORDER_THICKNESS * nagbar->scale; + int padding = NAGBAR_MESSAGE_PADDING * nagbar->scale; + int decor = padding + border; + + nagbar->details.x = decor; + nagbar->details.y = y + decor; + nagbar->details.width = width - decor * 2; + + PangoLayout *layout = get_pango_layout(cairo, nagbar->font, + nagbar->details.message, nagbar->scale, true); + pango_layout_set_width(layout, + (nagbar->details.width - padding * 2) * PANGO_SCALE); + pango_layout_set_wrap(layout, PANGO_WRAP_WORD_CHAR); + pango_layout_set_single_paragraph_mode(layout, false); + pango_cairo_update_layout(cairo, layout); + nagbar->details.total_lines = pango_layout_get_line_count(layout); + + PangoLayoutLine *line; + line = pango_layout_get_line_readonly(layout, nagbar->details.offset); + gint offset = line->start_index; + const char *text = pango_layout_get_text(layout); + pango_layout_set_text(layout, text + offset, strlen(text) - offset); + + int text_width, text_height; + pango_cairo_update_layout(cairo, layout); + pango_layout_get_pixel_size(layout, &text_width, &text_height); + + bool show_buttons = nagbar->details.offset > 0; + int button_width = get_detailed_scroll_button_width(cairo, nagbar); + if (show_buttons) { + nagbar->details.width -= button_width; + pango_layout_set_width(layout, + (nagbar->details.width - padding * 2) * PANGO_SCALE); + } + + uint32_t ideal_height; + do { + ideal_height = nagbar->details.y + text_height + decor + padding * 2; + if (ideal_height > NAGBAR_MAX_HEIGHT) { + ideal_height = NAGBAR_MAX_HEIGHT; + + if (!show_buttons) { + show_buttons = true; + nagbar->details.width -= button_width; + pango_layout_set_width(layout, + (nagbar->details.width - padding * 2) * PANGO_SCALE); + } + } + + nagbar->details.height = ideal_height - nagbar->details.y - decor; + pango_layout_set_height(layout, + (nagbar->details.height - padding * 2) * PANGO_SCALE); + pango_layout_set_ellipsize(layout, PANGO_ELLIPSIZE_END); + pango_cairo_update_layout(cairo, layout); + pango_layout_get_pixel_size(layout, &text_width, &text_height); + } while (text_height != (nagbar->details.height - padding * 2)); + + nagbar->details.visible_lines = pango_layout_get_line_count(layout); + + if (show_buttons) { + nagbar->details.button_up.x = + nagbar->details.x + nagbar->details.width; + nagbar->details.button_up.y = nagbar->details.y; + nagbar->details.button_up.width = button_width; + nagbar->details.button_up.height = nagbar->details.height / 2; + render_details_scroll_button(cairo, nagbar, + &nagbar->details.button_up); + + nagbar->details.button_down.x = + nagbar->details.x + nagbar->details.width; + nagbar->details.button_down.y = + nagbar->details.button_up.y + nagbar->details.button_up.height; + nagbar->details.button_down.width = button_width; + nagbar->details.button_down.height = nagbar->details.height / 2; + render_details_scroll_button(cairo, nagbar, + &nagbar->details.button_down); + } + + cairo_set_source_u32(cairo, nagbar->colors.border); + cairo_rectangle(cairo, nagbar->details.x, nagbar->details.y, + nagbar->details.width, nagbar->details.height); + cairo_fill(cairo); + + cairo_move_to(cairo, nagbar->details.x + padding, + nagbar->details.y + padding); + cairo_set_source_u32(cairo, nagbar->colors.text); + pango_cairo_show_layout(cairo, layout); + g_object_unref(layout); + + return ideal_height; } static uint32_t render_button(cairo_t *cairo, struct sway_nagbar *nagbar, @@ -50,7 +188,7 @@ static uint32_t render_button(cairo_t *cairo, struct sway_nagbar *nagbar, } button->x = *x - border - text_width - padding * 2; - button->y = (int)(height / 2.0 - text_height / 2.0) - padding; + button->y = (int)(ideal_height - text_height) / 2 - padding; button->width = text_width + padding * 2; button->height = text_height + padding * 2; @@ -70,7 +208,7 @@ static uint32_t render_button(cairo_t *cairo, struct sway_nagbar *nagbar, *x = button->x - border; - return nagbar->height; + return ideal_height; } static uint32_t render_to_cairo(cairo_t *cairo, struct sway_nagbar *nagbar) { @@ -93,6 +231,11 @@ static uint32_t render_to_cairo(cairo_t *cairo, struct sway_nagbar *nagbar) { } } + if (nagbar->details.visible) { + h = render_detailed(cairo, nagbar, max_height); + max_height = h > max_height ? h : max_height; + } + int border = NAGBAR_BAR_BORDER_THICKNESS * nagbar->scale; if (max_height > nagbar->height) { max_height += border; @@ -102,7 +245,7 @@ static uint32_t render_to_cairo(cairo_t *cairo, struct sway_nagbar *nagbar) { nagbar->width * nagbar->scale, border); cairo_fill(cairo); - return max_height > nagbar->height ? max_height : nagbar->height; + return max_height; } void render_frame(struct sway_nagbar *nagbar) { @@ -129,13 +272,16 @@ void render_frame(struct sway_nagbar *nagbar) { nagbar->buffers, nagbar->width * nagbar->scale, nagbar->height * nagbar->scale); - cairo_t *shm = nagbar->current_buffer->cairo; + if (!nagbar->current_buffer) { + wlr_log(WLR_DEBUG, "Failed to get buffer. Skipping frame."); + return; + } + cairo_t *shm = nagbar->current_buffer->cairo; cairo_save(shm); cairo_set_operator(shm, CAIRO_OPERATOR_CLEAR); cairo_paint(shm); cairo_restore(shm); - cairo_set_source_surface(shm, recorder, 0.0, 0.0); cairo_paint(shm); diff --git a/swaynagbar/swaynagbar.1.scd b/swaynagbar/swaynagbar.1.scd index 4235e2ea..2a34ae68 100644 --- a/swaynagbar/swaynagbar.1.scd +++ b/swaynagbar/swaynagbar.1.scd @@ -25,6 +25,15 @@ _swaynagbar_ [options...] *-h, --help* Show help message and quit. +*-l, --detailed-message * + Set the detailed message. A button to toggle details will be added. Details + are shown in a scrollable multi-line text area. If _msg_ is _-_, then the + detailed message will be read from stdin. + +*-L, --detailed-button * + Set the text for the button that toggles details. This has no effect if + there is not a detailed message. The default is _Toggle Details_. + *-m, --message * Set the message text. @@ -33,6 +42,9 @@ _swaynagbar_ [options...] _xdg\_output\_manager_ is not supported, then the first detected output will be used +*-s, --dismiss-button * + Sets the text for the dismiss nagbar button. The default is _X_. + *-t, --type error|warning* Set the message type. From a4f7bf23b21d0d838a8a19261d5fd69719003a03 Mon Sep 17 00:00:00 2001 From: Brian Ashworth Date: Fri, 27 Jul 2018 11:19:42 -0400 Subject: [PATCH 125/148] Address first round review for swaynag --- include/{swaynagbar => swaynag}/nagbar.h | 0 include/{swaynagbar => swaynag}/render.h | 0 meson.build | 4 ++-- sway/desktop/layer_shell.c | 8 +++++--- {swaynagbar => swaynag}/main.c | 20 ++++++++----------- {swaynagbar => swaynag}/meson.build | 2 +- {swaynagbar => swaynag}/nagbar.c | 8 ++++---- {swaynagbar => swaynag}/render.c | 6 ++++-- .../swaynagbar.1.scd => swaynag/swaynag.1.scd | 11 +++++----- 9 files changed, 29 insertions(+), 30 deletions(-) rename include/{swaynagbar => swaynag}/nagbar.h (100%) rename include/{swaynagbar => swaynag}/render.h (100%) rename {swaynagbar => swaynag}/main.c (93%) rename {swaynagbar => swaynag}/meson.build (94%) rename {swaynagbar => swaynag}/nagbar.c (98%) rename {swaynagbar => swaynag}/render.c (99%) rename swaynagbar/swaynagbar.1.scd => swaynag/swaynag.1.scd (76%) diff --git a/include/swaynagbar/nagbar.h b/include/swaynag/nagbar.h similarity index 100% rename from include/swaynagbar/nagbar.h rename to include/swaynag/nagbar.h diff --git a/include/swaynagbar/render.h b/include/swaynag/render.h similarity index 100% rename from include/swaynagbar/render.h rename to include/swaynag/render.h diff --git a/meson.build b/meson.build index 26d36e06..c6501c39 100644 --- a/meson.build +++ b/meson.build @@ -82,7 +82,7 @@ if scdoc.found() 'swaylock/swaylock.1.scd', 'swaymsg/swaymsg.1.scd', 'swayidle/swayidle.1.scd', - 'swaynagbar/swaynagbar.1.scd', + 'swaynag/swaynag.1.scd', ] foreach filename : man_files topic = filename.split('.')[-3].split('/')[-1] @@ -131,7 +131,7 @@ subdir('swaybg') subdir('swaybar') subdir('swaylock') subdir('swayidle') -subdir('swaynagbar') +subdir('swaynag') config = configuration_data() config.set('sysconfdir', join_paths(prefix, sysconfdir)) diff --git a/sway/desktop/layer_shell.c b/sway/desktop/layer_shell.c index 71a0163c..a2935883 100644 --- a/sway/desktop/layer_shell.c +++ b/sway/desktop/layer_shell.c @@ -214,9 +214,6 @@ void arrange_layers(struct sway_output *output) { wl_list_for_each(seat, &input_manager->seats, link) { seat_set_focus_layer(seat, topmost ? topmost->layer_surface : NULL); } - - arrange_windows(output->swayc); - transaction_commit_dirty(); } static void handle_output_destroy(struct wl_listener *listener, void *data) { @@ -250,6 +247,9 @@ static void handle_surface_commit(struct wl_listener *listener, void *data) { output_damage_surface(output, layer->geo.x, layer->geo.y, layer_surface->surface, false); } + + arrange_windows(output->swayc); + transaction_commit_dirty(); } static void unmap(struct sway_layer_surface *sway_layer) { @@ -287,6 +287,8 @@ static void handle_destroy(struct wl_listener *listener, void *data) { struct sway_output *output = sway_layer->layer_surface->output->data; if (output != NULL && output->swayc != NULL) { arrange_layers(output); + arrange_windows(output->swayc); + transaction_commit_dirty(); } wl_list_remove(&sway_layer->output_destroy.link); sway_layer->layer_surface->output = NULL; diff --git a/swaynagbar/main.c b/swaynag/main.c similarity index 93% rename from swaynagbar/main.c rename to swaynag/main.c index 389118c6..60560c72 100644 --- a/swaynagbar/main.c +++ b/swaynag/main.c @@ -4,7 +4,7 @@ #include "log.h" #include "list.h" #include "readline.h" -#include "swaynagbar/nagbar.h" +#include "swaynag/nagbar.h" #include "wlr-layer-shell-unstable-v1-client-protocol.h" static struct sway_nagbar nagbar; @@ -71,7 +71,7 @@ int main(int argc, char **argv) { | ZWLR_LAYER_SURFACE_V1_ANCHOR_RIGHT; nagbar.type = NAGBAR_ERROR; set_nagbar_colors(); - nagbar.font = strdup("pango:monospace 8"); + nagbar.font = strdup("pango:monospace 10"); nagbar.buttons = create_list(); struct sway_nagbar_button *button_close = @@ -91,7 +91,7 @@ int main(int argc, char **argv) { {"edge", required_argument, NULL, 'e'}, {"font", required_argument, NULL, 'f'}, {"help", no_argument, NULL, 'h'}, - {"detailed-message", required_argument, NULL, 'l'}, + {"detailed-message", no_argument, NULL, 'l'}, {"detailed-button", required_argument, NULL, 'L'}, {"message", required_argument, NULL, 'm'}, {"output", required_argument, NULL, 'o'}, @@ -102,7 +102,7 @@ int main(int argc, char **argv) { }; const char *usage = - "Usage: swaynagbar [options...]\n" + "Usage: swaynag [options...]\n" "\n" " -b, --button Create a button with text that " "executes action when pressed. Multiple buttons can be defined.\n" @@ -110,7 +110,7 @@ int main(int argc, char **argv) { " -e, --edge top|bottom Set the edge to use.\n" " -f, --font Set the font to use.\n" " -h, --help Show help message and quit.\n" - " -l, --detailed-message Set a detailed message.\n" + " -l, --detailed-message Read a detailed message from stdin.\n" " -L, --detailed-button Set the text of the detail button.\n" " -m, --message Set the message text.\n" " -o, --output Set the output to use.\n" @@ -119,7 +119,7 @@ int main(int argc, char **argv) { " -v, --version Show the version number and quit.\n"; while (1) { - int c = getopt_long(argc, argv, "b:de:f:hl:L:m:o:s:t:v", opts, NULL); + int c = getopt_long(argc, argv, "b:de:f:hlL:m:o:s:t:v", opts, NULL); if (c == -1) { break; } @@ -162,11 +162,7 @@ int main(int argc, char **argv) { break; case 'l': // Detailed Message free(nagbar.details.message); - if (strcmp(optarg, "-") == 0) { - nagbar.details.message = read_from_stdin(); - } else { - nagbar.details.message = strdup(optarg); - } + nagbar.details.message = read_from_stdin(); nagbar.details.button_up.text = strdup("▲"); nagbar.details.button_down.text = strdup("▼"); break; @@ -199,7 +195,7 @@ int main(int argc, char **argv) { set_nagbar_colors(); break; case 'v': // Version - fprintf(stdout, "sway version " SWAY_VERSION "\n"); + fprintf(stdout, "swaynag version " SWAY_VERSION "\n"); exit_code = EXIT_SUCCESS; goto cleanup; default: // Help or unknown flag diff --git a/swaynagbar/meson.build b/swaynag/meson.build similarity index 94% rename from swaynagbar/meson.build rename to swaynag/meson.build index 254462f2..6492e4dc 100644 --- a/swaynagbar/meson.build +++ b/swaynag/meson.build @@ -1,5 +1,5 @@ executable( - 'swaynagbar', [ + 'swaynag', [ 'main.c', 'nagbar.c', 'render.c', diff --git a/swaynagbar/nagbar.c b/swaynag/nagbar.c similarity index 98% rename from swaynagbar/nagbar.c rename to swaynag/nagbar.c index 9e8bbb4b..6647e8c2 100644 --- a/swaynagbar/nagbar.c +++ b/swaynag/nagbar.c @@ -6,8 +6,8 @@ #include #include "log.h" #include "list.h" -#include "swaynagbar/nagbar.h" -#include "swaynagbar/render.h" +#include "swaynag/nagbar.h" +#include "swaynag/render.h" #include "wlr-layer-shell-unstable-v1-client-protocol.h" static void nop() { @@ -15,7 +15,7 @@ static void nop() { } static bool terminal_execute(char *terminal, char *command) { - char fname[] = "/tmp/swaynagbarXXXXXX"; + char fname[] = "/tmp/swaynagXXXXXX"; FILE *tmp= fdopen(mkstemp(fname), "w"); if (!tmp) { wlr_log(WLR_ERROR, "Failed to create temp script"); @@ -352,7 +352,7 @@ void nagbar_setup(struct sway_nagbar *nagbar) { assert(nagbar->surface); nagbar->layer_surface = zwlr_layer_shell_v1_get_layer_surface( nagbar->layer_shell, nagbar->surface, nagbar->output.wl_output, - ZWLR_LAYER_SHELL_V1_LAYER_BOTTOM, "nagbar"); + ZWLR_LAYER_SHELL_V1_LAYER_TOP, "swaynag"); assert(nagbar->layer_surface); zwlr_layer_surface_v1_add_listener(nagbar->layer_surface, &layer_surface_listener, nagbar); diff --git a/swaynagbar/render.c b/swaynag/render.c similarity index 99% rename from swaynagbar/render.c rename to swaynag/render.c index 7bc2961e..150ae3f2 100644 --- a/swaynagbar/render.c +++ b/swaynag/render.c @@ -3,7 +3,7 @@ #include "log.h" #include "pango.h" #include "pool-buffer.h" -#include "swaynagbar/nagbar.h" +#include "swaynag/nagbar.h" #include "wlr-layer-shell-unstable-v1-client-protocol.h" static uint32_t render_message(cairo_t *cairo, struct sway_nagbar *nagbar) { @@ -274,7 +274,7 @@ void render_frame(struct sway_nagbar *nagbar) { nagbar->height * nagbar->scale); if (!nagbar->current_buffer) { wlr_log(WLR_DEBUG, "Failed to get buffer. Skipping frame."); - return; + goto cleanup; } cairo_t *shm = nagbar->current_buffer->cairo; @@ -293,6 +293,8 @@ void render_frame(struct sway_nagbar *nagbar) { wl_surface_commit(nagbar->surface); wl_display_roundtrip(nagbar->display); } + +cleanup: cairo_surface_destroy(recorder); cairo_destroy(cairo); } diff --git a/swaynagbar/swaynagbar.1.scd b/swaynag/swaynag.1.scd similarity index 76% rename from swaynagbar/swaynagbar.1.scd rename to swaynag/swaynag.1.scd index 2a34ae68..879aaf2e 100644 --- a/swaynagbar/swaynagbar.1.scd +++ b/swaynag/swaynag.1.scd @@ -2,11 +2,11 @@ swaynagbar(1) # NAME -swaynagbar - Show a warning or error message with buttons +swaynag - Show a warning or error message with buttons # SYNOPSIS -_swaynagbar_ [options...] +_swaynag_ [options...] # OPTIONS *-b, --button* @@ -25,10 +25,9 @@ _swaynagbar_ [options...] *-h, --help* Show help message and quit. -*-l, --detailed-message * - Set the detailed message. A button to toggle details will be added. Details - are shown in a scrollable multi-line text area. If _msg_ is _-_, then the - detailed message will be read from stdin. +*-l, --detailed-message* + Read a detailed message from stdin. A button to toggle details will be + added. Details are shown in a scrollable multi-line text area. *-L, --detailed-button * Set the text for the button that toggles details. This has no effect if From 8463a2896a932cd99f3dc93608b03cb4aba93293 Mon Sep 17 00:00:00 2001 From: Brian Ashworth Date: Sat, 28 Jul 2018 09:34:25 -0400 Subject: [PATCH 126/148] swaynag: implement config file support --- include/swaynag/nagbar.h | 20 +-- include/swaynag/types.h | 25 +++ meson.build | 1 + swaynag/main.c | 355 ++++++++++++++++++++++++++++----------- swaynag/meson.build | 1 + swaynag/nagbar.c | 5 + swaynag/render.c | 23 +-- swaynag/swaynag.1.scd | 33 ++-- swaynag/swaynag.5.scd | 57 +++++++ swaynag/types.c | 116 +++++++++++++ 10 files changed, 499 insertions(+), 137 deletions(-) create mode 100644 include/swaynag/types.h create mode 100644 swaynag/swaynag.5.scd create mode 100644 swaynag/types.c diff --git a/include/swaynag/nagbar.h b/include/swaynag/nagbar.h index 8b55e4fa..b5d9b2cb 100644 --- a/include/swaynag/nagbar.h +++ b/include/swaynag/nagbar.h @@ -1,8 +1,9 @@ #ifndef _SWAY_NAGBAR_NAGBAR_H -#define _SWAY_NAGBAR_NAGNAR_H +#define _SWAY_NAGBAR_NAGBAR_H #include #include "list.h" #include "pool-buffer.h" +#include "swaynag/types.h" #include "xdg-output-unstable-v1-client-protocol.h" #define NAGBAR_BAR_BORDER_THICKNESS 2 @@ -16,25 +17,12 @@ #define NAGBAR_MAX_HEIGHT 500 -enum sway_nagbar_type { - NAGBAR_ERROR, - NAGBAR_WARNING, -}; - enum sway_nagbar_action_type { NAGBAR_ACTION_DISMISS, NAGBAR_ACTION_EXPAND, NAGBAR_ACTION_COMMAND, }; -struct sway_nagbar_colors { - uint32_t button_background; - uint32_t background; - uint32_t text; - uint32_t border; - uint32_t border_bottom; -}; - struct sway_nagbar_pointer { struct wl_pointer *pointer; struct wl_cursor_theme *cursor_theme; @@ -72,6 +60,7 @@ struct sway_nagbar_details { int offset; int visible_lines; int total_lines; + struct sway_nagbar_button button_details; struct sway_nagbar_button button_up; struct sway_nagbar_button button_down; }; @@ -97,8 +86,7 @@ struct sway_nagbar { struct pool_buffer buffers[2]; struct pool_buffer *current_buffer; - enum sway_nagbar_type type; - struct sway_nagbar_colors colors; + struct sway_nagbar_type *type; uint32_t anchors; char *message; char *font; diff --git a/include/swaynag/types.h b/include/swaynag/types.h new file mode 100644 index 00000000..32056514 --- /dev/null +++ b/include/swaynag/types.h @@ -0,0 +1,25 @@ +#ifndef _SWAY_NAGBAR_TYPES_H +#define _SWAY_NAGBAR_TYPES_H + +struct sway_nagbar_type { + char *name; + uint32_t button_background; + uint32_t background; + uint32_t text; + uint32_t border; + uint32_t border_bottom; +}; + +void nagbar_types_add_default(list_t *types); + +struct sway_nagbar_type *nagbar_type_get(list_t *types, char *name); + +struct sway_nagbar_type *nagbar_type_clone(struct sway_nagbar_type *type); + +void nagbar_type_free(struct sway_nagbar_type *type); + +void nagbar_types_free(list_t *types); + +int nagbar_parse_type(int argc, char **argv, struct sway_nagbar_type *type); + +#endif diff --git a/meson.build b/meson.build index c6501c39..57ed70cb 100644 --- a/meson.build +++ b/meson.build @@ -83,6 +83,7 @@ if scdoc.found() 'swaymsg/swaymsg.1.scd', 'swayidle/swayidle.1.scd', 'swaynag/swaynag.1.scd', + 'swaynag/swaynag.5.scd', ] foreach filename : man_files topic = filename.split('.')[-3].split('/')[-1] diff --git a/swaynag/main.c b/swaynag/main.c index 60560c72..b199fac4 100644 --- a/swaynag/main.c +++ b/swaynag/main.c @@ -1,10 +1,14 @@ -#define _XOPEN_SOURCE 500 +#define _XOPEN_SOURCE 700 +#define _POSIX_C_SOURCE 200112L #include #include +#include +#include #include "log.h" #include "list.h" #include "readline.h" #include "swaynag/nagbar.h" +#include "swaynag/types.h" #include "wlr-layer-shell-unstable-v1-client-protocol.h" static struct sway_nagbar nagbar; @@ -19,22 +23,6 @@ void sway_terminate(int code) { exit(code); } -static void set_nagbar_colors() { - if (nagbar.type == NAGBAR_ERROR) { - nagbar.colors.button_background = 0x680A0AFF; - nagbar.colors.background = 0x900000FF; - nagbar.colors.text = 0xFFFFFFFF; - nagbar.colors.border = 0xD92424FF; - nagbar.colors.border_bottom = 0x470909FF; - } else if (nagbar.type == NAGBAR_WARNING) { - nagbar.colors.button_background = 0xFFC100FF; - nagbar.colors.background = 0xFFA800FF; - nagbar.colors.text = 0x000000FF; - nagbar.colors.border = 0xAB7100FF; - nagbar.colors.border_bottom = 0xAB7100FF; - } -} - static char *read_from_stdin() { char *buffer = NULL; while (!feof(stdin)) { @@ -61,32 +49,11 @@ static char *read_from_stdin() { return buffer; } -int main(int argc, char **argv) { - int exit_code = EXIT_SUCCESS; - bool debug = false; - - memset(&nagbar, 0, sizeof(nagbar)); - nagbar.anchors = ZWLR_LAYER_SURFACE_V1_ANCHOR_TOP - | ZWLR_LAYER_SURFACE_V1_ANCHOR_LEFT - | ZWLR_LAYER_SURFACE_V1_ANCHOR_RIGHT; - nagbar.type = NAGBAR_ERROR; - set_nagbar_colors(); - nagbar.font = strdup("pango:monospace 10"); - nagbar.buttons = create_list(); - - struct sway_nagbar_button *button_close = - calloc(sizeof(struct sway_nagbar_button), 1); - button_close->text = strdup("X"); - button_close->type = NAGBAR_ACTION_DISMISS; - list_add(nagbar.buttons, button_close); - - struct sway_nagbar_button *button_details = - calloc(sizeof(struct sway_nagbar_button), 1); - button_details->text = strdup("Toggle Details"); - button_details->type = NAGBAR_ACTION_EXPAND; - +static int parse_options(int argc, char **argv, struct sway_nagbar *nagbar, + list_t *types, char **config, bool *debug) { static struct option opts[] = { {"button", required_argument, NULL, 'b'}, + {"config", required_argument, NULL, 'c'}, {"debug", no_argument, NULL, 'd'}, {"edge", required_argument, NULL, 'e'}, {"font", required_argument, NULL, 'f'}, @@ -106,6 +73,7 @@ int main(int argc, char **argv) { "\n" " -b, --button Create a button with text that " "executes action when pressed. Multiple buttons can be defined.\n" + " -c, --config Path to config file.\n" " -d, --debug Enable debugging.\n" " -e, --edge top|bottom Set the edge to use.\n" " -f, --font Set the font to use.\n" @@ -115,97 +83,278 @@ int main(int argc, char **argv) { " -m, --message Set the message text.\n" " -o, --output Set the output to use.\n" " -s, --dismiss-button Set the dismiss button text.\n" - " -t, --type error|warning Set the message type.\n" + " -t, --type Set the message type.\n" " -v, --version Show the version number and quit.\n"; + optind = 1; while (1) { - int c = getopt_long(argc, argv, "b:de:f:hlL:m:o:s:t:v", opts, NULL); + int c = getopt_long(argc, argv, "b:c:de:f:hlL:m:o:s:t:v", opts, NULL); if (c == -1) { break; } switch (c) { case 'b': // Button - if (optind >= argc) { - fprintf(stderr, "Missing action for button %s\n", optarg); - exit_code = EXIT_FAILURE; - goto cleanup; + if (nagbar) { + if (optind >= argc) { + fprintf(stderr, "Missing action for button %s\n", optarg); + return EXIT_FAILURE; + } + struct sway_nagbar_button *button; + button = calloc(sizeof(struct sway_nagbar_button), 1); + button->text = strdup(optarg); + button->type = NAGBAR_ACTION_COMMAND; + button->action = strdup(argv[optind]); + optind++; + list_add(nagbar->buttons, button); + } + break; + case 'c': // Config + if (config) { + *config = strdup(optarg); } - struct sway_nagbar_button *button; - button = calloc(sizeof(struct sway_nagbar_button), 1); - button->text = strdup(optarg); - button->type = NAGBAR_ACTION_COMMAND; - button->action = strdup(argv[optind]); - optind++; - list_add(nagbar.buttons, button); break; case 'd': // Debug - debug = true; + if (debug) { + *debug = true; + } break; case 'e': // Edge - if (strcmp(optarg, "top") == 0) { - nagbar.anchors = ZWLR_LAYER_SURFACE_V1_ANCHOR_TOP - | ZWLR_LAYER_SURFACE_V1_ANCHOR_LEFT - | ZWLR_LAYER_SURFACE_V1_ANCHOR_RIGHT; - } else if (strcmp(optarg, "bottom") == 0) { - nagbar.anchors = ZWLR_LAYER_SURFACE_V1_ANCHOR_BOTTOM - | ZWLR_LAYER_SURFACE_V1_ANCHOR_LEFT - | ZWLR_LAYER_SURFACE_V1_ANCHOR_RIGHT; - } else { - fprintf(stderr, "Invalid edge: %s\n", optarg); - exit_code = EXIT_FAILURE; - goto cleanup; + if (nagbar) { + if (strcmp(optarg, "top") == 0) { + nagbar->anchors = ZWLR_LAYER_SURFACE_V1_ANCHOR_TOP + | ZWLR_LAYER_SURFACE_V1_ANCHOR_LEFT + | ZWLR_LAYER_SURFACE_V1_ANCHOR_RIGHT; + } else if (strcmp(optarg, "bottom") == 0) { + nagbar->anchors = ZWLR_LAYER_SURFACE_V1_ANCHOR_BOTTOM + | ZWLR_LAYER_SURFACE_V1_ANCHOR_LEFT + | ZWLR_LAYER_SURFACE_V1_ANCHOR_RIGHT; + } else { + fprintf(stderr, "Invalid edge: %s\n", optarg); + return EXIT_FAILURE; + } } break; case 'f': // Font - free(nagbar.font); - nagbar.font = strdup(optarg); + if (nagbar) { + free(nagbar->font); + nagbar->font = strdup(optarg); + } break; case 'l': // Detailed Message - free(nagbar.details.message); - nagbar.details.message = read_from_stdin(); - nagbar.details.button_up.text = strdup("▲"); - nagbar.details.button_down.text = strdup("▼"); + if (nagbar) { + free(nagbar->details.message); + nagbar->details.message = read_from_stdin(); + nagbar->details.button_up.text = strdup("▲"); + nagbar->details.button_down.text = strdup("▼"); + } break; case 'L': // Detailed Button Text - free(button_details->text); - button_details->text = strdup(optarg); + if (nagbar) { + free(nagbar->details.button_details.text); + nagbar->details.button_details.text = strdup(optarg); + } break; case 'm': // Message - free(nagbar.message); - nagbar.message = strdup(optarg); + if (nagbar) { + free(nagbar->message); + nagbar->message = strdup(optarg); + } break; case 'o': // Output - free(nagbar.output.name); - nagbar.output.name = strdup(optarg); + if (nagbar) { + free(nagbar->output.name); + nagbar->output.name = strdup(optarg); + } break; case 's': // Dismiss Button Text - free(button_close->text); - button_close->text = strdup(optarg); + if (nagbar) { + struct sway_nagbar_button *button_close; + button_close = nagbar->buttons->items[0]; + free(button_close->text); + button_close->text = strdup(optarg); + } break; case 't': // Type - if (strcmp(optarg, "error") == 0) { - nagbar.type = NAGBAR_ERROR; - } else if (strcmp(optarg, "warning") == 0) { - nagbar.type = NAGBAR_WARNING; - } else { - fprintf(stderr, "Type must be either 'error' or 'warning'\n"); - exit_code = EXIT_FAILURE; - goto cleanup; + if (nagbar) { + nagbar->type = nagbar_type_get(types, optarg); + if (!nagbar->type) { + fprintf(stderr, "Unknown type %s\n", optarg); + return EXIT_FAILURE; + } } - set_nagbar_colors(); break; case 'v': // Version fprintf(stdout, "swaynag version " SWAY_VERSION "\n"); - exit_code = EXIT_SUCCESS; - goto cleanup; + return -1; default: // Help or unknown flag fprintf(c == 'h' ? stdout : stderr, "%s", usage); - exit_code = c == 'h' ? EXIT_SUCCESS : EXIT_FAILURE; + return -1; + } + } + + return 0; +} + +static bool file_exists(const char *path) { + return path && access(path, R_OK) != -1; +} + +static char *get_config_path(void) { + static const char *config_paths[] = { + "$HOME/.swaynag/config", + "$XDG_CONFIG_HOME/swaynag/config", + SYSCONFDIR "/swaynag/config", + }; + + if (!getenv("XDG_CONFIG_HOME")) { + char *home = getenv("HOME"); + char *config_home = malloc(strlen(home) + strlen("/.config") + 1); + if (!config_home) { + wlr_log(WLR_ERROR, "Unable to allocate $HOME/.config"); + } else { + strcpy(config_home, home); + strcat(config_home, "/.config"); + setenv("XDG_CONFIG_HOME", config_home, 1); + wlr_log(WLR_DEBUG, "Set XDG_CONFIG_HOME to %s", config_home); + free(config_home); + } + } + + wordexp_t p; + char *path; + for (size_t i = 0; i < sizeof(config_paths) / sizeof(char *); ++i) { + if (wordexp(config_paths[i], &p, 0) == 0) { + path = strdup(p.we_wordv[0]); + wordfree(&p); + if (file_exists(path)) { + return path; + } + free(path); + } + } + + return NULL; +} + +static int load_config(char *path, struct sway_nagbar *nagbar, list_t *types) { + FILE *config = fopen(path, "r"); + if (!config) { + fprintf(stderr, "Failed to read config. Running without it.\n"); + return 0; + } + struct sway_nagbar_type *type = NULL; + char *line; + int line_number = 0; + while (!feof(config)) { + line = read_line(config); + if (!line) { + continue; + } + + line_number++; + if (line[0] == '#') { + free(line); + continue; + } + if (strlen(line) == 0) { + free(line); + continue; + } + + if (line[0] == '[') { + char *close = strchr(line, ']'); + if (!close) { + free(line); + fclose(config); + fprintf(stderr, "Closing bracket not found on line %d\n", + line_number); + return 1; + } + char *name = calloc(1, close - line); + strncat(name, line + 1, close - line - 1); + type = nagbar_type_get(types, name); + if (!type) { + type = calloc(1, sizeof(struct sway_nagbar_type)); + type->name = strdup(name); + list_add(types, type); + } + free(name); + } else { + char flag[strlen(line) + 3]; + sprintf(flag, "--%s", line); + char *argv[] = {"swaynag", flag}; + int result; + if (type) { + result = nagbar_parse_type(2, argv, type); + } else { + result = parse_options(2, argv, nagbar, types, NULL, NULL); + } + if (result != 0) { + free(line); + fclose(config); + return result; + } + } + + free(line); + } + fclose(config); + return 0; +} + +int main(int argc, char **argv) { + int exit_code = EXIT_SUCCESS; + + list_t *types = create_list(); + nagbar_types_add_default(types); + + memset(&nagbar, 0, sizeof(nagbar)); + nagbar.anchors = ZWLR_LAYER_SURFACE_V1_ANCHOR_TOP + | ZWLR_LAYER_SURFACE_V1_ANCHOR_LEFT + | ZWLR_LAYER_SURFACE_V1_ANCHOR_RIGHT; + nagbar.font = strdup("pango:monospace 10"); + nagbar.buttons = create_list(); + + struct sway_nagbar_button *button_close = + calloc(sizeof(struct sway_nagbar_button), 1); + button_close->text = strdup("X"); + button_close->type = NAGBAR_ACTION_DISMISS; + list_add(nagbar.buttons, button_close); + + nagbar.details.button_details.text = strdup("Toggle Details"); + nagbar.details.button_details.type = NAGBAR_ACTION_EXPAND; + + + char *config_path = NULL; + bool debug = false; + int launch_status = parse_options(argc, argv, NULL, NULL, + &config_path, &debug); + if (launch_status != 0) { + exit_code = launch_status; + goto cleanup; + } + wlr_log_init(debug ? WLR_DEBUG : WLR_ERROR, NULL); + + if (!config_path) { + config_path = get_config_path(); + } + if (config_path) { + wlr_log(WLR_DEBUG, "Loading config file: %s", config_path); + int config_status = load_config(config_path, &nagbar, types); + free(config_path); + if (config_status != 0) { + exit_code = config_status; goto cleanup; } } - wlr_log_init(debug ? WLR_DEBUG : WLR_ERROR, NULL); + if (argc > 1) { + int result = parse_options(argc, argv, &nagbar, types, NULL, NULL); + if (result != 0) { + exit_code = result; + goto cleanup; + } + } if (!nagbar.message) { wlr_log(WLR_ERROR, "No message passed. Please provide --message/-m"); @@ -213,16 +362,22 @@ int main(int argc, char **argv) { goto cleanup; } + if (!nagbar.type) { + nagbar.type = nagbar_type_get(types, "error"); + } + + nagbar.type = nagbar_type_clone(nagbar.type); + nagbar_types_free(types); + if (nagbar.details.message) { - list_add(nagbar.buttons, button_details); + list_add(nagbar.buttons, &nagbar.details.button_details); } else { - free(button_details->text); - free(button_details); + free(nagbar.details.button_details.text); } wlr_log(WLR_DEBUG, "Output: %s", nagbar.output.name); wlr_log(WLR_DEBUG, "Anchors: %d", nagbar.anchors); - wlr_log(WLR_DEBUG, "Type: %d", nagbar.type); + wlr_log(WLR_DEBUG, "Type: %s", nagbar.type->name); wlr_log(WLR_DEBUG, "Message: %s", nagbar.message); wlr_log(WLR_DEBUG, "Font: %s", nagbar.font); wlr_log(WLR_DEBUG, "Buttons"); @@ -238,8 +393,8 @@ int main(int argc, char **argv) { return exit_code; cleanup: - free(button_details->text); - free(button_details); + nagbar_types_free(types); + free(nagbar.details.button_details.text); nagbar_destroy(&nagbar); return exit_code; } diff --git a/swaynag/meson.build b/swaynag/meson.build index 6492e4dc..6a9df984 100644 --- a/swaynag/meson.build +++ b/swaynag/meson.build @@ -3,6 +3,7 @@ executable( 'main.c', 'nagbar.c', 'render.c', + 'types.c', ], include_directories: [sway_inc], dependencies: [ diff --git a/swaynag/nagbar.c b/swaynag/nagbar.c index 6647e8c2..a20a9095 100644 --- a/swaynag/nagbar.c +++ b/swaynag/nagbar.c @@ -8,6 +8,7 @@ #include "list.h" #include "swaynag/nagbar.h" #include "swaynag/render.h" +#include "swaynag/types.h" #include "wlr-layer-shell-unstable-v1-client-protocol.h" static void nop() { @@ -386,6 +387,10 @@ void nagbar_destroy(struct sway_nagbar *nagbar) { free(nagbar->details.button_up.text); free(nagbar->details.button_down.text); + if (nagbar->type) { + nagbar_type_free(nagbar->type); + } + if (nagbar->layer_surface) { zwlr_layer_surface_v1_destroy(nagbar->layer_surface); } diff --git a/swaynag/render.c b/swaynag/render.c index 150ae3f2..134c247e 100644 --- a/swaynag/render.c +++ b/swaynag/render.c @@ -4,6 +4,7 @@ #include "pango.h" #include "pool-buffer.h" #include "swaynag/nagbar.h" +#include "swaynag/types.h" #include "wlr-layer-shell-unstable-v1-client-protocol.h" static uint32_t render_message(cairo_t *cairo, struct sway_nagbar *nagbar) { @@ -22,7 +23,7 @@ static uint32_t render_message(cairo_t *cairo, struct sway_nagbar *nagbar) { return ideal_surface_height; } - cairo_set_source_u32(cairo, nagbar->colors.text); + cairo_set_source_u32(cairo, nagbar->type->text); cairo_move_to(cairo, padding, (int)(ideal_height - text_height) / 2); pango_printf(cairo, nagbar->font, nagbar->scale, true, "%s", nagbar->message); @@ -39,17 +40,17 @@ static void render_details_scroll_button(cairo_t *cairo, int border = NAGBAR_BUTTON_BORDER_THICKNESS * nagbar->scale; int padding = NAGBAR_BUTTON_PADDING * nagbar->scale; - cairo_set_source_u32(cairo, nagbar->colors.border); + cairo_set_source_u32(cairo, nagbar->type->border); cairo_rectangle(cairo, button->x, button->y, button->width, button->height); cairo_fill(cairo); - cairo_set_source_u32(cairo, nagbar->colors.button_background); + cairo_set_source_u32(cairo, nagbar->type->button_background); cairo_rectangle(cairo, button->x + border, button->y + border, button->width - (border * 2), button->height - (border * 2)); cairo_fill(cairo); - cairo_set_source_u32(cairo, nagbar->colors.text); + cairo_set_source_u32(cairo, nagbar->type->text); cairo_move_to(cairo, button->x + border + padding, button->y + border + (button->height - text_height) / 2); pango_printf(cairo, nagbar->font, nagbar->scale, true, "%s", button->text); @@ -154,14 +155,14 @@ static uint32_t render_detailed(cairo_t *cairo, struct sway_nagbar *nagbar, &nagbar->details.button_down); } - cairo_set_source_u32(cairo, nagbar->colors.border); + cairo_set_source_u32(cairo, nagbar->type->border); cairo_rectangle(cairo, nagbar->details.x, nagbar->details.y, nagbar->details.width, nagbar->details.height); cairo_fill(cairo); cairo_move_to(cairo, nagbar->details.x + padding, nagbar->details.y + padding); - cairo_set_source_u32(cairo, nagbar->colors.text); + cairo_set_source_u32(cairo, nagbar->type->text); pango_cairo_show_layout(cairo, layout); g_object_unref(layout); @@ -192,17 +193,17 @@ static uint32_t render_button(cairo_t *cairo, struct sway_nagbar *nagbar, button->width = text_width + padding * 2; button->height = text_height + padding * 2; - cairo_set_source_u32(cairo, nagbar->colors.border); + cairo_set_source_u32(cairo, nagbar->type->border); cairo_rectangle(cairo, button->x - border, button->y - border, button->width + border * 2, button->height + border * 2); cairo_fill(cairo); - cairo_set_source_u32(cairo, nagbar->colors.button_background); + cairo_set_source_u32(cairo, nagbar->type->button_background); cairo_rectangle(cairo, button->x, button->y, button->width, button->height); cairo_fill(cairo); - cairo_set_source_u32(cairo, nagbar->colors.text); + cairo_set_source_u32(cairo, nagbar->type->text); cairo_move_to(cairo, button->x + padding, button->y + padding); pango_printf(cairo, nagbar->font, nagbar->scale, true, "%s", button->text); @@ -215,7 +216,7 @@ static uint32_t render_to_cairo(cairo_t *cairo, struct sway_nagbar *nagbar) { uint32_t max_height = 0; cairo_set_operator(cairo, CAIRO_OPERATOR_SOURCE); - cairo_set_source_u32(cairo, nagbar->colors.background); + cairo_set_source_u32(cairo, nagbar->type->background); cairo_paint(cairo); uint32_t h = render_message(cairo, nagbar); @@ -240,7 +241,7 @@ static uint32_t render_to_cairo(cairo_t *cairo, struct sway_nagbar *nagbar) { if (max_height > nagbar->height) { max_height += border; } - cairo_set_source_u32(cairo, nagbar->colors.border_bottom); + cairo_set_source_u32(cairo, nagbar->type->border_bottom); cairo_rectangle(cairo, 0, nagbar->height * nagbar->scale - border, nagbar->width * nagbar->scale, border); cairo_fill(cairo); diff --git a/swaynag/swaynag.1.scd b/swaynag/swaynag.1.scd index 879aaf2e..7d250a45 100644 --- a/swaynag/swaynag.1.scd +++ b/swaynag/swaynag.1.scd @@ -1,4 +1,4 @@ -swaynagbar(1) +swaynag(1) # NAME @@ -13,13 +13,21 @@ _swaynag_ [options...] Create a button with the text _text_ that executes _action_ when pressed. Multiple buttons can be defined by providing the flag multiple times. +*-c, --config* + The config file to use. By default, the following paths are checked: + _$HOME/.swaynag/config_, _$XDG\_CONFIG\_HOME/swaynag/config_, and + _SYSCONFDIR/swaynag/config_. All flags aside from this one and _debug_ are + valid options in the configuration file using the format + _long-option=value_. All leading dashes should be omitted and the equals + sign is required. See swaynag(5) for more information. + *-d, --debug* Enable debugging. -*-e, --edge top|bottom* +*-e, --edge* top|bottom Set the edge to use. -*-f, --font * +*-f, --font* Set the font to use. *-h, --help* @@ -29,24 +37,29 @@ _swaynag_ [options...] Read a detailed message from stdin. A button to toggle details will be added. Details are shown in a scrollable multi-line text area. -*-L, --detailed-button * +*-L, --detailed-button* Set the text for the button that toggles details. This has no effect if there is not a detailed message. The default is _Toggle Details_. -*-m, --message * +*-m, --message* Set the message text. -*-o, --output * +*-o, --output* Set the output to use. This should be the name of a _xdg\_output_. If _xdg\_output\_manager_ is not supported, then the first detected output will be used -*-s, --dismiss-button * +*-s, --dismiss-button* Sets the text for the dismiss nagbar button. The default is _X_. -*-t, --type error|warning* - Set the message type. +*-t, --type* + Set the message type. Two types are created by default _error_ and + _warning_. Custom types can be defined in the config file. See + _--config_ and swaynag(5) for details. Both of the default types can be + overridden in the config file as well. -*-v, --version +*-v, --version* Show the version number and quit. +# SEE +swaynag(5) diff --git a/swaynag/swaynag.5.scd b/swaynag/swaynag.5.scd new file mode 100644 index 00000000..a4e05e3a --- /dev/null +++ b/swaynag/swaynag.5.scd @@ -0,0 +1,57 @@ +swaynag(5) + +# NAME + +swaynag - swaynag configuration file + +# SYNOPSIS + +$HOME/.swaynag/config, $XDG\_CONFIG\_HOME/swaynag/config, +SYSCONFDIR/swaynag/config + +# CONFIG FILE +At the top of the config file, _swaynag_ options can be set using the format +_long-option=value_. These will be used as default values if _swaynag_ is not +given the option. This can be useful for setting a preferred font, output, and +edge. + +Below the options, custom types may be defined. To define a type, use the +following format: + +``` +[name-of-type] +color=RRGGBB[AA] +``` + +All colors may be given in the form _RRGGBB_ or _RRGGBBAA_. The following +colors can be set: + +*background* + The background color for _swaynag_. + +*border* + The color to use for borders of buttons. + +*border-bottom* + The color of the border line at the bottom of _swaynag_. + +*button-background* + The background color for the buttons. + +*text* + The color of the text. + +# EXAMPLE +``` +font=Monospace 12 + +[green] +background=00AA00 +border=006600 +border-bottom=004400 +text=FFFFFF +button-background=00CC00 +``` + +# SEE +swaynag(1) diff --git a/swaynag/types.c b/swaynag/types.c new file mode 100644 index 00000000..8dd99d2a --- /dev/null +++ b/swaynag/types.c @@ -0,0 +1,116 @@ +#define _XOPEN_SOURCE 500 +#include +#include +#include +#include +#include +#include +#include "list.h" +#include "swaynag/types.h" +#include "util.h" + +void nagbar_types_add_default(list_t *types) { + struct sway_nagbar_type *type_error; + type_error = calloc(1, sizeof(struct sway_nagbar_type)); + type_error->name = strdup("error"); + type_error->button_background = 0x680A0AFF; + type_error->background = 0x900000FF; + type_error->text = 0xFFFFFFFF; + type_error->border = 0xD92424FF; + type_error->border_bottom = 0x470909FF; + list_add(types, type_error); + + struct sway_nagbar_type *type_warning; + type_warning = calloc(1, sizeof(struct sway_nagbar_type)); + type_warning->name = strdup("warning"); + type_warning->button_background = 0xFFC100FF; + type_warning->background = 0xFFA800FF; + type_warning->text = 0x000000FF; + type_warning->border = 0xAB7100FF; + type_warning->border_bottom = 0xAB7100FF; + list_add(types, type_warning); +} + +struct sway_nagbar_type *nagbar_type_get(list_t *types, char *name) { + for (int i = 0; i < types->length; i++) { + struct sway_nagbar_type *type = types->items[i]; + if (strcasecmp(type->name, name) == 0) { + return type; + } + } + return NULL; +} + +struct sway_nagbar_type *nagbar_type_clone(struct sway_nagbar_type *type) { + struct sway_nagbar_type *clone; + clone = calloc(1, sizeof(struct sway_nagbar_type)); + clone->name = strdup(type->name); + clone->button_background = type->button_background; + clone->background = type->background; + clone->text = type->text; + clone->border = type->border; + clone->border_bottom = type->border_bottom; + return clone; +} + +void nagbar_type_free(struct sway_nagbar_type *type) { + free(type->name); + free(type); +} + +void nagbar_types_free(list_t *types) { + while (types->length) { + struct sway_nagbar_type *type = types->items[0]; + nagbar_type_free(type); + list_del(types, 0); + } + list_free(types); +} + +int nagbar_parse_type(int argc, char **argv, struct sway_nagbar_type *type) { + enum color_option { + COLOR_BACKGROUND, + COLOR_BORDER, + COLOR_BORDER_BOTTOM, + COLOR_BUTTON, + COLOR_TEXT, + }; + + static struct option opts[] = { + {"background", required_argument, NULL, COLOR_BACKGROUND}, + {"border", required_argument, NULL, COLOR_BORDER}, + {"border-bottom", required_argument, NULL, COLOR_BORDER_BOTTOM}, + {"button-background", required_argument, NULL, COLOR_BUTTON}, + {"text", required_argument, NULL, COLOR_TEXT}, + {0, 0, 0, 0} + }; + + optind = 1; + while (1) { + int c = getopt_long(argc, argv, "", opts, NULL); + if (c == -1) { + break; + } + switch (c) { + case COLOR_BACKGROUND: + type->background = parse_color(optarg); + break; + case COLOR_BORDER: + type->border = parse_color(optarg); + break; + case COLOR_BORDER_BOTTOM: + type->border_bottom = parse_color(optarg); + break; + case COLOR_BUTTON: + type->button_background = parse_color(optarg); + break; + case COLOR_TEXT: + type->text = parse_color(optarg); + break; + default: + break; + } + } + return 0; +} + From 37709917b115e43a4aa824df8ada11edcaf499cc Mon Sep 17 00:00:00 2001 From: Drew DeVault Date: Sat, 28 Jul 2018 09:44:01 -0400 Subject: [PATCH 127/148] Set output to NULL if not specified This opens nagbar on the active output. --- swaynag/nagbar.c | 19 +++---------------- 1 file changed, 3 insertions(+), 16 deletions(-) diff --git a/swaynag/nagbar.c b/swaynag/nagbar.c index a20a9095..e451a53a 100644 --- a/swaynag/nagbar.c +++ b/swaynag/nagbar.c @@ -230,8 +230,7 @@ static void xdg_output_handle_name(void *data, struct output_state *state = data; char *outname = state->nagbar->output.name; wlr_log(WLR_DEBUG, "Checking against output %s for %s", name, outname); - if ((!outname && !state->nagbar->output.wl_output) - || (name && outname && strcmp(name, outname) == 0)) { + if (outname && !state->nagbar->output.wl_output) { wlr_log(WLR_DEBUG, "Using output %s", name); state->nagbar->output.wl_output = state->wl_output; state->nagbar->output.wl_name = state->wl_name; @@ -279,14 +278,6 @@ static void handle_global(void *data, struct wl_registry *registry, nagbar->xdg_output_manager, state->wl_output); zxdg_output_v1_add_listener(state->xdg_output, &xdg_output_listener, state); - } else if (!nagbar->output.wl_output && !nagbar->xdg_output_manager) { - wlr_log(WLR_ERROR, "Warning: zxdg_output_manager_v1 not supported." - " Falling back to first detected output"); - nagbar->output.wl_output = wl_registry_bind(registry, name, - &wl_output_interface, 3); - nagbar->output.wl_name = name; - wl_output_add_listener(nagbar->output.wl_output, - &output_listener, nagbar); } } else if (strcmp(interface, zwlr_layer_shell_v1_interface.name) == 0) { nagbar->layer_shell = wl_registry_bind( @@ -327,12 +318,8 @@ void nagbar_setup(struct sway_nagbar *nagbar) { wl_display_roundtrip(nagbar->display); } - if (!nagbar->output.wl_output) { - if (nagbar->output.name) { - wlr_log(WLR_ERROR, "Output '%s' not found", nagbar->output.name); - } else { - wlr_log(WLR_ERROR, "No outputs detected"); - } + if (!nagbar->output.wl_output && nagbar->output.name) { + wlr_log(WLR_ERROR, "Output '%s' not found", nagbar->output.name); nagbar_destroy(nagbar); exit(EXIT_FAILURE); } From 58f3fa74aeb3e0dd9500d687f3c97a6c10612c41 Mon Sep 17 00:00:00 2001 From: Drew DeVault Date: Sat, 28 Jul 2018 09:48:37 -0400 Subject: [PATCH 128/148] Disable pango markup for extended message --- swaynag/render.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/swaynag/render.c b/swaynag/render.c index 134c247e..ef0f72a8 100644 --- a/swaynag/render.c +++ b/swaynag/render.c @@ -25,7 +25,7 @@ static uint32_t render_message(cairo_t *cairo, struct sway_nagbar *nagbar) { cairo_set_source_u32(cairo, nagbar->type->text); cairo_move_to(cairo, padding, (int)(ideal_height - text_height) / 2); - pango_printf(cairo, nagbar->font, nagbar->scale, true, "%s", + pango_printf(cairo, nagbar->font, nagbar->scale, false, "%s", nagbar->message); return ideal_height; @@ -86,7 +86,7 @@ static uint32_t render_detailed(cairo_t *cairo, struct sway_nagbar *nagbar, nagbar->details.width = width - decor * 2; PangoLayout *layout = get_pango_layout(cairo, nagbar->font, - nagbar->details.message, nagbar->scale, true); + nagbar->details.message, nagbar->scale, false); pango_layout_set_width(layout, (nagbar->details.width - padding * 2) * PANGO_SCALE); pango_layout_set_wrap(layout, PANGO_WRAP_WORD_CHAR); From 6124d0f9a202ba2f39125602a35bb47d3742022b Mon Sep 17 00:00:00 2001 From: Brian Ashworth Date: Sat, 28 Jul 2018 22:56:12 -0400 Subject: [PATCH 129/148] swaynag: split config into own file and fix optind --- include/swaynag/config.h | 13 ++ include/swaynag/nagbar.h | 1 + swaynag/config.c | 292 ++++++++++++++++++++++++++++++++++++++ swaynag/main.c | 296 +-------------------------------------- swaynag/meson.build | 1 + swaynag/types.c | 1 + 6 files changed, 315 insertions(+), 289 deletions(-) create mode 100644 include/swaynag/config.h create mode 100644 swaynag/config.c diff --git a/include/swaynag/config.h b/include/swaynag/config.h new file mode 100644 index 00000000..caa32710 --- /dev/null +++ b/include/swaynag/config.h @@ -0,0 +1,13 @@ +#ifndef _SWAY_NAGBAR_CONFIG_H +#define _SWAY_NAGBAR_CONFIG_H +#include "swaynag/nagbar.h" +#include "list.h" + +int nagbar_parse_options(int argc, char **argv, struct sway_nagbar *nagbar, + list_t *types, char **config, bool *debug); + +char *nagbar_get_config_path(void); + +int nagbar_load_config(char *path, struct sway_nagbar *nagbar, list_t *types); + +#endif diff --git a/include/swaynag/nagbar.h b/include/swaynag/nagbar.h index b5d9b2cb..4ef101e9 100644 --- a/include/swaynag/nagbar.h +++ b/include/swaynag/nagbar.h @@ -1,6 +1,7 @@ #ifndef _SWAY_NAGBAR_NAGBAR_H #define _SWAY_NAGBAR_NAGBAR_H #include +#include #include "list.h" #include "pool-buffer.h" #include "swaynag/types.h" diff --git a/swaynag/config.c b/swaynag/config.c new file mode 100644 index 00000000..4e7edf2e --- /dev/null +++ b/swaynag/config.c @@ -0,0 +1,292 @@ +#define _XOPEN_SOURCE 700 +#define _POSIX_C_SOURCE 200112L +#include +#include +#include +#include "log.h" +#include "list.h" +#include "readline.h" +#include "swaynag/nagbar.h" +#include "swaynag/types.h" +#include "wlr-layer-shell-unstable-v1-client-protocol.h" + +static char *read_from_stdin() { + char *buffer = NULL; + while (!feof(stdin)) { + char *line = read_line(stdin); + if (!line) { + continue; + } + + if (!buffer) { + buffer = strdup(line); + } else { + buffer = realloc(buffer, strlen(buffer) + strlen(line) + 2); + strcat(buffer, line); + strcat(buffer, "\n"); + } + + free(line); + } + + if (buffer && buffer[strlen(buffer) - 1] == '\n') { + buffer[strlen(buffer) - 1] = '\0'; + } + + return buffer; +} + +int nagbar_parse_options(int argc, char **argv, struct sway_nagbar *nagbar, + list_t *types, char **config, bool *debug) { + static struct option opts[] = { + {"button", required_argument, NULL, 'b'}, + {"config", required_argument, NULL, 'c'}, + {"debug", no_argument, NULL, 'd'}, + {"edge", required_argument, NULL, 'e'}, + {"font", required_argument, NULL, 'f'}, + {"help", no_argument, NULL, 'h'}, + {"detailed-message", no_argument, NULL, 'l'}, + {"detailed-button", required_argument, NULL, 'L'}, + {"message", required_argument, NULL, 'm'}, + {"output", required_argument, NULL, 'o'}, + {"dismiss-button", required_argument, NULL, 's'}, + {"type", required_argument, NULL, 't'}, + {"version", no_argument, NULL, 'v'}, + {0, 0, 0, 0} + }; + + const char *usage = + "Usage: swaynag [options...]\n" + "\n" + " -b, --button Create a button with text that " + "executes action when pressed. Multiple buttons can be defined.\n" + " -c, --config Path to config file.\n" + " -d, --debug Enable debugging.\n" + " -e, --edge top|bottom Set the edge to use.\n" + " -f, --font Set the font to use.\n" + " -h, --help Show help message and quit.\n" + " -l, --detailed-message Read a detailed message from stdin.\n" + " -L, --detailed-button Set the text of the detail button.\n" + " -m, --message Set the message text.\n" + " -o, --output Set the output to use.\n" + " -s, --dismiss-button Set the dismiss button text.\n" + " -t, --type Set the message type.\n" + " -v, --version Show the version number and quit.\n"; + + optind = 1; + while (1) { + int c = getopt_long(argc, argv, "b:c:de:f:hlL:m:o:s:t:v", opts, NULL); + if (c == -1) { + break; + } + switch (c) { + case 'b': // Button + if (nagbar) { + if (optind >= argc) { + fprintf(stderr, "Missing action for button %s\n", optarg); + return EXIT_FAILURE; + } + struct sway_nagbar_button *button; + button = calloc(sizeof(struct sway_nagbar_button), 1); + button->text = strdup(optarg); + button->type = NAGBAR_ACTION_COMMAND; + button->action = strdup(argv[optind]); + list_add(nagbar->buttons, button); + } + optind++; + break; + case 'c': // Config + if (config) { + *config = strdup(optarg); + } + break; + case 'd': // Debug + if (debug) { + *debug = true; + } + break; + case 'e': // Edge + if (nagbar) { + if (strcmp(optarg, "top") == 0) { + nagbar->anchors = ZWLR_LAYER_SURFACE_V1_ANCHOR_TOP + | ZWLR_LAYER_SURFACE_V1_ANCHOR_LEFT + | ZWLR_LAYER_SURFACE_V1_ANCHOR_RIGHT; + } else if (strcmp(optarg, "bottom") == 0) { + nagbar->anchors = ZWLR_LAYER_SURFACE_V1_ANCHOR_BOTTOM + | ZWLR_LAYER_SURFACE_V1_ANCHOR_LEFT + | ZWLR_LAYER_SURFACE_V1_ANCHOR_RIGHT; + } else { + fprintf(stderr, "Invalid edge: %s\n", optarg); + return EXIT_FAILURE; + } + } + break; + case 'f': // Font + if (nagbar) { + free(nagbar->font); + nagbar->font = strdup(optarg); + } + break; + case 'l': // Detailed Message + if (nagbar) { + free(nagbar->details.message); + nagbar->details.message = read_from_stdin(); + nagbar->details.button_up.text = strdup("▲"); + nagbar->details.button_down.text = strdup("▼"); + } + break; + case 'L': // Detailed Button Text + if (nagbar) { + free(nagbar->details.button_details.text); + nagbar->details.button_details.text = strdup(optarg); + } + break; + case 'm': // Message + if (nagbar) { + free(nagbar->message); + nagbar->message = strdup(optarg); + } + break; + case 'o': // Output + if (nagbar) { + free(nagbar->output.name); + nagbar->output.name = strdup(optarg); + } + break; + case 's': // Dismiss Button Text + if (nagbar) { + struct sway_nagbar_button *button_close; + button_close = nagbar->buttons->items[0]; + free(button_close->text); + button_close->text = strdup(optarg); + } + break; + case 't': // Type + if (nagbar) { + nagbar->type = nagbar_type_get(types, optarg); + if (!nagbar->type) { + fprintf(stderr, "Unknown type %s\n", optarg); + return EXIT_FAILURE; + } + } + break; + case 'v': // Version + fprintf(stdout, "swaynag version " SWAY_VERSION "\n"); + return -1; + default: // Help or unknown flag + fprintf(c == 'h' ? stdout : stderr, "%s", usage); + return -1; + } + } + + return 0; +} + +static bool file_exists(const char *path) { + return path && access(path, R_OK) != -1; +} + +char *nagbar_get_config_path(void) { + static const char *config_paths[] = { + "$HOME/.swaynag/config", + "$XDG_CONFIG_HOME/swaynag/config", + SYSCONFDIR "/swaynag/config", + }; + + if (!getenv("XDG_CONFIG_HOME")) { + char *home = getenv("HOME"); + char *config_home = malloc(strlen(home) + strlen("/.config") + 1); + if (!config_home) { + wlr_log(WLR_ERROR, "Unable to allocate $HOME/.config"); + } else { + strcpy(config_home, home); + strcat(config_home, "/.config"); + setenv("XDG_CONFIG_HOME", config_home, 1); + wlr_log(WLR_DEBUG, "Set XDG_CONFIG_HOME to %s", config_home); + free(config_home); + } + } + + wordexp_t p; + char *path; + for (size_t i = 0; i < sizeof(config_paths) / sizeof(char *); ++i) { + if (wordexp(config_paths[i], &p, 0) == 0) { + path = strdup(p.we_wordv[0]); + wordfree(&p); + if (file_exists(path)) { + return path; + } + free(path); + } + } + + return NULL; +} + +int nagbar_load_config(char *path, struct sway_nagbar *nagbar, list_t *types) { + FILE *config = fopen(path, "r"); + if (!config) { + fprintf(stderr, "Failed to read config. Running without it.\n"); + return 0; + } + struct sway_nagbar_type *type = NULL; + char *line; + int line_number = 0; + while (!feof(config)) { + line = read_line(config); + if (!line) { + continue; + } + + line_number++; + if (line[0] == '#') { + free(line); + continue; + } + if (strlen(line) == 0) { + free(line); + continue; + } + + if (line[0] == '[') { + char *close = strchr(line, ']'); + if (!close) { + free(line); + fclose(config); + fprintf(stderr, "Closing bracket not found on line %d\n", + line_number); + return 1; + } + char *name = calloc(1, close - line); + strncat(name, line + 1, close - line - 1); + type = nagbar_type_get(types, name); + if (!type) { + type = calloc(1, sizeof(struct sway_nagbar_type)); + type->name = strdup(name); + list_add(types, type); + } + free(name); + } else { + char flag[strlen(line) + 3]; + sprintf(flag, "--%s", line); + char *argv[] = {"swaynag", flag}; + int result; + if (type) { + result = nagbar_parse_type(2, argv, type); + } else { + result = nagbar_parse_options(2, argv, nagbar, types, + NULL, NULL); + } + if (result != 0) { + free(line); + fclose(config); + return result; + } + } + + free(line); + } + fclose(config); + return 0; +} + diff --git a/swaynag/main.c b/swaynag/main.c index b199fac4..5c97e12a 100644 --- a/swaynag/main.c +++ b/swaynag/main.c @@ -1,12 +1,8 @@ -#define _XOPEN_SOURCE 700 -#define _POSIX_C_SOURCE 200112L -#include +#define _XOPEN_SOURCE 500 #include -#include -#include #include "log.h" #include "list.h" -#include "readline.h" +#include "swaynag/config.h" #include "swaynag/nagbar.h" #include "swaynag/types.h" #include "wlr-layer-shell-unstable-v1-client-protocol.h" @@ -23,285 +19,6 @@ void sway_terminate(int code) { exit(code); } -static char *read_from_stdin() { - char *buffer = NULL; - while (!feof(stdin)) { - char *line = read_line(stdin); - if (!line) { - continue; - } - - if (!buffer) { - buffer = strdup(line); - } else { - buffer = realloc(buffer, strlen(buffer) + strlen(line) + 2); - strcat(buffer, line); - strcat(buffer, "\n"); - } - - free(line); - } - - if (buffer && buffer[strlen(buffer) - 1] == '\n') { - buffer[strlen(buffer) - 1] = '\0'; - } - - return buffer; -} - -static int parse_options(int argc, char **argv, struct sway_nagbar *nagbar, - list_t *types, char **config, bool *debug) { - static struct option opts[] = { - {"button", required_argument, NULL, 'b'}, - {"config", required_argument, NULL, 'c'}, - {"debug", no_argument, NULL, 'd'}, - {"edge", required_argument, NULL, 'e'}, - {"font", required_argument, NULL, 'f'}, - {"help", no_argument, NULL, 'h'}, - {"detailed-message", no_argument, NULL, 'l'}, - {"detailed-button", required_argument, NULL, 'L'}, - {"message", required_argument, NULL, 'm'}, - {"output", required_argument, NULL, 'o'}, - {"dismiss-button", required_argument, NULL, 's'}, - {"type", required_argument, NULL, 't'}, - {"version", no_argument, NULL, 'v'}, - {0, 0, 0, 0} - }; - - const char *usage = - "Usage: swaynag [options...]\n" - "\n" - " -b, --button Create a button with text that " - "executes action when pressed. Multiple buttons can be defined.\n" - " -c, --config Path to config file.\n" - " -d, --debug Enable debugging.\n" - " -e, --edge top|bottom Set the edge to use.\n" - " -f, --font Set the font to use.\n" - " -h, --help Show help message and quit.\n" - " -l, --detailed-message Read a detailed message from stdin.\n" - " -L, --detailed-button Set the text of the detail button.\n" - " -m, --message Set the message text.\n" - " -o, --output Set the output to use.\n" - " -s, --dismiss-button Set the dismiss button text.\n" - " -t, --type Set the message type.\n" - " -v, --version Show the version number and quit.\n"; - - optind = 1; - while (1) { - int c = getopt_long(argc, argv, "b:c:de:f:hlL:m:o:s:t:v", opts, NULL); - if (c == -1) { - break; - } - switch (c) { - case 'b': // Button - if (nagbar) { - if (optind >= argc) { - fprintf(stderr, "Missing action for button %s\n", optarg); - return EXIT_FAILURE; - } - struct sway_nagbar_button *button; - button = calloc(sizeof(struct sway_nagbar_button), 1); - button->text = strdup(optarg); - button->type = NAGBAR_ACTION_COMMAND; - button->action = strdup(argv[optind]); - optind++; - list_add(nagbar->buttons, button); - } - break; - case 'c': // Config - if (config) { - *config = strdup(optarg); - } - break; - case 'd': // Debug - if (debug) { - *debug = true; - } - break; - case 'e': // Edge - if (nagbar) { - if (strcmp(optarg, "top") == 0) { - nagbar->anchors = ZWLR_LAYER_SURFACE_V1_ANCHOR_TOP - | ZWLR_LAYER_SURFACE_V1_ANCHOR_LEFT - | ZWLR_LAYER_SURFACE_V1_ANCHOR_RIGHT; - } else if (strcmp(optarg, "bottom") == 0) { - nagbar->anchors = ZWLR_LAYER_SURFACE_V1_ANCHOR_BOTTOM - | ZWLR_LAYER_SURFACE_V1_ANCHOR_LEFT - | ZWLR_LAYER_SURFACE_V1_ANCHOR_RIGHT; - } else { - fprintf(stderr, "Invalid edge: %s\n", optarg); - return EXIT_FAILURE; - } - } - break; - case 'f': // Font - if (nagbar) { - free(nagbar->font); - nagbar->font = strdup(optarg); - } - break; - case 'l': // Detailed Message - if (nagbar) { - free(nagbar->details.message); - nagbar->details.message = read_from_stdin(); - nagbar->details.button_up.text = strdup("▲"); - nagbar->details.button_down.text = strdup("▼"); - } - break; - case 'L': // Detailed Button Text - if (nagbar) { - free(nagbar->details.button_details.text); - nagbar->details.button_details.text = strdup(optarg); - } - break; - case 'm': // Message - if (nagbar) { - free(nagbar->message); - nagbar->message = strdup(optarg); - } - break; - case 'o': // Output - if (nagbar) { - free(nagbar->output.name); - nagbar->output.name = strdup(optarg); - } - break; - case 's': // Dismiss Button Text - if (nagbar) { - struct sway_nagbar_button *button_close; - button_close = nagbar->buttons->items[0]; - free(button_close->text); - button_close->text = strdup(optarg); - } - break; - case 't': // Type - if (nagbar) { - nagbar->type = nagbar_type_get(types, optarg); - if (!nagbar->type) { - fprintf(stderr, "Unknown type %s\n", optarg); - return EXIT_FAILURE; - } - } - break; - case 'v': // Version - fprintf(stdout, "swaynag version " SWAY_VERSION "\n"); - return -1; - default: // Help or unknown flag - fprintf(c == 'h' ? stdout : stderr, "%s", usage); - return -1; - } - } - - return 0; -} - -static bool file_exists(const char *path) { - return path && access(path, R_OK) != -1; -} - -static char *get_config_path(void) { - static const char *config_paths[] = { - "$HOME/.swaynag/config", - "$XDG_CONFIG_HOME/swaynag/config", - SYSCONFDIR "/swaynag/config", - }; - - if (!getenv("XDG_CONFIG_HOME")) { - char *home = getenv("HOME"); - char *config_home = malloc(strlen(home) + strlen("/.config") + 1); - if (!config_home) { - wlr_log(WLR_ERROR, "Unable to allocate $HOME/.config"); - } else { - strcpy(config_home, home); - strcat(config_home, "/.config"); - setenv("XDG_CONFIG_HOME", config_home, 1); - wlr_log(WLR_DEBUG, "Set XDG_CONFIG_HOME to %s", config_home); - free(config_home); - } - } - - wordexp_t p; - char *path; - for (size_t i = 0; i < sizeof(config_paths) / sizeof(char *); ++i) { - if (wordexp(config_paths[i], &p, 0) == 0) { - path = strdup(p.we_wordv[0]); - wordfree(&p); - if (file_exists(path)) { - return path; - } - free(path); - } - } - - return NULL; -} - -static int load_config(char *path, struct sway_nagbar *nagbar, list_t *types) { - FILE *config = fopen(path, "r"); - if (!config) { - fprintf(stderr, "Failed to read config. Running without it.\n"); - return 0; - } - struct sway_nagbar_type *type = NULL; - char *line; - int line_number = 0; - while (!feof(config)) { - line = read_line(config); - if (!line) { - continue; - } - - line_number++; - if (line[0] == '#') { - free(line); - continue; - } - if (strlen(line) == 0) { - free(line); - continue; - } - - if (line[0] == '[') { - char *close = strchr(line, ']'); - if (!close) { - free(line); - fclose(config); - fprintf(stderr, "Closing bracket not found on line %d\n", - line_number); - return 1; - } - char *name = calloc(1, close - line); - strncat(name, line + 1, close - line - 1); - type = nagbar_type_get(types, name); - if (!type) { - type = calloc(1, sizeof(struct sway_nagbar_type)); - type->name = strdup(name); - list_add(types, type); - } - free(name); - } else { - char flag[strlen(line) + 3]; - sprintf(flag, "--%s", line); - char *argv[] = {"swaynag", flag}; - int result; - if (type) { - result = nagbar_parse_type(2, argv, type); - } else { - result = parse_options(2, argv, nagbar, types, NULL, NULL); - } - if (result != 0) { - free(line); - fclose(config); - return result; - } - } - - free(line); - } - fclose(config); - return 0; -} - int main(int argc, char **argv) { int exit_code = EXIT_SUCCESS; @@ -327,7 +44,7 @@ int main(int argc, char **argv) { char *config_path = NULL; bool debug = false; - int launch_status = parse_options(argc, argv, NULL, NULL, + int launch_status = nagbar_parse_options(argc, argv, NULL, NULL, &config_path, &debug); if (launch_status != 0) { exit_code = launch_status; @@ -336,11 +53,11 @@ int main(int argc, char **argv) { wlr_log_init(debug ? WLR_DEBUG : WLR_ERROR, NULL); if (!config_path) { - config_path = get_config_path(); + config_path = nagbar_get_config_path(); } if (config_path) { wlr_log(WLR_DEBUG, "Loading config file: %s", config_path); - int config_status = load_config(config_path, &nagbar, types); + int config_status = nagbar_load_config(config_path, &nagbar, types); free(config_path); if (config_status != 0) { exit_code = config_status; @@ -349,7 +66,8 @@ int main(int argc, char **argv) { } if (argc > 1) { - int result = parse_options(argc, argv, &nagbar, types, NULL, NULL); + int result = nagbar_parse_options(argc, argv, &nagbar, types, + NULL, NULL); if (result != 0) { exit_code = result; goto cleanup; diff --git a/swaynag/meson.build b/swaynag/meson.build index 6a9df984..b2425504 100644 --- a/swaynag/meson.build +++ b/swaynag/meson.build @@ -1,5 +1,6 @@ executable( 'swaynag', [ + 'config.c', 'main.c', 'nagbar.c', 'render.c', diff --git a/swaynag/types.c b/swaynag/types.c index 8dd99d2a..dbc841f7 100644 --- a/swaynag/types.c +++ b/swaynag/types.c @@ -6,6 +6,7 @@ #include #include #include "list.h" +#include "swaynag/config.h" #include "swaynag/types.h" #include "util.h" From a6145914c60351d8e541192c7fe35556f8e02507 Mon Sep 17 00:00:00 2001 From: Brian Ashworth Date: Sat, 28 Jul 2018 23:15:12 -0400 Subject: [PATCH 130/148] swaynag: refactor {sway_,}nagbar to swaynag --- include/swaynag/config.h | 12 +- include/swaynag/render.h | 7 +- include/swaynag/{nagbar.h => swaynag.h} | 62 +++--- include/swaynag/types.h | 18 +- swaynag/config.c | 80 +++---- swaynag/main.c | 78 +++---- swaynag/meson.build | 2 +- swaynag/render.c | 254 ++++++++++----------- swaynag/{nagbar.c => swaynag.c} | 283 ++++++++++++------------ swaynag/types.c | 30 +-- 10 files changed, 415 insertions(+), 411 deletions(-) rename include/swaynag/{nagbar.h => swaynag.h} (51%) rename swaynag/{nagbar.c => swaynag.c} (51%) diff --git a/include/swaynag/config.h b/include/swaynag/config.h index caa32710..3fd5b3ce 100644 --- a/include/swaynag/config.h +++ b/include/swaynag/config.h @@ -1,13 +1,13 @@ -#ifndef _SWAY_NAGBAR_CONFIG_H -#define _SWAY_NAGBAR_CONFIG_H -#include "swaynag/nagbar.h" +#ifndef _SWAYNAG_CONFIG_H +#define _SWAYNAG_CONFIG_H +#include "swaynag/swaynag.h" #include "list.h" -int nagbar_parse_options(int argc, char **argv, struct sway_nagbar *nagbar, +int swaynag_parse_options(int argc, char **argv, struct swaynag *swaynag, list_t *types, char **config, bool *debug); -char *nagbar_get_config_path(void); +char *swaynag_get_config_path(void); -int nagbar_load_config(char *path, struct sway_nagbar *nagbar, list_t *types); +int swaynag_load_config(char *path, struct swaynag *swaynag, list_t *types); #endif diff --git a/include/swaynag/render.h b/include/swaynag/render.h index d9429f7f..d09e5929 100644 --- a/include/swaynag/render.h +++ b/include/swaynag/render.h @@ -1,6 +1,7 @@ -#ifndef _SWAY_NAGBAR_RENDER_H -#define _SWAY_NAGBAR_RENDER_H +#ifndef _SWAYNAG_RENDER_H +#define _SWAYNAG_RENDER_H +#include "swaynag/swaynag.h" -void render_frame(struct sway_nagbar *nagbar); +void render_frame(struct swaynag *swaynag); #endif diff --git a/include/swaynag/nagbar.h b/include/swaynag/swaynag.h similarity index 51% rename from include/swaynag/nagbar.h rename to include/swaynag/swaynag.h index 4ef101e9..6a56f14f 100644 --- a/include/swaynag/nagbar.h +++ b/include/swaynag/swaynag.h @@ -1,5 +1,5 @@ -#ifndef _SWAY_NAGBAR_NAGBAR_H -#define _SWAY_NAGBAR_NAGBAR_H +#ifndef _SWAYNAG_SWAYNAG_H +#define _SWAYNAG_SWAYNAG_H #include #include #include "list.h" @@ -7,24 +7,24 @@ #include "swaynag/types.h" #include "xdg-output-unstable-v1-client-protocol.h" -#define NAGBAR_BAR_BORDER_THICKNESS 2 -#define NAGBAR_MESSAGE_PADDING 8 -#define NAGBAR_DETAILS_BORDER_THICKNESS 3 -#define NAGBAR_BUTTON_BORDER_THICKNESS 3 -#define NAGBAR_BUTTON_GAP 20 -#define NAGBAR_BUTTON_GAP_CLOSE 15 -#define NAGBAR_BUTTON_MARGIN_RIGHT 2 -#define NAGBAR_BUTTON_PADDING 3 +#define SWAYNAG_BAR_BORDER_THICKNESS 2 +#define SWAYNAG_MESSAGE_PADDING 8 +#define SWAYNAG_DETAILS_BORDER_THICKNESS 3 +#define SWAYNAG_BUTTON_BORDER_THICKNESS 3 +#define SWAYNAG_BUTTON_GAP 20 +#define SWAYNAG_BUTTON_GAP_CLOSE 15 +#define SWAYNAG_BUTTON_MARGIN_RIGHT 2 +#define SWAYNAG_BUTTON_PADDING 3 -#define NAGBAR_MAX_HEIGHT 500 +#define SWAYNAG_MAX_HEIGHT 500 -enum sway_nagbar_action_type { - NAGBAR_ACTION_DISMISS, - NAGBAR_ACTION_EXPAND, - NAGBAR_ACTION_COMMAND, +enum swaynag_action_type { + SWAYNAG_ACTION_DISMISS, + SWAYNAG_ACTION_EXPAND, + SWAYNAG_ACTION_COMMAND, }; -struct sway_nagbar_pointer { +struct swaynag_pointer { struct wl_pointer *pointer; struct wl_cursor_theme *cursor_theme; struct wl_cursor_image *cursor_image; @@ -33,15 +33,15 @@ struct sway_nagbar_pointer { int y; }; -struct sway_nagbar_output { +struct swaynag_output { char *name; struct wl_output *wl_output; uint32_t wl_name; }; -struct sway_nagbar_button { +struct swaynag_button { char *text; - enum sway_nagbar_action_type type; + enum swaynag_action_type type; char *action; int x; int y; @@ -49,7 +49,7 @@ struct sway_nagbar_button { int height; }; -struct sway_nagbar_details { +struct swaynag_details { bool visible; char *message; @@ -61,12 +61,12 @@ struct sway_nagbar_details { int offset; int visible_lines; int total_lines; - struct sway_nagbar_button button_details; - struct sway_nagbar_button button_up; - struct sway_nagbar_button button_down; + struct swaynag_button button_details; + struct swaynag_button button_up; + struct swaynag_button button_down; }; -struct sway_nagbar { +struct swaynag { bool run_display; int querying_outputs; @@ -74,9 +74,9 @@ struct sway_nagbar { struct wl_compositor *compositor; struct wl_seat *seat; struct wl_shm *shm; - struct sway_nagbar_pointer pointer; + struct swaynag_pointer pointer; struct zxdg_output_manager_v1 *xdg_output_manager; - struct sway_nagbar_output output; + struct swaynag_output output; struct zwlr_layer_shell_v1 *layer_shell; struct zwlr_layer_surface_v1 *layer_surface; struct wl_surface *surface; @@ -87,18 +87,18 @@ struct sway_nagbar { struct pool_buffer buffers[2]; struct pool_buffer *current_buffer; - struct sway_nagbar_type *type; + struct swaynag_type *type; uint32_t anchors; char *message; char *font; list_t *buttons; - struct sway_nagbar_details details; + struct swaynag_details details; }; -void nagbar_setup(struct sway_nagbar *nagbar); +void swaynag_setup(struct swaynag *swaynag); -void nagbar_run(struct sway_nagbar *nagbar); +void swaynag_run(struct swaynag *swaynag); -void nagbar_destroy(struct sway_nagbar *nagbar); +void swaynag_destroy(struct swaynag *swaynag); #endif diff --git a/include/swaynag/types.h b/include/swaynag/types.h index 32056514..af83bd83 100644 --- a/include/swaynag/types.h +++ b/include/swaynag/types.h @@ -1,7 +1,7 @@ -#ifndef _SWAY_NAGBAR_TYPES_H -#define _SWAY_NAGBAR_TYPES_H +#ifndef _SWAYNAG_TYPES_H +#define _SWAYNAG_TYPES_H -struct sway_nagbar_type { +struct swaynag_type { char *name; uint32_t button_background; uint32_t background; @@ -10,16 +10,16 @@ struct sway_nagbar_type { uint32_t border_bottom; }; -void nagbar_types_add_default(list_t *types); +void swaynag_types_add_default(list_t *types); -struct sway_nagbar_type *nagbar_type_get(list_t *types, char *name); +struct swaynag_type *swaynag_type_get(list_t *types, char *name); -struct sway_nagbar_type *nagbar_type_clone(struct sway_nagbar_type *type); +struct swaynag_type *swaynag_type_clone(struct swaynag_type *type); -void nagbar_type_free(struct sway_nagbar_type *type); +void swaynag_type_free(struct swaynag_type *type); -void nagbar_types_free(list_t *types); +void swaynag_types_free(list_t *types); -int nagbar_parse_type(int argc, char **argv, struct sway_nagbar_type *type); +int swaynag_parse_type(int argc, char **argv, struct swaynag_type *type); #endif diff --git a/swaynag/config.c b/swaynag/config.c index 4e7edf2e..289fc82a 100644 --- a/swaynag/config.c +++ b/swaynag/config.c @@ -6,7 +6,7 @@ #include "log.h" #include "list.h" #include "readline.h" -#include "swaynag/nagbar.h" +#include "swaynag/swaynag.h" #include "swaynag/types.h" #include "wlr-layer-shell-unstable-v1-client-protocol.h" @@ -36,7 +36,7 @@ static char *read_from_stdin() { return buffer; } -int nagbar_parse_options(int argc, char **argv, struct sway_nagbar *nagbar, +int swaynag_parse_options(int argc, char **argv, struct swaynag *swaynag, list_t *types, char **config, bool *debug) { static struct option opts[] = { {"button", required_argument, NULL, 'b'}, @@ -81,17 +81,17 @@ int nagbar_parse_options(int argc, char **argv, struct sway_nagbar *nagbar, } switch (c) { case 'b': // Button - if (nagbar) { + if (swaynag) { if (optind >= argc) { fprintf(stderr, "Missing action for button %s\n", optarg); return EXIT_FAILURE; } - struct sway_nagbar_button *button; - button = calloc(sizeof(struct sway_nagbar_button), 1); + struct swaynag_button *button; + button = calloc(sizeof(struct swaynag_button), 1); button->text = strdup(optarg); - button->type = NAGBAR_ACTION_COMMAND; + button->type = SWAYNAG_ACTION_COMMAND; button->action = strdup(argv[optind]); - list_add(nagbar->buttons, button); + list_add(swaynag->buttons, button); } optind++; break; @@ -106,13 +106,13 @@ int nagbar_parse_options(int argc, char **argv, struct sway_nagbar *nagbar, } break; case 'e': // Edge - if (nagbar) { + if (swaynag) { if (strcmp(optarg, "top") == 0) { - nagbar->anchors = ZWLR_LAYER_SURFACE_V1_ANCHOR_TOP + swaynag->anchors = ZWLR_LAYER_SURFACE_V1_ANCHOR_TOP | ZWLR_LAYER_SURFACE_V1_ANCHOR_LEFT | ZWLR_LAYER_SURFACE_V1_ANCHOR_RIGHT; } else if (strcmp(optarg, "bottom") == 0) { - nagbar->anchors = ZWLR_LAYER_SURFACE_V1_ANCHOR_BOTTOM + swaynag->anchors = ZWLR_LAYER_SURFACE_V1_ANCHOR_BOTTOM | ZWLR_LAYER_SURFACE_V1_ANCHOR_LEFT | ZWLR_LAYER_SURFACE_V1_ANCHOR_RIGHT; } else { @@ -122,49 +122,49 @@ int nagbar_parse_options(int argc, char **argv, struct sway_nagbar *nagbar, } break; case 'f': // Font - if (nagbar) { - free(nagbar->font); - nagbar->font = strdup(optarg); + if (swaynag) { + free(swaynag->font); + swaynag->font = strdup(optarg); } break; case 'l': // Detailed Message - if (nagbar) { - free(nagbar->details.message); - nagbar->details.message = read_from_stdin(); - nagbar->details.button_up.text = strdup("▲"); - nagbar->details.button_down.text = strdup("▼"); + if (swaynag) { + free(swaynag->details.message); + swaynag->details.message = read_from_stdin(); + swaynag->details.button_up.text = strdup("▲"); + swaynag->details.button_down.text = strdup("▼"); } break; case 'L': // Detailed Button Text - if (nagbar) { - free(nagbar->details.button_details.text); - nagbar->details.button_details.text = strdup(optarg); + if (swaynag) { + free(swaynag->details.button_details.text); + swaynag->details.button_details.text = strdup(optarg); } break; case 'm': // Message - if (nagbar) { - free(nagbar->message); - nagbar->message = strdup(optarg); + if (swaynag) { + free(swaynag->message); + swaynag->message = strdup(optarg); } break; case 'o': // Output - if (nagbar) { - free(nagbar->output.name); - nagbar->output.name = strdup(optarg); + if (swaynag) { + free(swaynag->output.name); + swaynag->output.name = strdup(optarg); } break; case 's': // Dismiss Button Text - if (nagbar) { - struct sway_nagbar_button *button_close; - button_close = nagbar->buttons->items[0]; + if (swaynag) { + struct swaynag_button *button_close; + button_close = swaynag->buttons->items[0]; free(button_close->text); button_close->text = strdup(optarg); } break; case 't': // Type - if (nagbar) { - nagbar->type = nagbar_type_get(types, optarg); - if (!nagbar->type) { + if (swaynag) { + swaynag->type = swaynag_type_get(types, optarg); + if (!swaynag->type) { fprintf(stderr, "Unknown type %s\n", optarg); return EXIT_FAILURE; } @@ -186,7 +186,7 @@ static bool file_exists(const char *path) { return path && access(path, R_OK) != -1; } -char *nagbar_get_config_path(void) { +char *swaynag_get_config_path(void) { static const char *config_paths[] = { "$HOME/.swaynag/config", "$XDG_CONFIG_HOME/swaynag/config", @@ -223,13 +223,13 @@ char *nagbar_get_config_path(void) { return NULL; } -int nagbar_load_config(char *path, struct sway_nagbar *nagbar, list_t *types) { +int swaynag_load_config(char *path, struct swaynag *swaynag, list_t *types) { FILE *config = fopen(path, "r"); if (!config) { fprintf(stderr, "Failed to read config. Running without it.\n"); return 0; } - struct sway_nagbar_type *type = NULL; + struct swaynag_type *type = NULL; char *line; int line_number = 0; while (!feof(config)) { @@ -259,9 +259,9 @@ int nagbar_load_config(char *path, struct sway_nagbar *nagbar, list_t *types) { } char *name = calloc(1, close - line); strncat(name, line + 1, close - line - 1); - type = nagbar_type_get(types, name); + type = swaynag_type_get(types, name); if (!type) { - type = calloc(1, sizeof(struct sway_nagbar_type)); + type = calloc(1, sizeof(struct swaynag_type)); type->name = strdup(name); list_add(types, type); } @@ -272,9 +272,9 @@ int nagbar_load_config(char *path, struct sway_nagbar *nagbar, list_t *types) { char *argv[] = {"swaynag", flag}; int result; if (type) { - result = nagbar_parse_type(2, argv, type); + result = swaynag_parse_type(2, argv, type); } else { - result = nagbar_parse_options(2, argv, nagbar, types, + result = swaynag_parse_options(2, argv, swaynag, types, NULL, NULL); } if (result != 0) { diff --git a/swaynag/main.c b/swaynag/main.c index 5c97e12a..0493c1f0 100644 --- a/swaynag/main.c +++ b/swaynag/main.c @@ -3,19 +3,19 @@ #include "log.h" #include "list.h" #include "swaynag/config.h" -#include "swaynag/nagbar.h" +#include "swaynag/swaynag.h" #include "swaynag/types.h" #include "wlr-layer-shell-unstable-v1-client-protocol.h" -static struct sway_nagbar nagbar; +static struct swaynag swaynag; void sig_handler(int signal) { - nagbar_destroy(&nagbar); + swaynag_destroy(&swaynag); exit(EXIT_FAILURE); } void sway_terminate(int code) { - nagbar_destroy(&nagbar); + swaynag_destroy(&swaynag); exit(code); } @@ -23,28 +23,28 @@ int main(int argc, char **argv) { int exit_code = EXIT_SUCCESS; list_t *types = create_list(); - nagbar_types_add_default(types); + swaynag_types_add_default(types); - memset(&nagbar, 0, sizeof(nagbar)); - nagbar.anchors = ZWLR_LAYER_SURFACE_V1_ANCHOR_TOP + memset(&swaynag, 0, sizeof(swaynag)); + swaynag.anchors = ZWLR_LAYER_SURFACE_V1_ANCHOR_TOP | ZWLR_LAYER_SURFACE_V1_ANCHOR_LEFT | ZWLR_LAYER_SURFACE_V1_ANCHOR_RIGHT; - nagbar.font = strdup("pango:monospace 10"); - nagbar.buttons = create_list(); + swaynag.font = strdup("pango:monospace 10"); + swaynag.buttons = create_list(); - struct sway_nagbar_button *button_close = - calloc(sizeof(struct sway_nagbar_button), 1); + struct swaynag_button *button_close = + calloc(sizeof(struct swaynag_button), 1); button_close->text = strdup("X"); - button_close->type = NAGBAR_ACTION_DISMISS; - list_add(nagbar.buttons, button_close); + button_close->type = SWAYNAG_ACTION_DISMISS; + list_add(swaynag.buttons, button_close); - nagbar.details.button_details.text = strdup("Toggle Details"); - nagbar.details.button_details.type = NAGBAR_ACTION_EXPAND; + swaynag.details.button_details.text = strdup("Toggle Details"); + swaynag.details.button_details.type = SWAYNAG_ACTION_EXPAND; char *config_path = NULL; bool debug = false; - int launch_status = nagbar_parse_options(argc, argv, NULL, NULL, + int launch_status = swaynag_parse_options(argc, argv, NULL, NULL, &config_path, &debug); if (launch_status != 0) { exit_code = launch_status; @@ -53,11 +53,11 @@ int main(int argc, char **argv) { wlr_log_init(debug ? WLR_DEBUG : WLR_ERROR, NULL); if (!config_path) { - config_path = nagbar_get_config_path(); + config_path = swaynag_get_config_path(); } if (config_path) { wlr_log(WLR_DEBUG, "Loading config file: %s", config_path); - int config_status = nagbar_load_config(config_path, &nagbar, types); + int config_status = swaynag_load_config(config_path, &swaynag, types); free(config_path); if (config_status != 0) { exit_code = config_status; @@ -66,7 +66,7 @@ int main(int argc, char **argv) { } if (argc > 1) { - int result = nagbar_parse_options(argc, argv, &nagbar, types, + int result = swaynag_parse_options(argc, argv, &swaynag, types, NULL, NULL); if (result != 0) { exit_code = result; @@ -74,46 +74,46 @@ int main(int argc, char **argv) { } } - if (!nagbar.message) { + if (!swaynag.message) { wlr_log(WLR_ERROR, "No message passed. Please provide --message/-m"); exit_code = EXIT_FAILURE; goto cleanup; } - if (!nagbar.type) { - nagbar.type = nagbar_type_get(types, "error"); + if (!swaynag.type) { + swaynag.type = swaynag_type_get(types, "error"); } - nagbar.type = nagbar_type_clone(nagbar.type); - nagbar_types_free(types); + swaynag.type = swaynag_type_clone(swaynag.type); + swaynag_types_free(types); - if (nagbar.details.message) { - list_add(nagbar.buttons, &nagbar.details.button_details); + if (swaynag.details.message) { + list_add(swaynag.buttons, &swaynag.details.button_details); } else { - free(nagbar.details.button_details.text); + free(swaynag.details.button_details.text); } - wlr_log(WLR_DEBUG, "Output: %s", nagbar.output.name); - wlr_log(WLR_DEBUG, "Anchors: %d", nagbar.anchors); - wlr_log(WLR_DEBUG, "Type: %s", nagbar.type->name); - wlr_log(WLR_DEBUG, "Message: %s", nagbar.message); - wlr_log(WLR_DEBUG, "Font: %s", nagbar.font); + wlr_log(WLR_DEBUG, "Output: %s", swaynag.output.name); + wlr_log(WLR_DEBUG, "Anchors: %d", swaynag.anchors); + wlr_log(WLR_DEBUG, "Type: %s", swaynag.type->name); + wlr_log(WLR_DEBUG, "Message: %s", swaynag.message); + wlr_log(WLR_DEBUG, "Font: %s", swaynag.font); wlr_log(WLR_DEBUG, "Buttons"); - for (int i = 0; i < nagbar.buttons->length; i++) { - struct sway_nagbar_button *button = nagbar.buttons->items[i]; + for (int i = 0; i < swaynag.buttons->length; i++) { + struct swaynag_button *button = swaynag.buttons->items[i]; wlr_log(WLR_DEBUG, "\t[%s] `%s`", button->text, button->action); } signal(SIGTERM, sig_handler); - nagbar_setup(&nagbar); - nagbar_run(&nagbar); + swaynag_setup(&swaynag); + swaynag_run(&swaynag); return exit_code; cleanup: - nagbar_types_free(types); - free(nagbar.details.button_details.text); - nagbar_destroy(&nagbar); + swaynag_types_free(types); + free(swaynag.details.button_details.text); + swaynag_destroy(&swaynag); return exit_code; } diff --git a/swaynag/meson.build b/swaynag/meson.build index b2425504..f67c10ff 100644 --- a/swaynag/meson.build +++ b/swaynag/meson.build @@ -2,8 +2,8 @@ executable( 'swaynag', [ 'config.c', 'main.c', - 'nagbar.c', 'render.c', + 'swaynag.c', 'types.c', ], include_directories: [sway_inc], diff --git a/swaynag/render.c b/swaynag/render.c index ef0f72a8..67e26eaf 100644 --- a/swaynag/render.c +++ b/swaynag/render.c @@ -3,99 +3,100 @@ #include "log.h" #include "pango.h" #include "pool-buffer.h" -#include "swaynag/nagbar.h" +#include "swaynag/swaynag.h" #include "swaynag/types.h" #include "wlr-layer-shell-unstable-v1-client-protocol.h" -static uint32_t render_message(cairo_t *cairo, struct sway_nagbar *nagbar) { - uint32_t height = nagbar->height * nagbar->scale; - height -= NAGBAR_BAR_BORDER_THICKNESS * nagbar->scale; +static uint32_t render_message(cairo_t *cairo, struct swaynag *swaynag) { + uint32_t height = swaynag->height * swaynag->scale; + height -= SWAYNAG_BAR_BORDER_THICKNESS * swaynag->scale; int text_width, text_height; - get_text_size(cairo, nagbar->font, &text_width, &text_height, - nagbar->scale, true, "%s", nagbar->message); + get_text_size(cairo, swaynag->font, &text_width, &text_height, + swaynag->scale, true, "%s", swaynag->message); - int padding = NAGBAR_MESSAGE_PADDING * nagbar->scale; + int padding = SWAYNAG_MESSAGE_PADDING * swaynag->scale; uint32_t ideal_height = text_height + padding * 2; - uint32_t ideal_surface_height = ideal_height / nagbar->scale; - if (nagbar->height < ideal_surface_height) { + uint32_t ideal_surface_height = ideal_height / swaynag->scale; + if (swaynag->height < ideal_surface_height) { return ideal_surface_height; } - cairo_set_source_u32(cairo, nagbar->type->text); + cairo_set_source_u32(cairo, swaynag->type->text); cairo_move_to(cairo, padding, (int)(ideal_height - text_height) / 2); - pango_printf(cairo, nagbar->font, nagbar->scale, false, "%s", - nagbar->message); + pango_printf(cairo, swaynag->font, swaynag->scale, false, "%s", + swaynag->message); return ideal_height; } static void render_details_scroll_button(cairo_t *cairo, - struct sway_nagbar *nagbar, struct sway_nagbar_button *button) { + struct swaynag *swaynag, struct swaynag_button *button) { int text_width, text_height; - get_text_size(cairo, nagbar->font, &text_width, &text_height, - nagbar->scale, true, "%s", button->text); + get_text_size(cairo, swaynag->font, &text_width, &text_height, + swaynag->scale, true, "%s", button->text); - int border = NAGBAR_BUTTON_BORDER_THICKNESS * nagbar->scale; - int padding = NAGBAR_BUTTON_PADDING * nagbar->scale; + int border = SWAYNAG_BUTTON_BORDER_THICKNESS * swaynag->scale; + int padding = SWAYNAG_BUTTON_PADDING * swaynag->scale; - cairo_set_source_u32(cairo, nagbar->type->border); + cairo_set_source_u32(cairo, swaynag->type->border); cairo_rectangle(cairo, button->x, button->y, button->width, button->height); cairo_fill(cairo); - cairo_set_source_u32(cairo, nagbar->type->button_background); + cairo_set_source_u32(cairo, swaynag->type->button_background); cairo_rectangle(cairo, button->x + border, button->y + border, button->width - (border * 2), button->height - (border * 2)); cairo_fill(cairo); - cairo_set_source_u32(cairo, nagbar->type->text); + cairo_set_source_u32(cairo, swaynag->type->text); cairo_move_to(cairo, button->x + border + padding, button->y + border + (button->height - text_height) / 2); - pango_printf(cairo, nagbar->font, nagbar->scale, true, "%s", button->text); + pango_printf(cairo, swaynag->font, swaynag->scale, true, + "%s", button->text); } static int get_detailed_scroll_button_width(cairo_t *cairo, - struct sway_nagbar *nagbar) { + struct swaynag *swaynag) { int up_width, down_width, temp_height; - get_text_size(cairo, nagbar->font, &up_width, &temp_height, - nagbar->scale, true, "%s", nagbar->details.button_up.text); - get_text_size(cairo, nagbar->font, &down_width, &temp_height, - nagbar->scale, true, "%s", nagbar->details.button_down.text); + get_text_size(cairo, swaynag->font, &up_width, &temp_height, + swaynag->scale, true, "%s", swaynag->details.button_up.text); + get_text_size(cairo, swaynag->font, &down_width, &temp_height, + swaynag->scale, true, "%s", swaynag->details.button_down.text); int text_width = up_width > down_width ? up_width : down_width; - int border = NAGBAR_BUTTON_BORDER_THICKNESS * nagbar->scale; - int padding = NAGBAR_BUTTON_PADDING * nagbar->scale; + int border = SWAYNAG_BUTTON_BORDER_THICKNESS * swaynag->scale; + int padding = SWAYNAG_BUTTON_PADDING * swaynag->scale; return text_width + border * 2 + padding * 2; } -static uint32_t render_detailed(cairo_t *cairo, struct sway_nagbar *nagbar, +static uint32_t render_detailed(cairo_t *cairo, struct swaynag *swaynag, uint32_t y) { - uint32_t width = nagbar->width * nagbar->scale; - uint32_t height = nagbar->height * nagbar->scale; - height -= NAGBAR_BAR_BORDER_THICKNESS * nagbar->scale; + uint32_t width = swaynag->width * swaynag->scale; + uint32_t height = swaynag->height * swaynag->scale; + height -= SWAYNAG_BAR_BORDER_THICKNESS * swaynag->scale; - int border = NAGBAR_DETAILS_BORDER_THICKNESS * nagbar->scale; - int padding = NAGBAR_MESSAGE_PADDING * nagbar->scale; + int border = SWAYNAG_DETAILS_BORDER_THICKNESS * swaynag->scale; + int padding = SWAYNAG_MESSAGE_PADDING * swaynag->scale; int decor = padding + border; - nagbar->details.x = decor; - nagbar->details.y = y + decor; - nagbar->details.width = width - decor * 2; + swaynag->details.x = decor; + swaynag->details.y = y + decor; + swaynag->details.width = width - decor * 2; - PangoLayout *layout = get_pango_layout(cairo, nagbar->font, - nagbar->details.message, nagbar->scale, false); + PangoLayout *layout = get_pango_layout(cairo, swaynag->font, + swaynag->details.message, swaynag->scale, false); pango_layout_set_width(layout, - (nagbar->details.width - padding * 2) * PANGO_SCALE); + (swaynag->details.width - padding * 2) * PANGO_SCALE); pango_layout_set_wrap(layout, PANGO_WRAP_WORD_CHAR); pango_layout_set_single_paragraph_mode(layout, false); pango_cairo_update_layout(cairo, layout); - nagbar->details.total_lines = pango_layout_get_line_count(layout); + swaynag->details.total_lines = pango_layout_get_line_count(layout); PangoLayoutLine *line; - line = pango_layout_get_line_readonly(layout, nagbar->details.offset); + line = pango_layout_get_line_readonly(layout, swaynag->details.offset); gint offset = line->start_index; const char *text = pango_layout_get_text(layout); pango_layout_set_text(layout, text + offset, strlen(text) - offset); @@ -104,87 +105,87 @@ static uint32_t render_detailed(cairo_t *cairo, struct sway_nagbar *nagbar, pango_cairo_update_layout(cairo, layout); pango_layout_get_pixel_size(layout, &text_width, &text_height); - bool show_buttons = nagbar->details.offset > 0; - int button_width = get_detailed_scroll_button_width(cairo, nagbar); + bool show_buttons = swaynag->details.offset > 0; + int button_width = get_detailed_scroll_button_width(cairo, swaynag); if (show_buttons) { - nagbar->details.width -= button_width; + swaynag->details.width -= button_width; pango_layout_set_width(layout, - (nagbar->details.width - padding * 2) * PANGO_SCALE); + (swaynag->details.width - padding * 2) * PANGO_SCALE); } uint32_t ideal_height; do { - ideal_height = nagbar->details.y + text_height + decor + padding * 2; - if (ideal_height > NAGBAR_MAX_HEIGHT) { - ideal_height = NAGBAR_MAX_HEIGHT; + ideal_height = swaynag->details.y + text_height + decor + padding * 2; + if (ideal_height > SWAYNAG_MAX_HEIGHT) { + ideal_height = SWAYNAG_MAX_HEIGHT; if (!show_buttons) { show_buttons = true; - nagbar->details.width -= button_width; + swaynag->details.width -= button_width; pango_layout_set_width(layout, - (nagbar->details.width - padding * 2) * PANGO_SCALE); + (swaynag->details.width - padding * 2) * PANGO_SCALE); } } - nagbar->details.height = ideal_height - nagbar->details.y - decor; + swaynag->details.height = ideal_height - swaynag->details.y - decor; pango_layout_set_height(layout, - (nagbar->details.height - padding * 2) * PANGO_SCALE); + (swaynag->details.height - padding * 2) * PANGO_SCALE); pango_layout_set_ellipsize(layout, PANGO_ELLIPSIZE_END); pango_cairo_update_layout(cairo, layout); pango_layout_get_pixel_size(layout, &text_width, &text_height); - } while (text_height != (nagbar->details.height - padding * 2)); + } while (text_height != (swaynag->details.height - padding * 2)); - nagbar->details.visible_lines = pango_layout_get_line_count(layout); + swaynag->details.visible_lines = pango_layout_get_line_count(layout); if (show_buttons) { - nagbar->details.button_up.x = - nagbar->details.x + nagbar->details.width; - nagbar->details.button_up.y = nagbar->details.y; - nagbar->details.button_up.width = button_width; - nagbar->details.button_up.height = nagbar->details.height / 2; - render_details_scroll_button(cairo, nagbar, - &nagbar->details.button_up); + swaynag->details.button_up.x = + swaynag->details.x + swaynag->details.width; + swaynag->details.button_up.y = swaynag->details.y; + swaynag->details.button_up.width = button_width; + swaynag->details.button_up.height = swaynag->details.height / 2; + render_details_scroll_button(cairo, swaynag, + &swaynag->details.button_up); - nagbar->details.button_down.x = - nagbar->details.x + nagbar->details.width; - nagbar->details.button_down.y = - nagbar->details.button_up.y + nagbar->details.button_up.height; - nagbar->details.button_down.width = button_width; - nagbar->details.button_down.height = nagbar->details.height / 2; - render_details_scroll_button(cairo, nagbar, - &nagbar->details.button_down); + swaynag->details.button_down.x = + swaynag->details.x + swaynag->details.width; + swaynag->details.button_down.y = + swaynag->details.button_up.y + swaynag->details.button_up.height; + swaynag->details.button_down.width = button_width; + swaynag->details.button_down.height = swaynag->details.height / 2; + render_details_scroll_button(cairo, swaynag, + &swaynag->details.button_down); } - cairo_set_source_u32(cairo, nagbar->type->border); - cairo_rectangle(cairo, nagbar->details.x, nagbar->details.y, - nagbar->details.width, nagbar->details.height); + cairo_set_source_u32(cairo, swaynag->type->border); + cairo_rectangle(cairo, swaynag->details.x, swaynag->details.y, + swaynag->details.width, swaynag->details.height); cairo_fill(cairo); - cairo_move_to(cairo, nagbar->details.x + padding, - nagbar->details.y + padding); - cairo_set_source_u32(cairo, nagbar->type->text); + cairo_move_to(cairo, swaynag->details.x + padding, + swaynag->details.y + padding); + cairo_set_source_u32(cairo, swaynag->type->text); pango_cairo_show_layout(cairo, layout); g_object_unref(layout); return ideal_height; } -static uint32_t render_button(cairo_t *cairo, struct sway_nagbar *nagbar, +static uint32_t render_button(cairo_t *cairo, struct swaynag *swaynag, int button_index, int *x) { - uint32_t height = nagbar->height * nagbar->scale; - height -= NAGBAR_BAR_BORDER_THICKNESS * nagbar->scale; - struct sway_nagbar_button *button = nagbar->buttons->items[button_index]; + uint32_t height = swaynag->height * swaynag->scale; + height -= SWAYNAG_BAR_BORDER_THICKNESS * swaynag->scale; + struct swaynag_button *button = swaynag->buttons->items[button_index]; int text_width, text_height; - get_text_size(cairo, nagbar->font, &text_width, &text_height, - nagbar->scale, true, "%s", button->text); + get_text_size(cairo, swaynag->font, &text_width, &text_height, + swaynag->scale, true, "%s", button->text); - int border = NAGBAR_BUTTON_BORDER_THICKNESS * nagbar->scale; - int padding = NAGBAR_BUTTON_PADDING * nagbar->scale; + int border = SWAYNAG_BUTTON_BORDER_THICKNESS * swaynag->scale; + int padding = SWAYNAG_BUTTON_PADDING * swaynag->scale; uint32_t ideal_height = text_height + padding * 2 + border * 2; - uint32_t ideal_surface_height = ideal_height / nagbar->scale; - if (nagbar->height < ideal_surface_height) { + uint32_t ideal_surface_height = ideal_height / swaynag->scale; + if (swaynag->height < ideal_surface_height) { return ideal_surface_height; } @@ -193,64 +194,65 @@ static uint32_t render_button(cairo_t *cairo, struct sway_nagbar *nagbar, button->width = text_width + padding * 2; button->height = text_height + padding * 2; - cairo_set_source_u32(cairo, nagbar->type->border); + cairo_set_source_u32(cairo, swaynag->type->border); cairo_rectangle(cairo, button->x - border, button->y - border, button->width + border * 2, button->height + border * 2); cairo_fill(cairo); - cairo_set_source_u32(cairo, nagbar->type->button_background); + cairo_set_source_u32(cairo, swaynag->type->button_background); cairo_rectangle(cairo, button->x, button->y, button->width, button->height); cairo_fill(cairo); - cairo_set_source_u32(cairo, nagbar->type->text); + cairo_set_source_u32(cairo, swaynag->type->text); cairo_move_to(cairo, button->x + padding, button->y + padding); - pango_printf(cairo, nagbar->font, nagbar->scale, true, "%s", button->text); + pango_printf(cairo, swaynag->font, swaynag->scale, true, + "%s", button->text); *x = button->x - border; return ideal_height; } -static uint32_t render_to_cairo(cairo_t *cairo, struct sway_nagbar *nagbar) { +static uint32_t render_to_cairo(cairo_t *cairo, struct swaynag *swaynag) { uint32_t max_height = 0; cairo_set_operator(cairo, CAIRO_OPERATOR_SOURCE); - cairo_set_source_u32(cairo, nagbar->type->background); + cairo_set_source_u32(cairo, swaynag->type->background); cairo_paint(cairo); - uint32_t h = render_message(cairo, nagbar); + uint32_t h = render_message(cairo, swaynag); max_height = h > max_height ? h : max_height; - int x = (nagbar->width - NAGBAR_BUTTON_MARGIN_RIGHT) * nagbar->scale; - for (int i = 0; i < nagbar->buttons->length; i++) { - h = render_button(cairo, nagbar, i, &x); + int x = (swaynag->width - SWAYNAG_BUTTON_MARGIN_RIGHT) * swaynag->scale; + for (int i = 0; i < swaynag->buttons->length; i++) { + h = render_button(cairo, swaynag, i, &x); max_height = h > max_height ? h : max_height; - x -= NAGBAR_BUTTON_GAP * nagbar->scale; + x -= SWAYNAG_BUTTON_GAP * swaynag->scale; if (i == 0) { - x -= NAGBAR_BUTTON_GAP_CLOSE * nagbar->scale; + x -= SWAYNAG_BUTTON_GAP_CLOSE * swaynag->scale; } } - if (nagbar->details.visible) { - h = render_detailed(cairo, nagbar, max_height); + if (swaynag->details.visible) { + h = render_detailed(cairo, swaynag, max_height); max_height = h > max_height ? h : max_height; } - int border = NAGBAR_BAR_BORDER_THICKNESS * nagbar->scale; - if (max_height > nagbar->height) { + int border = SWAYNAG_BAR_BORDER_THICKNESS * swaynag->scale; + if (max_height > swaynag->height) { max_height += border; } - cairo_set_source_u32(cairo, nagbar->type->border_bottom); - cairo_rectangle(cairo, 0, nagbar->height * nagbar->scale - border, - nagbar->width * nagbar->scale, border); + cairo_set_source_u32(cairo, swaynag->type->border_bottom); + cairo_rectangle(cairo, 0, swaynag->height * swaynag->scale - border, + swaynag->width * swaynag->scale, border); cairo_fill(cairo); return max_height; } -void render_frame(struct sway_nagbar *nagbar) { - if (!nagbar->run_display) { +void render_frame(struct swaynag *swaynag) { + if (!swaynag->run_display) { return; } @@ -261,24 +263,24 @@ void render_frame(struct sway_nagbar *nagbar) { cairo_set_operator(cairo, CAIRO_OPERATOR_CLEAR); cairo_paint(cairo); cairo_restore(cairo); - uint32_t height = render_to_cairo(cairo, nagbar); - if (height != nagbar->height) { - zwlr_layer_surface_v1_set_size(nagbar->layer_surface, 0, height); - zwlr_layer_surface_v1_set_exclusive_zone(nagbar->layer_surface, + uint32_t height = render_to_cairo(cairo, swaynag); + if (height != swaynag->height) { + zwlr_layer_surface_v1_set_size(swaynag->layer_surface, 0, height); + zwlr_layer_surface_v1_set_exclusive_zone(swaynag->layer_surface, height); - wl_surface_commit(nagbar->surface); - wl_display_roundtrip(nagbar->display); + wl_surface_commit(swaynag->surface); + wl_display_roundtrip(swaynag->display); } else { - nagbar->current_buffer = get_next_buffer(nagbar->shm, - nagbar->buffers, - nagbar->width * nagbar->scale, - nagbar->height * nagbar->scale); - if (!nagbar->current_buffer) { + swaynag->current_buffer = get_next_buffer(swaynag->shm, + swaynag->buffers, + swaynag->width * swaynag->scale, + swaynag->height * swaynag->scale); + if (!swaynag->current_buffer) { wlr_log(WLR_DEBUG, "Failed to get buffer. Skipping frame."); goto cleanup; } - cairo_t *shm = nagbar->current_buffer->cairo; + cairo_t *shm = swaynag->current_buffer->cairo; cairo_save(shm); cairo_set_operator(shm, CAIRO_OPERATOR_CLEAR); cairo_paint(shm); @@ -286,13 +288,13 @@ void render_frame(struct sway_nagbar *nagbar) { cairo_set_source_surface(shm, recorder, 0.0, 0.0); cairo_paint(shm); - wl_surface_set_buffer_scale(nagbar->surface, nagbar->scale); - wl_surface_attach(nagbar->surface, - nagbar->current_buffer->buffer, 0, 0); - wl_surface_damage(nagbar->surface, 0, 0, - nagbar->width, nagbar->height); - wl_surface_commit(nagbar->surface); - wl_display_roundtrip(nagbar->display); + wl_surface_set_buffer_scale(swaynag->surface, swaynag->scale); + wl_surface_attach(swaynag->surface, + swaynag->current_buffer->buffer, 0, 0); + wl_surface_damage(swaynag->surface, 0, 0, + swaynag->width, swaynag->height); + wl_surface_commit(swaynag->surface); + wl_display_roundtrip(swaynag->display); } cleanup: diff --git a/swaynag/nagbar.c b/swaynag/swaynag.c similarity index 51% rename from swaynag/nagbar.c rename to swaynag/swaynag.c index e451a53a..367e0854 100644 --- a/swaynag/nagbar.c +++ b/swaynag/swaynag.c @@ -6,8 +6,8 @@ #include #include "log.h" #include "list.h" -#include "swaynag/nagbar.h" #include "swaynag/render.h" +#include "swaynag/swaynag.h" #include "swaynag/types.h" #include "wlr-layer-shell-unstable-v1-client-protocol.h" @@ -32,14 +32,14 @@ static bool terminal_execute(char *terminal, char *command) { return true; } -static void nagbar_button_execute(struct sway_nagbar *nagbar, - struct sway_nagbar_button *button) { +static void swaynag_button_execute(struct swaynag *swaynag, + struct swaynag_button *button) { wlr_log(WLR_DEBUG, "Executing [%s]: %s", button->text, button->action); - if (button->type == NAGBAR_ACTION_DISMISS) { - nagbar->run_display = false; - } else if (button->type == NAGBAR_ACTION_EXPAND) { - nagbar->details.visible = !nagbar->details.visible; - render_frame(nagbar); + if (button->type == SWAYNAG_ACTION_DISMISS) { + swaynag->run_display = false; + } else if (button->type == SWAYNAG_ACTION_EXPAND) { + swaynag->details.visible = !swaynag->details.visible; + render_frame(swaynag); } else { if (fork() == 0) { // Child process. Will be used to prevent zombie processes @@ -50,7 +50,7 @@ static void nagbar_button_execute(struct sway_nagbar *nagbar, if (terminal && strlen(terminal)) { wlr_log(WLR_DEBUG, "Found $TERMINAL: %s", terminal); if (!terminal_execute(terminal, button->action)) { - nagbar_destroy(nagbar); + swaynag_destroy(swaynag); exit(EXIT_FAILURE); } } else { @@ -67,17 +67,17 @@ static void nagbar_button_execute(struct sway_nagbar *nagbar, static void layer_surface_configure(void *data, struct zwlr_layer_surface_v1 *surface, uint32_t serial, uint32_t width, uint32_t height) { - struct sway_nagbar *nagbar = data; - nagbar->width = width; - nagbar->height = height; + struct swaynag *swaynag = data; + swaynag->width = width; + swaynag->height = height; zwlr_layer_surface_v1_ack_configure(surface, serial); - render_frame(nagbar); + render_frame(swaynag); } static void layer_surface_closed(void *data, struct zwlr_layer_surface_v1 *surface) { - struct sway_nagbar *nagbar = data; - nagbar_destroy(nagbar); + struct swaynag *swaynag = data; + swaynag_destroy(swaynag); } static struct zwlr_layer_surface_v1_listener layer_surface_listener = { @@ -88,67 +88,67 @@ static struct zwlr_layer_surface_v1_listener layer_surface_listener = { static void wl_pointer_enter(void *data, struct wl_pointer *wl_pointer, uint32_t serial, struct wl_surface *surface, wl_fixed_t surface_x, wl_fixed_t surface_y) { - struct sway_nagbar *nagbar = data; - struct sway_nagbar_pointer *pointer = &nagbar->pointer; - wl_surface_set_buffer_scale(pointer->cursor_surface, nagbar->scale); + struct swaynag *swaynag = data; + struct swaynag_pointer *pointer = &swaynag->pointer; + wl_surface_set_buffer_scale(pointer->cursor_surface, swaynag->scale); wl_surface_attach(pointer->cursor_surface, wl_cursor_image_get_buffer(pointer->cursor_image), 0, 0); wl_pointer_set_cursor(wl_pointer, serial, pointer->cursor_surface, - pointer->cursor_image->hotspot_x / nagbar->scale, - pointer->cursor_image->hotspot_y / nagbar->scale); + pointer->cursor_image->hotspot_x / swaynag->scale, + pointer->cursor_image->hotspot_y / swaynag->scale); wl_surface_commit(pointer->cursor_surface); } static void wl_pointer_motion(void *data, struct wl_pointer *wl_pointer, uint32_t time, wl_fixed_t surface_x, wl_fixed_t surface_y) { - struct sway_nagbar *nagbar = data; - nagbar->pointer.x = wl_fixed_to_int(surface_x); - nagbar->pointer.y = wl_fixed_to_int(surface_y); + struct swaynag *swaynag = data; + swaynag->pointer.x = wl_fixed_to_int(surface_x); + swaynag->pointer.y = wl_fixed_to_int(surface_y); } static void wl_pointer_button(void *data, struct wl_pointer *wl_pointer, uint32_t serial, uint32_t time, uint32_t button, uint32_t state) { - struct sway_nagbar *nagbar = data; + struct swaynag *swaynag = data; if (state != WL_POINTER_BUTTON_STATE_PRESSED) { return; } - double x = nagbar->pointer.x * nagbar->scale; - double y = nagbar->pointer.y * nagbar->scale; - for (int i = 0; i < nagbar->buttons->length; i++) { - struct sway_nagbar_button *nagbutton = nagbar->buttons->items[i]; + double x = swaynag->pointer.x * swaynag->scale; + double y = swaynag->pointer.y * swaynag->scale; + for (int i = 0; i < swaynag->buttons->length; i++) { + struct swaynag_button *nagbutton = swaynag->buttons->items[i]; if (x >= nagbutton->x && y >= nagbutton->y && x < nagbutton->x + nagbutton->width && y < nagbutton->y + nagbutton->height) { - nagbar_button_execute(nagbar, nagbutton); + swaynag_button_execute(swaynag, nagbutton); return; } } - if (nagbar->details.visible && - nagbar->details.total_lines != nagbar->details.visible_lines) { - struct sway_nagbar_button button_up = nagbar->details.button_up; + if (swaynag->details.visible && + swaynag->details.total_lines != swaynag->details.visible_lines) { + struct swaynag_button button_up = swaynag->details.button_up; if (x >= button_up.x && y >= button_up.y && x < button_up.x + button_up.width && y < button_up.y + button_up.height - && nagbar->details.offset > 0) { - nagbar->details.offset--; - render_frame(nagbar); + && swaynag->details.offset > 0) { + swaynag->details.offset--; + render_frame(swaynag); return; } - struct sway_nagbar_button button_down = nagbar->details.button_down; - int bot = nagbar->details.total_lines - nagbar->details.visible_lines; + struct swaynag_button button_down = swaynag->details.button_down; + int bot = swaynag->details.total_lines - swaynag->details.visible_lines; if (x >= button_down.x && y >= button_down.y && x < button_down.x + button_down.width && y < button_down.y + button_down.height - && nagbar->details.offset < bot) { - nagbar->details.offset++; - render_frame(nagbar); + && swaynag->details.offset < bot) { + swaynag->details.offset++; + render_frame(swaynag); return; } } @@ -156,25 +156,25 @@ static void wl_pointer_button(void *data, struct wl_pointer *wl_pointer, static void wl_pointer_axis(void *data, struct wl_pointer *wl_pointer, uint32_t time, uint32_t axis, wl_fixed_t value) { - struct sway_nagbar *nagbar = data; - if (!nagbar->details.visible - || nagbar->pointer.x < nagbar->details.x - || nagbar->pointer.y < nagbar->details.y - || nagbar->pointer.x >= nagbar->details.x + nagbar->details.width - || nagbar->pointer.y >= nagbar->details.y + nagbar->details.height - || nagbar->details.total_lines == nagbar->details.visible_lines) { + struct swaynag *swaynag = data; + if (!swaynag->details.visible + || swaynag->pointer.x < swaynag->details.x + || swaynag->pointer.y < swaynag->details.y + || swaynag->pointer.x >= swaynag->details.x + swaynag->details.width + || swaynag->pointer.y >= swaynag->details.y + swaynag->details.height + || swaynag->details.total_lines == swaynag->details.visible_lines) { return; } int direction = wl_fixed_to_int(value); - int bot = nagbar->details.total_lines - nagbar->details.visible_lines; - if (direction < 0 && nagbar->details.offset > 0) { - nagbar->details.offset--; - } else if (direction > 0 && nagbar->details.offset < bot) { - nagbar->details.offset++; + int bot = swaynag->details.total_lines - swaynag->details.visible_lines; + if (direction < 0 && swaynag->details.offset > 0) { + swaynag->details.offset--; + } else if (direction > 0 && swaynag->details.offset < bot) { + swaynag->details.offset++; } - render_frame(nagbar); + render_frame(swaynag); } static struct wl_pointer_listener pointer_listener = { @@ -191,11 +191,11 @@ static struct wl_pointer_listener pointer_listener = { static void seat_handle_capabilities(void *data, struct wl_seat *wl_seat, enum wl_seat_capability caps) { - struct sway_nagbar *nagbar = data; + struct swaynag *swaynag = data; if ((caps & WL_SEAT_CAPABILITY_POINTER)) { - nagbar->pointer.pointer = wl_seat_get_pointer(wl_seat); - wl_pointer_add_listener(nagbar->pointer.pointer, &pointer_listener, - nagbar); + swaynag->pointer.pointer = wl_seat_get_pointer(wl_seat); + wl_pointer_add_listener(swaynag->pointer.pointer, &pointer_listener, + swaynag); } } @@ -206,9 +206,9 @@ const struct wl_seat_listener seat_listener = { static void output_scale(void *data, struct wl_output *output, int32_t factor) { - struct sway_nagbar *nagbar = data; - nagbar->scale = factor; - render_frame(nagbar); + struct swaynag *swaynag = data; + swaynag->scale = factor; + render_frame(swaynag); } static struct wl_output_listener output_listener = { @@ -222,27 +222,27 @@ struct output_state { struct wl_output *wl_output; uint32_t wl_name; struct zxdg_output_v1 *xdg_output; - struct sway_nagbar *nagbar; + struct swaynag *swaynag; }; static void xdg_output_handle_name(void *data, struct zxdg_output_v1 *xdg_output, const char *name) { struct output_state *state = data; - char *outname = state->nagbar->output.name; + char *outname = state->swaynag->output.name; wlr_log(WLR_DEBUG, "Checking against output %s for %s", name, outname); - if (outname && !state->nagbar->output.wl_output) { + if (outname && !state->swaynag->output.wl_output) { wlr_log(WLR_DEBUG, "Using output %s", name); - state->nagbar->output.wl_output = state->wl_output; - state->nagbar->output.wl_name = state->wl_name; - wl_output_add_listener(state->nagbar->output.wl_output, - &output_listener, state->nagbar); - wl_display_roundtrip(state->nagbar->display); + state->swaynag->output.wl_output = state->wl_output; + state->swaynag->output.wl_name = state->wl_name; + wl_output_add_listener(state->swaynag->output.wl_output, + &output_listener, state->swaynag); + wl_display_roundtrip(state->swaynag->display); zxdg_output_v1_destroy(state->xdg_output); } else { zxdg_output_v1_destroy(state->xdg_output); wl_output_destroy(state->wl_output); } - state->nagbar->querying_outputs--; + state->swaynag->querying_outputs--; free(state); } @@ -256,35 +256,35 @@ static struct zxdg_output_v1_listener xdg_output_listener = { static void handle_global(void *data, struct wl_registry *registry, uint32_t name, const char *interface, uint32_t version) { - struct sway_nagbar *nagbar = data; + struct swaynag *swaynag = data; if (strcmp(interface, wl_compositor_interface.name) == 0) { - nagbar->compositor = wl_registry_bind(registry, name, + swaynag->compositor = wl_registry_bind(registry, name, &wl_compositor_interface, 3); } else if (strcmp(interface, wl_seat_interface.name) == 0) { - nagbar->seat = wl_registry_bind(registry, name, &wl_seat_interface, 1); - wl_seat_add_listener(nagbar->seat, &seat_listener, nagbar); + swaynag->seat = wl_registry_bind(registry, name, &wl_seat_interface, 1); + wl_seat_add_listener(swaynag->seat, &seat_listener, swaynag); } else if (strcmp(interface, wl_shm_interface.name) == 0) { - nagbar->shm = wl_registry_bind(registry, name, &wl_shm_interface, 1); + swaynag->shm = wl_registry_bind(registry, name, &wl_shm_interface, 1); } else if (strcmp(interface, wl_output_interface.name) == 0) { - if (!nagbar->output.wl_output && nagbar->xdg_output_manager) { - nagbar->querying_outputs++; + if (!swaynag->output.wl_output && swaynag->xdg_output_manager) { + swaynag->querying_outputs++; struct output_state *state = calloc(1, sizeof(struct output_state)); - state->nagbar = nagbar; + state->swaynag = swaynag; state->wl_output = wl_registry_bind(registry, name, &wl_output_interface, 3); state->wl_name = name; state->xdg_output = zxdg_output_manager_v1_get_xdg_output( - nagbar->xdg_output_manager, state->wl_output); + swaynag->xdg_output_manager, state->wl_output); zxdg_output_v1_add_listener(state->xdg_output, &xdg_output_listener, state); } } else if (strcmp(interface, zwlr_layer_shell_v1_interface.name) == 0) { - nagbar->layer_shell = wl_registry_bind( + swaynag->layer_shell = wl_registry_bind( registry, name, &zwlr_layer_shell_v1_interface, 1); } else if (strcmp(interface, zxdg_output_manager_v1_interface.name) == 0 && version >= ZXDG_OUTPUT_V1_NAME_SINCE_VERSION) { - nagbar->xdg_output_manager = wl_registry_bind(registry, name, + swaynag->xdg_output_manager = wl_registry_bind(registry, name, &zxdg_output_manager_v1_interface, ZXDG_OUTPUT_V1_NAME_SINCE_VERSION); } @@ -292,9 +292,9 @@ static void handle_global(void *data, struct wl_registry *registry, static void handle_global_remove(void *data, struct wl_registry *registry, uint32_t name) { - struct sway_nagbar *nagbar = data; - if (nagbar->output.wl_name == name) { - nagbar->run_display = false; + struct swaynag *swaynag = data; + if (swaynag->output.wl_name == name) { + swaynag->run_display = false; } } @@ -303,110 +303,111 @@ static const struct wl_registry_listener registry_listener = { .global_remove = handle_global_remove, }; -void nagbar_setup(struct sway_nagbar *nagbar) { - nagbar->display = wl_display_connect(NULL); - assert(nagbar->display); +void swaynag_setup(struct swaynag *swaynag) { + swaynag->display = wl_display_connect(NULL); + assert(swaynag->display); - nagbar->scale = 1; + swaynag->scale = 1; - struct wl_registry *registry = wl_display_get_registry(nagbar->display); - wl_registry_add_listener(registry, ®istry_listener, nagbar); - wl_display_roundtrip(nagbar->display); - assert(nagbar->compositor && nagbar->layer_shell && nagbar->shm); + struct wl_registry *registry = wl_display_get_registry(swaynag->display); + wl_registry_add_listener(registry, ®istry_listener, swaynag); + wl_display_roundtrip(swaynag->display); + assert(swaynag->compositor && swaynag->layer_shell && swaynag->shm); - while (nagbar->querying_outputs > 0) { - wl_display_roundtrip(nagbar->display); + while (swaynag->querying_outputs > 0) { + wl_display_roundtrip(swaynag->display); } - if (!nagbar->output.wl_output && nagbar->output.name) { - wlr_log(WLR_ERROR, "Output '%s' not found", nagbar->output.name); - nagbar_destroy(nagbar); + if (!swaynag->output.wl_output && swaynag->output.name) { + wlr_log(WLR_ERROR, "Output '%s' not found", swaynag->output.name); + swaynag_destroy(swaynag); exit(EXIT_FAILURE); } - struct sway_nagbar_pointer *pointer = &nagbar->pointer; - int scale = nagbar->scale < 1 ? 1 : nagbar->scale; + struct swaynag_pointer *pointer = &swaynag->pointer; + int scale = swaynag->scale < 1 ? 1 : swaynag->scale; pointer->cursor_theme = wl_cursor_theme_load( - NULL, 24 * scale, nagbar->shm); + NULL, 24 * scale, swaynag->shm); assert(pointer->cursor_theme); struct wl_cursor *cursor = wl_cursor_theme_get_cursor(pointer->cursor_theme, "left_ptr"); assert(cursor); pointer->cursor_image = cursor->images[0]; - pointer->cursor_surface = wl_compositor_create_surface(nagbar->compositor); + pointer->cursor_surface = wl_compositor_create_surface(swaynag->compositor); assert(pointer->cursor_surface); - nagbar->surface = wl_compositor_create_surface(nagbar->compositor); - assert(nagbar->surface); - nagbar->layer_surface = zwlr_layer_shell_v1_get_layer_surface( - nagbar->layer_shell, nagbar->surface, nagbar->output.wl_output, + swaynag->surface = wl_compositor_create_surface(swaynag->compositor); + assert(swaynag->surface); + swaynag->layer_surface = zwlr_layer_shell_v1_get_layer_surface( + swaynag->layer_shell, swaynag->surface, swaynag->output.wl_output, ZWLR_LAYER_SHELL_V1_LAYER_TOP, "swaynag"); - assert(nagbar->layer_surface); - zwlr_layer_surface_v1_add_listener(nagbar->layer_surface, - &layer_surface_listener, nagbar); - zwlr_layer_surface_v1_set_anchor(nagbar->layer_surface, nagbar->anchors); + assert(swaynag->layer_surface); + zwlr_layer_surface_v1_add_listener(swaynag->layer_surface, + &layer_surface_listener, swaynag); + zwlr_layer_surface_v1_set_anchor(swaynag->layer_surface, swaynag->anchors); wl_registry_destroy(registry); } -void nagbar_run(struct sway_nagbar *nagbar) { - nagbar->run_display = true; - render_frame(nagbar); - while (nagbar->run_display && wl_display_dispatch(nagbar->display) != -1) { +void swaynag_run(struct swaynag *swaynag) { + swaynag->run_display = true; + render_frame(swaynag); + while (swaynag->run_display + && wl_display_dispatch(swaynag->display) != -1) { // This is intentionally left blank } } -void nagbar_destroy(struct sway_nagbar *nagbar) { - nagbar->run_display = false; +void swaynag_destroy(struct swaynag *swaynag) { + swaynag->run_display = false; - free(nagbar->message); - free(nagbar->font); - while (nagbar->buttons->length) { - struct sway_nagbar_button *button = nagbar->buttons->items[0]; - list_del(nagbar->buttons, 0); + free(swaynag->message); + free(swaynag->font); + while (swaynag->buttons->length) { + struct swaynag_button *button = swaynag->buttons->items[0]; + list_del(swaynag->buttons, 0); free(button->text); free(button->action); free(button); } - list_free(nagbar->buttons); - free(nagbar->details.message); - free(nagbar->details.button_up.text); - free(nagbar->details.button_down.text); + list_free(swaynag->buttons); + free(swaynag->details.message); + free(swaynag->details.button_up.text); + free(swaynag->details.button_down.text); - if (nagbar->type) { - nagbar_type_free(nagbar->type); + if (swaynag->type) { + swaynag_type_free(swaynag->type); } - if (nagbar->layer_surface) { - zwlr_layer_surface_v1_destroy(nagbar->layer_surface); + if (swaynag->layer_surface) { + zwlr_layer_surface_v1_destroy(swaynag->layer_surface); } - if (nagbar->surface) { - wl_surface_destroy(nagbar->surface); + if (swaynag->surface) { + wl_surface_destroy(swaynag->surface); } - if (nagbar->output.wl_output) { - wl_output_destroy(nagbar->output.wl_output); + if (swaynag->output.wl_output) { + wl_output_destroy(swaynag->output.wl_output); } - if (&nagbar->buffers[0]) { - destroy_buffer(&nagbar->buffers[0]); + if (&swaynag->buffers[0]) { + destroy_buffer(&swaynag->buffers[0]); } - if (&nagbar->buffers[1]) { - destroy_buffer(&nagbar->buffers[1]); + if (&swaynag->buffers[1]) { + destroy_buffer(&swaynag->buffers[1]); } - if (nagbar->compositor) { - wl_compositor_destroy(nagbar->compositor); + if (swaynag->compositor) { + wl_compositor_destroy(swaynag->compositor); } - if (nagbar->shm) { - wl_shm_destroy(nagbar->shm); + if (swaynag->shm) { + wl_shm_destroy(swaynag->shm); } - if (nagbar->display) { - wl_display_disconnect(nagbar->display); + if (swaynag->display) { + wl_display_disconnect(swaynag->display); } } diff --git a/swaynag/types.c b/swaynag/types.c index dbc841f7..c92d0e89 100644 --- a/swaynag/types.c +++ b/swaynag/types.c @@ -10,9 +10,9 @@ #include "swaynag/types.h" #include "util.h" -void nagbar_types_add_default(list_t *types) { - struct sway_nagbar_type *type_error; - type_error = calloc(1, sizeof(struct sway_nagbar_type)); +void swaynag_types_add_default(list_t *types) { + struct swaynag_type *type_error; + type_error = calloc(1, sizeof(struct swaynag_type)); type_error->name = strdup("error"); type_error->button_background = 0x680A0AFF; type_error->background = 0x900000FF; @@ -21,8 +21,8 @@ void nagbar_types_add_default(list_t *types) { type_error->border_bottom = 0x470909FF; list_add(types, type_error); - struct sway_nagbar_type *type_warning; - type_warning = calloc(1, sizeof(struct sway_nagbar_type)); + struct swaynag_type *type_warning; + type_warning = calloc(1, sizeof(struct swaynag_type)); type_warning->name = strdup("warning"); type_warning->button_background = 0xFFC100FF; type_warning->background = 0xFFA800FF; @@ -32,9 +32,9 @@ void nagbar_types_add_default(list_t *types) { list_add(types, type_warning); } -struct sway_nagbar_type *nagbar_type_get(list_t *types, char *name) { +struct swaynag_type *swaynag_type_get(list_t *types, char *name) { for (int i = 0; i < types->length; i++) { - struct sway_nagbar_type *type = types->items[i]; + struct swaynag_type *type = types->items[i]; if (strcasecmp(type->name, name) == 0) { return type; } @@ -42,9 +42,9 @@ struct sway_nagbar_type *nagbar_type_get(list_t *types, char *name) { return NULL; } -struct sway_nagbar_type *nagbar_type_clone(struct sway_nagbar_type *type) { - struct sway_nagbar_type *clone; - clone = calloc(1, sizeof(struct sway_nagbar_type)); +struct swaynag_type *swaynag_type_clone(struct swaynag_type *type) { + struct swaynag_type *clone; + clone = calloc(1, sizeof(struct swaynag_type)); clone->name = strdup(type->name); clone->button_background = type->button_background; clone->background = type->background; @@ -54,21 +54,21 @@ struct sway_nagbar_type *nagbar_type_clone(struct sway_nagbar_type *type) { return clone; } -void nagbar_type_free(struct sway_nagbar_type *type) { +void swaynag_type_free(struct swaynag_type *type) { free(type->name); free(type); } -void nagbar_types_free(list_t *types) { +void swaynag_types_free(list_t *types) { while (types->length) { - struct sway_nagbar_type *type = types->items[0]; - nagbar_type_free(type); + struct swaynag_type *type = types->items[0]; + swaynag_type_free(type); list_del(types, 0); } list_free(types); } -int nagbar_parse_type(int argc, char **argv, struct sway_nagbar_type *type) { +int swaynag_parse_type(int argc, char **argv, struct swaynag_type *type) { enum color_option { COLOR_BACKGROUND, COLOR_BORDER, From 894d57f19259eaaa4d7777f067ce4cf877a480a5 Mon Sep 17 00:00:00 2001 From: Brian Ashworth Date: Sat, 28 Jul 2018 23:25:40 -0400 Subject: [PATCH 131/148] swaynag: fix output selection --- swaynag/swaynag.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/swaynag/swaynag.c b/swaynag/swaynag.c index 367e0854..e5d2e216 100644 --- a/swaynag/swaynag.c +++ b/swaynag/swaynag.c @@ -230,7 +230,8 @@ static void xdg_output_handle_name(void *data, struct output_state *state = data; char *outname = state->swaynag->output.name; wlr_log(WLR_DEBUG, "Checking against output %s for %s", name, outname); - if (outname && !state->swaynag->output.wl_output) { + if (!state->swaynag->output.wl_output && outname && name + && strcmp(outname, name) == 0) { wlr_log(WLR_DEBUG, "Using output %s", name); state->swaynag->output.wl_output = state->wl_output; state->swaynag->output.wl_name = state->wl_name; From ca40298af4205abd36b7d1766ffe5cb1e72ed711 Mon Sep 17 00:00:00 2001 From: Brian Ashworth Date: Sun, 29 Jul 2018 00:28:01 -0400 Subject: [PATCH 132/148] swaynag: add math to meson.build --- swaynag/meson.build | 1 + 1 file changed, 1 insertion(+) diff --git a/swaynag/meson.build b/swaynag/meson.build index f67c10ff..2ba3ed95 100644 --- a/swaynag/meson.build +++ b/swaynag/meson.build @@ -11,6 +11,7 @@ executable( cairo, client_protos, gdk_pixbuf, + math, pango, pangocairo, wayland_client, From e01acb6097b583fcf2f6d0e0afe1bd878dd9b683 Mon Sep 17 00:00:00 2001 From: Brian Ashworth Date: Sun, 29 Jul 2018 22:42:03 -0400 Subject: [PATCH 133/148] swaynag: allow more config options --- include/swaynag/config.h | 2 +- include/swaynag/swaynag.h | 11 --- include/swaynag/types.h | 18 ++++- swaynag/config.c | 149 ++++++++++++++++++++++++++++++++----- swaynag/main.c | 33 ++++++--- swaynag/render.c | 51 ++++++------- swaynag/swaynag.1.scd | 46 +++++++++++- swaynag/swaynag.5.scd | 52 +++++++++++-- swaynag/swaynag.c | 4 +- swaynag/types.c | 150 +++++++++++++++++++++++--------------- 10 files changed, 379 insertions(+), 137 deletions(-) diff --git a/include/swaynag/config.h b/include/swaynag/config.h index 3fd5b3ce..0d8889de 100644 --- a/include/swaynag/config.h +++ b/include/swaynag/config.h @@ -4,7 +4,7 @@ #include "list.h" int swaynag_parse_options(int argc, char **argv, struct swaynag *swaynag, - list_t *types, char **config, bool *debug); + list_t *types, struct swaynag_type *type, char **config, bool *debug); char *swaynag_get_config_path(void); diff --git a/include/swaynag/swaynag.h b/include/swaynag/swaynag.h index 6a56f14f..dd6fe0cd 100644 --- a/include/swaynag/swaynag.h +++ b/include/swaynag/swaynag.h @@ -7,15 +7,6 @@ #include "swaynag/types.h" #include "xdg-output-unstable-v1-client-protocol.h" -#define SWAYNAG_BAR_BORDER_THICKNESS 2 -#define SWAYNAG_MESSAGE_PADDING 8 -#define SWAYNAG_DETAILS_BORDER_THICKNESS 3 -#define SWAYNAG_BUTTON_BORDER_THICKNESS 3 -#define SWAYNAG_BUTTON_GAP 20 -#define SWAYNAG_BUTTON_GAP_CLOSE 15 -#define SWAYNAG_BUTTON_MARGIN_RIGHT 2 -#define SWAYNAG_BUTTON_PADDING 3 - #define SWAYNAG_MAX_HEIGHT 500 enum swaynag_action_type { @@ -88,9 +79,7 @@ struct swaynag { struct pool_buffer *current_buffer; struct swaynag_type *type; - uint32_t anchors; char *message; - char *font; list_t *buttons; struct swaynag_details details; }; diff --git a/include/swaynag/types.h b/include/swaynag/types.h index af83bd83..2183ce22 100644 --- a/include/swaynag/types.h +++ b/include/swaynag/types.h @@ -3,11 +3,25 @@ struct swaynag_type { char *name; + + char *font; + char *output; + uint32_t anchors; + uint32_t button_background; uint32_t background; uint32_t text; uint32_t border; uint32_t border_bottom; + + uint32_t bar_border_thickness; + uint32_t message_padding; + uint32_t details_border_thickness; + uint32_t button_border_thickness; + uint32_t button_gap; + uint32_t button_gap_close; + uint32_t button_margin_right; + uint32_t button_padding; }; void swaynag_types_add_default(list_t *types); @@ -16,10 +30,10 @@ struct swaynag_type *swaynag_type_get(list_t *types, char *name); struct swaynag_type *swaynag_type_clone(struct swaynag_type *type); +void swaynag_type_merge(struct swaynag_type *dest, struct swaynag_type *src); + void swaynag_type_free(struct swaynag_type *type); void swaynag_types_free(list_t *types); -int swaynag_parse_type(int argc, char **argv, struct swaynag_type *type); - #endif diff --git a/swaynag/config.c b/swaynag/config.c index 289fc82a..80c5ad88 100644 --- a/swaynag/config.c +++ b/swaynag/config.c @@ -8,6 +8,7 @@ #include "readline.h" #include "swaynag/swaynag.h" #include "swaynag/types.h" +#include "util.h" #include "wlr-layer-shell-unstable-v1-client-protocol.h" static char *read_from_stdin() { @@ -37,7 +38,23 @@ static char *read_from_stdin() { } int swaynag_parse_options(int argc, char **argv, struct swaynag *swaynag, - list_t *types, char **config, bool *debug) { + list_t *types, struct swaynag_type *type, char **config, bool *debug) { + enum type_options { + TO_COLOR_BACKGROUND = 256, + TO_COLOR_BORDER, + TO_COLOR_BORDER_BOTTOM, + TO_COLOR_BUTTON, + TO_COLOR_TEXT, + TO_THICK_BAR_BORDER, + TO_PADDING_MESSAGE, + TO_THICK_DET_BORDER, + TO_THICK_BTN_BORDER, + TO_GAP_BTN, + TO_GAP_BTN_DISMISS, + TO_MARGIN_BTN_RIGHT, + TO_PADDING_BTN, + }; + static struct option opts[] = { {"button", required_argument, NULL, 'b'}, {"config", required_argument, NULL, 'c'}, @@ -52,6 +69,21 @@ int swaynag_parse_options(int argc, char **argv, struct swaynag *swaynag, {"dismiss-button", required_argument, NULL, 's'}, {"type", required_argument, NULL, 't'}, {"version", no_argument, NULL, 'v'}, + + {"background", required_argument, NULL, TO_COLOR_BACKGROUND}, + {"border", required_argument, NULL, TO_COLOR_BORDER}, + {"border-bottom", required_argument, NULL, TO_COLOR_BORDER_BOTTOM}, + {"button-background", required_argument, NULL, TO_COLOR_BUTTON}, + {"text", required_argument, NULL, TO_COLOR_TEXT}, + {"border-bottom-size", required_argument, NULL, TO_THICK_BAR_BORDER}, + {"message-padding", required_argument, NULL, TO_PADDING_MESSAGE}, + {"details-border-size", required_argument, NULL, TO_THICK_DET_BORDER}, + {"button-border-size", required_argument, NULL, TO_THICK_BTN_BORDER}, + {"button-gap", required_argument, NULL, TO_GAP_BTN}, + {"button-dismiss-gap", required_argument, NULL, TO_GAP_BTN_DISMISS}, + {"button-margin-right", required_argument, NULL, TO_MARGIN_BTN_RIGHT}, + {"button-padding", required_argument, NULL, TO_PADDING_BTN}, + {0, 0, 0, 0} }; @@ -71,7 +103,22 @@ int swaynag_parse_options(int argc, char **argv, struct swaynag *swaynag, " -o, --output Set the output to use.\n" " -s, --dismiss-button Set the dismiss button text.\n" " -t, --type Set the message type.\n" - " -v, --version Show the version number and quit.\n"; + " -v, --version Show the version number and quit.\n" + "\n" + "The following appearance options can also be given:\n" + " --background RRGGBB[AA] Background color.\n" + " --border RRGGBB[AA] Border color.\n" + " --border-bottom RRGGBB[AA] Bottom border color.\n" + " --button-background RRGGBB[AA] Button background color.\n" + " --text RRGGBB[AA] Text color.\n" + " --border-bottom-size size Thickness of the bar border.\n" + " --message-padding padding Padding for the message.\n" + " --details-border-size size Thickness for the details border.\n" + " --button-border-size size Thickness for the button border.\n" + " --button-gap gap Size of the gap between buttons\n" + " --button-dismiss-gap gap Size of the gap for dismiss button.\n" + " --button-margin-right margin Margin from dismiss button to edge.\n" + " --button-padding padding Padding for the button text.\n"; optind = 1; while (1) { @@ -106,13 +153,13 @@ int swaynag_parse_options(int argc, char **argv, struct swaynag *swaynag, } break; case 'e': // Edge - if (swaynag) { + if (type) { if (strcmp(optarg, "top") == 0) { - swaynag->anchors = ZWLR_LAYER_SURFACE_V1_ANCHOR_TOP + type->anchors = ZWLR_LAYER_SURFACE_V1_ANCHOR_TOP | ZWLR_LAYER_SURFACE_V1_ANCHOR_LEFT | ZWLR_LAYER_SURFACE_V1_ANCHOR_RIGHT; } else if (strcmp(optarg, "bottom") == 0) { - swaynag->anchors = ZWLR_LAYER_SURFACE_V1_ANCHOR_BOTTOM + type->anchors = ZWLR_LAYER_SURFACE_V1_ANCHOR_BOTTOM | ZWLR_LAYER_SURFACE_V1_ANCHOR_LEFT | ZWLR_LAYER_SURFACE_V1_ANCHOR_RIGHT; } else { @@ -122,9 +169,9 @@ int swaynag_parse_options(int argc, char **argv, struct swaynag *swaynag, } break; case 'f': // Font - if (swaynag) { - free(swaynag->font); - swaynag->font = strdup(optarg); + if (type) { + free(type->font); + type->font = strdup(optarg); } break; case 'l': // Detailed Message @@ -148,9 +195,9 @@ int swaynag_parse_options(int argc, char **argv, struct swaynag *swaynag, } break; case 'o': // Output - if (swaynag) { - free(swaynag->output.name); - swaynag->output.name = strdup(optarg); + if (type) { + free(type->output); + type->output = strdup(optarg); } break; case 's': // Dismiss Button Text @@ -173,6 +220,71 @@ int swaynag_parse_options(int argc, char **argv, struct swaynag *swaynag, case 'v': // Version fprintf(stdout, "swaynag version " SWAY_VERSION "\n"); return -1; + case TO_COLOR_BACKGROUND: // Background color + if (type) { + type->background = parse_color(optarg); + } + break; + case TO_COLOR_BORDER: // Border color + if (type) { + type->border = parse_color(optarg); + } + break; + case TO_COLOR_BORDER_BOTTOM: // Bottom border color + if (type) { + type->border_bottom = parse_color(optarg); + } + break; + case TO_COLOR_BUTTON: // Button background color + if (type) { + type->button_background = parse_color(optarg); + } + break; + case TO_COLOR_TEXT: // Text color + if (type) { + type->text = parse_color(optarg); + } + break; + case TO_THICK_BAR_BORDER: // Bottom border thickness + if (type) { + type->bar_border_thickness = strtol(optarg, NULL, 0); + } + break; + case TO_PADDING_MESSAGE: // Message padding + if (type) { + type->message_padding = strtol(optarg, NULL, 0); + } + break; + case TO_THICK_DET_BORDER: // Details border thickness + if (type) { + type->details_border_thickness = strtol(optarg, NULL, 0); + } + break; + case TO_THICK_BTN_BORDER: // Button border thickness + if (type) { + type->button_border_thickness = strtol(optarg, NULL, 0); + } + break; + case TO_GAP_BTN: // Gap between buttons + if (type) { + type->button_gap = strtol(optarg, NULL, 0); + } + break; + case TO_GAP_BTN_DISMISS: // Gap between dismiss button + if (type) { + type->button_gap_close = strtol(optarg, NULL, 0); + } + break; + case TO_MARGIN_BTN_RIGHT: // Margin on the right side of button area + if (type) { + type->button_margin_right = strtol(optarg, NULL, 0); + } + break; + case TO_PADDING_BTN: // Padding for the button text + if (type) { + type->button_padding = strtol(optarg, NULL, 0); + } + break; default: // Help or unknown flag fprintf(c == 'h' ? stdout : stderr, "%s", usage); return -1; @@ -229,7 +341,12 @@ int swaynag_load_config(char *path, struct swaynag *swaynag, list_t *types) { fprintf(stderr, "Failed to read config. Running without it.\n"); return 0; } - struct swaynag_type *type = NULL; + + struct swaynag_type *type; + type = calloc(1, sizeof(struct swaynag_type)); + type->name = strdup(""); + list_add(types, type); + char *line; int line_number = 0; while (!feof(config)) { @@ -271,12 +388,8 @@ int swaynag_load_config(char *path, struct swaynag *swaynag, list_t *types) { sprintf(flag, "--%s", line); char *argv[] = {"swaynag", flag}; int result; - if (type) { - result = swaynag_parse_type(2, argv, type); - } else { - result = swaynag_parse_options(2, argv, swaynag, types, - NULL, NULL); - } + result = swaynag_parse_options(2, argv, swaynag, types, type, + NULL, NULL); if (result != 0) { free(line); fclose(config); diff --git a/swaynag/main.c b/swaynag/main.c index 0493c1f0..20d03c31 100644 --- a/swaynag/main.c +++ b/swaynag/main.c @@ -5,7 +5,6 @@ #include "swaynag/config.h" #include "swaynag/swaynag.h" #include "swaynag/types.h" -#include "wlr-layer-shell-unstable-v1-client-protocol.h" static struct swaynag swaynag; @@ -26,10 +25,6 @@ int main(int argc, char **argv) { swaynag_types_add_default(types); memset(&swaynag, 0, sizeof(swaynag)); - swaynag.anchors = ZWLR_LAYER_SURFACE_V1_ANCHOR_TOP - | ZWLR_LAYER_SURFACE_V1_ANCHOR_LEFT - | ZWLR_LAYER_SURFACE_V1_ANCHOR_RIGHT; - swaynag.font = strdup("pango:monospace 10"); swaynag.buttons = create_list(); struct swaynag_button *button_close = @@ -44,7 +39,7 @@ int main(int argc, char **argv) { char *config_path = NULL; bool debug = false; - int launch_status = swaynag_parse_options(argc, argv, NULL, NULL, + int launch_status = swaynag_parse_options(argc, argv, NULL, NULL, NULL, &config_path, &debug); if (launch_status != 0) { exit_code = launch_status; @@ -66,8 +61,13 @@ int main(int argc, char **argv) { } if (argc > 1) { + struct swaynag_type *type_args; + type_args = calloc(1, sizeof(struct swaynag_type)); + type_args->name = strdup(""); + list_add(types, type_args); + int result = swaynag_parse_options(argc, argv, &swaynag, types, - NULL, NULL); + type_args, NULL, NULL); if (result != 0) { exit_code = result; goto cleanup; @@ -84,7 +84,20 @@ int main(int argc, char **argv) { swaynag.type = swaynag_type_get(types, "error"); } - swaynag.type = swaynag_type_clone(swaynag.type); + // Construct a new type using the config defaults as base, then merging + // config type defaults on top, then merging arguments on top of that, and + // finally merging defaults on top. + struct swaynag_type *type = calloc(1, sizeof(struct swaynag_type)); + type->name = strdup(swaynag.type->name); + swaynag_type_merge(type, swaynag_type_get(types, "")); + swaynag_type_merge(type, swaynag.type); + swaynag_type_merge(type, swaynag_type_get(types, "")); + swaynag_type_merge(type, swaynag_type_get(types, "")); + swaynag.type = type; + if (swaynag.type->output) { + swaynag.output.name = strdup(swaynag.type->output); + } + swaynag_types_free(types); if (swaynag.details.message) { @@ -94,10 +107,10 @@ int main(int argc, char **argv) { } wlr_log(WLR_DEBUG, "Output: %s", swaynag.output.name); - wlr_log(WLR_DEBUG, "Anchors: %d", swaynag.anchors); + wlr_log(WLR_DEBUG, "Anchors: %d", swaynag.type->anchors); wlr_log(WLR_DEBUG, "Type: %s", swaynag.type->name); wlr_log(WLR_DEBUG, "Message: %s", swaynag.message); - wlr_log(WLR_DEBUG, "Font: %s", swaynag.font); + wlr_log(WLR_DEBUG, "Font: %s", swaynag.type->font); wlr_log(WLR_DEBUG, "Buttons"); for (int i = 0; i < swaynag.buttons->length; i++) { struct swaynag_button *button = swaynag.buttons->items[i]; diff --git a/swaynag/render.c b/swaynag/render.c index 67e26eaf..bc3e520e 100644 --- a/swaynag/render.c +++ b/swaynag/render.c @@ -9,13 +9,13 @@ static uint32_t render_message(cairo_t *cairo, struct swaynag *swaynag) { uint32_t height = swaynag->height * swaynag->scale; - height -= SWAYNAG_BAR_BORDER_THICKNESS * swaynag->scale; + height -= swaynag->type->bar_border_thickness * swaynag->scale; int text_width, text_height; - get_text_size(cairo, swaynag->font, &text_width, &text_height, + get_text_size(cairo, swaynag->type->font, &text_width, &text_height, swaynag->scale, true, "%s", swaynag->message); - int padding = SWAYNAG_MESSAGE_PADDING * swaynag->scale; + int padding = swaynag->type->message_padding * swaynag->scale; uint32_t ideal_height = text_height + padding * 2; uint32_t ideal_surface_height = ideal_height / swaynag->scale; @@ -25,7 +25,7 @@ static uint32_t render_message(cairo_t *cairo, struct swaynag *swaynag) { cairo_set_source_u32(cairo, swaynag->type->text); cairo_move_to(cairo, padding, (int)(ideal_height - text_height) / 2); - pango_printf(cairo, swaynag->font, swaynag->scale, false, "%s", + pango_printf(cairo, swaynag->type->font, swaynag->scale, false, "%s", swaynag->message); return ideal_height; @@ -34,11 +34,11 @@ static uint32_t render_message(cairo_t *cairo, struct swaynag *swaynag) { static void render_details_scroll_button(cairo_t *cairo, struct swaynag *swaynag, struct swaynag_button *button) { int text_width, text_height; - get_text_size(cairo, swaynag->font, &text_width, &text_height, + get_text_size(cairo, swaynag->type->font, &text_width, &text_height, swaynag->scale, true, "%s", button->text); - int border = SWAYNAG_BUTTON_BORDER_THICKNESS * swaynag->scale; - int padding = SWAYNAG_BUTTON_PADDING * swaynag->scale; + int border = swaynag->type->button_border_thickness * swaynag->scale; + int padding = swaynag->type->button_padding * swaynag->scale; cairo_set_source_u32(cairo, swaynag->type->border); cairo_rectangle(cairo, button->x, button->y, @@ -53,21 +53,21 @@ static void render_details_scroll_button(cairo_t *cairo, cairo_set_source_u32(cairo, swaynag->type->text); cairo_move_to(cairo, button->x + border + padding, button->y + border + (button->height - text_height) / 2); - pango_printf(cairo, swaynag->font, swaynag->scale, true, + pango_printf(cairo, swaynag->type->font, swaynag->scale, true, "%s", button->text); } static int get_detailed_scroll_button_width(cairo_t *cairo, struct swaynag *swaynag) { int up_width, down_width, temp_height; - get_text_size(cairo, swaynag->font, &up_width, &temp_height, + get_text_size(cairo, swaynag->type->font, &up_width, &temp_height, swaynag->scale, true, "%s", swaynag->details.button_up.text); - get_text_size(cairo, swaynag->font, &down_width, &temp_height, + get_text_size(cairo, swaynag->type->font, &down_width, &temp_height, swaynag->scale, true, "%s", swaynag->details.button_down.text); int text_width = up_width > down_width ? up_width : down_width; - int border = SWAYNAG_BUTTON_BORDER_THICKNESS * swaynag->scale; - int padding = SWAYNAG_BUTTON_PADDING * swaynag->scale; + int border = swaynag->type->button_border_thickness * swaynag->scale; + int padding = swaynag->type->button_padding * swaynag->scale; return text_width + border * 2 + padding * 2; } @@ -76,17 +76,17 @@ static uint32_t render_detailed(cairo_t *cairo, struct swaynag *swaynag, uint32_t y) { uint32_t width = swaynag->width * swaynag->scale; uint32_t height = swaynag->height * swaynag->scale; - height -= SWAYNAG_BAR_BORDER_THICKNESS * swaynag->scale; + height -= swaynag->type->bar_border_thickness * swaynag->scale; - int border = SWAYNAG_DETAILS_BORDER_THICKNESS * swaynag->scale; - int padding = SWAYNAG_MESSAGE_PADDING * swaynag->scale; + int border = swaynag->type->details_border_thickness * swaynag->scale; + int padding = swaynag->type->message_padding * swaynag->scale; int decor = padding + border; swaynag->details.x = decor; swaynag->details.y = y + decor; swaynag->details.width = width - decor * 2; - PangoLayout *layout = get_pango_layout(cairo, swaynag->font, + PangoLayout *layout = get_pango_layout(cairo, swaynag->type->font, swaynag->details.message, swaynag->scale, false); pango_layout_set_width(layout, (swaynag->details.width - padding * 2) * PANGO_SCALE); @@ -173,15 +173,15 @@ static uint32_t render_detailed(cairo_t *cairo, struct swaynag *swaynag, static uint32_t render_button(cairo_t *cairo, struct swaynag *swaynag, int button_index, int *x) { uint32_t height = swaynag->height * swaynag->scale; - height -= SWAYNAG_BAR_BORDER_THICKNESS * swaynag->scale; + height -= swaynag->type->bar_border_thickness * swaynag->scale; struct swaynag_button *button = swaynag->buttons->items[button_index]; int text_width, text_height; - get_text_size(cairo, swaynag->font, &text_width, &text_height, + get_text_size(cairo, swaynag->type->font, &text_width, &text_height, swaynag->scale, true, "%s", button->text); - int border = SWAYNAG_BUTTON_BORDER_THICKNESS * swaynag->scale; - int padding = SWAYNAG_BUTTON_PADDING * swaynag->scale; + int border = swaynag->type->button_border_thickness * swaynag->scale; + int padding = swaynag->type->button_padding * swaynag->scale; uint32_t ideal_height = text_height + padding * 2 + border * 2; uint32_t ideal_surface_height = ideal_height / swaynag->scale; @@ -206,7 +206,7 @@ static uint32_t render_button(cairo_t *cairo, struct swaynag *swaynag, cairo_set_source_u32(cairo, swaynag->type->text); cairo_move_to(cairo, button->x + padding, button->y + padding); - pango_printf(cairo, swaynag->font, swaynag->scale, true, + pango_printf(cairo, swaynag->type->font, swaynag->scale, true, "%s", button->text); *x = button->x - border; @@ -224,13 +224,14 @@ static uint32_t render_to_cairo(cairo_t *cairo, struct swaynag *swaynag) { uint32_t h = render_message(cairo, swaynag); max_height = h > max_height ? h : max_height; - int x = (swaynag->width - SWAYNAG_BUTTON_MARGIN_RIGHT) * swaynag->scale; + int x = swaynag->width - swaynag->type->button_margin_right; + x *= swaynag->scale; for (int i = 0; i < swaynag->buttons->length; i++) { h = render_button(cairo, swaynag, i, &x); max_height = h > max_height ? h : max_height; - x -= SWAYNAG_BUTTON_GAP * swaynag->scale; + x -= swaynag->type->button_gap * swaynag->scale; if (i == 0) { - x -= SWAYNAG_BUTTON_GAP_CLOSE * swaynag->scale; + x -= swaynag->type->button_gap_close * swaynag->scale; } } @@ -239,7 +240,7 @@ static uint32_t render_to_cairo(cairo_t *cairo, struct swaynag *swaynag) { max_height = h > max_height ? h : max_height; } - int border = SWAYNAG_BAR_BORDER_THICKNESS * swaynag->scale; + int border = swaynag->type->bar_border_thickness * swaynag->scale; if (max_height > swaynag->height) { max_height += border; } diff --git a/swaynag/swaynag.1.scd b/swaynag/swaynag.1.scd index 7d250a45..12787c3c 100644 --- a/swaynag/swaynag.1.scd +++ b/swaynag/swaynag.1.scd @@ -45,14 +45,12 @@ _swaynag_ [options...] Set the message text. *-o, --output* - Set the output to use. This should be the name of a _xdg\_output_. If - _xdg\_output\_manager_ is not supported, then the first detected output - will be used + Set the output to use. This should be the name of a _xdg\_output_. *-s, --dismiss-button* Sets the text for the dismiss nagbar button. The default is _X_. -*-t, --type* +*-t, --type* Set the message type. Two types are created by default _error_ and _warning_. Custom types can be defined in the config file. See _--config_ and swaynag(5) for details. Both of the default types can be @@ -61,5 +59,45 @@ _swaynag_ [options...] *-v, --version* Show the version number and quit. +# APPEARANCE OPTIONS +*--background* + Set the color of the background. + +*--border* + Set the color of the border. + +*--border-bottom* + Set the color of the bottom border. + +*--button-background* + Set the color for the background for buttons. + +*--text* + Set the text color. + +*--border-bottom-size* + Set the thickness of the bottom border. + +*--message-padding* + Set the padding for the message. + +*--details-border-size* + Set the thickness for the details border. + +*--button-border-size* + Set the thickness for the button border. + +*--button-gap* + Set the size of the gap between buttons. + +*--button-dismiss-gap* + Set the size of the gap between the dismiss button and another button. + +*--button-margin-right* + Set the margin from the right of the dismiss button to edge. + +*--button-padding* + Set the padding for the button text. + # SEE swaynag(5) diff --git a/swaynag/swaynag.5.scd b/swaynag/swaynag.5.scd index a4e05e3a..e2348d8b 100644 --- a/swaynag/swaynag.5.scd +++ b/swaynag/swaynag.5.scd @@ -20,37 +20,77 @@ following format: ``` [name-of-type] -color=RRGGBB[AA] +option=value ``` All colors may be given in the form _RRGGBB_ or _RRGGBBAA_. The following colors can be set: -*background* +*background=* The background color for _swaynag_. -*border* +*border=* The color to use for borders of buttons. -*border-bottom* +*border-bottom=* The color of the border line at the bottom of _swaynag_. -*button-background* +*button-background=* The background color for the buttons. -*text* +*text=* The color of the text. +The following sizing options can also be set: + +*border-bottom-size=* + Set the thickness of the bottom border. + +*message-padding=* + Set the padding for the message. + +*details-border-size=* + Set the thickness for the details border. + +*button-border-size=* + Set the thickness for the button border. + +*button-gap=* + Set the size of the gap between buttons. + +*button-dismiss-gap=* + Set the size of the gap between the dismiss button and another button. + +*button-margin-right=* + Set the margin from the right of the dismiss button to edge. + +*button-padding=* + Set the padding for the button text. + +Additionally, the following options can be assigned a default per-type: + +*edge=top|bottom* + Set the edge to use. + +*font=* + Set the font to use. + +*output=* + Set the output to use. This should be the name of a _xdg\_output_. + # EXAMPLE ``` font=Monospace 12 +edge=bottom [green] +edge=top background=00AA00 border=006600 border-bottom=004400 text=FFFFFF button-background=00CC00 +message-padding=10 ``` # SEE diff --git a/swaynag/swaynag.c b/swaynag/swaynag.c index e5d2e216..e79cd879 100644 --- a/swaynag/swaynag.c +++ b/swaynag/swaynag.c @@ -345,7 +345,8 @@ void swaynag_setup(struct swaynag *swaynag) { assert(swaynag->layer_surface); zwlr_layer_surface_v1_add_listener(swaynag->layer_surface, &layer_surface_listener, swaynag); - zwlr_layer_surface_v1_set_anchor(swaynag->layer_surface, swaynag->anchors); + zwlr_layer_surface_v1_set_anchor(swaynag->layer_surface, + swaynag->type->anchors); wl_registry_destroy(registry); } @@ -363,7 +364,6 @@ void swaynag_destroy(struct swaynag *swaynag) { swaynag->run_display = false; free(swaynag->message); - free(swaynag->font); while (swaynag->buttons->length) { struct swaynag_button *button = swaynag->buttons->items[0]; list_del(swaynag->buttons, 0); diff --git a/swaynag/types.c b/swaynag/types.c index c92d0e89..f429baf0 100644 --- a/swaynag/types.c +++ b/swaynag/types.c @@ -9,8 +9,26 @@ #include "swaynag/config.h" #include "swaynag/types.h" #include "util.h" +#include "wlr-layer-shell-unstable-v1-client-protocol.h" void swaynag_types_add_default(list_t *types) { + struct swaynag_type *type_defaults; + type_defaults = calloc(1, sizeof(struct swaynag_type)); + type_defaults->name = strdup(""); + type_defaults->font = strdup("pango:Monospace 10"); + type_defaults->anchors = ZWLR_LAYER_SURFACE_V1_ANCHOR_TOP + | ZWLR_LAYER_SURFACE_V1_ANCHOR_LEFT + | ZWLR_LAYER_SURFACE_V1_ANCHOR_RIGHT; + type_defaults->bar_border_thickness = 2; + type_defaults->message_padding = 8; + type_defaults->details_border_thickness = 3; + type_defaults->button_border_thickness = 3; + type_defaults->button_gap = 20; + type_defaults->button_gap_close = 15; + type_defaults->button_margin_right = 2; + type_defaults->button_padding = 3; + list_add(types, type_defaults); + struct swaynag_type *type_error; type_error = calloc(1, sizeof(struct swaynag_type)); type_error->name = strdup("error"); @@ -42,20 +60,84 @@ struct swaynag_type *swaynag_type_get(list_t *types, char *name) { return NULL; } -struct swaynag_type *swaynag_type_clone(struct swaynag_type *type) { - struct swaynag_type *clone; - clone = calloc(1, sizeof(struct swaynag_type)); - clone->name = strdup(type->name); - clone->button_background = type->button_background; - clone->background = type->background; - clone->text = type->text; - clone->border = type->border; - clone->border_bottom = type->border_bottom; - return clone; +void swaynag_type_merge(struct swaynag_type *dest, struct swaynag_type *src) { + if (!dest || !src) { + return; + } + + if (!dest->font && src->font) { + dest->font = strdup(src->font); + } + + if (!dest->output && src->output) { + dest->output = strdup(src->output); + } + + if (dest->anchors == 0 && src->anchors > 0) { + dest->anchors = src->anchors; + } + + // Colors + if (dest->button_background == 0 && src->button_background > 0) { + dest->button_background = src->button_background; + } + + if (dest->background == 0 && src->background > 0) { + dest->background = src->background; + } + + if (dest->text == 0 && src->text > 0) { + dest->text = src->text; + } + + if (dest->border == 0 && src->border > 0) { + dest->border = src->border; + } + + if (dest->border_bottom == 0 && src->border_bottom > 0) { + dest->border_bottom = src->border_bottom; + } + + // Sizing + if (dest->bar_border_thickness == 0 && src->bar_border_thickness > 0) { + dest->bar_border_thickness = src->bar_border_thickness; + } + + if (dest->message_padding == 0 && src->message_padding > 0) { + dest->message_padding = src->message_padding; + } + + if (dest->details_border_thickness == 0 + && src->details_border_thickness > 0) { + dest->details_border_thickness = src->details_border_thickness; + } + + if (dest->button_border_thickness == 0 + && src->button_border_thickness > 0) { + dest->button_border_thickness = src->button_border_thickness; + } + + if (dest->button_gap == 0 && src->button_gap > 0) { + dest->button_gap = src->button_gap; + } + + if (dest->button_gap_close == 0 && src->button_gap_close > 0) { + dest->button_gap_close = src->button_gap_close; + } + + if (dest->button_margin_right == 0 && src->button_margin_right > 0) { + dest->button_margin_right = src->button_margin_right; + } + + if (dest->button_padding == 0 && src->button_padding > 0) { + dest->button_padding = src->button_padding; + } } void swaynag_type_free(struct swaynag_type *type) { free(type->name); + free(type->font); + free(type->output); free(type); } @@ -67,51 +149,3 @@ void swaynag_types_free(list_t *types) { } list_free(types); } - -int swaynag_parse_type(int argc, char **argv, struct swaynag_type *type) { - enum color_option { - COLOR_BACKGROUND, - COLOR_BORDER, - COLOR_BORDER_BOTTOM, - COLOR_BUTTON, - COLOR_TEXT, - }; - - static struct option opts[] = { - {"background", required_argument, NULL, COLOR_BACKGROUND}, - {"border", required_argument, NULL, COLOR_BORDER}, - {"border-bottom", required_argument, NULL, COLOR_BORDER_BOTTOM}, - {"button-background", required_argument, NULL, COLOR_BUTTON}, - {"text", required_argument, NULL, COLOR_TEXT}, - {0, 0, 0, 0} - }; - - optind = 1; - while (1) { - int c = getopt_long(argc, argv, "", opts, NULL); - if (c == -1) { - break; - } - switch (c) { - case COLOR_BACKGROUND: - type->background = parse_color(optarg); - break; - case COLOR_BORDER: - type->border = parse_color(optarg); - break; - case COLOR_BORDER_BOTTOM: - type->border_bottom = parse_color(optarg); - break; - case COLOR_BUTTON: - type->button_background = parse_color(optarg); - break; - case COLOR_TEXT: - type->text = parse_color(optarg); - break; - default: - break; - } - } - return 0; -} - From 0ef3988438c251c84251ef7a2723791856505065 Mon Sep 17 00:00:00 2001 From: Brian Ashworth Date: Mon, 30 Jul 2018 01:02:50 -0400 Subject: [PATCH 134/148] swaynag: fix hidpi --- include/swaynag/swaynag.h | 8 ++- swaynag/main.c | 5 +- swaynag/render.c | 24 ++++---- swaynag/swaynag.c | 120 +++++++++++++++++++++++--------------- 4 files changed, 95 insertions(+), 62 deletions(-) diff --git a/include/swaynag/swaynag.h b/include/swaynag/swaynag.h index dd6fe0cd..09b95326 100644 --- a/include/swaynag/swaynag.h +++ b/include/swaynag/swaynag.h @@ -9,6 +9,8 @@ #define SWAYNAG_MAX_HEIGHT 500 +struct swaynag; + enum swaynag_action_type { SWAYNAG_ACTION_DISMISS, SWAYNAG_ACTION_EXPAND, @@ -28,6 +30,9 @@ struct swaynag_output { char *name; struct wl_output *wl_output; uint32_t wl_name; + uint32_t scale; + struct swaynag *swaynag; + struct wl_list link; }; struct swaynag_button { @@ -67,7 +72,8 @@ struct swaynag { struct wl_shm *shm; struct swaynag_pointer pointer; struct zxdg_output_manager_v1 *xdg_output_manager; - struct swaynag_output output; + struct wl_list outputs; // struct swaynag_output + struct swaynag_output *output; struct zwlr_layer_shell_v1 *layer_shell; struct zwlr_layer_surface_v1 *layer_surface; struct wl_surface *surface; diff --git a/swaynag/main.c b/swaynag/main.c index 20d03c31..854368e5 100644 --- a/swaynag/main.c +++ b/swaynag/main.c @@ -94,9 +94,6 @@ int main(int argc, char **argv) { swaynag_type_merge(type, swaynag_type_get(types, "")); swaynag_type_merge(type, swaynag_type_get(types, "")); swaynag.type = type; - if (swaynag.type->output) { - swaynag.output.name = strdup(swaynag.type->output); - } swaynag_types_free(types); @@ -106,7 +103,7 @@ int main(int argc, char **argv) { free(swaynag.details.button_details.text); } - wlr_log(WLR_DEBUG, "Output: %s", swaynag.output.name); + wlr_log(WLR_DEBUG, "Output: %s", swaynag.type->output); wlr_log(WLR_DEBUG, "Anchors: %d", swaynag.type->anchors); wlr_log(WLR_DEBUG, "Type: %s", swaynag.type->name); wlr_log(WLR_DEBUG, "Message: %s", swaynag.message); diff --git a/swaynag/render.c b/swaynag/render.c index bc3e520e..766409e4 100644 --- a/swaynag/render.c +++ b/swaynag/render.c @@ -25,10 +25,10 @@ static uint32_t render_message(cairo_t *cairo, struct swaynag *swaynag) { cairo_set_source_u32(cairo, swaynag->type->text); cairo_move_to(cairo, padding, (int)(ideal_height - text_height) / 2); - pango_printf(cairo, swaynag->type->font, swaynag->scale, false, "%s", - swaynag->message); + pango_printf(cairo, swaynag->type->font, swaynag->scale, false, + "%s", swaynag->message); - return ideal_height; + return ideal_surface_height; } static void render_details_scroll_button(cairo_t *cairo, @@ -61,9 +61,11 @@ static int get_detailed_scroll_button_width(cairo_t *cairo, struct swaynag *swaynag) { int up_width, down_width, temp_height; get_text_size(cairo, swaynag->type->font, &up_width, &temp_height, - swaynag->scale, true, "%s", swaynag->details.button_up.text); + swaynag->scale, true, + "%s", swaynag->details.button_up.text); get_text_size(cairo, swaynag->type->font, &down_width, &temp_height, - swaynag->scale, true, "%s", swaynag->details.button_down.text); + swaynag->scale, true, + "%s", swaynag->details.button_down.text); int text_width = up_width > down_width ? up_width : down_width; int border = swaynag->type->button_border_thickness * swaynag->scale; @@ -83,7 +85,7 @@ static uint32_t render_detailed(cairo_t *cairo, struct swaynag *swaynag, int decor = padding + border; swaynag->details.x = decor; - swaynag->details.y = y + decor; + swaynag->details.y = y * swaynag->scale + decor; swaynag->details.width = width - decor * 2; PangoLayout *layout = get_pango_layout(cairo, swaynag->type->font, @@ -167,7 +169,7 @@ static uint32_t render_detailed(cairo_t *cairo, struct swaynag *swaynag, pango_cairo_show_layout(cairo, layout); g_object_unref(layout); - return ideal_height; + return ideal_height / swaynag->scale; } static uint32_t render_button(cairo_t *cairo, struct swaynag *swaynag, @@ -211,7 +213,7 @@ static uint32_t render_button(cairo_t *cairo, struct swaynag *swaynag, *x = button->x - border; - return ideal_height; + return ideal_surface_height; } static uint32_t render_to_cairo(cairo_t *cairo, struct swaynag *swaynag) { @@ -245,8 +247,10 @@ static uint32_t render_to_cairo(cairo_t *cairo, struct swaynag *swaynag) { max_height += border; } cairo_set_source_u32(cairo, swaynag->type->border_bottom); - cairo_rectangle(cairo, 0, swaynag->height * swaynag->scale - border, - swaynag->width * swaynag->scale, border); + cairo_rectangle(cairo, 0, + swaynag->height * swaynag->scale - border, + swaynag->width * swaynag->scale, + border); cairo_fill(cairo); return max_height; diff --git a/swaynag/swaynag.c b/swaynag/swaynag.c index e79cd879..22511529 100644 --- a/swaynag/swaynag.c +++ b/swaynag/swaynag.c @@ -85,12 +85,34 @@ static struct zwlr_layer_surface_v1_listener layer_surface_listener = { .closed = layer_surface_closed, }; +static void surface_enter(void *data, struct wl_surface *surface, + struct wl_output *output) { + struct swaynag *swaynag = data; + struct swaynag_output *swaynag_output; + wl_list_for_each(swaynag_output, &swaynag->outputs, link) { + if (swaynag_output->wl_output == output) { + wlr_log(WLR_DEBUG, "Surface enter on output %s", + swaynag_output->name); + swaynag->output = swaynag_output; + swaynag->scale = swaynag->output->scale; + render_frame(swaynag); + break; + } + }; +} + +static struct wl_surface_listener surface_listener = { + .enter = surface_enter, + .leave = nop, +}; + static void wl_pointer_enter(void *data, struct wl_pointer *wl_pointer, uint32_t serial, struct wl_surface *surface, wl_fixed_t surface_x, wl_fixed_t surface_y) { struct swaynag *swaynag = data; struct swaynag_pointer *pointer = &swaynag->pointer; - wl_surface_set_buffer_scale(pointer->cursor_surface, swaynag->scale); + wl_surface_set_buffer_scale(pointer->cursor_surface, + swaynag->scale); wl_surface_attach(pointer->cursor_surface, wl_cursor_image_get_buffer(pointer->cursor_image), 0, 0); wl_pointer_set_cursor(wl_pointer, serial, pointer->cursor_surface, @@ -141,7 +163,8 @@ static void wl_pointer_button(void *data, struct wl_pointer *wl_pointer, } struct swaynag_button button_down = swaynag->details.button_down; - int bot = swaynag->details.total_lines - swaynag->details.visible_lines; + int bot = swaynag->details.total_lines; + bot -= swaynag->details.visible_lines; if (x >= button_down.x && y >= button_down.y && x < button_down.x + button_down.width @@ -206,9 +229,12 @@ const struct wl_seat_listener seat_listener = { static void output_scale(void *data, struct wl_output *output, int32_t factor) { - struct swaynag *swaynag = data; - swaynag->scale = factor; - render_frame(swaynag); + struct swaynag_output *swaynag_output = data; + swaynag_output->scale = factor; + if (swaynag_output->swaynag->output == swaynag_output) { + swaynag_output->swaynag->scale = swaynag_output->scale; + render_frame(swaynag_output->swaynag); + } } static struct wl_output_listener output_listener = { @@ -218,33 +244,19 @@ static struct wl_output_listener output_listener = { .scale = output_scale, }; -struct output_state { - struct wl_output *wl_output; - uint32_t wl_name; - struct zxdg_output_v1 *xdg_output; - struct swaynag *swaynag; -}; - static void xdg_output_handle_name(void *data, struct zxdg_output_v1 *xdg_output, const char *name) { - struct output_state *state = data; - char *outname = state->swaynag->output.name; + struct swaynag_output *swaynag_output = data; + char *outname = swaynag_output->swaynag->type->output; wlr_log(WLR_DEBUG, "Checking against output %s for %s", name, outname); - if (!state->swaynag->output.wl_output && outname && name + if (!swaynag_output->swaynag->output && outname && name && strcmp(outname, name) == 0) { wlr_log(WLR_DEBUG, "Using output %s", name); - state->swaynag->output.wl_output = state->wl_output; - state->swaynag->output.wl_name = state->wl_name; - wl_output_add_listener(state->swaynag->output.wl_output, - &output_listener, state->swaynag); - wl_display_roundtrip(state->swaynag->display); - zxdg_output_v1_destroy(state->xdg_output); - } else { - zxdg_output_v1_destroy(state->xdg_output); - wl_output_destroy(state->wl_output); + swaynag_output->swaynag->output = swaynag_output; } - state->swaynag->querying_outputs--; - free(state); + swaynag_output->name = strdup(name); + zxdg_output_v1_destroy(xdg_output); + swaynag_output->swaynag->querying_outputs--; } static struct zxdg_output_v1_listener xdg_output_listener = { @@ -267,18 +279,24 @@ static void handle_global(void *data, struct wl_registry *registry, } else if (strcmp(interface, wl_shm_interface.name) == 0) { swaynag->shm = wl_registry_bind(registry, name, &wl_shm_interface, 1); } else if (strcmp(interface, wl_output_interface.name) == 0) { - if (!swaynag->output.wl_output && swaynag->xdg_output_manager) { + if (!swaynag->output && swaynag->xdg_output_manager) { swaynag->querying_outputs++; - struct output_state *state = - calloc(1, sizeof(struct output_state)); - state->swaynag = swaynag; - state->wl_output = wl_registry_bind(registry, name, + struct swaynag_output *output = + calloc(1, sizeof(struct swaynag_output)); + output->wl_output = wl_registry_bind(registry, name, &wl_output_interface, 3); - state->wl_name = name; - state->xdg_output = zxdg_output_manager_v1_get_xdg_output( - swaynag->xdg_output_manager, state->wl_output); - zxdg_output_v1_add_listener(state->xdg_output, - &xdg_output_listener, state); + output->wl_name = name; + output->scale = 1; + output->swaynag = swaynag; + wl_list_insert(&swaynag->outputs, &output->link); + wl_output_add_listener(output->wl_output, + &output_listener, output); + + struct zxdg_output_v1 *xdg_output; + xdg_output = zxdg_output_manager_v1_get_xdg_output( + swaynag->xdg_output_manager, output->wl_output); + zxdg_output_v1_add_listener(xdg_output, + &xdg_output_listener, output); } } else if (strcmp(interface, zwlr_layer_shell_v1_interface.name) == 0) { swaynag->layer_shell = wl_registry_bind( @@ -294,7 +312,7 @@ static void handle_global(void *data, struct wl_registry *registry, static void handle_global_remove(void *data, struct wl_registry *registry, uint32_t name) { struct swaynag *swaynag = data; - if (swaynag->output.wl_name == name) { + if (swaynag->output->wl_name == name) { swaynag->run_display = false; } } @@ -309,6 +327,7 @@ void swaynag_setup(struct swaynag *swaynag) { assert(swaynag->display); swaynag->scale = 1; + wl_list_init(&swaynag->outputs); struct wl_registry *registry = wl_display_get_registry(swaynag->display); wl_registry_add_listener(registry, ®istry_listener, swaynag); @@ -319,16 +338,16 @@ void swaynag_setup(struct swaynag *swaynag) { wl_display_roundtrip(swaynag->display); } - if (!swaynag->output.wl_output && swaynag->output.name) { - wlr_log(WLR_ERROR, "Output '%s' not found", swaynag->output.name); + if (!swaynag->output && swaynag->type->output) { + wlr_log(WLR_ERROR, "Output '%s' not found", swaynag->type->output); swaynag_destroy(swaynag); exit(EXIT_FAILURE); } struct swaynag_pointer *pointer = &swaynag->pointer; - int scale = swaynag->scale < 1 ? 1 : swaynag->scale; - pointer->cursor_theme = wl_cursor_theme_load( - NULL, 24 * scale, swaynag->shm); + int scale = swaynag->output ? swaynag->scale : 1; + pointer->cursor_theme = wl_cursor_theme_load(NULL, 24 * scale, + swaynag->shm); assert(pointer->cursor_theme); struct wl_cursor *cursor = wl_cursor_theme_get_cursor(pointer->cursor_theme, "left_ptr"); @@ -339,8 +358,11 @@ void swaynag_setup(struct swaynag *swaynag) { swaynag->surface = wl_compositor_create_surface(swaynag->compositor); assert(swaynag->surface); + wl_surface_add_listener(swaynag->surface, &surface_listener, swaynag); + swaynag->layer_surface = zwlr_layer_shell_v1_get_layer_surface( - swaynag->layer_shell, swaynag->surface, swaynag->output.wl_output, + swaynag->layer_shell, swaynag->surface, + swaynag->output ? swaynag->output->wl_output : NULL, ZWLR_LAYER_SHELL_V1_LAYER_TOP, "swaynag"); assert(swaynag->layer_surface); zwlr_layer_surface_v1_add_listener(swaynag->layer_surface, @@ -388,10 +410,6 @@ void swaynag_destroy(struct swaynag *swaynag) { wl_surface_destroy(swaynag->surface); } - if (swaynag->output.wl_output) { - wl_output_destroy(swaynag->output.wl_output); - } - if (&swaynag->buffers[0]) { destroy_buffer(&swaynag->buffers[0]); } @@ -400,6 +418,14 @@ void swaynag_destroy(struct swaynag *swaynag) { destroy_buffer(&swaynag->buffers[1]); } + struct swaynag_output *output, *temp; + wl_list_for_each_safe(output, temp, &swaynag->outputs, link) { + wl_output_destroy(output->wl_output); + free(output->name); + wl_list_remove(&output->link); + free(output); + }; + if (swaynag->compositor) { wl_compositor_destroy(swaynag->compositor); } From 4f5cf330c8643a154215cbae5758b86022d6edb3 Mon Sep 17 00:00:00 2001 From: Brian Ashworth Date: Mon, 30 Jul 2018 13:52:02 -0400 Subject: [PATCH 135/148] swaynag: address some more of sircmpwn's comments Fixes segfauls for any case where swaynag->outputs was not inititalized including -h/--help, -v/--version, and invalid arguments. Sets sane defaults for colors not given. Any color not given will fallback to the default color values for type error. Adds support for a hidpi cursor --- include/swaynag/swaynag.h | 3 ++- swaynag/swaynag.c | 57 +++++++++++++++++++++++---------------- swaynag/types.c | 10 +++---- 3 files changed, 41 insertions(+), 29 deletions(-) diff --git a/include/swaynag/swaynag.h b/include/swaynag/swaynag.h index 09b95326..1bf8b640 100644 --- a/include/swaynag/swaynag.h +++ b/include/swaynag/swaynag.h @@ -19,6 +19,7 @@ enum swaynag_action_type { struct swaynag_pointer { struct wl_pointer *pointer; + uint32_t serial; struct wl_cursor_theme *cursor_theme; struct wl_cursor_image *cursor_image; struct wl_surface *cursor_surface; @@ -72,7 +73,7 @@ struct swaynag { struct wl_shm *shm; struct swaynag_pointer pointer; struct zxdg_output_manager_v1 *xdg_output_manager; - struct wl_list outputs; // struct swaynag_output + struct wl_list outputs; // swaynag_output::link struct swaynag_output *output; struct zwlr_layer_shell_v1 *layer_shell; struct zwlr_layer_surface_v1 *layer_surface; diff --git a/swaynag/swaynag.c b/swaynag/swaynag.c index 22511529..3966277d 100644 --- a/swaynag/swaynag.c +++ b/swaynag/swaynag.c @@ -106,19 +106,31 @@ static struct wl_surface_listener surface_listener = { .leave = nop, }; +static void update_cursor(struct swaynag *swaynag) { + struct swaynag_pointer *pointer = &swaynag->pointer; + pointer->cursor_theme = wl_cursor_theme_load(NULL, 24 * swaynag->scale, + swaynag->shm); + struct wl_cursor *cursor = + wl_cursor_theme_get_cursor(pointer->cursor_theme, "left_ptr"); + pointer->cursor_image = cursor->images[0]; + wl_surface_set_buffer_scale(pointer->cursor_surface, + swaynag->scale); + wl_surface_attach(pointer->cursor_surface, + wl_cursor_image_get_buffer(pointer->cursor_image), 0, 0); + wl_pointer_set_cursor(pointer->pointer, pointer->serial, + pointer->cursor_surface, + pointer->cursor_image->hotspot_x / swaynag->scale, + pointer->cursor_image->hotspot_y / swaynag->scale); + wl_surface_commit(pointer->cursor_surface); +} + static void wl_pointer_enter(void *data, struct wl_pointer *wl_pointer, uint32_t serial, struct wl_surface *surface, wl_fixed_t surface_x, wl_fixed_t surface_y) { struct swaynag *swaynag = data; struct swaynag_pointer *pointer = &swaynag->pointer; - wl_surface_set_buffer_scale(pointer->cursor_surface, - swaynag->scale); - wl_surface_attach(pointer->cursor_surface, - wl_cursor_image_get_buffer(pointer->cursor_image), 0, 0); - wl_pointer_set_cursor(wl_pointer, serial, pointer->cursor_surface, - pointer->cursor_image->hotspot_x / swaynag->scale, - pointer->cursor_image->hotspot_y / swaynag->scale); - wl_surface_commit(pointer->cursor_surface); + pointer->serial = serial; + update_cursor(swaynag); } static void wl_pointer_motion(void *data, struct wl_pointer *wl_pointer, @@ -233,6 +245,7 @@ static void output_scale(void *data, struct wl_output *output, swaynag_output->scale = factor; if (swaynag_output->swaynag->output == swaynag_output) { swaynag_output->swaynag->scale = swaynag_output->scale; + update_cursor(swaynag_output->swaynag); render_frame(swaynag_output->swaynag); } } @@ -345,14 +358,6 @@ void swaynag_setup(struct swaynag *swaynag) { } struct swaynag_pointer *pointer = &swaynag->pointer; - int scale = swaynag->output ? swaynag->scale : 1; - pointer->cursor_theme = wl_cursor_theme_load(NULL, 24 * scale, - swaynag->shm); - assert(pointer->cursor_theme); - struct wl_cursor *cursor = - wl_cursor_theme_get_cursor(pointer->cursor_theme, "left_ptr"); - assert(cursor); - pointer->cursor_image = cursor->images[0]; pointer->cursor_surface = wl_compositor_create_surface(swaynag->compositor); assert(pointer->cursor_surface); @@ -410,6 +415,10 @@ void swaynag_destroy(struct swaynag *swaynag) { wl_surface_destroy(swaynag->surface); } + if (swaynag->pointer.cursor_theme) { + wl_cursor_theme_destroy(swaynag->pointer.cursor_theme); + } + if (&swaynag->buffers[0]) { destroy_buffer(&swaynag->buffers[0]); } @@ -418,13 +427,15 @@ void swaynag_destroy(struct swaynag *swaynag) { destroy_buffer(&swaynag->buffers[1]); } - struct swaynag_output *output, *temp; - wl_list_for_each_safe(output, temp, &swaynag->outputs, link) { - wl_output_destroy(output->wl_output); - free(output->name); - wl_list_remove(&output->link); - free(output); - }; + if (swaynag->outputs.prev || swaynag->outputs.next) { + struct swaynag_output *output, *temp; + wl_list_for_each_safe(output, temp, &swaynag->outputs, link) { + wl_output_destroy(output->wl_output); + free(output->name); + wl_list_remove(&output->link); + free(output); + }; + } if (swaynag->compositor) { wl_compositor_destroy(swaynag->compositor); diff --git a/swaynag/types.c b/swaynag/types.c index f429baf0..66b802cd 100644 --- a/swaynag/types.c +++ b/swaynag/types.c @@ -19,6 +19,11 @@ void swaynag_types_add_default(list_t *types) { type_defaults->anchors = ZWLR_LAYER_SURFACE_V1_ANCHOR_TOP | ZWLR_LAYER_SURFACE_V1_ANCHOR_LEFT | ZWLR_LAYER_SURFACE_V1_ANCHOR_RIGHT; + type_defaults->button_background = 0x680A0AFF; + type_defaults->background = 0x900000FF; + type_defaults->text = 0xFFFFFFFF; + type_defaults->border = 0xD92424FF; + type_defaults->border_bottom = 0x470909FF; type_defaults->bar_border_thickness = 2; type_defaults->message_padding = 8; type_defaults->details_border_thickness = 3; @@ -32,11 +37,6 @@ void swaynag_types_add_default(list_t *types) { struct swaynag_type *type_error; type_error = calloc(1, sizeof(struct swaynag_type)); type_error->name = strdup("error"); - type_error->button_background = 0x680A0AFF; - type_error->background = 0x900000FF; - type_error->text = 0xFFFFFFFF; - type_error->border = 0xD92424FF; - type_error->border_bottom = 0x470909FF; list_add(types, type_error); struct swaynag_type *type_warning; From 1e7fbe493eadc32531bc2aae69d75e5ffbf214c5 Mon Sep 17 00:00:00 2001 From: Brian Ashworth Date: Mon, 30 Jul 2018 14:14:16 -0400 Subject: [PATCH 136/148] swaynag: swaybar like default colors --- swaynag/types.c | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/swaynag/types.c b/swaynag/types.c index 66b802cd..1e0a138b 100644 --- a/swaynag/types.c +++ b/swaynag/types.c @@ -19,11 +19,11 @@ void swaynag_types_add_default(list_t *types) { type_defaults->anchors = ZWLR_LAYER_SURFACE_V1_ANCHOR_TOP | ZWLR_LAYER_SURFACE_V1_ANCHOR_LEFT | ZWLR_LAYER_SURFACE_V1_ANCHOR_RIGHT; - type_defaults->button_background = 0x680A0AFF; - type_defaults->background = 0x900000FF; + type_defaults->button_background = 0x333333FF; + type_defaults->background = 0x323232FF; type_defaults->text = 0xFFFFFFFF; - type_defaults->border = 0xD92424FF; - type_defaults->border_bottom = 0x470909FF; + type_defaults->border = 0x222222FF; + type_defaults->border_bottom = 0x444444FF; type_defaults->bar_border_thickness = 2; type_defaults->message_padding = 8; type_defaults->details_border_thickness = 3; @@ -36,6 +36,11 @@ void swaynag_types_add_default(list_t *types) { struct swaynag_type *type_error; type_error = calloc(1, sizeof(struct swaynag_type)); + type_error->button_background = 0x680A0AFF; + type_error->background = 0x900000FF; + type_error->text = 0xFFFFFFFF; + type_error->border = 0xD92424FF; + type_error->border_bottom = 0x470909FF; type_error->name = strdup("error"); list_add(types, type_error); From 41d858b4397f1c89a13900f078854e28ea0ac45a Mon Sep 17 00:00:00 2001 From: Brian Ashworth Date: Mon, 30 Jul 2018 15:17:51 -0400 Subject: [PATCH 137/148] swaynag: add blank lines after headings in scdocs --- swaynag/swaynag.1.scd | 3 +++ swaynag/swaynag.5.scd | 3 +++ 2 files changed, 6 insertions(+) diff --git a/swaynag/swaynag.1.scd b/swaynag/swaynag.1.scd index 12787c3c..1c395aee 100644 --- a/swaynag/swaynag.1.scd +++ b/swaynag/swaynag.1.scd @@ -9,6 +9,7 @@ swaynag - Show a warning or error message with buttons _swaynag_ [options...] # OPTIONS + *-b, --button* Create a button with the text _text_ that executes _action_ when pressed. Multiple buttons can be defined by providing the flag multiple times. @@ -60,6 +61,7 @@ _swaynag_ [options...] Show the version number and quit. # APPEARANCE OPTIONS + *--background* Set the color of the background. @@ -100,4 +102,5 @@ _swaynag_ [options...] Set the padding for the button text. # SEE + swaynag(5) diff --git a/swaynag/swaynag.5.scd b/swaynag/swaynag.5.scd index e2348d8b..d3daadf7 100644 --- a/swaynag/swaynag.5.scd +++ b/swaynag/swaynag.5.scd @@ -10,6 +10,7 @@ $HOME/.swaynag/config, $XDG\_CONFIG\_HOME/swaynag/config, SYSCONFDIR/swaynag/config # CONFIG FILE + At the top of the config file, _swaynag_ options can be set using the format _long-option=value_. These will be used as default values if _swaynag_ is not given the option. This can be useful for setting a preferred font, output, and @@ -79,6 +80,7 @@ Additionally, the following options can be assigned a default per-type: Set the output to use. This should be the name of a _xdg\_output_. # EXAMPLE + ``` font=Monospace 12 edge=bottom @@ -94,4 +96,5 @@ message-padding=10 ``` # SEE + swaynag(1) From 26c5ef18ba295e016074c9d87affe5da44e71cb1 Mon Sep 17 00:00:00 2001 From: Brian Ashworth Date: Wed, 1 Aug 2018 22:55:20 -0400 Subject: [PATCH 138/148] swaynag: don't drop \n for first line --- swaynag/config.c | 12 ++++-------- 1 file changed, 4 insertions(+), 8 deletions(-) diff --git a/swaynag/config.c b/swaynag/config.c index 80c5ad88..d6c5739d 100644 --- a/swaynag/config.c +++ b/swaynag/config.c @@ -19,18 +19,14 @@ static char *read_from_stdin() { continue; } - if (!buffer) { - buffer = strdup(line); - } else { - buffer = realloc(buffer, strlen(buffer) + strlen(line) + 2); - strcat(buffer, line); - strcat(buffer, "\n"); - } + size_t curlen = buffer ? strlen(buffer) : 0; + buffer = realloc(buffer, curlen + strlen(line) + 2); + snprintf(buffer + curlen, strlen(line) + 2, "%s\n", line); free(line); } - if (buffer && buffer[strlen(buffer) - 1] == '\n') { + while (buffer && buffer[strlen(buffer) - 1] == '\n') { buffer[strlen(buffer) - 1] = '\0'; } From d6095588a143710d25be47577ea65517770e7a74 Mon Sep 17 00:00:00 2001 From: Michel Ganguin Date: Thu, 2 Aug 2018 09:36:47 +0200 Subject: [PATCH 139/148] Link xcb dependency to meson options "enable_xwayland" (#2393) * Link xcb dependency to meson options "enable_xwayland" * Link xcb dependency to meson options "enable_xwayland" --- meson.build | 2 +- sway/meson.build | 10 +++++----- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/meson.build b/meson.build index 05d334d2..0d75978f 100644 --- a/meson.build +++ b/meson.build @@ -44,13 +44,13 @@ systemd = dependency('libsystemd', required: false) elogind = dependency('libelogind', required: false) math = cc.find_library('m') rt = cc.find_library('rt') -xcb = dependency('xcb') git = find_program('git', required: false) conf_data = configuration_data() if get_option('enable-xwayland') conf_data.set('HAVE_XWAYLAND', true) + xcb = dependency('xcb') else conf_data.set('HAVE_XWAYLAND', false) endif diff --git a/sway/meson.build b/sway/meson.build index d92bb905..a9503c3b 100644 --- a/sway/meson.build +++ b/sway/meson.build @@ -153,10 +153,6 @@ sway_sources = files( 'tree/output.c', ) -if get_option('enable-xwayland') - sway_sources += 'desktop/xwayland.c' -endif - sway_deps = [ cairo, gdk_pixbuf, @@ -170,10 +166,14 @@ sway_deps = [ server_protos, wayland_server, wlroots, - xcb, xkbcommon, ] +if get_option('enable-xwayland') + sway_sources += 'desktop/xwayland.c' + sway_deps += xcb +endif + executable( 'sway', sway_sources, From 8314019f660cd28fc8cdb634f82b437105074258 Mon Sep 17 00:00:00 2001 From: Ryan Dwyer Date: Thu, 2 Aug 2018 20:54:03 +1000 Subject: [PATCH 140/148] Fix race condition crashes when unmapping views This fixes two issues which were both introduced in #2396. First issue: The PR changes the location of the buffer save to transaction_apply, but puts it inside the should_configure block. For unmapping (destroying) views, should_configure returns false so it wasn't saving the buffer. If a frame was rendered between the unmap and the transaction applying then it would result in a crash. Second issue: If a destroying view is involved in two transactions, we must not release the buffer between the transactions because there is no live buffer to grab any more. --- sway/desktop/transaction.c | 23 +++++++++++++++-------- 1 file changed, 15 insertions(+), 8 deletions(-) diff --git a/sway/desktop/transaction.c b/sway/desktop/transaction.c index 94070363..4e6af86a 100644 --- a/sway/desktop/transaction.c +++ b/sway/desktop/transaction.c @@ -195,11 +195,18 @@ static void transaction_apply(struct sway_transaction *transaction) { sizeof(struct sway_container_state)); if (container->type == C_VIEW) { - if (container->sway_view->saved_buffer) { - view_remove_saved_buffer(container->sway_view); - } - if (container->instructions->length > 1) { - view_save_buffer(container->sway_view); + if (container->destroying) { + if (container->instructions->length == 1 && + container->sway_view->saved_buffer) { + view_remove_saved_buffer(container->sway_view); + } + } else { + if (container->sway_view->saved_buffer) { + view_remove_saved_buffer(container->sway_view); + } + if (container->instructions->length > 1) { + view_save_buffer(container->sway_view); + } } } } @@ -276,9 +283,9 @@ static void transaction_commit(struct sway_transaction *transaction) { // mapping and its default geometry doesn't intersect an output. struct timespec when; wlr_surface_send_frame_done(con->sway_view->surface, &when); - if (!con->sway_view->saved_buffer) { - view_save_buffer(con->sway_view); - } + } + if (con->type == C_VIEW && !con->sway_view->saved_buffer) { + view_save_buffer(con->sway_view); } list_add(con->instructions, instruction); } From d64c8df7ce79a959df340796448031bcaff7b668 Mon Sep 17 00:00:00 2001 From: Ryan Dwyer Date: Thu, 2 Aug 2018 20:59:44 +1000 Subject: [PATCH 141/148] Allow moving containers when workspace itself is focused --- sway/commands/move.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/sway/commands/move.c b/sway/commands/move.c index 46ebcd83..702b42d9 100644 --- a/sway/commands/move.c +++ b/sway/commands/move.c @@ -59,8 +59,7 @@ static struct cmd_results *cmd_move_container(struct sway_container *current, && strcasecmp(argv[2], "workspace") == 0) { // move container to workspace x if (current->type == C_WORKSPACE) { - // TODO: Wrap children in a container and move that - return cmd_results_new(CMD_FAILURE, "move", "Unimplemented"); + current = container_wrap_children(current); } else if (current->type != C_CONTAINER && current->type != C_VIEW) { return cmd_results_new(CMD_FAILURE, "move", "Can only move containers and views."); From 7d8413d9628ed493790454410a28b9f0c41444e6 Mon Sep 17 00:00:00 2001 From: Marien Zwart Date: Wed, 1 Aug 2018 23:21:29 +1000 Subject: [PATCH 142/148] Reset signal mask after fork wlroots uses wl_event_loop_add_signal to handle SIGUSR1 from Xwayland. wl_event_loop_add_signal works by masking the signal and receiving it from a signalfd. The signal mask is preserved across fork and exec, so subprocesses spawned by Sway start with SIGUSR1 masked. Most subprocesses do not expect this and never unmask the signal, resulting in missing functionality or unexpected behavior for processes that use SIGUSR1 (such as i3status). Fix this by unmasking all signals between fork and exec. --- sway/commands/exec_always.c | 4 ++++ sway/config/bar.c | 4 ++++ 2 files changed, 8 insertions(+) diff --git a/sway/commands/exec_always.c b/sway/commands/exec_always.c index 9bf2b320..c730cb8b 100644 --- a/sway/commands/exec_always.c +++ b/sway/commands/exec_always.c @@ -4,6 +4,7 @@ #include #include #include +#include #include "sway/commands.h" #include "sway/config.h" #include "sway/tree/container.h" @@ -47,6 +48,9 @@ struct cmd_results *cmd_exec_always(int argc, char **argv) { if ((pid = fork()) == 0) { // Fork child process again setsid(); + sigset_t set; + sigemptyset(&set); + sigprocmask(SIG_SETMASK, &set, NULL); close(fd[0]); if ((child = fork()) == 0) { close(fd[1]); diff --git a/sway/config/bar.c b/sway/config/bar.c index 3a74331e..ae9383d6 100644 --- a/sway/config/bar.c +++ b/sway/config/bar.c @@ -10,6 +10,7 @@ #include #include #include +#include #include "sway/config.h" #include "stringop.h" #include "list.h" @@ -175,6 +176,9 @@ void invoke_swaybar(struct bar_config *bar) { if (bar->pid == 0) { setpgid(0, 0); close(filedes[0]); + sigset_t set; + sigemptyset(&set); + sigprocmask(SIG_SETMASK, &set, NULL); // run custom swaybar size_t len = snprintf(NULL, 0, "%s -b %s", From 9339026a31103b453545cf65cd45f345e44d0437 Mon Sep 17 00:00:00 2001 From: Ryan Dwyer Date: Thu, 2 Aug 2018 21:55:37 +1000 Subject: [PATCH 143/148] Fix focus related crashes * seat_set_focus_warp lacked a container NULL check * view mapping code needs to use seat_get_focus_inactive Also, seat_set_focus_warp triggered the wrong IPC event if focus was a workspace, which resulted in swaybar not showing the workspace as active. --- sway/input/seat.c | 8 ++++++-- sway/tree/view.c | 5 +++-- 2 files changed, 9 insertions(+), 4 deletions(-) diff --git a/sway/input/seat.c b/sway/input/seat.c index 869560af..dd4d5c3b 100644 --- a/sway/input/seat.c +++ b/sway/input/seat.c @@ -775,8 +775,12 @@ void seat_set_focus_warp(struct sway_seat *seat, } } - if (container->type == C_VIEW) { - ipc_event_window(container, "focus"); + if (container) { + if (container->type == C_VIEW) { + ipc_event_window(container, "focus"); + } else if (container->type == C_WORKSPACE) { + ipc_event_workspace(NULL, container, "focus"); + } } seat->has_focus = (container != NULL); diff --git a/sway/tree/view.c b/sway/tree/view.c index 051b93ce..97318daa 100644 --- a/sway/tree/view.c +++ b/sway/tree/view.c @@ -496,7 +496,7 @@ static struct sway_container *select_workspace(struct sway_view *view) { } // Use the focused workspace - ws = seat_get_focus(seat); + ws = seat_get_focus_inactive(seat, &root_container); if (ws->type != C_WORKSPACE) { ws = container_parent(ws, C_WORKSPACE); } @@ -505,7 +505,8 @@ static struct sway_container *select_workspace(struct sway_view *view) { static bool should_focus(struct sway_view *view) { struct sway_seat *seat = input_manager_current_seat(input_manager); - struct sway_container *prev_focus = seat_get_focus(seat); + struct sway_container *prev_focus = + seat_get_focus_inactive(seat, &root_container); struct sway_container *prev_ws = prev_focus->type == C_WORKSPACE ? prev_focus : container_parent(prev_focus, C_WORKSPACE); struct sway_container *map_ws = container_parent(view->swayc, C_WORKSPACE); From 9aa258d33a9baa42895214da7e82f4568fcb8f76 Mon Sep 17 00:00:00 2001 From: Drew DeVault Date: Thu, 2 Aug 2018 09:05:46 -0400 Subject: [PATCH 144/148] Revert "Fix popups" This reverts commit de86d65627e96cffe77f4abf11c4a0b982326ff9. --- include/sway/output.h | 8 -- include/sway/tree/container.h | 11 ++- include/sway/tree/view.h | 11 --- sway/desktop/output.c | 19 +---- sway/desktop/render.c | 37 +--------- sway/desktop/xdg_shell.c | 9 --- sway/desktop/xdg_shell_v6.c | 10 --- sway/input/cursor.c | 3 + sway/tree/container.c | 134 ++++++++++------------------------ sway/tree/view.c | 10 --- 10 files changed, 55 insertions(+), 197 deletions(-) diff --git a/include/sway/output.h b/include/sway/output.h index 80dcd37b..6283db68 100644 --- a/include/sway/output.h +++ b/include/sway/output.h @@ -67,18 +67,10 @@ struct sway_container *output_get_active_workspace(struct sway_output *output); void output_render(struct sway_output *output, struct timespec *when, pixman_region32_t *damage); -void output_surface_for_each_surface(struct sway_output *output, - struct wlr_surface *surface, double ox, double oy, - sway_surface_iterator_func_t iterator, void *user_data); - void output_view_for_each_surface(struct sway_output *output, struct sway_view *view, sway_surface_iterator_func_t iterator, void *user_data); -void output_view_for_each_popup(struct sway_output *output, - struct sway_view *view, sway_surface_iterator_func_t iterator, - void *user_data); - void output_layer_for_each_surface(struct sway_output *output, struct wl_list *layer_surfaces, sway_surface_iterator_func_t iterator, void *user_data); diff --git a/include/sway/tree/container.h b/include/sway/tree/container.h index 12ff8a5a..d4a42a71 100644 --- a/include/sway/tree/container.h +++ b/include/sway/tree/container.h @@ -230,10 +230,17 @@ struct sway_container *container_parent(struct sway_container *container, * surface-local coordinates of the given layout coordinates if the container * is a view and the view contains a surface at those coordinates. */ -struct sway_container *container_at(struct sway_container *workspace, - double lx, double ly, struct wlr_surface **surface, +struct sway_container *container_at(struct sway_container *container, + double ox, double oy, struct wlr_surface **surface, double *sx, double *sy); +/** + * Same as container_at, but only checks floating views and expects coordinates + * to be layout coordinates, as that's what floating views use. + */ +struct sway_container *floating_container_at(double lx, double ly, + struct wlr_surface **surface, double *sx, double *sy); + /** * Apply the function for each descendant of the container breadth first. */ diff --git a/include/sway/tree/view.h b/include/sway/tree/view.h index 37fd02bc..6d8c3e6c 100644 --- a/include/sway/tree/view.h +++ b/include/sway/tree/view.h @@ -47,8 +47,6 @@ struct sway_view_impl { bool (*has_client_side_decorations)(struct sway_view *view); void (*for_each_surface)(struct sway_view *view, wlr_surface_iterator_func_t iterator, void *user_data); - void (*for_each_popup)(struct sway_view *view, - wlr_surface_iterator_func_t iterator, void *user_data); void (*close)(struct sway_view *view); void (*close_popups)(struct sway_view *view); void (*destroy)(struct sway_view *view); @@ -256,18 +254,9 @@ void view_close_popups(struct sway_view *view); void view_damage_from(struct sway_view *view); -/** - * Iterate all surfaces of a view (toplevels + popups). - */ void view_for_each_surface(struct sway_view *view, wlr_surface_iterator_func_t iterator, void *user_data); -/** - * Iterate all popups recursively. - */ -void view_for_each_popup(struct sway_view *view, - wlr_surface_iterator_func_t iterator, void *user_data); - // view implementation void view_init(struct sway_view *view, enum sway_view_type type, diff --git a/sway/desktop/output.c b/sway/desktop/output.c index 66747a3f..31b53213 100644 --- a/sway/desktop/output.c +++ b/sway/desktop/output.c @@ -119,7 +119,7 @@ static void output_for_each_surface_iterator(struct wlr_surface *surface, data->user_data); } -void output_surface_for_each_surface(struct sway_output *output, +static void output_surface_for_each_surface(struct sway_output *output, struct wlr_surface *surface, double ox, double oy, sway_surface_iterator_func_t iterator, void *user_data) { struct surface_iterator_data data = { @@ -155,23 +155,6 @@ void output_view_for_each_surface(struct sway_output *output, output_for_each_surface_iterator, &data); } -void output_view_for_each_popup(struct sway_output *output, - struct sway_view *view, sway_surface_iterator_func_t iterator, - void *user_data) { - struct surface_iterator_data data = { - .user_iterator = iterator, - .user_data = user_data, - .output = output, - .ox = view->swayc->current.view_x - output->swayc->current.swayc_x, - .oy = view->swayc->current.view_y - output->swayc->current.swayc_y, - .width = view->swayc->current.view_width, - .height = view->swayc->current.view_height, - .rotation = 0, // TODO - }; - - view_for_each_popup(view, output_for_each_surface_iterator, &data); -} - void output_layer_for_each_surface(struct sway_output *output, struct wl_list *layer_surfaces, sway_surface_iterator_func_t iterator, void *user_data) { diff --git a/sway/desktop/render.c b/sway/desktop/render.c index 1f374740..f0e47c95 100644 --- a/sway/desktop/render.c +++ b/sway/desktop/render.c @@ -186,36 +186,13 @@ static void premultiply_alpha(float color[4], float opacity) { color[2] *= color[3]; } -static void render_view_toplevels(struct sway_view *view, +static void render_view_surfaces(struct sway_view *view, struct sway_output *output, pixman_region32_t *damage, float alpha) { struct render_data data = { .damage = damage, .alpha = alpha, }; - // Render all toplevels without descending into popups - output_surface_for_each_surface(output, view->surface, - view->swayc->current.view_x, view->swayc->current.view_y, - render_surface_iterator, &data); -} - -static void render_popup_iterator(struct sway_output *output, - struct wlr_surface *surface, struct wlr_box *box, float rotation, - void *data) { - // Render this popup's surface - render_surface_iterator(output, surface, box, rotation, data); - - // Render this popup's child toplevels - output_surface_for_each_surface(output, surface, box->x, box->y, - render_surface_iterator, data); -} - -static void render_view_popups(struct sway_view *view, - struct sway_output *output, pixman_region32_t *damage, float alpha) { - struct render_data data = { - .damage = damage, - .alpha = alpha, - }; - output_view_for_each_popup(output, view, render_popup_iterator, &data); + output_view_for_each_surface(output, view, render_surface_iterator, &data); } static void render_saved_view(struct sway_view *view, @@ -262,7 +239,7 @@ static void render_view(struct sway_output *output, pixman_region32_t *damage, if (view->saved_buffer) { render_saved_view(view, output, damage, view->swayc->alpha); } else { - render_view_toplevels(view, output, damage, view->swayc->alpha); + render_view_surfaces(view, output, damage, view->swayc->alpha); } if (view->using_csd) { @@ -866,7 +843,7 @@ void output_render(struct sway_output *output, struct timespec *when, render_saved_view(fullscreen_con->sway_view, output, damage, 1.0f); } else { - render_view_toplevels(fullscreen_con->sway_view, + render_view_surfaces(fullscreen_con->sway_view, output, damage, 1.0f); } } else { @@ -902,12 +879,6 @@ void output_render(struct sway_output *output, struct timespec *when, &output->layers[ZWLR_LAYER_SHELL_V1_LAYER_TOP]); } - struct sway_seat *seat = input_manager_current_seat(input_manager); - struct sway_container *focus = seat_get_focus(seat); - if (focus && focus->type == C_VIEW) { - render_view_popups(focus->sway_view, output, damage, focus->alpha); - } - render_overlay: render_layer(output, damage, &output->layers[ZWLR_LAYER_SHELL_V1_LAYER_OVERLAY]); diff --git a/sway/desktop/xdg_shell.c b/sway/desktop/xdg_shell.c index b364663d..9d6b27e5 100644 --- a/sway/desktop/xdg_shell.c +++ b/sway/desktop/xdg_shell.c @@ -179,14 +179,6 @@ static void for_each_surface(struct sway_view *view, user_data); } -static void for_each_popup(struct sway_view *view, - wlr_surface_iterator_func_t iterator, void *user_data) { - if (xdg_shell_view_from_view(view) == NULL) { - return; - } - wlr_xdg_surface_for_each_popup(view->wlr_xdg_surface, iterator, user_data); -} - static void _close(struct sway_view *view) { if (xdg_shell_view_from_view(view) == NULL) { return; @@ -227,7 +219,6 @@ static const struct sway_view_impl view_impl = { .set_fullscreen = set_fullscreen, .wants_floating = wants_floating, .for_each_surface = for_each_surface, - .for_each_popup = for_each_popup, .close = _close, .close_popups = close_popups, .destroy = destroy, diff --git a/sway/desktop/xdg_shell_v6.c b/sway/desktop/xdg_shell_v6.c index ffea03ad..6e4aae62 100644 --- a/sway/desktop/xdg_shell_v6.c +++ b/sway/desktop/xdg_shell_v6.c @@ -175,15 +175,6 @@ static void for_each_surface(struct sway_view *view, user_data); } -static void for_each_popup(struct sway_view *view, - wlr_surface_iterator_func_t iterator, void *user_data) { - if (xdg_shell_v6_view_from_view(view) == NULL) { - return; - } - wlr_xdg_surface_v6_for_each_popup(view->wlr_xdg_surface_v6, iterator, - user_data); -} - static void _close(struct sway_view *view) { if (xdg_shell_v6_view_from_view(view) == NULL) { return; @@ -224,7 +215,6 @@ static const struct sway_view_impl view_impl = { .set_fullscreen = set_fullscreen, .wants_floating = wants_floating, .for_each_surface = for_each_surface, - .for_each_popup = for_each_popup, .close = _close, .close_popups = close_popups, .destroy = destroy, diff --git a/sway/input/cursor.c b/sway/input/cursor.c index 79f6ec46..d6fdc1da 100644 --- a/sway/input/cursor.c +++ b/sway/input/cursor.c @@ -109,6 +109,9 @@ static struct sway_container *container_at_coords( } struct sway_container *c; + if ((c = floating_container_at(lx, ly, surface, sx, sy))) { + return c; + } if ((c = container_at(ws, lx, ly, surface, sx, sy))) { return c; } diff --git a/sway/tree/container.c b/sway/tree/container.c index b980c5e9..b6ff4d30 100644 --- a/sway/tree/container.c +++ b/sway/tree/container.c @@ -569,15 +569,10 @@ static struct sway_container *container_at_view(struct sway_container *swayc, *sx = _sx; *sy = _sy; *surface = _surface; - return swayc; } - return NULL; + return swayc; } -static struct sway_container *tiling_container_at( - struct sway_container *con, double lx, double ly, - struct wlr_surface **surface, double *sx, double *sy); - /** * container_at for a container with layout L_TABBED. */ @@ -604,7 +599,7 @@ static struct sway_container *container_at_tabbed(struct sway_container *parent, // Surfaces struct sway_container *current = seat_get_active_child(seat, parent); - return tiling_container_at(current, lx, ly, surface, sx, sy); + return container_at(current, lx, ly, surface, sx, sy); } /** @@ -629,7 +624,7 @@ static struct sway_container *container_at_stacked( // Surfaces struct sway_container *current = seat_get_active_child(seat, parent); - return tiling_container_at(current, lx, ly, surface, sx, sy); + return container_at(current, lx, ly, surface, sx, sy); } /** @@ -647,13 +642,45 @@ static struct sway_container *container_at_linear(struct sway_container *parent, .height = child->height, }; if (wlr_box_contains_point(&box, lx, ly)) { - return tiling_container_at(child, lx, ly, surface, sx, sy); + return container_at(child, lx, ly, surface, sx, sy); } } return NULL; } -static struct sway_container *floating_container_at(double lx, double ly, +struct sway_container *container_at(struct sway_container *parent, + double lx, double ly, + struct wlr_surface **surface, double *sx, double *sy) { + if (!sway_assert(parent->type >= C_WORKSPACE, + "Expected workspace or deeper")) { + return NULL; + } + if (parent->type == C_VIEW) { + return container_at_view(parent, lx, ly, surface, sx, sy); + } + if (!parent->children->length) { + return NULL; + } + + switch (parent->layout) { + case L_HORIZ: + case L_VERT: + return container_at_linear(parent, lx, ly, surface, sx, sy); + case L_TABBED: + return container_at_tabbed(parent, lx, ly, surface, sx, sy); + case L_STACKED: + return container_at_stacked(parent, lx, ly, surface, sx, sy); + case L_FLOATING: + sway_assert(false, "Didn't expect to see floating here"); + return NULL; + case L_NONE: + return NULL; + } + + return NULL; +} + +struct sway_container *floating_container_at(double lx, double ly, struct wlr_surface **surface, double *sx, double *sy) { for (int i = 0; i < root_container.children->length; ++i) { struct sway_container *output = root_container.children->items[i]; @@ -675,8 +702,7 @@ static struct sway_container *floating_container_at(double lx, double ly, .height = floater->height, }; if (wlr_box_contains_point(&box, lx, ly)) { - return tiling_container_at(floater, lx, ly, - surface, sx, sy); + return container_at(floater, lx, ly, surface, sx, sy); } } } @@ -684,90 +710,6 @@ static struct sway_container *floating_container_at(double lx, double ly, return NULL; } -static struct sway_container *tiling_container_at( - struct sway_container *con, double lx, double ly, - struct wlr_surface **surface, double *sx, double *sy) { - if (con->type == C_VIEW) { - return container_at_view(con, lx, ly, surface, sx, sy); - } - if (!con->children->length) { - return NULL; - } - - switch (con->layout) { - case L_HORIZ: - case L_VERT: - return container_at_linear(con, lx, ly, surface, sx, sy); - case L_TABBED: - return container_at_tabbed(con, lx, ly, surface, sx, sy); - case L_STACKED: - return container_at_stacked(con, lx, ly, surface, sx, sy); - case L_FLOATING: - sway_assert(false, "Didn't expect to see floating here"); - return NULL; - case L_NONE: - return NULL; - } - return NULL; -} - -static bool surface_is_popup(struct wlr_surface *surface) { - if (wlr_surface_is_xdg_surface(surface)) { - struct wlr_xdg_surface *xdg_surface = - wlr_xdg_surface_from_wlr_surface(surface); - while (xdg_surface) { - if (xdg_surface->role == WLR_XDG_SURFACE_ROLE_POPUP) { - return true; - } - xdg_surface = xdg_surface->toplevel->parent; - } - return false; - } - - if (wlr_surface_is_xdg_surface_v6(surface)) { - struct wlr_xdg_surface_v6 *xdg_surface_v6 = - wlr_xdg_surface_v6_from_wlr_surface(surface); - while (xdg_surface_v6) { - if (xdg_surface_v6->role == WLR_XDG_SURFACE_V6_ROLE_POPUP) { - return true; - } - xdg_surface_v6 = xdg_surface_v6->toplevel->parent; - } - return false; - } - - return false; -} - -struct sway_container *container_at(struct sway_container *workspace, - double lx, double ly, - struct wlr_surface **surface, double *sx, double *sy) { - if (!sway_assert(workspace->type == C_WORKSPACE, "Expected a workspace")) { - return NULL; - } - struct sway_container *c; - // Focused view's popups - struct sway_seat *seat = input_manager_current_seat(input_manager); - struct sway_container *focus = - seat_get_focus_inactive(seat, &root_container); - if (focus && focus->type == C_VIEW) { - container_at_view(focus, lx, ly, surface, sx, sy); - if (*surface && surface_is_popup(*surface)) { - return focus; - } - *surface = NULL; - } - // Floating - if ((c = floating_container_at(lx, ly, surface, sx, sy))) { - return c; - } - // Tiling - if ((c = tiling_container_at(workspace, lx, ly, surface, sx, sy))) { - return c; - } - return NULL; -} - void container_for_each_descendant_dfs(struct sway_container *container, void (*f)(struct sway_container *container, void *data), void *data) { diff --git a/sway/tree/view.c b/sway/tree/view.c index 97318daa..27a6a8bd 100644 --- a/sway/tree/view.c +++ b/sway/tree/view.c @@ -339,16 +339,6 @@ void view_for_each_surface(struct sway_view *view, } } -void view_for_each_popup(struct sway_view *view, - wlr_surface_iterator_func_t iterator, void *user_data) { - if (!view->surface) { - return; - } - if (view->impl->for_each_popup) { - view->impl->for_each_popup(view, iterator, user_data); - } -} - static void view_subsurface_create(struct sway_view *view, struct wlr_subsurface *subsurface); From 8392eae40f17e550338b8b7058d8e9c1a6ad4f78 Mon Sep 17 00:00:00 2001 From: Ryan Dwyer Date: Thu, 2 Aug 2018 23:30:26 +1000 Subject: [PATCH 145/148] Revert "Revert "Fix popups"" This reverts commit 9aa258d33a9baa42895214da7e82f4568fcb8f76. Reverting the revert, so that popups can be fixed. --- include/sway/output.h | 8 ++ include/sway/tree/container.h | 11 +-- include/sway/tree/view.h | 11 +++ sway/desktop/output.c | 19 ++++- sway/desktop/render.c | 37 +++++++++- sway/desktop/xdg_shell.c | 9 +++ sway/desktop/xdg_shell_v6.c | 10 +++ sway/input/cursor.c | 3 - sway/tree/container.c | 134 ++++++++++++++++++++++++---------- sway/tree/view.c | 10 +++ 10 files changed, 197 insertions(+), 55 deletions(-) diff --git a/include/sway/output.h b/include/sway/output.h index 6283db68..80dcd37b 100644 --- a/include/sway/output.h +++ b/include/sway/output.h @@ -67,10 +67,18 @@ struct sway_container *output_get_active_workspace(struct sway_output *output); void output_render(struct sway_output *output, struct timespec *when, pixman_region32_t *damage); +void output_surface_for_each_surface(struct sway_output *output, + struct wlr_surface *surface, double ox, double oy, + sway_surface_iterator_func_t iterator, void *user_data); + void output_view_for_each_surface(struct sway_output *output, struct sway_view *view, sway_surface_iterator_func_t iterator, void *user_data); +void output_view_for_each_popup(struct sway_output *output, + struct sway_view *view, sway_surface_iterator_func_t iterator, + void *user_data); + void output_layer_for_each_surface(struct sway_output *output, struct wl_list *layer_surfaces, sway_surface_iterator_func_t iterator, void *user_data); diff --git a/include/sway/tree/container.h b/include/sway/tree/container.h index d4a42a71..12ff8a5a 100644 --- a/include/sway/tree/container.h +++ b/include/sway/tree/container.h @@ -230,17 +230,10 @@ struct sway_container *container_parent(struct sway_container *container, * surface-local coordinates of the given layout coordinates if the container * is a view and the view contains a surface at those coordinates. */ -struct sway_container *container_at(struct sway_container *container, - double ox, double oy, struct wlr_surface **surface, +struct sway_container *container_at(struct sway_container *workspace, + double lx, double ly, struct wlr_surface **surface, double *sx, double *sy); -/** - * Same as container_at, but only checks floating views and expects coordinates - * to be layout coordinates, as that's what floating views use. - */ -struct sway_container *floating_container_at(double lx, double ly, - struct wlr_surface **surface, double *sx, double *sy); - /** * Apply the function for each descendant of the container breadth first. */ diff --git a/include/sway/tree/view.h b/include/sway/tree/view.h index 6d8c3e6c..37fd02bc 100644 --- a/include/sway/tree/view.h +++ b/include/sway/tree/view.h @@ -47,6 +47,8 @@ struct sway_view_impl { bool (*has_client_side_decorations)(struct sway_view *view); void (*for_each_surface)(struct sway_view *view, wlr_surface_iterator_func_t iterator, void *user_data); + void (*for_each_popup)(struct sway_view *view, + wlr_surface_iterator_func_t iterator, void *user_data); void (*close)(struct sway_view *view); void (*close_popups)(struct sway_view *view); void (*destroy)(struct sway_view *view); @@ -254,9 +256,18 @@ void view_close_popups(struct sway_view *view); void view_damage_from(struct sway_view *view); +/** + * Iterate all surfaces of a view (toplevels + popups). + */ void view_for_each_surface(struct sway_view *view, wlr_surface_iterator_func_t iterator, void *user_data); +/** + * Iterate all popups recursively. + */ +void view_for_each_popup(struct sway_view *view, + wlr_surface_iterator_func_t iterator, void *user_data); + // view implementation void view_init(struct sway_view *view, enum sway_view_type type, diff --git a/sway/desktop/output.c b/sway/desktop/output.c index 31b53213..66747a3f 100644 --- a/sway/desktop/output.c +++ b/sway/desktop/output.c @@ -119,7 +119,7 @@ static void output_for_each_surface_iterator(struct wlr_surface *surface, data->user_data); } -static void output_surface_for_each_surface(struct sway_output *output, +void output_surface_for_each_surface(struct sway_output *output, struct wlr_surface *surface, double ox, double oy, sway_surface_iterator_func_t iterator, void *user_data) { struct surface_iterator_data data = { @@ -155,6 +155,23 @@ void output_view_for_each_surface(struct sway_output *output, output_for_each_surface_iterator, &data); } +void output_view_for_each_popup(struct sway_output *output, + struct sway_view *view, sway_surface_iterator_func_t iterator, + void *user_data) { + struct surface_iterator_data data = { + .user_iterator = iterator, + .user_data = user_data, + .output = output, + .ox = view->swayc->current.view_x - output->swayc->current.swayc_x, + .oy = view->swayc->current.view_y - output->swayc->current.swayc_y, + .width = view->swayc->current.view_width, + .height = view->swayc->current.view_height, + .rotation = 0, // TODO + }; + + view_for_each_popup(view, output_for_each_surface_iterator, &data); +} + void output_layer_for_each_surface(struct sway_output *output, struct wl_list *layer_surfaces, sway_surface_iterator_func_t iterator, void *user_data) { diff --git a/sway/desktop/render.c b/sway/desktop/render.c index f0e47c95..1f374740 100644 --- a/sway/desktop/render.c +++ b/sway/desktop/render.c @@ -186,13 +186,36 @@ static void premultiply_alpha(float color[4], float opacity) { color[2] *= color[3]; } -static void render_view_surfaces(struct sway_view *view, +static void render_view_toplevels(struct sway_view *view, struct sway_output *output, pixman_region32_t *damage, float alpha) { struct render_data data = { .damage = damage, .alpha = alpha, }; - output_view_for_each_surface(output, view, render_surface_iterator, &data); + // Render all toplevels without descending into popups + output_surface_for_each_surface(output, view->surface, + view->swayc->current.view_x, view->swayc->current.view_y, + render_surface_iterator, &data); +} + +static void render_popup_iterator(struct sway_output *output, + struct wlr_surface *surface, struct wlr_box *box, float rotation, + void *data) { + // Render this popup's surface + render_surface_iterator(output, surface, box, rotation, data); + + // Render this popup's child toplevels + output_surface_for_each_surface(output, surface, box->x, box->y, + render_surface_iterator, data); +} + +static void render_view_popups(struct sway_view *view, + struct sway_output *output, pixman_region32_t *damage, float alpha) { + struct render_data data = { + .damage = damage, + .alpha = alpha, + }; + output_view_for_each_popup(output, view, render_popup_iterator, &data); } static void render_saved_view(struct sway_view *view, @@ -239,7 +262,7 @@ static void render_view(struct sway_output *output, pixman_region32_t *damage, if (view->saved_buffer) { render_saved_view(view, output, damage, view->swayc->alpha); } else { - render_view_surfaces(view, output, damage, view->swayc->alpha); + render_view_toplevels(view, output, damage, view->swayc->alpha); } if (view->using_csd) { @@ -843,7 +866,7 @@ void output_render(struct sway_output *output, struct timespec *when, render_saved_view(fullscreen_con->sway_view, output, damage, 1.0f); } else { - render_view_surfaces(fullscreen_con->sway_view, + render_view_toplevels(fullscreen_con->sway_view, output, damage, 1.0f); } } else { @@ -879,6 +902,12 @@ void output_render(struct sway_output *output, struct timespec *when, &output->layers[ZWLR_LAYER_SHELL_V1_LAYER_TOP]); } + struct sway_seat *seat = input_manager_current_seat(input_manager); + struct sway_container *focus = seat_get_focus(seat); + if (focus && focus->type == C_VIEW) { + render_view_popups(focus->sway_view, output, damage, focus->alpha); + } + render_overlay: render_layer(output, damage, &output->layers[ZWLR_LAYER_SHELL_V1_LAYER_OVERLAY]); diff --git a/sway/desktop/xdg_shell.c b/sway/desktop/xdg_shell.c index 9d6b27e5..b364663d 100644 --- a/sway/desktop/xdg_shell.c +++ b/sway/desktop/xdg_shell.c @@ -179,6 +179,14 @@ static void for_each_surface(struct sway_view *view, user_data); } +static void for_each_popup(struct sway_view *view, + wlr_surface_iterator_func_t iterator, void *user_data) { + if (xdg_shell_view_from_view(view) == NULL) { + return; + } + wlr_xdg_surface_for_each_popup(view->wlr_xdg_surface, iterator, user_data); +} + static void _close(struct sway_view *view) { if (xdg_shell_view_from_view(view) == NULL) { return; @@ -219,6 +227,7 @@ static const struct sway_view_impl view_impl = { .set_fullscreen = set_fullscreen, .wants_floating = wants_floating, .for_each_surface = for_each_surface, + .for_each_popup = for_each_popup, .close = _close, .close_popups = close_popups, .destroy = destroy, diff --git a/sway/desktop/xdg_shell_v6.c b/sway/desktop/xdg_shell_v6.c index 6e4aae62..ffea03ad 100644 --- a/sway/desktop/xdg_shell_v6.c +++ b/sway/desktop/xdg_shell_v6.c @@ -175,6 +175,15 @@ static void for_each_surface(struct sway_view *view, user_data); } +static void for_each_popup(struct sway_view *view, + wlr_surface_iterator_func_t iterator, void *user_data) { + if (xdg_shell_v6_view_from_view(view) == NULL) { + return; + } + wlr_xdg_surface_v6_for_each_popup(view->wlr_xdg_surface_v6, iterator, + user_data); +} + static void _close(struct sway_view *view) { if (xdg_shell_v6_view_from_view(view) == NULL) { return; @@ -215,6 +224,7 @@ static const struct sway_view_impl view_impl = { .set_fullscreen = set_fullscreen, .wants_floating = wants_floating, .for_each_surface = for_each_surface, + .for_each_popup = for_each_popup, .close = _close, .close_popups = close_popups, .destroy = destroy, diff --git a/sway/input/cursor.c b/sway/input/cursor.c index d6fdc1da..79f6ec46 100644 --- a/sway/input/cursor.c +++ b/sway/input/cursor.c @@ -109,9 +109,6 @@ static struct sway_container *container_at_coords( } struct sway_container *c; - if ((c = floating_container_at(lx, ly, surface, sx, sy))) { - return c; - } if ((c = container_at(ws, lx, ly, surface, sx, sy))) { return c; } diff --git a/sway/tree/container.c b/sway/tree/container.c index b6ff4d30..b980c5e9 100644 --- a/sway/tree/container.c +++ b/sway/tree/container.c @@ -569,10 +569,15 @@ static struct sway_container *container_at_view(struct sway_container *swayc, *sx = _sx; *sy = _sy; *surface = _surface; + return swayc; } - return swayc; + return NULL; } +static struct sway_container *tiling_container_at( + struct sway_container *con, double lx, double ly, + struct wlr_surface **surface, double *sx, double *sy); + /** * container_at for a container with layout L_TABBED. */ @@ -599,7 +604,7 @@ static struct sway_container *container_at_tabbed(struct sway_container *parent, // Surfaces struct sway_container *current = seat_get_active_child(seat, parent); - return container_at(current, lx, ly, surface, sx, sy); + return tiling_container_at(current, lx, ly, surface, sx, sy); } /** @@ -624,7 +629,7 @@ static struct sway_container *container_at_stacked( // Surfaces struct sway_container *current = seat_get_active_child(seat, parent); - return container_at(current, lx, ly, surface, sx, sy); + return tiling_container_at(current, lx, ly, surface, sx, sy); } /** @@ -642,45 +647,13 @@ static struct sway_container *container_at_linear(struct sway_container *parent, .height = child->height, }; if (wlr_box_contains_point(&box, lx, ly)) { - return container_at(child, lx, ly, surface, sx, sy); + return tiling_container_at(child, lx, ly, surface, sx, sy); } } return NULL; } -struct sway_container *container_at(struct sway_container *parent, - double lx, double ly, - struct wlr_surface **surface, double *sx, double *sy) { - if (!sway_assert(parent->type >= C_WORKSPACE, - "Expected workspace or deeper")) { - return NULL; - } - if (parent->type == C_VIEW) { - return container_at_view(parent, lx, ly, surface, sx, sy); - } - if (!parent->children->length) { - return NULL; - } - - switch (parent->layout) { - case L_HORIZ: - case L_VERT: - return container_at_linear(parent, lx, ly, surface, sx, sy); - case L_TABBED: - return container_at_tabbed(parent, lx, ly, surface, sx, sy); - case L_STACKED: - return container_at_stacked(parent, lx, ly, surface, sx, sy); - case L_FLOATING: - sway_assert(false, "Didn't expect to see floating here"); - return NULL; - case L_NONE: - return NULL; - } - - return NULL; -} - -struct sway_container *floating_container_at(double lx, double ly, +static struct sway_container *floating_container_at(double lx, double ly, struct wlr_surface **surface, double *sx, double *sy) { for (int i = 0; i < root_container.children->length; ++i) { struct sway_container *output = root_container.children->items[i]; @@ -702,7 +675,8 @@ struct sway_container *floating_container_at(double lx, double ly, .height = floater->height, }; if (wlr_box_contains_point(&box, lx, ly)) { - return container_at(floater, lx, ly, surface, sx, sy); + return tiling_container_at(floater, lx, ly, + surface, sx, sy); } } } @@ -710,6 +684,90 @@ struct sway_container *floating_container_at(double lx, double ly, return NULL; } +static struct sway_container *tiling_container_at( + struct sway_container *con, double lx, double ly, + struct wlr_surface **surface, double *sx, double *sy) { + if (con->type == C_VIEW) { + return container_at_view(con, lx, ly, surface, sx, sy); + } + if (!con->children->length) { + return NULL; + } + + switch (con->layout) { + case L_HORIZ: + case L_VERT: + return container_at_linear(con, lx, ly, surface, sx, sy); + case L_TABBED: + return container_at_tabbed(con, lx, ly, surface, sx, sy); + case L_STACKED: + return container_at_stacked(con, lx, ly, surface, sx, sy); + case L_FLOATING: + sway_assert(false, "Didn't expect to see floating here"); + return NULL; + case L_NONE: + return NULL; + } + return NULL; +} + +static bool surface_is_popup(struct wlr_surface *surface) { + if (wlr_surface_is_xdg_surface(surface)) { + struct wlr_xdg_surface *xdg_surface = + wlr_xdg_surface_from_wlr_surface(surface); + while (xdg_surface) { + if (xdg_surface->role == WLR_XDG_SURFACE_ROLE_POPUP) { + return true; + } + xdg_surface = xdg_surface->toplevel->parent; + } + return false; + } + + if (wlr_surface_is_xdg_surface_v6(surface)) { + struct wlr_xdg_surface_v6 *xdg_surface_v6 = + wlr_xdg_surface_v6_from_wlr_surface(surface); + while (xdg_surface_v6) { + if (xdg_surface_v6->role == WLR_XDG_SURFACE_V6_ROLE_POPUP) { + return true; + } + xdg_surface_v6 = xdg_surface_v6->toplevel->parent; + } + return false; + } + + return false; +} + +struct sway_container *container_at(struct sway_container *workspace, + double lx, double ly, + struct wlr_surface **surface, double *sx, double *sy) { + if (!sway_assert(workspace->type == C_WORKSPACE, "Expected a workspace")) { + return NULL; + } + struct sway_container *c; + // Focused view's popups + struct sway_seat *seat = input_manager_current_seat(input_manager); + struct sway_container *focus = + seat_get_focus_inactive(seat, &root_container); + if (focus && focus->type == C_VIEW) { + container_at_view(focus, lx, ly, surface, sx, sy); + if (*surface && surface_is_popup(*surface)) { + return focus; + } + *surface = NULL; + } + // Floating + if ((c = floating_container_at(lx, ly, surface, sx, sy))) { + return c; + } + // Tiling + if ((c = tiling_container_at(workspace, lx, ly, surface, sx, sy))) { + return c; + } + return NULL; +} + void container_for_each_descendant_dfs(struct sway_container *container, void (*f)(struct sway_container *container, void *data), void *data) { diff --git a/sway/tree/view.c b/sway/tree/view.c index 27a6a8bd..97318daa 100644 --- a/sway/tree/view.c +++ b/sway/tree/view.c @@ -339,6 +339,16 @@ void view_for_each_surface(struct sway_view *view, } } +void view_for_each_popup(struct sway_view *view, + wlr_surface_iterator_func_t iterator, void *user_data) { + if (!view->surface) { + return; + } + if (view->impl->for_each_popup) { + view->impl->for_each_popup(view, iterator, user_data); + } +} + static void view_subsurface_create(struct sway_view *view, struct wlr_subsurface *subsurface); From 787f08da7ff9ecb325def2e3a44dc5043d7e2753 Mon Sep 17 00:00:00 2001 From: Ryan Dwyer Date: Thu, 2 Aug 2018 23:34:26 +1000 Subject: [PATCH 146/148] Convert toplevel coordinates to output-local --- sway/desktop/render.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/sway/desktop/render.c b/sway/desktop/render.c index 1f374740..cdac9c72 100644 --- a/sway/desktop/render.c +++ b/sway/desktop/render.c @@ -194,7 +194,8 @@ static void render_view_toplevels(struct sway_view *view, }; // Render all toplevels without descending into popups output_surface_for_each_surface(output, view->surface, - view->swayc->current.view_x, view->swayc->current.view_y, + view->swayc->current.view_x - output->wlr_output->lx, + view->swayc->current.view_y - output->wlr_output->ly, render_surface_iterator, &data); } From e72f86784c6e2750668b3361269ded4d08d94ead Mon Sep 17 00:00:00 2001 From: minus Date: Thu, 2 Aug 2018 23:19:01 +0200 Subject: [PATCH 147/148] Fix crash on mouse motion on fullscreen container container_at expects a workspace, not the fullscreened container. Fixes #2409 --- sway/input/cursor.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/sway/input/cursor.c b/sway/input/cursor.c index 79f6ec46..99858d93 100644 --- a/sway/input/cursor.c +++ b/sway/input/cursor.c @@ -99,8 +99,7 @@ static struct sway_container *container_at_coords( return ws; } if (ws->sway_workspace->fullscreen) { - return container_at(ws->sway_workspace->fullscreen, lx, ly, - surface, sx, sy); + return container_at(ws, lx, ly, surface, sx, sy); } if ((*surface = layer_surface_at(output, &output->layers[ZWLR_LAYER_SHELL_V1_LAYER_TOP], From b3365645114b17b9a5d3efe6e0e81758cd8c47b5 Mon Sep 17 00:00:00 2001 From: emersion Date: Thu, 2 Aug 2018 22:48:43 +0100 Subject: [PATCH 148/148] Fix pointer events for fullscreen views --- include/sway/tree/container.h | 4 ++++ sway/input/cursor.c | 3 ++- sway/tree/container.c | 2 +- 3 files changed, 7 insertions(+), 2 deletions(-) diff --git a/include/sway/tree/container.h b/include/sway/tree/container.h index 12ff8a5a..c3942e9e 100644 --- a/include/sway/tree/container.h +++ b/include/sway/tree/container.h @@ -234,6 +234,10 @@ struct sway_container *container_at(struct sway_container *workspace, double lx, double ly, struct wlr_surface **surface, double *sx, double *sy); +struct sway_container *container_at_view(struct sway_container *view, + double lx, double ly, struct wlr_surface **surface, + double *sx, double *sy); + /** * Apply the function for each descendant of the container breadth first. */ diff --git a/sway/input/cursor.c b/sway/input/cursor.c index 99858d93..c2fc4e9e 100644 --- a/sway/input/cursor.c +++ b/sway/input/cursor.c @@ -99,7 +99,8 @@ static struct sway_container *container_at_coords( return ws; } if (ws->sway_workspace->fullscreen) { - return container_at(ws, lx, ly, surface, sx, sy); + return container_at_view(ws->sway_workspace->fullscreen, lx, ly, + surface, sx, sy); } if ((*surface = layer_surface_at(output, &output->layers[ZWLR_LAYER_SHELL_V1_LAYER_TOP], diff --git a/sway/tree/container.c b/sway/tree/container.c index b980c5e9..46c54e2d 100644 --- a/sway/tree/container.c +++ b/sway/tree/container.c @@ -535,7 +535,7 @@ struct sway_container *container_parent(struct sway_container *container, return container; } -static struct sway_container *container_at_view(struct sway_container *swayc, +struct sway_container *container_at_view(struct sway_container *swayc, double lx, double ly, struct wlr_surface **surface, double *sx, double *sy) { if (!sway_assert(swayc->type == C_VIEW, "Expected a view")) {