[retro-gtk] Add RetroControllerState
- From: Adrien Plazas <aplazas src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [retro-gtk] Add RetroControllerState
- Date: Sun, 16 Feb 2020 17:04:42 +0000 (UTC)
commit 7e126a1b41d522864b6bf4c2f0b59f4cbe2eae76
Author: Alexander Mikhaylenko <alexm gnome org>
Date: Tue Jan 14 14:14:39 2020 +0500
Add RetroControllerState
This will be used to provide low-latency input via shared memory.
shared/meson.build | 1 +
shared/retro-controller-state-private.h | 50 +++++
shared/retro-controller-state.c | 335 ++++++++++++++++++++++++++++++++
3 files changed, 386 insertions(+)
---
diff --git a/shared/meson.build b/shared/meson.build
index b8be7c7..60d6f7f 100644
--- a/shared/meson.build
+++ b/shared/meson.build
@@ -1,5 +1,6 @@
shared_sources = files([
'retro-controller-codes.c',
+ 'retro-controller-state.c',
'retro-controller-type.c',
'retro-input.c',
'retro-memfd.c',
diff --git a/shared/retro-controller-state-private.h b/shared/retro-controller-state-private.h
new file mode 100644
index 0000000..dc2f6ca
--- /dev/null
+++ b/shared/retro-controller-state-private.h
@@ -0,0 +1,50 @@
+// This file is part of retro-gtk. License: GPL-3.0+.
+
+#pragma once
+
+#if !defined(__RETRO_GTK_INSIDE__) && !defined(RETRO_GTK_COMPILATION)
+# error "Only <retro-gtk.h> can be included directly."
+#endif
+
+#include <glib-object.h>
+#include "retro-input.h"
+
+G_BEGIN_DECLS
+
+#define RETRO_TYPE_CONTROLLER_STATE (retro_controller_state_get_type())
+
+G_DECLARE_FINAL_TYPE (RetroControllerState, retro_controller_state, RETRO, CONTROLLER_STATE, GObject)
+
+RetroControllerState *retro_controller_state_new (gint fd);
+
+gint retro_controller_state_get_fd (RetroControllerState *self);
+
+void retro_controller_state_lock (RetroControllerState *self);
+void retro_controller_state_unlock (RetroControllerState *self);
+
+#ifdef RETRO_RUNNER_COMPILATION
+
+gboolean retro_controller_state_has_type (RetroControllerState *self,
+ RetroControllerType type);
+gint16 retro_controller_state_get_input (RetroControllerState *self,
+ RetroInput *input);
+
+gboolean retro_controller_state_get_supports_rumble (RetroControllerState *self);
+
+void retro_controller_state_snapshot (RetroControllerState *self);
+
+#else
+
+void retro_controller_state_set_for_type (RetroControllerState *self,
+ RetroControllerType type,
+ gint16 *state,
+ gsize n_items);
+void retro_controller_state_clear_type (RetroControllerState *self,
+ RetroControllerType type);
+
+void retro_controller_state_set_supports_rumble (RetroControllerState *self,
+ gboolean supports_rumble);
+
+#endif
+
+G_END_DECLS
diff --git a/shared/retro-controller-state.c b/shared/retro-controller-state.c
new file mode 100644
index 0000000..a036d80
--- /dev/null
+++ b/shared/retro-controller-state.c
@@ -0,0 +1,335 @@
+// This file is part of retro-gtk. License: GPL-3.0+.
+
+#include "retro-controller-state-private.h"
+
+#include <semaphore.h>
+#include <errno.h>
+#include <sys/mman.h>
+#include <unistd.h>
+#include "retro-controller-type.h"
+#include "retro-input-private.h"
+
+typedef struct {
+ gboolean is_dirty;
+ gboolean supports_rumble;
+ gint64 available_types;
+ gint16 joypad_data[RETRO_JOYPAD_ID_COUNT];
+ gint16 mouse_data[RETRO_MOUSE_ID_COUNT];
+ gint16 keyboard_data[RETRO_KEYBOARD_KEY_LAST];
+ gint16 lightgun_data[RETRO_LIGHTGUN_ID_COUNT];
+ gint16 analog_data[RETRO_ANALOG_ID_COUNT * RETRO_ANALOG_INDEX_COUNT];
+ gint16 pointer_data[RETRO_POINTER_ID_COUNT];
+} RetroControllerStateData;
+
+typedef struct {
+ sem_t semaphore;
+ RetroControllerStateData data;
+} RetroControllerStateSharedData;
+
+struct _RetroControllerState
+{
+ GObject parent_instance;
+
+ gint fd;
+ RetroControllerStateSharedData *shared_data;
+#ifdef RETRO_RUNNER_COMPILATION
+ RetroControllerStateData snapshot;
+#endif
+};
+
+G_DEFINE_TYPE (RetroControllerState, retro_controller_state, G_TYPE_OBJECT)
+
+enum {
+ PROP_0,
+ PROP_FD,
+ N_PROPS,
+};
+
+static GParamSpec *properties [N_PROPS];
+
+static gint16 *
+get_data_for_type (RetroControllerStateData *data,
+ RetroControllerType type)
+{
+ switch (type) {
+ case RETRO_CONTROLLER_TYPE_JOYPAD:
+ return data->joypad_data;
+
+ case RETRO_CONTROLLER_TYPE_MOUSE:
+ return data->mouse_data;
+
+ case RETRO_CONTROLLER_TYPE_KEYBOARD:
+ return data->keyboard_data;
+
+ case RETRO_CONTROLLER_TYPE_LIGHTGUN:
+ return data->lightgun_data;
+
+ case RETRO_CONTROLLER_TYPE_ANALOG:
+ return data->analog_data;
+
+ case RETRO_CONTROLLER_TYPE_POINTER:
+ return data->pointer_data;
+
+ case RETRO_CONTROLLER_TYPE_NONE:
+ default:
+ g_assert_not_reached ();
+ }
+}
+
+static void
+retro_controller_state_constructed (GObject *object)
+{
+ RetroControllerState *self = RETRO_CONTROLLER_STATE (object);
+
+ G_OBJECT_CLASS (retro_controller_state_parent_class)->constructed (object);
+
+ if (ftruncate (self->fd, sizeof (RetroControllerStateSharedData)) != 0)
+ g_critical ("Couldn't truncate controller data: %s", g_strerror (errno));
+
+ self->shared_data = mmap (NULL, sizeof (RetroControllerStateSharedData),
+ PROT_READ | PROT_WRITE, MAP_SHARED, self->fd, 0);
+
+#ifndef RETRO_RUNNER_COMPILATION
+ if (sem_init (&self->shared_data->semaphore, 1, 1) != 0)
+ g_critical ("Couldn't init semaphore: %s", g_strerror (errno));
+#endif
+}
+
+static void
+retro_controller_state_finalize (GObject *object)
+{
+ RetroControllerState *self = (RetroControllerState *)object;
+
+#ifndef RETRO_RUNNER_COMPILATION
+ if (sem_destroy (&self->shared_data->semaphore) != 0)
+ g_critical ("Couldn't destroy semaphore: %s", g_strerror (errno));
+#endif
+
+ if (self->shared_data) {
+ munmap (self->shared_data, sizeof (RetroControllerStateSharedData));
+ self->shared_data = NULL;
+ }
+
+ close (self->fd);
+
+ G_OBJECT_CLASS (retro_controller_state_parent_class)->finalize (object);
+}
+
+static void
+retro_controller_state_get_property (GObject *object,
+ guint prop_id,
+ GValue *value,
+ GParamSpec *pspec)
+{
+ RetroControllerState *self = RETRO_CONTROLLER_STATE (object);
+
+ switch (prop_id) {
+ case PROP_FD:
+ g_value_set_int (value, self->fd);
+
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+
+ break;
+ }
+}
+
+static void
+retro_controller_state_set_property (GObject *object,
+ guint prop_id,
+ const GValue *value,
+ GParamSpec *pspec)
+{
+ RetroControllerState *self = RETRO_CONTROLLER_STATE (object);
+
+ switch (prop_id) {
+ case PROP_FD:
+ self->fd = g_value_get_int (value);
+
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+
+ break;
+ }
+}
+
+static void
+retro_controller_state_class_init (RetroControllerStateClass *klass)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (klass);
+
+ object_class->constructed = retro_controller_state_constructed;
+ object_class->finalize = retro_controller_state_finalize;
+ object_class->get_property = retro_controller_state_get_property;
+ object_class->set_property = retro_controller_state_set_property;
+
+ properties[PROP_FD] =
+ g_param_spec_int ("fd",
+ "File descriptor",
+ "The file descriptor backing shared memory.",
+ -1,
+ G_MAXINT,
+ -1,
+ G_PARAM_READWRITE |
+ G_PARAM_CONSTRUCT_ONLY |
+ G_PARAM_STATIC_STRINGS);
+
+ g_object_class_install_properties (G_OBJECT_CLASS (klass), N_PROPS, properties);
+}
+
+static void
+retro_controller_state_init (RetroControllerState *self)
+{
+}
+
+RetroControllerState *
+retro_controller_state_new (gint fd)
+{
+ g_return_val_if_fail (fd >= 0, NULL);
+
+ return g_object_new (RETRO_TYPE_CONTROLLER_STATE, "fd", fd, NULL);
+}
+
+gint
+retro_controller_state_get_fd (RetroControllerState *self)
+{
+ g_return_val_if_fail (RETRO_IS_CONTROLLER_STATE (self), 0);
+
+ return self->fd;
+}
+
+void
+retro_controller_state_lock (RetroControllerState *self)
+{
+ g_return_if_fail (RETRO_IS_CONTROLLER_STATE (self));
+
+ if (sem_wait (&self->shared_data->semaphore) != 0)
+ g_critical ("Couldn't lock: %s", g_strerror (errno));
+}
+
+void
+retro_controller_state_unlock (RetroControllerState *self)
+{
+ g_return_if_fail (RETRO_IS_CONTROLLER_STATE (self));
+
+ if (sem_post (&self->shared_data->semaphore) != 0)
+ g_critical ("Couldn't unlock: %s", g_strerror (errno));
+}
+
+#ifdef RETRO_RUNNER_COMPILATION
+
+gboolean
+retro_controller_state_has_type (RetroControllerState *self,
+ RetroControllerType type)
+{
+ gint64 available_types;
+
+ g_return_val_if_fail (RETRO_IS_CONTROLLER_STATE (self), FALSE);
+ g_return_val_if_fail (type > RETRO_CONTROLLER_TYPE_NONE, FALSE);
+ g_return_val_if_fail (type < RETRO_CONTROLLER_TYPE_COUNT, FALSE);
+
+ available_types = self->snapshot.available_types;
+
+ return (available_types & (1 << type)) > 0;
+}
+
+gint16
+retro_controller_state_get_input (RetroControllerState *self,
+ RetroInput *input)
+{
+ RetroControllerType type;
+ guint id, index, stride;
+ gint16 *data;
+
+ g_return_val_if_fail (RETRO_IS_CONTROLLER_STATE (self), 0);
+ g_return_val_if_fail (input != NULL, 0);
+
+ type = input->any.type;
+ id = input->any.id;
+ index = input->any.index;
+
+ stride = retro_controller_type_get_id_count (type);
+
+ if (id > stride || index > retro_controller_type_get_index_count (type))
+ return 0;
+
+ data = get_data_for_type (&self->snapshot, type);
+
+ return data[index * stride + id];
+}
+
+gboolean
+retro_controller_state_get_supports_rumble (RetroControllerState *self)
+{
+ g_return_val_if_fail (RETRO_IS_CONTROLLER_STATE (self), FALSE);
+
+ return self->snapshot.supports_rumble;
+}
+
+void
+retro_controller_state_snapshot (RetroControllerState *self)
+{
+ g_return_if_fail (RETRO_IS_CONTROLLER_STATE (self));
+
+ if (!self->shared_data->data.is_dirty)
+ return;
+
+ self->shared_data->data.is_dirty = FALSE;
+
+ memcpy (&self->snapshot, &self->shared_data->data, sizeof (RetroControllerStateData));
+}
+
+#else
+
+void
+retro_controller_state_set_for_type (RetroControllerState *self,
+ RetroControllerType type,
+ gint16 *state,
+ gsize n_items)
+{
+ gint16 *data;
+
+ g_return_if_fail (RETRO_IS_CONTROLLER_STATE (self));
+ g_return_if_fail (state != NULL);
+
+ data = get_data_for_type (&self->shared_data->data, type);
+
+ memcpy (data, state, n_items * sizeof (gint16));
+ self->shared_data->data.available_types |= (1 << type);
+ self->shared_data->data.is_dirty = TRUE;
+}
+
+static gsize
+get_data_size_for_type (RetroControllerType type)
+{
+ return retro_controller_type_get_id_count (type) *
+ retro_controller_type_get_index_count (type);
+}
+
+void
+retro_controller_state_clear_type (RetroControllerState *self,
+ RetroControllerType type)
+{
+ gint16 *data;
+
+ g_return_if_fail (RETRO_IS_CONTROLLER_STATE (self));
+
+ data = get_data_for_type (&self->shared_data->data, type);
+ memset (data, 0, get_data_size_for_type (type) * sizeof (gint16));
+
+ self->shared_data->data.available_types &= ~(1 << type);
+ self->shared_data->data.is_dirty = TRUE;
+}
+
+void
+retro_controller_state_set_supports_rumble (RetroControllerState *self,
+ gboolean supports_rumble)
+{
+ g_return_if_fail (RETRO_IS_CONTROLLER_STATE (self));
+
+ self->shared_data->data.supports_rumble = supports_rumble;
+}
+
+#endif
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]