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