2 * @file gntdebug.c GNT Debug API
8 * Finch is the legal property of its developers, whose names are too numerous
9 * to list here. Please refer to the COPYRIGHT file distributed with this
10 * source distribution.
12 * This program is free software; you can redistribute it and/or modify
13 * it under the terms of the GNU General Public License as published by
14 * the Free Software Foundation; either version 2 of the License, or
15 * (at your option) any later version.
17 * This program is distributed in the hope that it will be useful,
18 * but WITHOUT ANY WARRANTY; without even the implied warranty of
19 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20 * GNU General Public License for more details.
22 * You should have received a copy of the GNU General Public License
23 * along with this program; if not, write to the Free Software
24 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA
28 #include <gntbutton.h>
29 #include <gntcheckbox.h>
31 #include <gntfilesel.h>
34 #include <gnttextview.h>
45 #define PREF_ROOT "/finch/debug"
48 handle_fprintf_stderr_cb(GIOChannel
*source
, GIOCondition cond
, gpointer null
)
53 size
= read(g_io_channel_unix_get_fd(source
), message
, sizeof(message
) - 1);
55 /* Something bad probably happened elsewhere ... let's ignore */
58 g_log("stderr", G_LOG_LEVEL_WARNING
, "%s", message
);
65 handle_fprintf_stderr(gboolean stop
)
68 static int readhandle
= -1;
72 if (readhandle
>= 0) {
73 g_source_remove(readhandle
);
79 dup2(pipes
[1], STDERR_FILENO
);
81 stderrch
= g_io_channel_unix_new(pipes
[0]);
82 g_io_channel_set_close_on_unref(stderrch
, TRUE
);
83 readhandle
= g_io_add_watch_full(stderrch
, G_PRIORITY_HIGH
,
84 G_IO_IN
| G_IO_ERR
| G_IO_PRI
,
85 handle_fprintf_stderr_cb
, NULL
, NULL
);
86 g_io_channel_unref(stderrch
);
98 match_string(const char *category
, const char *args
)
100 const char *str
= gnt_entry_get_text(GNT_ENTRY(debug
.search
));
103 if (g_strrstr(category
, str
) != NULL
)
105 if (g_strrstr(args
, str
) != NULL
)
111 finch_debug_print(PurpleDebugLevel level
, const char *category
,
114 if (debug
.window
&& !debug
.paused
&& match_string(category
, args
))
116 int pos
= gnt_text_view_get_lines_below(GNT_TEXT_VIEW(debug
.tview
));
117 GntTextFormatFlags flag
= GNT_TEXT_FLAG_NORMAL
;
119 time_t mtime
= time(NULL
);
120 mdate
= purple_utf8_strftime("%H:%M:%S ", localtime(&mtime
));
121 gnt_text_view_append_text_with_flags(GNT_TEXT_VIEW(debug
.tview
),
124 gnt_text_view_append_text_with_flags(GNT_TEXT_VIEW(debug
.tview
),
125 category
, GNT_TEXT_FLAG_BOLD
);
126 gnt_text_view_append_text_with_flags(GNT_TEXT_VIEW(debug
.tview
),
127 ": ", GNT_TEXT_FLAG_BOLD
);
131 case PURPLE_DEBUG_WARNING
:
132 flag
|= GNT_TEXT_FLAG_UNDERLINE
;
133 case PURPLE_DEBUG_ERROR
:
134 case PURPLE_DEBUG_FATAL
:
135 flag
|= GNT_TEXT_FLAG_BOLD
;
141 gnt_text_view_append_text_with_flags(GNT_TEXT_VIEW(debug
.tview
), args
, flag
);
143 gnt_text_view_scroll(GNT_TEXT_VIEW(debug
.tview
), 0);
148 finch_debug_is_enabled(PurpleDebugLevel level
, const char *category
)
150 return debug
.window
&& !debug
.paused
;
153 static PurpleDebugUiOps uiops
=
156 finch_debug_is_enabled
,
165 PurpleDebugUiOps
*finch_debug_get_ui_ops()
171 reset_debug_win(GntWidget
*w
, gpointer null
)
173 debug
.window
= debug
.tview
= debug
.search
= NULL
;
177 clear_debug_win(GntWidget
*w
, GntTextView
*tv
)
179 gnt_text_view_clear(tv
);
183 print_stderr(const char *string
)
185 g_printerr("%s", string
);
189 toggle_pause(GntWidget
*w
, gpointer n
)
191 debug
.paused
= !debug
.paused
;
196 purple_glib_log_handler(const gchar
*domain
, GLogLevelFlags flags
,
197 const gchar
*msg
, gpointer user_data
)
199 PurpleDebugLevel level
;
200 char *new_msg
= NULL
;
201 char *new_domain
= NULL
;
203 if ((flags
& G_LOG_LEVEL_ERROR
) == G_LOG_LEVEL_ERROR
)
204 level
= PURPLE_DEBUG_ERROR
;
205 else if ((flags
& G_LOG_LEVEL_CRITICAL
) == G_LOG_LEVEL_CRITICAL
)
206 level
= PURPLE_DEBUG_FATAL
;
207 else if ((flags
& G_LOG_LEVEL_WARNING
) == G_LOG_LEVEL_WARNING
)
208 level
= PURPLE_DEBUG_WARNING
;
209 else if ((flags
& G_LOG_LEVEL_MESSAGE
) == G_LOG_LEVEL_MESSAGE
)
210 level
= PURPLE_DEBUG_INFO
;
211 else if ((flags
& G_LOG_LEVEL_INFO
) == G_LOG_LEVEL_INFO
)
212 level
= PURPLE_DEBUG_INFO
;
213 else if ((flags
& G_LOG_LEVEL_DEBUG
) == G_LOG_LEVEL_DEBUG
)
214 level
= PURPLE_DEBUG_MISC
;
217 purple_debug_warning("gntdebug",
218 "Unknown glib logging level in %d\n", flags
);
220 level
= PURPLE_DEBUG_MISC
; /* This will never happen. */
224 new_msg
= purple_utf8_try_convert(msg
);
227 new_domain
= purple_utf8_try_convert(domain
);
231 purple_debug(level
, (new_domain
!= NULL
? new_domain
: "g_log"),
241 size_changed_cb(GntWidget
*widget
, int oldw
, int oldh
)
244 gnt_widget_get_size(widget
, &w
, &h
);
245 purple_prefs_set_int(PREF_ROOT
"/size/width", w
);
246 purple_prefs_set_int(PREF_ROOT
"/size/height", h
);
250 for_real(gpointer entry
)
252 purple_prefs_set_string(PREF_ROOT
"/filter", gnt_entry_get_text(entry
));
257 update_filter_string(GntEntry
*entry
, gpointer null
)
259 int id
= g_timeout_add(1000, for_real
, entry
);
260 g_object_set_data_full(G_OBJECT(entry
), "update-filter", GINT_TO_POINTER(id
),
261 (GDestroyNotify
)g_source_remove
);
265 file_save(GntFileSel
*fs
, const char *path
, const char *file
, GntTextView
*tv
)
269 if ((fp
= g_fopen(path
, "w+")) == NULL
) {
270 purple_notify_error(NULL
, NULL
, _("Unable to open file."), NULL
);
274 fprintf(fp
, "Finch Debug Log : %s\n", purple_date_format_full(NULL
));
275 fprintf(fp
, "%s", tv
->string
->str
);
277 gnt_widget_destroy(GNT_WIDGET(fs
));
281 file_cancel(GntWidget
*w
, GntFileSel
*fs
)
283 gnt_widget_destroy(GNT_WIDGET(fs
));
287 save_debug_win(GntWidget
*w
, GntTextView
*tv
)
289 GntWidget
*window
= gnt_file_sel_new();
290 GntFileSel
*sel
= GNT_FILE_SEL(window
);
291 gnt_file_sel_set_current_location(sel
, purple_home_dir());
292 gnt_file_sel_set_suggested_filename(sel
, "debug.txt");
293 g_signal_connect(G_OBJECT(sel
), "file_selected", G_CALLBACK(file_save
), tv
);
294 g_signal_connect(G_OBJECT(sel
->cancel
), "activate", G_CALLBACK(file_cancel
), sel
);
295 gnt_widget_show(window
);
298 void finch_debug_window_show()
300 GntWidget
*wid
, *box
, *label
;
302 debug
.paused
= FALSE
;
304 gnt_window_present(debug
.window
);
308 debug
.window
= gnt_vbox_new(FALSE
);
309 gnt_box_set_toplevel(GNT_BOX(debug
.window
), TRUE
);
310 gnt_box_set_title(GNT_BOX(debug
.window
), _("Debug Window"));
311 gnt_box_set_pad(GNT_BOX(debug
.window
), 0);
312 gnt_box_set_alignment(GNT_BOX(debug
.window
), GNT_ALIGN_MID
);
314 debug
.tview
= gnt_text_view_new();
315 gnt_box_add_widget(GNT_BOX(debug
.window
), debug
.tview
);
316 gnt_widget_set_size(debug
.tview
,
317 purple_prefs_get_int(PREF_ROOT
"/size/width"),
318 purple_prefs_get_int(PREF_ROOT
"/size/height"));
319 g_signal_connect(G_OBJECT(debug
.tview
), "size_changed", G_CALLBACK(size_changed_cb
), NULL
);
321 gnt_box_add_widget(GNT_BOX(debug
.window
), gnt_line_new(FALSE
));
323 box
= gnt_hbox_new(FALSE
);
324 gnt_box_set_alignment(GNT_BOX(box
), GNT_ALIGN_MID
);
325 gnt_box_set_fill(GNT_BOX(box
), FALSE
);
327 /* XXX: Setting the GROW_Y for the following widgets don't make sense. But right now
328 * it's necessary to make the width of the debug window resizable ... like I said,
329 * it doesn't make sense. The bug is likely in the packing in gntbox.c.
331 wid
= gnt_button_new(_("Clear"));
332 g_signal_connect(G_OBJECT(wid
), "activate", G_CALLBACK(clear_debug_win
), debug
.tview
);
333 GNT_WIDGET_SET_FLAGS(wid
, GNT_WIDGET_GROW_Y
);
334 gnt_box_add_widget(GNT_BOX(box
), wid
);
336 wid
= gnt_button_new(_("Save"));
337 g_signal_connect(G_OBJECT(wid
), "activate", G_CALLBACK(save_debug_win
), debug
.tview
);
338 GNT_WIDGET_SET_FLAGS(wid
, GNT_WIDGET_GROW_Y
);
339 gnt_box_add_widget(GNT_BOX(box
), wid
);
341 debug
.search
= gnt_entry_new(purple_prefs_get_string(PREF_ROOT
"/filter"));
342 label
= gnt_label_new(_("Filter:"));
343 GNT_WIDGET_UNSET_FLAGS(label
, GNT_WIDGET_GROW_X
);
344 gnt_box_add_widget(GNT_BOX(box
), label
);
345 gnt_box_add_widget(GNT_BOX(box
), debug
.search
);
346 g_signal_connect(G_OBJECT(debug
.search
), "text_changed", G_CALLBACK(update_filter_string
), NULL
);
348 wid
= gnt_check_box_new(_("Pause"));
349 g_signal_connect(G_OBJECT(wid
), "toggled", G_CALLBACK(toggle_pause
), NULL
);
350 GNT_WIDGET_SET_FLAGS(wid
, GNT_WIDGET_GROW_Y
);
351 gnt_box_add_widget(GNT_BOX(box
), wid
);
353 gnt_box_add_widget(GNT_BOX(debug
.window
), box
);
354 GNT_WIDGET_SET_FLAGS(box
, GNT_WIDGET_GROW_Y
);
356 gnt_widget_set_name(debug
.window
, "debug-window");
358 g_signal_connect(G_OBJECT(debug
.window
), "destroy", G_CALLBACK(reset_debug_win
), NULL
);
359 gnt_text_view_attach_scroll_widget(GNT_TEXT_VIEW(debug
.tview
), debug
.window
);
360 gnt_text_view_attach_pager_widget(GNT_TEXT_VIEW(debug
.tview
), debug
.window
);
362 gnt_widget_show(debug
.window
);
366 start_with_debugwin(gpointer null
)
368 finch_debug_window_show();
372 void finch_debug_init()
375 #define REGISTER_G_LOG_HANDLER(name) \
376 g_log_set_handler((name), G_LOG_LEVEL_MASK | G_LOG_FLAG_FATAL \
377 | G_LOG_FLAG_RECURSION, \
378 purple_glib_log_handler, NULL)
380 /* Register the glib log handlers. */
381 REGISTER_G_LOG_HANDLER(NULL
);
382 REGISTER_G_LOG_HANDLER("GLib");
383 REGISTER_G_LOG_HANDLER("GModule");
384 REGISTER_G_LOG_HANDLER("GLib-GObject");
385 REGISTER_G_LOG_HANDLER("GThread");
386 REGISTER_G_LOG_HANDLER("Gnt");
388 REGISTER_G_LOG_HANDLER("GStreamer");
390 REGISTER_G_LOG_HANDLER("stderr");
392 g_set_print_handler(print_stderr
); /* Redirect the debug messages to stderr */
393 if (!purple_debug_is_enabled())
394 handle_fprintf_stderr(FALSE
);
396 purple_prefs_add_none(PREF_ROOT
);
397 purple_prefs_add_string(PREF_ROOT
"/filter", "");
398 purple_prefs_add_none(PREF_ROOT
"/size");
399 purple_prefs_add_int(PREF_ROOT
"/size/width", 60);
400 purple_prefs_add_int(PREF_ROOT
"/size/height", 15);
402 if (purple_debug_is_enabled())
403 g_timeout_add(0, start_with_debugwin
, NULL
);
406 void finch_debug_uninit()
408 handle_fprintf_stderr(TRUE
);