Richard W.M. Jones
2016-May-30 18:56 UTC
[Libguestfs] [PATCH 0/2] p2v: Allow virt-p2v to be built with Gtk 2 or 3.
... and a small initial patch which makes it easier to test virt-p2v without having to start up a virtual machine. There is still a bug in Gtk 3 where the GtkTextView on the final (running) dialog ignores gtk_widget_set_size_request and so the window appears just a single pixel high. Rich.
Richard W.M. Jones
2016-May-30 18:56 UTC
[Libguestfs] [PATCH 1/2] p2v: Add --test-disk option for testing conversions.
Use, for example: ./run virt-p2v --test-disk=$(pwd)/test-data/phony-guests/windows.img to test conversions using a file of test data instead of the real host /dev/sda. --- docs/guestfs-hacking.pod | 13 ++++++++----- p2v/main.c | 32 +++++++++++++++++++++++++++++++- p2v/virt-p2v.pod | 5 +++++ 3 files changed, 44 insertions(+), 6 deletions(-) diff --git a/docs/guestfs-hacking.pod b/docs/guestfs-hacking.pod index c8db955..4e325d6 100644 --- a/docs/guestfs-hacking.pod +++ b/docs/guestfs-hacking.pod @@ -757,12 +757,15 @@ in virt-v2v. They are converted in the same way as foreign VMs. =head2 Running virt-p2v -You can run the F<p2v/virt-p2v> binary directly, although it's not -really recommended, but it's OK for quick tests of the GUI (but don't -try doing a conversion that way). +You can run the F<p2v/virt-p2v> binary directly, but it will try to +convert your machine's real F</dev/sda> which is unlikely to work +well. However virt-p2v also has a test mode in which you can supply a +test disk: -A better way is to run virt-p2v inside a VM on the local machine. To -do that, do: + ./run virt-p2v --test-disk=../test-data/phony-guests/windows.img + +A more realistic test is to run virt-p2v inside a VM on the local +machine. To do that, do: make -C p2v run-virt-p2v diff --git a/p2v/main.c b/p2v/main.c index 40b1ff2..e1be311 100644 --- a/p2v/main.c +++ b/p2v/main.c @@ -43,6 +43,8 @@ char **all_disks; char **all_removable; char **all_interfaces; +static const char *test_disk = NULL; + static void udevadm_settle (void); static void set_config_defaults (struct config *config); static void find_all_disks (void); @@ -56,6 +58,7 @@ static const struct option long_options[] = { { "cmdline", 1, 0, 0 }, { "long-options", 0, 0, 0 }, { "short-options", 0, 0, 0 }, + { "test-disk", 1, 0, 0 }, { "verbose", 0, 0, 'v' }, { "version", 0, 0, 'V' }, { 0, 0, 0, 0 } @@ -155,6 +158,15 @@ main (int argc, char *argv[]) cmdline = parse_cmdline_string (optarg); cmdline_source = CMDLINE_SOURCE_COMMAND_LINE; } + else if (STREQ (long_options[option_index].name, "test-disk")) { + if (test_disk != NULL) + error (EXIT_FAILURE, 0, + _("only a single --test-disk option can be used")); + if (optarg[0] != '/') + error (EXIT_FAILURE, 0, + _("--test-disk must be an absolute path")); + test_disk = optarg; + } else error (EXIT_FAILURE, 0, _("unknown long option: %s (%d)"), @@ -298,11 +310,29 @@ set_config_defaults (struct config *config) else config->flags = 0; - find_all_disks (); + /* Find all block devices in the system. */ + if (!test_disk) + find_all_disks (); + else { + /* For testing and debugging purposes, you can use + * --test-disk=/path/to/disk.img + */ + all_disks = malloc (2 * sizeof (char *)); + if (all_disks == NULL) + error (EXIT_FAILURE, errno, "realloc"); + all_disks[0] = strdup (test_disk); + if (all_disks[0] == NULL) + error (EXIT_FAILURE, errno, "strdup"); + all_disks[1] = NULL; + } if (all_disks) config->disks = guestfs_int_copy_string_list (all_disks); + + /* Find all removable devices in the system. */ if (all_removable) config->removable = guestfs_int_copy_string_list (all_removable); + + /* Find all network interfaces in the system. */ find_all_interfaces (); if (all_interfaces) config->interfaces = guestfs_int_copy_string_list (all_interfaces); diff --git a/p2v/virt-p2v.pod b/p2v/virt-p2v.pod index b4598d9..013efc2 100644 --- a/p2v/virt-p2v.pod +++ b/p2v/virt-p2v.pod @@ -581,6 +581,11 @@ Display help. This is used for debugging. Instead of parsing the kernel command line from F</proc/cmdline>, parse the string parameter C<CMDLINE>. +=item B<--test-disk=/PATH/TO/DISK.IMG> + +For testing or debugging purposes, replace F</dev/sda> with a local +file. You must use an absolute path. + =item B<-v> =item B<--verbose> -- 2.7.4
Richard W.M. Jones
2016-May-30 18:56 UTC
[Libguestfs] [PATCH 2/2] p2v: Allow virt-p2v to be built with Gtk 2 or 3.
Currently virt-p2v requires Gtk 2. This commit changes virt-p2v so it can be built with either Gtk 2 or 3. By careful use of macros, this code should compile on both recent Gtk 2 and Gtk 3. With no other options, ./configure will now prefer Gtk 3 if it finds it, or fall back to Gtk 2. But you can control this by setting './configure --with-gtk=2|3|check|no' where the options mean: * --with-gtk=2 - Only test for Gtk 2 * --with-gtk=3 - Only test for Gtk 3 * --with-gtk=check - Check for Gtk 3 then Gtk 2 (default) * --with-gtk=no - Don't build virt-p2v In the ./configure output you will see something like this: checking for --with-gtk option... 2 checking for GTK... yes checking if we can build virt-p2v... yes, with Gtk 2 --- docs/guestfs-building.pod | 10 +- m4/guestfs_misc_libraries.m4 | 43 ++- p2v/Makefile.am | 6 +- p2v/dependencies.m4 | 8 +- p2v/gui.c | 619 +++++++++++++++++++++++++++++-------------- p2v/main.c | 2 - 6 files changed, 470 insertions(+), 218 deletions(-) diff --git a/docs/guestfs-building.pod b/docs/guestfs-building.pod index f42d25f..faaa626 100644 --- a/docs/guestfs-building.pod +++ b/docs/guestfs-building.pod @@ -272,9 +272,15 @@ Optional. Used by virt-builder for checking digital signatures. Optional. If available, virt-builder will use this library for fast, parallel uncompression of templates. -=item gtk2 E<ge> 2.24 +=item Gtk E<ge> 2.24, or 3 -Optional. Used by the virt-p2v user interface. +Optional. + +Used by the virt-p2v graphical user interface. + +Either Gtk 2 or Gtk 3 can be used. If you want to select a specific +version of Gtk, use S<C<./configure --with-gtk=2>> or +S<C<./configure --with-gtk=3>>. =item zip diff --git a/m4/guestfs_misc_libraries.m4 b/m4/guestfs_misc_libraries.m4 index 9b22670..4ae0576 100644 --- a/m4/guestfs_misc_libraries.m4 +++ b/m4/guestfs_misc_libraries.m4 @@ -74,12 +74,41 @@ PKG_CHECK_MODULES([LIBCONFIG], [libconfig],[ [AC_MSG_WARN([libconfig not found, some features will be disabled])]) AM_CONDITIONAL([HAVE_LIBCONFIG],[test "x$LIBCONFIG_LIBS" != "x"]) -dnl Check for gtk2 library, used by virt-p2v. -PKG_CHECK_MODULES([GTK2], [gtk+-2.0], [ - AC_SUBST([GTK2_CFLAGS]) - AC_SUBST([GTK2_LIBS]) -], - [AC_MSG_WARN([gtk2 not found, virt-p2v will be disabled])]) +dnl Check for Gtk 2 or 3 library, used by virt-p2v. +AC_MSG_CHECKING([for --with-gtk option]) +AC_ARG_WITH([gtk], + [AS_HELP_STRING([--with-gtk=2|3|check|no], + [prefer Gtk version 2 or 3. @<:@default=check@:>@])], + [with_gtk="$withval" + AC_MSG_RESULT([$withval])], + [with_gtk="check" + AC_MSG_RESULT([not set, will check for installed Gtk])] +) + +if test "x$GTK_LIBS" = "x" && \ + ( test "x$with_gtk" = "x3" || test "x$with_gtk" = "xcheck" ) ; then + PKG_CHECK_MODULES([GTK], [gtk+-3.0], [ + AC_SUBST([GTK_CFLAGS]) + AC_SUBST([GTK_LIBS]) + GTK_VERSION=3 + AC_SUBST([GTK_VERSION]) + ], []) +fi +if test "x$GTK_LIBS" = "x" && \ + ( test "x$with_gtk" = "x2" || test "x$with_gtk" = "xcheck" ) ; then + PKG_CHECK_MODULES([GTK], [gtk+-2.0], [ + AC_SUBST([GTK_CFLAGS]) + AC_SUBST([GTK_LIBS]) + GTK_VERSION=2 + AC_SUBST([GTK_VERSION]) + ], []) +fi dnl Can we build virt-p2v? -AM_CONDITIONAL([HAVE_P2V], [test "x$GTK2_LIBS" != "x"]) +AC_MSG_CHECKING([if we can build virt-p2v]) +if test "x$GTK_LIBS" != "x"; then + AC_MSG_RESULT([yes, with Gtk $GTK_VERSION]) +else + AC_MSG_RESULT([no]) +fi +AM_CONDITIONAL([HAVE_P2V], [test "x$GTK_LIBS" != "x"]) diff --git a/p2v/Makefile.am b/p2v/Makefile.am index 3342563..53d7198 100644 --- a/p2v/Makefile.am +++ b/p2v/Makefile.am @@ -84,13 +84,13 @@ virt_p2v_CFLAGS = \ $(WARN_CFLAGS) $(WERROR_CFLAGS) \ $(PCRE_CFLAGS) \ $(LIBXML2_CFLAGS) \ - $(GTK2_CFLAGS) + $(GTK_CFLAGS) virt_p2v_LDADD = \ $(top_builddir)/src/libutils.la \ $(PCRE_LIBS) \ $(LIBXML2_LIBS) \ - $(GTK2_LIBS) \ + $(GTK_LIBS) \ ../gnulib/lib/libgnu.la # Scripts to build the disk image, USB key, or kickstart. @@ -104,7 +104,7 @@ dependencies_files = \ $(dependencies_files): dependencies.m4 define=`echo $@ | $(SED) 's/dependencies.//;y/abcdefghijklmnopqrstuvwxyz/ABCDEFGHIJKLMNOPQRSTUVWXYZ/'`; \ - m4 -D$$define=1 $< > $@-t + m4 -D$$define=1 -DGTK_VERSION=$(GTK_VERSION) $< > $@-t mv $@-t $@ # Support files needed by the virt-p2v-make-* scripts. diff --git a/p2v/dependencies.m4 b/p2v/dependencies.m4 index ab25a49..b5d4d6e 100644 --- a/p2v/dependencies.m4 +++ b/p2v/dependencies.m4 @@ -23,7 +23,7 @@ ifelse(REDHAT,1, dnl Used by the virt-p2v binary. pcre libxml2 - gtk2 + gtk`'GTK_VERSION dnl Run as external programs by the p2v binary. /usr/bin/ssh @@ -55,7 +55,7 @@ ifelse(REDHAT,1, ifelse(DEBIAN,1, libpcre3 libxml2 - libgtk2.0-0 + libgtk`'GTK_VERSION`'.0-0 openssh-client qemu-utils curl @@ -73,7 +73,7 @@ ifelse(DEBIAN,1, ifelse(ARCHLINUX,1, pcre libxml2 - gtk2 + gtk`'GTK_VERSION openssh qemu curl @@ -92,7 +92,7 @@ ifelse(ARCHLINUX,1, ifelse(SUSE,1, pcre libxml2 - gtk2 + gtk`'GTK_VERSION /usr/bin/ssh /usr/bin/qemu-nbd curl diff --git a/p2v/gui.c b/p2v/gui.c index f3e448f..147cadd 100644 --- a/p2v/gui.c +++ b/p2v/gui.c @@ -80,6 +80,104 @@ #define MAX_SUPPORTED_VCPUS 160 #define MAX_SUPPORTED_MEMORY_MB (UINT64_C (4000 * 1024)) +/* Backwards compatibility for some deprecated functions in Gtk 3. */ +#if GTK_CHECK_VERSION(3,2,0) /* gtk >= 3.2 */ +#define hbox_new(box, homogeneous, spacing) \ + do { \ + (box) = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, spacing); \ + if (homogeneous) \ + gtk_box_set_homogeneous (GTK_BOX (box), TRUE); \ + } while (0) +#define vbox_new(box, homogeneous, spacing) \ + do { \ + (box) = gtk_box_new (GTK_ORIENTATION_VERTICAL, spacing); \ + if (homogeneous) \ + gtk_box_set_homogeneous (GTK_BOX (box), TRUE); \ + } while (0) +#else /* gtk < 3.2 */ +#define hbox_new(box, homogeneous, spacing) \ + (box) = gtk_hbox_new ((homogeneous), (spacing)) +#define vbox_new(box, homogeneous, spacing) \ + (box) = gtk_vbox_new ((homogeneous), (spacing)) +#endif + +#if GTK_CHECK_VERSION(3,4,0) /* gtk >= 3.4 */ +/* GtkGrid is sufficiently similar to GtkTable that we can just + * redefine these functions. + */ +#define table_new(grid, rows, columns) \ + (grid) = gtk_grid_new () +#define table_attach(grid, child, left, right, top, bottom, xoptions, yoptions, xpadding, ypadding) \ + do { \ + gtk_grid_attach (GTK_GRID (grid), (child), \ + (left), (top), (right)-(left), (bottom)-(top)); \ + if ((xoptions) == GTK_EXPAND) \ + gtk_widget_set_hexpand ((grid), TRUE); \ + else if ((xoptions) == GTK_FILL) \ + gtk_widget_set_halign ((grid), GTK_ALIGN_FILL); \ + if ((yoptions) == GTK_EXPAND) \ + gtk_widget_set_vexpand ((grid), TRUE); \ + else if ((yoptions) == GTK_FILL) \ + gtk_widget_set_valign ((grid), GTK_ALIGN_FILL); \ + set_padding ((grid), (xpadding), (ypadding)); \ + } while (0) +#else +#define table_new(table, rows, columns) \ + (table) = gtk_table_new ((rows), (columns), FALSE) +#define table_attach(table, child, left, right,top, bottom, xoptions, yoptions, xpadding, ypadding) \ + gtk_table_attach (GTK_TABLE (table), (child), \ + (left), (right), (top), (bottom), \ + (xoptions), (yoptions), (xpadding), (ypadding)) +#endif + +#if GTK_CHECK_VERSION(3,8,0) /* gtk >= 3.8 */ +#define scrolled_window_add_with_viewport(container, child) \ + gtk_container_add (GTK_CONTAINER (container), child) +#else +#define scrolled_window_add_with_viewport(container, child) \ + gtk_scrolled_window_add_with_viewport (GTK_SCROLLED_WINDOW (container), child) +#endif + +#if GTK_CHECK_VERSION(3,10,0) /* gtk >= 3.10 */ +#undef GTK_STOCK_DIALOG_WARNING +#define GTK_STOCK_DIALOG_WARNING "dialog-warning" +#define gtk_image_new_from_stock gtk_image_new_from_icon_name +#endif + +#if GTK_CHECK_VERSION(3,14,0) /* gtk >= 3.14 */ +#define set_padding(widget, xpad, ypad) \ + do { \ + if ((xpad) != 0) { \ + gtk_widget_set_margin_start ((widget), (xpad)); \ + gtk_widget_set_margin_end ((widget), (xpad)); \ + } \ + if ((ypad) != 0) { \ + gtk_widget_set_margin_top ((widget), (ypad)); \ + gtk_widget_set_margin_bottom ((widget), (ypad)); \ + } \ + } while (0) +#define set_alignment(widget, xalign, yalign) \ + do { \ + if ((xalign) == 0.) \ + gtk_widget_set_halign ((widget), GTK_ALIGN_START); \ + else if ((xalign) == 1.) \ + gtk_widget_set_halign ((widget), GTK_ALIGN_END); \ + else \ + gtk_widget_set_halign ((widget), GTK_ALIGN_CENTER); \ + if ((yalign) == 0.) \ + gtk_widget_set_valign ((widget), GTK_ALIGN_START); \ + else if ((xalign) == 1.) \ + gtk_widget_set_valign ((widget), GTK_ALIGN_END); \ + else \ + gtk_widget_set_valign ((widget), GTK_ALIGN_CENTER); \ + } while (0) +#else /* gtk < 3.14 */ +#define set_padding(widget, xpad, ypad) \ + gtk_misc_set_padding(GTK_MISC(widget),(xpad),(ypad)) +#define set_alignment(widget, xalign, yalign) \ + gtk_misc_set_padding(GTK_MISC(widget),(xalign),(yalign)) +#endif + static void create_connection_dialog (struct config *); static void create_conversion_dialog (struct config *); static void create_running_dialog (void); @@ -127,7 +225,6 @@ gui_conversion (struct config *config) show_connection_dialog (); gtk_main (); - gdk_threads_leave (); } /*----------------------------------------------------------------------*/ @@ -135,6 +232,10 @@ gui_conversion (struct config *config) static void test_connection_clicked (GtkWidget *w, gpointer data); static void *test_connection_thread (void *data); +static gboolean start_spinner (gpointer user_data); +static gboolean stop_spinner (gpointer user_data); +static gboolean test_connection_error (gpointer user_data); +static gboolean test_connection_ok (gpointer user_data); static void configure_network_button_clicked (GtkWidget *w, gpointer data); static void about_button_clicked (GtkWidget *w, gpointer data); static void connection_next_clicked (GtkWidget *w, gpointer data); @@ -168,46 +269,46 @@ create_connection_dialog (struct config *config) /* The main dialog area. */ intro = gtk_label_new (_("Connect to a virt-v2v conversion server over SSH:")); gtk_label_set_line_wrap (GTK_LABEL (intro), TRUE); - gtk_misc_set_padding (GTK_MISC (intro), 10, 10); + set_padding (intro, 10, 10); - table = gtk_table_new (7, 2, FALSE); + table_new (table, 7, 2); server_label = gtk_label_new (_("Conversion server:")); - gtk_misc_set_alignment (GTK_MISC (server_label), 1., 0.5); - gtk_table_attach (GTK_TABLE (table), server_label, - 0, 1, 0, 1, GTK_FILL, GTK_FILL, 4, 4); + set_alignment (server_label, 1., 0.5); + table_attach (table, server_label, + 0, 1, 0, 1, GTK_FILL, GTK_FILL, 4, 4); server_entry = gtk_entry_new (); if (config->server != NULL) gtk_entry_set_text (GTK_ENTRY (server_entry), config->server); - gtk_table_attach (GTK_TABLE (table), server_entry, - 1, 2, 0, 1, GTK_FILL, GTK_FILL, 4, 4); + table_attach (table, server_entry, + 1, 2, 0, 1, GTK_FILL, GTK_FILL, 4, 4); port_label = gtk_label_new (_("SSH port:")); - gtk_misc_set_alignment (GTK_MISC (port_label), 1., 0.5); - gtk_table_attach (GTK_TABLE (table), port_label, - 0, 1, 1, 2, GTK_FILL, GTK_FILL, 4, 4); + set_alignment (port_label, 1., 0.5); + table_attach (table, port_label, + 0, 1, 1, 2, GTK_FILL, GTK_FILL, 4, 4); port_entry = gtk_entry_new (); gtk_entry_set_width_chars (GTK_ENTRY (port_entry), 6); snprintf (port_str, sizeof port_str, "%d", config->port); gtk_entry_set_text (GTK_ENTRY (port_entry), port_str); - gtk_table_attach (GTK_TABLE (table), port_entry, - 1, 2, 1, 2, GTK_FILL, GTK_FILL, 4, 4); + table_attach (table, port_entry, + 1, 2, 1, 2, GTK_FILL, GTK_FILL, 4, 4); username_label = gtk_label_new (_("User name:")); - gtk_misc_set_alignment (GTK_MISC (username_label), 1., 0.5); - gtk_table_attach (GTK_TABLE (table), username_label, - 0, 1, 2, 3, GTK_FILL, GTK_FILL, 4, 4); + set_alignment (username_label, 1., 0.5); + table_attach (table, username_label, + 0, 1, 2, 3, GTK_FILL, GTK_FILL, 4, 4); username_entry = gtk_entry_new (); if (config->username != NULL) gtk_entry_set_text (GTK_ENTRY (username_entry), config->username); else gtk_entry_set_text (GTK_ENTRY (username_entry), "root"); - gtk_table_attach (GTK_TABLE (table), username_entry, - 1, 2, 2, 3, GTK_FILL, GTK_FILL, 4, 4); + table_attach (table, username_entry, + 1, 2, 2, 3, GTK_FILL, GTK_FILL, 4, 4); password_label = gtk_label_new (_("Password:")); - gtk_misc_set_alignment (GTK_MISC (password_label), 1., 0.5); - gtk_table_attach (GTK_TABLE (table), password_label, - 0, 1, 3, 4, GTK_FILL, GTK_FILL, 4, 4); + set_alignment (password_label, 1., 0.5); + table_attach (table, password_label, + 0, 1, 3, 4, GTK_FILL, GTK_FILL, 4, 4); password_entry = gtk_entry_new (); gtk_entry_set_visibility (GTK_ENTRY (password_entry), FALSE); #ifdef GTK_INPUT_PURPOSE_PASSWORD @@ -216,53 +317,57 @@ create_connection_dialog (struct config *config) #endif if (config->password != NULL) gtk_entry_set_text (GTK_ENTRY (password_entry), config->password); - gtk_table_attach (GTK_TABLE (table), password_entry, - 1, 2, 3, 4, GTK_FILL, GTK_FILL, 4, 4); + table_attach (table, password_entry, + 1, 2, 3, 4, GTK_FILL, GTK_FILL, 4, 4); identity_label = gtk_label_new (_("SSH Identity URL:")); - gtk_misc_set_alignment (GTK_MISC (identity_label), 1., 0.5); - gtk_table_attach (GTK_TABLE (table), identity_label, - 0, 1, 4, 5, GTK_FILL, GTK_FILL, 4, 4); + set_alignment (identity_label, 1., 0.5); + table_attach (table, identity_label, + 0, 1, 4, 5, GTK_FILL, GTK_FILL, 4, 4); identity_entry = gtk_entry_new (); if (config->identity_url != NULL) gtk_entry_set_text (GTK_ENTRY (identity_entry), config->identity_url); - gtk_table_attach (GTK_TABLE (table), identity_entry, - 1, 2, 4, 5, GTK_FILL, GTK_FILL, 4, 4); + table_attach (table, identity_entry, + 1, 2, 4, 5, GTK_FILL, GTK_FILL, 4, 4); identity_tip_label = gtk_label_new (NULL); gtk_label_set_markup (GTK_LABEL (identity_tip_label), _("<i>If using password authentication, leave the SSH Identity URL blank</i>")); gtk_label_set_line_wrap (GTK_LABEL (identity_tip_label), TRUE); - gtk_table_attach (GTK_TABLE (table), identity_tip_label, - 1, 2, 5, 6, GTK_FILL, GTK_FILL, 4, 4); + table_attach (table, identity_tip_label, + 1, 2, 5, 6, GTK_FILL, GTK_FILL, 4, 4); sudo_button gtk_check_button_new_with_label (_("Use sudo when running virt-v2v")); gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (sudo_button), config->sudo); - gtk_table_attach (GTK_TABLE (table), sudo_button, - 1, 2, 6, 7, GTK_FILL, GTK_FILL, 4, 4); + table_attach (table, sudo_button, + 1, 2, 6, 7, GTK_FILL, GTK_FILL, 4, 4); - test_hbox = gtk_hbox_new (FALSE, 0); + hbox_new (test_hbox, FALSE, 0); test = gtk_button_new_with_label (_("Test connection")); gtk_box_pack_start (GTK_BOX (test_hbox), test, TRUE, FALSE, 0); - spinner_hbox = gtk_hbox_new (FALSE, 10); + hbox_new (spinner_hbox, FALSE, 10); spinner = gtk_spinner_new (); gtk_box_pack_start (GTK_BOX (spinner_hbox), spinner, FALSE, FALSE, 0); spinner_message = gtk_label_new (NULL); gtk_label_set_line_wrap (GTK_LABEL (spinner_message), TRUE); - gtk_misc_set_padding (GTK_MISC (spinner_message), 10, 10); + set_padding (spinner_message, 10, 10); gtk_box_pack_start (GTK_BOX (spinner_hbox), spinner_message, TRUE, TRUE, 0); - gtk_box_pack_start (GTK_BOX (GTK_DIALOG (conn_dlg)->vbox), - intro, TRUE, TRUE, 0); - gtk_box_pack_start (GTK_BOX (GTK_DIALOG (conn_dlg)->vbox), - table, TRUE, TRUE, 0); - gtk_box_pack_start (GTK_BOX (GTK_DIALOG (conn_dlg)->vbox), - test_hbox, FALSE, FALSE, 0); - gtk_box_pack_start (GTK_BOX (GTK_DIALOG (conn_dlg)->vbox), - spinner_hbox, TRUE, TRUE, 0); + gtk_box_pack_start + (GTK_BOX (gtk_dialog_get_content_area (GTK_DIALOG (conn_dlg))), + intro, TRUE, TRUE, 0); + gtk_box_pack_start + (GTK_BOX (gtk_dialog_get_content_area (GTK_DIALOG (conn_dlg))), + table, TRUE, TRUE, 0); + gtk_box_pack_start + (GTK_BOX (gtk_dialog_get_content_area (GTK_DIALOG (conn_dlg))), + test_hbox, FALSE, FALSE, 0); + gtk_box_pack_start + (GTK_BOX (gtk_dialog_get_content_area (GTK_DIALOG (conn_dlg))), + spinner_hbox, TRUE, TRUE, 0); /* Buttons. */ gtk_dialog_add_buttons (GTK_DIALOG (conn_dlg), @@ -303,7 +408,7 @@ show_connection_dialog (void) /* Show everything except the spinner. */ gtk_widget_show_all (conn_dlg); - gtk_widget_hide_all (spinner_hbox); + gtk_widget_hide (spinner_hbox); } /** @@ -396,43 +501,84 @@ test_connection_thread (void *data) struct config *copy = data; int r; - gdk_threads_enter (); + g_idle_add (start_spinner, NULL); + + wait_network_online (copy); + r = test_connection (copy); + free_config (copy); + + g_idle_add (stop_spinner, NULL); + + if (r == -1) + g_idle_add (test_connection_error, NULL); + else + g_idle_add (test_connection_ok, NULL); + + /* Thread is detached anyway, so no one is waiting for the status. */ + return NULL; +} + +/** + * Idle task called from C<test_connection_thread> (but run on the + * main thread) to start the spinner in the connection dialog. + */ +static gboolean +start_spinner (gpointer user_data) +{ gtk_label_set_text (GTK_LABEL (spinner_message), _("Testing the connection to the conversion server ...")); gtk_spinner_start (GTK_SPINNER (spinner)); - gdk_threads_leave (); + return FALSE; +} - wait_network_online (copy); - r = test_connection (copy); - free_config (copy); - - gdk_threads_enter (); +/** + * Idle task called from C<test_connection_thread> (but run on the + * main thread) to stop the spinner in the connection dialog. + */ +static gboolean +stop_spinner (gpointer user_data) +{ gtk_spinner_stop (GTK_SPINNER (spinner)); + return FALSE; +} - if (r == -1) { - /* Error testing the connection. */ - const char *err = get_ssh_error (); +/** + * Idle task called from C<test_connection_thread> (but run on the + * main thread) when there is an error. Display the error message and + * disable the C<Next> button so the user is forced to correct it. + */ +static gboolean +test_connection_error (gpointer user_data) +{ + const char *err = get_ssh_error (); - gtk_label_set_text (GTK_LABEL (spinner_message), err); - /* Disable the Next button. */ - gtk_widget_set_sensitive (next_button, FALSE); - } - else { - /* Connection is good. */ - gtk_label_set_text (GTK_LABEL (spinner_message), - _("Connected to the conversion server.\n" - "Press the \"Next\" button to configure the conversion process.")); - /* Enable the Next button. */ - gtk_widget_set_sensitive (next_button, TRUE); - gtk_widget_grab_focus (next_button); + gtk_label_set_text (GTK_LABEL (spinner_message), err); + /* Disable the Next button. */ + gtk_widget_set_sensitive (next_button, FALSE); - /* Update the information in the conversion dialog. */ - set_info_label (); - } - gdk_threads_leave (); + return FALSE; +} - /* Thread is detached anyway, so no one is waiting for the status. */ - return NULL; +/** + * Idle task called from C<test_connection_thread> (but run on the + * main thread) when the connection test was successful. + */ +static gboolean +test_connection_ok (gpointer user_data) +{ + gtk_label_set_text + (GTK_LABEL (spinner_message), + _("Connected to the conversion server.\n" + "Press the \"Next\" button to configure the conversion process.")); + + /* Enable the Next button. */ + gtk_widget_set_sensitive (next_button, TRUE); + gtk_widget_grab_focus (next_button); + + /* Update the information in the conversion dialog. */ + set_info_label (); + + return FALSE; } /** @@ -548,55 +694,55 @@ create_conversion_dialog (struct config *config) gtk_widget_set_size_request (conv_dlg, 900, 560); /* The main dialog area. */ - hbox = gtk_hbox_new (TRUE, 1); - left_vbox = gtk_vbox_new (FALSE, 1); - right_vbox = gtk_vbox_new (TRUE, 1); + hbox_new (hbox, TRUE, 1); + vbox_new (left_vbox, FALSE, 1); + vbox_new (right_vbox, TRUE, 1); /* The left column: target properties and output options. */ target_frame = gtk_frame_new (_("Target properties")); gtk_container_set_border_width (GTK_CONTAINER (target_frame), 4); - target_vbox = gtk_vbox_new (FALSE, 1); + vbox_new (target_vbox, FALSE, 1); - target_tbl = gtk_table_new (3, 3, FALSE); + table_new (target_tbl, 3, 3); guestname_label = gtk_label_new (_("Name:")); - gtk_misc_set_alignment (GTK_MISC (guestname_label), 1., 0.5); - gtk_table_attach (GTK_TABLE (target_tbl), guestname_label, - 0, 1, 0, 1, GTK_FILL, GTK_FILL, 1, 1); + set_alignment (guestname_label, 1., 0.5); + table_attach (target_tbl, guestname_label, + 0, 1, 0, 1, GTK_FILL, GTK_FILL, 1, 1); guestname_entry = gtk_entry_new (); if (config->guestname != NULL) gtk_entry_set_text (GTK_ENTRY (guestname_entry), config->guestname); - gtk_table_attach (GTK_TABLE (target_tbl), guestname_entry, - 1, 2, 0, 1, GTK_FILL, GTK_FILL, 1, 1); + table_attach (target_tbl, guestname_entry, + 1, 2, 0, 1, GTK_FILL, GTK_FILL, 1, 1); vcpus_label = gtk_label_new (_("# vCPUs:")); - gtk_misc_set_alignment (GTK_MISC (vcpus_label), 1., 0.5); - gtk_table_attach (GTK_TABLE (target_tbl), vcpus_label, - 0, 1, 1, 2, GTK_FILL, GTK_FILL, 1, 1); + set_alignment (vcpus_label, 1., 0.5); + table_attach (target_tbl, vcpus_label, + 0, 1, 1, 2, GTK_FILL, GTK_FILL, 1, 1); vcpus_entry = gtk_entry_new (); snprintf (vcpus_str, sizeof vcpus_str, "%d", config->vcpus); gtk_entry_set_text (GTK_ENTRY (vcpus_entry), vcpus_str); - gtk_table_attach (GTK_TABLE (target_tbl), vcpus_entry, - 1, 2, 1, 2, GTK_FILL, GTK_FILL, 1, 1); + table_attach (target_tbl, vcpus_entry, + 1, 2, 1, 2, GTK_FILL, GTK_FILL, 1, 1); vcpus_warning = gtk_image_new_from_stock (GTK_STOCK_DIALOG_WARNING, GTK_ICON_SIZE_BUTTON); - gtk_table_attach (GTK_TABLE (target_tbl), vcpus_warning, - 2, 3, 1, 2, 0, 0, 1, 1); + table_attach (target_tbl, vcpus_warning, + 2, 3, 1, 2, 0, 0, 1, 1); memory_label = gtk_label_new (_("Memory (MB):")); - gtk_misc_set_alignment (GTK_MISC (memory_label), 1., 0.5); - gtk_table_attach (GTK_TABLE (target_tbl), memory_label, - 0, 1, 2, 3, GTK_FILL, GTK_FILL, 1, 1); + set_alignment (memory_label, 1., 0.5); + table_attach (target_tbl, memory_label, + 0, 1, 2, 3, GTK_FILL, GTK_FILL, 1, 1); memory_entry = gtk_entry_new (); snprintf (memory_str, sizeof memory_str, "%" PRIu64, config->memory / 1024 / 1024); gtk_entry_set_text (GTK_ENTRY (memory_entry), memory_str); - gtk_table_attach (GTK_TABLE (target_tbl), memory_entry, - 1, 2, 2, 3, GTK_FILL, GTK_FILL, 1, 1); + table_attach (target_tbl, memory_entry, + 1, 2, 2, 3, GTK_FILL, GTK_FILL, 1, 1); memory_warning = gtk_image_new_from_stock (GTK_STOCK_DIALOG_WARNING, GTK_ICON_SIZE_BUTTON); - gtk_table_attach (GTK_TABLE (target_tbl), memory_warning, - 2, 3, 2, 3, 0, 0, 1, 1); + table_attach (target_tbl, memory_warning, + 2, 3, 2, 3, 0, 0, 1, 1); gtk_box_pack_start (GTK_BOX (target_vbox), target_tbl, TRUE, TRUE, 0); @@ -612,56 +758,56 @@ create_conversion_dialog (struct config *config) output_frame = gtk_frame_new (_("Virt-v2v output options")); gtk_container_set_border_width (GTK_CONTAINER (output_frame), 4); - output_vbox = gtk_vbox_new (FALSE, 1); + vbox_new (output_vbox, FALSE, 1); - output_tbl = gtk_table_new (5, 2, FALSE); + table_new (output_tbl, 5, 2); o_label = gtk_label_new (_("Output to (-o):")); - gtk_misc_set_alignment (GTK_MISC (o_label), 1., 0.5); - gtk_table_attach (GTK_TABLE (output_tbl), o_label, - 0, 1, 0, 1, GTK_FILL, GTK_FILL, 1, 1); + set_alignment (o_label, 1., 0.5); + table_attach (output_tbl, o_label, + 0, 1, 0, 1, GTK_FILL, GTK_FILL, 1, 1); o_combo = gtk_combo_box_text_new (); gtk_widget_set_tooltip_markup (o_combo, _("<b>libvirt</b> means send the converted guest to libvirt-managed KVM on the conversion server. <b>local</b> means put it in a directory on the conversion server. <b>rhev</b> means write it to RHEV-M/oVirt. <b>glance</b> means write it to OpenStack Glance. See the virt-v2v(1) manual page for more information about output options.")); repopulate_output_combo (config); - gtk_table_attach (GTK_TABLE (output_tbl), o_combo, - 1, 2, 0, 1, GTK_FILL, GTK_FILL, 1, 1); + table_attach (output_tbl, o_combo, + 1, 2, 0, 1, GTK_FILL, GTK_FILL, 1, 1); oc_label = gtk_label_new (_("Output conn. (-oc):")); - gtk_misc_set_alignment (GTK_MISC (oc_label), 1., 0.5); - gtk_table_attach (GTK_TABLE (output_tbl), oc_label, - 0, 1, 1, 2, GTK_FILL, GTK_FILL, 1, 1); + set_alignment (oc_label, 1., 0.5); + table_attach (output_tbl, oc_label, + 0, 1, 1, 2, GTK_FILL, GTK_FILL, 1, 1); oc_entry = gtk_entry_new (); gtk_widget_set_tooltip_markup (oc_entry, _("For <b>libvirt</b> only, the libvirt connection URI, or leave blank to add the guest to the default libvirt instance on the conversion server. For others, leave this field blank.")); if (config->output_connection != NULL) gtk_entry_set_text (GTK_ENTRY (oc_entry), config->output_connection); - gtk_table_attach (GTK_TABLE (output_tbl), oc_entry, - 1, 2, 1, 2, GTK_FILL, GTK_FILL, 1, 1); + table_attach (output_tbl, oc_entry, + 1, 2, 1, 2, GTK_FILL, GTK_FILL, 1, 1); os_label = gtk_label_new (_("Output storage (-os):")); - gtk_misc_set_alignment (GTK_MISC (os_label), 1., 0.5); - gtk_table_attach (GTK_TABLE (output_tbl), os_label, - 0, 1, 2, 3, GTK_FILL, GTK_FILL, 1, 1); + set_alignment (os_label, 1., 0.5); + table_attach (output_tbl, os_label, + 0, 1, 2, 3, GTK_FILL, GTK_FILL, 1, 1); os_entry = gtk_entry_new (); gtk_widget_set_tooltip_markup (os_entry, _("For <b>local</b>, put the directory name on the conversion server. For <b>rhev</b>, put the Export Storage Domain (server:/mountpoint). For others, leave this field blank.")); if (config->output_storage != NULL) gtk_entry_set_text (GTK_ENTRY (os_entry), config->output_storage); - gtk_table_attach (GTK_TABLE (output_tbl), os_entry, - 1, 2, 2, 3, GTK_FILL, GTK_FILL, 1, 1); + table_attach (output_tbl, os_entry, + 1, 2, 2, 3, GTK_FILL, GTK_FILL, 1, 1); of_label = gtk_label_new (_("Output format (-of):")); - gtk_misc_set_alignment (GTK_MISC (of_label), 1., 0.5); - gtk_table_attach (GTK_TABLE (output_tbl), of_label, - 0, 1, 3, 4, GTK_FILL, GTK_FILL, 1, 1); + set_alignment (of_label, 1., 0.5); + table_attach (output_tbl, of_label, + 0, 1, 3, 4, GTK_FILL, GTK_FILL, 1, 1); of_entry = gtk_entry_new (); gtk_widget_set_tooltip_markup (of_entry, _("The output disk format, typically <b>raw</b> or <b>qcow2</b>. If blank, defaults to <b>raw</b>.")); if (config->output_format != NULL) gtk_entry_set_text (GTK_ENTRY (of_entry), config->output_format); - gtk_table_attach (GTK_TABLE (output_tbl), of_entry, - 1, 2, 3, 4, GTK_FILL, GTK_FILL, 1, 1); + table_attach (output_tbl, of_entry, + 1, 2, 3, 4, GTK_FILL, GTK_FILL, 1, 1); oa_label = gtk_label_new (_("Output allocation (-oa):")); - gtk_misc_set_alignment (GTK_MISC (oa_label), 1., 0.5); - gtk_table_attach (GTK_TABLE (output_tbl), oa_label, - 0, 1, 4, 5, GTK_FILL, GTK_FILL, 1, 1); + set_alignment (oa_label, 1., 0.5); + table_attach (output_tbl, oa_label, + 0, 1, 4, 5, GTK_FILL, GTK_FILL, 1, 1); oa_combo = gtk_combo_box_text_new (); gtk_combo_box_text_append_text (GTK_COMBO_BOX_TEXT (oa_combo), "sparse"); @@ -675,8 +821,8 @@ create_conversion_dialog (struct config *config) gtk_combo_box_set_active (GTK_COMBO_BOX (oa_combo), 0); break; } - gtk_table_attach (GTK_TABLE (output_tbl), oa_combo, - 1, 2, 4, 5, GTK_FILL, GTK_FILL, 1, 1); + table_attach (output_tbl, oa_combo, + 1, 2, 4, 5, GTK_FILL, GTK_FILL, 1, 1); debug_button gtk_check_button_new_with_label (_("Enable server-side debugging\n" @@ -691,7 +837,7 @@ create_conversion_dialog (struct config *config) info_frame = gtk_frame_new (_("Information")); gtk_container_set_border_width (GTK_CONTAINER (info_frame), 4); info_label = gtk_label_new (NULL); - gtk_misc_set_alignment (GTK_MISC (info_label), 0.1, 0.5); + set_alignment (info_label, 0.1, 0.5); set_info_label (); gtk_container_add (GTK_CONTAINER (info_frame), info_label); @@ -704,8 +850,7 @@ create_conversion_dialog (struct config *config) GTK_POLICY_NEVER, GTK_POLICY_AUTOMATIC); disks_list = gtk_tree_view_new (); populate_disks (GTK_TREE_VIEW (disks_list)); - gtk_scrolled_window_add_with_viewport (GTK_SCROLLED_WINDOW (disks_sw), - disks_list); + scrolled_window_add_with_viewport (disks_sw, disks_list); gtk_container_add (GTK_CONTAINER (disks_frame), disks_sw); removable_frame = gtk_frame_new (_("Removable media")); @@ -716,8 +861,7 @@ create_conversion_dialog (struct config *config) GTK_POLICY_NEVER, GTK_POLICY_AUTOMATIC); removable_list = gtk_tree_view_new (); populate_removable (GTK_TREE_VIEW (removable_list)); - gtk_scrolled_window_add_with_viewport (GTK_SCROLLED_WINDOW (removable_sw), - removable_list); + scrolled_window_add_with_viewport (removable_sw, removable_list); gtk_container_add (GTK_CONTAINER (removable_frame), removable_sw); interfaces_frame = gtk_frame_new (_("Network interfaces")); @@ -732,8 +876,7 @@ create_conversion_dialog (struct config *config) G_CALLBACK (maybe_identify_click), NULL); gtk_widget_set_tooltip_markup (interfaces_list, _("Left click on an interface name to flash the light on the physical interface.")); populate_interfaces (GTK_TREE_VIEW (interfaces_list)); - gtk_scrolled_window_add_with_viewport (GTK_SCROLLED_WINDOW (interfaces_sw), - interfaces_list); + scrolled_window_add_with_viewport (interfaces_sw, interfaces_list); gtk_container_add (GTK_CONTAINER (interfaces_frame), interfaces_sw); /* Pack the top level dialog. */ @@ -747,8 +890,9 @@ create_conversion_dialog (struct config *config) gtk_box_pack_start (GTK_BOX (hbox), left_vbox, TRUE, TRUE, 0); gtk_box_pack_start (GTK_BOX (hbox), right_vbox, TRUE, TRUE, 0); - gtk_box_pack_start (GTK_BOX (GTK_DIALOG (conv_dlg)->vbox), - hbox, TRUE, TRUE, 0); + gtk_box_pack_start + (GTK_BOX (gtk_dialog_get_content_area (GTK_DIALOG (conv_dlg))), + hbox, TRUE, TRUE, 0); /* Buttons. */ gtk_dialog_add_buttons (GTK_DIALOG (conv_dlg), @@ -1415,11 +1559,12 @@ get_memory_from_conv_dlg (void) /*----------------------------------------------------------------------*/ /* Running dialog. */ -static void set_log_dir (const char *remote_dir); -static void set_status (const char *msg); -static void add_v2v_output (const char *msg); -static void add_v2v_output_2 (const char *msg, size_t len); +static gboolean set_log_dir (gpointer remote_dir); +static gboolean set_status (gpointer msg); +static gboolean add_v2v_output (gpointer msg); static void *start_conversion_thread (void *data); +static gboolean conversion_error (gpointer user_data); +static gboolean conversion_finished (gpointer user_data); static void cancel_conversion_clicked (GtkWidget *w, gpointer data); static void reboot_clicked (GtkWidget *w, gpointer data); static gboolean close_running_dialog (GtkWidget *w, GdkEvent *event, gpointer data); @@ -1444,23 +1589,27 @@ create_running_dialog (void) v2v_output = gtk_text_view_new (); gtk_text_view_set_editable (GTK_TEXT_VIEW (v2v_output), FALSE); gtk_text_view_set_wrap_mode (GTK_TEXT_VIEW (v2v_output), GTK_WRAP_CHAR); + /* XXX Gtk3 ignores this, why? */ gtk_widget_set_size_request (v2v_output, 700, 400); log_label = gtk_label_new (NULL); - gtk_misc_set_alignment (GTK_MISC (log_label), 0., 0.5); - gtk_misc_set_padding (GTK_MISC (log_label), 10, 10); + set_alignment (log_label, 0., 0.5); + set_padding (log_label, 10, 10); set_log_dir (NULL); status_label = gtk_label_new (NULL); - gtk_misc_set_alignment (GTK_MISC (status_label), 0., 0.5); - gtk_misc_set_padding (GTK_MISC (status_label), 10, 10); + set_alignment (status_label, 0., 0.5); + set_padding (status_label, 10, 10); gtk_container_add (GTK_CONTAINER (v2v_output_sw), v2v_output); - gtk_box_pack_start (GTK_BOX (GTK_DIALOG (run_dlg)->vbox), - v2v_output_sw, TRUE, TRUE, 0); - gtk_box_pack_start (GTK_BOX (GTK_DIALOG (run_dlg)->vbox), - log_label, TRUE, TRUE, 0); - gtk_box_pack_start (GTK_BOX (GTK_DIALOG (run_dlg)->vbox), - status_label, TRUE, TRUE, 0); + gtk_box_pack_start + (GTK_BOX (gtk_dialog_get_content_area (GTK_DIALOG (run_dlg))), + v2v_output_sw, TRUE, TRUE, 0); + gtk_box_pack_start + (GTK_BOX (gtk_dialog_get_content_area (GTK_DIALOG (run_dlg))), + log_label, TRUE, TRUE, 0); + gtk_box_pack_start + (GTK_BOX (gtk_dialog_get_content_area (GTK_DIALOG (run_dlg))), + status_label, TRUE, TRUE, 0); /* Buttons. */ gtk_dialog_add_buttons (GTK_DIALOG (run_dlg), @@ -1499,9 +1648,19 @@ show_running_dialog (void) gtk_widget_set_sensitive (reboot_button, FALSE); } -static void -set_log_dir (const char *remote_dir) +/** + * Display the remote log directory in the running dialog. + * + * If this isn't called from the main thread, then you must only + * call it via an idle task (C<g_idle_add>). + * + * B<NB:> This frees the remote_dir (C<user_data> pointer) which was + * strdup'd in C<notify_ui_callback>. + */ +static gboolean +set_log_dir (gpointer user_data) { + CLEANUP_FREE const char *remote_dir = user_data; CLEANUP_FREE char *msg; if (asprintf (&msg, @@ -1513,45 +1672,70 @@ set_log_dir (const char *remote_dir) error (EXIT_FAILURE, errno, "asprintf"); gtk_label_set_text (GTK_LABEL (log_label), msg); + + return FALSE; } -static void -set_status (const char *msg) +/** + * Display the conversion status in the running dialog. + * + * If this isn't called from the main thread, then you must only + * call it via an idle task (C<g_idle_add>). + * + * B<NB:> This frees the message (C<user_data> pointer) which was + * strdup'd in C<notify_ui_callback>. + */ +static gboolean +set_status (gpointer user_data) { + CLEANUP_FREE const char *msg = user_data; + gtk_label_set_text (GTK_LABEL (status_label), msg); + + return FALSE; } +static void add_v2v_output_helper (const char *msg, size_t len); + /** * Append output from the virt-v2v process to the buffer, and scroll * to ensure it is visible. + * + * If this isn't called from the main thread, then you must only + * call it via an idle task (C<g_idle_add>). + * + * B<NB:> This frees the message (C<user_data> pointer) which was + * strdup'd in C<notify_ui_callback>. */ -static void -add_v2v_output (const char *msg) +static gboolean +add_v2v_output (gpointer user_data) { + CLEANUP_FREE const char *msg = user_data; static size_t linelen = 0; const char *p0, *p; - /* Gtk2 (in ~ Fedora 23) has a regression where it takes much - * longer to display long lines, to the point where the virt-p2v - * UI would still be slowly display kernel modules while the - * conversion had finished. For this reason, arbitrarily break - * long lines. + /* Gtk2 (in ~ Fedora 23) has a regression where it takes much longer + * to display long lines, to the point where the virt-p2v UI would + * still be slowly displaying kernel modules while the conversion + * had finished. For this reason, arbitrarily break long lines. */ for (p0 = p = msg; *p; ++p) { linelen++; if (*p == '\n' || linelen > 1024) { - add_v2v_output_2 (p0, p-p0+1); + add_v2v_output_helper (p0, p-p0+1); if (*p != '\n') - add_v2v_output_2 ("\n", 1); + add_v2v_output_helper ("\n", 1); linelen = 0; p0 = p+1; } } - add_v2v_output_2 (p0, p-p0); + add_v2v_output_helper (p0, p-p0); + + return FALSE; } static void -add_v2v_output_2 (const char *msg, size_t len) +add_v2v_output_helper (const char *msg, size_t len) { GtkTextBuffer *buf; GtkTextIter iter; @@ -1689,73 +1873,108 @@ start_conversion_thread (void *data) { struct config *copy = data; int r; - GtkWidget *dlg; r = start_conversion (copy, notify_ui_callback); free_config (copy); - gdk_threads_enter (); - - if (r == -1) { - const char *err = get_conversion_error (); - - dlg = gtk_message_dialog_new (GTK_WINDOW (run_dlg), - GTK_DIALOG_DESTROY_WITH_PARENT, - GTK_MESSAGE_ERROR, - GTK_BUTTONS_OK, - _("Conversion failed: %s"), err); - gtk_window_set_title (GTK_WINDOW (dlg), _("Conversion failed")); - gtk_dialog_run (GTK_DIALOG (dlg)); - gtk_widget_destroy (dlg); - } - else { - dlg = gtk_message_dialog_new (GTK_WINDOW (run_dlg), - GTK_DIALOG_DESTROY_WITH_PARENT, - GTK_MESSAGE_INFO, - GTK_BUTTONS_OK, - _("The conversion was successful.")); - gtk_window_set_title (GTK_WINDOW (dlg), _("Conversion was successful")); - gtk_dialog_run (GTK_DIALOG (dlg)); - gtk_widget_destroy (dlg); - } - - /* Disable the cancel button. */ - gtk_widget_set_sensitive (cancel_button, FALSE); - - /* Enable the reboot button. */ - gtk_widget_set_sensitive (reboot_button, TRUE); - - gdk_threads_leave (); + if (r == -1) + g_idle_add (conversion_error, NULL); + else + g_idle_add (conversion_finished, NULL); /* Thread is detached anyway, so no one is waiting for the status. */ return NULL; } +/** + * Idle task called from C<start_conversion_thread> (but run on the + * main thread) when there was an error during the conversion. + */ +static gboolean +conversion_error (gpointer user_data) +{ + const char *err = get_conversion_error (); + GtkWidget *dlg; + + dlg = gtk_message_dialog_new (GTK_WINDOW (run_dlg), + GTK_DIALOG_DESTROY_WITH_PARENT, + GTK_MESSAGE_ERROR, + GTK_BUTTONS_OK, + _("Conversion failed: %s"), err); + gtk_window_set_title (GTK_WINDOW (dlg), _("Conversion failed")); + gtk_dialog_run (GTK_DIALOG (dlg)); + gtk_widget_destroy (dlg); + + /* Disable the cancel button. */ + gtk_widget_set_sensitive (cancel_button, FALSE); + + /* Enable the reboot button. */ + gtk_widget_set_sensitive (reboot_button, TRUE); + + return FALSE; +} + +/** + * Idle task called from C<start_conversion_thread> (but run on the + * main thread) when the conversion completed without errors. + */ +static gboolean +conversion_finished (gpointer user_data) +{ + GtkWidget *dlg; + + dlg = gtk_message_dialog_new (GTK_WINDOW (run_dlg), + GTK_DIALOG_DESTROY_WITH_PARENT, + GTK_MESSAGE_INFO, + GTK_BUTTONS_OK, + _("The conversion was successful.")); + gtk_window_set_title (GTK_WINDOW (dlg), _("Conversion was successful")); + gtk_dialog_run (GTK_DIALOG (dlg)); + gtk_widget_destroy (dlg); + + /* Disable the cancel button. */ + gtk_widget_set_sensitive (cancel_button, FALSE); + + /* Enable the reboot button. */ + gtk_widget_set_sensitive (reboot_button, TRUE); + + return FALSE; +} + +/** + * This is called from F<conversion.c>:C<start_conversion> + * when there is a status change or a log message. + */ static void notify_ui_callback (int type, const char *data) { - gdk_threads_enter (); + /* Because we call the functions as idle callbacks which run + * in the main thread some time later, we must duplicate the + * 'data' parameter (which is always a \0-terminated string). + * + * This is freed by the idle task function. + */ + char *copy = strdup (data); switch (type) { case NOTIFY_LOG_DIR: - set_log_dir (data); + g_idle_add (set_log_dir, (gpointer) copy); break; case NOTIFY_REMOTE_MESSAGE: - add_v2v_output (data); + g_idle_add (add_v2v_output, (gpointer) copy); break; case NOTIFY_STATUS: - set_status (data); + g_idle_add (set_status, (gpointer) copy); break; default: fprintf (stderr, "%s: unknown message during conversion: type=%d data=%s\n", guestfs_int_program_name, type, data); + free (copy); } - - gdk_threads_leave (); } static gboolean diff --git a/p2v/main.c b/p2v/main.c index e1be311..ba371fc 100644 --- a/p2v/main.c +++ b/p2v/main.c @@ -138,8 +138,6 @@ main (int argc, char *argv[]) if (glib_check_version (2, 32, 0) != NULL) /* This checks < 2.32 */ g_thread_init (NULL); #endif - gdk_threads_init (); - gdk_threads_enter (); gui_possible = gtk_init_check (&argc, &argv); for (;;) { -- 2.7.4
Apparently Analagous Threads
- [PATCH v3] p2v: Allow virt-p2v to be built with Gtk 2 or 3.
- [PATCH v2 0/3] p2v: Allow virt-p2v to be built with Gtk 2 or 3.
- [PATCH] p2v: add mnemonics to labels and buttons (RHBZ#1379289)
- [PATCH 0/2] p2v: Warn if vcpus or memory would be larger than supported by RHEL (RHBZ#823758).
- [PATCH v2 3/3] p2v: Allow virt-p2v to be built with Gtk 2 or 3.