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