1 /*M///////////////////////////////////////////////////////////////////////////////////////
2 //
3 // IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING.
4 //
5 // By downloading, copying, installing or using the software you agree to this license.
6 // If you do not agree to this license, do not download, install,
7 // copy or use the software.
8 //
9 //
10 // Intel License Agreement
11 // For Open Source Computer Vision Library
12 //
13 // Copyright (C) 2000, Intel Corporation, all rights reserved.
14 // Third party copyrights are property of their respective owners.
15 //
16 // Redistribution and use in source and binary forms, with or without modification,
17 // are permitted provided that the following conditions are met:
18 //
19 // * Redistribution's of source code must retain the above copyright notice,
20 // this list of conditions and the following disclaimer.
21 //
22 // * Redistribution's in binary form must reproduce the above copyright notice,
23 // this list of conditions and the following disclaimer in the documentation
24 // and/or other materials provided with the distribution.
25 //
26 // * The name of Intel Corporation may not be used to endorse or promote products
27 // derived from this software without specific prior written permission.
28 //
29 // This software is provided by the copyright holders and contributors "as is" and
30 // any express or implied warranties, including, but not limited to, the implied
31 // warranties of merchantability and fitness for a particular purpose are disclaimed.
32 // In no event shall the Intel Corporation or contributors be liable for any direct,
33 // indirect, incidental, special, exemplary, or consequential damages
34 // (including, but not limited to, procurement of substitute goods or services;
35 // loss of use, data, or profits; or business interruption) however caused
36 // and on any theory of liability, whether in contract, strict liability,
37 // or tort (including negligence or otherwise) arising in any way out of
38 // the use of this software, even if advised of the possibility of such damage.
39 //
40 //M*/
41
42 #include "precomp.hpp"
43 #include "opencv2/imgproc.hpp"
44
45 #ifndef WIN32
46
47 #if defined (HAVE_GTK)
48
49 #include <gtk/gtk.h>
50 #include <gdk/gdkkeysyms.h>
51 #include <gdk-pixbuf/gdk-pixbuf.h>
52 #include <stdio.h>
53
54 #if (GTK_MAJOR_VERSION == 3)
55 #define GTK_VERSION3
56 #endif //GTK_MAJOR_VERSION >= 3
57
58 #ifdef HAVE_OPENGL
59 #include <gtk/gtkgl.h>
60 #include <GL/gl.h>
61 #include <GL/glu.h>
62 #endif
63
64 // TODO Fix the initial window size when flags=0. Right now the initial window is by default
65 // 320x240 size. A better default would be actual size of the image. Problem
66 // is determining desired window size with trackbars while still allowing resizing.
67 //
68 // Gnome Totem source may be of use here, see bacon_video_widget_set_scale_ratio
69 // in totem/src/backend/bacon-video-widget-xine.c
70
71 ////////////////////////////////////////////////////////////
72 // CvImageWidget GTK Widget Public API
73 ////////////////////////////////////////////////////////////
74 typedef struct _CvImageWidget CvImageWidget;
75 typedef struct _CvImageWidgetClass CvImageWidgetClass;
76
77 struct _CvImageWidget {
78 GtkWidget widget;
79 CvMat * original_image;
80 CvMat * scaled_image;
81 int flags;
82 };
83
84 struct _CvImageWidgetClass
85 {
86 GtkWidgetClass parent_class;
87 };
88
89
90 /** Allocate new image viewer widget */
91 GtkWidget* cvImageWidgetNew (int flags);
92
93 /** Set the image to display in the widget */
94 void cvImageWidgetSetImage(CvImageWidget * widget, const CvArr *arr);
95
96 // standard GTK object macros
97 #define CV_IMAGE_WIDGET(obj) G_TYPE_CHECK_INSTANCE_CAST (obj, cvImageWidget_get_type (), CvImageWidget)
98 #define CV_IMAGE_WIDGET_CLASS(klass) GTK_CHECK_CLASS_CAST (klass, cvImageWidget_get_type (), CvImageWidgetClass)
99 #define CV_IS_IMAGE_WIDGET(obj) G_TYPE_CHECK_INSTANCE_TYPE (obj, cvImageWidget_get_type ())
100
101 /////////////////////////////////////////////////////////////////////////////
102 // Private API ////////////////////////////////////////////////////////
103 /////////////////////////////////////////////////////////////////////////////
104 GType cvImageWidget_get_type (void);
105
106 static GtkWidgetClass * parent_class = NULL;
107
108 // flag to help size initial window
109 #define CV_WINDOW_NO_IMAGE 2
110
cvImageWidgetSetImage(CvImageWidget * widget,const CvArr * arr)111 void cvImageWidgetSetImage(CvImageWidget * widget, const CvArr *arr){
112 CvMat * mat, stub;
113 int origin=0;
114
115 //printf("cvImageWidgetSetImage\n");
116
117 if( CV_IS_IMAGE_HDR( arr ))
118 origin = ((IplImage*)arr)->origin;
119
120 mat = cvGetMat(arr, &stub);
121
122 if(widget->original_image && !CV_ARE_SIZES_EQ(mat, widget->original_image)){
123 cvReleaseMat( &widget->original_image );
124 }
125 if(!widget->original_image){
126 widget->original_image = cvCreateMat( mat->rows, mat->cols, CV_8UC3 );
127 gtk_widget_queue_resize( GTK_WIDGET( widget ) );
128 }
129 cvConvertImage( mat, widget->original_image,
130 (origin != 0 ? CV_CVTIMG_FLIP : 0) + CV_CVTIMG_SWAP_RB );
131 if(widget->scaled_image){
132 cvResize( widget->original_image, widget->scaled_image, CV_INTER_AREA );
133 }
134
135 // window does not refresh without this
136 gtk_widget_queue_draw( GTK_WIDGET(widget) );
137 }
138
139 GtkWidget*
cvImageWidgetNew(int flags)140 cvImageWidgetNew (int flags)
141 {
142 CvImageWidget *image_widget;
143
144 image_widget = CV_IMAGE_WIDGET( gtk_widget_new (cvImageWidget_get_type (), NULL) );
145 image_widget->original_image = 0;
146 image_widget->scaled_image = 0;
147 image_widget->flags = flags | CV_WINDOW_NO_IMAGE;
148
149 return GTK_WIDGET (image_widget);
150 }
151
152 static void
cvImageWidget_realize(GtkWidget * widget)153 cvImageWidget_realize (GtkWidget *widget)
154 {
155 GdkWindowAttr attributes;
156 gint attributes_mask;
157
158 #if defined(GTK_VERSION3)
159 GtkAllocation allocation;
160 gtk_widget_get_allocation(widget, &allocation);
161 #endif //GTK_VERSION3
162
163 //printf("cvImageWidget_realize\n");
164 g_return_if_fail (widget != NULL);
165 g_return_if_fail (CV_IS_IMAGE_WIDGET (widget));
166
167 gtk_widget_set_realized(widget, TRUE);
168
169 #if defined(GTK_VERSION3)
170 attributes.x = allocation.x;
171 attributes.y = allocation.y;
172 attributes.width = allocation.width;
173 attributes.height = allocation.height;
174 #else
175 attributes.x = widget->allocation.x;
176 attributes.y = widget->allocation.y;
177 attributes.width = widget->allocation.width;
178 attributes.height = widget->allocation.height;
179 #endif //GTK_VERSION3
180
181 attributes.wclass = GDK_INPUT_OUTPUT;
182 attributes.window_type = GDK_WINDOW_CHILD;
183 attributes.event_mask = gtk_widget_get_events (widget) |
184 GDK_EXPOSURE_MASK | GDK_BUTTON_PRESS_MASK |
185 GDK_BUTTON_RELEASE_MASK | GDK_POINTER_MOTION_MASK;
186 attributes.visual = gtk_widget_get_visual (widget);
187
188 #if defined(GTK_VERSION3)
189 attributes_mask = GDK_WA_X | GDK_WA_Y | GDK_WA_VISUAL;
190 gtk_widget_set_window(
191 widget,
192 gdk_window_new(
193 gtk_widget_get_parent_window(widget),
194 &attributes,
195 attributes_mask
196 )
197 );
198
199 gtk_widget_set_style(
200 widget,
201 gtk_style_attach(
202 gtk_widget_get_style(widget),
203 gtk_widget_get_window(widget)
204 )
205 );
206
207 gdk_window_set_user_data (
208 gtk_widget_get_window(widget),
209 widget
210 );
211
212 gtk_style_set_background (
213 gtk_widget_get_style(widget),
214 gtk_widget_get_window(widget),
215 GTK_STATE_ACTIVE
216 );
217 #else
218 // The following lines are included to prevent breaking
219 // compatibility with older Gtk2 (<gtk+-2.18) libraries.
220 attributes.colormap = gtk_widget_get_colormap (widget);
221 attributes_mask = GDK_WA_X | GDK_WA_Y | GDK_WA_VISUAL | GDK_WA_COLORMAP;
222 widget->window = gdk_window_new (widget->parent->window, &attributes, attributes_mask);
223
224 widget->style = gtk_style_attach (widget->style, widget->window);
225 gdk_window_set_user_data (widget->window, widget);
226
227 gtk_style_set_background (widget->style, widget->window, GTK_STATE_ACTIVE);
228 #endif // GTK_VERSION3
229 }
230
cvImageWidget_calc_size(int im_width,int im_height,int max_width,int max_height)231 static CvSize cvImageWidget_calc_size( int im_width, int im_height, int max_width, int max_height ){
232 float aspect = (float)im_width/(float)im_height;
233 float max_aspect = (float)max_width/(float)max_height;
234 if(aspect > max_aspect){
235 return cvSize( max_width, cvRound(max_width/aspect) );
236 }
237 return cvSize( cvRound(max_height*aspect), max_height );
238 }
239
240 #if defined (GTK_VERSION3)
241 static void
cvImageWidget_get_preferred_width(GtkWidget * widget,gint * minimal_width,gint * natural_width)242 cvImageWidget_get_preferred_width (GtkWidget *widget, gint *minimal_width, gint *natural_width)
243 {
244 g_return_if_fail (widget != NULL);
245 g_return_if_fail (CV_IS_IMAGE_WIDGET (widget));
246 CvImageWidget * image_widget = CV_IMAGE_WIDGET( widget );
247
248 if(image_widget->original_image != NULL) {
249 *minimal_width = image_widget->flags & CV_WINDOW_AUTOSIZE ?
250 gdk_window_get_width(gtk_widget_get_window(widget)) : image_widget->original_image->cols;
251 }
252 else {
253 *minimal_width = 320;
254 }
255
256 if(image_widget->scaled_image != NULL) {
257 *natural_width = *minimal_width < image_widget->scaled_image->cols ?
258 image_widget->scaled_image->cols : *minimal_width;
259 }
260 else {
261 *natural_width = *minimal_width;
262 }
263 }
264
265 static void
cvImageWidget_get_preferred_height(GtkWidget * widget,gint * minimal_height,gint * natural_height)266 cvImageWidget_get_preferred_height (GtkWidget *widget, gint *minimal_height, gint *natural_height)
267 {
268 g_return_if_fail (widget != NULL);
269 g_return_if_fail (CV_IS_IMAGE_WIDGET (widget));
270 CvImageWidget * image_widget = CV_IMAGE_WIDGET( widget );
271
272 if(image_widget->original_image != NULL) {
273 *minimal_height = image_widget->flags & CV_WINDOW_AUTOSIZE ?
274 gdk_window_get_height(gtk_widget_get_window(widget)) : image_widget->original_image->rows;
275 }
276 else {
277 *minimal_height = 240;
278 }
279
280 if(image_widget->scaled_image != NULL) {
281 *natural_height = *minimal_height < image_widget->scaled_image->rows ?
282 image_widget->scaled_image->cols : *minimal_height;
283 }
284 else {
285 *natural_height = *minimal_height;
286 }
287 }
288
289 #else
290 static void
cvImageWidget_size_request(GtkWidget * widget,GtkRequisition * requisition)291 cvImageWidget_size_request (GtkWidget *widget,
292 GtkRequisition *requisition)
293 {
294 CvImageWidget * image_widget = CV_IMAGE_WIDGET( widget );
295
296 //printf("cvImageWidget_size_request ");
297 // the case the first time cvShowImage called or when AUTOSIZE
298 if( image_widget->original_image &&
299 ((image_widget->flags & CV_WINDOW_AUTOSIZE) ||
300 (image_widget->flags & CV_WINDOW_NO_IMAGE)))
301 {
302 //printf("original ");
303 requisition->width = image_widget->original_image->cols;
304 requisition->height = image_widget->original_image->rows;
305 }
306 // default case
307 else if(image_widget->scaled_image){
308 //printf("scaled ");
309 requisition->width = image_widget->scaled_image->cols;
310 requisition->height = image_widget->scaled_image->rows;
311 }
312 // the case before cvShowImage called
313 else{
314 //printf("default ");
315 requisition->width = 320;
316 requisition->height = 240;
317 }
318 //printf("%d %d\n",requisition->width, requisition->height);
319 }
320 #endif //GTK_VERSION3
321
cvImageWidget_set_size(GtkWidget * widget,int max_width,int max_height)322 static void cvImageWidget_set_size(GtkWidget * widget, int max_width, int max_height){
323 CvImageWidget * image_widget = CV_IMAGE_WIDGET( widget );
324
325 //printf("cvImageWidget_set_size %d %d\n", max_width, max_height);
326
327 // don't allow to set the size
328 if(image_widget->flags & CV_WINDOW_AUTOSIZE) return;
329 if(!image_widget->original_image) return;
330
331 CvSize scaled_image_size = cvImageWidget_calc_size( image_widget->original_image->cols,
332 image_widget->original_image->rows, max_width, max_height );
333
334 if( image_widget->scaled_image &&
335 ( image_widget->scaled_image->cols != scaled_image_size.width ||
336 image_widget->scaled_image->rows != scaled_image_size.height ))
337 {
338 cvReleaseMat( &image_widget->scaled_image );
339 }
340 if( !image_widget->scaled_image ){
341 image_widget->scaled_image = cvCreateMat( scaled_image_size.height, scaled_image_size.width, CV_8UC3 );
342
343
344 }
345 assert( image_widget->scaled_image );
346 }
347
348 static void
cvImageWidget_size_allocate(GtkWidget * widget,GtkAllocation * allocation)349 cvImageWidget_size_allocate (GtkWidget *widget,
350 GtkAllocation *allocation)
351 {
352 CvImageWidget *image_widget;
353
354 //printf("cvImageWidget_size_allocate\n");
355 g_return_if_fail (widget != NULL);
356 g_return_if_fail (CV_IS_IMAGE_WIDGET (widget));
357 g_return_if_fail (allocation != NULL);
358
359 #if defined (GTK_VERSION3)
360 gtk_widget_set_allocation(widget, allocation);
361 #else
362 widget->allocation = *allocation;
363 #endif //GTK_VERSION3
364 image_widget = CV_IMAGE_WIDGET (widget);
365
366
367 if( (image_widget->flags & CV_WINDOW_AUTOSIZE)==0 && image_widget->original_image ){
368 // (re) allocated scaled image
369 if( image_widget->flags & CV_WINDOW_NO_IMAGE ){
370 cvImageWidget_set_size( widget, image_widget->original_image->cols,
371 image_widget->original_image->rows);
372 }
373 else{
374 cvImageWidget_set_size( widget, allocation->width, allocation->height );
375 }
376 cvResize( image_widget->original_image, image_widget->scaled_image, CV_INTER_AREA );
377 }
378
379 if (gtk_widget_get_realized (widget))
380 {
381 image_widget = CV_IMAGE_WIDGET (widget);
382
383 if( image_widget->original_image &&
384 ((image_widget->flags & CV_WINDOW_AUTOSIZE) ||
385 (image_widget->flags & CV_WINDOW_NO_IMAGE)) )
386 {
387 #if defined (GTK_VERSION3)
388 allocation->width = image_widget->original_image->cols;
389 allocation->height = image_widget->original_image->rows;
390 gtk_widget_set_allocation(widget, allocation);
391 #else
392 widget->allocation.width = image_widget->original_image->cols;
393 widget->allocation.height = image_widget->original_image->rows;
394 #endif //GTK_VERSION3
395 gdk_window_move_resize( gtk_widget_get_window(widget),
396 allocation->x, allocation->y,
397 image_widget->original_image->cols, image_widget->original_image->rows );
398 if(image_widget->flags & CV_WINDOW_NO_IMAGE){
399 image_widget->flags &= ~CV_WINDOW_NO_IMAGE;
400 gtk_widget_queue_resize( GTK_WIDGET(widget) );
401 }
402 }
403 else{
404 gdk_window_move_resize (gtk_widget_get_window(widget),
405 allocation->x, allocation->y,
406 allocation->width, allocation->height );
407 }
408 }
409 }
410
411 #if defined (GTK_VERSION3)
412 static void
cvImageWidget_destroy(GtkWidget * object)413 cvImageWidget_destroy (GtkWidget *object)
414 #else
415 static void
416 cvImageWidget_destroy (GtkObject *object)
417 #endif //GTK_VERSION3
418 {
419 CvImageWidget *image_widget;
420
421 g_return_if_fail (object != NULL);
422 g_return_if_fail (CV_IS_IMAGE_WIDGET (object));
423
424 image_widget = CV_IMAGE_WIDGET (object);
425
426 cvReleaseMat( &image_widget->scaled_image );
427 cvReleaseMat( &image_widget->original_image );
428
429 #if defined (GTK_VERSION3)
430 if (GTK_WIDGET_CLASS (parent_class)->destroy)
431 (* GTK_WIDGET_CLASS (parent_class)->destroy) (object);
432 #else
433 if (GTK_OBJECT_CLASS (parent_class)->destroy)
434 (* GTK_OBJECT_CLASS (parent_class)->destroy) (object);
435 #endif //GTK_VERSION3
436 }
437
cvImageWidget_class_init(CvImageWidgetClass * klass)438 static void cvImageWidget_class_init (CvImageWidgetClass * klass)
439 {
440 #if defined (GTK_VERSION3)
441 GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass);
442 #else
443 GtkObjectClass *object_class;
444 GtkWidgetClass *widget_class;
445
446 object_class = (GtkObjectClass*) klass;
447 widget_class = (GtkWidgetClass*) klass;
448 #endif //GTK_VERSION3
449
450 parent_class = GTK_WIDGET_CLASS( g_type_class_peek (gtk_widget_get_type ()) );
451
452 #if defined (GTK_VERSION3)
453 widget_class->destroy = cvImageWidget_destroy;
454 widget_class->get_preferred_width = cvImageWidget_get_preferred_width;
455 widget_class->get_preferred_height = cvImageWidget_get_preferred_height;
456 #else
457 object_class->destroy = cvImageWidget_destroy;
458 widget_class->size_request = cvImageWidget_size_request;
459 #endif //GTK_VERSION3
460
461 widget_class->realize = cvImageWidget_realize;
462 widget_class->size_allocate = cvImageWidget_size_allocate;
463 widget_class->button_press_event = NULL;
464 widget_class->button_release_event = NULL;
465 widget_class->motion_notify_event = NULL;
466 }
467
468 static void
cvImageWidget_init(CvImageWidget * image_widget)469 cvImageWidget_init (CvImageWidget *image_widget)
470 {
471 image_widget->original_image=0;
472 image_widget->scaled_image=0;
473 image_widget->flags=0;
474 }
475
cvImageWidget_get_type(void)476 GType cvImageWidget_get_type (void){
477 static GType image_type = 0;
478
479 if (!image_type)
480 {
481 image_type = g_type_register_static_simple(
482 GTK_TYPE_WIDGET,
483 (gchar*) "CvImageWidget",
484 sizeof(CvImageWidgetClass),
485 (GClassInitFunc) cvImageWidget_class_init,
486 sizeof(CvImageWidget),
487 (GInstanceInitFunc) cvImageWidget_init,
488 (GTypeFlags)NULL
489 );
490 }
491
492 return image_type;
493 }
494 /////////////////////////////////////////////////////////////////////////////
495 // End CvImageWidget
496 /////////////////////////////////////////////////////////////////////////////
497
498
499 struct CvWindow;
500
501 typedef struct CvTrackbar
502 {
503 int signature;
504 GtkWidget* widget;
505 char* name;
506 CvTrackbar* next;
507 CvWindow* parent;
508 int* data;
509 int pos;
510 int maxval;
511 CvTrackbarCallback notify;
512 CvTrackbarCallback2 notify2;
513 void* userdata;
514 }
515 CvTrackbar;
516
517
518 typedef struct CvWindow
519 {
520 int signature;
521 GtkWidget* widget;
522 GtkWidget* frame;
523 GtkWidget* paned;
524 char* name;
525 CvWindow* prev;
526 CvWindow* next;
527
528 int last_key;
529 int flags;
530 int status;//0 normal, 1 fullscreen (YV)
531
532 CvMouseCallback on_mouse;
533 void* on_mouse_param;
534
535 struct
536 {
537 int pos;
538 int rows;
539 CvTrackbar* first;
540 }
541 toolbar;
542
543 #ifdef HAVE_OPENGL
544 bool useGl;
545
546 CvOpenGlDrawCallback glDrawCallback;
547 void* glDrawData;
548 #endif
549 }
550 CvWindow;
551
552
553 static gboolean icvOnClose( GtkWidget* widget, GdkEvent* event, gpointer user_data );
554 static gboolean icvOnKeyPress( GtkWidget* widget, GdkEventKey* event, gpointer user_data );
555 static void icvOnTrackbar( GtkWidget* widget, gpointer user_data );
556 static gboolean icvOnMouse( GtkWidget *widget, GdkEvent *event, gpointer user_data );
557
558 #ifdef HAVE_GTHREAD
559 int thread_started=0;
560 static gpointer icvWindowThreadLoop();
561 GMutex* last_key_mutex;
562 GCond* cond_have_key;
563 GMutex* window_mutex;
564 GThread* window_thread;
565 GtkWidget* cvTopLevelWidget = 0;
566 #endif
567
568 static int last_key = -1;
569 static CvWindow* hg_windows = 0;
570
cvInitSystem(int argc,char ** argv)571 CV_IMPL int cvInitSystem( int argc, char** argv )
572 {
573 static int wasInitialized = 0;
574
575 // check initialization status
576 if( !wasInitialized )
577 {
578 hg_windows = 0;
579
580 gtk_init( &argc, &argv );
581 setlocale(LC_NUMERIC,"C");
582
583 #ifdef HAVE_OPENGL
584 gtk_gl_init(&argc, &argv);
585 #endif
586
587 wasInitialized = 1;
588 }
589
590 return 0;
591 }
592
cvStartWindowThread()593 CV_IMPL int cvStartWindowThread(){
594 #ifdef HAVE_GTHREAD
595 cvInitSystem(0,NULL);
596 if (!thread_started) {
597 if (!g_thread_supported ()) {
598 /* the GThread system wasn't inited, so init it */
599 g_thread_init(NULL);
600 }
601
602 // this mutex protects the window resources
603 window_mutex = g_mutex_new();
604
605 // protects the 'last key pressed' variable
606 last_key_mutex = g_mutex_new();
607
608 // conditional that indicates a key has been pressed
609 cond_have_key = g_cond_new();
610
611 // this is the window update thread
612 window_thread = g_thread_create((GThreadFunc) icvWindowThreadLoop,
613 NULL, TRUE, NULL);
614 }
615 thread_started = window_thread!=NULL;
616 return thread_started;
617 #else
618 return 0;
619 #endif
620 }
621
622 #ifdef HAVE_GTHREAD
icvWindowThreadLoop()623 gpointer icvWindowThreadLoop(){
624 while(1){
625 g_mutex_lock(window_mutex);
626 gtk_main_iteration_do(FALSE);
627 g_mutex_unlock(window_mutex);
628
629 // little sleep
630 g_usleep(500);
631
632 g_thread_yield();
633 }
634 return NULL;
635 }
636
637 #define CV_LOCK_MUTEX() \
638 if(thread_started && g_thread_self()!=window_thread){ g_mutex_lock( window_mutex ); } else { }
639
640 #define CV_UNLOCK_MUTEX() \
641 if(thread_started && g_thread_self()!=window_thread){ g_mutex_unlock( window_mutex); } else { }
642
643 #else
644 #define CV_LOCK_MUTEX()
645 #define CV_UNLOCK_MUTEX()
646 #endif
647
icvFindWindowByName(const char * name)648 static CvWindow* icvFindWindowByName( const char* name )
649 {
650 CvWindow* window = hg_windows;
651 while( window != 0 && strcmp(name, window->name) != 0 )
652 window = window->next;
653
654 return window;
655 }
656
icvWindowByWidget(GtkWidget * widget)657 static CvWindow* icvWindowByWidget( GtkWidget* widget )
658 {
659 CvWindow* window = hg_windows;
660
661 while( window != 0 && window->widget != widget &&
662 window->frame != widget && window->paned != widget )
663 window = window->next;
664
665 return window;
666 }
667
cvGetModeWindow_GTK(const char * name)668 double cvGetModeWindow_GTK(const char* name)//YV
669 {
670 double result = -1;
671
672 CV_FUNCNAME( "cvGetModeWindow_GTK" );
673
674 __BEGIN__;
675
676 CvWindow* window;
677
678 if (!name)
679 CV_ERROR( CV_StsNullPtr, "NULL name string" );
680
681 window = icvFindWindowByName( name );
682 if (!window)
683 CV_ERROR( CV_StsNullPtr, "NULL window" );
684
685 CV_LOCK_MUTEX();
686 result = window->status;
687 CV_UNLOCK_MUTEX();
688
689 __END__;
690 return result;
691 }
692
693
cvSetModeWindow_GTK(const char * name,double prop_value)694 void cvSetModeWindow_GTK( const char* name, double prop_value)//Yannick Verdie
695 {
696
697 CV_FUNCNAME( "cvSetModeWindow_GTK" );
698
699 __BEGIN__;
700
701 CvWindow* window;
702
703 if(!name)
704 CV_ERROR( CV_StsNullPtr, "NULL name string" );
705
706 window = icvFindWindowByName( name );
707 if( !window )
708 CV_ERROR( CV_StsNullPtr, "NULL window" );
709
710 if(window->flags & CV_WINDOW_AUTOSIZE)//if the flag CV_WINDOW_AUTOSIZE is set
711 EXIT;
712
713 //so easy to do fullscreen here, Linux rocks !
714
715 if (window->status==CV_WINDOW_FULLSCREEN && prop_value==CV_WINDOW_NORMAL)
716 {
717 CV_LOCK_MUTEX();
718 gtk_window_unfullscreen(GTK_WINDOW(window->frame));
719 window->status=CV_WINDOW_NORMAL;
720 CV_UNLOCK_MUTEX();
721 EXIT;
722 }
723
724 if (window->status==CV_WINDOW_NORMAL && prop_value==CV_WINDOW_FULLSCREEN)
725 {
726 CV_LOCK_MUTEX();
727 gtk_window_fullscreen(GTK_WINDOW(window->frame));
728 window->status=CV_WINDOW_FULLSCREEN;
729 CV_UNLOCK_MUTEX();
730 EXIT;
731 }
732
733 __END__;
734 }
735
setWindowTitle(const String & winname,const String & title)736 void cv::setWindowTitle(const String& winname, const String& title)
737 {
738 CvWindow* window = icvFindWindowByName(winname.c_str());
739
740 if (!window)
741 {
742 namedWindow(winname);
743 window = icvFindWindowByName(winname.c_str());
744 }
745
746 if (!window)
747 CV_Error(Error::StsNullPtr, "NULL window");
748
749 CV_LOCK_MUTEX();
750 gtk_window_set_title(GTK_WINDOW(window->frame), title.c_str());
751 CV_UNLOCK_MUTEX();
752 }
753
cvGetPropWindowAutoSize_GTK(const char * name)754 double cvGetPropWindowAutoSize_GTK(const char* name)
755 {
756 double result = -1;
757
758 CV_FUNCNAME( "cvGetPropWindowAutoSize_GTK" );
759
760 __BEGIN__;
761
762 CvWindow* window;
763
764 if (!name)
765 CV_ERROR( CV_StsNullPtr, "NULL name string" );
766
767 window = icvFindWindowByName( name );
768 if (!window)
769 EXIT; // keep silence here
770
771 result = window->flags & CV_WINDOW_AUTOSIZE;
772
773 __END__;
774
775 return result;
776 }
777
cvGetRatioWindow_GTK(const char * name)778 double cvGetRatioWindow_GTK(const char* name)
779 {
780 double result = -1;
781
782 CV_FUNCNAME( "cvGetRatioWindow_GTK" );
783
784 __BEGIN__;
785
786 CvWindow* window;
787
788 if (!name)
789 CV_ERROR( CV_StsNullPtr, "NULL name string" );
790
791 window = icvFindWindowByName( name );
792 if (!window)
793 EXIT; // keep silence here
794
795 #if defined (GTK_VERSION3)
796 result = static_cast<double>(
797 gtk_widget_get_allocated_width(window->widget)) / gtk_widget_get_allocated_height(window->widget);
798 #else
799 result = static_cast<double>(window->widget->allocation.width) / window->widget->allocation.height;
800 #endif // GTK_VERSION3
801 __END__;
802
803 return result;
804 }
805
cvGetOpenGlProp_GTK(const char * name)806 double cvGetOpenGlProp_GTK(const char* name)
807 {
808 double result = -1;
809
810 #ifdef HAVE_OPENGL
811 CV_FUNCNAME( "cvGetOpenGlProp_GTK" );
812
813 __BEGIN__;
814
815 CvWindow* window;
816
817 if (!name)
818 CV_ERROR( CV_StsNullPtr, "NULL name string" );
819
820 window = icvFindWindowByName( name );
821 if (!window)
822 EXIT; // keep silence here
823
824 result = window->useGl;
825
826 __END__;
827 #else
828 (void)name;
829 #endif
830
831 return result;
832 }
833
834
835 // OpenGL support
836
837 #ifdef HAVE_OPENGL
838
839 namespace
840 {
createGlContext(CvWindow * window)841 void createGlContext(CvWindow* window)
842 {
843 GdkGLConfig* glconfig;
844
845 CV_FUNCNAME( "createGlContext" );
846
847 __BEGIN__;
848
849 // Try double-buffered visual
850 glconfig = gdk_gl_config_new_by_mode((GdkGLConfigMode)(GDK_GL_MODE_RGB | GDK_GL_MODE_DEPTH | GDK_GL_MODE_DOUBLE));
851 if (!glconfig)
852 CV_ERROR( CV_OpenGlApiCallError, "Can't Create A GL Device Context" );
853
854 // Set OpenGL-capability to the widget
855 if (!gtk_widget_set_gl_capability(window->widget, glconfig, NULL, TRUE, GDK_GL_RGBA_TYPE))
856 CV_ERROR( CV_OpenGlApiCallError, "Can't Create A GL Device Context" );
857
858 window->useGl = true;
859
860 __END__;
861 }
862
drawGl(CvWindow * window)863 void drawGl(CvWindow* window)
864 {
865 CV_FUNCNAME( "drawGl" );
866
867 __BEGIN__;
868
869 GdkGLContext* glcontext = gtk_widget_get_gl_context(window->widget);
870 GdkGLDrawable* gldrawable = gtk_widget_get_gl_drawable(window->widget);
871
872 if (!gdk_gl_drawable_gl_begin (gldrawable, glcontext))
873 CV_ERROR( CV_OpenGlApiCallError, "Can't Activate The GL Rendering Context" );
874
875 glViewport(0, 0, window->widget->allocation.width, window->widget->allocation.height);
876
877 glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
878
879 if (window->glDrawCallback)
880 window->glDrawCallback(window->glDrawData);
881
882 if (gdk_gl_drawable_is_double_buffered (gldrawable))
883 gdk_gl_drawable_swap_buffers(gldrawable);
884 else
885 glFlush();
886
887 gdk_gl_drawable_gl_end(gldrawable);
888
889 __END__;
890 }
891 }
892
893 #endif // HAVE_OPENGL
894
895 #if defined (GTK_VERSION3)
cvImageWidget_draw(GtkWidget * widget,cairo_t * cr,gpointer data)896 static gboolean cvImageWidget_draw(GtkWidget* widget, cairo_t *cr, gpointer data)
897 {
898 #ifdef HAVE_OPENGL
899 CvWindow* window = (CvWindow*)data;
900
901 if (window->useGl)
902 {
903 drawGl(window);
904 return TRUE;
905 }
906 #else
907 (void)data;
908 #endif
909
910 CvImageWidget *image_widget = NULL;
911 GdkPixbuf *pixbuf = NULL;
912
913 g_return_val_if_fail (widget != NULL, FALSE);
914 g_return_val_if_fail (CV_IS_IMAGE_WIDGET (widget), FALSE);
915
916 image_widget = CV_IMAGE_WIDGET (widget);
917
918 if( image_widget->scaled_image ){
919 // center image in available region
920 int x0 = (gtk_widget_get_allocated_width(widget) - image_widget->scaled_image->cols)/2;
921 int y0 = (gtk_widget_get_allocated_height(widget) - image_widget->scaled_image->rows)/2;
922
923 pixbuf = gdk_pixbuf_new_from_data(image_widget->scaled_image->data.ptr, GDK_COLORSPACE_RGB, false,
924 8, MIN(image_widget->scaled_image->cols, gtk_widget_get_allocated_width(widget)),
925 MIN(image_widget->scaled_image->rows, gtk_widget_get_allocated_height(widget)),
926 image_widget->scaled_image->step, NULL, NULL);
927
928 gdk_cairo_set_source_pixbuf(cr, pixbuf, x0, y0);
929 }
930 else if( image_widget->original_image ){
931 pixbuf = gdk_pixbuf_new_from_data(image_widget->original_image->data.ptr, GDK_COLORSPACE_RGB, false,
932 8, MIN(image_widget->original_image->cols, gtk_widget_get_allocated_width(widget)),
933 MIN(image_widget->original_image->rows, gtk_widget_get_allocated_height(widget)),
934 image_widget->original_image->step, NULL, NULL);
935 gdk_cairo_set_source_pixbuf(cr, pixbuf, 0, 0);
936 }
937
938 cairo_paint(cr);
939 g_object_unref(pixbuf);
940 return TRUE;
941 }
942
943 #else
cvImageWidget_expose(GtkWidget * widget,GdkEventExpose * event,gpointer data)944 static gboolean cvImageWidget_expose(GtkWidget* widget, GdkEventExpose* event, gpointer data)
945 {
946 #ifdef HAVE_OPENGL
947 CvWindow* window = (CvWindow*)data;
948
949 if (window->useGl)
950 {
951 drawGl(window);
952 return TRUE;
953 }
954 #else
955 (void)data;
956 #endif
957
958 CvImageWidget *image_widget = NULL;
959 cairo_t *cr = NULL;
960 GdkPixbuf *pixbuf = NULL;
961
962 g_return_val_if_fail (widget != NULL, FALSE);
963 g_return_val_if_fail (CV_IS_IMAGE_WIDGET (widget), FALSE);
964 g_return_val_if_fail (event != NULL, FALSE);
965
966 if (event->count > 0)
967 return FALSE;
968
969 cr = gdk_cairo_create(widget->window);
970 image_widget = CV_IMAGE_WIDGET (widget);
971
972 if( image_widget->scaled_image ){
973 // center image in available region
974 int x0 = (widget->allocation.width - image_widget->scaled_image->cols)/2;
975 int y0 = (widget->allocation.height - image_widget->scaled_image->rows)/2;
976
977 pixbuf = gdk_pixbuf_new_from_data(image_widget->scaled_image->data.ptr, GDK_COLORSPACE_RGB, false,
978 8, MIN(image_widget->scaled_image->cols, widget->allocation.width),
979 MIN(image_widget->scaled_image->rows, widget->allocation.height),
980 image_widget->scaled_image->step, NULL, NULL);
981
982 gdk_cairo_set_source_pixbuf(cr, pixbuf, x0, y0);
983 }
984 else if( image_widget->original_image ){
985 pixbuf = gdk_pixbuf_new_from_data(image_widget->original_image->data.ptr, GDK_COLORSPACE_RGB, false,
986 8, MIN(image_widget->original_image->cols, widget->allocation.width),
987 MIN(image_widget->original_image->rows, widget->allocation.height),
988 image_widget->original_image->step, NULL, NULL);
989 gdk_cairo_set_source_pixbuf(cr, pixbuf, 0, 0);
990 }
991
992 cairo_paint(cr);
993 g_object_unref(pixbuf);
994 cairo_destroy(cr);
995 return TRUE;
996 }
997 #endif //GTK_VERSION3
998
cvNamedWindow(const char * name,int flags)999 CV_IMPL int cvNamedWindow( const char* name, int flags )
1000 {
1001 int result = 0;
1002 CV_FUNCNAME( "cvNamedWindow" );
1003
1004 __BEGIN__;
1005
1006 CvWindow* window;
1007 int len;
1008
1009 cvInitSystem(1,(char**)&name);
1010 if( !name )
1011 CV_ERROR( CV_StsNullPtr, "NULL name string" );
1012
1013 // Check the name in the storage
1014 if( icvFindWindowByName( name ) != 0 )
1015 {
1016 result = 1;
1017 EXIT;
1018 }
1019
1020 len = strlen(name);
1021 CV_CALL( window = (CvWindow*)cvAlloc(sizeof(CvWindow) + len + 1));
1022 memset( window, 0, sizeof(*window));
1023 window->name = (char*)(window + 1);
1024 memcpy( window->name, name, len + 1 );
1025 window->flags = flags;
1026 window->signature = CV_WINDOW_MAGIC_VAL;
1027 window->last_key = 0;
1028 window->on_mouse = 0;
1029 window->on_mouse_param = 0;
1030 memset( &window->toolbar, 0, sizeof(window->toolbar));
1031 window->next = hg_windows;
1032 window->prev = 0;
1033 window->status = CV_WINDOW_NORMAL;//YV
1034
1035 CV_LOCK_MUTEX();
1036
1037 window->frame = gtk_window_new( GTK_WINDOW_TOPLEVEL );
1038
1039 window->paned = gtk_vbox_new( FALSE, 0 );
1040 window->widget = cvImageWidgetNew( flags );
1041 gtk_box_pack_end( GTK_BOX(window->paned), window->widget, TRUE, TRUE, 0 );
1042 gtk_widget_show( window->widget );
1043 gtk_container_add( GTK_CONTAINER(window->frame), window->paned );
1044 gtk_widget_show( window->paned );
1045
1046 #ifndef HAVE_OPENGL
1047 if (flags & CV_WINDOW_OPENGL)
1048 CV_ERROR( CV_OpenGlNotSupported, "Library was built without OpenGL support" );
1049 #else
1050 if (flags & CV_WINDOW_OPENGL)
1051 createGlContext(window);
1052
1053 window->glDrawCallback = 0;
1054 window->glDrawData = 0;
1055 #endif
1056
1057 //
1058 // configure event handlers
1059 // TODO -- move this to CvImageWidget ?
1060 g_signal_connect( window->frame, "key-press-event",
1061 G_CALLBACK(icvOnKeyPress), window );
1062 g_signal_connect( window->widget, "button-press-event",
1063 G_CALLBACK(icvOnMouse), window );
1064 g_signal_connect( window->widget, "button-release-event",
1065 G_CALLBACK(icvOnMouse), window );
1066 g_signal_connect( window->widget, "motion-notify-event",
1067 G_CALLBACK(icvOnMouse), window );
1068 g_signal_connect( window->frame, "delete-event",
1069 G_CALLBACK(icvOnClose), window );
1070 #if defined(GTK_VERSION3)
1071 g_signal_connect( window->widget, "draw",
1072 G_CALLBACK(cvImageWidget_draw), window );
1073 #else
1074 g_signal_connect( window->widget, "expose-event",
1075 G_CALLBACK(cvImageWidget_expose), window );
1076 #endif //GTK_VERSION3
1077
1078 gtk_widget_add_events (window->widget, GDK_BUTTON_RELEASE_MASK | GDK_BUTTON_PRESS_MASK | GDK_POINTER_MOTION_MASK) ;
1079
1080 gtk_widget_show( window->frame );
1081 gtk_window_set_title( GTK_WINDOW(window->frame), name );
1082
1083 if( hg_windows )
1084 hg_windows->prev = window;
1085 hg_windows = window;
1086
1087 gtk_window_set_resizable( GTK_WINDOW(window->frame), (flags & CV_WINDOW_AUTOSIZE) == 0 );
1088
1089
1090 // allow window to be resized
1091 if( (flags & CV_WINDOW_AUTOSIZE)==0 ){
1092 GdkGeometry geometry;
1093 geometry.min_width = 50;
1094 geometry.min_height = 50;
1095 gtk_window_set_geometry_hints( GTK_WINDOW( window->frame ), GTK_WIDGET( window->widget ),
1096 &geometry, (GdkWindowHints) (GDK_HINT_MIN_SIZE));
1097 }
1098
1099 CV_UNLOCK_MUTEX();
1100
1101 #ifdef HAVE_OPENGL
1102 if (window->useGl)
1103 cvSetOpenGlContext(name);
1104 #endif
1105
1106 result = 1;
1107 __END__;
1108
1109 return result;
1110 }
1111
1112
1113 #ifdef HAVE_OPENGL
1114
cvSetOpenGlContext(const char * name)1115 CV_IMPL void cvSetOpenGlContext(const char* name)
1116 {
1117 CvWindow* window;
1118 GdkGLContext* glcontext;
1119 GdkGLDrawable* gldrawable;
1120
1121 CV_FUNCNAME( "cvSetOpenGlContext" );
1122
1123 __BEGIN__;
1124
1125 if(!name)
1126 CV_ERROR( CV_StsNullPtr, "NULL name string" );
1127
1128 window = icvFindWindowByName( name );
1129 if (!window)
1130 CV_ERROR( CV_StsNullPtr, "NULL window" );
1131
1132 if (!window->useGl)
1133 CV_ERROR( CV_OpenGlNotSupported, "Window doesn't support OpenGL" );
1134
1135 glcontext = gtk_widget_get_gl_context(window->widget);
1136 gldrawable = gtk_widget_get_gl_drawable(window->widget);
1137
1138 if (!gdk_gl_drawable_make_current(gldrawable, glcontext))
1139 CV_ERROR( CV_OpenGlApiCallError, "Can't Activate The GL Rendering Context" );
1140
1141 __END__;
1142 }
1143
cvUpdateWindow(const char * name)1144 CV_IMPL void cvUpdateWindow(const char* name)
1145 {
1146 CV_FUNCNAME( "cvUpdateWindow" );
1147
1148 __BEGIN__;
1149
1150 CvWindow* window;
1151
1152 if (!name)
1153 CV_ERROR( CV_StsNullPtr, "NULL name string" );
1154
1155 window = icvFindWindowByName( name );
1156 if (!window)
1157 EXIT;
1158
1159 // window does not refresh without this
1160 gtk_widget_queue_draw( GTK_WIDGET(window->widget) );
1161
1162 __END__;
1163 }
1164
cvSetOpenGlDrawCallback(const char * name,CvOpenGlDrawCallback callback,void * userdata)1165 CV_IMPL void cvSetOpenGlDrawCallback(const char* name, CvOpenGlDrawCallback callback, void* userdata)
1166 {
1167 CvWindow* window;
1168
1169 CV_FUNCNAME( "cvCreateOpenGLCallback" );
1170
1171 __BEGIN__;
1172
1173 if(!name)
1174 CV_ERROR( CV_StsNullPtr, "NULL name string" );
1175
1176 window = icvFindWindowByName( name );
1177 if( !window )
1178 EXIT;
1179
1180 if (!window->useGl)
1181 CV_ERROR( CV_OpenGlNotSupported, "Window was created without OpenGL context" );
1182
1183 window->glDrawCallback = callback;
1184 window->glDrawData = userdata;
1185
1186 __END__;
1187 }
1188
1189 #endif // HAVE_OPENGL
1190
1191
1192
1193
icvDeleteWindow(CvWindow * window)1194 static void icvDeleteWindow( CvWindow* window )
1195 {
1196 CvTrackbar* trackbar;
1197
1198 if( window->prev )
1199 window->prev->next = window->next;
1200 else
1201 hg_windows = window->next;
1202
1203 if( window->next )
1204 window->next->prev = window->prev;
1205
1206 window->prev = window->next = 0;
1207
1208 gtk_widget_destroy( window->frame );
1209
1210 for( trackbar = window->toolbar.first; trackbar != 0; )
1211 {
1212 CvTrackbar* next = trackbar->next;
1213 cvFree( &trackbar );
1214 trackbar = next;
1215 }
1216
1217 cvFree( &window );
1218 #ifdef HAVE_GTHREAD
1219 // if last window, send key press signal
1220 // to jump out of any waiting cvWaitKey's
1221 if(hg_windows==0 && thread_started){
1222 g_cond_broadcast(cond_have_key);
1223 }
1224 #endif
1225 }
1226
1227
cvDestroyWindow(const char * name)1228 CV_IMPL void cvDestroyWindow( const char* name )
1229 {
1230 CV_FUNCNAME( "cvDestroyWindow" );
1231
1232 __BEGIN__;
1233
1234 CvWindow* window;
1235
1236 if(!name)
1237 CV_ERROR( CV_StsNullPtr, "NULL name string" );
1238
1239 window = icvFindWindowByName( name );
1240 if( !window )
1241 EXIT;
1242
1243 // note that it is possible for the update thread to run this function
1244 // if there is a call to cvShowImage in a mouse callback
1245 // (this would produce a deadlock on window_mutex)
1246 CV_LOCK_MUTEX();
1247
1248 icvDeleteWindow( window );
1249
1250 CV_UNLOCK_MUTEX();
1251
1252 __END__;
1253 }
1254
1255
1256 CV_IMPL void
cvDestroyAllWindows(void)1257 cvDestroyAllWindows( void )
1258 {
1259 CV_LOCK_MUTEX();
1260
1261 while( hg_windows )
1262 {
1263 CvWindow* window = hg_windows;
1264 icvDeleteWindow( window );
1265 }
1266 CV_UNLOCK_MUTEX();
1267 }
1268
1269 // CvSize icvCalcOptimalWindowSize( CvWindow * window, CvSize new_image_size){
1270 // CvSize window_size;
1271 // GtkWidget * toplevel = gtk_widget_get_toplevel( window->frame );
1272 // gdk_drawable_get_size( GDK_DRAWABLE(toplevel->window),
1273 // &window_size.width, &window_size.height );
1274
1275 // window_size.width = window_size.width + new_image_size.width - window->widget->allocation.width;
1276 // window_size.height = window_size.height + new_image_size.height - window->widget->allocation.height;
1277
1278 // return window_size;
1279 // }
1280
1281 CV_IMPL void
cvShowImage(const char * name,const CvArr * arr)1282 cvShowImage( const char* name, const CvArr* arr )
1283 {
1284 CV_FUNCNAME( "cvShowImage" );
1285
1286 __BEGIN__;
1287
1288 CvWindow* window;
1289
1290 if( !name )
1291 CV_ERROR( CV_StsNullPtr, "NULL name" );
1292
1293 CV_LOCK_MUTEX();
1294
1295 window = icvFindWindowByName(name);
1296 if(!window)
1297 {
1298 cvNamedWindow(name, 1);
1299 window = icvFindWindowByName(name);
1300 }
1301
1302 if( window && arr )
1303 {
1304 #ifdef HAVE_OPENGL
1305 if (window->useGl)
1306 {
1307 cv::imshow(name, cv::cvarrToMat(arr));
1308 return;
1309 }
1310 #endif
1311
1312 CvImageWidget * image_widget = CV_IMAGE_WIDGET( window->widget );
1313 cvImageWidgetSetImage( image_widget, arr );
1314 }
1315
1316 CV_UNLOCK_MUTEX();
1317
1318 __END__;
1319 }
1320
cvResizeWindow(const char * name,int width,int height)1321 CV_IMPL void cvResizeWindow(const char* name, int width, int height )
1322 {
1323 CV_FUNCNAME( "cvResizeWindow" );
1324
1325 __BEGIN__;
1326
1327 CvWindow* window;
1328 CvImageWidget * image_widget;
1329
1330 if( !name )
1331 CV_ERROR( CV_StsNullPtr, "NULL name" );
1332
1333 window = icvFindWindowByName(name);
1334 if(!window)
1335 EXIT;
1336
1337 image_widget = CV_IMAGE_WIDGET( window->widget );
1338 //if(image_widget->flags & CV_WINDOW_AUTOSIZE)
1339 //EXIT;
1340
1341 CV_LOCK_MUTEX();
1342
1343 gtk_window_set_resizable( GTK_WINDOW(window->frame), 1 );
1344 gtk_window_resize( GTK_WINDOW(window->frame), width, height );
1345
1346 // disable initial resize since presumably user wants to keep
1347 // this window size
1348 image_widget->flags &= ~CV_WINDOW_NO_IMAGE;
1349
1350 CV_UNLOCK_MUTEX();
1351
1352 __END__;
1353 }
1354
1355
cvMoveWindow(const char * name,int x,int y)1356 CV_IMPL void cvMoveWindow( const char* name, int x, int y )
1357 {
1358 CV_FUNCNAME( "cvMoveWindow" );
1359
1360 __BEGIN__;
1361
1362 CvWindow* window;
1363
1364 if( !name )
1365 CV_ERROR( CV_StsNullPtr, "NULL name" );
1366
1367 window = icvFindWindowByName(name);
1368 if(!window)
1369 EXIT;
1370
1371 CV_LOCK_MUTEX();
1372
1373 gtk_window_move( GTK_WINDOW(window->frame), x, y );
1374
1375 CV_UNLOCK_MUTEX();
1376
1377 __END__;
1378 }
1379
1380
1381 static CvTrackbar*
icvFindTrackbarByName(const CvWindow * window,const char * name)1382 icvFindTrackbarByName( const CvWindow* window, const char* name )
1383 {
1384 CvTrackbar* trackbar = window->toolbar.first;
1385
1386 for( ; trackbar != 0 && strcmp( trackbar->name, name ) != 0; trackbar = trackbar->next )
1387 ;
1388
1389 return trackbar;
1390 }
1391
1392 static int
icvCreateTrackbar(const char * trackbar_name,const char * window_name,int * val,int count,CvTrackbarCallback on_notify,CvTrackbarCallback2 on_notify2,void * userdata)1393 icvCreateTrackbar( const char* trackbar_name, const char* window_name,
1394 int* val, int count, CvTrackbarCallback on_notify,
1395 CvTrackbarCallback2 on_notify2, void* userdata )
1396 {
1397 int result = 0;
1398
1399 CV_FUNCNAME( "icvCreateTrackbar" );
1400
1401 __BEGIN__;
1402
1403 /*char slider_name[32];*/
1404 CvWindow* window = 0;
1405 CvTrackbar* trackbar = 0;
1406
1407 if( !window_name || !trackbar_name )
1408 CV_ERROR( CV_StsNullPtr, "NULL window or trackbar name" );
1409
1410 if( count <= 0 )
1411 CV_ERROR( CV_StsOutOfRange, "Bad trackbar maximal value" );
1412
1413 window = icvFindWindowByName(window_name);
1414 if( !window )
1415 EXIT;
1416
1417 trackbar = icvFindTrackbarByName(window,trackbar_name);
1418
1419 CV_LOCK_MUTEX();
1420
1421 if( !trackbar )
1422 {
1423 int len = strlen(trackbar_name);
1424 trackbar = (CvTrackbar*)cvAlloc(sizeof(CvTrackbar) + len + 1);
1425 memset( trackbar, 0, sizeof(*trackbar));
1426 trackbar->signature = CV_TRACKBAR_MAGIC_VAL;
1427 trackbar->name = (char*)(trackbar+1);
1428 memcpy( trackbar->name, trackbar_name, len + 1 );
1429 trackbar->parent = window;
1430 trackbar->next = window->toolbar.first;
1431 window->toolbar.first = trackbar;
1432
1433 GtkWidget* hscale_box = gtk_hbox_new( FALSE, 10 );
1434 GtkWidget* hscale_label = gtk_label_new( trackbar_name );
1435 GtkWidget* hscale = gtk_hscale_new_with_range( 0, count, 1 );
1436 gtk_scale_set_digits( GTK_SCALE(hscale), 0 );
1437 //gtk_scale_set_value_pos( hscale, GTK_POS_TOP );
1438 gtk_scale_set_draw_value( GTK_SCALE(hscale), TRUE );
1439
1440 trackbar->widget = hscale;
1441 gtk_box_pack_start( GTK_BOX(hscale_box), hscale_label, FALSE, FALSE, 5 );
1442 gtk_widget_show( hscale_label );
1443 gtk_box_pack_start( GTK_BOX(hscale_box), hscale, TRUE, TRUE, 5 );
1444 gtk_widget_show( hscale );
1445 gtk_box_pack_start( GTK_BOX(window->paned), hscale_box, FALSE, FALSE, 5 );
1446 gtk_widget_show( hscale_box );
1447
1448 }
1449
1450 if( val )
1451 {
1452 int value = *val;
1453 if( value < 0 )
1454 value = 0;
1455 if( value > count )
1456 value = count;
1457 gtk_range_set_value( GTK_RANGE(trackbar->widget), value );
1458 trackbar->pos = value;
1459 trackbar->data = val;
1460 }
1461
1462 trackbar->maxval = count;
1463 trackbar->notify = on_notify;
1464 trackbar->notify2 = on_notify2;
1465 trackbar->userdata = userdata;
1466 g_signal_connect( trackbar->widget, "value-changed",
1467 G_CALLBACK(icvOnTrackbar), trackbar );
1468
1469 // queue a widget resize to trigger a window resize to
1470 // compensate for the addition of trackbars
1471 gtk_widget_queue_resize( GTK_WIDGET(window->widget) );
1472
1473
1474 CV_UNLOCK_MUTEX();
1475
1476 result = 1;
1477
1478 __END__;
1479
1480 return result;
1481 }
1482
1483
1484 CV_IMPL int
cvCreateTrackbar(const char * trackbar_name,const char * window_name,int * val,int count,CvTrackbarCallback on_notify)1485 cvCreateTrackbar( const char* trackbar_name, const char* window_name,
1486 int* val, int count, CvTrackbarCallback on_notify )
1487 {
1488 return icvCreateTrackbar(trackbar_name, window_name, val, count,
1489 on_notify, 0, 0);
1490 }
1491
1492
1493 CV_IMPL int
cvCreateTrackbar2(const char * trackbar_name,const char * window_name,int * val,int count,CvTrackbarCallback2 on_notify2,void * userdata)1494 cvCreateTrackbar2( const char* trackbar_name, const char* window_name,
1495 int* val, int count, CvTrackbarCallback2 on_notify2,
1496 void* userdata )
1497 {
1498 return icvCreateTrackbar(trackbar_name, window_name, val, count,
1499 0, on_notify2, userdata);
1500 }
1501
1502
1503 CV_IMPL void
cvSetMouseCallback(const char * window_name,CvMouseCallback on_mouse,void * param)1504 cvSetMouseCallback( const char* window_name, CvMouseCallback on_mouse, void* param )
1505 {
1506 CV_FUNCNAME( "cvSetMouseCallback" );
1507
1508 __BEGIN__;
1509
1510 CvWindow* window = 0;
1511
1512 if( !window_name )
1513 CV_ERROR( CV_StsNullPtr, "NULL window name" );
1514
1515 window = icvFindWindowByName(window_name);
1516 if( !window )
1517 EXIT;
1518
1519 window->on_mouse = on_mouse;
1520 window->on_mouse_param = param;
1521
1522 __END__;
1523 }
1524
1525
cvGetTrackbarPos(const char * trackbar_name,const char * window_name)1526 CV_IMPL int cvGetTrackbarPos( const char* trackbar_name, const char* window_name )
1527 {
1528 int pos = -1;
1529
1530 CV_FUNCNAME( "cvGetTrackbarPos" );
1531
1532 __BEGIN__;
1533
1534 CvWindow* window;
1535 CvTrackbar* trackbar = 0;
1536
1537 if( trackbar_name == 0 || window_name == 0 )
1538 CV_ERROR( CV_StsNullPtr, "NULL trackbar or window name" );
1539
1540 window = icvFindWindowByName( window_name );
1541 if( window )
1542 trackbar = icvFindTrackbarByName( window, trackbar_name );
1543
1544 if( trackbar )
1545 pos = trackbar->pos;
1546
1547 __END__;
1548
1549 return pos;
1550 }
1551
1552
cvSetTrackbarPos(const char * trackbar_name,const char * window_name,int pos)1553 CV_IMPL void cvSetTrackbarPos( const char* trackbar_name, const char* window_name, int pos )
1554 {
1555 CV_FUNCNAME( "cvSetTrackbarPos" );
1556
1557 __BEGIN__;
1558
1559 CvWindow* window;
1560 CvTrackbar* trackbar = 0;
1561
1562 if( trackbar_name == 0 || window_name == 0 )
1563 CV_ERROR( CV_StsNullPtr, "NULL trackbar or window name" );
1564
1565 window = icvFindWindowByName( window_name );
1566 if( window )
1567 trackbar = icvFindTrackbarByName( window, trackbar_name );
1568
1569 if( trackbar )
1570 {
1571 if( pos < 0 )
1572 pos = 0;
1573
1574 if( pos > trackbar->maxval )
1575 pos = trackbar->maxval;
1576 }
1577
1578 CV_LOCK_MUTEX();
1579
1580 gtk_range_set_value( GTK_RANGE(trackbar->widget), pos );
1581
1582 CV_UNLOCK_MUTEX();
1583
1584 __END__;
1585 }
1586
1587
cvSetTrackbarMax(const char * trackbar_name,const char * window_name,int maxval)1588 CV_IMPL void cvSetTrackbarMax(const char* trackbar_name, const char* window_name, int maxval)
1589 {
1590 CV_FUNCNAME("cvSetTrackbarMax");
1591
1592 __BEGIN__;
1593
1594 if (maxval >= 0)
1595 {
1596 CvWindow* window = 0;
1597 CvTrackbar* trackbar = 0;
1598
1599 if (trackbar_name == 0 || window_name == 0)
1600 {
1601 CV_ERROR( CV_StsNullPtr, "NULL trackbar or window name");
1602 }
1603
1604 window = icvFindWindowByName( window_name );
1605 if (window)
1606 {
1607 trackbar = icvFindTrackbarByName(window, trackbar_name);
1608 if (trackbar)
1609 {
1610 trackbar->maxval = maxval;
1611
1612 CV_LOCK_MUTEX();
1613
1614 gtk_range_set_range(GTK_RANGE(trackbar->widget), 0, trackbar->maxval);
1615
1616 CV_UNLOCK_MUTEX();
1617 }
1618 }
1619 }
1620
1621 __END__;
1622 }
1623
1624
cvGetWindowHandle(const char * window_name)1625 CV_IMPL void* cvGetWindowHandle( const char* window_name )
1626 {
1627 void* widget = 0;
1628
1629 CV_FUNCNAME( "cvGetWindowHandle" );
1630
1631 __BEGIN__;
1632
1633 CvWindow* window;
1634
1635 if( window_name == 0 )
1636 CV_ERROR( CV_StsNullPtr, "NULL window name" );
1637
1638 window = icvFindWindowByName( window_name );
1639 if( window )
1640 widget = (void*)window->widget;
1641
1642 __END__;
1643
1644 return widget;
1645 }
1646
1647
cvGetWindowName(void * window_handle)1648 CV_IMPL const char* cvGetWindowName( void* window_handle )
1649 {
1650 const char* window_name = "";
1651
1652 CV_FUNCNAME( "cvGetWindowName" );
1653
1654 __BEGIN__;
1655
1656 CvWindow* window;
1657
1658 if( window_handle == 0 )
1659 CV_ERROR( CV_StsNullPtr, "NULL window" );
1660
1661 window = icvWindowByWidget( (GtkWidget*)window_handle );
1662 if( window )
1663 window_name = window->name;
1664
1665 __END__;
1666
1667 return window_name;
1668 }
1669
icvMakeGtkFilter(const char * name,const char * patterns,GtkFileFilter * images)1670 static GtkFileFilter* icvMakeGtkFilter(const char* name, const char* patterns, GtkFileFilter* images)
1671 {
1672 GtkFileFilter* filter = gtk_file_filter_new();
1673 gtk_file_filter_set_name(filter, name);
1674
1675 while(patterns[0])
1676 {
1677 gtk_file_filter_add_pattern(filter, patterns);
1678 gtk_file_filter_add_pattern(images, patterns);
1679 patterns += strlen(patterns) + 1;
1680 }
1681
1682 return filter;
1683 }
1684
icvShowSaveAsDialog(GtkWidget * widget,CvWindow * window)1685 static void icvShowSaveAsDialog(GtkWidget* widget, CvWindow* window)
1686 {
1687 if (!window || !widget)
1688 return;
1689
1690 CvImageWidget* image_widget = CV_IMAGE_WIDGET(window->widget);
1691 if (!image_widget || !image_widget->original_image)
1692 return;
1693
1694 GtkWidget* dialog = gtk_file_chooser_dialog_new("Save As...",
1695 GTK_WINDOW(widget),
1696 GTK_FILE_CHOOSER_ACTION_SAVE,
1697 GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
1698 GTK_STOCK_SAVE, GTK_RESPONSE_ACCEPT,
1699 NULL);
1700 gtk_file_chooser_set_do_overwrite_confirmation(GTK_FILE_CHOOSER(dialog), TRUE);
1701
1702 cv::String sname = gtk_window_get_title(GTK_WINDOW(window->frame));
1703 sname = sname.substr(sname.find_last_of("\\/") + 1) + ".png";
1704 gtk_file_chooser_set_current_name(GTK_FILE_CHOOSER(dialog), sname.c_str());
1705
1706 GtkFileFilter* filter_all = gtk_file_filter_new();
1707 gtk_file_filter_set_name(filter_all, "All Files");
1708 gtk_file_filter_add_pattern(filter_all, "*");
1709
1710 GtkFileFilter* filter_images = gtk_file_filter_new();
1711 gtk_file_filter_set_name(filter_images, "All Images");
1712
1713 GtkFileFilter* file_filters[] = {
1714 icvMakeGtkFilter("Portable Network Graphics files (*.png)", "*.png\0", filter_images),
1715 icvMakeGtkFilter("JPEG files (*.jpeg;*.jpg;*.jpe)", "*.jpeg\0*.jpg\0*.jpe\0", filter_images),
1716 icvMakeGtkFilter("Windows bitmap (*.bmp;*.dib)", "*.bmp\0*.dib\0", filter_images),
1717 icvMakeGtkFilter("TIFF Files (*.tiff;*.tif)", "*.tiff\0*.tif\0", filter_images),
1718 icvMakeGtkFilter("JPEG-2000 files (*.jp2)", "*.jp2\0", filter_images),
1719 icvMakeGtkFilter("WebP files (*.webp)", "*.webp\0", filter_images),
1720 icvMakeGtkFilter("Portable image format (*.pbm;*.pgm;*.ppm;*.pxm;*.pnm)", "*.pbm\0*.pgm\0*.ppm\0*.pxm\0*.pnm\0", filter_images),
1721 icvMakeGtkFilter("OpenEXR Image files (*.exr)", "*.exr\0", filter_images),
1722 icvMakeGtkFilter("Radiance HDR (*.hdr;*.pic)", "*.hdr\0*.pic\0", filter_images),
1723 icvMakeGtkFilter("Sun raster files (*.sr;*.ras)", "*.sr\0*.ras\0", filter_images),
1724 filter_images,
1725 filter_all
1726 };
1727
1728 for (size_t idx = 0; idx < sizeof(file_filters)/sizeof(file_filters[0]); ++idx)
1729 gtk_file_chooser_add_filter(GTK_FILE_CHOOSER(dialog), file_filters[idx]);
1730 gtk_file_chooser_set_filter(GTK_FILE_CHOOSER(dialog), filter_images);
1731
1732 cv::String filename;
1733 if (gtk_dialog_run(GTK_DIALOG(dialog)) == GTK_RESPONSE_ACCEPT)
1734 {
1735 char* fname = gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(dialog));
1736 filename = fname;
1737 g_free(fname);
1738 }
1739 gtk_widget_destroy(dialog);
1740
1741 if (!filename.empty())
1742 {
1743 cv::Mat bgr;
1744 cv::cvtColor(cv::cvarrToMat(image_widget->original_image), bgr, cv::COLOR_RGB2BGR);
1745 cv::imwrite(filename, bgr);
1746 }
1747 }
1748
1749 #if defined (GTK_VERSION3)
1750 #define GDK_Escape GDK_KEY_Escape
1751 #define GDK_Return GDK_KEY_Return
1752 #define GDK_Linefeed GDK_KEY_Linefeed
1753 #define GDK_Tab GDK_KEY_Tab
1754 #define GDK_s GDK_KEY_s
1755 #define GDK_S GDK_KEY_S
1756 #endif //GTK_VERSION3
1757
icvOnKeyPress(GtkWidget * widget,GdkEventKey * event,gpointer user_data)1758 static gboolean icvOnKeyPress(GtkWidget* widget, GdkEventKey* event, gpointer user_data)
1759 {
1760 int code = 0;
1761
1762 if ( (event->state & GDK_CONTROL_MASK) == GDK_CONTROL_MASK && (event->keyval == GDK_s || event->keyval == GDK_S))
1763 {
1764 try
1765 {
1766 icvShowSaveAsDialog(widget, (CvWindow*)user_data);
1767 }
1768 catch(...)
1769 {
1770 // suppress all exceptions here
1771 }
1772 }
1773
1774 switch( event->keyval )
1775 {
1776 case GDK_Escape:
1777 code = 27;
1778 break;
1779 case GDK_Return:
1780 case GDK_Linefeed:
1781 code = '\n';
1782 break;
1783 case GDK_Tab:
1784 code = '\t';
1785 break;
1786 default:
1787 code = event->keyval;
1788 }
1789
1790 code |= event->state << 16;
1791
1792 #ifdef HAVE_GTHREAD
1793 if(thread_started) g_mutex_lock(last_key_mutex);
1794 #endif
1795
1796 last_key = code;
1797
1798 #ifdef HAVE_GTHREAD
1799 if(thread_started){
1800 // signal any waiting threads
1801 g_cond_broadcast(cond_have_key);
1802 g_mutex_unlock(last_key_mutex);
1803 }
1804 #endif
1805
1806 return FALSE;
1807 }
1808
1809
icvOnTrackbar(GtkWidget * widget,gpointer user_data)1810 static void icvOnTrackbar( GtkWidget* widget, gpointer user_data )
1811 {
1812 int pos = cvRound( gtk_range_get_value(GTK_RANGE(widget)));
1813 CvTrackbar* trackbar = (CvTrackbar*)user_data;
1814
1815 if( trackbar && trackbar->signature == CV_TRACKBAR_MAGIC_VAL &&
1816 trackbar->widget == widget )
1817 {
1818 trackbar->pos = pos;
1819 if( trackbar->data )
1820 *trackbar->data = pos;
1821 if( trackbar->notify2 )
1822 trackbar->notify2(pos, trackbar->userdata);
1823 else if( trackbar->notify )
1824 trackbar->notify(pos);
1825 }
1826 }
1827
icvOnClose(GtkWidget * widget,GdkEvent *,gpointer user_data)1828 static gboolean icvOnClose( GtkWidget* widget, GdkEvent* /*event*/, gpointer user_data )
1829 {
1830 CvWindow* window = (CvWindow*)user_data;
1831 if( window->signature == CV_WINDOW_MAGIC_VAL &&
1832 window->frame == widget )
1833 {
1834 icvDeleteWindow(window);
1835 }
1836 return TRUE;
1837 }
1838
1839
icvOnMouse(GtkWidget * widget,GdkEvent * event,gpointer user_data)1840 static gboolean icvOnMouse( GtkWidget *widget, GdkEvent *event, gpointer user_data )
1841 {
1842 // TODO move this logic to CvImageWidget
1843 CvWindow* window = (CvWindow*)user_data;
1844 CvPoint2D32f pt32f(-1., -1.);
1845 CvPoint pt(-1,-1);
1846 int cv_event = -1, state = 0;
1847 CvImageWidget * image_widget = CV_IMAGE_WIDGET( widget );
1848
1849 if( window->signature != CV_WINDOW_MAGIC_VAL ||
1850 window->widget != widget || !window->widget ||
1851 !window->on_mouse /*|| !image_widget->original_image*/)
1852 return FALSE;
1853
1854 if( event->type == GDK_MOTION_NOTIFY )
1855 {
1856 GdkEventMotion* event_motion = (GdkEventMotion*)event;
1857
1858 cv_event = CV_EVENT_MOUSEMOVE;
1859 pt32f.x = cvRound(event_motion->x);
1860 pt32f.y = cvRound(event_motion->y);
1861 state = event_motion->state;
1862 }
1863 else if( event->type == GDK_BUTTON_PRESS ||
1864 event->type == GDK_BUTTON_RELEASE ||
1865 event->type == GDK_2BUTTON_PRESS )
1866 {
1867 GdkEventButton* event_button = (GdkEventButton*)event;
1868 pt32f.x = cvRound(event_button->x);
1869 pt32f.y = cvRound(event_button->y);
1870
1871
1872 if( event_button->type == GDK_BUTTON_PRESS )
1873 {
1874 cv_event = event_button->button == 1 ? CV_EVENT_LBUTTONDOWN :
1875 event_button->button == 2 ? CV_EVENT_MBUTTONDOWN :
1876 event_button->button == 3 ? CV_EVENT_RBUTTONDOWN : 0;
1877 }
1878 else if( event_button->type == GDK_BUTTON_RELEASE )
1879 {
1880 cv_event = event_button->button == 1 ? CV_EVENT_LBUTTONUP :
1881 event_button->button == 2 ? CV_EVENT_MBUTTONUP :
1882 event_button->button == 3 ? CV_EVENT_RBUTTONUP : 0;
1883 }
1884 else if( event_button->type == GDK_2BUTTON_PRESS )
1885 {
1886 cv_event = event_button->button == 1 ? CV_EVENT_LBUTTONDBLCLK :
1887 event_button->button == 2 ? CV_EVENT_MBUTTONDBLCLK :
1888 event_button->button == 3 ? CV_EVENT_RBUTTONDBLCLK : 0;
1889 }
1890 state = event_button->state;
1891 }
1892
1893 if( cv_event >= 0 ){
1894 // scale point if image is scaled
1895 if( (image_widget->flags & CV_WINDOW_AUTOSIZE)==0 &&
1896 image_widget->original_image &&
1897 image_widget->scaled_image ){
1898 // image origin is not necessarily at (0,0)
1899 #if defined (GTK_VERSION3)
1900 int x0 = (gtk_widget_get_allocated_width(widget) - image_widget->scaled_image->cols)/2;
1901 int y0 = (gtk_widget_get_allocated_height(widget) - image_widget->scaled_image->rows)/2;
1902 #else
1903 int x0 = (widget->allocation.width - image_widget->scaled_image->cols)/2;
1904 int y0 = (widget->allocation.height - image_widget->scaled_image->rows)/2;
1905 #endif //GTK_VERSION3
1906 pt.x = cvFloor( ((pt32f.x-x0)*image_widget->original_image->cols)/
1907 image_widget->scaled_image->cols );
1908 pt.y = cvFloor( ((pt32f.y-y0)*image_widget->original_image->rows)/
1909 image_widget->scaled_image->rows );
1910 }
1911 else{
1912 pt = cvPointFrom32f( pt32f );
1913 }
1914
1915 // if((unsigned)pt.x < (unsigned)(image_widget->original_image->width) &&
1916 // (unsigned)pt.y < (unsigned)(image_widget->original_image->height) )
1917 {
1918 int flags = (state & GDK_SHIFT_MASK ? CV_EVENT_FLAG_SHIFTKEY : 0) |
1919 (state & GDK_CONTROL_MASK ? CV_EVENT_FLAG_CTRLKEY : 0) |
1920 (state & (GDK_MOD1_MASK|GDK_MOD2_MASK) ? CV_EVENT_FLAG_ALTKEY : 0) |
1921 (state & GDK_BUTTON1_MASK ? CV_EVENT_FLAG_LBUTTON : 0) |
1922 (state & GDK_BUTTON2_MASK ? CV_EVENT_FLAG_MBUTTON : 0) |
1923 (state & GDK_BUTTON3_MASK ? CV_EVENT_FLAG_RBUTTON : 0);
1924 window->on_mouse( cv_event, pt.x, pt.y, flags, window->on_mouse_param );
1925 }
1926 }
1927
1928 return FALSE;
1929 }
1930
1931
icvAlarm(gpointer user_data)1932 static gboolean icvAlarm( gpointer user_data )
1933 {
1934 *(int*)user_data = 1;
1935 return FALSE;
1936 }
1937
1938
cvWaitKey(int delay)1939 CV_IMPL int cvWaitKey( int delay )
1940 {
1941 #ifdef HAVE_GTHREAD
1942 if(thread_started && g_thread_self()!=window_thread){
1943 gboolean expired;
1944 int my_last_key;
1945
1946 // wait for signal or timeout if delay > 0
1947 if(delay>0){
1948 GTimeVal timer;
1949 g_get_current_time(&timer);
1950 g_time_val_add(&timer, delay*1000);
1951 expired = !g_cond_timed_wait(cond_have_key, last_key_mutex, &timer);
1952 }
1953 else{
1954 g_cond_wait(cond_have_key, last_key_mutex);
1955 expired=false;
1956 }
1957 my_last_key = last_key;
1958 g_mutex_unlock(last_key_mutex);
1959 if(expired || hg_windows==0){
1960 return -1;
1961 }
1962 return my_last_key;
1963 }
1964 else{
1965 #endif
1966 int expired = 0;
1967 guint timer = 0;
1968 if( delay > 0 )
1969 timer = g_timeout_add( delay, icvAlarm, &expired );
1970 last_key = -1;
1971 while( gtk_main_iteration_do(TRUE) && last_key < 0 && !expired && hg_windows != 0 )
1972 ;
1973
1974 if( delay > 0 && !expired )
1975 g_source_remove(timer);
1976 #ifdef HAVE_GTHREAD
1977 }
1978 #endif
1979 return last_key;
1980 }
1981
1982
1983 #endif // HAVE_GTK
1984 #endif // WIN32
1985
1986 /* End of file. */
1987