1 /*********************************/
2 /*                               */
3 /*  Gtk file dialog              */
4 /*                               */
5 /*********************************/
6 
7 #include <sys/param.h>
8 #include "unix/guts.h"
9 #include "img.h"
10 
11 #if !(defined(__FreeBSD__ ) && PERL_REVISION == 5 && PERL_VERSION < 22)
12 /* Observed hangs on 2 processors with DBUS session running */
13 #define SAFE_DBUS
14 #endif
15 
16 #ifdef WITH_GTK
17 
18 #undef GT
19 
20 #undef dirty
21 
22 #define Window  XWindow
23 
24 #ifndef WITH_GTK_NONX11
25 #include <gdk/gdkx.h>
26 #endif
27 #include <gtk/gtk.h>
28 
29 static int           gtk_initialized        = 0;
30 static GApplication* gtk_app                = NULL;
31 static GtkWidget*    gtk_dialog             = NULL;
32 static char	     gtk_dialog_title[256];
33 static char*	     gtk_dialog_title_ptr   = NULL;
34 static Bool	     gtk_select_multiple    = FALSE;
35 static Bool	     gtk_overwrite_prompt   = FALSE;
36 static Bool	     gtk_show_hidden_files  = FALSE;
37 static char	     gtk_current_folder[MAXPATHLEN+1];
38 static char*	     gtk_current_folder_ptr = NULL;
39 static List*	     gtk_filters            = NULL;
40 static int	     gtk_filter_index       = 0;
41 
42 static GdkDisplay * display = NULL;
43 
44 static Color
gdk_color(GdkColor * c)45 gdk_color(GdkColor * c)
46 {
47 		return ((c->red >> 8) << 16) | ((c->green >> 8) << 8) | (c->blue >> 8);
48 }
49 
50 
51 typedef struct {
52 		GType (*func)(void);
53 		char * name;
54 		char * gtk_class;
55 		int    prima_class;
56 		Font * prima_font;
57 } GTFStruct;
58 
59 #define GT(x) gtk_##x##_get_type, #x
60 
61 static GTFStruct widget_types[] = {
62 		{ GT(button),       "GtkButton",         wcButton      , NULL },
63 		{ GT(check_button), "GtkCheckButton",    wcCheckBox    , NULL },
64 		{ GT(combo_box),    "GtkCombo",          wcCombo       , NULL },
65 		{ GT(dialog),       "GtkDialog",         wcDialog      , NULL },
66 		{ GT(entry),        "GtkEditable",       wcEdit        , NULL },
67 		{ GT(entry),        "GtkEntry",          wcInputLine   , NULL },
68 		{ GT(label),        "GtkLabel",          wcLabel       , &guts. default_msg_font },
69 		{ GT(menu),         "GtkMenuItem",       wcMenu        , &guts. default_menu_font },
70 		{ GT(menu_item),    "GtkMenuItem",       wcPopup       , NULL },
71 		{ GT(check_button), "GtkRadioButton",    wcRadio       , NULL },
72 		{ GT(scrollbar),    "GtkScrollBar",      wcScrollBar   , NULL },
73 		{ GT(widget),       "GtkWidget",         wcWidget      , &guts. default_widget_font },
74 		{ GT(window),       "GtkWindow",         wcWindow      , &guts. default_caption_font },
75 		{ GT(widget),       "GtkWidget",         wcApplication , &guts. default_font },
76 #if GTK_MAJOR_VERSION == 2
77 		{ GT(list),         "GtkList",           wcListBox     , NULL },
78 		{ GT(ruler),        "GtkRuler",          wcSlider      , NULL },
79 #else
80 		{ GT(list_box),     "GtkListBox",        wcListBox     , NULL },
81 		{ GT(spin_button),  "GtkSpinButton",     wcSlider      , NULL },
82 #endif
83 };
84 #undef GT
85 
86 #if GTK_MAJOR_VERSION == 3
87 GdkDisplay *
my_gdk_display_open_default(void)88 my_gdk_display_open_default (void)
89 {
90   GdkDisplay *display;
91 
92   display = gdk_display_get_default ();
93   if (display)
94     return display;
95 
96   display = gdk_display_open (gdk_get_display_arg_name ());
97 
98   return display;
99 }
100 #endif
101 
102 #ifdef SAFE_DBUS
103 /* GIO wants that callback, even empty */
gtk_application_activate(GApplication * app)104 static void gtk_application_activate (GApplication *app) {}
105 #endif
106 
107 Display*
prima_gtk_init(void)108 prima_gtk_init(void)
109 {
110 	int i, argc = 0;
111 	Display *ret;
112 	GtkSettings * settings;
113 	Color ** stdcolors;
114 	PangoWeight weight;
115 
116 	switch ( gtk_initialized) {
117 	case -1:
118 		return NULL;
119 	case 1:
120 #ifdef WITH_GTK_NONX11
121 		{
122 		}
123 		return (void*)1;
124 #else
125 		return gdk_x11_display_get_xdisplay(display);
126 #endif
127 	}
128 
129 #ifdef WITH_GTK_NONX11
130 	{
131 		char * display_str = getenv("DISPLAY");
132 		if ( display_str ) {
133 			struct stat s;
134 			if ((stat( display_str, &s) < 0) || !S_ISSOCK(s.st_mode))  /* not a socket */
135 				return (void*)0;
136 		}
137 	}
138 #endif
139 
140 #if PERL_REVISION == 5 && PERL_VERSION == 20
141 /* perl bug in 5.20.0, see more at https://rt.perl.org/Ticket/Display.html?id=122105 */
142 	gtk_disable_setlocale();
143 #endif
144 	if ( !gtk_parse_args (&argc, NULL) || (
145 		display =
146 #if GTK_MAJOR_VERSION == 2
147 			gdk_display_open_default_libgtk_only()
148 #else
149 			my_gdk_display_open_default()
150 #endif
151 		) == NULL) {
152 		gtk_initialized = -1;
153 		return false;
154 	} else {
155 		gtk_initialized = 1;
156 		XSetErrorHandler( guts.main_error_handler );
157 #ifdef WITH_GTK_NONX11
158 		ret = (void*)1;
159 #else
160 		ret = gdk_x11_display_get_xdisplay(display);
161 #endif
162 	}
163 #if PERL_REVISION == 5 && PERL_VERSION >= 22
164 /* https://rt.perl.org/Ticket/Display.html?id=133945 */
165 	sync_locale();
166 #endif
167 
168 #if defined(SAFE_DBUS) && (GLIB_MAJOR_VERSION > 2 || (GLIB_MAJOR_VERSION == 2 && GLIB_MINOR_VERSION >= 34))
169 	gtk_app = g_application_new ("org.prima", G_APPLICATION_NON_UNIQUE);
170 	g_signal_connect (gtk_app, "activate", G_CALLBACK (gtk_application_activate), NULL);
171 	if ( !g_application_register (gtk_app, NULL, NULL)) {
172   		g_object_unref (gtk_app);
173 		gtk_app = NULL;
174 	}
175 #endif
176 
177 	settings  = gtk_settings_get_default();
178 	stdcolors = prima_standard_colors();
179 #if GTK_MAJOR_VERSION == 2
180 	for ( i = 0; i < sizeof(widget_types)/sizeof(GTFStruct); i++) {
181 		GTFStruct * s = widget_types + i;
182 		Color     * c = stdcolors[ s-> prima_class >> 16 ];
183 		Font      * f = s->prima_font;
184 		GtkStyle  * t = gtk_rc_get_style_by_paths(settings, NULL, s->gtk_class, s->func());
185 		int selected  = (
186 			s->prima_class == wcRadio ||
187 			s->prima_class == wcCheckBox ||
188 			s->prima_class == wcButton
189 		) ? GTK_STATE_ACTIVE : GTK_STATE_SELECTED;
190 		if ( t == NULL ) {
191 			Pdebug("cannot query gtk style for %s\n", s->name);
192 			t = gtk_rc_get_style_by_paths(settings, NULL, NULL, GTK_TYPE_WIDGET);
193 			if ( !t ) continue;
194 		}
195 		c[ciFore]         = gdk_color( t-> fg + GTK_STATE_NORMAL );
196 		c[ciBack]         = gdk_color( t-> bg + GTK_STATE_NORMAL );
197 		c[ciDisabledText] = gdk_color( t-> fg + GTK_STATE_INSENSITIVE );
198 		c[ciDisabled]     = gdk_color( t-> bg + GTK_STATE_INSENSITIVE );
199 
200 		if ( s-> prima_class == wcMenu || s-> prima_class == wcPopup) {
201 			/* Observed on Centos7 - GTK_STATE_SELECTED gives white
202 			on white, while GTK_STATE_PRELIGHT gives correct colors.
203 			OTOH, on Ubuntu it is other way around. Without digging
204 			too much into GTK guts, just select the one that gives
205 			best contrast */
206 
207 			int da, db;
208 			Color ca1, ca2, cb1, cb2;
209 			ca1 = gdk_color( t-> fg + selected );
210 			ca2 = gdk_color( t-> bg + selected );
211 			da =
212 				abs( (int)(ca1 & 0xff)-(int)(ca2 & 0xff) ) +
213 				abs( (int)((ca1 & 0xff00)>>8)-(int)((ca2 & 0xff00)>>8) ) +
214 				abs( (int)((ca1 & 0xff0000)>>16)-(int)((ca2 & 0xff0000)>>16) )
215 			;
216 			cb1 = gdk_color( t-> fg + GTK_STATE_PRELIGHT );
217 			cb2 = gdk_color( t-> bg + GTK_STATE_PRELIGHT );
218 			db =
219 				abs( (int)(cb1 & 0xff)-(int)(cb2 & 0xff) ) +
220 				abs( (int)((cb1 & 0xff00)>>8)-(int)((cb2 & 0xff00)>>8) ) +
221 				abs( (int)((cb1 & 0xff0000)>>16)-(int)((cb2 & 0xff0000)>>16) )
222 			;
223 			c[ciHiliteText]   = (da > db) ? ca1 : cb1;
224 			c[ciHilite]       = (da > db) ? ca2 : cb2;
225 		} else {
226 			c[ciHiliteText]   = gdk_color( t-> fg + selected );
227 			c[ciHilite]       = gdk_color( t-> bg + selected );
228 		}
229 		Pdebug("gtk-color: %s %06x %06x %06x %06x %06x\n", s->name, c[0], c[1], c[2], c[3], c[4], c[5]);
230 
231 		if ( !f) continue;
232 		bzero(f, sizeof(Font));
233 		strncpy( f->name, pango_font_description_get_family(t->font_desc), 255);
234 		f->name[255]=0;
235 		/* does gnome ignore X resolution? */
236 		f-> size = pango_font_description_get_size(t->font_desc) / PANGO_SCALE * (96.0 / guts. resolution. y) + .5;
237 		weight = pango_font_description_get_weight(t->font_desc);
238 		if ( weight <= PANGO_WEIGHT_LIGHT ) f-> style |= fsThin;
239 		if ( weight >= PANGO_WEIGHT_BOLD  ) f-> style |= fsBold;
240 		if ( pango_font_description_get_style(t->font_desc) == PANGO_STYLE_ITALIC)
241 			f-> style |= fsItalic;
242 		strcpy( f->encoding, "Default" );
243 		f-> undef. width = f-> undef. height = f-> undef. pitch = f-> undef. vector = 1;
244 		apc_font_pick( application, f, f);
245 #define DEBUG_FONT(font) f->height,f->width,f->size,f->name,f->encoding
246 		Fdebug("gtk-font (%s): %d.[w=%d,s=%d].%s.%s\n", s->name, DEBUG_FONT(f));
247 	}
248 #endif
249 	return ret;
250 }
251 
252 Bool
prima_gtk_done(void)253 prima_gtk_done(void)
254 {
255 	if ( gtk_filters) {
256 		int i;
257 		for ( i = 0; i < gtk_filters-> count; i++)
258 			g_object_unref(( GObject*) gtk_filters-> items[i]);
259 		plist_destroy( gtk_filters);
260 		gtk_filters = NULL;
261 	}
262 	if ( gtk_app ) {
263 		g_object_unref( gtk_app );
264 		gtk_app = NULL;
265 	}
266 	gtk_initialized = 0;
267 	return true;
268 }
269 
270 #ifndef WITH_GTK_NONX11
271 static void
set_transient_for(void)272 set_transient_for(void)
273 {
274 	Handle toplevel = prima_find_toplevel_window(NULL_HANDLE);
275 	if ( toplevel ) {
276 		GdkWindow * g = NULL;
277 
278 #if GTK_MAJOR_VERSION > 2 || (GTK_MAJOR_VERSION == 2 && GTK_MINOR_VERSION >= 14)
279 		g = gtk_widget_get_window(GTK_WIDGET(gtk_dialog));
280 #else
281 		g = gtk_dialog->window;
282 #endif
283 		if ( g ) {
284 			Window w = GDK_WINDOW_XID(g);
285 			if ( w )
286 				XSetTransientForHint( DISP, w, PWidget(toplevel)-> handle);
287 		}
288 	}
289 }
290 #endif
291 
292 
293 static gboolean
do_events(gpointer data)294 do_events(gpointer data)
295 {
296 	int* stage = ( int*) data;
297 	static struct timeval last_event = {0,0}, t;
298 	if ( gtk_dialog != NULL && !*stage ) {
299 		*stage = 1;
300 #ifdef WITH_GTK_NONX11
301 		gtk_window_present(GTK_WINDOW(gtk_dialog));
302 #else
303 		set_transient_for();
304 #endif
305 	}
306 	if (( t.tv_sec - last_event.tv_sec) * 1000000 + t.tv_usec - last_event.tv_usec > 10000) {
307 		last_event = t;
308 		prima_one_loop_round( WAIT_NEVER, true);
309 	}
310 	return gtk_dialog != NULL;
311 }
312 
ignore_errors(Display * d,XErrorEvent * ev)313 static int ignore_errors(Display *d, XErrorEvent *ev) { return 0; }
314 
315 static char *
gtk_openfile(Bool open)316 gtk_openfile( Bool open)
317 {
318 	char *result = NULL;
319 	struct MsgDlg message_dlg, **storage;
320 	int stage = 0;
321 
322 	if ( gtk_dialog) return NULL; /* we're not reentrant */
323 
324 	XFlush(DISP);
325 	XCHECKPOINT;
326 	XSetErrorHandler(ignore_errors);
327 
328 	gtk_dialog = gtk_file_chooser_dialog_new (
329 		gtk_dialog_title_ptr ?
330 			gtk_dialog_title_ptr :
331 			( open ? "Open File" : "Save File"),
332 		NULL,
333 		open ? GTK_FILE_CHOOSER_ACTION_OPEN : GTK_FILE_CHOOSER_ACTION_SAVE,
334 #if GTK_MAJOR_VERSION == 3
335 		"_Cancel",
336 #else
337 		GTK_STOCK_CANCEL,
338 #endif
339 		GTK_RESPONSE_CANCEL,
340 #if GTK_MAJOR_VERSION == 3
341 		"_Open",
342 #else
343 		GTK_STOCK_OPEN,
344 #endif
345 		GTK_RESPONSE_ACCEPT,
346 		NULL);
347 #ifdef WITH_GTK_NONX11
348 	gtk_window_set_position( GTK_WINDOW(gtk_dialog), GTK_WIN_POS_CENTER);
349 #endif
350 
351 	gtk_file_chooser_set_local_only( GTK_FILE_CHOOSER (gtk_dialog), TRUE);
352 	if (open)
353 		gtk_file_chooser_set_select_multiple( GTK_FILE_CHOOSER (gtk_dialog), gtk_select_multiple);
354 
355 #if GTK_MAJOR_VERSION > 2 || (GTK_MAJOR_VERSION == 2 && GTK_MINOR_VERSION >= 8)
356 	gtk_file_chooser_set_do_overwrite_confirmation( GTK_FILE_CHOOSER (gtk_dialog), gtk_overwrite_prompt);
357 	gtk_file_chooser_set_show_hidden( GTK_FILE_CHOOSER (gtk_dialog), gtk_show_hidden_files);
358 #endif
359 	if ( gtk_current_folder_ptr)
360 		gtk_file_chooser_set_current_folder( GTK_FILE_CHOOSER (gtk_dialog), gtk_current_folder_ptr);
361 
362 	if ( gtk_filters) {
363 		int i;
364 		for ( i = 0; i < gtk_filters-> count; i++) {
365 			gtk_file_chooser_add_filter(
366 				GTK_FILE_CHOOSER (gtk_dialog),
367 				GTK_FILE_FILTER (gtk_filters-> items[i])
368 			);
369 			if ( i == gtk_filter_index)
370 				gtk_file_chooser_set_filter(
371 					GTK_FILE_CHOOSER (gtk_dialog),
372 					GTK_FILE_FILTER (gtk_filters-> items[i])
373 				);
374 		}
375 	}
376 
377 	/* lock prima interactions */
378 	bzero( &message_dlg, sizeof(message_dlg));
379 	storage = &guts. message_boxes;
380 	while ( *storage) storage = &((*storage)-> next);
381 	*storage = &message_dlg;
382 
383 	g_idle_add( do_events, &stage);
384 
385 	if (gtk_dialog_run (GTK_DIALOG (gtk_dialog)) == GTK_RESPONSE_ACCEPT) {
386 
387 		/* files */
388 		if ( gtk_select_multiple) {
389 			int size;
390 			char * ptr;
391 			GSList *names, *iter;
392 
393 			names = gtk_file_chooser_get_filenames ( GTK_FILE_CHOOSER (gtk_dialog));
394 
395 			/* count total length with escaped spaces and backslashes */
396 			size = 1;
397 			iter = names;
398 			while ( iter) {
399 				char * c = (char*) iter-> data;
400 				while ( *c) {
401 					if ( *c == ' ' || *c == '\\')
402 						size++;
403 					size++;
404 					c++;
405 				}
406 				size++;
407 				iter = iter-> next;
408 			}
409 
410 			if (( result = ptr = malloc( size))) {
411 				/* copy and encode */
412 				iter = names;
413 				while ( iter) {
414 					char * c = (char*) iter-> data;
415 					while ( *c) {
416 						if ( *c == ' ' || *c == '\\')
417 							*(ptr++) = '\\';
418 						*(ptr++) = *c;
419 						c++;
420 					}
421 					iter = iter-> next;
422 					*(ptr++) = ' ';
423 				}
424 				*(ptr - 1) = 0;
425 			} else {
426 					warn("gtk_openfile: cannot allocate %d bytes of memory", size);
427 			}
428 
429 			/* free */
430 			iter = names;
431 			while ( iter) {
432 				g_free( iter-> data);
433 				iter = iter-> next;
434 			}
435 			g_slist_free( names);
436 		} else {
437 			char * filename = gtk_file_chooser_get_filename ( GTK_FILE_CHOOSER (gtk_dialog));
438 			result = duplicate_string( filename);
439 			g_free (filename);
440 		}
441 
442 		/* directory */
443 		{
444 			char * d = gtk_file_chooser_get_current_folder( GTK_FILE_CHOOSER (gtk_dialog));
445 			if ( d) {
446 				strncpy( gtk_current_folder, d, MAXPATHLEN);
447 				gtk_current_folder_ptr = gtk_current_folder;
448 				g_free( d);
449 			} else {
450 				gtk_current_folder_ptr = NULL;
451 			}
452 		}
453 
454 		/* filter index */
455 		gtk_filter_index = 0;
456 		if ( gtk_filters) {
457 			int i;
458 			Handle f = ( Handle) gtk_file_chooser_get_filter( GTK_FILE_CHOOSER (gtk_dialog));
459 			for ( i = 0; i < gtk_filters-> count; i++)
460 				if ( gtk_filters-> items[i] == f) {
461 					gtk_filter_index = i;
462 					break;
463 				}
464 
465 		}
466 	}
467 
468 	if ( gtk_filters) {
469 		plist_destroy( gtk_filters);
470 		gtk_filters = NULL;
471 	}
472 
473 	*storage = message_dlg. next; /* unlock */
474 
475 	gtk_widget_destroy (gtk_dialog);
476 	gtk_dialog = NULL;
477 
478 	while ( gtk_events_pending()) gtk_main_iteration();
479 
480 	XSync(DISP, false);
481 	XCHECKPOINT;
482 	XSetErrorHandler(guts.main_error_handler);
483 
484 	return result;
485 }
486 
487 char *
prima_gtk_openfile(char * params)488 prima_gtk_openfile( char * params)
489 {
490 	if ( !DISP)
491 		return NULL;
492 	if( !prima_gtk_init())
493 		return NULL;
494 
495 	if ( strncmp( params, "directory", 9) == 0) {
496 		params += 9;
497 		if ( *params == '=') {
498 			params++;
499 			if ( *params == 0) {
500 				gtk_current_folder_ptr = NULL;
501 			} else {
502 				gtk_current_folder_ptr = gtk_current_folder;
503 				strncpy( gtk_current_folder, params, MAXPATHLEN);
504 				gtk_current_folder[MAXPATHLEN] = 0;
505 			}
506 		} else
507 			return duplicate_string( gtk_current_folder_ptr);
508 	} else if ( strncmp( params, "filters=", 8) == 0) {
509 		params += 8;
510 		if ( gtk_filters) {
511 			int i;
512 			for ( i = 0; i < gtk_filters-> count; i++)
513 				g_object_unref(( GObject*) gtk_filters-> items[i]);
514 			plist_destroy( gtk_filters);
515 			gtk_filters = NULL;
516 		}
517 		if ( *params != 0) {
518 			gtk_filters = plist_create(8, 8);
519 
520 			/* copy \0\0-terminated string */
521 			while ( *params) {
522 				char * pattern;
523 				GtkFileFilter * f = gtk_file_filter_new();
524 
525 				/* name */
526 				gtk_file_filter_set_name( f, params);
527 				while ( *params) params++;
528 				params++;
529 
530 				/* semicolon-separated shell globs */
531 				pattern = ( char *) params;
532 				while ( *params) {
533 					if ( *params == ';') {
534 						*params = 0;
535 						gtk_file_filter_add_pattern( f, pattern);
536 						pattern = params + 1;
537 					}
538 					params++;
539 				}
540 				gtk_file_filter_add_pattern( f, pattern);
541 
542 				list_add( gtk_filters, (Handle) f);
543 
544 				params++;
545 			}
546 		}
547 	} else if ( strncmp( params, "filterindex", 11) == 0) {
548 		params += 11;
549 		if ( *params == '=') {
550 			int fi = 0;
551 			sscanf( params + 1, "%d", &fi);
552 			gtk_filter_index = fi;
553 		} else {
554 			char buf[25];
555 			sprintf( buf, "%d", gtk_filter_index);
556 			return duplicate_string( buf);
557 		}
558 	} else if ( strncmp( params, "multi_select=", 13) == 0) {
559 		params += 13;
560 		gtk_select_multiple = (*params != '0');
561 	} else if ( strncmp( params, "overwrite_prompt=", 17) == 0) {
562 		params += 17;
563 		gtk_overwrite_prompt = (*params != '0');
564 	} else if (
565 		( strncmp( params, "open", 4) == 0) ||
566 		( strncmp( params, "save", 4) == 0)
567 	) {
568 		return gtk_openfile( strncmp( params, "open", 4) == 0);
569 	} else if ( strncmp( params, "show_hidden=", 12) == 0) {
570 		params += 12;
571 		gtk_show_hidden_files = (*params != '0');
572 	} else if ( strncmp( params, "title=", 6) == 0) {
573 		params += 6;
574 		if ( *params == 0) {
575 			gtk_dialog_title_ptr = NULL;
576 		} else {
577 			gtk_dialog_title_ptr = gtk_dialog_title;
578 			strncpy( gtk_dialog_title, params, 255);
579 			gtk_dialog_title[255] = 0;
580 		}
581 	} else {
582 		warn("gtk.OpenFile: Unknown function %s", params);
583 	}
584 
585 	return NULL;
586 }
587 
588 /* Thanks to Cosimo Cecchi @ gnome-screenshot for the code below */
589 Bool
prima_gtk_application_get_bitmap(Handle self,Handle image,int x,int y,int xLen,int yLen)590 prima_gtk_application_get_bitmap( Handle self, Handle image, int x, int y, int xLen, int yLen)
591 {
592 #if defined(SAFE_DBUS) && (GLIB_MAJOR_VERSION > 2 || (GLIB_MAJOR_VERSION == 2 && GLIB_MINOR_VERSION >= 34))
593 	DEFXX;
594 	int              i, found_png;
595 	PList            codecs;
596 	GVariant        *params, *results;
597 	GError   *       error = NULL;
598 	GDBusConnection *conn;
599 	char             filename[256];
600 
601 	/* do we have png? it seems gnome only saves scheenshots as pngs */
602 	codecs = plist_create( 16, 16);
603 	apc_img_codecs( codecs);
604 	found_png = false;
605 	for ( i = 0; i < codecs-> count; i++) {
606 		PImgCodec c = ( PImgCodec ) codecs-> items[ i];
607 		if ( strcmp( c-> info-> fileShortType, "PNG" ) == 0 ) {
608 			found_png = true;
609 			break;
610 		}
611 	}
612 	plist_destroy( codecs);
613 	if ( !found_png ) {
614 		Mdebug("PNG decoder not found\n");
615 		return false;
616 	}
617 
618 	/* execute gnome shell screenshot */
619 	snprintf(filename, 256, "/tmp/%d-sc.png", (int) getpid());
620 	params = g_variant_new("(iiiibs)",
621 		x, XX->size.y - y - yLen, xLen, yLen,
622 		0, filename);
623 
624 	if (!( conn = g_application_get_dbus_connection (g_application_get_default ()))) {
625 		Mdebug("cannot get dbus connection\n");
626 		return false;
627 	}
628 
629 	results = g_dbus_connection_call_sync (conn,
630 		"org.gnome.Shell.Screenshot",
631 		"/org/gnome/Shell/Screenshot",
632 		"org.gnome.Shell.Screenshot",
633 		"ScreenshotArea",
634 		params,
635 		NULL,
636 		G_DBUS_CALL_FLAGS_NONE,
637 		-1,
638 		NULL,
639 		&error
640 	);
641 	if ( results )
642 		g_variant_unref( results );
643 	if (error != NULL) {
644 		Mdebug("cannot get gnome shell screenshot\n");
645       		g_error_free (error);
646 		return false;
647 	}
648 
649 	/* load */
650 	codecs = apc_img_load( image, filename, false, NULL, NULL, NULL);
651 	unlink( filename );
652 	if ( !codecs ) {
653 		Mdebug("error loading png back\n");
654 		return false;
655 	}
656 	plist_destroy(codecs);
657 
658 	return true;
659 #else
660 	return false;
661 #endif
662 }
663 
664 #endif
665