1 /*
2  * wmslib/src/but/but.c, part of wmslib (Library functions)
3  * Copyright (C) 1994-1996 William Shubert.
4  * See "configure.h.in" for more copyright information.
5  */
6 
7 #include <configure.h>
8 
9 #ifdef  X11_DISP
10 
11 #ifdef  STDC_HEADERS
12 #include <stdlib.h>
13 #include <unistd.h>
14 #endif  /* STDC_HEADERS */
15 #include <stdio.h>
16 #include <X11/Xlib.h>
17 #include <X11/Xutil.h>
18 #include <X11/cursorfont.h>
19 #include <X11/Xatom.h>
20 #include <X11/keysym.h>
21 #include <sys/time.h>
22 #include <wms.h>
23 #include <but/but.h>
24 #include <but/net.h>
25 #include <but/timer.h>
26 #include <wms/str.h>
27 
28 
29 /**********************************************************************
30  * Types
31  **********************************************************************/
32 typedef struct MouseEventFinder_struct  {
33   bool  found;
34   Window  win;
35 } MouseEventFinder;
36 
37 
38 #if  XlibSpecificationRelease < 5
39 /*
40  * Older (X11R4 and earlier) releases of X11 used a char * where now an
41  *   XPointer is used.
42  */
43 typedef char  *XPointer;
44 #endif
45 
46 
47 /**********************************************************************
48  * Forward declarations
49  **********************************************************************/
50 static ButOut  keyPress(ButEnv *env, XKeyPressedEvent *evt);
51 static ButOut  keyRelease(ButEnv *env, XKeyReleasedEvent *evt);
52 static ButOut  handleEvent(ButEnv *env);
53 static int  activateEvent(ButEnv *env);
54 static bool  butEnv_stdColors(ButEnv *env);
55 static void  getWinXY(Display *dpy, ButWin *win);
56 static ButOut  serviceXData(void *packet, int fd);
57 static bool  performQueuedWinStuff(ButEnv *env);
58 static Bool  anotherMouseEvent(Display *dpy, XEvent *ev, XPointer arg);
59 #if  DEBUG
60 static int  butErrors(Display *dpy, XErrorEvent *err);
61 #endif
62 
63 
64 /**********************************************************************
65  * Globals
66  **********************************************************************/
67 ButTimer  *but_timerList;
68 Atom  but_wmDeleteWindow, but_wmProtocols;
69 
70 static fd_set  emptyfds;
71 static struct timeval  long_timeout;
72 
73 static void  makeStripes(ButEnv *env, int ssize);
74 
75 /*
76  * This array really should be const, but an error in my Xlib.h forces me
77  *   to leave it non-const.  :-(
78  */
79 static char  greymaps[17][4] = {
80   {0x00, 0x00, 0x00, 0x00},
81   {0x01, 0x00, 0x00, 0x00},
82   {0x01, 0x00, 0x04, 0x00},
83   {0x05, 0x00, 0x04, 0x00},
84   {0x05, 0x00, 0x05, 0x00},
85   {0x05, 0x02, 0x05, 0x00},
86   {0x05, 0x02, 0x05, 0x08},
87   {0x05, 0x0a, 0x05, 0x08},
88   {0x05, 0x0a, 0x05, 0x0a},
89   {0x07, 0x0a, 0x05, 0x0a},
90   {0x07, 0x0a, 0x0d, 0x0a},
91   {0x0f, 0x0a, 0x0d, 0x0a},
92   {0x0f, 0x0a, 0x0f, 0x0a},
93   {0x0f, 0x0b, 0x0f, 0x0a},
94   {0x0f, 0x0b, 0x0f, 0x0e},
95   {0x0f, 0x0f, 0x0f, 0x0e},
96   {0x0f, 0x0f, 0x0f, 0x0f}};
97 
98 
99 /**********************************************************************
100  * Functions
101  **********************************************************************/
102 /* Returns false if the display can't be opened. */
butEnv_create(const char * protocol,const char * dpyname,int shutdown (Display * dpy))103 ButEnv  *butEnv_create(const char *protocol,
104 		       const char *dpyname, int shutdown(Display *dpy))  {
105   Display  *dpy;
106   ButEnv  *env;
107 
108   env = (ButEnv *)wms_malloc(sizeof(ButEnv));
109   MAGIC_SET(env);
110   if ((dpy = env->dpy = XOpenDisplay(dpyname)) == NULL)  {
111     MAGIC_UNSET(env);
112     wms_free(env);
113     return(NULL);
114   }
115 #if  DEBUG
116   XSynchronize(dpy, True);
117   XSetErrorHandler(butErrors);
118 #endif
119   env->protocol = (char *)wms_malloc(strlen(protocol)+1);
120   /* Probably not a good idea to silently truncate the protocol like this. */
121   if (strlen(protocol) > BUTNET_MAXCMD)
122     env->protocol[BUTNET_MAXCMD] = '\0';
123   strcpy(env->protocol, protocol);
124   if (shutdown != NULL)
125     XSetIOErrorHandler(shutdown);
126   env->shutdown = shutdown;
127   env->last_mwin = 0;
128   env->last_mx = 0;
129   env->last_my = 0;
130   return(env);
131 }
132 
133 
butEnv_createNoDpy(const char * protocol)134 ButEnv  *butEnv_createNoDpy(const char *protocol)  {
135   ButEnv  *env;
136 
137   env = (ButEnv *)wms_malloc(sizeof(ButEnv));
138   MAGIC_SET(env);
139   env->dpy = NULL;
140 
141   return(env);
142 }
143 
144 
145 /*
146  * Returns:
147  *   0 - Black and white display or color=FALSE.
148  *   1 - Couldn't allocate standard colors.  Failed.
149  *   2 - Color successful.
150  *   3 - Truecolor display.  Color will always be successful.
151  */
butEnv_init(ButEnv * env,void * packet,const char * atomname,bool color)152 int  butEnv_init(ButEnv *env, void *packet, const char *atomname,
153 		 bool color)  {
154   Display  *dpy;
155   static int  firstInit = TRUE;
156   int  i;
157   XGCValues  defGc;
158   unsigned long  gcVals;
159   int  retval;
160   XVisualInfo  xvi, *vlr;
161   int  depth;
162   Window  dummyRoot;
163   unsigned int  tempW, tempH, dummyBWRet, dummyDepth;
164   int  dummyX, dummyY;
165 
166   dpy = env->dpy;
167   env->fonts = NULL;
168   env->colors = NULL;
169   env->colorPmaps = NULL;
170   env->winlist = NULL;
171   if (dpy)  {
172     but_wmDeleteWindow = XInternAtom(dpy, "WM_DELETE_WINDOW", 0);
173     but_wmProtocols = XInternAtom(dpy, "WM_PROTOCOLS", 0);
174     depth = DefaultDepth(dpy, DefaultScreen(dpy));
175     env->depth = depth;
176   }
177   if (firstInit)  {
178     firstInit = FALSE;
179     but_timerList = NULL;
180     FD_ZERO(&emptyfds);
181     long_timeout.tv_sec = 60 * 60 * 24 * 365;  /* 1 year. */
182     long_timeout.tv_usec = 0;
183   }
184   if (dpy && color)  {
185     xvi.visual = DefaultVisual(dpy, DefaultScreen(dpy));
186     xvi.visualid = XVisualIDFromVisual(xvi.visual);
187     vlr = XGetVisualInfo(dpy, VisualIDMask, &xvi, &i);
188     if (vlr[0].class == TrueColor)
189       retval = 3;
190     else if (vlr[0].class >= 2)  {  /* A color class */
191       retval = 2;
192     } else  {  /* B&W */
193       retval = 0;
194       color = FALSE;
195     }
196     XFree(vlr);
197   } else
198     retval = 0;
199   env->packet = packet;
200   if (dpy)  {
201     env->prop = XInternAtom(dpy, atomname, False);
202     XGetGeometry(dpy, DefaultRootWindow(dpy), &dummyRoot, &dummyX, &dummyY,
203 		 &tempW, &tempH, &dummyBWRet, &dummyDepth);
204     env->rootW = tempW;
205     env->rootH = tempH;
206   }
207   env->sReq = NULL;
208   env->sClear = NULL;
209   env->sNotify = NULL;
210   env->winlist = NULL;
211   env->wllen = env->wlmax = 0;
212   env->minWindows = 1;
213   env->butIn = NULL;
214   env->lockBut = NULL;
215   env->last_mwin = NULL;
216   env->stipDisable = None;
217   for (i = 0;  i < BUTWRITE_MAXCHARS;  ++i)
218     env->write[i].draw = NULL;
219   env->keyModifiers = 0;
220   env->eventNum = 0;
221   env->maxFd = 0;
222   for (i = 0;  i < 3;  ++i)  {
223     env->maxGFds[i] = 0;
224     FD_ZERO(&env->fMasks[i]);
225     env->fCallbacks[i] = NULL;
226   }
227   if (dpy)
228     butEnv_addFile(env, BUT_READFILE, ConnectionNumber(dpy),
229 		   env, serviceXData);
230   else
231     return(retval);
232 
233   defGc.function = GXcopy;
234   defGc.plane_mask = AllPlanes;
235   defGc.line_style = LineSolid;
236   defGc.cap_style = CapButt;
237   defGc.join_style = JoinMiter;
238   if (color)
239     defGc.fill_style = FillSolid;
240   else
241     defGc.fill_style = FillTiled;
242   defGc.fill_rule = EvenOddRule;
243   defGc.graphics_exposures = False;
244   gcVals = GCFunction | GCPlaneMask | GCLineStyle | GCCapStyle |
245     GCJoinStyle | GCFillStyle | GCFillRule | GCGraphicsExposures;
246   env->gc = XCreateGC(dpy, RootWindow(dpy, DefaultScreen(dpy)),
247 		      gcVals, &defGc);
248   env->gc2 = XCreateGC(dpy, RootWindow(dpy, DefaultScreen(dpy)),
249 		       gcVals, &defGc);
250 
251   env->numFonts = 1;
252   env->fonts = (XFontStruct **)wms_malloc(env->numFonts *
253 					  sizeof(XFontStruct *));
254   for (i = 0;  i < env->numFonts;  ++i)  {
255     env->fonts[i] = NULL;
256     butEnv_setFont(env, 0, "fixed", 0);
257   }
258   env->colorp = color;
259   env->numColors = BUT_DCOLORS;
260   env->colors = (ulong *)wms_malloc(env->numColors * sizeof(ulong));
261   env->colorPmaps = (Pixmap *)wms_malloc(env->numColors * sizeof(Pixmap));
262   if (!butEnv_stdColors(env))  {
263     retval = 1;
264     env->colorp = FALSE;
265     XSetFillStyle(dpy, env->gc, FillTiled);
266     XSetFillStyle(dpy, env->gc2, FillTiled);
267     butEnv_stdColors(env);
268   }
269   env->partner = 0;
270   env->numPartners = 0;
271   env->partners = NULL;
272   env->maxButIds = env->maxWinIds = 0;
273   env->id2But = NULL;
274   env->id2Win = NULL;
275   butEnv_rcInit(env);
276   return(retval);
277 }
278 
279 
butEnv_destroy(ButEnv * env)280 void  butEnv_destroy(ButEnv *env)  {
281   int  i;
282 
283   assert(MAGIC(env));
284   while (env->wllen != 0)  {
285     but_inEvent = TRUE;
286     for (i = 0;  i < env->wllen;  ++i)
287       butWin_destroy(env->winlist[i]);
288     but_inEvent = FALSE;
289     while (but_dList(NULL) || butWin_dList(NULL));
290   }
291   for (i = 0;  i < env->numPartners;  ++i)  {
292     if (env->partners[i] != NULL)  {
293       butRnet_destroy(env->partners[i], "Remote user has quit the program.");
294     }
295   }
296   if (env->fonts != NULL)
297     wms_free(env->fonts);
298   if (env->colors != NULL)
299     wms_free(env->colors);
300   if (env->colorPmaps != NULL)
301     wms_free(env->colorPmaps);
302   if (env->winlist != NULL)
303     wms_free(env->winlist);
304   if (env->protocol != NULL)
305     wms_free(env->protocol);
306   MAGIC_UNSET(env);
307   XCloseDisplay(env->dpy);
308   wms_free(env);
309 }
310 
311 
butEnv_events(ButEnv * env)312 void  butEnv_events(ButEnv *env)  {
313   int  i, selected_fds, fdGroup;
314   fd_set  fdSets[3];
315   struct timeval  next_timer;
316   ButOut  result, temp;
317 
318   if (env->dpy)
319     butEnv_rcActivate(env);
320   for (;;)  {
321     result = 0;
322     if (env->dpy)  {
323       do  {
324 	XFlush(env->dpy);
325 	while (XPending(env->dpy))  {
326 	  result |= handleEvent(env);
327 	  if (result & BUTOUT_STOPWAIT)
328 	    return;
329 	}
330 	result |= butEnv_checkTimers(env, &next_timer);
331 	if (result & BUTOUT_STOPWAIT)  {
332 	  return;
333 	}
334       } while(performQueuedWinStuff(env));
335       if (result & BUTOUT_ERR)  {
336 	XBell(env->dpy, 0);
337 	result = 0;
338       }
339     }
340     for (fdGroup = 0;  fdGroup < 3;  ++fdGroup)  {
341       fdSets[fdGroup] = env->fMasks[fdGroup];
342     }
343     if (env->dpy && (env->wllen <= env->minWindows))
344       return;
345     selected_fds = select(env->maxFd, &fdSets[BUT_READFILE],
346 			  &fdSets[BUT_WRITEFILE],
347 			  &fdSets[BUT_XFILE], &next_timer);
348     assert(selected_fds >= 0);
349     if (selected_fds > 0)  {
350       for (fdGroup = 0;  fdGroup < 3;  ++fdGroup)  {
351 	for (i = 0;  i < env->maxGFds[fdGroup];  ++i)  {
352 	  if (FD_ISSET(i, &fdSets[fdGroup]))  {
353 	    assert(env->fCallbacks[fdGroup][i].callback != NULL);
354 	    but_inEvent = TRUE;
355 	    result |= env->fCallbacks[fdGroup][i].
356 	      callback(env->fCallbacks[fdGroup][i].packet, i);
357 	    but_inEvent = FALSE;
358 	    do  {
359 	      temp = but_dList(NULL) | butWin_dList(NULL);
360 	      result |= temp;
361 	    } while (temp != 0);
362 	    if (env->dpy)  {
363 	      butEnv_rcActivate(env);
364 	      if (result & BUTOUT_ERR)
365 		XBell(env->dpy, 0);
366 	    }
367 	    if (result & BUTOUT_STOPWAIT)  {
368 	      return;
369 	    }
370 	  }
371 	}
372       }
373     }
374   }
375 }
376 
377 
378 /*
379  * Do all the resizes and redraws that have been queued up now.
380  * TRUE is returned if there was anything to do.
381  */
performQueuedWinStuff(ButEnv * env)382 static bool  performQueuedWinStuff(ButEnv *env)  {
383   bool  anythingDone = FALSE;
384   ButWin  *win, *winToDo, *ancestor;
385   int  i;
386 
387   for (i = 0;  i < env->wllen;  ++i)  {
388     win = env->winlist[i];
389     if (win->resizeNeeded)  {
390       anythingDone = TRUE;
391       win->resize(win);
392       win->resizeNeeded = FALSE;
393       win->resized = TRUE;
394     }
395   }
396   do  {
397     winToDo = NULL;
398     for (i = 0;  i < env->wllen;  ++i)  {
399       win = env->winlist[i];
400       if (win->redrawReady && win->numRedraws && win->resized)  {
401 	if (winToDo == NULL)  {
402 	  winToDo = win;
403 	} else  {
404 	  for (ancestor = win->parent;  ancestor;
405 	       ancestor = ancestor->parent)  {
406 	    if (ancestor == winToDo)
407 	      winToDo = win;
408 	  }
409 	}
410       }
411     }
412     if (winToDo)  {
413       anythingDone = TRUE;
414       butWin_performDraws(winToDo);
415     }
416   } while (winToDo);
417   return(anythingDone);
418 }
419 
420 
handleEvent(ButEnv * env)421 static ButOut  handleEvent(ButEnv *env)  {
422   ButOut  result, temp;
423 
424   but_inEvent = TRUE;
425   result = activateEvent(env);
426   but_inEvent = FALSE;
427   do  {
428     temp = but_dList(NULL) | butWin_dList(NULL);
429     result |= temp;
430   } while (temp != 0);
431   butEnv_rcActivate(env);
432   return(result);
433 }
434 
435 
activateEvent(ButEnv * env)436 static int  activateEvent(ButEnv *env)  {
437   Display  *dpy;
438   XEvent  event, ev2;
439   ButWin  *win = NULL;
440   int  old_w, old_h;
441   ButOut  result = 0;
442   MouseEventFinder  mef;
443 
444   dpy = env->dpy;
445   XNextEvent(dpy, &event);
446   switch(event.type)  {
447   case Expose:
448     win = butEnv_findWin(env, event.xexpose.window);
449     if (win != NULL)  {
450       assert(win->mapped);
451       butWin_redraw(win, event.xexpose.x, event.xexpose.y,
452 		    event.xexpose.width, event.xexpose.height);
453       /* Don't do any of the redraws until the expose count hits zero. */
454       win->redrawReady = (event.xexpose.count == 0);
455     }
456     break;
457   case MapNotify:
458     win = butEnv_findWin(env, event.xmap.window);
459     assert(MAGICNULL(win));
460     if (win == NULL)
461       return(result);
462     butWin_turnOnTimers(win);
463     win->mapped = TRUE;
464     if (win->map != NULL)
465       win->map(win);
466     if (!win->resized)  {
467       /*
468        * If you have no window manager, you won't get your ConfigureNotify
469        *   when you start up, so we have to fake that first resize when
470        *   we get mapped.  Yeah, it's ugly, but deal with it.
471        */
472       win->resizeNeeded = TRUE;
473     }
474     break;
475   case UnmapNotify:
476     win = butEnv_findWin(env, event.xunmap.window);
477     if (win == NULL)
478       return(result);
479     butWin_turnOffTimers(win);
480     win->mapped = FALSE;
481     if (win->unmap != NULL)  {
482       result |= win->unmap(win);
483     }
484     if (!win->isIcon && (win->iconWin == NULL))  {
485       butWin_dList(win);
486     }
487     break;
488   case ConfigureNotify:
489     win = butEnv_findWin(env, event.xunmap.window);
490     if (win == NULL)
491       return(result);
492     old_w = win->w;
493     old_h = win->h;
494     win->w = win->logicalW = event.xconfigure.width;
495     win->h = win->logicalH = event.xconfigure.height;
496     butWin_checkDims(win);
497     if ((win->w != old_w) || (win->h != old_h) || !win->resized)
498       win->resizeNeeded = TRUE;
499     getWinXY(dpy, win);
500     break;
501   case MappingNotify:
502   case ReparentNotify:
503   case DestroyNotify:  /* I should handle this correctly. */
504     break;
505   case ClientMessage:
506     if ((event.xclient.message_type == but_wmProtocols) &&
507 	(event.xclient.data.l[0] == but_wmDeleteWindow))  {
508       /* The WM asked this window to go away.  Bye! */
509       win = butEnv_findWin(env, event.xclient.window);
510       if (win == NULL)
511 	return(result);
512       if (win->quit)
513 	win->quit(win);
514       else
515 	butWin_dList(win);
516     }
517     break;
518   case ButtonPress:
519     env->eventTime = event.xbutton.time;
520     env->keyModifiers = event.xbutton.state;
521     ++env->eventNum;
522     win = butEnv_findWin(env, event.xbutton.window);
523     if (win)
524       result |= butWin_mPress(win, event.xbutton.x, event.xbutton.y,
525 			      event.xbutton.button);
526     return(result);
527     break;
528   case KeyRelease:
529     env->eventTime = event.xbutton.time;
530     env->keyModifiers = event.xbutton.state;
531     return(result | keyRelease(env, &(event.xkey)));
532     break;
533   case ButtonRelease:
534     env->eventTime = event.xbutton.time;
535     env->keyModifiers = event.xbutton.state;
536     win = butEnv_findWin(env, event.xbutton.window);
537     if (win)
538       result |= butWin_mRelease(win, event.xbutton.x, event.xbutton.y,
539 				event.xbutton.button);
540     return(result);
541     break;
542   case KeyPress:
543     env->eventTime = event.xbutton.time;
544     env->keyModifiers = event.xbutton.state;
545     ++env->eventNum;
546     return(result | keyPress(env, &(event.xkey)));
547     break;
548   case MotionNotify:
549     mef.found = FALSE;
550     mef.win = event.xmotion.window;
551     XCheckIfEvent(dpy, &ev2, anotherMouseEvent, (XPointer)&mef);
552     if (!mef.found)  {
553       win = butEnv_findWin(env, event.xmotion.window);
554       if (win)  {
555 	env->eventTime = event.xmotion.time;
556 	butWin_mMove(win, event.xmotion.x, event.xmotion.y);
557 	butRnet_mMove(env, win->id, event.xmotion.x,event.xmotion.y,
558 		      win->w,win->h, -1);
559       }
560     }
561     break;
562   case LeaveNotify:
563     win = butEnv_findWin(env, event.xcrossing.window);
564     if (win)  {
565       butWin_mMove(win, BUT_NOCHANGE,BUT_NOCHANGE);
566       butRnet_mMove(env, -2, -1,-1,-1,-1,-1);
567     }
568     break;
569   case FocusIn:
570     break;
571   case FocusOut:
572     if (env->lockBut)  {
573       if (env->lockBut->flags & BUT_KEYPRESSED)  {
574 	return(result | env->lockBut->action->kRelease(env->lockBut, "", 0));
575       }
576     }
577     break;
578   case SelectionRequest:
579     ev2.type = SelectionNotify;
580     ev2.xselection.type = SelectionNotify;
581     ev2.xselection.send_event = True;
582     ev2.xselection.display = event.xselectionrequest.display;
583     ev2.xselection.requestor = event.xselectionrequest.requestor;
584     ev2.xselection.selection = event.xselectionrequest.selection;
585     ev2.xselection.target = event.xselectionrequest.target;
586     if ((env->sReq == NULL) ||
587 	!env->sReq(env, &(event.xselectionrequest)))
588       ev2.xselection.property = None;
589     else
590       ev2.xselection.property = event.xselectionrequest.property;
591       ev2.xselection.time = event.xselectionrequest.time;
592     XSendEvent(env->dpy, event.xselectionrequest.requestor, False,
593 	       0, &ev2);
594     break;
595   case SelectionNotify:
596     if (env->sNotify != NULL)
597       env->sNotify(env, &(event.xselection));
598     break;
599   case SelectionClear:
600     if (env->sClear != NULL)
601       env->sClear(env);
602     break;
603   default:
604 #if  DEBUG
605     printf("UNKNOWN EVENT!  #%d\n", event.type);
606 #endif
607     break;
608   }
609   return(result);
610 }
611 
612 
anotherMouseEvent(Display * dpy,XEvent * ev,XPointer arg)613 static Bool  anotherMouseEvent(Display *dpy, XEvent *ev, XPointer arg)  {
614   MouseEventFinder  *mef = (MouseEventFinder *)arg;
615 
616   if (ev->type == MotionNotify)  {
617     if (ev->xmotion.window == mef->win)
618       mef->found = TRUE;
619   } else if ((ev->type == ButtonPress) || (ev->type == ButtonRelease))  {
620     if (ev->xbutton.window == mef->win)
621       mef->found = TRUE;
622   }
623   return(False);
624 }
625 
626 
627 #ifndef  STR_MAXLEN
628 #define STR_MAXLEN  100
629 #endif
keyPress(ButEnv * env,XKeyPressedEvent * evt)630 static ButOut  keyPress(ButEnv *env, XKeyPressedEvent *evt)  {
631   int  slen;
632   char  kbuf[STR_MAXLEN];
633   KeySym  keysym;
634   ButWin  *win = butEnv_findWin(env, evt->window);
635 
636   if (!win)
637     return(0);
638   assert(MAGIC(win));
639   slen = XLookupString(evt, kbuf, STR_MAXLEN-1, &keysym, NULL);
640   kbuf[slen] = '\0';
641   if (kbuf[0] == '\r')
642     kbuf[0] = '\n';
643   return(butWin_kPress(win, kbuf, keysym));
644 }
645 
646 
keyRelease(ButEnv * env,XKeyReleasedEvent * evt)647 static ButOut  keyRelease(ButEnv *env, XKeyReleasedEvent *evt)  {
648   int  slen;
649   char  kbuf[STR_MAXLEN];
650   KeySym  keysym;
651   ButWin  *win = butEnv_findWin(env, evt->window);
652 
653   if (win == NULL)
654     return(0);
655   slen = XLookupString(evt, kbuf, STR_MAXLEN-1, &keysym, NULL);
656   kbuf[slen] = '\0';
657   if (kbuf[0] == '\r')
658     kbuf[0] = '\n';
659   if (XPending(env->dpy))  {
660     XEvent  nextev;
661 
662     XPeekEvent(env->dpy, &nextev);
663     if (nextev.type == KeyPress)  {
664       if ((nextev.xkey.keycode == evt->keycode) &&
665 	  (evt->time == nextev.xkey.time))  {
666 	XNextEvent(env->dpy, &nextev);
667 	return(butWin_kPress(win, kbuf, keysym));
668       }
669     }
670   }
671   return(butWin_kRelease(win, kbuf, keysym));
672 }
673 
674 
675 /* Disable all timers used in a particular window.  Useful mostly so that
676  *   when you iconify a window, the timers shut off.  Even though the timers
677  *   don't take much CPU time, shutting them off makes it possible to
678  *   swap the entire application out of memory and this COULD have a
679  *   noticeable effect on system performance if other applicaitons need
680  *   lots of memory.
681  * The timers will stay in the timer queue, but they will not go off.
682  */
butWin_turnOffTimers(ButWin * win)683 void  butWin_turnOffTimers(ButWin *win)  {
684   ButTimer  *timer;
685 
686   for (timer = but_timerList;  timer != NULL;  timer = timer->next)  {
687     if ((timer->win == win) && (timer->state == butTimer_on) &&
688 	timer->winOnly)
689       timer->state = butTimer_off;
690   }
691 }
692 
693 
694 /* Re-enable all timers for a particular window.  This will undo the work
695  *   of but_turnoff_timers.
696  */
butWin_turnOnTimers(ButWin * win)697 void  butWin_turnOnTimers(ButWin *win)  {
698   ButTimer  *timer;
699 
700   for (timer = but_timerList;  timer != NULL;  timer = timer->next)  {
701     if ((timer->win == win) && (timer->state == butTimer_off) &&
702 	timer->winOnly)
703       timer->state = butTimer_on;
704   }
705 }
706 
707 
butEnv_stdColors(ButEnv * env)708 static bool  butEnv_stdColors(ButEnv *env)  {
709   ButColor  colorset[BUT_DCOLORS];
710   int  i, xblack, xwhite;
711   ButColor  black, white;
712   Display  *dpy = env->dpy;
713   Window  rootwin = DefaultRootWindow(dpy);
714 
715   xblack = BlackPixel(dpy, DefaultScreen(dpy));
716   xwhite = WhitePixel(dpy, DefaultScreen(dpy));
717   for (i = 0;  i < 17;  ++i)  {
718     env->greyMaps[i] =
719       XCreatePixmapFromBitmapData(dpy, rootwin, greymaps[i], 4,4,
720 				  xwhite, xblack, env->depth);
721   }
722 
723   black = butColor_create(0,0,0,0);
724   white = butColor_create(255,255,255,16);
725   colorset[BUT_FG] = black;
726   colorset[BUT_BG] = butColor_mix(white,3, black,1);
727   colorset[BUT_PBG] = butColor_mix(colorset[BUT_BG],7, black,1);
728   colorset[BUT_PBG].greyLevel = 14;
729   colorset[BUT_HIBG] = butColor_mix(colorset[BUT_BG],1, white,1);
730   colorset[BUT_LIT] = butColor_create(255,255,255, 8);
731   colorset[BUT_SHAD] = butColor_create(128,128,128, 0);
732   colorset[BUT_ENTERBG] = colorset[BUT_HIBG];
733   colorset[BUT_SELBG] = butColor_create(255,255,0,12);  /* Yellow. */
734   colorset[BUT_CHOICE] = butColor_create(0,255,0, 16);
735   colorset[BUT_WHITE] = white;
736   colorset[BUT_BLACK] = black;
737   for (i = 0;  i < BUT_DCOLORS;  ++i)  {
738     env->colorPmaps[i] = None;
739     if (butEnv_setColor(env, i, colorset[i]) == 0)
740       return(FALSE);
741   }
742   return(TRUE);
743 }
744 
745 
butEnv_setColor(ButEnv * env,int colornum,ButColor color)746 bool  butEnv_setColor(ButEnv *env, int colornum, ButColor color)  {
747   int  i;
748   Display  *dpy = env->dpy;
749   Colormap  cmap;
750   XColor  temp;
751   static uchar  bm1616[] = {1};
752   Window  rootwin = DefaultRootWindow(dpy);
753 
754   if (colornum >= env->numColors)  {
755     ulong  *newcolors;
756     Pixmap  *newpixmaps;
757 
758     newcolors = (ulong *)wms_malloc((colornum+1)*sizeof(ulong));
759     newpixmaps = (Pixmap *)wms_malloc((colornum+1)*sizeof(Pixmap));
760     for (i = 0;  i < env->numColors;  ++i)  {
761       newcolors[i] = env->colors[i];
762       newpixmaps[i] = env->colorPmaps[i];
763     }
764     if (env->colors)
765       wms_free(env->colors);
766     env->colors = newcolors;
767     if (env->colorPmaps)
768       wms_free(env->colorPmaps);
769     env->colorPmaps = newpixmaps;
770     env->numColors = colornum + 1;
771     for (;  i < env->numColors;  ++i)
772       env->colorPmaps[i] = None;
773   }
774   cmap = DefaultColormap(dpy, DefaultScreen(dpy));
775   if (env->colorPmaps[colornum] != None)  {
776     if (env->colorp)  {
777       XFreePixmap(env->dpy, env->colorPmaps[colornum]);
778       XFreeColors(env->dpy, cmap, &env->colors[colornum], 1, 0);
779     }
780   }
781   if (env->colorp)  {
782     temp.red = color.red;
783     temp.green = color.green;
784     temp.blue = color.blue;
785     temp.flags = DoRed | DoGreen | DoBlue;
786     if (XAllocColor(dpy, cmap, &temp) == 0)
787       return(FALSE);
788     env->colors[colornum] = temp.pixel;
789     env->colorPmaps[colornum] =
790       XCreatePixmapFromBitmapData(dpy, rootwin, bm1616, 1,1,
791 				  temp.pixel,temp.pixel, env->depth);
792   } else  {
793     env->colorPmaps[colornum] = env->greyMaps[color.greyLevel];
794     if (color.greyLevel < 16)
795       env->colors[colornum] = BlackPixel(dpy, DefaultScreen(dpy));
796     else
797       env->colors[colornum] = WhitePixel(dpy, DefaultScreen(dpy));
798   }
799   return(TRUE);
800 }
801 
802 
butEnv_setFont(ButEnv * env,int fontnum,const char * fontname,int fparam)803 int  butEnv_setFont(ButEnv *env, int fontnum, const char *fontname,
804 		    int fparam)  {
805   XFontStruct  *flist;
806   char  **fnames;
807   int  i, f_avail, minChar;
808   Str  fname, temp;
809   int  cstart, fontloaded = 0;
810 
811   str_init(&fname);
812   str_init(&temp);
813   if (fontnum >= env->numFonts)  {
814     XFontStruct  **newflist;
815 
816     newflist = (XFontStruct **)wms_malloc((fontnum+1) * sizeof(XFontStruct *));
817     for (i = 0;  i < env->numFonts;  ++i)
818       newflist[i] = env->fonts[i];
819     wms_free(env->fonts);
820     env->fonts = newflist;
821     env->numFonts = fontnum + 1;
822     for (;  i < env->numFonts;  ++i)
823       env->fonts[i] = NULL;
824   }
825   if (env->fonts[fontnum] != NULL)  {
826     XFreeFont(env->dpy, env->fonts[fontnum]);
827     env->fonts[fontnum] = NULL;
828   }
829   for (;;)  {
830     ++fontloaded;
831     if (*fontname == '/')
832       ++fontname;
833     if (*fontname == '\0')  {
834       fontloaded = 0;
835       fontname = "fixed";
836     }
837     for (cstart = 0;  (fontname[cstart] != '\0') && (fontname[cstart] != '/');
838 	 ++cstart);
839     str_copyCharsLen(&temp, fontname, cstart);
840     str_print(&fname, str_chars(&temp), fparam);
841     fontname += cstart;
842     fnames = XListFontsWithInfo(env->dpy, str_chars(&fname), 1,
843 				&f_avail, &flist);
844     if (f_avail > 0)  {
845       env->fonts[fontnum] = XLoadQueryFont(env->dpy, fnames[0]);
846       minChar = env->fonts[fontnum]->min_char_or_byte2;
847       if ((minChar > ' ') ||
848 	  (env->fonts[fontnum]->max_char_or_byte2 < 'z')) {
849 	printf("Char range: %d..%d, should be %d..%d\n",
850 	       env->fonts[fontnum]->min_char_or_byte2,
851 	       env->fonts[fontnum]->max_char_or_byte2,
852 	       ' ', 'z');
853 	XFreeFontInfo(fnames, flist, f_avail);
854 	continue;
855       }
856       if (env->fonts[fontnum]->per_char == NULL) {
857 	if (env->fonts[fontnum]->min_bounds.width < 1) {
858 	  XFreeFontInfo(fnames, flist, f_avail);
859 	  continue;
860 	}
861       } else {
862 	if (env->fonts[fontnum]->per_char['a' - minChar].width < 1) {
863 	  printf("Width of a is %d\n",
864 		 env->fonts[fontnum]->per_char['a'].width);
865 	  XFreeFontInfo(fnames, flist, f_avail);
866 	  continue;
867 	}
868       }
869       if (fontnum == 0)  {
870 	env->font0h = env->fonts[0]->ascent + env->fonts[0]->descent;
871 	env->stdButBw = (env->font0h + 3) / 6;
872 	makeStripes(env, (env->font0h / 15) * 2);
873       }
874       XFreeFontInfo(fnames, flist, f_avail);
875       str_deinit(&temp);
876       str_deinit(&fname);
877       return(fontloaded);
878     }
879   }
880 }
881 
882 
butEnv_drawAll(ButEnv * env)883 void  butEnv_drawAll(ButEnv *env)  {
884   int  i;
885   ButWin  *win;
886 
887   for (i = 0;  i < env->wllen;  ++i)  {
888     win = env->winlist[i];
889     butWin_redraw(win, 0,0, win->w,win->h);
890   }
891 }
892 
893 
butEnv_resizeAll(ButEnv * env)894 void  butEnv_resizeAll(ButEnv *env)  {
895   int  i;
896   ButWin  *win;
897 
898   for (i = 0;  i < env->wllen;  ++i)  {
899     win = env->winlist[i];
900     win->resize(win);
901   }
902 }
903 
904 
makeStripes(ButEnv * env,int ssize)905 static void  makeStripes(ButEnv *env, int ssize)  {
906   uint  j;
907   int  x, y;
908   uchar  *stripes;
909 
910   if (ssize == 0)
911     ssize = 2;
912   /* "ssize" is the width of a stripe pattern used to grey out text. */
913   j = (ssize + 7) / 8;
914   stripes = (uchar *)wms_malloc(ssize * j);
915   for (x = 0;  x < ssize*j;  ++x)
916     stripes[x] = 0;
917   for (y = 0;  y < ssize;  ++y)  {
918     for (x = 0;  x < ssize;  ++x)  {
919       if ((((ssize-x-1) >= y) && ((ssize-x-1) < y+(ssize/2))) ||
920 	  ((ssize-x-1) < y-(ssize/2)))
921 	stripes[(x>>3) + (y*j)] |= 1<<(x&7);
922     }
923   }
924   if (env->stipDisable != None)
925     XFreePixmap(env->dpy, env->stipDisable);
926   env->stipDisable =
927     XCreateBitmapFromData(env->dpy,
928 			  RootWindow(env->dpy, DefaultScreen(env->dpy)),
929 			  stripes, ssize,ssize);
930   wms_free(stripes);
931   XSetStipple(env->dpy, env->gc, env->stipDisable);
932   XSetStipple(env->dpy, env->gc2, env->stipDisable);
933 }
934 
935 
936 #if  DEBUG
butErrors(Display * dpy,XErrorEvent * err)937 static int  butErrors(Display *dpy, XErrorEvent *err)  {
938   char  ebuf[1024];
939 
940   XGetErrorText(dpy, err->error_code, ebuf, sizeof(ebuf));
941   fprintf(stderr, "Error: %s\n", ebuf);
942   assert(0);
943 }
944 #endif  /* DEBUG */
945 
946 
947 /*
948  * You know, it's really unbelievable to me that this is the only way in
949  *   X to find the location on the display of a window.  *sigh*.
950  * This code was blatantly stolen from "xwininfo.c".
951  */
getWinXY(Display * dpy,ButWin * win)952 static void  getWinXY(Display *dpy, ButWin *win)  {
953   static bool  errorPrinted = FALSE;
954   Status status;
955   Window wmframe = win->win;
956   XWindowAttributes frame_attr;
957 
958   while (True) {
959     Window root, parent;
960     Window *childlist;
961     unsigned int ujunk;
962 
963     status = XQueryTree(dpy, wmframe, &root, &parent, &childlist, &ujunk);
964     if (parent == root || !parent || !status)
965       break;
966     wmframe = parent;
967     if (status && childlist)
968       XFree((void *)childlist);
969   }
970   /* WM may be reparented, so find edges of the frame. */
971   /* Only works for ICCCM-compliant WMs, and then only if the
972      window has corner gravity.  We would need to know the original width
973      of the window to correctly handle the other gravities. */
974   if (!XGetWindowAttributes(dpy, wmframe, &frame_attr) && !errorPrinted)  {
975     fprintf(stderr, "wmslib: Can't get frame attributes.");
976     errorPrinted = TRUE;
977   }
978   win->x = frame_attr.x;
979   win->y = frame_attr.y;
980 }
981 
982 
butEnv_addFile(ButEnv * env,int group,int fd,void * packet,ButOut (* callback)(void * packet,int fd))983 void  butEnv_addFile(ButEnv *env, int group, int fd, void *packet,
984 		     ButOut (*callback)(void *packet, int fd))  {
985   int  i;
986 
987   assert((group >= 0) && (group < 3));
988   if (fd >= env->maxFd)
989     env->maxFd = fd + 1;
990   if (fd >= env->maxGFds[group])  {
991     ButFdCallback *newFc = wms_malloc((fd+1)*sizeof(ButFdCallback));
992     for (i = 0;  i < env->maxGFds[group];  ++i)
993       newFc[i] = env->fCallbacks[group][i];
994     for (;  i < fd;  ++i)
995       newFc[i].callback = NULL;
996     if (env->fCallbacks[group] != NULL)
997       wms_free(env->fCallbacks[group]);
998     env->maxGFds[group] = fd + 1;
999     env->fCallbacks[group] = newFc;
1000   }
1001   env->fCallbacks[group][fd].callback = callback;
1002   env->fCallbacks[group][fd].packet = packet;
1003   FD_SET(fd, &env->fMasks[group]);
1004 }
1005 
1006 
butEnv_rmFile(ButEnv * env,int group,int fd)1007 void  butEnv_rmFile(ButEnv *env, int group, int fd)  {
1008   assert((group >= 0) && (group < 3));
1009   assert(MAGIC(env));
1010   assert(fd < env->maxGFds[group]);
1011   env->fCallbacks[group][fd].callback = NULL;
1012   FD_CLR(fd, &env->fMasks[group]);
1013 }
1014 
1015 
serviceXData(void * packet,int fd)1016 static ButOut  serviceXData(void *packet, int fd)  {
1017   ButEnv  *env = packet;
1018 
1019   if (XPending(env->dpy))
1020     return(handleEvent(env));
1021   else
1022     return(0);
1023 }
1024 
1025 
butEnv_imageCreate(ButEnv * env,int w,int h)1026 XImage  *butEnv_imageCreate(ButEnv *env, int w, int h)  {
1027   Display  *dpy = env->dpy;
1028   XImage  *image;
1029 
1030   image = XCreateImage(dpy, DefaultVisual(dpy, DefaultScreen(dpy)),
1031 		       DefaultDepth(dpy, DefaultScreen(dpy)),
1032 		       ZPixmap, 0, NULL, w, h, 32, 0);
1033   image->data = wms_malloc(image->bytes_per_line * h);
1034   return(image);
1035 }
1036 
1037 
butEnv_imageDestroy(XImage * img)1038 void  butEnv_imageDestroy(XImage *img)  {
1039   wms_free(img->data);
1040   img->data = NULL;
1041   XDestroyImage(img);
1042 }
1043 
1044 
1045 #endif  /* X11_DISP */
1046