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