1 /* GStreamer Navigation
2  * Copyright (C) 2003 Ronald Bultje <rbultje@ronald.bitfreak.net>
3  * Copyright (C) 2007-2009 Jan Schmidt <thaytan@noraisin.net>
4  *
5  * navigation.c: navigation event virtual class function wrappers
6  *
7  * This library is free software; you can redistribute it and/or
8  * modify it under the terms of the GNU Library General Public
9  * License as published by the Free Software Foundation; either
10  * version 2 of the License, or (at your option) any later version.
11  *
12  * This library is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
15  * Library General Public License for more details.
16  *
17  * You should have received a copy of the GNU Library General Public
18  * License along with this library; if not, write to the
19  * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
20  * Boston, MA 02110-1301, USA.
21  */
22 
23 /**
24  * SECTION:gstnavigation
25  * @title: GstNavigation
26  * @short_description: Interface for creating, sending and parsing navigation
27  * events.
28  *
29  * The Navigation interface is used for creating and injecting navigation related
30  * events such as mouse button presses, cursor motion and key presses. The associated
31  * library also provides methods for parsing received events, and for sending and
32  * receiving navigation related bus events. One main usecase is DVD menu navigation.
33  *
34  * The main parts of the API are:
35  *
36  * * The GstNavigation interface, implemented by elements which provide an application
37  *   with the ability to create and inject navigation events into the pipeline.
38  * * GstNavigation event handling API. GstNavigation events are created in response to
39  *   calls on a GstNavigation interface implementation, and sent in the pipeline. Upstream
40  *   elements can use the navigation event API functions to parse the contents of received
41  *   messages.
42  *
43  * * GstNavigation message handling API. GstNavigation messages may be sent on the message
44  *   bus to inform applications of navigation related changes in the pipeline, such as the
45  *   mouse moving over a clickable region, or the set of available angles changing.
46  *
47  * The GstNavigation message functions provide functions for creating and parsing
48  * custom bus messages for signaling GstNavigation changes.
49  *
50  */
51 
52 #ifdef HAVE_CONFIG_H
53 #include "config.h"
54 #endif
55 
56 #include <gst/video/navigation.h>
57 #include <gst/video/video-enumtypes.h>
58 
59 static void gst_navigation_class_init (GstNavigationInterface * iface);
60 
61 #define GST_NAVIGATION_MESSAGE_NAME "GstNavigationMessage"
62 #define GST_NAVIGATION_QUERY_NAME "GstNavigationQuery"
63 #define GST_NAVIGATION_EVENT_NAME "application/x-gst-navigation"
64 
65 #define WARN_IF_FAIL(exp,msg) if(G_UNLIKELY(!(exp))){g_warning("%s",(msg));}
66 
67 GType
gst_navigation_get_type(void)68 gst_navigation_get_type (void)
69 {
70   static GType gst_navigation_type = 0;
71 
72   if (!gst_navigation_type) {
73     static const GTypeInfo gst_navigation_info = {
74       sizeof (GstNavigationInterface),
75       (GBaseInitFunc) gst_navigation_class_init,
76       NULL,
77       NULL,
78       NULL,
79       NULL,
80       0,
81       0,
82       NULL,
83     };
84 
85     gst_navigation_type = g_type_register_static (G_TYPE_INTERFACE,
86         "GstNavigation", &gst_navigation_info, 0);
87   }
88 
89   return gst_navigation_type;
90 }
91 
92 static void
gst_navigation_class_init(GstNavigationInterface * iface)93 gst_navigation_class_init (GstNavigationInterface * iface)
94 {
95   /* default virtual functions */
96   iface->send_event = NULL;
97 }
98 
99 /* The interface implementer should make sure that the object can handle
100  * the event. */
101 void
gst_navigation_send_event(GstNavigation * navigation,GstStructure * structure)102 gst_navigation_send_event (GstNavigation * navigation, GstStructure * structure)
103 {
104   GstNavigationInterface *iface = GST_NAVIGATION_GET_INTERFACE (navigation);
105 
106   if (iface->send_event) {
107     iface->send_event (navigation, structure);
108   } else {
109     gst_structure_free (structure);
110   }
111 }
112 
113 /**
114  * gst_navigation_send_key_event:
115  * @navigation: The navigation interface instance
116  * @event: The type of the key event. Recognised values are "key-press" and
117  * "key-release"
118  * @key: Character representation of the key. This is typically as produced
119  * by XKeysymToString.
120  */
121 void
gst_navigation_send_key_event(GstNavigation * navigation,const char * event,const char * key)122 gst_navigation_send_key_event (GstNavigation * navigation, const char *event,
123     const char *key)
124 {
125   g_return_if_fail (g_strcmp0 (event, "key-press") == 0 ||
126       g_strcmp0 (event, "key-release") == 0);
127 
128   gst_navigation_send_event (navigation,
129       gst_structure_new (GST_NAVIGATION_EVENT_NAME, "event", G_TYPE_STRING,
130           event, "key", G_TYPE_STRING, key, NULL));
131 }
132 
133 /**
134  * gst_navigation_send_mouse_event:
135  * @navigation: The navigation interface instance
136  * @event: The type of mouse event, as a text string. Recognised values are
137  * "mouse-button-press", "mouse-button-release" and "mouse-move".
138  * @button: The button number of the button being pressed or released. Pass 0
139  * for mouse-move events.
140  * @x: The x coordinate of the mouse event.
141  * @y: The y coordinate of the mouse event.
142  *
143  * Sends a mouse event to the navigation interface. Mouse event coordinates
144  * are sent relative to the display space of the related output area. This is
145  * usually the size in pixels of the window associated with the element
146  * implementing the #GstNavigation interface.
147  *
148  */
149 void
gst_navigation_send_mouse_event(GstNavigation * navigation,const char * event,int button,double x,double y)150 gst_navigation_send_mouse_event (GstNavigation * navigation, const char *event,
151     int button, double x, double y)
152 {
153   g_return_if_fail (g_strcmp0 (event, "mouse-button-press") == 0 ||
154       g_strcmp0 (event, "mouse-button-release") == 0 ||
155       g_strcmp0 (event, "mouse-move") == 0);
156 
157   gst_navigation_send_event (navigation,
158       gst_structure_new (GST_NAVIGATION_EVENT_NAME, "event", G_TYPE_STRING,
159           event, "button", G_TYPE_INT, button, "pointer_x", G_TYPE_DOUBLE, x,
160           "pointer_y", G_TYPE_DOUBLE, y, NULL));
161 }
162 
163 /**
164  * gst_navigation_send_command:
165  * @navigation: The navigation interface instance
166  * @command: The command to issue
167  *
168  * Sends the indicated command to the navigation interface.
169  */
170 void
gst_navigation_send_command(GstNavigation * navigation,GstNavigationCommand command)171 gst_navigation_send_command (GstNavigation * navigation,
172     GstNavigationCommand command)
173 {
174   gst_navigation_send_event (navigation,
175       gst_structure_new (GST_NAVIGATION_EVENT_NAME, "event", G_TYPE_STRING,
176           "command", "command-code", G_TYPE_UINT, (guint) command, NULL));
177 }
178 
179 /* Navigation Queries */
180 
181 #define GST_NAVIGATION_QUERY_HAS_TYPE(query,query_type) \
182 (gst_navigation_query_get_type (query) == GST_NAVIGATION_QUERY_ ## query_type)
183 
184 /**
185  * gst_navigation_query_get_type:
186  * @query: The query to inspect
187  *
188  * Inspect a #GstQuery and return the #GstNavigationQueryType associated with
189  * it if it is a #GstNavigation query.
190  *
191  * Returns: The #GstNavigationQueryType of the query, or
192  * #GST_NAVIGATION_QUERY_INVALID
193  */
194 GstNavigationQueryType
gst_navigation_query_get_type(GstQuery * query)195 gst_navigation_query_get_type (GstQuery * query)
196 {
197   const GstStructure *s;
198   const gchar *q_type;
199 
200   if (query == NULL || GST_QUERY_TYPE (query) != GST_QUERY_CUSTOM)
201     return GST_NAVIGATION_QUERY_INVALID;
202 
203   s = gst_query_get_structure (query);
204   if (s == NULL || !gst_structure_has_name (s, GST_NAVIGATION_QUERY_NAME))
205     return GST_NAVIGATION_QUERY_INVALID;
206 
207   q_type = gst_structure_get_string (s, "type");
208   if (q_type == NULL)
209     return GST_NAVIGATION_QUERY_INVALID;
210 
211   if (g_str_equal (q_type, "commands"))
212     return GST_NAVIGATION_QUERY_COMMANDS;
213   else if (g_str_equal (q_type, "angles"))
214     return GST_NAVIGATION_QUERY_ANGLES;
215 
216   return GST_NAVIGATION_QUERY_INVALID;
217 }
218 
219 /**
220  * gst_navigation_query_new_commands:
221  *
222  * Create a new #GstNavigation commands query. When executed, it will
223  * query the pipeline for the set of currently available commands.
224  *
225  * Returns: The new query.
226  */
227 GstQuery *
gst_navigation_query_new_commands(void)228 gst_navigation_query_new_commands (void)
229 {
230   GstQuery *query;
231   GstStructure *structure;
232 
233   structure = gst_structure_new (GST_NAVIGATION_QUERY_NAME,
234       "type", G_TYPE_STRING, "commands", NULL);
235   query = gst_query_new_custom (GST_QUERY_CUSTOM, structure);
236 
237   return query;
238 }
239 
240 static void
gst_query_list_add_command(GValue * list,GstNavigationCommand val)241 gst_query_list_add_command (GValue * list, GstNavigationCommand val)
242 {
243   GValue item = { 0, };
244 
245   g_value_init (&item, GST_TYPE_NAVIGATION_COMMAND);
246   g_value_set_enum (&item, val);
247   gst_value_list_append_value (list, &item);
248   g_value_unset (&item);
249 }
250 
251 /**
252  * gst_navigation_query_set_commands:
253  * @query: a #GstQuery
254  * @n_cmds: the number of commands to set.
255  * @...: A list of @GstNavigationCommand values, @n_cmds entries long.
256  *
257  * Set the #GstNavigation command query result fields in @query. The number
258  * of commands passed must be equal to @n_commands.
259  */
260 void
gst_navigation_query_set_commands(GstQuery * query,gint n_cmds,...)261 gst_navigation_query_set_commands (GstQuery * query, gint n_cmds, ...)
262 {
263   va_list ap;
264   GValue list = { 0, };
265   GstStructure *structure;
266   gint i;
267 
268   g_return_if_fail (GST_NAVIGATION_QUERY_HAS_TYPE (query, COMMANDS));
269 
270   g_value_init (&list, GST_TYPE_LIST);
271 
272   va_start (ap, n_cmds);
273   for (i = 0; i < n_cmds; i++) {
274     GstNavigationCommand val = va_arg (ap, GstNavigationCommand);
275     gst_query_list_add_command (&list, val);
276   }
277   va_end (ap);
278 
279   structure = gst_query_writable_structure (query);
280   gst_structure_take_value (structure, "commands", &list);
281 }
282 
283 /**
284  * gst_navigation_query_set_commandsv:
285  * @query: a #GstQuery
286  * @n_cmds: the number of commands to set.
287  * @cmds: (array length=n_cmds): An array containing @n_cmds
288  *     @GstNavigationCommand values.
289  *
290  * Set the #GstNavigation command query result fields in @query. The number
291  * of commands passed must be equal to @n_commands.
292  */
293 void
gst_navigation_query_set_commandsv(GstQuery * query,gint n_cmds,GstNavigationCommand * cmds)294 gst_navigation_query_set_commandsv (GstQuery * query, gint n_cmds,
295     GstNavigationCommand * cmds)
296 {
297   GValue list = { 0, };
298   GstStructure *structure;
299   gint i;
300 
301   g_return_if_fail (GST_NAVIGATION_QUERY_HAS_TYPE (query, COMMANDS));
302 
303   g_value_init (&list, GST_TYPE_LIST);
304   for (i = 0; i < n_cmds; i++) {
305     gst_query_list_add_command (&list, cmds[i]);
306   }
307   structure = gst_query_writable_structure (query);
308   gst_structure_take_value (structure, "commands", &list);
309 }
310 
311 /**
312  * gst_navigation_query_parse_commands_length:
313  * @query: a #GstQuery
314  * @n_cmds: (out) (optional): the number of commands in this query.
315  *
316  * Parse the number of commands in the #GstNavigation commands @query.
317  *
318  * Returns: %TRUE if the query could be successfully parsed. %FALSE if not.
319  */
320 gboolean
gst_navigation_query_parse_commands_length(GstQuery * query,guint * n_cmds)321 gst_navigation_query_parse_commands_length (GstQuery * query, guint * n_cmds)
322 {
323   const GstStructure *structure;
324   const GValue *list;
325 
326   g_return_val_if_fail (GST_NAVIGATION_QUERY_HAS_TYPE (query, COMMANDS), FALSE);
327 
328   if (n_cmds == NULL)
329     return TRUE;
330 
331   structure = gst_query_get_structure (query);
332   list = gst_structure_get_value (structure, "commands");
333   if (list == NULL)
334     *n_cmds = 0;
335   else
336     *n_cmds = gst_value_list_get_size (list);
337 
338   return TRUE;
339 }
340 
341 /**
342  * gst_navigation_query_parse_commands_nth:
343  * @query: a #GstQuery
344  * @nth: the nth command to retrieve.
345  * @cmd: (out) (optional): a pointer to store the nth command into.
346  *
347  * Parse the #GstNavigation command query and retrieve the @nth command from
348  * it into @cmd. If the list contains less elements than @nth, @cmd will be
349  * set to #GST_NAVIGATION_COMMAND_INVALID.
350  *
351  * Returns: %TRUE if the query could be successfully parsed. %FALSE if not.
352  */
353 gboolean
gst_navigation_query_parse_commands_nth(GstQuery * query,guint nth,GstNavigationCommand * cmd)354 gst_navigation_query_parse_commands_nth (GstQuery * query, guint nth,
355     GstNavigationCommand * cmd)
356 {
357   const GstStructure *structure;
358   const GValue *list;
359 
360   g_return_val_if_fail (GST_NAVIGATION_QUERY_HAS_TYPE (query, COMMANDS), FALSE);
361 
362   if (cmd == NULL)
363     return TRUE;
364 
365   structure = gst_query_get_structure (query);
366   list = gst_structure_get_value (structure, "commands");
367   if (list == NULL) {
368     *cmd = GST_NAVIGATION_COMMAND_INVALID;
369   } else {
370     if (nth < gst_value_list_get_size (list)) {
371       *cmd = (GstNavigationCommand)
372           g_value_get_enum (gst_value_list_get_value (list, nth));
373     } else
374       *cmd = GST_NAVIGATION_COMMAND_INVALID;
375   }
376 
377   return TRUE;
378 }
379 
380 /**
381  * gst_navigation_query_new_angles:
382  *
383  * Create a new #GstNavigation angles query. When executed, it will
384  * query the pipeline for the set of currently available angles, which may be
385  * greater than one in a multiangle video.
386  *
387  * Returns: The new query.
388  */
389 GstQuery *
gst_navigation_query_new_angles(void)390 gst_navigation_query_new_angles (void)
391 {
392   GstQuery *query;
393   GstStructure *structure;
394 
395   structure = gst_structure_new (GST_NAVIGATION_QUERY_NAME,
396       "type", G_TYPE_STRING, "angles", NULL);
397   query = gst_query_new_custom (GST_QUERY_CUSTOM, structure);
398 
399   return query;
400 }
401 
402 /**
403  * gst_navigation_query_set_angles:
404  * @query: a #GstQuery
405  * @cur_angle: the current viewing angle to set.
406  * @n_angles: the number of viewing angles to set.
407  *
408  * Set the #GstNavigation angles query result field in @query.
409  */
410 void
gst_navigation_query_set_angles(GstQuery * query,guint cur_angle,guint n_angles)411 gst_navigation_query_set_angles (GstQuery * query, guint cur_angle,
412     guint n_angles)
413 {
414   GstStructure *structure;
415 
416   g_return_if_fail (GST_NAVIGATION_QUERY_HAS_TYPE (query, ANGLES));
417 
418   structure = gst_query_writable_structure (query);
419   gst_structure_set (structure,
420       "angle", G_TYPE_UINT, cur_angle, "angles", G_TYPE_UINT, n_angles, NULL);
421 }
422 
423 /**
424  * gst_navigation_query_parse_angles:
425  * @query: a #GstQuery
426  * @cur_angle: (out) (optional): Pointer to a #guint into which to store the
427  *     currently selected angle value from the query, or NULL
428  * @n_angles: (out) (optional): Pointer to a #guint into which to store the
429  *     number of angles value from the query, or NULL
430  *
431  * Parse the current angle number in the #GstNavigation angles @query into the
432  * #guint pointed to by the @cur_angle variable, and the number of available
433  * angles into the #guint pointed to by the @n_angles variable.
434  *
435  * Returns: %TRUE if the query could be successfully parsed. %FALSE if not.
436  */
437 gboolean
gst_navigation_query_parse_angles(GstQuery * query,guint * cur_angle,guint * n_angles)438 gst_navigation_query_parse_angles (GstQuery * query, guint * cur_angle,
439     guint * n_angles)
440 {
441   const GstStructure *structure;
442   gboolean ret = TRUE;
443 
444   g_return_val_if_fail (GST_NAVIGATION_QUERY_HAS_TYPE (query, ANGLES), FALSE);
445 
446   structure = gst_query_get_structure (query);
447 
448   if (cur_angle)
449     ret &= gst_structure_get_uint (structure, "angle", cur_angle);
450 
451   if (n_angles)
452     ret &= gst_structure_get_uint (structure, "angles", n_angles);
453 
454   WARN_IF_FAIL (ret, "Couldn't extract details from angles query");
455 
456   return ret;
457 }
458 
459 /* Navigation Messages */
460 
461 #define GST_NAVIGATION_MESSAGE_HAS_TYPE(msg,msg_type) \
462 (gst_navigation_message_get_type (msg) == GST_NAVIGATION_MESSAGE_ ## msg_type)
463 
464 /**
465  * gst_navigation_message_get_type:
466  * @message: A #GstMessage to inspect.
467  *
468  * Check a bus message to see if it is a #GstNavigation event, and return
469  * the #GstNavigationMessageType identifying the type of the message if so.
470  *
471  * Returns: The type of the #GstMessage, or
472  * #GST_NAVIGATION_MESSAGE_INVALID if the message is not a #GstNavigation
473  * notification.
474  */
475 GstNavigationMessageType
gst_navigation_message_get_type(GstMessage * message)476 gst_navigation_message_get_type (GstMessage * message)
477 {
478   const GstStructure *s;
479   const gchar *m_type;
480 
481   if (message == NULL || GST_MESSAGE_TYPE (message) != GST_MESSAGE_ELEMENT)
482     return GST_NAVIGATION_MESSAGE_INVALID;
483 
484   s = gst_message_get_structure (message);
485   if (s == NULL || !gst_structure_has_name (s, GST_NAVIGATION_MESSAGE_NAME))
486     return GST_NAVIGATION_MESSAGE_INVALID;
487 
488   m_type = gst_structure_get_string (s, "type");
489   if (m_type == NULL)
490     return GST_NAVIGATION_MESSAGE_INVALID;
491 
492   if (g_str_equal (m_type, "mouse-over"))
493     return GST_NAVIGATION_MESSAGE_MOUSE_OVER;
494   else if (g_str_equal (m_type, "commands-changed"))
495     return GST_NAVIGATION_MESSAGE_COMMANDS_CHANGED;
496   else if (g_str_equal (m_type, "angles-changed"))
497     return GST_NAVIGATION_MESSAGE_ANGLES_CHANGED;
498   else if (g_str_equal (m_type, "event"))
499     return GST_NAVIGATION_MESSAGE_EVENT;
500 
501   return GST_NAVIGATION_MESSAGE_INVALID;
502 }
503 
504 /**
505  * gst_navigation_message_new_mouse_over:
506  * @src: A #GstObject to set as source of the new message.
507  * @active: %TRUE if the mouse has entered a clickable area of the display.
508  * %FALSE if it over a non-clickable area.
509  *
510  * Creates a new #GstNavigation message with type
511  * #GST_NAVIGATION_MESSAGE_MOUSE_OVER.
512  *
513  * Returns: The new #GstMessage.
514  */
515 GstMessage *
gst_navigation_message_new_mouse_over(GstObject * src,gboolean active)516 gst_navigation_message_new_mouse_over (GstObject * src, gboolean active)
517 {
518   GstStructure *s;
519   GstMessage *m;
520 
521   s = gst_structure_new (GST_NAVIGATION_MESSAGE_NAME,
522       "type", G_TYPE_STRING, "mouse-over", "active", G_TYPE_BOOLEAN, active,
523       NULL);
524 
525   m = gst_message_new_custom (GST_MESSAGE_ELEMENT, src, s);
526 
527   return m;
528 }
529 
530 /**
531  * gst_navigation_message_parse_mouse_over:
532  * @message: A #GstMessage to inspect.
533  * @active: (out) (optional): A pointer to a gboolean to receive the
534  *     active/inactive state, or NULL.
535  *
536  * Parse a #GstNavigation message of type #GST_NAVIGATION_MESSAGE_MOUSE_OVER
537  * and extract the active/inactive flag. If the mouse over event is marked
538  * active, it indicates that the mouse is over a clickable area.
539  *
540  * Returns: %TRUE if the message could be successfully parsed. %FALSE if not.
541  */
542 gboolean
gst_navigation_message_parse_mouse_over(GstMessage * message,gboolean * active)543 gst_navigation_message_parse_mouse_over (GstMessage * message,
544     gboolean * active)
545 {
546   if (!GST_NAVIGATION_MESSAGE_HAS_TYPE (message, MOUSE_OVER))
547     return FALSE;
548 
549   if (active) {
550     const GstStructure *s = gst_message_get_structure (message);
551     if (!gst_structure_get_boolean (s, "active", active))
552       return FALSE;
553   }
554 
555   return TRUE;
556 }
557 
558 /**
559  * gst_navigation_message_new_event:
560  * @src: A #GstObject to set as source of the new message.
561  * @event: (transfer none): A navigation #GstEvent
562  *
563  * Creates a new #GstNavigation message with type
564  * #GST_NAVIGATION_MESSAGE_EVENT.
565  *
566  * Returns: The new #GstMessage.
567  *
568  * Since: 1.6
569  */
570 GstMessage *
gst_navigation_message_new_event(GstObject * src,GstEvent * event)571 gst_navigation_message_new_event (GstObject * src, GstEvent * event)
572 {
573   GstStructure *s;
574   GstMessage *m;
575 
576   s = gst_structure_new (GST_NAVIGATION_MESSAGE_NAME,
577       "type", G_TYPE_STRING, "event", "event", GST_TYPE_EVENT, event, NULL);
578 
579   m = gst_message_new_custom (GST_MESSAGE_ELEMENT, src, s);
580 
581   return m;
582 }
583 
584 /**
585  * gst_navigation_message_parse_event:
586  * @message: A #GstMessage to inspect.
587  * @event: (out) (optional) (transfer full): a pointer to a #GstEvent to receive
588  *     the contained navigation event.
589  *
590  * Parse a #GstNavigation message of type #GST_NAVIGATION_MESSAGE_EVENT
591  * and extract contained #GstEvent. The caller must unref the @event when done
592  * with it.
593  *
594  * Returns: %TRUE if the message could be successfully parsed. %FALSE if not.
595  *
596  * Since: 1.6
597  */
598 gboolean
gst_navigation_message_parse_event(GstMessage * message,GstEvent ** event)599 gst_navigation_message_parse_event (GstMessage * message, GstEvent ** event)
600 {
601   if (!GST_NAVIGATION_MESSAGE_HAS_TYPE (message, EVENT))
602     return FALSE;
603 
604   if (event) {
605     const GstStructure *s = gst_message_get_structure (message);
606     if (!gst_structure_get (s, "event", GST_TYPE_EVENT, event, NULL))
607       return FALSE;
608   }
609 
610   return TRUE;
611 }
612 
613 /**
614  * gst_navigation_message_new_commands_changed:
615  * @src: A #GstObject to set as source of the new message.
616  *
617  * Creates a new #GstNavigation message with type
618  * #GST_NAVIGATION_MESSAGE_COMMANDS_CHANGED
619  *
620  * Returns: The new #GstMessage.
621  */
622 GstMessage *
gst_navigation_message_new_commands_changed(GstObject * src)623 gst_navigation_message_new_commands_changed (GstObject * src)
624 {
625   GstStructure *s;
626   GstMessage *m;
627 
628   s = gst_structure_new (GST_NAVIGATION_MESSAGE_NAME,
629       "type", G_TYPE_STRING, "commands-changed", NULL);
630 
631   m = gst_message_new_custom (GST_MESSAGE_ELEMENT, src, s);
632 
633   return m;
634 }
635 
636 /**
637  * gst_navigation_message_new_angles_changed:
638  * @src: A #GstObject to set as source of the new message.
639  * @cur_angle: The currently selected angle.
640  * @n_angles: The number of viewing angles now available.
641  *
642  * Creates a new #GstNavigation message with type
643  * #GST_NAVIGATION_MESSAGE_ANGLES_CHANGED for notifying an application
644  * that the current angle, or current number of angles available in a
645  * multiangle video has changed.
646  *
647  * Returns: The new #GstMessage.
648  */
649 GstMessage *
gst_navigation_message_new_angles_changed(GstObject * src,guint cur_angle,guint n_angles)650 gst_navigation_message_new_angles_changed (GstObject * src, guint cur_angle,
651     guint n_angles)
652 {
653   GstStructure *s;
654   GstMessage *m;
655 
656   s = gst_structure_new (GST_NAVIGATION_MESSAGE_NAME,
657       "type", G_TYPE_STRING, "angles-changed",
658       "angle", G_TYPE_UINT, cur_angle, "angles", G_TYPE_UINT, n_angles, NULL);
659 
660   m = gst_message_new_custom (GST_MESSAGE_ELEMENT, src, s);
661 
662   return m;
663 }
664 
665 /**
666  * gst_navigation_message_parse_angles_changed:
667  * @message: A #GstMessage to inspect.
668  * @cur_angle: (out) (optional): A pointer to a #guint to receive the new
669  *     current angle number, or NULL
670  * @n_angles: (out) (optional): A pointer to a #guint to receive the new angle
671  *     count, or NULL.
672  *
673  * Parse a #GstNavigation message of type GST_NAVIGATION_MESSAGE_ANGLES_CHANGED
674  * and extract the @cur_angle and @n_angles parameters.
675  *
676  * Returns: %TRUE if the message could be successfully parsed. %FALSE if not.
677  */
678 gboolean
gst_navigation_message_parse_angles_changed(GstMessage * message,guint * cur_angle,guint * n_angles)679 gst_navigation_message_parse_angles_changed (GstMessage * message,
680     guint * cur_angle, guint * n_angles)
681 {
682   const GstStructure *s;
683   gboolean ret = TRUE;
684 
685   g_return_val_if_fail (GST_NAVIGATION_MESSAGE_HAS_TYPE (message,
686           ANGLES_CHANGED), FALSE);
687 
688   s = gst_message_get_structure (message);
689   if (cur_angle)
690     ret &= gst_structure_get_uint (s, "angle", cur_angle);
691 
692   if (n_angles)
693     ret &= gst_structure_get_uint (s, "angles", n_angles);
694 
695   WARN_IF_FAIL (ret, "Couldn't extract details from angles-changed event");
696 
697   return ret;
698 }
699 
700 #define GST_NAVIGATION_EVENT_HAS_TYPE(event,event_type) \
701 (gst_navigation_event_get_type (event) == GST_NAVIGATION_EVENT_ ## event_type)
702 
703 /**
704  * gst_navigation_event_get_type:
705  * @event: A #GstEvent to inspect.
706  *
707  * Inspect a #GstEvent and return the #GstNavigationEventType of the event, or
708  * #GST_NAVIGATION_EVENT_INVALID if the event is not a #GstNavigation event.
709  */
710 GstNavigationEventType
gst_navigation_event_get_type(GstEvent * event)711 gst_navigation_event_get_type (GstEvent * event)
712 {
713   const GstStructure *s;
714   const gchar *e_type;
715 
716   if (event == NULL || GST_EVENT_TYPE (event) != GST_EVENT_NAVIGATION)
717     return GST_NAVIGATION_EVENT_INVALID;
718 
719   s = gst_event_get_structure (event);
720   if (s == NULL || !gst_structure_has_name (s, GST_NAVIGATION_EVENT_NAME))
721     return GST_NAVIGATION_EVENT_INVALID;
722 
723   e_type = gst_structure_get_string (s, "event");
724   if (e_type == NULL)
725     return GST_NAVIGATION_EVENT_INVALID;
726 
727   if (g_str_equal (e_type, "mouse-button-press"))
728     return GST_NAVIGATION_EVENT_MOUSE_BUTTON_PRESS;
729   else if (g_str_equal (e_type, "mouse-button-release"))
730     return GST_NAVIGATION_EVENT_MOUSE_BUTTON_RELEASE;
731   else if (g_str_equal (e_type, "mouse-move"))
732     return GST_NAVIGATION_EVENT_MOUSE_MOVE;
733   else if (g_str_equal (e_type, "key-press"))
734     return GST_NAVIGATION_EVENT_KEY_PRESS;
735   else if (g_str_equal (e_type, "key-release"))
736     return GST_NAVIGATION_EVENT_KEY_RELEASE;
737   else if (g_str_equal (e_type, "command"))
738     return GST_NAVIGATION_EVENT_COMMAND;
739 
740   return GST_NAVIGATION_EVENT_INVALID;
741 }
742 
743 /**
744  * gst_navigation_event_parse_key_event:
745  * @event: A #GstEvent to inspect.
746  * @key: (out) (optional) (transfer none): A pointer to a location to receive
747  *     the string identifying the key press. The returned string is owned by the
748  *     event, and valid only until the event is unreffed.
749  */
750 gboolean
gst_navigation_event_parse_key_event(GstEvent * event,const gchar ** key)751 gst_navigation_event_parse_key_event (GstEvent * event, const gchar ** key)
752 {
753   GstNavigationEventType e_type;
754   const GstStructure *s;
755 
756   e_type = gst_navigation_event_get_type (event);
757   g_return_val_if_fail (e_type == GST_NAVIGATION_EVENT_KEY_PRESS ||
758       e_type == GST_NAVIGATION_EVENT_KEY_RELEASE, FALSE);
759 
760   if (key) {
761     s = gst_event_get_structure (event);
762     *key = gst_structure_get_string (s, "key");
763     if (*key == NULL)
764       return FALSE;
765   }
766 
767   return TRUE;
768 }
769 
770 /**
771  * gst_navigation_event_parse_mouse_button_event:
772  * @event: A #GstEvent to inspect.
773  * @button: (out) (optional): Pointer to a gint that will receive the button
774  *     number associated with the event.
775  * @x: (out) (optional): Pointer to a gdouble to receive the x coordinate of the
776  *     mouse button event.
777  * @y: (out) (optional): Pointer to a gdouble to receive the y coordinate of the
778  *     mouse button event.
779  *
780  * Retrieve the details of either a #GstNavigation mouse button press event or
781  * a mouse button release event. Determine which type the event is using
782  * gst_navigation_event_get_type() to retrieve the #GstNavigationEventType.
783  *
784  * Returns: TRUE if the button number and both coordinates could be extracted,
785  *     otherwise FALSE.
786  */
787 gboolean
gst_navigation_event_parse_mouse_button_event(GstEvent * event,gint * button,gdouble * x,gdouble * y)788 gst_navigation_event_parse_mouse_button_event (GstEvent * event, gint * button,
789     gdouble * x, gdouble * y)
790 {
791   GstNavigationEventType e_type;
792   const GstStructure *s;
793   gboolean ret = TRUE;
794 
795   e_type = gst_navigation_event_get_type (event);
796   g_return_val_if_fail (e_type == GST_NAVIGATION_EVENT_MOUSE_BUTTON_PRESS ||
797       e_type == GST_NAVIGATION_EVENT_MOUSE_BUTTON_RELEASE, FALSE);
798 
799   s = gst_event_get_structure (event);
800   if (x)
801     ret &= gst_structure_get_double (s, "pointer_x", x);
802   if (y)
803     ret &= gst_structure_get_double (s, "pointer_y", y);
804   if (button)
805     ret &= gst_structure_get_int (s, "button", button);
806 
807   WARN_IF_FAIL (ret, "Couldn't extract details from mouse button event");
808 
809   return ret;
810 }
811 
812 /**
813  * gst_navigation_event_parse_mouse_move_event:
814  * @event: A #GstEvent to inspect.
815  * @x: (out) (optional): Pointer to a gdouble to receive the x coordinate of the
816  *     mouse movement.
817  * @y: (out) (optional): Pointer to a gdouble to receive the y coordinate of the
818  *     mouse movement.
819  *
820  * Inspect a #GstNavigation mouse movement event and extract the coordinates
821  * of the event.
822  *
823  * Returns: TRUE if both coordinates could be extracted, otherwise FALSE.
824  */
825 gboolean
gst_navigation_event_parse_mouse_move_event(GstEvent * event,gdouble * x,gdouble * y)826 gst_navigation_event_parse_mouse_move_event (GstEvent * event, gdouble * x,
827     gdouble * y)
828 {
829   const GstStructure *s;
830   gboolean ret = TRUE;
831 
832   g_return_val_if_fail (GST_NAVIGATION_EVENT_HAS_TYPE (event, MOUSE_MOVE),
833       FALSE);
834 
835   s = gst_event_get_structure (event);
836   if (x)
837     ret &= gst_structure_get_double (s, "pointer_x", x);
838   if (y)
839     ret &= gst_structure_get_double (s, "pointer_y", y);
840 
841   WARN_IF_FAIL (ret, "Couldn't extract positions from mouse move event");
842 
843   return ret;
844 }
845 
846 /**
847  * gst_navigation_event_parse_command:
848  * @event: A #GstEvent to inspect.
849  * @command: (out) (optional): Pointer to GstNavigationCommand to receive the
850  *     type of the navigation event.
851  *
852  * Inspect a #GstNavigation command event and retrieve the enum value of the
853  * associated command.
854  *
855  * Returns: TRUE if the navigation command could be extracted, otherwise FALSE.
856  */
857 gboolean
gst_navigation_event_parse_command(GstEvent * event,GstNavigationCommand * command)858 gst_navigation_event_parse_command (GstEvent * event,
859     GstNavigationCommand * command)
860 {
861   const GstStructure *s;
862   gboolean ret = TRUE;
863 
864   g_return_val_if_fail (GST_NAVIGATION_EVENT_HAS_TYPE (event, COMMAND), FALSE);
865 
866   if (command) {
867     s = gst_event_get_structure (event);
868     ret = gst_structure_get_uint (s, "command-code", (guint *) command);
869     WARN_IF_FAIL (ret, "Couldn't extract command code from command event");
870   }
871 
872   return ret;
873 }
874