1 /* GIMP - The GNU Image Manipulation Program
2 * Copyright (C) 1995 Spencer Kimball and Peter Mattis
3 *
4 * This program is free software: you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation; either version 3 of the License, or
7 * (at your option) any later version.
8 *
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
13 *
14 * You should have received a copy of the GNU General Public License
15 * along with this program. If not, see <https://www.gnu.org/licenses/>.
16 */
17
18 #include "config.h"
19
20 #include <errno.h>
21 #include <stdlib.h>
22 #include <string.h>
23 #include <locale.h>
24
25 #ifdef HAVE__NL_MEASUREMENT_MEASUREMENT
26 #include <langinfo.h>
27 #endif
28
29 #ifdef HAVE_UNISTD_H
30 #include <unistd.h>
31 #endif
32
33 #include <glib.h>
34
35 #ifdef G_OS_WIN32
36 #define _WIN32_WINNT 0x0500
37 #include <windows.h>
38 #include <process.h>
39 #endif
40
41 #if defined(G_OS_UNIX) && defined(HAVE_EXECINFO_H)
42 /* For get_backtrace() */
43 #include <stdlib.h>
44 #include <string.h>
45 #include <execinfo.h>
46 #endif
47
48 #include <cairo.h>
49 #include <gegl.h>
50 #include <gobject/gvaluecollector.h>
51 #include <gdk-pixbuf/gdk-pixbuf.h>
52
53 #include "libgimpbase/gimpbase.h"
54 #include "libgimpmath/gimpmath.h"
55 #include "libgimpcolor/gimpcolor.h"
56
57 #include "core-types.h"
58
59 #include "gimp.h"
60 #include "gimp-utils.h"
61 #include "gimpasync.h"
62 #include "gimpcontext.h"
63 #include "gimperror.h"
64
65 #include "gimp-intl.h"
66
67
68 #define MAX_FUNC 100
69
70
71 gint
gimp_get_pid(void)72 gimp_get_pid (void)
73 {
74 return (gint) getpid ();
75 }
76
77 guint64
gimp_get_physical_memory_size(void)78 gimp_get_physical_memory_size (void)
79 {
80 #ifdef G_OS_UNIX
81 #if defined(HAVE_UNISTD_H) && defined(_SC_PHYS_PAGES) && defined (_SC_PAGE_SIZE)
82 return (guint64) sysconf (_SC_PHYS_PAGES) * sysconf (_SC_PAGE_SIZE);
83 #endif
84 #endif
85
86 #ifdef G_OS_WIN32
87 # if defined(_MSC_VER) && (_MSC_VER <= 1200)
88 MEMORYSTATUS memory_status;
89 memory_status.dwLength = sizeof (memory_status);
90
91 GlobalMemoryStatus (&memory_status);
92 return memory_status.dwTotalPhys;
93 # else
94 /* requires w2k and newer SDK than provided with msvc6 */
95 MEMORYSTATUSEX memory_status;
96
97 memory_status.dwLength = sizeof (memory_status);
98
99 if (GlobalMemoryStatusEx (&memory_status))
100 return memory_status.ullTotalPhys;
101 # endif
102 #endif
103
104 return 0;
105 }
106
107 /*
108 * basically copied from gtk_get_default_language()
109 */
110 gchar *
gimp_get_default_language(const gchar * category)111 gimp_get_default_language (const gchar *category)
112 {
113 gchar *lang;
114 gchar *p;
115 gint cat = LC_CTYPE;
116
117 if (! category)
118 category = "LC_CTYPE";
119
120 #ifdef G_OS_WIN32
121
122 p = getenv ("LC_ALL");
123 if (p != NULL)
124 lang = g_strdup (p);
125 else
126 {
127 p = getenv ("LANG");
128 if (p != NULL)
129 lang = g_strdup (p);
130 else
131 {
132 p = getenv (category);
133 if (p != NULL)
134 lang = g_strdup (p);
135 else
136 lang = g_win32_getlocale ();
137 }
138 }
139
140 #else
141
142 if (strcmp (category, "LC_CTYPE") == 0)
143 cat = LC_CTYPE;
144 else if (strcmp (category, "LC_MESSAGES") == 0)
145 cat = LC_MESSAGES;
146 else
147 g_warning ("unsupported category used with gimp_get_default_language()");
148
149 lang = g_strdup (setlocale (cat, NULL));
150
151 #endif
152
153 p = strchr (lang, '.');
154 if (p)
155 *p = '\0';
156 p = strchr (lang, '@');
157 if (p)
158 *p = '\0';
159
160 return lang;
161 }
162
163 GimpUnit
gimp_get_default_unit(void)164 gimp_get_default_unit (void)
165 {
166 #if defined (HAVE__NL_MEASUREMENT_MEASUREMENT)
167 const gchar *measurement = nl_langinfo (_NL_MEASUREMENT_MEASUREMENT);
168
169 switch (*((guchar *) measurement))
170 {
171 case 1: /* metric */
172 return GIMP_UNIT_MM;
173
174 case 2: /* imperial */
175 return GIMP_UNIT_INCH;
176 }
177
178 #elif defined (G_OS_WIN32)
179 DWORD measurement;
180 int ret;
181
182 ret = GetLocaleInfo(LOCALE_USER_DEFAULT,
183 LOCALE_IMEASURE | LOCALE_RETURN_NUMBER,
184 (LPTSTR)&measurement,
185 sizeof(measurement) / sizeof(TCHAR) );
186
187 if (ret != 0) /* GetLocaleInfo succeeded */
188 {
189 switch ((guint) measurement)
190 {
191 case 0: /* metric */
192 return GIMP_UNIT_MM;
193
194 case 1: /* imperial */
195 return GIMP_UNIT_INCH;
196 }
197 }
198 #endif
199
200 return GIMP_UNIT_MM;
201 }
202
203 gchar **
gimp_properties_append(GType object_type,gint * n_properties,gchar ** names,GValue ** values,...)204 gimp_properties_append (GType object_type,
205 gint *n_properties,
206 gchar **names,
207 GValue **values,
208 ...)
209 {
210 va_list args;
211
212 g_return_val_if_fail (g_type_is_a (object_type, G_TYPE_OBJECT), NULL);
213 g_return_val_if_fail (n_properties != NULL, NULL);
214 g_return_val_if_fail (names != NULL || *n_properties == 0, NULL);
215 g_return_val_if_fail (values != NULL || *n_properties == 0, NULL);
216
217 va_start (args, values);
218 names = gimp_properties_append_valist (object_type, n_properties,
219 names, values, args);
220 va_end (args);
221
222 return names;
223 }
224
225 gchar **
gimp_properties_append_valist(GType object_type,gint * n_properties,gchar ** names,GValue ** values,va_list args)226 gimp_properties_append_valist (GType object_type,
227 gint *n_properties,
228 gchar **names,
229 GValue **values,
230 va_list args)
231 {
232 GObjectClass *object_class;
233 gchar *param_name;
234
235 g_return_val_if_fail (g_type_is_a (object_type, G_TYPE_OBJECT), NULL);
236 g_return_val_if_fail (n_properties != NULL, NULL);
237 g_return_val_if_fail (names != NULL || *n_properties == 0, NULL);
238 g_return_val_if_fail (values != NULL || *n_properties == 0, NULL);
239
240 object_class = g_type_class_ref (object_type);
241
242 param_name = va_arg (args, gchar *);
243
244 while (param_name)
245 {
246 GValue *value;
247 gchar *error = NULL;
248 GParamSpec *pspec = g_object_class_find_property (object_class,
249 param_name);
250
251 if (! pspec)
252 {
253 g_warning ("%s: object class `%s' has no property named `%s'",
254 G_STRFUNC, g_type_name (object_type), param_name);
255 break;
256 }
257
258 names = g_renew (gchar *, names, *n_properties + 1);
259 *values = g_renew (GValue, *values, *n_properties + 1);
260
261 value = &((*values)[*n_properties]);
262
263 names[*n_properties] = g_strdup (param_name);
264 value->g_type = 0;
265
266 g_value_init (value, G_PARAM_SPEC_VALUE_TYPE (pspec));
267
268 G_VALUE_COLLECT (value, args, 0, &error);
269
270 if (error)
271 {
272 g_warning ("%s: %s", G_STRFUNC, error);
273 g_free (error);
274 g_free (names[*n_properties]);
275 g_value_unset (value);
276 break;
277 }
278
279 *n_properties = *n_properties + 1;
280
281 param_name = va_arg (args, gchar *);
282 }
283
284 g_type_class_unref (object_class);
285
286 return names;
287 }
288
289 void
gimp_properties_free(gint n_properties,gchar ** names,GValue * values)290 gimp_properties_free (gint n_properties,
291 gchar **names,
292 GValue *values)
293 {
294 g_return_if_fail (names != NULL || n_properties == 0);
295 g_return_if_fail (values != NULL || n_properties == 0);
296
297 if (names && values)
298 {
299 gint i;
300
301 for (i = 0; i < n_properties; i++)
302 {
303 g_free (names[i]);
304 g_value_unset (&values[i]);
305 }
306
307 g_free (names);
308 g_free (values);
309 }
310 }
311
312 /* markup unescape code stolen and adapted from gmarkup.c
313 */
314 static gchar *
char_str(gunichar c,gchar * buf)315 char_str (gunichar c,
316 gchar *buf)
317 {
318 memset (buf, 0, 8);
319 g_unichar_to_utf8 (c, buf);
320 return buf;
321 }
322
323 static gboolean
unescape_gstring(GString * string)324 unescape_gstring (GString *string)
325 {
326 const gchar *from;
327 gchar *to;
328
329 /*
330 * Meeks' theorum: unescaping can only shrink text.
331 * for < etc. this is obvious, for  more
332 * thought is required, but this is patently so.
333 */
334 for (from = to = string->str; *from != '\0'; from++, to++)
335 {
336 *to = *from;
337
338 if (*to == '\r')
339 {
340 *to = '\n';
341 if (from[1] == '\n')
342 from++;
343 }
344 if (*from == '&')
345 {
346 from++;
347 if (*from == '#')
348 {
349 gboolean is_hex = FALSE;
350 gulong l;
351 gchar *end = NULL;
352
353 from++;
354
355 if (*from == 'x')
356 {
357 is_hex = TRUE;
358 from++;
359 }
360
361 /* digit is between start and p */
362 errno = 0;
363 if (is_hex)
364 l = strtoul (from, &end, 16);
365 else
366 l = strtoul (from, &end, 10);
367
368 if (end == from || errno != 0)
369 {
370 return FALSE;
371 }
372 else if (*end != ';')
373 {
374 return FALSE;
375 }
376 else
377 {
378 /* characters XML 1.1 permits */
379 if ((0 < l && l <= 0xD7FF) ||
380 (0xE000 <= l && l <= 0xFFFD) ||
381 (0x10000 <= l && l <= 0x10FFFF))
382 {
383 gchar buf[8];
384 char_str (l, buf);
385 strcpy (to, buf);
386 to += strlen (buf) - 1;
387 from = end;
388 }
389 else
390 {
391 return FALSE;
392 }
393 }
394 }
395
396 else if (strncmp (from, "lt;", 3) == 0)
397 {
398 *to = '<';
399 from += 2;
400 }
401 else if (strncmp (from, "gt;", 3) == 0)
402 {
403 *to = '>';
404 from += 2;
405 }
406 else if (strncmp (from, "amp;", 4) == 0)
407 {
408 *to = '&';
409 from += 3;
410 }
411 else if (strncmp (from, "quot;", 5) == 0)
412 {
413 *to = '"';
414 from += 4;
415 }
416 else if (strncmp (from, "apos;", 5) == 0)
417 {
418 *to = '\'';
419 from += 4;
420 }
421 else
422 {
423 return FALSE;
424 }
425 }
426 }
427
428 gimp_assert (to - string->str <= string->len);
429 if (to - string->str != string->len)
430 g_string_truncate (string, to - string->str);
431
432 return TRUE;
433 }
434
435 gchar *
gimp_markup_extract_text(const gchar * markup)436 gimp_markup_extract_text (const gchar *markup)
437 {
438 GString *string;
439 const gchar *p;
440 gboolean in_tag = FALSE;
441
442 if (! markup)
443 return NULL;
444
445 string = g_string_new (NULL);
446
447 for (p = markup; *p; p++)
448 {
449 if (in_tag)
450 {
451 if (*p == '>')
452 in_tag = FALSE;
453 }
454 else
455 {
456 if (*p == '<')
457 in_tag = TRUE;
458 else
459 g_string_append_c (string, *p);
460 }
461 }
462
463 unescape_gstring (string);
464
465 return g_string_free (string, FALSE);
466 }
467
468 /**
469 * gimp_enum_get_value_name:
470 * @enum_type: Enum type
471 * @value: Enum value
472 *
473 * Returns the value name for a given value of a given enum
474 * type. Useful to have inline in GIMP_LOG() messages for example.
475 *
476 * Returns: The value name.
477 **/
478 const gchar *
gimp_enum_get_value_name(GType enum_type,gint value)479 gimp_enum_get_value_name (GType enum_type,
480 gint value)
481 {
482 const gchar *value_name = NULL;
483
484 gimp_enum_get_value (enum_type,
485 value,
486 &value_name,
487 NULL /*value_nick*/,
488 NULL /*value_desc*/,
489 NULL /*value_help*/);
490
491 return value_name;
492 }
493
494 gboolean
gimp_get_fill_params(GimpContext * context,GimpFillType fill_type,GimpRGB * color,GimpPattern ** pattern,GError ** error)495 gimp_get_fill_params (GimpContext *context,
496 GimpFillType fill_type,
497 GimpRGB *color,
498 GimpPattern **pattern,
499 GError **error)
500
501 {
502 g_return_val_if_fail (GIMP_IS_CONTEXT (context), FALSE);
503 g_return_val_if_fail (color != NULL, FALSE);
504 g_return_val_if_fail (pattern != NULL, FALSE);
505 g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
506
507 *pattern = NULL;
508
509 switch (fill_type)
510 {
511 case GIMP_FILL_FOREGROUND:
512 gimp_context_get_foreground (context, color);
513 break;
514
515 case GIMP_FILL_BACKGROUND:
516 gimp_context_get_background (context, color);
517 break;
518
519 case GIMP_FILL_WHITE:
520 gimp_rgba_set (color, 1.0, 1.0, 1.0, GIMP_OPACITY_OPAQUE);
521 break;
522
523 case GIMP_FILL_TRANSPARENT:
524 gimp_rgba_set (color, 0.0, 0.0, 0.0, GIMP_OPACITY_TRANSPARENT);
525 break;
526
527 case GIMP_FILL_PATTERN:
528 *pattern = gimp_context_get_pattern (context);
529
530 if (! *pattern)
531 {
532 g_set_error_literal (error, GIMP_ERROR, GIMP_FAILED,
533 _("No patterns available for this operation."));
534
535 /* fall back to BG fill */
536 gimp_context_get_background (context, color);
537
538 return FALSE;
539 }
540 break;
541
542 default:
543 g_warning ("%s: invalid fill_type %d", G_STRFUNC, fill_type);
544 return FALSE;
545 }
546
547 return TRUE;
548 }
549
550 /**
551 * gimp_constrain_line:
552 * @start_x:
553 * @start_y:
554 * @end_x:
555 * @end_y:
556 * @n_snap_lines: Number evenly disributed lines to snap to.
557 * @offset_angle: The angle by which to offset the lines, in degrees.
558 * @xres: The horizontal resolution.
559 * @yres: The vertical resolution.
560 *
561 * Projects a line onto the specified subset of evenly radially
562 * distributed lines. @n_lines of 2 makes the line snap horizontally
563 * or vertically. @n_lines of 4 snaps on 45 degree steps. @n_lines of
564 * 12 on 15 degree steps. etc.
565 **/
566 void
gimp_constrain_line(gdouble start_x,gdouble start_y,gdouble * end_x,gdouble * end_y,gint n_snap_lines,gdouble offset_angle,gdouble xres,gdouble yres)567 gimp_constrain_line (gdouble start_x,
568 gdouble start_y,
569 gdouble *end_x,
570 gdouble *end_y,
571 gint n_snap_lines,
572 gdouble offset_angle,
573 gdouble xres,
574 gdouble yres)
575 {
576 GimpVector2 diff;
577 GimpVector2 dir;
578 gdouble angle;
579
580 offset_angle *= G_PI / 180.0;
581
582 diff.x = (*end_x - start_x) / xres;
583 diff.y = (*end_y - start_y) / yres;
584
585 angle = (atan2 (diff.y, diff.x) - offset_angle) * n_snap_lines / G_PI;
586 angle = RINT (angle) * G_PI / n_snap_lines + offset_angle;
587
588 dir.x = cos (angle);
589 dir.y = sin (angle);
590
591 gimp_vector2_mul (&dir, gimp_vector2_inner_product (&dir, &diff));
592
593 *end_x = start_x + dir.x * xres;
594 *end_y = start_y + dir.y * yres;
595 }
596
597 gint
gimp_file_compare(GFile * file1,GFile * file2)598 gimp_file_compare (GFile *file1,
599 GFile *file2)
600 {
601 if (g_file_equal (file1, file2))
602 {
603 return 0;
604 }
605 else
606 {
607 gchar *uri1 = g_file_get_uri (file1);
608 gchar *uri2 = g_file_get_uri (file2);
609 gint result = strcmp (uri1, uri2);
610
611 g_free (uri1);
612 g_free (uri2);
613
614 return result;
615 }
616 }
617
618 static inline gboolean
is_script(const gchar * filename)619 is_script (const gchar *filename)
620 {
621 #ifdef G_OS_WIN32
622 /* On Windows there is no concept like the Unix executable flag.
623 * There is a weak emulation provided by the MS C Runtime using file
624 * extensions (com, exe, cmd, bat). This needs to be extended to
625 * treat scripts (Python, Perl, ...) as executables, too. We use the
626 * PATHEXT variable, which is also used by cmd.exe.
627 */
628 static gchar **exts = NULL;
629
630 const gchar *ext = strrchr (filename, '.');
631 const gchar *pathext;
632 gint i;
633
634 if (exts == NULL)
635 {
636 pathext = g_getenv ("PATHEXT");
637 if (pathext != NULL)
638 {
639 exts = g_strsplit (pathext, G_SEARCHPATH_SEPARATOR_S, 100);
640 }
641 else
642 {
643 exts = g_new (gchar *, 1);
644 exts[0] = NULL;
645 }
646 }
647
648 for (i = 0; exts[i]; i++)
649 {
650 if (g_ascii_strcasecmp (ext, exts[i]) == 0)
651 return TRUE;
652 }
653 #endif /* G_OS_WIN32 */
654
655 return FALSE;
656 }
657
658 gboolean
gimp_file_is_executable(GFile * file)659 gimp_file_is_executable (GFile *file)
660 {
661 GFileInfo *info;
662 gboolean executable = FALSE;
663
664 g_return_val_if_fail (G_IS_FILE (file), FALSE);
665
666 info = g_file_query_info (file,
667 G_FILE_ATTRIBUTE_STANDARD_NAME ","
668 G_FILE_ATTRIBUTE_STANDARD_TYPE ","
669 G_FILE_ATTRIBUTE_ACCESS_CAN_EXECUTE ",",
670 G_FILE_QUERY_INFO_NONE,
671 NULL, NULL);
672
673 if (info)
674 {
675 GFileType file_type = g_file_info_get_file_type (info);
676 const gchar *filename = g_file_info_get_name (info);
677
678 if (file_type == G_FILE_TYPE_REGULAR &&
679 (g_file_info_get_attribute_boolean (info,
680 G_FILE_ATTRIBUTE_ACCESS_CAN_EXECUTE) ||
681 is_script (filename)))
682 {
683 executable = TRUE;
684 }
685
686 g_object_unref (info);
687 }
688
689 return executable;
690 }
691
692 /**
693 * gimp_file_get_extension:
694 * @file: A #GFile
695 *
696 * Returns @file's extension (including the .), or NULL if there is no
697 * extension. Note that this function handles compressed files too,
698 * e.g. for "file.png.gz" it will return ".png.gz".
699 *
700 * Returns: The @file's extension. Free with g_free() when no longer needed.
701 **/
702 gchar *
gimp_file_get_extension(GFile * file)703 gimp_file_get_extension (GFile *file)
704 {
705 gchar *uri;
706 gint uri_len;
707 gchar *ext = NULL;
708 gint search_len;
709
710 g_return_val_if_fail (G_IS_FILE (file), NULL);
711
712 uri = g_file_get_uri (file);
713 uri_len = strlen (uri);
714
715 if (g_str_has_suffix (uri, ".gz"))
716 search_len = uri_len - 3;
717 else if (g_str_has_suffix (uri, ".bz2"))
718 search_len = uri_len - 4;
719 else if (g_str_has_suffix (uri, ".xz"))
720 search_len = uri_len - 3;
721 else
722 search_len = uri_len;
723
724 ext = g_strrstr_len (uri, search_len, ".");
725
726 if (ext)
727 ext = g_strdup (ext);
728
729 g_free (uri);
730
731 return ext;
732 }
733
734 GFile *
gimp_file_with_new_extension(GFile * file,GFile * ext_file)735 gimp_file_with_new_extension (GFile *file,
736 GFile *ext_file)
737 {
738 gchar *uri;
739 gchar *file_ext;
740 gint file_ext_len = 0;
741 gchar *ext_file_ext = NULL;
742 gchar *uri_without_ext;
743 gchar *new_uri;
744 GFile *ret;
745
746 g_return_val_if_fail (G_IS_FILE (file), NULL);
747 g_return_val_if_fail (ext_file == NULL || G_IS_FILE (ext_file), NULL);
748
749 uri = g_file_get_uri (file);
750 file_ext = gimp_file_get_extension (file);
751
752 if (file_ext)
753 {
754 file_ext_len = strlen (file_ext);
755 g_free (file_ext);
756 }
757
758 if (ext_file)
759 ext_file_ext = gimp_file_get_extension (ext_file);
760
761 uri_without_ext = g_strndup (uri, strlen (uri) - file_ext_len);
762
763 g_free (uri);
764
765 new_uri = g_strconcat (uri_without_ext, ext_file_ext, NULL);
766
767 ret = g_file_new_for_uri (new_uri);
768
769 g_free (ext_file_ext);
770 g_free (uri_without_ext);
771 g_free (new_uri);
772
773 return ret;
774 }
775
776 gchar *
gimp_data_input_stream_read_line_always(GDataInputStream * stream,gsize * length,GCancellable * cancellable,GError ** error)777 gimp_data_input_stream_read_line_always (GDataInputStream *stream,
778 gsize *length,
779 GCancellable *cancellable,
780 GError **error)
781 {
782 GError *temp_error = NULL;
783 gchar *result;
784
785 g_return_val_if_fail (G_IS_DATA_INPUT_STREAM (stream), NULL);
786 g_return_val_if_fail (error == NULL || *error == NULL, NULL);
787
788 if (! error)
789 error = &temp_error;
790
791 result = g_data_input_stream_read_line (stream, length, cancellable, error);
792
793 if (! result && ! *error)
794 {
795 result = g_strdup ("");
796
797 if (length) *length = 0;
798 }
799
800 g_clear_error (&temp_error);
801
802 return result;
803 }
804
805 gboolean
gimp_ascii_strtoi(const gchar * nptr,gchar ** endptr,gint base,gint * result)806 gimp_ascii_strtoi (const gchar *nptr,
807 gchar **endptr,
808 gint base,
809 gint *result)
810 {
811 gchar *temp_endptr;
812 gint64 temp_result;
813
814 g_return_val_if_fail (nptr != NULL, FALSE);
815 g_return_val_if_fail (base == 0 || (base >= 2 && base <= 36), FALSE);
816
817 if (! endptr)
818 endptr = &temp_endptr;
819
820 temp_result = g_ascii_strtoll (nptr, endptr, base);
821
822 if (*endptr == nptr || errno == ERANGE ||
823 temp_result < G_MININT || temp_result > G_MAXINT)
824 {
825 errno = 0;
826
827 return FALSE;
828 }
829
830 if (result) *result = temp_result;
831
832 return TRUE;
833 }
834
835 gboolean
gimp_ascii_strtod(const gchar * nptr,gchar ** endptr,gdouble * result)836 gimp_ascii_strtod (const gchar *nptr,
837 gchar **endptr,
838 gdouble *result)
839 {
840 gchar *temp_endptr;
841 gdouble temp_result;
842
843 g_return_val_if_fail (nptr != NULL, FALSE);
844
845 if (! endptr)
846 endptr = &temp_endptr;
847
848 temp_result = g_ascii_strtod (nptr, endptr);
849
850 if (*endptr == nptr || errno == ERANGE)
851 {
852 errno = 0;
853
854 return FALSE;
855 }
856
857 if (result) *result = temp_result;
858
859 return TRUE;
860 }
861
862 gint
gimp_g_list_compare(GList * list1,GList * list2)863 gimp_g_list_compare (GList *list1,
864 GList *list2)
865 {
866 while (list1 && list2)
867 {
868 if (list1->data < list2->data)
869 return -1;
870 else if (list1->data > list2->data)
871 return +1;
872
873 list1 = g_list_next (list1);
874 list2 = g_list_next (list2);
875 }
876
877 if (! list1)
878 return -1;
879 else if (! list2)
880 return +1;
881
882 return 0;
883 }
884
885 typedef struct
886 {
887 gint ref_count;
888
889 GimpAsync *async;
890 gint idle_id;
891
892 GimpRunAsyncFunc func;
893 gpointer user_data;
894 GDestroyNotify user_data_destroy_func;
895 } GimpIdleRunAsyncData;
896
897 static GimpIdleRunAsyncData *
gimp_idle_run_async_data_new(void)898 gimp_idle_run_async_data_new (void)
899 {
900 GimpIdleRunAsyncData *data;
901
902 data = g_slice_new0 (GimpIdleRunAsyncData);
903
904 data->ref_count = 1;
905
906 return data;
907 }
908
909 static void
gimp_idle_run_async_data_inc_ref(GimpIdleRunAsyncData * data)910 gimp_idle_run_async_data_inc_ref (GimpIdleRunAsyncData *data)
911 {
912 data->ref_count++;
913 }
914
915 static void
gimp_idle_run_async_data_dec_ref(GimpIdleRunAsyncData * data)916 gimp_idle_run_async_data_dec_ref (GimpIdleRunAsyncData *data)
917 {
918 data->ref_count--;
919
920 if (data->ref_count == 0)
921 {
922 g_signal_handlers_disconnect_by_data (data->async, data);
923
924 if (! gimp_async_is_stopped (data->async))
925 gimp_async_abort (data->async);
926
927 g_object_unref (data->async);
928
929 if (data->user_data && data->user_data_destroy_func)
930 data->user_data_destroy_func (data->user_data);
931
932 g_slice_free (GimpIdleRunAsyncData, data);
933 }
934 }
935
936 static void
gimp_idle_run_async_cancel(GimpAsync * async,GimpIdleRunAsyncData * data)937 gimp_idle_run_async_cancel (GimpAsync *async,
938 GimpIdleRunAsyncData *data)
939 {
940 gimp_idle_run_async_data_inc_ref (data);
941
942 if (data->idle_id)
943 {
944 g_source_remove (data->idle_id);
945
946 data->idle_id = 0;
947 }
948
949 gimp_idle_run_async_data_dec_ref (data);
950 }
951
952 static void
gimp_idle_run_async_waiting(GimpAsync * async,GimpIdleRunAsyncData * data)953 gimp_idle_run_async_waiting (GimpAsync *async,
954 GimpIdleRunAsyncData *data)
955 {
956 gimp_idle_run_async_data_inc_ref (data);
957
958 if (data->idle_id)
959 {
960 g_source_remove (data->idle_id);
961
962 data->idle_id = 0;
963 }
964
965 g_signal_handlers_block_by_func (data->async,
966 gimp_idle_run_async_cancel,
967 data);
968
969 while (! gimp_async_is_stopped (data->async))
970 data->func (data->async, data->user_data);
971
972 g_signal_handlers_unblock_by_func (data->async,
973 gimp_idle_run_async_cancel,
974 data);
975
976 data->user_data = NULL;
977
978 gimp_idle_run_async_data_dec_ref (data);
979 }
980
981 static gboolean
gimp_idle_run_async_idle(GimpIdleRunAsyncData * data)982 gimp_idle_run_async_idle (GimpIdleRunAsyncData *data)
983 {
984 gimp_idle_run_async_data_inc_ref (data);
985
986 g_signal_handlers_block_by_func (data->async,
987 gimp_idle_run_async_cancel,
988 data);
989
990 data->func (data->async, data->user_data);
991
992 g_signal_handlers_unblock_by_func (data->async,
993 gimp_idle_run_async_cancel,
994 data);
995
996 if (gimp_async_is_stopped (data->async))
997 {
998 data->user_data = NULL;
999
1000 gimp_idle_run_async_data_dec_ref (data);
1001
1002 return G_SOURCE_REMOVE;
1003 }
1004
1005 gimp_idle_run_async_data_dec_ref (data);
1006
1007 return G_SOURCE_CONTINUE;
1008 }
1009
1010 GimpAsync *
gimp_idle_run_async(GimpRunAsyncFunc func,gpointer user_data)1011 gimp_idle_run_async (GimpRunAsyncFunc func,
1012 gpointer user_data)
1013 {
1014 return gimp_idle_run_async_full (G_PRIORITY_DEFAULT_IDLE, func,
1015 user_data, NULL);
1016 }
1017
1018 GimpAsync *
gimp_idle_run_async_full(gint priority,GimpRunAsyncFunc func,gpointer user_data,GDestroyNotify user_data_destroy_func)1019 gimp_idle_run_async_full (gint priority,
1020 GimpRunAsyncFunc func,
1021 gpointer user_data,
1022 GDestroyNotify user_data_destroy_func)
1023 {
1024 GimpIdleRunAsyncData *data;
1025
1026 g_return_val_if_fail (func != NULL, NULL);
1027
1028 data = gimp_idle_run_async_data_new ();
1029
1030 data->func = func;
1031 data->user_data = user_data;
1032 data->user_data_destroy_func = user_data_destroy_func;
1033
1034 data->async = gimp_async_new ();
1035
1036 g_signal_connect (data->async, "cancel",
1037 G_CALLBACK (gimp_idle_run_async_cancel),
1038 data);
1039 g_signal_connect (data->async, "waiting",
1040 G_CALLBACK (gimp_idle_run_async_waiting),
1041 data);
1042
1043 data->idle_id = g_idle_add_full (
1044 priority,
1045 (GSourceFunc) gimp_idle_run_async_idle,
1046 data,
1047 (GDestroyNotify) gimp_idle_run_async_data_dec_ref);
1048
1049 return g_object_ref (data->async);
1050 }
1051
1052
1053 /* debug stuff */
1054
1055 #include "gegl/gimp-babl.h"
1056 #include "gimpimage.h"
1057 #include "gimplayer.h"
1058 #include "gimplayer-new.h"
1059
1060 GimpImage *
gimp_create_image_from_buffer(Gimp * gimp,GeglBuffer * buffer,const gchar * image_name)1061 gimp_create_image_from_buffer (Gimp *gimp,
1062 GeglBuffer *buffer,
1063 const gchar *image_name)
1064 {
1065 GimpImage *image;
1066 GimpLayer *layer;
1067 const Babl *format;
1068
1069 g_return_val_if_fail (GIMP_IS_GIMP (gimp), NULL);
1070 g_return_val_if_fail (GEGL_IS_BUFFER (buffer), NULL);
1071
1072 if (! image_name)
1073 image_name = "Debug Image";
1074
1075 format = gegl_buffer_get_format (buffer);
1076
1077 image = gimp_create_image (gimp,
1078 gegl_buffer_get_width (buffer),
1079 gegl_buffer_get_height (buffer),
1080 gimp_babl_format_get_base_type (format),
1081 gimp_babl_format_get_precision (format),
1082 FALSE);
1083
1084 layer = gimp_layer_new_from_gegl_buffer (buffer, image, format,
1085 image_name,
1086 GIMP_OPACITY_OPAQUE,
1087 GIMP_LAYER_MODE_NORMAL,
1088 NULL /* same image */);
1089 gimp_image_add_layer (image, layer, NULL, -1, FALSE);
1090
1091 gimp_create_display (gimp, image, GIMP_UNIT_PIXEL, 1.0, NULL, 0);
1092
1093 /* unref the image unconditionally, even when no display was created */
1094 g_object_add_weak_pointer (G_OBJECT (image), (gpointer) &image);
1095 g_object_unref (image);
1096
1097 return image;
1098 }
1099