[retro-gtk/wip/aplazas/0.13: 16/22] Port CoreView to C



commit 680456f37b6c76f5fa0df9e0a97ef1ca186514ff
Author: Adrien Plazas <kekun plazas laposte net>
Date:   Tue Sep 5 12:17:41 2017 +0200

    Port CoreView to C

 demos/retro-demo.c                       |    1 +
 retro-gtk/Makefile.am                    |    6 +-
 retro-gtk/core.vala                      |  132 +-------
 retro-gtk/retro-core-view-extern.c       |  386 ------------------
 retro-gtk/retro-core-view-input-device.h |    1 +
 retro-gtk/retro-core-view.c              |  624 ++++++++++++++++++++++++++++++
 retro-gtk/retro-core-view.h              |   40 ++
 retro-gtk/retro-core-view.vala           |   82 ----
 retro-gtk/retro-gtk.h                    |    1 +
 9 files changed, 671 insertions(+), 602 deletions(-)
---
diff --git a/demos/retro-demo.c b/demos/retro-demo.c
index 8093da8..0cf7ccc 100644
--- a/demos/retro-demo.c
+++ b/demos/retro-demo.c
@@ -1,6 +1,7 @@
 // This file is part of retro-gtk. License: GPL-3.0+.
 
 #include <retro-gtk/retro-gtk.h>
+#include <retro-gtk/retro-core-view.h>
 
 #define RETRO_TYPE_DEMO_APPLICATION (retro_demo_application_get_type())
 
diff --git a/retro-gtk/Makefile.am b/retro-gtk/Makefile.am
index 0769308..6230ed5 100644
--- a/retro-gtk/Makefile.am
+++ b/retro-gtk/Makefile.am
@@ -45,8 +45,7 @@ libretro_gtk_la_SOURCES = \
        retro.vala \
        retro-core-descriptor.vala \
        retro-core-descriptor-error.vala \
-       retro-core-view.vala \
-       retro-core-view-extern.c \
+       retro-core-view.c \
        retro-core-view-input-device.c \
        retro-game-info.c \
        retro-input-descriptor.c \
@@ -70,7 +69,7 @@ video/retro-video-converter.c: retro-gtk-internal.h
 
 retro-core.c: retro-gtk-internal.h
 
-retro-core-view-extern.c: retro-gtk-internal.h
+retro-core-view.c: retro-gtk-internal.h
 
 retro-core-view-input-device.c: retro-gtk-internal.h
 
@@ -108,6 +107,7 @@ retro_gtkincludedir = $(includedir)/retro-gtk-0.12
 retro_gtkinclude_HEADERS = \
        retro-analog-id.h \
        retro-analog-index.h \
+       retro-core-view.h \
        retro-device-type.h \
        retro-gtk.h \
        retro-gtk-vala.h \
diff --git a/retro-gtk/core.vala b/retro-gtk/core.vala
index e898f2f..5da72d5 100644
--- a/retro-gtk/core.vala
+++ b/retro-gtk/core.vala
@@ -2,82 +2,27 @@
 
 namespace Retro {
 
-/**
- * Handles a Libretro module.
- */
 public class Core : Object {
        public signal void video_output (uint8[] data, uint width, uint height, size_t pitch, PixelFormat 
pixel_format, float aspect_ratio);
        public signal void audio_output (int16[] frames, double sample_rate);
        public signal void log (string log_domain, LogLevelFlags log_level, string message);
 
-       /**
-        * Stores the current Core instance in a stack.
-        *
-        * Stores the current instance of Core in a thread local global stack.
-        * It allows to know wich Core a callback is related to.
-        *
-        * Must be called before any call to a function from the module.
-        */
        internal extern void push_cb_data ();
-
-       /**
-        * Removes the Core at the head of the stack.
-        *
-        * Must be called after any call to {@link push_cb_data()}.
-        */
        internal extern static void pop_cb_data ();
-
        internal extern static unowned Core get_cb_data ();
 
        private extern uint get_api_version_real ();
-       /**
-        * The version of Libretro used by the module.
-        *
-        * Can be compared with {@link API_VERSION} to validate ABI
-        * compatibility.
-        */
        public uint api_version {
                get { return get_api_version_real (); }
        }
 
-       /**
-        * The file name of the module.
-        */
        public string file_name { internal set; get; }
-
-       /**
-        * The directory the core will use to look for for additional data.
-        */
        public string system_directory { set; get; default = "."; }
-
-       /**
-        * The absolute path to the source module file.
-        */
        public string libretro_path { set; get; default = "."; }
-
-       /**
-        * The directory the core will use to look for for additional assets.
-        */
        public string content_directory { set; get; default = "."; }
-
-       /**
-        * The directory the core will use to save user data.
-        */
        public string save_directory { set; get; default = "."; }
-
-       /**
-        * Whether or not the a game is loaded.
-        */
        public bool is_initiated { internal set; get; default = false; }
-
-       /**
-        * Whether or not the a game is loaded.
-        */
        public bool game_loaded { internal set; get; default = false; }
-
-       /**
-        * Whether or not the core supports games.
-        */
        public bool support_no_game { internal set; get; default = false; }
 
        internal double _frames_per_second;
@@ -89,11 +34,7 @@ public class Core : Object {
        private ulong input_controller_connected_id;
        private ulong input_controller_disconnected_id;
        private ulong input_key_event_id;
-       /**
-        * The input interface.
-        *
-        * It must be set before {@link init} is called.
-        */
+
        public Input input_interface {
                get { return _input_interface; }
                construct set {
@@ -120,36 +61,16 @@ public class Core : Object {
                }
        }
 
-       /**
-        * The rumble interface.
-        *
-        * Optional.
-        * If set, it must be set before {@link init} is called.
-        */
        public Rumble rumble_interface { set; get; }
 
-       /**
-        * Asks the frontend to shut down.
-        */
        public signal bool shutdown ();
 
-       /**
-        * Asks the frontend to display a message for an amount of frames.
-        */
        public signal bool message (string message, uint frames);
 
        internal Variables variables_interface;
 
        internal void *environment_internal;
 
-       /**
-        * Creates a Core from the file name of a Libretro implementation.
-        *
-        * The file must be a dynamically loadable shared object implementing the
-        * same version of the Libretro API as Retro.
-        *
-        * @param file_name the file name of the Libretro implementation to load
-        */
        public Core (string file_name) {
                constructor (file_name);
        }
@@ -161,69 +82,18 @@ public class Core : Object {
        private extern void constructor (string file_name);
        private extern void destructor ();
 
-       /**
-        * Initializes the module.
-        *
-        * Must be called before loading a game and running the core.
-        */
        public extern void init () throws Error;
-
        public extern void set_medias ([CCode (array_null_terminated = true, array_length = false)] string[] 
uris);
-
        public extern void set_current_media (uint media_index) throws Error;
-
        public extern void set_controller_port_device (uint port, DeviceType device);
-
-       /**
-        * Resets the current game.
-        */
        public extern void reset ();
-
-       /**
-        * Runs the game for one video frame.
-        *
-        * The callbacks must be set and the core must be initialized before
-        * running the core.
-        *
-        * During {@link run}, the input_poll callback will be called
-        * at least once.
-        *
-        * If a frame is not rendered for reasons where a game "dropped" a frame,
-        * this still counts as a frame, and {@link run} will explicitly dupe a
-        * frame if the can_dupe property of {@link video_interface} is set to true.
-        * In this case, the video callback can take a null argument for data.
-        */
        public extern void run ();
-
        public extern bool supports_serialization ();
-
        [CCode (array_length_type = "gsize")]
        public extern uint8[] serialize_state () throws Error;
-
        public extern void deserialize_state ([CCode (array_length_type = "gsize")] uint8[] data) throws 
Error;
-
-       /**
-        * Gets the size of a region of memory.
-        *
-        * @param id the region of memory
-        * @return the size of the region of memory
-        */
        public extern size_t get_memory_size (MemoryType id);
-
-       /**
-        * Gets a region of memory.
-        *
-        * @param id the region of memory
-        * @return the region of memory
-        */
        public extern uint8[] get_memory (MemoryType id);
-
-       /**
-        * Sets a region of memory.
-        *
-        * @param id the region of memory
-        * @param data the data to write in the memory region
-        */
        public extern void set_memory (MemoryType id, uint8[] data);
 
        private extern void init_input ();
diff --git a/retro-gtk/retro-core-view-input-device.h b/retro-gtk/retro-core-view-input-device.h
index 4520d40..fff4a3c 100644
--- a/retro-gtk/retro-core-view-input-device.h
+++ b/retro-gtk/retro-core-view-input-device.h
@@ -4,6 +4,7 @@
 #define RETRO_CORE_VIEW_INPUT_DEVICE_H
 
 #include <glib-object.h>
+#include "retro-core-view.h"
 #include "retro-device-type.h"
 #include "retro-gtk-internal.h"
 
diff --git a/retro-gtk/retro-core-view.c b/retro-gtk/retro-core-view.c
new file mode 100644
index 0000000..99ed6fc
--- /dev/null
+++ b/retro-gtk/retro-core-view.c
@@ -0,0 +1,624 @@
+// This file is part of retro-gtk. License: GPL-3.0+.
+
+#include <linux/input-event-codes.h>
+#include "retro-gtk-internal.h"
+#include "retro-core-view.h"
+#include "retro-core-view-input-device.h"
+#include "retro-joypad-id.h"
+#include "retro-mouse-id.h"
+#include "retro-pointer-id.h"
+
+static guint16 DEFAULT_KEY_JOYPAD_BUTTON_MAPPING[RETRO_JOYPAD_ID_COUNT] = {
+  KEY_S,
+  KEY_A,
+  KEY_BACKSPACE,
+  KEY_ENTER,
+  KEY_UP,
+  KEY_DOWN,
+  KEY_LEFT,
+  KEY_RIGHT,
+  KEY_D,
+  KEY_W,
+  KEY_Q,
+  KEY_E,
+  KEY_Z,
+  KEY_C,
+  KEY_1,
+  KEY_3,
+};
+
+struct _RetroCoreView
+{
+  GtkEventBox parent_instance;
+  RetroCore *core;
+  RetroCairoDisplay *display;
+  gboolean can_grab_pointer;
+  gboolean snap_pointer_to_borders;
+  GHashTable *key_state;
+  GHashTable *mouse_button_state;
+  GdkScreen *grabbed_screen;
+  GdkDevice *grabbed_device;
+  gdouble mouse_x_delta;
+  gdouble mouse_y_delta;
+  gint screen_center_x;
+  gint screen_center_y;
+  gint position_on_grab_x;
+  gint position_on_grab_y;
+  gboolean pointer_is_on_display;
+  gdouble pointer_x;
+  gdouble pointer_y;
+};
+
+G_DEFINE_TYPE (RetroCoreView, retro_core_view, GTK_TYPE_EVENT_BOX)
+
+enum {
+  PROP_CAN_GRAB_POINTER = 1,
+  PROP_SNAP_POINTER_TO_BORDERS,
+  N_PROPS
+};
+
+static GParamSpec *properties [N_PROPS];
+
+/* Private */
+
+static void
+set_input_pressed (GHashTable *table,
+                   guint       input)
+{
+  static gboolean value = TRUE;
+
+  if (g_hash_table_contains (table, &input))
+    g_hash_table_replace (table,
+                          g_memdup (&input, sizeof (guint)),
+                          g_memdup (&value, sizeof (gboolean)));
+  else
+    g_hash_table_insert (table,
+                         g_memdup (&input, sizeof (guint)),
+                         g_memdup (&value, sizeof (gboolean)));
+}
+
+static void
+set_input_released (GHashTable *table,
+                    guint       input)
+{
+  g_hash_table_remove (table, &input);
+}
+
+static gboolean
+get_input_state (GHashTable *table,
+                 guint       input)
+{
+  return g_hash_table_contains (table, &input);
+}
+
+static gint16
+axis_to_retro_axis (gdouble value)
+{
+  if (value <= -1.0)
+    return -G_MAXINT16;
+
+  if (value >= 1.0)
+    return G_MAXINT16;
+
+  return (gint16) (value * G_MAXINT16);
+}
+
+static void
+recenter_pointer (RetroCoreView *self)
+{
+  gdk_device_warp (self->grabbed_device, self->grabbed_screen,
+                   self->screen_center_x, self->screen_center_y);
+}
+
+static gboolean
+retro_core_view_get_is_pointer_grabbed (RetroCoreView *self)
+{
+  g_return_val_if_fail (self != NULL, FALSE);
+
+  return self->grabbed_device != NULL;
+}
+
+static void
+retro_core_view_grab (RetroCoreView *self,
+                      GdkDevice     *device,
+                      GdkWindow     *window,
+                      GdkEvent      *event)
+{
+  GdkSeat *seat;
+  GdkDisplay *display;
+  GdkCursor *cursor;
+  GdkScreen *screen = NULL;
+  GdkMonitor *monitor;
+  GdkRectangle monitor_geometry;
+
+  g_return_if_fail (self != NULL);
+  g_return_if_fail (device != NULL);
+  g_return_if_fail (window != NULL);
+  g_return_if_fail (event != NULL);
+
+  if (self->grabbed_device != NULL)
+    g_object_unref (self->grabbed_device);
+
+  if (self->grabbed_screen != NULL)
+    g_object_unref (self->grabbed_screen);
+
+  self->grabbed_device = g_object_ref (device);
+  seat = gdk_device_get_seat (device);
+  display = gdk_device_get_display (device);
+  cursor = gdk_cursor_new_for_display (display, GDK_BLANK_CURSOR);
+  gdk_seat_grab (seat, window, GDK_SEAT_CAPABILITY_ALL_POINTING, FALSE, cursor, event, NULL, NULL);
+  monitor = gdk_display_get_monitor_at_window (display, window);
+  gdk_monitor_get_geometry (monitor, &monitor_geometry);
+
+  gdk_device_get_position (device, &screen, &self->position_on_grab_x, &self->position_on_grab_y);
+  self->grabbed_screen = g_object_ref (screen);
+  self->screen_center_x = monitor_geometry.x + monitor_geometry.width / 2;
+  self->screen_center_y = monitor_geometry.y + monitor_geometry.height / 2;
+  self->mouse_x_delta = 0;
+  self->mouse_y_delta = 0;
+
+  recenter_pointer (self);
+
+  g_object_unref (cursor);
+}
+
+static void
+retro_core_view_ungrab (RetroCoreView *self)
+{
+  GdkSeat *seat;
+
+  g_return_if_fail (self != NULL);
+  g_return_if_fail (self->grabbed_device != NULL);
+
+  seat = gdk_device_get_seat (self->grabbed_device);
+  gdk_seat_ungrab (seat);
+  gdk_device_warp (self->grabbed_device, self->grabbed_screen,
+                   self->position_on_grab_x, self->position_on_grab_y);
+
+  g_clear_object (&self->grabbed_device);
+  g_clear_object (&self->grabbed_screen);
+}
+
+static gboolean
+retro_core_view_on_key_press_event (GtkWidget   *source,
+                                    GdkEventKey *event,
+                                    gpointer     data)
+{
+  RetroCoreView *self = RETRO_CORE_VIEW (data);
+
+  g_return_val_if_fail (self != NULL, FALSE);
+  g_return_val_if_fail (event != NULL, FALSE);
+
+  if (event->keyval == GDK_KEY_Escape &&
+      (event->state & GDK_CONTROL_MASK) &&
+      retro_core_view_get_is_pointer_grabbed (self))
+    retro_core_view_ungrab (self);
+
+  set_input_pressed (self->key_state, event->hardware_keycode);
+
+  return FALSE;
+}
+
+static gboolean
+retro_core_view_on_key_release_event (GtkWidget   *source,
+                                      GdkEventKey *event,
+                                      gpointer     data)
+{
+  RetroCoreView *self = RETRO_CORE_VIEW (data);
+
+  g_return_val_if_fail (self != NULL, FALSE);
+  g_return_val_if_fail (event != NULL, FALSE);
+
+  set_input_released (self->key_state, event->hardware_keycode);
+
+  return FALSE;
+}
+
+static gboolean
+retro_core_view_on_button_press_event (GtkWidget      *source,
+                                       GdkEventButton *event,
+                                       gpointer        data)
+{
+  RetroCoreView *self = RETRO_CORE_VIEW (data);
+
+  g_return_val_if_fail (self != NULL, FALSE);
+  g_return_val_if_fail (event != NULL, FALSE);
+
+  if (retro_core_view_get_can_grab_pointer (self)) {
+    if (retro_core_view_get_is_pointer_grabbed (self))
+      set_input_pressed (self->mouse_button_state, event->button);
+    else
+      retro_core_view_grab (self,
+                            event->device,
+                            event->window,
+                            (GdkEvent *) event);
+  }
+  else {
+    set_input_pressed (self->mouse_button_state, event->button);
+    self->pointer_is_on_display =
+      retro_cairo_display_get_coordinates_on_display (self->display,
+                                                      event->x,
+                                                      event->y,
+                                                      &self->pointer_x,
+                                                      &self->pointer_y);
+  }
+
+  return FALSE;
+}
+
+static gboolean
+retro_core_view_on_button_release_event (GtkWidget      *source,
+                                         GdkEventButton *event,
+                                         gpointer        data)
+{
+  RetroCoreView *self = RETRO_CORE_VIEW (data);
+
+  g_return_val_if_fail (self != NULL, FALSE);
+  g_return_val_if_fail (event != NULL, FALSE);
+
+  set_input_released (self->mouse_button_state, event->button);
+
+  return FALSE;
+}
+
+static gboolean
+retro_core_view_on_focus_out_event (GtkWidget     *source,
+                                    GdkEventFocus *event,
+                                    gpointer       data)
+{
+  RetroCoreView *self = RETRO_CORE_VIEW (data);
+
+  g_return_val_if_fail (self != NULL, FALSE);
+  g_return_val_if_fail (event != NULL, FALSE);
+
+  if (retro_core_view_get_is_pointer_grabbed (self))
+    retro_core_view_ungrab (self);
+
+  return FALSE;
+}
+
+static gboolean
+retro_core_view_on_motion_notify_event (GtkWidget      *source,
+                                        GdkEventMotion *event,
+                                        gpointer        data)
+{
+  RetroCoreView *self = RETRO_CORE_VIEW (data);
+
+  g_return_val_if_fail (self != NULL, FALSE);
+  g_return_val_if_fail (event != NULL, FALSE);
+
+  if (retro_core_view_get_can_grab_pointer (self)) {
+    if (retro_core_view_get_is_pointer_grabbed (self) &&
+        event->device == self->grabbed_device) {
+      self->mouse_x_delta += event->x_root - (double) self->screen_center_x;
+      self->mouse_y_delta += event->y_root - (double) self->screen_center_y;
+
+      recenter_pointer (self);
+    }
+  }
+  else {
+    self->pointer_is_on_display =
+      retro_cairo_display_get_coordinates_on_display (self->display,
+                                                      event->x,
+                                                      event->y,
+                                                      &self->pointer_x,
+                                                      &self->pointer_y);
+
+  }
+
+  return FALSE;
+}
+
+static gboolean
+retro_core_view_get_key_state (RetroCoreView *self,
+                               guint16        hardware_keycode)
+{
+  g_return_val_if_fail (self != NULL, FALSE);
+
+  return get_input_state (self->key_state, hardware_keycode);
+}
+
+static gboolean
+retro_core_view_get_joypad_button_state (RetroCoreView *self,
+                                         RetroJoypadId  button)
+{
+  guint16 hardware_keycode;
+
+  g_return_val_if_fail (self != NULL, FALSE);
+
+  if (button >= RETRO_JOYPAD_ID_COUNT)
+    return 0;
+
+  // GDK adds 8 to the Linux input event codes to create the hardware keycode.
+  hardware_keycode = DEFAULT_KEY_JOYPAD_BUTTON_MAPPING[button] + 8;
+
+  return retro_core_view_get_key_state (self, hardware_keycode);
+}
+
+static gboolean
+retro_core_view_get_mouse_button_state (RetroCoreView *self,
+                                        guint16        button)
+{
+  g_return_val_if_fail (self != NULL, FALSE);
+
+  return get_input_state (self->mouse_button_state, button);
+}
+
+static void
+retro_core_view_finalize (GObject *object)
+{
+  RetroCoreView *self = RETRO_CORE_VIEW (object);
+
+  g_clear_object (&self->core);
+  g_object_unref (self->display);
+  g_hash_table_unref (self->key_state);
+  g_hash_table_unref (self->mouse_button_state);
+  g_clear_object (&self->grabbed_screen);
+  g_clear_object (&self->grabbed_device);
+
+  G_OBJECT_CLASS (retro_core_view_parent_class)->finalize (object);
+}
+
+static void
+retro_core_view_get_property (GObject    *object,
+                              guint       prop_id,
+                              GValue     *value,
+                              GParamSpec *pspec)
+{
+  RetroCoreView *self = RETRO_CORE_VIEW (object);
+
+  switch (prop_id) {
+  case PROP_CAN_GRAB_POINTER:
+    g_value_set_boolean (value, retro_core_view_get_can_grab_pointer (self));
+
+    break;
+  case PROP_SNAP_POINTER_TO_BORDERS:
+    g_value_set_boolean (value, retro_core_view_get_snap_pointer_to_borders (self));
+
+    break;
+  default:
+    G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+
+    break;
+  }
+}
+
+static void
+retro_core_view_set_property (GObject      *object,
+                              guint         prop_id,
+                              const GValue *value,
+                              GParamSpec   *pspec)
+{
+  RetroCoreView *self = RETRO_CORE_VIEW (object);
+
+  switch (prop_id) {
+  case PROP_CAN_GRAB_POINTER:
+    retro_core_view_set_can_grab_pointer (self, g_value_get_boolean (value));
+
+    break;
+  case PROP_SNAP_POINTER_TO_BORDERS:
+    retro_core_view_set_snap_pointer_to_borders (self, g_value_get_boolean (value));
+
+    break;
+  default:
+    G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+
+    break;
+  }
+}
+
+static void
+retro_core_view_class_init (RetroCoreViewClass *klass)
+{
+  GObjectClass *object_class = G_OBJECT_CLASS (klass);
+
+  object_class->finalize = retro_core_view_finalize;
+  object_class->get_property = retro_core_view_get_property;
+  object_class->set_property = retro_core_view_set_property;
+
+  properties[PROP_CAN_GRAB_POINTER] =
+    g_param_spec_boolean ("can-grab-pointer",
+                          "Can grab pointer",
+                          "Whether the pointer can be grabbed",
+                          FALSE,
+                          G_PARAM_READWRITE |
+                          G_PARAM_STATIC_NAME |
+                          G_PARAM_STATIC_NICK |
+                          G_PARAM_STATIC_BLURB);
+  properties[PROP_SNAP_POINTER_TO_BORDERS] =
+    g_param_spec_boolean ("snap-pointer-to-borders",
+                          "Snap pointer to borders",
+                          "Wheter the pointer is considered to be on the border when it is out of the 
display",
+                          FALSE,
+                          G_PARAM_READWRITE |
+                          G_PARAM_STATIC_NAME |
+                          G_PARAM_STATIC_NICK |
+                          G_PARAM_STATIC_BLURB);
+  g_object_class_install_property (G_OBJECT_CLASS (klass), PROP_CAN_GRAB_POINTER, 
properties[PROP_CAN_GRAB_POINTER]);
+  g_object_class_install_property (G_OBJECT_CLASS (klass), PROP_SNAP_POINTER_TO_BORDERS, 
properties[PROP_SNAP_POINTER_TO_BORDERS]);
+}
+
+RetroCoreView* retro_core_view_construct (GType object_type) {
+  RetroCoreView * self = NULL;
+  self = (RetroCoreView*) g_object_new (object_type, NULL);
+  return self;
+}
+
+static void
+retro_core_view_init (RetroCoreView *self)
+{
+  g_object_set ((GtkWidget*) self, "can-focus", TRUE, NULL);
+
+  self->display = g_object_ref_sink (retro_cairo_display_new ());
+  gtk_widget_set_visible (GTK_WIDGET (self->display), TRUE);
+  g_object_set (GTK_WIDGET (self->display), "can-focus", FALSE, NULL);
+  gtk_container_add (GTK_CONTAINER (self), GTK_WIDGET (self->display));
+
+  self->key_state = g_hash_table_new_full (g_int_hash, g_int_equal, g_free, g_free);
+  self->mouse_button_state = g_hash_table_new_full (g_int_hash, g_int_equal, g_free, g_free);
+
+  g_signal_connect_object (self, "key-press-event", (GCallback) retro_core_view_on_key_press_event, self, 0);
+  g_signal_connect_object (self, "key-release-event", (GCallback) retro_core_view_on_key_release_event, 
self, 0);
+  g_signal_connect_object (self, "button-press-event", (GCallback) retro_core_view_on_button_press_event, 
self, 0);
+  g_signal_connect_object (self, "button-release-event", (GCallback) 
retro_core_view_on_button_release_event, self, 0);
+  g_signal_connect_object (self, "focus-out-event", (GCallback) retro_core_view_on_focus_out_event, self, 0);
+  g_signal_connect_object (self, "motion-notify-event", (GCallback) retro_core_view_on_motion_notify_event, 
self, 0);
+}
+
+/* Public */
+
+void
+retro_core_view_set_core (RetroCoreView *self,
+                          RetroCore *core)
+{
+  g_return_if_fail (self != NULL);
+
+  if (self->core == core)
+    return;
+
+  if (self->core != NULL) {
+    g_clear_object (&self->core);
+    retro_cairo_display_set_core (self->display, NULL);
+  }
+
+  if (core != NULL) {
+    self->core = g_object_ref (core);
+    retro_cairo_display_set_core (self->display, core);
+  }
+}
+
+RetroCairoDisplay *
+retro_core_view_get_display (RetroCoreView *self)
+{
+  g_return_val_if_fail (self != NULL, NULL);
+
+  return self->display;
+}
+
+RetroInputDevice *
+retro_core_view_as_input_device (RetroCoreView   *self,
+                                 RetroDeviceType  device_type)
+{
+  g_return_val_if_fail (self != NULL, NULL);
+
+  return RETRO_INPUT_DEVICE (retro_core_view_input_device_new (self, device_type));
+}
+
+gint16
+retro_core_view_get_input_state (RetroCoreView   *self,
+                                 RetroDeviceType  device,
+                                 guint            index,
+                                 guint            id)
+{
+  gint16 result;
+
+  g_return_val_if_fail (self != NULL, 0);
+
+  switch (device) {
+  case RETRO_DEVICE_TYPE_JOYPAD:
+    if (id >= RETRO_JOYPAD_ID_COUNT)
+      return 0;
+
+    return retro_core_view_get_joypad_button_state (self, id) ? G_MAXINT16 : 0;
+  case RETRO_DEVICE_TYPE_MOUSE:
+    switch (id) {
+    case RETRO_MOUSE_ID_X:
+      result = (gint16) self->mouse_x_delta;
+      self->mouse_x_delta = 0;
+
+      return result;
+    case RETRO_MOUSE_ID_Y:
+      result = self->mouse_y_delta;
+      self->mouse_y_delta = 0;
+
+      return result;
+    case RETRO_MOUSE_ID_LEFT:
+      return retro_core_view_get_mouse_button_state (self, 1) ? G_MAXINT16 : 0;
+    case RETRO_MOUSE_ID_RIGHT:
+      return retro_core_view_get_mouse_button_state (self, 3) ? G_MAXINT16 : 0;
+    default:
+      return 0;
+    }
+  case RETRO_DEVICE_TYPE_POINTER:
+    switch (id) {
+    case RETRO_POINTER_ID_X:
+      return axis_to_retro_axis (self->pointer_x);
+    case RETRO_POINTER_ID_Y:
+      return axis_to_retro_axis (self->pointer_y);
+    case RETRO_POINTER_ID_PRESSED:
+      if (!self->pointer_is_on_display ||
+          retro_core_view_get_snap_pointer_to_borders (self))
+        return 0;
+
+      return retro_core_view_get_mouse_button_state (self, 1) ? 1 : 0;
+    default:
+      return 0;
+    }
+  default:
+    return 0;
+  }
+}
+
+guint64
+retro_core_view_get_device_capabilities (RetroCoreView *self)
+{
+  g_return_val_if_fail (self != NULL, 0);
+
+  return 1 << RETRO_DEVICE_TYPE_JOYPAD |
+         1 << RETRO_DEVICE_TYPE_MOUSE |
+         1 << RETRO_DEVICE_TYPE_POINTER;
+}
+
+gboolean
+retro_core_view_get_can_grab_pointer (RetroCoreView *self)
+{
+  g_return_val_if_fail (self != NULL, FALSE);
+
+  return self->can_grab_pointer;
+}
+
+void
+retro_core_view_set_can_grab_pointer (RetroCoreView *self,
+                                      gboolean can_grab_pointer)
+{
+  g_return_if_fail (self != NULL);
+
+  if (self->can_grab_pointer == can_grab_pointer)
+    return;
+
+  self->can_grab_pointer = can_grab_pointer;
+
+  if (can_grab_pointer == FALSE &&
+      retro_core_view_get_is_pointer_grabbed (self))
+    retro_core_view_ungrab (self);
+
+  g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_CAN_GRAB_POINTER]);
+}
+
+gboolean
+retro_core_view_get_snap_pointer_to_borders (RetroCoreView *self)
+{
+  g_return_val_if_fail (self != NULL, FALSE);
+
+  return self->snap_pointer_to_borders;
+}
+
+void
+retro_core_view_set_snap_pointer_to_borders (RetroCoreView *self,
+                                             gboolean snap_pointer_to_borders)
+{
+  g_return_if_fail (self != NULL);
+
+  if (self->snap_pointer_to_borders == snap_pointer_to_borders)
+    return;
+
+  self->snap_pointer_to_borders = snap_pointer_to_borders;
+
+  g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_SNAP_POINTER_TO_BORDERS]);
+}
+
+RetroCoreView *
+retro_core_view_new (void)
+{
+  return g_object_new (RETRO_TYPE_CORE_VIEW, NULL);
+}
diff --git a/retro-gtk/retro-core-view.h b/retro-gtk/retro-core-view.h
new file mode 100644
index 0000000..bf26158
--- /dev/null
+++ b/retro-gtk/retro-core-view.h
@@ -0,0 +1,40 @@
+#ifndef RETRO_CORE_VIEW_H
+#define RETRO_CORE_VIEW_H
+
+#include <gtk/gtk.h>
+#include "retro-device-type.h"
+#include "retro-input-device.h"
+
+G_BEGIN_DECLS
+
+// FIXME Remove as soon as possible.
+typedef struct _RetroCairoDisplay RetroCairoDisplay;
+// FIXME Remove as soon as possible.
+typedef struct _RetroCore RetroCore;
+
+#define RETRO_TYPE_CORE_VIEW (retro_core_view_get_type())
+
+G_DECLARE_FINAL_TYPE (RetroCoreView, retro_core_view, RETRO, CORE_VIEW, GtkEventBox)
+
+RetroCoreView *retro_core_view_new (void);
+
+void retro_core_view_set_core (RetroCoreView *self,
+                               RetroCore *core);
+RetroCairoDisplay *retro_core_view_get_display (RetroCoreView *self);
+RetroInputDevice *retro_core_view_as_input_device (RetroCoreView *self,
+                                                   RetroDeviceType device_type);
+gint16 retro_core_view_get_input_state (RetroCoreView   *self,
+                                        RetroDeviceType  device,
+                                        guint            index,
+                                        guint            id);
+guint64 retro_core_view_get_device_capabilities (RetroCoreView *self);
+gboolean retro_core_view_get_can_grab_pointer (RetroCoreView *self);
+void retro_core_view_set_can_grab_pointer (RetroCoreView *self,
+                                           gboolean can_grab_pointer);
+gboolean retro_core_view_get_snap_pointer_to_borders (RetroCoreView *self);
+void retro_core_view_set_snap_pointer_to_borders (RetroCoreView *self,
+                                                  gboolean snap_pointer_to_borders);
+
+G_END_DECLS
+
+#endif /* RETRO_CORE_VIEW_H */
diff --git a/retro-gtk/retro-gtk.h b/retro-gtk/retro-gtk.h
index 281c1cf..8769435 100644
--- a/retro-gtk/retro-gtk.h
+++ b/retro-gtk/retro-gtk.h
@@ -5,6 +5,7 @@
 
 #include "retro-analog-id.h"
 #include "retro-analog-index.h"
+#include "retro-core-view.h"
 #include "retro-device-type.h"
 #include "retro-gtk-vala.h"
 #include "retro-input-descriptor.h"


[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]