[gnome-games/wip/aplazas/781572-remove-vala-macro: 3/19] gamepad: Port LinuxRawGamepad to C
- From: Adrien Plazas <aplazas src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [gnome-games/wip/aplazas/781572-remove-vala-macro: 3/19] gamepad: Port LinuxRawGamepad to C
- Date: Tue, 2 May 2017 12:10:27 +0000 (UTC)
commit 1c85038f304e08be0203c6fc5b78d41fb7e15698
Author: Adrien Plazas <kekun plazas laposte net>
Date: Fri Apr 21 16:14:42 2017 +0200
gamepad: Port LinuxRawGamepad to C
This will help to port part of the gamepad handling to C to avoid using
the Vala preprocessor.
https://bugzilla.gnome.org/show_bug.cgi?id=781572
src/Makefile.am | 11 +-
src/gamepad/linux/linux-raw-gamepad.c | 452 ++++++++++++++++++++++++++++++
src/gamepad/linux/linux-raw-gamepad.h | 20 ++
src/gamepad/linux/linux-raw-gamepad.vala | 270 ------------------
src/gamepad/linux/linux-raw-gamepad.vapi | 6 +
5 files changed, 488 insertions(+), 271 deletions(-)
---
diff --git a/src/Makefile.am b/src/Makefile.am
index d0aa85f..859cb8f 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -24,6 +24,7 @@ BUILT_SOURCES = \
EXTRA_DIST = \
$(gresource_file) \
gamepad/linux/libevdev.vapi \
+ gamepad/linux/linux-raw-gamepad.vapi \
gamepad/raw-gamepad.vapi \
gamepad/raw-gamepad-monitor.vapi \
gamepad/standard-gamepad-axis.vapi \
@@ -156,7 +157,7 @@ gnome_games_SOURCES = \
if ENABLE_GAMEPADS
gnome_games_SOURCES += \
- gamepad/linux/linux-raw-gamepad.vala \
+ gamepad/linux/linux-raw-gamepad.c \
$(NULL)
if ENABLE_UDEV
@@ -196,6 +197,7 @@ if ENABLE_GAMEPADS
gnome_games_VALAFLAGS += \
--vapidir=gamepad/linux/ \
--pkg libevdev \
+ --pkg linux-raw-gamepad \
--define ENABLE_LINUX_GAMEPADS
$(NULL)
endif
@@ -212,6 +214,7 @@ gnome_games_CFLAGS = \
$(GAMEPADS_CFLAGS) \
$(UDEV_CFLAGS) \
-I$(srcdir)/gamepad \
+ -I$(srcdir)/gamepad/linux \
-DGNOMELOCALEDIR=\""$(datadir)/locale"\" \
-DGAMES_PLUGINS_DIR=\"$(libdir)/gnome-games/plugins\" \
$(NULL)
@@ -220,6 +223,12 @@ gnome_games_CPPFLAGS = \
-DRETRO_GTK_USE_UNSTABLE_API \
$(NULL)
+if ENABLE_GAMEPADS
+gnome_games_CPPFLAGS += \
+ -DENABLE_LINUX_GAMEPADS \
+ $(NULL)
+endif
+
gnome_games_LDADD = \
$(GNOME_GAMES_LIBS) \
$(GAMEPADS_LIBS) \
diff --git a/src/gamepad/linux/linux-raw-gamepad.c b/src/gamepad/linux/linux-raw-gamepad.c
new file mode 100644
index 0000000..b98ce28
--- /dev/null
+++ b/src/gamepad/linux/linux-raw-gamepad.c
@@ -0,0 +1,452 @@
+// This file is part of GNOME Games. License: GPL-3.0+.
+
+#include "linux-raw-gamepad.h"
+
+#include <errno.h>
+#include <fcntl.h>
+#include <glib/gi18n-lib.h>
+#include <libevdev/libevdev.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include "../raw-gamepad.h"
+#include "../standard-gamepad-axis.h"
+#include "../standard-gamepad-button.h"
+
+#define GUID_DATA_LENGTH 8
+#define GUID_STRING_LENGTH 32 // (GUID_DATA_LENGTH * sizeof (guint16))
+
+struct _GamesLinuxRawGamepad {
+ GObject parent_instance;
+
+ gint fd;
+ glong event_source_id;
+ struct libevdev *device;
+ guint8 key_map[KEY_MAX];
+ guint8 abs_map[ABS_MAX];
+ struct input_absinfo abs_info[ABS_MAX];
+ gchar *guid;
+};
+
+static void games_raw_gamepad_interface_init (GamesRawGamepadInterface *interface);
+
+G_DEFINE_TYPE_WITH_CODE (GamesLinuxRawGamepad, games_linux_raw_gamepad, G_TYPE_OBJECT,
+ G_IMPLEMENT_INTERFACE (GAMES_TYPE_RAW_GAMEPAD,
+ games_raw_gamepad_interface_init))
+
+/* Private */
+
+static gchar
+guint16_get_hex (guint16 value,
+ guint8 nibble)
+{
+ static const gchar hex_to_ascii_map[] = "0123456789abcdef";
+
+ g_assert (nibble < 4);
+
+ return hex_to_ascii_map[((value >> (4 * nibble)) & 0xf)];
+}
+
+static gchar *
+guint16s_to_hex_string (guint16 *data)
+{
+ gchar *result;
+ gint data_i;
+ gint result_i;
+ guint16 element;
+
+ result = g_malloc (GUID_STRING_LENGTH + 1);
+ result[GUID_STRING_LENGTH] = '\0';
+ for (data_i = 0, result_i = 0; data_i < GUID_DATA_LENGTH; data_i++) {
+ element = data[data_i];
+ result[result_i++] = guint16_get_hex (element, 1);
+ result[result_i++] = guint16_get_hex (element, 0);
+ result[result_i++] = guint16_get_hex (element, 3);
+ result[result_i++] = guint16_get_hex (element, 2);
+ }
+
+ return result;
+}
+
+static gchar *
+compute_guid_string (struct libevdev *device)
+{
+ guint16 guid_array[GUID_DATA_LENGTH] = { 0 };
+
+ guid_array[0] = (guint16) GINT_TO_LE (libevdev_get_id_bustype (device));
+ guid_array[1] = 0;
+ guid_array[2] = (guint16) GINT_TO_LE (libevdev_get_id_vendor (device));
+ guid_array[3] = 0;
+ guid_array[4] = (guint16) GINT_TO_LE (libevdev_get_id_product (device));
+ guid_array[5] = 0;
+ guid_array[6] = (guint16) GINT_TO_LE (libevdev_get_id_version (device));
+ guid_array[7] = 0;
+
+ return guint16s_to_hex_string (guid_array);
+}
+
+static const gchar *
+get_guid (GamesRawGamepad *base)
+{
+ GamesLinuxRawGamepad *self;
+
+ self = GAMES_LINUX_RAW_GAMEPAD (base);
+
+ if (self->guid == NULL)
+ self->guid = compute_guid_string (self->device);
+
+ return self->guid;
+}
+
+static struct input_absinfo *_abs_info_dup (struct input_absinfo *self);
+
+static GamesStandardGamepadAxis
+axis_to_standard_axis (gint code)
+{
+ switch (code) {
+ case ABS_X:
+ return GAMES_STANDARD_GAMEPAD_AXIS_LEFT_X;
+ case ABS_Y:
+ return GAMES_STANDARD_GAMEPAD_AXIS_LEFT_Y;
+ case ABS_RX:
+ return GAMES_STANDARD_GAMEPAD_AXIS_RIGHT_X;
+ case ABS_RY:
+ return GAMES_STANDARD_GAMEPAD_AXIS_RIGHT_Y;
+ default:
+ return GAMES_STANDARD_GAMEPAD_AXIS_UNKNOWN;
+ }
+}
+
+static GamesStandardGamepadButton
+button_to_standard_button (gint code)
+{
+ switch (code) {
+ case BTN_A:
+ return GAMES_STANDARD_GAMEPAD_BUTTON_A;
+ case BTN_B:
+ return GAMES_STANDARD_GAMEPAD_BUTTON_B;
+ case BTN_X:
+ return GAMES_STANDARD_GAMEPAD_BUTTON_Y;
+ case BTN_Y:
+ return GAMES_STANDARD_GAMEPAD_BUTTON_X;
+ case BTN_TL:
+ return GAMES_STANDARD_GAMEPAD_BUTTON_SHOULDER_L;
+ case BTN_TR:
+ return GAMES_STANDARD_GAMEPAD_BUTTON_SHOULDER_R;
+ case BTN_TL2:
+ return GAMES_STANDARD_GAMEPAD_BUTTON_TRIGGER_L;
+ case BTN_TR2:
+ return GAMES_STANDARD_GAMEPAD_BUTTON_TRIGGER_R;
+ case BTN_SELECT:
+ return GAMES_STANDARD_GAMEPAD_BUTTON_SELECT;
+ case BTN_START:
+ return GAMES_STANDARD_GAMEPAD_BUTTON_START;
+ case BTN_MODE:
+ return GAMES_STANDARD_GAMEPAD_BUTTON_HOME;
+ case BTN_THUMBL:
+ return GAMES_STANDARD_GAMEPAD_BUTTON_STICK_L;
+ case BTN_THUMBR:
+ return GAMES_STANDARD_GAMEPAD_BUTTON_STICK_R;
+ default:
+ return GAMES_STANDARD_GAMEPAD_BUTTON_UNKNOWN;
+ }
+}
+
+static gdouble
+centered_axis_value (struct input_absinfo *abs_info,
+ gint32 value)
+{
+ gint64 min_absolute;
+ gint64 max_normalized;
+ gint64 value_normalized;
+ gint64 max_centered;
+ gint64 value_centered;
+ gint64 divisor;
+
+ g_return_val_if_fail (abs_info != NULL, 0.0);
+
+ min_absolute = llabs ((gint64) abs_info->minimum);
+
+ max_normalized = ((gint64) abs_info->maximum) + min_absolute;
+ value_normalized = ((gint64) value) + min_absolute;
+
+ max_centered = max_normalized / 2;
+ value_centered = (value_normalized - max_normalized) + max_centered;
+
+ divisor = value_centered < 0 ? max_centered + 1 : max_centered;;
+
+ return ((gdouble) value_centered) / ((gdouble) divisor);
+}
+
+static void
+handle_evdev_event (GamesLinuxRawGamepad *self)
+{
+ struct input_event event = { 0 };
+ gint code;
+ guint8 axis;
+ gdouble value;
+
+ g_return_if_fail (self != NULL);
+
+ if (libevdev_next_event (self->device, (guint) LIBEVDEV_READ_FLAG_NORMAL, &event) != 0)
+ return;
+
+ // FIXME Should not cast from uint to int? No need to store it?
+ code = (gint) event.code;
+ switch (event.type) {
+ case EV_KEY:
+ if ((code & BTN_GAMEPAD) == BTN_GAMEPAD)
+ g_signal_emit_by_name ((GamesRawGamepad*) self,
+ "standard-button-event",
+ button_to_standard_button (code),
+ (gboolean) event.value);
+ g_signal_emit_by_name ((GamesRawGamepad*) self,
+ "button-event",
+ (gint) self->key_map[code - BTN_MISC],
+ (gboolean) event.value);
+
+ break;
+ case EV_ABS:
+
+ switch (code) {
+ case ABS_HAT0X:
+ case ABS_HAT0Y:
+ case ABS_HAT1X:
+ case ABS_HAT1Y:
+ case ABS_HAT2X:
+ case ABS_HAT2Y:
+ case ABS_HAT3X:
+ case ABS_HAT3Y:
+ code = code - ABS_HAT0X;
+ g_signal_emit_by_name ((GamesRawGamepad*) self,
+ "dpad-event",
+ code / 2,
+ code % 2,
+ (gint) event.value);
+
+ return;
+ case ABS_X:
+ case ABS_Y:
+ case ABS_RX:
+ case ABS_RY:
+ axis = self->abs_map[code];
+ value = centered_axis_value (&self->abs_info[axis], event.value);
+ g_signal_emit_by_name ((GamesRawGamepad*) self,
+ "standard-axis-event",
+ axis_to_standard_axis (code),
+ value);
+
+ break;
+ }
+
+ axis = self->abs_map[code];
+ value = centered_axis_value (&self->abs_info[axis], event.value);
+ g_signal_emit_by_name ((GamesRawGamepad*) self, "axis-event", (gint) axis, value);
+
+ break;
+ }
+}
+
+static gboolean
+poll_events (GIOChannel *source,
+ GIOCondition condition,
+ gpointer data)
+{
+ GamesLinuxRawGamepad *self;
+
+ self = GAMES_LINUX_RAW_GAMEPAD (data);
+
+ g_return_val_if_fail (self != NULL, FALSE);
+
+ while (libevdev_has_event_pending (self->device))
+ handle_evdev_event (self);
+
+ return TRUE;
+}
+
+static gboolean
+has_key (struct libevdev *device,
+ guint code)
+{
+ return libevdev_has_event_code (device, (guint) EV_KEY, code);
+}
+
+static gboolean
+has_abs (struct libevdev *device,
+ guint code)
+{
+ return libevdev_has_event_code (device, (guint) EV_ABS, code);
+}
+
+static gboolean
+is_joystick (struct libevdev *device)
+{
+ gboolean has_joystick_axes_or_buttons;
+
+ g_return_val_if_fail (device != NULL, FALSE);
+
+ /* Same detection code as udev-builtin-input_id.c in systemd
+ * joysticks don’t necessarily have buttons; e. g.
+ * rudders/pedals are joystick-like, but buttonless; they have
+ * other fancy axes. */
+ has_joystick_axes_or_buttons =
+ has_key (device, BTN_TRIGGER) ||
+ has_key (device, BTN_A) ||
+ has_key (device, BTN_1) ||
+ has_abs (device, ABS_RX) ||
+ has_abs (device, ABS_RY) ||
+ has_abs (device, ABS_RZ) ||
+ has_abs (device, ABS_THROTTLE) ||
+ has_abs (device, ABS_RUDDER) ||
+ has_abs (device, ABS_WHEEL) ||
+ has_abs (device, ABS_GAS) ||
+ has_abs (device, ABS_BRAKE);
+
+ return has_joystick_axes_or_buttons;
+}
+
+static void
+remove_event_source (GamesLinuxRawGamepad *self)
+{
+ g_return_if_fail (self != NULL);
+
+ if (self->event_source_id < 0)
+ return;
+
+ g_source_remove ((guint) self->event_source_id);
+ self->event_source_id = -1;
+}
+
+/* Public */
+
+GamesLinuxRawGamepad *
+games_linux_raw_gamepad_new (const gchar *file_name,
+ GError **error)
+{
+ GamesLinuxRawGamepad *self = NULL;
+ GIOChannel *channel;
+ gint buttons_number;
+ gint axes_number;
+ guint i;
+
+ g_return_val_if_fail (file_name != NULL, NULL);
+
+ self = g_object_new (GAMES_TYPE_LINUX_RAW_GAMEPAD, NULL);
+
+ self->fd = open (file_name, O_RDONLY | O_NONBLOCK, (mode_t) 0);
+ if (self->fd < 0) {
+ g_set_error (error,
+ G_FILE_ERROR,
+ G_FILE_ERROR_FAILED,
+ _("Unable to open file “%s”: %s"),
+ file_name,
+ strerror (errno));
+ g_object_unref (self);
+
+ return NULL;
+ }
+
+ self->device = libevdev_new ();
+ if (libevdev_set_fd (self->device, self->fd) < 0) {
+ g_set_error (error,
+ G_FILE_ERROR,
+ G_FILE_ERROR_FAILED,
+ _("Evdev is unable to open “%s”: %s"),
+ file_name,
+ strerror (errno));
+ g_object_unref (self);
+
+ return NULL;
+ }
+
+ if (!is_joystick (self->device)) {
+ g_set_error (error,
+ G_FILE_ERROR,
+ G_FILE_ERROR_NXIO,
+ "“%s” is not a joystick",
+ file_name);
+ g_object_unref (self);
+
+ return NULL;
+ }
+
+ self->event_source_id = -1;
+
+ // Poll the events in the default main loop
+ channel = g_io_channel_unix_new (self->fd);
+ self->event_source_id = (glong) g_io_add_watch (channel, G_IO_IN, poll_events, self);
+ buttons_number = 0;
+
+ // Initialize dpads, buttons and axes
+ for (i = BTN_JOYSTICK; i < KEY_MAX; i++)
+ if (libevdev_has_event_code (self->device, (guint) EV_KEY, i)) {
+ self->key_map[i - BTN_MISC] = (guint8) buttons_number;
+ buttons_number++;
+ }
+ for (i = BTN_MISC; i < BTN_JOYSTICK; i++)
+ if (libevdev_has_event_code (self->device, (guint) EV_KEY, i)) {
+ self->key_map[i - BTN_MISC] = (guint8) buttons_number;
+ buttons_number++;
+ }
+
+ // Get info about axes
+ axes_number = 0;
+ for (i = 0; i < ABS_MAX; i++) {
+ // Skip dpads
+ if (i == ABS_HAT0X) {
+ i = ABS_HAT3Y;
+
+ continue;
+ }
+ if (libevdev_has_event_code (self->device, (guint) EV_ABS, i)) {
+ const struct input_absinfo *absinfo;
+
+ absinfo = libevdev_get_abs_info (self->device, i);
+ if (absinfo != NULL) {
+ self->abs_map[i] = (guint8) axes_number;
+ self->abs_info[axes_number] = *absinfo;
+ axes_number++;
+ }
+ }
+ }
+
+ g_io_channel_unref (channel);
+
+ return self;
+}
+
+/* Type */
+
+static void
+games_linux_raw_gamepad_finalize (GObject *object)
+{
+ GamesLinuxRawGamepad *self = GAMES_LINUX_RAW_GAMEPAD (object);
+
+ close (self->fd);
+ remove_event_source (self);
+ if (self->device != NULL)
+ libevdev_free (self->device);
+ if (self->guid == NULL)
+ g_free (self->guid);
+
+ G_OBJECT_CLASS (games_linux_raw_gamepad_parent_class)->finalize (object);
+}
+
+static void
+games_linux_raw_gamepad_class_init (GamesLinuxRawGamepadClass *klass)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (klass);
+
+ object_class->finalize = games_linux_raw_gamepad_finalize;
+}
+
+static void
+games_raw_gamepad_interface_init (GamesRawGamepadInterface *interface)
+{
+ interface->get_guid = get_guid;
+}
+
+static void
+games_linux_raw_gamepad_init (GamesLinuxRawGamepad *self)
+{
+ self->event_source_id = -1;
+}
diff --git a/src/gamepad/linux/linux-raw-gamepad.h b/src/gamepad/linux/linux-raw-gamepad.h
new file mode 100644
index 0000000..5500411
--- /dev/null
+++ b/src/gamepad/linux/linux-raw-gamepad.h
@@ -0,0 +1,20 @@
+// This file is part of GNOME Games. License: GPL-3.0+.
+
+#ifndef GAMES_LINUX_RAW_GAMEPAD_H
+#define GAMES_LINUX_RAW_GAMEPAD_H
+
+#include <glib-object.h>
+
+G_BEGIN_DECLS
+
+#define GAMES_TYPE_LINUX_RAW_GAMEPAD (games_linux_raw_gamepad_get_type())
+
+G_DECLARE_FINAL_TYPE (GamesLinuxRawGamepad, games_linux_raw_gamepad, GAMES, LINUX_RAW_GAMEPAD, GObject)
+
+/*GamesLinuxRawGamepad *games_linux_raw_gamepad_new (void);*/
+GamesLinuxRawGamepad *games_linux_raw_gamepad_new (const gchar *file_name,
+ GError **error);
+
+G_END_DECLS
+
+#endif /* GAMES_LINUX_RAW_GAMEPAD_H */
diff --git a/src/gamepad/linux/linux-raw-gamepad.vapi b/src/gamepad/linux/linux-raw-gamepad.vapi
new file mode 100644
index 0000000..fd0168b
--- /dev/null
+++ b/src/gamepad/linux/linux-raw-gamepad.vapi
@@ -0,0 +1,6 @@
+// This file is part of GNOME Games. License: GPL-3.0+.
+
+[CCode (cheader_filename = "linux-raw-gamepad.h")]
+private class Games.LinuxRawGamepad : GLib.Object, RawGamepad {
+ public LinuxRawGamepad (string file_name) throws GLib.FileError;
+}
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]