diff options
Diffstat (limited to 'matchbox2/xas.c')
-rw-r--r-- | matchbox2/xas.c | 906 |
1 files changed, 906 insertions, 0 deletions
diff --git a/matchbox2/xas.c b/matchbox2/xas.c new file mode 100644 index 0000000..47329dc --- /dev/null +++ b/matchbox2/xas.c @@ -0,0 +1,906 @@ +/* Asynchronous Xlib hack utiltys lib + * + * Copyright (c) 2005 Matthew Allum + * + * Contains portions of code from Metacity and Xlib thus; + * + * 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 "xas.h" + +#if MBWM_WANT_DEBUG +#include "mb-wm-debug.h" +#include <stdio.h> +#define XAS_DBG(x, a...) \ +if (mbwm_debug_flags & MBWM_DEBUG_XAS) \ + fprintf(stderr, __FILE__ ":%d,%s() " x "\n", __LINE__, __func__, ##a) +#else +#define XAS_DBG(x, a...) do {} while (0) +#endif +#define XAS_MARK() XAS_DBG("--mark--"); + +#ifdef MBWM_WANT_ASSERT +#include <assert.h> +#define XAS_ASSERT(x) assert(x) +#else +#define XAS_ASSERT(x) do {} while (0) +#endif + +#define XAS_ALIGN_VALUE(this, boundary) \ + (( ((unsigned long)(this)) + (((unsigned long)(boundary)) -1)) & (~(((unsigned long)(boundary))-1))) + +#define NEED_REPLIES +#include <X11/Xlibint.h> + +#ifndef NULL +#define NULL ((void*)0) +#endif + +typedef struct XasTask XasTask; +typedef struct XasTaskGetProperty XasTaskGetProperty; +typedef struct XasTaskGetWinAttr XasTaskGetWinAttr; +typedef struct XasTaskGetGeom XasTaskGetGeom; + +#define XAS_TASK(t) (XasTask*)(t) + +typedef enum XasTaskType +{ + XAS_TASK_UNKNOWN, + XAS_TASK_GET_PROPERTY, + XAS_TASK_GET_WIN_ATTR, + XAS_TASK_GET_GEOM, + +} XasTaskType; + +struct XasContext +{ + _XAsyncHandler async; + Display *xdpy; + XasTask *tasks_pending; + int n_tasks_pending; + XasTask *tasks_completed; + int n_tasks_completed; +}; + +struct XasTask +{ + XasTask *next; + XasTaskType type; + XasContext *ctx; + unsigned long request_seq; + Bool have_reply; + int error; + +}; + +struct XasTaskGetProperty +{ + XasTask task; + + Window window; + Atom property; + + Atom actual_type; + int actual_format; + + unsigned long n_items; + unsigned long bytes_after; + unsigned char *data; +}; + +struct XasTaskGetWinAttr +{ + XasTask task; + Window window; + XasWindowAttributes *attr; +}; + +struct XasTaskGetGeom +{ + XasTask task; + Drawable drw; + Window root; + int x; + int y; + unsigned int width; + unsigned int height; + unsigned int border; + unsigned int depth; +}; + +static XasTask* +task_list_append(XasTask *tasks, XasTask *task) +{ + XasTask *t = NULL; + + /* FIXME: use hash on seq_req key instead ? */ + + if (tasks == NULL) return task; + + for (t = tasks; t->next != NULL; t = t->next); + + t->next = task; + + return tasks; +} + +XasTask* +task_list_remove(XasTask *tasks, XasTask *task) +{ + XasTask *t = NULL, *p = NULL; + + if (task == tasks) + { + t = task->next; + tasks->next = NULL; + return t; + } + + p = tasks; + t = tasks->next; + + while (t != NULL) + { + if (t == task) + { + p->next = t->next; + task->next = NULL; + return tasks; + } + p = t; t = t->next; + } + + return tasks; +} + +static void +task_init(XasContext *ctx, XasTask *task, XasTaskType type) +{ + task->ctx = ctx; + task->next = NULL; + task->type = type; + task->request_seq = ctx->xdpy->request; + task->have_reply = False; +} + +static void +task_complete(XasContext *ctx, XasTask *task) +{ + ctx->tasks_pending = task_list_remove(ctx->tasks_pending, task); + ctx->n_tasks_pending--; + ctx->tasks_completed = task_list_append(ctx->tasks_completed, task); + ctx->n_tasks_completed++; +} + +static XasTask* +xas_find_task_for_request_seq(XasContext *ctx, + XasTask *task_list, + unsigned long request_seq) +{ + XasTask *task = NULL; + + task = task_list; + + while (task != NULL) + { + if (task->request_seq == request_seq) + return task; + task = task->next; + } + + XAS_DBG("Failed to find task\n"); + return NULL; +} + +static Bool +xas_async_get_geom_handler (XasContext *ctx, + XasTaskGetGeom *task, + xReply *rep, + char *buf, + int len) +{ + Display *dpy; + xGetGeometryReply *repl; + xGetGeometryReply replbuf; + + dpy = ctx->xdpy; + + task->task.have_reply = True; + + task_complete (ctx, XAS_TASK(task)); + + if (rep->generic.type == X_Error) + { + xError errbuf; + task->task.error = rep->error.errorCode; + _XGetAsyncReply (dpy, (char *)&errbuf, rep, buf, len, + (SIZEOF (xError) - SIZEOF (xReply)) >> 2, + False); + return True; + } + + repl = (xGetGeometryReply *) + _XGetAsyncReply(dpy, (char *)&replbuf, rep, buf, len, + (SIZEOF(xGetGeometryReply) - SIZEOF(xReply)) >> 2, + True); + + task->root = repl->root; + task->x = cvtINT16toInt (repl->x); + task->y = cvtINT16toInt (repl->y); + task->width = repl->width; + task->height = repl->height; + task->border = repl->borderWidth; + task->depth = repl->depth; + + return True; +} + + +static Bool +xas_async_get_win_attr_handler (XasContext *ctx, + XasTaskGetWinAttr *task, + xReply *rep, + char *buf, + int len) +{ + Display *dpy; + xGetWindowAttributesReply replbuf; + xGetWindowAttributesReply *repl; + + dpy = ctx->xdpy; + + task->task.have_reply = True; + + task_complete (ctx, XAS_TASK(task)); + + if (rep->generic.type == X_Error) + { + xError errbuf; + task->task.error = rep->error.errorCode; + _XGetAsyncReply (dpy, (char *)&errbuf, rep, buf, len, + (SIZEOF (xError) - SIZEOF (xReply)) >> 2, + False); + return True; + } + + repl = (xGetWindowAttributesReply *) + _XGetAsyncReply(dpy, (char *)&replbuf, rep, buf, len, + (SIZEOF(xGetWindowAttributesReply) - SIZEOF(xReply)) >> 2, + True); + + task->attr = (XasWindowAttributes *)Xmalloc(sizeof(XasWindowAttributes)); + + if (task->attr == NULL) + { + task->task.error = BadAlloc; + return True; + } + + task->attr->class = repl->class; + task->attr->bit_gravity = repl->bitGravity; + task->attr->win_gravity = repl->winGravity; + task->attr->backing_store = repl->backingStore; + task->attr->backing_planes = repl->backingBitPlanes; + task->attr->backing_pixel = repl->backingPixel; + task->attr->save_under = repl->saveUnder; + task->attr->colormap = repl->colormap; + task->attr->map_installed = repl->mapInstalled; + task->attr->map_state = repl->mapState; + task->attr->all_event_masks = repl->allEventMasks; + task->attr->your_event_mask = repl->yourEventMask; + task->attr->do_not_propagate_mask = repl->doNotPropagateMask; + task->attr->override_redirect = repl->override; + task->attr->visual = _XVIDtoVisual (dpy, repl->visualID); + + return True; +} + +static Bool +xas_async_get_property_handler (XasContext *ctx, + XasTaskGetProperty *task, + xReply *rep, + char *buf, + int len) +{ + Display *dpy; + xGetPropertyReply replbuf; + xGetPropertyReply *reply; + int bytes_read; + + /* Code and comments here wripped out of Metacity, Copyright + * Havoc Pennington. + */ + + /* FIXME: Really check this code is really reliable/safe.*/ + + dpy = ctx->xdpy; + + XAS_DBG ("seeing request seq %ld buflen %d", + dpy->last_request_read, len); + + task_complete (ctx, XAS_TASK(task)); + + /* read bytes so far */ + bytes_read = SIZEOF (xReply); + + if (rep->generic.type == X_Error) + { + xError errbuf; + + task->task.error = rep->error.errorCode; + + /* 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, /* 32-bit words */ + False); /* really seems like it should be True */ + return True; + } + + XAS_DBG ("already read %d bytes reading %d more for total of %d; generic.length = %ld", + bytes_read, (SIZEOF (xGetPropertyReply) - bytes_read) >> 2, + SIZEOF (xGetPropertyReply), rep->generic.length); + + reply = (xGetPropertyReply *) + _XGetAsyncReply (dpy, (char *)&replbuf, rep, buf, len, + (SIZEOF (xGetPropertyReply) - bytes_read) >> 2, + False); /* False means expecting more data to follow, + * don't eat the rest of the reply + */ + + bytes_read = SIZEOF (xGetPropertyReply); + + XAS_DBG ("have reply propertyType = %ld format = %d n_items = %ld", + reply->propertyType, reply->format, reply->nItems); + + /* 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 orbit2 */ + + 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 = XAS_ALIGN_VALUE (nbytes, 4); + if (nbytes + 1 > 0 && + (task->data = (unsigned char *) Xmalloc ((unsigned)nbytes + 1))) + { + + XAS_DBG ("already read %d bytes using %ld, more eating %ld more", + bytes_read, nbytes, netbytes); + + /* _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 = XAS_ALIGN_VALUE (netbytes, 4); /* to word boundary */ + if (nbytes + 1 > 0 && + (task->data = (unsigned char *) Xmalloc ((unsigned)nbytes + 1))) + { + XAS_DBG ("%s: already read %d bytes using %ld more, eating %ld more\n", + __FUNCTION__, bytes_read, nbytes, netbytes); + + /* _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 always 32 bits though */ + if (nbytes + 1 > 0 && + (task->data = (unsigned char *) Xmalloc ((unsigned)nbytes + 1))) + { + XAS_DBG ("already read %d bytes using %ld more, eating %ld more", + bytes_read, nbytes, netbytes); + + /* We have to copy the XGetWindowProperty() crackrock + * and get format 32 as long even on 64-bit platforms. + */ + if (sizeof (long) == 8) + { + unsigned char *netdata; + unsigned char *lptr; + unsigned 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. + */ + task->task.error = BadImplementation; + nbytes = netbytes = 0L; + break; + } + + if (task->data == NULL) + { + task->task.error = BadAlloc; + + XAS_DBG ("already read %d bytes eating %ld", bytes_read, netbytes); + + /* _XEatData (dpy, (unsigned long) netbytes); */ + _XGetAsyncData (dpy, NULL, buf, len, bytes_read, 0, netbytes); + + /* UnlockDisplay (dpy); */ + return BadAlloc; /* not Success */ + } + + (task->data)[nbytes] = '\0'; + } + + XAS_DBG ("have data"); + + 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 Bool +xas_async_handler (Display *dpy, + xReply *rep, + char *buf, + int len, + XPointer data) +{ + XasContext *ctx = (XasContext *)data; + XasTask *task; + + XAS_ASSERT(ctx->xdpy == dpy); + + if (!ctx->tasks_pending) + return False; + + task = xas_find_task_for_request_seq(ctx, + ctx->tasks_pending, + dpy->last_request_read); + if (!task) + return False; + + switch (task->type) + { + case XAS_TASK_GET_PROPERTY: + return xas_async_get_property_handler (ctx, + (XasTaskGetProperty*)task, + rep, buf, len); + case XAS_TASK_GET_WIN_ATTR: + return xas_async_get_win_attr_handler (ctx, + (XasTaskGetWinAttr*)task, + rep, buf, len); + case XAS_TASK_GET_GEOM: + return xas_async_get_geom_handler (ctx, + (XasTaskGetGeom*)task, + rep, buf, len); + case XAS_TASK_UNKNOWN: + default: + /* Should never get here */ + return False; + } + + return False; +} + +/* public */ + +XasContext* +xas_context_new(Display *xdpy) +{ + XasContext *ctx = NULL; + + ctx = (XasContext *)Xmalloc(sizeof(XasContext)); + + ctx->xdpy = xdpy; + ctx->async.next = xdpy->async_handlers; + ctx->async.handler = xas_async_handler; + ctx->async.data = (XPointer) ctx; + ctx->xdpy->async_handlers = &ctx->async; + + ctx->tasks_pending = NULL; + ctx->n_tasks_pending = 0; + ctx->tasks_completed = NULL; + ctx->n_tasks_completed = 0; + + return ctx; +} + +void +xas_context_destroy(XasContext *ctx) +{ + DeqAsyncHandler (ctx->xdpy, &ctx->async); + + /* FIXME: empty pending and completed lists */ + + free(ctx); +} + +XasCookie +xas_get_property(XasContext *ctx, + Window win, + Atom property, + long offset, + long length, + Bool delete, + Atom req_type) +{ + Display *dpy ; + XasTaskGetProperty *task; + xGetPropertyReq *req; + + LockDisplay (ctx->xdpy); + + dpy = ctx->xdpy; /* GetReq() needs this */ + + task = Xcalloc (1, sizeof (XasTaskGetProperty)); + if (task == NULL) + { + UnlockDisplay (dpy); + return 0; + } + + GetReq (GetProperty, req); + req->window = win; + req->property = property; + req->type = req_type; + req->delete = delete; + req->longOffset = offset; + req->longLength = length; + + /* needed ? error.sequenceNumber = ctx->xdpy->request;*/ + + task_init(ctx, XAS_TASK(task), XAS_TASK_GET_PROPERTY); + + task->window = win; + task->property = property; + + ctx->tasks_pending = task_list_append(ctx->tasks_pending, &task->task); + + ctx->n_tasks_pending ++; + + UnlockDisplay (dpy); + + SyncHandle (); + + return task->task.request_seq; +} + +Bool +xas_have_reply(XasContext *ctx, + XasCookie cookie) +{ + return (xas_find_task_for_request_seq(ctx, ctx->tasks_completed, cookie) != NULL); +} + + +Status +xas_get_property_reply(XasContext *ctx, + XasCookie cookie, + Atom *actual_type_return, + int *actual_format_return, + unsigned long *nitems_return, + unsigned long *bytes_after_return, + unsigned char **prop_return, + int *x_error_code) +{ + Display *dpy; + XasTaskGetProperty *task = NULL; + Bool result = False; + + dpy = ctx->xdpy; /* For SyncHandle (); */ + + if (x_error_code) *x_error_code = 0; /* No error as yet */ + + task = (XasTaskGetProperty *) xas_find_task_for_request_seq(ctx, + ctx->tasks_completed, + cookie); + if (task == NULL) + { + XAS_DBG("Failed to find cookie"); + return False; + } + + if (!task->task.error) + { + *actual_type_return = task->actual_type; + *actual_format_return = task->actual_format; + *nitems_return = task->n_items; + *bytes_after_return = task->bytes_after; + *prop_return = task->data; + + result = True; + } + else + { + XAS_DBG("is error"); + if (x_error_code) + *x_error_code = task->task.error; + result = False;; + } + + SyncHandle (); + + ctx->tasks_completed = task_list_remove(ctx->tasks_completed, + XAS_TASK(task)); + ctx->n_tasks_completed--; + + XFree (task); + + return result; +} + +XasCookie +xas_get_window_attributes(XasContext *ctx, + Window win) +{ + xResourceReq *req; + Display *dpy ; + XasTaskGetWinAttr *task; + + LockDisplay (ctx->xdpy); + + dpy = ctx->xdpy; /* GetReq() needs this */ + + task = Xcalloc (1, sizeof (XasTaskGetWinAttr)); + if (task == NULL) + { + UnlockDisplay (dpy); + return 0; + } + + GetResReq(GetWindowAttributes, win, req); + + task_init(ctx, XAS_TASK(task), XAS_TASK_GET_WIN_ATTR); + + task->window = win; + + ctx->tasks_pending = task_list_append(ctx->tasks_pending, &task->task); + ctx->n_tasks_pending ++; + + UnlockDisplay (dpy); + + SyncHandle (); + + return task->task.request_seq; +} + +XasWindowAttributes* +xas_get_window_attributes_reply(XasContext *ctx, + XasCookie cookie, + int *x_error_code) +{ + Display *dpy; + XasTaskGetWinAttr *task = NULL; + XasWindowAttributes *result = NULL; + + dpy = ctx->xdpy; /* For SyncHandle (); */ + + if (x_error_code) *x_error_code = 0; /* No error as yet */ + + task = (XasTaskGetWinAttr *) xas_find_task_for_request_seq(ctx, + ctx->tasks_completed, + cookie); + + if (task == NULL) + return NULL; + + if (task->task.type != XAS_TASK_GET_WIN_ATTR) + return NULL; + + if (!task->task.error) + { + result = task->attr; + } + else + { + XAS_DBG("is error"); + if (x_error_code) + *x_error_code = task->task.error; + } + + SyncHandle (); + + ctx->tasks_completed = task_list_remove(ctx->tasks_completed, + XAS_TASK(task)); + ctx->n_tasks_completed--; + + XFree (task); + + return result; +} + +XasCookie +xas_get_geometry(XasContext *ctx, + Drawable d) +{ + xResourceReq *req; + Display *dpy ; + XasTaskGetGeom *task; + + LockDisplay (ctx->xdpy); + + dpy = ctx->xdpy; /* GetReq() needs this */ + + task = Xcalloc (1, sizeof (XasTaskGetGeom)); + if (task == NULL) + { + UnlockDisplay (dpy); + return 0; + } + + GetResReq(GetGeometry, d, req); + + task_init(ctx, XAS_TASK(task), XAS_TASK_GET_GEOM); + + task->drw = d; + + ctx->tasks_pending = task_list_append(ctx->tasks_pending, &task->task); + ctx->n_tasks_pending ++; + + UnlockDisplay (dpy); + + SyncHandle (); + + return task->task.request_seq; +} + +Status +xas_get_geometry_reply (XasContext *ctx, + XasCookie cookie, + int *x_return, + int *y_return, + unsigned int *width_return, + unsigned int *height_return, + unsigned int *border_width_return, + unsigned int *depth_return, + int *x_error_code) +{ + Display *dpy; + XasTaskGetGeom *task = NULL; + Status result = False; + + dpy = ctx->xdpy; /* For SyncHandle (); */ + + if (x_error_code) *x_error_code = 0; /* No error as yet */ + + task = (XasTaskGetGeom *) xas_find_task_for_request_seq(ctx, + ctx->tasks_completed, + cookie); + + if (task == NULL) + { + XAS_DBG("Failed to find task\n"); + return False; + } + + if (task->task.type != XAS_TASK_GET_GEOM) + { + XAS_DBG("Found task, but type different to expected ( %i vs %i )\n", + task->task.type, XAS_TASK_GET_GEOM); + return False; + } + + if (!task->task.error) + { + *x_return = task->x; + *y_return = task->y; + *width_return = task->width; + *height_return = task->height; + *border_width_return = task->border; + *depth_return = task->depth; + result = True; + } + else + { + XAS_DBG("is error"); + if (x_error_code) + *x_error_code = task->task.error; + } + + SyncHandle (); + + ctx->tasks_completed = task_list_remove(ctx->tasks_completed, + XAS_TASK(task)); + ctx->n_tasks_completed--; + + XFree (task); + + return result; +} + + + |