gtk: implement GstNavigation interface
authorMatthew Waters <matthew@centricular.com>
Tue, 11 Aug 2015 11:34:59 +0000 (13:34 +0200)
committerMatthew Waters <matthew@centricular.com>
Tue, 11 Aug 2015 11:38:03 +0000 (13:38 +0200)
Now we can push key/mouse input into the pipeline for DVD use cases.

ext/gtk/gstgtkbasesink.c
ext/gtk/gtkgstbasewidget.c
ext/gtk/gtkgstbasewidget.h

index d53d77c..d0744a0 100644 (file)
@@ -56,6 +56,9 @@ static gboolean gst_gtk_base_sink_set_caps (GstBaseSink * bsink,
 static GstFlowReturn gst_gtk_base_sink_show_frame (GstVideoSink * bsink,
     GstBuffer * buf);
 
+static void
+gst_gtk_base_sink_navigation_interface_init (GstNavigationInterface * iface);
+
 enum
 {
   PROP_0,
@@ -67,7 +70,10 @@ enum
 
 #define gst_gtk_base_sink_parent_class parent_class
 G_DEFINE_ABSTRACT_TYPE_WITH_CODE (GstGtkBaseSink, gst_gtk_base_sink,
-    GST_TYPE_VIDEO_SINK, GST_DEBUG_CATEGORY_INIT (gst_debug_gtk_base_sink,
+    GST_TYPE_VIDEO_SINK,
+    G_IMPLEMENT_INTERFACE (GST_TYPE_NAVIGATION,
+        gst_gtk_base_sink_navigation_interface_init);
+    GST_DEBUG_CATEGORY_INIT (gst_debug_gtk_base_sink,
         "gtkbasesink", 0, "Gtk Video Sink base class"));
 
 static void
@@ -179,6 +185,10 @@ gst_gtk_base_sink_get_widget (GstGtkBaseSink * gtk_sink)
   g_signal_connect (gtk_sink->widget, "destroy",
       G_CALLBACK (widget_destroy_cb), gtk_sink);
 
+  /* back pointer */
+  gtk_gst_base_widget_set_element (GTK_GST_BASE_WIDGET (gtk_sink->widget),
+      GST_ELEMENT (gtk_sink));
+
   return gtk_sink->widget;
 }
 
@@ -230,6 +240,31 @@ gst_gtk_base_sink_set_property (GObject * object, guint prop_id,
   }
 }
 
+static void
+gst_gtk_base_sink_navigation_send_event (GstNavigation * navigation,
+    GstStructure * structure)
+{
+  GstGtkBaseSink *sink = GST_GTK_BASE_SINK (navigation);
+  GstEvent *event;
+  GstPad *pad;
+
+  event = gst_event_new_navigation (structure);
+  pad = gst_pad_get_peer (GST_VIDEO_SINK_PAD (sink));
+
+  GST_TRACE_OBJECT (sink, "navigation event %" GST_PTR_FORMAT, structure);
+
+  if (GST_IS_PAD (pad) && GST_IS_EVENT (event))
+    gst_pad_send_event (pad, event);
+
+  gst_object_unref (pad);
+}
+
+static void
+gst_gtk_base_sink_navigation_interface_init (GstNavigationInterface * iface)
+{
+  iface->send_event = gst_gtk_base_sink_navigation_send_event;
+}
+
 static gboolean
 gst_gtk_base_sink_start (GstBaseSink * bsink)
 {
index 0cfed24..4870fa4 100644 (file)
@@ -219,6 +219,85 @@ _queue_draw (GtkGstBaseWidget * widget)
   return G_SOURCE_REMOVE;
 }
 
+static const gchar *
+_gdk_key_to_navigation_string (guint keyval)
+{
+  /* TODO: expand */
+  switch (keyval) {
+#define KEY(key) case GDK_KEY_ ## key: return G_STRINGIFY(key)
+      KEY (Up);
+      KEY (Down);
+      KEY (Left);
+      KEY (Right);
+      KEY (Home);
+      KEY (End);
+#undef KEY
+    default:
+      return NULL;
+  }
+}
+
+static gboolean
+gtk_gst_base_widget_key_event (GtkWidget * widget, GdkEventKey * event)
+{
+  GtkGstBaseWidget *base_widget = GTK_GST_BASE_WIDGET (widget);
+  GstElement *element;
+
+  if ((element = g_weak_ref_get (&base_widget->element))) {
+    if (GST_IS_NAVIGATION (element)) {
+      const gchar *str = _gdk_key_to_navigation_string (event->keyval);
+      const gchar *key_type =
+          event->type == GDK_KEY_PRESS ? "key-press" : "key-release";
+
+      if (!str)
+        str = event->string;
+
+      gst_navigation_send_key_event (GST_NAVIGATION (element), key_type, str);
+    }
+    g_object_unref (element);
+  }
+
+  return TRUE;
+}
+
+static gboolean
+gtk_gst_base_widget_button_event (GtkWidget * widget, GdkEventButton * event)
+{
+  GtkGstBaseWidget *base_widget = GTK_GST_BASE_WIDGET (widget);
+  GstElement *element;
+
+  if ((element = g_weak_ref_get (&base_widget->element))) {
+    if (GST_IS_NAVIGATION (element)) {
+      const gchar *key_type =
+          event->type ==
+          GDK_BUTTON_PRESS ? "mouse-button-press" : "mouse-button-release";
+
+      gst_navigation_send_mouse_event (GST_NAVIGATION (element), key_type,
+          event->button, event->x, event->y);
+    }
+    g_object_unref (element);
+  }
+
+  return TRUE;
+}
+
+static gboolean
+gtk_gst_base_widget_motion_event (GtkWidget * widget, GdkEventMotion * event)
+{
+  GtkGstBaseWidget *base_widget = GTK_GST_BASE_WIDGET (widget);
+  GstElement *element;
+
+  if ((element = g_weak_ref_get (&base_widget->element))) {
+    if (GST_IS_NAVIGATION (element)) {
+      gst_navigation_send_mouse_event (GST_NAVIGATION (element), "motion-move",
+          0, event->x, event->y);
+    }
+    g_object_unref (element);
+  }
+
+  return TRUE;
+}
+
 void
 gtk_gst_base_widget_class_init (GtkGstBaseWidgetClass * klass)
 {
@@ -247,11 +326,18 @@ gtk_gst_base_widget_class_init (GtkGstBaseWidgetClass * klass)
 
   widget_klass->get_preferred_width = gtk_gst_base_widget_get_preferred_width;
   widget_klass->get_preferred_height = gtk_gst_base_widget_get_preferred_height;
+  widget_klass->key_press_event = gtk_gst_base_widget_key_event;
+  widget_klass->key_release_event = gtk_gst_base_widget_key_event;
+  widget_klass->button_press_event = gtk_gst_base_widget_button_event;
+  widget_klass->button_release_event = gtk_gst_base_widget_button_event;
+  widget_klass->motion_notify_event = gtk_gst_base_widget_motion_event;
 }
 
 void
 gtk_gst_base_widget_init (GtkGstBaseWidget * widget)
 {
+  int event_mask;
+
   widget->force_aspect_ratio = DEFAULT_FORCE_ASPECT_RATIO;
   widget->par_n = DEFAULT_PAR_N;
   widget->par_d = DEFAULT_PAR_D;
@@ -260,7 +346,17 @@ gtk_gst_base_widget_init (GtkGstBaseWidget * widget)
   gst_video_info_init (&widget->v_info);
   gst_video_info_init (&widget->pending_v_info);
 
+  g_weak_ref_init (&widget->element, NULL);
   g_mutex_init (&widget->lock);
+
+  gtk_widget_set_can_focus (GTK_WIDGET (widget), TRUE);
+  event_mask = gtk_widget_get_events (GTK_WIDGET (widget));
+  event_mask |= GDK_KEY_PRESS_MASK
+      | GDK_KEY_RELEASE_MASK
+      | GDK_BUTTON_PRESS_MASK
+      | GDK_BUTTON_RELEASE_MASK
+      | GDK_POINTER_MOTION_MASK | GDK_BUTTON_MOTION_MASK;
+  gtk_widget_set_events (GTK_WIDGET (widget), event_mask);
 }
 
 void
@@ -270,11 +366,19 @@ gtk_gst_base_widget_finalize (GObject * object)
 
   gst_buffer_replace (&widget->buffer, NULL);
   g_mutex_clear (&widget->lock);
+  g_weak_ref_clear (&widget->element);
 
   if (widget->draw_id)
     g_source_remove (widget->draw_id);
 }
 
+void
+gtk_gst_base_widget_set_element (GtkGstBaseWidget * widget,
+    GstElement * element)
+{
+  g_weak_ref_set (&widget->element, element);
+}
+
 gboolean
 gtk_gst_base_widget_set_format (GtkGstBaseWidget * widget,
     GstVideoInfo * v_info)
index 568027d..a402385 100644 (file)
@@ -68,6 +68,7 @@ struct _GtkGstBaseWidget
 
   /*< private >*/
   GMutex lock;
+  GWeakRef element;
 
   /* Pending draw idles callback */
   guint draw_id;
@@ -92,6 +93,7 @@ void            gtk_gst_base_widget_finalize             (GObject * object);
 /* API */
 gboolean        gtk_gst_base_widget_set_format           (GtkGstBaseWidget * widget, GstVideoInfo * v_info);
 void            gtk_gst_base_widget_set_buffer           (GtkGstBaseWidget * widget, GstBuffer * buffer);
+void            gtk_gst_base_widget_set_element          (GtkGstBaseWidget * widget, GstElement * element);
 
 G_END_DECLS