[gimp] app: new error dialog to backtrace and encourage people to report bugs.
- From: Jehan Pagès <jehanp src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [gimp] app: new error dialog to backtrace and encourage people to report bugs.
- Date: Sun, 28 Jan 2018 14:44:55 +0000 (UTC)
commit 9fdf35550bf592ec6235ec553d49fa467980b41d
Author: Jehan <jehan girinstud io>
Date: Tue Jan 23 03:38:46 2018 +0100
app: new error dialog to backtrace and encourage people to report bugs.
GIMP will now try to get a backtrace (on Unix machines only for now,
using g_on_error_stack_trace(); for Windows, we will likely have to look
into DrMinGW).
This is now applied to CRITICAL errors only, which usually means major
bugs but are currently mostly hidden unless you run GIMP in terminal. We
limit to 3 backtraces, because many CRITICAL typically get into domino
effect and cause more CRITICALs (for instance when a g_return*_if_fail()
returns too early).
app/core/gimp-gui.c | 11 +-
app/core/gimp-gui.h | 6 +-
app/core/gimp.c | 4 +-
app/dialogs/dialogs-constructors.c | 10 ++
app/dialogs/dialogs-constructors.h | 4 +
app/dialogs/dialogs.c | 2 +
app/errors.c | 159 +++++++++++++++++++---
app/gui/gui-message.c | 119 +++++++++++++---
app/gui/gui-message.h | 3 +-
app/pdb/message-cmds.c | 2 +-
app/version.c | 211 +++++++++++++++++++----------
app/version.h | 8 +-
app/widgets/Makefile.am | 2 +
app/widgets/gimpcriticaldialog.c | 262 ++++++++++++++++++++++++++++++++++++
app/widgets/gimpcriticaldialog.h | 64 +++++++++
app/widgets/widgets-types.h | 1 +
16 files changed, 739 insertions(+), 129 deletions(-)
---
diff --git a/app/core/gimp-gui.c b/app/core/gimp-gui.c
index 189910f..4c3842b 100644
--- a/app/core/gimp-gui.c
+++ b/app/core/gimp-gui.c
@@ -159,9 +159,10 @@ gimp_show_message (Gimp *gimp,
GObject *handler,
GimpMessageSeverity severity,
const gchar *domain,
- const gchar *message)
+ const gchar *message,
+ const gchar *trace)
{
- const gchar *desc = "Message";
+ const gchar *desc = trace ? "Error" : "Message";
g_return_if_fail (GIMP_IS_GIMP (gimp));
g_return_if_fail (handler == NULL || G_IS_OBJECT (handler));
@@ -174,8 +175,8 @@ gimp_show_message (Gimp *gimp,
{
if (gimp->gui.show_message)
{
- gimp->gui.show_message (gimp, handler,
- severity, domain, message);
+ gimp->gui.show_message (gimp, handler, severity,
+ domain, message, trace);
return;
}
else if (GIMP_IS_PROGRESS (handler) &&
@@ -190,6 +191,8 @@ gimp_show_message (Gimp *gimp,
gimp_enum_get_value (GIMP_TYPE_MESSAGE_SEVERITY, severity,
NULL, NULL, &desc, NULL);
g_printerr ("%s-%s: %s\n\n", domain, desc, message);
+ if (trace)
+ g_printerr ("Back trace:\n%s\n\n", trace);
}
void
diff --git a/app/core/gimp-gui.h b/app/core/gimp-gui.h
index 6118765..e80516d 100644
--- a/app/core/gimp-gui.h
+++ b/app/core/gimp-gui.h
@@ -35,7 +35,8 @@ struct _GimpGui
GObject *handler,
GimpMessageSeverity severity,
const gchar *domain,
- const gchar *message);
+ const gchar *message,
+ const gchar *trace);
void (* help) (Gimp *gimp,
GimpProgress *progress,
const gchar *help_domain,
@@ -144,7 +145,8 @@ void gimp_show_message (Gimp *gimp,
GObject *handler,
GimpMessageSeverity severity,
const gchar *domain,
- const gchar *message);
+ const gchar *message,
+ const gchar *trace);
void gimp_help (Gimp *gimp,
GimpProgress *progress,
const gchar *help_domain,
diff --git a/app/core/gimp.c b/app/core/gimp.c
index ef3c933..c2f1a04 100644
--- a/app/core/gimp.c
+++ b/app/core/gimp.c
@@ -1123,7 +1123,7 @@ gimp_message_valist (Gimp *gimp,
message = g_strdup_vprintf (format, args);
- gimp_show_message (gimp, handler, severity, NULL, message);
+ gimp_show_message (gimp, handler, severity, NULL, message, NULL);
g_free (message);
}
@@ -1138,7 +1138,7 @@ gimp_message_literal (Gimp *gimp,
g_return_if_fail (handler == NULL || G_IS_OBJECT (handler));
g_return_if_fail (message != NULL);
- gimp_show_message (gimp, handler, severity, NULL, message);
+ gimp_show_message (gimp, handler, severity, NULL, message, NULL);
}
void
diff --git a/app/dialogs/dialogs-constructors.c b/app/dialogs/dialogs-constructors.c
index 7ef92fe..f9abe47 100644
--- a/app/dialogs/dialogs-constructors.c
+++ b/app/dialogs/dialogs-constructors.c
@@ -35,6 +35,7 @@
#include "widgets/gimpchanneltreeview.h"
#include "widgets/gimpcoloreditor.h"
#include "widgets/gimpcolormapeditor.h"
+#include "widgets/gimpcriticaldialog.h"
#include "widgets/gimpdashboard.h"
#include "widgets/gimpdevicestatus.h"
#include "widgets/gimpdialogfactory.h"
@@ -216,6 +217,15 @@ dialogs_error_get (GimpDialogFactory *factory,
}
GtkWidget *
+dialogs_critical_get (GimpDialogFactory *factory,
+ GimpContext *context,
+ GimpUIManager *ui_manager,
+ gint view_size)
+{
+ return gimp_critical_dialog_new (_("GIMP critical error"));
+}
+
+GtkWidget *
dialogs_close_all_get (GimpDialogFactory *factory,
GimpContext *context,
GimpUIManager *ui_manager,
diff --git a/app/dialogs/dialogs-constructors.h b/app/dialogs/dialogs-constructors.h
index bcdfbed..6c86557 100644
--- a/app/dialogs/dialogs-constructors.h
+++ b/app/dialogs/dialogs-constructors.h
@@ -77,6 +77,10 @@ GtkWidget * dialogs_error_get (GimpDialogFactory *factory,
GimpContext *context,
GimpUIManager *ui_manager,
gint view_size);
+GtkWidget * dialogs_critical_get (GimpDialogFactory *factory,
+ GimpContext *context,
+ GimpUIManager *ui_manager,
+ gint view_size);
GtkWidget * dialogs_close_all_get (GimpDialogFactory *factory,
GimpContext *context,
GimpUIManager *ui_manager,
diff --git a/app/dialogs/dialogs.c b/app/dialogs/dialogs.c
index d817d74..ee744b8 100644
--- a/app/dialogs/dialogs.c
+++ b/app/dialogs/dialogs.c
@@ -286,6 +286,8 @@ static const GimpDialogFactoryEntry entries[] =
dialogs_action_search_get, TRUE, TRUE, TRUE),
TOPLEVEL ("gimp-error-dialog",
dialogs_error_get, TRUE, FALSE, FALSE),
+ TOPLEVEL ("gimp-critical-dialog",
+ dialogs_critical_get, TRUE, FALSE, FALSE),
TOPLEVEL ("gimp-close-all-dialog",
dialogs_close_all_get, TRUE, FALSE, FALSE),
TOPLEVEL ("gimp-quit-dialog",
diff --git a/app/errors.c b/app/errors.c
index e1520f0..e254792 100644
--- a/app/errors.c
+++ b/app/errors.c
@@ -21,8 +21,11 @@
#include <signal.h>
#include <stdarg.h>
+#include <stdio.h>
#include <stdlib.h>
+#include <string.h>
#include <sys/types.h>
+#include <sys/wait.h>
#ifdef HAVE_UNISTD_H
#include <unistd.h>
#endif
@@ -41,6 +44,7 @@
#include <windows.h>
#endif
+#define MAX_TRACES 3
/* private variables */
@@ -52,23 +56,24 @@ static gchar *full_prog_name = NULL;
/* local function prototypes */
-static G_GNUC_NORETURN void gimp_eek (const gchar *reason,
- const gchar *message,
- gboolean use_handler);
+static void gimp_third_party_message_log_func (const gchar *log_domain,
+ GLogLevelFlags flags,
+ const gchar *message,
+ gpointer data);
+static void gimp_message_log_func (const gchar *log_domain,
+ GLogLevelFlags flags,
+ const gchar *message,
+ gpointer data);
+static void gimp_error_log_func (const gchar *domain,
+ GLogLevelFlags flags,
+ const gchar *message,
+ gpointer data) G_GNUC_NORETURN;
-static void gimp_third_party_message_log_func (const gchar *log_domain,
- GLogLevelFlags flags,
- const gchar *message,
- gpointer data);
-static void gimp_message_log_func (const gchar *log_domain,
- GLogLevelFlags flags,
- const gchar *message,
- gpointer data);
-static void gimp_error_log_func (const gchar *domain,
- GLogLevelFlags flags,
- const gchar *message,
- gpointer data) G_GNUC_NORETURN;
+static G_GNUC_NORETURN void gimp_eek (const gchar *reason,
+ const gchar *message,
+ gboolean use_handler);
+static gchar * gimp_get_stack_trace (void);
/* public functions */
@@ -129,7 +134,7 @@ errors_init (Gimp *gimp,
for (i = 0; i < G_N_ELEMENTS (log_domains); i++)
g_log_set_handler (log_domains[i],
- G_LOG_LEVEL_MESSAGE,
+ G_LOG_LEVEL_MESSAGE | G_LOG_LEVEL_CRITICAL,
gimp_message_log_func, gimp);
g_log_set_handler ("GEGL",
@@ -176,7 +181,7 @@ gimp_third_party_message_log_func (const gchar *log_domain,
* messages.
*/
gimp_show_message (gimp, NULL, GIMP_MESSAGE_WARNING,
- log_domain, message);
+ log_domain, message, NULL);
}
else
{
@@ -190,17 +195,45 @@ gimp_message_log_func (const gchar *log_domain,
const gchar *message,
gpointer data)
{
- Gimp *gimp = data;
+ static gint n_traces;
+ GimpMessageSeverity severity = GIMP_MESSAGE_WARNING;
+ Gimp *gimp = data;
+ gchar *trace = NULL;
+
+ if (flags & G_LOG_LEVEL_CRITICAL)
+ {
+ severity = GIMP_MESSAGE_ERROR;
+
+ if (n_traces < MAX_TRACES)
+ {
+ /* Getting debug traces is time-expensive, and worse, some
+ * critical errors have the bad habit to create more errors
+ * (the first ones are therefore usually the most useful).
+ * This is why we keep track of how many times we made traces
+ * and stop doing them after a while.
+ * Hence when this happens, critical errors are simply processed as
+ * lower level errors.
+ */
+ trace = gimp_get_stack_trace ();
+ n_traces++;
+ }
+ }
if (gimp)
{
- gimp_show_message (gimp, NULL, GIMP_MESSAGE_WARNING, NULL, message);
+ gimp_show_message (gimp, NULL, severity,
+ NULL, message, trace);
}
else
{
g_printerr ("%s: %s\n\n",
gimp_filename_to_utf8 (full_prog_name), message);
+ if (trace)
+ g_printerr ("Back trace:\n%s\n\n", trace);
}
+
+ if (trace)
+ g_free (trace);
}
static void
@@ -268,3 +301,91 @@ gimp_eek (const gchar *reason,
exit (EXIT_FAILURE);
}
+
+static gchar *
+gimp_get_stack_trace (void)
+{
+ gchar *trace = NULL;
+#if defined(G_OS_UNIX)
+ GString *gtrace = NULL;
+ gchar buffer[256];
+ ssize_t read_n;
+ pid_t pid;
+ int status;
+ int out_fd[2];
+#endif
+
+ /* Though we should theoretically ask with GIMP_STACK_TRACE_QUERY, we
+ * just assume yes right now. TODO: improve this!
+ */
+ if (stack_trace_mode == GIMP_STACK_TRACE_NEVER)
+ return NULL;
+
+ /* This works only on UNIX systems. On Windows, we'll have to find
+ * another method, probably with DrMingW.
+ */
+#if defined(G_OS_UNIX)
+ if (pipe (out_fd) == -1)
+ {
+ return NULL;
+ }
+
+ /* This is a trick to get the stack trace inside a string.
+ * GLib's g_on_error_stack_trace() unfortunately writes directly to
+ * the standard output, which is a very unfortunate implementation.
+ */
+ pid = fork ();
+ if (pid == 0)
+ {
+ /* Child process. */
+
+ /* XXX I just don't understand why, but somehow the parent process
+ * doesn't get the output if I don't print something first. I just
+ * leave this very dirty hack until I figure out what's going on.
+ */
+ printf(" ");
+
+ /* Redirect the debugger output. */
+ dup2 (out_fd[1], STDOUT_FILENO);
+ close (out_fd[0]);
+ close (out_fd[1]);
+ g_on_error_stack_trace (full_prog_name);
+ _exit (0);
+ }
+ else if (pid > 0)
+ {
+ /* Main process. */
+ waitpid (pid, &status, 0);
+ }
+ else if (pid == (pid_t) -1)
+ {
+ /* No trace can be done. */
+ return NULL;
+ }
+
+ gtrace = g_string_new ("");
+
+ /* It is important to close the writing side of the pipe, otherwise
+ * the read() will wait forever without getting the information that
+ * writing is finished.
+ */
+ close (out_fd[1]);
+
+ while ((read_n = read (out_fd[0], buffer, 256)) > 0)
+ {
+ g_string_append_len (gtrace, buffer, read_n);
+ }
+ close (out_fd[0]);
+
+ if (gtrace)
+ trace = g_string_free (gtrace, FALSE);
+ if (trace && strlen (g_strstrip (trace)) == 0)
+ {
+ /* Empty strings are the same as no strings. */
+ g_free (trace);
+ trace = NULL;
+ }
+#endif
+
+ return trace;
+}
diff --git a/app/gui/gui-message.c b/app/gui/gui-message.c
index 5ab7608..c30cd4c 100644
--- a/app/gui/gui-message.c
+++ b/app/gui/gui-message.c
@@ -34,6 +34,7 @@
#include "plug-in/gimpplugin.h"
+#include "widgets/gimpcriticaldialog.h"
#include "widgets/gimpdialogfactory.h"
#include "widgets/gimpdockable.h"
#include "widgets/gimperrorconsole.h"
@@ -55,6 +56,7 @@ typedef struct
Gimp *gimp;
gchar *domain;
gchar *message;
+ gchar *trace;
GObject *handler;
GimpMessageSeverity severity;
} GimpLogMessageData;
@@ -64,14 +66,20 @@ static gboolean gui_message_idle (gpointer user_data);
static gboolean gui_message_error_console (Gimp *gimp,
GimpMessageSeverity severity,
const gchar *domain,
- const gchar *message);
+ const gchar *message,
+ const gchar *trace);
static gboolean gui_message_error_dialog (Gimp *gimp,
GObject *handler,
GimpMessageSeverity severity,
const gchar *domain,
- const gchar *message);
+ const gchar *message,
+ const gchar *trace);
static void gui_message_console (GimpMessageSeverity severity,
const gchar *domain,
+ const gchar *message,
+ const gchar *trace);
+static gchar * gui_message_format (GimpMessageSeverity severity,
+ const gchar *domain,
const gchar *message);
@@ -80,12 +88,13 @@ gui_message (Gimp *gimp,
GObject *handler,
GimpMessageSeverity severity,
const gchar *domain,
- const gchar *message)
+ const gchar *message,
+ const gchar *trace)
{
switch (gimp->message_handler)
{
case GIMP_ERROR_CONSOLE:
- if (gui_message_error_console (gimp, severity, domain, message))
+ if (gui_message_error_console (gimp, severity, domain, message, trace))
return;
gimp->message_handler = GIMP_MESSAGE_BOX;
@@ -104,6 +113,7 @@ gui_message (Gimp *gimp,
data->gimp = gimp;
data->domain = g_strdup (domain);
data->message = g_strdup (message);
+ data->trace = trace ? g_strdup (trace) : NULL;
data->handler = handler? g_object_ref (handler) : NULL;
data->severity = severity;
@@ -112,14 +122,15 @@ gui_message (Gimp *gimp,
data, g_free);
return;
}
- if (gui_message_error_dialog (gimp, handler, severity, domain, message))
+ if (gui_message_error_dialog (gimp, handler, severity,
+ domain, message, trace))
return;
gimp->message_handler = GIMP_CONSOLE;
/* fallthru */
case GIMP_CONSOLE:
- gui_message_console (severity, domain, message);
+ gui_message_console (severity, domain, message, trace);
break;
}
}
@@ -133,14 +144,18 @@ gui_message_idle (gpointer user_data)
data->handler,
data->severity,
data->domain,
- data->message))
+ data->message,
+ data->trace))
{
gui_message_console (data->severity,
data->domain,
- data->message);
+ data->message,
+ data->trace);
}
g_free (data->domain);
g_free (data->message);
+ if (data->trace)
+ g_free (data->trace);
if (data->handler)
g_object_unref (data->handler);
@@ -151,10 +166,13 @@ static gboolean
gui_message_error_console (Gimp *gimp,
GimpMessageSeverity severity,
const gchar *domain,
- const gchar *message)
+ const gchar *message,
+ const gchar *trace)
{
GtkWidget *dockable;
+ /* TODO: right now the error console does nothing with the trace. */
+
dockable = gimp_dialog_factory_find_widget (gimp_dialog_factory_get_singleton (),
"gimp-error-console");
@@ -255,16 +273,59 @@ global_error_dialog (void)
FALSE);
}
+static GtkWidget *
+global_critical_dialog (void)
+{
+ GdkScreen *screen;
+ gint monitor;
+
+ monitor = gimp_get_monitor_at_pointer (&screen);
+
+ return gimp_dialog_factory_dialog_new (gimp_dialog_factory_get_singleton (),
+ screen, monitor,
+ NULL /*ui_manager*/,
+ "gimp-critical-dialog", -1,
+ FALSE);
+}
+
static gboolean
gui_message_error_dialog (Gimp *gimp,
GObject *handler,
GimpMessageSeverity severity,
const gchar *domain,
- const gchar *message)
+ const gchar *message,
+ const gchar *trace)
{
- GtkWidget *dialog;
+ GtkWidget *dialog;
+ GtkMessageType type = GTK_MESSAGE_ERROR;
+
+ switch (severity)
+ {
+ case GIMP_MESSAGE_INFO: type = GTK_MESSAGE_INFO; break;
+ case GIMP_MESSAGE_WARNING: type = GTK_MESSAGE_WARNING; break;
+ case GIMP_MESSAGE_ERROR: type = GTK_MESSAGE_ERROR; break;
+ }
+
+ if (trace)
+ {
+ /* Process differently when traces are included.
+ * The reason is that this will take significant place, and cannot
+ * be processed as a progress message or in the global dialog. It
+ * will require its own dedicated dialog which will encourage
+ * people to report the bug.
+ */
+ gchar *text;
- if (GIMP_IS_PROGRESS (handler))
+ dialog = global_critical_dialog ();
+ text = gui_message_format (severity, domain, message);
+ gimp_critical_dialog_add (GIMP_CRITICAL_DIALOG (dialog),
+ text, trace);
+ g_free (text);
+ gtk_widget_show (dialog);
+
+ return TRUE;
+ }
+ else if (GIMP_IS_PROGRESS (handler))
{
/* If there's already an error dialog associated with this
* progress, then continue without trying gimp_progress_message().
@@ -278,15 +339,7 @@ gui_message_error_dialog (Gimp *gimp,
}
else if (GTK_IS_WIDGET (handler))
{
- GtkWidget *parent = GTK_WIDGET (handler);
- GtkMessageType type = GTK_MESSAGE_ERROR;
-
- switch (severity)
- {
- case GIMP_MESSAGE_INFO: type = GTK_MESSAGE_INFO; break;
- case GIMP_MESSAGE_WARNING: type = GTK_MESSAGE_WARNING; break;
- case GIMP_MESSAGE_ERROR: type = GTK_MESSAGE_ERROR; break;
- }
+ GtkWidget *parent = GTK_WIDGET (handler);
dialog =
gtk_message_dialog_new (GTK_WINDOW (gtk_widget_get_toplevel (parent)),
@@ -324,11 +377,31 @@ gui_message_error_dialog (Gimp *gimp,
static void
gui_message_console (GimpMessageSeverity severity,
const gchar *domain,
- const gchar *message)
+ const gchar *message,
+ const gchar *trace)
+{
+ gchar *formatted_message;
+
+ formatted_message = gui_message_format (severity, domain, message);
+ g_printerr ("%s\n\n", formatted_message);
+ g_free (formatted_message);
+
+ if (trace)
+ g_printerr ("Stack trace:\n%s\n\n", trace);
+}
+
+static gchar *
+gui_message_format (GimpMessageSeverity severity,
+ const gchar *domain,
+ const gchar *message)
{
const gchar *desc = "Message";
+ gchar *formatted_message;
gimp_enum_get_value (GIMP_TYPE_MESSAGE_SEVERITY, severity,
NULL, NULL, &desc, NULL);
- g_printerr ("%s-%s: %s\n\n", domain, desc, message);
+
+ formatted_message = g_strdup_printf ("%s-%s: %s", domain, desc, message);
+
+ return formatted_message;
}
diff --git a/app/gui/gui-message.h b/app/gui/gui-message.h
index 45f4300..805f266 100644
--- a/app/gui/gui-message.h
+++ b/app/gui/gui-message.h
@@ -23,7 +23,8 @@ void gui_message (Gimp *gimp,
GObject *handler,
GimpMessageSeverity severity,
const gchar *domain,
- const gchar *message);
+ const gchar *message,
+ const gchar *trace);
#endif /* __GUI_VTABLE_H__ */
diff --git a/app/pdb/message-cmds.c b/app/pdb/message-cmds.c
index 9df0980..7f2184e 100644
--- a/app/pdb/message-cmds.c
+++ b/app/pdb/message-cmds.c
@@ -61,7 +61,7 @@ message_invoker (GimpProcedure *procedure,
if (gimp->plug_in_manager->current_plug_in)
domain = gimp_plug_in_get_undo_desc (gimp->plug_in_manager->current_plug_in);
gimp_show_message (gimp, G_OBJECT (progress), GIMP_MESSAGE_WARNING,
- domain, message);
+ domain, message, NULL);
}
return gimp_procedure_get_return_values (procedure, success,
diff --git a/app/version.c b/app/version.c
index de439bc..15bc0db 100644
--- a/app/version.c
+++ b/app/version.c
@@ -38,15 +38,17 @@
#include "gimp-intl.h"
-static void
-gimp_show_library_version (const gchar *package,
- gint build_time_major,
- gint build_time_minor,
- gint build_time_micro,
- gint run_time_major,
- gint run_time_minor,
- gint run_time_micro)
+static gchar *
+gimp_library_version (const gchar *package,
+ gint build_time_major,
+ gint build_time_minor,
+ gint build_time_micro,
+ gint run_time_major,
+ gint run_time_minor,
+ gint run_time_micro,
+ gboolean localized)
{
+ gchar *lib_version;
gchar *build_time_version;
gchar *run_time_version;
@@ -60,91 +62,156 @@ gimp_show_library_version (const gchar *package,
run_time_micro);
/* show versions of libraries used by GIMP */
- g_print (_("using %s version %s (compiled against version %s)"),
- package, run_time_version, build_time_version);
- g_print ("\n");
-
+ lib_version = g_strdup_printf (localized ?
+ _("using %s version %s (compiled against version %s)") :
+ "using %s version %s (compiled against version %s)",
+ package, run_time_version, build_time_version);
g_free (run_time_version);
g_free (build_time_version);
+
+ return lib_version;
}
-static void
-gimp_show_library_versions (void)
+static gchar *
+gimp_library_versions (gboolean localized)
{
- gint gegl_major_version;
- gint gegl_minor_version;
- gint gegl_micro_version;
+ gchar *lib_versions;
+ gchar *lib_version;
+ gchar *temp;
+ gint gegl_major_version;
+ gint gegl_minor_version;
+ gint gegl_micro_version;
gegl_get_version (&gegl_major_version,
&gegl_minor_version,
&gegl_micro_version);
- gimp_show_library_version ("GEGL",
- GEGL_MAJOR_VERSION,
- GEGL_MINOR_VERSION,
- GEGL_MICRO_VERSION,
- gegl_major_version,
- gegl_minor_version,
- gegl_micro_version);
-
- gimp_show_library_version ("GLib",
- GLIB_MAJOR_VERSION,
- GLIB_MINOR_VERSION,
- GLIB_MICRO_VERSION,
- glib_major_version,
- glib_minor_version,
- glib_micro_version);
-
- gimp_show_library_version ("GdkPixbuf",
- GDK_PIXBUF_MAJOR,
- GDK_PIXBUF_MINOR,
- GDK_PIXBUF_MICRO,
- gdk_pixbuf_major_version,
- gdk_pixbuf_minor_version,
- gdk_pixbuf_micro_version);
+ lib_versions = gimp_library_version ("GEGL",
+ GEGL_MAJOR_VERSION,
+ GEGL_MINOR_VERSION,
+ GEGL_MICRO_VERSION,
+ gegl_major_version,
+ gegl_minor_version,
+ gegl_micro_version,
+ localized);
+
+ lib_version = gimp_library_version ("GLib",
+ GLIB_MAJOR_VERSION,
+ GLIB_MINOR_VERSION,
+ GLIB_MICRO_VERSION,
+ glib_major_version,
+ glib_minor_version,
+ glib_micro_version,
+ localized);
+ temp = g_strdup_printf ("%s\n%s", lib_versions, lib_version);
+ g_free (lib_versions);
+ g_free (lib_version);
+ lib_versions = temp;
+
+ lib_version = gimp_library_version ("GdkPixbuf",
+ GDK_PIXBUF_MAJOR,
+ GDK_PIXBUF_MINOR,
+ GDK_PIXBUF_MICRO,
+ gdk_pixbuf_major_version,
+ gdk_pixbuf_minor_version,
+ gdk_pixbuf_micro_version,
+ localized);
+ temp = g_strdup_printf ("%s\n%s", lib_versions, lib_version);
+ g_free (lib_versions);
+ g_free (lib_version);
+ lib_versions = temp;
#ifndef GIMP_CONSOLE_COMPILATION
- gimp_show_library_version ("GTK+",
- GTK_MAJOR_VERSION,
- GTK_MINOR_VERSION,
- GTK_MICRO_VERSION,
- gtk_major_version,
- gtk_minor_version,
- gtk_micro_version);
+ lib_version = gimp_library_version ("GTK+",
+ GTK_MAJOR_VERSION,
+ GTK_MINOR_VERSION,
+ GTK_MICRO_VERSION,
+ gtk_major_version,
+ gtk_minor_version,
+ gtk_micro_version,
+ localized);
+ temp = g_strdup_printf ("%s\n%s", lib_versions, lib_version);
+ g_free (lib_versions);
+ g_free (lib_version);
+ lib_versions = temp;
#endif
- gimp_show_library_version ("Pango",
- PANGO_VERSION_MAJOR,
- PANGO_VERSION_MINOR,
- PANGO_VERSION_MICRO,
- pango_version () / 100 / 100,
- pango_version () / 100 % 100,
- pango_version () % 100);
-
- gimp_show_library_version ("Fontconfig",
- FC_MAJOR, FC_MINOR, FC_REVISION,
- FcGetVersion () / 100 / 100,
- FcGetVersion () / 100 % 100,
- FcGetVersion () % 100);
-
- g_print (_("using %s version %s (compiled against version %s)"),
- "Cairo", cairo_version_string (), CAIRO_VERSION_STRING);
- g_print ("\n");
+ lib_version = gimp_library_version ("Pango",
+ PANGO_VERSION_MAJOR,
+ PANGO_VERSION_MINOR,
+ PANGO_VERSION_MICRO,
+ pango_version () / 100 / 100,
+ pango_version () / 100 % 100,
+ pango_version () % 100,
+ localized);
+ temp = g_strdup_printf ("%s\n%s", lib_versions, lib_version);
+ g_free (lib_versions);
+ g_free (lib_version);
+ lib_versions = temp;
+
+ lib_version = gimp_library_version ("Fontconfig",
+ FC_MAJOR, FC_MINOR, FC_REVISION,
+ FcGetVersion () / 100 / 100,
+ FcGetVersion () / 100 % 100,
+ FcGetVersion () % 100,
+ localized);
+ temp = g_strdup_printf ("%s\n%s", lib_versions, lib_version);
+ g_free (lib_versions);
+ g_free (lib_version);
+ lib_versions = temp;
+
+ lib_version = g_strdup_printf (localized ?
+ _("using %s version %s (compiled against version %s)") :
+ "using %s version %s (compiled against version %s)",
+ "Cairo", cairo_version_string (), CAIRO_VERSION_STRING);
+ temp = g_strdup_printf ("%s\n%s\n", lib_versions, lib_version);
+ g_free (lib_versions);
+ g_free (lib_version);
+ lib_versions = temp;
+
+ return lib_versions;
}
void
gimp_version_show (gboolean be_verbose)
{
- g_print (_("%s version %s"), GIMP_NAME, GIMP_VERSION);
- g_print ("\n");
+ gchar *version = gimp_version (be_verbose, TRUE);
+
+ g_print ("%s", version);
+
+ g_free (version);
+}
+
+gchar *
+gimp_version (gboolean be_verbose,
+ gboolean localized)
+{
+ gchar *version;
+ gchar *temp;
+
+ version = g_strdup_printf (localized ? _("%s version %s") : "%s version %s",
+ GIMP_NAME, GIMP_VERSION);;
+ temp = g_strconcat (version, "\n", NULL);
+ g_free (version);
+ version = temp;
if (be_verbose)
{
- g_print ("git-describe: %s", GIMP_GIT_VERSION);
- g_print ("\n");
- g_print ("C compiler:\n%s", CC_VERSION);
-
- g_print ("\n");
- gimp_show_library_versions ();
+ gchar *verbose_info;
+ gchar *lib_versions;
+
+ lib_versions = gimp_library_versions (localized);
+ verbose_info = g_strdup_printf ("git-describe: %s\n"
+ "C compiler:\n%s\n%s",
+ GIMP_GIT_VERSION, CC_VERSION,
+ lib_versions);
+ g_free (lib_versions);
+
+ temp = g_strconcat (version, verbose_info, NULL);
+ g_free (version);
+ g_free (verbose_info);
+ version = temp;
}
+
+ return version;
}
diff --git a/app/version.h b/app/version.h
index 60ca9d0..9f5f61a 100644
--- a/app/version.h
+++ b/app/version.h
@@ -19,12 +19,10 @@
#define __VERSION_H__
-#ifndef GIMP_APP_GLUE_COMPILATION
-#error You must not #include "version.h" from an app/ subdir
-#endif
-
-void gimp_version_show (gboolean be_verbose);
+void gimp_version_show (gboolean be_verbose);
+gchar * gimp_version (gboolean be_verbose,
+ gboolean localized);
#endif /* __VERSION_H__ */
diff --git a/app/widgets/Makefile.am b/app/widgets/Makefile.am
index bb6546c..d7013ef 100644
--- a/app/widgets/Makefile.am
+++ b/app/widgets/Makefile.am
@@ -116,6 +116,8 @@ libappwidgets_a_sources = \
gimpcontrollermouse.h \
gimpcontrollerwheel.c \
gimpcontrollerwheel.h \
+ gimpcriticaldialog.c \
+ gimpcriticaldialog.h \
gimpcursor.c \
gimpcursor.h \
gimpcurveview.c \
diff --git a/app/widgets/gimpcriticaldialog.c b/app/widgets/gimpcriticaldialog.c
new file mode 100644
index 0000000..80cd56f
--- /dev/null
+++ b/app/widgets/gimpcriticaldialog.c
@@ -0,0 +1,262 @@
+/* GIMP - The GNU Image Manipulation Program
+ * Copyright (C) 1995 Spencer Kimball and Peter Mattis
+ *
+ * gimpcriticaldialog.c
+ * Copyright (C) 2018 Jehan <jehan gimp org>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "config.h"
+
+#include <string.h>
+
+#include <gegl.h>
+#include <gtk/gtk.h>
+
+#include "libgimpbase/gimpbase.h"
+#include "libgimpwidgets/gimpwidgets.h"
+
+#include "widgets-types.h"
+
+#include "gimpcriticaldialog.h"
+
+#include "version.h"
+
+#include "gimp-intl.h"
+
+#define GIMP_CRITICAL_RESPONSE_CLIPBOARD 1
+#define GIMP_CRITICAL_RESPONSE_URL 2
+
+
+static void gimp_critical_dialog_response (GtkDialog *dialog,
+ gint response_id);
+
+G_DEFINE_TYPE (GimpCriticalDialog, gimp_critical_dialog, GIMP_TYPE_DIALOG)
+
+#define parent_class gimp_critical_dialog_parent_class
+
+
+static void
+gimp_critical_dialog_class_init (GimpCriticalDialogClass *klass)
+{
+ GtkDialogClass *dialog_class = GTK_DIALOG_CLASS (klass);
+
+ dialog_class->response = gimp_critical_dialog_response;
+}
+
+static void
+gimp_critical_dialog_init (GimpCriticalDialog *dialog)
+{
+ PangoAttrList *attrs;
+ PangoAttribute *attr;
+ gchar *text;
+ gchar *version;
+ GtkWidget *widget;
+ GtkTextBuffer *buffer;
+ const gchar *button1 = _("Copy bug information");
+ const gchar *button2 = _("Open bug tracker");
+
+ gtk_window_set_role (GTK_WINDOW (dialog), "gimp-critical");
+
+ gtk_dialog_add_buttons (GTK_DIALOG (dialog),
+ button1, GIMP_CRITICAL_RESPONSE_CLIPBOARD,
+ button2, GIMP_CRITICAL_RESPONSE_URL,
+ _("_Close"), GTK_RESPONSE_CLOSE,
+ NULL);
+ gtk_dialog_set_default_response (GTK_DIALOG (dialog), GTK_RESPONSE_CLOSE);
+ gtk_window_set_resizable (GTK_WINDOW (dialog), TRUE);
+
+ dialog->vbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, 0);
+ gtk_box_pack_start (GTK_BOX (gtk_dialog_get_content_area (GTK_DIALOG (dialog))),
+ dialog->vbox, TRUE, TRUE, 0);
+ gtk_widget_show (dialog->vbox);
+
+ /* The error label. */
+ dialog->label = gtk_label_new (NULL);
+ gtk_label_set_justify (GTK_LABEL (dialog->label), GTK_JUSTIFY_CENTER);
+ gtk_label_set_selectable (GTK_LABEL (dialog->label), TRUE);
+ gtk_box_pack_start (GTK_BOX (dialog->vbox),
+ dialog->label, FALSE, FALSE, 0);
+
+ attrs = pango_attr_list_new ();
+ attr = pango_attr_weight_new (PANGO_WEIGHT_SEMIBOLD);
+ pango_attr_list_insert (attrs, attr);
+ gtk_label_set_attributes (GTK_LABEL (dialog->label), attrs);
+ pango_attr_list_unref (attrs);
+
+ gtk_widget_show (dialog->label);
+
+ /* Generic "report a bug" instructions. */
+ text = g_strdup_printf ("%s\n"
+ " \xe2\x80\xa2 %s %s\n"
+ " \xe2\x80\xa2 %s %s\n"
+ " \xe2\x80\xa2 %s \n"
+ " \xe2\x80\xa2 %s \n"
+ " \xe2\x80\xa2 %s \n"
+ " \xe2\x80\xa2 %s\n\n"
+ "%s",
+ _("To help us improve GIMP, you can report the bug with "
+ "these simple steps:"),
+ _("Copy the bug information to clipboard by clicking: "),
+ button1,
+ _("Open our bug tracker in browser by clicking: "),
+ button2,
+ _("Create a login if you don't have one yet."),
+ _("Paste the clipboard text in a new bug report."),
+ _("Add relevant information in English in the bug report "
+ "explaining what you were doing when this error occurred."),
+ _("This error may have left GIMP in an inconsistent state. "
+ "It is advised to save your work and restart GIMP."),
+ _("Note: you can also close the dialog directly but reporting "
+ "bugs is the best way to make your software awesome."));
+ widget = gtk_label_new (text);
+ g_free (text);
+ gtk_label_set_selectable (GTK_LABEL (widget), TRUE);
+ gtk_box_pack_start (GTK_BOX (dialog->vbox), widget, FALSE, FALSE, 0);
+ gtk_widget_show (widget);
+
+ /* Bug details for developers. */
+ widget = gtk_scrolled_window_new (NULL, NULL);
+ gtk_box_pack_start (GTK_BOX (dialog->vbox), widget, TRUE, TRUE, 0);
+ gtk_widget_show (widget);
+
+ buffer = gtk_text_buffer_new (NULL);
+ version = gimp_version (TRUE, FALSE);
+ gtk_text_buffer_set_text (buffer, version, -1);
+ g_free (version);
+
+ dialog->details = gtk_text_view_new_with_buffer (buffer);
+ g_object_unref (buffer);
+ gtk_text_view_set_editable (GTK_TEXT_VIEW (dialog->details), FALSE);
+ gtk_widget_show (dialog->details);
+ gtk_container_add (GTK_CONTAINER (widget), dialog->details);
+}
+
+static void
+gimp_critical_dialog_response (GtkDialog *dialog,
+ gint response_id)
+{
+ GimpCriticalDialog *critical = GIMP_CRITICAL_DIALOG (dialog);
+
+ switch (response_id)
+ {
+ case GIMP_CRITICAL_RESPONSE_CLIPBOARD:
+ {
+ GtkClipboard *clipboard;
+
+ clipboard = gtk_clipboard_get_for_display (gdk_display_get_default (),
+ GDK_SELECTION_CLIPBOARD);
+ if (clipboard)
+ {
+ GtkTextBuffer *buffer;
+ gchar *text;
+ GtkTextIter start;
+ GtkTextIter end;
+
+ buffer = gtk_text_view_get_buffer (GTK_TEXT_VIEW (critical->details));
+ gtk_text_buffer_get_iter_at_offset (buffer, &start, 0);
+ gtk_text_buffer_get_iter_at_offset (buffer, &end, -1);
+ text = gtk_text_buffer_get_text (buffer, &start, &end, FALSE);
+ gtk_clipboard_set_text (clipboard, text, -1);
+ g_free (text);
+ }
+ }
+ break;
+ case GIMP_CRITICAL_RESPONSE_URL:
+ {
+ const gchar *url;
+
+ /* XXX Ideally I'd find a way to prefill the bug report
+ * through the URL or with POST data. But I could not find
+ * any. Anyway since we may soon ditch bugzilla to follow
+ * GNOME infrastructure changes, I don't want to waste too
+ * much time digging into it.
+ */
+ url = "https://bugzilla.gnome.org/enter_bug.cgi?product=GIMP";
+ gtk_show_uri (gdk_screen_get_default (),
+ url,
+ gtk_get_current_event_time(),
+ NULL);
+ }
+ break;
+ case GTK_RESPONSE_DELETE_EVENT:
+ case GTK_RESPONSE_CLOSE:
+ default:
+ gtk_widget_destroy (GTK_WIDGET (dialog));
+ break;
+ }
+}
+
+/* public functions */
+
+GtkWidget *
+gimp_critical_dialog_new (const gchar *title)
+{
+ g_return_val_if_fail (title != NULL, NULL);
+
+ return g_object_new (GIMP_TYPE_CRITICAL_DIALOG,
+ "title", title,
+ NULL);
+}
+
+void
+gimp_critical_dialog_add (GimpCriticalDialog *dialog,
+ const gchar *message,
+ const gchar *trace)
+{
+ GtkTextBuffer *buffer;
+ GtkTextIter end;
+ gchar *text;
+
+ if (! GIMP_IS_CRITICAL_DIALOG (dialog) || ! message || ! trace)
+ {
+ /* This is a bit hackish. We usually should use
+ * g_return_if_fail(). But I don't want to end up in a critical
+ * recursing loop if our code had bugs. We would crash GIMP with
+ * a CRITICAL which would otherwise not have necessarily ended up
+ * in a crash.
+ */
+ return;
+ }
+
+ /* The user text, which should be localized. */
+ if (! gtk_label_get_text (GTK_LABEL (dialog->label)) ||
+ strlen (gtk_label_get_text (GTK_LABEL (dialog->label))) == 0)
+ {
+ /* First critical error. Let's just display it. */
+ text = g_strdup_printf (_("GIMP encountered a critical error: %s"),
+ message);
+ }
+ else
+ {
+ /* Let's not display all errors. They will be in the bug report
+ * part anyway.
+ */
+ text = g_strdup_printf (_("GIMP encountered several critical errors!"));
+ }
+ gtk_label_set_text (GTK_LABEL (dialog->label),
+ text);
+ g_free (text);
+
+ /* The details text is untranslated on purpose. This is the message
+ * meant to go to clipboard for the bug report. It has to be in
+ * English.
+ */
+ buffer = gtk_text_view_get_buffer (GTK_TEXT_VIEW (dialog->details));
+ gtk_text_buffer_get_iter_at_offset (buffer, &end, -1);
+ text = g_strdup_printf ("\n\n> %s\n\nStack trace:\n%s", message, trace);
+ gtk_text_buffer_insert (buffer, &end, text, -1);
+ g_free (text);
+}
diff --git a/app/widgets/gimpcriticaldialog.h b/app/widgets/gimpcriticaldialog.h
new file mode 100644
index 0000000..070fc32
--- /dev/null
+++ b/app/widgets/gimpcriticaldialog.h
@@ -0,0 +1,64 @@
+/* GIMP - The GNU Image Manipulation Program
+ * Copyright (C) 1995 Spencer Kimball and Peter Mattis
+ *
+ * gimpcriticaldialog.h
+ * Copyright (C) 2018 Jehan <jehan gimp org>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef __GIMP_CRITICAL_DIALOG_H__
+#define __GIMP_CRITICAL_DIALOG_H__
+
+G_BEGIN_DECLS
+
+
+#define GIMP_TYPE_CRITICAL_DIALOG (gimp_critical_dialog_get_type ())
+#define GIMP_CRITICAL_DIALOG(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GIMP_TYPE_CRITICAL_DIALOG,
GimpCriticalDialog))
+#define GIMP_CRITICAL_DIALOG_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GIMP_TYPE_CRITICAL_DIALOG,
GimpCriticalDialogClass))
+#define GIMP_IS_CRITICAL_DIALOG(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GIMP_TYPE_CRITICAL_DIALOG))
+#define GIMP_IS_CRITICAL_DIALOG_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GIMP_TYPE_CRITICAL_DIALOG))
+#define GIMP_CRITICAL_DIALOG_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GIMP_TYPE_CRITICAL_DIALOG,
GimpCriticalDialogClass))
+
+
+typedef struct _GimpCriticalDialogClass GimpCriticalDialogClass;
+
+struct _GimpCriticalDialog
+{
+ GimpDialog parent_instance;
+
+ GtkWidget *vbox;
+
+ GtkWidget *label;
+ GtkWidget *details;
+};
+
+struct _GimpCriticalDialogClass
+{
+ GimpDialogClass parent_class;
+};
+
+
+GType gimp_critical_dialog_get_type (void) G_GNUC_CONST;
+
+GtkWidget * gimp_critical_dialog_new (const gchar *title);
+void gimp_critical_dialog_add (GimpCriticalDialog *dialog,
+ const gchar *message,
+ const gchar *trace);
+
+
+
+G_END_DECLS
+
+#endif /* __GIMP_CRITICAL_DIALOG_H__ */
diff --git a/app/widgets/widgets-types.h b/app/widgets/widgets-types.h
index 30f79c5..b82adb0 100644
--- a/app/widgets/widgets-types.h
+++ b/app/widgets/widgets-types.h
@@ -145,6 +145,7 @@ typedef struct _GimpSaveDialog GimpSaveDialog;
/* misc dialogs */
typedef struct _GimpColorDialog GimpColorDialog;
+typedef struct _GimpCriticalDialog GimpCriticalDialog;
typedef struct _GimpErrorDialog GimpErrorDialog;
typedef struct _GimpMessageDialog GimpMessageDialog;
typedef struct _GimpProgressDialog GimpProgressDialog;
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]