diff options
-rw-r--r-- | include/sway/input/cursor.h | 3 | ||||
-rw-r--r-- | include/sway/input/seat.h | 6 | ||||
-rw-r--r-- | include/sway/tree/container.h | 15 | ||||
-rw-r--r-- | include/sway/tree/view.h | 6 | ||||
-rw-r--r-- | include/swaylock/swaylock.h | 26 | ||||
-rw-r--r-- | meson.build | 2 | ||||
-rw-r--r-- | protocols/meson.build | 6 | ||||
-rw-r--r-- | sway/commands/border.c | 2 | ||||
-rw-r--r-- | sway/commands/layout.c | 4 | ||||
-rw-r--r-- | sway/commands/seat/cursor.c | 4 | ||||
-rw-r--r-- | sway/desktop/output.c | 518 | ||||
-rw-r--r-- | sway/input/cursor.c | 31 | ||||
-rw-r--r-- | sway/input/seat.c | 16 | ||||
-rw-r--r-- | sway/ipc-server.c | 22 | ||||
-rw-r--r-- | sway/tree/arrange.c | 64 | ||||
-rw-r--r-- | sway/tree/container.c | 336 | ||||
-rw-r--r-- | sway/tree/layout.c | 11 | ||||
-rw-r--r-- | sway/tree/view.c | 86 | ||||
-rw-r--r-- | swaylock/main.c | 158 | ||||
-rw-r--r-- | swaylock/render.c | 2 |
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 | ||
30 | void sway_cursor_destroy(struct sway_cursor *cursor); | 30 | void sway_cursor_destroy(struct sway_cursor *cursor); |
31 | struct sway_cursor *sway_cursor_create(struct sway_seat *seat); | 31 | struct sway_cursor *sway_cursor_create(struct sway_seat *seat); |
32 | void cursor_send_pointer_motion(struct sway_cursor *cursor, uint32_t time_msec); | 32 | void cursor_send_pointer_motion(struct sway_cursor *cursor, uint32_t time_msec, |
33 | bool allow_refocusing); | ||
33 | void dispatch_cursor_button(struct sway_cursor *cursor, uint32_t time_msec, | 34 | void 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 | */ | ||
100 | struct 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; | |||
11 | struct sway_view; | 11 | struct sway_view; |
12 | struct sway_seat; | 12 | struct 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 | ||
103 | struct sway_container *container_create(enum sway_container_type type); | 111 | struct 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 | */ |
162 | struct sway_container *container_at(struct sway_container *container, | 170 | struct 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 | ||
211 | void container_notify_child_title_changed(struct sway_container *container); | 219 | void container_notify_child_title_changed(struct sway_container *container); |
212 | 220 | ||
221 | /** | ||
222 | * Return the height of a regular title bar. | ||
223 | */ | ||
224 | size_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 | ||
275 | void view_update_marks_textures(struct sway_view *view); | 275 | void 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 | */ | ||
281 | bool 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 | ||
12 | enum auth_state { | 12 | enum 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 | ||
22 | struct swaylock_args { | 22 | struct 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 | ||
48 | struct swaylock_surface { | 50 | struct 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 | ||
67 | struct 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') | |||
29 | wayland_client = dependency('wayland-client') | 29 | wayland_client = dependency('wayland-client') |
30 | wayland_cursor = dependency('wayland-cursor') | 30 | wayland_cursor = dependency('wayland-cursor') |
31 | wayland_egl = dependency('wayland-egl') | 31 | wayland_egl = dependency('wayland-egl') |
32 | wayland_protos = dependency('wayland-protocols') | 32 | wayland_protos = dependency('wayland-protocols', version: '>=1.14') |
33 | xkbcommon = dependency('xkbcommon') | 33 | xkbcommon = dependency('xkbcommon') |
34 | cairo = dependency('cairo') | 34 | cairo = dependency('cairo') |
35 | pango = dependency('pango') | 35 | pango = 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 | ||
30 | client_protocols = [ | 30 | client_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 | ||
37 | server_protocols = [ | 38 | server_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 | ||
44 | client_protos_src = [] | 46 | client_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 | ||
272 | static 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 | |||
283 | static void render_rect(struct wlr_output *wlr_output, | 272 | static 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 | ||
302 | static 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 | |||
309 | static 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 | */ |
319 | static void render_container_simple_border_normal(struct sway_output *output, | 323 | static 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 | */ | ||
388 | static 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; |
473 | static 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) { | 554 | static 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 | ||
542 | static void render_container(struct sway_output *output, | 576 | static 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 | */ |
598 | static void render_container_tabbed(struct sway_output *output, | 629 | static 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 | */ |
606 | static void render_container_stacked(struct sway_output *output, | 690 | static 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 | ||
611 | static void render_container(struct sway_output *output, | 742 | static 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 | ||
138 | void cursor_send_pointer_motion(struct sway_cursor *cursor, uint32_t time_msec) { | 138 | void 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 | ||
183 | static void handle_cursor_motion_absolute( | 200 | static 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 | ||
193 | void dispatch_cursor_button(struct sway_cursor *cursor, | 210 | void 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 | ||
363 | static void handle_tool_tip(struct wl_listener *listener, void *data) { | 380 | static 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 | ||
721 | struct 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 | |||
721 | struct sway_container *sway_seat_get_focus(struct sway_seat *seat) { | 733 | struct 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 | ||
433 | static 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 | |||
432 | void ipc_client_handle_command(struct ipc_client *client) { | 443 | void 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 | |||
180 | static 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 | |||
193 | static 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 | ||
162 | void arrange_children_of(struct sway_container *parent) { | 206 | void 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 | ||
76 | static 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 | |||
89 | static 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 | |||
76 | struct sway_container *container_create(enum sway_container_type type) { | 114 | struct 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 | ||
414 | struct sway_container *container_at(struct sway_container *parent, | 455 | static 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); | 504 | static 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; | 533 | static 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)) { | 558 | static 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 | |||
576 | struct 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 | */ | ||
781 | static 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 | |||
661 | void container_notify_child_title_changed(struct sway_container *container) { | 844 | void 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 | |||
866 | size_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 | |||
898 | bool 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 | ||
27 | void sway_terminate(int exit_code) { | 30 | void 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 | ||
78 | static const struct zwlr_layer_surface_v1_listener layer_surface_listener; | 81 | static const struct zwlr_layer_surface_v1_listener layer_surface_listener; |
79 | 82 | ||
83 | static cairo_surface_t *select_image(struct swaylock_state *state, | ||
84 | struct swaylock_surface *surface); | ||
85 | |||
80 | static void create_layer_surface(struct swaylock_surface *surface) { | 86 | static 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 | ||
126 | static void output_geometry(void *data, struct wl_output *output, int32_t x, | 134 | static 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 | ||
132 | static void output_mode(void *data, struct wl_output *output, uint32_t flags, | 140 | static 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 | ||
137 | static void output_done(void *data, struct wl_output *output) { | 145 | static void handle_wl_output_done(void *data, struct wl_output *output) { |
138 | // Who cares | 146 | // Who cares |
139 | } | 147 | } |
140 | 148 | ||
141 | static void output_scale(void *data, struct wl_output *output, int32_t factor) { | 149 | static 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 | ||
149 | struct wl_output_listener output_listener = { | 158 | struct 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 | |||
165 | static void handle_xdg_output_logical_size(void *data, struct zxdg_output_v1 *output, | ||
166 | int width, int height) { | ||
167 | // Who cares | ||
168 | } | ||
169 | |||
170 | static void handle_xdg_output_logical_position(void *data, | ||
171 | struct zxdg_output_v1 *output, int x, int y) { | ||
172 | // Who cares | ||
173 | } | ||
174 | |||
175 | static 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 | |||
183 | static void handle_xdg_output_description(void *data, struct zxdg_output_v1 *output, | ||
184 | const char *description) { | ||
185 | // Who cares | ||
186 | } | ||
187 | |||
188 | static void handle_xdg_output_done(void *data, struct zxdg_output_v1 *output) { | ||
189 | // Who cares | ||
190 | } | ||
191 | |||
192 | struct 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 | ||
156 | static void handle_global(void *data, struct wl_registry *registry, | 200 | static 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 | ||
257 | static 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 | |||
271 | static 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 | |||
210 | static struct swaylock_state state; | 321 | static struct swaylock_state state; |
211 | 322 | ||
212 | int main(int argc, char **argv) { | 323 | int 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 { |