diff --git a/app/core/gimp-gui.c b/app/core/gimp-gui.c index f89df788b7..bbfc862551 100644 --- a/app/core/gimp-gui.c +++ b/app/core/gimp-gui.c @@ -321,17 +321,17 @@ gimp_get_empty_display (Gimp *gimp) return NULL; } -guint32 +GBytes * gimp_get_display_window_id (Gimp *gimp, GimpDisplay *display) { - g_return_val_if_fail (GIMP_IS_GIMP (gimp), -1); - g_return_val_if_fail (GIMP_IS_DISPLAY (display), -1); + g_return_val_if_fail (GIMP_IS_GIMP (gimp), NULL); + g_return_val_if_fail (GIMP_IS_DISPLAY (display), NULL); if (gimp->gui.display_get_window_id) return gimp->gui.display_get_window_id (display); - return -1; + return NULL; } GimpDisplay * diff --git a/app/core/gimp-gui.h b/app/core/gimp-gui.h index ac16fc3f64..d5daf1f21c 100644 --- a/app/core/gimp-gui.h +++ b/app/core/gimp-gui.h @@ -54,7 +54,7 @@ struct _GimpGui GimpObject * (* get_window_strategy) (Gimp *gimp); GimpDisplay * (* get_empty_display) (Gimp *gimp); - guint32 (* display_get_window_id) (GimpDisplay *display); + GBytes * (* display_get_window_id) (GimpDisplay *display); GimpDisplay * (* display_create) (Gimp *gimp, GimpImage *image, GimpUnit unit, @@ -122,7 +122,7 @@ GimpDisplay * gimp_get_display_by_id (Gimp *gimp, gint ID); gint gimp_get_display_id (Gimp *gimp, GimpDisplay *display); -guint32 gimp_get_display_window_id (Gimp *gimp, +GBytes * gimp_get_display_window_id (Gimp *gimp, GimpDisplay *display); GimpDisplay * gimp_create_display (Gimp *gimp, GimpImage *image, diff --git a/app/core/gimppdbprogress.c b/app/core/gimppdbprogress.c index 7ac6b42be6..0b7020b986 100644 --- a/app/core/gimppdbprogress.c +++ b/app/core/gimppdbprogress.c @@ -74,7 +74,7 @@ static void gimp_pdb_progress_progress_set_value (GimpProgress *progress gdouble percentage); static gdouble gimp_pdb_progress_progress_get_value (GimpProgress *progress); static void gimp_pdb_progress_progress_pulse (GimpProgress *progress); -static guint32 gimp_pdb_progress_progress_get_window_id (GimpProgress *progress); +static GBytes * gimp_pdb_progress_progress_get_window_id (GimpProgress *progress); static GObjectClass *parent_class = NULL; @@ -237,14 +237,13 @@ gimp_pdb_progress_set_property (GObject *object, } } -static gdouble -gimp_pdb_progress_run_callback (GimpPdbProgress *progress, - GimpProgressCommand command, - const gchar *text, - gdouble value) +static void +gimp_pdb_progress_run_callback (GimpPdbProgress *progress, + GimpProgressCommand command, + const gchar *text, + gdouble value, + GBytes **handle) { - gdouble retval = 0; - if (progress->callback_name && ! progress->callback_busy) { GimpValueArray *return_vals; @@ -270,17 +269,16 @@ gimp_pdb_progress_run_callback (GimpPdbProgress *progress, g_type_name (G_TYPE_FROM_INSTANCE (progress))); } else if (gimp_value_array_length (return_vals) >= 2 && - G_VALUE_HOLDS_DOUBLE (gimp_value_array_index (return_vals, 1))) + handle != NULL && + G_VALUE_HOLDS_BOXED (gimp_value_array_index (return_vals, 1))) { - retval = g_value_get_double (gimp_value_array_index (return_vals, 1)); + *handle = g_value_dup_boxed (gimp_value_array_index (return_vals, 1)); } gimp_value_array_unref (return_vals); progress->callback_busy = FALSE; } - - return retval; } static GimpProgress * @@ -294,7 +292,7 @@ gimp_pdb_progress_progress_start (GimpProgress *progress, { gimp_pdb_progress_run_callback (pdb_progress, GIMP_PROGRESS_COMMAND_START, - message, 0.0); + message, 0.0, NULL); pdb_progress->active = TRUE; pdb_progress->value = 0.0; @@ -314,7 +312,7 @@ gimp_pdb_progress_progress_end (GimpProgress *progress) { gimp_pdb_progress_run_callback (pdb_progress, GIMP_PROGRESS_COMMAND_END, - NULL, 0.0); + NULL, 0.0, NULL); pdb_progress->active = FALSE; pdb_progress->value = 0.0; @@ -338,7 +336,7 @@ gimp_pdb_progress_progress_set_text (GimpProgress *progress, if (pdb_progress->active) gimp_pdb_progress_run_callback (pdb_progress, GIMP_PROGRESS_COMMAND_SET_TEXT, - message, 0.0); + message, 0.0, NULL); } static void @@ -351,7 +349,7 @@ gimp_pdb_progress_progress_set_value (GimpProgress *progress, { gimp_pdb_progress_run_callback (pdb_progress, GIMP_PROGRESS_COMMAND_SET_VALUE, - NULL, percentage); + NULL, percentage, NULL); pdb_progress->value = percentage; } } @@ -373,18 +371,19 @@ gimp_pdb_progress_progress_pulse (GimpProgress *progress) if (pdb_progress->active) gimp_pdb_progress_run_callback (pdb_progress, GIMP_PROGRESS_COMMAND_PULSE, - NULL, 0.0); + NULL, 0.0, NULL); } -static guint32 +static GBytes * gimp_pdb_progress_progress_get_window_id (GimpProgress *progress) { GimpPdbProgress *pdb_progress = GIMP_PDB_PROGRESS (progress); + GBytes *handle = NULL; - return (guint32) - gimp_pdb_progress_run_callback (pdb_progress, - GIMP_PROGRESS_COMMAND_GET_WINDOW, - NULL, 0.0); + gimp_pdb_progress_run_callback (pdb_progress, + GIMP_PROGRESS_COMMAND_GET_WINDOW, + NULL, 0.0, &handle); + return handle; } GimpPdbProgress * diff --git a/app/core/gimpprogress.c b/app/core/gimpprogress.c index 90a2ffc93a..33029b8ca7 100644 --- a/app/core/gimpprogress.c +++ b/app/core/gimpprogress.c @@ -205,7 +205,7 @@ gimp_progress_pulse (GimpProgress *progress) progress_iface->pulse (progress); } -guint32 +GBytes * gimp_progress_get_window_id (GimpProgress *progress) { GimpProgressInterface *progress_iface; @@ -217,7 +217,7 @@ gimp_progress_get_window_id (GimpProgress *progress) if (progress_iface->get_window_id) return progress_iface->get_window_id (progress); - return 0; + return NULL; } gboolean diff --git a/app/core/gimpprogress.h b/app/core/gimpprogress.h index a7fad144a4..472374700d 100644 --- a/app/core/gimpprogress.h +++ b/app/core/gimpprogress.h @@ -44,7 +44,7 @@ struct _GimpProgressInterface gdouble (* get_value) (GimpProgress *progress); void (* pulse) (GimpProgress *progress); - guint32 (* get_window_id) (GimpProgress *progress); + GBytes * (* get_window_id) (GimpProgress *progress); gboolean (* message) (GimpProgress *progress, Gimp *gimp, @@ -74,7 +74,7 @@ void gimp_progress_set_value (GimpProgress *progress, gdouble gimp_progress_get_value (GimpProgress *progress); void gimp_progress_pulse (GimpProgress *progress); -guint32 gimp_progress_get_window_id (GimpProgress *progress); +GBytes * gimp_progress_get_window_id (GimpProgress *progress); gboolean gimp_progress_message (GimpProgress *progress, Gimp *gimp, diff --git a/app/core/gimpsubprogress.c b/app/core/gimpsubprogress.c index 82fb1ecff2..6ea205c0a9 100644 --- a/app/core/gimpsubprogress.c +++ b/app/core/gimpsubprogress.c @@ -55,7 +55,7 @@ static void gimp_sub_progress_set_value (GimpProgress *prog gdouble percentage); static gdouble gimp_sub_progress_get_value (GimpProgress *progress); static void gimp_sub_progress_pulse (GimpProgress *progress); -static guint32 gimp_sub_progress_get_window_id (GimpProgress *progress); +static GBytes * gimp_sub_progress_get_window_id (GimpProgress *progress); static gboolean gimp_sub_progress_message (GimpProgress *progress, Gimp *gimp, GimpMessageSeverity severity, @@ -224,7 +224,7 @@ gimp_sub_progress_pulse (GimpProgress *progress) gimp_progress_pulse (sub->progress); } -static guint32 +static GBytes * gimp_sub_progress_get_window_id (GimpProgress *progress) { GimpSubProgress *sub = GIMP_SUB_PROGRESS (progress); @@ -232,7 +232,7 @@ gimp_sub_progress_get_window_id (GimpProgress *progress) if (sub->progress) return gimp_progress_get_window_id (sub->progress); - return 0; + return NULL; } static gboolean diff --git a/app/display/gimpdisplay.c b/app/display/gimpdisplay.c index 142b1d350a..5a40bf3323 100644 --- a/app/display/gimpdisplay.c +++ b/app/display/gimpdisplay.c @@ -106,7 +106,7 @@ static void gimp_display_progress_set_value (GimpProgress *progre gdouble percentage); static gdouble gimp_display_progress_get_value (GimpProgress *progress); static void gimp_display_progress_pulse (GimpProgress *progress); -static guint32 gimp_display_progress_get_window_id (GimpProgress *progress); +static GBytes * gimp_display_progress_get_window_id (GimpProgress *progress); static gboolean gimp_display_progress_message (GimpProgress *progress, Gimp *gimp, GimpMessageSeverity severity, @@ -319,7 +319,7 @@ gimp_display_progress_pulse (GimpProgress *progress) gimp_progress_pulse (GIMP_PROGRESS (display->priv->shell)); } -static guint32 +static GBytes * gimp_display_progress_get_window_id (GimpProgress *progress) { GimpDisplayImpl *display = GIMP_DISPLAY_IMPL (progress); @@ -327,7 +327,7 @@ gimp_display_progress_get_window_id (GimpProgress *progress) if (display->priv->shell) return gimp_progress_get_window_id (GIMP_PROGRESS (display->priv->shell)); - return 0; + return NULL; } static gboolean diff --git a/app/display/gimpdisplayshell-progress.c b/app/display/gimpdisplayshell-progress.c index 4314a846ec..cf8c1e4449 100644 --- a/app/display/gimpdisplayshell-progress.c +++ b/app/display/gimpdisplayshell-progress.c @@ -99,15 +99,16 @@ gimp_display_shell_progress_pulse (GimpProgress *progress) gimp_progress_pulse (GIMP_PROGRESS (statusbar)); } -static guint32 +static GBytes * gimp_display_shell_progress_get_window_id (GimpProgress *progress) { - GtkWidget *toplevel = gtk_widget_get_toplevel (GTK_WIDGET (progress)); + GimpDisplayShell *shell = GIMP_DISPLAY_SHELL (progress); + GBytes *handle = NULL; - if (GTK_IS_WINDOW (toplevel)) - return gimp_window_get_native_id (GTK_WINDOW (toplevel)); + if (shell->window_handle) + handle = g_bytes_ref (shell->window_handle); - return 0; + return handle; } static gboolean diff --git a/app/display/gimpdisplayshell.c b/app/display/gimpdisplayshell.c index a2ee62c09f..70921d2ca0 100644 --- a/app/display/gimpdisplayshell.c +++ b/app/display/gimpdisplayshell.c @@ -23,6 +23,10 @@ #include #include +#ifdef GDK_WINDOWING_WAYLAND +#include +#endif + #include "libgimpbase/gimpbase.h" #include "libgimpmath/gimpmath.h" #include "libgimpcolor/gimpcolor.h" @@ -583,6 +587,8 @@ gimp_display_shell_constructed (GObject *object) G_CALLBACK (gimp_display_shell_canvas_grab_notify), shell); + gimp_widget_set_native_handle (GTK_WIDGET (shell), &shell->window_handle); + g_signal_connect (shell->canvas, "realize", G_CALLBACK (gimp_display_shell_canvas_realize), shell); @@ -847,6 +853,17 @@ gimp_display_shell_dispose (GObject *object) shell->display = NULL; + if (shell->window_handle != NULL) + { +#ifdef GDK_WINDOWING_WAYLAND + if (GDK_IS_WAYLAND_DISPLAY (gdk_display_get_default ()) && + /* The GdkWindow is likely already destroyed. */ + gtk_widget_get_window (GTK_WIDGET (shell)) != NULL) + gdk_wayland_window_unexport_handle (gtk_widget_get_window (GTK_WIDGET (shell))); +#endif + g_clear_pointer (&shell->window_handle, g_bytes_unref); + } + G_OBJECT_CLASS (parent_class)->dispose (object); } diff --git a/app/display/gimpdisplayshell.h b/app/display/gimpdisplayshell.h index e9786a0dbf..55099fd114 100644 --- a/app/display/gimpdisplayshell.h +++ b/app/display/gimpdisplayshell.h @@ -51,6 +51,8 @@ struct _GimpDisplayShell GimpDisplay *display; + GBytes *window_handle; + GimpUIManager *popup_manager; GdkMonitor *initial_monitor; diff --git a/app/gui/gui-message.c b/app/gui/gui-message.c index ef3762c3e2..bc38dd7122 100644 --- a/app/gui/gui-message.c +++ b/app/gui/gui-message.c @@ -283,10 +283,7 @@ progress_error_dialog (GimpProgress *progress) } else { - guint32 window_id = gimp_progress_get_window_id (progress); - - if (window_id) - gimp_window_set_transient_for (GTK_WINDOW (dialog), window_id); + gimp_window_set_transient_for (GTK_WINDOW (dialog), progress); } } diff --git a/app/gui/gui-vtable.c b/app/gui/gui-vtable.c index 1392ab8d76..f83293b1c2 100644 --- a/app/gui/gui-vtable.c +++ b/app/gui/gui-vtable.c @@ -132,7 +132,7 @@ static GFile * gui_get_theme_dir (Gimp *gimp); static GFile * gui_get_icon_theme_dir (Gimp *gimp); static GimpObject * gui_get_window_strategy (Gimp *gimp); static GimpDisplay * gui_get_empty_display (Gimp *gimp); -static guint32 gui_display_get_window_id (GimpDisplay *display); +static GBytes * gui_display_get_window_id (GimpDisplay *display); static GimpDisplay * gui_display_create (Gimp *gimp, GimpImage *image, GimpUnit unit, @@ -374,7 +374,7 @@ gui_get_empty_display (Gimp *gimp) return display; } -static guint32 +static GBytes * gui_display_get_window_id (GimpDisplay *display) { GimpDisplay *disp = GIMP_DISPLAY (display); @@ -382,13 +382,11 @@ gui_display_get_window_id (GimpDisplay *display) if (shell) { - GtkWidget *toplevel = gtk_widget_get_toplevel (GTK_WIDGET (shell)); - - if (GTK_IS_WINDOW (toplevel)) - return gimp_window_get_native_id (GTK_WINDOW (toplevel)); + if (shell) + return g_bytes_ref (shell->window_handle); } - return 0; + return NULL; } static GimpDisplay * @@ -703,12 +701,7 @@ gui_pdb_dialog_new (Gimp *gimp, gimp_docked_set_show_button_bar (GIMP_DOCKED (view), FALSE); if (progress) - { - guint32 window_id = gimp_progress_get_window_id (progress); - - if (window_id) - gimp_window_set_transient_for (GTK_WINDOW (dialog), window_id); - } + gimp_window_set_transient_for (GTK_WINDOW (dialog), progress); gtk_widget_show (dialog); diff --git a/app/pdb/display-cmds.c b/app/pdb/display-cmds.c index 3a51aed55c..6af856105a 100644 --- a/app/pdb/display-cmds.c +++ b/app/pdb/display-cmds.c @@ -144,20 +144,20 @@ display_get_window_handle_invoker (GimpProcedure *procedure, gboolean success = TRUE; GimpValueArray *return_vals; GimpDisplay *display; - gint window = 0; + GBytes *handle = NULL; display = g_value_get_object (gimp_value_array_index (args, 0)); if (success) { - window = (gint32) gimp_get_display_window_id (gimp, display); + handle = gimp_get_display_window_id (gimp, display); } return_vals = gimp_procedure_get_return_values (procedure, success, error ? *error : NULL); if (success) - g_value_set_int (gimp_value_array_index (return_vals, 1), window); + g_value_take_boxed (gimp_value_array_index (return_vals, 1), handle); return return_vals; } @@ -326,7 +326,8 @@ register_display_procs (GimpPDB *pdb) "gimp-display-get-window-handle"); gimp_procedure_set_static_help (procedure, "Get a handle to the native window for an image display.", - "This procedure returns a handle to the native window for a given image display. For example in the X backend of GDK, a native window handle is an Xlib XID. A value of 0 is returned for an invalid display or if this function is unimplemented for the windowing system that is being used.", + "This procedure returns a handle to the native window for a given image display.\n" + "It can be different types of data depending on the platform you are running on. For example in the X backend of GDK, a native window handle is an Xlib XID whereas on Wayland, it is a string handle. A value of NULL is returned for an invalid display or if this function is unimplemented for the windowing system that is being used.", NULL); gimp_procedure_set_static_attribution (procedure, "Sven Neumann ", @@ -339,11 +340,11 @@ register_display_procs (GimpPDB *pdb) FALSE, GIMP_PARAM_READWRITE)); gimp_procedure_add_return_value (procedure, - g_param_spec_int ("window", - "window", - "The native window handle or 0", - G_MININT32, G_MAXINT32, 0, - GIMP_PARAM_READWRITE)); + g_param_spec_boxed ("handle", + "handle", + "The native window handle or NULL", + G_TYPE_BYTES, + GIMP_PARAM_READWRITE)); gimp_pdb_register_procedure (pdb, procedure); g_object_unref (procedure); diff --git a/app/pdb/progress-cmds.c b/app/pdb/progress-cmds.c index 475abed075..be9497e4db 100644 --- a/app/pdb/progress-cmds.c +++ b/app/pdb/progress-cmds.c @@ -189,14 +189,14 @@ progress_get_window_handle_invoker (GimpProcedure *procedure, { gboolean success = TRUE; GimpValueArray *return_vals; - gint window = 0; + GBytes *handle = NULL; GimpPlugIn *plug_in = gimp->plug_in_manager->current_plug_in; if (plug_in && plug_in->open) { if (! gimp->no_interface) - window = gimp_plug_in_progress_get_window_id (plug_in); + handle = gimp_plug_in_progress_get_window_id (plug_in); } else success = FALSE; @@ -205,7 +205,7 @@ progress_get_window_handle_invoker (GimpProcedure *procedure, error ? *error : NULL); if (success) - g_value_set_int (gimp_value_array_index (return_vals, 1), window); + g_value_take_boxed (gimp_value_array_index (return_vals, 1), handle); return return_vals; } @@ -414,19 +414,20 @@ register_progress_procs (GimpPDB *pdb) gimp_object_set_static_name (GIMP_OBJECT (procedure), "gimp-progress-get-window-handle"); gimp_procedure_set_static_help (procedure, - "Returns the native window ID of the toplevel window this plug-in's progress is displayed in.", - "This function returns the native window ID of the toplevel window this plug-in\'s progress is displayed in.", + "Returns the native handle of the toplevel window this plug-in's progress is displayed in.", + "This function returns the native handle allowing to identify the toplevel window this plug-in's progress is displayed in.\n" + "This handle can be of various types (integer, string, etc.) depending on the platform you are running on which is why it returns a GBytes. There are usually no reasons to call this directly.", NULL); gimp_procedure_set_static_attribution (procedure, "Michael Natterer ", "Michael Natterer", "2004"); gimp_procedure_add_return_value (procedure, - g_param_spec_int ("window", - "window", - "The progress bar's toplevel window", - G_MININT32, G_MAXINT32, 0, - GIMP_PARAM_READWRITE)); + g_param_spec_boxed ("handle", + "handle", + "The progress bar's toplevel window's handle", + G_TYPE_BYTES, + GIMP_PARAM_READWRITE)); gimp_pdb_register_procedure (pdb, procedure); g_object_unref (procedure); diff --git a/app/plug-in/gimpplugin-progress.c b/app/plug-in/gimpplugin-progress.c index df22e1d227..a384987d7f 100644 --- a/app/plug-in/gimpplugin-progress.c +++ b/app/plug-in/gimpplugin-progress.c @@ -233,7 +233,7 @@ gimp_plug_in_progress_pulse (GimpPlugIn *plug_in) gimp_progress_pulse (proc_frame->progress); } -guint32 +GBytes * gimp_plug_in_progress_get_window_id (GimpPlugIn *plug_in) { GimpPlugInProcFrame *proc_frame; diff --git a/app/plug-in/gimpplugin-progress.h b/app/plug-in/gimpplugin-progress.h index 43514fa46d..2580893b73 100644 --- a/app/plug-in/gimpplugin-progress.h +++ b/app/plug-in/gimpplugin-progress.h @@ -34,7 +34,7 @@ void gimp_plug_in_progress_set_text (GimpPlugIn *plug_in, void gimp_plug_in_progress_set_value (GimpPlugIn *plug_in, gdouble percentage); void gimp_plug_in_progress_pulse (GimpPlugIn *plug_in); -guint32 gimp_plug_in_progress_get_window_id (GimpPlugIn *plug_in); +GBytes * gimp_plug_in_progress_get_window_id (GimpPlugIn *plug_in); gboolean gimp_plug_in_progress_install (GimpPlugIn *plug_in, const gchar *progress_callback); diff --git a/app/widgets/gimpfiledialog.c b/app/widgets/gimpfiledialog.c index 17024c6f63..d01d32a8a3 100644 --- a/app/widgets/gimpfiledialog.c +++ b/app/widgets/gimpfiledialog.c @@ -104,7 +104,7 @@ static void gimp_file_dialog_progress_set_value (GimpProgress *p gdouble percentage); static gdouble gimp_file_dialog_progress_get_value (GimpProgress *progress); static void gimp_file_dialog_progress_pulse (GimpProgress *progress); -static guint32 gimp_file_dialog_progress_get_window_id (GimpProgress *progress); +static GBytes * gimp_file_dialog_progress_get_window_id (GimpProgress *progress); static void gimp_file_dialog_add_user_dir (GimpFileDialog *dialog, GUserDirectory directory); @@ -368,6 +368,8 @@ gimp_file_dialog_constructed (GObject *object) dialog->progress = gimp_progress_box_new (); gtk_box_pack_end (GTK_BOX (gtk_dialog_get_content_area (GTK_DIALOG (dialog))), dialog->progress, FALSE, FALSE, 0); + + gimp_widget_set_native_handle (GTK_WIDGET (dialog), &dialog->window_handle); } static void @@ -575,12 +577,12 @@ gimp_file_dialog_progress_pulse (GimpProgress *progress) gimp_progress_pulse (GIMP_PROGRESS (dialog->progress)); } -static guint32 +static GBytes * gimp_file_dialog_progress_get_window_id (GimpProgress *progress) { GimpFileDialog *dialog = GIMP_FILE_DIALOG (progress); - return gimp_window_get_native_id (GTK_WINDOW (dialog)); + return dialog->window_handle; } diff --git a/app/widgets/gimpfiledialog.h b/app/widgets/gimpfiledialog.h index 415c0fb606..4a5ba79cdd 100644 --- a/app/widgets/gimpfiledialog.h +++ b/app/widgets/gimpfiledialog.h @@ -38,6 +38,8 @@ struct _GimpFileDialog { GtkFileChooserDialog parent_instance; + GBytes *window_handle; + Gimp *gimp; GimpImage *image; diff --git a/app/widgets/gimphelp.c b/app/widgets/gimphelp.c index 6110f7e82e..fc879b0eb9 100644 --- a/app/widgets/gimphelp.c +++ b/app/widgets/gimphelp.c @@ -447,12 +447,7 @@ gimp_help_browser_error (Gimp *gimp, -1); if (progress) - { - guint32 window_id = gimp_progress_get_window_id (progress); - - if (window_id) - gimp_window_set_transient_for (GTK_WINDOW (dialog), window_id); - } + gimp_window_set_transient_for (GTK_WINDOW (dialog), progress); gimp_message_box_set_primary_text (GIMP_MESSAGE_DIALOG (dialog)->box, "%s", primary); @@ -781,12 +776,7 @@ gimp_help_query_alt_user_manual (GimpIdleHelp *idle_help) idle_help->query_dialog = GTK_DIALOG (dialog); if (idle_help->progress) - { - guint32 window_id = gimp_progress_get_window_id (idle_help->progress); - - if (window_id) - gimp_window_set_transient_for (GTK_WINDOW (dialog), window_id); - } + gimp_window_set_transient_for (GTK_WINDOW (dialog), idle_help->progress); gimp_message_box_set_primary_text (GIMP_MESSAGE_DIALOG (dialog)->box, _("The GIMP user manual is not installed " diff --git a/app/widgets/gimpwidgets-utils.c b/app/widgets/gimpwidgets-utils.c index 44c0156fc4..ff1154a6e0 100644 --- a/app/widgets/gimpwidgets-utils.c +++ b/app/widgets/gimpwidgets-utils.c @@ -52,6 +52,7 @@ #include "gegl/gimp-babl.h" #include "core/gimp.h" +#include "core/gimpprogress.h" #include "core/gimptoolinfo.h" #include "gimpaccellabel.h" @@ -87,11 +88,15 @@ typedef struct gchar *settings_value; } BlinkStep; -static void gimp_widget_blink_after (GtkWidget *widget, - gint ms_timeout); -static void gimp_search_widget_rec (GtkWidget *widget, - BlinkSearch *data); -static void gimp_blink_free_script (GList *blink_scenario); +static void gimp_widget_blink_after (GtkWidget *widget, + gint ms_timeout); +static void gimp_search_widget_rec (GtkWidget *widget, + BlinkSearch *data); +static void gimp_blink_free_script (GList *blink_scenario); + +static gboolean gimp_window_transient_on_mapped (GtkWidget *widget, + GdkEventAny *event, + GimpProgress *progress); GtkWidget * @@ -905,54 +910,7 @@ gimp_window_set_hint (GtkWindow *window, } } -/** - * gimp_window_get_native_id: - * @window: a #GtkWindow - * - * This function is used to pass a window handle to plug-ins so that - * they can set their dialog windows transient to the parent window. - * - * Returns: a native window ID of the window's #GdkWindow or 0 - * if the window isn't realized yet - */ -guint32 -gimp_window_get_native_id (GtkWindow *window) -{ - GdkWindow *surface; - - g_return_val_if_fail (GTK_IS_WINDOW (window), 0); - - surface = gtk_widget_get_window (GTK_WIDGET (window)); - if (!surface) /* aka window is not yet realized */ - return 0; - -#ifdef GDK_WINDOWING_WIN32 - if (GDK_IS_WIN32_WINDOW (surface)) - return GPOINTER_TO_INT (GDK_WINDOW_HWND (gtk_widget_get_window (GTK_WIDGET (window)))); -#endif - -#ifdef GDK_WINDOWING_X11 - if (GDK_IS_X11_WINDOW (surface)) - return GDK_WINDOW_XID (gtk_widget_get_window (GTK_WIDGET (window))); -#endif - -#ifdef GDK_WINDOWING_WAYLAND - if (GDK_IS_WAYLAND_WINDOW (surface)) - g_debug ("Getting window ID for progress not supported on Wayland yet"); -#endif - - return 0; -} - #ifndef GDK_WINDOWING_WIN32 -static void -gimp_window_transient_realized (GtkWidget *window, - GdkWindow *parent) -{ - if (gtk_widget_get_realized (window)) - gdk_window_set_transient_for (gtk_widget_get_window (window), parent); -} - /* similar to what we have in libgimp/gimpui.c */ static GdkWindow * gimp_get_foreign_window (guint32 window) @@ -973,37 +931,15 @@ gimp_get_foreign_window (guint32 window) #endif void -gimp_window_set_transient_for (GtkWindow *window, - guint32 parent_ID) +gimp_window_set_transient_for (GtkWindow *window, + GimpProgress *parent) { - /* Cross-process transient-for is broken in gdk/win32 <= 2.10.6. It - * causes hangs, at least when used as by the gimp and script-fu - * processes. In some newer GTK+ version it will be fixed to be a - * no-op. If it eventually is fixed to actually work, change this to - * a run-time check of GTK+ version. Remember to change also the - * function with the same name in libgimp/gimpui.c - * - * Note: this hanging bug is still happening with GTK+3 as of 2019-10, - * with steps described in comment 4 in: - * https://bugzilla.gnome.org/show_bug.cgi?id=359538 - */ -#ifndef GDK_WINDOWING_WIN32 - GdkWindow *parent; + g_signal_connect_after (window, "map-event", + G_CALLBACK (gimp_window_transient_on_mapped), + parent); - parent = gimp_get_foreign_window (parent_ID); - if (! parent) - return; - - if (gtk_widget_get_realized (GTK_WIDGET (window))) - gdk_window_set_transient_for (gtk_widget_get_window (GTK_WIDGET (window)), - parent); - - g_signal_connect_object (window, "realize", - G_CALLBACK (gimp_window_transient_realized), - parent, 0); - - g_object_unref (parent); -#endif + if (gtk_widget_get_mapped (GTK_WIDGET (window))) + gimp_window_transient_on_mapped (GTK_WIDGET (window), NULL, parent); } static void @@ -2570,3 +2506,65 @@ gimp_utils_are_menu_path_identical (const gchar *path1, return identical; } + +static gboolean +gimp_window_transient_on_mapped (GtkWidget *window, + GdkEventAny *event, + GimpProgress *progress) +{ + GBytes *handle; + gboolean transient_set = FALSE; + + handle = gimp_progress_get_window_id (progress); + + if (handle == NULL) + return FALSE; + +#ifdef GDK_WINDOWING_WAYLAND + if (GDK_IS_WAYLAND_DISPLAY (gdk_display_get_default ())) + { + char *wayland_handle; + + wayland_handle = (char *) g_bytes_get_data (handle, NULL); + gdk_wayland_window_set_transient_for_exported (gtk_widget_get_window (window), + wayland_handle); + transient_set = TRUE; + } +#endif + + /* Cross-process transient-for is broken in gdk/win32 <= 2.10.6. It + * causes hangs, at least when used as by the gimp and script-fu + * processes. In some newer GTK+ version it will be fixed to be a + * no-op. If it eventually is fixed to actually work, change this to + * a run-time check of GTK+ version. Remember to change also the + * function with the same name in libgimp/gimpui.c + * + * Note: this hanging bug is still happening with GTK+3 as of 2019-10, + * with steps described in comment 4 in: + * https://bugzilla.gnome.org/show_bug.cgi?id=359538 + */ +#ifndef GDK_WINDOWING_WIN32 + if (! transient_set) + { + GdkWindow *parent; + guint32 *handle_data; + guint32 parent_ID; + gsize handle_size; + + handle_data = (guint32 *) g_bytes_get_data (handle, &handle_size); + g_return_val_if_fail (handle_size == sizeof (guint32), FALSE); + parent_ID = *handle_data; + + parent = gimp_get_foreign_window (parent_ID); + + if (parent) + gdk_window_set_transient_for (gtk_widget_get_window (window), parent); + + transient_set = TRUE; + } +#endif + + g_bytes_unref (handle); + + return FALSE; +} diff --git a/app/widgets/gimpwidgets-utils.h b/app/widgets/gimpwidgets-utils.h index e737146690..89f5562527 100644 --- a/app/widgets/gimpwidgets-utils.h +++ b/app/widgets/gimpwidgets-utils.h @@ -71,9 +71,8 @@ gboolean gimp_get_style_color (GtkWidget *widget GdkRGBA *color); void gimp_window_set_hint (GtkWindow *window, GimpWindowHint hint); -guint32 gimp_window_get_native_id (GtkWindow *window); void gimp_window_set_transient_for (GtkWindow *window, - guint32 parent_ID); + GimpProgress *progress); void gimp_widget_set_accel_help (GtkWidget *widget, GimpAction *action); diff --git a/libgimp/gimpdisplay_pdb.c b/libgimp/gimpdisplay_pdb.c index a4d0eafec7..0b888253c2 100644 --- a/libgimp/gimpdisplay_pdb.c +++ b/libgimp/gimpdisplay_pdb.c @@ -156,21 +156,23 @@ gimp_display_delete (GimpDisplay *display) * Get a handle to the native window for an image display. * * This procedure returns a handle to the native window for a given - * image display. For example in the X backend of GDK, a native window - * handle is an Xlib XID. A value of 0 is returned for an invalid - * display or if this function is unimplemented for the windowing - * system that is being used. + * image display. + * It can be different types of data depending on the platform you are + * running on. For example in the X backend of GDK, a native window + * handle is an Xlib XID whereas on Wayland, it is a string handle. A + * value of NULL is returned for an invalid display or if this function + * is unimplemented for the windowing system that is being used. * - * Returns: The native window handle or 0. + * Returns: (transfer full): The native window handle or NULL. * * Since: 2.4 **/ -gint +GBytes * gimp_display_get_window_handle (GimpDisplay *display) { GimpValueArray *args; GimpValueArray *return_vals; - gint window = 0; + GBytes *handle = NULL; args = gimp_value_array_new_from_types (NULL, GIMP_TYPE_DISPLAY, display, @@ -182,11 +184,11 @@ gimp_display_get_window_handle (GimpDisplay *display) gimp_value_array_unref (args); if (GIMP_VALUES_GET_ENUM (return_vals, 0) == GIMP_PDB_SUCCESS) - window = GIMP_VALUES_GET_INT (return_vals, 1); + handle = GIMP_VALUES_DUP_BYTES (return_vals, 1); gimp_value_array_unref (return_vals); - return window; + return handle; } /** diff --git a/libgimp/gimpdisplay_pdb.h b/libgimp/gimpdisplay_pdb.h index 48394dec96..d5fac224ad 100644 --- a/libgimp/gimpdisplay_pdb.h +++ b/libgimp/gimpdisplay_pdb.h @@ -35,7 +35,7 @@ G_BEGIN_DECLS gboolean gimp_display_id_is_valid (gint display_id); GimpDisplay* gimp_display_new (GimpImage *image); gboolean gimp_display_delete (GimpDisplay *display); -gint gimp_display_get_window_handle (GimpDisplay *display); +GBytes* gimp_display_get_window_handle (GimpDisplay *display); gboolean gimp_display_present (GimpDisplay *display); gboolean gimp_displays_flush (void); gboolean gimp_displays_reconnect (GimpImage *old_image, diff --git a/libgimp/gimpprocedure-params.h b/libgimp/gimpprocedure-params.h index 5db1d69381..79c11ff1d2 100644 --- a/libgimp/gimpprocedure-params.h +++ b/libgimp/gimpprocedure-params.h @@ -394,7 +394,7 @@ G_BEGIN_DECLS #define GIMP_VALUES_DUP_BYTES(args, n) \ g_value_dup_boxed (gimp_value_array_index (args, n)) -#define GIMP_VALUES_SET_BYTES(args, n, value, length) \ +#define GIMP_VALUES_SET_BYTES(args, n, value) \ g_value_set_boxed (gimp_value_array_index (args, n), value) #define GIMP_VALUES_TAKE_BYTES(args, n, value, length) \ diff --git a/libgimp/gimpprogress.c b/libgimp/gimpprogress.c index b1236c2ab4..610537850d 100644 --- a/libgimp/gimpprogress.c +++ b/libgimp/gimpprogress.c @@ -89,15 +89,15 @@ gimp_progress_install_vtable (const GimpProgressVtable *vtable, progress_data = g_slice_new0 (GimpProgressData); - progress_data->progress_callback = progress_callback; - progress_data->vtable.start = vtable->start; - progress_data->vtable.end = vtable->end; - progress_data->vtable.set_text = vtable->set_text; - progress_data->vtable.set_value = vtable->set_value; - progress_data->vtable.pulse = vtable->pulse; - progress_data->vtable.get_window = vtable->get_window; - progress_data->data = user_data; - progress_data->data_destroy = user_data_destroy; + progress_data->progress_callback = progress_callback; + progress_data->vtable.start = vtable->start; + progress_data->vtable.end = vtable->end; + progress_data->vtable.set_text = vtable->set_text; + progress_data->vtable.set_value = vtable->set_value; + progress_data->vtable.pulse = vtable->pulse; + progress_data->vtable.get_window_handle = vtable->get_window_handle; + progress_data->data = user_data; + progress_data->data_destroy = user_data_destroy; procedure = gimp_procedure_new (plug_in, progress_callback, @@ -384,15 +384,16 @@ gimp_temp_progress_run (GimpProcedure *procedure, case GIMP_PROGRESS_COMMAND_GET_WINDOW: { GimpValueArray *return_vals; - guint64 window_id = 0; + GBytes *window_handle = NULL; - if (progress_data->vtable.get_window) - window_id = progress_data->vtable.get_window (progress_data->data); + if (progress_data->vtable.get_window_handle) + window_handle = progress_data->vtable.get_window_handle (progress_data->data); return_vals = gimp_procedure_new_return_values (procedure, GIMP_PDB_SUCCESS, NULL); - GIMP_VALUES_SET_DOUBLE (return_vals, 1, window_id); + GIMP_VALUES_SET_BYTES (return_vals, 1, window_handle); + g_bytes_unref (window_handle); g_free (text); return return_vals; diff --git a/libgimp/gimpprogress.h b/libgimp/gimpprogress.h index 82aa2a9658..04abe6253e 100644 --- a/libgimp/gimpprogress.h +++ b/libgimp/gimpprogress.h @@ -79,9 +79,9 @@ typedef void (* GimpProgressVtablePulseFunc) (gpointer user_data); * GimpProgressVtableGetWindowFunc: * @user_data: (closure): User data * - * Returns: the ID of the window where the progress is displayed. + * Returns: the handle of the window where the progress is displayed. */ -typedef guint64 (* GimpProgressVtableGetWindowFunc) (gpointer user_data); +typedef GBytes * (* GimpProgressVtableGetWindowFunc) (gpointer user_data); typedef struct _GimpProgressVtable GimpProgressVtable; @@ -93,7 +93,7 @@ typedef struct _GimpProgressVtable GimpProgressVtable; * @set_text: sets a new text on the progress. * @set_value: sets a new percentage on the progress. * @pulse: makes the progress pulse. - * @get_window: returns the ID of the window where the progress is displayed. + * @get_window_handle: returns the handle of the window where the progress is displayed. * @_gimp_reserved1: reserved pointer for future expansion. * @_gimp_reserved2: reserved pointer for future expansion. * @_gimp_reserved3: reserved pointer for future expansion. @@ -110,7 +110,7 @@ struct _GimpProgressVtable GimpProgressVtableSetTextFunc set_text; GimpProgressVtableSetValueFunc set_value; GimpProgressVtablePulseFunc pulse; - GimpProgressVtableGetWindowFunc get_window; + GimpProgressVtableGetWindowFunc get_window_handle; /* Padding for future expansion. Must be initialized with NULL! */ void (* _gimp_reserved1) (void); diff --git a/libgimp/gimpprogress_pdb.c b/libgimp/gimpprogress_pdb.c index 97cb70c5da..18abb17383 100644 --- a/libgimp/gimpprogress_pdb.c +++ b/libgimp/gimpprogress_pdb.c @@ -219,22 +219,25 @@ gimp_progress_end (void) /** * gimp_progress_get_window_handle: * - * Returns the native window ID of the toplevel window this plug-in's + * Returns the native handle of the toplevel window this plug-in's * progress is displayed in. * - * This function returns the native window ID of the toplevel window - * this plug-in\'s progress is displayed in. + * This function returns the native handle allowing to identify the + * toplevel window this plug-in's progress is displayed in. + * This handle can be of various types (integer, string, etc.) + * depending on the platform you are running on which is why it returns + * a GBytes. There are usually no reasons to call this directly. * - * Returns: The progress bar's toplevel window. + * Returns: (transfer full): The progress bar's toplevel window's handle. * * Since: 2.2 **/ -gint +GBytes * gimp_progress_get_window_handle (void) { GimpValueArray *args; GimpValueArray *return_vals; - gint window = 0; + GBytes *handle = NULL; args = gimp_value_array_new_from_types (NULL, G_TYPE_NONE); @@ -245,11 +248,11 @@ gimp_progress_get_window_handle (void) gimp_value_array_unref (args); if (GIMP_VALUES_GET_ENUM (return_vals, 0) == GIMP_PDB_SUCCESS) - window = GIMP_VALUES_GET_INT (return_vals, 1); + handle = GIMP_VALUES_DUP_BYTES (return_vals, 1); gimp_value_array_unref (return_vals); - return window; + return handle; } /** diff --git a/libgimp/gimpprogress_pdb.h b/libgimp/gimpprogress_pdb.h index 1bcee8b01d..6a2060360c 100644 --- a/libgimp/gimpprogress_pdb.h +++ b/libgimp/gimpprogress_pdb.h @@ -38,7 +38,7 @@ G_GNUC_INTERNAL gboolean _gimp_progress_update (gdouble percentag gboolean gimp_progress_pulse (void); gboolean gimp_progress_set_text (const gchar *message); gboolean gimp_progress_end (void); -gint gimp_progress_get_window_handle (void); +GBytes* gimp_progress_get_window_handle (void); G_GNUC_INTERNAL gboolean _gimp_progress_install (const gchar *progress_callback); G_GNUC_INTERNAL gboolean _gimp_progress_uninstall (const gchar *progress_callback); gboolean gimp_progress_cancel (const gchar *progress_callback); diff --git a/libgimp/gimpprogressbar.c b/libgimp/gimpprogressbar.c index a397c4d914..223cc0b86f 100644 --- a/libgimp/gimpprogressbar.c +++ b/libgimp/gimpprogressbar.c @@ -21,6 +21,7 @@ #include "config.h" +#include #include #ifdef GDK_WINDOWING_WIN32 @@ -35,6 +36,8 @@ #include #endif +#include "libgimpwidgets/gimpwidgets.h" + #include "gimpuitypes.h" #include "gimp.h" @@ -52,21 +55,27 @@ **/ -static void gimp_progress_bar_dispose (GObject *object); - -static void gimp_progress_bar_start (const gchar *message, - gboolean cancelable, - gpointer user_data); -static void gimp_progress_bar_end (gpointer user_data); -static void gimp_progress_bar_set_text (const gchar *message, - gpointer user_data); -static void gimp_progress_bar_set_value (gdouble percentage, - gpointer user_data); -static void gimp_progress_bar_pulse (gpointer user_data); -static guint64 gimp_progress_bar_get_window (gpointer user_data); +typedef struct _GimpProgressBarPrivate +{ + GBytes *window_handle; +} GimpProgressBarPrivate; -G_DEFINE_TYPE (GimpProgressBar, gimp_progress_bar, GTK_TYPE_PROGRESS_BAR) +static void gimp_progress_bar_dispose (GObject *object); + +static void gimp_progress_bar_start (const gchar *message, + gboolean cancelable, + gpointer user_data); +static void gimp_progress_bar_end (gpointer user_data); +static void gimp_progress_bar_set_text (const gchar *message, + gpointer user_data); +static void gimp_progress_bar_set_value (gdouble percentage, + gpointer user_data); +static void gimp_progress_bar_pulse (gpointer user_data); +static GBytes * gimp_progress_bar_get_window_handle (gpointer user_data); + + +G_DEFINE_TYPE_WITH_PRIVATE (GimpProgressBar, gimp_progress_bar, GTK_TYPE_PROGRESS_BAR) #define parent_class gimp_progress_bar_parent_class @@ -82,25 +91,29 @@ gimp_progress_bar_class_init (GimpProgressBarClass *klass) static void gimp_progress_bar_init (GimpProgressBar *bar) { - GimpProgressVtable vtable = { 0, }; + GimpProgressVtable vtable = { 0, }; + GimpProgressBarPrivate *priv = gimp_progress_bar_get_instance_private (bar); gtk_progress_bar_set_text (GTK_PROGRESS_BAR (bar), " "); gtk_progress_bar_set_ellipsize (GTK_PROGRESS_BAR (bar), PANGO_ELLIPSIZE_END); - vtable.start = gimp_progress_bar_start; - vtable.end = gimp_progress_bar_end; - vtable.set_text = gimp_progress_bar_set_text; - vtable.set_value = gimp_progress_bar_set_value; - vtable.pulse = gimp_progress_bar_pulse; - vtable.get_window = gimp_progress_bar_get_window; + vtable.start = gimp_progress_bar_start; + vtable.end = gimp_progress_bar_end; + vtable.set_text = gimp_progress_bar_set_text; + vtable.set_value = gimp_progress_bar_set_value; + vtable.pulse = gimp_progress_bar_pulse; + vtable.get_window_handle = gimp_progress_bar_get_window_handle; bar->progress_callback = gimp_progress_install_vtable (&vtable, bar, NULL); + + gimp_widget_set_native_handle (GTK_WIDGET (bar), &priv->window_handle); } static void gimp_progress_bar_dispose (GObject *object) { - GimpProgressBar *bar = GIMP_PROGRESS_BAR (object); + GimpProgressBar *bar = GIMP_PROGRESS_BAR (object); + GimpProgressBarPrivate *priv = gimp_progress_bar_get_instance_private (bar); if (bar->progress_callback) { @@ -108,6 +121,17 @@ gimp_progress_bar_dispose (GObject *object) bar->progress_callback = NULL; } + if (priv->window_handle != NULL) + { +#ifdef GDK_WINDOWING_WAYLAND + if (GDK_IS_WAYLAND_DISPLAY (gdk_display_get_default ()) && + /* The GdkWindow is likely already destroyed. */ + gtk_widget_get_window (GTK_WIDGET (bar)) != NULL) + gdk_wayland_window_unexport_handle (gtk_widget_get_window (GTK_WIDGET (bar))); +#endif + g_clear_pointer (&priv->window_handle, g_bytes_unref); + } + G_OBJECT_CLASS (parent_class)->dispose (object); } @@ -180,47 +204,13 @@ gimp_progress_bar_pulse (gpointer user_data) gtk_main_iteration (); } -static guint64 -gimp_window_get_native_id (GtkWindow *window) +static GBytes * +gimp_progress_bar_get_window_handle (gpointer user_data) { - GdkWindow *surface; + GimpProgressBar *bar = GIMP_PROGRESS_BAR (user_data); + GimpProgressBarPrivate *priv = gimp_progress_bar_get_instance_private (bar); - g_return_val_if_fail (GTK_IS_WINDOW (window), 0); - - surface = gtk_widget_get_window (GTK_WIDGET (window)); - if (!surface) /* aka window is not yet realized */ - return 0; - -#ifdef GDK_WINDOWING_WIN32 - if (GDK_IS_WIN32_WINDOW (surface)) - return GPOINTER_TO_INT (GDK_WINDOW_HWND (gtk_widget_get_window (GTK_WIDGET (window)))); -#endif - -#ifdef GDK_WINDOWING_X11 - if (GDK_IS_X11_WINDOW (surface)) - return GPOINTER_TO_INT (GDK_WINDOW_XID (gtk_widget_get_window (GTK_WIDGET (window)))); -#endif - -#ifdef GDK_WINDOWING_WAYLAND - if (GDK_IS_WAYLAND_WINDOW (surface)) - g_debug ("Getting window ID for progress not supported on Wayland yet"); -#endif - - return 0; -} - -static guint64 -gimp_progress_bar_get_window (gpointer user_data) -{ - GimpProgressBar *bar = GIMP_PROGRESS_BAR (user_data); - GtkWidget *toplevel; - - toplevel = gtk_widget_get_toplevel (GTK_WIDGET (bar)); - - if (GTK_IS_WINDOW (toplevel)) - return gimp_window_get_native_id (GTK_WINDOW (toplevel)); - - return 0; + return g_bytes_ref (priv->window_handle); } /** diff --git a/libgimp/gimpui.c b/libgimp/gimpui.c index 5a38bc09a4..134e049628 100644 --- a/libgimp/gimpui.c +++ b/libgimp/gimpui.c @@ -32,6 +32,10 @@ #include #endif +#ifdef GDK_WINDOWING_WAYLAND +#include +#endif + #include "gimp.h" #include "gimpui.h" @@ -55,25 +59,26 @@ /* local function prototypes */ -static void gimp_ui_help_func (const gchar *help_id, - gpointer help_data); -static void gimp_ui_theme_changed (GFileMonitor *monitor, - GFile *file, - GFile *other_file, - GFileMonitorEvent event_type, - GtkCssProvider *css_provider); -static void gimp_ensure_modules (void); -#ifndef GDK_WINDOWING_WIN32 -static void gimp_window_transient_realized (GtkWidget *window, - GdkWindow *parent); -#endif -static gboolean gimp_window_set_transient_for (GtkWindow *window, - GdkWindow *parent); +static void gimp_ui_help_func (const gchar *help_id, + gpointer help_data); +static void gimp_ui_theme_changed (GFileMonitor *monitor, + GFile *file, + GFile *other_file, + GFileMonitorEvent event_type, + GtkCssProvider *css_provider); +static void gimp_ensure_modules (void); #ifdef GDK_WINDOWING_QUARTZ -static gboolean gimp_osx_focus_window (gpointer); +static gboolean gimp_osx_focus_window (gpointer); #endif +#ifndef GDK_WINDOWING_WIN32 +static GdkWindow * gimp_ui_get_foreign_window (guint32 window); +#endif +static gboolean gimp_window_transient_on_mapped (GtkWidget *window, + GdkEventAny *event, + GimpDisplay *display); + static gboolean gimp_ui_initialized = FALSE; @@ -174,84 +179,6 @@ gimp_ui_init (const gchar *prog_name) gimp_ui_initialized = TRUE; } -static GdkWindow * -gimp_ui_get_foreign_window (guint32 window) -{ -#ifdef GDK_WINDOWING_X11 - if (GDK_IS_X11_DISPLAY (gdk_display_get_default ())) - return gdk_x11_window_foreign_new_for_display (gdk_display_get_default (), - window); -#endif - -#ifdef GDK_WINDOWING_WIN32 - return gdk_win32_window_foreign_new_for_display (gdk_display_get_default (), - (HWND) (uintptr_t) window); -#endif - - return NULL; -} - -/** - * gimp_ui_get_display_window: - * @display: a #GimpDisplay. - * - * Returns the #GdkWindow of a display window. The purpose is to allow - * to make plug-in dialogs transient to the image display as explained - * with gdk_window_set_transient_for(). - * - * You shouldn't have to call this function directly. Use - * gimp_window_set_transient_for_display() instead. - * - * Returns: (nullable) (transfer full): A reference to a #GdkWindow or %NULL. - * You should unref the window using g_object_unref() as - * soon as you don't need it any longer. - * - * Since: 2.4 - */ -GdkWindow * -gimp_ui_get_display_window (GimpDisplay *display) -{ - guint32 window; - - g_return_val_if_fail (gimp_ui_initialized, NULL); - - window = gimp_display_get_window_handle (display); - if (window) - return gimp_ui_get_foreign_window (window); - - return NULL; -} - -/** - * gimp_ui_get_progress_window: - * - * Returns the #GdkWindow of the window this plug-in's progress bar is - * shown in. Use it to make plug-in dialogs transient to this window - * as explained with gdk_window_set_transient_for(). - * - * You shouldn't have to call this function directly. Use - * gimp_window_set_transient() instead. - * - * Returns: (transfer full): A reference to a #GdkWindow or %NULL. - * You should unref the window using g_object_unref() as - * soon as you don't need it any longer. - * - * Since: 2.4 - */ -GdkWindow * -gimp_ui_get_progress_window (void) -{ - guint32 window; - - g_return_val_if_fail (gimp_ui_initialized, NULL); - - window = gimp_progress_get_window_handle (); - if (window) - return gimp_ui_get_foreign_window (window); - - return NULL; -} - #ifdef GDK_WINDOWING_QUARTZ static void gimp_window_transient_show (GtkWidget *window) @@ -269,8 +196,8 @@ gimp_window_transient_show (GtkWidget *window) * @display: display of the image window that should become the parent * * Indicates to the window manager that @window is a transient dialog - * associated with the GIMP image window that is identified by it's - * display ID. See gdk_window_set_transient_for () for more information. + * associated with the GIMP image window that is identified by its + * display. See gdk_window_set_transient_for () for more information. * * Most of the time you will want to use the convenience function * gimp_window_set_transient(). @@ -283,22 +210,14 @@ gimp_window_set_transient_for_display (GtkWindow *window, { g_return_if_fail (gimp_ui_initialized); g_return_if_fail (GTK_IS_WINDOW (window)); + g_return_if_fail (GIMP_IS_DISPLAY (display)); - if (! gimp_window_set_transient_for (window, - gimp_ui_get_display_window (display))) - { - /* if setting the window transient failed, at least set - * WIN_POS_CENTER, which will center the window on the screen - * where the mouse is (see bug #684003). - */ - gtk_window_set_position (window, GTK_WIN_POS_CENTER); + g_signal_connect_after (window, "map-event", + G_CALLBACK (gimp_window_transient_on_mapped), + display); -#ifdef GDK_WINDOWING_QUARTZ - g_signal_connect (window, "show", - G_CALLBACK (gimp_window_transient_show), - NULL); -#endif - } + if (gtk_widget_get_mapped (GTK_WIDGET (window))) + gimp_window_transient_on_mapped (GTK_WIDGET (window), NULL, display); } /** @@ -317,17 +236,12 @@ gimp_window_set_transient (GtkWindow *window) g_return_if_fail (gimp_ui_initialized); g_return_if_fail (GTK_IS_WINDOW (window)); - if (! gimp_window_set_transient_for (window, gimp_ui_get_progress_window ())) - { - /* see above */ - gtk_window_set_position (window, GTK_WIN_POS_CENTER); + g_signal_connect_after (window, "map-event", + G_CALLBACK (gimp_window_transient_on_mapped), + NULL); -#ifdef GDK_WINDOWING_QUARTZ - g_signal_connect (window, "show", - G_CALLBACK (gimp_window_transient_show), - NULL); -#endif - } + if (gtk_widget_get_mapped (GTK_WIDGET (window))) + gimp_window_transient_on_mapped (GTK_WIDGET (window), NULL, NULL); } @@ -401,49 +315,6 @@ gimp_ensure_modules (void) } } -#ifndef GDK_WINDOWING_WIN32 -static void -gimp_window_transient_realized (GtkWidget *window, - GdkWindow *parent) -{ - if (gtk_widget_get_realized (window)) - gdk_window_set_transient_for (gtk_widget_get_window (window), parent); -} -#endif - -static gboolean -gimp_window_set_transient_for (GtkWindow *window, - GdkWindow *parent) -{ - gtk_window_set_transient_for (window, NULL); - - /* To know why it is disabled on Win32, see - * gimp_window_set_transient_for() in app/widgets/gimpwidgets-utils.c. - */ -#ifndef GDK_WINDOWING_WIN32 - g_signal_handlers_disconnect_matched (window, G_SIGNAL_MATCH_FUNC, - 0, 0, NULL, - gimp_window_transient_realized, - NULL); - - if (! parent) - return FALSE; - - if (gtk_widget_get_realized (GTK_WIDGET (window))) - gdk_window_set_transient_for (gtk_widget_get_window (GTK_WIDGET (window)), - parent); - - g_signal_connect_object (window, "realize", - G_CALLBACK (gimp_window_transient_realized), - parent, 0); - g_object_unref (parent); - - return TRUE; -#endif - - return FALSE; -} - #ifdef GDK_WINDOWING_QUARTZ static gboolean gimp_osx_focus_window (gpointer user_data) @@ -452,3 +323,94 @@ gimp_osx_focus_window (gpointer user_data) return FALSE; } #endif + +#ifndef GDK_WINDOWING_WIN32 +static GdkWindow * +gimp_ui_get_foreign_window (guint32 window) +{ +#ifdef GDK_WINDOWING_X11 + if (GDK_IS_X11_DISPLAY (gdk_display_get_default ())) + return gdk_x11_window_foreign_new_for_display (gdk_display_get_default (), + window); +#endif + +#ifdef GDK_WINDOWING_WIN32 + return gdk_win32_window_foreign_new_for_display (gdk_display_get_default (), + (HWND) (uintptr_t) window); +#endif + + return NULL; +} +#endif + +static gboolean +gimp_window_transient_on_mapped (GtkWidget *window, + GdkEventAny *event, + GimpDisplay *display) +{ + GBytes *handle; + gboolean transient_set = FALSE; + + if (display != NULL) + handle = gimp_display_get_window_handle (display); + else + handle = gimp_progress_get_window_handle (); + + if (handle == NULL) + return FALSE; + +#ifdef GDK_WINDOWING_WAYLAND + if (GDK_IS_WAYLAND_DISPLAY (gdk_display_get_default ())) + { + char *wayland_handle; + + wayland_handle = (char *) g_bytes_get_data (handle, NULL); + gdk_wayland_window_set_transient_for_exported (gtk_widget_get_window (window), + wayland_handle); + transient_set = TRUE; + } +#endif + + /* To know why it is disabled on Win32, see + * gimp_window_set_transient_for() in app/widgets/gimpwidgets-utils.c. + */ +#ifndef GDK_WINDOWING_WIN32 + if (! transient_set) + { + GdkWindow *parent; + guint32 *handle_data; + guint32 parent_ID; + gsize handle_size; + + handle_data = (guint32 *) g_bytes_get_data (handle, &handle_size); + g_return_val_if_fail (handle_size == sizeof (guint32), FALSE); + parent_ID = *handle_data; + + parent = gimp_ui_get_foreign_window (parent_ID); + + if (parent) + gdk_window_set_transient_for (gtk_widget_get_window (window), parent); + + transient_set = TRUE; + } +#endif + + if (! transient_set) + { + /* if setting the window transient failed, at least set + * WIN_POS_CENTER, which will center the window on the screen + * where the mouse is (see bug #684003). + */ + gtk_window_set_position (GTK_WINDOW (window), GTK_WIN_POS_CENTER); + +#ifdef GDK_WINDOWING_QUARTZ + g_signal_connect (window, "show", + G_CALLBACK (gimp_window_transient_show), + NULL); +#endif + } + + g_bytes_unref (handle); + + return FALSE; +} diff --git a/libgimp/gimpui.def b/libgimp/gimpui.def index 8b05ab3403..61527f329d 100644 --- a/libgimp/gimpui.def +++ b/libgimp/gimpui.def @@ -73,8 +73,6 @@ EXPORTS gimp_save_procedure_dialog_add_metadata gimp_save_procedure_dialog_get_type gimp_save_procedure_dialog_new - gimp_ui_get_display_window - gimp_ui_get_progress_window gimp_ui_init gimp_vectors_combo_box_get_type gimp_vectors_combo_box_new diff --git a/libgimp/gimpui.h b/libgimp/gimpui.h index 86b2b9c7a9..5f26c82bc4 100644 --- a/libgimp/gimpui.h +++ b/libgimp/gimpui.h @@ -57,11 +57,7 @@ G_BEGIN_DECLS void gimp_ui_init (const gchar *prog_name); -GdkWindow * gimp_ui_get_progress_window (void); - void gimp_window_set_transient (GtkWindow *window); - -GdkWindow * gimp_ui_get_display_window (GimpDisplay *display); void gimp_window_set_transient_for_display (GtkWindow *window, GimpDisplay *display); diff --git a/libgimpwidgets/gimpwidgets.def b/libgimpwidgets/gimpwidgets.def index 1dad4cfa3f..f5afdcd2dc 100644 --- a/libgimpwidgets/gimpwidgets.def +++ b/libgimpwidgets/gimpwidgets.def @@ -497,6 +497,7 @@ EXPORTS gimp_widget_get_monitor gimp_widget_set_bound_property gimp_widget_set_identifier + gimp_widget_set_native_handle gimp_widget_track_monitor gimp_widgets_error_quark gimp_widgets_init diff --git a/libgimpwidgets/gimpwidgetsutils.c b/libgimpwidgets/gimpwidgetsutils.c index 3d39741b0a..1aa97275b0 100644 --- a/libgimpwidgets/gimpwidgetsutils.c +++ b/libgimpwidgets/gimpwidgetsutils.c @@ -26,6 +26,16 @@ #include #include +#ifdef GDK_WINDOWING_WIN32 +#include +#endif +#ifdef GDK_WINDOWING_X11 +#include +#endif +#ifdef GDK_WINDOWING_WAYLAND +#include +#endif + #ifdef G_OS_WIN32 #ifdef _WIN32_WINNT #undef _WIN32_WINNT @@ -61,6 +71,17 @@ **/ +#ifdef GDK_WINDOWING_WAYLAND +static void gimp_widget_wayland_window_exported (GdkWindow *window, + const char *handle, + GBytes **phandle); +#endif + +static gboolean gimp_widget_set_handle_on_mapped (GtkWidget *widget, + GdkEventAny *event, + GBytes **phandle); + + static GtkWidget * find_mnemonic_widget (GtkWidget *widget, gint level) @@ -1036,3 +1057,113 @@ gimp_widget_get_color_transform (GtkWidget *widget, return NULL; } + +/** + * gimp_widget_set_native_handle: + * @widget: a #GtkWindow + * @handle: (out): pointer to store the native handle as a #GBytes. + * + * This function is used to store the handle representing @window into + * @handle so that it can later be reused to set other windows as + * transient to this one (even in other processes, such as plug-ins). + * + * Depending on the platform, the actual content of @handle can be + * various types. Moreover it may be filled asynchronously in a + * callback, so you should not assume that @handle is set after running + * this function. + * + * This convenience function is safe to use even before @widget is + * visible as it will will the handle once it is mapped. + */ +void +gimp_widget_set_native_handle (GtkWidget *widget, + GBytes **handle) +{ + g_return_if_fail (GTK_IS_WIDGET (widget)); + g_return_if_fail (gtk_widget_get_has_window (widget)); + + gtk_widget_add_events (widget, GDK_STRUCTURE_MASK); + g_signal_connect (widget, "map-event", + G_CALLBACK (gimp_widget_set_handle_on_mapped), + handle); + + if (gtk_widget_get_mapped (widget)) + gimp_widget_set_handle_on_mapped (widget, NULL, handle); +} + + +/* Private functions */ + +#ifdef GDK_WINDOWING_WAYLAND +static void +gimp_widget_wayland_window_exported (GdkWindow *window, + const char *handle, + GBytes **phandle) +{ + GBytes *wayland_handle; + + wayland_handle = g_bytes_new (handle, strlen (handle)); + + g_bytes_unref (*phandle); + *phandle = wayland_handle; +} +#endif + +static gboolean +gimp_widget_set_handle_on_mapped (GtkWidget *widget, + GdkEventAny *event, + GBytes **phandle) +{ + GdkWindow *surface; + GBytes *handle = NULL; +#if defined (GDK_WINDOWING_WIN32) || defined (GDK_WINDOWING_X11) + GtkWidget *toplevel = gtk_widget_get_toplevel (widget); + + widget = toplevel; +#endif + + g_clear_pointer (phandle, g_bytes_unref); + + surface = gtk_widget_get_window (GTK_WIDGET (widget)); + g_return_val_if_fail (surface != NULL, FALSE); + +#ifdef GDK_WINDOWING_WIN32 + if (GDK_IS_WIN32_WINDOW (surface)) + { + guint32 id; + + id = GPOINTER_TO_INT (GDK_WINDOW_HWND (gtk_widget_get_window (GTK_WIDGET (widget)))); + handle = g_bytes_new (&id, sizeof (guint32)); + } +#endif + +#ifdef GDK_WINDOWING_X11 + if (GDK_IS_X11_WINDOW (surface)) + { + guint32 id; + + id = GDK_WINDOW_XID (gtk_widget_get_window (GTK_WIDGET (widget))); + handle = g_bytes_new (&id, sizeof (guint32)); + } +#endif +#ifdef GDK_WINDOWING_WAYLAND + if (GDK_IS_WAYLAND_WINDOW (surface)) + { + /* I don't run this on "realize" event because somehow it locks + * the whole processus in Wayland. The "map-event" happens + * slightly after the window became visible and I didn't + * experience any lock. + */ + if (! gdk_wayland_window_export_handle (surface, + (GdkWaylandWindowExported) gimp_widget_wayland_window_exported, + phandle, NULL)) + g_printerr ("%s: gdk_wayland_window_export_handle() failed. " + "It will not be possible to set windows in other processes as transient to this display shell.\n", + G_STRFUNC); + } +#endif + + *phandle = handle; + + return FALSE; +} diff --git a/libgimpwidgets/gimpwidgetsutils.h b/libgimpwidgets/gimpwidgetsutils.h index 36ba7412d5..891bda5dd5 100644 --- a/libgimpwidgets/gimpwidgetsutils.h +++ b/libgimpwidgets/gimpwidgetsutils.h @@ -65,6 +65,8 @@ GimpColorTransform * gimp_widget_get_color_transform (GtkWidget *widget, GimpColorRenderingIntent proof_intent, gboolean proof_bpc); +void gimp_widget_set_native_handle (GtkWidget *widget, + GBytes **handle); G_END_DECLS diff --git a/pdb/groups/display.pdb b/pdb/groups/display.pdb index 1b3a35604c..c02a099622 100644 --- a/pdb/groups/display.pdb +++ b/pdb/groups/display.pdb @@ -145,9 +145,13 @@ sub display_get_window_handle { $help = <<'HELP'; This procedure returns a handle to the native window for a given image -display. For example in the X backend of GDK, a native window handle is -an Xlib XID. A value of 0 is returned for an invalid display or if this -function is unimplemented for the windowing system that is being used. +display. + +It can be different types of data depending on the platform you are running on. +For example in the X backend of GDK, a native window handle is an Xlib +XID whereas on Wayland, it is a string handle. A value of NULL is +returned for an invalid display or if this function is unimplemented for +the windowing system that is being used. HELP &neo_pdb_misc('2005', '2.4'); @@ -158,14 +162,14 @@ HELP ); @outargs = ( - { name => 'window', type => 'int32', - desc => 'The native window handle or 0' } + { name => 'handle', type => 'bytes', + desc => 'The native window handle or NULL' } ); %invoke = ( code => <<'CODE' { - window = (gint32) gimp_get_display_window_id (gimp, display); + handle = gimp_get_display_window_id (gimp, display); } CODE ); diff --git a/pdb/groups/progress.pdb b/pdb/groups/progress.pdb index 1c89cefe55..93c9000352 100644 --- a/pdb/groups/progress.pdb +++ b/pdb/groups/progress.pdb @@ -180,17 +180,20 @@ CODE } sub progress_get_window_handle { - $blurb = 'Returns the native window ID of the toplevel window this plug-in\'s progress is displayed in.'; + $blurb = 'Returns the native handle of the toplevel window this plug-in\'s progress is displayed in.'; $help = <<'HELP'; -This function returns the native window ID of the toplevel window this plug-in\'s progress is displayed in. +This function returns the native handle allowing to identify the toplevel window this plug-in's progress is displayed in. + +This handle can be of various types (integer, string, etc.) depending on the platform you are running on which is why it +returns a GBytes. There are usually no reasons to call this directly. HELP &mitch_pdb_misc('2004', '2.2'); @outargs = ( - { name => 'window', type => 'int32', - desc => 'The progress bar\'s toplevel window' } + { name => 'handle', type => 'bytes', + desc => 'The progress bar\'s toplevel window\'s handle' } ); %invoke = ( @@ -201,7 +204,7 @@ HELP if (plug_in && plug_in->open) { if (! gimp->no_interface) - window = gimp_plug_in_progress_get_window_id (plug_in); + handle = gimp_plug_in_progress_get_window_id (plug_in); } else success = FALSE; diff --git a/plug-ins/screenshot/screenshot-freedesktop.c b/plug-ins/screenshot/screenshot-freedesktop.c index 534d5a9155..0cb5296c06 100644 --- a/plug-ins/screenshot/screenshot-freedesktop.c +++ b/plug-ins/screenshot/screenshot-freedesktop.c @@ -136,8 +136,18 @@ screenshot_freedesktop_shoot (GdkMonitor *monitor, if (GDK_IS_X11_DISPLAY (gdk_display_get_default ())) { GdkWindow *window; + GBytes *handle; + guint32 *handle_data; + guint32 window_id; + gsize handle_size; - window = gimp_ui_get_progress_window (); + handle = gimp_progress_get_window_handle (); + handle_data = (guint32 *) g_bytes_get_data (handle, &handle_size); + g_return_val_if_fail (handle_size == sizeof (guint32), GIMP_PDB_EXECUTION_ERROR); + window_id = *handle_data; + g_bytes_unref (handle); + + window = gdk_x11_window_foreign_new_for_display (gdk_display_get_default (), window_id); if (window) { gint id;