Update to 2.0.0 tree from current Fremantle build
[opencv] / src / highgui / window_gtk.cpp
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 "_highgui.h"
43
44 #ifndef WIN32
45
46 #ifdef HAVE_GTK
47
48 #include "gtk/gtk.h"
49 #include "gdk/gdkkeysyms.h"
50 #include <stdio.h>
51
52 #include "cv.h"
53 /*#if _MSC_VER >= 1200
54 #pragma warning( disable: 4505 )
55 #pragma comment(lib,"gtk-win32-2.0.lib")
56 #pragma comment(lib,"glib-2.0.lib")
57 #pragma comment(lib,"gobject-2.0.lib")
58 #pragma comment(lib,"gdk-win32-2.0.lib")
59 #pragma comment(lib,"gdk_pixbuf-2.0.lib")
60 #endif*/
61
62
63 // TODO Fix the initial window size when flags=0.  Right now the initial window is by default
64 // 320x240 size.  A better default would be actual size of the image.  Problem
65 // is determining desired window size with trackbars while still allowing resizing.
66 //
67 // Gnome Totem source may be of use here, see bacon_video_widget_set_scale_ratio
68 // in totem/src/backend/bacon-video-widget-xine.c
69
70 ////////////////////////////////////////////////////////////
71 // CvImageWidget GTK Widget Public API
72 ////////////////////////////////////////////////////////////
73 typedef struct _CvImageWidget        CvImageWidget;
74 typedef struct _CvImageWidgetClass   CvImageWidgetClass;
75
76 struct _CvImageWidget {
77         GtkWidget widget;
78         CvMat * original_image;
79         CvMat * scaled_image;
80         int flags;
81 };
82
83 struct _CvImageWidgetClass
84 {
85   GtkWidgetClass parent_class;
86 };
87
88
89 /** Allocate new image viewer widget */
90 GtkWidget*     cvImageWidgetNew      (int flags);
91
92 /** Set the image to display in the widget */
93 void           cvImageWidgetSetImage(CvImageWidget * widget, const CvArr *arr);
94
95 // standard GTK object macros
96 #define CV_IMAGE_WIDGET(obj)          GTK_CHECK_CAST (obj, cvImageWidget_get_type (), CvImageWidget)
97 #define CV_IMAGE_WIDGET_CLASS(klass)  GTK_CHECK_CLASS_CAST (klass, cvImageWidget_get_type (), CvImageWidgetClass)
98 #define CV_IS_IMAGE_WIDGET(obj)       GTK_CHECK_TYPE (obj, cvImageWidget_get_type ())
99
100 /////////////////////////////////////////////////////////////////////////////
101 // Private API ////////////////////////////////////////////////////////
102 /////////////////////////////////////////////////////////////////////////////
103 GtkType        cvImageWidget_get_type (void);
104
105 static GtkWidgetClass * parent_class = NULL;
106
107 // flag to help size initial window
108 #define CV_WINDOW_NO_IMAGE 2
109
110 void cvImageWidgetSetImage(CvImageWidget * widget, const CvArr *arr){
111         CvMat * mat, stub;
112         int origin=0;
113
114         //printf("cvImageWidgetSetImage\n");
115
116         if( CV_IS_IMAGE_HDR( arr ))
117                 origin = ((IplImage*)arr)->origin;
118
119         mat = cvGetMat(arr, &stub);
120
121         if(widget->original_image && !CV_ARE_SIZES_EQ(mat, widget->original_image)){
122                 cvReleaseMat( &widget->original_image );
123         }
124         if(!widget->original_image){
125                 widget->original_image = cvCreateMat( mat->rows, mat->cols, CV_8UC3 );
126                 gtk_widget_queue_resize( GTK_WIDGET( widget ) );
127         }
128         cvConvertImage( mat, widget->original_image,
129                                         (origin != 0 ? CV_CVTIMG_FLIP : 0) + CV_CVTIMG_SWAP_RB );
130         if(widget->scaled_image){
131                 cvResize( widget->original_image, widget->scaled_image, CV_INTER_AREA );
132         }
133
134         // window does not refresh without this
135         gtk_widget_queue_draw( GTK_WIDGET(widget) );
136 }
137
138 GtkWidget*
139 cvImageWidgetNew (int flags)
140 {
141   CvImageWidget *image_widget;
142
143   image_widget = CV_IMAGE_WIDGET( gtk_type_new (cvImageWidget_get_type ()) );
144   image_widget->original_image = 0;
145   image_widget->scaled_image = 0;
146   image_widget->flags = flags | CV_WINDOW_NO_IMAGE;
147
148   return GTK_WIDGET (image_widget);
149 }
150
151 static void
152 cvImageWidget_realize (GtkWidget *widget)
153 {
154   CvImageWidget *image_widget;
155   GdkWindowAttr attributes;
156   gint attributes_mask;
157
158   //printf("cvImageWidget_realize\n");
159   g_return_if_fail (widget != NULL);
160   g_return_if_fail (CV_IS_IMAGE_WIDGET (widget));
161
162   GTK_WIDGET_SET_FLAGS (widget, GTK_REALIZED);
163   image_widget = CV_IMAGE_WIDGET (widget);
164
165   attributes.x = widget->allocation.x;
166   attributes.y = widget->allocation.y;
167   attributes.width = widget->allocation.width;
168   attributes.height = widget->allocation.height;
169   attributes.wclass = GDK_INPUT_OUTPUT;
170   attributes.window_type = GDK_WINDOW_CHILD;
171   attributes.event_mask = gtk_widget_get_events (widget) |
172     GDK_EXPOSURE_MASK | GDK_BUTTON_PRESS_MASK |
173     GDK_BUTTON_RELEASE_MASK | GDK_POINTER_MOTION_MASK;
174   attributes.visual = gtk_widget_get_visual (widget);
175   attributes.colormap = gtk_widget_get_colormap (widget);
176
177   attributes_mask = GDK_WA_X | GDK_WA_Y | GDK_WA_VISUAL | GDK_WA_COLORMAP;
178   widget->window = gdk_window_new (widget->parent->window, &attributes, attributes_mask);
179
180   widget->style = gtk_style_attach (widget->style, widget->window);
181
182   gdk_window_set_user_data (widget->window, widget);
183
184   gtk_style_set_background (widget->style, widget->window, GTK_STATE_ACTIVE);
185 }
186
187 static CvSize cvImageWidget_calc_size( int im_width, int im_height, int max_width, int max_height ){
188     float aspect = (float)im_width/(float)im_height;
189     float max_aspect = (float)max_width/(float)max_height;
190     if(aspect > max_aspect){
191         return cvSize( max_width, cvRound(max_width/aspect) );
192     }
193     return cvSize( cvRound(max_height*aspect), max_height );
194 }
195
196 static void
197 cvImageWidget_size_request (GtkWidget      *widget,
198                        GtkRequisition *requisition)
199 {
200         CvImageWidget * image_widget = CV_IMAGE_WIDGET( widget );
201
202         //printf("cvImageWidget_size_request ");
203         // the case the first time cvShowImage called or when AUTOSIZE
204         if( image_widget->original_image &&
205                 ((image_widget->flags & CV_WINDOW_AUTOSIZE) ||
206                  (image_widget->flags & CV_WINDOW_NO_IMAGE)))
207         {
208                 //printf("original ");
209                 requisition->width = image_widget->original_image->cols;
210                 requisition->height = image_widget->original_image->rows;
211         }
212         // default case
213         else if(image_widget->scaled_image){
214                 //printf("scaled ");
215                 requisition->width = image_widget->scaled_image->cols;
216                 requisition->height = image_widget->scaled_image->rows;
217         }
218         // the case before cvShowImage called
219         else{
220                 //printf("default ");
221                 requisition->width = 320;
222                 requisition->height = 240;
223         }
224         //printf("%d %d\n",requisition->width, requisition->height);
225 }
226
227 static void cvImageWidget_set_size(GtkWidget * widget, int max_width, int max_height){
228         CvImageWidget * image_widget = CV_IMAGE_WIDGET( widget );
229
230         //printf("cvImageWidget_set_size %d %d\n", max_width, max_height);
231
232         // don't allow to set the size
233         if(image_widget->flags & CV_WINDOW_AUTOSIZE) return;
234         if(!image_widget->original_image) return;
235
236         CvSize scaled_image_size = cvImageWidget_calc_size( image_widget->original_image->cols,
237                         image_widget->original_image->rows, max_width, max_height );
238
239         if( image_widget->scaled_image &&
240                         ( image_widget->scaled_image->cols != scaled_image_size.width ||
241                           image_widget->scaled_image->rows != scaled_image_size.height ))
242         {
243                 cvReleaseMat( &image_widget->scaled_image );
244         }
245         if( !image_widget->scaled_image ){
246                 image_widget->scaled_image = cvCreateMat( scaled_image_size.height, scaled_image_size.width,        CV_8UC3 );
247
248
249         }
250         assert( image_widget->scaled_image );
251 }
252
253 static void
254 cvImageWidget_size_allocate (GtkWidget     *widget,
255                         GtkAllocation *allocation)
256 {
257   CvImageWidget *image_widget;
258
259   //printf("cvImageWidget_size_allocate\n");
260   g_return_if_fail (widget != NULL);
261   g_return_if_fail (CV_IS_IMAGE_WIDGET (widget));
262   g_return_if_fail (allocation != NULL);
263
264   widget->allocation = *allocation;
265   image_widget = CV_IMAGE_WIDGET (widget);
266
267
268   if( (image_widget->flags & CV_WINDOW_AUTOSIZE)==0 && image_widget->original_image ){
269           // (re) allocated scaled image
270           if( image_widget->flags & CV_WINDOW_NO_IMAGE ){
271                   cvImageWidget_set_size( widget, image_widget->original_image->cols,
272                                                           image_widget->original_image->rows);
273           }
274           else{
275                   cvImageWidget_set_size( widget, allocation->width, allocation->height );
276           }
277           cvResize( image_widget->original_image, image_widget->scaled_image, CV_INTER_AREA );
278   }
279
280   if (GTK_WIDGET_REALIZED (widget))
281     {
282       image_widget = CV_IMAGE_WIDGET (widget);
283
284           if( image_widget->original_image &&
285                           ((image_widget->flags & CV_WINDOW_AUTOSIZE) ||
286                            (image_widget->flags & CV_WINDOW_NO_IMAGE)) )
287           {
288                   widget->allocation.width = image_widget->original_image->cols;
289                   widget->allocation.height = image_widget->original_image->rows;
290                   gdk_window_move_resize( widget->window, allocation->x, allocation->y,
291                                   image_widget->original_image->cols, image_widget->original_image->rows );
292                   if(image_widget->flags & CV_WINDOW_NO_IMAGE){
293                           image_widget->flags &= ~CV_WINDOW_NO_IMAGE;
294                           gtk_widget_queue_resize( GTK_WIDGET(widget) );
295                   }
296           }
297           else{
298                   gdk_window_move_resize (widget->window,
299                                   allocation->x, allocation->y,
300                                   allocation->width, allocation->height );
301
302           }
303     }
304 }
305
306 static gboolean
307 cvImageWidget_expose( GtkWidget      *widget,
308                  GdkEventExpose *event )
309 {
310   CvImageWidget *image_widget;
311
312   g_return_val_if_fail (widget != NULL, FALSE);
313   g_return_val_if_fail (CV_IS_IMAGE_WIDGET (widget), FALSE);
314   g_return_val_if_fail (event != NULL, FALSE);
315
316   if (event->count > 0)
317     return FALSE;
318
319   image_widget = CV_IMAGE_WIDGET (widget);
320
321   gdk_window_clear_area (widget->window,
322                          0, 0,
323                          widget->allocation.width,
324                          widget->allocation.height);
325   if( image_widget->scaled_image ){
326           // center image in available region
327           int x0 = (widget->allocation.width - image_widget->scaled_image->cols)/2;
328           int y0 = (widget->allocation.height - image_widget->scaled_image->rows)/2;
329
330           gdk_draw_rgb_image( widget->window, widget->style->fg_gc[GTK_STATE_NORMAL],
331                   x0, y0, MIN(image_widget->scaled_image->cols, widget->allocation.width),
332                   MIN(image_widget->scaled_image->rows, widget->allocation.height),
333                   GDK_RGB_DITHER_MAX, image_widget->scaled_image->data.ptr, image_widget->scaled_image->step );
334   }
335   else if( image_widget->original_image ){
336           gdk_draw_rgb_image( widget->window, widget->style->fg_gc[GTK_STATE_NORMAL],
337                   0, 0,
338                   MIN(image_widget->original_image->cols, widget->allocation.width),
339                    MIN(image_widget->original_image->rows, widget->allocation.height),
340                   GDK_RGB_DITHER_MAX, image_widget->original_image->data.ptr, image_widget->original_image->step );
341   }
342   return TRUE;
343 }
344
345 static void
346 cvImageWidget_destroy (GtkObject *object)
347 {
348   CvImageWidget *image_widget;
349
350   g_return_if_fail (object != NULL);
351   g_return_if_fail (CV_IS_IMAGE_WIDGET (object));
352
353   image_widget = CV_IMAGE_WIDGET (object);
354
355   cvReleaseMat( &image_widget->scaled_image );
356   cvReleaseMat( &image_widget->original_image );
357
358   if (GTK_OBJECT_CLASS (parent_class)->destroy)
359     (* GTK_OBJECT_CLASS (parent_class)->destroy) (object);
360 }
361
362 static void cvImageWidget_class_init (CvImageWidgetClass * klass)
363 {
364   GtkObjectClass *object_class;
365   GtkWidgetClass *widget_class;
366
367   object_class = (GtkObjectClass*) klass;
368   widget_class = (GtkWidgetClass*) klass;
369
370   parent_class = GTK_WIDGET_CLASS( gtk_type_class (gtk_widget_get_type ()) );
371
372   object_class->destroy = cvImageWidget_destroy;
373
374   widget_class->realize = cvImageWidget_realize;
375   widget_class->expose_event = cvImageWidget_expose;
376   widget_class->size_request = cvImageWidget_size_request;
377   widget_class->size_allocate = cvImageWidget_size_allocate;
378   widget_class->button_press_event = NULL;
379   widget_class->button_release_event = NULL;
380   widget_class->motion_notify_event = NULL;
381 }
382
383 static void
384 cvImageWidget_init (CvImageWidget *image_widget)
385 {
386         image_widget->original_image=0;
387         image_widget->scaled_image=0;
388         image_widget->flags=0;
389 }
390
391 GtkType cvImageWidget_get_type (void){
392   static GtkType image_type = 0;
393
394   if (!image_type)
395     {
396       static const GtkTypeInfo image_info =
397       {
398         (gchar*)"CvImageWidget",
399         sizeof (CvImageWidget),
400         sizeof (CvImageWidgetClass),
401         (GtkClassInitFunc) cvImageWidget_class_init,
402         (GtkObjectInitFunc) cvImageWidget_init,
403         /* reserved_1 */ NULL,
404         /* reserved_1 */ NULL,
405         (GtkClassInitFunc) NULL
406       };
407
408       image_type = gtk_type_unique (GTK_TYPE_WIDGET, &image_info);
409     }
410
411   return image_type;
412 }
413 /////////////////////////////////////////////////////////////////////////////
414 // End CvImageWidget
415 /////////////////////////////////////////////////////////////////////////////
416
417
418 struct CvWindow;
419
420 typedef struct CvTrackbar
421 {
422     int signature;
423     GtkWidget* widget;
424     char* name;
425     CvTrackbar* next;
426     CvWindow* parent;
427     int* data;
428     int pos;
429     int maxval;
430     CvTrackbarCallback notify;
431     CvTrackbarCallback2 notify2;
432     void* userdata;
433 }
434 CvTrackbar;
435
436 typedef struct CvWindow
437 {
438     int signature;
439     GtkWidget* widget;
440     GtkWidget* frame;
441     GtkWidget* paned;
442     char* name;
443     CvWindow* prev;
444     CvWindow* next;
445
446     int last_key;
447     int flags;
448
449     CvMouseCallback on_mouse;
450     void* on_mouse_param;
451
452     struct
453     {
454         int pos;
455         int rows;
456         CvTrackbar* first;
457     }
458     toolbar;
459 }
460 CvWindow;
461
462
463 static gboolean icvOnClose( GtkWidget* widget, GdkEvent* event, gpointer user_data );
464 static gboolean icvOnKeyPress( GtkWidget* widget, GdkEventKey* event, gpointer user_data );
465 static void icvOnTrackbar( GtkWidget* widget, gpointer user_data );
466 static gboolean icvOnMouse( GtkWidget *widget, GdkEvent *event, gpointer user_data );
467
468 #ifdef HAVE_GTHREAD
469 int thread_started=0;
470 static gpointer icvWindowThreadLoop();
471 GMutex*                            last_key_mutex;
472 GCond*                             cond_have_key;
473 GMutex*                            window_mutex;
474 GThread*                           window_thread;
475 GtkWidget*             cvTopLevelWidget = 0;
476 #endif
477
478 static int             last_key = -1;
479 static CvWindow* hg_windows = 0;
480
481 CV_IMPL int cvInitSystem( int argc, char** argv )
482 {
483     static int wasInitialized = 0;
484
485     // check initialization status
486     if( !wasInitialized )
487     {
488         hg_windows = 0;
489
490         gtk_init( &argc, &argv );
491         wasInitialized = 1;
492     }
493
494     return 0;
495 }
496
497 CV_IMPL int cvStartWindowThread(){
498 #ifdef HAVE_GTHREAD
499         cvInitSystem(0,NULL);
500     if (!thread_started) {
501         if (!g_thread_supported ()) {
502             /* the GThread system wasn't inited, so init it */
503             g_thread_init(NULL);
504         }
505
506         // this mutex protects the window resources
507         window_mutex = g_mutex_new();
508
509         // protects the 'last key pressed' variable
510         last_key_mutex = g_mutex_new();
511
512         // conditional that indicates a key has been pressed
513         cond_have_key = g_cond_new();
514
515         // this is the window update thread
516         window_thread = g_thread_create((GThreadFunc) icvWindowThreadLoop,
517                                         NULL, TRUE, NULL);
518     }
519     thread_started = window_thread!=NULL;
520     return thread_started;
521 #else
522     return 0;
523 #endif
524 }
525
526 #ifdef HAVE_GTHREAD
527 gpointer icvWindowThreadLoop(){
528         while(1){
529                 g_mutex_lock(window_mutex);
530                 gtk_main_iteration_do(FALSE);
531                 g_mutex_unlock(window_mutex);
532
533                 // little sleep
534                 g_usleep(500);
535
536                 g_thread_yield();
537         }
538         return NULL;
539 }
540
541 #define CV_LOCK_MUTEX() \
542 if(thread_started && g_thread_self()!=window_thread){ g_mutex_lock( window_mutex ); } else { }
543
544 #define CV_UNLOCK_MUTEX() \
545 if(thread_started && g_thread_self()!=window_thread){ g_mutex_unlock( window_mutex); } else { }
546
547 #else
548 #define CV_LOCK_MUTEX()
549 #define CV_UNLOCK_MUTEX()
550 #endif
551
552 static CvWindow* icvFindWindowByName( const char* name )
553 {
554     CvWindow* window = hg_windows;
555     while( window != 0 && strcmp(name, window->name) != 0 )
556         window = window->next;
557
558     return window;
559 }
560
561 static CvWindow* icvWindowByWidget( GtkWidget* widget )
562 {
563     CvWindow* window = hg_windows;
564
565     while( window != 0 && window->widget != widget &&
566            window->frame != widget && window->paned != widget )
567         window = window->next;
568
569     return window;
570 }
571
572 CV_IMPL int cvNamedWindow( const char* name, int flags )
573 {
574     int result = 0;
575     CV_FUNCNAME( "cvNamedWindow" );
576
577     __BEGIN__;
578
579     CvWindow* window;
580     int len;
581
582     cvInitSystem(1,(char**)&name);
583     if( !name )
584         CV_ERROR( CV_StsNullPtr, "NULL name string" );
585
586     // Check the name in the storage
587     if( icvFindWindowByName( name ) != 0 )
588     {
589         result = 1;
590         EXIT;
591     }
592
593     len = strlen(name);
594     CV_CALL( window = (CvWindow*)cvAlloc(sizeof(CvWindow) + len + 1));
595     memset( window, 0, sizeof(*window));
596     window->name = (char*)(window + 1);
597     memcpy( window->name, name, len + 1 );
598     window->flags = flags;
599     window->signature = CV_WINDOW_MAGIC_VAL;
600     window->last_key = 0;
601     window->on_mouse = 0;
602     window->on_mouse_param = 0;
603     memset( &window->toolbar, 0, sizeof(window->toolbar));
604     window->next = hg_windows;
605     window->prev = 0;
606
607         CV_LOCK_MUTEX();
608
609     window->frame = gtk_window_new( GTK_WINDOW_TOPLEVEL );
610
611     window->paned = gtk_vbox_new( FALSE, 0 );
612     window->widget = cvImageWidgetNew( flags );
613     gtk_box_pack_end( GTK_BOX(window->paned), window->widget, TRUE, TRUE, 0 );
614     gtk_widget_show( window->widget );
615     gtk_container_add( GTK_CONTAINER(window->frame), window->paned );
616     gtk_widget_show( window->paned );
617         //
618         // configure event handlers
619         // TODO -- move this to CvImageWidget ?
620     gtk_signal_connect( GTK_OBJECT(window->frame), "key-press-event",
621                         GTK_SIGNAL_FUNC(icvOnKeyPress), window );
622     gtk_signal_connect( GTK_OBJECT(window->widget), "button-press-event",
623                         GTK_SIGNAL_FUNC(icvOnMouse), window );
624     gtk_signal_connect( GTK_OBJECT(window->widget), "button-release-event",
625                         GTK_SIGNAL_FUNC(icvOnMouse), window );
626     gtk_signal_connect( GTK_OBJECT(window->widget), "motion-notify-event",
627                         GTK_SIGNAL_FUNC(icvOnMouse), window );
628     gtk_signal_connect( GTK_OBJECT(window->frame), "delete-event",
629                         GTK_SIGNAL_FUNC(icvOnClose), window );
630
631         gtk_widget_add_events (window->widget, GDK_EXPOSURE_MASK | GDK_BUTTON_RELEASE_MASK |
632                                           GDK_BUTTON_PRESS_MASK | GDK_POINTER_MOTION_MASK) ;
633
634     gtk_widget_show( window->frame );
635     gtk_window_set_title( GTK_WINDOW(window->frame), name );
636
637     if( hg_windows )
638         hg_windows->prev = window;
639     hg_windows = window;
640
641     gtk_window_set_resizable( GTK_WINDOW(window->frame), (flags & CV_WINDOW_AUTOSIZE) == 0 );
642
643
644         // allow window to be resized
645         if( (flags & CV_WINDOW_AUTOSIZE)==0 ){
646                 GdkGeometry geometry;
647                 geometry.min_width = 50;
648                 geometry.min_height = 50;
649                 gtk_window_set_geometry_hints( GTK_WINDOW( window->frame ), GTK_WIDGET( window->widget ),
650                         &geometry, (GdkWindowHints) (GDK_HINT_MIN_SIZE));
651         }
652
653         CV_UNLOCK_MUTEX();
654
655     result = 1;
656     __END__;
657
658     return result;
659 }
660
661
662 static void icvDeleteWindow( CvWindow* window )
663 {
664     CvTrackbar* trackbar;
665
666     if( window->prev )
667         window->prev->next = window->next;
668     else
669         hg_windows = window->next;
670
671     if( window->next )
672         window->next->prev = window->prev;
673
674     window->prev = window->next = 0;
675
676         gtk_widget_destroy( window->frame );
677
678     for( trackbar = window->toolbar.first; trackbar != 0; )
679     {
680         CvTrackbar* next = trackbar->next;
681         cvFree( &trackbar );
682         trackbar = next;
683     }
684
685     cvFree( &window );
686 #ifdef HAVE_GTHREAD
687         // if last window, send key press signal
688         // to jump out of any waiting cvWaitKey's
689         if(hg_windows==0 && thread_started){
690                 g_cond_broadcast(cond_have_key);
691         }
692 #endif
693 }
694
695
696 CV_IMPL void cvDestroyWindow( const char* name )
697 {
698     CV_FUNCNAME( "cvDestroyWindow" );
699
700     __BEGIN__;
701
702     CvWindow* window;
703
704     if(!name)
705         CV_ERROR( CV_StsNullPtr, "NULL name string" );
706
707     window = icvFindWindowByName( name );
708     if( !window )
709         EXIT;
710
711         // note that it is possible for the update thread to run this function
712         // if there is a call to cvShowImage in a mouse callback
713         // (this would produce a deadlock on window_mutex)
714         CV_LOCK_MUTEX();
715
716         icvDeleteWindow( window );
717
718         CV_UNLOCK_MUTEX();
719
720     __END__;
721 }
722
723
724 CV_IMPL void
725 cvDestroyAllWindows( void )
726 {
727         CV_LOCK_MUTEX();
728
729     while( hg_windows )
730     {
731         CvWindow* window = hg_windows;
732         icvDeleteWindow( window );
733     }
734         CV_UNLOCK_MUTEX();
735 }
736
737 CvSize icvCalcOptimalWindowSize( CvWindow * window, CvSize new_image_size){
738         CvSize window_size;
739         GtkWidget * toplevel = gtk_widget_get_toplevel( window->frame );
740         gdk_drawable_get_size( GDK_DRAWABLE(toplevel->window),
741                         &window_size.width, &window_size.height );
742
743         window_size.width = window_size.width + new_image_size.width - window->widget->allocation.width;
744         window_size.height = window_size.height + new_image_size.height - window->widget->allocation.height;
745
746         return window_size;
747 }
748
749 CV_IMPL void
750 cvShowImage( const char* name, const CvArr* arr )
751 {
752     CV_FUNCNAME( "cvShowImage" );
753
754     __BEGIN__;
755
756     CvWindow* window;
757
758     if( !name )
759         CV_ERROR( CV_StsNullPtr, "NULL name" );
760
761         CV_LOCK_MUTEX();
762
763     window = icvFindWindowByName(name);
764         if(!window)
765         {
766                 cvNamedWindow(name, 1);
767                 window = icvFindWindowByName(name);
768         }
769     if( window && arr ){
770                 CvImageWidget * image_widget = CV_IMAGE_WIDGET( window->widget );
771                 cvImageWidgetSetImage( image_widget, arr );
772         }
773
774         CV_UNLOCK_MUTEX();
775
776     __END__;
777 }
778
779 CV_IMPL void cvResizeWindow(const char* name, int width, int height )
780 {
781     CV_FUNCNAME( "cvResizeWindow" );
782
783     __BEGIN__;
784
785     CvWindow* window;
786         CvImageWidget * image_widget;
787
788     if( !name )
789         CV_ERROR( CV_StsNullPtr, "NULL name" );
790
791     window = icvFindWindowByName(name);
792     if(!window)
793         EXIT;
794
795         image_widget = CV_IMAGE_WIDGET( window->widget );
796         if(image_widget->flags & CV_WINDOW_AUTOSIZE)
797                 EXIT;
798
799         CV_LOCK_MUTEX();
800
801         gtk_window_set_resizable( GTK_WINDOW(window->frame), 1 );
802     gtk_window_resize( GTK_WINDOW(window->frame), width, height );
803
804         // disable initial resize since presumably user wants to keep
805         // this window size
806         image_widget->flags &= ~CV_WINDOW_NO_IMAGE;
807
808         CV_UNLOCK_MUTEX();
809
810     __END__;
811 }
812
813
814 CV_IMPL void cvMoveWindow( const char* name, int x, int y )
815 {
816     CV_FUNCNAME( "cvMoveWindow" );
817
818     __BEGIN__;
819
820     CvWindow* window;
821
822     if( !name )
823         CV_ERROR( CV_StsNullPtr, "NULL name" );
824
825     window = icvFindWindowByName(name);
826     if(!window)
827         EXIT;
828
829         CV_LOCK_MUTEX();
830
831     gtk_window_move( GTK_WINDOW(window->frame), x, y );
832
833         CV_UNLOCK_MUTEX();
834
835     __END__;
836 }
837
838
839 static CvTrackbar*
840 icvFindTrackbarByName( const CvWindow* window, const char* name )
841 {
842     CvTrackbar* trackbar = window->toolbar.first;
843
844     for( ; trackbar != 0 && strcmp( trackbar->name, name ) != 0; trackbar = trackbar->next )
845         ;
846
847     return trackbar;
848 }
849
850 static int
851 icvCreateTrackbar( const char* trackbar_name, const char* window_name,
852                    int* val, int count, CvTrackbarCallback on_notify,
853                    CvTrackbarCallback2 on_notify2, void* userdata )
854 {
855     int result = 0;
856
857     CV_FUNCNAME( "icvCreateTrackbar" );
858
859     __BEGIN__;
860
861     /*char slider_name[32];*/
862     CvWindow* window = 0;
863     CvTrackbar* trackbar = 0;
864
865     if( !window_name || !trackbar_name )
866         CV_ERROR( CV_StsNullPtr, "NULL window or trackbar name" );
867
868     if( count <= 0 )
869         CV_ERROR( CV_StsOutOfRange, "Bad trackbar maximal value" );
870
871     window = icvFindWindowByName(window_name);
872     if( !window )
873         EXIT;
874
875     trackbar = icvFindTrackbarByName(window,trackbar_name);
876
877         CV_LOCK_MUTEX();
878
879     if( !trackbar )
880     {
881         int len = strlen(trackbar_name);
882         trackbar = (CvTrackbar*)cvAlloc(sizeof(CvTrackbar) + len + 1);
883         memset( trackbar, 0, sizeof(*trackbar));
884         trackbar->signature = CV_TRACKBAR_MAGIC_VAL;
885         trackbar->name = (char*)(trackbar+1);
886         memcpy( trackbar->name, trackbar_name, len + 1 );
887         trackbar->parent = window;
888         trackbar->next = window->toolbar.first;
889         window->toolbar.first = trackbar;
890
891         GtkWidget* hscale_box = gtk_hbox_new( FALSE, 10 );
892         GtkWidget* hscale_label = gtk_label_new( trackbar_name );
893         GtkWidget* hscale = gtk_hscale_new_with_range( 0, count, 1 );
894         gtk_range_set_update_policy( GTK_RANGE(hscale), GTK_UPDATE_CONTINUOUS );
895         gtk_scale_set_digits( GTK_SCALE(hscale), 0 );
896         //gtk_scale_set_value_pos( hscale, GTK_POS_TOP );
897         gtk_scale_set_draw_value( GTK_SCALE(hscale), TRUE );
898
899         trackbar->widget = hscale;
900         gtk_box_pack_start( GTK_BOX(hscale_box), hscale_label, FALSE, FALSE, 5 );
901         gtk_widget_show( hscale_label );
902         gtk_box_pack_start( GTK_BOX(hscale_box), hscale, TRUE, TRUE, 5 );
903         gtk_widget_show( hscale );
904         gtk_box_pack_start( GTK_BOX(window->paned), hscale_box, FALSE, FALSE, 5 );
905         gtk_widget_show( hscale_box );
906
907         }
908
909     if( val )
910     {
911         int value = *val;
912         if( value < 0 )
913             value = 0;
914         if( value > count )
915             value = count;
916         gtk_range_set_value( GTK_RANGE(trackbar->widget), value );
917         trackbar->pos = value;
918         trackbar->data = val;
919     }
920
921     trackbar->maxval = count;
922     trackbar->notify = on_notify;
923     trackbar->notify2 = on_notify2;
924     trackbar->userdata = userdata;
925     gtk_signal_connect( GTK_OBJECT(trackbar->widget), "value-changed",
926                         GTK_SIGNAL_FUNC(icvOnTrackbar), trackbar );
927
928         // queue a widget resize to trigger a window resize to
929         // compensate for the addition of trackbars
930         gtk_widget_queue_resize( GTK_WIDGET(window->widget) );
931
932
933         CV_UNLOCK_MUTEX();
934
935     result = 1;
936
937     __END__;
938
939     return result;
940 }
941
942
943 CV_IMPL int
944 cvCreateTrackbar( const char* trackbar_name, const char* window_name,
945                   int* val, int count, CvTrackbarCallback on_notify )
946 {
947     return icvCreateTrackbar(trackbar_name, window_name, val, count,
948                              on_notify, 0, 0);
949 }
950
951
952 CV_IMPL int
953 cvCreateTrackbar2( const char* trackbar_name, const char* window_name,
954                    int* val, int count, CvTrackbarCallback2 on_notify2,
955                    void* userdata )
956 {
957     return icvCreateTrackbar(trackbar_name, window_name, val, count,
958                              0, on_notify2, userdata);
959 }
960
961
962 CV_IMPL void
963 cvSetMouseCallback( const char* window_name, CvMouseCallback on_mouse, void* param )
964 {
965     CV_FUNCNAME( "cvSetMouseCallback" );
966
967     __BEGIN__;
968
969     CvWindow* window = 0;
970
971     if( !window_name )
972         CV_ERROR( CV_StsNullPtr, "NULL window name" );
973
974     window = icvFindWindowByName(window_name);
975     if( !window )
976         EXIT;
977
978     window->on_mouse = on_mouse;
979     window->on_mouse_param = param;
980
981     __END__;
982 }
983
984
985 CV_IMPL int cvGetTrackbarPos( const char* trackbar_name, const char* window_name )
986 {
987     int pos = -1;
988
989     CV_FUNCNAME( "cvGetTrackbarPos" );
990
991     __BEGIN__;
992
993     CvWindow* window;
994     CvTrackbar* trackbar = 0;
995
996     if( trackbar_name == 0 || window_name == 0 )
997         CV_ERROR( CV_StsNullPtr, "NULL trackbar or window name" );
998
999     window = icvFindWindowByName( window_name );
1000     if( window )
1001         trackbar = icvFindTrackbarByName( window, trackbar_name );
1002
1003     if( trackbar )
1004         pos = trackbar->pos;
1005
1006     __END__;
1007
1008     return pos;
1009 }
1010
1011
1012 CV_IMPL void cvSetTrackbarPos( const char* trackbar_name, const char* window_name, int pos )
1013 {
1014     CV_FUNCNAME( "cvSetTrackbarPos" );
1015
1016     __BEGIN__;
1017
1018     CvWindow* window;
1019     CvTrackbar* trackbar = 0;
1020
1021     if( trackbar_name == 0 || window_name == 0 )
1022         CV_ERROR( CV_StsNullPtr, "NULL trackbar or window name" );
1023
1024     window = icvFindWindowByName( window_name );
1025     if( window )
1026         trackbar = icvFindTrackbarByName( window, trackbar_name );
1027
1028     if( trackbar )
1029     {
1030         if( pos < 0 )
1031             pos = 0;
1032
1033         if( pos > trackbar->maxval )
1034             pos = trackbar->maxval;
1035     }
1036
1037         CV_LOCK_MUTEX();
1038
1039     gtk_range_set_value( GTK_RANGE(trackbar->widget), pos );
1040
1041         CV_UNLOCK_MUTEX();
1042
1043     __END__;
1044 }
1045
1046
1047 CV_IMPL void* cvGetWindowHandle( const char* window_name )
1048 {
1049     void* widget = 0;
1050
1051     CV_FUNCNAME( "cvGetWindowHandle" );
1052
1053     __BEGIN__;
1054
1055     CvWindow* window;
1056
1057     if( window_name == 0 )
1058         CV_ERROR( CV_StsNullPtr, "NULL window name" );
1059
1060     window = icvFindWindowByName( window_name );
1061     if( window )
1062         widget = (void*)window->widget;
1063
1064     __END__;
1065
1066     return widget;
1067 }
1068
1069
1070 CV_IMPL const char* cvGetWindowName( void* window_handle )
1071 {
1072     const char* window_name = "";
1073
1074     CV_FUNCNAME( "cvGetWindowName" );
1075
1076     __BEGIN__;
1077
1078     CvWindow* window;
1079
1080     if( window_handle == 0 )
1081         CV_ERROR( CV_StsNullPtr, "NULL window" );
1082
1083     window = icvWindowByWidget( (GtkWidget*)window_handle );
1084     if( window )
1085         window_name = window->name;
1086
1087     __END__;
1088
1089     return window_name;
1090 }
1091
1092 static gboolean icvOnKeyPress( GtkWidget * /*widget*/,
1093                 GdkEventKey* event, gpointer /*user_data*/ )
1094 {
1095     int code = 0;
1096
1097     switch( event->keyval )
1098     {
1099     case GDK_Escape:
1100         code = 27;
1101         break;
1102     case GDK_Return:
1103     case GDK_Linefeed:
1104         code = '\n';
1105         break;
1106     case GDK_Tab:
1107         code = '\t';
1108         break;
1109     default:
1110         code = event->keyval;
1111     }
1112
1113     code |= event->state << 16;
1114
1115 #ifdef HAVE_GTHREAD
1116         if(thread_started) g_mutex_lock(last_key_mutex);
1117 #endif
1118
1119         last_key = code;
1120
1121 #ifdef HAVE_GTHREAD
1122         if(thread_started){
1123                 // signal any waiting threads
1124                 g_cond_broadcast(cond_have_key);
1125                 g_mutex_unlock(last_key_mutex);
1126         }
1127 #endif
1128
1129     return FALSE;
1130 }
1131
1132
1133 static void icvOnTrackbar( GtkWidget* widget, gpointer user_data )
1134 {
1135     int pos = cvRound( gtk_range_get_value(GTK_RANGE(widget)));
1136     CvTrackbar* trackbar = (CvTrackbar*)user_data;
1137
1138     if( trackbar && trackbar->signature == CV_TRACKBAR_MAGIC_VAL &&
1139         trackbar->widget == widget )
1140     {
1141         trackbar->pos = pos;
1142         if( trackbar->data )
1143             *trackbar->data = pos;
1144         if( trackbar->notify2 )
1145             trackbar->notify2(pos, trackbar->userdata);
1146         else if( trackbar->notify )
1147             trackbar->notify(pos);
1148     }
1149 }
1150
1151 static gboolean icvOnClose( GtkWidget* widget, GdkEvent* /*event*/, gpointer user_data )
1152 {
1153     CvWindow* window = (CvWindow*)user_data;
1154     if( window->signature == CV_WINDOW_MAGIC_VAL &&
1155         window->frame == widget )
1156         {
1157         icvDeleteWindow(window);
1158         }
1159     return TRUE;
1160 }
1161
1162
1163 static gboolean icvOnMouse( GtkWidget *widget, GdkEvent *event, gpointer user_data )
1164 {
1165         // TODO move this logic to CvImageWidget
1166     CvWindow* window = (CvWindow*)user_data;
1167         CvPoint2D32f pt32f = {-1., -1.};
1168     CvPoint pt = {-1,-1};
1169     int cv_event = -1, state = 0;
1170         CvImageWidget * image_widget = CV_IMAGE_WIDGET( widget );
1171
1172     if( window->signature != CV_WINDOW_MAGIC_VAL ||
1173         window->widget != widget || !window->widget ||
1174                 !window->on_mouse || !image_widget->original_image)
1175         return FALSE;
1176
1177     if( event->type == GDK_MOTION_NOTIFY )
1178     {
1179         GdkEventMotion* event_motion = (GdkEventMotion*)event;
1180
1181         cv_event = CV_EVENT_MOUSEMOVE;
1182         pt32f.x = cvRound(event_motion->x);
1183         pt32f.y = cvRound(event_motion->y);
1184         state = event_motion->state;
1185     }
1186     else if( event->type == GDK_BUTTON_PRESS ||
1187              event->type == GDK_BUTTON_RELEASE ||
1188              event->type == GDK_2BUTTON_PRESS )
1189     {
1190         GdkEventButton* event_button = (GdkEventButton*)event;
1191         pt32f.x = cvRound(event_button->x);
1192         pt32f.y = cvRound(event_button->y);
1193
1194
1195         if( event_button->type == GDK_BUTTON_PRESS )
1196         {
1197             cv_event = event_button->button == 1 ? CV_EVENT_LBUTTONDOWN :
1198                        event_button->button == 2 ? CV_EVENT_MBUTTONDOWN :
1199                        event_button->button == 3 ? CV_EVENT_RBUTTONDOWN : 0;
1200         }
1201         else if( event_button->type == GDK_BUTTON_RELEASE )
1202         {
1203             cv_event = event_button->button == 1 ? CV_EVENT_LBUTTONUP :
1204                        event_button->button == 2 ? CV_EVENT_MBUTTONUP :
1205                        event_button->button == 3 ? CV_EVENT_RBUTTONUP : 0;
1206         }
1207         else if( event_button->type == GDK_2BUTTON_PRESS )
1208         {
1209             cv_event = event_button->button == 1 ? CV_EVENT_LBUTTONDBLCLK :
1210                        event_button->button == 2 ? CV_EVENT_MBUTTONDBLCLK :
1211                        event_button->button == 3 ? CV_EVENT_RBUTTONDBLCLK : 0;
1212         }
1213         state = event_button->state;
1214     }
1215
1216     if( cv_event >= 0 ){
1217                 // scale point if image is scaled
1218                 if( (image_widget->flags & CV_WINDOW_AUTOSIZE)==0 &&
1219                      image_widget->original_image &&
1220                          image_widget->scaled_image ){
1221                         // image origin is not necessarily at (0,0)
1222                         int x0 = (widget->allocation.width - image_widget->scaled_image->cols)/2;
1223                         int y0 = (widget->allocation.height - image_widget->scaled_image->rows)/2;
1224                         pt.x = cvRound( ((pt32f.x-x0)*image_widget->original_image->cols)/
1225                                                                 image_widget->scaled_image->cols );
1226                         pt.y = cvRound( ((pt32f.y-y0)*image_widget->original_image->rows)/
1227                                                                 image_widget->scaled_image->rows );
1228                 }
1229                 else{
1230                         pt = cvPointFrom32f( pt32f );
1231                 }
1232
1233                 if((unsigned)pt.x < (unsigned)(image_widget->original_image->width) &&
1234                    (unsigned)pt.y < (unsigned)(image_widget->original_image->height) )
1235                 {
1236                         int flags = (state & GDK_SHIFT_MASK ? CV_EVENT_FLAG_SHIFTKEY : 0) |
1237                                 (state & GDK_CONTROL_MASK ? CV_EVENT_FLAG_CTRLKEY : 0) |
1238                                 (state & (GDK_MOD1_MASK|GDK_MOD2_MASK) ? CV_EVENT_FLAG_ALTKEY : 0) |
1239                                 (state & GDK_BUTTON1_MASK ? CV_EVENT_FLAG_LBUTTON : 0) |
1240                                 (state & GDK_BUTTON2_MASK ? CV_EVENT_FLAG_MBUTTON : 0) |
1241                                 (state & GDK_BUTTON3_MASK ? CV_EVENT_FLAG_RBUTTON : 0);
1242                         window->on_mouse( cv_event, pt.x, pt.y, flags, window->on_mouse_param );
1243                 }
1244         }
1245
1246                 return FALSE;
1247         }
1248
1249
1250 static gboolean icvAlarm( gpointer user_data )
1251 {
1252     *(int*)user_data = 1;
1253     return FALSE;
1254 }
1255
1256
1257 CV_IMPL int cvWaitKey( int delay )
1258 {
1259 #ifdef HAVE_GTHREAD
1260         if(thread_started && g_thread_self()!=window_thread){
1261                 gboolean expired;
1262                 int my_last_key;
1263
1264                 // wait for signal or timeout if delay > 0
1265                 if(delay>0){
1266                         GTimeVal timer;
1267                         g_get_current_time(&timer);
1268                         g_time_val_add(&timer, delay*1000);
1269                         expired = !g_cond_timed_wait(cond_have_key, last_key_mutex, &timer);
1270                 }
1271                 else{
1272                         g_cond_wait(cond_have_key, last_key_mutex);
1273                         expired=false;
1274                 }
1275                 my_last_key = last_key;
1276                 g_mutex_unlock(last_key_mutex);
1277                 if(expired || hg_windows==0){
1278                         return -1;
1279                 }
1280                 return my_last_key;
1281         }
1282         else{
1283 #endif
1284                 int expired = 0;
1285                 guint timer = 0;
1286                 if( delay > 0 )
1287                         timer = g_timeout_add( delay, icvAlarm, &expired );
1288                 last_key = -1;
1289                 while( gtk_main_iteration_do(TRUE) && last_key < 0 && !expired && hg_windows != 0 )
1290                         ;
1291
1292                 if( delay > 0 && !expired )
1293                         g_source_remove(timer);
1294 #ifdef HAVE_GTHREAD
1295         }
1296 #endif
1297         return last_key;
1298 }
1299
1300
1301 #endif  // HAVE_GTK
1302 #endif  // WIN32
1303
1304 /* End of file. */