3 * Copyright (C) 2015 Raspberry Pi Foundation
4 * Author: Thibault Saunier <thibault.saunier@collabora.com>
6 * gstvalidategtk.c: GstValidateActionTypes to use with gtk applications
8 * This library is free software; you can redistribute it and/or
9 * modify it under the terms of the GNU Lesser General Public
10 * License as published by the Free Software Foundation; either
11 * version 2.1 of the License, or (at your option) any later version.
13 * This library is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 * Lesser General Public License for more details.
18 * You should have received a copy of the GNU Lesser General Public
19 * License along with this library; if not, write to the
20 * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
21 * Boston, MA 02111-1307, USA.
34 #include "../../gst/validate/gst-validate-report.h"
35 #include "../../gst/validate/gst-validate-reporter.h"
36 #include "../../gst/validate/validate.h"
37 #include "../../gst/validate/gst-validate-scenario.h"
38 #include "../../gst/validate/gst-validate-utils.h"
40 #define ACTION_GDKEVENTS_QUARK g_quark_from_static_string("ACTION_GDKEVENTS_QUARK")
41 static GList *awaited_actions = NULL; /* A list of GstValidateAction to be executed */
44 get_widget_name (GtkWidget * widget)
46 const gchar *name = NULL;
48 if (GTK_IS_BUILDABLE (widget))
49 name = gtk_buildable_get_name (GTK_BUILDABLE (widget));
52 name = gtk_widget_get_name (widget);
59 get_event_type (GstValidateScenario * scenario, GstValidateAction * action)
62 const gchar *etype_str = gst_structure_get_string (action->structure, "type");
67 if (gst_validate_utils_enum_from_str (GDK_TYPE_EVENT_TYPE, etype_str, &type))
70 GST_VALIDATE_REPORT (scenario,
71 g_quark_from_static_string ("scenario::execution-error"),
72 "Uknown event type %s, the string should look like the ones defined in "
73 "gdk_event_type_get_type", etype_str);
78 #if ! GTK_CHECK_VERSION(3,20,0)
80 get_device (GstValidateAction * action, GdkInputSource input_source)
83 GdkDevice *device = NULL;
84 GdkDeviceManager *dev_manager;
86 dev_manager = gdk_display_get_device_manager (gdk_display_get_default ());
88 gdk_device_manager_list_devices (dev_manager, GDK_DEVICE_TYPE_MASTER);
90 for (tmp = devices; tmp; tmp = tmp->next) {
91 if (gdk_device_get_source (tmp->data) == input_source) {
97 g_list_free (devices);
104 _create_key_event (GdkWindow * window, GdkEventType etype, guint keyval,
105 guint hw_keycode, guint state, GdkDevice * device)
107 GdkEvent *event = gdk_event_new (etype);
108 GdkEventKey *kevent = (GdkEventKey *) event;
110 kevent->window = g_object_ref (window);
111 kevent->send_event = TRUE;
112 kevent->time = GDK_CURRENT_TIME;
113 kevent->keyval = keyval;
114 kevent->hardware_keycode = hw_keycode;
115 kevent->state = state;
117 gdk_event_set_device (event, device);
123 _create_keyboard_events (GstValidateAction * action,
124 GdkWindow * window, const gchar * keyname, const gchar * string,
128 #if GTK_CHECK_VERSION(3,20,0)
132 GList *events = NULL;
133 GdkDevice *device = NULL;
134 GstValidateScenario *scenario = gst_validate_action_get_scenario (action);
136 if (etype == GDK_NOTHING) {
137 etype = GDK_KEY_PRESS;
138 } else if (etype != GDK_KEY_PRESS && etype != GDK_KEY_RELEASE) {
139 GST_VALIDATE_REPORT (scenario,
140 g_quark_from_static_string ("scenario::execution-error"),
141 "GdkEvent type %s does not work with the 'keys' parameter",
142 gst_structure_get_string (action->structure, "type"));
146 #if GTK_CHECK_VERSION(3,20,0)
147 display = gdk_display_get_default ();
148 if (display == NULL) {
149 GST_VALIDATE_REPORT (scenario,
150 g_quark_from_static_string ("scenario::execution-error"),
151 "Could not find a display");
156 seat = gdk_display_get_default_seat (display);
157 device = gdk_seat_get_keyboard (seat);
159 device = get_device (action, GDK_SOURCE_KEYBOARD);
161 if (device == NULL) {
162 GST_VALIDATE_REPORT (scenario,
163 g_quark_from_static_string ("scenario::execution-error"),
164 "Could not find a keyboard device");
172 gtk_accelerator_parse_with_keycode (keyname, &keyval, &keys, &state);
174 g_list_append (events, _create_key_event (window, etype, keyval,
175 keys ? keys[0] : 0, state, device));
179 for (i = 0; string[i]; i++) {
182 guint keyval = gdk_unicode_to_keyval (string[i]);
184 gdk_keymap_get_entries_for_keyval (gdk_keymap_get_for_display
185 (gdk_display_get_default ()), keyval, &kmaps, &n_keys);
188 g_list_append (events, _create_key_event (window, etype, keyval,
189 kmaps[0].keycode, 0, device));
193 gst_object_unref (scenario);
197 gst_object_unref (scenario);
204 gchar **widget_paths;
210 static GtkWidget *_find_widget (GtkContainer * container,
211 WidgetNameWidget * res);
214 _widget_has_name (GtkWidget * widget, gchar * name)
216 if (g_strcmp0 (get_widget_name (GTK_WIDGET (widget)), name) == 0) {
224 _find_widget_cb (GtkWidget * child, WidgetNameWidget * res)
230 if (_widget_has_name (child, res->widget_paths[res->current_index])) {
231 res->current_index++;
233 if (res->widget_paths[res->current_index] == NULL) {
236 GST_ERROR ("%p GOT IT!!! %s", child,
237 gtk_buildable_get_name (GTK_BUILDABLE (child)));
238 } else if (GTK_CONTAINER (child)) {
239 res->widget = _find_widget (GTK_CONTAINER (child), res);
243 if (GTK_IS_CONTAINER (child)) {
244 res->widget = _find_widget (GTK_CONTAINER (child), res);
251 _find_widget (GtkContainer * container, WidgetNameWidget * res)
256 if (_widget_has_name (GTK_WIDGET (container),
257 res->widget_paths[res->current_index])) {
258 res->current_index++;
260 if (res->widget_paths[res->current_index] == NULL)
261 return GTK_WIDGET (container);
264 gtk_container_forall (container, (GtkCallback) _find_widget_cb, res);
267 res->current_index++;
269 if (res->widget_paths[res->current_index + 1] == NULL)
272 if (GTK_IS_CONTAINER (res->widget))
273 _find_widget (GTK_CONTAINER (res->widget), res);
281 _find_button (GtkWidget * widget, GtkWidget ** button)
283 if (GTK_IS_BUTTON (widget))
287 /* Copy pasted from gtk+/gtk/gtktestutils.c */
289 test_find_widget_input_windows (GtkWidget * widget, gboolean input_only)
292 GList *node, *children;
293 GSList *matches = NULL;
296 window = gtk_widget_get_window (widget);
298 gdk_window_get_user_data (window, &udata);
299 if (udata == widget && (!input_only || (GDK_IS_WINDOW (window)
300 && gdk_window_is_input_only (GDK_WINDOW (window)))))
301 matches = g_slist_prepend (matches, window);
302 children = gdk_window_get_children (gtk_widget_get_parent_window (widget));
303 for (node = children; node; node = node->next) {
304 gdk_window_get_user_data (node->data, &udata);
305 if (udata == widget && (!input_only || (GDK_IS_WINDOW (node->data)
306 && gdk_window_is_input_only (GDK_WINDOW (node->data)))))
307 matches = g_slist_prepend (matches, node->data);
309 return g_slist_reverse (matches);
313 widget_get_window (GtkWidget * widget)
315 GdkWindow *res = NULL;
316 GSList *iwindows = test_find_widget_input_windows (widget, FALSE);
319 iwindows = test_find_widget_input_windows (widget, TRUE);
322 res = iwindows->data;
324 g_slist_free (iwindows);
330 get_window (GstValidateScenario * scenario, GstValidateAction * action,
331 const gchar * widget_name)
334 GdkWindow *res = NULL;
335 gchar **widget_paths = NULL;
337 GList *toplevels = gtk_window_list_toplevels ();
340 widget_name = gst_structure_get_string (action->structure, "widget-name");
343 GST_VALIDATE_REPORT (scenario,
344 g_quark_from_static_string ("scenario::execution-error"),
345 "No Gtk topelevel window found, can not sent GdkEvent");
351 res = gtk_widget_get_window (toplevels->data);
356 widget_paths = g_strsplit (widget_name, "/", -1);
358 for (tmptoplevel = toplevels; tmptoplevel; tmptoplevel = tmptoplevel->next) {
362 wn.widget_paths = widget_paths;
363 wn.current_index = 0;
367 widget = _find_widget (tmptoplevel->data, &wn);
369 if (GTK_IS_TOOL_BUTTON (widget)) {
370 GST_ERROR ("IS TOOL BUTTON");
371 gtk_container_forall (GTK_CONTAINER (widget),
372 (GtkCallback) _find_button, &widget);
375 res = widget_get_window (widget);
381 g_list_free (toplevels);
386 static GstValidateActionReturn
387 _put_events (GstValidateAction * action, GList * events)
392 return GST_VALIDATE_EXECUTE_ACTION_ERROR_REPORTED;
394 gst_mini_object_set_qdata (GST_MINI_OBJECT (action), ACTION_GDKEVENTS_QUARK,
396 awaited_actions = g_list_append (awaited_actions, action);
398 for (tmp = events; tmp; tmp = tmp->next) {
399 gdk_event_put (tmp->data);
402 return GST_VALIDATE_EXECUTE_ACTION_ASYNC;
406 _execute_put_events (GstValidateScenario * scenario, GstValidateAction * action)
409 const gchar *keys, *string;
411 GList *events = NULL;
412 GdkWindow *window = get_window (scenario, action, NULL);
415 return GST_VALIDATE_EXECUTE_ACTION_ERROR_REPORTED;
417 etype = get_event_type (scenario, action);
419 return GST_VALIDATE_EXECUTE_ACTION_ERROR_REPORTED;
421 keys = gst_structure_get_string (action->structure, "keys");
422 string = gst_structure_get_string (action->structure, "string");
423 if (keys || string) {
424 events = _create_keyboard_events (action, window, keys, string, etype);
426 return _put_events (action, events);
429 GST_VALIDATE_REPORT (scenario,
430 g_quark_from_static_string ("scenario::execution-error"),
431 "Action parameters not supported yet");
433 return GST_VALIDATE_EXECUTE_ACTION_ERROR_REPORTED;
437 _process_event (GdkEvent * event, gpointer data)
440 GdkEvent *done_event = NULL;
441 GstValidateAction *action = NULL;
443 for (tmp = awaited_actions; tmp; tmp = tmp->next) {
444 GstValidateAction *tmp_action = tmp->data;
445 GdkEvent *awaited_event =
446 ((GList *) gst_mini_object_get_qdata (GST_MINI_OBJECT (tmp_action),
447 ACTION_GDKEVENTS_QUARK))->data;
449 if (awaited_event->type == event->type
450 && ((GdkEventAny *) event)->window ==
451 ((GdkEventAny *) awaited_event)->window) {
453 switch (awaited_event->type) {
455 case GDK_KEY_RELEASE:
456 if (event->key.keyval == awaited_event->key.keyval) {
457 done_event = awaited_event;
462 g_assert_not_reached ();
468 GList *awaited_events = gst_mini_object_get_qdata (GST_MINI_OBJECT (action),
469 ACTION_GDKEVENTS_QUARK);
471 awaited_events = g_list_remove (awaited_events, done_event);
472 gdk_event_free (done_event);
473 gst_mini_object_set_qdata (GST_MINI_OBJECT (action), ACTION_GDKEVENTS_QUARK,
474 awaited_events, NULL);
476 if (awaited_events == NULL) {
477 awaited_actions = g_list_remove (awaited_actions, action);
478 gst_validate_action_set_done (action);
482 gtk_main_do_event (event);
486 gst_validate_gtk_init (GstPlugin * plugin)
488 gdk_event_handler_set (_process_event, NULL, NULL);
491 gst_validate_register_action_type_dynamic (plugin, "gtk-put-event",
492 GST_RANK_PRIMARY, _execute_put_events, ((GstValidateActionParameter[]) {
495 .description = "The keyboard keys to be used for the event, parsed"
496 " with gtk_accelerator_parse_with_keycode, so refer to its documentation"
497 " for more information",
500 .possible_variables = NULL,
504 .description = "The string to be 'written' by the keyboard"
505 " sending KEY_PRESS GdkEvents",
508 .possible_variables = NULL,
512 .description = "The event type to get executed. "
513 "the string should look like the ones in GdkEventType but without"
514 " the leading 'GDK_'. It is not mandatory as it can be computed from"
515 " other present fields (e.g, an action with 'keys' will consider the type"
516 " as 'key_pressed' by default).",
521 .name = "widget-name",
522 .description = "The name of the target GdkWidget of the GdkEvent"
523 ". That widget has to contain a GdkWindow. If not specified,"
524 " the event will be sent to the first toplevel window",
527 .possible_variables = NULL,
531 "Put a GdkEvent on the event list using gdk_put_event",
532 GST_VALIDATE_ACTION_TYPE_NO_EXECUTION_NOT_FATAL |
533 GST_VALIDATE_ACTION_TYPE_DOESNT_NEED_PIPELINE);
539 GST_PLUGIN_DEFINE (GST_VERSION_MAJOR,
542 "GstValidate plugin to execute action specific to the Gtk toolkit",
543 gst_validate_gtk_init, VERSION, "LGPL", GST_PACKAGE_NAME,