1 /* $Id$ $Revision$ */
2 /* vim:set shiftwidth=4 ts=8: */
3 
4 /*************************************************************************
5  * Copyright (c) 2011 AT&T Intellectual Property
6  * All rights reserved. This program and the accompanying materials
7  * are made available under the terms of the Eclipse Public License v1.0
8  * which accompanies this distribution, and is available at
9  * http://www.eclipse.org/legal/epl-v10.html
10  *
11  * Contributors: See CVS logs. Details at http://www.graphviz.org/
12  *************************************************************************/
13 
14 /* Lefteris Koutsofios - AT&T Labs Research */
15 
16 #include "common.h"
17 #include "g.h"
18 #include "mem.h"
19 #include "leftyio.h"
20 #include "code.h"
21 #include "tbl.h"
22 #include "parse.h"
23 #include "exec.h"
24 #include "gfxview.h"
25 
26 #define max(a, b) (((a) >= (b)) ? (a) : (b))
27 #define min(a, b) (((a) >= (b)) ? (b) : (a))
28 
29 #define getwintnset(to) \
30     if (getint (to, &wattrp[wattri].u.i) != -1) wattri++;
31 #define getwpointnset(to) \
32     if (getxy (to, &wattrp[wattri].u.p) != -1) wattri++;
33 #define getwsizenset(to) \
34     if (getxy (to, &wattrp[wattri].u.s) != -1) wattri++;
35 #define getwrectnset(to) \
36     if (getrect (to, &wattrp[wattri].u.r) != -1) wattri++;
37 #define getwstrnset(to) \
38     if (getstr (to, &wattrp[wattri].u.t) != -1) wattri++;
39 #define getwcolornset(ko, vo) \
40     if (getcolor (ko, vo, &wattrp[wattri].u.c) != -1) wattri++;
41 
42 #define getaintnset(to, n, flag) \
43     if (to) { \
44         getint (to, &n), dp->flags |= flag; \
45     }
46 #define getastrnset(to, s, flag) \
47     if (to) \
48         getstr (to, &s), dp->flags |= flag;
49 
50 typedef struct colorname_t {
51     char *name;
52     unsigned char r, g, b;
53 } colorname_t;
54 
55 #include "colors.txt"
56 
57 typedef struct gfxrect_t {
58     struct gfxrect_t *next;
59     Tobj ko;
60     Grect_t r;
61 } gfxrect_t;
62 typedef struct gfxmenu_t {
63     struct gfxmenu_t *next;
64     Tobj ko;
65     long time;
66     int mi;
67 } gfxmenu_t;
68 #define LISTSIZE 100
69 typedef struct gfxnode_t {
70     int inuse;
71     int wi;
72     Tobj plvo, pmvo, prvo, pb3vo, pb4vo, pkvo[256];
73     Gpoint_t plp, pmp, prp, pb3p, pb4p, pkp[256];
74     char ls, ms, rs, b3s, b4s, ks[256];
75     struct gfxrect_t *rect[LISTSIZE];
76     struct gfxmenu_t *menu[LISTSIZE];
77 } gfxnode_t;
78 #define GFXNODEINCR 20
79 #define GFXNODESIZE sizeof (gfxnode_t)
80 static gfxnode_t *gfxnodes;
81 static int gfxnoden;
82 
83 #define ISAWIDGET(wi) (wi >= 0 && wi < Gwidgetn && Gwidgets[wi].inuse)
84 #define ISALABEL(wi) (Gwidgets[wi].type == G_LABELWIDGET)
85 #define ISACANVAS(wi) (Gwidgets[wi].type == G_CANVASWIDGET)
86 #define ISACANVAS2(wi) ( \
87     Gwidgets[wi].type == G_CANVASWIDGET || \
88     Gwidgets[wi].type == G_PCANVASWIDGET \
89 )
90 #define NODEID(wi) Gwidgets[wi].udata
91 #define ISABITMAP(bi) (bi >= 0 && bi < Gbitmapn && Gbitmaps[bi].inuse)
92 
93 static Gpoint_t *gpp = NULL;
94 static long gpn = 0;
95 #define GPINCR 100
96 #define GPSIZE sizeof (Gpoint_t)
97 
98 static Gwattr_t *wattrp = NULL;
99 static int wattrn = 0;
100 static int wattri = 0;
101 #define WATTRINCR 10
102 #define WATTRSIZE sizeof (Gwattr_t)
103 
104 static Tobj rootwo, rootbo;
105 
106 static void nodeinit (int);
107 static void nodeterm (int);
108 static void rectinit (int);
109 static void rectterm (int);
110 static void rectinsert (int, Tobj, Grect_t);
111 static void rectmerge (int, Tobj, Grect_t);
112 static Tobj rectfind (int, Gpoint_t);
113 static void rectdelete (int, Tobj);
114 static void rectprune (int);
115 static void menuinsert (int, Tobj, long, int);
116 static int menufind (int, Tobj, long);
117 static void menuprune (int);
118 static int scanhsv (char *, float *, float *, float *);
119 static void hsv2rgb (float, float, float, Gcolor_t *);
120 
121 static int getwattr (Tobj, int *);
122 static int getgattr (Tobj, Ggattr_t *);
123 static int getrect (Tobj, Grect_t *);
124 static int getxy (Tobj, Gxy_t *);
125 static int getint (Tobj, int *);
126 static int getdouble (Tobj, double *);
127 static int getstr (Tobj, char **);
128 static int getcolor (Tobj, Tobj, Gcolor_t *);
129 
GFXinit(void)130 void GFXinit (void) {
131     int ni;
132 
133     Tinss (root, "widgets", (rootwo = Ttable (100)));
134     Tinss (root, "bitmaps", (rootbo = Ttable (100)));
135     gfxnodes = Marrayalloc ((long) GFXNODEINCR * GFXNODESIZE);
136     gfxnoden = GFXNODEINCR;
137     for (ni = 0; ni < gfxnoden; ni++)
138         gfxnodes[ni].inuse = FALSE;
139     ni = 0;
140     gpp = Marrayalloc ((long) GPINCR * GPSIZE);
141     gpn = GPINCR;
142     wattrp = Marrayalloc ((long) WATTRINCR * WATTRSIZE);
143     wattrn = WATTRINCR, wattri = 0;
144 }
145 
GFXterm(void)146 void GFXterm (void) {
147     int ni;
148 
149     Marrayfree (wattrp), wattrp = NULL, wattrn = 0;
150     Marrayfree (gpp), gpp = NULL, gpn = 0;
151     for (ni = 0; ni < gfxnoden; ni++) {
152         if (gfxnodes[ni].inuse) {
153             Gdestroywidget (gfxnodes[ni].wi);
154             nodeterm (ni);
155             gfxnodes[ni].inuse = FALSE;
156         }
157     }
158     Marrayfree (gfxnodes), gfxnodes = NULL, gfxnoden = 0;
159     Tdels (root, "bitmaps");
160     Tdels (root, "widgets");
161 }
162 
163 /* callback for mem.c module - removes dead objects from gfxnodes */
GFXprune(void)164 void GFXprune (void) {
165     gfxnode_t *np;
166     int ni, ki;
167 
168     for (ni = 0; ni < gfxnoden; ni++) {
169         np = &gfxnodes[ni];
170         if (np->inuse) {
171             rectprune (ni), menuprune (ni);
172             if (np->plvo && !M_AREAOF (np->plvo))
173                 np->plvo = 0;
174             if (np->pmvo && !M_AREAOF (np->pmvo))
175                 np->pmvo = 0;
176             if (np->prvo && !M_AREAOF (np->prvo))
177                 np->prvo = 0;
178             if (np->pb3vo && !M_AREAOF (np->pb3vo))
179                 np->pb3vo = 0;
180             if (np->pb4vo && !M_AREAOF (np->pb4vo))
181                 np->pb4vo = 0;
182             for (ki = 0; ki < 256; ki++)
183                 if (np->pkvo[ki] && !M_AREAOF (np->pkvo[ki]))
184                     np->pkvo[ki] = 0;
185         }
186     }
187 }
188 
189 /* callback for g.c module - calls the appropriate lefty function */
GFXlabelcb(Gevent_t * evp)190 void GFXlabelcb (Gevent_t *evp) {
191     Tobj fo, to, co, wo;
192     char *fn;
193     char s[2];
194     long fm;
195 
196     fn = NULL;
197     wo = Tfindi (rootwo, evp->wi);
198     switch (evp->type) {
199     case G_MOUSE:
200         switch (evp->data) {
201         case G_LEFT:
202             fn = (evp->code == G_UP) ? "leftup" : "leftdown";
203             break;
204         case G_MIDDLE:
205             fn = (evp->code == G_UP) ? "middleup" : "middledown";
206             break;
207         case G_RIGHT:
208             fn = (evp->code == G_UP) ? "rightup" : "rightdown";
209             break;
210         }
211         break;
212     case G_KEYBD:
213         fn = (evp->code == G_UP) ? "keyup" : "keydown";
214         break;
215     }
216     if (!wo || !(fo = Tfinds (wo, fn)) || Tgettype (fo) != T_CODE)
217         if (!(fo = Tfinds (root, fn)) || Tgettype (fo) != T_CODE)
218             return;
219     fm = Mpushmark (fo);
220     to = Ttable (4);
221     Mpushmark (to);
222     Tinss (to, "widget", Tinteger (evp->wi));
223     if (evp->type == G_KEYBD)
224         s[0] = evp->data, s[1] = '\000', Tinss (to, "key", Tstring (s));
225     if ((co = Pfcall (fo, to)))
226         Eunit (co);
227     Mpopmark (fm);
228 }
229 
230 /* callback for g.c module - calls the appropriate lefty function */
GFXviewcb(Gevent_t * evp)231 void GFXviewcb (Gevent_t *evp) {
232     Tobj fo, to, co, wo;
233     char *fn;
234     long fm;
235 
236     wo = Tfindi (rootwo, evp->wi);
237     fn = "closeview";
238     if (!wo || !(fo = Tfinds (wo, fn)) || Tgettype (fo) != T_CODE)
239         if (!(fo = Tfinds (root, fn)) || Tgettype (fo) != T_CODE)
240             exit (0);
241     fm = Mpushmark (fo);
242     to = Ttable (2);
243     Mpushmark (to);
244     Tinss (to, "widget", Tinteger (evp->wi));
245     if ((co = Pfcall (fo, to)))
246         Eunit (co);
247     Mpopmark (fm);
248 }
249 
250 /* callback for g.c module - calls the appropriate lefty function */
GFXevent(Gevent_t * evp)251 void GFXevent (Gevent_t *evp) {
252     Tobj vo, pvo, fo, to, po, co, wo;
253     gfxnode_t *np;
254     Gpoint_t pp;
255     char *fn;
256     char s[2];
257     long fm;
258     int ni;
259 
260     pp.x = pp.y = 0;
261     pvo = NULL;
262     fn = NULL;
263     ni = Gwidgets[evp->wi].udata;
264     np = &gfxnodes[ni];
265     wo = Tfindi (rootwo, evp->wi);
266     if (!(vo = rectfind (ni, evp->p)))
267         vo = null;
268     switch (evp->type) {
269     case G_MOUSE:
270         switch (evp->data) {
271         case G_LEFT:
272             if (evp->code == G_UP)
273                 fn = "leftup", pvo = np->plvo, pp = np->plp, np->ls = 0;
274             else
275                 fn = "leftdown", np->plvo = vo, np->plp = evp->p, np->ls = 1;
276             break;
277         case G_MIDDLE:
278             if (evp->code == G_UP)
279                 fn = "middleup", pvo = np->pmvo, pp = np->pmp, np->ms = 0;
280             else
281                 fn = "middledown", np->pmvo = vo, np->pmp = evp->p, np->ms = 1;
282             break;
283         case G_RIGHT:
284             if (evp->code == G_UP)
285                 fn = "rightup", pvo = np->prvo, pp = np->prp, np->rs = 0;
286             else
287                 fn = "rightdown", np->prvo = vo, np->prp = evp->p, np->rs = 1;
288             break;
289         case G_BUTTON3:
290             if (evp->code == G_UP) {
291                 fn = "button3up";
292                 pvo = np->pb3vo, pp = np->pb3p, np->b3s = 0;
293             } else {
294                 fn = "button4down";
295                 np->pb3vo = vo, np->pb3p = evp->p, np->b3s = 1;
296             }
297             break;
298         case G_BUTTON4:
299             if (evp->code == G_UP) {
300                 fn = "button4up";
301                 pvo = np->pb4vo, pp = np->pb4p, np->b4s = 0;
302             } else {
303                 fn = "button4down";
304                 np->pb4vo = vo, np->pb4p = evp->p, np->b4s = 1;
305             }
306             break;
307         }
308         break;
309     case G_KEYBD:
310         if (evp->code == G_UP) {
311             fn = "keyup";
312             pvo = np->pkvo[evp->data];
313             pp = np->pkp[evp->data];
314             np->ks[evp->data] = 0;
315         } else {
316             fn = "keydown";
317             np->pkvo[evp->data] = vo;
318             np->pkp[evp->data] = evp->p;
319             np->ks[evp->data] = 1;
320         }
321         break;
322     }
323     if (!wo || !(fo = Tfinds (wo, fn)) || Tgettype (fo) != T_CODE)
324         if (!(fo = Tfinds (root, fn)) || Tgettype (fo) != T_CODE)
325             return;
326 
327     fm = Mpushmark (fo);
328     to = Ttable (4);
329     Mpushmark (to);
330     Tinss (to, "widget", Tinteger (evp->wi));
331     Tinss (to, "obj", vo);
332     Tinss (to, "pos", (po = Ttable (2)));
333     Tinss (po, "x", Treal (evp->p.x));
334     Tinss (po, "y", Treal (evp->p.y));
335     if (evp->code == G_UP) {
336         Tinss (to, "pobj", pvo);
337         Tinss (to, "ppos", (po = Ttable (2)));
338         Tinss (po, "x", Treal (pp.x));
339         Tinss (po, "y", Treal (pp.y));
340     }
341     if (evp->type == G_KEYBD)
342         s[0] = evp->data, s[1] = '\000', Tinss (to, "key", Tstring (s));
343     if ((co = Pfcall (fo, to)))
344         Eunit (co);
345     Mpopmark (fm);
346 }
347 
348 /* called directly from lefty.c, when any button is held down */
GFXmove(void)349 void GFXmove (void) {
350     gfxnode_t *np;
351     char *fn[5];
352     Tobj vo[5], fo, to, po, co, wo;
353     Gpoint_t cp, pp[5];
354     long fm;
355     int count, i, ni;
356 
357     for (ni = 0; ni < gfxnoden; ni++) {
358         np = &gfxnodes[ni];
359         if (
360             !np->inuse || !ISACANVAS (np->wi) ||
361             !Gwidgets[np->wi].u.c->buttonsdown
362         )
363             continue;
364         wo = Tfindi (rootwo, np->wi);
365         Ggetmousecoords (np->wi, &cp, &count);
366         if (!count) {
367             Gresetbstate (np->wi);
368             continue;
369         }
370         if (np->ls)
371             fn[0] = "leftmove",   pp[0] = np->plp, vo[0] = np->plvo;
372         else
373             fn[0] = NULL;
374         if (np->ms)
375             fn[1] = "middlemove", pp[1] = np->pmp, vo[1] = np->pmvo;
376         else
377             fn[1] = NULL;
378         if (np->rs)
379             fn[2] = "rightmove",  pp[2] = np->prp, vo[2] = np->prvo;
380         else
381             fn[2] = NULL;
382         if (np->b3s)
383             fn[3] = "button3move",  pp[3] = np->pb3p, vo[3] = np->pb3vo;
384         else
385             fn[3] = NULL;
386         if (np->b4s)
387             fn[4] = "button4move",  pp[4] = np->pb4p, vo[4] = np->pb4vo;
388         else
389             fn[4] = NULL;
390         for (i = 0; i < 5; i++) {
391             if (!fn[i])
392                 continue;
393             if (!wo || !(fo = Tfinds (wo, fn[i])) || Tgettype (fo) != T_CODE)
394                 fo = Tfinds (root, fn[i]);
395             if (fo && Tgettype (fo) == T_CODE) {
396                 fm = Mpushmark (fo);
397                 to = Ttable (4);
398                 Mpushmark (to);
399                 Tinss (to, "widget", Tinteger (np->wi));
400                 Tinss (to, "obj", vo[i]);
401                 Tinss (to, "pos", (po = Ttable (2)));
402                 Tinss (po, "x", Treal (cp.x));
403                 Tinss (po, "y", Treal (cp.y));
404                 Tinss (to, "ppos", (po = Ttable (2)));
405                 Tinss (po, "x", Treal (pp[i].x));
406                 Tinss (po, "y", Treal (pp[i].y));
407                 if ((co = Pfcall (fo, to)))
408                     Eunit (co);
409                 Mpopmark (fm);
410             }
411         }
412     }
413 }
414 
415 /* called directly from lefty.c, when any canvas needs to be redrawn */
GFXredraw(void)416 void GFXredraw (void) {
417     gfxnode_t *np;
418     Tobj wo, fo, co, to;
419     long fm;
420     int ni;
421 
422     for (ni = 0; ni < gfxnoden; ni++) {
423         np = &gfxnodes[ni];
424         if (
425             !np->inuse || !ISACANVAS (np->wi) ||
426             !Gwidgets[np->wi].u.c->needredraw
427         )
428             continue;
429         Gwidgets[np->wi].u.c->needredraw = FALSE;
430         wo = Tfindi (rootwo, np->wi);
431         if (!wo || !(fo = Tfinds (wo, "redraw")) || Tgettype (fo) != T_CODE)
432             if (!(fo = Tfinds (root, "redraw")) || Tgettype (fo) != T_CODE)
433                 return;
434 
435         fm = Mpushmark (fo);
436         to = Ttable (4);
437         Mpushmark (to);
438         Tinss (to, "widget", Tinteger (np->wi));
439         if ((co = Pfcall (fo, to)))
440             Eunit (co);
441         Mpopmark (fm);
442     }
443 }
444 
GFXtextcb(int wi,char * s)445 void GFXtextcb (int wi, char *s) {
446     Tobj wo, fo, co, to;
447     long fm;
448 
449     if (!(wo = Tfindi (rootwo, wi)))
450         return;
451 
452     if (!(fo = Tfinds (wo, "oneline")) || Tgettype (fo) != T_CODE)
453         return;
454 
455     fm = Mpushmark (fo);
456     to = Ttable (4);
457     Mpushmark (to);
458     Tinss (to, "widget", Tinteger (wi));
459     Tinss (to, "text", Tstring (s));
460     if ((co = Pfcall (fo, to)))
461         Eunit (co);
462     Mpopmark (fm);
463 }
464 
GFXbuttoncb(int wi,void * data)465 void GFXbuttoncb (int wi, void *data) {
466     Tobj wo, fo, co, to;
467     long fm;
468 
469     if (!(wo = Tfindi (rootwo, wi)))
470         return;
471 
472     if (!(fo = Tfinds (wo, "pressed")) || Tgettype (fo) != T_CODE)
473         return;
474 
475     fm = Mpushmark (fo);
476     to = Ttable (4);
477     Mpushmark (to);
478     Tinss (to, "widget", Tinteger (wi));
479     if ((co = Pfcall (fo, to)))
480         Eunit (co);
481     Mpopmark (fm);
482 }
483 
GFXarrayresizecb(int wi,Gawdata_t * dp)484 void GFXarrayresizecb (int wi, Gawdata_t *dp) {
485     Tobj wo, fo, co, so, to, lrtno, sxo, syo;
486     Tkvindex_t tkvi;
487     Gawcarray_t *cp;
488     int sx, sy, csx, csy, ci;
489     long fm;
490 
491     if (!(wo = Tfindi (rootwo, wi)))
492         return;
493 
494     if (!(fo = Tfinds (wo, "resize")) || Tgettype (fo) != T_CODE) {
495         Gawdefcoordscb (wi, dp);
496         return;
497     }
498 
499     fm = Mpushmark (fo);
500     to = Ttable (4);
501     Mpushmark (to);
502     Tinss (to, "widget", Tinteger (wi));
503     Tinss (to, "size", (so = Ttable (2)));
504     Tinss (so, "x", Treal ((double) dp->sx));
505     Tinss (so, "y", Treal ((double) dp->sy));
506 
507     lrtno = NULL;
508     if ((co = Pfcall (fo, to)))
509         lrtno = Eunit (co);
510     Mpopmark (fm);
511     if (!lrtno) {
512         Gawdefcoordscb (wi, dp);
513         return;
514     }
515     for (Tgetfirst (lrtno, &tkvi); tkvi.kvp; Tgetnext (&tkvi)) {
516         if (!T_ISNUMBER (tkvi.kvp->ko) || !T_ISTABLE ((so = tkvi.kvp->vo)))
517             continue;
518         wi = Tgetnumber (tkvi.kvp->ko);
519         if (!ISAWIDGET (wi))
520             continue;
521         for (ci = 0; ci < dp->cj; ci++) {
522             cp = &dp->carray[ci];
523             if (!cp->flag || cp->w != Gwidgets[wi].w)
524                 continue;
525             if ((sxo = Tfinds (so, "x")) && T_ISNUMBER (sxo))
526                 cp->sx = Tgetnumber (sxo);
527             if ((syo = Tfinds (so, "y")) && T_ISNUMBER (syo))
528                 cp->sy = Tgetnumber (syo);
529             break;
530         }
531     }
532     sx = dp->sx, sy = dp->sy;
533     csx = csy = 0;
534     for (ci = 0; ci < dp->cj; ci++) {
535         cp = &dp->carray[ci];
536         if (!cp->flag)
537             continue;
538         cp->ox = csx, cp->oy = csy;
539         if (dp->type == G_AWVARRAY)
540             cp->sx = sx - 2 * cp->bs, csy += cp->sy + 2 * cp->bs;
541         else
542             cp->sy = sy - 2 * cp->bs, csx += cp->sx + 2 * cp->bs;
543     }
544     if (dp->type == G_AWVARRAY)
545         dp->sy = csy;
546     else
547         dp->sx = csx;
548 }
549 
550 /* callback for when there is input on some file descriptor */
GFXmonitorfile(int fd)551 void GFXmonitorfile (int fd) {
552     Tobj fo, to, co;
553     long fm, tm;
554 
555     if (!(fo = Tfinds (root, "monitorfile")) || Tgettype (fo) != T_CODE)
556         return;
557 
558     fm = Mpushmark (fo);
559     to = Ttable (4);
560     tm = Mpushmark (to);
561     Tinss (to, "fd", Tinteger (fd));
562     if ((co = Pfcall (fo, to)))
563         Eunit (co);
564     Mpopmark (tm);
565     Mpopmark (fm);
566 }
567 
568 /* callback for when there is no X event and no file input */
GFXidle(void)569 void GFXidle (void) {
570     Tobj fo, to, co;
571     long fm, tm;
572 
573     if (!(fo = Tfinds (root, "idle")) || Tgettype (fo) != T_CODE)
574         return;
575 
576     fm = Mpushmark (fo);
577     to = Ttable (4);
578     tm = Mpushmark (to);
579     if ((co = Pfcall (fo, to)))
580         Eunit (co);
581     Mpopmark (tm);
582     Mpopmark (fm);
583 }
584 
585 /* LEFTY builtin */
GFXcreatewidget(int argc,lvar_t * argv)586 int GFXcreatewidget (int argc, lvar_t *argv) {
587     Tobj pwo, cwo, cho;
588     long rtnm;
589     int type, pwi, wi, ni;
590 
591     type = -1;
592     if (getint (argv[0].o, &pwi) == -1 || getwattr (argv[1].o, &type) == -1)
593         return L_FAILURE;
594     if (type != G_VIEWWIDGET && type != G_PCANVASWIDGET && !ISAWIDGET (pwi))
595         return L_FAILURE;
596     if (type == G_CANVASWIDGET || type == G_LABELWIDGET) {
597         for (ni = 0; ni < gfxnoden; ni++)
598             if (!gfxnodes[ni].inuse)
599                 break;
600         if (ni == gfxnoden) {
601             gfxnodes = Marraygrow (
602                 gfxnodes, (long) (ni + GFXNODEINCR) * GFXNODESIZE
603             );
604             for (ni = gfxnoden; ni < gfxnoden + GFXNODEINCR; ni++)
605                 gfxnodes[ni].inuse = FALSE;
606             ni = gfxnoden, gfxnoden += GFXNODEINCR;
607         }
608         nodeinit (ni);
609         if (wattri >= wattrn) {
610             wattrp = Marraygrow (
611                 wattrp, (long) (wattrn + WATTRINCR) * WATTRSIZE
612             );
613             wattrn += WATTRINCR;
614         }
615         switch (type) {
616         case G_CANVASWIDGET:
617             wattrp[wattri].id = G_ATTRUSERDATA;
618             wattrp[wattri].u.u = ni, wattri++;
619             break;
620         case G_LABELWIDGET:
621             wattrp[wattri].id = G_ATTRUSERDATA;
622             wattrp[wattri].u.u = ni, wattri++;
623             break;
624         }
625         wi = gfxnodes[ni].wi = Gcreatewidget (pwi, type, wattri, wattrp);
626         gfxnodes[ni].inuse = TRUE;
627         goto done;
628     }
629     wi = Gcreatewidget (pwi, type, wattri, wattrp);
630 
631 done:
632     Tinsi (rootwo, wi, (cwo = Ttable (4)));
633     rtno = Tinteger (wi);
634     rtnm = Mpushmark (rtno);
635     if (pwi != -1) {
636         Tinss (cwo, "parent", Tinteger (pwi));
637         if ((pwo = Tfindi (rootwo, pwi))) {
638             if (!(cho = Tfinds (pwo, "children")))
639                 Tinss (pwo, "children", (cho = Ttable (2)));
640             Tinsi (cho, wi, Tinteger (pwi));
641         }
642     }
643     Mpopmark (rtnm);
644     return L_SUCCESS;
645 }
646 
647 /* LEFTY builtin */
GFXsetwidgetattr(int argc,lvar_t * argv)648 int GFXsetwidgetattr (int argc, lvar_t *argv) {
649     int wi, type, rtn;
650 
651     if (getint (argv[0].o, &wi) == -1 || !ISAWIDGET (wi))
652         return L_FAILURE;
653     type = Gwidgets[wi].type;
654     if ((rtn = getwattr (argv[1].o, &type)) == -1)
655         return L_FAILURE;
656     Gsetwidgetattr (wi, wattri, wattrp);
657     rtno = Tinteger (rtn);
658     return L_SUCCESS;
659 }
660 
661 /* LEFTY builtin */
GFXgetwidgetattr(int argc,lvar_t * argv)662 int GFXgetwidgetattr (int argc, lvar_t *argv) {
663     Tkvindex_t tkvi;
664     Tobj po, so, ro, co, co2;
665     Gwattrmap_t *mapp;
666     int *ap;
667     int li, ai, type, color, wattri2, wi;
668     long rtnm;
669 
670     wattri = 0;
671     if (
672         getint (argv[0].o, &wi) == -1 || !ISAWIDGET (wi) ||
673         !T_ISTABLE (argv[1].o)
674     )
675         return L_FAILURE;
676     type = Gwidgets[wi].type;
677     for (ap = NULL, li = 0; Gwlist[li].wname; li++) {
678         if (type == Gwlist[li].wid) {
679             ap = Gwlist[li].attrid;
680             break;
681         }
682     }
683     if (!ap)
684         return L_FAILURE;
685     for (Tgetfirst (argv[1].o, &tkvi); tkvi.kvp; Tgetnext (&tkvi)) {
686         if (!T_ISSTRING (tkvi.kvp->vo))
687             continue;
688         for (ai = 0; ap[ai] != -1; ai++) {
689             if (
690                 strcmp (Gwattrmap[ap[ai]].name,
691                 Tgetstring (tkvi.kvp->vo)) == 0
692             ) {
693                 if (wattri >= wattrn) {
694                     wattrp = Marraygrow (
695                         wattrp, (long) (wattrn + WATTRINCR) * WATTRSIZE
696                     );
697                     wattrn += WATTRINCR;
698                 }
699                 if (ap[ai] == G_ATTRCOLOR) {
700                     for (color = 0; color < G_MAXCOLORS; color++) {
701                         if (wattri >= wattrn) {
702                             wattrp = Marraygrow (
703                                 wattrp, (long) (wattrn + WATTRINCR) * WATTRSIZE
704                             );
705                             wattrn += WATTRINCR;
706                         }
707                         wattrp[wattri].u.c.index = color;
708                         wattrp[wattri++].id = ap[ai];
709                     }
710                 } else
711                     wattrp[wattri++].id = ap[ai];
712                 break;
713             }
714         }
715     }
716     if (Ggetwidgetattr (wi, wattri, wattrp) == -1)
717         return L_FAILURE;
718     rtno = Ttable (wattri);
719     rtnm = Mpushmark (rtno);
720     for (wattri2 = 0; wattri2 < wattri; wattri2++) {
721         mapp = &Gwattrmap[wattrp[wattri2].id];
722         switch (mapp->type) {
723         case G_ATTRTYPEPOINT:
724             Tinss (rtno, mapp->name, (po = Ttable (2)));
725             Tinss (po, "x", Treal (wattrp[wattri2].u.p.x));
726             Tinss (po, "y", Treal (wattrp[wattri2].u.p.y));
727             break;
728         case G_ATTRTYPESIZE:
729             Tinss (rtno, mapp->name, (so = Ttable (2)));
730             Tinss (so, "x", Treal (wattrp[wattri2].u.s.x));
731             Tinss (so, "y", Treal (wattrp[wattri2].u.s.y));
732             break;
733         case G_ATTRTYPERECT:
734             Tinss (rtno, mapp->name, (ro = Ttable (2)));
735             Tinsi (ro, 0, (po = Ttable (2)));
736             Tinss (po, "x", Treal (wattrp[wattri2].u.r.o.x));
737             Tinss (po, "y", Treal (wattrp[wattri2].u.r.o.y));
738             Tinsi (ro, 1, (po = Ttable (2)));
739             Tinss (po, "x", Treal (wattrp[wattri2].u.r.c.x));
740             Tinss (po, "y", Treal (wattrp[wattri2].u.r.c.y));
741             break;
742         case G_ATTRTYPETEXT:
743             Tinss (rtno, mapp->name, Tstring (wattrp[wattri2].u.t));
744             break;
745         case G_ATTRTYPEINT:
746             Tinss (rtno, mapp->name, Tinteger (wattrp[wattri2].u.i));
747             break;
748         case G_ATTRTYPECOLOR:
749             Tinss (rtno, mapp->name, (co = Ttable (G_MAXCOLORS)));
750             for (color = 0; color < G_MAXCOLORS; color++) {
751                 Tinsi (co, color, (co2 = Ttable (3)));
752                 Tinss (co2, "r", Treal (wattrp[wattri2 + color].u.c.r));
753                 Tinss (co2, "g", Treal (wattrp[wattri2 + color].u.c.g));
754                 Tinss (co2, "b", Treal (wattrp[wattri2 + color].u.c.g));
755             }
756             wattri2 += (G_MAXCOLORS - 1);
757             break;
758         }
759     }
760     Mpopmark (rtnm);
761     return L_SUCCESS;
762 }
763 
764 /* LEFTY builtin */
GFXdestroywidget(int argc,lvar_t * argv)765 int GFXdestroywidget (int argc, lvar_t *argv) {
766     Tkvindex_t tkvi;
767     Tobj wo, cho, pwio, pwo;
768     lvar_t argv2[1];
769     int wi;
770 
771     if (getint (argv[0].o, &wi) == -1 || !ISAWIDGET (wi))
772         return L_FAILURE;
773 
774     wo = Tfindi (rootwo, wi);
775     if ((cho = Tfinds (wo, "children"))) {
776         while (Tgettablen (cho) > 0) {
777             Tgetfirst (cho, &tkvi);
778             argv2[0].o = tkvi.kvp->ko;
779             GFXdestroywidget (1, argv2);
780         }
781     }
782     if ((pwio = Tfinds (wo, "parent"))) {
783         pwo = Tfindi (rootwo, Tgetinteger (pwio));
784         cho = Tfinds (pwo, "children");
785         Tdeli (cho, wi);
786     }
787     if (ISACANVAS (wi) || ISALABEL (wi)) {
788         nodeterm (NODEID (wi));
789         gfxnodes[NODEID (wi)].inuse = FALSE;
790     }
791     Gdestroywidget (wi);
792     Tdeli (rootwo, wi);
793     return L_SUCCESS;
794 }
795 
796 /* LEFTY builtin */
GFXclear(int argc,lvar_t * argv)797 int GFXclear (int argc, lvar_t *argv) {
798     int wi;
799 
800     if (getint (argv[0].o, &wi) == -1 || !ISAWIDGET (wi))
801         return L_FAILURE;
802     Gcanvasclear (wi);
803     if (ISACANVAS (wi))
804         rectterm (NODEID (wi)), rectinit (NODEID (wi));
805     return L_SUCCESS;
806 }
807 
808 /* LEFTY builtin */
GFXsetgfxattr(int argc,lvar_t * argv)809 int GFXsetgfxattr (int argc, lvar_t *argv) {
810     Ggattr_t gattr;
811     int wi;
812 
813     if (
814         getint (argv[0].o, &wi) == -1 || !ISAWIDGET (wi) ||
815         getgattr (argv[1].o, &gattr) == -1
816     )
817         return L_FAILURE;
818     Gsetgfxattr (wi, &gattr);
819     return L_SUCCESS;
820 }
821 
822 /* LEFTY builtin */
GFXgetgfxattr(int argc,lvar_t * argv)823 int GFXgetgfxattr (int argc, lvar_t *argv) {
824     Tkvindex_t tkvi;
825     Ggattr_t gattr;
826     long rtnm;
827     int wi;
828     char *s;
829 
830     gattr.flags = 0;
831     if (
832         getint (argv[0].o, &wi) == -1 || !ISAWIDGET (wi) ||
833         !T_ISTABLE (argv[1].o)
834     )
835         return L_FAILURE;
836 
837     s = NULL;
838     for (Tgetfirst (argv[1].o, &tkvi); tkvi.kvp; Tgetnext (&tkvi)) {
839         if (!T_ISSTRING (tkvi.kvp->vo))
840             continue;
841         s = Tgetstring (tkvi.kvp->vo);
842         if (strcmp (s, "color") == 0)
843             gattr.flags |= G_GATTRCOLOR;
844         else if (strcmp (s, "width") == 0)
845             gattr.flags |= G_GATTRWIDTH;
846         else if (strcmp (s, "mode") == 0)
847             gattr.flags |= G_GATTRMODE;
848         else if (strcmp (s, "fill") == 0)
849             gattr.flags |= G_GATTRFILL;
850         else if (strcmp (s, "style") == 0)
851             gattr.flags |= G_GATTRSTYLE;
852     }
853     if (Ggetgfxattr (wi, &gattr) == -1)
854         return L_FAILURE;
855     rtno = Ttable (wattri);
856     rtnm = Mpushmark (rtno);
857     if (gattr.flags & G_GATTRCOLOR) {
858         Tinss (rtno, "color", Tinteger (gattr.color));
859     } else if (gattr.flags & G_GATTRWIDTH) {
860         Tinss (rtno, "width", Tinteger (gattr.width));
861     } else if (gattr.flags & G_GATTRMODE) {
862         s = (gattr.mode == G_SRC) ? "src" : "xor";
863         Tinss (rtno, "mode", Tstring (s));
864     } else if (gattr.flags & G_GATTRFILL) {
865         s = (gattr.fill) ? "on" : "off";
866         Tinss (rtno, "fill", Tstring (s));
867     } else if (gattr.flags & G_GATTRSTYLE) {
868         switch (gattr.style) {
869         case G_SOLID:       s = "solid";       break;
870         case G_DASHED:      s = "dashed";      break;
871         case G_DOTTED:      s = "dotted";      break;
872         case G_LONGDASHED:  s = "longdashed";  break;
873         case G_SHORTDASHED: s = "shortdashed"; break;
874         }
875         Tinss (rtno, "style", Tstring (s));
876     }
877     Mpopmark (rtnm);
878     return L_SUCCESS;
879 }
880 
881 /* LEFTY builtin */
GFXarrow(int argc,lvar_t * argv)882 int GFXarrow (int argc, lvar_t *argv) {
883     Ggattr_t gattr;
884     Gpoint_t p0, p1;
885     int wi;
886 
887     if (
888         getint (argv[0].o, &wi) == -1 || !ISAWIDGET (wi) || !ISACANVAS2 (wi) ||
889         getxy (argv[2].o, &p0) == -1 || getxy (argv[3].o, &p1) == -1 ||
890         getgattr ((argc == 5) ? argv[4].o : NULL, &gattr) == -1
891     )
892         return L_FAILURE;
893     Garrow (wi, p0, p1, &gattr);
894     return L_SUCCESS;
895 }
896 
897 /* LEFTY builtin */
GFXline(int argc,lvar_t * argv)898 int GFXline (int argc, lvar_t *argv) {
899     Ggattr_t gattr;
900     Gpoint_t p0, p1;
901     int wi;
902 
903     if (
904         getint (argv[0].o, &wi) == -1 || !ISAWIDGET (wi) || !ISACANVAS2 (wi) ||
905         getxy (argv[2].o, &p0) == -1 || getxy (argv[3].o, &p1) == -1 ||
906         getgattr ((argc == 5) ? argv[4].o : NULL, &gattr) == -1
907     )
908         return L_FAILURE;
909     Gline (wi, p0, p1, &gattr);
910     return L_SUCCESS;
911 }
912 
913 /* LEFTY builtin */
GFXbox(int argc,lvar_t * argv)914 int GFXbox (int argc, lvar_t *argv) {
915     Ggattr_t gattr;
916     Grect_t r;
917     int wi, rtn;
918 
919     if (
920         getint (argv[0].o, &wi) == -1 || !ISAWIDGET (wi) || !ISACANVAS2 (wi) ||
921         getrect (argv[2].o, &r) == -1 ||
922         getgattr ((argc == 4) ? argv[3].o : NULL, &gattr) == -1
923     )
924         return L_FAILURE;
925     rtn = Gbox (wi, r, &gattr);
926     if (rtn == 0 && argv[1].o != null && ISACANVAS (wi))
927         rectmerge (NODEID (wi), argv[1].o, r);
928     return L_SUCCESS;
929 }
930 
931 /* LEFTY builtin */
GFXpolygon(int argc,lvar_t * argv)932 int GFXpolygon (int argc, lvar_t *argv) {
933     Tobj po;
934     Ggattr_t gattr;
935     long i, pn;
936     int wi;
937 
938     if (
939         getint (argv[0].o, &wi) == -1 || !ISAWIDGET (wi) || !ISACANVAS2 (wi) ||
940         getgattr ((argc == 4) ? argv[3].o : NULL, &gattr) == -1
941     )
942         return L_FAILURE;
943     po = argv[2].o;
944     if ((pn = Tgettablen (po)) > gpn) {
945         gpp = Marraygrow (gpp, (long) pn * GPSIZE);
946         gpn = pn;
947     }
948     for (i = 0; i < pn; i++)
949         if (getxy (Tfindi (po, i), &gpp[i]) == -1)
950             return L_FAILURE;
951     Gpolygon (wi, pn, gpp, &gattr);
952     return L_SUCCESS;
953 }
954 
955 /* LEFTY builtin */
GFXsplinegon(int argc,lvar_t * argv)956 int GFXsplinegon (int argc, lvar_t *argv) {
957     Tobj po;
958     Ggattr_t gattr;
959     long i, pn;
960     int wi;
961 
962     if (
963         getint (argv[0].o, &wi) == -1 || !ISAWIDGET (wi) || !ISACANVAS2 (wi) ||
964         getgattr ((argc == 4) ? argv[3].o : NULL, &gattr) == -1
965     )
966         return L_FAILURE;
967     po = argv[2].o;
968     if ((pn = Tgettablen (po)) > gpn) {
969         gpp = Marraygrow (gpp, (long) pn * GPSIZE);
970         gpn = pn;
971     }
972     for (i = 0; i < pn; i++)
973         if (getxy (Tfindi (po, i), &gpp[i]) == -1)
974             return L_FAILURE;
975     Gsplinegon (wi, pn, gpp, &gattr);
976     return L_SUCCESS;
977 }
978 
979 /* LEFTY builtin */
GFXarc(int argc,lvar_t * argv)980 int GFXarc (int argc, lvar_t *argv) {
981     Ggattr_t gattr;
982     Grect_t r;
983     Gpoint_t op;
984     Gsize_t sp;
985     int wi, rtn;
986 
987     if (
988         getint (argv[0].o, &wi) == -1 || !ISAWIDGET (wi) || !ISACANVAS2 (wi) ||
989         getxy (argv[2].o, &op) == -1 || getxy (argv[3].o, &sp) == -1 ||
990         getgattr ((argc == 5) ? argv[4].o : NULL, &gattr) == -1
991     )
992         return L_FAILURE;
993     rtn = Garc (wi, op, sp, (double) 0, (double) 360, &gattr);
994     if (rtn == 0 && argv[1].o != null && ISACANVAS (wi)) {
995         r.o.x = op.x - sp.x, r.o.y = op.y - sp.y;
996         r.c.x = op.x + sp.x, r.c.y = op.y + sp.y;
997         rectmerge (NODEID (wi), argv[1].o, r);
998     }
999     return L_SUCCESS;
1000 }
1001 
1002 /* LEFTY builtin */
GFXtext(int argc,lvar_t * argv)1003 int GFXtext (int argc, lvar_t *argv) {
1004     Ggattr_t gattr;
1005     Gpoint_t p;
1006     char *s, *fn, *justs;
1007     double fs;
1008     int wi;
1009 
1010     if (
1011         getint (argv[0].o, &wi) == -1 || !ISAWIDGET (wi) || !ISACANVAS2 (wi) ||
1012         getxy (argv[2].o, &p) == -1 || getstr (argv[3].o, &s) == -1 ||
1013         getstr (argv[4].o, &fn) == -1 || getdouble (argv[5].o, &fs) == -1 ||
1014         getstr (argv[6].o, &justs) == -1 ||
1015         getgattr ((argc == 8) ? argv[7].o : NULL, &gattr) == -1
1016     )
1017         return L_FAILURE;
1018     Gtext (wi, s, p, fn, fs, justs, &gattr);
1019     return L_SUCCESS;
1020 }
1021 
1022 /* LEFTY builtin */
GFXtextsize(int argc,lvar_t * argv)1023 int GFXtextsize (int argc, lvar_t *argv) {
1024     Gsize_t sp;
1025     double fs;
1026     char *s, *fn;
1027     long m;
1028     int wi;
1029 
1030     if (
1031         getint (argv[0].o, &wi) == -1 || !ISAWIDGET (wi) || !ISACANVAS (wi) ||
1032         getstr (argv[1].o, &s) == -1 || getstr (argv[2].o, &fn) == -1 ||
1033         getdouble (argv[3].o, &fs) == -1
1034     )
1035         return L_FAILURE;
1036     if (Ggettextsize (wi, s, fn, fs, &sp) == -1)
1037         return L_FAILURE;
1038     m = Mpushmark ((rtno = Ttable (2)));
1039     Tinss (rtno, "x", Treal (sp.x)), Tinss (rtno, "y", Treal (sp.y));
1040     Mpopmark (m);
1041     return L_SUCCESS;
1042 }
1043 
1044 /* LEFTY builtin */
GFXcreatebitmap(int argc,lvar_t * argv)1045 int GFXcreatebitmap (int argc, lvar_t *argv) {
1046     Tobj bo, so;
1047     Gsize_t s;
1048     long rtnm;
1049     int wi, bi;
1050 
1051     if (
1052         getint (argv[0].o, &wi) == -1 || !ISAWIDGET (wi) || !ISACANVAS2 (wi) ||
1053         getxy (argv[1].o, &s) == -1
1054     )
1055         return L_FAILURE;
1056     if ((bi = Gcreatebitmap (wi, s)) == -1)
1057         return L_FAILURE;
1058     Tinsi (rootbo, bi, (bo = Ttable (4)));
1059     rtno = Tinteger (bi);
1060     rtnm = Mpushmark (rtno);
1061     Tinss (bo, "canvas", Tinteger (bi));
1062     Tinss (bo, "size", (so = Ttable (2)));
1063     Tinss (so, "x", Tinteger ((long) Gbitmaps[bi].size.x));
1064     Tinss (so, "y", Tinteger ((long) Gbitmaps[bi].size.y));
1065     Mpopmark (rtnm);
1066     return L_SUCCESS;
1067 }
1068 
1069 /* LEFTY builtin */
GFXdestroybitmap(int argc,lvar_t * argv)1070 int GFXdestroybitmap (int argc, lvar_t *argv) {
1071     int bi;
1072 
1073     if (getint (argv[0].o, &bi) == -1 || !ISABITMAP (bi))
1074         return L_FAILURE;
1075     Gdestroybitmap (bi);
1076     Tdeli (rootbo, bi);
1077     return L_SUCCESS;
1078 }
1079 
1080 /* LEFTY builtin */
GFXreadbitmap(int argc,lvar_t * argv)1081 int GFXreadbitmap (int argc, lvar_t *argv) {
1082     Tobj bo, so;
1083     long rtnm;
1084     int wi, bi, ioi;
1085 
1086     if (
1087         getint (argv[0].o, &wi) == -1 || !ISAWIDGET (wi) || !ISACANVAS2 (wi) ||
1088         getint (argv[1].o, &ioi) == -1 || ioi < 0 || ioi > ion ||
1089         !iop[ioi].inuse
1090     )
1091         return L_FAILURE;
1092     if ((bi = Greadbitmap (wi, iop[ioi].ifp)) == -1)
1093         return L_FAILURE;
1094     Tinsi (rootbo, bi, (bo = Ttable (4)));
1095     rtno = Tinteger (bi);
1096     rtnm = Mpushmark (rtno);
1097     Tinss (bo, "canvas", Tinteger (bi));
1098     Tinss (bo, "size", (so = Ttable (2)));
1099     Tinss (so, "x", Tinteger ((long) Gbitmaps[bi].size.x));
1100     Tinss (so, "y", Tinteger ((long) Gbitmaps[bi].size.y));
1101     Mpopmark (rtnm);
1102     return L_SUCCESS;
1103 }
1104 
1105 /* LEFTY builtin */
GFXwritebitmap(int argc,lvar_t * argv)1106 int GFXwritebitmap (int argc, lvar_t *argv) {
1107     int bi, ioi;
1108 
1109     if (
1110         getint (argv[0].o, &ioi) == -1 || ioi < 0 || ioi > ion ||
1111         !iop[ioi].inuse || getint (argv[1].o, &bi) == -1 || !ISABITMAP (bi)
1112     )
1113         return L_FAILURE;
1114     Gwritebitmap (iop[ioi].ofp, bi);
1115     return L_SUCCESS;
1116 }
1117 
1118 /* LEFTY builtin */
GFXbitblt(int argc,lvar_t * argv)1119 int GFXbitblt (int argc, lvar_t *argv) {
1120     Ggattr_t gattr;
1121     Grect_t r;
1122     Gpoint_t p;
1123     char *mode;
1124     int wi, bi, rtn;
1125 
1126     if (
1127         getint (argv[0].o, &wi) == -1 || !ISAWIDGET (wi) || !ISACANVAS2 (wi) ||
1128         getxy (argv[2].o, &p) == -1 || getrect (argv[3].o, &r) == -1 ||
1129         getint (argv[4].o, &bi) == -1 || getstr (argv[5].o, &mode) == -1 ||
1130         getgattr ((argc == 7) ? argv[6].o : NULL, &gattr) == -1
1131     )
1132         return L_FAILURE;
1133     rtn = Gbitblt (wi, p, r, bi, mode, &gattr);
1134     if (
1135         rtn == 0 && argv[1].o != null && ISACANVAS (wi) &&
1136         strcmp (mode, "b2c") == 0
1137     )
1138         rectmerge (NODEID (wi), argv[1].o, r);
1139     return L_SUCCESS;
1140 }
1141 
1142 /* LEFTY builtin */
GFXclearpick(int argc,lvar_t * argv)1143 int GFXclearpick (int argc, lvar_t *argv) {
1144     int wi;
1145 
1146     if (getint (argv[0].o, &wi) == -1 || !ISAWIDGET (wi) || !ISACANVAS (wi))
1147         return L_FAILURE;
1148     if (argv[1].o != null)
1149         rectdelete (NODEID (wi), argv[1].o);
1150     return L_SUCCESS;
1151 }
1152 
1153 /* LEFTY builtin */
GFXsetpick(int argc,lvar_t * argv)1154 int GFXsetpick (int argc, lvar_t *argv) {
1155     Grect_t r;
1156     int wi;
1157 
1158     if (
1159         getint (argv[0].o, &wi) == -1 || !ISAWIDGET (wi) || !ISACANVAS (wi) ||
1160         getrect (argv[2].o, &r) == -1
1161     )
1162         return L_FAILURE;
1163     if (argv[1].o != null)
1164         rectinsert (NODEID (wi), argv[1].o, r);
1165     return L_SUCCESS;
1166 }
1167 
1168 /* LEFTY builtin */
GFXdisplaymenu(int argc,lvar_t * argv)1169 int GFXdisplaymenu (int argc, lvar_t *argv) {
1170     Tobj mo, meo;
1171     char buf[50];
1172     char *entries[1];
1173     int wi, mi, mei;
1174     int rtn;
1175 
1176     if (
1177         getint (argv[0].o, &wi) == -1 || !ISAWIDGET (wi) ||
1178         !(ISACANVAS (wi) || ISALABEL (wi))
1179     )
1180         return L_FAILURE;
1181     mo = argv[1].o;
1182     if ((mi = menufind (NODEID (wi), mo, Tgettime (mo))) != -1) {
1183         if ((rtn = Gmenudisplay (wi, mi)) == -1)
1184             rtno = NULL;
1185         else
1186             rtno = Tinteger (rtn);
1187         return L_SUCCESS;
1188     }
1189     wattri = 0;
1190     mi = Gcreatewidget (wi, G_MENUWIDGET, wattri, wattrp);
1191     mei = 0;
1192     while ((meo = Tfindi (mo, mei))) {
1193         switch (Tgettype (meo)) {
1194         case T_STRING:
1195             entries[0] = Tgetstring (meo);
1196             break;
1197         case T_INTEGER:
1198             sprintf (buf, "%d", (int) Tgetnumber (meo));
1199             entries[0] = &buf[0];
1200             break;
1201         case T_REAL:
1202             sprintf (buf, "%f", Tgetnumber (meo));
1203             entries[0] = &buf[0];
1204             break;
1205         }
1206         Gmenuaddentries (mi, 1, &entries[0]);
1207         mei++;
1208     }
1209     menuinsert (NODEID (wi), mo, Tgettime (mo), mi);
1210     Ttime++;
1211     if ((rtn = Gmenudisplay (wi, mi)) == -1)
1212         rtno = NULL;
1213     else
1214         rtno = Tinteger (rtn);
1215     return L_SUCCESS;
1216 }
1217 
1218 /* LEFTY builtin */
GFXcolormap(int argc,lvar_t * argv)1219 int GFXcolormap (int argc, lvar_t *argv) {
1220     char *cs;
1221     int cni;
1222     long rtnm;
1223 
1224     if (getstr (argv[0].o, &cs) == -1)
1225         return L_FAILURE;
1226     rtno = NULL;
1227     for (cni = 0; cni < sizeof (colornames) / sizeof (colorname_t); cni++) {
1228         if (strcmp (colornames[cni].name, cs) != 0)
1229             continue;
1230         rtno = Ttable (4);
1231         rtnm = Mpushmark (rtno);
1232         Tinss (rtno, "r", Tinteger (colornames[cni].r));
1233         Tinss (rtno, "g", Tinteger (colornames[cni].g));
1234         Tinss (rtno, "b", Tinteger (colornames[cni].b));
1235         Mpopmark (rtnm);
1236         break;
1237     }
1238     return L_SUCCESS;
1239 }
1240 
getwattr(Tobj ao,int * type)1241 static int getwattr (Tobj ao, int *type) {
1242     Tkvindex_t tkvi;
1243     Tobj co;
1244     int *ap;
1245     char *ts;
1246     int li, ai;
1247 
1248     wattri = 0;
1249     if (!ao && Tgettype (ao) != T_TABLE)
1250         return -1;
1251     if (*type == -1) {
1252         if (getstr (Tfinds (ao, "type"), &ts) == -1)
1253             return -1;
1254         for (ap = NULL, li = 0; Gwlist[li].wname; li++) {
1255             if (strcmp (ts, Gwlist[li].wname) == 0) {
1256                 *type = Gwlist[li].wid;
1257                 ap = Gwlist[li].attrid;
1258                 break;
1259             }
1260         }
1261         if (*type == -1)
1262             return -1;
1263         if (wattri >= wattrn) {
1264             wattrp = Marraygrow (
1265                 wattrp, (long) (wattrn + WATTRINCR) * WATTRSIZE
1266             );
1267             wattrn += WATTRINCR;
1268         }
1269         switch (*type) {
1270         case G_TEXTWIDGET:
1271             wattrp[wattri].id = G_ATTRNEWLINECB;
1272             wattrp[wattri].u.func = GFXtextcb, wattri++;
1273             break;
1274         case G_ARRAYWIDGET:
1275             wattrp[wattri].id = G_ATTRRESIZECB;
1276             wattrp[wattri].u.func = GFXarrayresizecb, wattri++;
1277             break;
1278         case G_BUTTONWIDGET:
1279             wattrp[wattri].id = G_ATTRBUTTONCB;
1280             wattrp[wattri].u.func = GFXbuttoncb, wattri++;
1281             break;
1282         case G_LABELWIDGET:
1283             wattrp[wattri].id = G_ATTREVENTCB;
1284             wattrp[wattri].u.func = GFXlabelcb, wattri++;
1285             break;
1286         case G_CANVASWIDGET:
1287             wattrp[wattri].id = G_ATTREVENTCB;
1288             wattrp[wattri].u.func = GFXevent, wattri++;
1289             break;
1290         case G_VIEWWIDGET:
1291             wattrp[wattri].id = G_ATTREVENTCB;
1292             wattrp[wattri].u.func = GFXviewcb, wattri++;
1293             break;
1294         }
1295     } else {
1296         for (ap = NULL, li = 0; Gwlist[li].wname; li++) {
1297             if (*type == Gwlist[li].wid) {
1298                 ap = Gwlist[li].attrid;
1299                 break;
1300             }
1301         }
1302     }
1303     for (ai = 0; ap[ai] != -1; ai++) {
1304         if (wattri >= wattrn) {
1305             wattrp = Marraygrow (
1306                 wattrp, (long) (wattrn + WATTRINCR) * WATTRSIZE
1307             );
1308             wattrn += WATTRINCR;
1309         }
1310         wattrp[wattri].id = ap[ai];
1311         switch (Gwattrmap[ap[ai]].type) {
1312         case G_ATTRTYPEPOINT:
1313             getwpointnset (Tfinds (ao, Gwattrmap[ap[ai]].name));
1314             break;
1315         case G_ATTRTYPESIZE:
1316             getwsizenset (Tfinds (ao, Gwattrmap[ap[ai]].name));
1317             break;
1318         case G_ATTRTYPERECT:
1319             getwrectnset (Tfinds (ao, Gwattrmap[ap[ai]].name));
1320             break;
1321         case G_ATTRTYPETEXT:
1322             getwstrnset (Tfinds (ao, Gwattrmap[ap[ai]].name));
1323             break;
1324         case G_ATTRTYPEINT:
1325             getwintnset (Tfinds (ao, Gwattrmap[ap[ai]].name));
1326             break;
1327         case G_ATTRTYPECOLOR:
1328             if ((co = Tfinds (
1329                 ao, Gwattrmap[ap[ai]].name
1330             )) && Tgettype (co) == T_TABLE) {
1331                 for (Tgetfirst (co, &tkvi); tkvi.kvp; Tgetnext (&tkvi)) {
1332                     if (wattri >= wattrn) {
1333                         wattrp = Marraygrow (
1334                             wattrp, (long) (wattrn + WATTRINCR) * WATTRSIZE
1335                         );
1336                         wattrn += WATTRINCR;
1337                     }
1338                     wattrp[wattri].id = ap[ai];
1339                     getwcolornset (tkvi.kvp->ko, tkvi.kvp->vo);
1340                 }
1341             }
1342             break;
1343         }
1344     }
1345     return wattri;
1346 }
1347 
getgattr(Tobj ao,Ggattr_t * dp)1348 static int getgattr (Tobj ao, Ggattr_t *dp) {
1349     Tkvindex_t tkvi;
1350     char *s, *s2;
1351 
1352     dp->flags = 0;
1353     if (!ao)
1354         return 0;
1355     if (Tgettype (ao) != T_TABLE)
1356         return -1;
1357     for (Tgetfirst (ao, &tkvi); tkvi.kvp; Tgetnext (&tkvi)) {
1358         if (Tgettype (tkvi.kvp->ko) != T_STRING)
1359             continue;
1360         s = Tgetstring (tkvi.kvp->ko);
1361         if (strcmp (s, "color") == 0) {
1362             getaintnset (tkvi.kvp->vo, dp->color, G_GATTRCOLOR);
1363         } else if (strcmp (s, "width") == 0) {
1364             getaintnset (tkvi.kvp->vo, dp->width, G_GATTRWIDTH);
1365         } else if (strcmp (s, "mode") == 0) {
1366             getastrnset (tkvi.kvp->vo, s2, G_GATTRMODE);
1367             if (dp->flags & G_GATTRMODE) {
1368                 if (strcmp (s2, "src") == 0)
1369                     dp->mode = G_SRC;
1370                 else if (strcmp (s2, "xor") == 0)
1371                     dp->mode = G_XOR;
1372                 else
1373                     dp->flags &= ~G_GATTRMODE;
1374             }
1375         } else if (strcmp (s, "fill") == 0) {
1376             getastrnset (tkvi.kvp->vo, s2, G_GATTRFILL);
1377             if (dp->flags & G_GATTRFILL) {
1378                 if (strcmp (s2, "on") == 0)
1379                     dp->fill = 1;
1380                 else if (strcmp (s2, "off") == 0)
1381                     dp->fill = 0;
1382                 else
1383                     dp->flags &= ~G_GATTRFILL;
1384             }
1385         } else if (strcmp (s, "style") == 0) {
1386             getastrnset (tkvi.kvp->vo, s2, G_GATTRSTYLE);
1387             if (dp->flags & G_GATTRSTYLE) {
1388                 if (strcmp (s2, "solid") == 0)
1389                     dp->style = G_SOLID;
1390                 else if (strcmp (s2, "dashed") == 0)
1391                     dp->style = G_DASHED;
1392                 else if (strcmp (s2, "dotted") == 0)
1393                     dp->style = G_DOTTED;
1394                 else if (strcmp (s2, "longdashed") == 0)
1395                     dp->style = G_LONGDASHED;
1396                 else if (strcmp (s2, "shortdashed") == 0)
1397                     dp->style = G_SHORTDASHED;
1398                 else
1399                     dp->flags &= ~G_GATTRSTYLE;
1400             }
1401         }
1402     }
1403     return 0;
1404 }
1405 
getrect(Tobj ro,Grect_t * rp)1406 static int getrect (Tobj ro, Grect_t *rp) {
1407     if (ro && Tgettype (ro) == T_TABLE)
1408         if (
1409             getxy (Tfindi (ro, 0), &rp->o) != -1 &&
1410             getxy (Tfindi (ro, 1), &rp->c) != -1
1411         )
1412             return 0;
1413     return -1;
1414 }
1415 
getxy(Tobj po,Gxy_t * xyp)1416 static int getxy (Tobj po, Gxy_t *xyp) {
1417     Tobj xo, yo;
1418 
1419     if (po && Tgettype (po) == T_TABLE) {
1420         xo = Tfinds (po, "x"), yo = Tfinds (po, "y");
1421         if (xo && T_ISNUMBER (xo) && yo && T_ISNUMBER (yo)) {
1422             xyp->x = Tgetnumber (xo), xyp->y = Tgetnumber (yo);
1423             return 0;
1424         }
1425     }
1426     return -1;
1427 }
1428 
getint(Tobj to,int * ip)1429 static int getint (Tobj to, int *ip) {
1430     if (to && T_ISNUMBER (to)) {
1431         *ip = Tgetnumber (to);
1432         return 0;
1433     }
1434     return -1;
1435 }
1436 
getdouble(Tobj to,double * dp)1437 static int getdouble (Tobj to, double *dp) {
1438     if (to && T_ISNUMBER (to)) {
1439         *dp = Tgetnumber (to);
1440         return 0;
1441     }
1442     return -1;
1443 }
1444 
getstr(Tobj so,char ** s)1445 static int getstr (Tobj so, char **s) {
1446     if (so && T_ISSTRING (so)) {
1447         *s = Tgetstring (so);
1448         return 0;
1449     }
1450     return -1;
1451 }
1452 
getcolor(Tobj ko,Tobj co,Gcolor_t * cp)1453 static int getcolor (Tobj ko, Tobj co, Gcolor_t *cp) {
1454     Tobj ro, go, bo, ho, so, vo;
1455     char *s, *s1, *s2;
1456     float hc, sc, vc;
1457     int cni;
1458     int r, g, b, a;
1459 
1460     if (ko && T_ISNUMBER (ko))
1461         cp->index = Tgetnumber (ko);
1462     else
1463         return -1;
1464     if (!co || !(T_ISSTRING (co) || T_ISTABLE (co)))
1465         return -1;
1466     if (T_ISSTRING (co)) {
1467         s = Tgetstring (co);
1468         while (*s == ' ')
1469             s++; /* skip over any leading spaces */
1470         if (*s == '#' && sscanf (s, "#%2x%2x%2x%2x", &r, &g, &b, &a) >= 3) {
1471             cp->r = r;
1472             cp->g = g;
1473             cp->b = b;
1474             return 0;
1475         }
1476         if ((isdigit (*s) || *s == '.') && scanhsv (s, &hc, &sc, &vc)) {
1477             hsv2rgb (hc, sc, vc, cp);
1478             return 0;
1479         }
1480         for (cni = 0; cni < sizeof (colornames) / sizeof (colorname_t); cni++) {
1481             for (s1 = colornames[cni].name, s2 = s; *s1 && *s2; s1++, s2++) {
1482                 if (*s2 == ' ' || *s2 == '_')
1483                     s2++;
1484                 else if (*s1 != *s2)
1485                     break;
1486             }
1487             if (*s1 != *s2)
1488                 continue;
1489             cp->r = colornames[cni].r;
1490             cp->g = colornames[cni].g;
1491             cp->b = colornames[cni].b;
1492             return 0;
1493         }
1494         return -1;
1495     } else {
1496         ro = Tfinds (co, "r");
1497         go = Tfinds (co, "g");
1498         bo = Tfinds (co, "b");
1499         if (
1500             ro && T_ISNUMBER (ro) && go && T_ISNUMBER (go) &&
1501             bo && T_ISNUMBER (bo)
1502         ) {
1503             cp->r = Tgetnumber (ro);
1504             cp->g = Tgetnumber (go);
1505             cp->b = Tgetnumber (bo);
1506             return 0;
1507         } else {
1508             ho = Tfinds (co, "h");
1509             so = Tfinds (co, "s");
1510             vo = Tfinds (co, "v");
1511             if (
1512                 ho && T_ISNUMBER (ho) && so && T_ISNUMBER (so) &&
1513                 vo && T_ISNUMBER (vo)
1514             ) {
1515                 hc = Tgetnumber (ho);
1516                 sc = Tgetnumber (so);
1517                 vc = Tgetnumber (vo);
1518                 hsv2rgb (hc, sc, vc, cp);
1519                 return 0;
1520             } else
1521                 return -1;
1522         }
1523     }
1524     return -1;
1525 }
1526 
nodeinit(int ni)1527 static void nodeinit (int ni) {
1528     int li, ki;
1529 
1530     for (li = 0; li < LISTSIZE; li++)
1531         gfxnodes[ni].rect[li] = NULL, gfxnodes[ni].menu[li] = NULL;
1532     gfxnodes[ni].ls = gfxnodes[ni].ms = gfxnodes[ni].rs = 0;
1533     for (ki = 0; ki < 256; ki++)
1534         gfxnodes[ni].ks[ki] = 0;
1535     gfxnodes[ni].plvo = gfxnodes[ni].pmvo = gfxnodes[ni].prvo = 0;
1536     gfxnodes[ni].pb3vo = gfxnodes[ni].pb4vo = 0;
1537     for (ki = 0; ki < 256; ki++)
1538         gfxnodes[ni].pkvo[ki] = 0;
1539 }
1540 
nodeterm(int ni)1541 static void nodeterm (int ni) {
1542     gfxrect_t **rp;
1543     gfxrect_t *crp, *nrp;
1544     gfxmenu_t **mp;
1545     gfxmenu_t *cmp, *nmp;
1546     int li;
1547 
1548     for (li = 0; li < LISTSIZE; li++) {
1549         rp = &gfxnodes[ni].rect[li];
1550         for (crp = *rp; crp; crp = nrp)
1551             nrp = crp->next, free (crp);
1552         *rp = NULL;
1553         mp = &gfxnodes[ni].menu[li];
1554         for (cmp = *mp; cmp; cmp = nmp)
1555             nmp = cmp->next, free (cmp);
1556         *mp = NULL;
1557     }
1558 }
1559 
rectinit(int ni)1560 static void rectinit (int ni) {
1561     int li;
1562 
1563     for (li = 0; li < LISTSIZE; li++)
1564         gfxnodes[ni].rect[li] = NULL;
1565 }
1566 
rectterm(int ni)1567 static void rectterm (int ni) {
1568     gfxrect_t **rp;
1569     gfxrect_t *crp, *nrp;
1570     int li;
1571 
1572     for (li = 0; li < LISTSIZE; li++) {
1573         rp = &gfxnodes[ni].rect[li];
1574         for (crp = *rp; crp; crp = nrp)
1575             nrp = crp->next, free (crp);
1576         *rp = NULL;
1577     }
1578 }
1579 
rectinsert(int ni,Tobj ko,Grect_t r)1580 static void rectinsert (int ni, Tobj ko, Grect_t r) {
1581     gfxrect_t **rp;
1582     gfxrect_t *crp;
1583 
1584     rp = &gfxnodes[ni].rect[(uint64_t) ko % LISTSIZE];
1585     for (crp = *rp; crp; crp = crp->next)
1586         if (crp->ko == ko) {
1587             crp->r.o.x = min (r.o.x, r.c.x);
1588             crp->r.o.y = min (r.o.y, r.c.y);
1589             crp->r.c.x = max (r.o.x, r.c.x);
1590             crp->r.c.y = max (r.o.y, r.c.y);
1591             return;
1592         }
1593     if (!(crp = malloc (sizeof (gfxrect_t))))
1594         panic1 (POS, "rectinsert", "rect malloc failed");
1595 
1596     crp->ko = ko;
1597     crp->r.o.x = min (r.o.x, r.c.x);
1598     crp->r.o.y = min (r.o.y, r.c.y);
1599     crp->r.c.x = max (r.o.x, r.c.x);
1600     crp->r.c.y = max (r.o.y, r.c.y);
1601     crp->next = *rp;
1602     *rp = crp;
1603 }
1604 
rectmerge(int ni,Tobj ko,Grect_t r)1605 static void rectmerge (int ni, Tobj ko, Grect_t r) {
1606     gfxrect_t **rp;
1607     gfxrect_t *crp;
1608 
1609     rp = &gfxnodes[ni].rect[(uint64_t) ko % LISTSIZE];
1610     for (crp = *rp; crp; crp = crp->next)
1611         if (crp->ko == ko) {
1612             crp->r.o.x = min (crp->r.o.x, min (r.o.x, r.c.x));
1613             crp->r.o.y = min (crp->r.o.y, min (r.o.y, r.c.y));
1614             crp->r.c.x = max (crp->r.c.x, max (r.o.x, r.c.x));
1615             crp->r.c.y = max (crp->r.c.y, max (r.o.y, r.c.y));
1616             return;
1617         }
1618     if (!(crp = malloc (sizeof (gfxrect_t))))
1619         panic1 (POS, "rectmerge", "rect malloc failed");
1620 
1621     crp->ko = ko;
1622     crp->r.o.x = min (r.o.x, r.c.x);
1623     crp->r.o.y = min (r.o.y, r.c.y);
1624     crp->r.c.x = max (r.o.x, r.c.x);
1625     crp->r.c.y = max (r.o.y, r.c.y);
1626     crp->next = *rp;
1627     *rp = crp;
1628 }
1629 
rectfind(int ni,Gpoint_t p)1630 static Tobj rectfind (int ni, Gpoint_t p) {
1631     gfxrect_t **rp;
1632     gfxrect_t *crp;
1633     int li;
1634 
1635     for (li = 0; li < LISTSIZE; li++) {
1636         rp = &gfxnodes[ni].rect[li];
1637         for (crp = *rp; crp; crp = crp->next)
1638             if (
1639                 crp->r.o.x <= p.x && crp->r.c.x >= p.x &&
1640                 crp->r.o.y <= p.y && crp->r.c.y >= p.y
1641             )
1642                 return crp->ko;
1643     }
1644     return NULL;
1645 }
1646 
rectdelete(int ni,Tobj ko)1647 static void rectdelete (int ni, Tobj ko) {
1648     gfxrect_t **rp;
1649     gfxrect_t *crp, *prp;
1650 
1651     rp = &gfxnodes[ni].rect[(uint64_t) ko % LISTSIZE];
1652     for (crp = *rp, prp = NULL; crp; prp = crp, crp = crp->next)
1653         if (crp->ko == ko) {
1654             if (crp == *rp)
1655                 *rp = crp->next;
1656             else
1657                 prp->next = crp->next;
1658             free (crp);
1659             return;
1660         }
1661 }
1662 
rectprune(int ni)1663 static void rectprune (int ni) {
1664     gfxrect_t **rp;
1665     gfxrect_t *crp, *prp;
1666     int li;
1667 
1668     for (li = 0; li < LISTSIZE; li++) {
1669         rp = &gfxnodes[ni].rect[li];
1670         for (crp = *rp, prp = NULL; crp; ) {
1671             if (!M_AREAOF (crp->ko)) {
1672                 if (crp == *rp)
1673                     *rp = crp->next, free (crp), crp = *rp;
1674                 else
1675                     prp->next = crp->next, free (crp), crp = prp->next;
1676             } else
1677                 prp = crp, crp = crp->next;
1678         }
1679     }
1680 }
1681 
menuinsert(int ni,Tobj ko,long time,int mi)1682 static void menuinsert (int ni, Tobj ko, long time, int mi) {
1683     gfxmenu_t **mp;
1684     gfxmenu_t *cmp;
1685 
1686     mp = &gfxnodes[ni].menu[(uint64_t) ko % LISTSIZE];
1687     for (cmp = *mp; cmp; cmp = cmp->next)
1688         if (cmp->ko == ko) {
1689             cmp->time = time, cmp->mi = mi;
1690             return;
1691         }
1692     if (!(cmp = malloc (sizeof (gfxmenu_t))))
1693         panic1 (POS, "menuinsert", "menu malloc failed");
1694 
1695     cmp->ko = ko;
1696     cmp->time = time;
1697     cmp->mi = mi;
1698     cmp->next = *mp;
1699     *mp = cmp;
1700 }
1701 
menufind(int ni,Tobj ko,long time)1702 static int menufind (int ni, Tobj ko, long time) {
1703     gfxmenu_t **mp;
1704     gfxmenu_t *cmp;
1705 
1706     mp = &gfxnodes[ni].menu[(uint64_t) ko % LISTSIZE];
1707     for (cmp = *mp; cmp; cmp = cmp->next)
1708         if (cmp->ko == ko && cmp->time == time)
1709             return cmp->mi;
1710     return -1;
1711 }
1712 
menuprune(int ni)1713 static void menuprune (int ni) {
1714     gfxmenu_t **mp;
1715     gfxmenu_t *cmp, *pmp;
1716     int li;
1717 
1718     for (li = 0; li < LISTSIZE; li++) {
1719         mp = &gfxnodes[ni].menu[li];
1720         for (cmp = *mp, pmp = NULL; cmp; ) {
1721             if (!M_AREAOF (cmp->ko)) {
1722                 if (cmp == *mp)
1723                     *mp = cmp->next, free (cmp), cmp = *mp;
1724                 else
1725                     pmp->next = cmp->next, free (cmp), cmp = pmp->next;
1726             } else
1727                 pmp = cmp, cmp = cmp->next;
1728         }
1729     }
1730 }
1731 
1732 /*  scan a string for 3 floating point numbers separated by
1733     white-space or commas.
1734 */
scanhsv(char * strp,float * h,float * s,float * v)1735 static int scanhsv (char *strp, float *h, float *s, float *v) {
1736     char *endp;
1737 
1738     *h = strtod (strp, &endp);
1739     if (endp == strp)
1740         return 0;
1741     strp = endp;
1742     while (*strp == ',' || isspace (*strp))
1743         strp++;
1744     *s = strtod (strp, &endp);
1745     if (endp == strp)
1746         return 0;
1747     strp = endp;
1748     while (*strp == ',' || isspace (*strp))
1749         strp++;
1750     *v = strtod (strp, &endp);
1751     if (endp == strp)
1752         return 0;
1753     return 1;
1754 }
1755 
hsv2rgb(float h,float s,float v,Gcolor_t * cp)1756 static void hsv2rgb (float h, float s, float v, Gcolor_t *cp) {
1757     float r, g, b, f, p, q, t;
1758     int i;
1759 
1760     /* clip to reasonable values */
1761     h = max (min (h, 1.0), 0.0);
1762     s = max (min (s, 1.0), 0.0);
1763     v = max (min (v, 1.0), 0.0);
1764     r = g = b = 0.0;
1765 
1766     if (s == 0.0)
1767         r = g = b = v;
1768     else {
1769         if (h == 1.0)
1770             h = 0.0;
1771         h = h * 6.0;
1772         i = (int) h;
1773         f = h - (float) i;
1774         p = v * (1 - s);
1775         q = v * (1 - (s * f));
1776         t = v * (1 - (s * (1 - f)));
1777         switch (i) {
1778             case 0: r = v; g = t; b = p; break;
1779             case 1: r = q; g = v; b = p; break;
1780             case 2: r = p; g = v; b = t; break;
1781             case 3: r = p; g = q; b = v; break;
1782             case 4: r = t; g = p; b = v; break;
1783             case 5: r = v; g = p; b = q; break;
1784         }
1785     }
1786     cp->r = (int) (255.0 * r);
1787     cp->g = (int) (255.0 * g);
1788     cp->b = (int) (255.0 * b);
1789 }
1790