diff options
40 files changed, 314 insertions, 124 deletions
@@ -202,6 +202,11 @@ bindsym $mod+r mode "resize" # Read `man 5 sway-bar` for more information about this section. bar { position top + + # When the status_command prints a new line to stdout, swaybar updates. + # The default just shows the current date and time. + status_command while date +'%Y-%m-%d %l:%M:%S %p'; do sleep 1; done + colors { statusline #ffffff background #323232 diff --git a/include/sway/commands.h b/include/sway/commands.h index f7fafb96..48228a98 100644 --- a/include/sway/commands.h +++ b/include/sway/commands.h @@ -103,6 +103,7 @@ sway_cmd cmd_bar; sway_cmd cmd_bindcode; sway_cmd cmd_bindsym; sway_cmd cmd_border; +sway_cmd cmd_client_noop; sway_cmd cmd_client_focused; sway_cmd cmd_client_focused_inactive; sway_cmd cmd_client_unfocused; @@ -153,6 +154,7 @@ sway_cmd cmd_new_window; sway_cmd cmd_no_focus; sway_cmd cmd_output; sway_cmd cmd_permit; +sway_cmd cmd_popup_during_fullscreen; sway_cmd cmd_reject; sway_cmd cmd_reload; sway_cmd cmd_rename; diff --git a/include/sway/config.h b/include/sway/config.h index 02ace979..00b5f25b 100644 --- a/include/sway/config.h +++ b/include/sway/config.h @@ -35,7 +35,6 @@ enum binding_flags { 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 - BINDING_RELOAD=32, // the binding runs the reload command }; /** @@ -257,6 +256,12 @@ enum edge_border_types { E_SMART_NO_GAPS, /**< hide both if one window and gaps to edge is zero */ }; +enum sway_popup_during_fullscreen { + POPUP_SMART, + POPUP_IGNORE, + POPUP_LEAVE, +}; + enum command_context { CONTEXT_CONFIG = 1, CONTEXT_BINDING = 2, @@ -356,6 +361,7 @@ struct sway_config { bool pango_markup; size_t urgent_timeout; enum sway_fowa focus_on_window_activation; + enum sway_popup_during_fullscreen popup_during_fullscreen; // Flags bool focus_follows_mouse; diff --git a/include/sway/tree/container.h b/include/sway/tree/container.h index da6592b4..920ef038 100644 --- a/include/sway/tree/container.h +++ b/include/sway/tree/container.h @@ -292,4 +292,7 @@ bool sway_dir_to_wlr(enum movement_direction dir, enum wlr_direction *out); struct sway_container *container_split(struct sway_container *child, enum sway_container_layout layout); +bool container_is_transient_for(struct sway_container *child, + struct sway_container *ancestor); + #endif diff --git a/include/sway/tree/view.h b/include/sway/tree/view.h index eed3d13d..870ef2e0 100644 --- a/include/sway/tree/view.h +++ b/include/sway/tree/view.h @@ -49,6 +49,8 @@ struct sway_view_impl { 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); + bool (*is_transient_for)(struct sway_view *child, + struct sway_view *ancestor); void (*close)(struct sway_view *view); void (*close_popups)(struct sway_view *view); void (*destroy)(struct sway_view *view); @@ -399,4 +401,6 @@ void view_remove_saved_buffer(struct sway_view *view); void view_save_buffer(struct sway_view *view); +bool view_is_transient_for(struct sway_view *child, struct sway_view *ancestor); + #endif diff --git a/sway/commands.c b/sway/commands.c index 5dd27f7e..8db1df01 100644 --- a/sway/commands.c +++ b/sway/commands.c @@ -77,8 +77,10 @@ static struct cmd_handler handlers[] = { { "bar", cmd_bar }, { "bindcode", cmd_bindcode }, { "bindsym", cmd_bindsym }, + { "client.background", cmd_client_noop }, { "client.focused", cmd_client_focused }, { "client.focused_inactive", cmd_client_focused_inactive }, + { "client.placeholder", cmd_client_noop }, { "client.unfocused", cmd_client_unfocused }, { "client.urgent", cmd_client_urgent }, { "default_border", cmd_default_border }, @@ -107,6 +109,7 @@ static struct cmd_handler handlers[] = { { "new_window", cmd_default_border }, { "no_focus", cmd_no_focus }, { "output", cmd_output }, + { "popup_during_fullscreen", cmd_popup_during_fullscreen }, { "raise_floating", cmd_raise_floating }, { "seat", cmd_seat }, { "set", cmd_set }, diff --git a/sway/commands/bar.c b/sway/commands/bar.c index f760888e..03f4c557 100644 --- a/sway/commands/bar.c +++ b/sway/commands/bar.c @@ -46,14 +46,14 @@ struct cmd_results *cmd_bar(int argc, char **argv) { return error; } - if (!config->reading) { - if (!find_handler(argv[0], bar_config_handlers, - sizeof(bar_config_handlers))) { - return cmd_results_new(CMD_FAILURE, "bar", - "Can only be used in config file."); + if (find_handler(argv[0], bar_config_handlers, + sizeof(bar_config_handlers))) { + if (config->reading) { + return config_subcommand(argv, argc, bar_config_handlers, + sizeof(bar_config_handlers)); } - return config_subcommand(argv, argc, bar_config_handlers, - sizeof(bar_config_handlers)); + return cmd_results_new(CMD_FAILURE, "bar", + "Can only be used in config file."); } if (argc > 1) { diff --git a/sway/commands/bar/status_command.c b/sway/commands/bar/status_command.c index 6f6f81a3..5b4fdc87 100644 --- a/sway/commands/bar/status_command.c +++ b/sway/commands/bar/status_command.c @@ -13,8 +13,20 @@ struct cmd_results *bar_cmd_status_command(int argc, char **argv) { "status_command", "No bar defined."); } free(config->current_bar->status_command); - config->current_bar->status_command = join_args(argv, argc); - wlr_log(WLR_DEBUG, "Feeding bar with status command: %s", - config->current_bar->status_command); + config->current_bar->status_command = NULL; + + char *new_command = join_args(argv, argc); + if (strcmp(new_command, "-") != 0) { + config->current_bar->status_command = new_command; + wlr_log(WLR_DEBUG, "Feeding bar with status command: %s", + config->current_bar->status_command); + } else { + free(new_command); + } + + if (config->active && !config->validating) { + load_swaybars(); + } + return cmd_results_new(CMD_SUCCESS, NULL, NULL); } diff --git a/sway/commands/bind.c b/sway/commands/bind.c index 820c2a6a..701d9746 100644 --- a/sway/commands/bind.c +++ b/sway/commands/bind.c @@ -30,33 +30,6 @@ 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 @@ -214,9 +187,6 @@ static struct cmd_results *cmd_bindsym_or_bindcode(int argc, char **argv, } binding->command = join_args(argv + 1, argc - 1); - if (strcasestr(binding->command, "reload")) { - binding->flags |= BINDING_RELOAD; - } list_t *split = split_string(argv[0], "+"); for (int i = 0; i < split->length; ++i) { @@ -306,31 +276,16 @@ struct cmd_results *cmd_bindcode(int argc, char **argv) { * 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); - - struct sway_binding *binding_copy = binding; - // 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 (binding->flags & BINDING_RELOAD) { - binding_copy = sway_binding_dup(binding); - if (!binding_copy) { - wlr_log(WLR_ERROR, "Failed to duplicate binding during reload"); - return; - } - } + 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, NULL); if (results->status == CMD_SUCCESS) { - ipc_event_binding(binding_copy); + ipc_event_binding(binding); } else { wlr_log(WLR_DEBUG, "could not run command for binding: %s (%s)", binding->command, results->error); } - if (binding_copy->flags & BINDING_RELOAD) { - free_sway_binding(binding_copy); - } free_cmd_results(results); } diff --git a/sway/commands/client.c b/sway/commands/client.c index 6e5f08a2..9f54fa94 100644 --- a/sway/commands/client.c +++ b/sway/commands/client.c @@ -64,27 +64,27 @@ static struct cmd_results *handle_command(int argc, char **argv, if (!parse_color_float(argv[0], class->border)) { return cmd_results_new(CMD_INVALID, cmd_name, - "Unable to parse border color"); + "Unable to parse border color '%s'", argv[0]); } if (!parse_color_float(argv[1], class->background)) { return cmd_results_new(CMD_INVALID, cmd_name, - "Unable to parse background color"); + "Unable to parse background color '%s'", argv[1]); } if (!parse_color_float(argv[2], class->text)) { return cmd_results_new(CMD_INVALID, cmd_name, - "Unable to parse text color"); + "Unable to parse text color '%s'", argv[2]); } if (!parse_color_float(argv[3], class->indicator)) { return cmd_results_new(CMD_INVALID, cmd_name, - "Unable to parse indicator color"); + "Unable to parse indicator color '%s'", argv[3]); } if (!parse_color_float(argv[4], class->child_border)) { return cmd_results_new(CMD_INVALID, cmd_name, - "Unable to parse child border color"); + "Unable to parse child border color '%s'", argv[4]); } if (config->active) { @@ -114,3 +114,8 @@ struct cmd_results *cmd_client_unfocused(int argc, char **argv) { struct cmd_results *cmd_client_urgent(int argc, char **argv) { return handle_command(argc, argv, &config->border_colors.urgent, "client.urgent"); } + +struct cmd_results *cmd_client_noop(int argc, char **argv) { + wlr_log(WLR_INFO, "Warning: %s is ignored by sway", argv[-1]); + return cmd_results_new(CMD_SUCCESS, NULL, NULL); +} diff --git a/sway/commands/opacity.c b/sway/commands/opacity.c index 9cdaad7f..4e4fc994 100644 --- a/sway/commands/opacity.c +++ b/sway/commands/opacity.c @@ -21,6 +21,10 @@ struct cmd_results *cmd_opacity(int argc, char **argv) { struct sway_container *con = config->handler_context.container; + if (con == NULL) { + return cmd_results_new(CMD_FAILURE, "opacity", "No current container"); + } + float opacity = 0.0f; if (!parse_opacity(argv[0], &opacity)) { diff --git a/sway/commands/popup_during_fullscreen.c b/sway/commands/popup_during_fullscreen.c new file mode 100644 index 00000000..da1904b6 --- /dev/null +++ b/sway/commands/popup_during_fullscreen.c @@ -0,0 +1,25 @@ +#include <strings.h> +#include "sway/commands.h" +#include "sway/config.h" + +struct cmd_results *cmd_popup_during_fullscreen(int argc, char **argv) { + struct cmd_results *error = NULL; + if ((error = checkarg(argc, "popup_during_fullscreen", + EXPECTED_EQUAL_TO, 1))) { + return error; + } + + if (strcasecmp(argv[0], "smart") == 0) { + config->popup_during_fullscreen = POPUP_SMART; + } else if (strcasecmp(argv[0], "ignore") == 0) { + config->popup_during_fullscreen = POPUP_IGNORE; + } else if (strcasecmp(argv[0], "leave_fullscreen") == 0) { + config->popup_during_fullscreen = POPUP_LEAVE; + } else { + return cmd_results_new(CMD_INVALID, "popup_during_fullscreen", + "Expected " + "'popup_during_fullscreen smart|ignore|leave_fullscreen'"); + } + + return cmd_results_new(CMD_SUCCESS, NULL, NULL); +} diff --git a/sway/commands/reload.c b/sway/commands/reload.c index 36fb9092..9e136d48 100644 --- a/sway/commands/reload.c +++ b/sway/commands/reload.c @@ -3,15 +3,12 @@ #include "sway/commands.h" #include "sway/config.h" #include "sway/ipc-server.h" +#include "sway/server.h" #include "sway/tree/arrange.h" #include "list.h" +#include "log.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; - } - +static void do_reload(void *data) { // 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) { @@ -20,9 +17,12 @@ struct cmd_results *cmd_reload(int argc, char **argv) { } if (!load_main_config(config->current_config_path, true, false)) { - return cmd_results_new(CMD_FAILURE, "reload", - "Error(s) reloading config."); + wlr_log(WLR_ERROR, "Error(s) reloading config"); + list_foreach(bar_ids, free); + list_free(bar_ids); + return; } + ipc_event_workspace(NULL, NULL, "reload"); load_swaybars(); @@ -37,12 +37,26 @@ struct cmd_results *cmd_reload(int argc, char **argv) { } } - for (int i = 0; i < bar_ids->length; ++i) { - free(bar_ids->items[i]); - } + list_foreach(bar_ids, free); list_free(bar_ids); arrange_root(); +} + +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; + } + + if (!load_main_config(config->current_config_path, true, true)) { + return cmd_results_new(CMD_FAILURE, "reload", + "Error(s) reloading config."); + } + + // The reload command frees a lot of stuff, so to avoid use-after-frees + // we schedule the reload to happen using an idle event. + wl_event_loop_add_idle(server.wl_event_loop, do_reload, NULL); return cmd_results_new(CMD_SUCCESS, NULL, NULL); } diff --git a/sway/commands/seat/attach.c b/sway/commands/seat/attach.c index 3e771c00..6b4bcf1f 100644 --- a/sway/commands/seat/attach.c +++ b/sway/commands/seat/attach.c @@ -23,6 +23,8 @@ struct cmd_results *seat_cmd_attach(int argc, char **argv) { new_attachment->identifier = strdup(argv[0]); list_add(new_config->attachments, new_attachment); - apply_seat_config(new_config); + if (!config->validating) { + apply_seat_config(new_config); + } return cmd_results_new(CMD_SUCCESS, NULL, NULL); } diff --git a/sway/commands/seat/fallback.c b/sway/commands/seat/fallback.c index 56feaab5..11f5a08c 100644 --- a/sway/commands/seat/fallback.c +++ b/sway/commands/seat/fallback.c @@ -27,6 +27,8 @@ struct cmd_results *seat_cmd_fallback(int argc, char **argv) { "Expected 'fallback <true|false>'"); } - apply_seat_config(new_config); + if (!config->validating) { + apply_seat_config(new_config); + } return cmd_results_new(CMD_SUCCESS, NULL, NULL); } diff --git a/sway/commands/swaybg_command.c b/sway/commands/swaybg_command.c index 36f7fdcd..b184b193 100644 --- a/sway/commands/swaybg_command.c +++ b/sway/commands/swaybg_command.c @@ -9,12 +9,17 @@ struct cmd_results *cmd_swaybg_command(int argc, char **argv) { return error; } - if (config->swaybg_command) { - free(config->swaybg_command); + free(config->swaybg_command); + config->swaybg_command = NULL; + + char *new_command = join_args(argv, argc); + if (strcmp(new_command, "-") != 0) { + config->swaybg_command = new_command; + wlr_log(WLR_DEBUG, "Using custom swaybg command: %s", + config->swaybg_command); + } else { + free(new_command); } - config->swaybg_command = join_args(argv, argc); - wlr_log(WLR_DEBUG, "Using custom swaybg command: %s", - config->swaybg_command); return cmd_results_new(CMD_SUCCESS, NULL, NULL); } diff --git a/sway/commands/swaynag_command.c b/sway/commands/swaynag_command.c index c57a80a6..6c86f1a7 100644 --- a/sway/commands/swaynag_command.c +++ b/sway/commands/swaynag_command.c @@ -9,12 +9,17 @@ struct cmd_results *cmd_swaynag_command(int argc, char **argv) { return error; } - if (config->swaynag_command) { - free(config->swaynag_command); + free(config->swaynag_command); + config->swaynag_command = NULL; + + char *new_command = join_args(argv, argc); + if (strcmp(new_command, "-") != 0) { + config->swaybg_command = new_command; + wlr_log(WLR_DEBUG, "Using custom swaynag command: %s", + config->swaynag_command); + } else { + free(new_command); } - config->swaynag_command = join_args(argv, argc); - wlr_log(WLR_DEBUG, "Using custom swaynag command: %s", - config->swaynag_command); return cmd_results_new(CMD_SUCCESS, NULL, NULL); } diff --git a/sway/commands/urgent.c b/sway/commands/urgent.c index 53c37d4d..4f283c55 100644 --- a/sway/commands/urgent.c +++ b/sway/commands/urgent.c @@ -12,6 +12,9 @@ struct cmd_results *cmd_urgent(int argc, char **argv) { return error; } struct sway_container *container = config->handler_context.container; + if (!container) { + return cmd_results_new(CMD_FAILURE, "urgent", "No current container"); + } if (!container->view) { return cmd_results_new(CMD_INVALID, "urgent", "Only views can be urgent"); diff --git a/sway/config.c b/sway/config.c index b56c4f71..a50e9144 100644 --- a/sway/config.c +++ b/sway/config.c @@ -136,6 +136,8 @@ void free_config(struct sway_config *config) { free(config->floating_scroll_left_cmd); free(config->floating_scroll_right_cmd); free(config->font); + free(config->swaybg_command); + free(config->swaynag_command); free((char *)config->current_config_path); free((char *)config->current_config); free(config); @@ -166,7 +168,7 @@ static void set_color(float dest[static 4], uint32_t color) { } static void config_defaults(struct sway_config *config) { - config->swaynag_command = strdup("swaynag"); + if (!(config->swaynag_command = strdup("swaynag"))) goto cleanup; config->swaynag_config_errors = (struct swaynag_instance){ .args = "--type error " "--message 'There are errors in your config file' " @@ -212,6 +214,7 @@ static void config_defaults(struct sway_config *config) { if (!(config->font = strdup("monospace 10"))) goto cleanup; config->font_height = 17; // height of monospace 10 config->urgent_timeout = 500; + config->popup_during_fullscreen = POPUP_SMART; // floating view config->floating_maximum_width = 0; @@ -240,6 +243,8 @@ static void config_defaults(struct sway_config *config) { if (!(config->active_bar_modifiers = create_list())) goto cleanup; + if (!(config->swaybg_command = strdup("swaybg"))) goto cleanup; + if (!(config->config_chain = create_list())) goto cleanup; config->current_config_path = NULL; config->current_config = NULL; @@ -457,6 +462,12 @@ bool load_main_config(const char *file, bool is_active, bool validating) { success = success && load_config(path, config, &config->swaynag_config_errors); + if (validating) { + free_config(config); + config = old_config; + return success; + } + if (is_active) { for (int i = 0; i < config->output_configs->length; i++) { apply_output_config_to_outputs(config->output_configs->items[i]); diff --git a/sway/config/bar.c b/sway/config/bar.c index 48a632fb..b8695798 100644 --- a/sway/config/bar.c +++ b/sway/config/bar.c @@ -99,10 +99,6 @@ struct bar_config *default_bar_config(void) { if (!(bar->bindings = create_list())) { goto cleanup; } - if (!(bar->status_command = - strdup("while date +'%Y-%m-%d %l:%M:%S %p'; do sleep 1; done"))) { - goto cleanup; - } // set default colors if (!(bar->colors.background = strndup("#000000ff", 9))) { goto cleanup; diff --git a/sway/config/output.c b/sway/config/output.c index 6f337b66..2b041353 100644 --- a/sway/config/output.c +++ b/sway/config/output.c @@ -229,17 +229,16 @@ void apply_output_config(struct output_config *oc, struct sway_output *output) { } } - if (oc && oc->background) { - if (output->bg_pid != 0) { - terminate_swaybg(output->bg_pid); - } - + if (output->bg_pid != 0) { + terminate_swaybg(output->bg_pid); + } + if (oc && oc->background && config->swaybg_command) { wlr_log(WLR_DEBUG, "Setting background for output %d to %s", output_i, oc->background); size_t len = snprintf(NULL, 0, "%s %d \"%s\" %s %s", - config->swaybg_command ? config->swaybg_command : "swaybg", - output_i, oc->background, oc->background_option, + config->swaybg_command, output_i, oc->background, + oc->background_option, oc->background_fallback ? oc->background_fallback : ""); char *command = malloc(len + 1); if (!command) { @@ -247,8 +246,8 @@ void apply_output_config(struct output_config *oc, struct sway_output *output) { return; } snprintf(command, len + 1, "%s %d \"%s\" %s %s", - config->swaybg_command ? config->swaybg_command : "swaybg", - output_i, oc->background, oc->background_option, + config->swaybg_command, output_i, oc->background, + oc->background_option, oc->background_fallback ? oc->background_fallback : ""); wlr_log(WLR_DEBUG, "-> %s", command); @@ -260,6 +259,7 @@ void apply_output_config(struct output_config *oc, struct sway_output *output) { free(command); } } + if (oc) { switch (oc->dpms_state) { case DPMS_ON: @@ -353,4 +353,3 @@ void create_default_output_configs(void) { list_add(config->output_configs, oc); } } - diff --git a/sway/desktop/output.c b/sway/desktop/output.c index cfb5a710..adc1ee10 100644 --- a/sway/desktop/output.c +++ b/sway/desktop/output.c @@ -329,6 +329,14 @@ static void send_frame_done(struct sway_output *output, struct timespec *when) { workspace->current.fullscreen, &data); container_for_each_child(workspace->current.fullscreen, send_frame_done_container_iterator, &data); + for (int i = 0; i < workspace->current.floating->length; ++i) { + struct sway_container *floater = + workspace->current.floating->items[i]; + if (container_is_transient_for(floater, + workspace->current.fullscreen)) { + send_frame_done_container_iterator(floater, &data); + } + } #ifdef HAVE_XWAYLAND send_frame_done_unmanaged(output, &root->xwayland_unmanaged, when); #endif diff --git a/sway/desktop/render.c b/sway/desktop/render.c index c8b08a58..3617da87 100644 --- a/sway/desktop/render.c +++ b/sway/desktop/render.c @@ -961,6 +961,14 @@ void output_render(struct sway_output *output, struct timespec *when, render_container(output, damage, fullscreen_con, fullscreen_con->current.focused); } + + for (int i = 0; i < workspace->current.floating->length; ++i) { + struct sway_container *floater = + workspace->current.floating->items[i]; + if (container_is_transient_for(floater, fullscreen_con)) { + render_floating_container(output, damage, floater); + } + } #ifdef HAVE_XWAYLAND render_unmanaged(output, damage, &root->xwayland_unmanaged); #endif diff --git a/sway/desktop/xdg_shell.c b/sway/desktop/xdg_shell.c index 2e2815c1..46582204 100644 --- a/sway/desktop/xdg_shell.c +++ b/sway/desktop/xdg_shell.c @@ -207,6 +207,21 @@ static void for_each_popup(struct sway_view *view, wlr_xdg_surface_for_each_popup(view->wlr_xdg_surface, iterator, user_data); } +static bool is_transient_for(struct sway_view *child, + struct sway_view *ancestor) { + if (xdg_shell_view_from_view(child) == NULL) { + return false; + } + struct wlr_xdg_surface *surface = child->wlr_xdg_surface; + while (surface) { + if (surface->toplevel->parent == ancestor->wlr_xdg_surface) { + return true; + } + surface = surface->toplevel->parent; + } + return false; +} + static void _close(struct sway_view *view) { if (xdg_shell_view_from_view(view) == NULL) { return; @@ -248,6 +263,7 @@ static const struct sway_view_impl view_impl = { .wants_floating = wants_floating, .for_each_surface = for_each_surface, .for_each_popup = for_each_popup, + .is_transient_for = is_transient_for, .close = _close, .close_popups = close_popups, .destroy = destroy, @@ -410,6 +426,7 @@ static void handle_map(struct wl_listener *listener, void *data) { arrange_workspace(view->container->workspace); } } + transaction_commit_dirty(); xdg_shell_view->commit.notify = handle_commit; diff --git a/sway/desktop/xdg_shell_v6.c b/sway/desktop/xdg_shell_v6.c index e61bd652..165cc7eb 100644 --- a/sway/desktop/xdg_shell_v6.c +++ b/sway/desktop/xdg_shell_v6.c @@ -204,6 +204,21 @@ static void for_each_popup(struct sway_view *view, user_data); } +static bool is_transient_for(struct sway_view *child, + struct sway_view *ancestor) { + if (xdg_shell_v6_view_from_view(child) == NULL) { + return false; + } + struct wlr_xdg_surface_v6 *surface = child->wlr_xdg_surface_v6; + while (surface) { + if (surface->toplevel->parent == ancestor->wlr_xdg_surface_v6) { + return true; + } + surface = surface->toplevel->parent; + } + return false; +} + static void _close(struct sway_view *view) { if (xdg_shell_v6_view_from_view(view) == NULL) { return; @@ -245,6 +260,7 @@ static const struct sway_view_impl view_impl = { .wants_floating = wants_floating, .for_each_surface = for_each_surface, .for_each_popup = for_each_popup, + .is_transient_for = is_transient_for, .close = _close, .close_popups = close_popups, .destroy = destroy, diff --git a/sway/desktop/xwayland.c b/sway/desktop/xwayland.c index 4c710f7e..ebf2131e 100644 --- a/sway/desktop/xwayland.c +++ b/sway/desktop/xwayland.c @@ -15,6 +15,7 @@ #include "sway/tree/arrange.h" #include "sway/tree/container.h" #include "sway/tree/view.h" +#include "sway/tree/workspace.h" static const char *atom_map[ATOM_LAST] = { "_NET_WM_WINDOW_TYPE_NORMAL", @@ -253,6 +254,21 @@ static void handle_set_decorations(struct wl_listener *listener, void *data) { view_update_csd_from_client(view, csd); } +static bool is_transient_for(struct sway_view *child, + struct sway_view *ancestor) { + if (xwayland_view_from_view(child) == NULL) { + return false; + } + struct wlr_xwayland_surface *surface = child->wlr_xwayland_surface; + while (surface) { + if (surface->parent == ancestor->wlr_xwayland_surface) { + return true; + } + surface = surface->parent; + } + return false; +} + static void _close(struct sway_view *view) { if (xwayland_view_from_view(view) == NULL) { return; @@ -276,6 +292,7 @@ static const struct sway_view_impl view_impl = { .set_tiled = set_tiled, .set_fullscreen = set_fullscreen, .wants_floating = wants_floating, + .is_transient_for = is_transient_for, .close = _close, .destroy = destroy, }; diff --git a/sway/input/cursor.c b/sway/input/cursor.c index 331c6c7e..6d57c45f 100644 --- a/sway/input/cursor.c +++ b/sway/input/cursor.c @@ -98,6 +98,18 @@ static struct sway_node *node_at_coords( return NULL; } if (ws->fullscreen) { + // Try transient containers + for (int i = 0; i < ws->floating->length; ++i) { + struct sway_container *floater = ws->floating->items[i]; + if (container_is_transient_for(floater, ws->fullscreen)) { + struct sway_container *con = tiling_container_at( + &floater->node, lx, ly, surface, sx, sy); + if (con) { + return &con->node; + } + } + } + // Try fullscreen container struct sway_container *con = tiling_container_at(&ws->fullscreen->node, lx, ly, surface, sx, sy); if (con) { diff --git a/sway/input/keyboard.c b/sway/input/keyboard.c index 5fc8a806..fb1fe7b5 100644 --- a/sway/input/keyboard.c +++ b/sway/input/keyboard.c @@ -264,31 +264,27 @@ static void handle_keyboard_key(struct wl_listener *listener, void *data) { } // Identify and execute active pressed binding - struct sway_binding *next_repeat_binding = NULL; + struct sway_binding *binding = NULL; if (event->state == WLR_KEY_PRESSED) { - struct sway_binding *binding_pressed = NULL; get_active_binding(&keyboard->state_keycodes, - config->current_mode->keycode_bindings, &binding_pressed, + config->current_mode->keycode_bindings, &binding, code_modifiers, false, input_inhibited); get_active_binding(&keyboard->state_keysyms_translated, - config->current_mode->keysym_bindings, &binding_pressed, + config->current_mode->keysym_bindings, &binding, translated_modifiers, false, input_inhibited); get_active_binding(&keyboard->state_keysyms_raw, - config->current_mode->keysym_bindings, &binding_pressed, + config->current_mode->keysym_bindings, &binding, raw_modifiers, false, input_inhibited); - if (binding_pressed) { - if ((binding_pressed->flags & BINDING_RELOAD) == 0) { - next_repeat_binding = binding_pressed; - } - seat_execute_command(seat, binding_pressed); + if (binding) { + seat_execute_command(seat, binding); handled = true; } } // Set up (or clear) keyboard repeat for a pressed binding - if (next_repeat_binding && wlr_device->keyboard->repeat_info.delay > 0) { - keyboard->repeat_binding = next_repeat_binding; + if (binding && wlr_device->keyboard->repeat_info.delay > 0) { + keyboard->repeat_binding = binding; if (wl_event_source_timer_update(keyboard->key_repeat_source, wlr_device->keyboard->repeat_info.delay) < 0) { wlr_log(WLR_DEBUG, "failed to set key repeat timer"); diff --git a/sway/input/seat.c b/sway/input/seat.c index f5cb2f9e..f418785d 100644 --- a/sway/input/seat.c +++ b/sway/input/seat.c @@ -655,7 +655,10 @@ void seat_set_focus_warp(struct sway_seat *seat, struct sway_node *node, // Deny setting focus to a view which is hidden by a fullscreen container if (new_workspace && new_workspace->fullscreen && container && !container_is_fullscreen_or_child(container)) { - return; + // Unless it's a transient container + if (!container_is_transient_for(container, new_workspace->fullscreen)) { + return; + } } struct sway_output *last_output = last_workspace ? diff --git a/sway/ipc-json.c b/sway/ipc-json.c index 7c5a0a5d..f02f370b 100644 --- a/sway/ipc-json.c +++ b/sway/ipc-json.c @@ -514,8 +514,8 @@ json_object *ipc_json_describe_bar_config(struct bar_config *bar) { json_object_new_string(bar->hidden_state)); json_object_object_add(json, "position", json_object_new_string(bar->position)); - json_object_object_add(json, "status_command", - json_object_new_string(bar->status_command)); + json_object_object_add(json, "status_command", bar->status_command ? + json_object_new_string(bar->status_command) : NULL); json_object_object_add(json, "font", json_object_new_string((bar->font) ? bar->font : config->font)); if (bar->separator_symbol) { diff --git a/sway/meson.build b/sway/meson.build index 8ab28869..c7fc9697 100644 --- a/sway/meson.build +++ b/sway/meson.build @@ -70,6 +70,7 @@ sway_sources = files( 'commands/no_focus.c', 'commands/nop.c', 'commands/output.c', + 'commands/popup_during_fullscreen.c', 'commands/reload.c', 'commands/rename.c', 'commands/resize.c', diff --git a/sway/sway-bar.5.scd b/sway/sway-bar.5.scd index 00b9386e..8c7be8e7 100644 --- a/sway/sway-bar.5.scd +++ b/sway/sway-bar.5.scd @@ -17,6 +17,9 @@ Sway allows configuring swaybar in the sway configuration file. https://i3wm.org/docs/i3bar-protocol.html + If running this command via IPC, you can disable a running status command by + setting the command to a single dash: _swaybar bar bar-0 status\_command -_ + *pango\_markup* enabled|disabled Enables or disables pango markup for status lines. This has no effect on status lines using the i3bar JSON protocol. diff --git a/sway/sway.5.scd b/sway/sway.5.scd index 3fda6cef..28ab15df 100644 --- a/sway/sway.5.scd +++ b/sway/sway.5.scd @@ -71,6 +71,9 @@ The following commands may only be used in the configuration file. Executes custom background _command_. Default is _swaybg_. Refer to *output* below for more information. + It can be disabled by setting the command to a single dash: + _swaybg\_command -_ + *swaynag\_command* <command> Executes custom command for _swaynag_. Default is _swaynag_. Additional arguments may be appended to the end. This should only be used to either @@ -78,6 +81,9 @@ The following commands may only be used in the configuration file. arguments. This should be placed at the top of the config for the best results. + It can be disabled by setting the command to a single dash: + _swaynag\_command -_ + The following commands cannot be used directly in the configuration file. They are expected to be used with *bindsym* or at runtime through *swaymsg*(1). @@ -549,6 +555,12 @@ You may combine output commands into one, like so: You can get a list of output names with *swaymsg -t get\_outputs*. You may also match any output by using the output name "\*". +*popup\_during\_fullscreen* smart|ignore|leave\_fullscreen + Determines what to do when a fullscreen view opens a dialog. + If _smart_ (the default), the dialog will be displayed. If _ignore_, the + dialog will not be rendered. If _leave\_fullscreen_, the view will exit + fullscreen mode and the dialog will be rendered. + *set* $<name> <value> Sets variable $_name_ to _value_. You can use the new variable in the arguments of future commands. When the variable is used, it can be escaped diff --git a/sway/swaynag.c b/sway/swaynag.c index d905db2b..38e74b88 100644 --- a/sway/swaynag.c +++ b/sway/swaynag.c @@ -11,6 +11,10 @@ bool swaynag_spawn(const char *swaynag_command, struct swaynag_instance *swaynag) { + if (!swaynag_command) { + return true; + } + if (swaynag->detailed) { if (pipe(swaynag->fd) != 0) { wlr_log(WLR_ERROR, "Failed to create pipe for swaynag"); @@ -58,6 +62,10 @@ void swaynag_kill(struct swaynag_instance *swaynag) { void swaynag_log(const char *swaynag_command, struct swaynag_instance *swaynag, const char *fmt, ...) { + if (!swaynag_command) { + return; + } + if (!swaynag->detailed) { wlr_log(WLR_ERROR, "Attempting to write to non-detailed swaynag inst"); return; diff --git a/sway/tree/container.c b/sway/tree/container.c index 9db7aed1..1664514a 100644 --- a/sway/tree/container.c +++ b/sway/tree/container.c @@ -1212,3 +1212,10 @@ struct sway_container *container_split(struct sway_container *child, return cont; } + +bool container_is_transient_for(struct sway_container *child, + struct sway_container *ancestor) { + return config->popup_during_fullscreen == POPUP_SMART && + child->view && ancestor->view && + view_is_transient_for(child->view, ancestor->view); +} diff --git a/sway/tree/view.c b/sway/tree/view.c index ae73a687..1f00452d 100644 --- a/sway/tree/view.c +++ b/sway/tree/view.c @@ -576,6 +576,16 @@ void view_map(struct sway_view *view, struct wlr_surface *wlr_surface) { view_set_tiled(view, true); } + if (config->popup_during_fullscreen == POPUP_LEAVE && + view->container->workspace && + view->container->workspace->fullscreen && + view->container->workspace->fullscreen->view) { + struct sway_container *fs = view->container->workspace->fullscreen; + if (view_is_transient_for(view, fs->view)) { + container_set_fullscreen(fs, false); + } + } + if (should_focus(view)) { input_manager_set_focus(input_manager, &view->container->node); } @@ -1080,7 +1090,12 @@ bool view_is_visible(struct sway_view *view) { // Check view isn't hidden by another fullscreen view if (workspace->fullscreen && !container_is_fullscreen_or_child(view->container)) { - return false; + // However, if we're transient for the fullscreen view and we allow + // "popups" during fullscreen then it might be visible + if (!container_is_transient_for(view->container, + workspace->fullscreen)) { + return false; + } } return true; } @@ -1133,3 +1148,9 @@ void view_save_buffer(struct sway_view *view) { view->saved_buffer_height = view->surface->current.height; } } + +bool view_is_transient_for(struct sway_view *child, + struct sway_view *ancestor) { + return child->impl->is_transient_for && + child->impl->is_transient_for(child, ancestor); +} diff --git a/swaybar/bar.c b/swaybar/bar.c index c86e71b8..3990f1ca 100644 --- a/swaybar/bar.c +++ b/swaybar/bar.c @@ -572,8 +572,8 @@ void bar_run(struct swaybar *bar) { add_event(bar->status->read_fd, POLLIN, status_in, bar); } while (1) { - event_loop_poll(); wl_display_flush(bar->display); + event_loop_poll(); } } diff --git a/swaynag/main.c b/swaynag/main.c index d1a0d236..bae3c0e2 100644 --- a/swaynag/main.c +++ b/swaynag/main.c @@ -36,7 +36,7 @@ int main(int argc, char **argv) { swaynag.details.button_details = calloc(sizeof(struct swaynag_button), 1); - swaynag.details.button_details->text = strdup("Toggle Details"); + swaynag.details.button_details->text = strdup("Toggle details"); swaynag.details.button_details->type = SWAYNAG_ACTION_EXPAND; char *config_path = NULL; diff --git a/swaynag/swaynag.1.scd b/swaynag/swaynag.1.scd index 1c395aee..bb69e47d 100644 --- a/swaynag/swaynag.1.scd +++ b/swaynag/swaynag.1.scd @@ -40,7 +40,7 @@ _swaynag_ [options...] *-L, --detailed-button* <text> 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_. + there is not a detailed message. The default is _Toggle details_. *-m, --message* <msg> Set the message text. diff --git a/swaynag/swaynag.c b/swaynag/swaynag.c index 26d3589e..69da851e 100644 --- a/swaynag/swaynag.c +++ b/swaynag/swaynag.c @@ -390,6 +390,10 @@ void swaynag_run(struct swaynag *swaynag) { && wl_display_dispatch(swaynag->display) != -1) { // This is intentionally left blank } + + if (swaynag->display) { + wl_display_disconnect(swaynag->display); + } } void swaynag_destroy(struct swaynag *swaynag) { @@ -449,8 +453,4 @@ void swaynag_destroy(struct swaynag *swaynag) { if (swaynag->shm) { wl_shm_destroy(swaynag->shm); } - - if (swaynag->display) { - wl_display_disconnect(swaynag->display); - } } |