async-getprop.c

Go to the documentation of this file.
00001 /* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
00002 
00003 /* Asynchronous X property getting hack */
00004 
00005 /*
00006  * Copyright (C) 2002 Havoc Pennington
00007  * Copyright (C) 1986, 1998  The Open Group
00008  *
00009  * Permission to use, copy, modify, distribute, and sell this software
00010  * and its documentation for any purpose is hereby granted without
00011  * fee, provided that the above copyright notice appear in all copies
00012  * and that both that copyright notice and this permission notice
00013  * appear in supporting documentation.
00014  *
00015  * The above copyright notice and this permission notice shall be
00016  * included in all copies or substantial portions of the Software.
00017  *
00018  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
00019  * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
00020  * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
00021  * NONINFRINGEMENT.  IN NO EVENT SHALL THE OPEN GROUP BE LIABLE FOR
00022  * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF
00023  * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
00024  * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
00025  *
00026  * Except as contained in this notice, the name of The Open Group shall not be
00027  * used in advertising or otherwise to promote the sale, use or other dealings
00028  * in this Software without prior written authorization from The Open Group.
00029  */
00030 
00031 #include <assert.h>
00032 
00033 #undef DEBUG_SPEW
00034 #ifdef DEBUG_SPEW
00035 #include <stdio.h>
00036 #endif
00037 
00038 #include "async-getprop.h"
00039 
00040 #define NEED_REPLIES
00041 #include <X11/Xlibint.h>
00042 
00043 #ifndef NULL
00044 #define NULL ((void*)0)
00045 #endif
00046 
00047 typedef struct _ListNode ListNode;
00048 typedef struct _AgPerDisplayData AgPerDisplayData;
00049 
00050 struct _ListNode
00051 {
00052   ListNode *next;
00053 };
00054 
00055 struct _AgGetPropertyTask
00056 {
00057   ListNode node;
00058   
00059   AgPerDisplayData *dd;
00060   Window window;
00061   Atom property;
00062 
00063   unsigned long request_seq;
00064   int error;
00065 
00066   Atom actual_type;
00067   int  actual_format;
00068 
00069   unsigned long  n_items;
00070   unsigned long  bytes_after;
00071   char          *data;
00072 
00073   Bool have_reply;
00074 };
00075 
00076 struct _AgPerDisplayData
00077 {
00078   ListNode node;
00079   _XAsyncHandler async;
00080   
00081   Display *display;
00082   ListNode *pending_tasks;
00083   ListNode *pending_tasks_tail;
00084   ListNode *completed_tasks;
00085   ListNode *completed_tasks_tail;
00086   int n_tasks_pending;
00087   int n_tasks_completed;
00088 };
00089 
00090 static ListNode *display_datas = NULL;
00091 static ListNode *display_datas_tail = NULL;
00092 
00093 static void
00094 append_to_list (ListNode **head,
00095                 ListNode **tail,
00096                 ListNode  *task)
00097 {
00098   task->next = NULL;
00099   
00100   if (*tail == NULL)
00101     {
00102       assert (*head == NULL);
00103       *head = task;
00104       *tail = task;
00105     }
00106   else
00107     {
00108       (*tail)->next = task;
00109       *tail = task;
00110     }
00111 }
00112 
00113 static void
00114 remove_from_list (ListNode **head,
00115                   ListNode **tail,
00116                   ListNode  *task)
00117 {
00118   ListNode *prev;
00119   ListNode *node;
00120 
00121   prev = NULL;
00122   node = *head;
00123   while (node != NULL)
00124     {
00125       if (node == task)
00126         {
00127           if (prev)
00128             prev->next = node->next;
00129           else
00130             *head = node->next;
00131 
00132           if (node == *tail)
00133             *tail = prev;
00134           
00135           break;
00136         }
00137       
00138       prev = node;
00139       node = node->next;
00140     }
00141 
00142   /* can't remove what's not there */
00143   assert (node != NULL);
00144 
00145   node->next = NULL;
00146 }
00147 
00148 static void
00149 move_to_completed (AgPerDisplayData  *dd,
00150                    AgGetPropertyTask *task)
00151 {
00152   remove_from_list (&dd->pending_tasks,
00153                     &dd->pending_tasks_tail,
00154                     &task->node);
00155   
00156   append_to_list (&dd->completed_tasks,
00157                   &dd->completed_tasks_tail,
00158                   &task->node);
00159 
00160   dd->n_tasks_pending -= 1;
00161   dd->n_tasks_completed += 1;
00162 }
00163 
00164 static AgGetPropertyTask*
00165 find_pending_by_request_sequence (AgPerDisplayData *dd,
00166                                   unsigned long     request_seq)
00167 {
00168   ListNode *node;
00169 
00170   /* if the sequence is after our last pending task, we
00171    * aren't going to find a match
00172    */
00173   {
00174     AgGetPropertyTask *task = (AgGetPropertyTask*) dd->pending_tasks_tail;
00175     if (task != NULL)
00176       {
00177         if (task->request_seq < request_seq)
00178           return NULL;
00179         else if (task->request_seq == request_seq)
00180           return task; /* why not check this */
00181       }
00182   }
00183 
00184   /* Generally we should get replies in the order we sent
00185    * requests, so we should usually be using the task
00186    * at the head of the list, if we use any task at all.
00187    * I'm not sure this is 100% guaranteed, if it is,
00188    * it would be a big speedup.
00189    */
00190   
00191   node = dd->pending_tasks;
00192   while (node != NULL)
00193     {
00194       AgGetPropertyTask *task = (AgGetPropertyTask*) node;
00195       
00196       if (task->request_seq == request_seq)
00197         return task;
00198       
00199       node = node->next;
00200     }
00201   
00202   return NULL;
00203 }
00204 
00205 static Bool
00206 async_get_property_handler (Display *dpy,
00207                             xReply  *rep,
00208                             char    *buf,
00209                             int      len,
00210                             XPointer data)
00211 {
00212   xGetPropertyReply  replbuf;
00213   xGetPropertyReply *reply;
00214   AgGetPropertyTask *task;
00215   AgPerDisplayData *dd;
00216   int bytes_read;
00217 
00218   dd = (AgPerDisplayData*) data;
00219   
00220 #if 0
00221   printf ("%s: seeing request seq %ld buflen %d\n", __FUNCTION__,
00222           dpy->last_request_read, len);
00223 #endif
00224   
00225   task = find_pending_by_request_sequence (dd, dpy->last_request_read);
00226 
00227   if (task == NULL)
00228     return False;
00229 
00230   assert (dpy->last_request_read == task->request_seq);
00231 
00232   task->have_reply = True;
00233   move_to_completed (dd, task);
00234   
00235   /* read bytes so far */
00236   bytes_read = SIZEOF (xReply);
00237 
00238   if (rep->generic.type == X_Error)
00239     {
00240       xError errbuf;
00241 
00242       task->error = rep->error.errorCode;
00243       
00244 #ifdef DEBUG_SPEW
00245       printf ("%s: error code = %d (ignoring error, eating %d bytes, generic.length = %ld)\n",
00246               __FUNCTION__, task->error, (SIZEOF (xError) - bytes_read),
00247               rep->generic.length);
00248 #endif
00249 
00250       /* We return True (meaning we consumed the reply)
00251        * because otherwise it would invoke the X error handler,
00252        * and an async API is useless if you have to synchronously
00253        * trap X errors. Also GetProperty can always fail, pretty
00254        * much, so trapping errors is always what you want.
00255        *
00256        * We have to eat all the error reply data here.
00257        * (kind of a charade as we know sizeof(xError) == sizeof(xReply))
00258        *
00259        * Passing discard = True seems to break things; I don't understand
00260        * why, because there should be no extra data in an error reply,
00261        * right?
00262        */
00263       _XGetAsyncReply (dpy, (char *)&errbuf, rep, buf, len,
00264                        (SIZEOF (xError) - bytes_read) >> 2, /* in 32-bit words */
00265                        False); /* really seems like it should be True */
00266       
00267       return True;
00268     }
00269 
00270 #ifdef DEBUG_SPEW
00271   printf ("%s: already read %d bytes reading %d more for total of %d; generic.length = %ld\n",
00272           __FUNCTION__, bytes_read, (SIZEOF (xGetPropertyReply) - bytes_read) >> 2,
00273           SIZEOF (xGetPropertyReply), rep->generic.length);
00274 #endif
00275 
00276   /* (kind of a silly as we know sizeof(xGetPropertyReply) == sizeof(xReply)) */
00277   reply = (xGetPropertyReply *)
00278     _XGetAsyncReply (dpy, (char *)&replbuf, rep, buf, len,
00279                      (SIZEOF (xGetPropertyReply) - bytes_read) >> 2, /* in 32-bit words */
00280                      False); /* False means expecting more data to follow,
00281                               * don't eat the rest of the reply
00282                               */
00283 
00284   bytes_read = SIZEOF (xGetPropertyReply);
00285 
00286 #ifdef DEBUG_SPEW
00287   printf ("%s: have reply propertyType = %ld format = %d n_items = %ld\n",
00288           __FUNCTION__, reply->propertyType, reply->format, reply->nItems);
00289 #endif
00290 
00291   assert (task->data == NULL);
00292 
00293   /* This is all copied from XGetWindowProperty().  Not sure we should
00294    * LockDisplay(). Not sure I'm passing the right args to
00295    * XGetAsyncData(). Not sure about a lot of things.
00296    */
00297 
00298   /* LockDisplay (dpy); */
00299 
00300   if (reply->propertyType != None)
00301     {
00302       long nbytes, netbytes;
00303 
00304       /* this alignment macro from orbit2 */
00305 #define ALIGN_VALUE(this, boundary) \
00306   (( ((unsigned long)(this)) + (((unsigned long)(boundary)) -1)) & (~(((unsigned long)(boundary))-1)))
00307 
00308       switch (reply->format)
00309         {
00310           /*
00311            * One extra byte is malloced than is needed to contain the property
00312            * data, but this last byte is null terminated and convenient for
00313            * returning string properties, so the client doesn't then have to
00314            * recopy the string to make it null terminated.
00315            */
00316         case 8:
00317           nbytes = reply->nItems;
00318           /* there's padding to word boundary */
00319           netbytes = ALIGN_VALUE (nbytes, 4);
00320           if (nbytes + 1 > 0 &&
00321               (task->data = (char *) Xmalloc ((unsigned)nbytes + 1)))
00322             {
00323 #ifdef DEBUG_SPEW
00324               printf ("%s: already read %d bytes using %ld, more eating %ld more\n",
00325                       __FUNCTION__, bytes_read, nbytes, netbytes);
00326 #endif
00327               /* _XReadPad (dpy, (char *) task->data, netbytes); */
00328               _XGetAsyncData (dpy, task->data, buf, len,
00329                               bytes_read, nbytes,
00330                               netbytes);
00331             }
00332           break;
00333 
00334         case 16:
00335           nbytes = reply->nItems * sizeof (short);
00336           netbytes = reply->nItems << 1;
00337           netbytes = ALIGN_VALUE (netbytes, 4); /* align to word boundary */
00338           if (nbytes + 1 > 0 &&
00339               (task->data = (char *) Xmalloc ((unsigned)nbytes + 1)))
00340             {
00341 #ifdef DEBUG_SPEW
00342               printf ("%s: already read %d bytes using %ld more, eating %ld more\n",
00343                       __FUNCTION__, bytes_read, nbytes, netbytes);
00344 #endif
00345               /* _XRead16Pad (dpy, (short *) task->data, netbytes); */
00346               _XGetAsyncData (dpy, task->data, buf, len,
00347                               bytes_read, nbytes, netbytes);
00348             }
00349           break;
00350 
00351         case 32:
00352           /* NOTE buffer is in longs to match XGetWindowProperty() */
00353           nbytes = reply->nItems * sizeof (long);
00354           netbytes = reply->nItems << 2; /* wire size is always 32 bits though */
00355           if (nbytes + 1 > 0 &&
00356               (task->data = (char *) Xmalloc ((unsigned)nbytes + 1)))
00357             {
00358 #ifdef DEBUG_SPEW
00359               printf ("%s: already read %d bytes using %ld more, eating %ld more\n",
00360                       __FUNCTION__, bytes_read, nbytes, netbytes);
00361 #endif
00362 
00363               /* We have to copy the XGetWindowProperty() crackrock
00364                * and get format 32 as long even on 64-bit platforms.
00365                */
00366               if (sizeof (long) == 8)
00367                 {
00368                   char *netdata;
00369                   char *lptr;
00370                   char *end_lptr;
00371                   
00372                   /* Store the 32-bit values in the end of the array */
00373                   netdata = task->data + nbytes / 2;
00374 
00375                   _XGetAsyncData (dpy, netdata, buf, len,
00376                                   bytes_read, netbytes,
00377                                   netbytes);
00378 
00379                   /* Now move the 32-bit values to the front */
00380                   
00381                   lptr = task->data;
00382                   end_lptr = task->data + nbytes;
00383                   while (lptr != end_lptr)
00384                     {
00385                       *(long*) lptr = *(CARD32*) netdata;
00386                       lptr += sizeof (long);
00387                       netdata += sizeof (CARD32);
00388                     }
00389                 }
00390               else
00391                 {
00392                   /* Here the wire format matches our actual format */
00393                   _XGetAsyncData (dpy, task->data, buf, len,
00394                                   bytes_read, netbytes,
00395                                   netbytes);
00396                 }
00397             }
00398           break;
00399 
00400         default:
00401           /*
00402            * This part of the code should never be reached.  If it is,
00403            * the server sent back a property with an invalid format.
00404            * This is a BadImplementation error.
00405            *
00406            * However this async GetProperty API doesn't report errors
00407            * via the standard X mechanism, so don't do anything about
00408            * it, other than store it in task->error.
00409            */
00410           {
00411 #if 0
00412             xError error;
00413 #endif
00414             
00415             task->error = BadImplementation;
00416 
00417 #if 0
00418             error.sequenceNumber = task->request_seq;
00419             error.type = X_Error;
00420             error.majorCode = X_GetProperty;
00421             error.minorCode = 0;
00422             error.errorCode = BadImplementation;
00423 
00424             _XError (dpy, &error);
00425 #endif
00426           }
00427 
00428           nbytes = netbytes = 0L;
00429           break;
00430         }
00431 
00432       if (task->data == NULL)
00433         {
00434           task->error = BadAlloc;
00435 
00436 #ifdef DEBUG_SPEW
00437           printf ("%s: already read %d bytes eating %ld\n",
00438                   __FUNCTION__, bytes_read, netbytes);
00439 #endif
00440           /* _XEatData (dpy, (unsigned long) netbytes); */
00441           _XGetAsyncData (dpy, NULL, buf, len,
00442                           bytes_read, 0, netbytes);
00443 
00444           /* UnlockDisplay (dpy); */
00445           return BadAlloc; /* not Success */
00446         }
00447 
00448       (task->data)[nbytes] = '\0';
00449     }
00450 
00451 #ifdef DEBUG_SPEW
00452   printf ("%s: have data\n", __FUNCTION__);
00453 #endif
00454 
00455   task->actual_type = reply->propertyType;
00456   task->actual_format = reply->format;
00457   task->n_items = reply->nItems;
00458   task->bytes_after = reply->bytesAfter;
00459 
00460   /* UnlockDisplay (dpy); */
00461 
00462   return True;
00463 }
00464 
00465 static AgPerDisplayData*
00466 get_display_data (Display *display,
00467                   Bool     create)
00468 {
00469   ListNode *node;
00470   AgPerDisplayData *dd;
00471   
00472   node = display_datas;
00473   while (node != NULL)
00474     {
00475       dd = (AgPerDisplayData*) node;
00476       
00477       if (dd->display == display)
00478         return dd;
00479       
00480       node = node->next;
00481     }
00482 
00483   if (!create)
00484     return NULL;
00485   
00486   dd = Xcalloc (1, sizeof (AgPerDisplayData));
00487   if (dd == NULL)
00488     return NULL;
00489 
00490   dd->display = display;
00491   dd->async.next = display->async_handlers;
00492   dd->async.handler = async_get_property_handler;
00493   dd->async.data = (XPointer) dd;
00494   dd->display->async_handlers = &dd->async;
00495 
00496   append_to_list (&display_datas,
00497                   &display_datas_tail,
00498                   &dd->node);
00499   
00500   return dd;
00501 }
00502 
00503 static void
00504 maybe_free_display_data (AgPerDisplayData *dd)
00505 {
00506   if (dd->pending_tasks == NULL &&
00507       dd->completed_tasks == NULL)
00508     {
00509       DeqAsyncHandler (dd->display, &dd->async);
00510       remove_from_list (&display_datas, &display_datas_tail,
00511                         &dd->node);
00512       XFree (dd);
00513     }
00514 }
00515 
00516 AgGetPropertyTask*
00517 ag_task_create (Display *dpy,
00518                 Window   window,
00519                 Atom     property,
00520                 long     offset,
00521                 long     length,
00522                 Bool     delete,
00523                 Atom     req_type)
00524 {
00525   AgGetPropertyTask *task;
00526   xGetPropertyReq *req;
00527   AgPerDisplayData *dd;  
00528   
00529   /* Fire up our request */
00530   LockDisplay (dpy);
00531 
00532   dd = get_display_data (dpy, True);
00533   if (dd == NULL)
00534     {
00535       UnlockDisplay (dpy);
00536       return NULL;
00537     }
00538   
00539   GetReq (GetProperty, req);
00540   req->window = window;
00541   req->property = property;
00542   req->type = req_type;
00543   req->delete = delete;
00544   req->longOffset = offset;
00545   req->longLength = length;
00546 
00547   /* Queue up our async task */
00548   task = Xcalloc (1, sizeof (AgGetPropertyTask));
00549   if (task == NULL)
00550     {
00551       UnlockDisplay (dpy);
00552       return NULL;
00553     }
00554 
00555   task->dd = dd;
00556   task->window = window;
00557   task->property = property;
00558   task->request_seq = dpy->request;
00559 
00560   append_to_list (&dd->pending_tasks,
00561                   &dd->pending_tasks_tail,
00562                   &task->node);
00563   dd->n_tasks_pending += 1;
00564   
00565   UnlockDisplay (dpy);
00566 
00567   SyncHandle ();
00568 
00569   return task;
00570 }
00571 
00572 static void
00573 free_task (AgGetPropertyTask *task)
00574 {
00575   remove_from_list (&task->dd->completed_tasks,
00576                     &task->dd->completed_tasks_tail,
00577                     &task->node);
00578   task->dd->n_tasks_completed -= 1;
00579   maybe_free_display_data (task->dd);
00580   XFree (task);
00581 }
00582 
00583 Status
00584 ag_task_get_reply_and_free (AgGetPropertyTask  *task,
00585                             Atom               *actual_type,
00586                             int                *actual_format,
00587                             unsigned long      *nitems,
00588                             unsigned long      *bytesafter,
00589                             char              **prop)
00590 {
00591   Display *dpy;
00592 
00593   *prop = NULL;
00594 
00595   dpy = task->dd->display; /* Xlib macros require a variable named "dpy" */
00596   
00597   if (task->error != Success)
00598     {
00599       Status s = task->error;
00600 
00601       free_task (task);
00602       
00603       return s;
00604     }
00605 
00606   if (!task->have_reply)
00607     {
00608       free_task (task);
00609 
00610       return BadAlloc; /* not Success */
00611     }
00612 
00613   *actual_type = task->actual_type;
00614   *actual_format = task->actual_format;
00615   *nitems = task->n_items;
00616   *bytesafter = task->bytes_after;
00617 
00618   *prop = task->data; /* pass out ownership of task->data */
00619 
00620   SyncHandle ();
00621 
00622   free_task (task);
00623   
00624   return Success;
00625 }
00626 
00627 Bool
00628 ag_task_have_reply (AgGetPropertyTask *task)
00629 {
00630   return task->have_reply;
00631 }
00632 
00633 Atom
00634 ag_task_get_property (AgGetPropertyTask *task)
00635 {
00636   return task->property;
00637 }
00638 
00639 Window
00640 ag_task_get_window (AgGetPropertyTask *task)
00641 {
00642   return task->window;
00643 }
00644 
00645 Display*
00646 ag_task_get_display (AgGetPropertyTask *task)
00647 {
00648   return task->dd->display;
00649 }
00650 
00651 AgGetPropertyTask*
00652 ag_get_next_completed_task (Display *display)
00653 {
00654   AgPerDisplayData *dd;
00655 
00656   dd = get_display_data (display, False);
00657 
00658   if (dd == NULL)
00659     return NULL;
00660   
00661 #ifdef DEBUG_SPEW
00662   printf ("%d pending %d completed\n",
00663           dd->n_tasks_pending,
00664           dd->n_tasks_completed);
00665 #endif
00666 
00667   return (AgGetPropertyTask*) dd->completed_tasks;
00668 }
00669 
00670 void*
00671 ag_Xmalloc (unsigned long bytes)
00672 {
00673   return (void*) Xmalloc (bytes);
00674 }
00675 
00676 void*
00677 ag_Xmalloc0 (unsigned long bytes)
00678 {
00679   return (void*) Xcalloc (bytes, 1);
00680 }

Generated on Sat Aug 23 22:04:16 2008 for metacity by  doxygen 1.5.5