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