X11workbench Toolkit  1.0
clipboard_helper.c
Go to the documentation of this file.
1 // //
3 // _ _ _ _ _ _ //
4 // ___ | |(_) _ __ | |__ ___ __ _ _ __ __| | | |__ ___ | | _ __ ___ _ __ ___ //
5 // / __|| || || '_ \ | '_ \ / _ \ / _` || '__|/ _` | | '_ \ / _ \| || '_ \ / _ \| '__|/ __| //
6 // | (__ | || || |_) || |_) || (_) || (_| || | | (_| | | | | || __/| || |_) || __/| | _| (__ //
7 // \___||_||_|| .__/ |_.__/ \___/ \__,_||_| \__,_|_____|_| |_| \___||_|| .__/ \___||_|(_)\___| //
8 // |_| |_____| |_| //
9 // //
10 // internal clipboard support //
11 // //
13 
14 /*****************************************************************************
15 
16  X11workbench - X11 programmer's 'work bench' application and toolkit
17  Copyright (c) 2010-2019 by Bob Frazier (aka 'Big Bad Bombastic Bob')
18 
19 
20  DISCLAIMER: The X11workbench application and toolkit software are supplied
21  'as-is', with no warranties, either implied or explicit.
22  Any claims to alleged functionality or features should be
23  considered 'preliminary', and might not function as advertised.
24 
25  MIT-like license:
26 
27  There is no restriction as to what you can do with this software, so long
28  as you include the above copyright notice and DISCLAIMER for any distributed
29  work that is equal to or derived from this one, along with this paragraph
30  that explains the terms of the license if the source is also being made
31  available. A "derived work" describes a work that uses a significant portion
32  of the source files or algorithms that are included with this one.
33  Specifically excluded from this are files that were generated by the software,
34  or anything that is included with the software that is part of another package
35  (such as files that were created or added during the 'configure' process).
36  Specifically included is the use of part or all of any of the X11 workbench
37  toolkit source or header files in your distributed application. If you do not
38  ship the source, the above copyright statement is still required to be placed
39  in a reasonably prominent place, such as documentation, splash screens, and/or
40  'about the application' dialog boxes.
41 
42  Use and distribution are in accordance with GPL, LGPL, and/or the above
43  MIT-like license. See COPYING and README files for more information.
44 
45 
46  Additional information at http://sourceforge.net/projects/X11workbench
47 
48 ******************************************************************************/
49 
50 
60 #include <stdio.h>
61 #include <stdlib.h>
62 #include <stdarg.h>
63 #include <limits.h>
64 #include <unistd.h>
65 #include <memory.h>
66 #include <string.h>
67 #include <strings.h>
68 #include <signal.h>
69 #include <time.h>
70 #include <fcntl.h>
71 #include <errno.h>
72 #include <sys/time.h>
73 
74 // TODO: determine if pthread is available
75 #include <pthread.h> /* currently required */
76 
77 #define _CLIPBOARD_HELPER_C /* to exclude certain things from window_helper.h like Atoms */
78 
79 #include "window_helper.h"
80 #include "pixmap_helper.h"
81 #include "conf_help.h"
82 #include "platform_helper.h"
83 #include "draw_text.h"
84 #include "text_object.h"
85 
86 #define MIN_EVENT_LOOP_SLEEP_PERIOD 100 /* 0.1 millisec */
87 #define MAX_EVENT_LOOP_SLEEP_PERIOD 100000 /* 0.1 seconds max per loop for clipboard */
88 
89 
90 typedef struct _ClipboardTask_ // only for getting data; setting data done immediately
91 {
92  volatile struct _ClipboardTask_ *pNext; // singly linked list
93 
94  int fType; // get (0) or set (1) [others reserved]
95  void *pData; // 'WBAlloc'd data when 'get' completed
96  int cbLength; // length of buffer
97  Atom aSelection; // 'selection' to grab ('None' implies "CLIPBOARD" behavior)
98  Atom aType; // data type
99  int nFormat; // format of data (0, 8, 16, 32)
100  int nRefCount; // reference count (increment before 'owning' while global lock held)
101 
102  WB_COND cond; // condition to trigger when completed (and free when 'freed')
103 
104  // state-related things
105  int fState; // flag to indicate status
106  Atom aProp; // the property associated with this task
107  WB_UINT64 ullTime; // timestamp for various timeouts (reserved)
108 
109 } CLIPBOARD_TASK;
110 
111 
112 typedef struct _ClipboardData_ // for when I own the clipboard
113 {
114  struct _ClipboardData_ *pNext; // double-link list
115 
116  Atom aSelection; // 'selection' to grab ('None' implies "CLIPBOARD" behavior)
117  Atom aType; // type of data
118  int cbLength; // length of buffer
119  int nFormat; // format of data
120 
121  // cached data
122  Window wTo; // window that I'm currently sending to [for multi-requests]
123  int fState; // flag to indicate status
124 
125 
126  char aData[1]; // actual data goes here
127 
128 } CLIPBOARD_DATA;
129 
130 
131 static WB_THREAD hClipboardThread = (WB_THREAD)INVALID_HANDLE_VALUE;
132 static volatile int bClipboardQuitFlag = 0;
133 static volatile CLIPBOARD_TASK * volatile pCBTHead = NULL; // WBAlloc'd structures
134 static CLIPBOARD_DATA *pCBDHead = NULL; // both are linked lists
135 
136 static WB_MUTEX xClipboardMutex;
137 
138 static void * ClipboardThreadProc(void *);
139 
140 
141 
142 // WBInitClipboardSystem
143 // return ZERO if clipboard system is initialized properly
144 int WBInitClipboardSystem(Display *pDisplay, const char *szDisplayName)
145 {
146 int iRval;
147 #ifndef NO_DEBUG
148 unsigned long long ullTick;
149 #endif // NO_DEBUG
150 
151 #ifndef NO_DEBUG
152  ullTick = WBGetTimeIndex();
153 #endif // NO_DEBUG
154 
155  iRval = __StartInitClipboardSystem(pDisplay, szDisplayName);
156 
157  if(!iRval) // so far so good
158  {
159  iRval = __FinishInitClipboardSystem(pDisplay, szDisplayName);
160  }
161 
162 #ifndef NO_DEBUG
163  ullTick = WBGetTimeIndex() - ullTick;
164  WBDebugPrint("TEMPORARY: %s took %llu ticks)\n", __FUNCTION__, ullTick);
165 #endif // NO_DEBUG
166 
167  return iRval;
168 }
169 
170 static char *pGlobalDisplayNameForClipboardInit = NULL;
171 
172 int __StartInitClipboardSystem(Display *pDisplay, const char *szDisplayName)
173 {
174  // for debugging purposes, keep track of how long it takes
175 
176  // TODO: check 'hClipboardThread' and/or 'bClipboardQuitFlag'
177 
178  if(!szDisplayName ||
179  (pDisplay && pDisplay != WBGetDefaultDisplay())) // for now, this param is reserved
180  {
181  szDisplayName = GetStartupDisplayName();
182  if(!szDisplayName || !*szDisplayName)
183  {
184  szDisplayName = ":0.0"; // make sure it's SOMETHING
185  }
186  }
187 
188  pGlobalDisplayNameForClipboardInit = WBCopyString(szDisplayName);
189 
190  if(!pGlobalDisplayNameForClipboardInit)
191  {
192  WB_ERROR_PRINT("ERROR: in %s - NULL display name\n",__FUNCTION__);
193 
194  return 1; // error
195  }
196 
197  if(1) // TODO: check for initialization of mutex already
198  {
199  if(WBMutexCreate(&xClipboardMutex))
200  {
201  WB_ERROR_PRINT("ERROR: in %s - Unable to create mutex\n",__FUNCTION__);
202 
203  return 2; // error
204  }
205 // else
206 // {
207 // WB_ERROR_PRINT("TEMPORARY: %s - mutex created\n",__FUNCTION__);
208 // }
209  }
210 
211  // TODO: check if clipboard thread already started?
212 
213  bClipboardQuitFlag = 1; // initial value. when it's zero, I'm started. If < 0, error
214 
215  pCBTHead = NULL;
216  pCBDHead = NULL; // make sure in both cases
217 
218  hClipboardThread = WBThreadCreate(ClipboardThreadProc, pGlobalDisplayNameForClipboardInit);
219 
220  if(hClipboardThread == (WB_THREAD)INVALID_HANDLE_VALUE)
221  {
222  WBMutexFree(&xClipboardMutex);
223 
224  return 3;
225  }
226 
227  return 0;
228 }
229 
230 
231 int __FinishInitClipboardSystem(Display *pDisplay, const char *szDisplayName)
232 {
233 int iDelayPeriod = MIN_EVENT_LOOP_SLEEP_PERIOD;
234 
235  while(WBThreadRunning(hClipboardThread) &&
236  bClipboardQuitFlag > 0)
237  {
238  WBDelay(iDelayPeriod); // wait for my thread to initialize
239 
240  if(iDelayPeriod < MAX_EVENT_LOOP_SLEEP_PERIOD)
241  {
242  iDelayPeriod += (iDelayPeriod >> 1);
243  }
244  else
245  {
246  iDelayPeriod = MAX_EVENT_LOOP_SLEEP_PERIOD;
247  }
248  }
249 
250  if(pGlobalDisplayNameForClipboardInit)
251  {
252  WBFree(pGlobalDisplayNameForClipboardInit); // ok to free it now
253  pGlobalDisplayNameForClipboardInit = NULL;
254  }
255 
256  if(bClipboardQuitFlag || !WBThreadRunning(hClipboardThread))
257  {
258  WBThreadWait(hClipboardThread); // wait for thread to exit (should be already)
259 
260  hClipboardThread = (WB_THREAD)NULL; // make sure
261 
262  WBMutexFree(&xClipboardMutex);
263 
264  return 4; // error
265  }
266 
267  WB_DEBUG_PRINT(DebugLevel_Light | DebugSubSystem_Init, "INFO: %s - Clipboard Thread started\n",__FUNCTION__);
268 
269  return 0; // everything's ok
270 }
271 
272 
273 void WBExitClipboardSystem(Display *pDisplay)
274 {
275  if(pDisplay && pDisplay != WBGetDefaultDisplay())
276  {
277  WB_ERROR_PRINT("ERROR: invalid 'pDisplay' in WBExitClipboardSystem\n");
278  return;
279  }
280 
281  if(hClipboardThread != (WB_THREAD)INVALID_HANDLE_VALUE)
282  {
283  bClipboardQuitFlag = 1;
284 
285  WBThreadWait(hClipboardThread); // wait for thread to exit
286  }
287  else
288  {
289  WB_ERROR_PRINT("ERROR: in %s - hClipboardThread == INVALID_HANDLE_VALUE\n",__FUNCTION__);
290 
291  // continue anyway to free other resources...
292  }
293 
294  hClipboardThread = (WB_THREAD)INVALID_HANDLE_VALUE; // make sure
295 
296  // while mutex owned, free up the link lists
297 
298  while(pCBTHead) // unlikely but possible
299  {
300  volatile CLIPBOARD_TASK *pT = pCBTHead;
301  pCBTHead = pCBTHead->pNext;
302 
303  // since I'm not checking the ref count a crash COULD happen here
304  // the alternative is a MEMORY LEAK. Which is worse???
305  // The fact is that the calling thread should NOT allow this to be
306  // invoked until everything waiting on the clipboard has finished, so...
307 
308  WB_ERROR_PRINT("WARNING: %s - non-null 'clipboard task' being deleted (%p), ref count %d\n",
309  __FUNCTION__, pT, pT->nRefCount);
310 
311  if(pT->fType == 0 // only for 'get'
312  && pT->pData)
313  {
314  WBFree(pT->pData);
315  }
316 
317  WBCondFree((WB_COND *)&(pT->cond)); // assume it works (and signals anything waiting with an error)
318 
319  WBFree((void *)pT);
320  }
321 
322  while(pCBDHead) // this one is more likely to have data in it
323  {
324  CLIPBOARD_DATA *pD = pCBDHead;
325  pCBDHead = pCBDHead->pNext;
326 
327  WBFree(pD); // this one is very simple
328  }
329 
330 // pCBTHead = NULL;
331 // pCBDHead = NULL; // make sure in both cases
332 
333  if(1) // TODO: check for initialization of mutex already to avoid possible crashes
334  {
335  WBMutexFree(&xClipboardMutex);
336  }
337 
338  WB_DEBUG_PRINT(DebugLevel_Light | DebugSubSystem_Init, "INFO: %s - Clipboard Thread ended\n",__FUNCTION__);
339 }
340 
341 
342 #if 0 /* uncomment this if I need the functionality; otherwise, consider removing it */
343 // event check 'predicate' proc for selection events
344 static Bool __ClipboardThreadEventPredicate(Display *pDisplay, XEvent *pEvent, XPointer arg)
345 {
346  if(pEvent &&
347  (pEvent->type == SelectionNotify ||
348  pEvent->type == SelectionClear ||
349  pEvent->type == SelectionRequest))
350  {
351  return TRUE;
352  }
353  else
354  {
355  return FALSE;
356  }
357 }
358 #endif // 0
359 
360 
361 // a few utilities placed here to make the code read better
362 
363 void CleanupDoneList(CLIPBOARD_TASK **ppDoneList)
364 {
365 CLIPBOARD_TASK *pT;
366 
367  // MUST call this with the clipboard mutex owned
368 
369  pT = *ppDoneList; // makes me go through a loop before deleting one of the tasks
370 
371  if(pT)
372  {
373  // remove the first item 'pT' from the done list
374 
375  *ppDoneList = (CLIPBOARD_TASK *)pT->pNext;
376 
377  pT->pNext = NULL;
378 
379  if(!WBInterlockedDecrement((unsigned int *)&(pT->nRefCount))) // flags a delete when ref count is zero
380  {
381  // if the ref count is higher than zero, someone else must delete this instead
382 
383  if(!pT->fType && pT->pData) // it COULD happen...
384  {
385  WBFree(pT->pData);
386  pT->pData = NULL;
387  }
388 
389  WBCondFree((WB_COND *)&(pT->cond)); // assume it works
390 
391  WBFree((void *)pT); // do this while locked
392  }
393  }
394 }
395 
396 void AddNewItemToRunList(CLIPBOARD_TASK **ppRunList)
397 {
398 CLIPBOARD_TASK *pT, *pT2;
399 
400  // next, grab an item from the 'new' queue
401  pT = (CLIPBOARD_TASK *)pCBTHead;
402 
403  if(pT)
404  {
405  pCBTHead = pT->pNext;
406 
407  WBInterlockedIncrement((unsigned int *)&(pT->nRefCount)); // I own it now
408 
409  pT->pNext = NULL; // make sure
410 
411  // make sure the 'state' flag indicates zero [errors, aka < 0, will cause it to be deleted]
412  pT->fState = 0;
413  pT->aProp = None; // and also pre-assign this to 'none'
414 
415  // now 'walk' the running list and add it
416 
417  if(!*ppRunList)
418  {
419  *ppRunList = (CLIPBOARD_TASK *)pT;
420  }
421  else
422  {
423  pT2 = *ppRunList;
424 
425  while(pT2->pNext)
426  {
427  pT2 = (CLIPBOARD_TASK *)pT2->pNext;
428  }
429 
430  pT2->pNext = pT;
431  }
432  }
433 }
434 
435 
436 // static thread proc for clipboard
437 // display name is sent as const char *
438 // TODO: one thread per display??
439 
440 void * ClipboardThreadProc(void *pParam)
441 {
442 unsigned long long ullTick;
443 int iLen, iFactor, iDelayPeriod;
444 const char *pDisplayName = (const char *)pParam;
445 Display *pDisplay = NULL;
446 volatile CLIPBOARD_TASK *pT;
447 CLIPBOARD_TASK *pT2, *pT3;
448 CLIPBOARD_TASK *pRunList = NULL; // what I'm running at the moment
449 CLIPBOARD_TASK *pDoneList = NULL; // things that get free'd up later
450 CLIPBOARD_DATA *pD, *pD2;
451 Window wWindow = None;
452 XEvent evt, evt2;
453 Atom aType;
454 // NOTE: Atoms in 'window_helper.h' won't be defined, see headers above. They apply to the main thread.
455 // These local variables will do the job instead, since it becomes local to the thread this way
458 #ifdef X_HAVE_UTF8_STRING /* this indicates the extension is present */
459 Atom aUTF8_STRING;
460 #endif // X_HAVE_UTF8_STRING
461 // NOTE: these atoms won't need to be loaded - they're pre-defined (left for reference)
462 //Atom aPRIMARY = XA_PRIMARY;
463 //Atom aSECONDARY = XA_SECONDARY;
464 //Atom aSTRING = XA_STRING;
465 //Atom aBITMAP = XA_BITMAP;
466 //Atom aDRAWABLE = XA_DRAWABLE;
467 //Atom aCOLORMAP = XA_COLORMAP;
468 //Atom aPIXMAP = XA_PIXMAP;
469 
470 
471  ullTick = WBGetTimeIndex();
472 
473  pDisplay = XOpenDisplay(pDisplayName);
474 
475  if(!pDisplay)
476  {
477  WB_ERROR_PRINT("%s - can't open display %s\n", __FUNCTION__, pDisplayName);
478 
479  bClipboardQuitFlag = -1;
480  goto exit_point;
481  }
482 
483  // Load all of the atoms for use within this thread
484  aINCR = XInternAtom(pDisplay, "INCR", False);
485  aWBCLIP = XInternAtom(pDisplay, "WB_CLIP", False);
486  aCLIPBOARD = XInternAtom(pDisplay, "CLIPBOARD", False); // should I use XA_CLIPBOARD(pDisplay) instead??
487  aTEXT = XInternAtom(pDisplay, "TEXT", False);
488  aC_STRING = XInternAtom(pDisplay, "C_STRING", False);
489  aCOMPOUND_TEXT = XInternAtom(pDisplay, "COMPOUND_TEXT", False);
490  aTARGETS = XInternAtom(pDisplay, "TARGETS", False);
491  aMULTIPLE = XInternAtom(pDisplay, "MULTIPLE", False);
492  aTIMESTAMP = XInternAtom(pDisplay, "TIMESTAMP", False);
493  aPIXEL = XInternAtom(pDisplay, "PIXEL", False);
494 
495 #ifdef X_HAVE_UTF8_STRING /* this indicates the extension is present */
496  aUTF8_STRING = XInternAtom(pDisplay, "UTF8_STRING", False);
497 #endif // X_HAVE_UTF8_STRING
498 
499  aNULL = XInternAtom(pDisplay, "NULL", False);
500 
501  // NOTE: these will actually match the ones allocated from the default display
502  // and later on, I will have to rely on that. But for now I have my own
503  // copies. A nice TODO would be to make sure they match.
504 
505  if(aINCR == None ||
506  aWBCLIP == None ||
507  aCLIPBOARD == None ||
508  aTEXT == None ||
509  aC_STRING == None ||
510  aCOMPOUND_TEXT == None ||
511  aTARGETS == None ||
512  aMULTIPLE == None ||
513  aTIMESTAMP == None ||
514  aPIXEL == None ||
515 #ifdef X_HAVE_UTF8_STRING /* this indicates the extension is present */
516  aUTF8_STRING == None || // TODO: should this one be optional?
517 #endif // X_HAVE_UTF8_STRING
518  aNULL == None)
519  {
520  WB_ERROR_PRINT("%s - error getting Atoms\n"
521  " aINCR=%d\n"
522  " aWBCLIP=%d\n"
523  " aCLIPBOARD=%d\n"
524  " aTEXT=%d\n"
525  " aC_STRING=%d\n"
526  " aCOMPOUND_TEXT=%d\n"
527  " aTARGETS=%d\n"
528  " aMULTIPLE=%d\n"
529  " aTIMESTAMP=%d\n"
530  " aPIXEL=%d\n"
531 #ifdef X_HAVE_UTF8_STRING /* this indicates the extension is present */
532  " aUTF8_STRING=%d\n"
533 #endif // X_HAVE_UTF8_STRING
534  " aNULL=%d\n", __FUNCTION__,
535  (int)aINCR, (int)aWBCLIP, (int)aCLIPBOARD, (int)aTEXT, (int)aC_STRING,
536  (int)aCOMPOUND_TEXT, (int)aTARGETS, (int)aMULTIPLE, (int)aTIMESTAMP, (int)aPIXEL,
537 #ifdef X_HAVE_UTF8_STRING /* this indicates the extension is present */
538  (int)aUTF8_STRING,
539 #endif // X_HAVE_UTF8_STRING
540  (int)aNULL);
541 
542  bClipboardQuitFlag = -1;
543  goto exit_point;
544  }
545 
546  // create thread's 'simple window' and enable property change/selection stuff
547 
548  wWindow = XCreateSimpleWindow(pDisplay, DefaultRootWindow(pDisplay), 0, 0, 1, 1, 0, 0, 0);
549 
550  if(wWindow == None)
551  {
552  bClipboardQuitFlag = -1;
553 
554  WB_ERROR_PRINT("%s - can't create simple window\n", __FUNCTION__);
555 
556  goto exit_point;
557  }
558 
559  XSelectInput(pDisplay, wWindow, PropertyChangeMask); // important - select property change input
560 
561  // wWindow is going to own every clipboard data thing
562 
563  XSync(pDisplay, 0); // make sure that the display is "in sync"
564 
565  bClipboardQuitFlag = 0; // to trigger "I am done initializing" and it's ok
566 
567  WBDelay(100000); // wait 0.1 seconds for everything to stabilize (TODO: is this needed here?)
568 
569  pDisplayName = NULL; // no longer valid (TODO: cache it for multiple display instances?)
570 
571  ullTick = WBGetTimeIndex() - ullTick;
572 
573  WB_DEBUG_PRINT(DebugLevel_Light | DebugSubSystem_Init, "INFO: %s - Clipboard Thread initialization complete\n",__FUNCTION__);
574  WB_DEBUG_PRINT(DebugLevel_Medium | DebugSubSystem_Init, "CLIPBOARD THREAD STARTUP took %llu ticks\n", ullTick);
575 
576  // -------------------------------------------------------
577  // main handler loop - this executes the clipboard thread
578  // -------------------------------------------------------
579 
580  iDelayPeriod = MIN_EVENT_LOOP_SLEEP_PERIOD;
581 
582  while(!bClipboardQuitFlag)
583  {
584  if(WBMutexLock(&xClipboardMutex, 1000)) // wait up to 1msec to lock
585  {
586  // well looks like I could NOT own the mutex
587 
588  WBDelay(100); // make sure I do this at least once in this loop, waiting for clipboard mutex
589  }
590  else
591  {
592  // clipboard task lock achieved, do some housekeeping
593 
594  // first clean up the "done" list (removes only the first item in it)
595  CleanupDoneList(&pDoneList);
596 
597  // next, grab an item from the 'new' queue and add it to the 'run' list
598  AddNewItemToRunList(&pRunList);
599 
600  // unlock the mutex now
601  WBMutexUnlock(&xClipboardMutex); // not locked now
602  }
603 
604  // now go through my 'run list' and work on whatever is here
605  pT2 = NULL; // this will be my 'prev' pointer for unlinking
606  pT = pRunList;
607 
608  // NOTE: because of the use of automatic variables rather than globals,
609  // I pretty much need to do this in one big freaking loop.
610  pD = pCBDHead;
611  while(pT)
612  {
613  int bDoneAndSignal = 0; // this flag tells me to place the item in the 'done queue' and signal the owner
614 
616  // PROCESS RUN LIST - CHECK 'CANCEL STATE' FIRST
618 
619  // check 'fState' - if it's negative, the task has been canceled or was an error I didn't catch before
620 
621  if((int)WBInterlockedRead((unsigned int *)&(pT->fState)) < 0) // error state - these will be moved to 'done list'
622  {
623  bDoneAndSignal = 1; // if marked 'error' the request was canceled and it needs to be deleted
624  // this will flow through and signal the caller
625  }
627  // CONTINUE NORMAL PROCESSING (NOT CANCELED)
629  else if(pT->fType == 0) // get clipboard data
630  {
632  // GET CLIPBOARD DATA - INITIALIZATION, 'state zero'
634 
635  if(pT->fState == 0) // state zero - just starting out
636  {
637  Window wOwn;
638  Atom aSelection;
639 
640  aSelection = pT->aSelection;
641  if(aSelection == None)
642  {
643  wOwn = XGetSelectionOwner(pDisplay, XA_PRIMARY);
644 
645  if(wOwn != None)
646  {
647  aSelection = XA_PRIMARY;
648  }
649  else
650  {
651  wOwn = XGetSelectionOwner(pDisplay, aCLIPBOARD);
652 
653  if(wOwn != None)
654  {
655  aSelection = aCLIPBOARD;
656  }
657  }
658 
659  if(wOwn != None)
660  {
661  pT->aSelection = aSelection;
662  }
663  }
664  else
665  {
666  wOwn = XGetSelectionOwner(pDisplay, aSelection);
667 
668 #ifndef NO_DEBUG
669  if(aSelection != XA_PRIMARY && aSelection != aCLIPBOARD)
670  {
671  char *p1 = WBGetAtomName(pDisplay, aSelection);
672  WB_ERROR_PRINT("TEMPORARY: %s - selection = %d \"%s\" owner = %u (%08xH)\n",
673  __FUNCTION__, (int)aSelection, p1, (int)wOwn, (int)wOwn);
674  if(p1)
675  {
676  WBFree(p1);
677  }
678  }
679 #endif // NO_DEBUG
680  }
681 
682  if(wOwn == wWindow) // it's me
683  {
684 // WB_ERROR_PRINT("TEMPORARY: %s - request clipboard and I own it\n", __FUNCTION__);
685 
686  // re-acquire the mutex
687 
688  if(!WBMutexLock(&xClipboardMutex, 100000L)) // re-lock, up to 0.1 seconds
689  {
690  // walk the data chain looking for a match
691  pD = pCBDHead;
692 
693  while(pD && pD->aSelection != aSelection)
694  {
695  pD = pD->pNext;
696  }
697 
698  if(!pD) // try 'soft match' instead
699  {
700  pD = pCBDHead;
701 
702  while(pD)
703  {
704  if(pD->aSelection == None &&
705  (aSelection == XA_PRIMARY ||
706  aSelection == aCLIPBOARD))
707  {
708  break;
709  }
710 
711  pD = pD->pNext;
712  }
713  }
714 
715  if(pD) // it will match
716  {
717  if(pD->nFormat == 16) // format determines element size
718  {
719  iFactor = 2;
720  }
721  else if(pD->nFormat == 32)
722  {
723  iFactor = 4;
724  }
725  else
726  {
727  iFactor = 1;
728  }
729 
730  iLen = iFactor * pD->cbLength; // actual length in bytes
731 
732  pT->pData = WBAlloc(iLen + iFactor);
733 
734  if(pT->pData)
735  {
736  memcpy(pT->pData, pD->aData, iLen);
737 
738  pT->cbLength = pD->cbLength;
739  pT->nFormat = pD->nFormat;
740 
741  bDoneAndSignal = 1; // put in 'done' queue, signal caller
742 
743 // WB_ERROR_PRINT("TEMPORARY: %s - return clipboard data %p len=%d (%d)\n",
744 // __FUNCTION__, pT->pData, pT->cbLength, iLen);
745  }
746  else
747  {
748  // TODO: set fState to -1 and set 'bDoneAndSignal?
749  // this could infinitely re-try if I don't...
750 
751  WB_ERROR_PRINT("TEMPORARY: %s - clipboard data is NULL\n", __FUNCTION__);
752 
753  goto null_data_me_own; // for now I'll do this
754  }
755  }
756  else
757  {
758 null_data_me_own:
759  WB_ERROR_PRINT("TEMPORARY: %s - return NULL clipboard data, owned by me\n", __FUNCTION__);
760 
761  // NULL data
762  pT->cbLength = 0;
763  pT->nFormat = 8;
764  pT->pData = NULL; // make sure
765 
766  bDoneAndSignal = 1; // put in 'done' queue, signal caller
767  }
768 
769  WBMutexUnlock(&xClipboardMutex); // not locked now
770 
771  // since I have a ref count it's safe to be unlocked here
772  // the data members I 'muck' with are reserved and don't need
773  // 'atomic' access between this thread and the caller
774  }
775  else
776  {
777  // TODO: set fState to -1 and set 'bDoneAndSignal?
778  // this could infinitely re-try if I don't...
779 
780  WB_ERROR_PRINT("TEMPORARY: %s line %d - unable to lock mutex\n", __FUNCTION__, __LINE__);
781 
782  pT->fState = -1;
783  bDoneAndSignal = 1; // well I'll do this for now
784  }
785  }
786  else if(wOwn == None) // nobody owns the clipboard
787  {
788  // NULL data
789  pT->cbLength = 0;
790  pT->nFormat = 8;
791  pT->pData = NULL; // make sure
792 
793  bDoneAndSignal = 1; // put in 'done' queue, signal caller
794  }
795  else
796  {
797  // I'm not the owner of the clipboard data. So I must make a request
798  // for the data. When it arrives, I need to hand it off to the caller
799  // and then clean myself up [this will happen asynchronously].
800 
801  // convert output to 'WB_CLIP' property on the window of my choice
802  pT->aProp = aWBCLIP; // for now always write to this one - later, custom per request?
803 
804  XConvertSelection(pDisplay, aSelection, pT->aType,
805  pT->aProp,
806  wWindow, CurrentTime);
807 
808  // NOTE: the function return does not indicate success/fail
809 
810 // WB_ERROR_PRINT("TEMPORARY: %s line %d - XConvertSelection, fState set to 1\n", __FUNCTION__, __LINE__);
811 
812  pT->fState = 1; // this tells me I'm waiting for the first reply
813  pT->ullTime = WBGetTimeIndex(); // the time I sent the message
814  }
815  }
816  else if(pT->fState == 1) // XConvertSelection sent, no reply yet
817  {
818  // TODO: if I am in state 1 and too much time has passed, do I want to cancel the
819  // request and signal a failure?? or do I want to re-issue the
820  // XConvertSelection request?
821 
822  // if((WBGetTimeIndex() - pT->ullTime) > some limit)
823  // { do something, re-send request, whatever }
824 
825  // NOTE: if the thing gets canceled by the caller, it will get a state of '-1'
826  }
827 
829  }
830  else if(pT->fType == 1) // assign clipboard
831  {
833  // SET CLIPBOARD DATA - copy data, assign to data list
835 
836  // NOTE: I don't need to worry about the state here, since all I'm going
837  // to do is set up a copy of the data, own the clipboard, and tell
838  // everyone/thing I completed the task. So it's almost synchronous
839 
840 
841  // COPY TO BUFFER OPERATION
842  // TODO: 'cut buffer' XA_STRING selection uses XStoreBuffer (etc.) directly
843 
844  // walk the data chain looking for a match, and remove it if it matches.
845  // (I'm going to replace it right away and the data and struct are together)
846 
847  pD = pCBDHead;
848  pD2 = NULL;
849 
850  while(pD)
851  {
852  if(pD->aSelection == pT->aSelection)
853  {
854  break; // direct match
855  }
856 
857  pD2 = pD;
858  pD = pD->pNext;
859  }
860 
861  if(!pD) // try 'soft match' instead
862  {
863  pD = pCBDHead;
864  pD2 = NULL;
865 
866  while(pD)
867  {
868  if(pD->aSelection == None &&
869  (pT->aSelection == XA_PRIMARY ||
870  pT->aSelection == aCLIPBOARD))
871  {
872  break;
873  }
874 
875  if(pT->aSelection == None &&
876  (pD->aSelection == XA_PRIMARY ||
877  pD->aSelection == aCLIPBOARD))
878  {
879  break;
880  }
881 
882  pD2 = pD;
883  pD = pD->pNext;
884  }
885  }
886 
887  if(pD) // matching data exists
888  {
889  if(pD2)
890  {
891  pD2->pNext = pD->pNext;
892  }
893  else
894  {
895  pCBDHead = pD->pNext;
896  }
897 
898  WBFree(pD); // done with it now
899  }
900 
901  // NOW, must create 'CLIPBOARD_DATA' struct with this stuff in it.
902 
903  if(!pT->pData || !pT->cbLength) // empty the clipboard/selection?
904  {
905  if(XGetSelectionOwner(pDisplay, pT->aSelection) == wWindow)
906  {
907  // empty the selection now (because I'm assigning 'NULL')
908 
909  if(pT->aSelection == None) // indicates 'clipboard'
910  {
911  XSetSelectionOwner(pDisplay, XA_PRIMARY, None, CurrentTime);
912  XSetSelectionOwner(pDisplay, aCLIPBOARD, None, CurrentTime); // so I do BOTH of them...
913  }
914  else
915  {
916  XSetSelectionOwner(pDisplay, pT->aSelection, None, CurrentTime);
917  }
918  }
919 
920 // WB_ERROR_PRINT("TEMPORARY: %s - empty clipboard assignment\n", __FUNCTION__);
921  }
922  else
923  {
924  int iTrueLen;
925 
926  if(pT->nFormat == 16)
927  {
928  iTrueLen = pT->cbLength * 2;
929  }
930  else if(pT->nFormat == 32)
931  {
932  iTrueLen = pT->cbLength * 4;
933  }
934  else
935  {
936  iTrueLen = pT->cbLength;
937  }
938 
939  pD = (CLIPBOARD_DATA *)WBAlloc(sizeof(*pD) + 2 + iTrueLen);
940 
941  if(!pD)
942  {
943  // TODO: indicate 'error' in assigning clipboard data
944 
945  XSetSelectionOwner(pDisplay, pT->aSelection, None, CurrentTime);
946  // regardless of who owns it, set it to 'None' on error
947  WB_ERROR_PRINT("%s - not enough memory for clipboard data - empty clipboard assignment\n", __FUNCTION__);
948  }
949  else
950  {
951  memcpy(pD->aData, pT->pData, iTrueLen);
952 
953  pD->aSelection = pT->aSelection;
954  pD->aType = pT->aType;
955  pD->nFormat = pT->nFormat;
956  pD->cbLength = pT->cbLength;
957 
958 // WB_ERROR_PRINT("TEMPORARY: %s - owning selection - length=%d (%d)\n", __FUNCTION__, pD->cbLength, iTrueLen);
959 
960  if(pD->aSelection != None)
961  {
962  char *p1 = WBGetAtomName(pDisplay, pD->aSelection);
963 
964  XSetSelectionOwner(pDisplay, pD->aSelection, wWindow, CurrentTime);
965 
966 // WB_ERROR_PRINT("TEMPORARY: %s - owned \"%s\"\n", __FUNCTION__, p1);
967  WBFree(p1);
968  }
969  else
970  {
971  XSetSelectionOwner(pDisplay, XA_PRIMARY, wWindow, CurrentTime);
972  XSetSelectionOwner(pDisplay, aCLIPBOARD, wWindow, CurrentTime);
973 
974 // WB_ERROR_PRINT("TEMPORARY: %s - owned XA_PRIMARY and CLIPBOARD\n", __FUNCTION__);
975  }
976 
977  // add 'pD' to my chain
978 
979  pD->pNext = NULL;
980 
981  if(!pCBDHead)
982  {
983  pCBDHead = pD;
984  }
985  else
986  {
987  pD2 = pCBDHead;
988 
989  while(pD2->pNext)
990  {
991  pD2 = pD2->pNext;
992  }
993 
994  pD2->pNext = pD;
995  }
996 
997 #if 0 /* this is a debug-only feature - remove when it's working properly */
998  {
999  char *p1;
1000  WB_ERROR_PRINT("TEMPORARY: %s - walking clipboard data\n", __FUNCTION__);
1001  pD = pCBDHead;
1002 
1003  while(pD)
1004  {
1005  int iLen;
1006  if(pD->nFormat == 16)
1007  {
1008  iLen = 2 * pD->cbLength;
1009  }
1010  else if(pD->nFormat == 32)
1011  {
1012  iLen = 4 * pD->cbLength;
1013  }
1014  else
1015  {
1016  iLen = pD->cbLength;
1017  }
1018 
1019  p1 = WBGetAtomName(pDisplay, pD->aSelection);
1020  WBDebugPrint(" selection: \"%s\"\n", p1);
1021  WBFree(p1);
1022 
1023  p1 = WBGetAtomName(pDisplay, pD->aType);
1024  WBDebugPrint(" type: \"%s\"\n", p1);
1025  WBFree(p1);
1026 
1027  WBDebugPrint(" format: %d\n", pD->nFormat);
1028  WBDebugPrint(" length: %d (%d)\n", pD->cbLength, iLen);
1029 
1030  p1 = WBAlloc(iLen + 1);
1031 
1032  if(p1 && iLen)
1033  {
1034  memcpy(p1, pD->aData, iLen);
1035  }
1036  if(p1)
1037  {
1038  p1[iLen] = 0;
1039  }
1040 
1041  WBDebugPrint("----------\n%s\n----------\n", p1);
1042 
1043  WBFree(p1);
1044 
1045  pD = pD->pNext;
1046  }
1047  }
1048 #endif // 1
1049  }
1050 
1051  XFlush(pDisplay);
1052  }
1053 
1054  bDoneAndSignal = 1;
1055 
1057  }
1058 
1059  // flush and synchronize so that any events that "the above"
1060  // might have created will be read to be processed right away
1061 
1062  XFlush(pDisplay); // make sure
1063  XSync(pDisplay, 0); // same here
1064 
1065 
1066  if(bDoneAndSignal)
1067  {
1069  // signaling the completion of a task
1071 
1072 // WB_ERROR_PRINT("TEMPORARY: %s line %d - put thingy in done list, signal caller\n", __FUNCTION__, __LINE__);
1073 
1074  // after copying the data, remove it from the 'run' list
1075 
1076  if(pT2) // at THIS point, the 'prev' pointer for unlinking
1077  {
1078  pT2->pNext = pT->pNext;
1079  }
1080  else
1081  {
1082  pRunList = (CLIPBOARD_TASK *)pT->pNext;
1083  }
1084 
1085  pT->pNext = NULL;
1086 
1087  // now add it to the 'done' list
1088 
1089  if(!pDoneList)
1090  {
1091  pDoneList = (CLIPBOARD_TASK *)pT;
1092  }
1093  else
1094  {
1095  pT3 = pDoneList; // at THIS point, pT2 walks the 'done' list
1096 
1097  while(pT3->pNext)
1098  {
1099  pT3 = (CLIPBOARD_TASK *)pT3->pNext;
1100  }
1101 
1102  pT3->pNext = pT;
1103  }
1104 
1105  // must now signal the owner. Since I'm using the 'done list' to decrement
1106  // the reference count, the object will alays be 'good' here. So I just
1107  // need to signal the 'cond' object to get the owner to recognize I'm done
1108  // with the operation, and everything should be a-OK.
1109 
1110  WBCondSignal((WB_COND *)&(pT->cond)); // tell caller I'm done
1111 
1112  // now - leave pT2 'as-is' here but assign 'pT' to the 'pT2->pNext'
1113  if(pT2)
1114  {
1115  pT = pT2->pNext; // point to next item in list (after the one I moved)
1116  }
1117  else if(pRunList)
1118  {
1119  pT = pRunList->pNext;
1120  }
1121  else
1122  {
1123  pT = NULL; // end of loop
1124  }
1125 
1127  }
1128  else
1129  {
1131  // loop to next thingy - this is different than if I signaled
1132  // the caller, because I'm not pulling it out of the chain
1134 
1135  pT2 = (CLIPBOARD_TASK *)pT; // my new 'prev' pointer for unlinking (must re-assign here)
1136  pT = pT->pNext; // point to next item in list
1137  }
1138  }
1139 
1140  // check for the 'quit' flag before doing anything else
1141 
1142  if(bClipboardQuitFlag)
1143  {
1144  break;
1145  }
1146 
1148  // HANDLING X SERVER EVENTS
1150 
1151  // NEXT, look for selection events. If I find one directed at me, I must process
1152  // it according to whatever's currently going on.
1153 
1154  memset(&evt, 0, sizeof(evt));
1155 
1156  // read every available event, until I find one that I want
1157 
1158  while(XEventsQueued(pDisplay, QueuedAlready) > 0) // we have queued events!
1159  {
1160  // read through ALL of the incoming events until I find one
1161  // that I have to process. the others are 'eaten'
1162 
1163  XNextEvent(pDisplay, &evt);
1164 
1165  if(evt.type == SelectionClear ||
1166  evt.type == SelectionRequest ||
1167  evt.type == SelectionNotify)
1168  {
1169  WB_ERROR_PRINT("TEMPORARY: %s line %d - selection event found\n", __FUNCTION__, __LINE__);
1170  break;
1171  }
1172  else
1173  {
1174  WBDebugDumpEvent(&evt);
1175  WB_ERROR_PRINT("TEMPORARY: %s line %d - unrecognized event (ignoring)\n", __FUNCTION__, __LINE__);
1176  }
1177 
1178  evt.type = 0; // must do this in case I have no events left so next section is skipped
1179  }
1180 
1181 
1182  if(evt.type == SelectionClear ||
1183  evt.type == SelectionRequest ||
1184  evt.type == SelectionNotify)
1185  {
1186  int iFormat;
1187  unsigned long nItems, cbLeft;
1188  unsigned char *pBuf;
1189 
1190  WBDebugDumpEvent(&evt);
1191 
1192  // COPY TO BUFFER OPERATION
1193  // TODO: XA_STRING uses XStoreBuffer (etc.) directly, no owner, buffer 0
1194 
1195  // step 1: own selection using XSetSelectionOwner
1196  // step 2: wait for selection events using XNextEvent or similar
1197  // if I get 'SelectionClear', set 'clear' flag, continue processing incremental
1198  // step 3: on selection request, send the data, incrementally if needed (one message per loop)
1199  // (when selection is 'cleared' I can stop processing this)
1200 
1201  // COPY FROM CLIPBOARD OPERATION
1202  // when retrieving a selection, I'll be the one sending the SelectionRequest events
1203  // (this is typically done using 'XConvertSelection')
1204  // once retrieved, set appropriate flag in work unit, get next one [if any]
1205  // if I'm the clipboard owner, just copy the data as-is and mark 'success'
1206 
1207  // get data type in this order:
1208  // XA_CLIPBOARD(pDisplay) i.e. the atom 'CLIPBOARD' for the given display
1209  // XA_PRIMARY (this is the default for 'xclip')
1210  // XA_SECONDARY
1211  // XA_STRING
1212 
1213  // 'target' format will typically be
1214  // XA_UTF8_STRING(pDisplay) [the atom 'UTF8_STRING']
1215  // XA_STRING (usually when xselection.property == None in the SelectionEvent)
1216 
1217  // use XGetSelectionOwner to see who owns the selection
1218  // lock/unlock the X system before (and while) grabbing certain info
1219 
1220  // own the mutex and check for tasks, perform one per loop
1221 
1222  if(evt.type == SelectionClear)
1223  {
1225  // SELECTION CLEAR EVENT
1227 
1228  // I'm being asked NOT to own the clipboard, so first check that I'm still the owner
1229 
1230  Window wOwn = XGetSelectionOwner(pDisplay, evt.xselectionclear.selection);
1231 
1232  if(wOwn == wWindow) // do I still own it?
1233  {
1234  if(evt.xselectionclear.selection == aCLIPBOARD ||
1235  evt.xselectionclear.selection == XA_PRIMARY)
1236  {
1237  XSetSelectionOwner(pDisplay, XA_PRIMARY,
1238  None, CurrentTime); // no owner, now
1239  XSetSelectionOwner(pDisplay, aCLIPBOARD,
1240  None, CurrentTime); // no owner, now
1241  }
1242  else
1243  {
1244  XSetSelectionOwner(pDisplay, evt.xselectionclear.selection,
1245  None, CurrentTime); // no owner, now
1246  }
1247 
1248  XFlush(pDisplay);
1249  }
1250 
1251  pD = pCBDHead;
1252  pD2 = NULL;
1253 
1254  while(pD)
1255  {
1256  if(pD->aSelection == evt.xselectionclear.selection ||
1257  (pD->aSelection == None &&
1258  (evt.xselectionclear.selection == XA_PRIMARY ||
1259  evt.xselectionclear.selection == aCLIPBOARD)))
1260  {
1261  // remove this item from the chain
1262  if(!pD2)
1263  {
1264  pCBDHead = pD->pNext;
1265  }
1266  else
1267  {
1268  pD2->pNext = pD->pNext;
1269  }
1270 
1271  WBFree(pD); // free up the memory [that's all I need to do]
1272 
1273  pD = pD2; // this continues searching correctly
1274 
1275  if(!pD) // null means empty chain
1276  {
1277  break; // I am done
1278  }
1279  }
1280 
1281  pD2 = pD; // the new 'prev' item
1282  pD = pD->pNext; // "next" item in chain
1283  }
1284  }
1285  else if(evt.type == SelectionRequest) // copy FROM me
1286  {
1288  // SELECTION REQUEST EVENT
1290 
1291  pD = pCBDHead;
1292 
1293  while(pD && pD->aSelection != evt.xselectionrequest.selection)
1294  {
1295  pD = pD->pNext;
1296  }
1297 
1298  if(!pD) // try 'soft match' instead
1299  {
1300  pD = pCBDHead;
1301 
1302  while(pD)
1303  {
1304  if(pD->aSelection == None &&
1305  (evt.xselectionrequest.selection == XA_PRIMARY ||
1306  evt.xselectionrequest.selection == aCLIPBOARD))
1307  {
1308  break;
1309  }
1310 
1311  pD = pD->pNext;
1312  }
1313  }
1314 
1315  // send data to requestor
1316 
1317  if(evt.xselectionrequest.target == aTARGETS) // special target
1318  {
1319  Atom aT[2];
1320 
1321  aT[0] = aTARGETS;
1322  aT[1] = pD ? pD->aType : XA_STRING; // just do this for now
1323 
1324  XChangeProperty(pDisplay, evt.xselectionrequest.requestor,
1325  evt.xselectionrequest.property,
1326  XA_ATOM, 32, PropModeReplace,
1327  (void *)aT, sizeof(aT)/sizeof(aT[0]));
1328 
1329  WB_ERROR_PRINT("TEMPORARY: %s - sending TARGETS XChangeProperty\n", __FUNCTION__);
1330  }
1331  else if(pD &&
1332  (evt.xselectionrequest.target == pD->aType ||
1333  (evt.xselectionrequest.target == XA_STRING && pD->aType == aUTF8_STRING) ||
1334  (evt.xselectionrequest.target == aUTF8_STRING && pD->aType == XA_STRING)))
1335  {
1336  int nE = pD->cbLength;
1337 
1338  if(pD->nFormat == 16)
1339  {
1340  nE /= 2;
1341  }
1342  else if(pD->nFormat == 32)
1343  {
1344  nE /= 4;
1345  }
1346 
1347  // TODO: data conversion to/from UTF8 ??
1348 
1349 
1350  // TODO: handle 'chunking'
1351 
1352  XChangeProperty(pDisplay, evt.xselectionrequest.requestor,
1353  evt.xselectionrequest.property,
1354  pD->aType, pD->nFormat, PropModeReplace,
1355  (unsigned char *)pD->aData, nE);
1356 
1357  // TODO verify that XChangeProperty actually worked by using XSync
1358  // and testing for response messages. for now, just assume it works.
1359 
1360 
1361  WB_ERROR_PRINT("TEMPORARY: %s - sent XChangeProperty\n", __FUNCTION__);
1362  }
1363  else
1364  {
1365  // error - send 'None'
1366 
1367  XChangeProperty(pDisplay, evt.xselectionrequest.requestor,
1368  evt.xselectionrequest.property,
1369  None, 0, PropModeReplace,
1370  NULL, 0);
1371 
1372  WB_ERROR_PRINT("%s - sending 'None' for XChangeProperty\n", __FUNCTION__);
1373  }
1374 
1376  // EVENT REPLY TO REQUESTOR
1378 
1379  // now I need to reply to the message to say I did it
1380  // if I do _NOT_ do this in a timely manner, other applications
1381  // will be affected in a very... bad... way!
1382 
1383  memset(&evt2, 0, sizeof(evt2));
1384 
1385  evt2.xselection.type = SelectionNotify;
1386  evt2.xselection.display = evt.xselectionrequest.display;
1387  evt2.xselection.requestor = evt.xselectionrequest.requestor;
1388  evt2.xselection.property = evt.xselectionrequest.property;
1389  evt2.xselection.selection = evt.xselectionrequest.selection;
1390  evt2.xselection.target = evt.xselectionrequest.target;
1391  evt2.xselection.time = evt.xselectionrequest.time;
1392 
1393  XFlush(evt2.xselection.display);
1394 
1395  /* send the response event */
1396  XSendEvent(evt2.xselection.display, evt.xselectionrequest.requestor,
1397  0, 0, &evt2);
1398  XFlush(evt2.xselection.display);
1399 
1400  WB_ERROR_PRINT("TEMPORARY: %s - reply message sent\n", __FUNCTION__);
1401  }
1402  else if(evt.type == SelectionNotify)
1403  {
1405  // SELECTION NOTIFY EVENT
1407 
1408  int bDoneAndSignal = 0; // this flag tells me to place the item in the 'done queue' and signal the owner
1409 
1410  // now go through my 'run list' and work on whatever is here
1411  pT2 = NULL; // this will be my 'prev' pointer for unlinking
1412  pT = pRunList;
1413 
1414  while(pT)
1415  {
1416  WB_ERROR_PRINT("TEMPORARY: %s line %d - event loop for SelectionNotify\n", __FUNCTION__, __LINE__);
1417 
1418  if(pT->fType == 0 // getting clipboard/selection data
1419  && pT->aSelection == evt.xselection.selection // getting property from this selection
1420  && pT->aType == evt.xselection.target // and converting to THIS datatype
1421  && (pT->aProp == evt.xselection.property || // matching property name
1422  evt.xselection.property == None)) // or an error
1423  {
1424  pBuf = NULL; // make sure
1425 
1426  if(evt.xselection.property == None) // this means ERROR
1427  {
1428 #ifdef X_HAVE_UTF8_STRING /* this indicates the extension is present */
1429  if(pT->aType == aUTF8_STRING &&
1430  pT->fState == 1)
1431  {
1432  // fallback, re-issue the request but using XA_STRING instead of aUTF8_STRING
1433  pT->aType = XA_STRING; // change the type (will return this later)
1434 
1435  // ok here we go again, but an XA_STRINg this time
1436  XConvertSelection(pDisplay, pT->aSelection, pT->aType,
1437  pT->aProp,
1438  wWindow, CurrentTime);
1439  }
1440  else
1441 #endif // X_HAVE_UTF8_STRING
1442  {
1443  // assume this is an error condition
1444 
1445  WB_ERROR_PRINT("ERROR: %s line %d - Unable to do conversion\n", __FUNCTION__, __LINE__);
1446 
1447  pT->fState = -1; // an error
1448 
1449  bDoneAndSignal = 1;
1450  }
1451  }
1452  else if(pT->fState == 1) // state 1 - request sent, waiting for reply
1453  {
1454  // first, see what I have assigned to my property
1455 
1456  if(!XGetWindowProperty(pDisplay, wWindow, pT->aProp, 0, 0, False,
1457  AnyPropertyType, &aType, &iFormat, &nItems, &cbLeft, &pBuf))
1458  {
1459  if(pBuf)
1460  {
1461  XFree(pBuf);
1462  pBuf = NULL;
1463  }
1464 
1465  // is this the actual data, or a return that says "do it incrementally" ?
1466 
1467  if(aType == aINCR) // incremental
1468  {
1469  // begin incremental process by deleting the property
1470  XDeleteProperty(pDisplay, wWindow, pT->aProp);
1471  XFlush(pDisplay);
1472 
1473  // assign state 2, which will pick it up again as incremental
1474  pT->fState = 2;
1475  }
1476  else
1477  {
1478  int iLen = cbLeft; // the RAW length (in bytes)
1479 
1480  if(iFormat == 16)
1481  {
1482  pT->cbLength = cbLeft / 2;
1483  }
1484  else if(iFormat == 32)
1485  {
1486  pT->cbLength = cbLeft / 4;
1487  }
1488  else
1489  {
1490  pT->cbLength = cbLeft;
1491  }
1492 
1493  if(!XGetWindowProperty(pDisplay, wWindow, pT->aProp, 0, pT->cbLength, False,
1494  AnyPropertyType, &aType, &iFormat, &nItems, &cbLeft, &pBuf) &&
1495  pBuf)
1496  {
1497  pT->nFormat = iFormat;
1498  pT->aType = aType;
1499 
1500  if(nItems != pT->cbLength)
1501  {
1502  WB_ERROR_PRINT("WARNING: %s - nItems %ld does not match calculated length %d\n",
1503  __FUNCTION__, nItems, pT->cbLength);
1504  }
1505 
1506  pT->pData = WBAlloc(iLen + 4);
1507 
1508  if(pT->pData)
1509  {
1510  memcpy(pT->pData, pBuf, iLen);
1511  ((char *)pT->pData)[iLen] = 0; // make sure
1512 
1513  if(aType == XA_STRING || aType == aUTF8_STRING)
1514  {
1515  pT->nFormat = 8; // force it
1516  pT->cbLength = iLen + 1; // includes the terminating zero byte as part of the length
1517  }
1518 
1519  pT->fState = 3; // mark 'data complete' (for debugging, later, maybe)
1520 
1521  bDoneAndSignal = 1;
1522  }
1523  else
1524  {
1525  WB_ERROR_PRINT("ERROR: %s line %d - Unable to allocate pointer\n", __FUNCTION__, __LINE__);
1526 
1527  pT->fState = -1; // an error
1528 
1529  bDoneAndSignal = 1;
1530  }
1531  }
1532  else
1533  {
1534  WB_ERROR_PRINT("ERROR: %s line %d - Unable to get window property\n", __FUNCTION__, __LINE__);
1535 
1536  pT->fState = -1; // an error
1537 
1538  bDoneAndSignal = 1;
1539  }
1540 
1541  // regardless, delete the property I use to transfer data
1542 
1543  XDeleteProperty(pDisplay, wWindow, pT->aProp);
1544  XFlush(pDisplay);
1545  }
1546  }
1547  else // error condition
1548  {
1549  WB_ERROR_PRINT("ERROR: %s line %d - Unable to get window property\n", __FUNCTION__, __LINE__);
1550 
1551  pT->fState = -1; // an error
1552 
1553  bDoneAndSignal = 1;
1554  }
1555  }
1556  else if(pT->fState == 2) // incrementally reading the data
1557  {
1558  WB_ERROR_PRINT("WARNING: %s - INCREMENTAL not supported (yet)\n", __FUNCTION__);
1559 
1560  pT->fState = -1; // an error
1561 
1562  bDoneAndSignal = 1;
1563  }
1564 
1565  if(pBuf)
1566  {
1567  XFree(pBuf);
1568  }
1569  }
1570 
1571  if(bDoneAndSignal)
1572  {
1574  // TASK IS COMPLETE - SIGNAL THE CALLER
1576 
1577  WB_ERROR_PRINT("TEMPORARY: %s line %d - put thingy in done list, signal caller\n", __FUNCTION__, __LINE__);
1578 
1579  // after copying the data, remove it from the 'run' list
1580 
1581  if(pT2) // at THIS point, the 'prev' pointer for unlinking
1582  {
1583  pT2->pNext = pT->pNext;
1584  }
1585  else
1586  {
1587  pRunList = (CLIPBOARD_TASK *)pT->pNext;
1588  }
1589 
1590  pT->pNext = NULL;
1591 
1592  // now add it to the 'done' list
1593 
1594  if(!pDoneList)
1595  {
1596  pDoneList = (CLIPBOARD_TASK *)pT;
1597  }
1598  else
1599  {
1600  pT3 = pDoneList; // at THIS point, pT2 walks the 'done' list
1601 
1602  while(pT3->pNext)
1603  {
1604  pT3 = (CLIPBOARD_TASK *)pT3->pNext;
1605  }
1606 
1607  pT3->pNext = pT;
1608  }
1609 
1610  // must now signal the owner. Since I'm using the 'done list' to decrement
1611  // the reference count, the object will alays be 'good' here. So I just
1612  // need to signal the 'cond' object to get the owner to recognize I'm done
1613  // with the operation, and everything should be a-OK.
1614 
1615  WBCondSignal((WB_COND *)&(pT->cond)); // tell caller I'm done
1616 
1617  WB_ERROR_PRINT("TEMPORARY: %s line %d - signaled caller\n", __FUNCTION__, __LINE__);
1618 
1619  // now - leave pT2 'as-is' here but assign 'pT' to the 'pT2->pNext'
1620  if(pT2)
1621  {
1622  pT = pT2->pNext; // point to next item in list (after the one I moved)
1623  }
1624  else if(pRunList)
1625  {
1626  pT = pRunList->pNext;
1627  }
1628  else
1629  {
1630  pT = NULL; // end of loop
1631  }
1632  }
1633  else
1634  {
1635  // loop to next thingy
1636 
1637  pT2 = (CLIPBOARD_TASK *)pT; // my new 'prev' pointer for unlinking (must re-assign here)
1638  pT = pT->pNext; // point to next item in list
1639  }
1640  }
1641  }
1642  }
1643 
1644  // check for the 'quit' flag before doing anything else
1645 
1646  if(bClipboardQuitFlag)
1647  {
1648  break;
1649  }
1650 
1652  // end of loop
1654 
1655  if(!pCBTHead && // go ahead and test outside of a lock. if NULL, I need to sleep a bit
1656  XEventsQueued(pDisplay, QueuedAlready) <= 0) // no events
1657  {
1658  static unsigned long long ullLastTime = 0;
1659  unsigned long long ullTemp;
1660 
1661  // if nothing to do, sleep or something rather than spinning
1662  // this way if I have events to process, or if I have tasks to manage,
1663  // I can continue to cycle without delays
1664 
1665 // XFlush(pDisplay); // force flush just in case
1666 
1667  WBDelay(iDelayPeriod); // 1000); //if I'm not busy, use a sleep state to limit CPU utilization in the thread
1668 
1669  ullTemp = WBGetTimeIndex();
1670  if((ullTemp - ullLastTime) > 50000) // make sure it's more than 0.05 seconds, so I don't "spin"
1671  {
1672  ullLastTime = ullTemp;
1673 
1675  XSync(pDisplay, False); // force sync just in case
1677  }
1678 
1679  // each time through, advance the delay period by 50%
1680 
1681  if(iDelayPeriod < MAX_EVENT_LOOP_SLEEP_PERIOD)
1682  {
1683  iDelayPeriod += (iDelayPeriod >> 1);
1684  }
1685  else
1686  {
1687  iDelayPeriod = MAX_EVENT_LOOP_SLEEP_PERIOD;
1688  }
1689  }
1690  else
1691  {
1692  iDelayPeriod = MIN_EVENT_LOOP_SLEEP_PERIOD; // reset delay period to 0.1 seconds
1693  }
1694  }
1695 
1696  WB_DEBUG_PRINT(DebugLevel_Medium | DebugSubSystem_Init,
1697  "INFO: %s line %d - exit from main thread loop\n", __FUNCTION__, __LINE__);
1698 
1699 
1700  // At this point the clipboard thread is done. time to go away. clean up.
1701 
1702 
1703  // resource cleanup (which should be relatively simple)
1704  if(WBMutexLock(&xClipboardMutex, -1)) // wait forever to lock it (very important this succeeds)
1705  {
1706  WB_ERROR_PRINT("ERROR: %s - Clipboard Thread can't lock mutex on exit\n",__FUNCTION__);
1707  }
1708 
1709  while(pDoneList) // already signaled
1710  {
1711  pT = pDoneList;
1712  pDoneList = (CLIPBOARD_TASK *)pT->pNext;
1713 
1714  pT->pNext = NULL;
1715 
1716  if(!WBInterlockedDecrement((unsigned int *)&(pT->nRefCount))) // flags a delete when ref count is zero
1717  {
1718  if(!pT->fType && pT->pData) // it COULD happen...
1719  {
1720  WBFree(pT->pData);
1721  pT->pData = NULL;
1722  }
1723 
1724  WBCondFree((WB_COND *)&(pT->cond)); // assume it works
1725 
1726  WBFree((void *)pT); // do this while locked
1727  }
1728  }
1729 
1730  while(pRunList) // need to be signaled
1731  {
1732  pT = pRunList;
1733  pRunList = (CLIPBOARD_TASK *)pT->pNext;
1734 
1735  pT->pNext = NULL;
1736 
1737  WBCondSignal((WB_COND *)&(pT->cond)); // do this, regardless (won't wake up unless mutex unowned)
1738 
1739  if(!WBInterlockedDecrement((unsigned int *)&(pT->nRefCount))) // flags a delete when ref count is zero
1740  {
1741  if(!pT->fType && pT->pData) // it COULD happen...
1742  {
1743  WBFree(pT->pData);
1744  pT->pData = NULL;
1745  }
1746 
1747  WBCondFree((WB_COND *)&(pT->cond)); // assume it works
1748 
1749  WBFree((void *)pT); // do this while locked
1750  }
1751  }
1752 
1753  while(pCBTHead)
1754  {
1755  pT = pCBTHead;
1756  pCBTHead = pT->pNext;
1757 
1758  pT->pNext = NULL;
1759 
1760  WBCondSignal((WB_COND *)&(pT->cond)); // do this, regardless (won't wake up unless mutex unowned)
1761 
1762  if(!WBInterlockedDecrement((unsigned int *)&(pT->nRefCount))) // flags a delete when ref count is zero
1763  {
1764  // NOTE: unless it was mis-configured, these entries should NOT hve allocated 'pData', evar
1765 
1766  WBCondFree((WB_COND *)&(pT->cond)); // assume it works
1767 
1768  WBFree((void *)pT); // do this while locked
1769  }
1770  }
1771 
1772  XFlush(pDisplay);
1773  XSync(pDisplay, False);
1774 
1775  // and finally, unlock that mutex!
1776  WBMutexUnlock(&xClipboardMutex); // not locked now
1777 
1778  // now remove any data items I might be cacheing
1779 
1780  while(pCBDHead) // this one is likely to have data in it
1781  {
1782  CLIPBOARD_DATA *pD = pCBDHead;
1783  pCBDHead = pCBDHead->pNext;
1784 
1785  if(pDisplay && wWindow != None)
1786  {
1787  if(pD->aSelection != None &&
1788  XGetSelectionOwner(pDisplay, pD->aSelection) == wWindow)
1789  {
1790  XSetSelectionOwner(pDisplay, pD->aSelection, None, CurrentTime);
1791  }
1792  else if(pD->aSelection == None)
1793  {
1794  if(XGetSelectionOwner(pDisplay, aCLIPBOARD) == wWindow)
1795  {
1796  XSetSelectionOwner(pDisplay, aCLIPBOARD, None, CurrentTime);
1797  }
1798  else if(XGetSelectionOwner(pDisplay, XA_PRIMARY) == wWindow)
1799  {
1800  XSetSelectionOwner(pDisplay, XA_PRIMARY, None, CurrentTime);
1801  }
1802  }
1803  }
1804 
1805  WBFree(pD); // this one is very simple
1806  }
1807 
1808  // OK no longer owning the clipboard, so now it's time to
1809 
1810  if(pDisplay)
1811  {
1813  XFlush(pDisplay);
1815  }
1816 
1817 exit_point:
1818 
1819  if(wWindow != None)
1820  {
1822  XDestroyWindow(pDisplay, wWindow);
1824  }
1825 
1826  if(pDisplay)
1827  {
1829  XSync(pDisplay, FALSE); // try sync'ing first to avoid certain errors
1830  XCloseDisplay(pDisplay); // display is to be destroyed now
1832  }
1833 
1834  WB_DEBUG_PRINT(DebugLevel_Light | DebugSubSystem_Init,
1835  "INFO: %s - Clipboard Thread exit complete\n",__FUNCTION__);
1836 
1837  return NULL;
1838 }
1839 
1840 
1841 
1842 void * WBGetClipboardData(Display *pDisplay, Atom *paType, int *piFormat, unsigned long *pnData)
1843 {
1844  // TODO: find better way than using 'None' for the 'aSelection' parameter in order to get "that behavior"
1845 
1846  return WBGetSelectionData(pDisplay, None, paType, piFormat, pnData);
1847 }
1848 
1849 int WBSetClipboardData(Display *pDisplay, Atom aType, int iFormat, const void *pData, unsigned long nData)
1850 {
1851  // TODO: find better way than using 'None' for the 'aSelection' parameter in order to get "that behavior"
1852 
1853  return WBSetSelectionData(pDisplay, None, aType, iFormat, pData, nData);
1854 }
1855 
1856 void * WBGetSelectionData(Display *pDisplay, Atom aSelection, Atom *paType, int *piFormat, unsigned long *pnData)
1857 {
1858 void *pRval = NULL;
1859 volatile CLIPBOARD_TASK *pT, *pTask;
1860 Atom aType = None;
1861 int iFormat = 0;
1862 int iErr;
1863 
1864 
1865  if(paType)
1866  {
1867  aType = *paType;
1868  *paType = None; // this is actually my 'not found' indicator
1869  }
1870 
1871  if(piFormat)
1872  {
1873  iFormat = *piFormat;
1874  *piFormat = 0;
1875  }
1876 
1877  if(pnData)
1878  {
1879  *pnData = 0; // always
1880  }
1881 
1882  // for now, if 'pDisplay' isn't NULL or the default display, this will fail
1883  if(pDisplay && pDisplay != WBGetDefaultDisplay())
1884  {
1885  return NULL;
1886  }
1887 
1888  pTask = (CLIPBOARD_TASK *)WBAlloc(sizeof(*pTask));
1889  if(!pTask)
1890  {
1891  WB_ERROR_PRINT("%s - can't create 'CLIPBOARD_TASK' for clipboard task\n", __FUNCTION__);
1892 
1893  return NULL;
1894  }
1895 
1896  memset((void *)pTask, 0, sizeof(*pTask)); // always start by doing this
1897 
1898  pTask->fType = 0;
1899  pTask->pData = NULL; // always for 'get'
1900  pTask->cbLength = 0;
1901  pTask->aSelection = aSelection;
1902  pTask->aType = aType;
1903  pTask->nFormat = iFormat;
1904  pTask->nRefCount = 1;
1905  pTask->pNext = NULL;
1906 
1907  // create the 'cond' object
1908  if(WBCondCreate((WB_COND *)&(pTask->cond)))
1909  {
1910  WB_ERROR_PRINT("%s - can't create 'cond' for clipboard task\n", __FUNCTION__);
1911 
1912  goto exit_point0;
1913  }
1914 
1915  // own the global mutex and add this to the linked list. then wait on it.
1916 
1917  if(WBMutexLock(&xClipboardMutex, 200000L)) // 0.2 seconds
1918  {
1919  WB_ERROR_PRINT("%s - can't lock mutex for clipboard task\n", __FUNCTION__);
1920 
1921  goto exit_point;
1922  }
1923 
1924  // put it in the chain
1925 
1926  pT = pCBTHead;
1927 
1928  while(pT && pT->pNext)
1929  {
1930  pT = pT->pNext;
1931  }
1932 
1933  if(pT)
1934  {
1935  pT->pNext = pTask; // end of chain
1936  }
1937  else
1938  {
1939  pCBTHead = pTask; // head of chain
1940  }
1941 
1942  // wait for it to complete [TODO: do I need to do this?]
1943 
1944  iErr = WBCondWaitMutex((WB_COND *)&(pTask->cond), &xClipboardMutex, 200000L); // wait up to 0.2 sec for completion
1945 
1946  if(!iErr)
1947  {
1948 // WB_ERROR_PRINT("TEMPORARY: %s - completed wait\n", __FUNCTION__);
1949 
1950  pRval = pTask->pData; // always for 'get'
1951  pTask->pData = NULL; // so I don't accidentally free it, evar [assume WBAlloc from other thread is OK]
1952 
1953  if(paType)
1954  {
1955  *paType = pTask->aType;
1956  }
1957 
1958  if(piFormat)
1959  {
1960  *piFormat = pTask->nFormat;
1961  }
1962 
1963  if(pnData)
1964  {
1965  *pnData = pTask->cbLength;
1966  }
1967 
1968  // TODO: remove these extra checks later. also may add stuff to the main
1969  // thread loop to make sure that the 'done' list gets freed up at a reasonable rate.
1970 
1971  if(pTask->pNext)
1972  {
1973  WB_ERROR_PRINT("ERROR: %s - clipboard task %p has non-NULL 'pNext' on return (%p), refcount = %d\n",
1974  __FUNCTION__, pTask, pTask->pNext, pTask->nRefCount);
1975  }
1976  else if(pTask->nRefCount < 1 || pTask->nRefCount > 2) // should be 2, may be 1, anything else unexpected
1977  {
1978  WB_ERROR_PRINT("ERROR: %s - clipboard task %p has wrong refcount %d\n",
1979  __FUNCTION__, pTask, pTask->nRefCount);
1980  }
1981  else if(!pRval)
1982  {
1983  WB_ERROR_PRINT("TEMPORARY: %s - returning NULL\n", __FUNCTION__);
1984  }
1985 // else
1986 // {
1987 // WB_ERROR_PRINT("TEMPORARY: %s - returning %p\n", __FUNCTION__, pRval);
1988 // }
1989  }
1990  else
1991  {
1992  // some kind of error - unhook it from the chain. mutex is owned.
1993 
1994  WB_ERROR_PRINT("%s - %s error waiting on cond,mutex for completion\n", __FUNCTION__,
1995  (const char *)(iErr > 0 ? "timeout" : "unknown"));
1996 
1997 
1998  // the mutex is owned while I'm doing this, so it's ok
1999 
2000  pT = pCBTHead; // if it hasn't been processed yet, it's in this linked list
2001 
2002  while(pT && pT->pNext)
2003  {
2004  if(pT->pNext == pTask)
2005  {
2006  pT->pNext = pTask->pNext;
2007 
2008  break;
2009  }
2010  }
2011 
2012  // NOTE: if it wasn't in the 'pCBTHead' list, it's probably in the 'done' list and will be deleted.
2013  // but as a matter of course, while the mutex is locked, it's not going away. SO, I mark the
2014  // state with an 'error' flag.
2015 
2016  WBInterlockedExchange((unsigned int *)&(pTask->fState), -1); // changes state to 'error' so it will self-clean-up
2017  // in case that it wasn't in 'pCBTHead', which is likely
2018  }
2019 
2020  // at this point 'xClipboardMutex' is assumed to be LOCKED and I can go ahead and mess with the 'pTask' object
2021  // it should also be OUT of the 'pCBTHead' list, but may be in the 'done' list.
2022 
2023  if(!WBInterlockedDecrement((unsigned int *)&(pTask->nRefCount))) // flags a delete when ref count is zero
2024  {
2025 // WB_ERROR_PRINT("TEMPORARY: %s - ref count zero, deleting 'pTask'\n", __FUNCTION__);
2026 
2027  if(pTask->pData) // it COULD happen...
2028  {
2029  WBFree(pTask->pData);
2030  pTask->pData = NULL;
2031  }
2032 
2033  WBCondFree((WB_COND *)&(pTask->cond)); // assume it works
2034 
2035  WBFree((void *)pTask); // do this while locked
2036  }
2037 
2038  pTask = NULL; // so I don't try to re-use it
2039 
2040 // WB_ERROR_PRINT("TEMPORARY: %s - release mutex before exit\n", __FUNCTION__);
2041 
2042  WBMutexUnlock(&xClipboardMutex);
2043 
2044 exit_point:
2045 
2046  if(pTask)
2047  {
2048  WBCondFree((WB_COND *)&(pTask->cond)); // assume it works
2049  }
2050 
2051 exit_point0:
2052 
2053  if(pTask)
2054  {
2055  WBFree((void *)pTask); // TODO: on error, this might cause a crash... or not
2056  }
2057 
2058  return pRval; // for now (similar to an error return)
2059 }
2060 
2061 int WBSetSelectionData(Display *pDisplay, Atom aSelection, Atom aType, int iFormat, const void *pData, unsigned long nData)
2062 {
2063 int iRval = -1;
2064 volatile CLIPBOARD_TASK *pT, *pTask;
2065 
2066 
2067  // for now, if 'pDisplay' isn't NULL or the default display, this will fail
2068  if(pDisplay && pDisplay != WBGetDefaultDisplay())
2069  {
2070  return -1;
2071  }
2072 
2073  pTask = (CLIPBOARD_TASK *)WBAlloc(sizeof(*pTask));
2074  if(!pTask)
2075  {
2076  WB_ERROR_PRINT("%s - can't create 'CLIPBOARD_TASK' for clipboard task\n", __FUNCTION__);
2077 
2078  return -1;
2079  }
2080 
2081  memset((void *)pTask, 0, sizeof(*pTask)); // always start by doing this
2082 
2083  pTask->fType = 1;
2084  pTask->pData = (void *)pData; // will be treated as 'const'
2085  pTask->cbLength = nData;
2086  pTask->aSelection = aSelection;
2087  pTask->aType = aType;
2088  pTask->nFormat = iFormat;
2089  pTask->nRefCount = 1;
2090  pTask->pNext = NULL;
2091 
2092  // create the 'cond' object
2093  if(WBCondCreate((WB_COND *)&(pTask->cond)))
2094  {
2095  WB_ERROR_PRINT("%s - can't create 'cond' for clipboard task\n", __FUNCTION__);
2096 
2097  iRval = -2;
2098  goto exit_point0;
2099  }
2100 
2101  // own the global mutex and add this to the linked list. then wait on it.
2102 
2103  if(WBMutexLock(&xClipboardMutex, 200000L)) // up to 0.2 second for locking
2104  {
2105  WB_ERROR_PRINT("%s - can't lock mutex for clipboard task\n", __FUNCTION__);
2106 
2107  iRval = -3;
2108  goto exit_point;
2109  }
2110 
2111  // put pTask in the chain
2112 
2113  pT = pCBTHead;
2114 
2115  while(pT && pT->pNext)
2116  {
2117  pT = pT->pNext;
2118  }
2119 
2120  if(pT)
2121  {
2122  pT->pNext = pTask;
2123  }
2124  else
2125  {
2126  pCBTHead = pTask;
2127  }
2128 
2129  // wait for it to complete [TODO: do I need to do this?]
2130 
2131  if(WBCondWaitMutex((WB_COND *)&(pTask->cond), &xClipboardMutex, 200000L)) // wait up to 0.2 sec for completion
2132  {
2133  // some kind of error - unhook it from the chain. mutex is owned.
2134  WB_ERROR_PRINT("%s - error waiting on cond,mutex for completionk\n", __FUNCTION__);
2135 
2136  pT = pCBTHead; // if it hasn't been processed yet, it's in this linked list
2137 
2138  while(pT && pT->pNext)
2139  {
2140  if(pT->pNext == pTask)
2141  {
2142  pT->pNext = pTask->pNext;
2143 
2144  break;
2145  }
2146  }
2147 
2148  WBInterlockedExchange((unsigned int *)&(pTask->fState), -1); // changes state to 'error' so it will self-clean-up
2149  // in case that it wasn't in 'pCBTHead', which is likely
2150 
2151  iRval = -4;
2152  }
2153 
2154  // at this point 'xClipboardMutex' is assumed to be LOCKED
2155 
2156  if(!WBInterlockedDecrement((unsigned int *)&(pTask->nRefCount))) // flags a delete when ref count is zero
2157  {
2158  WBCondFree((WB_COND *)&(pTask->cond)); // assume it works
2159 
2160  WBFree((void *)pTask); // do this while locked
2161  }
2162 
2163  pTask = NULL; // so I don't try to re-use it
2164 
2165  WBMutexUnlock(&xClipboardMutex);
2166 
2167 exit_point:
2168 
2169  if(pTask)
2170  {
2171  WBCondFree((WB_COND *)&(pTask->cond)); // assume it works
2172  }
2173 
2174 exit_point0:
2175 
2176  if(pTask)
2177  {
2178  WBFree((void *)pTask); // TODO: on error, this might cause a crash... or not
2179  }
2180 
2181  return iRval;
2182 }
2183 
2184 
2185 
2186 
WB_UINT32 WB_COND
CONDITION HANDLE equivalent (similar to an 'event')
Atom aCOMPOUND_TEXT
COMPOUND_TEXT Atom for the clipboard.
'window helper' main header file for the X11workbench Toolkit API
#define WB_DEBUG_PRINT(L,...)
Preferred method of implementing conditional debug output.
Definition: debug_helper.h:364
Utilities for copying and drawing text, determining text extents, and so on.
int WBMutexUnlock(WB_MUTEX *pMtx)
Unlock a previously locked mutex.
Atom aMULTIPLE
MULTIPLE Atom for the clipboard.
void * WBThreadWait(WB_THREAD hThread)
Wait for a specified threat to exit.
int __StartInitClipboardSystem(Display *pDisplay, const char *szDisplayName)
initializes clipboard sub-system
Atom aINCR
INCR Atom for the clipboard.
const char * GetStartupDisplayName(void)
returns character name of the display to be opened and passed to WBInit
unsigned int WBInterlockedIncrement(volatile unsigned int *pValue)
Interlocked 'atomic' increment of an unsigned integer.
int WBSetSelectionData(Display *pDisplay, Atom aSelection, Atom aType, int iFormat, const void *pData, unsigned long nData)
Get clipboard data of requested type.
static __inline__ Display * WBGetDefaultDisplay(void)
Returns the default Display.
'configuration helper' main header file for the X11 Work Bench Toolkit API
unsigned int WBInterlockedExchange(volatile unsigned int *pValue, unsigned int nNewVal)
Interlocked 'atomic' exchange of an unsigned integer with a specified value.
Atom aTIMESTAMP
TIMESTAMP Atom for the clipboard.
pthread_mutex_t WB_MUTEX
MUTEX HANDLE equivalent.
int WBSetClipboardData(Display *pDisplay, Atom aType, int iFormat, const void *pData, unsigned long nData)
Get clipboard data of requested type.
Atom aPIXEL
PIXEL Atom for the clipboard.
WB_UINT64 WBGetTimeIndex(void)
Returns the current 'time index' (in microseconds)
WB_THREAD WBThreadCreate(void *(*function)(void *), void *pParam)
Create a new thread, returning its WB_THREAD identifier.
void * WBAlloc(int nSize)
High performance memory sub-allocator 'allocate'.
int WBCondSignal(WB_COND *pCond)
Signal a condition (event)
#define END_XCALL_DEBUG_WRAPPER
wrapper macro for calls into the X11 library. This macro follows the call(s)
unsigned long long WB_UINT64
Platform abstract unsigned 64-bit integer.
#define WB_ERROR_PRINT(...)
Preferred method of implementing an 'error level' debug message for all subsystems.
Definition: debug_helper.h:474
void WBFree(void *pBuf)
High performance memory sub-allocator 'free'.
void WBDelay(uint32_t uiDelay)
Delay for a specified period in microseconds.
unsigned int WBInterlockedDecrement(volatile unsigned int *pValue)
Interlocked 'atomic' decrement of an unsigned integer.
void * WBGetSelectionData(Display *pDisplay, Atom aSelection, Atom *paType, int *piFormat, unsigned long *pnData)
Get clipboard data of requested type.
Atom aCLIPBOARD
CLIPBOARD Atom for the clipboard.
A 'C++'-like object for managing text, that can be overridden for custom behavior.
void WBDebugDumpEvent(XEvent *pEvent)
dumps the contents of an XEvent
int WBMutexCreate(WB_MUTEX *pMtx)
Create a lockable mutex.
int WBMutexLock(WB_MUTEX *pMtx, int nTimeout)
Wait for and lock a mutex, blocking until it is available.
int WBThreadRunning(WB_THREAD hThread)
Determine whether a thread is running (can be suspended)
int WBInitClipboardSystem(Display *pDisplay, const char *szDisplayName)
initializes clipboard sub-system
Atom aTARGETS
TARGETS Atom for the clipboard.
void WBMutexFree(WB_MUTEX *pMtx)
Free a lockable mutex.
#define BEGIN_XCALL_DEBUG_WRAPPER
wrapper macro for calls into the X11 library. This macro precedes the call(s)
char * WBGetAtomName(Display *pDisplay, Atom aAtom)
Lookup and/or allocate an internal Atom for a named string.
pthread_t WB_THREAD
THREAD HANDLE equivalent.
#define INVALID_HANDLE_VALUE
INVALID HANDLE VALUE equivalent.
void WBDebugPrint(const char *pFmt,...) __attribute__((format(printf
conditional debug message output
char * WBCopyString(const char *pSrc)
A simple utility that returns a WBAlloc() copy of a 0-byte terminated string.
Definition file for platform-specific utility functions.
Atom aNULL
NULL Atom for the clipboard.
int WBCondWaitMutex(WB_COND *pCond, WB_MUTEX *pMtx, int nTimeout)
Wait for a signal on a condition (event)
void * WBGetClipboardData(Display *pDisplay, Atom *paType, int *piFormat, unsigned long *pnData)
Get clipboard data of requested type.
Atom aTEXT
TEXT Atom for the clipboard.
Atom aC_STRING
C_STRING Atom for the clipboard.
int __FinishInitClipboardSystem(Display *pDisplay, const char *szDisplayName)
initializes clipboard sub-system
unsigned int WBInterlockedRead(volatile unsigned int *pValue)
Interlocked 'atomic' read of an unsigned integer.
int WBCondCreate(WB_COND *pCond)
Create a signallable condition.
void WBExitClipboardSystem(Display *pDisplay)
Shut down the clipboard sub-system.
Atom aUTF8_STRING
UTF8_STRING Atom for the clipboard.
void WBCondFree(WB_COND *pCond)
Free a signallable condition.