1 /*
2  * $Id: connect.c,v 1.1 2005-09-18 22:05:34 dhmunro Exp $
3  * routines to connect to an X11 server
4  */
5 /* Copyright (c) 2005, The Regents of the University of California.
6  * All rights reserved.
7  * This file is part of yorick (http://yorick.sourceforge.net).
8  * Read the accompanying LICENSE file for details.
9  */
10 
11 #include "config.h"
12 #include "playx.h"
13 
14 #include "pstdlib.h"
15 
16 #include <string.h>
17 #include <X11/Xutil.h>
18 #include <X11/keysym.h>
19 
20 static p_scr *x_screen(x_display *xdpy, int number);
21 static int p_try_grays(Display *dpy, Colormap cmap, XColor *color,
22                        int c, int dc);
23 static void x_disconnect(x_display *xdpy);
24 static int x_err_installed = 0;
25 
26 x_display *x_displays = 0;
27 
28 p_scr *
p_multihead(p_scr * other,int number)29 p_multihead(p_scr *other, int number)
30 {
31   x_display *xdpy = other->xdpy;
32   return (xdpy->dpy && number<ScreenCount(xdpy->dpy) && number>0)?
33     x_screen(xdpy, number) : 0;
34 }
35 
36 void (*p_on_connect)(int dis, int fd) = 0;
37 
38 p_scr *
p_connect(char * server_name)39 p_connect(char *server_name)
40 {
41   extern void x_parse_fonts(x_display *xdpy);
42   x_display *xdpy;
43   char *opt;
44   int i;
45   Display *dpy;
46   if (!x_err_installed) {
47     XSetErrorHandler(&x_err_handler);
48     XSetIOErrorHandler(&x_panic);
49     x_err_installed = 1;
50   }
51   dpy = XOpenDisplay(server_name);
52   if (!dpy) return 0;
53   if (p_on_connect) p_on_connect(0, ConnectionNumber(dpy));
54 
55   xdpy = p_malloc(sizeof(x_display));
56   if (!xdpy) return 0;
57   xdpy->panic = 0;
58   xdpy->screens = 0;
59   xdpy->next = 0;
60   xdpy->dpy = dpy;
61   xdpy->wm_protocols = XInternAtom(dpy, "WM_PROTOCOLS", False);
62   xdpy->wm_delete = XInternAtom(dpy, "WM_DELETE_WINDOW", False);
63   xdpy->id2pwin = p_halloc(16);
64 
65   for (i=0 ; i<=P_NONE ; i++) xdpy->cursors[i] = None;
66 
67   xdpy->font = 0;
68   xdpy->unload_font = 1;  /* assume success */
69   for (i=0 ; i<N_FONT_CACHE ; i++) {
70     xdpy->cached[i].f = 0;
71     xdpy->cached[i].font = xdpy->cached[i].pixsize = 0;
72     xdpy->cached[i].next = -1;
73   }
74   xdpy->most_recent = -1;
75   for (i=0 ; i<20 ; i++) {
76     xdpy->available[i].nsizes = 0;
77     xdpy->available[i].sizes = 0;
78     xdpy->available[i].names = 0;
79   }
80   x_parse_fonts(xdpy);  /* see fonts.c */
81 
82   /* find default font */
83 
84   if (x_xfont) {
85     opt = x_xfont;
86   } else {
87     opt = XGetDefault(dpy, "Gist", "boldfont");
88     if (!opt) opt = XGetDefault(dpy, "Gist", "font");
89     if (!opt) opt = XGetDefault(dpy, "Gist", "Font");
90   }
91   if (opt) xdpy->font = XLoadQueryFont(dpy, opt);
92   if (!xdpy->font) xdpy->font = XLoadQueryFont(dpy, "9x15bold");
93   if (!xdpy->font) xdpy->font = XLoadQueryFont(dpy, "8x13bold");
94   if (!xdpy->font) xdpy->font = XLoadQueryFont(dpy, "9x15");
95   if (!xdpy->font) xdpy->font = XLoadQueryFont(dpy, "8x13");
96   if (!xdpy->font) xdpy->font = XLoadQueryFont(dpy, "fixed");
97   if (!xdpy->font) {
98     /* note: section 6.2.2 of O'Reilly volume one promises that
99      * the font associated with the default GC is always loaded */
100     XGCValues values;
101     GC gc = DefaultGC(dpy, DefaultScreen(dpy));
102     xdpy->unload_font = 0;  /* better not try to unload this one */
103     if (XGetGCValues(dpy, gc, GCFont, &values)) {
104       xdpy->font = XQueryFont(dpy, XGContextFromGC(gc));
105       /* XQueryFont returns fid==gc, really need font ID */
106       if (xdpy->font) xdpy->font->fid = values.font;
107     }
108   }
109   if (!xdpy->font) { /* according to O'Reilly(1) 6.2.2, this is impossible */
110     x_disconnect(xdpy);
111     return 0;
112   }
113 
114   xdpy->motion_q = 0;
115 
116   {
117     XModifierKeymap *xmkm = XGetModifierMapping(dpy);
118     int n = xmkm->max_keypermod;
119     KeySym keysym;
120     KeyCode *keys[5];
121     unsigned int states[5];
122     int k, i;
123     keys[0] = xmkm->modifiermap + n*Mod1MapIndex;
124     keys[1] = xmkm->modifiermap + n*Mod2MapIndex;
125     keys[2] = xmkm->modifiermap + n*Mod3MapIndex;
126     keys[3] = xmkm->modifiermap + n*Mod4MapIndex;
127     keys[4] = xmkm->modifiermap + n*Mod5MapIndex;
128     states[0] = Mod1Mask;
129     states[1] = Mod2Mask;
130     states[2] = Mod3Mask;
131     states[3] = Mod4Mask;
132     states[4] = Mod5Mask;
133     xdpy->alt_state = xdpy->meta_state = 0;
134     for (k=0 ; k<5 ; k++) {
135       for (i=0 ; i<n ; i++) {
136         keysym = XKeycodeToKeysym(dpy, keys[k][i], 0);
137         if (keysym==XK_Meta_L || keysym==XK_Meta_R) {
138           xdpy->meta_state = states[k];
139           break;
140         } else if (keysym==XK_Alt_L || keysym==XK_Alt_R) {
141           xdpy->alt_state = states[k];
142           break;
143         }
144       }
145     }
146     XFreeModifiermap(xmkm);
147   }
148 
149   xdpy->sel_owner = 0;
150   xdpy->sel_string = 0;
151   xdpy->n_menus = 0;
152 
153   /* set up X event handler */
154   if (x_wire_events) x_wire_events(xdpy, 0);
155 
156   xdpy->next = x_displays;
157   x_displays = xdpy;
158   return x_screen(xdpy, DefaultScreen(dpy));
159 }
160 
161 /* if this isn't set by p_gui, never reference any of the unix event stuff */
162 void (*x_wire_events)(x_display *xdpy, int disconnect) = 0;
163 
164 /* xdpyndx is index into xdpynative, xdpyplay
165  * this is a (perhaps misguided) attempt to assure that the
166  * xdpynative<->xdpyplay correspondence is always valid:
167  * the two are both set before the xdpyndx index is set;
168  * hopefully setting the index is an atomic operation */
169 static int xdpyndx = 0;
170 static Display *xdpynative[2] = {0,0};
171 static x_display *xdpyplay[2] = {0,0};
172 
173 /* hopefully this is faster than ctx hash, since total number of
174  * dpy pointers is very small (usually just one) */
175 x_display *
x_dpy(Display * dpy)176 x_dpy(Display *dpy)
177 {
178   if (dpy==xdpynative[xdpyndx]) {
179     /* usual case is lots of calls with same dpy, return immediately */
180     return xdpyplay[xdpyndx];
181   } else {
182     x_display *xdpy;
183     int i = 1-xdpyndx;
184     for (xdpy=x_displays ; xdpy ; xdpy=xdpy->next)
185       if (xdpy->dpy == dpy) {
186         xdpynative[i] = dpy;
187         xdpyplay[i] = xdpy;
188         break;
189       }
190     if (xdpy) xdpyndx = i;
191     return xdpy;
192   }
193 }
194 
195 static void
x_disconnect(x_display * xdpy)196 x_disconnect(x_display *xdpy)
197 {
198   Display *dpy = xdpy->dpy;
199   if (xdpyplay[0]==xdpy) {
200     xdpynative[0] = 0;
201     xdpyplay[0] = 0;
202   } else if (xdpyplay[1]==xdpy) {
203     xdpynative[1] = 0;
204     xdpyplay[1] = 0;
205   }
206   if (dpy) {
207     int i, j;
208     x_display **pxdpy = &x_displays;
209     p_hashtab *id2pwin = xdpy->id2pwin;
210     if (!xdpy->panic) {
211       Cursor cur;
212       XFontStruct *font = xdpy->font;
213       if (p_on_connect) p_on_connect(1, ConnectionNumber(dpy));
214       if (font) {
215         xdpy->font = 0;
216         if (xdpy->unload_font) XFreeFont(dpy, font);
217         else XFreeFontInfo((void *)0, font, 1);
218       }
219       for (i=0 ; i<N_FONT_CACHE ; i++) {
220         font = xdpy->cached[i].f;
221         if (!font) continue;
222         xdpy->cached[i].f = 0;
223         XFreeFont(dpy, font);
224       }
225       for (i=0 ; i<=P_NONE ; i++) {
226         cur = xdpy->cursors[i];
227         xdpy->cursors[i] = None;
228         if (cur!=None) XFreeCursor(dpy, cur);
229       }
230     }
231     for (i=0 ; i<20 ; i++) {
232       x_tmpzap(&xdpy->available[i].sizes);
233       if (xdpy->available[i].nsizes) {
234         for (j=0 ; j<xdpy->available[i].nsizes+1 ; j++)
235           x_tmpzap(&xdpy->available[i].names[j]);
236         xdpy->available[i].nsizes = 0;
237       }
238       x_tmpzap(&xdpy->available[i].names);
239     }
240     x_tmpzap(&xdpy->sel_string);
241     if (x_wire_events) x_wire_events(xdpy, 1);
242     if (!xdpy->panic) XCloseDisplay(dpy);
243     while (*pxdpy && *pxdpy!=xdpy) pxdpy = &xdpy->next;
244     if (*pxdpy) *pxdpy = xdpy->next;
245     if (id2pwin) {
246       void (*no_action)(void *) = 0;
247       xdpy->id2pwin = 0;
248       p_hfree(id2pwin, no_action);
249     }
250     xdpy->dpy = 0;
251     if (!xdpy->panic) p_free(xdpy);
252   }
253 }
254 
255 static p_scr *
x_screen(x_display * xdpy,int number)256 x_screen(x_display *xdpy, int number)
257 {
258   char *opt;
259   XColor color;
260   Colormap cmap;
261   XGCValues values;
262   int vclass;
263   Display *dpy = xdpy->dpy;
264   p_scr *s = p_malloc(sizeof(p_scr));
265   if (!s) return 0;
266 
267   s->xdpy = xdpy;
268 
269   s->scr_num = number;
270   s->root = RootWindow(dpy, number);
271   s->width = DisplayWidth(dpy, number);
272   s->height = DisplayHeight(dpy, number);
273   s->depth = DefaultDepth(dpy, number);
274   s->nwins = 0;
275   cmap = DefaultColormap(dpy, number);
276 
277   s->pixels = 0;
278   s->cmap = None;
279   s->free_colors = 0;
280   s->gc = 0;
281   {
282     /* create bitmap (same as X11/bitmaps/gray) for hi/lo effect
283      * -- may not be used, but see x_rotzap */
284     char bits[2];
285     bits[0] = 0x01;  bits[1] = 0x02;
286     s->gray = XCreateBitmapFromData(dpy, s->root, bits, 2, 2);
287   }
288   s->shared = 0;
289 
290   /* interrupt-protected temporaries for rotated text */
291   s->tmp = 0;
292   s->image = 0;
293   s->own_image_data = 0;
294   s->pixmap = None;
295   s->rotgc = 0;
296 
297   /* set up color handling
298    *
299    * - GrayScale and DirectColor models are silly and confusing:
300    *   since the screen can simultaneously display every possible color,
301    *   the changeable colormaps simply change the names of those colors
302    *   (hopefully the server doesn't gratuitously flash all the other
303    *   windows when colormaps are installed)
304    *
305    *   therefore, I create a complete colormap for those cases, and
306    *   arrange for it to be installed for every Gist window -- this
307    *   makes GrayScale equivalent to StaticGray and DirectColor
308    *   equivalent to TrueColor
309    *
310    * - PseudoColor is infinitely more complicated, and requires special
311    *   handling within each window, not here
312    */
313   s->vclass = vclass = DefaultVisual(dpy, number)->class;
314 
315   if (vclass != PseudoColor) {
316     /* pixels[i] is the pixel value for gray (i=0 black, i=255 white)
317      * - for TrueColor or DirectColor, an arbitrary color can be
318      *   constructed by using the s, g, and b masks to build the
319      *   required pixel:
320      *   (pixels[r]&rmask)|(pixels[g]&gmask)|(pixels[b]&bmask)
321      */
322     int i;
323     int mutible = (vclass==DirectColor || vclass==GrayScale);
324     p_col_t *pixels = s->pixels = p_malloc(sizeof(unsigned long)*256);
325     Visual *visual = DefaultVisual(dpy, number);
326 
327     if (!pixels) {
328       p_disconnect(s);
329       return 0;
330     }
331 
332     s->rmask = visual->red_mask;
333     s->gmask = visual->green_mask;
334     s->bmask = visual->blue_mask;
335 
336     if (mutible)
337       cmap = s->cmap = XCreateColormap(dpy, s->root, visual, AllocNone);
338 
339     color.flags = color.pad = 0;
340     color.pixel = 0;
341 
342     /* this is very slow, but there doesn't seem to be any faster way
343      * to get the correspondence between pixel values and rgb values */
344     for (i=0 ; i<256 ; i++) {
345       color.red = color.green = color.blue = (i<<8);
346       if (XAllocColor(dpy, cmap, &color)) {
347         pixels[i] = color.pixel;
348         if (!mutible) XFreeColors(dpy, cmap, &color.pixel, 1, 0UL);
349       } else {
350         /* should be impossible, but probably isn't (serious error) */
351         pixels[i] = i;
352       }
353     }
354 
355   } else { /* PseudoColor */
356     s->rmask = s->gmask = s->bmask = 0;
357   }
358 
359   /* get standard colors */
360 
361   if (x_foreground) {
362     opt = x_foreground;
363   } else {
364     opt = XGetDefault(dpy, "Gist", "foreground");
365     if (!opt) opt = XGetDefault(dpy, "Gist", "Foreground");
366   }
367   if (opt && XAllocNamedColor(dpy, cmap, opt,
368                               &s->colors[1], &color)) {
369       s->free_colors |= 2;
370   } else {
371     s->colors[1].pixel = BlackPixel(dpy, number);
372     XQueryColor(dpy, cmap, &s->colors[1]);
373   }
374 
375   if (x_background) {
376     opt = x_background;
377   } else {
378     opt = XGetDefault(dpy, "Gist", "background");
379     if (!opt) opt = XGetDefault(dpy, "Gist", "Background");
380   }
381   if (opt && XAllocNamedColor(dpy, cmap, opt,
382                               &s->colors[0], &color)) {
383       s->free_colors |= 1;
384   } else {
385     long bright = (long)s->colors[1].red + (long)s->colors[1].green +
386       (long)s->colors[1].blue;
387     s->colors[0].pixel=
388       bright<98302? WhitePixel(dpy, number) : BlackPixel(dpy, number);
389     XQueryColor(dpy, cmap, &s->colors[0]);
390   }
391 
392   s->colors[2].pixel = BlackPixel(dpy, number);
393   XQueryColor(dpy, cmap, &s->colors[2]);
394   s->colors[3].pixel = WhitePixel(dpy, number);
395   XQueryColor(dpy, cmap, &s->colors[3]);
396 
397   if (XAllocNamedColor(dpy, cmap, "red", &s->colors[4], &color))
398     s->free_colors |= 16;
399   else
400     s->colors[4] = s->colors[1];
401   if (XAllocNamedColor(dpy, cmap, "green", &s->colors[5], &color))
402     s->free_colors |= 32;
403   else
404     s->colors[5] = s->colors[1];
405   if (XAllocNamedColor(dpy, cmap, "blue", &s->colors[6], &color))
406     s->free_colors |= 64;
407   else
408     s->colors[6] = s->colors[1];
409   if (XAllocNamedColor(dpy, cmap, "cyan", &s->colors[7], &color))
410     s->free_colors |= 128;
411   else
412     s->colors[7] = s->colors[1];
413   if (XAllocNamedColor(dpy, cmap, "magenta", &s->colors[8], &color))
414     s->free_colors |= 256;
415   else
416     s->colors[8] = s->colors[1];
417   if (XAllocNamedColor(dpy, cmap, "yellow", &s->colors[9], &color))
418     s->free_colors |= 512;
419   else
420     s->colors[9] = s->colors[1];
421 
422   /* initialize generic graphics context and state */
423 
424   values.font = xdpy->font->fid;
425   values.foreground = s->gc_color = s->colors[1].pixel;
426   values.background = s->colors[0].pixel;
427   values.join_style = JoinRound;
428   s->gc_font = P_GUI_FONT | P_BOLD;  /* not any font */
429   s->gc_width = 0;
430   s->gc_type = 1023; /* CapButt is default, not used by Gist */
431   s->gc_fillstyle = FillSolid;
432   s->gc_w_clip = 0;
433   s->gc = XCreateGC(dpy, s->root,
434                     GCForeground | GCBackground | GCFont | GCJoinStyle,
435                     &values);
436 
437   /* get special GUI colors */
438 
439   if (p_try_grays(dpy, cmap, &s->colors[10], 100, 11))
440     s->free_colors |= 1024;
441   else
442     s->colors[10] = s->colors[2];
443   if (p_try_grays(dpy, cmap, &s->colors[11], 150, 11)) {
444     s->free_colors |= 2048;
445     if (!(s->free_colors&1024))
446       s->colors[10] = s->colors[11];
447   } else {
448     s->colors[11] = s->colors[10];
449   }
450   if (p_try_grays(dpy, cmap, &s->colors[13], 214, 11))
451     s->free_colors |= 8192;
452   else
453     s->colors[13] = s->colors[3];
454   if (p_try_grays(dpy, cmap, &s->colors[12], 190, 11)) {
455     s->free_colors |= 4096;
456     if (!(s->free_colors&4096))
457       s->colors[13] = s->colors[12];
458   } else {
459     s->colors[12] = s->colors[13];
460   }
461 
462   /* set up special graphics contexts for GUI */
463 
464   s->gui_flags = 0;
465   if (!(s->free_colors&1024) && !(s->free_colors&2048) &&
466       !(s->free_colors&4096) && !(s->free_colors&8192)) {
467     /* if got no grays at all, make middle two stippled */
468     s->gui_flags |= 1;
469     XSetStipple(dpy, s->gc, s->gray);
470   }
471 
472   if (p_signalling) p_abort();
473 
474   s->next = xdpy->screens;
475   xdpy->screens = s;
476   return s;
477 }
478 
479 static int
p_try_grays(Display * dpy,Colormap cmap,XColor * color,int c,int dc)480 p_try_grays(Display *dpy, Colormap cmap, XColor *color, int c, int dc)
481 {
482   int i;
483   for (i=0 ; dc-- ; i=((i<0)?1:-1)-i) {
484     color->red = color->green = color->blue = ((c+i)<<8);
485     if (XAllocColor(dpy, cmap, color))
486       return 1;
487   }
488   return 0;
489 }
490 
491 void (*x_on_panic)(p_scr *s) = 0;
492 
493 void
p_disconnect(p_scr * s)494 p_disconnect(p_scr *s)
495 {
496   int i;
497   x_display *xdpy = s->xdpy;
498   Display *dpy = xdpy? xdpy->dpy : 0;
499 
500   x_tmpzap(&s->pixels);
501   x_rotzap(s);
502 
503   if (dpy && !xdpy->panic) {
504     Colormap cmap = s->cmap;
505     if (cmap==None) cmap = DefaultColormap(dpy, s->scr_num);
506     for (i=0 ; s->free_colors && i<14 ; i++) {
507       if (s->free_colors & (1<<i)) {
508         s->free_colors &= ~(1<<i);
509         XFreeColors(dpy, cmap, &s->colors[i].pixel, 1, 0UL);
510       }
511     }
512     x_nuke_shared(s);
513 
514     x_cmzap(dpy, &s->cmap);
515     x_pxzap(dpy, &s->gray);
516     x_gczap(dpy, &s->gc);
517   }
518 
519   if (xdpy) {
520     p_scr **pthis = &xdpy->screens;
521     while (*pthis && *pthis!=s) pthis = &(*pthis)->next;
522     if (*pthis) *pthis = s->next;
523     if (xdpy->panic==1 && x_on_panic) x_on_panic(s);
524     if (!xdpy->screens) x_disconnect(xdpy);
525     s->xdpy = 0;
526   }
527   p_free(s);
528 }
529 
530 int
p_sshape(p_scr * s,int * width,int * height)531 p_sshape(p_scr *s, int *width, int *height)
532 {
533   *width = s->width;
534   *height = s->height;
535   return s->depth;
536 }
537 
538 int
p_wincount(p_scr * s)539 p_wincount(p_scr *s)
540 {
541   return s? s->nwins : 0;
542 }
543 
544 /* ------------------------------------------------------------------------ */
545 
546 void
x_rotzap(p_scr * s)547 x_rotzap(p_scr *s)
548 {
549   x_display *xdpy = s->xdpy;
550   Display *dpy = xdpy->dpy;
551   x_tmpzap(&s->tmp);
552   if (!xdpy->panic) x_gczap(dpy, &s->rotgc);
553   x_imzap(s);
554   if (!xdpy->panic && s->pixmap!=None) {
555     /* note -- XSetStipple gives error if pixmap is None?? */
556     if (s->gray!=None) XSetStipple(dpy, s->gc, s->gray);
557     XSetTSOrigin(dpy, s->gc, 0, 0);
558     x_pxzap(dpy, &s->pixmap);
559   }
560 }
561 
562 void
x_tmpzap(void * p)563 x_tmpzap(void *p)
564 {
565   void **ptmp = p;
566   void *tmp = *ptmp;
567   if (tmp) {
568     *ptmp = 0;
569     p_free(tmp);
570   }
571 }
572 
573 void
x_gczap(Display * dpy,GC * pgc)574 x_gczap(Display *dpy, GC *pgc)
575 {
576   GC gc = *pgc;
577   if (gc) {
578     *pgc = 0;
579     XFreeGC(dpy, gc);
580   }
581 }
582 
583 void
x_imzap(p_scr * s)584 x_imzap(p_scr *s)
585 {
586   XImage *im = s->image;
587   if (im) {
588     if (s->own_image_data) {
589       void *data = im->data;
590       if (data) {
591         im->data = 0;
592         p_free(data);
593       }
594     }
595     s->image = 0;
596     XDestroyImage(im);
597   }
598 }
599 
600 void
x_pxzap(Display * dpy,Pixmap * ppx)601 x_pxzap(Display *dpy, Pixmap *ppx)
602 {
603   Pixmap px = *ppx;
604   if (px!=None) {
605     *ppx = None;
606     XFreePixmap(dpy, px);
607   }
608 }
609 
610 void
x_cmzap(Display * dpy,Colormap * pcm)611 x_cmzap(Display *dpy, Colormap *pcm)
612 {
613   Colormap cm = *pcm;
614   if (cm!=None) {
615     *pcm = None;
616     XFreeColormap(dpy, cm);
617   }
618 }
619