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.
This commit is contained in:
Jehan 2023-08-14 14:23:06 +02:00
parent bf210243f2
commit 58b3b14082
38 changed files with 561 additions and 456 deletions

View File

@ -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 *

View File

@ -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,

View File

@ -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 *

View File

@ -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

View File

@ -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,

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -23,6 +23,10 @@
#include <gegl.h>
#include <gtk/gtk.h>
#ifdef GDK_WINDOWING_WAYLAND
#include <gdk/gdkwayland.h>
#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);
}

View File

@ -51,6 +51,8 @@ struct _GimpDisplayShell
GimpDisplay *display;
GBytes *window_handle;
GimpUIManager *popup_manager;
GdkMonitor *initial_monitor;

View File

@ -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);
}
}

View File

@ -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);

View File

@ -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 <sven@gimp.org>",
@ -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);

View File

@ -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 <mitch@gimp.org>",
"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);

View File

@ -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;

View File

@ -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);

View File

@ -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;
}

View File

@ -38,6 +38,8 @@ struct _GimpFileDialog
{
GtkFileChooserDialog parent_instance;
GBytes *window_handle;
Gimp *gimp;
GimpImage *image;

View File

@ -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 "

View File

@ -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;
}

View File

@ -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);

View File

@ -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;
}
/**

View File

@ -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,

View File

@ -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) \

View File

@ -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;

View File

@ -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);

View File

@ -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;
}
/**

View File

@ -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);

View File

@ -21,6 +21,7 @@
#include "config.h"
#include <gegl.h>
#include <gtk/gtk.h>
#ifdef GDK_WINDOWING_WIN32
@ -35,6 +36,8 @@
#include <gdk/gdkwayland.h>
#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);
}
/**

View File

@ -32,6 +32,10 @@
#include <Cocoa/Cocoa.h>
#endif
#ifdef GDK_WINDOWING_WAYLAND
#include <gdk/gdkwayland.h>
#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;
}

View File

@ -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

View File

@ -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);

View File

@ -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

View File

@ -26,6 +26,16 @@
#include <gegl.h>
#include <gtk/gtk.h>
#ifdef GDK_WINDOWING_WIN32
#include <gdk/gdkwin32.h>
#endif
#ifdef GDK_WINDOWING_X11
#include <gdk/gdkx.h>
#endif
#ifdef GDK_WINDOWING_WAYLAND
#include <gdk/gdkwayland.h>
#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;
}

View File

@ -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

View File

@ -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
);

View File

@ -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;

View File

@ -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;