2010-09-08 02:27:08 +00:00
|
|
|
/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
|
|
|
|
|
|
|
|
#include "config.h"
|
|
|
|
|
2022-12-22 19:02:28 +00:00
|
|
|
#include "shell-global.h"
|
|
|
|
#include "shell-tray-icon-private.h"
|
|
|
|
#include "shell-util.h"
|
2010-09-08 02:27:08 +00:00
|
|
|
#include "tray/na-tray-child.h"
|
|
|
|
#include "st.h"
|
|
|
|
|
2022-12-22 19:02:28 +00:00
|
|
|
#include <X11/Xatom.h>
|
|
|
|
|
2010-09-08 02:27:08 +00:00
|
|
|
enum {
|
|
|
|
PROP_0,
|
|
|
|
|
|
|
|
PROP_PID,
|
|
|
|
PROP_TITLE,
|
|
|
|
PROP_WM_CLASS
|
|
|
|
};
|
|
|
|
|
2015-09-24 18:07:44 +00:00
|
|
|
typedef struct _ShellTrayIconPrivate ShellTrayIconPrivate;
|
|
|
|
|
|
|
|
struct _ShellTrayIcon
|
|
|
|
{
|
2022-12-22 19:02:28 +00:00
|
|
|
ClutterClone parent;
|
|
|
|
NaTrayChild *tray_child;
|
|
|
|
ClutterActor *window_actor;
|
2010-09-08 02:27:08 +00:00
|
|
|
|
2022-12-22 19:02:28 +00:00
|
|
|
gulong window_actor_destroyed_handler;
|
|
|
|
gulong window_created_handler;
|
2010-09-08 02:27:08 +00:00
|
|
|
pid_t pid;
|
2022-12-22 19:02:28 +00:00
|
|
|
char *title;
|
|
|
|
char *wm_class;
|
2010-09-08 02:27:08 +00:00
|
|
|
};
|
|
|
|
|
2022-12-22 19:02:28 +00:00
|
|
|
G_DEFINE_TYPE (ShellTrayIcon, shell_tray_icon, CLUTTER_TYPE_CLONE);
|
2010-09-08 02:27:08 +00:00
|
|
|
|
|
|
|
static void
|
|
|
|
shell_tray_icon_finalize (GObject *object)
|
|
|
|
{
|
|
|
|
ShellTrayIcon *icon = SHELL_TRAY_ICON (object);
|
|
|
|
|
2022-12-22 19:02:28 +00:00
|
|
|
g_free (icon->title);
|
|
|
|
g_free (icon->wm_class);
|
2010-09-08 02:27:08 +00:00
|
|
|
|
|
|
|
G_OBJECT_CLASS (shell_tray_icon_parent_class)->finalize (object);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
2022-12-22 19:02:28 +00:00
|
|
|
shell_tray_icon_remove_window_actor (ShellTrayIcon *tray_icon)
|
2010-09-08 02:27:08 +00:00
|
|
|
{
|
2022-12-22 19:02:28 +00:00
|
|
|
if (tray_icon->window_actor)
|
|
|
|
{
|
|
|
|
g_clear_signal_handler (&tray_icon->window_actor_destroyed_handler,
|
|
|
|
tray_icon->window_actor);
|
|
|
|
g_clear_object (&tray_icon->window_actor);
|
|
|
|
}
|
2010-09-08 02:27:08 +00:00
|
|
|
|
2022-12-22 19:02:28 +00:00
|
|
|
clutter_clone_set_source (CLUTTER_CLONE (tray_icon), NULL);
|
|
|
|
}
|
2010-09-08 02:27:08 +00:00
|
|
|
|
2022-12-22 19:02:28 +00:00
|
|
|
static void
|
|
|
|
shell_tray_icon_window_created_cb (MetaDisplay *display,
|
|
|
|
MetaWindow *window,
|
|
|
|
ShellTrayIcon *tray_icon)
|
|
|
|
{
|
|
|
|
Window xwindow = meta_window_get_xwindow (window);
|
2010-09-08 02:27:08 +00:00
|
|
|
|
2022-12-22 19:02:28 +00:00
|
|
|
if (tray_icon->tray_child &&
|
|
|
|
xwindow == na_xembed_get_socket_window (NA_XEMBED (tray_icon->tray_child)))
|
|
|
|
{
|
|
|
|
ClutterActor *window_actor =
|
|
|
|
CLUTTER_ACTOR (meta_window_get_compositor_private (window));
|
|
|
|
|
|
|
|
clutter_clone_set_source (CLUTTER_CLONE (tray_icon), window_actor);
|
|
|
|
|
|
|
|
/* We want to explicitly clear the clone source when the window
|
|
|
|
actor is destroyed because otherwise we might end up keeping
|
|
|
|
it alive after it has been disposed. Otherwise this can cause
|
|
|
|
a crash if there is a paint after mutter notices that the top
|
|
|
|
level window has been destroyed, which causes it to dispose
|
|
|
|
the window, and before the tray manager notices that the
|
|
|
|
window is gone which would otherwise reset the window and
|
|
|
|
unref the clone */
|
|
|
|
tray_icon->window_actor = g_object_ref (window_actor);
|
|
|
|
tray_icon->window_actor_destroyed_handler =
|
|
|
|
g_signal_connect_swapped (window_actor,
|
|
|
|
"destroy",
|
|
|
|
G_CALLBACK (shell_tray_icon_remove_window_actor),
|
|
|
|
tray_icon);
|
|
|
|
|
|
|
|
/* Hide the original actor otherwise it will appear in the scene
|
|
|
|
as a normal window */
|
|
|
|
clutter_actor_set_opacity (window_actor, 0);
|
|
|
|
|
|
|
|
/* Also make sure it (or any of its children) doesn't block
|
|
|
|
events on wayland */
|
|
|
|
shell_util_set_hidden_from_pick (window_actor, TRUE);
|
|
|
|
|
|
|
|
/* Now that we've found the window we don't need to listen for
|
|
|
|
new windows anymore */
|
|
|
|
g_clear_signal_handler (&tray_icon->window_created_handler,
|
|
|
|
display);
|
|
|
|
}
|
2010-09-08 02:27:08 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
shell_tray_icon_get_property (GObject *object,
|
|
|
|
guint prop_id,
|
|
|
|
GValue *value,
|
|
|
|
GParamSpec *pspec)
|
|
|
|
{
|
|
|
|
ShellTrayIcon *icon = SHELL_TRAY_ICON (object);
|
|
|
|
|
|
|
|
switch (prop_id)
|
|
|
|
{
|
|
|
|
case PROP_PID:
|
2022-12-22 19:02:28 +00:00
|
|
|
g_value_set_uint (value, icon->pid);
|
2010-09-08 02:27:08 +00:00
|
|
|
break;
|
|
|
|
|
|
|
|
case PROP_TITLE:
|
2022-12-22 19:02:28 +00:00
|
|
|
g_value_set_string (value, icon->title);
|
2010-09-08 02:27:08 +00:00
|
|
|
break;
|
|
|
|
|
|
|
|
case PROP_WM_CLASS:
|
2022-12-22 19:02:28 +00:00
|
|
|
g_value_set_string (value, icon->wm_class);
|
2010-09-08 02:27:08 +00:00
|
|
|
break;
|
|
|
|
|
|
|
|
default:
|
|
|
|
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-12-22 19:02:28 +00:00
|
|
|
static void
|
|
|
|
shell_tray_icon_dispose (GObject *object)
|
|
|
|
{
|
|
|
|
ShellTrayIcon *tray_icon = SHELL_TRAY_ICON (object);
|
|
|
|
MetaDisplay *display = shell_global_get_display (shell_global_get ());
|
|
|
|
|
|
|
|
g_clear_signal_handler (&tray_icon->window_created_handler,
|
|
|
|
display);
|
|
|
|
shell_tray_icon_remove_window_actor (tray_icon);
|
|
|
|
|
|
|
|
G_OBJECT_CLASS (shell_tray_icon_parent_class)->dispose (object);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
shell_tray_icon_get_preferred_width (ClutterActor *actor,
|
|
|
|
float for_height,
|
|
|
|
float *min_width_p,
|
|
|
|
float *natural_width_p)
|
|
|
|
{
|
|
|
|
ShellTrayIcon *tray_icon = SHELL_TRAY_ICON (actor);
|
|
|
|
int width;
|
|
|
|
|
|
|
|
na_xembed_get_size (NA_XEMBED (tray_icon->tray_child), &width, NULL);
|
|
|
|
|
|
|
|
*min_width_p = width;
|
|
|
|
*natural_width_p = width;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
shell_tray_icon_get_preferred_height (ClutterActor *actor,
|
|
|
|
float for_width,
|
|
|
|
float *min_height_p,
|
|
|
|
float *natural_height_p)
|
|
|
|
{
|
|
|
|
ShellTrayIcon *tray_icon = SHELL_TRAY_ICON (actor);
|
|
|
|
int height;
|
|
|
|
|
|
|
|
na_xembed_get_size (NA_XEMBED (tray_icon->tray_child), NULL, &height);
|
|
|
|
|
|
|
|
*min_height_p = height;
|
|
|
|
*natural_height_p = height;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
shell_tray_icon_allocate (ClutterActor *actor,
|
|
|
|
const ClutterActorBox *box)
|
|
|
|
{
|
|
|
|
ShellTrayIcon *tray_icon = SHELL_TRAY_ICON (actor);
|
|
|
|
float wx, wy;
|
|
|
|
|
|
|
|
CLUTTER_ACTOR_CLASS (shell_tray_icon_parent_class)->allocate (actor, box);
|
|
|
|
|
|
|
|
/* Find the actor's new coordinates in terms of the stage.
|
|
|
|
*/
|
|
|
|
clutter_actor_get_transformed_position (actor, &wx, &wy);
|
|
|
|
na_xembed_set_root_position (NA_XEMBED (tray_icon->tray_child),
|
|
|
|
(int)(0.5 + wx), (int)(0.5 + wy));
|
|
|
|
}
|
|
|
|
|
2010-09-08 02:27:08 +00:00
|
|
|
static void
|
|
|
|
shell_tray_icon_class_init (ShellTrayIconClass *klass)
|
|
|
|
{
|
|
|
|
GObjectClass *object_class = G_OBJECT_CLASS (klass);
|
2022-12-22 19:02:28 +00:00
|
|
|
ClutterActorClass *actor_class = CLUTTER_ACTOR_CLASS (klass);
|
2010-09-08 02:27:08 +00:00
|
|
|
|
|
|
|
object_class->get_property = shell_tray_icon_get_property;
|
2022-12-22 19:02:28 +00:00
|
|
|
object_class->finalize = shell_tray_icon_finalize;
|
|
|
|
object_class->dispose = shell_tray_icon_dispose;
|
|
|
|
|
|
|
|
actor_class->get_preferred_width = shell_tray_icon_get_preferred_width;
|
|
|
|
actor_class->get_preferred_height = shell_tray_icon_get_preferred_height;
|
|
|
|
actor_class->allocate = shell_tray_icon_allocate;
|
2010-09-08 02:27:08 +00:00
|
|
|
|
|
|
|
g_object_class_install_property (object_class,
|
|
|
|
PROP_PID,
|
|
|
|
g_param_spec_uint ("pid",
|
|
|
|
"PID",
|
|
|
|
"The PID of the icon's application",
|
|
|
|
0, G_MAXUINT, 0,
|
2019-07-25 17:34:19 +00:00
|
|
|
G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
|
2010-09-08 02:27:08 +00:00
|
|
|
g_object_class_install_property (object_class,
|
|
|
|
PROP_TITLE,
|
|
|
|
g_param_spec_string ("title",
|
|
|
|
"Title",
|
|
|
|
"The icon's window title",
|
|
|
|
NULL,
|
2019-07-25 17:34:19 +00:00
|
|
|
G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
|
2010-09-08 02:27:08 +00:00
|
|
|
g_object_class_install_property (object_class,
|
|
|
|
PROP_WM_CLASS,
|
|
|
|
g_param_spec_string ("wm-class",
|
|
|
|
"WM Class",
|
|
|
|
"The icon's window WM_CLASS",
|
|
|
|
NULL,
|
2019-07-25 17:34:19 +00:00
|
|
|
G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
|
2010-09-08 02:27:08 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
shell_tray_icon_init (ShellTrayIcon *icon)
|
|
|
|
{
|
2022-12-22 19:02:28 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
shell_tray_icon_set_child (ShellTrayIcon *tray_icon,
|
|
|
|
NaTrayChild *tray_child)
|
|
|
|
{
|
|
|
|
MetaDisplay *display = shell_global_get_display (shell_global_get ());
|
|
|
|
MetaX11Display *x11_display;
|
|
|
|
Display *xdisplay;
|
|
|
|
Window plug_xid;
|
|
|
|
Atom type;
|
|
|
|
int result, format;
|
|
|
|
gulong nitems, bytes_after, *val = NULL;
|
|
|
|
|
|
|
|
g_return_if_fail (tray_icon != NULL);
|
|
|
|
g_return_if_fail (tray_child != NULL);
|
|
|
|
|
|
|
|
x11_display = meta_display_get_x11_display (display);
|
|
|
|
|
|
|
|
/* We do all this now rather than computing it on the fly later,
|
|
|
|
* because the shell may want to see their values from a
|
|
|
|
* tray-icon-removed signal handler, at which point the plug has
|
|
|
|
* already been removed from the socket.
|
|
|
|
*/
|
|
|
|
|
|
|
|
tray_icon->tray_child = tray_child;
|
|
|
|
|
|
|
|
tray_icon->title = na_tray_child_get_title (tray_icon->tray_child);
|
|
|
|
na_tray_child_get_wm_class (tray_icon->tray_child,
|
|
|
|
NULL, &tray_icon->wm_class);
|
|
|
|
|
|
|
|
plug_xid = na_xembed_get_plug_window (NA_XEMBED (tray_icon->tray_child));
|
|
|
|
|
|
|
|
xdisplay = meta_x11_display_get_xdisplay (x11_display);
|
|
|
|
meta_x11_error_trap_push (x11_display);
|
|
|
|
result = XGetWindowProperty (xdisplay, plug_xid,
|
|
|
|
XInternAtom (xdisplay, "_NET_WM_PID", False),
|
|
|
|
0, G_MAXLONG, False, XA_CARDINAL,
|
|
|
|
&type, &format, &nitems,
|
|
|
|
&bytes_after, (guchar **)&val);
|
|
|
|
|
|
|
|
if (!meta_x11_error_trap_pop_with_return (x11_display) &&
|
|
|
|
result == Success &&
|
|
|
|
type == XA_CARDINAL &&
|
|
|
|
nitems == 1)
|
|
|
|
tray_icon->pid = *val;
|
|
|
|
|
|
|
|
if (val)
|
|
|
|
XFree (val);
|
|
|
|
|
|
|
|
tray_icon->window_created_handler =
|
|
|
|
g_signal_connect (display,
|
|
|
|
"window-created",
|
|
|
|
G_CALLBACK (shell_tray_icon_window_created_cb),
|
|
|
|
tray_icon);
|
2010-09-08 02:27:08 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Public API
|
|
|
|
*/
|
|
|
|
ClutterActor *
|
2022-12-22 19:02:28 +00:00
|
|
|
shell_tray_icon_new (NaTrayChild *tray_child)
|
2010-09-08 02:27:08 +00:00
|
|
|
{
|
2022-12-22 19:02:28 +00:00
|
|
|
ShellTrayIcon *tray_icon;
|
|
|
|
|
|
|
|
g_return_val_if_fail (NA_IS_TRAY_CHILD (tray_child), NULL);
|
2010-09-08 02:27:08 +00:00
|
|
|
|
2022-12-22 19:02:28 +00:00
|
|
|
tray_icon = g_object_new (SHELL_TYPE_TRAY_ICON, NULL);
|
|
|
|
shell_tray_icon_set_child (tray_icon, tray_child);
|
|
|
|
|
|
|
|
return CLUTTER_ACTOR (tray_icon);
|
2010-09-08 02:27:08 +00:00
|
|
|
}
|
2011-01-13 20:04:37 +00:00
|
|
|
|
|
|
|
/**
|
|
|
|
* shell_tray_icon_click:
|
|
|
|
* @icon: a #ShellTrayIcon
|
|
|
|
* @event: the #ClutterEvent triggering the fake click
|
|
|
|
*
|
|
|
|
* Fakes a press and release on @icon. @event must be a
|
2014-01-04 17:14:25 +00:00
|
|
|
* %CLUTTER_BUTTON_RELEASE, %CLUTTER_KEY_PRESS or %CLUTTER_KEY_RELEASE event.
|
|
|
|
* Its relevant details will be passed on to the icon, but its
|
|
|
|
* coordinates will be ignored; the click is
|
2011-01-13 20:04:37 +00:00
|
|
|
* always made on the center of @icon.
|
|
|
|
*/
|
|
|
|
void
|
|
|
|
shell_tray_icon_click (ShellTrayIcon *icon,
|
|
|
|
ClutterEvent *event)
|
|
|
|
{
|
2022-12-22 19:02:28 +00:00
|
|
|
MetaDisplay *display = shell_global_get_display (shell_global_get ());
|
|
|
|
MetaX11Display *x11_display;
|
2012-11-02 15:09:04 +00:00
|
|
|
XKeyEvent xkevent;
|
2011-01-13 20:04:37 +00:00
|
|
|
XButtonEvent xbevent;
|
|
|
|
XCrossingEvent xcevent;
|
|
|
|
Display *xdisplay;
|
|
|
|
Window xwindow, xrootwindow;
|
2012-11-02 15:09:04 +00:00
|
|
|
ClutterEventType event_type = clutter_event_type (event);
|
2022-12-22 19:02:28 +00:00
|
|
|
int width, height;
|
2011-01-13 20:04:37 +00:00
|
|
|
|
2012-11-02 15:09:04 +00:00
|
|
|
g_return_if_fail (event_type == CLUTTER_BUTTON_RELEASE ||
|
2014-01-04 17:14:25 +00:00
|
|
|
event_type == CLUTTER_KEY_PRESS ||
|
2012-11-02 15:09:04 +00:00
|
|
|
event_type == CLUTTER_KEY_RELEASE);
|
2011-01-13 20:04:37 +00:00
|
|
|
|
2022-12-22 19:02:28 +00:00
|
|
|
x11_display = meta_display_get_x11_display (display);
|
|
|
|
if (!x11_display)
|
|
|
|
return;
|
|
|
|
|
|
|
|
xwindow = na_xembed_get_plug_window (NA_XEMBED (icon->tray_child));
|
|
|
|
if (xwindow == None)
|
2016-09-07 11:25:24 +00:00
|
|
|
{
|
|
|
|
g_warning ("shell tray: plug window is gone");
|
|
|
|
return;
|
|
|
|
}
|
2019-02-25 15:01:57 +00:00
|
|
|
|
2022-12-22 19:02:28 +00:00
|
|
|
na_xembed_get_size (NA_XEMBED (icon->tray_child), &width, &height);
|
2019-02-25 15:01:57 +00:00
|
|
|
|
2022-12-22 19:02:28 +00:00
|
|
|
meta_x11_error_trap_push (x11_display);
|
2011-01-13 20:04:37 +00:00
|
|
|
|
2022-12-22 19:02:28 +00:00
|
|
|
xdisplay = meta_x11_display_get_xdisplay (x11_display);
|
|
|
|
xrootwindow = XDefaultRootWindow (xdisplay);
|
2019-02-25 15:01:57 +00:00
|
|
|
|
2011-01-13 20:04:37 +00:00
|
|
|
/* First make the icon believe the pointer is inside it */
|
|
|
|
xcevent.type = EnterNotify;
|
|
|
|
xcevent.window = xwindow;
|
|
|
|
xcevent.root = xrootwindow;
|
|
|
|
xcevent.subwindow = None;
|
|
|
|
xcevent.time = clutter_event_get_time (event);
|
2022-12-22 19:02:28 +00:00
|
|
|
xcevent.x = width / 2;
|
|
|
|
xcevent.y = height / 2;
|
|
|
|
xcevent.x_root = xcevent.x;
|
|
|
|
xcevent.y_root = xcevent.y;
|
2011-01-13 20:04:37 +00:00
|
|
|
xcevent.mode = NotifyNormal;
|
|
|
|
xcevent.detail = NotifyNonlinear;
|
|
|
|
xcevent.same_screen = True;
|
|
|
|
XSendEvent (xdisplay, xwindow, False, 0, (XEvent *)&xcevent);
|
|
|
|
|
|
|
|
/* Now do the click */
|
2012-11-02 15:09:04 +00:00
|
|
|
if (event_type == CLUTTER_BUTTON_RELEASE)
|
|
|
|
{
|
|
|
|
xbevent.window = xwindow;
|
|
|
|
xbevent.root = xrootwindow;
|
|
|
|
xbevent.subwindow = None;
|
|
|
|
xbevent.time = xcevent.time;
|
|
|
|
xbevent.x = xcevent.x;
|
|
|
|
xbevent.y = xcevent.y;
|
|
|
|
xbevent.x_root = xcevent.x_root;
|
|
|
|
xbevent.y_root = xcevent.y_root;
|
|
|
|
xbevent.state = clutter_event_get_state (event);
|
|
|
|
xbevent.same_screen = True;
|
|
|
|
xbevent.type = ButtonPress;
|
|
|
|
xbevent.button = clutter_event_get_button (event);
|
|
|
|
XSendEvent (xdisplay, xwindow, False, 0, (XEvent *)&xbevent);
|
|
|
|
|
|
|
|
xbevent.type = ButtonRelease;
|
|
|
|
XSendEvent (xdisplay, xwindow, False, 0, (XEvent *)&xbevent);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
xkevent.window = xwindow;
|
|
|
|
xkevent.root = xrootwindow;
|
|
|
|
xkevent.subwindow = None;
|
|
|
|
xkevent.time = xcevent.time;
|
|
|
|
xkevent.x = xcevent.x;
|
|
|
|
xkevent.y = xcevent.y;
|
|
|
|
xkevent.x_root = xcevent.x_root;
|
|
|
|
xkevent.y_root = xcevent.y_root;
|
|
|
|
xkevent.state = clutter_event_get_state (event);
|
|
|
|
xkevent.same_screen = True;
|
|
|
|
xkevent.keycode = clutter_event_get_key_code (event);
|
|
|
|
|
2014-01-04 17:14:25 +00:00
|
|
|
xkevent.type = KeyPress;
|
2012-11-02 15:09:04 +00:00
|
|
|
XSendEvent (xdisplay, xwindow, False, 0, (XEvent *)&xkevent);
|
2014-01-04 17:14:25 +00:00
|
|
|
|
|
|
|
if (event_type == CLUTTER_KEY_RELEASE)
|
|
|
|
{
|
|
|
|
/* If the application takes a grab on KeyPress, we don't
|
|
|
|
* want to send it a KeyRelease. There's no good way of
|
|
|
|
* knowing whether a tray icon will take a grab, so just
|
|
|
|
* assume it does, and don't send the KeyRelease. That might
|
|
|
|
* make the tracking for key events messed up if it doesn't take
|
|
|
|
* a grab, but the tray icon won't get key focus in normal cases,
|
|
|
|
* so let's hope this isn't too damaging...
|
|
|
|
*/
|
|
|
|
xkevent.type = KeyRelease;
|
|
|
|
XSendEvent (xdisplay, xwindow, False, 0, (XEvent *)&xkevent);
|
|
|
|
}
|
2012-11-02 15:09:04 +00:00
|
|
|
}
|
2011-01-13 20:04:37 +00:00
|
|
|
|
|
|
|
/* And move the pointer back out */
|
|
|
|
xcevent.type = LeaveNotify;
|
|
|
|
XSendEvent (xdisplay, xwindow, False, 0, (XEvent *)&xcevent);
|
|
|
|
|
2022-12-22 19:02:28 +00:00
|
|
|
meta_x11_error_trap_pop (x11_display);
|
2011-01-13 20:04:37 +00:00
|
|
|
}
|