From 338a0399f8d7c0ebe9cbb989945d8fd646d8d407 Mon Sep 17 00:00:00 2001
From: Tony Crisci <tony@dubstepdish.com>
Date: Wed, 6 Dec 2017 08:28:46 -0500
Subject: [PATCH 01/49] input skeleton

---
 include/sway/input.h | 18 ++++++++++++++++++
 meson.build          |  1 +
 sway/meson.build     |  2 ++
 sway/server.c        |  4 ++++
 4 files changed, 25 insertions(+)
 create mode 100644 include/sway/input.h

diff --git a/include/sway/input.h b/include/sway/input.h
new file mode 100644
index 00000000..21ed61c5
--- /dev/null
+++ b/include/sway/input.h
@@ -0,0 +1,18 @@
+#ifndef _SWAY_INPUT_H
+#define _SWAY_INPUT_H
+#include <libinput.h>
+#include "sway/server.h"
+#include "config.h"
+#include "list.h"
+
+struct sway_input {
+	list_t *input_devices;
+};
+
+struct input_config *new_input_config(const char* identifier);
+
+char* libinput_dev_unique_id(struct libinput_device *dev);
+
+struct sway_input *sway_input_create(struct sway_server *server);
+
+#endif
diff --git a/meson.build b/meson.build
index 8e7b98ed..029aea46 100644
--- a/meson.build
+++ b/meson.build
@@ -29,6 +29,7 @@ xkbcommon      = dependency('xkbcommon')
 pango          = dependency('pango')
 pixman         = dependency('pixman-1')
 libcap         = dependency('libcap')
+libinput       = dependency('libinput')
 math           = cc.find_library('m')
 git = find_program('git', required: false)
 a2x = find_program('a2x', required: false)
diff --git a/sway/meson.build b/sway/meson.build
index 84f48137..8631b9c3 100644
--- a/sway/meson.build
+++ b/sway/meson.build
@@ -2,6 +2,7 @@ sway_sources = files(
 	'main.c',
 	'server.c',
 	'commands.c',
+	'input/input.c',
 	'commands/exit.c',
 	'commands/exec.c',
 	'commands/exec_always.c',
@@ -25,6 +26,7 @@ sway_deps = [
 	wlroots,
 	libcap,
 	math,
+	libinput,
 ]
 
 executable(
diff --git a/sway/server.c b/sway/server.c
index 024d8429..3873e625 100644
--- a/sway/server.c
+++ b/sway/server.c
@@ -11,6 +11,7 @@
 // TODO WLR: make Xwayland optional
 #include <wlr/xwayland.h>
 #include "sway/server.h"
+#include "sway/input.h"
 #include "log.h"
 
 bool server_init(struct sway_server *server) {
@@ -58,6 +59,9 @@ bool server_init(struct sway_server *server) {
 		wlr_backend_destroy(server->backend);
 		return false;
 	}
+
+	server->input = sway_input_create(server);
+
 	return true;
 }
 

From 21ce20885a6a6e9e7178778513b09fea9354c603 Mon Sep 17 00:00:00 2001
From: Tony Crisci <tony@dubstepdish.com>
Date: Thu, 7 Dec 2017 07:31:49 -0500
Subject: [PATCH 02/49] rename input to input-manager

---
 include/sway/{input.h => input-manager.h} | 5 +++--
 include/sway/server.h                     | 2 +-
 sway/input/{input.c => input-manager.c}   | 8 +++++---
 sway/meson.build                          | 2 +-
 sway/server.c                             | 4 ++--
 5 files changed, 12 insertions(+), 9 deletions(-)
 rename include/sway/{input.h => input-manager.h} (71%)
 rename sway/input/{input.c => input-manager.c} (89%)

diff --git a/include/sway/input.h b/include/sway/input-manager.h
similarity index 71%
rename from include/sway/input.h
rename to include/sway/input-manager.h
index 21ed61c5..a3662f7b 100644
--- a/include/sway/input.h
+++ b/include/sway/input-manager.h
@@ -5,7 +5,7 @@
 #include "config.h"
 #include "list.h"
 
-struct sway_input {
+struct sway_input_manager {
 	list_t *input_devices;
 };
 
@@ -13,6 +13,7 @@ struct input_config *new_input_config(const char* identifier);
 
 char* libinput_dev_unique_id(struct libinput_device *dev);
 
-struct sway_input *sway_input_create(struct sway_server *server);
+struct sway_input_manager *sway_input_manager_create(
+		struct sway_server *server);
 
 #endif
diff --git a/include/sway/server.h b/include/sway/server.h
index 3fa72e84..76a05476 100644
--- a/include/sway/server.h
+++ b/include/sway/server.h
@@ -22,7 +22,7 @@ struct sway_server {
 	struct wlr_compositor *compositor;
 	struct wlr_data_device_manager *data_device_manager;
 
-	struct sway_input *input;
+	struct sway_input_manager *input;
 
 	struct wl_listener output_add;
 	struct wl_listener output_remove;
diff --git a/sway/input/input.c b/sway/input/input-manager.c
similarity index 89%
rename from sway/input/input.c
rename to sway/input/input-manager.c
index 02b4995e..285a68b8 100644
--- a/sway/input/input.c
+++ b/sway/input/input-manager.c
@@ -6,15 +6,17 @@
 #include <string.h>
 #include <libinput.h>
 #include "sway/config.h"
-#include "sway/input.h"
+#include "sway/input-manager.h"
 #include "sway/server.h"
 #include "list.h"
 #include "log.h"
 
 struct input_config *current_input_config = NULL;
 
-struct sway_input *sway_input_create(struct sway_server *server) {
-	struct sway_input *input = calloc(1, sizeof(struct sway_input));
+struct sway_input_manager *sway_input_manager_create(
+		struct sway_server *server) {
+	struct sway_input_manager *input =
+		calloc(1, sizeof(struct sway_input_manager));
 	if (!input) {
 		return NULL;
 	}
diff --git a/sway/meson.build b/sway/meson.build
index 8631b9c3..b5cdbbf2 100644
--- a/sway/meson.build
+++ b/sway/meson.build
@@ -2,7 +2,7 @@ sway_sources = files(
 	'main.c',
 	'server.c',
 	'commands.c',
-	'input/input.c',
+	'input/input-manager.c',
 	'commands/exit.c',
 	'commands/exec.c',
 	'commands/exec_always.c',
diff --git a/sway/server.c b/sway/server.c
index 3873e625..174beac6 100644
--- a/sway/server.c
+++ b/sway/server.c
@@ -11,7 +11,7 @@
 // TODO WLR: make Xwayland optional
 #include <wlr/xwayland.h>
 #include "sway/server.h"
-#include "sway/input.h"
+#include "sway/input-manager.h"
 #include "log.h"
 
 bool server_init(struct sway_server *server) {
@@ -60,7 +60,7 @@ bool server_init(struct sway_server *server) {
 		return false;
 	}
 
-	server->input = sway_input_create(server);
+	server->input = sway_input_manager_create(server);
 
 	return true;
 }

From f6f63f60d6c7f9602dd1c07b45eb45a97e5b6f5a Mon Sep 17 00:00:00 2001
From: Tony Crisci <tony@dubstepdish.com>
Date: Thu, 7 Dec 2017 09:58:32 -0500
Subject: [PATCH 03/49] basic input manager and seat

---
 include/sway/input-manager.h |  9 ++++--
 include/sway/seat.h          | 20 +++++++++++++
 sway/input/input-manager.c   | 54 ++++++++++++++++++++++++++++++++++++
 sway/input/seat.c            | 24 ++++++++++++++++
 sway/meson.build             |  1 +
 5 files changed, 105 insertions(+), 3 deletions(-)
 create mode 100644 include/sway/seat.h
 create mode 100644 sway/input/seat.c

diff --git a/include/sway/input-manager.h b/include/sway/input-manager.h
index a3662f7b..4c01a043 100644
--- a/include/sway/input-manager.h
+++ b/include/sway/input-manager.h
@@ -1,12 +1,15 @@
-#ifndef _SWAY_INPUT_H
-#define _SWAY_INPUT_H
+#ifndef _SWAY_INPUT_MANAGER_H
+#define _SWAY_INPUT_MANAGER_H
 #include <libinput.h>
 #include "sway/server.h"
 #include "config.h"
 #include "list.h"
 
 struct sway_input_manager {
-	list_t *input_devices;
+	struct wl_listener input_add;
+	struct wl_listener input_remove;
+	struct sway_server *server;
+	list_t *seats;
 };
 
 struct input_config *new_input_config(const char* identifier);
diff --git a/include/sway/seat.h b/include/sway/seat.h
new file mode 100644
index 00000000..a2b8fe51
--- /dev/null
+++ b/include/sway/seat.h
@@ -0,0 +1,20 @@
+#ifndef _SWAY_SEAT_H
+#define _SWAY_SEAT_H
+
+#include <wlr/types/wlr_seat.h>
+#include "sway/input-manager.h"
+
+struct sway_seat {
+	struct wlr_seat *seat;
+};
+
+struct sway_seat *sway_seat_create(struct wl_display *display,
+		const char *seat_name);
+
+void sway_seat_add_device(struct sway_seat *seat,
+		struct wlr_input_device *device);
+
+void sway_seat_remove_device(struct sway_seat *seat,
+		struct wlr_input_device *device);
+
+#endif
diff --git a/sway/input/input-manager.c b/sway/input/input-manager.c
index 285a68b8..b5ab8cc1 100644
--- a/sway/input/input-manager.c
+++ b/sway/input/input-manager.c
@@ -7,12 +7,52 @@
 #include <libinput.h>
 #include "sway/config.h"
 #include "sway/input-manager.h"
+#include "sway/seat.h"
 #include "sway/server.h"
 #include "list.h"
 #include "log.h"
 
+static const char *default_seat = "seat0";
+
 struct input_config *current_input_config = NULL;
 
+static struct sway_seat *input_manager_get_seat(
+		struct sway_input_manager *input, const char *seat_name) {
+	struct sway_seat *seat = NULL;
+
+	for (int i = 0; i < input->seats->length; ++i) {
+		seat = input->seats->items[i];
+		if (strcmp(seat->seat->name, seat_name) == 0) {
+			return seat;
+		}
+	}
+
+	seat = sway_seat_create(input->server->wl_display, seat_name);
+	list_add(input->seats, seat);
+
+	return seat;
+}
+
+static void input_add_notify(struct wl_listener *listener, void *data) {
+	struct sway_input_manager *input =
+		wl_container_of(listener, input, input_add);
+	struct wlr_input_device *device = data;
+
+	// TODO device configuration
+	struct sway_seat *seat = input_manager_get_seat(input, default_seat);
+	sway_seat_add_device(seat, device);
+}
+
+static void input_remove_notify(struct wl_listener *listener, void *data) {
+	struct sway_input_manager *input =
+		wl_container_of(listener, input, input_remove);
+	struct wlr_input_device *device = data;
+
+	// TODO device configuration
+	struct sway_seat *seat = input_manager_get_seat(input, default_seat);
+	sway_seat_remove_device(seat, device);
+}
+
 struct sway_input_manager *sway_input_manager_create(
 		struct sway_server *server) {
 	struct sway_input_manager *input =
@@ -20,6 +60,20 @@ struct sway_input_manager *sway_input_manager_create(
 	if (!input) {
 		return NULL;
 	}
+	// XXX probably don't need the full server
+	input->server = server;
+
+	input->seats = create_list();
+
+	// create the default seat
+	input_manager_get_seat(input, default_seat);
+
+	input->input_add.notify = input_add_notify;
+	wl_signal_add(&server->backend->events.input_add, &input->input_add);
+
+	input->input_remove.notify = input_remove_notify;
+	wl_signal_add(&server->backend->events.input_remove, &input->input_remove);
+
 	return input;
 }
 
diff --git a/sway/input/seat.c b/sway/input/seat.c
new file mode 100644
index 00000000..f41b6dba
--- /dev/null
+++ b/sway/input/seat.c
@@ -0,0 +1,24 @@
+#define _XOPEN_SOURCE 700
+#include "sway/seat.h"
+#include "sway/input-manager.h"
+#include "log.h"
+
+struct sway_seat *sway_seat_create(struct wl_display *display,
+		const char *seat_name) {
+	struct sway_seat *seat = calloc(1, sizeof(struct sway_seat));
+	if (!seat) {
+		return NULL;
+	}
+	seat->seat = wlr_seat_create(display, seat_name);
+	return seat;
+}
+
+void sway_seat_add_device(struct sway_seat *seat,
+		struct wlr_input_device *device) {
+	sway_log(L_DEBUG, "input add: %s", device->name);
+}
+
+void sway_seat_remove_device(struct sway_seat *seat,
+		struct wlr_input_device *device) {
+	sway_log(L_DEBUG, "input remove: %s", device->name);
+}
diff --git a/sway/meson.build b/sway/meson.build
index b5cdbbf2..cea565d6 100644
--- a/sway/meson.build
+++ b/sway/meson.build
@@ -3,6 +3,7 @@ sway_sources = files(
 	'server.c',
 	'commands.c',
 	'input/input-manager.c',
+	'input/seat.c',
 	'commands/exit.c',
 	'commands/exec.c',
 	'commands/exec_always.c',

From ec7fc42a00db8c230ca1a050f0a1f7badc697fa5 Mon Sep 17 00:00:00 2001
From: Tony Crisci <tony@dubstepdish.com>
Date: Fri, 8 Dec 2017 07:22:26 -0500
Subject: [PATCH 04/49] sway cursor

---
 include/sway/cursor.h |  26 +++++++++
 include/sway/seat.h   |   1 +
 sway/input/cursor.c   | 128 ++++++++++++++++++++++++++++++++++++++++++
 sway/input/seat.c     |  52 +++++++++++++++++
 sway/meson.build      |   1 +
 5 files changed, 208 insertions(+)
 create mode 100644 include/sway/cursor.h
 create mode 100644 sway/input/cursor.c

diff --git a/include/sway/cursor.h b/include/sway/cursor.h
new file mode 100644
index 00000000..647bc8f1
--- /dev/null
+++ b/include/sway/cursor.h
@@ -0,0 +1,26 @@
+#ifndef _SWAY_CURSOR_H
+#define _SWAY_CURSOR_H
+
+#include "sway/seat.h"
+
+struct sway_cursor {
+	struct wlr_cursor *cursor;
+
+	struct wl_listener motion;
+	struct wl_listener motion_absolute;
+	struct wl_listener button;
+	struct wl_listener axis;
+
+	struct wl_listener touch_down;
+	struct wl_listener touch_up;
+	struct wl_listener touch_motion;
+
+	struct wl_listener tool_axis;
+	struct wl_listener tool_tip;
+
+	struct wl_listener request_set_cursor;
+};
+
+struct sway_cursor *sway_cursor_create(struct sway_seat *seat);
+
+#endif
diff --git a/include/sway/seat.h b/include/sway/seat.h
index a2b8fe51..2f8ca72e 100644
--- a/include/sway/seat.h
+++ b/include/sway/seat.h
@@ -6,6 +6,7 @@
 
 struct sway_seat {
 	struct wlr_seat *seat;
+	struct sway_cursor *cursor;
 };
 
 struct sway_seat *sway_seat_create(struct wl_display *display,
diff --git a/sway/input/cursor.c b/sway/input/cursor.c
new file mode 100644
index 00000000..819007d5
--- /dev/null
+++ b/sway/input/cursor.c
@@ -0,0 +1,128 @@
+#define _XOPEN_SOURCE 700
+#include <wlr/types/wlr_cursor.h>
+#include "sway/cursor.h"
+#include "log.h"
+
+static void handle_cursor_motion(struct wl_listener *listener, void *data) {
+	struct sway_cursor *cursor =
+		wl_container_of(listener, cursor, motion);
+	struct wlr_event_pointer_motion *event = data;
+	sway_log(L_DEBUG, "TODO: handle event: %p", event);
+}
+
+static void handle_cursor_motion_absolute(struct wl_listener *listener,
+		void *data) {
+	struct sway_cursor *cursor =
+		wl_container_of(listener, cursor, motion_absolute);
+	struct wlr_event_pointer_motion_absolute *event = data;
+	sway_log(L_DEBUG, "TODO: handle event: %p", event);
+}
+
+static void handle_cursor_button(struct wl_listener *listener, void *data) {
+	struct sway_cursor *cursor =
+		wl_container_of(listener, cursor, button);
+	struct wlr_event_pointer_button *event = data;
+	sway_log(L_DEBUG, "TODO: handle event: %p", event);
+}
+
+static void handle_cursor_axis(struct wl_listener *listener, void *data) {
+	struct sway_cursor *cursor =
+		wl_container_of(listener, cursor, axis);
+	struct wlr_event_pointer_axis *event = data;
+	sway_log(L_DEBUG, "TODO: handle event: %p", event);
+}
+
+static void handle_touch_down(struct wl_listener *listener, void *data) {
+	struct sway_cursor *cursor =
+		wl_container_of(listener, cursor, touch_down);
+	struct wlr_event_touch_down *event = data;
+	sway_log(L_DEBUG, "TODO: handle event: %p", event);
+}
+
+static void handle_touch_up(struct wl_listener *listener, void *data) {
+	struct sway_cursor *cursor =
+		wl_container_of(listener, cursor, touch_up);
+	struct wlr_event_touch_up *event = data;
+	sway_log(L_DEBUG, "TODO: handle event: %p", event);
+}
+
+static void handle_touch_motion(struct wl_listener *listener, void *data) {
+	struct sway_cursor *cursor =
+		wl_container_of(listener, cursor, touch_motion);
+	struct wlr_event_touch_motion *event = data;
+	sway_log(L_DEBUG, "TODO: handle event: %p", event);
+}
+
+static void handle_tool_axis(struct wl_listener *listener, void *data) {
+	struct sway_cursor *cursor =
+		wl_container_of(listener, cursor, tool_axis);
+	struct wlr_event_tablet_tool_axis *event = data;
+	sway_log(L_DEBUG, "TODO: handle event: %p", event);
+}
+
+static void handle_tool_tip(struct wl_listener *listener, void *data) {
+	struct sway_cursor *cursor =
+		wl_container_of(listener, cursor, tool_tip);
+	struct wlr_event_tablet_tool_tip *event = data;
+	sway_log(L_DEBUG, "TODO: handle event: %p", event);
+}
+
+static void handle_request_set_cursor(struct wl_listener *listener,
+		void *data) {
+	struct sway_cursor *cursor =
+		wl_container_of(listener, cursor, request_set_cursor);
+	struct wlr_seat_pointer_request_set_cursor_event *event = data;
+	sway_log(L_DEBUG, "TODO: handle event: %p", event);
+}
+
+struct sway_cursor *sway_cursor_create(struct sway_seat *seat) {
+	struct sway_cursor *cursor = calloc(1, sizeof(struct sway_cursor));
+	if (!sway_assert(cursor, "could not allocate sway cursor")) {
+		return NULL;
+	}
+
+	struct wlr_cursor *wlr_cursor = wlr_cursor_create();
+	if (!sway_assert(wlr_cursor, "could not allocate wlr cursor")) {
+		free(cursor);
+		return NULL;
+	}
+
+	// input events
+	wl_signal_add(&wlr_cursor->events.motion, &cursor->motion);
+	cursor->motion.notify = handle_cursor_motion;
+
+	wl_signal_add(&wlr_cursor->events.motion_absolute,
+		&cursor->motion_absolute);
+	cursor->motion_absolute.notify = handle_cursor_motion_absolute;
+
+	wl_signal_add(&wlr_cursor->events.button, &cursor->button);
+	cursor->button.notify = handle_cursor_button;
+
+	wl_signal_add(&wlr_cursor->events.axis, &cursor->axis);
+	cursor->axis.notify = handle_cursor_axis;
+
+	wl_signal_add(&wlr_cursor->events.touch_down, &cursor->touch_down);
+	cursor->touch_down.notify = handle_touch_down;
+
+	wl_signal_add(&wlr_cursor->events.touch_up, &cursor->touch_up);
+	cursor->touch_up.notify = handle_touch_up;
+
+	wl_signal_add(&wlr_cursor->events.touch_motion,
+		&cursor->touch_motion);
+	cursor->touch_motion.notify = handle_touch_motion;
+
+	wl_signal_add(&wlr_cursor->events.tablet_tool_axis,
+		&cursor->tool_axis);
+	cursor->tool_axis.notify = handle_tool_axis;
+
+	wl_signal_add(&wlr_cursor->events.tablet_tool_tip, &cursor->tool_tip);
+	cursor->tool_tip.notify = handle_tool_tip;
+
+	wl_signal_add(&seat->seat->events.request_set_cursor,
+			&cursor->request_set_cursor);
+	cursor->request_set_cursor.notify = handle_request_set_cursor;
+
+	cursor->cursor = wlr_cursor;
+
+	return cursor;
+}
diff --git a/sway/input/seat.c b/sway/input/seat.c
index f41b6dba..459b2ee2 100644
--- a/sway/input/seat.c
+++ b/sway/input/seat.c
@@ -1,5 +1,7 @@
 #define _XOPEN_SOURCE 700
+#include <wlr/types/wlr_cursor.h>
 #include "sway/seat.h"
+#include "sway/cursor.h"
 #include "sway/input-manager.h"
 #include "log.h"
 
@@ -9,16 +11,66 @@ struct sway_seat *sway_seat_create(struct wl_display *display,
 	if (!seat) {
 		return NULL;
 	}
+
 	seat->seat = wlr_seat_create(display, seat_name);
+	if (!sway_assert(seat->seat, "could not allocate seat")) {
+		return NULL;
+	}
+
+	seat->cursor = sway_cursor_create(seat);
+	if (!seat->cursor) {
+		wlr_seat_destroy(seat->seat);
+		free(seat);
+		return NULL;
+	}
+
+	wlr_seat_set_capabilities(seat->seat,
+		WL_SEAT_CAPABILITY_KEYBOARD |
+		WL_SEAT_CAPABILITY_POINTER |
+		WL_SEAT_CAPABILITY_TOUCH);
+
 	return seat;
 }
 
+static void seat_add_pointer(struct sway_seat *seat,
+		struct wlr_input_device *device) {
+	// TODO pointer configuration
+	wlr_cursor_attach_input_device(seat->cursor->cursor, device);
+}
+
 void sway_seat_add_device(struct sway_seat *seat,
 		struct wlr_input_device *device) {
 	sway_log(L_DEBUG, "input add: %s", device->name);
+	switch (device->type) {
+		case WLR_INPUT_DEVICE_POINTER:
+			seat_add_pointer(seat, device);
+			break;
+		case WLR_INPUT_DEVICE_KEYBOARD:
+		case WLR_INPUT_DEVICE_TOUCH:
+		case WLR_INPUT_DEVICE_TABLET_PAD:
+		case WLR_INPUT_DEVICE_TABLET_TOOL:
+			sway_log(L_DEBUG, "TODO: add other devices");
+			break;
+	}
+}
+
+static void seat_remove_pointer(struct sway_seat *seat,
+		struct wlr_input_device *device) {
+	// TODO
 }
 
 void sway_seat_remove_device(struct sway_seat *seat,
 		struct wlr_input_device *device) {
 	sway_log(L_DEBUG, "input remove: %s", device->name);
+	switch (device->type) {
+		case WLR_INPUT_DEVICE_POINTER:
+			seat_remove_pointer(seat, device);
+			break;
+		case WLR_INPUT_DEVICE_KEYBOARD:
+		case WLR_INPUT_DEVICE_TOUCH:
+		case WLR_INPUT_DEVICE_TABLET_PAD:
+		case WLR_INPUT_DEVICE_TABLET_TOOL:
+			sway_log(L_DEBUG, "TODO: remove other devices");
+			break;
+	}
 }
diff --git a/sway/meson.build b/sway/meson.build
index cea565d6..059204b2 100644
--- a/sway/meson.build
+++ b/sway/meson.build
@@ -4,6 +4,7 @@ sway_sources = files(
 	'commands.c',
 	'input/input-manager.c',
 	'input/seat.c',
+	'input/cursor.c',
 	'commands/exit.c',
 	'commands/exec.c',
 	'commands/exec_always.c',

From d76e745b738281fb98834fd5dee78f2a21727d80 Mon Sep 17 00:00:00 2001
From: Tony Crisci <tony@dubstepdish.com>
Date: Fri, 8 Dec 2017 08:07:47 -0500
Subject: [PATCH 05/49] input include directory

---
 include/sway/{ => input}/cursor.h        | 2 +-
 include/sway/{ => input}/input-manager.h | 2 +-
 include/sway/{ => input}/seat.h          | 2 +-
 sway/input/cursor.c                      | 2 +-
 sway/input/input-manager.c               | 4 ++--
 sway/input/seat.c                        | 6 +++---
 sway/server.c                            | 2 +-
 7 files changed, 10 insertions(+), 10 deletions(-)
 rename include/sway/{ => input}/cursor.h (94%)
 rename include/sway/{ => input}/input-manager.h (95%)
 rename include/sway/{ => input}/seat.h (91%)

diff --git a/include/sway/cursor.h b/include/sway/input/cursor.h
similarity index 94%
rename from include/sway/cursor.h
rename to include/sway/input/cursor.h
index 647bc8f1..91421964 100644
--- a/include/sway/cursor.h
+++ b/include/sway/input/cursor.h
@@ -1,7 +1,7 @@
 #ifndef _SWAY_CURSOR_H
 #define _SWAY_CURSOR_H
 
-#include "sway/seat.h"
+#include "sway/input/seat.h"
 
 struct sway_cursor {
 	struct wlr_cursor *cursor;
diff --git a/include/sway/input-manager.h b/include/sway/input/input-manager.h
similarity index 95%
rename from include/sway/input-manager.h
rename to include/sway/input/input-manager.h
index 4c01a043..5dc75ba7 100644
--- a/include/sway/input-manager.h
+++ b/include/sway/input/input-manager.h
@@ -2,7 +2,7 @@
 #define _SWAY_INPUT_MANAGER_H
 #include <libinput.h>
 #include "sway/server.h"
-#include "config.h"
+#include "sway/config.h"
 #include "list.h"
 
 struct sway_input_manager {
diff --git a/include/sway/seat.h b/include/sway/input/seat.h
similarity index 91%
rename from include/sway/seat.h
rename to include/sway/input/seat.h
index 2f8ca72e..a84b7efd 100644
--- a/include/sway/seat.h
+++ b/include/sway/input/seat.h
@@ -2,7 +2,7 @@
 #define _SWAY_SEAT_H
 
 #include <wlr/types/wlr_seat.h>
-#include "sway/input-manager.h"
+#include "sway/input/input-manager.h"
 
 struct sway_seat {
 	struct wlr_seat *seat;
diff --git a/sway/input/cursor.c b/sway/input/cursor.c
index 819007d5..85b7865d 100644
--- a/sway/input/cursor.c
+++ b/sway/input/cursor.c
@@ -1,6 +1,6 @@
 #define _XOPEN_SOURCE 700
 #include <wlr/types/wlr_cursor.h>
-#include "sway/cursor.h"
+#include "sway/input/cursor.h"
 #include "log.h"
 
 static void handle_cursor_motion(struct wl_listener *listener, void *data) {
diff --git a/sway/input/input-manager.c b/sway/input/input-manager.c
index b5ab8cc1..4f52e59a 100644
--- a/sway/input/input-manager.c
+++ b/sway/input/input-manager.c
@@ -6,8 +6,8 @@
 #include <string.h>
 #include <libinput.h>
 #include "sway/config.h"
-#include "sway/input-manager.h"
-#include "sway/seat.h"
+#include "sway/input/input-manager.h"
+#include "sway/input/seat.h"
 #include "sway/server.h"
 #include "list.h"
 #include "log.h"
diff --git a/sway/input/seat.c b/sway/input/seat.c
index 459b2ee2..1a2b728c 100644
--- a/sway/input/seat.c
+++ b/sway/input/seat.c
@@ -1,8 +1,8 @@
 #define _XOPEN_SOURCE 700
 #include <wlr/types/wlr_cursor.h>
-#include "sway/seat.h"
-#include "sway/cursor.h"
-#include "sway/input-manager.h"
+#include "sway/input/seat.h"
+#include "sway/input/cursor.h"
+#include "sway/input/input-manager.h"
 #include "log.h"
 
 struct sway_seat *sway_seat_create(struct wl_display *display,
diff --git a/sway/server.c b/sway/server.c
index 174beac6..7b9a5e8e 100644
--- a/sway/server.c
+++ b/sway/server.c
@@ -11,7 +11,7 @@
 // TODO WLR: make Xwayland optional
 #include <wlr/xwayland.h>
 #include "sway/server.h"
-#include "sway/input-manager.h"
+#include "sway/input/input-manager.h"
 #include "log.h"
 
 bool server_init(struct sway_server *server) {

From 7c67bea942d44b93cf03c3223067d2668905a3c0 Mon Sep 17 00:00:00 2001
From: Tony Crisci <tony@dubstepdish.com>
Date: Sat, 9 Dec 2017 11:51:28 -0500
Subject: [PATCH 06/49] sway xcursor manager

---
 include/sway/input/cursor.h |  1 +
 include/sway/input/seat.h   |  2 ++
 sway/input/seat.c           | 38 +++++++++++++++++++++++++++++++++++++
 3 files changed, 41 insertions(+)

diff --git a/include/sway/input/cursor.h b/include/sway/input/cursor.h
index 91421964..aa873f46 100644
--- a/include/sway/input/cursor.h
+++ b/include/sway/input/cursor.h
@@ -5,6 +5,7 @@
 
 struct sway_cursor {
 	struct wlr_cursor *cursor;
+	struct wlr_xcursor_manager *xcursor_manager;
 
 	struct wl_listener motion;
 	struct wl_listener motion_absolute;
diff --git a/include/sway/input/seat.h b/include/sway/input/seat.h
index a84b7efd..f7f8a1bb 100644
--- a/include/sway/input/seat.h
+++ b/include/sway/input/seat.h
@@ -18,4 +18,6 @@ void sway_seat_add_device(struct sway_seat *seat,
 void sway_seat_remove_device(struct sway_seat *seat,
 		struct wlr_input_device *device);
 
+void sway_seat_configure_xcursor(struct sway_seat *seat);
+
 #endif
diff --git a/sway/input/seat.c b/sway/input/seat.c
index 1a2b728c..1fd65980 100644
--- a/sway/input/seat.c
+++ b/sway/input/seat.c
@@ -1,8 +1,10 @@
 #define _XOPEN_SOURCE 700
 #include <wlr/types/wlr_cursor.h>
+#include <wlr/types/wlr_xcursor_manager.h>
 #include "sway/input/seat.h"
 #include "sway/input/cursor.h"
 #include "sway/input/input-manager.h"
+#include "sway/output.h"
 #include "log.h"
 
 struct sway_seat *sway_seat_create(struct wl_display *display,
@@ -29,6 +31,8 @@ struct sway_seat *sway_seat_create(struct wl_display *display,
 		WL_SEAT_CAPABILITY_POINTER |
 		WL_SEAT_CAPABILITY_TOUCH);
 
+	sway_seat_configure_xcursor(seat);
+
 	return seat;
 }
 
@@ -74,3 +78,37 @@ void sway_seat_remove_device(struct sway_seat *seat,
 			break;
 	}
 }
+
+void sway_seat_configure_xcursor(struct sway_seat *seat) {
+	// TODO configure theme and size
+	const char *cursor_theme = "default";
+
+	if (seat->cursor->xcursor_manager) {
+		wlr_xcursor_manager_destroy(seat->cursor->xcursor_manager);
+	}
+
+	seat->cursor->xcursor_manager =
+		wlr_xcursor_manager_create(NULL, 24);
+	if (sway_assert(seat->cursor->xcursor_manager,
+				"Cannot create XCursor manager for theme %s", cursor_theme)) {
+		return;
+	}
+
+	for (int i = 0; i < root_container.children->length; ++i) {
+		swayc_t *output_container = root_container.children->items[i];
+		struct wlr_output *output =
+			output_container->sway_output->wlr_output;
+		bool result =
+			wlr_xcursor_manager_load(seat->cursor->xcursor_manager,
+				output->scale);
+
+		sway_assert(result,
+			"Cannot load xcursor theme for output '%s' with scale %d",
+			output->name, output->scale);
+	}
+
+	wlr_xcursor_manager_set_cursor_image(seat->cursor->xcursor_manager,
+		"left_ptr", seat->cursor->cursor);
+	wlr_cursor_warp(seat->cursor->cursor, NULL, seat->cursor->cursor->x,
+		seat->cursor->cursor->y);
+}

From 9333a7eb5329073aecfaf776c8ee0572c7dff67c Mon Sep 17 00:00:00 2001
From: Tony Crisci <tony@dubstepdish.com>
Date: Sat, 9 Dec 2017 14:06:00 -0500
Subject: [PATCH 07/49] working xcursor

---
 sway/desktop/output.c |  7 +++++++
 sway/input/cursor.c   |  8 +++++++-
 sway/input/seat.c     | 18 ++++++++----------
 sway/main.c           |  3 ++-
 4 files changed, 24 insertions(+), 12 deletions(-)

diff --git a/sway/desktop/output.c b/sway/desktop/output.c
index 7eb48bdf..d2003834 100644
--- a/sway/desktop/output.c
+++ b/sway/desktop/output.c
@@ -12,6 +12,8 @@
 #include "sway/output.h"
 #include "sway/server.h"
 #include "sway/view.h"
+#include "sway/input/input-manager.h"
+#include "sway/input/seat.h"
 
 static void output_frame_view(swayc_t *view, void *data) {
 	struct sway_output *output = data;
@@ -120,6 +122,11 @@ void output_add_notify(struct wl_listener *listener, void *data) {
 	output->resolution.notify = output_resolution_notify;
 	wl_signal_add(&wlr_output->events.resolution, &output->resolution);
 
+    for (int i = 0; i < server->input->seats->length; ++i) {
+        struct sway_seat *seat = server->input->seats->items[i];
+        sway_seat_configure_xcursor(seat);
+    }
+
 	arrange_windows(output->swayc, -1, -1);
 }
 
diff --git a/sway/input/cursor.c b/sway/input/cursor.c
index 85b7865d..4f0344be 100644
--- a/sway/input/cursor.c
+++ b/sway/input/cursor.c
@@ -1,5 +1,6 @@
 #define _XOPEN_SOURCE 700
 #include <wlr/types/wlr_cursor.h>
+#include <wlr/types/wlr_xcursor_manager.h>
 #include "sway/input/cursor.h"
 #include "log.h"
 
@@ -7,7 +8,10 @@ static void handle_cursor_motion(struct wl_listener *listener, void *data) {
 	struct sway_cursor *cursor =
 		wl_container_of(listener, cursor, motion);
 	struct wlr_event_pointer_motion *event = data;
-	sway_log(L_DEBUG, "TODO: handle event: %p", event);
+	sway_log(L_DEBUG, "TODO: handle cursor motion event: dx=%f, dy=%f", event->delta_x, event->delta_y);
+	wlr_cursor_move(cursor->cursor, event->device, event->delta_x, event->delta_y);
+	sway_log(L_DEBUG, "TODO: new x=%f, y=%f", cursor->cursor->x, cursor->cursor->y);
+	wlr_xcursor_manager_set_cursor_image(cursor->xcursor_manager, "left_ptr", cursor->cursor);
 }
 
 static void handle_cursor_motion_absolute(struct wl_listener *listener,
@@ -87,6 +91,8 @@ struct sway_cursor *sway_cursor_create(struct sway_seat *seat) {
 		return NULL;
 	}
 
+	wlr_cursor_attach_output_layout(wlr_cursor, root_container.output_layout);
+
 	// input events
 	wl_signal_add(&wlr_cursor->events.motion, &cursor->motion);
 	cursor->motion.notify = handle_cursor_motion;
diff --git a/sway/input/seat.c b/sway/input/seat.c
index 1fd65980..5aed1f68 100644
--- a/sway/input/seat.c
+++ b/sway/input/seat.c
@@ -83,15 +83,13 @@ void sway_seat_configure_xcursor(struct sway_seat *seat) {
 	// TODO configure theme and size
 	const char *cursor_theme = "default";
 
-	if (seat->cursor->xcursor_manager) {
-		wlr_xcursor_manager_destroy(seat->cursor->xcursor_manager);
-	}
-
-	seat->cursor->xcursor_manager =
-		wlr_xcursor_manager_create(NULL, 24);
-	if (sway_assert(seat->cursor->xcursor_manager,
-				"Cannot create XCursor manager for theme %s", cursor_theme)) {
-		return;
+	if (!seat->cursor->xcursor_manager) {
+		seat->cursor->xcursor_manager =
+			wlr_xcursor_manager_create("default", 24);
+		if (sway_assert(seat->cursor->xcursor_manager,
+					"Cannot create XCursor manager for theme %s", cursor_theme)) {
+			return;
+		}
 	}
 
 	for (int i = 0; i < root_container.children->length; ++i) {
@@ -102,7 +100,7 @@ void sway_seat_configure_xcursor(struct sway_seat *seat) {
 			wlr_xcursor_manager_load(seat->cursor->xcursor_manager,
 				output->scale);
 
-		sway_assert(result,
+		sway_assert(!result,
 			"Cannot load xcursor theme for output '%s' with scale %d",
 			output->name, output->scale);
 	}
diff --git a/sway/main.c b/sway/main.c
index bc843591..363f4d96 100644
--- a/sway/main.c
+++ b/sway/main.c
@@ -381,11 +381,12 @@ int main(int argc, char **argv) {
 
 	sway_log(L_INFO, "Starting sway version " SWAY_VERSION "\n");
 
+	init_layout();
+
 	if (!server_init(&server)) {
 		return 1;
 	}
 
-	init_layout();
 	ipc_init(&server);
 	log_env();
 

From e69b052a6d88b1c24d5e48ad086480ee04c07c81 Mon Sep 17 00:00:00 2001
From: Tony Crisci <tony@dubstepdish.com>
Date: Sun, 10 Dec 2017 08:48:44 -0500
Subject: [PATCH 08/49] working pointer motion

---
 include/sway/container.h    |  4 +++
 include/sway/input/cursor.h |  3 ++
 sway/desktop/output.c       | 30 ++++++++++++++-----
 sway/input/cursor.c         | 42 ++++++++++++++++++++++----
 sway/tree/container.c       | 60 +++++++++++++++++++++++++++++++++++++
 5 files changed, 126 insertions(+), 13 deletions(-)

diff --git a/include/sway/container.h b/include/sway/container.h
index 08a98ed9..0e1cc8a3 100644
--- a/include/sway/container.h
+++ b/include/sway/container.h
@@ -3,6 +3,7 @@
 #include <stdint.h>
 #include <sys/types.h>
 #include <wlr/types/wlr_box.h>
+#include <wlr/types/wlr_surface.h>
 #include "list.h"
 
 typedef struct sway_container swayc_t;
@@ -136,4 +137,7 @@ swayc_t *destroy_view(swayc_t *view);
 
 swayc_t *swayc_parent_by_type(swayc_t *container, enum swayc_types type);
 
+swayc_t *swayc_at(swayc_t *parent, double lx, double ly,
+		struct wlr_surface **surface, double *sx, double *sy);
+
 #endif
diff --git a/include/sway/input/cursor.h b/include/sway/input/cursor.h
index aa873f46..cc529de6 100644
--- a/include/sway/input/cursor.h
+++ b/include/sway/input/cursor.h
@@ -4,9 +4,12 @@
 #include "sway/input/seat.h"
 
 struct sway_cursor {
+	struct sway_seat *seat;
 	struct wlr_cursor *cursor;
 	struct wlr_xcursor_manager *xcursor_manager;
 
+	double x, y;
+
 	struct wl_listener motion;
 	struct wl_listener motion_absolute;
 	struct wl_listener button;
diff --git a/sway/desktop/output.c b/sway/desktop/output.c
index d2003834..0e7f7060 100644
--- a/sway/desktop/output.c
+++ b/sway/desktop/output.c
@@ -25,8 +25,8 @@ static void output_frame_view(swayc_t *view, void *data) {
 	}
 	// TODO
 	// - Deal with wlr_output_layout
-	int width = sway_view->width;
-	int height = sway_view->height;
+	int width = sway_view->surface->current->width;
+	int height = sway_view->surface->current->height;
 	int render_width = width * wlr_output->scale;
 	int render_height = height * wlr_output->scale;
 	double ox = view->x, oy = view->y;
@@ -40,19 +40,33 @@ static void output_frame_view(swayc_t *view, void *data) {
 	//		return;
 	//}
 
+	// if the shell specifies window geometry, make the top left corner of the
+	// window in the top left corner of the container to avoid arbitrarily
+	// sized gaps based on the attached buffer size
+	int window_offset_x = 0;
+	int window_offset_y = 0;
+
+	if (view->sway_view->type == SWAY_XDG_SHELL_V6_VIEW) {
+		window_offset_x = view->sway_view->wlr_xdg_surface_v6->geometry->x;
+		window_offset_y = view->sway_view->wlr_xdg_surface_v6->geometry->y;
+	}
+
 	// TODO
 	double rotation = 0;
 	float matrix[16];
 
 	float translate_origin[16];
 	wlr_matrix_translate(&translate_origin,
-		(int)ox + render_width / 2, (int)oy + render_height / 2, 0);
+		(int)ox + render_width / 2 - window_offset_x,
+		(int)oy + render_height / 2 - window_offset_y,
+		0);
 
 	float rotate[16];
 	wlr_matrix_rotate(&rotate, rotation);
 
 	float translate_center[16];
-	wlr_matrix_translate(&translate_center, -render_width / 2,
+	wlr_matrix_translate(&translate_center,
+		-render_width / 2,
 		-render_height / 2, 0);
 
 	float scale[16];
@@ -122,10 +136,10 @@ void output_add_notify(struct wl_listener *listener, void *data) {
 	output->resolution.notify = output_resolution_notify;
 	wl_signal_add(&wlr_output->events.resolution, &output->resolution);
 
-    for (int i = 0; i < server->input->seats->length; ++i) {
-        struct sway_seat *seat = server->input->seats->items[i];
-        sway_seat_configure_xcursor(seat);
-    }
+	for (int i = 0; i < server->input->seats->length; ++i) {
+		struct sway_seat *seat = server->input->seats->items[i];
+		sway_seat_configure_xcursor(seat);
+	}
 
 	arrange_windows(output->swayc, -1, -1);
 }
diff --git a/sway/input/cursor.c b/sway/input/cursor.c
index 4f0344be..059f907d 100644
--- a/sway/input/cursor.c
+++ b/sway/input/cursor.c
@@ -2,16 +2,44 @@
 #include <wlr/types/wlr_cursor.h>
 #include <wlr/types/wlr_xcursor_manager.h>
 #include "sway/input/cursor.h"
+#include "sway/view.h"
+#include "list.h"
 #include "log.h"
 
+static void cursor_update_position(struct sway_cursor *cursor) {
+	double x = cursor->cursor->x;
+	double y = cursor->cursor->y;
+
+	wlr_xcursor_manager_set_cursor_image(cursor->xcursor_manager,
+		"left_ptr", cursor->cursor);
+
+	cursor->x = x;
+	cursor->y = y;
+}
+
+static void cursor_send_pointer_motion(struct sway_cursor *cursor,
+		uint32_t time) {
+	struct wlr_seat *seat = cursor->seat->seat;
+	struct wlr_surface *surface = NULL;
+	double sx, sy;
+	swayc_t *swayc =
+		swayc_at(&root_container, cursor->x, cursor->y, &surface, &sx, &sy);
+	if (swayc) {
+		wlr_seat_pointer_enter(seat, surface, sx, sy);
+		wlr_seat_pointer_notify_motion(seat, time, sx, sy);
+	} else {
+		wlr_seat_pointer_clear_focus(seat);
+	}
+}
+
 static void handle_cursor_motion(struct wl_listener *listener, void *data) {
 	struct sway_cursor *cursor =
 		wl_container_of(listener, cursor, motion);
 	struct wlr_event_pointer_motion *event = data;
-	sway_log(L_DEBUG, "TODO: handle cursor motion event: dx=%f, dy=%f", event->delta_x, event->delta_y);
-	wlr_cursor_move(cursor->cursor, event->device, event->delta_x, event->delta_y);
-	sway_log(L_DEBUG, "TODO: new x=%f, y=%f", cursor->cursor->x, cursor->cursor->y);
-	wlr_xcursor_manager_set_cursor_image(cursor->xcursor_manager, "left_ptr", cursor->cursor);
+	wlr_cursor_move(cursor->cursor, event->device,
+		event->delta_x, event->delta_y);
+	cursor_update_position(cursor);
+	cursor_send_pointer_motion(cursor, event->time_msec);
 }
 
 static void handle_cursor_motion_absolute(struct wl_listener *listener,
@@ -19,7 +47,10 @@ static void handle_cursor_motion_absolute(struct wl_listener *listener,
 	struct sway_cursor *cursor =
 		wl_container_of(listener, cursor, motion_absolute);
 	struct wlr_event_pointer_motion_absolute *event = data;
-	sway_log(L_DEBUG, "TODO: handle event: %p", event);
+	wlr_cursor_warp_absolute(cursor->cursor, event->device,
+		event->x_mm / event->width_mm, event->y_mm / event->height_mm);
+	cursor_update_position(cursor);
+	cursor_send_pointer_motion(cursor, event->time_msec);
 }
 
 static void handle_cursor_button(struct wl_listener *listener, void *data) {
@@ -91,6 +122,7 @@ struct sway_cursor *sway_cursor_create(struct sway_seat *seat) {
 		return NULL;
 	}
 
+	cursor->seat = seat;
 	wlr_cursor_attach_output_layout(wlr_cursor, root_container.output_layout);
 
 	// input events
diff --git a/sway/tree/container.c b/sway/tree/container.c
index e205fbcf..321ef8b1 100644
--- a/sway/tree/container.c
+++ b/sway/tree/container.c
@@ -3,6 +3,7 @@
 #include <stdlib.h>
 #include <string.h>
 #include <wlr/types/wlr_output_layout.h>
+#include <wlr/types/wlr_wl_shell.h>
 #include "sway/container.h"
 #include "sway/layout.h"
 #include "sway/output.h"
@@ -168,3 +169,62 @@ swayc_t *swayc_parent_by_type(swayc_t *container, enum swayc_types type) {
 	} while (container && container->type != type);
 	return container;
 }
+
+swayc_t *swayc_at(swayc_t *parent, double lx, double ly,
+		struct wlr_surface **surface, double *sx, double *sy) {
+	list_t *queue = create_list();
+	list_add(queue, parent);
+
+	swayc_t *swayc = NULL;
+	while (queue->length) {
+		swayc = queue->items[0];
+		list_del(queue, 0);
+		if (swayc->type == C_VIEW) {
+			struct sway_view *sview = swayc->sway_view;
+			swayc_t *soutput = swayc_parent_by_type(swayc, C_OUTPUT);
+			struct wlr_box *output_box =
+				wlr_output_layout_get_box(root_container.output_layout,
+					soutput->sway_output->wlr_output);
+			double ox = lx - output_box->x;
+			double oy = ly - output_box->y;
+			double view_sx = ox - swayc->x;
+			double view_sy = oy - swayc->y;
+			int width = swayc->sway_view->surface->current->width;
+			int height = swayc->sway_view->surface->current->height;
+
+			// TODO popups and subsurfaces
+			switch (sview->type) {
+				case SWAY_WL_SHELL_VIEW:
+					break;
+				case SWAY_XDG_SHELL_V6_VIEW:
+					// the top left corner of the sway container is the
+					// coordinate of the top left corner of the window geometry
+					view_sx += sview->wlr_xdg_surface_v6->geometry->x;
+					view_sy += sview->wlr_xdg_surface_v6->geometry->y;
+					break;
+				case SWAY_XWAYLAND_VIEW:
+					break;
+				default:
+					break;
+			}
+
+			if (view_sx > 0 && view_sx < width &&
+					view_sy > 0 && view_sy < height &&
+					pixman_region32_contains_point(
+						&sview->surface->current->input,
+						view_sx, view_sy, NULL)) {
+				*sx = view_sx;
+				*sy = view_sy;
+				*surface = swayc->sway_view->surface;
+				list_free(queue);
+				return swayc;
+			}
+		} else {
+			list_cat(queue, swayc->children);
+		}
+	}
+
+	list_free(queue);
+
+	return NULL;
+}

From 5f644d78fc77f48c7f7d839f7c2e318b51c2c6d7 Mon Sep 17 00:00:00 2001
From: Tony Crisci <tony@dubstepdish.com>
Date: Sun, 10 Dec 2017 10:08:16 -0500
Subject: [PATCH 09/49] button and axis events

---
 sway/input/cursor.c | 20 +++++++++++---------
 1 file changed, 11 insertions(+), 9 deletions(-)

diff --git a/sway/input/cursor.c b/sway/input/cursor.c
index 059f907d..5f2d650e 100644
--- a/sway/input/cursor.c
+++ b/sway/input/cursor.c
@@ -25,7 +25,7 @@ static void cursor_send_pointer_motion(struct sway_cursor *cursor,
 	swayc_t *swayc =
 		swayc_at(&root_container, cursor->x, cursor->y, &surface, &sx, &sy);
 	if (swayc) {
-		wlr_seat_pointer_enter(seat, surface, sx, sy);
+		wlr_seat_pointer_notify_enter(seat, surface, sx, sy);
 		wlr_seat_pointer_notify_motion(seat, time, sx, sy);
 	} else {
 		wlr_seat_pointer_clear_focus(seat);
@@ -57,49 +57,51 @@ static void handle_cursor_button(struct wl_listener *listener, void *data) {
 	struct sway_cursor *cursor =
 		wl_container_of(listener, cursor, button);
 	struct wlr_event_pointer_button *event = data;
-	sway_log(L_DEBUG, "TODO: handle event: %p", event);
+	wlr_seat_pointer_notify_button(cursor->seat->seat, event->time_msec,
+		event->button, event->state);
 }
 
 static void handle_cursor_axis(struct wl_listener *listener, void *data) {
 	struct sway_cursor *cursor =
 		wl_container_of(listener, cursor, axis);
 	struct wlr_event_pointer_axis *event = data;
-	sway_log(L_DEBUG, "TODO: handle event: %p", event);
+	wlr_seat_pointer_notify_axis(cursor->seat->seat, event->time_msec,
+		event->orientation, event->delta);
 }
 
 static void handle_touch_down(struct wl_listener *listener, void *data) {
 	struct sway_cursor *cursor =
 		wl_container_of(listener, cursor, touch_down);
 	struct wlr_event_touch_down *event = data;
-	sway_log(L_DEBUG, "TODO: handle event: %p", event);
+	sway_log(L_DEBUG, "TODO: handle touch down event: %p", event);
 }
 
 static void handle_touch_up(struct wl_listener *listener, void *data) {
 	struct sway_cursor *cursor =
 		wl_container_of(listener, cursor, touch_up);
 	struct wlr_event_touch_up *event = data;
-	sway_log(L_DEBUG, "TODO: handle event: %p", event);
+	sway_log(L_DEBUG, "TODO: handle touch up event: %p", event);
 }
 
 static void handle_touch_motion(struct wl_listener *listener, void *data) {
 	struct sway_cursor *cursor =
 		wl_container_of(listener, cursor, touch_motion);
 	struct wlr_event_touch_motion *event = data;
-	sway_log(L_DEBUG, "TODO: handle event: %p", event);
+	sway_log(L_DEBUG, "TODO: handle touch motion event: %p", event);
 }
 
 static void handle_tool_axis(struct wl_listener *listener, void *data) {
 	struct sway_cursor *cursor =
 		wl_container_of(listener, cursor, tool_axis);
 	struct wlr_event_tablet_tool_axis *event = data;
-	sway_log(L_DEBUG, "TODO: handle event: %p", event);
+	sway_log(L_DEBUG, "TODO: handle tool axis event: %p", event);
 }
 
 static void handle_tool_tip(struct wl_listener *listener, void *data) {
 	struct sway_cursor *cursor =
 		wl_container_of(listener, cursor, tool_tip);
 	struct wlr_event_tablet_tool_tip *event = data;
-	sway_log(L_DEBUG, "TODO: handle event: %p", event);
+	sway_log(L_DEBUG, "TODO: handle tool tip event: %p", event);
 }
 
 static void handle_request_set_cursor(struct wl_listener *listener,
@@ -107,7 +109,7 @@ static void handle_request_set_cursor(struct wl_listener *listener,
 	struct sway_cursor *cursor =
 		wl_container_of(listener, cursor, request_set_cursor);
 	struct wlr_seat_pointer_request_set_cursor_event *event = data;
-	sway_log(L_DEBUG, "TODO: handle event: %p", event);
+	sway_log(L_DEBUG, "TODO: handle request set cursor event: %p", event);
 }
 
 struct sway_cursor *sway_cursor_create(struct sway_seat *seat) {

From 21626e8153490bf155e812644454fe9610491ffd Mon Sep 17 00:00:00 2001
From: Tony Crisci <tony@dubstepdish.com>
Date: Sun, 10 Dec 2017 11:11:47 -0500
Subject: [PATCH 10/49] seat focus on button press

---
 include/sway/container.h           |  4 +++
 include/sway/input/input-manager.h |  3 ++
 include/sway/input/seat.h          |  8 +++++-
 sway/input/cursor.c                | 15 ++++++++++
 sway/input/input-manager.c         | 14 +++++++++-
 sway/input/seat.c                  | 44 ++++++++++++++++++++++++++++--
 sway/tree/container.c              |  6 ++++
 7 files changed, 90 insertions(+), 4 deletions(-)

diff --git a/include/sway/container.h b/include/sway/container.h
index 0e1cc8a3..f13745ee 100644
--- a/include/sway/container.h
+++ b/include/sway/container.h
@@ -124,6 +124,10 @@ struct sway_container {
 	 * Marks applied to the container, list_t of char*.
 	 */
 	list_t *marks;
+
+	struct {
+		struct wl_signal destroy;
+	} events;
 };
 
 void swayc_descendants_of_type(swayc_t *root, enum swayc_types type,
diff --git a/include/sway/input/input-manager.h b/include/sway/input/input-manager.h
index 5dc75ba7..9548c170 100644
--- a/include/sway/input/input-manager.h
+++ b/include/sway/input/input-manager.h
@@ -19,4 +19,7 @@ char* libinput_dev_unique_id(struct libinput_device *dev);
 struct sway_input_manager *sway_input_manager_create(
 		struct sway_server *server);
 
+bool sway_input_manager_swayc_has_focus(struct sway_input_manager *input,
+		swayc_t *container);
+
 #endif
diff --git a/include/sway/input/seat.h b/include/sway/input/seat.h
index f7f8a1bb..964c0f7b 100644
--- a/include/sway/input/seat.h
+++ b/include/sway/input/seat.h
@@ -7,9 +7,13 @@
 struct sway_seat {
 	struct wlr_seat *seat;
 	struct sway_cursor *cursor;
+	struct sway_input_manager *input;
+	swayc_t *focus;
+
+	struct wl_listener focus_destroy;
 };
 
-struct sway_seat *sway_seat_create(struct wl_display *display,
+struct sway_seat *sway_seat_create(struct sway_input_manager *input,
 		const char *seat_name);
 
 void sway_seat_add_device(struct sway_seat *seat,
@@ -20,4 +24,6 @@ void sway_seat_remove_device(struct sway_seat *seat,
 
 void sway_seat_configure_xcursor(struct sway_seat *seat);
 
+void sway_seat_set_focus(struct sway_seat *seat, swayc_t *container);
+
 #endif
diff --git a/sway/input/cursor.c b/sway/input/cursor.c
index 5f2d650e..217c2ddb 100644
--- a/sway/input/cursor.c
+++ b/sway/input/cursor.c
@@ -1,4 +1,9 @@
 #define _XOPEN_SOURCE 700
+#ifdef __linux__
+#include <linux/input-event-codes.h>
+#elif __FreeBSD__
+#include <dev/evdev/input-event-codes.h>
+#endif
 #include <wlr/types/wlr_cursor.h>
 #include <wlr/types/wlr_xcursor_manager.h>
 #include "sway/input/cursor.h"
@@ -57,6 +62,16 @@ static void handle_cursor_button(struct wl_listener *listener, void *data) {
 	struct sway_cursor *cursor =
 		wl_container_of(listener, cursor, button);
 	struct wlr_event_pointer_button *event = data;
+
+	if (event->button == BTN_LEFT) {
+		struct wlr_surface *surface = NULL;
+		double sx, sy;
+		swayc_t *swayc =
+			swayc_at(&root_container, cursor->x, cursor->y, &surface, &sx, &sy);
+
+		sway_seat_set_focus(cursor->seat, swayc);
+	}
+
 	wlr_seat_pointer_notify_button(cursor->seat->seat, event->time_msec,
 		event->button, event->state);
 }
diff --git a/sway/input/input-manager.c b/sway/input/input-manager.c
index 4f52e59a..ca80f267 100644
--- a/sway/input/input-manager.c
+++ b/sway/input/input-manager.c
@@ -27,7 +27,7 @@ static struct sway_seat *input_manager_get_seat(
 		}
 	}
 
-	seat = sway_seat_create(input->server->wl_display, seat_name);
+	seat = sway_seat_create(input, seat_name);
 	list_add(input->seats, seat);
 
 	return seat;
@@ -131,3 +131,15 @@ char *libinput_dev_unique_id(struct libinput_device *device) {
 	free(name);
 	return identifier;
 }
+
+bool sway_input_manager_swayc_has_focus(struct sway_input_manager *input,
+		swayc_t *container) {
+	for (int i = 0; i < input->seats->length; ++i) {
+		struct sway_seat *seat = input->seats->items[i];
+		if (seat->focus == container) {
+			return true;
+		}
+	}
+
+	return false;
+}
diff --git a/sway/input/seat.c b/sway/input/seat.c
index 5aed1f68..94f547cc 100644
--- a/sway/input/seat.c
+++ b/sway/input/seat.c
@@ -5,16 +5,17 @@
 #include "sway/input/cursor.h"
 #include "sway/input/input-manager.h"
 #include "sway/output.h"
+#include "sway/view.h"
 #include "log.h"
 
-struct sway_seat *sway_seat_create(struct wl_display *display,
+struct sway_seat *sway_seat_create(struct sway_input_manager *input,
 		const char *seat_name) {
 	struct sway_seat *seat = calloc(1, sizeof(struct sway_seat));
 	if (!seat) {
 		return NULL;
 	}
 
-	seat->seat = wlr_seat_create(display, seat_name);
+	seat->seat = wlr_seat_create(input->server->wl_display, seat_name);
 	if (!sway_assert(seat->seat, "could not allocate seat")) {
 		return NULL;
 	}
@@ -26,6 +27,8 @@ struct sway_seat *sway_seat_create(struct wl_display *display,
 		return NULL;
 	}
 
+	seat->input = input;
+
 	wlr_seat_set_capabilities(seat->seat,
 		WL_SEAT_CAPABILITY_KEYBOARD |
 		WL_SEAT_CAPABILITY_POINTER |
@@ -110,3 +113,40 @@ void sway_seat_configure_xcursor(struct sway_seat *seat) {
 	wlr_cursor_warp(seat->cursor->cursor, NULL, seat->cursor->cursor->x,
 		seat->cursor->cursor->y);
 }
+
+static void handle_focus_destroy(struct wl_listener *listener, void *data) {
+	struct sway_seat *seat = wl_container_of(listener, seat, focus_destroy);
+	//swayc_t *container = data;
+
+	// TODO set new focus based on the state of the tree
+	sway_seat_set_focus(seat, NULL);
+}
+
+void sway_seat_set_focus(struct sway_seat *seat, swayc_t *container) {
+	swayc_t *last_focus = seat->focus;
+
+	if (last_focus == container) {
+		return;
+	}
+
+	if (last_focus) {
+		wl_list_remove(&seat->focus_destroy.link);
+	}
+
+	if (container) {
+		struct sway_view *view = container->sway_view;
+		view->iface.set_activated(view, true);
+		wl_signal_add(&container->events.destroy, &seat->focus_destroy);
+		seat->focus_destroy.notify = handle_focus_destroy;
+		// TODO give keyboard focus
+	}
+
+	seat->focus = container;
+
+	if (last_focus &&
+			!sway_input_manager_swayc_has_focus(seat->input, last_focus)) {
+		struct sway_view *view = last_focus->sway_view;
+		view->iface.set_activated(view, false);
+
+	}
+}
diff --git a/sway/tree/container.c b/sway/tree/container.c
index 321ef8b1..78c8625f 100644
--- a/sway/tree/container.c
+++ b/sway/tree/container.c
@@ -40,6 +40,9 @@ static swayc_t *new_swayc(enum swayc_types type) {
 	if (type != C_VIEW) {
 		c->children = create_list();
 	}
+
+	wl_signal_init(&c->events.destroy);
+
 	return c;
 }
 
@@ -119,6 +122,9 @@ static void free_swayc(swayc_t *cont) {
 	if (!sway_assert(cont, "free_swayc passed NULL")) {
 		return;
 	}
+
+	wl_signal_emit(&cont->events.destroy, cont);
+
 	if (cont->children) {
 		// remove children until there are no more, free_swayc calls
 		// remove_child, which removes child from this container

From 6339c4f161a80cc00a8e57f402ca45c5dbc86937 Mon Sep 17 00:00:00 2001
From: Tony Crisci <tony@dubstepdish.com>
Date: Sun, 10 Dec 2017 13:28:04 -0500
Subject: [PATCH 11/49] remove pointer device

---
 sway/input/seat.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/sway/input/seat.c b/sway/input/seat.c
index 94f547cc..b3d36681 100644
--- a/sway/input/seat.c
+++ b/sway/input/seat.c
@@ -63,7 +63,7 @@ void sway_seat_add_device(struct sway_seat *seat,
 
 static void seat_remove_pointer(struct sway_seat *seat,
 		struct wlr_input_device *device) {
-	// TODO
+	wlr_cursor_detach_input_device(seat->cursor->cursor, device);
 }
 
 void sway_seat_remove_device(struct sway_seat *seat,

From 609f63934ab3eb925741450aa7f78db1c11bdd37 Mon Sep 17 00:00:00 2001
From: Tony Crisci <tony@dubstepdish.com>
Date: Sun, 10 Dec 2017 13:59:04 -0500
Subject: [PATCH 12/49] basic keyboard

---
 include/sway/input/keyboard.h | 13 ++++++++
 include/sway/input/seat.h     |  2 ++
 sway/input/keyboard.c         | 57 +++++++++++++++++++++++++++++++++++
 sway/input/seat.c             | 14 ++++++++-
 sway/meson.build              |  2 ++
 5 files changed, 87 insertions(+), 1 deletion(-)
 create mode 100644 include/sway/input/keyboard.h
 create mode 100644 sway/input/keyboard.c

diff --git a/include/sway/input/keyboard.h b/include/sway/input/keyboard.h
new file mode 100644
index 00000000..2e04065c
--- /dev/null
+++ b/include/sway/input/keyboard.h
@@ -0,0 +1,13 @@
+#include "sway/input/seat.h"
+
+struct sway_keyboard {
+	struct sway_seat *seat;
+	struct wlr_input_device *device;
+	struct wl_list link; // sway_seat::keyboards
+
+	struct wl_listener keyboard_key;
+	struct wl_listener keyboard_modifiers;
+};
+
+struct sway_keyboard *sway_keyboard_create(struct sway_seat *seat,
+		struct wlr_input_device *device);
diff --git a/include/sway/input/seat.h b/include/sway/input/seat.h
index 964c0f7b..a0c6ab07 100644
--- a/include/sway/input/seat.h
+++ b/include/sway/input/seat.h
@@ -10,6 +10,8 @@ struct sway_seat {
 	struct sway_input_manager *input;
 	swayc_t *focus;
 
+	struct wl_list keyboards;
+
 	struct wl_listener focus_destroy;
 };
 
diff --git a/sway/input/keyboard.c b/sway/input/keyboard.c
new file mode 100644
index 00000000..59f81e62
--- /dev/null
+++ b/sway/input/keyboard.c
@@ -0,0 +1,57 @@
+#include "sway/input/seat.h"
+#include "sway/input/keyboard.h"
+#include "log.h"
+
+static void handle_keyboard_key(struct wl_listener *listener, void *data) {
+	struct sway_keyboard *keyboard =
+		wl_container_of(listener, keyboard, keyboard_key);
+	struct wlr_event_keyboard_key *event = data;
+	wlr_seat_set_keyboard(keyboard->seat->seat, keyboard->device);
+	wlr_seat_keyboard_notify_key(keyboard->seat->seat, event->time_msec,
+		event->keycode, event->state);
+}
+
+static void handle_keyboard_modifiers(struct wl_listener *listener,
+		void *data) {
+	struct sway_keyboard *keyboard =
+		wl_container_of(listener, keyboard, keyboard_modifiers);
+	wlr_seat_set_keyboard(keyboard->seat->seat, keyboard->device);
+	wlr_seat_keyboard_notify_modifiers(keyboard->seat->seat);
+}
+
+struct sway_keyboard *sway_keyboard_create(struct sway_seat *seat,
+		struct wlr_input_device *device) {
+	struct sway_keyboard *keyboard =
+		calloc(1, sizeof(struct sway_keyboard));
+	if (!sway_assert(keyboard, "could not allocate sway keyboard")) {
+		return NULL;
+	}
+
+	keyboard->device = device;
+	keyboard->seat = seat;
+
+	// TODO keyboard config
+	struct xkb_rule_names rules;
+	memset(&rules, 0, sizeof(rules));
+	rules.rules = getenv("XKB_DEFAULT_RULES");
+	rules.model = getenv("XKB_DEFAULT_MODEL");
+	rules.layout = getenv("XKB_DEFAULT_LAYOUT");
+	rules.variant = getenv("XKB_DEFAULT_VARIANT");
+	rules.options = getenv("XKB_DEFAULT_OPTIONS");
+	struct xkb_context *context = xkb_context_new(XKB_CONTEXT_NO_FLAGS);
+	if (!sway_assert(context, "cannot create XKB context")) {
+		return NULL;
+	}
+
+	wlr_keyboard_set_keymap(device->keyboard, xkb_map_new_from_names(context,
+		&rules, XKB_KEYMAP_COMPILE_NO_FLAGS));
+	xkb_context_unref(context);
+
+	wl_signal_add(&device->keyboard->events.key, &keyboard->keyboard_key);
+	keyboard->keyboard_key.notify = handle_keyboard_key;
+
+	wl_signal_add(&device->keyboard->events.modifiers, &keyboard->keyboard_modifiers);
+	keyboard->keyboard_modifiers.notify = handle_keyboard_modifiers;
+
+	return keyboard;
+}
diff --git a/sway/input/seat.c b/sway/input/seat.c
index b3d36681..0a5329c8 100644
--- a/sway/input/seat.c
+++ b/sway/input/seat.c
@@ -4,6 +4,7 @@
 #include "sway/input/seat.h"
 #include "sway/input/cursor.h"
 #include "sway/input/input-manager.h"
+#include "sway/input/keyboard.h"
 #include "sway/output.h"
 #include "sway/view.h"
 #include "log.h"
@@ -36,6 +37,8 @@ struct sway_seat *sway_seat_create(struct sway_input_manager *input,
 
 	sway_seat_configure_xcursor(seat);
 
+	wl_list_init(&seat->keyboards);
+
 	return seat;
 }
 
@@ -45,6 +48,13 @@ static void seat_add_pointer(struct sway_seat *seat,
 	wlr_cursor_attach_input_device(seat->cursor->cursor, device);
 }
 
+static void seat_add_keyboard(struct sway_seat *seat,
+		struct wlr_input_device *device) {
+	struct sway_keyboard *keyboard = sway_keyboard_create(seat, device);
+	wl_list_insert(&seat->keyboards, &keyboard->link);
+	wlr_seat_set_keyboard(seat->seat, device);
+}
+
 void sway_seat_add_device(struct sway_seat *seat,
 		struct wlr_input_device *device) {
 	sway_log(L_DEBUG, "input add: %s", device->name);
@@ -53,6 +63,8 @@ void sway_seat_add_device(struct sway_seat *seat,
 			seat_add_pointer(seat, device);
 			break;
 		case WLR_INPUT_DEVICE_KEYBOARD:
+			seat_add_keyboard(seat, device);
+			break;
 		case WLR_INPUT_DEVICE_TOUCH:
 		case WLR_INPUT_DEVICE_TABLET_PAD:
 		case WLR_INPUT_DEVICE_TABLET_TOOL:
@@ -138,7 +150,7 @@ void sway_seat_set_focus(struct sway_seat *seat, swayc_t *container) {
 		view->iface.set_activated(view, true);
 		wl_signal_add(&container->events.destroy, &seat->focus_destroy);
 		seat->focus_destroy.notify = handle_focus_destroy;
-		// TODO give keyboard focus
+		wlr_seat_keyboard_notify_enter(seat->seat, view->surface);
 	}
 
 	seat->focus = container;
diff --git a/sway/meson.build b/sway/meson.build
index 18955693..79201f3a 100644
--- a/sway/meson.build
+++ b/sway/meson.build
@@ -5,6 +5,7 @@ sway_sources = files(
 	'input/input-manager.c',
 	'input/seat.c',
 	'input/cursor.c',
+	'input/keyboard.c',
 	'commands/exit.c',
 	'commands/exec.c',
 	'commands/exec_always.c',
@@ -30,6 +31,7 @@ sway_deps = [
 	libcap,
 	math,
 	libinput,
+	xkbcommon,
 ]
 
 executable(

From 5e9ee32d63d7a02e65f05ee00295ef41e115b2eb Mon Sep 17 00:00:00 2001
From: Tony Crisci <tony@dubstepdish.com>
Date: Sun, 10 Dec 2017 15:13:27 -0500
Subject: [PATCH 13/49] set focus on new window

---
 sway/desktop/wl_shell.c     | 7 +++++++
 sway/desktop/xdg_shell_v6.c | 7 +++++++
 sway/desktop/xwayland.c     | 7 +++++++
 3 files changed, 21 insertions(+)

diff --git a/sway/desktop/wl_shell.c b/sway/desktop/wl_shell.c
index 3f5a358a..3d3b1c44 100644
--- a/sway/desktop/wl_shell.c
+++ b/sway/desktop/wl_shell.c
@@ -7,6 +7,8 @@
 #include "sway/layout.h"
 #include "sway/server.h"
 #include "sway/view.h"
+#include "sway/input/seat.h"
+#include "sway/input/input-manager.h"
 #include "log.h"
 
 static bool assert_wl_shell(struct sway_view *view) {
@@ -126,4 +128,9 @@ void handle_wl_shell_surface(struct wl_listener *listener, void *data) {
 	sway_view->swayc = cont;
 
 	arrange_windows(cont->parent, -1, -1);
+
+	for (int i = 0; i < server->input->seats->length; ++i) {
+		struct sway_seat *seat = server->input->seats->items[i];
+		sway_seat_set_focus(seat, cont);
+	}
 }
diff --git a/sway/desktop/xdg_shell_v6.c b/sway/desktop/xdg_shell_v6.c
index 2435c256..8ad6a5ec 100644
--- a/sway/desktop/xdg_shell_v6.c
+++ b/sway/desktop/xdg_shell_v6.c
@@ -7,6 +7,8 @@
 #include "sway/layout.h"
 #include "sway/server.h"
 #include "sway/view.h"
+#include "sway/input/seat.h"
+#include "sway/input/input-manager.h"
 #include "log.h"
 
 static bool assert_xdg(struct sway_view *view) {
@@ -132,4 +134,9 @@ void handle_xdg_shell_v6_surface(struct wl_listener *listener, void *data) {
 	sway_view->swayc = cont;
 
 	arrange_windows(cont->parent, -1, -1);
+
+	for (int i = 0; i < server->input->seats->length; ++i) {
+		struct sway_seat *seat = server->input->seats->items[i];
+		sway_seat_set_focus(seat, cont);
+	}
 }
diff --git a/sway/desktop/xwayland.c b/sway/desktop/xwayland.c
index 65c7e1ec..724c8a82 100644
--- a/sway/desktop/xwayland.c
+++ b/sway/desktop/xwayland.c
@@ -10,6 +10,8 @@
 #include "sway/server.h"
 #include "sway/view.h"
 #include "sway/output.h"
+#include "sway/input/seat.h"
+#include "sway/input/input-manager.h"
 #include "log.h"
 
  static bool assert_xwayland(struct sway_view *view) {
@@ -171,4 +173,9 @@ void handle_xwayland_surface(struct wl_listener *listener, void *data) {
 	sway_view->swayc = cont;
 
 	arrange_windows(cont->parent, -1, -1);
+
+	for (int i = 0; i < server->input->seats->length; ++i) {
+		struct sway_seat *seat = server->input->seats->items[i];
+		sway_seat_set_focus(seat, cont);
+	}
 }

From 4d449743c5c476f1891a64b31f00cb7d5dd1555b Mon Sep 17 00:00:00 2001
From: Tony Crisci <tony@dubstepdish.com>
Date: Sun, 10 Dec 2017 15:37:17 -0500
Subject: [PATCH 14/49] keyboard remove

---
 include/sway/input/keyboard.h |  2 ++
 sway/input/keyboard.c         |  9 +++++++++
 sway/input/seat.c             | 31 +++++++++++++++++++++++++++++--
 3 files changed, 40 insertions(+), 2 deletions(-)

diff --git a/include/sway/input/keyboard.h b/include/sway/input/keyboard.h
index 2e04065c..19d40bdc 100644
--- a/include/sway/input/keyboard.h
+++ b/include/sway/input/keyboard.h
@@ -11,3 +11,5 @@ struct sway_keyboard {
 
 struct sway_keyboard *sway_keyboard_create(struct sway_seat *seat,
 		struct wlr_input_device *device);
+
+void sway_keyboard_destroy(struct sway_keyboard *keyboard);
diff --git a/sway/input/keyboard.c b/sway/input/keyboard.c
index 59f81e62..31d254df 100644
--- a/sway/input/keyboard.c
+++ b/sway/input/keyboard.c
@@ -53,5 +53,14 @@ struct sway_keyboard *sway_keyboard_create(struct sway_seat *seat,
 	wl_signal_add(&device->keyboard->events.modifiers, &keyboard->keyboard_modifiers);
 	keyboard->keyboard_modifiers.notify = handle_keyboard_modifiers;
 
+	wl_list_insert(&seat->keyboards, &keyboard->link);
+
 	return keyboard;
 }
+
+void sway_keyboard_destroy(struct sway_keyboard *keyboard) {
+	wl_list_remove(&keyboard->keyboard_key.link);
+	wl_list_remove(&keyboard->keyboard_modifiers.link);
+	wl_list_remove(&keyboard->link);
+	free(keyboard);
+}
diff --git a/sway/input/seat.c b/sway/input/seat.c
index 0a5329c8..7c827374 100644
--- a/sway/input/seat.c
+++ b/sway/input/seat.c
@@ -42,6 +42,18 @@ struct sway_seat *sway_seat_create(struct sway_input_manager *input,
 	return seat;
 }
 
+static struct sway_keyboard *seat_keyboard_from_device(struct sway_seat *seat,
+		struct wlr_input_device *device) {
+	struct sway_keyboard *keyboard = NULL;
+	wl_list_for_each(keyboard, &seat->keyboards, link) {
+		if (keyboard->device == device) {
+			return keyboard;
+		}
+	}
+
+	return keyboard;
+}
+
 static void seat_add_pointer(struct sway_seat *seat,
 		struct wlr_input_device *device) {
 	// TODO pointer configuration
@@ -50,8 +62,13 @@ static void seat_add_pointer(struct sway_seat *seat,
 
 static void seat_add_keyboard(struct sway_seat *seat,
 		struct wlr_input_device *device) {
-	struct sway_keyboard *keyboard = sway_keyboard_create(seat, device);
-	wl_list_insert(&seat->keyboards, &keyboard->link);
+	// TODO keyboard configuration
+	if (seat_keyboard_from_device(seat, device)) {
+		// already added
+		return;
+	}
+
+	sway_keyboard_create(seat, device);
 	wlr_seat_set_keyboard(seat->seat, device);
 }
 
@@ -73,6 +90,14 @@ void sway_seat_add_device(struct sway_seat *seat,
 	}
 }
 
+static void seat_remove_keyboard(struct sway_seat *seat,
+		struct wlr_input_device *device) {
+	struct sway_keyboard *keyboard = seat_keyboard_from_device(seat, device);
+	if (keyboard) {
+		sway_keyboard_destroy(keyboard);
+	}
+}
+
 static void seat_remove_pointer(struct sway_seat *seat,
 		struct wlr_input_device *device) {
 	wlr_cursor_detach_input_device(seat->cursor->cursor, device);
@@ -86,6 +111,8 @@ void sway_seat_remove_device(struct sway_seat *seat,
 			seat_remove_pointer(seat, device);
 			break;
 		case WLR_INPUT_DEVICE_KEYBOARD:
+			seat_remove_keyboard(seat, device);
+			break;
 		case WLR_INPUT_DEVICE_TOUCH:
 		case WLR_INPUT_DEVICE_TABLET_PAD:
 		case WLR_INPUT_DEVICE_TABLET_TOOL:

From 9ae906cd3752fd16ea2c3e046e24abb8ec8462a2 Mon Sep 17 00:00:00 2001
From: Tony Crisci <tony@dubstepdish.com>
Date: Sun, 10 Dec 2017 15:49:54 -0500
Subject: [PATCH 15/49] sway pointer

---
 include/sway/input/seat.h |  9 ++++++++-
 sway/input/seat.c         | 30 +++++++++++++++++++++++++++++-
 2 files changed, 37 insertions(+), 2 deletions(-)

diff --git a/include/sway/input/seat.h b/include/sway/input/seat.h
index a0c6ab07..5455601e 100644
--- a/include/sway/input/seat.h
+++ b/include/sway/input/seat.h
@@ -10,11 +10,18 @@ struct sway_seat {
 	struct sway_input_manager *input;
 	swayc_t *focus;
 
-	struct wl_list keyboards;
+	struct wl_list keyboards; // sway_keyboard::link
+	struct wl_list pointers; // sway_pointer::link
 
 	struct wl_listener focus_destroy;
 };
 
+struct sway_pointer {
+	struct sway_seat *seat;
+	struct wlr_input_device *device;
+	struct wl_list link;
+};
+
 struct sway_seat *sway_seat_create(struct sway_input_manager *input,
 		const char *seat_name);
 
diff --git a/sway/input/seat.c b/sway/input/seat.c
index 7c827374..9c17250d 100644
--- a/sway/input/seat.c
+++ b/sway/input/seat.c
@@ -42,6 +42,18 @@ struct sway_seat *sway_seat_create(struct sway_input_manager *input,
 	return seat;
 }
 
+static struct sway_pointer *seat_pointer_from_device(struct sway_seat *seat,
+		struct wlr_input_device *device) {
+	struct sway_pointer *pointer = NULL;
+	wl_list_for_each(pointer, &seat->pointers, link) {
+		if (pointer->device == device) {
+			return pointer;
+		}
+	}
+
+	return pointer;
+}
+
 static struct sway_keyboard *seat_keyboard_from_device(struct sway_seat *seat,
 		struct wlr_input_device *device) {
 	struct sway_keyboard *keyboard = NULL;
@@ -57,6 +69,16 @@ static struct sway_keyboard *seat_keyboard_from_device(struct sway_seat *seat,
 static void seat_add_pointer(struct sway_seat *seat,
 		struct wlr_input_device *device) {
 	// TODO pointer configuration
+	if (seat_pointer_from_device(seat, device)) {
+		// already added
+		return;
+	}
+
+	struct sway_pointer *pointer = calloc(1, sizeof(struct sway_pointer));
+	pointer->seat = seat;
+	pointer->device = device;
+	wl_list_insert(&seat->pointers, &pointer->link);
+
 	wlr_cursor_attach_input_device(seat->cursor->cursor, device);
 }
 
@@ -100,7 +122,13 @@ static void seat_remove_keyboard(struct sway_seat *seat,
 
 static void seat_remove_pointer(struct sway_seat *seat,
 		struct wlr_input_device *device) {
-	wlr_cursor_detach_input_device(seat->cursor->cursor, device);
+	struct sway_pointer *pointer = seat_pointer_from_device(seat, device);
+
+	if (pointer) {
+		wl_list_remove(&pointer->link);
+		free(pointer);
+		wlr_cursor_detach_input_device(seat->cursor->cursor, device);
+	}
 }
 
 void sway_seat_remove_device(struct sway_seat *seat,

From 462a451328a1d6f0b17d34b431d6bf3dec87c1ba Mon Sep 17 00:00:00 2001
From: Tony Crisci <tony@dubstepdish.com>
Date: Mon, 11 Dec 2017 04:17:14 -0500
Subject: [PATCH 16/49] input config

---
 include/sway/commands.h                |  4 ++
 include/sway/input/input-manager.h     |  2 +
 sway/commands.c                        | 90 ++++++++++++++++++--------
 sway/commands/input.c                  | 55 ++++++++++++++++
 sway/commands/input/accel_profile.c    | 27 ++++++++
 sway/commands/input/click_method.c     | 30 +++++++++
 sway/commands/input/drag_lock.c        | 26 ++++++++
 sway/commands/input/dwt.c              | 26 ++++++++
 sway/commands/input/events.c           | 30 +++++++++
 sway/commands/input/left_handed.c      | 26 ++++++++
 sway/commands/input/middle_emulation.c | 26 ++++++++
 sway/commands/input/natural_scroll.c   | 26 ++++++++
 sway/commands/input/pointer_accel.c    | 24 +++++++
 sway/commands/input/scroll_method.c    | 30 +++++++++
 sway/commands/input/tap.c              | 29 +++++++++
 sway/config.c                          | 53 +++++++++++++++
 sway/meson.build                       | 12 ++++
 17 files changed, 490 insertions(+), 26 deletions(-)
 create mode 100644 sway/commands/input.c
 create mode 100644 sway/commands/input/accel_profile.c
 create mode 100644 sway/commands/input/click_method.c
 create mode 100644 sway/commands/input/drag_lock.c
 create mode 100644 sway/commands/input/dwt.c
 create mode 100644 sway/commands/input/events.c
 create mode 100644 sway/commands/input/left_handed.c
 create mode 100644 sway/commands/input/middle_emulation.c
 create mode 100644 sway/commands/input/natural_scroll.c
 create mode 100644 sway/commands/input/pointer_accel.c
 create mode 100644 sway/commands/input/scroll_method.c
 create mode 100644 sway/commands/input/tap.c

diff --git a/include/sway/commands.h b/include/sway/commands.h
index b1f0423d..138e3c29 100644
--- a/include/sway/commands.h
+++ b/include/sway/commands.h
@@ -1,6 +1,8 @@
 #ifndef _SWAY_COMMANDS_H
 #define _SWAY_COMMANDS_H
 
+#include "config.h"
+
 /**
  * Indicates the result of a command's execution.
  */
@@ -39,6 +41,8 @@ enum expected_args {
 	EXPECTED_EQUAL_TO
 };
 
+void input_cmd_apply(struct input_config *input);
+
 struct cmd_results *checkarg(int argc, const char *name,
 		enum expected_args type, int val);
 
diff --git a/include/sway/input/input-manager.h b/include/sway/input/input-manager.h
index 9548c170..78bc161f 100644
--- a/include/sway/input/input-manager.h
+++ b/include/sway/input/input-manager.h
@@ -5,6 +5,8 @@
 #include "sway/config.h"
 #include "list.h"
 
+extern struct input_config *current_input_config;
+
 struct sway_input_manager {
 	struct wl_listener input_add;
 	struct wl_listener input_remove;
diff --git a/sway/commands.c b/sway/commands.c
index 05a66a7f..7710c6ab 100644
--- a/sway/commands.c
+++ b/sway/commands.c
@@ -9,6 +9,7 @@
 #include "sway/commands.h"
 #include "sway/config.h"
 #include "sway/security.h"
+#include "sway/input/input-manager.h"
 #include "stringop.h"
 #include "log.h"
 
@@ -56,6 +57,44 @@ struct cmd_results *checkarg(int argc, const char *name, enum expected_args type
 	return error;
 }
 
+void input_cmd_apply(struct input_config *input) {
+	int i;
+	i = list_seq_find(config->input_configs, input_identifier_cmp, input->identifier);
+	if (i >= 0) {
+		// merge existing config
+		struct input_config *ic = config->input_configs->items[i];
+		merge_input_config(ic, input);
+		free_input_config(input);
+		input = ic;
+	} else {
+		list_add(config->input_configs, input);
+	}
+
+	current_input_config = input;
+
+	if (input->identifier) {
+		// Try to find the input device and apply configuration now. If
+		// this is during startup then there will be no container and config
+		// will be applied during normal "new input" event from wlc.
+		/* TODO WLR
+		struct libinput_device *device = NULL;
+		for (int i = 0; i < input_devices->length; ++i) {
+			device = input_devices->items[i];
+			char* dev_identifier = libinput_dev_unique_id(device);
+			if (!dev_identifier) {
+				break;
+			}
+			int match = dev_identifier && strcmp(dev_identifier, input->identifier) == 0;
+			free(dev_identifier);
+			if (match) {
+				apply_input_config(input, device);
+				break;
+			}
+		}
+		*/
+	}
+}
+
 /**
  * Check and add color to buffer.
  *
@@ -96,6 +135,7 @@ static struct cmd_handler handlers[] = {
 	{ "exec_always", cmd_exec_always },
 	{ "exit", cmd_exit },
 	{ "include", cmd_include },
+	{ "input", cmd_input },
 };
 
 static int handler_compare(const void *_a, const void *_b) {
@@ -104,37 +144,35 @@ static int handler_compare(const void *_a, const void *_b) {
 	return strcasecmp(a->command, b->command);
 }
 
+static struct cmd_handler input_handlers[] = {
+	{ "accel_profile", input_cmd_accel_profile },
+	{ "click_method", input_cmd_click_method },
+	{ "drag_lock", input_cmd_drag_lock },
+	{ "dwt", input_cmd_dwt },
+	{ "events", input_cmd_events },
+	{ "left_handed", input_cmd_left_handed },
+	{ "middle_emulation", input_cmd_middle_emulation },
+	{ "natural_scroll", input_cmd_natural_scroll },
+	{ "pointer_accel", input_cmd_pointer_accel },
+	{ "scroll_method", input_cmd_scroll_method },
+	{ "tap", input_cmd_tap },
+};
+
 static struct cmd_handler *find_handler(char *line, enum cmd_status block) {
 	struct cmd_handler d = { .command=line };
 	struct cmd_handler *res = NULL;
 	sway_log(L_DEBUG, "find_handler(%s) %d", line, block == CMD_BLOCK_INPUT);
-	/* TODO
-	if (block == CMD_BLOCK_BAR) {
-		res = bsearch(&d, bar_handlers,
-			sizeof(bar_handlers) / sizeof(struct cmd_handler),
-			sizeof(struct cmd_handler), handler_compare);
-	} else if (block == CMD_BLOCK_BAR_COLORS){
-		res = bsearch(&d, bar_colors_handlers,
-			sizeof(bar_colors_handlers) / sizeof(struct cmd_handler),
-			sizeof(struct cmd_handler), handler_compare);
-	} else if (block == CMD_BLOCK_INPUT) {
+
+	if (block == CMD_BLOCK_INPUT) {
 		res = bsearch(&d, input_handlers,
-			sizeof(input_handlers) / sizeof(struct cmd_handler),
-			sizeof(struct cmd_handler), handler_compare);
-	} else if (block == CMD_BLOCK_IPC) {
-		res = bsearch(&d, ipc_handlers,
-			sizeof(ipc_handlers) / sizeof(struct cmd_handler),
-			sizeof(struct cmd_handler), handler_compare);
-	} else if (block == CMD_BLOCK_IPC_EVENTS) {
-		res = bsearch(&d, ipc_event_handlers,
-			sizeof(ipc_event_handlers) / sizeof(struct cmd_handler),
-			sizeof(struct cmd_handler), handler_compare);
+				sizeof(input_handlers) / sizeof(struct cmd_handler),
+				sizeof(struct cmd_handler), handler_compare);
 	} else {
-	*/
 		res = bsearch(&d, handlers,
-			sizeof(handlers) / sizeof(struct cmd_handler),
-			sizeof(struct cmd_handler), handler_compare);
-	//}
+				sizeof(handlers) / sizeof(struct cmd_handler),
+				sizeof(struct cmd_handler), handler_compare);
+	}
+
 	return res;
 }
 
@@ -238,8 +276,8 @@ struct cmd_results *config_command(char *exec, enum cmd_status block) {
 		argv[i] = do_var_replacement(argv[i]);
 		unescape_string(argv[i]);
 	}
-	/* Strip quotes for first argument.
-	 * TODO This part needs to be handled much better */
+	// Strip quotes for first argument.
+	// TODO This part needs to be handled much better
 	if (argc>1 && (*argv[1] == '\"' || *argv[1] == '\'')) {
 		strip_quotes(argv[1]);
 	}
diff --git a/sway/commands/input.c b/sway/commands/input.c
new file mode 100644
index 00000000..5ca9c2e6
--- /dev/null
+++ b/sway/commands/input.c
@@ -0,0 +1,55 @@
+#include <string.h>
+#include <strings.h>
+#include "sway/commands.h"
+#include "sway/input/input-manager.h"
+#include "log.h"
+
+struct cmd_results *cmd_input(int argc, char **argv) {
+	struct cmd_results *error = NULL;
+	if ((error = checkarg(argc, "input", EXPECTED_AT_LEAST, 2))) {
+		return error;
+	}
+
+	if (config->reading && strcmp("{", argv[1]) == 0) {
+		current_input_config = new_input_config(argv[0]);
+		sway_log(L_DEBUG, "entering input block: %s", current_input_config->identifier);
+		return cmd_results_new(CMD_BLOCK_INPUT, NULL, NULL);
+	}
+
+	if (argc > 2) {
+		int argc_new = argc-2;
+		char **argv_new = argv+2;
+
+		struct cmd_results *res;
+		current_input_config = new_input_config(argv[0]);
+		if (strcasecmp("accel_profile", argv[1]) == 0) {
+			res = input_cmd_accel_profile(argc_new, argv_new);
+		} else if (strcasecmp("click_method", argv[1]) == 0) {
+			res = input_cmd_click_method(argc_new, argv_new);
+		} else if (strcasecmp("drag_lock", argv[1]) == 0) {
+			res = input_cmd_drag_lock(argc_new, argv_new);
+		} else if (strcasecmp("dwt", argv[1]) == 0) {
+			res = input_cmd_dwt(argc_new, argv_new);
+		} else if (strcasecmp("events", argv[1]) == 0) {
+			res = input_cmd_events(argc_new, argv_new);
+		} else if (strcasecmp("left_handed", argv[1]) == 0) {
+			res = input_cmd_left_handed(argc_new, argv_new);
+		} else if (strcasecmp("middle_emulation", argv[1]) == 0) {
+			res = input_cmd_middle_emulation(argc_new, argv_new);
+		} else if (strcasecmp("natural_scroll", argv[1]) == 0) {
+			res = input_cmd_natural_scroll(argc_new, argv_new);
+		} else if (strcasecmp("pointer_accel", argv[1]) == 0) {
+			res = input_cmd_pointer_accel(argc_new, argv_new);
+		} else if (strcasecmp("scroll_method", argv[1]) == 0) {
+			res = input_cmd_scroll_method(argc_new, argv_new);
+		} else if (strcasecmp("tap", argv[1]) == 0) {
+			res = input_cmd_tap(argc_new, argv_new);
+		} else {
+			res = cmd_results_new(CMD_INVALID, "input <device>", "Unknown command %s", argv[1]);
+		}
+		current_input_config = NULL;
+		return res;
+	}
+
+	return cmd_results_new(CMD_BLOCK_INPUT, NULL, NULL);
+}
diff --git a/sway/commands/input/accel_profile.c b/sway/commands/input/accel_profile.c
new file mode 100644
index 00000000..13ded431
--- /dev/null
+++ b/sway/commands/input/accel_profile.c
@@ -0,0 +1,27 @@
+#include <string.h>
+#include <strings.h>
+#include "sway/commands.h"
+#include "sway/input/input-manager.h"
+
+struct cmd_results *input_cmd_accel_profile(int argc, char **argv) {
+	struct cmd_results *error = NULL;
+	if ((error = checkarg(argc, "accel_profile", EXPECTED_AT_LEAST, 1))) {
+		return error;
+	}
+	if (!current_input_config) {
+		return cmd_results_new(CMD_FAILURE, "accel_profile", "No input device defined.");
+	}
+	struct input_config *new_config = new_input_config(current_input_config->identifier);
+
+	if (strcasecmp(argv[0], "adaptive") == 0) {
+		new_config->accel_profile = LIBINPUT_CONFIG_ACCEL_PROFILE_ADAPTIVE;
+	} else if (strcasecmp(argv[0], "flat") == 0) {
+		new_config->accel_profile = LIBINPUT_CONFIG_ACCEL_PROFILE_FLAT;
+	} else {
+		return cmd_results_new(CMD_INVALID, "accel_profile",
+				"Expected 'accel_profile <adaptive|flat>'");
+	}
+
+	input_cmd_apply(new_config);
+	return cmd_results_new(CMD_SUCCESS, NULL, NULL);
+}
diff --git a/sway/commands/input/click_method.c b/sway/commands/input/click_method.c
new file mode 100644
index 00000000..40f075ce
--- /dev/null
+++ b/sway/commands/input/click_method.c
@@ -0,0 +1,30 @@
+#include <string.h>
+#include <strings.h>
+#include "sway/commands.h"
+#include "sway/input/input-manager.h"
+#include "log.h"
+
+struct cmd_results *input_cmd_click_method(int argc, char **argv) {
+	sway_log(L_DEBUG, "click_method for device:  %d %s", current_input_config==NULL, current_input_config->identifier);
+	struct cmd_results *error = NULL;
+	if ((error = checkarg(argc, "click_method", EXPECTED_AT_LEAST, 1))) {
+		return error;
+	}
+	if (!current_input_config) {
+		return cmd_results_new(CMD_FAILURE, "click_method", "No input device defined.");
+	}
+	struct input_config *new_config = new_input_config(current_input_config->identifier);
+
+	if (strcasecmp(argv[0], "none") == 0) {
+		new_config->click_method = LIBINPUT_CONFIG_CLICK_METHOD_NONE;
+	} else if (strcasecmp(argv[0], "button_areas") == 0) {
+		new_config->click_method = LIBINPUT_CONFIG_CLICK_METHOD_BUTTON_AREAS;
+	} else if (strcasecmp(argv[0], "clickfinger") == 0) {
+		new_config->click_method = LIBINPUT_CONFIG_CLICK_METHOD_CLICKFINGER;
+	} else {
+		return cmd_results_new(CMD_INVALID, "click_method", "Expected 'click_method <none|button_areas|clickfinger'");
+	}
+
+	input_cmd_apply(new_config);
+	return cmd_results_new(CMD_SUCCESS, NULL, NULL);
+}
diff --git a/sway/commands/input/drag_lock.c b/sway/commands/input/drag_lock.c
new file mode 100644
index 00000000..11e7fbea
--- /dev/null
+++ b/sway/commands/input/drag_lock.c
@@ -0,0 +1,26 @@
+#include <string.h>
+#include <strings.h>
+#include "sway/commands.h"
+#include "sway/input/input-manager.h"
+
+struct cmd_results *input_cmd_drag_lock(int argc, char **argv) {
+	struct cmd_results *error = NULL;
+	if ((error = checkarg(argc, "drag_lock", EXPECTED_AT_LEAST, 1))) {
+		return error;
+	}
+	if (!current_input_config) {
+		return cmd_results_new(CMD_FAILURE, "drag_lock", "No input device defined.");
+	}
+	struct input_config *new_config = new_input_config(current_input_config->identifier);
+
+	if (strcasecmp(argv[0], "enabled") == 0) {
+		new_config->drag_lock = LIBINPUT_CONFIG_DRAG_LOCK_ENABLED;
+	} else if (strcasecmp(argv[0], "disabled") == 0) {
+		new_config->drag_lock = LIBINPUT_CONFIG_DRAG_LOCK_DISABLED;
+	} else {
+		return cmd_results_new(CMD_INVALID, "drag_lock", "Expected 'drag_lock <enabled|disabled>'");
+	}
+
+	input_cmd_apply(new_config);
+	return cmd_results_new(CMD_SUCCESS, NULL, NULL);
+}
diff --git a/sway/commands/input/dwt.c b/sway/commands/input/dwt.c
new file mode 100644
index 00000000..f3cbf252
--- /dev/null
+++ b/sway/commands/input/dwt.c
@@ -0,0 +1,26 @@
+#include <string.h>
+#include <strings.h>
+#include "sway/commands.h"
+#include "sway/input/input-manager.h"
+
+struct cmd_results *input_cmd_dwt(int argc, char **argv) {
+	struct cmd_results *error = NULL;
+	if ((error = checkarg(argc, "dwt", EXPECTED_AT_LEAST, 1))) {
+		return error;
+	}
+	if (!current_input_config) {
+		return cmd_results_new(CMD_FAILURE, "dwt", "No input device defined.");
+	}
+	struct input_config *new_config = new_input_config(current_input_config->identifier);
+
+	if (strcasecmp(argv[0], "enabled") == 0) {
+		new_config->dwt = LIBINPUT_CONFIG_DWT_ENABLED;
+	} else if (strcasecmp(argv[0], "disabled") == 0) {
+		new_config->dwt = LIBINPUT_CONFIG_DWT_DISABLED;
+	} else {
+		return cmd_results_new(CMD_INVALID, "dwt", "Expected 'dwt <enabled|disabled>'");
+	}
+
+	input_cmd_apply(new_config);
+	return cmd_results_new(CMD_SUCCESS, NULL, NULL);
+}
diff --git a/sway/commands/input/events.c b/sway/commands/input/events.c
new file mode 100644
index 00000000..4b2fdff5
--- /dev/null
+++ b/sway/commands/input/events.c
@@ -0,0 +1,30 @@
+#include <string.h>
+#include <strings.h>
+#include "sway/commands.h"
+#include "sway/input/input-manager.h"
+#include "log.h"
+
+struct cmd_results *input_cmd_events(int argc, char **argv) {
+	sway_log(L_DEBUG, "events for device: %s", current_input_config->identifier);
+	struct cmd_results *error = NULL;
+	if ((error = checkarg(argc, "events", EXPECTED_AT_LEAST, 1))) {
+		return error;
+	}
+	if (!current_input_config) {
+		return cmd_results_new(CMD_FAILURE, "events", "No input device defined.");
+	}
+	struct input_config *new_config = new_input_config(current_input_config->identifier);
+
+	if (strcasecmp(argv[0], "enabled") == 0) {
+		new_config->send_events = LIBINPUT_CONFIG_SEND_EVENTS_ENABLED;
+	} else if (strcasecmp(argv[0], "disabled") == 0) {
+		new_config->send_events = LIBINPUT_CONFIG_SEND_EVENTS_DISABLED;
+	} else if (strcasecmp(argv[0], "disabled_on_external_mouse") == 0) {
+		new_config->send_events = LIBINPUT_CONFIG_SEND_EVENTS_DISABLED_ON_EXTERNAL_MOUSE;
+	} else {
+		return cmd_results_new(CMD_INVALID, "events", "Expected 'events <enabled|disabled|disabled_on_external_mouse>'");
+	}
+
+	input_cmd_apply(new_config);
+	return cmd_results_new(CMD_SUCCESS, NULL, NULL);
+}
diff --git a/sway/commands/input/left_handed.c b/sway/commands/input/left_handed.c
new file mode 100644
index 00000000..715df2a1
--- /dev/null
+++ b/sway/commands/input/left_handed.c
@@ -0,0 +1,26 @@
+#include <string.h>
+#include <strings.h>
+#include "sway/commands.h"
+#include "sway/input/input-manager.h"
+
+struct cmd_results *input_cmd_left_handed(int argc, char **argv) {
+	struct cmd_results *error = NULL;
+	if ((error = checkarg(argc, "left_handed", EXPECTED_AT_LEAST, 1))) {
+		return error;
+	}
+	if (!current_input_config) {
+		return cmd_results_new(CMD_FAILURE, "left_handed", "No input device defined.");
+	}
+	struct input_config *new_config = new_input_config(current_input_config->identifier);
+
+	if (strcasecmp(argv[0], "enabled") == 0) {
+		new_config->left_handed = 1;
+	} else if (strcasecmp(argv[0], "disabled") == 0) {
+		new_config->left_handed = 0;
+	} else {
+		return cmd_results_new(CMD_INVALID, "left_handed", "Expected 'left_handed <enabled|disabled>'");
+	}
+
+	input_cmd_apply(new_config);
+	return cmd_results_new(CMD_SUCCESS, NULL, NULL);
+}
diff --git a/sway/commands/input/middle_emulation.c b/sway/commands/input/middle_emulation.c
new file mode 100644
index 00000000..d31ce950
--- /dev/null
+++ b/sway/commands/input/middle_emulation.c
@@ -0,0 +1,26 @@
+#include <string.h>
+#include <strings.h>
+#include "sway/commands.h"
+#include "sway/input/input-manager.h"
+
+struct cmd_results *input_cmd_middle_emulation(int argc, char **argv) {
+	struct cmd_results *error = NULL;
+	if ((error = checkarg(argc, "middle_emulation", EXPECTED_AT_LEAST, 1))) {
+		return error;
+	}
+	if (!current_input_config) {
+		return cmd_results_new(CMD_FAILURE, "middle_emulation", "No input device defined.");
+	}
+	struct input_config *new_config = new_input_config(current_input_config->identifier);
+
+	if (strcasecmp(argv[0], "enabled") == 0) {
+		new_config->middle_emulation = LIBINPUT_CONFIG_MIDDLE_EMULATION_ENABLED;
+	} else if (strcasecmp(argv[0], "disabled") == 0) {
+		new_config->middle_emulation = LIBINPUT_CONFIG_MIDDLE_EMULATION_DISABLED;
+	} else {
+		return cmd_results_new(CMD_INVALID, "middle_emulation", "Expected 'middle_emulation <enabled|disabled>'");
+	}
+
+	input_cmd_apply(new_config);
+	return cmd_results_new(CMD_SUCCESS, NULL, NULL);
+}
diff --git a/sway/commands/input/natural_scroll.c b/sway/commands/input/natural_scroll.c
new file mode 100644
index 00000000..9d1dc506
--- /dev/null
+++ b/sway/commands/input/natural_scroll.c
@@ -0,0 +1,26 @@
+#include <string.h>
+#include <strings.h>
+#include "sway/commands.h"
+#include "sway/input/input-manager.h"
+
+struct cmd_results *input_cmd_natural_scroll(int argc, char **argv) {
+	struct cmd_results *error = NULL;
+	if ((error = checkarg(argc, "natural_scroll", EXPECTED_AT_LEAST, 1))) {
+		return error;
+	}
+	if (!current_input_config) {
+		return cmd_results_new(CMD_FAILURE, "natural_scoll", "No input device defined.");
+	}
+	struct input_config *new_config = new_input_config(current_input_config->identifier);
+
+	if (strcasecmp(argv[0], "enabled") == 0) {
+		new_config->natural_scroll = 1;
+	} else if (strcasecmp(argv[0], "disabled") == 0) {
+		new_config->natural_scroll = 0;
+	} else {
+		return cmd_results_new(CMD_INVALID, "natural_scroll", "Expected 'natural_scroll <enabled|disabled>'");
+	}
+
+	input_cmd_apply(new_config);
+	return cmd_results_new(CMD_SUCCESS, NULL, NULL);
+}
diff --git a/sway/commands/input/pointer_accel.c b/sway/commands/input/pointer_accel.c
new file mode 100644
index 00000000..87fb5cff
--- /dev/null
+++ b/sway/commands/input/pointer_accel.c
@@ -0,0 +1,24 @@
+#include <stdlib.h>
+#include <string.h>
+#include "sway/commands.h"
+#include "sway/input/input-manager.h"
+
+struct cmd_results *input_cmd_pointer_accel(int argc, char **argv) {
+	struct cmd_results *error = NULL;
+	if ((error = checkarg(argc, "pointer_accel", EXPECTED_AT_LEAST, 1))) {
+		return error;
+	}
+	if (!current_input_config) {
+		return cmd_results_new(CMD_FAILURE, "pointer_accel", "No input device defined.");
+	}
+	struct input_config *new_config = new_input_config(current_input_config->identifier);
+
+	float pointer_accel = atof(argv[0]);
+	if (pointer_accel < -1 || pointer_accel > 1) {
+		return cmd_results_new(CMD_INVALID, "pointer_accel", "Input out of range [-1, 1]");
+	}
+	new_config->pointer_accel = pointer_accel;
+
+	input_cmd_apply(new_config);
+	return cmd_results_new(CMD_SUCCESS, NULL, NULL);
+}
diff --git a/sway/commands/input/scroll_method.c b/sway/commands/input/scroll_method.c
new file mode 100644
index 00000000..98873938
--- /dev/null
+++ b/sway/commands/input/scroll_method.c
@@ -0,0 +1,30 @@
+#include <string.h>
+#include <strings.h>
+#include "sway/commands.h"
+#include "sway/input/input-manager.h"
+
+struct cmd_results *input_cmd_scroll_method(int argc, char **argv) {
+	struct cmd_results *error = NULL;
+	if ((error = checkarg(argc, "scroll_method", EXPECTED_AT_LEAST, 1))) {
+		return error;
+	}
+	if (!current_input_config) {
+		return cmd_results_new(CMD_FAILURE, "scroll_method", "No input device defined.");
+	}
+	struct input_config *new_config = new_input_config(current_input_config->identifier);
+
+	if (strcasecmp(argv[0], "none") == 0) {
+		new_config->scroll_method = LIBINPUT_CONFIG_SCROLL_NO_SCROLL;
+	} else if (strcasecmp(argv[0], "two_finger") == 0) {
+		new_config->scroll_method = LIBINPUT_CONFIG_SCROLL_2FG;
+	} else if (strcasecmp(argv[0], "edge") == 0) {
+		new_config->scroll_method = LIBINPUT_CONFIG_SCROLL_EDGE;
+	} else if (strcasecmp(argv[0], "on_button_down") == 0) {
+		new_config->scroll_method = LIBINPUT_CONFIG_SCROLL_ON_BUTTON_DOWN;
+	} else {
+		return cmd_results_new(CMD_INVALID, "scroll_method", "Expected 'scroll_method <none|two_finger|edge|on_button_down>'");
+	}
+
+	input_cmd_apply(new_config);
+	return cmd_results_new(CMD_SUCCESS, NULL, NULL);
+}
diff --git a/sway/commands/input/tap.c b/sway/commands/input/tap.c
new file mode 100644
index 00000000..1109466f
--- /dev/null
+++ b/sway/commands/input/tap.c
@@ -0,0 +1,29 @@
+#include <string.h>
+#include <strings.h>
+#include "sway/commands.h"
+#include "sway/input/input-manager.h"
+#include "log.h"
+
+struct cmd_results *input_cmd_tap(int argc, char **argv) {
+	sway_log(L_DEBUG, "tap for device: %s", current_input_config->identifier);
+	struct cmd_results *error = NULL;
+	if ((error = checkarg(argc, "tap", EXPECTED_AT_LEAST, 1))) {
+		return error;
+	}
+	if (!current_input_config) {
+		return cmd_results_new(CMD_FAILURE, "tap", "No input device defined.");
+	}
+	struct input_config *new_config = new_input_config(current_input_config->identifier);
+
+	if (strcasecmp(argv[0], "enabled") == 0) {
+		new_config->tap = LIBINPUT_CONFIG_TAP_ENABLED;
+	} else if (strcasecmp(argv[0], "disabled") == 0) {
+		new_config->tap = LIBINPUT_CONFIG_TAP_DISABLED;
+	} else {
+		return cmd_results_new(CMD_INVALID, "tap", "Expected 'tap <enabled|disabled>'");
+	}
+
+	sway_log(L_DEBUG, "apply-tap for device: %s", current_input_config->identifier);
+	input_cmd_apply(new_config);
+	return cmd_results_new(CMD_SUCCESS, NULL, NULL);
+}
diff --git a/sway/config.c b/sway/config.c
index 61131845..ec8e89b4 100644
--- a/sway/config.c
+++ b/sway/config.c
@@ -226,6 +226,59 @@ static int qstrcmp(const void* a, const void* b) {
 	return strcmp(*((char**) a), *((char**) b));
 }
 
+void merge_input_config(struct input_config *dst, struct input_config *src) {
+	if (src->identifier) {
+		if (dst->identifier) {
+			free(dst->identifier);
+		}
+		dst->identifier = strdup(src->identifier);
+	}
+	if (src->accel_profile != INT_MIN) {
+		dst->accel_profile = src->accel_profile;
+	}
+	if (src->click_method != INT_MIN) {
+		dst->click_method = src->click_method;
+	}
+	if (src->drag_lock != INT_MIN) {
+		dst->drag_lock = src->drag_lock;
+	}
+	if (src->dwt != INT_MIN) {
+		dst->dwt = src->dwt;
+	}
+	if (src->middle_emulation != INT_MIN) {
+		dst->middle_emulation = src->middle_emulation;
+	}
+	if (src->natural_scroll != INT_MIN) {
+		dst->natural_scroll = src->natural_scroll;
+	}
+	if (src->pointer_accel != FLT_MIN) {
+		dst->pointer_accel = src->pointer_accel;
+	}
+	if (src->scroll_method != INT_MIN) {
+		dst->scroll_method = src->scroll_method;
+	}
+	if (src->send_events != INT_MIN) {
+		dst->send_events = src->send_events;
+	}
+	if (src->tap != INT_MIN) {
+		dst->tap = src->tap;
+	}
+}
+
+void free_input_config(struct input_config *ic) {
+	if (!ic) {
+		return;
+	}
+	free(ic->identifier);
+	free(ic);
+}
+
+int input_identifier_cmp(const void *item, const void *data) {
+	const struct input_config *ic = item;
+	const char *identifier = data;
+	return strcmp(ic->identifier, identifier);
+}
+
 bool load_main_config(const char *file, bool is_active) {
 	char *path;
 	if (file != NULL) {
diff --git a/sway/meson.build b/sway/meson.build
index 79201f3a..aa3dd2a7 100644
--- a/sway/meson.build
+++ b/sway/meson.build
@@ -10,6 +10,18 @@ sway_sources = files(
 	'commands/exec.c',
 	'commands/exec_always.c',
 	'commands/include.c',
+	'commands/input.c',
+	'commands/input/accel_profile.c',
+	'commands/input/click_method.c',
+	'commands/input/drag_lock.c',
+	'commands/input/dwt.c',
+	'commands/input/events.c',
+	'commands/input/left_handed.c',
+	'commands/input/middle_emulation.c',
+	'commands/input/natural_scroll.c',
+	'commands/input/pointer_accel.c',
+	'commands/input/scroll_method.c',
+	'commands/input/tap.c',
 	'config.c',
 	'ipc-json.c',
 	'ipc-server.c',

From 163edc5a900fda58e006ed30e14ae10cc4aa13b3 Mon Sep 17 00:00:00 2001
From: Tony Crisci <tony@dubstepdish.com>
Date: Tue, 12 Dec 2017 08:29:37 -0500
Subject: [PATCH 17/49] sway input device

---
 include/sway/config.h              |   1 +
 include/sway/input/input-manager.h |  31 +++-
 include/sway/input/keyboard.h      |   4 +-
 include/sway/input/seat.h          |  13 +-
 sway/commands.c                    |  23 +--
 sway/config.c                      |  29 ++++
 sway/desktop/output.c              |   5 +-
 sway/desktop/wl_shell.c            |   5 +-
 sway/desktop/xdg_shell_v6.c        |   5 +-
 sway/desktop/xwayland.c            |   6 +-
 sway/input/input-manager.c         | 227 ++++++++++++++++++-----------
 sway/input/keyboard.c              |  18 +--
 sway/input/seat.c                  | 104 ++++++-------
 sway/server.c                      |   2 +-
 14 files changed, 258 insertions(+), 215 deletions(-)

diff --git a/include/sway/config.h b/include/sway/config.h
index 7de85ab7..d80f5a39 100644
--- a/include/sway/config.h
+++ b/include/sway/config.h
@@ -355,6 +355,7 @@ char *do_var_replacement(char *str);
 struct cmd_results *check_security_config();
 
 int input_identifier_cmp(const void *item, const void *data);
+struct input_config *new_input_config(const char* identifier);
 void merge_input_config(struct input_config *dst, struct input_config *src);
 void apply_input_config(struct input_config *ic, struct libinput_device *dev);
 void free_input_config(struct input_config *ic);
diff --git a/include/sway/input/input-manager.h b/include/sway/input/input-manager.h
index 78bc161f..7d7c463f 100644
--- a/include/sway/input/input-manager.h
+++ b/include/sway/input/input-manager.h
@@ -7,21 +7,40 @@
 
 extern struct input_config *current_input_config;
 
+/**
+ * The global singleton input manager
+ * TODO: make me not a global
+ */
+extern struct sway_input_manager *input_manager;
+
+struct sway_input_device {
+	char *identifier;
+	struct wlr_input_device *wlr_device;
+	struct input_config *config;
+	struct sway_keyboard *keyboard; // managed by the seat
+	struct wl_list link;
+};
+
 struct sway_input_manager {
 	struct wl_listener input_add;
 	struct wl_listener input_remove;
 	struct sway_server *server;
-	list_t *seats;
+	struct wl_list devices;
+	struct wl_list seats;
 };
 
-struct input_config *new_input_config(const char* identifier);
-
-char* libinput_dev_unique_id(struct libinput_device *dev);
-
 struct sway_input_manager *sway_input_manager_create(
 		struct sway_server *server);
 
-bool sway_input_manager_swayc_has_focus(struct sway_input_manager *input,
+bool sway_input_manager_has_focus(struct sway_input_manager *input,
 		swayc_t *container);
 
+void sway_input_manager_set_focus(struct sway_input_manager *input,
+		swayc_t *container);
+
+void sway_input_manager_configure_xcursor(struct sway_input_manager *input);
+
+void sway_input_manager_apply_config(struct sway_input_manager *input,
+		struct input_config *config);
+
 #endif
diff --git a/include/sway/input/keyboard.h b/include/sway/input/keyboard.h
index 19d40bdc..881805b4 100644
--- a/include/sway/input/keyboard.h
+++ b/include/sway/input/keyboard.h
@@ -2,7 +2,7 @@
 
 struct sway_keyboard {
 	struct sway_seat *seat;
-	struct wlr_input_device *device;
+	struct sway_input_device *device;
 	struct wl_list link; // sway_seat::keyboards
 
 	struct wl_listener keyboard_key;
@@ -10,6 +10,6 @@ struct sway_keyboard {
 };
 
 struct sway_keyboard *sway_keyboard_create(struct sway_seat *seat,
-		struct wlr_input_device *device);
+		struct sway_input_device *device);
 
 void sway_keyboard_destroy(struct sway_keyboard *keyboard);
diff --git a/include/sway/input/seat.h b/include/sway/input/seat.h
index 5455601e..bd94a357 100644
--- a/include/sway/input/seat.h
+++ b/include/sway/input/seat.h
@@ -10,26 +10,21 @@ struct sway_seat {
 	struct sway_input_manager *input;
 	swayc_t *focus;
 
-	struct wl_list keyboards; // sway_keyboard::link
-	struct wl_list pointers; // sway_pointer::link
+	list_t *devices;
 
 	struct wl_listener focus_destroy;
-};
 
-struct sway_pointer {
-	struct sway_seat *seat;
-	struct wlr_input_device *device;
-	struct wl_list link;
+	struct wl_list link; // input_manager::seats
 };
 
 struct sway_seat *sway_seat_create(struct sway_input_manager *input,
 		const char *seat_name);
 
 void sway_seat_add_device(struct sway_seat *seat,
-		struct wlr_input_device *device);
+		struct sway_input_device *device);
 
 void sway_seat_remove_device(struct sway_seat *seat,
-		struct wlr_input_device *device);
+		struct sway_input_device *device);
 
 void sway_seat_configure_xcursor(struct sway_seat *seat);
 
diff --git a/sway/commands.c b/sway/commands.c
index 7710c6ab..57f76ea9 100644
--- a/sway/commands.c
+++ b/sway/commands.c
@@ -71,28 +71,7 @@ void input_cmd_apply(struct input_config *input) {
 	}
 
 	current_input_config = input;
-
-	if (input->identifier) {
-		// Try to find the input device and apply configuration now. If
-		// this is during startup then there will be no container and config
-		// will be applied during normal "new input" event from wlc.
-		/* TODO WLR
-		struct libinput_device *device = NULL;
-		for (int i = 0; i < input_devices->length; ++i) {
-			device = input_devices->items[i];
-			char* dev_identifier = libinput_dev_unique_id(device);
-			if (!dev_identifier) {
-				break;
-			}
-			int match = dev_identifier && strcmp(dev_identifier, input->identifier) == 0;
-			free(dev_identifier);
-			if (match) {
-				apply_input_config(input, device);
-				break;
-			}
-		}
-		*/
-	}
+	sway_input_manager_apply_config(input_manager, input);
 }
 
 /**
diff --git a/sway/config.c b/sway/config.c
index ec8e89b4..7ae3c2a4 100644
--- a/sway/config.c
+++ b/sway/config.c
@@ -226,6 +226,34 @@ static int qstrcmp(const void* a, const void* b) {
 	return strcmp(*((char**) a), *((char**) b));
 }
 
+struct input_config *new_input_config(const char* identifier) {
+	struct input_config *input = calloc(1, sizeof(struct input_config));
+	if (!input) {
+		sway_log(L_DEBUG, "Unable to allocate input config");
+		return NULL;
+	}
+	sway_log(L_DEBUG, "new_input_config(%s)", identifier);
+	if (!(input->identifier = strdup(identifier))) {
+		free(input);
+		sway_log(L_DEBUG, "Unable to allocate input config");
+		return NULL;
+	}
+
+	input->tap = INT_MIN;
+	input->drag_lock = INT_MIN;
+	input->dwt = INT_MIN;
+	input->send_events = INT_MIN;
+	input->click_method = INT_MIN;
+	input->middle_emulation = INT_MIN;
+	input->natural_scroll = INT_MIN;
+	input->accel_profile = INT_MIN;
+	input->pointer_accel = FLT_MIN;
+	input->scroll_method = INT_MIN;
+	input->left_handed = INT_MIN;
+
+	return input;
+}
+
 void merge_input_config(struct input_config *dst, struct input_config *src) {
 	if (src->identifier) {
 		if (dst->identifier) {
@@ -453,6 +481,7 @@ bool load_include_configs(const char *path, struct sway_config *config) {
 	return true;
 }
 
+
 bool read_config(FILE *file, struct sway_config *config) {
 	bool success = true;
 	enum cmd_status block = CMD_BLOCK_END;
diff --git a/sway/desktop/output.c b/sway/desktop/output.c
index 0e7f7060..af067326 100644
--- a/sway/desktop/output.c
+++ b/sway/desktop/output.c
@@ -136,10 +136,7 @@ void output_add_notify(struct wl_listener *listener, void *data) {
 	output->resolution.notify = output_resolution_notify;
 	wl_signal_add(&wlr_output->events.resolution, &output->resolution);
 
-	for (int i = 0; i < server->input->seats->length; ++i) {
-		struct sway_seat *seat = server->input->seats->items[i];
-		sway_seat_configure_xcursor(seat);
-	}
+	sway_input_manager_configure_xcursor(input_manager);
 
 	arrange_windows(output->swayc, -1, -1);
 }
diff --git a/sway/desktop/wl_shell.c b/sway/desktop/wl_shell.c
index 3d3b1c44..df81d5be 100644
--- a/sway/desktop/wl_shell.c
+++ b/sway/desktop/wl_shell.c
@@ -129,8 +129,5 @@ void handle_wl_shell_surface(struct wl_listener *listener, void *data) {
 
 	arrange_windows(cont->parent, -1, -1);
 
-	for (int i = 0; i < server->input->seats->length; ++i) {
-		struct sway_seat *seat = server->input->seats->items[i];
-		sway_seat_set_focus(seat, cont);
-	}
+	sway_input_manager_set_focus(input_manager, cont);
 }
diff --git a/sway/desktop/xdg_shell_v6.c b/sway/desktop/xdg_shell_v6.c
index 8ad6a5ec..82775e2f 100644
--- a/sway/desktop/xdg_shell_v6.c
+++ b/sway/desktop/xdg_shell_v6.c
@@ -135,8 +135,5 @@ void handle_xdg_shell_v6_surface(struct wl_listener *listener, void *data) {
 
 	arrange_windows(cont->parent, -1, -1);
 
-	for (int i = 0; i < server->input->seats->length; ++i) {
-		struct sway_seat *seat = server->input->seats->items[i];
-		sway_seat_set_focus(seat, cont);
-	}
+	sway_input_manager_set_focus(input_manager, cont);
 }
diff --git a/sway/desktop/xwayland.c b/sway/desktop/xwayland.c
index 724c8a82..a7e84aa1 100644
--- a/sway/desktop/xwayland.c
+++ b/sway/desktop/xwayland.c
@@ -173,9 +173,5 @@ void handle_xwayland_surface(struct wl_listener *listener, void *data) {
 	sway_view->swayc = cont;
 
 	arrange_windows(cont->parent, -1, -1);
-
-	for (int i = 0; i < server->input->seats->length; ++i) {
-		struct sway_seat *seat = server->input->seats->items[i];
-		sway_seat_set_focus(seat, cont);
-	}
+	sway_input_manager_set_focus(input_manager, cont);
 }
diff --git a/sway/input/input-manager.c b/sway/input/input-manager.c
index ca80f267..b7f5615c 100644
--- a/sway/input/input-manager.c
+++ b/sway/input/input-manager.c
@@ -14,101 +14,27 @@
 
 static const char *default_seat = "seat0";
 
+// TODO make me not global
+struct sway_input_manager *input_manager;
+
 struct input_config *current_input_config = NULL;
 
 static struct sway_seat *input_manager_get_seat(
 		struct sway_input_manager *input, const char *seat_name) {
 	struct sway_seat *seat = NULL;
-
-	for (int i = 0; i < input->seats->length; ++i) {
-		seat = input->seats->items[i];
+	wl_list_for_each(seat, &input->seats, link) {
 		if (strcmp(seat->seat->name, seat_name) == 0) {
 			return seat;
 		}
 	}
 
-	seat = sway_seat_create(input, seat_name);
-	list_add(input->seats, seat);
-
-	return seat;
+	return sway_seat_create(input, seat_name);
 }
 
-static void input_add_notify(struct wl_listener *listener, void *data) {
-	struct sway_input_manager *input =
-		wl_container_of(listener, input, input_add);
-	struct wlr_input_device *device = data;
-
-	// TODO device configuration
-	struct sway_seat *seat = input_manager_get_seat(input, default_seat);
-	sway_seat_add_device(seat, device);
-}
-
-static void input_remove_notify(struct wl_listener *listener, void *data) {
-	struct sway_input_manager *input =
-		wl_container_of(listener, input, input_remove);
-	struct wlr_input_device *device = data;
-
-	// TODO device configuration
-	struct sway_seat *seat = input_manager_get_seat(input, default_seat);
-	sway_seat_remove_device(seat, device);
-}
-
-struct sway_input_manager *sway_input_manager_create(
-		struct sway_server *server) {
-	struct sway_input_manager *input =
-		calloc(1, sizeof(struct sway_input_manager));
-	if (!input) {
-		return NULL;
-	}
-	// XXX probably don't need the full server
-	input->server = server;
-
-	input->seats = create_list();
-
-	// create the default seat
-	input_manager_get_seat(input, default_seat);
-
-	input->input_add.notify = input_add_notify;
-	wl_signal_add(&server->backend->events.input_add, &input->input_add);
-
-	input->input_remove.notify = input_remove_notify;
-	wl_signal_add(&server->backend->events.input_remove, &input->input_remove);
-
-	return input;
-}
-
-struct input_config *new_input_config(const char* identifier) {
-	struct input_config *input = calloc(1, sizeof(struct input_config));
-	if (!input) {
-		sway_log(L_DEBUG, "Unable to allocate input config");
-		return NULL;
-	}
-	sway_log(L_DEBUG, "new_input_config(%s)", identifier);
-	if (!(input->identifier = strdup(identifier))) {
-		free(input);
-		sway_log(L_DEBUG, "Unable to allocate input config");
-		return NULL;
-	}
-
-	input->tap = INT_MIN;
-	input->drag_lock = INT_MIN;
-	input->dwt = INT_MIN;
-	input->send_events = INT_MIN;
-	input->click_method = INT_MIN;
-	input->middle_emulation = INT_MIN;
-	input->natural_scroll = INT_MIN;
-	input->accel_profile = INT_MIN;
-	input->pointer_accel = FLT_MIN;
-	input->scroll_method = INT_MIN;
-	input->left_handed = INT_MIN;
-
-	return input;
-}
-
-char *libinput_dev_unique_id(struct libinput_device *device) {
-	int vendor = libinput_device_get_id_vendor(device);
-	int product = libinput_device_get_id_product(device);
-	char *name = strdup(libinput_device_get_name(device));
+static char *get_device_identifier(struct wlr_input_device *device) {
+	int vendor = device->vendor;
+	int product = device->product;
+	char *name = strdup(device->name);
 
 	char *p = name;
 	for (; *p; ++p) {
@@ -132,10 +58,107 @@ char *libinput_dev_unique_id(struct libinput_device *device) {
 	return identifier;
 }
 
-bool sway_input_manager_swayc_has_focus(struct sway_input_manager *input,
+static struct sway_input_device *input_sway_device_from_wlr(struct sway_input_manager *input,
+		struct wlr_input_device *device) {
+	struct sway_input_device *sway_device = NULL;
+	wl_list_for_each(sway_device, &input->devices, link) {
+		if (sway_device->wlr_device == device) {
+			return sway_device;
+		}
+	}
+	return NULL;
+}
+
+static struct sway_input_device *input_sway_device_from_config(struct sway_input_manager *input,
+		struct input_config *config) {
+	struct sway_input_device *sway_device = NULL;
+	wl_list_for_each(sway_device, &input->devices, link) {
+		if (strcmp(sway_device->identifier, config->identifier) == 0) {
+			return sway_device;
+		}
+	}
+	return NULL;
+}
+
+static void input_add_notify(struct wl_listener *listener, void *data) {
+	struct sway_input_manager *input =
+		wl_container_of(listener, input, input_add);
+	struct wlr_input_device *device = data;
+
+	struct sway_input_device *sway_device =
+		calloc(1, sizeof(struct sway_input_device));
+	if (!sway_assert(sway_device, "could not allocate input device")) {
+		return;
+	}
+
+	sway_device->wlr_device = device;
+	sway_device->identifier = get_device_identifier(device);
+	wl_list_insert(&input->devices, &sway_device->link);
+
+	// find config
+	for (int i = 0; i < config->input_configs->length; ++i) {
+		struct input_config *input_config = config->input_configs->items[i];
+		if (strcmp(input_config->identifier, sway_device->identifier) == 0) {
+			sway_device->config = input_config;
+			break;
+		}
+	}
+
+	struct sway_seat *seat = input_manager_get_seat(input, default_seat);
+	sway_seat_add_device(seat, sway_device);
+}
+
+static void input_remove_notify(struct wl_listener *listener, void *data) {
+	struct sway_input_manager *input =
+		wl_container_of(listener, input, input_remove);
+	struct wlr_input_device *device = data;
+
+	struct sway_input_device *sway_device =
+		input_sway_device_from_wlr(input, device);
+
+	if (!sway_assert(sway_device, "could not find sway device")) {
+		return;
+	}
+
+	struct sway_seat *seat = NULL;
+	wl_list_for_each(seat, &input->seats, link) {
+		sway_seat_remove_device(seat, sway_device);
+	}
+
+	wl_list_remove(&sway_device->link);
+	free(sway_device->identifier);
+	free(sway_device);
+}
+
+struct sway_input_manager *sway_input_manager_create(
+		struct sway_server *server) {
+	struct sway_input_manager *input =
+		calloc(1, sizeof(struct sway_input_manager));
+	if (!input) {
+		return NULL;
+	}
+	// XXX probably don't need the full server
+	input->server = server;
+
+	wl_list_init(&input->devices);
+	wl_list_init(&input->seats);
+
+	// create the default seat
+	input_manager_get_seat(input, default_seat);
+
+	input->input_add.notify = input_add_notify;
+	wl_signal_add(&server->backend->events.input_add, &input->input_add);
+
+	input->input_remove.notify = input_remove_notify;
+	wl_signal_add(&server->backend->events.input_remove, &input->input_remove);
+
+	return input;
+}
+
+bool sway_input_manager_has_focus(struct sway_input_manager *input,
 		swayc_t *container) {
-	for (int i = 0; i < input->seats->length; ++i) {
-		struct sway_seat *seat = input->seats->items[i];
+	struct sway_seat *seat = NULL;
+	wl_list_for_each(seat, &input->seats, link) {
 		if (seat->focus == container) {
 			return true;
 		}
@@ -143,3 +166,35 @@ bool sway_input_manager_swayc_has_focus(struct sway_input_manager *input,
 
 	return false;
 }
+
+void sway_input_manager_set_focus(struct sway_input_manager *input,
+		swayc_t *container) {
+	struct sway_seat *seat ;
+	wl_list_for_each(seat, &input->seats, link) {
+		sway_seat_set_focus(seat, container);
+	}
+}
+
+void sway_input_manager_apply_config(struct sway_input_manager *input,
+		struct input_config *config) {
+	struct sway_input_device *sway_device =
+		input_sway_device_from_config(input, config);
+	if (!sway_device) {
+		return;
+	}
+
+	struct sway_seat *seat = NULL;
+	wl_list_for_each(seat, &input->seats, link) {
+		sway_seat_remove_device(seat, sway_device);
+	}
+
+	seat = input_manager_get_seat(input, default_seat);
+	sway_seat_add_device(seat, sway_device);
+}
+
+void sway_input_manager_configure_xcursor(struct sway_input_manager *input) {
+	struct sway_seat *seat = NULL;
+	wl_list_for_each(seat, &input->seats, link) {
+		sway_seat_configure_xcursor(seat);
+	}
+}
diff --git a/sway/input/keyboard.c b/sway/input/keyboard.c
index 31d254df..6a792c65 100644
--- a/sway/input/keyboard.c
+++ b/sway/input/keyboard.c
@@ -6,7 +6,7 @@ static void handle_keyboard_key(struct wl_listener *listener, void *data) {
 	struct sway_keyboard *keyboard =
 		wl_container_of(listener, keyboard, keyboard_key);
 	struct wlr_event_keyboard_key *event = data;
-	wlr_seat_set_keyboard(keyboard->seat->seat, keyboard->device);
+	wlr_seat_set_keyboard(keyboard->seat->seat, keyboard->device->wlr_device);
 	wlr_seat_keyboard_notify_key(keyboard->seat->seat, event->time_msec,
 		event->keycode, event->state);
 }
@@ -15,12 +15,12 @@ static void handle_keyboard_modifiers(struct wl_listener *listener,
 		void *data) {
 	struct sway_keyboard *keyboard =
 		wl_container_of(listener, keyboard, keyboard_modifiers);
-	wlr_seat_set_keyboard(keyboard->seat->seat, keyboard->device);
+	wlr_seat_set_keyboard(keyboard->seat->seat, keyboard->device->wlr_device);
 	wlr_seat_keyboard_notify_modifiers(keyboard->seat->seat);
 }
 
 struct sway_keyboard *sway_keyboard_create(struct sway_seat *seat,
-		struct wlr_input_device *device) {
+		struct sway_input_device *device) {
 	struct sway_keyboard *keyboard =
 		calloc(1, sizeof(struct sway_keyboard));
 	if (!sway_assert(keyboard, "could not allocate sway keyboard")) {
@@ -43,18 +43,18 @@ struct sway_keyboard *sway_keyboard_create(struct sway_seat *seat,
 		return NULL;
 	}
 
-	wlr_keyboard_set_keymap(device->keyboard, xkb_map_new_from_names(context,
-		&rules, XKB_KEYMAP_COMPILE_NO_FLAGS));
+	wlr_keyboard_set_keymap(device->wlr_device->keyboard,
+		xkb_map_new_from_names(context, &rules, XKB_KEYMAP_COMPILE_NO_FLAGS));
 	xkb_context_unref(context);
 
-	wl_signal_add(&device->keyboard->events.key, &keyboard->keyboard_key);
+	wl_signal_add(&device->wlr_device->keyboard->events.key,
+		&keyboard->keyboard_key);
 	keyboard->keyboard_key.notify = handle_keyboard_key;
 
-	wl_signal_add(&device->keyboard->events.modifiers, &keyboard->keyboard_modifiers);
+	wl_signal_add(&device->wlr_device->keyboard->events.modifiers,
+		&keyboard->keyboard_modifiers);
 	keyboard->keyboard_modifiers.notify = handle_keyboard_modifiers;
 
-	wl_list_insert(&seat->keyboards, &keyboard->link);
-
 	return keyboard;
 }
 
diff --git a/sway/input/seat.c b/sway/input/seat.c
index 9c17250d..80c6424f 100644
--- a/sway/input/seat.c
+++ b/sway/input/seat.c
@@ -29,6 +29,7 @@ struct sway_seat *sway_seat_create(struct sway_input_manager *input,
 	}
 
 	seat->input = input;
+	seat->devices = create_list();
 
 	wlr_seat_set_capabilities(seat->seat,
 		WL_SEAT_CAPABILITY_KEYBOARD |
@@ -37,67 +38,38 @@ struct sway_seat *sway_seat_create(struct sway_input_manager *input,
 
 	sway_seat_configure_xcursor(seat);
 
-	wl_list_init(&seat->keyboards);
+	wl_list_insert(&input->seats, &seat->link);
 
 	return seat;
 }
 
-static struct sway_pointer *seat_pointer_from_device(struct sway_seat *seat,
-		struct wlr_input_device *device) {
-	struct sway_pointer *pointer = NULL;
-	wl_list_for_each(pointer, &seat->pointers, link) {
-		if (pointer->device == device) {
-			return pointer;
-		}
-	}
-
-	return pointer;
-}
-
-static struct sway_keyboard *seat_keyboard_from_device(struct sway_seat *seat,
-		struct wlr_input_device *device) {
-	struct sway_keyboard *keyboard = NULL;
-	wl_list_for_each(keyboard, &seat->keyboards, link) {
-		if (keyboard->device == device) {
-			return keyboard;
-		}
-	}
-
-	return keyboard;
-}
-
 static void seat_add_pointer(struct sway_seat *seat,
-		struct wlr_input_device *device) {
+		struct sway_input_device *sway_device) {
 	// TODO pointer configuration
-	if (seat_pointer_from_device(seat, device)) {
-		// already added
-		return;
-	}
-
-	struct sway_pointer *pointer = calloc(1, sizeof(struct sway_pointer));
-	pointer->seat = seat;
-	pointer->device = device;
-	wl_list_insert(&seat->pointers, &pointer->link);
-
-	wlr_cursor_attach_input_device(seat->cursor->cursor, device);
+	wlr_cursor_attach_input_device(seat->cursor->cursor,
+		sway_device->wlr_device);
 }
 
 static void seat_add_keyboard(struct sway_seat *seat,
-		struct wlr_input_device *device) {
+		struct sway_input_device *device) {
 	// TODO keyboard configuration
-	if (seat_keyboard_from_device(seat, device)) {
-		// already added
-		return;
-	}
-
 	sway_keyboard_create(seat, device);
-	wlr_seat_set_keyboard(seat->seat, device);
+	wlr_seat_set_keyboard(seat->seat, device->wlr_device);
+}
+
+bool sway_seat_has_device(struct sway_seat *seat,
+		struct sway_input_device *device) {
+	return false;
 }
 
 void sway_seat_add_device(struct sway_seat *seat,
-		struct wlr_input_device *device) {
-	sway_log(L_DEBUG, "input add: %s", device->name);
-	switch (device->type) {
+		struct sway_input_device *device) {
+	if (sway_seat_has_device(seat, device)) {
+		return;
+	}
+
+	sway_log(L_DEBUG, "input add: %s", device->identifier);
+	switch (device->wlr_device->type) {
 		case WLR_INPUT_DEVICE_POINTER:
 			seat_add_pointer(seat, device);
 			break;
@@ -110,31 +82,30 @@ void sway_seat_add_device(struct sway_seat *seat,
 			sway_log(L_DEBUG, "TODO: add other devices");
 			break;
 	}
+
+	list_add(seat->devices, device);
 }
 
 static void seat_remove_keyboard(struct sway_seat *seat,
-		struct wlr_input_device *device) {
-	struct sway_keyboard *keyboard = seat_keyboard_from_device(seat, device);
-	if (keyboard) {
-		sway_keyboard_destroy(keyboard);
+		struct sway_input_device *device) {
+	if (device && device->keyboard) {
+		sway_keyboard_destroy(device->keyboard);
 	}
 }
 
 static void seat_remove_pointer(struct sway_seat *seat,
-		struct wlr_input_device *device) {
-	struct sway_pointer *pointer = seat_pointer_from_device(seat, device);
-
-	if (pointer) {
-		wl_list_remove(&pointer->link);
-		free(pointer);
-		wlr_cursor_detach_input_device(seat->cursor->cursor, device);
-	}
+		struct sway_input_device *device) {
+	wlr_cursor_detach_input_device(seat->cursor->cursor, device->wlr_device);
 }
 
 void sway_seat_remove_device(struct sway_seat *seat,
-		struct wlr_input_device *device) {
-	sway_log(L_DEBUG, "input remove: %s", device->name);
-	switch (device->type) {
+		struct sway_input_device *device) {
+	sway_log(L_DEBUG, "input remove: %s", device->identifier);
+	if (!sway_seat_has_device(seat, device)) {
+		return;
+	}
+
+	switch (device->wlr_device->type) {
 		case WLR_INPUT_DEVICE_POINTER:
 			seat_remove_pointer(seat, device);
 			break;
@@ -147,6 +118,13 @@ void sway_seat_remove_device(struct sway_seat *seat,
 			sway_log(L_DEBUG, "TODO: remove other devices");
 			break;
 	}
+
+	for (int i = 0; i < seat->devices->length; ++i) {
+		if (seat->devices->items[i] == device) {
+			list_del(seat->devices, i);
+			break;
+		}
+	}
 }
 
 void sway_seat_configure_xcursor(struct sway_seat *seat) {
@@ -211,7 +189,7 @@ void sway_seat_set_focus(struct sway_seat *seat, swayc_t *container) {
 	seat->focus = container;
 
 	if (last_focus &&
-			!sway_input_manager_swayc_has_focus(seat->input, last_focus)) {
+			!sway_input_manager_has_focus(seat->input, last_focus)) {
 		struct sway_view *view = last_focus->sway_view;
 		view->iface.set_activated(view, false);
 
diff --git a/sway/server.c b/sway/server.c
index 7b9a5e8e..32c8f03c 100644
--- a/sway/server.c
+++ b/sway/server.c
@@ -60,7 +60,7 @@ bool server_init(struct sway_server *server) {
 		return false;
 	}
 
-	server->input = sway_input_manager_create(server);
+	input_manager = sway_input_manager_create(server);
 
 	return true;
 }

From 538903bc5ace56c1dab0f5287fb4d0bab78a0165 Mon Sep 17 00:00:00 2001
From: Tony Crisci <tony@dubstepdish.com>
Date: Tue, 12 Dec 2017 09:02:30 -0500
Subject: [PATCH 18/49] config cleanup

---
 sway/commands/input/accel_profile.c    |  6 +++--
 sway/commands/input/click_method.c     | 12 ++++++---
 sway/commands/input/drag_lock.c        |  9 ++++---
 sway/commands/input/dwt.c              |  6 +++--
 sway/commands/input/events.c           | 15 +++++++----
 sway/commands/input/left_handed.c      |  9 ++++---
 sway/commands/input/middle_emulation.c | 12 ++++++---
 sway/commands/input/natural_scroll.c   |  9 ++++---
 sway/commands/input/pointer_accel.c    |  9 ++++---
 sway/commands/input/scroll_method.c    |  9 ++++---
 sway/commands/input/tap.c              |  9 ++++---
 sway/config.c                          | 37 +++++++++++++++++---------
 12 files changed, 94 insertions(+), 48 deletions(-)

diff --git a/sway/commands/input/accel_profile.c b/sway/commands/input/accel_profile.c
index 13ded431..9b5fb7a2 100644
--- a/sway/commands/input/accel_profile.c
+++ b/sway/commands/input/accel_profile.c
@@ -9,9 +9,11 @@ struct cmd_results *input_cmd_accel_profile(int argc, char **argv) {
 		return error;
 	}
 	if (!current_input_config) {
-		return cmd_results_new(CMD_FAILURE, "accel_profile", "No input device defined.");
+		return cmd_results_new(CMD_FAILURE, "accel_profile",
+				"No input device defined.");
 	}
-	struct input_config *new_config = new_input_config(current_input_config->identifier);
+	struct input_config *new_config =
+		new_input_config(current_input_config->identifier);
 
 	if (strcasecmp(argv[0], "adaptive") == 0) {
 		new_config->accel_profile = LIBINPUT_CONFIG_ACCEL_PROFILE_ADAPTIVE;
diff --git a/sway/commands/input/click_method.c b/sway/commands/input/click_method.c
index 40f075ce..a0e3bddf 100644
--- a/sway/commands/input/click_method.c
+++ b/sway/commands/input/click_method.c
@@ -5,15 +5,18 @@
 #include "log.h"
 
 struct cmd_results *input_cmd_click_method(int argc, char **argv) {
-	sway_log(L_DEBUG, "click_method for device:  %d %s", current_input_config==NULL, current_input_config->identifier);
+	sway_log(L_DEBUG, "click_method for device:  %d %s",
+		current_input_config==NULL, current_input_config->identifier);
 	struct cmd_results *error = NULL;
 	if ((error = checkarg(argc, "click_method", EXPECTED_AT_LEAST, 1))) {
 		return error;
 	}
 	if (!current_input_config) {
-		return cmd_results_new(CMD_FAILURE, "click_method", "No input device defined.");
+		return cmd_results_new(CMD_FAILURE, "click_method",
+			"No input device defined.");
 	}
-	struct input_config *new_config = new_input_config(current_input_config->identifier);
+	struct input_config *new_config =
+		new_input_config(current_input_config->identifier);
 
 	if (strcasecmp(argv[0], "none") == 0) {
 		new_config->click_method = LIBINPUT_CONFIG_CLICK_METHOD_NONE;
@@ -22,7 +25,8 @@ struct cmd_results *input_cmd_click_method(int argc, char **argv) {
 	} else if (strcasecmp(argv[0], "clickfinger") == 0) {
 		new_config->click_method = LIBINPUT_CONFIG_CLICK_METHOD_CLICKFINGER;
 	} else {
-		return cmd_results_new(CMD_INVALID, "click_method", "Expected 'click_method <none|button_areas|clickfinger'");
+		return cmd_results_new(CMD_INVALID, "click_method",
+			"Expected 'click_method <none|button_areas|clickfinger'");
 	}
 
 	input_cmd_apply(new_config);
diff --git a/sway/commands/input/drag_lock.c b/sway/commands/input/drag_lock.c
index 11e7fbea..149a6183 100644
--- a/sway/commands/input/drag_lock.c
+++ b/sway/commands/input/drag_lock.c
@@ -9,16 +9,19 @@ struct cmd_results *input_cmd_drag_lock(int argc, char **argv) {
 		return error;
 	}
 	if (!current_input_config) {
-		return cmd_results_new(CMD_FAILURE, "drag_lock", "No input device defined.");
+		return cmd_results_new(CMD_FAILURE,
+			"drag_lock", "No input device defined.");
 	}
-	struct input_config *new_config = new_input_config(current_input_config->identifier);
+	struct input_config *new_config =
+		new_input_config(current_input_config->identifier);
 
 	if (strcasecmp(argv[0], "enabled") == 0) {
 		new_config->drag_lock = LIBINPUT_CONFIG_DRAG_LOCK_ENABLED;
 	} else if (strcasecmp(argv[0], "disabled") == 0) {
 		new_config->drag_lock = LIBINPUT_CONFIG_DRAG_LOCK_DISABLED;
 	} else {
-		return cmd_results_new(CMD_INVALID, "drag_lock", "Expected 'drag_lock <enabled|disabled>'");
+		return cmd_results_new(CMD_INVALID, "drag_lock",
+			"Expected 'drag_lock <enabled|disabled>'");
 	}
 
 	input_cmd_apply(new_config);
diff --git a/sway/commands/input/dwt.c b/sway/commands/input/dwt.c
index f3cbf252..0954575c 100644
--- a/sway/commands/input/dwt.c
+++ b/sway/commands/input/dwt.c
@@ -11,14 +11,16 @@ struct cmd_results *input_cmd_dwt(int argc, char **argv) {
 	if (!current_input_config) {
 		return cmd_results_new(CMD_FAILURE, "dwt", "No input device defined.");
 	}
-	struct input_config *new_config = new_input_config(current_input_config->identifier);
+	struct input_config *new_config =
+		new_input_config(current_input_config->identifier);
 
 	if (strcasecmp(argv[0], "enabled") == 0) {
 		new_config->dwt = LIBINPUT_CONFIG_DWT_ENABLED;
 	} else if (strcasecmp(argv[0], "disabled") == 0) {
 		new_config->dwt = LIBINPUT_CONFIG_DWT_DISABLED;
 	} else {
-		return cmd_results_new(CMD_INVALID, "dwt", "Expected 'dwt <enabled|disabled>'");
+		return cmd_results_new(CMD_INVALID, "dwt",
+			"Expected 'dwt <enabled|disabled>'");
 	}
 
 	input_cmd_apply(new_config);
diff --git a/sway/commands/input/events.c b/sway/commands/input/events.c
index 4b2fdff5..f44c0ec7 100644
--- a/sway/commands/input/events.c
+++ b/sway/commands/input/events.c
@@ -5,24 +5,29 @@
 #include "log.h"
 
 struct cmd_results *input_cmd_events(int argc, char **argv) {
-	sway_log(L_DEBUG, "events for device: %s", current_input_config->identifier);
+	sway_log(L_DEBUG, "events for device: %s",
+		current_input_config->identifier);
 	struct cmd_results *error = NULL;
 	if ((error = checkarg(argc, "events", EXPECTED_AT_LEAST, 1))) {
 		return error;
 	}
 	if (!current_input_config) {
-		return cmd_results_new(CMD_FAILURE, "events", "No input device defined.");
+		return cmd_results_new(CMD_FAILURE, "events",
+			"No input device defined.");
 	}
-	struct input_config *new_config = new_input_config(current_input_config->identifier);
+	struct input_config *new_config =
+		new_input_config(current_input_config->identifier);
 
 	if (strcasecmp(argv[0], "enabled") == 0) {
 		new_config->send_events = LIBINPUT_CONFIG_SEND_EVENTS_ENABLED;
 	} else if (strcasecmp(argv[0], "disabled") == 0) {
 		new_config->send_events = LIBINPUT_CONFIG_SEND_EVENTS_DISABLED;
 	} else if (strcasecmp(argv[0], "disabled_on_external_mouse") == 0) {
-		new_config->send_events = LIBINPUT_CONFIG_SEND_EVENTS_DISABLED_ON_EXTERNAL_MOUSE;
+		new_config->send_events =
+			LIBINPUT_CONFIG_SEND_EVENTS_DISABLED_ON_EXTERNAL_MOUSE;
 	} else {
-		return cmd_results_new(CMD_INVALID, "events", "Expected 'events <enabled|disabled|disabled_on_external_mouse>'");
+		return cmd_results_new(CMD_INVALID, "events",
+			"Expected 'events <enabled|disabled|disabled_on_external_mouse>'");
 	}
 
 	input_cmd_apply(new_config);
diff --git a/sway/commands/input/left_handed.c b/sway/commands/input/left_handed.c
index 715df2a1..dc8fcd56 100644
--- a/sway/commands/input/left_handed.c
+++ b/sway/commands/input/left_handed.c
@@ -9,16 +9,19 @@ struct cmd_results *input_cmd_left_handed(int argc, char **argv) {
 		return error;
 	}
 	if (!current_input_config) {
-		return cmd_results_new(CMD_FAILURE, "left_handed", "No input device defined.");
+		return cmd_results_new(CMD_FAILURE, "left_handed",
+			"No input device defined.");
 	}
-	struct input_config *new_config = new_input_config(current_input_config->identifier);
+	struct input_config *new_config =
+		new_input_config(current_input_config->identifier);
 
 	if (strcasecmp(argv[0], "enabled") == 0) {
 		new_config->left_handed = 1;
 	} else if (strcasecmp(argv[0], "disabled") == 0) {
 		new_config->left_handed = 0;
 	} else {
-		return cmd_results_new(CMD_INVALID, "left_handed", "Expected 'left_handed <enabled|disabled>'");
+		return cmd_results_new(CMD_INVALID, "left_handed",
+			"Expected 'left_handed <enabled|disabled>'");
 	}
 
 	input_cmd_apply(new_config);
diff --git a/sway/commands/input/middle_emulation.c b/sway/commands/input/middle_emulation.c
index d31ce950..e19964d8 100644
--- a/sway/commands/input/middle_emulation.c
+++ b/sway/commands/input/middle_emulation.c
@@ -9,16 +9,20 @@ struct cmd_results *input_cmd_middle_emulation(int argc, char **argv) {
 		return error;
 	}
 	if (!current_input_config) {
-		return cmd_results_new(CMD_FAILURE, "middle_emulation", "No input device defined.");
+		return cmd_results_new(CMD_FAILURE, "middle_emulation",
+			"No input device defined.");
 	}
-	struct input_config *new_config = new_input_config(current_input_config->identifier);
+	struct input_config *new_config =
+		new_input_config(current_input_config->identifier);
 
 	if (strcasecmp(argv[0], "enabled") == 0) {
 		new_config->middle_emulation = LIBINPUT_CONFIG_MIDDLE_EMULATION_ENABLED;
 	} else if (strcasecmp(argv[0], "disabled") == 0) {
-		new_config->middle_emulation = LIBINPUT_CONFIG_MIDDLE_EMULATION_DISABLED;
+		new_config->middle_emulation =
+			LIBINPUT_CONFIG_MIDDLE_EMULATION_DISABLED;
 	} else {
-		return cmd_results_new(CMD_INVALID, "middle_emulation", "Expected 'middle_emulation <enabled|disabled>'");
+		return cmd_results_new(CMD_INVALID, "middle_emulation",
+			"Expected 'middle_emulation <enabled|disabled>'");
 	}
 
 	input_cmd_apply(new_config);
diff --git a/sway/commands/input/natural_scroll.c b/sway/commands/input/natural_scroll.c
index 9d1dc506..8272c5b3 100644
--- a/sway/commands/input/natural_scroll.c
+++ b/sway/commands/input/natural_scroll.c
@@ -9,16 +9,19 @@ struct cmd_results *input_cmd_natural_scroll(int argc, char **argv) {
 		return error;
 	}
 	if (!current_input_config) {
-		return cmd_results_new(CMD_FAILURE, "natural_scoll", "No input device defined.");
+		return cmd_results_new(CMD_FAILURE, "natural_scoll",
+			"No input device defined.");
 	}
-	struct input_config *new_config = new_input_config(current_input_config->identifier);
+	struct input_config *new_config =
+		new_input_config(current_input_config->identifier);
 
 	if (strcasecmp(argv[0], "enabled") == 0) {
 		new_config->natural_scroll = 1;
 	} else if (strcasecmp(argv[0], "disabled") == 0) {
 		new_config->natural_scroll = 0;
 	} else {
-		return cmd_results_new(CMD_INVALID, "natural_scroll", "Expected 'natural_scroll <enabled|disabled>'");
+		return cmd_results_new(CMD_INVALID, "natural_scroll",
+			"Expected 'natural_scroll <enabled|disabled>'");
 	}
 
 	input_cmd_apply(new_config);
diff --git a/sway/commands/input/pointer_accel.c b/sway/commands/input/pointer_accel.c
index 87fb5cff..2c9db5bf 100644
--- a/sway/commands/input/pointer_accel.c
+++ b/sway/commands/input/pointer_accel.c
@@ -9,13 +9,16 @@ struct cmd_results *input_cmd_pointer_accel(int argc, char **argv) {
 		return error;
 	}
 	if (!current_input_config) {
-		return cmd_results_new(CMD_FAILURE, "pointer_accel", "No input device defined.");
+		return cmd_results_new(CMD_FAILURE,
+			"pointer_accel", "No input device defined.");
 	}
-	struct input_config *new_config = new_input_config(current_input_config->identifier);
+	struct input_config *new_config =
+		new_input_config(current_input_config->identifier);
 
 	float pointer_accel = atof(argv[0]);
 	if (pointer_accel < -1 || pointer_accel > 1) {
-		return cmd_results_new(CMD_INVALID, "pointer_accel", "Input out of range [-1, 1]");
+		return cmd_results_new(CMD_INVALID, "pointer_accel",
+			"Input out of range [-1, 1]");
 	}
 	new_config->pointer_accel = pointer_accel;
 
diff --git a/sway/commands/input/scroll_method.c b/sway/commands/input/scroll_method.c
index 98873938..40277155 100644
--- a/sway/commands/input/scroll_method.c
+++ b/sway/commands/input/scroll_method.c
@@ -9,9 +9,11 @@ struct cmd_results *input_cmd_scroll_method(int argc, char **argv) {
 		return error;
 	}
 	if (!current_input_config) {
-		return cmd_results_new(CMD_FAILURE, "scroll_method", "No input device defined.");
+		return cmd_results_new(CMD_FAILURE, "scroll_method",
+			"No input device defined.");
 	}
-	struct input_config *new_config = new_input_config(current_input_config->identifier);
+	struct input_config *new_config =
+		new_input_config(current_input_config->identifier);
 
 	if (strcasecmp(argv[0], "none") == 0) {
 		new_config->scroll_method = LIBINPUT_CONFIG_SCROLL_NO_SCROLL;
@@ -22,7 +24,8 @@ struct cmd_results *input_cmd_scroll_method(int argc, char **argv) {
 	} else if (strcasecmp(argv[0], "on_button_down") == 0) {
 		new_config->scroll_method = LIBINPUT_CONFIG_SCROLL_ON_BUTTON_DOWN;
 	} else {
-		return cmd_results_new(CMD_INVALID, "scroll_method", "Expected 'scroll_method <none|two_finger|edge|on_button_down>'");
+		return cmd_results_new(CMD_INVALID, "scroll_method",
+			"Expected 'scroll_method <none|two_finger|edge|on_button_down>'");
 	}
 
 	input_cmd_apply(new_config);
diff --git a/sway/commands/input/tap.c b/sway/commands/input/tap.c
index 1109466f..18a54087 100644
--- a/sway/commands/input/tap.c
+++ b/sway/commands/input/tap.c
@@ -13,17 +13,20 @@ struct cmd_results *input_cmd_tap(int argc, char **argv) {
 	if (!current_input_config) {
 		return cmd_results_new(CMD_FAILURE, "tap", "No input device defined.");
 	}
-	struct input_config *new_config = new_input_config(current_input_config->identifier);
+	struct input_config *new_config =
+		new_input_config(current_input_config->identifier);
 
 	if (strcasecmp(argv[0], "enabled") == 0) {
 		new_config->tap = LIBINPUT_CONFIG_TAP_ENABLED;
 	} else if (strcasecmp(argv[0], "disabled") == 0) {
 		new_config->tap = LIBINPUT_CONFIG_TAP_DISABLED;
 	} else {
-		return cmd_results_new(CMD_INVALID, "tap", "Expected 'tap <enabled|disabled>'");
+		return cmd_results_new(CMD_INVALID, "tap",
+			"Expected 'tap <enabled|disabled>'");
 	}
 
-	sway_log(L_DEBUG, "apply-tap for device: %s", current_input_config->identifier);
+	sway_log(L_DEBUG, "apply-tap for device: %s",
+		current_input_config->identifier);
 	input_cmd_apply(new_config);
 	return cmd_results_new(CMD_SUCCESS, NULL, NULL);
 }
diff --git a/sway/config.c b/sway/config.c
index 7ae3c2a4..4bc74ee0 100644
--- a/sway/config.c
+++ b/sway/config.c
@@ -21,6 +21,7 @@
 #include <dev/evdev/input-event-codes.h>
 #endif
 #include <wlr/types/wlr_output.h>
+#include "sway/input/input-manager.h"
 #include "sway/commands.h"
 #include "sway/config.h"
 #include "sway/layout.h"
@@ -48,7 +49,8 @@ static void config_defaults(struct sway_config *config) {
 
 	if (!(config->cmd_queue = create_list())) goto cleanup;
 
-	if (!(config->current_mode = malloc(sizeof(struct sway_mode)))) goto cleanup;
+	if (!(config->current_mode = malloc(sizeof(struct sway_mode))))
+		goto cleanup;
 	if (!(config->current_mode->name = malloc(sizeof("default")))) goto cleanup;
 	strcpy(config->current_mode->name, "default");
 	if (!(config->current_mode->bindings = create_list())) goto cleanup;
@@ -337,8 +339,9 @@ bool load_main_config(const char *file, bool is_active) {
 	bool success = true;
 	DIR *dir = opendir(SYSCONFDIR "/sway/security.d");
 	if (!dir) {
-		sway_log(L_ERROR, "%s does not exist, sway will have no security configuration"
-				" and will probably be broken", SYSCONFDIR "/sway/security.d");
+		sway_log(L_ERROR,
+			"%s does not exist, sway will have no security configuration"
+			" and will probably be broken", SYSCONFDIR "/sway/security.d");
 	} else {
 		list_t *secconfigs = create_list();
 		char *base = SYSCONFDIR "/sway/security.d/";
@@ -362,8 +365,12 @@ bool load_main_config(const char *file, bool is_active) {
 		list_qsort(secconfigs, qstrcmp);
 		for (int i = 0; i < secconfigs->length; ++i) {
 			char *_path = secconfigs->items[i];
-			if (stat(_path, &s) || s.st_uid != 0 || s.st_gid != 0 || (((s.st_mode & 0777) != 0644) && (s.st_mode & 0777) != 0444)) {
-				sway_log(L_ERROR, "Refusing to load %s - it must be owned by root and mode 644 or 444", _path);
+			if (stat(_path, &s) || s.st_uid != 0 || s.st_gid != 0 ||
+					(((s.st_mode & 0777) != 0644) &&
+					 (s.st_mode & 0777) != 0444)) {
+				sway_log(L_ERROR,
+					"Refusing to load %s - it must be owned by root "
+					"and mode 644 or 444", _path);
 				success = false;
 			} else {
 				success = success && load_config(_path, config);
@@ -392,7 +399,8 @@ bool load_main_config(const char *file, bool is_active) {
 	return success;
 }
 
-static bool load_include_config(const char *path, const char *parent_dir, struct sway_config *config) {
+static bool load_include_config(const char *path, const char *parent_dir,
+		struct sway_config *config) {
 	// save parent config
 	const char *parent_config = config->current_config;
 
@@ -402,7 +410,8 @@ static bool load_include_config(const char *path, const char *parent_dir, struct
 		len = len + strlen(parent_dir) + 2;
 		full_path = malloc(len * sizeof(char));
 		if (!full_path) {
-			sway_log(L_ERROR, "Unable to allocate full path to included config");
+			sway_log(L_ERROR,
+				"Unable to allocate full path to included config");
 			return false;
 		}
 		snprintf(full_path, len, "%s/%s", parent_dir, path);
@@ -421,7 +430,9 @@ static bool load_include_config(const char *path, const char *parent_dir, struct
 	for (j = 0; j < config->config_chain->length; ++j) {
 		char *old_path = config->config_chain->items[j];
 		if (strcmp(real_path, old_path) == 0) {
-			sway_log(L_DEBUG, "%s already included once, won't be included again.", real_path);
+			sway_log(L_DEBUG,
+				"%s already included once, won't be included again.",
+				real_path);
 			free(real_path);
 			return false;
 		}
@@ -509,8 +520,8 @@ bool read_config(FILE *file, struct sway_config *config) {
 		switch(res->status) {
 		case CMD_FAILURE:
 		case CMD_INVALID:
-			sway_log(L_ERROR, "Error on line %i '%s': %s (%s)", line_number, line,
-				res->error, config->current_config);
+			sway_log(L_ERROR, "Error on line %i '%s': %s (%s)", line_number,
+				line, res->error, config->current_config);
 			success = false;
 			break;
 
@@ -585,8 +596,7 @@ bool read_config(FILE *file, struct sway_config *config) {
 
 			case CMD_BLOCK_INPUT:
 				sway_log(L_DEBUG, "End of input block");
-				// TODO: input
-				//current_input_config = NULL;
+				current_input_config = NULL;
 				block = CMD_BLOCK_END;
 				break;
 
@@ -651,7 +661,8 @@ char *do_var_replacement(char *str) {
 				char *newstr = malloc(strlen(str) - vnlen + vvlen + 1);
 				if (!newstr) {
 					sway_log(L_ERROR,
-							"Unable to allocate replacement during variable expansion");
+						"Unable to allocate replacement "
+						"during variable expansion");
 					break;
 				}
 				char *newptr = newstr;

From c173d30b9203520c274f34eb72fc787aa33ca211 Mon Sep 17 00:00:00 2001
From: Tony Crisci <tony@dubstepdish.com>
Date: Tue, 12 Dec 2017 10:55:20 -0500
Subject: [PATCH 19/49] seat configuration

---
 include/sway/commands.h    |  1 +
 include/sway/config.h      |  1 +
 sway/commands.c            |  2 ++
 sway/commands/input/seat.c | 28 ++++++++++++++++++++++++++++
 sway/config.c              |  1 +
 sway/input/input-manager.c | 11 +++++++----
 sway/meson.build           |  1 +
 7 files changed, 41 insertions(+), 4 deletions(-)
 create mode 100644 sway/commands/input/seat.c

diff --git a/include/sway/commands.h b/include/sway/commands.h
index 138e3c29..75340e03 100644
--- a/include/sway/commands.h
+++ b/include/sway/commands.h
@@ -180,6 +180,7 @@ sway_cmd bar_colors_cmd_statusline;
 sway_cmd bar_colors_cmd_focused_statusline;
 sway_cmd bar_colors_cmd_urgent_workspace;
 
+sway_cmd input_cmd_seat;
 sway_cmd input_cmd_accel_profile;
 sway_cmd input_cmd_click_method;
 sway_cmd input_cmd_drag_lock;
diff --git a/include/sway/config.h b/include/sway/config.h
index d80f5a39..9fcecfd6 100644
--- a/include/sway/config.h
+++ b/include/sway/config.h
@@ -69,6 +69,7 @@ struct input_config {
 
 	bool capturable;
 	struct wlr_box region;
+	char *seat;
 };
 
 /**
diff --git a/sway/commands.c b/sway/commands.c
index 57f76ea9..6645436a 100644
--- a/sway/commands.c
+++ b/sway/commands.c
@@ -123,6 +123,7 @@ static int handler_compare(const void *_a, const void *_b) {
 	return strcasecmp(a->command, b->command);
 }
 
+// must be in order for the bsearch
 static struct cmd_handler input_handlers[] = {
 	{ "accel_profile", input_cmd_accel_profile },
 	{ "click_method", input_cmd_click_method },
@@ -134,6 +135,7 @@ static struct cmd_handler input_handlers[] = {
 	{ "natural_scroll", input_cmd_natural_scroll },
 	{ "pointer_accel", input_cmd_pointer_accel },
 	{ "scroll_method", input_cmd_scroll_method },
+	{ "seat", input_cmd_seat },
 	{ "tap", input_cmd_tap },
 };
 
diff --git a/sway/commands/input/seat.c b/sway/commands/input/seat.c
new file mode 100644
index 00000000..9d86ac0e
--- /dev/null
+++ b/sway/commands/input/seat.c
@@ -0,0 +1,28 @@
+#define _XOPEN_SOURCE 700
+#include <string.h>
+#include <strings.h>
+#include "sway/commands.h"
+#include "sway/input/input-manager.h"
+#include "log.h"
+
+struct cmd_results *input_cmd_seat(int argc, char **argv) {
+	sway_log(L_DEBUG, "seat for device:  %d %s",
+		current_input_config==NULL, current_input_config->identifier);
+	struct cmd_results *error = NULL;
+	if ((error = checkarg(argc, "seat", EXPECTED_AT_LEAST, 1))) {
+		return error;
+	}
+	if (!current_input_config) {
+		return cmd_results_new(CMD_FAILURE, "seat",
+			"No input device defined.");
+	}
+	struct input_config *new_config =
+		new_input_config(current_input_config->identifier);
+
+	// TODO validate seat name
+	free(new_config->seat);
+	new_config->seat = strdup(argv[0]);
+
+	input_cmd_apply(new_config);
+	return cmd_results_new(CMD_SUCCESS, NULL, NULL);
+}
diff --git a/sway/config.c b/sway/config.c
index 4bc74ee0..b77b8b4b 100644
--- a/sway/config.c
+++ b/sway/config.c
@@ -300,6 +300,7 @@ void free_input_config(struct input_config *ic) {
 		return;
 	}
 	free(ic->identifier);
+	free(ic->seat);
 	free(ic);
 }
 
diff --git a/sway/input/input-manager.c b/sway/input/input-manager.c
index b7f5615c..b07a733e 100644
--- a/sway/input/input-manager.c
+++ b/sway/input/input-manager.c
@@ -104,7 +104,9 @@ static void input_add_notify(struct wl_listener *listener, void *data) {
 		}
 	}
 
-	struct sway_seat *seat = input_manager_get_seat(input, default_seat);
+	const char *seat_name =
+		(sway_device->config ? sway_device->config->seat : default_seat);
+	struct sway_seat *seat = input_manager_get_seat(input, seat_name);
 	sway_seat_add_device(seat, sway_device);
 }
 
@@ -176,9 +178,9 @@ void sway_input_manager_set_focus(struct sway_input_manager *input,
 }
 
 void sway_input_manager_apply_config(struct sway_input_manager *input,
-		struct input_config *config) {
+		struct input_config *input_config) {
 	struct sway_input_device *sway_device =
-		input_sway_device_from_config(input, config);
+		input_sway_device_from_config(input, input_config);
 	if (!sway_device) {
 		return;
 	}
@@ -188,7 +190,8 @@ void sway_input_manager_apply_config(struct sway_input_manager *input,
 		sway_seat_remove_device(seat, sway_device);
 	}
 
-	seat = input_manager_get_seat(input, default_seat);
+	const char *seat_name = (input_config->seat ? input_config->seat : default_seat);
+	seat = input_manager_get_seat(input, seat_name);
 	sway_seat_add_device(seat, sway_device);
 }
 
diff --git a/sway/meson.build b/sway/meson.build
index aa3dd2a7..fad1f88c 100644
--- a/sway/meson.build
+++ b/sway/meson.build
@@ -11,6 +11,7 @@ sway_sources = files(
 	'commands/exec_always.c',
 	'commands/include.c',
 	'commands/input.c',
+	'commands/input/seat.c',
 	'commands/input/accel_profile.c',
 	'commands/input/click_method.c',
 	'commands/input/drag_lock.c',

From 92fef27eaa0b52c9d37bdabff14ae21cd6660f46 Mon Sep 17 00:00:00 2001
From: Tony Crisci <tony@dubstepdish.com>
Date: Thu, 14 Dec 2017 11:11:56 -0500
Subject: [PATCH 20/49] basic configuration

---
 include/sway/commands.h            |   5 +
 include/sway/config.h              |  27 ++++-
 include/sway/input/input-manager.h |   9 +-
 include/sway/input/keyboard.h      |   9 +-
 include/sway/input/seat.h          |  20 +++-
 sway/commands.c                    |  32 ++++-
 sway/commands/input/seat.c         |  28 -----
 sway/commands/seat.c               |  35 ++++++
 sway/commands/seat/attach.c        |  26 +++++
 sway/config.c                      | 144 ++++++++++++++++++++++-
 sway/input/cursor.c                |   8 +-
 sway/input/input-manager.c         | 139 ++++++++++++++++------
 sway/input/keyboard.c              |  51 +++++---
 sway/input/seat.c                  | 181 ++++++++++++++++++-----------
 sway/meson.build                   |   3 +-
 15 files changed, 543 insertions(+), 174 deletions(-)
 delete mode 100644 sway/commands/input/seat.c
 create mode 100644 sway/commands/seat.c
 create mode 100644 sway/commands/seat/attach.c

diff --git a/include/sway/commands.h b/include/sway/commands.h
index 75340e03..ce74e1ed 100644
--- a/include/sway/commands.h
+++ b/include/sway/commands.h
@@ -17,6 +17,7 @@ enum cmd_status {
 	CMD_BLOCK_BAR,
 	CMD_BLOCK_BAR_COLORS,
 	CMD_BLOCK_INPUT,
+	CMD_BLOCK_SEAT,
 	CMD_BLOCK_COMMANDS,
 	CMD_BLOCK_IPC,
 	CMD_BLOCK_IPC_EVENTS,
@@ -42,6 +43,7 @@ enum expected_args {
 };
 
 void input_cmd_apply(struct input_config *input);
+void seat_cmd_apply(struct seat_config *seat);
 
 struct cmd_results *checkarg(int argc, const char *name,
 		enum expected_args type, int val);
@@ -111,6 +113,7 @@ sway_cmd cmd_gaps;
 sway_cmd cmd_hide_edge_borders;
 sway_cmd cmd_include;
 sway_cmd cmd_input;
+sway_cmd cmd_seat;
 sway_cmd cmd_ipc;
 sway_cmd cmd_kill;
 sway_cmd cmd_layout;
@@ -193,6 +196,8 @@ sway_cmd input_cmd_pointer_accel;
 sway_cmd input_cmd_scroll_method;
 sway_cmd input_cmd_tap;
 
+sway_cmd seat_cmd_attach;
+
 sway_cmd cmd_ipc_cmd;
 sway_cmd cmd_ipc_events;
 sway_cmd cmd_ipc_event_cmd;
diff --git a/include/sway/config.h b/include/sway/config.h
index 9fcecfd6..5df5d61e 100644
--- a/include/sway/config.h
+++ b/include/sway/config.h
@@ -69,7 +69,22 @@ struct input_config {
 
 	bool capturable;
 	struct wlr_box region;
-	char *seat;
+};
+
+/**
+ * Options for misc device configurations that happen in the seat block
+ */
+struct seat_attachment_config {
+	char *identifier;
+	// TODO other things are configured here for some reason
+};
+
+/**
+ * Options for multiseat and other misc device configurations
+ */
+struct seat_config {
+	char *name;
+	list_t *attachments; // list of seat_attachment configs
 };
 
 /**
@@ -260,6 +275,7 @@ struct sway_config {
 	list_t *pid_workspaces;
 	list_t *output_configs;
 	list_t *input_configs;
+	list_t *seat_configs;
 	list_t *criteria;
 	list_t *no_focus;
 	list_t *active_bar_modifiers;
@@ -358,9 +374,16 @@ struct cmd_results *check_security_config();
 int input_identifier_cmp(const void *item, const void *data);
 struct input_config *new_input_config(const char* identifier);
 void merge_input_config(struct input_config *dst, struct input_config *src);
-void apply_input_config(struct input_config *ic, struct libinput_device *dev);
 void free_input_config(struct input_config *ic);
 
+int seat_name_cmp(const void *item, const void *data);
+struct seat_config *new_seat_config(const char* name);
+void merge_seat_config(struct seat_config *dst, struct seat_config *src);
+void free_seat_config(struct seat_config *ic);
+struct seat_attachment_config *seat_attachment_config_new();
+struct seat_attachment_config *seat_config_get_attachment(
+		struct seat_config *seat_config, char *identifier);
+
 int output_name_cmp(const void *item, const void *data);
 void merge_output_config(struct output_config *dst, struct output_config *src);
 /** Sets up a WLC output handle based on a given output_config.
diff --git a/include/sway/input/input-manager.h b/include/sway/input/input-manager.h
index 7d7c463f..cdcffab6 100644
--- a/include/sway/input/input-manager.h
+++ b/include/sway/input/input-manager.h
@@ -6,6 +6,7 @@
 #include "list.h"
 
 extern struct input_config *current_input_config;
+extern struct seat_config *current_seat_config;
 
 /**
  * The global singleton input manager
@@ -17,7 +18,6 @@ struct sway_input_device {
 	char *identifier;
 	struct wlr_input_device *wlr_device;
 	struct input_config *config;
-	struct sway_keyboard *keyboard; // managed by the seat
 	struct wl_list link;
 };
 
@@ -40,7 +40,10 @@ void sway_input_manager_set_focus(struct sway_input_manager *input,
 
 void sway_input_manager_configure_xcursor(struct sway_input_manager *input);
 
-void sway_input_manager_apply_config(struct sway_input_manager *input,
-		struct input_config *config);
+void sway_input_manager_apply_input_config(struct sway_input_manager *input,
+		struct input_config *input_config);
+
+void sway_input_manager_apply_seat_config(struct sway_input_manager *input,
+		struct seat_config *seat_config);
 
 #endif
diff --git a/include/sway/input/keyboard.h b/include/sway/input/keyboard.h
index 881805b4..89cde3fa 100644
--- a/include/sway/input/keyboard.h
+++ b/include/sway/input/keyboard.h
@@ -1,15 +1,18 @@
 #include "sway/input/seat.h"
 
 struct sway_keyboard {
-	struct sway_seat *seat;
-	struct sway_input_device *device;
+	struct sway_seat_device *seat_device;
 	struct wl_list link; // sway_seat::keyboards
 
+	struct xkb_keymap *keymap;
+
 	struct wl_listener keyboard_key;
 	struct wl_listener keyboard_modifiers;
 };
 
 struct sway_keyboard *sway_keyboard_create(struct sway_seat *seat,
-		struct sway_input_device *device);
+		struct sway_seat_device *device);
+
+void sway_keyboard_configure(struct sway_keyboard *keyboard);
 
 void sway_keyboard_destroy(struct sway_keyboard *keyboard);
diff --git a/include/sway/input/seat.h b/include/sway/input/seat.h
index bd94a357..db69f83e 100644
--- a/include/sway/input/seat.h
+++ b/include/sway/input/seat.h
@@ -4,16 +4,25 @@
 #include <wlr/types/wlr_seat.h>
 #include "sway/input/input-manager.h"
 
+struct sway_seat_device {
+	struct sway_seat *sway_seat;
+	struct sway_input_device *input_device;
+	struct sway_keyboard *keyboard;
+	struct seat_attachment_config *attachment_config;
+	struct wl_list link; // sway_seat::devices
+};
+
 struct sway_seat {
-	struct wlr_seat *seat;
+	struct wlr_seat *wlr_seat;
+	struct seat_config *config;
 	struct sway_cursor *cursor;
 	struct sway_input_manager *input;
 	swayc_t *focus;
 
-	list_t *devices;
-
 	struct wl_listener focus_destroy;
 
+	struct wl_list devices; // sway_seat_device::link
+
 	struct wl_list link; // input_manager::seats
 };
 
@@ -23,6 +32,9 @@ struct sway_seat *sway_seat_create(struct sway_input_manager *input,
 void sway_seat_add_device(struct sway_seat *seat,
 		struct sway_input_device *device);
 
+void sway_seat_configure_device(struct sway_seat *seat,
+		struct sway_input_device *device);
+
 void sway_seat_remove_device(struct sway_seat *seat,
 		struct sway_input_device *device);
 
@@ -30,4 +42,6 @@ void sway_seat_configure_xcursor(struct sway_seat *seat);
 
 void sway_seat_set_focus(struct sway_seat *seat, swayc_t *container);
 
+void sway_seat_set_config(struct sway_seat *seat, struct seat_config *seat_config);
+
 #endif
diff --git a/sway/commands.c b/sway/commands.c
index 6645436a..e003e06d 100644
--- a/sway/commands.c
+++ b/sway/commands.c
@@ -71,7 +71,24 @@ void input_cmd_apply(struct input_config *input) {
 	}
 
 	current_input_config = input;
-	sway_input_manager_apply_config(input_manager, input);
+	sway_input_manager_apply_input_config(input_manager, input);
+}
+
+void seat_cmd_apply(struct seat_config *seat) {
+	int i;
+	i = list_seq_find(config->seat_configs, seat_name_cmp, seat->name);
+	if (i >= 0) {
+		// merge existing config
+		struct seat_config *sc = config->seat_configs->items[i];
+		merge_seat_config(sc, seat);
+		free_seat_config(seat);
+		seat = sc;
+	} else {
+		list_add(config->seat_configs, seat);
+	}
+
+	current_seat_config = seat;
+	sway_input_manager_apply_seat_config(input_manager, seat);
 }
 
 /**
@@ -115,6 +132,7 @@ static struct cmd_handler handlers[] = {
 	{ "exit", cmd_exit },
 	{ "include", cmd_include },
 	{ "input", cmd_input },
+	{ "seat", cmd_seat },
 };
 
 static int handler_compare(const void *_a, const void *_b) {
@@ -135,19 +153,27 @@ static struct cmd_handler input_handlers[] = {
 	{ "natural_scroll", input_cmd_natural_scroll },
 	{ "pointer_accel", input_cmd_pointer_accel },
 	{ "scroll_method", input_cmd_scroll_method },
-	{ "seat", input_cmd_seat },
 	{ "tap", input_cmd_tap },
 };
 
+// must be in order for the bsearch
+static struct cmd_handler seat_handlers[] = {
+	{ "attach", seat_cmd_attach },
+};
+
 static struct cmd_handler *find_handler(char *line, enum cmd_status block) {
 	struct cmd_handler d = { .command=line };
 	struct cmd_handler *res = NULL;
-	sway_log(L_DEBUG, "find_handler(%s) %d", line, block == CMD_BLOCK_INPUT);
+	sway_log(L_DEBUG, "find_handler(%s) %d", line, block == CMD_BLOCK_SEAT);
 
 	if (block == CMD_BLOCK_INPUT) {
 		res = bsearch(&d, input_handlers,
 				sizeof(input_handlers) / sizeof(struct cmd_handler),
 				sizeof(struct cmd_handler), handler_compare);
+	} else if (block == CMD_BLOCK_SEAT) {
+		res = bsearch(&d, seat_handlers,
+				sizeof(seat_handlers) / sizeof(struct cmd_handler),
+				sizeof(struct cmd_handler), handler_compare);
 	} else {
 		res = bsearch(&d, handlers,
 				sizeof(handlers) / sizeof(struct cmd_handler),
diff --git a/sway/commands/input/seat.c b/sway/commands/input/seat.c
deleted file mode 100644
index 9d86ac0e..00000000
--- a/sway/commands/input/seat.c
+++ /dev/null
@@ -1,28 +0,0 @@
-#define _XOPEN_SOURCE 700
-#include <string.h>
-#include <strings.h>
-#include "sway/commands.h"
-#include "sway/input/input-manager.h"
-#include "log.h"
-
-struct cmd_results *input_cmd_seat(int argc, char **argv) {
-	sway_log(L_DEBUG, "seat for device:  %d %s",
-		current_input_config==NULL, current_input_config->identifier);
-	struct cmd_results *error = NULL;
-	if ((error = checkarg(argc, "seat", EXPECTED_AT_LEAST, 1))) {
-		return error;
-	}
-	if (!current_input_config) {
-		return cmd_results_new(CMD_FAILURE, "seat",
-			"No input device defined.");
-	}
-	struct input_config *new_config =
-		new_input_config(current_input_config->identifier);
-
-	// TODO validate seat name
-	free(new_config->seat);
-	new_config->seat = strdup(argv[0]);
-
-	input_cmd_apply(new_config);
-	return cmd_results_new(CMD_SUCCESS, NULL, NULL);
-}
diff --git a/sway/commands/seat.c b/sway/commands/seat.c
new file mode 100644
index 00000000..4f9e259b
--- /dev/null
+++ b/sway/commands/seat.c
@@ -0,0 +1,35 @@
+#include <string.h>
+#include <strings.h>
+#include "sway/commands.h"
+#include "sway/input/input-manager.h"
+#include "log.h"
+
+struct cmd_results *cmd_seat(int argc, char **argv) {
+	struct cmd_results *error = NULL;
+	if ((error = checkarg(argc, "seat", EXPECTED_AT_LEAST, 2))) {
+		return error;
+	}
+
+	if (config->reading && strcmp("{", argv[1]) == 0) {
+		current_seat_config = new_seat_config(argv[0]);
+		sway_log(L_DEBUG, "entering seat block: %s", current_seat_config->name);
+		return cmd_results_new(CMD_BLOCK_SEAT, NULL, NULL);
+	}
+
+	if (argc > 2) {
+		int argc_new = argc-2;
+		char **argv_new = argv+2;
+
+		struct cmd_results *res;
+		current_seat_config = new_seat_config(argv[0]);
+		if (strcasecmp("attach", argv[1]) == 0) {
+			res = seat_cmd_attach(argc_new, argv_new);
+		} else {
+			res = cmd_results_new(CMD_INVALID, "seat <name>", "Unknown command %s", argv[1]);
+		}
+		current_seat_config = NULL;
+		return res;
+	}
+
+	return cmd_results_new(CMD_BLOCK_SEAT, NULL, NULL);
+}
diff --git a/sway/commands/seat/attach.c b/sway/commands/seat/attach.c
new file mode 100644
index 00000000..996c1bda
--- /dev/null
+++ b/sway/commands/seat/attach.c
@@ -0,0 +1,26 @@
+#define _XOPEN_SOURCE 700
+#include <string.h>
+#include <strings.h>
+#include "sway/input/input-manager.h"
+#include "sway/commands.h"
+#include "sway/config.h"
+#include "log.h"
+#include "stringop.h"
+
+struct cmd_results *seat_cmd_attach(int argc, char **argv) {
+	struct cmd_results *error = NULL;
+	if ((error = checkarg(argc, "attach", EXPECTED_AT_LEAST, 1))) {
+		return error;
+	}
+	if (!current_seat_config) {
+		return cmd_results_new(CMD_FAILURE, "attach", "No seat defined");
+	}
+
+	struct seat_config *new_config = new_seat_config(current_seat_config->name);
+	struct seat_attachment_config *new_attachment = seat_attachment_config_new();
+	new_attachment->identifier = strdup(argv[0]);
+	list_add(new_config->attachments, new_attachment);
+
+	seat_cmd_apply(new_config);
+	return cmd_results_new(CMD_SUCCESS, NULL, NULL);
+}
diff --git a/sway/config.c b/sway/config.c
index b77b8b4b..52633ad8 100644
--- a/sway/config.c
+++ b/sway/config.c
@@ -45,6 +45,7 @@ static void config_defaults(struct sway_config *config) {
 	if (!(config->criteria = create_list())) goto cleanup;
 	if (!(config->no_focus = create_list())) goto cleanup;
 	if (!(config->input_configs = create_list())) goto cleanup;
+	if (!(config->seat_configs = create_list())) goto cleanup;
 	if (!(config->output_configs = create_list())) goto cleanup;
 
 	if (!(config->cmd_queue = create_list())) goto cleanup;
@@ -258,9 +259,7 @@ struct input_config *new_input_config(const char* identifier) {
 
 void merge_input_config(struct input_config *dst, struct input_config *src) {
 	if (src->identifier) {
-		if (dst->identifier) {
-			free(dst->identifier);
-		}
+		free(dst->identifier);
 		dst->identifier = strdup(src->identifier);
 	}
 	if (src->accel_profile != INT_MIN) {
@@ -300,7 +299,6 @@ void free_input_config(struct input_config *ic) {
 		return;
 	}
 	free(ic->identifier);
-	free(ic->seat);
 	free(ic);
 }
 
@@ -310,6 +308,128 @@ int input_identifier_cmp(const void *item, const void *data) {
 	return strcmp(ic->identifier, identifier);
 }
 
+struct seat_config *new_seat_config(const char* name) {
+	struct seat_config *seat = calloc(1, sizeof(struct seat_config));
+	if (!seat) {
+		sway_log(L_DEBUG, "Unable to allocate seat config");
+		return NULL;
+	}
+
+	sway_log(L_DEBUG, "new_seat_config(%s)", name);
+	seat->name = strdup(name);
+	if (!sway_assert(seat->name, "could not allocate name for seat")) {
+		return NULL;
+	}
+
+	seat->attachments = create_list();
+	if (!sway_assert(seat->attachments,
+				"could not allocate seat attachments list")) {
+		return NULL;
+	}
+
+	return seat;
+}
+
+struct seat_attachment_config *seat_attachment_config_new() {
+	struct seat_attachment_config *attachment =
+		calloc(1, sizeof(struct seat_attachment_config));
+	if (!attachment) {
+		sway_log(L_DEBUG, "cannot allocate attachment config");
+		return NULL;
+	}
+	return attachment;
+}
+
+static void seat_attachment_config_free(
+		struct seat_attachment_config *attachment) {
+	free(attachment->identifier);
+	free(attachment);
+	return;
+}
+
+static struct seat_attachment_config *seat_attachment_config_copy(
+		struct seat_attachment_config *attachment) {
+	struct seat_attachment_config *copy = seat_attachment_config_new();
+	if (!copy) {
+		return NULL;
+	}
+
+	copy->identifier = strdup(attachment->identifier);
+
+	return copy;
+}
+
+static void merge_seat_attachment_config(struct seat_attachment_config *dest,
+		struct seat_attachment_config *source) {
+	// nothing to merge yet, but there will be some day
+}
+
+void merge_seat_config(struct seat_config *dest, struct seat_config *source) {
+	if (source->name) {
+		free(dest->name);
+		dest->name = strdup(source->name);
+	}
+
+	for (int i = 0; i < source->attachments->length; ++i) {
+		struct seat_attachment_config *source_attachment =
+			source->attachments->items[i];
+		bool found = false;
+		for (int j = 0; j < dest->attachments->length; ++j) {
+			struct seat_attachment_config *dest_attachment =
+				dest->attachments->items[j];
+			if (strcmp(source_attachment->identifier,
+						dest_attachment->identifier) == 0) {
+				merge_seat_attachment_config(dest_attachment,
+					source_attachment);
+				found = true;
+			}
+		}
+
+		if (!found) {
+			struct seat_attachment_config *copy =
+				seat_attachment_config_copy(source_attachment);
+			if (copy) {
+				list_add(dest->attachments, copy);
+			}
+		}
+	}
+}
+
+void free_seat_config(struct seat_config *seat) {
+	if (!seat) {
+		return;
+	}
+
+	free(seat->name);
+	for (int i = 0; i < seat->attachments->length; ++i) {
+		struct seat_attachment_config *attachment =
+			seat->attachments->items[i];
+		seat_attachment_config_free(attachment);
+	}
+
+	list_free(seat->attachments);
+	free(seat);
+}
+
+int seat_name_cmp(const void *item, const void *data) {
+	const struct seat_config *sc = item;
+	const char *name = data;
+	return strcmp(sc->name, name);
+}
+
+struct seat_attachment_config *seat_config_get_attachment(
+		struct seat_config *seat_config, char *identifier) {
+	for (int i = 0; i < seat_config->attachments->length; ++i) {
+		struct seat_attachment_config *attachment =
+			seat_config->attachments->items[i];
+		if (strcmp(attachment->identifier, identifier) == 0) {
+			return attachment;
+		}
+	}
+
+	return NULL;
+}
+
 bool load_main_config(const char *file, bool is_active) {
 	char *path;
 	if (file != NULL) {
@@ -368,7 +488,7 @@ bool load_main_config(const char *file, bool is_active) {
 			char *_path = secconfigs->items[i];
 			if (stat(_path, &s) || s.st_uid != 0 || s.st_gid != 0 ||
 					(((s.st_mode & 0777) != 0644) &&
-					 (s.st_mode & 0777) != 0444)) {
+					(s.st_mode & 0777) != 0444)) {
 				sway_log(L_ERROR,
 					"Refusing to load %s - it must be owned by root "
 					"and mode 644 or 444", _path);
@@ -547,6 +667,14 @@ bool read_config(FILE *file, struct sway_config *config) {
 			}
 			break;
 
+		case CMD_BLOCK_SEAT:
+			if (block == CMD_BLOCK_END) {
+				block = CMD_BLOCK_SEAT;
+			} else {
+				sway_log(L_ERROR, "Invalid block '%s'", line);
+			}
+			break;
+
 		case CMD_BLOCK_BAR:
 			if (block == CMD_BLOCK_END) {
 				block = CMD_BLOCK_BAR;
@@ -601,6 +729,12 @@ bool read_config(FILE *file, struct sway_config *config) {
 				block = CMD_BLOCK_END;
 				break;
 
+			case CMD_BLOCK_SEAT:
+				sway_log(L_DEBUG, "End of seat block");
+				current_seat_config = NULL;
+				block = CMD_BLOCK_END;
+				break;
+
 			case CMD_BLOCK_BAR:
 				sway_log(L_DEBUG, "End of bar block");
 				config->current_bar = NULL;
diff --git a/sway/input/cursor.c b/sway/input/cursor.c
index 217c2ddb..3aa2d1bc 100644
--- a/sway/input/cursor.c
+++ b/sway/input/cursor.c
@@ -24,7 +24,7 @@ static void cursor_update_position(struct sway_cursor *cursor) {
 
 static void cursor_send_pointer_motion(struct sway_cursor *cursor,
 		uint32_t time) {
-	struct wlr_seat *seat = cursor->seat->seat;
+	struct wlr_seat *seat = cursor->seat->wlr_seat;
 	struct wlr_surface *surface = NULL;
 	double sx, sy;
 	swayc_t *swayc =
@@ -72,7 +72,7 @@ static void handle_cursor_button(struct wl_listener *listener, void *data) {
 		sway_seat_set_focus(cursor->seat, swayc);
 	}
 
-	wlr_seat_pointer_notify_button(cursor->seat->seat, event->time_msec,
+	wlr_seat_pointer_notify_button(cursor->seat->wlr_seat, event->time_msec,
 		event->button, event->state);
 }
 
@@ -80,7 +80,7 @@ static void handle_cursor_axis(struct wl_listener *listener, void *data) {
 	struct sway_cursor *cursor =
 		wl_container_of(listener, cursor, axis);
 	struct wlr_event_pointer_axis *event = data;
-	wlr_seat_pointer_notify_axis(cursor->seat->seat, event->time_msec,
+	wlr_seat_pointer_notify_axis(cursor->seat->wlr_seat, event->time_msec,
 		event->orientation, event->delta);
 }
 
@@ -173,7 +173,7 @@ struct sway_cursor *sway_cursor_create(struct sway_seat *seat) {
 	wl_signal_add(&wlr_cursor->events.tablet_tool_tip, &cursor->tool_tip);
 	cursor->tool_tip.notify = handle_tool_tip;
 
-	wl_signal_add(&seat->seat->events.request_set_cursor,
+	wl_signal_add(&seat->wlr_seat->events.request_set_cursor,
 			&cursor->request_set_cursor);
 	cursor->request_set_cursor.notify = handle_request_set_cursor;
 
diff --git a/sway/input/input-manager.c b/sway/input/input-manager.c
index b07a733e..1950b6d9 100644
--- a/sway/input/input-manager.c
+++ b/sway/input/input-manager.c
@@ -18,12 +18,13 @@ static const char *default_seat = "seat0";
 struct sway_input_manager *input_manager;
 
 struct input_config *current_input_config = NULL;
+struct seat_config *current_seat_config = NULL;
 
 static struct sway_seat *input_manager_get_seat(
 		struct sway_input_manager *input, const char *seat_name) {
 	struct sway_seat *seat = NULL;
 	wl_list_for_each(seat, &input->seats, link) {
-		if (strcmp(seat->seat->name, seat_name) == 0) {
+		if (strcmp(seat->wlr_seat->name, seat_name) == 0) {
 			return seat;
 		}
 	}
@@ -58,56 +59,88 @@ static char *get_device_identifier(struct wlr_input_device *device) {
 	return identifier;
 }
 
-static struct sway_input_device *input_sway_device_from_wlr(struct sway_input_manager *input,
-		struct wlr_input_device *device) {
-	struct sway_input_device *sway_device = NULL;
-	wl_list_for_each(sway_device, &input->devices, link) {
-		if (sway_device->wlr_device == device) {
-			return sway_device;
+static struct sway_input_device *input_sway_device_from_wlr(
+		struct sway_input_manager *input, struct wlr_input_device *device) {
+	struct sway_input_device *input_device = NULL;
+	wl_list_for_each(input_device, &input->devices, link) {
+		if (input_device->wlr_device == device) {
+			return input_device;
 		}
 	}
 	return NULL;
 }
 
-static struct sway_input_device *input_sway_device_from_config(struct sway_input_manager *input,
-		struct input_config *config) {
-	struct sway_input_device *sway_device = NULL;
-	wl_list_for_each(sway_device, &input->devices, link) {
-		if (strcmp(sway_device->identifier, config->identifier) == 0) {
-			return sway_device;
+static struct sway_input_device *input_sway_device_from_config(
+		struct sway_input_manager *input, struct input_config *config) {
+	struct sway_input_device *input_device = NULL;
+	wl_list_for_each(input_device, &input->devices, link) {
+		if (strcmp(input_device->identifier, config->identifier) == 0) {
+			return input_device;
 		}
 	}
 	return NULL;
 }
 
+static struct sway_input_device *input_sway_device_from_identifier(
+		struct sway_input_manager *input, char *identifier) {
+	struct sway_input_device *input_device = NULL;
+	wl_list_for_each(input_device, &input->devices, link) {
+		if (strcmp(input_device->identifier, identifier) == 0) {
+			return input_device;
+		}
+	}
+	return NULL;
+}
+
+static bool input_has_seat_configuration(struct sway_input_manager *input) {
+	struct sway_seat *seat = NULL;
+	wl_list_for_each(seat, &input->seats, link) {
+		if (seat->config) {
+			return true;
+		}
+	}
+
+	return false;
+}
+
 static void input_add_notify(struct wl_listener *listener, void *data) {
 	struct sway_input_manager *input =
 		wl_container_of(listener, input, input_add);
 	struct wlr_input_device *device = data;
 
-	struct sway_input_device *sway_device =
+	struct sway_input_device *input_device =
 		calloc(1, sizeof(struct sway_input_device));
-	if (!sway_assert(sway_device, "could not allocate input device")) {
+	if (!sway_assert(input_device, "could not allocate input device")) {
 		return;
 	}
 
-	sway_device->wlr_device = device;
-	sway_device->identifier = get_device_identifier(device);
-	wl_list_insert(&input->devices, &sway_device->link);
+	input_device->wlr_device = device;
+	input_device->identifier = get_device_identifier(device);
+	wl_list_insert(&input->devices, &input_device->link);
 
 	// find config
 	for (int i = 0; i < config->input_configs->length; ++i) {
 		struct input_config *input_config = config->input_configs->items[i];
-		if (strcmp(input_config->identifier, sway_device->identifier) == 0) {
-			sway_device->config = input_config;
+		if (strcmp(input_config->identifier, input_device->identifier) == 0) {
+			input_device->config = input_config;
 			break;
 		}
 	}
 
-	const char *seat_name =
-		(sway_device->config ? sway_device->config->seat : default_seat);
-	struct sway_seat *seat = input_manager_get_seat(input, seat_name);
-	sway_seat_add_device(seat, sway_device);
+	struct sway_seat *seat = NULL;
+	if (!input_has_seat_configuration(input)) {
+		seat = input_manager_get_seat(input, default_seat);
+		sway_seat_add_device(seat, input_device);
+		return;
+	}
+
+	wl_list_for_each(seat, &input->seats, link) {
+		if (seat->config &&
+				(seat_config_get_attachment(seat->config, input_device->identifier) ||
+				seat_config_get_attachment(seat->config, "*"))) {
+			sway_seat_add_device(seat, input_device);
+		}
+	}
 }
 
 static void input_remove_notify(struct wl_listener *listener, void *data) {
@@ -115,21 +148,21 @@ static void input_remove_notify(struct wl_listener *listener, void *data) {
 		wl_container_of(listener, input, input_remove);
 	struct wlr_input_device *device = data;
 
-	struct sway_input_device *sway_device =
+	struct sway_input_device *input_device =
 		input_sway_device_from_wlr(input, device);
 
-	if (!sway_assert(sway_device, "could not find sway device")) {
+	if (!sway_assert(input_device, "could not find sway device")) {
 		return;
 	}
 
 	struct sway_seat *seat = NULL;
 	wl_list_for_each(seat, &input->seats, link) {
-		sway_seat_remove_device(seat, sway_device);
+		sway_seat_remove_device(seat, input_device);
 	}
 
-	wl_list_remove(&sway_device->link);
-	free(sway_device->identifier);
-	free(sway_device);
+	wl_list_remove(&input_device->link);
+	free(input_device->identifier);
+	free(input_device);
 }
 
 struct sway_input_manager *sway_input_manager_create(
@@ -139,7 +172,6 @@ struct sway_input_manager *sway_input_manager_create(
 	if (!input) {
 		return NULL;
 	}
-	// XXX probably don't need the full server
 	input->server = server;
 
 	wl_list_init(&input->devices);
@@ -177,22 +209,53 @@ void sway_input_manager_set_focus(struct sway_input_manager *input,
 	}
 }
 
-void sway_input_manager_apply_config(struct sway_input_manager *input,
+void sway_input_manager_apply_input_config(struct sway_input_manager *input,
 		struct input_config *input_config) {
-	struct sway_input_device *sway_device =
+	struct sway_input_device *input_device =
 		input_sway_device_from_config(input, input_config);
-	if (!sway_device) {
+	if (!input_device) {
 		return;
 	}
+	input_device->config = input_config;
 
 	struct sway_seat *seat = NULL;
 	wl_list_for_each(seat, &input->seats, link) {
-		sway_seat_remove_device(seat, sway_device);
+		sway_seat_configure_device(seat, input_device);
+	}
+}
+
+void sway_input_manager_apply_seat_config(struct sway_input_manager *input,
+		struct seat_config *seat_config) {
+	struct sway_seat *seat = input_manager_get_seat(input, seat_config->name);
+	// the old config is invalid so clear it
+	sway_seat_set_config(seat, NULL);
+
+	// clear devices
+	struct sway_input_device *input_device = NULL;
+	wl_list_for_each(input_device, &input->devices, link) {
+		sway_seat_remove_device(seat, input_device);
 	}
 
-	const char *seat_name = (input_config->seat ? input_config->seat : default_seat);
-	seat = input_manager_get_seat(input, seat_name);
-	sway_seat_add_device(seat, sway_device);
+	if (seat_config_get_attachment(seat_config, "*")) {
+		wl_list_for_each(input_device, &input->devices, link) {
+			sway_seat_add_device(seat, input_device);
+		}
+	} else {
+		for (int i = 0; i < seat_config->attachments->length; ++i) {
+			struct seat_attachment_config *attachment =
+				seat_config->attachments->items[i];
+
+			struct sway_input_device *device =
+				input_sway_device_from_identifier(input,
+					attachment->identifier);
+
+			if (device) {
+				sway_seat_add_device(seat, device);
+			}
+		}
+	}
+
+	sway_seat_set_config(seat, seat_config);
 }
 
 void sway_input_manager_configure_xcursor(struct sway_input_manager *input) {
diff --git a/sway/input/keyboard.c b/sway/input/keyboard.c
index 6a792c65..53db3270 100644
--- a/sway/input/keyboard.c
+++ b/sway/input/keyboard.c
@@ -5,32 +5,46 @@
 static void handle_keyboard_key(struct wl_listener *listener, void *data) {
 	struct sway_keyboard *keyboard =
 		wl_container_of(listener, keyboard, keyboard_key);
+	struct wlr_seat *wlr_seat = keyboard->seat_device->sway_seat->wlr_seat;
+	struct wlr_input_device *wlr_device =
+		keyboard->seat_device->input_device->wlr_device;
 	struct wlr_event_keyboard_key *event = data;
-	wlr_seat_set_keyboard(keyboard->seat->seat, keyboard->device->wlr_device);
-	wlr_seat_keyboard_notify_key(keyboard->seat->seat, event->time_msec,
-		event->keycode, event->state);
+	wlr_keyboard_set_keymap(wlr_device->keyboard, keyboard->keymap);
+	wlr_seat_set_keyboard(wlr_seat, wlr_device);
+	wlr_seat_keyboard_notify_key(wlr_seat, event->time_msec,event->keycode,
+		event->state);
 }
 
 static void handle_keyboard_modifiers(struct wl_listener *listener,
 		void *data) {
 	struct sway_keyboard *keyboard =
 		wl_container_of(listener, keyboard, keyboard_modifiers);
-	wlr_seat_set_keyboard(keyboard->seat->seat, keyboard->device->wlr_device);
-	wlr_seat_keyboard_notify_modifiers(keyboard->seat->seat);
+	struct wlr_seat *wlr_seat = keyboard->seat_device->sway_seat->wlr_seat;
+	struct wlr_input_device *wlr_device =
+		keyboard->seat_device->input_device->wlr_device;
+	wlr_keyboard_set_keymap(wlr_device->keyboard, keyboard->keymap);
+	wlr_seat_set_keyboard(wlr_seat, wlr_device);
+	wlr_seat_keyboard_notify_modifiers(wlr_seat);
 }
 
 struct sway_keyboard *sway_keyboard_create(struct sway_seat *seat,
-		struct sway_input_device *device) {
+		struct sway_seat_device *device) {
 	struct sway_keyboard *keyboard =
 		calloc(1, sizeof(struct sway_keyboard));
 	if (!sway_assert(keyboard, "could not allocate sway keyboard")) {
 		return NULL;
 	}
 
-	keyboard->device = device;
-	keyboard->seat = seat;
+	keyboard->seat_device = device;
+	device->keyboard = keyboard;
 
-	// TODO keyboard config
+	wl_list_init(&keyboard->keyboard_key.link);
+	wl_list_init(&keyboard->keyboard_modifiers.link);
+
+	return keyboard;
+}
+
+void sway_keyboard_configure(struct sway_keyboard *keyboard) {
 	struct xkb_rule_names rules;
 	memset(&rules, 0, sizeof(rules));
 	rules.rules = getenv("XKB_DEFAULT_RULES");
@@ -38,27 +52,32 @@ struct sway_keyboard *sway_keyboard_create(struct sway_seat *seat,
 	rules.layout = getenv("XKB_DEFAULT_LAYOUT");
 	rules.variant = getenv("XKB_DEFAULT_VARIANT");
 	rules.options = getenv("XKB_DEFAULT_OPTIONS");
+
 	struct xkb_context *context = xkb_context_new(XKB_CONTEXT_NO_FLAGS);
 	if (!sway_assert(context, "cannot create XKB context")) {
-		return NULL;
+		return;
 	}
 
-	wlr_keyboard_set_keymap(device->wlr_device->keyboard,
-		xkb_map_new_from_names(context, &rules, XKB_KEYMAP_COMPILE_NO_FLAGS));
+	keyboard->keymap =
+		xkb_keymap_new_from_names(context, &rules, XKB_KEYMAP_COMPILE_NO_FLAGS);
+	wlr_keyboard_set_keymap(keyboard->seat_device->input_device->wlr_device->keyboard, keyboard->keymap);
 	xkb_context_unref(context);
 
-	wl_signal_add(&device->wlr_device->keyboard->events.key,
+	wl_list_remove(&keyboard->keyboard_key.link);
+	wl_signal_add(
+		&keyboard->seat_device->input_device->wlr_device->keyboard->events.key,
 		&keyboard->keyboard_key);
 	keyboard->keyboard_key.notify = handle_keyboard_key;
 
-	wl_signal_add(&device->wlr_device->keyboard->events.modifiers,
+	wl_list_remove(&keyboard->keyboard_modifiers.link);
+	wl_signal_add(
+		&keyboard->seat_device->input_device->wlr_device->keyboard->events.modifiers,
 		&keyboard->keyboard_modifiers);
 	keyboard->keyboard_modifiers.notify = handle_keyboard_modifiers;
-
-	return keyboard;
 }
 
 void sway_keyboard_destroy(struct sway_keyboard *keyboard) {
+	xkb_keymap_unref(keyboard->keymap);
 	wl_list_remove(&keyboard->keyboard_key.link);
 	wl_list_remove(&keyboard->keyboard_modifiers.link);
 	wl_list_remove(&keyboard->link);
diff --git a/sway/input/seat.c b/sway/input/seat.c
index 80c6424f..1b25419b 100644
--- a/sway/input/seat.c
+++ b/sway/input/seat.c
@@ -9,6 +9,18 @@
 #include "sway/view.h"
 #include "log.h"
 
+static void seat_device_destroy(struct sway_seat_device *seat_device) {
+	if (!seat_device) {
+		return;
+	}
+
+	sway_keyboard_destroy(seat_device->keyboard);
+	wlr_cursor_detach_input_device(seat_device->sway_seat->cursor->cursor,
+		seat_device->input_device->wlr_device);
+	wl_list_remove(&seat_device->link);
+	free(seat_device);
+}
+
 struct sway_seat *sway_seat_create(struct sway_input_manager *input,
 		const char *seat_name) {
 	struct sway_seat *seat = calloc(1, sizeof(struct sway_seat));
@@ -16,22 +28,22 @@ struct sway_seat *sway_seat_create(struct sway_input_manager *input,
 		return NULL;
 	}
 
-	seat->seat = wlr_seat_create(input->server->wl_display, seat_name);
-	if (!sway_assert(seat->seat, "could not allocate seat")) {
+	seat->wlr_seat = wlr_seat_create(input->server->wl_display, seat_name);
+	if (!sway_assert(seat->wlr_seat, "could not allocate seat")) {
 		return NULL;
 	}
 
 	seat->cursor = sway_cursor_create(seat);
 	if (!seat->cursor) {
-		wlr_seat_destroy(seat->seat);
+		wlr_seat_destroy(seat->wlr_seat);
 		free(seat);
 		return NULL;
 	}
 
 	seat->input = input;
-	seat->devices = create_list();
+	wl_list_init(&seat->devices);
 
-	wlr_seat_set_capabilities(seat->seat,
+	wlr_seat_set_capabilities(seat->wlr_seat,
 		WL_SEAT_CAPABILITY_KEYBOARD |
 		WL_SEAT_CAPABILITY_POINTER |
 		WL_SEAT_CAPABILITY_TOUCH);
@@ -43,88 +55,94 @@ struct sway_seat *sway_seat_create(struct sway_input_manager *input,
 	return seat;
 }
 
-static void seat_add_pointer(struct sway_seat *seat,
-		struct sway_input_device *sway_device) {
+static void seat_configure_pointer(struct sway_seat *seat,
+		struct sway_seat_device *sway_device) {
 	// TODO pointer configuration
 	wlr_cursor_attach_input_device(seat->cursor->cursor,
-		sway_device->wlr_device);
+		sway_device->input_device->wlr_device);
 }
 
-static void seat_add_keyboard(struct sway_seat *seat,
-		struct sway_input_device *device) {
-	// TODO keyboard configuration
-	sway_keyboard_create(seat, device);
-	wlr_seat_set_keyboard(seat->seat, device->wlr_device);
+static void seat_configure_keyboard(struct sway_seat *seat,
+		struct sway_seat_device *seat_device) {
+	if (!seat_device->keyboard) {
+		sway_keyboard_create(seat, seat_device);
+	}
+	sway_keyboard_configure(seat_device->keyboard);
 }
 
-bool sway_seat_has_device(struct sway_seat *seat,
-		struct sway_input_device *device) {
-	return false;
+static struct sway_seat_device *sway_seat_get_device(struct sway_seat *seat,
+		struct sway_input_device *input_device) {
+	struct sway_seat_device *seat_device = NULL;
+	wl_list_for_each(seat_device, &seat->devices, link) {
+		if (seat_device->input_device == input_device) {
+			return seat_device;
+		}
+	}
+
+	return NULL;
+}
+
+void sway_seat_configure_device(struct sway_seat *seat,
+		struct sway_input_device *input_device) {
+	struct sway_seat_device *seat_device =
+		sway_seat_get_device(seat, input_device);
+	if (!seat_device) {
+		return;
+	}
+
+	if (seat->config) {
+		seat_device->attachment_config =
+			seat_config_get_attachment(seat->config, input_device->identifier);
+	}
+
+	switch (input_device->wlr_device->type) {
+		case WLR_INPUT_DEVICE_POINTER:
+			seat_configure_pointer(seat, seat_device);
+			break;
+		case WLR_INPUT_DEVICE_KEYBOARD:
+			seat_configure_keyboard(seat, seat_device);
+			wlr_seat_set_keyboard(seat->wlr_seat,
+				seat_device->input_device->wlr_device);
+			break;
+		case WLR_INPUT_DEVICE_TOUCH:
+		case WLR_INPUT_DEVICE_TABLET_PAD:
+		case WLR_INPUT_DEVICE_TABLET_TOOL:
+			sway_log(L_DEBUG, "TODO: configure other devices");
+			break;
+	}
 }
 
 void sway_seat_add_device(struct sway_seat *seat,
-		struct sway_input_device *device) {
-	if (sway_seat_has_device(seat, device)) {
+		struct sway_input_device *input_device) {
+	if (sway_seat_get_device(seat, input_device)) {
 		return;
 	}
 
-	sway_log(L_DEBUG, "input add: %s", device->identifier);
-	switch (device->wlr_device->type) {
-		case WLR_INPUT_DEVICE_POINTER:
-			seat_add_pointer(seat, device);
-			break;
-		case WLR_INPUT_DEVICE_KEYBOARD:
-			seat_add_keyboard(seat, device);
-			break;
-		case WLR_INPUT_DEVICE_TOUCH:
-		case WLR_INPUT_DEVICE_TABLET_PAD:
-		case WLR_INPUT_DEVICE_TABLET_TOOL:
-			sway_log(L_DEBUG, "TODO: add other devices");
-			break;
+	struct sway_seat_device *seat_device =
+		calloc(1, sizeof(struct sway_seat_device));
+	if (!seat_device) {
+		sway_log(L_DEBUG, "could not allocate seat device");
+		return;
 	}
 
-	list_add(seat->devices, device);
-}
+	seat_device->sway_seat = seat;
+	seat_device->input_device = input_device;
+	wl_list_insert(&seat->devices, &seat_device->link);
 
-static void seat_remove_keyboard(struct sway_seat *seat,
-		struct sway_input_device *device) {
-	if (device && device->keyboard) {
-		sway_keyboard_destroy(device->keyboard);
-	}
-}
-
-static void seat_remove_pointer(struct sway_seat *seat,
-		struct sway_input_device *device) {
-	wlr_cursor_detach_input_device(seat->cursor->cursor, device->wlr_device);
+	sway_seat_configure_device(seat, input_device);
 }
 
 void sway_seat_remove_device(struct sway_seat *seat,
-		struct sway_input_device *device) {
-	sway_log(L_DEBUG, "input remove: %s", device->identifier);
-	if (!sway_seat_has_device(seat, device)) {
+		struct sway_input_device *input_device) {
+	sway_log(L_DEBUG, "input remove: %s", input_device->identifier);
+	struct sway_seat_device *seat_device =
+		sway_seat_get_device(seat, input_device);
+
+	if (!seat_device) {
 		return;
 	}
 
-	switch (device->wlr_device->type) {
-		case WLR_INPUT_DEVICE_POINTER:
-			seat_remove_pointer(seat, device);
-			break;
-		case WLR_INPUT_DEVICE_KEYBOARD:
-			seat_remove_keyboard(seat, device);
-			break;
-		case WLR_INPUT_DEVICE_TOUCH:
-		case WLR_INPUT_DEVICE_TABLET_PAD:
-		case WLR_INPUT_DEVICE_TABLET_TOOL:
-			sway_log(L_DEBUG, "TODO: remove other devices");
-			break;
-	}
-
-	for (int i = 0; i < seat->devices->length; ++i) {
-		if (seat->devices->items[i] == device) {
-			list_del(seat->devices, i);
-			break;
-		}
-	}
+	seat_device_destroy(seat_device);
 }
 
 void sway_seat_configure_xcursor(struct sway_seat *seat) {
@@ -135,7 +153,8 @@ void sway_seat_configure_xcursor(struct sway_seat *seat) {
 		seat->cursor->xcursor_manager =
 			wlr_xcursor_manager_create("default", 24);
 		if (sway_assert(seat->cursor->xcursor_manager,
-					"Cannot create XCursor manager for theme %s", cursor_theme)) {
+					"Cannot create XCursor manager for theme %s",
+					cursor_theme)) {
 			return;
 		}
 	}
@@ -183,7 +202,7 @@ void sway_seat_set_focus(struct sway_seat *seat, swayc_t *container) {
 		view->iface.set_activated(view, true);
 		wl_signal_add(&container->events.destroy, &seat->focus_destroy);
 		seat->focus_destroy.notify = handle_focus_destroy;
-		wlr_seat_keyboard_notify_enter(seat->seat, view->surface);
+		wlr_seat_keyboard_notify_enter(seat->wlr_seat, view->surface);
 	}
 
 	seat->focus = container;
@@ -195,3 +214,29 @@ void sway_seat_set_focus(struct sway_seat *seat, swayc_t *container) {
 
 	}
 }
+
+void sway_seat_set_config(struct sway_seat *seat,
+		struct seat_config *seat_config) {
+	// clear configs
+	seat->config = NULL;
+
+	struct sway_seat_device *seat_device = NULL;
+	wl_list_for_each(seat_device, &seat->devices, link) {
+		seat_device->attachment_config = NULL;
+	}
+
+	if (!seat_config) {
+		return;
+	}
+
+	// add configs
+	seat->config = seat_config;
+
+	wl_list_for_each(seat_device, &seat->devices, link) {
+		seat_device->attachment_config =
+			seat_config_get_attachment(seat_config,
+				seat_device->input_device->identifier);
+		sway_seat_configure_device(seat, seat_device->input_device);
+	}
+
+}
diff --git a/sway/meson.build b/sway/meson.build
index fad1f88c..ad8160eb 100644
--- a/sway/meson.build
+++ b/sway/meson.build
@@ -11,7 +11,8 @@ sway_sources = files(
 	'commands/exec_always.c',
 	'commands/include.c',
 	'commands/input.c',
-	'commands/input/seat.c',
+	'commands/seat.c',
+	'commands/seat/attach.c',
 	'commands/input/accel_profile.c',
 	'commands/input/click_method.c',
 	'commands/input/drag_lock.c',

From 9eecbb5d8a988a0dded57ead1982dd0121071454 Mon Sep 17 00:00:00 2001
From: Tony Crisci <tony@dubstepdish.com>
Date: Fri, 15 Dec 2017 05:22:51 -0500
Subject: [PATCH 21/49] xkb config

---
 include/sway/commands.h           |  5 ++++
 include/sway/config.h             |  6 +++++
 sway/commands.c                   |  5 ++++
 sway/commands/input/xkb_layout.c  | 24 +++++++++++++++++++
 sway/commands/input/xkb_model.c   | 24 +++++++++++++++++++
 sway/commands/input/xkb_options.c | 24 +++++++++++++++++++
 sway/commands/input/xkb_rules.c   | 24 +++++++++++++++++++
 sway/commands/input/xkb_variant.c | 24 +++++++++++++++++++
 sway/config.c                     | 20 ++++++++++++++++
 sway/input/keyboard.c             | 38 +++++++++++++++++++++++++++----
 sway/main.c                       |  1 +
 sway/meson.build                  |  5 ++++
 12 files changed, 195 insertions(+), 5 deletions(-)
 create mode 100644 sway/commands/input/xkb_layout.c
 create mode 100644 sway/commands/input/xkb_model.c
 create mode 100644 sway/commands/input/xkb_options.c
 create mode 100644 sway/commands/input/xkb_rules.c
 create mode 100644 sway/commands/input/xkb_variant.c

diff --git a/include/sway/commands.h b/include/sway/commands.h
index ce74e1ed..61950d0d 100644
--- a/include/sway/commands.h
+++ b/include/sway/commands.h
@@ -195,6 +195,11 @@ sway_cmd input_cmd_natural_scroll;
 sway_cmd input_cmd_pointer_accel;
 sway_cmd input_cmd_scroll_method;
 sway_cmd input_cmd_tap;
+sway_cmd input_cmd_xkb_layout;
+sway_cmd input_cmd_xkb_model;
+sway_cmd input_cmd_xkb_options;
+sway_cmd input_cmd_xkb_rules;
+sway_cmd input_cmd_xkb_variant;
 
 sway_cmd seat_cmd_attach;
 
diff --git a/include/sway/config.h b/include/sway/config.h
index 5df5d61e..9bfda259 100644
--- a/include/sway/config.h
+++ b/include/sway/config.h
@@ -67,6 +67,12 @@ struct input_config {
 	int send_events;
 	int tap;
 
+	char *xkb_layout;
+	char *xkb_model;
+	char *xkb_options;
+	char *xkb_rules;
+	char *xkb_variant;
+
 	bool capturable;
 	struct wlr_box region;
 };
diff --git a/sway/commands.c b/sway/commands.c
index e003e06d..b8948fb7 100644
--- a/sway/commands.c
+++ b/sway/commands.c
@@ -154,6 +154,11 @@ static struct cmd_handler input_handlers[] = {
 	{ "pointer_accel", input_cmd_pointer_accel },
 	{ "scroll_method", input_cmd_scroll_method },
 	{ "tap", input_cmd_tap },
+	{ "xkb_layout", input_cmd_xkb_layout },
+	{ "xkb_model", input_cmd_xkb_model },
+	{ "xkb_options", input_cmd_xkb_options },
+	{ "xkb_rules", input_cmd_xkb_rules },
+	{ "xkb_variant", input_cmd_xkb_variant },
 };
 
 // must be in order for the bsearch
diff --git a/sway/commands/input/xkb_layout.c b/sway/commands/input/xkb_layout.c
new file mode 100644
index 00000000..9a9ce044
--- /dev/null
+++ b/sway/commands/input/xkb_layout.c
@@ -0,0 +1,24 @@
+#define _XOPEN_SOURCE 700
+#include "sway/commands.h"
+#include "sway/input/input-manager.h"
+#include "log.h"
+
+struct cmd_results *input_cmd_xkb_layout(int argc, char **argv) {
+	sway_log(L_DEBUG, "xkb layout for device: %s", current_input_config->identifier);
+	struct cmd_results *error = NULL;
+	if ((error = checkarg(argc, "xkb_layout", EXPECTED_AT_LEAST, 1))) {
+		return error;
+	}
+	if (!current_input_config) {
+		return cmd_results_new(CMD_FAILURE, "xkb_layout", "No input device defined.");
+	}
+	struct input_config *new_config =
+		new_input_config(current_input_config->identifier);
+
+	new_config->xkb_layout = strdup(argv[0]);
+
+	sway_log(L_DEBUG, "apply-xkb_layout for device: %s",
+		current_input_config->identifier);
+	input_cmd_apply(new_config);
+	return cmd_results_new(CMD_SUCCESS, NULL, NULL);
+}
diff --git a/sway/commands/input/xkb_model.c b/sway/commands/input/xkb_model.c
new file mode 100644
index 00000000..14a50ffb
--- /dev/null
+++ b/sway/commands/input/xkb_model.c
@@ -0,0 +1,24 @@
+#define _XOPEN_SOURCE 700
+#include "sway/commands.h"
+#include "sway/input/input-manager.h"
+#include "log.h"
+
+struct cmd_results *input_cmd_xkb_model(int argc, char **argv) {
+	sway_log(L_DEBUG, "xkb model for device: %s", current_input_config->identifier);
+	struct cmd_results *error = NULL;
+	if ((error = checkarg(argc, "xkb_model", EXPECTED_AT_LEAST, 1))) {
+		return error;
+	}
+	if (!current_input_config) {
+		return cmd_results_new(CMD_FAILURE, "xkb_model", "No input device defined.");
+	}
+	struct input_config *new_config =
+		new_input_config(current_input_config->identifier);
+
+	new_config->xkb_model = strdup(argv[0]);
+
+	sway_log(L_DEBUG, "apply-xkb_model for device: %s",
+		current_input_config->identifier);
+	input_cmd_apply(new_config);
+	return cmd_results_new(CMD_SUCCESS, NULL, NULL);
+}
diff --git a/sway/commands/input/xkb_options.c b/sway/commands/input/xkb_options.c
new file mode 100644
index 00000000..67eb5342
--- /dev/null
+++ b/sway/commands/input/xkb_options.c
@@ -0,0 +1,24 @@
+#define _XOPEN_SOURCE 700
+#include "sway/commands.h"
+#include "sway/input/input-manager.h"
+#include "log.h"
+
+struct cmd_results *input_cmd_xkb_options(int argc, char **argv) {
+	sway_log(L_DEBUG, "xkb options for device: %s", current_input_config->identifier);
+	struct cmd_results *error = NULL;
+	if ((error = checkarg(argc, "xkb_options", EXPECTED_AT_LEAST, 1))) {
+		return error;
+	}
+	if (!current_input_config) {
+		return cmd_results_new(CMD_FAILURE, "xkb_options", "No input device defined.");
+	}
+	struct input_config *new_config =
+		new_input_config(current_input_config->identifier);
+
+	new_config->xkb_options = strdup(argv[0]);
+
+	sway_log(L_DEBUG, "apply-xkb_options for device: %s",
+		current_input_config->identifier);
+	input_cmd_apply(new_config);
+	return cmd_results_new(CMD_SUCCESS, NULL, NULL);
+}
diff --git a/sway/commands/input/xkb_rules.c b/sway/commands/input/xkb_rules.c
new file mode 100644
index 00000000..3eda0bdd
--- /dev/null
+++ b/sway/commands/input/xkb_rules.c
@@ -0,0 +1,24 @@
+#define _XOPEN_SOURCE 700
+#include "sway/commands.h"
+#include "sway/input/input-manager.h"
+#include "log.h"
+
+struct cmd_results *input_cmd_xkb_rules(int argc, char **argv) {
+	sway_log(L_DEBUG, "xkb rules for device: %s", current_input_config->identifier);
+	struct cmd_results *error = NULL;
+	if ((error = checkarg(argc, "xkb_rules", EXPECTED_AT_LEAST, 1))) {
+		return error;
+	}
+	if (!current_input_config) {
+		return cmd_results_new(CMD_FAILURE, "xkb_rules", "No input device defined.");
+	}
+	struct input_config *new_config =
+		new_input_config(current_input_config->identifier);
+
+	new_config->xkb_rules = strdup(argv[0]);
+
+	sway_log(L_DEBUG, "apply-xkb_rules for device: %s",
+		current_input_config->identifier);
+	input_cmd_apply(new_config);
+	return cmd_results_new(CMD_SUCCESS, NULL, NULL);
+}
diff --git a/sway/commands/input/xkb_variant.c b/sway/commands/input/xkb_variant.c
new file mode 100644
index 00000000..c7f93ad4
--- /dev/null
+++ b/sway/commands/input/xkb_variant.c
@@ -0,0 +1,24 @@
+#define _XOPEN_SOURCE 700
+#include "sway/commands.h"
+#include "sway/input/input-manager.h"
+#include "log.h"
+
+struct cmd_results *input_cmd_xkb_variant(int argc, char **argv) {
+	sway_log(L_DEBUG, "xkb variant for device: %s", current_input_config->identifier);
+	struct cmd_results *error = NULL;
+	if ((error = checkarg(argc, "xkb_variant", EXPECTED_AT_LEAST, 1))) {
+		return error;
+	}
+	if (!current_input_config) {
+		return cmd_results_new(CMD_FAILURE, "xkb_variant", "No input device defined.");
+	}
+	struct input_config *new_config =
+		new_input_config(current_input_config->identifier);
+
+	new_config->xkb_variant = strdup(argv[0]);
+
+	sway_log(L_DEBUG, "apply-xkb_variant for device: %s",
+		current_input_config->identifier);
+	input_cmd_apply(new_config);
+	return cmd_results_new(CMD_SUCCESS, NULL, NULL);
+}
diff --git a/sway/config.c b/sway/config.c
index 52633ad8..4e34aa8c 100644
--- a/sway/config.c
+++ b/sway/config.c
@@ -292,6 +292,26 @@ void merge_input_config(struct input_config *dst, struct input_config *src) {
 	if (src->tap != INT_MIN) {
 		dst->tap = src->tap;
 	}
+	if (src->xkb_layout) {
+		free(dst->xkb_layout);
+		dst->xkb_layout = strdup(src->xkb_layout);
+	}
+	if (src->xkb_model) {
+		free(dst->xkb_model);
+		dst->xkb_model = strdup(src->xkb_model);
+	}
+	if (src->xkb_options) {
+		free(dst->xkb_options);
+		dst->xkb_options = strdup(src->xkb_options);
+	}
+	if (src->xkb_rules) {
+		free(dst->xkb_rules);
+		dst->xkb_rules = strdup(src->xkb_rules);
+	}
+	if (src->xkb_variant) {
+		free(dst->xkb_variant);
+		dst->xkb_variant = strdup(src->xkb_variant);
+	}
 }
 
 void free_input_config(struct input_config *ic) {
diff --git a/sway/input/keyboard.c b/sway/input/keyboard.c
index 53db3270..2ab0206a 100644
--- a/sway/input/keyboard.c
+++ b/sway/input/keyboard.c
@@ -1,5 +1,6 @@
 #include "sway/input/seat.h"
 #include "sway/input/keyboard.h"
+#include "sway/input/input-manager.h"
 #include "log.h"
 
 static void handle_keyboard_key(struct wl_listener *listener, void *data) {
@@ -47,17 +48,44 @@ struct sway_keyboard *sway_keyboard_create(struct sway_seat *seat,
 void sway_keyboard_configure(struct sway_keyboard *keyboard) {
 	struct xkb_rule_names rules;
 	memset(&rules, 0, sizeof(rules));
-	rules.rules = getenv("XKB_DEFAULT_RULES");
-	rules.model = getenv("XKB_DEFAULT_MODEL");
-	rules.layout = getenv("XKB_DEFAULT_LAYOUT");
-	rules.variant = getenv("XKB_DEFAULT_VARIANT");
-	rules.options = getenv("XKB_DEFAULT_OPTIONS");
+	struct input_config *input_config =
+		keyboard->seat_device->input_device->config;
+
+	if (input_config && input_config->xkb_layout) {
+		rules.layout = input_config->xkb_layout;
+	} else {
+		rules.layout = getenv("XKB_DEFAULT_LAYOUT");
+	}
+	if (input_config && input_config->xkb_model) {
+		rules.model = input_config->xkb_model;
+	} else {
+		rules.model = getenv("XKB_DEFAULT_MODEL");
+	}
+
+	if (input_config && input_config->xkb_options) {
+		rules.options = input_config->xkb_options;
+	} else {
+		rules.options = getenv("XKB_DEFAULT_OPTIONS");
+	}
+
+	if (input_config && input_config->xkb_rules) {
+		rules.rules = input_config->xkb_rules;
+	} else {
+		rules.rules = getenv("XKB_DEFAULT_RULES");
+	}
+
+	if (input_config && input_config->xkb_variant) {
+		rules.variant = input_config->xkb_variant;
+	} else {
+		rules.variant = getenv("XKB_DEFAULT_VARIANT");
+	}
 
 	struct xkb_context *context = xkb_context_new(XKB_CONTEXT_NO_FLAGS);
 	if (!sway_assert(context, "cannot create XKB context")) {
 		return;
 	}
 
+	xkb_keymap_unref(keyboard->keymap);
 	keyboard->keymap =
 		xkb_keymap_new_from_names(context, &rules, XKB_KEYMAP_COMPILE_NO_FLAGS);
 	wlr_keyboard_set_keymap(keyboard->seat_device->input_device->wlr_device->keyboard, keyboard->keymap);
diff --git a/sway/main.c b/sway/main.c
index 363f4d96..25032aa0 100644
--- a/sway/main.c
+++ b/sway/main.c
@@ -158,6 +158,7 @@ static void log_distro() {
 }
 
 static void log_kernel() {
+	return;
 	FILE *f = popen("uname -a", "r");
 	if (!f) {
 		sway_log(L_INFO, "Unable to determine kernel version");
diff --git a/sway/meson.build b/sway/meson.build
index ad8160eb..c81e1c2c 100644
--- a/sway/meson.build
+++ b/sway/meson.build
@@ -24,6 +24,11 @@ sway_sources = files(
 	'commands/input/pointer_accel.c',
 	'commands/input/scroll_method.c',
 	'commands/input/tap.c',
+	'commands/input/xkb_layout.c',
+	'commands/input/xkb_model.c',
+	'commands/input/xkb_options.c',
+	'commands/input/xkb_rules.c',
+	'commands/input/xkb_variant.c',
 	'config.c',
 	'ipc-json.c',
 	'ipc-server.c',

From 030fcb64da90242718b7276a4c98cd0b2a346aad Mon Sep 17 00:00:00 2001
From: Tony Crisci <tony@dubstepdish.com>
Date: Fri, 15 Dec 2017 05:57:28 -0500
Subject: [PATCH 22/49] keyboard cleanup

---
 sway/input/keyboard.c | 18 ++++++++----------
 1 file changed, 8 insertions(+), 10 deletions(-)

diff --git a/sway/input/keyboard.c b/sway/input/keyboard.c
index 2ab0206a..b9b571a6 100644
--- a/sway/input/keyboard.c
+++ b/sway/input/keyboard.c
@@ -10,10 +10,9 @@ static void handle_keyboard_key(struct wl_listener *listener, void *data) {
 	struct wlr_input_device *wlr_device =
 		keyboard->seat_device->input_device->wlr_device;
 	struct wlr_event_keyboard_key *event = data;
-	wlr_keyboard_set_keymap(wlr_device->keyboard, keyboard->keymap);
 	wlr_seat_set_keyboard(wlr_seat, wlr_device);
-	wlr_seat_keyboard_notify_key(wlr_seat, event->time_msec,event->keycode,
-		event->state);
+	wlr_seat_keyboard_notify_key(wlr_seat, event->time_msec,
+		event->keycode, event->state);
 }
 
 static void handle_keyboard_modifiers(struct wl_listener *listener,
@@ -23,7 +22,6 @@ static void handle_keyboard_modifiers(struct wl_listener *listener,
 	struct wlr_seat *wlr_seat = keyboard->seat_device->sway_seat->wlr_seat;
 	struct wlr_input_device *wlr_device =
 		keyboard->seat_device->input_device->wlr_device;
-	wlr_keyboard_set_keymap(wlr_device->keyboard, keyboard->keymap);
 	wlr_seat_set_keyboard(wlr_seat, wlr_device);
 	wlr_seat_keyboard_notify_modifiers(wlr_seat);
 }
@@ -50,6 +48,8 @@ void sway_keyboard_configure(struct sway_keyboard *keyboard) {
 	memset(&rules, 0, sizeof(rules));
 	struct input_config *input_config =
 		keyboard->seat_device->input_device->config;
+	struct wlr_input_device *wlr_device =
+		keyboard->seat_device->input_device->wlr_device;
 
 	if (input_config && input_config->xkb_layout) {
 		rules.layout = input_config->xkb_layout;
@@ -88,18 +88,16 @@ void sway_keyboard_configure(struct sway_keyboard *keyboard) {
 	xkb_keymap_unref(keyboard->keymap);
 	keyboard->keymap =
 		xkb_keymap_new_from_names(context, &rules, XKB_KEYMAP_COMPILE_NO_FLAGS);
-	wlr_keyboard_set_keymap(keyboard->seat_device->input_device->wlr_device->keyboard, keyboard->keymap);
+	wlr_keyboard_set_keymap(wlr_device->keyboard, keyboard->keymap);
+	wlr_keyboard_set_repeat_info(wlr_device->keyboard, 25, 600);
 	xkb_context_unref(context);
 
 	wl_list_remove(&keyboard->keyboard_key.link);
-	wl_signal_add(
-		&keyboard->seat_device->input_device->wlr_device->keyboard->events.key,
-		&keyboard->keyboard_key);
+	wl_signal_add(&wlr_device->keyboard->events.key, &keyboard->keyboard_key);
 	keyboard->keyboard_key.notify = handle_keyboard_key;
 
 	wl_list_remove(&keyboard->keyboard_modifiers.link);
-	wl_signal_add(
-		&keyboard->seat_device->input_device->wlr_device->keyboard->events.modifiers,
+	wl_signal_add( &wlr_device->keyboard->events.modifiers,
 		&keyboard->keyboard_modifiers);
 	keyboard->keyboard_modifiers.notify = handle_keyboard_modifiers;
 }

From b3a0309f2612523450b2b960c81d1c4895880454 Mon Sep 17 00:00:00 2001
From: Tony Crisci <tony@dubstepdish.com>
Date: Sat, 16 Dec 2017 07:39:22 -0500
Subject: [PATCH 23/49] fix logging issue for new wlroots

---
 sway/input/seat.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/sway/input/seat.c b/sway/input/seat.c
index 1b25419b..907c610a 100644
--- a/sway/input/seat.c
+++ b/sway/input/seat.c
@@ -168,7 +168,7 @@ void sway_seat_configure_xcursor(struct sway_seat *seat) {
 				output->scale);
 
 		sway_assert(!result,
-			"Cannot load xcursor theme for output '%s' with scale %d",
+			"Cannot load xcursor theme for output '%s' with scale %f",
 			output->name, output->scale);
 	}
 

From 2624f55f8decf7f1cc1dd64a3f72b130f7ef77e8 Mon Sep 17 00:00:00 2001
From: Tony Crisci <tony@dubstepdish.com>
Date: Sat, 16 Dec 2017 08:19:33 -0500
Subject: [PATCH 24/49] input and seat command docs

---
 sway/sway-input.5.txt | 50 +++++++++++++++++++++++++++++++++++++++----
 sway/sway.1.txt       | 23 +-------------------
 sway/sway.5.txt       |  7 +++++-
 3 files changed, 53 insertions(+), 27 deletions(-)

diff --git a/sway/sway-input.5.txt b/sway/sway-input.5.txt
index f0c8f87c..d4652a82 100644
--- a/sway/sway-input.5.txt
+++ b/sway/sway-input.5.txt
@@ -1,5 +1,5 @@
 /////
-vim:set ts=4 sw=4 tw=82 noet:
+vim:set ft=asciidoc ts=4 sw=4 tw=82 noet:
 /////
 sway-input (5)
 ==============
@@ -11,12 +11,37 @@ sway-input - input configuration file and commands
 Description
 -----------
 
-Sway allows for configuration of libinput devices within the sway configuration file.
+Sway allows for configuration of devices within the sway configuration file.
 sway-input commands must be used inside an _input { }_ block in the config.
 To obtain a list of available device identifiers, run **swaymsg -t get_inputs**.
 
-Commands
---------
+Input Commands
+--------------
+
+Keyboard Configuration
+~~~~~~~~~~~~~~~~~~~~~~
+
+For more information on these xkb configuration options, see
+**xkeyboard-config**(7).
+
+**input** <identifier> xkb_layout <layout_name>::
+	Sets the layout of the keyboard like _us_ or _de_.
+
+**input** <identifier> xkb_model <model_name>::
+	Sets the model of the keyboard. This has an influence for some extra keys your
+	keyboard might have.
+
+**input** <identifier> xkb_options <options>::
+	Sets extra xkb configuration options for the keyboard.
+
+**input** <identifier> xkb_rules <rules>::
+	Sets files of rules to be used for keyboard mapping composition.
+
+**input** <identifier> xkb_variant <variant>::
+	Sets the variant of the keyboard like _dvorak_ or _colemak_.
+
+Libinput Configuration
+~~~~~~~~~~~~~~~~~~~~~~
 
 **input** <identifier> accel_profile <adaptive|flat>::
 	Sets the pointer acceleration profile for the specified input device.
@@ -53,6 +78,23 @@ Commands
 **input** <identifier> tap <enabled|disabled>::
 	Enables or disables tap for specified input device.
 
+Seat Configuration
+------------------
+
+Configure options for multiseat mode. sway-seat commands must be used inside a
+_seat { }_ block in the config.
+
+A _seat_ is a collection of input devices that act independently of each other.
+Seats are identified by name and the default seat is _seat0_ if no seats are
+configured. Each seat has an independent keyboard focus and a separate cursor that
+is controlled by the pointer devices of the seat. This is useful for multiple
+people using the desktop at the same time with their own devices (each sitting in
+their own "seat").
+
+**seat** <name> attach <input_identifier>::
+	Attach an input device to this seat by its input identifier. A special value
+	of _*_ will attach all devices to the seat.
+
 See Also
 --------
 
diff --git a/sway/sway.1.txt b/sway/sway.1.txt
index 14ab9f49..17fc13da 100644
--- a/sway/sway.1.txt
+++ b/sway/sway.1.txt
@@ -1,5 +1,5 @@
 /////
-vim:set ts=4 sw=4 tw=82 noet:
+vim:set ft=asciidoc ts=4 sw=4 tw=82 noet:
 /////
 :quotes.~:
 
@@ -93,27 +93,6 @@ The following environment variables have an effect on sway:
 *SWAYSOCK*::
 	Specifies the path to the sway IPC socket.
 
-*WLC_DRM_DEVICE*::
-	Specifies the device to use in DRM mode.
-
-*WLC_SHM*::
-	Set 1 to force EGL clients to use shared memory.
-
-*WLC_OUTPUTS*::
-	Number of fake outputs to use when running in X11 mode.
-
-*WLC_XWAYLAND*::
-	Set to 0 to disable Xwayland support.
-
-*WLC_LIBINPUT*::
-	Set to 1 to force libinput (even in X11 mode).
-
-*WLC_REPEAT_DELAY*::
-	Configures the keyboard repeat delay.
-
-*WLC_REPEAT_RATE*::
-	Configures the keyboard repeat rate.
-
 *XKB_DEFAULT_RULES*, *XKB_DEFAULT_MODEL*, *XKB_DEFAULT_LAYOUT*, *XKB_DEFAULT_VARIANT*, *XKB_DEFAULT_OPTIONS*::
 	Configures the xkb keyboard settings. See xkeyboard-config(7).
 
diff --git a/sway/sway.5.txt b/sway/sway.5.txt
index 2a4ef205..afd3c9fa 100644
--- a/sway/sway.5.txt
+++ b/sway/sway.5.txt
@@ -312,7 +312,7 @@ The default colors are:
 **hide_edge_borders** <none|vertical|horizontal|both|smart>::
 	Hide window borders adjacent to the screen edges. Default is _none_.
 
-**input** <input device> <block of commands>::
+**input** <input_device> <block of commands>::
 	Append _{_ to this command, the following lines will be commands to configure
 	the named input device, and _}_ on its own line will close the block.
 	+
@@ -320,6 +320,11 @@ The default colors are:
 	+
 	See **sway-input**(5) for details.
 
+**seat** <seat_name> <block of commands>::
+	Append _{_ to this command, the following lines will be commands to configure
+	the named seat, and _}_ on its own line will close the block.
+	See **sway-input**(5) for details.
+
 **kill**::
 	Kills (force-closes) the currently-focused container and all of its children.
 

From d3d36047605e57e81d7452173a913a0c04cbcfc1 Mon Sep 17 00:00:00 2001
From: Tony Crisci <tony@dubstepdish.com>
Date: Sat, 16 Dec 2017 08:33:22 -0500
Subject: [PATCH 25/49] fix header includes

---
 include/sway/input/cursor.h        | 4 ++--
 include/sway/input/input-manager.h | 4 ++--
 include/sway/input/keyboard.h      | 5 +++++
 include/sway/input/seat.h          | 4 ++--
 4 files changed, 11 insertions(+), 6 deletions(-)

diff --git a/include/sway/input/cursor.h b/include/sway/input/cursor.h
index cc529de6..2f70cf4b 100644
--- a/include/sway/input/cursor.h
+++ b/include/sway/input/cursor.h
@@ -1,5 +1,5 @@
-#ifndef _SWAY_CURSOR_H
-#define _SWAY_CURSOR_H
+#ifndef _SWAY_INPUT_CURSOR_H
+#define _SWAY_INPUT_CURSOR_H
 
 #include "sway/input/seat.h"
 
diff --git a/include/sway/input/input-manager.h b/include/sway/input/input-manager.h
index cdcffab6..53064eed 100644
--- a/include/sway/input/input-manager.h
+++ b/include/sway/input/input-manager.h
@@ -1,5 +1,5 @@
-#ifndef _SWAY_INPUT_MANAGER_H
-#define _SWAY_INPUT_MANAGER_H
+#ifndef _SWAY_INPUT_INPUT_MANAGER_H
+#define _SWAY_INPUT_INPUT_MANAGER_H
 #include <libinput.h>
 #include "sway/server.h"
 #include "sway/config.h"
diff --git a/include/sway/input/keyboard.h b/include/sway/input/keyboard.h
index 89cde3fa..0b0c1549 100644
--- a/include/sway/input/keyboard.h
+++ b/include/sway/input/keyboard.h
@@ -1,3 +1,6 @@
+#ifndef _SWAY_INPUT_KEYBOARD_H
+#define _SWAY_INPUT_KEYBOARD_H
+
 #include "sway/input/seat.h"
 
 struct sway_keyboard {
@@ -16,3 +19,5 @@ struct sway_keyboard *sway_keyboard_create(struct sway_seat *seat,
 void sway_keyboard_configure(struct sway_keyboard *keyboard);
 
 void sway_keyboard_destroy(struct sway_keyboard *keyboard);
+
+#endif
diff --git a/include/sway/input/seat.h b/include/sway/input/seat.h
index db69f83e..d703f94c 100644
--- a/include/sway/input/seat.h
+++ b/include/sway/input/seat.h
@@ -1,5 +1,5 @@
-#ifndef _SWAY_SEAT_H
-#define _SWAY_SEAT_H
+#ifndef _SWAY_INPUT_SEAT_H
+#define _SWAY_INPUT_SEAT_H
 
 #include <wlr/types/wlr_seat.h>
 #include "sway/input/input-manager.h"

From f4a5a0ead4c8b155985c242db1fa5de5fa4807a0 Mon Sep 17 00:00:00 2001
From: Tony Crisci <tony@dubstepdish.com>
Date: Sat, 16 Dec 2017 11:25:59 -0500
Subject: [PATCH 26/49] put seat and input config in their own files

---
 sway/config.c       | 222 --------------------------------------------
 sway/config/input.c | 105 +++++++++++++++++++++
 sway/config/seat.c  | 127 +++++++++++++++++++++++++
 sway/meson.build    |   2 +
 4 files changed, 234 insertions(+), 222 deletions(-)
 create mode 100644 sway/config/input.c
 create mode 100644 sway/config/seat.c

diff --git a/sway/config.c b/sway/config.c
index 4e34aa8c..b591ae9e 100644
--- a/sway/config.c
+++ b/sway/config.c
@@ -12,7 +12,6 @@
 #include <signal.h>
 #include <libinput.h>
 #include <limits.h>
-#include <float.h>
 #include <dirent.h>
 #include <strings.h>
 #ifdef __linux__
@@ -229,227 +228,6 @@ static int qstrcmp(const void* a, const void* b) {
 	return strcmp(*((char**) a), *((char**) b));
 }
 
-struct input_config *new_input_config(const char* identifier) {
-	struct input_config *input = calloc(1, sizeof(struct input_config));
-	if (!input) {
-		sway_log(L_DEBUG, "Unable to allocate input config");
-		return NULL;
-	}
-	sway_log(L_DEBUG, "new_input_config(%s)", identifier);
-	if (!(input->identifier = strdup(identifier))) {
-		free(input);
-		sway_log(L_DEBUG, "Unable to allocate input config");
-		return NULL;
-	}
-
-	input->tap = INT_MIN;
-	input->drag_lock = INT_MIN;
-	input->dwt = INT_MIN;
-	input->send_events = INT_MIN;
-	input->click_method = INT_MIN;
-	input->middle_emulation = INT_MIN;
-	input->natural_scroll = INT_MIN;
-	input->accel_profile = INT_MIN;
-	input->pointer_accel = FLT_MIN;
-	input->scroll_method = INT_MIN;
-	input->left_handed = INT_MIN;
-
-	return input;
-}
-
-void merge_input_config(struct input_config *dst, struct input_config *src) {
-	if (src->identifier) {
-		free(dst->identifier);
-		dst->identifier = strdup(src->identifier);
-	}
-	if (src->accel_profile != INT_MIN) {
-		dst->accel_profile = src->accel_profile;
-	}
-	if (src->click_method != INT_MIN) {
-		dst->click_method = src->click_method;
-	}
-	if (src->drag_lock != INT_MIN) {
-		dst->drag_lock = src->drag_lock;
-	}
-	if (src->dwt != INT_MIN) {
-		dst->dwt = src->dwt;
-	}
-	if (src->middle_emulation != INT_MIN) {
-		dst->middle_emulation = src->middle_emulation;
-	}
-	if (src->natural_scroll != INT_MIN) {
-		dst->natural_scroll = src->natural_scroll;
-	}
-	if (src->pointer_accel != FLT_MIN) {
-		dst->pointer_accel = src->pointer_accel;
-	}
-	if (src->scroll_method != INT_MIN) {
-		dst->scroll_method = src->scroll_method;
-	}
-	if (src->send_events != INT_MIN) {
-		dst->send_events = src->send_events;
-	}
-	if (src->tap != INT_MIN) {
-		dst->tap = src->tap;
-	}
-	if (src->xkb_layout) {
-		free(dst->xkb_layout);
-		dst->xkb_layout = strdup(src->xkb_layout);
-	}
-	if (src->xkb_model) {
-		free(dst->xkb_model);
-		dst->xkb_model = strdup(src->xkb_model);
-	}
-	if (src->xkb_options) {
-		free(dst->xkb_options);
-		dst->xkb_options = strdup(src->xkb_options);
-	}
-	if (src->xkb_rules) {
-		free(dst->xkb_rules);
-		dst->xkb_rules = strdup(src->xkb_rules);
-	}
-	if (src->xkb_variant) {
-		free(dst->xkb_variant);
-		dst->xkb_variant = strdup(src->xkb_variant);
-	}
-}
-
-void free_input_config(struct input_config *ic) {
-	if (!ic) {
-		return;
-	}
-	free(ic->identifier);
-	free(ic);
-}
-
-int input_identifier_cmp(const void *item, const void *data) {
-	const struct input_config *ic = item;
-	const char *identifier = data;
-	return strcmp(ic->identifier, identifier);
-}
-
-struct seat_config *new_seat_config(const char* name) {
-	struct seat_config *seat = calloc(1, sizeof(struct seat_config));
-	if (!seat) {
-		sway_log(L_DEBUG, "Unable to allocate seat config");
-		return NULL;
-	}
-
-	sway_log(L_DEBUG, "new_seat_config(%s)", name);
-	seat->name = strdup(name);
-	if (!sway_assert(seat->name, "could not allocate name for seat")) {
-		return NULL;
-	}
-
-	seat->attachments = create_list();
-	if (!sway_assert(seat->attachments,
-				"could not allocate seat attachments list")) {
-		return NULL;
-	}
-
-	return seat;
-}
-
-struct seat_attachment_config *seat_attachment_config_new() {
-	struct seat_attachment_config *attachment =
-		calloc(1, sizeof(struct seat_attachment_config));
-	if (!attachment) {
-		sway_log(L_DEBUG, "cannot allocate attachment config");
-		return NULL;
-	}
-	return attachment;
-}
-
-static void seat_attachment_config_free(
-		struct seat_attachment_config *attachment) {
-	free(attachment->identifier);
-	free(attachment);
-	return;
-}
-
-static struct seat_attachment_config *seat_attachment_config_copy(
-		struct seat_attachment_config *attachment) {
-	struct seat_attachment_config *copy = seat_attachment_config_new();
-	if (!copy) {
-		return NULL;
-	}
-
-	copy->identifier = strdup(attachment->identifier);
-
-	return copy;
-}
-
-static void merge_seat_attachment_config(struct seat_attachment_config *dest,
-		struct seat_attachment_config *source) {
-	// nothing to merge yet, but there will be some day
-}
-
-void merge_seat_config(struct seat_config *dest, struct seat_config *source) {
-	if (source->name) {
-		free(dest->name);
-		dest->name = strdup(source->name);
-	}
-
-	for (int i = 0; i < source->attachments->length; ++i) {
-		struct seat_attachment_config *source_attachment =
-			source->attachments->items[i];
-		bool found = false;
-		for (int j = 0; j < dest->attachments->length; ++j) {
-			struct seat_attachment_config *dest_attachment =
-				dest->attachments->items[j];
-			if (strcmp(source_attachment->identifier,
-						dest_attachment->identifier) == 0) {
-				merge_seat_attachment_config(dest_attachment,
-					source_attachment);
-				found = true;
-			}
-		}
-
-		if (!found) {
-			struct seat_attachment_config *copy =
-				seat_attachment_config_copy(source_attachment);
-			if (copy) {
-				list_add(dest->attachments, copy);
-			}
-		}
-	}
-}
-
-void free_seat_config(struct seat_config *seat) {
-	if (!seat) {
-		return;
-	}
-
-	free(seat->name);
-	for (int i = 0; i < seat->attachments->length; ++i) {
-		struct seat_attachment_config *attachment =
-			seat->attachments->items[i];
-		seat_attachment_config_free(attachment);
-	}
-
-	list_free(seat->attachments);
-	free(seat);
-}
-
-int seat_name_cmp(const void *item, const void *data) {
-	const struct seat_config *sc = item;
-	const char *name = data;
-	return strcmp(sc->name, name);
-}
-
-struct seat_attachment_config *seat_config_get_attachment(
-		struct seat_config *seat_config, char *identifier) {
-	for (int i = 0; i < seat_config->attachments->length; ++i) {
-		struct seat_attachment_config *attachment =
-			seat_config->attachments->items[i];
-		if (strcmp(attachment->identifier, identifier) == 0) {
-			return attachment;
-		}
-	}
-
-	return NULL;
-}
-
 bool load_main_config(const char *file, bool is_active) {
 	char *path;
 	if (file != NULL) {
diff --git a/sway/config/input.c b/sway/config/input.c
new file mode 100644
index 00000000..6f8d31f7
--- /dev/null
+++ b/sway/config/input.c
@@ -0,0 +1,105 @@
+#define _XOPEN_SOURCE 700
+#include <stdlib.h>
+#include <limits.h>
+#include <float.h>
+#include "sway/config.h"
+#include "log.h"
+
+struct input_config *new_input_config(const char* identifier) {
+	struct input_config *input = calloc(1, sizeof(struct input_config));
+	if (!input) {
+		sway_log(L_DEBUG, "Unable to allocate input config");
+		return NULL;
+	}
+	sway_log(L_DEBUG, "new_input_config(%s)", identifier);
+	if (!(input->identifier = strdup(identifier))) {
+		free(input);
+		sway_log(L_DEBUG, "Unable to allocate input config");
+		return NULL;
+	}
+
+	input->tap = INT_MIN;
+	input->drag_lock = INT_MIN;
+	input->dwt = INT_MIN;
+	input->send_events = INT_MIN;
+	input->click_method = INT_MIN;
+	input->middle_emulation = INT_MIN;
+	input->natural_scroll = INT_MIN;
+	input->accel_profile = INT_MIN;
+	input->pointer_accel = FLT_MIN;
+	input->scroll_method = INT_MIN;
+	input->left_handed = INT_MIN;
+
+	return input;
+}
+
+void merge_input_config(struct input_config *dst, struct input_config *src) {
+	if (src->identifier) {
+		free(dst->identifier);
+		dst->identifier = strdup(src->identifier);
+	}
+	if (src->accel_profile != INT_MIN) {
+		dst->accel_profile = src->accel_profile;
+	}
+	if (src->click_method != INT_MIN) {
+		dst->click_method = src->click_method;
+	}
+	if (src->drag_lock != INT_MIN) {
+		dst->drag_lock = src->drag_lock;
+	}
+	if (src->dwt != INT_MIN) {
+		dst->dwt = src->dwt;
+	}
+	if (src->middle_emulation != INT_MIN) {
+		dst->middle_emulation = src->middle_emulation;
+	}
+	if (src->natural_scroll != INT_MIN) {
+		dst->natural_scroll = src->natural_scroll;
+	}
+	if (src->pointer_accel != FLT_MIN) {
+		dst->pointer_accel = src->pointer_accel;
+	}
+	if (src->scroll_method != INT_MIN) {
+		dst->scroll_method = src->scroll_method;
+	}
+	if (src->send_events != INT_MIN) {
+		dst->send_events = src->send_events;
+	}
+	if (src->tap != INT_MIN) {
+		dst->tap = src->tap;
+	}
+	if (src->xkb_layout) {
+		free(dst->xkb_layout);
+		dst->xkb_layout = strdup(src->xkb_layout);
+	}
+	if (src->xkb_model) {
+		free(dst->xkb_model);
+		dst->xkb_model = strdup(src->xkb_model);
+	}
+	if (src->xkb_options) {
+		free(dst->xkb_options);
+		dst->xkb_options = strdup(src->xkb_options);
+	}
+	if (src->xkb_rules) {
+		free(dst->xkb_rules);
+		dst->xkb_rules = strdup(src->xkb_rules);
+	}
+	if (src->xkb_variant) {
+		free(dst->xkb_variant);
+		dst->xkb_variant = strdup(src->xkb_variant);
+	}
+}
+
+void free_input_config(struct input_config *ic) {
+	if (!ic) {
+		return;
+	}
+	free(ic->identifier);
+	free(ic);
+}
+
+int input_identifier_cmp(const void *item, const void *data) {
+	const struct input_config *ic = item;
+	const char *identifier = data;
+	return strcmp(ic->identifier, identifier);
+}
diff --git a/sway/config/seat.c b/sway/config/seat.c
new file mode 100644
index 00000000..3a2fdaa6
--- /dev/null
+++ b/sway/config/seat.c
@@ -0,0 +1,127 @@
+#define _XOPEN_SOURCE 700
+#include <stdlib.h>
+#include <string.h>
+#include "sway/config.h"
+#include "log.h"
+
+struct seat_config *new_seat_config(const char* name) {
+	struct seat_config *seat = calloc(1, sizeof(struct seat_config));
+	if (!seat) {
+		sway_log(L_DEBUG, "Unable to allocate seat config");
+		return NULL;
+	}
+
+	sway_log(L_DEBUG, "new_seat_config(%s)", name);
+	seat->name = strdup(name);
+	if (!sway_assert(seat->name, "could not allocate name for seat")) {
+		return NULL;
+	}
+
+	seat->attachments = create_list();
+	if (!sway_assert(seat->attachments,
+				"could not allocate seat attachments list")) {
+		return NULL;
+	}
+
+	return seat;
+}
+
+struct seat_attachment_config *seat_attachment_config_new() {
+	struct seat_attachment_config *attachment =
+		calloc(1, sizeof(struct seat_attachment_config));
+	if (!attachment) {
+		sway_log(L_DEBUG, "cannot allocate attachment config");
+		return NULL;
+	}
+	return attachment;
+}
+
+static void seat_attachment_config_free(
+		struct seat_attachment_config *attachment) {
+	free(attachment->identifier);
+	free(attachment);
+	return;
+}
+
+static struct seat_attachment_config *seat_attachment_config_copy(
+		struct seat_attachment_config *attachment) {
+	struct seat_attachment_config *copy = seat_attachment_config_new();
+	if (!copy) {
+		return NULL;
+	}
+
+	copy->identifier = strdup(attachment->identifier);
+
+	return copy;
+}
+
+static void merge_seat_attachment_config(struct seat_attachment_config *dest,
+		struct seat_attachment_config *source) {
+	// nothing to merge yet, but there will be some day
+}
+
+void merge_seat_config(struct seat_config *dest, struct seat_config *source) {
+	if (source->name) {
+		free(dest->name);
+		dest->name = strdup(source->name);
+	}
+
+	for (int i = 0; i < source->attachments->length; ++i) {
+		struct seat_attachment_config *source_attachment =
+			source->attachments->items[i];
+		bool found = false;
+		for (int j = 0; j < dest->attachments->length; ++j) {
+			struct seat_attachment_config *dest_attachment =
+				dest->attachments->items[j];
+			if (strcmp(source_attachment->identifier,
+						dest_attachment->identifier) == 0) {
+				merge_seat_attachment_config(dest_attachment,
+					source_attachment);
+				found = true;
+			}
+		}
+
+		if (!found) {
+			struct seat_attachment_config *copy =
+				seat_attachment_config_copy(source_attachment);
+			if (copy) {
+				list_add(dest->attachments, copy);
+			}
+		}
+	}
+}
+
+void free_seat_config(struct seat_config *seat) {
+	if (!seat) {
+		return;
+	}
+
+	free(seat->name);
+	for (int i = 0; i < seat->attachments->length; ++i) {
+		struct seat_attachment_config *attachment =
+			seat->attachments->items[i];
+		seat_attachment_config_free(attachment);
+	}
+
+	list_free(seat->attachments);
+	free(seat);
+}
+
+int seat_name_cmp(const void *item, const void *data) {
+	const struct seat_config *sc = item;
+	const char *name = data;
+	return strcmp(sc->name, name);
+}
+
+struct seat_attachment_config *seat_config_get_attachment(
+		struct seat_config *seat_config, char *identifier) {
+	for (int i = 0; i < seat_config->attachments->length; ++i) {
+		struct seat_attachment_config *attachment =
+			seat_config->attachments->items[i];
+		if (strcmp(attachment->identifier, identifier) == 0) {
+			return attachment;
+		}
+	}
+
+	return NULL;
+}
diff --git a/sway/meson.build b/sway/meson.build
index c1f75b13..3d38c7c9 100644
--- a/sway/meson.build
+++ b/sway/meson.build
@@ -32,6 +32,8 @@ sway_sources = files(
 	'commands/output.c',
 	'config.c',
 	'config/output.c',
+	'config/seat.c',
+	'config/input.c',
 	'ipc-json.c',
 	'ipc-server.c',
 	'desktop/output.c',

From f16aa3c0ad8328da0820e81c99a619835f36082f Mon Sep 17 00:00:00 2001
From: Tony Crisci <tony@dubstepdish.com>
Date: Sat, 16 Dec 2017 12:14:24 -0500
Subject: [PATCH 27/49] rename config apply cmds

---
 include/sway/commands.h                | 3 ---
 include/sway/config.h                  | 2 ++
 sway/commands.c                        | 4 ++--
 sway/commands/input/accel_profile.c    | 3 ++-
 sway/commands/input/click_method.c     | 3 ++-
 sway/commands/input/drag_lock.c        | 3 ++-
 sway/commands/input/dwt.c              | 3 ++-
 sway/commands/input/events.c           | 3 ++-
 sway/commands/input/left_handed.c      | 3 ++-
 sway/commands/input/middle_emulation.c | 3 ++-
 sway/commands/input/natural_scroll.c   | 3 ++-
 sway/commands/input/pointer_accel.c    | 3 ++-
 sway/commands/input/scroll_method.c    | 3 ++-
 sway/commands/input/tap.c              | 3 ++-
 sway/commands/input/xkb_layout.c       | 3 ++-
 sway/commands/input/xkb_model.c        | 3 ++-
 sway/commands/input/xkb_options.c      | 3 ++-
 sway/commands/input/xkb_rules.c        | 3 ++-
 sway/commands/input/xkb_variant.c      | 3 ++-
 sway/commands/seat/attach.c            | 2 +-
 20 files changed, 37 insertions(+), 22 deletions(-)

diff --git a/include/sway/commands.h b/include/sway/commands.h
index 61950d0d..5008831d 100644
--- a/include/sway/commands.h
+++ b/include/sway/commands.h
@@ -42,9 +42,6 @@ enum expected_args {
 	EXPECTED_EQUAL_TO
 };
 
-void input_cmd_apply(struct input_config *input);
-void seat_cmd_apply(struct seat_config *seat);
-
 struct cmd_results *checkarg(int argc, const char *name,
 		enum expected_args type, int val);
 
diff --git a/include/sway/config.h b/include/sway/config.h
index 777fb5a8..eb642dc2 100644
--- a/include/sway/config.h
+++ b/include/sway/config.h
@@ -384,6 +384,7 @@ int input_identifier_cmp(const void *item, const void *data);
 struct input_config *new_input_config(const char* identifier);
 void merge_input_config(struct input_config *dst, struct input_config *src);
 void free_input_config(struct input_config *ic);
+void apply_input_config(struct input_config *input);
 
 int seat_name_cmp(const void *item, const void *data);
 struct seat_config *new_seat_config(const char* name);
@@ -392,6 +393,7 @@ void free_seat_config(struct seat_config *ic);
 struct seat_attachment_config *seat_attachment_config_new();
 struct seat_attachment_config *seat_config_get_attachment(
 		struct seat_config *seat_config, char *identifier);
+void apply_seat_config(struct seat_config *seat);
 
 int output_name_cmp(const void *item, const void *data);
 struct output_config *new_output_config();
diff --git a/sway/commands.c b/sway/commands.c
index 7485f2f6..3fb1842d 100644
--- a/sway/commands.c
+++ b/sway/commands.c
@@ -57,7 +57,7 @@ struct cmd_results *checkarg(int argc, const char *name, enum expected_args type
 	return error;
 }
 
-void input_cmd_apply(struct input_config *input) {
+void apply_input_config(struct input_config *input) {
 	int i;
 	i = list_seq_find(config->input_configs, input_identifier_cmp, input->identifier);
 	if (i >= 0) {
@@ -74,7 +74,7 @@ void input_cmd_apply(struct input_config *input) {
 	sway_input_manager_apply_input_config(input_manager, input);
 }
 
-void seat_cmd_apply(struct seat_config *seat) {
+void apply_seat_config(struct seat_config *seat) {
 	int i;
 	i = list_seq_find(config->seat_configs, seat_name_cmp, seat->name);
 	if (i >= 0) {
diff --git a/sway/commands/input/accel_profile.c b/sway/commands/input/accel_profile.c
index 9b5fb7a2..f72b7d48 100644
--- a/sway/commands/input/accel_profile.c
+++ b/sway/commands/input/accel_profile.c
@@ -1,5 +1,6 @@
 #include <string.h>
 #include <strings.h>
+#include "sway/config.h"
 #include "sway/commands.h"
 #include "sway/input/input-manager.h"
 
@@ -24,6 +25,6 @@ struct cmd_results *input_cmd_accel_profile(int argc, char **argv) {
 				"Expected 'accel_profile <adaptive|flat>'");
 	}
 
-	input_cmd_apply(new_config);
+	apply_input_config(new_config);
 	return cmd_results_new(CMD_SUCCESS, NULL, NULL);
 }
diff --git a/sway/commands/input/click_method.c b/sway/commands/input/click_method.c
index a0e3bddf..dcf64c1a 100644
--- a/sway/commands/input/click_method.c
+++ b/sway/commands/input/click_method.c
@@ -1,6 +1,7 @@
 #include <string.h>
 #include <strings.h>
 #include "sway/commands.h"
+#include "sway/config.h"
 #include "sway/input/input-manager.h"
 #include "log.h"
 
@@ -29,6 +30,6 @@ struct cmd_results *input_cmd_click_method(int argc, char **argv) {
 			"Expected 'click_method <none|button_areas|clickfinger'");
 	}
 
-	input_cmd_apply(new_config);
+	apply_input_config(new_config);
 	return cmd_results_new(CMD_SUCCESS, NULL, NULL);
 }
diff --git a/sway/commands/input/drag_lock.c b/sway/commands/input/drag_lock.c
index 149a6183..1783978a 100644
--- a/sway/commands/input/drag_lock.c
+++ b/sway/commands/input/drag_lock.c
@@ -1,5 +1,6 @@
 #include <string.h>
 #include <strings.h>
+#include "sway/config.h"
 #include "sway/commands.h"
 #include "sway/input/input-manager.h"
 
@@ -24,6 +25,6 @@ struct cmd_results *input_cmd_drag_lock(int argc, char **argv) {
 			"Expected 'drag_lock <enabled|disabled>'");
 	}
 
-	input_cmd_apply(new_config);
+	apply_input_config(new_config);
 	return cmd_results_new(CMD_SUCCESS, NULL, NULL);
 }
diff --git a/sway/commands/input/dwt.c b/sway/commands/input/dwt.c
index 0954575c..8108a110 100644
--- a/sway/commands/input/dwt.c
+++ b/sway/commands/input/dwt.c
@@ -1,5 +1,6 @@
 #include <string.h>
 #include <strings.h>
+#include "sway/config.h"
 #include "sway/commands.h"
 #include "sway/input/input-manager.h"
 
@@ -23,6 +24,6 @@ struct cmd_results *input_cmd_dwt(int argc, char **argv) {
 			"Expected 'dwt <enabled|disabled>'");
 	}
 
-	input_cmd_apply(new_config);
+	apply_input_config(new_config);
 	return cmd_results_new(CMD_SUCCESS, NULL, NULL);
 }
diff --git a/sway/commands/input/events.c b/sway/commands/input/events.c
index f44c0ec7..8a74c11e 100644
--- a/sway/commands/input/events.c
+++ b/sway/commands/input/events.c
@@ -1,5 +1,6 @@
 #include <string.h>
 #include <strings.h>
+#include "sway/config.h"
 #include "sway/commands.h"
 #include "sway/input/input-manager.h"
 #include "log.h"
@@ -30,6 +31,6 @@ struct cmd_results *input_cmd_events(int argc, char **argv) {
 			"Expected 'events <enabled|disabled|disabled_on_external_mouse>'");
 	}
 
-	input_cmd_apply(new_config);
+	apply_input_config(new_config);
 	return cmd_results_new(CMD_SUCCESS, NULL, NULL);
 }
diff --git a/sway/commands/input/left_handed.c b/sway/commands/input/left_handed.c
index dc8fcd56..35740df3 100644
--- a/sway/commands/input/left_handed.c
+++ b/sway/commands/input/left_handed.c
@@ -1,5 +1,6 @@
 #include <string.h>
 #include <strings.h>
+#include "sway/config.h"
 #include "sway/commands.h"
 #include "sway/input/input-manager.h"
 
@@ -24,6 +25,6 @@ struct cmd_results *input_cmd_left_handed(int argc, char **argv) {
 			"Expected 'left_handed <enabled|disabled>'");
 	}
 
-	input_cmd_apply(new_config);
+	apply_input_config(new_config);
 	return cmd_results_new(CMD_SUCCESS, NULL, NULL);
 }
diff --git a/sway/commands/input/middle_emulation.c b/sway/commands/input/middle_emulation.c
index e19964d8..7bc08ae6 100644
--- a/sway/commands/input/middle_emulation.c
+++ b/sway/commands/input/middle_emulation.c
@@ -1,5 +1,6 @@
 #include <string.h>
 #include <strings.h>
+#include "sway/config.h"
 #include "sway/commands.h"
 #include "sway/input/input-manager.h"
 
@@ -25,6 +26,6 @@ struct cmd_results *input_cmd_middle_emulation(int argc, char **argv) {
 			"Expected 'middle_emulation <enabled|disabled>'");
 	}
 
-	input_cmd_apply(new_config);
+	apply_input_config(new_config);
 	return cmd_results_new(CMD_SUCCESS, NULL, NULL);
 }
diff --git a/sway/commands/input/natural_scroll.c b/sway/commands/input/natural_scroll.c
index 8272c5b3..a7dcdc2c 100644
--- a/sway/commands/input/natural_scroll.c
+++ b/sway/commands/input/natural_scroll.c
@@ -1,5 +1,6 @@
 #include <string.h>
 #include <strings.h>
+#include "sway/config.h"
 #include "sway/commands.h"
 #include "sway/input/input-manager.h"
 
@@ -24,6 +25,6 @@ struct cmd_results *input_cmd_natural_scroll(int argc, char **argv) {
 			"Expected 'natural_scroll <enabled|disabled>'");
 	}
 
-	input_cmd_apply(new_config);
+	apply_input_config(new_config);
 	return cmd_results_new(CMD_SUCCESS, NULL, NULL);
 }
diff --git a/sway/commands/input/pointer_accel.c b/sway/commands/input/pointer_accel.c
index 2c9db5bf..d2261a63 100644
--- a/sway/commands/input/pointer_accel.c
+++ b/sway/commands/input/pointer_accel.c
@@ -1,5 +1,6 @@
 #include <stdlib.h>
 #include <string.h>
+#include "sway/config.h"
 #include "sway/commands.h"
 #include "sway/input/input-manager.h"
 
@@ -22,6 +23,6 @@ struct cmd_results *input_cmd_pointer_accel(int argc, char **argv) {
 	}
 	new_config->pointer_accel = pointer_accel;
 
-	input_cmd_apply(new_config);
+	apply_input_config(new_config);
 	return cmd_results_new(CMD_SUCCESS, NULL, NULL);
 }
diff --git a/sway/commands/input/scroll_method.c b/sway/commands/input/scroll_method.c
index 40277155..035262cf 100644
--- a/sway/commands/input/scroll_method.c
+++ b/sway/commands/input/scroll_method.c
@@ -1,5 +1,6 @@
 #include <string.h>
 #include <strings.h>
+#include "sway/config.h"
 #include "sway/commands.h"
 #include "sway/input/input-manager.h"
 
@@ -28,6 +29,6 @@ struct cmd_results *input_cmd_scroll_method(int argc, char **argv) {
 			"Expected 'scroll_method <none|two_finger|edge|on_button_down>'");
 	}
 
-	input_cmd_apply(new_config);
+	apply_input_config(new_config);
 	return cmd_results_new(CMD_SUCCESS, NULL, NULL);
 }
diff --git a/sway/commands/input/tap.c b/sway/commands/input/tap.c
index 18a54087..8547c0cd 100644
--- a/sway/commands/input/tap.c
+++ b/sway/commands/input/tap.c
@@ -1,5 +1,6 @@
 #include <string.h>
 #include <strings.h>
+#include "sway/config.h"
 #include "sway/commands.h"
 #include "sway/input/input-manager.h"
 #include "log.h"
@@ -27,6 +28,6 @@ struct cmd_results *input_cmd_tap(int argc, char **argv) {
 
 	sway_log(L_DEBUG, "apply-tap for device: %s",
 		current_input_config->identifier);
-	input_cmd_apply(new_config);
+	apply_input_config(new_config);
 	return cmd_results_new(CMD_SUCCESS, NULL, NULL);
 }
diff --git a/sway/commands/input/xkb_layout.c b/sway/commands/input/xkb_layout.c
index 9a9ce044..4470fb1d 100644
--- a/sway/commands/input/xkb_layout.c
+++ b/sway/commands/input/xkb_layout.c
@@ -1,4 +1,5 @@
 #define _XOPEN_SOURCE 700
+#include "sway/config.h"
 #include "sway/commands.h"
 #include "sway/input/input-manager.h"
 #include "log.h"
@@ -19,6 +20,6 @@ struct cmd_results *input_cmd_xkb_layout(int argc, char **argv) {
 
 	sway_log(L_DEBUG, "apply-xkb_layout for device: %s",
 		current_input_config->identifier);
-	input_cmd_apply(new_config);
+	apply_input_config(new_config);
 	return cmd_results_new(CMD_SUCCESS, NULL, NULL);
 }
diff --git a/sway/commands/input/xkb_model.c b/sway/commands/input/xkb_model.c
index 14a50ffb..167ce2e7 100644
--- a/sway/commands/input/xkb_model.c
+++ b/sway/commands/input/xkb_model.c
@@ -1,4 +1,5 @@
 #define _XOPEN_SOURCE 700
+#include "sway/config.h"
 #include "sway/commands.h"
 #include "sway/input/input-manager.h"
 #include "log.h"
@@ -19,6 +20,6 @@ struct cmd_results *input_cmd_xkb_model(int argc, char **argv) {
 
 	sway_log(L_DEBUG, "apply-xkb_model for device: %s",
 		current_input_config->identifier);
-	input_cmd_apply(new_config);
+	apply_input_config(new_config);
 	return cmd_results_new(CMD_SUCCESS, NULL, NULL);
 }
diff --git a/sway/commands/input/xkb_options.c b/sway/commands/input/xkb_options.c
index 67eb5342..180f40d5 100644
--- a/sway/commands/input/xkb_options.c
+++ b/sway/commands/input/xkb_options.c
@@ -1,4 +1,5 @@
 #define _XOPEN_SOURCE 700
+#include "sway/config.h"
 #include "sway/commands.h"
 #include "sway/input/input-manager.h"
 #include "log.h"
@@ -19,6 +20,6 @@ struct cmd_results *input_cmd_xkb_options(int argc, char **argv) {
 
 	sway_log(L_DEBUG, "apply-xkb_options for device: %s",
 		current_input_config->identifier);
-	input_cmd_apply(new_config);
+	apply_input_config(new_config);
 	return cmd_results_new(CMD_SUCCESS, NULL, NULL);
 }
diff --git a/sway/commands/input/xkb_rules.c b/sway/commands/input/xkb_rules.c
index 3eda0bdd..047e8c84 100644
--- a/sway/commands/input/xkb_rules.c
+++ b/sway/commands/input/xkb_rules.c
@@ -1,4 +1,5 @@
 #define _XOPEN_SOURCE 700
+#include "sway/config.h"
 #include "sway/commands.h"
 #include "sway/input/input-manager.h"
 #include "log.h"
@@ -19,6 +20,6 @@ struct cmd_results *input_cmd_xkb_rules(int argc, char **argv) {
 
 	sway_log(L_DEBUG, "apply-xkb_rules for device: %s",
 		current_input_config->identifier);
-	input_cmd_apply(new_config);
+	apply_input_config(new_config);
 	return cmd_results_new(CMD_SUCCESS, NULL, NULL);
 }
diff --git a/sway/commands/input/xkb_variant.c b/sway/commands/input/xkb_variant.c
index c7f93ad4..0e998457 100644
--- a/sway/commands/input/xkb_variant.c
+++ b/sway/commands/input/xkb_variant.c
@@ -1,4 +1,5 @@
 #define _XOPEN_SOURCE 700
+#include "sway/config.h"
 #include "sway/commands.h"
 #include "sway/input/input-manager.h"
 #include "log.h"
@@ -19,6 +20,6 @@ struct cmd_results *input_cmd_xkb_variant(int argc, char **argv) {
 
 	sway_log(L_DEBUG, "apply-xkb_variant for device: %s",
 		current_input_config->identifier);
-	input_cmd_apply(new_config);
+	apply_input_config(new_config);
 	return cmd_results_new(CMD_SUCCESS, NULL, NULL);
 }
diff --git a/sway/commands/seat/attach.c b/sway/commands/seat/attach.c
index 996c1bda..80ec63ce 100644
--- a/sway/commands/seat/attach.c
+++ b/sway/commands/seat/attach.c
@@ -21,6 +21,6 @@ struct cmd_results *seat_cmd_attach(int argc, char **argv) {
 	new_attachment->identifier = strdup(argv[0]);
 	list_add(new_config->attachments, new_attachment);
 
-	seat_cmd_apply(new_config);
+	apply_seat_config(new_config);
 	return cmd_results_new(CMD_SUCCESS, NULL, NULL);
 }

From b8261ab24bbc8159deffadb78f5108a612d27534 Mon Sep 17 00:00:00 2001
From: Tony Crisci <tony@dubstepdish.com>
Date: Sat, 16 Dec 2017 12:40:58 -0500
Subject: [PATCH 28/49] expect exactly one xkb arg

---
 sway/commands/input/xkb_layout.c  | 2 +-
 sway/commands/input/xkb_model.c   | 2 +-
 sway/commands/input/xkb_options.c | 2 +-
 sway/commands/input/xkb_rules.c   | 2 +-
 sway/commands/input/xkb_variant.c | 2 +-
 5 files changed, 5 insertions(+), 5 deletions(-)

diff --git a/sway/commands/input/xkb_layout.c b/sway/commands/input/xkb_layout.c
index 4470fb1d..4fc33e0b 100644
--- a/sway/commands/input/xkb_layout.c
+++ b/sway/commands/input/xkb_layout.c
@@ -7,7 +7,7 @@
 struct cmd_results *input_cmd_xkb_layout(int argc, char **argv) {
 	sway_log(L_DEBUG, "xkb layout for device: %s", current_input_config->identifier);
 	struct cmd_results *error = NULL;
-	if ((error = checkarg(argc, "xkb_layout", EXPECTED_AT_LEAST, 1))) {
+	if ((error = checkarg(argc, "xkb_layout", EXPECTED_EQUAL_TO, 1))) {
 		return error;
 	}
 	if (!current_input_config) {
diff --git a/sway/commands/input/xkb_model.c b/sway/commands/input/xkb_model.c
index 167ce2e7..338bd7de 100644
--- a/sway/commands/input/xkb_model.c
+++ b/sway/commands/input/xkb_model.c
@@ -7,7 +7,7 @@
 struct cmd_results *input_cmd_xkb_model(int argc, char **argv) {
 	sway_log(L_DEBUG, "xkb model for device: %s", current_input_config->identifier);
 	struct cmd_results *error = NULL;
-	if ((error = checkarg(argc, "xkb_model", EXPECTED_AT_LEAST, 1))) {
+	if ((error = checkarg(argc, "xkb_model", EXPECTED_EQUAL_TO, 1))) {
 		return error;
 	}
 	if (!current_input_config) {
diff --git a/sway/commands/input/xkb_options.c b/sway/commands/input/xkb_options.c
index 180f40d5..b24c33c1 100644
--- a/sway/commands/input/xkb_options.c
+++ b/sway/commands/input/xkb_options.c
@@ -7,7 +7,7 @@
 struct cmd_results *input_cmd_xkb_options(int argc, char **argv) {
 	sway_log(L_DEBUG, "xkb options for device: %s", current_input_config->identifier);
 	struct cmd_results *error = NULL;
-	if ((error = checkarg(argc, "xkb_options", EXPECTED_AT_LEAST, 1))) {
+	if ((error = checkarg(argc, "xkb_options", EXPECTED_EQUAL_TO, 1))) {
 		return error;
 	}
 	if (!current_input_config) {
diff --git a/sway/commands/input/xkb_rules.c b/sway/commands/input/xkb_rules.c
index 047e8c84..def614e9 100644
--- a/sway/commands/input/xkb_rules.c
+++ b/sway/commands/input/xkb_rules.c
@@ -7,7 +7,7 @@
 struct cmd_results *input_cmd_xkb_rules(int argc, char **argv) {
 	sway_log(L_DEBUG, "xkb rules for device: %s", current_input_config->identifier);
 	struct cmd_results *error = NULL;
-	if ((error = checkarg(argc, "xkb_rules", EXPECTED_AT_LEAST, 1))) {
+	if ((error = checkarg(argc, "xkb_rules", EXPECTED_EQUAL_TO, 1))) {
 		return error;
 	}
 	if (!current_input_config) {
diff --git a/sway/commands/input/xkb_variant.c b/sway/commands/input/xkb_variant.c
index 0e998457..cc135cce 100644
--- a/sway/commands/input/xkb_variant.c
+++ b/sway/commands/input/xkb_variant.c
@@ -7,7 +7,7 @@
 struct cmd_results *input_cmd_xkb_variant(int argc, char **argv) {
 	sway_log(L_DEBUG, "xkb variant for device: %s", current_input_config->identifier);
 	struct cmd_results *error = NULL;
-	if ((error = checkarg(argc, "xkb_variant", EXPECTED_AT_LEAST, 1))) {
+	if ((error = checkarg(argc, "xkb_variant", EXPECTED_EQUAL_TO, 1))) {
 		return error;
 	}
 	if (!current_input_config) {

From 24e240642984a3b7ee2fba0828eb4375e901aafc Mon Sep 17 00:00:00 2001
From: Tony Crisci <tony@dubstepdish.com>
Date: Sat, 16 Dec 2017 13:00:31 -0500
Subject: [PATCH 29/49] strip whitespace for device id

---
 sway/input/input-manager.c | 2 ++
 1 file changed, 2 insertions(+)

diff --git a/sway/input/input-manager.c b/sway/input/input-manager.c
index 1950b6d9..e4558366 100644
--- a/sway/input/input-manager.c
+++ b/sway/input/input-manager.c
@@ -9,6 +9,7 @@
 #include "sway/input/input-manager.h"
 #include "sway/input/seat.h"
 #include "sway/server.h"
+#include "stringop.h"
 #include "list.h"
 #include "log.h"
 
@@ -36,6 +37,7 @@ static char *get_device_identifier(struct wlr_input_device *device) {
 	int vendor = device->vendor;
 	int product = device->product;
 	char *name = strdup(device->name);
+	name = strip_whitespace(name);
 
 	char *p = name;
 	for (; *p; ++p) {

From b0ed1ad28d8c87a9ca0e5eacb1e7b840ed924117 Mon Sep 17 00:00:00 2001
From: Tony Crisci <tony@dubstepdish.com>
Date: Sat, 16 Dec 2017 13:16:58 -0500
Subject: [PATCH 30/49] device add/remove logging

---
 sway/input/input-manager.c | 6 ++++++
 sway/input/seat.c          | 7 ++++++-
 2 files changed, 12 insertions(+), 1 deletion(-)

diff --git a/sway/input/input-manager.c b/sway/input/input-manager.c
index e4558366..34766ee6 100644
--- a/sway/input/input-manager.c
+++ b/sway/input/input-manager.c
@@ -120,6 +120,9 @@ static void input_add_notify(struct wl_listener *listener, void *data) {
 	input_device->identifier = get_device_identifier(device);
 	wl_list_insert(&input->devices, &input_device->link);
 
+	sway_log(L_DEBUG, "adding device: '%s'",
+		input_device->identifier);
+
 	// find config
 	for (int i = 0; i < config->input_configs->length; ++i) {
 		struct input_config *input_config = config->input_configs->items[i];
@@ -157,6 +160,9 @@ static void input_remove_notify(struct wl_listener *listener, void *data) {
 		return;
 	}
 
+	sway_log(L_DEBUG, "removing device: '%s'",
+		input_device->identifier);
+
 	struct sway_seat *seat = NULL;
 	wl_list_for_each(seat, &input->seats, link) {
 		sway_seat_remove_device(seat, input_device);
diff --git a/sway/input/seat.c b/sway/input/seat.c
index 907c610a..1e62efa1 100644
--- a/sway/input/seat.c
+++ b/sway/input/seat.c
@@ -125,6 +125,9 @@ void sway_seat_add_device(struct sway_seat *seat,
 		return;
 	}
 
+	sway_log(L_DEBUG, "adding device %s to seat %s",
+		input_device->identifier, seat->wlr_seat->name);
+
 	seat_device->sway_seat = seat;
 	seat_device->input_device = input_device;
 	wl_list_insert(&seat->devices, &seat_device->link);
@@ -134,7 +137,6 @@ void sway_seat_add_device(struct sway_seat *seat,
 
 void sway_seat_remove_device(struct sway_seat *seat,
 		struct sway_input_device *input_device) {
-	sway_log(L_DEBUG, "input remove: %s", input_device->identifier);
 	struct sway_seat_device *seat_device =
 		sway_seat_get_device(seat, input_device);
 
@@ -142,6 +144,9 @@ void sway_seat_remove_device(struct sway_seat *seat,
 		return;
 	}
 
+	sway_log(L_DEBUG, "removing device %s from seat %s",
+		input_device->identifier, seat->wlr_seat->name);
+
 	seat_device_destroy(seat_device);
 }
 

From 9e4fc7253e5e82f443ae8e1a27ced12fb13f8a67 Mon Sep 17 00:00:00 2001
From: Tony Crisci <tony@dubstepdish.com>
Date: Sat, 16 Dec 2017 14:00:23 -0500
Subject: [PATCH 31/49] get device id correctly

---
 sway/input/input-manager.c | 15 +++++++++++++--
 1 file changed, 13 insertions(+), 2 deletions(-)

diff --git a/sway/input/input-manager.c b/sway/input/input-manager.c
index 34766ee6..6a49b13b 100644
--- a/sway/input/input-manager.c
+++ b/sway/input/input-manager.c
@@ -5,6 +5,7 @@
 #include <stdio.h>
 #include <string.h>
 #include <libinput.h>
+#include <math.h>
 #include "sway/config.h"
 #include "sway/input/input-manager.h"
 #include "sway/input/seat.h"
@@ -33,6 +34,13 @@ static struct sway_seat *input_manager_get_seat(
 	return sway_seat_create(input, seat_name);
 }
 
+static inline int strlen_num(int num) {
+	if (num == 0) {
+		return 2;
+	}
+	return (int)((ceil(log10(abs(num)))+2));
+}
+
 static char *get_device_identifier(struct wlr_input_device *device) {
 	int vendor = device->vendor;
 	int product = device->product;
@@ -46,9 +54,12 @@ static char *get_device_identifier(struct wlr_input_device *device) {
 		}
 	}
 
-	sway_log(L_DEBUG, "rewritten name %s", name);
+	int len =
+		(strlen(name) +
+		 strlen_num(device->vendor) +
+		 strlen_num(device->product) +
+		 3) * sizeof(char);
 
-	int len = strlen(name) + sizeof(char) * 6;
 	char *identifier = malloc(len);
 	if (!identifier) {
 		sway_log(L_ERROR, "Unable to allocate unique input device name");

From 0256cd1473a574c2eb087f420f8356fee9e08aa7 Mon Sep 17 00:00:00 2001
From: Tony Crisci <tony@dubstepdish.com>
Date: Sat, 16 Dec 2017 19:16:00 -0500
Subject: [PATCH 32/49] fix keyboard hotplugging

---
 include/sway/input/keyboard.h | 1 -
 sway/input/keyboard.c         | 5 +++--
 2 files changed, 3 insertions(+), 3 deletions(-)

diff --git a/include/sway/input/keyboard.h b/include/sway/input/keyboard.h
index 0b0c1549..d9251f4c 100644
--- a/include/sway/input/keyboard.h
+++ b/include/sway/input/keyboard.h
@@ -5,7 +5,6 @@
 
 struct sway_keyboard {
 	struct sway_seat_device *seat_device;
-	struct wl_list link; // sway_seat::keyboards
 
 	struct xkb_keymap *keymap;
 
diff --git a/sway/input/keyboard.c b/sway/input/keyboard.c
index b9b571a6..ce0df3c5 100644
--- a/sway/input/keyboard.c
+++ b/sway/input/keyboard.c
@@ -103,9 +103,10 @@ void sway_keyboard_configure(struct sway_keyboard *keyboard) {
 }
 
 void sway_keyboard_destroy(struct sway_keyboard *keyboard) {
-	xkb_keymap_unref(keyboard->keymap);
+	if (!keyboard) {
+		return;
+	}
 	wl_list_remove(&keyboard->keyboard_key.link);
 	wl_list_remove(&keyboard->keyboard_modifiers.link);
-	wl_list_remove(&keyboard->link);
 	free(keyboard);
 }

From c41801b75e612b8bfb0e9e386a190b59141f7857 Mon Sep 17 00:00:00 2001
From: Tony Crisci <tony@dubstepdish.com>
Date: Sat, 16 Dec 2017 20:06:58 -0500
Subject: [PATCH 33/49] set keyboard config at runtime

---
 sway/commands/input.c | 10 ++++++++++
 1 file changed, 10 insertions(+)

diff --git a/sway/commands/input.c b/sway/commands/input.c
index 5ca9c2e6..dac028a7 100644
--- a/sway/commands/input.c
+++ b/sway/commands/input.c
@@ -44,6 +44,16 @@ struct cmd_results *cmd_input(int argc, char **argv) {
 			res = input_cmd_scroll_method(argc_new, argv_new);
 		} else if (strcasecmp("tap", argv[1]) == 0) {
 			res = input_cmd_tap(argc_new, argv_new);
+		} else if (strcasecmp("xkb_layout", argv[1]) == 0) {
+			res = input_cmd_xkb_layout(argc_new, argv_new);
+		} else if (strcasecmp("xkb_model", argv[1]) == 0) {
+			res = input_cmd_xkb_model(argc_new, argv_new);
+		} else if (strcasecmp("xkb_options", argv[1]) == 0) {
+			res = input_cmd_xkb_options(argc_new, argv_new);
+		} else if (strcasecmp("xkb_rules", argv[1]) == 0) {
+			res = input_cmd_xkb_rules(argc_new, argv_new);
+		} else if (strcasecmp("xkb_variant", argv[1]) == 0) {
+			res = input_cmd_xkb_variant(argc_new, argv_new);
 		} else {
 			res = cmd_results_new(CMD_INVALID, "input <device>", "Unknown command %s", argv[1]);
 		}

From e27eff8a29abd74448322ae78baa99a489e43620 Mon Sep 17 00:00:00 2001
From: Tony Crisci <tony@dubstepdish.com>
Date: Sun, 17 Dec 2017 08:30:20 -0500
Subject: [PATCH 34/49] send keyboard enter on keyboard configuration

---
 sway/input/input-manager.c | 2 ++
 sway/input/seat.c          | 9 +++++++--
 2 files changed, 9 insertions(+), 2 deletions(-)

diff --git a/sway/input/input-manager.c b/sway/input/input-manager.c
index 6a49b13b..fa8e4b49 100644
--- a/sway/input/input-manager.c
+++ b/sway/input/input-manager.c
@@ -145,6 +145,7 @@ static void input_add_notify(struct wl_listener *listener, void *data) {
 
 	struct sway_seat *seat = NULL;
 	if (!input_has_seat_configuration(input)) {
+		sway_log(L_DEBUG, "no seat configuration, using default seat");
 		seat = input_manager_get_seat(input, default_seat);
 		sway_seat_add_device(seat, input_device);
 		return;
@@ -245,6 +246,7 @@ void sway_input_manager_apply_input_config(struct sway_input_manager *input,
 
 void sway_input_manager_apply_seat_config(struct sway_input_manager *input,
 		struct seat_config *seat_config) {
+	sway_log(L_DEBUG, "applying new seat config for seat %s", seat_config->name);
 	struct sway_seat *seat = input_manager_get_seat(input, seat_config->name);
 	// the old config is invalid so clear it
 	sway_seat_set_config(seat, NULL);
diff --git a/sway/input/seat.c b/sway/input/seat.c
index 1e62efa1..8fe82b46 100644
--- a/sway/input/seat.c
+++ b/sway/input/seat.c
@@ -68,6 +68,13 @@ static void seat_configure_keyboard(struct sway_seat *seat,
 		sway_keyboard_create(seat, seat_device);
 	}
 	sway_keyboard_configure(seat_device->keyboard);
+	wlr_seat_set_keyboard(seat->wlr_seat,
+		seat_device->input_device->wlr_device);
+	if (seat->focus) {
+		// force notify reenter to pick up the new configuration
+		wlr_seat_keyboard_clear_focus(seat->wlr_seat);
+		wlr_seat_keyboard_notify_enter(seat->wlr_seat, seat->focus->sway_view->surface);
+	}
 }
 
 static struct sway_seat_device *sway_seat_get_device(struct sway_seat *seat,
@@ -101,8 +108,6 @@ void sway_seat_configure_device(struct sway_seat *seat,
 			break;
 		case WLR_INPUT_DEVICE_KEYBOARD:
 			seat_configure_keyboard(seat, seat_device);
-			wlr_seat_set_keyboard(seat->wlr_seat,
-				seat_device->input_device->wlr_device);
 			break;
 		case WLR_INPUT_DEVICE_TOUCH:
 		case WLR_INPUT_DEVICE_TABLET_PAD:

From 88bcd43ebf59cfa03a9e9a158c6f7a258c1f7db2 Mon Sep 17 00:00:00 2001
From: Tony Crisci <tony@dubstepdish.com>
Date: Sun, 17 Dec 2017 10:39:22 -0500
Subject: [PATCH 35/49] seat fallback config

---
 include/sway/commands.h       |  1 +
 include/sway/config.h         |  1 +
 sway/commands.c               |  1 +
 sway/commands/seat.c          |  2 +
 sway/commands/seat/fallback.c | 29 +++++++++++
 sway/config/seat.c            |  5 ++
 sway/input/input-manager.c    | 93 ++++++++++++++++++++++++-----------
 sway/input/seat.c             |  3 +-
 sway/meson.build              |  1 +
 9 files changed, 105 insertions(+), 31 deletions(-)
 create mode 100644 sway/commands/seat/fallback.c

diff --git a/include/sway/commands.h b/include/sway/commands.h
index 5008831d..4ee7af2a 100644
--- a/include/sway/commands.h
+++ b/include/sway/commands.h
@@ -198,6 +198,7 @@ sway_cmd input_cmd_xkb_options;
 sway_cmd input_cmd_xkb_rules;
 sway_cmd input_cmd_xkb_variant;
 
+sway_cmd seat_cmd_fallback;
 sway_cmd seat_cmd_attach;
 
 sway_cmd cmd_ipc_cmd;
diff --git a/include/sway/config.h b/include/sway/config.h
index eb642dc2..fdfbbedb 100644
--- a/include/sway/config.h
+++ b/include/sway/config.h
@@ -90,6 +90,7 @@ struct seat_attachment_config {
  */
 struct seat_config {
 	char *name;
+	int fallback; // -1 means not set
 	list_t *attachments; // list of seat_attachment configs
 };
 
diff --git a/sway/commands.c b/sway/commands.c
index 3fb1842d..34afb6a0 100644
--- a/sway/commands.c
+++ b/sway/commands.c
@@ -165,6 +165,7 @@ static struct cmd_handler input_handlers[] = {
 // must be in order for the bsearch
 static struct cmd_handler seat_handlers[] = {
 	{ "attach", seat_cmd_attach },
+	{ "fallback", seat_cmd_fallback },
 };
 
 static struct cmd_handler *find_handler(char *line, enum cmd_status block) {
diff --git a/sway/commands/seat.c b/sway/commands/seat.c
index 4f9e259b..0149762a 100644
--- a/sway/commands/seat.c
+++ b/sway/commands/seat.c
@@ -24,6 +24,8 @@ struct cmd_results *cmd_seat(int argc, char **argv) {
 		current_seat_config = new_seat_config(argv[0]);
 		if (strcasecmp("attach", argv[1]) == 0) {
 			res = seat_cmd_attach(argc_new, argv_new);
+		} else if (strcasecmp("fallback", argv[1]) == 0) {
+			res = seat_cmd_fallback(argc_new, argv_new);
 		} else {
 			res = cmd_results_new(CMD_INVALID, "seat <name>", "Unknown command %s", argv[1]);
 		}
diff --git a/sway/commands/seat/fallback.c b/sway/commands/seat/fallback.c
new file mode 100644
index 00000000..7c129aae
--- /dev/null
+++ b/sway/commands/seat/fallback.c
@@ -0,0 +1,29 @@
+#include <string.h>
+#include <strings.h>
+#include "sway/config.h"
+#include "sway/commands.h"
+#include "sway/input/input-manager.h"
+
+struct cmd_results *seat_cmd_fallback(int argc, char **argv) {
+	struct cmd_results *error = NULL;
+	if ((error = checkarg(argc, "fallback", EXPECTED_AT_LEAST, 1))) {
+		return error;
+	}
+	if (!current_seat_config) {
+		return cmd_results_new(CMD_FAILURE, "fallback", "No seat defined");
+	}
+	struct seat_config *new_config =
+		new_seat_config(current_seat_config->name);
+
+	if (strcasecmp(argv[0], "true") == 0) {
+		new_config->fallback = 1;
+	} else if (strcasecmp(argv[0], "false") == 0) {
+		new_config->fallback = 0;
+	} else {
+		return cmd_results_new(CMD_INVALID, "fallback",
+			"Expected 'fallback <true|false>'");
+	}
+
+	apply_seat_config(new_config);
+	return cmd_results_new(CMD_SUCCESS, NULL, NULL);
+}
diff --git a/sway/config/seat.c b/sway/config/seat.c
index 3a2fdaa6..4c9e8d0d 100644
--- a/sway/config/seat.c
+++ b/sway/config/seat.c
@@ -17,6 +17,7 @@ struct seat_config *new_seat_config(const char* name) {
 		return NULL;
 	}
 
+	seat->fallback = -1;
 	seat->attachments = create_list();
 	if (!sway_assert(seat->attachments,
 				"could not allocate seat attachments list")) {
@@ -66,6 +67,10 @@ void merge_seat_config(struct seat_config *dest, struct seat_config *source) {
 		dest->name = strdup(source->name);
 	}
 
+	if (source->fallback != -1) {
+		dest->fallback = source->fallback;
+	}
+
 	for (int i = 0; i < source->attachments->length; ++i) {
 		struct seat_attachment_config *source_attachment =
 			source->attachments->items[i];
diff --git a/sway/input/input-manager.c b/sway/input/input-manager.c
index fa8e4b49..16301489 100644
--- a/sway/input/input-manager.c
+++ b/sway/input/input-manager.c
@@ -56,9 +56,9 @@ static char *get_device_identifier(struct wlr_input_device *device) {
 
 	int len =
 		(strlen(name) +
-		 strlen_num(device->vendor) +
-		 strlen_num(device->product) +
-		 3) * sizeof(char);
+		strlen_num(device->vendor) +
+		strlen_num(device->product) +
+		3) * sizeof(char);
 
 	char *identifier = malloc(len);
 	if (!identifier) {
@@ -151,13 +151,30 @@ static void input_add_notify(struct wl_listener *listener, void *data) {
 		return;
 	}
 
+	bool added = false;
 	wl_list_for_each(seat, &input->seats, link) {
 		if (seat->config &&
 				(seat_config_get_attachment(seat->config, input_device->identifier) ||
 				seat_config_get_attachment(seat->config, "*"))) {
 			sway_seat_add_device(seat, input_device);
+			added = true;
 		}
 	}
+
+	if (!added) {
+		wl_list_for_each(seat, &input->seats, link) {
+			if (seat->config && seat->config->fallback == 1) {
+				sway_seat_add_device(seat, input_device);
+				added = true;
+			}
+		}
+	}
+
+	if (!added) {
+		sway_log(L_DEBUG,
+			"device '%s' is not configured on any seats",
+			input_device->identifier);
+	}
 }
 
 static void input_remove_notify(struct wl_listener *listener, void *data) {
@@ -248,35 +265,53 @@ void sway_input_manager_apply_seat_config(struct sway_input_manager *input,
 		struct seat_config *seat_config) {
 	sway_log(L_DEBUG, "applying new seat config for seat %s", seat_config->name);
 	struct sway_seat *seat = input_manager_get_seat(input, seat_config->name);
-	// the old config is invalid so clear it
-	sway_seat_set_config(seat, NULL);
-
-	// clear devices
-	struct sway_input_device *input_device = NULL;
-	wl_list_for_each(input_device, &input->devices, link) {
-		sway_seat_remove_device(seat, input_device);
-	}
-
-	if (seat_config_get_attachment(seat_config, "*")) {
-		wl_list_for_each(input_device, &input->devices, link) {
-			sway_seat_add_device(seat, input_device);
-		}
-	} else {
-		for (int i = 0; i < seat_config->attachments->length; ++i) {
-			struct seat_attachment_config *attachment =
-				seat_config->attachments->items[i];
-
-			struct sway_input_device *device =
-				input_sway_device_from_identifier(input,
-					attachment->identifier);
-
-			if (device) {
-				sway_seat_add_device(seat, device);
-			}
-		}
+	if (!seat) {
+		return;
 	}
 
 	sway_seat_set_config(seat, seat_config);
+
+	// for every device, try to add it to a seat and if no seat has it
+	// attached, add it to the fallback seats.
+	struct sway_input_device *input_device = NULL;
+	wl_list_for_each(input_device, &input->devices, link) {
+		list_t *seat_list = create_list();
+		struct sway_seat *seat = NULL;
+		wl_list_for_each(seat, &input->seats, link) {
+			if (!seat->config) {
+				continue;
+			}
+			if (seat_config_get_attachment(seat->config, "*") ||
+					seat_config_get_attachment(seat->config, input_device->identifier)) {
+				list_add(seat_list, seat);
+			}
+		}
+
+		if (seat_list->length) {
+			wl_list_for_each(seat, &input->seats, link) {
+				bool attached = false;
+				for (int i = 0; i < seat_list->length; ++i) {
+					if (seat == seat_list->items[i]) {
+						attached = true;
+						break;
+					}
+				}
+				if (attached) {
+					sway_seat_add_device(seat, input_device);
+				} else {
+					sway_seat_remove_device(seat, input_device);
+				}
+			}
+		} else {
+			wl_list_for_each(seat, &input->seats, link) {
+				if (seat->config && seat->config->fallback == 1) {
+					sway_seat_add_device(seat, input_device);
+				} else {
+					sway_seat_remove_device(seat, input_device);
+				}
+			}
+		}
+	}
 }
 
 void sway_input_manager_configure_xcursor(struct sway_input_manager *input) {
diff --git a/sway/input/seat.c b/sway/input/seat.c
index 8fe82b46..94503687 100644
--- a/sway/input/seat.c
+++ b/sway/input/seat.c
@@ -120,6 +120,7 @@ void sway_seat_configure_device(struct sway_seat *seat,
 void sway_seat_add_device(struct sway_seat *seat,
 		struct sway_input_device *input_device) {
 	if (sway_seat_get_device(seat, input_device)) {
+		sway_seat_configure_device(seat, input_device);
 		return;
 	}
 
@@ -246,7 +247,5 @@ void sway_seat_set_config(struct sway_seat *seat,
 		seat_device->attachment_config =
 			seat_config_get_attachment(seat_config,
 				seat_device->input_device->identifier);
-		sway_seat_configure_device(seat, seat_device->input_device);
 	}
-
 }
diff --git a/sway/meson.build b/sway/meson.build
index 3d38c7c9..fee2ddd2 100644
--- a/sway/meson.build
+++ b/sway/meson.build
@@ -13,6 +13,7 @@ sway_sources = files(
 	'commands/input.c',
 	'commands/seat.c',
 	'commands/seat/attach.c',
+	'commands/seat/fallback.c',
 	'commands/input/accel_profile.c',
 	'commands/input/click_method.c',
 	'commands/input/drag_lock.c',

From a27736adcdfe01c5232adce58201463df34da5e7 Mon Sep 17 00:00:00 2001
From: Tony Crisci <tony@dubstepdish.com>
Date: Sun, 17 Dec 2017 10:44:53 -0500
Subject: [PATCH 36/49] cleanup input-manager

---
 sway/input/input-manager.c | 21 +++++++--------------
 1 file changed, 7 insertions(+), 14 deletions(-)

diff --git a/sway/input/input-manager.c b/sway/input/input-manager.c
index 16301489..12a66917 100644
--- a/sway/input/input-manager.c
+++ b/sway/input/input-manager.c
@@ -94,17 +94,6 @@ static struct sway_input_device *input_sway_device_from_config(
 	return NULL;
 }
 
-static struct sway_input_device *input_sway_device_from_identifier(
-		struct sway_input_manager *input, char *identifier) {
-	struct sway_input_device *input_device = NULL;
-	wl_list_for_each(input_device, &input->devices, link) {
-		if (strcmp(input_device->identifier, identifier) == 0) {
-			return input_device;
-		}
-	}
-	return NULL;
-}
-
 static bool input_has_seat_configuration(struct sway_input_manager *input) {
 	struct sway_seat *seat = NULL;
 	wl_list_for_each(seat, &input->seats, link) {
@@ -154,7 +143,8 @@ static void input_add_notify(struct wl_listener *listener, void *data) {
 	bool added = false;
 	wl_list_for_each(seat, &input->seats, link) {
 		if (seat->config &&
-				(seat_config_get_attachment(seat->config, input_device->identifier) ||
+				(seat_config_get_attachment(seat->config,
+											input_device->identifier) ||
 				seat_config_get_attachment(seat->config, "*"))) {
 			sway_seat_add_device(seat, input_device);
 			added = true;
@@ -263,7 +253,8 @@ void sway_input_manager_apply_input_config(struct sway_input_manager *input,
 
 void sway_input_manager_apply_seat_config(struct sway_input_manager *input,
 		struct seat_config *seat_config) {
-	sway_log(L_DEBUG, "applying new seat config for seat %s", seat_config->name);
+	sway_log(L_DEBUG, "applying new seat config for seat %s",
+		seat_config->name);
 	struct sway_seat *seat = input_manager_get_seat(input, seat_config->name);
 	if (!seat) {
 		return;
@@ -282,7 +273,8 @@ void sway_input_manager_apply_seat_config(struct sway_input_manager *input,
 				continue;
 			}
 			if (seat_config_get_attachment(seat->config, "*") ||
-					seat_config_get_attachment(seat->config, input_device->identifier)) {
+					seat_config_get_attachment(seat->config,
+						input_device->identifier)) {
 				list_add(seat_list, seat);
 			}
 		}
@@ -311,6 +303,7 @@ void sway_input_manager_apply_seat_config(struct sway_input_manager *input,
 				}
 			}
 		}
+		list_free(seat_list);
 	}
 }
 

From b0291d126f1362cd5f9c00ff16043fd903f257a7 Mon Sep 17 00:00:00 2001
From: Drew DeVault <sir@cmpwn.com>
Date: Sun, 17 Dec 2017 21:00:17 -0500
Subject: [PATCH 37/49] Fix compile error

---
 sway/input/seat.c | 3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)

diff --git a/sway/input/seat.c b/sway/input/seat.c
index 94503687..df1427fa 100644
--- a/sway/input/seat.c
+++ b/sway/input/seat.c
@@ -180,7 +180,8 @@ void sway_seat_configure_xcursor(struct sway_seat *seat) {
 
 		sway_assert(!result,
 			"Cannot load xcursor theme for output '%s' with scale %f",
-			output->name, output->scale);
+			// TODO: Fractional scaling
+			output->name, (double)output->scale);
 	}
 
 	wlr_xcursor_manager_set_cursor_image(seat->cursor->xcursor_manager,

From 41e71d950a031ac5e63b0695e47dbfe1606e9f93 Mon Sep 17 00:00:00 2001
From: Tony Crisci <tony@dubstepdish.com>
Date: Mon, 18 Dec 2017 07:13:02 -0500
Subject: [PATCH 38/49] remove verbose commit logging

---
 sway/desktop/wl_shell.c     | 2 --
 sway/desktop/xdg_shell_v6.c | 2 --
 sway/desktop/xwayland.c     | 2 --
 3 files changed, 6 deletions(-)

diff --git a/sway/desktop/wl_shell.c b/sway/desktop/wl_shell.c
index df81d5be..e7150bf3 100644
--- a/sway/desktop/wl_shell.c
+++ b/sway/desktop/wl_shell.c
@@ -55,8 +55,6 @@ static void handle_commit(struct wl_listener *listener, void *data) {
 	struct sway_wl_shell_surface *sway_surface =
 		wl_container_of(listener, sway_surface, commit);
 	struct sway_view *view = sway_surface->view;
-	sway_log(L_DEBUG, "wl_shell surface commit %dx%d",
-			sway_surface->pending_width, sway_surface->pending_height);
 	// NOTE: We intentionally discard the view's desired width here
 	// TODO: Let floating views do whatever
 	view->width = sway_surface->pending_width;
diff --git a/sway/desktop/xdg_shell_v6.c b/sway/desktop/xdg_shell_v6.c
index 82775e2f..015cc9d0 100644
--- a/sway/desktop/xdg_shell_v6.c
+++ b/sway/desktop/xdg_shell_v6.c
@@ -61,8 +61,6 @@ static void handle_commit(struct wl_listener *listener, void *data) {
 	struct sway_xdg_surface_v6 *sway_surface =
 		wl_container_of(listener, sway_surface, commit);
 	struct sway_view *view = sway_surface->view;
-	sway_log(L_DEBUG, "xdg surface commit %dx%d",
-			sway_surface->pending_width, sway_surface->pending_height);
 	// NOTE: We intentionally discard the view's desired width here
 	// TODO: Let floating views do whatever
 	view->width = sway_surface->pending_width;
diff --git a/sway/desktop/xwayland.c b/sway/desktop/xwayland.c
index 29bed955..42e82c64 100644
--- a/sway/desktop/xwayland.c
+++ b/sway/desktop/xwayland.c
@@ -84,8 +84,6 @@ static void handle_commit(struct wl_listener *listener, void *data) {
 	struct sway_xwayland_surface *sway_surface =
 		wl_container_of(listener, sway_surface, commit);
 	struct sway_view *view = sway_surface->view;
-	sway_log(L_DEBUG, "xwayland surface commit %dx%d",
-		sway_surface->pending_width, sway_surface->pending_height);
 	// NOTE: We intentionally discard the view's desired width here
 	// TODO: Let floating views do whatever
 	view->width = sway_surface->pending_width;

From 483ede0146cd5eb5b1a68dc365f99af0c66fd156 Mon Sep 17 00:00:00 2001
From: Tony Crisci <tony@dubstepdish.com>
Date: Mon, 18 Dec 2017 10:44:25 -0500
Subject: [PATCH 39/49] improve xkb command logging

---
 sway/commands/input/xkb_layout.c  | 4 ++--
 sway/commands/input/xkb_model.c   | 4 ++--
 sway/commands/input/xkb_options.c | 4 ++--
 sway/commands/input/xkb_rules.c   | 4 ++--
 sway/commands/input/xkb_variant.c | 4 ++--
 5 files changed, 10 insertions(+), 10 deletions(-)

diff --git a/sway/commands/input/xkb_layout.c b/sway/commands/input/xkb_layout.c
index 4fc33e0b..a25d3850 100644
--- a/sway/commands/input/xkb_layout.c
+++ b/sway/commands/input/xkb_layout.c
@@ -18,8 +18,8 @@ struct cmd_results *input_cmd_xkb_layout(int argc, char **argv) {
 
 	new_config->xkb_layout = strdup(argv[0]);
 
-	sway_log(L_DEBUG, "apply-xkb_layout for device: %s",
-		current_input_config->identifier);
+	sway_log(L_DEBUG, "apply-xkb_layout for device: %s layout: %s",
+		current_input_config->identifier, new_config->xkb_layout);
 	apply_input_config(new_config);
 	return cmd_results_new(CMD_SUCCESS, NULL, NULL);
 }
diff --git a/sway/commands/input/xkb_model.c b/sway/commands/input/xkb_model.c
index 338bd7de..9729e869 100644
--- a/sway/commands/input/xkb_model.c
+++ b/sway/commands/input/xkb_model.c
@@ -18,8 +18,8 @@ struct cmd_results *input_cmd_xkb_model(int argc, char **argv) {
 
 	new_config->xkb_model = strdup(argv[0]);
 
-	sway_log(L_DEBUG, "apply-xkb_model for device: %s",
-		current_input_config->identifier);
+	sway_log(L_DEBUG, "apply-xkb_model for device: %s model: %s",
+		current_input_config->identifier, new_config->xkb_model);
 	apply_input_config(new_config);
 	return cmd_results_new(CMD_SUCCESS, NULL, NULL);
 }
diff --git a/sway/commands/input/xkb_options.c b/sway/commands/input/xkb_options.c
index b24c33c1..504849cc 100644
--- a/sway/commands/input/xkb_options.c
+++ b/sway/commands/input/xkb_options.c
@@ -18,8 +18,8 @@ struct cmd_results *input_cmd_xkb_options(int argc, char **argv) {
 
 	new_config->xkb_options = strdup(argv[0]);
 
-	sway_log(L_DEBUG, "apply-xkb_options for device: %s",
-		current_input_config->identifier);
+	sway_log(L_DEBUG, "apply-xkb_options for device: %s options: %s",
+		current_input_config->identifier, new_config->xkb_options);
 	apply_input_config(new_config);
 	return cmd_results_new(CMD_SUCCESS, NULL, NULL);
 }
diff --git a/sway/commands/input/xkb_rules.c b/sway/commands/input/xkb_rules.c
index def614e9..db7d8abe 100644
--- a/sway/commands/input/xkb_rules.c
+++ b/sway/commands/input/xkb_rules.c
@@ -18,8 +18,8 @@ struct cmd_results *input_cmd_xkb_rules(int argc, char **argv) {
 
 	new_config->xkb_rules = strdup(argv[0]);
 
-	sway_log(L_DEBUG, "apply-xkb_rules for device: %s",
-		current_input_config->identifier);
+	sway_log(L_DEBUG, "apply-xkb_rules for device: %s rules: %s",
+		current_input_config->identifier, new_config->xkb_rules);
 	apply_input_config(new_config);
 	return cmd_results_new(CMD_SUCCESS, NULL, NULL);
 }
diff --git a/sway/commands/input/xkb_variant.c b/sway/commands/input/xkb_variant.c
index cc135cce..855e6abc 100644
--- a/sway/commands/input/xkb_variant.c
+++ b/sway/commands/input/xkb_variant.c
@@ -18,8 +18,8 @@ struct cmd_results *input_cmd_xkb_variant(int argc, char **argv) {
 
 	new_config->xkb_variant = strdup(argv[0]);
 
-	sway_log(L_DEBUG, "apply-xkb_variant for device: %s",
-		current_input_config->identifier);
+	sway_log(L_DEBUG, "apply-xkb_variant for device: %s variant: %s",
+		current_input_config->identifier, new_config->xkb_variant);
 	apply_input_config(new_config);
 	return cmd_results_new(CMD_SUCCESS, NULL, NULL);
 }

From 4c436a1a6f78ce9eae40791b85c02d44458de727 Mon Sep 17 00:00:00 2001
From: Tony Crisci <tony@dubstepdish.com>
Date: Mon, 18 Dec 2017 10:44:51 -0500
Subject: [PATCH 40/49] remove assumption of one device per identifier

---
 sway/input/input-manager.c | 29 +++++++++--------------------
 sway/input/keyboard.c      |  2 ++
 2 files changed, 11 insertions(+), 20 deletions(-)

diff --git a/sway/input/input-manager.c b/sway/input/input-manager.c
index 12a66917..52da8f5e 100644
--- a/sway/input/input-manager.c
+++ b/sway/input/input-manager.c
@@ -83,17 +83,6 @@ static struct sway_input_device *input_sway_device_from_wlr(
 	return NULL;
 }
 
-static struct sway_input_device *input_sway_device_from_config(
-		struct sway_input_manager *input, struct input_config *config) {
-	struct sway_input_device *input_device = NULL;
-	wl_list_for_each(input_device, &input->devices, link) {
-		if (strcmp(input_device->identifier, config->identifier) == 0) {
-			return input_device;
-		}
-	}
-	return NULL;
-}
-
 static bool input_has_seat_configuration(struct sway_input_manager *input) {
 	struct sway_seat *seat = NULL;
 	wl_list_for_each(seat, &input->seats, link) {
@@ -238,16 +227,16 @@ void sway_input_manager_set_focus(struct sway_input_manager *input,
 
 void sway_input_manager_apply_input_config(struct sway_input_manager *input,
 		struct input_config *input_config) {
-	struct sway_input_device *input_device =
-		input_sway_device_from_config(input, input_config);
-	if (!input_device) {
-		return;
-	}
-	input_device->config = input_config;
+	struct sway_input_device *input_device = NULL;
+	wl_list_for_each(input_device, &input->devices, link) {
+		if (strcmp(input_device->identifier, input_config->identifier) == 0) {
+			input_device->config = input_config;
 
-	struct sway_seat *seat = NULL;
-	wl_list_for_each(seat, &input->seats, link) {
-		sway_seat_configure_device(seat, input_device);
+			struct sway_seat *seat = NULL;
+			wl_list_for_each(seat, &input->seats, link) {
+				sway_seat_configure_device(seat, input_device);
+			}
+		}
 	}
 }
 
diff --git a/sway/input/keyboard.c b/sway/input/keyboard.c
index ce0df3c5..bb18edd2 100644
--- a/sway/input/keyboard.c
+++ b/sway/input/keyboard.c
@@ -91,6 +91,8 @@ void sway_keyboard_configure(struct sway_keyboard *keyboard) {
 	wlr_keyboard_set_keymap(wlr_device->keyboard, keyboard->keymap);
 	wlr_keyboard_set_repeat_info(wlr_device->keyboard, 25, 600);
 	xkb_context_unref(context);
+	struct wlr_seat *seat = keyboard->seat_device->sway_seat->wlr_seat;
+	wlr_seat_set_keyboard(seat, wlr_device);
 
 	wl_list_remove(&keyboard->keyboard_key.link);
 	wl_signal_add(&wlr_device->keyboard->events.key, &keyboard->keyboard_key);

From f2985000f364693fbeb832df1c4fd468c608e40f Mon Sep 17 00:00:00 2001
From: Tony Crisci <tony@dubstepdish.com>
Date: Mon, 18 Dec 2017 14:27:38 -0500
Subject: [PATCH 41/49] ipc get_inputs

---
 include/sway/ipc-json.h |  2 ++
 sway/ipc-json.c         | 37 +++++++++++++++++++++
 sway/ipc-server.c       | 14 ++++++++
 swaymsg/main.c          | 72 +++++++++++++++++++----------------------
 4 files changed, 86 insertions(+), 39 deletions(-)

diff --git a/include/sway/ipc-json.h b/include/sway/ipc-json.h
index 9435b664..eef5a018 100644
--- a/include/sway/ipc-json.h
+++ b/include/sway/ipc-json.h
@@ -2,10 +2,12 @@
 #define _SWAY_IPC_JSON_H
 #include <json-c/json.h>
 #include "sway/container.h"
+#include "sway/input/input-manager.h"
 
 json_object *ipc_json_get_version();
 
 json_object *ipc_json_describe_container(swayc_t *c);
 json_object *ipc_json_describe_container_recursive(swayc_t *c);
+json_object *ipc_json_describe_input(struct sway_input_device *device);
 
 #endif
diff --git a/sway/ipc-json.c b/sway/ipc-json.c
index 09a32c1b..bab9a201 100644
--- a/sway/ipc-json.c
+++ b/sway/ipc-json.c
@@ -141,3 +141,40 @@ json_object *ipc_json_describe_container_recursive(swayc_t *c) {
 
 	return object;
 }
+
+static const char *describe_device_type(struct sway_input_device *device) {
+	switch (device->wlr_device->type) {
+	case WLR_INPUT_DEVICE_POINTER:
+		return "pointer";
+	case WLR_INPUT_DEVICE_KEYBOARD:
+		return "keyboard";
+	case WLR_INPUT_DEVICE_TOUCH:
+		return "touch";
+	case WLR_INPUT_DEVICE_TABLET_TOOL:
+		return "tablet_tool";
+	case WLR_INPUT_DEVICE_TABLET_PAD:
+		return "tablet_pad";
+	}
+	return "unknown";
+}
+
+json_object *ipc_json_describe_input(struct sway_input_device *device) {
+	if (!(sway_assert(device, "Device must not be null"))) {
+		return NULL;
+	}
+
+	json_object *object = json_object_new_object();
+
+	json_object_object_add(object, "identifier",
+		json_object_new_string(device->identifier));
+	json_object_object_add(object, "name",
+		json_object_new_string(device->wlr_device->name));
+	json_object_object_add(object, "vendor",
+		json_object_new_int(device->wlr_device->vendor));
+	json_object_object_add(object, "product",
+		json_object_new_int(device->wlr_device->product));
+	json_object_object_add(object, "type",
+		json_object_new_string(describe_device_type(device)));
+
+	return object;
+}
diff --git a/sway/ipc-server.c b/sway/ipc-server.c
index b7cd2d76..046e40a8 100644
--- a/sway/ipc-server.c
+++ b/sway/ipc-server.c
@@ -20,6 +20,7 @@
 #include "sway/ipc-json.h"
 #include "sway/ipc-server.h"
 #include "sway/server.h"
+#include "sway/input/input-manager.h"
 #include "list.h"
 #include "log.h"
 
@@ -359,6 +360,19 @@ void ipc_client_handle_command(struct ipc_client *client) {
 		goto exit_cleanup;
 	}
 
+	case IPC_GET_INPUTS:
+	{
+		json_object *inputs = json_object_new_array();
+		struct sway_input_device *device = NULL;
+		wl_list_for_each(device, &input_manager->devices, link) {
+			json_object_array_add(inputs, ipc_json_describe_input(device));
+		}
+		const char *json_string = json_object_to_json_string(inputs);
+		ipc_send_reply(client, json_string, (uint32_t)strlen(json_string));
+		json_object_put(inputs); // free
+		goto exit_cleanup;
+	}
+
 	case IPC_GET_TREE:
 	{
 		json_object *tree =
diff --git a/swaymsg/main.c b/swaymsg/main.c
index 2f9cfb14..18f17a59 100644
--- a/swaymsg/main.c
+++ b/swaymsg/main.c
@@ -61,55 +61,49 @@ static void pretty_print_workspace(json_object *w) {
 	);
 }
 
-static void pretty_print_input(json_object *i) {
-	json_object *id, *name, *size, *caps;
-	json_object_object_get_ex(i, "identifier", &id);
-	json_object_object_get_ex(i, "name", &name);
-	json_object_object_get_ex(i, "size", &size);
-	json_object_object_get_ex(i, "capabilities", &caps);
-
-	printf( "Input device %s\n  Type: ", json_object_get_string(name));
-
+static const char *pretty_type_name(const char *name) {
+	// TODO these constants probably belong in the common lib
 	struct {
 		const char *a;
 		const char *b;
-	} cap_names[] = {
+	} type_names[] = {
 		{ "keyboard", "Keyboard" },
 		{ "pointer", "Mouse" },
-		{ "touch", "Touch" },
-		{ "tablet_tool", "Tablet tool" },
 		{ "tablet_pad", "Tablet pad" },
-		{ "gesture", "Gesture" },
-		{ "switch", "Switch" },
+		{ "tablet_tool", "Tablet tool" },
+		{ "touch", "Touch" },
 	};
 
-	size_t len = json_object_array_length(caps);
-	if (len == 0) {
-		printf("Unknown");
+	for (size_t i = 0; i < sizeof(type_names) / sizeof(type_names[0]); ++i) {
+		if (strcmp(type_names[i].a, name) == 0) {
+			return type_names[i].b;
+		}
 	}
 
-	json_object *cap;
-	for (size_t i = 0; i < len; ++i) {
-		cap = json_object_array_get_idx(caps, i);
-		const char *cap_s = json_object_get_string(cap);
-		const char *_name = NULL;
-		for (size_t j = 0; j < sizeof(cap_names) / sizeof(cap_names[0]); ++i) {
-			if (strcmp(cap_names[i].a, cap_s) == 0) {
-				_name = cap_names[i].b;
-				break;
-			}
-		}
-		printf("%s%s", _name ? _name : cap_s, len > 1 && i != len - 1 ? ", " : "");
-	}
-	printf("\n  Sway ID: %s\n", json_object_get_string(id));
-	if (size) {
-		json_object *width, *height;
-		json_object_object_get_ex(size, "width", &width);
-		json_object_object_get_ex(size, "height", &height);
-		printf("  Size: %lfmm x %lfmm\n",
-				json_object_get_double(width), json_object_get_double(height));
-	}
-	printf("\n");
+	return name;
+}
+
+static void pretty_print_input(json_object *i) {
+	json_object *id, *name, *type, *product, *vendor;
+	json_object_object_get_ex(i, "identifier", &id);
+	json_object_object_get_ex(i, "name", &name);
+	json_object_object_get_ex(i, "type", &type);
+	json_object_object_get_ex(i, "product", &product);
+	json_object_object_get_ex(i, "vendor", &vendor);
+
+	const char *fmt =
+		"Input device: %s\n"
+		"  Type: %s\n"
+		"  Identifier: %s\n"
+		"  Product ID: %d\n"
+		"  Vendor ID: %d\n\n";
+
+
+	printf(fmt, json_object_get_string(name),
+		pretty_type_name(json_object_get_string(type)),
+		json_object_get_string(id),
+		json_object_get_int(product),
+		json_object_get_int(vendor));
 }
 
 static void pretty_print_output(json_object *o) {

From 8b4d81114a6e25be9f8f7c7bca17e55dbebd675d Mon Sep 17 00:00:00 2001
From: Tony Crisci <tony@dubstepdish.com>
Date: Mon, 18 Dec 2017 17:52:50 -0500
Subject: [PATCH 42/49] fix memory leak in swaymsg

---
 swaymsg/main.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/swaymsg/main.c b/swaymsg/main.c
index 18f17a59..dfc175a8 100644
--- a/swaymsg/main.c
+++ b/swaymsg/main.c
@@ -335,7 +335,7 @@ int main(int argc, char **argv) {
 			} else {
 				pretty_print(type, obj);
 			}
-			free(obj);
+			json_object_put(obj);
 		}
 	}
 	close(socketfd);

From 9df4a2c7a8598c344aa5ee5fabd8566e348ee66f Mon Sep 17 00:00:00 2001
From: Tony Crisci <tony@dubstepdish.com>
Date: Tue, 19 Dec 2017 04:52:03 -0500
Subject: [PATCH 43/49] document fallback seat

---
 sway/sway-input.5.txt | 4 ++++
 1 file changed, 4 insertions(+)

diff --git a/sway/sway-input.5.txt b/sway/sway-input.5.txt
index d4652a82..0603616b 100644
--- a/sway/sway-input.5.txt
+++ b/sway/sway-input.5.txt
@@ -95,6 +95,10 @@ their own "seat").
 	Attach an input device to this seat by its input identifier. A special value
 	of _*_ will attach all devices to the seat.
 
+**seat** <name> fallback <true|false>::
+	Set this seat as the fallback seat. A fallback seat will attach any device not
+	explicitly attached to another seat (similar to a "default" seat).
+
 See Also
 --------
 

From 730af5e721f04cf12b613341e00b44a93f3397c6 Mon Sep 17 00:00:00 2001
From: Tony Crisci <tony@dubstepdish.com>
Date: Tue, 19 Dec 2017 04:57:42 -0500
Subject: [PATCH 44/49] use snprintf to get identifier len

---
 sway/input/input-manager.c | 16 ++--------------
 1 file changed, 2 insertions(+), 14 deletions(-)

diff --git a/sway/input/input-manager.c b/sway/input/input-manager.c
index 52da8f5e..b20b7b7e 100644
--- a/sway/input/input-manager.c
+++ b/sway/input/input-manager.c
@@ -34,13 +34,6 @@ static struct sway_seat *input_manager_get_seat(
 	return sway_seat_create(input, seat_name);
 }
 
-static inline int strlen_num(int num) {
-	if (num == 0) {
-		return 2;
-	}
-	return (int)((ceil(log10(abs(num)))+2));
-}
-
 static char *get_device_identifier(struct wlr_input_device *device) {
 	int vendor = device->vendor;
 	int product = device->product;
@@ -54,19 +47,14 @@ static char *get_device_identifier(struct wlr_input_device *device) {
 		}
 	}
 
-	int len =
-		(strlen(name) +
-		strlen_num(device->vendor) +
-		strlen_num(device->product) +
-		3) * sizeof(char);
-
+	const char *fmt = "%d:%d:%s";
+	int len = snprintf(NULL, 0, fmt, vendor, product, name) + 1;
 	char *identifier = malloc(len);
 	if (!identifier) {
 		sway_log(L_ERROR, "Unable to allocate unique input device name");
 		return NULL;
 	}
 
-	const char *fmt = "%d:%d:%s";
 	snprintf(identifier, len, fmt, vendor, product, name);
 	free(name);
 	return identifier;

From 5c036a3eac89e3dec71484fe9c22704b120254b1 Mon Sep 17 00:00:00 2001
From: Tony Crisci <tony@dubstepdish.com>
Date: Tue, 19 Dec 2017 05:26:55 -0500
Subject: [PATCH 45/49] error on not enough input/seat args for cmd

---
 sway/commands/input.c | 88 +++++++++++++++++++++----------------------
 sway/commands/seat.c  | 32 ++++++++--------
 2 files changed, 60 insertions(+), 60 deletions(-)

diff --git a/sway/commands/input.c b/sway/commands/input.c
index dac028a7..ccb1d276 100644
--- a/sway/commands/input.c
+++ b/sway/commands/input.c
@@ -16,50 +16,50 @@ struct cmd_results *cmd_input(int argc, char **argv) {
 		return cmd_results_new(CMD_BLOCK_INPUT, NULL, NULL);
 	}
 
-	if (argc > 2) {
-		int argc_new = argc-2;
-		char **argv_new = argv+2;
-
-		struct cmd_results *res;
-		current_input_config = new_input_config(argv[0]);
-		if (strcasecmp("accel_profile", argv[1]) == 0) {
-			res = input_cmd_accel_profile(argc_new, argv_new);
-		} else if (strcasecmp("click_method", argv[1]) == 0) {
-			res = input_cmd_click_method(argc_new, argv_new);
-		} else if (strcasecmp("drag_lock", argv[1]) == 0) {
-			res = input_cmd_drag_lock(argc_new, argv_new);
-		} else if (strcasecmp("dwt", argv[1]) == 0) {
-			res = input_cmd_dwt(argc_new, argv_new);
-		} else if (strcasecmp("events", argv[1]) == 0) {
-			res = input_cmd_events(argc_new, argv_new);
-		} else if (strcasecmp("left_handed", argv[1]) == 0) {
-			res = input_cmd_left_handed(argc_new, argv_new);
-		} else if (strcasecmp("middle_emulation", argv[1]) == 0) {
-			res = input_cmd_middle_emulation(argc_new, argv_new);
-		} else if (strcasecmp("natural_scroll", argv[1]) == 0) {
-			res = input_cmd_natural_scroll(argc_new, argv_new);
-		} else if (strcasecmp("pointer_accel", argv[1]) == 0) {
-			res = input_cmd_pointer_accel(argc_new, argv_new);
-		} else if (strcasecmp("scroll_method", argv[1]) == 0) {
-			res = input_cmd_scroll_method(argc_new, argv_new);
-		} else if (strcasecmp("tap", argv[1]) == 0) {
-			res = input_cmd_tap(argc_new, argv_new);
-		} else if (strcasecmp("xkb_layout", argv[1]) == 0) {
-			res = input_cmd_xkb_layout(argc_new, argv_new);
-		} else if (strcasecmp("xkb_model", argv[1]) == 0) {
-			res = input_cmd_xkb_model(argc_new, argv_new);
-		} else if (strcasecmp("xkb_options", argv[1]) == 0) {
-			res = input_cmd_xkb_options(argc_new, argv_new);
-		} else if (strcasecmp("xkb_rules", argv[1]) == 0) {
-			res = input_cmd_xkb_rules(argc_new, argv_new);
-		} else if (strcasecmp("xkb_variant", argv[1]) == 0) {
-			res = input_cmd_xkb_variant(argc_new, argv_new);
-		} else {
-			res = cmd_results_new(CMD_INVALID, "input <device>", "Unknown command %s", argv[1]);
-		}
-		current_input_config = NULL;
-		return res;
+	if ((error = checkarg(argc, "input", EXPECTED_AT_LEAST, 3))) {
+		return error;
 	}
 
-	return cmd_results_new(CMD_BLOCK_INPUT, NULL, NULL);
+	int argc_new = argc-2;
+	char **argv_new = argv+2;
+
+	struct cmd_results *res;
+	current_input_config = new_input_config(argv[0]);
+	if (strcasecmp("accel_profile", argv[1]) == 0) {
+		res = input_cmd_accel_profile(argc_new, argv_new);
+	} else if (strcasecmp("click_method", argv[1]) == 0) {
+		res = input_cmd_click_method(argc_new, argv_new);
+	} else if (strcasecmp("drag_lock", argv[1]) == 0) {
+		res = input_cmd_drag_lock(argc_new, argv_new);
+	} else if (strcasecmp("dwt", argv[1]) == 0) {
+		res = input_cmd_dwt(argc_new, argv_new);
+	} else if (strcasecmp("events", argv[1]) == 0) {
+		res = input_cmd_events(argc_new, argv_new);
+	} else if (strcasecmp("left_handed", argv[1]) == 0) {
+		res = input_cmd_left_handed(argc_new, argv_new);
+	} else if (strcasecmp("middle_emulation", argv[1]) == 0) {
+		res = input_cmd_middle_emulation(argc_new, argv_new);
+	} else if (strcasecmp("natural_scroll", argv[1]) == 0) {
+		res = input_cmd_natural_scroll(argc_new, argv_new);
+	} else if (strcasecmp("pointer_accel", argv[1]) == 0) {
+		res = input_cmd_pointer_accel(argc_new, argv_new);
+	} else if (strcasecmp("scroll_method", argv[1]) == 0) {
+		res = input_cmd_scroll_method(argc_new, argv_new);
+	} else if (strcasecmp("tap", argv[1]) == 0) {
+		res = input_cmd_tap(argc_new, argv_new);
+	} else if (strcasecmp("xkb_layout", argv[1]) == 0) {
+		res = input_cmd_xkb_layout(argc_new, argv_new);
+	} else if (strcasecmp("xkb_model", argv[1]) == 0) {
+		res = input_cmd_xkb_model(argc_new, argv_new);
+	} else if (strcasecmp("xkb_options", argv[1]) == 0) {
+		res = input_cmd_xkb_options(argc_new, argv_new);
+	} else if (strcasecmp("xkb_rules", argv[1]) == 0) {
+		res = input_cmd_xkb_rules(argc_new, argv_new);
+	} else if (strcasecmp("xkb_variant", argv[1]) == 0) {
+		res = input_cmd_xkb_variant(argc_new, argv_new);
+	} else {
+		res = cmd_results_new(CMD_INVALID, "input <device>", "Unknown command %s", argv[1]);
+	}
+	current_input_config = NULL;
+	return res;
 }
diff --git a/sway/commands/seat.c b/sway/commands/seat.c
index 0149762a..155bc510 100644
--- a/sway/commands/seat.c
+++ b/sway/commands/seat.c
@@ -16,22 +16,22 @@ struct cmd_results *cmd_seat(int argc, char **argv) {
 		return cmd_results_new(CMD_BLOCK_SEAT, NULL, NULL);
 	}
 
-	if (argc > 2) {
-		int argc_new = argc-2;
-		char **argv_new = argv+2;
-
-		struct cmd_results *res;
-		current_seat_config = new_seat_config(argv[0]);
-		if (strcasecmp("attach", argv[1]) == 0) {
-			res = seat_cmd_attach(argc_new, argv_new);
-		} else if (strcasecmp("fallback", argv[1]) == 0) {
-			res = seat_cmd_fallback(argc_new, argv_new);
-		} else {
-			res = cmd_results_new(CMD_INVALID, "seat <name>", "Unknown command %s", argv[1]);
-		}
-		current_seat_config = NULL;
-		return res;
+	if ((error = checkarg(argc, "seat", EXPECTED_AT_LEAST, 3))) {
+		return error;
 	}
 
-	return cmd_results_new(CMD_BLOCK_SEAT, NULL, NULL);
+	int argc_new = argc-2;
+	char **argv_new = argv+2;
+
+	struct cmd_results *res;
+	current_seat_config = new_seat_config(argv[0]);
+	if (strcasecmp("attach", argv[1]) == 0) {
+		res = seat_cmd_attach(argc_new, argv_new);
+	} else if (strcasecmp("fallback", argv[1]) == 0) {
+		res = seat_cmd_fallback(argc_new, argv_new);
+	} else {
+		res = cmd_results_new(CMD_INVALID, "seat <name>", "Unknown command %s", argv[1]);
+	}
+	current_seat_config = NULL;
+	return res;
 }

From 39e7871a859df5ce82f9d3d10088a3bc2b4ff356 Mon Sep 17 00:00:00 2001
From: Tony Crisci <tony@dubstepdish.com>
Date: Tue, 19 Dec 2017 05:28:06 -0500
Subject: [PATCH 46/49] dont set cursor image on motion

---
 sway/input/cursor.c | 3 ---
 1 file changed, 3 deletions(-)

diff --git a/sway/input/cursor.c b/sway/input/cursor.c
index 24d4642d..3b5cfce5 100644
--- a/sway/input/cursor.c
+++ b/sway/input/cursor.c
@@ -15,9 +15,6 @@ static void cursor_update_position(struct sway_cursor *cursor) {
 	double x = cursor->cursor->x;
 	double y = cursor->cursor->y;
 
-	wlr_xcursor_manager_set_cursor_image(cursor->xcursor_manager,
-		"left_ptr", cursor->cursor);
-
 	cursor->x = x;
 	cursor->y = y;
 }

From f35575f71dfab5be9a935bc5e21ca5ee5dc4c2c2 Mon Sep 17 00:00:00 2001
From: Tony Crisci <tony@dubstepdish.com>
Date: Tue, 19 Dec 2017 05:36:17 -0500
Subject: [PATCH 47/49] handle keymap not found

---
 sway/input/keyboard.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/sway/input/keyboard.c b/sway/input/keyboard.c
index bb18edd2..724941d8 100644
--- a/sway/input/keyboard.c
+++ b/sway/input/keyboard.c
@@ -85,10 +85,19 @@ void sway_keyboard_configure(struct sway_keyboard *keyboard) {
 		return;
 	}
 
-	xkb_keymap_unref(keyboard->keymap);
-	keyboard->keymap =
+	struct xkb_keymap *keymap =
 		xkb_keymap_new_from_names(context, &rules, XKB_KEYMAP_COMPILE_NO_FLAGS);
+
+	if (!keymap) {
+		sway_log(L_DEBUG, "cannot configure keyboard: keymap does not exist");
+		xkb_context_unref(context);
+		return;
+	}
+
+	xkb_keymap_unref(keyboard->keymap);
+	keyboard->keymap = keymap;
 	wlr_keyboard_set_keymap(wlr_device->keyboard, keyboard->keymap);
+
 	wlr_keyboard_set_repeat_info(wlr_device->keyboard, 25, 600);
 	xkb_context_unref(context);
 	struct wlr_seat *seat = keyboard->seat_device->sway_seat->wlr_seat;

From a4c1270ef0a3c5bd610b13867be7616a55e59dc5 Mon Sep 17 00:00:00 2001
From: Tony Crisci <tony@dubstepdish.com>
Date: Tue, 19 Dec 2017 05:38:41 -0500
Subject: [PATCH 48/49] fix memory leak in swaymsg

---
 swaymsg/main.c | 4 +++-
 1 file changed, 3 insertions(+), 1 deletion(-)

diff --git a/swaymsg/main.c b/swaymsg/main.c
index dfc175a8..b431872a 100644
--- a/swaymsg/main.c
+++ b/swaymsg/main.c
@@ -308,9 +308,11 @@ int main(int argc, char **argv) {
 	}
 	free(cmdtype);
 
-	char *command = strdup("");
+	char *command = NULL;
 	if (optind < argc) {
 		command = join_args(argv + optind, argc - optind);
+	} else {
+		command = strdup("");
 	}
 
 	int ret = 0;

From 63f7fb95172a72436698a1562b4f7ea1e9100a7e Mon Sep 17 00:00:00 2001
From: Tony Crisci <tony@dubstepdish.com>
Date: Wed, 20 Dec 2017 06:12:08 -0500
Subject: [PATCH 49/49] cleanup

---
 sway/config/seat.c         | 3 +++
 sway/input/input-manager.c | 9 +++++----
 sway/input/seat.c          | 1 +
 3 files changed, 9 insertions(+), 4 deletions(-)

diff --git a/sway/config/seat.c b/sway/config/seat.c
index 4c9e8d0d..113139e8 100644
--- a/sway/config/seat.c
+++ b/sway/config/seat.c
@@ -14,6 +14,7 @@ struct seat_config *new_seat_config(const char* name) {
 	sway_log(L_DEBUG, "new_seat_config(%s)", name);
 	seat->name = strdup(name);
 	if (!sway_assert(seat->name, "could not allocate name for seat")) {
+		free(seat);
 		return NULL;
 	}
 
@@ -21,6 +22,8 @@ struct seat_config *new_seat_config(const char* name) {
 	seat->attachments = create_list();
 	if (!sway_assert(seat->attachments,
 				"could not allocate seat attachments list")) {
+		free(seat->name);
+		free(seat);
 		return NULL;
 	}
 
diff --git a/sway/input/input-manager.c b/sway/input/input-manager.c
index b20b7b7e..4459c43b 100644
--- a/sway/input/input-manager.c
+++ b/sway/input/input-manager.c
@@ -119,10 +119,11 @@ static void input_add_notify(struct wl_listener *listener, void *data) {
 
 	bool added = false;
 	wl_list_for_each(seat, &input->seats, link) {
-		if (seat->config &&
-				(seat_config_get_attachment(seat->config,
-											input_device->identifier) ||
-				seat_config_get_attachment(seat->config, "*"))) {
+		bool has_attachment = seat->config &&
+			(seat_config_get_attachment(seat->config, input_device->identifier) ||
+			 seat_config_get_attachment(seat->config, "*"));
+
+		if (has_attachment) {
 			sway_seat_add_device(seat, input_device);
 			added = true;
 		}
diff --git a/sway/input/seat.c b/sway/input/seat.c
index df1427fa..fe90565a 100644
--- a/sway/input/seat.c
+++ b/sway/input/seat.c
@@ -30,6 +30,7 @@ struct sway_seat *sway_seat_create(struct sway_input_manager *input,
 
 	seat->wlr_seat = wlr_seat_create(input->server->wl_display, seat_name);
 	if (!sway_assert(seat->wlr_seat, "could not allocate seat")) {
+		free(seat);
 		return NULL;
 	}