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 &lt; etc. this is obvious, for &#xffff; 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