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