summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorLibravatar emersion <contact@emersion.fr>2018-05-23 22:54:52 +0100
committerLibravatar emersion <contact@emersion.fr>2018-05-23 22:54:52 +0100
commitcd0fca2ebf81c252b3743c4474a5fdbcd3e2afad (patch)
tree595f1e80551b64de0d4e24f5721bae27acc195d9
parentFix output hotplugging (diff)
parentMerge pull request #2022 from RedSoxFan/ipc-get-marks (diff)
downloadsway-cd0fca2ebf81c252b3743c4474a5fdbcd3e2afad.tar.gz
sway-cd0fca2ebf81c252b3743c4474a5fdbcd3e2afad.tar.zst
sway-cd0fca2ebf81c252b3743c4474a5fdbcd3e2afad.zip
Merge branch 'master' into fix-swaylock-hotplugging
-rw-r--r--include/sway/input/cursor.h3
-rw-r--r--include/sway/input/seat.h6
-rw-r--r--include/sway/tree/container.h15
-rw-r--r--include/sway/tree/view.h6
-rw-r--r--include/swaylock/swaylock.h26
-rw-r--r--meson.build2
-rw-r--r--protocols/meson.build6
-rw-r--r--sway/commands/border.c2
-rw-r--r--sway/commands/layout.c4
-rw-r--r--sway/commands/seat/cursor.c4
-rw-r--r--sway/desktop/output.c518
-rw-r--r--sway/input/cursor.c31
-rw-r--r--sway/input/seat.c16
-rw-r--r--sway/ipc-server.c22
-rw-r--r--sway/tree/arrange.c64
-rw-r--r--sway/tree/container.c336
-rw-r--r--sway/tree/layout.c11
-rw-r--r--sway/tree/view.c86
-rw-r--r--swaylock/main.c158
-rw-r--r--swaylock/render.c2
20 files changed, 985 insertions, 333 deletions
diff --git a/include/sway/input/cursor.h b/include/sway/input/cursor.h
index 20c1c903..42c894a4 100644
--- a/include/sway/input/cursor.h
+++ b/include/sway/input/cursor.h
@@ -29,7 +29,8 @@ struct sway_cursor {
29 29
30void sway_cursor_destroy(struct sway_cursor *cursor); 30void sway_cursor_destroy(struct sway_cursor *cursor);
31struct sway_cursor *sway_cursor_create(struct sway_seat *seat); 31struct sway_cursor *sway_cursor_create(struct sway_seat *seat);
32void cursor_send_pointer_motion(struct sway_cursor *cursor, uint32_t time_msec); 32void cursor_send_pointer_motion(struct sway_cursor *cursor, uint32_t time_msec,
33 bool allow_refocusing);
33void dispatch_cursor_button(struct sway_cursor *cursor, uint32_t time_msec, 34void dispatch_cursor_button(struct sway_cursor *cursor, uint32_t time_msec,
34 uint32_t button, enum wlr_button_state state); 35 uint32_t button, enum wlr_button_state state);
35 36
diff --git a/include/sway/input/seat.h b/include/sway/input/seat.h
index ff76841e..2e4da438 100644
--- a/include/sway/input/seat.h
+++ b/include/sway/input/seat.h
@@ -95,6 +95,12 @@ struct sway_container *seat_get_focus_inactive_view(struct sway_seat *seat,
95 struct sway_container *container); 95 struct sway_container *container);
96 96
97/** 97/**
98 * Return the immediate child of container which was most recently focused.
99 */
100struct sway_container *seat_get_active_child(struct sway_seat *seat,
101 struct sway_container *container);
102
103/**
98 * Iterate over the focus-inactive children of the container calling the 104 * Iterate over the focus-inactive children of the container calling the
99 * function on each. 105 * function on each.
100 */ 106 */
diff --git a/include/sway/tree/container.h b/include/sway/tree/container.h
index ec9e2385..493c70e2 100644
--- a/include/sway/tree/container.h
+++ b/include/sway/tree/container.h
@@ -11,6 +11,12 @@ extern struct sway_container root_container;
11struct sway_view; 11struct sway_view;
12struct sway_seat; 12struct sway_seat;
13 13
14#define TITLEBAR_BORDER_THICKNESS 1
15
16// Padding includes titlebar border
17#define TITLEBAR_H_PADDING 3
18#define TITLEBAR_V_PADDING 4
19
14/** 20/**
15 * Different kinds of containers. 21 * Different kinds of containers.
16 * 22 *
@@ -98,6 +104,8 @@ struct sway_container {
98 // Passed the previous parent 104 // Passed the previous parent
99 struct wl_signal reparent; 105 struct wl_signal reparent;
100 } events; 106 } events;
107
108 struct wl_listener reparent;
101}; 109};
102 110
103struct sway_container *container_create(enum sway_container_type type); 111struct sway_container *container_create(enum sway_container_type type);
@@ -160,7 +168,7 @@ struct sway_container *container_parent(struct sway_container *container,
160 * is a view and the view contains a surface at those coordinates. 168 * is a view and the view contains a surface at those coordinates.
161 */ 169 */
162struct sway_container *container_at(struct sway_container *container, 170struct sway_container *container_at(struct sway_container *container,
163 double lx, double ly, struct wlr_surface **surface, 171 double ox, double oy, struct wlr_surface **surface,
164 double *sx, double *sy); 172 double *sx, double *sy);
165 173
166/** 174/**
@@ -210,4 +218,9 @@ void container_calculate_title_height(struct sway_container *container);
210 218
211void container_notify_child_title_changed(struct sway_container *container); 219void container_notify_child_title_changed(struct sway_container *container);
212 220
221/**
222 * Return the height of a regular title bar.
223 */
224size_t container_titlebar_height(void);
225
213#endif 226#endif
diff --git a/include/sway/tree/view.h b/include/sway/tree/view.h
index 951912d0..0fb8f1b3 100644
--- a/include/sway/tree/view.h
+++ b/include/sway/tree/view.h
@@ -274,4 +274,10 @@ bool view_has_mark(struct sway_view *view, char *mark);
274 274
275void view_update_marks_textures(struct sway_view *view); 275void view_update_marks_textures(struct sway_view *view);
276 276
277/**
278 * Returns true if there's a possibility the view may be rendered on screen.
279 * Intended for damage tracking.
280 */
281bool view_is_visible(struct sway_view *view);
282
277#endif 283#endif
diff --git a/include/swaylock/swaylock.h b/include/swaylock/swaylock.h
index 56ec0afb..27db67cd 100644
--- a/include/swaylock/swaylock.h
+++ b/include/swaylock/swaylock.h
@@ -10,13 +10,13 @@
10#include "wlr-layer-shell-unstable-v1-client-protocol.h" 10#include "wlr-layer-shell-unstable-v1-client-protocol.h"
11 11
12enum auth_state { 12enum auth_state {
13 AUTH_STATE_IDLE, 13 AUTH_STATE_IDLE,
14 AUTH_STATE_CLEAR, 14 AUTH_STATE_CLEAR,
15 AUTH_STATE_INPUT, 15 AUTH_STATE_INPUT,
16 AUTH_STATE_INPUT_NOP, 16 AUTH_STATE_INPUT_NOP,
17 AUTH_STATE_BACKSPACE, 17 AUTH_STATE_BACKSPACE,
18 AUTH_STATE_VALIDATING, 18 AUTH_STATE_VALIDATING,
19 AUTH_STATE_INVALID, 19 AUTH_STATE_INVALID,
20}; 20};
21 21
22struct swaylock_args { 22struct swaylock_args {
@@ -37,12 +37,14 @@ struct swaylock_state {
37 struct zwlr_input_inhibit_manager_v1 *input_inhibit_manager; 37 struct zwlr_input_inhibit_manager_v1 *input_inhibit_manager;
38 struct wl_shm *shm; 38 struct wl_shm *shm;
39 struct wl_list surfaces; 39 struct wl_list surfaces;
40 struct wl_list images;
40 struct swaylock_args args; 41 struct swaylock_args args;
41 cairo_surface_t *background_image; 42 cairo_surface_t *background_image;
42 struct swaylock_password password; 43 struct swaylock_password password;
43 struct swaylock_xkb xkb; 44 struct swaylock_xkb xkb;
44 enum auth_state auth_state; 45 enum auth_state auth_state;
45 bool run_display; 46 bool run_display;
47 struct zxdg_output_manager_v1 *zxdg_output_manager;
46}; 48};
47 49
48struct swaylock_surface { 50struct swaylock_surface {
@@ -50,12 +52,22 @@ struct swaylock_surface {
50 struct swaylock_state *state; 52 struct swaylock_state *state;
51 struct wl_output *output; 53 struct wl_output *output;
52 uint32_t output_global_name; 54 uint32_t output_global_name;
55 struct zxdg_output_v1 *xdg_output;
53 struct wl_surface *surface; 56 struct wl_surface *surface;
54 struct zwlr_layer_surface_v1 *layer_surface; 57 struct zwlr_layer_surface_v1 *layer_surface;
55 struct pool_buffer buffers[2]; 58 struct pool_buffer buffers[2];
56 struct pool_buffer *current_buffer; 59 struct pool_buffer *current_buffer;
57 uint32_t width, height; 60 uint32_t width, height;
58 int32_t scale; 61 int32_t scale;
62 char *output_name;
63 struct wl_list link;
64};
65
66// There is exactly one swaylock_image for each -i argument
67struct swaylock_image {
68 char *path;
69 char *output_name;
70 cairo_surface_t *cairo_surface;
59 struct wl_list link; 71 struct wl_list link;
60}; 72};
61 73
diff --git a/meson.build b/meson.build
index b943236f..d4ee1a11 100644
--- a/meson.build
+++ b/meson.build
@@ -29,7 +29,7 @@ wayland_server = dependency('wayland-server')
29wayland_client = dependency('wayland-client') 29wayland_client = dependency('wayland-client')
30wayland_cursor = dependency('wayland-cursor') 30wayland_cursor = dependency('wayland-cursor')
31wayland_egl = dependency('wayland-egl') 31wayland_egl = dependency('wayland-egl')
32wayland_protos = dependency('wayland-protocols') 32wayland_protos = dependency('wayland-protocols', version: '>=1.14')
33xkbcommon = dependency('xkbcommon') 33xkbcommon = dependency('xkbcommon')
34cairo = dependency('cairo') 34cairo = dependency('cairo')
35pango = dependency('pango') 35pango = dependency('pango')
diff --git a/protocols/meson.build b/protocols/meson.build
index 9966c02f..a031245c 100644
--- a/protocols/meson.build
+++ b/protocols/meson.build
@@ -29,16 +29,18 @@ wayland_scanner_server = generator(
29 29
30client_protocols = [ 30client_protocols = [
31 [wl_protocol_dir, 'stable/xdg-shell/xdg-shell.xml'], 31 [wl_protocol_dir, 'stable/xdg-shell/xdg-shell.xml'],
32 [wl_protocol_dir, 'unstable/xdg-output/xdg-output-unstable-v1.xml'],
32 ['wlr-layer-shell-unstable-v1.xml'], 33 ['wlr-layer-shell-unstable-v1.xml'],
33 ['idle.xml'], 34 ['idle.xml'],
34 ['wlr-input-inhibitor-unstable-v1.xml'] 35 ['wlr-input-inhibitor-unstable-v1.xml'],
35] 36]
36 37
37server_protocols = [ 38server_protocols = [
38 [wl_protocol_dir, 'stable/xdg-shell/xdg-shell.xml'], 39 [wl_protocol_dir, 'stable/xdg-shell/xdg-shell.xml'],
39 [wl_protocol_dir, 'unstable/xdg-shell/xdg-shell-unstable-v6.xml'], 40 [wl_protocol_dir, 'unstable/xdg-shell/xdg-shell-unstable-v6.xml'],
41 [wl_protocol_dir, 'unstable/xdg-output/xdg-output-unstable-v1.xml'],
40 ['wlr-layer-shell-unstable-v1.xml'], 42 ['wlr-layer-shell-unstable-v1.xml'],
41 ['wlr-input-inhibitor-unstable-v1.xml'] 43 ['wlr-input-inhibitor-unstable-v1.xml'],
42] 44]
43 45
44client_protos_src = [] 46client_protos_src = []
diff --git a/sway/commands/border.c b/sway/commands/border.c
index 1eb06a21..4ba361da 100644
--- a/sway/commands/border.c
+++ b/sway/commands/border.c
@@ -41,7 +41,7 @@ struct cmd_results *cmd_border(int argc, char **argv) {
41 41
42 struct sway_seat *seat = input_manager_current_seat(input_manager); 42 struct sway_seat *seat = input_manager_current_seat(input_manager);
43 if (seat->cursor) { 43 if (seat->cursor) {
44 cursor_send_pointer_motion(seat->cursor, 0); 44 cursor_send_pointer_motion(seat->cursor, 0, false);
45 } 45 }
46 46
47 return cmd_results_new(CMD_SUCCESS, NULL, NULL); 47 return cmd_results_new(CMD_SUCCESS, NULL, NULL);
diff --git a/sway/commands/layout.c b/sway/commands/layout.c
index bb36bb18..58728f16 100644
--- a/sway/commands/layout.c
+++ b/sway/commands/layout.c
@@ -39,6 +39,10 @@ struct cmd_results *cmd_layout(int argc, char **argv) {
39 parent->layout = L_HORIZ; 39 parent->layout = L_HORIZ;
40 } else if (strcasecmp(argv[0], "splitv") == 0) { 40 } else if (strcasecmp(argv[0], "splitv") == 0) {
41 parent->layout = L_VERT; 41 parent->layout = L_VERT;
42 } else if (strcasecmp(argv[0], "tabbed") == 0) {
43 parent->layout = L_TABBED;
44 } else if (strcasecmp(argv[0], "stacking") == 0) {
45 parent->layout = L_STACKED;
42 } else if (strcasecmp(argv[0], "toggle") == 0 && argc == 2 && strcasecmp(argv[1], "split") == 0) { 46 } else if (strcasecmp(argv[0], "toggle") == 0 && argc == 2 && strcasecmp(argv[1], "split") == 0) {
43 if (parent->layout == L_HORIZ) { 47 if (parent->layout == L_HORIZ) {
44 parent->layout = L_VERT; 48 parent->layout = L_VERT;
diff --git a/sway/commands/seat/cursor.c b/sway/commands/seat/cursor.c
index 929384b0..4d0a22c7 100644
--- a/sway/commands/seat/cursor.c
+++ b/sway/commands/seat/cursor.c
@@ -36,7 +36,7 @@ struct cmd_results *seat_cmd_cursor(int argc, char **argv) {
36 int delta_x = strtol(argv[1], NULL, 10); 36 int delta_x = strtol(argv[1], NULL, 10);
37 int delta_y = strtol(argv[2], NULL, 10); 37 int delta_y = strtol(argv[2], NULL, 10);
38 wlr_cursor_move(cursor->cursor, NULL, delta_x, delta_y); 38 wlr_cursor_move(cursor->cursor, NULL, delta_x, delta_y);
39 cursor_send_pointer_motion(cursor, 0); 39 cursor_send_pointer_motion(cursor, 0, true);
40 } else if (strcasecmp(argv[0], "set") == 0) { 40 } else if (strcasecmp(argv[0], "set") == 0) {
41 if (argc < 3) { 41 if (argc < 3) {
42 return cmd_results_new(CMD_INVALID, "cursor", expected_syntax); 42 return cmd_results_new(CMD_INVALID, "cursor", expected_syntax);
@@ -45,7 +45,7 @@ struct cmd_results *seat_cmd_cursor(int argc, char **argv) {
45 float x = strtof(argv[1], NULL) / root_container.width; 45 float x = strtof(argv[1], NULL) / root_container.width;
46 float y = strtof(argv[2], NULL) / root_container.height; 46 float y = strtof(argv[2], NULL) / root_container.height;
47 wlr_cursor_warp_absolute(cursor->cursor, NULL, x, y); 47 wlr_cursor_warp_absolute(cursor->cursor, NULL, x, y);
48 cursor_send_pointer_motion(cursor, 0); 48 cursor_send_pointer_motion(cursor, 0, true);
49 } else { 49 } else {
50 if (argc < 2) { 50 if (argc < 2) {
51 return cmd_results_new(CMD_INVALID, "cursor", expected_syntax); 51 return cmd_results_new(CMD_INVALID, "cursor", expected_syntax);
diff --git a/sway/desktop/output.c b/sway/desktop/output.c
index 94562052..765647fd 100644
--- a/sway/desktop/output.c
+++ b/sway/desktop/output.c
@@ -269,17 +269,6 @@ static void render_unmanaged(struct sway_output *output,
269 render_surface_iterator, &data); 269 render_surface_iterator, &data);
270} 270}
271 271
272static void render_view(struct sway_view *view, struct sway_output *output,
273 pixman_region32_t *damage) {
274 struct render_data data = {
275 .output = output,
276 .damage = damage,
277 .alpha = view->swayc->alpha,
278 };
279 output_view_for_each_surface(
280 view, &data.root_geo, render_surface_iterator, &data);
281}
282
283static void render_rect(struct wlr_output *wlr_output, 272static void render_rect(struct wlr_output *wlr_output,
284 pixman_region32_t *output_damage, const struct wlr_box *_box, 273 pixman_region32_t *output_damage, const struct wlr_box *_box,
285 float color[static 4]) { 274 float color[static 4]) {
@@ -310,93 +299,149 @@ damage_finish:
310 pixman_region32_fini(&damage); 299 pixman_region32_fini(&damage);
311} 300}
312 301
302static void premultiply_alpha(float color[4], float opacity) {
303 color[3] *= opacity;
304 color[0] *= color[3];
305 color[1] *= color[3];
306 color[2] *= color[3];
307}
308
309static void render_view_surfaces(struct sway_view *view,
310 struct sway_output *output, pixman_region32_t *damage) {
311 struct render_data data = {
312 .output = output,
313 .damage = damage,
314 .alpha = view->swayc->alpha,
315 };
316 output_view_for_each_surface(
317 view, &data.root_geo, render_surface_iterator, &data);
318}
319
313/** 320/**
314 * Render decorations for a view with "border normal". 321 * Render a view's surface and left/bottom/right borders.
315 *
316 * Care must be taken not to render over the same pixel multiple times,
317 * otherwise the colors will be incorrect when using opacity.
318 */ 322 */
319static void render_container_simple_border_normal(struct sway_output *output, 323static void render_view(struct sway_output *output, pixman_region32_t *damage,
320 pixman_region32_t *output_damage, 324 struct sway_container *con, struct border_colors *colors) {
321 struct sway_container *con, struct border_colors *colors,
322 struct wlr_texture *title_texture, struct wlr_texture *marks_texture) {
323 struct wlr_box box;
324 float color[4];
325 struct sway_view *view = con->sway_view; 325 struct sway_view *view = con->sway_view;
326 float output_scale = output->wlr_output->scale; 326 render_view_surfaces(view, output, damage);
327 327
328 if (view->border_left) { 328 struct wlr_box box;
329 // Child border - left edge 329 float output_scale = output->wlr_output->scale;
330 memcpy(&color, colors->child_border, sizeof(float) * 4); 330 float color[4];
331 color[3] *= con->alpha;
332 box.x = con->x;
333 box.y = con->y + 1;
334 box.width = view->border_thickness;
335 box.height = con->height - 1
336 - view->border_thickness * view->border_bottom;
337 scale_box(&box, output_scale);
338 render_rect(output->wlr_output, output_damage, &box, color);
339 }
340 331
341 if (view->border_right) { 332 if (view->border != B_NONE) {
342 // Child border - right edge 333 if (view->border_left) {
343 if (con->parent->children->length == 1
344 && con->parent->layout == L_HORIZ) {
345 memcpy(&color, colors->indicator, sizeof(float) * 4);
346 } else {
347 memcpy(&color, colors->child_border, sizeof(float) * 4); 334 memcpy(&color, colors->child_border, sizeof(float) * 4);
335 premultiply_alpha(color, con->alpha);
336 box.x = con->x;
337 box.y = view->y;
338 box.width = view->border_thickness;
339 box.height = view->height;
340 scale_box(&box, output_scale);
341 render_rect(output->wlr_output, damage, &box, color);
348 } 342 }
349 color[3] *= con->alpha;
350 box.x = con->x + con->width - view->border_thickness;
351 box.y = con->y + 1;
352 box.width = view->border_thickness;
353 box.height = con->height - 1
354 - view->border_thickness * view->border_bottom;
355 scale_box(&box, output_scale);
356 render_rect(output->wlr_output, output_damage, &box, color);
357 }
358 343
359 if (view->border_bottom) { 344 if (view->border_right) {
360 // Child border - bottom edge 345 if (con->parent->children->length == 1
361 if (con->parent->children->length == 1 346 && con->parent->layout == L_HORIZ) {
362 && con->parent->layout == L_VERT) { 347 memcpy(&color, colors->indicator, sizeof(float) * 4);
363 memcpy(&color, colors->indicator, sizeof(float) * 4); 348 } else {
364 } else { 349 memcpy(&color, colors->child_border, sizeof(float) * 4);
365 memcpy(&color, colors->child_border, sizeof(float) * 4); 350 }
351 premultiply_alpha(color, con->alpha);
352 box.x = view->x + view->width;
353 box.y = view->y;
354 box.width = view->border_thickness;
355 box.height = view->height;
356 scale_box(&box, output_scale);
357 render_rect(output->wlr_output, damage, &box, color);
358 }
359
360 if (view->border_bottom) {
361 if (con->parent->children->length == 1
362 && con->parent->layout == L_VERT) {
363 memcpy(&color, colors->indicator, sizeof(float) * 4);
364 } else {
365 memcpy(&color, colors->child_border, sizeof(float) * 4);
366 }
367 premultiply_alpha(color, con->alpha);
368 box.x = con->x;
369 box.y = view->y + view->height;
370 box.width = con->width;
371 box.height = view->border_thickness;
372 scale_box(&box, output_scale);
373 render_rect(output->wlr_output, damage, &box, color);
366 } 374 }
367 color[3] *= con->alpha;
368 box.x = con->x;
369 box.y = con->y + con->height - view->border_thickness;
370 box.width = con->width;
371 box.height = view->border_thickness;
372 scale_box(&box, output_scale);
373 render_rect(output->wlr_output, output_damage, &box, color);
374 } 375 }
376}
377
378/**
379 * Render a titlebar.
380 *
381 * Care must be taken not to render over the same pixel multiple times,
382 * otherwise the colors will be incorrect when using opacity.
383 *
384 * The height is: 1px border, 3px padding, font height, 3px padding, 1px border
385 * The left side for L_TABBED is: 1px border, 2px padding, title
386 * The left side for other layouts is: 3px padding, title
387 */
388static void render_titlebar(struct sway_output *output,
389 pixman_region32_t *output_damage, struct sway_container *con,
390 int x, int y, int width,
391 struct border_colors *colors, struct wlr_texture *title_texture,
392 struct wlr_texture *marks_texture) {
393 struct wlr_box box;
394 float color[4];
395 struct sway_view *view = con->type == C_VIEW ? con->sway_view : NULL;
396 float output_scale = output->wlr_output->scale;
397 enum sway_container_layout layout = con->parent->layout;
398 bool is_last_child =
399 con->parent->children->items[con->parent->children->length - 1] == con;
375 400
376 // Single pixel bar above title 401 // Single pixel bar above title
377 memcpy(&color, colors->border, sizeof(float) * 4); 402 memcpy(&color, colors->border, sizeof(float) * 4);
378 color[3] *= con->alpha; 403 premultiply_alpha(color, con->alpha);
379 box.x = con->x; 404 box.x = x;
380 box.y = con->y; 405 box.y = y;
381 box.width = con->width; 406 box.width = width;
382 box.height = 1; 407 box.height = TITLEBAR_BORDER_THICKNESS;
383 scale_box(&box, output_scale); 408 scale_box(&box, output_scale);
384 render_rect(output->wlr_output, output_damage, &box, color); 409 render_rect(output->wlr_output, output_damage, &box, color);
385 410
386 // Single pixel bar below title 411 // Single pixel bar below title
387 memcpy(&color, colors->border, sizeof(float) * 4); 412 size_t left_offset = 0, right_offset = 0;
388 color[3] *= con->alpha; 413 bool connects_sides = false;
389 box.x = con->x + view->border_thickness; 414 if (layout == L_HORIZ || layout == L_VERT ||
390 box.y = view->y - 1; 415 (layout == L_STACKED && is_last_child)) {
391 box.width = con->width - view->border_thickness * 2; 416 if (view) {
392 box.height = 1; 417 left_offset = view->border_left * view->border_thickness;
418 right_offset = view->border_right * view->border_thickness;
419 connects_sides = true;
420 }
421 }
422 box.x = x + left_offset;
423 box.y = y + container_titlebar_height() - TITLEBAR_BORDER_THICKNESS;
424 box.width = width - left_offset - right_offset;
425 box.height = TITLEBAR_BORDER_THICKNESS;
393 scale_box(&box, output_scale); 426 scale_box(&box, output_scale);
394 render_rect(output->wlr_output, output_damage, &box, color); 427 render_rect(output->wlr_output, output_damage, &box, color);
395 428
396 // Setting these makes marks and title easier 429 if (layout == L_TABBED) {
397 size_t inner_x = con->x + view->border_thickness * view->border_left; 430 // Single pixel left edge
398 size_t inner_width = con->width - view->border_thickness * view->border_left 431 box.x = x;
399 - view->border_thickness * view->border_right; 432 box.y = y + TITLEBAR_BORDER_THICKNESS;
433 box.width = TITLEBAR_BORDER_THICKNESS;
434 box.height =
435 container_titlebar_height() - TITLEBAR_BORDER_THICKNESS * 2;
436 scale_box(&box, output_scale);
437 render_rect(output->wlr_output, output_damage, &box, color);
438
439 // Single pixel right edge
440 box.x = (x + width - TITLEBAR_BORDER_THICKNESS) * output_scale;
441 render_rect(output->wlr_output, output_damage, &box, color);
442 }
443
444 size_t inner_width = width - TITLEBAR_H_PADDING * 2;
400 445
401 // Marks 446 // Marks
402 size_t marks_width = 0; 447 size_t marks_width = 0;
@@ -404,14 +449,18 @@ static void render_container_simple_border_normal(struct sway_output *output,
404 struct wlr_box texture_box; 449 struct wlr_box texture_box;
405 wlr_texture_get_size(marks_texture, 450 wlr_texture_get_size(marks_texture,
406 &texture_box.width, &texture_box.height); 451 &texture_box.width, &texture_box.height);
407 texture_box.x = (inner_x + inner_width) * output_scale - texture_box.width; 452 texture_box.x =
408 texture_box.y = (con->y + view->border_thickness) * output_scale; 453 (x + width - TITLEBAR_H_PADDING) * output_scale - texture_box.width;
454 texture_box.y = (y + TITLEBAR_V_PADDING) * output_scale;
409 455
410 float matrix[9]; 456 float matrix[9];
411 wlr_matrix_project_box(matrix, &texture_box, 457 wlr_matrix_project_box(matrix, &texture_box,
412 WL_OUTPUT_TRANSFORM_NORMAL, 458 WL_OUTPUT_TRANSFORM_NORMAL,
413 0.0, output->wlr_output->transform_matrix); 459 0.0, output->wlr_output->transform_matrix);
414 460
461 if (inner_width * output_scale < texture_box.width) {
462 texture_box.width = inner_width * output_scale;
463 }
415 render_texture(output->wlr_output, output_damage, marks_texture, 464 render_texture(output->wlr_output, output_damage, marks_texture,
416 &texture_box, matrix, con->alpha); 465 &texture_box, matrix, con->alpha);
417 marks_width = texture_box.width; 466 marks_width = texture_box.width;
@@ -423,8 +472,8 @@ static void render_container_simple_border_normal(struct sway_output *output,
423 struct wlr_box texture_box; 472 struct wlr_box texture_box;
424 wlr_texture_get_size(title_texture, 473 wlr_texture_get_size(title_texture,
425 &texture_box.width, &texture_box.height); 474 &texture_box.width, &texture_box.height);
426 texture_box.x = inner_x * output_scale; 475 texture_box.x = (x + TITLEBAR_H_PADDING) * output_scale;
427 texture_box.y = (con->y + view->border_thickness) * output_scale; 476 texture_box.y = (y + TITLEBAR_V_PADDING) * output_scale;
428 477
429 float matrix[9]; 478 float matrix[9];
430 wlr_matrix_project_box(matrix, &texture_box, 479 wlr_matrix_project_box(matrix, &texture_box,
@@ -439,104 +488,89 @@ static void render_container_simple_border_normal(struct sway_output *output,
439 title_width = texture_box.width; 488 title_width = texture_box.width;
440 } 489 }
441 490
442 // Title background - above the text 491 // Padding above title
443 memcpy(&color, colors->background, sizeof(float) * 4); 492 memcpy(&color, colors->background, sizeof(float) * 4);
444 color[3] *= con->alpha; 493 premultiply_alpha(color, con->alpha);
445 box.x = inner_x; 494 box.x = x + (layout == L_TABBED) * TITLEBAR_BORDER_THICKNESS;
446 box.y = con->y + 1; 495 box.y = y + TITLEBAR_BORDER_THICKNESS;
447 box.width = inner_width; 496 box.width = width - (layout == L_TABBED) * TITLEBAR_BORDER_THICKNESS * 2;
448 box.height = view->border_thickness - 1; 497 box.height = TITLEBAR_V_PADDING - TITLEBAR_BORDER_THICKNESS;
449 scale_box(&box, output_scale); 498 scale_box(&box, output_scale);
450 render_rect(output->wlr_output, output_damage, &box, color); 499 render_rect(output->wlr_output, output_damage, &box, color);
451 500
452 // Title background - below the text 501 // Padding below title
453 box.y = (con->y + view->border_thickness + config->font_height) 502 box.y = (y + TITLEBAR_V_PADDING + config->font_height) * output_scale;
454 * output_scale;
455 render_rect(output->wlr_output, output_damage, &box, color); 503 render_rect(output->wlr_output, output_damage, &box, color);
456 504
457 // Title background - filler between title and marks 505 // Filler between title and marks
458 box.width = inner_width * output_scale - title_width - marks_width; 506 box.width = inner_width * output_scale - title_width - marks_width;
459 if (box.width > 0) { 507 if (box.width > 0) {
460 box.x = inner_x * output_scale + title_width; 508 box.x = (x + TITLEBAR_H_PADDING) * output_scale + title_width;
461 box.y = (con->y + view->border_thickness) * output_scale; 509 box.y = (y + TITLEBAR_V_PADDING) * output_scale;
462 box.height = config->font_height * output_scale; 510 box.height = config->font_height * output_scale;
463 render_rect(output->wlr_output, output_damage, &box, color); 511 render_rect(output->wlr_output, output_damage, &box, color);
464 } 512 }
465}
466 513
467/** 514 // Padding left of title
468 * Render decorations for a view with "border pixel". 515 left_offset = (layout == L_TABBED) * TITLEBAR_BORDER_THICKNESS;
469 * 516 box.x = x + left_offset;
470 * Care must be taken not to render over the same pixel multiple times, 517 box.y = y + TITLEBAR_V_PADDING;
471 * otherwise the colors will be incorrect when using opacity. 518 box.width = TITLEBAR_H_PADDING - left_offset;
472 */ 519 box.height = config->font_height;
473static void render_container_simple_border_pixel(struct sway_output *output, 520 scale_box(&box, output_scale);
474 pixman_region32_t *output_damage, struct sway_container *con, 521 render_rect(output->wlr_output, output_damage, &box, color);
475 struct border_colors *colors) {
476 struct wlr_box box;
477 float color[4];
478 struct sway_view *view = con->sway_view;
479 float output_scale = output->wlr_output->scale;
480 522
481 if (view->border_left) { 523 // Padding right of marks
482 // Child border - left edge 524 right_offset = (layout == L_TABBED) * TITLEBAR_BORDER_THICKNESS;
483 memcpy(&color, colors->child_border, sizeof(float) * 4); 525 box.x = x + width - TITLEBAR_H_PADDING;
484 color[3] *= con->alpha; 526 box.y = y + TITLEBAR_V_PADDING;
485 box.x = con->x; 527 box.width = TITLEBAR_H_PADDING - right_offset;
486 box.y = con->y + view->border_thickness * view->border_top; 528 box.height = config->font_height;
487 box.width = view->border_thickness; 529 scale_box(&box, output_scale);
488 box.height = con->height - view->border_thickness 530 render_rect(output->wlr_output, output_damage, &box, color);
489 * (view->border_top + view->border_bottom);
490 scale_box(&box, output_scale);
491 render_rect(output->wlr_output, output_damage, &box, color);
492 }
493 531
494 if (view->border_right) { 532 if (connects_sides) {
495 // Child border - right edge 533 // Left pixel in line with bottom bar
496 if (con->parent->children->length == 1 534 box.x = x;
497 && con->parent->layout == L_HORIZ) { 535 box.y = y + container_titlebar_height() - TITLEBAR_BORDER_THICKNESS;
498 memcpy(&color, colors->indicator, sizeof(float) * 4); 536 box.width = view->border_thickness * view->border_left;
499 } else { 537 box.height = TITLEBAR_BORDER_THICKNESS;
500 memcpy(&color, colors->child_border, sizeof(float) * 4);
501 }
502 color[3] *= con->alpha;
503 box.x = con->x + con->width - view->border_thickness;
504 box.y = con->y + view->border_thickness * view->border_top;
505 box.width = view->border_thickness;
506 box.height = con->height - view->border_thickness
507 * (view->border_top + view->border_bottom);
508 scale_box(&box, output_scale); 538 scale_box(&box, output_scale);
509 render_rect(output->wlr_output, output_damage, &box, color); 539 render_rect(output->wlr_output, output_damage, &box, color);
510 }
511 540
512 if (view->border_top) { 541 // Right pixel in line with bottom bar
513 // Child border - top edge 542 box.x = x + width - view->border_thickness * view->border_right;
514 memcpy(&color, colors->child_border, sizeof(float) * 4); 543 box.y = y + container_titlebar_height() - TITLEBAR_BORDER_THICKNESS;
515 color[3] *= con->alpha; 544 box.width = view->border_thickness * view->border_right;
516 box.x = con->x; 545 box.height = TITLEBAR_BORDER_THICKNESS;
517 box.y = con->y;
518 box.width = con->width;
519 box.height = view->border_thickness;
520 scale_box(&box, output_scale); 546 scale_box(&box, output_scale);
521 render_rect(output->wlr_output, output_damage, &box, color); 547 render_rect(output->wlr_output, output_damage, &box, color);
522 } 548 }
549}
523 550
524 if (view->border_bottom) { 551/**
525 // Child border - bottom edge 552 * Render the top border line for a view using "border pixel".
526 if (con->parent->children->length == 1 553 */
527 && con->parent->layout == L_VERT) { 554static void render_top_border(struct sway_output *output,
528 memcpy(&color, colors->indicator, sizeof(float) * 4); 555 pixman_region32_t *output_damage, struct sway_container *con,
529 } else { 556 struct border_colors *colors) {
530 memcpy(&color, colors->child_border, sizeof(float) * 4); 557 struct sway_view *view = con->sway_view;
531 } 558 if (!view->border_top) {
532 color[3] *= con->alpha; 559 return;
533 box.x = con->x;
534 box.y = con->y + con->height - view->border_thickness;
535 box.width = con->width;
536 box.height = view->border_thickness;
537 scale_box(&box, output_scale);
538 render_rect(output->wlr_output, output_damage, &box, color);
539 } 560 }
561 struct wlr_box box;
562 float color[4];
563 float output_scale = output->wlr_output->scale;
564
565 // Child border - top edge
566 memcpy(&color, colors->child_border, sizeof(float) * 4);
567 premultiply_alpha(color, con->alpha);
568 box.x = con->x;
569 box.y = con->y;
570 box.width = con->width;
571 box.height = view->border_thickness;
572 scale_box(&box, output_scale);
573 render_rect(output->wlr_output, output_damage, &box, color);
540} 574}
541 575
542static void render_container(struct sway_output *output, 576static void render_container(struct sway_output *output,
@@ -558,33 +592,30 @@ static void render_container_simple(struct sway_output *output,
558 struct sway_container *child = con->children->items[i]; 592 struct sway_container *child = con->children->items[i];
559 593
560 if (child->type == C_VIEW) { 594 if (child->type == C_VIEW) {
561 if (child->sway_view->border != B_NONE) { 595 struct border_colors *colors;
562 struct border_colors *colors; 596 struct wlr_texture *title_texture;
563 struct wlr_texture *title_texture; 597 struct wlr_texture *marks_texture;
564 struct wlr_texture *marks_texture; 598 if (focus == child || parent_focused) {
565 if (focus == child || parent_focused) { 599 colors = &config->border_colors.focused;
566 colors = &config->border_colors.focused; 600 title_texture = child->title_focused;
567 title_texture = child->title_focused; 601 marks_texture = child->sway_view->marks_focused;
568 marks_texture = child->sway_view->marks_focused; 602 } else if (seat_get_focus_inactive(seat, con) == child) {
569 } else if (seat_get_focus_inactive(seat, con) == child) { 603 colors = &config->border_colors.focused_inactive;
570 colors = &config->border_colors.focused_inactive; 604 title_texture = child->title_focused_inactive;
571 title_texture = child->title_focused_inactive; 605 marks_texture = child->sway_view->marks_focused_inactive;
572 marks_texture = child->sway_view->marks_focused_inactive; 606 } else {
573 } else { 607 colors = &config->border_colors.unfocused;
574 colors = &config->border_colors.unfocused; 608 title_texture = child->title_unfocused;
575 title_texture = child->title_unfocused; 609 marks_texture = child->sway_view->marks_unfocused;
576 marks_texture = child->sway_view->marks_unfocused; 610 }
577 } 611
578 612 if (child->sway_view->border == B_NORMAL) {
579 if (child->sway_view->border == B_NORMAL) { 613 render_titlebar(output, damage, child, child->x, child->y,
580 render_container_simple_border_normal(output, damage, 614 child->width, colors, title_texture, marks_texture);
581 child, colors, title_texture, marks_texture); 615 } else {
582 } else { 616 render_top_border(output, damage, child, colors);
583 render_container_simple_border_pixel(output, damage, child,
584 colors);
585 }
586 } 617 }
587 render_view(child->sway_view, output, damage); 618 render_view(output, damage, child, colors);
588 } else { 619 } else {
589 render_container(output, damage, child, 620 render_container(output, damage, child,
590 parent_focused || focus == child); 621 parent_focused || focus == child);
@@ -596,16 +627,116 @@ static void render_container_simple(struct sway_output *output,
596 * Render a container's children using the L_TABBED layout. 627 * Render a container's children using the L_TABBED layout.
597 */ 628 */
598static void render_container_tabbed(struct sway_output *output, 629static void render_container_tabbed(struct sway_output *output,
599 pixman_region32_t *damage, struct sway_container *con) { 630 pixman_region32_t *damage, struct sway_container *con,
600 // TODO 631 bool parent_focused) {
632 if (!con->children->length) {
633 return;
634 }
635 struct sway_seat *seat = input_manager_current_seat(input_manager);
636 struct sway_container *focus = seat_get_focus(seat);
637 struct sway_container *current = seat_get_active_child(seat, con);
638 struct border_colors *current_colors = NULL;
639
640 // Render tabs
641 for (int i = 0; i < con->children->length; ++i) {
642 struct sway_container *child = con->children->items[i];
643 struct border_colors *colors;
644 struct wlr_texture *title_texture;
645 struct wlr_texture *marks_texture;
646 struct sway_view *view =
647 child->type == C_VIEW ? child->sway_view : NULL;
648
649 if (focus == child || parent_focused) {
650 colors = &config->border_colors.focused;
651 title_texture = child->title_focused;
652 marks_texture = view ? view->marks_focused : NULL;
653 } else if (child == current) {
654 colors = &config->border_colors.focused_inactive;
655 title_texture = child->title_focused_inactive;
656 marks_texture = view ? view->marks_focused : NULL;
657 } else {
658 colors = &config->border_colors.unfocused;
659 title_texture = child->title_unfocused;
660 marks_texture = view ? view->marks_unfocused : NULL;
661 }
662
663 int tab_width = con->width / con->children->length;
664 int x = con->x + tab_width * i;
665 // Make last tab use the remaining width of the parent
666 if (i == con->children->length - 1) {
667 tab_width = con->width - tab_width * i;
668 }
669
670 render_titlebar(output, damage, child, x, child->y, tab_width, colors,
671 title_texture, marks_texture);
672
673 if (child == current) {
674 current_colors = colors;
675 }
676 }
677
678 // Render surface and left/right/bottom borders
679 if (current->type == C_VIEW) {
680 render_view(output, damage, current, current_colors);
681 } else {
682 render_container(output, damage, current,
683 parent_focused || current == focus);
684 }
601} 685}
602 686
603/** 687/**
604 * Render a container's children using the L_STACKED layout. 688 * Render a container's children using the L_STACKED layout.
605 */ 689 */
606static void render_container_stacked(struct sway_output *output, 690static void render_container_stacked(struct sway_output *output,
607 pixman_region32_t *damage, struct sway_container *con) { 691 pixman_region32_t *damage, struct sway_container *con,
608 // TODO 692 bool parent_focused) {
693 if (!con->children->length) {
694 return;
695 }
696 struct sway_seat *seat = input_manager_current_seat(input_manager);
697 struct sway_container *focus = seat_get_focus(seat);
698 struct sway_container *current = seat_get_active_child(seat, con);
699 struct border_colors *current_colors = NULL;
700
701 // Render titles
702 for (int i = 0; i < con->children->length; ++i) {
703 struct sway_container *child = con->children->items[i];
704 struct border_colors *colors;
705 struct wlr_texture *title_texture;
706 struct wlr_texture *marks_texture;
707 struct sway_view *view =
708 child->type == C_VIEW ? child->sway_view : NULL;
709
710 if (focus == child || parent_focused) {
711 colors = &config->border_colors.focused;
712 title_texture = child->title_focused;
713 marks_texture = view ? view->marks_focused : NULL;
714 } else if (child == current) {
715 colors = &config->border_colors.focused_inactive;
716 title_texture = child->title_focused_inactive;
717 marks_texture = view ? view->marks_focused_inactive : NULL;
718 } else {
719 colors = &config->border_colors.unfocused;
720 title_texture = child->title_unfocused;
721 marks_texture = view ? view->marks_unfocused : NULL;
722 }
723
724 int y = con->y + container_titlebar_height() * i;
725 render_titlebar(output, damage, child, child->x, y, child->width,
726 colors, title_texture, marks_texture);
727
728 if (child == current) {
729 current_colors = colors;
730 }
731 }
732
733 // Render surface and left/right/bottom borders
734 if (current->type == C_VIEW) {
735 render_view(output, damage, current, current_colors);
736 } else {
737 render_container(output, damage, current,
738 parent_focused || current == focus);
739 }
609} 740}
610 741
611static void render_container(struct sway_output *output, 742static void render_container(struct sway_output *output,
@@ -618,10 +749,10 @@ static void render_container(struct sway_output *output,
618 render_container_simple(output, damage, con, parent_focused); 749 render_container_simple(output, damage, con, parent_focused);
619 break; 750 break;
620 case L_STACKED: 751 case L_STACKED:
621 render_container_stacked(output, damage, con); 752 render_container_stacked(output, damage, con, parent_focused);
622 break; 753 break;
623 case L_TABBED: 754 case L_TABBED:
624 render_container_tabbed(output, damage, con); 755 render_container_tabbed(output, damage, con, parent_focused);
625 break; 756 break;
626 case L_FLOATING: 757 case L_FLOATING:
627 // TODO 758 // TODO
@@ -678,7 +809,8 @@ static void render_output(struct sway_output *output, struct timespec *when,
678 } 809 }
679 810
680 // TODO: handle views smaller than the output 811 // TODO: handle views smaller than the output
681 render_view(workspace->sway_workspace->fullscreen, output, damage); 812 render_view_surfaces(
813 workspace->sway_workspace->fullscreen, output, damage);
682 814
683 if (workspace->sway_workspace->fullscreen->type == SWAY_VIEW_XWAYLAND) { 815 if (workspace->sway_workspace->fullscreen->type == SWAY_VIEW_XWAYLAND) {
684 render_unmanaged(output, damage, 816 render_unmanaged(output, damage,
@@ -889,9 +1021,7 @@ static void output_damage_view(struct sway_output *output,
889 return; 1021 return;
890 } 1022 }
891 1023
892 struct sway_container *workspace = container_parent(view->swayc, 1024 if (!view_is_visible(view)) {
893 C_WORKSPACE);
894 if (workspace->sway_workspace->fullscreen && !view->is_fullscreen) {
895 return; 1025 return;
896 } 1026 }
897 1027
diff --git a/sway/input/cursor.c b/sway/input/cursor.c
index 9259c475..9a0b4f01 100644
--- a/sway/input/cursor.c
+++ b/sway/input/cursor.c
@@ -108,7 +108,7 @@ static struct sway_container *container_at_coords(
108 } 108 }
109 109
110 struct sway_container *c; 110 struct sway_container *c;
111 if ((c = container_at(ws, x, y, surface, sx, sy))) { 111 if ((c = container_at(ws, ox, oy, surface, sx, sy))) {
112 return c; 112 return c;
113 } 113 }
114 114
@@ -135,7 +135,8 @@ static struct sway_container *container_at_coords(
135 return output->swayc; 135 return output->swayc;
136} 136}
137 137
138void cursor_send_pointer_motion(struct sway_cursor *cursor, uint32_t time_msec) { 138void cursor_send_pointer_motion(struct sway_cursor *cursor, uint32_t time_msec,
139 bool allow_refocusing) {
139 if (time_msec == 0) { 140 if (time_msec == 0) {
140 time_msec = get_current_time_msec(); 141 time_msec = get_current_time_msec();
141 } 142 }
@@ -145,8 +146,24 @@ void cursor_send_pointer_motion(struct sway_cursor *cursor, uint32_t time_msec)
145 double sx, sy; 146 double sx, sy;
146 struct sway_container *c = container_at_coords(cursor->seat, 147 struct sway_container *c = container_at_coords(cursor->seat,
147 cursor->cursor->x, cursor->cursor->y, &surface, &sx, &sy); 148 cursor->cursor->x, cursor->cursor->y, &surface, &sx, &sy);
148 if (c && config->focus_follows_mouse) { 149 if (c && config->focus_follows_mouse && allow_refocusing) {
149 seat_set_focus_warp(cursor->seat, c, false); 150 struct sway_container *focus = seat_get_focus(cursor->seat);
151 if (focus && c->type == C_WORKSPACE) {
152 // Only follow the mouse if it would move to a new output
153 // Otherwise we'll focus the workspace, which is probably wrong
154 if (focus->type != C_OUTPUT) {
155 focus = container_parent(focus, C_OUTPUT);
156 }
157 struct sway_container *output = c;
158 if (output->type != C_OUTPUT) {
159 output = container_parent(c, C_OUTPUT);
160 }
161 if (output != focus) {
162 seat_set_focus_warp(cursor->seat, c, false);
163 }
164 } else {
165 seat_set_focus_warp(cursor->seat, c, false);
166 }
150 } 167 }
151 168
152 // reset cursor if switching between clients 169 // reset cursor if switching between clients
@@ -177,7 +194,7 @@ static void handle_cursor_motion(struct wl_listener *listener, void *data) {
177 struct wlr_event_pointer_motion *event = data; 194 struct wlr_event_pointer_motion *event = data;
178 wlr_cursor_move(cursor->cursor, event->device, 195 wlr_cursor_move(cursor->cursor, event->device,
179 event->delta_x, event->delta_y); 196 event->delta_x, event->delta_y);
180 cursor_send_pointer_motion(cursor, event->time_msec); 197 cursor_send_pointer_motion(cursor, event->time_msec, true);
181} 198}
182 199
183static void handle_cursor_motion_absolute( 200static void handle_cursor_motion_absolute(
@@ -187,7 +204,7 @@ static void handle_cursor_motion_absolute(
187 wlr_idle_notify_activity(cursor->seat->input->server->idle, cursor->seat->wlr_seat); 204 wlr_idle_notify_activity(cursor->seat->input->server->idle, cursor->seat->wlr_seat);
188 struct wlr_event_pointer_motion_absolute *event = data; 205 struct wlr_event_pointer_motion_absolute *event = data;
189 wlr_cursor_warp_absolute(cursor->cursor, event->device, event->x, event->y); 206 wlr_cursor_warp_absolute(cursor->cursor, event->device, event->x, event->y);
190 cursor_send_pointer_motion(cursor, event->time_msec); 207 cursor_send_pointer_motion(cursor, event->time_msec, true);
191} 208}
192 209
193void dispatch_cursor_button(struct sway_cursor *cursor, 210void dispatch_cursor_button(struct sway_cursor *cursor,
@@ -357,7 +374,7 @@ static void handle_tool_axis(struct wl_listener *listener, void *data) {
357 } 374 }
358 375
359 wlr_cursor_warp_absolute(cursor->cursor, event->device, x, y); 376 wlr_cursor_warp_absolute(cursor->cursor, event->device, x, y);
360 cursor_send_pointer_motion(cursor, event->time_msec); 377 cursor_send_pointer_motion(cursor, event->time_msec, true);
361} 378}
362 379
363static void handle_tool_tip(struct wl_listener *listener, void *data) { 380static void handle_tool_tip(struct wl_listener *listener, void *data) {
diff --git a/sway/input/seat.c b/sway/input/seat.c
index 9ac3e6a8..7a3e928a 100644
--- a/sway/input/seat.c
+++ b/sway/input/seat.c
@@ -602,7 +602,7 @@ void seat_set_focus_warp(struct sway_seat *seat,
602 wlr_output, seat->cursor->cursor->x, 602 wlr_output, seat->cursor->cursor->x,
603 seat->cursor->cursor->y)) { 603 seat->cursor->cursor->y)) {
604 wlr_cursor_warp(seat->cursor->cursor, NULL, x, y); 604 wlr_cursor_warp(seat->cursor->cursor, NULL, x, y);
605 cursor_send_pointer_motion(seat->cursor, 0); 605 cursor_send_pointer_motion(seat->cursor, 0, true);
606 } 606 }
607 } 607 }
608 } 608 }
@@ -613,7 +613,7 @@ void seat_set_focus_warp(struct sway_seat *seat,
613 } 613 }
614 614
615 if (last_workspace && last_workspace != new_workspace) { 615 if (last_workspace && last_workspace != new_workspace) {
616 cursor_send_pointer_motion(seat->cursor, 0); 616 cursor_send_pointer_motion(seat->cursor, 0, true);
617 } 617 }
618 618
619 seat->has_focus = (container != NULL); 619 seat->has_focus = (container != NULL);
@@ -718,6 +718,18 @@ struct sway_container *seat_get_focus_inactive(struct sway_seat *seat,
718 return seat_get_focus_by_type(seat, container, C_TYPES); 718 return seat_get_focus_by_type(seat, container, C_TYPES);
719} 719}
720 720
721struct sway_container *seat_get_active_child(struct sway_seat *seat,
722 struct sway_container *container) {
723 struct sway_container *focus = seat_get_focus_inactive(seat, container);
724 if (!focus) {
725 return NULL;
726 }
727 while (focus->parent != container) {
728 focus = focus->parent;
729 }
730 return focus;
731}
732
721struct sway_container *sway_seat_get_focus(struct sway_seat *seat) { 733struct sway_container *sway_seat_get_focus(struct sway_seat *seat) {
722 if (!seat->has_focus) { 734 if (!seat->has_focus) {
723 return NULL; 735 return NULL;
diff --git a/sway/ipc-server.c b/sway/ipc-server.c
index 8734e8f8..15ed6f80 100644
--- a/sway/ipc-server.c
+++ b/sway/ipc-server.c
@@ -22,6 +22,7 @@
22#include "sway/server.h" 22#include "sway/server.h"
23#include "sway/input/input-manager.h" 23#include "sway/input/input-manager.h"
24#include "sway/input/seat.h" 24#include "sway/input/seat.h"
25#include "sway/tree/view.h"
25#include "list.h" 26#include "list.h"
26#include "log.h" 27#include "log.h"
27 28
@@ -429,6 +430,16 @@ static void ipc_get_workspaces_callback(struct sway_container *workspace,
429 json_object_new_boolean(visible)); 430 json_object_new_boolean(visible));
430} 431}
431 432
433static void ipc_get_marks_callback(struct sway_container *con, void *data) {
434 json_object *marks = (json_object *)data;
435 if (con->type == C_VIEW && con->sway_view->marks) {
436 for (int i = 0; i < con->sway_view->marks->length; ++i) {
437 char *mark = (char *)con->sway_view->marks->items[i];
438 json_object_array_add(marks, json_object_new_string(mark));
439 }
440 }
441}
442
432void ipc_client_handle_command(struct ipc_client *client) { 443void ipc_client_handle_command(struct ipc_client *client) {
433 if (!sway_assert(client != NULL, "client != NULL")) { 444 if (!sway_assert(client != NULL, "client != NULL")) {
434 return; 445 return;
@@ -569,6 +580,17 @@ void ipc_client_handle_command(struct ipc_client *client) {
569 goto exit_cleanup; 580 goto exit_cleanup;
570 } 581 }
571 582
583 case IPC_GET_MARKS:
584 {
585 json_object *marks = json_object_new_array();
586 container_descendants(&root_container, C_VIEW, ipc_get_marks_callback,
587 marks);
588 const char *json_string = json_object_to_json_string(marks);
589 ipc_send_reply(client, json_string, (uint32_t)strlen(json_string));
590 json_object_put(marks);
591 goto exit_cleanup;
592 }
593
572 case IPC_GET_VERSION: 594 case IPC_GET_VERSION:
573 { 595 {
574 json_object *version = ipc_json_get_version(); 596 json_object *version = ipc_json_get_version();
diff --git a/sway/tree/arrange.c b/sway/tree/arrange.c
index 83bb20fb..37f4a066 100644
--- a/sway/tree/arrange.c
+++ b/sway/tree/arrange.c
@@ -86,6 +86,15 @@ static void apply_horiz_layout(struct sway_container *parent) {
86 if (!num_children) { 86 if (!num_children) {
87 return; 87 return;
88 } 88 }
89 size_t parent_offset = 0;
90 if (parent->parent->layout == L_TABBED) {
91 parent_offset = container_titlebar_height();
92 } else if (parent->parent->layout == L_STACKED) {
93 parent_offset =
94 container_titlebar_height() * parent->parent->children->length;
95 }
96 size_t parent_height = parent->height - parent_offset;
97
89 // Calculate total width of children 98 // Calculate total width of children
90 double total_width = 0; 99 double total_width = 0;
91 for (size_t i = 0; i < num_children; ++i) { 100 for (size_t i = 0; i < num_children; ++i) {
@@ -111,9 +120,9 @@ static void apply_horiz_layout(struct sway_container *parent) {
111 "Calculating arrangement for %p:%d (will scale %f by %f)", 120 "Calculating arrangement for %p:%d (will scale %f by %f)",
112 child, child->type, child->width, scale); 121 child, child->type, child->width, scale);
113 child->x = child_x; 122 child->x = child_x;
114 child->y = parent->y; 123 child->y = parent->y + parent_offset;
115 child->width = floor(child->width * scale); 124 child->width = floor(child->width * scale);
116 child->height = parent->height; 125 child->height = parent_height;
117 child_x += child->width; 126 child_x += child->width;
118 } 127 }
119 // Make last child use remaining width of parent 128 // Make last child use remaining width of parent
@@ -125,24 +134,33 @@ static void apply_vert_layout(struct sway_container *parent) {
125 if (!num_children) { 134 if (!num_children) {
126 return; 135 return;
127 } 136 }
137 size_t parent_offset = 0;
138 if (parent->parent->layout == L_TABBED) {
139 parent_offset = container_titlebar_height();
140 } else if (parent->parent->layout == L_STACKED) {
141 parent_offset =
142 container_titlebar_height() * parent->parent->children->length;
143 }
144 size_t parent_height = parent->height - parent_offset;
145
128 // Calculate total height of children 146 // Calculate total height of children
129 double total_height = 0; 147 double total_height = 0;
130 for (size_t i = 0; i < num_children; ++i) { 148 for (size_t i = 0; i < num_children; ++i) {
131 struct sway_container *child = parent->children->items[i]; 149 struct sway_container *child = parent->children->items[i];
132 if (child->height <= 0) { 150 if (child->height <= 0) {
133 if (num_children > 1) { 151 if (num_children > 1) {
134 child->height = parent->height / (num_children - 1); 152 child->height = parent_height / (num_children - 1);
135 } else { 153 } else {
136 child->height = parent->height; 154 child->height = parent_height;
137 } 155 }
138 } 156 }
139 total_height += child->height; 157 total_height += child->height;
140 } 158 }
141 double scale = parent->height / total_height; 159 double scale = parent_height / total_height;
142 160
143 // Resize 161 // Resize
144 wlr_log(L_DEBUG, "Arranging %p vertically", parent); 162 wlr_log(L_DEBUG, "Arranging %p vertically", parent);
145 double child_y = parent->y; 163 double child_y = parent->y + parent_offset;
146 struct sway_container *child; 164 struct sway_container *child;
147 for (size_t i = 0; i < num_children; ++i) { 165 for (size_t i = 0; i < num_children; ++i) {
148 child = parent->children->items[i]; 166 child = parent->children->items[i];
@@ -156,7 +174,33 @@ static void apply_vert_layout(struct sway_container *parent) {
156 child_y += child->height; 174 child_y += child->height;
157 } 175 }
158 // Make last child use remaining height of parent 176 // Make last child use remaining height of parent
159 child->height = parent->y + parent->height - child->y; 177 child->height = parent->y + parent_offset + parent_height - child->y;
178}
179
180static void apply_tabbed_layout(struct sway_container *parent) {
181 if (!parent->children->length) {
182 return;
183 }
184 for (int i = 0; i < parent->children->length; ++i) {
185 struct sway_container *child = parent->children->items[i];
186 child->x = parent->x;
187 child->y = parent->y;
188 child->width = parent->width;
189 child->height = parent->height;
190 }
191}
192
193static void apply_stacked_layout(struct sway_container *parent) {
194 if (!parent->children->length) {
195 return;
196 }
197 for (int i = 0; i < parent->children->length; ++i) {
198 struct sway_container *child = parent->children->items[i];
199 child->x = parent->x;
200 child->y = parent->y;
201 child->width = parent->width;
202 child->height = parent->height;
203 }
160} 204}
161 205
162void arrange_children_of(struct sway_container *parent) { 206void arrange_children_of(struct sway_container *parent) {
@@ -189,6 +233,12 @@ void arrange_children_of(struct sway_container *parent) {
189 case L_VERT: 233 case L_VERT:
190 apply_vert_layout(parent); 234 apply_vert_layout(parent);
191 break; 235 break;
236 case L_TABBED:
237 apply_tabbed_layout(parent);
238 break;
239 case L_STACKED:
240 apply_stacked_layout(parent);
241 break;
192 default: 242 default:
193 wlr_log(L_DEBUG, "TODO: arrange layout type %d", parent->layout); 243 wlr_log(L_DEBUG, "TODO: arrange layout type %d", parent->layout);
194 apply_horiz_layout(parent); 244 apply_horiz_layout(parent);
diff --git a/sway/tree/container.c b/sway/tree/container.c
index e47338e7..9cf18f61 100644
--- a/sway/tree/container.c
+++ b/sway/tree/container.c
@@ -73,6 +73,44 @@ static void container_close_notify(struct sway_container *container) {
73 } 73 }
74} 74}
75 75
76static void container_update_textures_recursive(struct sway_container *con) {
77 container_update_title_textures(con);
78
79 if (con->type == C_VIEW) {
80 view_update_marks_textures(con->sway_view);
81 } else {
82 for (int i = 0; i < con->children->length; ++i) {
83 struct sway_container *child = con->children->items[i];
84 container_update_textures_recursive(child);
85 }
86 }
87}
88
89static void handle_reparent(struct wl_listener *listener,
90 void *data) {
91 struct sway_container *container =
92 wl_container_of(listener, container, reparent);
93 struct sway_container *old_parent = data;
94
95 struct sway_container *old_output = old_parent;
96 if (old_output != NULL && old_output->type != C_OUTPUT) {
97 old_output = container_parent(old_output, C_OUTPUT);
98 }
99
100 struct sway_container *new_output = container->parent;
101 if (new_output != NULL && new_output->type != C_OUTPUT) {
102 new_output = container_parent(new_output, C_OUTPUT);
103 }
104
105 if (old_output && new_output) {
106 float old_scale = old_output->sway_output->wlr_output->scale;
107 float new_scale = new_output->sway_output->wlr_output->scale;
108 if (old_scale != new_scale) {
109 container_update_textures_recursive(container);
110 }
111 }
112}
113
76struct sway_container *container_create(enum sway_container_type type) { 114struct sway_container *container_create(enum sway_container_type type) {
77 // next id starts at 1 because 0 is assigned to root_container in layout.c 115 // next id starts at 1 because 0 is assigned to root_container in layout.c
78 static size_t next_id = 1; 116 static size_t next_id = 1;
@@ -92,6 +130,9 @@ struct sway_container *container_create(enum sway_container_type type) {
92 wl_signal_init(&c->events.destroy); 130 wl_signal_init(&c->events.destroy);
93 wl_signal_init(&c->events.reparent); 131 wl_signal_init(&c->events.reparent);
94 132
133 wl_signal_add(&c->events.reparent, &c->reparent);
134 c->reparent.notify = handle_reparent;
135
95 return c; 136 return c;
96} 137}
97 138
@@ -411,80 +452,154 @@ struct sway_container *container_parent(struct sway_container *container,
411 return container; 452 return container;
412} 453}
413 454
414struct sway_container *container_at(struct sway_container *parent, 455static struct sway_container *container_at_view(struct sway_container *swayc,
415 double lx, double ly, 456 double ox, double oy,
416 struct wlr_surface **surface, double *sx, double *sy) { 457 struct wlr_surface **surface, double *sx, double *sy) {
417 list_t *queue = get_bfs_queue(); 458 if (!sway_assert(swayc->type == C_VIEW, "Expected a view")) {
418 if (!queue) {
419 return NULL; 459 return NULL;
420 } 460 }
461 struct sway_view *sview = swayc->sway_view;
462 double view_sx = ox - sview->x;
463 double view_sy = oy - sview->y;
421 464
422 list_add(queue, parent); 465 double _sx, _sy;
466 struct wlr_surface *_surface = NULL;
467 switch (sview->type) {
468 case SWAY_VIEW_XWAYLAND:
469 _surface = wlr_surface_surface_at(sview->surface,
470 view_sx, view_sy, &_sx, &_sy);
471 break;
472 case SWAY_VIEW_XDG_SHELL_V6:
473 // the top left corner of the sway container is the
474 // coordinate of the top left corner of the window geometry
475 view_sx += sview->wlr_xdg_surface_v6->geometry.x;
476 view_sy += sview->wlr_xdg_surface_v6->geometry.y;
477
478 _surface = wlr_xdg_surface_v6_surface_at(
479 sview->wlr_xdg_surface_v6,
480 view_sx, view_sy, &_sx, &_sy);
481 break;
482 case SWAY_VIEW_XDG_SHELL:
483 // the top left corner of the sway container is the
484 // coordinate of the top left corner of the window geometry
485 view_sx += sview->wlr_xdg_surface->geometry.x;
486 view_sy += sview->wlr_xdg_surface->geometry.y;
487
488 _surface = wlr_xdg_surface_surface_at(
489 sview->wlr_xdg_surface,
490 view_sx, view_sy, &_sx, &_sy);
491 break;
492 }
493 if (_surface) {
494 *sx = _sx;
495 *sy = _sy;
496 *surface = _surface;
497 }
498 return swayc;
499}
423 500
424 struct sway_container *swayc = NULL; 501/**
425 while (queue->length) { 502 * container_at for a container with layout L_TABBED.
426 swayc = queue->items[0]; 503 */
427 list_del(queue, 0); 504static struct sway_container *container_at_tabbed(struct sway_container *parent,
428 if (swayc->type == C_VIEW) { 505 double ox, double oy,
429 struct sway_view *sview = swayc->sway_view; 506 struct wlr_surface **surface, double *sx, double *sy) {
430 struct sway_container *soutput = container_parent(swayc, C_OUTPUT); 507 if (oy < parent->y || oy > parent->y + parent->height) {
431 struct wlr_box *output_box = 508 return NULL;
432 wlr_output_layout_get_box( 509 }
433 root_container.sway_root->output_layout, 510 struct sway_seat *seat = input_manager_current_seat(input_manager);
434 soutput->sway_output->wlr_output); 511
435 double ox = lx - output_box->x; 512 // Tab titles
436 double oy = ly - output_box->y; 513 int title_height = container_titlebar_height();
437 double view_sx = ox - sview->x; 514 if (oy < parent->y + title_height) {
438 double view_sy = oy - sview->y; 515 int tab_width = parent->width / parent->children->length;
439 516 int child_index = (ox - parent->x) / tab_width;
440 double _sx, _sy; 517 if (child_index >= parent->children->length) {
441 struct wlr_surface *_surface; 518 child_index = parent->children->length - 1;
442 switch (sview->type) { 519 }
443 case SWAY_VIEW_XWAYLAND: 520 struct sway_container *child = parent->children->items[child_index];
444 _surface = wlr_surface_surface_at(sview->surface, 521 return seat_get_focus_inactive(seat, child);
445 view_sx, view_sy, &_sx, &_sy); 522 }
446 break; 523
447 case SWAY_VIEW_XDG_SHELL_V6: 524 // Surfaces
448 // the top left corner of the sway container is the 525 struct sway_container *current = seat_get_active_child(seat, parent);
449 // coordinate of the top left corner of the window geometry 526
450 view_sx += sview->wlr_xdg_surface_v6->geometry.x; 527 return container_at(current, ox, oy, surface, sx, sy);
451 view_sy += sview->wlr_xdg_surface_v6->geometry.y; 528}
452 529
453 _surface = wlr_xdg_surface_v6_surface_at( 530/**
454 sview->wlr_xdg_surface_v6, 531 * container_at for a container with layout L_STACKED.
455 view_sx, view_sy, &_sx, &_sy); 532 */
456 break; 533static struct sway_container *container_at_stacked(
457 case SWAY_VIEW_XDG_SHELL: 534 struct sway_container *parent, double ox, double oy,
458 // the top left corner of the sway container is the 535 struct wlr_surface **surface, double *sx, double *sy) {
459 // coordinate of the top left corner of the window geometry 536 if (oy < parent->y || oy > parent->y + parent->height) {
460 view_sx += sview->wlr_xdg_surface->geometry.x; 537 return NULL;
461 view_sy += sview->wlr_xdg_surface->geometry.y; 538 }
462 539 struct sway_seat *seat = input_manager_current_seat(input_manager);
463 _surface = wlr_xdg_surface_surface_at( 540
464 sview->wlr_xdg_surface, 541 // Title bars
465 view_sx, view_sy, &_sx, &_sy); 542 int title_height = container_titlebar_height();
466 break; 543 int child_index = (oy - parent->y) / title_height;
467 } 544 if (child_index < parent->children->length) {
468 if (_surface) { 545 struct sway_container *child = parent->children->items[child_index];
469 *sx = _sx; 546 return seat_get_focus_inactive(seat, child);
470 *sy = _sy; 547 }
471 *surface = _surface; 548
472 return swayc; 549 // Surfaces
473 } 550 struct sway_container *current = seat_get_active_child(seat, parent);
474 // Check the view's decorations 551
475 struct wlr_box swayc_box = { 552 return container_at(current, ox, oy, surface, sx, sy);
476 .x = swayc->x, 553}
477 .y = swayc->y, 554
478 .width = swayc->width, 555/**
479 .height = swayc->height, 556 * container_at for a container with layout L_HORIZ or L_VERT.
480 }; 557 */
481 if (wlr_box_contains_point(&swayc_box, ox, oy)) { 558static struct sway_container *container_at_linear(struct sway_container *parent,
482 return swayc; 559 double ox, double oy,
483 } 560 struct wlr_surface **surface, double *sx, double *sy) {
484 } else { 561 for (int i = 0; i < parent->children->length; ++i) {
485 list_cat(queue, swayc->children); 562 struct sway_container *child = parent->children->items[i];
563 struct wlr_box box = {
564 .x = child->x,
565 .y = child->y,
566 .width = child->width,
567 .height = child->height,
568 };
569 if (wlr_box_contains_point(&box, ox, oy)) {
570 return container_at(child, ox, oy, surface, sx, sy);
486 } 571 }
487 } 572 }
573 return NULL;
574}
575
576struct sway_container *container_at(struct sway_container *parent,
577 double ox, double oy,
578 struct wlr_surface **surface, double *sx, double *sy) {
579 if (!sway_assert(parent->type >= C_WORKSPACE,
580 "Expected workspace or deeper")) {
581 return NULL;
582 }
583 if (parent->type == C_VIEW) {
584 return container_at_view(parent, ox, oy, surface, sx, sy);
585 }
586 if (!parent->children->length) {
587 return NULL;
588 }
589
590 switch (parent->layout) {
591 case L_HORIZ:
592 case L_VERT:
593 return container_at_linear(parent, ox, oy, surface, sx, sy);
594 case L_TABBED:
595 return container_at_tabbed(parent, ox, oy, surface, sx, sy);
596 case L_STACKED:
597 return container_at_stacked(parent, ox, oy, surface, sx, sy);
598 case L_FLOATING:
599 return NULL; // TODO
600 case L_NONE:
601 return NULL;
602 }
488 603
489 return NULL; 604 return NULL;
490} 605}
@@ -658,19 +773,96 @@ void container_calculate_title_height(struct sway_container *container) {
658 container->title_height = height; 773 container->title_height = height;
659} 774}
660 775
776/**
777 * Calculate and return the length of the concatenated child titles.
778 * An example concatenated title is: V[Terminal, Firefox]
779 * If buffer is not NULL, also populate the buffer with the concatenated title.
780 */
781static size_t concatenate_child_titles(struct sway_container *parent,
782 char *buffer) {
783 size_t len = 2; // V[
784 if (buffer) {
785 switch (parent->layout) {
786 case L_VERT:
787 strcpy(buffer, "V[");
788 break;
789 case L_HORIZ:
790 strcpy(buffer, "H[");
791 break;
792 case L_TABBED:
793 strcpy(buffer, "T[");
794 break;
795 case L_STACKED:
796 strcpy(buffer, "S[");
797 break;
798 case L_FLOATING:
799 strcpy(buffer, "F[");
800 break;
801 case L_NONE:
802 strcpy(buffer, "D[");
803 break;
804 }
805 }
806
807 for (int i = 0; i < parent->children->length; ++i) {
808 if (i != 0) {
809 len += 1;
810 if (buffer) {
811 strcat(buffer, " ");
812 }
813 }
814 struct sway_container *child = parent->children->items[i];
815 const char *identifier = NULL;
816 if (child->type == C_VIEW) {
817 identifier = view_get_class(child->sway_view);
818 if (!identifier) {
819 identifier = view_get_app_id(child->sway_view);
820 }
821 } else {
822 identifier = child->name;
823 }
824 if (identifier) {
825 len += strlen(identifier);
826 if (buffer) {
827 strcat(buffer, identifier);
828 }
829 } else {
830 len += 6;
831 if (buffer) {
832 strcat(buffer, "(null)");
833 }
834 }
835 }
836
837 len += 1;
838 if (buffer) {
839 strcat(buffer, "]");
840 }
841 return len;
842}
843
661void container_notify_child_title_changed(struct sway_container *container) { 844void container_notify_child_title_changed(struct sway_container *container) {
662 if (!container || container->type != C_CONTAINER) { 845 if (!container || container->type != C_CONTAINER) {
663 return; 846 return;
664 } 847 }
665 if (container->layout != L_TABBED && container->layout != L_STACKED) {
666 return;
667 }
668 if (container->formatted_title) { 848 if (container->formatted_title) {
669 free(container->formatted_title); 849 free(container->formatted_title);
670 } 850 }
671 // TODO: iterate children and concatenate their titles 851
672 container->formatted_title = strdup(""); 852 size_t len = concatenate_child_titles(container, NULL);
853 char *buffer = calloc(len + 1, sizeof(char));
854 if (!sway_assert(buffer, "Unable to allocate title string")) {
855 return;
856 }
857 concatenate_child_titles(container, buffer);
858
859 container->name = buffer;
860 container->formatted_title = buffer;
673 container_calculate_title_height(container); 861 container_calculate_title_height(container);
674 container_update_title_textures(container); 862 container_update_title_textures(container);
675 container_notify_child_title_changed(container->parent); 863 container_notify_child_title_changed(container->parent);
676} 864}
865
866size_t container_titlebar_height() {
867 return config->font_height + TITLEBAR_V_PADDING * 2;
868}
diff --git a/sway/tree/layout.c b/sway/tree/layout.c
index ec1c6fe5..f8acdf6c 100644
--- a/sway/tree/layout.c
+++ b/sway/tree/layout.c
@@ -149,6 +149,8 @@ struct sway_container *container_remove_child(struct sway_container *child) {
149 } 149 }
150 } 150 }
151 child->parent = NULL; 151 child->parent = NULL;
152 container_notify_child_title_changed(parent);
153
152 return parent; 154 return parent;
153} 155}
154 156
@@ -182,6 +184,8 @@ void container_move_to(struct sway_container *container,
182 container_sort_workspaces(new_parent); 184 container_sort_workspaces(new_parent);
183 seat_set_focus(seat, new_parent); 185 seat_set_focus(seat, new_parent);
184 } 186 }
187 container_notify_child_title_changed(old_parent);
188 container_notify_child_title_changed(new_parent);
185 if (old_parent) { 189 if (old_parent) {
186 arrange_children_of(old_parent); 190 arrange_children_of(old_parent);
187 } 191 }
@@ -234,9 +238,9 @@ static bool is_parallel(enum sway_container_layout layout,
234 enum movement_direction dir) { 238 enum movement_direction dir) {
235 switch (layout) { 239 switch (layout) {
236 case L_TABBED: 240 case L_TABBED:
237 case L_STACKED:
238 case L_HORIZ: 241 case L_HORIZ:
239 return dir == MOVE_LEFT || dir == MOVE_RIGHT; 242 return dir == MOVE_LEFT || dir == MOVE_RIGHT;
243 case L_STACKED:
240 case L_VERT: 244 case L_VERT:
241 return dir == MOVE_UP || dir == MOVE_DOWN; 245 return dir == MOVE_UP || dir == MOVE_DOWN;
242 default: 246 default:
@@ -485,6 +489,9 @@ void container_move(struct sway_container *container,
485 } 489 }
486 } 490 }
487 491
492 container_notify_child_title_changed(old_parent);
493 container_notify_child_title_changed(container->parent);
494
488 if (old_parent) { 495 if (old_parent) {
489 seat_set_focus(config->handler_context.seat, old_parent); 496 seat_set_focus(config->handler_context.seat, old_parent);
490 seat_set_focus(config->handler_context.seat, container); 497 seat_set_focus(config->handler_context.seat, container);
@@ -832,6 +839,8 @@ struct sway_container *container_split(struct sway_container *child,
832 container_add_child(cont, child); 839 container_add_child(cont, child);
833 } 840 }
834 841
842 container_notify_child_title_changed(cont);
843
835 return cont; 844 return cont;
836} 845}
837 846
diff --git a/sway/tree/view.c b/sway/tree/view.c
index 648c1655..07157818 100644
--- a/sway/tree/view.c
+++ b/sway/tree/view.c
@@ -139,10 +139,20 @@ void view_autoconfigure(struct sway_view *view) {
139 return; 139 return;
140 } 140 }
141 141
142 int other_views = 1; 142 struct sway_container *ws = container_parent(view->swayc, C_WORKSPACE);
143
144 int other_views = 0;
143 if (config->hide_edge_borders == E_SMART) { 145 if (config->hide_edge_borders == E_SMART) {
144 struct sway_container *ws = container_parent(view->swayc, C_WORKSPACE); 146 struct sway_container *con = view->swayc;
145 other_views = container_count_descendants_of_type(ws, C_VIEW) - 1; 147 while (con != output) {
148 if (con->layout != L_TABBED && con->layout != L_STACKED) {
149 other_views += con->children ? con->children->length - 1 : 0;
150 if (other_views > 0) {
151 break;
152 }
153 }
154 con = con->parent;
155 }
146 } 156 }
147 157
148 view->border_top = view->border_bottom = true; 158 view->border_top = view->border_bottom = true;
@@ -151,47 +161,67 @@ void view_autoconfigure(struct sway_view *view) {
151 if (config->hide_edge_borders == E_BOTH 161 if (config->hide_edge_borders == E_BOTH
152 || config->hide_edge_borders == E_VERTICAL 162 || config->hide_edge_borders == E_VERTICAL
153 || (config->hide_edge_borders == E_SMART && !other_views)) { 163 || (config->hide_edge_borders == E_SMART && !other_views)) {
154 view->border_left = view->swayc->x != 0; 164 view->border_left = view->swayc->x != ws->x;
155 int right_x = view->swayc->x + view->swayc->width; 165 int right_x = view->swayc->x + view->swayc->width;
156 view->border_right = right_x != output->width; 166 view->border_right = right_x != ws->x + ws->width;
157 } 167 }
158 if (config->hide_edge_borders == E_BOTH 168 if (config->hide_edge_borders == E_BOTH
159 || config->hide_edge_borders == E_HORIZONTAL 169 || config->hide_edge_borders == E_HORIZONTAL
160 || (config->hide_edge_borders == E_SMART && !other_views)) { 170 || (config->hide_edge_borders == E_SMART && !other_views)) {
161 view->border_top = view->swayc->y != 0; 171 view->border_top = view->swayc->y != ws->y;
162 int bottom_y = view->swayc->y + view->swayc->height; 172 int bottom_y = view->swayc->y + view->swayc->height;
163 view->border_bottom = bottom_y != output->height; 173 view->border_bottom = bottom_y != ws->y + ws->height;
164 } 174 }
165 } 175 }
166 176
167 double x, y, width, height; 177 double x, y, width, height;
168 x = y = width = height = 0; 178 x = y = width = height = 0;
179 double y_offset = 0;
180
181 // In a tabbed or stacked container, the swayc's y is the top of the title
182 // area. We have to offset the surface y by the height of the title bar, and
183 // disable any top border because we'll always have the title bar.
184 if (view->swayc->parent->layout == L_TABBED) {
185 y_offset = container_titlebar_height();
186 view->border_top = 0;
187 } else if (view->swayc->parent->layout == L_STACKED) {
188 y_offset = container_titlebar_height()
189 * view->swayc->parent->children->length;
190 view->border_top = 0;
191 }
192
169 switch (view->border) { 193 switch (view->border) {
170 case B_NONE: 194 case B_NONE:
171 x = view->swayc->x; 195 x = view->swayc->x;
172 y = view->swayc->y; 196 y = view->swayc->y + y_offset;
173 width = view->swayc->width; 197 width = view->swayc->width;
174 height = view->swayc->height; 198 height = view->swayc->height - y_offset;
175 break; 199 break;
176 case B_PIXEL: 200 case B_PIXEL:
177 x = view->swayc->x + view->border_thickness * view->border_left; 201 x = view->swayc->x + view->border_thickness * view->border_left;
178 y = view->swayc->y + view->border_thickness * view->border_top; 202 y = view->swayc->y + view->border_thickness * view->border_top + y_offset;
179 width = view->swayc->width 203 width = view->swayc->width
180 - view->border_thickness * view->border_left 204 - view->border_thickness * view->border_left
181 - view->border_thickness * view->border_right; 205 - view->border_thickness * view->border_right;
182 height = view->swayc->height 206 height = view->swayc->height - y_offset
183 - view->border_thickness * view->border_top 207 - view->border_thickness * view->border_top
184 - view->border_thickness * view->border_bottom; 208 - view->border_thickness * view->border_bottom;
185 break; 209 break;
186 case B_NORMAL: 210 case B_NORMAL:
187 // Height is: border + title height + border + view height + border 211 // Height is: 1px border + 3px pad + title height + 3px pad + 1px border
188 x = view->swayc->x + view->border_thickness * view->border_left; 212 x = view->swayc->x + view->border_thickness * view->border_left;
189 y = view->swayc->y + config->font_height + view->border_thickness * 2;
190 width = view->swayc->width 213 width = view->swayc->width
191 - view->border_thickness * view->border_left 214 - view->border_thickness * view->border_left
192 - view->border_thickness * view->border_right; 215 - view->border_thickness * view->border_right;
193 height = view->swayc->height - config->font_height 216 if (y_offset) {
194 - view->border_thickness * (2 + view->border_bottom); 217 y = view->swayc->y + y_offset;
218 height = view->swayc->height - y_offset
219 - view->border_thickness * view->border_bottom;
220 } else {
221 y = view->swayc->y + container_titlebar_height();
222 height = view->swayc->height - container_titlebar_height()
223 - view->border_thickness * view->border_bottom;
224 }
195 break; 225 break;
196 } 226 }
197 227
@@ -440,6 +470,7 @@ void view_map(struct sway_view *view, struct wlr_surface *wlr_surface) {
440 input_manager_set_focus(input_manager, cont); 470 input_manager_set_focus(input_manager, cont);
441 471
442 view_update_title(view, false); 472 view_update_title(view, false);
473 container_notify_child_title_changed(view->swayc->parent);
443 view_execute_criteria(view); 474 view_execute_criteria(view);
444 475
445 container_damage_whole(cont); 476 container_damage_whole(cont);
@@ -863,3 +894,28 @@ void view_update_marks_textures(struct sway_view *view) {
863 &config->border_colors.urgent); 894 &config->border_colors.urgent);
864 container_damage_whole(view->swayc); 895 container_damage_whole(view->swayc);
865} 896}
897
898bool view_is_visible(struct sway_view *view) {
899 if (!view->swayc) {
900 return false;
901 }
902 // Check view isn't in a tabbed or stacked container on an inactive tab
903 struct sway_seat *seat = input_manager_current_seat(input_manager);
904 struct sway_container *container = view->swayc;
905 while (container->type != C_WORKSPACE) {
906 if (container->parent->layout == L_TABBED ||
907 container->parent->layout == L_STACKED) {
908 if (seat_get_active_child(seat, container->parent) != container) {
909 return false;
910 }
911 }
912 container = container->parent;
913 }
914 // Check view isn't hidden by another fullscreen view
915 struct sway_container *workspace = container;
916 if (workspace->sway_workspace->fullscreen && !view->is_fullscreen) {
917 return false;
918 }
919 // Check the workspace is visible
920 return workspace_is_visible(workspace);
921}
diff --git a/swaylock/main.c b/swaylock/main.c
index 9da99f97..79c06285 100644
--- a/swaylock/main.c
+++ b/swaylock/main.c
@@ -13,6 +13,7 @@
13#include <time.h> 13#include <time.h>
14#include <unistd.h> 14#include <unistd.h>
15#include <wayland-client.h> 15#include <wayland-client.h>
16#include <wordexp.h>
16#include <wlr/util/log.h> 17#include <wlr/util/log.h>
17#include "swaylock/seat.h" 18#include "swaylock/seat.h"
18#include "swaylock/swaylock.h" 19#include "swaylock/swaylock.h"
@@ -20,9 +21,11 @@
20#include "pool-buffer.h" 21#include "pool-buffer.h"
21#include "cairo.h" 22#include "cairo.h"
22#include "log.h" 23#include "log.h"
24#include "stringop.h"
23#include "util.h" 25#include "util.h"
24#include "wlr-input-inhibitor-unstable-v1-client-protocol.h" 26#include "wlr-input-inhibitor-unstable-v1-client-protocol.h"
25#include "wlr-layer-shell-unstable-v1-client-protocol.h" 27#include "wlr-layer-shell-unstable-v1-client-protocol.h"
28#include "xdg-output-unstable-v1-client-protocol.h"
26 29
27void sway_terminate(int exit_code) { 30void sway_terminate(int exit_code) {
28 exit(exit_code); 31 exit(exit_code);
@@ -77,9 +80,14 @@ static void destroy_surface(struct swaylock_surface *surface) {
77 80
78static const struct zwlr_layer_surface_v1_listener layer_surface_listener; 81static const struct zwlr_layer_surface_v1_listener layer_surface_listener;
79 82
83static cairo_surface_t *select_image(struct swaylock_state *state,
84 struct swaylock_surface *surface);
85
80static void create_layer_surface(struct swaylock_surface *surface) { 86static void create_layer_surface(struct swaylock_surface *surface) {
81 struct swaylock_state *state = surface->state; 87 struct swaylock_state *state = surface->state;
82 88
89 surface->image = select_image(state, surface);
90
83 surface->surface = wl_compositor_create_surface(state->compositor); 91 surface->surface = wl_compositor_create_surface(state->compositor);
84 assert(surface->surface); 92 assert(surface->surface);
85 93
@@ -123,22 +131,23 @@ static const struct zwlr_layer_surface_v1_listener layer_surface_listener = {
123 .closed = layer_surface_closed, 131 .closed = layer_surface_closed,
124}; 132};
125 133
126static void output_geometry(void *data, struct wl_output *output, int32_t x, 134static void handle_wl_output_geometry(void *data, struct wl_output *output, int32_t x,
127 int32_t y, int32_t width_mm, int32_t height_mm, int32_t subpixel, 135 int32_t y, int32_t width_mm, int32_t height_mm, int32_t subpixel,
128 const char *make, const char *model, int32_t transform) { 136 const char *make, const char *model, int32_t transform) {
129 // Who cares 137 // Who cares
130} 138}
131 139
132static void output_mode(void *data, struct wl_output *output, uint32_t flags, 140static void handle_wl_output_mode(void *data, struct wl_output *output, uint32_t flags,
133 int32_t width, int32_t height, int32_t refresh) { 141 int32_t width, int32_t height, int32_t refresh) {
134 // Who cares 142 // Who cares
135} 143}
136 144
137static void output_done(void *data, struct wl_output *output) { 145static void handle_wl_output_done(void *data, struct wl_output *output) {
138 // Who cares 146 // Who cares
139} 147}
140 148
141static void output_scale(void *data, struct wl_output *output, int32_t factor) { 149static void handle_wl_output_scale(void *data, struct wl_output *output,
150 int32_t factor) {
142 struct swaylock_surface *surface = data; 151 struct swaylock_surface *surface = data;
143 surface->scale = factor; 152 surface->scale = factor;
144 if (surface->state->run_display) { 153 if (surface->state->run_display) {
@@ -146,11 +155,46 @@ static void output_scale(void *data, struct wl_output *output, int32_t factor) {
146 } 155 }
147} 156}
148 157
149struct wl_output_listener output_listener = { 158struct wl_output_listener _wl_output_listener = {
150 .geometry = output_geometry, 159 .geometry = handle_wl_output_geometry,
151 .mode = output_mode, 160 .mode = handle_wl_output_mode,
152 .done = output_done, 161 .done = handle_wl_output_done,
153 .scale = output_scale, 162 .scale = handle_wl_output_scale,
163};
164
165static void handle_xdg_output_logical_size(void *data, struct zxdg_output_v1 *output,
166 int width, int height) {
167 // Who cares
168}
169
170static void handle_xdg_output_logical_position(void *data,
171 struct zxdg_output_v1 *output, int x, int y) {
172 // Who cares
173}
174
175static void handle_xdg_output_name(void *data, struct zxdg_output_v1 *output,
176 const char *name) {
177 wlr_log(L_DEBUG, "output name is %s", name);
178 struct swaylock_surface *surface = data;
179 surface->xdg_output = output;
180 surface->output_name = strdup(name);
181}
182
183static void handle_xdg_output_description(void *data, struct zxdg_output_v1 *output,
184 const char *description) {
185 // Who cares
186}
187
188static void handle_xdg_output_done(void *data, struct zxdg_output_v1 *output) {
189 // Who cares
190}
191
192struct zxdg_output_v1_listener _xdg_output_listener = {
193 .logical_position = handle_xdg_output_logical_position,
194 .logical_size = handle_xdg_output_logical_size,
195 .done = handle_xdg_output_done,
196 .name = handle_xdg_output_name,
197 .description = handle_xdg_output_description,
154}; 198};
155 199
156static void handle_global(void *data, struct wl_registry *registry, 200static void handle_global(void *data, struct wl_registry *registry,
@@ -172,6 +216,9 @@ static void handle_global(void *data, struct wl_registry *registry,
172 } else if (strcmp(interface, zwlr_input_inhibit_manager_v1_interface.name) == 0) { 216 } else if (strcmp(interface, zwlr_input_inhibit_manager_v1_interface.name) == 0) {
173 state->input_inhibit_manager = wl_registry_bind( 217 state->input_inhibit_manager = wl_registry_bind(
174 registry, name, &zwlr_input_inhibit_manager_v1_interface, 1); 218 registry, name, &zwlr_input_inhibit_manager_v1_interface, 1);
219 } else if (strcmp(interface, zxdg_output_manager_v1_interface.name) == 0) {
220 state->zxdg_output_manager = wl_registry_bind(
221 registry, name, &zxdg_output_manager_v1_interface, 2);
175 } else if (strcmp(interface, wl_output_interface.name) == 0) { 222 } else if (strcmp(interface, wl_output_interface.name) == 0) {
176 struct swaylock_surface *surface = 223 struct swaylock_surface *surface =
177 calloc(1, sizeof(struct swaylock_surface)); 224 calloc(1, sizeof(struct swaylock_surface));
@@ -180,7 +227,7 @@ static void handle_global(void *data, struct wl_registry *registry,
180 &wl_output_interface, 3); 227 &wl_output_interface, 3);
181 surface->output_global_name = name; 228 surface->output_global_name = name;
182 surface->image = state->background_image; 229 surface->image = state->background_image;
183 wl_output_add_listener(surface->output, &output_listener, surface); 230 wl_output_add_listener(surface->output, &_wl_output_listener, surface);
184 wl_list_insert(&state->surfaces, &surface->link); 231 wl_list_insert(&state->surfaces, &surface->link);
185 232
186 if (state->run_display) { 233 if (state->run_display) {
@@ -207,6 +254,70 @@ static const struct wl_registry_listener registry_listener = {
207 .global_remove = handle_global_remove, 254 .global_remove = handle_global_remove,
208}; 255};
209 256
257static cairo_surface_t *select_image(struct swaylock_state *state,
258 struct swaylock_surface *surface) {
259 struct swaylock_image *image;
260 cairo_surface_t *default_image = NULL;
261 wl_list_for_each(image, &state->images, link) {
262 if (lenient_strcmp(image->output_name, surface->output_name) == 0) {
263 return image->cairo_surface;
264 } else if (!image->output_name) {
265 default_image = image->cairo_surface;
266 }
267 }
268 return default_image;
269}
270
271static void load_image(char *arg, struct swaylock_state *state) {
272 // [<output>:]<path>
273 struct swaylock_image *image = calloc(1, sizeof(struct swaylock_image));
274 char *separator = strchr(arg, ':');
275 if (separator) {
276 *separator = '\0';
277 image->output_name = strdup(arg);
278 image->path = strdup(separator + 1);
279 } else {
280 image->output_name = NULL;
281 image->path = strdup(arg);
282 }
283
284 bool exists = false;
285 struct swaylock_image *iter_image;
286 wl_list_for_each(iter_image, &state->images, link) {
287 if (lenient_strcmp(iter_image->output_name, image->output_name) == 0) {
288 exists = true;
289 break;
290 }
291 }
292 if (exists) {
293 if (image->output_name) {
294 wlr_log(L_ERROR, "Multiple images defined for output %s",
295 image->output_name);
296 } else {
297 wlr_log(L_ERROR, "Multiple default images defined");
298 }
299 }
300
301 // Bash doesn't replace the ~ with $HOME if the output name is supplied
302 wordexp_t p;
303 if (wordexp(image->path, &p, 0) == 0) {
304 free(image->path);
305 image->path = strdup(p.we_wordv[0]);
306 wordfree(&p);
307 }
308
309 // Load the actual image
310 image->cairo_surface = load_background_image(image->path);
311 if (!image->cairo_surface) {
312 free(image);
313 return;
314 }
315 wl_list_insert(&state->images, &image->link);
316 state->args.mode = BACKGROUND_MODE_FILL;
317 wlr_log(L_DEBUG, "Loaded image %s for output %s",
318 image->path, image->output_name ? image->output_name : "*");
319}
320
210static struct swaylock_state state; 321static struct swaylock_state state;
211 322
212int main(int argc, char **argv) { 323int main(int argc, char **argv) {
@@ -233,14 +344,14 @@ int main(int argc, char **argv) {
233 " -v, --version Show the version number and quit.\n" 344 " -v, --version Show the version number and quit.\n"
234 " -i, --image [<output>:]<path> Display the given image.\n" 345 " -i, --image [<output>:]<path> Display the given image.\n"
235 " -u, --no-unlock-indicator Disable the unlock indicator.\n" 346 " -u, --no-unlock-indicator Disable the unlock indicator.\n"
236 " -f, --daemonize Detach from the controlling terminal.\n" 347 " -f, --daemonize Detach from the controlling terminal.\n";
237 " --socket <socket> Use the specified socket.\n";
238 348
239 state.args = (struct swaylock_args){ 349 state.args = (struct swaylock_args){
240 .mode = BACKGROUND_MODE_SOLID_COLOR, 350 .mode = BACKGROUND_MODE_SOLID_COLOR,
241 .color = 0xFFFFFFFF, 351 .color = 0xFFFFFFFF,
242 .show_indicator = true, 352 .show_indicator = true,
243 }; 353 };
354 wl_list_init(&state.images);
244 355
245 wlr_log_init(L_DEBUG, NULL); 356 wlr_log_init(L_DEBUG, NULL);
246 357
@@ -258,12 +369,7 @@ int main(int argc, char **argv) {
258 break; 369 break;
259 } 370 }
260 case 'i': 371 case 'i':
261 // TODO: Multiple background images (bleh) 372 load_image(optarg, &state);
262 state.background_image = load_background_image(optarg);
263 if (!state.background_image) {
264 return 1;
265 }
266 state.args.mode = BACKGROUND_MODE_FILL;
267 break; 373 break;
268 case 's': 374 case 's':
269 state.args.mode = parse_background_mode(optarg); 375 state.args.mode = parse_background_mode(optarg);
@@ -313,6 +419,7 @@ int main(int argc, char **argv) {
313 if (!state.input_inhibit_manager) { 419 if (!state.input_inhibit_manager) {
314 wlr_log(L_ERROR, "Compositor does not support the input inhibitor " 420 wlr_log(L_ERROR, "Compositor does not support the input inhibitor "
315 "protocol, refusing to run insecurely"); 421 "protocol, refusing to run insecurely");
422 return 1;
316 } 423 }
317 424
318 if (wl_list_empty(&state.surfaces)) { 425 if (wl_list_empty(&state.surfaces)) {
@@ -322,11 +429,24 @@ int main(int argc, char **argv) {
322 429
323 zwlr_input_inhibit_manager_v1_get_inhibitor(state.input_inhibit_manager); 430 zwlr_input_inhibit_manager_v1_get_inhibitor(state.input_inhibit_manager);
324 431
432 if (state.zxdg_output_manager) {
433 struct swaylock_surface *surface;
434 wl_list_for_each(surface, &state.surfaces, link) {
435 surface->xdg_output = zxdg_output_manager_v1_get_xdg_output(
436 state.zxdg_output_manager, surface->output);
437 zxdg_output_v1_add_listener(
438 surface->xdg_output, &_xdg_output_listener, surface);
439 }
440 wl_display_roundtrip(state.display);
441 } else {
442 wlr_log(L_INFO, "Compositor does not support zxdg output manager, "
443 "images assigned to named outputs will not work");
444 }
445
325 struct swaylock_surface *surface; 446 struct swaylock_surface *surface;
326 wl_list_for_each(surface, &state.surfaces, link) { 447 wl_list_for_each(surface, &state.surfaces, link) {
327 create_layer_surface(surface); 448 create_layer_surface(surface);
328 } 449 }
329 wl_display_roundtrip(state.display);
330 450
331 state.run_display = true; 451 state.run_display = true;
332 while (wl_display_dispatch(state.display) != -1 && state.run_display) { 452 while (wl_display_dispatch(state.display) != -1 && state.run_display) {
diff --git a/swaylock/render.c b/swaylock/render.c
index 7d9d25a5..cc40f4e9 100644
--- a/swaylock/render.c
+++ b/swaylock/render.c
@@ -23,7 +23,7 @@ void render_frame(struct swaylock_surface *surface) {
23 cairo_t *cairo = surface->current_buffer->cairo; 23 cairo_t *cairo = surface->current_buffer->cairo;
24 cairo_identity_matrix(cairo); 24 cairo_identity_matrix(cairo);
25 25
26 if (state->args.mode == BACKGROUND_MODE_SOLID_COLOR) { 26 if (state->args.mode == BACKGROUND_MODE_SOLID_COLOR || !surface->image) {
27 cairo_set_source_u32(cairo, state->args.color); 27 cairo_set_source_u32(cairo, state->args.color);
28 cairo_paint(cairo); 28 cairo_paint(cairo);
29 } else { 29 } else {