summaryrefslogtreecommitdiff
path: root/sway
diff options
context:
space:
mode:
Diffstat (limited to 'sway')
-rw-r--r--sway/commands.c1
-rw-r--r--sway/commands/bar/hidden_state.c2
-rw-r--r--sway/commands/bar/mode.c2
-rw-r--r--sway/commands/client.c16
-rw-r--r--sway/commands/exec_always.c2
-rw-r--r--sway/commands/focus.c8
-rw-r--r--sway/commands/move.c4
-rw-r--r--sway/commands/output.c7
-rw-r--r--sway/commands/output/render_bit_depth.c29
-rw-r--r--sway/commands/smart_gaps.c7
-rw-r--r--sway/commands/swap.c5
-rw-r--r--sway/config.c4
-rw-r--r--sway/config/bar.c2
-rw-r--r--sway/config/output.c46
-rw-r--r--sway/desktop/layer_shell.c29
-rw-r--r--sway/desktop/output.c58
-rw-r--r--sway/desktop/render.c34
-rw-r--r--sway/desktop/transaction.c2
-rw-r--r--sway/desktop/xdg_shell.c4
-rw-r--r--sway/desktop/xwayland.c2
-rw-r--r--sway/input/cursor.c47
-rw-r--r--sway/input/seat.c15
-rw-r--r--sway/input/seatop_resize_floating.c18
-rw-r--r--sway/ipc-server.c2
-rw-r--r--sway/main.c40
-rw-r--r--sway/meson.build1
-rw-r--r--sway/server.c47
-rw-r--r--sway/sway-output.5.scd15
-rw-r--r--sway/sway.5.scd17
-rw-r--r--sway/swaynag.c2
-rw-r--r--sway/tree/container.c125
-rw-r--r--sway/tree/output.c15
-rw-r--r--sway/tree/root.c8
-rw-r--r--sway/tree/view.c43
-rw-r--r--sway/tree/workspace.c46
35 files changed, 544 insertions, 161 deletions
diff --git a/sway/commands.c b/sway/commands.c
index 205406ad..5a1fd32e 100644
--- a/sway/commands.c
+++ b/sway/commands.c
@@ -51,6 +51,7 @@ static const struct cmd_handler handlers[] = {
{ "client.background", cmd_client_noop },
{ "client.focused", cmd_client_focused },
{ "client.focused_inactive", cmd_client_focused_inactive },
+ { "client.focused_tab_title", cmd_client_focused_tab_title },
{ "client.placeholder", cmd_client_noop },
{ "client.unfocused", cmd_client_unfocused },
{ "client.urgent", cmd_client_urgent },
diff --git a/sway/commands/bar/hidden_state.c b/sway/commands/bar/hidden_state.c
index 1f08a5d2..8b661e3a 100644
--- a/sway/commands/bar/hidden_state.c
+++ b/sway/commands/bar/hidden_state.c
@@ -54,7 +54,7 @@ struct cmd_results *bar_cmd_hidden_state(int argc, char **argv) {
}
const char *state = argv[0];
- if (config->reading) {
+ if (config->current_bar) {
error = bar_set_hidden_state(config->current_bar, state);
} else {
const char *id = argc == 2 ? argv[1] : NULL;
diff --git a/sway/commands/bar/mode.c b/sway/commands/bar/mode.c
index 8b3fb275..7c2f423b 100644
--- a/sway/commands/bar/mode.c
+++ b/sway/commands/bar/mode.c
@@ -58,7 +58,7 @@ struct cmd_results *bar_cmd_mode(int argc, char **argv) {
}
const char *mode = argv[0];
- if (config->reading) {
+ if (config->current_bar) {
error = bar_set_mode(config->current_bar, mode);
} else {
const char *id = argc == 2 ? argv[1] : NULL;
diff --git a/sway/commands/client.c b/sway/commands/client.c
index dd0694df..77263145 100644
--- a/sway/commands/client.c
+++ b/sway/commands/client.c
@@ -18,6 +18,12 @@ static struct cmd_results *handle_command(int argc, char **argv, char *cmd_name,
return error;
}
+ if (argc > 3 && strcmp(cmd_name, "client.focused_tab_title") == 0) {
+ sway_log(SWAY_ERROR,
+ "Warning: indicator and child_border colors have no effect for %s",
+ cmd_name);
+ }
+
struct border_colors colors = {0};
const char *ind_hex = argc > 3 ? argv[3] : default_indicator;
const char *child_hex = argc > 4 ? argv[4] : argv[1]; // def to background
@@ -80,3 +86,13 @@ struct cmd_results *cmd_client_noop(int argc, char **argv) {
sway_log(SWAY_INFO, "Warning: %s is ignored by sway", argv[-1]);
return cmd_results_new(CMD_SUCCESS, NULL);
}
+
+struct cmd_results *cmd_client_focused_tab_title(int argc, char **argv) {
+ struct cmd_results *result = handle_command(argc, argv,
+ "client.focused_tab_title",
+ &config->border_colors.focused_tab_title, "#2e9ef4ff");
+ if (result && result->status == CMD_SUCCESS) {
+ config->has_focused_tab_title = true;
+ }
+ return result;
+}
diff --git a/sway/commands/exec_always.c b/sway/commands/exec_always.c
index fce337d5..b35065c1 100644
--- a/sway/commands/exec_always.c
+++ b/sway/commands/exec_always.c
@@ -7,6 +7,7 @@
#include <signal.h>
#include "sway/commands.h"
#include "sway/config.h"
+#include "sway/server.h"
#include "sway/tree/container.h"
#include "sway/tree/root.h"
#include "sway/tree/workspace.h"
@@ -53,6 +54,7 @@ struct cmd_results *cmd_exec_process(int argc, char **argv) {
// Fork process
if ((pid = fork()) == 0) {
// Fork child process again
+ restore_nofile_limit();
setsid();
sigset_t set;
sigemptyset(&set);
diff --git a/sway/commands/focus.c b/sway/commands/focus.c
index 6771ca2f..b8d28480 100644
--- a/sway/commands/focus.c
+++ b/sway/commands/focus.c
@@ -267,6 +267,11 @@ static struct cmd_results *focus_mode(struct sway_workspace *ws,
new_focus = seat_get_focus_inactive_tiling(seat, ws);
}
if (new_focus) {
+ struct sway_container *new_focus_view =
+ seat_get_focus_inactive_view(seat, &new_focus->node);
+ if (new_focus_view) {
+ new_focus = new_focus_view;
+ }
seat_set_focus_container(seat, new_focus);
// If we're on the floating layer and the floating container area
@@ -446,7 +451,8 @@ struct cmd_results *cmd_focus(int argc, char **argv) {
return cmd_results_new(CMD_FAILURE, "");
}
struct sway_node *next_focus = NULL;
- if (container_is_floating(container)) {
+ if (container_is_floating(container) &&
+ container->pending.fullscreen_mode == FULLSCREEN_NONE) {
next_focus = node_get_in_direction_floating(container, seat, direction);
} else {
next_focus = node_get_in_direction_tiling(container, seat, direction, descend);
diff --git a/sway/commands/move.c b/sway/commands/move.c
index f2702fa1..1a05a7a6 100644
--- a/sway/commands/move.c
+++ b/sway/commands/move.c
@@ -874,6 +874,10 @@ static struct cmd_results *cmd_move_to_position(int argc, char **argv) {
return cmd_results_new(CMD_INVALID, "Invalid x position specified");
}
+ if (argc < 1) {
+ return cmd_results_new(CMD_FAILURE, expected_position_syntax);
+ }
+
struct movement_amount ly = { .amount = 0, .unit = MOVEMENT_UNIT_INVALID };
// Y direction
num_consumed_args = parse_movement_amount(argc, argv, &ly);
diff --git a/sway/commands/output.c b/sway/commands/output.c
index d8ef2885..125df5a7 100644
--- a/sway/commands/output.c
+++ b/sway/commands/output.c
@@ -18,6 +18,7 @@ static const struct cmd_handler output_handlers[] = {
{ "modeline", output_cmd_modeline },
{ "pos", output_cmd_position },
{ "position", output_cmd_position },
+ { "render_bit_depth", output_cmd_render_bit_depth },
{ "res", output_cmd_mode },
{ "resolution", output_cmd_mode },
{ "scale", output_cmd_scale },
@@ -33,9 +34,9 @@ struct cmd_results *cmd_output(int argc, char **argv) {
return error;
}
- // The NOOP-1 output is a dummy output used when there's no outputs
+ // The HEADLESS-1 output is a dummy output used when there's no outputs
// connected. It should never be configured.
- if (strcasecmp(argv[0], root->noop_output->wlr_output->name) == 0) {
+ if (strcasecmp(argv[0], root->fallback_output->wlr_output->name) == 0) {
return cmd_results_new(CMD_FAILURE,
"Refusing to configure the no op output");
}
@@ -52,7 +53,7 @@ struct cmd_results *cmd_output(int argc, char **argv) {
if (!sway_output) {
return cmd_results_new(CMD_FAILURE, "Unknown output");
}
- if (sway_output == root->noop_output) {
+ if (sway_output == root->fallback_output) {
return cmd_results_new(CMD_FAILURE,
"Refusing to configure the no op output");
}
diff --git a/sway/commands/output/render_bit_depth.c b/sway/commands/output/render_bit_depth.c
new file mode 100644
index 00000000..c419321e
--- /dev/null
+++ b/sway/commands/output/render_bit_depth.c
@@ -0,0 +1,29 @@
+#include <drm_fourcc.h>
+#include <strings.h>
+#include "sway/commands.h"
+#include "sway/config.h"
+
+struct cmd_results *output_cmd_render_bit_depth(int argc, char **argv) {
+ if (!config->handler_context.output_config) {
+ return cmd_results_new(CMD_FAILURE, "Missing output config");
+ }
+ if (!argc) {
+ return cmd_results_new(CMD_INVALID, "Missing bit depth argument.");
+ }
+
+ if (strcmp(*argv, "8") == 0) {
+ config->handler_context.output_config->render_bit_depth =
+ RENDER_BIT_DEPTH_8;
+ } else if (strcmp(*argv, "10") == 0) {
+ config->handler_context.output_config->render_bit_depth =
+ RENDER_BIT_DEPTH_10;
+ } else {
+ return cmd_results_new(CMD_INVALID,
+ "Invalid bit depth. Must be a value in (8|10).");
+ }
+
+ config->handler_context.leftovers.argc = argc - 1;
+ config->handler_context.leftovers.argv = argv + 1;
+ return NULL;
+}
+
diff --git a/sway/commands/smart_gaps.c b/sway/commands/smart_gaps.c
index b27f9ccd..a6d165dc 100644
--- a/sway/commands/smart_gaps.c
+++ b/sway/commands/smart_gaps.c
@@ -15,7 +15,12 @@ struct cmd_results *cmd_smart_gaps(int argc, char **argv) {
return error;
}
- config->smart_gaps = parse_boolean(argv[0], config->smart_gaps);
+ if (strcmp(argv[0], "inverse_outer") == 0) {
+ config->smart_gaps = SMART_GAPS_INVERSE_OUTER;
+ } else {
+ config->smart_gaps = parse_boolean(argv[0], config->smart_gaps)
+ ? SMART_GAPS_ON : SMART_GAPS_OFF;
+ }
arrange_root();
diff --git a/sway/commands/swap.c b/sway/commands/swap.c
index ce5e5128..9355944d 100644
--- a/sway/commands/swap.c
+++ b/sway/commands/swap.c
@@ -126,10 +126,10 @@ void container_swap(struct sway_container *con1, struct sway_container *con2) {
}
enum sway_fullscreen_mode fs1 = con1->pending.fullscreen_mode;
- enum sway_fullscreen_mode fs2 = con2->pending.fullscreen_mode;
if (fs1) {
container_fullscreen_disable(con1);
}
+ enum sway_fullscreen_mode fs2 = con2->pending.fullscreen_mode;
if (fs2) {
container_fullscreen_disable(con2);
}
@@ -247,6 +247,9 @@ struct cmd_results *cmd_swap(int argc, char **argv) {
} else if (!current) {
error = cmd_results_new(CMD_FAILURE,
"Can only swap with containers and views");
+ } else if (current == other) {
+ error = cmd_results_new(CMD_FAILURE,
+ "Cannot swap a container with itself");
} else if (container_has_ancestor(current, other)
|| container_has_ancestor(other, current)) {
error = cmd_results_new(CMD_FAILURE,
diff --git a/sway/config.c b/sway/config.c
index e3daacda..e4745a5c 100644
--- a/sway/config.c
+++ b/sway/config.c
@@ -266,7 +266,7 @@ static void config_defaults(struct sway_config *config) {
config->tiling_drag = true;
config->tiling_drag_threshold = 9;
- config->smart_gaps = false;
+ config->smart_gaps = SMART_GAPS_OFF;
config->gaps_inner = 0;
config->gaps_outer.top = 0;
config->gaps_outer.right = 0;
@@ -290,6 +290,8 @@ static void config_defaults(struct sway_config *config) {
config->hide_edge_borders_smart = ESMART_OFF;
config->hide_lone_tab = false;
+ config->has_focused_tab_title = false;
+
// border colors
color_to_rgba(config->border_colors.focused.border, 0x4C7899FF);
color_to_rgba(config->border_colors.focused.background, 0x285577FF);
diff --git a/sway/config/bar.c b/sway/config/bar.c
index e09add44..d1b342e6 100644
--- a/sway/config/bar.c
+++ b/sway/config/bar.c
@@ -219,6 +219,8 @@ static void invoke_swaybar(struct bar_config *bar) {
sigprocmask(SIG_SETMASK, &set, NULL);
signal(SIGPIPE, SIG_DFL);
+ restore_nofile_limit();
+
pid = fork();
if (pid < 0) {
sway_log_errno(SWAY_ERROR, "fork failed");
diff --git a/sway/config/output.c b/sway/config/output.c
index 8e937b28..fa509252 100644
--- a/sway/config/output.c
+++ b/sway/config/output.c
@@ -1,5 +1,6 @@
#define _POSIX_C_SOURCE 200809L
#include <assert.h>
+#include <drm_fourcc.h>
#include <stdbool.h>
#include <string.h>
#include <sys/socket.h>
@@ -67,6 +68,7 @@ struct output_config *new_output_config(const char *name) {
oc->subpixel = WL_OUTPUT_SUBPIXEL_UNKNOWN;
oc->max_render_time = -1;
oc->adaptive_sync = -1;
+ oc->render_bit_depth = RENDER_BIT_DEPTH_DEFAULT;
return oc;
}
@@ -113,6 +115,9 @@ void merge_output_config(struct output_config *dst, struct output_config *src) {
if (src->adaptive_sync != -1) {
dst->adaptive_sync = src->adaptive_sync;
}
+ if (src->render_bit_depth != RENDER_BIT_DEPTH_DEFAULT) {
+ dst->render_bit_depth = src->render_bit_depth;
+ }
if (src->background) {
free(dst->background);
dst->background = strdup(src->background);
@@ -351,9 +356,26 @@ static int compute_default_scale(struct wlr_output *output) {
return 2;
}
+/* Lists of formats to try, in order, when a specific render bit depth has
+ * been asked for. The second to last format in each list should always
+ * be XRGB8888, as a reliable backup in case the others are not available;
+ * the last should be DRM_FORMAT_INVALID, to indicate the end of the list. */
+static const uint32_t *bit_depth_preferences[] = {
+ [RENDER_BIT_DEPTH_8] = (const uint32_t []){
+ DRM_FORMAT_XRGB8888,
+ DRM_FORMAT_INVALID,
+ },
+ [RENDER_BIT_DEPTH_10] = (const uint32_t []){
+ DRM_FORMAT_XRGB2101010,
+ DRM_FORMAT_XBGR2101010,
+ DRM_FORMAT_XRGB8888,
+ DRM_FORMAT_INVALID,
+ },
+};
+
static void queue_output_config(struct output_config *oc,
struct sway_output *output) {
- if (output == root->noop_output) {
+ if (output == root->fallback_output) {
return;
}
@@ -437,10 +459,26 @@ static void queue_output_config(struct output_config *oc,
oc->adaptive_sync);
wlr_output_enable_adaptive_sync(wlr_output, oc->adaptive_sync == 1);
}
+
+ if (oc && oc->render_bit_depth != RENDER_BIT_DEPTH_DEFAULT) {
+ const uint32_t *fmts = bit_depth_preferences[oc->render_bit_depth];
+ assert(fmts);
+
+ for (size_t i = 0; fmts[i] != DRM_FORMAT_INVALID; i++) {
+ wlr_output_set_render_format(wlr_output, fmts[i]);
+ if (wlr_output_test(wlr_output)) {
+ break;
+ }
+
+ sway_log(SWAY_DEBUG, "Preferred output format 0x%08x "
+ "failed to work, falling back to next in "
+ "list, 0x%08x", fmts[i], fmts[i + 1]);
+ }
+ }
}
bool apply_output_config(struct output_config *oc, struct sway_output *output) {
- if (output == root->noop_output) {
+ if (output == root->fallback_output) {
return false;
}
@@ -535,7 +573,7 @@ bool apply_output_config(struct output_config *oc, struct sway_output *output) {
}
bool test_output_config(struct output_config *oc, struct sway_output *output) {
- if (output == root->noop_output) {
+ if (output == root->fallback_output) {
return false;
}
@@ -750,6 +788,8 @@ static bool _spawn_swaybg(char **command) {
sway_log_errno(SWAY_ERROR, "fork failed");
return false;
} else if (pid == 0) {
+ restore_nofile_limit();
+
pid = fork();
if (pid < 0) {
sway_log_errno(SWAY_ERROR, "fork failed");
diff --git a/sway/desktop/layer_shell.c b/sway/desktop/layer_shell.c
index 7f5a337b..27e457f1 100644
--- a/sway/desktop/layer_shell.c
+++ b/sway/desktop/layer_shell.c
@@ -352,6 +352,8 @@ static void unmap(struct sway_layer_surface *sway_layer) {
sway_layer->layer_surface->surface, true);
}
+static void layer_subsurface_destroy(struct sway_layer_subsurface *subsurface);
+
static void handle_destroy(struct wl_listener *listener, void *data) {
struct sway_layer_surface *sway_layer =
wl_container_of(listener, sway_layer, destroy);
@@ -360,6 +362,12 @@ static void handle_destroy(struct wl_listener *listener, void *data) {
if (sway_layer->layer_surface->mapped) {
unmap(sway_layer);
}
+
+ struct sway_layer_subsurface *subsurface, *subsurface_tmp;
+ wl_list_for_each_safe(subsurface, subsurface_tmp, &sway_layer->subsurfaces, link) {
+ layer_subsurface_destroy(subsurface);
+ }
+
wl_list_remove(&sway_layer->link);
wl_list_remove(&sway_layer->destroy.link);
wl_list_remove(&sway_layer->map.link);
@@ -428,11 +436,8 @@ static void subsurface_handle_commit(struct wl_listener *listener, void *data) {
subsurface_damage(subsurface, false);
}
-static void subsurface_handle_destroy(struct wl_listener *listener,
- void *data) {
- struct sway_layer_subsurface *subsurface =
- wl_container_of(listener, subsurface, destroy);
-
+static void layer_subsurface_destroy(struct sway_layer_subsurface *subsurface) {
+ wl_list_remove(&subsurface->link);
wl_list_remove(&subsurface->map.link);
wl_list_remove(&subsurface->unmap.link);
wl_list_remove(&subsurface->destroy.link);
@@ -440,6 +445,13 @@ static void subsurface_handle_destroy(struct wl_listener *listener,
free(subsurface);
}
+static void subsurface_handle_destroy(struct wl_listener *listener,
+ void *data) {
+ struct sway_layer_subsurface *subsurface =
+ wl_container_of(listener, subsurface, destroy);
+ layer_subsurface_destroy(subsurface);
+}
+
static struct sway_layer_subsurface *create_subsurface(
struct wlr_subsurface *wlr_subsurface,
struct sway_layer_surface *layer_surface) {
@@ -451,6 +463,7 @@ static struct sway_layer_subsurface *create_subsurface(
subsurface->wlr_subsurface = wlr_subsurface;
subsurface->layer_surface = layer_surface;
+ wl_list_insert(&layer_surface->subsurfaces, &subsurface->link);
subsurface->map.notify = subsurface_handle_map;
wl_signal_add(&wlr_subsurface->events.map, &subsurface->map);
@@ -624,7 +637,7 @@ void handle_layer_shell_surface(struct wl_listener *listener, void *data) {
output = ws->output;
}
}
- if (!output || output == root->noop_output) {
+ if (!output || output == root->fallback_output) {
if (!root->outputs->length) {
sway_log(SWAY_ERROR,
"no output to auto-assign layer surface '%s' to",
@@ -643,6 +656,8 @@ void handle_layer_shell_surface(struct wl_listener *listener, void *data) {
return;
}
+ wl_list_init(&sway_layer->subsurfaces);
+
sway_layer->surface_commit.notify = handle_surface_commit;
wl_signal_add(&layer_surface->surface->events.commit,
&sway_layer->surface_commit);
@@ -664,7 +679,7 @@ void handle_layer_shell_surface(struct wl_listener *listener, void *data) {
struct sway_output *output = layer_surface->output->data;
sway_layer->output_destroy.notify = handle_output_destroy;
- wl_signal_add(&output->events.destroy, &sway_layer->output_destroy);
+ wl_signal_add(&output->events.disable, &sway_layer->output_destroy);
wl_list_insert(&output->layers[layer_surface->pending.layer],
&sway_layer->link);
diff --git a/sway/desktop/output.c b/sway/desktop/output.c
index edec71ad..68f095c0 100644
--- a/sway/desktop/output.c
+++ b/sway/desktop/output.c
@@ -5,6 +5,7 @@
#include <time.h>
#include <wayland-server-core.h>
#include <wlr/backend/drm.h>
+#include <wlr/backend/headless.h>
#include <wlr/render/wlr_renderer.h>
#include <wlr/types/wlr_buffer.h>
#include <wlr/types/wlr_drm_lease_v1.h>
@@ -633,21 +634,19 @@ static void damage_surface_iterator(struct sway_output *output,
struct wlr_box box = *_box;
scale_box(&box, output->wlr_output->scale);
- if (pixman_region32_not_empty(&surface->buffer_damage)) {
- pixman_region32_t damage;
- pixman_region32_init(&damage);
- wlr_surface_get_effective_damage(surface, &damage);
- wlr_region_scale(&damage, &damage, output->wlr_output->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_output_damage_add(output->damage, &damage);
- pixman_region32_fini(&damage);
- }
+ pixman_region32_t damage;
+ pixman_region32_init(&damage);
+ wlr_surface_get_effective_damage(surface, &damage);
+ wlr_region_scale(&damage, &damage, output->wlr_output->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_output_damage_add(output->damage, &damage);
+ pixman_region32_fini(&damage);
if (whole) {
wlr_output_damage_add_box(output->damage, &box);
@@ -733,7 +732,7 @@ static void update_output_manager_config(struct sway_server *server) {
struct sway_output *output;
wl_list_for_each(output, &root->all_outputs, link) {
- if (output == root->noop_output) {
+ if (output == root->fallback_output) {
continue;
}
struct wlr_output_configuration_head_v1 *config_head =
@@ -755,18 +754,22 @@ static void update_output_manager_config(struct sway_server *server) {
static void handle_destroy(struct wl_listener *listener, void *data) {
struct sway_output *output = wl_container_of(listener, output, destroy);
struct sway_server *server = output->server;
- wl_signal_emit(&output->events.destroy, output);
+ output_begin_destroy(output);
if (output->enabled) {
output_disable(output);
}
- output_begin_destroy(output);
+
+ wl_list_remove(&output->link);
wl_list_remove(&output->destroy.link);
wl_list_remove(&output->commit.link);
wl_list_remove(&output->mode.link);
wl_list_remove(&output->present.link);
+ output->wlr_output->data = NULL;
+ output->wlr_output = NULL;
+
transaction_commit_dirty();
update_output_manager_config(server);
@@ -835,9 +838,22 @@ static void handle_present(struct wl_listener *listener, void *data) {
output->refresh_nsec = output_event->refresh;
}
+static unsigned int last_headless_num = 0;
+
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;
+
+ if (wlr_output == root->fallback_output->wlr_output) {
+ return;
+ }
+
+ if (wlr_output_is_headless(wlr_output)) {
+ char name[64];
+ snprintf(name, sizeof(name), "HEADLESS-%u", ++last_headless_num);
+ wlr_output_set_name(wlr_output, name);
+ }
+
sway_log(SWAY_DEBUG, "New output %p: %s (non-desktop: %d)",
wlr_output, wlr_output->name, wlr_output->non_desktop);
@@ -850,6 +866,12 @@ void handle_new_output(struct wl_listener *listener, void *data) {
return;
}
+ if (!wlr_output_init_render(wlr_output, server->allocator,
+ server->renderer)) {
+ sway_log(SWAY_ERROR, "Failed to init output render");
+ return;
+ }
+
struct sway_output *output = output_create(wlr_output);
if (!output) {
return;
diff --git a/sway/desktop/render.c b/sway/desktop/render.c
index 17fc8f6f..c088c936 100644
--- a/sway/desktop/render.c
+++ b/sway/desktop/render.c
@@ -52,7 +52,7 @@ static int scale_length(int length, int offset, float scale) {
static void scissor_output(struct wlr_output *wlr_output,
pixman_box32_t *rect) {
- struct wlr_renderer *renderer = wlr_backend_get_renderer(wlr_output->backend);
+ struct wlr_renderer *renderer = wlr_output->renderer;
assert(renderer);
struct wlr_box box = {
@@ -100,8 +100,7 @@ static void render_texture(struct wlr_output *wlr_output,
pixman_region32_t *output_damage, struct wlr_texture *texture,
const struct wlr_fbox *src_box, const struct wlr_box *dst_box,
const float matrix[static 9], float alpha) {
- struct wlr_renderer *renderer =
- wlr_backend_get_renderer(wlr_output->backend);
+ struct wlr_renderer *renderer = wlr_output->renderer;
struct sway_output *output = wlr_output->data;
pixman_region32_t damage;
@@ -218,8 +217,7 @@ void render_rect(struct sway_output *output,
pixman_region32_t *output_damage, const struct wlr_box *_box,
float color[static 4]) {
struct wlr_output *wlr_output = output->wlr_output;
- struct wlr_renderer *renderer =
- wlr_backend_get_renderer(wlr_output->backend);
+ struct wlr_renderer *renderer = wlr_output->renderer;
struct wlr_box box;
memcpy(&box, _box, sizeof(struct wlr_box));
@@ -764,6 +762,14 @@ static void render_containers_linear(struct sway_output *output,
}
}
+static bool container_is_focused(struct sway_container *con, void *data) {
+ return con->current.focused;
+}
+
+static bool container_has_focused_child(struct sway_container *con) {
+ return container_find_child(con, container_is_focused, NULL);
+}
+
/**
* Render a container's children using the L_TABBED layout.
*/
@@ -795,6 +801,10 @@ static void render_containers_tabbed(struct sway_output *output,
colors = &config->border_colors.focused;
title_texture = child->title_focused;
marks_texture = child->marks_focused;
+ } else if (config->has_focused_tab_title && container_has_focused_child(child)) {
+ colors = &config->border_colors.focused_tab_title;
+ title_texture = child->title_focused_tab_title;
+ marks_texture = child->marks_focused_tab_title;
} else if (child == parent->active_child) {
colors = &config->border_colors.focused_inactive;
title_texture = child->title_focused_inactive;
@@ -860,7 +870,11 @@ static void render_containers_stacked(struct sway_output *output,
colors = &config->border_colors.focused;
title_texture = child->title_focused;
marks_texture = child->marks_focused;
- } else if (child == parent->active_child) {
+ } else if (config->has_focused_tab_title && container_has_focused_child(child)) {
+ colors = &config->border_colors.focused_tab_title;
+ title_texture = child->title_focused_tab_title;
+ marks_texture = child->marks_focused_tab_title;
+ } else if (child == parent->active_child) {
colors = &config->border_colors.focused_inactive;
title_texture = child->title_focused_inactive;
marks_texture = child->marks_focused_inactive;
@@ -1013,13 +1027,7 @@ static void render_seatops(struct sway_output *output,
void output_render(struct sway_output *output, struct timespec *when,
pixman_region32_t *damage) {
struct wlr_output *wlr_output = output->wlr_output;
-
- struct wlr_renderer *renderer =
- wlr_backend_get_renderer(wlr_output->backend);
- if (!sway_assert(renderer != NULL,
- "expected the output backend to have a renderer")) {
- return;
- }
+ struct wlr_renderer *renderer = output->server->renderer;
struct sway_workspace *workspace = output->current.active_workspace;
if (workspace == NULL) {
diff --git a/sway/desktop/transaction.c b/sway/desktop/transaction.c
index b1f3fb32..f5a3a053 100644
--- a/sway/desktop/transaction.c
+++ b/sway/desktop/transaction.c
@@ -402,7 +402,7 @@ static void transaction_commit(struct sway_transaction *transaction) {
struct sway_transaction_instruction *instruction =
transaction->instructions->items[i];
struct sway_node *node = instruction->node;
- bool hidden = node_is_view(node) &&
+ bool hidden = node_is_view(node) && !node->destroying &&
!view_is_visible(node->sway_container->view);
if (should_configure(node, instruction)) {
instruction->serial = view_configure(node->sway_container->view,
diff --git a/sway/desktop/xdg_shell.c b/sway/desktop/xdg_shell.c
index c1e5bc68..5fae8296 100644
--- a/sway/desktop/xdg_shell.c
+++ b/sway/desktop/xdg_shell.c
@@ -72,8 +72,8 @@ static void popup_unconstrain(struct sway_xdg_popup *popup) {
// the output box expressed in the coordinate system of the toplevel parent
// of the popup
struct wlr_box output_toplevel_sx_box = {
- .x = output->lx - view->container->pending.content_x,
- .y = output->ly - view->container->pending.content_y,
+ .x = output->lx - view->container->pending.content_x + view->geometry.x,
+ .y = output->ly - view->container->pending.content_y + view->geometry.y,
.width = output->width,
.height = output->height,
};
diff --git a/sway/desktop/xwayland.c b/sway/desktop/xwayland.c
index 1af8d248..40288f97 100644
--- a/sway/desktop/xwayland.c
+++ b/sway/desktop/xwayland.c
@@ -436,6 +436,8 @@ static void handle_destroy(struct wl_listener *listener, void *data) {
wl_list_remove(&xwayland_view->commit.link);
}
+ xwayland_view->view.wlr_xwayland_surface = NULL;
+
wl_list_remove(&xwayland_view->destroy.link);
wl_list_remove(&xwayland_view->request_configure.link);
wl_list_remove(&xwayland_view->request_fullscreen.link);
diff --git a/sway/input/cursor.c b/sway/input/cursor.c
index 1e3e16d6..6fddee90 100644
--- a/sway/input/cursor.c
+++ b/sway/input/cursor.c
@@ -83,7 +83,28 @@ static struct wlr_surface *layer_surface_popup_at(struct sway_output *output,
struct sway_node *node_at_coords(
struct sway_seat *seat, double lx, double ly,
struct wlr_surface **surface, double *sx, double *sy) {
- // check for unmanaged views first
+ // find the output the cursor is on
+ struct wlr_output *wlr_output = wlr_output_layout_output_at(
+ root->output_layout, lx, ly);
+ if (wlr_output == NULL) {
+ return NULL;
+ }
+ struct sway_output *output = wlr_output->data;
+ if (!output || !output->enabled) {
+ // output is being destroyed or is being enabled
+ return NULL;
+ }
+ double ox = lx, oy = ly;
+ wlr_output_layout_output_coords(root->output_layout, wlr_output, &ox, &oy);
+
+ // layer surfaces on the overlay layer are rendered on top
+ if ((*surface = layer_surface_at(output,
+ &output->layers[ZWLR_LAYER_SHELL_V1_LAYER_OVERLAY],
+ ox, oy, sx, sy))) {
+ return NULL;
+ }
+
+ // check for unmanaged views
#if HAVE_XWAYLAND
struct wl_list *unmanaged = &root->xwayland_unmanaged;
struct sway_xwayland_unmanaged *unmanaged_surface;
@@ -101,19 +122,6 @@ struct sway_node *node_at_coords(
}
}
#endif
- // find the output the cursor is on
- struct wlr_output *wlr_output = wlr_output_layout_output_at(
- root->output_layout, lx, ly);
- if (wlr_output == NULL) {
- return NULL;
- }
- struct sway_output *output = wlr_output->data;
- if (!output || !output->enabled) {
- // output is being destroyed or is being enabled
- return NULL;
- }
- double ox = lx, oy = ly;
- wlr_output_layout_output_coords(root->output_layout, wlr_output, &ox, &oy);
if (root->fullscreen_global) {
// Try fullscreen container
@@ -131,11 +139,6 @@ struct sway_node *node_at_coords(
return NULL;
}
- if ((*surface = layer_surface_at(output,
- &output->layers[ZWLR_LAYER_SHELL_V1_LAYER_OVERLAY],
- ox, oy, sx, sy))) {
- return NULL;
- }
if (ws->fullscreen) {
// Try transient containers
for (int i = 0; i < ws->floating->length; ++i) {
@@ -924,6 +927,7 @@ static void handle_pointer_pinch_begin(struct wl_listener *listener, void *data)
struct sway_cursor *cursor = wl_container_of(
listener, cursor, pinch_begin);
struct wlr_event_pointer_pinch_begin *event = data;
+ cursor_handle_activity_from_device(cursor, event->device);
wlr_pointer_gestures_v1_send_pinch_begin(
cursor->pointer_gestures, cursor->seat->wlr_seat,
event->time_msec, event->fingers);
@@ -933,6 +937,7 @@ static void handle_pointer_pinch_update(struct wl_listener *listener, void *data
struct sway_cursor *cursor = wl_container_of(
listener, cursor, pinch_update);
struct wlr_event_pointer_pinch_update *event = data;
+ cursor_handle_activity_from_device(cursor, event->device);
wlr_pointer_gestures_v1_send_pinch_update(
cursor->pointer_gestures, cursor->seat->wlr_seat,
event->time_msec, event->dx, event->dy,
@@ -943,6 +948,7 @@ static void handle_pointer_pinch_end(struct wl_listener *listener, void *data) {
struct sway_cursor *cursor = wl_container_of(
listener, cursor, pinch_end);
struct wlr_event_pointer_pinch_end *event = data;
+ cursor_handle_activity_from_device(cursor, event->device);
wlr_pointer_gestures_v1_send_pinch_end(
cursor->pointer_gestures, cursor->seat->wlr_seat,
event->time_msec, event->cancelled);
@@ -952,6 +958,7 @@ static void handle_pointer_swipe_begin(struct wl_listener *listener, void *data)
struct sway_cursor *cursor = wl_container_of(
listener, cursor, swipe_begin);
struct wlr_event_pointer_swipe_begin *event = data;
+ cursor_handle_activity_from_device(cursor, event->device);
wlr_pointer_gestures_v1_send_swipe_begin(
cursor->pointer_gestures, cursor->seat->wlr_seat,
event->time_msec, event->fingers);
@@ -961,6 +968,7 @@ static void handle_pointer_swipe_update(struct wl_listener *listener, void *data
struct sway_cursor *cursor = wl_container_of(
listener, cursor, swipe_update);
struct wlr_event_pointer_swipe_update *event = data;
+ cursor_handle_activity_from_device(cursor, event->device);
wlr_pointer_gestures_v1_send_swipe_update(
cursor->pointer_gestures, cursor->seat->wlr_seat,
event->time_msec, event->dx, event->dy);
@@ -970,6 +978,7 @@ static void handle_pointer_swipe_end(struct wl_listener *listener, void *data) {
struct sway_cursor *cursor = wl_container_of(
listener, cursor, swipe_end);
struct wlr_event_pointer_swipe_end *event = data;
+ cursor_handle_activity_from_device(cursor, event->device);
wlr_pointer_gestures_v1_send_swipe_end(
cursor->pointer_gestures, cursor->seat->wlr_seat,
event->time_msec, event->cancelled);
diff --git a/sway/input/seat.c b/sway/input/seat.c
index c5c8459e..ce933b66 100644
--- a/sway/input/seat.c
+++ b/sway/input/seat.c
@@ -51,6 +51,16 @@ static void seat_device_destroy(struct sway_seat_device *seat_device) {
static void seat_node_destroy(struct sway_seat_node *seat_node) {
wl_list_remove(&seat_node->destroy.link);
wl_list_remove(&seat_node->link);
+
+ /*
+ * This is the only time we remove items from the focus stack without
+ * immediately re-adding them. If we just removed the last thing,
+ * mark that nothing has focus anymore.
+ */
+ if (wl_list_empty(&seat_node->seat->focus_stack)) {
+ seat_node->seat->has_focus = false;
+ }
+
free(seat_node);
}
@@ -1415,9 +1425,8 @@ struct sway_node *seat_get_focus(struct sway_seat *seat) {
if (!seat->has_focus) {
return NULL;
}
- if (wl_list_empty(&seat->focus_stack)) {
- return NULL;
- }
+ sway_assert(!wl_list_empty(&seat->focus_stack),
+ "focus_stack is empty, but has_focus is true");
struct sway_seat_node *current =
wl_container_of(seat->focus_stack.next, current, link);
return current->node;
diff --git a/sway/input/seatop_resize_floating.c b/sway/input/seatop_resize_floating.c
index 8400a4b3..df683026 100644
--- a/sway/input/seatop_resize_floating.c
+++ b/sway/input/seatop_resize_floating.c
@@ -80,17 +80,25 @@ static void handle_pointer_motion(struct sway_seat *seat, uint32_t time_msec) {
double height = e->ref_height + grow_height;
int min_width, max_width, min_height, max_height;
floating_calculate_constraints(&min_width, &max_width,
- &min_height, &max_height);
- width = fmax(min_width + border_width, fmin(width, max_width));
- height = fmax(min_height + border_height, fmin(height, max_height));
+ &min_height, &max_height);
+ width = fmin(width, max_width - border_width);
+ width = fmax(width, min_width + border_width);
+ width = fmax(width, 1);
+ height = fmin(height, max_height - border_height);
+ height = fmax(height, min_height + border_height);
+ height = fmax(height, 1);
// Apply the view's min/max size
if (con->view) {
double view_min_width, view_max_width, view_min_height, view_max_height;
view_get_constraints(con->view, &view_min_width, &view_max_width,
&view_min_height, &view_max_height);
- width = fmax(view_min_width + border_width, fmin(width, view_max_width));
- height = fmax(view_min_height + border_height, fmin(height, view_max_height));
+ width = fmin(width, view_max_width - border_width);
+ width = fmax(width, view_min_width + border_width);
+ width = fmax(width, 1);
+ height = fmin(height, view_max_height - border_height);
+ height = fmax(height, view_min_height + border_height);
+ height = fmax(height, 1);
}
diff --git a/sway/ipc-server.c b/sway/ipc-server.c
index aad9a7b5..1bf5a05f 100644
--- a/sway/ipc-server.c
+++ b/sway/ipc-server.c
@@ -687,7 +687,7 @@ void ipc_client_handle_command(struct ipc_client *client, uint32_t payload_lengt
}
struct sway_output *output;
wl_list_for_each(output, &root->all_outputs, link) {
- if (!output->enabled && output != root->noop_output) {
+ if (!output->enabled && output != root->fallback_output) {
json_object_array_add(outputs,
ipc_json_describe_disabled_output(output));
}
diff --git a/sway/main.c b/sway/main.c
index e960c4e2..b6f8a8bf 100644
--- a/sway/main.c
+++ b/sway/main.c
@@ -6,6 +6,7 @@
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
+#include <sys/resource.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <sys/wait.h>
@@ -27,6 +28,7 @@
static bool terminate_request = false;
static int exit_value = 0;
+static struct rlimit original_nofile_rlimit = {0};
struct sway_server server = {0};
struct sway_debug debug = {0};
@@ -63,7 +65,7 @@ void detect_proprietary(int allow_unsupported_gpu) {
sway_log(SWAY_ERROR,
"Proprietary Nvidia drivers are NOT supported. "
"Use Nouveau. To launch sway anyway, launch with "
- "--my-next-gpu-wont-be-nvidia and DO NOT report issues.");
+ "--unsupported-gpu and DO NOT report issues.");
exit(EXIT_FAILURE);
}
break;
@@ -151,6 +153,9 @@ static void log_kernel(void) {
static bool drop_permissions(void) {
if (getuid() != geteuid() || getgid() != getegid()) {
+ sway_log(SWAY_ERROR, "!!! DEPRECATION WARNING: "
+ "SUID privilege drop will be removed in a future release, please migrate to seatd-launch");
+
// Set the gid and uid in the correct order.
if (setgid(getgid()) != 0) {
sway_log(SWAY_ERROR, "Unable to drop root group, refusing to start");
@@ -169,6 +174,33 @@ static bool drop_permissions(void) {
return true;
}
+static void increase_nofile_limit(void) {
+ if (getrlimit(RLIMIT_NOFILE, &original_nofile_rlimit) != 0) {
+ sway_log_errno(SWAY_ERROR, "Failed to bump max open files limit: "
+ "getrlimit(NOFILE) failed");
+ return;
+ }
+
+ struct rlimit new_rlimit = original_nofile_rlimit;
+ new_rlimit.rlim_cur = new_rlimit.rlim_max;
+ if (setrlimit(RLIMIT_NOFILE, &new_rlimit) != 0) {
+ sway_log_errno(SWAY_ERROR, "Failed to bump max open files limit: "
+ "setrlimit(NOFILE) failed");
+ sway_log(SWAY_INFO, "Running with %d max open files",
+ (int)original_nofile_rlimit.rlim_cur);
+ }
+}
+
+void restore_nofile_limit(void) {
+ if (original_nofile_rlimit.rlim_cur == 0) {
+ return;
+ }
+ if (setrlimit(RLIMIT_NOFILE, &original_nofile_rlimit) != 0) {
+ sway_log_errno(SWAY_ERROR, "Failed to restore max open files limit: "
+ "setrlimit(NOFILE) failed");
+ }
+}
+
void enable_debug_flag(const char *flag) {
if (strcmp(flag, "damage=highlight") == 0) {
debug.damage = DAMAGE_HIGHLIGHT;
@@ -220,7 +252,6 @@ int main(int argc, char **argv) {
{"verbose", no_argument, NULL, 'V'},
{"get-socketpath", no_argument, NULL, 'p'},
{"unsupported-gpu", no_argument, NULL, 'u'},
- {"my-next-gpu-wont-be-nvidia", no_argument, NULL, 'u'},
{0, 0, 0, 0}
};
@@ -314,7 +345,6 @@ int main(int argc, char **argv) {
log_kernel();
log_distro();
log_env();
- detect_proprietary(allow_unsupported_gpu);
if (optind < argc) { // Behave as IPC client
if (optind != 1) {
@@ -341,6 +371,8 @@ int main(int argc, char **argv) {
return 0;
}
+ detect_proprietary(allow_unsupported_gpu);
+
if (!server_privileged_prepare(&server)) {
return 1;
}
@@ -350,6 +382,8 @@ int main(int argc, char **argv) {
exit(EXIT_FAILURE);
}
+ increase_nofile_limit();
+
// handle SIGTERM signals
signal(SIGTERM, sig_handler);
signal(SIGINT, sig_handler);
diff --git a/sway/meson.build b/sway/meson.build
index 1402db15..8eab31a2 100644
--- a/sway/meson.build
+++ b/sway/meson.build
@@ -188,6 +188,7 @@ sway_sources = files(
'commands/output/max_render_time.c',
'commands/output/mode.c',
'commands/output/position.c',
+ 'commands/output/render_bit_depth.c',
'commands/output/scale.c',
'commands/output/scale_filter.c',
'commands/output/subpixel.c',
diff --git a/sway/server.c b/sway/server.c
index b187fcd5..f50a0987 100644
--- a/sway/server.c
+++ b/sway/server.c
@@ -7,17 +7,18 @@
#include <wlr/backend.h>
#include <wlr/backend/headless.h>
#include <wlr/backend/multi.h>
-#include <wlr/backend/noop.h>
#include <wlr/backend/session.h>
#include <wlr/config.h>
#include <wlr/render/wlr_renderer.h>
#include <wlr/types/wlr_compositor.h>
#include <wlr/types/wlr_data_control_v1.h>
#include <wlr/types/wlr_drm_lease_v1.h>
+#include <wlr/types/wlr_drm.h>
#include <wlr/types/wlr_export_dmabuf_v1.h>
#include <wlr/types/wlr_gamma_control_v1.h>
#include <wlr/types/wlr_idle.h>
#include <wlr/types/wlr_layer_shell_v1.h>
+#include <wlr/types/wlr_linux_dmabuf_v1.h>
#include <wlr/types/wlr_pointer_constraints_v1.h>
#include <wlr/types/wlr_primary_selection_v1.h>
#include <wlr/types/wlr_relative_pointer_v1.h>
@@ -73,12 +74,29 @@ static void handle_drm_lease_request(struct wl_listener *listener, void *data) {
bool server_init(struct sway_server *server) {
sway_log(SWAY_DEBUG, "Initializing Wayland server");
- struct wlr_renderer *renderer = wlr_backend_get_renderer(server->backend);
- assert(renderer);
+ server->renderer = wlr_renderer_autocreate(server->backend);
+ if (!server->renderer) {
+ sway_log(SWAY_ERROR, "Failed to create renderer");
+ return false;
+ }
- wlr_renderer_init_wl_display(renderer, server->wl_display);
+ wlr_renderer_init_wl_shm(server->renderer, server->wl_display);
+
+ if (wlr_renderer_get_dmabuf_texture_formats(server->renderer) != NULL) {
+ wlr_drm_create(server->wl_display, server->renderer);
+ server->linux_dmabuf_v1 =
+ wlr_linux_dmabuf_v1_create(server->wl_display, server->renderer);
+ }
+
+ server->allocator = wlr_allocator_autocreate(server->backend,
+ server->renderer);
+ if (!server->allocator) {
+ sway_log(SWAY_ERROR, "Failed to create allocator");
+ return false;
+ }
- server->compositor = wlr_compositor_create(server->wl_display, renderer);
+ server->compositor = wlr_compositor_create(server->wl_display,
+ server->renderer);
server->compositor_new_surface.notify = handle_compositor_new_surface;
wl_signal_add(&server->compositor->events.new_surface,
&server->compositor_new_surface);
@@ -206,20 +224,20 @@ bool server_init(struct sway_server *server) {
return false;
}
- server->noop_backend = wlr_noop_backend_create(server->wl_display);
-
- struct wlr_output *wlr_output = wlr_noop_add_output(server->noop_backend);
- root->noop_output = output_create(wlr_output);
-
- server->headless_backend =
- wlr_headless_backend_create_with_renderer(server->wl_display, renderer);
+ server->headless_backend = wlr_headless_backend_create(server->wl_display);
if (!server->headless_backend) {
- sway_log(SWAY_INFO, "Failed to create secondary headless backend, "
- "starting without it");
+ sway_log(SWAY_ERROR, "Failed to create secondary headless backend");
+ wlr_backend_destroy(server->backend);
+ return false;
} else {
wlr_multi_backend_add(server->backend, server->headless_backend);
}
+ struct wlr_output *wlr_output =
+ wlr_headless_add_output(server->headless_backend, 800, 600);
+ wlr_output_set_name(wlr_output, "FALLBACK");
+ root->fallback_output = output_create(wlr_output);
+
// This may have been set already via -Dtxn-timeout
if (!server->txn_timeout_ms) {
server->txn_timeout_ms = 200;
@@ -276,6 +294,7 @@ bool server_start(struct sway_server *server) {
wlr_backend_destroy(server->backend);
return false;
}
+
return true;
}
diff --git a/sway/sway-output.5.scd b/sway/sway-output.5.scd
index 55d8f719..4159a851 100644
--- a/sway/sway-output.5.scd
+++ b/sway/sway-output.5.scd
@@ -157,6 +157,21 @@ must be separated by one space. For example:
adaptive sync can improve latency, but can cause flickering on some
hardware.
+*output* <name> render_bit_depth 8|10
+ Controls the color channel bit depth at which frames are rendered; the
+ default is currently 8 bits per channel.
+
+ Setting higher values will not have an effect if hardware and software lack
+ support for such bit depths. Successfully increasing the render bit depth
+ will not necessarily increase the bit depth of the frames sent to a display.
+ An increased render bit depth may provide smoother rendering of gradients,
+ and screenshots which can more precisely store the colors of programs
+ which display high bit depth colors.
+
+ Warnings: this can break screenshot/screencast programs which have not been
+ updated to work with different bit depths. This command is experimental,
+ and may be removed or changed in the future.
+
# SEE ALSO
*sway*(5) *sway-input*(5)
diff --git a/sway/sway.5.scd b/sway/sway.5.scd
index e8d3d606..641d0925 100644
--- a/sway/sway.5.scd
+++ b/sway/sway.5.scd
@@ -499,6 +499,12 @@ runtime.
*client.focused_inactive*
The most recently focused view within a container which is not focused.
+ *client.focused_tab_title*
+ A view that has focused descendant container.
+ Tab or stack container title that is the parent of the focused container
+ but is not directly focused. Defaults to focused_inactive if not
+ specified and does not use the indicator and child_border colors.
+
*client.placeholder*
Ignored (present for i3 compatibility).
@@ -554,6 +560,12 @@ The default colors are:
: #ffffff
: #484e50
: #5f676a
+| *focused_tab_title*
+: #333333
+: #5f676a
+: #ffffff
+: n/a
+: n/a
| *unfocused*
: #333333
: #222222
@@ -692,9 +704,10 @@ The default colors are:
borders will only be enabled if the workspace has more than one visible
child and gaps equal to zero.
-*smart_gaps* on|off
+*smart_gaps* on|off|toggle|inverse_outer
If smart_gaps are _on_ gaps will only be enabled if a workspace has more
- than one child.
+ than one child. If smart_gaps are _inverse_outer_ outer gaps will only
+ be enabled if a workspace has exactly one child.
*mark* --add|--replace [--toggle] <identifier>
Marks are arbitrary labels that can be used to identify certain windows and
diff --git a/sway/swaynag.c b/sway/swaynag.c
index ba582989..4a0a6d30 100644
--- a/sway/swaynag.c
+++ b/sway/swaynag.c
@@ -64,6 +64,8 @@ bool swaynag_spawn(const char *swaynag_command,
sway_log(SWAY_ERROR, "Failed to create fork for swaynag");
goto failed;
} else if (pid == 0) {
+ restore_nofile_limit();
+
pid = fork();
if (pid < 0) {
sway_log_errno(SWAY_ERROR, "fork failed");
diff --git a/sway/tree/container.c b/sway/tree/container.c
index 6a01eab3..79e04ec0 100644
--- a/sway/tree/container.c
+++ b/sway/tree/container.c
@@ -5,8 +5,12 @@
#include <stdlib.h>
#include <string.h>
#include <strings.h>
+#include <sys/stat.h>
#include <wayland-server-core.h>
+#include <wlr/types/wlr_linux_dmabuf_v1.h>
#include <wlr/types/wlr_output_layout.h>
+#include <wlr/render/drm_format_set.h>
+#include "linux-dmabuf-unstable-v1-protocol.h"
#include "cairo_util.h"
#include "pango.h"
#include "sway/config.h"
@@ -64,6 +68,7 @@ void container_destroy(struct sway_container *con) {
wlr_texture_destroy(con->title_focused_inactive);
wlr_texture_destroy(con->title_unfocused);
wlr_texture_destroy(con->title_urgent);
+ wlr_texture_destroy(con->title_focused_tab_title);
list_free(con->pending.children);
list_free(con->current.children);
list_free(con->outputs);
@@ -73,11 +78,10 @@ void container_destroy(struct sway_container *con) {
wlr_texture_destroy(con->marks_focused_inactive);
wlr_texture_destroy(con->marks_unfocused);
wlr_texture_destroy(con->marks_urgent);
+ wlr_texture_destroy(con->marks_focused_tab_title);
- if (con->view) {
- if (con->view->container == con) {
- con->view->container = NULL;
- }
+ if (con->view && con->view->container == con) {
+ con->view->container = NULL;
if (con->view->destroying) {
view_destroy(con->view);
}
@@ -382,19 +386,17 @@ struct sway_container *tiling_container_at(struct sway_node *parent,
}
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 && xdg_surface->role != WLR_XDG_SURFACE_ROLE_NONE) {
- if (xdg_surface->role == WLR_XDG_SURFACE_ROLE_POPUP) {
- return true;
- }
- xdg_surface = xdg_surface->toplevel->parent;
+ while (!wlr_surface_is_xdg_surface(surface)) {
+ if (!wlr_surface_is_subsurface(surface)) {
+ return false;
}
- return false;
+ struct wlr_subsurface *subsurface =
+ wlr_subsurface_from_wlr_surface(surface);
+ surface = subsurface->parent;
}
-
- return false;
+ struct wlr_xdg_surface *xdg_surface =
+ wlr_xdg_surface_from_wlr_surface(surface);
+ return xdg_surface->role == WLR_XDG_SURFACE_ROLE_POPUP;
}
struct sway_container *container_at(struct sway_workspace *workspace,
@@ -532,6 +534,13 @@ static void render_titlebar_text_texture(struct sway_output *output,
cairo_surface_t *surface = cairo_image_surface_create(
CAIRO_FORMAT_ARGB32, width, height);
+ cairo_status_t status = cairo_surface_status(surface);
+ if (status != CAIRO_STATUS_SUCCESS) {
+ sway_log(SWAY_ERROR, "cairo_image_surface_create failed: %s",
+ cairo_status_to_string(status));
+ return;
+ }
+
cairo_t *cairo = cairo_create(surface);
cairo_set_antialias(cairo, CAIRO_ANTIALIAS_BEST);
cairo_set_font_options(cairo, fo);
@@ -549,8 +558,7 @@ static void render_titlebar_text_texture(struct sway_output *output,
cairo_surface_flush(surface);
unsigned char *data = cairo_image_surface_get_data(surface);
int stride = cairo_image_surface_get_stride(surface);
- struct wlr_renderer *renderer = wlr_backend_get_renderer(
- output->wlr_output->backend);
+ struct wlr_renderer *renderer = output->wlr_output->renderer;
*texture = wlr_texture_from_pixels(
renderer, DRM_FORMAT_ARGB8888, stride, width, height, data);
cairo_surface_destroy(surface);
@@ -585,6 +593,8 @@ void container_update_title_textures(struct sway_container *container) {
&config->border_colors.unfocused);
update_title_texture(container, &container->title_urgent,
&config->border_colors.urgent);
+ update_title_texture(container, &container->title_focused_tab_title,
+ &config->border_colors.focused_tab_title);
container_damage_whole(container);
}
@@ -1050,6 +1060,16 @@ void container_end_mouse_operation(struct sway_container *container) {
}
}
+static bool devid_from_fd(int fd, dev_t *devid) {
+ struct stat stat;
+ if (fstat(fd, &stat) != 0) {
+ sway_log_errno(SWAY_ERROR, "fstat failed");
+ return false;
+ }
+ *devid = stat.st_rdev;
+ return true;
+}
+
static void set_fullscreen(struct sway_container *con, bool enable) {
if (!con->view) {
return;
@@ -1061,6 +1081,75 @@ static void set_fullscreen(struct sway_container *con, bool enable) {
con->view->foreign_toplevel, enable);
}
}
+
+ if (!server.linux_dmabuf_v1 || !con->view->surface) {
+ return;
+ }
+ if (!enable) {
+ wlr_linux_dmabuf_v1_set_surface_feedback(server.linux_dmabuf_v1,
+ con->view->surface, NULL);
+ return;
+ }
+
+ if (!con->pending.workspace || !con->pending.workspace->output) {
+ return;
+ }
+
+ struct sway_output *output = con->pending.workspace->output;
+ struct wlr_output *wlr_output = output->wlr_output;
+
+ // TODO: add wlroots helpers for all of this stuff
+
+ const struct wlr_drm_format_set *renderer_formats =
+ wlr_renderer_get_dmabuf_texture_formats(server.renderer);
+ assert(renderer_formats);
+
+ int renderer_drm_fd = wlr_renderer_get_drm_fd(server.renderer);
+ int backend_drm_fd = wlr_backend_get_drm_fd(wlr_output->backend);
+ if (renderer_drm_fd < 0 || backend_drm_fd < 0) {
+ return;
+ }
+
+ dev_t render_dev, scanout_dev;
+ if (!devid_from_fd(renderer_drm_fd, &render_dev) ||
+ !devid_from_fd(backend_drm_fd, &scanout_dev)) {
+ return;
+ }
+
+ const struct wlr_drm_format_set *output_formats =
+ wlr_output_get_primary_formats(output->wlr_output,
+ WLR_BUFFER_CAP_DMABUF);
+ if (!output_formats) {
+ return;
+ }
+
+ struct wlr_drm_format_set scanout_formats = {0};
+ if (!wlr_drm_format_set_intersect(&scanout_formats,
+ output_formats, renderer_formats)) {
+ return;
+ }
+
+ struct wlr_linux_dmabuf_feedback_v1_tranche tranches[] = {
+ {
+ .target_device = scanout_dev,
+ .flags = ZWP_LINUX_DMABUF_FEEDBACK_V1_TRANCHE_FLAGS_SCANOUT,
+ .formats = &scanout_formats,
+ },
+ {
+ .target_device = render_dev,
+ .formats = renderer_formats,
+ },
+ };
+
+ const struct wlr_linux_dmabuf_feedback_v1 feedback = {
+ .main_device = render_dev,
+ .tranches = tranches,
+ .tranches_len = sizeof(tranches) / sizeof(tranches[0]),
+ };
+ wlr_linux_dmabuf_v1_set_surface_feedback(server.linux_dmabuf_v1,
+ con->view->surface, &feedback);
+
+ wlr_drm_format_set_finish(&scanout_formats);
}
static void container_fullscreen_workspace(struct sway_container *con) {
@@ -1638,6 +1727,8 @@ void container_update_marks_textures(struct sway_container *con) {
&config->border_colors.unfocused);
update_marks_texture(con, &con->marks_urgent,
&config->border_colors.urgent);
+ update_marks_texture(con, &con->marks_focused_tab_title,
+ &config->border_colors.focused_tab_title);
container_damage_whole(con);
}
diff --git a/sway/tree/output.c b/sway/tree/output.c
index c095dce0..ad8d2482 100644
--- a/sway/tree/output.c
+++ b/sway/tree/output.c
@@ -56,8 +56,8 @@ static void restore_workspaces(struct sway_output *output) {
}
// Saved workspaces
- while (root->noop_output->workspaces->length) {
- struct sway_workspace *ws = root->noop_output->workspaces->items[0];
+ while (root->fallback_output->workspaces->length) {
+ struct sway_workspace *ws = root->fallback_output->workspaces->items[0];
workspace_detach(ws);
output_add_workspace(output, ws);
@@ -95,7 +95,7 @@ struct sway_output *output_create(struct wlr_output *wlr_output) {
output->detected_subpixel = wlr_output->subpixel;
output->scale_filter = SCALE_FILTER_NEAREST;
- wl_signal_init(&output->events.destroy);
+ wl_signal_init(&output->events.disable);
wl_list_insert(&root->all_outputs, &output->link);
@@ -192,7 +192,7 @@ static void output_evacuate(struct sway_output *output) {
new_output = fallback_output;
}
if (!new_output) {
- new_output = root->noop_output;
+ new_output = root->fallback_output;
}
struct sway_workspace *new_output_ws =
@@ -262,7 +262,7 @@ void output_disable(struct sway_output *output) {
}
sway_log(SWAY_DEBUG, "Disabling output '%s'", output->wlr_output->name);
- wl_signal_emit(&output->events.destroy, output);
+ wl_signal_emit(&output->events.disable, output);
output_evacuate(output);
@@ -286,13 +286,10 @@ void output_begin_destroy(struct sway_output *output) {
return;
}
sway_log(SWAY_DEBUG, "Destroying output '%s'", output->wlr_output->name);
+ wl_signal_emit(&output->node.events.destroy, &output->node);
output->node.destroying = true;
node_set_dirty(&output->node);
-
- wl_list_remove(&output->link);
- output->wlr_output->data = NULL;
- output->wlr_output = NULL;
}
struct sway_output *output_from_wlr_output(struct wlr_output *output) {
diff --git a/sway/tree/root.c b/sway/tree/root.c
index dd4d8e33..73f3993c 100644
--- a/sway/tree/root.c
+++ b/sway/tree/root.c
@@ -374,8 +374,8 @@ void root_for_each_container(void (*f)(struct sway_container *con, void *data),
}
// Saved workspaces
- for (int i = 0; i < root->noop_output->workspaces->length; ++i) {
- struct sway_workspace *ws = root->noop_output->workspaces->items[i];
+ for (int i = 0; i < root->fallback_output->workspaces->length; ++i) {
+ struct sway_workspace *ws = root->fallback_output->workspaces->items[i];
workspace_for_each_container(ws, f, data);
}
}
@@ -427,8 +427,8 @@ struct sway_container *root_find_container(
}
// Saved workspaces
- for (int i = 0; i < root->noop_output->workspaces->length; ++i) {
- struct sway_workspace *ws = root->noop_output->workspaces->items[i];
+ for (int i = 0; i < root->fallback_output->workspaces->length; ++i) {
+ struct sway_workspace *ws = root->fallback_output->workspaces->items[i];
if ((result = workspace_find_container(ws, test, data))) {
return result;
}
diff --git a/sway/tree/view.c b/sway/tree/view.c
index ed8c50f8..8b7061ba 100644
--- a/sway/tree/view.c
+++ b/sway/tree/view.c
@@ -752,10 +752,29 @@ void view_map(struct sway_view *view, struct wlr_surface *wlr_surface,
}
struct sway_seat *seat = input_manager_current_seat();
- struct sway_node *node = ws ? seat_get_focus_inactive(seat, &ws->node)
- : seat_get_focus_inactive(seat, &root->node);
- struct sway_container *target_sibling = node->type == N_CONTAINER ?
- node->sway_container : NULL;
+ struct sway_node *node =
+ seat_get_focus_inactive(seat, ws ? &ws->node : &root->node);
+ struct sway_container *target_sibling = NULL;
+ if (node && node->type == N_CONTAINER) {
+ if (container_is_floating(node->sway_container)) {
+ // If we're about to launch the view into the floating container, then
+ // launch it as a tiled view instead.
+ if (ws) {
+ target_sibling = seat_get_focus_inactive_tiling(seat, ws);
+ if (target_sibling) {
+ struct sway_container *con =
+ seat_get_focus_inactive_view(seat, &target_sibling->node);
+ if (con) {
+ target_sibling = con;
+ }
+ }
+ } else {
+ ws = seat_get_last_known_workspace(seat);
+ }
+ } else {
+ target_sibling = node->sway_container;
+ }
+ }
view->foreign_toplevel =
wlr_foreign_toplevel_handle_v1_create(server.foreign_toplevel_manager);
@@ -776,13 +795,6 @@ void view_map(struct sway_view *view, struct wlr_surface *wlr_surface,
&view->foreign_minimize);
- // 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 (target_sibling && container_is_floating(target_sibling)) {
- target_sibling = NULL;
- ws = seat_get_last_known_workspace(seat);
- }
-
struct sway_container *container = view->container;
if (target_sibling) {
container_add_sibling(target_sibling, container, 1);
@@ -1129,9 +1141,12 @@ void view_child_init(struct sway_view_child *child,
wl_signal_add(&view->events.unmap, &child->view_unmap);
child->view_unmap.notify = view_child_handle_view_unmap;
- struct sway_workspace *workspace = child->view->container->pending.workspace;
- if (workspace) {
- wlr_surface_send_enter(child->surface, workspace->output->wlr_output);
+ struct sway_container *container = child->view->container;
+ if (container != NULL) {
+ struct sway_workspace *workspace = container->pending.workspace;
+ if (workspace) {
+ wlr_surface_send_enter(child->surface, workspace->output->wlr_output);
+ }
}
view_child_init_subsurfaces(child, surface);
diff --git a/sway/tree/workspace.c b/sway/tree/workspace.c
index 8dd7789d..c84320bd 100644
--- a/sway/tree/workspace.c
+++ b/sway/tree/workspace.c
@@ -50,8 +50,8 @@ struct sway_output *workspace_get_initial_output(const char *name) {
} else if (focus && focus->type == N_CONTAINER) {
return focus->sway_container->pending.workspace->output;
}
- // Fallback to the first output or noop output for headless
- return root->outputs->length ? root->outputs->items[0] : root->noop_output;
+ // Fallback to the first output or the headless output
+ return root->outputs->length ? root->outputs->items[0] : root->fallback_output;
}
struct sway_workspace *workspace_create(struct sway_output *output,
@@ -844,24 +844,36 @@ struct sway_container *workspace_insert_tiling(struct sway_workspace *workspace,
return con;
}
+bool workspace_has_single_visible_container(struct sway_workspace *ws) {
+ struct sway_seat *seat = input_manager_get_default_seat();
+ struct sway_container *focus =
+ seat_get_focus_inactive_tiling(seat, ws);
+ if (focus && !focus->view) {
+ focus = seat_get_focus_inactive_view(seat, &focus->node);
+ }
+ return (focus && focus->view && view_ancestor_is_only_visible(focus->view));
+}
+
void workspace_add_gaps(struct sway_workspace *ws) {
- if (config->smart_gaps) {
- struct sway_seat *seat = input_manager_get_default_seat();
- struct sway_container *focus =
- seat_get_focus_inactive_tiling(seat, ws);
- if (focus && !focus->view) {
- focus = seat_get_focus_inactive_view(seat, &focus->node);
- }
- if (focus && focus->view && view_ancestor_is_only_visible(focus->view)) {
- ws->current_gaps.top = 0;
- ws->current_gaps.right = 0;
- ws->current_gaps.bottom = 0;
- ws->current_gaps.left = 0;
- return;
- }
+ if (config->smart_gaps == SMART_GAPS_ON
+ && workspace_has_single_visible_container(ws)) {
+ ws->current_gaps.top = 0;
+ ws->current_gaps.right = 0;
+ ws->current_gaps.bottom = 0;
+ ws->current_gaps.left = 0;
+ return;
+ }
+
+ if (config->smart_gaps == SMART_GAPS_INVERSE_OUTER
+ && !workspace_has_single_visible_container(ws)) {
+ ws->current_gaps.top = 0;
+ ws->current_gaps.right = 0;
+ ws->current_gaps.bottom = 0;
+ ws->current_gaps.left = 0;
+ } else {
+ ws->current_gaps = ws->gaps_outer;
}
- ws->current_gaps = ws->gaps_outer;
// Add inner gaps and make sure we don't turn out negative
ws->current_gaps.top = fmax(0, ws->current_gaps.top + ws->gaps_inner);
ws->current_gaps.right = fmax(0, ws->current_gaps.right + ws->gaps_inner);