mirror of
https://github.com/brl/mutter.git
synced 2024-11-25 01:20:42 -05:00
Add wacky hack suggested by Keith Packard to get X properties
2002-10-06 Havoc Pennington <hp@pobox.com> * src/async-getprop.c: Add wacky hack suggested by Keith Packard to get X properties asynchronously. Not actually used by metacity yet, but thinking about it.
This commit is contained in:
parent
b8e5a4d707
commit
a1bb0e0015
@ -1,3 +1,9 @@
|
|||||||
|
2002-10-06 Havoc Pennington <hp@pobox.com>
|
||||||
|
|
||||||
|
* src/async-getprop.c: Add wacky hack suggested by Keith Packard
|
||||||
|
to get X properties asynchronously. Not actually used by metacity
|
||||||
|
yet, but thinking about it.
|
||||||
|
|
||||||
2002-10-04 Havoc Pennington <hp@redhat.com>
|
2002-10-04 Havoc Pennington <hp@redhat.com>
|
||||||
|
|
||||||
* configure.in: actually link to RANDR_LIBS
|
* configure.in: actually link to RANDR_LIBS
|
||||||
|
@ -99,10 +99,12 @@ metacity_theme_viewer_LDADD= @METACITY_LIBS@
|
|||||||
metacity_dialog_LDADD=@METACITY_LIBS@
|
metacity_dialog_LDADD=@METACITY_LIBS@
|
||||||
|
|
||||||
testgradient_SOURCES=gradient.h gradient.c testgradient.c
|
testgradient_SOURCES=gradient.h gradient.c testgradient.c
|
||||||
|
testasyncgetprop_SOURCES=async-getprop.h async-getprop.c testasyncgetprop.c
|
||||||
|
|
||||||
noinst_PROGRAMS=testgradient
|
noinst_PROGRAMS=testgradient testasyncgetprop
|
||||||
|
|
||||||
testgradient_LDADD= @METACITY_LIBS@
|
testgradient_LDADD= @METACITY_LIBS@
|
||||||
|
testasyncgetprop_LDADD= @METACITY_LIBS@
|
||||||
|
|
||||||
desktopfilesdir=$(datadir)/gnome/wm-properties
|
desktopfilesdir=$(datadir)/gnome/wm-properties
|
||||||
desktopfiles_in_files=metacity.desktop.in
|
desktopfiles_in_files=metacity.desktop.in
|
||||||
|
516
src/async-getprop.c
Normal file
516
src/async-getprop.c
Normal file
@ -0,0 +1,516 @@
|
|||||||
|
/* 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/Xproto.h>
|
||||||
|
#include <X11/Xlibint.h>
|
||||||
|
|
||||||
|
#ifndef NULL
|
||||||
|
#define NULL ((void*)0)
|
||||||
|
#endif
|
||||||
|
|
||||||
|
struct _AgGetPropertyTask
|
||||||
|
{
|
||||||
|
Display *display;
|
||||||
|
Window window;
|
||||||
|
Atom property;
|
||||||
|
|
||||||
|
_XAsyncHandler async;
|
||||||
|
|
||||||
|
unsigned long request_seq;
|
||||||
|
int error;
|
||||||
|
|
||||||
|
Atom actual_type;
|
||||||
|
int actual_format;
|
||||||
|
|
||||||
|
unsigned long n_items;
|
||||||
|
unsigned long bytes_after;
|
||||||
|
unsigned char *data;
|
||||||
|
|
||||||
|
Bool have_reply;
|
||||||
|
|
||||||
|
AgGetPropertyTask *next;
|
||||||
|
};
|
||||||
|
|
||||||
|
static AgGetPropertyTask *pending_tasks = NULL;
|
||||||
|
static AgGetPropertyTask *pending_tasks_tail = NULL;
|
||||||
|
static AgGetPropertyTask *completed_tasks = NULL;
|
||||||
|
static AgGetPropertyTask *completed_tasks_tail = NULL;
|
||||||
|
static int n_tasks_pending = 0;
|
||||||
|
static int n_tasks_completed = 0;
|
||||||
|
|
||||||
|
static void
|
||||||
|
append_to_list (AgGetPropertyTask **head,
|
||||||
|
AgGetPropertyTask **tail,
|
||||||
|
AgGetPropertyTask *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 (AgGetPropertyTask **head,
|
||||||
|
AgGetPropertyTask **tail,
|
||||||
|
AgGetPropertyTask *task)
|
||||||
|
{
|
||||||
|
AgGetPropertyTask *prev;
|
||||||
|
AgGetPropertyTask *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 (AgGetPropertyTask *task)
|
||||||
|
{
|
||||||
|
remove_from_list (&pending_tasks,
|
||||||
|
&pending_tasks_tail,
|
||||||
|
task);
|
||||||
|
|
||||||
|
append_to_list (&completed_tasks,
|
||||||
|
&completed_tasks_tail,
|
||||||
|
task);
|
||||||
|
|
||||||
|
--n_tasks_pending;
|
||||||
|
++n_tasks_completed;
|
||||||
|
}
|
||||||
|
|
||||||
|
static Bool
|
||||||
|
async_get_property_handler (Display *dpy,
|
||||||
|
xReply *rep,
|
||||||
|
char *buf,
|
||||||
|
int len,
|
||||||
|
XPointer data)
|
||||||
|
{
|
||||||
|
xGetPropertyReply replbuf;
|
||||||
|
xGetPropertyReply *reply;
|
||||||
|
AgGetPropertyTask *task;
|
||||||
|
int bytes_read;
|
||||||
|
|
||||||
|
task = (AgGetPropertyTask*) data;
|
||||||
|
|
||||||
|
#if 0
|
||||||
|
printf ("%s: waiting for %ld seeing %ld buflen %d\n", __FUNCTION__,
|
||||||
|
task->request_seq, dpy->last_request_read, len);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
if (dpy->last_request_read != task->request_seq)
|
||||||
|
return False;
|
||||||
|
|
||||||
|
task->have_reply = True;
|
||||||
|
move_to_completed (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 orbit2 */
|
||||||
|
#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 = (unsigned 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 = (unsigned 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:
|
||||||
|
nbytes = reply->nItems * sizeof (long);
|
||||||
|
netbytes = reply->nItems << 2;
|
||||||
|
if (nbytes + 1 > 0 &&
|
||||||
|
(task->data = (unsigned 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
|
||||||
|
/* _XRead32 (dpy, (long *) task->data, netbytes); */
|
||||||
|
_XGetAsyncData (dpy, task->data, buf, len,
|
||||||
|
bytes_read, nbytes,
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
AgGetPropertyTask*
|
||||||
|
ag_task_create (Display *dpy,
|
||||||
|
Window window,
|
||||||
|
Atom property,
|
||||||
|
long offset,
|
||||||
|
long length,
|
||||||
|
Bool delete,
|
||||||
|
Atom req_type)
|
||||||
|
{
|
||||||
|
AgGetPropertyTask *task;
|
||||||
|
xGetPropertyReq *req;
|
||||||
|
xError error;
|
||||||
|
|
||||||
|
/* Fire up our request */
|
||||||
|
LockDisplay (dpy);
|
||||||
|
GetReq (GetProperty, req);
|
||||||
|
req->window = window;
|
||||||
|
req->property = property;
|
||||||
|
req->type = req_type;
|
||||||
|
req->delete = delete;
|
||||||
|
req->longOffset = offset;
|
||||||
|
req->longLength = length;
|
||||||
|
|
||||||
|
error.sequenceNumber = dpy->request;
|
||||||
|
|
||||||
|
/* Queue up our async task */
|
||||||
|
task = Xcalloc (1, sizeof (AgGetPropertyTask));
|
||||||
|
if (task == NULL)
|
||||||
|
{
|
||||||
|
UnlockDisplay (dpy);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
task->display = dpy;
|
||||||
|
task->window = window;
|
||||||
|
task->property = property;
|
||||||
|
task->request_seq = dpy->request;
|
||||||
|
task->async.next = dpy->async_handlers;
|
||||||
|
task->async.handler = async_get_property_handler;
|
||||||
|
task->async.data = (XPointer) task;
|
||||||
|
dpy->async_handlers = &task->async;
|
||||||
|
|
||||||
|
append_to_list (&pending_tasks,
|
||||||
|
&pending_tasks_tail,
|
||||||
|
task);
|
||||||
|
++n_tasks_pending;
|
||||||
|
|
||||||
|
UnlockDisplay (dpy);
|
||||||
|
|
||||||
|
SyncHandle ();
|
||||||
|
|
||||||
|
return 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->display; /* Xlib macros require a variable named "dpy" */
|
||||||
|
|
||||||
|
if (task->error != Success)
|
||||||
|
{
|
||||||
|
Status s = task->error;
|
||||||
|
DeqAsyncHandler (task->display, &task->async);
|
||||||
|
remove_from_list (&completed_tasks, &completed_tasks_tail, task);
|
||||||
|
--n_tasks_completed;
|
||||||
|
XFree (task);
|
||||||
|
return s;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!task->have_reply)
|
||||||
|
{
|
||||||
|
DeqAsyncHandler (task->display, &task->async);
|
||||||
|
remove_from_list (&completed_tasks, &completed_tasks_tail, task);
|
||||||
|
--n_tasks_completed;
|
||||||
|
XFree (task);
|
||||||
|
return BadAlloc; /* not Success */
|
||||||
|
}
|
||||||
|
|
||||||
|
*actual_type = task->actual_type;
|
||||||
|
*actual_format = task->actual_format;
|
||||||
|
*nitems = task->n_items;
|
||||||
|
*bytesafter = task->bytes_after;
|
||||||
|
|
||||||
|
*prop = task->data; /* pass out ownership of task->data */
|
||||||
|
|
||||||
|
SyncHandle ();
|
||||||
|
|
||||||
|
DeqAsyncHandler (dpy, &task->async);
|
||||||
|
remove_from_list (&completed_tasks, &completed_tasks_tail, task);
|
||||||
|
--n_tasks_completed;
|
||||||
|
XFree (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->display;
|
||||||
|
}
|
||||||
|
|
||||||
|
AgGetPropertyTask*
|
||||||
|
ag_get_next_completed_task (Display *display)
|
||||||
|
{
|
||||||
|
AgGetPropertyTask *node;
|
||||||
|
|
||||||
|
#ifdef DEBUG_SPEW
|
||||||
|
printf ("%d pending %d completed\n", n_tasks_pending, n_tasks_completed);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
node = completed_tasks;
|
||||||
|
while (node != NULL)
|
||||||
|
{
|
||||||
|
if (node->display == display)
|
||||||
|
return node;
|
||||||
|
|
||||||
|
node = node->next;
|
||||||
|
}
|
||||||
|
|
||||||
|
return NULL;
|
||||||
|
}
|
61
src/async-getprop.h
Normal file
61
src/async-getprop.h
Normal file
@ -0,0 +1,61 @@
|
|||||||
|
/* Asynchronous X property getting hack */
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Copyright (C) 2002 Havoc Pennington
|
||||||
|
*
|
||||||
|
* 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.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef ASYNC_GETPROP_H
|
||||||
|
#define ASYNC_GETPROP_H
|
||||||
|
|
||||||
|
#include <X11/Xlib.h>
|
||||||
|
#include <X11/Xutil.h>
|
||||||
|
|
||||||
|
typedef struct _AgGetPropertyTask AgGetPropertyTask;
|
||||||
|
|
||||||
|
AgGetPropertyTask* ag_task_create (Display *display,
|
||||||
|
Window window,
|
||||||
|
Atom property,
|
||||||
|
long offset,
|
||||||
|
long length,
|
||||||
|
Bool delete,
|
||||||
|
Atom req_type);
|
||||||
|
Status ag_task_get_reply_and_free (AgGetPropertyTask *task,
|
||||||
|
Atom *actual_type,
|
||||||
|
int *actual_format,
|
||||||
|
unsigned long *nitems,
|
||||||
|
unsigned long *bytesafter,
|
||||||
|
unsigned char **prop);
|
||||||
|
|
||||||
|
Bool ag_task_have_reply (AgGetPropertyTask *task);
|
||||||
|
Atom ag_task_get_property (AgGetPropertyTask *task);
|
||||||
|
Window ag_task_get_window (AgGetPropertyTask *task);
|
||||||
|
Display* ag_task_get_display (AgGetPropertyTask *task);
|
||||||
|
|
||||||
|
AgGetPropertyTask* ag_get_next_completed_task (Display *display);
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
495
src/testasyncgetprop.c
Normal file
495
src/testasyncgetprop.c
Normal file
@ -0,0 +1,495 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2002 Havoc Pennington
|
||||||
|
*
|
||||||
|
* 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 "async-getprop.h"
|
||||||
|
|
||||||
|
#include <time.h>
|
||||||
|
#include <sys/time.h>
|
||||||
|
#include <sys/types.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <errno.h>
|
||||||
|
#include <signal.h>
|
||||||
|
#include <sys/select.h>
|
||||||
|
#include <assert.h>
|
||||||
|
|
||||||
|
#ifndef TRUE
|
||||||
|
#define TRUE 1
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef FALSE
|
||||||
|
#define FALSE 0
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef NULL
|
||||||
|
#define NULL ((void*) 0)
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef HAVE_BACKTRACE
|
||||||
|
#include <execinfo.h>
|
||||||
|
static void
|
||||||
|
print_backtrace (void)
|
||||||
|
{
|
||||||
|
void *bt[500];
|
||||||
|
int bt_size;
|
||||||
|
int i;
|
||||||
|
char **syms;
|
||||||
|
|
||||||
|
bt_size = backtrace (bt, 500);
|
||||||
|
|
||||||
|
syms = backtrace_symbols (bt, bt_size);
|
||||||
|
|
||||||
|
i = 0;
|
||||||
|
while (i < bt_size)
|
||||||
|
{
|
||||||
|
fprintf (stderr, " %s\n", syms[i]);
|
||||||
|
++i;
|
||||||
|
}
|
||||||
|
|
||||||
|
free (syms);
|
||||||
|
}
|
||||||
|
#else
|
||||||
|
static void
|
||||||
|
print_backtrace (void)
|
||||||
|
{
|
||||||
|
fprintf (stderr, "Not compiled with backtrace support\n");
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
static int error_trap_depth = 0;
|
||||||
|
|
||||||
|
static int
|
||||||
|
x_error_handler (Display *xdisplay,
|
||||||
|
XErrorEvent *error)
|
||||||
|
{
|
||||||
|
char buf[64];
|
||||||
|
|
||||||
|
XGetErrorText (xdisplay, error->error_code, buf, 63);
|
||||||
|
|
||||||
|
if (error_trap_depth == 0)
|
||||||
|
{
|
||||||
|
print_backtrace ();
|
||||||
|
|
||||||
|
fprintf (stderr, "Unexpected X error: %s serial %ld error_code %d request_code %d minor_code %d)\n",
|
||||||
|
buf,
|
||||||
|
error->serial,
|
||||||
|
error->error_code,
|
||||||
|
error->request_code,
|
||||||
|
error->minor_code);
|
||||||
|
|
||||||
|
exit (1);
|
||||||
|
}
|
||||||
|
|
||||||
|
return 1; /* return value is meaningless */
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
error_trap_push (Display *xdisplay)
|
||||||
|
{
|
||||||
|
++error_trap_depth;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
error_trap_pop (Display *xdisplay)
|
||||||
|
{
|
||||||
|
if (error_trap_depth == 0)
|
||||||
|
{
|
||||||
|
fprintf (stderr, "Error trap underflow!\n");
|
||||||
|
exit (1);
|
||||||
|
}
|
||||||
|
|
||||||
|
XSync (xdisplay, False); /* get all errors out of the queue */
|
||||||
|
--error_trap_depth;
|
||||||
|
}
|
||||||
|
|
||||||
|
static char*
|
||||||
|
my_strdup (const char *str)
|
||||||
|
{
|
||||||
|
char *s;
|
||||||
|
|
||||||
|
s = malloc (strlen (str) + 1);
|
||||||
|
if (s == NULL)
|
||||||
|
{
|
||||||
|
fprintf (stderr, "malloc failed\n");
|
||||||
|
exit (1);
|
||||||
|
}
|
||||||
|
strcpy (s, str);
|
||||||
|
|
||||||
|
return s;
|
||||||
|
}
|
||||||
|
|
||||||
|
static char*
|
||||||
|
atom_name (Display *display,
|
||||||
|
Atom atom)
|
||||||
|
{
|
||||||
|
if (atom == None)
|
||||||
|
{
|
||||||
|
return my_strdup ("None");
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
char *xname;
|
||||||
|
char *ret;
|
||||||
|
|
||||||
|
error_trap_push (display);
|
||||||
|
xname = XGetAtomName (display, atom);
|
||||||
|
error_trap_pop (display);
|
||||||
|
if (xname == NULL)
|
||||||
|
return my_strdup ("[unknown atom]");
|
||||||
|
|
||||||
|
ret = my_strdup (xname);
|
||||||
|
XFree (xname);
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
#define ELAPSED(start_time, current_time) \
|
||||||
|
(((((double)current_time.tv_sec - start_time.tv_sec) * 1000000 + \
|
||||||
|
(current_time.tv_usec - start_time.tv_usec))) / 1000.0)
|
||||||
|
|
||||||
|
static struct timeval program_start_time;
|
||||||
|
|
||||||
|
static Bool
|
||||||
|
try_get_reply (Display *xdisplay,
|
||||||
|
AgGetPropertyTask *task)
|
||||||
|
{
|
||||||
|
if (ag_task_have_reply (task))
|
||||||
|
{
|
||||||
|
int result;
|
||||||
|
Atom actual_type;
|
||||||
|
int actual_format;
|
||||||
|
unsigned long n_items;
|
||||||
|
unsigned long bytes_after;
|
||||||
|
unsigned char *data;
|
||||||
|
char *name;
|
||||||
|
struct timeval current_time;
|
||||||
|
|
||||||
|
gettimeofday (¤t_time, NULL);
|
||||||
|
|
||||||
|
printf (" %gms (we have a reply for property %ld)\n",
|
||||||
|
ELAPSED (program_start_time, current_time),
|
||||||
|
ag_task_get_property (task));
|
||||||
|
|
||||||
|
data = NULL;
|
||||||
|
|
||||||
|
name = atom_name (xdisplay,
|
||||||
|
ag_task_get_property (task));
|
||||||
|
printf (" %s on 0x%lx:\n", name,
|
||||||
|
ag_task_get_window (task));
|
||||||
|
free (name);
|
||||||
|
|
||||||
|
result = ag_task_get_reply_and_free (task,
|
||||||
|
&actual_type,
|
||||||
|
&actual_format,
|
||||||
|
&n_items,
|
||||||
|
&bytes_after,
|
||||||
|
&data);
|
||||||
|
task = NULL;
|
||||||
|
|
||||||
|
if (result != Success)
|
||||||
|
{
|
||||||
|
fprintf (stderr, " error code %d getting reply\n", result);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
name = atom_name (xdisplay, actual_type);
|
||||||
|
printf (" actual_type = %s\n", name);
|
||||||
|
free (name);
|
||||||
|
|
||||||
|
printf (" actual_format = %d\n", actual_format);
|
||||||
|
|
||||||
|
printf (" n_items = %lu\n", n_items);
|
||||||
|
printf (" bytes_after = %lu\n", bytes_after);
|
||||||
|
|
||||||
|
printf (" data = \"%s\"\n", data ? (char*) data : "NULL");
|
||||||
|
}
|
||||||
|
|
||||||
|
return True;
|
||||||
|
}
|
||||||
|
|
||||||
|
return False;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void run_speed_comparison (Display *xdisplay,
|
||||||
|
Window window);
|
||||||
|
|
||||||
|
int
|
||||||
|
main (int argc, char **argv)
|
||||||
|
{
|
||||||
|
Display *xdisplay;
|
||||||
|
int i;
|
||||||
|
int n_left;
|
||||||
|
int n_props;
|
||||||
|
Window window;
|
||||||
|
const char *window_str;
|
||||||
|
char *end;
|
||||||
|
Atom *props;
|
||||||
|
struct timeval current_time;
|
||||||
|
|
||||||
|
if (argc < 2)
|
||||||
|
{
|
||||||
|
fprintf (stderr, "specify window ID\n");
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
window_str = argv[1];
|
||||||
|
|
||||||
|
end = NULL;
|
||||||
|
window = strtoul (window_str, &end, 0);
|
||||||
|
if (end == NULL || *end != '\0')
|
||||||
|
{
|
||||||
|
fprintf (stderr, "\"%s\" does not parse as a window ID\n", window_str);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
xdisplay = XOpenDisplay (NULL);
|
||||||
|
if (xdisplay == NULL)
|
||||||
|
{
|
||||||
|
fprintf (stderr, "Could not open display\n");
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (getenv ("METACITY_SYNC") != NULL)
|
||||||
|
XSynchronize (xdisplay, True);
|
||||||
|
|
||||||
|
XSetErrorHandler (x_error_handler);
|
||||||
|
|
||||||
|
n_props = 0;
|
||||||
|
props = XListProperties (xdisplay, window, &n_props);
|
||||||
|
if (n_props == 0 || props == NULL)
|
||||||
|
{
|
||||||
|
fprintf (stderr, "Window has no properties\n");
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
gettimeofday (&program_start_time, NULL);
|
||||||
|
|
||||||
|
i = 0;
|
||||||
|
while (i < n_props)
|
||||||
|
{
|
||||||
|
gettimeofday (¤t_time, NULL);
|
||||||
|
printf (" %gms (sending request for property %ld)\n",
|
||||||
|
ELAPSED (program_start_time, current_time),
|
||||||
|
props[i]);
|
||||||
|
if (ag_task_create (xdisplay,
|
||||||
|
window, props[i],
|
||||||
|
0, 0xffffffff,
|
||||||
|
False,
|
||||||
|
AnyPropertyType) == NULL)
|
||||||
|
{
|
||||||
|
fprintf (stderr, "Failed to send request\n");
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
++i;
|
||||||
|
}
|
||||||
|
|
||||||
|
XFree (props);
|
||||||
|
props = NULL;
|
||||||
|
|
||||||
|
n_left = n_props;
|
||||||
|
|
||||||
|
while (TRUE)
|
||||||
|
{
|
||||||
|
XEvent xevent;
|
||||||
|
int connection;
|
||||||
|
fd_set set;
|
||||||
|
AgGetPropertyTask *task;
|
||||||
|
|
||||||
|
/* Mop up event queue */
|
||||||
|
while (XPending (xdisplay) > 0)
|
||||||
|
{
|
||||||
|
XNextEvent (xdisplay, &xevent);
|
||||||
|
gettimeofday (¤t_time, NULL);
|
||||||
|
printf (" %gms (processing event type %d)\n",
|
||||||
|
ELAPSED (program_start_time, current_time),
|
||||||
|
xevent.xany.type);
|
||||||
|
}
|
||||||
|
|
||||||
|
while ((task = ag_get_next_completed_task (xdisplay)))
|
||||||
|
{
|
||||||
|
try_get_reply (xdisplay, task);
|
||||||
|
n_left -= 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (n_left == 0)
|
||||||
|
{
|
||||||
|
printf ("All %d replies received.\n", n_props);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Wake up if we may have a reply */
|
||||||
|
connection = ConnectionNumber (xdisplay);
|
||||||
|
|
||||||
|
FD_ZERO (&set);
|
||||||
|
FD_SET (connection, &set);
|
||||||
|
|
||||||
|
gettimeofday (¤t_time, NULL);
|
||||||
|
printf (" %gms (blocking for data %d left)\n",
|
||||||
|
ELAPSED (program_start_time, current_time), n_left);
|
||||||
|
select (connection + 1, &set, NULL, NULL, NULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
run_speed_comparison (xdisplay, window);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* This function doesn't have all the printf's
|
||||||
|
* and other noise, it just compares async to sync
|
||||||
|
*/
|
||||||
|
static void
|
||||||
|
run_speed_comparison (Display *xdisplay,
|
||||||
|
Window window)
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
int n_props;
|
||||||
|
struct timeval start, end;
|
||||||
|
int n_left;
|
||||||
|
|
||||||
|
/* We just use atom values 0 to n_props, many are probably BadAtom,
|
||||||
|
* that's fine. The larger this number the worse the async case
|
||||||
|
* looks; my guess is it's because of display->async_handlers
|
||||||
|
* becoming a very long list. Need to modify async-getprop.c
|
||||||
|
* to install only a single async handler per display.
|
||||||
|
*/
|
||||||
|
n_props = 200;
|
||||||
|
|
||||||
|
gettimeofday (&start, NULL);
|
||||||
|
|
||||||
|
i = 0;
|
||||||
|
while (i < n_props)
|
||||||
|
{
|
||||||
|
if (ag_task_create (xdisplay,
|
||||||
|
window, (Atom) i,
|
||||||
|
0, 0xffffffff,
|
||||||
|
False,
|
||||||
|
AnyPropertyType) == NULL)
|
||||||
|
{
|
||||||
|
fprintf (stderr, "Failed to send request\n");
|
||||||
|
exit (1);
|
||||||
|
}
|
||||||
|
|
||||||
|
++i;
|
||||||
|
}
|
||||||
|
|
||||||
|
n_left = n_props;
|
||||||
|
|
||||||
|
while (TRUE)
|
||||||
|
{
|
||||||
|
int connection;
|
||||||
|
fd_set set;
|
||||||
|
XEvent xevent;
|
||||||
|
AgGetPropertyTask *task;
|
||||||
|
|
||||||
|
/* Mop up event queue */
|
||||||
|
while (XPending (xdisplay) > 0)
|
||||||
|
XNextEvent (xdisplay, &xevent);
|
||||||
|
|
||||||
|
while ((task = ag_get_next_completed_task (xdisplay)))
|
||||||
|
{
|
||||||
|
int result;
|
||||||
|
Atom actual_type;
|
||||||
|
int actual_format;
|
||||||
|
unsigned long n_items;
|
||||||
|
unsigned long bytes_after;
|
||||||
|
unsigned char *data;
|
||||||
|
|
||||||
|
assert (ag_task_have_reply (task));
|
||||||
|
|
||||||
|
data = NULL;
|
||||||
|
result = ag_task_get_reply_and_free (task,
|
||||||
|
&actual_type,
|
||||||
|
&actual_format,
|
||||||
|
&n_items,
|
||||||
|
&bytes_after,
|
||||||
|
&data);
|
||||||
|
|
||||||
|
if (data)
|
||||||
|
XFree (data);
|
||||||
|
|
||||||
|
n_left -= 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (n_left == 0)
|
||||||
|
break;
|
||||||
|
|
||||||
|
/* Wake up if we may have a reply */
|
||||||
|
connection = ConnectionNumber (xdisplay);
|
||||||
|
|
||||||
|
FD_ZERO (&set);
|
||||||
|
FD_SET (connection, &set);
|
||||||
|
|
||||||
|
select (connection + 1, &set, NULL, NULL, NULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
gettimeofday (&end, NULL);
|
||||||
|
|
||||||
|
printf ("Async time: %gms\n",
|
||||||
|
ELAPSED (start, end));
|
||||||
|
|
||||||
|
gettimeofday (&start, NULL);
|
||||||
|
|
||||||
|
error_trap_push (xdisplay);
|
||||||
|
|
||||||
|
i = 0;
|
||||||
|
while (i < n_props)
|
||||||
|
{
|
||||||
|
Atom actual_type;
|
||||||
|
int actual_format;
|
||||||
|
unsigned long n_items;
|
||||||
|
unsigned long bytes_after;
|
||||||
|
unsigned char *data;
|
||||||
|
|
||||||
|
data = NULL;
|
||||||
|
if (XGetWindowProperty (xdisplay, window,
|
||||||
|
(Atom) i,
|
||||||
|
0, 0xffffffff,
|
||||||
|
False,
|
||||||
|
AnyPropertyType,
|
||||||
|
&actual_type,
|
||||||
|
&actual_format,
|
||||||
|
&n_items,
|
||||||
|
&bytes_after,
|
||||||
|
&data) == Success)
|
||||||
|
{
|
||||||
|
if (data)
|
||||||
|
XFree (data);
|
||||||
|
}
|
||||||
|
|
||||||
|
++i;
|
||||||
|
}
|
||||||
|
|
||||||
|
error_trap_pop (xdisplay);
|
||||||
|
|
||||||
|
gettimeofday (&end, NULL);
|
||||||
|
|
||||||
|
printf ("Sync time: %gms\n",
|
||||||
|
ELAPSED (start, end));
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user