aboutsummaryrefslogtreecommitdiff
path: root/util.c
diff options
context:
space:
mode:
Diffstat (limited to 'util.c')
-rw-r--r--util.c932
1 files changed, 908 insertions, 24 deletions
diff --git a/util.c b/util.c
index ee6040b2..27e3758d 100644
--- a/util.c
+++ b/util.c
@@ -1,6 +1,6 @@
/*
* HardInfo - Displays System Information
- * Copyright (C) 2003-2006 Leandro A. F. Pereira <leandro@linuxmag.com.br>
+ * Copyright (C) 2003-2007 Leandro A. F. Pereira <leandro@linuxmag.com.br>
*
* 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
@@ -15,16 +15,55 @@
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
+#include <config.h>
+
+#include <report.h>
#include <string.h>
+#include <shell.h>
+#include <iconcache.h>
#include <hardinfo.h>
#include <gtk/gtk.h>
+#include <binreloc.h>
+
+#include <sys/stat.h>
+#include <sys/types.h>
+
#define KiB 1024
#define MiB 1048576
#define GiB 1073741824
-inline gchar *
-size_human_readable(gfloat size)
+gchar *seconds_to_string(unsigned int seconds)
+{
+ unsigned int hours, minutes, days;
+
+ minutes = seconds / 60;
+ hours = minutes / 60;
+ minutes %= 60;
+ days = hours / 24;
+ hours %= 24;
+
+#define plural(x) ((x > 1) ? "s" : "")
+
+ if (days < 1) {
+ if (hours < 1) {
+ return g_strdup_printf("%d minute%s", minutes,
+ plural(minutes));
+ } else {
+ return g_strdup_printf("%d hour%s, %d minute%s",
+ hours,
+ plural(hours), minutes,
+ plural(minutes));
+ }
+ }
+
+ return g_strdup_printf("%d day%s, %d hour%s and %d minute%s",
+ days, plural(days), hours,
+ plural(hours), minutes,
+ plural(minutes));
+}
+
+inline gchar *size_human_readable(gfloat size)
{
if (size < KiB)
return g_strdup_printf("%.1f B", size);
@@ -36,44 +75,889 @@ size_human_readable(gfloat size)
return g_strdup_printf("%.1f GiB", size / GiB);
}
-inline void
-strend(gchar *str, gchar chr)
+inline void strend(gchar * str, gchar chr)
{
if (!str)
- return;
-
+ return;
+
char *p;
if ((p = strchr(str, chr)))
- *p = 0;
+ *p = 0;
}
-inline void
-remove_quotes(gchar *str)
+inline void remove_quotes(gchar * str)
{
if (!str)
- return;
+ return;
while (*str == '"')
- *(str++) = ' ';
-
+ *(str++) = ' ';
+
strend(str, '"');
}
-inline void
-remove_linefeed(gchar * str)
+inline void remove_linefeed(gchar * str)
{
strend(str, '\n');
}
-void
-widget_set_cursor(GtkWidget *widget, GdkCursorType cursor_type)
-{
- GdkCursor *cursor;
+void widget_set_cursor(GtkWidget * widget, GdkCursorType cursor_type)
+{
+ GdkCursor *cursor;
+
+ cursor = gdk_cursor_new(cursor_type);
+ gdk_window_set_cursor(GDK_WINDOW(widget->window), cursor);
+ gdk_cursor_unref(cursor);
+
+ while (gtk_events_pending())
+ gtk_main_iteration();
+}
+
+static gboolean __nonblock_cb(gpointer data)
+{
+ gtk_main_quit();
+ return FALSE;
+}
+
+void nonblock_sleep(guint msec)
+{
+ g_timeout_add(msec, (GSourceFunc) __nonblock_cb, NULL);
+ gtk_main();
+}
+
+static void __expand_cb(GtkWidget * widget, gpointer data)
+{
+ if (GTK_IS_EXPANDER(widget)) {
+ gtk_expander_set_expanded(GTK_EXPANDER(widget), TRUE);
+ } else if (GTK_IS_CONTAINER(widget)) {
+ gtk_container_foreach(GTK_CONTAINER(widget),
+ (GtkCallback) __expand_cb, NULL);
+ }
+}
+
+void file_chooser_open_expander(GtkWidget * chooser)
+{
+ gtk_container_foreach(GTK_CONTAINER(chooser),
+ (GtkCallback) __expand_cb, NULL);
+}
+
+void file_chooser_add_filters(GtkWidget * chooser, FileTypes * filters)
+{
+ GtkFileFilter *filter;
+ gint i;
+
+ for (i = 0; filters[i].name; i++) {
+ filter = gtk_file_filter_new();
+ gtk_file_filter_add_mime_type(filter, filters[i].mime_type);
+ gtk_file_filter_set_name(filter, filters[i].name);
+ gtk_file_chooser_add_filter(GTK_FILE_CHOOSER(chooser), filter);
+ }
+}
+
+gchar *
+file_chooser_get_extension(GtkWidget * chooser, FileTypes * filters)
+{
+ GtkFileFilter *filter;
+ const gchar *filter_name;
+ gint i;
+
+ filter = gtk_file_chooser_get_filter(GTK_FILE_CHOOSER(chooser));
+ filter_name = gtk_file_filter_get_name(filter);
+ for (i = 0; filters[i].name; i++) {
+ if (g_str_equal(filter_name, filters[i].name)) {
+ return filters[i].extension;
+ }
+ }
+
+ return NULL;
+}
+
+gpointer file_types_get_data_by_name(FileTypes * filters, gchar * filename)
+{
+ gint i;
+
+ for (i = 0; filters[i].name; i++) {
+ if (g_str_has_suffix(filename, filters[i].extension)) {
+ return filters[i].data;
+ }
+ }
+
+ return NULL;
+}
+
+gchar * file_chooser_build_filename(GtkWidget * chooser, gchar * extension)
+{
+ gchar *filename =
+ gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(chooser));
+ gchar *retval;
+
+ if (g_str_has_suffix(filename, extension)) {
+ return filename;
+ }
+
+ retval = g_strconcat(filename, extension, NULL);
+ g_free(filename);
+
+ return retval;
+}
+
+gboolean binreloc_init(gboolean try_hardcoded)
+{
+ GError *error = NULL;
+ gchar *tmp;
+
+ DEBUG("initializing binreloc (hardcoded = %d)", try_hardcoded);
+
+ /* If the runtime data directories we previously found, don't even try
+ to find them again. */
+ if (params.path_data && params.path_lib) {
+ DEBUG("data and lib path already found.");
+ return TRUE;
+ }
+
+ if (try_hardcoded || !gbr_init(&error)) {
+ /* We were asked to try hardcoded paths or BinReloc failed to initialize. */
+ params.path_data = g_strdup(PREFIX);
+ params.path_lib = g_strdup(LIBPREFIX);
+
+ if (error) {
+ g_error_free(error);
+ }
+
+ DEBUG("%strying hardcoded paths.", try_hardcoded ? "" : "binreloc init failed. ");
+ } else {
+ /* If we were able to initialize BinReloc, build the default data
+ and library paths. */
+ DEBUG("done, trying to use binreloc paths.");
+
+ tmp = gbr_find_data_dir(PREFIX);
+ params.path_data = g_build_filename(tmp, "hardinfo", NULL);
+ g_free(tmp);
+
+ tmp = gbr_find_lib_dir(PREFIX);
+ params.path_lib = g_build_filename(tmp, "hardinfo", NULL);
+ g_free(tmp);
+ }
+
+ DEBUG("searching for runtime data on these locations:");
+ DEBUG(" lib: %s", params.path_lib);
+ DEBUG(" data: %s", params.path_data);
+
+ /* Try to see if the uidefs.xml file isn't missing. This isn't the
+ definitive test, but it should do okay for most situations. */
+ tmp = g_build_filename(params.path_data, "benchmark.data", NULL);
+ if (!g_file_test(tmp, G_FILE_TEST_EXISTS)) {
+ DEBUG("runtime data not found");
+
+ g_free(params.path_data);
+ g_free(params.path_lib);
+ g_free(tmp);
+
+ params.path_data = params.path_lib = NULL;
+
+ if (try_hardcoded) {
+ /* We tried the hardcoded paths, but still was unable to find the
+ runtime data. Give up. */
+ DEBUG("giving up");
+ return FALSE;
+ } else {
+ /* Even though BinReloc worked OK, the runtime data was not found.
+ Try the hardcoded paths. */
+ DEBUG("trying to find elsewhere");
+ return binreloc_init(TRUE);
+ }
+ }
+ g_free(tmp);
+
+ DEBUG("runtime data found!");
+ /* We found the runtime data; hope everything is fine */
+ return TRUE;
+}
+
+static void
+log_handler(const gchar * log_domain,
+ GLogLevelFlags log_level,
+ const gchar * message, gpointer user_data)
+{
+ if (!params.gui_running) {
+ /* No GUI running: spit the message to the terminal */
+ g_print("\n\n*** %s: %s\n\n",
+ (log_level & G_LOG_FLAG_FATAL) ? "Error" : "Warning",
+ message);
+ } else {
+ /* Hooray! We have a GUI running! */
+ GtkWidget *dialog;
+
+ dialog = gtk_message_dialog_new_with_markup(NULL, GTK_DIALOG_MODAL,
+ (log_level &
+ G_LOG_FLAG_FATAL) ?
+ GTK_MESSAGE_ERROR :
+ GTK_MESSAGE_WARNING,
+ GTK_BUTTONS_CLOSE,
+ "<big><b>%s</b></big>\n\n%s",
+ (log_level &
+ G_LOG_FLAG_FATAL) ?
+ "Fatal Error" :
+ "Warning", message);
+
+ gtk_dialog_run(GTK_DIALOG(dialog));
+ gtk_widget_destroy(dialog);
+ }
+}
+
+void parameters_init(int *argc, char ***argv, ProgramParameters * param)
+{
+ static gboolean create_report = FALSE;
+ static gboolean show_version = FALSE;
+ static gboolean list_modules = FALSE;
+ static gboolean autoload_deps = FALSE;
+ static gchar *report_format = NULL;
+ static gchar **use_modules = NULL;
+
+ static GOptionEntry options[] = {
+ {
+ .long_name = "generate-report",
+ .short_name = 'r',
+ .arg = G_OPTION_ARG_NONE,
+ .arg_data = &create_report,
+ .description = "creates a report and prints to standard output"
+ },
+ {
+ .long_name = "report-format",
+ .short_name = 'f',
+ .arg = G_OPTION_ARG_STRING,
+ .arg_data = &report_format,
+ .description = "chooses a report format (text, html)"
+ },
+ {
+ .long_name = "list-modules",
+ .short_name = 'l',
+ .arg = G_OPTION_ARG_NONE,
+ .arg_data = &list_modules,
+ .description = "lists modules"
+ },
+ {
+ .long_name = "load-module",
+ .short_name = 'm',
+ .arg = G_OPTION_ARG_STRING_ARRAY,
+ .arg_data = &use_modules,
+ .description = "specify module to load"
+ },
+ {
+ .long_name = "autoload-deps",
+ .short_name = 'a',
+ .arg = G_OPTION_ARG_NONE,
+ .arg_data = &autoload_deps,
+ .description = "automatically load module dependencies"
+ },
+ {
+ .long_name = "version",
+ .short_name = 'v',
+ .arg = G_OPTION_ARG_NONE,
+ .arg_data = &show_version,
+ .description = "shows program version and quit"
+ },
+ { NULL }
+ };
+ GOptionContext *ctx;
+
+ ctx = g_option_context_new("- System Profiler and Benchmark tool");
+ g_option_context_set_ignore_unknown_options(ctx, FALSE);
+ g_option_context_set_help_enabled(ctx, TRUE);
- cursor = gdk_cursor_new(cursor_type);
- gdk_window_set_cursor(GDK_WINDOW(widget->window), cursor);
- gdk_cursor_unref(cursor);
+ g_option_context_add_main_entries(ctx, options, *(argv)[0]);
+ g_option_context_parse(ctx, argc, argv, NULL);
+
+ g_option_context_free(ctx);
+
+ if (*argc >= 2) {
+ g_print("Unrecognized arguments.\n"
+ "Try ``%s --help'' for more information.\n",
+ *(argv)[0]);
+ exit(1);
+ }
+
+ param->create_report = create_report;
+ param->report_format = REPORT_FORMAT_TEXT;
+ param->show_version = show_version;
+ param->list_modules = list_modules;
+ param->use_modules = use_modules;
+ param->autoload_deps = autoload_deps;
+
+ if (report_format && g_str_equal(report_format, "html"))
+ param->report_format = REPORT_FORMAT_HTML;
+
+ gchar *confdir = g_build_filename(g_get_home_dir(), ".hardinfo", NULL);
+ if (!g_file_test(confdir, G_FILE_TEST_EXISTS)) {
+ mkdir(confdir, 0744);
+ }
+ g_free(confdir);
+}
+
+gboolean ui_init(int *argc, char ***argv)
+{
+ DEBUG("initializing gtk+ UI");
+
+ g_set_application_name("HardInfo");
+ g_log_set_handler(NULL,
+ G_LOG_LEVEL_WARNING | G_LOG_FLAG_FATAL |
+ G_LOG_LEVEL_ERROR, log_handler, NULL);
+
+ return gtk_init_check(argc, argv);
+}
+
+void open_url(gchar * url)
+{
+ const gchar *browsers[] = {
+ "xdg-open", "gnome-open", "kfmclient openURL",
+ "sensible-browser", "firefox", "epiphany",
+ "iceweasel", "seamonkey", "galeon", "mozilla",
+ "opera", "konqueror", "netscape", "links -g",
+ NULL
+ };
+ gint i;
+
+ for (i = 0; browsers[i]; i++) {
+ gchar *cmdline = g_strdup_printf("%s '%s'", browsers[i], url);
+
+ if (g_spawn_command_line_async(cmdline, NULL)) {
+ g_free(cmdline);
+ return;
+ }
+
+ g_free(cmdline);
+ }
+
+ g_warning("Couldn't find a Web browser to open URL %s.", url);
+}
+
+/* Copyright: Jens Låås, SLU 2002 */
+gchar *strreplace(gchar * string, gchar * replace, gchar new_char)
+{
+ gchar *s;
+ for (s = string; *s; s++)
+ if (strchr(replace, *s))
+ *s = new_char;
+
+ return string;
+}
+
+static GHashTable *__module_methods = NULL;
+
+static void module_register_methods(ShellModule *module)
+{
+ ShellModuleMethod *(*get_methods) (void);
+ gchar *method_name;
+
+ if (__module_methods == NULL) {
+ __module_methods = g_hash_table_new(g_str_hash, g_str_equal);
+ }
+
+ if (g_module_symbol(module->dll, "hi_exported_methods", (gpointer) &get_methods)) {
+ ShellModuleMethod *methods = get_methods();
+
+ while (TRUE) {
+ ShellModuleMethod method = *methods;
+ gchar *name = g_path_get_basename(g_module_name(module->dll));
+
+ strend(name, '.');
+
+ method_name = g_strdup_printf("%s::%s", name, method.name);
+ g_hash_table_insert(__module_methods, method_name, method.function);
+ g_free(name);
+
+ if (!(*(++methods)).name)
+ break;
+ }
+ }
+
+}
+
+gchar *module_call_method(gchar *method)
+{
+ gchar *(*function) (void);
+
+ if (__module_methods == NULL) {
+ return NULL;
+ }
+
+ function = g_hash_table_lookup(__module_methods, method);
+ return function ? g_strdup(function()) :
+ g_strdup_printf("{Unknown method: \"%s\"}", method);
+}
+
+static ShellModule *module_load(gchar *filename)
+{
+ ShellModule *module;
+ gchar *tmp;
+
+ module = g_new0(ShellModule, 1);
+
+ if (params.gui_running) {
+ gchar *tmpicon;
+
+ tmpicon = g_strdup(filename);
+ gchar *dot = g_strrstr(tmpicon, "." G_MODULE_SUFFIX);
+
+ *dot = '\0';
+
+ tmp = g_strdup_printf("%s.png", tmpicon);
+ module->icon = icon_cache_get_pixbuf(tmp);
+
+ g_free(tmp);
+ g_free(tmpicon);
+ }
+
+ tmp = g_build_filename(params.path_lib, "modules", filename, NULL);
+ module->dll = g_module_open(tmp, G_MODULE_BIND_LAZY);
+ g_free(tmp);
+
+ if (module->dll) {
+ void (*init) (void);
+ ModuleEntry *(*get_module_entries) (void);
+ gint (*weight_func) (void);
+ gchar *(*name_func) (void);
+ ModuleEntry *entries;
+ gint i = 0;
+
+ if (!g_module_symbol(module->dll, "hi_module_get_entries", (gpointer) & get_module_entries) ||
+ !g_module_symbol(module->dll, "hi_module_get_name", (gpointer) & name_func)) {
+ goto failed;
+ }
+
+ if (g_module_symbol(module->dll, "hi_module_init", (gpointer) & init)) {
+ init();
+ }
+
+ g_module_symbol(module->dll, "hi_module_get_weight", (gpointer) & weight_func);
+
+ module->weight = weight_func ? weight_func() : 0;
+ module->name = name_func();
+
+ entries = get_module_entries();
+ while (entries[i].name) {
+ ShellModuleEntry *entry = g_new0(ShellModuleEntry, 1);
+
+ if (params.gui_running) {
+ entry->icon = icon_cache_get_pixbuf(entries[i].icon);
+ }
+
+ g_module_symbol(module->dll, "hi_more_info",
+ (gpointer) & (entry->morefunc));
+ g_module_symbol(module->dll, "hi_get_field",
+ (gpointer) & (entry->fieldfunc));
+ g_module_symbol(module->dll, "hi_note_func",
+ (gpointer) & (entry->notefunc));
+
+ entry->name = entries[i].name;
+ entry->scan_func = entries[i].scan_callback;
+ entry->func = entries[i].callback;
+ entry->number = i;
+
+ module->entries = g_slist_append(module->entries, entry);
+
+ i++;
+ }
+
+ module_register_methods(module);
+ } else {
+ failed:
+ DEBUG("loading module %s failed", filename);
- while(gtk_events_pending())
- gtk_main_iteration();
+ g_free(module->name);
+ g_free(module);
+ module = NULL;
+ }
+
+ return module;
+}
+
+static gboolean module_in_module_list(gchar *module, gchar **module_list)
+{
+ int i = 0;
+
+ if (!module_list)
+ return TRUE;
+
+ for (; module_list[i]; i++) {
+ if (g_str_equal(module_list[i], module))
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
+static gint module_cmp(gconstpointer m1, gconstpointer m2)
+{
+ ShellModule *a = (ShellModule *)m1;
+ ShellModule *b = (ShellModule *)m2;
+
+ return a->weight - b->weight;
+}
+
+static void module_entry_free(gpointer data, gpointer user_data)
+{
+ ShellModuleEntry *entry = (ShellModuleEntry *)data;
+
+ if (entry) {
+ /*g_free(entry->name);*/
+ g_object_unref(entry->icon);
+
+ g_free(entry);
+ }
+}
+
+static void module_free(ShellModule *module)
+{
+ g_free(module->name);
+ g_object_unref(module->icon);
+ /*g_module_close(module->dll);*/
+
+ DEBUG("module_free: module->entries, %p\n", module->entries);
+ g_slist_foreach(module->entries, (GFunc)module_entry_free, NULL);
+ g_slist_free(module->entries);
+
+ g_free(module);
+}
+
+ModuleAbout *module_get_about(ShellModule *module)
+{
+ ModuleAbout *(*get_about)(void);
+
+ if (g_module_symbol(module->dll, "hi_module_get_about",
+ (gpointer) &get_about)) {
+ return get_about();
+ }
+
+ return NULL;
+}
+
+static GSList *modules_check_deps(GSList *modules)
+{
+ GSList *mm;
+ ShellModule *module;
+
+ for (mm = modules; mm; mm = mm->next) {
+ gchar **(*get_deps)(void);
+ gchar **deps;
+ gint i;
+
+ module = (ShellModule *) mm->data;
+
+ if (g_module_symbol(module->dll, "hi_module_get_dependencies",
+ (gpointer) & get_deps)) {
+ for (i = 0, deps = get_deps(); deps[i]; i++) {
+ GSList *l;
+ ShellModule *m;
+ gboolean found = FALSE;
+
+ for (l = modules; l; l = l->next) {
+ m = (ShellModule *)l->data;
+ gchar *name = g_path_get_basename(g_module_name(m->dll));
+
+ if (g_str_equal(name, deps[i])) {
+ found = TRUE;
+ break;
+ }
+
+ g_free(name);
+ }
+
+ if (!found) {
+ if (params.autoload_deps) {
+ ShellModule *mod = module_load(deps[i]);
+
+ if (mod)
+ modules = g_slist_append(modules, mod);
+ modules = modules_check_deps(modules); /* re-check dependencies */
+
+ break;
+ }
+
+ if (params.gui_running) {
+ GtkWidget *dialog;
+
+ dialog = gtk_message_dialog_new(NULL,
+ GTK_DIALOG_DESTROY_WITH_PARENT,
+ GTK_MESSAGE_QUESTION,
+ GTK_BUTTONS_NONE,
+ "Module \"%s\" depends on module \"%s\", load it?",
+ module->name, deps[i]);
+ gtk_dialog_add_buttons(GTK_DIALOG(dialog),
+ GTK_STOCK_NO, GTK_RESPONSE_REJECT,
+ GTK_STOCK_OPEN, GTK_RESPONSE_ACCEPT,
+ NULL);
+
+ if (gtk_dialog_run(GTK_DIALOG(dialog)) == GTK_RESPONSE_ACCEPT) {
+ ShellModule *mod = module_load(deps[i]);
+
+ if (mod)
+ modules = g_slist_append(modules, mod);
+ modules = modules_check_deps(modules); /* re-check dependencies */
+ } else {
+ modules = g_slist_remove(modules, module);
+ module_free(module);
+ }
+
+ gtk_widget_destroy(dialog);
+ } else {
+ g_error("Module \"%s\" depends on module \"%s\".", module->name, deps[i]);
+ }
+ }
+ }
+ }
+ }
+
+ return modules;
+}
+
+static GSList *modules_load(gchar **module_list)
+{
+ GDir *dir;
+ GSList *modules = NULL;
+ ShellModule *module;
+ gchar *filename;
+
+ filename = g_build_filename(params.path_lib, "modules", NULL);
+ dir = g_dir_open(filename, 0, NULL);
+ g_free(filename);
+
+ if (dir) {
+ while ((filename = (gchar*)g_dir_read_name(dir))) {
+ if (g_strrstr(filename, "." G_MODULE_SUFFIX) &&
+ module_in_module_list(filename, module_list) &&
+ ((module = module_load(filename)))) {
+ modules = g_slist_append(modules, module);
+ }
+ }
+
+ g_dir_close(dir);
+ }
+
+ modules = modules_check_deps(modules);
+
+ if (g_slist_length(modules) == 0) {
+ if (params.use_modules == NULL) {
+ g_error("No module could be loaded. Check permissions on \"%s\" and try again.",
+ params.path_lib);
+ } else {
+ g_error("No module could be loaded. Please use hardinfo -l to list all avai"
+ "lable modules and try again with a valid module list.");
+
+ }
+ }
+
+ return g_slist_sort(modules, module_cmp);
+}
+
+GSList *modules_load_selected(void)
+{
+ return modules_load(params.use_modules);
+}
+
+GSList *modules_load_all(void)
+{
+ return modules_load(NULL);
+}
+
+gint tree_view_get_visible_height(GtkTreeView *tv)
+{
+ GtkTreePath *path;
+ GdkRectangle rect;
+ GtkTreeIter iter;
+ GtkTreeModel *model = gtk_tree_view_get_model(tv);
+ gint nrows = 1;
+
+ path = gtk_tree_path_new_first();
+ gtk_tree_view_get_cell_area(GTK_TREE_VIEW(tv),
+ path, NULL, &rect);
+
+ /* FIXME: isn't there any easier way to tell the number of rows? */
+ gtk_tree_model_get_iter_first(model, &iter);
+ do {
+ nrows++;
+ } while (gtk_tree_model_iter_next(model, &iter));
+
+ gtk_tree_path_free(path);
+
+ return nrows * rect.height;
+}
+
+void tree_view_save_image(gchar *filename)
+{
+ /* this is ridiculously complicated :/ why in the hell gtk+ makes this kind of
+ thing so difficult? */
+
+ /* FIXME: this does not work if the window (or part of it) isn't visible. does
+ anyone know how to fix this? :/ */
+ Shell *shell = shell_get_main_shell();
+ GtkWidget *widget = shell->info->view;
+
+ PangoLayout *layout;
+ PangoContext *context;
+ PangoRectangle rect;
+
+ GdkPixmap *pm;
+ GdkPixbuf *pb;
+ GdkGC *gc;
+ GdkColor black = { 0, 0, 0, 0 };
+ GdkColor white = { 0, 65535, 65535, 65535 };
+
+ gint w, h, visible_height;
+ gchar *tmp;
+
+ gboolean tv_enabled;
+
+ /* present the window */
+ gtk_window_present(GTK_WINDOW(shell->window));
+
+ /* if the treeview is disabled, we need to enable it so we get the
+ correct colors when saving. we make it insensitive later on if it
+ was this way before entering this function */
+ tv_enabled = GTK_WIDGET_IS_SENSITIVE(widget);
+ gtk_widget_set_sensitive(widget, TRUE);
+
+ gtk_widget_queue_draw(widget);
+
+ /* unselect things in the information treeview */
+ gtk_range_set_value(GTK_RANGE
+ (GTK_SCROLLED_WINDOW(shell->info->scroll)->
+ vscrollbar), 0.0);
+ gtk_tree_selection_unselect_all(
+ gtk_tree_view_get_selection(GTK_TREE_VIEW(widget)));
+ while (gtk_events_pending())
+ gtk_main_iteration();
+
+ /* initialize stuff */
+ gc = gdk_gc_new(widget->window);
+ gdk_gc_set_background(gc, &black);
+ gdk_gc_set_foreground(gc, &white);
+
+ context = gtk_widget_get_pango_context (widget);
+ layout = pango_layout_new(context);
+
+ visible_height = tree_view_get_visible_height(GTK_TREE_VIEW(widget));
+
+ /* draw the title */
+ tmp = g_strdup_printf("<b><big>%s</big></b>\n<small>%s</small>",
+ shell->selected->name,
+ shell->selected->notefunc(shell->selected->number));
+ pango_layout_set_markup(layout, tmp, -1);
+ pango_layout_set_width(layout, widget->allocation.width * PANGO_SCALE);
+ pango_layout_set_alignment(layout, PANGO_ALIGN_CENTER);
+ pango_layout_get_pixel_extents(layout, NULL, &rect);
+
+ w = widget->allocation.width;
+ h = visible_height + rect.height;
+
+ pm = gdk_pixmap_new(widget->window, w, rect.height, -1);
+ gdk_draw_rectangle(GDK_DRAWABLE(pm), gc, TRUE, 0, 0, w, rect.height);
+ gdk_draw_layout_with_colors (GDK_DRAWABLE(pm), gc, 0, 0, layout,
+ &white, &black);
+
+ /* copy the pixmap from the treeview and from the title */
+ pb = gdk_pixbuf_get_from_drawable(NULL,
+ widget->window,
+ NULL,
+ 0, 0,
+ 0, 0,
+ w, h);
+ pb = gdk_pixbuf_get_from_drawable(pb,
+ pm,
+ NULL,
+ 0, 0,
+ 0, visible_height,
+ w, rect.height);
+
+ /* save the pixbuf to a png file */
+ gdk_pixbuf_save(pb, filename, "png", NULL,
+ "compression", "9",
+ "tEXt::hardinfo::version", VERSION,
+ "tEXt::hardinfo::arch", ARCH,
+ NULL);
+
+ /* unref */
+ g_object_unref(pb);
+ g_object_unref(layout);
+ g_object_unref(pm);
+ g_object_unref(gc);
+ g_free(tmp);
+
+ gtk_widget_set_sensitive(widget, tv_enabled);
+}
+
+
+static gboolean __idle_free_do(gpointer ptr)
+{
+ if (ptr) {
+ g_free(ptr);
+ }
+
+ return FALSE;
+}
+
+gpointer idle_free(gpointer ptr)
+{
+ if (ptr) {
+ g_timeout_add(10000, __idle_free_do, ptr);
+ }
+
+ return ptr;
+}
+
+void module_entry_scan_all_except(ModuleEntry *entries, gint except_entry)
+{
+ ModuleEntry entry;
+ gint i = 0;
+ void (*scan_callback)(gboolean reload);
+
+ shell_view_set_enabled(FALSE);
+
+ for (entry = entries[0]; entry.name; entry = entries[++i]) {
+ if (i == except_entry)
+ continue;
+
+ shell_status_update(idle_free(g_strdup_printf("Scanning: %s...", entry.name)));
+
+ if ((scan_callback = entry.scan_callback)) {
+ scan_callback(FALSE);
+ }
+ }
+
+ shell_view_set_enabled(TRUE);
+ shell_status_update("Done.");
+}
+
+void module_entry_scan_all(ModuleEntry *entries)
+{
+ module_entry_scan_all_except(entries, -1);
+}
+
+void module_entry_reload(ShellModuleEntry *module_entry)
+{
+ if (module_entry->scan_func) {
+ module_entry->scan_func(TRUE);
+ }
+}
+
+void module_entry_scan(ShellModuleEntry *module_entry)
+{
+ if (module_entry->scan_func) {
+ module_entry->scan_func(FALSE);
+ }
+}
+
+gchar *module_entry_function(ShellModuleEntry *module_entry)
+{
+ if (module_entry->func) {
+ return g_strdup(module_entry->func());
+ }
+
+ return g_strdup("[Error]\n"
+ "Invalid module=");
+}
+
+const gchar *module_entry_get_note(ShellModuleEntry *module_entry)
+{
+ return module_entry->notefunc(module_entry->number);
}