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 "backend.hpp"
44
45#if defined (HAVE_GTK)
46
47#include <gtk/gtk.h>
48
49#if (GTK_MAJOR_VERSION == 2) && defined(HAVE_OPENGL) && !defined(HAVE_GTKGLEXT)
50 #undef HAVE_OPENGL // gtkglext is required
51#endif
52
53#include <gdk/gdkkeysyms.h>
54#include <gdk-pixbuf/gdk-pixbuf.h>
55#include <stdio.h>
56
57#if (GTK_MAJOR_VERSION == 2)
58 #define GTK_VERSION2 1
59#endif //GTK_MAJOR_VERSION >= 2.0
60#if (GTK_MAJOR_VERSION == 3)
61 #define GTK_VERSION3 1
62#endif //GTK_MAJOR_VERSION >= 3
63#if (GTK_MAJOR_VERSION == 3 && GTK_MINOR_VERSION >= 4)
64 #define GTK_VERSION3_4 1
65#endif
66
67#ifdef HAVE_OPENGL
68 #ifdef GTK_VERSION3
69 #include <gtk/gtkglarea.h>
70 #else
71 #include <gtk/gtkgl.h>
72 #include <GL/glu.h>
73 #endif
74 #include <GL/gl.h>
75#endif
76
77#include <opencv2/core/utils/logger.hpp>
78#include "opencv2/imgproc.hpp"
79
80using namespace cv;
81
82#ifndef BIT_ALLIN
83 #define BIT_ALLIN(x,y) ( ((x)&(y)) == (y) )
84#endif
85#ifndef BIT_MAP
86 #define BIT_MAP(x,y,z) ( ((x)&(y)) ? (z) : 0 )
87#endif
88
89// TODO Fix the initial window size when flags=0. Right now the initial window is by default
90// 320x240 size. A better default would be actual size of the image. Problem
91// is determining desired window size with trackbars while still allowing resizing.
92//
93// Gnome Totem source may be of use here, see bacon_video_widget_set_scale_ratio
94// in totem/src/backend/bacon-video-widget-xine.c
95
96////////////////////////////////////////////////////////////
97// CvImageWidget GTK Widget Public API
98////////////////////////////////////////////////////////////
99typedef struct _CvImageWidget CvImageWidget;
100typedef struct _CvImageWidgetClass CvImageWidgetClass;
101
102struct _CvImageWidget {
103 GtkWidget widget;
104 CvMat * original_image;
105 CvMat * scaled_image;
106 int flags;
107};
108
109struct _CvImageWidgetClass
110{
111 GtkWidgetClass parent_class;
112};
113
114
115/** Allocate new image viewer widget */
116GtkWidget* cvImageWidgetNew (int flags);
117
118// standard GTK object macros
119#define CV_IMAGE_WIDGET(obj) G_TYPE_CHECK_INSTANCE_CAST (obj, cvImageWidget_get_type (), CvImageWidget)
120#define CV_IMAGE_WIDGET_CLASS(klass) GTK_CHECK_CLASS_CAST (klass, cvImageWidget_get_type (), CvImageWidgetClass)
121#define CV_IS_IMAGE_WIDGET(obj) G_TYPE_CHECK_INSTANCE_TYPE (obj, cvImageWidget_get_type ())
122
123/////////////////////////////////////////////////////////////////////////////
124// Private API ////////////////////////////////////////////////////////
125/////////////////////////////////////////////////////////////////////////////
126GType cvImageWidget_get_type (void);
127
128static GtkWidgetClass * parent_class = NULL;
129
130// flag to help size initial window
131#define CV_WINDOW_NO_IMAGE 2
132
133/** Set the image to display in the widget */
134static
135void cvImageWidgetSetImage(CvImageWidget * widget, const CvArr *arr)
136{
137 CvMat * mat, stub;
138 int origin=0;
139
140 //printf("cvImageWidgetSetImage\n");
141
142 if( CV_IS_IMAGE_HDR( arr ))
143 origin = ((IplImage*)arr)->origin;
144
145 mat = cvGetMat(arr, header: &stub);
146
147 if(widget->original_image && !CV_ARE_SIZES_EQ(mat, widget->original_image)){
148 cvReleaseMat( mat: &widget->original_image );
149 }
150 if(!widget->original_image){
151 widget->original_image = cvCreateMat( rows: mat->rows, cols: mat->cols, CV_8UC3 );
152 gtk_widget_queue_resize( GTK_WIDGET( widget ) );
153 }
154 CV_Assert(origin == 0);
155 convertToShow(src: cv::cvarrToMat(arr), arr: widget->original_image);
156 if(widget->scaled_image){
157 cvResize( src: widget->original_image, dst: widget->scaled_image, interpolation: CV_INTER_AREA );
158 }
159
160 // window does not refresh without this
161 gtk_widget_queue_draw( GTK_WIDGET(widget) );
162}
163
164GtkWidget*
165cvImageWidgetNew (int flags)
166{
167 CvImageWidget *image_widget;
168
169 image_widget = CV_IMAGE_WIDGET( gtk_widget_new (cvImageWidget_get_type (), NULL) );
170 CV_Assert(image_widget && "GTK widget creation is failed. Ensure that there is no GTK2/GTK3 libraries conflict");
171 image_widget->original_image = 0;
172 image_widget->scaled_image = 0;
173 image_widget->flags = flags | CV_WINDOW_NO_IMAGE;
174
175 return GTK_WIDGET (image_widget);
176}
177
178static void
179cvImageWidget_realize (GtkWidget *widget)
180{
181 GdkWindowAttr attributes;
182 gint attributes_mask;
183
184#if defined(GTK_VERSION3)
185 GtkAllocation allocation;
186 gtk_widget_get_allocation(widget, allocation: &allocation);
187#endif //GTK_VERSION3
188
189 //printf("cvImageWidget_realize\n");
190 g_return_if_fail (widget != NULL);
191 g_return_if_fail (CV_IS_IMAGE_WIDGET (widget));
192
193 gtk_widget_set_realized(widget, TRUE);
194
195#if defined(GTK_VERSION3)
196 attributes.x = allocation.x;
197 attributes.y = allocation.y;
198 attributes.width = allocation.width;
199 attributes.height = allocation.height;
200#elif defined(GTK_VERSION2)
201 attributes.x = widget->allocation.x;
202 attributes.y = widget->allocation.y;
203 attributes.width = widget->allocation.width;
204 attributes.height = widget->allocation.height;
205#endif //GTK_VERSION3
206
207 attributes.wclass = GDK_INPUT_OUTPUT;
208 attributes.window_type = GDK_WINDOW_CHILD;
209 attributes.event_mask = gtk_widget_get_events (widget) |
210 GDK_EXPOSURE_MASK | GDK_BUTTON_PRESS_MASK |
211 GDK_BUTTON_RELEASE_MASK | GDK_POINTER_MOTION_MASK;
212 attributes.visual = gtk_widget_get_visual (widget);
213
214#if defined(GTK_VERSION3)
215 attributes_mask = GDK_WA_X | GDK_WA_Y | GDK_WA_VISUAL;
216 gtk_widget_set_window(
217 widget,
218 window: gdk_window_new(
219 parent: gtk_widget_get_parent_window(widget),
220 attributes: &attributes,
221 attributes_mask
222 )
223 );
224
225 gtk_widget_set_style(
226 widget,
227 style: gtk_style_attach(
228 style: gtk_widget_get_style(widget),
229 window: gtk_widget_get_window(widget)
230 )
231 );
232
233 gdk_window_set_user_data (
234 window: gtk_widget_get_window(widget),
235 user_data: widget
236 );
237
238 gtk_style_set_background (
239 style: gtk_widget_get_style(widget),
240 window: gtk_widget_get_window(widget),
241 state_type: GTK_STATE_ACTIVE
242 );
243 #elif defined(GTK_VERSION2)
244 // The following lines are included to prevent breaking
245 // compatibility with older Gtk2 (<gtk+-2.18) libraries.
246 attributes.colormap = gtk_widget_get_colormap (widget);
247 attributes_mask = GDK_WA_X | GDK_WA_Y | GDK_WA_VISUAL | GDK_WA_COLORMAP;
248 widget->window = gdk_window_new (widget->parent->window, &attributes, attributes_mask);
249
250 widget->style = gtk_style_attach (widget->style, widget->window);
251 gdk_window_set_user_data (widget->window, widget);
252
253 gtk_style_set_background (widget->style, widget->window, GTK_STATE_ACTIVE);
254#endif // GTK_VERSION3
255}
256
257static CvSize cvImageWidget_calc_size( int im_width, int im_height, int max_width, int max_height ){
258 float aspect = (float)im_width/(float)im_height;
259 float max_aspect = (float)max_width/(float)max_height;
260 if(aspect > max_aspect){
261 return cvSize( width: max_width, height: cvRound(value: max_width/aspect) );
262 }
263 return cvSize( width: cvRound(value: max_height*aspect), height: max_height );
264}
265
266#if defined (GTK_VERSION3)
267static void
268cvImageWidget_get_preferred_width (GtkWidget *widget, gint *minimal_width, gint *natural_width)
269{
270 g_return_if_fail (widget != NULL);
271 g_return_if_fail (CV_IS_IMAGE_WIDGET (widget));
272 CvImageWidget * image_widget = CV_IMAGE_WIDGET( widget );
273
274 if(image_widget->original_image != NULL) {
275 *minimal_width = (image_widget->flags & cv::WINDOW_AUTOSIZE) != cv::WINDOW_AUTOSIZE ?
276 gdk_window_get_width(window: gtk_widget_get_window(widget)) : image_widget->original_image->cols;
277 }
278 else {
279 *minimal_width = 320;
280 }
281
282 if(image_widget->scaled_image != NULL) {
283 *natural_width = *minimal_width < image_widget->scaled_image->cols ?
284 image_widget->scaled_image->cols : *minimal_width;
285 }
286 else {
287 *natural_width = *minimal_width;
288 }
289}
290
291static void
292cvImageWidget_get_preferred_height (GtkWidget *widget, gint *minimal_height, gint *natural_height)
293{
294 g_return_if_fail (widget != NULL);
295 g_return_if_fail (CV_IS_IMAGE_WIDGET (widget));
296 CvImageWidget * image_widget = CV_IMAGE_WIDGET( widget );
297
298 if(image_widget->original_image != NULL) {
299 *minimal_height = (image_widget->flags & cv::WINDOW_AUTOSIZE) != cv::WINDOW_AUTOSIZE ?
300 gdk_window_get_height(window: gtk_widget_get_window(widget)) : image_widget->original_image->rows;
301 }
302 else {
303 *minimal_height = 240;
304 }
305
306 if(image_widget->scaled_image != NULL) {
307 *natural_height = *minimal_height < image_widget->scaled_image->rows ?
308 image_widget->scaled_image->rows : *minimal_height;
309 }
310 else {
311 *natural_height = *minimal_height;
312 }
313}
314#endif //GTK_VERSION3
315
316#if defined (GTK_VERSION2)
317static void
318cvImageWidget_size_request (GtkWidget *widget,
319 GtkRequisition *requisition)
320{
321 CvImageWidget * image_widget = CV_IMAGE_WIDGET( widget );
322
323 //printf("cvImageWidget_size_request ");
324 // the case the first time cvShowImage called or when AUTOSIZE
325 if( image_widget->original_image &&
326 ((image_widget->flags & cv::WINDOW_AUTOSIZE) ||
327 (image_widget->flags & CV_WINDOW_NO_IMAGE)))
328 {
329 //printf("original ");
330 requisition->width = image_widget->original_image->cols;
331 requisition->height = image_widget->original_image->rows;
332 }
333 // default case
334 else if(image_widget->scaled_image){
335 //printf("scaled ");
336 requisition->width = image_widget->scaled_image->cols;
337 requisition->height = image_widget->scaled_image->rows;
338 }
339 // the case before cvShowImage called
340 else{
341 //printf("default ");
342 requisition->width = 320;
343 requisition->height = 240;
344 }
345 //printf("%d %d\n",requisition->width, requisition->height);
346}
347#endif //GTK_VERSION2
348
349static void cvImageWidget_set_size(GtkWidget * widget, int max_width, int max_height){
350 CvImageWidget * image_widget = CV_IMAGE_WIDGET( widget );
351
352 //printf("cvImageWidget_set_size %d %d\n", max_width, max_height);
353
354 // don't allow to set the size
355 if(image_widget->flags & cv::WINDOW_AUTOSIZE) return;
356 if(!image_widget->original_image) return;
357
358 CvSize scaled_image_size = cvImageWidget_calc_size( im_width: image_widget->original_image->cols,
359 im_height: image_widget->original_image->rows, max_width, max_height );
360
361 if( image_widget->scaled_image &&
362 ( image_widget->scaled_image->cols != scaled_image_size.width ||
363 image_widget->scaled_image->rows != scaled_image_size.height ))
364 {
365 cvReleaseMat( mat: &image_widget->scaled_image );
366 }
367 if( !image_widget->scaled_image ){
368 image_widget->scaled_image = cvCreateMat( rows: scaled_image_size.height, cols: scaled_image_size.width, CV_8UC3 );
369
370
371 }
372 CV_Assert(image_widget->scaled_image);
373}
374
375static void
376cvImageWidget_size_allocate (GtkWidget *widget,
377 GtkAllocation *allocation)
378{
379 CvImageWidget *image_widget;
380
381 //printf("cvImageWidget_size_allocate\n");
382 g_return_if_fail (widget != NULL);
383 g_return_if_fail (CV_IS_IMAGE_WIDGET (widget));
384 g_return_if_fail (allocation != NULL);
385
386#if defined (GTK_VERSION3)
387 gtk_widget_set_allocation(widget, allocation);
388#elif defined (GTK_VERSION2)
389 widget->allocation = *allocation;
390#endif //GTK_VERSION3
391 image_widget = CV_IMAGE_WIDGET (widget);
392
393
394 if( (image_widget->flags & cv::WINDOW_AUTOSIZE)==0 && image_widget->original_image ){
395 // (re) allocated scaled image
396 if( image_widget->flags & CV_WINDOW_NO_IMAGE ){
397 cvImageWidget_set_size( widget, max_width: image_widget->original_image->cols,
398 max_height: image_widget->original_image->rows);
399 }
400 else{
401 cvImageWidget_set_size( widget, max_width: allocation->width, max_height: allocation->height );
402 }
403 cvResize( src: image_widget->original_image, dst: image_widget->scaled_image, interpolation: CV_INTER_AREA );
404 }
405
406 if (gtk_widget_get_realized (widget))
407 {
408 image_widget = CV_IMAGE_WIDGET (widget);
409
410 if( image_widget->original_image &&
411 ((image_widget->flags & cv::WINDOW_AUTOSIZE) ||
412 (image_widget->flags & CV_WINDOW_NO_IMAGE)) )
413 {
414#if defined (GTK_VERSION3)
415 allocation->width = image_widget->original_image->cols;
416 allocation->height = image_widget->original_image->rows;
417 gtk_widget_set_allocation(widget, allocation);
418#elif defined (GTK_VERSION2)
419 widget->allocation.width = image_widget->original_image->cols;
420 widget->allocation.height = image_widget->original_image->rows;
421#endif //GTK_VERSION3
422 gdk_window_move_resize( window: gtk_widget_get_window(widget),
423 x: allocation->x, y: allocation->y,
424 width: image_widget->original_image->cols, height: image_widget->original_image->rows );
425 if(image_widget->flags & CV_WINDOW_NO_IMAGE){
426 image_widget->flags &= ~CV_WINDOW_NO_IMAGE;
427 gtk_widget_queue_resize( GTK_WIDGET(widget) );
428 }
429 }
430 else{
431 gdk_window_move_resize (window: gtk_widget_get_window(widget),
432 x: allocation->x, y: allocation->y,
433 width: allocation->width, height: allocation->height );
434 }
435 }
436}
437
438#if defined (GTK_VERSION3)
439static void
440cvImageWidget_destroy (GtkWidget *object)
441#else
442static void
443cvImageWidget_destroy (GtkObject *object)
444#endif //GTK_VERSION3
445{
446 CvImageWidget *image_widget;
447
448 g_return_if_fail (object != NULL);
449 g_return_if_fail (CV_IS_IMAGE_WIDGET (object));
450
451 image_widget = CV_IMAGE_WIDGET (object);
452
453 cvReleaseMat( mat: &image_widget->scaled_image );
454 cvReleaseMat( mat: &image_widget->original_image );
455
456#if defined (GTK_VERSION3)
457 if (GTK_WIDGET_CLASS (parent_class)->destroy)
458 (* GTK_WIDGET_CLASS (parent_class)->destroy) (object);
459#else
460 if (GTK_OBJECT_CLASS (parent_class)->destroy)
461 (* GTK_OBJECT_CLASS (parent_class)->destroy) (object);
462#endif //GTK_VERSION3
463}
464
465static void cvImageWidget_class_init (gpointer g_class, gpointer /*class_data*/)
466{
467 CvImageWidgetClass* klass = (CvImageWidgetClass*)g_class;
468#if defined (GTK_VERSION3)
469 GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass);
470#endif //GTK_VERSION3
471#if defined (GTK_VERSION2)
472 GtkObjectClass *object_class = (GtkObjectClass*) klass;
473 GtkWidgetClass *widget_class = (GtkWidgetClass*) klass;
474#endif //GTK_VERSION2
475
476 parent_class = GTK_WIDGET_CLASS( g_type_class_peek (gtk_widget_get_type ()) );
477
478#if defined (GTK_VERSION3)
479 widget_class->destroy = cvImageWidget_destroy;
480 widget_class->get_preferred_width = cvImageWidget_get_preferred_width;
481 widget_class->get_preferred_height = cvImageWidget_get_preferred_height;
482#endif //GTK_VERSION3
483#if defined (GTK_VERSION2)
484 object_class->destroy = cvImageWidget_destroy;
485 widget_class->size_request = cvImageWidget_size_request;
486#endif //GTK_VERSION2
487
488 widget_class->realize = cvImageWidget_realize;
489 widget_class->size_allocate = cvImageWidget_size_allocate;
490 widget_class->button_press_event = NULL;
491 widget_class->button_release_event = NULL;
492 widget_class->motion_notify_event = NULL;
493}
494
495static void
496cvImageWidget_init(GTypeInstance* instance, gpointer /*g_class*/)
497{
498 CvImageWidget* image_widget = (CvImageWidget*)instance;
499 image_widget->original_image=0;
500 image_widget->scaled_image=0;
501 image_widget->flags=0;
502}
503
504GType cvImageWidget_get_type (void){
505 static GType image_type = 0;
506
507 if (!image_type)
508 {
509 image_type = g_type_register_static_simple(
510 GTK_TYPE_WIDGET,
511 type_name: (gchar*) "CvImageWidget",
512 class_size: sizeof(CvImageWidgetClass),
513 class_init: cvImageWidget_class_init,
514 instance_size: sizeof(CvImageWidget),
515 instance_init: cvImageWidget_init,
516 flags: (GTypeFlags)0
517 );
518 }
519
520 return image_type;
521}
522/////////////////////////////////////////////////////////////////////////////
523// End CvImageWidget
524/////////////////////////////////////////////////////////////////////////////
525
526
527struct CvWindow;
528
529struct CvUIBase {
530 CvUIBase(int signature_) : signature(signature_) { }
531
532 int signature;
533};
534
535struct CvTrackbar : CvUIBase
536{
537 CvTrackbar(const std::string& trackbar_name) :
538 CvUIBase(CV_TRACKBAR_MAGIC_VAL),
539 widget(NULL), name(trackbar_name),
540 parent(NULL), data(NULL),
541 pos(0), maxval(0), minval(0),
542 notify(NULL), notify2(NULL), // deprecated
543 onChangeCallback(NULL), userdata(NULL)
544 {
545 // nothing
546 }
547 ~CvTrackbar()
548 {
549 // destroyed by parent window
550 }
551
552 GtkWidget* widget;
553 std::string name;
554 CvWindow* parent; // TODO weak_ptr
555 int* data;
556 int pos;
557 int maxval;
558 int minval;
559 CvTrackbarCallback notify; // deprecated
560 CvTrackbarCallback2 notify2; // deprecated
561 TrackbarCallback onChangeCallback;
562 void* userdata;
563};
564
565
566struct CvWindow : CvUIBase
567{
568 CvWindow(const std::string& window_name) :
569 CvUIBase(CV_WINDOW_MAGIC_VAL),
570 widget(NULL), frame(NULL), paned(NULL), name(window_name),
571 last_key(0), flags(0), status(0),
572 on_mouse(NULL), on_mouse_param(NULL)
573#ifdef HAVE_OPENGL
574 ,useGl(false), glDrawCallback(NULL), glDrawData(NULL), glArea(NULL)
575#endif
576 {
577 CV_LOG_INFO(NULL, "OpenCV/UI: creating GTK window: " << window_name);
578 }
579 ~CvWindow();
580 void destroy();
581
582 GtkWidget* widget;
583 GtkWidget* frame;
584 GtkWidget* paned;
585 std::string name;
586
587 int last_key;
588 int flags;
589 int status;//0 normal, 1 fullscreen (YV)
590
591 CvMouseCallback on_mouse;
592 void* on_mouse_param;
593
594 std::vector< std::shared_ptr<CvTrackbar> > trackbars;
595
596#ifdef HAVE_OPENGL
597 bool useGl;
598
599 CvOpenGlDrawCallback glDrawCallback;
600 void* glDrawData;
601 GtkWidget* glArea;
602#endif
603};
604
605
606static gboolean icvOnClose( GtkWidget* widget, GdkEvent* event, gpointer user_data );
607static gboolean icvOnKeyPress( GtkWidget* widget, GdkEventKey* event, gpointer user_data );
608static void icvOnTrackbar( GtkWidget* widget, gpointer user_data );
609static gboolean icvOnMouse( GtkWidget *widget, GdkEvent *event, gpointer user_data );
610
611int thread_started=0;
612static gpointer icvWindowThreadLoop(gpointer data);
613GMutex* last_key_mutex = NULL;
614GCond* cond_have_key = NULL;
615GThread* window_thread = NULL;
616
617static int last_key = -1;
618
619static
620std::vector< std::shared_ptr<CvWindow> >& getGTKWindows()
621{
622 static std::vector< std::shared_ptr<CvWindow> > g_windows;
623 return g_windows;
624}
625
626CV_IMPL int cvInitSystem( int argc, char** argv )
627{
628 static int wasInitialized = 0;
629 static bool hasError = false;
630
631 // check initialization status
632 if( !wasInitialized )
633 {
634 if (!gtk_init_check(argc: &argc, argv: &argv))
635 {
636 hasError = true;
637 wasInitialized = true;
638 CV_Error(Error::StsError, "Can't initialize GTK backend");
639 }
640
641 setlocale(LC_NUMERIC,locale: "C");
642
643 #if defined(HAVE_OPENGL) && not defined(GTK_VERSION3) // GTK3+ uses GtkGLArea so no need to check for GtkGLExt
644 if (!gtk_gl_init_check(&argc, &argv))
645 {
646 hasError = true;
647 wasInitialized = true;
648 CV_Error(Error::StsError, "Can't initialize GTK-OpenGL backend");
649 }
650 #endif
651
652 wasInitialized = 1;
653 }
654 if (hasError)
655 CV_Error(Error::StsError, "GTK backend is not available");
656
657 return 0;
658}
659
660CV_IMPL int cvStartWindowThread(){
661 cvInitSystem(argc: 0,NULL);
662 if (!thread_started)
663 {
664 (void)getWindowMutex(); // force mutex initialization
665
666 // protects the 'last key pressed' variable
667 last_key_mutex = g_mutex_new();
668
669 // conditional that indicates a key has been pressed
670 cond_have_key = g_cond_new();
671
672 window_thread = g_thread_new(name: "OpenCV window update", func: icvWindowThreadLoop, NULL);
673 }
674 thread_started = window_thread!=NULL;
675 return thread_started;
676}
677
678gpointer icvWindowThreadLoop(gpointer /*data*/)
679{
680 while(1){
681 {
682 cv::AutoLock lock(getWindowMutex());
683 gtk_main_iteration_do(FALSE);
684 }
685
686 // little sleep
687 g_usleep(microseconds: 500);
688
689 g_thread_yield();
690 }
691 return NULL;
692}
693
694#define CV_LOCK_MUTEX() cv::AutoLock lock(getWindowMutex())
695
696static
697std::shared_ptr<CvWindow> icvFindWindowByName(const std::string& name)
698{
699 auto& g_windows = getGTKWindows();
700 for(size_t i = 0; i < g_windows.size(); ++i)
701 {
702 auto window = g_windows[i];
703 if (!window)
704 continue;
705 if (window->name == name)
706 return window;
707 }
708 return std::shared_ptr<CvWindow>();
709}
710
711static inline
712std::shared_ptr<CvWindow> icvFindWindowByName(const char* name)
713{
714 CV_Assert(name);
715 return icvFindWindowByName(name: std::string(name));
716}
717
718
719static CvWindow* icvWindowByWidget( GtkWidget* widget )
720{
721 auto& g_windows = getGTKWindows();
722 for (size_t i = 0; i < g_windows.size(); ++i)
723 {
724 CvWindow* window = g_windows[i].get();
725 if (window->widget == widget || window->frame == widget || window->paned == widget)
726 return window;
727 }
728 return NULL;
729}
730
731static Rect getImageRect_(const std::shared_ptr<CvWindow>& window);
732
733CvRect cvGetWindowRect_GTK(const char* name)
734{
735 CV_Assert(name && "NULL name string");
736
737 CV_LOCK_MUTEX();
738 const auto window = icvFindWindowByName(name);
739 if (!window)
740 CV_Error( cv::Error::StsNullPtr, "NULL window" );
741
742 return cvRect(rc: getImageRect_(window));
743}
744
745#if defined(GTK_VERSION2)
746 #define gtk_widget_get_allocated_width(widget) (widget->allocation.width)
747 #define gtk_widget_get_allocated_height(widget) (widget->allocation.height)
748#endif
749
750static Rect getImageRect_(const std::shared_ptr<CvWindow>& window)
751{
752 CV_Assert(window);
753
754 gint wx, wy;
755#ifdef HAVE_OPENGL
756 if (window->useGl) {
757 gtk_widget_translate_coordinates(window->widget, gtk_widget_get_toplevel(window->widget), 0, 0, &wx, &wy);
758 return Rect(wx, wy, gtk_widget_get_allocated_width(window->widget), gtk_widget_get_allocated_height(window->widget));
759 }
760#endif
761
762 CvImageWidget * image_widget = CV_IMAGE_WIDGET( window->widget );
763 gtk_widget_translate_coordinates(src_widget: &image_widget->widget, dest_widget: gtk_widget_get_toplevel(widget: &image_widget->widget), src_x: 0, src_y: 0, dest_x: &wx, dest_y: &wy);
764 if (image_widget->scaled_image) {
765 return Rect(wx, wy, MIN(image_widget->scaled_image->cols, gtk_widget_get_allocated_width(window->widget)),
766 MIN(image_widget->scaled_image->rows, gtk_widget_get_allocated_height(window->widget)));
767 } else if (image_widget->original_image) {
768 return Rect(wx, wy, MIN(image_widget->original_image->cols, gtk_widget_get_allocated_width(window->widget)),
769 MIN(image_widget->original_image->rows, gtk_widget_get_allocated_height(window->widget)));
770 }
771
772 return Rect(-1, -1, -1, -1);
773}
774
775double cvGetModeWindow_GTK(const char* name)//YV
776{
777 CV_Assert(name && "NULL name string");
778
779 CV_LOCK_MUTEX();
780 const auto window = icvFindWindowByName(name);
781 if (!window)
782 CV_Error( cv::Error::StsNullPtr, "NULL window" );
783
784 double result = window->status;
785 return result;
786}
787
788static bool setModeWindow_(const std::shared_ptr<CvWindow>& window, int mode);
789void cvSetModeWindow_GTK( const char* name, double prop_value)//Yannick Verdie
790{
791 CV_Assert(name && "NULL name string");
792
793 CV_LOCK_MUTEX();
794
795 const auto window = icvFindWindowByName(name);
796 if (!window)
797 CV_Error( cv::Error::StsNullPtr, "NULL window" );
798
799 setModeWindow_(window, mode: (int)prop_value);
800}
801
802static bool setModeWindow_(const std::shared_ptr<CvWindow>& window, int mode)
803{
804 if (window->flags & cv::WINDOW_AUTOSIZE) //if the flag cv::WINDOW_AUTOSIZE is set
805 return false;
806
807 //so easy to do fullscreen here, Linux rocks !
808
809 if (window->status == mode)
810 return true;
811
812 if (window->status==cv::WINDOW_FULLSCREEN && mode==cv::WINDOW_NORMAL)
813 {
814 gtk_window_unfullscreen(GTK_WINDOW(window->frame));
815 window->status=cv::WINDOW_NORMAL;
816 return true;
817 }
818
819 if (window->status==cv::WINDOW_NORMAL && mode==cv::WINDOW_FULLSCREEN)
820 {
821 gtk_window_fullscreen(GTK_WINDOW(window->frame));
822 window->status=cv::WINDOW_FULLSCREEN;
823 return true;
824 }
825
826 return false;
827}
828
829void setWindowTitle_GTK(const String& winname, const String& title)
830{
831 CV_LOCK_MUTEX();
832
833 auto window = icvFindWindowByName(name: winname.c_str());
834
835 if (!window)
836 {
837 namedWindow(winname);
838 window = icvFindWindowByName(name: winname.c_str());
839 CV_Assert(window);
840 }
841
842 gtk_window_set_title(GTK_WINDOW(window->frame), title: title.c_str());
843}
844
845double cvGetPropWindowAutoSize_GTK(const char* name)
846{
847 CV_Assert(name && "NULL name string");
848
849 CV_LOCK_MUTEX();
850
851 const auto window = icvFindWindowByName(name);
852 if (!window)
853 return -1; // keep silence here
854
855 double result = window->flags & cv::WINDOW_AUTOSIZE;
856 return result;
857}
858
859static double getRatioWindow_(const std::shared_ptr<CvWindow>& window);
860double cvGetRatioWindow_GTK(const char* name)
861{
862 CV_Assert(name && "NULL name string");
863
864 CV_LOCK_MUTEX();
865
866 const auto window = icvFindWindowByName(name);
867 if (!window)
868 return -1; // keep silence here
869
870 return getRatioWindow_(window);
871}
872
873static double getRatioWindow_(const std::shared_ptr<CvWindow>& window)
874{
875 double result = static_cast<double>(
876 gtk_widget_get_allocated_width(widget: window->widget)) / gtk_widget_get_allocated_height(widget: window->widget);
877 return result;
878}
879
880double cvGetOpenGlProp_GTK(const char* name)
881{
882#ifdef HAVE_OPENGL
883 CV_Assert(name && "NULL name string");
884
885 CV_LOCK_MUTEX();
886
887 const auto window = icvFindWindowByName(name);
888 if (!window)
889 return -1; // keep silence here
890
891 double result = window->useGl;
892 return result;
893#else
894 (void)name;
895 return -1;
896#endif
897}
898
899
900// OpenGL support
901
902#ifdef HAVE_OPENGL
903namespace
904{
905
906#ifdef GTK_VERSION3
907
908 void glRealizeCallback(GtkGLArea* area, gpointer user_data) {
909 CV_UNUSED(user_data);
910 gtk_gl_area_make_current(area);
911 if (gtk_gl_area_get_error(area) != NULL)
912 CV_Error(cv::Error::OpenGlApiCallError, "OpenGL context is not initialized");
913 }
914
915 gboolean glRenderCallback(GtkGLArea* area, GdkGLContext* context, gpointer user_data) {
916 CV_UNUSED(context);
917 CvWindow* window = (CvWindow*)user_data;
918 gtk_gl_area_make_current(area);
919 if (gtk_gl_area_get_error(area) != NULL) {
920 CV_Error(cv::Error::OpenGlApiCallError, "OpenGL context is not initialized");
921 return FALSE;
922 }
923 if(window->glDrawCallback) {
924 window->glDrawCallback(window->glDrawData);
925 }
926// gtk_gl_area_queue_render(area);
927 return TRUE;
928 }
929
930#endif
931
932 void createGlContext(CvWindow* window)
933 {
934 #ifdef GTK_VERSION3
935 g_signal_connect(window->glArea, "realize", G_CALLBACK(glRealizeCallback), window);
936 g_signal_connect(window->glArea, "render", G_CALLBACK(glRenderCallback), window);
937 #else
938
939 GdkGLConfig* glconfig;
940
941 // Try double-buffered visual
942 glconfig = gdk_gl_config_new_by_mode((GdkGLConfigMode)(GDK_GL_MODE_RGB | GDK_GL_MODE_DEPTH | GDK_GL_MODE_DOUBLE));
943 if (!glconfig)
944 CV_Error( cv::Error::OpenGlApiCallError, "Can't Create A GL Device Context" );
945
946 // Set OpenGL-capability to the widget
947 if (!gtk_widget_set_gl_capability(window->widget, glconfig, NULL, TRUE, GDK_GL_RGBA_TYPE))
948 CV_Error( cv::Error::OpenGlApiCallError, "Can't Create A GL Device Context" );
949
950 #endif
951
952 window->useGl = true;
953 }
954
955 void drawGl(CvWindow* window)
956 {
957 #ifdef GTK_VERSION3
958
959 GtkGLArea* gtkGlArea = GTK_GL_AREA(window->glArea);
960 if (gtk_gl_area_get_error(gtkGlArea) != NULL)
961 CV_Error(cv::Error::OpenGlApiCallError, "Can't Activate The GL Rendering Context");
962
963 if (window->glDrawCallback)
964 window->glDrawCallback(window->glDrawData);
965
966 #else
967
968 GdkGLContext* glcontext = gtk_widget_get_gl_context(window->widget);
969 GdkGLDrawable* gldrawable = gtk_widget_get_gl_drawable(window->widget);
970
971 if (!gdk_gl_drawable_gl_begin (gldrawable, glcontext))
972 CV_Error( cv::Error::OpenGlApiCallError, "Can't Activate The GL Rendering Context" );
973
974 glViewport(0, 0, gtk_widget_get_allocated_width(window->widget), gtk_widget_get_allocated_height(window->widget));
975
976 glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
977
978 if (window->glDrawCallback)
979 window->glDrawCallback(window->glDrawData);
980
981 if (gdk_gl_drawable_is_double_buffered (gldrawable))
982 gdk_gl_drawable_swap_buffers(gldrawable);
983 else
984 glFlush();
985
986 gdk_gl_drawable_gl_end(gldrawable);
987
988 #endif
989 }
990}
991
992#endif // HAVE_OPENGL
993
994#if defined (GTK_VERSION2)
995static gboolean cvImageWidget_expose(GtkWidget* widget, GdkEventExpose* event, gpointer data)
996#elif defined (GTK_VERSION3)
997static gboolean cvImageWidget_draw(GtkWidget* widget, cairo_t *cr, gpointer data)
998#endif
999{
1000#ifdef HAVE_OPENGL
1001 CvWindow* window = (CvWindow*)data;
1002
1003 if (window->useGl)
1004 {
1005 drawGl(window);
1006 return TRUE;
1007 }
1008#else
1009 (void)data;
1010#endif
1011#if defined (GTK_VERSION2)
1012 (void)event;
1013#endif
1014
1015 CvImageWidget *image_widget = NULL;
1016 GdkPixbuf *pixbuf = NULL;
1017
1018 g_return_val_if_fail (widget != NULL, FALSE);
1019 g_return_val_if_fail (CV_IS_IMAGE_WIDGET (widget), FALSE);
1020
1021 image_widget = CV_IMAGE_WIDGET (widget);
1022#if defined (GTK_VERSION2)
1023 cairo_t *cr = gdk_cairo_create(widget->window);
1024#endif
1025
1026 if( image_widget->scaled_image ){
1027 // center image in available region
1028 int x0 = (gtk_widget_get_allocated_width(widget) - image_widget->scaled_image->cols)/2;
1029 int y0 = (gtk_widget_get_allocated_height(widget) - image_widget->scaled_image->rows)/2;
1030
1031 pixbuf = gdk_pixbuf_new_from_data(data: image_widget->scaled_image->data.ptr, colorspace: GDK_COLORSPACE_RGB, has_alpha: false,
1032 bits_per_sample: 8, MIN(image_widget->scaled_image->cols, gtk_widget_get_allocated_width(widget)),
1033 MIN(image_widget->scaled_image->rows, gtk_widget_get_allocated_height(widget)),
1034 rowstride: image_widget->scaled_image->step, NULL, NULL);
1035
1036 gdk_cairo_set_source_pixbuf(cr, pixbuf, pixbuf_x: x0, pixbuf_y: y0);
1037 }
1038 else if( image_widget->original_image ){
1039 pixbuf = gdk_pixbuf_new_from_data(data: image_widget->original_image->data.ptr, colorspace: GDK_COLORSPACE_RGB, has_alpha: false,
1040 bits_per_sample: 8, MIN(image_widget->original_image->cols, gtk_widget_get_allocated_width(widget)),
1041 MIN(image_widget->original_image->rows, gtk_widget_get_allocated_height(widget)),
1042 rowstride: image_widget->original_image->step, NULL, NULL);
1043 gdk_cairo_set_source_pixbuf(cr, pixbuf, pixbuf_x: 0, pixbuf_y: 0);
1044 }
1045
1046 cairo_paint(cr);
1047 if(pixbuf)
1048 g_object_unref(object: pixbuf);
1049#if defined (GTK_VERSION2)
1050 cairo_destroy(cr);
1051#endif
1052 return TRUE;
1053}
1054
1055static std::shared_ptr<CvWindow> namedWindow_(const std::string& name, int flags);
1056CV_IMPL int cvNamedWindow( const char* name, int flags )
1057{
1058 cvInitSystem(argc: name ? 1 : 0,argv: (char**)&name);
1059 CV_Assert(name && "NULL name string");
1060
1061 CV_LOCK_MUTEX();
1062
1063 // Check the name in the storage
1064 if (icvFindWindowByName(name))
1065 {
1066 return 1;
1067 }
1068 auto window = namedWindow_(name, flags);
1069 return window ? 1 : 0;
1070}
1071
1072static std::shared_ptr<CvWindow> namedWindow_(const std::string& name, int flags)
1073{
1074 cvInitSystem(argc: 0, NULL);
1075
1076 auto window_ptr = std::make_shared<CvWindow>(args: name);
1077 CvWindow* window = window_ptr.get();
1078 window->flags = flags;
1079 window->status = cv::WINDOW_NORMAL;//YV
1080
1081 window->frame = gtk_window_new( type: GTK_WINDOW_TOPLEVEL );
1082
1083 window->widget = cvImageWidgetNew( flags );
1084
1085#if defined(HAVE_OPENGL) && defined(GTK_VERSION3)
1086 if (flags & cv::WINDOW_OPENGL) {
1087 window->glArea = gtk_gl_area_new();
1088 gtk_container_add(GTK_CONTAINER(window->frame), window->glArea);
1089 gtk_widget_show(window->glArea);
1090 } else {
1091 window->paned = gtk_vbox_new( FALSE, 0 );
1092 gtk_box_pack_end( GTK_BOX(window->paned), window->widget, TRUE, TRUE, 0 );
1093 gtk_widget_show( window->widget );
1094 gtk_container_add( GTK_CONTAINER(window->frame), window->paned );
1095 gtk_widget_show( window->paned );
1096 }
1097#else
1098 window->paned = gtk_vbox_new( FALSE, spacing: 0 );
1099 gtk_box_pack_end( GTK_BOX(window->paned), child: window->widget, TRUE, TRUE, padding: 0 );
1100 gtk_widget_show( widget: window->widget );
1101 gtk_container_add( GTK_CONTAINER(window->frame), widget: window->paned );
1102 gtk_widget_show( widget: window->paned );
1103#endif
1104
1105#ifndef HAVE_OPENGL
1106 if (flags & cv::WINDOW_OPENGL)
1107 CV_Error( cv::Error::OpenGlNotSupported, "Library was built without OpenGL support" );
1108#else
1109 if (flags & cv::WINDOW_OPENGL)
1110 createGlContext(window);
1111
1112 window->glDrawCallback = 0;
1113 window->glDrawData = 0;
1114#endif
1115
1116 //
1117 // configure event handlers
1118 // TODO -- move this to CvImageWidget ?
1119 g_signal_connect( window->frame, "key-press-event",
1120 G_CALLBACK(icvOnKeyPress), window );
1121 g_signal_connect( window->widget, "button-press-event",
1122 G_CALLBACK(icvOnMouse), window );
1123 g_signal_connect( window->widget, "button-release-event",
1124 G_CALLBACK(icvOnMouse), window );
1125 g_signal_connect( window->widget, "motion-notify-event",
1126 G_CALLBACK(icvOnMouse), window );
1127 g_signal_connect( window->widget, "scroll-event",
1128 G_CALLBACK(icvOnMouse), window );
1129 g_signal_connect( window->frame, "delete-event",
1130 G_CALLBACK(icvOnClose), window );
1131#if defined(GTK_VERSION3)
1132 g_signal_connect( window->widget, "draw",
1133 G_CALLBACK(cvImageWidget_draw), window );
1134#else
1135 g_signal_connect( window->widget, "expose-event",
1136 G_CALLBACK(cvImageWidget_expose), window );
1137#endif //GTK_VERSION3
1138
1139
1140#if defined(GTK_VERSION3_4)
1141 gtk_widget_add_events (widget: window->widget, events: GDK_BUTTON_RELEASE_MASK | GDK_BUTTON_PRESS_MASK | GDK_POINTER_MOTION_MASK | GDK_SCROLL_MASK | GDK_SMOOTH_SCROLL_MASK) ;
1142#else
1143 gtk_widget_add_events (window->widget, GDK_BUTTON_RELEASE_MASK | GDK_BUTTON_PRESS_MASK | GDK_POINTER_MOTION_MASK | GDK_SCROLL_MASK) ;
1144#endif //GTK_VERSION3_4
1145
1146 gtk_widget_show( widget: window->frame );
1147 gtk_window_set_title(GTK_WINDOW(window->frame), title: name.c_str());
1148
1149 {
1150 AutoLock lock(getWindowMutex());
1151 getGTKWindows().push_back(x: window_ptr);
1152 }
1153
1154 bool b_nautosize = ((flags & cv::WINDOW_AUTOSIZE) == 0);
1155 gtk_window_set_resizable( GTK_WINDOW(window->frame), resizable: b_nautosize );
1156
1157 // allow window to be resized
1158 if( b_nautosize ){
1159 GdkGeometry geometry;
1160 geometry.min_width = 50;
1161 geometry.min_height = 50;
1162 gtk_window_set_geometry_hints( GTK_WINDOW( window->frame ), GTK_WIDGET( window->widget ),
1163 geometry: &geometry, geom_mask: (GdkWindowHints) (GDK_HINT_MIN_SIZE));
1164 }
1165
1166#ifdef HAVE_OPENGL
1167 if (window->useGl)
1168 cvSetOpenGlContext(name.c_str());
1169#endif
1170
1171 return window_ptr;
1172}
1173
1174
1175#ifdef HAVE_OPENGL
1176
1177CV_IMPL void cvSetOpenGlContext(const char* name)
1178{
1179 CV_Assert(name && "NULL name string");
1180
1181 CV_LOCK_MUTEX();
1182
1183 auto window = icvFindWindowByName(name);
1184 if (!window)
1185 CV_Error( cv::Error::StsNullPtr, "NULL window" );
1186
1187 if (!window->useGl)
1188 CV_Error( cv::Error::OpenGlNotSupported, "Window doesn't support OpenGL" );
1189
1190#ifdef GTK_VERSION3
1191
1192 if(gtk_gl_area_get_error(GTK_GL_AREA(window->glArea)) != NULL)
1193 CV_Error( cv::Error::OpenGlApiCallError, "Can't Activate The GL Rendering Context");
1194
1195#else
1196
1197 GdkGLContext* glcontext;
1198 GdkGLDrawable* gldrawable;
1199
1200 glcontext = gtk_widget_get_gl_context(window->widget);
1201 gldrawable = gtk_widget_get_gl_drawable(window->widget);
1202
1203 if (!gdk_gl_drawable_make_current(gldrawable, glcontext))
1204 CV_Error( cv::Error::OpenGlApiCallError, "Can't Activate The GL Rendering Context" );
1205
1206#endif
1207
1208}
1209
1210CV_IMPL void cvUpdateWindow(const char* name)
1211{
1212 CV_Assert(name && "NULL name string");
1213
1214 CV_LOCK_MUTEX();
1215
1216 auto window = icvFindWindowByName(name);
1217 if (!window)
1218 return;
1219
1220 // window does not refresh without this
1221#ifdef GTK_VERSION3
1222
1223 if ( GTK_IS_GL_AREA(window->glArea) ){
1224 gtk_gl_area_queue_render(GTK_GL_AREA(window->glArea));
1225 } else {
1226 gtk_widget_queue_draw( GTK_WIDGET(window->widget));
1227 }
1228
1229#else
1230
1231 gtk_widget_queue_draw( GTK_WIDGET(window->widget) );
1232
1233#endif
1234
1235}
1236
1237CV_IMPL void cvSetOpenGlDrawCallback(const char* name, CvOpenGlDrawCallback callback, void* userdata)
1238{
1239 CV_Assert(name && "NULL name string");
1240
1241 CV_LOCK_MUTEX();
1242
1243 auto window = icvFindWindowByName(name);
1244 if( !window )
1245 return;
1246
1247 if (!window->useGl)
1248 CV_Error( cv::Error::OpenGlNotSupported, "Window was created without OpenGL context" );
1249
1250 window->glDrawCallback = callback;
1251 window->glDrawData = userdata;
1252}
1253
1254#endif // HAVE_OPENGL
1255
1256
1257
1258CvWindow::~CvWindow()
1259{
1260 if (frame)
1261 destroy();
1262}
1263
1264inline void CvWindow::destroy()
1265{
1266 CV_LOG_INFO(NULL, "OpenCV/UI: destroying GTK window: " << name);
1267 gtk_widget_destroy(widget: frame);
1268 frame = nullptr;
1269}
1270
1271static void checkLastWindow()
1272{
1273 // if last window...
1274 if (getGTKWindows().empty())
1275 {
1276 if( thread_started )
1277 {
1278 // send key press signal to jump out of any waiting cvWaitKey's
1279 g_cond_broadcast( cond: cond_have_key );
1280 }
1281 else
1282 {
1283 // Some GTK+ modules (like the Unity module) use GDBusConnection,
1284 // which has a habit of postponing cleanup by performing it via
1285 // idle sources added to the main loop. Since this was the last window,
1286 // we can assume that no event processing is going to happen in the
1287 // nearest future, so we should force that cleanup (by handling all pending
1288 // events) while we still have the chance.
1289 // This is not needed if thread_started is true, because the background
1290 // thread will process events continuously.
1291 while( gtk_events_pending() )
1292 gtk_main_iteration();
1293 }
1294 }
1295}
1296
1297static
1298void icvDeleteWindow_( CvWindow* window )
1299{
1300 AutoLock lock(getWindowMutex());
1301 auto& g_windows = getGTKWindows();
1302 bool found = false;
1303 for (auto i = g_windows.begin(); i != g_windows.end(); ++i)
1304 {
1305 if (i->get() == window)
1306 {
1307 g_windows.erase(position: i);
1308 found = true;
1309 break;
1310 }
1311 }
1312 CV_LOG_IF_WARNING(NULL, !found, "OpenCV/GTK: Can't destroy non-registered window");
1313 checkLastWindow();
1314}
1315
1316CV_IMPL void cvDestroyWindow( const char* name )
1317{
1318 CV_Assert(name && "NULL name string");
1319
1320 CV_LOCK_MUTEX();
1321 auto& g_windows = getGTKWindows();
1322
1323 bool found = false;
1324 for (auto i = g_windows.begin(); i != g_windows.end(); ++i)
1325 {
1326 if (i->get()->name == name)
1327 {
1328 g_windows.erase(position: i);
1329 found = true;
1330 break;
1331 }
1332 }
1333 CV_LOG_IF_ERROR(NULL, !found, "OpenCV/GTK: Can't destroy non-registered window: '" << name << "'");
1334
1335 checkLastWindow();
1336}
1337
1338
1339CV_IMPL void
1340cvDestroyAllWindows( void )
1341{
1342 CV_LOCK_MUTEX();
1343
1344 getGTKWindows().clear();
1345 checkLastWindow();
1346}
1347
1348// CvSize icvCalcOptimalWindowSize( CvWindow * window, CvSize new_image_size){
1349// CvSize window_size;
1350// GtkWidget * toplevel = gtk_widget_get_toplevel( window->frame );
1351// gdk_drawable_get_size( GDK_DRAWABLE(toplevel->window),
1352// &window_size.width, &window_size.height );
1353
1354// window_size.width = window_size.width + new_image_size.width - window->widget->allocation.width;
1355// window_size.height = window_size.height + new_image_size.height - window->widget->allocation.height;
1356
1357// return window_size;
1358// }
1359
1360CV_IMPL void
1361cvShowImage( const char* name, const CvArr* arr )
1362{
1363 CV_Assert(name && "NULL name string");
1364
1365 CV_LOCK_MUTEX();
1366
1367 auto window = icvFindWindowByName(name);
1368 if(!window)
1369 {
1370 cvNamedWindow(name, flags: 1);
1371 window = icvFindWindowByName(name);
1372 }
1373 CV_Assert(window);
1374
1375 if (arr)
1376 {
1377 #ifdef HAVE_OPENGL
1378 if (window->useGl)
1379 {
1380 cv::imshow(name, cv::cvarrToMat(arr));
1381 return;
1382 }
1383 #endif
1384
1385 CvImageWidget * image_widget = CV_IMAGE_WIDGET( window->widget );
1386 cvImageWidgetSetImage( widget: image_widget, arr );
1387 }
1388}
1389
1390static void resizeWindow_(const std::shared_ptr<CvWindow>& window, int width, int height);
1391CV_IMPL void cvResizeWindow(const char* name, int width, int height )
1392{
1393 CV_Assert(name && "NULL name string");
1394
1395 CV_LOCK_MUTEX();
1396
1397 auto window = icvFindWindowByName(name);
1398 if(!window)
1399 return;
1400
1401 return resizeWindow_(window, width, height);
1402}
1403
1404static
1405void resizeWindow_(const std::shared_ptr<CvWindow>& window, int width, int height)
1406{
1407 CV_Assert(window);
1408 CvImageWidget* image_widget = CV_IMAGE_WIDGET( window->widget );
1409 //if(image_widget->flags & cv::WINDOW_AUTOSIZE)
1410 //EXIT;
1411
1412 gtk_window_set_resizable( GTK_WINDOW(window->frame), resizable: 1 );
1413 gtk_window_resize( GTK_WINDOW(window->frame), width, height );
1414
1415 // disable initial resize since presumably user wants to keep
1416 // this window size
1417 image_widget->flags &= ~CV_WINDOW_NO_IMAGE;
1418}
1419
1420
1421CV_IMPL void cvMoveWindow( const char* name, int x, int y )
1422{
1423 CV_Assert(name && "NULL name string");
1424
1425 CV_LOCK_MUTEX();
1426
1427 const auto window = icvFindWindowByName(name);
1428 if(!window)
1429 return;
1430
1431 gtk_window_move( GTK_WINDOW(window->frame), x, y );
1432}
1433
1434static
1435std::shared_ptr<CvTrackbar> icvFindTrackbarByName(const std::shared_ptr<CvWindow>& window, const std::string& name)
1436{
1437 CV_Assert(window);
1438 auto& trackbars = window->trackbars;
1439 for(size_t i = 0; i < trackbars.size(); ++i)
1440 {
1441 auto trackbar = trackbars[i];
1442 if (!trackbar)
1443 continue;
1444 if (trackbar->name == name)
1445 return trackbar;
1446 }
1447 return std::shared_ptr<CvTrackbar>();
1448}
1449
1450
1451static int
1452icvCreateTrackbar( const char* trackbar_name, const char* window_name,
1453 int* val, int count, CvTrackbarCallback on_notify,
1454 CvTrackbarCallback2 on_notify2, void* userdata )
1455{
1456 CV_Assert(window_name && "NULL window name");
1457 CV_Assert(trackbar_name && "NULL trackbar name");
1458
1459 if( count <= 0 )
1460 CV_Error( cv::Error::StsOutOfRange, "Bad trackbar maximal value" );
1461
1462 CV_LOCK_MUTEX();
1463
1464 const auto window = icvFindWindowByName(name: window_name);
1465 if(!window)
1466 return 0;
1467
1468 auto trackbar_ = icvFindTrackbarByName(window, name: trackbar_name);
1469 if (!trackbar_)
1470 {
1471 trackbar_ = std::make_shared<CvTrackbar>(args&: trackbar_name);
1472 CvTrackbar* trackbar = trackbar_.get();
1473 trackbar->parent = window.get();
1474 window->trackbars.push_back(x: trackbar_);
1475
1476 GtkWidget* hscale_box = gtk_hbox_new( FALSE, spacing: 10 );
1477 GtkWidget* hscale_label = gtk_label_new( str: trackbar_name );
1478 GtkWidget* hscale = gtk_hscale_new_with_range( min: 0, max: count, step: 1 );
1479 gtk_scale_set_digits( GTK_SCALE(hscale), digits: 0 );
1480 //gtk_scale_set_value_pos( hscale, GTK_POS_TOP );
1481 gtk_scale_set_draw_value( GTK_SCALE(hscale), TRUE );
1482
1483 trackbar->widget = hscale;
1484 gtk_box_pack_start( GTK_BOX(hscale_box), child: hscale_label, FALSE, FALSE, padding: 5 );
1485 gtk_widget_show( widget: hscale_label );
1486 gtk_box_pack_start( GTK_BOX(hscale_box), child: hscale, TRUE, TRUE, padding: 5 );
1487 gtk_widget_show( widget: hscale );
1488 gtk_box_pack_start( GTK_BOX(window->paned), child: hscale_box, FALSE, FALSE, padding: 5 );
1489 gtk_widget_show( widget: hscale_box );
1490 }
1491
1492 CvTrackbar* trackbar = trackbar_.get(); CV_DbgAssert(trackbar);
1493
1494 if( val )
1495 {
1496 int value = *val;
1497 if( value < 0 )
1498 value = 0;
1499 if( value > count )
1500 value = count;
1501 gtk_range_set_value( GTK_RANGE(trackbar->widget), value );
1502 trackbar->pos = value;
1503 trackbar->data = val;
1504 }
1505
1506 trackbar->maxval = count;
1507 trackbar->notify = on_notify;
1508 trackbar->notify2 = on_notify2;
1509 trackbar->userdata = userdata;
1510 g_signal_connect( trackbar->widget, "value-changed",
1511 G_CALLBACK(icvOnTrackbar), trackbar );
1512
1513 // queue a widget resize to trigger a window resize to
1514 // compensate for the addition of trackbars
1515 gtk_widget_queue_resize( GTK_WIDGET(window->widget) );
1516
1517 return 1;
1518}
1519
1520CV_IMPL int
1521cvCreateTrackbar( const char* trackbar_name, const char* window_name,
1522 int* val, int count, CvTrackbarCallback on_notify )
1523{
1524 return icvCreateTrackbar(trackbar_name, window_name, val, count,
1525 on_notify, on_notify2: 0, userdata: 0);
1526}
1527
1528CV_IMPL int
1529cvCreateTrackbar2( const char* trackbar_name, const char* window_name,
1530 int* val, int count, CvTrackbarCallback2 on_notify2,
1531 void* userdata )
1532{
1533 return icvCreateTrackbar(trackbar_name, window_name, val, count,
1534 on_notify: 0, on_notify2, userdata);
1535}
1536
1537static
1538std::shared_ptr<CvTrackbar> createTrackbar_(
1539 const std::shared_ptr<CvWindow>& window, const std::string& name,
1540 int count,
1541 TrackbarCallback onChange, void* userdata
1542)
1543{
1544 CV_Assert(window);
1545 CV_Assert(!name.empty());
1546
1547 if (count <= 0)
1548 CV_Error(Error::StsOutOfRange, "Bad trackbar maximal value");
1549
1550 auto trackbar_ = std::make_shared<CvTrackbar>(args: name);
1551 CvTrackbar* trackbar = trackbar_.get();
1552 trackbar->parent = window.get();
1553 window->trackbars.push_back(x: trackbar_);
1554
1555 GtkWidget* hscale_box = gtk_hbox_new( FALSE, spacing: 10 );
1556 GtkWidget* hscale_label = gtk_label_new(str: name.c_str());
1557 GtkWidget* hscale = gtk_hscale_new_with_range( min: 0, max: count, step: 1 );
1558 gtk_scale_set_digits( GTK_SCALE(hscale), digits: 0 );
1559 //gtk_scale_set_value_pos( hscale, GTK_POS_TOP );
1560 gtk_scale_set_draw_value( GTK_SCALE(hscale), TRUE );
1561
1562 trackbar->widget = hscale;
1563 gtk_box_pack_start( GTK_BOX(hscale_box), child: hscale_label, FALSE, FALSE, padding: 5 );
1564 gtk_widget_show( widget: hscale_label );
1565 gtk_box_pack_start( GTK_BOX(hscale_box), child: hscale, TRUE, TRUE, padding: 5 );
1566 gtk_widget_show( widget: hscale );
1567 gtk_box_pack_start( GTK_BOX(window->paned), child: hscale_box, FALSE, FALSE, padding: 5 );
1568 gtk_widget_show( widget: hscale_box );
1569
1570 trackbar->maxval = count;
1571 trackbar->onChangeCallback = onChange;
1572 trackbar->userdata = userdata;
1573 g_signal_connect(trackbar->widget, "value-changed",
1574 G_CALLBACK(icvOnTrackbar), trackbar);
1575
1576 // queue a widget resize to trigger a window resize to
1577 // compensate for the addition of trackbars
1578 gtk_widget_queue_resize(GTK_WIDGET(window->widget));
1579
1580 return trackbar_;
1581}
1582
1583
1584CV_IMPL void
1585cvSetMouseCallback( const char* window_name, CvMouseCallback on_mouse, void* param )
1586{
1587 CV_Assert(window_name && "NULL window name");
1588
1589 CV_LOCK_MUTEX();
1590
1591 const auto window = icvFindWindowByName(name: window_name);
1592 if (!window)
1593 return;
1594
1595 window->on_mouse = on_mouse;
1596 window->on_mouse_param = param;
1597}
1598
1599
1600CV_IMPL int cvGetTrackbarPos( const char* trackbar_name, const char* window_name )
1601{
1602 CV_Assert(window_name && "NULL window name");
1603 CV_Assert(trackbar_name && "NULL trackbar name");
1604
1605 CV_LOCK_MUTEX();
1606
1607 const auto window = icvFindWindowByName(name: window_name);
1608 if (!window)
1609 return -1;
1610
1611 const auto trackbar = icvFindTrackbarByName(window,name: trackbar_name);
1612 if (!trackbar)
1613 return -1;
1614
1615 return trackbar->pos;
1616}
1617
1618static void setTrackbarPos_(const std::shared_ptr<CvTrackbar>& trackbar, int pos);
1619CV_IMPL void cvSetTrackbarPos( const char* trackbar_name, const char* window_name, int pos )
1620{
1621 CV_Assert(window_name && "NULL window name");
1622 CV_Assert(trackbar_name && "NULL trackbar name");
1623
1624 CV_LOCK_MUTEX();
1625
1626 const auto window = icvFindWindowByName(name: window_name);
1627 if(!window)
1628 return;
1629
1630 const auto trackbar = icvFindTrackbarByName(window, name: trackbar_name);
1631 if (!trackbar)
1632 {
1633 CV_Error( cv::Error::StsNullPtr, "No trackbar found" );
1634 }
1635
1636 return setTrackbarPos_(trackbar, pos);
1637}
1638
1639static void setTrackbarPos_(const std::shared_ptr<CvTrackbar>& trackbar, int pos)
1640{
1641 CV_Assert(trackbar);
1642 CV_CheckLE(trackbar->minval, trackbar->maxval, "");
1643
1644 pos = std::max(a: pos, b: trackbar->minval);
1645 pos = std::min(a: pos, b: trackbar->maxval);
1646
1647 gtk_range_set_value( GTK_RANGE(trackbar->widget), value: pos );
1648}
1649
1650
1651CV_IMPL void cvSetTrackbarMax(const char* trackbar_name, const char* window_name, int maxval)
1652{
1653 CV_Assert(window_name && "NULL window name");
1654 CV_Assert(trackbar_name && "NULL trackbar name");
1655
1656 CV_LOCK_MUTEX();
1657
1658 const auto window = icvFindWindowByName(name: window_name);
1659 if(!window)
1660 return;
1661
1662 const auto trackbar = icvFindTrackbarByName(window,name: trackbar_name);
1663 if(!trackbar)
1664 return;
1665
1666 trackbar->maxval = maxval;
1667 if (trackbar->maxval >= trackbar->minval)
1668 gtk_range_set_range(GTK_RANGE(trackbar->widget), min: trackbar->minval, max: trackbar->maxval);
1669}
1670
1671
1672CV_IMPL void cvSetTrackbarMin(const char* trackbar_name, const char* window_name, int minval)
1673{
1674 CV_Assert(window_name && "NULL window name");
1675 CV_Assert(trackbar_name && "NULL trackbar name");
1676
1677 CV_LOCK_MUTEX();
1678
1679 const auto window = icvFindWindowByName(name: window_name);
1680 if(!window)
1681 return;
1682
1683 const auto trackbar = icvFindTrackbarByName(window,name: trackbar_name);
1684 if(!trackbar)
1685 return;
1686
1687 trackbar->minval = minval;
1688 if (trackbar->maxval >= trackbar->minval)
1689 gtk_range_set_range(GTK_RANGE(trackbar->widget), min: trackbar->minval, max: trackbar->maxval);
1690}
1691
1692
1693CV_IMPL void* cvGetWindowHandle( const char* window_name )
1694{
1695 CV_Assert(window_name && "NULL window name");
1696
1697 CV_LOCK_MUTEX();
1698
1699 const auto window = icvFindWindowByName(name: window_name);
1700 if(!window)
1701 return NULL;
1702
1703 return (void*)window->widget;
1704}
1705
1706
1707CV_IMPL const char* cvGetWindowName( void* window_handle )
1708{
1709 CV_Assert(window_handle && "NULL window handle");
1710
1711 CV_LOCK_MUTEX();
1712
1713 CvWindow* window = icvWindowByWidget( widget: (GtkWidget*)window_handle );
1714 if (window)
1715 return window->name.c_str();
1716
1717 return ""; // FIXME: NULL?
1718}
1719
1720static GtkFileFilter* icvMakeGtkFilter(const char* name, const char* patterns, GtkFileFilter* images)
1721{
1722 GtkFileFilter* filter = gtk_file_filter_new();
1723 gtk_file_filter_set_name(filter, name);
1724
1725 while(patterns[0])
1726 {
1727 gtk_file_filter_add_pattern(filter, pattern: patterns);
1728 gtk_file_filter_add_pattern(filter: images, pattern: patterns);
1729 patterns += strlen(s: patterns) + 1;
1730 }
1731
1732 return filter;
1733}
1734
1735static void icvShowSaveAsDialog(GtkWidget* widget, CvWindow* window)
1736{
1737 if (!window || !widget)
1738 return;
1739
1740 CvImageWidget* image_widget = CV_IMAGE_WIDGET(window->widget);
1741 if (!image_widget || !image_widget->original_image)
1742 return;
1743
1744 GtkWidget* dialog = gtk_file_chooser_dialog_new(title: "Save As...",
1745 GTK_WINDOW(widget),
1746 action: GTK_FILE_CHOOSER_ACTION_SAVE,
1747 GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
1748 GTK_STOCK_SAVE, GTK_RESPONSE_ACCEPT,
1749 NULL);
1750 gtk_file_chooser_set_do_overwrite_confirmation(GTK_FILE_CHOOSER(dialog), TRUE);
1751
1752 cv::String sname = gtk_window_get_title(GTK_WINDOW(window->frame));
1753 sname = sname.substr(pos: sname.find_last_of(s: "\\/") + 1) + ".png";
1754 gtk_file_chooser_set_current_name(GTK_FILE_CHOOSER(dialog), name: sname.c_str());
1755
1756 GtkFileFilter* filter_all = gtk_file_filter_new();
1757 gtk_file_filter_set_name(filter: filter_all, name: "All Files");
1758 gtk_file_filter_add_pattern(filter: filter_all, pattern: "*");
1759
1760 GtkFileFilter* filter_images = gtk_file_filter_new();
1761 gtk_file_filter_set_name(filter: filter_images, name: "All Images");
1762
1763 GtkFileFilter* file_filters[] = {
1764 icvMakeGtkFilter(name: "Portable Network Graphics files (*.png)", patterns: "*.png\0", images: filter_images),
1765 icvMakeGtkFilter(name: "JPEG files (*.jpeg;*.jpg;*.jpe)", patterns: "*.jpeg\0*.jpg\0*.jpe\0", images: filter_images),
1766 icvMakeGtkFilter(name: "Windows bitmap (*.bmp;*.dib)", patterns: "*.bmp\0*.dib\0", images: filter_images),
1767 icvMakeGtkFilter(name: "TIFF Files (*.tiff;*.tif)", patterns: "*.tiff\0*.tif\0", images: filter_images),
1768 icvMakeGtkFilter(name: "JPEG-2000 files (*.jp2)", patterns: "*.jp2\0", images: filter_images),
1769 icvMakeGtkFilter(name: "WebP files (*.webp)", patterns: "*.webp\0", images: filter_images),
1770 icvMakeGtkFilter(name: "Portable image format (*.pbm;*.pgm;*.ppm;*.pxm;*.pnm)", patterns: "*.pbm\0*.pgm\0*.ppm\0*.pxm\0*.pnm\0", images: filter_images),
1771 icvMakeGtkFilter(name: "OpenEXR Image files (*.exr)", patterns: "*.exr\0", images: filter_images),
1772 icvMakeGtkFilter(name: "Radiance HDR (*.hdr;*.pic)", patterns: "*.hdr\0*.pic\0", images: filter_images),
1773 icvMakeGtkFilter(name: "Sun raster files (*.sr;*.ras)", patterns: "*.sr\0*.ras\0", images: filter_images),
1774 filter_images,
1775 filter_all
1776 };
1777
1778 for (size_t idx = 0; idx < sizeof(file_filters)/sizeof(file_filters[0]); ++idx)
1779 gtk_file_chooser_add_filter(GTK_FILE_CHOOSER(dialog), filter: file_filters[idx]); // filter ownership is transferred to dialog
1780 gtk_file_chooser_set_filter(GTK_FILE_CHOOSER(dialog), filter: filter_images);
1781
1782 cv::String filename;
1783 if (gtk_dialog_run(GTK_DIALOG(dialog)) == GTK_RESPONSE_ACCEPT)
1784 {
1785 char* fname = gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(dialog));
1786 filename = fname;
1787 g_free(mem: fname);
1788 }
1789 gtk_widget_destroy(widget: dialog);
1790
1791 if (!filename.empty())
1792 {
1793 cv::Mat bgr;
1794 cv::cvtColor(src: cv::cvarrToMat(arr: image_widget->original_image), dst: bgr, code: cv::COLOR_RGB2BGR);
1795 cv::imwrite(filename, img: bgr);
1796 }
1797}
1798
1799#if defined(GTK_VERSION2) && !defined (GDK_KEY_Escape)
1800#define GDK_KEY_Escape GDK_Escape
1801#define GDK_KEY_Return GDK_Return
1802#define GDK_KEY_Linefeed GDK_Linefeed
1803#define GDK_KEY_Tab GDK_Tab
1804#define GDK_KEY_s GDK_s
1805#define GDK_KEY_S GDK_S
1806#endif //GDK_KEY_Escape
1807
1808static gboolean icvOnKeyPress(GtkWidget* widget, GdkEventKey* event, gpointer user_data)
1809{
1810 int code = 0;
1811
1812 if ( BIT_ALLIN(event->state, GDK_CONTROL_MASK) && (event->keyval == GDK_KEY_s || event->keyval == GDK_KEY_S))
1813 {
1814 try
1815 {
1816 icvShowSaveAsDialog(widget, window: (CvWindow*)user_data);
1817 }
1818 catch(...)
1819 {
1820 // suppress all exceptions here
1821 }
1822 }
1823
1824 switch( event->keyval )
1825 {
1826 case GDK_KEY_Escape:
1827 code = 27;
1828 break;
1829 case GDK_KEY_Return:
1830 case GDK_KEY_Linefeed:
1831 code = 13;
1832 break;
1833 case GDK_KEY_Tab:
1834 code = '\t';
1835 break;
1836 default:
1837 code = event->keyval;
1838 }
1839
1840 code |= event->state << 16;
1841
1842 if(thread_started)
1843 {
1844 g_mutex_lock(mutex: last_key_mutex);
1845 last_key = code;
1846 // signal any waiting threads
1847 g_cond_broadcast(cond: cond_have_key);
1848 g_mutex_unlock(mutex: last_key_mutex);
1849 }
1850 else
1851 {
1852 last_key = code;
1853 }
1854
1855 return FALSE;
1856}
1857
1858
1859static void icvOnTrackbar( GtkWidget* widget, gpointer user_data )
1860{
1861 int pos = cvRound( value: gtk_range_get_value(GTK_RANGE(widget)));
1862 CvTrackbar* trackbar = (CvTrackbar*)user_data;
1863
1864 if( trackbar && trackbar->signature == CV_TRACKBAR_MAGIC_VAL &&
1865 trackbar->widget == widget )
1866 {
1867 trackbar->pos = pos;
1868 if (trackbar->onChangeCallback)
1869 trackbar->onChangeCallback(pos, trackbar->userdata);
1870
1871 // deprecated
1872 if( trackbar->data )
1873 *trackbar->data = pos;
1874 if( trackbar->notify2 )
1875 trackbar->notify2(pos, trackbar->userdata);
1876 else if( trackbar->notify )
1877 trackbar->notify(pos);
1878 }
1879}
1880
1881static gboolean icvOnClose( GtkWidget* widget, GdkEvent* /*event*/, gpointer user_data )
1882{
1883 CvWindow* window = (CvWindow*)user_data;
1884 if( window->signature == CV_WINDOW_MAGIC_VAL &&
1885 window->frame == widget )
1886 {
1887 try
1888 {
1889 icvDeleteWindow_(window);
1890 }
1891 catch (...)
1892 {
1893 CV_LOG_WARNING(NULL, "OpenCV/GTK: unexpected C++ exception in icvDeleteWindow_");
1894 }
1895 }
1896 return TRUE;
1897}
1898
1899
1900static gboolean icvOnMouse( GtkWidget *widget, GdkEvent *event, gpointer user_data )
1901{
1902 // TODO move this logic to CvImageWidget
1903 // TODO add try-catch wrappers into all callbacks
1904 CvWindow* window = (CvWindow*)user_data;
1905 if (!window || !widget ||
1906 window->signature != CV_WINDOW_MAGIC_VAL ||
1907 window->widget != widget ||
1908 !window->on_mouse)
1909 return FALSE;
1910
1911 CvPoint2D32f pt32f = {.x: -1., .y: -1.};
1912 CvPoint pt = {.x: -1,.y: -1};
1913 int cv_event = -1, state = 0, flags = 0;
1914 CvImageWidget * image_widget = CV_IMAGE_WIDGET( widget );
1915
1916 if( event->type == GDK_MOTION_NOTIFY )
1917 {
1918 GdkEventMotion* event_motion = (GdkEventMotion*)event;
1919
1920 cv_event = CV_EVENT_MOUSEMOVE;
1921 pt32f.x = cvFloor(value: event_motion->x);
1922 pt32f.y = cvFloor(value: event_motion->y);
1923 state = event_motion->state;
1924 }
1925 else if( event->type == GDK_BUTTON_PRESS ||
1926 event->type == GDK_BUTTON_RELEASE ||
1927 event->type == GDK_2BUTTON_PRESS )
1928 {
1929 GdkEventButton* event_button = (GdkEventButton*)event;
1930 pt32f.x = cvFloor(value: event_button->x);
1931 pt32f.y = cvFloor(value: event_button->y);
1932
1933
1934 if( event_button->type == GDK_BUTTON_PRESS )
1935 {
1936 cv_event = event_button->button == 1 ? CV_EVENT_LBUTTONDOWN :
1937 event_button->button == 2 ? CV_EVENT_MBUTTONDOWN :
1938 event_button->button == 3 ? CV_EVENT_RBUTTONDOWN : 0;
1939 }
1940 else if( event_button->type == GDK_BUTTON_RELEASE )
1941 {
1942 cv_event = event_button->button == 1 ? CV_EVENT_LBUTTONUP :
1943 event_button->button == 2 ? CV_EVENT_MBUTTONUP :
1944 event_button->button == 3 ? CV_EVENT_RBUTTONUP : 0;
1945 }
1946 else if( event_button->type == GDK_2BUTTON_PRESS )
1947 {
1948 cv_event = event_button->button == 1 ? CV_EVENT_LBUTTONDBLCLK :
1949 event_button->button == 2 ? CV_EVENT_MBUTTONDBLCLK :
1950 event_button->button == 3 ? CV_EVENT_RBUTTONDBLCLK : 0;
1951 }
1952 state = event_button->state;
1953 }
1954 else if( event->type == GDK_SCROLL )
1955 {
1956 GdkEventScroll* event_scroll = (GdkEventScroll*)event;
1957 pt32f.x = cvFloor(value: event_scroll->x);
1958 pt32f.y = cvFloor(value: event_scroll->y);
1959
1960#if defined(GTK_VERSION3_4)
1961 // NOTE: in current implementation doesn't possible to put into callback function delta_x and delta_y separately
1962 double delta = (event->scroll.delta_x + event->scroll.delta_y);
1963 cv_event = (event->scroll.delta_x==0) ? CV_EVENT_MOUSEWHEEL : CV_EVENT_MOUSEHWHEEL;
1964#else
1965 cv_event = CV_EVENT_MOUSEWHEEL;
1966#endif //GTK_VERSION3_4
1967
1968 state = event->scroll.state;
1969
1970 switch(event->scroll.direction) {
1971#if defined(GTK_VERSION3_4)
1972 case GDK_SCROLL_SMOOTH: flags |= (((int)delta << 16));
1973 break;
1974#endif //GTK_VERSION3_4
1975 case GDK_SCROLL_LEFT: cv_event = CV_EVENT_MOUSEHWHEEL;
1976 /* FALLTHRU */
1977 case GDK_SCROLL_UP: flags |= ~0xffff;
1978 break;
1979 case GDK_SCROLL_RIGHT: cv_event = CV_EVENT_MOUSEHWHEEL;
1980 /* FALLTHRU */
1981 case GDK_SCROLL_DOWN: flags |= (((int)1 << 16));
1982 break;
1983 default: ;
1984 };
1985 }
1986
1987 if( cv_event >= 0 )
1988 {
1989 // scale point if image is scaled
1990 if( (image_widget->flags & cv::WINDOW_AUTOSIZE)==0 &&
1991 image_widget->original_image &&
1992 image_widget->scaled_image )
1993 {
1994 // image origin is not necessarily at (0,0)
1995 int x0 = (gtk_widget_get_allocated_width(widget) - image_widget->scaled_image->cols)/2;
1996 int y0 = (gtk_widget_get_allocated_height(widget) - image_widget->scaled_image->rows)/2;
1997 pt.x = cvFloor( value: ((pt32f.x-x0)*image_widget->original_image->cols)/
1998 image_widget->scaled_image->cols );
1999 pt.y = cvFloor( value: ((pt32f.y-y0)*image_widget->original_image->rows)/
2000 image_widget->scaled_image->rows );
2001 }
2002 else
2003 {
2004 pt = cvPointFrom32f( point: pt32f );
2005 }
2006
2007 if (!image_widget->original_image/*OpenGL*/ || (
2008 (unsigned)pt.x < (unsigned)(image_widget->original_image->width) &&
2009 (unsigned)pt.y < (unsigned)(image_widget->original_image->height)
2010 ))
2011 {
2012 // handle non-keyboard (mouse) modifiers first
2013 flags |=
2014 BIT_MAP(state, GDK_BUTTON1_MASK, CV_EVENT_FLAG_LBUTTON) |
2015 BIT_MAP(state, GDK_BUTTON2_MASK, CV_EVENT_FLAG_MBUTTON) |
2016 BIT_MAP(state, GDK_BUTTON3_MASK, CV_EVENT_FLAG_RBUTTON);
2017 // keyboard modifiers
2018 state &= gtk_accelerator_get_default_mod_mask();
2019 flags |=
2020 BIT_MAP(state, GDK_SHIFT_MASK, CV_EVENT_FLAG_SHIFTKEY) |
2021 BIT_MAP(state, GDK_CONTROL_MASK, CV_EVENT_FLAG_CTRLKEY) |
2022 BIT_MAP(state, GDK_MOD1_MASK, CV_EVENT_FLAG_ALTKEY) |
2023 BIT_MAP(state, GDK_MOD2_MASK, CV_EVENT_FLAG_ALTKEY);
2024 window->on_mouse( cv_event, pt.x, pt.y, flags, window->on_mouse_param );
2025 }
2026 }
2027
2028 return FALSE;
2029}
2030
2031
2032static gboolean icvAlarm( gpointer user_data )
2033{
2034 *(int*)user_data = 1;
2035 return FALSE;
2036}
2037
2038
2039CV_IMPL int cvWaitKey( int delay )
2040{
2041 if (thread_started && g_thread_self() != window_thread)
2042 {
2043 gboolean expired = true;
2044 int my_last_key;
2045
2046 g_mutex_lock(mutex: last_key_mutex);
2047 // wait for signal or timeout if delay > 0
2048 if(delay>0){
2049 GTimeVal timer;
2050 g_get_current_time(result: &timer);
2051 g_time_val_add(time_: &timer, microseconds: delay*1000);
2052 expired = !g_cond_timed_wait(cond: cond_have_key, mutex: last_key_mutex, timeval: &timer);
2053 }
2054 else{
2055 if (getGTKWindows().empty())
2056 {
2057 CV_LOG_WARNING(NULL, "cv::waitKey() is called without timeout and missing active windows. Ignoring");
2058 }
2059 else
2060 {
2061 g_cond_wait(cond: cond_have_key, mutex: last_key_mutex);
2062 expired=false;
2063 }
2064 }
2065 my_last_key = last_key;
2066 g_mutex_unlock(mutex: last_key_mutex);
2067 if (expired || getGTKWindows().empty())
2068 {
2069 return -1;
2070 }
2071 return my_last_key;
2072 }
2073 else
2074 {
2075 int expired = 0;
2076 guint timer = 0;
2077 if( delay > 0 )
2078 timer = g_timeout_add( interval: delay, function: icvAlarm, data: &expired );
2079 last_key = -1;
2080 while( gtk_main_iteration_do(TRUE) && last_key < 0 && !expired && (delay > 0 || !getGTKWindows().empty()))
2081 ;
2082
2083 if( delay > 0 && !expired )
2084 g_source_remove(tag: timer);
2085 }
2086 return last_key;
2087}
2088
2089namespace cv { namespace impl {
2090
2091using namespace cv::highgui_backend;
2092
2093class GTKTrackbar;
2094
2095class GTKWindow
2096 : public UIWindow
2097 , public std::enable_shared_from_this<GTKWindow>
2098{
2099protected:
2100 const std::string name_;
2101 std::weak_ptr<CvWindow> window_;
2102 std::map<std::string, std::shared_ptr<GTKTrackbar> > trackbars_;
2103public:
2104 GTKWindow(const std::string& name, const std::shared_ptr<CvWindow>& window)
2105 : name_(name)
2106 , window_(window)
2107 {
2108 // nothing
2109 }
2110
2111 ~GTKWindow() CV_OVERRIDE
2112 {
2113 if (!window_.expired())
2114 destroy();
2115 CV_LOG_DEBUG(NULL, "OpenCV/UI/GTK: GTKWindow(" << name_ << ") is disposed");
2116 }
2117
2118 const std::string& getID() const CV_OVERRIDE { return name_; }
2119
2120 bool isActive() const CV_OVERRIDE { return !window_.expired(); }
2121
2122 void destroy() CV_OVERRIDE
2123 {
2124 cv::AutoLock lock(getWindowMutex());
2125 if (!window_.expired())
2126 {
2127 auto window = window_.lock();
2128 if (window)
2129 window->destroy();
2130 window_.reset();
2131 }
2132 }
2133
2134 void imshow(InputArray image) CV_OVERRIDE
2135 {
2136 auto window = window_.lock();
2137 CV_Assert(window);
2138 CvImageWidget* image_widget = CV_IMAGE_WIDGET(window->widget);
2139 CV_Assert(image_widget);
2140 Mat img = image.getMat();
2141 CvMat c_img = cvMat(m: img); // TODO Drop C-API
2142 cvImageWidgetSetImage(widget: image_widget, arr: &c_img);
2143 }
2144
2145 double getProperty(int prop) const CV_OVERRIDE
2146 {
2147 auto window = window_.lock();
2148 CV_Assert(window);
2149 // see cvGetWindowProperty
2150 switch (prop)
2151 {
2152 case cv::WND_PROP_FULLSCREEN:
2153 return (double)window->status;
2154
2155 case cv::WND_PROP_AUTOSIZE:
2156 return (window->flags & cv::WINDOW_AUTOSIZE) ? 1.0 : 0.0;
2157
2158 case cv::WND_PROP_ASPECT_RATIO:
2159 return getRatioWindow_(window);
2160
2161#ifdef HAVE_OPENGL
2162 case cv::WND_PROP_OPENGL:
2163 return window->useGl ? 1.0 : 0.0;
2164#endif
2165
2166 default:
2167 break;
2168 }
2169 return std::numeric_limits<double>::quiet_NaN();
2170 }
2171
2172 bool setProperty(int prop, double value) CV_OVERRIDE
2173 {
2174 auto window = window_.lock();
2175 CV_Assert(window);
2176 // see cvSetWindowProperty
2177 switch (prop)
2178 {
2179 case cv::WND_PROP_FULLSCREEN:
2180 if ((int)value != cv::WINDOW_NORMAL && (int)value != cv::WINDOW_FULLSCREEN) // bad arg
2181 break;
2182 setModeWindow_(window, mode: value);
2183 return true;
2184
2185 default:
2186 break;
2187 }
2188 return false;
2189 }
2190
2191 void resize(int width, int height) CV_OVERRIDE
2192 {
2193 auto window = window_.lock();
2194 CV_Assert(window);
2195 resizeWindow_(window, width, height);
2196 }
2197
2198 void move(int x, int y) CV_OVERRIDE
2199 {
2200 auto window = window_.lock();
2201 CV_Assert(window);
2202 gtk_window_move(GTK_WINDOW(window->frame), x, y);
2203 }
2204
2205 Rect getImageRect() const CV_OVERRIDE
2206 {
2207 auto window = window_.lock();
2208 CV_Assert(window);
2209 return getImageRect_(window);
2210 }
2211
2212 void setTitle(const std::string& title) CV_OVERRIDE
2213 {
2214 auto window = window_.lock();
2215 CV_Assert(window);
2216 gtk_window_set_title(GTK_WINDOW(window->frame), title: title.c_str());
2217 }
2218
2219 void setMouseCallback(MouseCallback onMouse, void* userdata /*= 0*/) CV_OVERRIDE
2220 {
2221 auto window = window_.lock();
2222 CV_Assert(window);
2223 window->on_mouse = onMouse;
2224 window->on_mouse_param = userdata;
2225 }
2226
2227 std::shared_ptr<UITrackbar> createTrackbar(
2228 const std::string& name,
2229 int count,
2230 TrackbarCallback onChange /*= 0*/,
2231 void* userdata /*= 0*/
2232 ) CV_OVERRIDE
2233 {
2234 auto window = window_.lock();
2235 CV_Assert(window);
2236 CV_LOG_INFO(NULL, "OpenCV/UI: Creating GTK trackbar at '" << name_ << "': '" << name << "'");
2237 auto trackbar = createTrackbar_(window, name, count, onChange, userdata);
2238 auto ui_trackbar = std::make_shared<GTKTrackbar>(args: name, args&: trackbar, args: shared_from_this());
2239 {
2240 cv::AutoLock lock(getWindowMutex());
2241 trackbars_.emplace(args: name, args&: ui_trackbar);
2242 }
2243 return std::static_pointer_cast<UITrackbar>(r: ui_trackbar);
2244 }
2245
2246 std::shared_ptr<UITrackbar> findTrackbar(const std::string& name) CV_OVERRIDE
2247 {
2248 cv::AutoLock lock(getWindowMutex());
2249 auto i = trackbars_.find(x: name);
2250 if (i != trackbars_.end())
2251 {
2252 return std::static_pointer_cast<UITrackbar>(r: i->second);
2253 }
2254 return std::shared_ptr<UITrackbar>();
2255 }
2256}; // GTKWindow
2257
2258
2259class GTKTrackbar : public UITrackbar
2260{
2261protected:
2262 /*const*/ std::string name_;
2263 std::weak_ptr<CvTrackbar> trackbar_;
2264 std::weak_ptr<GTKWindow> parent_;
2265 std::map<std::string, std::shared_ptr<GTKTrackbar> > trackbars_;
2266public:
2267 GTKTrackbar(const std::string& name, const std::shared_ptr<CvTrackbar>& trackbar, const std::shared_ptr<GTKWindow>& parent)
2268 : trackbar_(trackbar)
2269 , parent_(parent)
2270 {
2271 name_ = std::string("<") + name + ">@" + parent->getID();
2272 }
2273
2274 ~GTKTrackbar() CV_OVERRIDE
2275 {
2276 if (!trackbar_.expired())
2277 destroy();
2278 CV_LOG_DEBUG(NULL, "OpenCV/UI/GTK: GTKTrackbar(" << name_ << ") is disposed");
2279 }
2280
2281 const std::string& getID() const CV_OVERRIDE { return name_; }
2282
2283 bool isActive() const CV_OVERRIDE { return !trackbar_.expired(); }
2284
2285 void destroy() CV_OVERRIDE
2286 {
2287 // nothing (destroyed with parent window, dedicated trackbar removal is not supported)
2288 }
2289
2290 int getPos() const CV_OVERRIDE
2291 {
2292 auto trackbar = trackbar_.lock();
2293 CV_Assert(trackbar);
2294 return trackbar->pos;
2295 }
2296 void setPos(int pos) CV_OVERRIDE
2297 {
2298 auto trackbar = trackbar_.lock();
2299 CV_Assert(trackbar);
2300 return setTrackbarPos_(trackbar, pos);
2301 }
2302
2303 cv::Range getRange() const CV_OVERRIDE
2304 {
2305 auto trackbar = trackbar_.lock();
2306 CV_Assert(trackbar);
2307 return cv::Range(trackbar->minval, trackbar->maxval);
2308 }
2309
2310 void setRange(const cv::Range& range) CV_OVERRIDE
2311 {
2312 auto trackbar = trackbar_.lock();
2313 CV_Assert(trackbar);
2314 CV_CheckLE(range.start, range.end, "Invalid trackbar range");
2315 gtk_range_set_range(GTK_RANGE(trackbar->widget), min: range.start, max: range.end);
2316 }
2317}; // GTKTrackbar
2318
2319
2320class GTKBackendUI : public UIBackend
2321{
2322public:
2323 GTKBackendUI()
2324 {
2325 // NB: avoid static initialization order fiasco
2326 (void)getGTKWindows();
2327 }
2328 ~GTKBackendUI() CV_OVERRIDE
2329 {
2330 destroyAllWindows();
2331 }
2332
2333 void destroyAllWindows() CV_OVERRIDE
2334 {
2335 cvDestroyAllWindows();
2336 }
2337
2338 // namedWindow
2339 virtual std::shared_ptr<UIWindow> createWindow(
2340 const std::string& winname,
2341 int flags
2342 ) CV_OVERRIDE
2343 {
2344 CV_LOG_INFO(NULL, "OpenCV/UI: Creating GTK window: " << winname << " (" << flags << ")");
2345 auto window = namedWindow_(name: winname, flags);
2346 auto ui_window = std::make_shared<GTKWindow>(args: winname, args&: window);
2347 return ui_window;
2348 }
2349
2350 int waitKeyEx(int delay) CV_OVERRIDE
2351 {
2352 return cvWaitKey(delay);
2353 }
2354 int pollKey() CV_OVERRIDE
2355 {
2356 return cvWaitKey(delay: 1); // TODO
2357 }
2358
2359 const std::string getName() const CV_OVERRIDE
2360 {
2361#if GTK_MAJOR_VERSION == 2
2362 return "GTK2";
2363#elif GTK_MAJOR_VERSION == 3
2364 return "GTK3";
2365#elif GTK_MAJOR_VERSION == 4
2366 return "GTK4";
2367#else
2368#error "Unsupported GTK version"
2369#endif
2370 }
2371}; // GTKBackendUI
2372
2373static
2374std::shared_ptr<GTKBackendUI>& getInstance()
2375{
2376 static std::shared_ptr<GTKBackendUI> g_instance = std::make_shared<GTKBackendUI>();
2377 return g_instance;
2378}
2379
2380} // namespace impl
2381
2382#ifndef BUILD_PLUGIN
2383namespace highgui_backend {
2384
2385std::shared_ptr<UIBackend> createUIBackendGTK()
2386{
2387 return impl::getInstance();
2388}
2389
2390} // namespace highgui_backend
2391#endif
2392
2393} // namespace
2394
2395#ifdef BUILD_PLUGIN
2396
2397#define ABI_VERSION 0
2398#define API_VERSION 0
2399#include "plugin_api.hpp"
2400
2401static
2402CvResult cv_getInstance(CV_OUT CvPluginUIBackend* handle) CV_NOEXCEPT
2403{
2404 try
2405 {
2406 if (!handle)
2407 return CV_ERROR_FAIL;
2408 *handle = cv::impl::getInstance().get();
2409 return CV_ERROR_OK;
2410 }
2411 catch (...)
2412 {
2413 return CV_ERROR_FAIL;
2414 }
2415}
2416
2417static const OpenCV_UI_Plugin_API plugin_api =
2418{
2419 {
2420 sizeof(OpenCV_UI_Plugin_API), ABI_VERSION, API_VERSION,
2421 CV_VERSION_MAJOR, CV_VERSION_MINOR, CV_VERSION_REVISION, CV_VERSION_STATUS,
2422 "GTK" CVAUX_STR(GTK_MAJOR_VERSION) " OpenCV UI plugin"
2423 },
2424 {
2425 /* 1*/cv_getInstance
2426 }
2427};
2428
2429const OpenCV_UI_Plugin_API* CV_API_CALL opencv_ui_plugin_init_v0(int requested_abi_version, int requested_api_version, void* /*reserved=NULL*/) CV_NOEXCEPT
2430{
2431 if (requested_abi_version == ABI_VERSION && requested_api_version <= API_VERSION)
2432 return &plugin_api;
2433 return NULL;
2434}
2435
2436#endif // BUILD_PLUGIN
2437
2438#endif // HAVE_GTK
2439

Provided by KDAB

Privacy Policy
Improve your Profiling and Debugging skills
Find out more

source code of opencv/modules/highgui/src/window_gtk.cpp