1 /*
2 * GStreamer
3 * Copyright (C) 2015 Matthew Waters <matthew@centricular.com>
4 *
5 * This library is free software; you can redistribute it and/or
6 * modify it under the terms of the GNU Library General Public
7 * License as published by the Free Software Foundation; either
8 * version 2 of the License, or (at your option) any later version.
9 *
10 * This library is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 * Library General Public License for more details.
14 *
15 * You should have received a copy of the GNU Library General Public
16 * License along with this library; if not, write to the
17 * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
18 * Boston, MA 02110-1301, USA.
19 */
20
21 #ifdef HAVE_CONFIG_H
22 #include "config.h"
23 #endif
24
25 #include <stdio.h>
26
27 #include "gtkgstbasewidget.h"
28
29 GST_DEBUG_CATEGORY (gst_debug_gtk_base_widget);
30 #define GST_CAT_DEFAULT gst_debug_gtk_base_widget
31
32 #define DEFAULT_FORCE_ASPECT_RATIO TRUE
33 #define DEFAULT_DISPLAY_PAR_N 0
34 #define DEFAULT_DISPLAY_PAR_D 1
35 #define DEFAULT_VIDEO_PAR_N 0
36 #define DEFAULT_VIDEO_PAR_D 1
37 #define DEFAULT_IGNORE_ALPHA TRUE
38
39 enum
40 {
41 PROP_0,
42 PROP_FORCE_ASPECT_RATIO,
43 PROP_PIXEL_ASPECT_RATIO,
44 PROP_IGNORE_ALPHA,
45 PROP_VIDEO_ASPECT_RATIO_OVERRIDE,
46 };
47
48 static gboolean
_calculate_par(GtkGstBaseWidget * widget,GstVideoInfo * info)49 _calculate_par (GtkGstBaseWidget * widget, GstVideoInfo * info)
50 {
51 gboolean ok;
52 gint width, height;
53 gint par_n, par_d;
54 gint display_par_n, display_par_d;
55
56 width = GST_VIDEO_INFO_WIDTH (info);
57 height = GST_VIDEO_INFO_HEIGHT (info);
58 if (width == 0 || height == 0)
59 return FALSE;
60
61 /* get video's PAR */
62 if (widget->video_par_n != 0 && widget->video_par_d != 0) {
63 par_n = widget->video_par_n;
64 par_d = widget->video_par_d;
65 } else {
66 par_n = GST_VIDEO_INFO_PAR_N (info);
67 par_d = GST_VIDEO_INFO_PAR_D (info);
68 }
69
70 if (!par_n)
71 par_n = 1;
72
73 /* get display's PAR */
74 if (widget->par_n != 0 && widget->par_d != 0) {
75 display_par_n = widget->par_n;
76 display_par_d = widget->par_d;
77 } else {
78 display_par_n = 1;
79 display_par_d = 1;
80 }
81
82
83 ok = gst_video_calculate_display_ratio (&widget->display_ratio_num,
84 &widget->display_ratio_den, width, height, par_n, par_d, display_par_n,
85 display_par_d);
86
87 if (ok) {
88 GST_LOG ("PAR: %u/%u DAR:%u/%u", par_n, par_d, display_par_n,
89 display_par_d);
90 return TRUE;
91 }
92
93 return FALSE;
94 }
95
96 static void
_apply_par(GtkGstBaseWidget * widget)97 _apply_par (GtkGstBaseWidget * widget)
98 {
99 guint display_ratio_num, display_ratio_den;
100 gint width, height;
101
102 width = GST_VIDEO_INFO_WIDTH (&widget->v_info);
103 height = GST_VIDEO_INFO_HEIGHT (&widget->v_info);
104
105 if (!width || !height)
106 return;
107
108 display_ratio_num = widget->display_ratio_num;
109 display_ratio_den = widget->display_ratio_den;
110
111 if (height % display_ratio_den == 0) {
112 GST_DEBUG ("keeping video height");
113 widget->display_width = (guint)
114 gst_util_uint64_scale_int (height, display_ratio_num,
115 display_ratio_den);
116 widget->display_height = height;
117 } else if (width % display_ratio_num == 0) {
118 GST_DEBUG ("keeping video width");
119 widget->display_width = width;
120 widget->display_height = (guint)
121 gst_util_uint64_scale_int (width, display_ratio_den, display_ratio_num);
122 } else {
123 GST_DEBUG ("approximating while keeping video height");
124 widget->display_width = (guint)
125 gst_util_uint64_scale_int (height, display_ratio_num,
126 display_ratio_den);
127 widget->display_height = height;
128 }
129
130 GST_DEBUG ("scaling to %dx%d", widget->display_width, widget->display_height);
131 }
132
133 static gboolean
_queue_draw(GtkGstBaseWidget * widget)134 _queue_draw (GtkGstBaseWidget * widget)
135 {
136 GTK_GST_BASE_WIDGET_LOCK (widget);
137 widget->draw_id = 0;
138
139 if (widget->pending_resize) {
140 widget->pending_resize = FALSE;
141
142 widget->v_info = widget->pending_v_info;
143 widget->negotiated = TRUE;
144
145 _apply_par (widget);
146
147 gtk_widget_queue_resize (GTK_WIDGET (widget));
148 } else {
149 gtk_widget_queue_draw (GTK_WIDGET (widget));
150 }
151
152 GTK_GST_BASE_WIDGET_UNLOCK (widget);
153
154 return G_SOURCE_REMOVE;
155 }
156
157 static void
_update_par(GtkGstBaseWidget * widget)158 _update_par (GtkGstBaseWidget * widget)
159 {
160 GTK_GST_BASE_WIDGET_LOCK (widget);
161 if (widget->pending_resize) {
162 GTK_GST_BASE_WIDGET_UNLOCK (widget);
163 return;
164 }
165
166 if (!_calculate_par (widget, &widget->v_info)) {
167 GTK_GST_BASE_WIDGET_UNLOCK (widget);
168 return;
169 }
170
171 widget->pending_resize = TRUE;
172 if (!widget->draw_id) {
173 widget->draw_id = g_idle_add_full (G_PRIORITY_HIGH_IDLE + 10,
174 (GSourceFunc) _queue_draw, widget, NULL);
175 }
176 GTK_GST_BASE_WIDGET_UNLOCK (widget);
177 }
178
179 static void
gtk_gst_base_widget_get_preferred_width(GtkWidget * widget,gint * min,gint * natural)180 gtk_gst_base_widget_get_preferred_width (GtkWidget * widget, gint * min,
181 gint * natural)
182 {
183 GtkGstBaseWidget *gst_widget = (GtkGstBaseWidget *) widget;
184 gint video_width = gst_widget->display_width;
185
186 if (!gst_widget->negotiated)
187 video_width = 10;
188
189 if (min)
190 *min = 1;
191 if (natural)
192 *natural = video_width;
193 }
194
195 static void
gtk_gst_base_widget_get_preferred_height(GtkWidget * widget,gint * min,gint * natural)196 gtk_gst_base_widget_get_preferred_height (GtkWidget * widget, gint * min,
197 gint * natural)
198 {
199 GtkGstBaseWidget *gst_widget = (GtkGstBaseWidget *) widget;
200 gint video_height = gst_widget->display_height;
201
202 if (!gst_widget->negotiated)
203 video_height = 10;
204
205 if (min)
206 *min = 1;
207 if (natural)
208 *natural = video_height;
209 }
210
211 static void
gtk_gst_base_widget_set_property(GObject * object,guint prop_id,const GValue * value,GParamSpec * pspec)212 gtk_gst_base_widget_set_property (GObject * object, guint prop_id,
213 const GValue * value, GParamSpec * pspec)
214 {
215 GtkGstBaseWidget *gtk_widget = GTK_GST_BASE_WIDGET (object);
216
217 switch (prop_id) {
218 case PROP_FORCE_ASPECT_RATIO:
219 gtk_widget->force_aspect_ratio = g_value_get_boolean (value);
220 break;
221 case PROP_PIXEL_ASPECT_RATIO:
222 gtk_widget->par_n = gst_value_get_fraction_numerator (value);
223 gtk_widget->par_d = gst_value_get_fraction_denominator (value);
224 _update_par (gtk_widget);
225 break;
226 case PROP_VIDEO_ASPECT_RATIO_OVERRIDE:
227 gtk_widget->video_par_n = gst_value_get_fraction_numerator (value);
228 gtk_widget->video_par_d = gst_value_get_fraction_denominator (value);
229 _update_par (gtk_widget);
230 break;
231 case PROP_IGNORE_ALPHA:
232 gtk_widget->ignore_alpha = g_value_get_boolean (value);
233 break;
234 default:
235 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
236 break;
237 }
238 }
239
240 static void
gtk_gst_base_widget_get_property(GObject * object,guint prop_id,GValue * value,GParamSpec * pspec)241 gtk_gst_base_widget_get_property (GObject * object, guint prop_id,
242 GValue * value, GParamSpec * pspec)
243 {
244 GtkGstBaseWidget *gtk_widget = GTK_GST_BASE_WIDGET (object);
245
246 switch (prop_id) {
247 case PROP_FORCE_ASPECT_RATIO:
248 g_value_set_boolean (value, gtk_widget->force_aspect_ratio);
249 break;
250 case PROP_PIXEL_ASPECT_RATIO:
251 gst_value_set_fraction (value, gtk_widget->par_n, gtk_widget->par_d);
252 break;
253 case PROP_VIDEO_ASPECT_RATIO_OVERRIDE:
254 gst_value_set_fraction (value, gtk_widget->video_par_n,
255 gtk_widget->video_par_d);
256 break;
257 case PROP_IGNORE_ALPHA:
258 g_value_set_boolean (value, gtk_widget->ignore_alpha);
259 break;
260 default:
261 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
262 break;
263 }
264 }
265
266 static const gchar *
_gdk_key_to_navigation_string(guint keyval)267 _gdk_key_to_navigation_string (guint keyval)
268 {
269 /* TODO: expand */
270 switch (keyval) {
271 #define KEY(key) case GDK_KEY_ ## key: return G_STRINGIFY(key)
272 KEY (Up);
273 KEY (Down);
274 KEY (Left);
275 KEY (Right);
276 KEY (Home);
277 KEY (End);
278 #undef KEY
279 default:
280 return NULL;
281 }
282 }
283
284 static gboolean
gtk_gst_base_widget_key_event(GtkWidget * widget,GdkEventKey * event)285 gtk_gst_base_widget_key_event (GtkWidget * widget, GdkEventKey * event)
286 {
287 GtkGstBaseWidget *base_widget = GTK_GST_BASE_WIDGET (widget);
288 GstElement *element;
289
290 if ((element = g_weak_ref_get (&base_widget->element))) {
291 if (GST_IS_NAVIGATION (element)) {
292 const gchar *str = _gdk_key_to_navigation_string (event->keyval);
293 const gchar *key_type =
294 event->type == GDK_KEY_PRESS ? "key-press" : "key-release";
295
296 if (!str)
297 str = event->string;
298
299 gst_navigation_send_key_event (GST_NAVIGATION (element), key_type, str);
300 }
301 g_object_unref (element);
302 }
303
304 return FALSE;
305 }
306
307 static void
_fit_stream_to_allocated_size(GtkGstBaseWidget * base_widget,GtkAllocation * allocation,GstVideoRectangle * result)308 _fit_stream_to_allocated_size (GtkGstBaseWidget * base_widget,
309 GtkAllocation * allocation, GstVideoRectangle * result)
310 {
311 if (base_widget->force_aspect_ratio) {
312 GstVideoRectangle src, dst;
313
314 src.x = 0;
315 src.y = 0;
316 src.w = base_widget->display_width;
317 src.h = base_widget->display_height;
318
319 dst.x = 0;
320 dst.y = 0;
321 dst.w = allocation->width;
322 dst.h = allocation->height;
323
324 gst_video_sink_center_rect (src, dst, result, TRUE);
325 } else {
326 result->x = 0;
327 result->y = 0;
328 result->w = allocation->width;
329 result->h = allocation->height;
330 }
331 }
332
333 void
gtk_gst_base_widget_display_size_to_stream_size(GtkGstBaseWidget * base_widget,gdouble x,gdouble y,gdouble * stream_x,gdouble * stream_y)334 gtk_gst_base_widget_display_size_to_stream_size (GtkGstBaseWidget * base_widget,
335 gdouble x, gdouble y, gdouble * stream_x, gdouble * stream_y)
336 {
337 gdouble stream_width, stream_height;
338 GtkAllocation allocation;
339 GstVideoRectangle result;
340
341 gtk_widget_get_allocation (GTK_WIDGET (base_widget), &allocation);
342 _fit_stream_to_allocated_size (base_widget, &allocation, &result);
343
344 stream_width = (gdouble) GST_VIDEO_INFO_WIDTH (&base_widget->v_info);
345 stream_height = (gdouble) GST_VIDEO_INFO_HEIGHT (&base_widget->v_info);
346
347 /* from display coordinates to stream coordinates */
348 if (result.w > 0)
349 *stream_x = (x - result.x) / result.w * stream_width;
350 else
351 *stream_x = 0.;
352
353 /* clip to stream size */
354 if (*stream_x < 0.)
355 *stream_x = 0.;
356 if (*stream_x > GST_VIDEO_INFO_WIDTH (&base_widget->v_info))
357 *stream_x = GST_VIDEO_INFO_WIDTH (&base_widget->v_info);
358
359 /* same for y-axis */
360 if (result.h > 0)
361 *stream_y = (y - result.y) / result.h * stream_height;
362 else
363 *stream_y = 0.;
364
365 if (*stream_y < 0.)
366 *stream_y = 0.;
367 if (*stream_y > GST_VIDEO_INFO_HEIGHT (&base_widget->v_info))
368 *stream_y = GST_VIDEO_INFO_HEIGHT (&base_widget->v_info);
369
370 GST_TRACE ("transform %fx%f into %fx%f", x, y, *stream_x, *stream_y);
371 }
372
373 static gboolean
gtk_gst_base_widget_button_event(GtkWidget * widget,GdkEventButton * event)374 gtk_gst_base_widget_button_event (GtkWidget * widget, GdkEventButton * event)
375 {
376 GtkGstBaseWidget *base_widget = GTK_GST_BASE_WIDGET (widget);
377 GstElement *element;
378
379 if ((element = g_weak_ref_get (&base_widget->element))) {
380 if (GST_IS_NAVIGATION (element)) {
381 const gchar *key_type =
382 event->type ==
383 GDK_BUTTON_PRESS ? "mouse-button-press" : "mouse-button-release";
384 gst_navigation_send_mouse_event (GST_NAVIGATION (element), key_type,
385 event->button, event->x, event->y);
386 }
387 g_object_unref (element);
388 }
389
390 return FALSE;
391 }
392
393 static gboolean
gtk_gst_base_widget_motion_event(GtkWidget * widget,GdkEventMotion * event)394 gtk_gst_base_widget_motion_event (GtkWidget * widget, GdkEventMotion * event)
395 {
396 GtkGstBaseWidget *base_widget = GTK_GST_BASE_WIDGET (widget);
397 GstElement *element;
398
399 if ((element = g_weak_ref_get (&base_widget->element))) {
400 if (GST_IS_NAVIGATION (element)) {
401 gst_navigation_send_mouse_event (GST_NAVIGATION (element), "mouse-move",
402 0, event->x, event->y);
403 }
404 g_object_unref (element);
405 }
406
407 return FALSE;
408 }
409
410 static gboolean
gtk_gst_base_widget_scroll_event(GtkWidget * widget,GdkEventScroll * event)411 gtk_gst_base_widget_scroll_event (GtkWidget * widget, GdkEventScroll * event)
412 {
413 GtkGstBaseWidget *base_widget = GTK_GST_BASE_WIDGET (widget);
414 GstElement *element;
415
416 if ((element = g_weak_ref_get (&base_widget->element))) {
417 if (GST_IS_NAVIGATION (element)) {
418 gdouble x, y, delta_x, delta_y;
419
420 gtk_gst_base_widget_display_size_to_stream_size (base_widget, event->x,
421 event->y, &x, &y);
422
423 if (!gdk_event_get_scroll_deltas ((GdkEvent *) event, &delta_x, &delta_y)) {
424 gdouble offset = 20;
425
426 delta_x = event->delta_x;
427 delta_y = event->delta_y;
428
429 switch (event->direction) {
430 case GDK_SCROLL_UP:
431 delta_y = offset;
432 break;
433 case GDK_SCROLL_DOWN:
434 delta_y = -offset;
435 break;
436 case GDK_SCROLL_LEFT:
437 delta_x = -offset;
438 break;
439 case GDK_SCROLL_RIGHT:
440 delta_x = offset;
441 break;
442 default:
443 break;
444 }
445 }
446 gst_navigation_send_mouse_scroll_event (GST_NAVIGATION (element),
447 x, y, delta_x, delta_y);
448 }
449 g_object_unref (element);
450 }
451 return FALSE;
452 }
453
454
455 void
gtk_gst_base_widget_class_init(GtkGstBaseWidgetClass * klass)456 gtk_gst_base_widget_class_init (GtkGstBaseWidgetClass * klass)
457 {
458 GObjectClass *gobject_klass = (GObjectClass *) klass;
459 GtkWidgetClass *widget_klass = (GtkWidgetClass *) klass;
460
461 gobject_klass->set_property = gtk_gst_base_widget_set_property;
462 gobject_klass->get_property = gtk_gst_base_widget_get_property;
463
464 g_object_class_install_property (gobject_klass, PROP_FORCE_ASPECT_RATIO,
465 g_param_spec_boolean ("force-aspect-ratio",
466 "Force aspect ratio",
467 "When enabled, scaling will respect original aspect ratio",
468 DEFAULT_FORCE_ASPECT_RATIO,
469 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS |
470 GST_PARAM_MUTABLE_PLAYING));
471
472 g_object_class_install_property (gobject_klass, PROP_PIXEL_ASPECT_RATIO,
473 gst_param_spec_fraction ("pixel-aspect-ratio", "Pixel Aspect Ratio",
474 "The pixel aspect ratio of the device",
475 0, 1, G_MAXINT, G_MAXINT, DEFAULT_DISPLAY_PAR_N,
476 DEFAULT_DISPLAY_PAR_D, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS |
477 GST_PARAM_MUTABLE_PLAYING));
478
479 g_object_class_install_property (gobject_klass,
480 PROP_VIDEO_ASPECT_RATIO_OVERRIDE,
481 gst_param_spec_fraction ("video-aspect-ratio-override",
482 "Video Pixel Aspect Ratio",
483 "The pixel aspect ratio of the video (0/1 = follow stream)", 0,
484 G_MAXINT, G_MAXINT, 1, DEFAULT_VIDEO_PAR_N, DEFAULT_VIDEO_PAR_D,
485 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS |
486 GST_PARAM_MUTABLE_PLAYING));
487
488 g_object_class_install_property (gobject_klass, PROP_IGNORE_ALPHA,
489 g_param_spec_boolean ("ignore-alpha", "Ignore Alpha",
490 "When enabled, alpha will be ignored and converted to black",
491 DEFAULT_IGNORE_ALPHA, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
492
493 widget_klass->get_preferred_width = gtk_gst_base_widget_get_preferred_width;
494 widget_klass->get_preferred_height = gtk_gst_base_widget_get_preferred_height;
495 widget_klass->key_press_event = gtk_gst_base_widget_key_event;
496 widget_klass->key_release_event = gtk_gst_base_widget_key_event;
497 widget_klass->button_press_event = gtk_gst_base_widget_button_event;
498 widget_klass->button_release_event = gtk_gst_base_widget_button_event;
499 widget_klass->motion_notify_event = gtk_gst_base_widget_motion_event;
500 widget_klass->scroll_event = gtk_gst_base_widget_scroll_event;
501
502 GST_DEBUG_CATEGORY_INIT (gst_debug_gtk_base_widget, "gtkbasewidget", 0,
503 "Gtk Video Base Widget");
504 }
505
506 void
gtk_gst_base_widget_init(GtkGstBaseWidget * widget)507 gtk_gst_base_widget_init (GtkGstBaseWidget * widget)
508 {
509 int event_mask;
510
511 widget->force_aspect_ratio = DEFAULT_FORCE_ASPECT_RATIO;
512 widget->par_n = DEFAULT_DISPLAY_PAR_N;
513 widget->par_d = DEFAULT_DISPLAY_PAR_D;
514 widget->video_par_n = DEFAULT_VIDEO_PAR_N;
515 widget->video_par_d = DEFAULT_VIDEO_PAR_D;
516 widget->ignore_alpha = DEFAULT_IGNORE_ALPHA;
517
518 gst_video_info_init (&widget->v_info);
519 gst_video_info_init (&widget->pending_v_info);
520
521 g_weak_ref_init (&widget->element, NULL);
522 g_mutex_init (&widget->lock);
523
524 gtk_widget_set_can_focus (GTK_WIDGET (widget), TRUE);
525 event_mask = gtk_widget_get_events (GTK_WIDGET (widget));
526 event_mask |= GDK_KEY_PRESS_MASK
527 | GDK_KEY_RELEASE_MASK
528 | GDK_BUTTON_PRESS_MASK
529 | GDK_BUTTON_RELEASE_MASK
530 | GDK_POINTER_MOTION_MASK | GDK_BUTTON_MOTION_MASK | GDK_SCROLL_MASK;
531 gtk_widget_set_events (GTK_WIDGET (widget), event_mask);
532 }
533
534 void
gtk_gst_base_widget_finalize(GObject * object)535 gtk_gst_base_widget_finalize (GObject * object)
536 {
537 GtkGstBaseWidget *widget = GTK_GST_BASE_WIDGET (object);
538
539 gst_buffer_replace (&widget->pending_buffer, NULL);
540 gst_buffer_replace (&widget->buffer, NULL);
541 g_mutex_clear (&widget->lock);
542 g_weak_ref_clear (&widget->element);
543
544 if (widget->draw_id)
545 g_source_remove (widget->draw_id);
546 }
547
548 void
gtk_gst_base_widget_set_element(GtkGstBaseWidget * widget,GstElement * element)549 gtk_gst_base_widget_set_element (GtkGstBaseWidget * widget,
550 GstElement * element)
551 {
552 g_weak_ref_set (&widget->element, element);
553 }
554
555 gboolean
gtk_gst_base_widget_set_format(GtkGstBaseWidget * widget,GstVideoInfo * v_info)556 gtk_gst_base_widget_set_format (GtkGstBaseWidget * widget,
557 GstVideoInfo * v_info)
558 {
559 GTK_GST_BASE_WIDGET_LOCK (widget);
560
561 if (gst_video_info_is_equal (&widget->pending_v_info, v_info)) {
562 GTK_GST_BASE_WIDGET_UNLOCK (widget);
563 return TRUE;
564 }
565
566 if (!_calculate_par (widget, v_info)) {
567 GTK_GST_BASE_WIDGET_UNLOCK (widget);
568 return FALSE;
569 }
570
571 widget->pending_resize = TRUE;
572 widget->pending_v_info = *v_info;
573
574 GTK_GST_BASE_WIDGET_UNLOCK (widget);
575
576 return TRUE;
577 }
578
579 void
gtk_gst_base_widget_set_buffer(GtkGstBaseWidget * widget,GstBuffer * buffer)580 gtk_gst_base_widget_set_buffer (GtkGstBaseWidget * widget, GstBuffer * buffer)
581 {
582 /* As we have no type, this is better then no check */
583 g_return_if_fail (GTK_IS_WIDGET (widget));
584
585 GTK_GST_BASE_WIDGET_LOCK (widget);
586
587 gst_buffer_replace (&widget->pending_buffer, buffer);
588
589 if (!widget->draw_id) {
590 widget->draw_id = g_idle_add_full (G_PRIORITY_DEFAULT,
591 (GSourceFunc) _queue_draw, widget, NULL);
592 }
593
594 GTK_GST_BASE_WIDGET_UNLOCK (widget);
595 }
596
597 void
gtk_gst_base_widget_queue_draw(GtkGstBaseWidget * widget)598 gtk_gst_base_widget_queue_draw (GtkGstBaseWidget * widget)
599 {
600 /* As we have no type, this is better then no check */
601 g_return_if_fail (GTK_IS_WIDGET (widget));
602
603 GTK_GST_BASE_WIDGET_LOCK (widget);
604
605 if (!widget->draw_id) {
606 widget->draw_id = g_idle_add_full (G_PRIORITY_DEFAULT,
607 (GSourceFunc) _queue_draw, widget, NULL);
608 }
609
610 GTK_GST_BASE_WIDGET_UNLOCK (widget);
611 }
612