#include <libgnomeui/gnome-app-util.h>
#include <libgnomeui/gnome-druid-page-edge.h>
#include "final.h"
+#include <libgnomevfs/gnome-vfs-utils.h>
#include <captive/macros.h>
/* Config: */
-#define PROGRESS_UPDATE_USEC 100000
+#define PROGRESS_UPDATE_USEC 200000
+/* Although proper GTK+ locking is provided below there are some
+ * bugs with compatibility of GTK+/Gnome-VFS/GConf.
+ * The main thread executes gtk_main()->g_main_loop_run()
+ * while the working thread initializes Gnome-VFS by GConf and
+ * executes also g_main_loop_run() while sharing some poll() fds.
+ */
+/* #define UI_GNOME_THREADS 1 */
static GnomeApp *App;
static GnomeDruidPage *PageFinish;
static GtkEntry *ScanPathLocationComboEntry;
static GtkButton *MicrosoftComConfirmButton;
+static GtkProgressBar *MicrosoftComProgress;
enum {
DRIVERS_TREE_STORE_COLUMN_TYPE,
DRIVERS_TREE_STORE_COLUMN_ID,
(page_active!=PageFinish && page_active!=MicrosoftComPage));
gtk_widget_set_sensitive(GTK_WIDGET(MicrosoftComConfirmButton),
(page_active==MicrosoftComPage));
+ gtk_progress_bar_set_fraction(MicrosoftComProgress,(gdouble)0);
+ gtk_progress_bar_set_text(MicrosoftComProgress,"");
}
}
g_return_if_fail(module_type!=NULL);
+ gdk_threads_enter();
+
iter=DriversTreeStore_Iter_hash_get_iter(module_type);
gtk_tree_store_set(DriversTreeStore,iter,
DRIVERS_TREE_STORE_COLUMN_TYPE,module_type,
? _("NOT FOUND; essential module for NTFS disks access")
: _("not found; optional module")),
-1);
+
+ gdk_flush();
+ gdk_threads_leave();
}
static void ui_gnome_module_available_notify(struct module_available *module_available)
g_return_if_fail(module_available!=NULL);
g_return_if_fail(module_available->module!=NULL);
- iter=DriversTreeStore_Iter_hash_get_iter(module_available->module->type);
+ gdk_threads_enter();
+
+ iter=DriversTreeStore_Iter_hash_get_iter((const gchar *)module_available->module->type);
gtk_tree_store_set(DriversTreeStore,iter,
DRIVERS_TREE_STORE_COLUMN_TYPE,module_available->module->type,
DRIVERS_TREE_STORE_COLUMN_ID ,module_available->module->id,
-1);
- if (!strcmp(module_available->module->type,"ntoskrnl.exe"))
+ if (!strcmp((const char *)module_available->module->type,"ntoskrnl.exe"))
some_module_ntoskrnl_exe_found=TRUE;
- if (!strcmp(module_available->module->type,"ntfs.sys"))
+ if (!strcmp((const char *)module_available->module->type,"ntfs.sys"))
some_module_ntfs_sys_found=TRUE;
some_modules_found=some_module_ntoskrnl_exe_found && some_module_ntfs_sys_found;
state_changed();
+
+ gdk_flush();
+ gdk_threads_leave();
}
static gboolean all_modules_found=FALSE;
static void ui_gnome_all_modules_found_notify(void)
{
+ gdk_threads_enter();
+
all_modules_found=TRUE;
state_changed();
+
+ gdk_flush();
+ gdk_threads_leave();
}
static gboolean aborted=FALSE;
static gboolean aborted_back=FALSE; /* 'Back' button was clicked. */
static struct timeval ProgressEntry_updated_timeval;
+static struct timeval ProgressBar_updated_timeval;
static void progress_start(void)
{
aborted=FALSE;
aborted_back=FALSE;
CAPTIVE_MEMZERO(&ProgressEntry_updated_timeval);
+ CAPTIVE_MEMZERO(&ProgressBar_updated_timeval);
state_changed();
}
state_changed();
}
+static gboolean want_progress_update(struct timeval *timeval)
+{
+struct timeval now_timeval;
+struct timeval diff_timeval;
+
+ g_return_val_if_fail(timeval!=NULL,FALSE);
+
+ gettimeofday( /* FIXME: errors ignored */
+ &now_timeval, /* tv */
+ NULL); /* tz */
+ timersub(&now_timeval,timeval,&diff_timeval);
+ if (!timeval->tv_sec || diff_timeval.tv_sec>0 || diff_timeval.tv_usec>=PROGRESS_UPDATE_USEC) {
+ *timeval=now_timeval;
+ return TRUE;
+ }
+ return FALSE;
+}
+
static gboolean ui_gnome_progress(GnomeVFSURI *uri)
{
+gboolean want_gdk_flush=FALSE;
+
/* 'uri' may be NULL */
+ gdk_threads_enter();
+
if (ProgressEntry) {
static gchar *uri_text=NULL;
}
if (uri_text) {
-struct timeval now_timeval;
-struct timeval diff_timeval;
-
- gettimeofday( /* FIXME: errors ignored */
- &now_timeval, /* tv */
- NULL); /* tz */
- timersub(&now_timeval,&ProgressEntry_updated_timeval,&diff_timeval);
- if (!ProgressEntry_updated_timeval.tv_sec || diff_timeval.tv_sec>0 || diff_timeval.tv_usec>=PROGRESS_UPDATE_USEC) {
+ if (want_progress_update(&ProgressEntry_updated_timeval)) {
gtk_entry_set_text(ProgressEntry,
uri_text+(strncmp(uri_text,"file://",strlen("file://")) ? 0 : strlen("file://")));
- ProgressEntry_updated_timeval=now_timeval;
+ want_gdk_flush=TRUE;
}
}
}
- while (gtk_events_pending())
- gtk_main_iteration();
+
+#ifndef UI_GNOME_THREADS
+ while (g_main_context_pending(NULL))
+ g_main_context_iteration(
+ NULL, /* context */
+ FALSE); /* may_block */
+#endif /* UI_GNOME_THREADS */
+
+ if (want_gdk_flush)
+ gdk_flush();
+ gdk_threads_leave();
+
+ /* Do not: g_thread_yield();
+ * as it is TOO much expensive and we are multithreaded anyway.
+ */
if (aborted)
return TRUE;
return FALSE;
}
+static void ui_gnome_progress_bar(gint done,gint length)
+{
+ g_return_if_fail(done>=0);
+ g_return_if_fail(length>=0);
+
+ if (!want_progress_update(&ProgressBar_updated_timeval))
+ return;
+
+ gdk_threads_enter();
+
+ if (!length) {
+ gtk_progress_bar_pulse(MicrosoftComProgress);
+ gtk_progress_bar_set_text(MicrosoftComProgress,"");
+ }
+ else {
+gchar *length_display;
+
+ /* Do not format 'done' by gnome_vfs_format_file_size_for_display()
+ * as the progress would not be visible for large 'done' sizes.
+ */
+ gtk_progress_bar_set_fraction(MicrosoftComProgress,((gdouble)done)/length);
+ length_display=gnome_vfs_format_file_size_for_display(length);
+ gtk_progress_bar_set_text(MicrosoftComProgress,
+ captive_printf_alloca("%d B / %s",(int)done,length_display));
+ g_free(length_display);
+ }
+
+ gdk_flush();
+ gdk_threads_leave();
+}
+
/* FIXME: Change it to "prepare" signal. */
void on_Page_map(GtkWidget *vbox_widget,GtkWidget *page_widget)
{
+ /* Handle non-object (NULL) signal with reversed parameters? */
+ if (GNOME_IS_DRUID_PAGE(vbox_widget) && page_widget==NULL) {
+ page_widget=vbox_widget;
+ vbox_widget=NULL;
+ }
g_return_if_fail(vbox_widget==NULL || GTK_IS_VBOX(vbox_widget));
g_return_if_fail(GNOME_IS_DRUID_PAGE(page_widget));
gtk_widget_thaw_child_notify(vbox_widget);
}
+typedef void (*process_t)(void);
+
+#ifdef UI_GNOME_THREADS
+/* 'GThreadFunc' type. */
+gpointer execute_process_func(process_t process /* data */)
+{
+ (*process)();
+
+ gdk_threads_enter();
+
+ gtk_main_quit(); /* Abort gtk_main() of execute_process(). */
+
+ gdk_flush();
+ gdk_threads_leave();
+
+ return NULL;
+}
+#endif /* UI_GNOME_THREADS */
+
+/* We are called inside gdk_threads_enter(). */
+static void execute_process(process_t process)
+{
+#ifdef UI_GNOME_THREADS
+GThread *gthread;
+#endif /* UI_GNOME_THREADS */
+
+ progress_start();
+#ifdef UI_GNOME_THREADS
+ gthread=g_thread_create_full(
+ (GThreadFunc)execute_process_func, /* func */
+ process, /* data */
+ 0, /* stack_size; 0 means the default size */
+ TRUE, /* joinable */
+ TRUE, /* bound; use system thread */
+ G_THREAD_PRIORITY_LOW, /* priority; G_THREAD_PRIORITY_LOW is the lowest one */
+ NULL); /* error */
+ gtk_main(); /* We are already called inside gdk_threads_enter(). */
+ /* I hope some other gtk_main_quit() did not occur as we would
+ * locked if the 'process' func did not finish yet.
+ */
+ g_thread_join(gthread);
+#else /* UI_GNOME_THREADS */
+ (*process)();
+#endif /* UI_GNOME_THREADS */
+ progress_end();
+}
+
+/* 'process_t' typed. */
+static void process_scan_disk(void)
+{
+ scan_disks_quick();
+ scan_disks();
+}
+
+static GnomeVFSURI *process_scan_path_scan_path_uri;
+
+/* 'process_t' typed. */
+static void process_scan_path(void)
+{
+ mod_uri_load_base_reporting(process_scan_path_scan_path_uri);
+}
+
+/* 'process_t' typed. */
+static void process_microsoft_com(void)
+{
+ microsoft_com();
+}
+
gboolean on_Page_next(GnomeDruidPage *gnomedruidpage,GtkWidget *widget,gpointer user_data /* unused */)
{
g_return_val_if_fail(GNOME_IS_DRUID_PAGE(gnomedruidpage),FALSE);
}
}
else if (page_active==ScanDiskPage) {
- progress_start();
- scan_disks_quick();
- scan_disks();
- progress_end();
+ execute_process(process_scan_disk);
if (aborted_back) {
gnome_druid_set_page(Druid,PageStart);
return TRUE; /* ignore button press */
GnomeVFSURI *scan_path_uri;
if ((scan_path_uri=gnome_vfs_uri_new(scan_path_uri_text))) {
- progress_start();
- mod_uri_load(scan_path_uri);
- progress_end();
+ process_scan_path_scan_path_uri=scan_path_uri;
+ execute_process(process_scan_path);
gnome_vfs_uri_unref(scan_path_uri);
if (aborted_back) {
gnome_druid_set_page(Druid,(all_modules_found ? PageStart : ScanDiskPage));
if (page_active!=MicrosoftComPage) /* bogus callback */
return;
- progress_start();
- microsoft_com();
- progress_end();
+ execute_process(process_microsoft_com);
if (aborted_back) {
gnome_druid_set_page(Druid,(all_modules_found ? PageStart : ScanPathPage));
return;
/* TODO: Avoid dialog if already on Finish page. */
reply=-1;
dialog=gnome_app_ok_cancel_modal(App,_(
- "Although essential modules (\"ntoskrnl.exe\" and \"ntfs.sys\") are available"
- "you may still want to get their better version and/or more modules."
+ "Although essential modules (\"ntoskrnl.exe\" and \"ntfs.sys\") are available "
+ "you may still want to get their better version and/or more modules. "
"Really quit?"),
(GnomeReplyCallback)on_DruidButtonOK_clicked_dialog_callback,
&reply); /* data */
g_signal_connect((gpointer)dialog,"close",G_CALLBACK(gtk_main_quit),NULL);
+ /* Never call gtk_main() from other thread than the initial one.
+ * We would have to switch GTK+ context (g_main_context()?).
+ */
gtk_main();
/* 'dialog' gets destroyed automatically */
if (reply==0) /* 0 for 'OK', 1 for 'Cancel', left -1 for dialog close. */
gtk_label_set_text_with_mnemonic(GTK_LABEL(widget),label_text_new);
}
+static void PageFinish_set_label_attr(GtkWidget *widget,gpointer callback_data /* unused */)
+{
+ g_return_if_fail(GTK_IS_WIDGET(widget));
+
+ /**/ if (GTK_IS_CONTAINER(widget))
+ gtk_container_foreach(GTK_CONTAINER(widget),
+ (GtkCallback)PageFinish_set_label_attr, /* callback */
+ callback_data); /* callback_data; unused */
+ else if (GTK_IS_LABEL(widget) && gtk_label_get_line_wrap(GTK_LABEL(widget)))
+ gtk_label_set_selectable(GTK_LABEL(widget),TRUE);
+}
+
/* of "ui-gnome-interface.h": */
GtkWidget *create_App(void);
/* of "ui-gnome-support.h": */
GtkCellRenderer *cell;
GtkBox *druid_button_box;
+ gdk_threads_enter();
+
App=GNOME_APP(create_App());
DriversTreeView=GTK_TREE_VIEW(lookup_widget(GTK_WIDGET(App),"DriversTreeView"));
PageFinish=GNOME_DRUID_PAGE(lookup_widget(GTK_WIDGET(App),"PageFinish"));
ScanPathLocationComboEntry=GTK_ENTRY(lookup_widget(GTK_WIDGET(App),"ScanPathLocationComboEntry"));
MicrosoftComConfirmButton=GTK_BUTTON(lookup_widget(GTK_WIDGET(App),"MicrosoftComConfirmButton"));
+ MicrosoftComProgress=GTK_PROGRESS_BAR(lookup_widget(GTK_WIDGET(App),"MicrosoftComProgress"));
ProgressEntry=GTK_ENTRY(lookup_widget(GTK_WIDGET(App),"ProgressEntry"));
druid_button_box=GTK_BOX(gtk_widget_get_parent(Druid->next));
gtk_widget_hide(GTK_WIDGET(Druid->finish));
DruidButtonSkip=GTK_BUTTON(gtk_button_new_from_stock(GTK_STOCK_REDO));
- gtk_container_foreach(GTK_CONTAINER(DruidButtonSkip),
- (GtkCallback)button_stock_set_label, /* callback */
- _("_Skip")); /* callback_data */
+ button_stock_set_label(
+ GTK_WIDGET(DruidButtonSkip), /* widget */
+ _("_Skip")); /* label_text_new */
gtk_box_pack_end(druid_button_box,GTK_WIDGET(DruidButtonSkip),FALSE,TRUE,0);
gtk_widget_show(GTK_WIDGET(DruidButtonSkip));
g_signal_connect((gpointer)DruidButtonSkip,"clicked",G_CALLBACK(on_DruidButtonSkip_clicked),NULL);
gtk_widget_show(GTK_WIDGET(DruidButtonOK));
g_signal_connect((gpointer)DruidButtonOK,"clicked",G_CALLBACK(on_DruidButtonOK_clicked),NULL);
+ PageFinish_set_label_attr(
+ GTK_WIDGET(PageFinish), /* widget */
+ NULL); /* callback_data; unused */
+
state_changed();
+
+ gdk_threads_leave();
}
static void ui_gnome_g_log_handler(const gchar *log_domain,GLogLevelFlags log_level,const gchar *message,gpointer user_data)
if (in_progress && aborted)
return;
+ gdk_threads_enter();
+
/**/ if (log_level & G_LOG_LEVEL_ERROR)
dialog=gnome_app_error(App,message);
else if (log_level & (G_LOG_LEVEL_CRITICAL|G_LOG_LEVEL_WARNING))
g_signal_connect((gpointer)dialog,"close",G_CALLBACK(gtk_main_quit),NULL);
gtk_main();
/* 'dialog' gets destroyed automatically */
+
+ gdk_flush();
+ gdk_threads_leave();
}
static void ui_gnome_interactive(void)
{
+ gdk_threads_enter();
+
+ /* Postpone gtk_widget_show_all() from App_init() here
+ * to have already passed all ui_gnome_module_available_notify().
+ */
+ gnome_druid_set_page(Druid,MicrosoftComPage);
gtk_widget_show_all(GTK_WIDGET(App));
+#if 0
+ /* gnome_druid_set_page(Druid,PageStart); */
+ gnome_druid_set_page(Druid,ScanDiskPage);
+ gnome_druid_set_page(Druid,ScanPathPage);
+ /* gnome_druid_set_page(Druid,MicrosoftComPage); */
+ gnome_druid_set_page(Druid,PageFinish);
+#endif
+ gnome_druid_set_page(Druid,PageStart);
+
gtk_main();
+
+ gdk_threads_leave();
+
exit(EXIT_SUCCESS);
}
acquire_module_available_notify=ui_gnome_module_available_notify;
acquire_module_all_modules_found_notify=ui_gnome_all_modules_found_notify;
ui_progress=ui_gnome_progress;
+ ui_progress_bar=ui_gnome_progress_bar;
ui_interactive=ui_gnome_interactive;
captivemodid_module_best_priority_notify=ui_gnome_module_best_priority_notify;
+#ifdef UI_GNOME_THREADS
+ /* gdk_threads_init() must be called before gtk_init()!
+ * gtk_init() gets called by create_App() here.
+ */
+ if (!g_thread_supported())
+ g_thread_init(NULL);
+ if (!gdk_threads_mutex)
+ gdk_threads_init();
+#endif /* UI_GNOME_THREADS */
+
/* Graphic widgets will all be hidden yet. */
App_init();
/* ui_gnome_g_log_handler() needs 'App'. */