Update to 2.0.0 tree from current Fremantle build
[opencv] / src / highgui / window_gtk.cpp
diff --git a/src/highgui/window_gtk.cpp b/src/highgui/window_gtk.cpp
new file mode 100644 (file)
index 0000000..c2a3617
--- /dev/null
@@ -0,0 +1,1304 @@
+/*M///////////////////////////////////////////////////////////////////////////////////////
+//
+//  IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING.
+//
+//  By downloading, copying, installing or using the software you agree to this license.
+//  If you do not agree to this license, do not download, install,
+//  copy or use the software.
+//
+//
+//                        Intel License Agreement
+//                For Open Source Computer Vision Library
+//
+// Copyright (C) 2000, Intel Corporation, all rights reserved.
+// Third party copyrights are property of their respective owners.
+//
+// Redistribution and use in source and binary forms, with or without modification,
+// are permitted provided that the following conditions are met:
+//
+//   * Redistribution's of source code must retain the above copyright notice,
+//     this list of conditions and the following disclaimer.
+//
+//   * Redistribution's in binary form must reproduce the above copyright notice,
+//     this list of conditions and the following disclaimer in the documentation
+//     and/or other materials provided with the distribution.
+//
+//   * The name of Intel Corporation may not be used to endorse or promote products
+//     derived from this software without specific prior written permission.
+//
+// This software is provided by the copyright holders and contributors "as is" and
+// any express or implied warranties, including, but not limited to, the implied
+// warranties of merchantability and fitness for a particular purpose are disclaimed.
+// In no event shall the Intel Corporation or contributors be liable for any direct,
+// indirect, incidental, special, exemplary, or consequential damages
+// (including, but not limited to, procurement of substitute goods or services;
+// loss of use, data, or profits; or business interruption) however caused
+// and on any theory of liability, whether in contract, strict liability,
+// or tort (including negligence or otherwise) arising in any way out of
+// the use of this software, even if advised of the possibility of such damage.
+//
+//M*/
+
+#include "_highgui.h"
+
+#ifndef WIN32
+
+#ifdef HAVE_GTK
+
+#include "gtk/gtk.h"
+#include "gdk/gdkkeysyms.h"
+#include <stdio.h>
+
+#include "cv.h"
+/*#if _MSC_VER >= 1200
+#pragma warning( disable: 4505 )
+#pragma comment(lib,"gtk-win32-2.0.lib")
+#pragma comment(lib,"glib-2.0.lib")
+#pragma comment(lib,"gobject-2.0.lib")
+#pragma comment(lib,"gdk-win32-2.0.lib")
+#pragma comment(lib,"gdk_pixbuf-2.0.lib")
+#endif*/
+
+
+// TODO Fix the initial window size when flags=0.  Right now the initial window is by default
+// 320x240 size.  A better default would be actual size of the image.  Problem
+// is determining desired window size with trackbars while still allowing resizing.
+//
+// Gnome Totem source may be of use here, see bacon_video_widget_set_scale_ratio
+// in totem/src/backend/bacon-video-widget-xine.c
+
+////////////////////////////////////////////////////////////
+// CvImageWidget GTK Widget Public API
+////////////////////////////////////////////////////////////
+typedef struct _CvImageWidget        CvImageWidget;
+typedef struct _CvImageWidgetClass   CvImageWidgetClass;
+
+struct _CvImageWidget {
+       GtkWidget widget;
+       CvMat * original_image;
+       CvMat * scaled_image;
+       int flags;
+};
+
+struct _CvImageWidgetClass
+{
+  GtkWidgetClass parent_class;
+};
+
+
+/** Allocate new image viewer widget */
+GtkWidget*     cvImageWidgetNew      (int flags);
+
+/** Set the image to display in the widget */
+void           cvImageWidgetSetImage(CvImageWidget * widget, const CvArr *arr);
+
+// standard GTK object macros
+#define CV_IMAGE_WIDGET(obj)          GTK_CHECK_CAST (obj, cvImageWidget_get_type (), CvImageWidget)
+#define CV_IMAGE_WIDGET_CLASS(klass)  GTK_CHECK_CLASS_CAST (klass, cvImageWidget_get_type (), CvImageWidgetClass)
+#define CV_IS_IMAGE_WIDGET(obj)       GTK_CHECK_TYPE (obj, cvImageWidget_get_type ())
+
+/////////////////////////////////////////////////////////////////////////////
+// Private API ////////////////////////////////////////////////////////
+/////////////////////////////////////////////////////////////////////////////
+GtkType        cvImageWidget_get_type (void);
+
+static GtkWidgetClass * parent_class = NULL;
+
+// flag to help size initial window
+#define CV_WINDOW_NO_IMAGE 2
+
+void cvImageWidgetSetImage(CvImageWidget * widget, const CvArr *arr){
+       CvMat * mat, stub;
+       int origin=0;
+
+       //printf("cvImageWidgetSetImage\n");
+
+       if( CV_IS_IMAGE_HDR( arr ))
+               origin = ((IplImage*)arr)->origin;
+
+       mat = cvGetMat(arr, &stub);
+
+       if(widget->original_image && !CV_ARE_SIZES_EQ(mat, widget->original_image)){
+               cvReleaseMat( &widget->original_image );
+       }
+       if(!widget->original_image){
+               widget->original_image = cvCreateMat( mat->rows, mat->cols, CV_8UC3 );
+               gtk_widget_queue_resize( GTK_WIDGET( widget ) );
+       }
+       cvConvertImage( mat, widget->original_image,
+                                       (origin != 0 ? CV_CVTIMG_FLIP : 0) + CV_CVTIMG_SWAP_RB );
+       if(widget->scaled_image){
+               cvResize( widget->original_image, widget->scaled_image, CV_INTER_AREA );
+       }
+
+       // window does not refresh without this
+       gtk_widget_queue_draw( GTK_WIDGET(widget) );
+}
+
+GtkWidget*
+cvImageWidgetNew (int flags)
+{
+  CvImageWidget *image_widget;
+
+  image_widget = CV_IMAGE_WIDGET( gtk_type_new (cvImageWidget_get_type ()) );
+  image_widget->original_image = 0;
+  image_widget->scaled_image = 0;
+  image_widget->flags = flags | CV_WINDOW_NO_IMAGE;
+
+  return GTK_WIDGET (image_widget);
+}
+
+static void
+cvImageWidget_realize (GtkWidget *widget)
+{
+  CvImageWidget *image_widget;
+  GdkWindowAttr attributes;
+  gint attributes_mask;
+
+  //printf("cvImageWidget_realize\n");
+  g_return_if_fail (widget != NULL);
+  g_return_if_fail (CV_IS_IMAGE_WIDGET (widget));
+
+  GTK_WIDGET_SET_FLAGS (widget, GTK_REALIZED);
+  image_widget = CV_IMAGE_WIDGET (widget);
+
+  attributes.x = widget->allocation.x;
+  attributes.y = widget->allocation.y;
+  attributes.width = widget->allocation.width;
+  attributes.height = widget->allocation.height;
+  attributes.wclass = GDK_INPUT_OUTPUT;
+  attributes.window_type = GDK_WINDOW_CHILD;
+  attributes.event_mask = gtk_widget_get_events (widget) |
+    GDK_EXPOSURE_MASK | GDK_BUTTON_PRESS_MASK |
+    GDK_BUTTON_RELEASE_MASK | GDK_POINTER_MOTION_MASK;
+  attributes.visual = gtk_widget_get_visual (widget);
+  attributes.colormap = gtk_widget_get_colormap (widget);
+
+  attributes_mask = GDK_WA_X | GDK_WA_Y | GDK_WA_VISUAL | GDK_WA_COLORMAP;
+  widget->window = gdk_window_new (widget->parent->window, &attributes, attributes_mask);
+
+  widget->style = gtk_style_attach (widget->style, widget->window);
+
+  gdk_window_set_user_data (widget->window, widget);
+
+  gtk_style_set_background (widget->style, widget->window, GTK_STATE_ACTIVE);
+}
+
+static CvSize cvImageWidget_calc_size( int im_width, int im_height, int max_width, int max_height ){
+    float aspect = (float)im_width/(float)im_height;
+    float max_aspect = (float)max_width/(float)max_height;
+    if(aspect > max_aspect){
+        return cvSize( max_width, cvRound(max_width/aspect) );
+    }
+    return cvSize( cvRound(max_height*aspect), max_height );
+}
+
+static void
+cvImageWidget_size_request (GtkWidget      *widget,
+                       GtkRequisition *requisition)
+{
+       CvImageWidget * image_widget = CV_IMAGE_WIDGET( widget );
+
+       //printf("cvImageWidget_size_request ");
+       // the case the first time cvShowImage called or when AUTOSIZE
+       if( image_widget->original_image &&
+               ((image_widget->flags & CV_WINDOW_AUTOSIZE) ||
+                (image_widget->flags & CV_WINDOW_NO_IMAGE)))
+       {
+               //printf("original ");
+               requisition->width = image_widget->original_image->cols;
+               requisition->height = image_widget->original_image->rows;
+       }
+       // default case
+       else if(image_widget->scaled_image){
+               //printf("scaled ");
+               requisition->width = image_widget->scaled_image->cols;
+               requisition->height = image_widget->scaled_image->rows;
+       }
+       // the case before cvShowImage called
+       else{
+               //printf("default ");
+               requisition->width = 320;
+               requisition->height = 240;
+       }
+       //printf("%d %d\n",requisition->width, requisition->height);
+}
+
+static void cvImageWidget_set_size(GtkWidget * widget, int max_width, int max_height){
+       CvImageWidget * image_widget = CV_IMAGE_WIDGET( widget );
+
+       //printf("cvImageWidget_set_size %d %d\n", max_width, max_height);
+
+       // don't allow to set the size
+       if(image_widget->flags & CV_WINDOW_AUTOSIZE) return;
+       if(!image_widget->original_image) return;
+
+       CvSize scaled_image_size = cvImageWidget_calc_size( image_widget->original_image->cols,
+                       image_widget->original_image->rows, max_width, max_height );
+
+       if( image_widget->scaled_image &&
+                       ( image_widget->scaled_image->cols != scaled_image_size.width ||
+                         image_widget->scaled_image->rows != scaled_image_size.height ))
+       {
+               cvReleaseMat( &image_widget->scaled_image );
+       }
+       if( !image_widget->scaled_image ){
+               image_widget->scaled_image = cvCreateMat( scaled_image_size.height, scaled_image_size.width,        CV_8UC3 );
+
+
+       }
+       assert( image_widget->scaled_image );
+}
+
+static void
+cvImageWidget_size_allocate (GtkWidget     *widget,
+                        GtkAllocation *allocation)
+{
+  CvImageWidget *image_widget;
+
+  //printf("cvImageWidget_size_allocate\n");
+  g_return_if_fail (widget != NULL);
+  g_return_if_fail (CV_IS_IMAGE_WIDGET (widget));
+  g_return_if_fail (allocation != NULL);
+
+  widget->allocation = *allocation;
+  image_widget = CV_IMAGE_WIDGET (widget);
+
+
+  if( (image_widget->flags & CV_WINDOW_AUTOSIZE)==0 && image_widget->original_image ){
+         // (re) allocated scaled image
+         if( image_widget->flags & CV_WINDOW_NO_IMAGE ){
+                 cvImageWidget_set_size( widget, image_widget->original_image->cols,
+                                                         image_widget->original_image->rows);
+         }
+         else{
+                 cvImageWidget_set_size( widget, allocation->width, allocation->height );
+         }
+         cvResize( image_widget->original_image, image_widget->scaled_image, CV_INTER_AREA );
+  }
+
+  if (GTK_WIDGET_REALIZED (widget))
+    {
+      image_widget = CV_IMAGE_WIDGET (widget);
+
+         if( image_widget->original_image &&
+                         ((image_widget->flags & CV_WINDOW_AUTOSIZE) ||
+                          (image_widget->flags & CV_WINDOW_NO_IMAGE)) )
+         {
+                 widget->allocation.width = image_widget->original_image->cols;
+                 widget->allocation.height = image_widget->original_image->rows;
+                 gdk_window_move_resize( widget->window, allocation->x, allocation->y,
+                                 image_widget->original_image->cols, image_widget->original_image->rows );
+                 if(image_widget->flags & CV_WINDOW_NO_IMAGE){
+                         image_widget->flags &= ~CV_WINDOW_NO_IMAGE;
+                         gtk_widget_queue_resize( GTK_WIDGET(widget) );
+                 }
+         }
+         else{
+                 gdk_window_move_resize (widget->window,
+                                 allocation->x, allocation->y,
+                                 allocation->width, allocation->height );
+
+         }
+    }
+}
+
+static gboolean
+cvImageWidget_expose( GtkWidget      *widget,
+                 GdkEventExpose *event )
+{
+  CvImageWidget *image_widget;
+
+  g_return_val_if_fail (widget != NULL, FALSE);
+  g_return_val_if_fail (CV_IS_IMAGE_WIDGET (widget), FALSE);
+  g_return_val_if_fail (event != NULL, FALSE);
+
+  if (event->count > 0)
+    return FALSE;
+
+  image_widget = CV_IMAGE_WIDGET (widget);
+
+  gdk_window_clear_area (widget->window,
+                         0, 0,
+                         widget->allocation.width,
+                         widget->allocation.height);
+  if( image_widget->scaled_image ){
+         // center image in available region
+         int x0 = (widget->allocation.width - image_widget->scaled_image->cols)/2;
+         int y0 = (widget->allocation.height - image_widget->scaled_image->rows)/2;
+
+         gdk_draw_rgb_image( widget->window, widget->style->fg_gc[GTK_STATE_NORMAL],
+                 x0, y0, MIN(image_widget->scaled_image->cols, widget->allocation.width),
+                 MIN(image_widget->scaled_image->rows, widget->allocation.height),
+                 GDK_RGB_DITHER_MAX, image_widget->scaled_image->data.ptr, image_widget->scaled_image->step );
+  }
+  else if( image_widget->original_image ){
+         gdk_draw_rgb_image( widget->window, widget->style->fg_gc[GTK_STATE_NORMAL],
+                 0, 0,
+                 MIN(image_widget->original_image->cols, widget->allocation.width),
+                  MIN(image_widget->original_image->rows, widget->allocation.height),
+                 GDK_RGB_DITHER_MAX, image_widget->original_image->data.ptr, image_widget->original_image->step );
+  }
+  return TRUE;
+}
+
+static void
+cvImageWidget_destroy (GtkObject *object)
+{
+  CvImageWidget *image_widget;
+
+  g_return_if_fail (object != NULL);
+  g_return_if_fail (CV_IS_IMAGE_WIDGET (object));
+
+  image_widget = CV_IMAGE_WIDGET (object);
+
+  cvReleaseMat( &image_widget->scaled_image );
+  cvReleaseMat( &image_widget->original_image );
+
+  if (GTK_OBJECT_CLASS (parent_class)->destroy)
+    (* GTK_OBJECT_CLASS (parent_class)->destroy) (object);
+}
+
+static void cvImageWidget_class_init (CvImageWidgetClass * klass)
+{
+  GtkObjectClass *object_class;
+  GtkWidgetClass *widget_class;
+
+  object_class = (GtkObjectClass*) klass;
+  widget_class = (GtkWidgetClass*) klass;
+
+  parent_class = GTK_WIDGET_CLASS( gtk_type_class (gtk_widget_get_type ()) );
+
+  object_class->destroy = cvImageWidget_destroy;
+
+  widget_class->realize = cvImageWidget_realize;
+  widget_class->expose_event = cvImageWidget_expose;
+  widget_class->size_request = cvImageWidget_size_request;
+  widget_class->size_allocate = cvImageWidget_size_allocate;
+  widget_class->button_press_event = NULL;
+  widget_class->button_release_event = NULL;
+  widget_class->motion_notify_event = NULL;
+}
+
+static void
+cvImageWidget_init (CvImageWidget *image_widget)
+{
+       image_widget->original_image=0;
+       image_widget->scaled_image=0;
+       image_widget->flags=0;
+}
+
+GtkType cvImageWidget_get_type (void){
+  static GtkType image_type = 0;
+
+  if (!image_type)
+    {
+      static const GtkTypeInfo image_info =
+      {
+        (gchar*)"CvImageWidget",
+        sizeof (CvImageWidget),
+        sizeof (CvImageWidgetClass),
+        (GtkClassInitFunc) cvImageWidget_class_init,
+        (GtkObjectInitFunc) cvImageWidget_init,
+        /* reserved_1 */ NULL,
+        /* reserved_1 */ NULL,
+        (GtkClassInitFunc) NULL
+      };
+
+      image_type = gtk_type_unique (GTK_TYPE_WIDGET, &image_info);
+    }
+
+  return image_type;
+}
+/////////////////////////////////////////////////////////////////////////////
+// End CvImageWidget
+/////////////////////////////////////////////////////////////////////////////
+
+
+struct CvWindow;
+
+typedef struct CvTrackbar
+{
+    int signature;
+    GtkWidget* widget;
+    char* name;
+    CvTrackbar* next;
+    CvWindow* parent;
+    int* data;
+    int pos;
+    int maxval;
+    CvTrackbarCallback notify;
+    CvTrackbarCallback2 notify2;
+    void* userdata;
+}
+CvTrackbar;
+
+typedef struct CvWindow
+{
+    int signature;
+    GtkWidget* widget;
+    GtkWidget* frame;
+    GtkWidget* paned;
+    char* name;
+    CvWindow* prev;
+    CvWindow* next;
+
+    int last_key;
+    int flags;
+
+    CvMouseCallback on_mouse;
+    void* on_mouse_param;
+
+    struct
+    {
+        int pos;
+        int rows;
+        CvTrackbar* first;
+    }
+    toolbar;
+}
+CvWindow;
+
+
+static gboolean icvOnClose( GtkWidget* widget, GdkEvent* event, gpointer user_data );
+static gboolean icvOnKeyPress( GtkWidget* widget, GdkEventKey* event, gpointer user_data );
+static void icvOnTrackbar( GtkWidget* widget, gpointer user_data );
+static gboolean icvOnMouse( GtkWidget *widget, GdkEvent *event, gpointer user_data );
+
+#ifdef HAVE_GTHREAD
+int thread_started=0;
+static gpointer icvWindowThreadLoop();
+GMutex*                                   last_key_mutex;
+GCond*                            cond_have_key;
+GMutex*                                   window_mutex;
+GThread*                          window_thread;
+GtkWidget*             cvTopLevelWidget = 0;
+#endif
+
+static int             last_key = -1;
+static CvWindow* hg_windows = 0;
+
+CV_IMPL int cvInitSystem( int argc, char** argv )
+{
+    static int wasInitialized = 0;
+
+    // check initialization status
+    if( !wasInitialized )
+    {
+        hg_windows = 0;
+
+        gtk_init( &argc, &argv );
+        wasInitialized = 1;
+    }
+
+    return 0;
+}
+
+CV_IMPL int cvStartWindowThread(){
+#ifdef HAVE_GTHREAD
+       cvInitSystem(0,NULL);
+    if (!thread_started) {
+       if (!g_thread_supported ()) {
+           /* the GThread system wasn't inited, so init it */
+           g_thread_init(NULL);
+       }
+
+       // this mutex protects the window resources
+       window_mutex = g_mutex_new();
+
+       // protects the 'last key pressed' variable
+       last_key_mutex = g_mutex_new();
+
+       // conditional that indicates a key has been pressed
+       cond_have_key = g_cond_new();
+
+       // this is the window update thread
+       window_thread = g_thread_create((GThreadFunc) icvWindowThreadLoop,
+                                       NULL, TRUE, NULL);
+    }
+    thread_started = window_thread!=NULL;
+    return thread_started;
+#else
+    return 0;
+#endif
+}
+
+#ifdef HAVE_GTHREAD
+gpointer icvWindowThreadLoop(){
+       while(1){
+               g_mutex_lock(window_mutex);
+               gtk_main_iteration_do(FALSE);
+               g_mutex_unlock(window_mutex);
+
+               // little sleep
+               g_usleep(500);
+
+               g_thread_yield();
+       }
+       return NULL;
+}
+
+#define CV_LOCK_MUTEX() \
+if(thread_started && g_thread_self()!=window_thread){ g_mutex_lock( window_mutex ); } else { }
+
+#define CV_UNLOCK_MUTEX() \
+if(thread_started && g_thread_self()!=window_thread){ g_mutex_unlock( window_mutex); } else { }
+
+#else
+#define CV_LOCK_MUTEX()
+#define CV_UNLOCK_MUTEX()
+#endif
+
+static CvWindow* icvFindWindowByName( const char* name )
+{
+    CvWindow* window = hg_windows;
+    while( window != 0 && strcmp(name, window->name) != 0 )
+        window = window->next;
+
+    return window;
+}
+
+static CvWindow* icvWindowByWidget( GtkWidget* widget )
+{
+    CvWindow* window = hg_windows;
+
+    while( window != 0 && window->widget != widget &&
+           window->frame != widget && window->paned != widget )
+        window = window->next;
+
+    return window;
+}
+
+CV_IMPL int cvNamedWindow( const char* name, int flags )
+{
+    int result = 0;
+    CV_FUNCNAME( "cvNamedWindow" );
+
+    __BEGIN__;
+
+    CvWindow* window;
+    int len;
+
+    cvInitSystem(1,(char**)&name);
+    if( !name )
+        CV_ERROR( CV_StsNullPtr, "NULL name string" );
+
+    // Check the name in the storage
+    if( icvFindWindowByName( name ) != 0 )
+    {
+        result = 1;
+        EXIT;
+    }
+
+    len = strlen(name);
+    CV_CALL( window = (CvWindow*)cvAlloc(sizeof(CvWindow) + len + 1));
+    memset( window, 0, sizeof(*window));
+    window->name = (char*)(window + 1);
+    memcpy( window->name, name, len + 1 );
+    window->flags = flags;
+    window->signature = CV_WINDOW_MAGIC_VAL;
+    window->last_key = 0;
+    window->on_mouse = 0;
+    window->on_mouse_param = 0;
+    memset( &window->toolbar, 0, sizeof(window->toolbar));
+    window->next = hg_windows;
+    window->prev = 0;
+
+       CV_LOCK_MUTEX();
+
+    window->frame = gtk_window_new( GTK_WINDOW_TOPLEVEL );
+
+    window->paned = gtk_vbox_new( FALSE, 0 );
+    window->widget = cvImageWidgetNew( flags );
+    gtk_box_pack_end( GTK_BOX(window->paned), window->widget, TRUE, TRUE, 0 );
+    gtk_widget_show( window->widget );
+    gtk_container_add( GTK_CONTAINER(window->frame), window->paned );
+    gtk_widget_show( window->paned );
+       //
+       // configure event handlers
+       // TODO -- move this to CvImageWidget ?
+    gtk_signal_connect( GTK_OBJECT(window->frame), "key-press-event",
+                        GTK_SIGNAL_FUNC(icvOnKeyPress), window );
+    gtk_signal_connect( GTK_OBJECT(window->widget), "button-press-event",
+                        GTK_SIGNAL_FUNC(icvOnMouse), window );
+    gtk_signal_connect( GTK_OBJECT(window->widget), "button-release-event",
+                        GTK_SIGNAL_FUNC(icvOnMouse), window );
+    gtk_signal_connect( GTK_OBJECT(window->widget), "motion-notify-event",
+                        GTK_SIGNAL_FUNC(icvOnMouse), window );
+    gtk_signal_connect( GTK_OBJECT(window->frame), "delete-event",
+                        GTK_SIGNAL_FUNC(icvOnClose), window );
+
+       gtk_widget_add_events (window->widget, GDK_EXPOSURE_MASK | GDK_BUTTON_RELEASE_MASK |
+                                          GDK_BUTTON_PRESS_MASK | GDK_POINTER_MOTION_MASK) ;
+
+    gtk_widget_show( window->frame );
+    gtk_window_set_title( GTK_WINDOW(window->frame), name );
+
+    if( hg_windows )
+        hg_windows->prev = window;
+    hg_windows = window;
+
+    gtk_window_set_resizable( GTK_WINDOW(window->frame), (flags & CV_WINDOW_AUTOSIZE) == 0 );
+
+
+       // allow window to be resized
+       if( (flags & CV_WINDOW_AUTOSIZE)==0 ){
+               GdkGeometry geometry;
+               geometry.min_width = 50;
+               geometry.min_height = 50;
+               gtk_window_set_geometry_hints( GTK_WINDOW( window->frame ), GTK_WIDGET( window->widget ),
+                       &geometry, (GdkWindowHints) (GDK_HINT_MIN_SIZE));
+       }
+
+       CV_UNLOCK_MUTEX();
+
+    result = 1;
+    __END__;
+
+    return result;
+}
+
+
+static void icvDeleteWindow( CvWindow* window )
+{
+    CvTrackbar* trackbar;
+
+    if( window->prev )
+        window->prev->next = window->next;
+    else
+        hg_windows = window->next;
+
+    if( window->next )
+        window->next->prev = window->prev;
+
+    window->prev = window->next = 0;
+
+       gtk_widget_destroy( window->frame );
+
+    for( trackbar = window->toolbar.first; trackbar != 0; )
+    {
+        CvTrackbar* next = trackbar->next;
+        cvFree( &trackbar );
+        trackbar = next;
+    }
+
+    cvFree( &window );
+#ifdef HAVE_GTHREAD
+       // if last window, send key press signal
+       // to jump out of any waiting cvWaitKey's
+       if(hg_windows==0 && thread_started){
+               g_cond_broadcast(cond_have_key);
+       }
+#endif
+}
+
+
+CV_IMPL void cvDestroyWindow( const char* name )
+{
+    CV_FUNCNAME( "cvDestroyWindow" );
+
+    __BEGIN__;
+
+    CvWindow* window;
+
+    if(!name)
+        CV_ERROR( CV_StsNullPtr, "NULL name string" );
+
+    window = icvFindWindowByName( name );
+    if( !window )
+        EXIT;
+
+       // note that it is possible for the update thread to run this function
+       // if there is a call to cvShowImage in a mouse callback
+       // (this would produce a deadlock on window_mutex)
+       CV_LOCK_MUTEX();
+
+       icvDeleteWindow( window );
+
+       CV_UNLOCK_MUTEX();
+
+    __END__;
+}
+
+
+CV_IMPL void
+cvDestroyAllWindows( void )
+{
+       CV_LOCK_MUTEX();
+
+    while( hg_windows )
+    {
+        CvWindow* window = hg_windows;
+        icvDeleteWindow( window );
+    }
+       CV_UNLOCK_MUTEX();
+}
+
+CvSize icvCalcOptimalWindowSize( CvWindow * window, CvSize new_image_size){
+       CvSize window_size;
+       GtkWidget * toplevel = gtk_widget_get_toplevel( window->frame );
+       gdk_drawable_get_size( GDK_DRAWABLE(toplevel->window),
+                       &window_size.width, &window_size.height );
+
+       window_size.width = window_size.width + new_image_size.width - window->widget->allocation.width;
+       window_size.height = window_size.height + new_image_size.height - window->widget->allocation.height;
+
+       return window_size;
+}
+
+CV_IMPL void
+cvShowImage( const char* name, const CvArr* arr )
+{
+    CV_FUNCNAME( "cvShowImage" );
+
+    __BEGIN__;
+
+    CvWindow* window;
+
+    if( !name )
+        CV_ERROR( CV_StsNullPtr, "NULL name" );
+
+       CV_LOCK_MUTEX();
+
+    window = icvFindWindowByName(name);
+       if(!window)
+       {
+               cvNamedWindow(name, 1);
+               window = icvFindWindowByName(name);
+       }
+    if( window && arr ){
+               CvImageWidget * image_widget = CV_IMAGE_WIDGET( window->widget );
+               cvImageWidgetSetImage( image_widget, arr );
+       }
+
+       CV_UNLOCK_MUTEX();
+
+    __END__;
+}
+
+CV_IMPL void cvResizeWindow(const char* name, int width, int height )
+{
+    CV_FUNCNAME( "cvResizeWindow" );
+
+    __BEGIN__;
+
+    CvWindow* window;
+       CvImageWidget * image_widget;
+
+    if( !name )
+        CV_ERROR( CV_StsNullPtr, "NULL name" );
+
+    window = icvFindWindowByName(name);
+    if(!window)
+        EXIT;
+
+       image_widget = CV_IMAGE_WIDGET( window->widget );
+       if(image_widget->flags & CV_WINDOW_AUTOSIZE)
+               EXIT;
+
+       CV_LOCK_MUTEX();
+
+       gtk_window_set_resizable( GTK_WINDOW(window->frame), 1 );
+    gtk_window_resize( GTK_WINDOW(window->frame), width, height );
+
+       // disable initial resize since presumably user wants to keep
+       // this window size
+       image_widget->flags &= ~CV_WINDOW_NO_IMAGE;
+
+       CV_UNLOCK_MUTEX();
+
+    __END__;
+}
+
+
+CV_IMPL void cvMoveWindow( const char* name, int x, int y )
+{
+    CV_FUNCNAME( "cvMoveWindow" );
+
+    __BEGIN__;
+
+    CvWindow* window;
+
+    if( !name )
+        CV_ERROR( CV_StsNullPtr, "NULL name" );
+
+    window = icvFindWindowByName(name);
+    if(!window)
+        EXIT;
+
+       CV_LOCK_MUTEX();
+
+    gtk_window_move( GTK_WINDOW(window->frame), x, y );
+
+       CV_UNLOCK_MUTEX();
+
+    __END__;
+}
+
+
+static CvTrackbar*
+icvFindTrackbarByName( const CvWindow* window, const char* name )
+{
+    CvTrackbar* trackbar = window->toolbar.first;
+
+    for( ; trackbar != 0 && strcmp( trackbar->name, name ) != 0; trackbar = trackbar->next )
+        ;
+
+    return trackbar;
+}
+
+static int
+icvCreateTrackbar( const char* trackbar_name, const char* window_name,
+                   int* val, int count, CvTrackbarCallback on_notify,
+                   CvTrackbarCallback2 on_notify2, void* userdata )
+{
+    int result = 0;
+
+    CV_FUNCNAME( "icvCreateTrackbar" );
+
+    __BEGIN__;
+
+    /*char slider_name[32];*/
+    CvWindow* window = 0;
+    CvTrackbar* trackbar = 0;
+
+    if( !window_name || !trackbar_name )
+        CV_ERROR( CV_StsNullPtr, "NULL window or trackbar name" );
+
+    if( count <= 0 )
+        CV_ERROR( CV_StsOutOfRange, "Bad trackbar maximal value" );
+
+    window = icvFindWindowByName(window_name);
+    if( !window )
+        EXIT;
+
+    trackbar = icvFindTrackbarByName(window,trackbar_name);
+
+       CV_LOCK_MUTEX();
+
+    if( !trackbar )
+    {
+        int len = strlen(trackbar_name);
+        trackbar = (CvTrackbar*)cvAlloc(sizeof(CvTrackbar) + len + 1);
+        memset( trackbar, 0, sizeof(*trackbar));
+        trackbar->signature = CV_TRACKBAR_MAGIC_VAL;
+        trackbar->name = (char*)(trackbar+1);
+        memcpy( trackbar->name, trackbar_name, len + 1 );
+        trackbar->parent = window;
+        trackbar->next = window->toolbar.first;
+        window->toolbar.first = trackbar;
+
+        GtkWidget* hscale_box = gtk_hbox_new( FALSE, 10 );
+        GtkWidget* hscale_label = gtk_label_new( trackbar_name );
+        GtkWidget* hscale = gtk_hscale_new_with_range( 0, count, 1 );
+        gtk_range_set_update_policy( GTK_RANGE(hscale), GTK_UPDATE_CONTINUOUS );
+        gtk_scale_set_digits( GTK_SCALE(hscale), 0 );
+        //gtk_scale_set_value_pos( hscale, GTK_POS_TOP );
+        gtk_scale_set_draw_value( GTK_SCALE(hscale), TRUE );
+
+        trackbar->widget = hscale;
+        gtk_box_pack_start( GTK_BOX(hscale_box), hscale_label, FALSE, FALSE, 5 );
+        gtk_widget_show( hscale_label );
+        gtk_box_pack_start( GTK_BOX(hscale_box), hscale, TRUE, TRUE, 5 );
+        gtk_widget_show( hscale );
+        gtk_box_pack_start( GTK_BOX(window->paned), hscale_box, FALSE, FALSE, 5 );
+        gtk_widget_show( hscale_box );
+
+       }
+
+    if( val )
+    {
+        int value = *val;
+        if( value < 0 )
+            value = 0;
+        if( value > count )
+            value = count;
+        gtk_range_set_value( GTK_RANGE(trackbar->widget), value );
+        trackbar->pos = value;
+        trackbar->data = val;
+    }
+
+    trackbar->maxval = count;
+    trackbar->notify = on_notify;
+    trackbar->notify2 = on_notify2;
+    trackbar->userdata = userdata;
+    gtk_signal_connect( GTK_OBJECT(trackbar->widget), "value-changed",
+                        GTK_SIGNAL_FUNC(icvOnTrackbar), trackbar );
+
+       // queue a widget resize to trigger a window resize to
+       // compensate for the addition of trackbars
+       gtk_widget_queue_resize( GTK_WIDGET(window->widget) );
+
+
+       CV_UNLOCK_MUTEX();
+
+    result = 1;
+
+    __END__;
+
+    return result;
+}
+
+
+CV_IMPL int
+cvCreateTrackbar( const char* trackbar_name, const char* window_name,
+                  int* val, int count, CvTrackbarCallback on_notify )
+{
+    return icvCreateTrackbar(trackbar_name, window_name, val, count,
+                             on_notify, 0, 0);
+}
+
+
+CV_IMPL int
+cvCreateTrackbar2( const char* trackbar_name, const char* window_name,
+                   int* val, int count, CvTrackbarCallback2 on_notify2,
+                   void* userdata )
+{
+    return icvCreateTrackbar(trackbar_name, window_name, val, count,
+                             0, on_notify2, userdata);
+}
+
+
+CV_IMPL void
+cvSetMouseCallback( const char* window_name, CvMouseCallback on_mouse, void* param )
+{
+    CV_FUNCNAME( "cvSetMouseCallback" );
+
+    __BEGIN__;
+
+    CvWindow* window = 0;
+
+    if( !window_name )
+        CV_ERROR( CV_StsNullPtr, "NULL window name" );
+
+    window = icvFindWindowByName(window_name);
+    if( !window )
+        EXIT;
+
+    window->on_mouse = on_mouse;
+    window->on_mouse_param = param;
+
+    __END__;
+}
+
+
+CV_IMPL int cvGetTrackbarPos( const char* trackbar_name, const char* window_name )
+{
+    int pos = -1;
+
+    CV_FUNCNAME( "cvGetTrackbarPos" );
+
+    __BEGIN__;
+
+    CvWindow* window;
+    CvTrackbar* trackbar = 0;
+
+    if( trackbar_name == 0 || window_name == 0 )
+        CV_ERROR( CV_StsNullPtr, "NULL trackbar or window name" );
+
+    window = icvFindWindowByName( window_name );
+    if( window )
+        trackbar = icvFindTrackbarByName( window, trackbar_name );
+
+    if( trackbar )
+        pos = trackbar->pos;
+
+    __END__;
+
+    return pos;
+}
+
+
+CV_IMPL void cvSetTrackbarPos( const char* trackbar_name, const char* window_name, int pos )
+{
+    CV_FUNCNAME( "cvSetTrackbarPos" );
+
+    __BEGIN__;
+
+    CvWindow* window;
+    CvTrackbar* trackbar = 0;
+
+    if( trackbar_name == 0 || window_name == 0 )
+        CV_ERROR( CV_StsNullPtr, "NULL trackbar or window name" );
+
+    window = icvFindWindowByName( window_name );
+    if( window )
+        trackbar = icvFindTrackbarByName( window, trackbar_name );
+
+    if( trackbar )
+    {
+        if( pos < 0 )
+            pos = 0;
+
+        if( pos > trackbar->maxval )
+            pos = trackbar->maxval;
+    }
+
+       CV_LOCK_MUTEX();
+
+    gtk_range_set_value( GTK_RANGE(trackbar->widget), pos );
+
+       CV_UNLOCK_MUTEX();
+
+    __END__;
+}
+
+
+CV_IMPL void* cvGetWindowHandle( const char* window_name )
+{
+    void* widget = 0;
+
+    CV_FUNCNAME( "cvGetWindowHandle" );
+
+    __BEGIN__;
+
+    CvWindow* window;
+
+    if( window_name == 0 )
+        CV_ERROR( CV_StsNullPtr, "NULL window name" );
+
+    window = icvFindWindowByName( window_name );
+    if( window )
+        widget = (void*)window->widget;
+
+    __END__;
+
+    return widget;
+}
+
+
+CV_IMPL const char* cvGetWindowName( void* window_handle )
+{
+    const char* window_name = "";
+
+    CV_FUNCNAME( "cvGetWindowName" );
+
+    __BEGIN__;
+
+    CvWindow* window;
+
+    if( window_handle == 0 )
+        CV_ERROR( CV_StsNullPtr, "NULL window" );
+
+    window = icvWindowByWidget( (GtkWidget*)window_handle );
+    if( window )
+        window_name = window->name;
+
+    __END__;
+
+    return window_name;
+}
+
+static gboolean icvOnKeyPress( GtkWidget * /*widget*/,
+                GdkEventKey* event, gpointer /*user_data*/ )
+{
+    int code = 0;
+
+    switch( event->keyval )
+    {
+    case GDK_Escape:
+        code = 27;
+        break;
+    case GDK_Return:
+    case GDK_Linefeed:
+        code = '\n';
+        break;
+    case GDK_Tab:
+        code = '\t';
+       break;
+    default:
+        code = event->keyval;
+    }
+
+    code |= event->state << 16;
+
+#ifdef HAVE_GTHREAD
+       if(thread_started) g_mutex_lock(last_key_mutex);
+#endif
+
+       last_key = code;
+
+#ifdef HAVE_GTHREAD
+       if(thread_started){
+               // signal any waiting threads
+               g_cond_broadcast(cond_have_key);
+               g_mutex_unlock(last_key_mutex);
+       }
+#endif
+
+    return FALSE;
+}
+
+
+static void icvOnTrackbar( GtkWidget* widget, gpointer user_data )
+{
+    int pos = cvRound( gtk_range_get_value(GTK_RANGE(widget)));
+    CvTrackbar* trackbar = (CvTrackbar*)user_data;
+
+    if( trackbar && trackbar->signature == CV_TRACKBAR_MAGIC_VAL &&
+        trackbar->widget == widget )
+    {
+        trackbar->pos = pos;
+        if( trackbar->data )
+            *trackbar->data = pos;
+        if( trackbar->notify2 )
+            trackbar->notify2(pos, trackbar->userdata);
+        else if( trackbar->notify )
+            trackbar->notify(pos);
+    }
+}
+
+static gboolean icvOnClose( GtkWidget* widget, GdkEvent* /*event*/, gpointer user_data )
+{
+    CvWindow* window = (CvWindow*)user_data;
+    if( window->signature == CV_WINDOW_MAGIC_VAL &&
+        window->frame == widget )
+       {
+        icvDeleteWindow(window);
+       }
+    return TRUE;
+}
+
+
+static gboolean icvOnMouse( GtkWidget *widget, GdkEvent *event, gpointer user_data )
+{
+       // TODO move this logic to CvImageWidget
+    CvWindow* window = (CvWindow*)user_data;
+       CvPoint2D32f pt32f = {-1., -1.};
+    CvPoint pt = {-1,-1};
+    int cv_event = -1, state = 0;
+       CvImageWidget * image_widget = CV_IMAGE_WIDGET( widget );
+
+    if( window->signature != CV_WINDOW_MAGIC_VAL ||
+        window->widget != widget || !window->widget ||
+               !window->on_mouse || !image_widget->original_image)
+        return FALSE;
+
+    if( event->type == GDK_MOTION_NOTIFY )
+    {
+        GdkEventMotion* event_motion = (GdkEventMotion*)event;
+
+        cv_event = CV_EVENT_MOUSEMOVE;
+        pt32f.x = cvRound(event_motion->x);
+        pt32f.y = cvRound(event_motion->y);
+        state = event_motion->state;
+    }
+    else if( event->type == GDK_BUTTON_PRESS ||
+             event->type == GDK_BUTTON_RELEASE ||
+             event->type == GDK_2BUTTON_PRESS )
+    {
+        GdkEventButton* event_button = (GdkEventButton*)event;
+        pt32f.x = cvRound(event_button->x);
+        pt32f.y = cvRound(event_button->y);
+
+
+        if( event_button->type == GDK_BUTTON_PRESS )
+        {
+            cv_event = event_button->button == 1 ? CV_EVENT_LBUTTONDOWN :
+                       event_button->button == 2 ? CV_EVENT_MBUTTONDOWN :
+                       event_button->button == 3 ? CV_EVENT_RBUTTONDOWN : 0;
+        }
+        else if( event_button->type == GDK_BUTTON_RELEASE )
+        {
+            cv_event = event_button->button == 1 ? CV_EVENT_LBUTTONUP :
+                       event_button->button == 2 ? CV_EVENT_MBUTTONUP :
+                       event_button->button == 3 ? CV_EVENT_RBUTTONUP : 0;
+        }
+        else if( event_button->type == GDK_2BUTTON_PRESS )
+        {
+            cv_event = event_button->button == 1 ? CV_EVENT_LBUTTONDBLCLK :
+                       event_button->button == 2 ? CV_EVENT_MBUTTONDBLCLK :
+                       event_button->button == 3 ? CV_EVENT_RBUTTONDBLCLK : 0;
+        }
+        state = event_button->state;
+    }
+
+    if( cv_event >= 0 ){
+               // scale point if image is scaled
+               if( (image_widget->flags & CV_WINDOW_AUTOSIZE)==0 &&
+                    image_widget->original_image &&
+                        image_widget->scaled_image ){
+                       // image origin is not necessarily at (0,0)
+                       int x0 = (widget->allocation.width - image_widget->scaled_image->cols)/2;
+                       int y0 = (widget->allocation.height - image_widget->scaled_image->rows)/2;
+                       pt.x = cvRound( ((pt32f.x-x0)*image_widget->original_image->cols)/
+                                                               image_widget->scaled_image->cols );
+                       pt.y = cvRound( ((pt32f.y-y0)*image_widget->original_image->rows)/
+                                                               image_widget->scaled_image->rows );
+               }
+               else{
+                       pt = cvPointFrom32f( pt32f );
+               }
+
+               if((unsigned)pt.x < (unsigned)(image_widget->original_image->width) &&
+                  (unsigned)pt.y < (unsigned)(image_widget->original_image->height) )
+               {
+                       int flags = (state & GDK_SHIFT_MASK ? CV_EVENT_FLAG_SHIFTKEY : 0) |
+                               (state & GDK_CONTROL_MASK ? CV_EVENT_FLAG_CTRLKEY : 0) |
+                               (state & (GDK_MOD1_MASK|GDK_MOD2_MASK) ? CV_EVENT_FLAG_ALTKEY : 0) |
+                               (state & GDK_BUTTON1_MASK ? CV_EVENT_FLAG_LBUTTON : 0) |
+                               (state & GDK_BUTTON2_MASK ? CV_EVENT_FLAG_MBUTTON : 0) |
+                               (state & GDK_BUTTON3_MASK ? CV_EVENT_FLAG_RBUTTON : 0);
+                       window->on_mouse( cv_event, pt.x, pt.y, flags, window->on_mouse_param );
+               }
+       }
+
+               return FALSE;
+       }
+
+
+static gboolean icvAlarm( gpointer user_data )
+{
+    *(int*)user_data = 1;
+    return FALSE;
+}
+
+
+CV_IMPL int cvWaitKey( int delay )
+{
+#ifdef HAVE_GTHREAD
+       if(thread_started && g_thread_self()!=window_thread){
+               gboolean expired;
+               int my_last_key;
+
+               // wait for signal or timeout if delay > 0
+               if(delay>0){
+                       GTimeVal timer;
+                       g_get_current_time(&timer);
+                       g_time_val_add(&timer, delay*1000);
+                       expired = !g_cond_timed_wait(cond_have_key, last_key_mutex, &timer);
+               }
+               else{
+                       g_cond_wait(cond_have_key, last_key_mutex);
+                       expired=false;
+               }
+               my_last_key = last_key;
+               g_mutex_unlock(last_key_mutex);
+               if(expired || hg_windows==0){
+                       return -1;
+               }
+               return my_last_key;
+       }
+       else{
+#endif
+               int expired = 0;
+               guint timer = 0;
+               if( delay > 0 )
+                       timer = g_timeout_add( delay, icvAlarm, &expired );
+               last_key = -1;
+               while( gtk_main_iteration_do(TRUE) && last_key < 0 && !expired && hg_windows != 0 )
+                       ;
+
+               if( delay > 0 && !expired )
+                       g_source_remove(timer);
+#ifdef HAVE_GTHREAD
+       }
+#endif
+       return last_key;
+}
+
+
+#endif  // HAVE_GTK
+#endif  // WIN32
+
+/* End of file. */