Sample command line call:
`GTK_DEBUG=interactive src/geeqie`
+### DEBUG_BT()
+
+Prints a backtrace.
+Use only for temporary debugging i.e. not in code in the repository
+
+```text
+When the LogWindow has focus, the F1 key executes the following action as a command line program:
+<options->log_window.action> <selected text>
+The log_window.action value must be set by editing the geeqierc.xml file manually.
+If no text is selected when the F1 key is pressed, the text either side of the cursor delimited by a space character or the beginning or end of the line is selected.
+This feature may be used to open an editor at a file location listed in the backtrace.
+```
+
+### DEBUG_FD()
+
+Prints a dump of the FileData hash list as a ref. count followed by the full path of the item.
+Use only for temporary debugging i.e. not in code in the repository
+
---
## GPL header
/* Define for large files, on AIX-style hosts. */
/* #undef _LARGE_FILES */
+/* Define to 1 if you have the <execinfo.h> header file. */
+#mesondefine HAVE_EXECINFO_H
#endif
# recursively expanded use the := operator instead of the = operator.
# This tag requires that the tag ENABLE_PREPROCESSING is set to YES.
-PREDEFINED = HAVE_ARCHIVE=1 \
+PREDEFINED = DEBUG=1 \
+ HAVE_ARCHIVE=1 \
HAVE_CLUTTER=1 \
HAVE_DJVU=1 \
+ HAVE_EXECINFO_H=1 \
HAVE_EXIV2=1 \
HAVE_FFMPEGTHUMBNAILER=1 \
HAVE_HEIF=1 \
HAVE_J2K=1 \
HAVE_JPEG=1 \
+ HAVE_JPEGXL=1 \
HAVE_LCMS=1 \
HAVE_LCMS2=1 \
HAVE_LIBCHAMPLAIN=1 \
summary({'nl_langinfo' : ['nl_langinfo not found - first weekday depends on locale:', false, 'first weekday defaults to Monday']}, section : 'Documentation', bool_yn : true)
endif
+# Required only for backtrace debugging
+result = cc.check_header('execinfo.h')
+if result
+ conf_data.set('HAVE_EXECINFO_H', 1)
+endif
+
conf_data.set_quoted('GETTEXT_PACKAGE', meson.project_name())
conf_data.set_quoted('GQ_APPDIR', gq_appdir)
conf_data.set_quoted('GQ_BINDIR', gq_bindir)
#include "main.h"
#include "debug.h"
+#include "filedata.h"
#include "logwindow.h"
+#include "misc.h"
#include "ui-fileops.h"
+#ifdef HAVE_EXECINFO_H
+#include <execinfo.h>
+#endif
+
#include <regex.h>
/*
return g_strdup(regexp);
}
+#ifdef HAVE_EXECINFO_H
+/**
+ * @brief Backtrace of geeqie files
+ * @param file
+ * @param function
+ * @param line
+ *
+ * Requires command line program addr2line \n
+ * Prints the contents of the backtrace buffer for Geeqie files. \n
+ * Format printed is: \n
+ * <full path to source file>:<line number>
+ *
+ * The log window F1 command and options->log_window.action may be used
+ * to open an editor at a backtrace location.
+ */
+void log_print_backtrace(const gchar *file, const gchar *function, gint line)
+{
+ FILE *fp;
+ char **bt_syms;
+ char path[2048];
+ gchar *address_offset;
+ gchar *cmd_line;
+ gchar *exe_path;
+ gchar *function_name;
+ gchar *paren_end;
+ gchar *paren_start;
+ gint bt_size;
+ gint i;
+ void *bt[1024];
+
+ if (runcmd((gchar *)"which addr2line >/dev/null 2>&1") == 0)
+ {
+ exe_path = g_path_get_dirname(gq_executable_path);
+ bt_size = backtrace(bt, 1024);
+ bt_syms = backtrace_symbols(bt, bt_size);
+
+ log_printf("Backtrace start");
+ log_printf("%s/../%s:%d %s\n", exe_path, file, line, function);
+
+ /* Last item is always "??:?", so ignore it */
+ for (i = 1; i < bt_size - 1; i++)
+ {
+ if (strstr(bt_syms[i], GQ_APPNAME_LC))
+ {
+ paren_start = g_strstr_len(bt_syms[i], -1, "(");
+ paren_end = g_strstr_len(bt_syms[i], -1, ")");
+ address_offset = g_strndup(paren_start + 1, paren_end - paren_start - 1);
+
+ cmd_line = g_strconcat("addr2line -p -f -C -e ", gq_executable_path, " ", address_offset, NULL);
+
+ fp = popen(cmd_line, "r");
+ if (fp == NULL)
+ {
+ log_printf("Failed to run command: %s", cmd_line);
+ }
+ else
+ {
+ while (fgets(path, sizeof(path), fp) != NULL)
+ {
+ /* Remove redundant newline */
+ path[strlen(path) - 1] = '\0';
+
+ function_name = g_strndup(path, g_strstr_len(path, strlen(path), "(") - path);
+
+ log_printf("%s %s", g_strstr_len(path, -1, "at ") + 3, function_name);
+
+ g_free(function_name);
+ }
+ }
+
+ pclose(fp);
+
+ g_free(address_offset);
+ g_free(cmd_line);
+ }
+ }
+ log_printf("Backtrace end");
+
+ free(bt_syms);
+ g_free(exe_path);
+ }
+}
+#else
+void log_print_backtrace(const gchar *file, const gchar *function, gint line)
+{
+}
+#endif
+
+/**
+ * @brief Print ref. count and image name
+ * @param file
+ * @param function
+ * @param line
+ *
+ * Print image ref. count and full path name of all images in
+ * the file_data_pool.
+ */
+void log_print_file_data_dump(const gchar *file, const gchar *function, gint line)
+{
+ gchar *exe_path;
+
+ exe_path = g_path_get_dirname(gq_executable_path);
+
+ log_printf("FileData dump start");
+ log_printf("%s/../%s:%d %s\n", exe_path, file, line, function);
+
+ file_data_dump();
+
+ log_printf("FileData dump end");
+
+ g_free(exe_path);
+}
+
#endif /* DEBUG */
/* vim: set shiftwidth=8 softtabstop=0 cindent cinoptions={1s: */
void log_domain_printf(const gchar *domain, const gchar *format, ...) G_GNUC_PRINTF(2, 3);
void log_domain_print_debug(const gchar *domain, const gchar *file_name, const gchar *function_name,
int line_number, const gchar *format, ...) G_GNUC_PRINTF(5, 6);
+void log_print_file_data_dump(const gchar *file, const gchar *function_name, gint line_number);
+void log_print_backtrace(const gchar *file, const gchar *function_name, gint line_number);
+
#define log_printf(...) log_domain_printf(DOMAIN_INFO, __VA_ARGS__)
#ifdef DEBUG
gtk_widget_set_name(GTK_WIDGET(widget), g_strdup_printf("%s:%d", __FILE__, __LINE__)); \
} while(0)
+#define DEBUG_BT() do \
+ { \
+ log_print_backtrace(__FILE__, __func__, __LINE__); \
+ } while(0)
+
+#define DEBUG_FD() do \
+ { \
+ log_print_file_data_dump(__FILE__, __func__, __LINE__); \
+ } while(0)
#else /* DEBUG */
#define get_regexp() (0)
#define DEBUG_N(n, ...) do { } while(0)
#define DEBUG_NAME(widget) do { } while(0)
+#define DEBUG_BT() do { } while(0)
+#define DEBUG_FD() do { } while(0)
#endif /* DEBUG */
return fd;
}
+/**
+ * @brief Print ref. count and image name
+ * @param
+ *
+ * Print image ref. count and full path name of all images in
+ * the file_data_pool.
+ *
+ * Used only by DEBUG_FD()
+ */
+void file_data_dump()
+{
+ FileData *fd;
+ GList *list;
+
+ if (file_data_pool)
+ {
+ list = g_hash_table_get_values(file_data_pool);
+
+ log_printf("%d", global_file_data_count);
+ log_printf("%d", g_list_length(list));
+
+ while (list)
+ {
+ fd = list->data;
+ log_printf("%-4d %s", fd->ref, fd->path);
+ list = list->next;
+ }
+
+ g_list_free(list);
+ }
+}
+
static void file_data_free(FileData *fd)
{
g_assert(fd->magick == FD_MAGICK);
void file_data_dec_page_num(FileData *fd);
void file_data_set_page_total(FileData *fd, gint page_total);
void file_data_set_page_num(FileData *fd, gint page_num);
+
+void file_data_dump();
#endif
/* vim: set shiftwidth=8 softtabstop=0 cindent cinoptions={1s: */
{
}
-static gboolean key_pressed(GtkWidget *UNUSED(widget), GdkEventKey *event,
- LogWindow *logwin)
+static gboolean iter_char_search_cb(gunichar ch, gpointer data)
{
+ gboolean ret;
+
+ if (ch == GPOINTER_TO_UINT(data))
+ {
+ ret = TRUE;
+ }
+ else
+ {
+ ret = FALSE;
+ }
+
+ return ret;
+}
+
+/**
+ * @brief Handle escape and F1 keys
+ * @param UNUSED
+ * @param event
+ * @param logwin
+ * @returns
+ *
+ * If escape key pressed, hide log window. \n
+ * If no text selected, form a selection bounded by space characters or
+ * start and end of line. \n
+ * If F1 pressed, execute command line program: \n
+ * <options->log_window.action> <selected text>
+ *
+*/
+static gboolean key_pressed(GtkWidget *UNUSED(widget), GdkEventKey *event, LogWindow *logwin)
+{
+ GtkTextBuffer *buffer;
+ GtkTextIter chr_end;
+ GtkTextIter chr_start;
+ GtkTextIter cursor_iter;
+ GtkTextIter line_end;
+ GtkTextIter line_start;
+ GtkTextMark *cursor_mark;
+ gchar *cmd_line;
+ gchar *sel_text;
+
if (event && event->keyval == GDK_KEY_Escape)
gtk_widget_hide(logwin->window);
+
+ if (event && event->keyval == GDK_KEY_F1)
+ {
+ if (strlen(options->log_window.action) > 0)
+ {
+ buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(logwin->text));
+
+ if (!gtk_text_buffer_get_has_selection(buffer))
+ {
+ cursor_mark = gtk_text_buffer_get_insert(buffer);
+ gtk_text_buffer_get_iter_at_mark(buffer, &cursor_iter, cursor_mark);
+
+ line_start = cursor_iter;
+ gtk_text_iter_set_line_offset(&line_start, 0);
+ line_end = cursor_iter;
+ gtk_text_iter_forward_to_line_end(&line_end);
+
+ chr_start = cursor_iter;
+ gtk_text_iter_backward_find_char(&chr_start, (GtkTextCharPredicate)iter_char_search_cb, GUINT_TO_POINTER(' '), &line_start);
+
+ chr_end = cursor_iter;
+ gtk_text_iter_forward_find_char(&chr_end, (GtkTextCharPredicate)iter_char_search_cb, GUINT_TO_POINTER(' '), &line_end);
+
+ gtk_text_buffer_select_range(buffer, &chr_start, &chr_end);
+ }
+
+ if (gtk_text_buffer_get_selection_bounds(gtk_text_view_get_buffer(GTK_TEXT_VIEW(logwin->text)), &chr_start, &chr_end))
+ {
+ sel_text = gtk_text_buffer_get_text( gtk_text_view_get_buffer(GTK_TEXT_VIEW(logwin->text)), &chr_start, &chr_end, FALSE);
+
+ cmd_line = g_strconcat(options->log_window.action, " ", sel_text, NULL);
+
+ runcmd(cmd_line);
+
+ g_free(sel_text);
+ g_free(cmd_line);
+ }
+ }
+ }
+
return FALSE;
}
options->log_window.line_wrap = FALSE;
options->log_window.paused = FALSE;
options->log_window.timer_data = FALSE;
+ options->log_window.action = g_strdup("");
options->read_metadata_in_idle = FALSE;
options->star_rating.star = STAR_RATING_STAR;
gboolean paused;
gboolean line_wrap;
gboolean timer_data;
+ gchar *action; /** Used with F1 key */
} log_window;
/* star rating */
WRITE_NL(); WRITE_UINT(*options, log_window_lines);
WRITE_NL(); WRITE_BOOL(*options, log_window.timer_data);
+ WRITE_NL(); WRITE_CHAR(*options, log_window.action);
WRITE_NL(); WRITE_BOOL(*options, appimage_notifications);
WRITE_NL(); WRITE_BOOL(*options, marks_save);
if (READ_INT(*options, log_window_lines)) continue;
if (READ_BOOL(*options, log_window.timer_data)) continue;
+ if (READ_CHAR(*options, log_window.action)) continue;
if (READ_BOOL(*options, appimage_notifications)) continue;
if (READ_BOOL(*options, marks_save)) continue;