summaryrefslogtreecommitdiff
path: root/src/core/async-getprop.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/core/async-getprop.c')
-rw-r--r--src/core/async-getprop.c680
1 files changed, 680 insertions, 0 deletions
diff --git a/src/core/async-getprop.c b/src/core/async-getprop.c
new file mode 100644
index 00000000..80322b41
--- /dev/null
+++ b/src/core/async-getprop.c
@@ -0,0 +1,680 @@
+/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
+
+/* Asynchronous X property getting hack */
+
+/*
+ * Copyright (C) 2002 Havoc Pennington
+ * Copyright (C) 1986, 1998 The Open Group
+ *
+ * Permission to use, copy, modify, distribute, and sell this software
+ * and its documentation for any purpose is hereby granted without
+ * fee, provided that the above copyright notice appear in all copies
+ * and that both that copyright notice and this permission notice
+ * appear in supporting documentation.
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE OPEN GROUP BE LIABLE FOR
+ * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF
+ * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * Except as contained in this notice, the name of The Open Group shall not be
+ * used in advertising or otherwise to promote the sale, use or other dealings
+ * in this Software without prior written authorization from The Open Group.
+ */
+
+#include <assert.h>
+
+#undef DEBUG_SPEW
+#ifdef DEBUG_SPEW
+#include <stdio.h>
+#endif
+
+#include "async-getprop.h"
+
+#define NEED_REPLIES
+#include <X11/Xlibint.h>
+
+#ifndef NULL
+#define NULL ((void*)0)
+#endif
+
+typedef struct _ListNode ListNode;
+typedef struct _AgPerDisplayData AgPerDisplayData;
+
+struct _ListNode
+{
+ ListNode *next;
+};
+
+struct _AgGetPropertyTask
+{
+ ListNode node;
+
+ AgPerDisplayData *dd;
+ Window window;
+ Atom property;
+
+ unsigned long request_seq;
+ int error;
+
+ Atom actual_type;
+ int actual_format;
+
+ unsigned long n_items;
+ unsigned long bytes_after;
+ char *data;
+
+ Bool have_reply;
+};
+
+struct _AgPerDisplayData
+{
+ ListNode node;
+ _XAsyncHandler async;
+
+ Display *display;
+ ListNode *pending_tasks;
+ ListNode *pending_tasks_tail;
+ ListNode *completed_tasks;
+ ListNode *completed_tasks_tail;
+ int n_tasks_pending;
+ int n_tasks_completed;
+};
+
+static ListNode *display_datas = NULL;
+static ListNode *display_datas_tail = NULL;
+
+static void
+append_to_list (ListNode **head,
+ ListNode **tail,
+ ListNode *task)
+{
+ task->next = NULL;
+
+ if (*tail == NULL)
+ {
+ assert (*head == NULL);
+ *head = task;
+ *tail = task;
+ }
+ else
+ {
+ (*tail)->next = task;
+ *tail = task;
+ }
+}
+
+static void
+remove_from_list (ListNode **head,
+ ListNode **tail,
+ ListNode *task)
+{
+ ListNode *prev;
+ ListNode *node;
+
+ prev = NULL;
+ node = *head;
+ while (node != NULL)
+ {
+ if (node == task)
+ {
+ if (prev)
+ prev->next = node->next;
+ else
+ *head = node->next;
+
+ if (node == *tail)
+ *tail = prev;
+
+ break;
+ }
+
+ prev = node;
+ node = node->next;
+ }
+
+ /* can't remove what's not there */
+ assert (node != NULL);
+
+ node->next = NULL;
+}
+
+static void
+move_to_completed (AgPerDisplayData *dd,
+ AgGetPropertyTask *task)
+{
+ remove_from_list (&dd->pending_tasks,
+ &dd->pending_tasks_tail,
+ &task->node);
+
+ append_to_list (&dd->completed_tasks,
+ &dd->completed_tasks_tail,
+ &task->node);
+
+ dd->n_tasks_pending -= 1;
+ dd->n_tasks_completed += 1;
+}
+
+static AgGetPropertyTask*
+find_pending_by_request_sequence (AgPerDisplayData *dd,
+ unsigned long request_seq)
+{
+ ListNode *node;
+
+ /* if the sequence is after our last pending task, we
+ * aren't going to find a match
+ */
+ {
+ AgGetPropertyTask *task = (AgGetPropertyTask*) dd->pending_tasks_tail;
+ if (task != NULL)
+ {
+ if (task->request_seq < request_seq)
+ return NULL;
+ else if (task->request_seq == request_seq)
+ return task; /* why not check this */
+ }
+ }
+
+ /* Generally we should get replies in the order we sent
+ * requests, so we should usually be using the task
+ * at the head of the list, if we use any task at all.
+ * I'm not sure this is 100% guaranteed, if it is,
+ * it would be a big speedup.
+ */
+
+ node = dd->pending_tasks;
+ while (node != NULL)
+ {
+ AgGetPropertyTask *task = (AgGetPropertyTask*) node;
+
+ if (task->request_seq == request_seq)
+ return task;
+
+ node = node->next;
+ }
+
+ return NULL;
+}
+
+static Bool
+async_get_property_handler (Display *dpy,
+ xReply *rep,
+ char *buf,
+ int len,
+ XPointer data)
+{
+ xGetPropertyReply replbuf;
+ xGetPropertyReply *reply;
+ AgGetPropertyTask *task;
+ AgPerDisplayData *dd;
+ int bytes_read;
+
+ dd = (AgPerDisplayData*) data;
+
+#if 0
+ printf ("%s: seeing request seq %ld buflen %d\n", __FUNCTION__,
+ dpy->last_request_read, len);
+#endif
+
+ task = find_pending_by_request_sequence (dd, dpy->last_request_read);
+
+ if (task == NULL)
+ return False;
+
+ assert (dpy->last_request_read == task->request_seq);
+
+ task->have_reply = True;
+ move_to_completed (dd, task);
+
+ /* read bytes so far */
+ bytes_read = SIZEOF (xReply);
+
+ if (rep->generic.type == X_Error)
+ {
+ xError errbuf;
+
+ task->error = rep->error.errorCode;
+
+#ifdef DEBUG_SPEW
+ printf ("%s: error code = %d (ignoring error, eating %d bytes, generic.length = %ld)\n",
+ __FUNCTION__, task->error, (SIZEOF (xError) - bytes_read),
+ rep->generic.length);
+#endif
+
+ /* We return True (meaning we consumed the reply)
+ * because otherwise it would invoke the X error handler,
+ * and an async API is useless if you have to synchronously
+ * trap X errors. Also GetProperty can always fail, pretty
+ * much, so trapping errors is always what you want.
+ *
+ * We have to eat all the error reply data here.
+ * (kind of a charade as we know sizeof(xError) == sizeof(xReply))
+ *
+ * Passing discard = True seems to break things; I don't understand
+ * why, because there should be no extra data in an error reply,
+ * right?
+ */
+ _XGetAsyncReply (dpy, (char *)&errbuf, rep, buf, len,
+ (SIZEOF (xError) - bytes_read) >> 2, /* in 32-bit words */
+ False); /* really seems like it should be True */
+
+ return True;
+ }
+
+#ifdef DEBUG_SPEW
+ printf ("%s: already read %d bytes reading %d more for total of %d; generic.length = %ld\n",
+ __FUNCTION__, bytes_read, (SIZEOF (xGetPropertyReply) - bytes_read) >> 2,
+ SIZEOF (xGetPropertyReply), rep->generic.length);
+#endif
+
+ /* (kind of a silly as we know sizeof(xGetPropertyReply) == sizeof(xReply)) */
+ reply = (xGetPropertyReply *)
+ _XGetAsyncReply (dpy, (char *)&replbuf, rep, buf, len,
+ (SIZEOF (xGetPropertyReply) - bytes_read) >> 2, /* in 32-bit words */
+ False); /* False means expecting more data to follow,
+ * don't eat the rest of the reply
+ */
+
+ bytes_read = SIZEOF (xGetPropertyReply);
+
+#ifdef DEBUG_SPEW
+ printf ("%s: have reply propertyType = %ld format = %d n_items = %ld\n",
+ __FUNCTION__, reply->propertyType, reply->format, reply->nItems);
+#endif
+
+ assert (task->data == NULL);
+
+ /* This is all copied from XGetWindowProperty(). Not sure we should
+ * LockDisplay(). Not sure I'm passing the right args to
+ * XGetAsyncData(). Not sure about a lot of things.
+ */
+
+ /* LockDisplay (dpy); */
+
+ if (reply->propertyType != None)
+ {
+ long nbytes, netbytes;
+
+ /* this alignment macro from matecorba2 */
+#define ALIGN_VALUE(this, boundary) \
+ (( ((unsigned long)(this)) + (((unsigned long)(boundary)) -1)) & (~(((unsigned long)(boundary))-1)))
+
+ switch (reply->format)
+ {
+ /*
+ * One extra byte is malloced than is needed to contain the property
+ * data, but this last byte is null terminated and convenient for
+ * returning string properties, so the client doesn't then have to
+ * recopy the string to make it null terminated.
+ */
+ case 8:
+ nbytes = reply->nItems;
+ /* there's padding to word boundary */
+ netbytes = ALIGN_VALUE (nbytes, 4);
+ if (nbytes + 1 > 0 &&
+ (task->data = (char *) Xmalloc ((unsigned)nbytes + 1)))
+ {
+#ifdef DEBUG_SPEW
+ printf ("%s: already read %d bytes using %ld, more eating %ld more\n",
+ __FUNCTION__, bytes_read, nbytes, netbytes);
+#endif
+ /* _XReadPad (dpy, (char *) task->data, netbytes); */
+ _XGetAsyncData (dpy, task->data, buf, len,
+ bytes_read, nbytes,
+ netbytes);
+ }
+ break;
+
+ case 16:
+ nbytes = reply->nItems * sizeof (short);
+ netbytes = reply->nItems << 1;
+ netbytes = ALIGN_VALUE (netbytes, 4); /* align to word boundary */
+ if (nbytes + 1 > 0 &&
+ (task->data = (char *) Xmalloc ((unsigned)nbytes + 1)))
+ {
+#ifdef DEBUG_SPEW
+ printf ("%s: already read %d bytes using %ld more, eating %ld more\n",
+ __FUNCTION__, bytes_read, nbytes, netbytes);
+#endif
+ /* _XRead16Pad (dpy, (short *) task->data, netbytes); */
+ _XGetAsyncData (dpy, task->data, buf, len,
+ bytes_read, nbytes, netbytes);
+ }
+ break;
+
+ case 32:
+ /* NOTE buffer is in longs to match XGetWindowProperty() */
+ nbytes = reply->nItems * sizeof (long);
+ netbytes = reply->nItems << 2; /* wire size is always 32 bits though */
+ if (nbytes + 1 > 0 &&
+ (task->data = (char *) Xmalloc ((unsigned)nbytes + 1)))
+ {
+#ifdef DEBUG_SPEW
+ printf ("%s: already read %d bytes using %ld more, eating %ld more\n",
+ __FUNCTION__, bytes_read, nbytes, netbytes);
+#endif
+
+ /* We have to copy the XGetWindowProperty() crackrock
+ * and get format 32 as long even on 64-bit platforms.
+ */
+ if (sizeof (long) == 8)
+ {
+ char *netdata;
+ char *lptr;
+ char *end_lptr;
+
+ /* Store the 32-bit values in the end of the array */
+ netdata = task->data + nbytes / 2;
+
+ _XGetAsyncData (dpy, netdata, buf, len,
+ bytes_read, netbytes,
+ netbytes);
+
+ /* Now move the 32-bit values to the front */
+
+ lptr = task->data;
+ end_lptr = task->data + nbytes;
+ while (lptr != end_lptr)
+ {
+ *(long*) lptr = *(CARD32*) netdata;
+ lptr += sizeof (long);
+ netdata += sizeof (CARD32);
+ }
+ }
+ else
+ {
+ /* Here the wire format matches our actual format */
+ _XGetAsyncData (dpy, task->data, buf, len,
+ bytes_read, netbytes,
+ netbytes);
+ }
+ }
+ break;
+
+ default:
+ /*
+ * This part of the code should never be reached. If it is,
+ * the server sent back a property with an invalid format.
+ * This is a BadImplementation error.
+ *
+ * However this async GetProperty API doesn't report errors
+ * via the standard X mechanism, so don't do anything about
+ * it, other than store it in task->error.
+ */
+ {
+#if 0
+ xError error;
+#endif
+
+ task->error = BadImplementation;
+
+#if 0
+ error.sequenceNumber = task->request_seq;
+ error.type = X_Error;
+ error.majorCode = X_GetProperty;
+ error.minorCode = 0;
+ error.errorCode = BadImplementation;
+
+ _XError (dpy, &error);
+#endif
+ }
+
+ nbytes = netbytes = 0L;
+ break;
+ }
+
+ if (task->data == NULL)
+ {
+ task->error = BadAlloc;
+
+#ifdef DEBUG_SPEW
+ printf ("%s: already read %d bytes eating %ld\n",
+ __FUNCTION__, bytes_read, netbytes);
+#endif
+ /* _XEatData (dpy, (unsigned long) netbytes); */
+ _XGetAsyncData (dpy, NULL, buf, len,
+ bytes_read, 0, netbytes);
+
+ /* UnlockDisplay (dpy); */
+ return BadAlloc; /* not Success */
+ }
+
+ (task->data)[nbytes] = '\0';
+ }
+
+#ifdef DEBUG_SPEW
+ printf ("%s: have data\n", __FUNCTION__);
+#endif
+
+ task->actual_type = reply->propertyType;
+ task->actual_format = reply->format;
+ task->n_items = reply->nItems;
+ task->bytes_after = reply->bytesAfter;
+
+ /* UnlockDisplay (dpy); */
+
+ return True;
+}
+
+static AgPerDisplayData*
+get_display_data (Display *display,
+ Bool create)
+{
+ ListNode *node;
+ AgPerDisplayData *dd;
+
+ node = display_datas;
+ while (node != NULL)
+ {
+ dd = (AgPerDisplayData*) node;
+
+ if (dd->display == display)
+ return dd;
+
+ node = node->next;
+ }
+
+ if (!create)
+ return NULL;
+
+ dd = Xcalloc (1, sizeof (AgPerDisplayData));
+ if (dd == NULL)
+ return NULL;
+
+ dd->display = display;
+ dd->async.next = display->async_handlers;
+ dd->async.handler = async_get_property_handler;
+ dd->async.data = (XPointer) dd;
+ dd->display->async_handlers = &dd->async;
+
+ append_to_list (&display_datas,
+ &display_datas_tail,
+ &dd->node);
+
+ return dd;
+}
+
+static void
+maybe_free_display_data (AgPerDisplayData *dd)
+{
+ if (dd->pending_tasks == NULL &&
+ dd->completed_tasks == NULL)
+ {
+ DeqAsyncHandler (dd->display, &dd->async);
+ remove_from_list (&display_datas, &display_datas_tail,
+ &dd->node);
+ XFree (dd);
+ }
+}
+
+AgGetPropertyTask*
+ag_task_create (Display *dpy,
+ Window window,
+ Atom property,
+ long offset,
+ long length,
+ Bool delete,
+ Atom req_type)
+{
+ AgGetPropertyTask *task;
+ xGetPropertyReq *req;
+ AgPerDisplayData *dd;
+
+ /* Fire up our request */
+ LockDisplay (dpy);
+
+ dd = get_display_data (dpy, True);
+ if (dd == NULL)
+ {
+ UnlockDisplay (dpy);
+ return NULL;
+ }
+
+ GetReq (GetProperty, req);
+ req->window = window;
+ req->property = property;
+ req->type = req_type;
+ req->delete = delete;
+ req->longOffset = offset;
+ req->longLength = length;
+
+ /* Queue up our async task */
+ task = Xcalloc (1, sizeof (AgGetPropertyTask));
+ if (task == NULL)
+ {
+ UnlockDisplay (dpy);
+ return NULL;
+ }
+
+ task->dd = dd;
+ task->window = window;
+ task->property = property;
+ task->request_seq = dpy->request;
+
+ append_to_list (&dd->pending_tasks,
+ &dd->pending_tasks_tail,
+ &task->node);
+ dd->n_tasks_pending += 1;
+
+ UnlockDisplay (dpy);
+
+ SyncHandle ();
+
+ return task;
+}
+
+static void
+free_task (AgGetPropertyTask *task)
+{
+ remove_from_list (&task->dd->completed_tasks,
+ &task->dd->completed_tasks_tail,
+ &task->node);
+ task->dd->n_tasks_completed -= 1;
+ maybe_free_display_data (task->dd);
+ XFree (task);
+}
+
+Status
+ag_task_get_reply_and_free (AgGetPropertyTask *task,
+ Atom *actual_type,
+ int *actual_format,
+ unsigned long *nitems,
+ unsigned long *bytesafter,
+ unsigned char **prop)
+{
+ Display *dpy;
+
+ *prop = NULL;
+
+ dpy = task->dd->display; /* Xlib macros require a variable named "dpy" */
+
+ if (task->error != Success)
+ {
+ Status s = task->error;
+
+ free_task (task);
+
+ return s;
+ }
+
+ if (!task->have_reply)
+ {
+ free_task (task);
+
+ return BadAlloc; /* not Success */
+ }
+
+ *actual_type = task->actual_type;
+ *actual_format = task->actual_format;
+ *nitems = task->n_items;
+ *bytesafter = task->bytes_after;
+
+ *prop = (unsigned char*) task->data; /* pass out ownership of task->data */
+
+ SyncHandle ();
+
+ free_task (task);
+
+ return Success;
+}
+
+Bool
+ag_task_have_reply (AgGetPropertyTask *task)
+{
+ return task->have_reply;
+}
+
+Atom
+ag_task_get_property (AgGetPropertyTask *task)
+{
+ return task->property;
+}
+
+Window
+ag_task_get_window (AgGetPropertyTask *task)
+{
+ return task->window;
+}
+
+Display*
+ag_task_get_display (AgGetPropertyTask *task)
+{
+ return task->dd->display;
+}
+
+AgGetPropertyTask*
+ag_get_next_completed_task (Display *display)
+{
+ AgPerDisplayData *dd;
+
+ dd = get_display_data (display, False);
+
+ if (dd == NULL)
+ return NULL;
+
+#ifdef DEBUG_SPEW
+ printf ("%d pending %d completed\n",
+ dd->n_tasks_pending,
+ dd->n_tasks_completed);
+#endif
+
+ return (AgGetPropertyTask*) dd->completed_tasks;
+}
+
+void*
+ag_Xmalloc (unsigned long bytes)
+{
+ return (void*) Xmalloc (bytes);
+}
+
+void*
+ag_Xmalloc0 (unsigned long bytes)
+{
+ return (void*) Xcalloc (bytes, 1);
+}