From c8085038cffb1dc1ba65d3746ec321f314f42567 Mon Sep 17 00:00:00 2001
From: Brian Ashworth <bosrsf04@gmail.com>
Date: Thu, 7 Mar 2019 03:37:49 -0500
Subject: [PATCH] ipc: describe libinput device configuration

This adds the device configurations to the ipc response for libinput
devices. Only supported configuration options for the device will be
added. This also moves `libinput_send_events` inside a new `libinput`
object that contains the rest of the configuration options. sway-ipc(7)
has been updated to reflect the changes and document the new additions.
---
 sway/ipc-json.c     | 198 ++++++++++++++++++++++++++++++++++++++++----
 sway/sway-ipc.7.scd | 144 ++++++++++++++++++++++++++++----
 swaymsg/main.c      |  10 ++-
 3 files changed, 317 insertions(+), 35 deletions(-)

diff --git a/sway/ipc-json.c b/sway/ipc-json.c
index 20dcafb1..e9564b04 100644
--- a/sway/ipc-json.c
+++ b/sway/ipc-json.c
@@ -617,6 +617,187 @@ json_object *ipc_json_describe_node_recursive(struct sway_node *node) {
 	return object;
 }
 
+static json_object *describe_libinput_device(struct libinput_device *device) {
+	json_object *object = json_object_new_object();
+
+	const char *events = "unknown";
+	switch (libinput_device_config_send_events_get_mode(device)) {
+	case LIBINPUT_CONFIG_SEND_EVENTS_ENABLED:
+		events = "enabled";
+		break;
+	case LIBINPUT_CONFIG_SEND_EVENTS_DISABLED_ON_EXTERNAL_MOUSE:
+		events = "disabled_on_external_mouse";
+		break;
+	case LIBINPUT_CONFIG_SEND_EVENTS_DISABLED:
+		events = "disabled";
+		break;
+	}
+	json_object_object_add(object, "send_events",
+			json_object_new_string(events));
+
+	if (libinput_device_config_tap_get_finger_count(device) > 0) {
+		const char *tap = "unknown";
+		switch (libinput_device_config_tap_get_enabled(device)) {
+		case LIBINPUT_CONFIG_TAP_ENABLED:
+			tap = "enabled";
+			break;
+		case LIBINPUT_CONFIG_TAP_DISABLED:
+			tap = "disabled";
+			break;
+		}
+		json_object_object_add(object, "tap", json_object_new_string(tap));
+
+		const char *button_map = "unknown";
+		switch (libinput_device_config_tap_get_button_map(device)) {
+		case LIBINPUT_CONFIG_TAP_MAP_LRM:
+			button_map = "lrm";
+			break;
+		case LIBINPUT_CONFIG_TAP_MAP_LMR:
+			button_map = "lmr";
+			break;
+		}
+		json_object_object_add(object, "tap_button_map",
+				json_object_new_string(button_map));
+
+		const char* drag = "unknown";
+		switch (libinput_device_config_tap_get_drag_enabled(device)) {
+		case LIBINPUT_CONFIG_DRAG_ENABLED:
+			drag = "enabled";
+			break;
+		case LIBINPUT_CONFIG_DRAG_DISABLED:
+			drag = "disabled";
+			break;
+		}
+		json_object_object_add(object, "tap_drag",
+				json_object_new_string(drag));
+
+		const char *drag_lock = "unknown";
+		switch (libinput_device_config_tap_get_drag_lock_enabled(device)) {
+		case LIBINPUT_CONFIG_DRAG_LOCK_ENABLED:
+			drag_lock = "enabled";
+			break;
+		case LIBINPUT_CONFIG_DRAG_LOCK_DISABLED:
+			drag_lock = "disabled";
+			break;
+		}
+		json_object_object_add(object, "tap_drag_lock",
+				json_object_new_string(drag_lock));
+	}
+
+	if (libinput_device_config_accel_is_available(device)) {
+		double accel = libinput_device_config_accel_get_speed(device);
+		json_object_object_add(object, "accel_speed",
+				json_object_new_double(accel));
+
+		const char *accel_profile = "unknown";
+		switch (libinput_device_config_accel_get_profile(device)) {
+		case LIBINPUT_CONFIG_ACCEL_PROFILE_NONE:
+			accel_profile = "none";
+			break;
+		case LIBINPUT_CONFIG_ACCEL_PROFILE_FLAT:
+			accel_profile = "flat";
+			break;
+		case LIBINPUT_CONFIG_ACCEL_PROFILE_ADAPTIVE:
+			accel_profile = "adaptive";
+			break;
+		}
+		json_object_object_add(object, "accel_profile",
+				json_object_new_string(accel_profile));
+	}
+
+	if (libinput_device_config_scroll_has_natural_scroll(device)) {
+		const char *natural_scroll = "disabled";
+		if (libinput_device_config_scroll_get_natural_scroll_enabled(device)) {
+			natural_scroll = "enabled";
+		}
+		json_object_object_add(object, "natural_scroll",
+				json_object_new_string(natural_scroll));
+	}
+
+	if (libinput_device_config_left_handed_is_available(device)) {
+		const char *left_handed = "disabled";
+		if (libinput_device_config_left_handed_get(device) != 0) {
+			left_handed = "enabled";
+		}
+		json_object_object_add(object, "left_handed",
+				json_object_new_string(left_handed));
+	}
+
+	uint32_t click_methods = libinput_device_config_click_get_methods(device);
+	if ((click_methods & ~LIBINPUT_CONFIG_CLICK_METHOD_NONE) != 0) {
+		const char *click_method = "unknown";
+		switch (libinput_device_config_click_get_method(device)) {
+		case LIBINPUT_CONFIG_CLICK_METHOD_NONE:
+			click_method = "none";
+			break;
+		case LIBINPUT_CONFIG_CLICK_METHOD_BUTTON_AREAS:
+			click_method = "button_areas";
+			break;
+		case LIBINPUT_CONFIG_CLICK_METHOD_CLICKFINGER:
+			click_method = "clickfinger";
+			break;
+		}
+		json_object_object_add(object, "click_method",
+				json_object_new_string(click_method));
+	}
+
+	if (libinput_device_config_middle_emulation_is_available(device)) {
+		const char *middle_emulation = "unknown";
+		switch (libinput_device_config_middle_emulation_get_enabled(device)) {
+		case LIBINPUT_CONFIG_MIDDLE_EMULATION_ENABLED:
+			middle_emulation = "enabled";
+			break;
+		case LIBINPUT_CONFIG_MIDDLE_EMULATION_DISABLED:
+			middle_emulation = "disabled";
+			break;
+		}
+		json_object_object_add(object, "middle_emulation",
+				json_object_new_string(middle_emulation));
+	}
+
+	uint32_t scroll_methods = libinput_device_config_scroll_get_methods(device);
+	if ((scroll_methods & ~LIBINPUT_CONFIG_SCROLL_NO_SCROLL) != 0) {
+		const char *scroll_method = "unknown";
+		switch (libinput_device_config_scroll_get_method(device)) {
+		case LIBINPUT_CONFIG_SCROLL_NO_SCROLL:
+			scroll_method = "none";
+			break;
+		case LIBINPUT_CONFIG_SCROLL_2FG:
+			scroll_method = "two_finger";
+			break;
+		case LIBINPUT_CONFIG_SCROLL_EDGE:
+			scroll_method = "edge";
+			break;
+		case LIBINPUT_CONFIG_SCROLL_ON_BUTTON_DOWN:
+			scroll_method = "on_button_down";
+			break;
+		}
+		json_object_object_add(object, "scroll_method",
+				json_object_new_string(scroll_method));
+
+		if ((scroll_methods & LIBINPUT_CONFIG_SCROLL_ON_BUTTON_DOWN) != 0) {
+			uint32_t button = libinput_device_config_scroll_get_button(device);
+			json_object_object_add(object, "scroll_button",
+					json_object_new_int(button));
+		}
+	}
+
+	if (libinput_device_config_dwt_is_available(device)) {
+		const char *dwt = "unknown";
+		switch (libinput_device_config_dwt_get_enabled(device)) {
+		case LIBINPUT_CONFIG_DWT_ENABLED:
+			dwt = "enabled";
+			break;
+		case LIBINPUT_CONFIG_DWT_DISABLED:
+			dwt = "disabled";
+			break;
+		}
+		json_object_object_add(object, "dwt", json_object_new_string(dwt));
+	}
+
+	return object;
+}
+
 json_object *ipc_json_describe_input(struct sway_input_device *device) {
 	if (!(sway_assert(device, "Device must not be null"))) {
 		return NULL;
@@ -660,21 +841,8 @@ json_object *ipc_json_describe_input(struct sway_input_device *device) {
 	if (wlr_input_device_is_libinput(device->wlr_device)) {
 		struct libinput_device *libinput_dev;
 		libinput_dev = wlr_libinput_get_device_handle(device->wlr_device);
-
-		const char *events = "unknown";
-		switch (libinput_device_config_send_events_get_mode(libinput_dev)) {
-		case LIBINPUT_CONFIG_SEND_EVENTS_ENABLED:
-			events = "enabled";
-			break;
-		case LIBINPUT_CONFIG_SEND_EVENTS_DISABLED_ON_EXTERNAL_MOUSE:
-			events = "disabled_on_external_mouse";
-			break;
-		case LIBINPUT_CONFIG_SEND_EVENTS_DISABLED:
-			events = "disabled";
-			break;
-		}
-		json_object_object_add(object, "libinput_send_events",
-				json_object_new_string(events));
+		json_object_object_add(object, "libinput",
+				describe_libinput_device(libinput_dev));
 	}
 
 	return object;
diff --git a/sway/sway-ipc.7.scd b/sway/sway-ipc.7.scd
index 6b400453..b43b3030 100644
--- a/sway/sway-ipc.7.scd
+++ b/sway/sway-ipc.7.scd
@@ -1034,10 +1034,66 @@ following properties:
 |- xkb_active_layout_name
 :  string
 :  (Only keyboards) The active keyboard layout in use
-|- libinput_send_events
+|- libinput
+:  object
+:  (Only libinput devices) An object describing the current device settings.
+   See below for more information
+
+The _libinput_ object describes the device configuration for libinput devices.
+Only properties that are supported for the device will be added to the object.
+In addition to the possible options listed, all string properties may also be
+_unknown_, in the case that a new option is added to libinput. See
+*sway-input*(5) for information on the meaning of the possible values. The
+following properties will be included for devices that support them:
+
+[- *PROPERTY*
+:- *DATA TYPE*
+:- *DESCRIPTION*
+|- send_events
 :  string
-:  (Only libinput devices) The send events value in use by libinput for this
-   device. It can be _enabled_, _disabled_, or _disabled\_on\_external\_mouse_
+:[ Whether events are being sent by the device. It can be _enabled_,
+   _disabled_, or _disabled\_on\_external\_mouse_
+|- tap
+:  string
+:  Whether tap to click is enabled. It can be _enabled_ or _disabled_
+|- tap_button_map
+:  string
+:  The finger to button mapping in use. It can be _lmr_ or _lrm_
+|- tap_drag
+:  string
+:  Whether tap-and-drag is enabled. It can be _enabled_ or _disabled_
+|- tap_drag_lock
+:  string
+:  Whether drag-lock is enabled. It can be _enabled_ or _disabled_
+|- accel_speed
+:  double
+:  The pointer-acceleration in use
+|- accel_profile
+:  string
+:  The acceleration profile in use. It can be _none_, _flat_, or _adaptive_
+|- natural_scroll
+:  string
+:  Whether natural scrolling is enabled. It can be _enabled_ or _disabled_
+|- left_handed
+:  string
+:  Whether left-handed mode is enabled. It can be _enabled_ or _disabled_
+|- click_method
+:  string
+:  The click method in use. It can be _none_, _button_areas_, or _clickfinger_
+|- middle_emulation
+:  string
+:  Whether middle emulation is enabled. It can be _enabled_ or _disabled_
+|- scroll_method
+:  string
+:  The scroll method in use. It can be _none_, _two_finger_, _edge_, or
+   _on_button_down_
+|- scroll_button
+:  int
+:  The scroll button to use when _scroll_method_ is _on_button_down_. This
+   will be given as an input event code
+|- dwt
+:  string
+:  Whether disable-while-typing is enabled. It can be _enabled_ or _disabled_
 
 
 *Example Reply:*
@@ -1050,7 +1106,9 @@ following properties:
 		"product": 1,
 		"type": "keyboard",
 		"xkb_active_layout_name": "English (US)",
-		"libinput_send_events": "enabled"
+		"libinput": {
+			"send_events": "enabled"
+		}
 	},
 	{
 		"identifier": "1267:5:Elan_Touchpad",
@@ -1058,7 +1116,21 @@ following properties:
 		"vendor": 1267,
 		"product": 5,
 		"type": "pointer",
-		"libinput_send_events": "enabled"
+		"libinput": {
+			"send_events": "enabled",
+			"tap": "enabled",
+			"tap_button_map": "lmr",
+			"tap_drag": "enabled",
+			"tap_drag_lock": "disabled",
+			"accel_speed": 0.0,
+			"accel_profile": "none",
+			"natural_scroll", "disabled",
+			"left_handed": "disabled",
+			"click_method": "button_areas",
+			"middle_emulation": "disabled",
+			"scroll_method": "edge",
+			"dwt": "enabled"
+		}
 	},
 	{
 		"identifier": "3034:22494:USB2.0_VGA_UVC_WebCam:_USB2.0_V",
@@ -1067,7 +1139,9 @@ following properties:
 		"product": 22494,
 		"type": "keyboard",
 		"xkb_active_layout_name": "English (US)",
-		"libinput_send_events": "enabled"
+		"libinput": {
+			"send_events": "enabled"
+		}
 	},
 	{
 		"identifier": "0:3:Sleep_Button",
@@ -1076,7 +1150,9 @@ following properties:
 		"product": 3,
 		"type": "keyboard",
 		"xkb_active_layout_name": "English (US)",
-		"libinput_send_events": "enabled"
+		"libinput": {
+			"send_events": "enabled"
+		}
 	},
 	{
 		"identifier": "0:5:Lid_Switch",
@@ -1084,7 +1160,10 @@ following properties:
 		"vendor": 0,
 		"product": 5,
 		"type": "switch",
-		"libinput_send_events": "enabled"
+		"libinput": {
+			"send_events": "enabled"
+		}
+	},
 	{
 		"identifier": "0:6:Video_Bus",
 		"name": "Video Bus",
@@ -1092,7 +1171,9 @@ following properties:
 		"product": 6,
 		"type": "keyboard",
 		"xkb_active_layout_name": "English (US)",
-		"libinput_send_events": "enabled"
+		"libinput": {
+			"send_events": "enabled"
+		}
 	},
 	{
 		"identifier": "0:1:Power_Button",
@@ -1101,7 +1182,9 @@ following properties:
 		"product": 1,
 		"type": "keyboard",
 		"xkb_active_layout_name": "English (US)",
-		"libinput_send_events": "enabled"
+		"libinput": {
+			"send_events": "enabled"
+		}
 	}
 ]
 ```
@@ -1150,7 +1233,9 @@ one seat. Each object has the following properties:
 				"product": 1,
 				"type": "keyboard",
 				"xkb_active_layout_name": "English (US)",
-				"libinput_send_events": "enabled"
+				"libinput": {
+					"send_events": "enabled"
+				}
 			},
 			{
 				"identifier": "1267:5:Elan_Touchpad",
@@ -1158,7 +1243,21 @@ one seat. Each object has the following properties:
 				"vendor": 1267,
 				"product": 5,
 				"type": "pointer",
-				"libinput_send_events": "enabled"
+				"libinput": {
+					"send_events": "enabled",
+					"tap": "enabled",
+					"tap_button_map": "lmr",
+					"tap_drag": "enabled",
+					"tap_drag_lock": "disabled",
+					"accel_speed": 0.0,
+					"accel_profile": "none",
+					"natural_scroll", "disabled",
+					"left_handed": "disabled",
+					"click_method": "button_areas",
+					"middle_emulation": "disabled",
+					"scroll_method": "edge",
+					"dwt": "enabled"
+				}
 			},
 			{
 				"identifier": "3034:22494:USB2.0_VGA_UVC_WebCam:_USB2.0_V",
@@ -1167,7 +1266,9 @@ one seat. Each object has the following properties:
 				"product": 22494,
 				"type": "keyboard",
 				"xkb_active_layout_name": "English (US)",
-				"libinput_send_events": "enabled"
+				"libinput": {
+					"send_events": "enabled"
+				}
 			},
 			{
 				"identifier": "0:3:Sleep_Button",
@@ -1176,7 +1277,9 @@ one seat. Each object has the following properties:
 				"product": 3,
 				"type": "keyboard",
 				"xkb_active_layout_name": "English (US)",
-				"libinput_send_events": "enabled"
+				"libinput": {
+					"send_events": "enabled"
+				}
 			},
 			{
 				"identifier": "0:5:Lid_Switch",
@@ -1184,7 +1287,10 @@ one seat. Each object has the following properties:
 				"vendor": 0,
 				"product": 5,
 				"type": "switch",
-				"libinput_send_events": "enabled"
+				"libinput": {
+					"send_events": "enabled"
+				}
+			},
 			{
 				"identifier": "0:6:Video_Bus",
 				"name": "Video Bus",
@@ -1192,7 +1298,9 @@ one seat. Each object has the following properties:
 				"product": 6,
 				"type": "keyboard",
 				"xkb_active_layout_name": "English (US)",
-				"libinput_send_events": "enabled"
+				"libinput": {
+					"send_events": "enabled"
+				}
 			},
 			{
 				"identifier": "0:1:Power_Button",
@@ -1201,7 +1309,9 @@ one seat. Each object has the following properties:
 				"product": 1,
 				"type": "keyboard",
 				"xkb_active_layout_name": "English (US)",
-				"libinput_send_events": "enabled"
+				"libinput": {
+					"send_events": "enabled"
+				}
 			}
 		]
 	}
diff --git a/swaymsg/main.c b/swaymsg/main.c
index 716d2d2e..e51c00d9 100644
--- a/swaymsg/main.c
+++ b/swaymsg/main.c
@@ -113,7 +113,7 @@ static const char *pretty_type_name(const char *name) {
 }
 
 static void pretty_print_input(json_object *i) {
-	json_object *id, *name, *type, *product, *vendor, *kbdlayout, *events;
+	json_object *id, *name, *type, *product, *vendor, *kbdlayout, *libinput;
 	json_object_object_get_ex(i, "identifier", &id);
 	json_object_object_get_ex(i, "name", &name);
 	json_object_object_get_ex(i, "type", &type);
@@ -139,8 +139,12 @@ static void pretty_print_input(json_object *i) {
 		printf("  Active Keyboard Layout: %s\n", layout ? layout : "(unnamed)");
 	}
 
-	if (json_object_object_get_ex(i, "libinput_send_events", &events)) {
-		printf("  Libinput Send Events: %s\n", json_object_get_string(events));
+	if (json_object_object_get_ex(i, "libinput", &libinput)) {
+		json_object *events;
+		if (json_object_object_get_ex(libinput, "send_events", &events)) {
+			printf("  Libinput Send Events: %s\n",
+					json_object_get_string(events));
+		}
 	}
 
 	printf("\n");