aboutsummaryrefslogtreecommitdiff
path: root/sway/desktop
diff options
context:
space:
mode:
authorDrew DeVault <sir@cmpwn.com>2018-07-23 20:27:56 -0400
committerDrew DeVault <sir@cmpwn.com>2018-07-23 20:31:11 -0400
commitf4b882475eee7a81c206c7825616cc4656b2f60b (patch)
tree38e6ebf81b235424f105dcbcbb194e5e9eac70c0 /sway/desktop
parentacd79e1505c06089e4fb9fb6c0c6e1d351ba9176 (diff)
parent224ade138208e9aa525423cbfbd643aa9d9b63c3 (diff)
Merge branch 'master' into pid-workspaces
Diffstat (limited to 'sway/desktop')
-rw-r--r--sway/desktop/desktop.c9
-rw-r--r--sway/desktop/idle_inhibit_v1.c80
-rw-r--r--sway/desktop/layer_shell.c21
-rw-r--r--sway/desktop/output.c957
-rw-r--r--sway/desktop/render.c919
-rw-r--r--sway/desktop/transaction.c107
-rw-r--r--sway/desktop/xdg_shell.c115
-rw-r--r--sway/desktop/xdg_shell_v6.c115
-rw-r--r--sway/desktop/xwayland.c189
9 files changed, 1509 insertions, 1003 deletions
diff --git a/sway/desktop/desktop.c b/sway/desktop/desktop.c
index e495790c..6575519d 100644
--- a/sway/desktop/desktop.c
+++ b/sway/desktop/desktop.c
@@ -13,3 +13,12 @@ void desktop_damage_surface(struct wlr_surface *surface, double lx, double ly,
}
}
}
+
+void desktop_damage_whole_container(struct sway_container *con) {
+ for (int i = 0; i < root_container.children->length; ++i) {
+ struct sway_container *cont = root_container.children->items[i];
+ if (cont->type == C_OUTPUT) {
+ output_damage_whole_container(cont->sway_output, con);
+ }
+ }
+}
diff --git a/sway/desktop/idle_inhibit_v1.c b/sway/desktop/idle_inhibit_v1.c
new file mode 100644
index 00000000..da17d0f2
--- /dev/null
+++ b/sway/desktop/idle_inhibit_v1.c
@@ -0,0 +1,80 @@
+#include <stdlib.h>
+#include <wlr/types/wlr_idle.h>
+#include "log.h"
+#include "sway/desktop/idle_inhibit_v1.h"
+#include "sway/tree/view.h"
+#include "sway/server.h"
+
+
+static void handle_destroy(struct wl_listener *listener, void *data) {
+ struct sway_idle_inhibitor_v1 *inhibitor =
+ wl_container_of(listener, inhibitor, destroy);
+ wlr_log(WLR_DEBUG, "Sway idle inhibitor destroyed");
+ wl_list_remove(&inhibitor->link);
+ wl_list_remove(&inhibitor->destroy.link);
+ idle_inhibit_v1_check_active(inhibitor->manager);
+ free(inhibitor);
+}
+
+void handle_idle_inhibitor_v1(struct wl_listener *listener, void *data) {
+ struct wlr_idle_inhibitor_v1 *wlr_inhibitor = data;
+ struct sway_idle_inhibit_manager_v1 *manager =
+ wl_container_of(listener, manager, new_idle_inhibitor_v1);
+ wlr_log(WLR_DEBUG, "New sway idle inhibitor");
+
+ struct sway_idle_inhibitor_v1 *inhibitor =
+ calloc(1, sizeof(struct sway_idle_inhibitor_v1));
+ if (!inhibitor) {
+ return;
+ }
+
+ inhibitor->manager = manager;
+ inhibitor->view = view_from_wlr_surface(wlr_inhibitor->surface);
+ wl_list_insert(&manager->inhibitors, &inhibitor->link);
+
+
+ inhibitor->destroy.notify = handle_destroy;
+ wl_signal_add(&wlr_inhibitor->events.destroy, &inhibitor->destroy);
+
+ idle_inhibit_v1_check_active(manager);
+}
+
+void idle_inhibit_v1_check_active(
+ struct sway_idle_inhibit_manager_v1 *manager) {
+ struct sway_idle_inhibitor_v1 *inhibitor;
+ bool inhibited = false;
+ wl_list_for_each(inhibitor, &manager->inhibitors, link) {
+ if (!inhibitor->view) {
+ /* Cannot guess if view is visible so assume it is */
+ inhibited = true;
+ break;
+ }
+ if (view_is_visible(inhibitor->view)) {
+ inhibited = true;
+ break;
+ }
+ }
+ wlr_idle_set_enabled(manager->idle, NULL, !inhibited);
+}
+
+struct sway_idle_inhibit_manager_v1 *sway_idle_inhibit_manager_v1_create(
+ struct wl_display *wl_display, struct wlr_idle *idle) {
+ struct sway_idle_inhibit_manager_v1 *manager =
+ calloc(1, sizeof(struct sway_idle_inhibit_manager_v1));
+ if (!manager) {
+ return NULL;
+ }
+
+ manager->wlr_manager = wlr_idle_inhibit_v1_create(wl_display);
+ if (!manager->wlr_manager) {
+ free(manager);
+ return NULL;
+ }
+ manager->idle = idle;
+ wl_signal_add(&manager->wlr_manager->events.new_inhibitor,
+ &manager->new_idle_inhibitor_v1);
+ manager->new_idle_inhibitor_v1.notify = handle_idle_inhibitor_v1;
+ wl_list_init(&manager->inhibitors);
+
+ return manager;
+}
diff --git a/sway/desktop/layer_shell.c b/sway/desktop/layer_shell.c
index de1fe349..a7d96717 100644
--- a/sway/desktop/layer_shell.c
+++ b/sway/desktop/layer_shell.c
@@ -12,7 +12,6 @@
#include "sway/layers.h"
#include "sway/output.h"
#include "sway/server.h"
-#include "sway/tree/arrange.h"
#include "sway/tree/layout.h"
#include "log.h"
@@ -174,9 +173,9 @@ void arrange_layers(struct sway_output *output) {
if (memcmp(&usable_area, &output->usable_area,
sizeof(struct wlr_box)) != 0) {
- wlr_log(L_DEBUG, "Usable area changed, rearranging output");
+ wlr_log(WLR_DEBUG, "Usable area changed, rearranging output");
memcpy(&output->usable_area, &usable_area, sizeof(struct wlr_box));
- arrange_and_commit(output->swayc);
+ container_set_dirty(output->swayc);
}
// Arrange non-exlusive surfaces from top->bottom
@@ -269,7 +268,7 @@ static void unmap(struct sway_layer_surface *sway_layer) {
static void handle_destroy(struct wl_listener *listener, void *data) {
struct sway_layer_surface *sway_layer =
wl_container_of(listener, sway_layer, destroy);
- wlr_log(L_DEBUG, "Layer surface destroyed (%s)",
+ wlr_log(WLR_DEBUG, "Layer surface destroyed (%s)",
sway_layer->layer_surface->namespace);
if (sway_layer->layer_surface->mapped) {
unmap(sway_layer);
@@ -316,7 +315,7 @@ void handle_layer_shell_surface(struct wl_listener *listener, void *data) {
struct wlr_layer_surface *layer_surface = data;
struct sway_server *server =
wl_container_of(listener, server, layer_shell_surface);
- wlr_log(L_DEBUG, "new layer surface: namespace %s layer %d anchor %d "
+ wlr_log(WLR_DEBUG, "new layer surface: namespace %s layer %d anchor %d "
"size %dx%d margin %d,%d,%d,%d",
layer_surface->namespace, layer_surface->layer, layer_surface->layer,
layer_surface->client_pending.desired_width,
@@ -326,12 +325,6 @@ void handle_layer_shell_surface(struct wl_listener *listener, void *data) {
layer_surface->client_pending.margin.bottom,
layer_surface->client_pending.margin.left);
- struct sway_layer_surface *sway_layer =
- calloc(1, sizeof(struct sway_layer_surface));
- if (!sway_layer) {
- return;
- }
-
if (!layer_surface->output) {
// Assign last active output
struct sway_container *output = NULL;
@@ -353,6 +346,12 @@ void handle_layer_shell_surface(struct wl_listener *listener, void *data) {
layer_surface->output = output->sway_output->wlr_output;
}
+ struct sway_layer_surface *sway_layer =
+ calloc(1, sizeof(struct sway_layer_surface));
+ if (!sway_layer) {
+ return;
+ }
+
sway_layer->surface_commit.notify = handle_surface_commit;
wl_signal_add(&layer_surface->surface->events.commit,
&sway_layer->surface_commit);
diff --git a/sway/desktop/output.c b/sway/desktop/output.c
index 1e7494b3..a206ac6b 100644
--- a/sway/desktop/output.c
+++ b/sway/desktop/output.c
@@ -56,33 +56,15 @@ static void rotate_child_position(double *sx, double *sy, double sw, double sh,
*sy = ry + ph/2 - sh/2;
}
-/**
- * Contains a surface's root geometry information. For instance, when rendering
- * a popup, this will contain the parent view's position and size.
- */
-struct root_geometry {
- double x, y;
- int width, height;
- float rotation;
-};
-
-struct render_data {
- struct root_geometry root_geo;
- struct sway_output *output;
- pixman_region32_t *damage;
- struct sway_view *view;
- float alpha;
-};
-
-static bool get_surface_box(struct root_geometry *geo,
+bool output_get_surface_box(struct root_geometry *geo,
struct sway_output *output, struct wlr_surface *surface, int sx, int sy,
struct wlr_box *surface_box) {
if (!wlr_surface_has_buffer(surface)) {
return false;
}
- int sw = surface->current->width;
- int sh = surface->current->height;
+ int sw = surface->current.width;
+ int sh = surface->current.height;
double _sx = sx, _sy = sy;
rotate_child_position(&_sx, &_sy, sw, sh, geo->width, geo->height,
@@ -110,24 +92,23 @@ static bool get_surface_box(struct root_geometry *geo,
return wlr_box_intersection(&output_box, &rotated_box, &intersection);
}
-static void surface_for_each_surface(struct wlr_surface *surface,
+void output_surface_for_each_surface(struct wlr_surface *surface,
double ox, double oy, struct root_geometry *geo,
wlr_surface_iterator_func_t iterator, void *user_data) {
geo->x = ox;
geo->y = oy;
- geo->width = surface->current->width;
- geo->height = surface->current->height;
+ geo->width = surface->current.width;
+ geo->height = surface->current.height;
geo->rotation = 0;
wlr_surface_for_each_surface(surface, iterator, user_data);
}
-static void output_view_for_each_surface(struct sway_view *view,
- struct root_geometry *geo, wlr_surface_iterator_func_t iterator,
- void *user_data) {
- struct render_data *data = user_data;
- geo->x = view->swayc->current.view_x - data->output->swayc->current.swayc_x;
- geo->y = view->swayc->current.view_y - data->output->swayc->current.swayc_y;
+void output_view_for_each_surface(struct sway_view *view,
+ struct sway_output *output, struct root_geometry *geo,
+ wlr_surface_iterator_func_t iterator, void *user_data) {
+ geo->x = view->swayc->current.view_x - output->swayc->current.swayc_x;
+ geo->y = view->swayc->current.view_y - output->swayc->current.swayc_y;
geo->width = view->swayc->current.view_width;
geo->height = view->swayc->current.view_height;
geo->rotation = 0; // TODO
@@ -135,20 +116,20 @@ static void output_view_for_each_surface(struct sway_view *view,
view_for_each_surface(view, iterator, user_data);
}
-static void layer_for_each_surface(struct wl_list *layer_surfaces,
+void output_layer_for_each_surface(struct wl_list *layer_surfaces,
struct root_geometry *geo, wlr_surface_iterator_func_t iterator,
void *user_data) {
struct sway_layer_surface *layer_surface;
wl_list_for_each(layer_surface, layer_surfaces, link) {
struct wlr_layer_surface *wlr_layer_surface =
layer_surface->layer_surface;
- surface_for_each_surface(wlr_layer_surface->surface,
+ output_surface_for_each_surface(wlr_layer_surface->surface,
layer_surface->geo.x, layer_surface->geo.y, geo, iterator,
user_data);
}
}
-static void unmanaged_for_each_surface(struct wl_list *unmanaged,
+void output_unmanaged_for_each_surface(struct wl_list *unmanaged,
struct sway_output *output, struct root_geometry *geo,
wlr_surface_iterator_func_t iterator, void *user_data) {
struct sway_xwayland_unmanaged *unmanaged_surface;
@@ -158,12 +139,12 @@ static void unmanaged_for_each_surface(struct wl_list *unmanaged,
double ox = unmanaged_surface->lx - output->swayc->current.swayc_x;
double oy = unmanaged_surface->ly - output->swayc->current.swayc_y;
- surface_for_each_surface(xsurface->surface, ox, oy, geo,
+ output_surface_for_each_surface(xsurface->surface, ox, oy, geo,
iterator, user_data);
}
}
-static void drag_icons_for_each_surface(struct wl_list *drag_icons,
+void output_drag_icons_for_each_surface(struct wl_list *drag_icons,
struct sway_output *output, struct root_geometry *geo,
wlr_surface_iterator_func_t iterator, void *user_data) {
struct sway_drag_icon *drag_icon;
@@ -172,7 +153,7 @@ static void drag_icons_for_each_surface(struct wl_list *drag_icons,
double oy = drag_icon->y - output->swayc->y;
if (drag_icon->wlr_drag_icon->mapped) {
- surface_for_each_surface(drag_icon->wlr_drag_icon->surface,
+ output_surface_for_each_surface(drag_icon->wlr_drag_icon->surface,
ox, oy, geo, iterator, user_data);
}
}
@@ -185,722 +166,7 @@ static void scale_box(struct wlr_box *box, float scale) {
box->height *= 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);
- assert(renderer);
-
- struct wlr_box box = {
- .x = rect->x1,
- .y = rect->y1,
- .width = rect->x2 - rect->x1,
- .height = rect->y2 - rect->y1,
- };
-
- int ow, oh;
- wlr_output_transformed_resolution(wlr_output, &ow, &oh);
-
- enum wl_output_transform transform =
- wlr_output_transform_invert(wlr_output->transform);
- wlr_box_transform(&box, transform, ow, oh, &box);
-
- wlr_renderer_scissor(renderer, &box);
-}
-
-static void render_texture(struct wlr_output *wlr_output,
- pixman_region32_t *output_damage, struct wlr_texture *texture,
- const struct wlr_box *box, const float matrix[static 9], float alpha) {
- struct wlr_renderer *renderer =
- wlr_backend_get_renderer(wlr_output->backend);
-
- pixman_region32_t damage;
- pixman_region32_init(&damage);
- pixman_region32_union_rect(&damage, &damage, box->x, box->y,
- box->width, box->height);
- pixman_region32_intersect(&damage, &damage, output_damage);
- bool damaged = pixman_region32_not_empty(&damage);
- if (!damaged) {
- goto damage_finish;
- }
-
- int nrects;
- pixman_box32_t *rects = pixman_region32_rectangles(&damage, &nrects);
- for (int i = 0; i < nrects; ++i) {
- scissor_output(wlr_output, &rects[i]);
- wlr_render_texture_with_matrix(renderer, texture, matrix, alpha);
- }
-
-damage_finish:
- pixman_region32_fini(&damage);
-}
-
-static void render_surface_iterator(struct wlr_surface *surface, int sx, int sy,
- void *_data) {
- struct render_data *data = _data;
- struct wlr_output *wlr_output = data->output->wlr_output;
- float rotation = data->root_geo.rotation;
- pixman_region32_t *output_damage = data->damage;
- float alpha = data->alpha;
-
- struct wlr_texture *texture = wlr_surface_get_texture(surface);
- if (!texture) {
- return;
- }
-
- struct wlr_box box;
- bool intersects = get_surface_box(&data->root_geo, data->output, surface,
- sx, sy, &box);
- if (!intersects) {
- return;
- }
-
- scale_box(&box, wlr_output->scale);
-
- float matrix[9];
- enum wl_output_transform transform =
- wlr_output_transform_invert(surface->current->transform);
- wlr_matrix_project_box(matrix, &box, transform, rotation,
- wlr_output->transform_matrix);
-
- render_texture(wlr_output, output_damage, texture, &box, matrix, alpha);
-}
-
-static void render_layer(struct sway_output *output,
- pixman_region32_t *damage, struct wl_list *layer_surfaces) {
- struct render_data data = {
- .output = output,
- .damage = damage,
- .alpha = 1.0f,
- };
- layer_for_each_surface(layer_surfaces, &data.root_geo,
- render_surface_iterator, &data);
-}
-
-static void render_unmanaged(struct sway_output *output,
- pixman_region32_t *damage, struct wl_list *unmanaged) {
- struct render_data data = {
- .output = output,
- .damage = damage,
- .alpha = 1.0f,
- };
- unmanaged_for_each_surface(unmanaged, output, &data.root_geo,
- render_surface_iterator, &data);
-}
-
-static void render_drag_icons(struct sway_output *output,
- pixman_region32_t *damage, struct wl_list *drag_icons) {
- struct render_data data = {
- .output = output,
- .damage = damage,
- .alpha = 1.0f,
- };
- drag_icons_for_each_surface(drag_icons, output, &data.root_geo,
- render_surface_iterator, &data);
-}
-
-static void render_rect(struct wlr_output *wlr_output,
- pixman_region32_t *output_damage, const struct wlr_box *_box,
- float color[static 4]) {
- struct wlr_renderer *renderer =
- wlr_backend_get_renderer(wlr_output->backend);
-
- struct wlr_box box;
- memcpy(&box, _box, sizeof(struct wlr_box));
- box.x -= wlr_output->lx * wlr_output->scale;
- box.y -= wlr_output->ly * wlr_output->scale;
-
- pixman_region32_t damage;
- pixman_region32_init(&damage);
- pixman_region32_union_rect(&damage, &damage, box.x, box.y,
- box.width, box.height);
- pixman_region32_intersect(&damage, &damage, output_damage);
- bool damaged = pixman_region32_not_empty(&damage);
- if (!damaged) {
- goto damage_finish;
- }
-
- int nrects;
- pixman_box32_t *rects = pixman_region32_rectangles(&damage, &nrects);
- for (int i = 0; i < nrects; ++i) {
- scissor_output(wlr_output, &rects[i]);
- wlr_render_rect(renderer, &box, color,
- wlr_output->transform_matrix);
- }
-
-damage_finish:
- pixman_region32_fini(&damage);
-}
-
-static void premultiply_alpha(float color[4], float opacity) {
- color[3] *= opacity;
- color[0] *= color[3];
- color[1] *= color[3];
- color[2] *= color[3];
-}
-
-static void render_view_surfaces(struct sway_view *view,
- struct sway_output *output, pixman_region32_t *damage, float alpha) {
- struct render_data data = {
- .output = output,
- .damage = damage,
- .view = view,
- .alpha = alpha,
- };
- output_view_for_each_surface(
- view, &data.root_geo, render_surface_iterator, &data);
-}
-
-static void render_saved_view(struct sway_view *view,
- struct sway_output *output, pixman_region32_t *damage, float alpha) {
- struct wlr_output *wlr_output = output->wlr_output;
-
- int width, height;
- struct wlr_texture *texture =
- transaction_get_saved_texture(view, &width, &height);
- if (!texture) {
- return;
- }
- struct wlr_box box = {
- .x = view->swayc->current.view_x - output->swayc->current.swayc_x,
- .y = view->swayc->current.view_y - output->swayc->current.swayc_y,
- .width = width,
- .height = height,
- };
-
- struct wlr_box output_box = {
- .width = output->swayc->current.swayc_width,
- .height = output->swayc->current.swayc_height,
- };
-
- struct wlr_box intersection;
- bool intersects = wlr_box_intersection(&output_box, &box, &intersection);
- if (!intersects) {
- return;
- }
-
- scale_box(&box, wlr_output->scale);
-
- float matrix[9];
- wlr_matrix_project_box(matrix, &box, WL_OUTPUT_TRANSFORM_NORMAL, 0,
- wlr_output->transform_matrix);
-
- render_texture(wlr_output, damage, texture, &box, matrix, alpha);
-}
-
-/**
- * Render a view's surface and left/bottom/right borders.
- */
-static void render_view(struct sway_output *output, pixman_region32_t *damage,
- struct sway_container *con, struct border_colors *colors) {
- struct sway_view *view = con->sway_view;
- if (view->swayc->instructions->length) {
- render_saved_view(view, output, damage, view->swayc->alpha);
- } else {
- render_view_surfaces(view, output, damage, view->swayc->alpha);
- }
-
- struct wlr_box box;
- float output_scale = output->wlr_output->scale;
- float color[4];
- struct sway_container_state *state = &con->current;
-
- if (state->border != B_NONE) {
- if (state->border_left) {
- memcpy(&color, colors->child_border, sizeof(float) * 4);
- premultiply_alpha(color, con->alpha);
- box.x = state->swayc_x;
- box.y = state->view_y;
- box.width = state->border_thickness;
- box.height = state->view_height;
- scale_box(&box, output_scale);
- render_rect(output->wlr_output, damage, &box, color);
- }
-
- if (state->border_right) {
- if (state->parent->current.children->length == 1
- && state->parent->current.layout == L_HORIZ) {
- memcpy(&color, colors->indicator, sizeof(float) * 4);
- } else {
- memcpy(&color, colors->child_border, sizeof(float) * 4);
- }
- premultiply_alpha(color, con->alpha);
- box.x = state->view_x + state->view_width;
- box.y = state->view_y;
- box.width = state->border_thickness;
- box.height = state->view_height;
- scale_box(&box, output_scale);
- render_rect(output->wlr_output, damage, &box, color);
- }
-
- if (state->border_bottom) {
- if (state->parent->current.children->length == 1
- && con->current.parent->current.layout == L_VERT) {
- memcpy(&color, colors->indicator, sizeof(float) * 4);
- } else {
- memcpy(&color, colors->child_border, sizeof(float) * 4);
- }
- premultiply_alpha(color, con->alpha);
- box.x = state->swayc_x;
- box.y = state->view_y + state->view_height;
- box.width = state->swayc_width;
- box.height = state->border_thickness;
- scale_box(&box, output_scale);
- render_rect(output->wlr_output, damage, &box, color);
- }
- }
-}
-
-/**
- * Render a titlebar.
- *
- * Care must be taken not to render over the same pixel multiple times,
- * otherwise the colors will be incorrect when using opacity.
- *
- * The height is: 1px border, 3px padding, font height, 3px padding, 1px border
- * The left side for L_TABBED is: 1px border, 2px padding, title
- * The left side for other layouts is: 3px padding, title
- */
-static void render_titlebar(struct sway_output *output,
- pixman_region32_t *output_damage, struct sway_container *con,
- int x, int y, int width,
- struct border_colors *colors, struct wlr_texture *title_texture,
- struct wlr_texture *marks_texture) {
- struct wlr_box box;
- float color[4];
- struct sway_container_state *state = &con->current;
- float output_scale = output->wlr_output->scale;
- enum sway_container_layout layout = state->parent->current.layout;
- list_t *children = state->parent->current.children;
- bool is_last_child = children->items[children->length - 1] == con;
- double output_x = output->swayc->current.swayc_x;
- double output_y = output->swayc->current.swayc_y;
-
- // Single pixel bar above title
- memcpy(&color, colors->border, sizeof(float) * 4);
- premultiply_alpha(color, con->alpha);
- box.x = x;
- box.y = y;
- box.width = width;
- box.height = TITLEBAR_BORDER_THICKNESS;
- scale_box(&box, output_scale);
- render_rect(output->wlr_output, output_damage, &box, color);
-
- // Single pixel bar below title
- size_t left_offset = 0, right_offset = 0;
- bool connects_sides = false;
- if (layout == L_HORIZ || layout == L_VERT ||
- (layout == L_STACKED && is_last_child)) {
- if (con->type == C_VIEW) {
- left_offset = state->border_left * state->border_thickness;
- right_offset = state->border_right * state->border_thickness;
- connects_sides = true;
- }
- }
- box.x = x + left_offset;
- box.y = y + container_titlebar_height() - TITLEBAR_BORDER_THICKNESS;
- box.width = width - left_offset - right_offset;
- box.height = TITLEBAR_BORDER_THICKNESS;
- scale_box(&box, output_scale);
- render_rect(output->wlr_output, output_damage, &box, color);
-
- if (layout == L_TABBED) {
- // Single pixel left edge
- box.x = x;
- box.y = y + TITLEBAR_BORDER_THICKNESS;
- box.width = TITLEBAR_BORDER_THICKNESS;
- box.height =
- container_titlebar_height() - TITLEBAR_BORDER_THICKNESS * 2;
- scale_box(&box, output_scale);
- render_rect(output->wlr_output, output_damage, &box, color);
-
- // Single pixel right edge
- box.x = (x + width - TITLEBAR_BORDER_THICKNESS) * output_scale;
- render_rect(output->wlr_output, output_damage, &box, color);
- }
-
- size_t inner_width = width - TITLEBAR_H_PADDING * 2;
-
- // Marks
- size_t marks_width = 0;
- if (config->show_marks && marks_texture) {
- struct wlr_box texture_box;
- wlr_texture_get_size(marks_texture,
- &texture_box.width, &texture_box.height);
- texture_box.x = (x - output_x + width - TITLEBAR_H_PADDING)
- * output_scale - texture_box.width;
- texture_box.y = (y - output_y + TITLEBAR_V_PADDING) * output_scale;
-
- float matrix[9];
- wlr_matrix_project_box(matrix, &texture_box,
- WL_OUTPUT_TRANSFORM_NORMAL,
- 0.0, output->wlr_output->transform_matrix);
-
- if (inner_width * output_scale < texture_box.width) {
- texture_box.width = inner_width * output_scale;
- }
- render_texture(output->wlr_output, output_damage, marks_texture,
- &texture_box, matrix, con->alpha);
- marks_width = texture_box.width;
- }
-
- // Title text
- size_t title_width = 0;
- if (title_texture) {
- struct wlr_box texture_box;
- wlr_texture_get_size(title_texture,
- &texture_box.width, &texture_box.height);
- texture_box.x = (x - output_x + TITLEBAR_H_PADDING) * output_scale;
- texture_box.y = (y - output_y + TITLEBAR_V_PADDING) * output_scale;
-
- float matrix[9];
- wlr_matrix_project_box(matrix, &texture_box,
- WL_OUTPUT_TRANSFORM_NORMAL,
- 0.0, output->wlr_output->transform_matrix);
-
- if (inner_width * output_scale - marks_width < texture_box.width) {
- texture_box.width = inner_width * output_scale - marks_width;
- }
- render_texture(output->wlr_output, output_damage, title_texture,
- &texture_box, matrix, con->alpha);
- title_width = texture_box.width;
- }
-
- // Padding above title
- memcpy(&color, colors->background, sizeof(float) * 4);
- premultiply_alpha(color, con->alpha);
- box.x = x + (layout == L_TABBED) * TITLEBAR_BORDER_THICKNESS;
- box.y = y + TITLEBAR_BORDER_THICKNESS;
- box.width = width - (layout == L_TABBED) * TITLEBAR_BORDER_THICKNESS * 2;
- box.height = TITLEBAR_V_PADDING - TITLEBAR_BORDER_THICKNESS;
- scale_box(&box, output_scale);
- render_rect(output->wlr_output, output_damage, &box, color);
-
- // Padding below title
- box.y = (y + TITLEBAR_V_PADDING + config->font_height) * output_scale;
- render_rect(output->wlr_output, output_damage, &box, color);
-
- // Filler between title and marks
- box.width = inner_width * output_scale - title_width - marks_width;
- if (box.width > 0) {
- box.x = (x + TITLEBAR_H_PADDING) * output_scale + title_width;
- box.y = (y + TITLEBAR_V_PADDING) * output_scale;
- box.height = config->font_height * output_scale;
- render_rect(output->wlr_output, output_damage, &box, color);
- }
-
- // Padding left of title
- left_offset = (layout == L_TABBED) * TITLEBAR_BORDER_THICKNESS;
- box.x = x + left_offset;
- box.y = y + TITLEBAR_V_PADDING;
- box.width = TITLEBAR_H_PADDING - left_offset;
- box.height = config->font_height;
- scale_box(&box, output_scale);
- render_rect(output->wlr_output, output_damage, &box, color);
-
- // Padding right of marks
- right_offset = (layout == L_TABBED) * TITLEBAR_BORDER_THICKNESS;
- box.x = x + width - TITLEBAR_H_PADDING;
- box.y = y + TITLEBAR_V_PADDING;
- box.width = TITLEBAR_H_PADDING - right_offset;
- box.height = config->font_height;
- scale_box(&box, output_scale);
- render_rect(output->wlr_output, output_damage, &box, color);
-
- if (connects_sides) {
- // Left pixel in line with bottom bar
- box.x = x;
- box.y = y + container_titlebar_height() - TITLEBAR_BORDER_THICKNESS;
- box.width = state->border_thickness * state->border_left;
- box.height = TITLEBAR_BORDER_THICKNESS;
- scale_box(&box, output_scale);
- render_rect(output->wlr_output, output_damage, &box, color);
-
- // Right pixel in line with bottom bar
- box.x = x + width - state->border_thickness * state->border_right;
- box.y = y + container_titlebar_height() - TITLEBAR_BORDER_THICKNESS;
- box.width = state->border_thickness * state->border_right;
- box.height = TITLEBAR_BORDER_THICKNESS;
- scale_box(&box, output_scale);
- render_rect(output->wlr_output, output_damage, &box, color);
- }
-}
-
-/**
- * Render the top border line for a view using "border pixel".
- */
-static void render_top_border(struct sway_output *output,
- pixman_region32_t *output_damage, struct sway_container *con,
- struct border_colors *colors) {
- struct sway_container_state *state = &con->current;
- if (!state->border_top) {
- return;
- }
- struct wlr_box box;
- float color[4];
- float output_scale = output->wlr_output->scale;
-
- // Child border - top edge
- memcpy(&color, colors->child_border, sizeof(float) * 4);
- premultiply_alpha(color, con->alpha);
- box.x = state->swayc_x;
- box.y = state->swayc_y;
- box.width = state->swayc_width;
- box.height = state->border_thickness;
- scale_box(&box, output_scale);
- render_rect(output->wlr_output, output_damage, &box, color);
-}
-
-static void render_container(struct sway_output *output,
- pixman_region32_t *damage, struct sway_container *con, bool parent_focused);
-
-/**
- * Render a container's children using a L_HORIZ or L_VERT layout.
- *
- * Wrap child views in borders and leave child containers borderless because
- * they'll apply their own borders to their children.
- */
-static void render_container_simple(struct sway_output *output,
- pixman_region32_t *damage, struct sway_container *con,
- bool parent_focused) {
- struct sway_seat *seat = input_manager_current_seat(input_manager);
- struct sway_container *focus = seat_get_focus(seat);
-
- for (int i = 0; i < con->current.children->length; ++i) {
- struct sway_container *child = con->current.children->items[i];
-
- if (child->type == C_VIEW) {
- struct sway_view *view = child->sway_view;
- struct border_colors *colors;
- struct wlr_texture *title_texture;
- struct wlr_texture *marks_texture;
- struct sway_container_state *state = &child->current;
-
- if (focus == child || parent_focused) {
- colors = &config->border_colors.focused;
- title_texture = child->title_focused;
- marks_texture = view->marks_focused;
- } else if (seat_get_focus_inactive(seat, con) == child) {
- colors = &config->border_colors.focused_inactive;
- title_texture = child->title_focused_inactive;
- marks_texture = view->marks_focused_inactive;
- } else {
- colors = &config->border_colors.unfocused;
- title_texture = child->title_unfocused;
- marks_texture = view->marks_unfocused;
- }
-
- if (state->border == B_NORMAL) {
- render_titlebar(output, damage, child, state->swayc_x,
- state->swayc_y, state->swayc_width, colors,
- title_texture, marks_texture);
- } else {
- render_top_border(output, damage, child, colors);
- }
- render_view(output, damage, child, colors);
- } else {
- render_container(output, damage, child,
- parent_focused || focus == child);
- }
- }
-}
-
-/**
- * Render a container's children using the L_TABBED layout.
- */
-static void render_container_tabbed(struct sway_output *output,
- pixman_region32_t *damage, struct sway_container *con,
- bool parent_focused) {
- if (!con->current.children->length) {
- return;
- }
- struct sway_seat *seat = input_manager_current_seat(input_manager);
- struct sway_container *focus = seat_get_focus(seat);
- struct sway_container *current = seat_get_active_current_child(seat, con);
- struct border_colors *current_colors = &config->border_colors.unfocused;
- struct sway_container_state *pstate = &con->current;
-
- // Render tabs
- for (int i = 0; i < con->current.children->length; ++i) {
- struct sway_container *child = con->current.children->items[i];
- struct sway_view *view = child->type == C_VIEW ? child->sway_view : NULL;
- struct sway_container_state *cstate = &child->current;
- struct border_colors *colors;
- struct wlr_texture *title_texture;
- struct wlr_texture *marks_texture;
-
- if (focus == child || parent_focused) {
- colors = &config->border_colors.focused;
- title_texture = child->title_focused;
- marks_texture = view ? view->marks_focused : NULL;
- } else if (child == current) {
- colors = &config->border_colors.focused_inactive;
- title_texture = child->title_focused_inactive;
- marks_texture = view ? view->marks_focused_inactive : NULL;
- } else {
- colors = &config->border_colors.unfocused;
- title_texture = child->title_unfocused;
- marks_texture = view ? view->marks_unfocused : NULL;
- }
-
- int tab_width = pstate->swayc_width / pstate->children->length;
- int x = pstate->swayc_x + tab_width * i;
- // Make last tab use the remaining width of the parent
- if (i == pstate->children->length - 1) {
- tab_width = pstate->swayc_width - tab_width * i;
- }
-
- render_titlebar(output, damage, child, x, cstate->swayc_y, tab_width,
- colors, title_texture, marks_texture);
-
- if (child == current) {
- current_colors = colors;
- }
- }
-
- // Render surface and left/right/bottom borders
- if (current) {
- if (current->type == C_VIEW) {
- render_view(output, damage, current, current_colors);
- } else {
- render_container(output, damage, current,
- parent_focused || current == focus);
- }
- }
-}
-
-/**
- * Render a container's children using the L_STACKED layout.
- */
-static void render_container_stacked(struct sway_output *output,
- pixman_region32_t *damage, struct sway_container *con,
- bool parent_focused) {
- if (!con->current.children->length) {
- return;
- }
- struct sway_seat *seat = input_manager_current_seat(input_manager);
- struct sway_container *focus = seat_get_focus(seat);
- struct sway_container *current = seat_get_active_current_child(seat, con);
- struct border_colors *current_colors = &config->border_colors.unfocused;
- struct sway_container_state *pstate = &con->current;
-
- // Render titles
- for (int i = 0; i < con->current.children->length; ++i) {
- struct sway_container *child = con->current.children->items[i];
- struct sway_view *view = child->type == C_VIEW ? child->sway_view : NULL;
- struct sway_container_state *cstate = &child->current;
- struct border_colors *colors;
- struct wlr_texture *title_texture;
- struct wlr_texture *marks_texture;
-
- if (focus == child || parent_focused) {
- colors = &config->border_colors.focused;
- title_texture = child->title_focused;
- marks_texture = view ? view->marks_focused : NULL;
- } else if (child == current) {
- colors = &config->border_colors.focused_inactive;
- title_texture = child->title_focused_inactive;
- marks_texture = view ? view->marks_focused_inactive : NULL;
- } else {
- colors = &config->border_colors.unfocused;
- title_texture = child->title_unfocused;
- marks_texture = view ? view->marks_unfocused : NULL;
- }
-
- int y = pstate->swayc_y + container_titlebar_height() * i;
- render_titlebar(output, damage, child, cstate->swayc_x, y,
- cstate->swayc_width, colors, title_texture, marks_texture);
-
- if (child == current) {
- current_colors = colors;
- }
- }
-
- // Render surface and left/right/bottom borders
- if (current) {
- if (current->type == C_VIEW) {
- render_view(output, damage, current, current_colors);
- } else {
- render_container(output, damage, current,
- parent_focused || current == focus);
- }
- }
-}
-
-static void render_container(struct sway_output *output,
- pixman_region32_t *damage, struct sway_container *con,
- bool parent_focused) {
- switch (con->current.layout) {
- case L_NONE:
- case L_HORIZ:
- case L_VERT:
- render_container_simple(output, damage, con, parent_focused);
- break;
- case L_STACKED:
- render_container_stacked(output, damage, con, parent_focused);
- break;
- case L_TABBED:
- render_container_tabbed(output, damage, con, parent_focused);
- break;
- case L_FLOATING:
- sway_assert(false, "Didn't expect to see floating here");
- }
-}
-
-static void render_floating_container(struct sway_output *soutput,
- pixman_region32_t *damage, struct sway_container *con) {
- if (con->type == C_VIEW) {
- struct sway_view *view = con->sway_view;
- struct sway_seat *seat = input_manager_current_seat(input_manager);
- struct sway_container *focus = seat_get_focus(seat);
- struct border_colors *colors;
- struct wlr_texture *title_texture;
- struct wlr_texture *marks_texture;
-
- if (focus == con) {
- colors = &config->border_colors.focused;
- title_texture = con->title_focused;
- marks_texture = view->marks_focused;
- } else {
- colors = &config->border_colors.unfocused;
- title_texture = con->title_unfocused;
- marks_texture = view->marks_unfocused;
- }
-
- if (con->current.border == B_NORMAL) {
- render_titlebar(soutput, damage, con, con->current.swayc_x,
- con->current.swayc_y, con->current.swayc_width, colors,
- title_texture, marks_texture);
- } else if (con->current.border != B_NONE) {
- render_top_border(soutput, damage, con, colors);
- }
- render_view(soutput, damage, con, colors);
- } else {
- render_container(soutput, damage, con, false);
- }
-}
-
-static void render_floating(struct sway_output *soutput,
- pixman_region32_t *damage) {
- for (int i = 0; i < root_container.current.children->length; ++i) {
- struct sway_container *output =
- root_container.current.children->items[i];
- for (int j = 0; j < output->current.children->length; ++j) {
- struct sway_container *ws = output->current.children->items[j];
- if (!workspace_is_visible(ws)) {
- continue;
- }
- list_t *floating =
- ws->current.ws_floating->current.children;
- for (int k = 0; k < floating->length; ++k) {
- struct sway_container *floater = floating->items[k];
- render_floating_container(soutput, damage, floater);
- }
- }
- }
-}
-
-static struct sway_container *output_get_active_workspace(
- struct sway_output *output) {
+struct sway_container *output_get_active_workspace(struct sway_output *output) {
struct sway_seat *seat = input_manager_current_seat(input_manager);
struct sway_container *focus =
seat_get_focus_inactive(seat, output->swayc);
@@ -915,117 +181,58 @@ static struct sway_container *output_get_active_workspace(
return workspace;
}
-static void render_output(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;
- }
-
- wlr_renderer_begin(renderer, wlr_output->width, wlr_output->height);
-
- bool damage_whole_before_swap = false;
- if (!pixman_region32_not_empty(damage)) {
- // Output isn't damaged but needs buffer swap
- goto renderer_end;
- }
-
- const char *damage_debug = getenv("SWAY_DAMAGE_DEBUG");
- if (damage_debug != NULL) {
- if (strcmp(damage_debug, "highlight") == 0) {
- wlr_renderer_clear(renderer, (float[]){1, 1, 0, 1});
- damage_whole_before_swap = true;
- } else if (strcmp(damage_debug, "rerender") == 0) {
- int width, height;
- wlr_output_transformed_resolution(wlr_output, &width, &height);
- pixman_region32_union_rect(damage, damage, 0, 0, width, height);
- }
+bool output_has_opaque_lockscreen(struct sway_output *output,
+ struct sway_seat *seat) {
+ if (!seat->exclusive_client) {
+ return false;
}
- struct sway_container *workspace = output_get_active_workspace(output);
- struct sway_view *fullscreen_view = workspace->current.ws_fullscreen;
-
- if (fullscreen_view) {
- float clear_color[] = {0.0f, 0.0f, 0.0f, 1.0f};
-
- int nrects;
- pixman_box32_t *rects = pixman_region32_rectangles(damage, &nrects);
- for (int i = 0; i < nrects; ++i) {
- scissor_output(wlr_output, &rects[i]);
- wlr_renderer_clear(renderer, clear_color);
+ struct wlr_layer_surface *wlr_layer_surface;
+ wl_list_for_each(wlr_layer_surface, &server.layer_shell->surfaces, link) {
+ if (wlr_layer_surface->output != output->wlr_output) {
+ continue;
}
-
- // TODO: handle views smaller than the output
- render_view_surfaces(fullscreen_view, output, damage, 1.0f);
-
- if (fullscreen_view->type == SWAY_VIEW_XWAYLAND) {
- render_unmanaged(output, damage,
- &root_container.sway_root->xwayland_unmanaged);
+ struct wlr_surface *wlr_surface = wlr_layer_surface->surface;
+ if (wlr_surface->resource->client != seat->exclusive_client) {
+ continue;
}
- } else {
- float clear_color[] = {0.25f, 0.25f, 0.25f, 1.0f};
-
- int nrects;
- pixman_box32_t *rects = pixman_region32_rectangles(damage, &nrects);
- for (int i = 0; i < nrects; ++i) {
- scissor_output(wlr_output, &rects[i]);
- wlr_renderer_clear(renderer, clear_color);
+ struct sway_layer_surface *sway_layer_surface =
+ layer_from_wlr_layer_surface(wlr_layer_surface);
+ pixman_box32_t output_box = {
+ .x2 = output->swayc->current.swayc_width,
+ .y2 = output->swayc->current.swayc_height,
+ };
+ pixman_region32_t surface_opaque_box;
+ pixman_region32_init(&surface_opaque_box);
+ pixman_region32_copy(&surface_opaque_box, &wlr_surface->opaque_region);
+ pixman_region32_translate(&surface_opaque_box,
+ sway_layer_surface->geo.x, sway_layer_surface->geo.y);
+ bool contains = pixman_region32_contains_rectangle(&surface_opaque_box,
+ &output_box);
+ pixman_region32_fini(&surface_opaque_box);
+ if (contains) {
+ return true;
}
-
- render_layer(output, damage,
- &output->layers[ZWLR_LAYER_SHELL_V1_LAYER_BACKGROUND]);
- render_layer(output, damage,
- &output->layers[ZWLR_LAYER_SHELL_V1_LAYER_BOTTOM]);
-
- struct sway_seat *seat = input_manager_current_seat(input_manager);
- struct sway_container *focus = seat_get_focus(seat);
- render_container(output, damage, workspace, focus == workspace);
- render_floating(output, damage);
-
- render_unmanaged(output, damage,
- &root_container.sway_root->xwayland_unmanaged);
- render_layer(output, damage,
- &output->layers[ZWLR_LAYER_SHELL_V1_LAYER_TOP]);
- }
- render_layer(output, damage,
- &output->layers[ZWLR_LAYER_SHELL_V1_LAYER_OVERLAY]);
- render_drag_icons(output, damage, &root_container.sway_root->drag_icons);
-
-renderer_end:
- if (root_container.sway_root->debug_tree) {
- wlr_render_texture(renderer, root_container.sway_root->debug_tree,
- wlr_output->transform_matrix, 0, 0, 1);
- }
-
- if (damage_whole_before_swap || root_container.sway_root->debug_tree) {
- int width, height;
- wlr_output_transformed_resolution(wlr_output, &width, &height);
- pixman_region32_union_rect(damage, damage, 0, 0, width, height);
}
-
- wlr_renderer_scissor(renderer, NULL);
- wlr_renderer_end(renderer);
- if (!wlr_output_damage_swap_buffers(output->damage, when, damage)) {
- return;
- }
- output->last_frame = *when;
+ return false;
}
struct send_frame_done_data {
struct root_geometry root_geo;
struct sway_output *output;
struct timespec *when;
+ struct wl_client *exclusive_client;
};
static void send_frame_done_iterator(struct wlr_surface *surface,
int sx, int sy, void *_data) {
struct send_frame_done_data *data = _data;
+ if (data->exclusive_client &&
+ data->exclusive_client != surface->resource->client) {
+ return;
+ }
- bool intersects = get_surface_box(&data->root_geo, data->output, surface,
+ bool intersects = output_get_surface_box(&data->root_geo, data->output, surface,
sx, sy, NULL);
if (intersects) {
wlr_surface_send_frame_done(surface, data->when);
@@ -1034,19 +241,19 @@ static void send_frame_done_iterator(struct wlr_surface *surface,
static void send_frame_done_layer(struct send_frame_done_data *data,
struct wl_list *layer_surfaces) {
- layer_for_each_surface(layer_surfaces, &data->root_geo,
+ output_layer_for_each_surface(layer_surfaces, &data->root_geo,
send_frame_done_iterator, data);
}
static void send_frame_done_unmanaged(struct send_frame_done_data *data,
struct wl_list *unmanaged) {
- unmanaged_for_each_surface(unmanaged, data->output, &data->root_geo,
+ output_unmanaged_for_each_surface(unmanaged, data->output, &data->root_geo,
send_frame_done_iterator, data);
}
static void send_frame_done_drag_icons(struct send_frame_done_data *data,
struct wl_list *drag_icons) {
- drag_icons_for_each_surface(drag_icons, data->output, &data->root_geo,
+ output_drag_icons_for_each_surface(drag_icons, data->output, &data->root_geo,
send_frame_done_iterator, data);
}
@@ -1061,7 +268,7 @@ static void send_frame_done_container_iterator(struct sway_container *con,
return;
}
- output_view_for_each_surface(con->sway_view, &data->root_geo,
+ output_view_for_each_surface(con->sway_view, data->output, &data->root_geo,
send_frame_done_iterator, data);
}
@@ -1072,9 +279,12 @@ static void send_frame_done_container(struct send_frame_done_data *data,
}
static void send_frame_done(struct sway_output *output, struct timespec *when) {
+ struct sway_seat *seat = input_manager_current_seat(input_manager);
struct send_frame_done_data data = {
.output = output,
.when = when,
+ .exclusive_client = output_has_opaque_lockscreen(output, seat) ?
+ seat->exclusive_client : NULL,
};
struct sway_container *workspace = output_get_active_workspace(output);
@@ -1125,7 +335,7 @@ static void damage_handle_frame(struct wl_listener *listener, void *data) {
}
if (needs_swap) {
- render_output(output, &now, &damage);
+ output_render(output, &now, &damage);
}
pixman_region32_fini(&damage);
@@ -1152,7 +362,7 @@ static void damage_surface_iterator(struct wlr_surface *surface, int sx, int sy,
bool whole = data->whole;
struct wlr_box box;
- bool intersects = get_surface_box(&data->root_geo, data->output, surface,
+ bool intersects = output_get_surface_box(&data->root_geo, data->output, surface,
sx, sy, &box);
if (!intersects) {
return;
@@ -1163,16 +373,22 @@ static void damage_surface_iterator(struct wlr_surface *surface, int sx, int sy,
int center_x = box.x + box.width/2;
int center_y = box.y + box.height/2;
- if (pixman_region32_not_empty(&surface->current->surface_damage)) {
+ if (pixman_region32_not_empty(&surface->buffer_damage)) {
+ enum wl_output_transform transform =
+ wlr_output_transform_invert(surface->current.transform);
+
pixman_region32_t damage;
pixman_region32_init(&damage);
- pixman_region32_copy(&damage, &surface->current->surface_damage);
- wlr_region_scale(&damage, &damage, output->wlr_output->scale);
- if (ceil(output->wlr_output->scale) > surface->current->scale) {
+ pixman_region32_copy(&damage, &surface->buffer_damage);
+ wlr_region_transform(&damage, &damage, transform,
+ surface->current.buffer_width, surface->current.buffer_height);
+ wlr_region_scale(&damage, &damage,
+ output->wlr_output->scale / (float)surface->current.scale);
+ if (ceil(output->wlr_output->scale) > surface->current.scale) {
// When scaling up a surface, it'll become blurry so we need to
// expand the damage region
wlr_region_expand(&damage, &damage,
- ceil(output->wlr_output->scale) - surface->current->scale);
+ ceil(output->wlr_output->scale) - surface->current.scale);
}
pixman_region32_translate(&damage, box.x, box.y);
wlr_region_rotated_bounds(&damage, &damage, rotation,
@@ -1196,7 +412,7 @@ void output_damage_surface(struct sway_output *output, double ox, double oy,
.whole = whole,
};
- surface_for_each_surface(surface, ox, oy, &data.root_geo,
+ output_surface_for_each_surface(surface, ox, oy, &data.root_geo,
damage_surface_iterator, &data);
}
@@ -1215,7 +431,7 @@ static void output_damage_view(struct sway_output *output,
.whole = whole,
};
- output_view_for_each_surface(view, &data.root_geo,
+ output_view_for_each_surface(view, output, &data.root_geo,
damage_surface_iterator, &data);
}
@@ -1247,11 +463,12 @@ static void output_damage_whole_container_iterator(struct sway_container *con,
void output_damage_whole_container(struct sway_output *output,
struct sway_container *con) {
+ // Pad the box by 1px, because the width is a double and might be a fraction
struct wlr_box box = {
- .x = con->current.swayc_x - output->wlr_output->lx,
- .y = con->current.swayc_y - output->wlr_output->ly,
- .width = con->current.swayc_width,
- .height = con->current.swayc_height,
+ .x = con->current.swayc_x - output->wlr_output->lx - 1,
+ .y = con->current.swayc_y - output->wlr_output->ly - 1,
+ .width = con->current.swayc_width + 2,
+ .height = con->current.swayc_height + 2,
};
scale_box(&box, output->wlr_output->scale);
wlr_output_damage_add_box(output->damage, &box);
@@ -1276,19 +493,21 @@ static void handle_destroy(struct wl_listener *listener, void *data) {
output->wlr_output->data = NULL;
free(output);
- arrange_and_commit(&root_container);
+ arrange_windows(&root_container);
}
static void handle_mode(struct wl_listener *listener, void *data) {
struct sway_output *output = wl_container_of(listener, output, mode);
arrange_layers(output);
- arrange_and_commit(output->swayc);
+ arrange_windows(output->swayc);
+ transaction_commit_dirty();
}
static void handle_transform(struct wl_listener *listener, void *data) {
struct sway_output *output = wl_container_of(listener, output, transform);
arrange_layers(output);
- arrange_and_commit(output->swayc);
+ arrange_windows(output->swayc);
+ transaction_commit_dirty();
}
static void handle_scale_iterator(struct sway_container *view, void *data) {
@@ -1299,7 +518,8 @@ static void handle_scale(struct wl_listener *listener, void *data) {
struct sway_output *output = wl_container_of(listener, output, scale);
arrange_layers(output);
container_descendants(output->swayc, C_VIEW, handle_scale_iterator, NULL);
- arrange_and_commit(output->swayc);
+ arrange_windows(output->swayc);
+ transaction_commit_dirty();
}
struct sway_output *output_from_wlr_output(struct wlr_output *wlr_output) {
@@ -1309,7 +529,7 @@ struct sway_output *output_from_wlr_output(struct wlr_output *wlr_output) {
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;
- wlr_log(L_DEBUG, "New output %p: %s", wlr_output, wlr_output->name);
+ wlr_log(WLR_DEBUG, "New output %p: %s", wlr_output, wlr_output->name);
struct sway_output *output = calloc(1, sizeof(struct sway_output));
if (!output) {
@@ -1368,5 +588,6 @@ void output_enable(struct sway_output *output) {
output->damage_destroy.notify = damage_handle_destroy;
arrange_layers(output);
- arrange_and_commit(&root_container);
+ arrange_windows(&root_container);
+ transaction_commit_dirty();
}
diff --git a/sway/desktop/render.c b/sway/desktop/render.c
new file mode 100644
index 00000000..7da54594
--- /dev/null
+++ b/sway/desktop/render.c
@@ -0,0 +1,919 @@
+#define _POSIX_C_SOURCE 200809L
+#include <assert.h>
+#include <stdlib.h>
+#include <strings.h>
+#include <time.h>
+#include <wayland-server.h>
+#include <wlr/render/wlr_renderer.h>
+#include <wlr/types/wlr_box.h>
+#include <wlr/types/wlr_buffer.h>
+#include <wlr/types/wlr_matrix.h>
+#include <wlr/types/wlr_output_damage.h>
+#include <wlr/types/wlr_output_layout.h>
+#include <wlr/types/wlr_output.h>
+#include <wlr/types/wlr_surface.h>
+#include <wlr/util/region.h>
+#include "log.h"
+#include "sway/config.h"
+#include "sway/debug.h"
+#include "sway/input/input-manager.h"
+#include "sway/input/seat.h"
+#include "sway/layers.h"
+#include "sway/output.h"
+#include "sway/server.h"
+#include "sway/tree/arrange.h"
+#include "sway/tree/container.h"
+#include "sway/tree/layout.h"
+#include "sway/tree/view.h"
+#include "sway/tree/workspace.h"
+
+struct render_data {
+ struct root_geometry root_geo;
+ struct sway_output *output;
+ pixman_region32_t *damage;
+ struct sway_view *view;
+ float alpha;
+};
+
+static void scale_box(struct wlr_box *box, float scale) {
+ box->x *= scale;
+ box->y *= scale;
+ box->width *= scale;
+ box->height *= 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);
+ assert(renderer);
+
+ struct wlr_box box = {
+ .x = rect->x1,
+ .y = rect->y1,
+ .width = rect->x2 - rect->x1,
+ .height = rect->y2 - rect->y1,
+ };
+
+ int ow, oh;
+ wlr_output_transformed_resolution(wlr_output, &ow, &oh);
+
+ enum wl_output_transform transform =
+ wlr_output_transform_invert(wlr_output->transform);
+ wlr_box_transform(&box, transform, ow, oh, &box);
+
+ wlr_renderer_scissor(renderer, &box);
+}
+
+static void render_texture(struct wlr_output *wlr_output,
+ pixman_region32_t *output_damage, struct wlr_texture *texture,
+ const struct wlr_box *box, const float matrix[static 9], float alpha) {
+ struct wlr_renderer *renderer =
+ wlr_backend_get_renderer(wlr_output->backend);
+
+ pixman_region32_t damage;
+ pixman_region32_init(&damage);
+ pixman_region32_union_rect(&damage, &damage, box->x, box->y,
+ box->width, box->height);
+ pixman_region32_intersect(&damage, &damage, output_damage);
+ bool damaged = pixman_region32_not_empty(&damage);
+ if (!damaged) {
+ goto damage_finish;
+ }
+
+ int nrects;
+ pixman_box32_t *rects = pixman_region32_rectangles(&damage, &nrects);
+ for (int i = 0; i < nrects; ++i) {
+ scissor_output(wlr_output, &rects[i]);
+ wlr_render_texture_with_matrix(renderer, texture, matrix, alpha);
+ }
+
+damage_finish:
+ pixman_region32_fini(&damage);
+}
+
+static void render_surface_iterator(struct wlr_surface *surface, int sx, int sy,
+ void *_data) {
+ struct render_data *data = _data;
+ struct wlr_output *wlr_output = data->output->wlr_output;
+ float rotation = data->root_geo.rotation;
+ pixman_region32_t *output_damage = data->damage;
+ float alpha = data->alpha;
+
+ struct wlr_texture *texture = wlr_surface_get_texture(surface);
+ if (!texture) {
+ return;
+ }
+
+ struct wlr_box box;
+ bool intersects = output_get_surface_box(&data->root_geo, data->output,
+ surface, sx, sy, &box);
+ if (!intersects) {
+ return;
+ }
+
+ scale_box(&box, wlr_output->scale);
+
+ float matrix[9];
+ enum wl_output_transform transform =
+ wlr_output_transform_invert(surface->current.transform);
+ wlr_matrix_project_box(matrix, &box, transform, rotation,
+ wlr_output->transform_matrix);
+
+ render_texture(wlr_output, output_damage, texture, &box, matrix, alpha);
+}
+
+static void render_layer(struct sway_output *output,
+ pixman_region32_t *damage, struct wl_list *layer_surfaces) {
+ struct render_data data = {
+ .output = output,
+ .damage = damage,
+ .alpha = 1.0f,
+ };
+ output_layer_for_each_surface(layer_surfaces, &data.root_geo,
+ render_surface_iterator, &data);
+}
+
+static void render_unmanaged(struct sway_output *output,
+ pixman_region32_t *damage, struct wl_list *unmanaged) {
+ struct render_data data = {
+ .output = output,
+ .damage = damage,
+ .alpha = 1.0f,
+ };
+ output_unmanaged_for_each_surface(unmanaged, output, &data.root_geo,
+ render_surface_iterator, &data);
+}
+
+static void render_drag_icons(struct sway_output *output,
+ pixman_region32_t *damage, struct wl_list *drag_icons) {
+ struct render_data data = {
+ .output = output,
+ .damage = damage,
+ .alpha = 1.0f,
+ };
+ output_drag_icons_for_each_surface(drag_icons, output, &data.root_geo,
+ render_surface_iterator, &data);
+}
+
+static void render_rect(struct wlr_output *wlr_output,
+ pixman_region32_t *output_damage, const struct wlr_box *_box,
+ float color[static 4]) {
+ struct wlr_renderer *renderer =
+ wlr_backend_get_renderer(wlr_output->backend);
+
+ struct wlr_box box;
+ memcpy(&box, _box, sizeof(struct wlr_box));
+ box.x -= wlr_output->lx * wlr_output->scale;
+ box.y -= wlr_output->ly * wlr_output->scale;
+
+ pixman_region32_t damage;
+ pixman_region32_init(&damage);
+ pixman_region32_union_rect(&damage, &damage, box.x, box.y,
+ box.width, box.height);
+ pixman_region32_intersect(&damage, &damage, output_damage);
+ bool damaged = pixman_region32_not_empty(&damage);
+ if (!damaged) {
+ goto damage_finish;
+ }
+
+ int nrects;
+ pixman_box32_t *rects = pixman_region32_rectangles(&damage, &nrects);
+ for (int i = 0; i < nrects; ++i) {
+ scissor_output(wlr_output, &rects[i]);
+ wlr_render_rect(renderer, &box, color,
+ wlr_output->transform_matrix);
+ }
+
+damage_finish:
+ pixman_region32_fini(&damage);
+}
+
+static void premultiply_alpha(float color[4], float opacity) {
+ color[3] *= opacity;
+ color[0] *= color[3];
+ color[1] *= color[3];
+ color[2] *= color[3];
+}
+
+static void render_view_surfaces(struct sway_view *view,
+ struct sway_output *output, pixman_region32_t *damage, float alpha) {
+ struct render_data data = {
+ .output = output,
+ .damage = damage,
+ .view = view,
+ .alpha = alpha,
+ };
+ output_view_for_each_surface(view, output, &data.root_geo,
+ render_surface_iterator, &data);
+}
+
+static void render_saved_view(struct sway_view *view,
+ struct sway_output *output, pixman_region32_t *damage, float alpha) {
+ struct wlr_output *wlr_output = output->wlr_output;
+
+ int width, height;
+ struct wlr_texture *texture =
+ transaction_get_saved_texture(view, &width, &height);
+ if (!texture) {
+ return;
+ }
+ struct wlr_box box = {
+ .x = view->swayc->current.view_x - output->swayc->current.swayc_x,
+ .y = view->swayc->current.view_y - output->swayc->current.swayc_y,
+ .width = width,
+ .height = height,
+ };
+
+ struct wlr_box output_box = {
+ .width = output->swayc->current.swayc_width,
+ .height = output->swayc->current.swayc_height,
+ };
+
+ struct wlr_box intersection;
+ bool intersects = wlr_box_intersection(&output_box, &box, &intersection);
+ if (!intersects) {
+ return;
+ }
+
+ scale_box(&box, wlr_output->scale);
+
+ float matrix[9];
+ wlr_matrix_project_box(matrix, &box, WL_OUTPUT_TRANSFORM_NORMAL, 0,
+ wlr_output->transform_matrix);
+
+ render_texture(wlr_output, damage, texture, &box, matrix, alpha);
+}
+
+/**
+ * Render a view's surface and left/bottom/right borders.
+ */
+static void render_view(struct sway_output *output, pixman_region32_t *damage,
+ struct sway_container *con, struct border_colors *colors) {
+ struct sway_view *view = con->sway_view;
+ if (view->swayc->instructions->length) {
+ render_saved_view(view, output, damage, view->swayc->alpha);
+ } else {
+ render_view_surfaces(view, output, damage, view->swayc->alpha);
+ }
+
+ if (view->using_csd) {
+ return;
+ }
+
+ struct wlr_box box;
+ float output_scale = output->wlr_output->scale;
+ float color[4];
+ struct sway_container_state *state = &con->current;
+
+ if (state->border != B_NONE) {
+ if (state->border_left) {
+ memcpy(&color, colors->child_border, sizeof(float) * 4);
+ premultiply_alpha(color, con->alpha);
+ box.x = state->swayc_x;
+ box.y = state->view_y;
+ box.width = state->border_thickness;
+ box.height = state->view_height;
+ scale_box(&box, output_scale);
+ render_rect(output->wlr_output, damage, &box, color);
+ }
+
+ if (state->border_right) {
+ if (state->parent->current.children->length == 1
+ && state->parent->current.layout == L_HORIZ) {
+ memcpy(&color, colors->indicator, sizeof(float) * 4);
+ } else {
+ memcpy(&color, colors->child_border, sizeof(float) * 4);
+ }
+ premultiply_alpha(color, con->alpha);
+ box.x = state->view_x + state->view_width;
+ box.y = state->view_y;
+ box.width = state->border_thickness;
+ box.height = state->view_height;
+ scale_box(&box, output_scale);
+ render_rect(output->wlr_output, damage, &box, color);
+ }
+
+ if (state->border_bottom) {
+ if (state->parent->current.children->length == 1
+ && con->current.parent->current.layout == L_VERT) {
+ memcpy(&color, colors->indicator, sizeof(float) * 4);
+ } else {
+ memcpy(&color, colors->child_border, sizeof(float) * 4);
+ }
+ premultiply_alpha(color, con->alpha);
+ box.x = state->swayc_x;
+ box.y = state->view_y + state->view_height;
+ box.width = state->swayc_width;
+ box.height = state->border_thickness;
+ scale_box(&box, output_scale);
+ render_rect(output->wlr_output, damage, &box, color);
+ }
+ }
+}
+
+/**
+ * Render a titlebar.
+ *
+ * Care must be taken not to render over the same pixel multiple times,
+ * otherwise the colors will be incorrect when using opacity.
+ *
+ * The height is: 1px border, 3px padding, font height, 3px padding, 1px border
+ * The left side for L_TABBED is: 1px border, 2px padding, title
+ * The left side for other layouts is: 3px padding, title
+ */
+static void render_titlebar(struct sway_output *output,
+ pixman_region32_t *output_damage, struct sway_container *con,
+ int x, int y, int width,
+ struct border_colors *colors, struct wlr_texture *title_texture,
+ struct wlr_texture *marks_texture) {
+ struct wlr_box box;
+ float color[4];
+ struct sway_container_state *state = &con->current;
+ float output_scale = output->wlr_output->scale;
+ enum sway_container_layout layout = state->parent->current.layout;
+ list_t *children = state->parent->current.children;
+ bool is_last_child = children->items[children->length - 1] == con;
+ double output_x = output->swayc->current.swayc_x;
+ double output_y = output->swayc->current.swayc_y;
+
+ // Single pixel bar above title
+ memcpy(&color, colors->border, sizeof(float) * 4);
+ premultiply_alpha(color, con->alpha);
+ box.x = x;
+ box.y = y;
+ box.width = width;
+ box.height = TITLEBAR_BORDER_THICKNESS;
+ scale_box(&box, output_scale);
+ render_rect(output->wlr_output, output_damage, &box, color);
+
+ // Single pixel bar below title
+ size_t left_offset = 0, right_offset = 0;
+ bool connects_sides = false;
+ if (layout == L_HORIZ || layout == L_VERT ||
+ (layout == L_STACKED && is_last_child)) {
+ if (con->type == C_VIEW) {
+ left_offset = state->border_left * state->border_thickness;
+ right_offset = state->border_right * state->border_thickness;
+ connects_sides = true;
+ }
+ }
+ box.x = x + left_offset;
+ box.y = y + container_titlebar_height() - TITLEBAR_BORDER_THICKNESS;
+ box.width = width - left_offset - right_offset;
+ box.height = TITLEBAR_BORDER_THICKNESS;
+ scale_box(&box, output_scale);
+ render_rect(output->wlr_output, output_damage, &box, color);
+
+ if (layout == L_TABBED) {
+ // Single pixel left edge
+ box.x = x;
+ box.y = y + TITLEBAR_BORDER_THICKNESS;
+ box.width = TITLEBAR_BORDER_THICKNESS;
+ box.height =
+ container_titlebar_height() - TITLEBAR_BORDER_THICKNESS * 2;
+ scale_box(&box, output_scale);
+ render_rect(output->wlr_output, output_damage, &box, color);
+
+ // Single pixel right edge
+ box.x = (x + width - TITLEBAR_BORDER_THICKNESS) * output_scale;
+ render_rect(output->wlr_output, output_damage, &box, color);
+ }
+
+ size_t inner_width = width - TITLEBAR_H_PADDING * 2;
+
+ // Marks
+ size_t marks_ob_width = 0; // output-buffer-local
+ if (config->show_marks && marks_texture) {
+ struct wlr_box texture_box;
+ wlr_texture_get_size(marks_texture,
+ &texture_box.width, &texture_box.height);
+ texture_box.x = (x - output_x + width - TITLEBAR_H_PADDING)
+ * output_scale - texture_box.width;
+ texture_box.y = (y - output_y + TITLEBAR_V_PADDING) * output_scale;
+
+ float matrix[9];
+ wlr_matrix_project_box(matrix, &texture_box,
+ WL_OUTPUT_TRANSFORM_NORMAL,
+ 0.0, output->wlr_output->transform_matrix);
+
+ if (inner_width * output_scale < texture_box.width) {
+ texture_box.width = inner_width * output_scale;
+ }
+ render_texture(output->wlr_output, output_damage, marks_texture,
+ &texture_box, matrix, con->alpha);
+ marks_ob_width = texture_box.width;
+
+ // Gap between the marks and bottom padding, for when the marks texture
+ // height is smaller than the config's font height
+ memcpy(&color, colors->background, sizeof(float) * 4);
+ premultiply_alpha(color, con->alpha);
+ box.x = texture_box.x;
+ box.y = texture_box.y + texture_box.height;
+ box.width = texture_box.width;
+ box.height = config->font_height * output_scale - texture_box.height;
+ if (box.height > 0) {
+ render_rect(output->wlr_output, output_damage, &box, color);
+ }
+ }
+
+ // Title text
+ size_t title_ob_width = 0; // output-buffer-local
+ if (title_texture) {
+ struct wlr_box texture_box;
+ wlr_texture_get_size(title_texture,
+ &texture_box.width, &texture_box.height);
+ texture_box.x = (x - output_x + TITLEBAR_H_PADDING) * output_scale;
+ texture_box.y = (y - output_y + TITLEBAR_V_PADDING) * output_scale;
+
+ float matrix[9];
+ wlr_matrix_project_box(matrix, &texture_box,
+ WL_OUTPUT_TRANSFORM_NORMAL,
+ 0.0, output->wlr_output->transform_matrix);
+
+ if (inner_width * output_scale - marks_ob_width < texture_box.width) {
+ texture_box.width = inner_width * output_scale - marks_ob_width;
+ }
+ render_texture(output->wlr_output, output_damage, title_texture,
+ &texture_box, matrix, con->alpha);
+ title_ob_width = texture_box.width;
+
+ // Gap between the title and bottom padding, for when the title texture
+ // height is smaller than the config's font height
+ memcpy(&color, colors->background, sizeof(float) * 4);
+ premultiply_alpha(color, con->alpha);
+ box.x = texture_box.x;
+ box.y = texture_box.y + texture_box.height;
+ box.width = texture_box.width;
+ box.height = config->font_height * output_scale - texture_box.height;
+ if (box.height > 0) {
+ render_rect(output->wlr_output, output_damage, &box, color);
+ }
+ }
+
+ // Padding above title
+ memcpy(&color, colors->background, sizeof(float) * 4);
+ premultiply_alpha(color, con->alpha);
+ box.x = x + (layout == L_TABBED) * TITLEBAR_BORDER_THICKNESS;
+ box.y = y + TITLEBAR_BORDER_THICKNESS;
+ box.width = width - (layout == L_TABBED) * TITLEBAR_BORDER_THICKNESS * 2;
+ box.height = TITLEBAR_V_PADDING - TITLEBAR_BORDER_THICKNESS;
+ scale_box(&box, output_scale);
+ render_rect(output->wlr_output, output_damage, &box, color);
+
+ // Padding below title
+ box.y = (y + TITLEBAR_V_PADDING + config->font_height) * output_scale;
+ render_rect(output->wlr_output, output_damage, &box, color);
+
+ // Filler between title and marks
+ box.width = inner_width * output_scale - title_ob_width - marks_ob_width;
+ if (box.width > 0) {
+ box.x = (x + TITLEBAR_H_PADDING) * output_scale + title_ob_width;
+ box.y = (y + TITLEBAR_V_PADDING) * output_scale;
+ box.height = config->font_height * output_scale;
+ render_rect(output->wlr_output, output_damage, &box, color);
+ }
+
+ // Padding left of title
+ left_offset = (layout == L_TABBED) * TITLEBAR_BORDER_THICKNESS;
+ box.x = x + left_offset;
+ box.y = y + TITLEBAR_V_PADDING;
+ box.width = TITLEBAR_H_PADDING - left_offset;
+ box.height = config->font_height;
+ scale_box(&box, output_scale);
+ render_rect(output->wlr_output, output_damage, &box, color);
+
+ // Padding right of marks
+ right_offset = (layout == L_TABBED) * TITLEBAR_BORDER_THICKNESS;
+ box.x = x + width - TITLEBAR_H_PADDING;
+ box.y = y + TITLEBAR_V_PADDING;
+ box.width = TITLEBAR_H_PADDING - right_offset;
+ box.height = config->font_height;
+ scale_box(&box, output_scale);
+ render_rect(output->wlr_output, output_damage, &box, color);
+
+ if (connects_sides) {
+ // Left pixel in line with bottom bar
+ box.x = x;
+ box.y = y + container_titlebar_height() - TITLEBAR_BORDER_THICKNESS;
+ box.width = state->border_thickness * state->border_left;
+ box.height = TITLEBAR_BORDER_THICKNESS;
+ scale_box(&box, output_scale);
+ render_rect(output->wlr_output, output_damage, &box, color);
+
+ // Right pixel in line with bottom bar
+ box.x = x + width - state->border_thickness * state->border_right;
+ box.y = y + container_titlebar_height() - TITLEBAR_BORDER_THICKNESS;
+ box.width = state->border_thickness * state->border_right;
+ box.height = TITLEBAR_BORDER_THICKNESS;
+ scale_box(&box, output_scale);
+ render_rect(output->wlr_output, output_damage, &box, color);
+ }
+}
+
+/**
+ * Render the top border line for a view using "border pixel".
+ */
+static void render_top_border(struct sway_output *output,
+ pixman_region32_t *output_damage, struct sway_container *con,
+ struct border_colors *colors) {
+ struct sway_container_state *state = &con->current;
+ if (!state->border_top) {
+ return;
+ }
+ struct wlr_box box;
+ float color[4];
+ float output_scale = output->wlr_output->scale;
+
+ // Child border - top edge
+ memcpy(&color, colors->child_border, sizeof(float) * 4);
+ premultiply_alpha(color, con->alpha);
+ box.x = state->swayc_x;
+ box.y = state->swayc_y;
+ box.width = state->swayc_width;
+ box.height = state->border_thickness;
+ scale_box(&box, output_scale);
+ render_rect(output->wlr_output, output_damage, &box, color);
+}
+
+static void render_container(struct sway_output *output,
+ pixman_region32_t *damage, struct sway_container *con, bool parent_focused);
+
+/**
+ * Render a container's children using a L_HORIZ or L_VERT layout.
+ *
+ * Wrap child views in borders and leave child containers borderless because
+ * they'll apply their own borders to their children.
+ */
+static void render_container_simple(struct sway_output *output,
+ pixman_region32_t *damage, struct sway_container *con,
+ bool parent_focused) {
+ for (int i = 0; i < con->current.children->length; ++i) {
+ struct sway_container *child = con->current.children->items[i];
+
+ if (child->type == C_VIEW) {
+ struct sway_view *view = child->sway_view;
+ struct border_colors *colors;
+ struct wlr_texture *title_texture;
+ struct wlr_texture *marks_texture;
+ struct sway_container_state *state = &child->current;
+
+ if (view_is_urgent(view)) {
+ colors = &config->border_colors.urgent;
+ title_texture = child->title_urgent;
+ marks_texture = view->marks_urgent;
+ } else if (state->focused || parent_focused) {
+ colors = &config->border_colors.focused;
+ title_texture = child->title_focused;
+ marks_texture = view->marks_focused;
+ } else if (con->current.focused_inactive_child == child) {
+ colors = &config->border_colors.focused_inactive;
+ title_texture = child->title_focused_inactive;
+ marks_texture = view->marks_focused_inactive;
+ } else {
+ colors = &config->border_colors.unfocused;
+ title_texture = child->title_unfocused;
+ marks_texture = view->marks_unfocused;
+ }
+
+ if (!view->using_csd) {
+ if (state->border == B_NORMAL) {
+ render_titlebar(output, damage, child, state->swayc_x,
+ state->swayc_y, state->swayc_width, colors,
+ title_texture, marks_texture);
+ } else {
+ render_top_border(output, damage, child, colors);
+ }
+ }
+ render_view(output, damage, child, colors);
+ } else {
+ render_container(output, damage, child,
+ parent_focused || child->current.focused);
+ }
+ }
+}
+
+/**
+ * Render a container's children using the L_TABBED layout.
+ */
+static void render_container_tabbed(struct sway_output *output,
+ pixman_region32_t *damage, struct sway_container *con,
+ bool parent_focused) {
+ if (!con->current.children->length) {
+ return;
+ }
+ struct sway_container_state *pstate = &con->current;
+ struct sway_container *current = pstate->focused_inactive_child;
+ struct border_colors *current_colors = &config->border_colors.unfocused;
+
+ double width_gap_adjustment = 2 * pstate->current_gaps;
+ int tab_width =
+ (pstate->swayc_width - width_gap_adjustment) / pstate->children->length;
+
+ // Render tabs
+ for (int i = 0; i < pstate->children->length; ++i) {
+ struct sway_container *child = pstate->children->items[i];
+ struct sway_view *view = child->type == C_VIEW ? child->sway_view : NULL;
+ struct sway_container_state *cstate = &child->current;
+ struct border_colors *colors;
+ struct wlr_texture *title_texture;
+ struct wlr_texture *marks_texture;
+ bool urgent = view ?
+ view_is_urgent(view) : container_has_urgent_child(child);
+
+ if (urgent) {
+ colors = &config->border_colors.urgent;
+ title_texture = child->title_urgent;
+ marks_texture = view ? view->marks_urgent : NULL;
+ } else if (cstate->focused || parent_focused) {
+ colors = &config->border_colors.focused;
+ title_texture = child->title_focused;
+ marks_texture = view ? view->marks_focused : NULL;
+ } else if (child == pstate->focused_inactive_child) {
+ colors = &config->border_colors.focused_inactive;
+ title_texture = child->title_focused_inactive;
+ marks_texture = view ? view->marks_focused_inactive : NULL;
+ } else {
+ colors = &config->border_colors.unfocused;
+ title_texture = child->title_unfocused;
+ marks_texture = view ? view->marks_unfocused : NULL;
+ }
+
+ int x = cstate->swayc_x + tab_width * i;
+
+ // Make last tab use the remaining width of the parent
+ if (i == pstate->children->length - 1) {
+ tab_width =
+ pstate->swayc_width - width_gap_adjustment - tab_width * i;
+ }
+
+ render_titlebar(output, damage, child, x, cstate->swayc_y, tab_width,
+ colors, title_texture, marks_texture);
+
+ if (child == current) {
+ current_colors = colors;
+ }
+ }
+
+ // Render surface and left/right/bottom borders
+ if (current->type == C_VIEW) {
+ render_view(output, damage, current, current_colors);
+ } else {
+ render_container(output, damage, current,
+ parent_focused || current->current.focused);
+ }
+}
+
+/**
+ * Render a container's children using the L_STACKED layout.
+ */
+static void render_container_stacked(struct sway_output *output,
+ pixman_region32_t *damage, struct sway_container *con,
+ bool parent_focused) {
+ if (!con->current.children->length) {
+ return;
+ }
+ struct sway_container_state *pstate = &con->current;
+ struct sway_container *current = pstate->focused_inactive_child;
+ struct border_colors *current_colors = &config->border_colors.unfocused;
+
+ size_t titlebar_height = container_titlebar_height();
+
+ // Render titles
+ for (int i = 0; i < pstate->children->length; ++i) {
+ struct sway_container *child = pstate->children->items[i];
+ struct sway_view *view = child->type == C_VIEW ? child->sway_view : NULL;
+ struct sway_container_state *cstate = &child->current;
+ struct border_colors *colors;
+ struct wlr_texture *title_texture;
+ struct wlr_texture *marks_texture;
+ bool urgent = view ?
+ view_is_urgent(view) : container_has_urgent_child(child);
+
+ if (urgent) {
+ colors = &config->border_colors.urgent;
+ title_texture = child->title_urgent;
+ marks_texture = view ? view->marks_urgent : NULL;
+ } else if (cstate->focused || parent_focused) {
+ colors = &config->border_colors.focused;
+ title_texture = child->title_focused;
+ marks_texture = view ? view->marks_focused : NULL;
+ } else if (child == pstate->focused_inactive_child) {
+ colors = &config->border_colors.focused_inactive;
+ title_texture = child->title_focused_inactive;
+ marks_texture = view ? view->marks_focused_inactive : NULL;
+ } else {
+ colors = &config->border_colors.unfocused;
+ title_texture = child->title_unfocused;
+ marks_texture = view ? view->marks_unfocused : NULL;
+ }
+
+ int y = cstate->swayc_y + titlebar_height * i;
+ render_titlebar(output, damage, child, cstate->swayc_x, y,
+ cstate->swayc_width, colors, title_texture, marks_texture);
+
+ if (child == current) {
+ current_colors = colors;
+ }
+ }
+
+ // Render surface and left/right/bottom borders
+ if (current->type == C_VIEW) {
+ render_view(output, damage, current, current_colors);
+ } else {
+ render_container(output, damage, current,
+ parent_focused || current->current.focused);
+ }
+}
+
+static void render_container(struct sway_output *output,
+ pixman_region32_t *damage, struct sway_container *con,
+ bool parent_focused) {
+ switch (con->current.layout) {
+ case L_NONE:
+ case L_HORIZ:
+ case L_VERT:
+ render_container_simple(output, damage, con, parent_focused);
+ break;
+ case L_STACKED:
+ render_container_stacked(output, damage, con, parent_focused);
+ break;
+ case L_TABBED:
+ render_container_tabbed(output, damage, con, parent_focused);
+ break;
+ case L_FLOATING:
+ sway_assert(false, "Didn't expect to see floating here");
+ }
+}
+
+static void render_floating_container(struct sway_output *soutput,
+ pixman_region32_t *damage, struct sway_container *con) {
+ if (con->type == C_VIEW) {
+ struct sway_view *view = con->sway_view;
+ struct border_colors *colors;
+ struct wlr_texture *title_texture;
+ struct wlr_texture *marks_texture;
+
+ if (view_is_urgent(view)) {
+ colors = &config->border_colors.urgent;
+ title_texture = con->title_urgent;
+ marks_texture = view->marks_urgent;
+ } else if (con->current.focused) {
+ colors = &config->border_colors.focused;
+ title_texture = con->title_focused;
+ marks_texture = view->marks_focused;
+ } else {
+ colors = &config->border_colors.unfocused;
+ title_texture = con->title_unfocused;
+ marks_texture = view->marks_unfocused;
+ }
+
+ if (!view->using_csd) {
+ if (con->current.border == B_NORMAL) {
+ render_titlebar(soutput, damage, con, con->current.swayc_x,
+ con->current.swayc_y, con->current.swayc_width, colors,
+ title_texture, marks_texture);
+ } else if (con->current.border != B_NONE) {
+ render_top_border(soutput, damage, con, colors);
+ }
+ }
+ render_view(soutput, damage, con, colors);
+ } else {
+ render_container(soutput, damage, con, false);
+ }
+}
+
+static void render_floating(struct sway_output *soutput,
+ pixman_region32_t *damage) {
+ for (int i = 0; i < root_container.current.children->length; ++i) {
+ struct sway_container *output =
+ root_container.current.children->items[i];
+ for (int j = 0; j < output->current.children->length; ++j) {
+ struct sway_container *ws = output->current.children->items[j];
+ if (!workspace_is_visible(ws)) {
+ continue;
+ }
+ list_t *floating =
+ ws->current.ws_floating->current.children;
+ for (int k = 0; k < floating->length; ++k) {
+ struct sway_container *floater = floating->items[k];
+ render_floating_container(soutput, damage, floater);
+ }
+ }
+ }
+}
+
+const char *damage_debug = NULL;
+
+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;
+ }
+
+ wlr_renderer_begin(renderer, wlr_output->width, wlr_output->height);
+
+ bool damage_whole_before_swap = false;
+ if (!pixman_region32_not_empty(damage)) {
+ // Output isn't damaged but needs buffer swap
+ goto renderer_end;
+ }
+
+ if (damage_debug != NULL) {
+ if (strcmp(damage_debug, "highlight") == 0) {
+ wlr_renderer_clear(renderer, (float[]){1, 1, 0, 1});
+ damage_whole_before_swap = true;
+ } else if (strcmp(damage_debug, "rerender") == 0) {
+ int width, height;
+ wlr_output_transformed_resolution(wlr_output, &width, &height);
+ pixman_region32_union_rect(damage, damage, 0, 0, width, height);
+ }
+ }
+
+ struct sway_container *workspace = output_get_active_workspace(output);
+ struct sway_view *fullscreen_view = workspace->current.ws_fullscreen;
+ struct sway_seat *seat = input_manager_current_seat(input_manager);
+
+ if (output_has_opaque_lockscreen(output, seat) && seat->focused_layer) {
+ struct wlr_layer_surface *wlr_layer_surface = seat->focused_layer;
+ struct sway_layer_surface *sway_layer_surface =
+ layer_from_wlr_layer_surface(seat->focused_layer);
+ struct render_data data = {
+ .output = output,
+ .damage = damage,
+ .alpha = 1.0f,
+ };
+ output_surface_for_each_surface(wlr_layer_surface->surface,
+ sway_layer_surface->geo.x, sway_layer_surface->geo.y,
+ &data.root_geo, render_surface_iterator, &data);
+ } else if (fullscreen_view) {
+ float clear_color[] = {0.0f, 0.0f, 0.0f, 1.0f};
+
+ int nrects;
+ pixman_box32_t *rects = pixman_region32_rectangles(damage, &nrects);
+ for (int i = 0; i < nrects; ++i) {
+ scissor_output(wlr_output, &rects[i]);
+ wlr_renderer_clear(renderer, clear_color);
+ }
+
+ // TODO: handle views smaller than the output
+ if (fullscreen_view->swayc->instructions->length) {
+ render_saved_view(fullscreen_view, output, damage, 1.0f);
+ } else {
+ render_view_surfaces(fullscreen_view, output, damage, 1.0f);
+ }
+
+ if (fullscreen_view->type == SWAY_VIEW_XWAYLAND) {
+ render_unmanaged(output, damage,
+ &root_container.sway_root->xwayland_unmanaged);
+ }
+ } else {
+ float clear_color[] = {0.25f, 0.25f, 0.25f, 1.0f};
+
+ int nrects;
+ pixman_box32_t *rects = pixman_region32_rectangles(damage, &nrects);
+ for (int i = 0; i < nrects; ++i) {
+ scissor_output(wlr_output, &rects[i]);
+ wlr_renderer_clear(renderer, clear_color);
+ }
+
+ render_layer(output, damage,
+ &output->layers[ZWLR_LAYER_SHELL_V1_LAYER_BACKGROUND]);
+ render_layer(output, damage,
+ &output->layers[ZWLR_LAYER_SHELL_V1_LAYER_BOTTOM]);
+
+ render_container(output, damage, workspace, workspace->current.focused);
+ render_floating(output, damage);
+
+ render_unmanaged(output, damage,
+ &root_container.sway_root->xwayland_unmanaged);
+ render_layer(output, damage,
+ &output->layers[ZWLR_LAYER_SHELL_V1_LAYER_TOP]);
+ }
+ render_layer(output, damage,
+ &output->layers[ZWLR_LAYER_SHELL_V1_LAYER_OVERLAY]);
+ render_drag_icons(output, damage, &root_container.sway_root->drag_icons);
+
+renderer_end:
+ if (root_container.sway_root->debug_tree) {
+ wlr_render_texture(renderer, root_container.sway_root->debug_tree,
+ wlr_output->transform_matrix, 0, 0, 1);
+ }
+
+ if (damage_whole_before_swap || root_container.sway_root->debug_tree) {
+ int width, height;
+ wlr_output_transformed_resolution(wlr_output, &width, &height);
+ pixman_region32_union_rect(damage, damage, 0, 0, width, height);
+ }
+
+ wlr_renderer_scissor(renderer, NULL);
+ wlr_renderer_end(renderer);
+ if (!wlr_output_damage_swap_buffers(output->damage, when, damage)) {
+ return;
+ }
+ output->last_frame = *when;
+}
diff --git a/sway/desktop/transaction.c b/sway/desktop/transaction.c
index d2932c87..2a89880a 100644
--- a/sway/desktop/transaction.c
+++ b/sway/desktop/transaction.c
@@ -4,8 +4,8 @@
#include <string.h>
#include <time.h>
#include <wlr/types/wlr_buffer.h>
-#include <wlr/types/wlr_linux_dmabuf.h>
#include "sway/debug.h"
+#include "sway/desktop/idle_inhibit_v1.h"
#include "sway/desktop/transaction.h"
#include "sway/output.h"
#include "sway/tree/container.h"
@@ -18,14 +18,14 @@
* How long we should wait for views to respond to the configure before giving
* up and applying the transaction anyway.
*/
-#define TIMEOUT_MS 200
+int txn_timeout_ms = 200;
/**
* If enabled, sway will always wait for the transaction timeout before
* applying it, rather than applying it when the views are ready. This allows us
* to observe the rendered state while a transaction is in progress.
*/
-#define TRANSACTION_DEBUG false
+bool txn_debug = false;
struct sway_transaction {
struct wl_event_source *timer;
@@ -46,7 +46,7 @@ struct sway_transaction_instruction {
bool ready;
};
-struct sway_transaction *transaction_create() {
+static struct sway_transaction *transaction_create() {
struct sway_transaction *transaction =
calloc(1, sizeof(struct sway_transaction));
transaction->instructions = create_list();
@@ -72,8 +72,8 @@ static void save_view_buffer(struct sway_view *view,
}
if (view->surface && wlr_surface_has_buffer(view->surface)) {
instruction->saved_buffer = wlr_buffer_ref(view->surface->buffer);
- instruction->saved_buffer_width = view->surface->current->width;
- instruction->saved_buffer_height = view->surface->current->height;
+ instruction->saved_buffer_width = view->surface->current.width;
+ instruction->saved_buffer_height = view->surface->current.height;
}
}
@@ -138,25 +138,18 @@ static void copy_pending_state(struct sway_container *container,
state->children = create_list();
list_cat(state->children, container->children);
}
-}
-static bool transaction_has_container(struct sway_transaction *transaction,
- struct sway_container *container) {
- for (int i = 0; i < transaction->instructions->length; ++i) {
- struct sway_transaction_instruction *instruction =
- transaction->instructions->items[i];
- if (instruction->container == container) {
- return true;
- }
+ struct sway_seat *seat = input_manager_current_seat(input_manager);
+ state->focused = seat_get_focus(seat) == container;
+
+ if (container->type != C_VIEW) {
+ state->focused_inactive_child =
+ seat_get_active_child(seat, container);
}
- return false;
}
-void transaction_add_container(struct sway_transaction *transaction,
+static void transaction_add_container(struct sway_transaction *transaction,
struct sway_container *container) {
- if (transaction_has_container(transaction, container)) {
- return;
- }
struct sway_transaction_instruction *instruction =
calloc(1, sizeof(struct sway_transaction_instruction));
instruction->transaction = transaction;
@@ -174,7 +167,7 @@ void transaction_add_container(struct sway_transaction *transaction,
* Apply a transaction to the "current" state of the tree.
*/
static void transaction_apply(struct sway_transaction *transaction) {
- wlr_log(L_DEBUG, "Applying transaction %p", transaction);
+ wlr_log(WLR_DEBUG, "Applying transaction %p", transaction);
if (server.debug_txn_timings) {
struct timespec now;
clock_gettime(CLOCK_MONOTONIC, &now);
@@ -185,9 +178,9 @@ static void transaction_apply(struct sway_transaction *transaction) {
float ms_waiting = (now.tv_sec - commit->tv_sec) * 1000 +
(now.tv_nsec - commit->tv_nsec) / 1000000.0;
float ms_total = ms_arranging + ms_waiting;
- wlr_log(L_DEBUG, "Transaction %p: %.1fms arranging, %.1fms waiting, "
- "%.1fms total (%.1f frames if 60Hz)", transaction,
- ms_arranging, ms_waiting, ms_total, ms_total / (1000 / 60));
+ wlr_log(WLR_DEBUG, "Transaction %p: %.1fms arranging, %.1fms waiting, "
+ "%.1fms total (%.1f frames if 60Hz)", transaction,
+ ms_arranging, ms_waiting, ms_total, ms_total / (1000.0f / 60));
}
// Apply the instruction state to the container's current state
@@ -209,10 +202,12 @@ static void transaction_apply(struct sway_transaction *transaction) {
.width = instruction->state.swayc_width,
.height = instruction->state.swayc_height,
};
- for (int j = 0; j < root_container.children->length; ++j) {
- struct sway_container *output = root_container.children->items[j];
- output_damage_box(output->sway_output, &old_box);
- output_damage_box(output->sway_output, &new_box);
+ for (int j = 0; j < root_container.current.children->length; ++j) {
+ struct sway_container *output = root_container.current.children->items[j];
+ if (output->sway_output) {
+ output_damage_box(output->sway_output, &old_box);
+ output_damage_box(output->sway_output, &new_box);
+ }
}
// There are separate children lists for each instruction state, the
@@ -227,29 +222,22 @@ static void transaction_apply(struct sway_transaction *transaction) {
}
}
-/**
- * For simplicity, we only progress the queue if it can be completely flushed.
- */
static void transaction_progress_queue() {
- // We iterate this list in reverse because we're more likely to find a
- // waiting transactions at the end of the list.
- for (int i = server.transactions->length - 1; i >= 0; --i) {
- struct sway_transaction *transaction = server.transactions->items[i];
+ while (server.transactions->length) {
+ struct sway_transaction *transaction = server.transactions->items[0];
if (transaction->num_waiting) {
return;
}
- }
- for (int i = 0; i < server.transactions->length; ++i) {
- struct sway_transaction *transaction = server.transactions->items[i];
transaction_apply(transaction);
transaction_destroy(transaction);
+ list_del(server.transactions, 0);
}
- server.transactions->length = 0;
+ idle_inhibit_v1_check_active(server.idle_inhibit_manager_v1);
}
static int handle_timeout(void *data) {
struct sway_transaction *transaction = data;
- wlr_log(L_DEBUG, "Transaction %p timed out (%li waiting)",
+ wlr_log(WLR_DEBUG, "Transaction %p timed out (%li waiting)",
transaction, transaction->num_waiting);
transaction->num_waiting = 0;
transaction_progress_queue();
@@ -283,8 +271,8 @@ static bool should_configure(struct sway_container *con,
return true;
}
-void transaction_commit(struct sway_transaction *transaction) {
- wlr_log(L_DEBUG, "Transaction %p committing with %i instructions",
+static void transaction_commit(struct sway_transaction *transaction) {
+ wlr_log(WLR_DEBUG, "Transaction %p committing with %i instructions",
transaction, transaction->instructions->length);
transaction->num_waiting = 0;
for (int i = 0; i < transaction->instructions->length; ++i) {
@@ -317,9 +305,10 @@ void transaction_commit(struct sway_transaction *transaction) {
} else {
// There are no other transactions in progress, and this one has nothing
// to wait for, so we can skip the queue.
- wlr_log(L_DEBUG, "Transaction %p has nothing to wait for", transaction);
+ wlr_log(WLR_DEBUG, "Transaction %p has nothing to wait for", transaction);
transaction_apply(transaction);
transaction_destroy(transaction);
+ idle_inhibit_v1_check_active(server.idle_inhibit_manager_v1);
return;
}
@@ -327,7 +316,7 @@ void transaction_commit(struct sway_transaction *transaction) {
// Set up a timer which the views must respond within
transaction->timer = wl_event_loop_add_timer(server.wl_event_loop,
handle_timeout, transaction);
- wl_event_source_timer_update(transaction->timer, TIMEOUT_MS);
+ wl_event_source_timer_update(transaction->timer, txn_timeout_ms);
}
// The debug tree shows the pending/live tree. Here is a good place to
@@ -347,7 +336,7 @@ static void set_instruction_ready(
struct timespec *start = &transaction->commit_time;
float ms = (now.tv_sec - start->tv_sec) * 1000 +
(now.tv_nsec - start->tv_nsec) / 1000000.0;
- wlr_log(L_DEBUG, "Transaction %p: %li/%li ready in %.1fms (%s)",
+ wlr_log(WLR_DEBUG, "Transaction %p: %li/%li ready in %.1fms (%s)",
transaction,
transaction->num_configures - transaction->num_waiting + 1,
transaction->num_configures, ms,
@@ -358,11 +347,11 @@ static void set_instruction_ready(
// If all views are ready, apply the transaction.
// If the transaction has timed out then its num_waiting will be 0 already.
if (transaction->num_waiting > 0 && --transaction->num_waiting == 0) {
-#if !TRANSACTION_DEBUG
- wlr_log(L_DEBUG, "Transaction %p is ready", transaction);
- wl_event_source_timer_update(transaction->timer, 0);
- transaction_progress_queue();
-#endif
+ if (!txn_debug) {
+ wlr_log(WLR_DEBUG, "Transaction %p is ready", transaction);
+ wl_event_source_timer_update(transaction->timer, 0);
+ transaction_progress_queue();
+ }
}
}
@@ -374,7 +363,9 @@ static void set_instructions_ready(struct sway_view *view, int index) {
for (int i = 0; i <= index; ++i) {
struct sway_transaction_instruction *instruction =
view->swayc->instructions->items[i];
- set_instruction_ready(instruction);
+ if (!instruction->ready) {
+ set_instruction_ready(instruction);
+ }
}
}
@@ -413,3 +404,17 @@ struct wlr_texture *transaction_get_saved_texture(struct sway_view *view,
*height = instruction->saved_buffer_height;
return instruction->saved_buffer->texture;
}
+
+void transaction_commit_dirty(void) {
+ if (!server.dirty_containers->length) {
+ return;
+ }
+ struct sway_transaction *transaction = transaction_create();
+ for (int i = 0; i < server.dirty_containers->length; ++i) {
+ struct sway_container *container = server.dirty_containers->items[i];
+ transaction_add_container(transaction, container);
+ container->dirty = false;
+ }
+ server.dirty_containers->length = 0;
+ transaction_commit(transaction);
+}
diff --git a/sway/desktop/xdg_shell.c b/sway/desktop/xdg_shell.c
index 47604c31..f3e4fef8 100644
--- a/sway/desktop/xdg_shell.c
+++ b/sway/desktop/xdg_shell.c
@@ -1,4 +1,5 @@
#define _POSIX_C_SOURCE 199309L
+#include <float.h>
#include <stdbool.h>
#include <stdlib.h>
#include <wayland-server.h>
@@ -45,6 +46,24 @@ static void popup_handle_destroy(struct wl_listener *listener, void *data) {
view_child_destroy(&popup->child);
}
+static void popup_unconstrain(struct sway_xdg_popup *popup) {
+ struct sway_view *view = popup->child.view;
+ struct wlr_xdg_popup *wlr_popup = popup->wlr_xdg_surface->popup;
+
+ struct sway_container *output = container_parent(view->swayc, C_OUTPUT);
+
+ // the output box expressed in the coordinate system of the toplevel parent
+ // of the popup
+ struct wlr_box output_toplevel_sx_box = {
+ .x = output->x - view->x,
+ .y = output->y - view->y,
+ .width = output->width,
+ .height = output->height,
+ };
+
+ wlr_xdg_popup_unconstrain_from_box(wlr_popup, &output_toplevel_sx_box);
+}
+
static struct sway_xdg_popup *popup_create(
struct wlr_xdg_popup *wlr_popup, struct sway_view *view) {
struct wlr_xdg_surface *xdg_surface = wlr_popup->base;
@@ -55,12 +74,15 @@ static struct sway_xdg_popup *popup_create(
return NULL;
}
view_child_init(&popup->child, &popup_impl, view, xdg_surface->surface);
+ popup->wlr_xdg_surface = xdg_surface;
wl_signal_add(&xdg_surface->events.new_popup, &popup->new_popup);
popup->new_popup.notify = popup_handle_new_popup;
wl_signal_add(&xdg_surface->events.destroy, &popup->destroy);
popup->destroy.notify = popup_handle_destroy;
+ popup_unconstrain(popup);
+
return popup;
}
@@ -74,6 +96,16 @@ static struct sway_xdg_shell_view *xdg_shell_view_from_view(
return (struct sway_xdg_shell_view *)view;
}
+static void get_constraints(struct sway_view *view, double *min_width,
+ double *max_width, double *min_height, double *max_height) {
+ struct wlr_xdg_toplevel_state *state =
+ &view->wlr_xdg_surface->toplevel->current;
+ *min_width = state->min_width > 0 ? state->min_width : DBL_MIN;
+ *max_width = state->max_width > 0 ? state->max_width : DBL_MAX;
+ *min_height = state->min_height > 0 ? state->min_height : DBL_MIN;
+ *max_height = state->max_height > 0 ? state->max_height : DBL_MAX;
+}
+
static const char *get_string_prop(struct sway_view *view, enum sway_view_prop prop) {
if (xdg_shell_view_from_view(view) == NULL) {
return NULL;
@@ -167,6 +199,7 @@ static void destroy(struct sway_view *view) {
}
static const struct sway_view_impl view_impl = {
+ .get_constraints = get_constraints,
.get_string_prop = get_string_prop,
.configure = configure,
.set_activated = set_activated,
@@ -192,10 +225,24 @@ static void handle_commit(struct wl_listener *listener, void *data) {
transaction_notify_view_ready(view, xdg_surface->configure_serial);
}
- view_update_title(view, false);
view_damage_from(view);
}
+static void handle_set_title(struct wl_listener *listener, void *data) {
+ struct sway_xdg_shell_view *xdg_shell_view =
+ wl_container_of(listener, xdg_shell_view, set_title);
+ struct sway_view *view = &xdg_shell_view->view;
+ view_update_title(view, false);
+ view_execute_criteria(view);
+}
+
+static void handle_set_app_id(struct wl_listener *listener, void *data) {
+ struct sway_xdg_shell_view *xdg_shell_view =
+ wl_container_of(listener, xdg_shell_view, set_app_id);
+ struct sway_view *view = &xdg_shell_view->view;
+ view_execute_criteria(view);
+}
+
static void handle_new_popup(struct wl_listener *listener, void *data) {
struct sway_xdg_shell_view *xdg_shell_view =
wl_container_of(listener, xdg_shell_view, new_popup);
@@ -222,8 +269,37 @@ static void handle_request_fullscreen(struct wl_listener *listener, void *data)
view_set_fullscreen(view, e->fullscreen);
- struct sway_container *ws = container_parent(view->swayc, C_WORKSPACE);
- arrange_and_commit(ws);
+ struct sway_container *output = container_parent(view->swayc, C_OUTPUT);
+ arrange_windows(output);
+ transaction_commit_dirty();
+}
+
+static void handle_request_move(struct wl_listener *listener, void *data) {
+ struct sway_xdg_shell_view *xdg_shell_view =
+ wl_container_of(listener, xdg_shell_view, request_move);
+ struct sway_view *view = &xdg_shell_view->view;
+ if (!container_is_floating(view->swayc)) {
+ return;
+ }
+ struct wlr_xdg_toplevel_move_event *e = data;
+ struct sway_seat *seat = e->seat->seat->data;
+ if (e->serial == seat->last_button_serial) {
+ seat_begin_move(seat, view->swayc, seat->last_button);
+ }
+}
+
+static void handle_request_resize(struct wl_listener *listener, void *data) {
+ struct sway_xdg_shell_view *xdg_shell_view =
+ wl_container_of(listener, xdg_shell_view, request_resize);
+ struct sway_view *view = &xdg_shell_view->view;
+ if (!container_is_floating(view->swayc)) {
+ return;
+ }
+ struct wlr_xdg_toplevel_resize_event *e = data;
+ struct sway_seat *seat = e->seat->seat->data;
+ if (e->serial == seat->last_button_serial) {
+ seat_begin_resize(seat, view->swayc, seat->last_button, e->edges);
+ }
}
static void handle_unmap(struct wl_listener *listener, void *data) {
@@ -240,6 +316,10 @@ static void handle_unmap(struct wl_listener *listener, void *data) {
wl_list_remove(&xdg_shell_view->commit.link);
wl_list_remove(&xdg_shell_view->new_popup.link);
wl_list_remove(&xdg_shell_view->request_fullscreen.link);
+ wl_list_remove(&xdg_shell_view->request_move.link);
+ wl_list_remove(&xdg_shell_view->request_resize.link);
+ wl_list_remove(&xdg_shell_view->set_title.link);
+ wl_list_remove(&xdg_shell_view->set_app_id.link);
}
static void handle_map(struct wl_listener *listener, void *data) {
@@ -251,8 +331,8 @@ static void handle_map(struct wl_listener *listener, void *data) {
view->natural_width = view->wlr_xdg_surface->geometry.width;
view->natural_height = view->wlr_xdg_surface->geometry.height;
if (!view->natural_width && !view->natural_height) {
- view->natural_width = view->wlr_xdg_surface->surface->current->width;
- view->natural_height = view->wlr_xdg_surface->surface->current->height;
+ view->natural_width = view->wlr_xdg_surface->surface->current.width;
+ view->natural_height = view->wlr_xdg_surface->surface->current.height;
}
view_map(view, view->wlr_xdg_surface->surface);
@@ -260,10 +340,11 @@ static void handle_map(struct wl_listener *listener, void *data) {
if (xdg_surface->toplevel->client_pending.fullscreen) {
view_set_fullscreen(view, true);
struct sway_container *ws = container_parent(view->swayc, C_WORKSPACE);
- arrange_and_commit(ws);
+ arrange_windows(ws);
} else {
- arrange_and_commit(view->swayc->parent);
+ arrange_windows(view->swayc->parent);
}
+ transaction_commit_dirty();
xdg_shell_view->commit.notify = handle_commit;
wl_signal_add(&xdg_surface->surface->events.commit,
@@ -276,6 +357,22 @@ static void handle_map(struct wl_listener *listener, void *data) {
xdg_shell_view->request_fullscreen.notify = handle_request_fullscreen;
wl_signal_add(&xdg_surface->toplevel->events.request_fullscreen,
&xdg_shell_view->request_fullscreen);
+
+ xdg_shell_view->request_move.notify = handle_request_move;
+ wl_signal_add(&xdg_surface->toplevel->events.request_move,
+ &xdg_shell_view->request_move);
+
+ xdg_shell_view->request_resize.notify = handle_request_resize;
+ wl_signal_add(&xdg_surface->toplevel->events.request_resize,
+ &xdg_shell_view->request_resize);
+
+ xdg_shell_view->set_title.notify = handle_set_title;
+ wl_signal_add(&xdg_surface->toplevel->events.set_title,
+ &xdg_shell_view->set_title);
+
+ xdg_shell_view->set_app_id.notify = handle_set_app_id;
+ wl_signal_add(&xdg_surface->toplevel->events.set_app_id,
+ &xdg_shell_view->set_app_id);
}
static void handle_destroy(struct wl_listener *listener, void *data) {
@@ -304,11 +401,11 @@ void handle_xdg_shell_surface(struct wl_listener *listener, void *data) {
struct wlr_xdg_surface *xdg_surface = data;
if (xdg_surface->role == WLR_XDG_SURFACE_ROLE_POPUP) {
- wlr_log(L_DEBUG, "New xdg_shell popup");
+ wlr_log(WLR_DEBUG, "New xdg_shell popup");
return;
}
- wlr_log(L_DEBUG, "New xdg_shell toplevel title='%s' app_id='%s'",
+ wlr_log(WLR_DEBUG, "New xdg_shell toplevel title='%s' app_id='%s'",
xdg_surface->toplevel->title, xdg_surface->toplevel->app_id);
wlr_xdg_surface_ping(xdg_surface);
diff --git a/sway/desktop/xdg_shell_v6.c b/sway/desktop/xdg_shell_v6.c
index b28c4b9c..46fd4769 100644
--- a/sway/desktop/xdg_shell_v6.c
+++ b/sway/desktop/xdg_shell_v6.c
@@ -1,4 +1,5 @@
#define _POSIX_C_SOURCE 199309L
+#include <float.h>
#include <stdbool.h>
#include <stdlib.h>
#include <wayland-server.h>
@@ -44,6 +45,24 @@ static void popup_handle_destroy(struct wl_listener *listener, void *data) {
view_child_destroy(&popup->child);
}
+static void popup_unconstrain(struct sway_xdg_popup_v6 *popup) {
+ struct sway_view *view = popup->child.view;
+ struct wlr_xdg_popup_v6 *wlr_popup = popup->wlr_xdg_surface_v6->popup;
+
+ struct sway_container *output = container_parent(view->swayc, C_OUTPUT);
+
+ // the output box expressed in the coordinate system of the toplevel parent
+ // of the popup
+ struct wlr_box output_toplevel_sx_box = {
+ .x = output->x - view->x,
+ .y = output->y - view->y,
+ .width = output->width,
+ .height = output->height,
+ };
+
+ wlr_xdg_popup_v6_unconstrain_from_box(wlr_popup, &output_toplevel_sx_box);
+}
+
static struct sway_xdg_popup_v6 *popup_create(
struct wlr_xdg_popup_v6 *wlr_popup, struct sway_view *view) {
struct wlr_xdg_surface_v6 *xdg_surface = wlr_popup->base;
@@ -54,12 +73,15 @@ static struct sway_xdg_popup_v6 *popup_create(
return NULL;
}
view_child_init(&popup->child, &popup_impl, view, xdg_surface->surface);
+ popup->wlr_xdg_surface_v6 = xdg_surface;
wl_signal_add(&xdg_surface->events.new_popup, &popup->new_popup);
popup->new_popup.notify = popup_handle_new_popup;
wl_signal_add(&xdg_surface->events.destroy, &popup->destroy);
popup->destroy.notify = popup_handle_destroy;
+ popup_unconstrain(popup);
+
return popup;
}
@@ -73,6 +95,16 @@ static struct sway_xdg_shell_v6_view *xdg_shell_v6_view_from_view(
return (struct sway_xdg_shell_v6_view *)view;
}
+static void get_constraints(struct sway_view *view, double *min_width,
+ double *max_width, double *min_height, double *max_height) {
+ struct wlr_xdg_toplevel_v6_state *state =
+ &view->wlr_xdg_surface_v6->toplevel->current;
+ *min_width = state->min_width > 0 ? state->min_width : DBL_MIN;
+ *max_width = state->max_width > 0 ? state->max_width : DBL_MAX;
+ *min_height = state->min_height > 0 ? state->min_height : DBL_MIN;
+ *max_height = state->max_height > 0 ? state->max_height : DBL_MAX;
+}
+
static const char *get_string_prop(struct sway_view *view, enum sway_view_prop prop) {
if (xdg_shell_v6_view_from_view(view) == NULL) {
return NULL;
@@ -163,6 +195,7 @@ static void destroy(struct sway_view *view) {
}
static const struct sway_view_impl view_impl = {
+ .get_constraints = get_constraints,
.get_string_prop = get_string_prop,
.configure = configure,
.set_activated = set_activated,
@@ -187,10 +220,24 @@ static void handle_commit(struct wl_listener *listener, void *data) {
transaction_notify_view_ready(view, xdg_surface_v6->configure_serial);
}
- view_update_title(view, false);
view_damage_from(view);
}
+static void handle_set_title(struct wl_listener *listener, void *data) {
+ struct sway_xdg_shell_v6_view *xdg_shell_v6_view =
+ wl_container_of(listener, xdg_shell_v6_view, set_title);
+ struct sway_view *view = &xdg_shell_v6_view->view;
+ view_update_title(view, false);
+ view_execute_criteria(view);
+}
+
+static void handle_set_app_id(struct wl_listener *listener, void *data) {
+ struct sway_xdg_shell_v6_view *xdg_shell_v6_view =
+ wl_container_of(listener, xdg_shell_v6_view, set_app_id);
+ struct sway_view *view = &xdg_shell_v6_view->view;
+ view_execute_criteria(view);
+}
+
static void handle_new_popup(struct wl_listener *listener, void *data) {
struct sway_xdg_shell_v6_view *xdg_shell_v6_view =
wl_container_of(listener, xdg_shell_v6_view, new_popup);
@@ -217,8 +264,37 @@ static void handle_request_fullscreen(struct wl_listener *listener, void *data)
view_set_fullscreen(view, e->fullscreen);
- struct sway_container *ws = container_parent(view->swayc, C_WORKSPACE);
- arrange_and_commit(ws);
+ struct sway_container *output = container_parent(view->swayc, C_OUTPUT);
+ arrange_windows(output);
+ transaction_commit_dirty();
+}
+
+static void handle_request_move(struct wl_listener *listener, void *data) {
+ struct sway_xdg_shell_v6_view *xdg_shell_v6_view =
+ wl_container_of(listener, xdg_shell_v6_view, request_move);
+ struct sway_view *view = &xdg_shell_v6_view->view;
+ if (!container_is_floating(view->swayc)) {
+ return;
+ }
+ struct wlr_xdg_toplevel_v6_move_event *e = data;
+ struct sway_seat *seat = e->seat->seat->data;
+ if (e->serial == seat->last_button_serial) {
+ seat_begin_move(seat, view->swayc, seat->last_button);
+ }
+}
+
+static void handle_request_resize(struct wl_listener *listener, void *data) {
+ struct sway_xdg_shell_v6_view *xdg_shell_v6_view =
+ wl_container_of(listener, xdg_shell_v6_view, request_resize);
+ struct sway_view *view = &xdg_shell_v6_view->view;
+ if (!container_is_floating(view->swayc)) {
+ return;
+ }
+ struct wlr_xdg_toplevel_v6_resize_event *e = data;
+ struct sway_seat *seat = e->seat->seat->data;
+ if (e->serial == seat->last_button_serial) {
+ seat_begin_resize(seat, view->swayc, seat->last_button, e->edges);
+ }
}
static void handle_unmap(struct wl_listener *listener, void *data) {
@@ -235,6 +311,10 @@ static void handle_unmap(struct wl_listener *listener, void *data) {
wl_list_remove(&xdg_shell_v6_view->commit.link);
wl_list_remove(&xdg_shell_v6_view->new_popup.link);
wl_list_remove(&xdg_shell_v6_view->request_fullscreen.link);
+ wl_list_remove(&xdg_shell_v6_view->request_move.link);
+ wl_list_remove(&xdg_shell_v6_view->request_resize.link);
+ wl_list_remove(&xdg_shell_v6_view->set_title.link);
+ wl_list_remove(&xdg_shell_v6_view->set_app_id.link);
}
static void handle_map(struct wl_listener *listener, void *data) {
@@ -246,8 +326,8 @@ static void handle_map(struct wl_listener *listener, void *data) {
view->natural_width = view->wlr_xdg_surface_v6->geometry.width;
view->natural_height = view->wlr_xdg_surface_v6->geometry.height;
if (!view->natural_width && !view->natural_height) {
- view->natural_width = view->wlr_xdg_surface_v6->surface->current->width;
- view->natural_height = view->wlr_xdg_surface_v6->surface->current->height;
+ view->natural_width = view->wlr_xdg_surface_v6->surface->current.width;
+ view->natural_height = view->wlr_xdg_surface_v6->surface->current.height;
}
view_map(view, view->wlr_xdg_surface_v6->surface);
@@ -255,10 +335,11 @@ static void handle_map(struct wl_listener *listener, void *data) {
if (xdg_surface->toplevel->client_pending.fullscreen) {
view_set_fullscreen(view, true);
struct sway_container *ws = container_parent(view->swayc, C_WORKSPACE);
- arrange_and_commit(ws);
+ arrange_windows(ws);
} else {
- arrange_and_commit(view->swayc->parent);
+ arrange_windows(view->swayc->parent);
}
+ transaction_commit_dirty();
xdg_shell_v6_view->commit.notify = handle_commit;
wl_signal_add(&xdg_surface->surface->events.commit,
@@ -271,6 +352,22 @@ static void handle_map(struct wl_listener *listener, void *data) {
xdg_shell_v6_view->request_fullscreen.notify = handle_request_fullscreen;
wl_signal_add(&xdg_surface->toplevel->events.request_fullscreen,
&xdg_shell_v6_view->request_fullscreen);
+
+ xdg_shell_v6_view->request_move.notify = handle_request_move;
+ wl_signal_add(&xdg_surface->toplevel->events.request_move,
+ &xdg_shell_v6_view->request_move);
+
+ xdg_shell_v6_view->request_resize.notify = handle_request_resize;
+ wl_signal_add(&xdg_surface->toplevel->events.request_resize,
+ &xdg_shell_v6_view->request_resize);
+
+ xdg_shell_v6_view->set_title.notify = handle_set_title;
+ wl_signal_add(&xdg_surface->toplevel->events.set_title,
+ &xdg_shell_v6_view->set_title);
+
+ xdg_shell_v6_view->set_app_id.notify = handle_set_app_id;
+ wl_signal_add(&xdg_surface->toplevel->events.set_app_id,
+ &xdg_shell_v6_view->set_app_id);
}
static void handle_destroy(struct wl_listener *listener, void *data) {
@@ -295,11 +392,11 @@ void handle_xdg_shell_v6_surface(struct wl_listener *listener, void *data) {
struct wlr_xdg_surface_v6 *xdg_surface = data;
if (xdg_surface->role == WLR_XDG_SURFACE_V6_ROLE_POPUP) {
- wlr_log(L_DEBUG, "New xdg_shell_v6 popup");
+ wlr_log(WLR_DEBUG, "New xdg_shell_v6 popup");
return;
}
- wlr_log(L_DEBUG, "New xdg_shell_v6 toplevel title='%s' app_id='%s'",
+ wlr_log(WLR_DEBUG, "New xdg_shell_v6 toplevel title='%s' app_id='%s'",
xdg_surface->toplevel->title, xdg_surface->toplevel->app_id);
wlr_xdg_surface_v6_ping(xdg_surface);
diff --git a/sway/desktop/xwayland.c b/sway/desktop/xwayland.c
index b3b1473d..65d4fcd4 100644
--- a/sway/desktop/xwayland.c
+++ b/sway/desktop/xwayland.c
@@ -69,16 +69,11 @@ static void unmanaged_handle_map(struct wl_listener *listener, void *data) {
surface->ly = xsurface->y;
desktop_damage_surface(xsurface->surface, surface->lx, surface->ly, true);
- if (!wlr_xwayland_surface_is_unmanaged(xsurface)) {
- struct sway_seat *seat = input_manager_current_seat(input_manager);
- struct wlr_xwayland *xwayland =
- seat->input->server->xwayland.wlr_xwayland;
- wlr_xwayland_set_seat(xwayland, seat->wlr_seat);
- seat_set_focus_surface(seat, xsurface->surface);
- }
-
- // TODO: we don't send surface enter/leave events to xwayland unmanaged
- // surfaces, but xwayland doesn't support HiDPI anyway
+ struct sway_seat *seat = input_manager_current_seat(input_manager);
+ struct wlr_xwayland *xwayland =
+ seat->input->server->xwayland.wlr_xwayland;
+ wlr_xwayland_set_seat(xwayland, seat->wlr_seat);
+ seat_set_focus_surface(seat, xsurface->surface, false);
}
static void unmanaged_handle_unmap(struct wl_listener *listener, void *data) {
@@ -89,18 +84,16 @@ static void unmanaged_handle_unmap(struct wl_listener *listener, void *data) {
wl_list_remove(&surface->link);
wl_list_remove(&surface->commit.link);
- if (!wlr_xwayland_surface_is_unmanaged(xsurface)) {
- struct sway_seat *seat = input_manager_current_seat(input_manager);
- if (seat->wlr_seat->keyboard_state.focused_surface ==
- xsurface->surface) {
- // Restore focus
- struct sway_container *previous =
- seat_get_focus_inactive(seat, &root_container);
- if (previous) {
- // Hack to get seat to re-focus the return value of get_focus
- seat_set_focus(seat, previous->parent);
- seat_set_focus(seat, previous);
- }
+ struct sway_seat *seat = input_manager_current_seat(input_manager);
+ if (seat->wlr_seat->keyboard_state.focused_surface ==
+ xsurface->surface) {
+ // Restore focus
+ struct sway_container *previous =
+ seat_get_focus_inactive(seat, &root_container);
+ if (previous) {
+ // Hack to get seat to re-focus the return value of get_focus
+ seat_set_focus(seat, previous->parent);
+ seat_set_focus(seat, previous);
}
}
}
@@ -119,7 +112,7 @@ static struct sway_xwayland_unmanaged *create_unmanaged(
struct sway_xwayland_unmanaged *surface =
calloc(1, sizeof(struct sway_xwayland_unmanaged));
if (surface == NULL) {
- wlr_log(L_ERROR, "Allocation failed");
+ wlr_log(WLR_ERROR, "Allocation failed");
return NULL;
}
@@ -246,6 +239,14 @@ static bool wants_floating(struct sway_view *view) {
return false;
}
+static bool has_client_side_decorations(struct sway_view *view) {
+ if (xwayland_view_from_view(view) == NULL) {
+ return false;
+ }
+ struct wlr_xwayland_surface *surface = view->wlr_xwayland_surface;
+ return surface->decorations != WLR_XWAYLAND_SURFACE_DECORATIONS_ALL;
+}
+
static void _close(struct sway_view *view) {
if (xwayland_view_from_view(view) == NULL) {
return;
@@ -269,6 +270,7 @@ static const struct sway_view_impl view_impl = {
.set_tiled = set_tiled,
.set_fullscreen = set_fullscreen,
.wants_floating = wants_floating,
+ .has_client_side_decorations = has_client_side_decorations,
.close = _close,
.destroy = destroy,
};
@@ -278,15 +280,42 @@ static void handle_commit(struct wl_listener *listener, void *data) {
wl_container_of(listener, xwayland_view, commit);
struct sway_view *view = &xwayland_view->view;
struct wlr_xwayland_surface *xsurface = view->wlr_xwayland_surface;
- struct wlr_surface_state *surface_state = xsurface->surface->current;
+ struct wlr_surface_state *surface_state = &xsurface->surface->current;
if (view->swayc->instructions->length) {
transaction_notify_view_ready_by_size(view,
surface_state->width, surface_state->height);
+ } else if (container_is_floating(view->swayc)) {
+ view_update_size(view, surface_state->width, surface_state->height);
}
+
view_damage_from(view);
}
+static void handle_destroy(struct wl_listener *listener, void *data) {
+ struct sway_xwayland_view *xwayland_view =
+ wl_container_of(listener, xwayland_view, destroy);
+ struct sway_view *view = &xwayland_view->view;
+
+ if (view->surface) {
+ view_unmap(view);
+ wl_list_remove(&xwayland_view->commit.link);
+ }
+
+ wl_list_remove(&xwayland_view->destroy.link);
+ wl_list_remove(&xwayland_view->request_configure.link);
+ wl_list_remove(&xwayland_view->request_fullscreen.link);
+ wl_list_remove(&xwayland_view->request_move.link);
+ wl_list_remove(&xwayland_view->request_resize.link);
+ wl_list_remove(&xwayland_view->set_title.link);
+ wl_list_remove(&xwayland_view->set_class.link);
+ wl_list_remove(&xwayland_view->set_window_type.link);
+ wl_list_remove(&xwayland_view->set_hints.link);
+ wl_list_remove(&xwayland_view->map.link);
+ wl_list_remove(&xwayland_view->unmap.link);
+ view_destroy(&xwayland_view->view);
+}
+
static void handle_unmap(struct wl_listener *listener, void *data) {
struct sway_xwayland_view *xwayland_view =
wl_container_of(listener, xwayland_view, unmap);
@@ -307,6 +336,15 @@ static void handle_map(struct wl_listener *listener, void *data) {
struct wlr_xwayland_surface *xsurface = data;
struct sway_view *view = &xwayland_view->view;
+ if (xsurface->override_redirect) {
+ // This window used not to have the override redirect flag and has it
+ // now. Switch to unmanaged.
+ handle_destroy(&xwayland_view->destroy, view);
+ struct sway_xwayland_unmanaged *unmanaged = create_unmanaged(xsurface);
+ unmanaged_handle_map(&unmanaged->map, xsurface);
+ return;
+ }
+
view->natural_width = xsurface->width;
view->natural_height = xsurface->height;
@@ -321,31 +359,11 @@ static void handle_map(struct wl_listener *listener, void *data) {
if (xsurface->fullscreen) {
view_set_fullscreen(view, true);
struct sway_container *ws = container_parent(view->swayc, C_WORKSPACE);
- arrange_and_commit(ws);
+ arrange_windows(ws);
} else {
- arrange_and_commit(view->swayc->parent);
- }
-}
-
-static void handle_destroy(struct wl_listener *listener, void *data) {
- struct sway_xwayland_view *xwayland_view =
- wl_container_of(listener, xwayland_view, destroy);
- struct sway_view *view = &xwayland_view->view;
-
- if (view->surface) {
- view_unmap(view);
- wl_list_remove(&xwayland_view->commit.link);
+ arrange_windows(view->swayc->parent);
}
-
- wl_list_remove(&xwayland_view->destroy.link);
- wl_list_remove(&xwayland_view->request_configure.link);
- wl_list_remove(&xwayland_view->request_fullscreen.link);
- wl_list_remove(&xwayland_view->set_title.link);
- wl_list_remove(&xwayland_view->set_class.link);
- wl_list_remove(&xwayland_view->set_window_type.link);
- wl_list_remove(&xwayland_view->map.link);
- wl_list_remove(&xwayland_view->unmap.link);
- view_destroy(&xwayland_view->view);
+ transaction_commit_dirty();
}
static void handle_request_configure(struct wl_listener *listener, void *data) {
@@ -379,8 +397,40 @@ static void handle_request_fullscreen(struct wl_listener *listener, void *data)
}
view_set_fullscreen(view, xsurface->fullscreen);
- struct sway_container *ws = container_parent(view->swayc, C_WORKSPACE);
- arrange_and_commit(ws);
+ struct sway_container *output = container_parent(view->swayc, C_OUTPUT);
+ arrange_windows(output);
+ transaction_commit_dirty();
+}
+
+static void handle_request_move(struct wl_listener *listener, void *data) {
+ struct sway_xwayland_view *xwayland_view =
+ wl_container_of(listener, xwayland_view, request_move);
+ struct sway_view *view = &xwayland_view->view;
+ struct wlr_xwayland_surface *xsurface = view->wlr_xwayland_surface;
+ if (!xsurface->mapped) {
+ return;
+ }
+ if (!container_is_floating(view->swayc)) {
+ return;
+ }
+ struct sway_seat *seat = input_manager_current_seat(input_manager);
+ seat_begin_move(seat, view->swayc, seat->last_button);
+}
+
+static void handle_request_resize(struct wl_listener *listener, void *data) {
+ struct sway_xwayland_view *xwayland_view =
+ wl_container_of(listener, xwayland_view, request_resize);
+ struct sway_view *view = &xwayland_view->view;
+ struct wlr_xwayland_surface *xsurface = view->wlr_xwayland_surface;
+ if (!xsurface->mapped) {
+ return;
+ }
+ if (!container_is_floating(view->swayc)) {
+ return;
+ }
+ struct wlr_xwayland_resize_event *e = data;
+ struct sway_seat *seat = input_manager_current_seat(input_manager);
+ seat_begin_resize(seat, view->swayc, seat->last_button, e->edges);
}
static void handle_set_title(struct wl_listener *listener, void *data) {
@@ -417,6 +467,25 @@ static void handle_set_window_type(struct wl_listener *listener, void *data) {
view_execute_criteria(view);
}
+static void handle_set_hints(struct wl_listener *listener, void *data) {
+ struct sway_xwayland_view *xwayland_view =
+ wl_container_of(listener, xwayland_view, set_hints);
+ struct sway_view *view = &xwayland_view->view;
+ struct wlr_xwayland_surface *xsurface = view->wlr_xwayland_surface;
+ if (!xsurface->mapped) {
+ return;
+ }
+ if (!xsurface->hints_urgency && view->urgent_timer) {
+ // The view is is in the timeout period. We'll ignore the request to
+ // unset urgency so that the view remains urgent until the timer clears
+ // it.
+ return;
+ }
+ if (view->allow_request_urgent) {
+ view_set_urgent(view, (bool)xsurface->hints_urgency);
+ }
+}
+
struct sway_view *view_from_wlr_xwayland_surface(
struct wlr_xwayland_surface *xsurface) {
return xsurface->data;
@@ -427,14 +496,13 @@ void handle_xwayland_surface(struct wl_listener *listener, void *data) {
xwayland_surface);
struct wlr_xwayland_surface *xsurface = data;
- if (wlr_xwayland_surface_is_unmanaged(xsurface) ||
- xsurface->override_redirect) {
- wlr_log(L_DEBUG, "New xwayland unmanaged surface");
+ if (xsurface->override_redirect) {
+ wlr_log(WLR_DEBUG, "New xwayland unmanaged surface");
create_unmanaged(xsurface);
return;
}
- wlr_log(L_DEBUG, "New xwayland surface title='%s' class='%s'",
+ wlr_log(WLR_DEBUG, "New xwayland surface title='%s' class='%s'",
xsurface->title, xsurface->class);
struct sway_xwayland_view *xwayland_view =
@@ -457,6 +525,14 @@ void handle_xwayland_surface(struct wl_listener *listener, void *data) {
&xwayland_view->request_fullscreen);
xwayland_view->request_fullscreen.notify = handle_request_fullscreen;
+ wl_signal_add(&xsurface->events.request_move,
+ &xwayland_view->request_move);
+ xwayland_view->request_move.notify = handle_request_move;
+
+ wl_signal_add(&xsurface->events.request_resize,
+ &xwayland_view->request_resize);
+ xwayland_view->request_resize.notify = handle_request_resize;
+
wl_signal_add(&xsurface->events.set_title, &xwayland_view->set_title);
xwayland_view->set_title.notify = handle_set_title;
@@ -467,6 +543,9 @@ void handle_xwayland_surface(struct wl_listener *listener, void *data) {
&xwayland_view->set_window_type);
xwayland_view->set_window_type.notify = handle_set_window_type;
+ wl_signal_add(&xsurface->events.set_hints, &xwayland_view->set_hints);
+ xwayland_view->set_hints.notify = handle_set_hints;
+
wl_signal_add(&xsurface->events.unmap, &xwayland_view->unmap);
xwayland_view->unmap.notify = handle_unmap;
@@ -484,7 +563,7 @@ void handle_xwayland_ready(struct wl_listener *listener, void *data) {
xcb_connection_t *xcb_conn = xcb_connect(NULL, NULL);
int err = xcb_connection_has_error(xcb_conn);
if (err) {
- wlr_log(L_ERROR, "XCB connect failed: %d", err);
+ wlr_log(WLR_ERROR, "XCB connect failed: %d", err);
return;
}
@@ -503,7 +582,7 @@ void handle_xwayland_ready(struct wl_listener *listener, void *data) {
free(reply);
if (error != NULL) {
- wlr_log(L_ERROR, "could not resolve atom %s, X11 error code %d",
+ wlr_log(WLR_ERROR, "could not resolve atom %s, X11 error code %d",
atom_map[i], error->error_code);
free(error);
break;