X11 Work Bench Toolkit  1.0
window_helper.c
Go to the documentation of this file.
1 
2 // _ _ _ _ //
3 // __ __(_) _ __ __| | ___ __ __ | |__ ___ | | _ __ ___ _ __ ___ //
4 // \ \ /\ / /| || '_ \ / _` | / _ \\ \ /\ / / | '_ \ / _ \| || '_ \ / _ \| '__|/ __| //
5 // \ V V / | || | | || (_| || (_) |\ V V / | | | || __/| || |_) || __/| | _| (__ //
6 // \_/\_/ |_||_| |_| \__,_| \___/ \_/\_/_____|_| |_| \___||_|| .__/ \___||_|(_)\___| //
7 // |_____| |_| //
8 // //
9 // basic window creation and message handling //
10 // //
12 
13 /*****************************************************************************
14 
15  X11workbench - X11 programmer's 'work bench' application and toolkit
16  Copyright (c) 2010-2016 by Bob Frazier (aka 'Big Bad Bombastic Bob')
17  all rights reserved
18 
19  DISCLAIMER: The X11workbench application and toolkit software are supplied
20  'as-is', with no warranties, either implied or explicit.
21  Any claims to alleged functionality or features should be
22  considered 'preliminary', and might not function as advertised.
23 
24  BSD-like license:
25 
26  There is no restriction as to what you can do with this software, so long
27  as you include the above copyright notice and DISCLAIMER for any distributed
28  work that is equal to or derived from this one, along with this paragraph
29  that explains the terms of the license if the source is also being made
30  available. A "derived work" describes a work that uses a significant portion
31  of the source files or algorithms that are included with this one.
32  Specifically excluded from this are files that were generated by the software,
33  or anything that is included with the software that is part of another package
34  (such as files that were created or added during the 'configure' process).
35  Specifically included is the use of part or all of any of the X11 workbench
36  toolkit source or header files in your distributed application. If you do not
37  ship the source, the above copyright statement is still required to be placed
38  in a reasonably prominent place, such as documentation, splash screens, and/or
39  'about the application' dialog boxes.
40 
41  Use and distribution are in accordance with GPL, LGPL, and/or the above
42  BSD-like license. See COPYING and README files for more information.
43 
44 
45  Additional information at http://sourceforge.net/projects/X11workbench
46 
47 ******************************************************************************/
48 
49 
64 #include <stdio.h>
65 #include <stdlib.h>
66 #include <stdarg.h>
67 #include <limits.h>
68 #include <unistd.h>
69 #include <memory.h>
70 #include <string.h>
71 #include <strings.h>
72 #include <signal.h>
73 #include <time.h>
74 #include <fcntl.h>
75 #include <errno.h>
76 #include <sys/time.h>
77 
78 // TODO: determine if pthread is available, optionally use for timers
79 #include <pthread.h> /* currently required */
80 
81 
82 #include <X11/cursorfont.h>
83 #ifndef XK_Delete /* moslty for interix */
84 #define XK_MISCELLANY /* mostly for interix */
85 #include <X11/keysymdef.h> // some platforms don't automatically include this with X headers
86 #endif // XK_Delete
87 
88 #define _WINDOW_HELPER_C_ /*this causes some header definitions to change slightly */
89 
90 #include "window_helper.h"
91 #include "pixmap_helper.h"
92 #include "conf_help.h"
93 
94 
95 const char *sz_xcall_func = NULL;
96 int i_xcall_line = 0;
97 
98 
99 
100 // **************************************************************************
101 // ALGORITHMS AND OTHER RELATED NOTES
102 // the hash algorithm is simple. First, apply 'WINDOW_ENTRY_HASH' to the
103 // window ID. Then use it as an index into the array of '_WINDOW_ENTRY_'
104 // structures. If the entry it points to does NOT match the window ID I'm
105 // interested in, increment the index 'mod ARRAY SIZE' until I either find
106 // the matching window ID, wrap around, or find a zero window ID. Wrapping
107 // around is basically prevented by ensuring that the array is at least twice
108 // the size of the maximum allowed window count. And, for the above reasons,
109 // the size of the array must be a power of 2 so that the hash works properly.
110 
111 // NOTE: window assignments tend to be consecutive so I must scramble them
112 // up or risk inefficiency in the hash algorithm. This can be
113 // improved later to minimize the impact of this behavior.
114 //
115 // NOTE 2: Window ID and other XID values APPEAR to depend on the definition
116 // of 'RESOURCE_AND_CLIENT_COUNT' and the max # of clients
117 // This basically makes the lower 29 bits of an XID consist of
118 // the client ID (5 to 9 bits) and the resource ID (remaining bits)
119 // with leading bits 31,30,29 all zeros.
120 // It may be possible to create "fake" windows with the high bit set
121 // **************************************************************************
122 
123 
124 /**********************************************************************/
125 /* */
126 /* externally visible global variables (general use) */
127 /* */
128 /**********************************************************************/
129 
130 XFontStruct *pDefaultFont = NULL; // default font
131 XFontSet fontsetDefault = None; // default font set, derived from default font
132 Display *pDefaultDisplay = NULL;
133 int bQuitFlag = FALSE; // defined here, used globally
134 
135 // within this file debug level is writable. outside of this file, it's (technically) read-only
136 unsigned int iWBDebugLevel = 0; // no debug except errors, all subsystems
137 int iStartupMinMax = 0; // main window open min/max/normal flag
138 
139 
140 
141 /**********************************************************************/
142 /* */
143 /* global atoms, initialized via 'WBInit' */
144 /* (atoms remain valid until all connections go away) */
145 /* */
146 /**********************************************************************/
167 Atom aMENU_COMMAND=None;
168 
205 
223 Atom aRESIZE_NOTIFY=None;
224 
232 Atom aDESTROY_NOTIFY=None;
233 
246 Atom aCONTROL_NOTIFY=None;
247 
272 Atom aQUERY_CLOSE=None;
273 
290 Atom aRECALC_LAYOUT=None;
291 
292 
308 Atom aDLG_FOCUS=None;
309 
330 Atom aSET_FOCUS=None;
331 
350 Atom aWM_CHAR=None;
351 
366 Atom aWM_TIMER=None;
367 
406 Atom aWM_POINTER=None;
407 
408 // things used by window managers
419 Atom aWM_PROTOCOLS=None; // WM supported protocols (see 'freedesktop.org' WM docs)
420 
429 Atom aWM_DELETE_WINDOW=None; // WM command to delete a window (a click on the 'x')
430 
440 Atom aWM_TAKE_FOCUS=None;
441 
442 
449 Atom aAVERAGE_WIDTH=None;
450 
451 
457 Atom aCLIPBOARD=None; // Atom for 'CLIPBOARD'
463 Atom aPRIMARY=XA_PRIMARY; // Atom for 'PRIMARY' XA_PRIMARY
469 Atom aSECONDARY=XA_SECONDARY;// Atom for 'SECONDARY' XA_SECONDARY
475 Atom aMANAGER=None; // Atom for 'MANAGER'
481 Atom aTARGET=None; // Atom for 'TARGET'
487 Atom aINCR=None; // Atom for 'INCR' (incremental transfers)
493 Atom aWINDOW=XA_WINDOW; // Atom for 'WINDOW'
499 Atom aBITMAP=XA_BITMAP; // Atom for 'BITMAP'
505 Atom aDRAWABLE=XA_DRAWABLE; // Atom for 'DRAWABLE'
511 Atom aCOLORMAP=XA_COLORMAP; // Atom for 'COLORMAP'
517 Atom aPIXEL=None; // Atom for 'PIXEL'
523 Atom aPIXMAP=XA_PIXMAP; // Atom for 'PIXMAP'
529 Atom aTEXT=None; // Atom for 'TEXT'
535 Atom aSTRING=XA_STRING; // Atom for 'STRING'
541 Atom aUTF8_STRING=None; // Atom for 'UTF8_STRING'
547 Atom aC_STRING=None; // Atom for 'C_STRING'
553 Atom aCOMPOUND_TEXT=None; // Atom for 'COMPOUND_TEXT'
559 Atom aTARGETS=None; // Atom for 'TARGETS'
565 Atom aMULTIPLE=None; // Atom for 'MULTIPLE'
571 Atom aTIMESTAMP=None; // Atom for 'TIMESTAMP'
577 Atom aNULL=None; // Atom for 'NULL'
578 
579 
580 /**********************************************************************/
581 /* */
582 /* module-specific globals and structures for window processing */
583 /* */
584 /**********************************************************************/
585 
586 // TODO: do I implement this data type as a 'super type' of WBWindow ?
587 //typedef unsigned long long WBWindow;
588 //
589 //#define WBWINDOW_MASK 0xffffffff /* portion of WBWindow that is an actual Window ID */
590 //#define WBWINDOW_FLAGS_NORMAL 0x0
591 //#define WBWINDOW_FLAGS_PSEUDO 0x100000000LL /* tag WBWindow as a 'pseudo' window ID */
592 
593 #ifdef _XSERVER64
594 #error _XSERVER64 defined
595 #endif
596 
597 
598 
615 typedef struct // the structure that identifies the window and what to do with it
616 {
617  Window wID;
618  const char *szClassName;
619  Display *pDisplay;
620  Window wParent;
621  GC hGC;
622  unsigned long clrFG;
623  unsigned long clrBG;
624  XFontStruct *pFontStruct;
625  XFontSet fontSet;
626  XWMHints *pWMHints;
627  Pixmap pxIcon;
628  Pixmap pxMask;
629 // WBWinEvent pCallback;
638  int (* pCallback)(Window wIDEvent, XEvent *pEvent);
639  WB_GEOM geomAbsolute;
640  Region rgnClip;
641  Region rgnPaint;
642  Window wIDMenu;
643 // WBWinEvent pMenuCallback; // valid only for menu windows
645  int (* pMenuCallback)(Window wIDEvent, XEvent *pEvent);
646  Cursor curRecent;
647  int idCursor;
648  int idDefaultCursor;
649  int iWaitCursorCount;
650  int width;
651  int height;
652  int border;
653  int iModalFlag;
654  int iModalReturn;
655  int iWindowState;
656  enum WMPropertiesWindowType eWindowType;
657  enum WMPropertiesWMProtocols eWMProtocols;
658  struct timeval tvLastActivity;
659  void *aWindowData[WINDOW_DATA_SIZE];
660 } _WINDOW_ENTRY_;
661 
662 #define WINDOW_ENTRY_ARRAY_SIZE 2048 /* must be a power of 2 and twice the max expected # of windows */
663 #define WINDOW_ENTRY_ARRAY_MAX 2047 /* must be the above value minus 1 */
664 #define WINDOW_ENTRY_HASH(X) (((((unsigned long)X & 0xf00) + \
665  (((unsigned long)X >> 3) & 0x1f) + \
666  (((unsigned long)X << 5) & 0xe0)) ^ \
667  ((unsigned long)X >> 12)) & WINDOW_ENTRY_ARRAY_MAX)
668 
669 #define WINDOW_ENTRY_UNUSED ((Window)-1)
670 
671 #define WB_WINDOW_DELETE -3 /* entry to be deleted during cleanup phase */
672 #define WB_WINDOW_DESTROYED -2 /* window has been destroyed, entry remains */
673 #define WB_WINDOW_DESTROYING -1 /* not destroyed yet - prevent recursion */
674 #define WB_WINDOW_UNMAPPED 0 /* window created, but not yet mapped */
675 #define WB_WINDOW_MAPPED 1 /* window mapped */
676 #define WB_WINDOW_SET_FOCUS_ON_MAP 2 /* focus is to be set when the window is mapped */
677 #define WB_WINDOW_DELETE_TIMEOUT 5 /* seconds to wait before deleting hash entry (was 30, now 5) */
678 
679 #define WB_CHECK_SET_FOCUS_ON_MAP(X) ((X).iWindowState == WB_WINDOW_SET_FOCUS_ON_MAP)
680 #define WB_IS_WINDOW_UNMAPPED(X) ((X).iWindowState == WB_WINDOW_UNMAPPED || (X).iWindowState == WB_WINDOW_SET_FOCUS_ON_MAP)
681 #define WB_IS_WINDOW_MAPPED(X) ((X).iWindowState == WB_WINDOW_MAPPED)
682 #define WB_IS_WINDOW_BEING_DESTROYED(X) ((X).iWindowState == WB_WINDOW_DESTROYING)
683 #define WB_IS_WINDOW_DESTROYED(X) ((X).iWindowState == WB_WINDOW_DESTROYED || (X).iWindowState == WB_WINDOW_DELETE)
684 #define WB_TO_DELETE_WINDOW_ENTRY(X) ((X).iWindowState == WB_WINDOW_DELETE)
685 
686 
687 //------------------------------------------------------------------------------------------------------
688 // NOT globally visible variables that define the application, default display params, hash table, etc.
689 //------------------------------------------------------------------------------------------------------
690 
691 static _WINDOW_ENTRY_ sWBHashEntries[WINDOW_ENTRY_ARRAY_SIZE]={{0,0}};
692 static volatile int nWBHashEntries = 0;
693 static Window wWBFakeWindow = 0; // fake window keeps me from losing connection when I close all windows
694 static Window wIDApplication = None; // application window ID
695 
696 // ignoring X errors - sometimes errors are expected (!?)
697 int bIgnoreXErrors = 0;
698 static WB_ERROR_INFO xErrorInfo = {NULL, NULL, 0, 0, 0, 0, 0, None};
699 
700 // the default display - if I manage more than one, I may need these to be display-specific
701 Time tmDefaultDisplay = 0; // most recent time value for default display (from events)
702 WB_UINT64 qwDefaultDisplayTick = 0LL; // the tick count for the last 'default display time'
703 
704 int bStandardColormap = 0; // flag that says 'cmapDefault' is initialized
705 XStandardColormap cmapDefault; // a copy of teh XStandardColormap for the Colormap for the default display
706 
707 
708 
709 /**********************************************************************/
710 /* */
711 /* module-specific globals and structures related to event queuing */
712 /* */
713 /**********************************************************************/
714 
715 #define EVENT_ARRAY_SIZE 0x2000 /* this many events maximum */
716 #define EVENT_ARRAY_MASK 0x1fff /* 'and' this for modular math */
717 
727 typedef struct __EVENT_ENTRY__
728 {
729  Display *pDisplay;
730  int iNext;
731  Window wID;
732  XEvent xEvt;
733 
740  char padding[256 - sizeof(Display *) - sizeof(Window) - sizeof(int) - sizeof(XEvent)];
741 
742 } EVENT_ENTRY;
743 
744 static EVENT_ENTRY axWBEvt[EVENT_ARRAY_SIZE];
745 
746 static int iWBQueuedEvent = -1, iWBQueueLast = -1, iWBPaintEvent = -1, iWBPaintLast = -1, iWBFreeEvent = -1;
747 static int iInitEventFlag = 0;
748 
749 //static WBAppEvent pAppEventCallback = NULL;
750 static int (* pAppEventCallback)(XEvent *pEvent) = NULL;
751 
752 
753 #define TIMER_ARRAY_SIZE 512
754 
763 typedef struct __TIMER_ENTRY__
764 {
767  unsigned long lTimeInterval;
768 
769  Display *pDisplay;
770  Window wID;
771  long lID;
772 } TIMER_ENTRY;
773 
774 static TIMER_ENTRY axWBTimer[TIMER_ARRAY_SIZE];
775 static TIMER_ENTRY *pTimerEntryActive = NULL, *pTimerEntryFree = NULL;
776  // pointers for two linked lists. entries must be in either 'active' or 'free' list.
777 
787 {
790 
791  XEvent event;
793 
794 static DELAYED_EVENT_ENTRY axWBDelayedEvent[TIMER_ARRAY_SIZE]; // same size as timers
795 static DELAYED_EVENT_ENTRY *pDelayedEventEntryActive = NULL, *pDelayedEventEntryFree = NULL;
796  // pointers for two linked lists. entries must be in either 'active' or 'free' list.
797 
798 
799 /**********************************************************************/
800 /* */
801 /* local function prototypes */
802 /* */
803 /**********************************************************************/
804 
805 #ifdef NO_DEBUG
806 static __inline__ _WINDOW_ENTRY_ *WBGetWindowEntry(Window wID);
807 #else
808 #define WBGetWindowEntry(X) Debug_WBGetWindowEntry(X, __FUNCTION__, __LINE__)
809 static __inline__ _WINDOW_ENTRY_ *Debug_WBGetWindowEntry(Window wID, const char *szFunction, int nLine);
810 #endif // NO_DEBUG
811 
812 static void __WBInitEvent();
813 static int __WBAddEvent(Display *pDisp, Window wID, XEvent *pEvent);
814 static void __WBDelWindowPaintEvents(Display *pDisp, Window wID);
815 static void __WBDelWindowEvents(Display *pDisp, Window wID);
816 static int __WBInsertPriorityEvent(Display *pDisp, Window wID, XEvent *pEvent);
817 static int __WBNextPaintEvent(Display *pDisp, XEvent *pEvent, Window wID);
818 static int __WBNextDisplayEvent(Display *pDisp, XEvent *pEvent);
819 static void WBInternalProcessExposeEvent(XExposeEvent *pEvent);
820 static int __internal_alloc_WMHints(_WINDOW_ENTRY_ *pEntry);
821 static Window __internal_GetParent(Display *pDisplay, Window wID, Window *pwRoot);
822 static const char * __internal_event_type_string(int iEventType);
823 static int __InternalCheckGetEvent(Display *pDisplay, XEvent *pEvent, Window wIDModal);
824 static void DeletAllTimersForWindow(Display *pDisplay, Window wID);
825 
826 void __InternalDestroyWindow(Display *pDisp, Window wID, _WINDOW_ENTRY_ *pEntry);
827 
828 
829 /********************************************************************************/
830 /* */
831 /* WBInit, WBInitDisplay, WBExit - global initialization/termination functions */
832 /* */
833 /********************************************************************************/
834 
835 static int (*pOldDisplayIOErrorHandler)(Display *) = NULL;
836 static int WBXIOErrorHandler(Display *pDisp);
837 static int WBXErrorHandler(Display *pDisplay, XErrorEvent *pError);
838 static int hBogus[3]; // 'bogus' handles 0-2
839 
840 static WB_GEOM geomStartup
841 #ifdef WB_C99_INITIALIZERS
842  = {.x=0,.y=0,.width=0,.height=0,.border=0}; // C99 syntax; MS does not support this even in 2010 compiler
843  // NOTE: older 'element:value' syntax is supported by GCC only; use '.element=value' (C99) instead
844 #else // WB_C99_INITIALIZERS
845  = {0,0,0,0,0}; // C89 syntax (universally supported)
846 #error this compiler does not support C99 structure initializers. Some code will not (yet) compile.
847 #endif // WB_C99_INITIALIZERS
848 static char szStartupDisplayName[512]=":0.0"; // NOTE: new values must be UTF-8
849 static char szDefaultDisplayName[512]= ""; // copy the "default" display name from WBInit() here
850 
851 // application name (from argv[0])
852 static char szAppName[PATH_MAX * 2]="";
853 
854 
855 Display *WBInit(const char *szDisplayName) // NOTE: this is 'single instance' only...
856 {
857 int i1;
858 Display *pRval;
859 
860 #ifndef WIN32
861  signal(SIGCHLD, SIG_IGN); // make sure that 'SIGCHLD' signals are *IGNORED* - this is important
862 #endif // WIN32
863 
864 
865  WBPlatformOnInit(); // initialize a few important 'platform helper' things
866 
867  bQuitFlag = FALSE; // in case I re-init an app
868 
869  // ********************************************************************************
870  // HACK ALERT - HACK ALERT - HACK ALERT
871  //
872  // before I begin, allocate 3 handles. Any handles <= '2' will be KEPT OPEN
873  // to avoid problems with the X display using things like stderr for I/O
874  // (that is in case stdin, stdout, and stderr weren't allocated or were freed)
875  // This assumes that these 3 aren't already open, but for a windowing application
876  // with no stdin/stdout/stderr, this is a distinct possibility
877  //
878  // It is caused by a limitation of the X11 libraries, and is probably a bug.
879 
880  for(i1=0; i1 < 3; i1++)
881  {
882  hBogus[i1] = open("/dev/null", O_RDWR);
883  }
884 
885  // NOTE: I can choose to clean this up later but for now it shouldn't do
886  // any harm to leave any file handles <= 2 open in 'hBogus'
887 
888  for(i1=2; i1 >= 0; i1--)
889  {
890  if(hBogus[i1] > 2)
891  {
892  close(hBogus[i1]);
893  hBogus[i1] = -1;
894  }
895  }
896 
897  // HACK ALERT - HACK ALERT - HACK ALERT
898  // ********************************************************************************
899 
900  // NEXT, determine what the display name should be and open it
901 
902  if(!szDisplayName)
903  {
904  szDisplayName = GetStartupDisplayName();
905  if(!szDisplayName || !*szDisplayName)
906  {
907  szDisplayName = ":0.0"; // make sure it's SOMETHING
908  }
909  }
910 
911  if((pRval = XOpenDisplay(szDisplayName)) == NULL)
912  {
913  WB_ERROR_PRINT("%s - can't open display %s\n", __FUNCTION__, szDisplayName);
914 
915  return NULL;
916  }
917 
918  // make a copy of the display name for use later on, like for the clipboard initialization
919  strncpy(szDefaultDisplayName, szDisplayName, sizeof(szDefaultDisplayName));
920 
921 
922  // new I/O error handler - 'quit flag' will be set on error but app will
923  // not terminate until the quit flag is 'noticed'
924 
925  pOldDisplayIOErrorHandler = XSetIOErrorHandler(WBXIOErrorHandler);
926 
927  if(WBInitDisplay(pRval)) // open and initialize display and basic windowing stuff
928  {
929  WBExit(); // in case there are resources to free, plus it does XCloseDisplay(pRval);
930 
931  WB_ERROR_PRINT("%s - Unable to initialize X11 WorkBench Toolkit\n", __FUNCTION__);
932 
933  return NULL;
934  }
935 
936 // XFlush(pRval);
937  XSync(pRval, 0); // make sure that the display is "in sync"
938 
939  usleep(100000); // wait 0.1 seconds for everything to stabilize (shorter times not good enough)
940  // sometimes the window manager will get caught in a race condition and
941  // forward along keystrokes that belong to the OLD application, such
942  // as the last <RETURN> keystroke release, which could cause a dialog box
943  // to mis-behave or a window to insert an unnecessary keystroke. Giving
944  // the window manager a chance to NOT be involved in a race condition should
945  // prevent this from happening, or at least minimize the chances.
946 
947  return pRval;
948 }
949 
950 static int WBXIOErrorHandler(Display *pDisp)
951 {
953  "I/O error occurred, setting 'bQuitFlag'\n");
954 
955  bQuitFlag = TRUE; // set QUIT flag on ALL I/O errors. This is the safest way to shut things down.
956 
957 // if(pOldDisplayIOErrorHandler)
958 // return(pOldDisplayIOErrorHandler(pDisp));
959 
960  return 0; // for now will do this
961 }
962 
963 
964 int WBInitDisplay(Display *pDisplay)
965 {
966  pDefaultDisplay = pDisplay;
967 
968  // create a 'fake' window that will keep me from losing connection when I terminate the app
969  wWBFakeWindow = XCreateSimpleWindow(pDisplay, DefaultRootWindow(pDisplay), -1, -1, 1, 1, 0, 0, 0);
970 
971  /*
972  * Load the font to use. See Sections 10.2 & 6.5.1 of the X11 docs
973  */
974 // pDefaultFont = XLoadQueryFont(pDisplay, WB_DEFAULT_FONT);
975  pDefaultFont = WBLoadFont(pDisplay, WB_DEFAULT_FONT, WB_DEFAULT_FONT_SIZE, 0);
976 
977  if(!pDefaultFont)
978  {
979  pDefaultFont = WBLoadFont(pDisplay, "*", WB_DEFAULT_FONT_SIZE,
981 
982  if(!pDefaultFont)
983  {
984  pDefaultFont = WBLoadFont(pDisplay, "*", WB_DEFAULT_FONT_SIZE, 0);
985 
986  if(!pDefaultFont)
987  {
988  WB_ERROR_PRINT("%s - NO DEFAULT FONT, returning 0\n", __FUNCTION__);
989  return 0;
990  }
991  }
992  }
993 
994  fontsetDefault = WBFontSetFromFont(pDisplay, pDefaultFont);
995 
996  // NOTE: the documentation for 'XInternAtom' does _NOT_ include a provision to
997  // free up unused atoms. There's no obvious way to get rid of the ones that
998  // are not being used any more. The docs say the following:
999  // "It will become undefined only when the last connection to the X server closes."
1000  // (As such, I should minimize introducing NEW atoms, even though it's kinda necessary)
1001  //
1002  // To compensate, MANY of the atoms that I create will use WBGetAtom() rather than XInternAtom()
1003 
1004  aMENU_COMMAND = WBGetAtom(pDisplay, "MENU_COMMAND");//XInternAtom(pDisplay, "MENU_COMMAND", False);
1005  aMENU_UI_COMMAND = WBGetAtom(pDisplay, "MENU_UI_COMMAND");//XInternAtom(pDisplay, "MENU_UI_COMMAND", False);
1006  aRESIZE_NOTIFY = WBGetAtom(pDisplay, "RESIZE_NOTIFY");//XInternAtom(pDisplay, "RESIZE_NOTIFY", False);
1007  aCONTROL_NOTIFY = WBGetAtom(pDisplay, "CONTROL_NOTIFY");//XInternAtom(pDisplay, "CONTROL_NOTIFY", False);
1008  aQUERY_CLOSE = WBGetAtom(pDisplay, "QUERY_CLOSE");//XInternAtom(pDisplay, "QUERY_CLOSE", False);
1009  aRECALC_LAYOUT = WBGetAtom(pDisplay, "RECALC_LAYOUT");//XInternAtom(pDisplay, "QUERY_CLOSE", False);
1010  aDESTROY_NOTIFY = WBGetAtom(pDisplay, "DESTROY_NOTIFY");//XInternAtom(pDisplay, "DESTROY_NOTIFY", False);
1011  aDLG_FOCUS = WBGetAtom(pDisplay, "DLG_FOCUS");//XInternAtom(pDisplay, "DLG_FOCUS", False);
1012  aSET_FOCUS = WBGetAtom(pDisplay, "SET_FOCUS");//XInternAtom(pDisplay, "SET_FOCUS", False);
1013 
1014  // additional 'window manager' messages generated internally by the toolkit
1015  aWM_CHAR = WBGetAtom(pDisplay, "WM_CHAR");//XInternAtom(pDisplay, "WM_CHAR", False);
1016  aWM_TIMER = WBGetAtom(pDisplay, "WM_TIMER");//XInternAtom(pDisplay, "WM_TIMER", False);
1017  aWM_POINTER = WBGetAtom(pDisplay, "WM_POINTER");//XInternAtom(pDisplay, "WM_POINTER", False);
1018 
1019  // these next atoms REQUIRE the use of 'XInternAtom' since they have 'global' scope for the entire X11 system
1020 
1021  // window manager messages (see open desktop specification for window managers)
1022  aWM_PROTOCOLS = XInternAtom(pDisplay, "WM_PROTOCOLS", False);
1023  aWM_DELETE_WINDOW = XInternAtom(pDisplay, "WM_DELETE_WINDOW", False);
1024  aWM_TAKE_FOCUS = XInternAtom(pDisplay, "WM_TAKE_FOCUS", False);
1025 
1026  // atoms used for fonts
1027  aAVERAGE_WIDTH = XInternAtom(pDisplay, "AVERAGE_WIDTH", False);
1028 
1029  // atoms for the clipboard
1030  aCLIPBOARD = XInternAtom(pDisplay, "CLIPBOARD", False);
1031 // aPRIMARY = XInternAtom(pDisplay, "PRIMARY", False);
1032 // aSECONDARY = XInternAtom(pDisplay, "SECONDARY", False);
1033  aMANAGER = XInternAtom(pDisplay, "MANAGER", False);
1034  aTARGET = XInternAtom(pDisplay, "TARGET", False);
1035  aINCR = XInternAtom(pDisplay, "INCR", False);
1036 // aWINDOW = XInternAtom(pDisplay, "WINDOW", False);
1037 // aBITMAP = XInternAtom(pDisplay, "BITMAP", False);
1038 // aDRAWABLE = XInternAtom(pDisplay, "DRAWABLE", False);
1039 // aCOLORMAP = XInternAtom(pDisplay, "COLORMAP", False);
1040  aPIXEL = XInternAtom(pDisplay, "PIXEL", False);
1041 // aPIXMAP = XInternAtom(pDisplay, "PIXMAP", False);
1042  aTEXT = XInternAtom(pDisplay, "TEXT", False);
1043 // aSTRING = XInternAtom(pDisplay, "STRING", False);
1044 #ifdef X_HAVE_UTF8_STRING /* this indicates the extension is present */
1045  aUTF8_STRING = XInternAtom(pDisplay, "UTF8_STRING", False);
1046 #endif // X_HAVE_UTF8_STRING
1047  aC_STRING = XInternAtom(pDisplay, "C_STRING", False);
1048  aCOMPOUND_TEXT = XInternAtom(pDisplay, "COMPOUND_TEXT", False);
1049  aTARGETS = XInternAtom(pDisplay, "TARGETS", False);
1050  aMULTIPLE = XInternAtom(pDisplay, "MULTIPLE", False);
1051  aTIMESTAMP = XInternAtom(pDisplay, "TIMESTAMP", False);
1052  aNULL = XInternAtom(pDisplay, "NULL", False);
1053 
1054 
1055  if(aMENU_COMMAND == None ||
1056  aMENU_UI_COMMAND == None ||
1057  aRESIZE_NOTIFY == None ||
1058  aCONTROL_NOTIFY == None ||
1059  aQUERY_CLOSE == None ||
1060  aDESTROY_NOTIFY == None ||
1061  aDLG_FOCUS == None ||
1062  aSET_FOCUS == None ||
1063  aWM_PROTOCOLS == None ||
1064  aWM_DELETE_WINDOW == None ||
1065  aWM_TAKE_FOCUS == None ||
1066  aAVERAGE_WIDTH == None ||
1067  aWM_CHAR == None ||
1068  aWM_TIMER == None ||
1069  aWM_POINTER == None ||
1070  aCLIPBOARD == None ||
1071  aPRIMARY == None ||
1072  aSECONDARY == None ||
1073  aMANAGER == None ||
1074  aTARGET == None ||
1075  aINCR == None ||
1076  aWINDOW == None ||
1077  aBITMAP == None ||
1078  aDRAWABLE == None ||
1079  aCOLORMAP == None ||
1080  aPIXEL == None ||
1081  aPIXMAP == None ||
1082  aTEXT == None ||
1083  aSTRING == None ||
1084 #ifdef X_HAVE_UTF8_STRING /* this indicates the extension is present */
1085  aUTF8_STRING == None || // TODO: should this one be optional?
1086 #endif // X_HAVE_UTF8_STRING
1087  aC_STRING == None ||
1088  aCOMPOUND_TEXT == None ||
1089  aTARGETS == None ||
1090  aMULTIPLE == None ||
1091  aTIMESTAMP == None ||
1092  aNULL == None )
1093  {
1094  return 1;
1095  }
1096 
1097 #if 0
1098  WB_ERROR_PRINT("%s - TEMPORARY - atom list (default)\n"
1099  " pDisplay=%p\n"
1100  " aINCR=%d\n"
1101  " aCLIPBOARD=%d\n"
1102  " aTEXT=%d\n"
1103  " aC_STRING=%d\n"
1104  " aCOMPOUND_TEXT=%d\n"
1105  " aTARGETS=%d\n"
1106  " aMULTIPLE=%d\n"
1107  " aTIMESTAMP=%d\n"
1108  " aPIXEL=%d\n"
1109 #ifdef X_HAVE_UTF8_STRING /* this indicates the extension is present */
1110  " aUTF8_STRING=%d\n"
1111 #endif // X_HAVE_UTF8_STRING
1112  " aNULL=%d\n",__FUNCTION__, pDisplay,
1115 #ifdef X_HAVE_UTF8_STRING /* this indicates the extension is present */
1116  aUTF8_STRING,
1117 #endif // X_HAVE_UTF8_STRING
1118  aNULL);
1119 #endif // 0
1120 
1121  // set an error handler that will continue execution after printing an appropriate message
1122  // in lieu of basically KILLING THE APP after some trivial B.S. "error"
1123 
1124 // BEGIN_XCALL_DEBUG_WRAPPER
1125  XSetErrorHandler(WBXErrorHandler);
1126 // END_XCALL_DEBUG_WRAPPER
1127 
1128  // initialize settings
1129 
1130  if(WBInitClipboardSystem(pDisplay, szDefaultDisplayName)) // initialize clipboard system
1131  {
1132  WB_ERROR_PRINT("%s - unable to initialize clipboard system\n", __FUNCTION__);
1133 
1134  return 2;
1135  }
1136 
1137  CHSettingsRefresh(pDisplay); // must init clipboard system *FIRST*
1138 
1139  return 0; // success
1140 }
1141 
1142 XFontStruct *WBGetDefaultFont(void)
1143 {
1144  extern XFontStruct *pDefaultFont; // default font
1145 
1146  if(!pDefaultFont)
1147  {
1148  WB_ERROR_PRINT("%s - pDefaultFont is NULL\n", __FUNCTION__);
1149  }
1150 
1151  return(pDefaultFont);
1152 }
1153 
1154 XFontSet WBGetDefaultFontSet(Display *pDisplay)
1155 {
1156 extern XFontSet fontsetDefault; // TODO: per-display font sets?
1157 
1158  if(!pDisplay || pDisplay == pDefaultDisplay)
1159  {
1160  if(fontsetDefault == None)
1161  {
1162  WB_ERROR_PRINT("%s - fontsetDefault is None\n", __FUNCTION__);
1163  }
1164 
1165  return fontsetDefault;
1166  }
1167 
1168  WB_ERROR_PRINT("TODO: %s - support use of non-default Display (returning 'None')\n", __FUNCTION__);
1169 
1170  return None; // for now...
1171 }
1172 
1174 {
1175  if(wWBFakeWindow == None)
1176  {
1177  WB_ERROR_PRINT("%s - wWBFakeWindow is 'None'\n", __FUNCTION__);
1178  }
1179 
1180  return wWBFakeWindow;
1181 }
1182 
1183 void __InternalDestroyWindow(Display *pDisp, Window wID, _WINDOW_ENTRY_ *pEntry)
1184 {
1185  Window wIDTemp;
1186  Atom aNCW;
1187  XClientMessageEvent xMsg;
1188  Status st;
1189  int i1;
1190 
1191 
1192 // WB_ERROR_PRINT("TEMPORARY - %s - Destroying window %u (%08xH), %d hash entries\n",
1193 // __FUNCTION__, (unsigned int)wID, (unsigned int)wID, nWBHashEntries);
1194 
1195  // first, I want to destroy any child windows that have window entries, FIRST. Do that now.
1196 
1197  for(i1=0; i1 < WINDOW_ENTRY_ARRAY_SIZE; i1++)
1198  {
1199  if(WB_LIKELY(sWBHashEntries[i1].wID == WINDOW_ENTRY_UNUSED) ||
1200  WB_LIKELY(sWBHashEntries[i1].wID == None))
1201  {
1202  continue; // ignore 'None' and 'UNUSED'
1203  }
1204 
1205  if(sWBHashEntries[i1].wID == wID)
1206  {
1207 // WB_ERROR_PRINT("TEMPORARY - %s - Destroy child windows, found 'me' %u (%08xH)\n",
1208 // __FUNCTION__, (unsigned int)wID, (unsigned int)wID);
1209  continue; // ignore "me"
1210  }
1211 
1212  if(sWBHashEntries[i1].wParent == wID)
1213  {
1214  if(WB_IS_WINDOW_BEING_DESTROYED(sWBHashEntries[i1]))
1215  {
1216 // WB_ERROR_PRINT("TEMPORARY - %s - child window already being destroyed %u (%08xH)\n",
1217 // __FUNCTION__, (unsigned int)sWBHashEntries[i1].wID, (unsigned int)sWBHashEntries[i1].wID);
1218  }
1219  else if(WB_IS_WINDOW_DESTROYED(sWBHashEntries[i1])) // not already destroyed (or being deleted)
1220  {
1221 // WB_ERROR_PRINT("TEMPORARY - %s - child window already destroyed %u (%08xH)\n",
1222 // __FUNCTION__, (unsigned int)sWBHashEntries[i1].wID, (unsigned int)sWBHashEntries[i1].wID);
1223  }
1224  else if(WB_TO_DELETE_WINDOW_ENTRY(sWBHashEntries[i1]))
1225  {
1226 // WB_ERROR_PRINT("TEMPORARY - %s - child window entry to be deleted %u (%08xH)\n",
1227 // __FUNCTION__, (unsigned int)sWBHashEntries[i1].wID, (unsigned int)sWBHashEntries[i1].wID);
1228  }
1229  else
1230  {
1231 // WB_ERROR_PRINT("TEMPORARY - %s - Destroying child window %u (%08xH)\n",
1232 // __FUNCTION__, (unsigned int)sWBHashEntries[i1].wID, (unsigned int)sWBHashEntries[i1].wID);
1233 
1234  __InternalDestroyWindow(pDisp, sWBHashEntries[i1].wID, &(sWBHashEntries[i1]));
1235  }
1236  }
1237  }
1238 
1239 
1240  if(!pEntry || !(pEntry->eWMProtocols & WMPropertiesWMProtocols_DeleteWindow))
1241  {
1242 destroy_without_wm:
1243 
1244  if(pEntry)
1245  {
1246  pEntry->iWindowState = WB_WINDOW_DESTROYED; // so that there is no recursion problem
1247  // this way the window has already been marked 'destroyed' before actually calling
1248  // the function to destroy it. Doing it this way tells everything else down the
1249  // pike that I shouldn't be querying info or trying to destroy it TWICE.
1250  }
1251 
1252 // WB_ERROR_PRINT("TEMPORARY: %s - call to XDestroyWindow for %u (%08xH)\n", __FUNCTION__, (int)wID, (int)wID);
1253 
1255  XDestroyWindow(pDisp, wID); // do this anyway (since it's still mapped and not destroyed)
1257 
1259 // XFlush(pDefaultDisplay);
1260  XSync(pDefaultDisplay, FALSE); // try sync'ing to avoid certain problems
1262 
1263  // TODO: if pEntry is not NULL, do I wait for it to REALLY get destroyed?
1264 
1265  return;
1266  }
1267 
1268  if(!pDisp)
1269  {
1270  pDisp = pDefaultDisplay;
1271  }
1272 
1273  wIDTemp = DefaultRootWindow(pDisp);
1274 
1275  if(wIDTemp == None)
1276  {
1277  WB_ERROR_PRINT("ERROR - %s - default root window = 'None'\n", __FUNCTION__);
1278  goto destroy_without_wm;
1279  }
1280 
1281  aNCW = XInternAtom(pDisp, "_NET_CLOSE_WINDOW", False); /* global scope, must use XInternAtom */
1282 
1283  if(aNCW == None)
1284  {
1285  WB_ERROR_PRINT("ERROR - %s - atom for _NET_CLOSE_WINDOW = 'None'\n", __FUNCTION__);
1286  goto destroy_without_wm;
1287  }
1288 
1289  memset(&xMsg, 0, sizeof(xMsg));
1290 
1291  xMsg.type = ClientMessage;
1292  xMsg.serial = 0;
1293  xMsg.send_event = 1;
1294  xMsg.display = pDisp;
1295  xMsg.window = wID;
1296  xMsg.message_type = aNCW; // TODO: different atom for different display?
1297  xMsg.format = 32;
1298  xMsg.data.l[0] = CurrentTime; //WBGetLastEventTime();
1299  xMsg.data.l[1] = 1; // normal application
1300 
1301  pEntry->iWindowState = WB_WINDOW_DESTROYING; // so that there is no recursion problem
1302 
1304  st = XSendEvent(pDisp, wIDTemp, False, NoEventMask, (XEvent *)&xMsg);
1306 
1308  XSync(pDefaultDisplay, FALSE); // try sync'ing to avoid certain errors
1310 
1311 // WB_ERROR_PRINT("<<TEMPORARY: %s - message sent to %d to close %d (%s)\n", __FUNCTION__, (int)wIDTemp, (int)wID, pEntry->szClassName);
1312 
1313 #if 0 /* this section never works */
1314  // waiting for the entry to be marked "destroyed"
1315 
1316  if(st)
1317  {
1318  WB_UINT64 tmNow = WBGetTimeIndex();
1319 
1320  WB_ERROR_PRINT("INFO: %s - 'DESTROY WINDOW' message sent OK to WM\n", __FUNCTION__);
1321 
1322  st = 0;
1323  while(pEntry && (WBGetTimeIndex() - tmNow) < 50000) // wait up to 0.05 seconds
1324  {
1325  XEvent evt;
1326 
1327 #ifdef HAVE_NANOSLEEP
1328  struct timespec tsp;
1329  tsp.tv_sec = 0;
1330  tsp.tv_nsec = 100000; // wait for .1 msec
1331 
1332  nanosleep(&tsp, NULL);
1333 #else // HAVE_NANOSLEEP
1334 
1335  usleep(100); // wait 0.1 msec
1336 #endif // HAVE_NANOSLEEP
1337 
1338  // look for (and dispatch) destroy notify messages for my window
1339  if(XCheckTypedEvent(pDisp, DestroyNotify, &evt))
1340  {
1341  WBDispatch(&evt); // process destroy notifications
1342  }
1343 
1344  if(WB_IS_WINDOW_DESTROYED(*pEntry))
1345  {
1346  st = 1;
1347 
1348  WB_ERROR_PRINT("INFO: %s - 'DESTROY WINDOW' message acknowledged by WM\n", __FUNCTION__);
1349 
1350  break;
1351  }
1352  }
1353  }
1354 
1355  if(!st)
1356 #endif // 0
1357 
1358  {
1359 // WB_DEBUG_PRINT(DebugLevel_WARN | DebugSubSystem_Window,
1360 // "WARNING - %s - did not destroy window via WM, calling XDestroyWindow for %d (%s)\n",
1361 // __FUNCTION__, (int)wID, pEntry->szClassName);
1362 
1363  pEntry->iWindowState = WB_WINDOW_DESTROYED; // so that there is no recursion problem or attempts to use the window
1364 
1365 // WB_ERROR_PRINT("TEMPORARY: %s - call to XDestroyWindow for %u (%08xH)\n", __FUNCTION__, (int)wID, (int)wID);
1366 
1368  XDestroyWindow(pDisp, wID); // do this anyway (since it's still mapped and not destroyed)
1370 
1372  XSync(pDefaultDisplay, FALSE); // try sync'ing to avoid certain problems
1374  }
1375 }
1376 
1377 void WBExit()
1378 {
1379 int i1;
1380 //Window wIDRoot, wIDTemp;
1381 
1382  bQuitFlag = TRUE; // in case this makes something happen
1383 
1385  "TRACE: WBExit\n");
1386 
1387  if(!pDefaultDisplay)
1388  {
1389  WB_ERROR_PRINT("ERROR: %s - default display contains NULL\n", __FUNCTION__);
1390 
1391  WBExitClipboardSystem(NULL); // just in case
1392  return;
1393  }
1394 
1395  WBExitClipboardSystem(pDefaultDisplay); // do this now
1396 
1397  XFlush(pDefaultDisplay);
1398 
1399  // for each remaining window in my list, destroy it
1400  for(i1=WINDOW_ENTRY_ARRAY_MAX - 1; i1 >= 0; i1--)
1401  {
1402  Display *pDisp = sWBHashEntries[i1].pDisplay;
1403  Window wID = sWBHashEntries[i1].wID;
1404 
1405  if(!wID || wID == WINDOW_ENTRY_UNUSED)
1406  {
1407  continue;
1408  }
1409 
1410  if(!pDisp)
1411  {
1412  pDisp = pDefaultDisplay;
1413  }
1414 
1415  if(WB_IS_WINDOW_MAPPED(sWBHashEntries[i1]))
1416  {
1417  WB_ERROR_PRINT("INFO: %s destroying window %d\n", __FUNCTION__, (int)wID);
1418 
1419  // send a 'destroy' request to the window manager
1420 
1421  __InternalDestroyWindow(pDisp, wID, &(sWBHashEntries[i1]));
1422  }
1423 
1425 
1426  XFlush(pDisp);
1427  }
1428 
1429 // TODO: cache font sets within the font helper, and manage all of them there.
1430 // Font_OnExit(pDefaultDisplay); // call this _BEFORE_ I close the display
1431 
1432  if(fontsetDefault != None)
1433  {
1434  XFreeFontSet(pDefaultDisplay, fontsetDefault);
1435  fontsetDefault = None;
1436  }
1437 
1438  if(pDefaultFont)
1439  {
1440  XFreeFont(pDefaultDisplay, pDefaultFont);
1441  pDefaultFont = NULL;
1442  }
1443 
1444  if(wWBFakeWindow != None) // NOTE: WM doesn't know about this
1445  {
1446  WB_ERROR_PRINT("INFO: %s destroying 'fake' window %d\n", __FUNCTION__, (int)wWBFakeWindow);
1447 
1449  XDestroyWindow(pDefaultDisplay, wWBFakeWindow);
1451  }
1452 
1454  XSync(pDefaultDisplay, FALSE); // try sync'ing first to avoid certain errors
1455  XCloseDisplay(pDefaultDisplay); // display is to be destroyed now
1457 
1458  wWBFakeWindow = None;
1459  pDefaultDisplay = NULL;
1460 
1461  PXM_OnExit(); // pixmap_helper
1462  CHOnExit(); // config_helper
1463  WBPlatformOnExit(); // platform_helper
1464 
1465  if(pOldDisplayIOErrorHandler)
1466  {
1467  XSetIOErrorHandler(pOldDisplayIOErrorHandler);
1468  pOldDisplayIOErrorHandler = NULL;
1469  }
1470 }
1471 
1472 Display *WBThreadInitDisplay(void)
1473 {
1474 Display *pDisplay;
1475 
1476  if(!szDefaultDisplayName[0]) // no display has been opened yet
1477  {
1478  return NULL;
1479  }
1480 
1481  pDisplay = XOpenDisplay(szDefaultDisplayName); // open a copy of the display
1482 
1483 
1484  // TODO: other initialization
1485 
1486 
1487  if(pDisplay)
1488  {
1490 // XFlush(pDisplay);
1491  XSync(pDisplay, FALSE); // sync everything now
1493  }
1494 
1495  return pDisplay;
1496 }
1497 
1498 void WBThreadFreeDisplay(Display *pThreadDisplay)
1499 {
1500  if(pThreadDisplay)
1501  {
1503 // XFlush(pThreadDisplay);
1504  XSync(pThreadDisplay, FALSE); // try sync'ing first to avoid certain errors
1505  XCloseDisplay(pThreadDisplay); // display is to be destroyed now
1507  }
1508 }
1509 
1510 
1511 
1512 // if this function is called first, the arguments will be parsed in order to obtain
1513 // information needed by WBInit and WBInitDisplay. Otherwise, default values are used.
1514 
1515 int WBParseStandardArguments(int *pargc, char ***pargv, char ***penvp)
1516 {
1517 int argc = *pargc;
1518 char **argv = *pargv;
1519 char **envp = *penvp;
1520 char **p1;
1521 char **argvNew = NULL;
1522 int argcNew = 0;
1523 int i1, i2;
1524 int iDebugLevelSeen = 0;
1525 
1526 static const char * const aszCmdLineOptions[]=
1527 {
1528  "debug","subsys","display","minimize","maximize","geometry",
1529  NULL // marks end of list
1530 };
1531 
1532 static const char * const aszDebugSubSys[]=
1533 {
1534  "init","application","window","menu","event",
1535  "dialog","dialogctrl","frame","keyboard",
1536  "mouse","font","settings","clipboard",
1537  NULL
1538 };
1539 
1540 enum
1541 {
1542  option_debug=0,
1543  option_subsys,
1544  option_display,
1545  option_minimize,
1546  option_maximize,
1547  option_geometry
1548 };
1549 
1550  // grab the name of the program and cache it. I'll need the path info.
1551 
1552  strncpy(szAppName, argv[0], sizeof(szAppName));
1553  szAppName[sizeof(szAppName) - 1] = 0; // make sure
1554 
1555  // search for DISPLAY environment variable, pre-assign to szStartupDisplayName
1556  p1 = envp;
1557 
1558  if(p1)
1559  {
1560  while(*p1)
1561  {
1562  if(!strncmp(*p1, "DISPLAY=", 8))
1563  {
1564  strncpy(szStartupDisplayName, *p1 + 8, sizeof(szStartupDisplayName));
1565 
1566  szStartupDisplayName[sizeof(szStartupDisplayName) - 1] = 0;
1567  break; // I am done
1568  }
1569 
1570  p1++;
1571  }
1572  }
1573  else // with no environment pointers, use the 'getenv' C language function
1574  {
1575  char *p1a = getenv("DISPLAY");
1576 
1577  if(p1a)
1578  {
1579  strncpy(szStartupDisplayName, p1a, sizeof(szStartupDisplayName));
1580 
1581  szStartupDisplayName[sizeof(szStartupDisplayName) - 1] = 0;
1582  }
1583  }
1584 
1585  // parse arg list and make copy as needed
1586 
1587  while(argc > 1)
1588  {
1589  int argc0 = argc; // value at start of loop iteration
1590  int iFlag = 0;
1591 
1592  if(argv[1][0] != '-' || !argv[1][1])
1593  {
1594  // end of arguments is a '-' by itself or something not starting with '-'
1595  break;
1596  }
1597 
1598  if(argv[1][1] != '-') // double '-' is what I'm looking for
1599  {
1600  if(!argvNew) // aren't making copies yet
1601  {
1602  argv++; // keep looking but don't touch anything yet
1603  argc--;
1604  continue;
1605  }
1606  }
1607  else
1608  {
1609  // options here start with two '-' and are followed by
1610  // text=value or text [space] value
1611 
1612  for(i1=0; aszCmdLineOptions[i1]; i1++)
1613  {
1614  const char *szOpt = aszCmdLineOptions[i1];
1615  int iLen = strlen(szOpt);
1616  const char *szArg = argv[1] + 2;
1617  const char *szVal = szArg + iLen;
1618 
1619  if(!strncmp(szOpt, szArg, iLen) &&
1620  (*szVal == '=' || !*szVal))
1621  {
1622  if(!*szVal)
1623  {
1624  if(argc > 2)
1625  {
1626  // skip over arg for loop
1627  argv++;
1628  argc--;
1629  szVal = argv[1];
1630  }
1631  }
1632 
1633  switch(i1)
1634  {
1635  case option_debug:
1636  i2 = atoi(szVal);
1637 
1638  if(*szVal < '0' || *szVal > '9' || i2 < 0 || i2 > DebugLevel_Excessive)
1639  {
1640  WBDebugPrint("Invalid debug level %s\n", szVal);
1641 
1642  if(argvNew)
1643  {
1644  WBFree(argvNew);
1645  }
1646 
1647  return -1;
1648  }
1649 
1650  iDebugLevelSeen = 1;
1651  iWBDebugLevel = (iWBDebugLevel & DebugSubSystem_MASK)
1652  | (atoi(szVal) & DebugLevel_MASK);
1653  break;
1654 
1655  case option_subsys:
1656  for(i2=0; aszDebugSubSys[i2]; i2++)
1657  {
1658  if(!strcasecmp(aszDebugSubSys[i2], szVal))
1659  {
1660  iWBDebugLevel |= 1L << (i2 + DebugSubSystem_BITSHIFT);
1661  break;
1662  }
1663  }
1664  if(!aszDebugSubSys[i2])
1665  {
1666  i2 = atoi(szVal);
1667  if(i2 > 0 && (i2 + DebugSubSystem_BITSHIFT) <= 32)
1668  {
1669  iWBDebugLevel |= 1L << (i2 + DebugSubSystem_BITSHIFT - 1);
1670  }
1671  else
1672  {
1673  WBDebugPrint("unrecognized subsystem %s\n", szVal);
1674 
1675  if(argvNew)
1676  {
1677  WBFree(argvNew);
1678  }
1679 
1680  return -1;
1681  }
1682  }
1683 
1684  if(!iDebugLevelSeen)
1685  {
1686  // assume EXCESSIVE debug level unless otherwise specified
1687 
1688  iWBDebugLevel = (iWBDebugLevel & DebugSubSystem_MASK)
1689  | DebugLevel_Excessive;
1690  }
1691 
1692  break;
1693 
1694  case option_display:
1695  strncpy(szStartupDisplayName, szVal, sizeof(szStartupDisplayName));
1696  break;
1697 
1698  case option_minimize:
1699  iStartupMinMax = -1;
1700  break;
1701  case option_maximize:
1702  iStartupMinMax = 1;
1703  break;
1704  case option_geometry:
1705  sscanf(szVal, "%dx%d+%d+%d",
1706  &geomStartup.width, &geomStartup.height, &geomStartup.x, &geomStartup.y);
1707  break;
1708  default:
1709  WBDebugPrint("Unrecognized option: --%s\n", szArg);
1710 
1711  if(argvNew)
1712  {
1713  WBFree(argvNew);
1714  }
1715 
1716  return -1;
1717  }
1718 
1719  iFlag = 1; // skip this entry (I found a match)
1720  break;
1721  }
1722  }
1723  }
1724 
1725  // at this point the argument wasn't recognized and so I must
1726  // copy it into the destination
1727 
1728  if(!argvNew) // need to allocate copy
1729  {
1730  argvNew = WBAlloc(sizeof(*argvNew) * (argc + 2));
1731  if(!argvNew)
1732  {
1733  return -1; // not enough memory
1734  }
1735 
1736  i2 = *pargc; // the original 'argc' value
1737  argcNew = 0;
1738  while(i2 >= argc0) // I want to do this once on the first pass, etc.
1739  {
1740  argvNew[argcNew] = argv[argcNew];
1741  argcNew++;
1742  i2--;
1743  }
1744  }
1745 
1746  if(!iFlag)
1747  {
1748  argvNew[argcNew++] = argv[1];
1749  }
1750 
1751  argv++;
1752  argc--;
1753  }
1754 
1755  if(argvNew)
1756  {
1757  while(argc > 1)
1758  {
1759  argvNew[argcNew++] = argv[1];
1760  argv++;
1761  argc--;
1762  }
1763 
1764  *pargv = argvNew;
1765  *pargc = argcNew;
1766  }
1767 
1768  if((iWBDebugLevel & DebugLevel_MASK) > 0)
1769  {
1770  WBDebugPrint("DebugLevel set to %d\n", (int)(iWBDebugLevel & DebugLevel_MASK));
1771  }
1772 
1773  if((iWBDebugLevel & DebugSubSystem_MASK) != 0)
1774  {
1775  i2 = 0;
1776  for(i1=0; aszDebugSubSys[i1]; i1++)
1777  {
1778  if(iWBDebugLevel & (1L << (i1 + DebugSubSystem_BITSHIFT)))
1779  {
1780  if(!i2)
1781  {
1782  WBDebugPrint("Debug SubSystem: %s", aszDebugSubSys[i1]);
1783  i2 = 1;
1784  }
1785  else
1786  {
1787  WBDebugPrint(", %s", aszDebugSubSys[i1]);
1788  }
1789  }
1790  }
1791 
1792  for(; (i1 + DebugSubSystem_BITSHIFT) < 32; i1++)
1793  {
1794  if(iWBDebugLevel & (1L << (i1 + DebugSubSystem_BITSHIFT)))
1795  {
1796  if(!i2)
1797  {
1798  WBDebugPrint("Debug SubSystem: %d", i1 + 1);
1799  i2 = 1;
1800  }
1801  else
1802  {
1803  WBDebugPrint(", %d", i1 + 1);
1804  }
1805  }
1806  }
1807 
1808  WBDebugPrint("\n");
1809  }
1810 
1811  return 0;
1812 }
1813 
1814 void WBToolkitUsage(void)
1815 {
1816  fputs("X11 WorkBench Toolkit options (these should precede other options)\n"
1817  "--debug n debug level (default 0, errors only)\n"
1818  " 1 = minimal, 7 is maximum debug level\n"
1819  "--subsys xx subsystem to debug (implies --debug 7 if not specified)\n"
1820  " 'xx' is a subsystem name or bit value (see window_helper.h)\n"
1821  " A bit value of 1 is equivalent to the lowest subsystem bit\n"
1822  " NOTE: this option can be specified multiple times\n"
1823  "--display xx X11 display to use (default is DISPLAY env variable)\n"
1824  "--minimize show minimized window on startup\n"
1825  "--maximize show maximized window on startup\n"
1826  "--geometry geom specify window geometry on startup\n"
1827  " geometry spec as per X specification (see X man page)\n"
1828  " typical value might be 800x600+100+50\n"
1829  " (default centers window with reasonable size)\n"
1830  "\n", stderr);
1831 }
1832 
1833 const char *GetStartupDisplayName(void)
1834 {
1835  return szStartupDisplayName;
1836 }
1837 
1838 const char *GetStartupAppName(void)
1839 {
1840  return szAppName;
1841 }
1842 
1844 {
1845  if(pGeom)
1846  {
1847  memcpy(pGeom, &geomStartup, sizeof(WB_GEOM));
1848  }
1849 }
1850 
1852 {
1853  return iStartupMinMax;
1854 }
1855 
1856 void WBInitWindowAttributes(XSetWindowAttributes *pXSWA, unsigned long lBorderPixel,
1857  unsigned long lBackgroundPixel, Colormap clrMap, int iBitGravity)
1858 {
1859  memset(pXSWA, 0, sizeof(*pXSWA));
1860 
1861  pXSWA->border_pixel = lBorderPixel;
1862  pXSWA->background_pixel = lBackgroundPixel;
1863  pXSWA->colormap = clrMap;
1864  pXSWA->bit_gravity = iBitGravity;
1865 }
1866 
1867 void WBInitSizeHints(XSizeHints *pSH, Display *pDisplay, int iMinHeight, int iMinWidth)
1868 {
1869  WB_GEOM geomStartup;
1870 
1871  if(!pDisplay)
1872  {
1873  pDisplay = WBGetDefaultDisplay();
1874  }
1875 
1876 #define WBInitSizeHints__max(X,Y) ((X) > (Y) ? (X) : (Y))
1877 
1878  memset(pSH, 0, sizeof(*pSH));
1879 
1880  pSH->flags = (PPosition | PSize);
1881 
1882  GetStartupGeometry(&geomStartup); // based on user-specified or stored parameters
1883 
1884  // making this SIMPLE AS POSSIBLE, default to 2/3 of the screen (vertically and horizontally) and
1885  // center the window. If the screen is 16x9, reduce the width estimate to 4/3 the height
1886 
1887  if(!geomStartup.width || !geomStartup.height) // width or height zero --> default
1888  {
1889  int iDisplayHeight = DisplayHeight(pDisplay, DefaultScreen(pDisplay));
1890  int iDisplayWidth = DisplayWidth(pDisplay, DefaultScreen(pDisplay));
1891  int iActualDisplayWidth = iDisplayWidth;
1892 
1893  if(iDisplayWidth > iDisplayHeight * 4 / 3) // for 16x9 screens, we want a reasonable estimate
1894  {
1895  iDisplayWidth = iDisplayHeight * 4 / 3;
1896  }
1897 
1898  // base window dimensions on 2/3 of the size of the display or min height/width
1899 
1900  pSH->width = WBInitSizeHints__max(iMinWidth, iDisplayWidth * 2 / 3);
1901  pSH->height = WBInitSizeHints__max(iMinHeight, iDisplayHeight * 2 / 3);
1902 
1903  // vertically and horizontally center the window
1904 
1905  pSH->x = (iActualDisplayWidth - pSH->width) / 2; // use the actual display width this time
1906  pSH->y = (iDisplayHeight - pSH->height) / 2;
1907  }
1908  else
1909  {
1910  // user-specified dimensions take precedence
1911 
1912  pSH->x = geomStartup.x;
1913  pSH->y = geomStartup.y;
1914 
1915  pSH->width = geomStartup.width;
1916  pSH->height = geomStartup.height;
1917  }
1918 
1919 #undef WBInitSizeHints__max
1920 
1921 }
1922 
1923 
1924 
1925 int WBShowModal(Window wID, int bMenuSplashFlag)
1926 {
1927 int iRval = -1;
1928 WB_GEOM geom;
1929 _WINDOW_ENTRY_ *pEntry = WBGetWindowEntry(wID);
1930 
1931 
1932  if(!pEntry)
1933  return -1;
1934 
1935  pEntry->iModalFlag = 1;
1936  pEntry->iModalReturn = -1;
1937 
1939  "Show Modal %d (%08xH) bMenuSplashFlag=%d\n", (int)wID, (int)wID, bMenuSplashFlag);
1940 
1941  // make sure I'm "transient for" the parent window [always] except for menus
1942  if(bMenuSplashFlag <= 0) // not for menus but splash ok
1943  {
1944  Atom a1, a2;
1945 // Window wParent;
1946 
1947  a1 = XInternAtom(pEntry->pDisplay, "_NET_WM_STATE", False); /* global scope, must use XInternAtom */
1948  a2 = XInternAtom(pEntry->pDisplay, "_NET_WM_STATE_MODAL", False); /* global scope, must use XInternAtom */
1949  XChangeProperty(pEntry->pDisplay, wID, a1, XA_ATOM, 32, PropModePrepend, (unsigned char *)&a2, 1);
1950  // note: Keeping existing properties, and adding my own
1951 
1952 // // setting 'WM_TRANSIENT_FOR' allows me to use the parent window while the dialog is open - I do NOT want that!
1953 // a1 = XInternAtom(pEntry->pDisplay, "WM_TRANSIENT_FOR", False); /* global scope, must use XInternAtom */
1954 // wParent = WBGetParentWindow(wID); NOTE this is not the way to do it, fix elsewhere eh?
1955 // XChangeProperty(pEntry->pDisplay, wID, a1, XA_WINDOW, 32, PropModeReplace, (unsigned char *)&wParent, 1);
1956  }
1957 
1958  XMapRaised(pEntry->pDisplay, wID);
1959 
1960  if(!bMenuSplashFlag) // dialog boxes ONLY
1961  {
1962  WBSetInputFocus(wID); // NOTE: menu popups _MUST_ be declared with 'focus bit' 0, and handled differently
1963  }
1964 
1965  if(bMenuSplashFlag > 0)
1966  {
1967 // WB_ERROR_PRINT("TEMPORARY: %s - grabbing mouse and keyboard\n", __FUNCTION__);
1968 
1969  // ----------------------------
1970  // mouse and keyboard grab time
1971  // ----------------------------
1972 
1973  WBGetWindowGeom2(wID, &geom); // geometry relative to root window
1974 
1976  XGrabPointer(pEntry->pDisplay,wID, 1,
1977  ButtonPressMask | ButtonReleaseMask | ButtonMotionMask | PointerMotionMask
1978  | EnterWindowMask | LeaveWindowMask,
1979  GrabModeAsync, // pointer mode
1980  GrabModeAsync, // keyboard mode
1981  None, None, CurrentTime);
1982  XGrabKeyboard(pEntry->pDisplay, wID, 1,
1983  GrabModeAsync, // pointer mode
1984  GrabModeAsync, // keyboard mode
1985  CurrentTime);
1987  }
1988 // else
1989 // {
1990 // WB_ERROR_PRINT("TEMPORARY: showing modal for NON-menu\n");
1991 // }
1992 
1993 
1994 // WB_ERROR_PRINT("TEMPORARY: %s - begin modal event processing\n", __FUNCTION__);
1995 
1996  while(!bQuitFlag)
1997  {
1998  XEvent event;
1999 
2000  // do a mini event loop but only for this window. The event handler should
2001  // be smart enough to handle a condition where I attempt to change focus
2002 
2003  // for future implementation - see XmTrackingEvent for hints on how to implement this properly
2004 
2005  // TODO: modal filtering for window and children of this window
2006 
2007  pEntry = WBGetWindowEntry(wID); // re-assign 'pEntry'
2008 
2009  if(!pEntry)
2010  {
2011  break;
2012  }
2013 
2014  if(!WBIsValid(pEntry->pDisplay, wID) ||
2015  !pEntry->iModalFlag)
2016  {
2017  break;
2018  }
2019 
2020  if(!__InternalCheckGetEvent(pEntry->pDisplay, &event, wID))
2021  {
2022 #ifdef HAVE_NANOSLEEP
2023  struct timespec tsp;
2024  tsp.tv_sec = 0;
2025  tsp.tv_nsec = 100000; // wait for .1 msec
2026 
2027  nanosleep(&tsp, NULL);
2028 #else // HAVE_NANOSLEEP
2029 
2030  usleep(100); // 100 microsecs - a POSIX alternative to 'nanosleep'
2031 
2032 #endif // HAVE_NANOSLEEP
2033  continue;
2034  }
2035 
2036  // check for application events - these will continue to happen
2037  // even during a modal loop.
2038 
2039  if(event.xany.window == None)
2040  {
2041  // TODO: if messages aren't consistent with 'xany', filter them here.
2042 
2043  WBAppDispatch(&event);
2044  continue;
2045  }
2046 
2047 //#ifndef NO_DEBUG
2048 // WBDebugDumpEvent(&event); // TEMPORARY
2049 //#endif // NO_DEBUG
2050 
2051  // For menus, check for mousie events outside of my window. If this happens
2052  // I want to cancel the modal loop and re-play the event.
2053 
2054  if(bMenuSplashFlag > 0) // menus only
2055  {
2056  if((event.type == ButtonPress || event.type == ButtonRelease) &&
2057  !WBPointInGeom(event.xbutton.x_root, event.xbutton.y_root, geom))
2058  {
2060  "WBShowModal - (menu) mouse button press outside of grab window\n");
2061 
2063  XUngrabPointer(pEntry->pDisplay, CurrentTime);
2064  XUngrabKeyboard(pEntry->pDisplay, CurrentTime);
2065  XAllowEvents(pEntry->pDisplay, ReplayPointer, CurrentTime/*event.xbutton.time*/); // re-play the mousie event
2066 
2067  XPutBackEvent(pEntry->pDisplay, &event); // TODO: correct window ID?
2069 
2070  WBDestroyWindow(wID);
2071  return -1; // "canceled"
2072  }
2073  }
2074  else if(bMenuSplashFlag < 0) // splash screens
2075  {
2076  }
2077  else if(event.xany.window != wID && event.type == FocusIn) // dialog boxes getting focus
2078  {
2079  // in this case it is EXTREMELY important to determine if the main
2080  // window is getting an 'enter focus' notification, since I will need
2081  // to re-assign focus to 'wID' ASAP. Examples are when the user clicks
2082  // on the main window while a dialog box is visible, or if the user attempts
2083  // to change focus to the main window via alt+tab or clicking on an icon in
2084  // the task bar. Therefore, if 'event.xany.window' isn't a child of 'wID'
2085  // I shall call 'SetFocus' on 'wID' and eat the message.
2086  // For this to work, top level windows must allow 'EnterNotify' messages.
2087 
2088  if(!WBIsChildWindow(wID, event.xany.window))
2089  {
2090  Display *pDisplay = pEntry->pDisplay ? pEntry->pDisplay : pDefaultDisplay;
2091 
2092 // fprintf(stderr, "** TEMPORARY: window %08xH 'FocusIn' in modal loop for %08xH\n",
2093 // (unsigned int)event.xany.window, (unsigned int)wID);
2094 
2095  WBPostDelayedSetFocusAppEvent(pDisplay, wID, event.xany.window, 100); // 100 msec delay
2096  // activate, set focus, raise, and map 'wID' (with 'event.xany.window' possibly having HAD focus)
2097  // and the 100 msec delay is to ensure that NO race conditions happen with the window manager
2098 
2099  continue; // old message eaten
2100  }
2101  }
2102 
2103 
2104  // see if this event is intended for the modal window or one of its children
2105  // and if so, I want to dispatch it
2106 
2107  if(event.xany.window == wID ||
2108  event.type == Expose || // always handle expose events
2109  event.type == GraphicsExpose ||
2110  event.type == NoExpose ||
2111  event.type == DestroyNotify || // always allow destroy notifications
2112  event.type == SelectionRequest ||
2113  event.type == SelectionClear ||
2114  event.type == SelectionNotify ||
2115  (bMenuSplashFlag > 0 && // check for ANY KB+mouse events when NOT a splash window
2116  (event.type == KeyPress || event.type == KeyRelease ||
2117  event.type == ButtonPress || event.type == ButtonRelease ||
2118  event.type == MotionNotify || event.type == EnterNotify ||
2119  event.type == LeaveNotify)) ||
2120  (bMenuSplashFlag >= 0 && WBIsChildWindow(wID, event.xany.window)) || // check for 'is a child of modal' when NOT a splash window
2121  WB_UNLIKELY(event.type == ClientMessage &&
2122  (event.xclient.message_type == aWM_PROTOCOLS || // all of these get through
2123  event.xclient.message_type == aWM_TIMER))) // all timers get through
2124  {
2125  if(event.xany.window != wID && bMenuSplashFlag > 0 && // menu only because I'm capturing keyboard and mouse
2126  (event.type == KeyPress || event.type == KeyRelease || event.type == MotionNotify))
2127  {
2128  if(event.type == MotionNotify)
2129  {
2130  int iX, iY;
2131  // mouse coordinates have been adjusted for the window for which
2132  // it was intended. these must be 'altered'
2133  WBXlatCoordPoint(event.xmotion.window, event.xmotion.x, event.xmotion.y,
2134  wID, &iX, &iY);
2135  event.xmotion.x = iX;
2136  event.xmotion.y = iY;
2137  event.xmotion.window = wID; // assign THIS window's ID for such messages
2138  }
2139  else
2140  {
2141 // Window wParent = WBGetParentWindow(wID);
2142  if(!WBIsChildWindow(wID, event.xany.window))
2143  {
2144  event.xany.window = wID; // assign THIS window's ID for such messages
2145  }
2146  }
2147  }
2148 
2149 #ifndef NO_DEBUG
2150  if(event.xany.window != wID &&
2151  WB_LIKELY(event.type != ClientMessage ||
2152  (event.xclient.message_type != aWM_TIMER && event.xclient.message_type != aWM_PROTOCOLS)))
2153 
2154  {
2156  "%s - event %s for window %d (%08xH) in modal loop\n",
2157  __FUNCTION__,
2158  WBEventName(event.type),
2159  (int)event.xany.window, (int)event.xany.window);
2160  }
2161 
2162  if(event.type == KeyPress || event.type == KeyRelease)
2163  {
2165  "%s - TEMPORARY - processing key press/release within modal loop: %d (%08xH), %d (%08xH)\n",
2166  __FUNCTION__, (int)event.xany.window, (int)event.xany.window, (int)wID, (int)wID);
2167  }
2168 
2169 #endif // NO_DEBUG
2170 
2171  WBWindowDispatch(event.xany.window, &event);
2172  }
2173 #ifndef NO_DEBUG
2174  else
2175  {
2176  // say something for debug purposes about messages that weren't processed
2177 
2178 // WB_DEBUG_PRINT(DebugLevel_ERROR,
2179 // "** TEMPORARY: IGNORING %s in modal loop: %d (%08xH), %d (%08xH)\n",
2180 // __internal_event_type_string(event.type),
2181 // (int)event.xany.window, (int)event.xany.window, (int)wID, (int)wID);
2182 
2183  if(event.type == KeyPress || event.type == KeyRelease)
2184  {
2186  "%s - key press/release in modal loop: %d (%08xH), %d (%08xH)\n",
2187  __FUNCTION__, (int)event.xany.window, (int)event.xany.window, (int)wID, (int)wID);
2188  }
2189  }
2190 #endif // NO_DEBUG
2191 
2192  // other stuff gets ignored
2193 
2194  }
2195 
2196  if(!pEntry)
2197  {
2198  return -1;
2199  }
2200 
2201  if(bMenuSplashFlag > 0) // menus only
2202  {
2204  XUngrabPointer(pEntry->pDisplay, CurrentTime);
2205  XUngrabKeyboard(pEntry->pDisplay, CurrentTime);
2207  }
2208 
2209  iRval = pEntry->iModalReturn;
2210 
2212  "Exit from modal loop, window %d (%08xH) - returning %d (%08xH)\n",
2213  (int)wID, (int)wID, iRval, iRval);
2214 
2215  // destroy the window now that I'm out of the loop
2216 
2217 // WB_ERROR_PRINT("** TEMPORARY - %s - destroying window %d\n", __FUNCTION__,(int)wID);
2218 
2219  WBDestroyWindow(wID);
2220 
2221  return iRval;
2222 }
2223 
2224 void WBEndModal(Window wID, int iRval)
2225 {
2226  _WINDOW_ENTRY_ *pEntry = WBGetWindowEntry(wID);
2227 
2228  if(!pEntry)
2229  return;
2230 
2231  pEntry->iModalFlag = 0;
2232  pEntry->iModalReturn = iRval;
2233 
2235  "WBEndModal - Window %d (%08xH), returning %d (%08xH)\n",
2236  (int)wID, (int)wID, iRval, iRval);
2237 }
2238 
2239 void WBSetInputFocus(Window wID) // set input focus to specific window (revert window is previous focus window)
2240 {
2241  _WINDOW_ENTRY_ *pEntry = WBGetWindowEntry(wID);
2242  Window wIDCurrent;
2243  int iRevert;
2244  Display *pDisp = pEntry ? pEntry->pDisplay : pDefaultDisplay;
2245 
2247  XGetInputFocus(pDisp, &wIDCurrent, &iRevert);
2249 
2250  if(wID != wIDCurrent) // don't have focus
2251  {
2252  _WINDOW_ENTRY_ *pPrev = WBGetWindowEntry(wIDCurrent);
2253  if(pPrev)
2254  {
2255  if(WB_CHECK_SET_FOCUS_ON_MAP(*pPrev))
2256  {
2258  "%s:%d - %d (%08xH) disabling focus change on map\n",
2259  __FUNCTION__, __LINE__, (int)wIDCurrent, (int)wIDCurrent);
2260 
2261  pPrev->iWindowState = WB_WINDOW_UNMAPPED; // remove 'set focus' state
2262  }
2263  }
2264 
2265  // see if expose event has been done on this yet...
2266  if(WB_LIKELY(pEntry) && //WB_UNLIKELY(!pEntry->width && !pEntry->height && !pEntry->border))
2267  WB_UNLIKELY(WB_IS_WINDOW_UNMAPPED(*pEntry)))
2268  {
2270  "%s:%d - %d (%08xH) not yet visible, enabling focus change on map\n",
2271  __FUNCTION__, __LINE__, (int)wID, (int)wID);
2272 
2273  pEntry->iWindowState = WB_WINDOW_SET_FOCUS_ON_MAP;
2274  }
2275  else
2276  {
2278  "%s:%d - %d (%08xH) calling XSetInputFocus\n",
2279  __FUNCTION__, __LINE__, (int)wID, (int)wID);
2280 
2281 // WB_ERROR_PRINT("TEMPORARY - %s calling XSetInputFocus on wID=%u (%08xH)\n", __FUNCTION__, (int)wID, (uint32_t)wID);
2282 
2284  XSetInputFocus(pDisp, wID, RevertToParent, CurrentTime);
2286  }
2287  }
2288 }
2289 
2290 // construction and destruction
2291 
2292 void WBDestroyWindow(Window wID)
2293 {
2294  _WINDOW_ENTRY_ *pEntry = WBGetWindowEntry(wID);
2295  Display *pDisplay = pEntry ? pEntry->pDisplay : pDefaultDisplay;
2296  XEvent event;
2297 
2298  if(pEntry && WB_IS_WINDOW_DESTROYED(*pEntry)) // recursive or extra call
2299  {
2300  WB_WARN_PRINT("%s - Recursive call, Window %d (%08xH)\n",
2301  __FUNCTION__, (int)wID, (int)wID);
2302 
2303  __WBDelWindowPaintEvents(pDisplay, wID); // make sure expose events go away
2304 
2305  return;
2306  }
2307 
2308  if(pEntry && WB_IS_WINDOW_BEING_DESTROYED(*pEntry)) // recursive call in need of XDestroyWindow
2309  {
2310  pEntry->iWindowState = WB_WINDOW_DESTROYED; // initially, do this (in case of extra recursion)
2311 
2312  WB_ERROR_PRINT("INFO: %s recursively destroying window %d\n", __FUNCTION__, (int)wID);
2313 
2315  XDestroyWindow(pDisplay, wID); // handler should clean up the rest
2316  // NOTE: this _CAN_ recurse in some cases
2317 // XFlush(pDisplay);
2318  XSync(pDisplay, False); // wait for server to actually do it - specific to debug output, really
2320 
2321  __WBDelWindowPaintEvents(pDisplay, wID); // make sure expose events go away
2322 
2323  return;
2324  }
2325 
2327  "%s - Window %d (%08xH) class %s\n",
2328  __FUNCTION__, (int)wID, (int)wID, pEntry ? (pEntry->szClassName ? pEntry->szClassName : "NULL") : "*Unk*");
2329 
2330  // get all events for this window and remove them
2331 
2333  XSync(pDisplay, FALSE); // force a sync first
2335 
2336  while(XCheckWindowEvent(pDisplay, wID, EVENT_ALL_MASK, &event))
2337  {
2338 // usleep(1000); // force sleep during loop?
2339  }
2340 
2341  if(!pEntry)
2342  {
2343  WB_WARN_PRINT("WARNING: WBDestroyWindow for wID not in list - window already destroyed?\n");
2344 
2345  XSync(pDisplay, 0);
2346 
2347  __WBDelWindowEvents(pDefaultDisplay, wID);
2348 
2349  WB_ERROR_PRINT("INFO: %s destroying UN-REGISTERED window %d\n", __FUNCTION__, (int)wID);
2350 
2351 // BEGIN_XCALL_DEBUG_WRAPPER
2352 // XDestroyWindow(pDefaultDisplay, wID);
2353 // END_XCALL_DEBUG_WRAPPER
2354  __InternalDestroyWindow(pDisplay, wID, NULL);
2355  }
2356  else
2357  {
2358  // TODO: should I send a notification first?
2359 
2361  "WBDestroyWindow - wID %d (%08xH)\n",
2362  (int)wID, (int)wID);
2363 
2364  // I'll want to process some additional events at this point, JUST for the
2365  // window and its children
2366 
2368  XSync(pDisplay, 0);
2370 
2371 // NOTE: I used to dispatch remaining events, but this might dispatch EXPOSE and I don't want that
2372 // while(XCheckWindowEvent(pDisplay, wID, EVENT_ALL_MASK, &event))
2373 // {
2374 // WBWindowDispatch(wID, &event);
2375 // }
2376 
2378  "WBDestroyWindow - wID %d (%08xH), display flushed\n",
2379  (int)wID, (int)wID);
2380 
2381  if(WB_IS_WINDOW_MAPPED(*pEntry))
2382  {
2384  "WBDestroyWindow - wID %d (%08xH), calling XDestroyWindow\n",
2385  (int)wID, (int)wID);
2386 
2387  __InternalDestroyWindow(pDisplay, wID, pEntry); // may mark it 'DESTROYING' so recursion won't happen (see above)
2388 
2389 // if(!WB_IS_WINDOW_DESTROYED(*pEntry))
2390 // {
2391 // pEntry->iWindowState = WB_WINDOW_DESTROYED; // initially, do this (in case of recursion)
2392 //
2393 // WB_ERROR_PRINT("** WARNING: %s destroying window %d (marked it 'destroyed') "/*X errors ignored*/"\n", __FUNCTION__, (int)wID);
2394 //
2396 // BEGIN_XCALL_DEBUG_WRAPPER
2397 // XDestroyWindow(pDisplay, wID); // handler should clean up the rest
2398 // // NOTE: this _CAN_ recurse in some cases
2400 // XSync(pDisplay, False);
2401 // END_XCALL_DEBUG_WRAPPER
2403 // }
2404  }
2405  else if(!WB_IS_WINDOW_BEING_DESTROYED(*pEntry) && !WB_IS_WINDOW_DESTROYED(*pEntry))
2406  {
2408  "WBDestroyWindow - wID %d (%08xH), destroying 'unmapped' window\n",
2409  (int)wID, (int)wID);
2410 
2411  __InternalDestroyWindow(pDisplay, wID, pEntry); // may mark it 'DESTROYING' so recursion won't happen (see above)
2412  }
2413  // NOTE: the above 'if' may preclude this next one from EVAR happening
2414  else if(!WB_TO_DELETE_WINDOW_ENTRY(*pEntry)) // has not called 'WBUnregisterWindowCallback'
2415  {
2417  "WBDestroyWindow - wID %d (%08xH), Marking 'unmapped' window 'destroyed'\n",
2418  (int)wID, (int)wID);
2419 
2420  pEntry->iWindowState = WB_WINDOW_DESTROYED; // initially, do this
2421  }
2422 
2423  __WBDelWindowEvents(pDisplay, wID);
2424 
2425  if(!WB_TO_DELETE_WINDOW_ENTRY(*pEntry)) // in case 'Destroy' notification didn't make this happen
2426  {
2428  "WARNING - %s - last ditch attempt, unregistering window callback for window %d\n", __FUNCTION__, (int)wID);
2430  }
2431  }
2432 }
2433 
2434 
2435 // application callback registration
2436 
2438 {
2439  pAppEventCallback = pCallback;
2440 }
2441 
2443 {
2444  pAppEventCallback = NULL;
2445 }
2446 
2447 
2448 /**********************************************************************/
2449 /* */
2450 /* window entry array processing */
2451 /* */
2452 /**********************************************************************/
2453 
2454 
2455 static __inline__ void InternalRestoreWindowDefaults(int iIndex)
2456 {
2457  sWBHashEntries[iIndex].szClassName = NULL; // no class name, initially
2458  sWBHashEntries[iIndex].wParent = 0; // mark it as "unassigned"
2459  sWBHashEntries[iIndex].idCursor = WB_DEFAULT_CURSOR;
2460  sWBHashEntries[iIndex].idDefaultCursor = WB_DEFAULT_CURSOR;
2461  sWBHashEntries[iIndex].iWaitCursorCount = 0;
2462  sWBHashEntries[iIndex].curRecent = None;
2463  bzero(&(sWBHashEntries[iIndex].geomAbsolute), sizeof(sWBHashEntries[iIndex].geomAbsolute));
2464  sWBHashEntries[iIndex].rgnClip = 0;
2465  sWBHashEntries[iIndex].rgnPaint = 0;
2466 
2467  sWBHashEntries[iIndex].iModalFlag = 0;
2468  sWBHashEntries[iIndex].iModalReturn = -1;
2469 
2470  sWBHashEntries[iIndex].width = sWBHashEntries[iIndex].height
2471  = sWBHashEntries[iIndex].border = 0; // make sure these are zero as well (indicates 'not yet visible')
2472 
2473 
2474  // this is primarily for when I unregister the callback (I must also unregister the menu)
2475  sWBHashEntries[iIndex].wIDMenu = 0;
2476  sWBHashEntries[iIndex].pMenuCallback = 0;
2477  sWBHashEntries[iIndex].iWindowState = WB_WINDOW_UNMAPPED;
2478 
2479  sWBHashEntries[iIndex].eWindowType = WMPropertiesWindowType_Normal; // TODO: notify window manager?
2480  sWBHashEntries[iIndex].eWMProtocols = WMPropertiesWMProtocols_None; // TODO: notify window manager?
2481 
2482  // zero out the window data (TODO: zero out everything?)
2483  bzero(sWBHashEntries[iIndex].aWindowData, sizeof(sWBHashEntries[iIndex].aWindowData));
2484 }
2485 
2486 //static /*__inline*/ WBWindow WBWindowFromWindow(Window wID)
2487 //{
2488 // return (WBWindow)((unsigned long long)wID | WBWINDOW_FLAGS_NORMAL);
2489 //}
2490 
2491 //static /*__inline*/ Window WBWindowToWindow(WBWindow wID)
2492 //{
2493 // return (Window)(wID & WBWINDOW_MASK);
2494 //}
2495 
2496 
2497 // WBGetWindowEntry - the debug version indicates what the caller's line number and function are
2498 
2499 #ifdef NO_DEBUG
2500 static /*__inline__*/ _WINDOW_ENTRY_ *WBGetWindowEntry(Window wID)
2501 #else
2502 #define WBGetWindowEntry(X) Debug_WBGetWindowEntry(X, __FUNCTION__, __LINE__)
2503 static /*__inline__*/ _WINDOW_ENTRY_ *Debug_WBGetWindowEntry(Window wID, const char *szFunction, int nLine)
2504 #endif // NO_DEBUG
2505 {
2506  int i1, iStart;
2507 
2508  if(!nWBHashEntries)
2509  {
2510 #ifndef NO_DEBUG
2512  "%s:%d - Window hash table empty, %d (%08xH) not found\n",
2513  szFunction, nLine, (int)wID, (int)wID);
2514 #endif // NO_DEBUG
2515  return(NULL);
2516  }
2517 
2518  i1 = iStart = WINDOW_ENTRY_HASH(wID);
2519 
2520  while(sWBHashEntries[i1].wID) // this must be assigned to zero for this to work properly
2521  {
2522  if(sWBHashEntries[i1].wID == wID)
2523  {
2524  gettimeofday(&(sWBHashEntries[i1].tvLastActivity), NULL);
2525  return(sWBHashEntries + i1);
2526  }
2527 
2528  i1++;
2529  i1 &= WINDOW_ENTRY_ARRAY_MAX;
2530 
2531  if(i1 == iStart)
2532  break;
2533  }
2534 
2535 #ifndef NO_DEBUG
2537  "%s:%d - Window ID %d (%08xH) not found\n",
2538  szFunction, nLine, (int)wID, (int)wID);
2539 #endif // NO_DEBUG
2540 
2541  return(NULL);
2542 }
2543 
2544 // window callback registration - this also adds a window entry if one does
2545 // not already exist for the specified window ID. This function assumes
2546 // that the window's display is the default display.
2547 
2548 static _WINDOW_ENTRY_ *__AddOrLocateEntry(Window wID)
2549 {
2550 register int i1, i2, iStart;
2551 _WINDOW_ENTRY_ *pRval = NULL;
2552 
2553 
2554  i2 = -1; // 'unused' entry marker
2555  i1 = WINDOW_ENTRY_HASH(wID);
2556 
2557  if(!nWBHashEntries)
2558  {
2560  "__AddOrLocateEntry - zero out window hash/array since it's empty\n");
2561  bzero(sWBHashEntries, sizeof(sWBHashEntries));
2562  }
2563  else
2564  {
2565  // keep track of the one I start with as 'iStart'
2566 
2567  iStart = i1;
2568 
2569  while(sWBHashEntries[i1].wID) // the array must be pre-assigned to zero for this to work properly
2570  {
2571  if(sWBHashEntries[i1].wID == wID)
2572  {
2573  pRval = sWBHashEntries + i1;
2574  break;
2575  }
2576 
2577  if(sWBHashEntries[i1].wID == WINDOW_ENTRY_UNUSED)
2578  {
2579  i2 = i1; // keep track of the 1st unused entry in the chain
2580  }
2581 
2582  i1++;
2583  i1 &= WINDOW_ENTRY_ARRAY_MAX;
2584 
2585  if(i1 == iStart)
2586  {
2587  i1 = -1; // error flag (hash table full)
2588  break;
2589  }
2590  }
2591  }
2592 
2593  // if pRval is NULL, I need to add the entry
2594  // in that case, either i2 or i1 points to it.
2595  // if both i1 and i2 are negative, it's an error
2596  // The number of active entries is limited to 3/4
2597  // of the hash table size for speed and efficiency.
2598 
2599  if(!pRval)
2600  {
2601  if(i1 < 0 && i2 < 0)
2602  return NULL;
2603 
2604  // --------------------------
2605  // HASH TABLE SIZE LIMITATION
2606  // --------------------------
2607 
2608  // if 4 times the # of entries is larger than 3 times the # of hash entries,
2609  // then the hash is 3/4 full and grossly inefficient. This is my limit.
2610 
2611  if((nWBHashEntries * 4) > (3 * sizeof(sWBHashEntries)/sizeof(sWBHashEntries[0]))) // an efficient algorithm
2612  return NULL; // enforce hash table twice maximum
2613 
2614  nWBHashEntries++; // keep track
2615 
2616  if(i2 >= 0)
2617  i1 = i2;
2618 
2619  pRval = sWBHashEntries + i1;
2620  pRval->wID = wID; // mark it "mine"
2621  pRval->pDisplay = pDefaultDisplay; // for now - TODO get the real display
2622 
2623  InternalRestoreWindowDefaults(i1);
2624 
2625  WBRestoreDefaultCursor(wID); // assign the default cursor for a new entry
2626  }
2627 
2628  if(pRval)
2629  {
2630  gettimeofday(&(pRval->tvLastActivity), NULL);
2631  }
2632 
2633  return pRval;
2634 }
2635 
2636 static void __WindowEntryRestoreDefaultResources(int iIndex)
2637 {
2638  Display *pDisp = pDefaultDisplay;
2639 
2640  if(sWBHashEntries[iIndex].pDisplay)
2641  {
2642  pDisp = sWBHashEntries[iIndex].pDisplay;
2643  }
2644 
2646  if(sWBHashEntries[iIndex].fontSet != None && sWBHashEntries[iIndex].fontSet != fontsetDefault) // must delete it
2647  {
2648  XFreeFontSet(pDisp, sWBHashEntries[iIndex].fontSet);
2649  sWBHashEntries[iIndex].fontSet = None;
2650  }
2651  if(sWBHashEntries[iIndex].pFontStruct && sWBHashEntries[iIndex].pFontStruct != pDefaultFont) // must delete it
2652  {
2653  XFreeFont(pDisp, sWBHashEntries[iIndex].pFontStruct);
2654  sWBHashEntries[iIndex].pFontStruct = NULL;
2655  }
2656  if(sWBHashEntries[iIndex].pxIcon)
2657  {
2658  XFreePixmap(pDisp, sWBHashEntries[iIndex].pxIcon);
2659  sWBHashEntries[iIndex].pxIcon = 0;
2660  }
2661  if(sWBHashEntries[iIndex].pxMask)
2662  {
2663  XFreePixmap(pDisp, sWBHashEntries[iIndex].pxMask);
2664  sWBHashEntries[iIndex].pxMask = 0;
2665  }
2666  if(sWBHashEntries[iIndex].pWMHints)
2667  {
2668  XFree(sWBHashEntries[iIndex].pWMHints);
2669  sWBHashEntries[iIndex].pWMHints = NULL;
2670  }
2671  if(sWBHashEntries[iIndex].curRecent != None)
2672  {
2673  XFreeCursor(pDisp, sWBHashEntries[iIndex].curRecent);
2674  sWBHashEntries[iIndex].curRecent = None;
2675  }
2676  if(sWBHashEntries[iIndex].rgnClip != 0)
2677  {
2678  XDestroyRegion(sWBHashEntries[iIndex].rgnClip);
2679  sWBHashEntries[iIndex].rgnClip = 0;
2680  }
2681  if(sWBHashEntries[iIndex].rgnPaint != 0)
2682  {
2683  WB_WARN_PRINT("WARNING: paint region non-zero in __WindowEntryRestoreDefaultResources\n");
2684  XDestroyRegion(sWBHashEntries[iIndex].rgnPaint);
2685  sWBHashEntries[iIndex].rgnPaint = 0;
2686  }
2688 
2689 }
2690 
2691 static void __WindowEntryDestructor(int iIndex)
2692 {
2693 int iPrev, iNext;
2694 
2695  __WindowEntryRestoreDefaultResources(iIndex); // make sure no resources are allocated
2696 
2697  sWBHashEntries[iIndex].pCallback = 0;
2698  sWBHashEntries[iIndex].pDisplay = NULL; // must happen AFTER restoring default resources
2699 
2700  // restore these and additional default values
2701  InternalRestoreWindowDefaults(iIndex);
2702 
2704  // _ _ _ _____ _ _ ____ _ //
2705  // | | | | __ _ ___| |__ |_ _|_ _| |__ | | ___ / ___| | ___ __ _ _ __ _ _ _ __ //
2706  // | |_| |/ _` / __| '_ \ | |/ _` | '_ \| |/ _ \ | | | |/ _ \/ _` | '_ \| | | | '_ \ //
2707  // | _ | (_| \__ \ | | | | | (_| | |_) | | __/ | |___| | __/ (_| | | | | |_| | |_) | //
2708  // |_| |_|\__,_|___/_| |_| |_|\__,_|_.__/|_|\___| \____|_|\___|\__,_|_| |_|\__,_| .__/ //
2709  // |_| //
2711 
2712  // NOW, I have to 'zero out' or mark the entry 'unused'. To do this I
2713  // have to define what an unused window ID is ('WINDOW_ENTRY_UNUSED').
2714  // (this MUST happen or the hash table won't work)
2715  // there are a couple of cases where deleting an entry might break a 'chain'
2716  // As a result I need to ensure that I make use of 'unused' markers and only
2717  // remove them when the final entry in the chain is followed by a 'NULL'.
2718  //
2719  // Preceding entries that aren't 'NULL' indicate I am part of a 'chain' and
2720  // so I can't mark it 'NULL' if this entry isn't the last entry in the 'chain'
2721  // or the 'chain' breaks. Therefore it will be marked 'unused'.
2722 
2723  iPrev = (iIndex - 1) & WINDOW_ENTRY_ARRAY_MAX;
2724  iNext = (iIndex + 1) & WINDOW_ENTRY_ARRAY_MAX;
2725 
2726  if(!sWBHashEntries[iNext].wID) // next isn't NULL so I'm the end of the chain
2727  {
2728  sWBHashEntries[iIndex].wID = 0; // it's safe to do this now
2729 
2730  for(iIndex=iPrev; iIndex != iNext && sWBHashEntries[iIndex].wID == WINDOW_ENTRY_UNUSED;
2731  iIndex = (iIndex - 1) & WINDOW_ENTRY_ARRAY_MAX) // roundie roundie
2732  {
2733  sWBHashEntries[iIndex].wID = 0; // zero out back up the chain until I hit something that's not 'unused'
2734  }
2735  }
2736  else
2737  {
2738  if(!sWBHashEntries[iPrev].wID) // previous is NULL
2739  {
2740  // I'm not a chain. I can mark this zero
2741  sWBHashEntries[iIndex].wID = 0; // just zero it out (the normal case)
2742  }
2743  else
2744  {
2745  sWBHashEntries[iIndex].wID = WINDOW_ENTRY_UNUSED; // mark this as "unused" but not the end of a chain
2746  }
2747  }
2748 
2749  nWBHashEntries--;
2750 }
2751 
2752 static void __PeriodicWindowEntryCleanup(void)
2753 {
2754 int i1;
2755 struct timeval tvNow;
2756 static struct timeval tvLastTime = {0,0};
2757 
2758  gettimeofday(&tvNow, NULL);
2759 
2760  if(tvNow.tv_sec == tvLastTime.tv_sec)
2761  {
2762  return; // so I don't do this too often - only once per second
2763  }
2764 
2765  tvLastTime.tv_sec = tvNow.tv_sec;
2766 
2767  tvNow.tv_sec -= WB_WINDOW_DELETE_TIMEOUT;
2768 
2769  // for each remaining window in my list, destroy it
2770  // TODO: a more intelligent way to do this. Currently it loops 2048 times
2771  // and this is NOT efficient. however, at 1Ghz, 2048 loops should easily
2772  // be less than 1 msec (maybe 10 to 20 cycles per loop on average).
2773  for(i1=WINDOW_ENTRY_ARRAY_MAX - 1; i1 >= 0; i1--)
2774  {
2775  Window wID = sWBHashEntries[i1].wID;
2776 
2777  if(!wID || wID == WINDOW_ENTRY_UNUSED)
2778  {
2779  continue;
2780  }
2781 
2782  if(sWBHashEntries[i1].iWindowState == WB_WINDOW_DELETE &&
2783  sWBHashEntries[i1].tvLastActivity.tv_sec < tvNow.tv_sec) // aged enough?
2784  {
2786  "%s - destroying entry %d for window %u (%08xH)\n",
2787  __FUNCTION__, i1,
2788  (int)sWBHashEntries[i1].wID, (int)sWBHashEntries[i1].wID);
2789 
2790 
2791 // WB_ERROR_PRINT("TEMPORARY: %s - delete window entry %d for window %u (%08xH)\n",
2792 // __FUNCTION__, i1,
2793 // (int)sWBHashEntries[i1].wID, (int)sWBHashEntries[i1].wID);
2794 
2795  __WindowEntryRestoreDefaultResources(i1); // make sure no resources are allocated
2796  __WindowEntryDestructor(i1); // finalizes destruction and marks it 'unused'
2797  }
2798  }
2799 }
2800 
2801 Window WBCreateWindow(Display *pDisplay, Window wIDParent,
2802  WBWinEvent pProc, const char *szClass,
2803  int iX, int iY, int iWidth, int iHeight, int iBorder, int iIO,
2804  int iFlags, XSetWindowAttributes *pXSWA)
2805 {
2806 Window idRval;
2807 _WINDOW_ENTRY_ *pEntry;
2808 WB_GEOM geom;
2809 
2810 
2811  if(!iFlags)
2812  {
2813  iFlags = CWBorderPixel | CWBackPixel | CWColormap | CWBitGravity; // for now...
2814  }
2815 
2816  idRval = XCreateWindow(pDisplay,
2817  wIDParent != None ? wIDParent : DefaultRootWindow(pDisplay),
2818  iX, iY, iWidth, iHeight, iBorder,
2819  DefaultDepth(pDisplay, DefaultScreen(pDisplay)),
2820  iIO,
2821  DefaultVisual(pDisplay, DefaultScreen(pDisplay)),
2822  iFlags,
2823  pXSWA);
2824 
2825  if(idRval != None)
2826  {
2827  WBRegisterWindowCallback(idRval, pProc); // this must happen first (it creates the internal structures)
2828  WBSetWindowClassName(idRval, szClass);
2829 
2830  if(wIDParent != None)
2831  {
2832  WBSetParentWindow(idRval, wIDParent); // assign the cached copy of the window's parent
2833  }
2834 
2835  pEntry = WBGetWindowEntry(idRval);
2836 
2837  if(!pEntry)
2838  {
2839  WB_ERROR_PRINT("ERROR: %s - unable to get window entry (window still created)\n", __FUNCTION__);
2840  }
2841  else
2842  {
2843  pEntry->pDisplay = pDisplay; // make sure
2844 
2845  if(!pEntry->pWMHints) // normally THIS WILL BE THE CASE
2846  {
2847  // if no window hints were configured, set some up
2848 
2849  if(!__internal_alloc_WMHints(pEntry))
2850  {
2852  XSetWMHints(pDisplay, idRval, pEntry->pWMHints);
2854  }
2855  }
2856 
2857  geom.x = 0; // client area always begins at 0,0
2858  geom.y = 0;
2859  geom.width = iWidth;
2860  geom.height = iHeight;
2861  geom.border = iBorder;
2862 
2863  WBInvalidateGeom(idRval, &geom, 0); // invalidate but don't paint yet
2864  }
2865  }
2866 
2867  return idRval;
2868 }
2869 
2870 void WBSetWMProperties(Window wID, const char *szTitle, XSizeHints *pNormalHints,
2871  XWMHints *pWMHints, XClassHint *pClassHints)
2872 {
2873 _WINDOW_ENTRY_ *pEntry = WBGetWindowEntry(wID);
2874 Display *pDisplay = pEntry ? pEntry->pDisplay : pDefaultDisplay;
2875 XTextProperty xTextProp;
2876 // XSetStandardProperties(pDisplay, pNew->wbFW.wID, szTitle, szTitle, None,
2877 // NULL, 0, NULL); // argv, argc, &xsh);
2878 // // this has been superseded by XSetWMProperties() and should not be used any more
2879 
2880 // typedef struct {
2881 // unsigned char *value;/* property data */
2882 // Atom encoding; /* type of property */
2883 // int format; /* 8, 16, or 32 */
2884 // unsigned long nitems;/* number of items in value */
2885 // } XTextProperty;
2886 
2887  if(szTitle)
2888  {
2889  xTextProp.value = (void *)szTitle;
2890  xTextProp.encoding = XA_STRING;
2891  xTextProp.format = 8; // 8-bit bytes in this one
2892  xTextProp.nitems = strlen(szTitle);
2893  }
2894 
2895  if(pWMHints)
2896  {
2897  if(!pEntry->pWMHints)
2898  {
2899  // if no window hints were configured, set some up
2900 
2901  if(__internal_alloc_WMHints(pEntry))
2902  {
2903  // TODO: error??
2904  }
2905  }
2906 
2907  if(pEntry->pWMHints)
2908  {
2909  // combine the hints!
2910 
2911  if(pWMHints->flags & InputHint)
2912  {
2913  pEntry->pWMHints->input = pWMHints->input;
2914  pEntry->pWMHints->flags |= InputHint;
2915  }
2916 
2917  if(pWMHints->flags & StateHint)
2918  {
2919  pEntry->pWMHints->initial_state = pWMHints->initial_state;
2920  pEntry->pWMHints->flags |= StateHint;
2921  }
2922 
2923  if(pWMHints->flags & IconPixmapHint)
2924  {
2926  if(pEntry->pxIcon != None)
2927  {
2928  XFreePixmap(pDisplay, pEntry->pxIcon);
2929 // pEntry->pxIcon = None;
2930  }
2932 
2933  pEntry->pxIcon = pWMHints->icon_pixmap;
2934  pEntry->pWMHints->icon_pixmap = pWMHints->icon_pixmap;
2935  pEntry->pWMHints->flags |= IconPixmapHint;
2936  }
2937  else if(pEntry->pxIcon != None &&
2938  (!(pEntry->pWMHints->flags & IconPixmapHint) || pEntry->pWMHints->icon_pixmap == None))
2939  {
2940  pEntry->pWMHints->icon_pixmap = pEntry->pxIcon;
2941  pEntry->pWMHints->flags |= IconPixmapHint;
2942  }
2943 
2944  if(pWMHints->flags & IconMaskHint)
2945  {
2947  if(pEntry->pxMask != None) // this is where I keep track of it
2948  {
2949  XFreePixmap(pDisplay, pEntry->pxMask);
2950 // pEntry->pxMask = None;
2951  }
2953 
2954  pEntry->pxMask = pWMHints->icon_mask;
2955  pEntry->pWMHints->icon_mask = pWMHints->icon_mask;
2956  pEntry->pWMHints->flags |= IconMaskHint;
2957  }
2958  else if(pEntry->pxMask != None &&
2959  (!(pEntry->pWMHints->flags & IconMaskHint) || pEntry->pWMHints->icon_mask == None))
2960  {
2961  pEntry->pWMHints->icon_mask = pEntry->pxMask;
2962  pEntry->pWMHints->flags |= IconMaskHint;
2963  }
2964 
2965  if(pWMHints->flags & IconWindowHint)
2966  {
2967  pEntry->pWMHints->icon_window = pWMHints->icon_window;
2968  pEntry->pWMHints->flags |= IconWindowHint;
2969  }
2970 
2971  if(pWMHints->flags & IconPositionHint)
2972  {
2973  pEntry->pWMHints->icon_x = pWMHints->icon_x;
2974  pEntry->pWMHints->icon_y = pWMHints->icon_y;
2975  pEntry->pWMHints->flags |= IconPositionHint;
2976  }
2977 
2978  if(pWMHints->flags & WindowGroupHint)
2979  {
2980  pEntry->pWMHints->window_group = pWMHints->window_group;
2981  pEntry->pWMHints->flags |= WindowGroupHint;
2982  }
2983  }
2984  }
2985 
2987  if(szTitle)
2988  {
2989  XSetWMProperties(pDisplay, wID, &xTextProp, &xTextProp, NULL, 0,
2990  pNormalHints,
2991  pEntry->pWMHints ? pEntry->pWMHints : pWMHints,
2992  pClassHints);
2993  }
2994  else
2995  {
2996  XSetWMProperties(pDisplay, wID, NULL, NULL, NULL, 0,
2997  pNormalHints,
2998  pEntry->pWMHints ? pEntry->pWMHints : pWMHints,
2999  pClassHints);
3000  }
3002 
3003  if(pWMHints)
3004  {
3005  if(pEntry->pWMHints)
3006  {
3007  XFree(pEntry->pWMHints);
3008  }
3009 
3011  pEntry->pWMHints = XGetWMHints(pDisplay, wID);
3013  }
3014 
3015 // void XSetWMProperties(Display *display, Window w,
3016 // XTextProperty *window_name, XTextProperty *icon_name,
3017 // char **argv, int argc,
3018 // XSizeHints *normal_hints, XWMHints *wm_hints,
3019 // XClassHint *class_hints);
3020 }
3021 
3022 void WBSetWindowTitle(Window wID, const char *szTitle)
3023 {
3024 _WINDOW_ENTRY_ *pEntry = WBGetWindowEntry(wID);
3025 Display *pDisplay = pEntry ? pEntry->pDisplay : pDefaultDisplay;
3026 Atom a1, a2;
3027 
3028 
3029  a1 = XInternAtom(pDisplay, "WM_NAME", False); /* global scope, must use XInternAtom */
3030  a2 = XInternAtom(pDisplay, "WM_ICON_NAME", False); /* global scope, must use XInternAtom */
3031 
3032  if(a1 != None)
3033  {
3035  XChangeProperty(pDisplay, wID, a1, XA_STRING, 8, PropModeReplace, (unsigned char *)szTitle, strlen(szTitle));
3037  }
3038 
3039  if(a2 != None)
3040  {
3042  XChangeProperty(pDisplay, wID, a2, XA_STRING, 8, PropModeReplace, (unsigned char *)szTitle, strlen(szTitle));
3044  }
3045 }
3046 
3047 void WBRegisterWindowCallback(Window wID, WBWinEvent pCallback)
3048 {
3049 _WINDOW_ENTRY_ *pEntry = __AddOrLocateEntry(wID);
3050 
3051  if(pEntry)
3052  {
3053  int bNewEntry = 0;
3054 
3055  if(!pEntry->pCallback)
3056  {
3057  bNewEntry = 1;
3058  }
3059 
3060  pEntry->pCallback = pCallback;
3061 
3062  if(bNewEntry)
3063  {
3064  WBRestoreDefaultCursor(wID); // assign the default cursor if this is a new entry
3065  }
3066  }
3067 }
3068 
3070 {
3071  int i1, /*iPrev, iNext,*/ iStart;
3072 
3073  if(wID == wIDApplication)
3074  {
3075  wIDApplication = None; // application window being unregistered
3076  }
3077 
3078  if(!nWBHashEntries)
3079  {
3081  "%s - nWBHashEntries == NULL\n", __FUNCTION__);
3082  return;
3083  }
3084 
3085  i1 = iStart = WINDOW_ENTRY_HASH(wID);
3086 
3087  while(sWBHashEntries[i1].wID) // the array must be pre-assigned to zero for this to work properly
3088  {
3089  if(sWBHashEntries[i1].wID == wID)
3090  break;
3091 
3092  i1++;
3093  i1 &= WINDOW_ENTRY_ARRAY_MAX;
3094 
3095  if(i1 == iStart)
3096  {
3097  i1 = -1; // as a flag that I didn't find it
3098  break;
3099  }
3100  }
3101 
3102  // free resources, mark the 'last activity' time, and
3103  // mark this entry as "to be destroyed"
3104 
3105  // NOTE: used to NOT actually change the callback address (TODO: change name of function?)
3106  // but in this case I'm going to NULL it. I don't want window callbacks being called
3107 
3108  if(i1 >= 0 && sWBHashEntries[i1].wID == wID) // guarantees I have the right one
3109  {
3110  // look through all of the timers and unregister any that involve this window
3111  // this is to avoid certain problems where windows aren't being notified properly
3112 
3113  DeletAllTimersForWindow(sWBHashEntries[i1].pDisplay, wID);
3114 
3115  if(sWBHashEntries[i1].iWindowState != WB_WINDOW_DELETE)
3116  {
3118  "Entry %d for window %d (%08xH) marked as 'to be destroyed'\n",
3119  i1, (int)wID, (int)wID);
3120 
3121 // __WindowEntryDestructor(i1);
3122  __WindowEntryRestoreDefaultResources(i1); // make sure no resources are allocated
3123 
3124  gettimeofday(&(sWBHashEntries[i1].tvLastActivity), NULL);
3125 
3126  sWBHashEntries[i1].iWindowState = WB_WINDOW_DELETE;
3127  }
3128 
3129  sWBHashEntries[i1].pCallback = NULL; // no more callback function. 'DestroyNotify' may still happen.
3130  // for 'DestroyNotify' events following this, WBDispatch will deal with that.
3131  }
3132 
3133 }
3134 
3135 // this one is ONLY called before mapping the window
3137 {
3138 
3139  // TODO: implement this
3140  // see _NET_WM_WINDOW_TYPE and _NET_WM_STATE atom documentation, wm-spec-latest.html
3141 
3142 }
3143 
3144 // this one is called AFTER mapping the window. The 'root' window
3145 // will have to be notified of the changes being made.
3146 void WBChangeWMPropertiesWindowType(Window wID, enum WMPropertiesWindowType wmProp, enum WMPropertiesWindowType wmChangeMask)
3147 {
3148 
3149  // TODO: implement this
3150  // see _NET_WM_WINDOW_TYPE and _NET_WM_STATE atom documentation, wm-spec-latest.html
3151 
3152 }
3153 
3155 {
3156 _WINDOW_ENTRY_ *pEntry = WBGetWindowEntry(wID);
3157 
3158 
3159  if(!pEntry)
3160  {
3161  return WMPropertiesWindowType_Normal;
3162  }
3163 
3164  return pEntry->eWindowType;
3165 }
3166 
3167 // this is a MUCH nicer way of handling variable length lists of WM_PROTOCOL atoms, as far as
3168 // the code's appearance goes. A bit of extra work, but you don't do this very often
3169 void WBSetWMProtocols(Window wID, Atom aProperty, ...)
3170 {
3171 va_list va, va2;
3172 Atom *pTemp, aArg;
3173 int i1, nItems;
3174 Display *pDisplay;
3175 Atom aTemp[32]; // temp storage, in case I need "something"
3176 _WINDOW_ENTRY_ *pEntry = WBGetWindowEntry(wID);
3177 
3178 
3179  pTemp = aTemp;
3180 
3181  pDisplay = WBGetWindowDisplay(wID);
3182 
3183  if(!pDisplay)
3184  {
3185  WB_ERROR_PRINT("ERROR - %s - no Display pointer\n", __FUNCTION__);
3186  return;
3187  }
3188 
3189  if(aProperty == None)
3190  {
3191  WB_ERROR_PRINT("TEMPORARY: %s - removing WM PROTOCOLS atom list\n", __FUNCTION__);
3192 
3194  XSetWMProtocols(pDisplay, wID, pTemp, 0);
3196 
3197  return;
3198  }
3199 
3200  va_start(va, aProperty);
3201 
3202  // copy the va_list so I can count items and possibly allocate memory
3203  va_copy(va2, va);
3204  nItems = 1; // always at least one if the first isn't "None"
3205 
3206  while(1)
3207  {
3208  aArg = va_arg(va2, Atom);
3209 
3210  if(aArg == None)
3211  {
3212  break;
3213  }
3214 
3215  nItems++;
3216  }
3217 
3218  va_end(va2); // cleanup
3219 
3220  // using the # of items I count here, determine if my array size is sufficient
3221  // and WBAlloc() a bigger one if needed. or not.
3222 
3223  if(nItems >= sizeof(aTemp)/sizeof(aTemp[0]))
3224  {
3225  pTemp = (Atom *)WBAlloc((nItems + 1) * sizeof(Atom));
3226 
3227  if(!pTemp)
3228  {
3229  WB_ERROR_PRINT("ERROR - %s - not enough memory for atom list, errno=%d (%xH)\n", __FUNCTION__, errno, errno);
3230  return;
3231  }
3232  }
3233 
3234  pTemp[0] = aProperty;
3235 
3236  for(i1=1; i1 < nItems; i1++)
3237  {
3238  aArg = va_arg(va, Atom);
3239 
3240  // TODO: debug, check value?
3241 
3242  pTemp[i1] = aArg;
3243  }
3244 
3245  pTemp[nItems] = None; // end with 'none'
3246 
3247  va_end(va); // cleanup
3248 
3249  // next, scan the list for properties that require special flags
3250 
3251  if(pEntry) // only if I have a window entry
3252  {
3253  pEntry->eWMProtocols &= ~WMPropertiesWMProtocols_Mask;
3254 
3255  for(i1=0; i1 < nItems; i1++)
3256  {
3257  if(pTemp[i1] == aWM_DELETE_WINDOW) // this window supports WM_DELETE
3258  {
3259  pEntry->eWMProtocols |= WMPropertiesWMProtocols_DeleteWindow;
3260  }
3261 
3262  // TODO, others
3263  }
3264  }
3265 
3266  // NOW, do the deed. and assign the atoms to WM_PROTOCOLS
3267 
3269  XSetWMProtocols(pDisplay, wID, pTemp, nItems);
3271 
3272 
3273  if(pTemp != aTemp) // if I allocated the buffer
3274  {
3275  WBFree(pTemp);
3276  }
3277 
3278 }
3279 
3280 
3281 Window WBLocateWindow(WBLocateWindowCallback callback, void *pData)
3282 {
3283 int i1, i2;
3284 
3285  if(!callback)
3286  {
3287  return (Window)0;
3288  }
3289 
3290  for(i1=0; i1 <= WINDOW_ENTRY_ARRAY_MAX; i1++)
3291  {
3292  Window wID = sWBHashEntries[i1].wID;
3293  if(wID && wID != WINDOW_ENTRY_UNUSED)
3294  {
3295  i2 = callback(wID, pData);
3296  if(i2 > 0)
3297  {
3298  return wID;
3299  }
3300  else if(i2 < 0)
3301  {
3302  return 0;
3303  }
3304  }
3305  }
3306 
3307  return 0;
3308 }
3309 
3310 
3311 // locate and return the application window. there can be only one.
3312 
3314 {
3315  return wIDApplication;
3316 }
3317 
3318 void WBSetApplicationWindow(Window wID)
3319 {
3320 _WINDOW_ENTRY_ *pEntry;
3321 
3322  if(wID == None)
3323  {
3324  wIDApplication = None;
3325  }
3326  else
3327  {
3328  pEntry = WBGetWindowEntry(wID);
3329 
3330  if(!pEntry)
3331  {
3332  wIDApplication = None;
3333  }
3334  else
3335  {
3336  wIDApplication = wID;
3337  }
3338  }
3339 }
3340 
3341 
3342 /**********************************************************************/
3343 /* */
3344 /* periodic timer and delayed event helper functions */
3345 /* */
3346 /**********************************************************************/
3347 
3348 //#define TIMER_ARRAY_SIZE 512
3349 //
3350 //static struct __TIMER_ENTRY__
3351 //{
3352 // struct __TIMER_ENTRY__ *pNext; // linked lists for performance
3353 // unsigned long long lTimeIndex; // time index for which this timer next expires
3354 // unsigned long lTimeInterval; // interval (or zero for one-shot timer)
3355 //
3356 // Display *pDisplay; // display associated with timer
3357 // Window wID; // window to receive timer event
3358 // long lID; // timer identifier
3359 //} TIMER_ENTRY;
3360 //
3361 //static TIMER_ENTRY axWBTimer[TIMER_ARRAY_SIZE];
3362 //static TIMER_ENTRY *pTimerEntryActive = NULL, *pTimerEntryFree = NULL;
3363 // // pointers for two linked lists. entries must be in either 'active' or 'free' list.
3364 
3366 {
3367 struct timeval tv;
3368 
3369  gettimeofday(&tv, NULL);
3370 
3371 #ifdef HAS_WB_UINT64_BUILTIN /* meaning that the WB_UINT64 data type is a 'built-in' */
3372 
3373  return (WB_UINT64)tv.tv_sec * (WB_UINT64)1000000
3374  + (WB_UINT64)tv.tv_usec;
3375 
3376 #else // it's a structure typedef
3377 
3378 // TODO: convert to double, do the math, then convert to struct using floor, frac
3379 #error not implemented (yet)
3380 
3381 #endif // WB_UINT64
3382 }
3383 
3384 int CreateTimer(Display *pDisplay, Window wID, unsigned long lInterval, long lID, int iPeriodic)
3385 {
3386 int i1;
3387 TIMER_ENTRY *pCur;
3388 // if timer linked lists aren't set up yet, do that now
3389 
3390 
3391  if(!pTimerEntryActive && !pTimerEntryFree) // initial state
3392  {
3393  bzero(axWBTimer, sizeof(axWBTimer));
3394 
3395  for(i1=1; i1 < TIMER_ARRAY_SIZE; i1++)
3396  {
3397  axWBTimer[i1 - 1].pNext = axWBTimer + i1; // build linked list
3398  }
3399 
3400  pTimerEntryFree = axWBTimer;
3401  }
3402 
3403  // search for match, return -2 if found
3404  pCur = pTimerEntryActive;
3405  while(pCur)
3406  {
3407  if(pCur->pDisplay == pDisplay &&
3408  pCur->wID == wID &&
3409  pCur->lID == lID)
3410  {
3411  return -2;
3412  }
3413 
3414  pCur = pCur->pNext;
3415  }
3416 
3417  if(!pTimerEntryFree)
3418  {
3419  return -1; // no memory
3420  }
3421 
3422  pCur = pTimerEntryFree;
3423  pTimerEntryFree = pTimerEntryFree->pNext;
3424 // pCur->pNext = NULL; // TODO in case I have to add locking later
3425 
3426  pCur->pDisplay = pDisplay;
3427  pCur->wID = wID;
3428  pCur->lID = lID;
3429 
3430  pCur->lTimeIndex = WBGetTimeIndex() + lInterval;
3431  if(iPeriodic)
3432  {
3433  pCur->lTimeInterval = lInterval;
3434  }
3435  else
3436  {
3437  pCur->lTimeInterval = 0;
3438  }
3439 
3440  pCur->pNext = pTimerEntryActive; // goes at the beginning of the list (it's easier)
3441  pTimerEntryActive = pCur;
3442 
3443  return 0;
3444 }
3445 
3446 static void __DeleteTimer(TIMER_ENTRY *pPrev, TIMER_ENTRY *pEntry)
3447 {
3448  pEntry->lTimeIndex = pEntry->lTimeInterval = 0;
3449  pEntry->pDisplay = NULL;
3450  pEntry->wID = 0;
3451  pEntry->lID = 0;
3452 
3453  if(!pPrev) // assume head of linked list
3454  {
3455  if(pTimerEntryActive != pEntry) // sanity checks
3456  {
3457  // a leak is better than a crash
3458  WB_ERROR_PRINT("%s - (1) unable to properly delete timer due to pointer inconsistency %p %p %p\n",
3459  __FUNCTION__, pTimerEntryActive, pEntry, pEntry->pNext);
3460  }
3461  else
3462  {
3463  pTimerEntryActive = pEntry->pNext;
3464  pEntry->pNext = pTimerEntryFree;
3465  pTimerEntryFree = pEntry;
3466  }
3467  }
3468  else if(pPrev->pNext == pEntry) // more sanity checks
3469  {
3470  pPrev->pNext = pEntry->pNext;
3471  pEntry->pNext = pTimerEntryFree;
3472  pTimerEntryFree = pEntry;
3473  }
3474  else
3475  {
3476  WB_ERROR_PRINT("%s - (2) unable to properly delete timer due to pointer inconsistency %p %p %p %p\n",
3477  __FUNCTION__, pPrev, pPrev->pNext, pEntry, pEntry->pNext);
3478  }
3479 }
3480 
3481 void DeleteTimer(Display *pDisplay, Window wID, long lID)
3482 {
3483 TIMER_ENTRY *pCur, *pPrev;
3484 
3485  pCur = pTimerEntryActive;
3486  pPrev = NULL;
3487 
3488  while(pCur)
3489  {
3490  if(pCur->pDisplay == pDisplay &&
3491  pCur->wID == wID &&
3492  pCur->lID == lID)
3493  {
3494  __DeleteTimer(pPrev, pCur);
3495  return;
3496  }
3497 
3498  pPrev = pCur;
3499  pCur = pCur->pNext;
3500  }
3501 }
3502 
3503 static void DeletAllTimersForWindow(Display *pDisplay, Window wID)
3504 {
3505 TIMER_ENTRY *pCur, *pPrev, *pNext;
3506 
3507  pCur = pTimerEntryActive;
3508  pPrev = NULL;
3509 
3510  while(pCur)
3511  {
3512  if(pCur->pDisplay == pDisplay &&
3513  pCur->wID == wID)
3514  {
3515  pNext = pCur->pNext;
3516 
3517  __DeleteTimer(pPrev, pCur);
3518 
3519  pCur = pNext; // must do it THIS way if I remove the entry in between
3520  // note: 'pPrev' doesn't change, but 'pNext' does (pPrev->pNext should equal pCur now)
3521  }
3522  else
3523  {
3524  pPrev = pCur;
3525  pCur = pCur->pNext;
3526  }
3527  }
3528 }
3529 
3530 static int __CheckTimers(Display *pDisplay, XEvent *pEvent)
3531 {
3532 TIMER_ENTRY *pCur, *pPrev;
3533 WB_UINT64 lTime = WBGetTimeIndex();
3534 
3535 // Find the _NEXT_ registered timer for which the current time 'crosses'
3536 
3537  pCur = pTimerEntryActive;
3538  pPrev = NULL;
3539 
3540  while(pCur)
3541  {
3542  if(pCur->pDisplay == pDisplay &&
3543  lTime >= pCur->lTimeIndex) // time index has crossed "the threshold" for the timer
3544  {
3545  if(!pEvent)
3546  {
3547  return 1; // only indicate that I found a timer that's active (don't process it)
3548  }
3549 
3550  if(pCur->lTimeInterval)
3551  {
3552  pCur->lTimeIndex += pCur->lTimeInterval;
3553 
3554  if(lTime >= pCur->lTimeIndex) // to prevent 'spinning'
3555  {
3556  pCur->lTimeIndex = lTime + pCur->lTimeInterval;
3557  }
3558  }
3559  else
3560  {
3561  __DeleteTimer(pPrev, pCur);
3562  }
3563 
3564  // now fill out the event structure pointed to by 'pEvent'
3565  bzero(pEvent, sizeof(*pEvent));
3566 
3567  pEvent->xclient.type = ClientMessage;
3568  pEvent->xclient.serial = 0;
3569  pEvent->xclient.send_event = 0;
3570  pEvent->xclient.display = pDisplay;
3571  pEvent->xclient.window = pCur->wID;
3572  pEvent->xclient.message_type = aWM_TIMER;
3573  pEvent->xclient.format=32; // 32-bit integers
3574  pEvent->xclient.data.l[0] = pCur->lID;
3575 
3576  return 1; // found/processed a timer
3577  }
3578 
3579  pPrev = pCur;
3580  pCur = pCur->pNext;
3581  }
3582 
3583  return 0; // no timer found/processed
3584 }
3585 
3586 static void __CreateDelayedEvent(XEvent *pEvent, unsigned int uiInterval)
3587 {
3588 int i1;
3589 DELAYED_EVENT_ENTRY *pCur;
3590 // if timer linked lists aren't set up yet, do that now
3591 
3592 
3593  if(!pDelayedEventEntryActive && !pDelayedEventEntryFree) // initial state
3594  {
3595  bzero(axWBDelayedEvent, sizeof(axWBDelayedEvent));
3596 
3597  for(i1=1; i1 < TIMER_ARRAY_SIZE; i1++)
3598  {
3599  axWBDelayedEvent[i1 - 1].pNext = axWBDelayedEvent + i1; // build linked list
3600  }
3601 
3602  pDelayedEventEntryFree = axWBDelayedEvent;
3603  }
3604 
3605  // search for match, return -2 if found
3606  pCur = pDelayedEventEntryFree;
3607  pDelayedEventEntryFree = pDelayedEventEntryFree->pNext;
3608 // pCur->pNext = NULL; // TODO in case I have to add locking later
3609 
3610  memcpy(&(pCur->event), pEvent, sizeof(pCur->event));
3611 
3612  pCur->lTimeIndex = WBGetTimeIndex() + uiInterval;
3613 
3614  pCur->pNext = pDelayedEventEntryActive; // goes at the beginning of the list (it's easier)
3615  pDelayedEventEntryActive = pCur;
3616 }
3617 
3618 static void __DeleteDelayedEvent(DELAYED_EVENT_ENTRY *pPrev, DELAYED_EVENT_ENTRY *pEntry)
3619 {
3620  pEntry->lTimeIndex = 0;
3621  bzero(&(pEntry->event), sizeof(pEntry->event));
3622 
3623  if(!pPrev) // assume head of linked list
3624  {
3625  if(pDelayedEventEntryActive != pEntry) // sanity checks
3626  {
3627  // a leak is better than a crash
3628  WB_ERROR_PRINT("%s - (1) unable to properly delete delayed event due to pointer inconsistency %p %p %p\n",
3629  __FUNCTION__, pTimerEntryActive, pEntry, pEntry->pNext);
3630  }
3631  else
3632  {
3633  pDelayedEventEntryActive = pEntry->pNext;
3634  pEntry->pNext = pDelayedEventEntryFree;
3635  pDelayedEventEntryFree = pEntry;
3636  }
3637  }
3638  else if(pPrev->pNext == pEntry) // more sanity checks
3639  {
3640  pPrev->pNext = pEntry->pNext;
3641  pEntry->pNext = pDelayedEventEntryFree;
3642  pDelayedEventEntryFree = pEntry;
3643  }
3644  else
3645  {
3646  WB_ERROR_PRINT("%s - (2) unable to properly delete delayed event due to pointer inconsistency %p %p %p %p\n",
3647  __FUNCTION__, pPrev, pPrev->pNext, pEntry, pEntry->pNext);
3648  }
3649 }
3650 
3651 static int __attribute__((noinline)) __CheckDelayedEvents(Display *pDisplay, XEvent *pEvent)
3652 {
3653 DELAYED_EVENT_ENTRY *pCur, *pPrev;
3654 WB_UINT64 lTime = WBGetTimeIndex();
3655 
3656  // Find the _NEXT_ registered delayed event for which the current time 'crosses'
3657 
3658  pCur = pDelayedEventEntryActive;
3659  pPrev = NULL;
3660 
3661  while(pCur)
3662  {
3663  if((pCur->event.xany.display == pDisplay || pCur->event.xany.window == None) &&
3664  lTime >= pCur->lTimeIndex)
3665  {
3666  // fill out the event structure pointed to by 'pEvent'
3667 
3668  if(pCur->event.xany.window != None)
3669  {
3670  _WINDOW_ENTRY_ *pEntry = WBGetWindowEntry(pCur->event.xany.window);
3671 
3672  if(!pEntry || WB_IS_WINDOW_DESTROYED(*pEntry))
3673  {
3674  __DeleteDelayedEvent(pPrev, pCur);
3675  return 0; // none processed [code is easier this way]
3676  }
3677  }
3678 
3679  if(pEvent) // if NULL, I'm only looking to see if there IS one
3680  {
3681  memcpy(pEvent, &(pCur->event), sizeof(*pEvent));
3682 
3683  __DeleteDelayedEvent(pPrev, pCur);
3684  }
3685 
3686  return 1; // processed
3687  }
3688 
3689  pPrev = pCur;
3690  pCur = pCur->pNext;
3691  }
3692 
3693  return 0; // no timer processed
3694 }
3695 
3696 
3697 
3698 /**********************************************************************/
3699 /* */
3700 /* message loop helper functions */
3701 /* */
3702 /**********************************************************************/
3703 
3704 static Bool __WBCheckIfEventPredicate(Display *pDisplay, XEvent *pEvent, XPointer arg)
3705 {
3706  if(pEvent && pEvent->type != Expose)
3707  {
3708  return TRUE;
3709  }
3710  else
3711  {
3712  return FALSE;
3713  }
3714 }
3715 
3716 static Bool __WBCheckIfEventPredicate0(Display *pDisplay, XEvent *pEvent, XPointer arg)
3717 {
3718  if(pEvent &&
3719  (pEvent->type == SelectionNotify ||
3720  pEvent->type == SelectionClear ||
3721  pEvent->type == SelectionRequest))
3722  {
3723  return TRUE;
3724  }
3725  else
3726  {
3727  return FALSE;
3728  }
3729 }
3730 
3731 // WBCheckGetEvent - get next (prioritized) event, do translations
3732 
3733 static int iMouseState = 0; // artificial mouse state (global) for dbl-click and drag
3734 static int iMouseModShiftCtrl = 0; // Mod/Shift/Ctrl state for last mouse state
3735 static int iMouseDragButtonState = 0; // mouse button bit-flags for last mouse state and auto-drag-cancel
3736 static WB_UINT64 tmMouse; // timeval for most recent processed mouse event
3737 static Window wMouseCapture = None;
3738 static int iMouseX = 0, iMouseY = 0; // RAW x,y position from mouse message
3739 
3740 
3741 enum
3742 {
3743  MouseState_NONE = 0,
3744  MouseState_LCLICK = 1,
3745  MouseState_WAS_LCLICK,
3746  MouseState_LDRAG, // 'drag' states imply mouse captured
3747  MouseState_RCLICK,
3748  MouseState_WAS_RCLICK,
3749  MouseState_RDRAG,
3750  MouseState_CCLICK,
3751  MouseState_WAS_CCLICK,
3752  MouseState_CDRAG
3753 };
3754 
3756 {
3757 WB_UINT64 qwTick = WBGetTimeIndex();
3758 
3759  return tmDefaultDisplay + (qwTick - qwDefaultDisplayTick) / 1000;
3760 }
3761 
3762 int WBCheckGetEvent(Display *pDisplay, XEvent *pEvent)
3763 {
3764  return __InternalCheckGetEvent(pDisplay, pEvent, None); // no modal window implies "do certain things differently"
3765 }
3766 
3767 void WBWaitForEvent(Display *pDisplay)
3768 {
3769 int iTemp;
3770 
3771  // First, see if I have any priority or other queued events
3772 
3773  iTemp = iWBQueuedEvent;
3774 
3775  // check internal queues first, if there's something there
3776  // these queues won't change without calling WBCheckGetEvent()
3777 
3778  while(WB_LIKELY(iTemp >= 0))
3779  {
3780  if(WB_LIKELY(axWBEvt[iTemp].pDisplay == pDisplay))
3781  {
3782  return; // found one
3783  }
3784 
3785  iTemp = axWBEvt[iTemp].iNext;
3786  }
3787 
3788  iTemp = iWBPaintEvent;
3789 
3790  while(iTemp >= 0)
3791  {
3792  if(WB_LIKELY(axWBEvt[iTemp].pDisplay == pDisplay))
3793  {
3794  return; // found one
3795  }
3796 
3797  iTemp = axWBEvt[iTemp].iNext;
3798  }
3799 
3800  iTemp = 10; // see below
3801 
3802  while(!bQuitFlag) // forever, unless I quit
3803  {
3804  // check timers and internal event queues
3805 
3806  if(__CheckTimers(pDisplay, NULL)) // a timer is ready to create an event?
3807  {
3808  return;
3809  }
3810 
3811  if(__CheckDelayedEvents(pDisplay, NULL)) // delayed events
3812  {
3813  return;
3814  }
3815 
3816  // call XSync before checking if events have been queued
3817  // I do this every 10 loops, waiting ~1msec per loop
3818 
3819  if(iTemp >= 10)
3820  {
3822  XSync(pDisplay, 0); // make sure I'm sync'd up before waiting
3824 
3825  iTemp = 0;
3826  }
3827  else
3828  {
3829  iTemp++;
3830  }
3831 
3832  if(XEventsQueued(pDisplay, QueuedAlready))
3833  {
3834  return; // I have events waiting!
3835  }
3836 
3837 #ifdef HAVE_NANOSLEEP
3838  {
3839  struct timespec tsp;
3840  tsp.tv_sec = 0;
3841  tsp.tv_nsec = 1000000; // wait for 1 msec
3842 
3843  nanosleep(&tsp, NULL);
3844  }
3845 #else // HAVE_NANOSLEEP
3846 
3847  usleep(1000); // 1000 microsecs - a POSIX alternative to 'nanosleep'
3848 
3849 #endif // HAVE_NANOSLEEP
3850  }
3851 }
3852 
3853 // "internal" version that allows me to do things different for modal loops
3854 
3855 int __InternalCheckGetEvent(Display *pDisplay, XEvent *pEvent, Window wIDModal)
3856 {
3857 int iRval = 0, iQueued;
3858 int iFirstTime = 1;
3859 static unsigned long long ullLastTime = 0;
3860 
3861  do
3862  {
3863  iQueued = XEventsQueued(pDisplay, QueuedAlready);
3864 
3865  if(!iQueued && iFirstTime)
3866  {
3867  unsigned long long ullTemp = WBGetTimeIndex();
3868  if((ullTemp - ullLastTime) > 50000) // make sure it's more than 0.05 seconds, so I don't "spin"
3869  {
3870  ullLastTime = ullTemp;
3871 
3873 // XFlush(pDisplay);
3874  XSync(pDisplay, 0); // no events? attempt a sync (but only do this once per loop)
3876  }
3877 
3878  iQueued = XEventsQueued(pDisplay, QueuedAlready);
3879  }
3880 
3881  iFirstTime = 0; // only XSync once per loop and only if I need to
3882 
3883  // check for selection events first...
3884 
3885  iRval = iQueued && // SelectionNotify, SelectionClear, SelectionRequest
3886  XCheckIfEvent(pDisplay, pEvent, __WBCheckIfEventPredicate0, NULL);
3887 
3888  if(!iRval)
3889  {
3890  if(WB_UNLIKELY(0 != (iRval = __CheckDelayedEvents(pDisplay, pEvent)))) // delayed events first
3891  {
3892  break;
3893  }
3894 
3895  if(WB_UNLIKELY(0 != (iRval = __CheckTimers(pDisplay, pEvent)))) // then timers
3896  {
3897  break;
3898  }
3899 
3900  // then check for actual events in the X11 event queue
3901 
3902  iRval = iQueued /*XEventsQueued(pDisplay, QueuedAlready)*/ &&
3903  (XCheckIfEvent(pDisplay, pEvent, __WBCheckIfEventPredicate, NULL) ||
3904  XCheckMaskEvent(pDisplay, ExposureMask, pEvent));
3905  }
3906 
3907  if(iRval)
3908  {
3909  if(pDisplay == pDefaultDisplay) // for default display, grab the timestamp
3910  {
3911  Time tmEvent = 0;
3912 
3913  switch(pEvent->type) // for the ones that have timestamps, I check for them
3914  {
3915  case KeyPress:
3916  case KeyRelease:
3917  tmEvent =((XKeyEvent *)pEvent)->time;
3918  break;
3919  case ButtonPress:
3920  case ButtonRelease:
3921  tmEvent =((XButtonEvent *)pEvent)->time;
3922  break;
3923  case MotionNotify:
3924  tmEvent =((XMotionEvent *)pEvent)->time;
3925  break;
3926  case EnterNotify:
3927  case LeaveNotify:
3928  tmEvent =((XCrossingEvent *)pEvent)->time;
3929  break;
3930  case PropertyNotify:
3931  tmEvent =((XPropertyEvent *)pEvent)->time;
3932  break;
3933  case SelectionRequest:
3934  tmEvent =((XSelectionRequestEvent *)pEvent)->time;
3935  break;
3936  case SelectionClear:
3937  tmEvent =((XSelectionClearEvent *)pEvent)->time;
3938  break;
3939  case SelectionNotify:
3940  tmEvent =((XSelectionEvent *)pEvent)->time;
3941  break;
3942  }
3943 
3944  if(tmEvent > tmDefaultDisplay)
3945  {
3946  tmDefaultDisplay = tmEvent; // with the assumption that the millis here NEVER wrap around
3947  qwDefaultDisplayTick = WBGetTimeIndex(); // snap this too so I can adjust it
3948  }
3949  }
3950 
3951  if(pEvent->type == Expose) // expose events get special handling
3952  {
3953  WBProcessExposeEvent((XExposeEvent *)pEvent); // this consolidates incoming Expose events
3954 
3955  // after processing the expose event, use XSync to immediately sync back up
3956  // in case there are more events NOT received yet. Only for EXPOSE though.
3957 
3959  XSync(pDisplay, 0);
3961 
3962  iRval = 0;
3963  continue; // added - this might have been a bug...?
3964  }
3965 
3966  // TODO: other events that might be part of the above list of events to grab first
3967 
3968  break;
3969  }
3970  else // no display events
3971  {
3972  if(XEventsQueued(pDisplay, QueuedAfterFlush) > 0) // flushes and attempts to read more events into queue
3973  {
3974  continue; // more events were read in, so go for it
3975  }
3976 
3977  // regular 'next event' processing
3978 
3979  iRval = WBNextEvent(pDisplay, pEvent); // get 'internal' queued events
3980 
3981  break; // regardless if an event is found I break out now
3982  }
3983 
3984  // this conveniently re-tries when I get an Expose event (to try and consolidate them)
3985 
3986 
3987  } while(1);
3988 
3989  if(iRval)
3990  {
3991  if(pEvent->type == ConfigureNotify)
3992  {
3993  WB_GEOM gm;
3994  Window wIDTemp;
3995  // NOTE: this could be done in WBWindowDispatch but the chance that
3996  // it might be missed demands it be handled at a lower level
3997 
3998  _WINDOW_ENTRY_ *pEntry = WBGetWindowEntry(pEvent->xconfigure.window);
3999 
4000  if(pEntry) // notification needs to update internal 'absolute geometry' data
4001  {
4002  // NOTE: window will get at least one of these when it becomes visible
4003  // until then the values are initialized as zeros
4004 
4005  if(pEntry->geomAbsolute.width == 0 && // first time through, trust the message
4006  pEntry->geomAbsolute.height == 0)
4007  {
4008  pEntry->geomAbsolute.x = pEvent->xconfigure.x;
4009  pEntry->geomAbsolute.y = pEvent->xconfigure.y;
4010  pEntry->geomAbsolute.width = pEvent->xconfigure.width;
4011  pEntry->geomAbsolute.height = pEvent->xconfigure.height;
4012  pEntry->geomAbsolute.border = pEvent->xconfigure.border_width;
4013 
4014  if(!pEntry->rgnClip) // first time through, create clip region that encompasses all of it
4015  {
4016  XRectangle xrct;
4017 
4018  // NOTE: I must do it THIS way because the window isn't mapped yet.
4019 
4020  pEntry->width = pEvent->xconfigure.width;
4021  pEntry->height = pEvent->xconfigure.height;
4022  pEntry->border = pEvent->xconfigure.border_width;
4023 
4024  pEntry->rgnClip = XCreateRegion();
4025 
4026  if(pEntry->rgnClip)
4027  {
4028  xrct.x = 0;//(short)pEntry->x;
4029  xrct.y = 0;//(short)pEntry->y;
4030  xrct.width = (unsigned short)pEntry->width;
4031  xrct.height = (unsigned short)pEntry->height;
4032 
4033  XUnionRectWithRegion(&xrct, pEntry->rgnClip, pEntry->rgnClip);
4034  }
4035  }
4036  }
4037  else if(WB_IS_WINDOW_MAPPED(*pEntry)) // get values from parent window(s) and adjust absolute coords
4038  { // NOTE: this won't work if this OR the PARENT windows aren't mapped
4039  wIDTemp = pEvent->xconfigure.window;
4040  pEntry->geomAbsolute.x = pEntry->geomAbsolute.y = 0;
4041 
4042  while(wIDTemp)
4043  {
4044  WBGetWindowGeom(wIDTemp, &gm);
4045  pEntry->geomAbsolute.x += gm.x;
4046  pEntry->geomAbsolute.y += gm.y;
4047 
4048  if(wIDTemp == pEvent->xconfigure.window)
4049  {
4050  pEntry->geomAbsolute.width = gm.width;
4051  pEntry->geomAbsolute.height = gm.height;
4052  pEntry->geomAbsolute.border = gm.border;
4053  }
4054 
4055  wIDTemp = WBGetParentWindow(wIDTemp);
4056  }
4057  }
4058  else
4059  {
4060  WB_ERROR_PRINT("TEMPORARY: %s - subsequent ConfigureNotify and window is not yet mapped\n", __FUNCTION__);
4061  }
4062 
4064  "%s - update internal geometry %d (%08xH) %d, %d, %d, %d\n",
4065  __FUNCTION__,
4066  (int)pEvent->xconfigure.window, (int)pEvent->xconfigure.window,
4067  pEntry->geomAbsolute.x, pEntry->geomAbsolute.y,
4068  pEntry->geomAbsolute.width, pEntry->geomAbsolute.height);
4069  }
4070  }
4071 
4072  // MOUSE translations (double-click, drag help)
4073  // to use these, the client shouldn't handle the system mouse events
4074  // Additionally, behavior DIFFERS SIGNIFICANTLY when inside a modal loop
4075 
4076  if(pEvent->type == ButtonPress ||
4077  pEvent->type == ButtonRelease ||
4078  pEvent->type == MotionNotify)
4079  {
4080  int iButton1 = (((XButtonEvent *)pEvent)->state & Button1Mask)
4081  || ((XButtonEvent *)pEvent)->button == Button1;
4082  int iButton2 = (((XButtonEvent *)pEvent)->state & Button2Mask)
4083  || ((XButtonEvent *)pEvent)->button == Button2;
4084  int iButton3 = (((XButtonEvent *)pEvent)->state & Button3Mask)
4085  || ((XButtonEvent *)pEvent)->button == Button3;
4086  int iButton4 = (((XButtonEvent *)pEvent)->state & Button4Mask)
4087  || ((XButtonEvent *)pEvent)->button == Button4;
4088  int iButton5 = (((XButtonEvent *)pEvent)->state & Button5Mask)
4089  || ((XButtonEvent *)pEvent)->button == Button5;
4090 
4091  int iDragButtonState = (iButton1 ? 1 : 0) // mostly for dragging, but used in other places, too
4092  | (iButton2 ? 2 : 0)
4093  | (iButton3 ? 4 : 0);
4094 
4095  int iModShiftCtrl = ((XButtonEvent *)pEvent)->state & (ShiftMask | LockMask | ControlMask | Mod1Mask
4096  | Mod2Mask | Mod3Mask | Mod4Mask | Mod5Mask);
4097  int iX, iY;
4098 
4099  if(pEvent->type == ButtonPress &&
4100  (iMouseState == MouseState_NONE ||
4101  iMouseState == MouseState_WAS_LCLICK))
4102  {
4103  Window wIDCurrent = None;
4104  int iRevert = 0;
4105  Window wID0 = ((XButtonEvent *)pEvent)->window;
4106 
4107  // NOTE: if window does _NOT_ have input focus, make sure it gets the focus
4108  // and re-display it's container in front of other windows (as needed)
4109 
4110 
4112  XGetInputFocus(pDisplay, &wIDCurrent, &iRevert);
4114 
4115 // WB_ERROR_PRINT("TEMPORARY: mouse click %d, old window %d (%08xH), new window %d (%08xH)\n",
4116 // iMouseState, (int)wIDCurrent, (int)wIDCurrent, (int)wID0, (int)wID0);
4117 
4118  if(wIDCurrent != wID0 &&
4119  ( wIDModal == None || // ok if NOT in modal state
4120  WBIsChildWindow(wIDModal, wID0))) // window I'm switching focus to is child of modal loop owner
4121  {
4122 // WBSetInputFocus(((XButtonEvent *)pEvent)->window);
4123  // TODO: replicate part of 'set focus' code as private function to avoid duplication?
4124 
4125 
4126  _WINDOW_ENTRY_ *pPrev = WBGetWindowEntry(wIDCurrent);
4127  _WINDOW_ENTRY_ *pEntry = WBGetWindowEntry(wID0);
4128 
4129 // WB_ERROR_PRINT("TEMPORARY: mouse click %d, old window %d (%08xH), new window %d (%08xH)\n",
4130 // iMouseState, (int)wIDCurrent, (int)wIDCurrent, (int)wID0, (int)wID0);
4131 
4132  if(pPrev)
4133  {
4134  if(WB_CHECK_SET_FOCUS_ON_MAP(*pPrev))
4135  {
4137  "%s:%d - %d (%08xH) disabling focus change on map\n",
4138  __FUNCTION__, __LINE__, (int)wIDCurrent, (int)wIDCurrent);
4139 
4140  pPrev->iWindowState = WB_WINDOW_UNMAPPED; // remove 'set focus' state
4141  }
4142  }
4143 
4144  // see if expose event has been done on this yet...
4145  if(WB_LIKELY(pEntry) && //WB_UNLIKELY(!pEntry->width && !pEntry->height && !pEntry->border))
4146  WB_UNLIKELY(WB_IS_WINDOW_UNMAPPED(*pEntry)))
4147  {
4149  "%s:%d - %d (%08xH) not yet visible, enabling focus change on map\n",
4150  __FUNCTION__, __LINE__, (int)wID0, (int)wID0);
4151 
4152  pEntry->iWindowState = WB_WINDOW_SET_FOCUS_ON_MAP;
4153  }
4154  else
4155  {
4157  "%s:%d - %d (%08xH) calling XSetInputFocus\n",
4158  __FUNCTION__, __LINE__, (int)wID0, (int)wID0);
4159 
4160 // WB_ERROR_PRINT("TEMPORARY - %s calling XSetInputFocus on wID=%u (%08xH)\n", __FUNCTION__, (int)wID0, (uint32_t)wID0);
4161 
4163  XSetInputFocus(pDisplay, wID0, RevertToParent, CurrentTime);
4165  }
4166 
4167  iMouseState = MouseState_NONE; // if window focus changes, I don't want double clicking or dragging to happen
4168  }
4169 
4170  // TODO: bring top level window forward if not topmost already
4171  }
4172 
4173 
4174  // based on current state
4175  switch(iMouseState)
4176  {
4177  case MouseState_WAS_LCLICK:
4178 
4179  if(pEvent->type == ButtonPress)
4180  {
4181  int iDblClickThresh = CHGetDoubleClickDistance(pDisplay);
4182  WB_UINT64 tmTemp = WBGetTimeIndex(); // snapshot the current time (microseconds)
4183 
4184  if(abs(iMouseX - ((XButtonEvent *)pEvent)->x) >= iDblClickThresh || // too far
4185  abs(iMouseY - ((XButtonEvent *)pEvent)->y) >= iDblClickThresh || // too far
4186  (tmTemp - tmMouse) > 1000 * CHGetDoubleClickTime(pDisplay)) // too slow
4187  {
4188  iMouseState = MouseState_NONE;
4189 
4190  // flow through to next section
4191  }
4192  else if(!iButton1 || iButton2 || iButton3)
4193  {
4194  iMouseState = MouseState_NONE;
4195 
4196  // flow through to next section
4197  }
4198  else
4199  {
4200  // post a double-click notification
4201 
4202  XClientMessageEvent evt = {
4203  .type=ClientMessage,
4204  .serial=0,
4205  .send_event=0,
4206  .display=pDisplay,
4207  .window=((XButtonEvent *)pEvent)->window,
4208  .message_type=aWM_POINTER,
4209  .format=32
4210  };
4211 
4212  evt.data.l[0] = WB_POINTER_DBLCLICK;
4213  evt.data.l[1] = iDragButtonState;
4214  evt.data.l[2] = (iModShiftCtrl & ShiftMask ? WB_KEYEVENT_SHIFT : 0)
4215  | (iModShiftCtrl & ControlMask ? WB_KEYEVENT_CTRL : 0)
4216  | (iModShiftCtrl & Mod1Mask ? WB_KEYEVENT_ALT : 0);
4217 
4218  WBXlatCoordPoint(((XButtonEvent *)pEvent)->window, ((XButtonEvent *)pEvent)->x, ((XButtonEvent *)pEvent)->y,
4219  ((XButtonEvent *)pEvent)->window, &iX, &iY);
4220 
4221  evt.data.l[3] = iX;
4222  evt.data.l[4] = iY;
4223 
4224  WBPostPriorityEvent(((XButtonEvent *)pEvent)->window, (XEvent *)&evt);
4225 
4226  break;
4227  }
4228  }
4229  else if(pEvent->type == ButtonRelease)
4230  {
4231  iMouseState = MouseState_NONE;
4232 
4233  break;
4234  }
4235  else
4236  {
4237  // TODO: mouse motion is OK, but what about anything else?
4238 
4239  break;
4240  }
4241 
4242  // at this poitn the state has been re-assigned
4243 
4244  case MouseState_NONE: // base condition - no current activity
4245  if(pEvent->type == ButtonPress)
4246  {
4247  // for now, post a 'CLICK' notification every time
4248 
4249  XClientMessageEvent evt = {
4250  .type=ClientMessage,
4251  .serial=0,
4252  .send_event=0,
4253  .display=pDisplay,
4254  .window=((XButtonEvent *)pEvent)->window,
4255  .message_type=aWM_POINTER,
4256  .format=32
4257  };
4258 
4259  evt.data.l[0] = WB_POINTER_CLICK;
4260  evt.data.l[1] = iDragButtonState
4261  | (iButton4 ? 8 : 0)
4262  | (iButton5 ? 16 : 0);
4263  evt.data.l[2] = (iModShiftCtrl & ShiftMask ? WB_KEYEVENT_SHIFT : 0)
4264  | (iModShiftCtrl & ControlMask ? WB_KEYEVENT_CTRL : 0)
4265  | (iModShiftCtrl & Mod1Mask ? WB_KEYEVENT_ALT : 0);
4266 
4267  WBXlatCoordPoint(((XButtonEvent *)pEvent)->window, ((XButtonEvent *)pEvent)->x, ((XButtonEvent *)pEvent)->y,
4268  ((XButtonEvent *)pEvent)->window, &iX, &iY);
4269 
4270  evt.data.l[3] = iX;
4271  evt.data.l[4] = iY;
4272 
4273  WBPostPriorityEvent(((XButtonEvent *)pEvent)->window, (XEvent *)&evt);
4274 
4275 // WB_ERROR_PRINT("TEMPORARY: mouse click at %d %d for %d (%08xH) %d %d %d\n",
4276 // iX, iY, (int)((XButtonEvent *)pEvent)->window, (int)((XButtonEvent *)pEvent)->window,
4277 // iButton1, iButton2, iButton3);
4278 
4279  if(iButton1 && !iButton2 && !iButton3 && !iButton4 && !iButton5)
4280  {
4281  // left button - for now this is all I test for
4282  iMouseState = MouseState_LCLICK;
4283  iMouseModShiftCtrl = iModShiftCtrl;
4284 
4285  tmMouse = WBGetTimeIndex(); // snapshot the time (microseconds)
4286 
4287  iMouseX = ((XButtonEvent *)pEvent)->x;
4288  iMouseY = ((XButtonEvent *)pEvent)->y;
4289  }
4290  else if(iButton4 || iButton5)
4291  {
4292  // notify window of mouse wheel scrolling. iButton4 is "scroll up", iButton5 is "scroll down"
4293 
4294  XClientMessageEvent evt = {
4295  .type=ClientMessage,
4296  .serial=0,
4297  .send_event=0,
4298  .display=pDisplay,
4299  .window=pEvent->xany.window,
4300  .message_type=aWM_POINTER,
4301  .format=32
4302  };
4303 
4304  if(iButton4)
4305  {
4306  evt.data.l[0] = WB_POINTER_SCROLLUP;
4307  }
4308  else
4309  {
4310  evt.data.l[0] = WB_POINTER_SCROLLDOWN;
4311  }
4312 
4313  evt.data.l[1] = iDragButtonState
4314  | (iButton4 ? 8 : 0)
4315  | (iButton5 ? 16 : 0);
4316  evt.data.l[2] = (iModShiftCtrl & ShiftMask ? WB_KEYEVENT_SHIFT : 0)
4317  | (iModShiftCtrl & ControlMask ? WB_KEYEVENT_CTRL : 0)
4318  | (iModShiftCtrl & Mod1Mask ? WB_KEYEVENT_ALT : 0);
4319 
4320  WBXlatCoordPoint(((XButtonEvent *)pEvent)->window, iMouseX, iMouseY,
4321  ((XButtonEvent *)pEvent)->window, &iX, &iY);
4322 
4323  evt.data.l[3] = iX; // TODO: should this be UN-TRANSLATED or SCREEN coordinates?
4324  evt.data.l[4] = iY; // should I indicate WHICH WINDOW this should be for?
4325 
4326  WBWindowDispatch(evt.window, (XEvent *)&evt);
4327  }
4328  }
4329  else if(pEvent->type == ButtonRelease)
4330  {
4331 // if(iButton1 || iButton2 || iButton3 || iButton4 || iButton5)
4332 // {
4333 // WB_ERROR_PRINT("TEMPORARY: %s button release, button state %d %d %d %d %d\n",
4334 // __FUNCTION__, iButton1, iButton2, iButton3, iButton4, iButton5);
4335 // }
4336  }
4337  else // if(pEvent->type == MotionNotify)
4338  {
4339 // if(iButton1 || iButton2 || iButton3 || iButton4 || iButton5)
4340 // {
4341 // WB_ERROR_PRINT("TEMPORARY: %s motion notify, button state %d %d %d %d %d\n",
4342 // __FUNCTION__, iButton1, iButton2, iButton3, iButton4, iButton5);
4343 // }
4344  }
4345 
4346  break;
4347 
4348  case MouseState_LCLICK:
4349 
4350  if(pEvent->type == ButtonPress)
4351  {
4352  if(!iButton1 || iButton2 || iButton3)
4353  {
4354  // TODO: MCLICK and RCLICK?
4355  iMouseState = MouseState_NONE;
4356  }
4357  else
4358  {
4359  // TODO: is this a double-click?
4360  }
4361  }
4362  else if(pEvent->type == ButtonRelease)
4363  {
4364  iMouseState = MouseState_WAS_LCLICK;
4365  }
4366  else if(iMouseModShiftCtrl != iModShiftCtrl) // modifier state change
4367  {
4368  iMouseState = MouseState_NONE;
4369  }
4370  else
4371  {
4372  int iDragThresh = CHGetDragThreshold(pDisplay);
4373 
4374  // DRAG DETECT (reserved)
4375 
4376  if(abs(iMouseX - ((XButtonEvent *)pEvent)->x) >= iDragThresh ||
4377  abs(iMouseY - ((XButtonEvent *)pEvent)->y) >= iDragThresh)
4378  {
4379  // check for entering drag state - window proc needs to respond correctly
4380 
4381  XClientMessageEvent evt = {
4382  .type=ClientMessage,
4383  .serial=0,
4384  .send_event=0,
4385  .display=pDisplay,
4386  .window=pEvent->xany.window,
4387  .message_type=aWM_POINTER,
4388  .format=32
4389  };
4390 
4391  evt.data.l[0] = WB_POINTER_DRAG;
4392  evt.data.l[1] = iDragButtonState
4393  | (iButton4 ? 8 : 0)
4394  | (iButton5 ? 16 : 0);
4395  evt.data.l[2] = (iModShiftCtrl & ShiftMask ? WB_KEYEVENT_SHIFT : 0)
4396  | (iModShiftCtrl & ControlMask ? WB_KEYEVENT_CTRL : 0)
4397  | (iModShiftCtrl & Mod1Mask ? WB_KEYEVENT_ALT : 0);
4398 
4399  iMouseDragButtonState = iDragButtonState;
4400 
4401  WBXlatCoordPoint(((XButtonEvent *)pEvent)->window, iMouseX, iMouseY,
4402  ((XButtonEvent *)pEvent)->window, &iX, &iY);
4403 
4404  // NOTE: sending ORIGINAL 'click' mouse coordinates since the drag moves the mouse
4405  evt.data.l[3] = iX; // TODO: should this be UN-TRANSLATED or SCREEN coordinates?
4406  evt.data.l[4] = iY; // should I indicate WHICH WINDOW this should be for?
4407 
4408  WBXlatCoordPoint(((XButtonEvent *)pEvent)->window, ((XButtonEvent *)pEvent)->x, ((XButtonEvent *)pEvent)->y,
4409  ((XButtonEvent *)pEvent)->window, &iX, &iY);
4410 
4411  if(WBWindowDispatch(evt.window, (XEvent *)&evt) == (int)evt.window) // must return window ID
4412  {
4413  iMouseState = MouseState_LDRAG;
4414 
4415  if(wMouseCapture == None)
4416  {
4417  XGrabPointer(pDisplay, ((XButtonEvent *)pEvent)->window, 1,
4418  ButtonPressMask | ButtonReleaseMask | ButtonMotionMask | PointerMotionMask
4419  | EnterWindowMask | LeaveWindowMask,
4420  GrabModeAsync, // pointer mode
4421  GrabModeAsync, // keyboard mode
4422  None, None, CurrentTime);
4423 
4424  wMouseCapture = ((XButtonEvent *)pEvent)->window;
4425 
4426  // post a motion event to update pointer position
4427  evt.data.l[0] = WB_POINTER_MOVE;
4428  evt.data.l[3] = iX;
4429  evt.data.l[4] = iY;
4430 
4431  WBPostPriorityEvent(((XButtonEvent *)pEvent)->window, (XEvent *)&evt);
4432 
4433  iMouseX = ((XButtonEvent *)pEvent)->x; // keep track of starting drag position
4434  iMouseY = ((XButtonEvent *)pEvent)->y;
4435  }
4436  }
4437  else
4438  {
4439  iMouseState = MouseState_NONE; // TODO: check for drag support first
4440  }
4441  }
4442  }
4443 
4444  break;
4445 
4446  case MouseState_LDRAG:
4447  case MouseState_RDRAG:
4448  case MouseState_CDRAG:
4449 
4450  if(pEvent->type == ButtonPress || // someone pressed a different mouse button
4451  iMouseDragButtonState != iDragButtonState || // button state has changed
4452  iMouseModShiftCtrl != iModShiftCtrl) // modifier state change
4453  {
4454  WBMouseCancel(pDisplay, ((XButtonEvent *)pEvent)->window); // always call this
4455 #if 0
4456  XClientMessageEvent evt = {
4457  .type=ClientMessage,
4458  .serial=0,
4459  .send_event=0,
4460  .display=pDisplay,
4461  .window=((XButtonEvent *)pEvent)->window,
4462  .message_type=aWM_POINTER,
4463  .format=32
4464  };
4465 
4466  evt.data.l[0] = WB_POINTER_CANCEL;
4467  evt.data.l[1] = iMouseDragButtonState // send previous button state
4468  | (iButton4 ? 8 : 0)
4469  | (iButton5 ? 16 : 0);
4470  evt.data.l[2] = (iMouseModShiftCtrl & ShiftMask ? WB_KEYEVENT_SHIFT : 0) // send previous iACS
4471  | (iMouseModShiftCtrl & ControlMask ? WB_KEYEVENT_CTRL : 0)
4472  | (iMouseModShiftCtrl & Mod1Mask ? WB_KEYEVENT_ALT : 0);
4473 
4474  WBXlatCoordPoint(((XButtonEvent *)pEvent)->window, ((XButtonEvent *)pEvent)->x, ((XButtonEvent *)pEvent)->y,
4475  ((XButtonEvent *)pEvent)->window, &iX, &iY);
4476 
4477  evt.data.l[3] = iX; // TODO: should this be UN-TRANSLATED or SCREEN coordinates?
4478  evt.data.l[4] = iY; // should I indicate WHICH WINDOW this should be for?
4479 
4480  WBPostPriorityEvent(((XButtonEvent *)pEvent)->window, (XEvent *)&evt);
4481 
4482  iMouseState = MouseState_NONE; // canceled
4483 
4484  if(wMouseCapture != None)
4485  {
4486  // cancel mouse capture
4487 
4488  XUngrabPointer(pDisplay, CurrentTime);
4489  wMouseCapture = None;
4490  }
4491 #endif // 0
4492  }
4493  else if(pEvent->type == MotionNotify) // mods and buttons NOT changed
4494  {
4495  // modifiers still match (see above) so no need for cancelation here
4496 
4497  if(iButton1 && // for now rely on other code to cancel this if another button is pressed
4498  (iMouseX != ((XButtonEvent *)pEvent)->x ||
4499  iMouseY != ((XButtonEvent *)pEvent)->y))
4500  {
4501  XClientMessageEvent evt = {
4502  .type=ClientMessage,
4503  .serial=0,
4504  .send_event=0,
4505  .display=pDisplay,
4506  .window=((XButtonEvent *)pEvent)->window,
4507  .message_type=aWM_POINTER,
4508  .format=32
4509  };
4510 
4511  evt.data.l[0] = WB_POINTER_MOVE;
4512  evt.data.l[1] = iDragButtonState
4513  | (iButton4 ? 8 : 0)
4514  | (iButton5 ? 16 : 0);
4515  evt.data.l[2] = (iModShiftCtrl & ShiftMask ? WB_KEYEVENT_SHIFT : 0)
4516  | (iModShiftCtrl & ControlMask ? WB_KEYEVENT_CTRL : 0)
4517  | (iModShiftCtrl & Mod1Mask ? WB_KEYEVENT_ALT : 0);
4518 
4519  WBXlatCoordPoint(((XButtonEvent *)pEvent)->window, ((XButtonEvent *)pEvent)->x, ((XButtonEvent *)pEvent)->y,
4520  ((XButtonEvent *)pEvent)->window, &iX, &iY);
4521 
4522  evt.data.l[3] = iX; // TODO: should this be UN-TRANSLATED or SCREEN coordinates?
4523  evt.data.l[4] = iY; // should I indicate WHICH WINDOW this should be for?
4524 
4525  iMouseX = ((XButtonEvent *)pEvent)->x; // keep track of current drag position
4526  iMouseY = ((XButtonEvent *)pEvent)->y;
4527 
4528  WBPostPriorityEvent(((XButtonEvent *)pEvent)->window, (XEvent *)&evt);
4529  }
4530  }
4531  else if(pEvent->type == ButtonRelease) // TODO: check mouse button and key mask match
4532  {
4533  // handle drop event
4534 
4535  XClientMessageEvent evt = {
4536  .type=ClientMessage,
4537  .serial=0,
4538  .send_event=0,
4539  .display=pDisplay,
4540  .window=((XButtonEvent *)pEvent)->window,
4541  .message_type=aWM_POINTER,
4542  .format=32
4543  };
4544 
4545  evt.data.l[0] = WB_POINTER_DROP;
4546  evt.data.l[1] = iDragButtonState;
4547  evt.data.l[2] = (iModShiftCtrl & ShiftMask ? WB_KEYEVENT_SHIFT : 0)
4548  | (iModShiftCtrl & ControlMask ? WB_KEYEVENT_CTRL : 0)
4549  | (iModShiftCtrl & Mod1Mask ? WB_KEYEVENT_ALT : 0);
4550 
4551  WBXlatCoordPoint(((XButtonEvent *)pEvent)->window, ((XButtonEvent *)pEvent)->x, ((XButtonEvent *)pEvent)->y,
4552  ((XButtonEvent *)pEvent)->window, &iX, &iY);
4553 
4554  evt.data.l[3] = iX; // TODO: should this be UN-TRANSLATED or SCREEN coordinates?
4555  evt.data.l[4] = iY; // should I indicate WHICH WINDOW this should be for?
4556 
4557  WBPostPriorityEvent(((XButtonEvent *)pEvent)->window, (XEvent *)&evt);
4558 
4559  if(wMouseCapture != None)
4560  {
4561  // end the mouse capture
4562  XUngrabPointer(pDisplay, CurrentTime);
4563  wMouseCapture = None;
4564  }
4565 
4566  iMouseState = MouseState_NONE; // complete
4567  }
4568 
4569  break;
4570  }
4571  }
4572  else if(iMouseState != MouseState_NONE &&
4573  (pEvent->type == KeyPress ||
4574  pEvent->type == KeyRelease))
4575  {
4576  int iModShiftCtrl = ((XKeyEvent *)pEvent)->state & (ShiftMask | LockMask | ControlMask | Mod1Mask
4577  | Mod2Mask | Mod3Mask | Mod4Mask | Mod5Mask);
4578 
4579  int iKey = XLookupKeysym((XKeyEvent *)pEvent, 0); // always use '0' index for these
4580  //WBKeyEventProcessKey((XKeyEvent *)pEvent, NULL, NULL, NULL)
4581 
4582  // mouse state cancelation due to pressing or releasing a key AFTERWARDS
4583  // for now limit this to escape, space, enter, and one of the modifier keys
4584 
4585  if(iModShiftCtrl != iMouseModShiftCtrl ||
4586  (pEvent->type == KeyPress &&
4587  (iKey == XK_Escape || iKey == XK_Return || iKey == XK_KP_Enter)))
4588  {
4589  // cancel any mouse states due to pressing a key
4590 
4591  WBMouseCancel(pDisplay, ((XButtonEvent *)pEvent)->window);
4592 #if 0
4593  if(wMouseCapture != None)
4594  {
4595  // cancel mouse capture
4596  XUngrabPointer(pDisplay, CurrentTime);
4597  wMouseCapture = None;
4598  }
4599 
4600  iMouseState = MouseState_NONE; // complete
4601 #endif // 0
4602  }
4603  }
4604  }
4605 
4606  return iRval;
4607 }
4608 
4609 
4610 /**********************************************************************/
4611 /* */
4612 /* message dispatching functions */
4613 /* (see also 'WBInternalProcessExposeEvent', below) */
4614 /* */
4615 /**********************************************************************/
4616 
4617 int WBAppDispatch(XEvent *pEvent)
4618 {
4619 Atom aTemp;
4620 XEvent evtTemp;
4621 
4622  // internal-only messages [not hooked]
4623 
4624  if(pEvent->xany.type == ClientMessage
4625  && pEvent->xclient.message_type == aSET_FOCUS) // async assign focus (top level window only)
4626  {
4627  XWindowChanges xwc;
4628  Window wID = (Window)pEvent->xclient.data.l[0];
4629  Window wIDFrom = (Window)pEvent->xclient.data.l[1]; // window inadvertently getting focus
4630  Window wIDRoot = None;
4631  _WINDOW_ENTRY_ *pEntry = WBGetWindowEntry(wID);
4632  Display *pDisplay;
4633 
4634  if(pEntry && pEntry->pDisplay && pEvent->xany.display &&
4635  pEvent->xany.display != pEntry->pDisplay)
4636  {
4637  pEntry = NULL; // anticipate multiple displays with matching window ID's in this case...
4638  WB_ERROR_PRINT("ERROR - %s - display pointer mismatch\n", __FUNCTION__);
4639  }
4640 
4641  if(pEntry)
4642  {
4643  if(pEntry->pDisplay)
4644  {
4645  pDisplay = pEntry->pDisplay;
4646  }
4647  else
4648  {
4649  pDisplay = pDefaultDisplay;
4650  }
4651  }
4652  else if(pEvent->xany.display) // TODO: verify it's not different from that for pEntry
4653  {
4654  pDisplay = pEvent->xany.display;
4655  }
4656  else
4657  {
4658  pDisplay = pDefaultDisplay;
4659  }
4660 
4661 // fprintf(stderr, "** TEMPORARY - %s setting focus to %08xH\n", __FUNCTION__, (unsigned int)wID);
4662  // NOTE: if this is not a top level window, results may be unexpected
4663 
4664  // tell the WM to mark the desired window as "the active window"
4665  aTemp = XInternAtom (pEvent->xany.display ? pEvent->xany.display : pDefaultDisplay,
4666  "_NET_ACTIVE_WINDOW", False); /* global scope, must use XInternAtom */
4667 
4668  bzero(&evtTemp, sizeof(evtTemp));
4669 
4670  evtTemp.xany.type = ClientMessage;
4671  evtTemp.xany.send_event = True; // will send it, not post
4672  evtTemp.xany.display = pEvent->xany.display ? pEvent->xany.display : pDefaultDisplay;
4673  evtTemp.xany.window = wID; // in this case, the window to make active
4674  evtTemp.xany.serial = 0;
4675 
4676  __internal_GetParent(evtTemp.xany.display, wID, &wIDRoot); // send to 'wRoot'
4677 
4678  // client message params
4679  evtTemp.xclient.message_type = aTemp;
4680  evtTemp.xclient.format = 32;
4681  evtTemp.xclient.data.l[0] = 0; // "use old method" (should be suitable)
4682  evtTemp.xclient.data.l[1] = 0; // timestamp (zero for now)
4683  evtTemp.xclient.data.l[2] = wIDFrom; // window that was incorrectly set 'active'
4684 
4686  XSendEvent(pDisplay, wIDRoot, False,
4687  SubstructureRedirectMask | SubstructureNotifyMask,
4688  &evtTemp);
4690 
4691  WBSetInputFocus(wID); // set input focus to this window (after doing the above)
4692  // window managers that ignore _NET_ACTIVE_WINDOW should work OK with JUST
4693  // a call to XSetInputFocus. fluxbox works when you use XSetInputFocus
4694 
4695  bzero(&xwc, sizeof(xwc));
4696  xwc.stack_mode = Above;
4697 
4698  // bring window forward
4700 
4701  XConfigureWindow(pDisplay, wID, CWStackMode, &xwc);
4702  XMapWindow(pDisplay, wID);
4703 
4705 
4706  return 1; // handled
4707  }
4708 
4709  if(!pAppEventCallback || !pAppEventCallback(pEvent))
4710  {
4711  return WBAppDefault(pEvent);
4712  }
4713 
4714  return 0;
4715 }
4716 
4717 void WBDispatch(XEvent *pEvent)
4718 {
4719  // determine the window ID for the message, if applicable
4720 
4721 // if(pEvent->type == KeymapEvent)
4722 // {
4723 // }
4724  if(pEvent->xany.window == None) // the application
4725  {
4726  WBAppDispatch(pEvent);
4727  }
4728  else
4729  {
4730  WBWindowDispatch(pEvent->xany.window, pEvent);
4731  }
4732 
4733  __PeriodicWindowEntryCleanup(); // TODO: find a more intelligent way to make this work
4734 }
4735 
4736 
4737 int WBWindowDispatch(Window wID, XEvent *pEvent)
4738 {
4739  _WINDOW_ENTRY_ *pEntry = WBGetWindowEntry(wID);
4740  int iRval = 0, iSetFocus = 0;
4741 
4742  if(pEvent->type == ClientMessage)
4743  {
4745  "%s - client message, pEntry==%pH\n", __FUNCTION__, pEntry);
4746  }
4747 
4748  if(pEntry)
4749  {
4750  // detect resize, mouse, and keystrokes for menu
4751 
4752  if(pEntry->wIDMenu)
4753  {
4754  _WINDOW_ENTRY_ *pMenuEntry = WBGetWindowEntry(pEntry->wIDMenu);
4755 
4756  if(WB_UNLIKELY(!pMenuEntry))
4757  {
4758  WB_WARN_PRINT("%s - pMenuEntry is NULL, pEntry->wIDMenu = %d (%08xH)\n",
4759  __FUNCTION__, (int)pEntry->wIDMenu, (int)pEntry->wIDMenu);
4760  }
4761  else if(WB_UNLIKELY(WB_IS_WINDOW_DESTROYED(*pMenuEntry)))
4762  {
4763  // DO NOT dispatch messages to destroyed menus
4764 
4765  WB_WARN_PRINT("%s:%d - %d (%08xH) NOT mapped\n",
4766  __FUNCTION__, __LINE__, (int)wID, (int)wID);
4767 
4768  return 0; // NOT handled (should I change this?)
4769  }
4770  else if(pMenuEntry->pMenuCallback &&
4771  (pEvent->type == ConfigureNotify || // resize, reposition
4772  pEvent->type == EnterNotify ||
4773  pEvent->type == LeaveNotify ||
4774  pEvent->type == KeyPress ||
4775  pEvent->type == KeyRelease ||
4776  pEvent->type == ButtonPress ||
4777  pEvent->type == ButtonRelease ||
4778  pEvent->type == MotionNotify ||
4779  pEvent->type == ClientMessage))
4780  {
4781  // TODO: do I want to exclude 'Expose' and other event types ???
4782  // [this is the only part that calls the client's callback function]
4783 
4784  iRval = pMenuEntry->pMenuCallback(pMenuEntry->wID, pEvent);
4785 
4786  if(iRval)
4787  {
4788  return iRval;
4789  }
4790  }
4791  }
4792 
4793  if(pEvent->type == Expose)
4794  {
4795  WB_GEOM geom;
4796 
4797  if(WB_UNLIKELY(WB_IS_WINDOW_DESTROYED(*pEntry)))
4798  {
4799  // DO NOT dispatch messages to destroyed menus
4800 
4801  WB_WARN_PRINT("%s:%d - %d (%08xH) NOT mapped\n",
4802  __FUNCTION__, __LINE__, (int)wID, (int)wID);
4803 
4804  return 0; // NOT handled (should I change this?)
4805  }
4806 
4807  WBGetWindowGeom(wID, &geom);
4808 
4809  pEntry->width = geom.width; // update geometry for 'pEntry'
4810  pEntry->height = geom.height;
4811  pEntry->border = geom.border;
4812 
4813  iSetFocus = WB_CHECK_SET_FOCUS_ON_MAP(*pEntry);
4814  }
4815 
4816  if(pEvent->type == ReparentNotify && wID == pEvent->xreparent.window)
4817  {
4819  "%s - re-parent event, %d (%08xH), %d (%08xH)\n",
4820  __FUNCTION__, (int)wID, (int)wID,
4821  (int)pEvent->xreparent.parent, (int)pEvent->xreparent.parent);
4822 
4823  pEntry->wParent = pEvent->xreparent.parent;
4824  }
4825 
4826  if(pEntry->pCallback) // CAN be NULL, especially if there are stale events waiting to be sent
4827  {
4828  iRval = pEntry->pCallback(wID, pEvent);
4829 
4830  if(iSetFocus)
4831  {
4833  "%s:%d - %d (%08xH) mapped, setting focus\n",
4834  __FUNCTION__, __LINE__, (int)wID, (int)wID);
4835 
4836 // WB_ERROR_PRINT("TEMPORARY - %s calling XSetInputFocus on wID=%u (%08xH)\n", __FUNCTION__, (int)wID, (uint32_t)wID);
4837 
4839  XSetInputFocus(pEntry->pDisplay, wID, RevertToParent, CurrentTime);
4841  }
4842 
4843  if(iRval) // if my callback 'handled' me properly
4844  {
4845  if(pEvent->type != DestroyNotify || // not a destroy notification
4846  pEvent->xdestroywindow.window != wID) // destroyed window isn't me
4847  {
4848  if(pEvent->type == DestroyNotify)
4849  {
4850  WB_ERROR_PRINT("%s - UNEXPECTED - DestroyNotify for %d and message is for %d\n",
4851  __FUNCTION__, (int)pEvent->xdestroywindow.window, (int)wID);
4852  }
4853 
4854  // return NOW - no further processing
4855 
4856  return iRval;
4857  }
4858 
4859  // at this point, I had a DestroyNotify message for THIS window - flow through to next section
4860  }
4861  }
4862  else if(pEvent->type == DestroyNotify)
4863  {
4864  if(pEvent->xdestroywindow.window == wID)
4865  {
4866  // 'pEntry' is still valid, but SHOULD be marked 'to be destroyed'
4867  if(!WB_IS_WINDOW_DESTROYED(*pEntry)) // already destroyed? no need to say anything
4868  {
4869  // I am being destroyed and I have no callback
4870  WB_ERROR_PRINT("INFO: %s - DestroyNotify for wID %d (%s) and NO CALLBACK (this is sometimes expected)\n",
4871  __FUNCTION__, (int)wID, pEntry->szClassName);
4872  }
4873  }
4874  else
4875  {
4876  WB_ERROR_PRINT("ERROR: %s - (UNEXPECTED) DestroyNotify for %d and message is for %d (no callback)\n",
4877  __FUNCTION__, (int)pEvent->xdestroywindow.window, (int)wID);
4878  }
4879  }
4880  else if(iSetFocus) // still handle this, just in case
4881  {
4883  "%s:%d - %d (%08xH) mapped, setting focus\n",
4884  __FUNCTION__, __LINE__, (int)wID, (int)wID);
4885 
4886 // WB_ERROR_PRINT("TEMPORARY - %s calling XSetInputFocus on wID=%u (%08xH)\n", __FUNCTION__, (int)wID, (uint32_t)wID);
4887 
4889  XSetInputFocus(pEntry->pDisplay, wID, RevertToParent, CurrentTime);
4891  }
4892 
4893  // handle any DestroyNotify events at this point. A parent window may send these to child windows, even
4894  // after the window is actually destroyed and its callback NULL'd
4895  //
4896  // (NOTE: if I had a callback, I still handle destroy notifications for ME at this point)
4897 
4898  if(pEvent->type == DestroyNotify &&
4899  pEvent->xdestroywindow.window == wID) // I am being destroyed
4900  {
4901  // window destroyed, already called callback (if there is one)
4902 
4903  _WINDOW_ENTRY_ *pEntry2 = WBGetWindowEntry(wID);
4904 
4906  "%s - DestroyNotify for %d (%08xH)\n",
4907  __FUNCTION__, (int)wID, (int)wID);
4908 
4909  if(pEntry2 && !iRval) // must call 'Default'
4910  {
4911 // WB_ERROR_PRINT("TEMPORARY - doing default processing for DestroyNotify\n");
4912  WBDefault(wID, pEvent);
4913  }
4914 
4915  // if this window is the current 'application' window, set the 'quit' flag.
4916  if(wID == wIDApplication)
4917  {
4918  bQuitFlag = TRUE; // application window destroyed, application must exit
4919  }
4920 
4921  if(pEntry2 && pEntry2 == pEntry)
4922  {
4923  // window destruction means delete remaining events, then unregister the callback
4924  __WBDelWindowEvents(pEntry->pDisplay, wID);
4925 
4926  pEntry->iWindowState = WB_WINDOW_DESTROYED; // to flag "I am destroying it now"
4927 // WB_ERROR_PRINT(">>TEMPORARY - %s - marking window %d (%s) destroyed and unregistering callback\n", __FUNCTION__, (int)wID, pEntry->szClassName);
4928 
4929  WBUnregisterWindowCallback(wID); // NOTE: this assigns 'wIDApplication' to None if this was the application window
4930 
4931  return iRval;
4932  }
4933  else
4934  {
4935  WB_ERROR_PRINT("WARNING - %s - pEntry == %p, pEntry2 == %p\n", __FUNCTION__, pEntry, pEntry2);
4936  }
4937  }
4938  }
4939  else
4940  {
4941  WB_DEBUG_PRINT(DebugLevel_WARN/*Medium*/ | DebugSubSystem_Window | DebugSubSystem_Event /*| DebugSubSystem_Selection*/,
4942  "%s - Event %s for window %d (%08xH) not handled [no pEntry]\n",
4943  __FUNCTION__, WBEventName(pEvent->type), (int)wID, (int)wID);
4944  }
4945 
4946  // NOTE: I get here if the registered callback returns zero or isn't assigned
4947  // and it's either NOT a 'DestroyNotify' or there's no window entry
4948 
4949  if(!iRval)
4950  {
4951  iRval = WBDefault(wID, pEvent);
4952  }
4953  else
4954  {
4955  WBDefault(wID, pEvent); // do it anyway but don't use THAT return value
4956  }
4957 
4958  return iRval;
4959 }
4960 
4961 int WBAppDefault(XEvent *pEvent)
4962 {
4963  return 0; // not handled
4964 }
4965 
4966 int WBDefault(Window wID, XEvent *pEvent)
4967 {
4968  int i1;
4969  _WINDOW_ENTRY_ *pEntry = WBGetWindowEntry(wID);
4970  Display *pDisplay = pEvent->xany.display ? pEvent->xany.display
4971  : pEntry ? pEntry->pDisplay : pDefaultDisplay;
4972 
4973 
4974  if (pEvent->type == Expose && pEvent->xexpose.count == 0)
4975  {
4976  // for now, just erase the background using whatever background color is currently assigned
4977  if(pEntry && pEntry->hGC != None)
4978  {
4979  GC gc = WBBeginPaint(wID, &(pEvent->xexpose), NULL);
4980  if(gc != None)
4981  {
4982  WBClearWindow(wID, gc);
4983  WBEndPaint(wID, gc);
4984  }
4985  }
4986  else
4987  {
4988  WB_ERROR_PRINT("TEMPORARY: %s - window has NO assigned GC, calling XClearWindow\n", __FUNCTION__);
4989 
4990  XClearWindow(pDisplay, wID); // TODO: rather than erase background, see if I need to
4991  }
4992 
4993  return 1; // handled
4994  }
4995  else if(pEvent->type == ConfigureNotify && pEvent->xconfigure.window == wID)
4996  {
4998  "%s - window %08xH gets ConfigureNotify\n",
4999  __FUNCTION__, (unsigned int)wID);
5000 
5001  // see if it's a move, or a re-size. on re-size I have to re-paint and re-calculate
5002  // the various display thingies. But if I'm just moving it, no need.
5003 
5004  if(pEvent->xconfigure.width != pEntry->width ||
5005  pEvent->xconfigure.height != pEntry->height ||
5006  pEvent->xconfigure.border_width != pEntry->border)
5007  {
5008  if(!WB_IS_WINDOW_MAPPED(*pEntry) || // if I'm not mapped, just change the numbers and create a region
5009  !pEntry->width || !pEntry->height) // same if width/height was zero (but is no longer)
5010  {
5011  XRectangle xrct;
5012 
5013  // NOTE: I must do it THIS way because the window isn't mapped yet.
5014 
5015  pEntry->width = pEvent->xconfigure.width;
5016  pEntry->height = pEvent->xconfigure.height;
5017  pEntry->border = pEvent->xconfigure.border_width;
5018 
5019  if(pEntry->rgnClip != None)
5020  {
5021  XDestroyRegion(pEntry->rgnClip);
5022  }
5023 
5024  pEntry->rgnClip = XCreateRegion();
5025 
5026  if(pEntry->rgnClip &&
5027  pEntry->width && pEntry->height) // make sure non-zero
5028  {
5029  xrct.x = 0;//(short)pEntry->x;
5030  xrct.y = 0;//(short)pEntry->y;
5031  xrct.width = (unsigned short)pEntry->width;
5032  xrct.height = (unsigned short)pEntry->height;
5033 
5034  XUnionRectWithRegion(&xrct, pEntry->rgnClip, pEntry->rgnClip);
5035  }
5036  }
5037  else
5038  {
5039 // WB_ERROR_PRINT("TEMPORARY: %s - ConfigureNotify invalidating geometry\n", __FUNCTION__);
5040 
5041  WBInvalidateGeom(wID, NULL, 1); // for simplicity, just re-paint it - note, NOT saying "do it now"
5042  }
5043  }
5044 
5045  return 1; // handled
5046  }
5047  else if(pEvent->type == VisibilityNotify &&
5048  pEvent->xvisibility.window == wID &&
5049  pEvent->xvisibility.state != VisibilityFullyObscured)
5050  {
5051  // Focus and Z-order changes generate this. If my window's state changes to at least partially 'unobscured',
5052  // then the response is to invalidate the entire window so it re-paints.
5053 
5054  // TODO: see what was covering it up before, to limit the re-painting
5055 
5057  "%s - window %08xH gets VisibilityNotify, state=%d\n",
5058  __FUNCTION__, (unsigned int)pEvent->xvisibility.window, pEvent->xvisibility.state);
5059 
5060  WBInvalidateGeom(pEvent->xvisibility.window, NULL, 1); // TODO: determine what changed
5061 
5062  return 1; // handled
5063  }
5064  else if(pEvent->type == MapRequest &&
5065  (pEvent->xmaprequest.parent == wID || pEvent->xmaprequest.window == wID))
5066  {
5067  // XMapWindow and related can generate this. I want to see if it happens, ever.
5068 
5069  WB_ERROR_PRINT("TEMPORARY: %s - window %08xH gets MapRequest for %08xH\n",
5070  __FUNCTION__, (unsigned int)pEvent->xmaprequest.parent, (unsigned int)pEvent->xmaprequest.window);
5071  }
5072  else if(pEvent->type == ClientMessage)
5073  {
5074 // fprintf(stderr, "TEMPORARY: client message in WBDefault\n");
5075  if(pEvent->xclient.message_type == aWM_PROTOCOLS && pEvent->xclient.window == wID)
5076  {
5077  if(pEvent->xclient.data.l[0] == aWM_DELETE_WINDOW)
5078  {
5079  // If this is managed by the WM, I'll get this message in response to a request
5080  // to close the window. This includes clicking the 'x' button in the title bar
5081 
5082  XClientMessageEvent evt;
5083 
5085  "%s - WM_DELETE_WINDOW for %d (%08xH)\n",
5086  __FUNCTION__, (int)wID, (int)wID);
5087 
5088  WB_ERROR_PRINT("TEMPORARY: %s - WM_PROTOCOLS WM_DELETE_WINDOW, requests call to XDestroyWindow for %u (%08xH)\n", __FUNCTION__, (int)wID, (int)wID);
5089 
5090  // the default action is to FIRST query the window to see if it's ok to destroy it. if it is,
5091  // the window should THEN safely destroy all of its child windows, etc. before returning, so that
5092  // I don't end up with a pile of X11 errors.
5093 
5094  bzero(&evt, sizeof(evt));
5095  evt.type = ClientMessage;
5096  evt.display = pDisplay;
5097  evt.window = wID;
5098  evt.message_type = aQUERY_CLOSE; // QUERY_CLOSE request
5099  evt.format = 32;
5100  evt.data.l[0] = 1; // tell it to destroy contents if ok to close
5101 
5102  i1 = WBWindowDispatch(evt.window, (XEvent *)&evt);
5103 
5104  if(i1 <= 0) // ok to close the window (or there was an error, so close anyway)
5105  {
5106  if(i1 < 0)
5107  {
5108  WB_ERROR_PRINT("ERROR: %s - QUERY_CLOSE returns %d, destroying window %u (%08xH) anyway\n",
5109  __FUNCTION__, i1, (int)wID, (int)wID);
5110  }
5111  else
5112  {
5113  WB_ERROR_PRINT("INFO: %s - QUERY_CLOSE returns %d, ok to destroy window %u (%08xH)\n",
5114  __FUNCTION__, i1, (int)wID, (int)wID);
5115  }
5116 
5117  if(wID == wIDApplication) // if it's the "application window", set the quit flag
5118  {
5119 // WB_ERROR_PRINT("TEMPORARY: %s - setting 'quit' flag\n", __FUNCTION__);
5120 
5121  bQuitFlag = TRUE; // application window destroyed, application must exit
5122  }
5123 
5124  // window destruction means delete remaining events, then unregister the callback
5125  __WBDelWindowEvents(pEntry->pDisplay, wID);
5126 
5127  pEntry->iWindowState = WB_WINDOW_DESTROYED; // to flag "I am destroying it now"
5128 
5129  WBUnregisterWindowCallback(wID); // make sure this happens - FYI it assigns matching wIDApplication to 'None'
5130 
5131 // WBDestroyWindow(wID); // destroy the window (DO NOT CALL THIS, the application will stay running if it's the main frame)
5132 
5133  // default action is to destroy the window *NOW*
5135  XDestroyWindow(pDisplay, wID); // todo: wrap this in ???
5137  }
5138 
5139  return 1; // "handled"
5140  }
5141  }
5142  else if(pEvent->xclient.message_type == aWM_TIMER && pEvent->xclient.window == wID)
5143  {
5145  "%s - un-handled WM_TIMER for %d (%08xH)\n",
5146  __FUNCTION__, (int)wID, (int)wID);
5147  }
5148 #ifndef NO_DEBUG // uncomment this block to dump every event NOT handled
5149  else
5150  {
5152  "%s - un-handled ClientMessage for %d (%08xH)\n",
5153  __FUNCTION__, (int)wID, (int)wID);
5154 
5156  {
5157  WBDebugDumpEvent(pEvent);
5158  }
5159  }
5160 #endif // NO_DEBUG
5161  }
5162  else if(pEvent->type == KeyPress ||
5163  pEvent->type == KeyRelease)
5164  {
5166  "%s - key press/release event for %d (%08xH) in default handler\n",
5167  __FUNCTION__, (int)wID, (int)wID);
5168 
5169  if(pEvent->type == KeyPress /* || pEvent->type == KeyRelease */)
5170  {
5171  // generate a WM_CHAR event for unhandled 'keypress' events
5172  // this lets all windows have a chance at intercepting the
5173  // keypress event before a WM_CHAR is generated
5174 
5175  XClientMessageEvent evt;
5176  int cbData, iACS = 0;
5177 
5178  // post a high-priority message to myself to display the menu
5179 
5180  bzero(&evt, sizeof(evt));
5181  evt.type = ClientMessage;
5182  evt.display = pDisplay;
5183  evt.window = pEvent->xany.window;
5184  evt.message_type = aWM_CHAR; // WM_CHAR notification
5185  evt.format = 32;
5186 
5187  cbData = sizeof(evt.data) - 3 * sizeof(evt.data.l[0]);
5188  evt.data.l[0] = WBKeyEventProcessKey((XKeyEvent *)pEvent,
5189  (char *)&(evt.data.l[3]),
5190  &cbData, &iACS);
5191 
5192  evt.data.l[1] = iACS;
5193  evt.data.l[2] = cbData;
5194 
5195  // pressing or releasing shift, ctrl, alt, or 'meta' must not
5196  // generate an event. Fortunately these are all within a range
5197 
5198  if(evt.data.l[0] < XK_Shift_L || evt.data.l[0] > XK_Hyper_R)
5199  {
5200  // NOTE: l[0] is return value from WBKeyEventProcessKey
5201  // l[1] is iACS return value
5202  // l[2] is # of characters decoded into 'l[3]'
5203  // l[3..] is character decode buffer
5204 
5205  if(iACS & WB_KEYEVENT_KEYSYM)
5206  {
5207  // temporary debug
5209  "%s - generating WM_CHAR for KEYSYM %ld (%lxH) iACS=%xH window %d (%08xH)\n",
5210  __FUNCTION__, evt.data.l[0], evt.data.l[0], iACS,
5211  (int)pEvent->xany.window, (int)pEvent->xany.window);
5212  }
5213  else
5214  {
5215  // temporary debug
5217  "%s - generating WM_CHAR \"%.*s\" iACS=%xH window %d (%08xH)\n",
5218  __FUNCTION__, cbData, (char *)&(evt.data.l[3]), iACS,
5219  (int)pEvent->xany.window, (int)pEvent->xany.window);
5220  }
5221 
5222  WBPostPriorityEvent(pEvent->xany.window, (XEvent *)&evt);
5223  }
5224  }
5225 
5226  }
5227  else if(pEvent->type == DestroyNotify &&
5228  pEvent->xdestroywindow.window == wID)
5229  {
5230  _WINDOW_ENTRY_ *pEntry = WBGetWindowEntry(wID);
5231 
5232  if(pEntry && pEntry->wIDMenu)
5233  {
5234  Window wIDMenu = pEntry->wIDMenu;
5235  pEntry->wIDMenu = 0;
5236 
5237  // Look for any other occurences of this menu attached to any other window
5238  // TODO: make this more efficient, eh?
5239  // TODO: see if I even need this. I've not been sharing WBMenu resources at all...
5240 
5241  for(i1=0; i1 <= WINDOW_ENTRY_ARRAY_MAX; i1++)
5242  {
5243  if(sWBHashEntries[i1].wIDMenu == wIDMenu)
5244  break;
5245  }
5246 
5247  // am I the last guy using the menu?
5248  if(i1 > WINDOW_ENTRY_ARRAY_MAX)
5249  {
5250  WBDestroyWindow(wIDMenu);
5251  }
5252  }
5253  }
5254  else if(pEvent->type == SelectionRequest)
5255  {
5256  // NOTE: selection events are handled by the Clipboard worker thread. However, I can still (possibly)
5257  // get these things sent to me. The *POLITE* thing to do is respond to them with an error sent
5258  // back to the requestor. And so, that's what I do. And display it in on stderr in debug code...
5259 #ifndef NO_DEBUG
5260  char *p1 = pEvent->xselectionrequest.selection != None ? WBGetAtomName(pDisplay, pEvent->xselectionrequest.selection) : NULL;
5261  char *p2 = pEvent->xselectionrequest.target != None ? WBGetAtomName(pDisplay, pEvent->xselectionrequest.target) : NULL;
5262  char *p3 = pEvent->xselectionrequest.property != None ? WBGetAtomName(pDisplay, pEvent->xselectionrequest.property) : NULL;
5263 
5264  WB_ERROR_PRINT("TEMPORARY - %s - SelectionRequest wID=%d(%08xH) class=%s owner=%d requestor=%d selection=%s target=%s property=%s\n",
5265  __FUNCTION__,
5266  (int)wID, (int)wID,
5267  (const char *)(pEntry ? pEntry->szClassName : "UNK"),
5268  (int)pEvent->xselectionrequest.owner,
5269  (int)pEvent->xselectionrequest.requestor,
5270  p1 ? p1 : "NULL",
5271  p2 ? p2 : "NULL",
5272  p3 ? p3 : "NULL");
5273 
5274  if(p1)
5275  {
5276  WBFree(p1);
5277  }
5278  if(p2)
5279  {
5280  WBFree(p2);
5281  }
5282  if(p3)
5283  {
5284  WBFree(p3);
5285  }
5286 #endif // NO_DEBUG
5287 
5288  // the default rejects the request. Send a 'SelectionNotify' to indicate the failure
5289  // failing to send the SelectionNotify can REALLY screw things up for the requestor
5290 
5291  if(pEvent->xselectionrequest.owner == wID) // only if I'm the selection owner
5292  {
5293  XSelectionEvent evtN;
5294 
5295  memset(&evtN, 0, sizeof(evtN));
5296 
5297  evtN.type = SelectionNotify;
5298  evtN.send_event = True; // 'cause I'm going to use 'XSendEvent' to reply immediately
5299  evtN.requestor = pEvent->xselectionrequest.requestor;
5300  evtN.selection = pEvent->xselectionrequest.selection;
5301  evtN.target = pEvent->xselectionrequest.target;
5302  evtN.property = None; // to indicate failure
5303  evtN.time = pEvent->xselectionrequest.time; // same time as request (for now)
5304 
5306  XSendEvent(pDisplay, evtN.requestor, False, 0, (XEvent *)&evtN);
5308  }
5309  }
5310  else if(pEvent->type == SelectionClear)
5311  {
5312 #ifndef NO_DEBUG
5313  char *p1 = WBGetAtomName(pDisplay, pEvent->xselectionclear.selection);
5314 
5315  WB_ERROR_PRINT("TEMPORARY - %s - SelectionClear wID=%d(%08xH) class=%s window=%d selection=%s\n",
5316  __FUNCTION__,
5317  (int)wID, (int)wID,
5318  (const char *)(pEntry ? pEntry->szClassName : "UNK"),
5319  (int)pEvent->xselectionclear.window,
5320  p1 ? p1 : "NULL");
5321 
5322  if(p1)
5323  {
5324  WBFree(p1);
5325  }
5326 #endif // NO_DEBUG
5327 
5328  // the default rejects the request. No need to send a 'SelectionNotify'
5329  }
5330  else if(pEvent->type == SelectionNotify)
5331  {
5332 #ifndef NO_DEBUG
5333  char *p1 = pEvent->xselection.selection != None ? WBGetAtomName(pDisplay, pEvent->xselection.selection) : NULL;
5334  char *p2 = pEvent->xselection.target != None ? WBGetAtomName(pDisplay, pEvent->xselection.target) : NULL;
5335  char *p3 = pEvent->xselection.property != None ? WBGetAtomName(pDisplay, pEvent->xselection.property) : NULL;
5336 
5337  WB_ERROR_PRINT("TEMPORARY - %s - SelectionNotify wID=%d(%08xH) class=%s requestor=%d selection=%s target=%s property=%s\n",
5338  __FUNCTION__,
5339  (int)wID, (int)wID,
5340  (const char *)(pEntry ? pEntry->szClassName : "UNK"),
5341  (int)pEvent->xselection.requestor,
5342  p1 ? p1 : "NULL",
5343  p2 ? p2 : "NULL",
5344  p3 ? p3 : "NULL");
5345 
5346  if(p1)
5347  {
5348  WBFree(p1);
5349  }
5350  if(p2)
5351  {
5352  WBFree(p2);
5353  }
5354  if(p3)
5355  {
5356  WBFree(p3);
5357  }
5358 #endif // NO_DEBUG
5359  }
5360 //#ifndef NO_DEBUG // uncomment this block to dump every event NOT handled
5361 // else
5362 // {
5363 // WBDebugDumpEvent(pEvent);
5364 // }
5365 //#endif // NO_DEBUG
5366 
5367  return 0; // indicate 'not handled' for now
5368 }
5369 
5370 
5371 void WBProcessExposeEvent(XExposeEvent *pEvent)
5372 {
5373 XEvent xevt;
5374 
5375  // first, handle the expose event as-is
5376  WBInternalProcessExposeEvent(pEvent);
5377 
5378  // next, grab whatever expose events are still out there and combine them, too
5379 
5380 // XFlush(pEvent->display);
5381  XSync(pEvent->display, 0); // to grab whatever expose events might be out there
5382 
5383  while(!bQuitFlag && XCheckTypedWindowEvent(pEvent->display, pEvent->window, Expose, &xevt))
5384  {
5385  WBInternalProcessExposeEvent((XExposeEvent *)&xevt);
5386  }
5387 }
5388 
5389 
5390 void WBMouseCancel(Display *pDisplay, Window wID)
5391 {
5392 XClientMessageEvent evt;
5393 int iX, iY;
5394 
5395 
5396  if((iMouseState != MouseState_LDRAG &&
5397  iMouseState != MouseState_RDRAG &&
5398  iMouseState != MouseState_CDRAG &&
5399  iMouseState != MouseState_WAS_LCLICK &&
5400  iMouseState != MouseState_WAS_RCLICK &&
5401  iMouseState != MouseState_WAS_CCLICK) // expected states to do a 'Mouse Cancel' for
5402  || wMouseCapture == None)
5403  {
5404  if(wMouseCapture != None)
5405  {
5406  WB_ERROR_PRINT("ERROR: %s - incorrect mouse state: %d (%08xH)\n", __FUNCTION__, iMouseState, iMouseState);
5407 
5408  XUngrabPointer(pDisplay ? pDisplay : WBGetDefaultDisplay(), CurrentTime);
5409 
5410  wMouseCapture = None; // not captured
5411  }
5412  else if(iMouseState == MouseState_NONE)
5413  {
5414  return; // just ignore it. no harm, no foul. mouse not captured, state is 'None'
5415  }
5416  else if(iMouseState != MouseState_WAS_LCLICK &&
5417  iMouseState != MouseState_WAS_RCLICK &&
5418  iMouseState != MouseState_WAS_CCLICK) // the "was" states are ok here. 'mouse cancel' will not be sent, however
5419  {
5420  // any other mouse state is unexpected here. spit out a message
5421 
5422  WB_ERROR_PRINT("ERROR: %s - mouse not captured. mouse state: %d (%08xH) \n", __FUNCTION__, iMouseState, iMouseState);
5423  }
5424 
5425  // TODO: send notification for a 'WAS' state??
5426 
5427  iMouseState = MouseState_NONE; // by convention, do this (always) on mouse cancel
5428 
5429  return; // for now, just do this [no capture]
5430  }
5431 
5432  if(wID == None)
5433  {
5434  wID = wMouseCapture; // the window with the mouse capture (checked for 'None' previously)
5435  }
5436 
5437  if(!pDisplay)
5438  {
5439  pDisplay = WBGetWindowDisplay(wID);
5440  if(!pDisplay)
5441  {
5442  pDisplay = WBGetDefaultDisplay(); // desperately make it work
5443  }
5444  }
5445 
5446  bzero(&evt, sizeof(evt));
5447  evt.type=ClientMessage;
5448  evt.display=pDisplay;
5449  evt.window=wID;
5450  evt.message_type=aWM_POINTER;
5451  evt.format=32;
5452  evt.data.l[0] = WB_POINTER_CANCEL;
5453 
5454  evt.data.l[1] = iMouseDragButtonState;
5455  evt.data.l[2] = (iMouseModShiftCtrl & ShiftMask ? WB_KEYEVENT_SHIFT : 0)
5456  | (iMouseModShiftCtrl & ControlMask ? WB_KEYEVENT_CTRL : 0)
5457  | (iMouseModShiftCtrl & Mod1Mask ? WB_KEYEVENT_ALT : 0);
5458 
5459  WBXlatCoordPoint(wMouseCapture, iMouseX, iMouseY, wMouseCapture, &iX, &iY);
5460 
5461  evt.data.l[3] = iX;
5462  evt.data.l[4] = iY;
5463 
5464  WBPostPriorityEvent(wID, (XEvent *)&evt); // post the WM_POINTER 'WB_POINTER_CANCEL' event
5465 
5466  iMouseState = MouseState_NONE; // canceled
5467 
5468  XUngrabPointer(pDisplay, CurrentTime);
5469 
5470  wMouseCapture = None;
5471  iMouseModShiftCtrl = iMouseDragButtonState = 0;
5472 }
5473 
5474 
5475 
5476 int WBMapWindow(Display *pDisplay, Window wID)
5477 {
5478  _WINDOW_ENTRY_ *pEntry = WBGetWindowEntry(wID);
5479  int iRval, iSetFocus = 0;
5480 
5481  if(pEntry)
5482  {
5483  iSetFocus = WB_CHECK_SET_FOCUS_ON_MAP(*pEntry);
5484 
5485  pEntry->iWindowState = WB_WINDOW_MAPPED;
5486 
5487  if(!pEntry->pWMHints)
5488  {
5489  // if no window hints were configured, set some up
5490 
5491  if(!__internal_alloc_WMHints(pEntry))
5492  {
5494  XSetWMHints(pEntry->pDisplay, wID, pEntry->pWMHints);
5496  }
5497  }
5498  }
5499 
5501  iRval = XMapWindow(pDisplay, wID);
5503 
5504  if(iSetFocus)
5505  {
5507  "%s:%d - %d (%08xH) mapped, setting focus\n",
5508  __FUNCTION__, __LINE__, (int)wID, (int)wID);
5509 
5510 // WB_ERROR_PRINT("TEMPORARY - %s calling XSetInputFocus on wID=%u (%08xH)\n", __FUNCTION__, (int)wID, (uint32_t)wID);
5511 
5513  XSetInputFocus(pEntry->pDisplay, wID, RevertToParent, CurrentTime);
5515  }
5516 
5517  return iRval;
5518 }
5519 
5520 int WBMapRaised(Display *pDisplay, Window wID)
5521 {
5522  _WINDOW_ENTRY_ *pEntry = WBGetWindowEntry(wID);
5523 
5524  int iRval, iSetFocus = 0;
5525 
5526  if(pEntry)
5527  {
5528  iSetFocus = WB_CHECK_SET_FOCUS_ON_MAP(*pEntry);
5529 
5530  pEntry->iWindowState = WB_WINDOW_MAPPED;
5531 
5532  if(!pEntry->pWMHints)
5533  {
5534  // if no window hints were configured, set some up
5535 
5536  if(!__internal_alloc_WMHints(pEntry))
5537  {
5539  XSetWMHints(pEntry->pDisplay, wID, pEntry->pWMHints);
5541  }
5542  }
5543  }
5544 
5545  iRval = XMapRaised(pDisplay, wID);
5546 
5547  if(iSetFocus)
5548  {
5550  "%s:%d - %d (%08xH) mapped, setting focus\n",
5551  __FUNCTION__, __LINE__, (int)wID, (int)wID);
5552 
5553 // WB_ERROR_PRINT("TEMPORARY - %s calling XSetInputFocus on wID=%u (%08xH)\n", __FUNCTION__, (int)wID, (uint32_t)wID);
5554 
5556  XSetInputFocus(pEntry->pDisplay, wID, RevertToParent, CurrentTime);
5558  }
5559 
5560  return iRval;
5561 }
5562 
5563 int WBUnmapWindow(Display *pDisplay, Window wID)
5564 {
5565  _WINDOW_ENTRY_ *pEntry = WBGetWindowEntry(wID);
5566  int iRval;
5567 
5568  if(pEntry)
5569  {
5570  if(WB_IS_WINDOW_UNMAPPED(*pEntry) || WB_IS_WINDOW_DESTROYED(*pEntry))
5571  {
5572  return 0;
5573  }
5574 
5575  pEntry->iWindowState = WB_WINDOW_UNMAPPED; // aka 'unmapped'
5576  }
5577 
5579  iRval = XUnmapWindow(pDisplay, wID);
5581 
5582  // TODO: if window HAD focus, set it to the main app window?
5583 
5584  return iRval;
5585 }
5586 
5587 
5588 int WBIsMapped(Display *pDisplay, Window wID)
5589 {
5590  _WINDOW_ENTRY_ *pEntry = WBGetWindowEntry(wID);
5591 
5592  if(pEntry && WB_IS_WINDOW_MAPPED(*pEntry) && !WB_IS_WINDOW_DESTROYED(*pEntry))
5593  {
5594  return 1;
5595  }
5596 
5597  // TODO: handle windows NOT being tracked???
5598 
5599  return 0;
5600 }
5601 
5602 int WBIsValid(Display *pDisplay, Window wID)
5603 {
5604  _WINDOW_ENTRY_ *pEntry = WBGetWindowEntry(wID);
5605 
5606  if(pEntry && !WB_IS_WINDOW_DESTROYED(*pEntry))
5607  {
5608 // fprintf(stderr, "TEMPORARY - returning 'valid' for window %d\n", wID);
5609  return 1;
5610  }
5611 
5612  // TODO: handle windows NOT being tracked???
5613 
5614  return 0;
5615 }
5616 
5617 
5618 
5619 /**********************************************************************/
5620 /* */
5621 /* basic window property functions */
5622 /* */
5623 /**********************************************************************/
5624 
5625 static int __internal_alloc_WMHints(_WINDOW_ENTRY_ *pEntry)
5626 {
5627  if(pEntry)
5628  {
5629  if(pEntry->pWMHints)
5630  {
5631  return 0;
5632  }
5633  else
5634  {
5635  pEntry->pWMHints = XAllocWMHints();
5636  if(pEntry->pWMHints)
5637  {
5638  // set defaults
5639  pEntry->pWMHints->initial_state = NormalState;
5640  pEntry->pWMHints->input = True; // this means the WINDOW MANAGER should manage input focus (this is normal)
5641  pEntry->pWMHints->flags = InputHint | StateHint;
5642 
5643  return 0;
5644  }
5645  }
5646  }
5647 
5648  return -1;
5649 }
5650 
5651 Display * WBGetWindowDisplay(Window wID)
5652 {
5653  _WINDOW_ENTRY_ *pEntry = WBGetWindowEntry(wID);
5654 
5655  if(pEntry)
5656  {
5657  return pEntry->pDisplay;
5658  }
5659 
5660  return pDefaultDisplay;
5661 }
5662 
5663 void WBSetWindowIcon(Window wID, int idIcon)
5664 {
5665  _WINDOW_ENTRY_ *pEntry = WBGetWindowEntry(wID);
5666  Display *pDisp = pEntry ? pEntry->pDisplay : pDefaultDisplay;
5667 
5668  if(pEntry)
5669  {
5670  if(__internal_alloc_WMHints(pEntry))
5671  {
5672  return;
5673  }
5674 
5676  if(pEntry->pxIcon != None)
5677  {
5678  XFreePixmap(pDisp, pEntry->pxIcon);
5679  pEntry->pxIcon = None;
5680  }
5681 
5682  if(pEntry->pxMask != None)
5683  {
5684  XFreePixmap(pDisp, pEntry->pxMask);
5685  pEntry->pxMask = None;
5686  }
5688 
5689  pEntry->pxIcon = PXM_GetIconPixmap(idIcon, NULL, &(pEntry->pxMask));
5690 
5691  pEntry->pWMHints->flags |= IconPixmapHint; // (InputHint|StateHint|IconPixmapHint);
5692  if(pEntry->pxMask)
5693  pEntry->pWMHints->flags |= IconMaskHint;
5694  else
5695  pEntry->pWMHints->flags &= ~IconMaskHint;
5696 
5697  pEntry->pWMHints->icon_pixmap = pEntry->pxIcon;
5698  pEntry->pWMHints->icon_mask = pEntry->pxMask;
5699 
5701  XSetWMHints(pDisp, wID, pEntry->pWMHints);
5703  }
5704 }
5705 
5706 void WBSetWindowFontStruct(Window wID, XFontStruct *pFontStruct)
5707 {
5708  _WINDOW_ENTRY_ *pEntry = WBGetWindowEntry(wID);
5709 
5710  if(pEntry)
5711  {
5712  if(pEntry->pFontStruct && pEntry->pFontStruct != pDefaultFont) // must delete it
5713  {
5714  XFreeFont(pEntry->pDisplay, pEntry->pFontStruct);
5715  }
5716 
5717  if(pEntry->fontSet != None && pEntry->fontSet != fontsetDefault)
5718  {
5719  XFreeFontSet(pEntry->pDisplay, pEntry->fontSet);
5720  }
5721 
5722  pEntry->fontSet = None; // always, before I do the next part
5723 
5724  pEntry->pFontStruct = pFontStruct;
5725 
5726  if(pFontStruct)
5727  {
5728  pEntry->fontSet = WBFontSetFromFont(pEntry->pDisplay, pFontStruct);
5729  }
5730  }
5731 }
5732 
5733 void WBSetWindowFontSet(Window wID, XFontSet fontSet)
5734 {
5735  _WINDOW_ENTRY_ *pEntry = WBGetWindowEntry(wID);
5736 
5737  if(pEntry)
5738  {
5739  if(pEntry->pFontStruct && pEntry->pFontStruct != pDefaultFont) // must delete it
5740  {
5741  XFreeFont(pEntry->pDisplay, pEntry->pFontStruct);
5742  }
5743 
5744  if(pEntry->fontSet != None && pEntry->fontSet != fontsetDefault)
5745  {
5746  XFreeFontSet(pEntry->pDisplay, pEntry->fontSet);
5747  }
5748 
5749  pEntry->pFontStruct = NULL;
5750 
5751  if(fontSet != None)
5752  {
5753  pEntry->fontSet = fontSet;
5754  pEntry->pFontStruct = WBFontFromFontSet(pEntry->pDisplay, fontSet);
5755  }
5756  }
5757 }
5758 
5759 void WBCreateWindowDefaultGC(Window wID, unsigned long clrFG, unsigned long clrBG)
5760 {
5761  XGCValues gcv; /* Struct for creating GC */
5762 
5763  _WINDOW_ENTRY_ *pEntry = WBGetWindowEntry(wID);
5764 
5765  if(!pEntry)
5766  return;
5767 
5768  if(pEntry->hGC)
5769  {
5770  XFreeGC(pEntry->pDisplay, pEntry->hGC);
5771  }
5772 
5773  // the GC's font will be used with regular 'XDrawText' and 'XDrawString' calls
5774  // The associated Font Set must be queried separately for calls to XmbXXX or Xutf8XXX functions
5775 
5776  if(!pEntry->pFontStruct)
5777  {
5778  gcv.font = pDefaultFont->fid;
5779  }
5780  else
5781  {
5782  gcv.font = pEntry->pFontStruct->fid;
5783  }
5784 
5785  gcv.foreground = clrFG;
5786  gcv.background = clrBG;
5787 
5788  pEntry->hGC = XCreateGC(pEntry->pDisplay, wID, (GCFont | GCForeground | GCBackground), &gcv);
5789  pEntry->clrFG = clrFG;
5790  pEntry->clrBG = clrBG;
5791 }
5792 
5793 void WBSetWindowDefaultGC(Window wID, GC hGC)
5794 {
5795  _WINDOW_ENTRY_ *pEntry = WBGetWindowEntry(wID);
5796 
5797  if(pEntry)
5798  {
5799  if(pEntry->hGC)
5800  {
5801  XFreeGC(pEntry->pDisplay, pEntry->hGC);
5802  }
5803 
5804  pEntry->hGC = hGC;
5805  }
5806 }
5807 
5808 void WBSetWindowData(Window wID, int iIndex, void *pData)
5809 {
5810  _WINDOW_ENTRY_ *pEntry = WBGetWindowEntry(wID);
5811 
5812  if(pEntry && iIndex >= 0 && iIndex <= WINDOW_DATA_SIZE)
5813  {
5814  pEntry->aWindowData[iIndex] = pData;
5815  }
5816 }
5817 
5818 GC WBGetWindowDefaultGC(Window wID)
5819 {
5820  _WINDOW_ENTRY_ *pEntry = WBGetWindowEntry(wID);
5821 
5822  if(pEntry)
5823  {
5824  return(pEntry->hGC);
5825  }
5826 
5827  return(NULL);
5828 }
5829 
5830 GC WBGetWindowCopyGC(Window wID)
5831 {
5832  _WINDOW_ENTRY_ *pEntry = WBGetWindowEntry(wID);
5833  GC gcRval = 0;
5834 
5835  if(!pEntry)
5836  {
5837  return NULL;
5838  }
5839 
5840  gcRval = XCreateGC(pEntry->pDisplay, wID, 0, NULL);
5841  if(gcRval)
5842  {
5843  int i1 = XCopyGC(pEntry->pDisplay, pEntry->hGC, GCAll /*0x7fffff*/, gcRval); // 23 bits in the mask
5844  if(i1 != 0)
5845  { // NOTE: docs say that args 3 and 4 are reversed, but header says THIS way
5846  // I have to believe that the header file is correct
5847 
5848  WB_ERROR_PRINT("ERROR: %s - XCopyGC for window %d (%08xH) returns %d\n", __FUNCTION__, (int)wID, (int)wID, i1);
5849 
5850  XFreeGC(pEntry->pDisplay, gcRval);
5851  gcRval = 0;
5852  }
5853  }
5854 
5855  return(gcRval);
5856 }
5857 
5858 GC WBCopyDrawableGC(Display *pDisplay, Drawable dw, GC gcSrc)
5859 {
5860 GC gcRval;
5861 
5862  if(!pDisplay)
5863  {
5864  pDisplay = WBGetDefaultDisplay();
5865  }
5866 
5867  gcRval = XCreateGC(pDisplay, dw, 0, NULL);
5868 
5869  if(gcRval != None)
5870  {
5871  int i1 = XCopyGC(pDisplay, gcSrc, GCAll /*0x7fffff*/, gcRval); // 23 bits in the mask
5872 
5873  if(i1 > 1)// != 0) TODO: find out why it gives me that ridiculous error code 1
5874  { // NOTE: docs say that args 3 and 4 are reversed, but header says THIS way
5875  // I have to believe that the header file is correct
5876 
5877  WB_ERROR_PRINT("ERROR: %s - XCopyGC for Drawable %d (%08xH) returns %d\n", __FUNCTION__, (int)dw, (int)dw, i1);
5878 
5879  XFreeGC(pDisplay, gcRval);
5880  gcRval = None;
5881  }
5882  }
5883  else
5884  {
5885  WB_ERROR_PRINT("ERROR: %s - XCreateGC for Drawable %d (%08xH) returns NULL\n", __FUNCTION__, (int)dw, (int)dw);
5886  }
5887 
5888  return(gcRval);
5889 }
5890 
5891 XFontStruct * WBGetGCFont(Display *pDisplay, GC gc)
5892 {
5893 XGCValues val;
5894 Status ret;
5895 XFontStruct *pF, *pRval;
5896 
5897 
5898  if(!pDisplay)
5899  {
5900  pDisplay = WBGetDefaultDisplay();
5901  }
5902 
5904  ret = XGetGCValues(pDisplay, gc, GCFont, &val);
5906 
5907  if(!ret || val.font == None)
5908  {
5909  return WBCopyFont(WBGetDefaultFont());
5910  }
5911 
5912  pRval = NULL;
5913 
5915  pF = XQueryFont(pDisplay, val.font);
5916 
5917  if(pF)
5918  {
5919  unsigned long lName = 0;
5920  if(XGetFontProperty(pF, XA_FONT, &lName))
5921  {
5922  char *pName = WBGetAtomName(WBGetDefaultDisplay(), (Atom)lName);
5923  if(pName)
5924  {
5925  pRval = XLoadQueryFont(pDisplay, pName);
5926 
5927  WBFree(pName);
5928  }
5929  }
5930 
5931  XFreeFontInfo(NULL, pF, 1); // to JUST free "the info" (required for XQueryFont)
5932  // NOTE: do not assign NULL to pF here, it's a flag below
5933  }
5935 
5936  if(!pF)
5937  {
5938  return WBCopyFont(WBGetDefaultFont());
5939  }
5940 
5941  return pRval; // caller must delete this using XFreeFont()
5942 }
5943 
5944 
5945 unsigned long WBGetWindowFGColor(Window wID)
5946 {
5947  _WINDOW_ENTRY_ *pEntry = WBGetWindowEntry(wID);
5948 
5949  if(pEntry)
5950  {
5951  return(pEntry->clrFG);
5952  }
5953 
5954  return(0);
5955 }
5956 
5957 unsigned long WBGetWindowBGColor(Window wID)
5958 {
5959  _WINDOW_ENTRY_ *pEntry = WBGetWindowEntry(wID);
5960 
5961  if(pEntry)
5962  {
5963  return(pEntry->clrBG);
5964  }
5965 
5966  return(0);
5967 }
5968 
5969 unsigned long WBGetGCFGColor(Display *pDisplay, GC gc)
5970 {
5971 XGCValues val;
5972 Status ret;
5973 
5974 
5976  ret = XGetGCValues(pDisplay, gc, GCForeground, &val);
5978 
5979  if(!ret)
5980  {
5981  // use pre-defined color for BLACK
5982 
5983  return BlackPixel(pDisplay, DefaultScreen(pDisplay));
5984  }
5985 
5986  return val.foreground;
5987 }
5988 
5989 unsigned long WBGetGCBGColor(Display *pDisplay, GC gc)
5990 {
5991 XGCValues val;
5992 Status ret;
5993 
5994 
5996  ret = XGetGCValues(pDisplay, gc, GCBackground, &val);
5998 
5999  if(!ret)
6000  {
6001  // use pre-defined color for WHITE
6002 
6003  return WhitePixel(pDisplay, DefaultScreen(pDisplay));
6004  }
6005 
6006  return val.background;
6007 }
6008 
6009 
6010 void WBDefaultStandardColormap(Display *pDisplay, XStandardColormap *pMap)
6011 {
6012 XStandardColormap *pMaps = NULL;
6013 XStandardColormap cmap;
6014 Colormap cmDefault;
6015 unsigned long lTemp; //, lWhite = WhitePixel(pDisplay, DefaultScreen(pDisplay));
6016 XColor clrRed, clrGreen, clrBlue;
6017 int i1, nMaps = 0;
6018 
6019 
6020  if(!pMap)
6021  {
6022  return;
6023  }
6024 
6025  if(!pDisplay)
6026  {
6027  pDisplay = WBGetDefaultDisplay();
6028  }
6029 
6030  if(pDisplay == WBGetDefaultDisplay() &&
6031  bStandardColormap)
6032  {
6033  // use the cached structure. faster.
6034 
6035  memcpy(pMap, &cmapDefault, sizeof(cmapDefault));
6036 
6037  return;
6038  }
6039 
6040  bzero(&cmap, sizeof(cmap));
6041 
6042  cmDefault = DefaultColormap(pDisplay, DefaultScreen(pDisplay));
6043 
6044  // TRY to use the APIs to get this information. I mean, REALLY TRY.
6045  if(!XGetRGBColormaps(pDisplay, DefaultRootWindow(pDisplay), &pMaps, &nMaps, XA_RGB_DEFAULT_MAP) ||
6046  nMaps == 0)
6047  {
6048  if(pMaps)
6049  {
6050  XFree(pMaps);
6051  }
6052 
6053  pMaps = NULL;
6054 
6055  if(!XGetRGBColormaps(pDisplay, DefaultRootWindow(pDisplay), &pMaps, &nMaps, XA_RGB_BEST_MAP) ||
6056  nMaps == 0)
6057  {
6058  if(pMaps)
6059  {
6060  XFree(pMaps);
6061  }
6062 
6063  pMaps = NULL;
6064  }
6065  }
6066 
6067  if(pMaps) // meaning that the above 'thingy' actually worked (I never really see it work, though)
6068  {
6069  for(i1=0; i1 < nMaps; i1++)
6070  {
6071  if(pMaps[i1].colormap == cmDefault)
6072  {
6073  memcpy(&cmap, &(pMaps[i1]), sizeof(XStandardColormap));
6074  break;
6075  }
6076  }
6077 
6078  XFree(pMaps);
6079 
6080  cmap.killid = None; // make sure
6081  cmap.visualid = None;
6082 
6083  if(i1 < nMaps) // I broke out of the loop? I found one?
6084  {
6085  if(pDisplay == WBGetDefaultDisplay())
6086  {
6087  memcpy(&cmapDefault, &cmap, sizeof(cmap));
6088  }
6089 
6090  memcpy(pMap, &cmap, sizeof(cmap));
6091  return;
6092  }
6093  }
6094 
6095 // WB_ERROR_PRINT("TEMPORARY: %s no matching XStandardColormap found - creating\n", __FUNCTION__);
6096 
6097  // --------------------------------------------------------
6098  // DERIVE A COLOR MAP FROM RGB COLORS AND KNOWN INFORMATION
6099  // --------------------------------------------------------
6100 
6101  bzero(&clrRed, sizeof(clrRed));
6102  bzero(&clrGreen, sizeof(clrGreen));
6103  bzero(&clrBlue, sizeof(clrBlue));
6104 
6105  clrRed.red = 65535;
6106  clrGreen.green = 65535;
6107  clrBlue.blue = 65535;
6108  XAllocColor(pDisplay, cmDefault, &clrRed);
6109  XAllocColor(pDisplay, cmDefault, &clrGreen);
6110  XAllocColor(pDisplay, cmDefault, &clrBlue);
6111 
6112 // WB_ERROR_PRINT("TEMPORARY: %s red: %08lxH green: %08lxH blue: %08lxH\n", __FUNCTION__,
6113 // clrRed.pixel, clrGreen.pixel, clrBlue.pixel);
6114 
6115  // black is my 'base' pixel.
6116  cmap.base_pixel = BlackPixel(pDisplay, DefaultScreen(pDisplay));
6117 
6118  // next, 'nuke out' how the pixel multipliers work, using the Red, Green, and Blue 'alloc'd colors
6119  if(clrRed.pixel >= clrGreen.pixel && clrRed.pixel >= clrBlue.pixel)
6120  {
6121  if(clrGreen.pixel >= clrBlue.pixel)
6122  {
6123  cmap.blue_max = clrBlue.pixel - cmap.base_pixel;
6124  cmap.blue_mult = 1;
6125 
6126  cmap.green_mult = 1;
6127  while(cmap.green_mult < cmap.blue_max)
6128  {
6129  cmap.green_mult <<= 1;
6130  }
6131 
6132  lTemp = (clrGreen.pixel - cmap.base_pixel);
6133  cmap.green_max = lTemp / cmap.green_mult;
6134 
6135  cmap.red_mult = cmap.green_mult;
6136 
6137  while(cmap.red_mult < lTemp)
6138  {
6139  cmap.red_mult <<= 1;
6140  }
6141 
6142  cmap.red_max = (clrRed.pixel - cmap.base_pixel)
6143  / cmap.red_mult;
6144  }
6145  else
6146  {
6147  cmap.green_max = clrGreen.pixel - cmap.base_pixel;
6148  cmap.green_mult = 1;
6149 
6150  cmap.blue_mult = 1;
6151 
6152  while(cmap.blue_mult < cmap.green_max)
6153  {
6154  cmap.blue_mult <<= 1;
6155  }
6156 
6157  lTemp = (clrBlue.pixel - cmap.base_pixel);
6158  cmap.blue_max = lTemp / cmap.blue_mult;
6159 
6160  cmap.red_mult = cmap.blue_mult;
6161 
6162  while(cmap.red_mult < lTemp)
6163  {
6164  cmap.red_mult <<= 1;
6165  }
6166 
6167  cmap.red_max = (clrRed.pixel - cmap.base_pixel)
6168  / cmap.red_mult;
6169  }
6170  }
6171  else if(clrGreen.pixel >= clrRed.pixel && clrGreen.pixel >= clrBlue.pixel)
6172  {
6173  if(clrRed.pixel >= clrBlue.pixel)
6174  {
6175  cmap.blue_max = clrBlue.pixel - cmap.base_pixel;
6176  cmap.blue_mult = 1;
6177 
6178  cmap.red_mult = 1;
6179  while(cmap.red_mult < cmap.blue_max)
6180  {
6181  cmap.red_mult <<= 1;
6182  }
6183 
6184  lTemp = (clrRed.pixel - cmap.base_pixel);
6185  cmap.red_max = lTemp / cmap.red_mult;
6186 
6187  cmap.green_mult = cmap.red_mult;
6188 
6189  while(cmap.green_mult < lTemp)
6190  {
6191  cmap.green_mult <<= 1;
6192  }
6193 
6194  cmap.green_max = (clrGreen.pixel - cmap.base_pixel)
6195  / cmap.green_mult;
6196  }
6197  else
6198  {
6199  cmap.red_max = clrRed.pixel - cmap.base_pixel;
6200  cmap.red_mult = 1;
6201 
6202  cmap.blue_mult = 1;
6203 
6204  while(cmap.blue_mult < cmap.green_max)
6205  {
6206  cmap.blue_mult <<= 1;
6207  }
6208 
6209  lTemp = (clrBlue.pixel - cmap.base_pixel);
6210  cmap.blue_max = lTemp / cmap.blue_mult;
6211 
6212  cmap.green_mult = cmap.blue_mult;
6213 
6214  while(cmap.green_mult < lTemp)
6215  {
6216  cmap.green_mult <<= 1;
6217  }
6218 
6219  cmap.green_max = (clrGreen.pixel - cmap.base_pixel)
6220  / cmap.green_mult;
6221  }
6222  }
6223  else
6224  {
6225  if(clrRed.pixel >= clrGreen.pixel)
6226  {
6227  cmap.green_max = clrGreen.pixel - cmap.base_pixel;
6228  cmap.green_mult = 1;
6229 
6230  cmap.red_mult = 1;
6231  while(cmap.red_mult < cmap.green_max)
6232  {
6233  cmap.red_mult <<= 1;
6234  }
6235 
6236  lTemp = (clrRed.pixel - cmap.base_pixel);
6237  cmap.red_max = lTemp / cmap.red_mult;
6238 
6239  cmap.blue_mult = cmap.red_mult;
6240 
6241  while(cmap.blue_mult < lTemp)
6242  {
6243  cmap.blue_mult <<= 1;
6244  }
6245 
6246  cmap.blue_max = (clrBlue.pixel - cmap.base_pixel)
6247  / cmap.blue_mult;
6248  }
6249  else
6250  {
6251  cmap.red_max = clrRed.pixel - cmap.base_pixel;
6252  cmap.red_mult = 1;
6253 
6254  cmap.green_mult = 1;
6255  while(cmap.green_mult < cmap.red_max)
6256  {
6257  cmap.green_mult <<= 1;
6258  }
6259 
6260  lTemp = (clrGreen.pixel - cmap.base_pixel);
6261  cmap.green_max = lTemp / cmap.green_mult;
6262 
6263 
6264  cmap.blue_mult = cmap.green_mult;
6265 
6266  while(cmap.blue_mult < lTemp)
6267  {
6268  cmap.blue_mult <<= 1;
6269  }
6270 
6271  cmap.blue_max = (clrBlue.pixel - cmap.base_pixel)
6272  / cmap.blue_mult;
6273  }
6274  }
6275 
6276  XFreeColors(pDisplay, cmDefault, &clrRed.pixel, 1, 0);
6277  XFreeColors(pDisplay, cmDefault, &clrGreen.pixel, 1, 0);
6278  XFreeColors(pDisplay, cmDefault, &clrBlue.pixel, 1, 0);
6279 
6280 // WB_ERROR_PRINT("TEMPORARY: red: %u,%u grn: %u,%u blu: %u,%u\n",
6281 // (unsigned int)cmap.red_max, (unsigned int)cmap.red_mult,
6282 // (unsigned int)cmap.green_max, (unsigned int)cmap.green_mult,
6283 // (unsigned int)cmap.blue_max, (unsigned int)cmap.blue_mult);
6284 
6285  if(pDisplay == WBGetDefaultDisplay())
6286  {
6287  memcpy(&cmapDefault, &cmap, sizeof(cmap));
6288  }
6289 
6290  memcpy(pMap, &cmap, sizeof(cmap));
6291 }
6292 
6293 
6294 XFontStruct *WBGetWindowFontStruct(Window wID)
6295 {
6296  _WINDOW_ENTRY_ *pEntry = WBGetWindowEntry(wID);
6297 
6298  if(pEntry && pEntry->pFontStruct)
6299  {
6300  return(pEntry->pFontStruct);
6301  }
6302 
6303  if(!pDefaultFont)
6304  {
6305  WB_ERROR_PRINT("%s - default font is NULL\n", __FUNCTION__);
6306  }
6307 
6308  return(pDefaultFont); // use global font structure
6309 }
6310 
6311 XFontSet WBGetWindowFontSet(Window wID)
6312 {
6313  _WINDOW_ENTRY_ *pEntry = WBGetWindowEntry(wID);
6314 
6315  if(pEntry && pEntry->fontSet != None)
6316  {
6317  return(pEntry->fontSet);
6318  }
6319 
6320  if(fontsetDefault == None)
6321  {
6322  WB_ERROR_PRINT("%s - default font is NULL\n", __FUNCTION__);
6323  }
6324 
6325  return(fontsetDefault); // use global font set
6326 }
6327 
6328 void WBSetWindowClassName(Window wID, const char *szClassName)
6329 {
6330  _WINDOW_ENTRY_ *pEntry = WBGetWindowEntry(wID);
6331 
6332  if(pEntry)
6333  {
6334  pEntry->szClassName = szClassName;
6335  }
6336 }
6337 
6338 const char *WBGetWindowClassName(Window wID)
6339 {
6340  _WINDOW_ENTRY_ *pEntry = WBGetWindowEntry(wID);
6341 
6342  if(pEntry)
6343  {
6344  return(pEntry->szClassName);
6345  }
6346 
6347  return(NULL);
6348 }
6349 
6350 void *WBGetWindowData(Window wID, int iIndex)
6351 {
6352  _WINDOW_ENTRY_ *pEntry = WBGetWindowEntry(wID);
6353 
6354  if(pEntry && iIndex >= 0 && iIndex <= WINDOW_DATA_SIZE)
6355  {
6356  return(pEntry->aWindowData[iIndex]);
6357  }
6358 
6359  return(NULL);
6360 }
6361 
6362 // use this to set the cursor without assigning the 'current cursor'
6363 static void __InternalSetWindowCursor(Window wID, int idCursor)
6364 {
6365  _WINDOW_ENTRY_ *pEntry = WBGetWindowEntry(wID);
6366  Display *pDisplay = pEntry ? pEntry->pDisplay : pDefaultDisplay;
6367  Cursor curNew = idCursor != -1 ? XCreateFontCursor(pDisplay, idCursor) : None;
6368  XColor clrF, clrB;
6369 
6370  if(curNew == None && idCursor != -1)
6371  {
6372  WB_ERROR_PRINT("ERROR - %s - Cursor ID %d (%xH) returns 'None'\n", __FUNCTION__, idCursor, idCursor);
6373  }
6374 
6375  // this will work for *ANY* window (assuming default display for unmapped windows)
6376  // unfortunately it may cause a memory leak for windows that aren't mapped
6377  if(curNew != None)
6378  XDefineCursor(pDisplay, wID, curNew);
6379  else
6380  XUndefineCursor(pDisplay, wID);
6381 
6382  // and this will manage resources for internally 'known' windows
6383 
6384  if(pEntry)
6385  {
6386  if(pEntry->curRecent != None)
6387  XFreeCursor(pDisplay, pEntry->curRecent);
6388 
6389  pEntry->curRecent = curNew;
6390 
6391 #if 1
6392  if(curNew != None)
6393  {
6394  // TODO: use the default FG and BG colors of the window and
6395  // change the cursor colors accordingly. This should force
6396  // the cursor to change immediately
6397 
6398  // for now it's always white background, black foreground
6399 
6400  bzero(&clrF, sizeof(clrF));
6401  bzero(&clrB, sizeof(clrB));
6402  clrB.pixel = /*pEntry->clrFG*/ WhitePixel(pDisplay, DefaultScreen(pDisplay));
6403  clrF.pixel = /*pEntry->clrBG*/ BlackPixel(pDisplay, DefaultScreen(pDisplay));
6404  XQueryColor(pDisplay, DefaultColormap(pDisplay, DefaultScreen(pDisplay)), &clrF);
6405  XQueryColor(pDisplay, DefaultColormap(pDisplay, DefaultScreen(pDisplay)), &clrB);
6406 
6407  XRecolorCursor(pDisplay, curNew, &clrB, &clrF); // this will 'flash' it, kinda
6408  XRecolorCursor(pDisplay, curNew, &clrF, &clrB); // show NOW
6409  }
6410 #endif // 0
6411  }
6412 }
6413 
6414 void WBSetWindowDefaultCursor(Window wID, int idStandardCursor)
6415 {
6416  _WINDOW_ENTRY_ *pEntry = WBGetWindowEntry(wID);
6417 
6418  if(pEntry)
6419  {
6420  pEntry->idDefaultCursor = idStandardCursor;
6421 
6422  if(!pEntry->iWaitCursorCount) // if we're not currently displaying a wait cursor
6423  WBSetWindowCursor(wID, idStandardCursor); // change the cursor immediately to this one
6424  }
6425 }
6426 
6428 {
6429  _WINDOW_ENTRY_ *pEntry = WBGetWindowEntry(wID);
6430 
6431  if(pEntry)
6432  return pEntry->idDefaultCursor;
6433 
6434  return WB_DEFAULT_CURSOR; // the standard default cursor
6435 }
6436 
6437 void WBBeginWaitCursor(Window wID)
6438 {
6439  _WINDOW_ENTRY_ *pEntry = WBGetWindowEntry(wID);
6440  Window wIDRoot = 0, wIDParent = 0;
6441  Window *pwIDChildren = NULL;
6442  unsigned int u1, nChildren = 0;
6443 
6444  if(pEntry)
6445  {
6446  if(!(pEntry->iWaitCursorCount++))
6447  {
6448  __InternalSetWindowCursor(wID, WB_WAIT_CURSOR); // change the cursor immediately to the 'watch' cursor
6449 
6450  // also do the same for all of the child windows
6451 
6452  if(XQueryTree(pEntry->pDisplay, wID, &wIDRoot, &wIDParent, &pwIDChildren, &nChildren))
6453  {
6454  if(pwIDChildren)
6455  {
6456  for(u1=nChildren; u1; u1--) // go backwards
6457  {
6458  Window wKid = pwIDChildren[u1 - 1];
6459  _WINDOW_ENTRY_ *pE = WBGetWindowEntry(wKid);
6460 
6461  if(pE)
6462  {
6463  __InternalSetWindowCursor(wKid, WB_WAIT_CURSOR); // all must use the wait cursor now
6464  }
6465  }
6466 
6467  XFree(pwIDChildren);
6468  }
6469  }
6470  }
6471  }
6472  else
6473  {
6474  __InternalSetWindowCursor(wID, WB_WAIT_CURSOR); // change the cursor immediately to the 'watch' cursor
6475  }
6476 }
6477 
6478 void WBEndWaitCursor(Window wID)
6479 {
6480  _WINDOW_ENTRY_ *pEntry = WBGetWindowEntry(wID);
6481  Window wIDRoot = 0, wIDParent = 0;
6482  Window *pwIDChildren = NULL;
6483  unsigned int u1, nChildren = 0;
6484 
6485 
6486  if(pEntry)
6487  {
6488  if(pEntry->iWaitCursorCount <= 1)
6489  {
6490  pEntry->iWaitCursorCount = 0;
6491 // WBRestoreDefaultCursor(wID);
6492  __InternalSetWindowCursor(wID, pEntry->idCursor);
6493 
6494  // walk child windows, restore THEIR cursors also
6495 
6496  if(XQueryTree(pEntry->pDisplay, wID, &wIDRoot, &wIDParent, &pwIDChildren, &nChildren))
6497  {
6498  if(pwIDChildren)
6499  {
6500  for(u1=nChildren; u1; u1--) // go backwards
6501  {
6502  Window wKid = pwIDChildren[u1 - 1];
6503  _WINDOW_ENTRY_ *pE = WBGetWindowEntry(wKid);
6504 
6505  if(pE)
6506  {
6507  __InternalSetWindowCursor(wKid, pE->idCursor); // restore to what it was
6508  }
6509  }
6510 
6511  XFree(pwIDChildren);
6512  }
6513  }
6514  }
6515  else
6516  {
6517  pEntry->iWaitCursorCount--;
6518  }
6519  }
6520  else
6521  {
6522  __InternalSetWindowCursor(wID, -1); // change the cursor immediately to the 'watch' cursor
6523  }
6524 }
6525 
6526 void WBSetWindowCursor(Window wID, int idCursor)
6527 {
6528  _WINDOW_ENTRY_ *pEntry = WBGetWindowEntry(wID);
6529 
6530  if(pEntry)
6531  {
6532  pEntry->idCursor = idCursor;
6533  }
6534 
6535  __InternalSetWindowCursor(wID, idCursor);
6536 }
6537 
6538 void WBRestoreDefaultCursor(Window wID)
6539 {
6540  _WINDOW_ENTRY_ *pEntry = WBGetWindowEntry(wID);
6541 
6542  if(pEntry)
6543  {
6544  pEntry->idCursor = pEntry->idDefaultCursor;
6545  __InternalSetWindowCursor(wID, pEntry->idDefaultCursor); // restore the cursor immediately
6546  }
6547 }
6548 
6549 
6550 // read-only window properties
6551 
6552 void WBGetWindowGeom0(Window wID, WB_GEOM *pGeom) // absolute window geometry (from latest notification)
6553 {
6554  _WINDOW_ENTRY_ *pEntry = WBGetWindowEntry(wID);
6555 
6556  if(pGeom)
6557  {
6558  if(!pEntry || (!pEntry->geomAbsolute.width && !pEntry->geomAbsolute.height))
6559  {
6560  bzero(pGeom, sizeof(*pGeom));
6561 
6562  Window wIDTemp = wID;
6563 
6564  while(wIDTemp)
6565  {
6566  WB_GEOM gm;
6567  WBGetWindowGeom(wIDTemp, &gm);
6568 
6569  pGeom->x += gm.x;
6570  pGeom->y += gm.y;
6571 
6572  if(wIDTemp == wID)
6573  {
6574  pGeom->width = gm.width;
6575  pGeom->height = gm.height;
6576  pGeom->border = gm.border;
6577 
6579  "%s - geometry for window %d (%08xH) = %d,%d,%d,%d,%d\n",
6580  __FUNCTION__, (int)wID, (int)wID,
6581  gm.x, gm.y, gm.width, gm.height, gm.border);
6582  }
6583  else
6584  {
6586  "%s - geometry for parent window %d (%08xH) = %d,%d,%d,%d,%d\n",
6587  __FUNCTION__, (int)wIDTemp, (int)wIDTemp,
6588  gm.x, gm.y, gm.width, gm.height, gm.border);
6589  }
6590 
6591  wIDTemp = WBGetParentWindow(wIDTemp);
6592  }
6593  }
6594  else
6595  {
6596  memcpy(pGeom, &(pEntry->geomAbsolute), sizeof(*pGeom));
6597  }
6598  }
6599 }
6600 
6601 void WBGetWindowGeom(Window wID, WB_GEOM *pGeom)
6602 {
6603  _WINDOW_ENTRY_ *pEntry = WBGetWindowEntry(wID);
6604  Display *pDisp = pEntry ? pEntry->pDisplay : pDefaultDisplay;
6605  Window winRoot = None;
6606  unsigned int uiDepth = 0;
6607 
6608  if(!pGeom)
6609  {
6610  return;
6611  }
6612 
6613 // bzero(&xwa, sizeof(xwa));
6614  bzero(pGeom, sizeof(*pGeom));
6615 
6616  if(WB_LIKELY(pEntry) &&
6617  WB_UNLIKELY(WB_IS_WINDOW_DESTROYED(*pEntry) || WB_IS_WINDOW_BEING_DESTROYED(*pEntry)))
6618  {
6619  WB_WARN_PRINT("%s:%d - %d (%08xH) NOT mapped\n",
6620  __FUNCTION__, __LINE__, (int)wID, (int)wID);
6621  return;
6622  }
6623 
6624  if(!pEntry)
6625  {
6627  "%s:%d - pEntry is NULL, %d (%08xH) may NOT be mapped\n",
6628  __FUNCTION__, __LINE__, (int)wID, (int)wID);
6629  }
6630  else if(!WB_IS_WINDOW_MAPPED(*pEntry) &&
6631  pEntry->geomAbsolute.width > 0 && pEntry->geomAbsolute.height > 0)
6632  {
6633  // if not mapped, return the best guess geometry
6634 
6635  pGeom->x = 0; // pEntry->geomAbsolute.x;
6636  pGeom->y = 0; // pEntry->geomAbsolute.y;
6637  pGeom->width = pEntry->geomAbsolute.width;
6638  pGeom->height = pEntry->geomAbsolute.height;
6639  pGeom->border = pEntry->geomAbsolute.border;
6640 
6641  WB_ERROR_PRINT("TEMPORARY: %s - unmapped window geometry %d, %d, %d, %d\n",
6642  __FUNCTION__, pGeom->x, pGeom->y, pGeom->width, pGeom->height);
6643 
6644  return;
6645  }
6646 // else if(!WB_IS_WINDOW_MAPPED(*pEntry))
6647 // {
6648 // WB_ERROR_PRINT("TEMPORARY: %s - unmapped window, no 'absolute' GEOM available (pre 'X' calls)\n", __FUNCTION__);
6649 // }
6650 
6652  XSync(pDisp, 0);
6653  XGetGeometry(pDisp, wID, &winRoot,
6654  &(pGeom->x), &(pGeom->y),
6655  &(pGeom->width), &(pGeom->height),
6656  &(pGeom->border), &uiDepth);
6658 
6659 // if(pEntry && !WB_IS_WINDOW_MAPPED(*pEntry))
6660 // {
6661 // WB_ERROR_PRINT("TEMPORARY: %s - unmapped window, no 'absolute' GEOM available (post 'X' calls)\n", __FUNCTION__);
6662 // }
6663 
6664 //fprintf(stderr, "XGetGeometry: window %08xH %d, %d, %d, %d\n",
6665 // wID, pGeom->x, pGeom->y, pGeom->width, pGeom->height);
6666 
6667 #if 0 /* 1 */
6668  {
6669  XWindowAttributes xwa; /* getting size of window, etc. */
6670  memset(&xwa, 0, sizeof(xwa));
6671  if(XGetWindowAttributes(pDisp, winRoot, &xwa))
6672  {
6674  "%s - window %08xH %d, %d, %d, %d\n",
6675  __FUNCTION__, winRoot, xwa.x, xwa.y, xwa.width, xwa.height);
6676 // pGeom->x = xwa.x;
6677 // pGeom->y = xwa.y;
6678 // pGeom->width = xwa.width;
6679 // pGeom->height = xwa.height;
6680 // pGeom->border = xwa.border_width;
6681  }
6682  }
6683 #endif // 0,1
6684 }
6685 
6686 void WBGetWindowGeom2(Window wID, WB_GEOM *pGeom)
6687 {
6688  _WINDOW_ENTRY_ *pEntry = WBGetWindowEntry(wID);
6689  Display *pDisp = pEntry ? pEntry->pDisplay : pDefaultDisplay;
6690  Window winRoot = 0, wParent;
6691  unsigned int uiDepth = 0;
6692  WB_GEOM geom;
6693 
6694  bzero(pGeom, sizeof(*pGeom));
6695 
6696  if(WB_LIKELY(pEntry) &&
6697  WB_UNLIKELY(WB_IS_WINDOW_DESTROYED(*pEntry) || WB_IS_WINDOW_BEING_DESTROYED(*pEntry)))
6698  {
6699  WB_WARN_PRINT("%s:%d - %d (%08xH) NOT mapped\n",
6700  __FUNCTION__, __LINE__, (int)wID, (int)wID);
6701  return;
6702  }
6703  else if(!WB_IS_WINDOW_MAPPED(*pEntry) &&
6704  pEntry->geomAbsolute.width > 0 && pEntry->geomAbsolute.height > 0)
6705  {
6706  // if not mapped, return the best guess geometry
6707 
6708  pGeom->x = pEntry->geomAbsolute.x;
6709  pGeom->y = pEntry->geomAbsolute.y;
6710  pGeom->width = pEntry->geomAbsolute.width;
6711  pGeom->height = pEntry->geomAbsolute.height;
6712  pGeom->border = pEntry->geomAbsolute.border;
6713 
6714  WB_ERROR_PRINT("TEMPORARY: %s - unmapped window geometry %d, %d, %d, %d\n",
6715  __FUNCTION__, pGeom->x, pGeom->y, pGeom->width, pGeom->height);
6716 
6717  return;
6718  }
6719 // else if(!WB_IS_WINDOW_MAPPED(*pEntry))
6720 // {
6721 // WB_ERROR_PRINT("TEMPORARY: %s - unmapped window, no 'absolute' GEOM available\n", __FUNCTION__);
6722 // }
6723 
6724 
6726  XGetGeometry(pDisp, wID, &winRoot,
6727  &(pGeom->x), &(pGeom->y),
6728  &(pGeom->width), &(pGeom->height),
6729  &(pGeom->border), &uiDepth);
6731 
6733  "%s - geometry for window %d (%08xH) = %d,%d,%d,%d,%d\n",
6734  __FUNCTION__, (int)wID, (int)wID,
6735  pGeom->x, pGeom->y, pGeom->width, pGeom->height, pGeom->border);
6736 
6737  wParent = WBGetParentWindow(wID);
6738 
6739  while(wParent > 0 && winRoot != wParent)
6740  {
6741  _WINDOW_ENTRY_ *pParentEntry = WBGetWindowEntry(wParent);
6742 
6743  if(WB_LIKELY(pParentEntry) &&
6744  WB_UNLIKELY(WB_IS_WINDOW_DESTROYED(*pParentEntry) || WB_IS_WINDOW_BEING_DESTROYED(*pParentEntry)))
6745  {
6746  WB_WARN_PRINT("%s:%d - %d (%08xH) NOT mapped\n",
6747  __FUNCTION__, __LINE__, (int)wID, (int)wID);
6748  continue;
6749  }
6750 
6751  bzero(&geom, sizeof(geom));
6752 
6754  XGetGeometry(pDisp, wParent, &winRoot, // assumes parent windows have same display, which is reasonable
6755  &geom.x, &geom.y,
6756  &geom.width, &geom.height,
6757  &geom.border, &uiDepth);
6759 
6761  "%s - geometry for parent window %d (%08xH) = %d,%d,%d,%d,%d\n",
6762  __FUNCTION__, (int)wParent, (int)wParent,
6763  geom.x, geom.y, geom.width, geom.height, geom.border);
6764 
6765  pGeom->x += geom.x;
6766  pGeom->y += geom.y;
6767 
6768  wParent = WBGetParentWindow(wParent);
6769  }
6770 }
6771 
6772 void WBGetWindowRect(Window wID, WB_RECT *pRect)
6773 {
6774  WB_GEOM geom;
6775 
6776  if(!pRect)
6777  return;
6778 
6779  WBGetWindowGeom(wID, &geom);
6780 
6781  pRect->left = geom.x; // + geom.border;
6782  pRect->top = geom.y; // + geom.border; // TODO: verify
6783  pRect->right = pRect->left + geom.width + 2 * geom.border;
6784  pRect->bottom = pRect->top + geom.height + 2 * geom.border; // TODO: verify
6785 }
6786 
6787 void WBGetClientRect(Window wID, WB_RECT *pRect)
6788 {
6789  WB_GEOM geom;
6790 
6791  if(!pRect)
6792  return;
6793 
6794  WBGetWindowGeom(wID, &geom);
6795 
6796  // NOTE: GEOM 'border' isn't correct unless I'm trying to calculate
6797  // the size of the border surrounding a window. the client rect
6798  // will always have 0,0 as its upper left corner
6799 
6800  pRect->left = 0;// geom.border; // 0
6801  pRect->top = 0; // geom.border; // 0
6802  pRect->right = geom.width;
6803  pRect->bottom = geom.height;
6804 }
6805 
6806 void WBXlatCoordPoint(Window wIDSrc, int iXSrc, int iYSrc, Window wIDDest, int *piXDest, int *piYDest)
6807 {
6808 WB_GEOM gOrig, gXlat;
6809 
6810  if(wIDSrc == wIDDest)
6811  {
6812  if(piXDest)
6813  *piXDest = iXSrc;
6814  if(piYDest)
6815  *piYDest = iYSrc;
6816  return;
6817  }
6818 
6819  if(wIDSrc != 0) // not "root window"
6820  WBGetWindowGeom0(wIDSrc, &gOrig);
6821  else
6822  bzero(&gOrig, sizeof(gOrig));
6823 
6824  if(wIDDest != 0)
6825  WBGetWindowGeom0(wIDDest, &gXlat);
6826  else
6827  bzero(&gXlat, sizeof(gXlat));
6828 
6829 //WB_WARN_PRINT("TEMPORARY: xlat %d,%d via %d,%d %d,%d\n",
6830 // iXSrc, iYSrc, gOrig.x, gOrig.y, gXlat.x, gXlat.y);
6831 
6832  if(piXDest)
6833  *piXDest = iXSrc + gOrig.x // xlat relative to absolute position
6834  - gXlat.x; // new relative position from absolute position
6835 
6836  if(piYDest)
6837  *piYDest = iYSrc + gOrig.y
6838  - gXlat.y;
6839 }
6840 
6841 void WBXlatCoordGeom(Window wIDSrc, const WB_GEOM *pGeomSrc, Window wIDDest, WB_GEOM *pGeomDest)
6842 {
6843 WB_GEOM gOrig, gXlat;
6844 
6845  if(!pGeomSrc || !pGeomDest)
6846  return;
6847 
6848  if(wIDSrc == wIDDest)
6849  {
6850  memcpy(pGeomDest, pGeomSrc, sizeof(*pGeomDest));
6851  return;
6852  }
6853 
6854  if(wIDSrc != 0) // not "root window"
6855  WBGetWindowGeom(wIDSrc, &gOrig);
6856  else
6857  bzero(&gOrig, sizeof(gOrig));
6858 
6859  if(wIDDest != 0)
6860  WBGetWindowGeom(wIDDest, &gXlat);
6861  else
6862  bzero(&gXlat, sizeof(gXlat));
6863 
6864  pGeomDest->x = pGeomSrc->x + gOrig.x - gXlat.x;
6865  pGeomDest->y = pGeomSrc->y + gOrig.y - gXlat.y;
6866  pGeomDest->width = pGeomSrc->width;
6867  pGeomDest->height = pGeomSrc->height;
6868  pGeomDest->border = pGeomSrc->border;
6869 }
6870 
6871 void WBXlatCoordRect(Window wIDSrc, const WB_RECT *pRectSrc, Window wIDDest, WB_RECT *pRectDest)
6872 {
6873 WB_GEOM geomSrc, geomDest;
6874 
6875  if(!pRectSrc || !pRectDest)
6876  return;
6877 
6878  if(wIDSrc == wIDDest)
6879  {
6880  memcpy(pRectDest, pRectSrc, sizeof(*pRectDest));
6881  return;
6882  }
6883 
6884  geomSrc.x = pRectSrc->left;
6885  geomSrc.y = pRectSrc->top;
6886  geomSrc.width = pRectSrc->right - geomSrc.x;
6887  geomSrc.height = pRectSrc->bottom - geomSrc.y;
6888  geomSrc.border = 0;
6889 
6890  WBXlatCoordGeom(wIDSrc, &geomSrc, wIDDest, &geomDest);
6891 
6892  pRectDest->left = geomDest.x;
6893  pRectDest->right = geomDest.y;
6894  pRectDest->right = geomDest.width + geomDest.x;
6895  pRectDest->bottom = geomDest.y + geomDest.height;
6896 }
6897 
6898 int WBPointInWindow(Window wIDRef, int iX, int iY, Window wIDQuery)
6899 {
6900 WB_RECT rctDest;
6901 
6902  if(wIDRef != wIDQuery)
6903  WBXlatCoordPoint(wIDRef, iX, iY, wIDQuery, &iX, &iY);
6904 
6905  WBGetWindowRect(wIDQuery, &rctDest);
6906 
6907  return(iX >= rctDest.left && iX < rctDest.right
6908  && iY >= rctDest.top && iY < rctDest.bottom);
6909 }
6910 
6911 // keyboard mappings
6912 
6913 int WBKeyEventProcessKey(const XKeyEvent *pEvent, char *pBuf, int *pcbLen, int *piAltCtrlShift)
6914 {
6915 KeySym ks;
6916 int cRval = 0, cbLen, cbLen0 = pcbLen ? *pcbLen : 1;
6917 int iACSMask = ShiftMask | ControlMask | Mod1Mask /* | Mod2Mask | Mod3Mask | Mod4Mask | Mod5Mask */;
6918 int bKeySym = 0;
6919 
6920  // extract as much information from this keystroke as possible.
6921 
6922  iACSMask &= ((XKeyEvent *)pEvent)->state;
6923 
6924  if(piAltCtrlShift)
6925  {
6926  *piAltCtrlShift = 0;
6927  }
6928 
6929  if(!pEvent)
6930  {
6931  return -1;
6932  }
6933 
6934  if(pBuf)
6935  {
6936  cbLen = XLookupString((XKeyEvent *)pEvent, pBuf, cbLen0, &ks, NULL);
6937 
6938  if((int)ks == XK_Delete || (int)ks == XK_KP_Delete)
6939  {
6940  cbLen = 0; // force this for the delete key - sometimes it translates
6941  }
6942 
6943  // TODO: force other keys to use keycodes? maybe backspace, escape, ???
6944 
6945  if(cbLen <= 0)
6946  {
6947  *pBuf = 0;
6948  bKeySym = 1;
6949  cbLen = 0; // just in case
6950  cRval = (int)ks; // return the keysym code
6951  }
6952  else
6953  {
6954  if(cbLen < cbLen0)
6955  {
6956  pBuf[cbLen] = 0;
6957  }
6958 
6959  cRval = *pBuf; // always (for now)
6960  }
6961  if(pcbLen)
6962  {
6963  *pcbLen = cbLen;
6964  }
6965  }
6966  else
6967  {
6968  int tbuf[8]; // note: must be int aligned, that's why
6969 
6970  cbLen = XLookupString((XKeyEvent *)pEvent, (char *)tbuf, sizeof(tbuf) - 1, &ks, NULL);
6971 
6972  if(pcbLen)
6973  {
6974  *pcbLen = cbLen; // calculates actual length this way
6975  }
6976 
6977  if(cbLen <= 0)
6978  {
6979  cRval = ks; // use the scan code
6980  bKeySym = 1;
6981  }
6982  else if(cbLen == 1)
6983  {
6984  cRval = *((unsigned char *)tbuf);
6985  }
6986  else if(cbLen == 2)
6987  {
6988  cRval = *((unsigned short *)tbuf);
6989  }
6990  else if(cbLen == 4)
6991  {
6992  cRval = *((unsigned int *)tbuf);
6993  }
6994  else
6995  {
6996  cRval = *((unsigned char *)tbuf); // for now
6997  }
6998  }
6999 
7000  if(piAltCtrlShift)
7001  {
7002  *piAltCtrlShift = (bKeySym ? WB_KEYEVENT_KEYSYM : 0)
7003  | ((iACSMask & Mod1Mask) ? WB_KEYEVENT_ALT : 0)
7004  | ((iACSMask & ControlMask) ? WB_KEYEVENT_CTRL : 0)
7005  | ((iACSMask & ShiftMask) ? WB_KEYEVENT_SHIFT : 0);
7006  }
7007 
7008  return cRval;
7009 }
7010 
7011 
7012 
7013 // parent-child relationships
7014 
7015 static Window __internal_GetParent(Display *pDisplay, Window wID, Window *pwRoot)
7016 {
7017 Window wIDRoot = 0, wIDParent = 0;
7018 Window *pwIDChildren = NULL;
7019 unsigned int nChildren = 0;
7020 
7021  // TODO: if parent is desktop, return 'None' ?
7022 
7023  if(XQueryTree(pDisplay, wID, &wIDRoot, &wIDParent, &pwIDChildren, &nChildren))
7024  {
7025  if(pwIDChildren)
7026  XFree(pwIDChildren);
7027  }
7028 
7029  if(pwRoot)
7030  {
7031  *pwRoot = wIDRoot;
7032  }
7033 
7034  return wIDParent;
7035 }
7036 
7037 Window WBGetParentWindow(Window wID)
7038 {
7039  _WINDOW_ENTRY_ *pEntry = WBGetWindowEntry(wID);
7040  Display *pDisplay = pEntry ? pEntry->pDisplay : pDefaultDisplay;
7041  Window wIDRoot = 0, wIDParent = 0;
7042  Window *pwIDChildren = NULL;
7043  unsigned int nChildren = 0;
7044 
7045  if(pEntry && pEntry->wParent > 0)
7046  {
7047  return pEntry->wParent;
7048  }
7049 
7050  if(XQueryTree(pDisplay, wID, &wIDRoot, &wIDParent, &pwIDChildren, &nChildren))
7051  {
7052  if(pwIDChildren)
7053  XFree(pwIDChildren);
7054  }
7055 
7056  if(wIDParent > 0 && pEntry)
7057  {
7058  pEntry->wParent = wIDParent; // cache it
7059  }
7060 
7061  return wIDParent;