aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--config.in5
-rw-r--r--include/sway/commands.h2
-rw-r--r--include/sway/config.h8
-rw-r--r--include/sway/tree/container.h3
-rw-r--r--include/sway/tree/view.h4
-rw-r--r--sway/commands.c3
-rw-r--r--sway/commands/bar.c14
-rw-r--r--sway/commands/bar/status_command.c18
-rw-r--r--sway/commands/bind.c49
-rw-r--r--sway/commands/client.c15
-rw-r--r--sway/commands/opacity.c4
-rw-r--r--sway/commands/popup_during_fullscreen.c25
-rw-r--r--sway/commands/reload.c36
-rw-r--r--sway/commands/seat/attach.c4
-rw-r--r--sway/commands/seat/fallback.c4
-rw-r--r--sway/commands/swaybg_command.c15
-rw-r--r--sway/commands/swaynag_command.c15
-rw-r--r--sway/commands/urgent.c3
-rw-r--r--sway/config.c13
-rw-r--r--sway/config/bar.c4
-rw-r--r--sway/config/output.c19
-rw-r--r--sway/desktop/output.c8
-rw-r--r--sway/desktop/render.c8
-rw-r--r--sway/desktop/xdg_shell.c17
-rw-r--r--sway/desktop/xdg_shell_v6.c16
-rw-r--r--sway/desktop/xwayland.c17
-rw-r--r--sway/input/cursor.c12
-rw-r--r--sway/input/keyboard.c20
-rw-r--r--sway/input/seat.c5
-rw-r--r--sway/ipc-json.c4
-rw-r--r--sway/meson.build1
-rw-r--r--sway/sway-bar.5.scd3
-rw-r--r--sway/sway.5.scd12
-rw-r--r--sway/swaynag.c8
-rw-r--r--sway/tree/container.c7
-rw-r--r--sway/tree/view.c23
-rw-r--r--swaybar/bar.c2
-rw-r--r--swaynag/main.c2
-rw-r--r--swaynag/swaynag.1.scd2
-rw-r--r--swaynag/swaynag.c8
40 files changed, 314 insertions, 124 deletions
diff --git a/config.in b/config.in
index 124f825d..72523816 100644
--- a/config.in
+++ b/config.in
@@ -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);
- }
}