1 /* grOGL3.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 
24 #include <GL/gl.h>
25 #include <GL/glx.h>
26 #include <GL/glu.h>
27 
28 #include "utils/magic.h"
29 #include "utils/geometry.h"
30 #include "graphics/graphics.h"
31 #include "windows/windows.h"
32 #include "graphics/graphicsInt.h"
33 #include "textio/textio.h"
34 #include "utils/signals.h"
35 #include "utils/utils.h"
36 #include "utils/hash.h"
37 #include "dbwind/dbwind.h"
38 #include "database/fonts.h"
39 #include "grOGLInt.h"
40 
41 extern Display *grXdpy;
42 
43 static XFontStruct *grXFonts[4];
44 #define grSmallFont     grXFonts[0]
45 #define grMediumFont    grXFonts[1]
46 #define grLargeFont     grXFonts[2]
47 #define grXLargeFont    grXFonts[3]
48 GLuint	grXBases[4];
49 
50 
51 
52 /*---------------------------------------------------------
53  * groglDrawGrid:
54  *	groglDrawGrid adds a grid to the grid layer, using the current
55  *	write mask and color.
56  *
57  * Results:
58  *	TRUE is returned normally.  However, if the grid gets too small
59  *	to be useful, then nothing is drawn and FALSE is returned.
60  *
61  * Side Effects:    None.
62  *---------------------------------------------------------
63  */
64 
65 bool
groglDrawGrid(prect,outline,clip)66 groglDrawGrid (prect, outline, clip)
67     Rect *prect;			/* A rectangle that forms the template
68 			         * for the grid.  Note:  in order to maintain
69 			         * precision for the grid, the rectangle
70 			         * coordinates are specified in units of
71 			         * screen coordinates multiplied by SUBPIXEL.
72 			         */
73     int outline;		/* the outline style */
74     Rect *clip;			/* a clipping rectangle */
75 {
76     int xsize, ysize;
77     int x, y;
78     int xstart, ystart;
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)
84 	return FALSE;
85     if (GRID_TOO_SMALL(xsize, ysize))
86 	return FALSE;
87 
88     xstart = prect->r_xbot % xsize;
89     while (xstart < clip->r_xbot << SUBPIXELBITS) xstart += xsize;
90     ystart = prect->r_ybot % ysize;
91     while (ystart < clip->r_ybot << SUBPIXELBITS) ystart += ysize;
92 
93     groglSetLineStyle(outline);
94 
95     glBegin(GL_LINES);
96 
97     snum = 0;
98     low = clip->r_ybot;
99     hi = clip->r_ytop;
100     for (x = xstart; x < (clip->r_xtop + 1) << SUBPIXELBITS; x += xsize)
101     {
102 	shifted = x >> SUBPIXELBITS;
103 	glVertex2i(shifted, low);
104 	glVertex2i(shifted, hi);
105 	snum++;
106     }
107 
108     snum = 0;
109     low = clip->r_xbot;
110     hi = clip->r_xtop;
111     for (y = ystart; y < (clip->r_ytop + 1) << SUBPIXELBITS; y += ysize)
112     {
113 	shifted = y >> SUBPIXELBITS;
114 	glVertex2i(low, shifted);
115 	glVertex2i(hi, shifted);
116 	snum++;
117     }
118 
119     glEnd();
120     return TRUE;
121 }
122 
123 
124 /*---------------------------------------------------------
125  * groglPreLoadFont
126  *	This local routine loads the X fonts used by Magic.
127  *	At this time, we need the font information to size
128  *	the window (making room for the title at the top),
129  *	but with no rendering context set (no window to
130  *	draw to) we defer transferring the font bitmaps to
131  *	OpenGL display lists until later.
132  *
133  * Results:	Success/Fail
134  *
135  * Side Effects:    None.
136  *---------------------------------------------------------
137  */
138 
139 bool
groglPreLoadFont()140 groglPreLoadFont()
141 {
142     XFontStruct *fontInfo;
143     int i;
144     char *s;
145     char *unable = "Unable to load font";
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     for (i = 0; i < 4; i++) {
159     	 s = XGetDefault(grXdpy,"magic",optionnames[i]);
160 	 if (s) fontnames[i] = s;
161          if ((fontInfo = XLoadQueryFont(grXdpy, fontnames[i])) == NULL) {
162 	      TxError("%s %s\n",unable,fontnames[i]);
163               if ((grXFonts[i]= XLoadQueryFont(grXdpy,GR_DEFAULT_FONT))==NULL) {
164 	           TxError("%s %s\n",unable,GR_DEFAULT_FONT);
165 		   return FALSE;
166 	      }
167          }
168 	 grXFonts[i] = fontInfo;
169     }
170     return TRUE;
171 }
172 
173 
174 /*---------------------------------------------------------
175  * groglLoadFont
176  *	This local routine transfers the X font bitmaps
177  *	into OpenGL display lists for simple text
178  *	rendering.
179  *
180  * Results:	Success/Fail.
181  *
182  * Side Effects:    None.
183  *---------------------------------------------------------
184  */
185 
186 bool
groglLoadFont()187 groglLoadFont()
188 {
189     XFontStruct *fontInfo;
190     Font id;
191     unsigned int first, last, i;
192 
193     for (i = 0; i < 4; i++) {
194 	fontInfo = grXFonts[i];
195 	id = fontInfo->fid;
196 	first = fontInfo->min_char_or_byte2;
197 	last = fontInfo->max_char_or_byte2;
198 
199 	grXBases[i] = glGenLists(last+1);
200 	if (grXBases[i] == 0) {
201 	    TxError("Out of display lists!\n");
202 	    return FALSE;
203 	}
204 	glXUseXFont(id, first, last-first+1, grXBases[i]+first);
205     }
206     return TRUE;
207 }
208 
209 
210 /*---------------------------------------------------------
211  * groglSetCharSize:
212  *	This local routine sets the character size in the display,
213  *	if necessary.
214  *
215  * Results:	None.
216  *
217  * Side Effects:    None.
218  *---------------------------------------------------------
219  */
220 
221 void
groglSetCharSize(size)222 groglSetCharSize (size)
223     int size;		/* Width of characters */
224 {
225     oglCurrent.fontSize = size;
226 
227     switch (size) {
228     case GR_TEXT_DEFAULT:
229     case GR_TEXT_SMALL:
230 	oglCurrent.font = grSmallFont;
231 	break;
232     case GR_TEXT_MEDIUM:
233 	oglCurrent.font = grMediumFont;
234 	break;
235     case GR_TEXT_LARGE:
236 	oglCurrent.font = grLargeFont;
237 	break;
238     case GR_TEXT_XLARGE:
239 	oglCurrent.font = grXLargeFont;
240 	break;
241     default:
242 	    TxError("%s%d\n", "groglSetCharSize: Unknown character size ",
243 		size );
244 	    break;
245 	break;
246     }
247 }
248 
249 
250 /*
251  * ----------------------------------------------------------------------------
252  * GrOGLTextSize --
253  *
254  *	Determine the size of a text string.
255  *
256  * Results:
257  *	None.
258  *
259  * Side effects:
260  *	A rectangle is filled in that is the size of the text in pixels.
261  *	The origin (0, 0) of this rectangle is located on the baseline
262  *	at the far left side of the string.
263  * ----------------------------------------------------------------------------
264  */
265 
266 void
GrOGLTextSize(text,size,r)267 GrOGLTextSize(text, size, r)
268     char *text;
269     int size;
270     Rect *r;
271 {
272     XCharStruct overall;
273     XFontStruct *font;
274     int dir,fa,fd;
275 
276     switch (size) {
277     case GR_TEXT_DEFAULT:
278     case GR_TEXT_SMALL:
279 	font = grSmallFont;
280 	break;
281     case GR_TEXT_MEDIUM:
282 	font = grMediumFont;
283 	break;
284     case GR_TEXT_LARGE:
285 	font = grLargeFont;
286 	break;
287     case GR_TEXT_XLARGE:
288 	font = grXLargeFont;
289 	break;
290     default:
291 	TxError("%s%d\n", "GrOGLTextSize: Unknown character size ",
292 		size );
293 	break;
294     }
295     if (font == NULL) return;
296     XTextExtents(font, text, strlen(text), &dir, &fa, &fd, &overall);
297 
298     r->r_ytop = overall.ascent;
299     r->r_ybot = -overall.descent;
300     r->r_xtop = overall.width - overall.lbearing;
301     r->r_xbot = -overall.lbearing - 1;
302 }
303 
304 
305 /*
306  * ----------------------------------------------------------------------------
307  * GrOGLReadPixel --
308  *
309  *	Read one pixel from the screen.
310  *
311  * Results:
312  *	An integer containing the pixel's color.
313  *	(Except OpenGL has no such function, so we just return 0)
314  *
315  * Side effects:
316  *	none.
317  *
318  * ----------------------------------------------------------------------------
319  */
320 
321 int
GrOGLReadPixel(w,x,y)322 GrOGLReadPixel (w, x, y)
323     MagWindow *w;
324     int x,y;		/* the location of a pixel in screen coords */
325 {
326    return 0;
327 }
328 
329 
330 /*
331  * ----------------------------------------------------------------------------
332  * GrOGLBitBlt --
333  *
334  *	Copy information from one part of the screen to the other.
335  *
336  * Results:
337  *	None.
338  *
339  * Side effects:
340  *	changes the screen.
341  * ----------------------------------------------------------------------------
342  */
343 
344 void
GrOGLBitBlt(r,p)345 GrOGLBitBlt(r, p)
346     Rect *r;
347     Point *p;
348 {
349     glCopyPixels(r->r_xbot, r->r_ybot, r->r_xtop - r->r_xbot + 1,
350 		r->r_ytop - r->r_ybot + 1, GL_COLOR);
351 }
352 
353 #ifdef VECTOR_FONTS
354 
355 /*
356  *----------------------------------------------------------------------
357  *
358  * Technically, there should be no self-intersecting polygons in outline
359  * fonts.  However, decomposition of bezier curves into line segments
360  * may occasionally produce one, so it needs to be handled.
361  *----------------------------------------------------------------------
362  */
363 
364 void
myCombine(GLdouble coords[3],GLdouble * vertex_data[4],GLfloat weight[4],GLdouble ** outData,void * dataptr)365 myCombine(GLdouble coords[3], GLdouble *vertex_data[4],
366           GLfloat weight[4], GLdouble **outData, void *dataptr)
367 {
368     /* This needs to be free'd at the end of gluTessEndPolygon()! */
369     GLdouble *new = (GLdouble *)mallocMagic(2 * sizeof(GLdouble));
370     new[0] = coords[0];
371     new[1] = coords[1];
372     *outData = new;
373     /* Diagnostic */
374     TxError("Intersecting polygon in char \"%c\" at %g %g!\n",
375 	*((char *)dataptr), coords[0], coords[1]);
376 }
377 
378 /*
379  *----------------------------------------------------------------------
380  * Draw a text character
381  * This routine differs from grtoglFillPolygon() in that it uses the
382  * glu library to handle non-convex polygons as may appear in font
383  * outlines.
384  *----------------------------------------------------------------------
385  */
386 
387 void
groglDrawCharacter(clist,tc,pixsize)388 groglDrawCharacter(clist, tc, pixsize)
389     FontChar *clist;
390     unsigned char tc;
391     int pixsize;
392 {
393     Point *tp;
394     int np, nptotal;
395     int i, j;
396     static GLUtesselator *tess = NULL;
397     static GLdouble *v = NULL;
398     static int maxnp = 0;
399     FontChar *ccur;
400 
401     if (pixsize < 5) return;	/* Label too small to be useful */
402 
403     if (tess == NULL)
404     {
405 	tess = gluNewTess();
406 	gluTessCallback(tess, GLU_TESS_BEGIN, (_GLUfuncptr)glBegin);
407 	gluTessCallback(tess, GLU_TESS_VERTEX, (_GLUfuncptr)glVertex3dv);
408 	gluTessCallback(tess, GLU_TESS_END, (_GLUfuncptr)glEnd);
409 	gluTessCallback(tess, GLU_TESS_COMBINE_DATA, (_GLUfuncptr)myCombine);
410     }
411     // Boundary-only does not look particularly good. . .
412     gluTessProperty(tess, GLU_TESS_BOUNDARY_ONLY, GL_FALSE);
413 
414     nptotal = 0;
415     for (ccur = clist; ccur != NULL; ccur = ccur->fc_next)
416 	nptotal += ccur->fc_numpoints;
417 
418     if (nptotal > maxnp)
419     {
420 	if (v != NULL) freeMagic((char *)v);
421 	maxnp = nptotal;
422 	v = (GLdouble *)mallocMagic(nptotal * 3 * sizeof(GLdouble));
423     }
424 
425     j = 0;
426     for (ccur = clist; ccur != NULL; ccur = ccur->fc_next)
427     {
428 	tp = ccur->fc_points;
429 	np = ccur->fc_numpoints;
430 
431 	for (i = 0; i < np; i++, j += 3) {
432 	    v[j] = tp[i].p_x;
433 	    v[j + 1] = tp[i].p_y;
434 	    v[j + 2] = 0;
435 	}
436     }
437 
438     gluTessBeginPolygon(tess, (GLvoid *)(&tc));
439     j = 0;
440     for (ccur = clist; ccur != NULL; ccur = ccur->fc_next)
441     {
442 	np = ccur->fc_numpoints;
443 	gluTessBeginContour(tess);
444 	for (i = 0; i < np; i++, j += 3) {
445 	    gluTessVertex(tess, &v[j], &v[j]);
446 	}
447 	gluTessEndContour(tess);
448     }
449     gluTessEndPolygon(tess);
450 }
451 
452 /*---------------------------------------------------------
453  * groglFontText:
454  *
455  *	This routine draws text from font vectors using the
456  *	font vector routines in DBlabel.c.  Text is clipped
457  *	to the clipping rectangle.
458  *
459  *	For speed, we should be transferring the font
460  *	vectors into OpenGL display lists!
461  *
462  *---------------------------------------------------------
463  */
464 
465 void
groglFontText(text,font,size,rotate,pos,clip,obscure)466 groglFontText(text, font, size, rotate, pos, clip, obscure)
467     char *text;			/* The text to be drawn */
468     int   font;			/* Font to use from fontList */
469     int   size;			/* Pixel size of the font */
470     int	  rotate;		/* Text rotation */
471     Point *pos;			/* Text base position */
472     Rect  *clip;		/* Clipping area */
473     LinkedRect *obscure;	/* List of obscuring areas */
474 {
475     char *tptr;
476     Point *coffset;		/* vector to next character */
477     Rect *cbbox;
478     GLfloat fsize, matvals[16];
479     FontChar *clist;
480     int cheight, baseline;
481     float tmp;
482 
483     /* Keep it simple for now---ignore clip and obscure */
484 
485     glDisable(GL_BLEND);
486     glPushMatrix();
487     glTranslated(pos->p_x, pos->p_y, 0);
488     glRotated(rotate, 0, 0, 1);
489 
490     /* Get label size */
491     cbbox = &DBFontList[font]->mf_extents;
492 
493     fsize = (GLfloat)size / (GLfloat)cbbox->r_ytop;
494     glScalef(fsize, fsize, 1.0);
495 
496     /* Adjust to baseline */
497     baseline = 0;
498     for (tptr = text; *tptr != '\0'; tptr++)
499     {
500 	DBFontChar(font, *tptr, NULL, NULL, &cbbox);
501 	if (cbbox->r_ybot < baseline)
502 	    baseline = cbbox->r_ybot;
503     }
504     glTranslated(0, -baseline, 0);
505 
506     for (tptr = text; *tptr != '\0'; tptr++)
507     {
508 	DBFontChar(font, *tptr, &clist, &coffset, NULL);
509 	groglDrawCharacter(clist, *tptr, size);
510 	glTranslated(coffset->p_x, coffset->p_y, 0);
511     }
512     glPopMatrix();
513 }
514 
515 #endif /* VECTOR_FONTS */
516 
517 static GC grXcopyGC = (GC)NULL;
518 
519 /*
520  * ----------------------------------------------------------------------------
521  * groglFreeBackingStore --
522  *	Free up Pixmap memory for a backing store cell.
523  *
524  * Results:
525  *	None.
526  *
527  * Side effects:
528  *	memory Free'd
529  * ----------------------------------------------------------------------------
530  */
531 
532 void
groglFreeBackingStore(MagWindow * window)533 groglFreeBackingStore(MagWindow *window)
534 {
535     Pixmap pmap = (Pixmap)window->w_backingStore;
536     if (pmap == (Pixmap)NULL) return;
537     XFreePixmap(grXdpy, pmap);
538     window->w_backingStore = (ClientData)NULL;
539     /* XFreeGC(grXdpy, grXcopyGC); */
540     /* TxPrintf("groglFreeBackingStore called\n"); */
541 }
542 
543 /*
544  * ----------------------------------------------------------------------------
545  * groglCreateBackingStore --
546  *	Create Pixmap memory for a backing store cell and copy data
547  *	from the window into it.
548  *
549  * Results:
550  *	None.
551  *
552  * Side effects:
553  *	memory Allocated.
554  *
555  * ----------------------------------------------------------------------------
556  */
557 
558 void
groglCreateBackingStore(MagWindow * w)559 groglCreateBackingStore(MagWindow *w)
560 {
561     Pixmap pmap;
562     Window wind = (Window)w->w_grdata;
563     unsigned int width, height;
564     GC gc;
565     XGCValues gcValues;
566 
567     /* ignore for all windows except layout */
568     if (w->w_client != DBWclientID) return;
569 
570     /* deferred */
571     if (w->w_grdata == (Window)NULL) return;
572 
573     width = w->w_screenArea.r_xtop - w->w_screenArea.r_xbot;
574     height = w->w_screenArea.r_ytop - w->w_screenArea.r_ybot;
575 
576     if (w->w_backingStore != (ClientData)NULL) groglFreeBackingStore(w);
577 
578     if (grXcopyGC == (GC)NULL)
579     {
580 	gcValues.graphics_exposures = FALSE;
581 	grXcopyGC = XCreateGC(grXdpy, wind, GCGraphicsExposures, &gcValues);
582     }
583 
584     pmap = XCreatePixmap(grXdpy, wind, width, height, oglCurrent.depth);
585     w->w_backingStore = (ClientData)pmap;
586 
587     /* TxPrintf("groglCreateBackingStore area %d %d %d %d\n",
588 	w->w_screenArea.r_xbot, w->w_screenArea.r_ybot,
589 	w->w_screenArea.r_xtop, w->w_screenArea.r_ytop); */
590 }
591 
592 /*
593  * ----------------------------------------------------------------------------
594  * groglGetBackingStore --
595  *	Copy data from a backing store Pixmap into the indicated window.
596  *
597  * Results:
598  *	TRUE if backing store was copied successfully, FALSE if not.
599  *
600  * Side effects:
601  *	Data copied into Pixmap memory.
602  *
603  * ----------------------------------------------------------------------------
604  */
605 
606 bool
groglGetBackingStore(MagWindow * w,Rect * area)607 groglGetBackingStore(MagWindow *w, Rect *area)
608 {
609     Pixmap pmap;
610     Window wind = (Window)w->w_grdata;
611     unsigned int width, height;
612     int ybot;
613     int xoff, yoff;
614     Rect r;
615 
616     pmap = (Pixmap)w->w_backingStore;
617     if (pmap == (Pixmap)NULL)
618 	return FALSE;
619 
620     /* Make a local copy of area so we don't disturb the original */
621     r = *area;
622     GeoClip(&r, &(w->w_screenArea));
623 
624     width = r.r_xtop - r.r_xbot;
625     height = r.r_ytop - r.r_ybot;
626     ybot = glTransY(w, r.r_ytop);
627 
628     xoff = w->w_screenArea.r_xbot - w->w_allArea.r_xbot;
629     yoff = w->w_allArea.r_ytop - w->w_screenArea.r_ytop;
630 
631     XCopyArea(grXdpy, pmap, wind, grXcopyGC, r.r_xbot - xoff, ybot - yoff,
632 		width, height, r.r_xbot, ybot);
633 
634     /* TxPrintf("groglGetBackingStore %d %d %d %d\n",
635 		r.r_xbot, r.r_ybot, r.r_xtop, r.r_ytop); */
636     return TRUE;
637 }
638 
639 /*
640  * ----------------------------------------------------------------------------
641  * groglScrollBackingStore --
642  *	Enable fast scrolling by shifting part of the backing store
643  *	from one position to another, with the amount of shift indicated
644  *	by the X and/or Y value of the indicated point.
645  *
646  * Results:
647  *	TRUE on success, FALSE on failure.
648  *
649  * Side effects:
650  *	Data shifted in Pixmap memory.
651  *
652  * ----------------------------------------------------------------------------
653  */
654 
655 bool
groglScrollBackingStore(MagWindow * w,Point * shift)656 groglScrollBackingStore(MagWindow *w, Point *shift)
657 {
658     Pixmap pmap;
659     unsigned int width, height;
660     int xorigin, yorigin, xshift, yshift;
661 
662     pmap = (Pixmap)w->w_backingStore;
663     if (pmap == (Pixmap)NULL)
664     {
665 	TxPrintf("groglScrollBackingStore %d %d failure\n",
666 		shift->p_x, shift->p_y);
667 	return FALSE;
668     }
669 
670     width = w->w_screenArea.r_xtop - w->w_screenArea.r_xbot;
671     height = w->w_screenArea.r_ytop - w->w_screenArea.r_ybot;
672     xorigin = 0;
673     yorigin = 0;
674     xshift = shift->p_x;
675     yshift = -shift->p_y;
676 
677     if (xshift > 0)
678 	width -= xshift;
679     else if (xshift < 0)
680     {
681 	width += xshift;
682 	xorigin = -xshift;
683 	xshift = 0;
684     }
685     if (yshift > 0)
686 	height -= yshift;
687     else if (yshift < 0)
688     {
689 	height += yshift;
690 	yorigin = -yshift;
691 	yshift = 0;
692     }
693 
694     XCopyArea(grXdpy, pmap, pmap, grXcopyGC, xorigin, yorigin, width, height,
695 		xshift, yshift);
696 
697     /* TxPrintf("groglScrollBackingStore %d %d\n", shift->p_x, shift->p_y); */
698     return TRUE;
699 }
700 
701 /*
702  * ----------------------------------------------------------------------------
703  * groglPutBackingStore --
704  *	Copy data from the window into backing store.
705  *
706  * Results:
707  *	None.
708  *
709  * Side effects:
710  *	Graphics drawing into the window.
711  * ----------------------------------------------------------------------------
712  */
713 
714 void
groglPutBackingStore(MagWindow * w,Rect * area)715 groglPutBackingStore(MagWindow *w, Rect *area)
716 {
717     Pixmap pmap = (Pixmap)w->w_backingStore;
718     Window wind = (Window)w->w_grdata;
719     unsigned int width, height;
720     int ybot, xoff, yoff;
721 
722     if (pmap == (Pixmap)NULL) return;
723 
724     /* Attempting to write backing store into an obscured	*/
725     /* window (which we keep track of with Visibility events)	*/
726     /* causes backing store to be invalid.			*/
727 
728     if (w->w_flags & WIND_OBSCURED)
729     {
730 	groglFreeBackingStore(w);
731 	w->w_backingStore = (ClientData)NULL;
732 	return;
733     }
734 
735     width = area->r_xtop - area->r_xbot;
736     height = area->r_ytop - area->r_ybot;
737     ybot = glTransY(w, area->r_ytop);
738 
739     xoff = w->w_screenArea.r_xbot - w->w_allArea.r_xbot;
740     yoff = w->w_allArea.r_ytop - w->w_screenArea.r_ytop;
741 
742     XCopyArea(grXdpy, wind, pmap, grXcopyGC, area->r_xbot, ybot,
743 		width, height, area->r_xbot - xoff, ybot - yoff);
744 
745     /* TxPrintf("groglPutBackingStore %d %d %d %d\n",
746 		area->r_xbot, area->r_ybot, area->r_xtop, area->r_ytop); */
747 }
748 
749 
750 /*---------------------------------------------------------
751  * groglPutText:
752  *      (modified on SunPutText)
753  *
754  *	This routine puts a chunk of text on the screen in the current
755  *	color, size, etc.  The caller must ensure that it fits on
756  *	the screen -- no clipping is done except to the obscuring rectangle
757  *	list and the clip rectangle.
758  *
759  * Results:
760  *	none.
761  *
762  * Side Effects:
763  *	The text is drawn on the screen.
764  *
765  *---------------------------------------------------------
766  */
767 
768 void
groglPutText(text,pos,clip,obscure)769 groglPutText (text, pos, clip, obscure)
770     char *text;			/* The text to be drawn. */
771     Point *pos;			/* A point located at the leftmost point of
772 				 * the baseline for this string.
773 				 */
774     Rect *clip;			/* A rectangle to clip against */
775     LinkedRect *obscure;	/* A list of obscuring rectangles */
776 
777 {
778     Rect location;
779     Rect overlap;
780     Rect textrect;
781     LinkedRect *ob;
782     void grOGLGeoSub();
783     int i;
784     float tscale;
785 
786     GrOGLTextSize(text, oglCurrent.fontSize, &textrect);
787 
788     location.r_xbot = pos->p_x + textrect.r_xbot;
789     location.r_xtop = pos->p_x + textrect.r_xtop;
790     location.r_ybot = pos->p_y + textrect.r_ybot;
791     location.r_ytop = pos->p_y + textrect.r_ytop;
792 
793     /* erase parts of the bitmap that are obscured */
794     for (ob = obscure; ob != NULL; ob = ob->r_next)
795     {
796 	if (GEO_TOUCH(&ob->r_r, &location))
797 	{
798 	    overlap = location;
799 	    GeoClip(&overlap, &ob->r_r);
800 	    grOGLGeoSub(&location, &overlap);
801 	}
802     }
803 
804     overlap = location;
805     GeoClip(&overlap, clip);
806 
807     /* copy the text to the color screen */
808     if ((overlap.r_xbot < overlap.r_xtop)&&(overlap.r_ybot <= overlap.r_ytop))
809     {
810 	glScissor(overlap.r_xbot, overlap.r_ybot, overlap.r_xtop - overlap.r_xbot,
811 		overlap.r_ytop - overlap.r_ybot);
812 	glEnable(GL_SCISSOR_TEST);
813 	glDisable(GL_BLEND);
814 	glRasterPos2i(pos->p_x, pos->p_y);
815 	glListBase(grXBases[(oglCurrent.fontSize == GR_TEXT_DEFAULT) ?
816 		GR_TEXT_SMALL : oglCurrent.fontSize]);
817 	glCallLists(strlen(text), GL_UNSIGNED_BYTE, (unsigned char *)text);
818 	glDisable(GL_SCISSOR_TEST);
819     }
820 }
821 
822 
823 /* grOGLGeoSub:
824  *	return the tallest sub-rectangle of r not obscured by area
825  *	area must be within r.
826  */
827 
828 void
grOGLGeoSub(r,area)829 grOGLGeoSub(r, area)
830 Rect *r;		/* Rectangle to be subtracted from. */
831 Rect *area;		/* Area to be subtracted. */
832 
833 {
834     if (r->r_xbot == area->r_xbot) r->r_xbot = area->r_xtop;
835     else
836     if (r->r_xtop == area->r_xtop) r->r_xtop = area->r_xbot;
837     else
838     if (r->r_ybot <= area->r_ybot) r->r_ybot = area->r_ytop;
839     else
840     if (r->r_ytop == area->r_ytop) r->r_ytop = area->r_ybot;
841     else
842     r->r_xtop = area->r_xbot;
843 }
844