Files
magic/graphics/grOGL3.c
T
Darryl L. Miles 0b29b1cc12 fix GrTextSizePtr API interface return type consistency
commit 86b5d591d from 20241004 by me, modified return type of
GrTextSize API in the graphics from 'void' to 'int' to convey
and error scenario to indicate when 'r' was not filled in for
the caller, but this is a multi graphics driver API interface
so requires all downstream graphics engines to also support
the return type change.

This API can error and indicates when 'Rect *r' was
successfully updated.
2025-07-27 12:45:51 -04:00

850 lines
22 KiB
C

/* grOGL3.c -
*
* *********************************************************************
* * Copyright (C) 1985, 1990 Regents of the University of California. *
* * Permission to use, copy, modify, and distribute this *
* * software and its documentation for any purpose and without *
* * fee is hereby granted, provided that the above copyright *
* * notice appear in all copies. The University of California *
* * makes no representations about the suitability of this *
* * software for any purpose. It is provided "as is" without *
* * express or implied warranty. Export of this software outside *
* * of the United States of America may require an export license. *
* *********************************************************************
*
* This file contains additional functions to manipulate an X window system
* color display. Included here are device-dependent routines to draw and
* erase text and draw a grid.
*
*/
#include <stdio.h>
#include <string.h>
#include <GL/gl.h>
#include <GL/glx.h>
#include <GL/glu.h>
#include "utils/magic.h"
#include "utils/geometry.h"
#include "graphics/graphics.h"
#include "windows/windows.h"
#include "graphics/graphicsInt.h"
#include "textio/textio.h"
#include "utils/signals.h"
#include "utils/utils.h"
#include "utils/hash.h"
#include "dbwind/dbwind.h"
#include "database/fonts.h"
#include "grOGLInt.h"
/* C99 compat */
#include "utils/malloc.h"
extern Display *grXdpy;
static XFontStruct *grXFonts[4];
#define grSmallFont grXFonts[0]
#define grMediumFont grXFonts[1]
#define grLargeFont grXFonts[2]
#define grXLargeFont grXFonts[3]
GLuint grXBases[4];
/*---------------------------------------------------------
* groglDrawGrid:
* groglDrawGrid adds a grid to the grid layer, using the current
* write mask and color.
*
* Results:
* TRUE is returned normally. However, if the grid gets too small
* to be useful, then nothing is drawn and FALSE is returned.
*
* Side Effects: None.
*---------------------------------------------------------
*/
bool
groglDrawGrid (prect, outline, clip)
Rect *prect; /* A rectangle that forms the template
* for the grid. Note: in order to maintain
* precision for the grid, the rectangle
* coordinates are specified in units of
* screen coordinates multiplied by SUBPIXEL.
*/
int outline; /* the outline style */
Rect *clip; /* a clipping rectangle */
{
int xsize, ysize;
int x, y;
int xstart, ystart;
int snum, low, hi, shifted;
xsize = prect->r_xtop - prect->r_xbot;
ysize = prect->r_ytop - prect->r_ybot;
if (!xsize || !ysize)
return FALSE;
if (GRID_TOO_SMALL(xsize, ysize))
return FALSE;
xstart = prect->r_xbot % xsize;
while (xstart < clip->r_xbot << SUBPIXELBITS) xstart += xsize;
ystart = prect->r_ybot % ysize;
while (ystart < clip->r_ybot << SUBPIXELBITS) ystart += ysize;
groglSetLineStyle(outline);
glBegin(GL_LINES);
snum = 0;
low = clip->r_ybot;
hi = clip->r_ytop;
for (x = xstart; x < (clip->r_xtop + 1) << SUBPIXELBITS; x += xsize)
{
shifted = x >> SUBPIXELBITS;
glVertex2i(shifted, low);
glVertex2i(shifted, hi);
snum++;
}
snum = 0;
low = clip->r_xbot;
hi = clip->r_xtop;
for (y = ystart; y < (clip->r_ytop + 1) << SUBPIXELBITS; y += ysize)
{
shifted = y >> SUBPIXELBITS;
glVertex2i(low, shifted);
glVertex2i(hi, shifted);
snum++;
}
glEnd();
return TRUE;
}
/*---------------------------------------------------------
* groglPreLoadFont
* This local routine loads the X fonts used by Magic.
* At this time, we need the font information to size
* the window (making room for the title at the top),
* but with no rendering context set (no window to
* draw to) we defer transferring the font bitmaps to
* OpenGL display lists until later.
*
* Results: Success/Fail
*
* Side Effects: None.
*---------------------------------------------------------
*/
bool
groglPreLoadFont()
{
XFontStruct *fontInfo;
int i;
char *s;
char *unable = "Unable to load font";
static char *fontnames[4] = {
X_FONT_SMALL,
X_FONT_MEDIUM,
X_FONT_LARGE,
X_FONT_XLARGE };
static char *optionnames[4] = {
"small",
"medium",
"large",
"xlarge"};
for (i = 0; i < 4; i++) {
s = XGetDefault(grXdpy,"magic",optionnames[i]);
if (s) fontnames[i] = s;
if ((fontInfo = XLoadQueryFont(grXdpy, fontnames[i])) == NULL) {
TxError("%s %s\n",unable,fontnames[i]);
if ((grXFonts[i]= XLoadQueryFont(grXdpy,GR_DEFAULT_FONT))==NULL) {
TxError("%s %s\n",unable,GR_DEFAULT_FONT);
return FALSE;
}
}
grXFonts[i] = fontInfo;
}
return TRUE;
}
/*---------------------------------------------------------
* groglLoadFont
* This local routine transfers the X font bitmaps
* into OpenGL display lists for simple text
* rendering.
*
* Results: Success/Fail.
*
* Side Effects: None.
*---------------------------------------------------------
*/
bool
groglLoadFont()
{
XFontStruct *fontInfo;
Font id;
unsigned int first, last, i;
for (i = 0; i < 4; i++) {
fontInfo = grXFonts[i];
id = fontInfo->fid;
first = fontInfo->min_char_or_byte2;
last = fontInfo->max_char_or_byte2;
grXBases[i] = glGenLists(last+1);
if (grXBases[i] == 0) {
TxError("Out of display lists!\n");
return FALSE;
}
glXUseXFont(id, first, last-first+1, grXBases[i]+first);
}
return TRUE;
}
/*---------------------------------------------------------
* groglSetCharSize:
* This local routine sets the character size in the display,
* if necessary.
*
* Results: None.
*
* Side Effects: None.
*---------------------------------------------------------
*/
void
groglSetCharSize (size)
int size; /* Width of characters */
{
oglCurrent.fontSize = size;
switch (size) {
case GR_TEXT_DEFAULT:
case GR_TEXT_SMALL:
oglCurrent.font = grSmallFont;
break;
case GR_TEXT_MEDIUM:
oglCurrent.font = grMediumFont;
break;
case GR_TEXT_LARGE:
oglCurrent.font = grLargeFont;
break;
case GR_TEXT_XLARGE:
oglCurrent.font = grXLargeFont;
break;
default:
TxError("%s%d\n", "groglSetCharSize: Unknown character size ",
size );
break;
break;
}
}
/*
* ----------------------------------------------------------------------------
* GrOGLTextSize --
*
* Determine the size of a text string.
*
* Results:
* Returns 0 when 'r' updated, otherwise -1 on error (no side-effects).
*
* Side effects:
* A rectangle is filled in that is the size of the text in pixels.
* The origin (0, 0) of this rectangle is located on the baseline
* at the far left side of the string.
* ----------------------------------------------------------------------------
*/
int
GrOGLTextSize(text, size, r)
char *text;
int size;
Rect *r;
{
XCharStruct overall;
XFontStruct *font;
int dir,fa,fd;
switch (size) {
case GR_TEXT_DEFAULT:
case GR_TEXT_SMALL:
font = grSmallFont;
break;
case GR_TEXT_MEDIUM:
font = grMediumFont;
break;
case GR_TEXT_LARGE:
font = grLargeFont;
break;
case GR_TEXT_XLARGE:
font = grXLargeFont;
break;
default:
font = NULL;
TxError("%s%d\n", "GrOGLTextSize: Unknown character size ",
size );
break;
}
if (font == NULL) return -1;
XTextExtents(font, text, strlen(text), &dir, &fa, &fd, &overall);
r->r_ytop = overall.ascent;
r->r_ybot = -overall.descent;
r->r_xtop = overall.width - overall.lbearing;
r->r_xbot = -overall.lbearing - 1;
return 0;
}
/*
* ----------------------------------------------------------------------------
* GrOGLReadPixel --
*
* Read one pixel from the screen.
*
* Results:
* An integer containing the pixel's color.
* (Except OpenGL has no such function, so we just return 0)
*
* Side effects:
* none.
*
* ----------------------------------------------------------------------------
*/
int
GrOGLReadPixel (w, x, y)
MagWindow *w;
int x,y; /* the location of a pixel in screen coords */
{
return 0;
}
/*
* ----------------------------------------------------------------------------
* GrOGLBitBlt --
*
* Copy information from one part of the screen to the other.
*
* Results:
* None.
*
* Side effects:
* changes the screen.
* ----------------------------------------------------------------------------
*/
void
GrOGLBitBlt(r, p)
Rect *r;
Point *p;
{
glCopyPixels(r->r_xbot, r->r_ybot, r->r_xtop - r->r_xbot + 1,
r->r_ytop - r->r_ybot + 1, GL_COLOR);
}
#ifdef VECTOR_FONTS
/*
*----------------------------------------------------------------------
*
* Technically, there should be no self-intersecting polygons in outline
* fonts. However, decomposition of bezier curves into line segments
* may occasionally produce one, so it needs to be handled.
*----------------------------------------------------------------------
*/
void
myCombine(GLdouble coords[3], GLdouble *vertex_data[4],
GLfloat weight[4], GLdouble **outData, void *dataptr)
{
/* This needs to be free'd at the end of gluTessEndPolygon()! */
GLdouble *new = (GLdouble *)mallocMagic(2 * sizeof(GLdouble));
new[0] = coords[0];
new[1] = coords[1];
*outData = new;
/* Diagnostic */
TxError("Intersecting polygon in char \"%c\" at %g %g!\n",
*((char *)dataptr), coords[0], coords[1]);
}
/*
*----------------------------------------------------------------------
* Draw a text character
* This routine differs from grtoglFillPolygon() in that it uses the
* glu library to handle non-convex polygons as may appear in font
* outlines.
*----------------------------------------------------------------------
*/
void
groglDrawCharacter(clist, tc, pixsize)
FontChar *clist;
unsigned char tc;
int pixsize;
{
Point *tp;
int np, nptotal;
int i, j;
static GLUtesselator *tess = NULL;
static GLdouble *v = NULL;
static int maxnp = 0;
FontChar *ccur;
if (pixsize < 5) return; /* Label too small to be useful */
if (tess == NULL)
{
tess = gluNewTess();
gluTessCallback(tess, GLU_TESS_BEGIN, (_GLUfuncptr)glBegin);
gluTessCallback(tess, GLU_TESS_VERTEX, (_GLUfuncptr)glVertex3dv);
gluTessCallback(tess, GLU_TESS_END, (_GLUfuncptr)glEnd);
gluTessCallback(tess, GLU_TESS_COMBINE_DATA, (_GLUfuncptr)myCombine);
}
// Boundary-only does not look particularly good. . .
gluTessProperty(tess, GLU_TESS_BOUNDARY_ONLY, GL_FALSE);
nptotal = 0;
for (ccur = clist; ccur != NULL; ccur = ccur->fc_next)
nptotal += ccur->fc_numpoints;
if (nptotal > maxnp)
{
if (v != NULL) freeMagic((char *)v);
maxnp = nptotal;
v = (GLdouble *)mallocMagic(nptotal * 3 * sizeof(GLdouble));
}
j = 0;
for (ccur = clist; ccur != NULL; ccur = ccur->fc_next)
{
tp = ccur->fc_points;
np = ccur->fc_numpoints;
for (i = 0; i < np; i++, j += 3) {
v[j] = tp[i].p_x;
v[j + 1] = tp[i].p_y;
v[j + 2] = 0;
}
}
gluTessBeginPolygon(tess, (GLvoid *)(&tc));
j = 0;
for (ccur = clist; ccur != NULL; ccur = ccur->fc_next)
{
np = ccur->fc_numpoints;
gluTessBeginContour(tess);
for (i = 0; i < np; i++, j += 3) {
gluTessVertex(tess, &v[j], &v[j]);
}
gluTessEndContour(tess);
}
gluTessEndPolygon(tess);
}
/*---------------------------------------------------------
* groglFontText:
*
* This routine draws text from font vectors using the
* font vector routines in DBlabel.c. Text is clipped
* to the clipping rectangle.
*
* For speed, we should be transferring the font
* vectors into OpenGL display lists!
*
*---------------------------------------------------------
*/
void
groglFontText(text, font, size, rotate, pos, clip, obscure)
char *text; /* The text to be drawn */
int font; /* Font to use from fontList */
int size; /* Pixel size of the font */
int rotate; /* Text rotation */
Point *pos; /* Text base position */
Rect *clip; /* Clipping area */
LinkedRect *obscure; /* List of obscuring areas */
{
char *tptr;
Point *coffset; /* vector to next character */
Rect *cbbox;
GLfloat fsize, matvals[16];
FontChar *clist;
int cheight, baseline;
float tmp;
/* Keep it simple for now---ignore clip and obscure */
glDisable(GL_BLEND);
glPushMatrix();
glTranslated(pos->p_x, pos->p_y, 0);
glRotated(rotate, 0, 0, 1);
/* Get label size */
cbbox = &DBFontList[font]->mf_extents;
fsize = (GLfloat)size / (GLfloat)cbbox->r_ytop;
glScalef(fsize, fsize, 1.0);
/* Adjust to baseline */
baseline = 0;
for (tptr = text; *tptr != '\0'; tptr++)
{
DBFontChar(font, *tptr, NULL, NULL, &cbbox);
if (cbbox->r_ybot < baseline)
baseline = cbbox->r_ybot;
}
glTranslated(0, -baseline, 0);
for (tptr = text; *tptr != '\0'; tptr++)
{
DBFontChar(font, *tptr, &clist, &coffset, NULL);
groglDrawCharacter(clist, *tptr, size);
glTranslated(coffset->p_x, coffset->p_y, 0);
}
glPopMatrix();
}
#endif /* VECTOR_FONTS */
static GC grXcopyGC = (GC)NULL;
/*
* ----------------------------------------------------------------------------
* groglFreeBackingStore --
* Free up Pixmap memory for a backing store cell.
*
* Results:
* None.
*
* Side effects:
* memory Free'd
* ----------------------------------------------------------------------------
*/
void
groglFreeBackingStore(MagWindow *window)
{
Pixmap pmap = (Pixmap)window->w_backingStore;
if (pmap == (Pixmap)NULL) return;
XFreePixmap(grXdpy, pmap);
window->w_backingStore = (ClientData)NULL;
/* XFreeGC(grXdpy, grXcopyGC); */
/* TxPrintf("groglFreeBackingStore called\n"); */
}
/*
* ----------------------------------------------------------------------------
* groglCreateBackingStore --
* Create Pixmap memory for a backing store cell and copy data
* from the window into it.
*
* Results:
* None.
*
* Side effects:
* memory Allocated.
*
* ----------------------------------------------------------------------------
*/
void
groglCreateBackingStore(MagWindow *w)
{
Pixmap pmap;
Window wind = (Window)w->w_grdata;
unsigned int width, height;
GC gc;
XGCValues gcValues;
/* ignore for all windows except layout */
if (w->w_client != DBWclientID) return;
/* deferred */
if (w->w_grdata == (Window)NULL) return;
width = w->w_screenArea.r_xtop - w->w_screenArea.r_xbot;
height = w->w_screenArea.r_ytop - w->w_screenArea.r_ybot;
if (w->w_backingStore != (ClientData)NULL) groglFreeBackingStore(w);
if (grXcopyGC == (GC)NULL)
{
gcValues.graphics_exposures = FALSE;
grXcopyGC = XCreateGC(grXdpy, wind, GCGraphicsExposures, &gcValues);
}
pmap = XCreatePixmap(grXdpy, wind, width, height, oglCurrent.depth);
w->w_backingStore = (ClientData)pmap;
/* TxPrintf("groglCreateBackingStore area %d %d %d %d\n",
w->w_screenArea.r_xbot, w->w_screenArea.r_ybot,
w->w_screenArea.r_xtop, w->w_screenArea.r_ytop); */
}
/*
* ----------------------------------------------------------------------------
* groglGetBackingStore --
* Copy data from a backing store Pixmap into the indicated window.
*
* Results:
* TRUE if backing store was copied successfully, FALSE if not.
*
* Side effects:
* Data copied into Pixmap memory.
*
* ----------------------------------------------------------------------------
*/
bool
groglGetBackingStore(MagWindow *w, Rect *area)
{
Pixmap pmap;
Window wind = (Window)w->w_grdata;
unsigned int width, height;
int ybot;
int xoff, yoff;
Rect r;
pmap = (Pixmap)w->w_backingStore;
if (pmap == (Pixmap)NULL)
return FALSE;
/* Make a local copy of area so we don't disturb the original */
r = *area;
GeoClip(&r, &(w->w_screenArea));
width = r.r_xtop - r.r_xbot;
height = r.r_ytop - r.r_ybot;
ybot = glTransY(w, r.r_ytop);
xoff = w->w_screenArea.r_xbot - w->w_allArea.r_xbot;
yoff = w->w_allArea.r_ytop - w->w_screenArea.r_ytop;
XCopyArea(grXdpy, pmap, wind, grXcopyGC, r.r_xbot - xoff, ybot - yoff,
width, height, r.r_xbot, ybot);
/* TxPrintf("groglGetBackingStore %d %d %d %d\n",
r.r_xbot, r.r_ybot, r.r_xtop, r.r_ytop); */
return TRUE;
}
/*
* ----------------------------------------------------------------------------
* groglScrollBackingStore --
* Enable fast scrolling by shifting part of the backing store
* from one position to another, with the amount of shift indicated
* by the X and/or Y value of the indicated point.
*
* Results:
* TRUE on success, FALSE on failure.
*
* Side effects:
* Data shifted in Pixmap memory.
*
* ----------------------------------------------------------------------------
*/
bool
groglScrollBackingStore(MagWindow *w, Point *shift)
{
Pixmap pmap;
unsigned int width, height;
int xorigin, yorigin, xshift, yshift;
pmap = (Pixmap)w->w_backingStore;
if (pmap == (Pixmap)NULL)
{
TxPrintf("groglScrollBackingStore %d %d failure\n",
shift->p_x, shift->p_y);
return FALSE;
}
width = w->w_screenArea.r_xtop - w->w_screenArea.r_xbot;
height = w->w_screenArea.r_ytop - w->w_screenArea.r_ybot;
xorigin = 0;
yorigin = 0;
xshift = shift->p_x;
yshift = -shift->p_y;
if (xshift > 0)
width -= xshift;
else if (xshift < 0)
{
width += xshift;
xorigin = -xshift;
xshift = 0;
}
if (yshift > 0)
height -= yshift;
else if (yshift < 0)
{
height += yshift;
yorigin = -yshift;
yshift = 0;
}
XCopyArea(grXdpy, pmap, pmap, grXcopyGC, xorigin, yorigin, width, height,
xshift, yshift);
/* TxPrintf("groglScrollBackingStore %d %d\n", shift->p_x, shift->p_y); */
return TRUE;
}
/*
* ----------------------------------------------------------------------------
* groglPutBackingStore --
* Copy data from the window into backing store.
*
* Results:
* None.
*
* Side effects:
* Graphics drawing into the window.
* ----------------------------------------------------------------------------
*/
void
groglPutBackingStore(MagWindow *w, Rect *area)
{
Pixmap pmap = (Pixmap)w->w_backingStore;
Window wind = (Window)w->w_grdata;
unsigned int width, height;
int ybot, xoff, yoff;
if (pmap == (Pixmap)NULL) return;
/* Attempting to write backing store into an obscured */
/* window (which we keep track of with Visibility events) */
/* causes backing store to be invalid. */
if (w->w_flags & WIND_OBSCURED)
{
groglFreeBackingStore(w);
w->w_backingStore = (ClientData)NULL;
return;
}
width = area->r_xtop - area->r_xbot;
height = area->r_ytop - area->r_ybot;
ybot = glTransY(w, area->r_ytop);
xoff = w->w_screenArea.r_xbot - w->w_allArea.r_xbot;
yoff = w->w_allArea.r_ytop - w->w_screenArea.r_ytop;
XCopyArea(grXdpy, wind, pmap, grXcopyGC, area->r_xbot, ybot,
width, height, area->r_xbot - xoff, ybot - yoff);
/* TxPrintf("groglPutBackingStore %d %d %d %d\n",
area->r_xbot, area->r_ybot, area->r_xtop, area->r_ytop); */
}
/*---------------------------------------------------------
* groglPutText:
* (modified on SunPutText)
*
* This routine puts a chunk of text on the screen in the current
* color, size, etc. The caller must ensure that it fits on
* the screen -- no clipping is done except to the obscuring rectangle
* list and the clip rectangle.
*
* Results:
* none.
*
* Side Effects:
* The text is drawn on the screen.
*
*---------------------------------------------------------
*/
void
groglPutText (text, pos, clip, obscure)
char *text; /* The text to be drawn. */
Point *pos; /* A point located at the leftmost point of
* the baseline for this string.
*/
Rect *clip; /* A rectangle to clip against */
LinkedRect *obscure; /* A list of obscuring rectangles */
{
Rect location;
Rect overlap;
Rect textrect;
LinkedRect *ob;
void grOGLGeoSub();
int i;
float tscale;
if (GrOGLTextSize(text, oglCurrent.fontSize, &textrect) < 0)
return;
location.r_xbot = pos->p_x + textrect.r_xbot;
location.r_xtop = pos->p_x + textrect.r_xtop;
location.r_ybot = pos->p_y + textrect.r_ybot;
location.r_ytop = pos->p_y + textrect.r_ytop;
/* erase parts of the bitmap that are obscured */
for (ob = obscure; ob != NULL; ob = ob->r_next)
{
if (GEO_TOUCH(&ob->r_r, &location))
{
overlap = location;
GeoClip(&overlap, &ob->r_r);
grOGLGeoSub(&location, &overlap);
}
}
overlap = location;
GeoClip(&overlap, clip);
/* copy the text to the color screen */
if ((overlap.r_xbot < overlap.r_xtop)&&(overlap.r_ybot <= overlap.r_ytop))
{
glScissor(overlap.r_xbot, overlap.r_ybot, overlap.r_xtop - overlap.r_xbot,
overlap.r_ytop - overlap.r_ybot);
glEnable(GL_SCISSOR_TEST);
glDisable(GL_BLEND);
glRasterPos2i(pos->p_x, pos->p_y);
glListBase(grXBases[(oglCurrent.fontSize == GR_TEXT_DEFAULT) ?
GR_TEXT_SMALL : oglCurrent.fontSize]);
glCallLists(strlen(text), GL_UNSIGNED_BYTE, (unsigned char *)text);
glDisable(GL_SCISSOR_TEST);
}
}
/* grOGLGeoSub:
* return the tallest sub-rectangle of r not obscured by area
* area must be within r.
*/
void
grOGLGeoSub(r, area)
Rect *r; /* Rectangle to be subtracted from. */
Rect *area; /* Area to be subtracted. */
{
if (r->r_xbot == area->r_xbot) r->r_xbot = area->r_xtop;
else
if (r->r_xtop == area->r_xtop) r->r_xtop = area->r_xbot;
else
if (r->r_ybot <= area->r_ybot) r->r_ybot = area->r_ytop;
else
if (r->r_ytop == area->r_ytop) r->r_ytop = area->r_ybot;
else
r->r_xtop = area->r_xbot;
}