tray: Add NaXembed object
This GObject subclass is a minimal wrapper similar to GtkSocket, meant to replace the GTK widgetry involved in the tray manager. Part-of: <https://gitlab.gnome.org/GNOME/gnome-shell/-/merge_requests/2590>
This commit is contained in:
parent
aef1db9585
commit
f9519f4a55
@ -2,11 +2,13 @@ tray_sources = [
|
|||||||
'na-tray-child.c',
|
'na-tray-child.c',
|
||||||
'na-tray-child.h',
|
'na-tray-child.h',
|
||||||
'na-tray-manager.c',
|
'na-tray-manager.c',
|
||||||
'na-tray-manager.h'
|
'na-tray-manager.h',
|
||||||
|
'na-xembed.c',
|
||||||
|
'na-xembed.h'
|
||||||
]
|
]
|
||||||
|
|
||||||
libtray = static_library('tray', tray_sources,
|
libtray = static_library('tray', tray_sources,
|
||||||
c_args: ['-DG_LOG_DOMAIN="notification_area"'],
|
c_args: ['-DG_LOG_DOMAIN="notification_area"'],
|
||||||
dependencies: [clutter_dep, gtk_dep],
|
dependencies: [mutter_dep, gtk_dep],
|
||||||
include_directories: conf_inc
|
include_directories: conf_inc
|
||||||
)
|
)
|
||||||
|
882
src/tray/na-xembed.c
Normal file
882
src/tray/na-xembed.c
Normal file
@ -0,0 +1,882 @@
|
|||||||
|
/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
|
||||||
|
/* Copyright (C) 2022 Red Hat Inc.
|
||||||
|
*
|
||||||
|
* This library 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/>.
|
||||||
|
*
|
||||||
|
* Author: Carlos Garnacho <carlosg@gnome.org>
|
||||||
|
*
|
||||||
|
* Based on GTK+ code by Owen Taylor <otaylor@gtk.org>
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "config.h"
|
||||||
|
|
||||||
|
#include "na-xembed.h"
|
||||||
|
|
||||||
|
#include <meta/meta-x11-errors.h>
|
||||||
|
#include <X11/extensions/Xfixes.h>
|
||||||
|
#include <X11/Xlib.h>
|
||||||
|
#include <X11/Xutil.h>
|
||||||
|
|
||||||
|
typedef struct _NaXembedPrivate NaXembedPrivate;
|
||||||
|
|
||||||
|
struct _NaXembedPrivate
|
||||||
|
{
|
||||||
|
MetaX11Display *x11_display;
|
||||||
|
Window socket_window;
|
||||||
|
Window plug_window;
|
||||||
|
|
||||||
|
int root_x;
|
||||||
|
int root_y;
|
||||||
|
int request_width;
|
||||||
|
int request_height;
|
||||||
|
int current_width;
|
||||||
|
int current_height;
|
||||||
|
int resize_count;
|
||||||
|
int xembed_version;
|
||||||
|
|
||||||
|
unsigned int event_func_id;
|
||||||
|
guint resize_id;
|
||||||
|
|
||||||
|
XVisualInfo *xvisual_info;
|
||||||
|
|
||||||
|
Atom atom__XEMBED;
|
||||||
|
Atom atom__XEMBED_INFO;
|
||||||
|
Atom atom_WM_NORMAL_HINTS;
|
||||||
|
|
||||||
|
gboolean have_size;
|
||||||
|
gboolean need_map;
|
||||||
|
gboolean is_mapped;
|
||||||
|
gboolean has_alpha;
|
||||||
|
};
|
||||||
|
|
||||||
|
/* XEMBED messages */
|
||||||
|
typedef enum _XembedMessageType XembedMessageType;
|
||||||
|
|
||||||
|
enum _XembedMessageType {
|
||||||
|
XEMBED_EMBEDDED_NOTIFY,
|
||||||
|
XEMBED_WINDOW_ACTIVATE,
|
||||||
|
XEMBED_WINDOW_DEACTIVATE,
|
||||||
|
XEMBED_REQUEST_FOCUS,
|
||||||
|
XEMBED_FOCUS_IN,
|
||||||
|
XEMBED_FOCUS_OUT,
|
||||||
|
XEMBED_FOCUS_NEXT,
|
||||||
|
XEMBED_FOCUS_PREV,
|
||||||
|
XEMBED_GRAB_KEY,
|
||||||
|
XEMBED_UNGRAB_KEY,
|
||||||
|
XEMBED_MODALITY_ON,
|
||||||
|
XEMBED_MODALITY_OFF,
|
||||||
|
};
|
||||||
|
|
||||||
|
/* Flags for _XEMBED_INFO */
|
||||||
|
#define XEMBED_MAPPED (1 << 0)
|
||||||
|
|
||||||
|
#define XEMBED_PROTOCOL_VERSION 1
|
||||||
|
|
||||||
|
G_DEFINE_TYPE_WITH_PRIVATE (NaXembed, na_xembed, G_TYPE_OBJECT)
|
||||||
|
|
||||||
|
enum {
|
||||||
|
PLUG_ADDED,
|
||||||
|
PLUG_REMOVED,
|
||||||
|
LAST_SIGNAL
|
||||||
|
};
|
||||||
|
|
||||||
|
static guint signals[LAST_SIGNAL] = { 0, };
|
||||||
|
|
||||||
|
enum {
|
||||||
|
PROP_0,
|
||||||
|
PROP_X11_DISPLAY,
|
||||||
|
N_PROPS
|
||||||
|
};
|
||||||
|
|
||||||
|
static GParamSpec *props[N_PROPS] = { 0, };
|
||||||
|
|
||||||
|
static void
|
||||||
|
xembed_send_message (NaXembed *xembed,
|
||||||
|
Window recipient,
|
||||||
|
XembedMessageType message,
|
||||||
|
glong detail,
|
||||||
|
glong data1,
|
||||||
|
glong data2)
|
||||||
|
{
|
||||||
|
NaXembedPrivate *priv = na_xembed_get_instance_private (xembed);
|
||||||
|
XClientMessageEvent xclient;
|
||||||
|
|
||||||
|
memset (&xclient, 0, sizeof (xclient));
|
||||||
|
xclient.window = recipient;
|
||||||
|
xclient.type = ClientMessage;
|
||||||
|
xclient.message_type = priv->atom__XEMBED;
|
||||||
|
xclient.format = 32;
|
||||||
|
xclient.data.l[0] = 0; /* Time */
|
||||||
|
xclient.data.l[1] = message;
|
||||||
|
xclient.data.l[2] = detail;
|
||||||
|
xclient.data.l[3] = data1;
|
||||||
|
xclient.data.l[4] = data2;
|
||||||
|
|
||||||
|
meta_x11_error_trap_push (priv->x11_display);
|
||||||
|
XSendEvent (meta_x11_display_get_xdisplay (priv->x11_display),
|
||||||
|
recipient,
|
||||||
|
False, NoEventMask, (XEvent*) &xclient);
|
||||||
|
meta_x11_error_trap_pop (priv->x11_display);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
na_xembed_end_embedding (NaXembed *xembed)
|
||||||
|
{
|
||||||
|
NaXembedPrivate *priv = na_xembed_get_instance_private (xembed);
|
||||||
|
|
||||||
|
priv->plug_window = None;
|
||||||
|
priv->current_width = 0;
|
||||||
|
priv->current_height = 0;
|
||||||
|
priv->resize_count = 0;
|
||||||
|
g_clear_handle_id (&priv->resize_id, g_source_remove);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
na_xembed_send_configure_event (NaXembed *xembed)
|
||||||
|
{
|
||||||
|
NaXembedPrivate *priv = na_xembed_get_instance_private (xembed);
|
||||||
|
XConfigureEvent xconfigure;
|
||||||
|
|
||||||
|
memset (&xconfigure, 0, sizeof (xconfigure));
|
||||||
|
xconfigure.type = ConfigureNotify;
|
||||||
|
|
||||||
|
xconfigure.event = priv->plug_window;
|
||||||
|
xconfigure.window = priv->plug_window;
|
||||||
|
|
||||||
|
xconfigure.x = priv->root_x;
|
||||||
|
xconfigure.y = priv->root_y;
|
||||||
|
xconfigure.width = priv->current_width;
|
||||||
|
xconfigure.height = priv->current_height;
|
||||||
|
|
||||||
|
xconfigure.border_width = 0;
|
||||||
|
xconfigure.above = None;
|
||||||
|
xconfigure.override_redirect = False;
|
||||||
|
|
||||||
|
meta_x11_error_trap_push (priv->x11_display);
|
||||||
|
XSendEvent (meta_x11_display_get_xdisplay (priv->x11_display),
|
||||||
|
priv->plug_window,
|
||||||
|
False, NoEventMask,
|
||||||
|
(XEvent*) &xconfigure);
|
||||||
|
meta_x11_error_trap_pop (priv->x11_display);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
na_xembed_synchronize_size (NaXembed *xembed)
|
||||||
|
{
|
||||||
|
NaXembedPrivate *priv = na_xembed_get_instance_private (xembed);
|
||||||
|
Display *xdisplay = meta_x11_display_get_xdisplay (priv->x11_display);
|
||||||
|
int x, y, width, height;
|
||||||
|
|
||||||
|
x = priv->root_x;
|
||||||
|
y = priv->root_y;
|
||||||
|
width = priv->request_width;
|
||||||
|
height = priv->request_height;
|
||||||
|
|
||||||
|
XMoveResizeWindow (xdisplay,
|
||||||
|
priv->socket_window,
|
||||||
|
x, y,
|
||||||
|
width, height);
|
||||||
|
|
||||||
|
if (priv->plug_window)
|
||||||
|
{
|
||||||
|
meta_x11_error_trap_push (priv->x11_display);
|
||||||
|
|
||||||
|
if (width != priv->current_width ||
|
||||||
|
height != priv->current_height)
|
||||||
|
{
|
||||||
|
XMoveResizeWindow (xdisplay,
|
||||||
|
priv->plug_window,
|
||||||
|
0, 0,
|
||||||
|
width, height);
|
||||||
|
if (priv->resize_count)
|
||||||
|
priv->resize_count--;
|
||||||
|
|
||||||
|
priv->current_width = width;
|
||||||
|
priv->current_height = height;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (priv->need_map)
|
||||||
|
{
|
||||||
|
XMapWindow (xdisplay, priv->plug_window);
|
||||||
|
priv->need_map = FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
while (priv->resize_count)
|
||||||
|
{
|
||||||
|
na_xembed_send_configure_event (xembed);
|
||||||
|
priv->resize_count--;
|
||||||
|
}
|
||||||
|
|
||||||
|
meta_x11_error_trap_pop (priv->x11_display);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static gboolean
|
||||||
|
synchronize_size_cb (gpointer user_data)
|
||||||
|
{
|
||||||
|
NaXembed *xembed = user_data;
|
||||||
|
NaXembedPrivate *priv = na_xembed_get_instance_private (xembed);
|
||||||
|
|
||||||
|
na_xembed_synchronize_size (xembed);
|
||||||
|
priv->resize_id = 0;
|
||||||
|
|
||||||
|
return G_SOURCE_REMOVE;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
na_xembed_resize (NaXembed *xembed)
|
||||||
|
{
|
||||||
|
NaXembedPrivate *priv = na_xembed_get_instance_private (xembed);
|
||||||
|
XSizeHints hints;
|
||||||
|
long supplied;
|
||||||
|
|
||||||
|
g_clear_handle_id (&priv->resize_id, g_source_remove);
|
||||||
|
|
||||||
|
meta_x11_error_trap_push (priv->x11_display);
|
||||||
|
|
||||||
|
priv->request_width = 1;
|
||||||
|
priv->request_height = 1;
|
||||||
|
|
||||||
|
if (XGetWMNormalHints (meta_x11_display_get_xdisplay (priv->x11_display),
|
||||||
|
priv->plug_window,
|
||||||
|
&hints, &supplied))
|
||||||
|
{
|
||||||
|
if (hints.flags & PMinSize)
|
||||||
|
{
|
||||||
|
priv->request_width = MAX (hints.min_width, 1);
|
||||||
|
priv->request_height = MAX (hints.min_height, 1);
|
||||||
|
}
|
||||||
|
else if (hints.flags & PBaseSize)
|
||||||
|
{
|
||||||
|
priv->request_width = MAX (hints.base_width, 1);
|
||||||
|
priv->request_height = MAX (hints.base_height, 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
priv->have_size = TRUE;
|
||||||
|
meta_x11_error_trap_pop (priv->x11_display);
|
||||||
|
|
||||||
|
priv->resize_id = g_idle_add (synchronize_size_cb, xembed);
|
||||||
|
}
|
||||||
|
|
||||||
|
static gboolean
|
||||||
|
na_xembed_get_info (NaXembed *xembed,
|
||||||
|
Window xwindow,
|
||||||
|
unsigned long *version,
|
||||||
|
unsigned long *flags)
|
||||||
|
{
|
||||||
|
NaXembedPrivate *priv = na_xembed_get_instance_private (xembed);
|
||||||
|
MetaX11Display *x11_display = priv->x11_display;
|
||||||
|
Display *xdisplay = meta_x11_display_get_xdisplay (x11_display);
|
||||||
|
Atom type;
|
||||||
|
int format;
|
||||||
|
unsigned long nitems, bytes_after;
|
||||||
|
unsigned char *data;
|
||||||
|
unsigned long *data_long;
|
||||||
|
int status;
|
||||||
|
|
||||||
|
meta_x11_error_trap_push (x11_display);
|
||||||
|
status = XGetWindowProperty (xdisplay,
|
||||||
|
xwindow,
|
||||||
|
priv->atom__XEMBED_INFO,
|
||||||
|
0, 2, False,
|
||||||
|
priv->atom__XEMBED_INFO,
|
||||||
|
&type, &format,
|
||||||
|
&nitems, &bytes_after, &data);
|
||||||
|
meta_x11_error_trap_pop (x11_display);
|
||||||
|
|
||||||
|
if (status != Success)
|
||||||
|
return FALSE;
|
||||||
|
if (type == None)
|
||||||
|
return FALSE;
|
||||||
|
|
||||||
|
if (type != priv->atom__XEMBED_INFO)
|
||||||
|
{
|
||||||
|
g_warning ("_XEMBED_INFO property has wrong type");
|
||||||
|
XFree (data);
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (nitems < 2)
|
||||||
|
{
|
||||||
|
g_warning ("_XEMBED_INFO too short");
|
||||||
|
XFree (data);
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
data_long = (unsigned long *)data;
|
||||||
|
if (version)
|
||||||
|
*version = data_long[0];
|
||||||
|
if (flags)
|
||||||
|
*flags = data_long[1] & XEMBED_MAPPED;
|
||||||
|
|
||||||
|
XFree (data);
|
||||||
|
return TRUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
na_xembed_add_window (NaXembed *xembed,
|
||||||
|
Window xid,
|
||||||
|
gboolean need_reparent)
|
||||||
|
{
|
||||||
|
NaXembedPrivate *priv = na_xembed_get_instance_private (xembed);
|
||||||
|
Display *xdisplay = meta_x11_display_get_xdisplay (priv->x11_display);
|
||||||
|
XVisualInfo template = { 0, };
|
||||||
|
int n_xvisuals;
|
||||||
|
unsigned long version;
|
||||||
|
unsigned long flags;
|
||||||
|
|
||||||
|
priv->plug_window = xid;
|
||||||
|
|
||||||
|
meta_x11_error_trap_push (priv->x11_display);
|
||||||
|
|
||||||
|
XSelectInput (xdisplay,
|
||||||
|
priv->plug_window,
|
||||||
|
StructureNotifyMask | PropertyChangeMask);
|
||||||
|
|
||||||
|
if (meta_x11_error_trap_pop_with_return (priv->x11_display))
|
||||||
|
{
|
||||||
|
priv->plug_window = None;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* OK, we now will reliably get destroy notification on socket->plug_window */
|
||||||
|
|
||||||
|
meta_x11_error_trap_push (priv->x11_display);
|
||||||
|
|
||||||
|
if (need_reparent)
|
||||||
|
{
|
||||||
|
XSetWindowAttributes socket_attrs;
|
||||||
|
XWindowAttributes plug_attrs;
|
||||||
|
int result;
|
||||||
|
|
||||||
|
result = XGetWindowAttributes (xdisplay, priv->plug_window, &plug_attrs);
|
||||||
|
if (result == 0)
|
||||||
|
{
|
||||||
|
meta_x11_error_trap_pop (priv->x11_display);
|
||||||
|
priv->plug_window = None;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
template.visualid = plug_attrs.visual->visualid;
|
||||||
|
priv->xvisual_info =
|
||||||
|
XGetVisualInfo (meta_x11_display_get_xdisplay (priv->x11_display),
|
||||||
|
VisualIDMask,
|
||||||
|
&template,
|
||||||
|
&n_xvisuals);
|
||||||
|
|
||||||
|
if (!priv->xvisual_info)
|
||||||
|
{
|
||||||
|
meta_x11_error_trap_pop (priv->x11_display);
|
||||||
|
priv->plug_window = None;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
priv->has_alpha = (priv->xvisual_info->depth >
|
||||||
|
__builtin_popcount (priv->xvisual_info->red_mask |
|
||||||
|
priv->xvisual_info->green_mask |
|
||||||
|
priv->xvisual_info->blue_mask));
|
||||||
|
|
||||||
|
socket_attrs.override_redirect = True;
|
||||||
|
|
||||||
|
priv->socket_window =
|
||||||
|
XCreateWindow (xdisplay,
|
||||||
|
meta_x11_display_get_xroot (priv->x11_display),
|
||||||
|
-100, -100, 1, 1, 0,
|
||||||
|
priv->xvisual_info->depth,
|
||||||
|
InputOutput,
|
||||||
|
plug_attrs.visual,
|
||||||
|
CWOverrideRedirect,
|
||||||
|
&socket_attrs);
|
||||||
|
|
||||||
|
XUnmapWindow (xdisplay, priv->plug_window); /* Shouldn't actually be necessary for XEMBED, but just in case */
|
||||||
|
XReparentWindow (xdisplay,
|
||||||
|
priv->plug_window,
|
||||||
|
priv->socket_window,
|
||||||
|
0, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
priv->have_size = FALSE;
|
||||||
|
|
||||||
|
priv->xembed_version = -1;
|
||||||
|
if (na_xembed_get_info (xembed, priv->plug_window, &version, &flags))
|
||||||
|
{
|
||||||
|
priv->xembed_version = MIN (XEMBED_PROTOCOL_VERSION, version);
|
||||||
|
priv->is_mapped = (flags & XEMBED_MAPPED) != 0;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
/* FIXME, we should probably actually check the state before we started */
|
||||||
|
priv->is_mapped = TRUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
priv->need_map = priv->is_mapped;
|
||||||
|
|
||||||
|
meta_x11_error_trap_pop (priv->x11_display);
|
||||||
|
|
||||||
|
meta_x11_error_trap_push (priv->x11_display);
|
||||||
|
XFixesChangeSaveSet (xdisplay, priv->plug_window,
|
||||||
|
SetModeInsert, SaveSetRoot, SaveSetUnmap);
|
||||||
|
meta_x11_error_trap_pop (priv->x11_display);
|
||||||
|
|
||||||
|
xembed_send_message (xembed,
|
||||||
|
priv->plug_window,
|
||||||
|
XEMBED_EMBEDDED_NOTIFY, 0,
|
||||||
|
priv->socket_window,
|
||||||
|
priv->xembed_version);
|
||||||
|
|
||||||
|
na_xembed_resize (xembed);
|
||||||
|
|
||||||
|
g_signal_emit (xembed, signals[PLUG_ADDED], 0);
|
||||||
|
|
||||||
|
XMapWindow (xdisplay, priv->socket_window);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
na_xembed_handle_map_request (NaXembed *xembed)
|
||||||
|
{
|
||||||
|
NaXembedPrivate *priv = na_xembed_get_instance_private (xembed);
|
||||||
|
|
||||||
|
if (!priv->is_mapped)
|
||||||
|
{
|
||||||
|
priv->is_mapped = TRUE;
|
||||||
|
priv->need_map = TRUE;
|
||||||
|
na_xembed_resize (xembed);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
na_xembed_handle_unmap_notify (NaXembed *xembed)
|
||||||
|
{
|
||||||
|
NaXembedPrivate *priv = na_xembed_get_instance_private (xembed);
|
||||||
|
|
||||||
|
if (priv->is_mapped)
|
||||||
|
{
|
||||||
|
priv->is_mapped = FALSE;
|
||||||
|
na_xembed_resize (xembed);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
xembed_filter_func (MetaX11Display *x11_display,
|
||||||
|
XEvent *xevent,
|
||||||
|
gpointer user_data)
|
||||||
|
{
|
||||||
|
NaXembed *xembed = user_data;
|
||||||
|
NaXembedPrivate *priv = na_xembed_get_instance_private (xembed);
|
||||||
|
Display *xdisplay = meta_x11_display_get_xdisplay (priv->x11_display);
|
||||||
|
|
||||||
|
if (priv->socket_window == None)
|
||||||
|
return;
|
||||||
|
if (xevent->xany.window != priv->socket_window &&
|
||||||
|
xevent->xany.window != priv->plug_window)
|
||||||
|
return;
|
||||||
|
|
||||||
|
switch (xevent->type)
|
||||||
|
{
|
||||||
|
case ClientMessage:
|
||||||
|
/* We choose to ignore all _XEMBED ClientEvent messages
|
||||||
|
* addressed to the socket.
|
||||||
|
*/
|
||||||
|
break;
|
||||||
|
case CreateNotify:
|
||||||
|
{
|
||||||
|
XCreateWindowEvent *xcwe = &xevent->xcreatewindow;
|
||||||
|
|
||||||
|
if (!priv->plug_window)
|
||||||
|
na_xembed_add_window (xembed, xcwe->window, FALSE);
|
||||||
|
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case ConfigureRequest:
|
||||||
|
{
|
||||||
|
XConfigureRequestEvent *xcre = &xevent->xconfigurerequest;
|
||||||
|
|
||||||
|
if (!priv->plug_window)
|
||||||
|
na_xembed_add_window (xembed, xcre->window, FALSE);
|
||||||
|
|
||||||
|
if (priv->plug_window)
|
||||||
|
{
|
||||||
|
if (xcre->value_mask & (CWWidth | CWHeight))
|
||||||
|
{
|
||||||
|
priv->resize_count++;
|
||||||
|
na_xembed_resize (xembed);
|
||||||
|
}
|
||||||
|
else if (xcre->value_mask & (CWX | CWY))
|
||||||
|
{
|
||||||
|
na_xembed_send_configure_event (xembed);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Ignore stacking requests. */
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case DestroyNotify:
|
||||||
|
{
|
||||||
|
XDestroyWindowEvent *xdwe = &xevent->xdestroywindow;
|
||||||
|
|
||||||
|
/* Note that we get destroy notifies both from SubstructureNotify on
|
||||||
|
* our window and StructureNotify on socket->plug_window
|
||||||
|
*/
|
||||||
|
if (priv->plug_window && (xdwe->window == priv->plug_window))
|
||||||
|
{
|
||||||
|
g_object_ref (xembed);
|
||||||
|
g_signal_emit (xembed, signals[PLUG_REMOVED], 0);
|
||||||
|
na_xembed_end_embedding (xembed);
|
||||||
|
g_object_unref (xembed);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case MapRequest:
|
||||||
|
if (!priv->plug_window)
|
||||||
|
na_xembed_add_window (xembed, xevent->xmaprequest.window, FALSE);
|
||||||
|
|
||||||
|
if (priv->plug_window == xevent->xmaprequest.window)
|
||||||
|
na_xembed_handle_map_request (xembed);
|
||||||
|
break;
|
||||||
|
case PropertyNotify:
|
||||||
|
if (priv->plug_window &&
|
||||||
|
xevent->xproperty.window == priv->plug_window)
|
||||||
|
{
|
||||||
|
if (xevent->xproperty.atom == priv->atom_WM_NORMAL_HINTS)
|
||||||
|
{
|
||||||
|
priv->have_size = FALSE;
|
||||||
|
na_xembed_resize (xembed);
|
||||||
|
}
|
||||||
|
else if (xevent->xproperty.atom == priv->atom__XEMBED_INFO)
|
||||||
|
{
|
||||||
|
unsigned long flags;
|
||||||
|
|
||||||
|
if (na_xembed_get_info (xembed, priv->plug_window, NULL, &flags))
|
||||||
|
{
|
||||||
|
gboolean was_mapped = priv->is_mapped;
|
||||||
|
gboolean is_mapped = (flags & XEMBED_MAPPED) != 0;
|
||||||
|
|
||||||
|
if (was_mapped != is_mapped)
|
||||||
|
{
|
||||||
|
if (is_mapped)
|
||||||
|
{
|
||||||
|
na_xembed_handle_map_request (xembed);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
meta_x11_error_trap_push (priv->x11_display);
|
||||||
|
XMapWindow (xdisplay, priv->plug_window);
|
||||||
|
meta_x11_error_trap_pop (priv->x11_display);
|
||||||
|
|
||||||
|
na_xembed_handle_unmap_notify (xembed);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case ReparentNotify:
|
||||||
|
{
|
||||||
|
XReparentEvent *xre = &xevent->xreparent;
|
||||||
|
|
||||||
|
if (priv->plug_window == None &&
|
||||||
|
xre->parent == priv->socket_window)
|
||||||
|
{
|
||||||
|
na_xembed_add_window (xembed, xre->window, FALSE);
|
||||||
|
}
|
||||||
|
else if (priv->plug_window != None &&
|
||||||
|
xre->window == priv->plug_window &&
|
||||||
|
xre->parent != priv->socket_window)
|
||||||
|
{
|
||||||
|
g_object_ref (xembed);
|
||||||
|
g_signal_emit (xembed, signals[PLUG_REMOVED], 0);
|
||||||
|
na_xembed_end_embedding (xembed);
|
||||||
|
g_object_unref (xembed);
|
||||||
|
}
|
||||||
|
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case UnmapNotify:
|
||||||
|
if (priv->plug_window != None &&
|
||||||
|
xevent->xunmap.window == priv->plug_window)
|
||||||
|
na_xembed_handle_unmap_notify (xembed);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
na_xembed_set_property (GObject *object,
|
||||||
|
guint prop_id,
|
||||||
|
const GValue *value,
|
||||||
|
GParamSpec *pspec)
|
||||||
|
{
|
||||||
|
NaXembed *xembed = NA_XEMBED (object);
|
||||||
|
NaXembedPrivate *priv = na_xembed_get_instance_private (xembed);
|
||||||
|
|
||||||
|
switch (prop_id)
|
||||||
|
{
|
||||||
|
case PROP_X11_DISPLAY:
|
||||||
|
priv->x11_display = g_value_get_object (value);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
na_xembed_get_property (GObject *object,
|
||||||
|
guint prop_id,
|
||||||
|
GValue *value,
|
||||||
|
GParamSpec *pspec)
|
||||||
|
{
|
||||||
|
NaXembed *xembed = NA_XEMBED (object);
|
||||||
|
NaXembedPrivate *priv = na_xembed_get_instance_private (xembed);
|
||||||
|
|
||||||
|
switch (prop_id)
|
||||||
|
{
|
||||||
|
case PROP_X11_DISPLAY:
|
||||||
|
g_value_set_object (value, priv->x11_display);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
na_xembed_finalize (GObject *object)
|
||||||
|
{
|
||||||
|
NaXembed *xembed = NA_XEMBED (object);
|
||||||
|
NaXembedPrivate *priv = na_xembed_get_instance_private (xembed);
|
||||||
|
|
||||||
|
g_clear_pointer (&priv->xvisual_info, XFree);
|
||||||
|
|
||||||
|
if (priv->x11_display && priv->event_func_id)
|
||||||
|
meta_x11_display_remove_event_func (priv->x11_display, priv->event_func_id);
|
||||||
|
|
||||||
|
if (priv->plug_window)
|
||||||
|
na_xembed_end_embedding (xembed);
|
||||||
|
|
||||||
|
G_OBJECT_CLASS (na_xembed_parent_class)->finalize (object);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
na_xembed_constructed (GObject *object)
|
||||||
|
{
|
||||||
|
NaXembed *xembed = NA_XEMBED (object);
|
||||||
|
NaXembedPrivate *priv = na_xembed_get_instance_private (xembed);
|
||||||
|
Display *xdisplay;
|
||||||
|
|
||||||
|
G_OBJECT_CLASS (na_xembed_parent_class)->constructed (object);
|
||||||
|
|
||||||
|
xdisplay = meta_x11_display_get_xdisplay (priv->x11_display);
|
||||||
|
|
||||||
|
priv->event_func_id =
|
||||||
|
meta_x11_display_add_event_func (priv->x11_display,
|
||||||
|
xembed_filter_func,
|
||||||
|
object,
|
||||||
|
NULL);
|
||||||
|
|
||||||
|
priv->atom__XEMBED = XInternAtom (xdisplay, "_XEMBED", False);
|
||||||
|
priv->atom__XEMBED_INFO = XInternAtom (xdisplay, "_XEMBED_INFO", False);
|
||||||
|
priv->atom_WM_NORMAL_HINTS = XInternAtom (xdisplay, "WM_NORMAL_HINTS", False);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
na_xembed_class_init (NaXembedClass *klass)
|
||||||
|
{
|
||||||
|
GObjectClass *object_class = G_OBJECT_CLASS (klass);
|
||||||
|
|
||||||
|
object_class->set_property = na_xembed_set_property;
|
||||||
|
object_class->get_property = na_xembed_get_property;
|
||||||
|
object_class->finalize = na_xembed_finalize;
|
||||||
|
object_class->constructed = na_xembed_constructed;
|
||||||
|
|
||||||
|
signals[PLUG_ADDED] =
|
||||||
|
g_signal_new ("plug-added",
|
||||||
|
G_OBJECT_CLASS_TYPE (klass),
|
||||||
|
G_SIGNAL_RUN_LAST,
|
||||||
|
G_STRUCT_OFFSET (NaXembedClass, plug_added),
|
||||||
|
NULL, NULL, NULL,
|
||||||
|
G_TYPE_NONE, 0);
|
||||||
|
|
||||||
|
signals[PLUG_REMOVED] =
|
||||||
|
g_signal_new ("plug-removed",
|
||||||
|
G_OBJECT_CLASS_TYPE (klass),
|
||||||
|
G_SIGNAL_RUN_LAST,
|
||||||
|
G_STRUCT_OFFSET (NaXembedClass, plug_removed),
|
||||||
|
NULL, NULL, NULL,
|
||||||
|
G_TYPE_NONE, 0);
|
||||||
|
|
||||||
|
props[PROP_X11_DISPLAY] =
|
||||||
|
g_param_spec_object ("x11-display",
|
||||||
|
"x11-display",
|
||||||
|
"x11-display",
|
||||||
|
META_TYPE_X11_DISPLAY,
|
||||||
|
G_PARAM_READWRITE |
|
||||||
|
G_PARAM_CONSTRUCT_ONLY);
|
||||||
|
|
||||||
|
g_object_class_install_properties (object_class, N_PROPS, props);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
na_xembed_init (NaXembed *xembed)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
na_xembed_add_id (NaXembed *xembed,
|
||||||
|
Window window)
|
||||||
|
{
|
||||||
|
na_xembed_add_window (xembed, window, TRUE);
|
||||||
|
}
|
||||||
|
|
||||||
|
Window
|
||||||
|
na_xembed_get_plug_window (NaXembed *xembed)
|
||||||
|
{
|
||||||
|
NaXembedPrivate *priv = na_xembed_get_instance_private (xembed);
|
||||||
|
|
||||||
|
return priv->plug_window;
|
||||||
|
}
|
||||||
|
|
||||||
|
Window
|
||||||
|
na_xembed_get_socket_window (NaXembed *xembed)
|
||||||
|
{
|
||||||
|
NaXembedPrivate *priv = na_xembed_get_instance_private (xembed);
|
||||||
|
|
||||||
|
return priv->socket_window;
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
na_xembed_set_root_position (NaXembed *xembed,
|
||||||
|
int x,
|
||||||
|
int y)
|
||||||
|
{
|
||||||
|
NaXembedPrivate *priv = na_xembed_get_instance_private (xembed);
|
||||||
|
|
||||||
|
if (priv->root_x == x &&
|
||||||
|
priv->root_y == y)
|
||||||
|
return;
|
||||||
|
|
||||||
|
priv->root_x = x;
|
||||||
|
priv->root_y = y;
|
||||||
|
|
||||||
|
if (priv->resize_id == 0)
|
||||||
|
priv->resize_id = g_idle_add (synchronize_size_cb, xembed);
|
||||||
|
}
|
||||||
|
|
||||||
|
MetaX11Display *
|
||||||
|
na_xembed_get_x11_display (NaXembed *xembed)
|
||||||
|
{
|
||||||
|
NaXembedPrivate *priv = na_xembed_get_instance_private (xembed);
|
||||||
|
|
||||||
|
return priv->x11_display;
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
na_xembed_get_size (NaXembed *xembed,
|
||||||
|
int *width,
|
||||||
|
int *height)
|
||||||
|
{
|
||||||
|
NaXembedPrivate *priv = na_xembed_get_instance_private (xembed);
|
||||||
|
|
||||||
|
if (width)
|
||||||
|
*width = priv->request_width;
|
||||||
|
if (height)
|
||||||
|
*height = priv->request_height;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
get_pixel_details (unsigned long pixel_mask,
|
||||||
|
int *shift,
|
||||||
|
int *precision)
|
||||||
|
{
|
||||||
|
unsigned long m = 0;
|
||||||
|
int s = 0;
|
||||||
|
int p = 0;
|
||||||
|
|
||||||
|
if (pixel_mask != 0)
|
||||||
|
{
|
||||||
|
m = pixel_mask;
|
||||||
|
while (!(m & 0x1))
|
||||||
|
{
|
||||||
|
s++;
|
||||||
|
m >>= 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
while (m & 0x1)
|
||||||
|
{
|
||||||
|
p++;
|
||||||
|
m >>= 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (shift)
|
||||||
|
*shift = s;
|
||||||
|
if (precision)
|
||||||
|
*precision = p;
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
na_xembed_set_background_color (NaXembed *xembed,
|
||||||
|
const ClutterColor *color)
|
||||||
|
{
|
||||||
|
NaXembedPrivate *priv = na_xembed_get_instance_private (xembed);
|
||||||
|
XVisualInfo *xvisual_info;
|
||||||
|
Display *xdisplay;
|
||||||
|
unsigned long pixel;
|
||||||
|
|
||||||
|
if (!priv->socket_window)
|
||||||
|
return;
|
||||||
|
if (!priv->xvisual_info)
|
||||||
|
return;
|
||||||
|
|
||||||
|
xvisual_info = priv->xvisual_info;
|
||||||
|
|
||||||
|
if (priv->has_alpha)
|
||||||
|
{
|
||||||
|
pixel = 0;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
double red, green, blue;
|
||||||
|
int red_shift, red_prec, green_shift, green_prec, blue_shift, blue_prec;
|
||||||
|
unsigned int padding;
|
||||||
|
|
||||||
|
/* Shifting by >= width-of-type isn't defined in C */
|
||||||
|
if (xvisual_info->depth >= 32)
|
||||||
|
padding = 0;
|
||||||
|
else
|
||||||
|
padding = ((~(uint32_t)0)) << xvisual_info->depth;
|
||||||
|
|
||||||
|
red = (double) color->red / 255.0;
|
||||||
|
green = (double) color->green / 255.0;
|
||||||
|
blue = (double) color->blue / 255.0;
|
||||||
|
|
||||||
|
get_pixel_details (xvisual_info->red_mask, &red_shift, &red_prec);
|
||||||
|
get_pixel_details (xvisual_info->green_mask, &green_shift, &green_prec);
|
||||||
|
get_pixel_details (xvisual_info->blue_mask, &blue_shift, &blue_prec);
|
||||||
|
|
||||||
|
pixel = ~(xvisual_info->red_mask |
|
||||||
|
xvisual_info->green_mask |
|
||||||
|
xvisual_info->blue_mask |
|
||||||
|
padding);
|
||||||
|
|
||||||
|
pixel += (((int) (red * ((1 << red_prec) - 1))) << red_shift) +
|
||||||
|
(((int) (green * ((1 << green_prec) - 1))) << green_shift) +
|
||||||
|
(((int) (blue * ((1 << blue_prec) - 1))) << blue_shift);
|
||||||
|
}
|
||||||
|
|
||||||
|
xdisplay = meta_x11_display_get_xdisplay (priv->x11_display);
|
||||||
|
|
||||||
|
XSetWindowBackground (xdisplay, priv->socket_window, pixel);
|
||||||
|
XClearWindow (xdisplay, priv->socket_window);
|
||||||
|
}
|
63
src/tray/na-xembed.h
Normal file
63
src/tray/na-xembed.h
Normal file
@ -0,0 +1,63 @@
|
|||||||
|
/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
|
||||||
|
/* na-xembed.h
|
||||||
|
* Copyright (C) 2022 Red Hat Inc.
|
||||||
|
*
|
||||||
|
* This library 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/>.
|
||||||
|
*
|
||||||
|
* Author: Carlos Garnacho <carlosg@gnome.org>
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef NA_XEMBED_H
|
||||||
|
#define NA_XEMBED_H
|
||||||
|
|
||||||
|
#include <meta/meta-x11-display.h>
|
||||||
|
#include <glib-object.h>
|
||||||
|
#include <X11/Xlib.h>
|
||||||
|
|
||||||
|
G_BEGIN_DECLS
|
||||||
|
|
||||||
|
#define NA_TYPE_XEMBED (na_xembed_get_type ())
|
||||||
|
G_DECLARE_DERIVABLE_TYPE (NaXembed, na_xembed, NA, XEMBED, GObject)
|
||||||
|
|
||||||
|
struct _NaXembedClass
|
||||||
|
{
|
||||||
|
GObjectClass parent_class;
|
||||||
|
|
||||||
|
void (* plug_added) (NaXembed *xembed);
|
||||||
|
void (* plug_removed) (NaXembed *xembed);
|
||||||
|
};
|
||||||
|
|
||||||
|
MetaX11Display * na_xembed_get_x11_display (NaXembed *xembed);
|
||||||
|
|
||||||
|
void na_xembed_add_id (NaXembed *xembed,
|
||||||
|
Window window);
|
||||||
|
|
||||||
|
Window na_xembed_get_plug_window (NaXembed *xembed);
|
||||||
|
|
||||||
|
Window na_xembed_get_socket_window (NaXembed *xembed);
|
||||||
|
|
||||||
|
void na_xembed_set_root_position (NaXembed *xembed,
|
||||||
|
int x,
|
||||||
|
int y);
|
||||||
|
|
||||||
|
void na_xembed_get_size (NaXembed *xembed,
|
||||||
|
int *width,
|
||||||
|
int *height);
|
||||||
|
|
||||||
|
void na_xembed_set_background_color (NaXembed *xembed,
|
||||||
|
const ClutterColor *color);
|
||||||
|
|
||||||
|
G_END_DECLS
|
||||||
|
|
||||||
|
#endif /* NA_XEMBED_H */
|
Loading…
Reference in New Issue
Block a user