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