1
0
mirror of https://gitlab.gnome.org/GNOME/nautilus synced 2024-06-30 23:46:35 +00:00

previewer: Manage window handles internally

We've been expecting NautilusFilesView to pass a window handle to
nautilus_previewer_call_show_file(), requiring it to call async
methods from NautilusWindow to get them.

There are a few problems with this approach:

  1. It's a layer violation for the view to call into the window.
     Not only is this conceptually wrong, it's keeping us from
     reusing the view in other windows which are not NautilusWindow
     instances, like as the upcoming FileChooser window.
  2. It's leaking details of the NautilusPreviewer2 D-Bus API into
     NautilusFilesView and NautilusWindow, namely the expected format
     of the window handle.
  3. Even though this format is a de-facto standard nowadays, we are
     not using it for anything else, so there is no value in keeping
     it in NautilusWindow API.
  4. Even if we start using it for other purposes, there it nothing
     specific to NautilusWindow in how to generate a window handle;
     it's the same process for any toplevel surface.
  5. Tying the window handle's validity to the lifetime of the
     NautilusWindow instance means we keep it exported even if no
     longer used by the previewer, and in theory we would use the
     same handle for multiple peers.

So, have NautilusFilesView pass a generic GtkRoot pointer instead
and handle the exporting and unexporting lifetime of window handles
in the previewer glue code instead.

The NautilusWindow API, no unused, is also removed. If the need arises
in the future to reuse this export/unexport logic, we can always move
it into ui-utilities.c or something like that.

Part of: https://gitlab.gnome.org/GNOME/nautilus/-/work_items/3402
This commit is contained in:
António Fernandes 2024-05-02 22:17:14 +01:00
parent 0c5d28bcd6
commit d1742d078d
5 changed files with 137 additions and 180 deletions

View File

@ -1275,52 +1275,13 @@ get_view_directory (NautilusFilesView *view)
return path;
}
typedef struct
{
gchar *uri;
gboolean is_update;
} PreviewExportData;
static void
preview_export_data_free (gpointer _data)
{
PreviewExportData *data = _data;
g_free (data->uri);
g_free (data);
}
G_DEFINE_AUTOPTR_CLEANUP_FUNC (PreviewExportData, preview_export_data_free)
static void
on_window_handle_export (NautilusWindow *window,
const char *handle,
gpointer user_data)
{
g_autoptr (PreviewExportData) data = user_data;
nautilus_previewer_call_show_file (data->uri, handle, !data->is_update);
}
static void
nautilus_files_view_preview (NautilusFilesView *view,
PreviewExportData *data)
{
if (!nautilus_window_export_handle (nautilus_files_view_get_window (view),
on_window_handle_export,
data))
{
/* Let's use a fallback, so at least a preview will be displayed */
nautilus_previewer_call_show_file (data->uri, "x11:0", !data->is_update);
}
}
static void
nautilus_files_view_preview_update (NautilusFilesView *view)
{
NautilusFilesViewPrivate *priv = nautilus_files_view_get_instance_private (view);
GtkApplication *app;
GtkWindow *window;
GtkRoot *window;
g_autolist (NautilusFile) selection = NULL;
PreviewExportData *data;
if (!priv->active ||
!nautilus_previewer_is_visible ())
@ -1329,8 +1290,8 @@ nautilus_files_view_preview_update (NautilusFilesView *view)
}
app = GTK_APPLICATION (g_application_get_default ());
window = GTK_WINDOW (nautilus_files_view_get_window (view));
if (window == NULL || window != gtk_application_get_active_window (app))
window = gtk_widget_get_root (GTK_WIDGET (view));
if (window == NULL || GTK_WINDOW (window) != gtk_application_get_active_window (app))
{
return;
}
@ -1341,11 +1302,9 @@ nautilus_files_view_preview_update (NautilusFilesView *view)
return;
}
data = g_new0 (PreviewExportData, 1);
data->uri = nautilus_file_get_uri (selection->data);
data->is_update = TRUE;
g_autofree gchar *uri = nautilus_file_get_uri (selection->data);
nautilus_files_view_preview (view, data);
nautilus_previewer_call_show_file (uri, window, FALSE);
}
void
@ -1819,14 +1778,13 @@ action_preview_selection (GSimpleAction *action,
{
NautilusFilesView *view = NAUTILUS_FILES_VIEW (user_data);
g_autolist (NautilusFile) selection = NULL;
PreviewExportData *data = g_new0 (PreviewExportData, 1);
GtkRoot *window = gtk_widget_get_root (GTK_WIDGET (view));
selection = nautilus_view_get_selection (NAUTILUS_VIEW (view));
data->uri = nautilus_file_get_uri (selection->data);
data->is_update = FALSE;
g_autofree gchar *uri = nautilus_file_get_uri (selection->data);
nautilus_files_view_preview (view, data);
nautilus_previewer_call_show_file (uri, window, TRUE);
}
static void

View File

@ -31,6 +31,13 @@
#include <gio/gio.h>
#ifdef GDK_WINDOWING_WAYLAND
#include <gdk/wayland/gdkwayland.h>
#endif
#ifdef GDK_WINDOWING_X11
#include <gdk/x11/gdkx.h>
#endif
#define PREVIEWER2_DBUS_IFACE "org.gnome.NautilusPreviewer2"
static const char *previewer_dbus_name = "org.gnome.NautilusPreviewer" PROFILE;
@ -43,7 +50,12 @@ static guint subscription_id = 0;
static GCancellable *cancellable = NULL;
static GtkRoot *current_window = NULL; /* weak ref */
static gchar *exported_window_handle = NULL;
static void real_call_show_file (const gchar *uri,
const gchar *window_handle,
gboolean close_if_already_visible);
static void create_new_bus (void);
static void previewer_selection_event (GDBusConnection *connection,
const gchar *sender_name,
@ -53,6 +65,61 @@ static void previewer_selection_event (GDBusConnection *connection,
GVariant *parameters,
gpointer user_data);
#ifdef GDK_WINDOWING_WAYLAND
typedef struct
{
gchar *uri;
gboolean close_if_already_visible;
} PreviewExportData;
static void
preview_export_data_free (gpointer _data)
{
PreviewExportData *data = _data;
g_free (data->uri);
g_free (data);
}
G_DEFINE_AUTOPTR_CLEANUP_FUNC (PreviewExportData, preview_export_data_free)
static void
wayland_window_handle_exported (GdkToplevel *toplevel,
const char *wayland_handle_str,
gpointer user_data)
{
PreviewExportData *data = user_data;
g_autofree char *wayland_handle = g_strdup_printf ("wayland:%s", wayland_handle_str);
real_call_show_file (data->uri, wayland_handle, data->close_if_already_visible);
}
#endif
static void
clear_exported_window_handle (void)
{
if (exported_window_handle == NULL)
{
return;
}
#ifdef GDK_WINDOWING_WAYLAND
if (current_window != NULL &&
GDK_IS_WAYLAND_DISPLAY (gtk_widget_get_display (GTK_WIDGET (current_window))))
{
GdkSurface *gdk_surface = gtk_native_get_surface (GTK_NATIVE (current_window));
if (GDK_IS_WAYLAND_TOPLEVEL (gdk_surface) &&
g_str_has_prefix (exported_window_handle, "wayland:"))
{
gdk_wayland_toplevel_drop_exported_handle (GDK_WAYLAND_TOPLEVEL (gdk_surface),
exported_window_handle + strlen ("wayland:"));
}
}
#endif
g_free (exported_window_handle);
exported_window_handle = NULL;
}
static void
on_ping_finished (GObject *object,
GAsyncResult *res,
@ -173,9 +240,65 @@ previewer2_method_ready_cb (GObject *source,
void
nautilus_previewer_call_show_file (const gchar *uri,
const gchar *window_handle,
GtkRoot *window,
gboolean close_if_already_visible)
{
/* Reuse existing handle if called again for the same window. */
if (current_window == window &&
exported_window_handle != NULL)
{
real_call_show_file (uri, exported_window_handle, close_if_already_visible);
return;
}
/* Otherwise, obtain a new window handle. */
clear_exported_window_handle ();
g_set_weak_pointer (&current_window, window);
GdkSurface *gdk_surface = gtk_native_get_surface (GTK_NATIVE (window));
#ifdef GDK_WINDOWING_X11
if (GDK_IS_X11_DISPLAY (gtk_widget_get_display (GTK_WIDGET (window))))
{
guint xid = (guint) gdk_x11_surface_get_xid (gdk_surface);
g_autofree char *window_handle = g_strdup_printf ("x11:%x", xid);
real_call_show_file (uri, window_handle, close_if_already_visible);
return;
}
#endif
#ifdef GDK_WINDOWING_WAYLAND
if (GDK_IS_WAYLAND_DISPLAY (gtk_widget_get_display (GTK_WIDGET (window))))
{
g_autoptr (PreviewExportData) data = g_new0 (PreviewExportData, 1);
data->uri = g_strdup (uri);
data->close_if_already_visible = close_if_already_visible;
if (gdk_wayland_toplevel_export_handle (GDK_WAYLAND_TOPLEVEL (gdk_surface),
wayland_window_handle_exported,
data,
preview_export_data_free))
{
/* Don't let autoptr free data successfully taken by the call. */
(void) g_steal_pointer (&data);
return;
}
}
#endif
g_warning ("Couldn't export handle, unsupported windowing system");
/* Let's use a fallback, so at least a preview will be displayed */
real_call_show_file (uri, "x11:0", close_if_already_visible);
}
static void
real_call_show_file (const gchar *uri,
const gchar *window_handle,
gboolean close_if_already_visible)
{
g_set_str (&exported_window_handle, window_handle);
if (!ensure_previewer_proxy ())
{
return;
@ -270,6 +393,8 @@ nautilus_previewer_teardown (GDBusConnection *connection)
g_cancellable_cancel (cancellable);
g_clear_object (&cancellable);
g_clear_object (&previewer_proxy);
clear_exported_window_handle ();
g_clear_weak_pointer (&current_window);
}
gboolean

View File

@ -24,12 +24,13 @@
#include <gio/gio.h>
#include <glib.h>
#include <gtk/gtk.h>
G_BEGIN_DECLS
void nautilus_previewer_call_show_file (const gchar *uri,
const gchar *window_handle,
gboolean close_if_already_visible);
GtkRoot *window,
gboolean close_if_already_visible);
void nautilus_previewer_call_close (void);
gboolean nautilus_previewer_is_visible (void);

View File

@ -38,10 +38,6 @@
#include <gdk/wayland/gdkwayland.h>
#endif
#ifdef GDK_WINDOWING_X11
#include <gdk/x11/gdkx.h>
#endif
#include "gtk/nautilusgtkplacessidebarprivate.h"
#include "nautilus-application.h"
@ -126,9 +122,6 @@ struct _NautilusWindow
/* focus widget before the location bar has been shown temporarily */
GtkWidget *last_focus_widget;
/* Handle when exported */
gchar *export_handle;
guint sidebar_width_handler_id;
gulong bookmarks_id;
gulong starred_id;
@ -1013,20 +1006,6 @@ action_toggle_sidebar (GSimpleAction *action,
adw_overlay_split_view_set_show_sidebar (ADW_OVERLAY_SPLIT_VIEW (window->split_view), !revealed);
}
static guint
get_window_xid (NautilusWindow *window)
{
#ifdef GDK_WINDOWING_X11
if (GDK_IS_X11_DISPLAY (gtk_widget_get_display (GTK_WIDGET (window))))
{
GdkSurface *gdk_surface = gtk_native_get_surface (GTK_NATIVE (window));
return (guint) gdk_x11_surface_get_xid (gdk_surface);
}
#endif
return 0;
}
static void
nautilus_window_set_up_sidebar (NautilusWindow *window)
{
@ -1836,8 +1815,6 @@ nautilus_window_dispose (GObject *object)
g_clear_signal_handler (&window->starred_id, nautilus_tag_manager_get ());
}
nautilus_window_unexport_handle (window);
gtk_widget_dispose_template (GTK_WIDGET (window), NAUTILUS_TYPE_WINDOW);
G_OBJECT_CLASS (nautilus_window_parent_class)->dispose (object);
@ -2011,101 +1988,6 @@ nautilus_window_key_bubble (GtkEventControllerKey *controller,
return GDK_EVENT_PROPAGATE;
}
#ifdef GDK_WINDOWING_WAYLAND
typedef struct
{
NautilusWindow *window;
NautilusWindowHandleExported callback;
gpointer user_data;
} WaylandWindowHandleExportedData;
static void
wayland_window_handle_exported (GdkToplevel *toplevel,
const char *wayland_handle_str,
gpointer user_data)
{
WaylandWindowHandleExportedData *data = user_data;
data->window->export_handle = g_strdup_printf ("wayland:%s", wayland_handle_str);
data->callback (data->window, data->window->export_handle, data->user_data);
}
#endif
gboolean
nautilus_window_export_handle (NautilusWindow *window,
NautilusWindowHandleExported callback,
gpointer user_data)
{
if (window->export_handle != NULL)
{
callback (window, window->export_handle, user_data);
return TRUE;
}
#ifdef GDK_WINDOWING_X11
if (GDK_IS_X11_DISPLAY (gtk_widget_get_display (GTK_WIDGET (window))))
{
window->export_handle = g_strdup_printf ("x11:%x", get_window_xid (window));
callback (window, window->export_handle, user_data);
return TRUE;
}
#endif
#ifdef GDK_WINDOWING_WAYLAND
if (GDK_IS_WAYLAND_DISPLAY (gtk_widget_get_display (GTK_WIDGET (window))))
{
GdkSurface *gdk_surface = gtk_native_get_surface (GTK_NATIVE (window));
WaylandWindowHandleExportedData *data;
data = g_new0 (WaylandWindowHandleExportedData, 1);
data->window = window;
data->callback = callback;
data->user_data = user_data;
if (!gdk_wayland_toplevel_export_handle (GDK_WAYLAND_TOPLEVEL (gdk_surface),
wayland_window_handle_exported,
data,
g_free))
{
g_free (data);
return FALSE;
}
else
{
return TRUE;
}
}
#endif
g_warning ("Couldn't export handle, unsupported windowing system");
return FALSE;
}
void
nautilus_window_unexport_handle (NautilusWindow *window)
{
if (window->export_handle == NULL)
{
return;
}
#ifdef GDK_WINDOWING_WAYLAND
if (GDK_IS_WAYLAND_DISPLAY (gtk_widget_get_display (GTK_WIDGET (window))))
{
GdkSurface *gdk_surface = gtk_native_get_surface (GTK_NATIVE (window));
if (GDK_IS_WAYLAND_TOPLEVEL (gdk_surface) &&
g_str_has_prefix (window->export_handle, "wayland:"))
{
gdk_wayland_toplevel_drop_exported_handle (GDK_WAYLAND_TOPLEVEL (gdk_surface),
window->export_handle + strlen ("wayland:"));
}
}
#endif
g_clear_pointer (&window->export_handle, g_free);
}
/**
* nautilus_window_show:
* @widget: GtkWidget

View File

@ -41,10 +41,6 @@ typedef gboolean (* NautilusWindowGoToCallback) (NautilusWindow *window,
GError *error,
gpointer user_data);
typedef void (* NautilusWindowHandleExported) (NautilusWindow *window,
const char *handle,
gpointer user_data);
/* window geometry */
/* Min values are very small, and a Nautilus window at this tiny size is *almost*
* completely unusable. However, if all the extra bits (sidebar, location bar, etc)
@ -94,11 +90,6 @@ void nautilus_window_initialize_slot (NautilusWindow *window,
NautilusWindowSlot *slot,
NautilusOpenFlags flags);
gboolean nautilus_window_export_handle (NautilusWindow *window,
NautilusWindowHandleExported callback,
gpointer user_data);
void nautilus_window_unexport_handle (NautilusWindow *window);
void nautilus_window_back_or_forward_in_new_tab (NautilusWindow *window,
NautilusNavigationDirection back);