mirror of
https://gitlab.gnome.org/GNOME/nautilus
synced 2024-06-30 23:46:35 +00:00
This private mutter protocol requires each client to have a unique hard-coded enum id. Requires mutter patch to add this enum value on that side.
300 lines
8.5 KiB
C
300 lines
8.5 KiB
C
/*
|
|
* Copyright © 2016 Red Hat, Inc
|
|
*
|
|
* This program is free software; you can redistribute it and/or
|
|
* modify it under the terms of the GNU Lesser General Public
|
|
* License as published by the Free Software Foundation; either
|
|
* version 2 of the License, or (at your option) any later version.
|
|
*
|
|
* This library is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
* Lesser General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU Lesser General Public
|
|
* License along with this library. If not, see <http://www.gnu.org/licenses/>.
|
|
*
|
|
* Authors:
|
|
* Jonas Ådahl <jadahl@redhat.com>
|
|
*/
|
|
|
|
#include "config.h"
|
|
|
|
#include "externalwindow-wayland.h"
|
|
|
|
#include <gdk/gdk.h>
|
|
#include <gdk/wayland/gdkwayland.h>
|
|
#include <wayland-client.h>
|
|
|
|
#include "mutter-x11-interop-client-protocol.h"
|
|
#include "shell-dbus.h"
|
|
|
|
#pragma GCC diagnostic ignored "-Wshadow"
|
|
|
|
#define WAYLAND_HANDLE_PREFIX "wayland:"
|
|
#define X11_HANDLE_PREFIX "x11:"
|
|
|
|
typedef enum _ServiceClientType
|
|
{
|
|
SERVICE_CLIENT_TYPE_NONE,
|
|
SERVICE_CLIENT_TYPE_PORTAL_BACKEND,
|
|
SERVICE_CLIENT_TYPE_FILECHOOSER_PORTAL_BACKEND,
|
|
} ServiceClientType;
|
|
|
|
typedef enum _ParentWindowType
|
|
{
|
|
PARENT_WINDOW_TYPE_WAYLAND,
|
|
PARENT_WINDOW_TYPE_X11,
|
|
} ParentWindowType;
|
|
|
|
struct _ExternalWindowWayland
|
|
{
|
|
ExternalWindow parent;
|
|
|
|
ParentWindowType parent_type;
|
|
struct {
|
|
char *xdg_foreign_handle;
|
|
} wayland;
|
|
struct {
|
|
int xid;
|
|
gulong mapped_handler_id;
|
|
} x11;
|
|
|
|
GdkSurface *parent_surface;
|
|
};
|
|
|
|
struct _ExternalWindowWaylandClass
|
|
{
|
|
ExternalWindowClass parent_class;
|
|
};
|
|
|
|
G_DEFINE_TYPE (ExternalWindowWayland, external_window_wayland,
|
|
EXTERNAL_TYPE_WINDOW)
|
|
|
|
static struct mutter_x11_interop *x11_interop = NULL;
|
|
|
|
ExternalWindowWayland *
|
|
external_window_wayland_new (const char *handle_str)
|
|
{
|
|
g_autoptr(ExternalWindowWayland) external_window_wayland = NULL;
|
|
|
|
external_window_wayland = g_object_new (EXTERNAL_TYPE_WINDOW_WAYLAND,
|
|
NULL);
|
|
|
|
if (g_str_has_prefix (handle_str, WAYLAND_HANDLE_PREFIX))
|
|
{
|
|
external_window_wayland->parent_type = PARENT_WINDOW_TYPE_WAYLAND;
|
|
external_window_wayland->wayland.xdg_foreign_handle =
|
|
g_strdup (handle_str + strlen (WAYLAND_HANDLE_PREFIX));
|
|
}
|
|
else if (g_str_has_prefix (handle_str, X11_HANDLE_PREFIX))
|
|
{
|
|
const char *x11_handle_str = handle_str + strlen (X11_HANDLE_PREFIX);
|
|
int xid;
|
|
|
|
errno = 0;
|
|
xid = strtol (x11_handle_str, NULL, 16);
|
|
if (errno != 0)
|
|
{
|
|
g_warning ("Failed to reference external X11 window, invalid XID %s",
|
|
x11_handle_str);
|
|
return NULL;
|
|
}
|
|
|
|
external_window_wayland->parent_type = PARENT_WINDOW_TYPE_X11;
|
|
external_window_wayland->x11.xid = xid;
|
|
}
|
|
else
|
|
{
|
|
g_warning ("Invalid external window handle string '%s'", handle_str);
|
|
return NULL;
|
|
}
|
|
|
|
return g_steal_pointer (&external_window_wayland);
|
|
}
|
|
|
|
static void
|
|
set_x11_parent (ExternalWindow *external_window)
|
|
{
|
|
ExternalWindowWayland *external_window_wayland =
|
|
EXTERNAL_WINDOW_WAYLAND (external_window);
|
|
GdkSurface *parent_surface = external_window_wayland->parent_surface;
|
|
struct wl_surface *wl_surface;
|
|
|
|
g_return_if_fail (parent_surface);
|
|
|
|
wl_surface = gdk_wayland_surface_get_wl_surface (parent_surface);
|
|
mutter_x11_interop_set_x11_parent (x11_interop, wl_surface,
|
|
external_window_wayland->x11.xid);
|
|
}
|
|
|
|
static void
|
|
on_mapped (GdkSurface *surface,
|
|
GParamSpec *pspec,
|
|
ExternalWindow *external_window)
|
|
{
|
|
if (!gdk_surface_get_mapped (surface))
|
|
return;
|
|
|
|
set_x11_parent (external_window);
|
|
}
|
|
|
|
static void
|
|
external_window_wayland_set_parent_of (ExternalWindow *external_window,
|
|
GdkSurface *surface)
|
|
{
|
|
ExternalWindowWayland *external_window_wayland =
|
|
EXTERNAL_WINDOW_WAYLAND (external_window);
|
|
|
|
g_clear_signal_handler (&external_window_wayland->x11.mapped_handler_id,
|
|
external_window_wayland->parent_surface);
|
|
g_clear_object (&external_window_wayland->parent_surface);
|
|
|
|
switch (external_window_wayland->parent_type)
|
|
{
|
|
case PARENT_WINDOW_TYPE_WAYLAND:
|
|
{
|
|
GdkToplevel *toplevel = GDK_TOPLEVEL (surface);
|
|
const char *handle = external_window_wayland->wayland.xdg_foreign_handle;
|
|
|
|
if (!gdk_wayland_toplevel_set_transient_for_exported (toplevel, handle))
|
|
g_warning ("Failed to set portal window transient for external parent");
|
|
break;
|
|
}
|
|
case PARENT_WINDOW_TYPE_X11:
|
|
if (gdk_surface_get_mapped (surface))
|
|
{
|
|
set_x11_parent (external_window);
|
|
}
|
|
else
|
|
{
|
|
external_window_wayland->x11.mapped_handler_id =
|
|
g_signal_connect (surface, "notify::mapped",
|
|
G_CALLBACK (on_mapped), external_window);
|
|
}
|
|
break;
|
|
}
|
|
|
|
g_set_object (&external_window_wayland->parent_surface, surface);
|
|
}
|
|
|
|
static void
|
|
external_window_wayland_dispose (GObject *object)
|
|
{
|
|
ExternalWindowWayland *external_window_wayland =
|
|
EXTERNAL_WINDOW_WAYLAND (object);
|
|
|
|
g_clear_pointer (&external_window_wayland->wayland.xdg_foreign_handle,
|
|
g_free);
|
|
g_clear_signal_handler (&external_window_wayland->x11.mapped_handler_id,
|
|
external_window_wayland->parent_surface);
|
|
g_clear_object (&external_window_wayland->parent_surface);
|
|
|
|
G_OBJECT_CLASS (external_window_wayland_parent_class)->dispose (object);
|
|
}
|
|
|
|
static void
|
|
external_window_wayland_init (ExternalWindowWayland *external_window_wayland)
|
|
{
|
|
}
|
|
|
|
static void
|
|
external_window_wayland_class_init (ExternalWindowWaylandClass *klass)
|
|
{
|
|
GObjectClass *object_class = G_OBJECT_CLASS (klass);
|
|
ExternalWindowClass *external_window_class = EXTERNAL_WINDOW_CLASS (klass);
|
|
|
|
object_class->dispose = external_window_wayland_dispose;
|
|
|
|
external_window_class->set_parent_of = external_window_wayland_set_parent_of;
|
|
}
|
|
|
|
static void
|
|
handle_registry_global (void *user_data,
|
|
struct wl_registry *registry,
|
|
uint32_t id,
|
|
const char *interface,
|
|
uint32_t version)
|
|
{
|
|
struct mutter_x11_interop **x11_interop = user_data;
|
|
|
|
if (strcmp (interface, mutter_x11_interop_interface.name) == 0)
|
|
{
|
|
*x11_interop = wl_registry_bind (registry, id,
|
|
&mutter_x11_interop_interface, 1);
|
|
}
|
|
}
|
|
|
|
static void
|
|
handle_registry_global_remove (void *user_data,
|
|
struct wl_registry *registry,
|
|
uint32_t name)
|
|
{
|
|
}
|
|
|
|
static const struct wl_registry_listener registry_listener = {
|
|
handle_registry_global,
|
|
handle_registry_global_remove
|
|
};
|
|
|
|
static void
|
|
init_x11_interop (GdkDisplay *display)
|
|
{
|
|
struct wl_display *wl_display;
|
|
struct wl_registry *wl_registry;
|
|
|
|
wl_display = gdk_wayland_display_get_wl_display (display);
|
|
wl_registry = wl_display_get_registry (wl_display);
|
|
wl_registry_add_listener (wl_registry, ®istry_listener, &x11_interop);
|
|
wl_display_roundtrip (wl_display);
|
|
|
|
if (!x11_interop)
|
|
g_debug ("Missing X11 interop protocol support");
|
|
}
|
|
|
|
GdkDisplay *
|
|
init_external_window_wayland_display (GError **error)
|
|
{
|
|
g_autoptr(OrgGnomeMutterServiceChannel) proxy = NULL;
|
|
g_autoptr(GVariant) fd_variant = NULL;
|
|
g_autoptr(GUnixFDList) fd_list = NULL;
|
|
int fd;
|
|
g_autofree char *fd_str = NULL;
|
|
GdkDisplay *display;
|
|
|
|
proxy = org_gnome_mutter_service_channel_proxy_new_for_bus_sync (
|
|
G_BUS_TYPE_SESSION,
|
|
(G_DBUS_PROXY_FLAGS_DO_NOT_AUTO_START |
|
|
G_DBUS_PROXY_FLAGS_DO_NOT_LOAD_PROPERTIES |
|
|
G_DBUS_PROXY_FLAGS_DO_NOT_CONNECT_SIGNALS),
|
|
"org.gnome.Mutter.ServiceChannel",
|
|
"/org/gnome/Mutter/ServiceChannel",
|
|
NULL, error);
|
|
if (!proxy)
|
|
return NULL;
|
|
|
|
if (!org_gnome_mutter_service_channel_call_open_wayland_service_connection_sync (
|
|
proxy,
|
|
SERVICE_CLIENT_TYPE_FILECHOOSER_PORTAL_BACKEND,
|
|
NULL,
|
|
&fd_variant,
|
|
&fd_list,
|
|
NULL, error))
|
|
return NULL;
|
|
|
|
fd = g_unix_fd_list_get (fd_list, g_variant_get_handle (fd_variant), error);
|
|
if (fd < 0)
|
|
return NULL;
|
|
|
|
fd_str = g_strdup_printf ("%d", fd);
|
|
|
|
g_setenv ("WAYLAND_SOCKET", fd_str, TRUE);
|
|
gdk_set_allowed_backends ("wayland");
|
|
display = gdk_display_open (NULL);
|
|
g_assert (display);
|
|
|
|
init_x11_interop (display);
|
|
|
|
return display;
|
|
}
|