* 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, write to the
- * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
- * Boston, MA 02111-1307, USA.
+ * License along with this library. If not, see <http://www.gnu.org/licenses/>.
*/
/* Portions of code in this file are based on code from Xlib
*/
in this Software without prior written authorization from The Open Group.
*/
-#include <X11/Xlibint.h>
+#include "config.h"
+
#include "gdkasync.h"
-#include "gdkx.h"
+#include "gdkprivate-x11.h"
+
+#ifdef NEED_XIPROTO_H_FOR_XREPLY
+#include <X11/extensions/XIproto.h>
+#endif
+
+#include <X11/Xlibint.h>
+
+typedef struct _ChildInfoChildState ChildInfoChildState;
+typedef struct _ChildInfoState ChildInfoState;
+typedef struct _ListChildrenState ListChildrenState;
+typedef struct _SendEventState SendEventState;
typedef struct _SetInputFocusState SetInputFocusState;
+typedef struct _RoundtripState RoundtripState;
-struct _SetInputFocusState
+typedef enum {
+ CHILD_INFO_GET_PROPERTY,
+ CHILD_INFO_GET_WA,
+ CHILD_INFO_GET_GEOMETRY
+} ChildInfoReq;
+
+struct _ChildInfoChildState
+{
+ gulong seq[3];
+};
+
+struct _ChildInfoState
+{
+ gboolean get_wm_state;
+ Window *children;
+ guint nchildren;
+ GdkChildInfoX11 *child_info;
+ ChildInfoChildState *child_states;
+
+ guint current_child;
+ guint n_children_found;
+ gint current_request;
+ gboolean have_error;
+ gboolean child_has_error;
+};
+
+struct _ListChildrenState
+{
+ Display *dpy;
+ gulong get_property_req;
+ gboolean have_error;
+ gboolean has_wm_state;
+};
+
+struct _SendEventState
{
Display *dpy;
Window window;
_XAsyncHandler async;
+ gulong send_event_req;
+ gulong get_input_focus_req;
+ gboolean have_error;
+ GdkSendXEventCallback callback;
+ gpointer data;
+};
+
+struct _SetInputFocusState
+{
+ Display *dpy;
+ _XAsyncHandler async;
gulong set_input_focus_req;
gulong get_input_focus_req;
};
+struct _RoundtripState
+{
+ Display *dpy;
+ _XAsyncHandler async;
+ gulong get_input_focus_req;
+ GdkDisplay *display;
+ GdkRoundTripCallback callback;
+ gpointer data;
+};
+
+static gboolean
+callback_idle (gpointer data)
+{
+ SendEventState *state = (SendEventState *)data;
+
+ state->callback (state->window, !state->have_error, state->data);
+
+ g_free (state);
+
+ return FALSE;
+}
+
static Bool
-set_input_focus_handler (Display *dpy,
- xReply *rep,
- char *buf,
- int len,
- XPointer data)
+send_event_handler (Display *dpy,
+ xReply *rep,
+ char *buf,
+ int len,
+ XPointer data)
{
- SetInputFocusState *state = (SetInputFocusState *)data;
+ SendEventState *state = (SendEventState *)data;
- if (dpy->last_request_read == state->set_input_focus_req)
+ if (dpy->last_request_read == state->send_event_req)
{
if (rep->generic.type == X_Error &&
- rep->error.errorCode == BadMatch)
+ rep->error.errorCode == BadWindow)
{
- /* Consume BadMatch errors, since we have no control
- * over them.
- */
+ state->have_error = TRUE;
return True;
}
}
-
- if (dpy->last_request_read == state->get_input_focus_req)
+ else if (dpy->last_request_read == state->get_input_focus_req)
{
xGetInputFocusReply replbuf;
- xGetInputFocusReply *repl;
+ xGetInputFocusReply *repl G_GNUC_UNUSED;
if (rep->generic.type != X_Error)
{
True);
}
+ if (state->callback)
+ gdk_threads_add_idle (callback_idle, state);
+
DeqAsyncHandler(state->dpy, &state->async);
- g_free (state);
-
return (rep->generic.type != X_Error);
}
return False;
}
+static void
+client_message_to_wire (XClientMessageEvent *ev,
+ xEvent *event)
+{
+ int i;
+ event->u.clientMessage.window = ev->window;
+ event->u.u.type = ev->type;
+ event->u.u.detail = ev->format;
+ switch (ev->format)
+ {
+ case 8:
+ event->u.clientMessage.u.b.type = ev->message_type;
+ for (i = 0; i < 20; i++)
+ event->u.clientMessage.u.b.bytes[i] = ev->data.b[i];
+ break;
+ case 16:
+ event->u.clientMessage.u.s.type = ev->message_type;
+ event->u.clientMessage.u.s.shorts0 = ev->data.s[0];
+ event->u.clientMessage.u.s.shorts1 = ev->data.s[1];
+ event->u.clientMessage.u.s.shorts2 = ev->data.s[2];
+ event->u.clientMessage.u.s.shorts3 = ev->data.s[3];
+ event->u.clientMessage.u.s.shorts4 = ev->data.s[4];
+ event->u.clientMessage.u.s.shorts5 = ev->data.s[5];
+ event->u.clientMessage.u.s.shorts6 = ev->data.s[6];
+ event->u.clientMessage.u.s.shorts7 = ev->data.s[7];
+ event->u.clientMessage.u.s.shorts8 = ev->data.s[8];
+ event->u.clientMessage.u.s.shorts9 = ev->data.s[9];
+ break;
+ case 32:
+ event->u.clientMessage.u.l.type = ev->message_type;
+ event->u.clientMessage.u.l.longs0 = ev->data.l[0];
+ event->u.clientMessage.u.l.longs1 = ev->data.l[1];
+ event->u.clientMessage.u.l.longs2 = ev->data.l[2];
+ event->u.clientMessage.u.l.longs3 = ev->data.l[3];
+ event->u.clientMessage.u.l.longs4 = ev->data.l[4];
+ break;
+ default:
+ /* client passing bogus data, let server complain */
+ break;
+ }
+}
+
void
-_gdk_x11_set_input_focus_safe (GdkDisplay *display,
- Window window,
- int revert_to,
- Time time)
+_gdk_x11_send_client_message_async (GdkDisplay *display,
+ Window window,
+ gboolean propagate,
+ glong event_mask,
+ XClientMessageEvent *event_send,
+ GdkSendXEventCallback callback,
+ gpointer data)
{
Display *dpy;
- SetInputFocusState *state;
+ SendEventState *state;
dpy = GDK_DISPLAY_XDISPLAY (display);
- state = g_new (SetInputFocusState, 1);
+ state = g_new (SendEventState, 1);
state->dpy = dpy;
state->window = window;
+ state->callback = callback;
+ state->data = data;
+ state->have_error = FALSE;
LockDisplay(dpy);
state->async.next = dpy->async_handlers;
- state->async.handler = set_input_focus_handler;
+ state->async.handler = send_event_handler;
state->async.data = (XPointer) state;
dpy->async_handlers = &state->async;
{
- xSetInputFocusReq *req;
+ register xSendEventReq *req;
+ xEvent ev;
+
+ client_message_to_wire (event_send, &ev);
+
+ GetReq(SendEvent, req);
+ req->destination = window;
+ req->propagate = propagate;
+ req->eventMask = event_mask;
+ /* gross, matches Xproto.h */
+#ifdef WORD64
+ memcpy ((char *) req->eventdata, (char *) &ev, SIZEOF(xEvent));
+#else
+ memcpy ((char *) &req->event, (char *) &ev, SIZEOF(xEvent));
+#endif
+
+ state->send_event_req = dpy->request;
+ }
+
+ /*
+ * XSync (dpy, 0)
+ */
+ {
+ xReq *req;
- GetReq(SetInputFocus, req);
- req->focus = window;
- req->revertTo = revert_to;
- req->time = time;
- state->set_input_focus_req = dpy->request;
+ GetEmptyReq(GetInputFocus, req);
+ state->get_input_focus_req = dpy->request;
}
+
+ UnlockDisplay(dpy);
+ SyncHandle();
+}
+
+static Bool
+list_children_handler (Display *dpy,
+ xReply *rep,
+ char *buf,
+ int len,
+ XPointer data)
+{
+ ListChildrenState *state = (ListChildrenState *)data;
+
+ if (dpy->last_request_read != state->get_property_req)
+ return False;
+
+ if (rep->generic.type == X_Error)
+ {
+ state->have_error = TRUE;
+ return False;
+ }
+ else
+ {
+ xGetPropertyReply replbuf;
+ xGetPropertyReply *repl;
+
+ repl = (xGetPropertyReply *)
+ _XGetAsyncReply(dpy, (char *)&replbuf, rep, buf, len,
+ (sizeof(xGetPropertyReply) - sizeof(xReply)) >> 2,
+ True);
+
+ state->has_wm_state = repl->propertyType != None;
+ /* Since we called GetProperty with longLength of 0, we don't
+ * have to worry about consuming the property data that would
+ * normally follow after the reply
+ */
+
+ return True;
+ }
+}
+
+static gboolean
+list_children_and_wm_state (Display *dpy,
+ Window w,
+ Atom wm_state_atom,
+ gboolean *has_wm_state,
+ Window **children,
+ unsigned int *nchildren)
+{
+ ListChildrenState state;
+ _XAsyncHandler async;
+ long nbytes;
+ xQueryTreeReply rep;
+ register xResourceReq *req;
+ xGetPropertyReq *prop_req;
+
+ LockDisplay(dpy);
+
+ *children = NULL;
+ *nchildren = 0;
+ *has_wm_state = FALSE;
+
+ state.have_error = FALSE;
+ state.has_wm_state = FALSE;
+
+ if (wm_state_atom)
+ {
+ async.next = dpy->async_handlers;
+ async.handler = list_children_handler;
+ async.data = (XPointer) &state;
+ dpy->async_handlers = &async;
+
+ GetReq (GetProperty, prop_req);
+ prop_req->window = w;
+ prop_req->property = wm_state_atom;
+ prop_req->type = AnyPropertyType;
+ prop_req->delete = False;
+ prop_req->longOffset = 0;
+ prop_req->longLength = 0;
+
+ state.get_property_req = dpy->request;
+ }
+
+ GetResReq(QueryTree, w, req);
+ if (!_XReply(dpy, (xReply *)&rep, 0, xFalse))
+ {
+ state.have_error = TRUE;
+ goto out;
+ }
+
+ if (rep.nChildren != 0)
+ {
+ nbytes = rep.nChildren << 2;
+ if (state.have_error)
+ {
+ _XEatData(dpy, (unsigned long) nbytes);
+ goto out;
+ }
+ *children = g_new (Window, rep.nChildren);
+ _XRead32 (dpy, (long *) *children, nbytes);
+ }
+
+ *nchildren = rep.nChildren;
+ *has_wm_state = state.has_wm_state;
+
+ out:
+ if (wm_state_atom)
+ DeqAsyncHandler(dpy, &async);
+ UnlockDisplay(dpy);
+ SyncHandle();
+
+ return !state.have_error;
+}
+
+static void
+handle_get_wa_reply (Display *dpy,
+ ChildInfoState *state,
+ xGetWindowAttributesReply *repl)
+{
+ GdkChildInfoX11 *child = &state->child_info[state->n_children_found];
+ child->is_mapped = repl->mapState != IsUnmapped;
+ child->window_class = repl->class;
+}
+
+static void
+handle_get_geometry_reply (Display *dpy,
+ ChildInfoState *state,
+ xGetGeometryReply *repl)
+{
+ GdkChildInfoX11 *child = &state->child_info[state->n_children_found];
+
+ child->x = cvtINT16toInt (repl->x);
+ child->y = cvtINT16toInt (repl->y);
+ child->width = repl->width;
+ child->height = repl->height;
+}
+
+static void
+handle_get_property_reply (Display *dpy,
+ ChildInfoState *state,
+ xGetPropertyReply *repl)
+{
+ GdkChildInfoX11 *child = &state->child_info[state->n_children_found];
+ child->has_wm_state = repl->propertyType != None;
+
+ /* Since we called GetProperty with longLength of 0, we don't
+ * have to worry about consuming the property data that would
+ * normally follow after the reply
+ */
+}
+
+static void
+next_child (ChildInfoState *state)
+{
+ if (state->current_request == CHILD_INFO_GET_GEOMETRY)
+ {
+ if (!state->have_error && !state->child_has_error)
+ {
+ state->child_info[state->n_children_found].window = state->children[state->current_child];
+ state->n_children_found++;
+ }
+ state->current_child++;
+ if (state->get_wm_state)
+ state->current_request = CHILD_INFO_GET_PROPERTY;
+ else
+ state->current_request = CHILD_INFO_GET_WA;
+ state->child_has_error = FALSE;
+ state->have_error = FALSE;
+ }
+ else
+ state->current_request++;
+}
+
+static Bool
+get_child_info_handler (Display *dpy,
+ xReply *rep,
+ char *buf,
+ int len,
+ XPointer data)
+{
+ Bool result = True;
+
+ ChildInfoState *state = (ChildInfoState *)data;
+
+ if (dpy->last_request_read != state->child_states[state->current_child].seq[state->current_request])
+ return False;
+
+ if (rep->generic.type == X_Error)
+ {
+ state->child_has_error = TRUE;
+ if (rep->error.errorCode != BadDrawable ||
+ rep->error.errorCode != BadWindow)
+ {
+ state->have_error = TRUE;
+ result = False;
+ }
+ }
+ else
+ {
+ switch (state->current_request)
+ {
+ case CHILD_INFO_GET_PROPERTY:
+ {
+ xGetPropertyReply replbuf;
+ xGetPropertyReply *repl;
+
+ repl = (xGetPropertyReply *)
+ _XGetAsyncReply(dpy, (char *)&replbuf, rep, buf, len,
+ (sizeof(xGetPropertyReply) - sizeof(xReply)) >> 2,
+ True);
+
+ handle_get_property_reply (dpy, state, repl);
+ }
+ break;
+ case CHILD_INFO_GET_WA:
+ {
+ xGetWindowAttributesReply replbuf;
+ xGetWindowAttributesReply *repl;
+
+ repl = (xGetWindowAttributesReply *)
+ _XGetAsyncReply(dpy, (char *)&replbuf, rep, buf, len,
+ (sizeof(xGetWindowAttributesReply) - sizeof(xReply)) >> 2,
+ True);
+
+ handle_get_wa_reply (dpy, state, repl);
+ }
+ break;
+ case CHILD_INFO_GET_GEOMETRY:
+ {
+ xGetGeometryReply replbuf;
+ xGetGeometryReply *repl;
+
+ repl = (xGetGeometryReply *)
+ _XGetAsyncReply(dpy, (char *)&replbuf, rep, buf, len,
+ (sizeof(xGetGeometryReply) - sizeof(xReply)) >> 2,
+ True);
+
+ handle_get_geometry_reply (dpy, state, repl);
+ }
+ break;
+ }
+ }
+
+ next_child (state);
+
+ return result;
+}
+
+gboolean
+_gdk_x11_get_window_child_info (GdkDisplay *display,
+ Window window,
+ gboolean get_wm_state,
+ gboolean *win_has_wm_state,
+ GdkChildInfoX11 **children,
+ guint *nchildren)
+{
+ Display *dpy;
+ _XAsyncHandler async;
+ ChildInfoState state;
+ Atom wm_state_atom;
+ gboolean has_wm_state;
+ Bool result;
+ guint i;
+
+ *children = NULL;
+ *nchildren = 0;
+
+ dpy = GDK_DISPLAY_XDISPLAY (display);
+ if (get_wm_state)
+ wm_state_atom = gdk_x11_get_xatom_by_name_for_display (display, "WM_STATE");
+ else
+ wm_state_atom = None;
+
+ state.children = NULL;
+ state.nchildren = 0;
+
+ gdk_x11_display_error_trap_push (display);
+ result = list_children_and_wm_state (dpy, window,
+ win_has_wm_state ? wm_state_atom : None,
+ &has_wm_state,
+ &state.children, &state.nchildren);
+ gdk_x11_display_error_trap_pop_ignored (display);
+ if (!result)
+ {
+ g_free (state.children);
+ return FALSE;
+ }
+
+ if (has_wm_state)
+ {
+ if (win_has_wm_state)
+ *win_has_wm_state = TRUE;
+ g_free (state.children);
+ return TRUE;
+ }
+ else
+ {
+ if (win_has_wm_state)
+ *win_has_wm_state = FALSE;
+ }
+
+ state.get_wm_state = get_wm_state;
+ state.child_info = g_new (GdkChildInfoX11, state.nchildren);
+ state.child_states = g_new (ChildInfoChildState, state.nchildren);
+ state.current_child = 0;
+ state.n_children_found = 0;
+ if (get_wm_state)
+ state.current_request = CHILD_INFO_GET_PROPERTY;
+ else
+ state.current_request = CHILD_INFO_GET_WA;
+ state.have_error = FALSE;
+ state.child_has_error = FALSE;
+
+ LockDisplay(dpy);
+
+ async.next = dpy->async_handlers;
+ async.handler = get_child_info_handler;
+ async.data = (XPointer) &state;
+ dpy->async_handlers = &async;
+
+ for (i = 0; i < state.nchildren; i++)
+ {
+ xResourceReq *resource_req;
+ xGetPropertyReq *prop_req;
+ Window window = state.children[i];
+
+ if (get_wm_state)
+ {
+ GetReq (GetProperty, prop_req);
+ prop_req->window = window;
+ prop_req->property = wm_state_atom;
+ prop_req->type = AnyPropertyType;
+ prop_req->delete = False;
+ prop_req->longOffset = 0;
+ prop_req->longLength = 0;
+
+ state.child_states[i].seq[CHILD_INFO_GET_PROPERTY] = dpy->request;
+ }
+
+ GetResReq(GetWindowAttributes, window, resource_req);
+ state.child_states[i].seq[CHILD_INFO_GET_WA] = dpy->request;
+
+ GetResReq(GetGeometry, window, resource_req);
+ state.child_states[i].seq[CHILD_INFO_GET_GEOMETRY] = dpy->request;
+ }
+
+ if (i != 0)
+ {
+ /* Wait for the last reply
+ */
+ xGetGeometryReply rep;
+
+ /* On error, our async handler will get called
+ */
+ if (_XReply (dpy, (xReply *)&rep, 0, xTrue))
+ handle_get_geometry_reply (dpy, &state, &rep);
+
+ next_child (&state);
+ }
+
+ if (!state.have_error)
+ {
+ *children = state.child_info;
+ *nchildren = state.n_children_found;
+ }
+ else
+ {
+ g_free (state.child_info);
+ }
+
+ g_free (state.children);
+ g_free (state.child_states);
+
+ DeqAsyncHandler(dpy, &async);
+ UnlockDisplay(dpy);
+ SyncHandle();
+
+ return !state.have_error;
+}
+
+static gboolean
+roundtrip_callback_idle (gpointer data)
+{
+ RoundtripState *state = (RoundtripState *)data;
+
+ state->callback (state->display, state->data, state->get_input_focus_req);
+
+ g_free (state);
+
+ return FALSE;
+}
+
+static Bool
+roundtrip_handler (Display *dpy,
+ xReply *rep,
+ char *buf,
+ int len,
+ XPointer data)
+{
+ RoundtripState *state = (RoundtripState *)data;
+
+ if (dpy->last_request_read == state->get_input_focus_req)
+ {
+ xGetInputFocusReply replbuf;
+ xGetInputFocusReply *repl G_GNUC_UNUSED;
+
+ if (rep->generic.type != X_Error)
+ {
+ /* Actually does nothing, since there are no additional bytes
+ * to read, but maintain good form.
+ */
+ repl = (xGetInputFocusReply *)
+ _XGetAsyncReply(dpy, (char *)&replbuf, rep, buf, len,
+ (sizeof(xGetInputFocusReply) - sizeof(xReply)) >> 2,
+ True);
+ }
+
+
+ if (state->callback)
+ gdk_threads_add_idle (roundtrip_callback_idle, state);
+
+ DeqAsyncHandler(state->dpy, &state->async);
+
+ return (rep->generic.type != X_Error);
+ }
+
+ return False;
+}
+
+void
+_gdk_x11_roundtrip_async (GdkDisplay *display,
+ GdkRoundTripCallback callback,
+ gpointer data)
+{
+ Display *dpy;
+ RoundtripState *state;
+
+ dpy = GDK_DISPLAY_XDISPLAY (display);
+
+ state = g_new (RoundtripState, 1);
+
+ state->display = display;
+ state->dpy = dpy;
+ state->callback = callback;
+ state->data = data;
+
+ LockDisplay(dpy);
+
+ state->async.next = dpy->async_handlers;
+ state->async.handler = roundtrip_handler;
+ state->async.data = (XPointer) state;
+ dpy->async_handlers = &state->async;
/*
* XSync (dpy, 0)