4
* $Id: xbasic.c 1210 2005-01-14 00:47:34Z travo $
6
* Implement the basic X windows engine for GIST.
9
* 12/06/04 mdh Correct bug: Do not set xeng->w in Kill because xeng has
13
/* Copyright (c) 1994. The Regents of the University of California.
14
All rights reserved. */
22
static char *xType= "PLAY Window";
24
static int ChangePalette(Engine *engine);
25
static GpReal TextWidth(const char *text, int nc, const GpTextAttribs *t);
26
static void Kill(Engine *engine);
27
static void ShutDown(XEngine *xEngine);
28
static int Clear(Engine *engine, int always);
29
static int Flush(Engine *engine);
30
static void GetXRectangle(GpXYMap *map, GpBox *box,
31
int *x0, int *y0, int *x1, int *y1);
32
static void ClearArea(Engine *engine, GpBox *box);
33
static void SetXTransform(GpTransform *trans, int landscape, int dpi);
34
static void gx_translate(GpBox *trans_window, int x, int y);
35
static GpBox *DamageClip(GpBox *damage);
36
static void ChangeMap(Engine *engine);
37
static void chk_clipping(XEngine *xeng);
38
static int SetupLine(XEngine *xeng, GpLineAttribs *gistAl, int join);
39
static int DrawLines(Engine *engine, long n, const GpReal *px,
40
const GpReal *py, int closed, int smooth);
41
static int DrawMarkers(Engine *engine, long n, const GpReal *px,
43
static int DrwText(Engine *engine, GpReal x0, GpReal y0, const char *text);
44
static int DrawFill(Engine *engine, long n, const GpReal *px,
46
static int GetCells(GpMap *map, GpReal xmin, GpReal xmax,
47
GpReal px, GpReal qx, long width,
48
int *i0, int *di, int *ncols, int *x0, int *x1);
49
static int DrawCells(Engine *engine, GpReal px, GpReal py, GpReal qx,
50
GpReal qy, long width, long height, long nColumns,
51
const GpColor *colors);
52
static int DrawDisjoint(Engine *engine, long n, const GpReal *px,
53
const GpReal *py, const GpReal *qx, const GpReal *qy);
54
static void GetVisibleNDC(XEngine *xeng,
55
GpReal *xn, GpReal *xx, GpReal *yn, GpReal *yx);
57
/* Hook for hlevel.c error handling. */
58
extern void (*HLevelHook)(Engine *engine);
61
extern int GxJustifyText(GpXYMap *map, GpReal x0, GpReal y0, const char *text,
62
int *ix, int *iy, int xbox[], int ybox[]);
63
extern int GxJustifyNext(const char **text, int *ix, int *iy);
65
static int gxErrorFlag= 0;
66
static void GxErrorHandler(void);
68
/* ------------------------------------------------------------------------ */
71
ChangePalette(Engine *engine)
73
XEngine *xeng= (XEngine *)engine;
75
p_win *win = xeng->win;
76
GpColorCell *palette= engine->palette; /* requested palette */
77
int nColors= engine->nColors;
78
int width, height, depth;
81
depth = p_sshape(s, &width, &height);
82
if (depth > 8) depth = 8;
83
if (nColors > 256) nColors= 256;
85
p_palette(win, palette, nColors);
87
xeng->e.colorChange= 0;
92
/* ------------------------------------------------------------------------ */
94
static int nChars, lineHeight, textHeight, prevWidth, maxWidth, alignH;
95
static int textAscent;
96
static int firstTextLine= 0;
98
static p_scr *current_scr;
99
static p_win *current_win;
100
static int current_state, current_fsym, current_fsize;
101
static int chunkWidth, nChunk, supersub, dy_super, dy_sub, x_chunks;
105
TextWidth(const char *text, int nc, const GpTextAttribs *t)
108
if (firstTextLine) nChars= nc;
110
width = p_txwidth(current_scr, text, nc, gistA.t.font, current_fsize);
112
/* record width of first line */
113
prevWidth= chunkWidth= width;
118
int firstChunk= firstTextLine;
119
const char *txt= text;
121
for ( ; nc-- ; text++) {
123
if ((nc && c=='!') || c=='^' || c=='_') {
125
width += p_txwidth(current_scr, txt, (int)(text-txt),
126
gistA.t.font, current_fsize);
128
nChunk= (int)(text-txt);
134
/* process single character escapes immediately */
138
if (c!='!' && c!='^' && c!='_') {
140
width += p_txwidth(current_scr, &c, 1,
141
current_fsym, current_fsize);
152
width += p_txwidth(current_scr, txt, (int)(text-txt),
153
gistA.t.font, current_fsize);
155
nChunk= (int)(text-txt);
159
/* record width of first line */
160
prevWidth= width; /* this is whole line */
164
return (GpReal)width;
168
GxJustifyText(GpXYMap *map, GpReal x0, GpReal y0, const char *text,
169
int *ix, int *iy, int xbox[], int ybox[])
171
int nLines, alignV, dx, dy, xmin, xmax, ymin, ymax, ix0, iy0;
174
/* abort if string screen position is ridiculous */
175
x0 = map->x.scale*x0 + map->x.offset;
176
y0 = map->y.scale*y0 + map->y.offset;
177
if (x0<-16000. || x0>16000. || y0<-16000. || y0>16000.) return -1;
180
/* call p_font before any calls to TextWidth
181
* - may improve efficiency of p_txwidth, p_txheight */
182
p_font(current_win, gistA.t.font, current_fsize, gistA.t.orient);
184
/* Set nLines, maxWidth, nChars, prevWidth */
186
nChars = prevWidth = chunkWidth = nChunk = supersub = 0;
187
nLines = GtTextShape(text, &gistA.t, &TextWidth, &rwidest);
188
maxWidth = (int)rwidest;
191
1 - this chunk is superscript
192
2 - this chunk is subscript (1 and 2 together meaningless)
193
4 - this chunk is single symbol char
197
/* Compute height of one line */
198
textHeight = p_txheight(current_scr, gistA.t.font, current_fsize,
200
dy_super = (supersub&1)? textAscent/3 : 0;
201
dy_sub = (supersub&2)? textAscent/3 : 0;
202
lineHeight = textHeight+dy_sub+dy_super;
204
/* Compute displacement and bounding box of entire string,
205
relative to specified location */
206
GtGetAlignment(&gistA.t, &alignH, &alignV);
207
/* Note: xmax= xmin+maxWidth */
208
if (alignH==TH_LEFT) {
211
} else if (alignH==TH_CENTER) {
221
/* Note: ymax= ymin+nLines*lineHeight and ymin= dy-textAscent */
222
if (alignV<=TV_CAP) {
223
dy= textAscent + dy_super;
225
ymax= nLines*lineHeight;
226
} else if (alignV==TV_HALF) {
228
ymax= nLines*lineHeight;
229
dy= ymin-(ymax-lineHeight)/2;
230
ymin= dy-textAscent-dy_super;
232
} else if (alignV==TV_BASE) {
233
dy= -(nLines-1)*lineHeight;
234
ymin= dy-textAscent-dy_super;
235
ymax= textHeight-textAscent + dy_sub;
237
ymin= dy_sub-nLines*lineHeight;
238
dy= ymin+textAscent+dy_super;
242
/* handle orientation (path) of text */
243
if (gistA.t.orient==TX_LEFT) { /* upside down */
246
tmp= xmin; xmin= -xmax; xmax= -tmp;
247
tmp= ymin; ymin= -ymax; ymax= -tmp;
248
} else if (gistA.t.orient==TX_UP) { /* reading upwards */
250
tmp= dx; dx= dy; dy= -tmp;
251
tmp= xmin; xmin= ymin; ymin= -xmax;
252
xmax= ymax; ymax= -tmp;
253
} else if (gistA.t.orient==TX_DOWN) { /* reading downwards */
255
tmp= dx; dx= -dy; dy= tmp;
256
tmp= xmin; xmin= -ymax; ymax= xmax;
257
xmax= -ymin; ymin= tmp;
260
/* get bounding box and adjusted reference point */
264
xbox[0] = ix0+xmin; ybox[0] = iy0+ymin;
265
xbox[1] = ix0+xmax; ybox[1] = iy0+ymax;
270
if (nChunk) p_font(current_win,
271
gistA.t.font, current_fsize, gistA.t.orient);
276
GxJustifyNext(const char **text, int *ix, int *iy)
278
const char *txt= *text+nChunk;
279
int xadj= 0, yadj= 0;
284
/* last chunk was the last one on a line */
285
txt= GtNextLine(txt, &nChars, 0);
290
/* scan for end of first chunk */
292
for (nChunk=0 ; nChunk<nChars ; nChunk++) {
294
if ((nChunk+1<nChars && c=='!') || c=='^' || c=='_') break;
300
/* compute width of this chunk if necessary, compute width
301
of whole line if necessary for justification */
302
if (alignH!=TH_LEFT || gistA.t.orient!=TX_RIGHT) {
303
/* need to compute width of entire line */
304
int width= prevWidth;
306
prevWidth= (int)TextWidth(txt, nChars, &gistA.t);
307
if (alignH==TH_CENTER) xadj= (width-prevWidth)/2;
308
else if (alignH==TH_RIGHT) xadj= width-prevWidth;
309
/* TextWidth sets chunkWidth */
310
} else if (nChunk<nChars) {
311
/* just need width of this chunk */
312
if (nChunk) chunkWidth = p_txwidth(current_scr, txt, nChunk,
313
gistA.t.font, current_fsize);
314
else chunkWidth= 0; /* unused */
317
/* reset state, adjusting (possibly rotated) x and y as well */
320
if (current_state&1) yadj+= dy_super;
321
else if (current_state&2) yadj-= dy_sub;
322
if (nChunk && (current_state&4))
323
p_font(current_win, gistA.t.font, current_fsize, gistA.t.orient);
327
/* previous chunk ended with an escape character, or was single
328
escaped symbol character -- can't get here unles gtDoEscapes */
330
xadj= chunkWidth; /* width of previous chunk */
331
x_chunks+= chunkWidth; /* accumulate all chunks except last */
333
if (!(current_state&4)) {
340
/* this chunk begins with escaped character */
343
if (c=='!' || c=='^' || c=='_') {
344
/* chunk is just ordinary text */
345
for ( ; nChunk<nChars ; nChunk++) {
347
if ((nChunk+1<nChars && c=='!') || c=='^' || c=='_') break;
349
p_font(current_win, gistA.t.font, current_fsize, gistA.t.orient);
352
/* chunk is single symbol char */
353
p_font(current_win, current_fsym, current_fsize, gistA.t.orient);
358
for (nChunk=0 ; nChunk<nChars ; nChunk++) {
360
if ((nChunk+1<nChars && c=='!') || c=='^' || c=='_') break;
363
p_font(current_win, gistA.t.font, current_fsize, gistA.t.orient);
365
if (current_state&1) {
366
yadj+= dy_super; /* return from super to normal */
369
if (current_state&2) yadj-= dy_sub;
370
yadj-= dy_super; /* move to superscript */
373
} else if (c1=='_') {
374
if (current_state&2) {
375
yadj-= dy_sub; /* return from sub to normal */
378
if (current_state&1) yadj+= dy_super;
379
yadj+= dy_sub; /* move to subscript */
383
/* just finished a symbol char */
389
(nChunk<nChars || alignH!=TH_LEFT || gistA.t.orient!=TX_RIGHT)) {
391
if (nChunk==1 && (current_state&4) && txt[0]==']') txt= ⁁
392
chunkWidth = p_txwidth(current_scr, txt, nChunk,
393
(current_state&4)? current_fsym : gistA.t.font,
396
chunkWidth= 0; /* unused */
400
if (gistA.t.orient==TX_RIGHT) {
403
} else if (gistA.t.orient==TX_LEFT) {
406
} else if (gistA.t.orient==TX_UP) {
417
/* ------------------------------------------------------------------------ */
419
/* notes for Kill() and g_on_destroy
420
* (1) Kill() is program-driven (e.g.- winkill)
421
* g_on_destroy() is event-driven (e.g.- mouse click)
422
* (2) however, the p_destroy() function MIGHT or MIGHT NOT
423
* result in a call to g_on_destroy()
424
* under Windows, g_on_destroy() is naturally called
425
* by the call p_destroy() must make to close the window
426
* under UNIX/X, the play event handler calls p_destroy()
427
* after g_on_destroy() returns
428
* (3) therefore g_on_destroy() must not call p_destroy(), since
429
* this would cause infinite recursion on Windows, and a
431
* (4) worse, if this is the final window on an X display, gist
432
* should disconnect from the display, but that can only
433
* be done after the final p_destroy(), which is after
434
* the g_on_destroy() in the event-driven case
435
* -- thus, the p_disconnect must take place on the next
436
* event, which is during the GhBeforeWait hlevel.c function
437
* -- unfortunately, in the meantime, a new window could
438
* have been created on that screen,
439
* hence the g_test_pending() routine
440
* -- the mess is further compounded by the fact that there
441
* could be an unlimited number of different screens with
442
* pending disconnections, after the 4th one, the current
443
* code simply gives up and forgets about disconnecting
445
extern void (*g_pending_task)(void);
446
void (*g_pending_task)(void) = 0;
447
static void g_test_pending(p_scr *s);
452
XEngine *xeng= (XEngine *)engine;
453
p_win *w = xeng->win;
458
/* for program-driven Kill(), can take care of p_disconnect immediately */
459
if (g_pending_task) g_pending_task();
463
Clear(Engine *engine, int always)
465
XEngine *xeng = (XEngine *)engine;
466
if (!xeng->w) return 1;
467
if ((always || xeng->e.marked) && xeng->w==xeng->win) {
468
int tm = xeng->topMargin;
469
int lm = xeng->leftMargin;
471
int xmax = (int)xeng->swapped.window.xmax;
472
int ymax = (int)xeng->swapped.window.ymin;
473
if (xmax > lm+xeng->wtop) xmax = lm+xeng->wtop;
474
if (ymax > tm+xeng->htop) ymax = tm+xeng->htop;
475
if (xeng->clipping) {
476
p_clip(xeng->w, 0,0,0,0);
479
p_color(xeng->w, P_BG);
480
p_rect(xeng->w, lm, tm, xmax, ymax, 0);
485
if (xeng->e.colorChange) ChangePalette(engine);
491
Flush(Engine *engine)
493
XEngine *xeng = (XEngine *)engine;
494
if (!xeng->w) return 1;
496
/* test whether an X error has been reported */
497
if (gxErrorFlag) GxErrorHandler();
502
GetXRectangle(GpXYMap *map, GpBox *box,
503
int *x0, int *y0, int *x1, int *y1)
505
/* get corners of clip rectangle in pixel coordinates */
506
int wmin= (int)(map->x.scale*box->xmin+map->x.offset);
507
int wmax= (int)(map->x.scale*box->xmax+map->x.offset);
515
wmin= (int)(map->y.scale*box->ymin+map->y.offset);
516
wmax= (int)(map->y.scale*box->ymax+map->y.offset);
527
ClearArea(Engine *engine, GpBox *box)
529
XEngine *xeng= (XEngine *)engine;
533
/* if this is animation mode, do not try to clear window */
535
int lm = xeng->leftMargin;
536
int tm = xeng->topMargin;
537
GetXRectangle(&engine->devMap, box, &x0, &y0, &x1, &y1);
538
if (x0 < lm) x0 = lm;
539
if (x1 > lm+xeng->wtop) x1 = lm+xeng->wtop;
540
if (y0 < tm) y0 = tm;
541
if (y1 > tm+xeng->htop) y1 = tm+xeng->htop;
543
p_rect(w, x0, y0, x1, y1, 0);
548
SetXTransform(GpTransform *trans, int landscape, int dpi)
550
trans->viewport= landscape? gLandscape : gPortrait;
551
trans->window.xmin= 0.0;
552
trans->window.xmax= PixelsPerNDC(dpi)*trans->viewport.xmax;
553
trans->window.ymin= PixelsPerNDC(dpi)*trans->viewport.ymax;
554
trans->window.ymax= 0.0;
558
gx_translate(GpBox *trans_window, int x, int y)
560
trans_window->xmax += x - trans_window->xmin;
561
trans_window->xmin = x;
562
trans_window->ymin += y - trans_window->ymax;
563
trans_window->ymax = y;
569
DamageClip(GpBox *damage)
571
cPort= gistT.viewport;
572
if (cPort.xmin>cPort.xmax)
573
{ GpReal tmp= cPort.xmin; cPort.xmin= cPort.xmax; cPort.xmax= tmp; }
574
if (cPort.ymin>cPort.ymax)
575
{ GpReal tmp= cPort.ymin; cPort.ymin= cPort.ymax; cPort.ymax= tmp; }
576
/* (assume damage box is properly ordered) */
577
if (damage->xmin>cPort.xmin) cPort.xmin= damage->xmin;
578
if (damage->xmax<cPort.xmax) cPort.xmax= damage->xmax;
579
if (damage->ymin>cPort.ymin) cPort.ymin= damage->ymin;
580
if (damage->ymax<cPort.ymax) cPort.ymax= damage->ymax;
581
if (cPort.xmin>cPort.xmax || cPort.ymin>cPort.ymax) return 0;
586
ChangeMap(Engine *engine)
588
XEngine *xeng = (XEngine *)engine;
590
int landscape = xeng->width > xeng->height;
595
/* check to be sure that landscape/portrait mode hasn't changed */
596
if (landscape!=xeng->e.landscape) {
597
/* this is probably insane if in animation mode... */
598
SetXTransform(&xeng->e.transform, xeng->e.landscape, xeng->dpi);
599
xeng->width = (int)xeng->e.transform.window.xmax;
600
xeng->height = (int)xeng->e.transform.window.ymin;
601
xeng->swapped = xeng->e.transform;
602
/* make adjustments to allow for SetXTransform, then recenter */
603
if (xeng->w != xeng->win) {
604
xeng->a_x += xeng->x+1;
605
xeng->a_y += xeng->y+1;
607
xeng->x = xeng->y = -1;
608
GxRecenter(xeng, xeng->wtop+xeng->leftMargin, xeng->htop+xeng->topMargin);
611
/* do generic change map */
612
GpComposeMap(engine);
614
/* get current clip window */
615
if (xeng->e.damaged) clipport = DamageClip(&xeng->e.damage);
616
else clipport = &gistT.viewport;
618
/* set clipping rectangle for this XEngine */
619
GetXRectangle(&engine->devMap, clipport, &x0, &y0, &x1, &y1);
620
if (xeng->w == xeng->win) {
621
/* additional restriction for vignetting by window borders */
622
int lm = xeng->leftMargin;
623
int tm = xeng->topMargin;
624
if (x0 < lm) x0 = lm;
625
if (x1 > lm+xeng->wtop) x1 = lm+xeng->wtop;
626
if (y0 < tm) y0 = tm;
627
if (y1 > tm+xeng->htop) y1 = tm+xeng->htop;
630
if (x1<=x0) x1 = x0+1;
631
if (y1<=y0) y1 = y0+1;
632
p_clip(xeng->w, x0, y0, x1, y1);
637
chk_clipping(XEngine *xeng)
639
p_win *w = xeng->win;
640
if (!xeng->clipping) {
642
int lm = xeng->leftMargin;
643
int tm = xeng->topMargin;
644
if (xeng->e.damaged) {
645
GpBox *box = DamageClip(&xeng->e.damage);
648
GpSetMap(&xeng->swapped.viewport, &xeng->swapped.window, &map);
650
map = xeng->e.devMap;
651
GetXRectangle(&map, box, &x0, &y0, &x1, &y1);
652
/* additional restriction for vignetting by window borders */
653
if (x0 < lm) x0 = lm;
654
if (x1 > lm+xeng->wtop) x1 = lm+xeng->wtop;
655
if (y0 < tm) y0 = tm;
656
if (y1 > tm+xeng->htop) y1 = tm+xeng->htop;
664
if (x1<=x0) x1 = x0+1;
665
if (y1<=y0) y1 = y0+1;
666
p_clip(xeng->w, x0, y0, x1, y1);
670
/* ------------------------------------------------------------------------ */
673
SetupLine(XEngine *xeng, GpLineAttribs *gistAl, int join)
675
GpXYMap *map= &xeng->e.map;
677
xt[0] = map->x.scale;
678
xt[1] = map->x.offset;
679
yt[0] = map->y.scale;
680
yt[1] = map->y.offset;
681
p_d_map(xeng->w, xt, yt, 1);
683
if (gistAl->type != L_NONE) {
684
int type = gistAl->type-1;
685
int width = (unsigned int)(DEFAULT_LINE_INCHES*xeng->dpi*gistAl->width);
686
if (join) type |= P_SQUARE;
687
p_pen(xeng->w, width, type);
688
p_color(xeng->w, gistAl->color);
696
DrawLines(Engine *engine, long n, const GpReal *px,
697
const GpReal *py, int closed, int smooth)
699
XEngine *xeng = (XEngine *)engine;
705
if (n<=0 || SetupLine(xeng, &gistA.l, 0)) return 0;
707
closed = (closed && n>1 && (px[0]!=px[n-1] || py[0]!=py[n-1]));
708
for (i=0 ; i<n ; i=imax) {
710
npts = (imax<=n)? 2047 : (int)(n-i);
711
p_d_pnts(w, px+i, py+i, npts);
712
if (closed && imax>=n)
713
p_d_pnts(w, px, py, -1);
721
/* ------------------------------------------------------------------------ */
723
/* play has no polymarker primitive, use GpPseudoMark-- unless
724
user requested tiny points, in which case we use p_dots */
726
DrawMarkers(Engine *engine, long n, const GpReal *px, const GpReal *py)
728
XEngine *xeng = (XEngine *)engine;
730
if (!w || !xeng->mapped) return 1;
733
if (gistA.m.type!=M_POINT || gistA.m.size>1.5) {
734
return GpPseudoMark(engine, n, px, py);
739
GpXYMap *map= &xeng->e.map;
741
xt[0] = map->x.scale;
742
xt[1] = map->x.offset;
743
yt[0] = map->y.scale;
744
yt[1] = map->y.offset;
745
p_d_map(w, xt, yt, 1);
747
p_color(w, gistA.m.color);
749
for (i=0 ; i<n ; i=imax) {
751
npts = (imax<=n)? 2048 : (int)(n-i);
752
p_d_pnts(w, px+i, py+i, npts);
760
/* ------------------------------------------------------------------------ */
763
DrwText(Engine *engine, GpReal x0, GpReal y0, const char *text)
765
XEngine *xeng = (XEngine *)engine;
767
GpXYMap *map = &xeng->e.map;
768
int ix, iy, len, xbox[2], ybox[2], xn, xx, yn, yx;
772
if (!w || !xeng->mapped) return 1;
775
current_fsize = (int)((xeng->dpi/ONE_INCH)*gistA.t.height);
776
if (current_fsize<4) current_fsize = 4; /* totally illegible */
777
if (current_fsize>180) current_fsize = 180; /* way too big */
778
current_fsym = T_SYMBOL | (gistA.t.font&3);
779
current_scr = xeng->s;
782
/* get current window */
783
xn = (int)(gistT.window.xmin*map->x.scale + map->x.offset);
784
xx = (int)(gistT.window.xmax*map->x.scale + map->x.offset);
785
yn = (int)(gistT.window.ymin*map->y.scale + map->y.offset);
786
yx = (int)(gistT.window.ymax*map->y.scale + map->y.offset);
787
if (yn > yx) { int tmp=yn ; yn=yx; yx=tmp ; }
789
/* handle multi-line strings */
790
len = GxJustifyText(map, x0, y0, text, &ix, &iy, xbox, ybox);
791
if (len < 0) return 0;
793
/* consider whether string is completely clipped */
794
if (ybox[0]>yx || ybox[1]<yn || xbox[0]>xx || xbox[1]<xn) return 0;
796
/* erase background if string is opaque */
797
if (gistA.t.opaque) {
799
p_rect(w, xbox[0], ybox[0], xbox[1], ybox[1], 0);
801
p_color(w, gistA.t.color);
805
if (len==1 && (current_state&4) && text[0]==']') txt = caret;
807
p_text(w, ix, iy, txt, len);
809
len = GxJustifyNext(&text, &ix, &iy);
816
/* ------------------------------------------------------------------------ */
819
DrawFill(Engine *engine, long n, const GpReal *px,
822
XEngine *xeng = (XEngine *)engine;
827
if (!w || !xeng->mapped) return 1;
828
has_edge = !SetupLine(xeng, &gistA.e, 0); /* but shouldn't set color */
829
p_color(w, gistA.f.color);
831
/* This gives incorrect results if more than one pass through the loop,
832
* but there is no way to give the correct result, so may as well... */
833
for (i=0 ; i<n ; i=imax) {
835
npts = (imax<=n)? 2048 : (int)(n-i);
836
p_d_pnts(w, px+i, py+i, npts);
837
/* Can Nonconvex or Convex be detected? */
843
p_color(w, gistA.e.color);
844
for (i=0 ; i<n ; i=imax) {
846
npts = (imax<=n)? 2047 : (int)(n-i);
847
p_d_pnts(w, px+i, py+i, npts);
849
p_d_pnts(w, px, py, -1);
857
/* ------------------------------------------------------------------------ */
860
GetCells(GpMap *map, GpReal xmin, GpReal xmax,
861
GpReal px, GpReal qx, long width,
862
int *i0, int *di, int *ncols, int *x0, int *x1)
864
GpReal scale = map->scale;
865
GpReal offset = map->offset;
866
GpReal x, dx = (qx-px)/width;
869
if (xmin>xmax) x=xmin, xmin=xmax, xmax=x;
872
if (qx<xmin || px>xmax) return 0;
875
if (x < 1.) imin = 0;
876
else imin=(int)x, px+=imin*dx;
878
if (x < 1.) imax = 0;
879
else imax=(int)x, qx-=imax*dx;
880
width -= imin + imax;
881
if (width < 3) { /* see comment below */
884
} else if (px >= xmin) {
886
} else if (width < 2) {
889
} else if (qx-xmax <= xmin-px) {
899
imax = width - imin - 1;
903
if (px<xmin || qx>xmax) return 0;
906
if (x < 1.) imin = 0;
907
else imin=(int)x, px-=imin*dx;
909
if (x < 1.) imax = 0;
910
else imax=(int)x, qx+=imax*dx;
911
width -= imin + imax;
912
if (width < 3) { /* see comment below */
915
} else if (qx >= xmin) {
917
} else if (width < 2) {
920
} else if (px-xmax <= xmin-qx) {
929
/* width<3 logic above guarantees these will not overflow as int */
930
px = px*scale + offset;
931
qx = qx*scale + offset;
939
*i0 = imin + width - 1;
945
/* cell array always at least 1 pixel */
946
if (*x1 == *x0) *x1 += 1;
952
DrawCells(Engine *engine, GpReal px, GpReal py, GpReal qx,
953
GpReal qy, long width, long height, long nColumns,
954
const GpColor *colors)
956
XEngine *xeng = (XEngine *)engine;
958
GpXYMap *map= &xeng->e.map;
959
int i0, j0, di, dj, ncols, nrows, x0, y0, x1, y1;
961
if (!w || !xeng->mapped) return 1;
964
if (GetCells(&map->x, gistT.window.xmin, gistT.window.xmax,
965
px, qx, width, &i0, &di, &ncols, &x0, &x1) &&
966
GetCells(&map->y, gistT.window.ymin, gistT.window.ymax,
967
py, qy, height, &j0, &dj, &nrows, &y0, &y1)) {
968
unsigned char *ndxs = (unsigned char *)colors;
969
if (di<0 || dj<0 || ncols!=width || nrows!=height || nColumns!=width) {
970
int r, c, nr, nc, i, j;
971
ndxs = p_malloc(gistA.rgb?3*ncols*nrows:ncols*nrows);
975
for (j=j0,c=0,nr=nrows ; nr-- ; j+=dj,c+=ncols) {
977
for (i=i0,r=0 ; nc-- ; i+=di,r++) {
978
ndxs[3*(c+r)] = colors[3*(j+i)];
979
ndxs[3*(c+r)+1] = colors[3*(j+i)+1];
980
ndxs[3*(c+r)+2] = colors[3*(j+i)+2];
984
for (j=j0,c=0,nr=nrows ; nr-- ; j+=dj,c+=ncols) {
986
for (i=i0,r=0 ; nc-- ; i+=di,r++)
987
ndxs[c+r] = colors[j+i];
991
if (ncols && nrows) {
993
p_rgb_cell(w, ndxs, ncols, nrows, x0, y0, x1, y1);
995
p_ndx_cell(w, ndxs, ncols, nrows, x0, y0, x1, y1);
997
if (ndxs!=colors) p_free(ndxs);
1004
/* ------------------------------------------------------------------------ */
1007
DrawDisjoint(Engine *engine, long n, const GpReal *px,
1008
const GpReal *py, const GpReal *qx, const GpReal *qy)
1010
XEngine *xeng = (XEngine *)engine;
1015
if (!w || !xeng->mapped) return 1;
1016
if (SetupLine(xeng, &gistA.l, 1)) return 0;
1018
p_d_pnts(w, px, py, 0);
1021
nseg = (imax<=n)? 1024 : (int)(n-i);
1023
p_d_pnts(w, px+i, py+i, -1);
1024
p_d_pnts(w, qx+i, qy+i, -1);
1034
/* ------------------------------------------------------------------------ */
1036
extern int (*gg_on_expose)(void *c, int *xy);
1037
extern int (*gg_on_destroy)(void *c);
1038
extern int (*gg_on_resize)(void *c,int w,int h);
1039
extern int (*gg_on_focus)(void *c,int in);
1040
extern int (*gg_on_key)(void *c,int k,int md);
1041
extern int (*gg_on_click)(void *c,int b,int md,int x,int y, unsigned long ms);
1042
extern int (*gg_on_motion)(void *c,int md,int x,int y);
1043
extern int (*gg_on_deselect)(void *c);
1044
int (*gg_on_expose)(void *c, int *xy) = 0;
1045
int (*gg_on_destroy)(void *c) = 0;
1046
int (*gg_on_resize)(void *c,int w,int h) = 0;
1047
int (*gg_on_focus)(void *c,int in) = 0;
1048
int (*gg_on_key)(void *c,int k,int md);
1049
int (*gg_on_click)(void *c,int b,int md,int x,int y, unsigned long ms) = 0;
1050
int (*gg_on_motion)(void *c,int md,int x,int y) = 0;
1051
int (*gg_on_deselect)(void *c) = 0;
1053
static void g_on_expose(void *c, int *xy);
1054
static void g_on_destroy(void *c);
1055
static void g_on_resize(void *c,int w,int h);
1056
static void g_on_focus(void *c,int in);
1057
static void g_on_key(void *c,int k,int md);
1058
static void g_on_click(void *c,int b,int md,int x,int y, unsigned long ms);
1059
static void g_on_motion(void *c,int md,int x,int y);
1060
static void g_on_deselect(void *c);
1061
static void g_on_panic(p_scr *screen);
1063
static Engine *waiting_for = 0;
1064
static void (*wait_callback)(void) = 0;
1066
extern int gist_expose_wait(Engine *eng, void (*e_callback)(void));
1069
gist_expose_wait(Engine *eng, void (*e_callback)(void))
1076
XEngine *xeng = GisXEngine(eng);
1077
if (!xeng || !xeng->w) return 1;
1078
if (xeng->mapped) return 2;
1080
wait_callback = e_callback;
1086
g_on_expose(void *c, int *xy)
1088
if (!gg_on_expose || gg_on_expose(c,xy)) {
1090
if (c && c == waiting_for) {
1092
if (wait_callback) wait_callback();
1095
if (!xeng->w) return;
1097
if (xeng->HandleExpose)
1098
/* the alternate handler should probably call GxExpose */
1099
xeng->HandleExpose(&xeng->e, xeng->e.drawing, xy);
1101
GxExpose(&xeng->e, xeng->e.drawing, xy);
1106
g_on_click(void *c,int b,int md,int x,int y, unsigned long ms)
1108
if (!gg_on_click || gg_on_click(c,b,md,x,y,ms)) {
1110
if (!xeng->w) return;
1111
if (xeng->HandleClick)
1112
xeng->HandleClick(&xeng->e, b, md, x, y, ms);
1117
g_on_motion(void *c,int md,int x,int y)
1119
if (!gg_on_motion || gg_on_motion(c,md,x,y)) {
1121
if (!xeng->w) return;
1122
if (xeng->HandleMotion)
1123
xeng->HandleMotion(&xeng->e, md, x, y);
1128
g_on_destroy(void *c)
1130
if (!gg_on_destroy || gg_on_destroy(c)) {
1132
/* if xeng->win==0, assume ShutDown already called */
1133
if (xeng->win) ShutDown(xeng);
1138
g_on_resize(void *c,int w,int h)
1140
if (!gg_on_resize || gg_on_resize(c,w,h)) {
1142
if (xeng->w) GxRecenter(xeng, w, h);
1147
g_on_focus(void *c,int in)
1149
if (!gg_on_focus || gg_on_focus(c,in)) {
1151
if (xeng->w && xeng->HandleMotion && in==2)
1152
xeng->HandleMotion(&xeng->e, 0, -1, -1);
1157
g_on_key(void *c,int k,int md)
1159
if (!gg_on_key || gg_on_key(c,k,md)) {
1161
if (xeng->w && xeng->HandleKey)
1162
xeng->HandleKey(&xeng->e, k, md);
1166
static void g_on_deselect(void *c)
1168
if (!gg_on_deselect || gg_on_deselect(c)) {
1173
GxExpose(Engine *engine, Drauing *drawing, int *xy)
1175
XEngine *xeng = (XEngine *)engine;
1177
if (!drawing || !xeng->w) return;
1178
/* xy=0 to redraw all, otherwise x0,y0,x1,y1 */
1180
GpXYMap *map = &engine->devMap;
1181
damage.xmin= (xy[0]-map->x.offset)/map->x.scale;
1182
damage.xmax= (xy[2]-map->x.offset)/map->x.scale;
1183
damage.ymax= (xy[1]-map->y.offset)/map->y.scale;
1184
damage.ymin= (xy[3]-map->y.offset)/map->y.scale;
1186
damage.xmin = xeng->swapped.viewport.xmin;
1187
damage.xmax = xeng->swapped.viewport.xmax;
1188
damage.ymin = xeng->swapped.viewport.ymin;
1189
damage.ymax = xeng->swapped.viewport.ymax;
1191
if (engine->damaged) {
1192
GpSwallow(&engine->damage, &damage);
1194
engine->damage = damage;
1195
engine->damaged = 1;
1197
GdSetDrawing(drawing);
1200
GpPreempt(0); /* not correct if damaged during a preempt... */
1205
GxRecenter(XEngine *xeng, int width, int height)
1208
int eWidth = xeng->width;
1209
int eHeight = xeng->height;
1210
width -= xeng->leftMargin;
1211
height -= xeng->topMargin;
1213
xeng->htop = height;
1214
x = (eWidth-width)/2;
1215
/* put center of page at center of landscape window */
1216
if (eWidth>eHeight) y = (eHeight-height)/2;
1217
/* put center of upper square of page at center of portrait window */
1218
else y = (eWidth-height)/2;
1219
/* once either dimension is big enough for whole picture, stop moving it */
1222
if (x!=xeng->x || y!=xeng->y) {
1223
int tmargin = xeng->topMargin;
1224
int lmargin = xeng->leftMargin;
1225
gx_translate(&xeng->swapped.window, -x+lmargin, -y+tmargin);
1226
if (xeng->w == xeng->win) {
1227
gx_translate(&xeng->e.transform.window, -x+lmargin, -y+tmargin);
1228
GpDeviceMap(&xeng->e);
1230
xeng->a_x -= x - xeng->x;
1231
xeng->a_y -= y - xeng->y;
1232
lmargin = tmargin = 0;
1236
if (xeng->wtop>0) x = xeng->wtop+lmargin;
1238
if (xeng->htop>0) y = xeng->htop+tmargin;
1241
p_clip(xeng->win, lmargin, tmargin, x, y);
1245
/* ------------------------------------------------------------------------ */
1247
typedef struct g_scr g_scr;
1253
static g_scr *g_screens = 0;
1254
static int n_screens = 0;
1258
g_initializer(int *pargc, char *argv[])
1260
extern char *g_argv0;
1261
g_argv0 = argv? argv[0] : 0;
1262
p_gui(&g_on_expose, &g_on_destroy, &g_on_resize, &g_on_focus,
1263
&g_on_key, &g_on_click, &g_on_motion, &g_on_deselect,
1268
g_connect(char *displayName)
1271
int i, j, i0=-1, len=0, number=0;
1273
/* split display into base name and screen number (separated by dot) */
1274
if (displayName) while (displayName[len]) len++;
1276
for (i=len-1 ; i>=0 ; i--) if (displayName[i]=='.') break;
1279
for (i++ ; i<len && displayName[i]<='9' && displayName[i]>='0' ; i++)
1280
number = 10*number + (displayName[i]-'0');
1281
if (i == len) len = i0;
1285
if (!len) displayName = 0;
1287
for (i=0 ; i<n_screens ; i++) {
1289
if (g_screens[i].name) {
1290
for ( ; j<len ; j++)
1291
if (g_screens[i].s && g_screens[i].name[j]!=displayName[j]) break;
1293
if (j==len && (len? (!g_screens[i].name[j]) : !g_screens[i].name)) {
1294
if (number == g_screens[i].number) break;
1295
else if (i0<0) i0 = i;
1298
if (i<n_screens) s = g_screens[i].s;
1301
if (i0<0) s = p_connect(displayName);
1302
else s = p_multihead(g_screens[i0].s, number);
1305
for (i=0 ; i<n_screens ; i++) if (!g_screens[i].s) break;
1306
if (i==n_screens && !(i & (i-1))) {
1308
g_screens = p_realloc(g_screens, sizeof(g_scr)*n);
1310
g_screens[i].number = number;
1311
g_screens[i].name = displayName? p_strncat(0, displayName, len) : 0;
1313
if (i==n_screens) n_screens++;
1320
g_disconnect(p_scr *s)
1325
for (i=0 ; i<n_screens ; i++) {
1326
if (g_screens[i].s == s) {
1327
name = g_screens[i].name;
1328
g_screens[i].name = 0;
1338
GxEngine(p_scr *s, char *name, GpTransform *toPixels,
1339
int x, int y, int topMargin, int leftMargin, long engineSize)
1342
unsigned int width, height;
1343
GpReal pixels_per_page;
1348
/* Graphics window will have dimensions of toPixels transform window */
1349
if (toPixels->window.xmin<toPixels->window.xmax)
1350
width = (unsigned int)(toPixels->window.xmax - toPixels->window.xmin);
1352
width = (unsigned int)(toPixels->window.xmin - toPixels->window.xmax);
1353
if (toPixels->window.ymin<toPixels->window.ymax)
1354
height = (unsigned int)(toPixels->window.ymax - toPixels->window.ymin);
1356
height = (unsigned int)(toPixels->window.ymin - toPixels->window.ymax);
1358
/* Reconstruct dpi (dots per inch) from toPixels transform */
1359
pixels_per_page = toPixels->window.ymin;
1360
if (pixels_per_page < toPixels->window.xmax)
1361
pixels_per_page = toPixels->window.xmax;
1362
dpi = (int)(0.01 + pixels_per_page*ONE_INCH/gPortrait.ymax);
1364
/* adjust VDC window so GpDeviceMap in GpNewEngine sets proper
1365
* transform, which will have xmin=x<0, ymax=y<0 */
1366
gx_translate(&toPixels->window, x+leftMargin, y+topMargin);
1369
(XEngine *)GpNewEngine(engineSize, name, xType, toPixels, width>height,
1370
&Kill, &Clear, &Flush, &ChangeMap,
1371
&ChangePalette, &DrawLines, &DrawMarkers,
1372
&DrwText, &DrawFill, &DrawCells,
1375
strcpy(gistError, "memory manager failed in GxEngine");
1379
/* XEngines can repair damage */
1380
xEngine->e.ClearArea = &ClearArea;
1382
/* Fill in Engine properties specific to XEngine */
1385
xEngine->width = width;
1386
xEngine->height = height;
1387
xEngine->topMargin = topMargin;
1388
xEngine->leftMargin = leftMargin;
1391
xEngine->mapped = xEngine->clipping = 0;
1394
xEngine->e.colorMode = 0;
1397
xEngine->a_width = xEngine->a_height= 0;
1398
xEngine->a_x = xEngine->a_y= 0;
1399
xEngine->swapped = xEngine->e.transform;
1401
xEngine->HandleExpose = 0;
1402
xEngine->HandleClick = 0;
1403
xEngine->HandleMotion = 0;
1404
xEngine->HandleKey = 0;
1409
/* default top window represents 6 inch square */
1410
int gx75width = 450;
1411
int gx100width = 600;
1412
int gx75height = 450;
1413
int gx100height = 600;
1415
int gist_private_map = 0;
1416
int gist_input_hint = 0;
1417
int gist_rgb_hint = 0;
1420
GpBXEngine(char *name, int landscape, int dpi, char *displayName)
1422
p_scr *s = g_connect(displayName);
1423
int topWidth = DefaultTopWidth(dpi);
1424
int topHeight = DefaultTopHeight(dpi);
1425
GpTransform toPixels;
1426
int x, y, width, height, hints;
1431
SetXTransform(&toPixels, landscape, dpi);
1432
width = (int)toPixels.window.xmax;
1433
height = (int)toPixels.window.ymin;
1434
x = (width-topWidth)/2;
1435
if (landscape) y = (height-topHeight)/2;
1436
else y = (width-topHeight)/2;
1439
xeng = GxEngine(s, name, &toPixels, -x,-y,0,0, sizeof(XEngine));
1441
xeng->wtop = topWidth;
1442
xeng->htop = topHeight;
1443
/* possibly want optional P_RGBMODEL as well */
1444
hints = (gist_private_map?P_PRIVMAP:0) | (gist_input_hint?0:P_NOKEY) |
1445
(gist_rgb_hint?P_RGBMODEL:0);
1446
xeng->win = xeng->w =
1447
p_window(s, topWidth, topHeight, name, P_BG, hints, xeng);
1449
GpDelEngine(&xeng->e);
1457
GxInput(Engine *engine,
1458
void (*HandleExpose)(Engine *, Drauing *, int *),
1459
void (*HandleClick)(Engine *,int,int,int,int,unsigned long),
1460
void (*HandleMotion)(Engine *,int,int,int),
1461
void (*HandleKey)(Engine *,int,int))
1463
XEngine *xeng = GisXEngine(engine);
1464
if (!xeng) return 1;
1465
xeng->HandleExpose = HandleExpose;
1466
xeng->HandleClick = HandleClick;
1467
xeng->HandleMotion = HandleMotion;
1468
xeng->HandleKey = HandleKey;
1473
GisXEngine(Engine *engine)
1475
return (engine && engine->type==xType)? (XEngine *)engine : 0;
1478
/* ------------------------------------------------------------------------ */
1481
GxAnimate(Engine *engine, GpBox *viewport)
1483
XEngine *xeng = GisXEngine(engine);
1484
int x, y, x0, y0, x1, y1;
1486
GpReal xmin, xmax, ymin, ymax;
1487
GpReal scalx, offx, scaly, offy;
1489
if (!xeng || !xeng->w) return 1;
1490
if (xeng->w!=xeng->win) GxDirect(engine);
1492
v = &xeng->e.transform.viewport; /* NDC */
1493
w = &xeng->e.transform.window; /* pixels */
1495
/* get NDC-->pixel mapping coefficients */
1496
scalx = xeng->e.devMap.x.scale;
1497
offx = xeng->e.devMap.x.offset;
1498
scaly = xeng->e.devMap.y.scale;
1499
offy = xeng->e.devMap.y.offset;
1501
/* clip given viewport to portion of NDC space which is actually
1502
* visible now -- note that v is either gLandscape or gPortrait,
1503
* so that min<max for v; must also be true for input viewport */
1504
GetVisibleNDC(xeng, &xmin, &xmax, &ymin, &ymax);
1505
if (viewport->xmin>xmin) xmin = viewport->xmin;
1506
if (viewport->xmax<xmax) xmax = viewport->xmax;
1507
if (viewport->ymin>ymin) ymin = viewport->ymin;
1508
if (viewport->ymax<ymax) ymax = viewport->ymax;
1510
/* install NDC-->pixel transform for animation pixmap */
1516
/* set the engine transform to map the specified viewport into
1517
* offscreen pixels, and get (x,y) offset from full window pixels
1518
* to offscreen pixels */
1519
w->xmin = scalx*xmin+offx;
1520
w->xmax = scalx*xmax+offx;
1521
if (w->xmax > w->xmin) {
1530
w->ymin = scaly*ymin+offy;
1531
w->ymax = scaly*ymax+offy;
1532
if (w->ymax > w->ymin) {
1541
GpDeviceMap((Engine *)xeng);
1542
GetXRectangle(&xeng->e.devMap, v, &x0, &y0, &x1, &y1);
1546
/* create the offscreen pixmap */
1547
xeng->w = p_offscreen(xeng->win, x1, y1);
1549
xeng->w = xeng->win;
1550
xeng->e.transform = xeng->swapped;
1551
GpDeviceMap((Engine *)xeng);
1555
xeng->a_height = y1;
1559
/* set coordinate mapping for offscreen */
1560
ChangeMap((Engine *)xeng);
1562
/* reset mapping clip to whole visible window */
1563
if (xeng->wtop>0) x1 = xeng->wtop+xeng->leftMargin;
1564
else x1 = xeng->leftMargin+1;
1565
if (xeng->htop>0) y1 = xeng->htop+xeng->topMargin;
1566
else y1 = xeng->topMargin+1;
1568
p_clip(xeng->win, xeng->leftMargin, xeng->topMargin, x1, y1);
1575
GetVisibleNDC(XEngine *xeng,
1576
GpReal *xn, GpReal *xx, GpReal *yn, GpReal *yx)
1578
GpReal scalx = xeng->e.devMap.x.scale;
1579
GpReal offx = xeng->e.devMap.x.offset;
1580
GpReal scaly = xeng->e.devMap.y.scale;
1581
GpReal offy = xeng->e.devMap.y.offset;
1582
int xmin, xmax, ymin, ymax;
1584
xmin = xeng->leftMargin;
1585
xmax = xmin+xeng->wtop;
1586
ymax = xeng->topMargin;
1587
ymin = ymax+xeng->htop;
1589
/* Convert pixels to NDC coordinates */
1590
*xn = (xmin-offx)/scalx;
1591
*xx = (xmax-offx)/scalx;
1592
*yn = (ymin-offy)/scaly;
1593
*yx = (ymax-offy)/scaly;
1597
GxStrobe(Engine *engine, int clear)
1599
XEngine *xeng = GisXEngine(engine);
1601
if (!xeng || !xeng->w || xeng->w==xeng->win) return 1;
1603
p_bitblt(xeng->win, xeng->a_x, xeng->a_y, xeng->w,
1604
0, 0, xeng->a_width, xeng->a_height);
1605
if (clear) p_clear(xeng->w);
1611
GxDirect(Engine *engine)
1613
XEngine *xeng = GisXEngine(engine);
1615
if (!xeng || !xeng->w || xeng->w==xeng->win) return 1;
1618
xeng->w = xeng->win;
1620
/* set coordinate map and clipping to values for graphics window */
1621
xeng->e.transform = xeng->swapped;
1622
GpDeviceMap((Engine *)xeng);
1623
ChangeMap((Engine *)xeng);
1628
/* ------------------------------------------------------------------------ */
1630
void (*HLevelHook)(Engine *engine)= 0;
1632
/* hack to disconnect if last engine destroyed (see GhBeforeWait) */
1633
#define G_N_PENDING 4
1634
static p_scr *g_pending_scr[G_N_PENDING];
1635
static void g_do_disconnect(void);
1637
g_do_disconnect(void)
1641
for (i=0 ; i<=G_N_PENDING ; i++) {
1642
s = g_pending_scr[i];
1643
g_pending_scr[i] = 0;
1644
if (s) g_disconnect(s);
1650
g_test_pending(p_scr *s)
1653
if (g_pending_task == g_do_disconnect) {
1654
for (i=0 ; i<=G_N_PENDING ; i++)
1655
if (g_pending_scr[i] == s) {
1656
g_pending_scr[i] = 0;
1661
for (i=0 ; i<=G_N_PENDING ; i++) g_pending_scr[i] = 0;
1666
ShutDown(XEngine *xeng)
1670
p_win *win = xeng->win;
1672
/* destroy any hlevel references without further ado */
1673
if (HLevelHook) HLevelHook((Engine *)xeng);
1674
xeng->w = xeng->win = 0;
1676
if (w && w!=win) p_destroy(w);
1677
GpDelEngine(&xeng->e);
1681
for (eng=GpNextEngine(0) ; eng ; eng=GpNextEngine(eng)) {
1682
xe2 = GisXEngine(eng);
1683
if (xe2 && xe2->s==s) break;
1687
if (g_pending_task == g_do_disconnect) {
1688
for (i=0 ; i<=G_N_PENDING ; i++)
1689
if (g_pending_scr[i] == s) break;
1690
if (i >= G_N_PENDING) {
1691
for (i=0 ; i<=G_N_PENDING ; i++)
1692
if (!g_pending_scr[i]) break;
1693
if (i < G_N_PENDING) g_pending_scr[i] = s;
1696
g_pending_scr[0] = s;
1697
for (i=1 ; i<=G_N_PENDING ; i++) g_pending_scr[i] = 0;
1698
g_pending_task = g_do_disconnect;
1704
static void (*XErrHandler)(char *errMsg)= 0;
1707
g_on_panic(p_scr *screen)
1712
for (eng=GpNextEngine(eng) ; eng ; eng=GpNextEngine(eng)) {
1713
xeng= GisXEngine(eng);
1714
if (xeng && xeng->s==screen) break;
1717
xeng->s = 0; /* be sure not to call p_disconnect */
1721
XErrHandler("play on_panic called (screen graphics engines killed)");
1724
/* this routine actually calls the XErrHandler, which may not return
1725
and/or which may trigger additional X protocol requests */
1726
static void GxErrorHandler(void)
1733
int GpSetXHandler(void (*ErrHandler)(char *errMsg))
1735
/* install X error handlers which don't call exit */
1736
XErrHandler= ErrHandler;
1740
/* ------------------------------------------------------------------------ */
1743
g_rgb_read(Engine *eng, GpColor *rgb, long *nx, long *ny)
1745
XEngine *xeng = GisXEngine(eng);
1746
if (!xeng || !xeng->w || !xeng->win) return 1;
1751
p_rgb_read(xeng->win, rgb, xeng->leftMargin, xeng->topMargin,
1752
xeng->leftMargin+xeng->wtop, xeng->topMargin+xeng->htop);
1757
/* ------------------------------------------------------------------------ */