1 /* grX11su3.c -
2  *
3  *     *********************************************************************
4  *     * Copyright (C) 1985, 1990 Regents of the University of California. *
5  *     * Permission to use, copy, modify, and distribute this              *
6  *     * software and its documentation for any purpose and without        *
7  *     * fee is hereby granted, provided that the above copyright          *
8  *     * notice appear in all copies.  The University of California        *
9  *     * makes no representations about the suitability of this            *
10  *     * software for any purpose.  It is provided "as is" without         *
11  *     * express or implied warranty.  Export of this software outside     *
12  *     * of the United States of America may require an export license.    *
13  *     *********************************************************************
14  *
15  * This file contains additional functions to manipulate an X window system
16  * color display.  Included here are device-dependent routines to draw and
17  * erase text and draw a grid.
18  *
19  */
20 
21 #include <stdio.h>
22 #include <string.h>
23 #include <math.h>
24 #include <X11/Xlib.h>
25 
26 #include "utils/magic.h"
27 #include "utils/geometry.h"
28 #include "graphics/graphics.h"
29 #include "windows/windows.h"
30 #include "graphics/graphicsInt.h"
31 #include "textio/textio.h"
32 #include "utils/signals.h"
33 #include "utils/utils.h"
34 #include "utils/hash.h"
35 #include "dbwind/dbwind.h"
36 #include "database/fonts.h"
37 #include "grX11Int.h"
38 
39 /* locals */
40 
41 static XFontStruct *grXFonts[4];
42 #define	grSmallFont	grXFonts[0]
43 #define	grMediumFont	grXFonts[1]
44 #define	grLargeFont	grXFonts[2]
45 #define	grXLargeFont	grXFonts[3]
46 
47 
48 
49 /*---------------------------------------------------------
50  * grxDrawGrid:
51  *	grxDrawGrid adds a grid to the grid layer, using the current
52  *	write mask and color.
53  *
54  * Results:
55  *	TRUE is returned normally.  However, if the grid gets too small
56  *	to be useful, then nothing is drawn and FALSE is returned.
57  *
58  * Side Effects:    None.
59  *---------------------------------------------------------
60  */
61 
62 #define GR_NUM_GRIDS 64
63 
64 bool
grx11DrawGrid(prect,outline,clip)65 grx11DrawGrid (prect, outline, clip)
66     Rect *prect;			/* A rectangle that forms the template
67 			         * for the grid.  Note:  in order to maintain
68 			         * precision for the grid, the rectangle
69 			         * coordinates are specified in units of
70 			         * screen coordinates multiplied by SUBPIXEL.
71 			         */
72     int outline;		/* the outline style */
73     Rect *clip;			/* a clipping rectangle */
74 {
75     int xsize, ysize;
76     int x, y;
77     int xstart, ystart;
78     XSegment seg[GR_NUM_GRIDS];
79     int snum, low, hi, shifted;
80 
81     xsize = prect->r_xtop - prect->r_xbot;
82     ysize = prect->r_ytop - prect->r_ybot;
83     if (!xsize || !ysize || GRID_TOO_SMALL(xsize, ysize))
84 	return FALSE;
85 
86     xstart = prect->r_xbot % xsize;
87     while (xstart < clip->r_xbot << SUBPIXELBITS) xstart += xsize;
88     ystart = prect->r_ybot % ysize;
89     while (ystart < clip->r_ybot << SUBPIXELBITS) ystart += ysize;
90 
91     grx11SetLineStyle(outline);
92 
93     snum = 0;
94     low = grMagicToX(clip->r_ybot);
95     hi = grMagicToX(clip->r_ytop);
96     for (x = xstart; x < (clip->r_xtop + 1) << SUBPIXELBITS; x += xsize)
97     {
98         if (snum == GR_NUM_GRIDS)
99 	{
100 	    XDrawSegments(grXdpy, grCurrent.window, grGCDraw, seg, snum);
101 	    snum = 0;
102 	}
103 	shifted = x >> SUBPIXELBITS;
104 	seg[snum].x1 = shifted;
105 	seg[snum].y1 = low;
106 	seg[snum].x2 = shifted;
107 	seg[snum].y2 = hi;
108 	snum++;
109     }
110     XDrawSegments(grXdpy, grCurrent.window, grGCDraw, seg, snum);
111 
112     snum = 0;
113     low = clip->r_xbot;
114     hi = clip->r_xtop;
115     for (y = ystart; y < (clip->r_ytop + 1) << SUBPIXELBITS; y += ysize)
116     {
117         if (snum == GR_NUM_GRIDS)
118 	{
119 	    XDrawSegments(grXdpy, grCurrent.window, grGCDraw, seg, snum);
120 	    snum = 0;
121 	}
122 	shifted = grMagicToX(y >> SUBPIXELBITS);
123 	seg[snum].x1 = low;
124 	seg[snum].y1 = shifted;
125 	seg[snum].x2 = hi;
126 	seg[snum].y2 = shifted;
127 	snum++;
128     }
129     XDrawSegments(grXdpy, grCurrent.window, grGCDraw, seg, snum);
130     return TRUE;
131 }
132 
133 
134 /*---------------------------------------------------------
135  * grxLoadFont
136  *	This local routine loads the X fonts used by Magic.
137  *
138  * Results:	Success/failure
139  *
140  * Side Effects:    None.
141  *---------------------------------------------------------
142  */
143 
144 bool
grx11LoadFont()145 grx11LoadFont()
146 {
147     static char *fontnames[4] = {
148       X_FONT_SMALL,
149       X_FONT_MEDIUM,
150       X_FONT_LARGE,
151       X_FONT_XLARGE };
152     static char *optionnames[4] = {
153       "small",
154       "medium",
155       "large",
156       "xlarge"};
157 
158     int i;
159     char *unable = "Unable to load font";
160 
161     for (i=0; i!= 4; i++)
162     {
163     	 char	*s = XGetDefault(grXdpy,"magic",optionnames[i]);
164 	 if (s) fontnames[i] = s;
165          if ((grXFonts[i] = XLoadQueryFont(grXdpy, fontnames[i])) == NULL)
166          {
167 	      TxError("%s %s\n",unable,fontnames[i]);
168               if ((grXFonts[i]= XLoadQueryFont(grXdpy,GR_DEFAULT_FONT))==NULL)
169 	      {
170 	           TxError("%s %s\n",unable,GR_DEFAULT_FONT);
171 		   return FALSE;
172 	      }
173          }
174     }
175     return TRUE;
176 }
177 
178 
179 /*---------------------------------------------------------
180  * grxSetCharSize:
181  *	This local routine sets the character size in the display,
182  *	if necessary.
183  *
184  * Results:	None.
185  *
186  * Side Effects:    None.
187  *---------------------------------------------------------
188  */
189 
190 void
grx11SetCharSize(size)191 grx11SetCharSize (size)
192     int size;		/* Width of characters, in pixels (6 or 8). */
193 {
194     grCurrent.fontSize = size;
195     switch (size)
196     {
197 	case GR_TEXT_DEFAULT:
198 	case GR_TEXT_SMALL:
199 	    grCurrent.font = grSmallFont;
200 	    break;
201 	case GR_TEXT_MEDIUM:
202 	    grCurrent.font = grMediumFont;
203 	    break;
204 	case GR_TEXT_LARGE:
205 	    grCurrent.font = grLargeFont;
206 	    break;
207 	case GR_TEXT_XLARGE:
208 	    grCurrent.font = grXLargeFont;
209 	    break;
210 	default:
211 	    TxError("%s%d\n", "grx11SetCharSize: Unknown character size ",
212 		size );
213 	    break;
214     }
215 }
216 
217 
218 /*
219  * ----------------------------------------------------------------------------
220  * GrXTextSize --
221  *
222  *	Determine the size of a text string.
223  *
224  * Results:
225  *	None.
226  *
227  * Side effects:
228  *	A rectangle is filled in that is the size of the text in pixels.
229  *	The origin (0, 0) of this rectangle is located on the baseline
230  *	at the far left side of the string.
231  * ----------------------------------------------------------------------------
232  */
233 
234 void
GrX11TextSize(text,size,r)235 GrX11TextSize(text, size, r)
236     char *text;
237     int size;
238     Rect *r;
239 {
240     XCharStruct overall;
241     XFontStruct *font;
242     int dir,fa,fd;
243 
244     switch (size) {
245     case GR_TEXT_DEFAULT:
246     case GR_TEXT_SMALL:
247 	font = grSmallFont;
248 	break;
249     case GR_TEXT_MEDIUM:
250 	font = grMediumFont;
251 	break;
252     case GR_TEXT_LARGE:
253 	font = grLargeFont;
254 	break;
255     case GR_TEXT_XLARGE:
256 	font = grXLargeFont;
257 	break;
258     default:
259 	TxError("%s%d\n", "GrX11TextSize: Unknown character size ",
260 		size );
261 	break;
262     }
263     if (font == NULL) return;
264     XTextExtents(font, text, strlen(text), &dir, &fa, &fd, &overall);
265     r->r_ytop = overall.ascent;
266     r->r_ybot = -overall.descent;
267     r->r_xtop = overall.width - overall.lbearing;
268     r->r_xbot = -overall.lbearing - 1;
269 }
270 
271 
272 /*
273  * ----------------------------------------------------------------------------
274  * GrXReadPixel --
275  *
276  *	Read one pixel from the screen.
277  *
278  * Results:
279  *	An integer containing the pixel's color.
280  *
281  * Side effects:
282  *	none.
283  *
284  * ----------------------------------------------------------------------------
285  */
286 
287 int
GrX11ReadPixel(w,x,y)288 GrX11ReadPixel (w, x, y)
289     MagWindow *w;
290     int x,y;		/* the location of a pixel in screen coords */
291 {
292     XImage *image;
293     unsigned long value;
294     XWindowAttributes	att;
295 
296     XGetWindowAttributes(grXdpy,grCurrent.window, &att);
297     if ( x < 0 || x >= att.width || grMagicToX(y) < 0
298 		|| grMagicToX(y) >= att.height)
299 	return(0);
300     image = XGetImage(grXdpy, grCurrent.window, x, grMagicToX(y), 1, 1,
301 		      ~0, ZPixmap);
302     value = XGetPixel(image, 0, 0);
303     return (value & (1 << grDisplay.depth) - 1);
304 }
305 
306 
307 /*
308  * ----------------------------------------------------------------------------
309  * GrXBitBlt --
310  *
311  *	Copy information in bit block transfers.
312  *
313  * Results:
314  *	None.
315  *
316  * Side effects:
317  *	changes the screen.
318  * ----------------------------------------------------------------------------
319  */
320 
321 void
GrX11BitBlt(r,p)322 GrX11BitBlt(r, p)
323     Rect *r;
324     Point *p;
325 {
326     Drawable wind = (Drawable)grCurrent.window;
327 
328     XCopyArea(grXdpy, wind, wind, grGCCopy,
329 	      r->r_xbot, grMagicToX(r->r_ytop),
330 	      r->r_xtop - r->r_xbot + 1, r->r_ytop - r->r_ybot + 1,
331 	      p->p_x, grMagicToX(p->p_y));
332 }
333 
334 static GC grXcopyGC = (GC)NULL;
335 
336 /*
337  * ----------------------------------------------------------------------------
338  * grx11FreeBackingStore --
339  *	Free up Pixmap memory for a backing store cell.
340  *
341  * Results:
342  *	None.
343  *
344  * Side effects:
345  *	memory Free'd
346  * ----------------------------------------------------------------------------
347  */
348 
349 void
grx11FreeBackingStore(MagWindow * window)350 grx11FreeBackingStore(MagWindow *window)
351 {
352     Pixmap pmap = (Pixmap)window->w_backingStore;
353     if (pmap == (Pixmap)NULL) return;
354     XFreePixmap(grXdpy, pmap);
355     window->w_backingStore = (ClientData)NULL;
356     /* XFreeGC(grXdpy, grXcopyGC); */
357     /* TxPrintf("grx11FreeBackingStore called\n"); */
358 }
359 
360 /*
361  * ----------------------------------------------------------------------------
362  * grx11CreateBackingStore --
363  *	Create Pixmap memory for a backing store cell and copy data
364  *	from the window into it.
365  *
366  * Results:
367  *	None.
368  *
369  * Side effects:
370  *	memory Allocated.
371  *
372  * ----------------------------------------------------------------------------
373  */
374 
375 void
grx11CreateBackingStore(MagWindow * w)376 grx11CreateBackingStore(MagWindow *w)
377 {
378     Pixmap pmap;
379     Window wind = (Window)w->w_grdata;
380     unsigned int width, height;
381     GC gc;
382     XGCValues gcValues;
383     int grDepth;
384 
385     /* ignore for all windows except layout */
386     if (w->w_client != DBWclientID) return;
387 
388     /* deferred */
389     if (w->w_grdata == (Window)NULL) return;
390 
391     width = w->w_screenArea.r_xtop - w->w_screenArea.r_xbot;
392     height = w->w_screenArea.r_ytop - w->w_screenArea.r_ybot;
393 
394     if (w->w_backingStore != (ClientData)NULL) grx11FreeBackingStore(w);
395 
396     if (grXcopyGC == (GC)NULL)
397     {
398 	gcValues.graphics_exposures = FALSE;
399 	grXcopyGC = XCreateGC(grXdpy, wind, GCGraphicsExposures, &gcValues);
400     }
401 
402     grDepth = grDisplay.depth;
403     if(grClass == 3) grDepth = 8;  /* Needed since grDisplay.depth is reset
404 				     to 7 if Pseudocolor      */
405 
406     pmap = XCreatePixmap(grXdpy, wind, width, height, grDepth);
407     w->w_backingStore = (ClientData)pmap;
408 
409     /* TxPrintf("grx11CreateBackingStore area %d %d %d %d\n",
410 	w->w_screenArea.r_xbot, w->w_screenArea.r_ybot,
411 	w->w_screenArea.r_xtop, w->w_screenArea.r_ytop); */
412 }
413 
414 /*
415  * ----------------------------------------------------------------------------
416  * grx11GetBackingStore --
417  *	Copy data from a backing store Pixmap into the indicated window.
418  *
419  * Results:
420  *	TRUE if backing store was copied successfully, FALSE if not.
421  *
422  * Side effects:
423  *	Data copied into Pixmap memory.
424  *
425  * ----------------------------------------------------------------------------
426  */
427 
428 bool
grx11GetBackingStore(MagWindow * w,Rect * area)429 grx11GetBackingStore(MagWindow *w, Rect *area)
430 {
431     Pixmap pmap;
432     Window wind = (Window)w->w_grdata;
433     unsigned int width, height;
434     int ybot;
435     int xoff, yoff;
436     Rect r;
437 
438     pmap = (Pixmap)w->w_backingStore;
439     if (pmap == (Pixmap)NULL)
440 	return FALSE;
441 
442     /* Make a local copy of area so we don't disturb the original */
443     r = *area;
444     GeoClip(&r, &(w->w_screenArea));
445 
446     width = r.r_xtop - r.r_xbot;
447     height = r.r_ytop - r.r_ybot;
448     ybot = grMagicToX(r.r_ytop);
449 
450     xoff = w->w_screenArea.r_xbot - w->w_allArea.r_xbot;
451     yoff = w->w_allArea.r_ytop - w->w_screenArea.r_ytop;
452 
453     XCopyArea(grXdpy, pmap, wind, grXcopyGC, r.r_xbot - xoff, ybot - yoff,
454 		width, height, r.r_xbot, ybot);
455 
456     /* TxPrintf("grx11GetBackingStore %d %d %d %d\n",
457 		r.r_xbot, r.r_ybot, r.r_xtop, r.r_ytop); */
458     return TRUE;
459 }
460 
461 /*
462  * ----------------------------------------------------------------------------
463  * grx11ScrollBackingStore --
464  *	Enable fast scrolling by shifting part of the backing store
465  *	from one position to another, with the amount of shift indicated
466  *	by the X and/or Y value of the indicated point.
467  *
468  * Results:
469  *	TRUE on success, FALSE on failure.
470  *
471  * Side effects:
472  *	Data shifted in Pixmap memory.
473  *
474  * ----------------------------------------------------------------------------
475  */
476 
477 bool
grx11ScrollBackingStore(MagWindow * w,Point * shift)478 grx11ScrollBackingStore(MagWindow *w, Point *shift)
479 {
480     Pixmap pmap;
481     unsigned int width, height;
482     int xorigin, yorigin, xshift, yshift;
483 
484     pmap = (Pixmap)w->w_backingStore;
485     if (pmap == (Pixmap)NULL)
486     {
487 	TxPrintf("grx11ScrollBackingStore %d %d failure\n",
488 		shift->p_x, shift->p_y);
489 	return FALSE;
490     }
491 
492     width = w->w_screenArea.r_xtop - w->w_screenArea.r_xbot;
493     height = w->w_screenArea.r_ytop - w->w_screenArea.r_ybot;
494     xorigin = 0;
495     yorigin = 0;
496     xshift = shift->p_x;
497     yshift = -shift->p_y;
498 
499     if (xshift > 0)
500 	width -= xshift;
501     else if (xshift < 0)
502     {
503 	width += xshift;
504 	xorigin = -xshift;
505 	xshift = 0;
506     }
507     if (yshift > 0)
508 	height -= yshift;
509     else if (yshift < 0)
510     {
511 	height += yshift;
512 	yorigin = -yshift;
513 	yshift = 0;
514     }
515 
516     XCopyArea(grXdpy, pmap, pmap, grXcopyGC, xorigin, yorigin, width, height,
517 		xshift, yshift);
518 
519     /* TxPrintf("grx11ScrollBackingStore %d %d\n", shift->p_x, shift->p_y); */
520     return TRUE;
521 }
522 
523 /*
524  * ----------------------------------------------------------------------------
525  * grx11PutBackingStore --
526  *	Copy data from the window into backing store.
527  *
528  * Results:
529  *	None.
530  *
531  * Side effects:
532  *	Graphics drawing into the window.
533  * ----------------------------------------------------------------------------
534  */
535 
536 void
grx11PutBackingStore(MagWindow * w,Rect * area)537 grx11PutBackingStore(MagWindow *w, Rect *area)
538 {
539     Pixmap pmap = (Pixmap)w->w_backingStore;
540     Window wind = (Window)w->w_grdata;
541     unsigned int width, height;
542     int ybot, xoff, yoff;
543 
544     if (pmap == (Pixmap)NULL) return;
545 
546     /* Attempting to write backing store into an obscured	*/
547     /* window immediately invalidates everything in backing	*/
548     /* store.  This is extreme, but is much simpler and under	*/
549     /* normal conditions faster than tracking all obscured	*/
550     /* areas separately.					*/
551 
552     if (w->w_flags & WIND_OBSCURED)
553     {
554 	grx11FreeBackingStore(w);
555 	w->w_backingStore = (ClientData)NULL;
556 	return;
557     }
558 
559     width = area->r_xtop - area->r_xbot;
560     height = area->r_ytop - area->r_ybot;
561     ybot = grMagicToX(area->r_ytop);
562 
563     xoff = w->w_screenArea.r_xbot - w->w_allArea.r_xbot;
564     yoff = w->w_allArea.r_ytop - w->w_screenArea.r_ytop;
565 
566     XCopyArea(grXdpy, wind, pmap, grXcopyGC, area->r_xbot, ybot,
567 		width, height, area->r_xbot - xoff, ybot - yoff);
568 
569     /* TxPrintf("grx11PutBackingStore %d %d %d %d\n",
570 		area->r_xbot, area->r_ybot, area->r_xtop, area->r_ytop); */
571 }
572 
573 /*
574  * ----------------------------------------------------------------------------
575  * GrX11RectConvert --
576  *	Convert a magic rectangle into an X11 rectangle
577  *	(Both passed as pointers)
578  *
579  * Results:
580  *	None.
581  *
582  * Side effects:
583  *	Converted value returned in xr.
584  * ----------------------------------------------------------------------------
585  */
586 
587 void
grx11RectConvert(mr,xr)588 grx11RectConvert(mr, xr)
589     Rect *mr;
590     XRectangle *xr;
591 {
592 	xr->x = mr->r_xbot;
593 	xr->y = grMagicToX(mr->r_ytop);
594 	xr->width = mr->r_xtop - mr->r_xbot + 1;
595 	xr->height = mr->r_ytop - mr->r_ybot + 1;
596 }
597 
598 /*
599  *---------------------------------------------------------
600  * grx11FontText:
601  *
602  *	This routine is a fancier version of grx11PutText used for
603  *	drawing vector outline fonts from the fontList records.
604  *
605  *---------------------------------------------------------
606  */
607 
608 void
grx11FontText(text,font,size,rotate,pos,clip,obscure)609 grx11FontText(text, font, size, rotate, pos, clip, obscure)
610     char *text;
611     int font;
612     int size;			/* pixel size of the text */
613     int rotate;			/* text rotation */
614     Point *pos;			/* text base position */
615     Rect *clip;
616     LinkedRect *obscure;
617 {
618     char *tptr;
619     FontChar *ccur, *clist;
620     Point *coffset, *tp, loffset, locoffset, corners[4], lpos;
621     Rect *cbbox, charbbox, *frect;
622     int np, i, j, w, h, llx, lly, baseline;
623     XPoint *xp;
624     Pixmap pxm;
625     double fscale, scx, scy, tmpx, tmpy, rrad, cr, sr;
626     static GC fontgc = (GC)NULL;
627 
628     frect = &DBFontList[font]->mf_extents;
629     fscale = (double)size / (double)frect->r_ytop;
630     rrad = (double)rotate * 0.0174532925;
631     cr = cos(rrad);
632     sr = sin(rrad);
633     lpos = GeoOrigin;
634 
635     /* 1st pass: find the descent of the string */
636 
637     baseline = 0;
638     for (tptr = text; *tptr != '\0'; tptr++)
639     {
640         DBFontChar(font, *tptr, NULL, NULL, &cbbox);
641 	if (cbbox->r_ybot < -baseline)
642 	    baseline = -cbbox->r_ybot;
643     }
644     baseline = (int)((double)baseline * fscale);
645 
646     for (tptr = text; *tptr != '\0'; tptr++)
647     {
648 	scx = (double)lpos.p_x * fscale;
649 	scy = (double)lpos.p_y * fscale;
650 
651 	tmpx = scx * cr + scy * sr;
652 	tmpy = scy * cr - scx * sr;
653 
654 	loffset.p_x = pos->p_x + (int)round(tmpx);
655 	loffset.p_y = grMagicToX(pos->p_y + baseline) + (int)round(tmpy);
656 
657         DBFontChar(font, *tptr, &clist, &coffset, &cbbox);
658 	np = 0;
659 	for (ccur = clist; ccur != NULL; ccur = ccur->fc_next)
660 	    np += ccur->fc_numpoints;
661 
662 	xp = (XPoint *)mallocMagic(np * sizeof(XPoint));
663 
664 	j = 0;
665 	for (ccur = clist; ccur != NULL; ccur = ccur->fc_next)
666 	{
667 	    tp = ccur->fc_points;
668 	    for (i = 0; i < ccur->fc_numpoints; i++, j++)
669 	    {
670 		scx = (double)tp[i].p_x * fscale;
671 		scy = (double)tp[i].p_y * fscale;
672 
673 		tmpx = scx * cr - scy * sr;
674 		tmpy = scx * sr + scy * cr;
675 
676 		xp[j].x = (int)round(tmpx);
677 		xp[j].y = (int)round(tmpy);
678 
679 		/* Initialize bounding box */
680 		if (j == 0)
681 		{
682 		    charbbox.r_xbot = charbbox.r_xtop = xp[j].x;
683 		    charbbox.r_ybot = charbbox.r_ytop = xp[j].y;
684 		}
685 		else
686 		{
687 		    if (xp[j].x < charbbox.r_xbot)
688 			charbbox.r_xbot = xp[j].x;
689 		    else if (xp[j].x > charbbox.r_xtop)
690 			charbbox.r_xtop = xp[j].x;
691 		    if (xp[j].y < charbbox.r_ybot)
692 			charbbox.r_ybot = xp[j].y;
693 		    else if (xp[j].y > charbbox.r_ytop)
694 			charbbox.r_ytop = xp[j].y;
695 		}
696 	    }
697 	}
698 
699 	/* Create a bitmap */
700 
701 	w = charbbox.r_xtop - charbbox.r_xbot + 1;
702 	h = charbbox.r_ytop - charbbox.r_ybot + 1;
703 
704 	/* Adjust all points to the bounding box origin, and invert Y */
705 	for (j = 0; j < np; j++)
706 	{
707 	    xp[j].x -= charbbox.r_xbot;
708 	    xp[j].y = charbbox.r_ytop - xp[j].y;
709 	}
710 
711 	pxm = XCreatePixmap(grXdpy, grCurrent.window, w, h, 1);
712 
713 	if (fontgc == (GC)NULL)
714 	{
715 	    XGCValues values;
716 	    values.foreground = 0;
717 	    values.background = 0;
718 	    fontgc = XCreateGC(grXdpy, pxm, GCForeground | GCBackground, &values);
719 	}
720 
721 	locoffset.p_x = loffset.p_x + charbbox.r_xbot;
722 	locoffset.p_y = loffset.p_y - charbbox.r_ytop;
723 
724 	XSetForeground(grXdpy, fontgc, 0);
725 	XSetFunction(grXdpy, fontgc, GXcopy);
726 	XFillRectangle(grXdpy, pxm, fontgc, 0, 0, w, h);
727 	XSetFunction(grXdpy, fontgc, GXxor);
728 	XSetForeground(grXdpy, fontgc, 1);
729 
730 	j = 0;
731 	for (ccur = clist; ccur != NULL; ccur = ccur->fc_next)
732 	{
733 	    np = ccur->fc_numpoints;
734 	    XFillPolygon(grXdpy, pxm, fontgc, &xp[j], np, Complex, CoordModeOrigin);
735 	    j += np;
736 	}
737 	freeMagic((char *)xp);
738 
739 	XSetClipMask(grXdpy, grGCText, pxm);
740 	XSetClipOrigin(grXdpy, grGCText, locoffset.p_x, locoffset.p_y);
741 	XFillRectangle(grXdpy, grCurrent.window, grGCText, locoffset.p_x,
742 		locoffset.p_y, w, h);
743 
744 	lpos.p_x += coffset->p_x;
745 	lpos.p_y += coffset->p_y;
746 
747         XFreePixmap(grXdpy, pxm);
748     }
749 }
750 
751 /*---------------------------------------------------------
752  * grxPutText:
753  *      (modified on SunPutText)
754  *
755  *	This routine puts a chunk of text on the screen in the current
756  *	color, size, etc.  The caller must ensure that it fits on
757  *	the screen -- no clipping is done except to the obscuring rectangle
758  *	list and the clip rectangle.
759  *
760  * Results:
761  *	none.
762  *
763  * Side Effects:
764  *	The text is drawn on the screen.
765  *
766  *---------------------------------------------------------
767  */
768 
769 void
grx11PutText(text,pos,clip,obscure)770 grx11PutText (text, pos, clip, obscure)
771     char *text;			/* The text to be drawn. */
772     Point *pos;			/* A point located at the leftmost point of
773 				 * the baseline for this string.
774 				 */
775     Rect *clip;			/* A rectangle to clip against */
776     LinkedRect *obscure;	/* A list of obscuring rectangles */
777 
778 {
779     Rect location;
780     Rect overlap;
781     Rect textrect;
782     LinkedRect *ob;
783     void grX11suGeoSub();
784 
785     if (grCurrent.font == NULL) return;
786 
787     GrX11TextSize(text, grCurrent.fontSize, &textrect);
788 
789     location.r_xbot = pos->p_x + textrect.r_xbot;
790     location.r_xtop = pos->p_x + textrect.r_xtop;
791     location.r_ybot = pos->p_y + textrect.r_ybot;
792     location.r_ytop = pos->p_y + textrect.r_ytop;
793 
794     /* erase parts of the bitmap that are obscured */
795     for (ob = obscure; ob != NULL; ob = ob->r_next)
796     {
797 	if (GEO_TOUCH(&ob->r_r, &location))
798 	{
799 	    overlap = location;
800 	    GeoClip(&overlap, &ob->r_r);
801 	    grX11suGeoSub(&location, &overlap);
802 	}
803     }
804 
805     overlap = location;
806     GeoClip(&overlap, clip);
807 
808     /* copy the text to the color screen */
809     if ((overlap.r_xbot < overlap.r_xtop)&&(overlap.r_ybot <= overlap.r_ytop))
810     {
811 	XRectangle xr;
812 
813 	XSetFont(grXdpy, grGCText, grCurrent.font->fid);
814 	grx11RectConvert(&overlap, &xr);
815 	XSetClipRectangles(grXdpy, grGCText, 0, 0, &xr, 1, Unsorted);
816 	XDrawString(grXdpy, grCurrent.window, grGCText,
817 		    pos->p_x, grMagicToX(pos->p_y),
818 		    text, strlen(text));
819     }
820 }
821 
822 
823 /*
824  *---------------------------------------------------------
825  * grX11suGeoSub --
826  *	return the tallest sub-rectangle of r not obscured by area
827  *	area must be within r.
828  *
829  * Results:
830  *	None.
831  *
832  * Side effects:
833  *	Source rectangle "r" is modified.
834  *---------------------------------------------------------
835  */
836 
837 void
grX11suGeoSub(r,area)838 grX11suGeoSub(r, area)
839     Rect *r;		/* Rectangle to be subtracted from. */
840     Rect *area;		/* Area to be subtracted. */
841 {
842     if (r->r_xbot == area->r_xbot) r->r_xbot = area->r_xtop;
843     else
844     if (r->r_xtop == area->r_xtop) r->r_xtop = area->r_xbot;
845     else
846     if (r->r_ybot <= area->r_ybot) r->r_ybot = area->r_ytop;
847     else
848     if (r->r_ytop == area->r_ytop) r->r_ytop = area->r_ybot;
849     else
850     r->r_xtop = area->r_xbot;
851 }
852