diff --git a/bin/input-monitor.c b/bin/input-monitor.c index 8d3e407f77b70981134fe2a8adccef93e0ea11dd..7d9efd21c39d90d295dbc0e251cf31a95b91b72a 100644 --- a/bin/input-monitor.c +++ b/bin/input-monitor.c @@ -43,6 +43,7 @@ static FILE *original_stdout = NULL; static gboolean opt_one_line = FALSE; static gboolean opt_seq = FALSE; +static gboolean opt_verbose = FALSE; static void print_json (JsonBuilder *builder) @@ -114,6 +115,46 @@ added (SrtInputDeviceMonitor *monitor, json_builder_end_array (builder); } + if (opt_verbose) + { + g_autofree gchar *uevent = srt_input_device_dup_uevent (dev); + + if (uevent != NULL) + { + const char *start = uevent; + const char *end; + + /* uevent is conceptually a single string, but we + * present it as an array of lines here, because that's + * a lot easier to read! */ + json_builder_set_member_name (builder, "uevent"); + json_builder_begin_array (builder); + + while (*start != '\0') + { + g_autofree gchar *valid = NULL; + + while (*start == '\n') + start++; + + if (*start == '\0') + break; + + end = strchrnul (start, '\n'); + + valid = g_utf8_make_valid (start, (end - start) - 1); + json_builder_add_string_value (builder, valid); + + if (*end == '\0') + break; + + start = end + 1; + } + + json_builder_end_array (builder); + } + } + /* TODO: Print more details here */ } json_builder_end_object (builder); @@ -236,6 +277,8 @@ static const GOptionEntry option_entries[] = NULL }, { "udev", 0, G_OPTION_FLAG_NO_ARG, G_OPTION_ARG_CALLBACK, opt_udev_cb, "Find devices using udev", NULL }, + { "verbose", 0, G_OPTION_FLAG_NONE, G_OPTION_ARG_NONE, &opt_verbose, + "Be more verbose", NULL }, { "version", 0, G_OPTION_FLAG_NONE, G_OPTION_ARG_NONE, &opt_print_version, "Print version number and exit", NULL }, { NULL } diff --git a/steam-runtime-tools/input-device-internal.h b/steam-runtime-tools/input-device-internal.h index 3d9b64d57aa64275587d0036f81514bfebb6d2ef..02acf67cc57b2aac3e9c74be720b5e0158fdfb52 100644 --- a/steam-runtime-tools/input-device-internal.h +++ b/steam-runtime-tools/input-device-internal.h @@ -36,6 +36,7 @@ struct _SrtInputDeviceInterface const char * (*get_sys_path) (SrtInputDevice *device); const char * (*get_subsystem) (SrtInputDevice *device); gchar ** (*dup_udev_properties) (SrtInputDevice *device); + gchar * (*dup_uevent) (SrtInputDevice *device); }; struct _SrtInputDeviceMonitorInterface diff --git a/steam-runtime-tools/input-device.c b/steam-runtime-tools/input-device.c index cdf89fa09e1f8b6018845fa9dd390c5c6606b05d..7188bbf30cfdc143baeae719ef44c3381111bede 100644 --- a/steam-runtime-tools/input-device.c +++ b/steam-runtime-tools/input-device.c @@ -135,6 +135,49 @@ srt_input_device_get_subsystem (SrtInputDevice *device) return iface->get_subsystem (device); } +/** + * srt_input_device_dup_uevent: + * @device: An object implementing #SrtInputDeviceInterface + * + * Return the `uevent` data from the kernel. + * + * Returns: (nullable) (transfer full): A multi-line string, or %NULL. + * Free with g_free(). + */ +gchar * +srt_input_device_dup_uevent (SrtInputDevice *device) +{ + SrtInputDeviceInterface *iface = SRT_INPUT_DEVICE_GET_INTERFACE (device); + + g_return_val_if_fail (iface != NULL, NULL); + + return iface->dup_uevent (device); +} + +/* + * Default implementation: read the file in /sys. The kernel provides this, + * so it should always be present, except in containers. + */ +static gchar * +srt_input_device_default_dup_uevent (SrtInputDevice *device) +{ + g_autofree gchar *uevent_path = NULL; + g_autofree gchar *contents = NULL; + const char *sys_path; + + sys_path = srt_input_device_get_sys_path (device); + + if (sys_path == NULL) + return NULL; + + uevent_path = g_build_filename (sys_path, "uevent", NULL); + + if (g_file_get_contents (uevent_path, &contents, NULL, NULL)) + return g_steal_pointer (&contents); + + return NULL; +} + /** * srt_input_device_dup_udev_properties: * @device: An object implementing #SrtInputDeviceInterface @@ -166,6 +209,7 @@ srt_input_device_default_init (SrtInputDeviceInterface *iface) IMPLEMENT2 (get_sys_path, get_null_string); IMPLEMENT2 (get_subsystem, get_null_string); IMPLEMENT2 (dup_udev_properties, get_null_strv); + IMPLEMENT (dup_uevent); #undef IMPLEMENT #undef IMPLEMENT2 diff --git a/steam-runtime-tools/input-device.h b/steam-runtime-tools/input-device.h index 1db5782dd3bb0990581f42aed11d33d73821e5ce..67685b7366cbc67a6451a897fe7714a623599673 100644 --- a/steam-runtime-tools/input-device.h +++ b/steam-runtime-tools/input-device.h @@ -52,6 +52,8 @@ _SRT_PUBLIC const char *srt_input_device_get_subsystem (SrtInputDevice *device); _SRT_PUBLIC gchar **srt_input_device_dup_udev_properties (SrtInputDevice *device); +_SRT_PUBLIC +gchar *srt_input_device_dup_uevent (SrtInputDevice *device); /** * SrtInputDeviceMonitorFlags: diff --git a/steam-runtime-tools/udev-input-device.c b/steam-runtime-tools/udev-input-device.c index 2303ddaf4d77f99a5df13dea042b87e760ca8400..7245a7535079218fa177f2642ffe747a4b3cd9af 100644 --- a/steam-runtime-tools/udev-input-device.c +++ b/steam-runtime-tools/udev-input-device.c @@ -64,6 +64,7 @@ static struct const char *(*udev_device_get_syspath) (struct udev_device *); struct udev_list_entry *(*udev_device_get_properties_list_entry) (struct udev_device *); const char *(*udev_device_get_property_value) (struct udev_device *, const char *); + const char *(*udev_device_get_sysattr_value) (struct udev_device *, const char *); struct udev_device *(*udev_device_ref) (struct udev_device *); struct udev_device *(*udev_device_unref) (struct udev_device *); @@ -208,6 +209,14 @@ srt_udev_input_device_dup_udev_properties (SrtInputDevice *device) return (gchar **) g_ptr_array_free (g_steal_pointer (&arr), FALSE); } +static gchar * +srt_udev_input_device_dup_uevent (SrtInputDevice *device) +{ + SrtUdevInputDevice *self = SRT_UDEV_INPUT_DEVICE (device); + + return g_strdup (symbols.udev_device_get_sysattr_value (self->dev, "uevent")); +} + static void srt_udev_input_device_iface_init (SrtInputDeviceInterface *iface) { @@ -217,6 +226,7 @@ srt_udev_input_device_iface_init (SrtInputDeviceInterface *iface) IMPLEMENT (get_sys_path); IMPLEMENT (get_subsystem); IMPLEMENT (dup_udev_properties); + IMPLEMENT (dup_uevent); #undef IMPLEMENT } @@ -380,6 +390,7 @@ srt_udev_input_device_monitor_initable_init (GInitable *initable, SYMBOL (udev_device_get_syspath); SYMBOL (udev_device_get_properties_list_entry); SYMBOL (udev_device_get_property_value); + SYMBOL (udev_device_get_sysattr_value); SYMBOL (udev_device_ref); SYMBOL (udev_device_unref); diff --git a/tests/input-device.c b/tests/input-device.c index 7e82f344537f3251e04efb2910bb2869c4389cb6..74335137a54d4e20527e1e6273f993c8161eb983 100644 --- a/tests/input-device.c +++ b/tests/input-device.c @@ -103,6 +103,7 @@ test_input_device_usb (Fixture *f, g_autoptr(MockInputDevice) mock_device = mock_input_device_new (); SrtInputDevice *device = SRT_INPUT_DEVICE (mock_device); g_auto(GStrv) udev_properties = NULL; + g_autofree gchar *uevent = NULL; mock_device->dev_node = g_strdup ("/dev/input/event0"); mock_device->sys_path = g_strdup ("/sys/devices/mock/usb/hid/input/input0/event0"); @@ -110,6 +111,7 @@ test_input_device_usb (Fixture *f, mock_device->udev_properties = g_new0 (gchar *, 2); mock_device->udev_properties[0] = g_strdup ("ID_INPUT_JOYSTICK=1"); mock_device->udev_properties[1] = NULL; + mock_device->uevent = g_strdup ("A=a\nB=b\n"); /* TODO: Fill in somewhat realistic details for a USB-attached * Steam Controller */ @@ -120,6 +122,9 @@ test_input_device_usb (Fixture *f, "/sys/devices/mock/usb/hid/input/input0/event0"); g_assert_cmpstr (srt_input_device_get_subsystem (device), ==, "input"); + uevent = srt_input_device_dup_uevent (device); + g_assert_cmpstr (uevent, ==, "A=a\nB=b\n"); + udev_properties = srt_input_device_dup_udev_properties (device); g_assert_nonnull (udev_properties); g_assert_cmpstr (udev_properties[0], ==, "ID_INPUT_JOYSTICK=1"); @@ -158,8 +163,12 @@ device_added_cb (SrtInputDeviceMonitor *monitor, * so we have to just emit debug messages. */ if (f->config->type == MOCK) { + g_autofree gchar *uevent = NULL; g_auto(GStrv) udev_properties = NULL; + uevent = srt_input_device_dup_uevent (device); + g_assert_cmpstr (uevent, ==, "ONE=1\nTWO=2\n"); + udev_properties = srt_input_device_dup_udev_properties (device); g_assert_nonnull (udev_properties); g_assert_cmpstr (udev_properties[0], ==, "ID_INPUT_JOYSTICK=1"); diff --git a/tests/mock-input-device.c b/tests/mock-input-device.c index ecd0fae376347ffa05016298ed66b9fd31a62ee8..cbbc05824e3f0fd8bfb639512d010c450f973ae3 100644 --- a/tests/mock-input-device.c +++ b/tests/mock-input-device.c @@ -64,6 +64,7 @@ mock_input_device_finalize (GObject *object) g_clear_pointer (&self->dev_node, g_free); g_clear_pointer (&self->subsystem, g_free); g_clear_pointer (&self->udev_properties, g_strfreev); + g_clear_pointer (&self->uevent, g_free); G_OBJECT_CLASS (mock_input_device_parent_class)->finalize (object); } @@ -108,6 +109,14 @@ mock_input_device_dup_udev_properties (SrtInputDevice *device) return g_strdupv (self->udev_properties); } +static gchar * +mock_input_device_dup_uevent (SrtInputDevice *device) +{ + MockInputDevice *self = MOCK_INPUT_DEVICE (device); + + return g_strdup (self->uevent); +} + static void mock_input_device_iface_init (SrtInputDeviceInterface *iface) { @@ -117,6 +126,7 @@ mock_input_device_iface_init (SrtInputDeviceInterface *iface) IMPLEMENT (get_sys_path); IMPLEMENT (get_subsystem); IMPLEMENT (dup_udev_properties); + IMPLEMENT (dup_uevent); #undef IMPLEMENT } @@ -271,6 +281,7 @@ add_steam_controller (MockInputDeviceMonitor *self, device->sys_path = g_strdup_printf ("/sys/devices/mock/usb/hid/input/input0/event%s", tail); device->subsystem = g_strdup ("input"); + device->uevent = g_strdup ("ONE=1\nTWO=2\n"); g_ptr_array_add (arr, g_strdup ("ID_INPUT_JOYSTICK=1")); g_ptr_array_add (arr, NULL); diff --git a/tests/mock-input-device.h b/tests/mock-input-device.h index 8e294dc97c7b3073753048a98e7666c334d73b27..1f73593d79ab1ee1970f233b15344112ec532265 100644 --- a/tests/mock-input-device.h +++ b/tests/mock-input-device.h @@ -53,6 +53,7 @@ struct _MockInputDevice gchar *dev_node; gchar *subsystem; gchar **udev_properties; + gchar *uevent; }; struct _MockInputDeviceClass