537 lines
18 KiB
C
537 lines
18 KiB
C
/* -------------------------------
|
|
* vim:tabstop=4:shiftwidth=4
|
|
* xembed.c
|
|
* Tue, 24 Aug 2004 12:05:38 +0700
|
|
* -------------------------------
|
|
* XEMBED protocol implementation
|
|
* -------------------------------*/
|
|
|
|
/* Currently broken:
|
|
* - XEMBED_{ACTIVATE,REGISTER,UNREGISTER}_ACCELERATOR
|
|
* - XEMBED_REQUEST_FOCUS */
|
|
|
|
#include <X11/X.h>
|
|
#include <X11/Xmd.h>
|
|
#include <X11/Xlib.h>
|
|
#include <X11/Xutil.h>
|
|
|
|
#include "common.h"
|
|
#include "debug.h"
|
|
#include "xembed.h"
|
|
#include "xutils.h"
|
|
#include "wmh.h"
|
|
|
|
#include "list.h"
|
|
|
|
/* Internal return codes */
|
|
#define XEMBED_RESULT_OK 0
|
|
#define XEMBED_RESULT_UNSUPPORTED 1
|
|
#define XEMBED_RESULT_X11ERROR 2
|
|
|
|
/* XEMBED messages */
|
|
#define XEMBED_EMBEDDED_NOTIFY 0
|
|
#define XEMBED_WINDOW_ACTIVATE 1
|
|
#define XEMBED_WINDOW_DEACTIVATE 2
|
|
#define XEMBED_REQUEST_FOCUS 3
|
|
#define XEMBED_FOCUS_IN 4
|
|
#define XEMBED_FOCUS_OUT 5
|
|
#define XEMBED_FOCUS_NEXT 6
|
|
#define XEMBED_FOCUS_PREV 7
|
|
/* 8-9 were used for XEMBED_GRAB_KEY/XEMBED_UNGRAB_KEY */
|
|
#define XEMBED_MODALITY_ON 10
|
|
#define XEMBED_MODALITY_OFF 11
|
|
#define XEMBED_REGISTER_ACCELERATOR 12
|
|
#define XEMBED_UNREGISTER_ACCELERATOR 13
|
|
#define XEMBED_ACTIVATE_ACCELERATOR 14
|
|
|
|
/* Details for XEMBED_FOCUS_IN */
|
|
#define XEMBED_FOCUS_CURRENT 0
|
|
#define XEMBED_FOCUS_FIRST 1
|
|
#define XEMBED_FOCUS_LAST 2
|
|
|
|
/* Modifiers field for XEMBED_REGISTER_ACCELERATOR */
|
|
#define XEMBED_MODIFIER_SHIFT (1 << 0)
|
|
#define XEMBED_MODIFIER_CONTROL (1 << 1)
|
|
#define XEMBED_MODIFIER_ALT (1 << 2)
|
|
#define XEMBED_MODIFIER_SUPER (1 << 3)
|
|
#define XEMBED_MODIFIER_HYPER (1 << 4)
|
|
|
|
/* Flags for XEMBED_ACTIVATE_ACCELERATOR */
|
|
#define XEMBED_ACCELERATOR_OVERLOADED (1 << 0)
|
|
|
|
/* Directions for focusing */
|
|
#define XEMBED_DIRECTION_DEFAULT 0
|
|
#define XEMBED_DIRECTION_UP_DOWN 1
|
|
#define XEMBED_DIRECTION_LEFT_RIGHT 2
|
|
|
|
/* Flags for _XEMBED_INFO */
|
|
#define XEMBED_MAPPED (1 << 0)
|
|
|
|
/* Structure to hold XEMBED accelerator data */
|
|
struct XEMBEDAccel {
|
|
struct XEMBEDAccel *next, *prev;
|
|
int overloaded; /* Is this accelerator overloaded? */
|
|
long id; /* Accelerator Id */
|
|
long symb; /* Symbol */
|
|
long mods; /* Modifiers */
|
|
};
|
|
|
|
/* Shortcuts for sending XEMBED messages */
|
|
#define xembed_send_msg(dpy, dst, timestamp, msg, detail, data1, data2) \
|
|
x11_send_client_msg32(dpy, dst, dst, tray_data.xembed_data.xa_xembed, timestamp, msg, detail, data1, data2)
|
|
|
|
#define xembed_send_embedded_notify(dpy, src, dst, timestamp) \
|
|
xembed_send_msg(dpy, dst, timestamp, XEMBED_EMBEDDED_NOTIFY, 0, src, 0)
|
|
|
|
#define xembed_send_window_activate(dpy, dst, timestamp) \
|
|
xembed_send_msg(dpy, dst, timestamp, XEMBED_WINDOW_ACTIVATE, 0, 0, 0)
|
|
|
|
#define xembed_send_window_deactivate(dpy, dst, timestamp) \
|
|
xembed_send_msg(dpy, dst, timestamp, XEMBED_WINDOW_DEACTIVATE, 0, 0, 0)
|
|
|
|
#define xembed_send_focus_in(dpy, dst, focus, timestamp) \
|
|
xembed_send_msg(dpy, dst, timestamp, XEMBED_FOCUS_IN, focus, 0, 0)
|
|
|
|
#define xembed_send_focus_out(dpy, dst, timestamp) \
|
|
xembed_send_msg(dpy, dst, timestamp, XEMBED_FOCUS_OUT, 0, 0, 0)
|
|
|
|
#define xembed_send_activate_accelerator(dpy, dst, timestamp, id, overloaded) \
|
|
xembed_send_msg(dpy, dst, timestamp, XEMBED_ACTIVATE_ACCELERATOR, id, overloaded, 0)
|
|
|
|
/* Retrieve XEMBED data for the given icon */
|
|
int xembed_retrieve_data(struct TrayIcon *ti);
|
|
/* Post icon XEMBED data to window property */
|
|
int xembed_post_data(struct TrayIcon *ti);
|
|
/* Returns the next icon in tab chain */
|
|
struct TrayIcon *xembed_next();
|
|
/* Returns the previous icon in tab chain */
|
|
struct TrayIcon *xembed_prev();
|
|
/* XEMBED event handler */
|
|
int xembed_process_kbd_event(XKeyEvent xkey);
|
|
/* Register new XEMBED accelerator */
|
|
void xembed_add_accel(long id, long symb, long mods);
|
|
/* Delete previously registered XEMBED accelerator */
|
|
void xembed_del_accel(long id);
|
|
/* Activate previously registered XEMBED accelerator */
|
|
void xembed_act_accel(struct XEMBEDAccel *accel);
|
|
/* Switch XEMBED focus to the specified icon */
|
|
void xembed_switch_focus_to(struct TrayIcon *tgt, long focus);
|
|
/* Broadcast the focus change to all icons */
|
|
void xembed_track_focus_change(int activate);
|
|
/* Process XEMBED message */
|
|
void xembed_message(XClientMessageEvent ev);
|
|
/* Tries to request focus from WM */
|
|
void xembed_request_focus_from_wm();
|
|
|
|
void xembed_init()
|
|
{
|
|
/* 1. Initialize data structures */
|
|
tray_data.xembed_data.window_has_focus = False;
|
|
tray_data.xembed_data.focus_requested = False;
|
|
tray_data.xembed_data.current = NULL;
|
|
tray_data.xembed_data.accels = NULL;
|
|
tray_data.xembed_data.timestamp = CurrentTime;
|
|
tray_data.xembed_data.xa_xembed = XInternAtom(tray_data.dpy, "_XEMBED", False);
|
|
tray_data.xembed_data.xa_xembed_info = XInternAtom(tray_data.dpy, "_XEMBED_INFO", False);
|
|
/* 2. Create focus proxy (see XEMBED spec) */
|
|
tray_data.xembed_data.focus_proxy =
|
|
XCreateSimpleWindow(tray_data.dpy, tray_data.tray, -1, -1, 1, 1, 0, 0, 0);
|
|
XSelectInput(tray_data.dpy, tray_data.xembed_data.focus_proxy, FocusChangeMask | KeyPressMask | KeyReleaseMask);
|
|
XMapRaised(tray_data.dpy, tray_data.xembed_data.focus_proxy);
|
|
if (!x11_ok()) DIE(("could not create focus proxy\n"));
|
|
LOG_TRACE(("created focus proxy, wid=0x%x\n", tray_data.xembed_data.focus_proxy));
|
|
}
|
|
|
|
void xembed_handle_event(XEvent ev)
|
|
{
|
|
switch (ev.type) {
|
|
case FocusOut:
|
|
/* Broadcast that the focus has left tray window */
|
|
LOG_TRACE(("FocusOut 0x%x\n", ev.xfocus.window));
|
|
if (ev.xfocus.window == tray_data.xembed_data.focus_proxy)
|
|
xembed_track_focus_change(False);
|
|
break;
|
|
case ClientMessage:
|
|
/* Handle XEMBED-related messages */
|
|
if (ev.xclient.message_type == tray_data.xembed_data.xa_xembed) {
|
|
xembed_message(ev.xclient);
|
|
} else if (ev.xclient.message_type == tray_data.xa_tray_opcode) {
|
|
/* we peek at _NET_SYSTEM_TRAY_OPCODE messages
|
|
* to obtain proper timestamp for embedding */
|
|
tray_data.xembed_data.timestamp = ev.xclient.data.l[0];
|
|
if (tray_data.xembed_data.timestamp == CurrentTime)
|
|
tray_data.xembed_data.timestamp = x11_get_server_timestamp(tray_data.dpy, tray_data.tray);
|
|
} else if (ev.xclient.message_type == tray_data.xa_wm_protocols &&
|
|
ev.xclient.data.l[0] == tray_data.xa_wm_take_focus &&
|
|
tray_data.xembed_data.focus_requested)
|
|
{
|
|
XSetInputFocus(tray_data.dpy, tray_data.xembed_data.focus_proxy, RevertToParent, ev.xclient.data.l[1]);
|
|
if (!x11_ok()) DIE_IE(("Could not set focus to XEMBED focus proxy\n"));
|
|
LOG_TRACE(("focus set to focus proxy\n"));
|
|
xembed_track_focus_change(True);
|
|
tray_data.xembed_data.focus_requested = False;
|
|
}
|
|
break;
|
|
case KeyRelease:
|
|
case KeyPress:
|
|
/* Propagate key events to currently focused icon */
|
|
tray_data.xembed_data.timestamp = ev.xkey.time;
|
|
if (ev.type == KeyRelease && xembed_process_kbd_event(ev.xkey))
|
|
break;
|
|
if (tray_data.xembed_data.current != NULL) {
|
|
int rc;
|
|
LOG_TRACE(("current icon accepts_focus: %d\n", tray_data.xembed_data.current->is_xembed_accepts_focus));
|
|
rc = XSendEvent(tray_data.dpy, tray_data.xembed_data.current->wid, False, NoEventMask, &ev);
|
|
if (!x11_ok() || rc == 0) {
|
|
tray_data.xembed_data.current->is_invalid = True;
|
|
return;
|
|
}
|
|
LOG_TRACE(("sent key event to 0x%x\n", tray_data.xembed_data.current->wid));
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
|
|
int xembed_check_support(struct TrayIcon *ti)
|
|
{
|
|
int rc = xembed_retrieve_data(ti);
|
|
ti->is_xembed_supported = (rc == XEMBED_RESULT_OK);
|
|
return rc != XEMBED_RESULT_X11ERROR;
|
|
}
|
|
|
|
int xembed_get_mapped_state(struct TrayIcon *ti)
|
|
{
|
|
/* It is OK to retrieve data each time this function
|
|
* is called, since there is some overhead only during
|
|
* initialization, when xembed_retrieve_data is called 2
|
|
* times in a row(). */
|
|
int rc = xembed_retrieve_data(ti);
|
|
if (ti->is_xembed_supported && rc == XEMBED_RESULT_OK)
|
|
return ((ti->xembed_data[1] & XEMBED_MAPPED) != 0);
|
|
else {
|
|
ti->is_xembed_supported = False;
|
|
ti->is_invalid = (rc == XEMBED_RESULT_X11ERROR);
|
|
return False;
|
|
}
|
|
}
|
|
|
|
int xembed_set_mapped_state(struct TrayIcon *ti, int state)
|
|
{
|
|
if (!ti->is_xembed_supported) return FAILURE;
|
|
if (state)
|
|
ti->xembed_data[1] |= XEMBED_MAPPED;
|
|
else
|
|
ti->xembed_data[1] &= ~XEMBED_MAPPED;
|
|
return xembed_post_data(ti);
|
|
}
|
|
|
|
int xembed_embed(struct TrayIcon *ti)
|
|
{
|
|
/* if XEMBED is not supported, do nothing */
|
|
if (!ti->is_xembed_supported) return SUCCESS;
|
|
/* By default, consider that all icons accept focus */
|
|
ti->is_xembed_accepts_focus = True;
|
|
/* Send notification */
|
|
if (!xembed_send_embedded_notify(tray_data.dpy, tray_data.tray, ti->wid, tray_data.xembed_data.timestamp))
|
|
return FAILURE;
|
|
ti->xembed_last_timestamp = tray_data.xembed_data.timestamp;
|
|
ti->xembed_last_msgid = XEMBED_EMBEDDED_NOTIFY;
|
|
if (tray_data.xembed_data.current == NULL) {
|
|
/* No icon has focus. Set focus to this one */
|
|
if (!xembed_send_focus_in(tray_data.dpy, ti->wid, XEMBED_FOCUS_FIRST, tray_data.xembed_data.timestamp))
|
|
return FAILURE;
|
|
tray_data.xembed_data.current = ti;
|
|
}
|
|
/* Send activation message if tray window has focus */
|
|
if (tray_data.xembed_data.window_has_focus)
|
|
return xembed_send_window_activate(tray_data.dpy, ti->wid, tray_data.xembed_data.timestamp);
|
|
return SUCCESS;
|
|
}
|
|
|
|
int xembed_unembed(struct TrayIcon *ti)
|
|
{
|
|
struct TrayIcon *tmp;
|
|
tray_data.xembed_data.timestamp = x11_get_server_timestamp(tray_data.dpy, tray_data.tray);
|
|
if (ti == tray_data.xembed_data.current) {
|
|
/* Currently focused icon is being unembedded,
|
|
* move focus to the next icon. */
|
|
tmp = xembed_next();
|
|
if (tmp == ti || tmp->is_xembed_accepts_focus == False) {
|
|
xembed_switch_focus_to(NULL, 0);
|
|
} else {
|
|
xembed_switch_focus_to(tmp, XEMBED_FOCUS_FIRST);
|
|
}
|
|
}
|
|
return SUCCESS;
|
|
}
|
|
|
|
/*********** implementation level ***************/
|
|
void xembed_switch_focus_to(struct TrayIcon *tgt, long focus)
|
|
{
|
|
/* 1. Send "focus out" message to the currently focused icon */
|
|
if (tray_data.xembed_data.current != NULL) {
|
|
LOG_TRACE(("XEMBED focus was removed from icon 0x%x (pointer %p)\n", tray_data.xembed_data.current->wid, tray_data.xembed_data.current));
|
|
xembed_send_focus_out(tray_data.dpy, tray_data.xembed_data.current->wid, tray_data.xembed_data.timestamp);
|
|
}
|
|
/* 2. Send "focus in" message to the icon to be focused */
|
|
if (tgt != NULL) {
|
|
xembed_send_focus_in(tray_data.dpy, tgt->wid, focus, tray_data.xembed_data.timestamp);
|
|
LOG_TRACE(("XEMBED focus was set to icon 0x%x (pointer %p)\n", tgt->wid, tgt));
|
|
} else {
|
|
LOG_TRACE(("XEMBED focus was unset\n"));
|
|
}
|
|
tray_data.xembed_data.current = tgt;
|
|
}
|
|
|
|
static int activate = 0;
|
|
|
|
int broadcast_activate_msg(struct TrayIcon *ti)
|
|
{
|
|
if (activate)
|
|
xembed_send_window_activate(tray_data.dpy, ti->wid, tray_data.xembed_data.timestamp);
|
|
else
|
|
xembed_send_window_deactivate(tray_data.dpy, ti->wid, tray_data.xembed_data.timestamp);
|
|
return NO_MATCH;
|
|
}
|
|
|
|
void xembed_track_focus_change(int in)
|
|
{
|
|
if (tray_data.xembed_data.window_has_focus == in) return;
|
|
tray_data.xembed_data.window_has_focus = in;
|
|
activate = in;
|
|
icon_list_forall(&broadcast_activate_msg);
|
|
LOG_TRACE(("XEMBED focus is %s\n", in ? "ON" : "OFF"));
|
|
}
|
|
|
|
void xembed_message(XClientMessageEvent ev)
|
|
{
|
|
long msgid;
|
|
LOG_TRACE(("this is an _XEMBED message, window: 0x%x, timestamp: %u, opcode: %u, \ndetail: 0x%x, data1 = 0x%x, data2 = 0x%x\n",
|
|
ev.window, ev.data.l[0], ev.data.l[1], ev.data.l[2], ev.data.l[3], ev.data.l[4]));
|
|
#ifdef DEBUG
|
|
if (tray_data.xembed_data.current != NULL)
|
|
LOG_TRACE(("XEMBED focus is in window 0x%x (pointer %p)\n", tray_data.xembed_data.current->wid, tray_data.xembed_data.current));
|
|
else
|
|
LOG_TRACE(("XEMBED focus is unset\n"));
|
|
#endif
|
|
if (ev.window != tray_data.tray) {
|
|
LOG_TRACE(("inoring _XEMBED message to some other window\n"));
|
|
return;
|
|
}
|
|
/* Update timestamp if necessary */
|
|
if (ev.data.l[0] == CurrentTime)
|
|
ev.data.l[0] = x11_get_server_timestamp(tray_data.dpy, tray_data.tray);
|
|
tray_data.xembed_data.timestamp = ev.data.l[0];
|
|
msgid = ev.data.l[1];
|
|
LOG_TRACE(("_XEMBED message %u\n", msgid));
|
|
switch (msgid) {
|
|
case XEMBED_REQUEST_FOCUS:
|
|
xembed_request_focus_from_wm();
|
|
break;
|
|
case XEMBED_FOCUS_NEXT:
|
|
case XEMBED_FOCUS_PREV:
|
|
if (tray_data.xembed_data.current != NULL) {
|
|
struct TrayIcon *old_focus, *new_focus;
|
|
old_focus = tray_data.xembed_data.current;
|
|
new_focus = (msgid == XEMBED_FOCUS_NEXT) ?
|
|
xembed_next():
|
|
xembed_prev();
|
|
if (new_focus->is_xembed_accepts_focus) {
|
|
/* If the last message for the new focus target was focus_{next,prev} and
|
|
* it has the same timestamp as the current message, it is likely that
|
|
* the corresponding icon does not want to be focused at all. So mark it
|
|
* as not accepting focus. */
|
|
if (new_focus->xembed_last_timestamp == tray_data.xembed_data.timestamp &&
|
|
(new_focus->xembed_last_msgid == XEMBED_FOCUS_NEXT ||
|
|
new_focus->xembed_last_msgid == XEMBED_FOCUS_PREV))
|
|
{
|
|
new_focus->is_xembed_accepts_focus = False;
|
|
new_focus = False;
|
|
}
|
|
old_focus->xembed_last_timestamp = tray_data.xembed_data.timestamp;
|
|
old_focus->xembed_last_msgid = msgid;
|
|
} else
|
|
new_focus = NULL;
|
|
xembed_switch_focus_to(new_focus, (msgid == XEMBED_FOCUS_NEXT) ?
|
|
XEMBED_FOCUS_FIRST :
|
|
XEMBED_FOCUS_LAST);
|
|
}
|
|
break;
|
|
case XEMBED_REGISTER_ACCELERATOR:
|
|
xembed_add_accel(ev.data.l[2], ev.data.l[3], ev.data.l[4]);
|
|
break;
|
|
case XEMBED_UNREGISTER_ACCELERATOR:
|
|
xembed_del_accel(ev.data.l[2]);
|
|
break;
|
|
default:
|
|
LOG_TRACE(("Unhandled _XEMBED message, id = %d\n", ev.data.l[1]));
|
|
break;
|
|
}
|
|
}
|
|
|
|
void xembed_add_accel(long id, long symb, long mods)
|
|
{
|
|
struct XEMBEDAccel *xaccel, *tmp;
|
|
xaccel = (struct XEMBEDAccel *) malloc(sizeof(struct XEMBEDAccel));
|
|
if (xaccel == NULL) {
|
|
LOG_ERR_OOM(("Could not register new XEMBED accelerator\n"));
|
|
return;
|
|
}
|
|
xaccel->id = id;
|
|
xaccel->symb = symb;
|
|
xaccel->mods = mods;
|
|
xaccel->overloaded = 0;
|
|
/* Check if there are already registered accelerators that are overloaded
|
|
* by this one */
|
|
for (tmp = tray_data.xembed_data.accels; tmp != NULL; tmp = tmp->next)
|
|
if (tmp->symb == symb && tmp->mods == mods) {
|
|
xaccel->overloaded++;
|
|
tmp->overloaded++;
|
|
}
|
|
LIST_ADD_ITEM(tray_data.xembed_data.accels, xaccel);
|
|
LOG_TRACE(("added new XEMBED accelerator: id=0x%x, sym=0x%x, mods=0x%x, overloaded=%d\n",
|
|
id, symb, mods, xaccel->overloaded));
|
|
}
|
|
|
|
void xembed_del_accel(long id)
|
|
{
|
|
struct XEMBEDAccel *tmp, *tgt = NULL;
|
|
for (tmp = tray_data.xembed_data.accels; tmp != NULL; tmp = tmp->next)
|
|
if (tmp->id == id) {
|
|
tgt = tmp;
|
|
return;
|
|
}
|
|
if (tgt == NULL) {
|
|
LOG_TRACE(("refusing to remove unregistered XEMBED accelerator\n"));
|
|
return;
|
|
}
|
|
/* Update overloaded status of the remaining accelerators */
|
|
for (tmp = tray_data.xembed_data.accels; tmp != NULL; tmp = tmp->next)
|
|
if (tmp->symb == tgt->symb && tmp->mods == tgt->mods)
|
|
tmp->overloaded--;
|
|
LIST_DEL_ITEM(tray_data.xembed_data.accels, tgt);
|
|
LOG_TRACE(("removed XEMBED accelator id=0x%x", tgt->id));
|
|
free(tgt);
|
|
}
|
|
|
|
static struct XEMBEDAccel *cur_accel;
|
|
|
|
int xembed_act_accel_helper(struct TrayIcon *ti)
|
|
{
|
|
xembed_send_activate_accelerator(tray_data.dpy,
|
|
ti->wid,
|
|
tray_data.xembed_data.timestamp,
|
|
cur_accel->id, cur_accel->overloaded ? 1 : 0);
|
|
return NO_MATCH;
|
|
}
|
|
|
|
void xembed_act_accel(struct XEMBEDAccel *accel)
|
|
{
|
|
LOG_TRACE(("activating XEMBED accelerator: id=0x%x (symb=0x%x, mods=0x%x)\n", accel->id, accel->symb, accel->mods));
|
|
cur_accel = accel;
|
|
icon_list_forall(&xembed_act_accel_helper);
|
|
}
|
|
|
|
int xembed_process_kbd_event(XKeyEvent xkey)
|
|
{
|
|
struct XEMBEDAccel *tmp;
|
|
int hits = 0;
|
|
KeySym keysym;
|
|
static char buf[20];
|
|
XLookupString(&xkey, buf, 20, &keysym, NULL);
|
|
LOG_TRACE(("Key event (type=%d) with keycode=0x%x, symb=0x%x, state=0x%x\n", xkey.type, xkey.keycode, keysym, xkey.state));
|
|
for (tmp = tray_data.xembed_data.accels; tmp != NULL; tmp = tmp->next)
|
|
if (tmp->symb == keysym && tmp->mods == xkey.state) {
|
|
xembed_act_accel(tmp);
|
|
hits = 1;
|
|
}
|
|
return hits;
|
|
}
|
|
|
|
struct TrayIcon *xembed_next()
|
|
{
|
|
struct TrayIcon *tmp, *blocker;
|
|
tmp = tray_data.xembed_data.current != NULL ? tray_data.xembed_data.current : NULL;
|
|
blocker = tmp != NULL ? tmp : icon_list_next(NULL);
|
|
do {
|
|
tmp = icon_list_next(tmp);
|
|
} while ((!tmp->is_xembed_supported || !tmp->is_xembed_accepts_focus) && tmp != blocker);
|
|
return tmp;
|
|
}
|
|
|
|
struct TrayIcon *xembed_prev()
|
|
{
|
|
struct TrayIcon *tmp, *blocker;
|
|
tmp = tray_data.xembed_data.current != NULL ? tray_data.xembed_data.current : NULL;
|
|
blocker = tmp != NULL ? tmp : icon_list_prev(tmp);
|
|
do {
|
|
tmp = icon_list_prev(tmp);
|
|
} while ((!tmp->is_xembed_supported || !tmp->is_xembed_accepts_focus) && tmp != blocker);
|
|
return tmp;
|
|
}
|
|
|
|
int xembed_retrieve_data(struct TrayIcon *ti)
|
|
{
|
|
Atom act_type;
|
|
int act_fmt;
|
|
unsigned long nitems, bytesafter, *data;
|
|
unsigned char *tmpdata;
|
|
int rc;
|
|
/* NOTE: x11_get_win_prop32 is not used since we need to distinguish between
|
|
* X11 errors and absence of the property */
|
|
x11_ok();
|
|
|
|
rc = XGetWindowProperty(tray_data.dpy,
|
|
ti->wid,
|
|
tray_data.xembed_data.xa_xembed_info,
|
|
0,
|
|
2,
|
|
False,
|
|
tray_data.xembed_data.xa_xembed_info,
|
|
&act_type,
|
|
&act_fmt,
|
|
&nitems,
|
|
&bytesafter,
|
|
&tmpdata);
|
|
if (rc != Success)
|
|
return XEMBED_RESULT_X11ERROR;
|
|
rc = (act_type == tray_data.xembed_data.xa_xembed_info && nitems == 2);
|
|
if (rc) {
|
|
data = (unsigned long*) tmpdata;
|
|
ti->xembed_data[0] = data[0];
|
|
ti->xembed_data[1] = data[1];
|
|
}
|
|
if (nitems && tmpdata != NULL) XFree(tmpdata);
|
|
return rc ? XEMBED_RESULT_OK : XEMBED_RESULT_UNSUPPORTED;
|
|
}
|
|
|
|
int xembed_post_data(struct TrayIcon *ti)
|
|
{
|
|
if (!ti->is_xembed_supported) return XEMBED_RESULT_UNSUPPORTED;
|
|
XChangeProperty(tray_data.dpy,
|
|
ti->wid,
|
|
tray_data.xembed_data.xa_xembed_info,
|
|
tray_data.xembed_data.xa_xembed_info,
|
|
32,
|
|
PropModeReplace,
|
|
(unsigned char *) ti->xembed_data,
|
|
2);
|
|
return x11_ok() ? XEMBED_RESULT_OK : XEMBED_RESULT_X11ERROR;
|
|
}
|
|
|
|
void xembed_request_focus_from_wm()
|
|
{
|
|
if (!tray_data.is_reparented) {
|
|
x11_send_client_msg32(tray_data.dpy,
|
|
DefaultRootWindow(tray_data.dpy),
|
|
tray_data.tray,
|
|
XInternAtom(tray_data.dpy, "_NET_ACTIVE_WINDOW", True),
|
|
1, /* Request is from application */
|
|
x11_get_server_timestamp(tray_data.dpy, tray_data.tray), /* Timestamp */
|
|
0, /* None window is focused current (?) */
|
|
0, /* Unused */
|
|
0);/* Unused */
|
|
tray_data.xembed_data.focus_requested = True;
|
|
}
|
|
}
|
|
|