From de86d65627e96cffe77f4abf11c4a0b982326ff9 Mon Sep 17 00:00:00 2001
From: Ryan Dwyer <ryandwyer1@gmail.com>
Date: Tue, 31 Jul 2018 18:41:30 +1000
Subject: Fix popups

Fixes the render and container_at order for popups.

Fixes #2210

For rendering:

* render_view_surfaces has been renamed to render_view_toplevels
* render_view_toplevels now uses output_surface_for_each_surface (which
is now public), as that function uses wlr_surface_for_each_surface which
doesn't descend into popups
* Views now have a for_each_popup iterator, which is used by the
renderer to render the focused view's popups
* When rendering a popup, toplevels (xdg subsurfaces) of that popup are
also rendered

For sending frame done, the logic has been updated to match the
rendering logic:

* send_frame_done_container no longer descends into popups
* for_each_popup is used to send frame done to the focused view's popups
and their child toplevels

For container_at:

* floating_container_at is now static, which means it had to be moved
higher in the file.
* container_at now considers popups for the focused view before checking
containers.
* tiling_container_at has been introduced, so that it doesn't call
container_at recursively (it would check popups recursively if it did)
---
 include/sway/output.h         |  8 ++++++++
 include/sway/tree/container.h | 11 ++---------
 include/sway/tree/view.h      | 11 +++++++++++
 3 files changed, 21 insertions(+), 9 deletions(-)

(limited to 'include')

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);
 void output_render(struct sway_output *output, struct timespec *when,
 	pixman_region32_t *damage);
 
+void output_surface_for_each_surface(struct sway_output *output,
+		struct wlr_surface *surface, double ox, double oy,
+		sway_surface_iterator_func_t iterator, void *user_data);
+
 void output_view_for_each_surface(struct sway_output *output,
 	struct sway_view *view, sway_surface_iterator_func_t iterator,
 	void *user_data);
 
+void output_view_for_each_popup(struct sway_output *output,
+		struct sway_view *view, sway_surface_iterator_func_t iterator,
+		void *user_data);
+
 void output_layer_for_each_surface(struct sway_output *output,
 	struct wl_list *layer_surfaces, sway_surface_iterator_func_t iterator,
 	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,17 +230,10 @@ struct sway_container *container_parent(struct sway_container *container,
  * surface-local coordinates of the given layout coordinates if the container
  * is a view and the view contains a surface at those coordinates.
  */
-struct sway_container *container_at(struct sway_container *container,
-		double ox, double oy, struct wlr_surface **surface,
+struct sway_container *container_at(struct sway_container *workspace,
+		double lx, double ly, struct wlr_surface **surface,
 		double *sx, double *sy);
 
-/**
- * Same as container_at, but only checks floating views and expects coordinates
- * to be layout coordinates, as that's what floating views use.
- */
-struct sway_container *floating_container_at(double lx, double ly,
-		struct wlr_surface **surface, double *sx, double *sy);
-
 /**
  * Apply the function for each descendant of the container breadth first.
  */
diff --git a/include/sway/tree/view.h b/include/sway/tree/view.h
index 0152ed55..9f6d36fe 100644
--- a/include/sway/tree/view.h
+++ b/include/sway/tree/view.h
@@ -47,6 +47,8 @@ struct sway_view_impl {
 	bool (*has_client_side_decorations)(struct sway_view *view);
 	void (*for_each_surface)(struct sway_view *view,
 		wlr_surface_iterator_func_t iterator, void *user_data);
+	void (*for_each_popup)(struct sway_view *view,
+		wlr_surface_iterator_func_t iterator, void *user_data);
 	void (*close)(struct sway_view *view);
 	void (*destroy)(struct sway_view *view);
 };
@@ -248,9 +250,18 @@ void view_close(struct sway_view *view);
 
 void view_damage_from(struct sway_view *view);
 
+/**
+ * Iterate all surfaces of a view (toplevels + popups).
+ */
 void view_for_each_surface(struct sway_view *view,
 	wlr_surface_iterator_func_t iterator, void *user_data);
 
+/**
+ * Iterate all popups recursively.
+ */
+void view_for_each_popup(struct sway_view *view,
+	wlr_surface_iterator_func_t iterator, void *user_data);
+
 // view implementation
 
 void view_init(struct sway_view *view, enum sway_view_type type,
-- 
cgit v1.2.3


From 7a59508da467a3b793e355e28ae67ce04633761c Mon Sep 17 00:00:00 2001
From: Ryan Dwyer <ryandwyer1@gmail.com>
Date: Tue, 31 Jul 2018 19:58:34 +1000
Subject: Close popups when changing focus

Also reverts the send frame done changes from the previous commit.
---
 include/sway/tree/view.h    |  3 +++
 sway/desktop/output.c       | 33 ++-------------------------------
 sway/desktop/xdg_shell.c    | 13 +++++++++++++
 sway/desktop/xdg_shell_v6.c | 13 +++++++++++++
 sway/input/seat.c           |  7 +++++++
 sway/tree/view.c            |  6 ++++++
 6 files changed, 44 insertions(+), 31 deletions(-)

(limited to 'include')

diff --git a/include/sway/tree/view.h b/include/sway/tree/view.h
index 9f6d36fe..e722ca5e 100644
--- a/include/sway/tree/view.h
+++ b/include/sway/tree/view.h
@@ -50,6 +50,7 @@ struct sway_view_impl {
 	void (*for_each_popup)(struct sway_view *view,
 		wlr_surface_iterator_func_t iterator, void *user_data);
 	void (*close)(struct sway_view *view);
+	void (*close_popups)(struct sway_view *view);
 	void (*destroy)(struct sway_view *view);
 };
 
@@ -248,6 +249,8 @@ void view_set_tiled(struct sway_view *view, bool tiled);
 
 void view_close(struct sway_view *view);
 
+void view_close_popups(struct sway_view *view);
+
 void view_damage_from(struct sway_view *view);
 
 /**
diff --git a/sway/desktop/output.c b/sway/desktop/output.c
index 4c9d978c..66747a3f 100644
--- a/sway/desktop/output.c
+++ b/sway/desktop/output.c
@@ -312,9 +312,8 @@ static void send_frame_done_container_iterator(struct sway_container *con,
 		return;
 	}
 
-	// Toplevels only
-	output_surface_for_each_surface(data->output, con->sway_view->surface,
-		con->x, con->y, send_frame_done_iterator, data->when);
+	output_view_for_each_surface(data->output, con->sway_view,
+		send_frame_done_iterator, data->when);
 }
 
 static void send_frame_done_container(struct sway_output *output,
@@ -327,27 +326,6 @@ static void send_frame_done_container(struct sway_output *output,
 		send_frame_done_container_iterator, &data);
 }
 
-static void send_frame_done_popup_iterator(struct sway_output *output,
-		struct wlr_surface *surface, struct wlr_box *box, float rotation,
-		void *data) {
-	// Send frame done to this popup's surface
-	send_frame_done_iterator(output, surface, box, rotation, data);
-
-	// Send frame done to this popup's child toplevels
-	output_surface_for_each_surface(output, surface, box->x, box->y,
-			send_frame_done_iterator, data);
-}
-
-static void send_frame_done_popups(struct sway_output *output,
-		struct sway_view *view, struct timespec *when) {
-	struct send_frame_done_data data = {
-		.output = output,
-		.when = when,
-	};
-	output_view_for_each_popup(output, view,
-			send_frame_done_popup_iterator, &data);
-}
-
 static void send_frame_done(struct sway_output *output, struct timespec *when) {
 	if (output_has_opaque_overlay_layer_surface(output)) {
 		goto send_frame_overlay;
@@ -385,13 +363,6 @@ static void send_frame_done(struct sway_output *output, struct timespec *when) {
 			&output->layers[ZWLR_LAYER_SHELL_V1_LAYER_TOP], when);
 	}
 
-	struct sway_seat *seat = input_manager_current_seat(input_manager);
-	struct sway_container *focus = seat_get_focus(seat);
-	if (focus && focus->type == C_VIEW) {
-		send_frame_done_popups(output, focus->sway_view, when);
-	}
-
-
 send_frame_overlay:
 	send_frame_done_layer(output,
 		&output->layers[ZWLR_LAYER_SHELL_V1_LAYER_OVERLAY], when);
diff --git a/sway/desktop/xdg_shell.c b/sway/desktop/xdg_shell.c
index 9f94bd74..b364663d 100644
--- a/sway/desktop/xdg_shell.c
+++ b/sway/desktop/xdg_shell.c
@@ -197,6 +197,18 @@ static void _close(struct sway_view *view) {
 	}
 }
 
+static void close_popups_iterator(struct wlr_surface *surface,
+		int sx, int sy, void *data) {
+	struct wlr_xdg_surface *xdg_surface =
+		wlr_xdg_surface_from_wlr_surface(surface);
+	wlr_xdg_surface_send_close(xdg_surface);
+}
+
+static void close_popups(struct sway_view *view) {
+	wlr_xdg_surface_for_each_popup(view->wlr_xdg_surface,
+			close_popups_iterator, NULL);
+}
+
 static void destroy(struct sway_view *view) {
 	struct sway_xdg_shell_view *xdg_shell_view =
 		xdg_shell_view_from_view(view);
@@ -217,6 +229,7 @@ static const struct sway_view_impl view_impl = {
 	.for_each_surface = for_each_surface,
 	.for_each_popup = for_each_popup,
 	.close = _close,
+	.close_popups = close_popups,
 	.destroy = destroy,
 };
 
diff --git a/sway/desktop/xdg_shell_v6.c b/sway/desktop/xdg_shell_v6.c
index 4502c386..ffea03ad 100644
--- a/sway/desktop/xdg_shell_v6.c
+++ b/sway/desktop/xdg_shell_v6.c
@@ -194,6 +194,18 @@ static void _close(struct sway_view *view) {
 	}
 }
 
+static void close_popups_iterator(struct wlr_surface *surface,
+		int sx, int sy, void *data) {
+	struct wlr_xdg_surface_v6 *xdg_surface_v6 =
+		wlr_xdg_surface_v6_from_wlr_surface(surface);
+	wlr_xdg_surface_v6_send_close(xdg_surface_v6);
+}
+
+static void close_popups(struct sway_view *view) {
+	wlr_xdg_surface_v6_for_each_popup(view->wlr_xdg_surface_v6,
+			close_popups_iterator, NULL);
+}
+
 static void destroy(struct sway_view *view) {
 	struct sway_xdg_shell_v6_view *xdg_shell_v6_view =
 		xdg_shell_v6_view_from_view(view);
@@ -214,6 +226,7 @@ static const struct sway_view_impl view_impl = {
 	.for_each_surface = for_each_surface,
 	.for_each_popup = for_each_popup,
 	.close = _close,
+	.close_popups = close_popups,
 	.destroy = destroy,
 };
 
diff --git a/sway/input/seat.c b/sway/input/seat.c
index 53a92989..8ed0dce2 100644
--- a/sway/input/seat.c
+++ b/sway/input/seat.c
@@ -737,6 +737,13 @@ void seat_set_focus_warp(struct sway_seat *seat,
 		}
 	}
 
+	// Close any popups on the old focus
+	if (last_focus && last_focus != container) {
+		if (last_focus->type == C_VIEW) {
+			view_close_popups(last_focus->sway_view);
+		}
+	}
+
 	if (last_focus) {
 		if (last_workspace) {
 			ipc_event_workspace(last_workspace, container, "focus");
diff --git a/sway/tree/view.c b/sway/tree/view.c
index c1207821..5d9b625f 100644
--- a/sway/tree/view.c
+++ b/sway/tree/view.c
@@ -302,6 +302,12 @@ void view_close(struct sway_view *view) {
 	}
 }
 
+void view_close_popups(struct sway_view *view) {
+	if (view->impl->close_popups) {
+		view->impl->close_popups(view);
+	}
+}
+
 void view_damage_from(struct sway_view *view) {
 	for (int i = 0; i < root_container.children->length; ++i) {
 		struct sway_container *cont = root_container.children->items[i];
-- 
cgit v1.2.3


From e8b179e313632c150ff7775f4f740296465cb58a Mon Sep 17 00:00:00 2001
From: Ian Fan <ianfan0@gmail.com>
Date: Fri, 13 Jul 2018 16:58:45 +0100
Subject: ipc: add shutdown event

---
 include/ipc.h             |  5 +++--
 include/sway/ipc-server.h |  1 +
 sway/ipc-server.c         | 16 ++++++++++++++++
 sway/main.c               |  1 +
 4 files changed, 21 insertions(+), 2 deletions(-)

(limited to 'include')

diff --git a/include/ipc.h b/include/ipc.h
index 0010718b..ffc57d1b 100644
--- a/include/ipc.h
+++ b/include/ipc.h
@@ -27,8 +27,9 @@ enum ipc_command_type {
 	IPC_EVENT_WINDOW = ((1<<31) | 3),
 	IPC_EVENT_BARCONFIG_UPDATE = ((1<<31) | 4),
 	IPC_EVENT_BINDING = ((1<<31) | 5),
-	IPC_EVENT_MODIFIER = ((1<<31) | 6),
-	IPC_EVENT_INPUT = ((1<<31) | 7),
+	IPC_EVENT_SHUTDOWN = ((1<<31) | 6),
+	IPC_EVENT_MODIFIER = ((1<<31) | 16),
+	IPC_EVENT_INPUT = ((1<<31) | 17),
 };
 
 #endif
diff --git a/include/sway/ipc-server.h b/include/sway/ipc-server.h
index 6469f097..a11735cf 100644
--- a/include/sway/ipc-server.h
+++ b/include/sway/ipc-server.h
@@ -16,5 +16,6 @@ void ipc_event_workspace(struct sway_container *old,
 void ipc_event_window(struct sway_container *window, const char *change);
 void ipc_event_barconfig_update(struct bar_config *bar);
 void ipc_event_mode(const char *mode, bool pango);
+void ipc_event_shutdown(const char *reason);
 
 #endif
diff --git a/sway/ipc-server.c b/sway/ipc-server.c
index be703915..72031e24 100644
--- a/sway/ipc-server.c
+++ b/sway/ipc-server.c
@@ -353,6 +353,20 @@ void ipc_event_mode(const char *mode, bool pango) {
 	json_object_put(obj);
 }
 
+void ipc_event_shutdown(const char *reason) {
+	if (!ipc_has_event_listeners(IPC_EVENT_SHUTDOWN)) {
+		return;
+	}
+	wlr_log(WLR_DEBUG, "Sending shutdown::%s event", reason);
+
+	json_object *json = json_object_new_object();
+	json_object_object_add(json, "change", json_object_new_string(reason));
+
+	const char *json_string = json_object_to_json_string(json);
+	ipc_send_event(json_string, IPC_EVENT_SHUTDOWN);
+	json_object_put(json);
+}
+
 int ipc_client_handle_writable(int client_fd, uint32_t mask, void *data) {
 	struct ipc_client *client = data;
 
@@ -549,6 +563,8 @@ void ipc_client_handle_command(struct ipc_client *client) {
 				client->subscribed_events |= event_mask(IPC_EVENT_BARCONFIG_UPDATE);
 			} else if (strcmp(event_type, "mode") == 0) {
 				client->subscribed_events |= event_mask(IPC_EVENT_MODE);
+			} else if (strcmp(event_type, "shutdown") == 0) {
+				client->subscribed_events |= event_mask(IPC_EVENT_SHUTDOWN);
 			} else if (strcmp(event_type, "window") == 0) {
 				client->subscribed_events |= event_mask(IPC_EVENT_WINDOW);
 			} else if (strcmp(event_type, "modifier") == 0) {
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;
 void sway_terminate(int exit_code) {
 	terminate_request = true;
 	exit_value = exit_code;
+	ipc_event_shutdown("exit");
 	wl_display_terminate(server.wl_display);
 }
 
-- 
cgit v1.2.3


From 317217f2c87aba4463806e211c22296ac9230b6c Mon Sep 17 00:00:00 2001
From: Ian Fan <ianfan0@gmail.com>
Date: Sat, 14 Jul 2018 11:10:36 +0100
Subject: ipc: add window::mark event

---
 include/sway/tree/view.h |  2 ++
 sway/commands/mark.c     |  2 +-
 sway/ipc-json.c          |  9 +++++++++
 sway/tree/view.c         | 13 +++++++++----
 4 files changed, 21 insertions(+), 5 deletions(-)

(limited to 'include')

diff --git a/include/sway/tree/view.h b/include/sway/tree/view.h
index 0152ed55..800df073 100644
--- a/include/sway/tree/view.h
+++ b/include/sway/tree/view.h
@@ -311,6 +311,8 @@ void view_clear_marks(struct sway_view *view);
 
 bool view_has_mark(struct sway_view *view, char *mark);
 
+void view_add_mark(struct sway_view *view, char *mark);
+
 void view_update_marks_textures(struct sway_view *view);
 
 /**
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) {
 	view_find_and_unmark(mark);
 
 	if (!toggle || !had_mark) {
-		list_add(view->marks, strdup(mark));
+		view_add_mark(view, mark);
 	}
 
 	free(mark);
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
 	bool urgent = c->type == C_VIEW ?
 		view_is_urgent(c->sway_view) : container_has_urgent_child(c);
 	json_object_object_add(object, "urgent", json_object_new_boolean(urgent));
+
+	if (c->type == C_VIEW) {
+		json_object *marks = json_object_new_array();
+		list_t *view_marks = c->sway_view->marks;
+		for (int i = 0; i < view_marks->length; ++i) {
+			json_object_array_add(marks, json_object_new_string(view_marks->items[i]));
+		}
+		json_object_object_add(object, "marks", marks);
+	}
 }
 
 static void focus_inactive_children_iterator(struct sway_container *c, void *data) {
diff --git a/sway/tree/view.c b/sway/tree/view.c
index 7300f207..48b39e80 100644
--- a/sway/tree/view.c
+++ b/sway/tree/view.c
@@ -888,6 +888,7 @@ bool view_find_and_unmark(char *mark) {
 			free(view_mark);
 			list_del(view->marks, i);
 			view_update_marks_textures(view);
+			ipc_event_window(container, "mark");
 			return true;
 		}
 	}
@@ -895,11 +896,10 @@ bool view_find_and_unmark(char *mark) {
 }
 
 void view_clear_marks(struct sway_view *view) {
-	for (int i = 0; i < view->marks->length; ++i) {
-		free(view->marks->items[i]);
+	while (view->marks->length) {
+		list_del(view->marks, 0);
+		ipc_event_window(view->swayc, "mark");
 	}
-	list_free(view->marks);
-	view->marks = create_list();
 }
 
 bool view_has_mark(struct sway_view *view, char *mark) {
@@ -912,6 +912,11 @@ bool view_has_mark(struct sway_view *view, char *mark) {
 	return false;
 }
 
+void view_add_mark(struct sway_view *view, char *mark) {
+	list_add(view->marks, strdup(mark));
+	ipc_event_window(view->swayc, "mark");
+}
+
 static void update_marks_texture(struct sway_view *view,
 		struct wlr_texture **texture, struct border_colors *class) {
 	struct sway_container *output = container_parent(view->swayc, C_OUTPUT);
-- 
cgit v1.2.3


From 6865b8aae9736e73a2153bce89d979737f8162cc Mon Sep 17 00:00:00 2001
From: Ian Fan <ianfan0@gmail.com>
Date: Wed, 18 Jul 2018 11:37:50 +0100
Subject: ipc: add binding event

---
 include/sway/config.h     |  2 --
 include/sway/ipc-server.h |  1 +
 sway/commands/bind.c      | 52 +++++++++++++++++++++++++++++++-
 sway/ipc-server.c         | 76 +++++++++++++++++++++++++++++++++++++++++++++++
 4 files changed, 128 insertions(+), 3 deletions(-)

(limited to 'include')

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);
 
 void free_sway_binding(struct sway_binding *sb);
 
-struct sway_binding *sway_binding_dup(struct sway_binding *sb);
-
 void seat_execute_command(struct sway_seat *seat, struct sway_binding *binding);
 
 void load_swaybars();
diff --git a/include/sway/ipc-server.h b/include/sway/ipc-server.h
index a11735cf..4b6d0e25 100644
--- a/include/sway/ipc-server.h
+++ b/include/sway/ipc-server.h
@@ -17,5 +17,6 @@ void ipc_event_window(struct sway_container *window, const char *change);
 void ipc_event_barconfig_update(struct bar_config *bar);
 void ipc_event_mode(const char *mode, bool pango);
 void ipc_event_shutdown(const char *reason);
+void ipc_event_binding(struct sway_binding *binding);
 
 #endif
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 @@
+#define _XOPEN_SOURCE 500
 #ifdef __linux__
 #include <linux/input-event-codes.h>
 #elif __FreeBSD__
@@ -5,9 +6,11 @@
 #endif
 #include <xkbcommon/xkbcommon.h>
 #include <xkbcommon/xkbcommon-names.h>
+#include <string.h>
 #include <strings.h>
 #include "sway/commands.h"
 #include "sway/config.h"
+#include "sway/ipc-server.h"
 #include "list.h"
 #include "log.h"
 #include "stringop.h"
@@ -27,6 +30,33 @@ void free_sway_binding(struct sway_binding *binding) {
 	free(binding);
 }
 
+static struct sway_binding *sway_binding_dup(struct sway_binding *sb) {
+	struct sway_binding *new_sb = calloc(1, sizeof(struct sway_binding));
+	if (!new_sb) {
+		return NULL;
+	}
+
+	new_sb->type = sb->type;
+	new_sb->order = sb->order;
+	new_sb->flags = sb->flags;
+	new_sb->modifiers = sb->modifiers;
+	new_sb->command = strdup(sb->command);
+
+	new_sb->keys = create_list();
+	int i;
+	for (i = 0; i < sb->keys->length; ++i) {
+		xkb_keysym_t *key = malloc(sizeof(xkb_keysym_t));
+		if (!key) {
+			free_sway_binding(new_sb);
+			return NULL;
+		}
+		*key = *(xkb_keysym_t *)sb->keys->items[i];
+		list_add(new_sb->keys, key);
+	}
+
+	return new_sb;
+}
+
 /**
  * Returns true if the bindings have the same key and modifier combinations.
  * 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) {
 void seat_execute_command(struct sway_seat *seat, struct sway_binding *binding) {
 	wlr_log(WLR_DEBUG, "running command for binding: %s",
 		binding->command);
+
+	struct sway_binding *binding_copy = binding;
+	bool reload = false;
+	// if this is a reload command we need to make a duplicate of the
+	// binding since it will be gone after the reload has completed.
+	if (strcasecmp(binding->command, "reload") == 0) {
+		reload = true;
+		binding_copy = sway_binding_dup(binding);
+		if (!binding_copy) {
+			wlr_log(WLR_ERROR, "Failed to duplicate binding during reload");
+			return;
+		}
+	}
+
 	config->handler_context.seat = seat;
 	struct cmd_results *results = execute_command(binding->command, NULL);
-	if (results->status != CMD_SUCCESS) {
+	if (results->status == CMD_SUCCESS) {
+		ipc_event_binding(binding_copy);
+	} else {
 		wlr_log(WLR_DEBUG, "could not run command for binding: %s (%s)",
 			binding->command, results->error);
 	}
+
+	if (reload) { // free the binding if we made a copy
+		free_sway_binding(binding_copy);
+	}
 	free_cmd_results(results);
 }
diff --git a/sway/ipc-server.c b/sway/ipc-server.c
index 72031e24..8fceafa2 100644
--- a/sway/ipc-server.c
+++ b/sway/ipc-server.c
@@ -3,12 +3,18 @@
 // Any value will hide SOCK_CLOEXEC on FreeBSD (__BSD_VISIBLE=0)
 #define _XOPEN_SOURCE 700
 #endif
+#ifdef __linux__
+#include <linux/input-event-codes.h>
+#elif __FreeBSD__
+#include <dev/evdev/input-event-codes.h>
+#endif
 #include <assert.h>
 #include <errno.h>
 #include <fcntl.h>
 #include <json-c/json.h>
 #include <stdbool.h>
 #include <stdint.h>
+#include <stdio.h>
 #include <stdlib.h>
 #include <string.h>
 #include <sys/socket.h>
@@ -28,6 +34,7 @@
 #include "sway/tree/view.h"
 #include "list.h"
 #include "log.h"
+#include "util.h"
 
 static int ipc_socket = -1;
 static struct wl_event_source *ipc_event_source =  NULL;
@@ -367,6 +374,75 @@ void ipc_event_shutdown(const char *reason) {
 	json_object_put(json);
 }
 
+void ipc_event_binding(struct sway_binding *binding) {
+	if (!ipc_has_event_listeners(IPC_EVENT_BINDING)) {
+		return;
+	}
+	wlr_log(WLR_DEBUG, "Sending binding event");
+
+	json_object *json_binding = json_object_new_object();
+	json_object_object_add(json_binding, "command", json_object_new_string(binding->command));
+
+	const char *names[10];
+	int len = get_modifier_names(names, binding->modifiers);
+	json_object *modifiers = json_object_new_array();
+	for (int i = 0; i < len; ++i) {
+		json_object_array_add(modifiers, json_object_new_string(names[i]));
+	}
+	json_object_object_add(json_binding, "event_state_mask", modifiers);
+
+	json_object *input_codes = json_object_new_array();
+	int input_code = 0;
+	json_object *symbols = json_object_new_array();
+	json_object *symbol = NULL;
+
+	if (binding->type == BINDING_KEYCODE) { // bindcode: populate input_codes
+		uint32_t keycode;
+		for (int i = 0; i < binding->keys->length; ++i) {
+			keycode = *(uint32_t *)binding->keys->items[i];
+			json_object_array_add(input_codes, json_object_new_int(keycode));
+			if (i == 0) {
+				input_code = keycode;
+			}
+		}
+	} else { // bindsym/mouse: populate symbols
+		uint32_t keysym;
+		char buffer[64];
+		for (int i = 0; i < binding->keys->length; ++i) {
+			keysym = *(uint32_t *)binding->keys->items[i];
+			if (keysym >= BTN_LEFT && keysym <= BTN_LEFT + 8) {
+				snprintf(buffer, 64, "button%u", keysym - BTN_LEFT + 1);
+			} else if (xkb_keysym_get_name(keysym, buffer, 64) < 0) {
+				continue;
+			}
+
+			json_object *str = json_object_new_string(buffer);
+			if (i == 0) {
+				// str is owned by both symbol and symbols. Make sure
+				// to bump the ref count.
+				json_object_array_add(symbols, json_object_get(str));
+				symbol = str;
+			} else {
+				json_object_array_add(symbols, str);
+			}
+		}
+	}
+
+	json_object_object_add(json_binding, "input_codes", input_codes);
+	json_object_object_add(json_binding, "input_code", json_object_new_int(input_code));
+	json_object_object_add(json_binding, "symbols", symbols);
+	json_object_object_add(json_binding, "symbol", symbol);
+	json_object_object_add(json_binding, "input_type", binding->type == BINDING_MOUSE ?
+			json_object_new_string("mouse") : json_object_new_string("keyboard"));
+
+	json_object *json = json_object_new_object();
+	json_object_object_add(json, "change", json_object_new_string("run"));
+	json_object_object_add(json, "binding", json_binding);
+	const char *json_string = json_object_to_json_string(json);
+	ipc_send_event(json_string, IPC_EVENT_BINDING);
+	json_object_put(json);
+}
+
 int ipc_client_handle_writable(int client_fd, uint32_t mask, void *data) {
 	struct ipc_client *client = data;
 
-- 
cgit v1.2.3


From 3edaf2ce2a8a4753c162491329a7dfa492ff7e77 Mon Sep 17 00:00:00 2001
From: Ian Fan <ianfan0@gmail.com>
Date: Wed, 18 Jul 2018 12:30:39 +0100
Subject: ipc: add tick event

---
 completions/bash/swaymsg |  1 +
 completions/zsh/_swaymsg |  1 +
 include/ipc.h            |  2 ++
 sway/ipc-server.c        | 30 ++++++++++++++++++++++++++++++
 swaymsg/main.c           |  8 +++++++-
 swaymsg/swaymsg.1.scd    |  3 +++
 6 files changed, 44 insertions(+), 1 deletion(-)

(limited to 'include')

diff --git a/completions/bash/swaymsg b/completions/bash/swaymsg
index e4b2c1b7..20092bdc 100644
--- a/completions/bash/swaymsg
+++ b/completions/bash/swaymsg
@@ -16,6 +16,7 @@ _swaymsg()
     'get_version'
     'get_binding_modes'
     'get_config'
+    'send_tick'
   )
 
   short=(
diff --git a/completions/zsh/_swaymsg b/completions/zsh/_swaymsg
index 28de474d..a7a1c8e0 100644
--- a/completions/zsh/_swaymsg
+++ b/completions/zsh/_swaymsg
@@ -24,6 +24,7 @@ types=(
 'get_version'
 'get_binding_modes'
 'get_config'
+'send_tick'
 )
 
 _arguments -s \
diff --git a/include/ipc.h b/include/ipc.h
index ffc57d1b..2138d3fa 100644
--- a/include/ipc.h
+++ b/include/ipc.h
@@ -15,6 +15,7 @@ enum ipc_command_type {
 	IPC_GET_VERSION = 7,
 	IPC_GET_BINDING_MODES = 8,
 	IPC_GET_CONFIG = 9,
+	IPC_SEND_TICK = 10,
 
 	// sway-specific command types
 	IPC_GET_INPUTS = 100,
@@ -28,6 +29,7 @@ enum ipc_command_type {
 	IPC_EVENT_BARCONFIG_UPDATE = ((1<<31) | 4),
 	IPC_EVENT_BINDING = ((1<<31) | 5),
 	IPC_EVENT_SHUTDOWN = ((1<<31) | 6),
+	IPC_EVENT_TICK = ((1<<31) | 7),
 	IPC_EVENT_MODIFIER = ((1<<31) | 16),
 	IPC_EVENT_INPUT = ((1<<31) | 17),
 };
diff --git a/sway/ipc-server.c b/sway/ipc-server.c
index dc6b353b..63633567 100644
--- a/sway/ipc-server.c
+++ b/sway/ipc-server.c
@@ -441,6 +441,21 @@ void ipc_event_binding(struct sway_binding *binding) {
 	json_object_put(json);
 }
 
+static void ipc_event_tick(const char *payload) {
+	if (!ipc_has_event_listeners(IPC_EVENT_TICK)) {
+		return;
+	}
+	wlr_log(WLR_DEBUG, "Sending tick event");
+
+	json_object *json = json_object_new_object();
+	json_object_object_add(json, "first", json_object_new_boolean(false));
+	json_object_object_add(json, "payload", json_object_new_string(payload));
+
+	const char *json_string = json_object_to_json_string(json);
+	ipc_send_event(json_string, IPC_EVENT_TICK);
+	json_object_put(json);
+}
+
 int ipc_client_handle_writable(int client_fd, uint32_t mask, void *data) {
 	struct ipc_client *client = data;
 
@@ -582,6 +597,13 @@ void ipc_client_handle_command(struct ipc_client *client) {
 		goto exit_cleanup;
 	}
 
+	case IPC_SEND_TICK:
+	{
+		ipc_event_tick(buf);
+		ipc_send_reply(client, "{\"success\": true}", 17);
+		goto exit_cleanup;
+	}
+
 	case IPC_GET_OUTPUTS:
 	{
 		json_object *outputs = json_object_new_array();
@@ -628,6 +650,7 @@ void ipc_client_handle_command(struct ipc_client *client) {
 			goto exit_cleanup;
 		}
 
+		bool is_tick = false;
 		// parse requested event types
 		for (size_t i = 0; i < json_object_array_length(request); i++) {
 			const char *event_type = json_object_get_string(json_object_array_get_idx(request, i));
@@ -645,6 +668,9 @@ void ipc_client_handle_command(struct ipc_client *client) {
 				client->subscribed_events |= event_mask(IPC_EVENT_MODIFIER);
 			} else if (strcmp(event_type, "binding") == 0) {
 				client->subscribed_events |= event_mask(IPC_EVENT_BINDING);
+			} else if (strcmp(event_type, "tick") == 0) {
+				client->subscribed_events |= event_mask(IPC_EVENT_TICK);
+				is_tick = true;
 			} else {
 				client_valid =
 					ipc_send_reply(client, "{\"success\": false}", 18);
@@ -656,6 +682,10 @@ void ipc_client_handle_command(struct ipc_client *client) {
 
 		json_object_put(request);
 		client_valid = ipc_send_reply(client, "{\"success\": true}", 17);
+		if (is_tick) {
+			client->current_command = IPC_EVENT_TICK;
+			ipc_send_reply(client, "{\"first\": true, \"payload\": \"\"}", 30);
+		}
 		goto exit_cleanup;
 	}
 
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) {
 	if (type != IPC_COMMAND && type != IPC_GET_WORKSPACES &&
 			type != IPC_GET_INPUTS && type != IPC_GET_OUTPUTS &&
 			type != IPC_GET_VERSION && type != IPC_GET_SEATS &&
-			type != IPC_GET_CONFIG) {
+			type != IPC_GET_CONFIG && type != IPC_SEND_TICK) {
 		printf("%s\n", json_object_to_json_string_ext(resp,
 			JSON_C_TO_STRING_PRETTY | JSON_C_TO_STRING_SPACED));
 		return;
 	}
 
+	if (type == IPC_SEND_TICK) {
+		return;
+	}
+
 	if (type == IPC_GET_VERSION) {
 		pretty_print_version(resp);
 		return;
@@ -384,6 +388,8 @@ int main(int argc, char **argv) {
 		type = IPC_GET_BINDING_MODES;
 	} else if (strcasecmp(cmdtype, "get_config") == 0) {
 		type = IPC_GET_CONFIG;
+	} else if (strcasecmp(cmdtype, "send_tick") == 0) {
+		type = IPC_SEND_TICK;
 	} else {
 		sway_abort("Unknown message type %s", cmdtype);
 	}
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]
 
 *get\_config*
 	Gets a JSON-encoded copy of the current configuration.
+
+*send\_tick*
+	Sends a tick event to all subscribed clients.
-- 
cgit v1.2.3


From 03eaf444a4a432e5712d40f93d849b51d2028b63 Mon Sep 17 00:00:00 2001
From: Ian Fan <ianfan0@gmail.com>
Date: Wed, 18 Jul 2018 21:55:14 +0100
Subject: ipc: prevent emitting a workspace::focus event when moving a
 container to a different workspace or output

When a container is moved from, say, workspace 1 to workspace 2, workspace 2 is focused in order to arrange the windows before focus is moved back to workspace 1, which caused a workspace:focus event from workspace 2 to workspace 1 to be emitted. This commit inhibits that event.
---
 include/sway/input/seat.h | 2 +-
 sway/commands/move.c      | 4 ++--
 sway/input/cursor.c       | 6 +++---
 sway/input/seat.c         | 6 +++---
 sway/tree/layout.c        | 4 ++--
 5 files changed, 11 insertions(+), 11 deletions(-)

(limited to 'include')

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);
 void seat_set_focus(struct sway_seat *seat, struct sway_container *container);
 
 void seat_set_focus_warp(struct sway_seat *seat,
-		struct sway_container *container, bool warp);
+		struct sway_container *container, bool warp, bool notify);
 
 void seat_set_focus_surface(struct sway_seat *seat,
 		struct wlr_surface *surface, bool unfocus);
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,
 		container_move_to(current, destination);
 		struct sway_container *focus = seat_get_focus_inactive(
 				config->handler_context.seat, old_parent);
-		seat_set_focus(config->handler_context.seat, focus);
+		seat_set_focus_warp(config->handler_context.seat, focus, true, false);
 		container_reap_empty(old_parent);
 		container_reap_empty(destination->parent);
 
@@ -135,7 +135,7 @@ static struct cmd_results *cmd_move_container(struct sway_container *current,
 		struct sway_container *old_parent = current->parent;
 		struct sway_container *old_ws = container_parent(current, C_WORKSPACE);
 		container_move_to(current, focus);
-		seat_set_focus(config->handler_context.seat, old_parent);
+		seat_set_focus_warp(config->handler_context.seat, old_parent, true, false);
 		container_reap_empty(old_parent);
 		container_reap_empty(focus->parent);
 
diff --git a/sway/input/cursor.c b/sway/input/cursor.c
index 96ac7b33..d6fdc1da 100644
--- a/sway/input/cursor.c
+++ b/sway/input/cursor.c
@@ -349,7 +349,7 @@ void cursor_send_pointer_motion(struct sway_cursor *cursor, uint32_t time_msec,
 				output = container_parent(c, C_OUTPUT);
 			}
 			if (output != focus) {
-				seat_set_focus_warp(seat, c, false);
+				seat_set_focus_warp(seat, c, false, true);
 			}
 		} else if (c->type == C_VIEW) {
 			// Focus c if the following are true:
@@ -359,13 +359,13 @@ void cursor_send_pointer_motion(struct sway_cursor *cursor, uint32_t time_msec,
 			if (!wlr_seat_keyboard_has_grab(cursor->seat->wlr_seat) &&
 					c != prev_c &&
 					view_is_visible(c->sway_view)) {
-				seat_set_focus_warp(seat, c, false);
+				seat_set_focus_warp(seat, c, false, true);
 			} else {
 				struct sway_container *next_focus =
 					seat_get_focus_inactive(seat, &root_container);
 				if (next_focus && next_focus->type == C_VIEW &&
 						view_is_visible(next_focus->sway_view)) {
-					seat_set_focus_warp(seat, next_focus, false);
+					seat_set_focus_warp(seat, next_focus, false, true);
 				}
 			}
 		}
diff --git a/sway/input/seat.c b/sway/input/seat.c
index 76050aa9..fe3cbc53 100644
--- a/sway/input/seat.c
+++ b/sway/input/seat.c
@@ -617,7 +617,7 @@ static int handle_urgent_timeout(void *data) {
 }
 
 void seat_set_focus_warp(struct sway_seat *seat,
-		struct sway_container *container, bool warp) {
+		struct sway_container *container, bool warp, bool notify) {
 	if (seat->focused_layer) {
 		return;
 	}
@@ -739,7 +739,7 @@ void seat_set_focus_warp(struct sway_seat *seat,
 
 	if (last_focus) {
 		if (last_workspace) {
-			if (last_workspace != new_workspace) {
+			if (notify && last_workspace != new_workspace) {
 				 ipc_event_workspace(last_workspace, new_workspace, "focus");
 			}
 			if (!workspace_is_visible(last_workspace)
@@ -779,7 +779,7 @@ void seat_set_focus_warp(struct sway_seat *seat,
 
 void seat_set_focus(struct sway_seat *seat,
 		struct sway_container *container) {
-	seat_set_focus_warp(seat, container, true);
+	seat_set_focus_warp(seat, container, true, true);
 }
 
 void seat_set_focus_surface(struct sway_seat *seat,
diff --git a/sway/tree/layout.c b/sway/tree/layout.c
index 9fbbccaf..1f82e534 100644
--- a/sway/tree/layout.c
+++ b/sway/tree/layout.c
@@ -1001,13 +1001,13 @@ static void swap_focus(struct sway_container *con1,
 		if (focus == con1 && (con2->parent->layout == L_TABBED
 					|| con2->parent->layout == L_STACKED)) {
 			if (workspace_is_visible(ws2)) {
-				seat_set_focus_warp(seat, con2, false);
+				seat_set_focus_warp(seat, con2, false, true);
 			}
 			seat_set_focus(seat, ws1 != ws2 ? con2 : con1);
 		} else if (focus == con2 && (con1->parent->layout == L_TABBED
 					|| con1->parent->layout == L_STACKED)) {
 			if (workspace_is_visible(ws1)) {
-				seat_set_focus_warp(seat, con1, false);
+				seat_set_focus_warp(seat, con1, false, true);
 			}
 			seat_set_focus(seat, ws1 != ws2 ? con1 : con2);
 		} else if (ws1 != ws2) {
-- 
cgit v1.2.3


From 46cfa8ff56acff0139b2e24300cbc3ea19da723f Mon Sep 17 00:00:00 2001
From: Ian Fan <ianfan0@gmail.com>
Date: Wed, 1 Aug 2018 16:22:06 +0100
Subject: ipc: remove extraneous values

Removes IPC_EVENT_MODIFIER and IPC_EVENT_INPUT, which were sway-specific and unused
---
 include/ipc.h     | 2 --
 sway/ipc-server.c | 2 --
 2 files changed, 4 deletions(-)

(limited to 'include')

diff --git a/include/ipc.h b/include/ipc.h
index 2138d3fa..a3f60e19 100644
--- a/include/ipc.h
+++ b/include/ipc.h
@@ -30,8 +30,6 @@ enum ipc_command_type {
 	IPC_EVENT_BINDING = ((1<<31) | 5),
 	IPC_EVENT_SHUTDOWN = ((1<<31) | 6),
 	IPC_EVENT_TICK = ((1<<31) | 7),
-	IPC_EVENT_MODIFIER = ((1<<31) | 16),
-	IPC_EVENT_INPUT = ((1<<31) | 17),
 };
 
 #endif
diff --git a/sway/ipc-server.c b/sway/ipc-server.c
index 63633567..7d2d8969 100644
--- a/sway/ipc-server.c
+++ b/sway/ipc-server.c
@@ -664,8 +664,6 @@ void ipc_client_handle_command(struct ipc_client *client) {
 				client->subscribed_events |= event_mask(IPC_EVENT_SHUTDOWN);
 			} else if (strcmp(event_type, "window") == 0) {
 				client->subscribed_events |= event_mask(IPC_EVENT_WINDOW);
-			} else if (strcmp(event_type, "modifier") == 0) {
-				client->subscribed_events |= event_mask(IPC_EVENT_MODIFIER);
 			} else if (strcmp(event_type, "binding") == 0) {
 				client->subscribed_events |= event_mask(IPC_EVENT_BINDING);
 			} else if (strcmp(event_type, "tick") == 0) {
-- 
cgit v1.2.3