5001bd8810
Previously when a client requests that a window should be docked the shell would reparent the socket window onto the stage's window and then use ClutterX11TexturePixmap to get a texture to represent the window. This will not work if Clutter is no longer using the X11 winsys for example if it becomes its own display server. Instead this patch leaves the socket window as a child of the root window and lets mutter create a MetaWindow out of it. If Mutter is acting as a display server then this mechanism will still work via the headless x server. The ShellGtkEmbed instance now registers for notification of the ‘window-created’ signal of the display so that it can find the MetaWindow that gets created to represent the socket window. When this window is found it is prevented from being displayed on the screen by setting the actor's opacity to 0. An input shape is then set on the window to prevent it receiving any input. Instead of being a subclass of ClutterX11TexturePixmap, ShellGtkEmbed is now a subclass of ClutterClone. When the MetaWindow is found for the socket window the clone's source is set to the invisible actor for the window so it can be displayed in the panel as before. The ShellEmbeddedWindow no longer needs to know what the stage is because it no longer reparents the socket window. Therefore the ShellTrayManager doesn't need to know the stage either so shell_tray_manager_manage_stage has been replaced with just shell_tray_manager_manage_screen. https://bugzilla.gnome.org/show_bug.cgi?id=693438
233 lines
6.9 KiB
C
233 lines
6.9 KiB
C
/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
|
|
|
|
#include "config.h"
|
|
|
|
#include <gdk/gdkx.h>
|
|
#include <clutter/x11/clutter-x11.h>
|
|
|
|
#include "shell-embedded-window-private.h"
|
|
|
|
/* This type is a subclass of GtkWindow that ties the window to a
|
|
* ShellGtkEmbed; the resizing logic is bound to the clutter logic.
|
|
*
|
|
* The typical usage we might expect is
|
|
*
|
|
* - ShellEmbeddedWindow is created and filled with content
|
|
* - ShellEmbeddedWindow is shown with gtk_widget_show_all()
|
|
* - ShellGtkEmbed is created for the ShellEmbeddedWindow
|
|
* - actor is added to a stage
|
|
*
|
|
* The way it works is that the GtkWindow is mapped if and only if both:
|
|
*
|
|
* - gtk_widget_visible (window) [widget has been shown]
|
|
* - Actor is mapped [actor and all parents visible, actor in stage]
|
|
*/
|
|
|
|
G_DEFINE_TYPE (ShellEmbeddedWindow, shell_embedded_window, GTK_TYPE_WINDOW);
|
|
|
|
enum {
|
|
PROP_0
|
|
};
|
|
|
|
struct _ShellEmbeddedWindowPrivate {
|
|
ShellGtkEmbed *actor;
|
|
|
|
GdkRectangle position;
|
|
};
|
|
|
|
/*
|
|
* The normal gtk_window_show() starts all of the complicated asynchronous
|
|
* window resizing code running; we don't want or need any of that.
|
|
* Bypassing the normal code does mean that the extra geometry management
|
|
* available on GtkWindow: gridding, maximum sizes, etc, is ignored; we
|
|
* don't really want that anyways - we just want a way of embedding a
|
|
* GtkWidget into a Clutter stage.
|
|
*/
|
|
static void
|
|
shell_embedded_window_show (GtkWidget *widget)
|
|
{
|
|
ShellEmbeddedWindow *window = SHELL_EMBEDDED_WINDOW (widget);
|
|
GtkWidgetClass *widget_class;
|
|
|
|
/* Skip GtkWindow, but run the default GtkWidget handling which
|
|
* marks the widget visible */
|
|
widget_class = g_type_class_peek (GTK_TYPE_WIDGET);
|
|
widget_class->show (widget);
|
|
|
|
if (window->priv->actor)
|
|
{
|
|
/* Size is 0x0 if the GtkWindow is not shown */
|
|
clutter_actor_queue_relayout (CLUTTER_ACTOR (window->priv->actor));
|
|
|
|
if (CLUTTER_ACTOR_IS_REALIZED (window->priv->actor))
|
|
gtk_widget_map (widget);
|
|
}
|
|
}
|
|
|
|
static void
|
|
shell_embedded_window_hide (GtkWidget *widget)
|
|
{
|
|
ShellEmbeddedWindow *window = SHELL_EMBEDDED_WINDOW (widget);
|
|
|
|
if (window->priv->actor)
|
|
clutter_actor_queue_relayout (CLUTTER_ACTOR (window->priv->actor));
|
|
|
|
GTK_WIDGET_CLASS (shell_embedded_window_parent_class)->hide (widget);
|
|
}
|
|
|
|
static gboolean
|
|
shell_embedded_window_configure_event (GtkWidget *widget,
|
|
GdkEventConfigure *event)
|
|
{
|
|
/* Normally a configure event coming back from X triggers the
|
|
* resizing logic inside GtkWindow; we just ignore them
|
|
* since we are handling the resizing logic separately.
|
|
*/
|
|
return FALSE;
|
|
}
|
|
|
|
static void
|
|
shell_embedded_window_check_resize (GtkContainer *container)
|
|
{
|
|
ShellEmbeddedWindow *window = SHELL_EMBEDDED_WINDOW (container);
|
|
|
|
/* Check resize is called when a resize is queued on something
|
|
* inside the GtkWindow; we need to make sure that in response
|
|
* to this gtk_widget_size_request() and then
|
|
* gtk_widget_size_allocate() are called; we defer to the Clutter
|
|
* logic and assume it will do the right thing.
|
|
*/
|
|
if (window->priv->actor)
|
|
clutter_actor_queue_relayout (CLUTTER_ACTOR (window->priv->actor));
|
|
}
|
|
|
|
static GObject *
|
|
shell_embedded_window_constructor (GType gtype,
|
|
guint n_properties,
|
|
GObjectConstructParam *properties)
|
|
{
|
|
GObject *object;
|
|
GObjectClass *parent_class;
|
|
|
|
parent_class = G_OBJECT_CLASS (shell_embedded_window_parent_class);
|
|
object = parent_class->constructor (gtype, n_properties, properties);
|
|
|
|
/* Setting the resize mode to immediate means that calling queue_resize()
|
|
* on a widget within the window will immmediately call check_resize()
|
|
* to be called, instead of having it queued to an idle. From our perspective,
|
|
* this is ideal since we just are going to queue a resize to Clutter's
|
|
* idle resize anyways.
|
|
*/
|
|
g_object_set (object,
|
|
"resize-mode", GTK_RESIZE_IMMEDIATE,
|
|
"type", GTK_WINDOW_POPUP,
|
|
NULL);
|
|
|
|
return object;
|
|
}
|
|
|
|
static void
|
|
shell_embedded_window_class_init (ShellEmbeddedWindowClass *klass)
|
|
{
|
|
GObjectClass *object_class = G_OBJECT_CLASS (klass);
|
|
GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass);
|
|
GtkContainerClass *container_class = GTK_CONTAINER_CLASS (klass);
|
|
|
|
g_type_class_add_private (klass, sizeof (ShellEmbeddedWindowPrivate));
|
|
|
|
object_class->constructor = shell_embedded_window_constructor;
|
|
|
|
widget_class->show = shell_embedded_window_show;
|
|
widget_class->hide = shell_embedded_window_hide;
|
|
widget_class->configure_event = shell_embedded_window_configure_event;
|
|
|
|
container_class->check_resize = shell_embedded_window_check_resize;
|
|
}
|
|
|
|
static void
|
|
shell_embedded_window_init (ShellEmbeddedWindow *window)
|
|
{
|
|
window->priv = G_TYPE_INSTANCE_GET_PRIVATE (window, SHELL_TYPE_EMBEDDED_WINDOW,
|
|
ShellEmbeddedWindowPrivate);
|
|
}
|
|
|
|
/*
|
|
* Private routines called by ShellGtkEmbed
|
|
*/
|
|
|
|
void
|
|
_shell_embedded_window_set_actor (ShellEmbeddedWindow *window,
|
|
ShellGtkEmbed *actor)
|
|
|
|
{
|
|
g_return_if_fail (SHELL_IS_EMBEDDED_WINDOW (window));
|
|
|
|
window->priv->actor = actor;
|
|
|
|
if (actor &&
|
|
CLUTTER_ACTOR_IS_MAPPED (actor) &&
|
|
gtk_widget_get_visible (GTK_WIDGET (window)))
|
|
gtk_widget_map (GTK_WIDGET (window));
|
|
}
|
|
|
|
void
|
|
_shell_embedded_window_allocate (ShellEmbeddedWindow *window,
|
|
int x,
|
|
int y,
|
|
int width,
|
|
int height)
|
|
{
|
|
GtkAllocation allocation;
|
|
|
|
g_return_if_fail (SHELL_IS_EMBEDDED_WINDOW (window));
|
|
|
|
if (window->priv->position.x == x &&
|
|
window->priv->position.y == y &&
|
|
window->priv->position.width == width &&
|
|
window->priv->position.height == height)
|
|
return;
|
|
|
|
window->priv->position.x = x;
|
|
window->priv->position.y = y;
|
|
window->priv->position.width = width;
|
|
window->priv->position.height = height;
|
|
|
|
if (gtk_widget_get_realized (GTK_WIDGET (window)))
|
|
gdk_window_move_resize (gtk_widget_get_window (GTK_WIDGET (window)),
|
|
x, y, width, height);
|
|
|
|
allocation.x = 0;
|
|
allocation.y = 0;
|
|
allocation.width = width;
|
|
allocation.height = height;
|
|
|
|
gtk_widget_size_allocate (GTK_WIDGET (window), &allocation);
|
|
}
|
|
|
|
void
|
|
_shell_embedded_window_map (ShellEmbeddedWindow *window)
|
|
{
|
|
g_return_if_fail (SHELL_IS_EMBEDDED_WINDOW (window));
|
|
|
|
if (gtk_widget_get_visible (GTK_WIDGET (window)))
|
|
gtk_widget_map (GTK_WIDGET (window));
|
|
}
|
|
|
|
void
|
|
_shell_embedded_window_unmap (ShellEmbeddedWindow *window)
|
|
{
|
|
g_return_if_fail (SHELL_IS_EMBEDDED_WINDOW (window));
|
|
|
|
gtk_widget_unmap (GTK_WIDGET (window));
|
|
}
|
|
|
|
/*
|
|
* Public API
|
|
*/
|
|
GtkWidget *
|
|
shell_embedded_window_new (void)
|
|
{
|
|
return g_object_new (SHELL_TYPE_EMBEDDED_WINDOW,
|
|
NULL);
|
|
}
|