- Optification is done by auto builder now
[gnuplot] / src / gplt_x11.c
1 #ifndef lint
2 static char *RCSid() { return RCSid("$Id: gplt_x11.c,v 1.167.2.11 2009/04/10 04:37:00 sfeam Exp $"); }
3 #endif
4
5 #define X11_POLYLINE 1
6 #define MOUSE_ALL_WINDOWS 1
7
8 /* GNUPLOT - gplt_x11.c */
9
10 /*[
11  * Copyright 1986 - 1993, 1998, 2004   Thomas Williams, Colin Kelley
12  *
13  * Permission to use, copy, and distribute this software and its
14  * documentation for any purpose with or without fee is hereby granted,
15  * provided that the above copyright notice appear in all copies and
16  * that both that copyright notice and this permission notice appear
17  * in supporting documentation.
18  *
19  * Permission to modify the software is granted, but not the right to
20  * distribute the complete modified source code.  Modifications are to
21  * be distributed as patches to the released version.  Permission to
22  * distribute binaries produced by compiling modified sources is granted,
23  * provided you
24  *   1. distribute the corresponding source modifications from the
25  *    released version in the form of a patch file along with the binaries,
26  *   2. add special version identification to distinguish your version
27  *    in addition to the base release version number,
28  *   3. provide your name and address as the primary contact for the
29  *    support of your modified version, and
30  *   4. retain our contact information in regard to use of the base
31  *    software.
32  * Permission to distribute the released version of the source code along
33  * with corresponding source modifications in the form of a patch file is
34  * granted with same provisions 2 through 4 for binary distributions.
35  *
36  * This software is provided "as is" without express or implied warranty
37  * to the extent permitted by applicable law.
38 ]*/
39
40
41 /* lph changes:
42  * (a) make EXPORT_SELECTION the default and specify NOEXPORT to undefine
43  * (b) append X11 terminal number to resource name
44  * (c) change cursor for active terminal
45  */
46
47 /*-----------------------------------------------------------------------------
48  *   gnuplot_x11 - X11 outboard terminal driver for gnuplot 3.3
49  *
50  *   Requires installation of companion inboard x11 driver in gnuplot/term.c
51  *
52  *   Acknowledgements:
53  *      Chris Peterson (MIT)
54  *      Dana Chee (Bellcore)
55  *      Arthur Smith (Cornell)
56  *      Hendri Hondorp (University of Twente, The Netherlands)
57  *      Bill Kucharski (Solbourne)
58  *      Charlie Kline (University of Illinois)
59  *      Yehavi Bourvine (Hebrew University of Jerusalem, Israel)
60  *      Russell Lang (Monash University, Australia)
61  *      O'Reilly & Associates: X Window System - Volumes 1 & 2
62  *
63  *   This code is provided as is and with no warranties of any kind.
64  *
65  * drd: change to allow multiple windows to be maintained independently
66  *
67  *---------------------------------------------------------------------------*/
68
69 /* drd : export the graph via ICCCM primary selection. well... not quite
70  * ICCCM since we dont support full list of targets, but this
71  * is a start.  define EXPORT_SELECTION if you want this feature
72  */
73
74 /*lph: add a "feature" to undefine EXPORT_SELECTION
75    The following makes EXPORT_SELECTION the default and
76    defining NOEXPORT over-rides the default
77  */
78
79 /* Petr Mikulik and Johannes Zellner: added mouse support (October 1999)
80  * Implementation and functionality is based on os2/gclient.c; see mousing.c
81  * Pieter-Tjerk de Boer <ptdeboer@cs.utwente.nl>: merged two versions
82  * of mouse patches. (November 1999) (See also mouse.[ch]).
83  */
84
85 /* X11 support for Petr Mikulik's pm3d
86  * by Johannes Zellner <johannes@zellner.org>
87  * (November 1999 - January 2000, Oct. 2000)
88  */
89
90 /* Polyline support May 2003
91  * Ethan Merritt <merritt@u.washington.edu>
92  */
93
94 /* Dynamically created windows July 2003
95  * Across-pipe title text and close command October 2003
96  * Dan Sebald <daniel.sebald@ieee.org>
97  */
98
99 /* Daniel Sebald: added X11 support for images. (27 February 2003)
100  */
101
102 /* Shigeharu Takeno <shige@iee.niit.ac.jp> February 2005
103  * Support for multi-byte fonts based, with permission, on the "gnuplot+"
104  * patches by Masahito Yamaga <ma@yama-ga.com>
105  */
106
107 #include "syscfg.h"
108 #include "stdfn.h"
109 #include "gp_types.h"
110 #include "term_api.h"
111 #include "gplt_x11.h"
112
113 #ifdef EXPORT_SELECTION
114 # undef EXPORT_SELECTION
115 #endif /* EXPORT SELECTION */
116 #ifndef NOEXPORT
117 # define EXPORT_SELECTION XA_PRIMARY
118 #endif /* NOEXPORT */
119
120
121 #if !(defined(VMS) || defined(CRIPPLED_SELECT))
122 # define DEFAULT_X11
123 #endif
124
125 #if defined(VMS) && defined(CRIPPLED_SELECT)
126 Error. Incompatible options.
127 #endif
128
129 #include <X11/Xos.h>
130 #include <X11/Xlib.h>
131 #include <X11/Xresource.h>
132 #include <X11/Xutil.h>
133 #include <X11/Xatom.h>
134 #include <X11/keysym.h>
135 #ifdef USE_X11_MULTIBYTE
136 # include <X11/Xlocale.h>
137 #endif
138
139 #include <math.h>
140 #include "getcolor.h"
141
142 #ifdef USE_MOUSE
143 # include <X11/cursorfont.h>
144 #else
145 # define XC_crosshair 34
146 #endif
147
148 #include <signal.h>
149
150 #ifdef HAVE_SYS_BSDTYPES_H
151 # include <sys/bsdtypes.h>
152 #endif
153
154 #if defined(HAVE_SYS_SYSTEMINFO_H) && defined(HAVE_SYSINFO)
155 # include <sys/systeminfo.h>
156 # define SYSINFO_METHOD "sysinfo"
157 # define GP_SYSTEMINFO(host) sysinfo (SI_HOSTNAME, (host), MAXHOSTNAMELEN)
158 #else
159 # define SYSINFO_METHOD "gethostname"
160 # define GP_SYSTEMINFO(host) gethostname ((host), MAXHOSTNAMELEN)
161 #endif /* HAVE_SYS_SYSTEMINFO_H && HAVE_SYSINFO */
162
163 #ifdef USE_MOUSE
164 # ifdef OS2_IPC
165 #  define INCL_DOSPROCESS
166 #  define INCL_DOSSEMAPHORES
167 #  include <os2.h>
168 # endif
169 # include "gpexecute.h"
170 # include "mouse.h"
171 # include <unistd.h>
172 # include <fcntl.h>
173 # include <errno.h>
174 static unsigned long gnuplotXID = 0; /* WINDOWID of gnuplot */
175
176 #ifdef MOUSE_ALL_WINDOWS
177 # include "axis.h" /* Just to pick up FIRST_X_AXIS enums */
178 typedef struct axis_scale_t {
179     int term_lower;
180     double term_scale;
181     double min;
182     double logbase;
183 } axis_scale_t;
184 #endif
185
186 #endif /* USE_MOUSE */
187
188 #ifdef __EMX__
189 /* for gethostname ... */
190 # include <netdb.h>
191 /* for __XOS2RedirRoot */
192 #include <X11/Xlibint.h>
193 #endif
194
195
196 #ifdef VMS
197 # ifdef __DECC
198 #  include <starlet.h>
199 # endif
200 # define EXIT(status) sys$delprc(0, 0)  /* VMS does not drop itself */
201 #else /* !VMS */
202 # ifdef PIPE_IPC
203 #  define EXIT(status)                         \
204     do {                                       \
205         gp_exec_event(GE_pending, 0, 0, 0, 0, 0); \
206         close(1);                              \
207         close(0);                              \
208         exit(status);                          \
209     } while (0)
210 # else
211 #  define EXIT(status) exit(status)
212 # endif /* PIPE_IPC */
213 #endif /* !VMS */
214
215 #ifdef OSK
216 # define EINTR  E_ILLFNC
217 #endif
218
219
220 #define Ncolors 13
221
222 typedef struct cmap_t {
223     struct cmap_t *prev_cmap;  /* Linked list pointers and number */
224     struct cmap_t *next_cmap;
225     Colormap colormap;
226     unsigned long colors[Ncolors];      /* line colors as pixel values */
227     unsigned long rgbcolors[Ncolors];   /* line colors in rgb format */
228     unsigned long xorpixel;     /* line colors */
229     int total;
230     int allocated;
231     unsigned long *pixels;      /* pm3d colors */
232 } cmap_t;
233
234 /* always allocate a default colormap (done in preset()) */
235 static cmap_t default_cmap;
236
237 /* information about one window/plot */
238 typedef struct plot_struct {
239     Window window;
240     Pixmap pixmap;
241     unsigned int posn_flags;
242     int x, y;
243     unsigned int width, height; /* window size */
244     unsigned int gheight;       /* height of the part of the window that
245                                  * contains the graph (i.e., excluding the
246                                  * status line at the bottom if mouse is
247                                  * enabled) */
248     unsigned int px, py;
249     int ncommands, max_commands;
250     char **commands;
251     char *titlestring;
252 #ifdef USE_MOUSE
253     int button;                 /* buttons which are currently pressed */
254     char str[0xff];             /* last displayed string */
255 #endif
256     Time time;                  /* time stamp of previous event */
257     int lwidth;                 /* this and the following 6 lines declare */
258     int type;                   /* variables used during drawing in exec_cmd() */
259     int user_width;
260     enum JUSTIFY jmode;
261     double angle;               /* Text rotation angle in degrees */
262     int lt;
263 #ifdef USE_MOUSE
264     TBOOLEAN mouse_on;          /* is mouse bar on? */
265     TBOOLEAN ruler_on;          /* is ruler on? */
266     TBOOLEAN ruler_lineto_on;   /* is line between ruler and mouse cursor on? */
267     int ruler_x, ruler_y;       /* coordinates of ruler */
268     int ruler_lineto_x, ruler_lineto_y; /* draw line from ruler to current mouse pos */
269     TBOOLEAN zoombox_on;        /* is zoombox on? */
270     int zoombox_x1, zoombox_y1, zoombox_x2, zoombox_y2; /* coordinates of zoombox as last drawn */
271     char zoombox_str1a[64], zoombox_str1b[64], zoombox_str2a[64], zoombox_str2b[64];    /* strings to be drawn at corners of zoombox ; 1/2 indicate corner; a/b indicate above/below */
272     TBOOLEAN resizing;          /* TRUE while waiting for an acknowledgement of resize */
273 #endif
274     /* points to the cmap which is currently used for drawing.
275      * This is always the default colormap, if not in pm3d.
276      */
277     cmap_t *cmap;
278 #if defined(USE_MOUSE) && defined(MOUSE_ALL_WINDOWS)
279     /* This array holds per-axis scaling information sufficient to reconstruct
280      * plot coordinates of a mouse click.  It is a snapshot of the contents of
281      * gnuplot's axis_array structure at the time the plot was drawn.
282      */
283     int almost2d;
284     int axis_mask;              /* Bits set to show which axes are active */
285     axis_scale_t axis_scale[2*SECOND_AXES];
286 #endif
287     /* Last text position  - used by enhanced text mode */
288     int xLast, yLast;
289     /* Saved text position  - used by enhanced text mode */
290     int xSave, ySave;
291     /* Last rgb color that was set */
292     unsigned long current_rgb;
293
294     struct plot_struct *prev_plot;  /* Linked list pointers and number */
295     struct plot_struct *next_plot;
296     int plot_number;
297 } plot_struct;
298
299 static plot_struct *Add_Plot_To_Linked_List __PROTO((int));
300 static void Remove_Plot_From_Linked_List __PROTO((Window));
301 static plot_struct *Find_Plot_In_Linked_List_By_Number __PROTO((int));
302 static plot_struct *Find_Plot_In_Linked_List_By_Window __PROTO((Window));
303 static plot_struct *Find_Plot_In_Linked_List_By_CMap __PROTO((cmap_t *));
304
305 static struct plot_struct *current_plot = NULL;
306 static struct plot_struct *plot_list_start = NULL;
307
308 static void x11_setfill __PROTO((GC *gc, int style, TBOOLEAN poly));
309
310 /* information about window/plot to be removed */
311 typedef struct plot_remove_struct {
312     Window plot_window_to_remove;
313     struct plot_remove_struct *next_remove;
314     int processed;
315 } plot_remove_struct;
316
317 static void Add_Plot_To_Remove_FIFO_Queue __PROTO((Window));
318 static void Process_Remove_FIFO_Queue __PROTO((void));
319
320 static struct plot_remove_struct *remove_fifo_queue_start = NULL;
321 static int process_remove_fifo_queue = 0;
322
323 static cmap_t *Add_CMap_To_Linked_List __PROTO((void));
324 static void Remove_CMap_From_Linked_List __PROTO((cmap_t *));
325 static cmap_t *Find_CMap_In_Linked_List __PROTO((cmap_t *));
326 static int cmaps_differ __PROTO((cmap_t *, cmap_t *));
327
328 /* current_cmap always points to a valid colormap.  At start up
329  * it is the default colormap.  When a palette command comes
330  * across the pipe, the current_cmap is set to point at the
331  * resulting colormap which ends up in the linked list of colormaps.
332  * The current_cmap should never be removed from the linked list
333  * even if all windows are deleted, because that colormap will be
334  * used for the next plot.
335  */
336 static struct cmap_t *current_cmap = NULL;
337 static struct cmap_t *cmap_list_start = NULL;
338
339 /* These might work better as fuctions, but defines will do for now. */
340 #define ERROR_NOTICE(str)         "\nGNUPLOT (gplt_x11):  " str
341 #define ERROR_NOTICE_NEWLINE(str) "\n                     " str
342
343 enum { NOT_AVAILABLE = -1 };
344
345 #define SEL_LEN 0xff
346 static char selection[SEL_LEN] = "";
347
348
349 #ifdef USE_MOUSE
350 # define GRAPH_HEIGHT(plot)  ((plot)->gheight)
351 # define PIXMAP_HEIGHT(plot)  ((plot)->gheight + vchar)
352   /* note: PIXMAP_HEIGHT is the height of the plot including the status line,
353      even if the latter is not enabled right now */
354 #else
355 # define GRAPH_HEIGHT(plot)  ((plot)->height)
356 # define PIXMAP_HEIGHT(plot)  ((plot)->height)
357 #endif
358
359 static void CmapClear __PROTO((cmap_t *));
360 static void RecolorWindow __PROTO((plot_struct *));
361 static void FreeColors __PROTO((cmap_t *));
362 static void ReleaseColormap __PROTO((cmap_t *));
363 static unsigned long *ReallocColors __PROTO((cmap_t *, int));
364 static void PaletteMake __PROTO((t_sm_palette *));
365 static void PaletteSetColor __PROTO((plot_struct *, double));
366 static int GetVisual __PROTO((int, Visual **, int *));
367 static void scan_palette_from_buf __PROTO((void));
368
369 #if defined(WITH_IMAGE)
370 static unsigned short BitMaskDetails __PROTO((unsigned long mask, unsigned short *left_shift, unsigned short *right_shift));
371 #endif
372 #if defined(WITH_IMAGE) || defined(BINARY_X11_POLYGON)
373 TBOOLEAN swap_endian = 0;  /* For binary data. */
374 /* Petr's byte swapping routine. */
375 static inline void
376 byteswap(char* data, int datalen)
377 {
378     char tmp, *dest = data + datalen - 1;
379     if (datalen < 2) return;
380     while (dest > data) {
381         tmp = *dest;
382         *dest-- = *data;
383         *data++ = tmp;
384     }
385 }
386 /* Additional macros that should be more efficient when data size is known. */
387 char byteswap_char;
388 #define byteswap1(x)
389 #define byteswap2(x) \
390     byteswap_char = ((char *)x)[0]; \
391     ((char *)x)[0] = ((char *)x)[1]; \
392     ((char *)x)[1] = byteswap_char;
393 #define byteswap4(x) \
394     byteswap_char = ((char *)x)[0]; \
395     ((char *)x)[0] = ((char *)x)[3]; \
396     ((char *)x)[3] = byteswap_char; \
397     byteswap_char = ((char *)x)[1]; \
398     ((char *)x)[1] = ((char *)x)[2]; \
399     ((char *)x)[2] = byteswap_char
400 #endif
401
402 static void store_command __PROTO((char *, plot_struct *));
403 static void prepare_plot __PROTO((plot_struct *, int));
404 static void delete_plot __PROTO((plot_struct *));
405
406 static int record __PROTO((void));
407 static void process_event __PROTO((XEvent *));  /* from Xserver */
408 static void process_configure_notify_event __PROTO((XEvent *event));
409
410 static void mainloop __PROTO((void));
411
412 static void display __PROTO((plot_struct *));
413 static void UpdateWindow __PROTO((plot_struct *));
414 #ifdef USE_MOUSE
415 static int ErrorHandler __PROTO((Display *, XErrorEvent *));
416 static void DrawRuler __PROTO((plot_struct *));
417 static void EventuallyDrawMouseAddOns __PROTO((plot_struct *));
418 static void DrawBox __PROTO((plot_struct *));
419 static void DrawLineToRuler __PROTO((plot_struct *));
420 static void AnnotatePoint __PROTO((plot_struct *, int, int, const char[], const char[]));
421 static long int SetTime __PROTO((plot_struct *, Time));
422 static unsigned long AllocateXorPixel __PROTO((cmap_t *));
423 static void GetGCXor __PROTO((plot_struct *, GC *));
424 static void GetGCXorDashed __PROTO((plot_struct *, GC *));
425 #if 0
426 static void GetGCBlackAndWhite __PROTO((plot_struct *, GC *, Pixmap, int));
427 static int SplitAt __PROTO((char **, int, char *, char));
428 static void xfree __PROTO((void *));
429 #endif
430 static void EraseCoords __PROTO((plot_struct *));
431 static void DrawCoords __PROTO((plot_struct *, const char *));
432 static void DisplayCoords __PROTO((plot_struct *, const char *));
433
434 static TBOOLEAN is_meta __PROTO((KeySym));
435 static char* getMultiTabConsoleSwitchCommand __PROTO((unsigned long *));
436 #endif /* USE_MOUSE */
437
438 static void DrawRotated __PROTO((plot_struct *, Display *, GC,
439                                  int, int, const char *, int));
440 static int DrawRotatedErrorHandler __PROTO((Display *, XErrorEvent *));
441 static void exec_cmd __PROTO((plot_struct *, char *));
442
443 static void reset_cursor __PROTO((void));
444
445 static void preset __PROTO((int, char **));
446 static char *pr_GetR __PROTO((XrmDatabase, char *));
447 static void pr_color __PROTO((cmap_t *));
448 static void pr_dashes __PROTO((void));
449 static void pr_encoding __PROTO((void));
450 static void pr_font __PROTO((char *));
451 static void pr_geometry __PROTO((char *));
452 static void pr_pointsize __PROTO((void));
453 static void pr_width __PROTO((void));
454 static void pr_window __PROTO((plot_struct *));
455 static void ProcessEvents __PROTO((Window));
456 static void pr_raise __PROTO((void));
457 static void pr_persist __PROTO((void));
458 static void pr_feedback __PROTO((void));
459 static void pr_ctrlq __PROTO((void));
460 static void pr_fastrotate __PROTO((void));
461
462 #ifdef EXPORT_SELECTION
463 static void export_graph __PROTO((plot_struct *));
464 static void handle_selection_event __PROTO((XEvent *));
465 static void pr_exportselection __PROTO((void));
466 #endif
467
468 #if defined(USE_MOUSE) && defined(MOUSE_ALL_WINDOWS)
469 static void mouse_to_coords __PROTO((plot_struct *, XEvent *,
470                         double *, double *, double *, double *));
471 static double mouse_to_axis __PROTO((int, axis_scale_t *));
472 #endif
473
474 static char *FallbackFont = "fixed";
475 #ifdef USE_X11_MULTIBYTE
476 static char *FallbackFontMB =
477     "mbfont:*-medium-r-normal--14-*;*-medium-r-normal--16-*";
478 # define FontSetSep ';'
479 static int usemultibyte = 0;
480 static int multibyte_fonts_usable=1;
481 static int fontset_transsep __PROTO((char *, char *, int));
482 #endif /* USE_X11_MULTIBYTE */
483 static int gpXTextWidth __PROTO((XFontStruct *, const char *, int));
484 static int gpXTextHeight __PROTO((XFontStruct *));
485 static void gpXSetFont __PROTO((Display *, GC, Font));
486 static void gpXDrawImageString __PROTO((Display *, Drawable, GC, int, int, const char *, int));
487 static void gpXDrawString __PROTO((Display *, Drawable, GC, int, int, const char *, int));
488 static void gpXFreeFont __PROTO((Display *, XFontStruct *));
489 static XFontStruct *gpXLoadQueryFont __PROTO((Display *, char *));
490 static char *gpFallbackFont __PROTO((void));
491 static int gpXGetFontascent __PROTO((XFontStruct *cfont));
492
493 enum set_encoding_id encoding = S_ENC_DEFAULT; /* EAM - mirrored from core code by 'QE' */
494 static char default_font[64] = { '\0' };
495 static char default_encoding[16] = { '\0' };
496
497 #define Nwidths 10
498 static unsigned int widths[Nwidths] = { 2, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
499
500 #define Ndashes 10
501 static char dashes[Ndashes][5];
502
503 t_sm_palette sm_palette = {
504     -1,                         /* colorFormulae */
505     SMPAL_COLOR_MODE_NONE,      /* colorMode */
506     0, 0, 0,                    /* formula[RGB] */
507     0,                          /* positive */
508     0,                          /* use_maxcolors */
509     -1,                         /* colors */
510     (rgb_color *) 0,            /* color */
511     0,                          /* ps_allcF */
512     0,                          /* gradient_num */
513     (gradient_struct *) 0       /* gradient */
514     /* Afunc, Bfunc and Cfunc can't be initialised here */
515 };
516
517 static int have_pm3d = 1;
518 static int num_colormaps = 0;
519 static unsigned int maximal_possible_colors = 0x100;
520 static unsigned int minimal_possible_colors;
521
522 /* the following visual names must match the
523  * definitions in X.h in this order ! I hope
524  * this is standard (joze) */
525 static char *visual_name[] = {
526     "StaticGray",
527     "GrayScale",
528     "StaticColor",
529     "PseudoColor",
530     "TrueColor",
531     "DirectColor",
532     (char *) 0
533 };
534
535 static Display *dpy;
536 static int scr;
537 static Window root;
538 static Visual *vis = (Visual *) 0;
539 static GC gc = (GC) 0;
540 static GC *current_gc = (GC *) 0;
541 static GC gc_xor = (GC) 0;
542 static GC gc_xor_dashed = (GC) 0;
543 static GC fill_gc = (GC) 0;
544 static XFontStruct *font = NULL;
545 #ifdef USE_X11_MULTIBYTE
546 static XFontSet mbfont = NULL;
547 #endif
548 static int do_raise = yes, persist = no;
549 static TBOOLEAN fast_rotate = TRUE;
550 static int feedback = yes;
551 static int ctrlq = no;
552 static int dashedlines = no;
553 #ifdef EXPORT_SELECTION
554 static TBOOLEAN exportselection = TRUE;
555 #endif
556 static Cursor cursor;
557 static Cursor cursor_default;
558 #ifdef USE_MOUSE
559 static Cursor cursor_exchange;
560 static Cursor cursor_sizing;
561 static Cursor cursor_zooming;
562 #ifndef TITLE_BAR_DRAWING_MSG
563 static Cursor cursor_waiting;
564 static Cursor cursor_save;
565 static int button_pressed = 0;
566 #endif
567 #endif
568
569 static int windows_open = 0;
570
571 static int gX = 100, gY = 100;
572 static unsigned int gW = 640, gH = 450;
573 static unsigned int gFlags = PSize;
574
575 static unsigned int BorderWidth = 2;
576 static unsigned int dep;                /* depth */
577 static long max_request_size;
578
579 static Bool Mono = 0, Gray = 0, Rv = 0, Clear = 0;
580 static char X_Name[64] = "gnuplot";
581 static char X_Class[64] = "Gnuplot";
582
583 static int cx = 0, cy = 0;
584
585 /* Font characteristics held locally but sent back via pipe to x11.trm */
586 static int vchar, hchar;
587
588 /* Speficy negative values as indicator of uninitialized state */
589 static double xscale = -1.;
590 static double yscale = -1.;
591 double pointsize = -1.;
592 /* Avoid a crash upon using uninitialized variables from
593    above and avoid unnecessary calls to display().
594    Probably this is not the best fix ... */
595 #define Call_display(plot) if (xscale<0.) display(plot);
596 #define X(x) (int) ((x) * xscale)
597 #define Y(y) (int) ((4095-(y)) * yscale)
598 #define RevX(x) (((x)+0.5)/xscale)
599 #define RevY(y) (4095-((y)+0.5)/yscale)
600 /* note: the 0.5 term in RevX(x) and RevY(y) compensates for the round-off in X(x) and Y(y) */
601
602 #if defined(WITH_IMAGE) || defined(BINARY_X11_POLYGON)
603 #define Nbuf X11_COMMAND_BUFFER_LENGTH
604 #else
605 #define Nbuf 1024
606 #endif
607 static char buf[Nbuf];
608 static int buffered_input_available = 0;
609
610 static FILE *X11_ipc;
611
612 /* when using an ICCCM-compliant window manager, we can ask it
613  * to send us an event when user chooses 'close window'. We do this
614  * by setting WM_DELETE_WINDOW atom in property WM_PROTOCOLS
615  */
616
617 static Atom WM_PROTOCOLS, WM_DELETE_WINDOW;
618
619 static XPoint Diamond[5], Triangle[4];
620 static XSegment Plus[2], Cross[2], Star[4];
621
622 /* pixmaps used for filled boxes (ULIG) */
623 /* FIXME EAM - These data structures are a duplicate of the ones in bitmap.c */
624
625 /* pattern stipples for pattern fillstyle */
626 #define stipple_pattern_width 8
627 #define stipple_pattern_height 8
628 #define stipple_pattern_num 8
629 static const char stipple_pattern_bits[stipple_pattern_num][8] = {
630     { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 } /* no fill */
631    , { 0x81, 0x42, 0x24, 0x18, 0x18, 0x24, 0x42, 0x41 } /* cross-hatch      (1) */
632    , { 0x88, 0x55, 0x22, 0x55, 0x88, 0x55, 0x22, 0x55 } /* double crosshatch(2) */
633    , { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff } /* solid fill       (3) */
634    , { 0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80 } /* diagonal stripes (4) */
635    , { 0x80, 0x40, 0x20, 0x10, 0x08, 0x04, 0x02, 0x01 } /* diagonal stripes (5) */
636    , { 0x11, 0x11, 0x22, 0x22, 0x44, 0x44, 0x88, 0x88 } /* diagonal stripes (6) */
637    , { 0x88, 0x88, 0x44, 0x44, 0x22, 0x22, 0x11, 0x11 } /* diagonal stripes (7) */
638 };
639
640 static Pixmap stipple_pattern[stipple_pattern_num];
641 static int stipple_initialized = 0;
642
643 #ifdef X11_POLYLINE
644 static XPoint *polyline = NULL;
645 static int polyline_space = 0;
646 static int polyline_size = 0;
647 #endif
648
649 /*
650  * Main program
651  */
652 int
653 main(int argc, char *argv[])
654 {
655
656 #ifdef PIPE_IPC
657     int getfl;
658 #endif
659
660 #ifdef OSK
661     /* malloc large blocks, otherwise problems with fragmented mem */
662     _mallocmin(102400);
663 #endif
664 #ifdef __EMX__
665     /* close open file handles */
666     fcloseall();
667 #endif
668
669 #ifdef USE_X11_MULTIBYTE
670     if (setlocale(LC_ALL, "")==NULL || XSupportsLocale()==False)
671       multibyte_fonts_usable=0;
672     setlocale(LC_NUMERIC, "C"); /* HBB 20050525 */
673 #endif /* USE_X11_MULTIBYTE */
674     preset(argc, argv);
675
676 /* set up the alternative cursor */
677     cursor_default = XCreateFontCursor(dpy, XC_crosshair);
678     cursor = cursor_default;
679 #ifdef USE_MOUSE
680     /* create cursors for the splot actions */
681     cursor_exchange = XCreateFontCursor(dpy, XC_exchange);
682     cursor_sizing = XCreateFontCursor(dpy, XC_sizing);
683     /* arrow, top_left_arrow, left_ptr, sb_left_arrow, sb_right_arrow,
684      * plus, pencil, draft_large, right_ptr, draft_small */
685     cursor_zooming = XCreateFontCursor(dpy, XC_draft_small);
686 #ifndef TITLE_BAR_DRAWING_MSG
687     cursor_waiting = XCreateFontCursor(dpy, XC_watch);
688     cursor_save = (Cursor)0;
689 #endif
690 #endif
691 #ifdef PIPE_IPC
692     if (!pipe_died) {
693         /* set up nonblocking stdout */
694         getfl = fcntl(1, F_GETFL);      /* get current flags */
695         fcntl(1, F_SETFL, getfl | O_NONBLOCK);
696         signal(SIGPIPE, pipe_died_handler);
697     }
698 # endif
699
700 #ifdef X11_POLYLINE
701     polyline_space = 100;
702     polyline = calloc(polyline_space, sizeof(XPoint));
703     if (!polyline) fprintf(stderr, "Panic: cannot allocate polyline\n");
704 #endif
705
706     mainloop();
707
708     if (persist) {
709         FPRINTF((stderr, "waiting for %d windows\n", windows_open));
710
711 #ifndef DEBUG
712         /* HBB 20030519: Some programs executing gnuplot -persist may
713          * be waiting for all default handles to be closed before they
714          * consider the sub-process finished.  Emacs, e.g., does.  So,
715          * unless this is a DEBUG build, drop our connection to stderr
716          * now.  Using Freopen() ensures that debug fprintf()s won't
717          * crash. */
718         freopen("/dev/null", "w", stderr);
719 #endif
720
721         /* read x events until all windows have been quit */
722         while (windows_open > 0) {
723             XEvent event;
724             XNextEvent(dpy, &event);
725             process_event(&event);
726         }
727     }
728     XCloseDisplay(dpy);
729
730     FPRINTF((stderr, "exiting\n"));
731
732     EXIT(0);
733 }
734
735 /*-----------------------------------------------------------------------------
736  *   mainloop processing - process X events and input from gnuplot
737  *
738  *   Three different versions of main loop processing are provided to support
739  *   three different platforms.
740  *
741  *   DEFAULT_X11:     use select() for both X events and input on stdin
742  *                    from gnuplot inboard driver
743  *
744  *   CRIPPLED_SELECT: use select() to service X events and check during
745  *                    select timeout for temporary plot file created
746  *                    by inboard driver
747  *
748  *   VMS:             use XNextEvent to service X events and AST to
749  *                    service input from gnuplot inboard driver on stdin
750  *---------------------------------------------------------------------------*/
751
752
753 #ifdef DEFAULT_X11
754
755 /*
756  * DEFAULT_X11 mainloop
757  */
758 static void
759 mainloop()
760 {
761     int nf, cn = ConnectionNumber(dpy), in;
762     SELECT_TYPE_ARG1 nfds;
763     struct timeval timeout, *timer = (struct timeval *) 0;
764     fd_set tset;
765
766 #ifdef PIPE_IPC
767     int usleep_count = 0;
768     int out;
769     out = fileno(stdout);
770 #endif
771
772     X11_ipc = stdin;
773     in = fileno(X11_ipc);
774
775 #ifdef PIPE_IPC
776     if (out > in)
777         nfds = ((cn > out) ? cn : out) + 1;
778     else
779 #endif
780         nfds = ((cn > in) ? cn : in) + 1;
781
782 #ifdef ISC22
783 /* Added by Robert Eckardt, RobertE@beta.TP2.Ruhr-Uni-Bochum.de */
784     timeout.tv_sec = 0;         /* select() in ISC2.2 needs timeout */
785     timeout.tv_usec = 300000;   /* otherwise input from gnuplot is */
786     timer = &timeout;           /* suspended til next X event. */
787 #endif /* ISC22   (0.3s are short enough not to be noticed */
788
789     while (1) {
790
791         /* XNextEvent does an XFlush() before waiting. But here.
792          * we must ensure that the queue is flushed, since we
793          * dont call XNextEvent until an event arrives. (I have
794          * twice wasted quite some time over this issue, so now
795          * I am making sure of it !
796          */
797
798         XFlush(dpy);
799
800         FD_ZERO(&tset);
801         FD_SET(cn, &tset);
802
803         /* Don't wait for events if we know that input is
804          * already sitting in a buffer.  Also don't wait for
805          * input to become available.
806          */
807         if (buffered_input_available) {
808             timeout.tv_sec = 0;
809             timeout.tv_usec = 0;
810             timer = &timeout;
811         } else {
812             timer = (struct timeval *) 0;
813             FD_SET(in, &tset);
814         }
815
816 #ifdef PIPE_IPC
817         if (buffered_output_pending && !pipe_died) {
818             /* check, if stdout becomes writable */
819             FD_SET(out, &tset);
820         }
821 #ifdef HAVE_USLEEP
822         /* Make sure this loop does not monopolize CPU if the pipe is jammed */
823         if (++usleep_count > 10) {
824             usleep(100);
825             usleep_count = 0;
826         }
827 #endif
828 #endif
829
830         nf = select(nfds, SELECT_TYPE_ARG234 &tset, 0, 0, SELECT_TYPE_ARG5 timer);
831
832         if (nf < 0) {
833             if (errno == EINTR)
834                 continue;
835             perror("gnuplot_x11: select failed");
836             EXIT(1);
837         }
838
839         if (nf > 0)
840             XNoOp(dpy);
841
842         if (XPending(dpy)) {
843             /* used to use CheckMaskEvent() but that cannot receive
844              * maskable events such as ClientMessage. So now we do
845              * one event, then return to the select.
846              * And that almost works, except that under some Xservers
847              * running without a window manager (e.g. Hummingbird Exceed under Win95)
848              * a bogus ConfigureNotify is sent followed by a valid ConfigureNotify
849              * when the window is maximized.  The two events are queued, apparently
850              * in a single I/O because select() above doesn't see the second, valid
851              * event.  This little loop fixes the problem by flushing the
852              * event queue completely.
853              */
854             XEvent xe;
855             do {
856                 XNextEvent(dpy, &xe);
857                 process_event(&xe);
858             } while (XPending(dpy));
859         }
860
861         if (FD_ISSET(in, &tset) || buffered_input_available) {
862             if (!record())      /* end of input */
863                 return;
864         }
865 #ifdef PIPE_IPC
866         if (!pipe_died && (FD_ISSET(out, &tset) || buffered_output_pending)) {
867             gp_exec_event(GE_pending, 0, 0, 0, 0, 0);
868         }
869 #endif
870         /* A method in which the ErrorHandler can queue plots to be
871            removed from the linked list.  This prevents the situation
872            that could arise if the ErrorHandler directly removed
873            plots and some other part of the program were utilizing
874            a pointer to a plot that was destroyed. */
875         if (process_remove_fifo_queue) {
876             Process_Remove_FIFO_Queue();
877         }
878     }
879 }
880
881 #elif defined(CRIPPLED_SELECT)
882
883 char X11_ipcpath[32];
884
885 /*
886  * CRIPPLED_SELECT mainloop
887  */
888 static void
889 mainloop()
890 {
891     SELECT_TYPE_ARG1 nf, nfds, cn = ConnectionNumber(dpy);
892     struct timeval timeout, *timer;
893     fd_set tset;
894     unsigned long all = (unsigned long) (-1L);
895     XEvent xe;
896
897     timeout.tv_sec = 1;
898     timeout.tv_usec = 0;
899     timer = &timeout;
900     sprintf(X11_ipcpath, "/tmp/Gnuplot_%d", getppid());
901     nfds = cn + 1;
902
903     while (1) {
904         XFlush(dpy);            /* see above */
905
906         FD_ZERO(&tset);
907         FD_SET(cn, &tset);
908
909         /* Don't wait for events if we know that input is
910          * already sitting in a buffer.  Also don't wait for
911          * input to become available.
912          */
913         if (buffered_input_available) {
914             timeout.tv_sec = 0;
915             timeout.tv_usec = 0;
916             timer = &timeout;
917         } else {
918             timer = (struct timeval *) 0;
919             FD_SET(in, &tset);
920         }
921
922         nfds = (cn > in) ? cn + 1 : in + 1;
923
924         nf = select(nfds, SELECT_TYPE_ARG234 &tset, 0, 0, SELECT_TYPE_ARG5 timer);
925
926         if (nf < 0) {
927             if (errno == EINTR)
928                 continue;
929             perror("gnuplot_x11: select failed");
930             EXIT(1);
931         }
932
933         if (nf > 0)
934             XNoOp(dpy);
935
936         if (FD_ISSET(cn, &tset)) {
937             while (XCheckMaskEvent(dpy, all, &xe)) {
938                 process_event(&xe);
939             }
940         }
941         if ((X11_ipc = fopen(X11_ipcpath, "r"))) {
942             unlink(X11_ipcpath);
943             record();
944             fclose(X11_ipc);
945         }
946     }
947 }
948
949
950 #elif defined(VMS)
951 /*-----------------------------------------------------------------------------
952  *    VMS mainloop - Yehavi Bourvine - YEHAVI@VMS.HUJI.AC.IL
953  *---------------------------------------------------------------------------*/
954
955 /*  In VMS there is no decent Select(). hence, we have to loop inside
956  *  XGetNextEvent for getting the next X window event. In order to get input
957  *  from the master we assign a channel to SYS$INPUT and use AST's in order to
958  *  receive data. In order to exit the mainloop, we need to somehow make
959  *  XNextEvent return from within the ast. We do this with a XSendEvent() to
960  *  ourselves !
961  *  This needs a window to send the message to, so we create an unmapped window
962  *  for this purpose. Event type XClientMessage is perfect for this, but it
963  *  appears that such messages come from elsewhere (motif window manager,
964  *  perhaps ?) So we need to check fairly carefully that it is the ast event
965  *  that has been received.
966  */
967
968 #include <iodef.h>
969 char STDIIN[] = "SYS$INPUT:";
970 short STDIINchannel, STDIINiosb[4];
971 struct {
972     short size, type;
973     char *address;
974 } STDIINdesc;
975 char STDIINbuffer[64];
976 int status;
977
978 ast()
979 {
980     int status = sys$qio(0, STDIINchannel, IO$_READVBLK, STDIINiosb, record,
981                          0, STDIINbuffer, sizeof(STDIINbuffer) - 1, 0, 0, 0, 0);
982     if ((status & 0x1) == 0)
983         EXIT(status);
984 }
985
986 Window message_window;
987
988 static void
989 mainloop()
990 {
991     /* dummy unmapped window for receiving internally-generated terminate
992      * messages
993      */
994     message_window = XCreateSimpleWindow(dpy, root, 0, 0, 1, 1, 1, 0, 0);
995
996     STDIINdesc.size = strlen(STDIIN);
997     STDIINdesc.type = 0;
998     STDIINdesc.address = STDIIN;
999     status = sys$assign(&STDIINdesc, &STDIINchannel, 0, 0, 0);
1000     if ((status & 0x1) == 0)
1001         EXIT(status);
1002     ast();
1003
1004     for (;;) {
1005         XEvent xe;
1006         XNextEvent(dpy, &xe);
1007         if (xe.type == ClientMessage && xe.xclient.window == message_window) {
1008             if (xe.xclient.message_type == None && xe.xclient.format == 8 && strcmp(xe.xclient.data.b, "die gnuplot die") == 0) {
1009                 FPRINTF((stderr, "quit message from ast\n"));
1010                 return;
1011             } else {
1012                 FPRINTF((stderr, "Bogus XClientMessage event from window manager ?\n"));
1013             }
1014         }
1015         process_event(&xe);
1016     }
1017 }
1018 #else /* !(DEFAULT_X11 || CRIPPLED_SELECT || VMS */
1019 # error You lose. No mainloop.
1020 #endif                          /* !(DEFAULT_X11 || CRIPPLED_SELECT || VMS */
1021
1022 /* delete a window / plot */
1023 static void
1024 delete_plot(plot_struct *plot)
1025 {
1026     int i;
1027
1028     FPRINTF((stderr, "Delete plot %d\n", plot->plot_number));
1029
1030     for (i = 0; i < plot->ncommands; ++i)
1031         free(plot->commands[i]);
1032     plot->ncommands = 0;
1033     if (plot->commands)
1034         free(plot->commands);
1035     plot->commands = NULL;
1036     plot->max_commands = 0;
1037
1038
1039     /* Free up memory for window title. */
1040     if (plot->titlestring) {
1041         free(plot->titlestring);
1042         plot->titlestring = 0;
1043     }
1044
1045     if (plot->window) {
1046         FPRINTF((stderr, "Destroy window 0x%x\n", plot->window));
1047         XDestroyWindow(dpy, plot->window);
1048         plot->window = None;
1049         --windows_open;
1050     }
1051
1052     if (stipple_initialized) {  /* ULIG */
1053         int i;
1054         for (i = 0; i < stipple_pattern_num; i++)
1055             XFreePixmap(dpy, stipple_pattern[i]);
1056         stipple_initialized = 0;
1057     }
1058
1059     if (plot->pixmap) {
1060         XFreePixmap(dpy, plot->pixmap);
1061         plot->pixmap = None;
1062     }
1063     /* Release the colormap here to free color resources, but only
1064      * if this plot is using a colormap not used by another plot
1065      * and is not using the current colormap.
1066      */
1067     if (plot->cmap != current_cmap && !Find_Plot_In_Linked_List_By_CMap(plot->cmap))
1068         Remove_CMap_From_Linked_List(plot->cmap);
1069     /* but preserve geometry */
1070 }
1071
1072
1073 /* prepare the plot structure */
1074 static void
1075 prepare_plot(plot_struct *plot, int term_number)
1076 {
1077     int i;
1078
1079     for (i = 0; i < plot->ncommands; ++i)
1080         free(plot->commands[i]);
1081     plot->ncommands = 0;
1082
1083     if (!plot->posn_flags) {
1084         /* first time this window has been used - use default or -geometry
1085          * settings
1086          */
1087         plot->posn_flags = gFlags;
1088         plot->x = gX;
1089         plot->y = gY;
1090         plot->width = gW;
1091         plot->height = gH;
1092         plot->pixmap = None;
1093 #ifdef USE_MOUSE
1094         plot->gheight = gH;
1095         plot->resizing = FALSE;
1096         plot->str[0] = '\0';
1097         plot->zoombox_on = FALSE;
1098 #endif
1099     }
1100     if (!plot->window) {
1101         plot->cmap = current_cmap;      /* color space */
1102         RecolorWindow(plot);
1103         pr_window(plot);
1104 #ifdef USE_MOUSE
1105         /*
1106          * set all mouse parameters
1107          * to a well-defined state.
1108          */
1109         plot->button = 0;
1110         plot->mouse_on = TRUE;
1111         plot->x = NOT_AVAILABLE;
1112         plot->y = NOT_AVAILABLE;
1113         if (plot->str[0] != '\0') {
1114             /* if string was non-empty last time, initialize it as almost empty: one space, to prevent window resize */
1115             plot->str[0] = ' ';
1116             plot->str[1] = '\0';
1117         }
1118         plot->time = 0;         /* XXX how should we initialize this ? XXX */
1119 #endif
1120     }
1121
1122     /* We don't know that it is the same window as before, so we reset the
1123      * cursors for all windows and then define the cursor for the active
1124      * window
1125      */
1126     plot->angle = 0;            /* default is horizontal */
1127     reset_cursor();
1128     XDefineCursor(dpy, plot->window, cursor);
1129 }
1130
1131 /* store a command in a plot structure */
1132 static void
1133 store_command(char *buffer, plot_struct *plot)
1134 {
1135     char *p;
1136
1137     /* binary can't be printed as string */
1138 #if defined(WITH_IMAGE) && defined(BINARY_X11_POLYGON)
1139     if (*buffer == X11_GR_IMAGE || *buffer == X11_GR_FILLED_POLYGON || *buffer == X11_GR_SET_COLOR)
1140 #else
1141 #ifdef WITH_IMAGE
1142     if (*buffer == X11_GR_IMAGE)
1143 #endif
1144 #ifdef BINARY_X11_POLYGON
1145     if (*buffer != X11_GR_FILLED_POLYGON && *buffer != X11_GR_SET_COLOR)
1146 #endif
1147 #endif
1148 #if defined(WITH_IMAGE) || defined(BINARY_X11_POLYGON)
1149     {FPRINTF((stderr, "Store in %d : %c\n", plot->plot_number, *buffer));}
1150     else
1151 #endif
1152     {FPRINTF((stderr, "Store in %d : %s", plot->plot_number, buffer));}
1153
1154     if (plot->ncommands >= plot->max_commands) {
1155         plot->max_commands = plot->max_commands * 2 + 1;
1156         plot->commands = (plot->commands)
1157             ? (char **) realloc(plot->commands, plot->max_commands * sizeof(char *))
1158             : (char **) malloc(sizeof(char *));
1159     }
1160     p = (char *) malloc((unsigned) strlen(buffer) + 1);
1161     if (!plot->commands || !p) {
1162         fputs("gnuplot: can't get memory. X11 aborted.\n", stderr);
1163         EXIT(1);
1164     }
1165     plot->commands[plot->ncommands++] = strcpy(p, buffer);
1166 }
1167
1168 #ifndef VMS
1169
1170 static int read_input __PROTO((void));
1171
1172 /*
1173  * Handle input.  Use read instead of fgets because stdio buffering
1174  * causes trouble when combined with calls to select.
1175  */
1176 static int
1177 read_input()
1178 {
1179     static int rdbuf_size = 10 * Nbuf;
1180     static char rdbuf[10 * Nbuf];
1181     static int total_chars;
1182     static int rdbuf_offset;
1183     static int buf_offset;
1184     static int partial_read = 0;
1185     int fd = fileno(X11_ipc);
1186
1187     if (!partial_read)
1188         buf_offset = 0;
1189
1190     if (!buffered_input_available) {
1191         total_chars = read(fd, rdbuf, rdbuf_size);
1192         buffered_input_available = 1;
1193         partial_read = 0;
1194         rdbuf_offset = 0;
1195         if (total_chars == 0)
1196             return -2;
1197         if (total_chars < 0)
1198             return -1;
1199     }
1200
1201     if (rdbuf_offset < total_chars) {
1202         while (rdbuf_offset < total_chars && buf_offset < Nbuf) {
1203             char c = rdbuf[rdbuf_offset++];
1204             buf[buf_offset++] = c;
1205             if (c == '\n')
1206                 break;
1207         }
1208
1209         if (buf_offset == Nbuf) {
1210             fputs("\ngplt_x11.c: buffer overflow in read_input!\n"
1211                     "            X11 aborted.\n", stderr);
1212             EXIT(1);
1213         } else
1214             buf[buf_offset] = NUL;
1215     }
1216
1217     if (rdbuf_offset == total_chars) {
1218         buffered_input_available = 0;
1219         if (buf[buf_offset - 1] != '\n')
1220             partial_read = 1;
1221     }
1222
1223     return partial_read;
1224 }
1225
1226 static void read_input_line __PROTO((void));
1227
1228 /*
1229  * Handle a whole input line, issuing an error message if a complete
1230  * read does not appear after a few tries.
1231  */
1232 static void
1233 read_input_line()
1234 {
1235     int i_read;
1236     for (i_read = 1; read_input() == 1; i_read++) {
1237         if (i_read == 5)
1238             fprintf(stderr, "\ngplt_x11.c: A complete buffer instruction is not appearing across\n"
1239                               "            link.  Check for system overload or driver error.\n");
1240     };
1241 }
1242
1243 /*
1244  * This function builds back a palette from what x11.trm has written
1245  * into the pipe.  It cheats:  SMPAL_COLOR_MODE_FUNCTIONS for user defined
1246  * formulaes to transform gray into the three color components is not
1247  * implemented.  If this had to be done, one would have to include all
1248  * the code for evaluating functions here too, and, even worse: how to
1249  * transmit all function definition from gnuplot to here!
1250  * To avoid this, the following is done:  For grayscale, and rgbformulae
1251  * everything is easy.  Gradients are more difficult: Each gradient point
1252  * is encoded in a 8-byte string (which does not include any '\n' and
1253  * transmitted here.  Here the gradient is rebuilt.
1254  * Form user defined formulae x11.trm builds a special gradient:  The gray
1255  * values are equally spaced and are not transmitted, only the 6 bytes for
1256  * the rgbcolor are sent.  These are assembled into a gradient which is
1257  * than used in the palette.
1258  *
1259  * This function belongs completely into record(), but is quiet large so it
1260  * became a function of its own.
1261 */
1262 static void
1263 scan_palette_from_buf(void)
1264 {
1265     t_sm_palette tpal;
1266     char cm, pos, mod;
1267     if (4 != sscanf( buf+2, "%c %c %c %d", &cm, &pos, &mod,
1268                      &(tpal.use_maxcolors) ) ) {
1269         fprintf( stderr, "%s:%d error in setting palette.\n",
1270                  __FILE__, __LINE__);
1271
1272         return;
1273     }
1274
1275     tpal.colorMode = cm;
1276     tpal.positive = pos;
1277     tpal.cmodel = mod;
1278     tpal.gradient = NULL;
1279
1280     /* function palettes are transmitted as approximated gradients: */
1281     if (tpal.colorMode == SMPAL_COLOR_MODE_FUNCTIONS)
1282       tpal.colorMode = SMPAL_COLOR_MODE_GRADIENT;
1283
1284     switch( tpal.colorMode ) {
1285     case SMPAL_COLOR_MODE_GRAY:
1286         read_input_line();
1287         if (1 != sscanf( buf, "%lf", &(tpal.gamma) )) {
1288             fprintf( stderr, "%s:%d error in setting palette.\n",
1289                      __FILE__, __LINE__);
1290             return;
1291         }
1292         break;
1293     case SMPAL_COLOR_MODE_RGB:
1294         read_input_line();
1295         if (3 != sscanf( buf, "%d %d %d", &(tpal.formulaR),
1296                          &(tpal.formulaG), &(tpal.formulaB) )) {
1297             fprintf( stderr, "%s:%d error in setting palette.\n",
1298                      __FILE__, __LINE__);
1299             return;
1300         }
1301         break;
1302     case SMPAL_COLOR_MODE_GRADIENT: {
1303         int i=0;
1304         read_input_line();
1305         if (1 != sscanf( buf, "%d", &(tpal.gradient_num) )) {
1306             fprintf( stderr, "%s:%d error in setting palette.\n",
1307                      __FILE__, __LINE__);
1308             return;
1309         }
1310         tpal.gradient = (gradient_struct*)
1311           malloc( tpal.gradient_num * sizeof(gradient_struct) );
1312         assert(tpal.gradient);
1313         for( i=0; i<tpal.gradient_num; i++ ) {
1314             /*  this %50 *must* match the amount of gradient structs
1315                 written to the pipe by x11.trm!  */
1316             if (i%50 == 0) {
1317                 read_input_line();
1318             }
1319             str_to_gradient_entry( &(buf[8*(i%50)]), &(tpal.gradient[i]) );
1320         }
1321         break;
1322       }
1323     case SMPAL_COLOR_MODE_FUNCTIONS:
1324         fprintf( stderr, "%s:%d ooops: No function palettes for x11!\n",
1325                  __FILE__, __LINE__ );
1326         break;
1327     default:
1328         fprintf( stderr, "%s:%d ooops: Unknown colorMode '%c'.\n",
1329                  __FILE__, __LINE__, (char)(tpal.colorMode) );
1330         tpal.colorMode = SMPAL_COLOR_MODE_GRAY;
1331         break;
1332     }
1333     PaletteMake(&tpal);
1334
1335     if (tpal.gradient)
1336         free(tpal.gradient);
1337 }
1338
1339
1340 /*
1341  * record - record new plot from gnuplot inboard X11 driver (Unix)
1342  */
1343 static struct plot_struct *plot = NULL;
1344 static int
1345 record()
1346 {
1347     while (1) {
1348         int status = read_input();
1349         if (status == -2)
1350             return 0;
1351         if (status != 0)
1352             return status;
1353
1354         switch (*buf) {
1355         case 'G':               /* enter graphics mode */
1356             {
1357                 int plot_number;
1358 #ifndef USE_MOUSE
1359                 sscanf(buf, "G%d", &plot_number);
1360 #else
1361 #ifdef OS2_IPC
1362                 sscanf(buf, "G%d %lu %li", &plot_number, &gnuplotXID, &ppidGnu);
1363 #else
1364                 sscanf(buf, "G%d %lu", &plot_number, &gnuplotXID);
1365 #endif
1366 #endif
1367                 FPRINTF((stderr, "plot for window number %d\n", plot_number));
1368                 if (!(plot = Find_Plot_In_Linked_List_By_Number(plot_number)))
1369                     plot = Add_Plot_To_Linked_List(plot_number);
1370                 if (plot)
1371                     prepare_plot(plot, plot_number);
1372                 current_plot = plot;
1373 #ifdef OS2_IPC
1374                 if (!input_from_PM_Terminal) {  /* get shared memory */
1375                     sprintf(mouseShareMemName, "\\SHAREMEM\\GP%i_Mouse_Input", (int) ppidGnu);
1376                     if (DosGetNamedSharedMem(&input_from_PM_Terminal, mouseShareMemName, PAG_WRITE))
1377                         DosBeep(1440L, 1000L);  /* indicates error */
1378                     semInputReady = 0;
1379                 }
1380 #endif
1381 #ifdef USE_MOUSE
1382 #ifdef TITLE_BAR_DRAWING_MSG
1383                 /* show a message in the wm's title bar that the
1384                  * graph will be redrawn. This might be useful
1385                  * for slow redrawing (large plots). The title
1386                  * string is reset to the default at the end of
1387                  * display(). We should make this configurable!
1388                  */
1389                 if (plot) {
1390                     if (plot->window) {
1391                         char *msg;
1392                         char *added_text = " drawing ...";
1393                         int orig_len = (plot->titlestring ? strlen(plot->titlestring) : 0);
1394                         if (msg = (char *) malloc(orig_len + strlen(added_text) + 1)) {
1395                             strcpy(msg, plot->titlestring);
1396                             strcat(msg, added_text);
1397                             XStoreName(dpy, plot->window, msg);
1398                             free(msg);
1399                         } else
1400                             XStoreName(dpy, plot->window, added_text + 1);
1401                     }
1402                 }
1403 #else
1404                 if (!button_pressed) {
1405                     cursor_save = cursor;
1406                     cursor = cursor_waiting;
1407                     if (plot)
1408                         XDefineCursor(dpy, plot->window, cursor);
1409                 }
1410 #endif
1411 #endif
1412                 /* continue; */
1413                 /* return 1; To do: Should a "return" be here?  Gnuplot usually sends 'G' followed by other commands.  So perhaps not needed. DJS */
1414             }
1415             break;
1416         case 'N':               /* just update the plot number */
1417             {
1418                 int itmp;
1419                 if (strcspn(buf+1, " \n") && sscanf(buf, "N%d", &itmp))
1420                     current_plot = Add_Plot_To_Linked_List(itmp);
1421                 return 1;
1422             }
1423             break;
1424         case 'C':               /* close the plot with given number */
1425             {
1426                 int itmp;
1427                 if (strcspn(buf+1, " \n") && sscanf(buf, "C%d", &itmp)) {
1428                   plot_struct *psp;
1429                   if ((psp = Find_Plot_In_Linked_List_By_Number(itmp)))
1430                     Remove_Plot_From_Linked_List(psp->window);
1431                 } else if (current_plot) {
1432                   Remove_Plot_From_Linked_List(current_plot->window);
1433                 }
1434                 return 1;
1435             }
1436             break;
1437         case '^':               /* raise the plot with given number or the whole group */
1438             {
1439                 int itmp;
1440                 if (strcspn(buf+1," \n") && sscanf(buf, "^%d", &itmp)) {
1441                     plot_struct *psp;
1442                     if ((psp = Find_Plot_In_Linked_List_By_Number(itmp))) {
1443                         XRaiseWindow(dpy, psp->window);
1444                     }
1445                 } else {
1446                     /* Find end of list, i.e., first created. */
1447                     plot_struct *psp = plot_list_start;
1448                     while (psp != NULL) {
1449                         if (psp->next_plot == NULL) break;
1450                         psp = psp->next_plot;
1451                     }
1452                     while (psp != NULL) {
1453                         XRaiseWindow(dpy, psp->window);
1454                         psp = psp->prev_plot;
1455                     }
1456                 }
1457                 return 1;
1458             }
1459             break;
1460         case 'v':               /* lower the plot with given number or the whole group */
1461             {
1462                 int itmp;
1463                 if (strcspn(buf+1," \n") && sscanf(buf, "v%d", &itmp)) {
1464                     plot_struct *psp;
1465                     if ((psp = Find_Plot_In_Linked_List_By_Number(itmp))) {
1466                         XLowerWindow(dpy, psp->window);
1467                     }
1468                 } else if (current_plot) {
1469                     plot_struct *psp = plot_list_start;
1470                     while (psp != NULL) {
1471                         XLowerWindow(dpy, psp->window);
1472                         psp = psp->next_plot;
1473                     }
1474                 }
1475                 return 1;
1476             }
1477             break;
1478         case 'n':               /* update the plot name (title) */
1479             {
1480                 if (!current_plot)
1481                     current_plot = Add_Plot_To_Linked_List(0);
1482                 if (current_plot) {
1483                     char *cp;
1484                     if (current_plot->titlestring)
1485                         free(current_plot->titlestring);
1486                     if ((current_plot->titlestring = (char *) malloc(strlen(buf+1) + 1) )) {
1487                         strcpy(current_plot->titlestring, buf+1);
1488                         cp = current_plot->titlestring;
1489                     } else
1490                         cp = "<lost name>";
1491                     if (current_plot->window)
1492                         XStoreName(dpy, current_plot->window, cp);
1493                 }
1494                 return 1;
1495             }
1496             break;
1497         case 'E':               /* leave graphics mode / suspend */
1498             if (plot)
1499                 display(plot);
1500 #ifdef USE_MOUSE
1501             if (plot == current_plot)
1502                 gp_exec_event(GE_plotdone, 0, 0, 0, 0, 0);      /* notify main program */
1503 #endif
1504             return 1;
1505         case 'R':               /* leave x11 mode */
1506             reset_cursor();
1507             return 0;
1508
1509         case X11_GR_MAKE_PALETTE:
1510             if (have_pm3d)
1511                 scan_palette_from_buf();
1512             return 1;
1513 #if 0
1514 /* (DJS 28sep2004)  Possibly remove.  Not sure this is useful for anything.
1515  * What gnuplot command would issue this?  When would gnuplot know it is OK to
1516  * release a palette inside gnuplot_x11?  I see no X11_GR_RELEASE_PALETTE
1517  * or 'e' inside x11.trm.
1518  *
1519  * Plots and colormaps are independent in new scheme, so the line below
1520  * "if (plot)" is outdated.  Also, sm_palette is a global, static structure.
1521  *  sm_palette.gradient should be set to NULL after freeing the memory.
1522  */
1523         case X11_GR_RELEASE_PALETTE:
1524             /* turn pm3d off */
1525             FPRINTF((stderr, "X11_GR_RELEASE_PALETTE\n"));
1526             if (plot)
1527                 ReleaseColormap(plot);
1528             sm_palette.colorMode = SMPAL_COLOR_MODE_NONE;
1529             free( sm_palette.gradient );
1530             return 1;
1531 #endif
1532
1533 #if defined(WITH_IMAGE) || defined(BINARY_X11_POLYGON)
1534         case X11_GR_CHECK_ENDIANESS:
1535             {
1536                 /* Initialize variable in case short happens to be longer than two bytes. */
1537                 unsigned short tmp = (unsigned short) ENDIAN_VALUE;
1538                 ((char *)&tmp)[0] = buf[1];
1539                 ((char *)&tmp)[1] = buf[2];
1540                 if (tmp == (unsigned short) ENDIAN_VALUE) swap_endian = 0;
1541                 else swap_endian = 1;
1542             }
1543             return 1;
1544 #endif
1545
1546         case 'X':               /* tell the driver about do_raise /  persist */
1547             {
1548                 int tmp_do_raise = UNSET, tmp_persist = UNSET;
1549                 int tmp_dashed = UNSET, tmp_ctrlq = UNSET;
1550                 sscanf(buf, "X%d%d%d%d", &tmp_do_raise, &tmp_persist, &tmp_dashed, &tmp_ctrlq);
1551                 if (UNSET != tmp_do_raise)
1552                     do_raise = tmp_do_raise;
1553                 if (UNSET != tmp_persist)
1554                     persist = tmp_persist;
1555                 if (UNSET != tmp_dashed)
1556                     dashedlines = tmp_dashed;
1557                 if (UNSET != tmp_ctrlq)
1558                     ctrlq = tmp_ctrlq;
1559             }
1560             return 1;
1561
1562         case 's': /* set window geometry */
1563             {
1564                 char strtmp[256];
1565                 sscanf(&buf[2], "%s", strtmp);
1566                 pr_geometry(strtmp);
1567             }
1568             return 1;
1569
1570 #ifdef USE_MOUSE
1571         case 'u':
1572 #ifdef PIPE_IPC
1573             if (!pipe_died)
1574 #endif
1575             {
1576                 /* `set cursor' */
1577                 int c, x, y;
1578                 sscanf(buf, "u%4d%4d%4d", &c, &x, &y);
1579                 if (plot) {
1580                     switch (c) {
1581                     case -4:    /* switch off line between ruler and mouse cursor */
1582                         DrawLineToRuler(plot);
1583                         plot->ruler_lineto_on = FALSE;
1584                         break;
1585                     case -3:    /* switch on line between ruler and mouse cursor */
1586                         if (plot->ruler_on && plot->ruler_lineto_on)
1587                             break;
1588                         plot->ruler_lineto_x = X(x);
1589                         plot->ruler_lineto_y = Y(y);
1590                         plot->ruler_lineto_on = TRUE;
1591                         DrawLineToRuler(plot);
1592                         break;
1593                     case -2:    /* warp pointer */
1594                         XWarpPointer(dpy, None /* src_w */ ,
1595                                      plot->window /* dest_w */ , 0, 0, 0, 0, X(x), Y(y));
1596                     case -1:    /* zoombox */
1597                         plot->zoombox_x1 = plot->zoombox_x2 = X(x);
1598                         plot->zoombox_y1 = plot->zoombox_y2 = Y(y);
1599                         plot->zoombox_on = TRUE;
1600                         DrawBox(plot);
1601                         break;
1602                     case 0:     /* standard cross-hair cursor */
1603                         cursor = cursor_default;
1604                         XDefineCursor(dpy, plot->window, cursor);
1605                         break;
1606                     case 1:     /* cursor during rotation */
1607                         cursor = cursor_exchange;
1608                         XDefineCursor(dpy, plot->window, cursor);
1609                         break;
1610                     case 2:     /* cursor during scaling */
1611                         cursor = cursor_sizing;
1612                         XDefineCursor(dpy, plot->window, cursor);
1613                         break;
1614                     case 3:     /* cursor during zooming */
1615                         cursor = cursor_zooming;
1616                         XDefineCursor(dpy, plot->window, cursor);
1617                         break;
1618                     }
1619                     if (c >= 0 && plot->zoombox_on) {
1620                         /* erase zoom box */
1621                         DrawBox(plot);
1622                         plot->zoombox_on = FALSE;
1623                     }
1624                     if (c >= 0 && plot->ruler_lineto_on) {
1625                         /* erase line from ruler to cursor */
1626                         DrawLineToRuler(plot);
1627                         plot->ruler_lineto_on = FALSE;
1628                     }
1629                 }
1630             }
1631             return 1;
1632
1633         case 't':
1634 #ifdef PIPE_IPC
1635             if (!pipe_died)
1636 #endif
1637             {
1638                 int where;
1639                 char *second;
1640                 if (sscanf(buf, "t%4d", &where) != 1)
1641                     return 1;
1642                 buf[strlen(buf) - 1] = 0;       /* remove trailing \n */
1643                 if (plot) {
1644                     switch (where) {
1645                     case 0:
1646                         DisplayCoords(plot, buf + 5);
1647                         break;
1648                     case 1:
1649                         second = strchr(buf + 5, '\r');
1650                         if (second == NULL) {
1651                             *(plot->zoombox_str1a) = '\0';
1652                             *(plot->zoombox_str1b) = '\0';
1653                             break;
1654                         }
1655                         *second = 0;
1656                         second++;
1657                         if (plot->zoombox_on)
1658                             DrawBox(plot);
1659                         strcpy(plot->zoombox_str1a, buf + 5);
1660                         strcpy(plot->zoombox_str1b, second);
1661                         if (plot->zoombox_on)
1662                             DrawBox(plot);
1663                         break;
1664                     case 2:
1665                         second = strchr(buf + 5, '\r');
1666                         if (second == NULL) {
1667                             *(plot->zoombox_str2a) = '\0';
1668                             *(plot->zoombox_str2b) = '\0';
1669                             break;
1670                         }
1671                         *second = 0;
1672                         second++;
1673                         if (plot->zoombox_on)
1674                             DrawBox(plot);
1675                         strcpy(plot->zoombox_str2a, buf + 5);
1676                         strcpy(plot->zoombox_str2b, second);
1677                         if (plot->zoombox_on)
1678                             DrawBox(plot);
1679                         break;
1680                     }
1681                 }
1682             }
1683             return 1;
1684
1685         case 'r':
1686 #ifdef PIPE_IPC
1687             if (!pipe_died)
1688 #endif
1689             {
1690                 if (plot) {
1691                     int x, y;
1692                     DrawRuler(plot);    /* erase previous ruler */
1693                     sscanf(buf, "r%4d%4d", &x, &y);
1694                     if (x < 0) {
1695                         DrawLineToRuler(plot);
1696                         plot->ruler_on = FALSE;
1697                     } else {
1698                         plot->ruler_on = TRUE;
1699                         plot->ruler_x = x;
1700                         plot->ruler_y = y;
1701                         plot->ruler_lineto_x = X(x);
1702                         plot->ruler_lineto_y = Y(y);
1703                         DrawLineToRuler(plot);
1704                     }
1705                     DrawRuler(plot);    /* draw new one */
1706                 }
1707             }
1708             return 1;
1709
1710         case 'z':
1711 #ifdef PIPE_IPC
1712             if (!pipe_died)
1713 #endif
1714             {
1715                 int len = strlen(buf + 1) - 1;  /* discard newline '\n' */
1716                 memcpy(selection, buf + 1, len < SEL_LEN ? len : SEL_LEN);
1717                 /* terminate */
1718                 selection[len + 1 < SEL_LEN ? len + 1 : SEL_LEN - 1] = '\0';
1719                 XStoreBytes(dpy, buf + 1, len);
1720                 XFlush(dpy);
1721 #ifdef EXPORT_SELECTION
1722                 if (plot && exportselection)
1723                     export_graph(plot);
1724 #endif
1725             }
1726             return 1;
1727 #endif
1728 #ifdef USE_MOUSE
1729         case 'Q':
1730             /* Set default font immediately and return size info through pipe */
1731             if (buf[1] == 'G') {
1732                 int scaled_hchar, scaled_vchar;
1733                 char *c = &(buf[strlen(buf)-1]);
1734                 while (*c <= ' ') *c-- = '\0';
1735                 strncpy(default_font, &buf[2], strlen(&buf[2])+1);
1736                 pr_font(NULL);
1737                 if (plot) {
1738                     /* EAM FIXME - this is all out of order; initialization doesnt */
1739                     /*             happen until term->graphics() is called.        */
1740                     xscale = (plot->width > 0) ? plot->width / 4096. : gW / 4096.;
1741                     yscale = (plot->height > 0) ? plot->height / 4096. : gH / 4096.;
1742                     scaled_hchar = (1.0/xscale) * hchar;
1743                     scaled_vchar = (1.0/yscale) * vchar;
1744                     FPRINTF((stderr, "gplt_x11: preset default font to %s hchar = %d vchar = %d \n",
1745                              default_font, scaled_hchar, scaled_vchar));
1746                     gp_exec_event(GE_fontprops, plot->width, plot->height,
1747                                   scaled_hchar, scaled_vchar, 0);
1748                 }
1749                 return 1;
1750             }
1751             /* fall through */
1752 #endif
1753         default:
1754             if (plot)
1755                 store_command(buf, plot);
1756             continue;
1757         }
1758     }
1759     if (feof(X11_ipc) || ferror(X11_ipc))
1760         return 0;
1761     else
1762         return 1;
1763 }
1764
1765 #else /* VMS */
1766
1767 /*
1768  *   record - record new plot from gnuplot inboard X11 driver (VMS)
1769  */
1770 static struct plot_struct *plot = NULL;
1771 record()
1772 {
1773     int status;
1774
1775     if ((STDIINiosb[0] & 0x1) == 0)
1776         EXIT(STDIINiosb[0]);
1777     STDIINbuffer[STDIINiosb[1]] = '\0';
1778     strcpy(buf, STDIINbuffer);
1779
1780     switch (*buf) {
1781     case 'G':                   /* enter graphics mode */
1782         {
1783             int plot_number = atoi(buf + 1);    /* 0 if none specified */
1784             FPRINTF((stderr, "plot for window number %d\n", plot_number));
1785             if (!(plot = Find_Plot_In_Linked_List_By_Number(plot_number)))
1786                 plot = Add_Plot_To_Linked_List(plot_number);
1787             if (plot)
1788                 prepare_plot(plot, plot_number);
1789             current_plot = plot;
1790             break;
1791         }
1792     case 'E':                   /* leave graphics mode */
1793         if (plot)
1794             display(plot);
1795         break;
1796     case 'R':                   /* exit x11 mode */
1797         FPRINTF((stderr, "received R - sending ClientMessage\n"));
1798         reset_cursor();
1799         sys$cancel(STDIINchannel);
1800         /* this is ridiculous - cook up an event to ourselves,
1801          * in order to get the mainloop() out of the XNextEvent() call
1802          * it seems that window manager can also send clientmessages,
1803          * so put a checksum into the message
1804          */
1805         {
1806             XClientMessageEvent event;
1807             event.type = ClientMessage;
1808             event.send_event = True;
1809             event.display = dpy;
1810             event.window = message_window;
1811             event.message_type = None;
1812             event.format = 8;
1813             strcpy(event.data.b, "die gnuplot die");
1814             XSendEvent(dpy, message_window, False, 0, (XEvent *) & event);
1815             XFlush(dpy);
1816         }
1817         return;                 /* no ast */
1818     default:
1819         if (plot)
1820             store_command(buf, plot);
1821         break;
1822     }
1823     ast();
1824 }
1825 #endif /* VMS */
1826
1827 static int
1828 DrawRotatedErrorHandler(Display * display, XErrorEvent * error_event)
1829 {
1830   return 0;  /* do nothing */
1831 }
1832
1833 static void
1834 DrawRotated(plot_struct *plot, Display *dpy, GC gc, int xdest, int ydest,
1835         const char *str, int len)
1836 {
1837     Window w = plot->window;
1838     Drawable d = plot->pixmap;
1839     double angle = plot->angle;
1840     enum JUSTIFY just = plot->jmode;
1841     int x, y;
1842     double src_x, src_y;
1843     double dest_x, dest_y;
1844     int width = gpXTextWidth(font, str, len);
1845     int height = vchar;
1846     double src_cen_x = (double)width * 0.5;
1847     double src_cen_y = (double)height * 0.5;
1848     static const double deg2rad = .01745329251994329576; /* atan2(1, 1) / 45.0; */
1849     double sa = sin(angle * deg2rad);
1850     double ca = cos(angle * deg2rad);
1851     int dest_width = (double)height * fabs(sa) + (double)width * fabs(ca) + 2;
1852     int dest_height = (double)width * fabs(sa) + (double)height * fabs(ca) + 2;
1853     double dest_cen_x = (double)dest_width * 0.5;
1854     double dest_cen_y = (double)dest_height * 0.5;
1855     char* data = (char*) malloc(dest_width * dest_height * sizeof(char));
1856     Pixmap pixmap_src = XCreatePixmap(dpy, root, (unsigned int)width, (unsigned int)height, 1);
1857     XImage *image_src;
1858     XImage *image_dest;
1859     XImage *image_scr;
1860     unsigned long fgpixel = 0;
1861     unsigned long bgpixel = 0;
1862     XWindowAttributes win_attrib;
1863     int xscr, yscr, xoff, yoff;
1864     unsigned int scr_width, scr_height;
1865     XErrorHandler prevErrorHandler;
1866
1867     unsigned long gcFunctionMask = GCFunction;
1868     XGCValues gcValues;
1869     int gcCurrentFunction = 0;
1870     Status s;
1871
1872     /* bitmapGC is static, so that is has to be initialized only once */
1873     static GC bitmapGC = (GC) 0;
1874
1875     /* eventually initialize bitmapGC */
1876     if ((GC)0 == bitmapGC) {
1877         bitmapGC = XCreateGC(dpy, pixmap_src, 0, (XGCValues *) 0);
1878         XSetForeground(dpy, bitmapGC, 1);
1879         XSetBackground(dpy, bitmapGC, 0);
1880     }
1881
1882     s = XGetGCValues(dpy, gc, gcFunctionMask|GCForeground|GCBackground, &gcValues);
1883     if (s) {
1884         /* success */
1885         fgpixel = gcValues.foreground;
1886         bgpixel = gcValues.background;
1887         gcCurrentFunction = gcValues.function; /* save current function */
1888     }
1889
1890     /* set font for the bitmap GC */
1891     if (font)
1892       gpXSetFont(dpy, bitmapGC, font->fid);
1893
1894     /* draw string to the source bitmap */
1895     gpXDrawImageString(dpy, pixmap_src, bitmapGC, 0, gpXGetFontascent(font), str, len);
1896
1897     /* create XImage's of depth 1 */
1898     /* source from pixmap */
1899     image_src = XGetImage(dpy, pixmap_src, 0, 0, (unsigned int)width, (unsigned int)height,
1900             1, XYPixmap /* ZPixmap, XYBitmap */ );
1901
1902     /* empty dest */
1903     assert(data);
1904     memset((void*)data, 0, (size_t)dest_width * dest_height);
1905     image_dest = XCreateImage(dpy, vis, 1, XYBitmap,
1906             0, data, (unsigned int)dest_width, (unsigned int)dest_height, 8, 0);
1907 #define RotateX(_x, _y) (( (_x) * ca + (_y) * sa + dest_cen_x))
1908 #define RotateY(_x, _y) ((-(_x) * sa + (_y) * ca + dest_cen_y))
1909     /* copy & rotate from source --> dest */
1910     for (y = 0, src_y = -src_cen_y; y < height; y++, src_y++) {
1911         for (x = 0, src_x = -src_cen_x; x < width; x++, src_x++) {
1912             /* TODO: move some operations outside the inner loop (joze) */
1913             dest_x = rint(RotateX(src_x, src_y));
1914             dest_y = rint(RotateY(src_x, src_y));
1915             if (dest_x >= 0 && dest_x < dest_width && dest_y >= 0 && dest_y < dest_height)
1916                 XPutPixel(image_dest, (int)dest_x, (int)dest_y, XGetPixel(image_src, x, y));
1917         }
1918     }
1919
1920     src_cen_y = 0; /* EAM 29-Sep-2002 - vertical justification has already been done */
1921
1922     switch (just) {
1923         case LEFT:
1924         default:
1925             xdest -= RotateX(-src_cen_x, src_cen_y);
1926             ydest -= RotateY(-src_cen_x, src_cen_y);
1927             break;
1928         case CENTRE:
1929             xdest -= RotateX(0, src_cen_y);
1930             ydest -= RotateY(0, src_cen_y);
1931             break;
1932         case RIGHT:
1933             xdest -= RotateX(src_cen_x, src_cen_y);
1934             ydest -= RotateY(src_cen_x, src_cen_y);
1935             break;
1936     }
1937
1938 #undef RotateX
1939 #undef RotateY
1940
1941     if (fast_rotate) {
1942     /* This default method is a lot faster, but may corrupt the colors
1943      * underneath the rotated text if the X display Visual is PseudoColor.
1944      * EAM - August 2005
1945      */
1946         assert(s);      /* Previous success in reading XGetGCValues() */
1947         /* Force pixels of new text to black, background unchanged */
1948         gcValues.function = GXand;
1949         gcValues.background = WhitePixel(dpy, scr);
1950         gcValues.foreground = BlackPixel(dpy, scr);
1951         XChangeGC(dpy, gc, gcFunctionMask|GCBackground|GCForeground, &gcValues);
1952         XPutImage(dpy, d, gc, image_dest, 0, 0, xdest, ydest, dest_width, dest_height);
1953
1954         /* Force pixels of new text to color, background unchanged */
1955         gcValues.function = GXor;
1956         gcValues.background = BlackPixel(dpy, scr);
1957         gcValues.foreground = fgpixel;
1958         XChangeGC(dpy, gc, gcFunctionMask|GCBackground|GCForeground, &gcValues);
1959         XPutImage(dpy, d, gc, image_dest, 0, 0, xdest, ydest, dest_width, dest_height);
1960
1961     } else {
1962     /* Slow but sure version - grab the current screen area where the new
1963      * text will go and substitute in the pixels of the new text one by one.
1964      * NB: selected by X Resource
1965      *                             gnuplot*fastrotate: off
1966      */
1967         assert(s);      /* Previous success in reading XGetGCValues() */
1968         gcValues.function = GXcopy;
1969         XChangeGC(dpy, gc, gcFunctionMask, &gcValues);
1970         s = XGetWindowAttributes(dpy, w, &win_attrib);
1971         /* compute screen coords that are within the current window */
1972         xscr = (xdest<0)? 0 : xdest;
1973         yscr = (ydest<0)? 0 : ydest;;
1974         scr_width = dest_width; scr_height = dest_height;
1975         if (xscr + dest_width > win_attrib.width)
1976             scr_width = win_attrib.width - xscr;
1977         if (yscr + dest_height > win_attrib.height)
1978             scr_height = win_attrib.height - yscr;
1979         xoff = xscr - xdest;
1980         yoff = yscr - ydest;
1981         scr_width -= xoff;
1982         scr_height -= yoff;
1983         prevErrorHandler = XSetErrorHandler(DrawRotatedErrorHandler);
1984
1985         image_scr = XGetImage(dpy, d, xscr, yscr, scr_width,
1986                           scr_height, AllPlanes, XYPixmap);
1987         if (image_scr != 0){
1988             /* copy from 1 bit bitmap image of text to the full depth image of screen*/
1989             for (y = 0; y < scr_height; y++){
1990                 for (x = 0; x < scr_width; x++){
1991                     if (XGetPixel(image_dest, x + xoff, y + yoff)){
1992                         XPutPixel(image_scr, x, y, fgpixel);
1993                     }
1994                 }
1995             }
1996             /* copy the rotated image to the drawable d */
1997             XPutImage(dpy, d, gc, image_scr, 0, 0, xscr, yscr, scr_width, scr_height);
1998
1999             XDestroyImage(image_scr);
2000         }
2001         XSetErrorHandler(prevErrorHandler);
2002     } /* End slow rotatation code */
2003
2004     /* free resources */
2005     XFreePixmap(dpy, pixmap_src);
2006     XDestroyImage(image_src);
2007     XDestroyImage(image_dest);
2008
2009     if (s) {
2010         /* restore original state of gc */
2011         gcValues.function = gcCurrentFunction;
2012         gcValues.background = bgpixel;
2013         XChangeGC(dpy, gc, gcFunctionMask|GCBackground, &gcValues);
2014     }
2015 }
2016
2017 /*
2018  *   exec_cmd - execute drawing command from inboard driver
2019  */
2020 static void
2021 exec_cmd(plot_struct *plot, char *command)
2022 {
2023     int x, y, sw, sl, sj;
2024     char *buffer, *str;
2025
2026     buffer = command;
2027     /* binary can't be printed as string */
2028 #if defined(WITH_IMAGE) && defined(BINARY_X11_POLYGON)
2029     if (*buffer == X11_GR_IMAGE || *buffer == X11_GR_FILLED_POLYGON || *buffer == X11_GR_SET_COLOR)
2030 #else
2031 #ifdef WITH_IMAGE
2032     if (*buffer == X11_GR_IMAGE)
2033 #endif
2034 #ifdef BINARY_X11_POLYGON
2035     if (*buffer == X11_GR_FILLED_POLYGON && *buffer == X11_GR_SET_COLOR)
2036 #endif
2037 #endif
2038 #if defined(WITH_IMAGE) || defined(BINARY_X11_POLYGON)
2039     {FPRINTF((stderr, "(display) buffer = |%c|\n", *buffer));}
2040     else
2041 #endif
2042     {FPRINTF((stderr, "(display) buffer = |%s|\n", buffer));}
2043
2044 #ifdef X11_POLYLINE
2045     /*   X11_vector(x, y) - draw vector  */
2046     if (*buffer == 'V') {
2047         sscanf(buffer, "V%4d%4d", &x, &y);
2048         if (polyline_size == 0) {
2049             polyline[polyline_size].x = X(cx);
2050             polyline[polyline_size].y = Y(cy);
2051         }
2052         if (++polyline_size >= polyline_space) {
2053             polyline_space += 100;
2054             polyline = realloc(polyline, polyline_space * sizeof(XPoint));
2055             if (!polyline) fprintf(stderr, "Panic: cannot realloc polyline\n");
2056         }
2057         polyline[polyline_size].x = X(x);
2058         polyline[polyline_size].y = Y(y);
2059         cx = x;
2060         cy = y;
2061         /* Limit the number of vertices in any single polyline */
2062         if (polyline_size > max_request_size) {
2063             FPRINTF((stderr, "(display) dumping polyline size %d\n", polyline_size));
2064             XDrawLines(dpy, plot->pixmap, *current_gc,
2065                         polyline, polyline_size+1, CoordModeOrigin);
2066             polyline_size = 0;
2067         }
2068         return;
2069     } else if (polyline_size > 0) {
2070         FPRINTF((stderr, "(display) dumping polyline size %d\n", polyline_size));
2071         XDrawLines(dpy, plot->pixmap, *current_gc,
2072                         polyline, polyline_size+1, CoordModeOrigin);
2073         polyline_size = 0;
2074     }
2075 #else
2076     /*   X11_vector(x, y) - draw vector  */
2077     if (*buffer == 'V') {
2078         sscanf(buffer, "V%4d%4d", &x, &y);
2079         XDrawLine(dpy, plot->pixmap, *current_gc, X(cx), Y(cy), X(x), Y(y));
2080         cx = x;
2081         cy = y;
2082     } else
2083 #endif
2084     /*   X11_move(x, y) - move  */
2085     if (*buffer == 'M')
2086         sscanf(buffer, "M%4d%4d", &cx, &cy);
2087
2088     /* change default font (QD) encoding (QE) or current font (QF)  */
2089     else if (*buffer == 'Q') {
2090         char *c;
2091         switch (buffer[1]) {
2092         case 'F':
2093                 /* Strip out just the font name */
2094                 c = &(buffer[strlen(buffer)-1]);
2095                 while (*c <= ' ') *c-- = '\0';
2096                 pr_font(&buffer[2]);
2097                 if (font)
2098                   gpXSetFont(dpy, gc, font->fid);
2099                 break;
2100         case 'E':
2101                 /* Save the requested font encoding */
2102                 {
2103                     int tmp;
2104                     sscanf(buffer, "QE%d", &tmp);
2105                     encoding = (enum set_encoding_id)tmp;
2106                 }
2107                 FPRINTF((stderr, "gnuplot_x11: changing encoding to %d\n", encoding));
2108                 break;
2109         case 'D':
2110                 /* Save the request default font */
2111                 c = &(buffer[strlen(buffer)-1]);
2112                 while (*c <= ' ') *c-- = '\0';
2113                 strncpy(default_font, &buffer[2], strlen(&buffer[2])+1);
2114                 FPRINTF((stderr, "gnuplot_x11: exec_cmd() set default_font to \"%s\"\n", default_font));
2115                 break;
2116         }
2117     }
2118
2119     /*   X11_put_text(x, y, str) - draw text   */
2120     else if (*buffer == 'T') {
2121         /* Enhanced text mode added November 2003 - Ethan A Merritt */
2122         int x_offset=0, y_offset=0, v_offset=0;
2123
2124         switch (buffer[1]) {
2125
2126         case 'j':       /* Set start for right-justified enhanced text */
2127                     sscanf(buffer+2, "%4d%4d", &x_offset, &y_offset);
2128                     plot->xLast = x_offset - (plot->xLast - x_offset);
2129                     plot->yLast = y_offset - (vchar/3) / yscale;
2130                     return;
2131         case 'k':       /* Set start for center-justified enhanced text */
2132                     sscanf(buffer+2, "%4d%4d", &x_offset, &y_offset);
2133                     plot->xLast = x_offset - 0.5*(plot->xLast - x_offset);
2134                     plot->yLast = y_offset - (vchar/3) / yscale;
2135                     return;
2136         case 'l':       /* Set start for left-justified enhanced text */
2137                     sscanf(buffer+2, "%4d%4d", &x_offset, &y_offset);
2138                     plot->xLast = x_offset;
2139                     plot->yLast = y_offset - (vchar/3) / yscale;
2140                     return;
2141         case 'o':       /* Enhanced mode print with no update */
2142         case 'c':       /* Enhanced mode print with update to center */
2143         case 'u':       /* Enhanced mode print with update */
2144         case 's':       /* Enhanced mode update with no print */
2145                     sscanf(buffer+2, "%4d%4d", &x_offset, &y_offset);
2146                     /* EAM FIXME - This code has only been tested for x_offset == 0 */
2147                     if (plot->angle != 0) {
2148                         int xtmp=0, ytmp=0;
2149                         xtmp += x_offset * cos((double)(plot->angle) * 0.01745);
2150                         xtmp -= y_offset * sin((double)(plot->angle) * 0.01745) * yscale/xscale;
2151                         ytmp += x_offset * sin((double)(plot->angle) * 0.01745) * xscale/yscale;
2152                         ytmp += y_offset * cos((double)(plot->angle) * 0.01745);
2153                         x_offset = xtmp;
2154                         y_offset = ytmp;
2155                     }
2156                     x = plot->xLast + x_offset;
2157                     y = plot->yLast + y_offset;
2158                     v_offset = 0;
2159                     str = buffer + 10;
2160                     break;
2161         case 'p':       /* Push (Save) position for later use */
2162                     plot->xSave = plot->xLast;
2163                     plot->ySave = plot->yLast;
2164                     return;
2165         case 'r':       /* Pop (Restore) saved position */
2166                     plot->xLast = plot->xSave;
2167                     plot->yLast = plot->ySave;
2168                     return;
2169         default:
2170                     sscanf(buffer, "T%4d%4d", &x, &y);
2171                     v_offset = vchar/3;         /* Why is this??? */
2172                     str = buffer + 9;
2173                     break;
2174         }
2175
2176         sl = strlen(str) - 1;
2177         sw = gpXTextWidth(font, str, sl);
2178
2179 /*      EAM - May 2002  Modify to allow colored text.
2180  *      1) do not force foreground of gc to be black
2181  *      2) write text to (*current_gc), rather than to gc, so that text color can be set
2182  *         using pm3d mappings.
2183  */
2184
2185         switch (plot->jmode) {
2186             default:
2187             case LEFT:
2188                 sj = 0;
2189                 break;
2190             case CENTRE:
2191                 sj = -sw / 2;
2192                 break;
2193             case RIGHT:
2194                 sj = -sw;
2195                 break;
2196         }
2197
2198         if (sl == 0) /* Pointless to draw empty string */
2199             ;
2200         else if (buffer[1] == 's') /* Enhanced text mode reserve space only */
2201             ;
2202         else if (plot->angle != 0) {
2203             /* rotated text */
2204             DrawRotated(plot, dpy, *current_gc, X(x), Y(y), str, sl);
2205         } else {
2206             /* horizontal text */
2207             gpXDrawString(dpy, plot->pixmap, *current_gc,
2208                     X(x) + sj, Y(y) + v_offset, str, sl);
2209         }
2210
2211         /* Update current text position */
2212         if (buffer[1] == 'c') {
2213             plot->xLast = RevX(X(x) + sj + sw/2) - x_offset;
2214             plot->yLast = y - y_offset;
2215         } else if (buffer[1] != 'o') {
2216             plot->xLast = RevX(X(x) + sj + sw) - x_offset;
2217             plot->yLast = y - y_offset;
2218             if (plot->angle != 0) { /* This correction is not perfect */
2219                 plot->yLast += RevX(sw) * sin((plot->angle) * 0.01745) * xscale/yscale;
2220                 plot->xLast -= RevX(sw) * (1.0 - cos((plot->angle) * 0.01745));
2221             }
2222         }
2223
2224     } else if (*buffer == 'F') {        /* fill box */
2225         int style, xtmp, ytmp, w, h;
2226
2227         if (sscanf(buffer + 1, "%4d%4d%4d%4d%4d", &style, &xtmp, &ytmp, &w, &h) == 5) {
2228             x11_setfill(&gc, style, FALSE);
2229
2230             /* gnuplot has origin at bottom left, but X uses top left
2231              * There may be an off-by-one (or more) error here.
2232              */
2233             ytmp += h;          /* top left corner of rectangle to be filled */
2234             w *= xscale;
2235             h *= yscale;
2236             XFillRectangle(dpy, plot->pixmap, gc, X(xtmp), Y(ytmp), w + 1, h + 1);
2237             /* reset everything */
2238             XSetForeground(dpy, gc, plot->cmap->colors[plot->lt + 3]);
2239             XSetFillStyle(dpy, gc, FillSolid);
2240         }
2241     }
2242     /*   X11_justify_text(mode) - set text justification mode  */
2243     else if (*buffer == 'J')
2244         sscanf(buffer, "J%4d", (int *) &plot->jmode);
2245
2246     else if (*buffer == 'A')
2247         sscanf(buffer + 1, "%lf", &plot->angle);
2248
2249     /*  X11_linewidth(plot->lwidth) - set line width */
2250     else if (*buffer == 'W')
2251         sscanf(buffer + 1, "%4d", &plot->user_width);
2252
2253     /*   X11_linetype(plot->type) - set line type  */
2254     else if (*buffer == 'L') {
2255         sscanf(buffer, "L%4d", &plot->lt);
2256         plot->lt = (plot->lt % 8) + 2;
2257
2258         if (plot->lt < 0) /* LT_NODRAW, LT_BACKGROUND, LT_UNDEFINED */
2259             plot->lt = -3;
2260
2261         else { /* Fixme: no mechanism to hold width or dashstyle for LT_BACKGROUND */
2262             /* default width is 0 {which X treats as 1} */
2263             plot->lwidth = widths[plot->lt] ? plot->user_width * widths[plot->lt] : plot->user_width;
2264
2265             if ((dashedlines && dashes[plot->lt][0])
2266             ||  (plot->lt == LT_AXIS+2 && dashes[LT_AXIS+2][0])) {
2267                 plot->type = LineOnOffDash;
2268                 XSetDashes(dpy, gc, 0, dashes[plot->lt], strlen(dashes[plot->lt]));
2269             } else {
2270                 plot->type = LineSolid;
2271             }
2272         }
2273
2274         XSetForeground(dpy, gc, plot->cmap->colors[plot->lt + 3]);
2275         XSetLineAttributes(dpy, gc, plot->lwidth, plot->type, CapButt, JoinBevel);
2276         plot->current_rgb = plot->cmap->rgbcolors[plot->lt + 3];
2277         current_gc = &gc;
2278     }
2279     /*   X11_point(number) - draw a point */
2280     else if (*buffer == 'P') {
2281         int point;
2282         sscanf(buffer + 1, "%d %d %d", &point, &x, &y);
2283         if (point == -2) {
2284             /* set point size */
2285             plot->px = (int) (x * xscale * pointsize);
2286             plot->py = (int) (y * yscale * pointsize);
2287         } else if (point == -1) {
2288             /* dot */
2289             XDrawPoint(dpy, plot->pixmap, *current_gc, X(x), Y(y));
2290         } else {
2291             unsigned char fill = 0;
2292             unsigned char upside_down_fill = 0;
2293             short upside_down_sign = 1;
2294             int delta = (plot->px + plot->py + 1)/2;
2295
2296             /* Force line type to solid, with round ends */
2297             XSetLineAttributes(dpy, *current_gc, plot->lwidth, LineSolid, CapRound, JoinRound);
2298
2299             switch (point % 13) {
2300             case 0:             /* do plus */
2301                 Plus[0].x1 = (short) X(x) - delta;
2302                 Plus[0].y1 = (short) Y(y);
2303                 Plus[0].x2 = (short) X(x) + delta;
2304                 Plus[0].y2 = (short) Y(y);
2305                 Plus[1].x1 = (short) X(x);
2306                 Plus[1].y1 = (short) Y(y) - delta;
2307                 Plus[1].x2 = (short) X(x);
2308                 Plus[1].y2 = (short) Y(y) + delta;
2309
2310                 XDrawSegments(dpy, plot->pixmap, *current_gc, Plus, 2);
2311                 break;
2312             case 1:             /* do X */
2313                 Cross[0].x1 = (short) X(x) - delta;
2314                 Cross[0].y1 = (short) Y(y) - delta;
2315                 Cross[0].x2 = (short) X(x) + delta;
2316                 Cross[0].y2 = (short) Y(y) + delta;
2317                 Cross[1].x1 = (short) X(x) - delta;
2318                 Cross[1].y1 = (short) Y(y) + delta;
2319                 Cross[1].x2 = (short) X(x) + delta;
2320                 Cross[1].y2 = (short) Y(y) - delta;
2321
2322                 XDrawSegments(dpy, plot->pixmap, *current_gc, Cross, 2);
2323                 break;
2324             case 2:             /* do star */
2325                 Star[0].x1 = (short) X(x) - delta;
2326                 Star[0].y1 = (short) Y(y);
2327                 Star[0].x2 = (short) X(x) + delta;
2328                 Star[0].y2 = (short) Y(y);
2329                 Star[1].x1 = (short) X(x);
2330                 Star[1].y1 = (short) Y(y) - delta;
2331                 Star[1].x2 = (short) X(x);
2332                 Star[1].y2 = (short) Y(y) + delta;
2333                 Star[2].x1 = (short) X(x) - delta;
2334                 Star[2].y1 = (short) Y(y) - delta;
2335                 Star[2].x2 = (short) X(x) + delta;
2336                 Star[2].y2 = (short) Y(y) + delta;
2337                 Star[3].x1 = (short) X(x) - delta;
2338                 Star[3].y1 = (short) Y(y) + delta;
2339                 Star[3].x2 = (short) X(x) + delta;
2340                 Star[3].y2 = (short) Y(y) - delta;
2341
2342                 XDrawSegments(dpy, plot->pixmap, *current_gc, Star, 4);
2343                 break;
2344             case 3:             /* do box */
2345                 XDrawRectangle(dpy, plot->pixmap, *current_gc, X(x) - delta, Y(y) - delta,
2346                         (delta + delta), (delta + delta));
2347                 XDrawPoint(dpy, plot->pixmap, *current_gc, X(x), Y(y));
2348                 break;
2349             case 4:             /* filled box */
2350                 XFillRectangle(dpy, plot->pixmap, *current_gc, X(x) - delta, Y(y) - delta,
2351                         (delta + delta), (delta + delta));
2352                 break;
2353             case 5:             /* circle */
2354                 XDrawArc(dpy, plot->pixmap, *current_gc, X(x) - delta, Y(y) - delta,
2355                         2 * delta, 2 * delta, 0, 23040 /* 360 * 64 */);
2356                 XDrawPoint(dpy, plot->pixmap, *current_gc, X(x), Y(y));
2357                 break;
2358             case 6:             /* filled circle */
2359                 XFillArc(dpy, plot->pixmap, *current_gc, X(x) - delta, Y(y) - delta,
2360                         2 * delta, 2 * delta, 0, 23040 /* 360 * 64 */);
2361                 break;
2362             case 10:            /* filled upside-down triangle */
2363                 upside_down_fill = 1;
2364                 /* FALLTHRU */
2365             case 9:             /* do upside-down triangle */
2366                 upside_down_sign = (short)-1;
2367             case 8:             /* filled triangle */
2368                 fill = 1;
2369                 /* FALLTHRU */
2370             case 7:             /* do triangle */
2371                 {
2372                     short temp_x, temp_y;
2373
2374                     temp_x = (short) (1.33 * (double) delta + 0.5);
2375                     temp_y = (short) (1.33 * (double) delta + 0.5);
2376
2377                     Triangle[0].x = (short) X(x);
2378                     Triangle[0].y = (short) Y(y) - upside_down_sign * temp_y;
2379                     Triangle[1].x = (short) temp_x;
2380                     Triangle[1].y = (short) upside_down_sign * 2 * delta;
2381                     Triangle[2].x = (short) -(2 * temp_x);
2382                     Triangle[2].y = (short) 0;
2383                     Triangle[3].x = (short) temp_x;
2384                     Triangle[3].y = (short) -(upside_down_sign * 2 * delta);
2385
2386                     if ((upside_down_sign == 1 && fill) || upside_down_fill) {
2387                         XFillPolygon(dpy, plot->pixmap, *current_gc,
2388                                 Triangle, 4, Convex, CoordModePrevious);
2389                     } else {
2390                         XDrawLines(dpy, plot->pixmap, *current_gc, Triangle, 4, CoordModePrevious);
2391                         XDrawPoint(dpy, plot->pixmap, *current_gc, X(x), Y(y));
2392                     }
2393                 }
2394                 break;
2395             case 12:            /* filled diamond */
2396                 fill = 1;
2397                 /* FALLTHRU */
2398             case 11:            /* do diamond */
2399                 Diamond[0].x = (short) X(x) - delta;
2400                 Diamond[0].y = (short) Y(y);
2401                 Diamond[1].x = (short) delta;
2402                 Diamond[1].y = (short) -delta;
2403                 Diamond[2].x = (short) delta;
2404                 Diamond[2].y = (short) delta;
2405                 Diamond[3].x = (short) -delta;
2406                 Diamond[3].y = (short) delta;
2407                 Diamond[4].x = (short) -delta;
2408                 Diamond[4].y = (short) -delta;
2409
2410                 /*
2411                  * Should really do a check with XMaxRequestSize()
2412                  */
2413
2414                 if (fill) {
2415                     XFillPolygon(dpy, plot->pixmap, *current_gc,
2416                             Diamond, 5, Convex, CoordModePrevious);
2417                 } else {
2418                     XDrawLines(dpy, plot->pixmap, *current_gc, Diamond, 5, CoordModePrevious);
2419                     XDrawPoint(dpy, plot->pixmap, *current_gc, X(x), Y(y));
2420                 }
2421                 break;
2422             }
2423
2424             /* Restore original line style */
2425             XSetLineAttributes(dpy, *current_gc, plot->lwidth, plot->type, CapButt, JoinBevel);
2426         }
2427     }
2428     else if (*buffer == X11_GR_SET_LINECOLOR) {
2429             int lt;
2430             sscanf(buffer + 1, "%4d", &lt);
2431             lt = (lt % 8) + 2;
2432             if (lt < 0) /* LT_NODRAW, LT_BACKGROUND, LT_UNDEFINED */
2433                 lt = -3;
2434             XSetForeground(dpy, gc, plot->cmap->colors[lt + 3]);
2435             plot->current_rgb = plot->cmap->rgbcolors[lt + 3];
2436             current_gc = &gc;
2437     } else if (*buffer == X11_GR_SET_RGBCOLOR) {
2438             int rgb255color;
2439             XColor xcolor;
2440             sscanf(buffer + 1, "%x", &rgb255color);
2441             xcolor.red = (double)(0xffff) * (double)((rgb255color >> 16) & 0xff) /255.;
2442             xcolor.green = (double)(0xffff) * (double)((rgb255color >> 8) & 0xff) /255.;
2443             xcolor.blue = (double)(0xffff) * (double)(rgb255color & 0xff) /255.;
2444             FPRINTF((stderr, "gplt_x11: got request for color %d %d %d\n",
2445                     xcolor.red, xcolor.green, xcolor.blue));
2446             if (XAllocColor(dpy, plot->cmap->colormap, &xcolor)) {
2447                 XSetForeground(dpy, gc, xcolor.pixel);
2448                 plot->current_rgb = rgb255color;
2449             } else {
2450                 FPRINTF((stderr, "          failed to allocate color\n"));
2451             }
2452             current_gc = &gc;
2453     } else if (*buffer == X11_GR_SET_COLOR) {   /* set color */
2454         if (have_pm3d) {        /* ignore, if your X server is not supported */
2455 #ifndef BINARY_X11_POLYGON
2456             double gray;
2457             sscanf(buffer + 1, "%lf", &gray);
2458             PaletteSetColor(plot, gray);
2459             current_gc = &gc;
2460 #else
2461             /* This command will fit within a single buffer so it doesn't
2462              * need to be so elaborate.
2463              */
2464             unsigned char *iptr;
2465             float gray;
2466             unsigned i_remaining;
2467             char *bptr;
2468             TBOOLEAN code_detected = 0;
2469
2470             iptr = (unsigned char *) &gray;
2471             i_remaining = sizeof(gray);
2472
2473             /* Decode and reconstruct the data. */
2474             for (bptr = buffer + 1; i_remaining; ) {
2475               unsigned char uctmp = *bptr++;
2476               if (code_detected) {
2477                 code_detected = 0;
2478                 *iptr++ = uctmp - 1 + SET_COLOR_TRANSLATION_CHAR;
2479                 i_remaining--;
2480               }
2481               else {
2482                 if ( uctmp == SET_COLOR_CODE_CHAR ) {
2483                   code_detected = 1;
2484                 }
2485                 else {
2486                   *iptr++ = uctmp + SET_COLOR_TRANSLATION_CHAR;
2487                   i_remaining--;
2488                 }
2489               }
2490             }
2491
2492             if (swap_endian) {
2493               byteswap((char *)&gray, sizeof(gray));
2494             }
2495
2496             PaletteSetColor(plot, (double)gray);
2497             current_gc = &gc;
2498 #endif
2499         }
2500     } else if (*buffer == X11_GR_FILLED_POLYGON) {      /* filled polygon */
2501         if (have_pm3d) {        /* ignore, if your X server is not supported */
2502
2503 #ifndef BINARY_X11_POLYGON
2504
2505             static XPoint *points = NULL;
2506             static int st_npoints = 0;
2507             static int saved_npoints = -1, saved_i = -1;        /* HBB 20010919 */
2508             int i, npoints, style;
2509             char *ptr = buffer + 1;
2510
2511             sscanf(ptr, "%4d", &npoints);
2512
2513             if (npoints > 0) {
2514                 ptr += 4;
2515                 sscanf(ptr, "%4d", &style);
2516             }
2517
2518             /* HBB 20010919: Implement buffer overflow protection by
2519              * breaking up long lines */
2520             if (npoints == -1) {
2521                 /* This is a continuation line. */
2522                 if (saved_npoints < 100) {
2523                     fprintf(stderr, "gnuplot_x11: filled_polygon() protocol error\n");
2524                     EXIT(1);
2525                 }
2526                 /* Continue filling at end of previous list: */
2527                 i = saved_i;
2528                 npoints = saved_npoints;
2529             } else {
2530                 saved_npoints = npoints;
2531                 i = 0;
2532             }
2533
2534             ptr += 4;
2535             if (npoints > st_npoints) {
2536                 XPoint *new_points = realloc(points, sizeof(XPoint) * npoints);
2537                 st_npoints = npoints;
2538                 if (!new_points) {
2539                     perror("gnuplot_x11: exec_cmd()->points");
2540                     EXIT(1);
2541                 }
2542                 points = new_points;
2543             }
2544
2545             while (*ptr != 'x' && i < npoints) {        /* not end-of-line marker */
2546                 sscanf(ptr, "%4d%4d", &x, &y);
2547                 ptr += 8;
2548                 points[i].x = X(x);
2549                 points[i].y = Y(y);
2550                 i++;
2551             }
2552
2553             if (i >= npoints) {
2554                 /* only do the call if list is complete by now */
2555                 int fillpar, idx;
2556                 XColor xcolor, bgnd;
2557                 float dim;
2558
2559 #else /* BINARY_X11_POLYGON */
2560
2561             static TBOOLEAN transferring = 0;
2562             static unsigned char *iptr;
2563             static int int_cache[2];
2564 #define style int_cache[1]
2565 #define npoints int_cache[0]
2566             static unsigned i_remaining;
2567             unsigned short i_buffer;
2568             char *bptr;
2569             static TBOOLEAN code_detected = 0;
2570             static XPoint *points = NULL;
2571             static int st_npoints = 0;
2572
2573             /* The first value read will be the number of points or the number of
2574              * points followed by style.  Set up parameters to point to npoints.
2575              */
2576             if (!transferring) {
2577                 iptr = (unsigned char *) &npoints;
2578                 i_remaining = sizeof(int_cache);
2579             }
2580
2581             i_buffer = BINARY_MAX_CHAR_PER_TRANSFER;
2582
2583             /* Decode and reconstruct the data. */
2584             for (bptr = &buffer[1]; i_buffer && i_remaining; i_buffer--) {
2585
2586                 unsigned char uctmp = *bptr++;
2587
2588                 if (code_detected) {
2589                     code_detected = 0;
2590                     *iptr++ = uctmp - 1 + FILLED_POLYGON_TRANSLATION_CHAR;
2591                     i_remaining--;
2592                 } else {
2593                     if ( uctmp == FILLED_POLYGON_CODE_CHAR ) {
2594                         code_detected = 1;
2595                     } else {
2596                         *iptr++ = uctmp + FILLED_POLYGON_TRANSLATION_CHAR;
2597                         i_remaining--;
2598                     }
2599                 }
2600
2601                 if(!i_remaining && !transferring) {
2602                     /* The number of points was just read.  Now set up points array and continue. */
2603                     if (swap_endian) {
2604                         byteswap((char *)&npoints, sizeof(npoints));
2605                         byteswap((char *)&style, sizeof(style));
2606                     }
2607                     if (npoints > st_npoints) {
2608                         XPoint *new_points = realloc(points, npoints*2*sizeof(int));
2609                         st_npoints = npoints;
2610                         if (!new_points) {
2611                             perror("gnuplot_x11: exec_cmd()->points");
2612                             EXIT(1);
2613                         }
2614                         points = new_points;
2615                     }
2616                     i_remaining = npoints*2*sizeof(int);
2617                     iptr = (unsigned char *) points;
2618                     transferring = 1;
2619                 }
2620
2621             }
2622
2623             if (!i_remaining) {
2624
2625                 int i;
2626                 transferring = 0;
2627
2628                 /* If the byte order needs to be swapped, do so. */
2629                 if (swap_endian) {
2630                     i = 2*npoints;
2631                     for (i--; i >= 0; i--) {
2632                         byteswap((char *)&((int *)points)[i], sizeof(int));
2633                     }
2634                 }
2635
2636                 /* Convert the ints to XPoint format. This looks like it tramples
2637                  * on itself, but the XPoint x and y are smaller than an int.
2638                  */
2639                 for (i=0; i < npoints; i++) {
2640                     points[i].x = X( ((int *)points)[2*i] );
2641                     points[i].y = Y( ((int *)points)[2*i+1] );
2642                 }
2643
2644 #endif /* BINARY_X11_POLYGON */
2645
2646                 /* Load selected pattern or fill into a separate gc */
2647                 if (!fill_gc)
2648                     fill_gc = XCreateGC(dpy, plot->window, 0, 0);
2649                 XCopyGC(dpy, *current_gc, ~0, fill_gc);
2650
2651                 x11_setfill(&fill_gc, style, TRUE);
2652
2653                 XFillPolygon(dpy, plot->pixmap, fill_gc, points, npoints,
2654                              Nonconvex, CoordModeOrigin);
2655
2656 #ifndef BINARY_X11_POLYGON
2657
2658                 /* Flag this continuation line as closed */
2659                 saved_npoints = saved_i = -1;
2660             } else {
2661                 /* Store how far we got: */
2662                 saved_i = i;
2663             }
2664
2665 #else /* BINARY_X11_POLYGON */
2666
2667             }
2668 #undef style
2669 #undef npoints
2670
2671 #endif /* BINARY_X11_POLYGON */
2672
2673         }
2674
2675     }
2676 #ifdef WITH_IMAGE
2677     else if (*buffer == X11_GR_IMAGE) { /* image */
2678
2679         static unsigned char *iptr;
2680         static TBOOLEAN transferring = 0;
2681         static unsigned short *image;
2682         static int M, N;
2683         static int pixel_1_1_x, pixel_1_1_y, pixel_M_N_x, pixel_M_N_y;
2684         static int visual_1_1_x, visual_1_1_y, visual_M_N_x, visual_M_N_y;
2685         static int color_mode;
2686         static unsigned i_remaining;
2687
2688         /* ignore, if your X server is not supported */
2689         if (!have_pm3d)
2690             return;
2691
2692 /* These might work better as fuctions, but defines will do for now. */
2693 #define ERROR_NOTICE(str)         "\nGNUPLOT (gplt_x11):  " str
2694 #define ERROR_NOTICE_NEWLINE(str) "\n                     " str
2695
2696         if (!transferring) {
2697
2698             /* Get variables. */
2699             if (11 != sscanf( &buffer[1], "%x %x %x %x %x %x %x %x %x %x %x", &M, &N, &pixel_1_1_x,
2700                               &pixel_1_1_y, &pixel_M_N_x, &pixel_M_N_y, &visual_1_1_x, &visual_1_1_y,
2701                               &visual_M_N_x, &visual_M_N_y, &color_mode)) {
2702                 fprintf(stderr, ERROR_NOTICE("Couldn't read image parameters correctly.\n\n"));
2703             } else {
2704
2705                 /* Number of symbols depends upon whether it is color or palette lookup. */
2706                 i_remaining = M*N*sizeof(image[0]);
2707                 if (color_mode == IC_RGB) i_remaining *= 3;
2708
2709                 if (!i_remaining) {
2710                     fprintf(stderr, ERROR_NOTICE("Image of size zero.\n\n"));
2711                 } else {
2712                     image = (unsigned short *) malloc(i_remaining);
2713                     if (image) {
2714                         iptr = (unsigned char *) image;
2715                         transferring = 1;
2716                     } else {
2717                         fprintf(stderr, ERROR_NOTICE("Cannot allocate memory for image.\n\n"));
2718                     }
2719                 }
2720             }
2721
2722         } else {
2723
2724             unsigned short i_buffer;
2725             char *bptr;
2726             static TBOOLEAN code_detected = 0;
2727
2728             i_buffer = BINARY_MAX_CHAR_PER_TRANSFER;
2729
2730             /* Decode and reconstruct the data. */
2731             for (bptr = &buffer[1]; i_buffer && i_remaining; i_buffer--) {
2732                 unsigned char uctmp = *bptr++;
2733                 if (code_detected) {
2734                     code_detected = 0;
2735                     *iptr++ = uctmp - 1 + IMAGE_TRANSLATION_CHAR;
2736                     i_remaining--;
2737                 } else {
2738                     if (uctmp == IMAGE_CODE_CHAR) {
2739                         code_detected = 1;
2740                     } else {
2741                         *iptr++ = uctmp + IMAGE_TRANSLATION_CHAR;
2742                         i_remaining--;
2743                     }
2744                 }
2745             }
2746
2747             if (!i_remaining) {
2748
2749                 /* Expand or contract the image into a new image and place this on the screen. */
2750
2751                 static unsigned short R_msb_mask=0, R_rshift, R_lshift;
2752                 static unsigned short G_msb_mask, G_rshift, G_lshift;
2753                 static unsigned short B_msb_mask, B_rshift, B_lshift;
2754                 static unsigned long prev_red_mask, prev_green_mask, prev_blue_mask;
2755 #define LET_XPUTPIXEL_SWAP_BYTES 1
2756 #if !LET_XPUTPIXEL_SWAP_BYTES
2757                 static TBOOLEAN swap_image_bytes = FALSE;
2758 #endif
2759                 TBOOLEAN create_image = FALSE;
2760 #define SINGLE_PALETTE_BIT_SHIFT 0  /* Discard over time, 16aug2004 */
2761 #if SINGLE_PALETTE_BIT_SHIFT
2762                 static int prev_allocated=0;
2763                 static short palette_bit_shift=0;
2764 #endif
2765
2766                 transferring = 0;
2767
2768                 /* If the byte order needs to be swapped, do so. */
2769                 if (swap_endian) {
2770                     int i = M*N;
2771                     if (color_mode == IC_RGB) {i *= 3;}
2772                     for (i--; i >= 0; i--) {
2773                         /* The assumption is that image data through the pipe is 16 bits. */
2774                         byteswap2(&image[i]);
2775                     }
2776                 }
2777
2778                 if (color_mode == IC_PALETTE) {
2779
2780 #if SINGLE_PALETTE_BIT_SHIFT
2781 /* If the palette size is 2 to some power then a single bit shift would work.
2782  * The multiply/shift method doesn't seem noticably slower and generally works
2783  * with any size pallete.  Keep this around for the time being in case some
2784  * use for it comes up.
2785  */
2786                     /* Initialize the palette shift, or if the palette size changes then update the shift. */
2787                     if (!palette_bit_shift || (plot->cmap->allocated != prev_allocated)) {
2788                         short i_bits, n_colors;
2789                         prev_allocated = plot->cmap->allocated;
2790                         for (palette_bit_shift = 16, n_colors = 1; palette_bit_shift > 0; palette_bit_shift--) {
2791                             if (n_colors >= plot->cmap->allocated) {break;}
2792                             n_colors <<= 1;
2793                         }
2794                     }
2795 #endif
2796
2797                     create_image = TRUE;
2798                 }
2799                 else {
2800
2801                     switch( vis->class ) {
2802
2803                         static char *display_error_text_after = " display class cannot use component"
2804                             ERROR_NOTICE_NEWLINE("data.  Try palette mode.\n\n");
2805
2806                     case TrueColor:
2807
2808                         /* This algorithm for construction of pixel values from the RGB components
2809                          * doesn't look to be the most portable.  However, it tries to be.  It
2810                          * gets the red/green/blue masks from the X11 information and generates
2811                          * appropriate shifts and masks from that.  The X11 documentation mentions
2812                          * something about using the masks in some odd way to generate an index
2813                          * for a color table.  It all seemed like a bother for no benefit however.
2814                          * Certainly we don't want to have only a few bits of color, and we don't
2815                          * want to have some huge color table.  My feeling on the matter is to
2816                          * just pack bits according to the masks that X11 indicates.  If someone
2817                          * finds they have a peculiar display type that doesn't work, that can be
2818                          * considered when and if it happens.
2819                          */
2820
2821                         /* Get the color mask information and compute the proper bit shifts, but only upon
2822                          * first call or if the masks change.  I'm not sure if it is necessary to check if
2823                          * the masks changed, but I left it in as a safeguard in case there is some strange
2824                          * system out there somewhere.
2825                          */
2826                         if (!R_msb_mask || (vis->red_mask != prev_red_mask) ||
2827                             (vis->green_mask != prev_green_mask) || (vis->blue_mask != prev_blue_mask)) {
2828                             short R_bits, G_bits, B_bits, min_bits;
2829                             prev_red_mask = vis->red_mask;
2830                             prev_green_mask = vis->green_mask;
2831                             prev_blue_mask = vis->blue_mask;
2832                             R_msb_mask = BitMaskDetails(vis->red_mask, &R_rshift, &R_lshift);
2833                             G_msb_mask = BitMaskDetails(vis->green_mask, &G_rshift, &G_lshift);
2834                             B_msb_mask = BitMaskDetails(vis->blue_mask, &B_rshift, &B_lshift);
2835
2836                             /* Graphics info in case strange behavior occurs in a particular system.
2837                              */
2838                             FPRINTF((stderr, "\n\nvis->visualid: 0x%x   vis->class: %d   vis->bits_per_rgb: %d\n",
2839                                 (int)vis->visualid, vis->class, vis->bits_per_rgb));
2840                             FPRINTF((stderr, "vis->red_mask: %lx   vis->green_mask: %lx   vis->blue_mask: %lx\n",
2841                                 vis->red_mask, vis->green_mask, vis->blue_mask));
2842                             FPRINTF((stderr, "ImageByteOrder:  %d\n\n", ImageByteOrder(dpy)));
2843
2844                             R_bits = 32-R_rshift-R_lshift;
2845                             G_bits = 32-G_rshift-G_lshift;
2846                             B_bits = 32-B_rshift-B_lshift;
2847                             min_bits = GPMIN(GPMIN(R_bits, G_bits), B_bits);
2848                             if (R_bits > min_bits) {
2849                                 R_msb_mask >>= (R_bits-min_bits);
2850                                 R_lshift += (R_bits-min_bits);
2851                                 R_bits = min_bits;
2852                             }
2853                             if (G_bits > min_bits) {
2854                                 G_msb_mask >>= (G_bits-min_bits);
2855                                 G_lshift += (G_bits-min_bits);
2856                                 G_bits = min_bits;
2857                             }
2858                             if (B_bits > min_bits) {
2859                                 B_msb_mask >>= (B_bits-min_bits);
2860                                 B_lshift += (B_bits-min_bits);
2861                                 B_bits = min_bits;
2862                             }
2863                             R_rshift = 16-R_bits;
2864                             G_rshift = 16-G_bits;
2865                             B_rshift = 16-B_bits;
2866
2867 #if !LET_XPUTPIXEL_SWAP_BYTES
2868                             /* Now deal with the byte order issue. */
2869                             if (ImageByteOrder(dpy)) {
2870                                 /* If the bit field for each channel is 8 bits, the masks can be rearranged instead
2871                                  * of having to actually perform a byte order swap on the image data.
2872                                  */
2873                                 if ((R_bits == 8) && (G_bits == 8) && (B_bits == 8)) {
2874                                     R_lshift = 24 - R_lshift;
2875                                     G_lshift = 24 - G_lshift;
2876                                     B_lshift = 24 - B_lshift;
2877                                     swap_image_bytes = FALSE;
2878                                 } else {
2879                                     swap_image_bytes = TRUE;
2880                                 }
2881                             } else {
2882                                 swap_image_bytes = FALSE;
2883                             }
2884 #endif
2885                         }
2886
2887                         create_image = TRUE;
2888                         break;
2889
2890                     case PseudoColor:
2891                         fprintf(stderr, ERROR_NOTICE("PseudoColor"));
2892                         fprintf(stderr, display_error_text_after);
2893                         break;
2894
2895                     case GrayScale:
2896                         fprintf(stderr, ERROR_NOTICE("GrayScale"));
2897                         fprintf(stderr, display_error_text_after);
2898                         break;
2899
2900                     case StaticColor:
2901                         fprintf(stderr, ERROR_NOTICE("StaticColor"));
2902                         fprintf(stderr, display_error_text_after);
2903                         break;
2904
2905                     case StaticGray:
2906                         fprintf(stderr, ERROR_NOTICE("StaticGray"));
2907                         fprintf(stderr, display_error_text_after);
2908                         break;
2909
2910                     case DirectColor:
2911                         fprintf(stderr, ERROR_NOTICE("DirectColor display class currently")
2912                                 ERROR_NOTICE_NEWLINE("not supported.\n\n"));
2913                         break;
2914
2915                     default:
2916                         fprintf(stderr, ERROR_NOTICE("Unknown X11 display class.\n\n"));
2917                         break;
2918
2919                     }
2920
2921                 }
2922
2923                 if (create_image) {
2924
2925                     int M_pixel, N_pixel;
2926                     int M_view, N_view;
2927                     int pixel_1_1_x_plot, pixel_1_1_y_plot, pixel_M_N_x_plot, pixel_M_N_y_plot;
2928                     int view_1_1_x_plot, view_1_1_y_plot, view_M_N_x_plot, view_M_N_y_plot;
2929                     int final_1_1_x_plot, final_1_1_y_plot;
2930                     int i_start, j_start;
2931                     int itmp;
2932
2933                     /* Compute the image extent with sanity check. */
2934                     pixel_1_1_x_plot = X(pixel_1_1_x);
2935                     pixel_M_N_x_plot = X(pixel_M_N_x);
2936                     M_pixel = pixel_M_N_x_plot - pixel_1_1_x_plot;
2937                     if (M_pixel < 0) {
2938                         M_pixel = -M_pixel;
2939                         itmp = pixel_1_1_x_plot;
2940                         pixel_1_1_x_plot = pixel_M_N_x_plot;
2941                         pixel_M_N_x_plot = itmp;
2942                     }
2943                     pixel_1_1_y_plot = Y(pixel_1_1_y);
2944                     pixel_M_N_y_plot = Y(pixel_M_N_y);
2945                     N_pixel = pixel_M_N_y_plot - pixel_1_1_y_plot;
2946                     if (N_pixel < 0) {
2947                         N_pixel = -N_pixel;
2948                         itmp = pixel_1_1_y_plot;
2949                         pixel_1_1_y_plot = pixel_M_N_y_plot;
2950                         pixel_M_N_y_plot = itmp;
2951                     }
2952
2953                     /* Compute the visual extent of the plot with sanity check. */
2954                     view_1_1_x_plot = X(visual_1_1_x);
2955                     view_M_N_x_plot = X(visual_M_N_x);
2956                     if (view_M_N_x_plot < view_1_1_x_plot) {
2957                         itmp = view_1_1_x_plot;
2958                         view_1_1_x_plot = view_M_N_x_plot;
2959                         view_M_N_x_plot = itmp;
2960                     }
2961                     view_1_1_y_plot = Y(visual_1_1_y);
2962                     view_M_N_y_plot = Y(visual_M_N_y);
2963                     if (view_M_N_y_plot < view_1_1_y_plot) {
2964                         itmp = view_1_1_y_plot;
2965                         view_1_1_y_plot = view_M_N_y_plot;
2966                         view_M_N_y_plot = itmp;
2967                     }
2968
2969                     /* Determine parameters for the image that will be built and put on screen. */
2970                     itmp = view_1_1_x_plot - pixel_1_1_x_plot;
2971                     if (itmp > 0) {
2972                         i_start = itmp; final_1_1_x_plot = view_1_1_x_plot;
2973                     } else {
2974                         i_start = 0; final_1_1_x_plot = pixel_1_1_x_plot;
2975                     }
2976                     itmp = pixel_M_N_x_plot - view_M_N_x_plot;
2977                     if (itmp > 0) {
2978                         M_view = M_pixel - itmp - i_start;
2979                     } else {
2980                         M_view = M_pixel - i_start;
2981                     }
2982
2983                     itmp = view_1_1_y_plot - pixel_1_1_y_plot;
2984                     if (itmp > 0) {
2985                         j_start = itmp; final_1_1_y_plot = view_1_1_y_plot;
2986                     } else {
2987                         j_start = 0; final_1_1_y_plot = pixel_1_1_y_plot;
2988                     }
2989                     itmp = pixel_M_N_y_plot - view_M_N_y_plot;
2990                     if (itmp > 0) {
2991                         N_view = N_pixel - itmp - j_start;
2992                     } else {
2993                         N_view = N_pixel - j_start;
2994                     }
2995
2996                     if ((M_view > 0) && (N_view > 0)) {
2997
2998                         char *sample_data;
2999                         short sample_data_size;
3000                         int i_view, j_view;
3001
3002                         /* Determine if 2 bytes is sufficient or 4 bytes are necessary for color image data. */
3003                         if (dep > 16) {
3004                             sample_data_size = 4;
3005                         } else {
3006                             sample_data_size = 2;
3007                         }
3008
3009                         /* Expand or compress the original image to the pixels it will occupy on the screen. */
3010                         sample_data = (char *) malloc(M_view*N_view*sample_data_size);
3011
3012                         if (sample_data) {
3013
3014                             XImage *image_dest;
3015
3016                             /* Create an initialized image object. */
3017                             image_dest = XCreateImage(dpy, vis, dep, ZPixmap, 0, sample_data, M_view, N_view,
3018                                                       8*sample_data_size, M_view*sample_data_size);
3019
3020                             /* Fill in the output image data by decimating or repeating the input image data. */
3021                             for (j_view=0; j_view < N_view; j_view++) {
3022                                 int j = ((j_view+j_start) * N) / N_pixel;
3023                                 int row_start = j*M;
3024                                 for (i_view=0; i_view < M_view; i_view++) {
3025                                     int i = ((i_view+i_start) * M) / M_pixel;
3026                                     if (color_mode == IC_PALETTE) {
3027
3028 #if SINGLE_PALETTE_BIT_SHIFT
3029 /* If the palette size is 2 to some power then a single bit shift would work.
3030  * The multiply/shift method doesn't seem noticably slower and generally works
3031  * with any size pallete.  Keep this around for the time being in case some
3032  * use for it comes up.
3033  */
3034                                         unsigned long pixel = plot->cmap->pixels[image[row_start + i]>>palette_bit_shift];
3035 #else
3036                                         /* The methods below avoid using floating point.  The basic idea is to multiply
3037                                          * the palette size by the "normalized" data coming across the pipe.  (I think
3038                                          * some DSP people call this Q1 or Q0 format, or something like that.)  Following
3039                                          * this multiplication by division by 2^16 gives the final answer.  Most moderately
3040                                          * advanced CPUs have bit shifting.  The first method uses this.  However, a bit
3041                                          * shift isn't necessary because we can just take the top word of a two word
3042                                          * number and we've effectively divided by 2^16.  The second method uses this.
3043                                          * However, my guess is that C compilers more effeciently translate the first
3044                                          * method with the bit shift than the second method which pulls out the top word.
3045                                          * (There is one instruction less for the shift version than the word selection
3046                                          * version on gcc for x86.)
3047                                          */
3048 #if 1
3049                                         unsigned long pixel;
3050                                         unsigned short index  = (plot->cmap->allocated * (unsigned long) image[row_start + i]) >> 16;
3051                                         pixel = plot->cmap->pixels[index];
3052 #else
3053                                         unsigned long index;
3054                                         unsigned long pixel;
3055                                         index  = plot->cmap->allocated * (unsigned long) image[row_start + i];
3056                                         pixel = plot->cmap->pixels[((unsigned short *)&index)[1]];
3057 #endif
3058 #endif
3059                                         XPutPixel(image_dest, i_view, j_view, pixel);
3060                                     } else {
3061                                         int index3 = 3*(row_start + i);
3062                                         unsigned long pixel = ((unsigned int)((image[index3++]>>R_rshift)&R_msb_mask)) << R_lshift;
3063                                         pixel |= ((unsigned int)((image[index3++]>>G_rshift)&G_msb_mask)) << G_lshift;
3064                                         pixel |= ((unsigned int)((image[index3]>>B_rshift)&B_msb_mask)) << B_lshift;
3065                                         XPutPixel(image_dest, i_view, j_view, pixel);
3066                                     }
3067                                 }
3068                             }
3069
3070 #if !LET_XPUTPIXEL_SWAP_BYTES
3071                             /* Swap the image byte order if necessary. */
3072                             if (swap_image_bytes) {
3073                                 int i_swap = M_view*N_view - 1;
3074                                 if (sample_data_size == 2) {
3075                                     for (; i_swap >= 0; i_swap--) {
3076                                         byteswap2(&(((unsigned short *)sample_data)[i_swap]));
3077                                     }
3078                                 } else {
3079                                     for (; i_swap >= 0; i_swap--) {
3080                                         byteswap4(&(((unsigned int *)sample_data)[i_swap]));
3081                                     }
3082                                 }
3083                             }
3084 #endif
3085
3086                             /* Copy the image to the drawable d. */
3087                             XPutImage(dpy, plot->pixmap, gc, image_dest, 0, 0,
3088                                       final_1_1_x_plot, final_1_1_y_plot, M_view, N_view);
3089
3090                             /* Free resources. */
3091                             XDestroyImage(image_dest);
3092
3093                             /* XDestroyImage frees the sample_data memory, so no "free" here. */
3094
3095                         } else {
3096                             fprintf(stderr, ERROR_NOTICE("Could not allocate memory for image.\n\n"));
3097                         }
3098                     }
3099
3100                 }
3101
3102                 /* image was not used as part of X resource, so must "free" here. */
3103                 free(image);
3104
3105             }
3106         }
3107
3108     }
3109 #endif /* WITH_IMAGE */
3110 #if defined(USE_MOUSE) && defined(MOUSE_ALL_WINDOWS)
3111     /*   Axis scaling information to save for later mouse clicks */
3112     else if (*buffer == 'S') {
3113         int axis, axis_mask;
3114
3115         sscanf(&buffer[1], "%d %d", &axis, &axis_mask);
3116         if (axis == -2) {
3117             plot->almost2d = axis_mask;
3118         } else if (axis < 0) {
3119             plot->axis_mask = axis_mask;
3120         } else if (axis < 2*SECOND_AXES) {
3121             sscanf(&buffer[1], "%d %lg %d %lg %lg", &axis,
3122                 &(plot->axis_scale[axis].min), &(plot->axis_scale[axis].term_lower),
3123                 &(plot->axis_scale[axis].term_scale), &(plot->axis_scale[axis].logbase));
3124             FPRINTF((stderr, "gnuplot_x11: axis %d scaling %14.3lg %14d %14.3lg %14.3lg\n",
3125                 axis, plot->axis_scale[axis].min, plot->axis_scale[axis].term_lower,
3126                 plot->axis_scale[axis].term_scale, plot->axis_scale[axis].logbase));
3127         }
3128     }
3129 #endif
3130     else {
3131         fprintf(stderr, "gnuplot_x11: unknown command <%s>\n", buffer);
3132     }
3133 }
3134
3135 /*
3136  *   display - display a stored plot
3137  */
3138 static void
3139 display(plot_struct *plot)
3140 {
3141     int n;
3142
3143     FPRINTF((stderr, "Display %d ; %d commands\n", plot->plot_number, plot->ncommands));
3144
3145     if (plot->ncommands == 0)
3146         return;
3147
3148     /* set scaling factor between internal driver & window geometry */
3149     xscale = plot->width / 4096.0;
3150     yscale = GRAPH_HEIGHT(plot) / 4096.0;
3151
3152     /* initial point sizes, until overridden with P7xxxxyyyy */
3153     plot->px = (int) (xscale * pointsize);
3154     plot->py = (int) (yscale * pointsize);
3155
3156     /* create new pixmap & GC */
3157     if (!plot->pixmap) {
3158         FPRINTF((stderr, "Create pixmap %d : %dx%dx%d\n", plot->plot_number, plot->width, PIXMAP_HEIGHT(plot), dep));
3159         plot->pixmap = XCreatePixmap(dpy, root, plot->width, PIXMAP_HEIGHT(plot), dep);
3160     }
3161
3162     if (gc)
3163         XFreeGC(dpy, gc);
3164
3165     gc = XCreateGC(dpy, plot->pixmap, 0, (XGCValues *) 0);
3166
3167     if (font)
3168       gpXSetFont(dpy, gc, font->fid);
3169
3170     XSetFillStyle(dpy, gc, FillSolid);
3171
3172     /* initialize stipple for filled boxes (ULIG) */
3173     if (!stipple_initialized) {
3174         int i;
3175         for (i = 0; i < stipple_pattern_num; i++)
3176             stipple_pattern[i] =
3177                 XCreateBitmapFromData(dpy, plot->pixmap, stipple_pattern_bits[i],
3178                                 stipple_pattern_width, stipple_pattern_height);
3179         stipple_initialized = 1;
3180     }
3181
3182     /* set pixmap background */
3183     XSetForeground(dpy, gc, plot->cmap->colors[0]);
3184     XFillRectangle(dpy, plot->pixmap, gc, 0, 0, plot->width, PIXMAP_HEIGHT(plot) + vchar);
3185     XSetBackground(dpy, gc, plot->cmap->colors[0]);
3186
3187     if (!plot->window)
3188         pr_window(plot);
3189
3190     /* top the window but don't put keyboard or mouse focus into it. */
3191     if (do_raise)
3192         XMapRaised(dpy, plot->window);
3193
3194     /* momentarily clear the window first if requested */
3195     if (Clear) {
3196         XClearWindow(dpy, plot->window);
3197         XFlush(dpy);
3198     }
3199     /* loop over accumulated commands from inboard driver */
3200     for (n = 0; n < plot->ncommands; n++) {
3201         exec_cmd(plot, plot->commands[n]);
3202     }
3203
3204 #ifdef EXPORT_SELECTION
3205     if (exportselection)
3206         export_graph(plot);
3207 #endif
3208
3209     UpdateWindow(plot);
3210 #ifdef USE_MOUSE
3211 #ifdef TITLE_BAR_DRAWING_MSG
3212     if (plot->window) {
3213         /* restore default window title */
3214         char *cp = plot->titlestring;
3215         if (!cp) cp = "";
3216         XStoreName(dpy, plot->window, cp);
3217     }
3218 #else
3219     if (!button_pressed) {
3220         cursor = cursor_save ? cursor_save : cursor_default;
3221         cursor_save = (Cursor)0;
3222         XDefineCursor(dpy, plot->window, cursor);
3223     }
3224 #endif
3225 #endif
3226 }
3227
3228 static void
3229 UpdateWindow(plot_struct * plot)
3230 {
3231 #ifdef USE_MOUSE
3232     XEvent event;
3233 #endif
3234     if (None == plot->window) {
3235         return;
3236     }
3237
3238     if (!plot->pixmap) {
3239         /* create a black background pixmap */
3240         FPRINTF((stderr, "Create pixmap %d : %dx%dx%d\n", plot->plot_number, plot->width, PIXMAP_HEIGHT(plot), dep));
3241         plot->pixmap = XCreatePixmap(dpy, root, plot->width, PIXMAP_HEIGHT(plot), dep);
3242         if (gc)
3243             XFreeGC(dpy, gc);
3244         gc = XCreateGC(dpy, plot->pixmap, 0, (XGCValues *) 0);
3245         if (font)
3246           gpXSetFont(dpy, gc, font->fid);
3247         /* set pixmap background */
3248         XSetForeground(dpy, gc, plot->cmap->colors[0]);
3249         XFillRectangle(dpy, plot->pixmap, gc, 0, 0, plot->width, PIXMAP_HEIGHT(plot) + vchar);
3250         XSetBackground(dpy, gc, plot->cmap->colors[0]);
3251     }
3252     XSetWindowBackgroundPixmap(dpy, plot->window, plot->pixmap);
3253     XClearWindow(dpy, plot->window);
3254
3255 #ifdef USE_MOUSE
3256     EventuallyDrawMouseAddOns(plot);
3257
3258     XFlush(dpy);
3259
3260     /* XXX discard expose events. This is a kludge for
3261      * preventing the event dispatcher calling UpdateWindow()
3262      * and the latter again generating expose events, which
3263      * again would trigger the event dispatcher ... (joze) XXX */
3264     while (XCheckWindowEvent(dpy, plot->window, ExposureMask, &event))
3265         /* EMPTY */ ;
3266 #endif
3267 }
3268
3269
3270 static void
3271 CmapClear(cmap_t * cmap_ptr)
3272 {
3273     cmap_ptr->total = (int) 0;
3274     cmap_ptr->allocated = (int) 0;
3275     cmap_ptr->pixels = (unsigned long *) 0;
3276 }
3277
3278 static void
3279 RecolorWindow(plot_struct * plot)
3280 {
3281     if (None != plot->window) {
3282         XSetWindowColormap(dpy, plot->window, plot->cmap->colormap);
3283         XSetWindowBackground(dpy, plot->window, plot->cmap->colors[0]);
3284         XSetWindowBorder(dpy, plot->window, plot->cmap->colors[1]);
3285 #ifdef USE_MOUSE
3286         if (gc_xor) {
3287             XFreeGC(dpy, gc_xor);
3288             gc_xor = (GC) 0;
3289             GetGCXor(plot, &gc_xor);    /* recreate gc_xor */
3290         }
3291 #endif
3292     }
3293 }
3294
3295 /*
3296  * free all *pm3d* colors (*not* the line colors cmap->colors)
3297  * of a plot_struct's colormap.  This could be either a private
3298  * or the default colormap.  Note, that the line colors are not
3299  * free'd nor even touched.
3300  */
3301 static void
3302 FreeColors(cmap_t *cmp)
3303 {
3304     if (cmp->total && cmp->pixels) {
3305         if (cmp->allocated) {
3306             FPRINTF((stderr, "freeing palette colors\n"));
3307             XFreeColors(dpy, cmp->colormap, cmp->pixels,
3308                         cmp->allocated, 0 /* XXX ??? XXX */ );
3309         }
3310         free(cmp->pixels);
3311     }
3312     CmapClear(cmp);
3313 }
3314
3315 /*
3316  * free pm3d colors and eventually a private colormap.
3317  * set the plot_struct's colormap to the default colormap
3318  * and the line `colors' to the line colors of the default
3319  * colormap.
3320  */
3321 static void
3322 ReleaseColormap(cmap_t *cmp)
3323 {
3324     if (cmp && cmp != current_cmap) {
3325         FreeColors(cmp);
3326         FPRINTF((stderr, "releasing private colormap\n"));
3327         if (cmp->colormap && cmp->colormap != current_cmap->colormap) {
3328             XFreeColormap(dpy, cmp->colormap);
3329         }
3330         free((char *) cmp);
3331     }
3332 }
3333
3334 static unsigned long *
3335 ReallocColors(cmap_t *cmap, int n)
3336 {
3337     FreeColors(cmap);
3338     cmap->total = n;
3339     cmap->pixels = (unsigned long *)
3340         malloc(sizeof(unsigned long) * cmap->total);
3341     return cmap->pixels;
3342 }
3343
3344 /*
3345  * check if the display supports the visual of type `class'.
3346  *
3347  * If multiple visuals of `class' are supported, try to get
3348  * the one with the highest depth.
3349  *
3350  * If visual class and depth are equal to the default visual
3351  * class and depth, the latter is preferred.
3352  *
3353  * modifies: best, depth
3354  *
3355  * returns 1 if a visual which matches the request
3356  * could be found else 0.
3357  */
3358 static int
3359 GetVisual(int class, Visual ** visual, int *depth)
3360 {
3361     XVisualInfo *visualsavailable;
3362     int nvisuals = 0;
3363     long vinfo_mask = VisualClassMask;
3364     XVisualInfo vinfo;
3365
3366     vinfo.class = class;
3367     *depth = 0;
3368     *visual = 0;
3369
3370     visualsavailable = XGetVisualInfo(dpy, vinfo_mask, &vinfo, &nvisuals);
3371
3372     if (visualsavailable && nvisuals > 0) {
3373         int i;
3374         for (i = 0; i < nvisuals; i++) {
3375             if (visualsavailable[i].depth > *depth) {
3376                 *visual = visualsavailable[i].visual;
3377                 *depth = visualsavailable[i].depth;
3378             }
3379         }
3380         XFree(visualsavailable);
3381         if (*visual && (*visual)->class == (DefaultVisual(dpy, scr))->class && *depth == DefaultDepth(dpy, scr)) {
3382             /* prefer the default visual */
3383             *visual = DefaultVisual(dpy, scr);
3384         }
3385     }
3386     return nvisuals > 0;
3387 }
3388
3389 static void
3390 PaletteMake(t_sm_palette * tpal)
3391 {
3392     int max_colors;
3393     int min_colors;
3394     char *save_title = (char *) 0;
3395
3396     /* The information retained in a linked list is the cmap_t structure.
3397      * That colormap structure doesn't contain the palette specifications
3398      * t_sm_palette used to generate the colormap.  Therefore, the making
3399      * of the colormap must be carried all the way through before we can
3400      * determine if it is unique from the other colormaps in the linked list.
3401      *
3402      * Rather than create and initialize a colormap outside of the list, we'll
3403      * just put a new one in the list, and if it isn't unique remove it later.
3404      */
3405
3406     cmap_t *new_cmap = Add_CMap_To_Linked_List();
3407
3408     /* Continue until valid palette is built.  May require multiple passes. */
3409     while (1) {
3410
3411         if (tpal) {
3412
3413             if (tpal->use_maxcolors > 0) {
3414                 max_colors = tpal->use_maxcolors;
3415             } else {
3416                 max_colors = maximal_possible_colors;
3417             }
3418
3419             FPRINTF((stderr, "(PaletteMake) tpal->use_maxcolors = %d\n",
3420                      tpal->use_maxcolors));
3421
3422             /*  free old gradient table  */
3423             if (sm_palette.gradient) {
3424                 free( sm_palette.gradient );
3425                 sm_palette.gradient = NULL;
3426                 sm_palette.gradient_num = 0;
3427             }
3428
3429             sm_palette.colorMode = tpal->colorMode;
3430             sm_palette.positive = tpal->positive;
3431             sm_palette.use_maxcolors = tpal->use_maxcolors;
3432             sm_palette.cmodel = tpal->cmodel;
3433
3434             switch( sm_palette.colorMode ) {
3435             case SMPAL_COLOR_MODE_GRAY:
3436                 sm_palette.gamma = tpal->gamma;
3437                 break;
3438             case SMPAL_COLOR_MODE_RGB:
3439                 sm_palette.formulaR = tpal->formulaR;
3440                 sm_palette.formulaG = tpal->formulaG;
3441                 sm_palette.formulaB = tpal->formulaB;
3442                 break;
3443             case SMPAL_COLOR_MODE_FUNCTIONS:
3444                 fprintf( stderr, "Ooops:  no SMPAL_COLOR_MODE_FUNCTIONS here!\n" );
3445                 break;
3446             case SMPAL_COLOR_MODE_GRADIENT:
3447                 sm_palette.gradient_num = tpal->gradient_num;
3448                 /* Take over the memory from tpal. */
3449                 sm_palette.gradient = tpal->gradient;
3450                 tpal->gradient = NULL;
3451                 break;
3452             default:
3453                 fprintf(stderr,"%s:%d ooops: Unknown color mode '%c'.\n",
3454                         __FILE__, __LINE__, (char)(sm_palette.colorMode) );
3455             }
3456
3457         } else {
3458             max_colors = maximal_possible_colors;
3459             FPRINTF((stderr, "(PaletteMake) tpal=NULL\n"));
3460         }
3461
3462         if (minimal_possible_colors < max_colors)
3463             min_colors = minimal_possible_colors;
3464         else
3465             min_colors = max_colors / (num_colormaps > 1 ? 2 : 8);
3466
3467         if (current_plot) {
3468
3469             if (current_plot->window) {
3470                 char *msg;
3471                 char *added_text = " allocating colors ...";
3472                 int orig_len = (current_plot->titlestring ? strlen(current_plot->titlestring) : 0);
3473                 XFetchName(dpy, current_plot->window, &save_title);
3474                 if ((msg = (char *) malloc(orig_len + strlen(added_text) + 1))) {
3475                     if (current_plot->titlestring)
3476                         strcpy(msg, current_plot->titlestring);
3477                     else
3478                         msg[0] = '\0';
3479                     strcat(msg, added_text);
3480                     XStoreName(dpy, current_plot->window, msg);
3481                     free(msg);
3482                 }
3483             }
3484
3485             if (!num_colormaps) {
3486                 XFree(XListInstalledColormaps(dpy, current_plot->window, &num_colormaps));
3487                 FPRINTF((stderr, "(PaletteMake) num_colormaps = %d\n", num_colormaps));
3488             }
3489
3490         }
3491
3492         /* TODO */
3493         /* EventuallyChangeVisual(plot); */
3494
3495         /*
3496          * start with trying to allocate max_colors. This should
3497          * always succeed with TrueColor visuals >= 16bit. If it
3498          * fails (for example for a PseudoColor visual of depth 8),
3499          * try it with half of the colors. Proceed until min_colors
3500          * is reached. If this fails we should probably install a
3501          * private colormap.
3502          * Note that I make no difference for different
3503          * visual types here. (joze)
3504          */
3505         for ( /* EMPTY */ ; max_colors >= min_colors; max_colors /= 2) {
3506
3507             XColor xcolor;
3508             double fact = 1.0 / (double)(max_colors-1);
3509
3510             if (current_plot)
3511                 ReallocColors(new_cmap, max_colors);
3512
3513             for (new_cmap->allocated = 0; new_cmap->allocated < max_colors; new_cmap->allocated++) {
3514
3515                 double gray = (double) new_cmap->allocated * fact;
3516                 rgb_color color;
3517                 rgb1_from_gray( gray, &color );
3518                 xcolor.red = 0xffff * color.r + 0.5;
3519                 xcolor.green = 0xffff * color.g + 0.5;
3520                 xcolor.blue = 0xffff * color.b + 0.5;
3521
3522                 if (XAllocColor(dpy, new_cmap->colormap, &xcolor)) {
3523                     new_cmap->pixels[new_cmap->allocated] = xcolor.pixel;
3524                 } else {
3525                     FPRINTF((stderr, "failed at color %d\n", new_cmap->allocated));
3526                     break;
3527                 }
3528             }
3529
3530             if (new_cmap->allocated == max_colors) {
3531                 break;          /* success! */
3532             }
3533
3534             /* reduce the number of max_colors to at
3535              * least less than new_cmap->allocated */
3536             while (max_colors > new_cmap->allocated && max_colors >= min_colors) {
3537                 max_colors /= 2;
3538             }
3539         }
3540
3541         FPRINTF((stderr, "(PaletteMake) allocated = %d\n", new_cmap->allocated));
3542         FPRINTF((stderr, "(PaletteMake) max_colors = %d\n", max_colors));
3543         FPRINTF((stderr, "(PaletteMake) min_colors = %d\n", min_colors));
3544
3545         if (new_cmap->allocated < min_colors && tpal) {
3546             /* create a private colormap on second pass. */
3547             FPRINTF((stderr, "switching to private colormap\n"));
3548             tpal = 0;
3549         } else {
3550             break;
3551         }
3552     }
3553
3554     /* Now check the uniqueness of the new colormap against all the other
3555      * colormaps in the linked list.
3556      */
3557     {
3558         cmap_t *cmp = cmap_list_start;
3559
3560         while (cmp != NULL) {
3561             if ((cmp != new_cmap) && !cmaps_differ(cmp, new_cmap))
3562                 break;
3563             cmp = cmp->next_cmap;
3564         }
3565
3566         if (cmp != NULL) {
3567             /* Found a match. Discard the newly created colormap.  (Could
3568              * have simply discarded the old one and combined parts of
3569              * code similar to these if statements, but we'll avoid removing
3570              * old blocks of memory so that it is more likely that heap
3571              * memory will remain more tidy.
3572              */
3573             Remove_CMap_From_Linked_List(new_cmap);
3574             if (current_plot) {
3575                 current_plot->cmap = cmp;
3576                 RecolorWindow(current_plot);
3577             }
3578             current_cmap = cmp;
3579         } else {
3580             /* Unique and truely new colormap.  Make it the current map. */
3581             if (current_plot) {
3582                 current_plot->cmap = new_cmap;
3583                 RecolorWindow(current_plot);
3584             }
3585             /* If no other plots using colormap, then can be removed. */
3586             if (!Find_Plot_In_Linked_List_By_CMap(current_cmap))
3587                 Remove_CMap_From_Linked_List(current_cmap);
3588             current_cmap = new_cmap;
3589         }
3590
3591     }
3592
3593     if (save_title) {
3594         /* Restore window title (current_plot and current_plot->window are
3595          * valid, otherwise would not have been able to get save_title.
3596          */
3597         XStoreName(dpy, current_plot->window, save_title);
3598         XFree(save_title);
3599     }
3600
3601 }
3602
3603 static void
3604 PaletteSetColor(plot_struct * plot, double gray)
3605 {
3606     if (plot->cmap->allocated) {
3607         int index;
3608
3609         gray = floor(gray * plot->cmap->allocated) / (plot->cmap->allocated - 1);
3610         index = gray * (plot->cmap->allocated - 1);
3611         if (index >= plot->cmap->allocated)
3612                 index = plot->cmap->allocated -1;
3613
3614         XSetForeground(dpy, gc, plot->cmap->pixels[index]);
3615         plot->current_rgb = plot->cmap->pixels[index];
3616     }
3617 }
3618
3619 #ifdef USE_MOUSE
3620
3621 static int
3622 ErrorHandler(Display * display, XErrorEvent * error_event)
3623 {
3624     /* Don't remove directly.  Main program might be using the memory. */
3625     (void) display;             /* avoid -Wunused warnings */
3626     Add_Plot_To_Remove_FIFO_Queue((Window) error_event->resourceid);
3627     gp_exec_event(GE_reset, 0, 0, 0, 0, 0);
3628     return 0;
3629 }
3630
3631 static void
3632 DrawRuler(plot_struct * plot)
3633 {
3634     if (plot->ruler_on) {
3635         int x = X(plot->ruler_x);
3636         int y = Y(plot->ruler_y);
3637         if (!gc_xor) {
3638             /* create a gc for `rubberbanding' (well ...) */
3639             GetGCXor(plot, &gc_xor);
3640         }
3641         /* vertical line */
3642         XDrawLine(dpy, plot->window, gc_xor, x, 0, x, GRAPH_HEIGHT(plot));
3643         /* horizontal line */
3644         XDrawLine(dpy, plot->window, gc_xor, 0, y, plot->width, y);
3645     }
3646 }
3647
3648 static void
3649 EventuallyDrawMouseAddOns(plot_struct * plot)
3650 {
3651     DrawRuler(plot);
3652     DrawLineToRuler(plot);
3653     if (plot->zoombox_on)
3654         DrawBox(plot);
3655     DrawCoords(plot, plot->str);
3656     /*
3657        TODO more ...
3658      */
3659 }
3660
3661
3662 /*
3663  * draw a line using the gc with the GXxor function.
3664  * This can be used to turn on *and off* a line between
3665  * the current mouse pointer and the ruler.
3666  */
3667 static void
3668 DrawLineToRuler(plot_struct * plot)
3669 {
3670     if (plot->ruler_on == FALSE || plot->ruler_lineto_on == FALSE)
3671         return;
3672     if (plot->ruler_lineto_x < 0)
3673         return;
3674     if (!gc_xor) {
3675         GetGCXor(plot, &gc_xor);
3676     }
3677     XDrawLine(dpy, plot->window, gc_xor, 
3678             X(plot->ruler_x), Y(plot->ruler_y),
3679             plot->ruler_lineto_x, plot->ruler_lineto_y);
3680 }
3681
3682
3683
3684
3685 /*
3686  * draw a box using the gc with the GXxor function.
3687  * This can be used to turn on *and off* a box. The
3688  * corners of the box are annotated with the strings
3689  * stored in the plot structure.
3690  */
3691 static void
3692 DrawBox(plot_struct * plot)
3693 {
3694     int width;
3695     int height;
3696     int X0 = plot->zoombox_x1;
3697     int Y0 = plot->zoombox_y1;
3698     int X1 = plot->zoombox_x2;
3699     int Y1 = plot->zoombox_y2;
3700
3701     if (!gc_xor_dashed) {
3702         GetGCXorDashed(plot, &gc_xor_dashed);
3703     }
3704
3705     if (X1 < X0) {
3706         int tmp = X1;
3707         X1 = X0;
3708         X0 = tmp;
3709     }
3710
3711     if (Y1 < Y0) {
3712         int tmp = Y1;
3713         Y1 = Y0;
3714         Y0 = tmp;
3715     }
3716
3717     width = X1 - X0;
3718     height = Y1 - Y0;
3719
3720     XDrawRectangle(dpy, plot->window, gc_xor_dashed, X0, Y0, width, height);
3721
3722     if (plot->zoombox_str1a[0] || plot->zoombox_str1b[0])
3723         AnnotatePoint(plot, plot->zoombox_x1, plot->zoombox_y1, plot->zoombox_str1a, plot->zoombox_str1b);
3724     if (plot->zoombox_str2a[0] || plot->zoombox_str2b[0])
3725         AnnotatePoint(plot, plot->zoombox_x2, plot->zoombox_y2, plot->zoombox_str2a, plot->zoombox_str2b);
3726 }
3727
3728
3729 /*
3730  * draw the strings xstr and ystr centered horizontally
3731  * and vertically at the point x, y. Use the GXxor
3732  * as usually, so that we can also remove the coords
3733  * later.
3734  */
3735 static void
3736 AnnotatePoint(plot_struct * plot, int x, int y, const char xstr[], const char ystr[])
3737 {
3738     int ylen, xlen;
3739     int xwidth, ywidth;
3740
3741     xlen = strlen(xstr);
3742     xwidth = gpXTextWidth(font, xstr, xlen);
3743
3744     ylen = strlen(ystr);
3745     ywidth = gpXTextWidth(font, ystr, ylen);
3746
3747     /* horizontal centering disabled (joze) */
3748
3749     if (!gc_xor) {
3750         GetGCXor(plot, &gc_xor);
3751     }
3752     gpXDrawString(dpy, plot->window, gc_xor, x, y - 3, xstr, xlen);
3753     gpXDrawString(dpy, plot->window, gc_xor, x, y + vchar, ystr, ylen);
3754 }
3755
3756 /* returns the time difference to the last click in milliseconds */
3757 static long int
3758 SetTime(plot_struct *plot, Time t)
3759 {
3760     long int diff = t - plot->time;
3761
3762     FPRINTF((stderr, "(SetTime) difftime = %ld\n", diff));
3763
3764     plot->time = t;
3765     return diff > 0 ? diff : 0;
3766 }
3767
3768 static unsigned long
3769 AllocateXorPixel(cmap_t *cmap_ptr)
3770 {
3771     unsigned long pixel;
3772     XColor xcolor;
3773
3774     xcolor.pixel = cmap_ptr->colors[0]; /* background color */
3775     XQueryColor(dpy, cmap_ptr->colormap, &xcolor);
3776
3777     if (xcolor.red + xcolor.green + xcolor.blue < 0xffff) {
3778         /* it is admittedly somehow arbitrary to call
3779          * everything with a gray value < 0xffff a
3780          * dark background. Try to use the background's
3781          * complement for drawing which will always
3782          * result in white when using xor. */
3783         xcolor.red = ~xcolor.red;
3784         xcolor.green = ~xcolor.green;
3785         xcolor.blue = ~xcolor.blue;
3786         if (XAllocColor(dpy, cmap_ptr->colormap, &xcolor)) {
3787             /* use white foreground for dark backgrounds */
3788             pixel = xcolor.pixel;
3789         } else {
3790             /* simple xor if we've run out of colors. */
3791             pixel = WhitePixel(dpy, scr);
3792         }
3793     } else {
3794         /* use the background itself for drawing.
3795          * xoring two same colors will always result
3796          * in black. This color is already allocated. */
3797         pixel = xcolor.pixel;
3798     }
3799     cmap_ptr->xorpixel = pixel;
3800     return pixel;
3801 }
3802
3803 static void
3804 GetGCXor(plot_struct * plot, GC * ret)
3805 {
3806     XGCValues values;
3807     unsigned long mask = 0;
3808
3809     values.foreground = AllocateXorPixel(plot->cmap);
3810
3811 #ifdef USE_X11_MULTIBYTE
3812     if (usemultibyte) {
3813         mask = GCForeground | GCFunction;
3814         values.function = GXxor;
3815     } else
3816 #endif
3817     {
3818         mask = GCForeground | GCFunction | GCFont;
3819         values.function = GXxor;
3820         values.font = font->fid;
3821     }
3822
3823     *ret = XCreateGC(dpy, plot->window, mask, &values);
3824 }
3825
3826 static void
3827 GetGCXorDashed(plot_struct * plot, GC * gc)
3828 {
3829     GetGCXor(plot, gc);
3830     XSetLineAttributes(dpy, *gc, 0,     /* line width, X11 treats 0 as a `thin' line */
3831                        LineOnOffDash,   /* also: LineDoubleDash */
3832                        CapNotLast,      /* also: CapButt, CapRound, CapProjecting */
3833                        JoinMiter /* also: JoinRound, JoinBevel */ );
3834 }
3835
3836 #if 0
3837 /*
3838  * returns the newly created gc
3839  * pixmap: where the gc will be used
3840  * mode == 0 --> black on white
3841  * mode == 1 --> white on black
3842  */
3843 /* FIXME HBB 20020225: This function is not used anywhere ??? */
3844 static void
3845 GetGCBlackAndWhite(plot_struct * plot, GC * ret, Pixmap pixmap, int mode)
3846 {
3847     XGCValues values;
3848     unsigned long mask = 0;
3849
3850     mask = GCForeground | GCBackground | GCFont | GCFunction;
3851     if (!mode) {
3852 #if 0
3853         values.foreground = BlackPixel(dpy, scr);
3854         values.background = WhitePixel(dpy, scr);
3855 #else
3856         values.foreground = plot->cmap->colors[1];
3857         values.background = plot->cmap->colors[0];
3858 #endif
3859     } else {
3860         /*
3861          * swap colors
3862          */
3863 #if 0
3864         values.foreground = WhitePixel(dpy, scr);
3865         values.background = BlackPixel(dpy, scr);
3866 #else
3867         values.foreground = plot->cmap->colors[0];
3868         values.background = plot->cmap->colors[1];
3869 #endif
3870     }
3871     values.function = GXcopy;
3872     values.font = font->fid;
3873
3874     *ret = XCreateGC(dpy, pixmap, mask, &values);
3875 }
3876
3877 /*
3878  * split a string at `splitchar'.
3879  */
3880 /* FIXME HBB 20020225: This function is not used anywhere ??? */
3881 static int
3882 SplitAt(char **args, int maxargs, char *buf, char splitchar)
3883 {
3884     int argc = 0;
3885
3886     while (*buf != '\0' && argc < maxargs) {
3887
3888         if ((*buf == splitchar))
3889             *buf++ = '\0';
3890
3891         if (!(*buf))            /* don't count the terminating NULL */
3892             break;
3893
3894         /* Save the argument.  */
3895         *args++ = buf;
3896         argc++;
3897
3898         /* Skip over the argument */
3899         while ((*buf != '\0') && (*buf != splitchar))
3900             buf++;
3901     }
3902
3903     *args = '\0';               /* terminate */
3904     return argc;
3905 }
3906
3907 /* FIXME HBB 20020225: This function is not used anywhere ??? */
3908 static void
3909 xfree(void *fred)
3910 {
3911     if (fred)
3912         free(fred);
3913 }
3914 #endif
3915
3916 /* erase the last displayed position string */
3917 static void
3918 EraseCoords(plot_struct * plot)
3919 {
3920     DrawCoords(plot, plot->str);
3921 }
3922
3923
3924
3925 static void
3926 DrawCoords(plot_struct * plot, const char *str)
3927 {
3928     if (!gc_xor) {
3929         GetGCXor(plot, &gc_xor);
3930     }
3931
3932     if (str[0] != 0)
3933         gpXDrawString(dpy, plot->window, gc_xor, 1, plot->gheight + vchar - 1, str, strlen(str));
3934 }
3935
3936
3937 /* display text (e.g. mouse position) in the lower left corner of the window. */
3938 static void
3939 DisplayCoords(plot_struct * plot, const char *s)
3940 {
3941     /* first, erase old text */
3942     EraseCoords(plot);
3943
3944     if (s[0] == 0) {
3945         /* no new text? */
3946         if (plot->height > plot->gheight) {
3947             /* and window has space for text? then make it smaller, unless we're already doing a resize: */
3948             if (!plot->resizing) {
3949                 XResizeWindow(dpy, plot->window, plot->width, plot->gheight);
3950                 plot->resizing = TRUE;
3951             }
3952         }
3953     } else {
3954         /* so we do have new text */
3955         if (plot->height == plot->gheight) {
3956             /* window not large enough? then make it larger, unless we're already doing a resize: */
3957             if (!plot->resizing) {
3958                 XResizeWindow(dpy, plot->window, plot->width, plot->gheight + vchar);
3959                 plot->resizing = TRUE;
3960             }
3961         }
3962     }
3963
3964     /* finally, draw the new text: */
3965     DrawCoords(plot, s);
3966
3967     /* and save it, for later erasing: */
3968     strcpy(plot->str, s);
3969 }
3970
3971
3972 static TBOOLEAN
3973 is_meta(KeySym mod)
3974 {
3975     /* CJK keyboards may have these meta keys */
3976     if (0xFF20 <= mod && mod <= 0xFF3F)
3977         return TRUE;
3978
3979     /* Everyone else may have these meta keys */
3980     switch (mod) {
3981         case XK_Multi_key:
3982         case XK_Shift_L:
3983         case XK_Shift_R:
3984         case XK_Control_L:
3985         case XK_Control_R:
3986         case XK_Meta_L:
3987         case XK_Meta_R:
3988         case XK_Alt_L:
3989         case XK_Alt_R:
3990                         return TRUE;
3991         default:
3992                         return FALSE;
3993     }
3994 }
3995
3996 /* It returns NULL if we are not running in any known (=implemented) multitab
3997  * console.
3998  * Otherwise it returns a command to be executed in order to switch to the
3999  * appropriate tab of a multitab console.
4000  * In addition, it may return non-zero newGnuplotXID in order to overwrite zero
4001  * value of gnuplotXID (because Konsole's in KDE <3.2 don't set WINDOWID contrary
4002  * to all other xterm's).
4003  * Currently implemented for:
4004  *      - KDE's Konsole.
4005  * Note: if the returned command is !NULL, then it must be free()'d by the caller.
4006  */
4007 static char*
4008 getMultiTabConsoleSwitchCommand(unsigned long *newGnuplotXID)
4009 {
4010 #ifdef HAVE_STRDUP      /* We assume that any machine missing strdup is too old for KDE */
4011     char *cmd = NULL; /* result */
4012     char *ptr = getenv("KONSOLE_DCOP_SESSION"); /* Try KDE's Konsole first. */
4013     *newGnuplotXID = 0;
4014     if (ptr) {
4015         /* We are in KDE's Konsole, or in a terminal window detached from a Konsole.
4016          * In order to active a tab:
4017          * 1. get environmental variable KONSOLE_DCOP_SESSION: it includes konsole id and session name
4018          * 2. if
4019          *      $WINDOWID is defined and it equals
4020          *          `dcop konsole-3152 konsole-mainwindow#1 getWinID`
4021          *      (KDE 3.2) or when $WINDOWID is undefined (KDE 3.1), then run commands
4022          *    dcop konsole-3152 konsole activateSession session-2; \
4023          *    dcop konsole-3152 konsole-mainwindow#1 raise
4024          * Note: by $WINDOWID we mean gnuplot's text console WINDOWID.
4025          * Missing: focus is not transferred unless $WINDOWID is defined (should be fixed in KDE 3.2).
4026          *
4027          * Implementation and tests on KDE 3.1.4: Petr Mikulik.
4028          */
4029         char *konsole_name = NULL;
4030         *newGnuplotXID = 0; /* don't change gnuplotXID by default */
4031         /* use 'while' instead of 'if' to easily break out (aka catch exception) */
4032         while (1) {
4033             char *konsole_tab;
4034             unsigned long w;
4035             FILE *p;
4036             ptr = strchr(ptr, '(');
4037             /* the string for tab nb 4 looks like 'DCOPRef(konsole-2866, session-4)' */
4038             if (!ptr) return NULL;
4039             konsole_name = strdup(ptr+1);
4040             konsole_tab = strchr(konsole_name, ',');
4041             if (!konsole_tab) break;
4042             *konsole_tab++ = 0;
4043             ptr = strchr(konsole_tab, ')');
4044             if (ptr) *ptr = 0;
4045             /* Not necessary to define DCOP_RAISE: returning newly known
4046              * newGnuplotXID instead is sufficient.
4047              */
4048 /* #define DCOP_RAISE */
4049 #ifdef DCOP_RAISE
4050             cmd = malloc(2*strlen(konsole_name) + strlen(konsole_tab) + 128);
4051 #else
4052             cmd = malloc(strlen(konsole_name) + strlen(konsole_tab) + 64);
4053 #endif
4054             sprintf(cmd, "dcop %s konsole-mainwindow#1 getWinID 2>/dev/null", konsole_name);
4055                 /* is  2>/dev/null  portable among various shells? */
4056             p = popen(cmd, "r");
4057             if (p) {
4058                 fscanf(p, "%lu", &w);
4059                 pclose(p);
4060             }
4061             if (gnuplotXID) { /* $WINDOWID is known */
4062                 if (w != gnuplotXID) break;
4063                     /* `dcop getWinID`==$WINDOWID thus we are running in a window detached from Konsole */
4064             } else {
4065                 *newGnuplotXID = w;
4066                     /* $WINDOWID has not been known (KDE 3.1), thus set it up */
4067             }
4068 #ifdef DCOP_RAISE
4069             /* not necessary: returning newly known newGnuplotXID instead is sufficient */
4070             sprintf(cmd, "dcop %s konsole-mainwindow#1 raise;", konsole_name);
4071             sprintf(cmd+strlen(cmd), "dcop %s konsole activateSession %s", konsole_name, konsole_tab);
4072 #else
4073             sprintf(cmd, "dcop %s konsole activateSession %s", konsole_name, konsole_tab);
4074 #endif
4075             free(konsole_name);
4076             return cmd;
4077         }
4078         free(konsole_name);
4079         free(cmd);
4080         return NULL;
4081     }
4082     /* now test for GNOME multitab console */
4083     /* ... if somebody bothers to implement it ... */
4084
4085 #endif /* HAVE_STRDUP */
4086     /* we are not running in any known (implemented) multitab console */
4087     return NULL;
4088 }
4089
4090 #endif
4091
4092
4093 /*---------------------------------------------------------------------------
4094  *  reset all cursors (since we dont have a record of the previous terminal #)
4095  *---------------------------------------------------------------------------*/
4096
4097 static void
4098 reset_cursor()
4099 {
4100     plot_struct *plot = plot_list_start;
4101
4102     while (plot) {
4103         if (plot->window) {
4104             FPRINTF((stderr, "Window for plot %d exists\n", plot->plot_number));
4105             XUndefineCursor(dpy, plot->window);
4106         }
4107         plot = plot->next_plot;
4108     }
4109
4110     FPRINTF((stderr, "Cursors reset\n"));
4111     return;
4112 }
4113
4114 /*-----------------------------------------------------------------------------
4115  *   resize - rescale last plot if window resized
4116  *---------------------------------------------------------------------------*/
4117
4118 #ifdef USE_MOUSE
4119
4120 /* the status of the shift, ctrl and alt keys
4121 */
4122 static int modifier_mask = 0;
4123
4124 static void update_modifiers __PROTO((unsigned int));
4125
4126 static void
4127 update_modifiers(unsigned int state)
4128 {
4129     int old_mod_mask;
4130
4131     old_mod_mask = modifier_mask;
4132     modifier_mask = ((state & ShiftMask) ? Mod_Shift : 0)
4133         | ((state & ControlMask) ? Mod_Ctrl : 0)
4134         | ((state & Mod1Mask) ? Mod_Alt : 0);
4135     if (old_mod_mask != modifier_mask) {
4136         gp_exec_event(GE_modifier, 0, 0, modifier_mask, 0, 0);
4137     }
4138 }
4139
4140 #endif
4141
4142 static void
4143 process_configure_notify_event(XEvent *event)
4144 {
4145     plot_struct *plot;
4146     int force_redraw = 0;
4147
4148     /* Filter down to the last ConfigureNotify event */
4149     XSync(dpy, False);
4150     while (XCheckTypedWindowEvent(dpy, event->xany.window, ConfigureNotify, event))
4151         ;
4152
4153     plot = Find_Plot_In_Linked_List_By_Window(event->xconfigure.window);
4154     if (plot) {
4155         int w = event->xconfigure.width, h = event->xconfigure.height;
4156
4157         /* store settings in case window is closed then recreated */
4158         /* but: don't do this if both x and y are 0, since some
4159          * (all?) systems set these to zero when only resizing
4160          * (not moving) the window. This does mean that a move to
4161          * (0, 0) won't be registered: can we solve that? */
4162         if (event->xconfigure.x != 0 || event->xconfigure.y != 0) {
4163             plot->x = event->xconfigure.x;
4164             plot->y = event->xconfigure.y;
4165             plot->posn_flags = (plot->posn_flags & ~PPosition) | USPosition;
4166         }
4167 #ifdef USE_MOUSE
4168         /* first, check whether we were waiting for completion of a resize */
4169         if (plot->resizing) {
4170             /* it seems to be impossible to distinguish between a
4171              * resize caused by our call to XResizeWindow(), and a
4172              * resize started by the user/windowmanager; but we can
4173              * make a good guess which can only fail if the user
4174              * resizes the window while we're also resizing it
4175              * ourselves: */
4176             if (w == plot->width
4177             && (h == plot->gheight || h == plot->gheight + vchar)) {
4178                 /* most likely, it's a resize for showing/hiding the status line.
4179                  * Test whether the height is now correct; if not, start another resize. */
4180                 plot->resizing = FALSE;
4181                 if (w == plot->width
4182                 && h == plot->gheight + (plot->str[0] ? vchar : 0)) {
4183                         /* Was successful, status line can be drawn without rescaling plot. */
4184                         plot->height = h;
4185                         return;
4186                 } else {
4187                         /* Possibly, a resize attempt _failed_ because window manager denied it.
4188                            Resizing again goes into a vicious endless loop!
4189                            (Seen with fluxbox-1.0.0 and a tab group of gnuplot windows.) */
4190                         /* Instead of just appending the status line, redraw/scale the plot. */
4191                         force_redraw = TRUE;
4192                 }
4193             }
4194         }
4195 #endif
4196
4197         if (w > 1 && h > 1 && (force_redraw || w != plot->width || h != plot->height)) {
4198
4199             plot->width = w;
4200             plot->height = h;
4201 #ifdef USE_MOUSE
4202             /* Make sure that unsigned number doesn't underflow. */
4203             if (plot->str[0])
4204                 plot->gheight = (vchar > plot->height) ? 0 : plot->height - vchar;
4205             else
4206                 plot->gheight = plot->height;
4207 #endif
4208             plot->posn_flags = (plot->posn_flags & ~PSize) | USSize;
4209
4210             if (stipple_initialized) {
4211                 int i;
4212                 for (i = 0; i < stipple_pattern_num; i++)
4213                         XFreePixmap(dpy, stipple_pattern[i]);
4214                 stipple_initialized = 0;
4215             }
4216
4217             if (plot->pixmap) {
4218                 /* it is the wrong size now */
4219                 FPRINTF((stderr, "Free pixmap %d\n", 0));
4220                 XFreePixmap(dpy, plot->pixmap);
4221                 plot->pixmap = None;
4222             }
4223             display(plot);
4224         }
4225     }
4226 }
4227
4228 static void
4229 process_event(XEvent *event)
4230 {
4231     plot_struct *plot;
4232     KeySym keysym;
4233     char key_sequence[8];
4234
4235     FPRINTF((stderr, "Event 0x%x\n", event->type));
4236
4237     switch (event->type) {
4238     case ConfigureNotify:
4239         process_configure_notify_event(event);
4240         break;
4241
4242     case KeyPress:
4243         plot = Find_Plot_In_Linked_List_By_Window(event->xkey.window);
4244
4245         /* Unlike XKeycodeToKeysym, XLookupString applies the current */
4246         /* shift, ctrl, alt, and meta modifiers to yield a character. */
4247         /* keysym = XKeycodeToKeysym(dpy, event->xkey.keycode, 0); */
4248         XLookupString((XKeyEvent *)event, key_sequence, sizeof(key_sequence), &keysym, NULL);
4249
4250 #ifdef USE_MOUSE
4251         update_modifiers(event->xkey.state);
4252 #endif
4253             switch (keysym) {
4254 #ifdef USE_MOUSE
4255             case ' ': {
4256                 static int cmd_tried = 0;
4257                 static char *cmd = NULL;
4258                 static unsigned long newGnuplotXID = 0;
4259                 
4260                 /* If the "-ctrlq" resource is set, ignore ' ' unless control key is also pressed */
4261                 if (ctrlq && !(modifier_mask & Mod_Ctrl))
4262                     break;
4263
4264                 if (!cmd_tried) {
4265                     cmd = getMultiTabConsoleSwitchCommand(&newGnuplotXID);
4266                     cmd_tried = 1;
4267                 }
4268                 /* overwrite gnuplotXID (re)set after x11.trm:X11_options() */
4269                 if (newGnuplotXID) gnuplotXID = newGnuplotXID;
4270                 if (cmd) system(cmd);
4271                 }
4272                 if (gnuplotXID) {
4273                     XMapRaised(dpy, gnuplotXID);
4274                     XSetInputFocus(dpy, gnuplotXID, 0 /*revert */ , CurrentTime);
4275                     XFlush(dpy);
4276                 }
4277                 return;
4278             case 'm': /* Toggle mouse display, but only if we control the window here */
4279                 if (((plot != current_plot) && (!modifier_mask))
4280 #ifdef PIPE_IPC
4281                     || pipe_died
4282 #endif
4283                    ) {
4284                     plot->mouse_on = !(plot->mouse_on);
4285                     DisplayCoords(plot, plot->mouse_on ? " " : "");
4286                 }
4287                 break;
4288 #endif
4289             case 'q':
4290 #ifdef USE_MOUSE
4291                 /* If the "-ctrlq" resource is set, ignore q unless control key is also pressed */
4292                 if (ctrlq && !(modifier_mask & Mod_Ctrl)) {
4293                     FPRINTF((stderr, "ignoring q, modifier_mask = %o\n", modifier_mask));
4294                     break;
4295                 }
4296 #endif
4297                 /* close X window */
4298                 Remove_Plot_From_Linked_List(event->xkey.window);
4299                 return;
4300             default:
4301                 break;
4302             }                   /* switch (keysym) */
4303 #ifdef USE_MOUSE
4304
4305         if (is_meta(keysym))
4306             return;
4307
4308         switch (keysym) {
4309
4310 #define KNOWN_KEYSYMS(gp_keysym)                                     \
4311         if (plot == current_plot) {                                  \
4312             gp_exec_event(GE_keypress,                               \
4313                 (int)RevX(event->xkey.x), (int)RevY(event->xkey.y),  \
4314                 gp_keysym, 0, plot->plot_number);                    \
4315         } else {                                                     \
4316             gp_exec_event(GE_keypress_old,                              \
4317                 (int)RevX(event->xkey.x), (int)RevY(event->xkey.y),  \
4318                 gp_keysym, 0, plot->plot_number);                    \
4319         }                                                            \
4320         return;
4321
4322 /* Prevent hysteresis if redraw cannot keep up with rate of keystrokes */
4323 #define DRAIN_KEYSTROKES(key)                                   \
4324         if (plot == current_plot) {                             \
4325             while (XCheckTypedWindowEvent(dpy,                  \
4326                     event->xany.window, KeyPress, event));      \
4327         }
4328
4329         case XK_BackSpace:
4330             KNOWN_KEYSYMS(GP_BackSpace);
4331         case XK_Tab:
4332             KNOWN_KEYSYMS(GP_Tab);
4333         case XK_Linefeed:
4334             KNOWN_KEYSYMS(GP_Linefeed);
4335         case XK_Clear:
4336             KNOWN_KEYSYMS(GP_Clear);
4337         case XK_Return:
4338             KNOWN_KEYSYMS(GP_Return);
4339         case XK_Pause:
4340             KNOWN_KEYSYMS(GP_Pause);
4341         case XK_Scroll_Lock:
4342             KNOWN_KEYSYMS(GP_Scroll_Lock);
4343 #ifdef XK_Sys_Req
4344         case XK_Sys_Req:
4345             KNOWN_KEYSYMS(GP_Sys_Req);
4346 #endif
4347         case XK_Escape:
4348             KNOWN_KEYSYMS(GP_Escape);
4349         case XK_Insert:
4350             KNOWN_KEYSYMS(GP_Insert);
4351         case XK_Delete:
4352             KNOWN_KEYSYMS(GP_Delete);
4353         case XK_Home:
4354             KNOWN_KEYSYMS(GP_Home);
4355         case XK_Left:
4356             DRAIN_KEYSTROKES(XK_Left);
4357             KNOWN_KEYSYMS(GP_Left);
4358         case XK_Up:
4359             DRAIN_KEYSTROKES(XK_Up);
4360             KNOWN_KEYSYMS(GP_Up);
4361         case XK_Right:
4362             DRAIN_KEYSTROKES(XK_Right);
4363             KNOWN_KEYSYMS(GP_Right);
4364         case XK_Down:
4365             DRAIN_KEYSTROKES(XK_Down);
4366             KNOWN_KEYSYMS(GP_Down);
4367         case XK_Prior:          /* XXX */
4368             KNOWN_KEYSYMS(GP_PageUp);
4369         case XK_Next:           /* XXX */
4370             KNOWN_KEYSYMS(GP_PageDown);
4371         case XK_End:
4372             KNOWN_KEYSYMS(GP_End);
4373         case XK_Begin:
4374             KNOWN_KEYSYMS(GP_Begin);
4375         case XK_KP_Space:
4376             KNOWN_KEYSYMS(GP_KP_Space);
4377         case XK_KP_Tab:
4378             KNOWN_KEYSYMS(GP_KP_Tab);
4379         case XK_KP_Enter:
4380             KNOWN_KEYSYMS(GP_KP_Enter);
4381         case XK_KP_F1:
4382             KNOWN_KEYSYMS(GP_KP_F1);
4383         case XK_KP_F2:
4384             KNOWN_KEYSYMS(GP_KP_F2);
4385         case XK_KP_F3:
4386             KNOWN_KEYSYMS(GP_KP_F3);
4387         case XK_KP_F4:
4388             KNOWN_KEYSYMS(GP_KP_F4);
4389 #ifdef XK_KP_Home
4390         case XK_KP_Home:
4391             KNOWN_KEYSYMS(GP_KP_Home);
4392 #endif
4393 #ifdef XK_KP_Left
4394         case XK_KP_Left:
4395             KNOWN_KEYSYMS(GP_KP_Left);
4396 #endif
4397 #ifdef XK_KP_Up
4398         case XK_KP_Up:
4399             KNOWN_KEYSYMS(GP_KP_Up);
4400 #endif
4401 #ifdef XK_KP_Right
4402         case XK_KP_Right:
4403             KNOWN_KEYSYMS(GP_KP_Right);
4404 #endif
4405 #ifdef XK_KP_Down
4406         case XK_KP_Down:
4407             KNOWN_KEYSYMS(GP_KP_Down);
4408 #endif
4409 #ifdef XK_KP_Page_Up
4410         case XK_KP_Page_Up:
4411             KNOWN_KEYSYMS(GP_KP_Page_Up);
4412 #endif
4413 #ifdef XK_KP_Page_Down
4414         case XK_KP_Page_Down:
4415             KNOWN_KEYSYMS(GP_KP_Page_Down);
4416 #endif
4417 #ifdef XK_KP_End
4418         case XK_KP_End:
4419             KNOWN_KEYSYMS(GP_KP_End);
4420 #endif
4421 #ifdef XK_KP_Begin
4422         case XK_KP_Begin:
4423             KNOWN_KEYSYMS(GP_KP_Begin);
4424 #endif
4425 #ifdef XK_KP_Insert
4426         case XK_KP_Insert:
4427             KNOWN_KEYSYMS(GP_KP_Insert);
4428 #endif
4429 #ifdef XK_KP_Delete
4430         case XK_KP_Delete:
4431             KNOWN_KEYSYMS(GP_KP_Delete);
4432 #endif
4433         case XK_KP_Equal:
4434             KNOWN_KEYSYMS(GP_KP_Equal);
4435         case XK_KP_Multiply:
4436             KNOWN_KEYSYMS(GP_KP_Multiply);
4437         case XK_KP_Add:
4438             KNOWN_KEYSYMS(GP_KP_Add);
4439         case XK_KP_Separator:
4440             KNOWN_KEYSYMS(GP_KP_Separator);
4441         case XK_KP_Subtract:
4442             KNOWN_KEYSYMS(GP_KP_Subtract);
4443         case XK_KP_Decimal:
4444             KNOWN_KEYSYMS(GP_KP_Decimal);
4445         case XK_KP_Divide:
4446             KNOWN_KEYSYMS(GP_KP_Divide);
4447
4448         case XK_KP_0:
4449             KNOWN_KEYSYMS(GP_KP_0);
4450         case XK_KP_1:
4451             KNOWN_KEYSYMS(GP_KP_1);
4452         case XK_KP_2:
4453             KNOWN_KEYSYMS(GP_KP_2);
4454         case XK_KP_3:
4455             KNOWN_KEYSYMS(GP_KP_3);
4456         case XK_KP_4:
4457             KNOWN_KEYSYMS(GP_KP_4);
4458         case XK_KP_5:
4459             KNOWN_KEYSYMS(GP_KP_5);
4460         case XK_KP_6:
4461             KNOWN_KEYSYMS(GP_KP_6);
4462         case XK_KP_7:
4463             KNOWN_KEYSYMS(GP_KP_7);
4464         case XK_KP_8:
4465             KNOWN_KEYSYMS(GP_KP_8);
4466         case XK_KP_9:
4467             KNOWN_KEYSYMS(GP_KP_9);
4468
4469         case XK_F1:
4470             KNOWN_KEYSYMS(GP_F1);
4471         case XK_F2:
4472             KNOWN_KEYSYMS(GP_F2);
4473         case XK_F3:
4474             KNOWN_KEYSYMS(GP_F3);
4475         case XK_F4:
4476             KNOWN_KEYSYMS(GP_F4);
4477         case XK_F5:
4478             KNOWN_KEYSYMS(GP_F5);
4479         case XK_F6:
4480             KNOWN_KEYSYMS(GP_F6);
4481         case XK_F7:
4482             KNOWN_KEYSYMS(GP_F7);
4483         case XK_F8:
4484             KNOWN_KEYSYMS(GP_F8);
4485         case XK_F9:
4486             KNOWN_KEYSYMS(GP_F9);
4487         case XK_F10:
4488             KNOWN_KEYSYMS(GP_F10);
4489         case XK_F11:
4490             KNOWN_KEYSYMS(GP_F11);
4491         case XK_F12:
4492             KNOWN_KEYSYMS(GP_F12);
4493
4494         default:
4495             KNOWN_KEYSYMS((int) keysym);
4496             break;
4497         }
4498 #endif
4499         break;
4500     case KeyRelease:
4501 #ifdef USE_MOUSE
4502         update_modifiers(event->xkey.state);
4503         keysym = XKeycodeToKeysym(dpy, event->xkey.keycode, 0);
4504         if (is_meta(keysym)) {
4505             plot = Find_Plot_In_Linked_List_By_Window(event->xkey.window);
4506             cursor = cursor_default;
4507             if (plot)
4508                 XDefineCursor(dpy, plot->window, cursor);
4509         }
4510 #endif
4511         break;
4512
4513     case ClientMessage:
4514         if (event->xclient.message_type == WM_PROTOCOLS &&
4515             event->xclient.format == 32 && event->xclient.data.l[0] == WM_DELETE_WINDOW) {
4516             Remove_Plot_From_Linked_List(event->xclient.window);
4517             /* EAM FIXME - may have to generate GE_reset event here also */
4518         }
4519         break;
4520
4521 #ifdef USE_MOUSE
4522     case DestroyNotify:
4523         plot = Find_Plot_In_Linked_List_By_Window(event->xconfigure.window);
4524         if (plot == current_plot) {
4525             gp_exec_event(GE_reset, 0, 0, 0, 0, 0);
4526         }
4527         break;
4528
4529     case Expose:
4530         /*
4531          * we need to handle expose events here, because
4532          * there might stuff like rulers which has to
4533          * be redrawn. Well. It's not really hard to handle
4534          * this. Note that the x and y fields are not used
4535          * to update the pointer pos because the pointer
4536          * might be on another window which generates the
4537          * expose events. (joze)
4538          */
4539
4540         /* Check for any ConfigureNotify events further down in the X11
4541          * event queue. If one is found, handle it first and let the
4542          * expose event that is generated be handled later.
4543          * Jay Painter Nov 2003.
4544          */
4545         if (XCheckTypedWindowEvent(dpy, event->xany.window, ConfigureNotify, event)) {
4546             process_configure_notify_event(event);
4547             break;
4548         }
4549         while (XCheckTypedWindowEvent(dpy, event->xany.window, Expose, event));
4550
4551         plot = Find_Plot_In_Linked_List_By_Window(event->xexpose.window);
4552
4553         if (plot)
4554             UpdateWindow(plot);
4555         break;
4556
4557     case EnterNotify:
4558         plot = Find_Plot_In_Linked_List_By_Window(event->xcrossing.window);
4559         if (!plot)
4560             break;
4561         if (plot == current_plot) {
4562             Call_display(plot);
4563             gp_exec_event(GE_motion, (int) RevX(event->xcrossing.x), (int) RevY(event->xcrossing.y), 0, 0, 0);
4564         }
4565         if (plot->zoombox_on) {
4566             DrawBox(plot);
4567             plot->zoombox_x2 = event->xcrossing.x;
4568             plot->zoombox_y2 = event->xcrossing.y;
4569             DrawBox(plot);
4570         }
4571         if (plot->ruler_on) {
4572             DrawLineToRuler(plot);
4573             plot->ruler_lineto_x = event->xcrossing.x;
4574             plot->ruler_lineto_y = event->xcrossing.y;
4575             DrawLineToRuler(plot);
4576         }
4577         break;
4578     case MotionNotify:
4579         update_modifiers(event->xmotion.state);
4580         plot = Find_Plot_In_Linked_List_By_Window(event->xmotion.window);
4581         if (!plot)
4582             break;
4583         {
4584             Window root, child;
4585             int root_x, root_y, pos_x, pos_y;
4586             unsigned int keys_buttons;
4587             if (!XQueryPointer(dpy, event->xmotion.window, &root, &child, &root_x, &root_y, &pos_x, &pos_y, &keys_buttons))
4588                 break;
4589
4590             if (plot == current_plot
4591 #ifdef PIPE_IPC
4592                 && !pipe_died
4593 #endif
4594                ) {
4595                 Call_display(plot);
4596                 gp_exec_event(GE_motion, (int) RevX(pos_x), (int) RevY(pos_y), 0, 0, 0);
4597 #if defined(USE_MOUSE) && defined(MOUSE_ALL_WINDOWS)
4598             } else if (plot->axis_mask && plot->mouse_on && plot->almost2d) {
4599                 /* This is not the active plot window, but we can still update the mouse coords */
4600                 char mouse_format[60];
4601                 char *m = mouse_format;
4602                 double x, y, x2, y2;
4603
4604                 *m = '\0';
4605                 mouse_to_coords(plot, event, &x, &y, &x2, &y2);
4606                 if (plot->axis_mask & (1<<FIRST_X_AXIS)) {
4607                     sprintf(m, "x=  %10g %c", x, '\0');
4608                     m += 15;
4609                 }
4610                 if (plot->axis_mask & (1<<SECOND_X_AXIS)) {
4611                     sprintf(m, "x2= %10g %c", x2, '\0');
4612                     m += 15;
4613                 }
4614                 if (plot->axis_mask & (1<<FIRST_Y_AXIS)) {
4615                     sprintf(m, "y=  %10g %c", y, '\0');
4616                     m += 15;
4617                 }
4618                 if (plot->axis_mask & (1<<SECOND_Y_AXIS)) {
4619                     sprintf(m, "y2= %10g %c", y2, '\0');
4620                     m += 15;
4621                 }
4622                 DisplayCoords(plot, mouse_format);
4623             } else if (!plot->mouse_on) {
4624                 DisplayCoords(plot, "");
4625 #endif
4626             }
4627
4628             if (plot->zoombox_on) {
4629                 DrawBox(plot);
4630                 plot->zoombox_x2 = pos_x;
4631                 plot->zoombox_y2 = pos_y;
4632                 DrawBox(plot);
4633             }
4634             if (plot->ruler_on && plot->ruler_lineto_on) {
4635                 DrawLineToRuler(plot);
4636                 plot->ruler_lineto_x = event->xcrossing.x;
4637                 plot->ruler_lineto_y = event->xcrossing.y;
4638                 DrawLineToRuler(plot);
4639             }
4640         }
4641         break;
4642     case ButtonPress:
4643         update_modifiers(event->xbutton.state);
4644 #ifndef TITLE_BAR_DRAWING_MSG
4645         button_pressed |= (1 << event->xbutton.button);
4646 #endif
4647         plot = Find_Plot_In_Linked_List_By_Window(event->xbutton.window);
4648         if (!plot)
4649             break;
4650         {
4651             if (plot == current_plot) {
4652                 Call_display(plot);
4653                 gp_exec_event(GE_buttonpress, (int) RevX(event->xbutton.x), (int) RevY(event->xbutton.y), event->xbutton.button, 0, 0);
4654             }
4655         }
4656         break;
4657     case ButtonRelease:
4658 #ifndef TITLE_BAR_DRAWING_MSG
4659         button_pressed &= ~(1 << event->xbutton.button);
4660 #endif
4661         plot = Find_Plot_In_Linked_List_By_Window(event->xbutton.window);
4662         if (!plot)
4663             break;
4664         if (plot == current_plot) {
4665
4666             long int doubleclick = SetTime(plot, event->xbutton.time);
4667
4668             update_modifiers(event->xbutton.state);
4669             Call_display(plot);
4670             gp_exec_event(GE_buttonrelease,
4671                           (int) RevX(event->xbutton.x), (int) RevY(event->xbutton.y),
4672                           event->xbutton.button, (int) doubleclick, 0);
4673         }
4674
4675 #ifdef DEBUG
4676 #if defined(USE_MOUSE) && defined(MOUSE_ALL_WINDOWS)
4677         /* This causes gnuplot_x11 to pass mouse clicks back from all plot windows,
4678          * not just the current plot. But who should we notify that a click has
4679          * happened, and how?  The fprintf to stderr is just for debugging. */
4680         else if (plot->axis_mask) {
4681             double x, y, x2, y2;
4682             mouse_to_coords(plot, event, &x, &y, &x2, &y2);
4683             fprintf(stderr, "gnuplot_x11 %d: mouse button %1d from window %d at %g %g\n",
4684                     __LINE__, event->xbutton.button, (plot ? plot->plot_number : 0), x, y);
4685         }
4686 #endif
4687 #endif
4688
4689         break;
4690 #endif /* USE_MOUSE */
4691 #ifdef EXPORT_SELECTION
4692     case SelectionNotify:
4693         break;
4694     case SelectionRequest:
4695         if (exportselection)
4696             handle_selection_event(event);
4697         break;
4698 #endif
4699
4700     }
4701 }
4702
4703 /*-----------------------------------------------------------------------------
4704  *   preset - determine options, open display, create window
4705  *---------------------------------------------------------------------------*/
4706 /*
4707 #define On(v) ( !strcmp(v, "on") || !strcmp(v, "true") || \
4708                 !strcmp(v, "On") || !strcmp(v, "True") || \
4709                 !strcmp(v, "ON") || !strcmp(v, "TRUE") )
4710 */
4711 #define On(v) ( !strncasecmp(v, "on", 2) || !strncasecmp(v, "true", 4) )
4712
4713 #define AppDefDir "/usr/lib/X11/app-defaults"
4714 #ifndef MAXHOSTNAMELEN
4715 #define MAXHOSTNAMELEN 64
4716 #endif
4717
4718 static XrmDatabase dbCmd, dbApp, dbDef, dbEnv, db = (XrmDatabase) 0;
4719
4720 static char *type[20];
4721 static XrmValue value;
4722
4723 static XrmOptionDescRec options[] = {
4724     {"-mono", ".mono", XrmoptionNoArg, (XPointer) "on"},
4725     {"-gray", ".gray", XrmoptionNoArg, (XPointer) "on"},
4726     {"-clear", ".clear", XrmoptionNoArg, (XPointer) "on"},
4727     {"-tvtwm", ".tvtwm", XrmoptionNoArg, (XPointer) "on"},
4728     {"-pointsize", ".pointsize", XrmoptionSepArg, (XPointer) NULL},
4729     {"-display", ".display", XrmoptionSepArg, (XPointer) NULL},
4730     {"-name", ".name", XrmoptionSepArg, (XPointer) NULL},
4731     {"-geometry", "*geometry", XrmoptionSepArg, (XPointer) NULL},
4732     {"-background", "*background", XrmoptionSepArg, (XPointer) NULL},
4733     {"-bg", "*background", XrmoptionSepArg, (XPointer) NULL},
4734     {"-foreground", "*foreground", XrmoptionSepArg, (XPointer) NULL},
4735     {"-fg", "*foreground", XrmoptionSepArg, (XPointer) NULL},
4736     {"-bordercolor", "*bordercolor", XrmoptionSepArg, (XPointer) NULL},
4737     {"-bd", "*bordercolor", XrmoptionSepArg, (XPointer) NULL},
4738     {"-borderwidth", ".borderWidth", XrmoptionSepArg, (XPointer) NULL},
4739     {"-bw", ".borderWidth", XrmoptionSepArg, (XPointer) NULL},
4740     {"-font", "*font", XrmoptionSepArg, (XPointer) NULL},
4741     {"-fn", "*font", XrmoptionSepArg, (XPointer) NULL},
4742     {"-reverse", "*reverseVideo", XrmoptionNoArg, (XPointer) "on"},
4743     {"-rv", "*reverseVideo", XrmoptionNoArg, (XPointer) "on"},
4744     {"+rv", "*reverseVideo", XrmoptionNoArg, (XPointer) "off"},
4745     {"-iconic", "*iconic", XrmoptionNoArg, (XPointer) "on"},
4746     {"-synchronous", "*synchronous", XrmoptionNoArg, (XPointer) "on"},
4747     {"-xnllanguage", "*xnllanguage", XrmoptionSepArg, (XPointer) NULL},
4748     {"-selectionTimeout", "*selectionTimeout", XrmoptionSepArg, (XPointer) NULL},
4749     {"-title", ".title", XrmoptionSepArg, (XPointer) NULL},
4750     {"-xrm", NULL, XrmoptionResArg, (XPointer) NULL},
4751     {"-raise", "*raise", XrmoptionNoArg, (XPointer) "on"},
4752     {"-noraise", "*raise", XrmoptionNoArg, (XPointer) "off"},
4753     {"-feedback", "*feedback", XrmoptionNoArg, (XPointer) "on"},
4754     {"-nofeedback", "*feedback", XrmoptionNoArg, (XPointer) "off"},
4755     {"-ctrlq", "*ctrlq", XrmoptionNoArg, (XPointer) "on"},
4756     {"-dashed", "*dashed", XrmoptionNoArg, (XPointer) "on"},
4757     {"-solid", "*dashed", XrmoptionNoArg, (XPointer) "off"},
4758     {"-persist", "*persist", XrmoptionNoArg, (XPointer) "on"}
4759 };
4760
4761 #define Nopt (sizeof(options) / sizeof(options[0]))
4762
4763 static void
4764 preset(int argc, char *argv[])
4765 {
4766     int Argc = argc;
4767     char **Argv = argv;
4768
4769 #ifdef VMS
4770     char *ldisplay = (char *) 0;
4771 #else
4772     char *ldisplay = getenv("DISPLAY");
4773 #endif
4774     char *home = getenv("HOME");
4775     char *server_defaults, *env, buffer[256];
4776 #if 0
4777     Visual *TrueColor_vis, *PseudoColor_vis, *StaticGray_vis, *GrayScale_vis;
4778     int TrueColor_depth, PseudoColor_depth, StaticGray_depth, GrayScale_depth;
4779 #endif
4780     char *db_string;
4781
4782     FPRINTF((stderr, "(preset) \n"));
4783
4784     /* avoid bus error when env vars are not set */
4785     if (ldisplay == NULL)
4786         ldisplay = "";
4787     if (home == NULL)
4788         home = "";
4789
4790 /*---set to ignore ^C and ^Z----------------------------------------------*/
4791
4792     signal(SIGINT, SIG_IGN);
4793 #ifdef SIGTSTP
4794     signal(SIGTSTP, SIG_IGN);
4795 #endif
4796
4797 /*---prescan arguments for "-name"----------------------------------------*/
4798
4799     while (++Argv, --Argc > 0) {
4800         if (!strcmp(*Argv, "-name") && Argc > 1) {
4801             strncpy(X_Name, Argv[1], sizeof(X_Name) - 1);
4802             strncpy(X_Class, Argv[1], sizeof(X_Class) - 1);
4803             /* just in case */
4804             X_Name[sizeof(X_Name) - 1] = NUL;
4805             X_Class[sizeof(X_Class) - 1] = NUL;
4806             if (X_Class[0] >= 'a' && X_Class[0] <= 'z')
4807                 X_Class[0] -= 0x20;
4808         }
4809     }
4810     Argc = argc;
4811     Argv = argv;
4812
4813 /*---parse command line---------------------------------------------------*/
4814
4815     XrmInitialize();
4816     XrmParseCommand(&dbCmd, options, Nopt, X_Name, &Argc, Argv);
4817     if (Argc > 1) {
4818 #ifdef PIPE_IPC
4819         if (!strcmp(Argv[1], "-noevents")) {
4820             pipe_died = 1;
4821         } else {
4822 #endif
4823             fprintf(stderr, "\n\
4824 gnuplot: bad option: %s\n\
4825 gnuplot: X11 aborted.\n", Argv[1]);
4826             EXIT(1);
4827 #ifdef PIPE_IPC
4828         }
4829 #endif
4830     }
4831     if (pr_GetR(dbCmd, ".display"))
4832         ldisplay = (char *) value.addr;
4833
4834 /*---open display---------------------------------------------------------*/
4835
4836 #ifdef USE_MOUSE
4837     XSetErrorHandler(ErrorHandler);
4838 #endif
4839     dpy = XOpenDisplay(ldisplay);
4840     if (!dpy) {
4841         fprintf(stderr, "\n\
4842 gnuplot: unable to open display '%s'\n\
4843 gnuplot: X11 aborted.\n", ldisplay);
4844         EXIT(1);
4845     }
4846     scr = DefaultScreen(dpy);
4847     root = DefaultRootWindow(dpy);
4848     server_defaults = XResourceManagerString(dpy);
4849     vis = DefaultVisual(dpy, scr);
4850     dep = DefaultDepth(dpy, scr);
4851     default_cmap.colormap = DefaultColormap(dpy, scr);
4852     current_cmap = &default_cmap;
4853     max_request_size = XMaxRequestSize(dpy) / 2;
4854
4855
4856 /**** atoms we will need later ****/
4857
4858     WM_PROTOCOLS = XInternAtom(dpy, "WM_PROTOCOLS", False);
4859     WM_DELETE_WINDOW = XInternAtom(dpy, "WM_DELETE_WINDOW", False);
4860
4861
4862 /*---get application defaults--(subset of Xt processing)------------------*/
4863
4864 #ifdef VMS
4865     strcpy(buffer, "DECW$USER_DEFAULTS:GNUPLOT_X11.INI");
4866 #elif defined OS2
4867 /* for XFree86 ... */
4868     {
4869         char appdefdir[MAXPATHLEN];
4870         strncpy(appdefdir,
4871                 __XOS2RedirRoot("/XFree86/lib/X11/app-defaults"),
4872                 sizeof(appdefdir));
4873         sprintf(buffer, "%s/%s", appdefdir, "Gnuplot");
4874     }
4875 # else /* !OS/2 */
4876     strcpy(buffer, AppDefDir);
4877     strcat(buffer, "/");
4878     strcat(buffer, "Gnuplot");
4879 #endif /* !VMS */
4880
4881     dbApp = XrmGetFileDatabase(buffer);
4882     XrmMergeDatabases(dbApp, &db);
4883
4884 /*---get server or ~/.Xdefaults-------------------------------------------*/
4885
4886     if (server_defaults)
4887         dbDef = XrmGetStringDatabase(server_defaults);
4888     else {
4889 #ifdef VMS
4890         strcpy(buffer, "DECW$USER_DEFAULTS:DECW$XDEFAULTS.DAT");
4891 #else
4892         strcpy(buffer, home);
4893         strcat(buffer, "/.Xdefaults");
4894 #endif
4895         dbDef = XrmGetFileDatabase(buffer);
4896     }
4897     XrmMergeDatabases(dbDef, &db);
4898
4899 /*---get XENVIRONMENT or  ~/.Xdefaults-hostname---------------------------*/
4900
4901 #ifndef VMS
4902     if ((env = getenv("XENVIRONMENT")) != NULL)
4903         dbEnv = XrmGetFileDatabase(env);
4904     else {
4905         char *p = NULL, host[MAXHOSTNAMELEN];
4906
4907         if (GP_SYSTEMINFO(host) < 0) {
4908             fprintf(stderr, "gnuplot: %s failed. X11 aborted.\n", SYSINFO_METHOD);
4909             EXIT(1);
4910         }
4911         if ((p = strchr(host, '.')) != NULL)
4912             *p = '\0';
4913         strcpy(buffer, home);
4914         strcat(buffer, "/.Xdefaults-");
4915         strcat(buffer, host);
4916         dbEnv = XrmGetFileDatabase(buffer);
4917     }
4918     XrmMergeDatabases(dbEnv, &db);
4919 #endif /* not VMS */
4920
4921 /*---merge command line options-------------------------------------------*/
4922
4923     XrmMergeDatabases(dbCmd, &db);
4924
4925 /*---set geometry, font, colors, line widths, dash styles, point size-----*/
4926
4927     /* a specific visual can be forced by the X resource visual */
4928     db_string = pr_GetR(db, ".visual") ? (char *) value.addr : (char *) 0;
4929     if (db_string) {
4930         Visual *visual = (Visual *) 0;
4931         int depth = (int) 0;
4932         char **ptr = visual_name;
4933         int i;
4934         for (i = 0; *ptr; i++, ptr++) {
4935             if (!strcmp(db_string, *ptr)) {
4936 #if 0
4937                 if (DirectColor == i) {
4938                     fprintf(stderr, "DirectColor not supported by pm3d, using default.\n");
4939                 } else
4940 #endif
4941                 if (GetVisual(i, &visual, &depth)) {
4942                     vis = visual;
4943                     dep = depth;
4944                     if (vis != DefaultVisual(dpy, scr)) {
4945                         /* this will be the default colormap */
4946                         default_cmap.colormap = XCreateColormap(dpy, root, vis, AllocNone);
4947                     }
4948                 } else {
4949                     fprintf(stderr, "%s not supported by %s, using default.\n", *ptr, ldisplay);
4950                 }
4951                 break;
4952             }
4953         }
4954     }
4955 #if 0
4956     if (DirectColor == vis->class) {
4957         have_pm3d = 0;
4958     }
4959 #endif
4960 #if 0
4961     /* removed this message as it is annoying
4962      * when using gnuplot in a pipe (joze) */
4963     if (vis->class < (sizeof(visual_name) / sizeof(char **)) - 1) {
4964         fprintf(stderr, "Using %s at depth %d.\n", visual_name[vis->class], dep);
4965     }
4966 #endif
4967     CmapClear(&default_cmap);
4968
4969     /* set default of maximal_possible_colors */
4970     if (dep > 12) {
4971         maximal_possible_colors = 0x200;
4972     } else if (dep > 8) {
4973         maximal_possible_colors = 0x100;
4974     } else {
4975         /* will be something like PseudoColor * 8 */
4976         maximal_possible_colors = 240;  /* leave 16 for line colors */
4977     }
4978
4979     /* check database for maxcolors */
4980     db_string = pr_GetR(db, ".maxcolors") ? (char *) value.addr : (char *) 0;
4981     if (db_string) {
4982         int itmp;
4983         if (sscanf(db_string, "%d", &itmp)) {
4984             if (itmp <= 0) {
4985                 fprintf(stderr, "\nmaxcolors must be strictly positive.\n");
4986             } else if (itmp > pow((double) 2, (double) dep)) {
4987                 fprintf(stderr, "\noops, cannot use this many colors on a %d bit deep display.\n", dep);
4988             } else {
4989                 maximal_possible_colors = itmp;
4990             }
4991         } else {
4992             fprintf(stderr, "\nunable to parse '%s' as integer\n", db_string);
4993         }
4994     }
4995
4996     /* setting a default for minimal_possible_colors */
4997     minimal_possible_colors = maximal_possible_colors / (num_colormaps > 1 ? 2 : 8);    /* 0x20 / 30 */
4998     /* check database for mincolors */
4999     db_string = pr_GetR(db, ".mincolors") ? (char *) value.addr : (char *) 0;
5000     if (db_string) {
5001         int itmp;
5002         if (sscanf(db_string, "%d", &itmp)) {
5003             if (itmp <= 0) {
5004                 fprintf(stderr, "\nmincolors must be strictly positive.\n");
5005             } else if (itmp > pow((double) 2, (double) dep)) {
5006                 fprintf(stderr, "\noops, cannot use this many colors on a %d bit deep display.\n", dep);
5007             } else if (itmp > maximal_possible_colors) {
5008                 fprintf(stderr, "\nmincolors must be <= %d\n", maximal_possible_colors);
5009             } else {
5010                 minimal_possible_colors = itmp;
5011             }
5012         } else {
5013             fprintf(stderr, "\nunable to parse '%s' as integer\n", db_string);
5014         }
5015     }
5016
5017     pr_geometry(NULL);
5018     pr_encoding();              /* check for global default encoding */
5019     pr_font(NULL);              /* set current font to default font */
5020     pr_color(&default_cmap);    /* set colors for default colormap */
5021     pr_width();
5022     pr_dashes();
5023     pr_pointsize();
5024     pr_raise();
5025     pr_persist();
5026     pr_feedback();
5027     pr_ctrlq();
5028     pr_fastrotate();
5029 #ifdef EXPORT_SELECTION
5030     pr_exportselection();
5031 #endif
5032
5033 }
5034
5035 /*-----------------------------------------------------------------------------
5036  *   pr_GetR - get resource from database using "-name" option (if any)
5037  *---------------------------------------------------------------------------*/
5038
5039 static char *
5040 pr_GetR(XrmDatabase xrdb, char *resource)
5041 {
5042     char name[128], class[128], *rc;
5043
5044     strcpy(name, X_Name);
5045     strcat(name, resource);
5046     strcpy(class, X_Class);
5047     strcat(class, resource);
5048     rc = XrmGetResource(xrdb, name, class, type, &value)
5049         ? (char *) value.addr : (char *) 0;
5050     return (rc);
5051 }
5052
5053 /*-----------------------------------------------------------------------------
5054  *   pr_color - determine color values
5055  *---------------------------------------------------------------------------*/
5056
5057 static const char color_keys[Ncolors][30] = {
5058     "background", "bordercolor", "text", "border", "axis",
5059     "line1", "line2", "line3", "line4",
5060     "line5", "line6", "line7", "line8"
5061 };
5062 static char color_values[Ncolors][30] = {
5063     "white", "black", "black", "black", "black",
5064     "red", "green", "blue", "magenta",
5065     "cyan", "sienna", "orange", "coral"
5066 };
5067 static char color_values_rv[Ncolors][30] = {
5068     "black", "white", "white", "white", "white",
5069     "red", "green", "blue", "magenta",
5070     "cyan", "sienna", "orange", "coral"
5071 };
5072 static char gray_values[Ncolors][30] = {
5073     "black", "white", "white", "gray50", "gray50",
5074     "gray100", "gray60", "gray80", "gray40",
5075     "gray90", "gray50", "gray70", "gray30"
5076 };
5077
5078 static void
5079 pr_color(cmap_t * cmap_ptr)
5080 {
5081     unsigned long black = BlackPixel(dpy, scr), white = WhitePixel(dpy, scr);
5082     char option[20], color[30], *v, *ctype;
5083     XColor xcolor;
5084     double intensity = -1;
5085     int n;
5086
5087     if (pr_GetR(db, ".mono") && On(value.addr))
5088         Mono++;
5089     if (pr_GetR(db, ".gray") && On(value.addr))
5090         Gray++;
5091     if (pr_GetR(db, ".reverseVideo") && On(value.addr))
5092         Rv++;
5093
5094     if (!Gray && (vis->class == GrayScale || vis->class == StaticGray))
5095         Mono++;
5096
5097     if (!Mono) {
5098
5099         ctype = (Gray) ? "Gray" : "Color";
5100
5101         if (current_cmap != cmap_ptr) {
5102             /* for private colormaps: make sure
5103              * that pixel 0 gets black (joze) */
5104             xcolor.red = 0;
5105             xcolor.green = 0;
5106             xcolor.blue = 0;
5107             XAllocColor(dpy, cmap_ptr->colormap, &xcolor);
5108         }
5109
5110         for (n = 0; n < Ncolors; n++) {
5111             strcpy(option, ".");
5112             strcat(option, color_keys[n]);
5113             if (n > 1)
5114                 strcat(option, ctype);
5115             v = pr_GetR(db, option) ? (char *) value.addr
5116                 : ((Gray) ? gray_values[n]
5117                    : (Rv ? color_values_rv[n] : color_values[n]));
5118
5119             if (sscanf(v, "%30[^, ], %lf", color, &intensity) == 2) {
5120                 if (intensity < 0 || intensity > 1) {
5121                     fprintf(stderr, "\ngnuplot: invalid color intensity in '%s'\n", color);
5122                     intensity = 1;
5123                 }
5124             } else {
5125                 strcpy(color, v);
5126                 intensity = 1;
5127             }
5128
5129             if (!XParseColor(dpy, cmap_ptr->colormap, color, &xcolor)) {
5130                 fprintf(stderr, "\ngnuplot: unable to parse '%s'. Using black.\n", color);
5131                 cmap_ptr->colors[n] = black;
5132             } else {
5133                 xcolor.red *= intensity;
5134                 xcolor.green *= intensity;
5135                 xcolor.blue *= intensity;
5136                 if (XAllocColor(dpy, cmap_ptr->colormap, &xcolor)) {
5137                     cmap_ptr->colors[n] = xcolor.pixel;
5138                     cmap_ptr->rgbcolors[n] = ((xcolor.red>>8 & 0xff) << 16)
5139                                            + ((xcolor.green>>8 & 0xff) << 8)
5140                                            + (xcolor.blue>>8);
5141                 } else {
5142                     fprintf(stderr, "\ngnuplot: can't allocate '%s'. Using black.\n", v);
5143                     cmap_ptr->colors[n] = black;
5144                 }
5145             }
5146         }
5147     } else {
5148         cmap_ptr->colors[0] = (Rv) ? black : white;
5149         for (n = 1; n < Ncolors; n++)
5150             cmap_ptr->colors[n] = (Rv) ? white : black;
5151     }
5152 #ifdef USE_MOUSE
5153     {
5154         /* create the xor GC just for allocating the xor value
5155          * before a palette is created. This way the xor foreground
5156          * will be available. */
5157         AllocateXorPixel(cmap_ptr);
5158     }
5159 #endif
5160 }
5161
5162 /*-----------------------------------------------------------------------------
5163  *   pr_dashes - determine line dash styles
5164  *---------------------------------------------------------------------------*/
5165
5166 static const char dash_keys[Ndashes][10] = {
5167     "border", "axis",
5168     "line1", "line2", "line3", "line4", "line5", "line6", "line7", "line8"
5169 };
5170
5171 static char dash_mono[Ndashes][10] = {
5172     "0", "16",
5173     "0", "42", "13", "44", "15", "4441", "42", "13"
5174 };
5175
5176 static char dash_color[Ndashes][10] = {
5177     "0", "16",
5178     "0", "0", "0", "0", "0", "0", "0", "0"
5179 };
5180
5181 static void
5182 pr_dashes()
5183 {
5184     int n, j, l, ok;
5185     char option[20], *v;
5186
5187     if (pr_GetR(db, ".dashed")) {
5188         dashedlines = (!strncasecmp(value.addr, "on", 2) || !strncasecmp(value.addr, "true", 4));
5189     }
5190
5191     for (n = 0; n < Ndashes; n++) {
5192         strcpy(option, ".");
5193         strcat(option, dash_keys[n]);
5194         strcat(option, "Dashes");
5195         v = pr_GetR(db, option)
5196             ? (char *) value.addr : ((Mono) ? dash_mono[n] : dash_color[n]);
5197         l = strlen(v);
5198         if (l == 1 && *v == '0') {
5199             dashes[n][0] = (unsigned char) 0;
5200             continue;
5201         }
5202         for (ok = 0, j = 0; j < l; j++) {
5203             if (v[j] >= '1' && v[j] <= '9')
5204                 ok++;
5205         }
5206         if (ok != l || (ok != 2 && ok != 4)) {
5207             fprintf(stderr, "gnuplot: illegal dashes value %s:%s\n",
5208                     option, v);
5209             dashes[n][0] = (unsigned char) 0;
5210             continue;
5211         }
5212         for (j = 0; j < l; j++) {
5213             dashes[n][j] = (unsigned char) (v[j] - '0');
5214         }
5215         dashes[n][l] = (unsigned char) 0;
5216     }
5217 }
5218
5219 /*-----------------------------------------------------------------------------
5220  *   pr_font - determine font
5221  *---------------------------------------------------------------------------*/
5222
5223 /* wrapper functions */
5224 int gpXTextWidth (XFontStruct *cfont, const char *str, int count)
5225 {
5226 #ifdef USE_X11_MULTIBYTE
5227     if (usemultibyte)
5228         return XmbTextEscapement(mbfont, str, count);
5229 #endif
5230     return XTextWidth(cfont, str, count);
5231 }
5232
5233 int gpXTextHeight (XFontStruct *cfont)
5234 {
5235 #ifdef USE_X11_MULTIBYTE
5236     static XFontSetExtents *extents;
5237     if (usemultibyte) {
5238         extents = XExtentsOfFontSet(mbfont);
5239         return extents->max_logical_extent.height;
5240     } else
5241 #endif
5242         return cfont->ascent + cfont->descent;
5243 }
5244
5245 void gpXSetFont (Display *disp, GC gc, Font fontid)
5246 {
5247 #ifdef USE_X11_MULTIBYTE
5248     if (!usemultibyte)
5249 #endif
5250         XSetFont(disp, gc, fontid);
5251 }
5252
5253 void gpXDrawImageString (Display *disp, Drawable d, GC gc, int x, int y,
5254                          const char *str, int len)
5255 {
5256 #ifdef USE_X11_MULTIBYTE
5257     if (usemultibyte) {
5258         XmbDrawImageString(disp, d, mbfont, gc, x, y, str, len);
5259         return;
5260     }
5261 #endif
5262     XDrawImageString(disp, d, gc, x, y, str, len);
5263 }
5264
5265 void gpXDrawString (Display *disp, Drawable d, GC gc, int x, int y,
5266                     const char *str, int len)
5267 {
5268 #ifdef USE_X11_MULTIBYTE
5269     if (usemultibyte) {
5270         XmbDrawString(disp, d, mbfont, gc, x, y, str, len);
5271         return;
5272     }
5273 #endif
5274     XDrawString(disp, d, gc, x, y, str, len);
5275 }
5276
5277 void gpXFreeFont(Display *disp, XFontStruct *cfont)
5278 {
5279 #ifndef USE_X11_MULTIBYTE
5280     if (cfont)
5281         XFreeFont(disp, cfont);
5282 #else
5283     if (font) {
5284         XFreeFont(disp, font);
5285         font=NULL;
5286     }
5287     if (mbfont) {
5288         XFreeFontSet(disp, mbfont);
5289         mbfont=NULL;
5290     }
5291 #endif
5292 }
5293
5294 XFontStruct *gpXLoadQueryFont (Display *disp, char *fontname)
5295 {
5296 #ifndef USE_X11_MULTIBYTE
5297     return XLoadQueryFont(disp, fontname);
5298 #else
5299     static char **miss, *def;
5300     static TBOOLEAN first_time = TRUE;
5301     int n_miss;
5302     char tmpfname[256];
5303
5304     if (!usemultibyte)
5305         return XLoadQueryFont(disp, fontname);
5306     else {
5307         fontset_transsep(tmpfname, fontname, 256-1);
5308         mbfont = XCreateFontSet(disp, tmpfname, &miss, &n_miss, &def);
5309
5310         /* This test seemed to make sense for Japanese locales, which only */
5311         /* claim to require a small number of character sets.  But it is   */
5312         /* highly likely to fail for more generic locales like en_US.UTF-8 */
5313         /* that claim to "require" about 2 dozen obscure character sets.   */
5314         /* EAM - do not fail the request; just continue after a warning.   */
5315         if (n_miss>0) {
5316 #if (0)
5317             if (mbfont) {
5318                 XFreeFontSet(disp, mbfont);
5319                 mbfont=NULL;
5320             }
5321 #else
5322             if (first_time) {
5323                 fprintf(stderr,"gnuplot_x11: Some character sets not available\n");
5324                 first_time = FALSE;
5325             }
5326             while (n_miss-- > 0)
5327                 FPRINTF((stderr,"Missing charset: %s\n", miss[n_miss]));
5328 #endif
5329             XFreeStringList(miss);
5330         }
5331
5332         return NULL;
5333     }
5334 #endif
5335 }
5336
5337 char *gpFallbackFont(void)
5338 {
5339 #ifdef USE_X11_MULTIBYTE
5340     if (usemultibyte)
5341         return FallbackFontMB;
5342 #endif
5343     return FallbackFont;
5344 }
5345
5346 int gpXGetFontascent(XFontStruct *cfont)
5347 {
5348 #ifndef USE_X11_MULTIBYTE
5349     return cfont->ascent;
5350 #else
5351     static XFontStruct **eachfonts;
5352     char **fontnames;
5353     int max_ascent = 0;
5354     int i, n_fonts;
5355
5356     if(!usemultibyte) return font->ascent;
5357     n_fonts = XFontsOfFontSet(mbfont, &eachfonts, &fontnames);
5358     for (i = 0; i < n_fonts; i++) {
5359         if (eachfonts[i]->ascent > max_ascent)
5360           max_ascent = eachfonts[i]->ascent;
5361     }
5362     return max_ascent;
5363 #endif
5364 }
5365
5366 #ifdef USE_X11_MULTIBYTE
5367 int fontset_transsep(char *nfname, char *ofname, int n)
5368 {
5369     char *s;
5370
5371     strncpy(nfname, ofname, n);
5372     if (nfname[n-1]!='\0')
5373         nfname[n]='\0';
5374     if (strchr(nfname, ','))
5375         return 1;
5376     s = nfname;
5377     while ((s = strchr(nfname, FontSetSep)) != NULL){
5378         *s = ',';
5379         nfname = s;
5380     }
5381     return 0;
5382 }
5383 #endif
5384
5385 static void
5386 pr_encoding()
5387 {
5388     char *encoding;
5389     if ((encoding = pr_GetR(db, ".encoding"))) {
5390         strncpy(default_encoding, encoding, sizeof(default_encoding)-1);
5391     }
5392 }
5393
5394 static void
5395 pr_font( fontname )
5396 char *fontname;
5397 {
5398     static char previous_font_name[128];
5399     char fontspec[128];
5400     int  fontsize = 0;
5401 #ifdef USE_X11_MULTIBYTE
5402     char *orgfontname = NULL;
5403 #endif
5404
5405     if (!fontname || !(*fontname))
5406         fontname = default_font;
5407
5408     if (!fontname || !(*fontname)) {
5409         if ((fontname = pr_GetR(db, ".font")))
5410             strncpy(default_font, fontname, sizeof(default_font)-1);
5411             FPRINTF((stderr, "gnuplot_x11: setting default font %s from Xresource\n",
5412                     fontname));
5413     }
5414
5415 #ifdef USE_X11_MULTIBYTE
5416     if (fontname && strncmp(fontname, "mbfont:", 7) == 0) {
5417         if (multibyte_fonts_usable) {
5418             usemultibyte = 1;
5419             orgfontname = fontname;
5420             fontname = &fontname[7];
5421         } else {
5422             usemultibyte=0;
5423             fontname=NULL;
5424         }
5425     } else usemultibyte=0;
5426 #endif
5427     if (!fontname)
5428       fontname = gpFallbackFont();
5429
5430     /* EAM DEBUG - Free previous font before searching for a new one. */
5431     /* This doesn't seem right, since we will probably need it again  */
5432     /* very soon. But if we don't free it, we gradually leak memory.  */
5433     gpXFreeFont(dpy, font);
5434
5435     font = gpXLoadQueryFont(dpy, fontname);
5436
5437 #ifndef USE_X11_MULTIBYTE
5438     if (!font) {
5439 #else
5440     if (!font && !mbfont && !strchr(fontname, FontSetSep)) {
5441 #endif
5442         /* EAM 19-Aug-2002 Try to construct a plausible X11 full font spec */
5443         /* We are passed "font<, size><, slant>"                             */
5444         char shortname[64], *fontencoding, slant, *weight;
5445         int  sep;
5446 #ifdef USE_X11_MULTIBYTE
5447         int backfont = 0;
5448 #endif
5449
5450         /* Enhanced font processing wants a method of requesting a new size  */
5451         /* for whatever the previously selected font was, so we have to save */
5452         /* and reuse the previous font name to construct the new spec.       */
5453         if (!strncmp(fontname, "DEFAULT", 7)) {
5454             sscanf(&fontname[8], "%d", &fontsize);
5455             fontname = default_font;
5456 #ifdef USE_X11_MULTIBYTE
5457             backfont = 1;
5458 #endif
5459         } else if (*fontname == ',') {
5460             sscanf(&fontname[1], "%d", &fontsize);
5461             fontname = previous_font_name;
5462 #ifdef USE_X11_MULTIBYTE
5463             backfont = 1;
5464 #endif
5465         }
5466 #ifdef USE_X11_MULTIBYTE
5467         if (backfont && fontname && strncmp(fontname, "mbfont:", 7) == 0
5468             && multibyte_fonts_usable) {
5469             usemultibyte = 1;
5470             orgfontname = fontname;
5471             fontname = &fontname[7];
5472         }
5473 #endif
5474
5475         sep = strcspn(fontname, ",");
5476         if (sep >= sizeof(shortname))
5477             sep = sizeof(shortname) - 1;
5478         strncpy(shortname, fontname, sep);
5479         shortname[sep] = '\0';
5480         if (!fontsize)
5481             sscanf( &(fontname[sep+1]), "%d", &fontsize);
5482         if (fontsize > 99 || fontsize < 1)
5483             fontsize = 12;
5484
5485         slant = strstr(&fontname[sep+1], "italic")  ? 'i' :
5486                 strstr(&fontname[sep+1], "oblique") ? 'o' :
5487                                                      'r' ;
5488
5489         weight = strstr(&fontname[sep+1], "bold")   ? "bold" :
5490                  strstr(&fontname[sep+1], "medium") ? "medium" :
5491                                                      "*" ;
5492
5493         if (!strncmp("Symbol", shortname, 6) || !strncmp("symbol", shortname, 6))
5494             fontencoding = "*-*";
5495 #ifdef USE_X11_MULTIBYTE
5496         else if (usemultibyte)
5497             fontencoding = "*-*";
5498 #endif
5499         else
5500             fontencoding = (
5501                 encoding == S_ENC_CP437     ? "dosencoding-cp437" :
5502                 encoding == S_ENC_CP850     ? "dosencoding-cp850" :
5503                 encoding == S_ENC_ISO8859_1 ? "iso8859-1" :
5504                 encoding == S_ENC_ISO8859_2 ? "iso8859-2" :
5505                 encoding == S_ENC_ISO8859_15 ? "iso8859-15" :
5506                 encoding == S_ENC_KOI8_R    ? "koi8-r" :
5507                 encoding == S_ENC_KOI8_U    ? "koi8-u" :
5508                 default_encoding[0] ? default_encoding :
5509                 "*-*" ) ;
5510
5511         sprintf(fontspec, "-*-%s-%s-%c-*-*-%d-*-*-*-*-*-%s",
5512                 shortname, weight, slant, fontsize, fontencoding
5513                 );
5514         font = gpXLoadQueryFont(dpy, fontspec);
5515
5516 #ifndef USE_X11_MULTIBYTE
5517         if (!font) {
5518 #else
5519         if (!font && !mbfont) {
5520 #endif
5521             /* Try to decode some common PostScript font names */
5522             if (!strcmp("Times-Bold", shortname)
5523                 || !strcmp("times-bold", shortname)) {
5524                 sprintf(fontspec, "-*-times-bold-r-*-*-%d-*-*-*-*-*-%s",
5525                         fontsize, fontencoding);
5526             } else if (!strcmp("Times-Roman", shortname)
5527                        || !strcmp("times-roman", shortname)) {
5528                 sprintf(fontspec, "-*-times-medium-r-*-*-%d-*-*-*-*-*-%s",
5529                         fontsize, fontencoding);
5530             } else if (!strcmp("Times-Italic", shortname)
5531                        || !strcmp("times-italic", shortname)) {
5532                 sprintf(fontspec, "-*-times-medium-i-*-*-%d-*-*-*-*-*-%s",
5533                         fontsize, fontencoding);
5534             } else if (!strcmp("Times-BoldItalic", shortname)
5535                        || !strcmp("times-bolditalic", shortname)) {
5536                 sprintf(fontspec, "-*-times-bold-i-*-*-%d-*-*-*-*-*-%s",
5537                         fontsize, fontencoding);
5538             } else if (!strcmp("Helvetica-Bold", shortname) ||
5539                        !strcmp("helvetica-bold", shortname)) {
5540                 sprintf(fontspec, "-*-helvetica-bold-r-*-*-%d-*-*-*-*-*-%s",
5541                         fontsize, fontencoding);
5542             } else if (!strcmp("Helvetica-Oblique", shortname)
5543                        || !strcmp("helvetica-oblique", shortname)) {
5544                 sprintf(fontspec, "-*-helvetica-medium-o-*-*-%d-*-*-*-*-*-%s",
5545                         fontsize, fontencoding);
5546             } else if (!strcmp("Helvetica-BoldOblique", shortname)
5547                        || !strcmp("helvetica-boldoblique", shortname)) {
5548                 sprintf(fontspec, "-*-helvetica-bold-o-*-*-%d-*-*-*-*-*-%s",
5549                         fontsize, fontencoding);
5550             } else if (!strcmp("Helvetica-Narrow-Bold", shortname)
5551                        || !strcmp("helvetica-narrow-bold", shortname)) {
5552                 sprintf(fontspec, "-*-arial narrow-bold-r-*-*-%d-*-*-*-*-*-%s",
5553                         fontsize, fontencoding);
5554             }
5555 #ifdef USE_X11_MULTIBYTE
5556             /* Japanese standard PostScript font names (adviced from
5557              * N.Matsuda). */
5558             else if (multibyte_fonts_usable
5559                      && (!strncmp("Ryumin-Light", shortname,
5560                                   strlen("Ryumin-Light"))
5561                          || !strncmp("ryumin-light", shortname,
5562                                      strlen("ryumin-light")))) {
5563                 if (!usemultibyte) {
5564                     usemultibyte = 1;
5565                     orgfontname = fontname;
5566                 }
5567                 sprintf(fontspec, "-*-mincho-medium-%c-*--%d-*",
5568                         slant, fontsize);
5569             }
5570             else if (multibyte_fonts_usable
5571                      && (!strncmp("GothicBBB-Medium", shortname,
5572                                   strlen("GothicBBB-Medium"))
5573                          || !strncmp("gothicbbb-medium", shortname,
5574                                      strlen("gothicbbb-medium")))) {
5575                 if (!usemultibyte) {
5576                     usemultibyte = 1;
5577                     orgfontname = fontname;
5578                 }
5579                 /* FIXME: Doesn't work on most non-japanese setups, because */
5580                 /* many purely Western fonts are gothic-bold.               */
5581                 sprintf(fontspec, "-*-gothic-bold-%c-*--%d-*", slant, fontsize);
5582             }
5583 #endif /* USE_X11_MULTIBYTE */
5584             font = gpXLoadQueryFont(dpy, fontspec);
5585
5586 #ifdef USE_X11_MULTIBYTE
5587             if (usemultibyte && !mbfont) {
5588                 /* But (mincho|gothic) X fonts are not provided
5589                  * on some X servers even in Japan
5590                  */
5591                 sprintf(fontspec, "*-%s-%c-*--%d-*", weight, slant, fontsize);
5592                 font = gpXLoadQueryFont(dpy, fontspec);
5593             }
5594 #endif /* USE_X11_MULTIBYTE */
5595         }
5596
5597     }
5598
5599 #ifndef USE_X11_MULTIBYTE
5600     if (font) {
5601 #else
5602     if (font || mbfont) {
5603         if (usemultibyte && orgfontname)
5604           fontname = orgfontname;
5605 #endif
5606         strncpy(previous_font_name, fontname, sizeof(previous_font_name)-1);
5607         FPRINTF((stderr, "gnuplot_x11:saving current font name \"%s\"\n", previous_font_name));
5608     }
5609
5610     /* By now we have tried everything we can to honor the specific request. */
5611     /* Try some common scaleable fonts before falling back to a last resort  */
5612     /* fixed font.                                                           */
5613 #ifndef USE_X11_MULTIBYTE
5614     if (!font) {
5615 #else
5616     if (!usemultibyte && !font) {
5617 #endif
5618         sprintf(fontspec, "-*-bitstream vera sans-bold-r-*-*-%d-*-*-*-*-*-*-*", fontsize);
5619         font = gpXLoadQueryFont(dpy, fontspec);
5620         fontname = fontspec;
5621         if (!font) {
5622             sprintf(fontspec, "-*-arial-medium-r-*-*-%d-*-*-*-*-*-*-*", fontsize);
5623             font = gpXLoadQueryFont(dpy, fontspec);
5624         }
5625         if (!font) {
5626             sprintf(fontspec, "-*-helvetica-medium-r-*-*-%d-*-*-*-*-*-*", fontsize);
5627             font = gpXLoadQueryFont(dpy, fontspec);
5628         }
5629         if (!font) {
5630             font = gpXLoadQueryFont(dpy, gpFallbackFont());
5631             fontname = gpFallbackFont();
5632         }
5633         if (!font) {
5634             fprintf(stderr, "\ngnuplot_x11: can't find usable font - X11 aborted.\n");
5635             EXIT(1);
5636         }
5637         FPRINTF((stderr, "\ngnuplot_x11: requested font not found, using '%s' instead.\n", fontname));
5638     }
5639 #ifdef USE_X11_MULTIBYTE
5640     if (usemultibyte && !mbfont) { /* multibyte font setting */
5641         font = gpXLoadQueryFont(dpy, gpFallbackFont());
5642         if (!mbfont) {
5643             usemultibyte=0;
5644             font = gpXLoadQueryFont(dpy, gpFallbackFont());
5645             if (!font) {
5646                 fprintf(stderr, "\ngnuplot_x11: can't find usable font - X11 aborted.\n");
5647                 EXIT(1);
5648             }
5649         }
5650         fontname = gpFallbackFont();
5651     }
5652 #endif /* USE_X11_MULTIBYTE */
5653
5654     vchar = gpXTextHeight(font);
5655     hchar = gpXTextWidth(font, "0123456789", 10) / 10;
5656
5657     FPRINTF((stderr, "gnuplot_x11: pr_font() set font %s, vchar %d hchar %d\n",
5658                 fontname, vchar, hchar));
5659
5660 }
5661
5662 /*-----------------------------------------------------------------------------
5663  *   pr_geometry - determine window geometry
5664  *---------------------------------------------------------------------------*/
5665
5666 static void
5667 pr_geometry(char *instr)
5668 {
5669     char *geometry = (instr != NULL)? instr : pr_GetR(db, ".geometry");
5670     int x, y, flags;
5671     unsigned int w, h;
5672
5673     if (geometry) {
5674         flags = XParseGeometry(geometry, &x, &y, &w, &h);
5675         if (flags & WidthValue)
5676             gW = w;
5677         if (flags & HeightValue)
5678             gH = h;
5679         if (flags & (WidthValue | HeightValue))
5680             gFlags = (gFlags & ~PSize) | USSize;
5681
5682         if (flags & XValue)
5683             gX = (flags & XNegative) ? x + DisplayWidth(dpy, scr) - gW - BorderWidth * 2 : x;
5684
5685         if (flags & YValue)
5686             gY = (flags & YNegative) ? y + DisplayHeight(dpy, scr) - gH - BorderWidth * 2 : y;
5687
5688         if (flags & (XValue | YValue))
5689             gFlags = (gFlags & ~PPosition) | USPosition;
5690     }
5691 }
5692
5693 /*-----------------------------------------------------------------------------
5694  *   pr_pointsize - determine size of points for 'points' plotting style
5695  *---------------------------------------------------------------------------*/
5696
5697 static void
5698 pr_pointsize()
5699 {
5700     if (pr_GetR(db, ".pointsize")) {
5701         if (sscanf((char *) value.addr, "%lf", &pointsize) == 1) {
5702             if (pointsize <= 0 || pointsize > 10) {
5703                 fprintf(stderr, "\ngnuplot: invalid pointsize '%s'\n", value.addr);
5704                 pointsize = 1;
5705             }
5706         } else {
5707             fprintf(stderr, "\ngnuplot: invalid pointsize '%s'\n", value.addr);
5708             pointsize = 1;
5709         }
5710     } else {
5711         pointsize = 1;
5712     }
5713 }
5714
5715 /*-----------------------------------------------------------------------------
5716  *   pr_width - determine line width values
5717  *---------------------------------------------------------------------------*/
5718
5719 static const char width_keys[Nwidths][30] = {
5720     "border", "axis",
5721     "line1", "line2", "line3", "line4", "line5", "line6", "line7", "line8"
5722 };
5723
5724 static void
5725 pr_width()
5726 {
5727     int n;
5728     char option[20], *v;
5729
5730     for (n = 0; n < Nwidths; n++) {
5731         strcpy(option, ".");
5732         strcat(option, width_keys[n]);
5733         strcat(option, "Width");
5734         if ((v = pr_GetR(db, option)) != NULL) {
5735             if (*v < '0' || *v > '9' || strlen(v) > 1)
5736                 fprintf(stderr, "gnuplot: illegal width value %s:%s\n", option, v);
5737             else
5738                 widths[n] = (unsigned int) atoi(v);
5739         }
5740     }
5741 }
5742
5743 /*-----------------------------------------------------------------------------
5744  *   pr_window - create window
5745  *---------------------------------------------------------------------------*/
5746 static void
5747 ProcessEvents(Window win)
5748 {
5749     XSelectInput(dpy, win, KeyPressMask | KeyReleaseMask
5750                  | StructureNotifyMask | PointerMotionMask | PointerMotionHintMask
5751                  | ButtonPressMask | ButtonReleaseMask | ExposureMask | EnterWindowMask);
5752     XSync(dpy, 0);
5753 }
5754
5755 static void
5756 pr_window(plot_struct *plot)
5757 {
5758     char *title = pr_GetR(db, ".title");
5759     static XSizeHints hints;
5760     int Tvtwm = 0;
5761
5762     FPRINTF((stderr, "(pr_window) \n"));
5763
5764     if (have_pm3d) {
5765         XSetWindowAttributes attr;
5766         unsigned long mask = CWBackPixel | CWBorderPixel | CWColormap;
5767         attr.background_pixel = plot->cmap->colors[0];
5768         attr.border_pixel = plot->cmap->colors[1];
5769         attr.colormap = plot->cmap->colormap;
5770         plot->window = XCreateWindow(dpy, root, plot->x, plot->y, plot->width,
5771                                      plot->height, BorderWidth, dep, InputOutput, vis, mask, &attr);
5772     } else
5773         plot->window = XCreateSimpleWindow(dpy, root, plot->x, plot->y,
5774                                            plot->width, plot->height, BorderWidth, plot->cmap->colors[1], plot->cmap->colors[0]);
5775
5776     /* Return if something wrong. */
5777     if (!plot->window)
5778         return;
5779
5780     /* ask ICCCM-compliant window manager to tell us when close window
5781      * has been chosen, rather than just killing us
5782      */
5783
5784     XChangeProperty(dpy, plot->window, WM_PROTOCOLS, XA_ATOM, 32, PropModeReplace, (unsigned char *) &WM_DELETE_WINDOW, 1);
5785
5786     if (pr_GetR(db, ".clear") && On(value.addr))
5787         Clear++;
5788     if (pr_GetR(db, ".tvtwm") && On(value.addr))
5789         Tvtwm++;
5790
5791     if (!Tvtwm) {
5792         hints.flags = plot->posn_flags;
5793     } else {
5794         hints.flags = (plot->posn_flags & ~USPosition) | PPosition;     /* ? */
5795     }
5796     hints.x = gX;
5797     hints.y = gY;
5798     hints.width = plot->width;
5799     hints.height = plot->height;
5800
5801     XSetNormalHints(dpy, plot->window, &hints);
5802
5803     if (pr_GetR(db, ".iconic") && On(value.addr)) {
5804         XWMHints wmh;
5805
5806         wmh.flags = StateHint;
5807         wmh.initial_state = IconicState;
5808         XSetWMHints(dpy, plot->window, &wmh);
5809     }
5810 #if 0 /* 1 clear, 0 do not clear */
5811     if (plot->titlestring) {
5812         free(plot->titlestring);
5813         plot->titlestring = 0;
5814     }
5815 #endif
5816
5817     ProcessEvents(plot->window);
5818
5819     /* If title doesn't exist, create one. */
5820 #if 1
5821 #define ICON_TEXT "gplt"
5822 #define TEMP_NUM_LEN 16
5823     {
5824     /* append the X11 terminal number (if greater than zero) */
5825     char numstr[sizeof(ICON_TEXT)+TEMP_NUM_LEN+1]; /* space for text, number and terminating \0 */
5826     if (plot->plot_number)
5827         sprintf(numstr, "%s%d%c", ICON_TEXT, plot->plot_number, '\0');
5828     else
5829         sprintf(numstr, "%s%c", ICON_TEXT, '\0');
5830     FPRINTF((stderr, "term_number is %d", plot->plot_number));
5831     XSetIconName(dpy, plot->window, numstr);
5832 #undef TEMP_NUM_LEN
5833     if (!plot->titlestring) {
5834         int orig_len;
5835         if (!title) title = X_Class;
5836         orig_len = strlen(title);
5837         /* memory for text, white space, number and terminating \0 */
5838         if ((plot->titlestring = (char *) malloc(orig_len + ((orig_len && plot->plot_number) ? 1 : 0) + strlen(numstr) - strlen(ICON_TEXT) + 1))) {
5839             strcpy(plot->titlestring, title);
5840             if (orig_len && plot->plot_number)
5841                 plot->titlestring[orig_len++] = ' ';
5842             strcpy(plot->titlestring + orig_len, numstr + strlen(ICON_TEXT));
5843             XStoreName(dpy, plot->window, plot->titlestring);
5844         } else
5845             XStoreName(dpy, plot->window, title);
5846     } else {
5847         XStoreName(dpy, plot->window, plot->titlestring);
5848     }
5849     }
5850 #undef ICON_TEXT
5851 #endif
5852
5853     XMapWindow(dpy, plot->window);
5854
5855     windows_open++;
5856 }
5857
5858
5859 /***** pr_raise ***/
5860 static void
5861 pr_raise()
5862 {
5863     if (pr_GetR(db, ".raise"))
5864         do_raise = (On(value.addr));
5865 }
5866
5867
5868 static void
5869 pr_persist()
5870 {
5871     if (pr_GetR(db, ".persist"))
5872         persist = (On(value.addr));
5873 }
5874
5875 static void
5876 pr_feedback()
5877 {
5878     if (pr_GetR(db, ".feedback"))
5879         feedback = !(!strncasecmp(value.addr, "off", 3) || !strncasecmp(value.addr, "false", 5));
5880     FPRINTF((stderr, "gplt_x11: set feedback to %d (%s)\n", feedback, value.addr));
5881 }
5882
5883 static void
5884 pr_ctrlq()
5885 {
5886     if (pr_GetR(db, ".ctrlq")) {
5887         ctrlq = (!strncasecmp(value.addr, "on", 2) || !strncasecmp(value.addr, "true", 4));
5888         FPRINTF((stderr, "gplt_x11: require <ctrl>q and <ctrl><space>\n"));
5889     }
5890 }
5891
5892 static void
5893 pr_fastrotate()
5894 {
5895     if (pr_GetR(db, ".fastrotate")) {
5896         fast_rotate = (!strncasecmp(value.addr, "on", 2) || !strncasecmp(value.addr, "true", 4));
5897         FPRINTF((stderr, "gplt_x11: Use fast but imperfect text rotation\n"));
5898     }
5899 }
5900
5901 #ifdef EXPORT_SELECTION
5902 static void
5903 pr_exportselection()
5904 {
5905     /* Allow export selection to be turned on or off using X resource *exportselection */
5906     if (pr_GetR(db, ".exportselection")) {
5907         if (!strncmp((char *)value.addr, "off", 3) || !strncmp((char *)value.addr, "false", 5)) {
5908             exportselection = FALSE;
5909             FPRINTF((stderr, "gnuplot_x11: exportselection is disabled\n"));
5910         }
5911     }
5912 }
5913 #endif
5914
5915 /************ code to handle selection export *********************/
5916
5917 #ifdef EXPORT_SELECTION
5918
5919 /* bit of a bodge, but ... */
5920 static struct plot_struct *exported_plot;
5921 static Time export_time;
5922
5923 static void
5924 export_graph(struct plot_struct *plot)
5925 {
5926     FPRINTF((stderr, "export_graph(0x%x)\n", plot));
5927
5928     XSetSelectionOwner(dpy, EXPORT_SELECTION, plot->window, CurrentTime);
5929     /* to check we have selection, we would have to do a
5930      * GetSelectionOwner(), but if it failed, it failed - no big deal
5931      */
5932     if (! *selection) {
5933         exported_plot = plot;
5934         export_time = (!plot || !(plot->time)) ? 1 : plot->time;
5935     }
5936 }
5937
5938 static void
5939 handle_selection_event(XEvent *event)
5940 {
5941     switch (event->type) {
5942     case SelectionRequest:
5943         {
5944             XEvent reply;
5945
5946             static Atom XA_TARGETS = (Atom) 0;
5947             static Atom XA_TIMESTAMP = (Atom) 0;
5948
5949             if (XA_TARGETS == 0)
5950                 XA_TARGETS = XInternAtom(dpy, "TARGETS", False);
5951             if (XA_TIMESTAMP == 0)
5952                 XA_TIMESTAMP = XInternAtom(dpy, "TIMESTAMP", False);
5953
5954             reply.type = SelectionNotify;
5955             reply.xselection.send_event = True;
5956             reply.xselection.display = event->xselectionrequest.display;
5957             reply.xselection.requestor = event->xselectionrequest.requestor;
5958             reply.xselection.selection = event->xselectionrequest.selection;
5959             reply.xselection.target = event->xselectionrequest.target;
5960             reply.xselection.property = event->xselectionrequest.property;
5961             reply.xselection.time = event->xselectionrequest.time;
5962
5963             if (reply.xselection.target == XA_TARGETS) {
5964                 static Atom targets[] = { XA_PIXMAP, XA_COLORMAP };
5965                 static Atom mousecoord[] = { XA_STRING };
5966
5967                 FPRINTF((stderr, "Targets request from %d\n", reply.xselection.requestor));
5968
5969                 if (*selection)
5970                     XChangeProperty(dpy, reply.xselection.requestor,
5971                                 reply.xselection.property, reply.xselection.target,
5972                                 32, PropModeReplace, (unsigned char *) (mousecoord), 1);
5973                 else if (exported_plot)
5974                     XChangeProperty(dpy, reply.xselection.requestor,
5975                                 reply.xselection.property, reply.xselection.target,
5976                                 32, PropModeReplace, (unsigned char *) targets, 2);
5977             } else if (reply.xselection.target == XA_COLORMAP) {
5978
5979                 FPRINTF((stderr, "colormap request from %d\n", reply.xselection.requestor));
5980
5981                 XChangeProperty(dpy, reply.xselection.requestor,
5982                                 reply.xselection.property, reply.xselection.target,
5983                                 32, PropModeReplace, (unsigned char *) &(default_cmap.colormap), 1);
5984             } else if (reply.xselection.target == XA_PIXMAP && exported_plot) {
5985
5986                 FPRINTF((stderr, "pixmap request from %d\n", reply.xselection.requestor));
5987
5988                 XChangeProperty(dpy, reply.xselection.requestor,
5989                                 reply.xselection.property, reply.xselection.target,
5990                                 32, PropModeReplace, (unsigned char *) &(exported_plot->pixmap), 1);
5991                 exported_plot = NULL;
5992             } else if (reply.xselection.target == XA_TIMESTAMP) {
5993
5994                 FPRINTF((stderr, "timestamp request from %d : %ld\n",
5995                         reply.xselection.requestor, export_time));
5996
5997                 XChangeProperty(dpy, reply.xselection.requestor,
5998                                 reply.xselection.property, reply.xselection.target,
5999                                 32, PropModeReplace, (unsigned char *) &(export_time), 1);
6000 #ifdef PIPE_IPC
6001             } else if (reply.xselection.target == XA_STRING && *selection) {
6002                 FPRINTF((stderr, "XA_STRING request\n"));
6003                 XChangeProperty(dpy, reply.xselection.requestor,
6004                                 reply.xselection.property, reply.xselection.target,
6005                                 8, PropModeReplace, (unsigned char *) selection, strlen(selection));
6006                 *selection = '\0';
6007 #endif
6008             } else {
6009                 FPRINTF((stderr, "selection request target: %d\n", reply.xselection.target));
6010                 reply.xselection.property = None;
6011                 if (!exported_plot && ! *selection)
6012                     /* We have now satisfied the select request. Say goodbye */
6013                     XSetSelectionOwner(dpy, EXPORT_SELECTION, None, CurrentTime);
6014             }
6015
6016             XSendEvent(dpy, reply.xselection.requestor, False, 0L, &reply);
6017             /* we never block on XNextEvent(), so must flush manually
6018              * (took me *ages* to find this out !)
6019              */
6020
6021             XFlush(dpy);
6022         }
6023         break;
6024     }
6025 }
6026
6027 #endif /* EXPORT_SELECTION */
6028
6029 #if defined(USE_MOUSE) && defined(MOUSE_ALL_WINDOWS)
6030 /* Convert X-window mouse coordinates to coordinate system of plot axes */
6031 static void
6032 mouse_to_coords(plot_struct *plot, XEvent *event,
6033                 double *x, double *y, double *x2, double *y2)
6034 {
6035     int xx =         4096. * (event->xbutton.x + 0.5)/ plot->width;
6036     int yy = 4095. - 4096. * (event->xbutton.y + 0.5)/ plot->gheight;
6037
6038     FPRINTF((stderr, "gnuplot_x11 %d: mouse at %d %d\t", __LINE__, xx, yy));
6039
6040     *x  = mouse_to_axis(xx, &(plot->axis_scale[FIRST_X_AXIS]));
6041     *y  = mouse_to_axis(yy, &(plot->axis_scale[FIRST_Y_AXIS]));
6042     *x2 = mouse_to_axis(xx, &(plot->axis_scale[SECOND_X_AXIS]));
6043     *y2 = mouse_to_axis(yy, &(plot->axis_scale[SECOND_Y_AXIS]));
6044
6045     FPRINTF((stderr, "mouse x y %10g %10g x2 y2 %10g %10g\n", *x, *y, *x2, *y2 ));
6046 }
6047
6048 static double
6049 mouse_to_axis(int mouse_coord, axis_scale_t *axis)
6050 {
6051     double axis_coord;
6052
6053     if (axis->term_scale == 0.0)
6054         return 0.;
6055
6056     axis_coord = ((double)(mouse_coord - axis->term_lower)) / axis->term_scale + axis->min;
6057     if (axis->logbase > 0.0)
6058         axis_coord = exp(axis_coord * axis->logbase);
6059
6060     return axis_coord;
6061 }
6062 #endif
6063
6064
6065 /*-----------------------------------------------------------------------------
6066  *   Add_Plot_To_Linked_List - Create space for plot and put in linked list.
6067  *---------------------------------------------------------------------------*/
6068
6069 static plot_struct *
6070 Add_Plot_To_Linked_List(int plot_number)
6071 {
6072     /* Make sure plot does not already exist in the list. */
6073     plot_struct *psp = Find_Plot_In_Linked_List_By_Number(plot_number);
6074
6075     if (psp == NULL) {
6076         psp = (plot_struct *) malloc(sizeof(plot_struct));
6077         if (psp) {
6078             /* Initialize structure variables. */
6079             memset((void*)psp, 0, sizeof(plot_struct));
6080             psp->plot_number = plot_number;
6081             /* Add link to beginning of the list. */
6082             psp->prev_plot = NULL;
6083             if (plot_list_start != NULL) {
6084                 plot_list_start->prev_plot = psp;
6085                 psp->next_plot = plot_list_start;
6086             } else
6087                 psp->next_plot = NULL;
6088             plot_list_start = psp;
6089         }
6090         else {
6091             psp = NULL;
6092             fprintf(stderr, ERROR_NOTICE("Could not allocate memory for plot.\n\n"));
6093         }
6094     }
6095
6096     return psp;
6097 }
6098
6099
6100 /*-----------------------------------------------------------------------------
6101  *   Remove_Plot_From_Linked_List - Remove from linked list and free memory.
6102  *---------------------------------------------------------------------------*/
6103
6104 static void
6105 Remove_Plot_From_Linked_List(Window plot_window)
6106 {
6107     /* Make sure plot exists in the list. */
6108     plot_struct *psp = Find_Plot_In_Linked_List_By_Window(plot_window);
6109
6110     if (psp != NULL) {
6111         /* Remove link from the list. */
6112         if (psp->next_plot != NULL)
6113             psp->next_plot->prev_plot = psp->prev_plot;
6114         if (psp->prev_plot != NULL) {
6115             psp->prev_plot->next_plot = psp->next_plot;
6116         } else {
6117             plot_list_start = psp->next_plot;
6118         }
6119         /* If global pointers point at this plot, reassign them. */
6120         if (current_plot == psp) {
6121 #if 0 /* Make some other plot current. */
6122             if (psp->prev_plot != NULL)
6123                 current_plot = psp->prev_plot;
6124             else
6125                 current_plot = psp->next_plot;
6126 #else /* No current plot. */
6127             current_plot = NULL;
6128 #endif
6129         }
6130         if (plot == psp)
6131             plot = current_plot;
6132         /* Deallocate memory.  Make sure plot removed from list first. */
6133         delete_plot(psp);
6134         free(psp);
6135     }
6136 }
6137
6138
6139 /*-----------------------------------------------------------------------------
6140  *   Find_Plot_In_Linked_List_By_Number - Search for the plot in the linked list.
6141  *---------------------------------------------------------------------------*/
6142
6143 static plot_struct *
6144 Find_Plot_In_Linked_List_By_Number(int plot_number)
6145 {
6146     plot_struct *psp = plot_list_start;
6147
6148     while (psp != NULL) {
6149         if (psp->plot_number == plot_number)
6150             break;
6151         psp = psp->next_plot;
6152     }
6153
6154     return psp;
6155 }
6156
6157
6158 /*-----------------------------------------------------------------------------
6159  *   Find_Plot_In_Linked_List_By_Window - Search for the plot in the linked list.
6160  *---------------------------------------------------------------------------*/
6161
6162 static plot_struct *
6163 Find_Plot_In_Linked_List_By_Window(Window window)
6164 {
6165     plot_struct *psp = plot_list_start;
6166
6167     while (psp != NULL) {
6168         if (psp->window == window)
6169             break;
6170         psp = psp->next_plot;
6171     }
6172
6173     return psp;
6174 }
6175
6176
6177 /*-----------------------------------------------------------------------------
6178  *   Find_Plot_In_Linked_List_By_CMap - Search for the plot in the linked list.
6179  *---------------------------------------------------------------------------*/
6180
6181 static plot_struct *
6182 Find_Plot_In_Linked_List_By_CMap(cmap_t *cmp)
6183 {
6184     plot_struct *psp = plot_list_start;
6185
6186     while (psp != NULL) {
6187         if (psp->cmap == cmp)
6188             break;
6189         psp = psp->next_plot;
6190     }
6191
6192     return psp;
6193 }
6194
6195
6196 /* NOTE:  The removing of plots via the ErrorHandler routine is rather
6197    tricky.  The error events can happen at any time during execution of
6198    the program, very similar to an interrupt.  The consequence is that
6199    the error handling routine can't remove plots from the linked list
6200    directly.  Instead we use a queuing system in which the main code
6201    eventually removes the plots.
6202
6203    Furthermore, to be safe, only the error handling routine should create
6204    and delete elements in the FIFO.  Otherwise, the possibility of bogus
6205    pointers can arise if error events happen at the exact wrong time.
6206    (Requires a lot of thought.)
6207
6208    The scheme here is for the error handler to put elements in the
6209    queue marked as "processed = 0" and then indicate that the main
6210    code should process elements in the queue.  The main code then
6211    copies the information about the plot to remove and sets the value
6212    "processed = 1".  Afterward the main code removes the plot.
6213 */
6214
6215 /*-----------------------------------------------------------------------------
6216  *   Add_Plot_To_Remove_FIFO_Queue - Method for error handler to destroy plot.
6217  *---------------------------------------------------------------------------*/
6218
6219 static void
6220 Add_Plot_To_Remove_FIFO_Queue(Window plot_window)
6221 {
6222     /* Clean up any processed links. */
6223     plot_remove_struct *prsp = remove_fifo_queue_start;
6224     FPRINTF((stderr, "Add plot to remove FIFO queue called.\n"));
6225     while (prsp != NULL) {
6226         if (prsp->processed) {
6227             remove_fifo_queue_start = prsp->next_remove;
6228             free(prsp);
6229             prsp = remove_fifo_queue_start;
6230             FPRINTF((stderr, "  -> Removed a processed element from FIFO queue.\n"));
6231         } else {
6232             break;
6233         }
6234     }
6235
6236     /* Go to end of list while checking if this window is already in list. */
6237     while (prsp != NULL) {
6238         if (prsp->plot_window_to_remove == plot_window) {
6239             /* Discard this request because the same window is yet to be processed.
6240                X11 could be stuck sending the same error message again and again
6241                while the main program is not responding for some reason.  This would
6242                lead to the FIFO queue growing indefinitely. */
6243             return;
6244         }
6245         if (prsp->next_remove == NULL)
6246             break;
6247         else
6248             prsp = prsp->next_remove;
6249     }
6250
6251     /* Create link and add to end of queue. */
6252     {plot_remove_struct *prsp_new = (plot_remove_struct *) malloc(sizeof(plot_remove_struct));
6253     if (prsp_new) {
6254         /* Initialize structure variables. */
6255         prsp_new->next_remove = NULL;
6256         prsp_new->plot_window_to_remove = plot_window;
6257         prsp_new->processed = 0;
6258         if (remove_fifo_queue_start)
6259             prsp->next_remove = prsp_new;
6260         else
6261             remove_fifo_queue_start = prsp_new;
6262         process_remove_fifo_queue = 1; /* Indicate to main loop that there is a plot to remove. */
6263         FPRINTF((stderr, "  -> Added an element to FIFO queue.\n"));
6264     }
6265     else {
6266         fprintf(stderr, ERROR_NOTICE("Could not allocate memory for plot remove queue.\n\n"));
6267     }}
6268 }
6269
6270
6271 /*-----------------------------------------------------------------------------
6272  *   Process_Remove_FIFO_Queue - Remove plots queued by error handler.
6273  *---------------------------------------------------------------------------*/
6274
6275 static void
6276 Process_Remove_FIFO_Queue()
6277 {
6278     plot_remove_struct *prsp = remove_fifo_queue_start;
6279
6280     /* Clear flag before processing so that if an ErrorHandler event
6281      * comes along while running the remainder of this routine, any new
6282      * error events that were missed because of timing issues will be
6283      * processed next time through the main loop.
6284      */
6285     process_remove_fifo_queue = 0;
6286
6287     /* Go through the list and process any unprocessed queue request.
6288      * No clean up is done here because having two asynchronous routines
6289      * modifying the queue would be too dodgy.  The ErrorHandler creates
6290      * and removes links in the queue based upon the processed flag.
6291      */
6292     while (prsp != NULL) {
6293
6294         /* Make a copy of the remove information structure before changing flag.
6295          * Otherwise, there would be the possibility of the error handler routine
6296          * removing the associated link upon seeing the "processed" flag set.  From
6297          * this side of things, the pointer becomes invalid once that flag is set.
6298          */
6299         plot_remove_struct prs;
6300         prs.plot_window_to_remove = prsp->plot_window_to_remove;
6301         prs.next_remove = prsp->next_remove;
6302         prs.processed = prsp->processed;
6303
6304         /* Set processed flag before processing the event.  This is so
6305          * that the FIFO queue does not have to repeat window entries.
6306          * If the error handler were to break in right before the
6307          * "processed" flag is set to 1 and not put another link in
6308          * the FIFO because it sees the window in question is already
6309          * in the FIFO, we're OK.  The reason is that the window in
6310          * question is still to be processed.  On the other hand, if
6311          * we were to process then set the flag, an entry in the queue
6312          * could potentially be lost.
6313          */
6314         prsp->processed = 1;
6315         FPRINTF((stderr, "Processed element in remove FIFO queue.\n"));
6316
6317         /* NOW process the plot to remove. */
6318         if (!prs.processed)
6319             Remove_Plot_From_Linked_List(prs.plot_window_to_remove);
6320
6321         prsp = prs.next_remove;
6322     }
6323
6324     /* Issue an X11 error so that error handler cleans up queue?
6325      * Really, this isn't super important.  Without issuing a bogus
6326      * error, the processed queue elements will be deleted the
6327      * next time there is an error.  Until another error comes
6328      * along it means there is maybe ten or so words of memory
6329      * reserved on the heap for the FIFO queue.  In some sense,
6330      * the extra code is hardly worth the effort, especially
6331      * when X11 documentation is so sparse on the matter of errors.
6332      */
6333
6334 }
6335
6336
6337 #ifdef WITH_IMAGE
6338 /* Extract details about the extent of a bit mask by doing a
6339  * single bit shift up and then down (left shift) and down
6340  * and then up (right shift).  When the pre- and post-shift
6341  * numbers are not equal, a bit was lost so that is the
6342  * extent of the mask.
6343  */
6344 static unsigned short
6345 BitMaskDetails(unsigned long mask, unsigned short *left_shift, unsigned short *right_shift)
6346 {
6347     unsigned short i;
6348     unsigned long m = mask;
6349
6350     if (mask == 0) {
6351         *left_shift = 0;
6352         *right_shift = 0;
6353         return 0;
6354     }
6355
6356     for (i=0; i < 32; i++) {
6357         if ( (((m << 1)&0xffffffff) >> 1) != m )
6358             break;
6359         else
6360             m <<= 1;
6361     }
6362     *left_shift = i;
6363
6364     m = mask;
6365     for (i=0; i < 32 ; i++) {
6366         if ( ((m >> 1) << 1) != m )
6367             break;
6368         else
6369             m >>= 1;
6370     }
6371     *right_shift = i;
6372
6373     return (unsigned short) m;
6374 }
6375 #endif
6376
6377
6378 /*-----------------------------------------------------------------------------
6379  *   Add_CMap_To_Linked_List - Create space for colormap and put in linked list.
6380  *---------------------------------------------------------------------------*/
6381
6382 static cmap_t *
6383 Add_CMap_To_Linked_List(void)
6384 {
6385     cmap_t *cmp = (cmap_t *) malloc(sizeof(cmap_t));
6386     if (cmp) {
6387         /* Add link to beginning of the list. */
6388         cmp->prev_cmap = NULL;
6389         if (cmap_list_start != NULL) {
6390             cmap_list_start->prev_cmap = cmp;
6391             cmp->next_cmap = cmap_list_start;
6392         } else
6393             cmp->next_cmap = NULL;
6394         cmap_list_start = cmp;
6395     } else {
6396         cmp = NULL;
6397         fprintf(stderr, ERROR_NOTICE("Could not allocate memory for color map.\n\n"));
6398     }
6399     /* Initialize structure variables. */
6400     CmapClear(cmp);
6401     cmp->colormap = XCreateColormap(dpy, root, vis, AllocNone);
6402     assert(cmp->colormap);
6403     pr_color(cmp);      /* set default colors for lines */
6404     return cmp;
6405 }
6406
6407
6408 /*-----------------------------------------------------------------------------
6409  *   Remove_CMap_From_Linked_List - Remove from linked list and free memory.
6410  *---------------------------------------------------------------------------*/
6411
6412 static void
6413 Remove_CMap_From_Linked_List(cmap_t *cmp)
6414 {
6415     /* Make sure colormap exists in the list. */
6416     cmp = Find_CMap_In_Linked_List(cmp);
6417
6418     if (cmp != NULL) {
6419         /* Remove link from the list. */
6420         if (cmp->next_cmap != NULL)
6421             cmp->next_cmap->prev_cmap = cmp->prev_cmap;
6422         if (cmp->prev_cmap != NULL) {
6423             cmp->prev_cmap->next_cmap = cmp->next_cmap;
6424         } else {
6425             cmap_list_start = cmp->next_cmap;
6426         }
6427         /* If global pointers point at this plot, reassign them. */
6428         if (current_cmap == cmp) {
6429 #if 0 /* Make some other cmap current. */
6430             if (cmp->prev_cmap != NULL)
6431                 current_cmap = cmp->prev_cmap;
6432             else
6433                 current_cmap = psp->next_cmap;
6434 #else /* No current cmap. */
6435             current_cmap = &default_cmap;
6436 #endif
6437         }
6438         /* Remove any memory for pixels and memory for colormap structure. */
6439         ReleaseColormap(cmp);
6440     }
6441 }
6442
6443
6444 /*-----------------------------------------------------------------------------
6445  *   Find_CMap_In_Linked_List - Search for the color map in the linked list.
6446  *---------------------------------------------------------------------------*/
6447
6448 static cmap_t *
6449 Find_CMap_In_Linked_List(cmap_t *colormap)
6450 {
6451     cmap_t *cmp = cmap_list_start;
6452
6453     while (cmp != NULL) {
6454         if (cmp == colormap)
6455             break;
6456         cmp = cmp->next_cmap;
6457     }
6458
6459     return cmp;
6460 }
6461
6462
6463 /*-----------------------------------------------------------------------------
6464  *   cmaps_differ - Compare two colormaps, return 1 if differ.
6465  *---------------------------------------------------------------------------*/
6466
6467 static int
6468 cmaps_differ(cmap_t *cmap1, cmap_t *cmap2)
6469 {
6470
6471     /* First compare non-pointer elements. */
6472     if ( memcmp(&(cmap1->colors[0]), &(cmap2->colors[0]), (long)&(cmap1->pixels)-(long)&(cmap1->colors[0])) )
6473         return 1;
6474
6475     /* Now compare pointer elements. */
6476     if (cmap1->allocated) {
6477         if (cmap1->pixels && cmap2->pixels) {
6478             if ( memcmp(cmap1->pixels, cmap2->pixels, cmap1->allocated*sizeof(cmap1->pixels[0])) )
6479                 return 1;
6480         } else
6481             return 1;
6482     }
6483
6484     return 0;  /* They are the same. */
6485
6486 }
6487
6488 /*
6489  * Shared code for setting fill style
6490  */
6491 static void
6492 x11_setfill(GC *gc, int style, TBOOLEAN poly)
6493 {
6494     int fillpar, idx;
6495     XColor xcolor, bgnd;
6496     float dim;
6497
6498     /* upper 3 nibbles contain fillparameter (ULIG) */
6499     fillpar = style >> 4;
6500
6501     /* lower nibble contains fillstyle */
6502     switch (style & 0xf) {
6503     case FS_SOLID:
6504         /* filldensity is from 0..100 percent */
6505         if (fillpar >= 100)
6506             break;
6507         dim = (double)(fillpar)/100.;
6508         /* retrieve current rgb color and shift it towards the background color */
6509         xcolor.red = (double)(0xffff) * (double)((plot->current_rgb >> 16) & 0xff) /255.;
6510         xcolor.green = (double)(0xffff) * (double)((plot->current_rgb >> 8) & 0xff) /255.;
6511         xcolor.blue = (double)(0xffff) * (double)(plot->current_rgb & 0xff) /255.;
6512         bgnd.red = (double)(0xffff) * (double)((plot->cmap->rgbcolors[0] >> 16) & 0xff) /255.;
6513         bgnd.green = (double)(0xffff) * (double)((plot->cmap->rgbcolors[0] >> 8) & 0xff) /255.;
6514         bgnd.blue = (double)(0xffff) * (double)(plot->cmap->rgbcolors[0] & 0xff) /255.;
6515         xcolor.red   = dim*xcolor.red   + (1.-dim)*bgnd.red;
6516         xcolor.green = dim*xcolor.green + (1.-dim)*bgnd.green;
6517         xcolor.blue  = dim*xcolor.blue  + (1.-dim)*bgnd.blue;
6518         if (XAllocColor(dpy, plot->cmap->colormap, &xcolor))
6519             XSetForeground(dpy, *gc, xcolor.pixel);
6520         break;
6521     case FS_PATTERN:
6522         /* use fill pattern according to fillpattern */
6523         idx = (int) fillpar;    /* fillpattern is enumerated */
6524         if (idx < 0)
6525             idx = 0;
6526         idx = idx % stipple_pattern_num;
6527         XSetStipple(dpy, *gc, stipple_pattern[idx]);
6528         XSetFillStyle(dpy, *gc, FillOpaqueStippled);
6529         if (poly)
6530             XSetBackground(dpy, *gc, plot->cmap->colors[0]);
6531         else
6532             XSetForeground(dpy, *gc, plot->cmap->colors[plot->lt + 3]);
6533         break;
6534     case FS_EMPTY: /* fill with background color */
6535         XSetFillStyle(dpy, *gc, FillSolid);
6536         XSetForeground(dpy, *gc, plot->cmap->colors[0]);
6537         break;
6538     default:
6539         XSetFillStyle(dpy, *gc, FillSolid);
6540         if (!poly)
6541             XSetForeground(dpy, *gc, plot->cmap->colors[0]);
6542     }
6543 }