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-2019 by Bob Frazier (aka 'Big Bad Bombastic Bob')
17 
18  DISCLAIMER: The X11workbench application and toolkit software are supplied
19  'as-is', with no warranties, either implied or explicit.
20  Any claims to alleged functionality or features should be
21  considered 'preliminary', and might not function as advertised.
22 
23  MIT-like license:
24 
25  There is no restriction as to what you can do with this software, so long
26  as you include the above copyright notice and DISCLAIMER for any distributed
27  work that is equal to or derived from this one, along with this paragraph
28  that explains the terms of the license if the source is also being made
29  available. A "derived work" describes a work that uses a significant portion
30  of the source files or algorithms that are included with this one.
31  Specifically excluded from this are files that were generated by the software,
32  or anything that is included with the software that is part of another package
33  (such as files that were created or added during the 'configure' process).
34  Specifically included is the use of part or all of any of the X11 workbench
35  toolkit source or header files in your distributed application. If you do not
36  ship the source, the above copyright statement is still required to be placed
37  in a reasonably prominent place, such as documentation, splash screens, and/or
38  'about the application' dialog boxes.
39 
40  Use and distribution are in accordance with GPL, LGPL, and/or the above
41  MIT-like license. See COPYING and README files for more information.
42 
43 
44  Additional information at http://sourceforge.net/projects/X11workbench
45 
46 ******************************************************************************/
47 
48 
63 #include <stdio.h>
64 #include <stdlib.h>
65 #include <stdarg.h>
66 #include <limits.h>
67 #include <unistd.h>
68 #include <memory.h>
69 #include <string.h>
70 #include <strings.h>
71 #include <signal.h>
72 #include <time.h>
73 #include <fcntl.h>
74 #include <errno.h>
75 #include <sys/time.h> // for 'gettimeofday'
76 
77 // TODO: determine if pthread is available, optionally use for timers
78 #include <pthread.h> /* currently required */
79 
80 
81 #include <X11/cursorfont.h>
82 #ifndef XK_Delete /* moslty for interix */
83 #define XK_MISCELLANY /* mostly for interix */
84 #include <X11/keysymdef.h> // some platforms don't automatically include this with X headers
85 #endif // XK_Delete
86 
87 #define _WINDOW_HELPER_C_ /*this causes some header definitions to change slightly */
88 
89 #include "window_helper.h"
90 #include "pixmap_helper.h"
91 #include "conf_help.h"
92 
93 
94 #define MIN_EVENT_LOOP_SLEEP_PERIOD 100 /* 0.1 millisec */
95 #define MAX_EVENT_LOOP_SLEEP_PERIOD 50000 /* 50 msecs - better for Linux within a VM - worst was 3.6% during idle */
96 //#define MAX_EVENT_LOOP_SLEEP_PERIOD 100000 /* 0.1 sec - way too long, actually */
97 
98 const char *sz_xcall_func = NULL;
99 int i_xcall_line = 0;
100 
101 
102 //#define USE_WINDOW_XIMAGE /* comment this out to disable using an XImage to perform graphiic operations on a window */
103 
104 
105 // **************************************************************************
106 // ALGORITHMS AND OTHER RELATED NOTES
107 // the hash algorithm is simple. First, apply 'WINDOW_ENTRY_HASH' to the
108 // window ID. Then use it as an index into the array of '_WINDOW_ENTRY_'
109 // structures. If the entry it points to does NOT match the window ID I'm
110 // interested in, increment the index 'mod ARRAY SIZE' until I either find
111 // the matching window ID, wrap around, or find a zero window ID. Wrapping
112 // around is basically prevented by ensuring that the array is at least twice
113 // the size of the maximum allowed window count. And, for the above reasons,
114 // the size of the array must be a power of 2 so that the hash works properly.
115 
116 // NOTE: window assignments tend to be consecutive so I must scramble them
117 // up or risk inefficiency in the hash algorithm. This can be
118 // improved later to minimize the impact of this behavior.
119 //
120 // NOTE 2: Window ID and other XID values APPEAR to depend on the definition
121 // of 'RESOURCE_AND_CLIENT_COUNT' and the max # of clients
122 // This basically makes the lower 29 bits of an XID consist of
123 // the client ID (5 to 9 bits) and the resource ID (remaining bits)
124 // with leading bits 31,30,29 all zeros.
125 // It may be possible to create "fake" windows with the high bit set
126 // **************************************************************************
127 
128 
129 /**********************************************************************/
130 /* */
131 /* externally visible global variables (general use) */
132 /* */
133 /**********************************************************************/
134 
135 //XFontStruct *pDefaultFont = NULL; // default font
136 //XFontSet fontsetDefault = None; // default font set, derived from default font
137 WB_FONT pDefaultFont = NULL;
138 Display *pDefaultDisplay = NULL;
139 
140 int iStartupMinMax = 0; // main window open min/max/normal flag
141 
142 
143 /**********************************************************************/
144 /* */
145 /* global atoms, initialized via 'WBInit' */
146 /* (atoms remain valid until all connections go away) */
147 /* */
148 /**********************************************************************/
166 Atom aMENU_COMMAND=None;
167 
205 
224 Atom aRESIZE_NOTIFY=None;
225 
233 Atom aDESTROY_NOTIFY=None;
234 
256 Atom aCONTROL_NOTIFY=None;
257 
284 Atom aSCROLL_NOTIFY = None; // specific notification for scrollbars
285 
312 Atom aQUERY_CLOSE=None;
313 
336 Atom aRECALC_LAYOUT=None;
337 
338 
360 Atom aDLG_FOCUS=None;
361 
383 Atom aSET_FOCUS=None;
384 
404 Atom aWB_CHAR=None;
405 
423 Atom aWB_TIMER=None;
424 
465 Atom aWB_POINTER=None;
466 
467 // things used by window managers
475 Atom aWM_PROTOCOLS=None; // WM supported protocols (see 'freedesktop.org' WM docs)
476 
485 Atom aWM_DELETE_WINDOW=None; // WM command to delete a window (a click on the 'x')
486 
496 Atom aWM_TAKE_FOCUS=None;
497 
498 
505 Atom aAVERAGE_WIDTH=None;
506 
507 
515 Atom aCLIPBOARD=None; // Atom for 'CLIPBOARD'
523 Atom aPRIMARY=XA_PRIMARY; // Atom for 'PRIMARY' XA_PRIMARY
531 Atom aSECONDARY=XA_SECONDARY;// Atom for 'SECONDARY' XA_SECONDARY
539 Atom aMANAGER=None; // Atom for 'MANAGER'
547 Atom aTARGET=None; // Atom for 'TARGET'
555 Atom aINCR=None; // Atom for 'INCR' (incremental transfers)
563 Atom aWINDOW=XA_WINDOW; // Atom for 'WINDOW'
571 Atom aBITMAP=XA_BITMAP; // Atom for 'BITMAP'
579 Atom aDRAWABLE=XA_DRAWABLE; // Atom for 'DRAWABLE'
587 Atom aCOLORMAP=XA_COLORMAP; // Atom for 'COLORMAP'
595 Atom aPIXEL=None; // Atom for 'PIXEL'
603 Atom aPIXMAP=XA_PIXMAP; // Atom for 'PIXMAP'
611 Atom aTEXT=None; // Atom for 'TEXT'
619 Atom aSTRING=XA_STRING; // Atom for 'STRING'
627 Atom aUTF8_STRING=None; // Atom for 'UTF8_STRING'
635 Atom aC_STRING=None; // Atom for 'C_STRING'
643 Atom aCOMPOUND_TEXT=None; // Atom for 'COMPOUND_TEXT'
651 Atom aTARGETS=None; // Atom for 'TARGETS'
659 Atom aMULTIPLE=None; // Atom for 'MULTIPLE'
667 Atom aTIMESTAMP=None; // Atom for 'TIMESTAMP'
675 Atom aNULL=None; // Atom for 'NULL'
676 
677 
678 /**********************************************************************/
679 /* */
680 /* module-specific globals and structures for window processing */
681 /* */
682 /**********************************************************************/
683 
684 // TODO: do I implement this data type as a 'super type' of WBWindow ?
685 //typedef unsigned long long WBWindow;
686 //
687 //#define WBWINDOW_MASK 0xffffffff /* portion of WBWindow that is an actual Window ID */
688 //#define WBWINDOW_FLAGS_NORMAL 0x0
689 //#define WBWINDOW_FLAGS_PSEUDO 0x100000000LL /* tag WBWindow as a 'pseudo' window ID */
690 
691 #ifdef _XSERVER64
692 #error _XSERVER64 defined
693 #endif
694 
695 
696 
760 typedef struct s_internal_window_entry // the structure that identifies the window and what to do with it
761 {
762  Window wID;
763  const char *szClassName;
764  Display *pDisplay;
766  XImage *pImage;
767  Window wParent;
769  unsigned long clrFG;
770  unsigned long clrBG;
772  XWMHints *pWMHints;
773  Pixmap pxIcon;
774  Pixmap pxMask;
775 
783  int (* pCallback)(Window wIDEvent, XEvent *pEvent);
785  Region rgnClip;
786  Region rgnPaint;
787  Window wIDMenu;
788 
789  int (* pMenuCallback)(Window wIDEvent, XEvent *pEvent);
790  Cursor curRecent;
791  int idCursor;
794  int width;
795  int height;
796  int border;
802  struct timeval tvLastActivity;
805 
806 #define WINDOW_ENTRY_ARRAY_SIZE 2048 /* must be a power of 2 and twice the max expected # of windows */
807 #define WINDOW_ENTRY_ARRAY_MAX 2047 /* must be the above value minus 1 */
808 #define WINDOW_ENTRY_HASH(X) (((((unsigned long)X & 0xf00) + \
809  (((unsigned long)X >> 3) & 0x1f) + \
810  (((unsigned long)X << 5) & 0xe0)) ^ \
811  ((unsigned long)X >> 12)) & WINDOW_ENTRY_ARRAY_MAX)
812 
813 #define WINDOW_ENTRY_UNUSED ((Window)-1)
814 
815 #define WB_WINDOW_DELETE -3 /* entry to be deleted during cleanup phase */
816 #define WB_WINDOW_DESTROYED -2 /* window has been destroyed, entry remains */
817 #define WB_WINDOW_DESTROYING -1 /* not destroyed yet - prevent recursion */
818 #define WB_WINDOW_UNMAPPED 0 /* window created, but not yet mapped */
819 #define WB_WINDOW_MAPPED 1 /* window mapped */
820 #define WB_WINDOW_SET_FOCUS_ON_MAP 2 /* focus is to be set when the window is mapped */
821 #define WB_WINDOW_DELETE_TIMEOUT 5 /* seconds to wait before deleting hash entry (was 30, now 5) */
822 
823 #define WB_CHECK_SET_FOCUS_ON_MAP(X) ((X).iWindowState == WB_WINDOW_SET_FOCUS_ON_MAP)
824 #define WB_IS_WINDOW_UNMAPPED(X) ((X).iWindowState == WB_WINDOW_UNMAPPED || (X).iWindowState == WB_WINDOW_SET_FOCUS_ON_MAP)
825 #define WB_IS_WINDOW_MAPPED(X) ((X).iWindowState == WB_WINDOW_MAPPED)
826 #define WB_IS_WINDOW_BEING_DESTROYED(X) ((X).iWindowState == WB_WINDOW_DESTROYING)
827 #define WB_IS_WINDOW_DESTROYED(X) ((X).iWindowState == WB_WINDOW_DESTROYED || (X).iWindowState == WB_WINDOW_DELETE)
828 #define WB_TO_DELETE_WINDOW_ENTRY(X) ((X).iWindowState == WB_WINDOW_DELETE)
829 
830 
831 //------------------------------------------------------------------------------------------------------
832 // NOT globally visible variables that define the application, default display params, hash table, etc.
833 //------------------------------------------------------------------------------------------------------
834 
835 static _WINDOW_ENTRY_ sWBHashEntries[WINDOW_ENTRY_ARRAY_SIZE]={{0,0}};
836 static volatile int nWBHashEntries = 0;
837 static Window wWBFakeWindow = 0; // fake window keeps me from losing connection when I close all windows
838 static Window wIDApplication = None; // application window ID
839 
840 // ignoring X errors - sometimes errors are expected (!?)
841 int bIgnoreXErrors = 0;
842 static WB_ERROR_INFO xErrorInfo = {NULL, NULL, 0, 0, 0, 0, 0, None};
843 
844 // the default display - if I manage more than one, I may need these to be display-specific
845 Time tmDefaultDisplay = 0; // most recent time value for default display (from events)
846 WB_UINT64 qwDefaultDisplayTick = 0LL; // the tick count for the last 'default display time'
847 
848 int bStandardColormap = 0; // flag that says 'cmapDefault' is initialized
849 XStandardColormap cmapDefault; // a copy of teh XStandardColormap for the Colormap for the default display
850 
851 
852 
853 /**********************************************************************/
854 /* */
855 /* module-specific globals and structures related to event queuing */
856 /* */
857 /**********************************************************************/
858 
859 #define EVENT_ARRAY_SIZE 0x2000 /* this many events maximum */
860 #define EVENT_ARRAY_MASK 0x1fff /* 'and' this for modular math */
861 
871 typedef struct s_EVENT_ENTRY
872 {
873  Display *pDisplay;
874  int iNext;
875  Window wID;
876  XEvent xEvt;
877 
884  char padding[256 - sizeof(Display *) - sizeof(Window) - sizeof(int) - sizeof(XEvent)];
885 
886 } EVENT_ENTRY;
887 
888 static EVENT_ENTRY axWBEvt[EVENT_ARRAY_SIZE];
889 
890 static int iWBQueuedEvent = -1, iWBQueueLast = -1, iWBPaintEvent = -1, iWBPaintLast = -1, iWBFreeEvent = -1;
891 static int iInitEventFlag = 0;
892 
893 //static WBAppEvent pAppEventCallback = NULL;
894 static int (* pAppEventCallback)(XEvent *pEvent) = NULL;
895 
896 
897 #define TIMER_ARRAY_SIZE 512
898 
907 typedef struct s_TIMER_ENTRY
908 {
911  unsigned long lTimeInterval;
912 
913  Display *pDisplay;
914  Window wID;
915  long lID;
916 } TIMER_ENTRY;
917 
918 static TIMER_ENTRY axWBTimer[TIMER_ARRAY_SIZE];
919 static TIMER_ENTRY *pTimerEntryActive = NULL, *pTimerEntryFree = NULL;
920  // pointers for two linked lists. entries must be in either 'active' or 'free' list.
921 
930 typedef struct s_DELAYED_EVENT_ENTRY
931 {
934 
935  XEvent event;
937 
938 static DELAYED_EVENT_ENTRY axWBDelayedEvent[TIMER_ARRAY_SIZE]; // same size as timers
939 static DELAYED_EVENT_ENTRY *pDelayedEventEntryActive = NULL, *pDelayedEventEntryFree = NULL;
940  // pointers for two linked lists. entries must be in either 'active' or 'free' list.
941 
942 
943 /**********************************************************************/
944 /* */
945 /* local function prototypes */
946 /* */
947 /**********************************************************************/
948 
949 #ifdef NO_DEBUG
950 static __inline__ _WINDOW_ENTRY_ *WBGetWindowEntry(Window wID);
951 #else
952 #define WBGetWindowEntry(X) Debug_WBGetWindowEntry(X, __FUNCTION__, __LINE__)
953 static __inline__ _WINDOW_ENTRY_ *Debug_WBGetWindowEntry(Window wID, const char *szFunction, int nLine);
954 #endif // NO_DEBUG
955 
956 static void __WBInitEvent();
957 static int __WBAddEvent(Display *pDisp, Window wID, XEvent *pEvent);
958 static void __WBDelWindowPaintEvents(Display *pDisp, Window wID);
959 static void __WBDelWindowEvents(Display *pDisp, Window wID);
960 static int __WBInsertPriorityEvent(Display *pDisp, Window wID, XEvent *pEvent);
961 static int __WBNextPaintEvent(Display *pDisp, XEvent *pEvent, Window wID);
962 static int __WBNextDisplayEvent(Display *pDisp, XEvent *pEvent);
963 static void WBInternalProcessExposeEvent(XExposeEvent *pEvent);
964 static int __internal_alloc_WMHints(_WINDOW_ENTRY_ *pEntry);
965 static Window __internal_GetParent(Display *pDisplay, Window wID, Window *pwRoot);
966 static const char * __internal_event_type_string(int iEventType);
967 static int __InternalCheckGetEvent(Display *pDisplay, XEvent *pEvent, Window wIDModal);
968 static void DeletAllTimersForWindow(Display *pDisplay, Window wID);
969 
970 void __InternalDestroyWindow(Display *pDisp, Window wID, _WINDOW_ENTRY_ *pEntry);
971 
972 
973 /********************************************************************************/
974 /* */
975 /* WBInit, WBInitDisplay, WBExit - global initialization/termination functions */
976 /* */
977 /********************************************************************************/
978 
979 static int disable_imagecache = 0;
980 static int (*pOldDisplayIOErrorHandler)(Display *) = NULL;
981 static int WBXIOErrorHandler(Display *pDisp);
982 static int WBXErrorHandler(Display *pDisplay, XErrorEvent *pError);
983 static int hBogus[3]; // 'bogus' handles 0-2
984 
985 static WB_GEOM geomStartup
986 #ifdef WB_C99_INITIALIZERS
987  = {.x=0,.y=0,.width=0,.height=0,.border=0}; // C99 syntax; MS does not support this even in 2010 compiler
988  // NOTE: older 'element:value' syntax is supported by GCC only; use '.element=value' (C99) instead
989 #else // WB_C99_INITIALIZERS
990  = {0,0,0,0,0}; // C89 syntax (universally supported)
991 #error this compiler does not support C99 structure initializers. Some code will not (yet) compile.
992 #endif // WB_C99_INITIALIZERS
993 static char szStartupDisplayName[512]=":0.0"; // NOTE: new values must be UTF-8
994 static char szDefaultDisplayName[512]= ""; // copy the "default" display name from WBInit() here
995 
996 // libXft aka Freetype -
997 #ifdef X11WORKBENCH_TOOLKIT_HAVE_XFT
998 extern FT_Library __ftlib; /* located in font_helper.c */
999 #endif // X11WORKBENCH_TOOLKIT_HAVE_XFT
1000 
1001 
1002 
1003 // helpers called by WBParseStandardArguments()
1004 
1005 void __internal_startup_display(const char *szVal)
1006 {
1007  strncpy(szStartupDisplayName, szVal, sizeof(szStartupDisplayName));
1008 
1009  szStartupDisplayName[sizeof(szStartupDisplayName) - 1] = 0;
1010 }
1011 
1012 
1013 void __internal_startup_minimize(void)
1014 {
1015  iStartupMinMax = -1;
1016 }
1017 
1018 
1019 void __internal_startup_maximize(void)
1020 {
1021  iStartupMinMax = 1;
1022 }
1023 
1024 
1025 void __internal_startup_geometry(const char *szVal)
1026 {
1027  sscanf(szVal, "%dx%d+%d+%d",
1028  &geomStartup.width, &geomStartup.height, &geomStartup.x, &geomStartup.y);
1029 }
1030 
1031 
1032 void __internal_disable_imagecache(void)
1033 {
1034  disable_imagecache = 1;
1035 }
1036 
1037 
1038 
1039 // MAIN WINDOW INITIALIZATION FUNCTION - call this after WBParseStandardArguments() to be
1040 // fully compliant with X11 command line standards.
1041 
1042 Display *WBInit(const char *szDisplayName) // NOTE: this is 'single instance' only...
1043 {
1044 int i1;
1045 Display *pRval;
1046 
1047 #ifndef WIN32
1048  signal(SIGCHLD, SIG_IGN); // make sure that 'SIGCHLD' signals are *IGNORED* - this is important
1049 #endif // WIN32
1050 
1051 
1052  WBPlatformOnInit(); // initialize a few important 'platform helper' things
1053 
1054  bQuitFlag = FALSE; // in case I re-init an app. note the variable is in 'platform_helper.c'
1055 
1056  // ********************************************************************************
1057  // HACK ALERT - HACK ALERT - HACK ALERT
1058  //
1059  // before I begin, allocate 3 handles. Any handles <= '2' will be KEPT OPEN
1060  // to avoid problems with the X display using things like stderr for I/O
1061  // (that is in case stdin, stdout, and stderr weren't allocated or were freed)
1062  // This assumes that these 3 aren't already open, but for a windowing application
1063  // with no stdin/stdout/stderr, this is a distinct possibility
1064  //
1065  // It is caused by a limitation of the X11 libraries, and is probably a bug.
1066 
1067  for(i1=0; i1 < 3; i1++)
1068  {
1069  hBogus[i1] = open("/dev/null", O_RDWR);
1070  }
1071 
1072  // NOTE: I can choose to clean this up later but for now it shouldn't do
1073  // any harm to leave any file handles <= 2 open in 'hBogus'
1074 
1075  for(i1=2; i1 >= 0; i1--)
1076  {
1077  if(hBogus[i1] > 2)
1078  {
1079  close(hBogus[i1]);
1080  hBogus[i1] = -1;
1081  }
1082  }
1083 
1084  // HACK ALERT - HACK ALERT - HACK ALERT
1085  // ********************************************************************************
1086 
1087  // NEXT, determine what the display name should be and open it
1088 
1089  if(!szDisplayName)
1090  {
1091  szDisplayName = GetStartupDisplayName();
1092  if(!szDisplayName || !*szDisplayName)
1093  {
1094  szDisplayName = ":0.0"; // make sure it's SOMETHING
1095  }
1096  }
1097 
1098  if((pRval = XOpenDisplay(szDisplayName)) == NULL)
1099  {
1100  WB_ERROR_PRINT("%s - can't open display %s\n", __FUNCTION__, szDisplayName);
1101 
1102  return NULL;
1103  }
1104 
1105  // make a copy of the display name for use later on, like for the clipboard initialization
1106  strncpy(szDefaultDisplayName, szDisplayName, sizeof(szDefaultDisplayName));
1107 
1108 
1109  // new I/O error handler - 'quit flag' will be set on error but app will
1110  // not terminate until the quit flag is 'noticed'
1111 
1112  pOldDisplayIOErrorHandler = XSetIOErrorHandler(WBXIOErrorHandler);
1113 
1114  if(WBInitDisplay(pRval)) // open and initialize display and basic windowing stuff (including clipboard)
1115  {
1116  WBExit(); // in case there are resources to free, plus it does XCloseDisplay(pRval);
1117 
1118  WB_ERROR_PRINT("%s - Unable to initialize X11 WorkBench Toolkit\n", __FUNCTION__);
1119 
1120  return NULL;
1121  }
1122 
1123 // XFlush(pRval);
1124  XSync(pRval, 0); // make sure that the display is "in sync"
1125 
1126 #if 0 // see if I still need this by removing it
1127  WBDelay(100000); // wait 0.1 seconds for everything to stabilize (shorter times not good enough)
1128  // sometimes the window manager will get caught in a race condition and
1129  // forward along keystrokes that belong to the OLD application, such
1130  // as the last <RETURN> keystroke release, which could cause a dialog box
1131  // to mis-behave or a window to insert an unnecessary keystroke. Giving
1132  // the window manager a chance to NOT be involved in a race condition should
1133  // prevent this from happening, or at least minimize the chances.
1134 #endif // 0
1135 
1136  // now that I've initialized the display I can initialize things
1137  // that depend up on it, beginning with the font stuff
1138 
1139  __internal_font_helper_init(); // initialize font helper
1140 
1141  // TODO: other things
1142 
1143  return pRval;
1144 }
1145 
1146 static int WBXIOErrorHandler(Display *pDisp)
1147 {
1148  WB_DEBUG_PRINT(DebugLevel_ERROR | DebugSubSystem_Application,
1149  "I/O error occurred, setting 'bQuitFlag'\n");
1150 
1151  bQuitFlag = TRUE; // set QUIT flag on ALL I/O errors. This is the safest way to shut things down.
1152 
1153 // if(pOldDisplayIOErrorHandler)
1154 // return(pOldDisplayIOErrorHandler(pDisp));
1155 
1156  return 0; // for now will do this
1157 }
1158 
1159 
1160 int WBInitDisplay(Display *pDisplay)
1161 {
1162 unsigned long long ullTick;
1163 
1164  ullTick = WBGetTimeIndex();
1165 
1166  pDefaultDisplay = pDisplay;
1167 
1168  // create a 'fake' window that will keep me from losing connection when I terminate the app
1169  wWBFakeWindow = XCreateSimpleWindow(pDisplay, DefaultRootWindow(pDisplay), -1, -1, 1, 1, 0, 0, 0);
1170 
1171  // assign a global X11 error handler that will continue execution after printing an appropriate
1172  // debug message (as needed), in lieu of basically KILLING THE APPLICATION due to some trivial B.S. "error"
1173 
1174 // BEGIN_XCALL_DEBUG_WRAPPER
1175  XSetErrorHandler(WBXErrorHandler);
1176 // END_XCALL_DEBUG_WRAPPER
1177 
1178  // begin clipboard initialization with a background thread
1179 
1180  if(__StartInitClipboardSystem(pDisplay, szDefaultDisplayName)) // initialize clipboard system
1181  {
1182  WB_ERROR_PRINT("%s - unable to initialize clipboard system\n", __FUNCTION__);
1183 
1184  return 2;
1185  }
1186 
1187  /*
1188  * Load the font to use. See Sections 10.2 & 6.5.1 of the X11 docs
1189  */
1190 
1191  pDefaultFont = WBLoadFont(pDisplay, WB_DEFAULT_FONT, WB_DEFAULT_FONT_SIZE, 0);
1192 
1193  if(!pDefaultFont)
1194  {
1195  pDefaultFont = WBLoadFont(pDisplay, "*", WB_DEFAULT_FONT_SIZE,
1197 
1198  if(!pDefaultFont)
1199  {
1200  pDefaultFont = WBLoadFont(pDisplay, "*", WB_DEFAULT_FONT_SIZE, 0);
1201 
1202  if(!pDefaultFont)
1203  {
1204  WB_ERROR_PRINT("%s - NO DEFAULT FONT, returning 0\n", __FUNCTION__);
1205  return 0;
1206  }
1207  }
1208  }
1209 
1210 // fontsetDefault = WBFontSetFromFont(pDisplay, pDefaultFont);
1211 
1212  //------
1213  // ATOMS
1214  //------
1215 
1216  // NOTE: the documentation for 'XInternAtom' does _NOT_ include a provision to
1217  // free up unused atoms. There's no obvious way to get rid of the ones that
1218  // are not being used any more. The docs say the following:
1219  // "It will become undefined only when the last connection to the X server closes."
1220  // (As such, I should minimize introducing NEW atoms, even though it's kinda necessary)
1221  //
1222  // To compensate, MANY of the atoms that I create will use WBGetAtom() rather than XInternAtom()
1223  // and are considered to be 'WorkBench internal' only - so don't use outside of the toolkit
1224 
1225  // ClientMessage events (in general) for various notifications
1226  aMENU_COMMAND = WBGetAtom(pDisplay, "MENU_COMMAND"); //XInternAtom(pDisplay, "MENU_COMMAND", False);
1227  aMENU_UI_COMMAND = WBGetAtom(pDisplay, "MENU_UI_COMMAND"); //XInternAtom(pDisplay, "MENU_UI_COMMAND", False);
1228  aRESIZE_NOTIFY = WBGetAtom(pDisplay, "RESIZE_NOTIFY"); //XInternAtom(pDisplay, "RESIZE_NOTIFY", False);
1229  aCONTROL_NOTIFY = WBGetAtom(pDisplay, "CONTROL_NOTIFY"); //XInternAtom(pDisplay, "CONTROL_NOTIFY", False);
1230  aSCROLL_NOTIFY = WBGetAtom(pDisplay, "SCROLL_NOTIFY"); //XInternAtom(pDisplay, "SCROLL_NOTIFY", False);
1231  aQUERY_CLOSE = WBGetAtom(pDisplay, "QUERY_CLOSE"); //XInternAtom(pDisplay, "QUERY_CLOSE", False);
1232  aRECALC_LAYOUT = WBGetAtom(pDisplay, "RECALC_LAYOUT"); //XInternAtom(pDisplay, "QUERY_CLOSE", False);
1233  aDESTROY_NOTIFY = WBGetAtom(pDisplay, "DESTROY_NOTIFY"); //XInternAtom(pDisplay, "DESTROY_NOTIFY", False);
1234  aDLG_FOCUS = WBGetAtom(pDisplay, "DLG_FOCUS"); //XInternAtom(pDisplay, "DLG_FOCUS", False);
1235  aSET_FOCUS = WBGetAtom(pDisplay, "SET_FOCUS"); //XInternAtom(pDisplay, "SET_FOCUS", False);
1236 
1237  // no doubt the above list will grow as new types of ClientMessage events are added
1238 
1239  // internally-generated 'TIMER' event notification
1240  aWB_TIMER = WBGetAtom(pDisplay, "WB_TIMER"); //XInternAtom(pDisplay, "WB_TIMER", False);
1241 
1242  // additional 'window manager' ClientMessage events that are generated internally by the toolkit
1243  // as a result of 'message translation' (basically user input 'RAW' to something more usable)
1244  aWB_CHAR = WBGetAtom(pDisplay, "WB_CHAR"); //XInternAtom(pDisplay, "WB_CHAR", False);
1245  aWB_POINTER = WBGetAtom(pDisplay, "WB_POINTER"); //XInternAtom(pDisplay, "WB_POINTER", False);
1246 
1247 
1248  // these next atoms REQUIRE the use of 'XInternAtom' since they have 'global' scope for the entire X11 system
1249 
1250  // window manager messages (see open desktop specification for window managers)
1251  // these should already be on the server since the WM would create them
1252  aWM_PROTOCOLS = XInternAtom(pDisplay, "WM_PROTOCOLS", False);
1253  aWM_DELETE_WINDOW = XInternAtom(pDisplay, "WM_DELETE_WINDOW", False);
1254  aWM_TAKE_FOCUS = XInternAtom(pDisplay, "WM_TAKE_FOCUS", False);
1255 
1256  // atoms used for fonts (font properties, basically)
1257  aAVERAGE_WIDTH = XInternAtom(pDisplay, "AVERAGE_WIDTH", False);
1258 
1259  // atoms for the clipboard (these are all standards, should be on the server already)
1260  aCLIPBOARD = XInternAtom(pDisplay, "CLIPBOARD", False);
1261  aMANAGER = XInternAtom(pDisplay, "MANAGER", False);
1262  aTARGET = XInternAtom(pDisplay, "TARGET", False);
1263  aINCR = XInternAtom(pDisplay, "INCR", False);
1264  aPIXEL = XInternAtom(pDisplay, "PIXEL", False);
1265  aTEXT = XInternAtom(pDisplay, "TEXT", False);
1266 #ifdef X_HAVE_UTF8_STRING /* this indicates the extension is present - rarely would it NOT be */
1267  aUTF8_STRING = XInternAtom(pDisplay, "UTF8_STRING", False);
1268 #endif // X_HAVE_UTF8_STRING
1269  aC_STRING = XInternAtom(pDisplay, "C_STRING", False);
1270  aCOMPOUND_TEXT = XInternAtom(pDisplay, "COMPOUND_TEXT", False);
1271  aTARGETS = XInternAtom(pDisplay, "TARGETS", False);
1272  aMULTIPLE = XInternAtom(pDisplay, "MULTIPLE", False);
1273  aTIMESTAMP = XInternAtom(pDisplay, "TIMESTAMP", False);
1274 
1275 // other atoms that are pre-defined - left as comments for future reference, as needed
1276 //
1277 // aPRIMARY = XA_PRIMARY; //XInternAtom(pDisplay, "PRIMARY", False);
1278 // aSECONDARY = XA_SECONDARY; //XInternAtom(pDisplay, "SECONDARY", False);
1279 // aWINDOW = XA_WINDOW; //XInternAtom(pDisplay, "WINDOW", False);
1280 // aBITMAP = XA_BITMAP; //XInternAtom(pDisplay, "BITMAP", False);
1281 // aDRAWABLE = XA_DRAWABLE; //XInternAtom(pDisplay, "DRAWABLE", False);
1282 // aCOLORMAP = XA_COLORMAP; //XInternAtom(pDisplay, "COLORMAP", False);
1283 // aPIXMAP = XA_PIXMAP; //XInternAtom(pDisplay, "PIXMAP", False);
1284 // aSTRING = XA_STRING; //XInternAtom(pDisplay, "STRING", False);
1285 
1286  // and, of course, the string 'NULL' which should be globally defined
1287  aNULL = XInternAtom(pDisplay, "NULL", False);
1288 
1289  // make sure my atoms work. Mostly, they're pre-defined or 'WorkBench internal'
1290  // atoms, so no reason why they should not
1291 
1292  if(aMENU_COMMAND == None ||
1293  aMENU_UI_COMMAND == None ||
1294  aRESIZE_NOTIFY == None ||
1295  aCONTROL_NOTIFY == None ||
1296  aSCROLL_NOTIFY == None ||
1297  aQUERY_CLOSE == None ||
1298  aDESTROY_NOTIFY == None ||
1299  aDLG_FOCUS == None ||
1300  aSET_FOCUS == None ||
1301  aWM_PROTOCOLS == None ||
1302  aWM_DELETE_WINDOW == None ||
1303  aWM_TAKE_FOCUS == None ||
1304  aAVERAGE_WIDTH == None ||
1305  aWB_CHAR == None ||
1306  aWB_TIMER == None ||
1307  aWB_POINTER == None ||
1308  aCLIPBOARD == None ||
1309  aMANAGER == None ||
1310  aTARGET == None ||
1311  aINCR == None ||
1312  aPIXEL == None ||
1313  aTEXT == None ||
1314 #ifdef X_HAVE_UTF8_STRING /* this indicates the extension is present */
1315  aUTF8_STRING == None || // TODO: should this one be optional?
1316 #endif // X_HAVE_UTF8_STRING
1317  aC_STRING == None ||
1318  aCOMPOUND_TEXT == None ||
1319  aTARGETS == None ||
1320  aMULTIPLE == None ||
1321  aTIMESTAMP == None ||
1322 // aPRIMARY == None ||
1323 // aSECONDARY == None ||
1324 // aWINDOW == None ||
1325 // aBITMAP == None ||
1326 // aDRAWABLE == None ||
1327 // aCOLORMAP == None ||
1328 // aPIXMAP == None ||
1329 // aSTRING == None ||
1330  aNULL == None )
1331  {
1332  return 1;
1333  }
1334 
1335 
1336  // FINALLY, initialize settings and finish the clipboard system
1337 
1338  if(__FinishInitClipboardSystem(pDisplay, szDefaultDisplayName)) // initialize clipboard system
1339  {
1340  WB_ERROR_PRINT("%s - unable to initialize clipboard system\n", __FUNCTION__);
1341 
1342  return 2;
1343  }
1344 
1345  CHSettingsRefresh(pDisplay); // must init first part of the clipboard system *FIRST* before I call this
1346 
1347  // now there's a minimum time delay that I need so that any characters
1348  // that have been posted to a console window are processed by that console
1349  // window and not by me. If I grab the focus too early, it could interpret the
1350  // return for the previous command as an input to the program.
1351  //
1352  // This needs to be longer for a debug build than for a release one, due to debug output
1353  // on stderr (mostly). At least that's how it seems to me.
1354  //
1355  // So, I need to delay for about 0.25 seconds at a minimum for debug builds, and 0.1 seconds
1356  // for a release build.
1357 
1358 #ifdef NO_DEBUG
1359 #define MIN_STARTUP_DELAY 100000LL
1360 #else
1361 #define MIN_STARTUP_DELAY 250000LL
1362 #endif // NO_DEBUG
1363 
1364  ullTick = WBGetTimeIndex() - ullTick;
1365  if(ullTick < MIN_STARTUP_DELAY)
1366  {
1367  // TODO: eat all keyboard and mouse input events...
1368 
1369  usleep(MIN_STARTUP_DELAY - ullTick);
1370  }
1371 
1372  return 0; // success
1373 }
1374 
1375 
1377 {
1378  if(!pDefaultFont)
1379  {
1380  WB_ERROR_PRINT("%s - pDefaultFont is NULL\n", __FUNCTION__);
1381  }
1382 
1383  return (WB_FONTC)pDefaultFont;
1384 }
1385 
1386 //XFontStruct *WBGetDefaultFont(void)
1387 //{
1388 // extern XFontStruct *pDefaultFont; // default font
1389 //
1390 // if(!pDefaultFont)
1391 // {
1392 // WB_ERROR_PRINT("%s - pDefaultFont is NULL\n", __FUNCTION__);
1393 // }
1394 //
1395 // return(pDefaultFont);
1396 //}
1397 
1398 //XFontSet WBGetDefaultFontSet(Display *pDisplay)
1399 //{
1400 //extern XFontSet fontsetDefault; // TODO: per-display font sets?
1401 //
1402 // if(!pDisplay || pDisplay == pDefaultDisplay)
1403 // {
1404 // if(fontsetDefault == None)
1405 // {
1406 // WB_ERROR_PRINT("%s - fontsetDefault is None\n", __FUNCTION__);
1407 // }
1408 //
1409 // return fontsetDefault;
1410 // }
1411 //
1412 // WB_ERROR_PRINT("TODO: %s - support use of non-default Display (returning 'None')\n", __FUNCTION__);
1413 //
1414 // return None; // for now...
1415 //}
1416 
1418 {
1419  if(wWBFakeWindow == None)
1420  {
1421  WB_ERROR_PRINT("%s - wWBFakeWindow is 'None'\n", __FUNCTION__);
1422  }
1423 
1424  return wWBFakeWindow;
1425 }
1426 
1427 void __InternalDestroyWindow(Display *pDisp, Window wID, _WINDOW_ENTRY_ *pEntry)
1428 {
1429  Window wIDTemp;
1430  Atom aNCW;
1431  XClientMessageEvent xMsg;
1432  Status st;
1433  XImage *pTempImage;
1434  int i1;
1435 
1436 
1437 // WB_ERROR_PRINT("TEMPORARY - %s - Destroying window %u (%08xH), %d hash entries\n",
1438 // __FUNCTION__, (unsigned int)wID, (unsigned int)wID, nWBHashEntries);
1439 
1440  // first, I want to destroy any child windows that have window entries, FIRST. Do that now.
1441 
1442  for(i1=0; i1 < WINDOW_ENTRY_ARRAY_SIZE; i1++)
1443  {
1444  if(WB_LIKELY(sWBHashEntries[i1].wID == WINDOW_ENTRY_UNUSED) ||
1445  WB_LIKELY(sWBHashEntries[i1].wID == None))
1446  {
1447  continue; // ignore 'None' and 'UNUSED'
1448  }
1449 
1450  if(sWBHashEntries[i1].wID == wID)
1451  {
1452 // WB_ERROR_PRINT("TEMPORARY - %s - Destroy child windows, found 'me' %u (%08xH)\n",
1453 // __FUNCTION__, (unsigned int)wID, (unsigned int)wID);
1454  continue; // ignore "me"
1455  }
1456 
1457  if(sWBHashEntries[i1].wParent == wID)
1458  {
1459  if(WB_IS_WINDOW_BEING_DESTROYED(sWBHashEntries[i1]))
1460  {
1461 // WB_ERROR_PRINT("TEMPORARY - %s - child window already being destroyed %u (%08xH)\n",
1462 // __FUNCTION__, (unsigned int)sWBHashEntries[i1].wID, (unsigned int)sWBHashEntries[i1].wID);
1463  }
1464  else if(WB_IS_WINDOW_DESTROYED(sWBHashEntries[i1])) // not already destroyed (or being deleted)
1465  {
1466 // WB_ERROR_PRINT("TEMPORARY - %s - child window already destroyed %u (%08xH)\n",
1467 // __FUNCTION__, (unsigned int)sWBHashEntries[i1].wID, (unsigned int)sWBHashEntries[i1].wID);
1468  }
1469  else if(WB_TO_DELETE_WINDOW_ENTRY(sWBHashEntries[i1]))
1470  {
1471 // WB_ERROR_PRINT("TEMPORARY - %s - child window entry to be deleted %u (%08xH)\n",
1472 // __FUNCTION__, (unsigned int)sWBHashEntries[i1].wID, (unsigned int)sWBHashEntries[i1].wID);
1473  }
1474  else
1475  {
1476 // WB_ERROR_PRINT("TEMPORARY - %s - Destroying child window %u (%08xH)\n",
1477 // __FUNCTION__, (unsigned int)sWBHashEntries[i1].wID, (unsigned int)sWBHashEntries[i1].wID);
1478 
1479  __InternalDestroyWindow(pDisp, sWBHashEntries[i1].wID, &(sWBHashEntries[i1]));
1480  }
1481  }
1482  }
1483 
1484 
1485  if(!pEntry || !(pEntry->eWMProtocols & WMPropertiesWMProtocols_DeleteWindow))
1486  {
1487 destroy_without_wm:
1488 
1489  if(pEntry)
1490  {
1491  pEntry->iWindowState = WB_WINDOW_DESTROYED; // so that there is no recursion problem
1492  // this way the window has already been marked 'destroyed' before actually calling
1493  // the function to destroy it. Doing it this way tells everything else down the
1494  // pike that I shouldn't be querying info or trying to destroy it TWICE.
1495  }
1496 
1497 // WB_ERROR_PRINT("TEMPORARY: %s - call to XDestroyWindow for %u (%08xH)\n", __FUNCTION__, (int)wID, (int)wID);
1498 
1500  XDestroyWindow(pDisp, wID); // do this anyway (since it's still mapped and not destroyed)
1502 
1504 // XFlush(pDefaultDisplay);
1505  XSync(pDefaultDisplay, FALSE); // try sync'ing to avoid certain problems
1507 
1508  // TODO: if pEntry is not NULL, do I wait for it to REALLY get destroyed?
1509 
1510  if(pEntry)
1511  {
1512  pTempImage = pEntry->pImage;
1513  pEntry->pImage = NULL;
1514 
1515  if(pTempImage)
1516  {
1518  XDestroyImage(pTempImage); // buh-bye!
1520  }
1521  }
1522 
1523  return;
1524  }
1525 
1526  if(!pDisp)
1527  {
1528  pDisp = pDefaultDisplay;
1529  }
1530 
1531  wIDTemp = DefaultRootWindow(pDisp);
1532 
1533  if(wIDTemp == None)
1534  {
1535  WB_ERROR_PRINT("ERROR - %s - default root window = 'None'\n", __FUNCTION__);
1536  goto destroy_without_wm;
1537  }
1538 
1539  aNCW = XInternAtom(pDisp, "_NET_CLOSE_WINDOW", False); /* global scope, must use XInternAtom */
1540 
1541  if(aNCW == None)
1542  {
1543  WB_ERROR_PRINT("ERROR - %s - atom for _NET_CLOSE_WINDOW = 'None'\n", __FUNCTION__);
1544  goto destroy_without_wm;
1545  }
1546 
1547  memset(&xMsg, 0, sizeof(xMsg));
1548 
1549  xMsg.type = ClientMessage;
1550  xMsg.serial = 0;
1551  xMsg.send_event = 1;
1552  xMsg.display = pDisp;
1553  xMsg.window = wID;
1554  xMsg.message_type = aNCW; // TODO: different atom for different display?
1555  xMsg.format = 32;
1556  xMsg.data.l[0] = CurrentTime; //WBGetLastEventTime();
1557  xMsg.data.l[1] = 1; // normal application
1558 
1559  pEntry->iWindowState = WB_WINDOW_DESTROYING; // so that there is no recursion problem
1560 
1562  st = XSendEvent(pDisp, wIDTemp, False, NoEventMask, (XEvent *)&xMsg);
1564 
1566  XSync(pDefaultDisplay, FALSE); // try sync'ing to avoid certain errors
1568 
1569 // WB_ERROR_PRINT("<<TEMPORARY: %s - message sent to %d to close %d (%s)\n", __FUNCTION__, (int)wIDTemp, (int)wID, pEntry->szClassName);
1570 
1571 #if 0 /* this section never works */
1572  // waiting for the entry to be marked "destroyed"
1573 
1574  if(st)
1575  {
1576  WB_UINT64 tmNow = WBGetTimeIndex();
1577 
1578  WB_ERROR_PRINT("INFO: %s - 'DESTROY WINDOW' message sent OK to WM\n", __FUNCTION__);
1579 
1580  st = 0;
1581  while(pEntry && (WBGetTimeIndex() - tmNow) < 50000) // wait up to 0.05 seconds
1582  {
1583  XEvent evt;
1584 
1585  WBDelay(MIN_EVENT_LOOP_SLEEP_PERIOD); // wait 0.1 msec
1586 
1587  // look for (and dispatch) destroy notify messages for my window
1588  if(XCheckTypedEvent(pDisp, DestroyNotify, &evt))
1589  {
1590  WBDispatch(&evt); // process destroy notifications
1591  }
1592 
1593  if(WB_IS_WINDOW_DESTROYED(*pEntry))
1594  {
1595  st = 1;
1596 
1597  WB_ERROR_PRINT("INFO: %s - 'DESTROY WINDOW' message acknowledged by WM\n", __FUNCTION__);
1598 
1599  break;
1600  }
1601  }
1602  }
1603 
1604  if(!st)
1605 #endif // 0
1606 
1607  {
1608 
1609 // WB_DEBUG_PRINT(DebugLevel_WARN | DebugSubSystem_Window,
1610 // "WARNING - %s - did not destroy window via WM, calling XDestroyWindow for %d (%s)\n",
1611 // __FUNCTION__, (int)wID, pEntry->szClassName);
1612 
1613  pEntry->iWindowState = WB_WINDOW_DESTROYED; // so that there is no recursion problem or attempts to use the window
1614 
1615 // WB_ERROR_PRINT("TEMPORARY: %s - call to XDestroyWindow for %u (%08xH)\n", __FUNCTION__, (int)wID, (int)wID);
1616 
1618  XDestroyWindow(pDisp, wID); // do this anyway (since it's still mapped and not destroyed)
1620 
1622  XSync(pDefaultDisplay, FALSE); // try sync'ing to avoid certain problems
1624 
1625  // if there's a cached XImage for this window, destroy it
1626 
1627  pTempImage = pEntry->pImage;
1628  pEntry->pImage = NULL;
1629 
1630  if(pTempImage)
1631  {
1633  XDestroyImage(pTempImage); // buh-bye!
1635  }
1636  }
1637 }
1638 
1639 void WBExit()
1640 {
1641 int i1;
1642 //Window wIDRoot, wIDTemp;
1643 
1644  bQuitFlag = TRUE; // in case this makes something happen
1645 
1646  WB_DEBUG_PRINT(DebugLevel_Heavy | DebugSubSystem_Init,
1647  "TRACE: WBExit\n");
1648 
1649  if(!pDefaultDisplay)
1650  {
1651  WB_ERROR_PRINT("ERROR: %s - default display contains NULL\n", __FUNCTION__);
1652 
1653  WBExitClipboardSystem(NULL); // just in case
1654  return;
1655  }
1656 
1657  WBExitClipboardSystem(pDefaultDisplay); // do this now
1658 
1659  XFlush(pDefaultDisplay);
1660 
1661  // for each remaining window in my list, destroy it
1662  for(i1=WINDOW_ENTRY_ARRAY_MAX - 1; i1 >= 0; i1--)
1663  {
1664  Display *pDisp = sWBHashEntries[i1].pDisplay;
1665  Window wID = sWBHashEntries[i1].wID;
1666 
1667  if(!wID || wID == WINDOW_ENTRY_UNUSED)
1668  {
1669  continue;
1670  }
1671 
1672  if(!pDisp)
1673  {
1674  pDisp = pDefaultDisplay;
1675  }
1676 
1677  if(WB_IS_WINDOW_MAPPED(sWBHashEntries[i1]))
1678  {
1679  WB_ERROR_PRINT("INFO: %s destroying window %d\n", __FUNCTION__, (int)wID);
1680 
1681  // send a 'destroy' request to the window manager
1682 
1683  __InternalDestroyWindow(pDisp, wID, &(sWBHashEntries[i1]));
1684  }
1685 
1687 
1688  XFlush(pDisp);
1689  }
1690 
1691 // TODO: cache font sets within the font helper, and manage all of them there.
1692 // Font_OnExit(pDefaultDisplay); // call this _BEFORE_ I close the display
1693 
1694 // if(fontsetDefault != None)
1695 // {
1696 // XFreeFontSet(pDefaultDisplay, fontsetDefault);
1697 // fontsetDefault = None;
1698 // }
1699 
1700  if(pDefaultFont)
1701  {
1702 // XFreeFont(pDefaultDisplay, pDefaultFont);
1703  WBFreeFont(pDefaultDisplay, pDefaultFont);
1704  pDefaultFont = NULL;
1705  }
1706 
1707  if(wWBFakeWindow != None) // NOTE: WM doesn't know about this
1708  {
1709  WB_ERROR_PRINT("INFO: %s destroying 'fake' window %d\n", __FUNCTION__, (int)wWBFakeWindow);
1710 
1712  XDestroyWindow(pDefaultDisplay, wWBFakeWindow);
1714  }
1715 
1717  XSync(pDefaultDisplay, FALSE); // try sync'ing first to avoid certain errors
1718  XCloseDisplay(pDefaultDisplay); // display is to be destroyed now
1720 
1721  wWBFakeWindow = None;
1722  pDefaultDisplay = NULL;
1723 
1724  __internal_font_helper_exit(); // font helper
1725  PXM_OnExit(); // pixmap_helper
1726  CHOnExit(); // config_helper
1727  WBPlatformOnExit(); // platform_helper
1728 
1729  if(pOldDisplayIOErrorHandler)
1730  {
1731  XSetIOErrorHandler(pOldDisplayIOErrorHandler);
1732  pOldDisplayIOErrorHandler = NULL;
1733  }
1734 }
1735 
1736 const char *GetStartupDisplayName(void)
1737 {
1738  return szStartupDisplayName;
1739 }
1740 
1742 {
1743  if(pGeom)
1744  {
1745  memcpy(pGeom, &geomStartup, sizeof(WB_GEOM));
1746  }
1747 }
1748 
1750 {
1751  return iStartupMinMax;
1752 }
1753 
1754 
1755 Display *WBThreadInitDisplay(void)
1756 {
1757 Display *pDisplay;
1758 
1759  if(!szDefaultDisplayName[0]) // no display has been opened yet
1760  {
1761  return NULL;
1762  }
1763 
1764  pDisplay = XOpenDisplay(szDefaultDisplayName); // open a copy of the display
1765 
1766 
1767  // TODO: other initialization
1768 
1769 
1770  if(pDisplay)
1771  {
1773 // XFlush(pDisplay);
1774  XSync(pDisplay, FALSE); // sync everything now
1776  }
1777 
1778  return pDisplay;
1779 }
1780 
1781 void WBThreadFreeDisplay(Display *pThreadDisplay)
1782 {
1783  if(pThreadDisplay)
1784  {
1786 // XFlush(pThreadDisplay);
1787  XSync(pThreadDisplay, FALSE); // try sync'ing first to avoid certain errors
1788  XCloseDisplay(pThreadDisplay); // display is to be destroyed now
1790  }
1791 }
1792 
1793 void WBInitWindowAttributes(XSetWindowAttributes *pXSWA, unsigned long lBorderPixel,
1794  unsigned long lBackgroundPixel, Colormap clrMap, int iBitGravity)
1795 {
1796  memset(pXSWA, 0, sizeof(*pXSWA));
1797 
1798  pXSWA->border_pixel = lBorderPixel;
1799  pXSWA->background_pixel = lBackgroundPixel;
1800  pXSWA->colormap = clrMap;
1801  pXSWA->bit_gravity = iBitGravity;
1802 }
1803 
1804 void WBInitSizeHints(XSizeHints *pSH, Display *pDisplay, int iMinHeight, int iMinWidth)
1805 {
1806  WB_GEOM geomStartup;
1807 
1808  if(!pDisplay)
1809  {
1810  pDisplay = WBGetDefaultDisplay();
1811  }
1812 
1813 #define WBInitSizeHints__max(X,Y) ((X) > (Y) ? (X) : (Y))
1814 
1815  memset(pSH, 0, sizeof(*pSH));
1816 
1817  pSH->flags = (PPosition | PSize);
1818 
1819  GetStartupGeometry(&geomStartup); // based on user-specified or stored parameters
1820 
1821  // making this SIMPLE AS POSSIBLE, default to 2/3 of the screen (vertically and horizontally) and
1822  // center the window. If the screen is 16x9, reduce the width estimate to 4/3 the height
1823 
1824  if(!geomStartup.width || !geomStartup.height) // width or height zero --> default
1825  {
1826  int iDisplayHeight = DisplayHeight(pDisplay, DefaultScreen(pDisplay));
1827  int iDisplayWidth = DisplayWidth(pDisplay, DefaultScreen(pDisplay));
1828  int iActualDisplayWidth = iDisplayWidth;
1829 
1830  if(iDisplayWidth > iDisplayHeight * 4 / 3) // for 16x9 screens, we want a reasonable estimate
1831  {
1832  iDisplayWidth = iDisplayHeight * 4 / 3;
1833  }
1834 
1835  // base window dimensions on 2/3 of the size of the display or min height/width
1836 
1837  pSH->width = WBInitSizeHints__max(iMinWidth, iDisplayWidth * 2 / 3);
1838  pSH->height = WBInitSizeHints__max(iMinHeight, iDisplayHeight * 2 / 3);
1839 
1840  // vertically and horizontally center the window
1841 
1842  pSH->x = (iActualDisplayWidth - pSH->width) / 2; // use the actual display width this time
1843  pSH->y = (iDisplayHeight - pSH->height) / 2;
1844  }
1845  else
1846  {
1847  // user-specified dimensions take precedence
1848 
1849  pSH->x = geomStartup.x;
1850  pSH->y = geomStartup.y;
1851 
1852  pSH->width = geomStartup.width;
1853  pSH->height = geomStartup.height;
1854  }
1855 
1856 #undef WBInitSizeHints__max
1857 
1858 }
1859 
1860 
1861 
1862 int WBShowModal(Window wID, int bMenuSplashFlag)
1863 {
1864 int iRval = -1;
1865 int iSleepPeriod;
1866 WB_GEOM geom;
1867 _WINDOW_ENTRY_ *pEntry = WBGetWindowEntry(wID);
1868 
1869 
1870  if(!pEntry)
1871  return -1;
1872 
1873  pEntry->iModalFlag = 1;
1874  pEntry->iModalReturn = -1;
1875 
1876  WB_DEBUG_PRINT(DebugLevel_Heavy | DebugSubSystem_Window,
1877  "Show Modal %d (%08xH) bMenuSplashFlag=%d\n", (int)wID, (int)wID, bMenuSplashFlag);
1878 
1879  // make sure I'm "transient for" the parent window [always] except for menus
1880  if(bMenuSplashFlag <= 0) // not for menus but splash ok
1881  {
1882  Atom a1, a2;
1883 // Window wParent;
1884 
1885  a1 = XInternAtom(pEntry->pDisplay, "_NET_WM_STATE", False); /* global scope, must use XInternAtom */
1886  a2 = XInternAtom(pEntry->pDisplay, "_NET_WM_STATE_MODAL", False); /* global scope, must use XInternAtom */
1887  XChangeProperty(pEntry->pDisplay, wID, a1, XA_ATOM, 32, PropModePrepend, (unsigned char *)&a2, 1);
1888  // note: Keeping existing properties, and adding my own
1889 
1890 // // setting 'WM_TRANSIENT_FOR' allows me to use the parent window while the dialog is open - I do NOT want that!
1891 // a1 = XInternAtom(pEntry->pDisplay, "WM_TRANSIENT_FOR", False); /* global scope, must use XInternAtom */
1892 // wParent = WBGetParentWindow(wID); NOTE this is not the way to do it, fix elsewhere eh?
1893 // XChangeProperty(pEntry->pDisplay, wID, a1, XA_WINDOW, 32, PropModeReplace, (unsigned char *)&wParent, 1);
1894  }
1895 
1896  XMapRaised(pEntry->pDisplay, wID);
1897 
1898  if(!bMenuSplashFlag) // dialog boxes ONLY
1899  {
1900  WBSetInputFocus(wID); // NOTE: menu popups _MUST_ be declared with 'focus bit' 0, and handled differently
1901  }
1902 
1903  if(bMenuSplashFlag > 0)
1904  {
1905 // WB_ERROR_PRINT("TEMPORARY: %s - grabbing mouse and keyboard\n", __FUNCTION__);
1906 
1907  // ----------------------------
1908  // mouse and keyboard grab time
1909  // ----------------------------
1910 
1911  WBGetWindowGeom2(wID, &geom); // geometry relative to root window
1912 
1914  XGrabPointer(pEntry->pDisplay,wID, 1,
1915  ButtonPressMask | ButtonReleaseMask | ButtonMotionMask | PointerMotionMask
1916  | EnterWindowMask | LeaveWindowMask,
1917  GrabModeAsync, // pointer mode
1918  GrabModeAsync, // keyboard mode
1919  None, None, CurrentTime);
1920  XGrabKeyboard(pEntry->pDisplay, wID, 1,
1921  GrabModeAsync, // pointer mode
1922  GrabModeAsync, // keyboard mode
1923  CurrentTime);
1925  }
1926 // else
1927 // {
1928 // WB_ERROR_PRINT("TEMPORARY: showing modal for NON-menu\n");
1929 // }
1930 
1931 
1932 // WB_ERROR_PRINT("TEMPORARY: %s - begin modal event processing\n", __FUNCTION__);
1933 
1934  iSleepPeriod = MIN_EVENT_LOOP_SLEEP_PERIOD; // initial value, in microseconds
1935 
1936  while(!bQuitFlag)
1937  {
1938  XEvent event;
1939 
1940  // do a mini event loop but only for this window. The event handler should
1941  // be smart enough to handle a condition where I attempt to change focus
1942 
1943  // for future implementation - see XmTrackingEvent for hints on how to implement this properly
1944 
1945  // TODO: modal filtering for window and children of this window
1946 
1947  pEntry = WBGetWindowEntry(wID); // re-assign 'pEntry'
1948 
1949  if(!pEntry)
1950  {
1951  break;
1952  }
1953 
1954  if(!WBIsValid(pEntry->pDisplay, wID) ||
1955  !pEntry->iModalFlag)
1956  {
1957  break;
1958  }
1959 
1960  if(!__InternalCheckGetEvent(pEntry->pDisplay, &event, wID))
1961  {
1962  WBDelay(iSleepPeriod); // 100 microsecs (0.1 milliseconds) initially, grows over time
1963  if(iSleepPeriod < MAX_EVENT_LOOP_SLEEP_PERIOD) // up to 'MAX_EVENT_LOOP_SLEEP_PERIOD' microseconds
1964  {
1965  iSleepPeriod += (iSleepPeriod >> 1); // increase by 50%
1966  }
1967  else
1968  {
1969  iSleepPeriod = MAX_EVENT_LOOP_SLEEP_PERIOD; // the maximum
1970  }
1971 
1972  continue;
1973  }
1974 
1975  iSleepPeriod = MIN_EVENT_LOOP_SLEEP_PERIOD; // reset to the initial value
1976 
1977  // check for application events - these will continue to happen
1978  // even during a modal loop.
1979 
1980  if(event.xany.window == None)
1981  {
1982  // TODO: if messages aren't consistent with 'xany', filter them here.
1983 
1984  WBAppDispatch(&event);
1985  continue;
1986  }
1987 
1988 //#ifndef NO_DEBUG
1989 // WBDebugDumpEvent(&event); // TEMPORARY
1990 //#endif // NO_DEBUG
1991 
1992  // For menus, check for mousie events outside of my window. If this happens
1993  // I want to cancel the modal loop and re-play the event.
1994 
1995  if(bMenuSplashFlag > 0) // menus only
1996  {
1997  if((event.type == ButtonPress || event.type == ButtonRelease) &&
1998  !WBPointInGeom(event.xbutton.x_root, event.xbutton.y_root, geom))
1999  {
2000  WB_DEBUG_PRINT(DebugLevel_Chatty | DebugSubSystem_Mouse | DebugSubSystem_Menu,
2001  "WBShowModal - (menu) mouse button press outside of grab window\n");
2002 
2004  XUngrabPointer(pEntry->pDisplay, CurrentTime);
2005  XUngrabKeyboard(pEntry->pDisplay, CurrentTime);
2006  XAllowEvents(pEntry->pDisplay, ReplayPointer, CurrentTime/*event.xbutton.time*/); // re-play the mousie event
2007 
2008  XPutBackEvent(pEntry->pDisplay, &event); // TODO: correct window ID?
2010 
2011  WBDestroyWindow(wID);
2012  return -1; // "canceled"
2013  }
2014  }
2015  else if(bMenuSplashFlag < 0) // splash screens
2016  {
2017  }
2018  else if(event.xany.window != wID && event.type == FocusIn) // dialog boxes getting focus
2019  {
2020  // in this case it is EXTREMELY important to determine if the main
2021  // window is getting an 'enter focus' notification, since I will need
2022  // to re-assign focus to 'wID' ASAP. Examples are when the user clicks
2023  // on the main window while a dialog box is visible, or if the user attempts
2024  // to change focus to the main window via alt+tab or clicking on an icon in
2025  // the task bar. Therefore, if 'event.xany.window' isn't a child of 'wID'
2026  // I shall call 'SetFocus' on 'wID' and eat the message.
2027  // For this to work, top level windows must allow 'EnterNotify' messages.
2028 
2029  if(!WBIsChildWindow(wID, event.xany.window))
2030  {
2031  Display *pDisplay = pEntry->pDisplay ? pEntry->pDisplay : pDefaultDisplay;
2032 
2033 // fprintf(stderr, "** TEMPORARY: window %08xH 'FocusIn' in modal loop for %08xH\n",
2034 // (unsigned int)event.xany.window, (unsigned int)wID);
2035 
2036  WBPostDelayedSetFocusAppEvent(pDisplay, wID, event.xany.window, 100); // 100 msec delay
2037  // activate, set focus, raise, and map 'wID' (with 'event.xany.window' possibly having HAD focus)
2038  // and the 100 msec delay is to ensure that NO race conditions happen with the window manager
2039 
2040  continue; // old message eaten
2041  }
2042  }
2043 
2044 
2045  // see if this event is intended for the modal window or one of its children
2046  // and if so, I want to dispatch it
2047 
2048  if(event.xany.window == wID ||
2049  event.type == Expose || // always handle expose events
2050  event.type == GraphicsExpose ||
2051 // event.type == NoExpose ||
2052  event.type == DestroyNotify || // always allow destroy notifications
2053  event.type == SelectionRequest ||
2054  event.type == SelectionClear ||
2055  event.type == SelectionNotify ||
2056  (bMenuSplashFlag > 0 && // check for ANY KB+mouse events when NOT a splash window
2057  (event.type == KeyPress || event.type == KeyRelease ||
2058  event.type == ButtonPress || event.type == ButtonRelease ||
2059  event.type == MotionNotify || event.type == EnterNotify ||
2060  event.type == LeaveNotify)) ||
2061  (bMenuSplashFlag >= 0 && WBIsChildWindow(wID, event.xany.window)) || // check for 'is a child of modal' when NOT a splash window
2062  WB_UNLIKELY(event.type == ClientMessage &&
2063  (event.xclient.message_type == aWM_PROTOCOLS || // all of these get through
2064  event.xclient.message_type == aWB_TIMER))) // all timers get through
2065  {
2066  if(event.xany.window != wID && bMenuSplashFlag > 0 && // menu only because I'm capturing keyboard and mouse
2067  (event.type == KeyPress || event.type == KeyRelease || event.type == MotionNotify))
2068  {
2069  if(event.type == MotionNotify)
2070  {
2071  int iX, iY;
2072  // mouse coordinates have been adjusted for the window for which
2073  // it was intended. these must be 'altered'
2074  WBXlatCoordPoint(event.xmotion.window, event.xmotion.x, event.xmotion.y,
2075  wID, &iX, &iY);
2076  event.xmotion.x = iX;
2077  event.xmotion.y = iY;
2078  event.xmotion.window = wID; // assign THIS window's ID for such messages
2079  }
2080  else
2081  {
2082 // Window wParent = WBGetParentWindow(wID);
2083  if(!WBIsChildWindow(wID, event.xany.window))
2084  {
2085  event.xany.window = wID; // assign THIS window's ID for such messages
2086  }
2087  }
2088  }
2089 
2090 #ifndef NO_DEBUG
2091  if(event.xany.window != wID &&
2092  WB_LIKELY(event.type != ClientMessage ||
2093  (event.xclient.message_type != aWB_TIMER && event.xclient.message_type != aWM_PROTOCOLS)))
2094 
2095  {
2096  WB_DEBUG_PRINT(DebugLevel_Chatty | DebugSubSystem_Event | DebugSubSystem_Window,
2097  "%s - event %s for window %d (%08xH) in modal loop\n",
2098  __FUNCTION__,
2099  WBEventName(event.type),
2100  (int)event.xany.window, (int)event.xany.window);
2101  }
2102 
2103  if(event.type == KeyPress || event.type == KeyRelease)
2104  {
2105  WB_DEBUG_PRINT(DebugLevel_Chatty | DebugSubSystem_Keyboard | DebugSubSystem_Event | DebugSubSystem_Window,
2106  "%s - TEMPORARY - processing key press/release within modal loop: %d (%08xH), %d (%08xH)\n",
2107  __FUNCTION__, (int)event.xany.window, (int)event.xany.window, (int)wID, (int)wID);
2108  }
2109 
2110 #endif // NO_DEBUG
2111 
2112  WBWindowDispatch(event.xany.window, &event);
2113  }
2114 #ifndef NO_DEBUG
2115  else
2116  {
2117  // say something for debug purposes about messages that weren't processed
2118 
2119 // WB_DEBUG_PRINT(DebugLevel_ERROR,
2120 // "** TEMPORARY: IGNORING %s in modal loop: %d (%08xH), %d (%08xH)\n",
2121 // __internal_event_type_string(event.type),
2122 // (int)event.xany.window, (int)event.xany.window, (int)wID, (int)wID);
2123 
2124  if(event.type == KeyPress || event.type == KeyRelease)
2125  {
2126  WB_DEBUG_PRINT(DebugLevel_Chatty | DebugSubSystem_Keyboard | DebugSubSystem_Event | DebugSubSystem_Window,
2127  "%s - key press/release in modal loop: %d (%08xH), %d (%08xH)\n",
2128  __FUNCTION__, (int)event.xany.window, (int)event.xany.window, (int)wID, (int)wID);
2129  }
2130  }
2131 #endif // NO_DEBUG
2132 
2133  // other stuff gets ignored
2134 
2135  }
2136 
2137  if(!pEntry)
2138  {
2139  return -1;
2140  }
2141 
2142  if(bMenuSplashFlag > 0) // menus only
2143  {
2145  XUngrabPointer(pEntry->pDisplay, CurrentTime);
2146  XUngrabKeyboard(pEntry->pDisplay, CurrentTime);
2148  }
2149 
2150  iRval = pEntry->iModalReturn;
2151 
2152  WB_DEBUG_PRINT(DebugLevel_Heavy | DebugSubSystem_Window,
2153  "Exit from modal loop, window %d (%08xH) - returning %d (%08xH)\n",
2154  (int)wID, (int)wID, iRval, iRval);
2155 
2156  // destroy the window now that I'm out of the loop
2157 
2158 // WB_ERROR_PRINT("** TEMPORARY - %s - destroying window %d\n", __FUNCTION__,(int)wID);
2159 
2160  WBDestroyWindow(wID);
2161 
2162  return iRval;
2163 }
2164 
2165 void WBEndModal(Window wID, int iRval)
2166 {
2167  _WINDOW_ENTRY_ *pEntry = WBGetWindowEntry(wID);
2168 
2169  if(!pEntry)
2170  return;
2171 
2172  pEntry->iModalFlag = 0;
2173  pEntry->iModalReturn = iRval;
2174 
2175  WB_DEBUG_PRINT(DebugLevel_Chatty | DebugSubSystem_Window,
2176  "WBEndModal - Window %d (%08xH), returning %d (%08xH)\n",
2177  (int)wID, (int)wID, iRval, iRval);
2178 }
2179 
2180 void WBSetInputFocus(Window wID) // set input focus to specific window (revert window is previous focus window)
2181 {
2182  _WINDOW_ENTRY_ *pEntry = WBGetWindowEntry(wID);
2183  Window wIDCurrent;
2184  int iRevert;
2185  Display *pDisp = pEntry ? pEntry->pDisplay : pDefaultDisplay;
2186 
2188  XGetInputFocus(pDisp, &wIDCurrent, &iRevert);
2190 
2191  if(wID != wIDCurrent) // don't have focus
2192  {
2193  _WINDOW_ENTRY_ *pPrev = WBGetWindowEntry(wIDCurrent);
2194  if(pPrev)
2195  {
2196  if(WB_CHECK_SET_FOCUS_ON_MAP(*pPrev))
2197  {
2198  WB_DEBUG_PRINT(DebugLevel_Light | DebugSubSystem_Window,
2199  "%s:%d - %d (%08xH) disabling focus change on map\n",
2200  __FUNCTION__, __LINE__, (int)wIDCurrent, (int)wIDCurrent);
2201 
2202  pPrev->iWindowState = WB_WINDOW_UNMAPPED; // remove 'set focus' state
2203  }
2204  }
2205 
2206  // see if expose event has been done on this yet...
2207  if(WB_LIKELY(pEntry) && //WB_UNLIKELY(!pEntry->width && !pEntry->height && !pEntry->border))
2208  WB_UNLIKELY(WB_IS_WINDOW_UNMAPPED(*pEntry)))
2209  {
2210  WB_DEBUG_PRINT(DebugLevel_Light | DebugSubSystem_Window,
2211  "%s:%d - %d (%08xH) not yet visible, enabling focus change on map\n",
2212  __FUNCTION__, __LINE__, (int)wID, (int)wID);
2213 
2214  pEntry->iWindowState = WB_WINDOW_SET_FOCUS_ON_MAP;
2215  }
2216  else
2217  {
2218  WB_DEBUG_PRINT(DebugLevel_Light | DebugSubSystem_Window,
2219  "%s:%d - %d (%08xH) calling XSetInputFocus\n",
2220  __FUNCTION__, __LINE__, (int)wID, (int)wID);
2221 
2222 // WB_ERROR_PRINT("TEMPORARY - %s calling XSetInputFocus on wID=%u (%08xH)\n", __FUNCTION__, (int)wID, (uint32_t)wID);
2223 
2225  XSetInputFocus(pDisp, wID, RevertToParent, CurrentTime);
2227  }
2228  }
2229 }
2230 
2231 // construction and destruction
2232 
2233 void WBDestroyWindow(Window wID)
2234 {
2235  _WINDOW_ENTRY_ *pEntry = WBGetWindowEntry(wID);
2236  Display *pDisplay = pEntry ? pEntry->pDisplay : pDefaultDisplay;
2237  XEvent event;
2238 
2239  if(pEntry && WB_IS_WINDOW_DESTROYED(*pEntry)) // recursive or extra call
2240  {
2241  WB_WARN_PRINT("%s - Recursive call, Window %d (%08xH)\n",
2242  __FUNCTION__, (int)wID, (int)wID);
2243 
2244  __WBDelWindowPaintEvents(pDisplay, wID); // make sure expose events go away
2245 
2246  return;
2247  }
2248 
2249  if(pEntry && WB_IS_WINDOW_BEING_DESTROYED(*pEntry)) // recursive call in need of XDestroyWindow
2250  {
2251  pEntry->iWindowState = WB_WINDOW_DESTROYED; // initially, do this (in case of extra recursion)
2252 
2253  WB_ERROR_PRINT("INFO: %s recursively destroying window %d\n", __FUNCTION__, (int)wID);
2254 
2256  XDestroyWindow(pDisplay, wID); // handler should clean up the rest
2257  // NOTE: this _CAN_ recurse in some cases
2258 // XFlush(pDisplay);
2259  XSync(pDisplay, False); // wait for server to actually do it - specific to debug output, really
2261 
2262  __WBDelWindowPaintEvents(pDisplay, wID); // make sure expose events go away
2263 
2264  return;
2265  }
2266 
2267  WB_DEBUG_PRINT(DebugLevel_Chatty | DebugSubSystem_Window,
2268  "%s - Window %d (%08xH) class %s\n",
2269  __FUNCTION__, (int)wID, (int)wID, pEntry ? (pEntry->szClassName ? pEntry->szClassName : "NULL") : "*Unk*");
2270 
2271  // get all events for this window and remove them
2272 
2274  XSync(pDisplay, FALSE); // force a sync first
2276 
2277  while(XCheckWindowEvent(pDisplay, wID, EVENT_ALL_MASK, &event))
2278  {
2279 // WBDelay(1000); // force sleep during loop?
2280  }
2281 
2282  if(!pEntry)
2283  {
2284  WB_WARN_PRINT("WARNING: WBDestroyWindow for wID not in list - window already destroyed?\n");
2285 
2286  XSync(pDisplay, 0);
2287 
2288  __WBDelWindowEvents(pDefaultDisplay, wID);
2289 
2290  WB_ERROR_PRINT("INFO: %s destroying UN-REGISTERED window %d\n", __FUNCTION__, (int)wID);
2291 
2292 // BEGIN_XCALL_DEBUG_WRAPPER
2293 // XDestroyWindow(pDefaultDisplay, wID);
2294 // END_XCALL_DEBUG_WRAPPER
2295  __InternalDestroyWindow(pDisplay, wID, NULL);
2296  }
2297  else
2298  {
2299  // TODO: should I send a notification first?
2300 
2301  WB_DEBUG_PRINT(DebugLevel_Heavy | DebugSubSystem_Window,
2302  "WBDestroyWindow - wID %d (%08xH)\n",
2303  (int)wID, (int)wID);
2304 
2305  // I'll want to process some additional events at this point, JUST for the
2306  // window and its children
2307 
2309  XSync(pDisplay, 0);
2311 
2312 // NOTE: I used to dispatch remaining events, but this might dispatch EXPOSE and I don't want that
2313 // while(XCheckWindowEvent(pDisplay, wID, EVENT_ALL_MASK, &event))
2314 // {
2315 // WBWindowDispatch(wID, &event);
2316 // }
2317 
2318  WB_DEBUG_PRINT(DebugLevel_Excessive | DebugSubSystem_Window,
2319  "WBDestroyWindow - wID %d (%08xH), display flushed\n",
2320  (int)wID, (int)wID);
2321 
2322  if(WB_IS_WINDOW_MAPPED(*pEntry))
2323  {
2324  WB_DEBUG_PRINT(DebugLevel_Excessive | DebugSubSystem_Window,
2325  "WBDestroyWindow - wID %d (%08xH), calling XDestroyWindow\n",
2326  (int)wID, (int)wID);
2327 
2328  __InternalDestroyWindow(pDisplay, wID, pEntry); // may mark it 'DESTROYING' so recursion won't happen (see above)
2329 
2330 // if(!WB_IS_WINDOW_DESTROYED(*pEntry))
2331 // {
2332 // pEntry->iWindowState = WB_WINDOW_DESTROYED; // initially, do this (in case of recursion)
2333 //
2334 // WB_ERROR_PRINT("** WARNING: %s destroying window %d (marked it 'destroyed') "/*X errors ignored*/"\n", __FUNCTION__, (int)wID);
2335 //
2337 // BEGIN_XCALL_DEBUG_WRAPPER
2338 // XDestroyWindow(pDisplay, wID); // handler should clean up the rest
2339 // // NOTE: this _CAN_ recurse in some cases
2341 // XSync(pDisplay, False);
2342 // END_XCALL_DEBUG_WRAPPER
2344 // }
2345  }
2346  else if(!WB_IS_WINDOW_BEING_DESTROYED(*pEntry) && !WB_IS_WINDOW_DESTROYED(*pEntry))
2347  {
2348  WB_DEBUG_PRINT(DebugLevel_Excessive | DebugSubSystem_Window,
2349  "WBDestroyWindow - wID %d (%08xH), destroying 'unmapped' window\n",
2350  (int)wID, (int)wID);
2351 
2352  __InternalDestroyWindow(pDisplay, wID, pEntry); // may mark it 'DESTROYING' so recursion won't happen (see above)
2353  }
2354  // NOTE: the above 'if' may preclude this next one from EVAR happening
2355  else if(!WB_TO_DELETE_WINDOW_ENTRY(*pEntry)) // has not called 'WBUnregisterWindowCallback'
2356  {
2357  WB_DEBUG_PRINT(DebugLevel_Excessive | DebugSubSystem_Window,
2358  "WBDestroyWindow - wID %d (%08xH), Marking 'unmapped' window 'destroyed'\n",
2359  (int)wID, (int)wID);
2360 
2361  pEntry->iWindowState = WB_WINDOW_DESTROYED; // initially, do this
2362  }
2363 
2364  __WBDelWindowEvents(pDisplay, wID);
2365 
2366  if(!WB_TO_DELETE_WINDOW_ENTRY(*pEntry)) // in case 'Destroy' notification didn't make this happen
2367  {
2368  WB_DEBUG_PRINT(DebugLevel_WARN | DebugSubSystem_Window,
2369  "WARNING - %s - last ditch attempt, unregistering window callback for window %d\n", __FUNCTION__, (int)wID);
2371  }
2372  }
2373 }
2374 
2375 
2376 // application callback registration
2377 
2379 {
2380  pAppEventCallback = pCallback;
2381 }
2382 
2384 {
2385  pAppEventCallback = NULL;
2386 }
2387 
2388 
2389 /**********************************************************************/
2390 /* */
2391 /* window entry array processing */
2392 /* */
2393 /**********************************************************************/
2394 
2395 
2396 static __inline__ void InternalRestoreWindowDefaults(int iIndex)
2397 {
2398  sWBHashEntries[iIndex].szClassName = NULL; // no class name, initially
2399  sWBHashEntries[iIndex].wParent = 0; // mark it as "unassigned"
2400  sWBHashEntries[iIndex].idCursor = WB_DEFAULT_CURSOR;
2401  sWBHashEntries[iIndex].idDefaultCursor = WB_DEFAULT_CURSOR;
2402  sWBHashEntries[iIndex].iWaitCursorCount = 0;
2403  sWBHashEntries[iIndex].curRecent = None;
2404  bzero(&(sWBHashEntries[iIndex].geomAbsolute), sizeof(sWBHashEntries[iIndex].geomAbsolute));
2405  sWBHashEntries[iIndex].rgnClip = 0;
2406  sWBHashEntries[iIndex].rgnPaint = 0;
2407 
2408  sWBHashEntries[iIndex].iModalFlag = 0;
2409  sWBHashEntries[iIndex].iModalReturn = -1;
2410 
2411  sWBHashEntries[iIndex].width = sWBHashEntries[iIndex].height
2412  = sWBHashEntries[iIndex].border = 0; // make sure these are zero as well (indicates 'not yet visible')
2413 
2414 
2415  // this is primarily for when I unregister the callback (I must also unregister the menu)
2416  sWBHashEntries[iIndex].wIDMenu = 0;
2417  sWBHashEntries[iIndex].pMenuCallback = 0;
2418  sWBHashEntries[iIndex].iWindowState = WB_WINDOW_UNMAPPED;
2419 
2420  sWBHashEntries[iIndex].eWindowType = WMPropertiesWindowType_Normal; // TODO: notify window manager?
2421  sWBHashEntries[iIndex].eWMProtocols = WMPropertiesWMProtocols_None; // TODO: notify window manager?
2422 
2423  // zero out the window data (TODO: zero out everything?)
2424  bzero(sWBHashEntries[iIndex].aWindowData, sizeof(sWBHashEntries[iIndex].aWindowData));
2425 }
2426 
2427 //static /*__inline*/ WBWindow WBWindowFromWindow(Window wID)
2428 //{
2429 // return (WBWindow)((unsigned long long)wID | WBWINDOW_FLAGS_NORMAL);
2430 //}
2431 
2432 //static /*__inline*/ Window WBWindowToWindow(WBWindow wID)
2433 //{
2434 // return (Window)(wID & WBWINDOW_MASK);
2435 //}
2436 
2437 
2438 // WBGetWindowEntry - the debug version indicates what the caller's line number and function are
2439 
2440 #ifdef NO_DEBUG
2441 static /*__inline__*/ _WINDOW_ENTRY_ *WBGetWindowEntry(Window wID)
2442 #else
2443 #define WBGetWindowEntry(X) Debug_WBGetWindowEntry(X, __FUNCTION__, __LINE__)
2444 static /*__inline__*/ _WINDOW_ENTRY_ *Debug_WBGetWindowEntry(Window wID, const char *szFunction, int nLine)
2445 #endif // NO_DEBUG
2446 {
2447  int i1, iStart;
2448 
2449  if(!nWBHashEntries)
2450  {
2451 #ifndef NO_DEBUG
2452  WB_DEBUG_PRINT(DebugLevel_Light | DebugSubSystem_Window,
2453  "%s:%d - Window hash table empty, %d (%08xH) not found\n",
2454  szFunction, nLine, (int)wID, (int)wID);
2455 #endif // NO_DEBUG
2456  return(NULL);
2457  }
2458 
2459  i1 = iStart = WINDOW_ENTRY_HASH(wID);
2460 
2461  while(sWBHashEntries[i1].wID) // this must be assigned to zero for this to work properly
2462  {
2463  if(sWBHashEntries[i1].wID == wID)
2464  {
2465  gettimeofday(&(sWBHashEntries[i1].tvLastActivity), NULL);
2466  return(sWBHashEntries + i1);
2467  }
2468 
2469  i1++;
2470  i1 &= WINDOW_ENTRY_ARRAY_MAX;
2471 
2472  if(i1 == iStart)
2473  break;
2474  }
2475 
2476 #ifndef NO_DEBUG
2477  WB_DEBUG_PRINT(DebugLevel_Light | DebugSubSystem_Window,
2478  "%s:%d - Window ID %d (%08xH) not found\n",
2479  szFunction, nLine, (int)wID, (int)wID);
2480 #endif // NO_DEBUG
2481 
2482  return(NULL);
2483 }
2484 
2485 // window callback registration - this also adds a window entry if one does
2486 // not already exist for the specified window ID. This function assumes
2487 // that the window's display is the default display.
2488 
2489 static _WINDOW_ENTRY_ *__AddOrLocateEntry(Window wID)
2490 {
2491 register int i1, i2, iStart;
2492 _WINDOW_ENTRY_ *pRval = NULL;
2493 
2494 
2495  i2 = -1; // 'unused' entry marker
2496  i1 = WINDOW_ENTRY_HASH(wID);
2497 
2498  if(!nWBHashEntries)
2499  {
2500  WB_DEBUG_PRINT(DebugLevel_Excessive | DebugSubSystem_Window,
2501  "__AddOrLocateEntry - zero out window hash/array since it's empty\n");
2502  bzero(sWBHashEntries, sizeof(sWBHashEntries));
2503  }
2504  else
2505  {
2506  // keep track of the one I start with as 'iStart'
2507 
2508  iStart = i1;
2509 
2510  while(sWBHashEntries[i1].wID) // the array must be pre-assigned to zero for this to work properly
2511  {
2512  if(sWBHashEntries[i1].wID == wID)
2513  {
2514  pRval = sWBHashEntries + i1;
2515  break;
2516  }
2517 
2518  if(sWBHashEntries[i1].wID == WINDOW_ENTRY_UNUSED)
2519  {
2520  i2 = i1; // keep track of the 1st unused entry in the chain
2521  }
2522 
2523  i1++;
2524  i1 &= WINDOW_ENTRY_ARRAY_MAX;
2525 
2526  if(i1 == iStart)
2527  {
2528  i1 = -1; // error flag (hash table full)
2529  break;
2530  }
2531  }
2532  }
2533 
2534  // if pRval is NULL, I need to add the entry
2535  // in that case, either i2 or i1 points to it.
2536  // if both i1 and i2 are negative, it's an error
2537  // The number of active entries is limited to 3/4
2538  // of the hash table size for speed and efficiency.
2539 
2540  if(!pRval)
2541  {
2542  if(i1 < 0 && i2 < 0)
2543  return NULL;
2544 
2545  // --------------------------
2546  // HASH TABLE SIZE LIMITATION
2547  // --------------------------
2548 
2549  // if 4 times the # of entries is larger than 3 times the # of hash entries,
2550  // then the hash is 3/4 full and grossly inefficient. This is my limit.
2551 
2552  if((nWBHashEntries * 4) > (3 * sizeof(sWBHashEntries)/sizeof(sWBHashEntries[0]))) // an efficient algorithm
2553  return NULL; // enforce hash table twice maximum
2554 
2555  nWBHashEntries++; // keep track
2556 
2557  if(i2 >= 0)
2558  i1 = i2;
2559 
2560  pRval = sWBHashEntries + i1;
2561  pRval->wID = wID; // mark it "mine"
2562  pRval->pDisplay = pDefaultDisplay; // for now - TODO get the real display
2563  pRval->pImage = NULL; // pre-assign the cached image to NULL
2564 
2565  InternalRestoreWindowDefaults(i1);
2566 
2567  WBRestoreDefaultCursor(wID); // assign the default cursor for a new entry
2568  }
2569 
2570  if(pRval)
2571  {
2572  gettimeofday(&(pRval->tvLastActivity), NULL);
2573  }
2574 
2575  return pRval;
2576 }
2577 
2578 static void __WindowEntryRestoreDefaultResources(int iIndex)
2579 {
2580  Display *pDisp = pDefaultDisplay;
2581 
2582  if(sWBHashEntries[iIndex].pDisplay)
2583  {
2584  pDisp = sWBHashEntries[iIndex].pDisplay;
2585  }
2586 
2588 // if(sWBHashEntries[iIndex].fontSet != None && sWBHashEntries[iIndex].fontSet != fontsetDefault) // must delete it
2589 // {
2590 // XFreeFontSet(pDisp, sWBHashEntries[iIndex].fontSet);
2591 // sWBHashEntries[iIndex].fontSet = None;
2592 // }
2593 // if(sWBHashEntries[iIndex].pFontStruct && sWBHashEntries[iIndex].pFontStruct != pDefaultFont) // must delete it
2594 // {
2595 // XFreeFont(pDisp, sWBHashEntries[iIndex].pFontStruct);
2596 // sWBHashEntries[iIndex].pFontStruct = NULL;
2597 // }
2598  if(sWBHashEntries[iIndex].pFont && sWBHashEntries[iIndex].pFont != pDefaultFont) // must delete it
2599  {
2600  WBFreeFont(pDisp, sWBHashEntries[iIndex].pFont);
2601  sWBHashEntries[iIndex].pFont = NULL;
2602  }
2603  if(sWBHashEntries[iIndex].pxIcon)
2604  {
2605  XFreePixmap(pDisp, sWBHashEntries[iIndex].pxIcon);
2606  sWBHashEntries[iIndex].pxIcon = 0;
2607  }
2608  if(sWBHashEntries[iIndex].pxMask)
2609  {
2610  XFreePixmap(pDisp, sWBHashEntries[iIndex].pxMask);
2611  sWBHashEntries[iIndex].pxMask = 0;
2612  }
2613  if(sWBHashEntries[iIndex].pWMHints)
2614  {
2615  XFree(sWBHashEntries[iIndex].pWMHints);
2616  sWBHashEntries[iIndex].pWMHints = NULL;
2617  }
2618  if(sWBHashEntries[iIndex].curRecent != None)
2619  {
2620  XFreeCursor(pDisp, sWBHashEntries[iIndex].curRecent);
2621  sWBHashEntries[iIndex].curRecent = None;
2622  }
2623  if(sWBHashEntries[iIndex].rgnClip != 0)
2624  {
2625  XDestroyRegion(sWBHashEntries[iIndex].rgnClip);
2626  sWBHashEntries[iIndex].rgnClip = 0;
2627  }
2628  if(sWBHashEntries[iIndex].rgnPaint != 0)
2629  {
2630  WB_WARN_PRINT("WARNING: paint region non-zero in __WindowEntryRestoreDefaultResources\n");
2631  XDestroyRegion(sWBHashEntries[iIndex].rgnPaint);
2632  sWBHashEntries[iIndex].rgnPaint = 0;
2633  }
2634  if(sWBHashEntries[iIndex].pImage != NULL)
2635  {
2636  XDestroyImage(sWBHashEntries[iIndex].pImage);
2637  sWBHashEntries[iIndex].pImage = NULL;
2638  }
2640 
2641 }
2642 
2643 static void __WindowEntryDestructor(int iIndex)
2644 {
2645 int iPrev, iNext;
2646 
2647  __WindowEntryRestoreDefaultResources(iIndex); // make sure no resources are allocated
2648 
2649  sWBHashEntries[iIndex].pCallback = 0;
2650  sWBHashEntries[iIndex].pDisplay = NULL; // must happen AFTER restoring default resources
2651 
2652  // restore these and additional default values
2653  InternalRestoreWindowDefaults(iIndex);
2654 
2656  // _ _ _ _____ _ _ ____ _ //
2657  // | | | | __ _ ___| |__ |_ _|_ _| |__ | | ___ / ___| | ___ __ _ _ __ _ _ _ __ //
2658  // | |_| |/ _` / __| '_ \ | |/ _` | '_ \| |/ _ \ | | | |/ _ \/ _` | '_ \| | | | '_ \ //
2659  // | _ | (_| \__ \ | | | | | (_| | |_) | | __/ | |___| | __/ (_| | | | | |_| | |_) | //
2660  // |_| |_|\__,_|___/_| |_| |_|\__,_|_.__/|_|\___| \____|_|\___|\__,_|_| |_|\__,_| .__/ //
2661  // |_| //
2663 
2664  // NOW, I have to 'zero out' or mark the entry 'unused'. To do this I
2665  // have to define what an unused window ID is ('WINDOW_ENTRY_UNUSED').
2666  // (this MUST happen or the hash table won't work)
2667  // there are a couple of cases where deleting an entry might break a 'chain'
2668  // As a result I need to ensure that I make use of 'unused' markers and only
2669  // remove them when the final entry in the chain is followed by a 'NULL'.
2670  //
2671  // Preceding entries that aren't 'NULL' indicate I am part of a 'chain' and
2672  // so I can't mark it 'NULL' if this entry isn't the last entry in the 'chain'
2673  // or the 'chain' breaks. Therefore it will be marked 'unused'.
2674 
2675  iPrev = (iIndex - 1) & WINDOW_ENTRY_ARRAY_MAX;
2676  iNext = (iIndex + 1) & WINDOW_ENTRY_ARRAY_MAX;
2677 
2678  if(!sWBHashEntries[iNext].wID) // next isn't NULL so I'm the end of the chain
2679  {
2680  sWBHashEntries[iIndex].wID = 0; // it's safe to do this now
2681 
2682  for(iIndex=iPrev; iIndex != iNext && sWBHashEntries[iIndex].wID == WINDOW_ENTRY_UNUSED;
2683  iIndex = (iIndex - 1) & WINDOW_ENTRY_ARRAY_MAX) // roundie roundie
2684  {
2685  sWBHashEntries[iIndex].wID = 0; // zero out back up the chain until I hit something that's not 'unused'
2686  }
2687  }
2688  else
2689  {
2690  if(!sWBHashEntries[iPrev].wID) // previous is NULL
2691  {
2692  // I'm not a chain. I can mark this zero
2693  sWBHashEntries[iIndex].wID = 0; // just zero it out (the normal case)
2694  }
2695  else
2696  {
2697  sWBHashEntries[iIndex].wID = WINDOW_ENTRY_UNUSED; // mark this as "unused" but not the end of a chain
2698  }
2699  }
2700 
2701  nWBHashEntries--;
2702 }
2703 
2704 static void __PeriodicWindowEntryCleanup(void)
2705 {
2706 int i1;
2707 struct timeval tvNow;
2708 static struct timeval tvLastTime = {0,0};
2709 
2710  gettimeofday(&tvNow, NULL);
2711 
2712  if(tvNow.tv_sec == tvLastTime.tv_sec)
2713  {
2714  return; // so I don't do this too often - only once per second
2715  }
2716 
2717  tvLastTime.tv_sec = tvNow.tv_sec;
2718 
2719  tvNow.tv_sec -= WB_WINDOW_DELETE_TIMEOUT;
2720 
2721  // for each remaining window in my list, destroy it
2722  // TODO: a more intelligent way to do this. Currently it loops 2048 times
2723  // and this is NOT efficient. however, at 1Ghz, 2048 loops should easily
2724  // be less than 1 msec (maybe 10 to 20 cycles per loop on average).
2725  for(i1=WINDOW_ENTRY_ARRAY_MAX - 1; i1 >= 0; i1--)
2726  {
2727  Window wID = sWBHashEntries[i1].wID;
2728 
2729  if(!wID || wID == WINDOW_ENTRY_UNUSED)
2730  {
2731  continue;
2732  }
2733 
2734  if(sWBHashEntries[i1].iWindowState == WB_WINDOW_DELETE &&
2735  sWBHashEntries[i1].tvLastActivity.tv_sec < tvNow.tv_sec) // aged enough?
2736  {
2737  WB_DEBUG_PRINT(DebugLevel_Light | DebugSubSystem_Window,
2738  "%s - destroying entry %d for window %u (%08xH)\n",
2739  __FUNCTION__, i1,
2740  (int)sWBHashEntries[i1].wID, (int)sWBHashEntries[i1].wID);
2741 
2742 
2743 // WB_ERROR_PRINT("TEMPORARY: %s - delete window entry %d for window %u (%08xH)\n",
2744 // __FUNCTION__, i1,
2745 // (int)sWBHashEntries[i1].wID, (int)sWBHashEntries[i1].wID);
2746 
2747 // __WindowEntryRestoreDefaultResources(i1); // make sure no resources are allocated - actually next function does this already
2748  __WindowEntryDestructor(i1); // finalizes destruction and marks it 'unused'
2749  }
2750  }
2751 }
2752 
2753 Window WBCreateWindow(Display *pDisplay, Window wIDParent,
2754  WBWinEvent pProc, const char *szClass,
2755  int iX, int iY, int iWidth, int iHeight, int iBorder, int iIO,
2756  WB_UINT64 iFlags, XSetWindowAttributes *pXSWA)
2757 {
2758 Window idRval;
2759 _WINDOW_ENTRY_ *pEntry;
2760 WB_GEOM geom;
2761 unsigned long valuemask;
2762 XSetWindowAttributes xswa;
2763 
2764 
2765  if(!pDisplay)
2766  {
2767  pDisplay = WBGetDefaultDisplay();
2768 
2769  if(!pDisplay) // in case the library wasn't initialized
2770  {
2771  return None;
2772  }
2773  }
2774 
2775 // POOBAH
2776 #ifdef HAS_WB_UINT64_BUILTIN
2777  valuemask = (unsigned long)(iFlags & 0xffffffffL);
2778 #else // HAS_WB_UINT64_BUILTIN
2779  valuemask = iFlags.dw2; // assume low endian (for now)
2780 #endif // HAS_WB_UINT64_BUILTIN
2781 
2782  if(!valuemask)
2783  {
2784  valuemask = CWBorderPixel | CWBackPixel | CWColormap | CWBitGravity; // for now...
2785  if(!pXSWA)
2786  {
2787  pXSWA = &xswa;
2788 
2789  WBInitWindowAttributes(&xswa,
2790  BlackPixel(pDisplay, DefaultScreen(pDisplay)),
2791  WhitePixel(pDisplay, DefaultScreen(pDisplay)),
2792  DefaultColormap(pDisplay, DefaultScreen(pDisplay)),
2793  CenterGravity);
2794  }
2795  }
2796 
2797  idRval = XCreateWindow(pDisplay,
2798  wIDParent != None ? wIDParent : DefaultRootWindow(pDisplay),
2799  iX, iY, iWidth, iHeight, iBorder,
2800  DefaultDepth(pDisplay, DefaultScreen(pDisplay)),
2801  iIO,
2802  DefaultVisual(pDisplay, DefaultScreen(pDisplay)),
2803  valuemask,
2804  pXSWA);
2805 
2806  if(idRval != None)
2807  {
2808  WBRegisterWindowCallback(idRval, pProc); // this must happen first (it creates the internal structures)
2809  WBSetWindowClassName(idRval, szClass);
2810 
2811  if(wIDParent != None)
2812  {
2813  WBSetParentWindow(idRval, wIDParent); // assign the cached copy of the window's parent
2814  }
2815 
2816  pEntry = WBGetWindowEntry(idRval);
2817 
2818  if(!pEntry)
2819  {
2820  WB_ERROR_PRINT("ERROR: %s - unable to get window entry (window still created)\n", __FUNCTION__);
2821  }
2822  else
2823  {
2824  pEntry->iFlags = iFlags; // cache it
2825  pEntry->pDisplay = pDisplay; // make sure
2826 
2827  if(!pEntry->pWMHints) // normally THIS WILL BE THE CASE
2828  {
2829  // if no window hints were configured, set some up
2830 
2831  if(!__internal_alloc_WMHints(pEntry))
2832  {
2834  XSetWMHints(pDisplay, idRval, pEntry->pWMHints);
2836  }
2837  }
2838 
2839  geom.x = 0; // client area always begins at 0,0
2840  geom.y = 0;
2841  geom.width = iWidth;
2842  geom.height = iHeight;
2843  geom.border = iBorder;
2844 
2845  WBInvalidateGeom(idRval, &geom, 0); // invalidate but don't paint yet
2846  }
2847  }
2848 
2849  return idRval;
2850 }
2851 
2852 void WBSetWMProperties(Window wID, const char *szTitle, XSizeHints *pNormalHints,
2853  XWMHints *pWMHints, XClassHint *pClassHints)
2854 {
2855 _WINDOW_ENTRY_ *pEntry = WBGetWindowEntry(wID);
2856 Display *pDisplay = pEntry ? pEntry->pDisplay : pDefaultDisplay;
2857 XTextProperty xTextProp;
2858 // XSetStandardProperties(pDisplay, pNew->wbFW.wID, szTitle, szTitle, None,
2859 // NULL, 0, NULL); // argv, argc, &xsh);
2860 // // this has been superseded by XSetWMProperties() and should not be used any more
2861 
2862 // typedef struct {
2863 // unsigned char *value;/* property data */
2864 // Atom encoding; /* type of property */
2865 // int format; /* 8, 16, or 32 */
2866 // unsigned long nitems;/* number of items in value */
2867 // } XTextProperty;
2868 
2869  if(szTitle)
2870  {
2871  xTextProp.value = (void *)szTitle;
2872  xTextProp.encoding = XA_STRING;
2873  xTextProp.format = 8; // 8-bit bytes in this one
2874  xTextProp.nitems = strlen(szTitle);
2875  }
2876 
2877  if(pWMHints)
2878  {
2879  if(!pEntry->pWMHints)
2880  {
2881  // if no window hints were configured, set some up
2882 
2883  if(__internal_alloc_WMHints(pEntry))
2884  {
2885  // TODO: error??
2886  }
2887  }
2888 
2889  if(pEntry->pWMHints)
2890  {
2891  // combine the hints!
2892 
2893  if(pWMHints->flags & InputHint)
2894  {
2895  pEntry->pWMHints->input = pWMHints->input;
2896  pEntry->pWMHints->flags |= InputHint;
2897  }
2898 
2899  if(pWMHints->flags & StateHint)
2900  {
2901  pEntry->pWMHints->initial_state = pWMHints->initial_state;
2902  pEntry->pWMHints->flags |= StateHint;
2903  }
2904 
2905  if(pWMHints->flags & IconPixmapHint)
2906  {
2908  if(pEntry->pxIcon != None)
2909  {
2910  XFreePixmap(pDisplay, pEntry->pxIcon);
2911 // pEntry->pxIcon = None;
2912  }
2914 
2915  pEntry->pxIcon = pWMHints->icon_pixmap;
2916  pEntry->pWMHints->icon_pixmap = pWMHints->icon_pixmap;
2917  pEntry->pWMHints->flags |= IconPixmapHint;
2918  }
2919  else if(pEntry->pxIcon != None &&
2920  (!(pEntry->pWMHints->flags & IconPixmapHint) || pEntry->pWMHints->icon_pixmap == None))
2921  {
2922  pEntry->pWMHints->icon_pixmap = pEntry->pxIcon;
2923  pEntry->pWMHints->flags |= IconPixmapHint;
2924  }
2925 
2926  if(pWMHints->flags & IconMaskHint)
2927  {
2929  if(pEntry->pxMask != None) // this is where I keep track of it
2930  {
2931  XFreePixmap(pDisplay, pEntry->pxMask);
2932 // pEntry->pxMask = None;
2933  }
2935 
2936  pEntry->pxMask = pWMHints->icon_mask;
2937  pEntry->pWMHints->icon_mask = pWMHints->icon_mask;
2938  pEntry->pWMHints->flags |= IconMaskHint;
2939  }
2940  else if(pEntry->pxMask != None &&
2941  (!(pEntry->pWMHints->flags & IconMaskHint) || pEntry->pWMHints->icon_mask == None))
2942  {
2943  pEntry->pWMHints->icon_mask = pEntry->pxMask;
2944  pEntry->pWMHints->flags |= IconMaskHint;
2945  }
2946 
2947  if(pWMHints->flags & IconWindowHint)
2948  {
2949  pEntry->pWMHints->icon_window = pWMHints->icon_window;
2950  pEntry->pWMHints->flags |= IconWindowHint;
2951  }
2952 
2953  if(pWMHints->flags & IconPositionHint)
2954  {
2955  pEntry->pWMHints->icon_x = pWMHints->icon_x;
2956  pEntry->pWMHints->icon_y = pWMHints->icon_y;
2957  pEntry->pWMHints->flags |= IconPositionHint;
2958  }
2959 
2960  if(pWMHints->flags & WindowGroupHint)
2961  {
2962  pEntry->pWMHints->window_group = pWMHints->window_group;
2963  pEntry->pWMHints->flags |= WindowGroupHint;
2964  }
2965  }
2966  }
2967 
2969  if(szTitle)
2970  {
2971  XSetWMProperties(pDisplay, wID, &xTextProp, &xTextProp, NULL, 0,
2972  pNormalHints,
2973  pEntry->pWMHints ? pEntry->pWMHints : pWMHints,
2974  pClassHints);
2975  }
2976  else
2977  {
2978  XSetWMProperties(pDisplay, wID, NULL, NULL, NULL, 0,
2979  pNormalHints,
2980  pEntry->pWMHints ? pEntry->pWMHints : pWMHints,
2981  pClassHints);
2982  }
2984 
2985  if(pWMHints)
2986  {
2987  if(pEntry->pWMHints)
2988  {
2989  XFree(pEntry->pWMHints);
2990  }
2991 
2993  pEntry->pWMHints = XGetWMHints(pDisplay, wID);
2995  }
2996 
2997 // void XSetWMProperties(Display *display, Window w,
2998 // XTextProperty *window_name, XTextProperty *icon_name,
2999 // char **argv, int argc,
3000 // XSizeHints *normal_hints, XWMHints *wm_hints,
3001 // XClassHint *class_hints);
3002 }
3003 
3004 void WBSetWindowTitle(Window wID, const char *szTitle)
3005 {
3006 _WINDOW_ENTRY_ *pEntry = WBGetWindowEntry(wID);
3007 Display *pDisplay = pEntry ? pEntry->pDisplay : pDefaultDisplay;
3008 Atom a1, a2;
3009 
3010 
3011  a1 = XInternAtom(pDisplay, "WM_NAME", False); /* global scope, must use XInternAtom */
3012  a2 = XInternAtom(pDisplay, "WM_ICON_NAME", False); /* global scope, must use XInternAtom */
3013 
3014  if(a1 != None)
3015  {
3017  XChangeProperty(pDisplay, wID, a1, XA_STRING, 8, PropModeReplace, (unsigned char *)szTitle, strlen(szTitle));
3019  }
3020 
3021  if(a2 != None)
3022  {
3024  XChangeProperty(pDisplay, wID, a2, XA_STRING, 8, PropModeReplace, (unsigned char *)szTitle, strlen(szTitle));
3026  }
3027 }
3028 
3029 void WBRegisterWindowCallback(Window wID, WBWinEvent pCallback)
3030 {
3031 _WINDOW_ENTRY_ *pEntry = __AddOrLocateEntry(wID);
3032 
3033  if(pEntry)
3034  {
3035  int bNewEntry = 0;
3036 
3037  if(!pEntry->pCallback)
3038  {
3039  bNewEntry = 1;
3040  }
3041 
3042  pEntry->pCallback = pCallback;
3043 
3044  if(bNewEntry)
3045  {
3046  WBRestoreDefaultCursor(wID); // assign the default cursor if this is a new entry
3047  }
3048  }
3049 }
3050 
3052 {
3053  int i1, /*iPrev, iNext,*/ iStart;
3054 
3055  if(wID == wIDApplication)
3056  {
3057  wIDApplication = None; // application window being unregistered
3058  }
3059 
3060  if(!nWBHashEntries)
3061  {
3062  WB_DEBUG_PRINT(DebugLevel_Excessive | DebugSubSystem_Window,
3063  "%s - nWBHashEntries == NULL\n", __FUNCTION__);
3064  return;
3065  }
3066 
3067  i1 = iStart = WINDOW_ENTRY_HASH(wID);
3068 
3069  while(sWBHashEntries[i1].wID) // the array must be pre-assigned to zero for this to work properly
3070  {
3071  if(sWBHashEntries[i1].wID == wID)
3072  break;
3073 
3074  i1++;
3075  i1 &= WINDOW_ENTRY_ARRAY_MAX;
3076 
3077  if(i1 == iStart)
3078  {
3079  i1 = -1; // as a flag that I didn't find it
3080  break;
3081  }
3082  }
3083 
3084  // free resources, mark the 'last activity' time, and
3085  // mark this entry as "to be destroyed"
3086 
3087  // NOTE: used to NOT actually change the callback address (TODO: change name of function?)
3088  // but in this case I'm going to NULL it. I don't want window callbacks being called
3089 
3090  if(i1 >= 0 && sWBHashEntries[i1].wID == wID) // guarantees I have the right one
3091  {
3092  // look through all of the timers and unregister any that involve this window
3093  // this is to avoid certain problems where windows aren't being notified properly
3094 
3095  DeletAllTimersForWindow(sWBHashEntries[i1].pDisplay, wID);
3096 
3097  if(sWBHashEntries[i1].iWindowState != WB_WINDOW_DELETE)
3098  {
3099  WB_DEBUG_PRINT(DebugLevel_Medium | DebugSubSystem_Window,
3100  "Entry %d for window %d (%08xH) marked as 'to be destroyed'\n",
3101  i1, (int)wID, (int)wID);
3102 
3103 // __WindowEntryDestructor(i1);
3104  __WindowEntryRestoreDefaultResources(i1); // make sure no resources are allocated
3105 
3106  gettimeofday(&(sWBHashEntries[i1].tvLastActivity), NULL);
3107 
3108  sWBHashEntries[i1].iWindowState = WB_WINDOW_DELETE;
3109  }
3110 
3111  sWBHashEntries[i1].pCallback = NULL; // no more callback function. 'DestroyNotify' may still happen.
3112  // for 'DestroyNotify' events following this, WBDispatch will deal with that.
3113  }
3114 
3115 }
3116 
3117 // this one is ONLY called before mapping the window
3119 {
3120 
3121  // TODO: implement this
3122  // see _NET_WM_WINDOW_TYPE and _NET_WM_STATE atom documentation, wm-spec-latest.html
3123 
3124 }
3125 
3126 // this one is called AFTER mapping the window. The 'root' window
3127 // will have to be notified of the changes being made.
3128 void WBChangeWMPropertiesWindowType(Window wID, enum WMPropertiesWindowType wmProp, enum WMPropertiesWindowType wmChangeMask)
3129 {
3130 
3131  // TODO: implement this
3132  // see _NET_WM_WINDOW_TYPE and _NET_WM_STATE atom documentation, wm-spec-latest.html
3133 
3134 }
3135 
3137 {
3138 _WINDOW_ENTRY_ *pEntry = WBGetWindowEntry(wID);
3139 
3140 
3141  if(!pEntry)
3142  {
3143  return WMPropertiesWindowType_Normal;
3144  }
3145 
3146  return pEntry->eWindowType;
3147 }
3148 
3149 // this is a MUCH nicer way of handling variable length lists of WM_PROTOCOL atoms, as far as
3150 // the code's appearance goes. A bit of extra work, but you don't do this very often
3151 void WBSetWMProtocols(Window wID, Atom aProperty, ...)
3152 {
3153 va_list va, va2;
3154 Atom *pTemp, aArg;
3155 int i1, nItems;
3156 Display *pDisplay;
3157 Atom aTemp[32]; // temp storage, in case I need "something"
3158 _WINDOW_ENTRY_ *pEntry = WBGetWindowEntry(wID);
3159 
3160 
3161  pTemp = aTemp;
3162 
3163  pDisplay = WBGetWindowDisplay(wID);
3164 
3165  if(!pDisplay)
3166  {
3167  WB_ERROR_PRINT("ERROR - %s - no Display pointer\n", __FUNCTION__);
3168  return;
3169  }
3170 
3171  if(aProperty == None)
3172  {
3173  WB_ERROR_PRINT("TEMPORARY: %s - removing WM PROTOCOLS atom list\n", __FUNCTION__);
3174 
3176  XSetWMProtocols(pDisplay, wID, pTemp, 0);
3178 
3179  return;
3180  }
3181 
3182  va_start(va, aProperty);
3183 
3184  // copy the va_list so I can count items and possibly allocate memory
3185  va_copy(va2, va);
3186  nItems = 1; // always at least one if the first isn't "None"
3187 
3188  while(1)
3189  {
3190  aArg = va_arg(va2, Atom);
3191 
3192  if(aArg == None)
3193  {
3194  break;
3195  }
3196 
3197  nItems++;
3198  }
3199 
3200  va_end(va2); // cleanup
3201 
3202  // using the # of items I count here, determine if my array size is sufficient
3203  // and WBAlloc() a bigger one if needed. or not.
3204 
3205  if(nItems >= sizeof(aTemp)/sizeof(aTemp[0]))
3206  {
3207  pTemp = (Atom *)WBAlloc((nItems + 1) * sizeof(Atom));
3208 
3209  if(!pTemp)
3210  {
3211  WB_ERROR_PRINT("ERROR - %s - not enough memory for atom list, errno=%d (%xH)\n", __FUNCTION__, errno, errno);
3212  return;
3213  }
3214  }
3215 
3216  pTemp[0] = aProperty;
3217 
3218  for(i1=1; i1 < nItems; i1++)
3219  {
3220  aArg = va_arg(va, Atom);
3221 
3222  // TODO: debug, check value?
3223 
3224  pTemp[i1] = aArg;
3225  }
3226 
3227  pTemp[nItems] = None; // end with 'none'
3228 
3229  va_end(va); // cleanup
3230 
3231  // next, scan the list for properties that require special flags
3232 
3233  if(pEntry) // only if I have a window entry
3234  {
3235  pEntry->eWMProtocols &= ~WMPropertiesWMProtocols_Mask;
3236 
3237  for(i1=0; i1 < nItems; i1++)
3238  {
3239  if(pTemp[i1] == aWM_DELETE_WINDOW) // this window supports WM_DELETE
3240  {
3241  pEntry->eWMProtocols |= WMPropertiesWMProtocols_DeleteWindow;
3242  }
3243 
3244  // TODO, others
3245  }
3246  }
3247 
3248  // NOW, do the deed. and assign the atoms to WM_PROTOCOLS
3249 
3251  XSetWMProtocols(pDisplay, wID, pTemp, nItems);
3253 
3254 
3255  if(pTemp != aTemp) // if I allocated the buffer
3256  {
3257  WBFree(pTemp);
3258  }
3259 
3260 }
3261 
3262 
3263 Window WBLocateWindow(WBLocateWindowCallback callback, void *pData)
3264 {
3265 int i1, i2;
3266 
3267  if(!callback)
3268  {
3269  return (Window)0;
3270  }
3271 
3272  for(i1=0; i1 <= WINDOW_ENTRY_ARRAY_MAX; i1++)
3273  {
3274  Window wID = sWBHashEntries[i1].wID;
3275  if(wID && wID != WINDOW_ENTRY_UNUSED)
3276  {
3277  i2 = callback(wID, pData);
3278  if(i2 > 0)
3279  {
3280  return wID;
3281  }
3282  else if(i2 < 0)
3283  {
3284  return 0;
3285  }
3286  }
3287  }
3288 
3289  return 0;
3290 }
3291 
3292 
3293 // locate and return the application window. there can be only one.
3294 
3296 {
3297  return wIDApplication;
3298 }
3299 
3300 void WBSetApplicationWindow(Window wID)
3301 {
3302 _WINDOW_ENTRY_ *pEntry;
3303 
3304  if(wID == None)
3305  {
3306  wIDApplication = None;
3307  }
3308  else
3309  {
3310  pEntry = WBGetWindowEntry(wID);
3311 
3312  if(!pEntry)
3313  {
3314  wIDApplication = None;
3315  }
3316  else
3317  {
3318  wIDApplication = wID;
3319  }
3320  }
3321 }
3322 
3323 
3324 /**********************************************************************/
3325 /* */
3326 /* periodic timer and delayed event helper functions */
3327 /* */
3328 /**********************************************************************/
3329 
3330 //#define TIMER_ARRAY_SIZE 512
3331 //
3332 //static struct s_TIMER_ENTRY
3333 //{
3334 // struct s_TIMER_ENTRY *pNext; // linked lists for performance
3335 // unsigned long long lTimeIndex; // time index for which this timer next expires
3336 // unsigned long lTimeInterval; // interval (or zero for one-shot timer)
3337 //
3338 // Display *pDisplay; // display associated with timer
3339 // Window wID; // window to receive timer event
3340 // long lID; // timer identifier
3341 //} TIMER_ENTRY;
3342 //
3343 //static TIMER_ENTRY axWBTimer[TIMER_ARRAY_SIZE];
3344 //static TIMER_ENTRY *pTimerEntryActive = NULL, *pTimerEntryFree = NULL;
3345 // // pointers for two linked lists. entries must be in either 'active' or 'free' list.
3346 
3347 int CreateTimer(Display *pDisplay, Window wID, unsigned long lInterval, long lID, int iPeriodic)
3348 {
3349 int i1;
3350 TIMER_ENTRY *pCur;
3351 // if timer linked lists aren't set up yet, do that now
3352 
3353 
3354  if(!pTimerEntryActive && !pTimerEntryFree) // initial state
3355  {
3356  bzero(axWBTimer, sizeof(axWBTimer));
3357 
3358  for(i1=1; i1 < TIMER_ARRAY_SIZE; i1++)
3359  {
3360  axWBTimer[i1 - 1].pNext = axWBTimer + i1; // build linked list
3361  }
3362 
3363  pTimerEntryFree = axWBTimer;
3364  }
3365 
3366  // search for match, return -2 if found
3367  pCur = pTimerEntryActive;
3368  while(pCur)
3369  {
3370  if(pCur->pDisplay == pDisplay &&
3371  pCur->wID == wID &&
3372  pCur->lID == lID)
3373  {
3374  return -2;
3375  }
3376 
3377  pCur = pCur->pNext;
3378  }
3379 
3380  if(!pTimerEntryFree)
3381  {
3382  return -1; // no memory
3383  }
3384 
3385  pCur = pTimerEntryFree;
3386  pTimerEntryFree = pTimerEntryFree->pNext;
3387 // pCur->pNext = NULL; // TODO in case I have to add locking later
3388 
3389  pCur->pDisplay = pDisplay;
3390  pCur->wID = wID;
3391  pCur->lID = lID;
3392 
3393  pCur->lTimeIndex = WBGetTimeIndex() + lInterval;
3394  if(iPeriodic)
3395  {
3396  pCur->lTimeInterval = lInterval;
3397  }
3398  else
3399  {
3400  pCur->lTimeInterval = 0;
3401  }
3402 
3403  pCur->pNext = pTimerEntryActive; // goes at the beginning of the list (it's easier)
3404  pTimerEntryActive = pCur;
3405 
3406  return 0;
3407 }
3408 
3409 static void __DeleteTimer(TIMER_ENTRY *pPrev, TIMER_ENTRY *pEntry)
3410 {
3411  pEntry->lTimeIndex = pEntry->lTimeInterval = 0;
3412  pEntry->pDisplay = NULL;
3413  pEntry->wID = 0;
3414  pEntry->lID = 0;
3415 
3416  if(!pPrev) // assume head of linked list
3417  {
3418  if(pTimerEntryActive != pEntry) // sanity checks
3419  {
3420  // a leak is better than a crash
3421  WB_ERROR_PRINT("%s - (1) unable to properly delete timer due to pointer inconsistency %p %p %p\n",
3422  __FUNCTION__, pTimerEntryActive, pEntry, pEntry->pNext);
3423  }
3424  else
3425  {
3426  pTimerEntryActive = pEntry->pNext;
3427  pEntry->pNext = pTimerEntryFree;
3428  pTimerEntryFree = pEntry;
3429  }
3430  }
3431  else if(pPrev->pNext == pEntry) // more sanity checks
3432  {
3433  pPrev->pNext = pEntry->pNext;
3434  pEntry->pNext = pTimerEntryFree;
3435  pTimerEntryFree = pEntry;
3436  }
3437  else
3438  {
3439  WB_ERROR_PRINT("%s - (2) unable to properly delete timer due to pointer inconsistency %p %p %p %p\n",
3440  __FUNCTION__, pPrev, pPrev->pNext, pEntry, pEntry->pNext);
3441  }
3442 }
3443 
3444 void DeleteTimer(Display *pDisplay, Window wID, long lID)
3445 {
3446 TIMER_ENTRY *pCur, *pPrev;
3447 
3448  pCur = pTimerEntryActive;
3449  pPrev = NULL;
3450 
3451  while(pCur)
3452  {
3453  if(pCur->pDisplay == pDisplay &&
3454  pCur->wID == wID &&
3455  pCur->lID == lID)
3456  {
3457  __DeleteTimer(pPrev, pCur);
3458  return;
3459  }
3460 
3461  pPrev = pCur;
3462  pCur = pCur->pNext;
3463  }
3464 }
3465 
3466 static void DeletAllTimersForWindow(Display *pDisplay, Window wID)
3467 {
3468 TIMER_ENTRY *pCur, *pPrev, *pNext;
3469 
3470  pCur = pTimerEntryActive;
3471  pPrev = NULL;
3472 
3473  while(pCur)
3474  {
3475  if(pCur->pDisplay == pDisplay &&
3476  pCur->wID == wID)
3477  {
3478  pNext = pCur->pNext;
3479 
3480  __DeleteTimer(pPrev, pCur);
3481 
3482  pCur = pNext; // must do it THIS way if I remove the entry in between
3483  // note: 'pPrev' doesn't change, but 'pNext' does (pPrev->pNext should equal pCur now)
3484  }
3485  else
3486  {
3487  pPrev = pCur;
3488  pCur = pCur->pNext;
3489  }
3490  }
3491 }
3492 
3493 static int __CheckTimers(Display *pDisplay, XEvent *pEvent)
3494 {
3495 TIMER_ENTRY *pCur, *pPrev;
3496 WB_UINT64 lTime = WBGetTimeIndex();
3497 
3498 // Find the _NEXT_ registered timer for which the current time 'crosses'
3499 
3500  pCur = pTimerEntryActive;
3501  pPrev = NULL;
3502 
3503  while(pCur)
3504  {
3505  if(pCur->pDisplay == pDisplay &&
3506  lTime >= pCur->lTimeIndex) // time index has crossed "the threshold" for the timer
3507  {
3508  if(!pEvent)
3509  {
3510  return 1; // only indicate that I found a timer that's active (don't process it)
3511  }
3512 
3513  if(pCur->lTimeInterval)
3514  {
3515  pCur->lTimeIndex += pCur->lTimeInterval;
3516 
3517  if(lTime >= pCur->lTimeIndex) // to prevent 'spinning'
3518  {
3519  pCur->lTimeIndex = lTime + pCur->lTimeInterval;
3520  }
3521  }
3522  else
3523  {
3524  __DeleteTimer(pPrev, pCur);
3525  }
3526 
3527  // now fill out the event structure pointed to by 'pEvent'
3528  bzero(pEvent, sizeof(*pEvent));
3529 
3530  pEvent->xclient.type = ClientMessage;
3531  pEvent->xclient.serial = 0;
3532  pEvent->xclient.send_event = 0;
3533  pEvent->xclient.display = pDisplay;
3534  pEvent->xclient.window = pCur->wID;
3535  pEvent->xclient.message_type = aWB_TIMER;
3536  pEvent->xclient.format=32; // 32-bit integers
3537  pEvent->xclient.data.l[0] = pCur->lID;
3538 
3539  return 1; // found/processed a timer
3540  }
3541 
3542  pPrev = pCur;
3543  pCur = pCur->pNext;
3544  }
3545 
3546  return 0; // no timer found/processed
3547 }
3548 
3549 static void __CreateDelayedEvent(XEvent *pEvent, unsigned int uiInterval)
3550 {
3551 int i1;
3552 DELAYED_EVENT_ENTRY *pCur;
3553 // if timer linked lists aren't set up yet, do that now
3554 
3555 
3556  if(!pDelayedEventEntryActive && !pDelayedEventEntryFree) // initial state
3557  {
3558  bzero(axWBDelayedEvent, sizeof(axWBDelayedEvent));
3559 
3560  for(i1=1; i1 < TIMER_ARRAY_SIZE; i1++)
3561  {
3562  axWBDelayedEvent[i1 - 1].pNext = axWBDelayedEvent + i1; // build linked list
3563  }
3564 
3565  pDelayedEventEntryFree = axWBDelayedEvent;
3566  }
3567 
3568  // search for match, return -2 if found
3569  pCur = pDelayedEventEntryFree;
3570  pDelayedEventEntryFree = pDelayedEventEntryFree->pNext;
3571 // pCur->pNext = NULL; // TODO in case I have to add locking later
3572 
3573  memcpy(&(pCur->event), pEvent, sizeof(pCur->event));
3574 
3575  pCur->lTimeIndex = WBGetTimeIndex() + uiInterval;
3576 
3577  pCur->pNext = pDelayedEventEntryActive; // goes at the beginning of the list (it's easier)
3578  pDelayedEventEntryActive = pCur;
3579 }
3580 
3581 static void __DeleteDelayedEvent(DELAYED_EVENT_ENTRY *pPrev, DELAYED_EVENT_ENTRY *pEntry)
3582 {
3583  pEntry->lTimeIndex = 0;
3584  bzero(&(pEntry->event), sizeof(pEntry->event));
3585 
3586  if(!pPrev) // assume head of linked list
3587  {
3588  if(pDelayedEventEntryActive != pEntry) // sanity checks
3589  {
3590  // a leak is better than a crash
3591  WB_ERROR_PRINT("%s - (1) unable to properly delete delayed event due to pointer inconsistency %p %p %p\n",
3592  __FUNCTION__, pTimerEntryActive, pEntry, pEntry->pNext);
3593  }
3594  else
3595  {
3596  pDelayedEventEntryActive = pEntry->pNext;
3597  pEntry->pNext = pDelayedEventEntryFree;
3598  pDelayedEventEntryFree = pEntry;
3599  }
3600  }
3601  else if(pPrev->pNext == pEntry) // more sanity checks
3602  {
3603  pPrev->pNext = pEntry->pNext;
3604  pEntry->pNext = pDelayedEventEntryFree;
3605  pDelayedEventEntryFree = pEntry;
3606  }
3607  else
3608  {
3609  WB_ERROR_PRINT("%s - (2) unable to properly delete delayed event due to pointer inconsistency %p %p %p %p\n",
3610  __FUNCTION__, pPrev, pPrev->pNext, pEntry, pEntry->pNext);
3611  }
3612 }
3613 
3614 static int __attribute__((noinline)) __CheckDelayedEvents(Display *pDisplay, XEvent *pEvent)
3615 {
3616 DELAYED_EVENT_ENTRY *pCur, *pPrev;
3617 WB_UINT64 lTime = WBGetTimeIndex();
3618 
3619  // Find the _NEXT_ registered delayed event for which the current time 'crosses'
3620 
3621  pCur = pDelayedEventEntryActive;
3622  pPrev = NULL;
3623 
3624  while(pCur)
3625  {
3626  if((pCur->event.xany.display == pDisplay || pCur->event.xany.window == None) &&
3627  lTime >= pCur->lTimeIndex)
3628  {
3629  // fill out the event structure pointed to by 'pEvent'
3630 
3631  if(pCur->event.xany.window != None)
3632  {
3633  _WINDOW_ENTRY_ *pEntry = WBGetWindowEntry(pCur->event.xany.window);
3634 
3635  if(!pEntry || WB_IS_WINDOW_DESTROYED(*pEntry))
3636  {
3637  __DeleteDelayedEvent(pPrev, pCur);
3638  return 0; // none processed [code is easier this way]
3639  }
3640  }
3641 
3642  if(pEvent) // if NULL, I'm only looking to see if there IS one
3643  {
3644  memcpy(pEvent, &(pCur->event), sizeof(*pEvent));
3645 
3646  __DeleteDelayedEvent(pPrev, pCur);
3647  }
3648 
3649  return 1; // processed
3650  }
3651 
3652  pPrev = pCur;
3653  pCur = pCur->pNext;
3654  }
3655 
3656  return 0; // no timer processed
3657 }
3658 
3659 
3660 
3661 /**********************************************************************/
3662 /* */
3663 /* message loop helper functions */
3664 /* */
3665 /**********************************************************************/
3666 
3667 static Bool __WBCheckIfEventPredicate(Display *pDisplay, XEvent *pEvent, XPointer arg)
3668 {
3669  if(pEvent && pEvent->type != Expose)
3670  {
3671  return TRUE;
3672  }
3673  else
3674  {
3675  return FALSE;
3676  }
3677 }
3678 
3679 static Bool __WBCheckIfEventPredicate0(Display *pDisplay, XEvent *pEvent, XPointer arg)
3680 {
3681  if(pEvent &&
3682  (pEvent->type == SelectionNotify ||
3683  pEvent->type == SelectionClear ||
3684  pEvent->type == SelectionRequest))
3685  {
3686  return TRUE;
3687  }
3688  else
3689  {
3690  return FALSE;
3691  }
3692 }
3693 
3694 // WBCheckGetEvent - get next (prioritized) event, do translations
3695 
3696 static int iMouseState = 0; // artificial mouse state (global) for dbl-click and drag
3697 static int iMouseModShiftCtrl = 0; // Mod/Shift/Ctrl state for last mouse state
3698 static int iMouseDragButtonState = 0; // mouse button bit-flags for last mouse state and auto-drag-cancel
3699 static WB_UINT64 tmMouse; // timeval for most recent processed mouse event
3700 static Window wMouseCapture = None;
3701 static int iMouseX = 0, iMouseY = 0; // RAW x,y position from mouse message
3702 
3703 
3704 enum
3705 {
3706  MouseState_NONE = 0,
3707  MouseState_LCLICK = 1,
3708  MouseState_WAS_LCLICK,
3709  MouseState_LDRAG, // 'drag' states imply mouse captured
3710  MouseState_RCLICK,
3711  MouseState_WAS_RCLICK,
3712  MouseState_RDRAG,
3713  MouseState_CCLICK,
3714  MouseState_WAS_CCLICK,
3715  MouseState_CDRAG
3716 };
3717 
3719 {
3720 WB_UINT64 qwTick = WBGetTimeIndex();
3721 
3722  return tmDefaultDisplay + (qwTick - qwDefaultDisplayTick) / 1000;
3723 }
3724 
3725 int WBCheckGetEvent(Display *pDisplay, XEvent *pEvent)
3726 {
3727  return __InternalCheckGetEvent(pDisplay, pEvent, None); // no modal window implies "do certain things differently"
3728 }
3729 
3730 void WBWaitForEvent(Display *pDisplay)
3731 {
3732 int iTemp, iSleepPeriod;
3733 
3734  // First, see if I have any priority or other queued events
3735 
3736  iTemp = iWBQueuedEvent;
3737 
3738  // check internal queues first, if there's something there
3739  // these queues won't change without calling WBCheckGetEvent()
3740 
3741  iSleepPeriod = MIN_EVENT_LOOP_SLEEP_PERIOD; // the initial value
3742 
3743  while(WB_LIKELY(iTemp >= 0))
3744  {
3745  if(WB_LIKELY(axWBEvt[iTemp].pDisplay == pDisplay))
3746  {
3747  return; // found one
3748  }
3749 
3750  iTemp = axWBEvt[iTemp].iNext;
3751  }
3752 
3753  iTemp = iWBPaintEvent;
3754 
3755  while(iTemp >= 0)
3756  {
3757  if(WB_LIKELY(axWBEvt[iTemp].pDisplay == pDisplay))
3758  {
3759  return; // found one
3760  }
3761 
3762  iTemp = axWBEvt[iTemp].iNext;
3763  }
3764 
3765  while(!bQuitFlag) // forever, unless I quit
3766  {
3767  // check timers and internal event queues
3768 
3769  if(__CheckTimers(pDisplay, NULL)) // a timer is ready to create an event?
3770  {
3771  return;
3772  }
3773 
3774  if(__CheckDelayedEvents(pDisplay, NULL)) // delayed events
3775  {
3776  return;
3777  }
3778 
3779  if(XEventsQueued(pDisplay, QueuedAfterFlush)) // this appears to be the most efficient method to use
3780  {
3781  return; // I have events waiting!
3782  }
3783 
3784  // perform a variable delay, up to MAX_EVENT_LOOP_SLEEP_PERIOD, before checking again, so I don't 'spin'
3785  // we start with the min period, so we'll more effectively capture 'bunches' of events, which would be
3786  // typical for UI actions - this makes it appear 'snappier'. But when no events happen, the sleep period
3787  // will increase until it hits 'MAX_EVENT_LOOP_SLEEP_PERIOD', which should be tweeked accordingly to get
3788  // minimum CPU usage while "idling" (and yet still respond to important things).
3789 
3790  WBDelay(iSleepPeriod); // 100 microsecs (0.1 milliseconds) initially, grows over time
3791 
3792  if(iSleepPeriod < MAX_EVENT_LOOP_SLEEP_PERIOD) // up to 'MAX_EVENT_LOOP_SLEEP_PERIOD' microseconds
3793  {
3794  iSleepPeriod += (iSleepPeriod >> 2); // increase by 25%
3795  }
3796  else
3797  {
3798  iSleepPeriod = MAX_EVENT_LOOP_SLEEP_PERIOD; // the maximum
3799  }
3800  }
3801 }
3802 
3803 // "internal" version that allows me to do things different for modal loops
3804 // it prioritizes events based on what type they are, to make UI response 'snappier'
3805 
3806 int __InternalCheckGetEvent(Display *pDisplay, XEvent *pEvent, Window wIDModal)
3807 {
3808 int iRval = 0, iQueued;
3809 int iFirstTime = 1;
3810 //static unsigned long long ullLastTime = 0;
3811 
3812  do
3813  {
3815  if(iFirstTime)
3816  {
3817  iQueued = XEventsQueued(pDisplay, QueuedAfterFlush); // QueuedAfterFlush on the first pass
3818 
3819  iFirstTime = 0; // assign it here so I only do this code section once
3820  }
3821  else
3822  {
3823  iQueued = XEventsQueued(pDisplay, QueuedAlready); // see if events are queued already (first, no flushing)
3824  }
3825 
3827 
3828  // check for selection events first...
3829 
3831  iRval = iQueued && // SelectionNotify, SelectionClear, SelectionRequest
3832  XCheckIfEvent(pDisplay, pEvent, __WBCheckIfEventPredicate0, NULL);
3834 
3835  if(!iRval)
3836  {
3837  if(WB_UNLIKELY(0 != (iRval = __CheckDelayedEvents(pDisplay, pEvent)))) // delayed events first
3838  {
3839  break;
3840  }
3841 
3842  if(WB_UNLIKELY(0 != (iRval = __CheckTimers(pDisplay, pEvent)))) // then timers
3843  {
3844  break;
3845  }
3846 
3847  // then check for actual events in the X11 event queue
3848 
3850  iRval = iQueued /*XEventsQueued(pDisplay, QueuedAlready)*/ &&
3851  (XCheckIfEvent(pDisplay, pEvent, __WBCheckIfEventPredicate, NULL) ||
3852  XCheckMaskEvent(pDisplay, ExposureMask, pEvent));
3854  }
3855 
3856  if(iRval)
3857  {
3858  if(pDisplay == pDefaultDisplay) // for default display, grab the timestamp
3859  {
3860  Time tmEvent = 0;
3861 
3862  // for certain events I want to get the time of the event
3863  // and so I'll record that here
3864 
3865  switch(pEvent->type) // for the ones that have timestamps, I check for them
3866  {
3867  case KeyPress:
3868  case KeyRelease:
3869  tmEvent =((XKeyEvent *)pEvent)->time;
3870  break;
3871  case ButtonPress:
3872  case ButtonRelease:
3873  tmEvent =((XButtonEvent *)pEvent)->time;
3874  break;
3875  case MotionNotify:
3876  tmEvent =((XMotionEvent *)pEvent)->time;
3877  break;
3878  case EnterNotify:
3879  case LeaveNotify:
3880  tmEvent =((XCrossingEvent *)pEvent)->time;
3881  break;
3882  case PropertyNotify:
3883  tmEvent =((XPropertyEvent *)pEvent)->time;
3884  break;
3885  case SelectionRequest:
3886  tmEvent =((XSelectionRequestEvent *)pEvent)->time;
3887  break;
3888  case SelectionClear:
3889  tmEvent =((XSelectionClearEvent *)pEvent)->time;
3890  break;
3891  case SelectionNotify:
3892  tmEvent =((XSelectionEvent *)pEvent)->time;
3893  break;
3894  }
3895 
3896  if(tmEvent > tmDefaultDisplay)
3897  {
3898  tmDefaultDisplay = tmEvent; // with the assumption that the millis here NEVER wrap around
3899  qwDefaultDisplayTick = WBGetTimeIndex(); // snap this too so I can adjust it
3900  }
3901  }
3902 
3903  if(pEvent->type == Expose) // expose events get special handling
3904  {
3905  // EXPOSE EVENTS - some special handling
3906 
3907  WBProcessExposeEvent((XExposeEvent *)pEvent); // this consolidates incoming Expose events
3908 
3909  // after processing the expose event, use XSync to immediately sync back up
3910  // in case there are more events NOT received yet. Only for EXPOSE though.
3911 
3913  XFlush(pDisplay); // send the events to the server (asynchronously)
3914 // XSync(pDisplay, 0); // this way, I am waiting for the window painting to actually happen
3916 
3917  iRval = 0;
3918  continue; // added - this might have been a bug...?
3919  }
3920  else if(pEvent->type == NoExpose) // pretend I never got this
3921  {
3922  iRval = 0;
3923  continue; // act like it wasn't even there - try again
3924  }
3925 
3926  // TODO: other events that might be part of the above list of events to grab first
3927 
3928  break;
3929  }
3930  else // no display events
3931  {
3932  // use XEventsQueued to see if I have events. Use 'QueueAfterFlush' which appears to be the
3933  // most efficient way of doing this.
3934 
3935  if(XEventsQueued(pDisplay, QueuedAfterFlush) > 0) // flushes and attempts to read more events into queue
3936  {
3937  continue; // more events were read in, so loop back to the beginning
3938  }
3939 
3940  // regular 'next event' processing
3941 
3942  iRval = WBNextEvent(pDisplay, pEvent); // get 'internal' queued events
3943 
3944  break; // regardless if an event is found I break out now
3945  }
3946 
3947  // this conveniently re-tries when I get an Expose event (to try and consolidate them)
3948 
3949 
3950  } while(1);
3951 
3952  if(iRval)
3953  {
3954  if(pEvent->type == ConfigureNotify)
3955  {
3956  WB_GEOM gm;
3957  Window wIDTemp;
3958  // NOTE: this could be done in WBWindowDispatch but the chance that
3959  // it might be missed demands it be handled at a lower level
3960 
3961  _WINDOW_ENTRY_ *pEntry = WBGetWindowEntry(pEvent->xconfigure.window);
3962 
3963  if(pEntry) // notification needs to update internal 'absolute geometry' data
3964  {
3965  // NOTE: window will get at least one of these when it becomes visible
3966  // until then the values are initialized as zeros
3967 
3968  if(pEntry->geomAbsolute.width == 0 && // first time through, trust the message
3969  pEntry->geomAbsolute.height == 0)
3970  {
3971  pEntry->geomAbsolute.x = pEvent->xconfigure.x;
3972  pEntry->geomAbsolute.y = pEvent->xconfigure.y;
3973  pEntry->geomAbsolute.width = pEvent->xconfigure.width;
3974  pEntry->geomAbsolute.height = pEvent->xconfigure.height;
3975  pEntry->geomAbsolute.border = pEvent->xconfigure.border_width;
3976 
3977  // first time through, create clip region that's empty, and record the window's width/height info
3978  if(!pEntry->rgnClip)
3979  {
3980  // NOTE: I must do it THIS way because the window isn't mapped yet.
3981 
3982  pEntry->width = pEvent->xconfigure.width;
3983  pEntry->height = pEvent->xconfigure.height;
3984  pEntry->border = pEvent->xconfigure.border_width;
3985 
3986  pEntry->rgnClip = XCreateRegion();
3987  }
3988 
3989  if(pEntry->rgnClip)
3990  {
3991  XRectangle xrct;
3992 
3993  xrct.x = 0;//(short)pEntry->x;
3994  xrct.y = 0;//(short)pEntry->y;
3995  xrct.width = (unsigned short)pEntry->width;
3996  xrct.height = (unsigned short)pEntry->height;
3997 
3998  XUnionRectWithRegion(&xrct, pEntry->rgnClip, pEntry->rgnClip); // clip region includes this, now
3999  }
4000  }
4001  else if(WB_IS_WINDOW_MAPPED(*pEntry)) // get values from parent window(s) and adjust absolute coords
4002  { // NOTE: this won't work if this OR the PARENT windows aren't mapped
4003  wIDTemp = pEvent->xconfigure.window;
4004  pEntry->geomAbsolute.x = pEntry->geomAbsolute.y = 0;
4005 
4006  while(wIDTemp)
4007  {
4008  WBGetWindowGeom(wIDTemp, &gm);
4009  pEntry->geomAbsolute.x += gm.x;
4010  pEntry->geomAbsolute.y += gm.y;
4011 
4012  if(wIDTemp == pEvent->xconfigure.window)
4013  {
4014  pEntry->geomAbsolute.width = gm.width;
4015  pEntry->geomAbsolute.height = gm.height;
4016  pEntry->geomAbsolute.border = gm.border;
4017  }
4018 
4019  wIDTemp = WBGetParentWindow(wIDTemp);
4020  }
4021  }
4022  else
4023  {
4024  WB_ERROR_PRINT("TEMPORARY: %s - subsequent ConfigureNotify and window is not yet mapped\n", __FUNCTION__);
4025  }
4026 
4027  WB_DEBUG_PRINT(DebugLevel_Heavy | DebugSubSystem_Window,
4028  "%s - update internal geometry %d (%08xH) %d, %d, %d, %d\n",
4029  __FUNCTION__,
4030  (int)pEvent->xconfigure.window, (int)pEvent->xconfigure.window,
4031  pEntry->geomAbsolute.x, pEntry->geomAbsolute.y,
4032  pEntry->geomAbsolute.width, pEntry->geomAbsolute.height);
4033  }
4034  }
4035 
4036  // MOUSE translations (double-click, drag help)
4037  // to use these, the client shouldn't handle the system mouse events
4038  // Additionally, behavior DIFFERS SIGNIFICANTLY when inside a modal loop
4039 
4040  if(pEvent->type == ButtonPress ||
4041  pEvent->type == ButtonRelease ||
4042  pEvent->type == MotionNotify)
4043  {
4044  int iButton1 = (((XButtonEvent *)pEvent)->state & Button1Mask)
4045  || ((XButtonEvent *)pEvent)->button == Button1;
4046  int iButton2 = (((XButtonEvent *)pEvent)->state & Button2Mask)
4047  || ((XButtonEvent *)pEvent)->button == Button2;
4048  int iButton3 = (((XButtonEvent *)pEvent)->state & Button3Mask)
4049  || ((XButtonEvent *)pEvent)->button == Button3;
4050  int iButton4 = (((XButtonEvent *)pEvent)->state & Button4Mask)
4051  || ((XButtonEvent *)pEvent)->button == Button4;
4052  int iButton5 = (((XButtonEvent *)pEvent)->state & Button5Mask)
4053  || ((XButtonEvent *)pEvent)->button == Button5;
4054 
4055  int iDragButtonState = (iButton1 ? 1 : 0) // mostly for dragging, but used in other places, too
4056  | (iButton2 ? 2 : 0)
4057  | (iButton3 ? 4 : 0);
4058 
4059  int iModShiftCtrl = ((XButtonEvent *)pEvent)->state & (ShiftMask | LockMask | ControlMask | Mod1Mask
4060  | Mod2Mask | Mod3Mask | Mod4Mask | Mod5Mask);
4061  int iX, iY;
4062 
4063  if(pEvent->type == ButtonPress &&
4064  (iMouseState == MouseState_NONE ||
4065  iMouseState == MouseState_WAS_LCLICK))
4066  {
4067  Window wIDCurrent = None;
4068  int iRevert = 0;
4069  Window wID0 = ((XButtonEvent *)pEvent)->window;
4070 
4071  // NOTE: if window does _NOT_ have input focus, make sure it gets the focus
4072  // and re-display it's container in front of other windows (as needed)
4073 
4074 
4076  XGetInputFocus(pDisplay, &wIDCurrent, &iRevert);
4078 
4079 // WB_ERROR_PRINT("TEMPORARY: mouse click %d, old window %d (%08xH), new window %d (%08xH)\n",
4080 // iMouseState, (int)wIDCurrent, (int)wIDCurrent, (int)wID0, (int)wID0);
4081 
4082  if(wIDCurrent != wID0 &&
4083  ( wIDModal == None || // ok if NOT in modal state
4084  WBIsChildWindow(wIDModal, wID0))) // window I'm switching focus to is child of modal loop owner
4085  {
4086 // WBSetInputFocus(((XButtonEvent *)pEvent)->window);
4087  // TODO: replicate part of 'set focus' code as private function to avoid duplication?
4088 
4089 
4090  _WINDOW_ENTRY_ *pPrev = WBGetWindowEntry(wIDCurrent);
4091  _WINDOW_ENTRY_ *pEntry = WBGetWindowEntry(wID0);
4092 
4093 // WB_ERROR_PRINT("TEMPORARY: mouse click %d, old window %d (%08xH), new window %d (%08xH)\n",
4094 // iMouseState, (int)wIDCurrent, (int)wIDCurrent, (int)wID0, (int)wID0);
4095 
4096  if(pPrev)
4097  {
4098  if(WB_CHECK_SET_FOCUS_ON_MAP(*pPrev))
4099  {
4100  WB_DEBUG_PRINT(DebugLevel_Light | DebugSubSystem_Window,
4101  "%s:%d - %d (%08xH) disabling focus change on map\n",
4102  __FUNCTION__, __LINE__, (int)wIDCurrent, (int)wIDCurrent);
4103 
4104  pPrev->iWindowState = WB_WINDOW_UNMAPPED; // remove 'set focus' state
4105  }
4106  }
4107 
4108  // see if expose event has been done on this yet...
4109  if(WB_LIKELY(pEntry) && //WB_UNLIKELY(!pEntry->width && !pEntry->height && !pEntry->border))
4110  WB_UNLIKELY(WB_IS_WINDOW_UNMAPPED(*pEntry)))
4111  {
4112  WB_DEBUG_PRINT(DebugLevel_WARN | DebugSubSystem_Window,
4113  "%s:%d - %d (%08xH) not yet visible, enabling focus change on map\n",
4114  __FUNCTION__, __LINE__, (int)wID0, (int)wID0);
4115 
4116  pEntry->iWindowState = WB_WINDOW_SET_FOCUS_ON_MAP;
4117  }
4118  else
4119  {
4120  WB_DEBUG_PRINT(DebugLevel_Light | DebugSubSystem_Window,
4121  "%s:%d - %d (%08xH) calling XSetInputFocus\n",
4122  __FUNCTION__, __LINE__, (int)wID0, (int)wID0);
4123 
4124 // WB_ERROR_PRINT("TEMPORARY - %s calling XSetInputFocus on wID=%u (%08xH)\n", __FUNCTION__, (int)wID0, (uint32_t)wID0);
4125 
4127  XSetInputFocus(pDisplay, wID0, RevertToParent, CurrentTime);
4129  }
4130 
4131  iMouseState = MouseState_NONE; // if window focus changes, I don't want double clicking or dragging to happen
4132  }
4133 
4134  // TODO: bring top level window forward if not topmost already
4135  }
4136 
4137 
4138  // based on current state
4139  switch(iMouseState)
4140  {
4141  case MouseState_WAS_LCLICK:
4142 
4143  if(pEvent->type == ButtonPress)
4144  {
4145  int iDblClickThresh = CHGetDoubleClickDistance(pDisplay);
4146  WB_UINT64 tmTemp = WBGetTimeIndex(); // snapshot the current time (microseconds)
4147 
4148  if(abs(iMouseX - ((XButtonEvent *)pEvent)->x) >= iDblClickThresh || // too far
4149  abs(iMouseY - ((XButtonEvent *)pEvent)->y) >= iDblClickThresh || // too far
4150  (tmTemp - tmMouse) > 1000 * CHGetDoubleClickTime(pDisplay)) // too slow
4151  {
4152  iMouseState = MouseState_NONE;
4153 
4154  // flow through to next section
4155  }
4156  else if(!iButton1 || iButton2 || iButton3)
4157  {
4158  iMouseState = MouseState_NONE;
4159 
4160  // flow through to next section
4161  }
4162  else
4163  {
4164  // post a double-click notification
4165 
4166  XClientMessageEvent evt = {
4167  .type=ClientMessage,
4168  .serial=0,
4169  .send_event=0,
4170  .display=pDisplay,
4171  .window=((XButtonEvent *)pEvent)->window,
4172  .message_type=aWB_POINTER,
4173  .format=32
4174  };
4175 
4176  evt.data.l[0] = WB_POINTER_DBLCLICK;
4177  evt.data.l[1] = iDragButtonState;
4178  evt.data.l[2] = (iModShiftCtrl & ShiftMask ? WB_KEYEVENT_SHIFT : 0)
4179  | (iModShiftCtrl & ControlMask ? WB_KEYEVENT_CTRL : 0)
4180  | (iModShiftCtrl & Mod1Mask ? WB_KEYEVENT_ALT : 0);
4181 
4182  WBXlatCoordPoint(((XButtonEvent *)pEvent)->window, ((XButtonEvent *)pEvent)->x, ((XButtonEvent *)pEvent)->y,
4183  ((XButtonEvent *)pEvent)->window, &iX, &iY);
4184 
4185  evt.data.l[3] = iX;
4186  evt.data.l[4] = iY;
4187 
4188  WBPostPriorityEvent(((XButtonEvent *)pEvent)->window, (XEvent *)&evt);
4189 
4190  break;
4191  }
4192  }
4193  else if(pEvent->type == ButtonRelease)
4194  {
4195  iMouseState = MouseState_NONE;
4196 
4197  break;
4198  }
4199  else
4200  {
4201  // TODO: mouse motion is OK, but what about anything else?
4202 
4203  break;
4204  }
4205 
4206  // at this poitn the state has been re-assigned
4207 
4208  case MouseState_NONE: // base condition - no current activity
4209  if(pEvent->type == ButtonPress)
4210  {
4211  // for now, post a 'CLICK' notification every time
4212 
4213  XClientMessageEvent evt = {
4214  .type=ClientMessage,
4215  .serial=0,
4216  .send_event=0,
4217  .display=pDisplay,
4218  .window=((XButtonEvent *)pEvent)->window,
4219  .message_type=aWB_POINTER,
4220  .format=32
4221  };
4222 
4223  evt.data.l[0] = WB_POINTER_CLICK;
4224  evt.data.l[1] = iDragButtonState
4225  | (iButton4 ? 8 : 0)
4226  | (iButton5 ? 16 : 0);
4227  evt.data.l[2] = (iModShiftCtrl & ShiftMask ? WB_KEYEVENT_SHIFT : 0)
4228  | (iModShiftCtrl & ControlMask ? WB_KEYEVENT_CTRL : 0)
4229  | (iModShiftCtrl & Mod1Mask ? WB_KEYEVENT_ALT : 0);
4230 
4231  WBXlatCoordPoint(((XButtonEvent *)pEvent)->window, ((XButtonEvent *)pEvent)->x, ((XButtonEvent *)pEvent)->y,
4232  ((XButtonEvent *)pEvent)->window, &iX, &iY);
4233 
4234  evt.data.l[3] = iX;
4235  evt.data.l[4] = iY;
4236 
4237  WBPostPriorityEvent(((XButtonEvent *)pEvent)->window, (XEvent *)&evt);
4238 
4239 // WB_ERROR_PRINT("TEMPORARY: mouse click at %d %d for %d (%08xH) %d %d %d\n",
4240 // iX, iY, (int)((XButtonEvent *)pEvent)->window, (int)((XButtonEvent *)pEvent)->window,
4241 // iButton1, iButton2, iButton3);
4242 
4243  if(iButton1 && !iButton2 && !iButton3 && !iButton4 && !iButton5)
4244  {
4245  // left button - for now this is all I test for
4246  iMouseState = MouseState_LCLICK;
4247  iMouseModShiftCtrl = iModShiftCtrl;
4248 
4249  tmMouse = WBGetTimeIndex(); // snapshot the time (microseconds)
4250 
4251  iMouseX = ((XButtonEvent *)pEvent)->x;
4252  iMouseY = ((XButtonEvent *)pEvent)->y;
4253  }
4254  else if(iButton4 || iButton5)
4255  {
4256  // notify window of mouse wheel scrolling. iButton4 is "scroll up", iButton5 is "scroll down"
4257 
4258  XClientMessageEvent evt = {
4259  .type=ClientMessage,
4260  .serial=0,
4261  .send_event=0,
4262  .display=pDisplay,
4263  .window=pEvent->xany.window,
4264  .message_type=aWB_POINTER,
4265  .format=32
4266  };
4267 
4268  if(iButton4)
4269  {
4270  evt.data.l[0] = WB_POINTER_SCROLLUP;
4271  }
4272  else
4273  {
4274  evt.data.l[0] = WB_POINTER_SCROLLDOWN;
4275  }
4276 
4277  evt.data.l[1] = iDragButtonState
4278  | (iButton4 ? 8 : 0)
4279  | (iButton5 ? 16 : 0);
4280  evt.data.l[2] = (iModShiftCtrl & ShiftMask ? WB_KEYEVENT_SHIFT : 0)
4281  | (iModShiftCtrl & ControlMask ? WB_KEYEVENT_CTRL : 0)
4282  | (iModShiftCtrl & Mod1Mask ? WB_KEYEVENT_ALT : 0);
4283 
4284  WBXlatCoordPoint(((XButtonEvent *)pEvent)->window, iMouseX, iMouseY,
4285  ((XButtonEvent *)pEvent)->window, &iX, &iY);
4286 
4287  evt.data.l[3] = iX; // TODO: should this be UN-TRANSLATED or SCREEN coordinates?
4288  evt.data.l[4] = iY; // should I indicate WHICH WINDOW this should be for?
4289 
4290  WBWindowDispatch(evt.window, (XEvent *)&evt);
4291  }
4292  }
4293  else if(pEvent->type == ButtonRelease)
4294  {
4295 // if(iButton1 || iButton2 || iButton3 || iButton4 || iButton5)
4296 // {
4297 // WB_ERROR_PRINT("TEMPORARY: %s button release, button state %d %d %d %d %d\n",
4298 // __FUNCTION__, iButton1, iButton2, iButton3, iButton4, iButton5);
4299 // }
4300  }
4301  else // if(pEvent->type == MotionNotify)
4302  {
4303 // if(iButton1 || iButton2 || iButton3 || iButton4 || iButton5)
4304 // {
4305 // WB_ERROR_PRINT("TEMPORARY: %s motion notify, button state %d %d %d %d %d\n",
4306 // __FUNCTION__, iButton1, iButton2, iButton3, iButton4, iButton5);
4307 // }
4308  }
4309 
4310  break;
4311 
4312  case MouseState_LCLICK:
4313 
4314  if(pEvent->type == ButtonPress)
4315  {
4316  if(!iButton1 || iButton2 || iButton3)
4317  {
4318  // TODO: MCLICK and RCLICK?
4319  iMouseState = MouseState_NONE;
4320  }
4321  else
4322  {
4323  // TODO: is this a double-click?
4324  }
4325  }
4326  else if(pEvent->type == ButtonRelease)
4327  {
4328  iMouseState = MouseState_WAS_LCLICK;
4329  }
4330  else if(iMouseModShiftCtrl != iModShiftCtrl) // modifier state change
4331  {
4332  iMouseState = MouseState_NONE;
4333  }
4334  else
4335  {
4336  int iTemp, iDragThresh = CHGetDragThreshold(pDisplay);
4337 
4338  // DRAG DETECT (reserved)
4339 
4340  if(abs(iMouseX - ((XButtonEvent *)pEvent)->x) >= iDragThresh ||
4341  abs(iMouseY - ((XButtonEvent *)pEvent)->y) >= iDragThresh)
4342  {
4343  // check for entering drag state - window proc needs to respond correctly
4344 
4345  XClientMessageEvent evt = {
4346  .type=ClientMessage,
4347  .serial=0,
4348  .send_event=0,
4349  .display=pDisplay,
4350  .window=pEvent->xany.window,
4351  .message_type=aWB_POINTER,
4352  .format=32
4353  };
4354 
4355  evt.data.l[0] = WB_POINTER_DRAG;
4356  evt.data.l[1] = iDragButtonState
4357  | (iButton4 ? 8 : 0)
4358  | (iButton5 ? 16 : 0);
4359  evt.data.l[2] = (iModShiftCtrl & ShiftMask ? WB_KEYEVENT_SHIFT : 0)
4360  | (iModShiftCtrl & ControlMask ? WB_KEYEVENT_CTRL : 0)
4361  | (iModShiftCtrl & Mod1Mask ? WB_KEYEVENT_ALT : 0);
4362 
4363  iMouseDragButtonState = iDragButtonState;
4364 
4365  WBXlatCoordPoint(((XButtonEvent *)pEvent)->window, iMouseX, iMouseY,
4366  ((XButtonEvent *)pEvent)->window, &iX, &iY);
4367 
4368  // NOTE: sending ORIGINAL 'click' mouse coordinates since the drag moves the mouse
4369  evt.data.l[3] = iX; // TODO: should this be UN-TRANSLATED or SCREEN coordinates?
4370  evt.data.l[4] = iY; // should I indicate WHICH WINDOW this should be for?
4371 
4372  WBXlatCoordPoint(((XButtonEvent *)pEvent)->window, ((XButtonEvent *)pEvent)->x, ((XButtonEvent *)pEvent)->y,
4373  ((XButtonEvent *)pEvent)->window, &iX, &iY);
4374 
4375  iTemp = WBWindowDispatch(evt.window, (XEvent *)&evt);
4376  if(iTemp == (int)evt.window) // must return window ID
4377  {
4378  iMouseState = MouseState_LDRAG;
4379 
4380  if(wMouseCapture == None)
4381  {
4382  XGrabPointer(pDisplay, ((XButtonEvent *)pEvent)->window, 1,
4383  ButtonPressMask | ButtonReleaseMask | ButtonMotionMask | PointerMotionMask
4384  | EnterWindowMask | LeaveWindowMask,
4385  GrabModeAsync, // pointer mode
4386  GrabModeAsync, // keyboard mode
4387  None, None, CurrentTime);
4388 
4389  wMouseCapture = ((XButtonEvent *)pEvent)->window;
4390 
4391  // post a motion event to update pointer position
4392  evt.data.l[0] = WB_POINTER_MOVE;
4393  evt.data.l[3] = iX;
4394  evt.data.l[4] = iY;
4395 
4396  WBPostPriorityEvent(((XButtonEvent *)pEvent)->window, (XEvent *)&evt);
4397 
4398  iMouseX = ((XButtonEvent *)pEvent)->x; // keep track of starting drag position
4399  iMouseY = ((XButtonEvent *)pEvent)->y;
4400 
4401 // WB_ERROR_PRINT("TEMPORARY: %s:%d mouse drag begins: %d %d,%d\n",
4402 // __FUNCTION__, __LINE__,
4403 // iButton1, iMouseX, iMouseY);
4404  }
4405 // else
4406 // {
4407 // WB_ERROR_PRINT("TEMPORARY: %s:%d mouse drag, but already captured: %d %d,%d\n",
4408 // __FUNCTION__, __LINE__,
4409 // iButton1, iMouseX, iMouseY);
4410 // }
4411  }
4412  else
4413  {
4414 // WB_ERROR_PRINT("TEMPORARY: %s:%d mouse drag NOT enabled. %08xH %08xH\n",
4415 // __FUNCTION__, __LINE__, iTemp, (int)evt.window);
4416 
4417  iMouseState = MouseState_NONE; // TODO: check for drag support first
4418  }
4419  }
4420  }
4421 
4422  break;
4423 
4424  case MouseState_LDRAG:
4425  case MouseState_RDRAG:
4426  case MouseState_CDRAG:
4427 
4428  if(pEvent->type == ButtonPress || // someone pressed a different mouse button
4429  iMouseDragButtonState != iDragButtonState || // button state has changed
4430  iMouseModShiftCtrl != iModShiftCtrl) // modifier state change
4431  {
4432 // WB_ERROR_PRINT("TEMPORARY: %s:%d mouse was dragging, now canceled\n", __FUNCTION__, __LINE__);
4433 
4434  WBMouseCancel(pDisplay, ((XButtonEvent *)pEvent)->window); // always call this
4435  }
4436  else if(pEvent->type == MotionNotify) // mods and buttons NOT changed
4437  {
4438  // modifiers still match (see above) so no need for cancelation here
4439 
4440  if(iButton1 && // for now rely on other code to cancel this if another button is pressed
4441  (iMouseX != ((XButtonEvent *)pEvent)->x ||
4442  iMouseY != ((XButtonEvent *)pEvent)->y))
4443  {
4444  XClientMessageEvent evt = {
4445  .type=ClientMessage,
4446  .serial=0,
4447  .send_event=0,
4448  .display=pDisplay,
4449  .window=((XButtonEvent *)pEvent)->window,
4450  .message_type=aWB_POINTER,
4451  .format=32
4452  };
4453 
4454 // WB_ERROR_PRINT("TEMPORARY: %s:%d mouse drag motion\n", __FUNCTION__, __LINE__);
4455 
4456  evt.data.l[0] = WB_POINTER_MOVE;
4457  evt.data.l[1] = iDragButtonState
4458  | (iButton4 ? 8 : 0)
4459  | (iButton5 ? 16 : 0);
4460  evt.data.l[2] = (iModShiftCtrl & ShiftMask ? WB_KEYEVENT_SHIFT : 0)
4461  | (iModShiftCtrl & ControlMask ? WB_KEYEVENT_CTRL : 0)
4462  | (iModShiftCtrl & Mod1Mask ? WB_KEYEVENT_ALT : 0);
4463 
4464  WBXlatCoordPoint(((XButtonEvent *)pEvent)->window, ((XButtonEvent *)pEvent)->x, ((XButtonEvent *)pEvent)->y,
4465  ((XButtonEvent *)pEvent)->window, &iX, &iY);
4466 
4467  evt.data.l[3] = iX; // TODO: should this be UN-TRANSLATED or SCREEN coordinates?
4468  evt.data.l[4] = iY; // should I indicate WHICH WINDOW this should be for?
4469 
4470  iMouseX = ((XButtonEvent *)pEvent)->x; // keep track of current drag position
4471  iMouseY = ((XButtonEvent *)pEvent)->y;
4472 
4473  WBPostPriorityEvent(((XButtonEvent *)pEvent)->window, (XEvent *)&evt);
4474  }
4475 // else
4476 // {
4477 // WB_ERROR_PRINT("TEMPORARY: %s:%d mouse drag motion NOT handled: %d %d,%d (%d,%d) \n",
4478 // __FUNCTION__, __LINE__,
4479 // iButton1, iMouseX, iMouseY, ((XButtonEvent *)pEvent)->x, ((XButtonEvent *)pEvent)->y);
4480 // }
4481  }
4482  else if(pEvent->type == ButtonRelease) // TODO: check mouse button and key mask match
4483  {
4484  // handle drop event
4485 
4486  XClientMessageEvent evt = {
4487  .type=ClientMessage,
4488  .serial=0,
4489  .send_event=0,
4490  .display=pDisplay,
4491  .window=((XButtonEvent *)pEvent)->window,
4492  .message_type=aWB_POINTER,
4493  .format=32
4494  };
4495 
4496  evt.data.l[0] = WB_POINTER_DROP;
4497  evt.data.l[1] = iDragButtonState;
4498  evt.data.l[2] = (iModShiftCtrl & ShiftMask ? WB_KEYEVENT_SHIFT : 0)
4499  | (iModShiftCtrl & ControlMask ? WB_KEYEVENT_CTRL : 0)
4500  | (iModShiftCtrl & Mod1Mask ? WB_KEYEVENT_ALT : 0);
4501 
4502  WBXlatCoordPoint(((XButtonEvent *)pEvent)->window, ((XButtonEvent *)pEvent)->x, ((XButtonEvent *)pEvent)->y,
4503  ((XButtonEvent *)pEvent)->window, &iX, &iY);
4504 
4505  evt.data.l[3] = iX; // TODO: should this be UN-TRANSLATED or SCREEN coordinates?
4506  evt.data.l[4] = iY; // should I indicate WHICH WINDOW this should be for?
4507 
4508  WBPostPriorityEvent(((XButtonEvent *)pEvent)->window, (XEvent *)&evt);
4509 
4510  if(wMouseCapture != None)
4511  {
4512  // end the mouse capture
4513  XUngrabPointer(pDisplay, CurrentTime);
4514  wMouseCapture = None;
4515  }
4516 
4517  iMouseState = MouseState_NONE; // complete
4518  }
4519 
4520  break;
4521  }
4522  }
4523  else if(iMouseState != MouseState_NONE &&
4524  (pEvent->type == KeyPress ||
4525  pEvent->type == KeyRelease))
4526  {
4527  int iModShiftCtrl = ((XKeyEvent *)pEvent)->state & (ShiftMask | LockMask | ControlMask | Mod1Mask
4528  | Mod2Mask | Mod3Mask | Mod4Mask | Mod5Mask);
4529 
4530  int iKey = XLookupKeysym((XKeyEvent *)pEvent, 0); // always use '0' index for these
4531  //WBKeyEventProcessKey((XKeyEvent *)pEvent, NULL, NULL, NULL)
4532 
4533  // mouse state cancelation due to pressing or releasing a key AFTERWARDS
4534  // for now limit this to escape, space, enter, and one of the modifier keys
4535 
4536  if(iModShiftCtrl != iMouseModShiftCtrl ||
4537  (pEvent->type == KeyPress &&
4538  (iKey == XK_Escape || iKey == XK_Return || iKey == XK_KP_Enter)))
4539  {
4540  // cancel any mouse states due to pressing a key
4541 
4542  WBMouseCancel(pDisplay, ((XButtonEvent *)pEvent)->window);
4543  }
4544  }
4545  }
4546 
4547  return iRval;
4548 }
4549 
4550 
4551 /**********************************************************************/
4552 /* */
4553 /* message dispatching functions */
4554 /* (see also 'WBInternalProcessExposeEvent', below) */
4555 /* */
4556 /**********************************************************************/
4557 
4558 int WBAppDispatch(XEvent *pEvent)
4559 {
4560 Atom aTemp;
4561 XEvent evtTemp;
4562 
4563  // internal-only messages [not hooked]
4564 
4565  if(pEvent->xany.type == ClientMessage
4566  && pEvent->xclient.message_type == aSET_FOCUS) // async assign focus (top level window only)
4567  {
4568  XWindowChanges xwc;
4569  Window wID = (Window)pEvent->xclient.data.l[0];
4570  Window wIDFrom = (Window)pEvent->xclient.data.l[1]; // window inadvertently getting focus
4571  Window wIDRoot = None;
4572  _WINDOW_ENTRY_ *pEntry = WBGetWindowEntry(wID);
4573  Display *pDisplay;
4574 
4575  if(pEntry && pEntry->pDisplay && pEvent->xany.display &&
4576  pEvent->xany.display != pEntry->pDisplay)
4577  {
4578  pEntry = NULL; // anticipate multiple displays with matching window ID's in this case...
4579  WB_ERROR_PRINT("ERROR - %s - display pointer mismatch\n", __FUNCTION__);
4580  }
4581 
4582  if(pEntry)
4583  {
4584  if(pEntry->pDisplay)
4585  {
4586  pDisplay = pEntry->pDisplay;
4587  }
4588  else
4589  {
4590  pDisplay = pDefaultDisplay;
4591  }
4592  }
4593  else if(pEvent->xany.display) // TODO: verify it's not different from that for pEntry
4594  {
4595  pDisplay = pEvent->xany.display;
4596  }
4597  else
4598  {
4599  pDisplay = pDefaultDisplay;
4600  }
4601 
4602 // fprintf(stderr, "** TEMPORARY - %s setting focus to %08xH\n", __FUNCTION__, (unsigned int)wID);
4603  // NOTE: if this is not a top level window, results may be unexpected
4604 
4605  // tell the WM to mark the desired window as "the active window"
4606  aTemp = XInternAtom (pEvent->xany.display ? pEvent->xany.display : pDefaultDisplay,
4607  "_NET_ACTIVE_WINDOW", False); /* global scope, must use XInternAtom */
4608 
4609  bzero(&evtTemp, sizeof(evtTemp));
4610 
4611  evtTemp.xany.type = ClientMessage;
4612  evtTemp.xany.send_event = True; // will send it, not post
4613  evtTemp.xany.display = pEvent->xany.display ? pEvent->xany.display : pDefaultDisplay;
4614  evtTemp.xany.window = wID; // in this case, the window to make active
4615  evtTemp.xany.serial = 0;
4616 
4617  __internal_GetParent(evtTemp.xany.display, wID, &wIDRoot); // send to 'wRoot'
4618 
4619  // client message params
4620  evtTemp.xclient.message_type = aTemp;
4621  evtTemp.xclient.format = 32;
4622  evtTemp.xclient.data.l[0] = 0; // "use old method" (should be suitable)
4623  evtTemp.xclient.data.l[1] = 0; // timestamp (zero for now)
4624  evtTemp.xclient.data.l[2] = wIDFrom; // window that was incorrectly set 'active'
4625 
4627  XSendEvent(pDisplay, wIDRoot, False,
4628  SubstructureRedirectMask | SubstructureNotifyMask,
4629  &evtTemp);
4631 
4632  WBSetInputFocus(wID); // set input focus to this window (after doing the above)
4633  // window managers that ignore _NET_ACTIVE_WINDOW should work OK with JUST
4634  // a call to XSetInputFocus. fluxbox works when you use XSetInputFocus
4635 
4636  bzero(&xwc, sizeof(xwc));
4637  xwc.stack_mode = Above;
4638 
4639  // bring window forward
4641 
4642  XConfigureWindow(pDisplay, wID, CWStackMode, &xwc);
4643  XMapWindow(pDisplay, wID);
4644 
4646 
4647  return 1; // handled
4648  }
4649 
4650  if(!pAppEventCallback || !pAppEventCallback(pEvent))
4651  {
4652  return WBAppDefault(pEvent);
4653  }
4654 
4655  return 0;
4656 }
4657 
4658 void WBDispatch(XEvent *pEvent)
4659 {
4660  // determine the window ID for the message, if applicable
4661 
4662 // if(pEvent->type == KeymapEvent)
4663 // {
4664 // }
4665  if(pEvent->xany.window == None) // the application
4666  {
4667  WBAppDispatch(pEvent);
4668  }
4669  else
4670  {
4671  WBWindowDispatch(pEvent->xany.window, pEvent);
4672  }
4673 
4674  __PeriodicWindowEntryCleanup(); // TODO: find a more intelligent way to make this work
4675 }
4676 
4677 
4678 int WBWindowDispatch(Window wID, XEvent *pEvent)
4679 {
4680  _WINDOW_ENTRY_ *pEntry = WBGetWindowEntry(wID);
4681  int iRval = 0, iSetFocus = 0;
4682 
4683  if(pEvent->type == ClientMessage)
4684  {
4685  WB_DEBUG_PRINT(DebugLevel_Heavy | DebugSubSystem_Event | DebugSubSystem_Window,
4686  "%s - client message, pEntry==%pH\n", __FUNCTION__, pEntry);
4687  }
4688 
4689  if(pEntry)
4690  {
4691  // detect resize, mouse, and keystrokes for menu
4692 
4693  if(pEntry->wIDMenu)
4694  {
4695  _WINDOW_ENTRY_ *pMenuEntry = WBGetWindowEntry(pEntry->wIDMenu);
4696 
4697  if(WB_UNLIKELY(!pMenuEntry))
4698  {
4699  WB_WARN_PRINT("%s - pMenuEntry is NULL, pEntry->wIDMenu = %d (%08xH)\n",
4700  __FUNCTION__, (int)pEntry->wIDMenu, (int)pEntry->wIDMenu);
4701  }
4702  else if(WB_UNLIKELY(WB_IS_WINDOW_DESTROYED(*pMenuEntry)))
4703  {
4704  // DO NOT dispatch messages to destroyed menus
4705 
4706  WB_WARN_PRINT("%s:%d - %d (%08xH) NOT mapped\n",
4707  __FUNCTION__, __LINE__, (int)wID, (int)wID);
4708 
4709  goto exit_point; // NOT handled (should I change this?)
4710  }
4711  else if(pMenuEntry->pMenuCallback &&
4712  (pEvent->type == ConfigureNotify || // resize, reposition
4713  pEvent->type == EnterNotify ||
4714  pEvent->type == LeaveNotify ||
4715  pEvent->type == KeyPress ||
4716  pEvent->type == KeyRelease ||
4717  pEvent->type == ButtonPress ||
4718  pEvent->type == ButtonRelease ||
4719  pEvent->type == MotionNotify ||
4720  pEvent->type == ClientMessage))
4721  {
4722  // TODO: do I want to exclude 'Expose' and other event types ???
4723  // [this is the only part that calls the client's callback function]
4724 
4725  iRval = pMenuEntry->pMenuCallback(pMenuEntry->wID, pEvent);
4726 
4727  if(iRval)
4728  {
4729  goto exit_point; // handled
4730  }
4731  }
4732  }
4733 
4734  if(pEvent->type == Expose)
4735  {
4736  WB_GEOM geom;
4737 
4738  if(WB_UNLIKELY(WB_IS_WINDOW_DESTROYED(*pEntry)))
4739  {
4740  // DO NOT dispatch messages to destroyed menus
4741 
4742  WB_WARN_PRINT("%s:%d - %d (%08xH) NOT mapped\n",
4743  __FUNCTION__, __LINE__, (int)wID, (int)wID);
4744 
4745  goto exit_point; // NOT handled (should I change this?)
4746  }
4747 
4748  WBGetWindowGeom(wID, &geom);
4749 
4750  pEntry->width = geom.width; // update geometry for 'pEntry'
4751  pEntry->height = geom.height;
4752  pEntry->border = geom.border;
4753 
4754  iSetFocus = WB_CHECK_SET_FOCUS_ON_MAP(*pEntry);
4755  }
4756 
4757  if(pEvent->type == ReparentNotify && wID == pEvent->xreparent.window)
4758  {
4759  WB_DEBUG_PRINT(DebugLevel_Medium | DebugSubSystem_Window | DebugSubSystem_Event,
4760  "%s - re-parent event, %d (%08xH), %d (%08xH)\n",
4761  __FUNCTION__, (int)wID, (int)wID,
4762  (int)pEvent->xreparent.parent, (int)pEvent->xreparent.parent);
4763 
4764  pEntry->wParent = pEvent->xreparent.parent;
4765  }
4766 
4767  if(pEntry->pCallback) // CAN be NULL, especially if there are stale events waiting to be sent
4768  {
4769  iRval = pEntry->pCallback(wID, pEvent);
4770 
4771  if(iSetFocus)
4772  {
4773  WB_DEBUG_PRINT(DebugLevel_Light | DebugSubSystem_Window,
4774  "%s:%d - %d (%08xH) mapped, setting focus\n",
4775  __FUNCTION__, __LINE__, (int)wID, (int)wID);
4776 
4777 // WB_ERROR_PRINT("TEMPORARY - %s calling XSetInputFocus on wID=%u (%08xH)\n", __FUNCTION__, (int)wID, (uint32_t)wID);
4778 
4780  XSetInputFocus(pEntry->pDisplay, wID, RevertToParent, CurrentTime);
4782  }
4783 
4784  if(iRval) // if my callback 'handled' me properly
4785  {
4786  if(pEvent->type != DestroyNotify || // not a destroy notification
4787  pEvent->xdestroywindow.window != wID) // destroyed window isn't me
4788  {
4789  if(pEvent->type == DestroyNotify)
4790  {
4791  WB_ERROR_PRINT("%s - UNEXPECTED - DestroyNotify for %d and message is for %d\n",
4792  __FUNCTION__, (int)pEvent->xdestroywindow.window, (int)wID);
4793  }
4794 
4795  // return NOW - no further processing
4796 
4797  goto exit_point; // handled
4798  }
4799 
4800  // at this point, I had a DestroyNotify message for THIS window - flow through to next section
4801  }
4802  }
4803  else if(pEvent->type == DestroyNotify)
4804  {
4805  if(pEvent->xdestroywindow.window == wID)
4806  {
4807  // 'pEntry' is still valid, but SHOULD be marked 'to be destroyed'
4808  if(!WB_IS_WINDOW_DESTROYED(*pEntry)) // already destroyed? no need to say anything
4809  {
4810  // I am being destroyed and I have no callback
4811  WB_ERROR_PRINT("INFO: %s - DestroyNotify for wID %d (%s) and NO CALLBACK (this is sometimes expected)\n",
4812  __FUNCTION__, (int)wID, pEntry->szClassName);
4813  }
4814  }
4815  else
4816  {
4817  WB_ERROR_PRINT("ERROR: %s - (UNEXPECTED) DestroyNotify for %d and message is for %d (no callback)\n",
4818  __FUNCTION__, (int)pEvent->xdestroywindow.window, (int)wID);
4819  }
4820  }
4821  else if(iSetFocus) // still handle this, just in case
4822  {
4823  WB_DEBUG_PRINT(DebugLevel_Light | DebugSubSystem_Window,
4824  "%s:%d - %d (%08xH) mapped, setting focus\n",
4825  __FUNCTION__, __LINE__, (int)wID, (int)wID);
4826 
4827 // WB_ERROR_PRINT("TEMPORARY - %s calling XSetInputFocus on wID=%u (%08xH)\n", __FUNCTION__, (int)wID, (uint32_t)wID);
4828 
4830  XSetInputFocus(pEntry->pDisplay, wID, RevertToParent, CurrentTime);
4832  }
4833 
4834  // handle any DestroyNotify events at this point. A parent window may send these to child windows, even
4835  // after the window is actually destroyed and its callback NULL'd
4836  //
4837  // (NOTE: if I had a callback, I still handle destroy notifications for ME at this point)
4838 
4839  if(pEvent->type == DestroyNotify &&
4840  pEvent->xdestroywindow.window == wID) // I am being destroyed
4841  {
4842  // window destroyed, already called callback (if there is one)
4843 
4844  _WINDOW_ENTRY_ *pEntry2 = WBGetWindowEntry(wID);
4845 
4846  WB_DEBUG_PRINT(DebugLevel_Medium | DebugSubSystem_Window | DebugSubSystem_Event,
4847  "%s - DestroyNotify for %d (%08xH)\n",
4848  __FUNCTION__, (int)wID, (int)wID);
4849 
4850  if(pEntry2 && !iRval) // must call 'Default'
4851  {
4852 // WB_ERROR_PRINT("TEMPORARY - doing default processing for DestroyNotify\n");
4853  WBDefault(wID, pEvent);
4854  }
4855 
4856  // if this window is the current 'application' window, set the 'quit' flag.
4857  if(wID == wIDApplication)
4858  {
4859  bQuitFlag = TRUE; // application window destroyed, application must exit
4860  }
4861 
4862  if(pEntry2 && pEntry2 == pEntry)
4863  {
4864  // window destruction means delete remaining events, then unregister the callback
4865  __WBDelWindowEvents(pEntry->pDisplay, wID);
4866 
4867  pEntry->iWindowState = WB_WINDOW_DESTROYED; // to flag "I am destroying it now"
4868 // WB_ERROR_PRINT(">>TEMPORARY - %s - marking window %d (%s) destroyed and unregistering callback\n", __FUNCTION__, (int)wID, pEntry->szClassName);
4869 
4870  WBUnregisterWindowCallback(wID); // NOTE: this assigns 'wIDApplication' to None if this was the application window
4871 
4872  goto exit_point; // handled
4873  }
4874  else
4875  {
4876  WB_ERROR_PRINT("WARNING - %s - pEntry == %p, pEntry2 == %p\n", __FUNCTION__, pEntry, pEntry2);
4877  }
4878  }
4879  }
4880  else
4881  {
4882  WB_DEBUG_PRINT(DebugLevel_WARN/*Medium*/ | DebugSubSystem_Window | DebugSubSystem_Event /*| DebugSubSystem_Selection*/,
4883  "%s - Event %s for window %d (%08xH) not handled [no pEntry]\n",
4884  __FUNCTION__, WBEventName(pEvent->type), (int)wID, (int)wID);
4885  }
4886 
4887  // NOTE: I get here if the registered callback returns zero or isn't assigned
4888  // and it's either NOT a 'DestroyNotify' or there's no window entry
4889 
4890  if(!iRval)
4891  {
4892  iRval = WBDefault(wID, pEvent);
4893  }
4894  else
4895  {
4896  WBDefault(wID, pEvent); // do it anyway but don't use THAT return value
4897  }
4898 
4899 exit_point:
4900 
4901  // TODO: any "always do this" stuff belongs here
4902 
4903  return iRval;
4904 }
4905 
4906 int WBAppDefault(XEvent *pEvent)
4907 {
4908  return 0; // not handled
4909 }
4910 
4911 int WBDefault(Window wID, XEvent *pEvent)
4912 {
4913  int i1;
4914  _WINDOW_ENTRY_ *pEntry = WBGetWindowEntry(wID);
4915  Display *pDisplay = pEvent->xany.display ? pEvent->xany.display
4916  : pEntry ? pEntry->pDisplay : pDefaultDisplay;
4917 
4918 
4919  if (pEvent->type == Expose && pEvent->xexpose.count == 0)
4920  {
4921  // for now, just erase the background using whatever background color is currently assigned
4922  if(pEntry && pEntry->hGC != NULL)
4923  {
4924  WBGC gc = WBBeginPaint(wID, &(pEvent->xexpose), NULL);
4925  if(gc != NULL)
4926  {
4927  WBClearWindow(wID, gc);
4928  WBEndPaint(wID, gc);
4929  }
4930  }
4931  else
4932  {
4933 // WB_ERROR_PRINT("TEMPORARY: %s - window has NO assigned GC, calling XClearWindow\n", __FUNCTION__);
4934 
4935  XClearWindow(pDisplay, wID); // TODO: rather than erase background, see if I need to
4936  }
4937 
4938  return 1; // handled
4939  }
4940  else if(pEvent->type == ConfigureNotify && pEvent->xconfigure.window == wID)
4941  {
4942  WB_DEBUG_PRINT(DebugLevel_Medium | DebugSubSystem_Window | DebugSubSystem_Event,
4943  "%s - window %08xH gets ConfigureNotify\n",
4944  __FUNCTION__, (unsigned int)wID);
4945 
4946  // see if it's a move, or a re-size. on re-size I have to re-paint and re-calculate
4947  // the various display thingies. But if I'm just moving it, no need.
4948 
4949  if(pEvent->xconfigure.width != pEntry->width ||
4950  pEvent->xconfigure.height != pEntry->height ||
4951  pEvent->xconfigure.border_width != pEntry->border)
4952  {
4953  if(!WB_IS_WINDOW_MAPPED(*pEntry) || // if I'm not mapped, just change the numbers and create a region
4954  !pEntry->width || !pEntry->height) // same if width/height was zero (but is no longer)
4955  {
4956  XRectangle xrct;
4957 
4958  // NOTE: I must do it THIS way because the window isn't mapped yet.
4959 
4960  pEntry->width = pEvent->xconfigure.width;
4961  pEntry->height = pEvent->xconfigure.height;
4962  pEntry->border = pEvent->xconfigure.border_width;
4963 
4964  if(pEntry->rgnClip != None)
4965  {
4966  XDestroyRegion(pEntry->rgnClip);
4967  }
4968 
4969  pEntry->rgnClip = XCreateRegion();
4970 
4971  if(pEntry->rgnClip &&
4972  pEntry->width && pEntry->height) // make sure non-zero
4973  {
4974  xrct.x = 0;//(short)pEntry->x;
4975  xrct.y = 0;//(short)pEntry->y;
4976  xrct.width = (unsigned short)pEntry->width;
4977  xrct.height = (unsigned short)pEntry->height;
4978 
4979  XUnionRectWithRegion(&xrct, pEntry->rgnClip, pEntry->rgnClip);
4980  }
4981  }
4982  else
4983  {
4984 // WB_ERROR_PRINT("TEMPORARY: %s - ConfigureNotify invalidating geometry\n", __FUNCTION__);
4985 
4986  WBInvalidateGeom(wID, NULL, 1); // for simplicity, just re-paint it - note, NOT saying "do it now"
4987  }
4988  }
4989 
4990  return 1; // handled
4991  }
4992  else if(pEvent->type == VisibilityNotify &&
4993  pEvent->xvisibility.window == wID &&
4994  pEvent->xvisibility.state != VisibilityFullyObscured)
4995  {
4996  // Focus and Z-order changes generate this. If my window's state changes to at least partially 'unobscured',
4997  // then the response is to invalidate the entire window so it re-paints.
4998 
4999  // TODO: see what was covering it up before, to limit the re-painting
5000 
5001  WB_DEBUG_PRINT(DebugLevel_Medium | DebugSubSystem_Window | DebugSubSystem_Event,
5002  "%s - window %08xH gets VisibilityNotify, state=%d\n",
5003  __FUNCTION__, (unsigned int)pEvent->xvisibility.window, pEvent->xvisibility.state);
5004 
5005  WBInvalidateGeom(pEvent->xvisibility.window, NULL, 1); // TODO: determine what changed
5006 
5007  return 1; // handled
5008  }
5009  else if(pEvent->type == MapRequest &&
5010  (pEvent->xmaprequest.parent == wID || pEvent->xmaprequest.window == wID))
5011  {
5012  // XMapWindow and related can generate this. I want to see if it happens, ever.
5013 
5014  WB_ERROR_PRINT("TEMPORARY: %s - window %08xH gets MapRequest for %08xH\n",
5015  __FUNCTION__, (unsigned int)pEvent->xmaprequest.parent, (unsigned int)pEvent->xmaprequest.window);
5016  }
5017  else if(pEvent->type == ClientMessage)
5018  {
5019 // fprintf(stderr, "TEMPORARY: client message in WBDefault\n");
5020  if(pEvent->xclient.message_type == aWM_PROTOCOLS && pEvent->xclient.window == wID)
5021  {
5022  if(pEvent->xclient.data.l[0] == aWM_DELETE_WINDOW)
5023  {
5024  // If this is managed by the WM, I'll get this message in response to a request
5025  // to close the window. This includes clicking the 'x' button in the title bar
5026 
5027  XClientMessageEvent evt;
5028 
5029  WB_DEBUG_PRINT(DebugLevel_Medium | DebugSubSystem_Window | DebugSubSystem_Event,
5030  "%s - WM_DELETE_WINDOW for %d (%08xH)\n",
5031  __FUNCTION__, (int)wID, (int)wID);
5032 
5033  WB_ERROR_PRINT("TEMPORARY: %s - WM_PROTOCOLS WM_DELETE_WINDOW, requests call to XDestroyWindow for %u (%08xH)\n", __FUNCTION__, (int)wID, (int)wID);
5034 
5035  // the default action is to FIRST query the window to see if it's ok to destroy it. if it is,
5036  // the window should THEN safely destroy all of its child windows, etc. before returning, so that
5037  // I don't end up with a pile of X11 errors.
5038 
5039  bzero(&evt, sizeof(evt));
5040  evt.type = ClientMessage;
5041  evt.display = pDisplay;
5042  evt.window = wID;
5043  evt.message_type = aQUERY_CLOSE; // QUERY_CLOSE request
5044  evt.format = 32;
5045  evt.data.l[0] = 1; // tell it to destroy contents if ok to close
5046 
5047  i1 = WBWindowDispatch(evt.window, (XEvent *)&evt);
5048 
5049  if(i1 <= 0) // ok to close the window (or there was an error, so close anyway)
5050  {
5051  if(i1 < 0)
5052  {
5053  WB_ERROR_PRINT("ERROR: %s - QUERY_CLOSE returns %d, destroying window %u (%08xH) anyway\n",
5054  __FUNCTION__, i1, (int)wID, (int)wID);
5055  }
5056  else
5057  {
5058  WB_ERROR_PRINT("INFO: %s - QUERY_CLOSE returns %d, ok to destroy window %u (%08xH)\n",
5059  __FUNCTION__, i1, (int)wID, (int)wID);
5060  }
5061 
5062  if(wID == wIDApplication) // if it's the "application window", set the quit flag
5063  {
5064 // WB_ERROR_PRINT("TEMPORARY: %s - setting 'quit' flag\n", __FUNCTION__);
5065 
5066  bQuitFlag = TRUE; // application window destroyed, application must exit
5067  }
5068 
5069  // window destruction means delete remaining events, then unregister the callback
5070  __WBDelWindowEvents(pEntry->pDisplay, wID);
5071 
5072  pEntry->iWindowState = WB_WINDOW_DESTROYED; // to flag "I am destroying it now"
5073 
5074  WBUnregisterWindowCallback(wID); // make sure this happens - FYI it assigns matching wIDApplication to 'None'
5075 
5076 // WBDestroyWindow(wID); // destroy the window (DO NOT CALL THIS, the application will stay running if it's the main frame)
5077 
5078  // default action is to destroy the window *NOW*
5080  XDestroyWindow(pDisplay, wID); // todo: wrap this in ???
5082  }
5083 
5084  return 1; // "handled"
5085  }
5086  }
5087  else if(pEvent->xclient.message_type == aWB_TIMER && pEvent->xclient.window == wID)
5088  {
5089  WB_DEBUG_PRINT(DebugLevel_Light | DebugSubSystem_Window | DebugSubSystem_Event,
5090  "%s - un-handled WB_TIMER for %d (%08xH)\n",
5091  __FUNCTION__, (int)wID, (int)wID);
5092  }
5093 #ifndef NO_DEBUG // uncomment this block to dump every event NOT handled
5094  else
5095  {
5096  WB_DEBUG_PRINT(DebugLevel_Light | DebugSubSystem_Window | DebugSubSystem_Event,
5097  "%s - un-handled ClientMessage for %d (%08xH)\n",
5098  __FUNCTION__, (int)wID, (int)wID);
5099 
5100  WB_IF_DEBUG_LEVEL(DebugLevel_Light | DebugSubSystem_Window | DebugSubSystem_Event)
5101  {
5102  WBDebugDumpEvent(pEvent);
5103  }
5104  }
5105 #endif // NO_DEBUG
5106  }
5107  else if(pEvent->type == KeyPress ||
5108  pEvent->type == KeyRelease)
5109  {
5110  WB_DEBUG_PRINT(DebugLevel_Excessive | DebugSubSystem_Window | DebugSubSystem_Event | DebugSubSystem_Keyboard,
5111  "%s - key press/release event for %d (%08xH) in default handler\n",
5112  __FUNCTION__, (int)wID, (int)wID);
5113 
5114  if(pEvent->type == KeyPress /* || pEvent->type == KeyRelease */)
5115  {
5116  // generate a WB_CHAR event for unhandled 'keypress' events
5117  // this lets all windows have a chance at intercepting the
5118  // keypress event before a WB_CHAR is generated
5119 
5120  XClientMessageEvent evt;
5121  int cbData, iACS = 0;
5122 
5123  // post a high-priority message to myself to display the menu
5124 
5125  bzero(&evt, sizeof(evt));
5126  evt.type = ClientMessage;
5127  evt.display = pDisplay;
5128  evt.window = pEvent->xany.window;
5129  evt.message_type = aWB_CHAR; // WB_CHAR notification
5130  evt.format = 32;
5131 
5132  cbData = sizeof(evt.data) - 3 * sizeof(evt.data.l[0]);
5133  evt.data.l[0] = WBKeyEventProcessKey((XKeyEvent *)pEvent,
5134  (char *)&(evt.data.l[3]),
5135  &cbData, &iACS);
5136 
5137  evt.data.l[1] = iACS;
5138  evt.data.l[2] = cbData;
5139 
5140  // pressing or releasing shift, ctrl, alt, or 'meta' must not
5141  // generate an event. Fortunately these are all within a range
5142 
5143  if(evt.data.l[0] < XK_Shift_L || evt.data.l[0] > XK_Hyper_R)
5144  {
5145  // NOTE: l[0] is return value from WBKeyEventProcessKey
5146  // l[1] is iACS return value
5147  // l[2] is # of characters decoded into 'l[3]'
5148  // l[3..] is character decode buffer
5149 
5150  if(iACS & WB_KEYEVENT_KEYSYM)
5151  {
5152  // temporary debug
5153  WB_DEBUG_PRINT(DebugLevel_Heavy | DebugSubSystem_Event | DebugSubSystem_Keyboard,
5154  "%s - generating WB_CHAR for KEYSYM %ld (%lxH) iACS=%xH window %d (%08xH)\n",
5155  __FUNCTION__, evt.data.l[0], evt.data.l[0], iACS,
5156  (int)pEvent->xany.window, (int)pEvent->xany.window);
5157  }
5158  else
5159  {
5160  // temporary debug
5161  WB_DEBUG_PRINT(DebugLevel_Heavy | DebugSubSystem_Event | DebugSubSystem_Keyboard,
5162  "%s - generating WB_CHAR \"%.*s\" iACS=%xH window %d (%08xH)\n",
5163  __FUNCTION__, cbData, (char *)&(evt.data.l[3]), iACS,
5164  (int)pEvent->xany.window, (int)pEvent->xany.window);
5165  }
5166 
5167  WBPostPriorityEvent(pEvent->xany.window, (XEvent *)&evt);
5168  }
5169  }
5170 
5171  }
5172  else if(pEvent->type == DestroyNotify &&
5173  pEvent->xdestroywindow.window == wID)
5174  {
5175  _WINDOW_ENTRY_ *pEntry = WBGetWindowEntry(wID);
5176 
5177  if(pEntry && pEntry->wIDMenu)
5178  {
5179  Window wIDMenu = pEntry->wIDMenu;
5180  pEntry->wIDMenu = 0;
5181 
5182  // Look for any other occurences of this menu attached to any other window
5183  // TODO: make this more efficient, eh?
5184  // TODO: see if I even need this. I've not been sharing WBMenu resources at all...
5185 
5186  for(i1=0; i1 <= WINDOW_ENTRY_ARRAY_MAX; i1++)
5187  {
5188  if(sWBHashEntries[i1].wIDMenu == wIDMenu)
5189  break;
5190  }
5191 
5192  // am I the last guy using the menu?
5193  if(i1 > WINDOW_ENTRY_ARRAY_MAX)
5194  {
5195  WBDestroyWindow(wIDMenu);
5196  }
5197  }
5198  }
5199  else if(pEvent->type == SelectionRequest)
5200  {
5201  // NOTE: selection events are handled by the Clipboard worker thread. However, I can still (possibly)
5202  // get these things sent to me. The *POLITE* thing to do is respond to them with an error sent
5203  // back to the requestor. And so, that's what I do. And display it in on stderr in debug code...
5204 #ifndef NO_DEBUG
5205  char *p1 = pEvent->xselectionrequest.selection != None ? WBGetAtomName(pDisplay, pEvent->xselectionrequest.selection) : NULL;
5206  char *p2 = pEvent->xselectionrequest.target != None ? WBGetAtomName(pDisplay, pEvent->xselectionrequest.target) : NULL;
5207  char *p3 = pEvent->xselectionrequest.property != None ? WBGetAtomName(pDisplay, pEvent->xselectionrequest.property) : NULL;
5208 
5209  WB_ERROR_PRINT("TEMPORARY - %s - SelectionRequest wID=%d(%08xH) class=%s owner=%d requestor=%d selection=%s target=%s property=%s\n",
5210  __FUNCTION__,
5211  (int)wID, (int)wID,
5212  (const char *)(pEntry ? pEntry->szClassName : "UNK"),
5213  (int)pEvent->xselectionrequest.owner,
5214  (int)pEvent->xselectionrequest.requestor,
5215  p1 ? p1 : "NULL",
5216  p2 ? p2 : "NULL",
5217  p3 ? p3 : "NULL");
5218 
5219  if(p1)
5220  {
5221  WBFree(p1);
5222  }
5223  if(p2)
5224  {
5225  WBFree(p2);
5226  }
5227  if(p3)
5228  {
5229  WBFree(p3);
5230  }
5231 #endif // NO_DEBUG
5232 
5233  // the default rejects the request. Send a 'SelectionNotify' to indicate the failure
5234  // failing to send the SelectionNotify can REALLY screw things up for the requestor
5235 
5236  if(pEvent->xselectionrequest.owner == wID) // only if I'm the selection owner
5237  {
5238  XSelectionEvent evtN;
5239 
5240  memset(&evtN, 0, sizeof(evtN));
5241 
5242  evtN.type = SelectionNotify;
5243  evtN.send_event = True; // 'cause I'm going to use 'XSendEvent' to reply immediately
5244  evtN.requestor = pEvent->xselectionrequest.requestor;
5245  evtN.selection = pEvent->xselectionrequest.selection;
5246  evtN.target = pEvent->xselectionrequest.target;
5247  evtN.property = None; // to indicate failure
5248  evtN.time = pEvent->xselectionrequest.time; // same time as request (for now)
5249 
5251  XSendEvent(pDisplay, evtN.requestor, False, 0, (XEvent *)&evtN);
5253  }
5254  }
5255  else if(pEvent->type == SelectionClear)
5256  {
5257 #ifndef NO_DEBUG
5258  char *p1 = WBGetAtomName(pDisplay, pEvent->xselectionclear.selection);
5259 
5260  WB_ERROR_PRINT("TEMPORARY - %s - SelectionClear wID=%d(%08xH) class=%s window=%d selection=%s\n",
5261  __FUNCTION__,
5262  (int)wID, (int)wID,
5263  (const char *)(pEntry ? pEntry->szClassName : "UNK"),
5264  (int)pEvent->xselectionclear.window,
5265  p1 ? p1 : "NULL");
5266 
5267  if(p1)
5268  {
5269  WBFree(p1);
5270  }
5271 #endif // NO_DEBUG
5272 
5273  // the default rejects the request. No need to send a 'SelectionNotify'
5274  }
5275  else if(pEvent->type == SelectionNotify)
5276  {
5277 #ifndef NO_DEBUG
5278  char *p1 = pEvent->xselection.selection != None ? WBGetAtomName(pDisplay, pEvent->xselection.selection) : NULL;
5279  char *p2 = pEvent->xselection.target != None ? WBGetAtomName(pDisplay, pEvent->xselection.target) : NULL;
5280  char *p3 = pEvent->xselection.property != None ? WBGetAtomName(pDisplay, pEvent->xselection.property) : NULL;
5281 
5282  WB_ERROR_PRINT("TEMPORARY - %s - SelectionNotify wID=%d(%08xH) class=%s requestor=%d selection=%s target=%s property=%s\n",
5283  __FUNCTION__,
5284  (int)wID, (int)wID,
5285  (const char *)(pEntry ? pEntry->szClassName : "UNK"),
5286  (int)pEvent->xselection.requestor,
5287  p1 ? p1 : "NULL",
5288  p2 ? p2 : "NULL",
5289  p3 ? p3 : "NULL");
5290 
5291  if(p1)
5292  {
5293  WBFree(p1);
5294  }
5295  if(p2)
5296  {
5297  WBFree(p2);
5298  }
5299  if(p3)
5300  {
5301  WBFree(p3);
5302  }
5303 #endif // NO_DEBUG
5304  }
5305 //#ifndef NO_DEBUG // uncomment this block to dump every event NOT handled
5306 // else
5307 // {
5308 // WBDebugDumpEvent(pEvent);
5309 // }
5310 //#endif // NO_DEBUG
5311 
5312  return 0; // indicate 'not handled' for now
5313 }
5314 
5315 
5316 void WBProcessExposeEvent(XExposeEvent *pEvent)
5317 {
5318 XEvent xevt;
5319 
5320  // first, handle the expose event as-is
5321  WBInternalProcessExposeEvent(pEvent);
5322 
5323  // next, grab whatever expose events are still out there and combine them, too
5324 
5325 // XFlush(pEvent->display);
5326  XSync(pEvent->display, 0); // to grab whatever expose events might be out there
5327 
5328  while(!bQuitFlag && XCheckTypedWindowEvent(pEvent->display, pEvent->window, Expose, &xevt))
5329  {
5330  WBInternalProcessExposeEvent((XExposeEvent *)&xevt);
5331  }
5332 }
5333 
5334 
5335 void WBMouseCancel(Display *pDisplay, Window wID)
5336 {
5337 XClientMessageEvent evt;
5338 int iX, iY;
5339 
5340 
5341  if((iMouseState != MouseState_LDRAG &&
5342  iMouseState != MouseState_RDRAG &&
5343  iMouseState != MouseState_CDRAG &&
5344  iMouseState != MouseState_WAS_LCLICK &&
5345  iMouseState != MouseState_WAS_RCLICK &&
5346  iMouseState != MouseState_WAS_CCLICK) // expected states to do a 'Mouse Cancel' for
5347  || wMouseCapture == None)
5348  {
5349  if(wMouseCapture != None)
5350  {
5351  WB_ERROR_PRINT("ERROR: %s - incorrect mouse state: %d (%08xH)\n", __FUNCTION__, iMouseState, iMouseState);
5352 
5353  XUngrabPointer(pDisplay ? pDisplay : WBGetDefaultDisplay(), CurrentTime);
5354 
5355  wMouseCapture = None; // not captured
5356  }
5357  else if(iMouseState == MouseState_NONE)
5358  {
5359  return; // just ignore it. no harm, no foul. mouse not captured, state is 'None'
5360  }
5361  else if(iMouseState != MouseState_WAS_LCLICK &&
5362  iMouseState != MouseState_WAS_RCLICK &&
5363  iMouseState != MouseState_WAS_CCLICK) // the "was" states are ok here. 'mouse cancel' will not be sent, however
5364  {
5365  // any other mouse state is unexpected here. spit out a message
5366 
5367  WB_ERROR_PRINT("ERROR: %s - mouse not captured. mouse state: %d (%08xH) \n", __FUNCTION__, iMouseState, iMouseState);
5368  }
5369 
5370  // TODO: send notification for a 'WAS' state??
5371 
5372  iMouseState = MouseState_NONE; // by convention, do this (always) on mouse cancel
5373 
5374  return; // for now, just do this [no capture]
5375  }
5376 
5377  if(wID == None)
5378  {
5379  wID = wMouseCapture; // the window with the mouse capture (checked for 'None' previously)
5380  }
5381 
5382  if(!pDisplay)
5383  {
5384  pDisplay = WBGetWindowDisplay(wID);
5385  if(!pDisplay)
5386  {
5387  pDisplay = WBGetDefaultDisplay(); // desperately make it work
5388  }
5389  }
5390 
5391  bzero(&evt, sizeof(evt));
5392  evt.type=ClientMessage;
5393  evt.display=pDisplay;
5394  evt.window=wID;
5395  evt.message_type=aWB_POINTER;
5396  evt.format=32;
5397  evt.data.l[0] = WB_POINTER_CANCEL;
5398 
5399  evt.data.l[1] = iMouseDragButtonState;
5400  evt.data.l[2] = (iMouseModShiftCtrl & ShiftMask ? WB_KEYEVENT_SHIFT : 0)
5401  | (iMouseModShiftCtrl & ControlMask ? WB_KEYEVENT_CTRL : 0)
5402  | (iMouseModShiftCtrl & Mod1Mask ? WB_KEYEVENT_ALT : 0);
5403 
5404  WBXlatCoordPoint(wMouseCapture, iMouseX, iMouseY, wMouseCapture, &iX, &iY);
5405 
5406  evt.data.l[3] = iX;
5407  evt.data.l[4] = iY;
5408 
5409  WBPostPriorityEvent(wID, (XEvent *)&evt); // post the WB_POINTER 'WB_POINTER_CANCEL' event
5410 
5411  iMouseState = MouseState_NONE; // canceled
5412 
5413  XUngrabPointer(pDisplay, CurrentTime);
5414 
5415  wMouseCapture = None;
5416  iMouseModShiftCtrl = iMouseDragButtonState = 0;
5417 }
5418 
5419 
5420 
5421 int WBMapWindow(Display *pDisplay, Window wID)
5422 {
5423  _WINDOW_ENTRY_ *pEntry = WBGetWindowEntry(wID);
5424  int iRval, iSetFocus = 0;
5425 
5426  if(pEntry)
5427  {
5428  iSetFocus = WB_CHECK_SET_FOCUS_ON_MAP(*pEntry);
5429 
5430  pEntry->iWindowState = WB_WINDOW_MAPPED;
5431 
5432  if(!pEntry->pWMHints)
5433  {
5434  // if no window hints were configured, set some up
5435 
5436  if(!__internal_alloc_WMHints(pEntry))
5437  {
5439  XSetWMHints(pEntry->pDisplay, wID, pEntry->pWMHints);
5441  }
5442  }
5443  }
5444 
5446  iRval = XMapWindow(pDisplay, wID);
5448 
5449  if(iSetFocus)
5450  {
5451  WB_DEBUG_PRINT(DebugLevel_Light | DebugSubSystem_Window,
5452  "%s:%d - %d (%08xH) mapped, setting focus\n",
5453  __FUNCTION__, __LINE__, (int)wID, (int)wID);
5454 
5455 // WB_ERROR_PRINT("TEMPORARY - %s calling XSetInputFocus on wID=%u (%08xH)\n", __FUNCTION__, (int)wID, (uint32_t)wID);
5456 
5458  XSetInputFocus(pEntry->pDisplay, wID, RevertToParent, CurrentTime);
5460  }
5461 
5462  return iRval;
5463 }
5464 
5465 int WBMapRaised(Display *pDisplay, Window wID)
5466 {
5467  _WINDOW_ENTRY_ *pEntry = WBGetWindowEntry(wID);
5468 
5469  int iRval, iSetFocus = 0;
5470 
5471  if(pEntry)
5472  {
5473  iSetFocus = WB_CHECK_SET_FOCUS_ON_MAP(*pEntry);
5474 
5475  pEntry->iWindowState = WB_WINDOW_MAPPED;
5476 
5477  if(!pEntry->pWMHints)
5478  {
5479  // if no window hints were configured, set some up
5480 
5481  if(!__internal_alloc_WMHints(pEntry))
5482  {
5484  XSetWMHints(pEntry->pDisplay, wID, pEntry->pWMHints);
5486  }
5487  }
5488  }
5489 
5490  iRval = XMapRaised(pDisplay, wID);
5491 
5492  if(iSetFocus)
5493  {
5494  WB_DEBUG_PRINT(DebugLevel_Light | DebugSubSystem_Window,
5495  "%s:%d - %d (%08xH) mapped, setting focus\n",
5496  __FUNCTION__, __LINE__, (int)wID, (int)wID);
5497 
5498 // WB_ERROR_PRINT("TEMPORARY - %s calling XSetInputFocus on wID=%u (%08xH)\n", __FUNCTION__, (int)wID, (uint32_t)wID);
5499 
5501  XSetInputFocus(pEntry->pDisplay, wID, RevertToParent, CurrentTime);
5503  }
5504 
5505  return iRval;
5506 }
5507 
5508 int WBUnmapWindow(Display *pDisplay, Window wID)
5509 {
5510  _WINDOW_ENTRY_ *pEntry = WBGetWindowEntry(wID);
5511  int iRval;
5512 
5513  if(pEntry)
5514  {
5515  if(WB_IS_WINDOW_UNMAPPED(*pEntry) || WB_IS_WINDOW_DESTROYED(*pEntry))
5516  {
5517  return 0;
5518  }
5519 
5520  pEntry->iWindowState = WB_WINDOW_UNMAPPED; // aka 'unmapped'
5521  }
5522 
5524  iRval = XUnmapWindow(pDisplay, wID);
5526 
5527  // TODO: if window HAD focus, set it to the main app window?
5528 
5529  return iRval;
5530 }
5531 
5532 
5533 int WBIsMapped(Display *pDisplay, Window wID)
5534 {
5535  _WINDOW_ENTRY_ *pEntry = WBGetWindowEntry(wID);
5536 
5537  if(pEntry && WB_IS_WINDOW_MAPPED(*pEntry) && !WB_IS_WINDOW_DESTROYED(*pEntry))
5538  {
5539  return 1;
5540  }
5541 
5542  // TODO: handle windows NOT being tracked???
5543 
5544  return 0;
5545 }
5546 
5547 int WBIsValid(Display *pDisplay, Window wID)
5548 {
5549  _WINDOW_ENTRY_ *pEntry = WBGetWindowEntry(wID);
5550 
5551  if(pEntry && !WB_IS_WINDOW_DESTROYED(*pEntry))
5552  {
5553 // fprintf(stderr, "TEMPORARY - returning 'valid' for window %d\n", wID);
5554  return 1;
5555  }
5556 
5557  // TODO: handle windows NOT being tracked???
5558 
5559  return 0;
5560 }
5561 
5562 
5563 
5564 /**********************************************************************/
5565 /* */
5566 /* basic window property functions */
5567 /* */
5568 /**********************************************************************/
5569 
5570 static int __internal_alloc_WMHints(_WINDOW_ENTRY_ *pEntry)
5571 {
5572  if(pEntry)
5573  {
5574  if(pEntry->pWMHints)
5575  {
5576  return 0;
5577  }
5578  else
5579  {
5580  pEntry->pWMHints = XAllocWMHints();
5581  if(pEntry->pWMHints)
5582  {
5583  // set defaults
5584  pEntry->pWMHints->initial_state = NormalState;
5585  pEntry->pWMHints->input = True; // this means the WINDOW MANAGER should manage input focus (this is normal)
5586  pEntry->pWMHints->flags = InputHint | StateHint;
5587 
5588  return 0;
5589  }
5590  }
5591  }
5592 
5593  return -1;
5594 }
5595 
5596 Display * WBGetWindowDisplay(Window wID)
5597 {
5598  _WINDOW_ENTRY_ *pEntry = WBGetWindowEntry(wID);
5599 
5600  if(pEntry)
5601  {
5602  return pEntry->pDisplay;
5603  }
5604 
5605  return pDefaultDisplay;
5606 }
5607 
5608 void WBSetWindowIcon(Window wID, int idIcon)
5609 {
5610  _WINDOW_ENTRY_ *pEntry = WBGetWindowEntry(wID);
5611  Display *pDisp = pEntry ? pEntry->pDisplay : pDefaultDisplay;
5612 
5613  if(pEntry)
5614  {
5615  if(__internal_alloc_WMHints(pEntry))
5616  {
5617  return;
5618  }
5619 
5621  if(pEntry->pxIcon != None)
5622  {
5623  XFreePixmap(pDisp, pEntry->pxIcon);
5624  pEntry->pxIcon = None;
5625  }
5626 
5627  if(pEntry->pxMask != None)
5628  {
5629  XFreePixmap(pDisp, pEntry->pxMask);
5630  pEntry->pxMask = None;
5631  }
5633 
5634  pEntry->pxIcon = PXM_GetIconPixmap(idIcon, NULL, &(pEntry->pxMask));
5635 
5636  pEntry->pWMHints->flags |= IconPixmapHint; // (InputHint|StateHint|IconPixmapHint);
5637  if(pEntry->pxMask)
5638  pEntry->pWMHints->flags |= IconMaskHint;
5639  else
5640  pEntry->pWMHints->flags &= ~IconMaskHint;
5641 
5642  pEntry->pWMHints->icon_pixmap = pEntry->pxIcon;
5643  pEntry->pWMHints->icon_mask = pEntry->pxMask;
5644 
5646  XSetWMHints(pDisp, wID, pEntry->pWMHints);
5648  }
5649 }
5650 
5651 //void WBSetWindowFontStruct(Window wID, XFontStruct *pFontStruct)
5652 //{
5653 // _WINDOW_ENTRY_ *pEntry = WBGetWindowEntry(wID);
5654 //
5655 // if(pEntry)
5656 // {
5657 // if(pEntry->pFontStruct && pEntry->pFontStruct != pDefaultFont) // must delete it
5658 // {
5659 // XFreeFont(pEntry->pDisplay, pEntry->pFontStruct);
5660 // }
5661 //
5662 // if(pEntry->fontSet != None && pEntry->fontSet != fontsetDefault)
5663 // {
5664 // XFreeFontSet(pEntry->pDisplay, pEntry->fontSet);
5665 // }
5666 //
5667 // pEntry->fontSet = None; // always, before I do the next part
5668 //
5669 // pEntry->pFontStruct = pFontStruct;
5670 //
5671 // if(pFontStruct)
5672 // {
5673 // pEntry->fontSet = WBFontSetFromFont(pEntry->pDisplay, pFontStruct);
5674 // }
5675 // }
5676 //}
5677 
5678 //void WBSetWindowFontSet(Window wID, XFontSet fontSet)
5679 //{
5680 // _WINDOW_ENTRY_ *pEntry = WBGetWindowEntry(wID);
5681 //
5682 // if(pEntry)
5683 // {
5684 // if(pEntry->pFontStruct && pEntry->pFontStruct != pDefaultFont) // must delete it
5685 // {
5686 // XFreeFont(pEntry->pDisplay, pEntry->pFontStruct);
5687 // }
5688 //
5689 // if(pEntry->fontSet != None && pEntry->fontSet != fontsetDefault)
5690 // {
5691 // XFreeFontSet(pEntry->pDisplay, pEntry->fontSet);
5692 // }
5693 //
5694 // pEntry->pFontStruct = NULL;
5695 //
5696 // if(fontSet != None)
5697 // {
5698 // pEntry->fontSet = fontSet;
5699 // pEntry->pFontStruct = WBFontFromFontSet(pEntry->pDisplay, fontSet);
5700 // }
5701 // }
5702 //}
5703 
5704 void WBSetWindowFont(Window wID, WB_FONTC pFont)
5705 {
5706  _WINDOW_ENTRY_ *pEntry = WBGetWindowEntry(wID);
5707 
5708  if(pEntry)
5709  {
5710  if(pEntry->pFont && pEntry->pFont != pDefaultFont) // must delete it
5711  {
5712  WBFreeFont(pEntry->pDisplay, pEntry->pFont);
5713  }
5714 
5715 // if(pFont) TODO: do I use NULL or make a copy of the default instead?
5716 // {
5717  pEntry->pFont = WBCopyFont(pEntry->pDisplay, pFont); // for now do it THIS way
5718 // }
5719 // else
5720 // {
5721 // pEntry->pFont = NULL;
5722 // }
5723  }
5724 }
5725 
5726 void WBCreateWindowDefaultGC(Window wID, unsigned long clrFG, unsigned long clrBG)
5727 {
5728  XGCValues gcv; /* Struct for creating GC */
5729 
5730  _WINDOW_ENTRY_ *pEntry = WBGetWindowEntry(wID);
5731 
5732  if(!pEntry)
5733  return;
5734 
5735  if(pEntry->hGC)
5736  {
5737  WBFreeGC(pEntry->hGC);
5738  // I shall re-assign this below, no need to NULL it
5739  }
5740 
5741  // the GC's font will be used with regular 'XDrawText' and 'XDrawString' calls
5742  // The associated Font Set must be queried separately for calls to XmbXXX or Xutf8XXX functions
5743 
5744  memset(&gcv, 0, sizeof(gcv));
5745 
5746  gcv.foreground = clrFG;
5747  gcv.background = clrBG;
5748 
5749 // This is what I did before, and it's left commented for future reference
5750 // if(!pEntry->pFontStruct)
5751 // {
5752 // gcv.font = pDefaultFont->fid;
5753 // }
5754 // else
5755 // {
5756 // gcv.font = pEntry->pFontStruct->fid;
5757 // }
5758 
5759 #ifndef NO_DEBUG
5760 #warning does the default GC _REALLY_ need to have a default X11 (legacy) font ID assigned in the XGCValues struct???
5761 #endif // NO_DEBUG
5762  pEntry->hGC = WBCreateGC(pEntry->pDisplay, wID, (/*GCFont |*/ GCForeground | GCBackground), &gcv);
5763 
5764 // TEMPORARY - for debugging a specific problem
5765  if(pEntry->hGC && ((CARD32)pEntry->hGC->values.clip_mask & 0xe0000000))
5766  {
5767  WB_ERROR_PRINT("ERROR: %s - hGC clip_mask is NOT VALID\n", __FUNCTION__);
5768  }
5769 
5770  pEntry->clrFG = clrFG; // cache this info in 'pEntry' as well
5771  pEntry->clrBG = clrBG;
5772 }
5773 
5774 void WBSetWindowDefaultGC(Window wID, WBGC hGC)
5775 {
5776  _WINDOW_ENTRY_ *pEntry = WBGetWindowEntry(wID);
5777 
5778  if(pEntry)
5779  {
5780  if(pEntry->hGC)
5781  {
5782  WBFreeGC(pEntry->hGC);
5783  }
5784 
5785  pEntry->hGC = hGC;
5786 
5787 // TEMPORARY - for debugging a specific problem
5788  if(hGC && ((CARD32)hGC->values.clip_mask & 0xe0000000))
5789  {
5790  WB_ERROR_PRINT("ERROR: %s - hGC clip_mask is NOT VALID\n", __FUNCTION__);
5791  }
5792  }
5793 }
5794 
5795 void WBSetWindowData(Window wID, int iIndex, void *pData)
5796 {
5797  _WINDOW_ENTRY_ *pEntry = WBGetWindowEntry(wID);
5798 
5799  if(pEntry && iIndex >= 0 && iIndex <= WINDOW_DATA_SIZE)
5800  {
5801  pEntry->aWindowData[iIndex] = pData;
5802  }
5803 }
5804 
5806 {
5807  _WINDOW_ENTRY_ *pEntry = WBGetWindowEntry(wID);
5808 
5809  if(pEntry)
5810  {
5811  return(pEntry->hGC);
5812  }
5813 
5814  return(NULL);
5815 }
5816 
5818 {
5819  _WINDOW_ENTRY_ *pEntry = WBGetWindowEntry(wID);
5820  WBGC gcRval = 0;
5821 
5822  if(!pEntry)
5823  {
5824  return NULL;
5825  }
5826 
5827 // WB_WARN_PRINT("TEMPORARY: %s - calling WBCopyGC()\n", __FUNCTION__);
5828  gcRval = WBCopyGC(pEntry->hGC);
5829 
5830  return(gcRval);
5831 }
5832 
5833 unsigned long WBGetWindowFGColor(Window wID)
5834 {
5835  _WINDOW_ENTRY_ *pEntry = WBGetWindowEntry(wID);
5836 
5837  if(pEntry)
5838  {
5839  return(pEntry->clrFG);
5840  }
5841 
5842  return(0);
5843 }
5844 
5845 unsigned long WBGetWindowBGColor(Window wID)
5846 {
5847  _WINDOW_ENTRY_ *pEntry = WBGetWindowEntry(wID);
5848 
5849  if(pEntry)
5850  {
5851  return(pEntry->clrBG);
5852  }
5853 
5854  return(0);
5855 }
5856 
5857 unsigned long WBGetGCFGColor(WBGC gc)
5858 {
5859 //XGCValues val;
5860 //Status ret;
5861 //
5862 //
5863 // BEGIN_XCALL_DEBUG_WRAPPER
5864 // ret = XGetGCValues(pDisplay, gc, GCForeground, &val);
5865 // END_XCALL_DEBUG_WRAPPER
5866 //
5867 // if(!ret)
5868 // {
5869 // // use pre-defined color for BLACK
5870 //
5871 // return BlackPixel(pDisplay, DefaultScreen(pDisplay));
5872 // }
5873 //
5874 // return val.foreground;
5875 
5876  if(gc != NULL)
5877  {
5878  return gc->values.foreground;
5879  }
5880  else
5881  {
5882  return BlackPixel(WBGetDefaultDisplay(), DefaultScreen(WBGetDefaultDisplay()));
5883  }
5884 }
5885 
5886 unsigned long WBGetGCBGColor(WBGC gc)
5887 {
5888 //XGCValues val;
5889 //Status ret;
5890 //
5891 //
5892 // BEGIN_XCALL_DEBUG_WRAPPER
5893 // ret = XGetGCValues(pDisplay, gc, GCBackground, &val);
5894 // END_XCALL_DEBUG_WRAPPER
5895 //
5896 // if(!ret)
5897 // {
5898 // // use pre-defined color for WHITE
5899 //
5900 // return WhitePixel(pDisplay, DefaultScreen(pDisplay));
5901 // }
5902 //
5903 // return val.background;
5904 
5905 
5906  if(gc != NULL)
5907  {
5908  return gc->values.background;
5909  }
5910  else
5911  {
5912  return WhitePixel(WBGetDefaultDisplay(), DefaultScreen(WBGetDefaultDisplay()));
5913  }
5914 }
5915 
5916 
5917 void WBDefaultStandardColormap(Display *pDisplay, XStandardColormap *pMap)
5918 {
5919 XStandardColormap *pMaps = NULL;
5920 XStandardColormap cmap;
5921 Colormap cmDefault;
5922 unsigned long lTemp; //, lWhite = WhitePixel(pDisplay, DefaultScreen(pDisplay));
5923 XColor clrRed, clrGreen, clrBlue;
5924 int i1, nMaps = 0;
5925 
5926 
5927  if(!pMap)
5928  {
5929  return;
5930  }
5931 
5932  if(!pDisplay)
5933  {
5934  pDisplay = WBGetDefaultDisplay();
5935  }
5936 
5937  if(pDisplay == WBGetDefaultDisplay() &&
5938  bStandardColormap)
5939  {
5940  // use the cached structure. faster.
5941 
5942  memcpy(pMap, &cmapDefault, sizeof(cmapDefault));
5943 
5944  return;
5945  }
5946 
5947  bzero(&cmap, sizeof(cmap));
5948 
5949  cmDefault = DefaultColormap(pDisplay, DefaultScreen(pDisplay));
5950 
5951  // TRY to use the APIs to get this information. I mean, REALLY TRY.
5952  if(!XGetRGBColormaps(pDisplay, DefaultRootWindow(pDisplay), &pMaps, &nMaps, XA_RGB_DEFAULT_MAP) ||
5953  nMaps == 0)
5954  {
5955  if(pMaps)
5956  {
5957  XFree(pMaps);
5958  }
5959 
5960  pMaps = NULL;
5961 
5962  if(!XGetRGBColormaps(pDisplay, DefaultRootWindow(pDisplay), &pMaps, &nMaps, XA_RGB_BEST_MAP) ||
5963  nMaps == 0)
5964  {
5965  if(pMaps)
5966  {
5967  XFree(pMaps);
5968  }
5969 
5970  pMaps = NULL;
5971  }
5972  }
5973 
5974  if(pMaps) // meaning that the above 'thingy' actually worked (I never really see it work, though)
5975  {
5976  for(i1=0; i1 < nMaps; i1++)
5977  {
5978  if(pMaps[i1].colormap == cmDefault)
5979  {
5980  memcpy(&cmap, &(pMaps[i1]), sizeof(XStandardColormap));
5981  break;
5982  }
5983  }
5984 
5985  XFree(pMaps);
5986 
5987  cmap.killid = None; // make sure
5988  cmap.visualid = None;
5989 
5990  if(i1 < nMaps) // I broke out of the loop? I found one?
5991  {
5992  if(pDisplay == WBGetDefaultDisplay())
5993  {
5994  memcpy(&cmapDefault, &cmap, sizeof(cmap));
5995  }
5996 
5997  memcpy(pMap, &cmap, sizeof(cmap));
5998  return;
5999  }
6000  }
6001 
6002 // WB_ERROR_PRINT("TEMPORARY: %s no matching XStandardColormap found - creating\n", __FUNCTION__);
6003 
6004  // --------------------------------------------------------
6005  // DERIVE A COLOR MAP FROM RGB COLORS AND KNOWN INFORMATION
6006  // --------------------------------------------------------
6007 
6008  bzero(&clrRed, sizeof(clrRed));
6009  bzero(&clrGreen, sizeof(clrGreen));
6010  bzero(&clrBlue, sizeof(clrBlue));
6011 
6012  clrRed.red = 65535;
6013  clrGreen.green = 65535;
6014  clrBlue.blue = 65535;
6015  XAllocColor(pDisplay, cmDefault, &clrRed);
6016  XAllocColor(pDisplay, cmDefault, &clrGreen);
6017  XAllocColor(pDisplay, cmDefault, &clrBlue);
6018 
6019 // WB_ERROR_PRINT("TEMPORARY: %s red: %08lxH green: %08lxH blue: %08lxH\n", __FUNCTION__,
6020 // clrRed.pixel, clrGreen.pixel, clrBlue.pixel);
6021 
6022  // black is my 'base' pixel.
6023  cmap.base_pixel = BlackPixel(pDisplay, DefaultScreen(pDisplay));
6024 
6025  // next, 'nuke out' how the pixel multipliers work, using the Red, Green, and Blue 'alloc'd colors
6026  if(clrRed.pixel >= clrGreen.pixel && clrRed.pixel >= clrBlue.pixel)
6027  {
6028  if(clrGreen.pixel >= clrBlue.pixel)
6029  {
6030  cmap.blue_max = clrBlue.pixel - cmap.base_pixel;
6031  cmap.blue_mult = 1;
6032 
6033  cmap.green_mult = 1;
6034  while(cmap.green_mult < cmap.blue_max)
6035  {
6036  cmap.green_mult <<= 1;
6037  }
6038 
6039  lTemp = (clrGreen.pixel - cmap.base_pixel);
6040  cmap.green_max = lTemp / cmap.green_mult;
6041 
6042  cmap.red_mult = cmap.green_mult;
6043 
6044  while(cmap.red_mult < lTemp)
6045  {
6046  cmap.red_mult <<= 1;
6047  }
6048 
6049  cmap.red_max = (clrRed.pixel - cmap.base_pixel)
6050  / cmap.red_mult;
6051  }
6052  else
6053  {
6054  cmap.green_max = clrGreen.pixel - cmap.base_pixel;
6055  cmap.green_mult = 1;
6056 
6057  cmap.blue_mult = 1;
6058 
6059  while(cmap.blue_mult < cmap.green_max)
6060  {
6061  cmap.blue_mult <<= 1;
6062  }
6063 
6064  lTemp = (clrBlue.pixel - cmap.base_pixel);
6065  cmap.blue_max = lTemp / cmap.blue_mult;
6066 
6067  cmap.red_mult = cmap.blue_mult;
6068 
6069  while(cmap.red_mult < lTemp)
6070  {
6071  cmap.red_mult <<= 1;
6072  }
6073 
6074  cmap.red_max = (clrRed.pixel - cmap.base_pixel)
6075  / cmap.red_mult;
6076  }
6077  }
6078  else if(clrGreen.pixel >= clrRed.pixel && clrGreen.pixel >= clrBlue.pixel)
6079  {
6080  if(clrRed.pixel >= clrBlue.pixel)
6081  {
6082  cmap.blue_max = clrBlue.pixel - cmap.base_pixel;
6083  cmap.blue_mult = 1;
6084 
6085  cmap.red_mult = 1;
6086  while(cmap.red_mult < cmap.blue_max)
6087  {
6088  cmap.red_mult <<= 1;
6089  }
6090 
6091  lTemp = (clrRed.pixel - cmap.base_pixel);
6092  cmap.red_max = lTemp / cmap.red_mult;
6093 
6094  cmap.green_mult = cmap.red_mult;
6095 
6096  while(cmap.green_mult < lTemp)
6097  {
6098  cmap.green_mult <<= 1;
6099  }
6100 
6101  cmap.green_max = (clrGreen.pixel - cmap.base_pixel)
6102  / cmap.green_mult;
6103  }
6104  else
6105  {
6106  cmap.red_max = clrRed.pixel - cmap.base_pixel;
6107  cmap.red_mult = 1;
6108 
6109  cmap.blue_mult = 1;
6110 
6111  while(cmap.blue_mult < cmap.green_max)
6112  {
6113  cmap.blue_mult <<= 1;
6114  }
6115 
6116  lTemp = (clrBlue.pixel - cmap.base_pixel);
6117  cmap.blue_max = lTemp / cmap.blue_mult;
6118 
6119  cmap.green_mult = cmap.blue_mult;
6120 
6121  while(cmap.green_mult < lTemp)
6122  {
6123  cmap.green_mult <<= 1;
6124  }
6125 
6126  cmap.green_max = (clrGreen.pixel - cmap.base_pixel)
6127  / cmap.green_mult;
6128  }
6129  }
6130  else
6131  {
6132  if(clrRed.pixel >= clrGreen.pixel)
6133  {
6134  cmap.green_max = clrGreen.pixel - cmap.base_pixel;
6135  cmap.green_mult = 1;
6136 
6137  cmap.red_mult = 1;
6138  while(cmap.red_mult < cmap.green_max)
6139  {
6140  cmap.red_mult <<= 1;
6141  }
6142 
6143  lTemp = (clrRed.pixel - cmap.base_pixel);
6144  cmap.red_max = lTemp / cmap.red_mult;
6145 
6146  cmap.blue_mult = cmap.red_mult;
6147 
6148  while(cmap.blue_mult < lTemp)
6149  {
6150  cmap.blue_mult <<= 1;
6151  }
6152 
6153  cmap.blue_max = (clrBlue.pixel - cmap.base_pixel)
6154  / cmap.blue_mult;
6155  }
6156  else
6157  {
6158  cmap.red_max = clrRed.pixel - cmap.base_pixel;
6159  cmap.red_mult = 1;
6160 
6161  cmap.green_mult = 1;
6162  while(cmap.green_mult < cmap.red_max)
6163  {
6164  cmap.green_mult <<= 1;
6165  }
6166 
6167  lTemp = (clrGreen.pixel - cmap.base_pixel);
6168  cmap.green_max = lTemp / cmap.green_mult;
6169 
6170 
6171  cmap.blue_mult = cmap.green_mult;
6172 
6173  while(cmap.blue_mult < lTemp)
6174  {
6175  cmap.blue_mult <<= 1;
6176  }
6177 
6178  cmap.blue_max = (clrBlue.pixel - cmap.base_pixel)
6179  / cmap.blue_mult;
6180  }
6181  }
6182 
6183  XFreeColors(pDisplay, cmDefault, &clrRed.pixel, 1, 0);
6184  XFreeColors(pDisplay, cmDefault, &clrGreen.pixel, 1, 0);
6185  XFreeColors(pDisplay, cmDefault, &clrBlue.pixel, 1, 0);
6186 
6187 // WB_ERROR_PRINT("TEMPORARY: red: %u,%u grn: %u,%u blu: %u,%u\n",
6188 // (unsigned int)cmap.red_max, (unsigned int)cmap.red_mult,
6189 // (unsigned int)cmap.green_max, (unsigned int)cmap.green_mult,
6190 // (unsigned int)cmap.blue_max, (unsigned int)cmap.blue_mult);
6191 
6192  if(pDisplay == WBGetDefaultDisplay())
6193  {
6194  memcpy(&cmapDefault, &cmap, sizeof(cmap));
6195  }
6196 
6197  memcpy(pMap, &cmap, sizeof(cmap));
6198 }
6199 
6200 
6202 {
6203  _WINDOW_ENTRY_ *pEntry = WBGetWindowEntry(wID);
6204 
6205  if(pEntry && pEntry->pFont)
6206  {
6207  return pEntry->pFont;
6208  }
6209 
6210  return NULL;
6211 }
6212 
6214 {
6215  _WINDOW_ENTRY_ *pEntry = WBGetWindowEntry(wID);
6216 
6217  if(pEntry && pEntry->pFont)
6218  {
6219  return WBCopyFont(pEntry->pDisplay, pEntry->pFont);
6220  }
6221 
6222  if(!pDefaultFont)
6223  {
6224  WB_ERROR_PRINT("%s - default font is NULL\n", __FUNCTION__);
6225 
6226  return NULL;
6227  }
6228 
6229  return WBCopyFont(pEntry->pDisplay, pDefaultFont);
6230 }
6231 
6232 //XFontSet WBGetWindowFontSet(Window wID)
6233 //{
6234 // _WINDOW_ENTRY_ *pEntry = WBGetWindowEntry(wID);
6235 //
6236 // if(pEntry && pEntry->fontSet != None)
6237 // {
6238 // return(pEntry->fontSet);
6239 // }
6240 //
6241 // if(fontsetDefault == None)
6242 // {
6243 // WB_ERROR_PRINT("%s - default font is NULL\n", __FUNCTION__);
6244 // }
6245 //
6246 // return(fontsetDefault); // use global font set
6247 //}
6248 
6249 void WBSetWindowClassName(Window wID, const char *szClassName)
6250 {
6251  _WINDOW_ENTRY_ *pEntry = WBGetWindowEntry(wID);
6252 
6253  if(pEntry)
6254  {
6255  pEntry->szClassName = szClassName;
6256  }
6257 }
6258 
6259 const char *WBGetWindowClassName(Window wID)
6260 {
6261  _WINDOW_ENTRY_ *pEntry = WBGetWindowEntry(wID);
6262 
6263  if(pEntry)
6264  {
6265  return(pEntry->szClassName);
6266  }
6267 
6268  return(NULL);
6269 }
6270 
6271 void *WBGetWindowData(Window wID, int iIndex)
6272 {
6273  _WINDOW_ENTRY_ *pEntry = WBGetWindowEntry(wID);
6274 
6275  if(pEntry && iIndex >= 0 && iIndex <= WINDOW_DATA_SIZE)
6276  {
6277  return(pEntry->aWindowData[iIndex]);
6278  }
6279 
6280  return(NULL);
6281 }
6282 
6283 // use this to set the cursor without assigning the 'current cursor'
6284 static void __InternalSetWindowCursor(Window wID, int idCursor)
6285 {
6286  _WINDOW_ENTRY_ *pEntry = WBGetWindowEntry(wID);
6287  Display *pDisplay = pEntry ? pEntry->pDisplay : pDefaultDisplay;
6288  Cursor curNew = idCursor != -1 ? XCreateFontCursor(pDisplay, idCursor) : None;
6289  XColor clrF, clrB;
6290 
6291  if(curNew == None && idCursor != -1)
6292  {
6293  WB_ERROR_PRINT("ERROR - %s - Cursor ID %d (%xH) returns 'None'\n", __FUNCTION__, idCursor, idCursor);
6294  }
6295 
6296  // this will work for *ANY* window (assuming default display for unmapped windows)
6297  // unfortunately it may cause a memory leak for windows that aren't mapped
6298  if(curNew != None)
6299  XDefineCursor(pDisplay, wID, curNew);
6300  else
6301  XUndefineCursor(pDisplay, wID);
6302 
6303  // and this will manage resources for internally 'known' windows
6304 
6305  if(pEntry)
6306  {
6307  if(pEntry->curRecent != None)
6308  XFreeCursor(pDisplay, pEntry->curRecent);
6309 
6310  pEntry->curRecent = curNew;
6311 
6312 #if 1
6313  if(curNew != None)
6314  {
6315  // TODO: use the default FG and BG colors of the window and
6316  // change the cursor colors accordingly. This should force
6317  // the cursor to change immediately
6318 
6319  // for now it's always white background, black foreground
6320 
6321  bzero(&clrF, sizeof(clrF));
6322  bzero(&clrB, sizeof(clrB));
6323  clrB.pixel = /*pEntry->clrFG*/ WhitePixel(pDisplay, DefaultScreen(pDisplay));
6324  clrF.pixel = /*pEntry->clrBG*/ BlackPixel(pDisplay, DefaultScreen(pDisplay));
6325  XQueryColor(pDisplay, DefaultColormap(pDisplay, DefaultScreen(pDisplay)), &clrF);
6326  XQueryColor(pDisplay, DefaultColormap(pDisplay, DefaultScreen(pDisplay)), &clrB);
6327 
6328  XRecolorCursor(pDisplay, curNew, &clrB, &clrF); // this will 'flash' it, kinda
6329  XRecolorCursor(pDisplay, curNew, &clrF, &clrB); // show NOW
6330  }
6331 #endif // 0
6332  }
6333 }
6334 
6335 void WBSetWindowDefaultCursor(Window wID, int idStandardCursor)
6336 {
6337  _WINDOW_ENTRY_ *pEntry = WBGetWindowEntry(wID);
6338 
6339  if(pEntry)
6340  {
6341  pEntry->idDefaultCursor = idStandardCursor;
6342 
6343  if(!pEntry->iWaitCursorCount) // if we're not currently displaying a wait cursor
6344  WBSetWindowCursor(wID, idStandardCursor); // change the cursor immediately to this one
6345  }
6346 }
6347 
6349 {
6350  _WINDOW_ENTRY_ *pEntry = WBGetWindowEntry(wID);
6351 
6352  if(pEntry)
6353  return pEntry->idDefaultCursor;
6354 
6355  return WB_DEFAULT_CURSOR; // the standard default cursor
6356 }
6357 
6358 void WBBeginWaitCursor(Window wID)
6359 {
6360  _WINDOW_ENTRY_ *pEntry = WBGetWindowEntry(wID);
6361  Window wIDRoot = 0, wIDParent = 0;
6362  Window *pwIDChildren = NULL;
6363  unsigned int u1, nChildren = 0;
6364 
6365  if(pEntry)
6366  {
6367  if(!(pEntry->iWaitCursorCount++))
6368  {
6369  __InternalSetWindowCursor(wID, WB_WAIT_CURSOR); // change the cursor immediately to the 'watch' cursor
6370 
6371  // also do the same for all of the child windows
6372 
6373  if(XQueryTree(pEntry->pDisplay, wID, &wIDRoot, &wIDParent, &pwIDChildren, &nChildren))
6374  {
6375  if(pwIDChildren)
6376  {
6377  for(u1=nChildren; u1; u1--) // go backwards
6378  {
6379  Window wKid = pwIDChildren[u1 - 1];
6380  _WINDOW_ENTRY_ *pE = WBGetWindowEntry(wKid);
6381 
6382  if(pE)
6383  {
6384  __InternalSetWindowCursor(wKid, WB_WAIT_CURSOR); // all must use the wait cursor now
6385  }
6386  }
6387 
6388  XFree(pwIDChildren);
6389  }
6390  }
6391  }
6392  }
6393  else
6394  {
6395  __InternalSetWindowCursor(wID, WB_WAIT_CURSOR); // change the cursor immediately to the 'watch' cursor
6396  }
6397 }
6398 
6399 void WBEndWaitCursor(Window wID)
6400 {
6401  _WINDOW_ENTRY_ *pEntry = WBGetWindowEntry(wID);
6402  Window wIDRoot = 0, wIDParent = 0;
6403  Window *pwIDChildren = NULL;
6404  unsigned int u1, nChildren = 0;
6405 
6406 
6407  if(pEntry)
6408  {
6409  if(pEntry->iWaitCursorCount <= 1)
6410  {
6411  pEntry->iWaitCursorCount = 0;
6412 // WBRestoreDefaultCursor(wID);
6413  __InternalSetWindowCursor(wID, pEntry->idCursor);
6414 
6415  // walk child windows, restore THEIR cursors also
6416 
6417  if(XQueryTree(pEntry->pDisplay, wID, &wIDRoot, &wIDParent, &pwIDChildren, &nChildren))
6418  {
6419  if(pwIDChildren)
6420  {
6421  for(u1=nChildren; u1; u1--) // go backwards
6422  {
6423  Window wKid = pwIDChildren[u1 - 1];
6424  _WINDOW_ENTRY_ *pE = WBGetWindowEntry(wKid);
6425 
6426  if(pE)
6427  {
6428  __InternalSetWindowCursor(wKid, pE->idCursor); // restore to what it was
6429  }
6430  }
6431 
6432  XFree(pwIDChildren);
6433  }
6434  }
6435  }
6436  else
6437  {
6438  pEntry->iWaitCursorCount--;
6439  }
6440  }
6441  else
6442  {
6443  __InternalSetWindowCursor(wID, -1); // change the cursor immediately to the 'watch' cursor
6444  }
6445 }
6446 
6447 void WBSetWindowCursor(Window wID, int idCursor)
6448 {
6449  _WINDOW_ENTRY_ *pEntry = WBGetWindowEntry(wID);
6450 
6451  if(pEntry)
6452  {
6453  pEntry->idCursor = idCursor;
6454  }
6455 
6456  __InternalSetWindowCursor(wID, idCursor);
6457 }
6458 
6459 void WBRestoreDefaultCursor(Window wID)
6460 {
6461  _WINDOW_ENTRY_ *pEntry = WBGetWindowEntry(wID);
6462 
6463  if(pEntry)
6464  {
6465  pEntry->idCursor = pEntry->idDefaultCursor;
6466  __InternalSetWindowCursor(wID, pEntry->idDefaultCursor); // restore the cursor immediately
6467  }
6468 }
6469 
6470 
6471 // read-only window properties
6472 
6473 void WBGetWindowGeom0(Window wID, WB_GEOM *pGeom) // absolute window geometry (from latest notification)
6474 {
6475  _WINDOW_ENTRY_ *pEntry = WBGetWindowEntry(wID);
6476 
6477  if(pGeom)
6478  {
6479  if(!pEntry || (!pEntry->geomAbsolute.width && !pEntry->geomAbsolute.height))
6480  {
6481  bzero(pGeom, sizeof(*pGeom));
6482 
6483  Window wIDTemp = wID;
6484 
6485  while(wIDTemp)
6486  {
6487  WB_GEOM gm;
6488  WBGetWindowGeom(wIDTemp, &gm);
6489 
6490  pGeom->x += gm.x;
6491  pGeom->y += gm.y;
6492 
6493  if(wIDTemp == wID)
6494  {
6495  pGeom->width = gm.width;
6496  pGeom->height = gm.height;
6497  pGeom->border = gm.border;
6498 
6499  WB_DEBUG_PRINT(DebugLevel_Excessive | DebugSubSystem_Window,
6500  "%s - geometry for window %d (%08xH) = %d,%d,%d,%d,%d\n",
6501  __FUNCTION__, (int)wID, (int)wID,
6502  gm.x, gm.y, gm.width, gm.height, gm.border);
6503  }
6504  else
6505  {
6506  WB_DEBUG_PRINT(DebugLevel_Excessive | DebugSubSystem_Window,
6507  "%s - geometry for parent window %d (%08xH) = %d,%d,%d,%d,%d\n",
6508  __FUNCTION__, (int)wIDTemp, (int)wIDTemp,
6509  gm.x, gm.y, gm.width, gm.height, gm.border);
6510  }
6511 
6512  wIDTemp = WBGetParentWindow(wIDTemp);
6513  }
6514  }
6515  else
6516  {
6517  memcpy(pGeom, &(pEntry->geomAbsolute), sizeof(*pGeom));
6518  }
6519  }
6520 }
6521 
6522 void WBGetWindowGeom(Window wID, WB_GEOM *pGeom)
6523 {
6524  _WINDOW_ENTRY_ *pEntry = WBGetWindowEntry(wID);
6525  Display *pDisp = pEntry ? pEntry->pDisplay : pDefaultDisplay;
6526  Window winRoot = None;
6527  unsigned int uiDepth = 0;
6528 
6529  if(!pGeom)
6530  {
6531  return;
6532  }
6533 
6534 // bzero(&xwa, sizeof(xwa));
6535  bzero(pGeom, sizeof(*pGeom));
6536 
6537  if(WB_LIKELY(pEntry) &&
6538  WB_UNLIKELY(WB_IS_WINDOW_DESTROYED(*pEntry) || WB_IS_WINDOW_BEING_DESTROYED(*pEntry)))
6539  {
6540  WB_WARN_PRINT("%s:%d - %d (%08xH) NOT mapped\n",
6541  __FUNCTION__, __LINE__, (int)wID, (int)wID);
6542  return;
6543  }
6544 
6545  if(!pEntry)
6546  {
6547  WB_DEBUG_PRINT(DebugLevel_Excessive | DebugSubSystem_Window,
6548  "%s:%d - pEntry is NULL, %d (%08xH) may NOT be mapped\n",
6549  __FUNCTION__, __LINE__, (int)wID, (int)wID);
6550  }
6551  else if(!WB_IS_WINDOW_MAPPED(*pEntry) &&
6552  pEntry->geomAbsolute.width > 0 && pEntry->geomAbsolute.height > 0)
6553  {
6554  // if not mapped, return the best guess geometry
6555 
6556  pGeom->x = 0; // pEntry->geomAbsolute.x;
6557  pGeom->y = 0; // pEntry->geomAbsolute.y;
6558  pGeom->width = pEntry->geomAbsolute.width;
6559  pGeom->height = pEntry->geomAbsolute.height;
6560  pGeom->border = pEntry->geomAbsolute.border;
6561 
6562  WB_ERROR_PRINT("TEMPORARY: %s - unmapped window geometry %d, %d, %d, %d\n",
6563  __FUNCTION__, pGeom->x, pGeom->y, pGeom->width, pGeom->height);
6564 
6565  return;
6566  }
6567 // else if(!WB_IS_WINDOW_MAPPED(*pEntry))
6568 // {
6569 // WB_ERROR_PRINT("TEMPORARY: %s - unmapped window, no 'absolute' GEOM available (pre 'X' calls)\n", __FUNCTION__);
6570 // }
6571 
6573  XSync(pDisp, 0);
6574  XGetGeometry(pDisp, wID, &winRoot,
6575  &(pGeom->x), &(pGeom->y),
6576  &(pGeom->width), &(pGeom->height),
6577  &(pGeom->border), &uiDepth);
6579 
6580 // if(pEntry && !WB_IS_WINDOW_MAPPED(*pEntry))
6581 // {
6582 // WB_ERROR_PRINT("TEMPORARY: %s - unmapped window, no 'absolute' GEOM available (post 'X' calls)\n", __FUNCTION__);
6583 // }
6584 
6585 //fprintf(stderr, "XGetGeometry: window %08xH %d, %d, %d, %d\n",
6586 // wID, pGeom->x, pGeom->y, pGeom->width, pGeom->height);
6587 
6588 #if 0 /* 1 */
6589  {
6590  XWindowAttributes xwa; /* getting size of window, etc. */
6591  memset(&xwa, 0, sizeof(xwa));
6592  if(XGetWindowAttributes(pDisp, winRoot, &xwa))
6593  {
6594  WB_DEBUG_PRINT(DebugLevel_Excessive | DebugSubSystem_Window,
6595  "%s - window %08xH %d, %d, %d, %d\n",
6596  __FUNCTION__, winRoot, xwa.x, xwa.y, xwa.width, xwa.height);
6597 // pGeom->x = xwa.x;
6598 // pGeom->y = xwa.y;
6599 // pGeom->width = xwa.width;
6600 // pGeom->height = xwa.height;
6601 // pGeom->border = xwa.border_width;
6602  }
6603  }
6604 #endif // 0,1
6605 }
6606 
6607 void WBGetWindowGeom2(Window wID, WB_GEOM *pGeom)
6608 {
6609  _WINDOW_ENTRY_ *pEntry = WBGetWindowEntry(wID);
6610  Display *pDisp = pEntry ? pEntry->pDisplay : pDefaultDisplay;
6611  Window winRoot = 0, wParent;
6612  unsigned int uiDepth = 0;
6613  WB_GEOM geom;
6614 
6615  bzero(pGeom, sizeof(*pGeom));
6616 
6617  if(WB_LIKELY(pEntry) &&
6618  WB_UNLIKELY(WB_IS_WINDOW_DESTROYED(*pEntry) || WB_IS_WINDOW_BEING_DESTROYED(*pEntry)))
6619  {
6620  WB_WARN_PRINT("%s:%d - %d (%08xH) NOT mapped\n",
6621  __FUNCTION__, __LINE__, (int)wID, (int)wID);
6622  return;
6623  }
6624  else if(!WB_IS_WINDOW_MAPPED(*pEntry) &&
6625  pEntry->geomAbsolute.width > 0 && pEntry->geomAbsolute.height > 0)
6626  {
6627  // if not mapped, return the best guess geometry
6628 
6629  pGeom->x = pEntry->geomAbsolute.x;
6630  pGeom->y = pEntry->geomAbsolute.y;
6631  pGeom->width = pEntry->geomAbsolute.width;
6632  pGeom->height = pEntry->geomAbsolute.height;
6633  pGeom->border = pEntry->geomAbsolute.border;
6634 
6635  WB_ERROR_PRINT("TEMPORARY: %s - unmapped window geometry %d, %d, %d, %d\n",
6636  __FUNCTION__, pGeom->x, pGeom->y, pGeom->width, pGeom->height);
6637 
6638  return;
6639  }
6640 // else if(!WB_IS_WINDOW_MAPPED(*pEntry))
6641 // {
6642 // WB_ERROR_PRINT("TEMPORARY: %s - unmapped window, no 'absolute' GEOM available\n", __FUNCTION__);
6643 // }
6644 
6645 
6647  XGetGeometry(pDisp, wID, &winRoot,
6648  &(pGeom->x), &(pGeom->y),
6649  &(pGeom->width), &(pGeom->height),
6650  &(pGeom->border), &uiDepth);
6652 
6653  WB_DEBUG_PRINT(DebugLevel_Excessive | DebugSubSystem_Window,
6654  "%s - geometry for window %d (%08xH) = %d,%d,%d,%d,%d\n",
6655  __FUNCTION__, (int)wID, (int)wID,
6656  pGeom->x, pGeom->y, pGeom->width, pGeom->height, pGeom->border);
6657 
6658  wParent = WBGetParentWindow(wID);
6659 
6660  while(wParent > 0 && winRoot != wParent)
6661  {
6662  _WINDOW_ENTRY_ *pParentEntry = WBGetWindowEntry(wParent);
6663 
6664  if(WB_LIKELY(pParentEntry) &&
6665  WB_UNLIKELY(WB_IS_WINDOW_DESTROYED(*pParentEntry) || WB_IS_WINDOW_BEING_DESTROYED(*pParentEntry)))
6666  {
6667  WB_WARN_PRINT("%s:%d - %d (%08xH) NOT mapped\n",
6668  __FUNCTION__, __LINE__, (int)wID, (int)wID);
6669  continue;
6670  }
6671 
6672  bzero(&geom, sizeof(geom));
6673 
6675  XGetGeometry(pDisp, wParent, &winRoot, // assumes parent windows have same display, which is reasonable
6676  &geom.x, &geom.y,
6677  &geom.width, &geom.height,
6678  &geom.border, &uiDepth);
6680 
6681  WB_DEBUG_PRINT(DebugLevel_Excessive | DebugSubSystem_Window,
6682  "%s - geometry for parent window %d (%08xH) = %d,%d,%d,%d,%d\n",
6683  __FUNCTION__, (int)wParent, (int)wParent,
6684  geom.x, geom.y, geom.width, geom.height, geom.border);
6685 
6686  pGeom->x += geom.x;
6687  pGeom->y += geom.y;
6688 
6689  wParent = WBGetParentWindow(wParent);
6690  }
6691 }
6692 
6693 void WBGetWindowRect(Window wID, WB_RECT *pRect)
6694 {
6695  WB_GEOM geom;
6696 
6697  if(!pRect)
6698  return;
6699 
6700  WBGetWindowGeom(wID, &geom);
6701 
6702  pRect->left = geom.x; // + geom.border;
6703  pRect->top = geom.y; // + geom.border; // TODO: verify
6704  pRect->right = pRect->left + geom.width + 2 * geom.border;
6705  pRect->bottom = pRect->top + geom.height + 2 * geom.border; // TODO: verify
6706 }
6707 
6708 void WBGetClientRect(Window wID, WB_RECT *pRect)
6709 {
6710  WB_GEOM geom;
6711 
6712  if(!pRect)
6713  return;
6714 
6715  WBGetWindowGeom(wID, &geom);
6716 
6717  // NOTE: GEOM 'border' isn't correct unless I'm trying to calculate
6718  // the size of the border surrounding a window. the client rect
6719  // will always have 0,0 as its upper left corner
6720 
6721  pRect->left = 0;// geom.border; // 0
6722  pRect->top = 0; // geom.border; // 0
6723  pRect->right = geom.width;
6724  pRect->bottom = geom.height;
6725 }
6726 
6727 void WBXlatCoordPoint(Window wIDSrc, int iXSrc, int iYSrc, Window wIDDest, int *piXDest, int *piYDest)
6728 {
6729 WB_GEOM gOrig, gXlat;
6730 
6731  if(wIDSrc == wIDDest)
6732  {
6733  if(piXDest)
6734  *piXDest = iXSrc;
6735  if(piYDest)
6736  *piYDest = iYSrc;
6737  return;
6738  }
6739 
6740  if(wIDSrc != 0) // not "root window"
6741  WBGetWindowGeom0(wIDSrc, &gOrig);
6742  else
6743  bzero(&gOrig, sizeof(gOrig));
6744 
6745  if(wIDDest != 0)
6746  WBGetWindowGeom0(wIDDest, &gXlat);
6747  else
6748  bzero(&gXlat, sizeof(gXlat));
6749 
6750 //WB_WARN_PRINT("TEMPORARY: xlat %d,%d via %d,%d %d,%d\n",
6751 // iXSrc, iYSrc, gOrig.x, gOrig.y, gXlat.x, gXlat.y);
6752 
6753  if(piXDest)
6754  *piXDest = iXSrc + gOrig.x // xlat relative to absolute position
6755  - gXlat.x; // new relative position from absolute position
6756 
6757  if(piYDest)
6758  *piYDest = iYSrc + gOrig.y
6759  - gXlat.y;
6760 }
6761 
6762 void WBXlatCoordGeom(Window wIDSrc, const WB_GEOM *pGeomSrc, Window wIDDest, WB_GEOM *pGeomDest)
6763 {
6764 WB_GEOM gOrig, gXlat;
6765 
6766  if(!pGeomSrc || !pGeomDest)
6767  return;
6768 
6769  if(wIDSrc == wIDDest)
6770  {
6771  memcpy(pGeomDest, pGeomSrc, sizeof(*pGeomDest));
6772  return;
6773  }
6774 
6775  if(wIDSrc != 0) // not "root window"
6776  WBGetWindowGeom(wIDSrc, &gOrig);
6777  else
6778  bzero(&gOrig, sizeof(gOrig));
6779 
6780  if(wIDDest != 0)
6781  WBGetWindowGeom(wIDDest, &gXlat);
6782  else
6783  bzero(&gXlat, sizeof(gXlat));
6784 
6785  pGeomDest->x = pGeomSrc->x + gOrig.x - gXlat.x;
6786  pGeomDest->y = pGeomSrc->y + gOrig.y - gXlat.y;
6787  pGeomDest->width = pGeomSrc->width;
6788  pGeomDest->height = pGeomSrc->height;
6789  pGeomDest->border = pGeomSrc->border;
6790 }
6791 
6792 void WBXlatCoordRect(Window wIDSrc, const WB_RECT *pRectSrc, Window wIDDest, WB_RECT *pRectDest)
6793 {
6794 WB_GEOM geomSrc, geomDest;
6795 
6796  if(!pRectSrc || !pRectDest)
6797  return;
6798 
6799  if(wIDSrc == wIDDest)
6800  {
6801  memcpy(pRectDest, pRectSrc, sizeof(*pRectDest));
6802  return;
6803  }
6804 
6805  geomSrc.x = pRectSrc->left;
6806  geomSrc.y = pRectSrc->top;
6807  geomSrc.width = pRectSrc->right - geomSrc.x;
6808  geomSrc.height = pRectSrc->bottom - geomSrc.y;
6809  geomSrc.border = 0;
6810 
6811  WBXlatCoordGeom(wIDSrc, &geomSrc, wIDDest, &geomDest);
6812 
6813  pRectDest->left = geomDest.x;
6814  pRectDest->right = geomDest.y;
6815  pRectDest->right = geomDest.width + geomDest.x;
6816  pRectDest->bottom = geomDest.y + geomDest.height;
6817 }
6818 
6819 int WBPointInWindow(Window wIDRef, int iX, int iY, Window wIDQuery)
6820 {
6821 WB_RECT rctDest;
6822 
6823  if(wIDRef != wIDQuery)
6824  WBXlatCoordPoint(wIDRef, iX, iY, wIDQuery, &iX, &iY);
6825 
6826  WBGetWindowRect(wIDQuery, &rctDest);
6827 
6828  return(iX >= rctDest.left && iX < rctDest.right
6829  && iY >= rctDest.top && iY < rctDest.bottom);
6830 }
6831 
6832 // keyboard mappings
6833 
6834 int WBKeyEventProcessKey(const XKeyEvent *pEvent, char *pBuf, int *pcbLen, int *piAltCtrlShift)
6835 {
6836 KeySym ks;
6837 int cRval = 0, cbLen, cbLen0 = pcbLen ? *pcbLen : 1;
6838 int iACSMask = ShiftMask | ControlMask | Mod1Mask /* | Mod2Mask | Mod3Mask | Mod4Mask | Mod5Mask */;
6839 int bKeySym = 0;
6840 
6841  // extract as much information from this keystroke as possible.
6842 
6843  iACSMask &= ((XKeyEvent *)pEvent)->state;
6844 
6845  if(piAltCtrlShift)
6846  {
6847  *piAltCtrlShift = 0;
6848  }
6849 
6850  if(!pEvent)
6851  {
6852  return -1;
6853  }
6854 
6855  if(pBuf)
6856  {
6857  cbLen = XLookupString((XKeyEvent *)pEvent, pBuf, cbLen0, &ks, NULL);
6858 
6859  if((int)ks == XK_Delete || (int)ks == XK_KP_Delete)
6860  {
6861  cbLen = 0; // force this for the delete key - sometimes it translates
6862  }
6863 
6864  // TODO: force other keys to use keycodes? maybe backspace, escape, ???
6865 
6866  if(cbLen <= 0)
6867  {
6868  *pBuf = 0;
6869  bKeySym = 1;
6870  cbLen = 0; // just in case
6871  cRval = (int)ks; // return the keysym code
6872  }
6873  else
6874  {
6875  if(cbLen < cbLen0)
6876  {
6877  pBuf[cbLen] = 0;
6878  }
6879 
6880  cRval = *pBuf; // always (for now)
6881  }
6882  if(pcbLen)
6883  {
6884  *pcbLen = cbLen;
6885  }
6886  }
6887  else
6888  {
6889  int tbuf[8]; // note: must be int aligned, that's why
6890 
6891  cbLen = XLookupString((XKeyEvent *)pEvent, (char *)tbuf, sizeof(tbuf) - 1, &ks, NULL);
6892 
6893  if(pcbLen)
6894  {
6895  *pcbLen = cbLen; // calculates actual length this way
6896  }
6897 
6898  if(cbLen <= 0)
6899  {
6900  cRval = ks; // use the scan code
6901  bKeySym = 1;
6902  }
6903  else if(cbLen == 1)
6904  {
6905  cRval = *((unsigned char *)tbuf);
6906  }
6907  else if(cbLen == 2)
6908  {
6909  cRval = *((unsigned short *)tbuf);
6910  }
6911  else if(cbLen == 4)
6912  {
6913  cRval = *((unsigned int *)tbuf);
6914  }
6915  else
6916  {
6917  cRval = *((unsigned char *)tbuf); // for now
6918  }
6919  }
6920 
6921  if(piAltCtrlShift)
6922  {
6923  *piAltCtrlShift = (bKeySym ? WB_KEYEVENT_KEYSYM : 0)
6924  | ((iACSMask & Mod1Mask) ? WB_KEYEVENT_ALT : 0)
6925  | ((iACSMask & ControlMask) ? WB_KEYEVENT_CTRL : 0)
6926  | ((iACSMask & ShiftMask) ? WB_KEYEVENT_SHIFT : 0);
6927  }
6928 
6929  return cRval;
6930 }
6931 
6932 
6933 
6934 // parent-child relationships
6935 
6936 static Window __internal_GetParent(Display *pDisplay, Window wID, Window *pwRoot)
6937 {
6938 Window wIDRoot = 0, wIDParent = 0;
6939 Window *pwIDChildren = NULL;
6940 unsigned int nChildren = 0;
6941 
6942  // TODO: if parent is desktop, return 'None' ?
6943 
6944  if(XQueryTree(pDisplay, wID, &wIDRoot, &wIDParent, &pwIDChildren, &nChildren))
6945  {
6946  if(pwIDChildren)
6947  XFree(pwIDChildren);
6948  }
6949 
6950  if(pwRoot)
6951  {
6952  *pwRoot = wIDRoot;
6953  }
6954 
6955  return wIDParent;
6956 }
6957 
6958 Window WBGetParentWindow(Window wID)
6959 {
6960  _WINDOW_ENTRY_ *pEntry = WBGetWindowEntry(wID);
6961  Display *pDisplay = pEntry ? pEntry->pDisplay : pDefaultDisplay;
6962  Window wIDRoot = 0, wIDParent = 0;
6963  Window *pwIDChildren = NULL;
6964  unsigned int nChildren = 0;
6965 
6966  if(pEntry && pEntry->wParent > 0)
6967  {
6968  return pEntry->wParent;
6969  }
6970 
6971  if(XQueryTree(pDisplay, wID, &wIDRoot, &wIDParent, &pwIDChildren, &nChildren))
6972  {
6973  if(pwIDChildren)
6974  XFree(pwIDChildren);
6975  }
6976 
6977  if(wIDParent > 0 && pEntry)
6978  {
6979  pEntry->wParent = wIDParent; // cache it
6980  }
6981 
6982  return wIDParent;
6983 }
6984 
6985 void WBSetParentWindow(Window wID, Window wIDParent)
6986 {
6987  _WINDOW_ENTRY_ *pEntry = WBGetWindowEntry(wID);
6988 
6989  if(pEntry)
6990  pEntry->wParent = wIDParent;
6991 }
6992 
6993 int WBReparentWindow(Window wID, Window wIDParent, int iX, int iY)
6994 {
6995  _WINDOW_ENTRY_ *pEntry = WBGetWindowEntry(wID);
6996  Display *pDisplay = pDefaultDisplay;
6997  int iRval;
6998 
6999  if(pEntry)
7000  {
7001  pDisplay = pEntry->pDisplay;
7002  }
7003 
7004  iRval = XReparentWindow(pDisplay, wID, wIDParent, iX, iY);
7005 
7006  if(iRval < 0 && pEntry) // TODO: verify if non-zero or negative is error
7007  {
7008  pEntry->wParent = wIDParent;
7009  }
7010 
7011  return iRval;
7012 }
7013 
7014 int WBIsChildWindow(Window wIDParent, Window wIDChild)
7015 {
7016  _WINDOW_ENTRY_ *pEntry = WBGetWindowEntry(wIDParent);
7017  _WINDOW_ENTRY_ *pEntry2 = WBGetWindowEntry(wIDChild);
7018  Display *pDisplay = pDefaultDisplay;
7019  Window wIDRoot = 0, wIDP = 0;
7020  Window *pwIDChildren = NULL;
7021  int iRval = 0;
7022  unsigned int i1, nChildren = 0;
7023 
7024 
7025  if(wIDParent == wIDChild)
7026  return 0;
7027 
7028  if(pEntry2 && pEntry2->wParent > 0)
7029  {
7030  return pEntry2->wParent == wIDParent ? 1 : 0;
7031  }
7032 
7033 // if(!pEntry2)
7034 // {
7035 // fprintf(stderr, "WARNING: wIDChild %d (%08xH) has no entry\n", wIDChild, wIDChild);
7036 //
7037 // if(!WBIsChildWindow(DefaultRootWindow(pDisplay), wIDChild))
7038 // return 0; // not a valid window - I don't want crashes, so I do this
7039 // }
7040 
7041  if(pEntry)
7042  {
7043  if(WB_IS_WINDOW_DESTROYED(*pEntry) || WB_IS_WINDOW_UNMAPPED(*pEntry))
7044  return 0; // can't determine parent/child relationship
7045 
7046  // TODO: if the window has been unmapped, I may get an error
7047  // so use some kind of flag to determine if the window was unmapped...
7048 
7049  pDisplay = pEntry->pDisplay;
7050  }
7051  else
7052  {
7053  // this window may be invalid, so check for that case
7054  if(!WBIsChildWindow(DefaultRootWindow(pDisplay), wIDParent))
7055  return 0; // not a valid window - I don't want crashes, so I do this
7056  }
7057 
7058  if(XQueryTree(pDisplay, wIDParent, &wIDRoot, &wIDP, &pwIDChildren, &nChildren))
7059  {
7060  for(i1=0; pwIDChildren && i1 < nChildren; i1++)
7061  {
7062  if(pwIDChildren[i1] == wIDChild)
7063  {
7064  iRval = 1;
7065  break;
7066  }
7067  }
7068 
7069  // recurse children and get their children (etc.)
7070  for(i1=0; !iRval && pwIDChildren && i1 < nChildren; i1++)
7071  {
7072  iRval = WBIsChildWindow(pwIDChildren[i1], wIDChild); // recurse to the bottom
7073  }
7074 
7075  if(pwIDChildren)
7076  XFree(pwIDChildren);
7077 
7078  return iRval;
7079  }
7080 
7081 #if 0
7082  _WINDOW_ENTRY_ *pEntry = WBGetWindowEntry(wIDChild);
7083  Display *pDisplay = pDefaultDisplay;
7084  Window wIDRoot = 0, wIDP = 0;
7085  Window *pwIDChildren = NULL;
7086  unsigned int i1, nChildren = 0;
7087 
7088  if(wIDParent == wIDChild)
7089  return 0;
7090 
7091  if(pEntry)
7092  pDisplay = pEntry->pDisplay;
7093 
7094  if(XQueryTree(pDisplay, wIDChild, &wIDRoot, &wIDP, &pwIDChildren, &nChildren))
7095  {
7096  if(pwIDChildren)
7097  XFree(pwIDChildren);
7098 
7099  if(wIDRoot == wIDParent || // check for parent = root
7100  wIDP == wIDParent) // and a direct parent/child relationship
7101  {
7102  return 1;
7103  }
7104  else if(!wIDP || wIDP == wIDRoot)
7105  {
7106  return 0;
7107  }
7108  else
7109  {
7110  return WBIsChildWindow(wIDParent, wIDP);
7111  }
7112  }
7113 #endif // 0
7114  return 0;
7115 }
7116 
7117 
7118 /**********************************************************************/
7119 /* */
7120 /* basic menu helper functions */
7121 /* */
7122 /**********************************************************************/
7123 
7124 void WBRegisterMenuCallback(Window wID, WBWinEvent pMenuCallback)
7125 {
7126  _WINDOW_ENTRY_ *pEntry = WBGetWindowEntry(wID);
7127 
7128  if(pEntry)
7129  {
7130  pEntry->pMenuCallback = pMenuCallback;
7131  }
7132  else
7133  {
7134  WB_WARN_PRINT("%s - NULL pEntry wID = %d (%08xH)\n", __FUNCTION__, (int)wID, (int)wID);
7135  }
7136 }
7137 
7138 void WBAddMenuWindow(Window wID, Window wIDMenu)
7139 {
7140  _WINDOW_ENTRY_ *pEntry = WBGetWindowEntry(wID);
7141 
7142  if(pEntry)
7143  {
7144  // for now there is only one
7145 
7146  pEntry->wIDMenu = wIDMenu;
7147  }
7148  else
7149  {
7150  WB_WARN_PRINT("%s - NULL pEntry wID = %d (%08xH)\n", __FUNCTION__, (int)wID, (int)wID);
7151  }
7152 }
7153 
7154 Window WBGetMenuWindow(Window wID)
7155 {
7156  _WINDOW_ENTRY_ *pEntry = WBGetWindowEntry(wID);
7157 
7158  if(pEntry)
7159  {
7160  return pEntry->wIDMenu;
7161  }
7162  else
7163  {
7164  WB_WARN_PRINT("%s - NULL pEntry wID = %d (%08xH)\n", __FUNCTION__, (int)wID, (int)wID);
7165  }
7166 
7167  return 0;
7168 }
7169 
7170 void WBRemoveMenuWindow(Window wID, Window wIDMenu)
7171 {
7172  int i1;
7173  _WINDOW_ENTRY_ *pEntry = NULL;
7174 
7175 
7176  if(wID == -1) // meaning ALL of them
7177  {
7178  for(i1=0; i1 <= WINDOW_ENTRY_ARRAY_MAX; i1++)
7179  {
7180  if(sWBHashEntries[i1].wIDMenu == wIDMenu)
7181  {
7182  sWBHashEntries[i1].wIDMenu = 0;
7183  }
7184  }
7185 
7186  return;
7187  }
7188 
7189  pEntry = WBGetWindowEntry(wID);
7190  if(pEntry)
7191  {
7192  if(pEntry->wIDMenu == wIDMenu)
7193  {
7194  pEntry->wIDMenu = 0;
7195  }
7196  }
7197  else
7198  {
7199  WB_WARN_PRINT("%s - NULL pEntry wID = %d (%08xH)\n", __FUNCTION__, (int)wID, (int)wID);
7200  }
7201 }
7202 
7203 
7204 
7205 /**********************************************************************/
7206 /* */
7207 /* expose event (aka 'paint') helpers */
7208 /* */
7209 /**********************************************************************/
7210 
7211 void WBUpdateWindow(Window wID)
7212 {
7213  XRectangle xrct;
7214  XExposeEvent evt;
7215  _WINDOW_ENTRY_ *pEntry = WBGetWindowEntry(wID);
7216 
7218  XFlush(WBGetWindowDisplay(wID));
7219 
7220  if(pEntry)
7221  {
7222  if(pEntry->rgnClip == None ||
7223  XEmptyRegion(pEntry->rgnClip))
7224  {
7225 #ifndef NO_DEBUG
7226  if(pEntry->rgnClip == None) // should not happen in this case
7227  {
7228  WB_ERROR_PRINT("ERROR: %s - clipping region is 'None'\n", __FUNCTION__);
7229  }
7230  else
7231  {
7232  XClipBox(pEntry->rgnClip, &xrct);
7233 
7234  if(xrct.width != 0 && xrct.height != 0)
7235  {
7236  WB_ERROR_PRINT("ERROR: %s - clipping region is 'empty', but bounds = %d, %d, %d, %d\n", __FUNCTION__,
7237  xrct.x, xrct.y, xrct.width, xrct.height);
7238 
7239 // WBDebugDumpRegion(pEntry->rgnClip, 1);
7240  }
7241  }
7242 #endif // NO_DEBUG
7243  }
7244  else
7245  {
7246  XClipBox(pEntry->rgnClip, &xrct);
7247  // generate an 'expose' event and post it
7248 
7249  bzero(&evt, sizeof(evt));
7250  evt.type = Expose;
7251  evt.display = pEntry->pDisplay;
7252  evt.window = wID;
7253  evt.x = xrct.x;
7254  evt.y = xrct.y;
7255  evt.width = xrct.width;
7256  evt.height = xrct.height;
7257 
7258  WB_DEBUG_PRINT(DebugLevel_Medium | DebugSubSystem_Expose,
7259  "%s.%d creating/handling Expose event, %d,%d,%d,%d\n",
7260  __FUNCTION__, __LINE__, evt.x, evt.y, evt.width, evt.height);
7261 
7262  WBProcessExposeEvent(&evt); // better than posting it (this consolidates every Expose event for that window)
7263  // NOTE: it also calls WBInvalidateGeom() internally but NOT 'WBUpdateWindow()'
7264  // It also checks the message queue an combines *ALL* expose events that are waiting
7265  }
7266 
7267  }
7269 }
7270 
7272 {
7273  XRectangle xrct;
7274  XExposeEvent evt;
7275  XEvent evt0; // NOTE if it's too small I get a stack overflow so MUST be 'XEvent'
7276  _WINDOW_ENTRY_ *pEntry = WBGetWindowEntry(wID);
7277 
7278 #define WB_IS_WINDOW_UNMAPPED(X) ((X).iWindowState == WB_WINDOW_UNMAPPED || (X).iWindowState == WB_WINDOW_SET_FOCUS_ON_MAP)
7279 #define WB_IS_WINDOW_MAPPED(X) ((X).iWindowState == WB_WINDOW_MAPPED)
7280 #define WB_IS_WINDOW_BEING_DESTROYED(X) ((X).iWindowState == WB_WINDOW_DESTROYING)
7281 #define WB_IS_WINDOW_DESTROYED(X) ((X).iWindowState == WB_WINDOW_DESTROYED || (X).iWindowState == WB_WINDOW_DELETE)
7282 
7284  XFlush(WBGetWindowDisplay(wID));
7286 
7287  if(pEntry &&
7288  WB_IS_WINDOW_MAPPED(*pEntry) &&
7289  pEntry->rgnClip != None && !XEmptyRegion(pEntry->rgnClip))
7290  {
7292  XClipBox(pEntry->rgnClip, &xrct);
7294 
7295  // generate an 'expose' event and post it
7296 
7297  bzero(&evt, sizeof(evt));
7298  evt.type = Expose;
7299  evt.display = pEntry->pDisplay;
7300  evt.window = wID;
7301  evt.x = xrct.x;
7302  evt.y = xrct.y;
7303  evt.width = xrct.width;
7304  evt.height = xrct.height;
7305 
7306  WB_DEBUG_PRINT(DebugLevel_Medium | DebugSubSystem_Expose,
7307  "%s.%d creating/handling Expose event, %d,%d,%d,%d\n",
7308  __FUNCTION__, __LINE__, evt.x, evt.y, evt.width, evt.height);
7309 
7310  WBInternalProcessExposeEvent(&evt); // better than posting it (this consolidates every Expose event for that window)
7311  // NOTE: it also calls WBInvalidateGeom() internally but NOT 'WBUpdateWindow()'
7312 
7313  if(__WBNextPaintEvent(pEntry->pDisplay, &evt0, wID) >= 0) // grab the 'combined' paint event from the queue
7314  {
7315  WBWindowDispatch(wID, &evt0); // send "combined painting" message synchronously
7316  }
7317  else
7318  {
7319  WBWindowDispatch(wID, (XEvent *)&evt); // send message synchronously
7320  }
7321  }
7322 }
7323 
7324 void WBInvalidateGeom(Window wID, const WB_GEOM *pGeom, int bPaintNow)
7325 {
7326  WB_GEOM geom;
7327  XRectangle xrct;
7328 #ifndef NO_DEBUG
7329  XRectangle xrct2;
7330 #endif // NO_DEBUG
7331 
7332  _WINDOW_ENTRY_ *pEntry = WBGetWindowEntry(wID);
7333 
7335  XFlush(WBGetWindowDisplay(wID));
7336 
7337  if(pEntry)
7338  {
7339  if(!pGeom)
7340  {
7341  WBGetWindowGeom(wID, &geom);
7342  geom.x = geom.y = geom.border; // always use coordinates relative to window origin (0,0)
7343  pGeom = &geom;
7344 
7345  WB_DEBUG_PRINT(DebugLevel_Medium | DebugSubSystem_Expose,
7346  "%s.%d - NULL pGeom, %d,%d,%d,%d\n", __FUNCTION__, __LINE__,
7347  pGeom->x, pGeom->y, pGeom->width, pGeom->height);
7348  }
7349  else
7350  {
7351  WB_DEBUG_PRINT(DebugLevel_Medium | DebugSubSystem_Expose,
7352  "%s.%d - %d,%d,%d,%d\n", __FUNCTION__, __LINE__,
7353  pGeom->x, pGeom->y, pGeom->width, pGeom->height);
7354  }
7355 
7356  // NOTE: XRectangle doesn't encompass the full range of x,y,width,height - limited to 16-bit values
7357 
7358  xrct.x = (short)pGeom->x;
7359  xrct.y = (short)pGeom->y;
7360  xrct.width = (unsigned short)pGeom->width;
7361  xrct.height = (unsigned short)pGeom->height;
7362 
7363  if(!pEntry->rgnClip)
7364  {
7365  pEntry->rgnClip = XCreateRegion();
7366  }
7367 
7368  if(pEntry->rgnClip)
7369  {
7370  XUnionRectWithRegion(&xrct, pEntry->rgnClip, pEntry->rgnClip);
7371 
7372  WB_DEBUG_PRINT(DebugLevel_Medium | DebugSubSystem_Expose,
7373  "%s.%d - resulting invalid rect: %d,%d,%d,%d\n", __FUNCTION__, __LINE__,
7374  xrct.x, xrct.y, xrct.width, xrct.height);
7375 
7376  if(WBCheckDebugLevel(DebugLevel_Medium | DebugSubSystem_Expose))
7377  {
7378  XRectangle xrct;
7379 
7381  XClipBox(pEntry->rgnClip, &xrct);
7383 
7384  WB_ERROR_PRINT("%s.%d - Bounding rectangle of new invalid region: %d, %d, %d, %d\n",
7385  __FUNCTION__, __LINE__, xrct.x, xrct.y, xrct.width, xrct.height);
7386 
7387 // WBDebugDumpRegion(pEntry->rgnClip, 1);
7388  }
7389 
7390  if(bPaintNow)
7391  {
7392  WBUpdateWindow(wID);
7393  }
7394  }
7395  }
7397 }
7398 
7399 void WBInvalidateRegion(Window wID, Region rgn, int bPaintFlag)
7400 {
7401 // WB_GEOM geom;
7402 
7403  _WINDOW_ENTRY_ *pEntry = WBGetWindowEntry(wID);
7404 
7405  if(rgn == None)
7406  {
7407  WBInvalidateGeom(wID, NULL, bPaintFlag);
7408  return;
7409  }
7410 
7412  XFlush(WBGetWindowDisplay(wID));
7414 
7415  if(pEntry)
7416  {
7417  if(!pEntry->rgnClip)
7418  {
7419  pEntry->rgnClip = XCreateRegion();
7420  }
7421 
7422  if(pEntry->rgnClip)
7423  {
7425  XUnionRegion(rgn, pEntry->rgnClip, pEntry->rgnClip);
7427 
7428  if(bPaintFlag)
7429  {
7430  WBUpdateWindow(wID);
7431  }
7432 
7433  if(WBCheckDebugLevel(DebugLevel_Medium | DebugSubSystem_Expose))
7434  {
7435  XRectangle xrct;
7436 
7438  XClipBox(pEntry->rgnClip, &xrct);
7440 
7441  // use WB_ERROR_PRINT since it avoids the test for debug level
7442  WB_ERROR_PRINT("%s.%d - Bounding rectangle of new invalid region: %d, %d, %d, %d\n",
7443  __FUNCTION__, __LINE__, xrct.x, xrct.y, xrct.width, xrct.height);
7444 
7445 // WBDebugDumpRegion(pEntry->rgnClip, 1);
7446  }
7447  }
7448  else
7449  {
7450  WB_ERROR_PRINT("%s.%d - unable to create clipping region\n", __FUNCTION__, __LINE__);
7451  }
7452  }
7453 }
7454 
7455 void WBValidateGeom(Window wID, const WB_GEOM *pGeom)
7456 {
7457 // WB_GEOM geom;
7458  Region rgnTemp;
7459  XRectangle xrct;
7460  _WINDOW_ENTRY_ *pEntry;
7461 
7462  pEntry = WBGetWindowEntry(wID);
7463 
7465  XFlush(WBGetWindowDisplay(wID));
7467 
7468  if(pEntry)
7469  {
7470  if(!pEntry->rgnClip)
7471  {
7472  pEntry->rgnClip = XCreateRegion(); // put an empty one there
7473 
7474  return;
7475  }
7476 
7477  if(!pGeom) // validate everything
7478  {
7479  if(pEntry->rgnClip)
7480  {
7482  XDestroyRegion(pEntry->rgnClip);
7484  }
7485 
7486  pEntry->rgnClip = XCreateRegion(); // put an empty one there
7487 
7488  return;
7489  }
7490 
7491  xrct.x = (short)pGeom->x;
7492  xrct.y = (short)pGeom->y;
7493  xrct.width = (unsigned short)pGeom->width;
7494  xrct.height = (unsigned short)pGeom->height;
7495 
7497  rgnTemp = XCreateRegion();
7499 
7500  if(!rgnTemp)
7501  {
7502  return; // oops (error)
7503  }
7504 
7506  XUnionRectWithRegion(&xrct, rgnTemp, rgnTemp);
7507  XSubtractRegion(rgnTemp, pEntry->rgnClip, pEntry->rgnClip);
7508 
7509  XDestroyRegion(rgnTemp);
7510 
7511  if(XEmptyRegion(pEntry->rgnClip)) // if it's empty, destroy it
7512  {
7513  // now I leave the empty region alone
7514  }
7515 
7517  }
7518 }
7519 
7520 void WBValidateRegion(Window wID, Region rgn)
7521 {
7522  _WINDOW_ENTRY_ *pEntry;
7523 
7524  pEntry = WBGetWindowEntry(wID);
7525 
7527  XFlush(WBGetWindowDisplay(wID));
7529 
7530  if(pEntry)
7531  {
7532  if(!pEntry->rgnClip)
7533  {
7534  pEntry->rgnClip = XCreateRegion(); // put an empty one there
7535 
7536  return;
7537  }
7538 
7539  if(rgn == None) // validate everything
7540  {
7541  if(pEntry->rgnClip)
7542  {
7544  XDestroyRegion(pEntry->rgnClip);
7546  }
7547 
7548  pEntry->rgnClip = XCreateRegion(); // put an empty one there
7549 
7550  return;
7551  }
7552 
7554  XSubtractRegion(rgn, pEntry->rgnClip, pEntry->rgnClip);
7555  if(XEmptyRegion(pEntry->rgnClip))
7556  {
7557  // now I leave the empty region alone
7558  }
7560  }
7561 }
7562 
7563 Region WBGetInvalidRegion(Window wID)
7564 {
7565  _WINDOW_ENTRY_ *pEntry = WBGetWindowEntry(wID);
7566 
7568  XFlush(WBGetWindowDisplay(wID));
7570 
7571  if(pEntry && pEntry->rgnClip)
7572  {
7573  int iRet;
7574 
7576  iRet = XEmptyRegion(pEntry->rgnClip);
7578 
7579  if(iRet)
7580  {
7581  return None;
7582  }
7583  else
7584  {
7585  Region rgnRval = XCreateRegion();
7586 
7587  if(rgnRval == None)
7588  {
7589  WB_ERROR_PRINT("ERROR: %s - no region created\n", __FUNCTION__);
7590  }
7591  else
7592  {
7594  XUnionRegion(pEntry->rgnClip, rgnRval, rgnRval);
7596  }
7597 
7598  return rgnRval;
7599  }
7600  }
7601 
7602  return None;
7603 }
7604 
7605 Region WBGetPaintRegion(Window wID)
7606 {
7607  _WINDOW_ENTRY_ *pEntry = WBGetWindowEntry(wID);
7608 
7610  XFlush(WBGetWindowDisplay(wID));
7612 
7613  if(pEntry && pEntry->rgnPaint)
7614  {
7615  Region rgnRval = XCreateRegion();
7616 
7617  if(rgnRval == None)
7618  {
7619  WB_ERROR_PRINT("ERROR: %s - no region created\n", __FUNCTION__);
7620  }
7621  else
7622  {
7624  XUnionRegion(pEntry->rgnPaint, rgnRval, rgnRval);
7626  }
7627 
7628  return rgnRval;
7629  }
7630 
7631  return None;
7632 }
7633 
7634 Region WBCopyRegion(Region rgnSource)
7635 {
7636 Region rgnRval;
7637 
7638  if((CARD32)(intptr_t)rgnSource & 0xe0000000) // see man page for XGetGCValues and the implication of invalid XIDs
7639  {
7640  WB_ERROR_PRINT("ERROR: %s - invalid source region %d (%08xH), no region created\n",
7641  __FUNCTION__, (int)(intptr_t)rgnSource, (int)(intptr_t)rgnSource);
7642 
7643  return NULL;
7644  }
7645 
7647  rgnRval = XCreateRegion();
7649 
7650  if(rgnRval == None)
7651  {
7652  WB_ERROR_PRINT("ERROR: %s - unable to create region\n", __FUNCTION__);
7653  }
7654  else
7655  {
7657  XUnionRegion(rgnSource, rgnRval, rgnRval);
7659  }
7660 
7661  return rgnRval;
7662 }
7663 
7664 Region WBRectToRegion(const WB_RECT *pRect)
7665 {
7666 XRectangle xrct;
7667 Region rgnRval;
7668 
7669  if(!pRect)
7670  {
7671  return None;
7672  }
7673 
7675  rgnRval = XCreateRegion();
7677 
7678  if(rgnRval == None)
7679  {
7680  WB_ERROR_PRINT("ERROR: %s - no region created\n", __FUNCTION__);
7681  }
7682  else
7683  {
7684  xrct.x = (short)pRect->left;
7685  xrct.y = (short)pRect->top;
7686  xrct.width = (unsigned short)(pRect->right - pRect->left);
7687  xrct.height = (unsigned short)(pRect->bottom - pRect->top);
7688 
7690  XUnionRectWithRegion(&xrct, rgnRval, rgnRval);
7692  }
7693 
7694  return rgnRval;
7695 }
7696 
7697 Region WBGeomToRegion(const WB_GEOM *pGeom)
7698 {
7699 XRectangle xrct;
7700 Region rgnRval;
7701 
7702  if(!pGeom)
7703  {
7704  return None;
7705  }
7706 
7708  rgnRval = XCreateRegion();
7710 
7711  if(rgnRval == None)
7712  {
7713  WB_ERROR_PRINT("ERROR: %s - no region created\n", __FUNCTION__);
7714  }
7715  else
7716  {
7717  xrct.x = (short)pGeom->x;
7718  xrct.y = (short)pGeom->y;
7719  xrct.width = (unsigned short)pGeom->width;
7720  xrct.height = (unsigned short)pGeom->height;
7721 
7723  XUnionRectWithRegion(&xrct, rgnRval, rgnRval);
7725  }
7726 
7727  return rgnRval;
7728 }
7729 
7730 // paint helpers
7731 
7732 WBGC WBBeginPaint(Window wID, XExposeEvent *pEvent, WB_GEOM *pgBounds)
7733 {
7734 _WINDOW_ENTRY_ *pEntry = WBGetWindowEntry(wID);
7735 WB_GEOM geomTemp;
7736 WBGC gcRval;
7737 int iRet;
7738 
7739 
7740  if(!pEvent || pEvent->type != Expose || !pEntry)
7741  {
7742  WB_DEBUG_PRINT(DebugLevel_WARN | DebugSubSystem_Expose,
7743  "%s.%d - returning None (bad parameter)\n", __FUNCTION__, __LINE__);
7744  return None;
7745  }
7746 
7747  geomTemp.x = pEvent->x;
7748  geomTemp.y = pEvent->y;
7749  geomTemp.width = pEvent->width;
7750  geomTemp.height = pEvent->height;
7751 
7753  iRet = XEmptyRegion(pEntry->rgnClip);
7755 
7756  if(iRet)
7757  {
7758  geomTemp.x = geomTemp.y = geomTemp.width = geomTemp.height = 0;
7759  gcRval = NULL; // nothing to paint
7760 
7761  WB_DEBUG_PRINT(DebugLevel_Medium | DebugSubSystem_Expose,
7762  "%s.%d - returning NULL (nothing to paint)\n", __FUNCTION__, __LINE__);
7763  }
7764  else
7765  {
7766  gcRval = WBBeginPaintGeom(wID, &geomTemp);
7767 
7768  if(gcRval && pgBounds)
7769  {
7770  pgBounds->x = geomTemp.x;
7771  pgBounds->y = geomTemp.y;
7772  pgBounds->width = geomTemp.width;
7773  pgBounds->height = geomTemp.height;
7774  }
7775  }
7776 
7777  return gcRval;
7778 }
7779 
7780 WBGC WBBeginPaintGeom(Window wID, WB_GEOM *pgBounds) // WBGC will get the 'invalid' region assigned as clip region
7781 {
7782  _WINDOW_ENTRY_ *pEntry = WBGetWindowEntry(wID);
7783  WBGC gcRval;
7784  Region rgnPaint;
7785  XRectangle xrct;
7786  int iRet;
7787 
7788 
7789  if(!pEntry || !pgBounds)
7790  {
7791  WB_ERROR_PRINT("%s.%d - invalid parameters\n", __FUNCTION__, __LINE__);
7792 
7793  return NULL;
7794  }
7795 
7797  XSync(WBGetWindowDisplay(wID), 0);
7798  if(pEntry->rgnClip)
7799  {
7800  iRet = XEmptyRegion(pEntry->rgnClip);
7801  }
7803 
7804  if(!pEntry->rgnClip || iRet) // clipping region is empty?
7805  {
7806  if(!pEntry->rgnClip)
7807  {
7808  WB_DEBUG_PRINT(DebugLevel_Light | DebugSubSystem_Expose,
7809  "%s.%d - no clip region (returning NULL)\n", __FUNCTION__, __LINE__);
7810  }
7811  else // if(pBounds)
7812  {
7813  WB_DEBUG_PRINT(DebugLevel_Light | DebugSubSystem_Expose,
7814  "%s.%d - empty clip region - bounds = %d,%d,%d,%d (returning NULL)\n",
7815  __FUNCTION__, __LINE__, pgBounds->x, pgBounds->y, pgBounds->width, pgBounds->height);
7816  }
7817 
7818  // NOTE: If I'm in the middle of painting, *AND* the region is empty, then additional 'Expose'
7819  // handlers might call this function. I need to gracefully allow the empty region
7820 
7821  if(!pEntry->rgnClip)
7822  {
7824  pEntry->rgnClip = XCreateRegion(); // create an empty region
7826  }
7827 
7828  return NULL; // don't return a WBGC - no need to paint
7829  }
7830 
7831  gcRval = WBCopyGC(pEntry->hGC);
7832 
7833  if(!gcRval)
7834  {
7835  WB_ERROR_PRINT("%s - * BUG * at line %d - unable to copy GC for window %d (%08xH)\n",
7836  __FUNCTION__, __LINE__, (int)wID, (int)wID);
7837 
7838  // it's checked again, but I need to flow through to the next part
7839  }
7840 
7841  // using pEntry->rgnClip create a paint region
7842  // that intersects with the paint area
7843 
7844  if(pEntry->rgnPaint)
7845  {
7846  WB_WARN_PRINT("%s - * BUG * at line %d - non-zero paint region for window %d (%08xH)\n",
7847  __FUNCTION__, __LINE__, (int)wID, (int)wID);
7848 
7850  XDestroyRegion(pEntry->rgnPaint);
7852 
7853  pEntry->rgnPaint = None;
7854  }
7855 
7856  if(gcRval)
7857  {
7859  rgnPaint = XCreateRegion();
7861 
7862  if(rgnPaint)
7863  {
7864  Region rgnTemp = XCreateRegion();
7865 
7866  if(!rgnTemp)
7867  {
7869  XDestroyRegion(rgnPaint);
7871 
7872  rgnPaint = None;
7873 
7874  WBFreeGC(gcRval);
7875  gcRval = NULL;
7876 
7877  WB_ERROR_PRINT("ERROR: %s - could not create clip region\n", __FUNCTION__);
7878  }
7879  else
7880  {
7881  // combine specified region with clipping region. result is new paint region
7882  xrct.x = pgBounds->x;
7883  xrct.y = pgBounds->y;
7884  xrct.width = pgBounds->width;
7885  xrct.height = pgBounds->height;
7886 
7888  XUnionRectWithRegion(&xrct, rgnTemp, rgnTemp); // the paint rectangle as a region
7889  XUnionRegion(pEntry->rgnClip, rgnPaint, rgnPaint); // a copy of the invalid region
7890  XIntersectRegion(rgnTemp, rgnPaint, rgnPaint); // INTERSECT with xrct (usually from the Expose event)
7891 
7892  // so now the paint region includes the intersect of the xrct passed as 'pgBounds' with the invalid region
7893  // (if called by WBBeginPaint, 'pgBounds' will be the rectangular area from the Expose event)
7894 
7895  XDestroyRegion(rgnTemp);
7897  }
7898  }
7899 
7900  if(rgnPaint) // using this as a flag, of sorts - could also use 'gcRval'
7901  {
7902  // by default we clip children so I must enumerate them now
7903  // for those that are visible, clip their rectangles out of
7904  // the paint region.
7905 
7906  // TDDO: clip children
7907 
7908 
7910  iRet = XEmptyRegion(rgnPaint);
7912 
7913  if(iRet) // it's empty - return NULL
7914  {
7916  XDestroyRegion(rgnPaint);
7918 
7919  WBFreeGC(gcRval);
7920 
7921  WB_DEBUG_PRINT(DebugLevel_WARN | DebugSubSystem_Expose,
7922  "%s.%d - empty paint region - returning NULL gcRval for window %d (%08xH)\n",
7923  __FUNCTION__, __LINE__, (int)wID, (int)wID);
7924 
7925  return NULL;
7926  }
7927 
7928  pEntry->rgnPaint = rgnPaint;
7929 
7930  // gcRval is always NOT-NULL here
7931 
7932  // assign the clipping region to the GC
7933 
7934  WBSetClipOrigin(gcRval, 0, 0);
7935  WBSetRegion(gcRval, rgnPaint);
7936  }
7937  }
7938 
7939  if(gcRval != NULL /*&& pgBounds*/) // pgBounds is ALWAYS 'NOT NULL'
7940  {
7941  // get the 'bounds' from the expose event
7942  // and intersect them. This should prevent me from re-painting several times
7943 
7945  XClipBox(rgnPaint, &xrct);
7947 
7948  pgBounds->x = xrct.x;
7949  pgBounds->y = xrct.y;
7950  pgBounds->width = xrct.width;
7951  pgBounds->height = xrct.height;
7952  pgBounds->border = 0;
7953 
7954 
7955  WB_DEBUG_PRINT(DebugLevel_Medium | DebugSubSystem_Expose,
7956  "%s.%d - bounds=%d,%d,%d,%d\n",
7957  __FUNCTION__, __LINE__,
7958  xrct.x, xrct.y, xrct.width, xrct.height);
7959  }
7960 
7961  if(gcRval == NULL)
7962  {
7963  WB_DEBUG_PRINT(DebugLevel_WARN | DebugSubSystem_Expose,
7964  "%s.%d - returning NULL gcRval for window %d (%08xH)\n",
7965  __FUNCTION__, __LINE__, (int)wID, (int)wID);
7966  }
7967 
7968  return gcRval;
7969 }
7970 
7971 void WBClearWindow(Window wID, WBGC gc)
7972 {
7973 unsigned long clrFG, clrBG;
7974 _WINDOW_ENTRY_ *pEntry = WBGetWindowEntry(wID);
7975 Display *pDisplay;
7976 XRectangle xrct;
7977 
7978 
7979  if(!pEntry || pEntry->rgnPaint == None)
7980  {
7981  WB_ERROR_PRINT("ERROR: %s - WBClearWindow with no paint region\n", __FUNCTION__);
7982 
7983  return; // for now don't do anything
7984  }
7985 
7986  pDisplay = pEntry->pDisplay; // cache it
7987 
7988  clrFG = WBGetGCFGColor(gc);
7989  clrBG = WBGetGCBGColor(gc);
7990 
7992  XClipBox(pEntry->rgnPaint, &xrct);
7994 
7996 // if(xrct.x < 0 || xrct.y < 0 ||
7997 // xrct.width > pEntry->width ||
7998 // xrct.height > pEntry->height)
7999 // {
8000 // WB_ERROR_PRINT("TEMPORARY: %s - rect exceeds window bounds (%d, %d, %d, %d)\n", __FUNCTION__,
8001 // xrct.x, xrct.y, xrct.x + xrct.width, xrct.y + xrct.height);
8002 // }
8003 // else
8004 // {
8005 // WB_ERROR_PRINT("TEMPORARY: %s - filling rect (%d, %d, %d, %d)\n", __FUNCTION__,
8006 // xrct.x, xrct.y, xrct.x + xrct.width, xrct.y + xrct.height);
8007 // }
8009 
8010 // BEGIN_XCALL_DEBUG_WRAPPER
8011 //
8012 // // use XClearArea first (this should refresh what was there before if 'covered up')
8013 // XClearArea(pDisplay, wID, xrct.x, xrct.y, xrct.width, xrct.height, False);
8014 //
8015 // END_XCALL_DEBUG_WRAPPER
8016 
8017  // now erase the background according to the clip rectangle
8018 
8019 // BEGIN_XCALL_DEBUG_WRAPPER
8020  WBSetForeground(gc, clrBG);
8021 
8022  if(xrct.width > 0 && xrct.height > 0)
8023  {
8024 // WB_ERROR_PRINT("TEMPORARY: %s - clearing window %08xH geom %d, %d, %d, %d\n",
8025 // __FUNCTION__, (WB_UINT32)wID, xrct.x, xrct.y, xrct.width, xrct.height);
8026 
8027  WBFillRectangle(pDisplay, wID, gc, xrct.x, xrct.y, xrct.width, xrct.height);
8028  }
8029 
8030  WBSetForeground(gc, clrFG);
8031 }
8032 
8033 void WBEndPaint(Window wID, WBGC gc)
8034 {
8035  // validate the paint region and remove it from the 'invalid' region
8036 
8037  _WINDOW_ENTRY_ *pEntry = WBGetWindowEntry(wID);
8038 
8039  if(pEntry->rgnClip != None)
8040  {
8041  if(pEntry->pImage)
8042  {
8043  // for now update with everything; later, just update the modified rgnPaint paint region?
8044  // (it would have to be an intersection between the paint region and the clipping region)
8045 
8046  WBUpdateWindowWithImage(pEntry->pDisplay, wID);
8047  }
8048 
8049  if(pEntry->rgnPaint != None)
8050  {
8052  XSubtractRegion(pEntry->rgnClip, pEntry->rgnPaint, pEntry->rgnClip);
8054 
8055 // TODO: should I leave the empty region anyway, or not? leaving it might work better...
8056 //
8057 // if(XEmptyRegion(pEntry->rgnClip))
8058 // {
8059 // XDestroyRegion(pEntry->rgnClip);
8060 // pEntry->rgnClip = None;
8061 // }
8062 
8063 // some debugging code
8064 //
8065 // {
8066 // XRectangle xrct;
8067 //
8068 // BEGIN_XCALL_DEBUG_WRAPPER
8069 // XClipBox(pEntry->rgnPaint, &xrct);
8070 // END_XCALL_DEBUG_WRAPPER
8071 //
8072 // WB_ERROR_PRINT("TEMPORARY: %s - new clip region, window %08xH geom %d, %d, %d, %d\n",
8073 // __FUNCTION__, (WB_UINT32)wID, xrct.x, xrct.y, xrct.width, xrct.height);
8074 // }
8075  }
8076  else // destroy clip region if paint region is 'None'
8077  {
8078  WB_ERROR_PRINT("ERROR: %s - no paint region\n", __FUNCTION__);
8079 
8080 // XDestroyRegion(pEntry->rgnClip);
8081 // pEntry->rgnClip = None;
8082  }
8083  }
8084  else
8085  {
8086  WB_ERROR_PRINT("ERROR: %s - no clip region\n", __FUNCTION__);
8087  }
8088 
8089  if(pEntry->rgnPaint != None)
8090  {
8092  XDestroyRegion(pEntry->rgnPaint);
8094 
8095  pEntry->rgnPaint = None;
8096  }
8097 
8098  if(gc)
8099  {
8100  WBFreeGC(gc);
8101  }
8102 }
8103 
8104 
8105 /**********************************************************************/
8106 /* */
8107 /* Cached XImage handling for paint and graphics */
8108 /* */
8109 /**********************************************************************/
8110 
8111 // POOBAH
8112 XImage *WBGetWindowImage(Display *pDisplay, Window wID)
8113 {
8114 _WINDOW_ENTRY_ *pEntry = WBGetWindowEntry(wID);
8115 
8116  if(!pEntry || pEntry->iWindowState == WB_WINDOW_DESTROYED) // so that there is no attempt to use the window
8117  {
8118  return NULL;
8119  }
8120 
8122  {
8123  return NULL; // always
8124  }
8125 
8126 #ifdef USE_WINDOW_XIMAGE
8127  if(!disable_imagecache && !(pEntry->pImage)) // this allows me to do a 'soft disable' of the image cache
8128  {
8129  Window winRoot = None;
8130  int iX0=0, iY0=0;
8131  unsigned int iWidth0=0, iHeight0=0, iBorder;
8132  unsigned int uiDepth = 0;
8133 
8134  if(!pDisplay)
8135  {
8136  pDisplay = pEntry->pDisplay;
8137  if(!pDisplay)
8138  {
8139  pDisplay = WBGetDefaultDisplay();
8140  }
8141  }
8142 
8144  XGetGeometry(pDisplay, wID, &winRoot, &iX0, &iY0, &iWidth0, &iHeight0, &iBorder, &uiDepth);
8145 
8146  // TODO: if the window has never been painted, create a blank pixmap and fill with the background color
8147  // It may also be possible (for a completely invalid window) to erase its pixmap and then create
8148  // a 'blank' one of the appropriate size without grabbing the bits off of the X server
8149 
8150  pEntry->pImage = WBXGetImage(pDisplay, wID, 0, 0, iWidth0, iHeight0, 0xffffffff, ZPixmap);
8151  // NOTE: this may fail if part of the window extends past the screen size. In those cases, it may
8152  // be necessary to 'fudge' it a bit.
8153  // TODO: test for and handle this condition
8155 
8156  // NOTE: the XImage has width and height members indicating such. when window is re-sized, these can be
8157  // compared, and if the new window size is larger than the pixmap (or just different) either a
8158  // new pixmap can be derived from the old one, or it can simply be deleted and re-created when needed.
8159  }
8160 #endif // USE_WINDOW_XIMAGE
8161 
8162  return pEntry->pImage;
8163 }
8164 
8165 
8166 int WBAssignWindowImage(Display *pDisplay, Window wID, XImage *pImage)
8167 {
8168 _WINDOW_ENTRY_ *pEntry = WBGetWindowEntry(wID);
8169 
8170 
8171  if(!pEntry || (pEntry->iFlags & WBCreateWindow_flagsNoImageCache))
8172  {
8173  return -1; // not using an image, can't assign one
8174  }
8175 
8176 
8177  return -1; // for now
8178 }
8179 
8180 
8181 int WBCopyIntoWindowImage(Display *pDisplay, Window wID, XImage *pSrcImage,
8182  int xSrc, int ySrc, int width, int height,
8183  int xOffs, int yOffs)
8184 {
8185 _WINDOW_ENTRY_ *pEntry = WBGetWindowEntry(wID);
8186 
8187 
8188  if(!pEntry || (pEntry->iFlags & WBCreateWindow_flagsNoImageCache))
8189  {
8190  return -1; // not using an image, can't assign one nor copy
8191  // TODO: handle this situation using a Pixmap and copy to window instead??
8192  }
8193 
8194 
8195  return -1; // for now
8196 }
8197 
8198 
8199 void WBUpdateWindowWithImage(Display *pDisplay, Window wID)
8200 {
8201 _WINDOW_ENTRY_ *pEntry = WBGetWindowEntry(wID);
8202 
8203  // TODO: see __internalDoAntiAlias in draw_text.c for similar operations
8204 
8205  if(!pEntry || pEntry->iWindowState == WB_WINDOW_DESTROYED) // so that there is no attempt to use the window
8206  {
8207  if(!pEntry)
8208  {
8209  WB_WARN_PRINT("WARNING: %s - no 'window entry' for Window %d (%08xH)\n",
8210  __FUNCTION__, (unsigned int)wID, (unsigned int)wID);
8211  }
8212  else
8213  {
8214  WB_DEBUG_PRINT(DebugLevel_Medium, "WARNING: %s - NOT updating 'destroyed' Window %d (%08xH)\n",
8215  __FUNCTION__, (unsigned int)wID, (unsigned int)wID);
8216  }
8217 
8218  return;
8219  }
8220 
8221  // use the current (default) graphics context. if not valid, fail.
8222  // TODO: make a copy and ONLY update the current 'invalid' region instead??
8223  // this might actually speed things up a LOT
8224 
8225  if(pEntry->hGC == None) // no default GC - can't do this
8226  {
8227  WB_DEBUG_PRINT(DebugLevel_Light, "WARNING: %s - no GC for Window %d (%08xH)\n",
8228  __FUNCTION__, (unsigned int)wID, (unsigned int)wID);
8229  return;
8230  }
8231 
8232  if(!(pEntry->pImage))
8233  {
8234  WB_WARN_PRINT("WARNING: %s - no XImage for %d (%08xH) - this should never happen\n",
8235  __FUNCTION__, (unsigned int)wID, (unsigned int)wID);
8236  }
8237  else
8238  {
8239  Window winRoot = None;
8240  int iX0=0, iY0=0;
8241  unsigned int iWidth0=0, iHeight0=0, iBorder;
8242  unsigned int uiDepth = 0;
8243 
8244  if(!pDisplay)
8245  {
8246  pDisplay = pEntry->pDisplay;
8247  if(!pDisplay)
8248  {
8249  pDisplay = WBGetDefaultDisplay();
8250  }
8251  }
8252 
8254  // TODO: always use cached information from pEntry ???
8255  XGetGeometry(pDisplay, wID, &winRoot, &iX0, &iY0, &iWidth0, &iHeight0, &iBorder, &uiDepth);
8257 
8258  // if the image size is too small, reduce width and height accordingly
8259 
8260  if(WB_UNLIKELY(pEntry->pImage->width < iWidth0))
8261  {
8262  iWidth0 = pEntry->pImage->width;
8263  }
8264 
8265  if(WB_UNLIKELY(pEntry->pImage->height < iHeight0))
8266  {
8267  iHeight0 = pEntry->pImage->height;
8268  }
8269 
8270  // TODO: limit the update to the bounding rectangle of the invalid/clipping region. This
8271  // might be fastest. But, when the invalid/clip region is empty, do it ALL.
8272 
8273  WBXPutImage(pDisplay, wID, pEntry->hGC, pEntry->pImage, 0, 0, iX0, iY0, iWidth0, iHeight0);
8274  // NOTE: this process should be very fast by comparison to getting the image from the drawable (window)
8275  }
8276 }
8277 
8278 
8279 
8280 /**********************************************************************/
8281 /* */
8282 /* internal event queue processing */
8283 /* */
8284 /**********************************************************************/
8285 
8286 static void __WBInitEvent()
8287 {
8288  int i1;
8289 
8290  if(iInitEventFlag)
8291  {
8292  return; // already done
8293  }
8294 
8295  iInitEventFlag = 1;
8296 
8297  // initialize the 'free' event pool, and all of the other event queue entries
8298 
8299  iWBFreeEvent = 0;
8300  for(i1=1; i1 < EVENT_ARRAY_SIZE; i1++)
8301  {
8302  axWBEvt[i1 - 1].iNext = i1;
8303  }
8304 
8305  axWBEvt[EVENT_ARRAY_SIZE - 1].iNext = -1; // end of chain
8306  iWBQueuedEvent = iWBQueueLast = -1; // because the queue is empty
8307  iWBPaintEvent = iWBPaintLast = -1; // same here
8308 }
8309 
8310 
8311 static int __WBAddEvent(Display *pDisp, Window wID, XEvent *pEvent)
8312 {
8313  int iEvent = -1;
8314  // TODO: synchronization objects?
8315 
8316  if(!iInitEventFlag)
8317  {
8318  __WBInitEvent();
8319  }
8320 
8321  if(iWBFreeEvent < 0) // none left
8322  {
8323  return -1;
8324  }
8325 
8326  if(pEvent->type == Expose)
8327  {
8328  WBProcessExposeEvent((XExposeEvent *)pEvent);
8329  }
8330 
8331  iEvent = iWBFreeEvent;
8332  iWBFreeEvent = axWBEvt[iWBFreeEvent].iNext; // new 'free' pointer
8333  axWBEvt[iEvent].iNext = -1; // mark the end of the 'iEvent's entry
8334 
8335  // add to iWBQueuedEvent linked list
8336 
8337  if(iWBQueuedEvent < 0)
8338  {
8339  iWBQueuedEvent = iEvent;
8340  iWBQueueLast = iEvent;
8341  }
8342  else
8343  {
8344  if(iWBQueueLast < 0)
8345  {
8346  // traverse the queue
8347  iWBQueueLast = iWBQueuedEvent;
8348  while(axWBEvt[iWBQueueLast].iNext != -1)
8349  {
8350  iWBQueueLast = axWBEvt[iWBQueueLast].iNext;
8351  }
8352  }
8353 
8354  axWBEvt[iWBQueueLast].iNext = iEvent;
8355  iWBQueueLast = iEvent;
8356  }
8357 
8358  axWBEvt[iEvent].pDisplay = pDisp;
8359  axWBEvt[iEvent].wID = wID;
8360  memcpy(&(axWBEvt[iEvent].xEvt), pEvent, sizeof(XEvent));
8361 
8362  return 0; // success
8363 }
8364 
8365 static void __WBDelWindowPaintEvents(Display *pDisp, Window wID)
8366 {
8367  int iEvent, iPrev, iTemp;
8368  XEvent event;
8369 
8370  if(iWBPaintEvent < 0)
8371  {
8372  return;
8373  }
8374 
8375  iEvent = iWBPaintEvent;
8376  iPrev = -1;
8377 
8378  while(iEvent >= 0)
8379  {
8380  if(axWBEvt[iEvent].wID == wID &&
8381  axWBEvt[iEvent].pDisplay == pDisp)
8382  {
8383  if(iPrev < 0)
8384  {
8385  iWBPaintEvent = axWBEvt[iEvent].iNext; // remove from head
8386  if(iWBPaintEvent < 0)
8387  {
8388  iWBPaintLast = -1;
8389  }
8390  }
8391  else
8392  {
8393  axWBEvt[iPrev].iNext = axWBEvt[iEvent].iNext; // remove from chain
8394  if(axWBEvt[iPrev].iNext < 0)
8395  {
8396  iWBPaintLast = iPrev;
8397  }
8398  }
8399 
8400  iTemp = iEvent;
8401 
8402  // add 'iEvent' to the chain of free events
8403  iEvent = axWBEvt[iTemp].iNext;
8404  axWBEvt[iTemp].iNext = iWBFreeEvent;
8405  iWBFreeEvent = iTemp;
8406  }
8407  else
8408  {
8409  iPrev = iEvent;
8410  iEvent = axWBEvt[iEvent].iNext;
8411  }
8412  }
8413 
8414  XSync(pDisp, 0);
8415 
8416  // now the X events (remove them from the queue)
8417  while(XCheckWindowEvent(pDisp, wID, ExposureMask, &event))
8418  ;
8419 }
8420 
8421 static void __WBDelWindowEvents(Display *pDisp, Window wID)
8422 {
8423  int iEvent, iPrev, iTemp;
8424  XEvent event;
8425 
8426 // if(iWBQueuedEvent < 0 && iWBPaintEvent < 0)
8427 // {
8428 // return;
8429 // }
8430 
8431  // traverse iWBQueuedEvent
8432 
8433  iEvent = iWBQueuedEvent;
8434  iPrev = -1;
8435  while(iEvent >= 0)
8436  {
8437  if(axWBEvt[iEvent].wID == wID &&
8438  axWBEvt[iEvent].pDisplay == pDisp)
8439  {
8440  if(iPrev < 0)
8441  {
8442  iWBQueuedEvent = axWBEvt[iEvent].iNext; // remove from head
8443  if(iWBQueuedEvent < 0)
8444  {
8445  iWBQueueLast = -1;
8446  }
8447  }
8448  else
8449  {
8450  axWBEvt[iPrev].iNext = axWBEvt[iEvent].iNext; // remove from chain
8451  if(axWBEvt[iPrev].iNext < 0)
8452  {
8453  iWBQueueLast = iPrev;
8454  }
8455  }
8456 
8457  iTemp = iEvent;
8458 
8459  // add 'iEvent' to the chain of free events
8460  iEvent = axWBEvt[iTemp].iNext;
8461  axWBEvt[iTemp].iNext = iWBFreeEvent;
8462  iWBFreeEvent = iTemp;
8463  }
8464  else
8465  {
8466  iPrev = iEvent;
8467  iEvent = axWBEvt[iEvent].iNext;
8468  }
8469  }
8470 
8471  // NOW traverse iWBPaintEvent
8472 
8473  iEvent = iWBPaintEvent;
8474  iPrev = -1;
8475  while(iEvent >= 0)
8476  {
8477  if(axWBEvt[iEvent].wID == wID &&
8478  axWBEvt[iEvent].pDisplay == pDisp)
8479  {
8480  if(iPrev < 0)
8481  {
8482  iWBPaintEvent = axWBEvt[iEvent].iNext; // remove from head
8483  if(iWBPaintEvent < 0)
8484  {
8485  iWBPaintLast = -1;
8486  }
8487  }
8488  else
8489  {
8490  axWBEvt[iPrev].iNext = axWBEvt[iEvent].iNext; // remove from chain
8491  if(axWBEvt[iPrev].iNext < 0)
8492  {
8493  iWBPaintLast = iPrev;
8494  }
8495  }
8496 
8497  iTemp = iEvent;
8498 
8499  // add 'iEvent' to the chain of free events
8500  iEvent = axWBEvt[iTemp].iNext;
8501  axWBEvt[iTemp].iNext = iWBFreeEvent;
8502  iWBFreeEvent = iTemp;
8503  }
8504  else
8505  {
8506  iPrev = iEvent;
8507  iEvent = axWBEvt[iEvent].iNext;
8508  }
8509  }
8510 
8511 // do XSync and exit
8512 
8513  XSync(pDisp, 0);
8514 
8515  // now the X 'expose' events (remove them from the queue)
8516  while(XCheckWindowEvent(pDisp, wID, EVENT_ALL_MASK, &event))
8517  ;
8518 
8519 // XSync(pDisp);
8520 //
8521 // // the X events again
8522 // while(XCheckWindowEvent(pDisp, wID, EVENT_ALL_MASK, &event))
8523 // ;
8524 }
8525 
8526 static int __WBInsertPriorityEvent(Display *pDisp, Window wID, XEvent *pEvent)
8527 {
8528  int iEvent = -1;
8529  // TODO: synchronization objects?
8530 
8531  if(!iInitEventFlag)
8532  {
8533  __WBInitEvent();
8534  }
8535 
8536  if(iWBFreeEvent < 0) // none left
8537  {
8538  return -1;
8539  }
8540 
8541 // TODO: do I do something different for Expose events?
8542 // if(pEvent->type == Expose)
8543 // WBProcessExposeEvent((XExposeEvent *)pEvent);
8544 
8545  iEvent = iWBFreeEvent;
8546  iWBFreeEvent = axWBEvt[iEvent].iNext;
8547  axWBEvt[iEvent].iNext = -1; // the end
8548 
8549  // add to iWBQueuedEvent linked list
8550 
8551  if(iWBQueuedEvent < 0)
8552  {
8553  iWBQueuedEvent = iEvent;
8554  iWBQueueLast = iEvent;
8555  }
8556  else
8557  {
8558  int iTempEvent = iWBQueuedEvent;
8559 
8560  // insert at the beginning of the chain
8561  iWBQueuedEvent = iEvent;
8562  axWBEvt[iWBQueuedEvent].iNext = iTempEvent;
8563  }
8564 
8565  axWBEvt[iEvent].pDisplay = pDisp;
8566  axWBEvt[iEvent].wID = wID;
8567  memcpy(&(axWBEvt[iEvent].xEvt), pEvent, sizeof(XEvent));
8568 
8569  return 0;
8570 }
8571 
8572 static int __WBNextPaintEvent(Display *pDisp, XEvent *pEvent, Window wID)
8573 {
8574  int iRval = iWBPaintEvent, iPrev = -1;
8575 
8576  while(iRval >= 0)
8577  {
8578  if(WB_LIKELY(axWBEvt[iRval].pDisplay == pDisp) &&
8579  (WB_LIKELY(wID == None) || axWBEvt[iRval].xEvt.xany.window == wID))
8580  {
8581  break;
8582  }
8583 
8584  iPrev = iRval;
8585  iRval = axWBEvt[iRval].iNext;
8586  }
8587 
8588  if(iRval >= 0)
8589  {
8590  WB_DEBUG_PRINT(DebugLevel_Verbose | DebugSubSystem_Window | DebugSubSystem_Event,
8591  "%s - getting an EXPOSE event for %d (%08xH)\n",
8592  __FUNCTION__,
8593  (int)axWBEvt[iRval].xEvt.xany.window,
8594  (int)axWBEvt[iRval].xEvt.xany.window);
8595 
8596  if(iRval == iWBPaintEvent)
8597  {
8598  iWBPaintEvent = axWBEvt[iRval].iNext;
8599  }
8600  else
8601  {
8602  if(iRval == iWBPaintLast)
8603  {
8604  iWBPaintLast = iPrev;
8605  axWBEvt[iPrev].iNext = -1;
8606  }
8607  else if(iPrev >= 0)
8608  {
8609  axWBEvt[iPrev].iNext = axWBEvt[iRval].iNext;
8610  }
8611  }
8612 
8613  if(iWBPaintEvent < 0)
8614  {
8615  iWBPaintLast = -1;
8616  }
8617 
8618  if(pEvent)
8619  {
8620  memcpy(pEvent, &(axWBEvt[iRval].xEvt), sizeof(XEvent));
8621  }
8622 
8623  axWBEvt[iRval].iNext = iWBFreeEvent;
8624  iWBFreeEvent = iRval;
8625  }
8626 
8627  return iRval;
8628 }
8629 
8630 
8631 static int __WBNextDisplayEvent(Display *pDisp, XEvent *pEvent)
8632 {
8633  int iRval = iWBQueuedEvent, iPrev = -1;
8634 
8635  while(WB_LIKELY(iRval >= 0))
8636  {
8637  if(WB_LIKELY(axWBEvt[iRval].pDisplay == pDisp))
8638  {
8639  break;
8640  }
8641 
8642  iPrev = iRval;
8643  iRval = axWBEvt[iRval].iNext;
8644  }
8645 
8646  if(WB_LIKELY(iRval >= 0)) // I want the "I have an event" path to be faster
8647  {
8648  if(WB_LIKELY(iRval == iWBQueuedEvent)) // TODO: is this "likely" ?
8649  {
8650  iWBQueuedEvent = axWBEvt[iRval].iNext;
8651 
8652  if(iWBQueuedEvent < 0)
8653  {
8654  iWBQueueLast = -1;
8655  }
8656  }
8657  else
8658  {
8659  if(iRval == iWBQueueLast)
8660  {
8661  iWBQueueLast = iPrev;
8662  axWBEvt[iPrev].iNext = -1;
8663  }
8664  else if(iPrev >= 0)
8665  {
8666  axWBEvt[iPrev].iNext = axWBEvt[iRval].iNext;
8667  }
8668  }
8669 
8670  WB_DEBUG_PRINT(DebugLevel_Excessive | DebugSubSystem_Window | DebugSubSystem_Event,
8671  "%s - getting %s event for %d (%08xH)\n",
8672  __FUNCTION__,
8673  WBEventName(axWBEvt[iRval].xEvt.type),
8674  (int)axWBEvt[iRval].xEvt.xany.window,
8675  (int)axWBEvt[iRval].xEvt.xany.window);
8676 
8677  if(WB_LIKELY(pEvent))
8678  {
8679  memcpy(pEvent, &(axWBEvt[iRval].xEvt), sizeof(XEvent));
8680  }
8681 
8682  axWBEvt[iRval].iNext = iWBFreeEvent;
8683  iWBFreeEvent = iRval;
8684  }
8685 
8686  // TODO: is this really "unlikely" ?
8687  if(WB_UNLIKELY(iRval < 0)) // no event found in normal queue - try paint queue
8688  {
8689  // this function is separated out so I can call it directly - see WBUpdateWindowImmediately()
8690 
8691  iRval = __WBNextPaintEvent(pDisp, pEvent, None);
8692  }
8693 
8694  return iRval >= 0;
8695 }
8696 
8697 static void WBInternalProcessExposeEvent(XExposeEvent *pEvent)
8698 {
8699 int iEvent;
8700 Window wID;
8701 Display *pDisp;
8702 WB_GEOM geom;
8703 
8704 
8705  if(!iInitEventFlag)
8706  {
8707  __WBInitEvent();
8708  }
8709 
8710  // expose events are combined whenever possible
8711  // so that I can choose to respond to them immediately, increment
8712  // the 'count' member to indicate that at least one more event is waiting
8713  // then, make sure I place an event in the 'paint' queue that encompasses
8714  // the entire invalid region. Then I make sure that the 'invalid' region
8715  // of the window contains the rectangular area specified by the Expose
8716  // event so that the final paint message will paint everything properly.
8717 
8718  wID = pEvent->window;
8719  pDisp = pEvent->display;
8720 
8721  // STEP 1: search the 'paint' queue for a matching window ID
8722 
8723  for(iEvent=iWBPaintEvent; iEvent >= 0; iEvent = axWBEvt[iEvent].iNext)
8724  {
8725  if(axWBEvt[iEvent].wID == wID && axWBEvt[iEvent].pDisplay == pDisp)
8726  {
8727  break;
8728  }
8729  }
8730 
8731  if(iEvent >= 0) // found!
8732  {
8733  // expand the expose event's rectangle to include THIS one
8734 
8735  if(axWBEvt[iEvent].xEvt.xexpose.x > pEvent->x)
8736  {
8737  axWBEvt[iEvent].xEvt.xexpose.x = pEvent->x;
8738  }
8739 
8740  if(axWBEvt[iEvent].xEvt.xexpose.y > pEvent->y)
8741  {
8742  axWBEvt[iEvent].xEvt.xexpose.y = pEvent->y;
8743  }
8744 
8745  if(axWBEvt[iEvent].xEvt.xexpose.x + axWBEvt[iEvent].xEvt.xexpose.width < pEvent->x + pEvent->width)
8746  {
8747  axWBEvt[iEvent].xEvt.xexpose.width = pEvent->x + pEvent->width - axWBEvt[iEvent].xEvt.xexpose.x;
8748  }
8749 
8750  if(axWBEvt[iEvent].xEvt.xexpose.y + axWBEvt[iEvent].xEvt.xexpose.height < pEvent->y + pEvent->height)
8751  {
8752  axWBEvt[iEvent].xEvt.xexpose.height = pEvent->y + pEvent->height - axWBEvt[iEvent].xEvt.xexpose.y;
8753  }
8754  }
8755  else
8756  {
8757  // make a copy of the event and put it into the 'paint' queue
8758  // note that if I have no room, I'll simply have to reject it
8759 
8760  if(iWBFreeEvent < 0) // I check here because an existing event might be editable
8761  {
8762  WB_ERROR_PRINT("ERROR: %s - no more free 'event queue' entries\n", __FUNCTION__);
8763  return; // can't do anything else, really
8764  }
8765  else
8766  {
8767  iEvent = iWBFreeEvent;
8768  iWBFreeEvent = axWBEvt[iWBFreeEvent].iNext; // remove 'iEvent' from the head of the free chain
8769 
8770  axWBEvt[iEvent].iNext = -1; // the end of the chain on my new "not free any more" event 'iEvent'
8771 
8772  // add to iWBPaintEvent linked list
8773 
8774  if(iWBPaintEvent < 0)
8775  {
8776  iWBPaintEvent = iEvent;
8777  iWBPaintLast = iEvent;
8778  }
8779  else
8780  {
8781  if(iWBPaintLast < 0)
8782  {
8783  // traverse the queue
8784  iWBPaintLast = iWBPaintEvent;
8785  while(axWBEvt[iWBPaintLast].iNext != -1)
8786  {
8787  iWBPaintLast = axWBEvt[iWBPaintLast].iNext;
8788  }
8789  }
8790 
8791  axWBEvt[iWBPaintLast].iNext = iEvent;
8792  iWBPaintLast = iEvent;
8793  }
8794 
8795  axWBEvt[iEvent].pDisplay = pDisp;
8796  axWBEvt[iEvent].wID = wID;
8797  memcpy(&(axWBEvt[iEvent].xEvt), pEvent, sizeof(XEvent));
8798 
8799  axWBEvt[iEvent].xEvt.xexpose.count = 0; // always make this a zero
8800  }
8801  }
8802 
8803  // make sure I invalidate the region covered by the expose event
8804 
8805  geom.x = pEvent->x;
8806  geom.y = pEvent->y;
8807  geom.width = pEvent->width;
8808  geom.height = pEvent->height;
8809  geom.border = 0;
8810 
8811 // WB_ERROR_PRINT("TEMPORARY: %s - %d,%d,%d,%d\n", __FUNCTION__, geom.x, geom.y, geom.width, geom.height);
8812 
8813  WBInvalidateGeom(wID, &geom, 0);
8814 
8815  pEvent->count++; // increment so it indicates "more to come" before I pass it along
8816 
8817  // make sure window is no longer "unmapped'
8818  // TODO: fix this so it's less of a hack??
8819  {
8820  _WINDOW_ENTRY_ *pEntry = WBGetWindowEntry(wID);
8821  if(pEntry && WB_IS_WINDOW_UNMAPPED(*pEntry))
8822  {
8823  if(WB_CHECK_SET_FOCUS_ON_MAP(*pEntry))
8824  {
8825  WBSetInputFocus(wID); // hopefully will NOT cause trouble
8826  pEntry->iWindowState = WB_WINDOW_MAPPED;
8827  }
8828  else
8829  {
8830  pEntry->iWindowState = WB_WINDOW_MAPPED;
8831  }
8832  }
8833  }
8834 
8835 }
8836 
8837 // event queue functions
8838 
8839 int WBNextEvent(Display *pDisplay, XEvent *pEvent)
8840 {
8841  return __WBNextDisplayEvent(pDisplay, pEvent);
8842 }
8843 
8844 int WBPostEvent(Window wID, XEvent *pEvent)
8845 {
8846  _WINDOW_ENTRY_ *pEntry = WBGetWindowEntry(wID);
8847 
8848  if(!pEntry)
8849  return -1;
8850 
8851 // if(pEvent->type == ClientMessage)
8852 // fprintf(stderr, "TEMPORARY: client message in WBPostEvent\n");
8853 
8854  return __WBAddEvent(pEntry->pDisplay, wID, pEvent);
8855 }
8856 
8857 void WBPostDelayedEvent(XEvent *pEvent, unsigned int nDelay)
8858 {
8859  __CreateDelayedEvent(pEvent, nDelay);
8860 }
8861 
8862 int WBPostPriorityEvent(Window wID, XEvent *pEvent)
8863 {
8864  _WINDOW_ENTRY_ *pEntry = WBGetWindowEntry(wID);
8865 
8866 // if(pEvent->type == ClientMessage)
8867 // fprintf(stderr, "TEMPORARY: client message in WBPostPriorityEvent\n");
8868 
8869  if(!pEntry)
8870  {
8871 // fprintf(stderr, "TEMPORARY: returning -1 from WBPostPriorityEvent because pEntry is NULL\n");
8872  return -1;
8873  }
8874 
8875  return __WBInsertPriorityEvent(pEntry->pDisplay, wID, pEvent);
8876 }
8877 
8878 int WBPostAppEvent(XEvent *pEvent)
8879 {
8880  Display *pDisp = pDefaultDisplay;
8881 
8882 // pEvent->xany.window = 0; // make sure
8883  if(pEvent->xany.display)
8884  {
8885  pDisp = pEvent->xany.display;
8886  }
8887 
8888  return __WBAddEvent(pDisp, 0, pEvent);
8889 }
8890 
8891 void WBPostDelayedSetFocusAppEvent(Display *pDisplay, Window wID, Window wIDFrom, unsigned int iDelay)
8892 {
8893 XClientMessageEvent evtClient;
8894 
8895  if(!pDisplay)
8896  {
8897  _WINDOW_ENTRY_ *pEntry = WBGetWindowEntry(wID);
8898 
8899  if(pEntry && pEntry->pDisplay)
8900  {
8901  pDisplay = pEntry->pDisplay;
8902  }
8903  else
8904  {
8905  pDisplay = pDefaultDisplay;
8906  }
8907  }
8908 
8909  memset(&evtClient, 0, sizeof(evtClient));
8910 
8911  evtClient.type = ClientMessage;
8912  evtClient.display = pDisplay;
8913  evtClient.window = None; // sent to application
8914  evtClient.message_type = aSET_FOCUS;
8915  evtClient.format = 32;
8916  evtClient.data.l[0] = wID; // set focus here
8917  evtClient.data.l[1] = wIDFrom; // was focus THERE (but should not be NOW)
8918 
8919  if(iDelay > 0)
8920  {
8921  WBPostDelayedEvent((XEvent *)&evtClient, iDelay);
8922  }
8923  else
8924  {
8925  WBPostAppEvent((XEvent *)&evtClient);
8926  }
8927 }
8928 
8929 
8930 
8931 #define EVENT_NAME_ENTRY(X) { X, #X }
8932 
8933 static struct
8934 {
8935  int iID;
8936  const char *szName;
8937 } aIDName[]=
8938 {
8939  EVENT_NAME_ENTRY(KeyPress),
8940  EVENT_NAME_ENTRY(KeyRelease),
8941  EVENT_NAME_ENTRY(ButtonPress),
8942  EVENT_NAME_ENTRY(ButtonRelease),
8943  EVENT_NAME_ENTRY(MotionNotify),
8944  EVENT_NAME_ENTRY(EnterNotify),
8945  EVENT_NAME_ENTRY(LeaveNotify),
8946  EVENT_NAME_ENTRY(FocusIn),
8947  EVENT_NAME_ENTRY(FocusOut),
8948  EVENT_NAME_ENTRY(KeymapNotify),
8949  EVENT_NAME_ENTRY(Expose),
8950  EVENT_NAME_ENTRY(GraphicsExpose),
8951  EVENT_NAME_ENTRY(NoExpose),
8952  EVENT_NAME_ENTRY(VisibilityNotify),
8953  EVENT_NAME_ENTRY(CreateNotify),
8954  EVENT_NAME_ENTRY(DestroyNotify),
8955  EVENT_NAME_ENTRY(UnmapNotify),
8956  EVENT_NAME_ENTRY(MapNotify),
8957  EVENT_NAME_ENTRY(MapRequest),
8958  EVENT_NAME_ENTRY(ReparentNotify),
8959  EVENT_NAME_ENTRY(ConfigureNotify),
8960  EVENT_NAME_ENTRY(ConfigureRequest),
8961  EVENT_NAME_ENTRY(GravityNotify),
8962  EVENT_NAME_ENTRY(ResizeRequest),
8963  EVENT_NAME_ENTRY(CirculateNotify),
8964  EVENT_NAME_ENTRY(CirculateRequest),
8965  EVENT_NAME_ENTRY(PropertyNotify),
8966  EVENT_NAME_ENTRY(SelectionClear),
8967  EVENT_NAME_ENTRY(SelectionRequest),
8968  EVENT_NAME_ENTRY(SelectionNotify),
8969  EVENT_NAME_ENTRY(ColormapNotify),
8970  EVENT_NAME_ENTRY(ClientMessage),
8971  EVENT_NAME_ENTRY(MappingNotify),
8972  EVENT_NAME_ENTRY(LASTEvent),
8973  { 0, NULL }
8974 };
8975 
8976 const char * WBEventName(int iEventID)
8977 {
8978  int i1;
8979  static char aTemp[32];
8980 
8981  for(i1=0; aIDName[i1].iID; i1++)
8982  {
8983  if(aIDName[i1].iID == iEventID)
8984  {
8985  return aIDName[i1].szName;
8986  }
8987  }
8988 
8989  sprintf(aTemp, "[Event %d]", iEventID);
8990  return aTemp;
8991 }
8992 
8993 #include <X11/Xproto.h>
8994 
8995 #define X_PROTO_ENTRY(X) { X, #X },
8996 
8997 struct
8998 {
8999  int iProto;
9000  const char *szProto;
9001 } aXProto[] =
9002 {
9003  X_PROTO_ENTRY(X_CreateWindow)
9004  X_PROTO_ENTRY(X_ChangeWindowAttributes)
9005  X_PROTO_ENTRY(X_GetWindowAttributes)
9006  X_PROTO_ENTRY(X_DestroyWindow)
9007  X_PROTO_ENTRY(X_DestroySubwindows)
9008  X_PROTO_ENTRY(X_ChangeSaveSet)
9009  X_PROTO_ENTRY(X_ReparentWindow)
9010  X_PROTO_ENTRY(X_MapWindow)
9011  X_PROTO_ENTRY(X_MapSubwindows)
9012  X_PROTO_ENTRY(X_UnmapWindow)
9013  X_PROTO_ENTRY(X_UnmapSubwindows)
9014  X_PROTO_ENTRY(X_ConfigureWindow)
9015  X_PROTO_ENTRY(X_CirculateWindow)
9016  X_PROTO_ENTRY(X_GetGeometry)
9017  X_PROTO_ENTRY(X_QueryTree)
9018  X_PROTO_ENTRY(X_InternAtom)
9019  X_PROTO_ENTRY(X_GetAtomName)
9020  X_PROTO_ENTRY(X_ChangeProperty)
9021  X_PROTO_ENTRY(X_DeleteProperty)
9022  X_PROTO_ENTRY(X_GetProperty)
9023  X_PROTO_ENTRY(X_ListProperties)
9024  X_PROTO_ENTRY(X_SetSelectionOwner)
9025  X_PROTO_ENTRY(X_GetSelectionOwner)
9026  X_PROTO_ENTRY(X_ConvertSelection)
9027  X_PROTO_ENTRY(X_SendEvent)
9028  X_PROTO_ENTRY(X_GrabPointer)
9029  X_PROTO_ENTRY(X_UngrabPointer)
9030  X_PROTO_ENTRY(X_GrabButton)
9031  X_PROTO_ENTRY(X_UngrabButton)
9032  X_PROTO_ENTRY(X_ChangeActivePointerGrab)
9033  X_PROTO_ENTRY(X_GrabKeyboard)
9034  X_PROTO_ENTRY(X_UngrabKeyboard)
9035  X_PROTO_ENTRY(X_GrabKey)
9036  X_PROTO_ENTRY(X_UngrabKey)
9037  X_PROTO_ENTRY(X_AllowEvents)
9038  X_PROTO_ENTRY(X_GrabServer)
9039  X_PROTO_ENTRY(X_UngrabServer)
9040  X_PROTO_ENTRY(X_QueryPointer)
9041  X_PROTO_ENTRY(X_GetMotionEvents)
9042  X_PROTO_ENTRY(X_TranslateCoords)
9043  X_PROTO_ENTRY(X_WarpPointer)
9044  X_PROTO_ENTRY(X_SetInputFocus)
9045  X_PROTO_ENTRY(X_GetInputFocus)
9046  X_PROTO_ENTRY(X_QueryKeymap)
9047  X_PROTO_ENTRY(X_OpenFont)
9048  X_PROTO_ENTRY(X_CloseFont)
9049  X_PROTO_ENTRY(X_QueryFont)
9050  X_PROTO_ENTRY(X_QueryTextExtents)
9051  X_PROTO_ENTRY(X_ListFonts)
9052  X_PROTO_ENTRY(X_ListFontsWithInfo)
9053  X_PROTO_ENTRY(X_SetFontPath)
9054  X_PROTO_ENTRY(X_GetFontPath)
9055  X_PROTO_ENTRY(X_CreatePixmap)
9056  X_PROTO_ENTRY(X_FreePixmap)
9057  X_PROTO_ENTRY(X_CreateGC)
9058  X_PROTO_ENTRY(X_ChangeGC)
9059  X_PROTO_ENTRY(X_CopyGC)
9060  X_PROTO_ENTRY(X_SetDashes)
9061  X_PROTO_ENTRY(X_SetClipRectangles)
9062  X_PROTO_ENTRY(X_FreeGC)
9063  X_PROTO_ENTRY(X_ClearArea)
9064  X_PROTO_ENTRY(X_CopyArea)
9065  X_PROTO_ENTRY(X_CopyPlane)
9066  X_PROTO_ENTRY(X_PolyPoint)
9067  X_PROTO_ENTRY(X_PolyLine)
9068  X_PROTO_ENTRY(X_PolySegment)
9069  X_PROTO_ENTRY(X_PolyRectangle)
9070  X_PROTO_ENTRY(X_PolyArc)
9071  X_PROTO_ENTRY(X_FillPoly)
9072  X_PROTO_ENTRY(X_PolyFillRectangle)
9073  X_PROTO_ENTRY(X_PolyFillArc)
9074  X_PROTO_ENTRY(X_PutImage)
9075  X_PROTO_ENTRY(X_GetImage)
9076  X_PROTO_ENTRY(X_PolyText8)
9077  X_PROTO_ENTRY(X_PolyText16)
9078  X_PROTO_ENTRY(X_ImageText8)
9079  X_PROTO_ENTRY(X_ImageText16)
9080  X_PROTO_ENTRY(X_CreateColormap)
9081  X_PROTO_ENTRY(X_FreeColormap)
9082  X_PROTO_ENTRY(X_CopyColormapAndFree)
9083  X_PROTO_ENTRY(X_InstallColormap)
9084  X_PROTO_ENTRY(X_UninstallColormap)
9085  X_PROTO_ENTRY(X_ListInstalledColormaps)
9086  X_PROTO_ENTRY(X_AllocColor)
9087  X_PROTO_ENTRY(X_AllocNamedColor)
9088  X_PROTO_ENTRY(X_AllocColorCells)
9089  X_PROTO_ENTRY(X_AllocColorPlanes)
9090  X_PROTO_ENTRY(X_FreeColors)
9091  X_PROTO_ENTRY(X_StoreColors)
9092  X_PROTO_ENTRY(X_StoreNamedColor)
9093  X_PROTO_ENTRY(X_QueryColors)
9094  X_PROTO_ENTRY(X_LookupColor)
9095  X_PROTO_ENTRY(X_CreateCursor)
9096  X_PROTO_ENTRY(X_CreateGlyphCursor)
9097  X_PROTO_ENTRY(X_FreeCursor)
9098  X_PROTO_ENTRY(X_RecolorCursor)
9099  X_PROTO_ENTRY(X_QueryBestSize)
9100  X_PROTO_ENTRY(X_QueryExtension)
9101  X_PROTO_ENTRY(X_ListExtensions)
9102  X_PROTO_ENTRY(X_ChangeKeyboardMapping)
9103  X_PROTO_ENTRY(X_GetKeyboardMapping)
9104  X_PROTO_ENTRY(X_ChangeKeyboardControl)
9105  X_PROTO_ENTRY(X_GetKeyboardControl)
9106  X_PROTO_ENTRY(X_Bell)
9107  X_PROTO_ENTRY(X_ChangePointerControl)
9108  X_PROTO_ENTRY(X_GetPointerControl)
9109  X_PROTO_ENTRY(X_SetScreenSaver)
9110  X_PROTO_ENTRY(X_GetScreenSaver)
9111  X_PROTO_ENTRY(X_ChangeHosts)
9112  X_PROTO_ENTRY(X_ListHosts)
9113  X_PROTO_ENTRY(X_SetAccessControl)
9114  X_PROTO_ENTRY(X_SetCloseDownMode)
9115  X_PROTO_ENTRY(X_KillClient)
9116  X_PROTO_ENTRY(X_RotateProperties)
9117  X_PROTO_ENTRY(X_ForceScreenSaver)
9118  X_PROTO_ENTRY(X_SetPointerMapping)
9119  X_PROTO_ENTRY(X_GetPointerMapping)
9120  X_PROTO_ENTRY(X_SetModifierMapping)
9121  X_PROTO_ENTRY(X_GetModifierMapping)
9122  X_PROTO_ENTRY(X_NoOperation)
9123  {0, 0}
9124 };
9125 
9126 
9127 static const char * __internal_event_type_string(int iEventType)
9128 {
9129  switch(iEventType)
9130  {
9131  case KeyPress:
9132  return "KeyPress";
9133  case KeyRelease:
9134  return "KeyRelease";
9135  case ButtonPress:
9136  return "ButtonPress";
9137  case ButtonRelease:
9138  return "ButtonRelease";
9139  case MotionNotify:
9140  return "MotionNotify";
9141  case EnterNotify:
9142  return "EnterNotify";
9143  case LeaveNotify:
9144  return "LeaveNotify";
9145  case FocusIn:
9146  return "FocusIn";
9147  case FocusOut:
9148  return "FocusOut";
9149  case KeymapNotify:
9150  return "KeymapNotify";
9151  case Expose:
9152  return "Expose";
9153  case GraphicsExpose:
9154  return "GraphicsExpose";
9155  case NoExpose:
9156  return "NoExpose";
9157  case VisibilityNotify:
9158  return "VisibilityNotify";
9159  case CreateNotify:
9160  return "CreateNotify";
9161  case DestroyNotify:
9162  return "DestroyNotify";
9163  case UnmapNotify:
9164  return "UnmapNotify";
9165  case MapNotify:
9166  return "MapNotify";
9167  case MapRequest:
9168  return "MapRequest";
9169  case ReparentNotify:
9170  return "ReparentNotify";
9171  case ConfigureNotify:
9172  return "ConfigureNotify";
9173  case ConfigureRequest:
9174  return "ConfigureRequest";
9175  case GravityNotify:
9176  return "GravityNotify";
9177  case ResizeRequest:
9178  return "ResizeRequest";
9179  case CirculateNotify:
9180  return "CirculateNotify";
9181  case CirculateRequest:
9182  return "CirculateRequest";
9183  case PropertyNotify:
9184  return "PropertyNotify";
9185  case SelectionClear:
9186  return "SelectionClear";
9187  case SelectionRequest:
9188  return "SelectionRequest";
9189  case SelectionNotify:
9190  return "SelectionNotify";
9191  case ColormapNotify:
9192  return "ColormapNotify";
9193  case ClientMessage:
9194  return "ClientMessage";
9195  case MappingNotify:
9196  return "MappingNotify";
9197 #ifdef GenericEvent /* this wasn't present in earlier versions of X11 */
9198  case GenericEvent:
9199  return "GenericEvent";
9200 #endif // GenericEvent
9201  }
9202  return "*Unknown Event";
9203 }
9204 
9205 
9206 void WBDebugDumpGC(Display *pDisplay, WBGC hGC)
9207 {
9208 XGCValues xgcv;
9209 
9210  if(!hGC)
9211  {
9212  WBDebugPrint("GC DUMP - NULL hGC\n");
9213  return;
9214  }
9215 
9216  bzero(&xgcv, sizeof(xgcv));
9217  memcpy(&xgcv, &(hGC->values), sizeof(xgcv));
9218 
9219  WBGetGCValues(hGC,
9220  GCFunction | GCPlaneMask | GCForeground | GCBackground | GCLineWidth | GCLineStyle | GCCapStyle
9221  | GCJoinStyle | GCFillStyle | GCFillRule | GCTile | GCStipple | GCTileStipXOrigin | GCTileStipYOrigin
9222  | GCFont | GCSubwindowMode | GCGraphicsExposures | GCClipXOrigin | GCClipYOrigin /*| GCClipMask*/
9223  | GCDashOffset /*| GCDashList*/ | GCArcMode,
9224  &xgcv);
9225 
9226  WBDebugPrint("WBGC DUMP - GC = %ld (%08lxH)\n", (long)(hGC->gc), (long)(hGC->gc));
9227  WBDebugPrint("function = %d (%08xH)\n", xgcv.function, xgcv.function);
9228  WBDebugPrint("plane_mask = %ld (%016lxH)\n", xgcv.plane_mask, xgcv.plane_mask);
9229  WBDebugPrint("foreground = %ld (%016lxH)\n", xgcv.foreground, xgcv.foreground);
9230  WBDebugPrint("background = %ld (%016lxH)\n", xgcv.background, xgcv.background);
9231  WBDebugPrint("line_width = %d (%08xH)\n", xgcv.line_width, xgcv.line_width);
9232  WBDebugPrint("line_style = %d (%08xH)\n", xgcv.line_style, xgcv.line_style);
9233  WBDebugPrint("cap_style = %d (%08xH)\n", xgcv.cap_style, xgcv.cap_style);
9234  WBDebugPrint("join_style = %d (%08xH)\n", xgcv.join_style, xgcv.join_style);
9235  WBDebugPrint("fill_style = %d (%08xH)\n", xgcv.fill_style, xgcv.fill_style);
9236  WBDebugPrint("fill_rule = %d (%08xH)\n", xgcv.fill_rule, xgcv.fill_rule);
9237  WBDebugPrint("arc_mode = %d (%08xH)\n", xgcv.arc_mode, xgcv.arc_mode);
9238  WBDebugPrint("tile = %d (%08xH)\n", (int)xgcv.tile, (int)xgcv.tile);
9239  WBDebugPrint("stipple = %d (%08xH)\n", (int)xgcv.stipple, (int)xgcv.stipple);
9240  WBDebugPrint("ts_x_origin = %d (%08xH)\n", xgcv.ts_x_origin, xgcv.ts_x_origin);
9241  WBDebugPrint("ts_y_origin = %d (%08xH)\n", xgcv.ts_y_origin, xgcv.ts_y_origin);
9242  WBDebugPrint("font = %d (%08xH)\n", (int)xgcv.font, (int)xgcv.font);
9243  WBDebugPrint("subwindow_mode = %d (%08xH)\n", xgcv.subwindow_mode, xgcv.subwindow_mode);
9244  WBDebugPrint("graphics_exposures = %d (%08xH)\n", xgcv.graphics_exposures, xgcv.graphics_exposures);
9245  WBDebugPrint("clip_x_origin = %d (%08xH)\n", xgcv.clip_x_origin, xgcv.clip_x_origin);
9246  WBDebugPrint("clip_y_origin = %d (%08xH)\n", xgcv.clip_y_origin, xgcv.clip_y_origin);
9247 // WBDebugPrint("clip_mask = %d (%08xH)\n", (int)xgcv.clip_mask, (int)xgcv.clip_mask);
9248  WBDebugPrint("dash_offset = %d (%08xH)\n", xgcv.dash_offset, xgcv.dash_offset);
9249 // WBDebugPrint("dashes = %d (%08xH)\n", xgcv.dashes, xgcv.dashes);
9250 }
9251 
9252 
9253 void WBDebugDumpEvent(XEvent *pEvent)
9254 {
9255 char *p1;
9256 
9257  WBDebugPrint("EVENT %s\n", __internal_event_type_string(pEvent->xany.type));
9258  WBDebugPrint(" serial: %ld\n", pEvent->xany.serial);
9259  WBDebugPrint(" send_event: %c\n", pEvent->xany.send_event ? 'T' : 'F');
9260  WBDebugPrint(" display: %p\n", pEvent->xany.display);
9261  WBDebugPrint(" Window: %08xH\n", (unsigned int)pEvent->xany.window);
9262 
9263  switch(pEvent->xany.type)
9264  {
9265  case ClientMessage: /* these require WBGetAtomName since the atoms may not be 'global' */
9266  p1 = WBGetAtomName(pEvent->xany.display, pEvent->xclient.message_type);
9267  WBDebugPrint(" message_type: %s\n", p1);
9268  WBFree(p1);
9269 
9270  WBDebugPrint(" format: %d\n", pEvent->xclient.format);
9271  WBDebugPrint(" data[0].l: %ld (%08lxH)\n", pEvent->xclient.data.l[0], pEvent->xclient.data.l[0]);
9272  WBDebugPrint(" data[1].l: %ld (%08lxH)\n", pEvent->xclient.data.l[1], pEvent->xclient.data.l[1]);
9273  WBDebugPrint(" data[2].l: %ld (%08lxH)\n", pEvent->xclient.data.l[2], pEvent->xclient.data.l[2]);
9274  WBDebugPrint(" data[3].l: %ld (%08lxH)\n", pEvent->xclient.data.l[3], pEvent->xclient.data.l[3]);
9275  WBDebugPrint(" data[4].l: %ld (%08lxH)\n", pEvent->xclient.data.l[4], pEvent->xclient.data.l[4]);
9276 
9277  WBDebugDump("** client event data dump **", &(pEvent->xclient.data), sizeof(pEvent->xclient.data));
9278  break;
9279 
9280 
9281  case SelectionNotify: /* note THESE always use 'XGetAtomName' */
9282  p1 = XGetAtomName(pEvent->xany.display, pEvent->xselection.selection);
9283  WBDebugPrint(" selection: %s\n", p1);
9284  XFree(p1);
9285 
9286  p1 = XGetAtomName(pEvent->xany.display, pEvent->xselection.target);
9287  WBDebugPrint(" target: %s\n", p1);
9288  XFree(p1);
9289 
9290  p1 = XGetAtomName(pEvent->xany.display, pEvent->xselection.property);
9291  WBDebugPrint(" property: %s\n", p1);
9292  XFree(p1);
9293 
9294  WBDebugPrint(" time: %ld\n", (unsigned long)pEvent->xselection.time);
9295  break;
9296 
9297 
9298  case SelectionRequest: /* note THESE always use 'XGetAtomName' */
9299  WBDebugPrint(" Requestor: %08xH\n", (unsigned int)pEvent->xselectionrequest.requestor);
9300 
9301  p1 = XGetAtomName(pEvent->xany.display, pEvent->xselectionrequest.selection);
9302  WBDebugPrint(" selection: %s\n", p1);
9303  XFree(p1);
9304 
9305  p1 = XGetAtomName(pEvent->xany.display, pEvent->xselectionrequest.target);
9306  WBDebugPrint(" target: %s\n", p1);
9307  XFree(p1);
9308 
9309  p1 = XGetAtomName(pEvent->xany.display, pEvent->xselectionrequest.property);
9310  WBDebugPrint(" property: %s\n", p1);
9311  XFree(p1);
9312 
9313  WBDebugPrint(" time: %ld\n", (unsigned long)pEvent->xselectionrequest.time);
9314  break;
9315 
9316 // default:
9317  // nothing else
9318  }
9319 }
9320 
9321 
9322 void WBDebugDumpRegion(Region hRgn, int bRotate)
9323 {
9324 XRectangle xrct;
9325 int iW, iH;
9326 
9327  XClipBox(hRgn, &xrct);
9328 
9329  WBDebugPrint("REGION %u (%08xH): clip rect: %d, %d, %d, %d bRotate=%d\n",
9330  (unsigned int)(WB_UINTPTR)hRgn,
9331  (unsigned int)(WB_UINTPTR)hRgn,
9332  xrct.x, xrct.y, xrct.width + xrct.x, xrct.height + xrct.y,
9333  bRotate);
9334 
9335  if(xrct.width == 0 || xrct.height == 0)
9336  {
9337  WBDebugPrint(" (Region is empty)\n");
9338  return;
9339  }
9340 
9341  if(bRotate)
9342  {
9343  iW = xrct.x;
9344  xrct.x = xrct.y;
9345  xrct.y = iW;
9346 
9347  iW = xrct.width;
9348  xrct.width = xrct.height;
9349  xrct.height = iW;
9350  }
9351 
9352  WBDebugPrint(" ");
9353  for(iW=xrct.x; iW < xrct.x + xrct.width; iW++)
9354  {
9355  WBDebugPrint("%d", (iW / 100) % 10);
9356  }
9357 
9358  WBDebugPrint("\n ");
9359 
9360  for(iW=xrct.x; iW < xrct.x + xrct.width; iW++)
9361  {
9362  WBDebugPrint("%d", (iW / 10) % 10);
9363  }
9364 
9365  WBDebugPrint("\n ");
9366 
9367  for(iW=xrct.x; iW < xrct.x + xrct.width; iW++)
9368  {
9369  WBDebugPrint("%d", iW % 10);
9370  }
9371 
9372  WBDebugPrint("\n ");
9373 
9374  for(iW=xrct.x; iW < xrct.x + xrct.width; iW++)
9375  {
9376  WBDebugPrint("-");
9377  }
9378 
9379  WBDebugPrint("+\n");
9380 
9381 
9382  for(iH=xrct.y; iH < xrct.y + xrct.height; iH++)
9383  {
9384  WBDebugPrint("%5d|", iH);
9385  for(iW=xrct.x; iW < xrct.x + xrct.width; iW++)
9386  {
9387  if((!bRotate && XPointInRegion(hRgn, iW, iH)) ||
9388  (bRotate && XPointInRegion(hRgn, iH, iW)))
9389  {
9390  WBDebugPrint(".");
9391  }
9392  else
9393  {
9394  WBDebugPrint(" ");
9395  }
9396  }
9397  WBDebugPrint("|\r\n");
9398  }
9399 
9400  WBDebugPrint(" ");
9401 
9402  for(iW=xrct.x; iW < xrct.x + xrct.width; iW++)
9403  {
9404  WBDebugPrint("=");
9405  }
9406 
9407  WBDebugPrint("+\n");
9408 
9409 }
9410 
9411 
9412 // ****************************************************
9413 //
9414 // E R R O R H A N D L I N G
9415 //
9416 // ****************************************************
9417 
9418 static int WBXErrorHandler(Display *pDisplay, XErrorEvent *pError)
9419 {
9420 char tbuf[512];
9421 int i1;
9422 _WINDOW_ENTRY_ *pEntry;
9423 
9424 
9425  // if the error results from an expected condition, just set the
9426  // appropriate error information in the exception buffer and return
9427  // For now this will be a manual check after a function call but
9428  // later I may enable structured exception handling or a signal
9429  // 'SIGUSR1' might work for this...
9430 
9431  // cacheing the latest error information
9432  xErrorInfo.pDisplay = pDisplay;
9433  xErrorInfo.serial = pError->serial;
9434  xErrorInfo.pFunc = sz_xcall_func;
9435  xErrorInfo.iLine = i_xcall_line;
9436  xErrorInfo.error_code = pError->error_code;
9437  xErrorInfo.request_code = pError->request_code;
9438  xErrorInfo.minor_code = pError->minor_code;
9439  xErrorInfo.resourceid = pError->resourceid;
9440 
9441  if(bIgnoreXErrors)
9442  {
9443  return 1;
9444  }
9445 
9446 
9447  WB_ERROR_PRINT("%s","************************************************************************\n");
9448 
9449  // NOTE: pError->type is always zero - replies are always 1 - these are reserved values, see X11/X.h
9450 
9451  WB_ERROR_PRINT("window_helper.c:%d %s\n display %pH serial %08lxH\n",
9452  __LINE__, __FUNCTION__, pDisplay, pError->serial);
9453  if(sz_xcall_func)
9454  {
9455  WB_ERROR_PRINT(" called from %s line %d\n", sz_xcall_func, i_xcall_line);
9456  }
9457 // else
9458 // {
9459 // WB_ERROR_PRINT("called from unknown function\n");
9460 // }
9461 
9462  XGetErrorText(pDisplay, pError->error_code, tbuf, sizeof(tbuf));
9463  WB_ERROR_PRINT(" Error Code: %d %s\n", pError->error_code, tbuf);
9464 
9465  for(i1=0; aXProto[i1].szProto; i1++)
9466  {
9467  if(aXProto[i1].iProto == pError->request_code)
9468  {
9469  WB_ERROR_PRINT(" Request Code: %s\n", aXProto[i1].szProto);
9470  break;
9471  }
9472  }
9473 
9474  if(!aXProto[i1].szProto)
9475  {
9476  WB_ERROR_PRINT(" Request Code: UNKNOWN (%d, %08xH)\n", pError->request_code, pError->request_code);
9477  }
9478 
9479  WB_ERROR_PRINT(" Minor Request Code: %d (%08xH)\n", pError->minor_code, pError->minor_code);
9480 
9481  if(pError->error_code == BadWindow ||
9482  ((pError->error_code == BadMatch || pError->error_code == BadDrawable) &&
9483  (pError->request_code == X_SetInputFocus ||
9484  pError->request_code == X_GetGeometry)))
9485  {
9486  pEntry = WBGetWindowEntry((Window)pError->resourceid);
9487 
9488  if(pEntry)
9489  {
9490 #ifndef NO_DEBUG
9491 
9492  const char *pState;
9493 
9494  switch(pEntry->iWindowState)
9495  {
9496  case WB_WINDOW_DELETE:
9497  pState = "WB_WINDOW_DELETE";
9498  break;
9499  case WB_WINDOW_DESTROYED:
9500  pState = "WB_WINDOW_DESTROYED";
9501  break;
9502  case WB_WINDOW_UNMAPPED:
9503  pState = "WB_WINDOW_UNMAPPED";
9504  break;
9505  case WB_WINDOW_MAPPED:
9506  pState = "WB_WINDOW_MAPPED";
9507  break;
9508  case WB_WINDOW_SET_FOCUS_ON_MAP:
9509  pState = "WB_WINDOW_SET_FOCUS_ON_MAP";
9510  break;
9511  default:
9512  pState = "UNKNOWN STATE";
9513  }
9514 
9515  WB_ERROR_PRINT(" X Resource ID: %d (%08xH) state=%d %s\n",
9516  (int)pError->resourceid, (int)pError->resourceid,
9517  pEntry->iWindowState, pState);
9518 
9519  if(pEntry->szClassName)
9520  {
9521  WB_ERROR_PRINT(" Window 'class name': %s\n", pEntry->szClassName);
9522  }
9523 
9524 #endif // NO_DEBUG
9525 
9526  while(pEntry && pEntry->wParent != None)
9527  {
9528  Window wID = pEntry->wParent;
9529  pEntry = WBGetWindowEntry(wID);
9530 
9531  if(!pEntry)
9532  {
9533  WB_ERROR_PRINT(" Parent window: %d (%08xH) (no entry)\n",
9534  (int)wID, (int)wID);
9535  }
9536  else
9537  {
9538 #ifndef NO_DEBUG
9539 
9540  switch(pEntry->iWindowState)
9541  {
9542  case WB_WINDOW_DELETE:
9543  pState = "WB_WINDOW_DELETE";
9544  break;
9545  case WB_WINDOW_DESTROYED:
9546  pState = "WB_WINDOW_DESTROYED";
9547  break;
9548  case WB_WINDOW_UNMAPPED:
9549  pState = "WB_WINDOW_UNMAPPED";
9550  break;
9551  case WB_WINDOW_MAPPED:
9552  pState = "WB_WINDOW_MAPPED";
9553  break;
9554  case WB_WINDOW_SET_FOCUS_ON_MAP:
9555  pState = "WB_WINDOW_SET_FOCUS_ON_MAP";
9556  break;
9557  default:
9558  pState = "UNKNOWN STATE";
9559  }
9560 
9561  WB_ERROR_PRINT(" Parent window: %d (%08xH) state=%d %s\n",
9562  (int)wID, (int)wID,
9563  pEntry->iWindowState, pState);
9564 
9565  if(pEntry->szClassName)
9566  {
9567  WB_ERROR_PRINT(" Parent Window 'class name': %s\n", pEntry->szClassName);
9568  }
9569 
9570 #endif // NO_DEBUG
9571  }
9572  }
9573  }
9574  else
9575  {
9576  WB_ERROR_PRINT(" X Resource ID: %d (%08xH) (no window entry)\n", (int)pError->resourceid, (int)pError->resourceid);
9577  }
9578  }
9579  else
9580  {
9581  WB_ERROR_PRINT(" X Resource ID: %d (%08xH)\n", (int)pError->resourceid, (int)pError->resourceid);
9582  }
9583 
9584  WB_ERROR_PRINT("%s","************************************************************************\n");
9585  return 1;
9586 }
9587 
9588 
9589 void WBErrorClear(void)
9590 {
9591  memset(&xErrorInfo, 0, sizeof(xErrorInfo));
9592 }
9593 
9594 int WBErrorCheck(void)
9595 {
9596  return xErrorInfo.pDisplay != NULL;
9597 }
9598 
9600 {
9601  if(!xErrorInfo.pDisplay)
9602  {
9603  return 0;
9604  }
9605 
9606  // check for possibility of retry
9607 
9608  return xErrorInfo.error_code == BadAlloc; // for now this is the only retryable one
9609 }
9610 
9612 {
9613  return &xErrorInfo;
9614 }
9615 
9616 
9617 
9618 // certain C compilers might not handle inlines well. So I define function bodies for them here
9619 
9620 #if !defined(__DOXYGEN__) && !defined(__GNUC__) && !defined(_MSVC_VER)
9621 
9622 Display * WBGetDefaultDisplay(void)
9623 {
9624  return pDefaultDisplay;
9625 }
9626 
9627 void WBInvalidateRect(Window wID, const WB_RECT *pRCT, int bPaintFlag)
9628 {
9629  WB_GEOM geom;
9630 
9631  if(!pRCT)
9632  {
9633  WBInvalidateGeom(wID, NULL, bPaintFlag);
9634  return;
9635  }
9636 
9637  geom.x = pRCT->left;
9638  geom.y = pRCT->top;
9639  geom.width = pRCT->right - pRCT->left;
9640  geom.height = pRCT->bottom - pRCT->top;
9641  geom.border = 0;
9642 
9643  WBInvalidateGeom(wID, &geom, bPaintFlag);
9644 }
9645 
9646 void WBValidateRect(Window wID, WB_RECT *pRCT)
9647 {
9648  WB_GEOM geom;
9649 
9650  if(!pRCT)
9651  {
9652  WBValidateGeom(wID, NULL);
9653  return;
9654  }
9655 
9656  geom.x = pRCT->left;
9657  geom.y = pRCT->top;
9658  geom.width = pRCT->right - pRCT->left;
9659  geom.height = pRCT->bottom - pRCT->top;
9660  geom.border = 0;
9661 
9662  WBValidateGeom(wID, &geom);
9663 }
9664 
9665 void WBSupressErrorOutput(void)
9666 {
9667 extern int bIgnoreXErrors;
9668 
9669  // TODO: serialize this with a mutex or other sync object?
9670  bIgnoreXErrors++;
9671 }
9672 
9673 void WBAllowErrorOutput(void)
9674 {
9675 extern int bIgnoreXErrors;
9676 
9677  // TODO: serialize this with a mutex or other sync object?
9678  if(bIgnoreXErrors > 0)
9679  {
9680  bIgnoreXErrors--;
9681  }
9682  else
9683  {
9684  bIgnoreXErrors = 0;
9685  }
9686 }
9687 
9688 #endif // !defined(__DOXYGEN__) && !defined(__GNUC__) && !defined(_MSVC_VER)
9689 
9690 
9691 
GC gc
the associated 'GC'
#define WB_LIKELY(x)
optimization for code branching when condition is 'likely'. use within conditionals
int WBPointInWindow(Window wIDRef, int iX, int iY, Window wIDQuery)
Returna a non-zero value if X,Y coordinates relative to the reference window are within the query win...
Core (internal) structure for storing and dispatching events.
void WBSetWMProperties(Window wID, const char *szTitle, XSizeHints *pNormalHints, XWMHints *pWMHints, XClassHint *pClassHints)
assign standard WM (Window Manager) properties via XSetWMProperties
void WBPlatformOnExit(void)
Resource 'free-up' on exit (must call)
int border
cached border value from latest 'Expose' event (TODO: ConfigureNotify?)
Atom aPRIMARY
PRIMARY Atom for the clipboard - uses XA_PRIMARY.
unsigned long clrFG
default foreground color (also assigned to GC)
Pixmap PXM_GetIconPixmap(int idIcon, XPM_ATTRIBUTES *pAttr, Pixmap *pMask)
Create Icon pixmap pair using pre-defined resource ID.
unsigned long lTimeInterval
interval (or zero for one-shot timer)
enum WMPropertiesWMProtocols eWMProtocols
a combination of WMPropertiesWMProtocols values, indicating supported protocols (default None)
int CHGetDoubleClickDistance(Display *pDisplay)
returns default double click distance (from X settings)
Definition: conf_help.c:2241
Atom aMENU_UI_COMMAND
UI notifications sent by menus to owning Frame windows via ClientMessage using 'WBWindowDispatch'.
Display * pDisplay
display associated with timer
static __inline__ void WBValidateRect(Window wID, WB_RECT *pRCT)
'Paint' helper, validates a WB_RECT for asynchronous Expose event generation
Structure definition for X11 error information.
Atom aCOMPOUND_TEXT
COMPOUND_TEXT Atom for the clipboard.
#define WB_POINTER_DBLCLICK
WB_POINTER 'double-click' event, send in lieu of WB_POINTER_CLICK for double-click.
void * aWindowData[WINDOW_DATA_SIZE]
4 void pointers, to be uased as needed for 'window data'
'window helper' main header file for the X11workbench Toolkit API
Atom aSECONDARY
SECONDARY Atom for the clipboard - uses XA_SECONDARY.
#define WB_POINTER_CLICK
Mouse 'click' event.
void WBAddMenuWindow(Window wID, Window wIDMenu)
Add a MENU WINDOW to a (frame) window.
void WBFreeFont(Display *pDisplay, WB_FONT pFont)
free a WB_FONT that was created using one of the WBFont APIs
Definition: font_helper.c:250
int bQuitFlag
'Quit' Flag - you should check this periodically in your main (message) loop and exit whenever it is ...
unsigned long clrBG
default background color (also assigned to GC)
void WBClearWindow(Window wID, WBGC gc)
'Paint' helper, erases background by painting the background color within the clipping region
#define EVENT_ALL_MASK
An event mask for ALL events, with bits 0 through 24 set - see X.h which only defines bits 0 to 24 fo...
void __internal_font_helper_exit(void)
un-initialization for font helper - call once at end of program (WBExit() does this for you)
Definition: font_helper.c:145
#define WB_DEBUG_PRINT(L,...)
Preferred method of implementing conditional debug output.
Definition: debug_helper.h:364
void * WBGetWindowData(Window wID, int iIndex)
Gets the data associated with this window and the specified index.
#define WB_POINTER_DROP
WB_POINTER 'drop' event, only sent if drag/drop supported AND was not 'canceled'; see WB_POINTER_CANC...
void WBUnregisterAppCallback()
unregister callback function for application events
void WBDefaultStandardColormap(Display *pDisplay, XStandardColormap *pMap)
returns a default XStandardColormap structure for the default screen of the specified display
static __inline__ int WBCheckDebugLevel(WB_UINT64 dwLevel)
Check specified debug level against the value returned by WBGetDebugLevel() and return non-zero for a...
Definition: debug_helper.h:300
int WBKeyEventProcessKey(const XKeyEvent *pEvent, char *pBuf, int *pcbLen, int *piAltCtrlShift)
Generic keyboard event translation utility.
Atom aMULTIPLE
MULTIPLE Atom for the clipboard.
int WBUnmapWindow(Display *pDisplay, Window wID)
wrapper for XUnmapWindow, makes window invisible without destroying it
void WBPostDelayedEvent(XEvent *pEvent, unsigned int nDelay)
Delays placing a copy of the specified event at the end of the regular (internal) event queue by a sp...
Display * WBThreadInitDisplay(void)
initializes default Display for a thread (must call WBInit() first)
void WBRestoreDefaultCursor(Window wID)
restore the default cursor
WB_GEOM geomAbsolute
absolute window geometry (from notification)
int iNext
index for 'next' in a linked list
void WBExit()
deletes any remaining global objects, frees the Display pointer, and terminates event processing
static __inline__ void WBSupressErrorOutput(void)
Supress X11 XErrorEvent output to stderr.
XImage * WBGetWindowImage(Display *pDisplay, Window wID)
Obtain an XImage for the entire window.
void WBCreateWindowDefaultGC(Window wID, unsigned long clrFG, unsigned long clrBG)
creates a default WBGC for a window
void WBWaitForEvent(Display *pDisplay)
Wait for an event, blocking indefinitely.
void WBGetWindowRect(Window wID, WB_RECT *pRect)
Returns the WB_RECT (rectangle) defined by the window's geometry, including the border area.
#define WB_POINTER_SCROLLDOWN
WB_POINTER 'scroll down' event, caused by mouse button 5.
int(* pCallback)(Window wIDEvent, XEvent *pEvent)
Pointer to the window's event callback function.
void WBInitWindowAttributes(XSetWindowAttributes *pXSWA, unsigned long lBorderPixel, unsigned long lBackgroundPixel, Colormap clrMap, int iBitGravity)
initializes the XSetWIndowAttributes structure with minimal attributes
Display * WBInit(const char *szDisplayName)
initializes default objects for the specified Display (required)
int iModalFlag
current modal state (0 for modeless) (see WBShowModal)
struct s_TIMER_ENTRY TIMER_ENTRY
Core (internal) structure for managing timers.
int WBCheckGetEvent(Display *pDisplay, XEvent *pEvent)
Main message loop, high level API to check for and retrieve the next event.
int WBNextEvent(Display *pDisplay, XEvent *pEvent)
low-level event queue wrapper. Implements the client-side event queue. Does not block if no events av...
void WBSetWindowClassName(Window wID, const char *szClassName)
Assignes the window's class name pointer.
void WBSetParentWindow(Window wID, Window wIDParent)
Assigns the parent to the specified window within the internal structure.
#define WB_KEYEVENT_ALT
'AltCtrlShift' bit flag for ALT modifier for WBKeyEventProcessKey()
void CHOnExit(void)
frees resources used by Configuration 'helper' functions
Definition: conf_help.c:166
int __StartInitClipboardSystem(Display *pDisplay, const char *szDisplayName)
initializes clipboard sub-system
WB_FONT WBCopyFont(Display *pDisplay, WB_FONTC pOldFont)
make a copy of an existing font (best when assigning to a window)
Definition: font_helper.c:168
int idCursor
last cursor ID used by 'WBSetCursor()'
int WBShowModal(Window wID, int bMenuSplashFlag)
Shows a 'modal' window by processing events until the window closes.
Window WBLocateWindow(WBLocateWindowCallback callback, void *pData)
Locate a window by enumerating with a callback function.
void WBGetWindowGeom0(Window wID, WB_GEOM *pGeom)
Returns the ABSOLUTE window geometry relative to the screen.
char padding[256 - sizeof(Display *) - sizeof(Window) - sizeof(int) - sizeof(XEvent)]
alignment padding to make the struct exactly 256 bytes in length
unsigned int border
Window wIDMenu
window ID for attached menu window
unsigned int width
Atom aINCR
INCR Atom for the clipboard.
unsigned long serial
serial number from XErrorEvent
const char * GetStartupDisplayName(void)
returns character name of the display to be opened and passed to WBInit
Pixmap pxMask
icon mask pixmap (may be None)
int WBWindowDispatch(Window wID, XEvent *pEvent)
Dispatches a window XEvent. May be called directly.
void WBDebugDump(const char *szTitle, void *pData, int cbData)
conditionally dumps binary data to debug message output
int width
cached width value from latest 'Expose' event
void GetStartupGeometry(WB_GEOM *pGeom)
returns the startup geometry based on command line parameters
const char * WBEventName(int iEventID)
debug function to return the name of an X11 event
static __inline__ void WBAllowErrorOutput(void)
Restore X11 XErrorEvent output to stderr.
int WBCopyIntoWindowImage(Display *pDisplay, Window wID, XImage *pSrcImage, int xSrc, int ySrc, int width, int height, int xOffs, int yOffs)
Copy an XImage into the cached XImage for the entire window.
int WBXPutImage(Display *pDisplay, Drawable dw, WBGC gc, XImage *pImage, int src_x, int src_y, int dest_x, int dest_y, unsigned int width, unsigned int height)
Write contents of an XImage onto a Drawable.
struct s_EVENT_ENTRY EVENT_ENTRY
Core (internal) structure for storing and dispatching events.
void PXM_OnExit(void)
Frees resources allocated by Pixmap utility functions.
Atom aWM_DELETE_WINDOW
Delete Window notification event.
int iWindowState
indicates if window mapped
Atom aWM_PROTOCOLS
WM supported protocols.
void WBUpdateWindowImmediately(Window wID)
'Paint' helper, generates an immediate Expose event for non-empty 'invalid' region
int WBPostPriorityEvent(Window wID, XEvent *pEvent)
Places a copy of the specified event at the end of the priority (internal) event queue.
#define WINDOW_DATA_SIZE
The 'window data' array size (currently 4 void pointers)
Atom aWB_CHAR
keystroke/character notifications generated by API
void WBGetClientRect(Window wID, WB_RECT *pRect)
Returns the WB_RECT (rectangle) defined by the window's geometry, excluding the border area.
static __inline__ Display * WBGetDefaultDisplay(void)
Returns the default Display.
internal wrapper struct for X11 'geometry' definition
'configuration helper' main header file for the X11 Work Bench Toolkit API
Display * pDisplay
the Display pointer for the event
Region WBCopyRegion(Region rgnSource)
Simple utility to copy a region.
Atom aRECALC_LAYOUT
notify window that it should re-calculate things like scrollbars and viewports
Window WBGetApplicationWindow(void)
Get the main 'Application' window.
struct s_DELAYED_EVENT_ENTRY * pNext
linked lists for performance
Atom aDLG_FOCUS
dialog focus messages
Window wID
window to receive timer event
Atom aTIMESTAMP
TIMESTAMP Atom for the clipboard.
Core (internal) structure for managing timers.
void WBInvalidateRegion(Window wID, Region rgn, int bPaintFlag)
'Paint' helper, invalidates a region for asynchronous Expose event generation
int WBSetForeground(WBGC hGC, unsigned long foreground)
Assign foreground color, a wrapper for XSetForeground()
WB_UINT64 lTimeIndex
time index for which this timer next expires
Atom aDRAWABLE
DRAWABLE Atom for the clipboard - uses XA_DRAWABLE.
int WBAssignWindowImage(Display *pDisplay, Window wID, XImage *pImage)
Assign an XImage for the entire window.
Atom aWB_POINTER
pointer click/double-click/drag notifications generated by API
WB_UINT64 lTimeIndex
time index for which this timer next expires
force fixed pitch
Definition: font_helper.h:481
XEvent event
copy of delayed event
WMPropertiesWindowType
Window type enumeration. Reserved for future implementation.
WB_FONT WBLoadFont(Display *pDisplay, const char *szFontName, int iFontSize, int iFlags)
load a WB_FONT font object based on a font name, size, and font flags
Definition: font_helper.c:291
do not use a 'backing store' when drawing the window (native X11 calls)
WB_FONTC WBQueryWindowFont(Window wID)
Returns the WB_FONT assigned to the window (may be NULL), not a copy.
void WBRegisterWindowCallback(Window wID, WBWinEvent pCallback)
register callback function for a window (required)
Atom aMANAGER
MANAGER Atom for the clipboard.
Atom aMENU_COMMAND
commands sent by menus via ClientMessage
int(* WBAppEvent)(XEvent *pEvent)
event callback function type definition for application events
Atom aPIXEL
PIXEL Atom for the clipboard.
void DeleteTimer(Display *pDisplay, Window wID, long lID)
Deletes an existing timer's resources.
long lID
timer identifier
WB_UINT64 WBGetTimeIndex(void)
Returns the current 'time index' (in microseconds)
Atom aSCROLL_NOTIFY
void WBInvalidateGeom(Window wID, const WB_GEOM *pGeom, int bPaintNow)
'Paint' helper, invalidates a geometry for asynchronous Expose event generation
void WBSetWindowCursor(Window wID, int idCursor)
immediately set the window cursor
void WBSetWMPropertiesWindowType(Window wID, enum WMPropertiesWindowType wmProp)
assign standard WM (Window Manager) 'window type' properties BEFORE mapping it (reserved)
Region WBGetPaintRegion(Window wID)
'Paint' helper, returns a copy of the current 'paint' region for the window
Region WBRectToRegion(const WB_RECT *pRect)
'Paint' helper, converts a WB_RECT structure to a Region.
unsigned long WBGetWindowFGColor(Window wID)
Returns the currently assigned foreground color.
void WBSetApplicationWindow(Window wID)
Assign the main 'Appklication' window.
WB_FONT WBGetWindowFont(Window wID)
Returns a copy of the current WB_FONT assigned to the window (may be NULL)
void WBGetWindowGeom2(Window wID, WB_GEOM *pGeom)
Returns the geometry of the window relative to the root window.
struct s_DELAYED_EVENT_ENTRY DELAYED_EVENT_ENTRY
Core (internal) structure for managing delayed events.
Atom aWINDOW
WINDOW Atom for the clipboard - uses XA_WINDOW.
Atom aAVERAGE_WIDTH
Atoms for fonts - Average Character Width.
void * WBAlloc(int nSize)
High performance memory sub-allocator 'allocate'.
const char * szClassName
window 'class name', mostly for debug and tracing, points to NULL or persistent name string
Window WBGetParentWindow(Window wID)
Returns the window's parent (or None if there is no parent)
#define END_XCALL_DEBUG_WRAPPER
wrapper macro for calls into the X11 library. This macro follows the call(s)
#define WB_POINTER_SCROLLUP
WB_POINTER 'scroll up' event, caused by mouse button 4.
Pixmap pxIcon
icon pixmap (may be None)
unsigned long long WB_UINT64
Platform abstract unsigned 64-bit integer.
int height
cached height value from latest 'Expose' event (TODO: ConfigureNotify?)
Region rgnClip
complex clip (aka 'invalid') region (0 implies 'none')
void WBProcessExposeEvent(XExposeEvent *pEvent)
low-level event processing, internal handling of Expose events
int WBPostAppEvent(XEvent *pEvent)
Places a copy of the specified event at the end of the priority (internal) event queue.
void WBValidateGeom(Window wID, const WB_GEOM *pGeom)
'Paint' helper, validates a geometry for asynchronous Expose event generation
int WBSetRegion(WBGC hGC, Region rgnClip)
Assign clipping region, wrapper for XSetRegion()
Region WBGetInvalidRegion(Window wID)
'Paint' helper, returns a copy of the invalid region for a window
int WBIsMapped(Display *pDisplay, Window wID)
Returns non-zero if window has been mapped; zero otherwise.
Window wParent
cached window parent ID
Window WBGetMenuWindow(Window wID)
Returns the Window ID of the (first) menu window assigned to a (frame) window.
WBGC WBCopyGC(WBGC hGCOrig)
makes a copy of a WBGC, a more sensible wrapper for XCopyGC()
#define WB_ERROR_PRINT(...)
Preferred method of implementing an 'error level' debug message for all subsystems.
Definition: debug_helper.h:474
WB_UINT64 iFlags
'iFlags' specified when window was created (for reference)
unsigned long WBGetWindowBGColor(Window wID)
Returns the currently assigned background color.
int WBAppDispatch(XEvent *pEvent)
Dispatches an application XEvent. May be called directly.
Window WBGetHiddenHelperWindow(void)
Returns a special 'hidden' window used for information purposes.
void WBFree(void *pBuf)
High performance memory sub-allocator 'free'.
static __inline__ void WBInvalidateRect(Window wID, const WB_RECT *pRCT, int bPaintFlag)
'Paint' helper, invalidates a WB_RECT for asynchronous Expose event generation
int WBPostEvent(Window wID, XEvent *pEvent)
Places a copy of the specified event at the end of the regular (internal) event queue.
WBGC hGC
default graphics context
int WBMapRaised(Display *pDisplay, Window wID)
wrapper for XMapRaised, makes window visible and moves to top
void WBDestroyWindow(Window wID)
Destroy a window.
XImage * pImage
locally cached XImage for the window
int CHGetDoubleClickTime(Display *pDisplay)
returns default double click time (from X settings)
Definition: conf_help.c:2202
#define WB_POINTER_CANCEL
WB_POINTER 'cancel' event, cancels an ongoing operation, such as drag/drop (useful for resource clean...
#define WB_KEYEVENT_SHIFT
'AltCtrlShift' bit flag for Shift modifier for WBKeyEventProcessKey()
void WBDelay(uint32_t uiDelay)
Delay for a specified period in microseconds.
XGCValues values
cached XGCValues for the GC
WBGC WBCreateGC(Display *pDisplay, Drawable dw, unsigned long valuemask, const XGCValues *values)
Creates a WBGC, wrapper for XCreateGC()
#define WB_DEFAULT_CURSOR
The default window cursor (this is what xterm uses)
void WBSetWindowFont(Window wID, WB_FONTC pFont)
assigns the default WB_FONT object for a window
int iLine
Line number of the function as assigned by BEGIN_XCALL_DEBUG_WRAPPER.
Window wID
the window for which the event is queued
XWMHints * pWMHints
XWMHints structure (cached)
void WBPlatformOnInit(void)
Resource initialization on startup.
void WBXlatCoordPoint(Window wIDSrc, int iXSrc, int iYSrc, Window wIDDest, int *piXDest, int *piYDest)
Translate X,Y point coordinates relative to a window.
void WBUpdateWindow(Window wID)
'Paint' helper, generates an asynchronous Expose event for non-empty 'invalid' region
void WBSetWindowIcon(Window wID, int idIcon)
assigns an icon resource (by ID) to a window
force sans-serif
Definition: font_helper.h:487
#define WB_POINTER_MOVE
WB_POINTER 'move' event, for motion notification during drag/drop.
#define WB_POINTER_DRAG
WB_POINTER 'drag' event, window proc MUST return the window ID to auto-support drag/drop.
Window WBCreateWindow(Display *pDisplay, Window wIDParent, WBWinEvent pProc, const char *szClass, int iX, int iY, int iWidth, int iHeight, int iBorder, int iIO, WB_UINT64 iFlags, XSetWindowAttributes *pXSWA)
Create a window.
unsigned long long WB_UINTPTR
Platform abstract unsigned integer that matches pointer size.
Atom aCLIPBOARD
CLIPBOARD Atom for the clipboard.
WBGC WBGetWindowDefaultGC(Window wID)
Returns the default WBGC currently assigned to the window (not a copy)
int WBIsChildWindow(Window wIDParent, Window wIDChild)
Returns non-zero if wIDParent is in a parent relationsihp with wIDChild.
int WBInitDisplay(Display *pDisplay)
initializes default objects for the specified Display
Atom WBGetAtom(Display *pDisplay, const char *szAtomName)
Lookup and/or allocate an internal Atom for a named string (lookups include X11 atoms)
int WBGetWindowDefaultCursor(Window wID)
returns the default cursor ID for a window
void WBDispatch(XEvent *pEvent)
Generic Event Dispatcher, using message type to dispatch.
void WBInitSizeHints(XSizeHints *pSH, Display *pDisplay, int iMinHeight, int iMinWidth)
initializes the XSizeHints structure with standard attributes
Window wID
window to which the structure is mapped
int WBIsValid(Display *pDisplay, Window wID)
returns non-zero if 'valid' (i.e. 'not destroyed')
#define WB_WAIT_CURSOR
The 'wait' cursor (this is what xterm uses)
void WBDebugDumpEvent(XEvent *pEvent)
dumps the contents of an XEvent
void WBUnregisterWindowCallback(Window wID)
un-register the window's callback function (implies resource destruction)
void WBErrorClear(void)
Clear the 'last error' information obtained via WBGetLastError()
const char * sz_xcall_func
debug helper variable tracking the function calling into the X11 library
Definition: window_helper.c:98
XImage * WBXGetImage(Display *pDisplay, Drawable dw, int x, int y, unsigned int width, unsigned int height, unsigned long plane_mask, int format)
Read contents of a Drawable onto an XImage.
int iModalReturn
return value for a modal window (see WBShowModal)
Region rgnPaint
rectangular paint region (0 implies 'none')
Display * pDisplay
display associated with this window
WBGC WBBeginPaint(Window wID, XExposeEvent *pEvent, WB_GEOM *pgBounds)
'Paint' helper, creates a WBGC for use in updating the window in an Expose event handler
int(* WBWinEvent)(Window wID, XEvent *pEvent)
event callback function type for window events
WBGC WBGetWindowCopyGC(Window wID)
makes a copy of the default WBGC so that it can be modified
int minor_code
minor code from XErrorEvent
Atom aWM_TAKE_FOCUS
'Take Focus' - TODO document this properly
void CHSettingsRefresh(Display *pDisplay)
refresh the internally cached X settings
Definition: conf_help.c:1798
Display * pDisplay
Display pointer passed into error handler function (NULL if no error)
int WBReparentWindow(Window wID, Window wIDParent, int iX, int iY)
Assigns a new parent to the specified window (aka 'Re-parent')
Atom aPIXMAP
PIXMAP Atom for the clipboard - uses XA_PIXMAP.
void WBMouseCancel(Display *pDisplay, Window wID)
low-level event processing, internal handling of Expose events
WB_FONT pFont
default font for window, NULL to use pDefaultFont
enum WMPropertiesWindowType WBGetWMPropertiesWindowType(Window wID)
re-assign standard WM (Window Manager) 'window type' properties and notify the root window (reserved)
void WBDebugDumpGC(Display *pDisplay, WBGC hGC)
dumps a GC's settings
void WBXlatCoordRect(Window wIDSrc, const WB_RECT *pRectSrc, Window wIDDest, WB_RECT *pRectDest)
Translate rectangle coordinates relative to a window.
void WBBeginWaitCursor(Window wID)
increment 'wait cursor' count, set cursor to WB_WAIT_CURSOR
unsigned int height
const char * WBGetWindowClassName(Window wID)
Returns the window's assigned class name pointer.
int WBAppDefault(XEvent *pEvent)
implements the default application event callback behavior
internal wrapper struct for 'rectangle' definition
int(* WBLocateWindowCallback)(Window wID, void *pData)
callback definition for WBLocateWindow
#define WB_DEFAULT_FONT
The default X11 font name (currently "fixed")
int WBErrorCheckRetry(void)
Check to see whether or not an X11 error was detected AND can be re-tried.
int WBSetClipOrigin(WBGC hGC, int clip_x_origin, int clip_y_origin)
Set clip origin, a wrapper for XSetClipOrigin()
int WBErrorCheck(void)
Check to see whether or not an X11 error was detected.
Atom aQUERY_CLOSE
query if it's ok to close (and optionally destroy yourself if ok) a window
void WBSetWindowDefaultCursor(Window wID, int idStandardCursor)
Assigns a default cursor (by ID) to a window.
WMPropertiesWMProtocols
Window WMProtocols support enumeration.
void WBRegisterMenuCallback(Window wID, WBWinEvent pMenuCallback)
(internal) Register a MENU callback for a window
Display * WBGetWindowDisplay(Window wID)
returns the Display associated with a window
Atom aCONTROL_NOTIFY
dialog control and child window notification messages
void WBFreeGC(WBGC hGC)
Free resources for a WBGC, wrapper for XFreeGC()
int error_code
error code from XErrorEvent
void WBSetWindowData(Window wID, int iIndex, void *pData)
assign 'data pointer' for a window and specified index value
void WBChangeWMPropertiesWindowType(Window wID, enum WMPropertiesWindowType wmProp, enum WMPropertiesWindowType wmChangeMask)
re-assign standard WM (Window Manager) 'window type' properties and notify the root window (reserved)
#define WB_DEFAULT_FONT_SIZE
The default X11 font size (currently 13)
XID resourceid
resource ID (usually a Window) from XErrorEvent
Atom aTARGETS
TARGETS Atom for the clipboard.
Core (internal) structure for managing delayed events.
void __internal_font_helper_init(void)
initialization for font helper - call once at start of program (WBInit() does this for you)
Definition: font_helper.c:96
struct s_TIMER_ENTRY * pNext
linked lists for performance
Atom aTARGET
TARGET Atom for the clipboard.
void WBSetWMProtocols(Window wID, Atom aProperty,...)
re-assign standard WM (Window Manager) 'window type' properties and notify the root window (reserved)
Status WBGetGCValues(WBGC hGC, unsigned long valuemask, XGCValues *values)
Change a WBGC, a wrapper for XGetGCValues()
void WBRegisterAppCallback(WBAppEvent pCallback)
Registers a callback function for application events.
#define BEGIN_XCALL_DEBUG_WRAPPER
wrapper macro for calls into the X11 library. This macro precedes the call(s)
unsigned long WBGetGCFGColor(WBGC gc)
returns the currently assigned foreground color for a WBGC
void WBThreadFreeDisplay(Display *pThreadDisplay)
un-initializes a Display for a thread that was allocated by WBThreadInitDisplay()
unsigned long WBGetGCBGColor(WBGC gc)
returns the currently assigned background color for a WBGC
struct timeval tvLastActivity
time of last activity (TODO: find a better way than 'gettimeofday')
int idDefaultCursor
default cursor
int CHGetDragThreshold(Display *pDisplay)
returns default drag threshold (from X settings)
Definition: conf_help.c:2280
void WBSetWindowDefaultGC(Window wID, WBGC hGC)
assigns a default WBGC to a window
void WBSetInputFocus(Window wID)
set input focus to a specific window
int iWaitCursorCount
current 'count' for wait cursor
Atom aCOLORMAP
COLORMAP Atom for the clipboard - uses XA_COLORMAP.
Atom aBITMAP
BITMAP Atom for the clipboard - uses XA_BITMAP.
int WBFillRectangle(Display *display, Drawable d, WBGC gc, int x, int y, unsigned int width, unsigned int height)
Wrapper for XFillRectangle()
void WBGetWindowGeom(Window wID, WB_GEOM *pGeom)
Returns the RAW geometry of the window as reported by the window manager.
Region WBGeomToRegion(const WB_GEOM *pGeom)
'Paint' helper, converts a WB_GEOM structure to a Region.
char * WBGetAtomName(Display *pDisplay, Atom aAtom)
Lookup and/or allocate an internal Atom for a named string.
enum WMPropertiesWindowType eWindowType
a combination of WMPropertiesWindowType values (default None)
void WBDebugPrint(const char *pFmt,...) __attribute__((format(printf
conditional debug message output
Atom aWB_TIMER
timer notifications generated by API
Atom aDESTROY_NOTIFY
notify parent that child is being destroyed
#define WB_KEYEVENT_CTRL
'AltCtrlShift' bit flag for Control modifier for WBKeyEventProcessKey()
An allocated structure containing XFontStruct, XFontInfo, and XftFont [as applicable] for a specified...
Definition: font_helper.h:152
int WBDefault(Window wID, XEvent *pEvent)
implements the default window event callback behavior
void WBEndWaitCursor(Window wID)
decrement 'wait cursor' count, restore to default when zero
int CreateTimer(Display *pDisplay, Window wID, unsigned long lInterval, long lID, int iPeriodic)
Creates a one-shot or periodic timer.
void WBEndModal(Window wID, int iRval)
End a modal window with a specific return value.
int GetStartupMinMax(void)
returns the min/max/normal window state for startup
Cursor curRecent
most recent cursor resource (must be freed via XFreeCursor)
void WBUpdateWindowWithImage(Display *pDisplay, Window wID)
Update the window's appearance with the contents of the cached XImage.
XEvent xEvt
the event I'm queueing
Atom aNULL
NULL Atom for the clipboard.
void WBSetWindowTitle(Window wID, const char *szTitle)
assign window (and icon) title
#define WB_IF_DEBUG_LEVEL(L)
Preferred method of implementing conditional debug 'if block' code.
Definition: debug_helper.h:404
void WBEndPaint(Window wID, WBGC gc)
'Paint' helper, frees resources and marks the update region 'valid'
internal wrapper struct for GC with local cache
Atom aRESIZE_NOTIFY
notification of window re-size via ClientMessage
#define WBPointInGeom(X, Y, G)
Returns logical TRUE if the point (X,Y) is within the borders of the geometry 'G'.
Atom aTEXT
TEXT Atom for the clipboard.
int(* pMenuCallback)(Window wIDEvent, XEvent *pEvent)
Pointer to the window's MENU callback function - may be NULL, valid only for windows with menus.
Atom aSET_FOCUS
dialog focus messages
const char * pFunc
Name of the function as assigned by BEGIN_XCALL_DEBUG_WRAPPER.
Time WBGetLastEventTime(void)
Main message loop, high level API to check for and retrieve the next event.
Atom aC_STRING
C_STRING Atom for the clipboard.
void WBDebugDumpRegion(Region hRgn, int bRotate)
dumps contents of a region
int WBMapWindow(Display *pDisplay, Window wID)
Wrapper for XMapWindow, makes window visible.
int __FinishInitClipboardSystem(Display *pDisplay, const char *szDisplayName)
initializes clipboard sub-system
Atom aSTRING
STRING Atom for the clipboard - uses XA_STRING.
int request_code
request code from XErrorEvent
#define WB_WARN_PRINT(...)
Preferred method of implementing a 'warning level' debug message for all subsystems.
Definition: debug_helper.h:467
void WBExitClipboardSystem(Display *pDisplay)
Shut down the clipboard sub-system.
#define WB_UNLIKELY(x)
optimization for code branching when condition is 'unlikely'. use within conditionals
void WBValidateRegion(Window wID, Region rgn)
'Paint' helper, validates a region for asynchronous Expose event generation
Atom aUTF8_STRING
UTF8_STRING Atom for the clipboard.
int i_xcall_line
debug helper variable indicating the line number of the function calling into the X11 library
Definition: window_helper.c:99
void WBRemoveMenuWindow(Window wID, Window wIDMenu)
Remove (detach) the specified menu window from a (frame) window.
const WB_ERROR_INFO * WBGetLastError(void)
Obtain a const pointer to the internal WB_ERROR_INFO structure.
void WBPostDelayedSetFocusAppEvent(Display *pDisplay, Window wID, Window wIDFrom, unsigned int iDelay)
Creates a 'set focus' ClientMessage event for the application event handler.
WBGC WBBeginPaintGeom(Window wID, WB_GEOM *pgBounds)
'Paint' helper, creates a WBGC for use in updating the window for a specified rectangular area
#define WB_KEYEVENT_KEYSYM
'AltCtrlShift' bit flag for 'VK_' keys for WBKeyEventProcessKey()
void WBXlatCoordGeom(Window wIDSrc, const WB_GEOM *pGeomSrc, Window wIDDest, WB_GEOM *pGeomDest)
Translate geometry coordinates relative to a window.
WB_FONTC WBGetDefaultFont(void)
Returns a pointer to the default font WB_FONT for the default display. This is a shared resource; do ...