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