summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorLibravatar emersion <contact@emersion.fr>2018-08-02 08:11:10 +0100
committerLibravatar GitHub <noreply@github.com>2018-08-02 08:11:10 +0100
commit47bf4ed0cbf104d09bba7f39acbf2ceb84c2c694 (patch)
tree8f0085c1829ab97a920acd9d5116732779177631
parentCorrectly track saved surfaces during multiple transactions (diff)
parentMerge pull request #2391 from RyanDwyer/fix-popups-v2 (diff)
downloadsway-47bf4ed0cbf104d09bba7f39acbf2ceb84c2c694.tar.gz
sway-47bf4ed0cbf104d09bba7f39acbf2ceb84c2c694.tar.zst
sway-47bf4ed0cbf104d09bba7f39acbf2ceb84c2c694.zip
Merge branch 'master' into fix-resize-wiggle
-rw-r--r--completions/bash/swaymsg4
-rw-r--r--completions/zsh/_swaymsg3
-rw-r--r--include/ipc.h5
-rw-r--r--include/sway/config.h2
-rw-r--r--include/sway/input/seat.h2
-rw-r--r--include/sway/ipc-server.h2
-rw-r--r--include/sway/output.h8
-rw-r--r--include/sway/tree/container.h11
-rw-r--r--include/sway/tree/view.h16
-rw-r--r--sway/commands/bind.c52
-rw-r--r--sway/commands/mark.c2
-rw-r--r--sway/commands/move.c4
-rw-r--r--sway/commands/reload.c29
-rw-r--r--sway/desktop/output.c19
-rw-r--r--sway/desktop/render.c37
-rw-r--r--sway/desktop/xdg_shell.c22
-rw-r--r--sway/desktop/xdg_shell_v6.c23
-rw-r--r--sway/input/cursor.c9
-rw-r--r--sway/input/seat.c21
-rw-r--r--sway/ipc-json.c9
-rw-r--r--sway/ipc-server.c136
-rw-r--r--sway/main.c1
-rw-r--r--sway/tree/container.c148
-rw-r--r--sway/tree/layout.c14
-rw-r--r--sway/tree/output.c2
-rw-r--r--sway/tree/view.c31
-rw-r--r--swaymsg/main.c8
-rw-r--r--swaymsg/swaymsg.1.scd3
28 files changed, 529 insertions, 94 deletions
diff --git a/completions/bash/swaymsg b/completions/bash/swaymsg
index 8ec90b6f..20092bdc 100644
--- a/completions/bash/swaymsg
+++ b/completions/bash/swaymsg
@@ -14,7 +14,9 @@ _swaymsg()
14 'get_marks' 14 'get_marks'
15 'get_bar_config' 15 'get_bar_config'
16 'get_version' 16 'get_version'
17 'get_clipboard' 17 'get_binding_modes'
18 'get_config'
19 'send_tick'
18 ) 20 )
19 21
20 short=( 22 short=(
diff --git a/completions/zsh/_swaymsg b/completions/zsh/_swaymsg
index 2e39deb6..a7a1c8e0 100644
--- a/completions/zsh/_swaymsg
+++ b/completions/zsh/_swaymsg
@@ -22,6 +22,9 @@ types=(
22'get_marks' 22'get_marks'
23'get_bar_config' 23'get_bar_config'
24'get_version' 24'get_version'
25'get_binding_modes'
26'get_config'
27'send_tick'
25) 28)
26 29
27_arguments -s \ 30_arguments -s \
diff --git a/include/ipc.h b/include/ipc.h
index 0010718b..a3f60e19 100644
--- a/include/ipc.h
+++ b/include/ipc.h
@@ -15,6 +15,7 @@ enum ipc_command_type {
15 IPC_GET_VERSION = 7, 15 IPC_GET_VERSION = 7,
16 IPC_GET_BINDING_MODES = 8, 16 IPC_GET_BINDING_MODES = 8,
17 IPC_GET_CONFIG = 9, 17 IPC_GET_CONFIG = 9,
18 IPC_SEND_TICK = 10,
18 19
19 // sway-specific command types 20 // sway-specific command types
20 IPC_GET_INPUTS = 100, 21 IPC_GET_INPUTS = 100,
@@ -27,8 +28,8 @@ enum ipc_command_type {
27 IPC_EVENT_WINDOW = ((1<<31) | 3), 28 IPC_EVENT_WINDOW = ((1<<31) | 3),
28 IPC_EVENT_BARCONFIG_UPDATE = ((1<<31) | 4), 29 IPC_EVENT_BARCONFIG_UPDATE = ((1<<31) | 4),
29 IPC_EVENT_BINDING = ((1<<31) | 5), 30 IPC_EVENT_BINDING = ((1<<31) | 5),
30 IPC_EVENT_MODIFIER = ((1<<31) | 6), 31 IPC_EVENT_SHUTDOWN = ((1<<31) | 6),
31 IPC_EVENT_INPUT = ((1<<31) | 7), 32 IPC_EVENT_TICK = ((1<<31) | 7),
32}; 33};
33 34
34#endif 35#endif
diff --git a/include/sway/config.h b/include/sway/config.h
index 0f74b439..909b6827 100644
--- a/include/sway/config.h
+++ b/include/sway/config.h
@@ -488,8 +488,6 @@ int sway_binding_cmp_keys(const void *a, const void *b);
488 488
489void free_sway_binding(struct sway_binding *sb); 489void free_sway_binding(struct sway_binding *sb);
490 490
491struct sway_binding *sway_binding_dup(struct sway_binding *sb);
492
493void seat_execute_command(struct sway_seat *seat, struct sway_binding *binding); 491void seat_execute_command(struct sway_seat *seat, struct sway_binding *binding);
494 492
495void load_swaybars(); 493void load_swaybars();
diff --git a/include/sway/input/seat.h b/include/sway/input/seat.h
index 07febe2c..92387601 100644
--- a/include/sway/input/seat.h
+++ b/include/sway/input/seat.h
@@ -99,7 +99,7 @@ void seat_configure_xcursor(struct sway_seat *seat);
99void seat_set_focus(struct sway_seat *seat, struct sway_container *container); 99void seat_set_focus(struct sway_seat *seat, struct sway_container *container);
100 100
101void seat_set_focus_warp(struct sway_seat *seat, 101void seat_set_focus_warp(struct sway_seat *seat,
102 struct sway_container *container, bool warp); 102 struct sway_container *container, bool warp, bool notify);
103 103
104void seat_set_focus_surface(struct sway_seat *seat, 104void seat_set_focus_surface(struct sway_seat *seat,
105 struct wlr_surface *surface, bool unfocus); 105 struct wlr_surface *surface, bool unfocus);
diff --git a/include/sway/ipc-server.h b/include/sway/ipc-server.h
index 6469f097..4b6d0e25 100644
--- a/include/sway/ipc-server.h
+++ b/include/sway/ipc-server.h
@@ -16,5 +16,7 @@ void ipc_event_workspace(struct sway_container *old,
16void ipc_event_window(struct sway_container *window, const char *change); 16void ipc_event_window(struct sway_container *window, const char *change);
17void ipc_event_barconfig_update(struct bar_config *bar); 17void ipc_event_barconfig_update(struct bar_config *bar);
18void ipc_event_mode(const char *mode, bool pango); 18void ipc_event_mode(const char *mode, bool pango);
19void ipc_event_shutdown(const char *reason);
20void ipc_event_binding(struct sway_binding *binding);
19 21
20#endif 22#endif
diff --git a/include/sway/output.h b/include/sway/output.h
index 6283db68..80dcd37b 100644
--- a/include/sway/output.h
+++ b/include/sway/output.h
@@ -67,10 +67,18 @@ struct sway_container *output_get_active_workspace(struct sway_output *output);
67void output_render(struct sway_output *output, struct timespec *when, 67void output_render(struct sway_output *output, struct timespec *when,
68 pixman_region32_t *damage); 68 pixman_region32_t *damage);
69 69
70void output_surface_for_each_surface(struct sway_output *output,
71 struct wlr_surface *surface, double ox, double oy,
72 sway_surface_iterator_func_t iterator, void *user_data);
73
70void output_view_for_each_surface(struct sway_output *output, 74void output_view_for_each_surface(struct sway_output *output,
71 struct sway_view *view, sway_surface_iterator_func_t iterator, 75 struct sway_view *view, sway_surface_iterator_func_t iterator,
72 void *user_data); 76 void *user_data);
73 77
78void output_view_for_each_popup(struct sway_output *output,
79 struct sway_view *view, sway_surface_iterator_func_t iterator,
80 void *user_data);
81
74void output_layer_for_each_surface(struct sway_output *output, 82void output_layer_for_each_surface(struct sway_output *output,
75 struct wl_list *layer_surfaces, sway_surface_iterator_func_t iterator, 83 struct wl_list *layer_surfaces, sway_surface_iterator_func_t iterator,
76 void *user_data); 84 void *user_data);
diff --git a/include/sway/tree/container.h b/include/sway/tree/container.h
index d4a42a71..12ff8a5a 100644
--- a/include/sway/tree/container.h
+++ b/include/sway/tree/container.h
@@ -230,18 +230,11 @@ struct sway_container *container_parent(struct sway_container *container,
230 * surface-local coordinates of the given layout coordinates if the container 230 * surface-local coordinates of the given layout coordinates if the container
231 * is a view and the view contains a surface at those coordinates. 231 * is a view and the view contains a surface at those coordinates.
232 */ 232 */
233struct sway_container *container_at(struct sway_container *container, 233struct sway_container *container_at(struct sway_container *workspace,
234 double ox, double oy, struct wlr_surface **surface, 234 double lx, double ly, struct wlr_surface **surface,
235 double *sx, double *sy); 235 double *sx, double *sy);
236 236
237/** 237/**
238 * Same as container_at, but only checks floating views and expects coordinates
239 * to be layout coordinates, as that's what floating views use.
240 */
241struct sway_container *floating_container_at(double lx, double ly,
242 struct wlr_surface **surface, double *sx, double *sy);
243
244/**
245 * Apply the function for each descendant of the container breadth first. 238 * Apply the function for each descendant of the container breadth first.
246 */ 239 */
247void container_for_each_descendant_bfs(struct sway_container *container, 240void container_for_each_descendant_bfs(struct sway_container *container,
diff --git a/include/sway/tree/view.h b/include/sway/tree/view.h
index 0f9b0bb2..37fd02bc 100644
--- a/include/sway/tree/view.h
+++ b/include/sway/tree/view.h
@@ -47,7 +47,10 @@ struct sway_view_impl {
47 bool (*has_client_side_decorations)(struct sway_view *view); 47 bool (*has_client_side_decorations)(struct sway_view *view);
48 void (*for_each_surface)(struct sway_view *view, 48 void (*for_each_surface)(struct sway_view *view,
49 wlr_surface_iterator_func_t iterator, void *user_data); 49 wlr_surface_iterator_func_t iterator, void *user_data);
50 void (*for_each_popup)(struct sway_view *view,
51 wlr_surface_iterator_func_t iterator, void *user_data);
50 void (*close)(struct sway_view *view); 52 void (*close)(struct sway_view *view);
53 void (*close_popups)(struct sway_view *view);
51 void (*destroy)(struct sway_view *view); 54 void (*destroy)(struct sway_view *view);
52}; 55};
53 56
@@ -249,11 +252,22 @@ void view_set_tiled(struct sway_view *view, bool tiled);
249 252
250void view_close(struct sway_view *view); 253void view_close(struct sway_view *view);
251 254
255void view_close_popups(struct sway_view *view);
256
252void view_damage_from(struct sway_view *view); 257void view_damage_from(struct sway_view *view);
253 258
259/**
260 * Iterate all surfaces of a view (toplevels + popups).
261 */
254void view_for_each_surface(struct sway_view *view, 262void view_for_each_surface(struct sway_view *view,
255 wlr_surface_iterator_func_t iterator, void *user_data); 263 wlr_surface_iterator_func_t iterator, void *user_data);
256 264
265/**
266 * Iterate all popups recursively.
267 */
268void view_for_each_popup(struct sway_view *view,
269 wlr_surface_iterator_func_t iterator, void *user_data);
270
257// view implementation 271// view implementation
258 272
259void view_init(struct sway_view *view, enum sway_view_type type, 273void view_init(struct sway_view *view, enum sway_view_type type,
@@ -314,6 +328,8 @@ void view_clear_marks(struct sway_view *view);
314 328
315bool view_has_mark(struct sway_view *view, char *mark); 329bool view_has_mark(struct sway_view *view, char *mark);
316 330
331void view_add_mark(struct sway_view *view, char *mark);
332
317void view_update_marks_textures(struct sway_view *view); 333void view_update_marks_textures(struct sway_view *view);
318 334
319/** 335/**
diff --git a/sway/commands/bind.c b/sway/commands/bind.c
index 133fd089..8270b958 100644
--- a/sway/commands/bind.c
+++ b/sway/commands/bind.c
@@ -1,3 +1,4 @@
1#define _XOPEN_SOURCE 500
1#ifdef __linux__ 2#ifdef __linux__
2#include <linux/input-event-codes.h> 3#include <linux/input-event-codes.h>
3#elif __FreeBSD__ 4#elif __FreeBSD__
@@ -5,9 +6,11 @@
5#endif 6#endif
6#include <xkbcommon/xkbcommon.h> 7#include <xkbcommon/xkbcommon.h>
7#include <xkbcommon/xkbcommon-names.h> 8#include <xkbcommon/xkbcommon-names.h>
9#include <string.h>
8#include <strings.h> 10#include <strings.h>
9#include "sway/commands.h" 11#include "sway/commands.h"
10#include "sway/config.h" 12#include "sway/config.h"
13#include "sway/ipc-server.h"
11#include "list.h" 14#include "list.h"
12#include "log.h" 15#include "log.h"
13#include "stringop.h" 16#include "stringop.h"
@@ -27,6 +30,33 @@ void free_sway_binding(struct sway_binding *binding) {
27 free(binding); 30 free(binding);
28} 31}
29 32
33static struct sway_binding *sway_binding_dup(struct sway_binding *sb) {
34 struct sway_binding *new_sb = calloc(1, sizeof(struct sway_binding));
35 if (!new_sb) {
36 return NULL;
37 }
38
39 new_sb->type = sb->type;
40 new_sb->order = sb->order;
41 new_sb->flags = sb->flags;
42 new_sb->modifiers = sb->modifiers;
43 new_sb->command = strdup(sb->command);
44
45 new_sb->keys = create_list();
46 int i;
47 for (i = 0; i < sb->keys->length; ++i) {
48 xkb_keysym_t *key = malloc(sizeof(xkb_keysym_t));
49 if (!key) {
50 free_sway_binding(new_sb);
51 return NULL;
52 }
53 *key = *(xkb_keysym_t *)sb->keys->items[i];
54 list_add(new_sb->keys, key);
55 }
56
57 return new_sb;
58}
59
30/** 60/**
31 * Returns true if the bindings have the same key and modifier combinations. 61 * Returns true if the bindings have the same key and modifier combinations.
32 * Note that keyboard layout is not considered, so the bindings might actually 62 * Note that keyboard layout is not considered, so the bindings might actually
@@ -275,11 +305,31 @@ struct cmd_results *cmd_bindcode(int argc, char **argv) {
275void seat_execute_command(struct sway_seat *seat, struct sway_binding *binding) { 305void seat_execute_command(struct sway_seat *seat, struct sway_binding *binding) {
276 wlr_log(WLR_DEBUG, "running command for binding: %s", 306 wlr_log(WLR_DEBUG, "running command for binding: %s",
277 binding->command); 307 binding->command);
308
309 struct sway_binding *binding_copy = binding;
310 bool reload = false;
311 // if this is a reload command we need to make a duplicate of the
312 // binding since it will be gone after the reload has completed.
313 if (strcasecmp(binding->command, "reload") == 0) {
314 reload = true;
315 binding_copy = sway_binding_dup(binding);
316 if (!binding_copy) {
317 wlr_log(WLR_ERROR, "Failed to duplicate binding during reload");
318 return;
319 }
320 }
321
278 config->handler_context.seat = seat; 322 config->handler_context.seat = seat;
279 struct cmd_results *results = execute_command(binding->command, NULL); 323 struct cmd_results *results = execute_command(binding->command, NULL);
280 if (results->status != CMD_SUCCESS) { 324 if (results->status == CMD_SUCCESS) {
325 ipc_event_binding(binding_copy);
326 } else {
281 wlr_log(WLR_DEBUG, "could not run command for binding: %s (%s)", 327 wlr_log(WLR_DEBUG, "could not run command for binding: %s (%s)",
282 binding->command, results->error); 328 binding->command, results->error);
283 } 329 }
330
331 if (reload) { // free the binding if we made a copy
332 free_sway_binding(binding_copy);
333 }
284 free_cmd_results(results); 334 free_cmd_results(results);
285} 335}
diff --git a/sway/commands/mark.c b/sway/commands/mark.c
index 5a897e69..9ea8c301 100644
--- a/sway/commands/mark.c
+++ b/sway/commands/mark.c
@@ -58,7 +58,7 @@ struct cmd_results *cmd_mark(int argc, char **argv) {
58 view_find_and_unmark(mark); 58 view_find_and_unmark(mark);
59 59
60 if (!toggle || !had_mark) { 60 if (!toggle || !had_mark) {
61 list_add(view->marks, strdup(mark)); 61 view_add_mark(view, mark);
62 } 62 }
63 63
64 free(mark); 64 free(mark);
diff --git a/sway/commands/move.c b/sway/commands/move.c
index 1aae3838..46ebcd83 100644
--- a/sway/commands/move.c
+++ b/sway/commands/move.c
@@ -98,7 +98,7 @@ static struct cmd_results *cmd_move_container(struct sway_container *current,
98 container_move_to(current, destination); 98 container_move_to(current, destination);
99 struct sway_container *focus = seat_get_focus_inactive( 99 struct sway_container *focus = seat_get_focus_inactive(
100 config->handler_context.seat, old_parent); 100 config->handler_context.seat, old_parent);
101 seat_set_focus(config->handler_context.seat, focus); 101 seat_set_focus_warp(config->handler_context.seat, focus, true, false);
102 container_reap_empty(old_parent); 102 container_reap_empty(old_parent);
103 container_reap_empty(destination->parent); 103 container_reap_empty(destination->parent);
104 104
@@ -135,7 +135,7 @@ static struct cmd_results *cmd_move_container(struct sway_container *current,
135 struct sway_container *old_parent = current->parent; 135 struct sway_container *old_parent = current->parent;
136 struct sway_container *old_ws = container_parent(current, C_WORKSPACE); 136 struct sway_container *old_ws = container_parent(current, C_WORKSPACE);
137 container_move_to(current, focus); 137 container_move_to(current, focus);
138 seat_set_focus(config->handler_context.seat, old_parent); 138 seat_set_focus_warp(config->handler_context.seat, old_parent, true, false);
139 container_reap_empty(old_parent); 139 container_reap_empty(old_parent);
140 container_reap_empty(focus->parent); 140 container_reap_empty(focus->parent);
141 141
diff --git a/sway/commands/reload.c b/sway/commands/reload.c
index cea6a94b..5c1b19b4 100644
--- a/sway/commands/reload.c
+++ b/sway/commands/reload.c
@@ -1,17 +1,46 @@
1#define _XOPEN_SOURCE 500
2#include <string.h>
1#include "sway/commands.h" 3#include "sway/commands.h"
2#include "sway/config.h" 4#include "sway/config.h"
5#include "sway/ipc-server.h"
3#include "sway/tree/arrange.h" 6#include "sway/tree/arrange.h"
7#include "list.h"
4 8
5struct cmd_results *cmd_reload(int argc, char **argv) { 9struct cmd_results *cmd_reload(int argc, char **argv) {
6 struct cmd_results *error = NULL; 10 struct cmd_results *error = NULL;
7 if ((error = checkarg(argc, "reload", EXPECTED_EQUAL_TO, 0))) { 11 if ((error = checkarg(argc, "reload", EXPECTED_EQUAL_TO, 0))) {
8 return error; 12 return error;
9 } 13 }
14
15 // store bar ids to check against new bars for barconfig_update events
16 list_t *bar_ids = create_list();
17 for (int i = 0; i < config->bars->length; ++i) {
18 struct bar_config *bar = config->bars->items[i];
19 list_add(bar_ids, strdup(bar->id));
20 }
21
10 if (!load_main_config(config->current_config_path, true)) { 22 if (!load_main_config(config->current_config_path, true)) {
11 return cmd_results_new(CMD_FAILURE, "reload", "Error(s) reloading config."); 23 return cmd_results_new(CMD_FAILURE, "reload", "Error(s) reloading config.");
12 } 24 }
25 ipc_event_workspace(NULL, NULL, "reload");
13 26
14 load_swaybars(); 27 load_swaybars();
28
29 for (int i = 0; i < config->bars->length; ++i) {
30 struct bar_config *bar = config->bars->items[i];
31 for (int j = 0; j < bar_ids->length; ++j) {
32 if (strcmp(bar->id, bar_ids->items[j]) == 0) {
33 ipc_event_barconfig_update(bar);
34 break;
35 }
36 }
37 }
38
39 for (int i = 0; i < bar_ids->length; ++i) {
40 free(bar_ids->items[i]);
41 }
42 list_free(bar_ids);
43
15 arrange_windows(&root_container); 44 arrange_windows(&root_container);
16 return cmd_results_new(CMD_SUCCESS, NULL, NULL); 45 return cmd_results_new(CMD_SUCCESS, NULL, NULL);
17} 46}
diff --git a/sway/desktop/output.c b/sway/desktop/output.c
index 31b53213..66747a3f 100644
--- a/sway/desktop/output.c
+++ b/sway/desktop/output.c
@@ -119,7 +119,7 @@ static void output_for_each_surface_iterator(struct wlr_surface *surface,
119 data->user_data); 119 data->user_data);
120} 120}
121 121
122static void output_surface_for_each_surface(struct sway_output *output, 122void output_surface_for_each_surface(struct sway_output *output,
123 struct wlr_surface *surface, double ox, double oy, 123 struct wlr_surface *surface, double ox, double oy,
124 sway_surface_iterator_func_t iterator, void *user_data) { 124 sway_surface_iterator_func_t iterator, void *user_data) {
125 struct surface_iterator_data data = { 125 struct surface_iterator_data data = {
@@ -155,6 +155,23 @@ void output_view_for_each_surface(struct sway_output *output,
155 output_for_each_surface_iterator, &data); 155 output_for_each_surface_iterator, &data);
156} 156}
157 157
158void output_view_for_each_popup(struct sway_output *output,
159 struct sway_view *view, sway_surface_iterator_func_t iterator,
160 void *user_data) {
161 struct surface_iterator_data data = {
162 .user_iterator = iterator,
163 .user_data = user_data,
164 .output = output,
165 .ox = view->swayc->current.view_x - output->swayc->current.swayc_x,
166 .oy = view->swayc->current.view_y - output->swayc->current.swayc_y,
167 .width = view->swayc->current.view_width,
168 .height = view->swayc->current.view_height,
169 .rotation = 0, // TODO
170 };
171
172 view_for_each_popup(view, output_for_each_surface_iterator, &data);
173}
174
158void output_layer_for_each_surface(struct sway_output *output, 175void output_layer_for_each_surface(struct sway_output *output,
159 struct wl_list *layer_surfaces, sway_surface_iterator_func_t iterator, 176 struct wl_list *layer_surfaces, sway_surface_iterator_func_t iterator,
160 void *user_data) { 177 void *user_data) {
diff --git a/sway/desktop/render.c b/sway/desktop/render.c
index f0e47c95..1f374740 100644
--- a/sway/desktop/render.c
+++ b/sway/desktop/render.c
@@ -186,13 +186,36 @@ static void premultiply_alpha(float color[4], float opacity) {
186 color[2] *= color[3]; 186 color[2] *= color[3];
187} 187}
188 188
189static void render_view_surfaces(struct sway_view *view, 189static void render_view_toplevels(struct sway_view *view,
190 struct sway_output *output, pixman_region32_t *damage, float alpha) { 190 struct sway_output *output, pixman_region32_t *damage, float alpha) {
191 struct render_data data = { 191 struct render_data data = {
192 .damage = damage, 192 .damage = damage,
193 .alpha = alpha, 193 .alpha = alpha,
194 }; 194 };
195 output_view_for_each_surface(output, view, render_surface_iterator, &data); 195 // Render all toplevels without descending into popups
196 output_surface_for_each_surface(output, view->surface,
197 view->swayc->current.view_x, view->swayc->current.view_y,
198 render_surface_iterator, &data);
199}
200
201static void render_popup_iterator(struct sway_output *output,
202 struct wlr_surface *surface, struct wlr_box *box, float rotation,
203 void *data) {
204 // Render this popup's surface
205 render_surface_iterator(output, surface, box, rotation, data);
206
207 // Render this popup's child toplevels
208 output_surface_for_each_surface(output, surface, box->x, box->y,
209 render_surface_iterator, data);
210}
211
212static void render_view_popups(struct sway_view *view,
213 struct sway_output *output, pixman_region32_t *damage, float alpha) {
214 struct render_data data = {
215 .damage = damage,
216 .alpha = alpha,
217 };
218 output_view_for_each_popup(output, view, render_popup_iterator, &data);
196} 219}
197 220
198static void render_saved_view(struct sway_view *view, 221static void render_saved_view(struct sway_view *view,
@@ -239,7 +262,7 @@ static void render_view(struct sway_output *output, pixman_region32_t *damage,
239 if (view->saved_buffer) { 262 if (view->saved_buffer) {
240 render_saved_view(view, output, damage, view->swayc->alpha); 263 render_saved_view(view, output, damage, view->swayc->alpha);
241 } else { 264 } else {
242 render_view_surfaces(view, output, damage, view->swayc->alpha); 265 render_view_toplevels(view, output, damage, view->swayc->alpha);
243 } 266 }
244 267
245 if (view->using_csd) { 268 if (view->using_csd) {
@@ -843,7 +866,7 @@ void output_render(struct sway_output *output, struct timespec *when,
843 render_saved_view(fullscreen_con->sway_view, 866 render_saved_view(fullscreen_con->sway_view,
844 output, damage, 1.0f); 867 output, damage, 1.0f);
845 } else { 868 } else {
846 render_view_surfaces(fullscreen_con->sway_view, 869 render_view_toplevels(fullscreen_con->sway_view,
847 output, damage, 1.0f); 870 output, damage, 1.0f);
848 } 871 }
849 } else { 872 } else {
@@ -879,6 +902,12 @@ void output_render(struct sway_output *output, struct timespec *when,
879 &output->layers[ZWLR_LAYER_SHELL_V1_LAYER_TOP]); 902 &output->layers[ZWLR_LAYER_SHELL_V1_LAYER_TOP]);
880 } 903 }
881 904
905 struct sway_seat *seat = input_manager_current_seat(input_manager);
906 struct sway_container *focus = seat_get_focus(seat);
907 if (focus && focus->type == C_VIEW) {
908 render_view_popups(focus->sway_view, output, damage, focus->alpha);
909 }
910
882render_overlay: 911render_overlay:
883 render_layer(output, damage, 912 render_layer(output, damage,
884 &output->layers[ZWLR_LAYER_SHELL_V1_LAYER_OVERLAY]); 913 &output->layers[ZWLR_LAYER_SHELL_V1_LAYER_OVERLAY]);
diff --git a/sway/desktop/xdg_shell.c b/sway/desktop/xdg_shell.c
index e6e1527e..b364663d 100644
--- a/sway/desktop/xdg_shell.c
+++ b/sway/desktop/xdg_shell.c
@@ -179,6 +179,14 @@ static void for_each_surface(struct sway_view *view,
179 user_data); 179 user_data);
180} 180}
181 181
182static void for_each_popup(struct sway_view *view,
183 wlr_surface_iterator_func_t iterator, void *user_data) {
184 if (xdg_shell_view_from_view(view) == NULL) {
185 return;
186 }
187 wlr_xdg_surface_for_each_popup(view->wlr_xdg_surface, iterator, user_data);
188}
189
182static void _close(struct sway_view *view) { 190static void _close(struct sway_view *view) {
183 if (xdg_shell_view_from_view(view) == NULL) { 191 if (xdg_shell_view_from_view(view) == NULL) {
184 return; 192 return;
@@ -189,6 +197,18 @@ static void _close(struct sway_view *view) {
189 } 197 }
190} 198}
191 199
200static void close_popups_iterator(struct wlr_surface *surface,
201 int sx, int sy, void *data) {
202 struct wlr_xdg_surface *xdg_surface =
203 wlr_xdg_surface_from_wlr_surface(surface);
204 wlr_xdg_surface_send_close(xdg_surface);
205}
206
207static void close_popups(struct sway_view *view) {
208 wlr_xdg_surface_for_each_popup(view->wlr_xdg_surface,
209 close_popups_iterator, NULL);
210}
211
192static void destroy(struct sway_view *view) { 212static void destroy(struct sway_view *view) {
193 struct sway_xdg_shell_view *xdg_shell_view = 213 struct sway_xdg_shell_view *xdg_shell_view =
194 xdg_shell_view_from_view(view); 214 xdg_shell_view_from_view(view);
@@ -207,7 +227,9 @@ static const struct sway_view_impl view_impl = {
207 .set_fullscreen = set_fullscreen, 227 .set_fullscreen = set_fullscreen,
208 .wants_floating = wants_floating, 228 .wants_floating = wants_floating,
209 .for_each_surface = for_each_surface, 229 .for_each_surface = for_each_surface,
230 .for_each_popup = for_each_popup,
210 .close = _close, 231 .close = _close,
232 .close_popups = close_popups,
211 .destroy = destroy, 233 .destroy = destroy,
212}; 234};
213 235
diff --git a/sway/desktop/xdg_shell_v6.c b/sway/desktop/xdg_shell_v6.c
index 5feee3e4..ffea03ad 100644
--- a/sway/desktop/xdg_shell_v6.c
+++ b/sway/desktop/xdg_shell_v6.c
@@ -175,6 +175,15 @@ static void for_each_surface(struct sway_view *view,
175 user_data); 175 user_data);
176} 176}
177 177
178static void for_each_popup(struct sway_view *view,
179 wlr_surface_iterator_func_t iterator, void *user_data) {
180 if (xdg_shell_v6_view_from_view(view) == NULL) {
181 return;
182 }
183 wlr_xdg_surface_v6_for_each_popup(view->wlr_xdg_surface_v6, iterator,
184 user_data);
185}
186
178static void _close(struct sway_view *view) { 187static void _close(struct sway_view *view) {
179 if (xdg_shell_v6_view_from_view(view) == NULL) { 188 if (xdg_shell_v6_view_from_view(view) == NULL) {
180 return; 189 return;
@@ -185,6 +194,18 @@ static void _close(struct sway_view *view) {
185 } 194 }
186} 195}
187 196
197static void close_popups_iterator(struct wlr_surface *surface,
198 int sx, int sy, void *data) {
199 struct wlr_xdg_surface_v6 *xdg_surface_v6 =
200 wlr_xdg_surface_v6_from_wlr_surface(surface);
201 wlr_xdg_surface_v6_send_close(xdg_surface_v6);
202}
203
204static void close_popups(struct sway_view *view) {
205 wlr_xdg_surface_v6_for_each_popup(view->wlr_xdg_surface_v6,
206 close_popups_iterator, NULL);
207}
208
188static void destroy(struct sway_view *view) { 209static void destroy(struct sway_view *view) {
189 struct sway_xdg_shell_v6_view *xdg_shell_v6_view = 210 struct sway_xdg_shell_v6_view *xdg_shell_v6_view =
190 xdg_shell_v6_view_from_view(view); 211 xdg_shell_v6_view_from_view(view);
@@ -203,7 +224,9 @@ static const struct sway_view_impl view_impl = {
203 .set_fullscreen = set_fullscreen, 224 .set_fullscreen = set_fullscreen,
204 .wants_floating = wants_floating, 225 .wants_floating = wants_floating,
205 .for_each_surface = for_each_surface, 226 .for_each_surface = for_each_surface,
227 .for_each_popup = for_each_popup,
206 .close = _close, 228 .close = _close,
229 .close_popups = close_popups,
207 .destroy = destroy, 230 .destroy = destroy,
208}; 231};
209 232
diff --git a/sway/input/cursor.c b/sway/input/cursor.c
index 96ac7b33..79f6ec46 100644
--- a/sway/input/cursor.c
+++ b/sway/input/cursor.c
@@ -109,9 +109,6 @@ static struct sway_container *container_at_coords(
109 } 109 }
110 110
111 struct sway_container *c; 111 struct sway_container *c;
112 if ((c = floating_container_at(lx, ly, surface, sx, sy))) {
113 return c;
114 }
115 if ((c = container_at(ws, lx, ly, surface, sx, sy))) { 112 if ((c = container_at(ws, lx, ly, surface, sx, sy))) {
116 return c; 113 return c;
117 } 114 }
@@ -349,7 +346,7 @@ void cursor_send_pointer_motion(struct sway_cursor *cursor, uint32_t time_msec,
349 output = container_parent(c, C_OUTPUT); 346 output = container_parent(c, C_OUTPUT);
350 } 347 }
351 if (output != focus) { 348 if (output != focus) {
352 seat_set_focus_warp(seat, c, false); 349 seat_set_focus_warp(seat, c, false, true);
353 } 350 }
354 } else if (c->type == C_VIEW) { 351 } else if (c->type == C_VIEW) {
355 // Focus c if the following are true: 352 // Focus c if the following are true:
@@ -359,13 +356,13 @@ void cursor_send_pointer_motion(struct sway_cursor *cursor, uint32_t time_msec,
359 if (!wlr_seat_keyboard_has_grab(cursor->seat->wlr_seat) && 356 if (!wlr_seat_keyboard_has_grab(cursor->seat->wlr_seat) &&
360 c != prev_c && 357 c != prev_c &&
361 view_is_visible(c->sway_view)) { 358 view_is_visible(c->sway_view)) {
362 seat_set_focus_warp(seat, c, false); 359 seat_set_focus_warp(seat, c, false, true);
363 } else { 360 } else {
364 struct sway_container *next_focus = 361 struct sway_container *next_focus =
365 seat_get_focus_inactive(seat, &root_container); 362 seat_get_focus_inactive(seat, &root_container);
366 if (next_focus && next_focus->type == C_VIEW && 363 if (next_focus && next_focus->type == C_VIEW &&
367 view_is_visible(next_focus->sway_view)) { 364 view_is_visible(next_focus->sway_view)) {
368 seat_set_focus_warp(seat, next_focus, false); 365 seat_set_focus_warp(seat, next_focus, false, true);
369 } 366 }
370 } 367 }
371 } 368 }
diff --git a/sway/input/seat.c b/sway/input/seat.c
index 53a92989..869560af 100644
--- a/sway/input/seat.c
+++ b/sway/input/seat.c
@@ -393,7 +393,6 @@ struct sway_seat *seat_create(struct sway_input_manager *input,
393 WL_SEAT_CAPABILITY_POINTER | 393 WL_SEAT_CAPABILITY_POINTER |
394 WL_SEAT_CAPABILITY_TOUCH); 394 WL_SEAT_CAPABILITY_TOUCH);
395 395
396 seat_configure_xcursor(seat);
397 396
398 wl_list_insert(&input->seats, &seat->link); 397 wl_list_insert(&input->seats, &seat->link);
399 398
@@ -438,6 +437,7 @@ static void seat_apply_input_config(struct sway_seat *seat,
438 437
439static void seat_configure_pointer(struct sway_seat *seat, 438static void seat_configure_pointer(struct sway_seat *seat,
440 struct sway_seat_device *sway_device) { 439 struct sway_seat_device *sway_device) {
440 seat_configure_xcursor(seat);
441 wlr_cursor_attach_input_device(seat->cursor->cursor, 441 wlr_cursor_attach_input_device(seat->cursor->cursor,
442 sway_device->input_device->wlr_device); 442 sway_device->input_device->wlr_device);
443 seat_apply_input_config(seat, sway_device); 443 seat_apply_input_config(seat, sway_device);
@@ -617,7 +617,7 @@ static int handle_urgent_timeout(void *data) {
617} 617}
618 618
619void seat_set_focus_warp(struct sway_seat *seat, 619void seat_set_focus_warp(struct sway_seat *seat,
620 struct sway_container *container, bool warp) { 620 struct sway_container *container, bool warp, bool notify) {
621 if (seat->focused_layer) { 621 if (seat->focused_layer) {
622 return; 622 return;
623 } 623 }
@@ -737,9 +737,18 @@ void seat_set_focus_warp(struct sway_seat *seat,
737 } 737 }
738 } 738 }
739 739
740 // Close any popups on the old focus
741 if (last_focus && last_focus != container) {
742 if (last_focus->type == C_VIEW) {
743 view_close_popups(last_focus->sway_view);
744 }
745 }
746
740 if (last_focus) { 747 if (last_focus) {
741 if (last_workspace) { 748 if (last_workspace) {
742 ipc_event_workspace(last_workspace, container, "focus"); 749 if (notify && last_workspace != new_workspace) {
750 ipc_event_workspace(last_workspace, new_workspace, "focus");
751 }
743 if (!workspace_is_visible(last_workspace) 752 if (!workspace_is_visible(last_workspace)
744 && workspace_is_empty(last_workspace)) { 753 && workspace_is_empty(last_workspace)) {
745 if (last_workspace == last_focus) { 754 if (last_workspace == last_focus) {
@@ -766,6 +775,10 @@ void seat_set_focus_warp(struct sway_seat *seat,
766 } 775 }
767 } 776 }
768 777
778 if (container->type == C_VIEW) {
779 ipc_event_window(container, "focus");
780 }
781
769 seat->has_focus = (container != NULL); 782 seat->has_focus = (container != NULL);
770 783
771 update_debug_tree(); 784 update_debug_tree();
@@ -773,7 +786,7 @@ void seat_set_focus_warp(struct sway_seat *seat,
773 786
774void seat_set_focus(struct sway_seat *seat, 787void seat_set_focus(struct sway_seat *seat,
775 struct sway_container *container) { 788 struct sway_container *container) {
776 seat_set_focus_warp(seat, container, true); 789 seat_set_focus_warp(seat, container, true, true);
777} 790}
778 791
779void seat_set_focus_surface(struct sway_seat *seat, 792void seat_set_focus_surface(struct sway_seat *seat,
diff --git a/sway/ipc-json.c b/sway/ipc-json.c
index c49ea47e..4c2bcc98 100644
--- a/sway/ipc-json.c
+++ b/sway/ipc-json.c
@@ -201,6 +201,15 @@ static void ipc_json_describe_view(struct sway_container *c, json_object *object
201 bool urgent = c->type == C_VIEW ? 201 bool urgent = c->type == C_VIEW ?
202 view_is_urgent(c->sway_view) : container_has_urgent_child(c); 202 view_is_urgent(c->sway_view) : container_has_urgent_child(c);
203 json_object_object_add(object, "urgent", json_object_new_boolean(urgent)); 203 json_object_object_add(object, "urgent", json_object_new_boolean(urgent));
204
205 if (c->type == C_VIEW) {
206 json_object *marks = json_object_new_array();
207 list_t *view_marks = c->sway_view->marks;
208 for (int i = 0; i < view_marks->length; ++i) {
209 json_object_array_add(marks, json_object_new_string(view_marks->items[i]));
210 }
211 json_object_object_add(object, "marks", marks);
212 }
204} 213}
205 214
206static void focus_inactive_children_iterator(struct sway_container *c, void *data) { 215static void focus_inactive_children_iterator(struct sway_container *c, void *data) {
diff --git a/sway/ipc-server.c b/sway/ipc-server.c
index be703915..7d2d8969 100644
--- a/sway/ipc-server.c
+++ b/sway/ipc-server.c
@@ -3,12 +3,18 @@
3// Any value will hide SOCK_CLOEXEC on FreeBSD (__BSD_VISIBLE=0) 3// Any value will hide SOCK_CLOEXEC on FreeBSD (__BSD_VISIBLE=0)
4#define _XOPEN_SOURCE 700 4#define _XOPEN_SOURCE 700
5#endif 5#endif
6#ifdef __linux__
7#include <linux/input-event-codes.h>
8#elif __FreeBSD__
9#include <dev/evdev/input-event-codes.h>
10#endif
6#include <assert.h> 11#include <assert.h>
7#include <errno.h> 12#include <errno.h>
8#include <fcntl.h> 13#include <fcntl.h>
9#include <json-c/json.h> 14#include <json-c/json.h>
10#include <stdbool.h> 15#include <stdbool.h>
11#include <stdint.h> 16#include <stdint.h>
17#include <stdio.h>
12#include <stdlib.h> 18#include <stdlib.h>
13#include <string.h> 19#include <string.h>
14#include <sys/socket.h> 20#include <sys/socket.h>
@@ -28,6 +34,7 @@
28#include "sway/tree/view.h" 34#include "sway/tree/view.h"
29#include "list.h" 35#include "list.h"
30#include "log.h" 36#include "log.h"
37#include "util.h"
31 38
32static int ipc_socket = -1; 39static int ipc_socket = -1;
33static struct wl_event_source *ipc_event_source = NULL; 40static struct wl_event_source *ipc_event_source = NULL;
@@ -291,13 +298,11 @@ void ipc_event_workspace(struct sway_container *old,
291 wlr_log(WLR_DEBUG, "Sending workspace::%s event", change); 298 wlr_log(WLR_DEBUG, "Sending workspace::%s event", change);
292 json_object *obj = json_object_new_object(); 299 json_object *obj = json_object_new_object();
293 json_object_object_add(obj, "change", json_object_new_string(change)); 300 json_object_object_add(obj, "change", json_object_new_string(change));
294 if (strcmp("focus", change) == 0) { 301 if (old) {
295 if (old) { 302 json_object_object_add(obj, "old",
296 json_object_object_add(obj, "old", 303 ipc_json_describe_container_recursive(old));
297 ipc_json_describe_container_recursive(old)); 304 } else {
298 } else { 305 json_object_object_add(obj, "old", NULL);
299 json_object_object_add(obj, "old", NULL);
300 }
301 } 306 }
302 307
303 if (new) { 308 if (new) {
@@ -353,6 +358,104 @@ void ipc_event_mode(const char *mode, bool pango) {
353 json_object_put(obj); 358 json_object_put(obj);
354} 359}
355 360
361void ipc_event_shutdown(const char *reason) {
362 if (!ipc_has_event_listeners(IPC_EVENT_SHUTDOWN)) {
363 return;
364 }
365 wlr_log(WLR_DEBUG, "Sending shutdown::%s event", reason);
366
367 json_object *json = json_object_new_object();
368 json_object_object_add(json, "change", json_object_new_string(reason));
369
370 const char *json_string = json_object_to_json_string(json);
371 ipc_send_event(json_string, IPC_EVENT_SHUTDOWN);
372 json_object_put(json);
373}
374
375void ipc_event_binding(struct sway_binding *binding) {
376 if (!ipc_has_event_listeners(IPC_EVENT_BINDING)) {
377 return;
378 }
379 wlr_log(WLR_DEBUG, "Sending binding event");
380
381 json_object *json_binding = json_object_new_object();
382 json_object_object_add(json_binding, "command", json_object_new_string(binding->command));
383
384 const char *names[10];
385 int len = get_modifier_names(names, binding->modifiers);
386 json_object *modifiers = json_object_new_array();
387 for (int i = 0; i < len; ++i) {
388 json_object_array_add(modifiers, json_object_new_string(names[i]));
389 }
390 json_object_object_add(json_binding, "event_state_mask", modifiers);
391
392 json_object *input_codes = json_object_new_array();
393 int input_code = 0;
394 json_object *symbols = json_object_new_array();
395 json_object *symbol = NULL;
396
397 if (binding->type == BINDING_KEYCODE) { // bindcode: populate input_codes
398 uint32_t keycode;
399 for (int i = 0; i < binding->keys->length; ++i) {
400 keycode = *(uint32_t *)binding->keys->items[i];
401 json_object_array_add(input_codes, json_object_new_int(keycode));
402 if (i == 0) {
403 input_code = keycode;
404 }
405 }
406 } else { // bindsym/mouse: populate symbols
407 uint32_t keysym;
408 char buffer[64];
409 for (int i = 0; i < binding->keys->length; ++i) {
410 keysym = *(uint32_t *)binding->keys->items[i];
411 if (keysym >= BTN_LEFT && keysym <= BTN_LEFT + 8) {
412 snprintf(buffer, 64, "button%u", keysym - BTN_LEFT + 1);
413 } else if (xkb_keysym_get_name(keysym, buffer, 64) < 0) {
414 continue;
415 }
416
417 json_object *str = json_object_new_string(buffer);
418 if (i == 0) {
419 // str is owned by both symbol and symbols. Make sure
420 // to bump the ref count.
421 json_object_array_add(symbols, json_object_get(str));
422 symbol = str;
423 } else {
424 json_object_array_add(symbols, str);
425 }
426 }
427 }
428
429 json_object_object_add(json_binding, "input_codes", input_codes);
430 json_object_object_add(json_binding, "input_code", json_object_new_int(input_code));
431 json_object_object_add(json_binding, "symbols", symbols);
432 json_object_object_add(json_binding, "symbol", symbol);
433 json_object_object_add(json_binding, "input_type", binding->type == BINDING_MOUSE ?
434 json_object_new_string("mouse") : json_object_new_string("keyboard"));
435
436 json_object *json = json_object_new_object();
437 json_object_object_add(json, "change", json_object_new_string("run"));
438 json_object_object_add(json, "binding", json_binding);
439 const char *json_string = json_object_to_json_string(json);
440 ipc_send_event(json_string, IPC_EVENT_BINDING);
441 json_object_put(json);
442}
443
444static void ipc_event_tick(const char *payload) {
445 if (!ipc_has_event_listeners(IPC_EVENT_TICK)) {
446 return;
447 }
448 wlr_log(WLR_DEBUG, "Sending tick event");
449
450 json_object *json = json_object_new_object();
451 json_object_object_add(json, "first", json_object_new_boolean(false));
452 json_object_object_add(json, "payload", json_object_new_string(payload));
453
454 const char *json_string = json_object_to_json_string(json);
455 ipc_send_event(json_string, IPC_EVENT_TICK);
456 json_object_put(json);
457}
458
356int ipc_client_handle_writable(int client_fd, uint32_t mask, void *data) { 459int ipc_client_handle_writable(int client_fd, uint32_t mask, void *data) {
357 struct ipc_client *client = data; 460 struct ipc_client *client = data;
358 461
@@ -494,6 +597,13 @@ void ipc_client_handle_command(struct ipc_client *client) {
494 goto exit_cleanup; 597 goto exit_cleanup;
495 } 598 }
496 599
600 case IPC_SEND_TICK:
601 {
602 ipc_event_tick(buf);
603 ipc_send_reply(client, "{\"success\": true}", 17);
604 goto exit_cleanup;
605 }
606
497 case IPC_GET_OUTPUTS: 607 case IPC_GET_OUTPUTS:
498 { 608 {
499 json_object *outputs = json_object_new_array(); 609 json_object *outputs = json_object_new_array();
@@ -540,6 +650,7 @@ void ipc_client_handle_command(struct ipc_client *client) {
540 goto exit_cleanup; 650 goto exit_cleanup;
541 } 651 }
542 652
653 bool is_tick = false;
543 // parse requested event types 654 // parse requested event types
544 for (size_t i = 0; i < json_object_array_length(request); i++) { 655 for (size_t i = 0; i < json_object_array_length(request); i++) {
545 const char *event_type = json_object_get_string(json_object_array_get_idx(request, i)); 656 const char *event_type = json_object_get_string(json_object_array_get_idx(request, i));
@@ -549,12 +660,15 @@ void ipc_client_handle_command(struct ipc_client *client) {
549 client->subscribed_events |= event_mask(IPC_EVENT_BARCONFIG_UPDATE); 660 client->subscribed_events |= event_mask(IPC_EVENT_BARCONFIG_UPDATE);
550 } else if (strcmp(event_type, "mode") == 0) { 661 } else if (strcmp(event_type, "mode") == 0) {
551 client->subscribed_events |= event_mask(IPC_EVENT_MODE); 662 client->subscribed_events |= event_mask(IPC_EVENT_MODE);
663 } else if (strcmp(event_type, "shutdown") == 0) {
664 client->subscribed_events |= event_mask(IPC_EVENT_SHUTDOWN);
552 } else if (strcmp(event_type, "window") == 0) { 665 } else if (strcmp(event_type, "window") == 0) {
553 client->subscribed_events |= event_mask(IPC_EVENT_WINDOW); 666 client->subscribed_events |= event_mask(IPC_EVENT_WINDOW);
554 } else if (strcmp(event_type, "modifier") == 0) {
555 client->subscribed_events |= event_mask(IPC_EVENT_MODIFIER);
556 } else if (strcmp(event_type, "binding") == 0) { 667 } else if (strcmp(event_type, "binding") == 0) {
557 client->subscribed_events |= event_mask(IPC_EVENT_BINDING); 668 client->subscribed_events |= event_mask(IPC_EVENT_BINDING);
669 } else if (strcmp(event_type, "tick") == 0) {
670 client->subscribed_events |= event_mask(IPC_EVENT_TICK);
671 is_tick = true;
558 } else { 672 } else {
559 client_valid = 673 client_valid =
560 ipc_send_reply(client, "{\"success\": false}", 18); 674 ipc_send_reply(client, "{\"success\": false}", 18);
@@ -566,6 +680,10 @@ void ipc_client_handle_command(struct ipc_client *client) {
566 680
567 json_object_put(request); 681 json_object_put(request);
568 client_valid = ipc_send_reply(client, "{\"success\": true}", 17); 682 client_valid = ipc_send_reply(client, "{\"success\": true}", 17);
683 if (is_tick) {
684 client->current_command = IPC_EVENT_TICK;
685 ipc_send_reply(client, "{\"first\": true, \"payload\": \"\"}", 30);
686 }
569 goto exit_cleanup; 687 goto exit_cleanup;
570 } 688 }
571 689
diff --git a/sway/main.c b/sway/main.c
index a20f1dac..477ffa5a 100644
--- a/sway/main.c
+++ b/sway/main.c
@@ -36,6 +36,7 @@ struct sway_server server;
36void sway_terminate(int exit_code) { 36void sway_terminate(int exit_code) {
37 terminate_request = true; 37 terminate_request = true;
38 exit_value = exit_code; 38 exit_value = exit_code;
39 ipc_event_shutdown("exit");
39 wl_display_terminate(server.wl_display); 40 wl_display_terminate(server.wl_display);
40} 41}
41 42
diff --git a/sway/tree/container.c b/sway/tree/container.c
index 4e85021d..b980c5e9 100644
--- a/sway/tree/container.c
+++ b/sway/tree/container.c
@@ -62,8 +62,10 @@ void container_create_notify(struct sway_container *container) {
62 // TODO send ipc event type based on the container type 62 // TODO send ipc event type based on the container type
63 wl_signal_emit(&root_container.sway_root->events.new_container, container); 63 wl_signal_emit(&root_container.sway_root->events.new_container, container);
64 64
65 if (container->type == C_VIEW || container->type == C_CONTAINER) { 65 if (container->type == C_VIEW) {
66 ipc_event_window(container, "new"); 66 ipc_event_window(container, "new");
67 } else if (container->type == C_WORKSPACE) {
68 ipc_event_workspace(NULL, container, "init");
67 } 69 }
68} 70}
69 71
@@ -281,7 +283,7 @@ static struct sway_container *container_output_destroy(
281 container_remove_child(workspace); 283 container_remove_child(workspace);
282 if (!workspace_is_empty(workspace)) { 284 if (!workspace_is_empty(workspace)) {
283 container_add_child(new_output, workspace); 285 container_add_child(new_output, workspace);
284 ipc_event_workspace(workspace, NULL, "move"); 286 ipc_event_workspace(NULL, workspace, "move");
285 } else { 287 } else {
286 container_destroy(workspace); 288 container_destroy(workspace);
287 } 289 }
@@ -319,7 +321,13 @@ static struct sway_container *container_destroy_noreaping(
319 } 321 }
320 322
321 wl_signal_emit(&con->events.destroy, con); 323 wl_signal_emit(&con->events.destroy, con);
322 ipc_event_window(con, "close"); 324
325 // emit IPC event
326 if (con->type == C_VIEW) {
327 ipc_event_window(con, "close");
328 } else if (con->type == C_WORKSPACE) {
329 ipc_event_workspace(NULL, con, "empty");
330 }
323 331
324 // The below functions move their children to somewhere else. 332 // The below functions move their children to somewhere else.
325 if (con->type == C_OUTPUT) { 333 if (con->type == C_OUTPUT) {
@@ -561,10 +569,15 @@ static struct sway_container *container_at_view(struct sway_container *swayc,
561 *sx = _sx; 569 *sx = _sx;
562 *sy = _sy; 570 *sy = _sy;
563 *surface = _surface; 571 *surface = _surface;
572 return swayc;
564 } 573 }
565 return swayc; 574 return NULL;
566} 575}
567 576
577static struct sway_container *tiling_container_at(
578 struct sway_container *con, double lx, double ly,
579 struct wlr_surface **surface, double *sx, double *sy);
580
568/** 581/**
569 * container_at for a container with layout L_TABBED. 582 * container_at for a container with layout L_TABBED.
570 */ 583 */
@@ -591,7 +604,7 @@ static struct sway_container *container_at_tabbed(struct sway_container *parent,
591 // Surfaces 604 // Surfaces
592 struct sway_container *current = seat_get_active_child(seat, parent); 605 struct sway_container *current = seat_get_active_child(seat, parent);
593 606
594 return container_at(current, lx, ly, surface, sx, sy); 607 return tiling_container_at(current, lx, ly, surface, sx, sy);
595} 608}
596 609
597/** 610/**
@@ -616,7 +629,7 @@ static struct sway_container *container_at_stacked(
616 // Surfaces 629 // Surfaces
617 struct sway_container *current = seat_get_active_child(seat, parent); 630 struct sway_container *current = seat_get_active_child(seat, parent);
618 631
619 return container_at(current, lx, ly, surface, sx, sy); 632 return tiling_container_at(current, lx, ly, surface, sx, sy);
620} 633}
621 634
622/** 635/**
@@ -634,45 +647,13 @@ static struct sway_container *container_at_linear(struct sway_container *parent,
634 .height = child->height, 647 .height = child->height,
635 }; 648 };
636 if (wlr_box_contains_point(&box, lx, ly)) { 649 if (wlr_box_contains_point(&box, lx, ly)) {
637 return container_at(child, lx, ly, surface, sx, sy); 650 return tiling_container_at(child, lx, ly, surface, sx, sy);
638 } 651 }
639 } 652 }
640 return NULL; 653 return NULL;
641} 654}
642 655
643struct sway_container *container_at(struct sway_container *parent, 656static struct sway_container *floating_container_at(double lx, double ly,
644 double lx, double ly,
645 struct wlr_surface **surface, double *sx, double *sy) {
646 if (!sway_assert(parent->type >= C_WORKSPACE,
647 "Expected workspace or deeper")) {
648 return NULL;
649 }
650 if (parent->type == C_VIEW) {
651 return container_at_view(parent, lx, ly, surface, sx, sy);
652 }
653 if (!parent->children->length) {
654 return NULL;
655 }
656
657 switch (parent->layout) {
658 case L_HORIZ:
659 case L_VERT:
660 return container_at_linear(parent, lx, ly, surface, sx, sy);
661 case L_TABBED:
662 return container_at_tabbed(parent, lx, ly, surface, sx, sy);
663 case L_STACKED:
664 return container_at_stacked(parent, lx, ly, surface, sx, sy);
665 case L_FLOATING:
666 sway_assert(false, "Didn't expect to see floating here");
667 return NULL;
668 case L_NONE:
669 return NULL;
670 }
671
672 return NULL;
673}
674
675struct sway_container *floating_container_at(double lx, double ly,
676 struct wlr_surface **surface, double *sx, double *sy) { 657 struct wlr_surface **surface, double *sx, double *sy) {
677 for (int i = 0; i < root_container.children->length; ++i) { 658 for (int i = 0; i < root_container.children->length; ++i) {
678 struct sway_container *output = root_container.children->items[i]; 659 struct sway_container *output = root_container.children->items[i];
@@ -694,7 +675,8 @@ struct sway_container *floating_container_at(double lx, double ly,
694 .height = floater->height, 675 .height = floater->height,
695 }; 676 };
696 if (wlr_box_contains_point(&box, lx, ly)) { 677 if (wlr_box_contains_point(&box, lx, ly)) {
697 return container_at(floater, lx, ly, surface, sx, sy); 678 return tiling_container_at(floater, lx, ly,
679 surface, sx, sy);
698 } 680 }
699 } 681 }
700 } 682 }
@@ -702,6 +684,90 @@ struct sway_container *floating_container_at(double lx, double ly,
702 return NULL; 684 return NULL;
703} 685}
704 686
687static struct sway_container *tiling_container_at(
688 struct sway_container *con, double lx, double ly,
689 struct wlr_surface **surface, double *sx, double *sy) {
690 if (con->type == C_VIEW) {
691 return container_at_view(con, lx, ly, surface, sx, sy);
692 }
693 if (!con->children->length) {
694 return NULL;
695 }
696
697 switch (con->layout) {
698 case L_HORIZ:
699 case L_VERT:
700 return container_at_linear(con, lx, ly, surface, sx, sy);
701 case L_TABBED:
702 return container_at_tabbed(con, lx, ly, surface, sx, sy);
703 case L_STACKED:
704 return container_at_stacked(con, lx, ly, surface, sx, sy);
705 case L_FLOATING:
706 sway_assert(false, "Didn't expect to see floating here");
707 return NULL;
708 case L_NONE:
709 return NULL;
710 }
711 return NULL;
712}
713
714static bool surface_is_popup(struct wlr_surface *surface) {
715 if (wlr_surface_is_xdg_surface(surface)) {
716 struct wlr_xdg_surface *xdg_surface =
717 wlr_xdg_surface_from_wlr_surface(surface);
718 while (xdg_surface) {
719 if (xdg_surface->role == WLR_XDG_SURFACE_ROLE_POPUP) {
720 return true;
721 }
722 xdg_surface = xdg_surface->toplevel->parent;
723 }
724 return false;
725 }
726
727 if (wlr_surface_is_xdg_surface_v6(surface)) {
728 struct wlr_xdg_surface_v6 *xdg_surface_v6 =
729 wlr_xdg_surface_v6_from_wlr_surface(surface);
730 while (xdg_surface_v6) {
731 if (xdg_surface_v6->role == WLR_XDG_SURFACE_V6_ROLE_POPUP) {
732 return true;
733 }
734 xdg_surface_v6 = xdg_surface_v6->toplevel->parent;
735 }
736 return false;
737 }
738
739 return false;
740}
741
742struct sway_container *container_at(struct sway_container *workspace,
743 double lx, double ly,
744 struct wlr_surface **surface, double *sx, double *sy) {
745 if (!sway_assert(workspace->type == C_WORKSPACE, "Expected a workspace")) {
746 return NULL;
747 }
748 struct sway_container *c;
749 // Focused view's popups
750 struct sway_seat *seat = input_manager_current_seat(input_manager);
751 struct sway_container *focus =
752 seat_get_focus_inactive(seat, &root_container);
753 if (focus && focus->type == C_VIEW) {
754 container_at_view(focus, lx, ly, surface, sx, sy);
755 if (*surface && surface_is_popup(*surface)) {
756 return focus;
757 }
758 *surface = NULL;
759 }
760 // Floating
761 if ((c = floating_container_at(lx, ly, surface, sx, sy))) {
762 return c;
763 }
764 // Tiling
765 if ((c = tiling_container_at(workspace, lx, ly, surface, sx, sy))) {
766 return c;
767 }
768 return NULL;
769}
770
705void container_for_each_descendant_dfs(struct sway_container *container, 771void container_for_each_descendant_dfs(struct sway_container *container,
706 void (*f)(struct sway_container *container, void *data), 772 void (*f)(struct sway_container *container, void *data),
707 void *data) { 773 void *data) {
diff --git a/sway/tree/layout.c b/sway/tree/layout.c
index a0764a54..1f82e534 100644
--- a/sway/tree/layout.c
+++ b/sway/tree/layout.c
@@ -217,7 +217,9 @@ void container_move_to(struct sway_container *container,
217 container_sort_workspaces(new_parent); 217 container_sort_workspaces(new_parent);
218 seat_set_focus(seat, new_parent); 218 seat_set_focus(seat, new_parent);
219 workspace_output_raise_priority(container, old_parent, new_parent); 219 workspace_output_raise_priority(container, old_parent, new_parent);
220 ipc_event_workspace(container, NULL, "move"); 220 ipc_event_workspace(NULL, container, "move");
221 } else if (container->type == C_VIEW) {
222 ipc_event_window(container, "move");
221 } 223 }
222 container_notify_subtree_changed(old_parent); 224 container_notify_subtree_changed(old_parent);
223 container_notify_subtree_changed(new_parent); 225 container_notify_subtree_changed(new_parent);
@@ -578,6 +580,10 @@ void container_move(struct sway_container *container,
578 container_notify_subtree_changed(old_parent); 580 container_notify_subtree_changed(old_parent);
579 container_notify_subtree_changed(container->parent); 581 container_notify_subtree_changed(container->parent);
580 582
583 if (container->type == C_VIEW) {
584 ipc_event_window(container, "move");
585 }
586
581 if (old_parent) { 587 if (old_parent) {
582 seat_set_focus(config->handler_context.seat, old_parent); 588 seat_set_focus(config->handler_context.seat, old_parent);
583 seat_set_focus(config->handler_context.seat, container); 589 seat_set_focus(config->handler_context.seat, container);
@@ -592,7 +598,7 @@ void container_move(struct sway_container *container,
592 next_ws = container_parent(next_ws, C_WORKSPACE); 598 next_ws = container_parent(next_ws, C_WORKSPACE);
593 } 599 }
594 if (last_ws && next_ws && last_ws != next_ws) { 600 if (last_ws && next_ws && last_ws != next_ws) {
595 ipc_event_workspace(last_ws, container, "focus"); 601 ipc_event_workspace(last_ws, next_ws, "focus");
596 workspace_detect_urgent(last_ws); 602 workspace_detect_urgent(last_ws);
597 workspace_detect_urgent(next_ws); 603 workspace_detect_urgent(next_ws);
598 } 604 }
@@ -995,13 +1001,13 @@ static void swap_focus(struct sway_container *con1,
995 if (focus == con1 && (con2->parent->layout == L_TABBED 1001 if (focus == con1 && (con2->parent->layout == L_TABBED
996 || con2->parent->layout == L_STACKED)) { 1002 || con2->parent->layout == L_STACKED)) {
997 if (workspace_is_visible(ws2)) { 1003 if (workspace_is_visible(ws2)) {
998 seat_set_focus_warp(seat, con2, false); 1004 seat_set_focus_warp(seat, con2, false, true);
999 } 1005 }
1000 seat_set_focus(seat, ws1 != ws2 ? con2 : con1); 1006 seat_set_focus(seat, ws1 != ws2 ? con2 : con1);
1001 } else if (focus == con2 && (con1->parent->layout == L_TABBED 1007 } else if (focus == con2 && (con1->parent->layout == L_TABBED
1002 || con1->parent->layout == L_STACKED)) { 1008 || con1->parent->layout == L_STACKED)) {
1003 if (workspace_is_visible(ws1)) { 1009 if (workspace_is_visible(ws1)) {
1004 seat_set_focus_warp(seat, con1, false); 1010 seat_set_focus_warp(seat, con1, false, true);
1005 } 1011 }
1006 seat_set_focus(seat, ws1 != ws2 ? con1 : con2); 1012 seat_set_focus(seat, ws1 != ws2 ? con1 : con2);
1007 } else if (ws1 != ws2) { 1013 } else if (ws1 != ws2) {
diff --git a/sway/tree/output.c b/sway/tree/output.c
index da535c18..31e3bf9b 100644
--- a/sway/tree/output.c
+++ b/sway/tree/output.c
@@ -22,7 +22,7 @@ static void restore_workspaces(struct sway_container *output) {
22 if (highest == output) { 22 if (highest == output) {
23 container_remove_child(ws); 23 container_remove_child(ws);
24 container_add_child(output, ws); 24 container_add_child(output, ws);
25 ipc_event_workspace(ws, NULL, "move"); 25 ipc_event_workspace(NULL, ws, "move");
26 j--; 26 j--;
27 } 27 }
28 } 28 }
diff --git a/sway/tree/view.c b/sway/tree/view.c
index 0dbd3812..051b93ce 100644
--- a/sway/tree/view.c
+++ b/sway/tree/view.c
@@ -303,6 +303,12 @@ void view_close(struct sway_view *view) {
303 } 303 }
304} 304}
305 305
306void view_close_popups(struct sway_view *view) {
307 if (view->impl->close_popups) {
308 view->impl->close_popups(view);
309 }
310}
311
306void view_damage_from(struct sway_view *view) { 312void view_damage_from(struct sway_view *view) {
307 for (int i = 0; i < root_container.children->length; ++i) { 313 for (int i = 0; i < root_container.children->length; ++i) {
308 struct sway_container *cont = root_container.children->items[i]; 314 struct sway_container *cont = root_container.children->items[i];
@@ -333,6 +339,16 @@ void view_for_each_surface(struct sway_view *view,
333 } 339 }
334} 340}
335 341
342void view_for_each_popup(struct sway_view *view,
343 wlr_surface_iterator_func_t iterator, void *user_data) {
344 if (!view->surface) {
345 return;
346 }
347 if (view->impl->for_each_popup) {
348 view->impl->for_each_popup(view, iterator, user_data);
349 }
350}
351
336static void view_subsurface_create(struct sway_view *view, 352static void view_subsurface_create(struct sway_view *view,
337 struct wlr_subsurface *subsurface); 353 struct wlr_subsurface *subsurface);
338 354
@@ -865,6 +881,8 @@ void view_update_title(struct sway_view *view, bool force) {
865 881
866 // Update title after the global font height is updated 882 // Update title after the global font height is updated
867 container_update_title_textures(view->swayc); 883 container_update_title_textures(view->swayc);
884
885 ipc_event_window(view->swayc, "title");
868} 886}
869 887
870static bool find_by_mark_iterator(struct sway_container *con, 888static bool find_by_mark_iterator(struct sway_container *con,
@@ -887,6 +905,7 @@ bool view_find_and_unmark(char *mark) {
887 free(view_mark); 905 free(view_mark);
888 list_del(view->marks, i); 906 list_del(view->marks, i);
889 view_update_marks_textures(view); 907 view_update_marks_textures(view);
908 ipc_event_window(container, "mark");
890 return true; 909 return true;
891 } 910 }
892 } 911 }
@@ -894,11 +913,10 @@ bool view_find_and_unmark(char *mark) {
894} 913}
895 914
896void view_clear_marks(struct sway_view *view) { 915void view_clear_marks(struct sway_view *view) {
897 for (int i = 0; i < view->marks->length; ++i) { 916 while (view->marks->length) {
898 free(view->marks->items[i]); 917 list_del(view->marks, 0);
918 ipc_event_window(view->swayc, "mark");
899 } 919 }
900 list_free(view->marks);
901 view->marks = create_list();
902} 920}
903 921
904bool view_has_mark(struct sway_view *view, char *mark) { 922bool view_has_mark(struct sway_view *view, char *mark) {
@@ -911,6 +929,11 @@ bool view_has_mark(struct sway_view *view, char *mark) {
911 return false; 929 return false;
912} 930}
913 931
932void view_add_mark(struct sway_view *view, char *mark) {
933 list_add(view->marks, strdup(mark));
934 ipc_event_window(view->swayc, "mark");
935}
936
914static void update_marks_texture(struct sway_view *view, 937static void update_marks_texture(struct sway_view *view,
915 struct wlr_texture **texture, struct border_colors *class) { 938 struct wlr_texture **texture, struct border_colors *class) {
916 struct sway_container *output = container_parent(view->swayc, C_OUTPUT); 939 struct sway_container *output = container_parent(view->swayc, C_OUTPUT);
diff --git a/swaymsg/main.c b/swaymsg/main.c
index c4141ca5..3767daf3 100644
--- a/swaymsg/main.c
+++ b/swaymsg/main.c
@@ -250,12 +250,16 @@ static void pretty_print(int type, json_object *resp) {
250 if (type != IPC_COMMAND && type != IPC_GET_WORKSPACES && 250 if (type != IPC_COMMAND && type != IPC_GET_WORKSPACES &&
251 type != IPC_GET_INPUTS && type != IPC_GET_OUTPUTS && 251 type != IPC_GET_INPUTS && type != IPC_GET_OUTPUTS &&
252 type != IPC_GET_VERSION && type != IPC_GET_SEATS && 252 type != IPC_GET_VERSION && type != IPC_GET_SEATS &&
253 type != IPC_GET_CONFIG) { 253 type != IPC_GET_CONFIG && type != IPC_SEND_TICK) {
254 printf("%s\n", json_object_to_json_string_ext(resp, 254 printf("%s\n", json_object_to_json_string_ext(resp,
255 JSON_C_TO_STRING_PRETTY | JSON_C_TO_STRING_SPACED)); 255 JSON_C_TO_STRING_PRETTY | JSON_C_TO_STRING_SPACED));
256 return; 256 return;
257 } 257 }
258 258
259 if (type == IPC_SEND_TICK) {
260 return;
261 }
262
259 if (type == IPC_GET_VERSION) { 263 if (type == IPC_GET_VERSION) {
260 pretty_print_version(resp); 264 pretty_print_version(resp);
261 return; 265 return;
@@ -384,6 +388,8 @@ int main(int argc, char **argv) {
384 type = IPC_GET_BINDING_MODES; 388 type = IPC_GET_BINDING_MODES;
385 } else if (strcasecmp(cmdtype, "get_config") == 0) { 389 } else if (strcasecmp(cmdtype, "get_config") == 0) {
386 type = IPC_GET_CONFIG; 390 type = IPC_GET_CONFIG;
391 } else if (strcasecmp(cmdtype, "send_tick") == 0) {
392 type = IPC_SEND_TICK;
387 } else { 393 } else {
388 sway_abort("Unknown message type %s", cmdtype); 394 sway_abort("Unknown message type %s", cmdtype);
389 } 395 }
diff --git a/swaymsg/swaymsg.1.scd b/swaymsg/swaymsg.1.scd
index a6e279da..8cf1b222 100644
--- a/swaymsg/swaymsg.1.scd
+++ b/swaymsg/swaymsg.1.scd
@@ -64,3 +64,6 @@ _swaymsg_ [options...] [message]
64 64
65*get\_config* 65*get\_config*
66 Gets a JSON-encoded copy of the current configuration. 66 Gets a JSON-encoded copy of the current configuration.
67
68*send\_tick*
69 Sends a tick event to all subscribed clients.