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
gst_navigation_default_init(GstNavigationInterface * iface)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
gst_navigation_send_event(GstNavigation * navigation,GstStructure * structure)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
gst_navigation_send_key_event(GstNavigation * navigation,const char * event,const char * key)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
gst_navigation_send_mouse_event(GstNavigation * navigation,const char * event,int button,double x,double y)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
gst_navigation_send_mouse_scroll_event(GstNavigation * navigation,double x,double y,double delta_x,double delta_y)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
gst_navigation_send_command(GstNavigation * navigation,GstNavigationCommand command)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
gst_navigation_query_get_type(GstQuery * query)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 *
gst_navigation_query_new_commands(void)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
gst_query_list_add_command(GValue * list,GstNavigationCommand val)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
gst_navigation_query_set_commands(GstQuery * query,gint n_cmds,...)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
gst_navigation_query_set_commandsv(GstQuery * query,gint n_cmds,GstNavigationCommand * cmds)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
gst_navigation_query_parse_commands_length(GstQuery * query,guint * n_cmds)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
gst_navigation_query_parse_commands_nth(GstQuery * query,guint nth,GstNavigationCommand * cmd)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 *
gst_navigation_query_new_angles(void)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
gst_navigation_query_set_angles(GstQuery * query,guint cur_angle,guint n_angles)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
gst_navigation_query_parse_angles(GstQuery * query,guint * cur_angle,guint * n_angles)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
gst_navigation_message_get_type(GstMessage * message)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 *
gst_navigation_message_new_mouse_over(GstObject * src,gboolean active)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
gst_navigation_message_parse_mouse_over(GstMessage * message,gboolean * active)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 *
gst_navigation_message_new_event(GstObject * src,GstEvent * event)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
gst_navigation_message_parse_event(GstMessage * message,GstEvent ** event)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 *
gst_navigation_message_new_commands_changed(GstObject * src)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 *
gst_navigation_message_new_angles_changed(GstObject * src,guint cur_angle,guint n_angles)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
gst_navigation_message_parse_angles_changed(GstMessage * message,guint * cur_angle,guint * n_angles)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
gst_navigation_event_get_type(GstEvent * event)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
gst_navigation_event_parse_key_event(GstEvent * event,const gchar ** key)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
gst_navigation_event_parse_mouse_button_event(GstEvent * event,gint * button,gdouble * x,gdouble * y)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
gst_navigation_event_parse_mouse_move_event(GstEvent * event,gdouble * x,gdouble * y)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
gst_navigation_event_parse_mouse_scroll_event(GstEvent * event,gdouble * x,gdouble * y,gdouble * delta_x,gdouble * delta_y)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
gst_navigation_event_parse_command(GstEvent * event,GstNavigationCommand * command)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 }
923