aboutsummaryrefslogtreecommitdiff
path: root/sway/tree
diff options
context:
space:
mode:
Diffstat (limited to 'sway/tree')
-rw-r--r--sway/tree/container.c52
-rw-r--r--sway/tree/output.c18
-rw-r--r--sway/tree/root.c46
-rw-r--r--sway/tree/view.c109
-rw-r--r--sway/tree/workspace.c44
5 files changed, 185 insertions, 84 deletions
diff --git a/sway/tree/container.c b/sway/tree/container.c
index cf6f5b54..d9c721f5 100644
--- a/sway/tree/container.c
+++ b/sway/tree/container.c
@@ -21,6 +21,7 @@
#include "sway/tree/arrange.h"
#include "sway/tree/view.h"
#include "sway/tree/workspace.h"
+#include "list.h"
#include "log.h"
#include "stringop.h"
@@ -67,8 +68,7 @@ void container_destroy(struct sway_container *con) {
list_free(con->current.children);
list_free(con->outputs);
- list_foreach(con->marks, free);
- list_free(con->marks);
+ list_free_items_and_destroy(con->marks);
wlr_texture_destroy(con->marks_focused);
wlr_texture_destroy(con->marks_focused_inactive);
wlr_texture_destroy(con->marks_unfocused);
@@ -453,19 +453,26 @@ static void update_title_texture(struct sway_container *con,
int width = 0;
int height = con->title_height * scale;
- cairo_t *c = cairo_create(NULL);
+ // We must use a non-nil cairo_t for cairo_set_font_options to work.
+ // Therefore, we cannot use cairo_create(NULL).
+ cairo_surface_t *dummy_surface = cairo_image_surface_create(
+ CAIRO_FORMAT_ARGB32, 0, 0);
+ cairo_t *c = cairo_create(dummy_surface);
+ cairo_set_antialias(c, CAIRO_ANTIALIAS_BEST);
+ cairo_font_options_t *fo = cairo_font_options_create();
+ cairo_font_options_set_hint_style(fo, CAIRO_HINT_STYLE_FULL);
+ cairo_font_options_set_antialias(fo, CAIRO_ANTIALIAS_SUBPIXEL);
+ cairo_font_options_set_subpixel_order(fo, to_cairo_subpixel_order(output->wlr_output->subpixel));
+ cairo_set_font_options(c, fo);
get_text_size(c, config->font, &width, NULL, NULL, scale,
config->pango_markup, "%s", con->formatted_title);
+ cairo_surface_destroy(dummy_surface);
cairo_destroy(c);
cairo_surface_t *surface = cairo_image_surface_create(
CAIRO_FORMAT_ARGB32, width, height);
cairo_t *cairo = cairo_create(surface);
cairo_set_antialias(cairo, CAIRO_ANTIALIAS_BEST);
- cairo_font_options_t *fo = cairo_font_options_create();
- cairo_font_options_set_hint_style(fo, CAIRO_HINT_STYLE_FULL);
- cairo_font_options_set_antialias(fo, CAIRO_ANTIALIAS_SUBPIXEL);
- cairo_font_options_set_subpixel_order(fo, to_cairo_subpixel_order(output->wlr_output->subpixel));
cairo_set_font_options(cairo, fo);
cairo_font_options_destroy(fo);
cairo_set_source_rgba(cairo, class->background[0], class->background[1],
@@ -594,7 +601,7 @@ void container_update_representation(struct sway_container *con) {
}
size_t container_titlebar_height(void) {
- return config->font_height + TITLEBAR_V_PADDING * 2;
+ return config->font_height + config->titlebar_v_padding * 2;
}
void container_init_floating(struct sway_container *con) {
@@ -857,15 +864,7 @@ bool container_has_urgent_child(struct sway_container *container) {
void container_end_mouse_operation(struct sway_container *container) {
struct sway_seat *seat;
wl_list_for_each(seat, &server.input->seats, link) {
- if (seat->op_container == container) {
- seat->op_target_node = NULL; // ensure tiling move doesn't apply
- seat_end_mouse_operation(seat);
- }
- // If the user is doing a tiling drag over this container,
- // keep the operation active but unset the target container.
- if (seat->op_target_node == &container->node) {
- seat->op_target_node = NULL;
- }
+ seatop_unref(seat, container);
}
}
@@ -979,7 +978,7 @@ void container_discover_outputs(struct sway_container *con) {
output_get_box(output, &output_box);
struct wlr_box intersection;
bool intersects =
- wlr_box_intersection(&con_box, &output_box, &intersection);
+ wlr_box_intersection(&intersection, &con_box, &output_box);
int index = list_find(con->outputs, output);
if (intersects && index == -1) {
@@ -1267,7 +1266,9 @@ bool container_find_and_unmark(char *mark) {
}
void container_clear_marks(struct sway_container *con) {
- list_foreach(con->marks, free);
+ for (int i = 0; i < con->marks->length; ++i) {
+ free(con->marks->items[i]);
+ }
con->marks->length = 0;
ipc_event_window(con, "mark");
}
@@ -1375,3 +1376,16 @@ void container_update_marks_textures(struct sway_container *con) {
&config->border_colors.urgent);
container_damage_whole(con);
}
+
+void container_raise_floating(struct sway_container *con) {
+ // Bring container to front by putting it at the end of the floating list.
+ struct sway_container *floater = con;
+ while (floater->parent) {
+ floater = floater->parent;
+ }
+ if (container_is_floating(floater)) {
+ list_move_to_end(floater->workspace->floating, floater);
+ node_set_dirty(&floater->workspace->node);
+ }
+}
+
diff --git a/sway/tree/output.c b/sway/tree/output.c
index 3c4614a8..f24be010 100644
--- a/sway/tree/output.c
+++ b/sway/tree/output.c
@@ -58,6 +58,7 @@ struct sway_output *output_create(struct wlr_output *wlr_output) {
wlr_output->data = output;
wl_signal_add(&wlr_output->events.destroy, &output->destroy);
+ wl_signal_init(&output->events.destroy);
wl_list_insert(&root->all_outputs, &output->link);
@@ -76,7 +77,6 @@ void output_enable(struct sway_output *output, struct output_config *oc) {
for (size_t i = 0; i < len; ++i) {
wl_list_init(&output->layers[i]);
}
- wl_signal_init(&output->events.destroy);
output->enabled = true;
list_add(root->outputs, output);
@@ -88,11 +88,12 @@ void output_enable(struct sway_output *output, struct output_config *oc) {
restore_workspaces(output);
+ struct sway_workspace *ws = NULL;
if (!output->workspaces->length) {
// Create workspace
char *ws_name = workspace_next_name(wlr_output->name);
wlr_log(WLR_DEBUG, "Creating default workspace %s", ws_name);
- struct sway_workspace *ws = workspace_create(output, ws_name);
+ ws = workspace_create(output, ws_name);
// Set each seat's focus if not already set
struct sway_seat *seat = NULL;
wl_list_for_each(seat, &server.input->seats, link) {
@@ -104,9 +105,15 @@ void output_enable(struct sway_output *output, struct output_config *oc) {
ipc_event_workspace(NULL, ws, "init");
}
-
apply_output_config(oc, output);
+ if (ws && config->default_orientation == L_NONE) {
+ // Since the output transformation and resolution could have changed
+ // due to applying the output config, the previously set layout for the
+ // created workspace may not be correct for `default_orientation auto`
+ ws->layout = output_get_default_layout(output);
+ }
+
input_manager_configure_xcursor();
wl_signal_add(&wlr_output->events.mode, &output->mode);
@@ -218,6 +225,11 @@ void output_disable(struct sway_output *output) {
root_for_each_container(untrack_output, output);
+ if (output->bg_pid) {
+ terminate_swaybg(output->bg_pid);
+ output->bg_pid = 0;
+ }
+
int index = list_find(root->outputs, output);
list_del(root->outputs, index);
diff --git a/sway/tree/root.c b/sway/tree/root.c
index 544d666a..e1624863 100644
--- a/sway/tree/root.c
+++ b/sway/tree/root.c
@@ -5,6 +5,7 @@
#include <wlr/types/wlr_output_layout.h>
#include "sway/desktop/transaction.h"
#include "sway/input/seat.h"
+#include "sway/ipc-server.h"
#include "sway/output.h"
#include "sway/tree/arrange.h"
#include "sway/tree/container.h"
@@ -68,13 +69,18 @@ void root_scratchpad_add_container(struct sway_container *con) {
list_add(root->scratchpad, con);
struct sway_seat *seat = input_manager_current_seat();
+ struct sway_node *new_focus = NULL;
if (parent) {
arrange_container(parent);
- seat_set_focus(seat, seat_get_focus_inactive(seat, &parent->node));
- } else {
+ new_focus = seat_get_focus_inactive(seat, &parent->node);
+ }
+ if (!new_focus) {
arrange_workspace(workspace);
- seat_set_focus(seat, seat_get_focus_inactive(seat, &workspace->node));
+ new_focus = seat_get_focus_inactive(seat, &workspace->node);
}
+ seat_set_focus(seat, new_focus);
+
+ ipc_event_window(con, "move");
}
void root_scratchpad_remove_container(struct sway_container *con) {
@@ -85,45 +91,51 @@ void root_scratchpad_remove_container(struct sway_container *con) {
int index = list_find(root->scratchpad, con);
if (index != -1) {
list_del(root->scratchpad, index);
+ ipc_event_window(con, "move");
}
}
void root_scratchpad_show(struct sway_container *con) {
struct sway_seat *seat = input_manager_current_seat();
- struct sway_workspace *ws = seat_get_focused_workspace(seat);
+ struct sway_workspace *new_ws = seat_get_focused_workspace(seat);
+ struct sway_workspace *old_ws = con->workspace;
- // If the current con or any of its parents are in fullscreen mode, we
- // first need to disable it before showing the scratchpad con.
- if (ws->fullscreen) {
- container_set_fullscreen(ws->fullscreen, false);
+ // If the current con or any of its parents are in fullscreen mode, we
+ // first need to disable it before showing the scratchpad con.
+ if (new_ws->fullscreen) {
+ container_set_fullscreen(new_ws->fullscreen, false);
}
// Show the container
- if (con->workspace) {
+ if (old_ws) {
container_detach(con);
}
- workspace_add_floating(ws, con);
+ workspace_add_floating(new_ws, con);
// Make sure the container's center point overlaps this workspace
double center_lx = con->x + con->width / 2;
double center_ly = con->y + con->height / 2;
struct wlr_box workspace_box;
- workspace_get_box(ws, &workspace_box);
+ workspace_get_box(new_ws, &workspace_box);
if (!wlr_box_contains_point(&workspace_box, center_lx, center_ly)) {
// Maybe resize it
- if (con->width > ws->width || con->height > ws->height) {
+ if (con->width > new_ws->width || con->height > new_ws->height) {
container_init_floating(con);
}
// Center it
- double new_lx = ws->x + (ws->width - con->width) / 2;
- double new_ly = ws->y + (ws->height - con->height) / 2;
+ double new_lx = new_ws->x + (new_ws->width - con->width) / 2;
+ double new_ly = new_ws->y + (new_ws->height - con->height) / 2;
container_floating_move_to(con, new_lx, new_ly);
}
- arrange_workspace(ws);
+ arrange_workspace(new_ws);
seat_set_focus(seat, seat_get_focus_inactive(seat, &con->node));
+
+ if (new_ws != old_ws) {
+ ipc_event_window(con, "move");
+ }
}
void root_scratchpad_hide(struct sway_container *con) {
@@ -133,10 +145,12 @@ void root_scratchpad_hide(struct sway_container *con) {
container_detach(con);
arrange_workspace(ws);
- if (&con->node == focus) {
+ if (&con->node == focus || node_has_ancestor(focus, &con->node)) {
seat_set_focus(seat, seat_get_focus_inactive(seat, &ws->node));
}
list_move_to_end(root->scratchpad, con);
+
+ ipc_event_window(con, "move");
}
struct pid_workspace {
diff --git a/sway/tree/view.c b/sway/tree/view.c
index d7110619..5371ee20 100644
--- a/sway/tree/view.c
+++ b/sway/tree/view.c
@@ -437,9 +437,14 @@ void view_execute_criteria(struct sway_view *view) {
wlr_log(WLR_DEBUG, "for_window '%s' matches view %p, cmd: '%s'",
criteria->raw, view, criteria->cmdlist);
list_add(view->executed_criteria, criteria);
- struct cmd_results *res = execute_command(
+ list_t *res_list = execute_command(
criteria->cmdlist, NULL, view->container);
- free_cmd_results(res);
+ while (res_list->length) {
+ struct cmd_results *res = res_list->items[0];
+ free_cmd_results(res);
+ list_del(res_list, 0);
+ }
+ list_free(res_list);
}
list_free(criterias);
}
@@ -454,7 +459,7 @@ static struct sway_workspace *select_workspace(struct sway_view *view) {
for (int i = 0; i < criterias->length; ++i) {
struct criteria *criteria = criterias->items[i];
if (criteria->type == CT_ASSIGN_OUTPUT) {
- struct sway_output *output = output_by_name(criteria->target);
+ struct sway_output *output = output_by_name_or_id(criteria->target);
if (output) {
ws = output_get_active_workspace(output);
break;
@@ -600,7 +605,6 @@ void view_map(struct sway_view *view, struct wlr_surface *wlr_surface,
view_update_title(view, false);
container_update_representation(view->container);
- view_execute_criteria(view);
if (decoration) {
view_update_csd_from_client(view, decoration);
@@ -617,6 +621,8 @@ void view_map(struct sway_view *view, struct wlr_surface *wlr_surface,
}
}
+ view_execute_criteria(view);
+
if (should_focus(view)) {
input_manager_set_focus(&view->container->node);
}
@@ -648,14 +654,8 @@ void view_unmap(struct sway_view *view) {
struct sway_seat *seat;
wl_list_for_each(seat, &server.input->seats, link) {
- if (config->mouse_warping == WARP_CONTAINER) {
- struct sway_node *node = seat_get_focus(seat);
- if (node && node->type == N_CONTAINER) {
- cursor_warp_to_container(seat->cursor, node->sway_container);
- } else if (node && node->type == N_WORKSPACE) {
- cursor_warp_to_workspace(seat->cursor, node->sway_workspace);
- }
- }
+ seat->cursor->image_surface = NULL;
+ seat_consider_warp_to_focus(seat);
}
transaction_commit_dirty();
@@ -674,6 +674,8 @@ void view_update_size(struct sway_view *view, int width, int height) {
container_set_geometry_from_content(view->container);
}
+static const struct sway_view_child_impl subsurface_impl;
+
static void subsurface_get_root_coords(struct sway_view_child *child,
int *root_sx, int *root_sy) {
struct wlr_surface *surface = child->surface;
@@ -689,18 +691,47 @@ static void subsurface_get_root_coords(struct sway_view_child *child,
}
}
+static void subsurface_destroy(struct sway_view_child *child) {
+ if (!sway_assert(child->impl == &subsurface_impl,
+ "Expected a subsurface")) {
+ return;
+ }
+ struct sway_subsurface *subsurface = (struct sway_subsurface *)child;
+ wl_list_remove(&subsurface->destroy.link);
+ free(subsurface);
+}
+
static const struct sway_view_child_impl subsurface_impl = {
.get_root_coords = subsurface_get_root_coords,
+ .destroy = subsurface_destroy,
};
+static void subsurface_handle_destroy(struct wl_listener *listener,
+ void *data) {
+ struct sway_subsurface *subsurface =
+ wl_container_of(listener, subsurface, destroy);
+ struct sway_view_child *child = &subsurface->child;
+ view_child_destroy(child);
+}
+
+static void view_child_damage(struct sway_view_child *child, bool whole);
+
static void view_subsurface_create(struct sway_view *view,
- struct wlr_subsurface *subsurface) {
- struct sway_view_child *child = calloc(1, sizeof(struct sway_view_child));
- if (child == NULL) {
+ struct wlr_subsurface *wlr_subsurface) {
+ struct sway_subsurface *subsurface =
+ calloc(1, sizeof(struct sway_subsurface));
+ if (subsurface == NULL) {
wlr_log(WLR_ERROR, "Allocation failed");
return;
}
- view_child_init(child, &subsurface_impl, view, subsurface->surface);
+ view_child_init(&subsurface->child, &subsurface_impl, view,
+ wlr_subsurface->surface);
+
+ wl_signal_add(&wlr_subsurface->events.destroy, &subsurface->destroy);
+ subsurface->destroy.notify = subsurface_handle_destroy;
+
+ subsurface->child.mapped = true;
+ view_child_damage(&subsurface->child, true);
}
static void view_child_damage(struct sway_view_child *child, bool whole) {
@@ -745,6 +776,7 @@ static void view_child_handle_surface_map(struct wl_listener *listener,
void *data) {
struct sway_view_child *child =
wl_container_of(listener, child, surface_map);
+ child->mapped = true;
view_child_damage(child, true);
}
@@ -753,6 +785,7 @@ static void view_child_handle_surface_unmap(struct wl_listener *listener,
struct sway_view_child *child =
wl_container_of(listener, child, surface_unmap);
view_child_damage(child, true);
+ child->mapped = false;
}
void view_child_init(struct sway_view_child *child,
@@ -771,6 +804,7 @@ void view_child_init(struct sway_view_child *child,
wl_signal_add(&surface->events.destroy, &child->surface_destroy);
child->surface_destroy.notify = view_child_handle_surface_destroy;
+ // Not all child views have a map/unmap event
child->surface_map.notify = view_child_handle_surface_map;
child->surface_unmap.notify = view_child_handle_surface_unmap;
@@ -781,6 +815,10 @@ void view_child_init(struct sway_view_child *child,
}
void view_child_destroy(struct sway_view_child *child) {
+ if (child->mapped && child->view->container != NULL) {
+ view_child_damage(child, true);
+ }
+
wl_list_remove(&child->surface_commit.link);
wl_list_remove(&child->surface_destroy.link);
@@ -824,12 +862,29 @@ struct sway_view *view_from_wlr_surface(struct wlr_surface *wlr_surface) {
return NULL;
}
+static char *escape_pango_markup(const char *buffer) {
+ size_t length = escape_markup_text(buffer, NULL);
+ char *escaped_title = calloc(length + 1, sizeof(char));
+ escape_markup_text(buffer, escaped_title);
+ return escaped_title;
+}
+
static size_t append_prop(char *buffer, const char *value) {
if (!value) {
return 0;
}
- lenient_strcat(buffer, value);
- return strlen(value);
+ // If using pango_markup in font, we need to escape all markup chars
+ // from values to make sure tags are not inserted by clients
+ if (config->pango_markup) {
+ char *escaped_value = escape_pango_markup(value);
+ lenient_strcat(buffer, escaped_value);
+ size_t len = strlen(escaped_value);
+ free(escaped_value);
+ return len;
+ } else {
+ lenient_strcat(buffer, value);
+ return strlen(value);
+ }
}
/**
@@ -838,11 +893,7 @@ static size_t append_prop(char *buffer, const char *value) {
*/
static size_t parse_title_format(struct sway_view *view, char *buffer) {
if (!view->title_format || strcmp(view->title_format, "%title") == 0) {
- const char *title = view_get_title(view);
- if (buffer && title) {
- strcpy(buffer, title);
- }
- return title ? strlen(title) : 0;
+ return append_prop(buffer, view_get_title(view));
}
size_t len = 0;
@@ -882,14 +933,6 @@ static size_t parse_title_format(struct sway_view *view, char *buffer) {
return len;
}
-static char *escape_title(char *buffer) {
- size_t length = escape_markup_text(buffer, NULL);
- char *escaped_title = calloc(length + 1, sizeof(char));
- escape_markup_text(buffer, escaped_title);
- free(buffer);
- return escaped_title;
-}
-
void view_update_title(struct sway_view *view, bool force) {
const char *title = view_get_title(view);
@@ -912,10 +955,6 @@ void view_update_title(struct sway_view *view, bool force) {
return;
}
parse_title_format(view, buffer);
- // now we have the title, but needs to be escaped when using pango markup
- if (config->pango_markup) {
- buffer = escape_title(buffer);
- }
view->container->title = strdup(title);
view->container->formatted_title = buffer;
diff --git a/sway/tree/workspace.c b/sway/tree/workspace.c
index 4be63311..7f18046d 100644
--- a/sway/tree/workspace.c
+++ b/sway/tree/workspace.c
@@ -35,10 +35,8 @@ struct sway_output *workspace_get_initial_output(const char *name) {
struct workspace_config *wsc = workspace_find_config(name);
if (wsc) {
for (int i = 0; i < wsc->outputs->length; i++) {
- struct sway_output *output = output_by_name(wsc->outputs->items[i]);
- if (!output) {
- output = output_by_identifier(wsc->outputs->items[i]);
- }
+ struct sway_output *output =
+ output_by_name_or_id(wsc->outputs->items[i]);
if (output) {
return output;
}
@@ -113,7 +111,10 @@ struct sway_workspace *workspace_create(struct sway_output *output,
// Add output priorities
for (int i = 0; i < wsc->outputs->length; ++i) {
- list_add(ws->output_priority, strdup(wsc->outputs->items[i]));
+ char *name = wsc->outputs->items[i];
+ if (strcmp(name, "*") != 0) {
+ list_add(ws->output_priority, strdup(name));
+ }
}
}
}
@@ -142,7 +143,7 @@ void workspace_destroy(struct sway_workspace *workspace) {
free(workspace->name);
free(workspace->representation);
- free_flat_list(workspace->output_priority);
+ list_free_items_and_destroy(workspace->output_priority);
list_free(workspace->floating);
list_free(workspace->tiling);
list_free(workspace->current.floating);
@@ -182,7 +183,11 @@ static bool workspace_valid_on_output(const char *output_name,
const char *ws_name) {
struct workspace_config *wsc = workspace_find_config(ws_name);
char identifier[128];
- struct sway_output *output = output_by_name(output_name);
+ struct sway_output *output = output_by_name_or_id(output_name);
+ if (!output) {
+ return false;
+ }
+ output_name = output->wlr_output->name;
output_get_identifier(identifier, sizeof(identifier), output);
if (!wsc) {
@@ -190,7 +195,8 @@ static bool workspace_valid_on_output(const char *output_name,
}
for (int i = 0; i < wsc->outputs->length; i++) {
- if (strcmp(wsc->outputs->items[i], output_name) == 0 ||
+ if (strcmp(wsc->outputs->items[i], "*") == 0 ||
+ strcmp(wsc->outputs->items[i], output_name) == 0 ||
strcmp(wsc->outputs->items[i], identifier) == 0) {
return true;
}
@@ -286,6 +292,14 @@ char *workspace_next_name(const char *output_name) {
// assignments primarily, falling back to bindings and numbers.
struct sway_mode *mode = config->current_mode;
+ char identifier[128];
+ struct sway_output *output = output_by_name_or_id(output_name);
+ if (!output) {
+ return NULL;
+ }
+ output_name = output->wlr_output->name;
+ output_get_identifier(identifier, sizeof(identifier), output);
+
int order = INT_MAX;
char *target = NULL;
for (int i = 0; i < mode->keysym_bindings->length; ++i) {
@@ -304,7 +318,9 @@ char *workspace_next_name(const char *output_name) {
}
bool found = false;
for (int j = 0; j < wsc->outputs->length; ++j) {
- if (strcmp(wsc->outputs->items[j], output_name) == 0) {
+ if (strcmp(wsc->outputs->items[j], "*") == 0 ||
+ strcmp(wsc->outputs->items[j], output_name) == 0 ||
+ strcmp(wsc->outputs->items[j], identifier) == 0) {
found = true;
free(target);
target = strdup(wsc->workspace);
@@ -525,13 +541,19 @@ void workspace_output_add_priority(struct sway_workspace *workspace,
struct sway_output *workspace_output_get_highest_available(
struct sway_workspace *ws, struct sway_output *exclude) {
+ char exclude_id[128] = {'\0'};
+ if (exclude) {
+ output_get_identifier(exclude_id, sizeof(exclude_id), exclude);
+ }
+
for (int i = 0; i < ws->output_priority->length; i++) {
char *name = ws->output_priority->items[i];
- if (exclude && strcasecmp(name, exclude->wlr_output->name) == 0) {
+ if (exclude && (strcmp(name, exclude->wlr_output->name) == 0
+ || strcmp(name, exclude_id) == 0)) {
continue;
}
- struct sway_output *output = output_by_name(name);
+ struct sway_output *output = output_by_name_or_id(name);
if (output) {
return output;
}