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