home *** CD-ROM | disk | FTP | other *** search
/ Celestin Apprentice 2 / Apprentice-Release2.iso / Source Code / C / Libraries / stdwin / Ports / x11 / selection.c < prev    next >
Encoding:
C/C++ Source or Header  |  1991-04-03  |  8.2 KB  |  374 lines  |  [TEXT/????]

  1. /* Selection Interface, a la ICCCM */
  2.  
  3.  
  4. /*
  5.     Features:
  6.     
  7.     - supports CLIPBOARD, PRIMARY and SECONDARY selections
  8.     - only supports STRING as selection type
  9.     - XXX no required funny targets TARGETS, MULTIPLE, TIMESTAMP
  10.     - no side effect targets DELETE, INSERT_*
  11.     - no INCR targets (XXX should accept these when receiving?)
  12.     - XXX truncates large selections to 32 K
  13.     - fixed timeout 1 second or 10 requests
  14.     
  15.     XXX To do:
  16.     
  17.     - delete selection data when we delete its window
  18.     - report the selection's window in WE_LOST_SEL events
  19. */
  20.  
  21.  
  22. #include "x11.h"
  23. #include "llevent.h" /* For _w_lasttime; */
  24.  
  25. #ifdef _IBMR2
  26. #include <sys/select.h>
  27. #endif
  28.  
  29.  
  30. #ifndef AMOEBA
  31.  
  32. /* Provide default definitions for FD_ZERO and FD_SET */
  33.  
  34. #ifndef FD_ZERO
  35. #define FD_ZERO(p)    bzero((char *)(p), sizeof(*(p)))
  36. #endif
  37.  
  38. #ifndef NFDBITS
  39. #define NFDBITS        (sizeof(long)*8)    /* Assume 8 bits/byte */
  40. #endif
  41.  
  42. #ifndef FD_SET
  43. #define FD_SET(n, p)    ((p)->fds_bits[(n)/NFDBITS] |= (1 << ((n) % NFDBITS)))
  44. #endif
  45.  
  46. #endif /* AMOEBA */
  47.  
  48.  
  49. /* Data structure describing what we know about each selection */
  50.  
  51. struct seldescr {
  52.     Atom atom;
  53.     char *data;
  54.     int len;
  55.     Time time;
  56.     int lost;
  57. };
  58.  
  59. static struct seldescr seldata[] = {
  60.     {None,        NULL,    0,    CurrentTime,    0},    /* CLIPBOARD */
  61.     {XA_PRIMARY,    NULL,    0,    CurrentTime,    0},
  62.     {XA_SECONDARY,    NULL,    0,    CurrentTime,    0},
  63. };
  64.  
  65. #define NSEL ( sizeof(seldata) / sizeof(seldata[0]) )
  66.  
  67. static void
  68. initseldata()
  69. {
  70.     /* The CLIPBOARD atom is not predefined, so intern it here... */
  71.     if (seldata[0].atom == None)
  72.         seldata[0].atom = XInternAtom(_wd, "CLIPBOARD", False);
  73. }
  74.  
  75.  
  76. /* Attempt to acquire selection ownership.  Return nonzero if success. */
  77.  
  78. int
  79. wsetselection(win, sel, data, len)
  80.     WINDOW *win;
  81.     int sel; /* WS_CLIPBOARD, WS_PRIMARY or WS_SECONDARY */
  82.     char *data;
  83.     int len;
  84. {
  85.     Window owner;
  86.     initseldata();
  87.     if (sel < 0 || sel >= NSEL) {
  88.         _wwarning("wsetselection: invalid selection number %d", sel);
  89.         return 0;
  90.     }
  91.     seldata[sel].lost = 0;
  92.     XSetSelectionOwner(_wd, seldata[sel].atom, win->wo.wid, _w_lasttime);
  93.     owner = XGetSelectionOwner(_wd, seldata[sel].atom);
  94.     if (owner != win->wo.wid)
  95.         return 0; /* Didn't get it */
  96.     /* Squirrel away the data value for other clients */
  97.     seldata[sel].len = 0;
  98.     seldata[sel].time = CurrentTime;
  99.     if (seldata[sel].data != NULL)
  100.         free(seldata[sel].data);
  101.     seldata[sel].data = malloc(len+1);
  102.     if (seldata[sel].data == NULL) {
  103.         _wwarning("wsetselection: no mem for data (%d bytes)", len);
  104.         return 0;
  105.     }
  106.     memcpy(seldata[sel].data, data, len);
  107.     seldata[sel].data[len] = '\0';
  108.                 /* Trailing NULL byte for wgetselection */
  109.     seldata[sel].len = len;
  110.     seldata[sel].time = _w_lasttime;
  111.     return 1;
  112. }
  113.  
  114.  
  115. /* Give up selection ownership */
  116.  
  117. void
  118. wresetselection(sel)
  119.     int sel;
  120. {
  121.     if (sel < 0 || sel >= NSEL) {
  122.         _wwarning("wresetselection: invalid selection number %d", sel);
  123.         return;
  124.     }
  125.     seldata[sel].lost = 0;
  126.     if (seldata[sel].data != NULL) {
  127.         XSetSelectionOwner(_wd, seldata[sel].atom, None,
  128.                             seldata[sel].time);
  129.         free(seldata[sel].data);
  130.         seldata[sel].data = NULL;
  131.         seldata[sel].len = 0;
  132.         seldata[sel].len = CurrentTime;
  133.     }
  134. }
  135.  
  136.  
  137. /* Attempt to get the value of a selection */
  138.  
  139. char *
  140. wgetselection(sel, len_return)
  141.     int sel;
  142.     int *len_return;
  143. {
  144.     static char *prop;
  145.     WINDOW *win;
  146.     XEvent e;
  147.     int i;
  148.     
  149.     initseldata();
  150.     
  151.     /* Free data retrieved last time */
  152.     if (prop != NULL) {
  153.         XFree(prop);
  154.         prop = NULL;
  155.     }
  156.     
  157.     /* Check selection code */
  158.     if (sel < 0 || sel >= NSEL) {
  159.         _wwarning("wgetselection: invalid selection number %d", sel);
  160.         return NULL;
  161.     }
  162.     
  163.     /* We may own this selection ourself... */
  164.     if (seldata[sel].data != NULL) {
  165.         _wdebug(2, "wgetselection: we have it");
  166.         *len_return = seldata[sel].len;
  167.         return seldata[sel].data;
  168.     }
  169.     
  170.     /* Have to ask some other client (the selection owner) */
  171.     win = _w_get_last_active();
  172.     if (win == NULL) {
  173.         _wwarning("wgetselection: no window");
  174.         return NULL;
  175.     }
  176.     
  177.     /* Tell the server to ask the selection owner */
  178.     XConvertSelection(_wd,
  179.         seldata[sel].atom,    /* selection */
  180.         XA_STRING,        /* target */
  181.         XA_STRING,        /* property (why not?) */
  182.         win->wo.wid,        /* window */
  183.         _w_lasttime);        /* time */
  184.     
  185.     /* Now wait for a SelectionNotify event -- 10 times */
  186.     _wdebug(2, "waiting for SelectionNotify");
  187.     for (i = 0; i < 10; i++) {
  188.         e.xselection.property = None;
  189.         if (XCheckTypedWindowEvent(_wd, win->wo.wid,
  190.                         SelectionNotify, &e)) {
  191.             _wdebug(2, "got a SelectionNotify");
  192.             break;
  193.         }
  194.         else {
  195. #ifdef AMOEBA
  196.             /* XXX For now, just sleep a bit -- there is a
  197.                routine to do this, though */
  198.             millisleep(100);
  199. #else /* !AMOEBA */
  200.             /* Use select */
  201.             /* SGI/SYSV interface */
  202.             fd_set readfds;
  203.             struct timeval timeout;
  204.             int nfound;
  205.             FD_ZERO(&readfds);
  206.             FD_SET(_wd->fd, &readfds);
  207.             timeout.tv_sec = 0;
  208.             timeout.tv_usec = 100000;
  209.             nfound = select(_wd->fd+1, &readfds,
  210.                 (fd_set *)NULL, (fd_set *)NULL, &timeout);
  211.             if (nfound < 0)
  212.                 _wwarning("select failed, errno %d", errno);
  213.             else
  214.                 _wdebug(3, "select: nfound=%d\n", nfound);
  215. #endif /* !AMOEBA */
  216.         }
  217.     }
  218.     
  219.     if (e.xselection.property != None) {
  220.         /* Got a reply, now fetch the property */
  221.         int status;
  222.         Atom actual_type;
  223.         int actual_format;
  224.         unsigned long nitems;
  225.         unsigned long bytes_after;
  226.         status = XGetWindowProperty(_wd,
  227.             win->wo.wid,
  228.             e.xselection.property,
  229.             0L,
  230.             8192L, /* Times sizeof(long) is 32K ! */
  231.             True,
  232.             AnyPropertyType,
  233.             &actual_type,
  234.             &actual_format,
  235.             &nitems,
  236.             &bytes_after,
  237.             (unsigned char **)&prop);
  238.         if (status != Success) {
  239.             _wdebug(1, "XGetWindowProperty failed (%d)", status);
  240.             return NULL;
  241.         }
  242.         if (bytes_after != 0) {
  243.             _wwarning("truncated %d property bytes", bytes_after);
  244.             /* XXX should fetch the rest as well */
  245.             XDeleteProperty(_wd, win->wo.wid,
  246.                         e.xselection.property);
  247.         }
  248.         if (actual_type != XA_STRING) {
  249.             _wdebug(1, "bad property type: 0x%lx", actual_type);
  250.             if (prop != NULL) {
  251.                 XFree(prop);
  252.                 prop = NULL;
  253.             }
  254.             return NULL;
  255.         }
  256.         *len_return = nitems * (actual_format/8);
  257.         _wdebug(2, "got a selection, %d bytes", *len_return);
  258.         return prop;
  259.         /* Will be freed next time this function is called */
  260.     }
  261.     
  262.     _wdebug(1, "SelectionNotify with property=None");
  263.     return NULL;
  264. }
  265.  
  266.  
  267. /* Handle a SelectionClear event -- called from llevent.c */
  268.  
  269. void
  270. _w_selectionclear(selection)
  271.     Atom selection;
  272. {
  273.     int sel;
  274.     
  275.     _wdebug(2, "clearing a selection");
  276.     initseldata();
  277.     
  278.     for (sel = 0; sel < NSEL; sel++) {
  279.         if (seldata[sel].atom == selection) {
  280.             wresetselection(sel);
  281.             seldata[sel].lost = 1;
  282.             break;
  283.         }
  284.     }
  285. }
  286.  
  287.  
  288. /* Generate a WE_LOST_SEL event if one is pending -- called from event.c */
  289.  
  290. int
  291. _w_lostselection(ep)
  292.     EVENT *ep;
  293. {
  294.     int sel;
  295.     
  296.     for (sel = 0; sel < NSEL; sel++) {
  297.         if (seldata[sel].lost) {
  298.             seldata[sel].lost = 0;
  299.             ep->type = WE_LOST_SEL;
  300.             /* XXX Should we report the window?
  301.                This is not easily available. */
  302.             ep->window = NULL;
  303.             ep->u.sel = sel;
  304.             return 1;
  305.         }
  306.     }
  307.     return 0;
  308. }
  309.  
  310.  
  311. /* Reply to a SelectionRequest event -- called from llevent.c */
  312.  
  313. void
  314. _w_selectionreply(owner, requestor, selection, target, property, time)
  315.     Window owner;
  316.     Window requestor;
  317.     Atom selection;
  318.     Atom target;
  319.     Atom property;
  320.     Time time;
  321. {
  322.     XSelectionEvent e;
  323.     int sel;
  324.     
  325.     _wdebug(2, "replying to selection request");
  326.     initseldata();
  327.     
  328.     /* Fill in the reply event */
  329.     e.type = SelectionNotify;
  330.     e.requestor = requestor;
  331.     e.selection = selection;
  332.     e.target = target;
  333.     e.property = None; /* Replaced by property type if OK */
  334.     e.time = time;
  335.     
  336.     if (property == None)
  337.         property = target; /* Obsolete client */
  338.     
  339.     for (sel = 0; sel < NSEL; sel++) {
  340.         if (seldata[sel].atom == selection) {
  341.             /* It's this selection */
  342.             if (seldata[sel].data != NULL &&
  343.                 (time == CurrentTime ||
  344.                 seldata[sel].time == CurrentTime ||
  345.                 ((long)time - (long)seldata[sel].time) >= 0)) {
  346.                 
  347.                 /* And we have it.  Store it as a property
  348.                    on the requestor window.  The requestor
  349.                    will eventually delete it (says ICCCM). */
  350.                 
  351.                 /* XXX Always convert to STRING, OK? */
  352.                 XChangeProperty(_wd,
  353.                     requestor,
  354.                     property,
  355.                     XA_STRING,
  356.                     8,
  357.                     PropModeReplace,
  358.                     (unsigned char *)seldata[sel].data,
  359.                     seldata[sel].len);
  360.                 /* Tell requestor we stored the property */
  361.                 e.property = property;
  362.             }
  363.             else {
  364.                 /* But we don't have it or the time is wrong */
  365.                 _wdebug(1, "stale selection request");
  366.             }
  367.             break;
  368.         }
  369.     }
  370.     
  371.     if (!XSendEvent(_wd, requestor, False, 0L, (XEvent*)&e))
  372.         _wdebug(1, "XSendEvent failed");
  373. }
  374.