1/* Hello, Emacs, this is -*-C-*- */
2
3/* GNUPLOT - ggi.trm */
4
5/*[
6 * Copyright 2000, 2004
7 *
8 * Permission to use, copy, and distribute this software and its
9 * documentation for any purpose with or without fee is hereby granted,
10 * provided that the above copyright notice appear in all copies and
11 * that both that copyright notice and this permission notice appear
12 * in supporting documentation.
13 *
14 * Permission to modify the software is granted, but not the right to
15 * distribute the complete modified source code.  Modifications are to
16 * be distributed as patches to the released version.  Permission to
17 * distribute binaries produced by compiling modified sources is granted,
18 * provided you
19 *   1. distribute the corresponding source modifications from the
20 *    released version in the form of a patch file along with the binaries,
21 *   2. add special version identification to distinguish your version
22 *    in addition to the base release version number,
23 *   3. provide your name and address as the primary contact for the
24 *    support of your modified version, and
25 *   4. retain our contact information in regard to use of the base
26 *    software.
27 * Permission to distribute the released version of the source code along
28 * with corresponding source modifications in the form of a patch file is
29 * granted with same provisions 2 through 4 for binary distributions.
30 *
31 * This software is provided "as is" without express or implied warranty
32 * to the extent permitted by applicable law.
33 ]*/
34
35/*
36 * AUTHOR:
37 *   Cesar Crusius <crusius@leland.stanford.edu>
38 *   event / mouse processing & double-buffering
39 *   by Johannes Zellner <johannes@zellner.org>
40 *   pm3d support by Johannes Zellner <johannes@zellner.org> (Oct. 2000)
41 *
42 *   TODO:
43 *
44 *   	- reimplement wmh (if it's available)
45 *   	- implement window title using wmh (if it's available)
46 *   	- check for availability of two frames, and if not
47 *   	  do it with one frame. (will eventually not do
48 *   	  with mouse/event reporting)
49 *   	- check if libxmi is available and if so, use
50 *   	  it to draw filled polygons.
51 *   	- enable cursors using blits.
52 */
53
54#include "driver.h"
55
56#ifdef TERM_REGISTER
57register_term(ggi)
58#endif
59
60#ifdef TERM_PROTO
61
62#include <ggi/ggi.h>
63#ifdef USE_MOUSE
64#  include <ggi/ggi-unix.h>
65#endif
66
67#ifdef HAVE_GGI_WMH_H
68#   include <ggi/wmh.h>
69static TBOOLEAN GGI_use_whm = 0;
70#endif
71
72#if 1
73#if defined(HAVE_GGI_XMI_H)
74#   define ENABLE_XMI 1
75#endif
76#endif
77
78#ifdef ENABLE_XMI
79#   include <ggi/xmi.h>
80#endif
81
82static void GGI_line_colors(void);
83TERM_PUBLIC void GGI_graphics(void);
84TERM_PUBLIC void GGI_set_size(void);
85TERM_PUBLIC void GGI_init(void);
86TERM_PUBLIC void GGI_linetype(int);
87TERM_PUBLIC void GGI_move(unsigned int,unsigned int);
88TERM_PUBLIC void GGI_options(void);
89TERM_PUBLIC void GGI_put_text(unsigned int,unsigned int, const char*);
90TERM_PUBLIC void GGI_suspend(void);
91TERM_PUBLIC void GGI_resume(void);
92TERM_PUBLIC void GGI_fillbox(int style, unsigned int x, unsigned int y,
93				unsigned int w, unsigned int h);
94TERM_PUBLIC void GGI_close(void);
95TERM_PUBLIC void GGI_reset(void);
96TERM_PUBLIC void GGI_text(void);
97TERM_PUBLIC void GGI_vector(unsigned int,unsigned int);
98
99#ifdef USE_MOUSE
100
101/* zoom box information */
102
103typedef struct  {
104    int x;
105    int y;
106} GGI_point_t;
107
108typedef struct {
109    int x;
110    int y;
111    int width;
112    int height;
113    char str[0xff];
114} GGI_vertex_t;
115
116TERM_PUBLIC long int GGI_SetTime(const struct timeval* current);
117TERM_PUBLIC int GGI_from_keysym(uint32_t keysym);
118TERM_PUBLIC int GGI_from_button(uint32_t button);
119TERM_PUBLIC int GGI_y(int32_t y);
120TERM_PUBLIC int GGI_dispatch_event(const ggi_event* event);
121TERM_PUBLIC int GGI_eventually_update_modifiers(const ggi_event* event, const int add);
122TERM_PUBLIC int GGI_waitforinput(int);
123TERM_PUBLIC void GGI_draw_ruler(void);
124TERM_PUBLIC void GGI_clear_zoombox(void);
125TERM_PUBLIC void GGI_draw_zoombox(void);
126TERM_PUBLIC void GGI_set_ruler(int, int);
127TERM_PUBLIC void GGI_set_cursor(int, int, int);
128TERM_PUBLIC void GGI_save_frame_canvas(void);
129TERM_PUBLIC void GGI_save_frame_stl(void);
130TERM_PUBLIC void GGI_replot(void);
131TERM_PUBLIC void GGI_clear(const GGI_vertex_t* v, const int tag);
132TERM_PUBLIC void GGI_save_puts(GGI_vertex_t* v, const int tag);
133TERM_PUBLIC void GGI_set_vertex(GGI_vertex_t* v, const int x, const int y, const char* str, const int tag);
134TERM_PUBLIC void GGI_abort_zooming(void);
135TERM_PUBLIC void GGI_put_tmptext(int, const char str[]);
136TERM_PUBLIC void GGI_relative(int r[2]);
137TERM_PUBLIC void GGI_clear_hline(int x1, int x2, int y);
138TERM_PUBLIC void GGI_clear_vline(int y1, int y2, int x);
139TERM_PUBLIC void GGI_draw_hline(int x1, int x2, int y);
140TERM_PUBLIC void GGI_draw_vline(int y1, int y2, int x);
141TERM_PUBLIC void GGI_set_clipboard(const char[]);
142#endif /* USE_MOUSE */
143
144TERM_PUBLIC int GGI_make_palette(t_sm_palette*);
145TERM_PUBLIC void GGI_previous_palette(void);
146TERM_PUBLIC void GGI_set_color(struct t_colorspec *colorspec);
147#ifdef ENABLE_XMI
148TERM_PUBLIC void GGI_filled_polygon(int, gpiPoint*);
149#endif
150
151#define GOT_GGI_PROTO
152#endif
153
154#ifndef TERM_PROTO_ONLY
155#ifdef TERM_BODY
156
157#define GGI_XMAX 800
158#define GGI_YMAX 600
159#define GGI_VCHAR 8
160#define GGI_HCHAR 8
161#define GGI_VTIC 8
162#define GGI_HTIC 8
163
164
165#ifdef USE_MOUSE
166static GGI_vertex_t GGI_zoom[2][2];
167static GGI_vertex_t GGI_stl_vertex;
168static GGI_point_t GGI_ruler = {-1, -1};
169static GGI_point_t GGI_zoombox[2] = {{-1, -1}, {-1, -1}};
170
171static struct timeval GGI_timestamp;
172static int GGI_mouse_x = 0;
173static int GGI_mouse_y = 0;
174static int GGI_modifiers = 0;
175static int GGI_use_mouse = 1; /* mouse is on by default */
176static unsigned int GGIcanvas_height = 0;
177static int GGI_font_width = 0;
178static int GGI_font_height = 0;
179static int GGI_saved_canvas = 0;
180static int GGI_saved_stl = 0;
181static int GGI_needs_update = 1;
182#endif
183
184static t_sm_palette GGI_save_pal = {
185    -1, -1, -1, -1, -1, -1, -1, -1,
186    (rgb_color*) 0, -1, -1
187};
188
189/* First to some global variables
190 *
191 * GGIvisual is our 'piece of paper.'
192 * GGIborderColor and axixColor have the obvious meanings.
193 * GGIcolors are the colors for linestyles 0 and up.
194 * GGImap is for initializing colors.
195 * GGIx,GGIy are the current coordinates.
196 * GGIwidth, GGIheight are the extensions in the visual.
197 * GGIymax = term->ymax
198 */
199static ggi_visual_t GGIvisual = (ggi_visual_t)0;
200static ggi_pixel GGIborderColor;
201static ggi_pixel GGIaxisColor;
202static ggi_pixel GGIblack;
203static ggi_pixel GGIbackground;
204static ggi_pixel GGIcolors[7];
205#define GGI_PM3D_COLORS 240
206static const int ggi_pm3d_colors = GGI_PM3D_COLORS;
207static ggi_pixel GGI_smooth_colors[GGI_PM3D_COLORS];
208static unsigned int GGIx,GGIy;
209static unsigned int GGIwidth, GGIheight, GGIymax;
210#if 0
211static unsigned int Xenv;
212#endif
213static int GGI_frames =
214#ifdef USE_MOUSE
2152
216#else
2171
218#endif
219;
220
221#ifdef ENABLE_XMI
222static miGC* GGI_miGC = (miGC*)0;
223static miPaintedSet* GGI_miPaintedSet = (miPaintedSet*)0;
224static miPixel GGI_miPixels[2]; /* only GGI_miPixels[1] is used */
225static int GGI_numblendstages = 0;
226static miBlendStage GGI_blendstages[4];
227#endif
228
229static TBOOLEAN GGI_mode_changed = 1;
230static char GGI_mode_spec[0xff] = "";
231static int GGI_acceleration = 7; /* arbitrary */
232
233enum GGI_id {
234    GGI_MODE,
235    GGI_ACCELERATION,
236    GGI_OTHER
237};
238
239static struct gen_table GGI_opts[] =
240{
241    { "mo$de", GGI_MODE },
242    { "ac$celeration", GGI_ACCELERATION },
243    { NULL, GGI_OTHER }
244};
245
246static void
247GGI_line_colors()
248{
249    ggi_pixel GGIwhite,GGIred,GGIgreen,GGIblue,GGIcyan,GGImagenta,GGIgray;
250    ggi_pixel GGIyellow;
251    ggi_color color;
252
253    color.r = 0xFFFF; color.g = 0xFFFF; color.b = 0xFFFF; GGIwhite   = ggiMapColor(GGIvisual,&color);
254    color.r = 0x0000; color.g = 0x0000; color.b = 0x0000; GGIblack   = ggiMapColor(GGIvisual,&color);
255    color.r = 0xFFFF; color.g = 0x0000; color.b = 0x0000; GGIred     = ggiMapColor(GGIvisual,&color);
256    color.r = 0x0000; color.g = 0xFFFF; color.b = 0x0000; GGIgreen   = ggiMapColor(GGIvisual,&color);
257    color.r = 0x0000; color.g = 0x0000; color.b = 0xFFFF; GGIblue    = ggiMapColor(GGIvisual,&color);
258    color.r = 0x0000; color.g = 0xFFFF; color.b = 0xFFFF; GGIcyan    = ggiMapColor(GGIvisual,&color);
259    color.r = 0xFFFF; color.g = 0x0000; color.b = 0xFFFF; GGImagenta = ggiMapColor(GGIvisual,&color);
260    color.r = 0xFFFF; color.g = 0xFFFF; color.b = 0x0000; GGIyellow  = ggiMapColor(GGIvisual,&color);
261    color.r = 0x8888; color.g = 0x8888; color.b = 0x8888; GGIgray    = ggiMapColor(GGIvisual,&color);
262
263    GGIborderColor = GGIwhite;
264    GGIbackground  = GGIblack;
265    GGIaxisColor   = GGIgray;
266    GGIcolors[0]   = GGIred;
267    GGIcolors[1]   = GGIgreen;
268    GGIcolors[2]   = GGIblue;
269    GGIcolors[3]   = GGImagenta;
270    GGIcolors[4]   = GGIcyan;
271    GGIcolors[5]   = GGIyellow;
272    GGIcolors[6]   = GGIgray;
273}
274
275/* Called before a graphic is displayed */
276TERM_PUBLIC void GGI_graphics()
277{
278#ifdef USE_MOUSE
279    int i, j;
280    int display_frame = ggiGetDisplayFrame(GGIvisual);
281#endif
282#if 0
283    if(!Xenv)
284    {
285	GGI_line_colors();
286	return;
287    }
288#endif
289    ggiSetGCForeground(GGIvisual,GGIblack);
290#ifdef USE_MOUSE
291    /* write to the currently not displayed buffer */
292    ggiSetWriteFrame(GGIvisual, !display_frame);
293
294    /* mark the contents of the alternate frame as invalid */
295    GGI_saved_canvas = 0;
296    GGI_saved_stl = 0;
297    GGI_needs_update = 1;
298
299    /* reset the stl vertex */
300    GGI_stl_vertex.width = 0;
301
302    /* reset the zoom box coordinates */
303    for (i = 0; i < 2; i++) {
304	for (j = 0; j < 2; j++) {
305	    GGI_zoom[i][j].width = 0;
306	}
307	GGI_zoombox[i].x = -1;
308	GGI_zoombox[i].y = -1;
309    }
310#endif /* USE_MOUSE */
311
312    /* clear *write* buffer. */
313    ggiDrawBox(GGIvisual, 0, 0, GGIwidth, GGIheight);
314
315#ifdef USE_MOUSE
316    if (GGI_use_mouse) {
317	/* copy the contents of the currently
318	 * displayed stl to the write frame.
319	 * This way the stl won't jitter. */
320	ggiSetReadFrame(GGIvisual, display_frame);
321	ggiCopyBox(GGIvisual, 0, GGIcanvas_height,
322	    GGIwidth, GGI_font_height, 0, GGIcanvas_height);
323    }
324#endif
325}
326
327TERM_PUBLIC void
328GGI_set_size()
329{
330    ggi_mode mode;
331    ggiGetMode(GGIvisual,&mode);
332
333    GGIwidth  = mode.virt.x;
334    GGIheight = mode.virt.y;
335
336    term->xmax = mode.virt.x - 1;
337
338#ifdef USE_MOUSE
339    GGIcanvas_height = mode.virt.y - (GGI_use_mouse ? GGI_font_height : 0);
340    term->ymax = GGIcanvas_height - 1;
341#else
342    term->ymax = mode.virt.y - 1;
343#endif
344    GGIymax = term->ymax;
345}
346
347/*
348 * init
349 * -----------------------
350 * Called only once, when the terminal is initialized. We have to open the visual here because it
351 * is during 'init' that we have to change the terminal dimensions (xmax, ymax).
352 */
353TERM_PUBLIC void GGI_init()
354{
355    int success = 0;
356    ggi_mode mode;
357
358#if 0
359    if (0 != giiInit()) {
360	ggiPanic("*** giiInit() failed *** \n");
361    }
362#endif
363
364    if (0 != ggiInit()) {
365	ggiPanic("*** ggiInit() failed *** \n");
366    }
367
368    if (NULL == (GGIvisual = ggiOpen(NULL))) {
369	/* TODO: abort a bit more gracefully */
370	ggiPanic("(GGI_init() unable to open default\n");
371    }
372
373    if (strlen(GGI_mode_spec)) {
374	/* user specified mode */
375	if (!ggiParseMode(GGI_mode_spec, &mode)) {
376	    mode.frames = GGI_frames;
377	    if (!ggiSetMode(GGIvisual, &mode)) {
378		success = 1;
379	    }
380	}
381    }
382
383    if (!success) {
384	/* try the default mode */
385	if(ggiSetSimpleMode(GGIvisual,GGI_AUTO,GGI_AUTO,GGI_frames,GT_AUTO)) {
386	    ggiPanic("(GGI_init() unable to set default mode\n");
387	    GGIvisual = (ggi_visual_t)0;
388	}
389    }
390
391    ggiGetMode(GGIvisual, &mode);
392
393    /* print the mode only once if it has changed */
394    if (GGI_mode_changed) {
395	GGI_mode_changed = 0;
396	ggiFPrintMode(stderr, &mode);
397	fprintf(stderr, "\n");
398    }
399
400
401#ifdef USE_MOUSE
402    /* must come before GGI_set_size() */
403    ggiGetCharSize(GGIvisual, &GGI_font_width, &GGI_font_height);
404#endif
405
406    GGI_set_size();
407
408#ifdef USE_MOUSE
409    ggiSetReadFrame(GGIvisual, 0);
410    ggiSetWriteFrame(GGIvisual, 0);
411    ggiSetDisplayFrame(GGIvisual, 0);
412#endif
413
414#ifdef HAVE_GGI_WMH_H
415    /* Initialize WMH extension */
416    if (ggiWmhInit() != 0 || ggiWmhAttach(GGIvisual) < 0) {
417	GGI_use_whm = 0;
418    } else {
419	GGI_use_whm = 1;
420	ggiWmhAllowResize(GGIvisual, 100, 50, 2000, 2000, 10, 10);
421	ggiWmhSetTitle(GGIvisual, "GGI Gnuplot Driver");
422	ggiWmhSetIconTitle(GGIvisual, "Gnuplot");
423    }
424#endif
425
426    /*
427     * if(!(Xenv=!ggiWmhAttach(GGIvisual))) ggiWmhDetach(GGIvisual);
428     * else Xenv=!ggiWmhSetTitle(GGIvisual,"GGI Gnuplot Driver");
429     */
430#if 0
431    if(!Xenv)
432    {
433	/*
434	 * ggiWmhDetach(GGIvisual);
435	 * ggiWmhExit();
436	 */
437	ggiClose(GGIvisual);
438	ggiExit();
439	GGIvisual=NULL;
440    }
441#endif
442    GGI_line_colors();
443    ggiSetFlags(GGIvisual, GGIFLAG_ASYNC);
444#ifdef USE_MOUSE
445    GGI_mouse_x = 0;
446    GGI_mouse_y = 0;
447    GGI_modifiers = 0;
448    {
449	struct timeval tv;
450	struct timezone tz;
451	gettimeofday(&tv, &tz);
452	GGI_SetTime(&tv); /* initialize time */
453    }
454#endif
455
456#ifdef ENABLE_XMI
457    if (0 != xmiInit()) {
458	/* TODO: abort a bit more gracefully */
459	ggiPanic("(GGI_init() unable to initialize xmi\n");
460    }
461
462    if (xmiAttach(GGIvisual) < 0) {
463	ggiPanic("(GGI_init) Unable to attach XMI extension to visual\n");
464    }
465
466    /* miPaintedSet */
467    if (GGI_miPaintedSet) {
468	miDeletePaintedSet(GGIvisual, GGI_miPaintedSet);
469	GGI_miPaintedSet = (miPaintedSet*)0;
470    }
471    GGI_miPaintedSet = miNewPaintedSet(GGIvisual);
472    miClearPaintedSet(GGIvisual, GGI_miPaintedSet);
473
474    /* miGC */
475    if (GGI_miGC) {
476	miDeleteGC(GGIvisual, GGI_miGC);
477	GGI_miGC = (miGC*)0;
478    }
479    GGI_miGC = miNewGC(GGIvisual, 2,
480	GGI_miPixels, GGI_numblendstages, GGI_blendstages);
481#endif
482    setvbuf(stdin, (char*)0, _IONBF, 0);
483}
484
485TERM_PUBLIC void GGI_linetype(int linetype)
486{
487    if(linetype == LT_BLACK)
488	ggiSetGCForeground(GGIvisual,GGIborderColor);
489    if(linetype == LT_AXIS)
490	ggiSetGCForeground(GGIvisual,GGIaxisColor);
491    if(linetype < 0)
492	return;
493    if(linetype >= 6)
494	linetype%=6;
495    ggiSetGCForeground(GGIvisual,GGIcolors[linetype]);
496}
497
498TERM_PUBLIC void GGI_move(unsigned int x, unsigned int y)
499{
500    GGIx=x;
501    GGIy=GGI_y(y);
502}
503
504TERM_PUBLIC void
505GGI_options()
506{
507    while (!END_OF_COMMAND) {
508	switch(lookup_table(&GGI_opts[0], c_token)) {
509	case GGI_ACCELERATION:
510	{
511	    int itmp;
512	    struct value a;
513
514	    c_token++;
515	    itmp = (int) real(const_express(&a));
516	    if (itmp < 1) {
517		fprintf(stderr, "acceleration must be strictly positive!\n");
518	    } else {
519		GGI_acceleration = itmp;
520	    }
521	    break;
522	}
523	case GGI_MODE:
524	    c_token++;
525	    /* fallthru */
526	default:
527	    if (!END_OF_COMMAND) {
528		copy_str(GGI_mode_spec, c_token, 0xfe);
529		GGI_mode_changed = 1;
530	    }
531	    break;
532	}
533	c_token++;
534    } /* while(command) */
535
536    if (*GGI_mode_spec) {
537	sprintf(term_options, "mode %s acceleration %d",
538		GGI_mode_spec, GGI_acceleration);
539    } else {
540	sprintf(term_options, "acceleration %d", GGI_acceleration);
541    }
542}
543
544TERM_PUBLIC void
545GGI_close()
546{
547    ggiFlush(GGIvisual);
548    /* DETACH EXTENSIONS */
549#if HAVE_WMH_H
550    if(GGI_use_whm) {
551	ggiWmhDetach(GGIvisual);
552    }
553#endif
554#ifdef ENABLE_XMI
555    xmiDetach(GGIvisual);
556#endif
557
558    ggiClose(GGIvisual);
559    GGIvisual = (ggi_visual_t)0;
560
561    /* EXIT EXTENSIONS */
562#if HAVE_WMH_H
563    if(GGI_use_whm) {
564	ggiWmhExit();
565	GGI_use_whm = 0;
566    }
567#endif
568#ifdef ENABLE_XMI
569    xmiExit();
570#endif
571}
572
573/* Called when terminal is terminated i.e.
574 * when switching to another terminal. */
575TERM_PUBLIC void
576GGI_reset()
577{
578    if(GGIvisual!=NULL) {
579	GGI_close();
580    }
581# if 0 /* not needed */
582    GGI_save_pal.colorFormulae = -1; /* force later reallocation of palette */
583# endif
584}
585
586TERM_PUBLIC void
587GGI_put_text(unsigned int x, unsigned int y, const char *str)
588{
589    ggi_pixel current_foreground;
590    ggiGetGCForeground(GGIvisual,&current_foreground);
591    ggiSetGCForeground(GGIvisual,GGIborderColor);
592    ggiPuts(GGIvisual,x,GGI_y(y) - 4 /* ? (joze ? */,str);
593    ggiSetGCForeground(GGIvisual,current_foreground);
594}
595
596TERM_PUBLIC void
597GGI_suspend()
598{
599    /* this fails on the console */
600    GGI_text();
601}
602
603TERM_PUBLIC void
604GGI_resume()
605{
606    /* do nothing */
607}
608
609TERM_PUBLIC void
610GGI_fillbox(
611    int style,
612    unsigned int x, unsigned int y,
613    unsigned int w, unsigned int h)
614{
615    ggiDrawBox(GGIvisual, x, GGI_y((int)(y+h)), w, h);
616}
617
618TERM_PUBLIC void
619GGI_text()
620{
621    ggiFlush(GGIvisual);
622#ifdef USE_MOUSE
623    /* now display the buffer which was just written */
624    ggiSetDisplayFrame(GGIvisual, ggiGetWriteFrame(GGIvisual));
625    return;
626#else
627    /* Wait for a key to be pressed and exit graphics mode if
628     * running in console mode. */
629    /* TODO: return immediately, if in X */
630    ggiGetc(GGIvisual);
631    GGI_close();
632#endif
633}
634
635TERM_PUBLIC void
636GGI_vector(unsigned int x, unsigned int y)
637{
638    y = GGI_y(y);
639    ggiDrawLine(GGIvisual,GGIx,GGIy,x,y);
640    GGIx=x;
641    GGIy=y;
642}
643
644#ifdef USE_MOUSE
645
646/* translate ggi keysym to gnuplot keysym */
647TERM_PUBLIC int
648GGI_from_keysym(uint32_t keysym)
649{
650    switch (keysym) {
651	case GIIUC_BackSpace:
652	    return GP_BackSpace;
653	case GIIUC_Tab:
654	    return GP_Tab;
655	case GIIUC_Linefeed:
656	    return GP_Linefeed;
657	case GIIK_Clear:
658	    return GP_Clear;
659	case GIIUC_Return:
660	    return GP_Return;
661	case GIIK_Pause:
662	    return GP_Pause;
663	case GIIK_ScrollLock:
664	    return GP_Scroll_Lock;
665	case GIIK_SysRq:
666	    return GP_Sys_Req;
667	case GIIUC_Escape:
668	    return GP_Escape;
669	case GIIK_Insert:
670	    return GP_Insert;
671	case GIIUC_Delete:
672	    return GP_Delete;
673	case GIIK_Home:
674	    return GP_Home;
675	case GIIK_Left:
676	    return GP_Left;
677	case GIIK_Up:
678	    return GP_Up;
679	case GIIK_Right:
680	    return GP_Right;
681	case GIIK_Down:
682	    return GP_Down;
683	case GIIK_PageUp:
684	    return GP_PageUp;
685	case GIIK_PageDown:
686	    return GP_PageDown;
687	case GIIK_End:
688	    return GP_End;
689	case GIIK_Begin:
690	    return GP_Begin;
691	case GIIK_PSpace:
692	    return GP_KP_Space;
693	case GIIK_PTab:
694	    return GP_KP_Tab;
695	case GIIK_PEnter:
696	    return GP_KP_Enter;
697
698	case GIIK_PF1:
699	    return GP_KP_F1;
700	case GIIK_PF2:
701	    return GP_KP_F2;
702	case GIIK_PF3:
703	    return GP_KP_F3;
704	case GIIK_PF4:
705	    return GP_KP_F4;
706
707#if 0
708	case 1:
709	    return GP_KP_Insert;    /* ~ KP_0 */
710	case 1:
711	    return GP_KP_End;       /* ~ KP_1 */
712	case 1:
713	    return GP_KP_Down;      /* ~ KP_2 */
714	case 1:
715	    return GP_KP_Page_Down; /* ~ KP_3 */
716	case 1:
717	    return GP_KP_Left;      /* ~ KP_4 */
718	case 1:
719	    return GP_KP_Begin;     /* ~ KP_5 */
720	case 1:
721	    return GP_KP_Right;     /* ~ KP_6 */
722	case 1:
723	    return GP_KP_Home;      /* ~ KP_7 */
724	case 1:
725	    return GP_KP_Up;        /* ~ KP_8 */
726	case 1:
727	    return GP_KP_Page_Up;   /* ~ KP_9 */
728#endif
729
730#if 0
731	case GIIK_PDelete:
732	    return GP_KP_Delete;
733#endif
734	case GIIK_PEqual:
735	    return GP_KP_Equal;
736	case GIIK_PAsterisk:
737	    return GP_KP_Multiply;
738	case GIIK_PPlus:
739	    return GP_KP_Add;
740	case GIIK_PSeparator:
741	    return GP_KP_Separator;
742	case GIIK_PMinus:
743	    return GP_KP_Subtract;
744	case GIIK_PDecimal:
745	    return GP_KP_Decimal;
746	case GIIK_PSlash:
747	    return GP_KP_Divide;
748
749	case GIIK_P0:
750	    return GP_KP_0;
751	case GIIK_P1:
752	    return GP_KP_1;
753	case GIIK_P2:
754	    return GP_KP_2;
755	case GIIK_P3:
756	    return GP_KP_3;
757	case GIIK_P4:
758	    return GP_KP_4;
759	case GIIK_P5:
760	    return GP_KP_5;
761	case GIIK_P6:
762	    return GP_KP_6;
763	case GIIK_P7:
764	    return GP_KP_7;
765	case GIIK_P8:
766	    return GP_KP_8;
767	case GIIK_P9:
768	    return GP_KP_9;
769
770	case GIIK_F1:
771	    return GP_F1;
772	case GIIK_F2:
773	    return GP_F2;
774	case GIIK_F3:
775	    return GP_F3;
776	case GIIK_F4:
777	    return GP_F4;
778	case GIIK_F5:
779	    return GP_F5;
780	case GIIK_F6:
781	    return GP_F6;
782	case GIIK_F7:
783	    return GP_F7;
784	case GIIK_F8:
785	    return GP_F8;
786	case GIIK_F9:
787	    return GP_F9;
788	case GIIK_F10:
789	    return GP_F10;
790	case GIIK_F11:
791	    return GP_F11;
792	case GIIK_F12:
793	    return GP_F12;
794
795	default:
796	    /* return it untranslated */
797	    return keysym;
798    }
799}
800
801TERM_PUBLIC long int
802GGI_SetTime(const struct timeval* current)
803{
804    /* --> dsec in musec */
805    int dsec = (current->tv_sec - GGI_timestamp.tv_sec) * 1000000;
806    /* --> dmu in millisec */
807    int dmu = (current->tv_usec - GGI_timestamp.tv_usec + dsec) / 1000;
808
809    GGI_timestamp = *current;
810    return dmu;
811}
812
813TERM_PUBLIC int
814GGI_from_button(uint32_t button)
815{
816    switch (button) {
817    case GII_PBUTTON_LEFT:
818	return 1;
819    case GII_PBUTTON_MIDDLE:
820	return 2;
821    case GII_PBUTTON_RIGHT:
822	return 3;
823    default:
824	/* should not happen */
825	return 0;
826    }
827}
828
829TERM_PUBLIC int
830GGI_y(int32_t y)
831{
832    return GGIymax - y;
833}
834
835TERM_PUBLIC int
836GGI_eventually_update_modifiers(const ggi_event* event, const int add)
837{
838    int mod = 0;
839    int old_modifiers = GGI_modifiers;
840
841    switch (event->key.sym) {
842    case GIIK_Shift:
843	mod = Mod_Shift;
844	break;
845    case GIIK_Ctrl:
846	mod = Mod_Ctrl;
847	break;
848    case GIIK_Alt:
849    case GIIK_Meta:
850	mod = Mod_Alt;
851	break;
852    default:
853	return 0;
854    }
855
856    if (add) {
857	GGI_modifiers |= mod;
858    } else {
859	GGI_modifiers &= ~mod;
860    }
861
862    if (GGI_modifiers != old_modifiers) {
863
864	struct gp_event_t gp_ev;
865
866	gp_ev.type = GE_modifier;
867	gp_ev.mx   = GGI_mouse_x;
868	gp_ev.my   = GGI_y(GGI_mouse_y);
869	gp_ev.par1 = 0;
870	gp_ev.par2 = 0;
871	gp_ev.par1 = GGI_modifiers;
872
873	do_event(&gp_ev);
874    }
875
876    return 1;
877}
878
879TERM_PUBLIC int
880GGI_dispatch_event(const ggi_event* event)
881{
882    struct gp_event_t gp_ev;
883
884    gp_ev.type = 0;
885    gp_ev.mx   = GGI_mouse_x;
886    gp_ev.my   = GGI_y(GGI_mouse_y);
887    gp_ev.par1 = 0;
888    gp_ev.par2 = 0;
889
890    switch (event->any.type) {
891
892	/* [-- KEY EVENTS --] */
893    case evKeyPress:
894    case evKeyRepeat:
895	if (GGI_eventually_update_modifiers(event, 1)) {
896	    /* was just a modifier pressed */
897	    return 0;
898	}
899	gp_ev.type = GE_keypress;
900	gp_ev.par1 = GGI_from_keysym(event->key.sym);
901	if ('q' == gp_ev.par1) {
902	    return 'q';
903	}
904	break;
905    case evKeyRelease:
906	if (GGI_eventually_update_modifiers(event, 0)) {
907	    /* was just a modifier pressed */
908	    return 0;
909	}
910	break;
911
912	/* [-- POINTER EVENTS --] */
913    case evPtrRelative:
914	/* relative motion is not implemented. Should it ? */
915	/*
916	 * fprintf(stderr, "%s:%d report this to <johannes@zellner.org> %d %d\n",
917	 *     __FILE__, __LINE__, event->pmove.x, event->pmove.y);
918	 */
919	gp_ev.type  = GE_motion;
920	GGI_mouse_x += GGI_acceleration * event->pmove.x;
921	GGI_mouse_y += GGI_acceleration * event->pmove.y;
922	break;
923    case evPtrAbsolute:
924	gp_ev.type  = GE_motion;
925	GGI_mouse_x = event->pmove.x;
926	GGI_mouse_y = event->pmove.y;
927	break;
928    case evPtrButtonPress:
929	gp_ev.type = GE_buttonpress;
930	gp_ev.par1 = GGI_from_button(event->pbutton.button);
931	break;
932    case evPtrButtonRelease:
933	gp_ev.type = GE_buttonrelease;
934	gp_ev.par1 = GGI_from_button(event->pbutton.button);
935	gp_ev.par2 = GGI_SetTime(&(event->pbutton.time));
936	break;
937#ifdef HAVE_GGI_WMH_H
938    case evCommand:
939	/* [-- resizing --] */
940	if (GGI_use_whm) {
941	    /* fprintf(stderr, "(GGI_dispatch_event) \n"); */
942	    if (event->cmd.code==GGICMD_REQUEST_SWITCH) {
943		/*
944		 * ggi_cmddata_switchrequest *req;
945		 * req = &(event->cmd.data);
946		 * ggi_resize(GGIvisual, &(req->mode));
947		 */
948		/*
949		 * while( ggiEventPoll(GGIvisual, emAll, &tv) ) {
950		 *     ggiEventRead(GGIvisual, event, emAll);
951		 * }
952		 */
953	    }
954	}
955	break;
956#endif
957    default:
958	/* fprintf(stderr, "(GGI_dispatch_event) unhandled event\n"); */
959	break;
960    }
961    do_event(&gp_ev);
962    gp_ev.type = GE_plotdone;
963    do_event(&gp_ev);
964    return 0;
965}
966
967/* save currently displayed frame to alternate buffer */
968TERM_PUBLIC void
969GGI_save_frame_canvas()
970{
971    if (!GGI_saved_canvas && GGIvisual) {
972	int display_frame = ggiGetDisplayFrame(GGIvisual);
973
974	/* save the currently displayed frame to alternate frame */
975	ggiSetReadFrame(GGIvisual, display_frame);
976	ggiSetWriteFrame(GGIvisual, !display_frame);
977	ggiCopyBox(GGIvisual, 0, 0, GGIwidth, GGIcanvas_height, 0, 0);
978
979	/* write again directly to the display frame */
980	ggiSetWriteFrame(GGIvisual, display_frame);
981
982	/* remember that the alternate frame is valid */
983	GGI_saved_canvas = 1;
984    }
985}
986
987TERM_PUBLIC void
988GGI_save_frame_stl()
989{
990    if (!GGI_saved_stl) {
991	int display_frame = ggiGetDisplayFrame(GGIvisual);
992
993	/* clear the stl part of the alternate buffer */
994	ggiSetGCForeground(GGIvisual, GGIblack);
995	ggiSetWriteFrame(GGIvisual, !display_frame);
996	ggiDrawBox(GGIvisual, 0, GGIcanvas_height, GGIwidth, GGI_font_height);
997	ggiSetWriteFrame(GGIvisual, display_frame);
998
999	/* clear the currently displayed area, which is left
1000	 * from a previous plot (see above, where the stl of
1001	 * the previous plot is copied to the current frame) */
1002	ggiSetReadFrame(GGIvisual, !display_frame);
1003	ggiCopyBox(GGIvisual, 0, GGIcanvas_height,
1004		   GGIwidth, GGI_font_height, 0, GGIcanvas_height);
1005
1006	GGI_saved_stl = 1;
1007    }
1008}
1009
1010TERM_PUBLIC void
1011GGI_replot()
1012{
1013    struct gp_event_t ev = {
1014	GE_replot,
1015	0, 0, 0, 0
1016    };
1017
1018    do_event(&ev);
1019}
1020
1021TERM_PUBLIC void
1022GGI_clear(const GGI_vertex_t* v, const int tag)
1023{
1024    if (tag && v->width) {
1025	/* turn off current */
1026	ggiSetReadFrame(GGIvisual, !ggiGetDisplayFrame(GGIvisual));
1027	ggiCopyBox(GGIvisual, v->x, v->y, v->width, v->height, v->x, v->y);
1028    }
1029}
1030
1031TERM_PUBLIC void
1032GGI_save_puts(GGI_vertex_t* v, const int tag)
1033{
1034    GGI_clear(v, tag);
1035
1036    if (v->width) {
1037
1038	/* draw the text in the axis color (gray) */
1039	ggiSetGCForeground(GGIvisual, GGIaxisColor);
1040
1041	/* write the string directly to the display */
1042	ggiPuts(GGIvisual, v->x, v->y, v->str);
1043
1044    }
1045}
1046
1047TERM_PUBLIC void
1048GGI_set_vertex(
1049    GGI_vertex_t* v,
1050    const int x,
1051    const int y,
1052    const char* str,
1053    const int tag)
1054{
1055    GGI_clear(v, tag);
1056
1057    v->x = x;
1058    v->y = y;
1059    v->height = GGI_font_height;
1060
1061    if (str && *str) {
1062	v->width = strlen(str) * GGI_font_width;
1063	strcpy(v->str, str);
1064    } else {
1065	/* turn string off */
1066	v->width = 0;
1067	*(v->str) = '\0';
1068    }
1069}
1070
1071TERM_PUBLIC void
1072GGI_relative(int r[2])
1073{
1074    int diff = r[1] - r[0];
1075    if (diff < 0) {
1076	r[0] = r[1];
1077	r[1] = -diff;
1078    } else {
1079	r[1] = diff;
1080    }
1081}
1082
1083TERM_PUBLIC void
1084GGI_clear_hline(int x1, int x2, int y)
1085{
1086    if (GGI_saved_canvas && x1 >= 0 && x2 >= 0 && y >= 0) {
1087	int r[2];
1088	ggiSetReadFrame(GGIvisual, !ggiGetDisplayFrame(GGIvisual));
1089
1090	r[0] = x1;
1091	r[1] = x2;
1092	GGI_relative(r);
1093	/* horizontal line */
1094	ggiCopyBox(GGIvisual, r[0], y, r[1], 1, r[0], y);
1095    }
1096}
1097
1098TERM_PUBLIC void
1099GGI_clear_vline(int y1, int y2, int x)
1100{
1101    if (GGI_saved_canvas && y1 >= 0 && y2 >= 0 && x >= 0) {
1102	int r[2];
1103	ggiSetReadFrame(GGIvisual, !ggiGetDisplayFrame(GGIvisual));
1104
1105	r[0] = y1;
1106	r[1] = y2;
1107	GGI_relative(r);
1108	/* vertical line */
1109	ggiCopyBox(GGIvisual, x, r[0], 1, r[1], x, r[0]);
1110    }
1111}
1112
1113TERM_PUBLIC void
1114GGI_draw_hline(int x1, int x2, int y)
1115{
1116    if (x1 >= 0 && x2 >= 0 && y >= 0) {
1117	int r[2];
1118
1119	r[0] = x1;
1120	r[1] = x2;
1121	GGI_relative(r);
1122	/* horizontal line */
1123	ggiDrawHLine(GGIvisual, r[0], y, r[1]);
1124    }
1125}
1126
1127TERM_PUBLIC void
1128GGI_draw_vline(int y1, int y2, int x)
1129{
1130    if (y1 >= 0 && y2 >= 0 && x >= 0) {
1131	int r[2];
1132
1133	r[0] = y1;
1134	r[1] = y2;
1135	GGI_relative(r);
1136	/* vertical line */
1137	ggiDrawVLine(GGIvisual, x, r[0], r[1]);
1138    }
1139}
1140
1141TERM_PUBLIC void
1142GGI_draw_ruler()
1143{
1144    if (GGI_ruler.x >= 0 && GGI_ruler.y >= 0) {
1145	ggi_pixel current_foreground;
1146
1147	GGI_save_frame_canvas();
1148
1149	/* TODO: we could choose a nicer color here */
1150	ggiGetGCForeground(GGIvisual, &current_foreground);
1151	ggiSetGCForeground(GGIvisual, GGIaxisColor);
1152
1153	ggiDrawHLine(GGIvisual, 0, GGI_ruler.y, GGIwidth);
1154	ggiDrawVLine(GGIvisual, GGI_ruler.x, 0, GGIcanvas_height);
1155
1156	/* restore old foreground color */
1157	/* XXX need this ? */
1158	ggiSetGCForeground(GGIvisual, current_foreground);
1159    }
1160}
1161
1162TERM_PUBLIC void
1163GGI_clear_zoombox()
1164{
1165    GGI_clear_hline(GGI_zoombox[0].x, GGI_zoombox[1].x, GGI_zoombox[0].y);
1166    GGI_clear_hline(GGI_zoombox[0].x, GGI_zoombox[1].x, GGI_zoombox[1].y);
1167    GGI_clear_vline(GGI_zoombox[0].y, GGI_zoombox[1].y, GGI_zoombox[0].x);
1168    GGI_clear_vline(GGI_zoombox[0].y, GGI_zoombox[1].y, GGI_zoombox[1].x);
1169}
1170
1171TERM_PUBLIC void
1172GGI_draw_zoombox()
1173{
1174    if (GGI_zoombox[0].x >= 0 && GGI_zoombox[0].y >= 0
1175	&& GGI_zoombox[0].x >= 0 && GGI_zoombox[0].y >= 0) {
1176	ggi_pixel current_foreground;
1177
1178	GGI_save_frame_canvas();
1179
1180	/* TODO: we could choose a nicer color here */
1181	ggiGetGCForeground(GGIvisual, &current_foreground);
1182	ggiSetGCForeground(GGIvisual, GGIaxisColor);
1183
1184	GGI_draw_hline(GGI_zoombox[0].x, GGI_zoombox[1].x, GGI_zoombox[0].y);
1185	GGI_draw_hline(GGI_zoombox[0].x, GGI_zoombox[1].x, GGI_zoombox[1].y);
1186	GGI_draw_vline(GGI_zoombox[0].y, GGI_zoombox[1].y, GGI_zoombox[0].x);
1187	GGI_draw_vline(GGI_zoombox[0].y, GGI_zoombox[1].y, GGI_zoombox[1].x);
1188
1189	/* restore old foreground color */
1190	/* XXX need this ? */
1191	ggiSetGCForeground(GGIvisual, current_foreground);
1192    }
1193}
1194
1195TERM_PUBLIC void
1196GGI_abort_zooming()
1197{
1198    /* empty string: finish zooming */
1199    int i, j;
1200    GGI_clear_zoombox();
1201    for (i = 0; i < 2; i++) {
1202	for (j = 0; j < 2; j++) {
1203	    GGI_set_vertex(&(GGI_zoom[i][j]), 0, 0, (char*)0, GGI_saved_canvas);
1204	}
1205	GGI_zoombox[i].x = -1;
1206    }
1207}
1208
1209TERM_PUBLIC int
1210GGI_waitforinput(int options)
1211{
1212    char c;
1213
1214    /* XXX:  if the input device it not a tty (e.g. /dev/null)
1215     *       mouse events are not processed. This is necessary
1216     *       as on some systems /dev/null is not selectable.
1217     */
1218    /* FIXME: Not implemented yet */
1219    if (options == TERM_ONLY_CHECK_MOUSING) return '\0';
1220
1221    if (GGIvisual) {
1222	fd_set fds;
1223	int fd = fileno(stdin);
1224	int i, j;
1225	do {
1226	    ggi_event_mask mask = emAll; /* TODO: choose a more selective mask */
1227	    ggiSetEventMask(GGIvisual, mask);
1228
1229	    FD_ZERO(&fds);
1230	    FD_SET(fd, &fds); /* listen to stdin */
1231
1232	    if (GGI_needs_update) {
1233		/* draw the ruler below the other items */
1234		GGI_draw_ruler();
1235
1236		/* update the zoombox */
1237		GGI_draw_zoombox();
1238		for (i = 0; i < 2; i++) {
1239		    for (j = 0; j < 2; j++) {
1240			GGI_save_puts(&(GGI_zoom[i][j]), GGI_saved_canvas);
1241		    }
1242		}
1243
1244		/* update the status line */
1245		GGI_save_puts(&GGI_stl_vertex, GGI_saved_stl);
1246
1247		ggiFlush(GGIvisual);
1248
1249		GGI_needs_update = 0;
1250	    }
1251
1252	    ggiEventSelect(GGIvisual, &mask, fd + 1,
1253		SELECT_TYPE_ARG234 &fds, 0, 0, (struct timeval*)0);
1254
1255	    if (mask) {
1256		ggi_event event;
1257		/* mask pointer motions and key repeat events,
1258		 * to they don't pile up */
1259		ggiEventRead(GGIvisual, &event, mask);
1260		ggiRemoveEventMask(GGIvisual, emPtrMove | emKeyRepeat);
1261		if ('q' == GGI_dispatch_event(&event)) {
1262		    term_reset();
1263		    break;
1264		} else {
1265		    ggiAddEventMask(GGIvisual, emPtrMove | emKeyRepeat);
1266		}
1267	    }
1268
1269	} while (!FD_ISSET(fd, &fds) && GGIvisual);
1270    }
1271
1272    if (read(0, &c, 1)!=1) return EOF;
1273    else return c;
1274}
1275
1276TERM_PUBLIC void
1277GGI_put_tmptext(int i, const char str[])
1278{
1279    char* second;
1280
1281    if (GGIvisual == NULL)
1282	return;
1283
1284    switch (i) {
1285	case 0: /* statusline text */
1286
1287	    if (!str || !(*str)) {
1288		/* statusline is empty. This is the case,
1289		 * if the mouse was just turned off. */
1290		if (GGI_use_mouse) {
1291		    /* The user just toggled of the mouse. */
1292		    GGI_use_mouse = 0;
1293		    GGI_set_size();
1294		    GGI_replot();
1295		}
1296	    } else {
1297		/* statusline is non-empty */
1298		if (!GGI_use_mouse) {
1299		    /* The mouse was off before and was just turned on. */
1300		    GGI_use_mouse = 1;
1301		    GGI_set_size();
1302		    GGI_replot();
1303		}
1304		GGI_save_frame_stl();
1305		GGI_set_vertex(&GGI_stl_vertex, 0, GGIcanvas_height, str, GGI_saved_stl);
1306	    }
1307	    break;
1308
1309	case 1: /* coordinate text for first  corner of zoombox */
1310	case 2: /* coordinate text for second corner of zoombox */
1311	    GGI_save_frame_canvas();
1312	    second = (char*) strchr(str, '\r');
1313	    --i; /* transform to [0, 1] */
1314	    GGI_clear_zoombox();
1315	    if (second == NULL) {
1316		/* remove box and / or coordinates */
1317		GGI_set_vertex(&(GGI_zoom[i][0]), 0, 0, (char*)0, GGI_saved_canvas);
1318		GGI_set_vertex(&(GGI_zoom[i][1]), 0, 0, (char*)0, GGI_saved_canvas);
1319		break;
1320	    } else {
1321		*second = '\0'; /* XXX this assumes that str is writable XXX */
1322		second++;
1323		GGI_set_vertex(&(GGI_zoom[i][0]), GGI_mouse_x, GGI_mouse_y - GGI_font_height - 1, str, GGI_saved_canvas);
1324		GGI_set_vertex(&(GGI_zoom[i][1]), GGI_mouse_x, GGI_mouse_y + 1, second, GGI_saved_canvas);
1325		GGI_zoombox[i].x = GGI_mouse_x;
1326		GGI_zoombox[i].y = GGI_mouse_y;
1327	    }
1328	    break;
1329    }
1330    GGI_needs_update++;
1331}
1332
1333TERM_PUBLIC void
1334GGI_set_ruler(int x, int y)
1335{
1336    if (x < 0) {
1337
1338	/* turn ruler off */
1339	GGI_clear_hline(0, GGIwidth, GGI_ruler.y);
1340	GGI_clear_vline(0, GGIcanvas_height, GGI_ruler.x);
1341	GGI_ruler.x = -1;
1342	GGI_ruler.y = -1;
1343
1344    } else {
1345	GGI_ruler.x = x;
1346	GGI_ruler.y = GGI_y(y);
1347    }
1348    GGI_needs_update++;
1349}
1350
1351TERM_PUBLIC void
1352GGI_set_cursor(int c, int x, int y)
1353{
1354    /* TODO */
1355    switch (c) {
1356	case 0:
1357	    GGI_abort_zooming();
1358	    break;
1359	case 1:
1360	case 2:
1361	case 3:
1362	default:
1363	    /* XXX not implemented */
1364	    break;
1365    }
1366    GGI_needs_update++;
1367}
1368
1369TERM_PUBLIC void
1370GGI_set_clipboard(const char s[])
1371{
1372    /* XXX: not implemented */
1373    (void) s;			/* avoid -Wunused */
1374}
1375
1376#endif
1377
1378TERM_PUBLIC int
1379GGI_make_palette(t_sm_palette *palette)
1380{
1381    /* reallocate only, if it has changed */
1382    if (palette && (GGI_save_pal.colorFormulae < 0
1383	    || palette->colorFormulae != GGI_save_pal.colorFormulae
1384	    || palette->colorMode != GGI_save_pal.colorMode
1385	    || palette->formulaR != GGI_save_pal.formulaR
1386	    || palette->formulaG != GGI_save_pal.formulaG
1387	    || palette->formulaB != GGI_save_pal.formulaB
1388	    || palette->positive != GGI_save_pal.positive)) {
1389	int i;
1390	ggi_color color;
1391	for (i = 0; i < ggi_pm3d_colors; i++) {
1392	    color.r = (short)floor(palette->color[i].r * 0xffff),
1393	    color.g = (short)floor(palette->color[i].g * 0xffff),
1394	    color.b = (short)floor(palette->color[i].b * 0xffff),
1395	    GGI_smooth_colors[i] = ggiMapColor(GGIvisual, &color);
1396	}
1397	GGI_save_pal = *palette;
1398    } else {
1399	return ggi_pm3d_colors;
1400    }
1401    return 0;
1402}
1403
1404TERM_PUBLIC void
1405GGI_previous_palette()
1406{
1407#if 0
1408#ifdef ENABLE_XMI
1409    fprintf(stderr, "(GGI_previous_palette) \n");
1410    if (GGI_miPaintedSet) {
1411	miPoint offset;
1412	offset.x = 0; offset.y = 0;
1413	miCopyPaintedSetToVisual(GGIvisual, GGI_miGC, GGI_miPaintedSet, offset);
1414	miClearPaintedSet(GGIvisual, GGI_miPaintedSet);
1415    }
1416#endif
1417#endif
1418}
1419
1420TERM_PUBLIC void
1421GGI_set_color(struct t_colorspec *colorspec)
1422{
1423    ggi_color rgb;
1424    double gray = colorspec->value;
1425
1426    if (colorspec->type == TC_RGB) {
1427	rgb.r = ((colorspec->lt >> 16) & 0xff) * (65535./255.);
1428	rgb.g = ((colorspec->lt >> 8) & 0xff) * (65535./255.);
1429	rgb.b = ((colorspec->lt) & 0xff) * (65535./255.);
1430	ggiSetGCForeground(GGIvisual, ggiMapColor(GGIvisual,&rgb));
1431    }
1432
1433    if (colorspec->type != TC_FRAC)
1434	return;
1435
1436    if (GGIvisual) {
1437	int idx = (gray <= 0) ? 0 : (int)(gray * ggi_pm3d_colors);
1438	if (idx >= ggi_pm3d_colors)
1439	    idx = ggi_pm3d_colors - 1;
1440	ggiSetGCForeground(GGIvisual, GGI_smooth_colors[idx]);
1441#ifdef ENABLE_XMI
1442	GGI_miGC->pixels[1] = GGI_smooth_colors[idx];
1443#endif
1444    }
1445}
1446
1447#ifdef ENABLE_XMI
1448TERM_PUBLIC void
1449GGI_filled_polygon(int points, gpiPoint *corners)
1450{
1451    static miPoint offset = {0, 0};
1452    if (GGI_miPaintedSet) {
1453#define MI_POINTS 4
1454	miPoint mi_corners[MI_POINTS];
1455	unsigned int i;
1456	if (MI_POINTS != points) {
1457	    fprintf(stderr, "(GGI_filled_polygon) internal error %s:%d\n", __FILE__, __LINE__);
1458	    return;
1459	}
1460	for (i = 0; i < MI_POINTS; i++) {
1461	    mi_corners[i].x = corners[i].x;
1462	    mi_corners[i].y = GGI_y(corners[i].y);
1463	}
1464	miFillPolygon(GGIvisual, GGI_miPaintedSet, GGI_miGC,
1465	    MI_SHAPE_GENERAL, MI_COORD_MODE_ORIGIN, MI_POINTS, mi_corners);
1466	miCopyPaintedSetToVisual(GGIvisual, GGI_miGC, GGI_miPaintedSet, offset);
1467	miClearPaintedSet(GGIvisual, GGI_miPaintedSet);
1468    }
1469}
1470#endif
1471
1472#endif /* TERM_BODY */
1473
1474#ifdef TERM_TABLE
1475
1476TERM_TABLE_START(ggi_driver)
1477    "ggi", "GGI target",
1478    GGI_XMAX, GGI_YMAX, GGI_VCHAR, GGI_HCHAR, GGI_VTIC, GGI_HTIC,
1479    GGI_options, GGI_init, GGI_reset, GGI_text,
1480    null_scale, GGI_graphics, GGI_move, GGI_vector,
1481    GGI_linetype, GGI_put_text,
1482    0, /* angle */
1483    0, /* justify text */
1484    0, /* point */
1485    0, /* arrow */
1486    0, /* set_font */
1487    0, /* set_pointsize */
1488    TERM_CAN_MULTIPLOT,
1489    GGI_suspend,
1490    GGI_resume,
1491    GGI_fillbox,
1492    0 /* linewidth */
1493#ifdef USE_MOUSE
1494    , GGI_waitforinput, GGI_put_tmptext, GGI_set_ruler, GGI_set_cursor, GGI_set_clipboard
1495#endif
1496    , GGI_make_palette,
1497    GGI_previous_palette,
1498    GGI_set_color,
1499#ifdef ENABLE_XMI
1500    GGI_filled_polygon
1501#else
1502    0 /* GGI_filled_polygon */
1503#endif
1504TERM_TABLE_END(ggi_driver)
1505
1506#endif /* TERM_TABLE */
1507#endif /* TERM_PROTO_ONLY */
1508
1509#ifdef TERM_HELP
1510START_HELP(ggi)
1511"1 ggi",
1512"?commands set terminal ggi",
1513"?set terminal ggi",
1514"?set term ggi",
1515"?terminal ggi",
1516"?term ggi",
1517"?ggi",
1518" Legacy terminal driver for the GGI (General Graphics Interface) project."
1519"",
1520" Syntax:",
1521"    set terminal ggi [acceleration <integer>] [[mode] {mode}]",
1522"",
1523" In X the window cannot be resized using window manager handles, but the",
1524" mode can be given with the mode option, e.g.:",
1525"  - V1024x768",
1526"  - V800x600",
1527"  - V640x480",
1528"  - V320x200",
1529" Please refer to the ggi documentation for other modes. The 'mode' keyword",
1530" is optional. It is recommended to select the target by environment variables",
1531" as explained in the libggi manual page. To get DGA on X, you should for",
1532" example",
1533"    bash> export GGI_DISPLAY=DGA",
1534"    csh>  setenv GGI_DISPLAY DGA",
1535"",
1536" 'acceleration' is only used for targets which report relative pointer",
1537" motion events (e.g. DGA) and is a strictly positive integer multiplication",
1538" factor for the relative distances.  The default for acceleration is 7.",
1539"",
1540" Examples:",
1541"    set term ggi acc 10",
1542"    set term ggi acc 1 mode V1024x768",
1543"    set term ggi V1024x768"
1544END_HELP(ggi)
1545#endif
1546