From 58b3b1408202ca889b228f903e61a14c5c51f6bb Mon Sep 17 00:00:00 2001 From: Jehan Date: Mon, 14 Aug 2023 14:23:06 +0200 Subject: [PATCH] app, libgimp*, pdb, plug-ins: reimplement generic inter-process transient window. Having windows ID as guint32 is a mistake. Different systems have different protocols. In Wayland in particular, Windows handles are exchanged as strings. What this commit does is the following: In core: - get_window_id() virtual function in core GimpProgress is changed to return a GBytes, as a generic "data" to represent a window differently on different systems. - All implementations of get_window_id() in various classes implementing this interface are updated accordingly: * GimpSubProgress * GimpDisplay returns the handle of its shell. * GimpDisplayShell now creates its window handle at construction with libgimpwidget's gimp_widget_set_native_handle() and simply return this handle every time it's requested. * GimpFileDialog also creates its window handle at construction with gimp_widget_set_native_handle(). - gimp_window_set_transient_for() in core is changed to take a GimpProgress as argument (instead of a guint32 ID), requests and process the ID itself, according to the running platform. In particular, the following were improved: * Unlike old code, it will work even if the window is not visible yet. In such a case, the function simply adds a signal handler to set transient at mapping. It makes it easier to use it at construction in a reliable way. * It now works for Wayland too, additionally to X11. - GimpPdbProgress now exchanges a GBytes too with the command GIMP_PROGRESS_COMMAND_GET_WINDOW. - display_get_window_id() in gimp-gui.h also returns a GBytes now. PDB/libgimp: - gimp_display_get_window_handle() and gimp_progress_get_window_handle() now return a GBytes to represent a window handle in an opaque way (depending on the running platform). In libgimp: - GimpProgress's get_window() virtual function changed to return a GBytes and renamed get_window_handle(). - In particular GimpProgressBar is the only implementation of get_window_handle(). It creates its handle at object construction with libgimpwidget's gimp_widget_set_native_handle() and the virtual method's implementation simply returns the GBytes. In libgimpUi: - gimp_ui_get_display_window() and gimp_ui_get_progress_window() were removed. We should not assume anymore that it is possible to create a GdkWindow to be used. For instance this is not possible with Wayland which has its own way to set a window transient with a string handle. - gimp_window_set_transient_for_display() and gimp_window_set_transient() now use an internal implementation similar to core gimp_window_set_transient_for(), with the same improvements (works even at construction when the window is not visible yet + works for Wayland too). In libgimpwidgets: - New gimp_widget_set_native_handle() is a helper function used both in core and libgimp* libraries for widgets which we want to be usable as possible parents. It takes care of getting the relevant window handle (depending on the running platform) and stores it in a given pointer, either immediately or after a callback once the widget is mapped. So it can be used at construction. Also it sets a handle for X11 or Wayland. In plug-ins: - Screenshot uses the new gimp_progress_get_window_handle() directly now in its X11 code path and creates out of it a GdkWindows itself with gdk_x11_window_foreign_new_for_display(). Our inter-process transient implementation only worked for X11, and with this commit, it works for Wayland too. There is code for Windows but it is currently disabled as it apparently hangs (there is a comment in-code which links to this old report: https://bugzilla.gnome.org/show_bug.cgi?id=359538). NikcDC tested yesterday with re-enabling the code and said they experienced a freeze. ;-( Finally there is no infrastructure yet to make this work on macOS and apparently there is no implementation of window handle in GDK for macOS that I could find. I'm not sure if macOS doesn't have this concept of setting transient on another processus's window or GDK is simply lacking the implementation. --- app/core/gimp-gui.c | 8 +- app/core/gimp-gui.h | 4 +- app/core/gimppdbprogress.c | 43 ++- app/core/gimpprogress.c | 4 +- app/core/gimpprogress.h | 4 +- app/core/gimpsubprogress.c | 6 +- app/display/gimpdisplay.c | 6 +- app/display/gimpdisplayshell-progress.c | 11 +- app/display/gimpdisplayshell.c | 17 ++ app/display/gimpdisplayshell.h | 2 + app/gui/gui-message.c | 5 +- app/gui/gui-vtable.c | 19 +- app/pdb/display-cmds.c | 19 +- app/pdb/progress-cmds.c | 21 +- app/plug-in/gimpplugin-progress.c | 2 +- app/plug-in/gimpplugin-progress.h | 2 +- app/widgets/gimpfiledialog.c | 8 +- app/widgets/gimpfiledialog.h | 2 + app/widgets/gimphelp.c | 14 +- app/widgets/gimpwidgets-utils.c | 160 +++++------ app/widgets/gimpwidgets-utils.h | 3 +- libgimp/gimpdisplay_pdb.c | 20 +- libgimp/gimpdisplay_pdb.h | 2 +- libgimp/gimpprocedure-params.h | 2 +- libgimp/gimpprogress.c | 27 +- libgimp/gimpprogress.h | 8 +- libgimp/gimpprogress_pdb.c | 19 +- libgimp/gimpprogress_pdb.h | 2 +- libgimp/gimpprogressbar.c | 110 ++++--- libgimp/gimpui.c | 286 ++++++++----------- libgimp/gimpui.def | 2 - libgimp/gimpui.h | 4 - libgimpwidgets/gimpwidgets.def | 1 + libgimpwidgets/gimpwidgetsutils.c | 131 +++++++++ libgimpwidgets/gimpwidgetsutils.h | 2 + pdb/groups/display.pdb | 16 +- pdb/groups/progress.pdb | 13 +- plug-ins/screenshot/screenshot-freedesktop.c | 12 +- 38 files changed, 561 insertions(+), 456 deletions(-) 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;