--- /dev/null
+#ifndef lint
+static char *RCSid() { return RCSid("$Id: gplt_x11.c,v 1.167.2.11 2009/04/10 04:37:00 sfeam Exp $"); }
+#endif
+
+#define X11_POLYLINE 1
+#define MOUSE_ALL_WINDOWS 1
+
+/* GNUPLOT - gplt_x11.c */
+
+/*[
+ * Copyright 1986 - 1993, 1998, 2004 Thomas Williams, Colin Kelley
+ *
+ * Permission to use, copy, and distribute this software and its
+ * documentation for any purpose with or without fee is hereby granted,
+ * provided that the above copyright notice appear in all copies and
+ * that both that copyright notice and this permission notice appear
+ * in supporting documentation.
+ *
+ * Permission to modify the software is granted, but not the right to
+ * distribute the complete modified source code. Modifications are to
+ * be distributed as patches to the released version. Permission to
+ * distribute binaries produced by compiling modified sources is granted,
+ * provided you
+ * 1. distribute the corresponding source modifications from the
+ * released version in the form of a patch file along with the binaries,
+ * 2. add special version identification to distinguish your version
+ * in addition to the base release version number,
+ * 3. provide your name and address as the primary contact for the
+ * support of your modified version, and
+ * 4. retain our contact information in regard to use of the base
+ * software.
+ * Permission to distribute the released version of the source code along
+ * with corresponding source modifications in the form of a patch file is
+ * granted with same provisions 2 through 4 for binary distributions.
+ *
+ * This software is provided "as is" without express or implied warranty
+ * to the extent permitted by applicable law.
+]*/
+
+
+/* lph changes:
+ * (a) make EXPORT_SELECTION the default and specify NOEXPORT to undefine
+ * (b) append X11 terminal number to resource name
+ * (c) change cursor for active terminal
+ */
+
+/*-----------------------------------------------------------------------------
+ * gnuplot_x11 - X11 outboard terminal driver for gnuplot 3.3
+ *
+ * Requires installation of companion inboard x11 driver in gnuplot/term.c
+ *
+ * Acknowledgements:
+ * Chris Peterson (MIT)
+ * Dana Chee (Bellcore)
+ * Arthur Smith (Cornell)
+ * Hendri Hondorp (University of Twente, The Netherlands)
+ * Bill Kucharski (Solbourne)
+ * Charlie Kline (University of Illinois)
+ * Yehavi Bourvine (Hebrew University of Jerusalem, Israel)
+ * Russell Lang (Monash University, Australia)
+ * O'Reilly & Associates: X Window System - Volumes 1 & 2
+ *
+ * This code is provided as is and with no warranties of any kind.
+ *
+ * drd: change to allow multiple windows to be maintained independently
+ *
+ *---------------------------------------------------------------------------*/
+
+/* drd : export the graph via ICCCM primary selection. well... not quite
+ * ICCCM since we dont support full list of targets, but this
+ * is a start. define EXPORT_SELECTION if you want this feature
+ */
+
+/*lph: add a "feature" to undefine EXPORT_SELECTION
+ The following makes EXPORT_SELECTION the default and
+ defining NOEXPORT over-rides the default
+ */
+
+/* Petr Mikulik and Johannes Zellner: added mouse support (October 1999)
+ * Implementation and functionality is based on os2/gclient.c; see mousing.c
+ * Pieter-Tjerk de Boer <ptdeboer@cs.utwente.nl>: merged two versions
+ * of mouse patches. (November 1999) (See also mouse.[ch]).
+ */
+
+/* X11 support for Petr Mikulik's pm3d
+ * by Johannes Zellner <johannes@zellner.org>
+ * (November 1999 - January 2000, Oct. 2000)
+ */
+
+/* Polyline support May 2003
+ * Ethan Merritt <merritt@u.washington.edu>
+ */
+
+/* Dynamically created windows July 2003
+ * Across-pipe title text and close command October 2003
+ * Dan Sebald <daniel.sebald@ieee.org>
+ */
+
+/* Daniel Sebald: added X11 support for images. (27 February 2003)
+ */
+
+/* Shigeharu Takeno <shige@iee.niit.ac.jp> February 2005
+ * Support for multi-byte fonts based, with permission, on the "gnuplot+"
+ * patches by Masahito Yamaga <ma@yama-ga.com>
+ */
+
+#include "syscfg.h"
+#include "stdfn.h"
+#include "gp_types.h"
+#include "term_api.h"
+#include "gplt_x11.h"
+
+#ifdef EXPORT_SELECTION
+# undef EXPORT_SELECTION
+#endif /* EXPORT SELECTION */
+#ifndef NOEXPORT
+# define EXPORT_SELECTION XA_PRIMARY
+#endif /* NOEXPORT */
+
+
+#if !(defined(VMS) || defined(CRIPPLED_SELECT))
+# define DEFAULT_X11
+#endif
+
+#if defined(VMS) && defined(CRIPPLED_SELECT)
+Error. Incompatible options.
+#endif
+
+#include <X11/Xos.h>
+#include <X11/Xlib.h>
+#include <X11/Xresource.h>
+#include <X11/Xutil.h>
+#include <X11/Xatom.h>
+#include <X11/keysym.h>
+#ifdef USE_X11_MULTIBYTE
+# include <X11/Xlocale.h>
+#endif
+
+#include <math.h>
+#include "getcolor.h"
+
+#ifdef USE_MOUSE
+# include <X11/cursorfont.h>
+#else
+# define XC_crosshair 34
+#endif
+
+#include <signal.h>
+
+#ifdef HAVE_SYS_BSDTYPES_H
+# include <sys/bsdtypes.h>
+#endif
+
+#if defined(HAVE_SYS_SYSTEMINFO_H) && defined(HAVE_SYSINFO)
+# include <sys/systeminfo.h>
+# define SYSINFO_METHOD "sysinfo"
+# define GP_SYSTEMINFO(host) sysinfo (SI_HOSTNAME, (host), MAXHOSTNAMELEN)
+#else
+# define SYSINFO_METHOD "gethostname"
+# define GP_SYSTEMINFO(host) gethostname ((host), MAXHOSTNAMELEN)
+#endif /* HAVE_SYS_SYSTEMINFO_H && HAVE_SYSINFO */
+
+#ifdef USE_MOUSE
+# ifdef OS2_IPC
+# define INCL_DOSPROCESS
+# define INCL_DOSSEMAPHORES
+# include <os2.h>
+# endif
+# include "gpexecute.h"
+# include "mouse.h"
+# include <unistd.h>
+# include <fcntl.h>
+# include <errno.h>
+static unsigned long gnuplotXID = 0; /* WINDOWID of gnuplot */
+
+#ifdef MOUSE_ALL_WINDOWS
+# include "axis.h" /* Just to pick up FIRST_X_AXIS enums */
+typedef struct axis_scale_t {
+ int term_lower;
+ double term_scale;
+ double min;
+ double logbase;
+} axis_scale_t;
+#endif
+
+#endif /* USE_MOUSE */
+
+#ifdef __EMX__
+/* for gethostname ... */
+# include <netdb.h>
+/* for __XOS2RedirRoot */
+#include <X11/Xlibint.h>
+#endif
+
+
+#ifdef VMS
+# ifdef __DECC
+# include <starlet.h>
+# endif
+# define EXIT(status) sys$delprc(0, 0) /* VMS does not drop itself */
+#else /* !VMS */
+# ifdef PIPE_IPC
+# define EXIT(status) \
+ do { \
+ gp_exec_event(GE_pending, 0, 0, 0, 0, 0); \
+ close(1); \
+ close(0); \
+ exit(status); \
+ } while (0)
+# else
+# define EXIT(status) exit(status)
+# endif /* PIPE_IPC */
+#endif /* !VMS */
+
+#ifdef OSK
+# define EINTR E_ILLFNC
+#endif
+
+
+#define Ncolors 13
+
+typedef struct cmap_t {
+ struct cmap_t *prev_cmap; /* Linked list pointers and number */
+ struct cmap_t *next_cmap;
+ Colormap colormap;
+ unsigned long colors[Ncolors]; /* line colors as pixel values */
+ unsigned long rgbcolors[Ncolors]; /* line colors in rgb format */
+ unsigned long xorpixel; /* line colors */
+ int total;
+ int allocated;
+ unsigned long *pixels; /* pm3d colors */
+} cmap_t;
+
+/* always allocate a default colormap (done in preset()) */
+static cmap_t default_cmap;
+
+/* information about one window/plot */
+typedef struct plot_struct {
+ Window window;
+ Pixmap pixmap;
+ unsigned int posn_flags;
+ int x, y;
+ unsigned int width, height; /* window size */
+ unsigned int gheight; /* height of the part of the window that
+ * contains the graph (i.e., excluding the
+ * status line at the bottom if mouse is
+ * enabled) */
+ unsigned int px, py;
+ int ncommands, max_commands;
+ char **commands;
+ char *titlestring;
+#ifdef USE_MOUSE
+ int button; /* buttons which are currently pressed */
+ char str[0xff]; /* last displayed string */
+#endif
+ Time time; /* time stamp of previous event */
+ int lwidth; /* this and the following 6 lines declare */
+ int type; /* variables used during drawing in exec_cmd() */
+ int user_width;
+ enum JUSTIFY jmode;
+ double angle; /* Text rotation angle in degrees */
+ int lt;
+#ifdef USE_MOUSE
+ TBOOLEAN mouse_on; /* is mouse bar on? */
+ TBOOLEAN ruler_on; /* is ruler on? */
+ TBOOLEAN ruler_lineto_on; /* is line between ruler and mouse cursor on? */
+ int ruler_x, ruler_y; /* coordinates of ruler */
+ int ruler_lineto_x, ruler_lineto_y; /* draw line from ruler to current mouse pos */
+ TBOOLEAN zoombox_on; /* is zoombox on? */
+ int zoombox_x1, zoombox_y1, zoombox_x2, zoombox_y2; /* coordinates of zoombox as last drawn */
+ 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 */
+ TBOOLEAN resizing; /* TRUE while waiting for an acknowledgement of resize */
+#endif
+ /* points to the cmap which is currently used for drawing.
+ * This is always the default colormap, if not in pm3d.
+ */
+ cmap_t *cmap;
+#if defined(USE_MOUSE) && defined(MOUSE_ALL_WINDOWS)
+ /* This array holds per-axis scaling information sufficient to reconstruct
+ * plot coordinates of a mouse click. It is a snapshot of the contents of
+ * gnuplot's axis_array structure at the time the plot was drawn.
+ */
+ int almost2d;
+ int axis_mask; /* Bits set to show which axes are active */
+ axis_scale_t axis_scale[2*SECOND_AXES];
+#endif
+ /* Last text position - used by enhanced text mode */
+ int xLast, yLast;
+ /* Saved text position - used by enhanced text mode */
+ int xSave, ySave;
+ /* Last rgb color that was set */
+ unsigned long current_rgb;
+
+ struct plot_struct *prev_plot; /* Linked list pointers and number */
+ struct plot_struct *next_plot;
+ int plot_number;
+} plot_struct;
+
+static plot_struct *Add_Plot_To_Linked_List __PROTO((int));
+static void Remove_Plot_From_Linked_List __PROTO((Window));
+static plot_struct *Find_Plot_In_Linked_List_By_Number __PROTO((int));
+static plot_struct *Find_Plot_In_Linked_List_By_Window __PROTO((Window));
+static plot_struct *Find_Plot_In_Linked_List_By_CMap __PROTO((cmap_t *));
+
+static struct plot_struct *current_plot = NULL;
+static struct plot_struct *plot_list_start = NULL;
+
+static void x11_setfill __PROTO((GC *gc, int style, TBOOLEAN poly));
+
+/* information about window/plot to be removed */
+typedef struct plot_remove_struct {
+ Window plot_window_to_remove;
+ struct plot_remove_struct *next_remove;
+ int processed;
+} plot_remove_struct;
+
+static void Add_Plot_To_Remove_FIFO_Queue __PROTO((Window));
+static void Process_Remove_FIFO_Queue __PROTO((void));
+
+static struct plot_remove_struct *remove_fifo_queue_start = NULL;
+static int process_remove_fifo_queue = 0;
+
+static cmap_t *Add_CMap_To_Linked_List __PROTO((void));
+static void Remove_CMap_From_Linked_List __PROTO((cmap_t *));
+static cmap_t *Find_CMap_In_Linked_List __PROTO((cmap_t *));
+static int cmaps_differ __PROTO((cmap_t *, cmap_t *));
+
+/* current_cmap always points to a valid colormap. At start up
+ * it is the default colormap. When a palette command comes
+ * across the pipe, the current_cmap is set to point at the
+ * resulting colormap which ends up in the linked list of colormaps.
+ * The current_cmap should never be removed from the linked list
+ * even if all windows are deleted, because that colormap will be
+ * used for the next plot.
+ */
+static struct cmap_t *current_cmap = NULL;
+static struct cmap_t *cmap_list_start = NULL;
+
+/* These might work better as fuctions, but defines will do for now. */
+#define ERROR_NOTICE(str) "\nGNUPLOT (gplt_x11): " str
+#define ERROR_NOTICE_NEWLINE(str) "\n " str
+
+enum { NOT_AVAILABLE = -1 };
+
+#define SEL_LEN 0xff
+static char selection[SEL_LEN] = "";
+
+
+#ifdef USE_MOUSE
+# define GRAPH_HEIGHT(plot) ((plot)->gheight)
+# define PIXMAP_HEIGHT(plot) ((plot)->gheight + vchar)
+ /* note: PIXMAP_HEIGHT is the height of the plot including the status line,
+ even if the latter is not enabled right now */
+#else
+# define GRAPH_HEIGHT(plot) ((plot)->height)
+# define PIXMAP_HEIGHT(plot) ((plot)->height)
+#endif
+
+static void CmapClear __PROTO((cmap_t *));
+static void RecolorWindow __PROTO((plot_struct *));
+static void FreeColors __PROTO((cmap_t *));
+static void ReleaseColormap __PROTO((cmap_t *));
+static unsigned long *ReallocColors __PROTO((cmap_t *, int));
+static void PaletteMake __PROTO((t_sm_palette *));
+static void PaletteSetColor __PROTO((plot_struct *, double));
+static int GetVisual __PROTO((int, Visual **, int *));
+static void scan_palette_from_buf __PROTO((void));
+
+#if defined(WITH_IMAGE)
+static unsigned short BitMaskDetails __PROTO((unsigned long mask, unsigned short *left_shift, unsigned short *right_shift));
+#endif
+#if defined(WITH_IMAGE) || defined(BINARY_X11_POLYGON)
+TBOOLEAN swap_endian = 0; /* For binary data. */
+/* Petr's byte swapping routine. */
+static inline void
+byteswap(char* data, int datalen)
+{
+ char tmp, *dest = data + datalen - 1;
+ if (datalen < 2) return;
+ while (dest > data) {
+ tmp = *dest;
+ *dest-- = *data;
+ *data++ = tmp;
+ }
+}
+/* Additional macros that should be more efficient when data size is known. */
+char byteswap_char;
+#define byteswap1(x)
+#define byteswap2(x) \
+ byteswap_char = ((char *)x)[0]; \
+ ((char *)x)[0] = ((char *)x)[1]; \
+ ((char *)x)[1] = byteswap_char;
+#define byteswap4(x) \
+ byteswap_char = ((char *)x)[0]; \
+ ((char *)x)[0] = ((char *)x)[3]; \
+ ((char *)x)[3] = byteswap_char; \
+ byteswap_char = ((char *)x)[1]; \
+ ((char *)x)[1] = ((char *)x)[2]; \
+ ((char *)x)[2] = byteswap_char
+#endif
+
+static void store_command __PROTO((char *, plot_struct *));
+static void prepare_plot __PROTO((plot_struct *, int));
+static void delete_plot __PROTO((plot_struct *));
+
+static int record __PROTO((void));
+static void process_event __PROTO((XEvent *)); /* from Xserver */
+static void process_configure_notify_event __PROTO((XEvent *event));
+
+static void mainloop __PROTO((void));
+
+static void display __PROTO((plot_struct *));
+static void UpdateWindow __PROTO((plot_struct *));
+#ifdef USE_MOUSE
+static int ErrorHandler __PROTO((Display *, XErrorEvent *));
+static void DrawRuler __PROTO((plot_struct *));
+static void EventuallyDrawMouseAddOns __PROTO((plot_struct *));
+static void DrawBox __PROTO((plot_struct *));
+static void DrawLineToRuler __PROTO((plot_struct *));
+static void AnnotatePoint __PROTO((plot_struct *, int, int, const char[], const char[]));
+static long int SetTime __PROTO((plot_struct *, Time));
+static unsigned long AllocateXorPixel __PROTO((cmap_t *));
+static void GetGCXor __PROTO((plot_struct *, GC *));
+static void GetGCXorDashed __PROTO((plot_struct *, GC *));
+#if 0
+static void GetGCBlackAndWhite __PROTO((plot_struct *, GC *, Pixmap, int));
+static int SplitAt __PROTO((char **, int, char *, char));
+static void xfree __PROTO((void *));
+#endif
+static void EraseCoords __PROTO((plot_struct *));
+static void DrawCoords __PROTO((plot_struct *, const char *));
+static void DisplayCoords __PROTO((plot_struct *, const char *));
+
+static TBOOLEAN is_meta __PROTO((KeySym));
+static char* getMultiTabConsoleSwitchCommand __PROTO((unsigned long *));
+#endif /* USE_MOUSE */
+
+static void DrawRotated __PROTO((plot_struct *, Display *, GC,
+ int, int, const char *, int));
+static int DrawRotatedErrorHandler __PROTO((Display *, XErrorEvent *));
+static void exec_cmd __PROTO((plot_struct *, char *));
+
+static void reset_cursor __PROTO((void));
+
+static void preset __PROTO((int, char **));
+static char *pr_GetR __PROTO((XrmDatabase, char *));
+static void pr_color __PROTO((cmap_t *));
+static void pr_dashes __PROTO((void));
+static void pr_encoding __PROTO((void));
+static void pr_font __PROTO((char *));
+static void pr_geometry __PROTO((char *));
+static void pr_pointsize __PROTO((void));
+static void pr_width __PROTO((void));
+static void pr_window __PROTO((plot_struct *));
+static void ProcessEvents __PROTO((Window));
+static void pr_raise __PROTO((void));
+static void pr_persist __PROTO((void));
+static void pr_feedback __PROTO((void));
+static void pr_ctrlq __PROTO((void));
+static void pr_fastrotate __PROTO((void));
+
+#ifdef EXPORT_SELECTION
+static void export_graph __PROTO((plot_struct *));
+static void handle_selection_event __PROTO((XEvent *));
+static void pr_exportselection __PROTO((void));
+#endif
+
+#if defined(USE_MOUSE) && defined(MOUSE_ALL_WINDOWS)
+static void mouse_to_coords __PROTO((plot_struct *, XEvent *,
+ double *, double *, double *, double *));
+static double mouse_to_axis __PROTO((int, axis_scale_t *));
+#endif
+
+static char *FallbackFont = "fixed";
+#ifdef USE_X11_MULTIBYTE
+static char *FallbackFontMB =
+ "mbfont:*-medium-r-normal--14-*;*-medium-r-normal--16-*";
+# define FontSetSep ';'
+static int usemultibyte = 0;
+static int multibyte_fonts_usable=1;
+static int fontset_transsep __PROTO((char *, char *, int));
+#endif /* USE_X11_MULTIBYTE */
+static int gpXTextWidth __PROTO((XFontStruct *, const char *, int));
+static int gpXTextHeight __PROTO((XFontStruct *));
+static void gpXSetFont __PROTO((Display *, GC, Font));
+static void gpXDrawImageString __PROTO((Display *, Drawable, GC, int, int, const char *, int));
+static void gpXDrawString __PROTO((Display *, Drawable, GC, int, int, const char *, int));
+static void gpXFreeFont __PROTO((Display *, XFontStruct *));
+static XFontStruct *gpXLoadQueryFont __PROTO((Display *, char *));
+static char *gpFallbackFont __PROTO((void));
+static int gpXGetFontascent __PROTO((XFontStruct *cfont));
+
+enum set_encoding_id encoding = S_ENC_DEFAULT; /* EAM - mirrored from core code by 'QE' */
+static char default_font[64] = { '\0' };
+static char default_encoding[16] = { '\0' };
+
+#define Nwidths 10
+static unsigned int widths[Nwidths] = { 2, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
+
+#define Ndashes 10
+static char dashes[Ndashes][5];
+
+t_sm_palette sm_palette = {
+ -1, /* colorFormulae */
+ SMPAL_COLOR_MODE_NONE, /* colorMode */
+ 0, 0, 0, /* formula[RGB] */
+ 0, /* positive */
+ 0, /* use_maxcolors */
+ -1, /* colors */
+ (rgb_color *) 0, /* color */
+ 0, /* ps_allcF */
+ 0, /* gradient_num */
+ (gradient_struct *) 0 /* gradient */
+ /* Afunc, Bfunc and Cfunc can't be initialised here */
+};
+
+static int have_pm3d = 1;
+static int num_colormaps = 0;
+static unsigned int maximal_possible_colors = 0x100;
+static unsigned int minimal_possible_colors;
+
+/* the following visual names must match the
+ * definitions in X.h in this order ! I hope
+ * this is standard (joze) */
+static char *visual_name[] = {
+ "StaticGray",
+ "GrayScale",
+ "StaticColor",
+ "PseudoColor",
+ "TrueColor",
+ "DirectColor",
+ (char *) 0
+};
+
+static Display *dpy;
+static int scr;
+static Window root;
+static Visual *vis = (Visual *) 0;
+static GC gc = (GC) 0;
+static GC *current_gc = (GC *) 0;
+static GC gc_xor = (GC) 0;
+static GC gc_xor_dashed = (GC) 0;
+static GC fill_gc = (GC) 0;
+static XFontStruct *font = NULL;
+#ifdef USE_X11_MULTIBYTE
+static XFontSet mbfont = NULL;
+#endif
+static int do_raise = yes, persist = no;
+static TBOOLEAN fast_rotate = TRUE;
+static int feedback = yes;
+static int ctrlq = no;
+static int dashedlines = no;
+#ifdef EXPORT_SELECTION
+static TBOOLEAN exportselection = TRUE;
+#endif
+static Cursor cursor;
+static Cursor cursor_default;
+#ifdef USE_MOUSE
+static Cursor cursor_exchange;
+static Cursor cursor_sizing;
+static Cursor cursor_zooming;
+#ifndef TITLE_BAR_DRAWING_MSG
+static Cursor cursor_waiting;
+static Cursor cursor_save;
+static int button_pressed = 0;
+#endif
+#endif
+
+static int windows_open = 0;
+
+static int gX = 100, gY = 100;
+static unsigned int gW = 640, gH = 450;
+static unsigned int gFlags = PSize;
+
+static unsigned int BorderWidth = 2;
+static unsigned int dep; /* depth */
+static long max_request_size;
+
+static Bool Mono = 0, Gray = 0, Rv = 0, Clear = 0;
+static char X_Name[64] = "gnuplot";
+static char X_Class[64] = "Gnuplot";
+
+static int cx = 0, cy = 0;
+
+/* Font characteristics held locally but sent back via pipe to x11.trm */
+static int vchar, hchar;
+
+/* Speficy negative values as indicator of uninitialized state */
+static double xscale = -1.;
+static double yscale = -1.;
+double pointsize = -1.;
+/* Avoid a crash upon using uninitialized variables from
+ above and avoid unnecessary calls to display().
+ Probably this is not the best fix ... */
+#define Call_display(plot) if (xscale<0.) display(plot);
+#define X(x) (int) ((x) * xscale)
+#define Y(y) (int) ((4095-(y)) * yscale)
+#define RevX(x) (((x)+0.5)/xscale)
+#define RevY(y) (4095-((y)+0.5)/yscale)
+/* note: the 0.5 term in RevX(x) and RevY(y) compensates for the round-off in X(x) and Y(y) */
+
+#if defined(WITH_IMAGE) || defined(BINARY_X11_POLYGON)
+#define Nbuf X11_COMMAND_BUFFER_LENGTH
+#else
+#define Nbuf 1024
+#endif
+static char buf[Nbuf];
+static int buffered_input_available = 0;
+
+static FILE *X11_ipc;
+
+/* when using an ICCCM-compliant window manager, we can ask it
+ * to send us an event when user chooses 'close window'. We do this
+ * by setting WM_DELETE_WINDOW atom in property WM_PROTOCOLS
+ */
+
+static Atom WM_PROTOCOLS, WM_DELETE_WINDOW;
+
+static XPoint Diamond[5], Triangle[4];
+static XSegment Plus[2], Cross[2], Star[4];
+
+/* pixmaps used for filled boxes (ULIG) */
+/* FIXME EAM - These data structures are a duplicate of the ones in bitmap.c */
+
+/* pattern stipples for pattern fillstyle */
+#define stipple_pattern_width 8
+#define stipple_pattern_height 8
+#define stipple_pattern_num 8
+static const char stipple_pattern_bits[stipple_pattern_num][8] = {
+ { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 } /* no fill */
+ , { 0x81, 0x42, 0x24, 0x18, 0x18, 0x24, 0x42, 0x41 } /* cross-hatch (1) */
+ , { 0x88, 0x55, 0x22, 0x55, 0x88, 0x55, 0x22, 0x55 } /* double crosshatch(2) */
+ , { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff } /* solid fill (3) */
+ , { 0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80 } /* diagonal stripes (4) */
+ , { 0x80, 0x40, 0x20, 0x10, 0x08, 0x04, 0x02, 0x01 } /* diagonal stripes (5) */
+ , { 0x11, 0x11, 0x22, 0x22, 0x44, 0x44, 0x88, 0x88 } /* diagonal stripes (6) */
+ , { 0x88, 0x88, 0x44, 0x44, 0x22, 0x22, 0x11, 0x11 } /* diagonal stripes (7) */
+};
+
+static Pixmap stipple_pattern[stipple_pattern_num];
+static int stipple_initialized = 0;
+
+#ifdef X11_POLYLINE
+static XPoint *polyline = NULL;
+static int polyline_space = 0;
+static int polyline_size = 0;
+#endif
+
+/*
+ * Main program
+ */
+int
+main(int argc, char *argv[])
+{
+
+#ifdef PIPE_IPC
+ int getfl;
+#endif
+
+#ifdef OSK
+ /* malloc large blocks, otherwise problems with fragmented mem */
+ _mallocmin(102400);
+#endif
+#ifdef __EMX__
+ /* close open file handles */
+ fcloseall();
+#endif
+
+#ifdef USE_X11_MULTIBYTE
+ if (setlocale(LC_ALL, "")==NULL || XSupportsLocale()==False)
+ multibyte_fonts_usable=0;
+ setlocale(LC_NUMERIC, "C"); /* HBB 20050525 */
+#endif /* USE_X11_MULTIBYTE */
+ preset(argc, argv);
+
+/* set up the alternative cursor */
+ cursor_default = XCreateFontCursor(dpy, XC_crosshair);
+ cursor = cursor_default;
+#ifdef USE_MOUSE
+ /* create cursors for the splot actions */
+ cursor_exchange = XCreateFontCursor(dpy, XC_exchange);
+ cursor_sizing = XCreateFontCursor(dpy, XC_sizing);
+ /* arrow, top_left_arrow, left_ptr, sb_left_arrow, sb_right_arrow,
+ * plus, pencil, draft_large, right_ptr, draft_small */
+ cursor_zooming = XCreateFontCursor(dpy, XC_draft_small);
+#ifndef TITLE_BAR_DRAWING_MSG
+ cursor_waiting = XCreateFontCursor(dpy, XC_watch);
+ cursor_save = (Cursor)0;
+#endif
+#endif
+#ifdef PIPE_IPC
+ if (!pipe_died) {
+ /* set up nonblocking stdout */
+ getfl = fcntl(1, F_GETFL); /* get current flags */
+ fcntl(1, F_SETFL, getfl | O_NONBLOCK);
+ signal(SIGPIPE, pipe_died_handler);
+ }
+# endif
+
+#ifdef X11_POLYLINE
+ polyline_space = 100;
+ polyline = calloc(polyline_space, sizeof(XPoint));
+ if (!polyline) fprintf(stderr, "Panic: cannot allocate polyline\n");
+#endif
+
+ mainloop();
+
+ if (persist) {
+ FPRINTF((stderr, "waiting for %d windows\n", windows_open));
+
+#ifndef DEBUG
+ /* HBB 20030519: Some programs executing gnuplot -persist may
+ * be waiting for all default handles to be closed before they
+ * consider the sub-process finished. Emacs, e.g., does. So,
+ * unless this is a DEBUG build, drop our connection to stderr
+ * now. Using Freopen() ensures that debug fprintf()s won't
+ * crash. */
+ freopen("/dev/null", "w", stderr);
+#endif
+
+ /* read x events until all windows have been quit */
+ while (windows_open > 0) {
+ XEvent event;
+ XNextEvent(dpy, &event);
+ process_event(&event);
+ }
+ }
+ XCloseDisplay(dpy);
+
+ FPRINTF((stderr, "exiting\n"));
+
+ EXIT(0);
+}
+
+/*-----------------------------------------------------------------------------
+ * mainloop processing - process X events and input from gnuplot
+ *
+ * Three different versions of main loop processing are provided to support
+ * three different platforms.
+ *
+ * DEFAULT_X11: use select() for both X events and input on stdin
+ * from gnuplot inboard driver
+ *
+ * CRIPPLED_SELECT: use select() to service X events and check during
+ * select timeout for temporary plot file created
+ * by inboard driver
+ *
+ * VMS: use XNextEvent to service X events and AST to
+ * service input from gnuplot inboard driver on stdin
+ *---------------------------------------------------------------------------*/
+
+
+#ifdef DEFAULT_X11
+
+/*
+ * DEFAULT_X11 mainloop
+ */
+static void
+mainloop()
+{
+ int nf, cn = ConnectionNumber(dpy), in;
+ SELECT_TYPE_ARG1 nfds;
+ struct timeval timeout, *timer = (struct timeval *) 0;
+ fd_set tset;
+
+#ifdef PIPE_IPC
+ int usleep_count = 0;
+ int out;
+ out = fileno(stdout);
+#endif
+
+ X11_ipc = stdin;
+ in = fileno(X11_ipc);
+
+#ifdef PIPE_IPC
+ if (out > in)
+ nfds = ((cn > out) ? cn : out) + 1;
+ else
+#endif
+ nfds = ((cn > in) ? cn : in) + 1;
+
+#ifdef ISC22
+/* Added by Robert Eckardt, RobertE@beta.TP2.Ruhr-Uni-Bochum.de */
+ timeout.tv_sec = 0; /* select() in ISC2.2 needs timeout */
+ timeout.tv_usec = 300000; /* otherwise input from gnuplot is */
+ timer = &timeout; /* suspended til next X event. */
+#endif /* ISC22 (0.3s are short enough not to be noticed */
+
+ while (1) {
+
+ /* XNextEvent does an XFlush() before waiting. But here.
+ * we must ensure that the queue is flushed, since we
+ * dont call XNextEvent until an event arrives. (I have
+ * twice wasted quite some time over this issue, so now
+ * I am making sure of it !
+ */
+
+ XFlush(dpy);
+
+ FD_ZERO(&tset);
+ FD_SET(cn, &tset);
+
+ /* Don't wait for events if we know that input is
+ * already sitting in a buffer. Also don't wait for
+ * input to become available.
+ */
+ if (buffered_input_available) {
+ timeout.tv_sec = 0;
+ timeout.tv_usec = 0;
+ timer = &timeout;
+ } else {
+ timer = (struct timeval *) 0;
+ FD_SET(in, &tset);
+ }
+
+#ifdef PIPE_IPC
+ if (buffered_output_pending && !pipe_died) {
+ /* check, if stdout becomes writable */
+ FD_SET(out, &tset);
+ }
+#ifdef HAVE_USLEEP
+ /* Make sure this loop does not monopolize CPU if the pipe is jammed */
+ if (++usleep_count > 10) {
+ usleep(100);
+ usleep_count = 0;
+ }
+#endif
+#endif
+
+ nf = select(nfds, SELECT_TYPE_ARG234 &tset, 0, 0, SELECT_TYPE_ARG5 timer);
+
+ if (nf < 0) {
+ if (errno == EINTR)
+ continue;
+ perror("gnuplot_x11: select failed");
+ EXIT(1);
+ }
+
+ if (nf > 0)
+ XNoOp(dpy);
+
+ if (XPending(dpy)) {
+ /* used to use CheckMaskEvent() but that cannot receive
+ * maskable events such as ClientMessage. So now we do
+ * one event, then return to the select.
+ * And that almost works, except that under some Xservers
+ * running without a window manager (e.g. Hummingbird Exceed under Win95)
+ * a bogus ConfigureNotify is sent followed by a valid ConfigureNotify
+ * when the window is maximized. The two events are queued, apparently
+ * in a single I/O because select() above doesn't see the second, valid
+ * event. This little loop fixes the problem by flushing the
+ * event queue completely.
+ */
+ XEvent xe;
+ do {
+ XNextEvent(dpy, &xe);
+ process_event(&xe);
+ } while (XPending(dpy));
+ }
+
+ if (FD_ISSET(in, &tset) || buffered_input_available) {
+ if (!record()) /* end of input */
+ return;
+ }
+#ifdef PIPE_IPC
+ if (!pipe_died && (FD_ISSET(out, &tset) || buffered_output_pending)) {
+ gp_exec_event(GE_pending, 0, 0, 0, 0, 0);
+ }
+#endif
+ /* A method in which the ErrorHandler can queue plots to be
+ removed from the linked list. This prevents the situation
+ that could arise if the ErrorHandler directly removed
+ plots and some other part of the program were utilizing
+ a pointer to a plot that was destroyed. */
+ if (process_remove_fifo_queue) {
+ Process_Remove_FIFO_Queue();
+ }
+ }
+}
+
+#elif defined(CRIPPLED_SELECT)
+
+char X11_ipcpath[32];
+
+/*
+ * CRIPPLED_SELECT mainloop
+ */
+static void
+mainloop()
+{
+ SELECT_TYPE_ARG1 nf, nfds, cn = ConnectionNumber(dpy);
+ struct timeval timeout, *timer;
+ fd_set tset;
+ unsigned long all = (unsigned long) (-1L);
+ XEvent xe;
+
+ timeout.tv_sec = 1;
+ timeout.tv_usec = 0;
+ timer = &timeout;
+ sprintf(X11_ipcpath, "/tmp/Gnuplot_%d", getppid());
+ nfds = cn + 1;
+
+ while (1) {
+ XFlush(dpy); /* see above */
+
+ FD_ZERO(&tset);
+ FD_SET(cn, &tset);
+
+ /* Don't wait for events if we know that input is
+ * already sitting in a buffer. Also don't wait for
+ * input to become available.
+ */
+ if (buffered_input_available) {
+ timeout.tv_sec = 0;
+ timeout.tv_usec = 0;
+ timer = &timeout;
+ } else {
+ timer = (struct timeval *) 0;
+ FD_SET(in, &tset);
+ }
+
+ nfds = (cn > in) ? cn + 1 : in + 1;
+
+ nf = select(nfds, SELECT_TYPE_ARG234 &tset, 0, 0, SELECT_TYPE_ARG5 timer);
+
+ if (nf < 0) {
+ if (errno == EINTR)
+ continue;
+ perror("gnuplot_x11: select failed");
+ EXIT(1);
+ }
+
+ if (nf > 0)
+ XNoOp(dpy);
+
+ if (FD_ISSET(cn, &tset)) {
+ while (XCheckMaskEvent(dpy, all, &xe)) {
+ process_event(&xe);
+ }
+ }
+ if ((X11_ipc = fopen(X11_ipcpath, "r"))) {
+ unlink(X11_ipcpath);
+ record();
+ fclose(X11_ipc);
+ }
+ }
+}
+
+
+#elif defined(VMS)
+/*-----------------------------------------------------------------------------
+ * VMS mainloop - Yehavi Bourvine - YEHAVI@VMS.HUJI.AC.IL
+ *---------------------------------------------------------------------------*/
+
+/* In VMS there is no decent Select(). hence, we have to loop inside
+ * XGetNextEvent for getting the next X window event. In order to get input
+ * from the master we assign a channel to SYS$INPUT and use AST's in order to
+ * receive data. In order to exit the mainloop, we need to somehow make
+ * XNextEvent return from within the ast. We do this with a XSendEvent() to
+ * ourselves !
+ * This needs a window to send the message to, so we create an unmapped window
+ * for this purpose. Event type XClientMessage is perfect for this, but it
+ * appears that such messages come from elsewhere (motif window manager,
+ * perhaps ?) So we need to check fairly carefully that it is the ast event
+ * that has been received.
+ */
+
+#include <iodef.h>
+char STDIIN[] = "SYS$INPUT:";
+short STDIINchannel, STDIINiosb[4];
+struct {
+ short size, type;
+ char *address;
+} STDIINdesc;
+char STDIINbuffer[64];
+int status;
+
+ast()
+{
+ int status = sys$qio(0, STDIINchannel, IO$_READVBLK, STDIINiosb, record,
+ 0, STDIINbuffer, sizeof(STDIINbuffer) - 1, 0, 0, 0, 0);
+ if ((status & 0x1) == 0)
+ EXIT(status);
+}
+
+Window message_window;
+
+static void
+mainloop()
+{
+ /* dummy unmapped window for receiving internally-generated terminate
+ * messages
+ */
+ message_window = XCreateSimpleWindow(dpy, root, 0, 0, 1, 1, 1, 0, 0);
+
+ STDIINdesc.size = strlen(STDIIN);
+ STDIINdesc.type = 0;
+ STDIINdesc.address = STDIIN;
+ status = sys$assign(&STDIINdesc, &STDIINchannel, 0, 0, 0);
+ if ((status & 0x1) == 0)
+ EXIT(status);
+ ast();
+
+ for (;;) {
+ XEvent xe;
+ XNextEvent(dpy, &xe);
+ if (xe.type == ClientMessage && xe.xclient.window == message_window) {
+ if (xe.xclient.message_type == None && xe.xclient.format == 8 && strcmp(xe.xclient.data.b, "die gnuplot die") == 0) {
+ FPRINTF((stderr, "quit message from ast\n"));
+ return;
+ } else {
+ FPRINTF((stderr, "Bogus XClientMessage event from window manager ?\n"));
+ }
+ }
+ process_event(&xe);
+ }
+}
+#else /* !(DEFAULT_X11 || CRIPPLED_SELECT || VMS */
+# error You lose. No mainloop.
+#endif /* !(DEFAULT_X11 || CRIPPLED_SELECT || VMS */
+
+/* delete a window / plot */
+static void
+delete_plot(plot_struct *plot)
+{
+ int i;
+
+ FPRINTF((stderr, "Delete plot %d\n", plot->plot_number));
+
+ for (i = 0; i < plot->ncommands; ++i)
+ free(plot->commands[i]);
+ plot->ncommands = 0;
+ if (plot->commands)
+ free(plot->commands);
+ plot->commands = NULL;
+ plot->max_commands = 0;
+
+
+ /* Free up memory for window title. */
+ if (plot->titlestring) {
+ free(plot->titlestring);
+ plot->titlestring = 0;
+ }
+
+ if (plot->window) {
+ FPRINTF((stderr, "Destroy window 0x%x\n", plot->window));
+ XDestroyWindow(dpy, plot->window);
+ plot->window = None;
+ --windows_open;
+ }
+
+ if (stipple_initialized) { /* ULIG */
+ int i;
+ for (i = 0; i < stipple_pattern_num; i++)
+ XFreePixmap(dpy, stipple_pattern[i]);
+ stipple_initialized = 0;
+ }
+
+ if (plot->pixmap) {
+ XFreePixmap(dpy, plot->pixmap);
+ plot->pixmap = None;
+ }
+ /* Release the colormap here to free color resources, but only
+ * if this plot is using a colormap not used by another plot
+ * and is not using the current colormap.
+ */
+ if (plot->cmap != current_cmap && !Find_Plot_In_Linked_List_By_CMap(plot->cmap))
+ Remove_CMap_From_Linked_List(plot->cmap);
+ /* but preserve geometry */
+}
+
+
+/* prepare the plot structure */
+static void
+prepare_plot(plot_struct *plot, int term_number)
+{
+ int i;
+
+ for (i = 0; i < plot->ncommands; ++i)
+ free(plot->commands[i]);
+ plot->ncommands = 0;
+
+ if (!plot->posn_flags) {
+ /* first time this window has been used - use default or -geometry
+ * settings
+ */
+ plot->posn_flags = gFlags;
+ plot->x = gX;
+ plot->y = gY;
+ plot->width = gW;
+ plot->height = gH;
+ plot->pixmap = None;
+#ifdef USE_MOUSE
+ plot->gheight = gH;
+ plot->resizing = FALSE;
+ plot->str[0] = '\0';
+ plot->zoombox_on = FALSE;
+#endif
+ }
+ if (!plot->window) {
+ plot->cmap = current_cmap; /* color space */
+ RecolorWindow(plot);
+ pr_window(plot);
+#ifdef USE_MOUSE
+ /*
+ * set all mouse parameters
+ * to a well-defined state.
+ */
+ plot->button = 0;
+ plot->mouse_on = TRUE;
+ plot->x = NOT_AVAILABLE;
+ plot->y = NOT_AVAILABLE;
+ if (plot->str[0] != '\0') {
+ /* if string was non-empty last time, initialize it as almost empty: one space, to prevent window resize */
+ plot->str[0] = ' ';
+ plot->str[1] = '\0';
+ }
+ plot->time = 0; /* XXX how should we initialize this ? XXX */
+#endif
+ }
+
+ /* We don't know that it is the same window as before, so we reset the
+ * cursors for all windows and then define the cursor for the active
+ * window
+ */
+ plot->angle = 0; /* default is horizontal */
+ reset_cursor();
+ XDefineCursor(dpy, plot->window, cursor);
+}
+
+/* store a command in a plot structure */
+static void
+store_command(char *buffer, plot_struct *plot)
+{
+ char *p;
+
+ /* binary can't be printed as string */
+#if defined(WITH_IMAGE) && defined(BINARY_X11_POLYGON)
+ if (*buffer == X11_GR_IMAGE || *buffer == X11_GR_FILLED_POLYGON || *buffer == X11_GR_SET_COLOR)
+#else
+#ifdef WITH_IMAGE
+ if (*buffer == X11_GR_IMAGE)
+#endif
+#ifdef BINARY_X11_POLYGON
+ if (*buffer != X11_GR_FILLED_POLYGON && *buffer != X11_GR_SET_COLOR)
+#endif
+#endif
+#if defined(WITH_IMAGE) || defined(BINARY_X11_POLYGON)
+ {FPRINTF((stderr, "Store in %d : %c\n", plot->plot_number, *buffer));}
+ else
+#endif
+ {FPRINTF((stderr, "Store in %d : %s", plot->plot_number, buffer));}
+
+ if (plot->ncommands >= plot->max_commands) {
+ plot->max_commands = plot->max_commands * 2 + 1;
+ plot->commands = (plot->commands)
+ ? (char **) realloc(plot->commands, plot->max_commands * sizeof(char *))
+ : (char **) malloc(sizeof(char *));
+ }
+ p = (char *) malloc((unsigned) strlen(buffer) + 1);
+ if (!plot->commands || !p) {
+ fputs("gnuplot: can't get memory. X11 aborted.\n", stderr);
+ EXIT(1);
+ }
+ plot->commands[plot->ncommands++] = strcpy(p, buffer);
+}
+
+#ifndef VMS
+
+static int read_input __PROTO((void));
+
+/*
+ * Handle input. Use read instead of fgets because stdio buffering
+ * causes trouble when combined with calls to select.
+ */
+static int
+read_input()
+{
+ static int rdbuf_size = 10 * Nbuf;
+ static char rdbuf[10 * Nbuf];
+ static int total_chars;
+ static int rdbuf_offset;
+ static int buf_offset;
+ static int partial_read = 0;
+ int fd = fileno(X11_ipc);
+
+ if (!partial_read)
+ buf_offset = 0;
+
+ if (!buffered_input_available) {
+ total_chars = read(fd, rdbuf, rdbuf_size);
+ buffered_input_available = 1;
+ partial_read = 0;
+ rdbuf_offset = 0;
+ if (total_chars == 0)
+ return -2;
+ if (total_chars < 0)
+ return -1;
+ }
+
+ if (rdbuf_offset < total_chars) {
+ while (rdbuf_offset < total_chars && buf_offset < Nbuf) {
+ char c = rdbuf[rdbuf_offset++];
+ buf[buf_offset++] = c;
+ if (c == '\n')
+ break;
+ }
+
+ if (buf_offset == Nbuf) {
+ fputs("\ngplt_x11.c: buffer overflow in read_input!\n"
+ " X11 aborted.\n", stderr);
+ EXIT(1);
+ } else
+ buf[buf_offset] = NUL;
+ }
+
+ if (rdbuf_offset == total_chars) {
+ buffered_input_available = 0;
+ if (buf[buf_offset - 1] != '\n')
+ partial_read = 1;
+ }
+
+ return partial_read;
+}
+
+static void read_input_line __PROTO((void));
+
+/*
+ * Handle a whole input line, issuing an error message if a complete
+ * read does not appear after a few tries.
+ */
+static void
+read_input_line()
+{
+ int i_read;
+ for (i_read = 1; read_input() == 1; i_read++) {
+ if (i_read == 5)
+ fprintf(stderr, "\ngplt_x11.c: A complete buffer instruction is not appearing across\n"
+ " link. Check for system overload or driver error.\n");
+ };
+}
+
+/*
+ * This function builds back a palette from what x11.trm has written
+ * into the pipe. It cheats: SMPAL_COLOR_MODE_FUNCTIONS for user defined
+ * formulaes to transform gray into the three color components is not
+ * implemented. If this had to be done, one would have to include all
+ * the code for evaluating functions here too, and, even worse: how to
+ * transmit all function definition from gnuplot to here!
+ * To avoid this, the following is done: For grayscale, and rgbformulae
+ * everything is easy. Gradients are more difficult: Each gradient point
+ * is encoded in a 8-byte string (which does not include any '\n' and
+ * transmitted here. Here the gradient is rebuilt.
+ * Form user defined formulae x11.trm builds a special gradient: The gray
+ * values are equally spaced and are not transmitted, only the 6 bytes for
+ * the rgbcolor are sent. These are assembled into a gradient which is
+ * than used in the palette.
+ *
+ * This function belongs completely into record(), but is quiet large so it
+ * became a function of its own.
+*/
+static void
+scan_palette_from_buf(void)
+{
+ t_sm_palette tpal;
+ char cm, pos, mod;
+ if (4 != sscanf( buf+2, "%c %c %c %d", &cm, &pos, &mod,
+ &(tpal.use_maxcolors) ) ) {
+ fprintf( stderr, "%s:%d error in setting palette.\n",
+ __FILE__, __LINE__);
+
+ return;
+ }
+
+ tpal.colorMode = cm;
+ tpal.positive = pos;
+ tpal.cmodel = mod;
+ tpal.gradient = NULL;
+
+ /* function palettes are transmitted as approximated gradients: */
+ if (tpal.colorMode == SMPAL_COLOR_MODE_FUNCTIONS)
+ tpal.colorMode = SMPAL_COLOR_MODE_GRADIENT;
+
+ switch( tpal.colorMode ) {
+ case SMPAL_COLOR_MODE_GRAY:
+ read_input_line();
+ if (1 != sscanf( buf, "%lf", &(tpal.gamma) )) {
+ fprintf( stderr, "%s:%d error in setting palette.\n",
+ __FILE__, __LINE__);
+ return;
+ }
+ break;
+ case SMPAL_COLOR_MODE_RGB:
+ read_input_line();
+ if (3 != sscanf( buf, "%d %d %d", &(tpal.formulaR),
+ &(tpal.formulaG), &(tpal.formulaB) )) {
+ fprintf( stderr, "%s:%d error in setting palette.\n",
+ __FILE__, __LINE__);
+ return;
+ }
+ break;
+ case SMPAL_COLOR_MODE_GRADIENT: {
+ int i=0;
+ read_input_line();
+ if (1 != sscanf( buf, "%d", &(tpal.gradient_num) )) {
+ fprintf( stderr, "%s:%d error in setting palette.\n",
+ __FILE__, __LINE__);
+ return;
+ }
+ tpal.gradient = (gradient_struct*)
+ malloc( tpal.gradient_num * sizeof(gradient_struct) );
+ assert(tpal.gradient);
+ for( i=0; i<tpal.gradient_num; i++ ) {
+ /* this %50 *must* match the amount of gradient structs
+ written to the pipe by x11.trm! */
+ if (i%50 == 0) {
+ read_input_line();
+ }
+ str_to_gradient_entry( &(buf[8*(i%50)]), &(tpal.gradient[i]) );
+ }
+ break;
+ }
+ case SMPAL_COLOR_MODE_FUNCTIONS:
+ fprintf( stderr, "%s:%d ooops: No function palettes for x11!\n",
+ __FILE__, __LINE__ );
+ break;
+ default:
+ fprintf( stderr, "%s:%d ooops: Unknown colorMode '%c'.\n",
+ __FILE__, __LINE__, (char)(tpal.colorMode) );
+ tpal.colorMode = SMPAL_COLOR_MODE_GRAY;
+ break;
+ }
+ PaletteMake(&tpal);
+
+ if (tpal.gradient)
+ free(tpal.gradient);
+}
+
+
+/*
+ * record - record new plot from gnuplot inboard X11 driver (Unix)
+ */
+static struct plot_struct *plot = NULL;
+static int
+record()
+{
+ while (1) {
+ int status = read_input();
+ if (status == -2)
+ return 0;
+ if (status != 0)
+ return status;
+
+ switch (*buf) {
+ case 'G': /* enter graphics mode */
+ {
+ int plot_number;
+#ifndef USE_MOUSE
+ sscanf(buf, "G%d", &plot_number);
+#else
+#ifdef OS2_IPC
+ sscanf(buf, "G%d %lu %li", &plot_number, &gnuplotXID, &ppidGnu);
+#else
+ sscanf(buf, "G%d %lu", &plot_number, &gnuplotXID);
+#endif
+#endif
+ FPRINTF((stderr, "plot for window number %d\n", plot_number));
+ if (!(plot = Find_Plot_In_Linked_List_By_Number(plot_number)))
+ plot = Add_Plot_To_Linked_List(plot_number);
+ if (plot)
+ prepare_plot(plot, plot_number);
+ current_plot = plot;
+#ifdef OS2_IPC
+ if (!input_from_PM_Terminal) { /* get shared memory */
+ sprintf(mouseShareMemName, "\\SHAREMEM\\GP%i_Mouse_Input", (int) ppidGnu);
+ if (DosGetNamedSharedMem(&input_from_PM_Terminal, mouseShareMemName, PAG_WRITE))
+ DosBeep(1440L, 1000L); /* indicates error */
+ semInputReady = 0;
+ }
+#endif
+#ifdef USE_MOUSE
+#ifdef TITLE_BAR_DRAWING_MSG
+ /* show a message in the wm's title bar that the
+ * graph will be redrawn. This might be useful
+ * for slow redrawing (large plots). The title
+ * string is reset to the default at the end of
+ * display(). We should make this configurable!
+ */
+ if (plot) {
+ if (plot->window) {
+ char *msg;
+ char *added_text = " drawing ...";
+ int orig_len = (plot->titlestring ? strlen(plot->titlestring) : 0);
+ if (msg = (char *) malloc(orig_len + strlen(added_text) + 1)) {
+ strcpy(msg, plot->titlestring);
+ strcat(msg, added_text);
+ XStoreName(dpy, plot->window, msg);
+ free(msg);
+ } else
+ XStoreName(dpy, plot->window, added_text + 1);
+ }
+ }
+#else
+ if (!button_pressed) {
+ cursor_save = cursor;
+ cursor = cursor_waiting;
+ if (plot)
+ XDefineCursor(dpy, plot->window, cursor);
+ }
+#endif
+#endif
+ /* continue; */
+ /* return 1; To do: Should a "return" be here? Gnuplot usually sends 'G' followed by other commands. So perhaps not needed. DJS */
+ }
+ break;
+ case 'N': /* just update the plot number */
+ {
+ int itmp;
+ if (strcspn(buf+1, " \n") && sscanf(buf, "N%d", &itmp))
+ current_plot = Add_Plot_To_Linked_List(itmp);
+ return 1;
+ }
+ break;
+ case 'C': /* close the plot with given number */
+ {
+ int itmp;
+ if (strcspn(buf+1, " \n") && sscanf(buf, "C%d", &itmp)) {
+ plot_struct *psp;
+ if ((psp = Find_Plot_In_Linked_List_By_Number(itmp)))
+ Remove_Plot_From_Linked_List(psp->window);
+ } else if (current_plot) {
+ Remove_Plot_From_Linked_List(current_plot->window);
+ }
+ return 1;
+ }
+ break;
+ case '^': /* raise the plot with given number or the whole group */
+ {
+ int itmp;
+ if (strcspn(buf+1," \n") && sscanf(buf, "^%d", &itmp)) {
+ plot_struct *psp;
+ if ((psp = Find_Plot_In_Linked_List_By_Number(itmp))) {
+ XRaiseWindow(dpy, psp->window);
+ }
+ } else {
+ /* Find end of list, i.e., first created. */
+ plot_struct *psp = plot_list_start;
+ while (psp != NULL) {
+ if (psp->next_plot == NULL) break;
+ psp = psp->next_plot;
+ }
+ while (psp != NULL) {
+ XRaiseWindow(dpy, psp->window);
+ psp = psp->prev_plot;
+ }
+ }
+ return 1;
+ }
+ break;
+ case 'v': /* lower the plot with given number or the whole group */
+ {
+ int itmp;
+ if (strcspn(buf+1," \n") && sscanf(buf, "v%d", &itmp)) {
+ plot_struct *psp;
+ if ((psp = Find_Plot_In_Linked_List_By_Number(itmp))) {
+ XLowerWindow(dpy, psp->window);
+ }
+ } else if (current_plot) {
+ plot_struct *psp = plot_list_start;
+ while (psp != NULL) {
+ XLowerWindow(dpy, psp->window);
+ psp = psp->next_plot;
+ }
+ }
+ return 1;
+ }
+ break;
+ case 'n': /* update the plot name (title) */
+ {
+ if (!current_plot)
+ current_plot = Add_Plot_To_Linked_List(0);
+ if (current_plot) {
+ char *cp;
+ if (current_plot->titlestring)
+ free(current_plot->titlestring);
+ if ((current_plot->titlestring = (char *) malloc(strlen(buf+1) + 1) )) {
+ strcpy(current_plot->titlestring, buf+1);
+ cp = current_plot->titlestring;
+ } else
+ cp = "<lost name>";
+ if (current_plot->window)
+ XStoreName(dpy, current_plot->window, cp);
+ }
+ return 1;
+ }
+ break;
+ case 'E': /* leave graphics mode / suspend */
+ if (plot)
+ display(plot);
+#ifdef USE_MOUSE
+ if (plot == current_plot)
+ gp_exec_event(GE_plotdone, 0, 0, 0, 0, 0); /* notify main program */
+#endif
+ return 1;
+ case 'R': /* leave x11 mode */
+ reset_cursor();
+ return 0;
+
+ case X11_GR_MAKE_PALETTE:
+ if (have_pm3d)
+ scan_palette_from_buf();
+ return 1;
+#if 0
+/* (DJS 28sep2004) Possibly remove. Not sure this is useful for anything.
+ * What gnuplot command would issue this? When would gnuplot know it is OK to
+ * release a palette inside gnuplot_x11? I see no X11_GR_RELEASE_PALETTE
+ * or 'e' inside x11.trm.
+ *
+ * Plots and colormaps are independent in new scheme, so the line below
+ * "if (plot)" is outdated. Also, sm_palette is a global, static structure.
+ * sm_palette.gradient should be set to NULL after freeing the memory.
+ */
+ case X11_GR_RELEASE_PALETTE:
+ /* turn pm3d off */
+ FPRINTF((stderr, "X11_GR_RELEASE_PALETTE\n"));
+ if (plot)
+ ReleaseColormap(plot);
+ sm_palette.colorMode = SMPAL_COLOR_MODE_NONE;
+ free( sm_palette.gradient );
+ return 1;
+#endif
+
+#if defined(WITH_IMAGE) || defined(BINARY_X11_POLYGON)
+ case X11_GR_CHECK_ENDIANESS:
+ {
+ /* Initialize variable in case short happens to be longer than two bytes. */
+ unsigned short tmp = (unsigned short) ENDIAN_VALUE;
+ ((char *)&tmp)[0] = buf[1];
+ ((char *)&tmp)[1] = buf[2];
+ if (tmp == (unsigned short) ENDIAN_VALUE) swap_endian = 0;
+ else swap_endian = 1;
+ }
+ return 1;
+#endif
+
+ case 'X': /* tell the driver about do_raise / persist */
+ {
+ int tmp_do_raise = UNSET, tmp_persist = UNSET;
+ int tmp_dashed = UNSET, tmp_ctrlq = UNSET;
+ sscanf(buf, "X%d%d%d%d", &tmp_do_raise, &tmp_persist, &tmp_dashed, &tmp_ctrlq);
+ if (UNSET != tmp_do_raise)
+ do_raise = tmp_do_raise;
+ if (UNSET != tmp_persist)
+ persist = tmp_persist;
+ if (UNSET != tmp_dashed)
+ dashedlines = tmp_dashed;
+ if (UNSET != tmp_ctrlq)
+ ctrlq = tmp_ctrlq;
+ }
+ return 1;
+
+ case 's': /* set window geometry */
+ {
+ char strtmp[256];
+ sscanf(&buf[2], "%s", strtmp);
+ pr_geometry(strtmp);
+ }
+ return 1;
+
+#ifdef USE_MOUSE
+ case 'u':
+#ifdef PIPE_IPC
+ if (!pipe_died)
+#endif
+ {
+ /* `set cursor' */
+ int c, x, y;
+ sscanf(buf, "u%4d%4d%4d", &c, &x, &y);
+ if (plot) {
+ switch (c) {
+ case -4: /* switch off line between ruler and mouse cursor */
+ DrawLineToRuler(plot);
+ plot->ruler_lineto_on = FALSE;
+ break;
+ case -3: /* switch on line between ruler and mouse cursor */
+ if (plot->ruler_on && plot->ruler_lineto_on)
+ break;
+ plot->ruler_lineto_x = X(x);
+ plot->ruler_lineto_y = Y(y);
+ plot->ruler_lineto_on = TRUE;
+ DrawLineToRuler(plot);
+ break;
+ case -2: /* warp pointer */
+ XWarpPointer(dpy, None /* src_w */ ,
+ plot->window /* dest_w */ , 0, 0, 0, 0, X(x), Y(y));
+ case -1: /* zoombox */
+ plot->zoombox_x1 = plot->zoombox_x2 = X(x);
+ plot->zoombox_y1 = plot->zoombox_y2 = Y(y);
+ plot->zoombox_on = TRUE;
+ DrawBox(plot);
+ break;
+ case 0: /* standard cross-hair cursor */
+ cursor = cursor_default;
+ XDefineCursor(dpy, plot->window, cursor);
+ break;
+ case 1: /* cursor during rotation */
+ cursor = cursor_exchange;
+ XDefineCursor(dpy, plot->window, cursor);
+ break;
+ case 2: /* cursor during scaling */
+ cursor = cursor_sizing;
+ XDefineCursor(dpy, plot->window, cursor);
+ break;
+ case 3: /* cursor during zooming */
+ cursor = cursor_zooming;
+ XDefineCursor(dpy, plot->window, cursor);
+ break;
+ }
+ if (c >= 0 && plot->zoombox_on) {
+ /* erase zoom box */
+ DrawBox(plot);
+ plot->zoombox_on = FALSE;
+ }
+ if (c >= 0 && plot->ruler_lineto_on) {
+ /* erase line from ruler to cursor */
+ DrawLineToRuler(plot);
+ plot->ruler_lineto_on = FALSE;
+ }
+ }
+ }
+ return 1;
+
+ case 't':
+#ifdef PIPE_IPC
+ if (!pipe_died)
+#endif
+ {
+ int where;
+ char *second;
+ if (sscanf(buf, "t%4d", &where) != 1)
+ return 1;
+ buf[strlen(buf) - 1] = 0; /* remove trailing \n */
+ if (plot) {
+ switch (where) {
+ case 0:
+ DisplayCoords(plot, buf + 5);
+ break;
+ case 1:
+ second = strchr(buf + 5, '\r');
+ if (second == NULL) {
+ *(plot->zoombox_str1a) = '\0';
+ *(plot->zoombox_str1b) = '\0';
+ break;
+ }
+ *second = 0;
+ second++;
+ if (plot->zoombox_on)
+ DrawBox(plot);
+ strcpy(plot->zoombox_str1a, buf + 5);
+ strcpy(plot->zoombox_str1b, second);
+ if (plot->zoombox_on)
+ DrawBox(plot);
+ break;
+ case 2:
+ second = strchr(buf + 5, '\r');
+ if (second == NULL) {
+ *(plot->zoombox_str2a) = '\0';
+ *(plot->zoombox_str2b) = '\0';
+ break;
+ }
+ *second = 0;
+ second++;
+ if (plot->zoombox_on)
+ DrawBox(plot);
+ strcpy(plot->zoombox_str2a, buf + 5);
+ strcpy(plot->zoombox_str2b, second);
+ if (plot->zoombox_on)
+ DrawBox(plot);
+ break;
+ }
+ }
+ }
+ return 1;
+
+ case 'r':
+#ifdef PIPE_IPC
+ if (!pipe_died)
+#endif
+ {
+ if (plot) {
+ int x, y;
+ DrawRuler(plot); /* erase previous ruler */
+ sscanf(buf, "r%4d%4d", &x, &y);
+ if (x < 0) {
+ DrawLineToRuler(plot);
+ plot->ruler_on = FALSE;
+ } else {
+ plot->ruler_on = TRUE;
+ plot->ruler_x = x;
+ plot->ruler_y = y;
+ plot->ruler_lineto_x = X(x);
+ plot->ruler_lineto_y = Y(y);
+ DrawLineToRuler(plot);
+ }
+ DrawRuler(plot); /* draw new one */
+ }
+ }
+ return 1;
+
+ case 'z':
+#ifdef PIPE_IPC
+ if (!pipe_died)
+#endif
+ {
+ int len = strlen(buf + 1) - 1; /* discard newline '\n' */
+ memcpy(selection, buf + 1, len < SEL_LEN ? len : SEL_LEN);
+ /* terminate */
+ selection[len + 1 < SEL_LEN ? len + 1 : SEL_LEN - 1] = '\0';
+ XStoreBytes(dpy, buf + 1, len);
+ XFlush(dpy);
+#ifdef EXPORT_SELECTION
+ if (plot && exportselection)
+ export_graph(plot);
+#endif
+ }
+ return 1;
+#endif
+#ifdef USE_MOUSE
+ case 'Q':
+ /* Set default font immediately and return size info through pipe */
+ if (buf[1] == 'G') {
+ int scaled_hchar, scaled_vchar;
+ char *c = &(buf[strlen(buf)-1]);
+ while (*c <= ' ') *c-- = '\0';
+ strncpy(default_font, &buf[2], strlen(&buf[2])+1);
+ pr_font(NULL);
+ if (plot) {
+ /* EAM FIXME - this is all out of order; initialization doesnt */
+ /* happen until term->graphics() is called. */
+ xscale = (plot->width > 0) ? plot->width / 4096. : gW / 4096.;
+ yscale = (plot->height > 0) ? plot->height / 4096. : gH / 4096.;
+ scaled_hchar = (1.0/xscale) * hchar;
+ scaled_vchar = (1.0/yscale) * vchar;
+ FPRINTF((stderr, "gplt_x11: preset default font to %s hchar = %d vchar = %d \n",
+ default_font, scaled_hchar, scaled_vchar));
+ gp_exec_event(GE_fontprops, plot->width, plot->height,
+ scaled_hchar, scaled_vchar, 0);
+ }
+ return 1;
+ }
+ /* fall through */
+#endif
+ default:
+ if (plot)
+ store_command(buf, plot);
+ continue;
+ }
+ }
+ if (feof(X11_ipc) || ferror(X11_ipc))
+ return 0;
+ else
+ return 1;
+}
+
+#else /* VMS */
+
+/*
+ * record - record new plot from gnuplot inboard X11 driver (VMS)
+ */
+static struct plot_struct *plot = NULL;
+record()
+{
+ int status;
+
+ if ((STDIINiosb[0] & 0x1) == 0)
+ EXIT(STDIINiosb[0]);
+ STDIINbuffer[STDIINiosb[1]] = '\0';
+ strcpy(buf, STDIINbuffer);
+
+ switch (*buf) {
+ case 'G': /* enter graphics mode */
+ {
+ int plot_number = atoi(buf + 1); /* 0 if none specified */
+ FPRINTF((stderr, "plot for window number %d\n", plot_number));
+ if (!(plot = Find_Plot_In_Linked_List_By_Number(plot_number)))
+ plot = Add_Plot_To_Linked_List(plot_number);
+ if (plot)
+ prepare_plot(plot, plot_number);
+ current_plot = plot;
+ break;
+ }
+ case 'E': /* leave graphics mode */
+ if (plot)
+ display(plot);
+ break;
+ case 'R': /* exit x11 mode */
+ FPRINTF((stderr, "received R - sending ClientMessage\n"));
+ reset_cursor();
+ sys$cancel(STDIINchannel);
+ /* this is ridiculous - cook up an event to ourselves,
+ * in order to get the mainloop() out of the XNextEvent() call
+ * it seems that window manager can also send clientmessages,
+ * so put a checksum into the message
+ */
+ {
+ XClientMessageEvent event;
+ event.type = ClientMessage;
+ event.send_event = True;
+ event.display = dpy;
+ event.window = message_window;
+ event.message_type = None;
+ event.format = 8;
+ strcpy(event.data.b, "die gnuplot die");
+ XSendEvent(dpy, message_window, False, 0, (XEvent *) & event);
+ XFlush(dpy);
+ }
+ return; /* no ast */
+ default:
+ if (plot)
+ store_command(buf, plot);
+ break;
+ }
+ ast();
+}
+#endif /* VMS */
+
+static int
+DrawRotatedErrorHandler(Display * display, XErrorEvent * error_event)
+{
+ return 0; /* do nothing */
+}
+
+static void
+DrawRotated(plot_struct *plot, Display *dpy, GC gc, int xdest, int ydest,
+ const char *str, int len)
+{
+ Window w = plot->window;
+ Drawable d = plot->pixmap;
+ double angle = plot->angle;
+ enum JUSTIFY just = plot->jmode;
+ int x, y;
+ double src_x, src_y;
+ double dest_x, dest_y;
+ int width = gpXTextWidth(font, str, len);
+ int height = vchar;
+ double src_cen_x = (double)width * 0.5;
+ double src_cen_y = (double)height * 0.5;
+ static const double deg2rad = .01745329251994329576; /* atan2(1, 1) / 45.0; */
+ double sa = sin(angle * deg2rad);
+ double ca = cos(angle * deg2rad);
+ int dest_width = (double)height * fabs(sa) + (double)width * fabs(ca) + 2;
+ int dest_height = (double)width * fabs(sa) + (double)height * fabs(ca) + 2;
+ double dest_cen_x = (double)dest_width * 0.5;
+ double dest_cen_y = (double)dest_height * 0.5;
+ char* data = (char*) malloc(dest_width * dest_height * sizeof(char));
+ Pixmap pixmap_src = XCreatePixmap(dpy, root, (unsigned int)width, (unsigned int)height, 1);
+ XImage *image_src;
+ XImage *image_dest;
+ XImage *image_scr;
+ unsigned long fgpixel = 0;
+ unsigned long bgpixel = 0;
+ XWindowAttributes win_attrib;
+ int xscr, yscr, xoff, yoff;
+ unsigned int scr_width, scr_height;
+ XErrorHandler prevErrorHandler;
+
+ unsigned long gcFunctionMask = GCFunction;
+ XGCValues gcValues;
+ int gcCurrentFunction = 0;
+ Status s;
+
+ /* bitmapGC is static, so that is has to be initialized only once */
+ static GC bitmapGC = (GC) 0;
+
+ /* eventually initialize bitmapGC */
+ if ((GC)0 == bitmapGC) {
+ bitmapGC = XCreateGC(dpy, pixmap_src, 0, (XGCValues *) 0);
+ XSetForeground(dpy, bitmapGC, 1);
+ XSetBackground(dpy, bitmapGC, 0);
+ }
+
+ s = XGetGCValues(dpy, gc, gcFunctionMask|GCForeground|GCBackground, &gcValues);
+ if (s) {
+ /* success */
+ fgpixel = gcValues.foreground;
+ bgpixel = gcValues.background;
+ gcCurrentFunction = gcValues.function; /* save current function */
+ }
+
+ /* set font for the bitmap GC */
+ if (font)
+ gpXSetFont(dpy, bitmapGC, font->fid);
+
+ /* draw string to the source bitmap */
+ gpXDrawImageString(dpy, pixmap_src, bitmapGC, 0, gpXGetFontascent(font), str, len);
+
+ /* create XImage's of depth 1 */
+ /* source from pixmap */
+ image_src = XGetImage(dpy, pixmap_src, 0, 0, (unsigned int)width, (unsigned int)height,
+ 1, XYPixmap /* ZPixmap, XYBitmap */ );
+
+ /* empty dest */
+ assert(data);
+ memset((void*)data, 0, (size_t)dest_width * dest_height);
+ image_dest = XCreateImage(dpy, vis, 1, XYBitmap,
+ 0, data, (unsigned int)dest_width, (unsigned int)dest_height, 8, 0);
+#define RotateX(_x, _y) (( (_x) * ca + (_y) * sa + dest_cen_x))
+#define RotateY(_x, _y) ((-(_x) * sa + (_y) * ca + dest_cen_y))
+ /* copy & rotate from source --> dest */
+ for (y = 0, src_y = -src_cen_y; y < height; y++, src_y++) {
+ for (x = 0, src_x = -src_cen_x; x < width; x++, src_x++) {
+ /* TODO: move some operations outside the inner loop (joze) */
+ dest_x = rint(RotateX(src_x, src_y));
+ dest_y = rint(RotateY(src_x, src_y));
+ if (dest_x >= 0 && dest_x < dest_width && dest_y >= 0 && dest_y < dest_height)
+ XPutPixel(image_dest, (int)dest_x, (int)dest_y, XGetPixel(image_src, x, y));
+ }
+ }
+
+ src_cen_y = 0; /* EAM 29-Sep-2002 - vertical justification has already been done */
+
+ switch (just) {
+ case LEFT:
+ default:
+ xdest -= RotateX(-src_cen_x, src_cen_y);
+ ydest -= RotateY(-src_cen_x, src_cen_y);
+ break;
+ case CENTRE:
+ xdest -= RotateX(0, src_cen_y);
+ ydest -= RotateY(0, src_cen_y);
+ break;
+ case RIGHT:
+ xdest -= RotateX(src_cen_x, src_cen_y);
+ ydest -= RotateY(src_cen_x, src_cen_y);
+ break;
+ }
+
+#undef RotateX
+#undef RotateY
+
+ if (fast_rotate) {
+ /* This default method is a lot faster, but may corrupt the colors
+ * underneath the rotated text if the X display Visual is PseudoColor.
+ * EAM - August 2005
+ */
+ assert(s); /* Previous success in reading XGetGCValues() */
+ /* Force pixels of new text to black, background unchanged */
+ gcValues.function = GXand;
+ gcValues.background = WhitePixel(dpy, scr);
+ gcValues.foreground = BlackPixel(dpy, scr);
+ XChangeGC(dpy, gc, gcFunctionMask|GCBackground|GCForeground, &gcValues);
+ XPutImage(dpy, d, gc, image_dest, 0, 0, xdest, ydest, dest_width, dest_height);
+
+ /* Force pixels of new text to color, background unchanged */
+ gcValues.function = GXor;
+ gcValues.background = BlackPixel(dpy, scr);
+ gcValues.foreground = fgpixel;
+ XChangeGC(dpy, gc, gcFunctionMask|GCBackground|GCForeground, &gcValues);
+ XPutImage(dpy, d, gc, image_dest, 0, 0, xdest, ydest, dest_width, dest_height);
+
+ } else {
+ /* Slow but sure version - grab the current screen area where the new
+ * text will go and substitute in the pixels of the new text one by one.
+ * NB: selected by X Resource
+ * gnuplot*fastrotate: off
+ */
+ assert(s); /* Previous success in reading XGetGCValues() */
+ gcValues.function = GXcopy;
+ XChangeGC(dpy, gc, gcFunctionMask, &gcValues);
+ s = XGetWindowAttributes(dpy, w, &win_attrib);
+ /* compute screen coords that are within the current window */
+ xscr = (xdest<0)? 0 : xdest;
+ yscr = (ydest<0)? 0 : ydest;;
+ scr_width = dest_width; scr_height = dest_height;
+ if (xscr + dest_width > win_attrib.width)
+ scr_width = win_attrib.width - xscr;
+ if (yscr + dest_height > win_attrib.height)
+ scr_height = win_attrib.height - yscr;
+ xoff = xscr - xdest;
+ yoff = yscr - ydest;
+ scr_width -= xoff;
+ scr_height -= yoff;
+ prevErrorHandler = XSetErrorHandler(DrawRotatedErrorHandler);
+
+ image_scr = XGetImage(dpy, d, xscr, yscr, scr_width,
+ scr_height, AllPlanes, XYPixmap);
+ if (image_scr != 0){
+ /* copy from 1 bit bitmap image of text to the full depth image of screen*/
+ for (y = 0; y < scr_height; y++){
+ for (x = 0; x < scr_width; x++){
+ if (XGetPixel(image_dest, x + xoff, y + yoff)){
+ XPutPixel(image_scr, x, y, fgpixel);
+ }
+ }
+ }
+ /* copy the rotated image to the drawable d */
+ XPutImage(dpy, d, gc, image_scr, 0, 0, xscr, yscr, scr_width, scr_height);
+
+ XDestroyImage(image_scr);
+ }
+ XSetErrorHandler(prevErrorHandler);
+ } /* End slow rotatation code */
+
+ /* free resources */
+ XFreePixmap(dpy, pixmap_src);
+ XDestroyImage(image_src);
+ XDestroyImage(image_dest);
+
+ if (s) {
+ /* restore original state of gc */
+ gcValues.function = gcCurrentFunction;
+ gcValues.background = bgpixel;
+ XChangeGC(dpy, gc, gcFunctionMask|GCBackground, &gcValues);
+ }
+}
+
+/*
+ * exec_cmd - execute drawing command from inboard driver
+ */
+static void
+exec_cmd(plot_struct *plot, char *command)
+{
+ int x, y, sw, sl, sj;
+ char *buffer, *str;
+
+ buffer = command;
+ /* binary can't be printed as string */
+#if defined(WITH_IMAGE) && defined(BINARY_X11_POLYGON)
+ if (*buffer == X11_GR_IMAGE || *buffer == X11_GR_FILLED_POLYGON || *buffer == X11_GR_SET_COLOR)
+#else
+#ifdef WITH_IMAGE
+ if (*buffer == X11_GR_IMAGE)
+#endif
+#ifdef BINARY_X11_POLYGON
+ if (*buffer == X11_GR_FILLED_POLYGON && *buffer == X11_GR_SET_COLOR)
+#endif
+#endif
+#if defined(WITH_IMAGE) || defined(BINARY_X11_POLYGON)
+ {FPRINTF((stderr, "(display) buffer = |%c|\n", *buffer));}
+ else
+#endif
+ {FPRINTF((stderr, "(display) buffer = |%s|\n", buffer));}
+
+#ifdef X11_POLYLINE
+ /* X11_vector(x, y) - draw vector */
+ if (*buffer == 'V') {
+ sscanf(buffer, "V%4d%4d", &x, &y);
+ if (polyline_size == 0) {
+ polyline[polyline_size].x = X(cx);
+ polyline[polyline_size].y = Y(cy);
+ }
+ if (++polyline_size >= polyline_space) {
+ polyline_space += 100;
+ polyline = realloc(polyline, polyline_space * sizeof(XPoint));
+ if (!polyline) fprintf(stderr, "Panic: cannot realloc polyline\n");
+ }
+ polyline[polyline_size].x = X(x);
+ polyline[polyline_size].y = Y(y);
+ cx = x;
+ cy = y;
+ /* Limit the number of vertices in any single polyline */
+ if (polyline_size > max_request_size) {
+ FPRINTF((stderr, "(display) dumping polyline size %d\n", polyline_size));
+ XDrawLines(dpy, plot->pixmap, *current_gc,
+ polyline, polyline_size+1, CoordModeOrigin);
+ polyline_size = 0;
+ }
+ return;
+ } else if (polyline_size > 0) {
+ FPRINTF((stderr, "(display) dumping polyline size %d\n", polyline_size));
+ XDrawLines(dpy, plot->pixmap, *current_gc,
+ polyline, polyline_size+1, CoordModeOrigin);
+ polyline_size = 0;
+ }
+#else
+ /* X11_vector(x, y) - draw vector */
+ if (*buffer == 'V') {
+ sscanf(buffer, "V%4d%4d", &x, &y);
+ XDrawLine(dpy, plot->pixmap, *current_gc, X(cx), Y(cy), X(x), Y(y));
+ cx = x;
+ cy = y;
+ } else
+#endif
+ /* X11_move(x, y) - move */
+ if (*buffer == 'M')
+ sscanf(buffer, "M%4d%4d", &cx, &cy);
+
+ /* change default font (QD) encoding (QE) or current font (QF) */
+ else if (*buffer == 'Q') {
+ char *c;
+ switch (buffer[1]) {
+ case 'F':
+ /* Strip out just the font name */
+ c = &(buffer[strlen(buffer)-1]);
+ while (*c <= ' ') *c-- = '\0';
+ pr_font(&buffer[2]);
+ if (font)
+ gpXSetFont(dpy, gc, font->fid);
+ break;
+ case 'E':
+ /* Save the requested font encoding */
+ {
+ int tmp;
+ sscanf(buffer, "QE%d", &tmp);
+ encoding = (enum set_encoding_id)tmp;
+ }
+ FPRINTF((stderr, "gnuplot_x11: changing encoding to %d\n", encoding));
+ break;
+ case 'D':
+ /* Save the request default font */
+ c = &(buffer[strlen(buffer)-1]);
+ while (*c <= ' ') *c-- = '\0';
+ strncpy(default_font, &buffer[2], strlen(&buffer[2])+1);
+ FPRINTF((stderr, "gnuplot_x11: exec_cmd() set default_font to \"%s\"\n", default_font));
+ break;
+ }
+ }
+
+ /* X11_put_text(x, y, str) - draw text */
+ else if (*buffer == 'T') {
+ /* Enhanced text mode added November 2003 - Ethan A Merritt */
+ int x_offset=0, y_offset=0, v_offset=0;
+
+ switch (buffer[1]) {
+
+ case 'j': /* Set start for right-justified enhanced text */
+ sscanf(buffer+2, "%4d%4d", &x_offset, &y_offset);
+ plot->xLast = x_offset - (plot->xLast - x_offset);
+ plot->yLast = y_offset - (vchar/3) / yscale;
+ return;
+ case 'k': /* Set start for center-justified enhanced text */
+ sscanf(buffer+2, "%4d%4d", &x_offset, &y_offset);
+ plot->xLast = x_offset - 0.5*(plot->xLast - x_offset);
+ plot->yLast = y_offset - (vchar/3) / yscale;
+ return;
+ case 'l': /* Set start for left-justified enhanced text */
+ sscanf(buffer+2, "%4d%4d", &x_offset, &y_offset);
+ plot->xLast = x_offset;
+ plot->yLast = y_offset - (vchar/3) / yscale;
+ return;
+ case 'o': /* Enhanced mode print with no update */
+ case 'c': /* Enhanced mode print with update to center */
+ case 'u': /* Enhanced mode print with update */
+ case 's': /* Enhanced mode update with no print */
+ sscanf(buffer+2, "%4d%4d", &x_offset, &y_offset);
+ /* EAM FIXME - This code has only been tested for x_offset == 0 */
+ if (plot->angle != 0) {
+ int xtmp=0, ytmp=0;
+ xtmp += x_offset * cos((double)(plot->angle) * 0.01745);
+ xtmp -= y_offset * sin((double)(plot->angle) * 0.01745) * yscale/xscale;
+ ytmp += x_offset * sin((double)(plot->angle) * 0.01745) * xscale/yscale;
+ ytmp += y_offset * cos((double)(plot->angle) * 0.01745);
+ x_offset = xtmp;
+ y_offset = ytmp;
+ }
+ x = plot->xLast + x_offset;
+ y = plot->yLast + y_offset;
+ v_offset = 0;
+ str = buffer + 10;
+ break;
+ case 'p': /* Push (Save) position for later use */
+ plot->xSave = plot->xLast;
+ plot->ySave = plot->yLast;
+ return;
+ case 'r': /* Pop (Restore) saved position */
+ plot->xLast = plot->xSave;
+ plot->yLast = plot->ySave;
+ return;
+ default:
+ sscanf(buffer, "T%4d%4d", &x, &y);
+ v_offset = vchar/3; /* Why is this??? */
+ str = buffer + 9;
+ break;
+ }
+
+ sl = strlen(str) - 1;
+ sw = gpXTextWidth(font, str, sl);
+
+/* EAM - May 2002 Modify to allow colored text.
+ * 1) do not force foreground of gc to be black
+ * 2) write text to (*current_gc), rather than to gc, so that text color can be set
+ * using pm3d mappings.
+ */
+
+ switch (plot->jmode) {
+ default:
+ case LEFT:
+ sj = 0;
+ break;
+ case CENTRE:
+ sj = -sw / 2;
+ break;
+ case RIGHT:
+ sj = -sw;
+ break;
+ }
+
+ if (sl == 0) /* Pointless to draw empty string */
+ ;
+ else if (buffer[1] == 's') /* Enhanced text mode reserve space only */
+ ;
+ else if (plot->angle != 0) {
+ /* rotated text */
+ DrawRotated(plot, dpy, *current_gc, X(x), Y(y), str, sl);
+ } else {
+ /* horizontal text */
+ gpXDrawString(dpy, plot->pixmap, *current_gc,
+ X(x) + sj, Y(y) + v_offset, str, sl);
+ }
+
+ /* Update current text position */
+ if (buffer[1] == 'c') {
+ plot->xLast = RevX(X(x) + sj + sw/2) - x_offset;
+ plot->yLast = y - y_offset;
+ } else if (buffer[1] != 'o') {
+ plot->xLast = RevX(X(x) + sj + sw) - x_offset;
+ plot->yLast = y - y_offset;
+ if (plot->angle != 0) { /* This correction is not perfect */
+ plot->yLast += RevX(sw) * sin((plot->angle) * 0.01745) * xscale/yscale;
+ plot->xLast -= RevX(sw) * (1.0 - cos((plot->angle) * 0.01745));
+ }
+ }
+
+ } else if (*buffer == 'F') { /* fill box */
+ int style, xtmp, ytmp, w, h;
+
+ if (sscanf(buffer + 1, "%4d%4d%4d%4d%4d", &style, &xtmp, &ytmp, &w, &h) == 5) {
+ x11_setfill(&gc, style, FALSE);
+
+ /* gnuplot has origin at bottom left, but X uses top left
+ * There may be an off-by-one (or more) error here.
+ */
+ ytmp += h; /* top left corner of rectangle to be filled */
+ w *= xscale;
+ h *= yscale;
+ XFillRectangle(dpy, plot->pixmap, gc, X(xtmp), Y(ytmp), w + 1, h + 1);
+ /* reset everything */
+ XSetForeground(dpy, gc, plot->cmap->colors[plot->lt + 3]);
+ XSetFillStyle(dpy, gc, FillSolid);
+ }
+ }
+ /* X11_justify_text(mode) - set text justification mode */
+ else if (*buffer == 'J')
+ sscanf(buffer, "J%4d", (int *) &plot->jmode);
+
+ else if (*buffer == 'A')
+ sscanf(buffer + 1, "%lf", &plot->angle);
+
+ /* X11_linewidth(plot->lwidth) - set line width */
+ else if (*buffer == 'W')
+ sscanf(buffer + 1, "%4d", &plot->user_width);
+
+ /* X11_linetype(plot->type) - set line type */
+ else if (*buffer == 'L') {
+ sscanf(buffer, "L%4d", &plot->lt);
+ plot->lt = (plot->lt % 8) + 2;
+
+ if (plot->lt < 0) /* LT_NODRAW, LT_BACKGROUND, LT_UNDEFINED */
+ plot->lt = -3;
+
+ else { /* Fixme: no mechanism to hold width or dashstyle for LT_BACKGROUND */
+ /* default width is 0 {which X treats as 1} */
+ plot->lwidth = widths[plot->lt] ? plot->user_width * widths[plot->lt] : plot->user_width;
+
+ if ((dashedlines && dashes[plot->lt][0])
+ || (plot->lt == LT_AXIS+2 && dashes[LT_AXIS+2][0])) {
+ plot->type = LineOnOffDash;
+ XSetDashes(dpy, gc, 0, dashes[plot->lt], strlen(dashes[plot->lt]));
+ } else {
+ plot->type = LineSolid;
+ }
+ }
+
+ XSetForeground(dpy, gc, plot->cmap->colors[plot->lt + 3]);
+ XSetLineAttributes(dpy, gc, plot->lwidth, plot->type, CapButt, JoinBevel);
+ plot->current_rgb = plot->cmap->rgbcolors[plot->lt + 3];
+ current_gc = &gc;
+ }
+ /* X11_point(number) - draw a point */
+ else if (*buffer == 'P') {
+ int point;
+ sscanf(buffer + 1, "%d %d %d", &point, &x, &y);
+ if (point == -2) {
+ /* set point size */
+ plot->px = (int) (x * xscale * pointsize);
+ plot->py = (int) (y * yscale * pointsize);
+ } else if (point == -1) {
+ /* dot */
+ XDrawPoint(dpy, plot->pixmap, *current_gc, X(x), Y(y));
+ } else {
+ unsigned char fill = 0;
+ unsigned char upside_down_fill = 0;
+ short upside_down_sign = 1;
+ int delta = (plot->px + plot->py + 1)/2;
+
+ /* Force line type to solid, with round ends */
+ XSetLineAttributes(dpy, *current_gc, plot->lwidth, LineSolid, CapRound, JoinRound);
+
+ switch (point % 13) {
+ case 0: /* do plus */
+ Plus[0].x1 = (short) X(x) - delta;
+ Plus[0].y1 = (short) Y(y);
+ Plus[0].x2 = (short) X(x) + delta;
+ Plus[0].y2 = (short) Y(y);
+ Plus[1].x1 = (short) X(x);
+ Plus[1].y1 = (short) Y(y) - delta;
+ Plus[1].x2 = (short) X(x);
+ Plus[1].y2 = (short) Y(y) + delta;
+
+ XDrawSegments(dpy, plot->pixmap, *current_gc, Plus, 2);
+ break;
+ case 1: /* do X */
+ Cross[0].x1 = (short) X(x) - delta;
+ Cross[0].y1 = (short) Y(y) - delta;
+ Cross[0].x2 = (short) X(x) + delta;
+ Cross[0].y2 = (short) Y(y) + delta;
+ Cross[1].x1 = (short) X(x) - delta;
+ Cross[1].y1 = (short) Y(y) + delta;
+ Cross[1].x2 = (short) X(x) + delta;
+ Cross[1].y2 = (short) Y(y) - delta;
+
+ XDrawSegments(dpy, plot->pixmap, *current_gc, Cross, 2);
+ break;
+ case 2: /* do star */
+ Star[0].x1 = (short) X(x) - delta;
+ Star[0].y1 = (short) Y(y);
+ Star[0].x2 = (short) X(x) + delta;
+ Star[0].y2 = (short) Y(y);
+ Star[1].x1 = (short) X(x);
+ Star[1].y1 = (short) Y(y) - delta;
+ Star[1].x2 = (short) X(x);
+ Star[1].y2 = (short) Y(y) + delta;
+ Star[2].x1 = (short) X(x) - delta;
+ Star[2].y1 = (short) Y(y) - delta;
+ Star[2].x2 = (short) X(x) + delta;
+ Star[2].y2 = (short) Y(y) + delta;
+ Star[3].x1 = (short) X(x) - delta;
+ Star[3].y1 = (short) Y(y) + delta;
+ Star[3].x2 = (short) X(x) + delta;
+ Star[3].y2 = (short) Y(y) - delta;
+
+ XDrawSegments(dpy, plot->pixmap, *current_gc, Star, 4);
+ break;
+ case 3: /* do box */
+ XDrawRectangle(dpy, plot->pixmap, *current_gc, X(x) - delta, Y(y) - delta,
+ (delta + delta), (delta + delta));
+ XDrawPoint(dpy, plot->pixmap, *current_gc, X(x), Y(y));
+ break;
+ case 4: /* filled box */
+ XFillRectangle(dpy, plot->pixmap, *current_gc, X(x) - delta, Y(y) - delta,
+ (delta + delta), (delta + delta));
+ break;
+ case 5: /* circle */
+ XDrawArc(dpy, plot->pixmap, *current_gc, X(x) - delta, Y(y) - delta,
+ 2 * delta, 2 * delta, 0, 23040 /* 360 * 64 */);
+ XDrawPoint(dpy, plot->pixmap, *current_gc, X(x), Y(y));
+ break;
+ case 6: /* filled circle */
+ XFillArc(dpy, plot->pixmap, *current_gc, X(x) - delta, Y(y) - delta,
+ 2 * delta, 2 * delta, 0, 23040 /* 360 * 64 */);
+ break;
+ case 10: /* filled upside-down triangle */
+ upside_down_fill = 1;
+ /* FALLTHRU */
+ case 9: /* do upside-down triangle */
+ upside_down_sign = (short)-1;
+ case 8: /* filled triangle */
+ fill = 1;
+ /* FALLTHRU */
+ case 7: /* do triangle */
+ {
+ short temp_x, temp_y;
+
+ temp_x = (short) (1.33 * (double) delta + 0.5);
+ temp_y = (short) (1.33 * (double) delta + 0.5);
+
+ Triangle[0].x = (short) X(x);
+ Triangle[0].y = (short) Y(y) - upside_down_sign * temp_y;
+ Triangle[1].x = (short) temp_x;
+ Triangle[1].y = (short) upside_down_sign * 2 * delta;
+ Triangle[2].x = (short) -(2 * temp_x);
+ Triangle[2].y = (short) 0;
+ Triangle[3].x = (short) temp_x;
+ Triangle[3].y = (short) -(upside_down_sign * 2 * delta);
+
+ if ((upside_down_sign == 1 && fill) || upside_down_fill) {
+ XFillPolygon(dpy, plot->pixmap, *current_gc,
+ Triangle, 4, Convex, CoordModePrevious);
+ } else {
+ XDrawLines(dpy, plot->pixmap, *current_gc, Triangle, 4, CoordModePrevious);
+ XDrawPoint(dpy, plot->pixmap, *current_gc, X(x), Y(y));
+ }
+ }
+ break;
+ case 12: /* filled diamond */
+ fill = 1;
+ /* FALLTHRU */
+ case 11: /* do diamond */
+ Diamond[0].x = (short) X(x) - delta;
+ Diamond[0].y = (short) Y(y);
+ Diamond[1].x = (short) delta;
+ Diamond[1].y = (short) -delta;
+ Diamond[2].x = (short) delta;
+ Diamond[2].y = (short) delta;
+ Diamond[3].x = (short) -delta;
+ Diamond[3].y = (short) delta;
+ Diamond[4].x = (short) -delta;
+ Diamond[4].y = (short) -delta;
+
+ /*
+ * Should really do a check with XMaxRequestSize()
+ */
+
+ if (fill) {
+ XFillPolygon(dpy, plot->pixmap, *current_gc,
+ Diamond, 5, Convex, CoordModePrevious);
+ } else {
+ XDrawLines(dpy, plot->pixmap, *current_gc, Diamond, 5, CoordModePrevious);
+ XDrawPoint(dpy, plot->pixmap, *current_gc, X(x), Y(y));
+ }
+ break;
+ }
+
+ /* Restore original line style */
+ XSetLineAttributes(dpy, *current_gc, plot->lwidth, plot->type, CapButt, JoinBevel);
+ }
+ }
+ else if (*buffer == X11_GR_SET_LINECOLOR) {
+ int lt;
+ sscanf(buffer + 1, "%4d", <);
+ lt = (lt % 8) + 2;
+ if (lt < 0) /* LT_NODRAW, LT_BACKGROUND, LT_UNDEFINED */
+ lt = -3;
+ XSetForeground(dpy, gc, plot->cmap->colors[lt + 3]);
+ plot->current_rgb = plot->cmap->rgbcolors[lt + 3];
+ current_gc = &gc;
+ } else if (*buffer == X11_GR_SET_RGBCOLOR) {
+ int rgb255color;
+ XColor xcolor;
+ sscanf(buffer + 1, "%x", &rgb255color);
+ xcolor.red = (double)(0xffff) * (double)((rgb255color >> 16) & 0xff) /255.;
+ xcolor.green = (double)(0xffff) * (double)((rgb255color >> 8) & 0xff) /255.;
+ xcolor.blue = (double)(0xffff) * (double)(rgb255color & 0xff) /255.;
+ FPRINTF((stderr, "gplt_x11: got request for color %d %d %d\n",
+ xcolor.red, xcolor.green, xcolor.blue));
+ if (XAllocColor(dpy, plot->cmap->colormap, &xcolor)) {
+ XSetForeground(dpy, gc, xcolor.pixel);
+ plot->current_rgb = rgb255color;
+ } else {
+ FPRINTF((stderr, " failed to allocate color\n"));
+ }
+ current_gc = &gc;
+ } else if (*buffer == X11_GR_SET_COLOR) { /* set color */
+ if (have_pm3d) { /* ignore, if your X server is not supported */
+#ifndef BINARY_X11_POLYGON
+ double gray;
+ sscanf(buffer + 1, "%lf", &gray);
+ PaletteSetColor(plot, gray);
+ current_gc = &gc;
+#else
+ /* This command will fit within a single buffer so it doesn't
+ * need to be so elaborate.
+ */
+ unsigned char *iptr;
+ float gray;
+ unsigned i_remaining;
+ char *bptr;
+ TBOOLEAN code_detected = 0;
+
+ iptr = (unsigned char *) &gray;
+ i_remaining = sizeof(gray);
+
+ /* Decode and reconstruct the data. */
+ for (bptr = buffer + 1; i_remaining; ) {
+ unsigned char uctmp = *bptr++;
+ if (code_detected) {
+ code_detected = 0;
+ *iptr++ = uctmp - 1 + SET_COLOR_TRANSLATION_CHAR;
+ i_remaining--;
+ }
+ else {
+ if ( uctmp == SET_COLOR_CODE_CHAR ) {
+ code_detected = 1;
+ }
+ else {
+ *iptr++ = uctmp + SET_COLOR_TRANSLATION_CHAR;
+ i_remaining--;
+ }
+ }
+ }
+
+ if (swap_endian) {
+ byteswap((char *)&gray, sizeof(gray));
+ }
+
+ PaletteSetColor(plot, (double)gray);
+ current_gc = &gc;
+#endif
+ }
+ } else if (*buffer == X11_GR_FILLED_POLYGON) { /* filled polygon */
+ if (have_pm3d) { /* ignore, if your X server is not supported */
+
+#ifndef BINARY_X11_POLYGON
+
+ static XPoint *points = NULL;
+ static int st_npoints = 0;
+ static int saved_npoints = -1, saved_i = -1; /* HBB 20010919 */
+ int i, npoints, style;
+ char *ptr = buffer + 1;
+
+ sscanf(ptr, "%4d", &npoints);
+
+ if (npoints > 0) {
+ ptr += 4;
+ sscanf(ptr, "%4d", &style);
+ }
+
+ /* HBB 20010919: Implement buffer overflow protection by
+ * breaking up long lines */
+ if (npoints == -1) {
+ /* This is a continuation line. */
+ if (saved_npoints < 100) {
+ fprintf(stderr, "gnuplot_x11: filled_polygon() protocol error\n");
+ EXIT(1);
+ }
+ /* Continue filling at end of previous list: */
+ i = saved_i;
+ npoints = saved_npoints;
+ } else {
+ saved_npoints = npoints;
+ i = 0;
+ }
+
+ ptr += 4;
+ if (npoints > st_npoints) {
+ XPoint *new_points = realloc(points, sizeof(XPoint) * npoints);
+ st_npoints = npoints;
+ if (!new_points) {
+ perror("gnuplot_x11: exec_cmd()->points");
+ EXIT(1);
+ }
+ points = new_points;
+ }
+
+ while (*ptr != 'x' && i < npoints) { /* not end-of-line marker */
+ sscanf(ptr, "%4d%4d", &x, &y);
+ ptr += 8;
+ points[i].x = X(x);
+ points[i].y = Y(y);
+ i++;
+ }
+
+ if (i >= npoints) {
+ /* only do the call if list is complete by now */
+ int fillpar, idx;
+ XColor xcolor, bgnd;
+ float dim;
+
+#else /* BINARY_X11_POLYGON */
+
+ static TBOOLEAN transferring = 0;
+ static unsigned char *iptr;
+ static int int_cache[2];
+#define style int_cache[1]
+#define npoints int_cache[0]
+ static unsigned i_remaining;
+ unsigned short i_buffer;
+ char *bptr;
+ static TBOOLEAN code_detected = 0;
+ static XPoint *points = NULL;
+ static int st_npoints = 0;
+
+ /* The first value read will be the number of points or the number of
+ * points followed by style. Set up parameters to point to npoints.
+ */
+ if (!transferring) {
+ iptr = (unsigned char *) &npoints;
+ i_remaining = sizeof(int_cache);
+ }
+
+ i_buffer = BINARY_MAX_CHAR_PER_TRANSFER;
+
+ /* Decode and reconstruct the data. */
+ for (bptr = &buffer[1]; i_buffer && i_remaining; i_buffer--) {
+
+ unsigned char uctmp = *bptr++;
+
+ if (code_detected) {
+ code_detected = 0;
+ *iptr++ = uctmp - 1 + FILLED_POLYGON_TRANSLATION_CHAR;
+ i_remaining--;
+ } else {
+ if ( uctmp == FILLED_POLYGON_CODE_CHAR ) {
+ code_detected = 1;
+ } else {
+ *iptr++ = uctmp + FILLED_POLYGON_TRANSLATION_CHAR;
+ i_remaining--;
+ }
+ }
+
+ if(!i_remaining && !transferring) {
+ /* The number of points was just read. Now set up points array and continue. */
+ if (swap_endian) {
+ byteswap((char *)&npoints, sizeof(npoints));
+ byteswap((char *)&style, sizeof(style));
+ }
+ if (npoints > st_npoints) {
+ XPoint *new_points = realloc(points, npoints*2*sizeof(int));
+ st_npoints = npoints;
+ if (!new_points) {
+ perror("gnuplot_x11: exec_cmd()->points");
+ EXIT(1);
+ }
+ points = new_points;
+ }
+ i_remaining = npoints*2*sizeof(int);
+ iptr = (unsigned char *) points;
+ transferring = 1;
+ }
+
+ }
+
+ if (!i_remaining) {
+
+ int i;
+ transferring = 0;
+
+ /* If the byte order needs to be swapped, do so. */
+ if (swap_endian) {
+ i = 2*npoints;
+ for (i--; i >= 0; i--) {
+ byteswap((char *)&((int *)points)[i], sizeof(int));
+ }
+ }
+
+ /* Convert the ints to XPoint format. This looks like it tramples
+ * on itself, but the XPoint x and y are smaller than an int.
+ */
+ for (i=0; i < npoints; i++) {
+ points[i].x = X( ((int *)points)[2*i] );
+ points[i].y = Y( ((int *)points)[2*i+1] );
+ }
+
+#endif /* BINARY_X11_POLYGON */
+
+ /* Load selected pattern or fill into a separate gc */
+ if (!fill_gc)
+ fill_gc = XCreateGC(dpy, plot->window, 0, 0);
+ XCopyGC(dpy, *current_gc, ~0, fill_gc);
+
+ x11_setfill(&fill_gc, style, TRUE);
+
+ XFillPolygon(dpy, plot->pixmap, fill_gc, points, npoints,
+ Nonconvex, CoordModeOrigin);
+
+#ifndef BINARY_X11_POLYGON
+
+ /* Flag this continuation line as closed */
+ saved_npoints = saved_i = -1;
+ } else {
+ /* Store how far we got: */
+ saved_i = i;
+ }
+
+#else /* BINARY_X11_POLYGON */
+
+ }
+#undef style
+#undef npoints
+
+#endif /* BINARY_X11_POLYGON */
+
+ }
+
+ }
+#ifdef WITH_IMAGE
+ else if (*buffer == X11_GR_IMAGE) { /* image */
+
+ static unsigned char *iptr;
+ static TBOOLEAN transferring = 0;
+ static unsigned short *image;
+ static int M, N;
+ static int pixel_1_1_x, pixel_1_1_y, pixel_M_N_x, pixel_M_N_y;
+ static int visual_1_1_x, visual_1_1_y, visual_M_N_x, visual_M_N_y;
+ static int color_mode;
+ static unsigned i_remaining;
+
+ /* ignore, if your X server is not supported */
+ if (!have_pm3d)
+ return;
+
+/* These might work better as fuctions, but defines will do for now. */
+#define ERROR_NOTICE(str) "\nGNUPLOT (gplt_x11): " str
+#define ERROR_NOTICE_NEWLINE(str) "\n " str
+
+ if (!transferring) {
+
+ /* Get variables. */
+ if (11 != sscanf( &buffer[1], "%x %x %x %x %x %x %x %x %x %x %x", &M, &N, &pixel_1_1_x,
+ &pixel_1_1_y, &pixel_M_N_x, &pixel_M_N_y, &visual_1_1_x, &visual_1_1_y,
+ &visual_M_N_x, &visual_M_N_y, &color_mode)) {
+ fprintf(stderr, ERROR_NOTICE("Couldn't read image parameters correctly.\n\n"));
+ } else {
+
+ /* Number of symbols depends upon whether it is color or palette lookup. */
+ i_remaining = M*N*sizeof(image[0]);
+ if (color_mode == IC_RGB) i_remaining *= 3;
+
+ if (!i_remaining) {
+ fprintf(stderr, ERROR_NOTICE("Image of size zero.\n\n"));
+ } else {
+ image = (unsigned short *) malloc(i_remaining);
+ if (image) {
+ iptr = (unsigned char *) image;
+ transferring = 1;
+ } else {
+ fprintf(stderr, ERROR_NOTICE("Cannot allocate memory for image.\n\n"));
+ }
+ }
+ }
+
+ } else {
+
+ unsigned short i_buffer;
+ char *bptr;
+ static TBOOLEAN code_detected = 0;
+
+ i_buffer = BINARY_MAX_CHAR_PER_TRANSFER;
+
+ /* Decode and reconstruct the data. */
+ for (bptr = &buffer[1]; i_buffer && i_remaining; i_buffer--) {
+ unsigned char uctmp = *bptr++;
+ if (code_detected) {
+ code_detected = 0;
+ *iptr++ = uctmp - 1 + IMAGE_TRANSLATION_CHAR;
+ i_remaining--;
+ } else {
+ if (uctmp == IMAGE_CODE_CHAR) {
+ code_detected = 1;
+ } else {
+ *iptr++ = uctmp + IMAGE_TRANSLATION_CHAR;
+ i_remaining--;
+ }
+ }
+ }
+
+ if (!i_remaining) {
+
+ /* Expand or contract the image into a new image and place this on the screen. */
+
+ static unsigned short R_msb_mask=0, R_rshift, R_lshift;
+ static unsigned short G_msb_mask, G_rshift, G_lshift;
+ static unsigned short B_msb_mask, B_rshift, B_lshift;
+ static unsigned long prev_red_mask, prev_green_mask, prev_blue_mask;
+#define LET_XPUTPIXEL_SWAP_BYTES 1
+#if !LET_XPUTPIXEL_SWAP_BYTES
+ static TBOOLEAN swap_image_bytes = FALSE;
+#endif
+ TBOOLEAN create_image = FALSE;
+#define SINGLE_PALETTE_BIT_SHIFT 0 /* Discard over time, 16aug2004 */
+#if SINGLE_PALETTE_BIT_SHIFT
+ static int prev_allocated=0;
+ static short palette_bit_shift=0;
+#endif
+
+ transferring = 0;
+
+ /* If the byte order needs to be swapped, do so. */
+ if (swap_endian) {
+ int i = M*N;
+ if (color_mode == IC_RGB) {i *= 3;}
+ for (i--; i >= 0; i--) {
+ /* The assumption is that image data through the pipe is 16 bits. */
+ byteswap2(&image[i]);
+ }
+ }
+
+ if (color_mode == IC_PALETTE) {
+
+#if SINGLE_PALETTE_BIT_SHIFT
+/* If the palette size is 2 to some power then a single bit shift would work.
+ * The multiply/shift method doesn't seem noticably slower and generally works
+ * with any size pallete. Keep this around for the time being in case some
+ * use for it comes up.
+ */
+ /* Initialize the palette shift, or if the palette size changes then update the shift. */
+ if (!palette_bit_shift || (plot->cmap->allocated != prev_allocated)) {
+ short i_bits, n_colors;
+ prev_allocated = plot->cmap->allocated;
+ for (palette_bit_shift = 16, n_colors = 1; palette_bit_shift > 0; palette_bit_shift--) {
+ if (n_colors >= plot->cmap->allocated) {break;}
+ n_colors <<= 1;
+ }
+ }
+#endif
+
+ create_image = TRUE;
+ }
+ else {
+
+ switch( vis->class ) {
+
+ static char *display_error_text_after = " display class cannot use component"
+ ERROR_NOTICE_NEWLINE("data. Try palette mode.\n\n");
+
+ case TrueColor:
+
+ /* This algorithm for construction of pixel values from the RGB components
+ * doesn't look to be the most portable. However, it tries to be. It
+ * gets the red/green/blue masks from the X11 information and generates
+ * appropriate shifts and masks from that. The X11 documentation mentions
+ * something about using the masks in some odd way to generate an index
+ * for a color table. It all seemed like a bother for no benefit however.
+ * Certainly we don't want to have only a few bits of color, and we don't
+ * want to have some huge color table. My feeling on the matter is to
+ * just pack bits according to the masks that X11 indicates. If someone
+ * finds they have a peculiar display type that doesn't work, that can be
+ * considered when and if it happens.
+ */
+
+ /* Get the color mask information and compute the proper bit shifts, but only upon
+ * first call or if the masks change. I'm not sure if it is necessary to check if
+ * the masks changed, but I left it in as a safeguard in case there is some strange
+ * system out there somewhere.
+ */
+ if (!R_msb_mask || (vis->red_mask != prev_red_mask) ||
+ (vis->green_mask != prev_green_mask) || (vis->blue_mask != prev_blue_mask)) {
+ short R_bits, G_bits, B_bits, min_bits;
+ prev_red_mask = vis->red_mask;
+ prev_green_mask = vis->green_mask;
+ prev_blue_mask = vis->blue_mask;
+ R_msb_mask = BitMaskDetails(vis->red_mask, &R_rshift, &R_lshift);
+ G_msb_mask = BitMaskDetails(vis->green_mask, &G_rshift, &G_lshift);
+ B_msb_mask = BitMaskDetails(vis->blue_mask, &B_rshift, &B_lshift);
+
+ /* Graphics info in case strange behavior occurs in a particular system.
+ */
+ FPRINTF((stderr, "\n\nvis->visualid: 0x%x vis->class: %d vis->bits_per_rgb: %d\n",
+ (int)vis->visualid, vis->class, vis->bits_per_rgb));
+ FPRINTF((stderr, "vis->red_mask: %lx vis->green_mask: %lx vis->blue_mask: %lx\n",
+ vis->red_mask, vis->green_mask, vis->blue_mask));
+ FPRINTF((stderr, "ImageByteOrder: %d\n\n", ImageByteOrder(dpy)));
+
+ R_bits = 32-R_rshift-R_lshift;
+ G_bits = 32-G_rshift-G_lshift;
+ B_bits = 32-B_rshift-B_lshift;
+ min_bits = GPMIN(GPMIN(R_bits, G_bits), B_bits);
+ if (R_bits > min_bits) {
+ R_msb_mask >>= (R_bits-min_bits);
+ R_lshift += (R_bits-min_bits);
+ R_bits = min_bits;
+ }
+ if (G_bits > min_bits) {
+ G_msb_mask >>= (G_bits-min_bits);
+ G_lshift += (G_bits-min_bits);
+ G_bits = min_bits;
+ }
+ if (B_bits > min_bits) {
+ B_msb_mask >>= (B_bits-min_bits);
+ B_lshift += (B_bits-min_bits);
+ B_bits = min_bits;
+ }
+ R_rshift = 16-R_bits;
+ G_rshift = 16-G_bits;
+ B_rshift = 16-B_bits;
+
+#if !LET_XPUTPIXEL_SWAP_BYTES
+ /* Now deal with the byte order issue. */
+ if (ImageByteOrder(dpy)) {
+ /* If the bit field for each channel is 8 bits, the masks can be rearranged instead
+ * of having to actually perform a byte order swap on the image data.
+ */
+ if ((R_bits == 8) && (G_bits == 8) && (B_bits == 8)) {
+ R_lshift = 24 - R_lshift;
+ G_lshift = 24 - G_lshift;
+ B_lshift = 24 - B_lshift;
+ swap_image_bytes = FALSE;
+ } else {
+ swap_image_bytes = TRUE;
+ }
+ } else {
+ swap_image_bytes = FALSE;
+ }
+#endif
+ }
+
+ create_image = TRUE;
+ break;
+
+ case PseudoColor:
+ fprintf(stderr, ERROR_NOTICE("PseudoColor"));
+ fprintf(stderr, display_error_text_after);
+ break;
+
+ case GrayScale:
+ fprintf(stderr, ERROR_NOTICE("GrayScale"));
+ fprintf(stderr, display_error_text_after);
+ break;
+
+ case StaticColor:
+ fprintf(stderr, ERROR_NOTICE("StaticColor"));
+ fprintf(stderr, display_error_text_after);
+ break;
+
+ case StaticGray:
+ fprintf(stderr, ERROR_NOTICE("StaticGray"));
+ fprintf(stderr, display_error_text_after);
+ break;
+
+ case DirectColor:
+ fprintf(stderr, ERROR_NOTICE("DirectColor display class currently")
+ ERROR_NOTICE_NEWLINE("not supported.\n\n"));
+ break;
+
+ default:
+ fprintf(stderr, ERROR_NOTICE("Unknown X11 display class.\n\n"));
+ break;
+
+ }
+
+ }
+
+ if (create_image) {
+
+ int M_pixel, N_pixel;
+ int M_view, N_view;
+ int pixel_1_1_x_plot, pixel_1_1_y_plot, pixel_M_N_x_plot, pixel_M_N_y_plot;
+ int view_1_1_x_plot, view_1_1_y_plot, view_M_N_x_plot, view_M_N_y_plot;
+ int final_1_1_x_plot, final_1_1_y_plot;
+ int i_start, j_start;
+ int itmp;
+
+ /* Compute the image extent with sanity check. */
+ pixel_1_1_x_plot = X(pixel_1_1_x);
+ pixel_M_N_x_plot = X(pixel_M_N_x);
+ M_pixel = pixel_M_N_x_plot - pixel_1_1_x_plot;
+ if (M_pixel < 0) {
+ M_pixel = -M_pixel;
+ itmp = pixel_1_1_x_plot;
+ pixel_1_1_x_plot = pixel_M_N_x_plot;
+ pixel_M_N_x_plot = itmp;
+ }
+ pixel_1_1_y_plot = Y(pixel_1_1_y);
+ pixel_M_N_y_plot = Y(pixel_M_N_y);
+ N_pixel = pixel_M_N_y_plot - pixel_1_1_y_plot;
+ if (N_pixel < 0) {
+ N_pixel = -N_pixel;
+ itmp = pixel_1_1_y_plot;
+ pixel_1_1_y_plot = pixel_M_N_y_plot;
+ pixel_M_N_y_plot = itmp;
+ }
+
+ /* Compute the visual extent of the plot with sanity check. */
+ view_1_1_x_plot = X(visual_1_1_x);
+ view_M_N_x_plot = X(visual_M_N_x);
+ if (view_M_N_x_plot < view_1_1_x_plot) {
+ itmp = view_1_1_x_plot;
+ view_1_1_x_plot = view_M_N_x_plot;
+ view_M_N_x_plot = itmp;
+ }
+ view_1_1_y_plot = Y(visual_1_1_y);
+ view_M_N_y_plot = Y(visual_M_N_y);
+ if (view_M_N_y_plot < view_1_1_y_plot) {
+ itmp = view_1_1_y_plot;
+ view_1_1_y_plot = view_M_N_y_plot;
+ view_M_N_y_plot = itmp;
+ }
+
+ /* Determine parameters for the image that will be built and put on screen. */
+ itmp = view_1_1_x_plot - pixel_1_1_x_plot;
+ if (itmp > 0) {
+ i_start = itmp; final_1_1_x_plot = view_1_1_x_plot;
+ } else {
+ i_start = 0; final_1_1_x_plot = pixel_1_1_x_plot;
+ }
+ itmp = pixel_M_N_x_plot - view_M_N_x_plot;
+ if (itmp > 0) {
+ M_view = M_pixel - itmp - i_start;
+ } else {
+ M_view = M_pixel - i_start;
+ }
+
+ itmp = view_1_1_y_plot - pixel_1_1_y_plot;
+ if (itmp > 0) {
+ j_start = itmp; final_1_1_y_plot = view_1_1_y_plot;
+ } else {
+ j_start = 0; final_1_1_y_plot = pixel_1_1_y_plot;
+ }
+ itmp = pixel_M_N_y_plot - view_M_N_y_plot;
+ if (itmp > 0) {
+ N_view = N_pixel - itmp - j_start;
+ } else {
+ N_view = N_pixel - j_start;
+ }
+
+ if ((M_view > 0) && (N_view > 0)) {
+
+ char *sample_data;
+ short sample_data_size;
+ int i_view, j_view;
+
+ /* Determine if 2 bytes is sufficient or 4 bytes are necessary for color image data. */
+ if (dep > 16) {
+ sample_data_size = 4;
+ } else {
+ sample_data_size = 2;
+ }
+
+ /* Expand or compress the original image to the pixels it will occupy on the screen. */
+ sample_data = (char *) malloc(M_view*N_view*sample_data_size);
+
+ if (sample_data) {
+
+ XImage *image_dest;
+
+ /* Create an initialized image object. */
+ image_dest = XCreateImage(dpy, vis, dep, ZPixmap, 0, sample_data, M_view, N_view,
+ 8*sample_data_size, M_view*sample_data_size);
+
+ /* Fill in the output image data by decimating or repeating the input image data. */
+ for (j_view=0; j_view < N_view; j_view++) {
+ int j = ((j_view+j_start) * N) / N_pixel;
+ int row_start = j*M;
+ for (i_view=0; i_view < M_view; i_view++) {
+ int i = ((i_view+i_start) * M) / M_pixel;
+ if (color_mode == IC_PALETTE) {
+
+#if SINGLE_PALETTE_BIT_SHIFT
+/* If the palette size is 2 to some power then a single bit shift would work.
+ * The multiply/shift method doesn't seem noticably slower and generally works
+ * with any size pallete. Keep this around for the time being in case some
+ * use for it comes up.
+ */
+ unsigned long pixel = plot->cmap->pixels[image[row_start + i]>>palette_bit_shift];
+#else
+ /* The methods below avoid using floating point. The basic idea is to multiply
+ * the palette size by the "normalized" data coming across the pipe. (I think
+ * some DSP people call this Q1 or Q0 format, or something like that.) Following
+ * this multiplication by division by 2^16 gives the final answer. Most moderately
+ * advanced CPUs have bit shifting. The first method uses this. However, a bit
+ * shift isn't necessary because we can just take the top word of a two word
+ * number and we've effectively divided by 2^16. The second method uses this.
+ * However, my guess is that C compilers more effeciently translate the first
+ * method with the bit shift than the second method which pulls out the top word.
+ * (There is one instruction less for the shift version than the word selection
+ * version on gcc for x86.)
+ */
+#if 1
+ unsigned long pixel;
+ unsigned short index = (plot->cmap->allocated * (unsigned long) image[row_start + i]) >> 16;
+ pixel = plot->cmap->pixels[index];
+#else
+ unsigned long index;
+ unsigned long pixel;
+ index = plot->cmap->allocated * (unsigned long) image[row_start + i];
+ pixel = plot->cmap->pixels[((unsigned short *)&index)[1]];
+#endif
+#endif
+ XPutPixel(image_dest, i_view, j_view, pixel);
+ } else {
+ int index3 = 3*(row_start + i);
+ unsigned long pixel = ((unsigned int)((image[index3++]>>R_rshift)&R_msb_mask)) << R_lshift;
+ pixel |= ((unsigned int)((image[index3++]>>G_rshift)&G_msb_mask)) << G_lshift;
+ pixel |= ((unsigned int)((image[index3]>>B_rshift)&B_msb_mask)) << B_lshift;
+ XPutPixel(image_dest, i_view, j_view, pixel);
+ }
+ }
+ }
+
+#if !LET_XPUTPIXEL_SWAP_BYTES
+ /* Swap the image byte order if necessary. */
+ if (swap_image_bytes) {
+ int i_swap = M_view*N_view - 1;
+ if (sample_data_size == 2) {
+ for (; i_swap >= 0; i_swap--) {
+ byteswap2(&(((unsigned short *)sample_data)[i_swap]));
+ }
+ } else {
+ for (; i_swap >= 0; i_swap--) {
+ byteswap4(&(((unsigned int *)sample_data)[i_swap]));
+ }
+ }
+ }
+#endif
+
+ /* Copy the image to the drawable d. */
+ XPutImage(dpy, plot->pixmap, gc, image_dest, 0, 0,
+ final_1_1_x_plot, final_1_1_y_plot, M_view, N_view);
+
+ /* Free resources. */
+ XDestroyImage(image_dest);
+
+ /* XDestroyImage frees the sample_data memory, so no "free" here. */
+
+ } else {
+ fprintf(stderr, ERROR_NOTICE("Could not allocate memory for image.\n\n"));
+ }
+ }
+
+ }
+
+ /* image was not used as part of X resource, so must "free" here. */
+ free(image);
+
+ }
+ }
+
+ }
+#endif /* WITH_IMAGE */
+#if defined(USE_MOUSE) && defined(MOUSE_ALL_WINDOWS)
+ /* Axis scaling information to save for later mouse clicks */
+ else if (*buffer == 'S') {
+ int axis, axis_mask;
+
+ sscanf(&buffer[1], "%d %d", &axis, &axis_mask);
+ if (axis == -2) {
+ plot->almost2d = axis_mask;
+ } else if (axis < 0) {
+ plot->axis_mask = axis_mask;
+ } else if (axis < 2*SECOND_AXES) {
+ sscanf(&buffer[1], "%d %lg %d %lg %lg", &axis,
+ &(plot->axis_scale[axis].min), &(plot->axis_scale[axis].term_lower),
+ &(plot->axis_scale[axis].term_scale), &(plot->axis_scale[axis].logbase));
+ FPRINTF((stderr, "gnuplot_x11: axis %d scaling %14.3lg %14d %14.3lg %14.3lg\n",
+ axis, plot->axis_scale[axis].min, plot->axis_scale[axis].term_lower,
+ plot->axis_scale[axis].term_scale, plot->axis_scale[axis].logbase));
+ }
+ }
+#endif
+ else {
+ fprintf(stderr, "gnuplot_x11: unknown command <%s>\n", buffer);
+ }
+}
+
+/*
+ * display - display a stored plot
+ */
+static void
+display(plot_struct *plot)
+{
+ int n;
+
+ FPRINTF((stderr, "Display %d ; %d commands\n", plot->plot_number, plot->ncommands));
+
+ if (plot->ncommands == 0)
+ return;
+
+ /* set scaling factor between internal driver & window geometry */
+ xscale = plot->width / 4096.0;
+ yscale = GRAPH_HEIGHT(plot) / 4096.0;
+
+ /* initial point sizes, until overridden with P7xxxxyyyy */
+ plot->px = (int) (xscale * pointsize);
+ plot->py = (int) (yscale * pointsize);
+
+ /* create new pixmap & GC */
+ if (!plot->pixmap) {
+ FPRINTF((stderr, "Create pixmap %d : %dx%dx%d\n", plot->plot_number, plot->width, PIXMAP_HEIGHT(plot), dep));
+ plot->pixmap = XCreatePixmap(dpy, root, plot->width, PIXMAP_HEIGHT(plot), dep);
+ }
+
+ if (gc)
+ XFreeGC(dpy, gc);
+
+ gc = XCreateGC(dpy, plot->pixmap, 0, (XGCValues *) 0);
+
+ if (font)
+ gpXSetFont(dpy, gc, font->fid);
+
+ XSetFillStyle(dpy, gc, FillSolid);
+
+ /* initialize stipple for filled boxes (ULIG) */
+ if (!stipple_initialized) {
+ int i;
+ for (i = 0; i < stipple_pattern_num; i++)
+ stipple_pattern[i] =
+ XCreateBitmapFromData(dpy, plot->pixmap, stipple_pattern_bits[i],
+ stipple_pattern_width, stipple_pattern_height);
+ stipple_initialized = 1;
+ }
+
+ /* set pixmap background */
+ XSetForeground(dpy, gc, plot->cmap->colors[0]);
+ XFillRectangle(dpy, plot->pixmap, gc, 0, 0, plot->width, PIXMAP_HEIGHT(plot) + vchar);
+ XSetBackground(dpy, gc, plot->cmap->colors[0]);
+
+ if (!plot->window)
+ pr_window(plot);
+
+ /* top the window but don't put keyboard or mouse focus into it. */
+ if (do_raise)
+ XMapRaised(dpy, plot->window);
+
+ /* momentarily clear the window first if requested */
+ if (Clear) {
+ XClearWindow(dpy, plot->window);
+ XFlush(dpy);
+ }
+ /* loop over accumulated commands from inboard driver */
+ for (n = 0; n < plot->ncommands; n++) {
+ exec_cmd(plot, plot->commands[n]);
+ }
+
+#ifdef EXPORT_SELECTION
+ if (exportselection)
+ export_graph(plot);
+#endif
+
+ UpdateWindow(plot);
+#ifdef USE_MOUSE
+#ifdef TITLE_BAR_DRAWING_MSG
+ if (plot->window) {
+ /* restore default window title */
+ char *cp = plot->titlestring;
+ if (!cp) cp = "";
+ XStoreName(dpy, plot->window, cp);
+ }
+#else
+ if (!button_pressed) {
+ cursor = cursor_save ? cursor_save : cursor_default;
+ cursor_save = (Cursor)0;
+ XDefineCursor(dpy, plot->window, cursor);
+ }
+#endif
+#endif
+}
+
+static void
+UpdateWindow(plot_struct * plot)
+{
+#ifdef USE_MOUSE
+ XEvent event;
+#endif
+ if (None == plot->window) {
+ return;
+ }
+
+ if (!plot->pixmap) {
+ /* create a black background pixmap */
+ FPRINTF((stderr, "Create pixmap %d : %dx%dx%d\n", plot->plot_number, plot->width, PIXMAP_HEIGHT(plot), dep));
+ plot->pixmap = XCreatePixmap(dpy, root, plot->width, PIXMAP_HEIGHT(plot), dep);
+ if (gc)
+ XFreeGC(dpy, gc);
+ gc = XCreateGC(dpy, plot->pixmap, 0, (XGCValues *) 0);
+ if (font)
+ gpXSetFont(dpy, gc, font->fid);
+ /* set pixmap background */
+ XSetForeground(dpy, gc, plot->cmap->colors[0]);
+ XFillRectangle(dpy, plot->pixmap, gc, 0, 0, plot->width, PIXMAP_HEIGHT(plot) + vchar);
+ XSetBackground(dpy, gc, plot->cmap->colors[0]);
+ }
+ XSetWindowBackgroundPixmap(dpy, plot->window, plot->pixmap);
+ XClearWindow(dpy, plot->window);
+
+#ifdef USE_MOUSE
+ EventuallyDrawMouseAddOns(plot);
+
+ XFlush(dpy);
+
+ /* XXX discard expose events. This is a kludge for
+ * preventing the event dispatcher calling UpdateWindow()
+ * and the latter again generating expose events, which
+ * again would trigger the event dispatcher ... (joze) XXX */
+ while (XCheckWindowEvent(dpy, plot->window, ExposureMask, &event))
+ /* EMPTY */ ;
+#endif
+}
+
+
+static void
+CmapClear(cmap_t * cmap_ptr)
+{
+ cmap_ptr->total = (int) 0;
+ cmap_ptr->allocated = (int) 0;
+ cmap_ptr->pixels = (unsigned long *) 0;
+}
+
+static void
+RecolorWindow(plot_struct * plot)
+{
+ if (None != plot->window) {
+ XSetWindowColormap(dpy, plot->window, plot->cmap->colormap);
+ XSetWindowBackground(dpy, plot->window, plot->cmap->colors[0]);
+ XSetWindowBorder(dpy, plot->window, plot->cmap->colors[1]);
+#ifdef USE_MOUSE
+ if (gc_xor) {
+ XFreeGC(dpy, gc_xor);
+ gc_xor = (GC) 0;
+ GetGCXor(plot, &gc_xor); /* recreate gc_xor */
+ }
+#endif
+ }
+}
+
+/*
+ * free all *pm3d* colors (*not* the line colors cmap->colors)
+ * of a plot_struct's colormap. This could be either a private
+ * or the default colormap. Note, that the line colors are not
+ * free'd nor even touched.
+ */
+static void
+FreeColors(cmap_t *cmp)
+{
+ if (cmp->total && cmp->pixels) {
+ if (cmp->allocated) {
+ FPRINTF((stderr, "freeing palette colors\n"));
+ XFreeColors(dpy, cmp->colormap, cmp->pixels,
+ cmp->allocated, 0 /* XXX ??? XXX */ );
+ }
+ free(cmp->pixels);
+ }
+ CmapClear(cmp);
+}
+
+/*
+ * free pm3d colors and eventually a private colormap.
+ * set the plot_struct's colormap to the default colormap
+ * and the line `colors' to the line colors of the default
+ * colormap.
+ */
+static void
+ReleaseColormap(cmap_t *cmp)
+{
+ if (cmp && cmp != current_cmap) {
+ FreeColors(cmp);
+ FPRINTF((stderr, "releasing private colormap\n"));
+ if (cmp->colormap && cmp->colormap != current_cmap->colormap) {
+ XFreeColormap(dpy, cmp->colormap);
+ }
+ free((char *) cmp);
+ }
+}
+
+static unsigned long *
+ReallocColors(cmap_t *cmap, int n)
+{
+ FreeColors(cmap);
+ cmap->total = n;
+ cmap->pixels = (unsigned long *)
+ malloc(sizeof(unsigned long) * cmap->total);
+ return cmap->pixels;
+}
+
+/*
+ * check if the display supports the visual of type `class'.
+ *
+ * If multiple visuals of `class' are supported, try to get
+ * the one with the highest depth.
+ *
+ * If visual class and depth are equal to the default visual
+ * class and depth, the latter is preferred.
+ *
+ * modifies: best, depth
+ *
+ * returns 1 if a visual which matches the request
+ * could be found else 0.
+ */
+static int
+GetVisual(int class, Visual ** visual, int *depth)
+{
+ XVisualInfo *visualsavailable;
+ int nvisuals = 0;
+ long vinfo_mask = VisualClassMask;
+ XVisualInfo vinfo;
+
+ vinfo.class = class;
+ *depth = 0;
+ *visual = 0;
+
+ visualsavailable = XGetVisualInfo(dpy, vinfo_mask, &vinfo, &nvisuals);
+
+ if (visualsavailable && nvisuals > 0) {
+ int i;
+ for (i = 0; i < nvisuals; i++) {
+ if (visualsavailable[i].depth > *depth) {
+ *visual = visualsavailable[i].visual;
+ *depth = visualsavailable[i].depth;
+ }
+ }
+ XFree(visualsavailable);
+ if (*visual && (*visual)->class == (DefaultVisual(dpy, scr))->class && *depth == DefaultDepth(dpy, scr)) {
+ /* prefer the default visual */
+ *visual = DefaultVisual(dpy, scr);
+ }
+ }
+ return nvisuals > 0;
+}
+
+static void
+PaletteMake(t_sm_palette * tpal)
+{
+ int max_colors;
+ int min_colors;
+ char *save_title = (char *) 0;
+
+ /* The information retained in a linked list is the cmap_t structure.
+ * That colormap structure doesn't contain the palette specifications
+ * t_sm_palette used to generate the colormap. Therefore, the making
+ * of the colormap must be carried all the way through before we can
+ * determine if it is unique from the other colormaps in the linked list.
+ *
+ * Rather than create and initialize a colormap outside of the list, we'll
+ * just put a new one in the list, and if it isn't unique remove it later.
+ */
+
+ cmap_t *new_cmap = Add_CMap_To_Linked_List();
+
+ /* Continue until valid palette is built. May require multiple passes. */
+ while (1) {
+
+ if (tpal) {
+
+ if (tpal->use_maxcolors > 0) {
+ max_colors = tpal->use_maxcolors;
+ } else {
+ max_colors = maximal_possible_colors;
+ }
+
+ FPRINTF((stderr, "(PaletteMake) tpal->use_maxcolors = %d\n",
+ tpal->use_maxcolors));
+
+ /* free old gradient table */
+ if (sm_palette.gradient) {
+ free( sm_palette.gradient );
+ sm_palette.gradient = NULL;
+ sm_palette.gradient_num = 0;
+ }
+
+ sm_palette.colorMode = tpal->colorMode;
+ sm_palette.positive = tpal->positive;
+ sm_palette.use_maxcolors = tpal->use_maxcolors;
+ sm_palette.cmodel = tpal->cmodel;
+
+ switch( sm_palette.colorMode ) {
+ case SMPAL_COLOR_MODE_GRAY:
+ sm_palette.gamma = tpal->gamma;
+ break;
+ case SMPAL_COLOR_MODE_RGB:
+ sm_palette.formulaR = tpal->formulaR;
+ sm_palette.formulaG = tpal->formulaG;
+ sm_palette.formulaB = tpal->formulaB;
+ break;
+ case SMPAL_COLOR_MODE_FUNCTIONS:
+ fprintf( stderr, "Ooops: no SMPAL_COLOR_MODE_FUNCTIONS here!\n" );
+ break;
+ case SMPAL_COLOR_MODE_GRADIENT:
+ sm_palette.gradient_num = tpal->gradient_num;
+ /* Take over the memory from tpal. */
+ sm_palette.gradient = tpal->gradient;
+ tpal->gradient = NULL;
+ break;
+ default:
+ fprintf(stderr,"%s:%d ooops: Unknown color mode '%c'.\n",
+ __FILE__, __LINE__, (char)(sm_palette.colorMode) );
+ }
+
+ } else {
+ max_colors = maximal_possible_colors;
+ FPRINTF((stderr, "(PaletteMake) tpal=NULL\n"));
+ }
+
+ if (minimal_possible_colors < max_colors)
+ min_colors = minimal_possible_colors;
+ else
+ min_colors = max_colors / (num_colormaps > 1 ? 2 : 8);
+
+ if (current_plot) {
+
+ if (current_plot->window) {
+ char *msg;
+ char *added_text = " allocating colors ...";
+ int orig_len = (current_plot->titlestring ? strlen(current_plot->titlestring) : 0);
+ XFetchName(dpy, current_plot->window, &save_title);
+ if ((msg = (char *) malloc(orig_len + strlen(added_text) + 1))) {
+ if (current_plot->titlestring)
+ strcpy(msg, current_plot->titlestring);
+ else
+ msg[0] = '\0';
+ strcat(msg, added_text);
+ XStoreName(dpy, current_plot->window, msg);
+ free(msg);
+ }
+ }
+
+ if (!num_colormaps) {
+ XFree(XListInstalledColormaps(dpy, current_plot->window, &num_colormaps));
+ FPRINTF((stderr, "(PaletteMake) num_colormaps = %d\n", num_colormaps));
+ }
+
+ }
+
+ /* TODO */
+ /* EventuallyChangeVisual(plot); */
+
+ /*
+ * start with trying to allocate max_colors. This should
+ * always succeed with TrueColor visuals >= 16bit. If it
+ * fails (for example for a PseudoColor visual of depth 8),
+ * try it with half of the colors. Proceed until min_colors
+ * is reached. If this fails we should probably install a
+ * private colormap.
+ * Note that I make no difference for different
+ * visual types here. (joze)
+ */
+ for ( /* EMPTY */ ; max_colors >= min_colors; max_colors /= 2) {
+
+ XColor xcolor;
+ double fact = 1.0 / (double)(max_colors-1);
+
+ if (current_plot)
+ ReallocColors(new_cmap, max_colors);
+
+ for (new_cmap->allocated = 0; new_cmap->allocated < max_colors; new_cmap->allocated++) {
+
+ double gray = (double) new_cmap->allocated * fact;
+ rgb_color color;
+ rgb1_from_gray( gray, &color );
+ xcolor.red = 0xffff * color.r + 0.5;
+ xcolor.green = 0xffff * color.g + 0.5;
+ xcolor.blue = 0xffff * color.b + 0.5;
+
+ if (XAllocColor(dpy, new_cmap->colormap, &xcolor)) {
+ new_cmap->pixels[new_cmap->allocated] = xcolor.pixel;
+ } else {
+ FPRINTF((stderr, "failed at color %d\n", new_cmap->allocated));
+ break;
+ }
+ }
+
+ if (new_cmap->allocated == max_colors) {
+ break; /* success! */
+ }
+
+ /* reduce the number of max_colors to at
+ * least less than new_cmap->allocated */
+ while (max_colors > new_cmap->allocated && max_colors >= min_colors) {
+ max_colors /= 2;
+ }
+ }
+
+ FPRINTF((stderr, "(PaletteMake) allocated = %d\n", new_cmap->allocated));
+ FPRINTF((stderr, "(PaletteMake) max_colors = %d\n", max_colors));
+ FPRINTF((stderr, "(PaletteMake) min_colors = %d\n", min_colors));
+
+ if (new_cmap->allocated < min_colors && tpal) {
+ /* create a private colormap on second pass. */
+ FPRINTF((stderr, "switching to private colormap\n"));
+ tpal = 0;
+ } else {
+ break;
+ }
+ }
+
+ /* Now check the uniqueness of the new colormap against all the other
+ * colormaps in the linked list.
+ */
+ {
+ cmap_t *cmp = cmap_list_start;
+
+ while (cmp != NULL) {
+ if ((cmp != new_cmap) && !cmaps_differ(cmp, new_cmap))
+ break;
+ cmp = cmp->next_cmap;
+ }
+
+ if (cmp != NULL) {
+ /* Found a match. Discard the newly created colormap. (Could
+ * have simply discarded the old one and combined parts of
+ * code similar to these if statements, but we'll avoid removing
+ * old blocks of memory so that it is more likely that heap
+ * memory will remain more tidy.
+ */
+ Remove_CMap_From_Linked_List(new_cmap);
+ if (current_plot) {
+ current_plot->cmap = cmp;
+ RecolorWindow(current_plot);
+ }
+ current_cmap = cmp;
+ } else {
+ /* Unique and truely new colormap. Make it the current map. */
+ if (current_plot) {
+ current_plot->cmap = new_cmap;
+ RecolorWindow(current_plot);
+ }
+ /* If no other plots using colormap, then can be removed. */
+ if (!Find_Plot_In_Linked_List_By_CMap(current_cmap))
+ Remove_CMap_From_Linked_List(current_cmap);
+ current_cmap = new_cmap;
+ }
+
+ }
+
+ if (save_title) {
+ /* Restore window title (current_plot and current_plot->window are
+ * valid, otherwise would not have been able to get save_title.
+ */
+ XStoreName(dpy, current_plot->window, save_title);
+ XFree(save_title);
+ }
+
+}
+
+static void
+PaletteSetColor(plot_struct * plot, double gray)
+{
+ if (plot->cmap->allocated) {
+ int index;
+
+ gray = floor(gray * plot->cmap->allocated) / (plot->cmap->allocated - 1);
+ index = gray * (plot->cmap->allocated - 1);
+ if (index >= plot->cmap->allocated)
+ index = plot->cmap->allocated -1;
+
+ XSetForeground(dpy, gc, plot->cmap->pixels[index]);
+ plot->current_rgb = plot->cmap->pixels[index];
+ }
+}
+
+#ifdef USE_MOUSE
+
+static int
+ErrorHandler(Display * display, XErrorEvent * error_event)
+{
+ /* Don't remove directly. Main program might be using the memory. */
+ (void) display; /* avoid -Wunused warnings */
+ Add_Plot_To_Remove_FIFO_Queue((Window) error_event->resourceid);
+ gp_exec_event(GE_reset, 0, 0, 0, 0, 0);
+ return 0;
+}
+
+static void
+DrawRuler(plot_struct * plot)
+{
+ if (plot->ruler_on) {
+ int x = X(plot->ruler_x);
+ int y = Y(plot->ruler_y);
+ if (!gc_xor) {
+ /* create a gc for `rubberbanding' (well ...) */
+ GetGCXor(plot, &gc_xor);
+ }
+ /* vertical line */
+ XDrawLine(dpy, plot->window, gc_xor, x, 0, x, GRAPH_HEIGHT(plot));
+ /* horizontal line */
+ XDrawLine(dpy, plot->window, gc_xor, 0, y, plot->width, y);
+ }
+}
+
+static void
+EventuallyDrawMouseAddOns(plot_struct * plot)
+{
+ DrawRuler(plot);
+ DrawLineToRuler(plot);
+ if (plot->zoombox_on)
+ DrawBox(plot);
+ DrawCoords(plot, plot->str);
+ /*
+ TODO more ...
+ */
+}
+
+
+/*
+ * draw a line using the gc with the GXxor function.
+ * This can be used to turn on *and off* a line between
+ * the current mouse pointer and the ruler.
+ */
+static void
+DrawLineToRuler(plot_struct * plot)
+{
+ if (plot->ruler_on == FALSE || plot->ruler_lineto_on == FALSE)
+ return;
+ if (plot->ruler_lineto_x < 0)
+ return;
+ if (!gc_xor) {
+ GetGCXor(plot, &gc_xor);
+ }
+ XDrawLine(dpy, plot->window, gc_xor,
+ X(plot->ruler_x), Y(plot->ruler_y),
+ plot->ruler_lineto_x, plot->ruler_lineto_y);
+}
+
+
+
+
+/*
+ * draw a box using the gc with the GXxor function.
+ * This can be used to turn on *and off* a box. The
+ * corners of the box are annotated with the strings
+ * stored in the plot structure.
+ */
+static void
+DrawBox(plot_struct * plot)
+{
+ int width;
+ int height;
+ int X0 = plot->zoombox_x1;
+ int Y0 = plot->zoombox_y1;
+ int X1 = plot->zoombox_x2;
+ int Y1 = plot->zoombox_y2;
+
+ if (!gc_xor_dashed) {
+ GetGCXorDashed(plot, &gc_xor_dashed);
+ }
+
+ if (X1 < X0) {
+ int tmp = X1;
+ X1 = X0;
+ X0 = tmp;
+ }
+
+ if (Y1 < Y0) {
+ int tmp = Y1;
+ Y1 = Y0;
+ Y0 = tmp;
+ }
+
+ width = X1 - X0;
+ height = Y1 - Y0;
+
+ XDrawRectangle(dpy, plot->window, gc_xor_dashed, X0, Y0, width, height);
+
+ if (plot->zoombox_str1a[0] || plot->zoombox_str1b[0])
+ AnnotatePoint(plot, plot->zoombox_x1, plot->zoombox_y1, plot->zoombox_str1a, plot->zoombox_str1b);
+ if (plot->zoombox_str2a[0] || plot->zoombox_str2b[0])
+ AnnotatePoint(plot, plot->zoombox_x2, plot->zoombox_y2, plot->zoombox_str2a, plot->zoombox_str2b);
+}
+
+
+/*
+ * draw the strings xstr and ystr centered horizontally
+ * and vertically at the point x, y. Use the GXxor
+ * as usually, so that we can also remove the coords
+ * later.
+ */
+static void
+AnnotatePoint(plot_struct * plot, int x, int y, const char xstr[], const char ystr[])
+{
+ int ylen, xlen;
+ int xwidth, ywidth;
+
+ xlen = strlen(xstr);
+ xwidth = gpXTextWidth(font, xstr, xlen);
+
+ ylen = strlen(ystr);
+ ywidth = gpXTextWidth(font, ystr, ylen);
+
+ /* horizontal centering disabled (joze) */
+
+ if (!gc_xor) {
+ GetGCXor(plot, &gc_xor);
+ }
+ gpXDrawString(dpy, plot->window, gc_xor, x, y - 3, xstr, xlen);
+ gpXDrawString(dpy, plot->window, gc_xor, x, y + vchar, ystr, ylen);
+}
+
+/* returns the time difference to the last click in milliseconds */
+static long int
+SetTime(plot_struct *plot, Time t)
+{
+ long int diff = t - plot->time;
+
+ FPRINTF((stderr, "(SetTime) difftime = %ld\n", diff));
+
+ plot->time = t;
+ return diff > 0 ? diff : 0;
+}
+
+static unsigned long
+AllocateXorPixel(cmap_t *cmap_ptr)
+{
+ unsigned long pixel;
+ XColor xcolor;
+
+ xcolor.pixel = cmap_ptr->colors[0]; /* background color */
+ XQueryColor(dpy, cmap_ptr->colormap, &xcolor);
+
+ if (xcolor.red + xcolor.green + xcolor.blue < 0xffff) {
+ /* it is admittedly somehow arbitrary to call
+ * everything with a gray value < 0xffff a
+ * dark background. Try to use the background's
+ * complement for drawing which will always
+ * result in white when using xor. */
+ xcolor.red = ~xcolor.red;
+ xcolor.green = ~xcolor.green;
+ xcolor.blue = ~xcolor.blue;
+ if (XAllocColor(dpy, cmap_ptr->colormap, &xcolor)) {
+ /* use white foreground for dark backgrounds */
+ pixel = xcolor.pixel;
+ } else {
+ /* simple xor if we've run out of colors. */
+ pixel = WhitePixel(dpy, scr);
+ }
+ } else {
+ /* use the background itself for drawing.
+ * xoring two same colors will always result
+ * in black. This color is already allocated. */
+ pixel = xcolor.pixel;
+ }
+ cmap_ptr->xorpixel = pixel;
+ return pixel;
+}
+
+static void
+GetGCXor(plot_struct * plot, GC * ret)
+{
+ XGCValues values;
+ unsigned long mask = 0;
+
+ values.foreground = AllocateXorPixel(plot->cmap);
+
+#ifdef USE_X11_MULTIBYTE
+ if (usemultibyte) {
+ mask = GCForeground | GCFunction;
+ values.function = GXxor;
+ } else
+#endif
+ {
+ mask = GCForeground | GCFunction | GCFont;
+ values.function = GXxor;
+ values.font = font->fid;
+ }
+
+ *ret = XCreateGC(dpy, plot->window, mask, &values);
+}
+
+static void
+GetGCXorDashed(plot_struct * plot, GC * gc)
+{
+ GetGCXor(plot, gc);
+ XSetLineAttributes(dpy, *gc, 0, /* line width, X11 treats 0 as a `thin' line */
+ LineOnOffDash, /* also: LineDoubleDash */
+ CapNotLast, /* also: CapButt, CapRound, CapProjecting */
+ JoinMiter /* also: JoinRound, JoinBevel */ );
+}
+
+#if 0
+/*
+ * returns the newly created gc
+ * pixmap: where the gc will be used
+ * mode == 0 --> black on white
+ * mode == 1 --> white on black
+ */
+/* FIXME HBB 20020225: This function is not used anywhere ??? */
+static void
+GetGCBlackAndWhite(plot_struct * plot, GC * ret, Pixmap pixmap, int mode)
+{
+ XGCValues values;
+ unsigned long mask = 0;
+
+ mask = GCForeground | GCBackground | GCFont | GCFunction;
+ if (!mode) {
+#if 0
+ values.foreground = BlackPixel(dpy, scr);
+ values.background = WhitePixel(dpy, scr);
+#else
+ values.foreground = plot->cmap->colors[1];
+ values.background = plot->cmap->colors[0];
+#endif
+ } else {
+ /*
+ * swap colors
+ */
+#if 0
+ values.foreground = WhitePixel(dpy, scr);
+ values.background = BlackPixel(dpy, scr);
+#else
+ values.foreground = plot->cmap->colors[0];
+ values.background = plot->cmap->colors[1];
+#endif
+ }
+ values.function = GXcopy;
+ values.font = font->fid;
+
+ *ret = XCreateGC(dpy, pixmap, mask, &values);
+}
+
+/*
+ * split a string at `splitchar'.
+ */
+/* FIXME HBB 20020225: This function is not used anywhere ??? */
+static int
+SplitAt(char **args, int maxargs, char *buf, char splitchar)
+{
+ int argc = 0;
+
+ while (*buf != '\0' && argc < maxargs) {
+
+ if ((*buf == splitchar))
+ *buf++ = '\0';
+
+ if (!(*buf)) /* don't count the terminating NULL */
+ break;
+
+ /* Save the argument. */
+ *args++ = buf;
+ argc++;
+
+ /* Skip over the argument */
+ while ((*buf != '\0') && (*buf != splitchar))
+ buf++;
+ }
+
+ *args = '\0'; /* terminate */
+ return argc;
+}
+
+/* FIXME HBB 20020225: This function is not used anywhere ??? */
+static void
+xfree(void *fred)
+{
+ if (fred)
+ free(fred);
+}
+#endif
+
+/* erase the last displayed position string */
+static void
+EraseCoords(plot_struct * plot)
+{
+ DrawCoords(plot, plot->str);
+}
+
+
+
+static void
+DrawCoords(plot_struct * plot, const char *str)
+{
+ if (!gc_xor) {
+ GetGCXor(plot, &gc_xor);
+ }
+
+ if (str[0] != 0)
+ gpXDrawString(dpy, plot->window, gc_xor, 1, plot->gheight + vchar - 1, str, strlen(str));
+}
+
+
+/* display text (e.g. mouse position) in the lower left corner of the window. */
+static void
+DisplayCoords(plot_struct * plot, const char *s)
+{
+ /* first, erase old text */
+ EraseCoords(plot);
+
+ if (s[0] == 0) {
+ /* no new text? */
+ if (plot->height > plot->gheight) {
+ /* and window has space for text? then make it smaller, unless we're already doing a resize: */
+ if (!plot->resizing) {
+ XResizeWindow(dpy, plot->window, plot->width, plot->gheight);
+ plot->resizing = TRUE;
+ }
+ }
+ } else {
+ /* so we do have new text */
+ if (plot->height == plot->gheight) {
+ /* window not large enough? then make it larger, unless we're already doing a resize: */
+ if (!plot->resizing) {
+ XResizeWindow(dpy, plot->window, plot->width, plot->gheight + vchar);
+ plot->resizing = TRUE;
+ }
+ }
+ }
+
+ /* finally, draw the new text: */
+ DrawCoords(plot, s);
+
+ /* and save it, for later erasing: */
+ strcpy(plot->str, s);
+}
+
+
+static TBOOLEAN
+is_meta(KeySym mod)
+{
+ /* CJK keyboards may have these meta keys */
+ if (0xFF20 <= mod && mod <= 0xFF3F)
+ return TRUE;
+
+ /* Everyone else may have these meta keys */
+ switch (mod) {
+ case XK_Multi_key:
+ case XK_Shift_L:
+ case XK_Shift_R:
+ case XK_Control_L:
+ case XK_Control_R:
+ case XK_Meta_L:
+ case XK_Meta_R:
+ case XK_Alt_L:
+ case XK_Alt_R:
+ return TRUE;
+ default:
+ return FALSE;
+ }
+}
+
+/* It returns NULL if we are not running in any known (=implemented) multitab
+ * console.
+ * Otherwise it returns a command to be executed in order to switch to the
+ * appropriate tab of a multitab console.
+ * In addition, it may return non-zero newGnuplotXID in order to overwrite zero
+ * value of gnuplotXID (because Konsole's in KDE <3.2 don't set WINDOWID contrary
+ * to all other xterm's).
+ * Currently implemented for:
+ * - KDE's Konsole.
+ * Note: if the returned command is !NULL, then it must be free()'d by the caller.
+ */
+static char*
+getMultiTabConsoleSwitchCommand(unsigned long *newGnuplotXID)
+{
+#ifdef HAVE_STRDUP /* We assume that any machine missing strdup is too old for KDE */
+ char *cmd = NULL; /* result */
+ char *ptr = getenv("KONSOLE_DCOP_SESSION"); /* Try KDE's Konsole first. */
+ *newGnuplotXID = 0;
+ if (ptr) {
+ /* We are in KDE's Konsole, or in a terminal window detached from a Konsole.
+ * In order to active a tab:
+ * 1. get environmental variable KONSOLE_DCOP_SESSION: it includes konsole id and session name
+ * 2. if
+ * $WINDOWID is defined and it equals
+ * `dcop konsole-3152 konsole-mainwindow#1 getWinID`
+ * (KDE 3.2) or when $WINDOWID is undefined (KDE 3.1), then run commands
+ * dcop konsole-3152 konsole activateSession session-2; \
+ * dcop konsole-3152 konsole-mainwindow#1 raise
+ * Note: by $WINDOWID we mean gnuplot's text console WINDOWID.
+ * Missing: focus is not transferred unless $WINDOWID is defined (should be fixed in KDE 3.2).
+ *
+ * Implementation and tests on KDE 3.1.4: Petr Mikulik.
+ */
+ char *konsole_name = NULL;
+ *newGnuplotXID = 0; /* don't change gnuplotXID by default */
+ /* use 'while' instead of 'if' to easily break out (aka catch exception) */
+ while (1) {
+ char *konsole_tab;
+ unsigned long w;
+ FILE *p;
+ ptr = strchr(ptr, '(');
+ /* the string for tab nb 4 looks like 'DCOPRef(konsole-2866, session-4)' */
+ if (!ptr) return NULL;
+ konsole_name = strdup(ptr+1);
+ konsole_tab = strchr(konsole_name, ',');
+ if (!konsole_tab) break;
+ *konsole_tab++ = 0;
+ ptr = strchr(konsole_tab, ')');
+ if (ptr) *ptr = 0;
+ /* Not necessary to define DCOP_RAISE: returning newly known
+ * newGnuplotXID instead is sufficient.
+ */
+/* #define DCOP_RAISE */
+#ifdef DCOP_RAISE
+ cmd = malloc(2*strlen(konsole_name) + strlen(konsole_tab) + 128);
+#else
+ cmd = malloc(strlen(konsole_name) + strlen(konsole_tab) + 64);
+#endif
+ sprintf(cmd, "dcop %s konsole-mainwindow#1 getWinID 2>/dev/null", konsole_name);
+ /* is 2>/dev/null portable among various shells? */
+ p = popen(cmd, "r");
+ if (p) {
+ fscanf(p, "%lu", &w);
+ pclose(p);
+ }
+ if (gnuplotXID) { /* $WINDOWID is known */
+ if (w != gnuplotXID) break;
+ /* `dcop getWinID`==$WINDOWID thus we are running in a window detached from Konsole */
+ } else {
+ *newGnuplotXID = w;
+ /* $WINDOWID has not been known (KDE 3.1), thus set it up */
+ }
+#ifdef DCOP_RAISE
+ /* not necessary: returning newly known newGnuplotXID instead is sufficient */
+ sprintf(cmd, "dcop %s konsole-mainwindow#1 raise;", konsole_name);
+ sprintf(cmd+strlen(cmd), "dcop %s konsole activateSession %s", konsole_name, konsole_tab);
+#else
+ sprintf(cmd, "dcop %s konsole activateSession %s", konsole_name, konsole_tab);
+#endif
+ free(konsole_name);
+ return cmd;
+ }
+ free(konsole_name);
+ free(cmd);
+ return NULL;
+ }
+ /* now test for GNOME multitab console */
+ /* ... if somebody bothers to implement it ... */
+
+#endif /* HAVE_STRDUP */
+ /* we are not running in any known (implemented) multitab console */
+ return NULL;
+}
+
+#endif
+
+
+/*---------------------------------------------------------------------------
+ * reset all cursors (since we dont have a record of the previous terminal #)
+ *---------------------------------------------------------------------------*/
+
+static void
+reset_cursor()
+{
+ plot_struct *plot = plot_list_start;
+
+ while (plot) {
+ if (plot->window) {
+ FPRINTF((stderr, "Window for plot %d exists\n", plot->plot_number));
+ XUndefineCursor(dpy, plot->window);
+ }
+ plot = plot->next_plot;
+ }
+
+ FPRINTF((stderr, "Cursors reset\n"));
+ return;
+}
+
+/*-----------------------------------------------------------------------------
+ * resize - rescale last plot if window resized
+ *---------------------------------------------------------------------------*/
+
+#ifdef USE_MOUSE
+
+/* the status of the shift, ctrl and alt keys
+*/
+static int modifier_mask = 0;
+
+static void update_modifiers __PROTO((unsigned int));
+
+static void
+update_modifiers(unsigned int state)
+{
+ int old_mod_mask;
+
+ old_mod_mask = modifier_mask;
+ modifier_mask = ((state & ShiftMask) ? Mod_Shift : 0)
+ | ((state & ControlMask) ? Mod_Ctrl : 0)
+ | ((state & Mod1Mask) ? Mod_Alt : 0);
+ if (old_mod_mask != modifier_mask) {
+ gp_exec_event(GE_modifier, 0, 0, modifier_mask, 0, 0);
+ }
+}
+
+#endif
+
+static void
+process_configure_notify_event(XEvent *event)
+{
+ plot_struct *plot;
+ int force_redraw = 0;
+
+ /* Filter down to the last ConfigureNotify event */
+ XSync(dpy, False);
+ while (XCheckTypedWindowEvent(dpy, event->xany.window, ConfigureNotify, event))
+ ;
+
+ plot = Find_Plot_In_Linked_List_By_Window(event->xconfigure.window);
+ if (plot) {
+ int w = event->xconfigure.width, h = event->xconfigure.height;
+
+ /* store settings in case window is closed then recreated */
+ /* but: don't do this if both x and y are 0, since some
+ * (all?) systems set these to zero when only resizing
+ * (not moving) the window. This does mean that a move to
+ * (0, 0) won't be registered: can we solve that? */
+ if (event->xconfigure.x != 0 || event->xconfigure.y != 0) {
+ plot->x = event->xconfigure.x;
+ plot->y = event->xconfigure.y;
+ plot->posn_flags = (plot->posn_flags & ~PPosition) | USPosition;
+ }
+#ifdef USE_MOUSE
+ /* first, check whether we were waiting for completion of a resize */
+ if (plot->resizing) {
+ /* it seems to be impossible to distinguish between a
+ * resize caused by our call to XResizeWindow(), and a
+ * resize started by the user/windowmanager; but we can
+ * make a good guess which can only fail if the user
+ * resizes the window while we're also resizing it
+ * ourselves: */
+ if (w == plot->width
+ && (h == plot->gheight || h == plot->gheight + vchar)) {
+ /* most likely, it's a resize for showing/hiding the status line.
+ * Test whether the height is now correct; if not, start another resize. */
+ plot->resizing = FALSE;
+ if (w == plot->width
+ && h == plot->gheight + (plot->str[0] ? vchar : 0)) {
+ /* Was successful, status line can be drawn without rescaling plot. */
+ plot->height = h;
+ return;
+ } else {
+ /* Possibly, a resize attempt _failed_ because window manager denied it.
+ Resizing again goes into a vicious endless loop!
+ (Seen with fluxbox-1.0.0 and a tab group of gnuplot windows.) */
+ /* Instead of just appending the status line, redraw/scale the plot. */
+ force_redraw = TRUE;
+ }
+ }
+ }
+#endif
+
+ if (w > 1 && h > 1 && (force_redraw || w != plot->width || h != plot->height)) {
+
+ plot->width = w;
+ plot->height = h;
+#ifdef USE_MOUSE
+ /* Make sure that unsigned number doesn't underflow. */
+ if (plot->str[0])
+ plot->gheight = (vchar > plot->height) ? 0 : plot->height - vchar;
+ else
+ plot->gheight = plot->height;
+#endif
+ plot->posn_flags = (plot->posn_flags & ~PSize) | USSize;
+
+ if (stipple_initialized) {
+ int i;
+ for (i = 0; i < stipple_pattern_num; i++)
+ XFreePixmap(dpy, stipple_pattern[i]);
+ stipple_initialized = 0;
+ }
+
+ if (plot->pixmap) {
+ /* it is the wrong size now */
+ FPRINTF((stderr, "Free pixmap %d\n", 0));
+ XFreePixmap(dpy, plot->pixmap);
+ plot->pixmap = None;
+ }
+ display(plot);
+ }
+ }
+}
+
+static void
+process_event(XEvent *event)
+{
+ plot_struct *plot;
+ KeySym keysym;
+ char key_sequence[8];
+
+ FPRINTF((stderr, "Event 0x%x\n", event->type));
+
+ switch (event->type) {
+ case ConfigureNotify:
+ process_configure_notify_event(event);
+ break;
+
+ case KeyPress:
+ plot = Find_Plot_In_Linked_List_By_Window(event->xkey.window);
+
+ /* Unlike XKeycodeToKeysym, XLookupString applies the current */
+ /* shift, ctrl, alt, and meta modifiers to yield a character. */
+ /* keysym = XKeycodeToKeysym(dpy, event->xkey.keycode, 0); */
+ XLookupString((XKeyEvent *)event, key_sequence, sizeof(key_sequence), &keysym, NULL);
+
+#ifdef USE_MOUSE
+ update_modifiers(event->xkey.state);
+#endif
+ switch (keysym) {
+#ifdef USE_MOUSE
+ case ' ': {
+ static int cmd_tried = 0;
+ static char *cmd = NULL;
+ static unsigned long newGnuplotXID = 0;
+
+ /* If the "-ctrlq" resource is set, ignore ' ' unless control key is also pressed */
+ if (ctrlq && !(modifier_mask & Mod_Ctrl))
+ break;
+
+ if (!cmd_tried) {
+ cmd = getMultiTabConsoleSwitchCommand(&newGnuplotXID);
+ cmd_tried = 1;
+ }
+ /* overwrite gnuplotXID (re)set after x11.trm:X11_options() */
+ if (newGnuplotXID) gnuplotXID = newGnuplotXID;
+ if (cmd) system(cmd);
+ }
+ if (gnuplotXID) {
+ XMapRaised(dpy, gnuplotXID);
+ XSetInputFocus(dpy, gnuplotXID, 0 /*revert */ , CurrentTime);
+ XFlush(dpy);
+ }
+ return;
+ case 'm': /* Toggle mouse display, but only if we control the window here */
+ if (((plot != current_plot) && (!modifier_mask))
+#ifdef PIPE_IPC
+ || pipe_died
+#endif
+ ) {
+ plot->mouse_on = !(plot->mouse_on);
+ DisplayCoords(plot, plot->mouse_on ? " " : "");
+ }
+ break;
+#endif
+ case 'q':
+#ifdef USE_MOUSE
+ /* If the "-ctrlq" resource is set, ignore q unless control key is also pressed */
+ if (ctrlq && !(modifier_mask & Mod_Ctrl)) {
+ FPRINTF((stderr, "ignoring q, modifier_mask = %o\n", modifier_mask));
+ break;
+ }
+#endif
+ /* close X window */
+ Remove_Plot_From_Linked_List(event->xkey.window);
+ return;
+ default:
+ break;
+ } /* switch (keysym) */
+#ifdef USE_MOUSE
+
+ if (is_meta(keysym))
+ return;
+
+ switch (keysym) {
+
+#define KNOWN_KEYSYMS(gp_keysym) \
+ if (plot == current_plot) { \
+ gp_exec_event(GE_keypress, \
+ (int)RevX(event->xkey.x), (int)RevY(event->xkey.y), \
+ gp_keysym, 0, plot->plot_number); \
+ } else { \
+ gp_exec_event(GE_keypress_old, \
+ (int)RevX(event->xkey.x), (int)RevY(event->xkey.y), \
+ gp_keysym, 0, plot->plot_number); \
+ } \
+ return;
+
+/* Prevent hysteresis if redraw cannot keep up with rate of keystrokes */
+#define DRAIN_KEYSTROKES(key) \
+ if (plot == current_plot) { \
+ while (XCheckTypedWindowEvent(dpy, \
+ event->xany.window, KeyPress, event)); \
+ }
+
+ case XK_BackSpace:
+ KNOWN_KEYSYMS(GP_BackSpace);
+ case XK_Tab:
+ KNOWN_KEYSYMS(GP_Tab);
+ case XK_Linefeed:
+ KNOWN_KEYSYMS(GP_Linefeed);
+ case XK_Clear:
+ KNOWN_KEYSYMS(GP_Clear);
+ case XK_Return:
+ KNOWN_KEYSYMS(GP_Return);
+ case XK_Pause:
+ KNOWN_KEYSYMS(GP_Pause);
+ case XK_Scroll_Lock:
+ KNOWN_KEYSYMS(GP_Scroll_Lock);
+#ifdef XK_Sys_Req
+ case XK_Sys_Req:
+ KNOWN_KEYSYMS(GP_Sys_Req);
+#endif
+ case XK_Escape:
+ KNOWN_KEYSYMS(GP_Escape);
+ case XK_Insert:
+ KNOWN_KEYSYMS(GP_Insert);
+ case XK_Delete:
+ KNOWN_KEYSYMS(GP_Delete);
+ case XK_Home:
+ KNOWN_KEYSYMS(GP_Home);
+ case XK_Left:
+ DRAIN_KEYSTROKES(XK_Left);
+ KNOWN_KEYSYMS(GP_Left);
+ case XK_Up:
+ DRAIN_KEYSTROKES(XK_Up);
+ KNOWN_KEYSYMS(GP_Up);
+ case XK_Right:
+ DRAIN_KEYSTROKES(XK_Right);
+ KNOWN_KEYSYMS(GP_Right);
+ case XK_Down:
+ DRAIN_KEYSTROKES(XK_Down);
+ KNOWN_KEYSYMS(GP_Down);
+ case XK_Prior: /* XXX */
+ KNOWN_KEYSYMS(GP_PageUp);
+ case XK_Next: /* XXX */
+ KNOWN_KEYSYMS(GP_PageDown);
+ case XK_End:
+ KNOWN_KEYSYMS(GP_End);
+ case XK_Begin:
+ KNOWN_KEYSYMS(GP_Begin);
+ case XK_KP_Space:
+ KNOWN_KEYSYMS(GP_KP_Space);
+ case XK_KP_Tab:
+ KNOWN_KEYSYMS(GP_KP_Tab);
+ case XK_KP_Enter:
+ KNOWN_KEYSYMS(GP_KP_Enter);
+ case XK_KP_F1:
+ KNOWN_KEYSYMS(GP_KP_F1);
+ case XK_KP_F2:
+ KNOWN_KEYSYMS(GP_KP_F2);
+ case XK_KP_F3:
+ KNOWN_KEYSYMS(GP_KP_F3);
+ case XK_KP_F4:
+ KNOWN_KEYSYMS(GP_KP_F4);
+#ifdef XK_KP_Home
+ case XK_KP_Home:
+ KNOWN_KEYSYMS(GP_KP_Home);
+#endif
+#ifdef XK_KP_Left
+ case XK_KP_Left:
+ KNOWN_KEYSYMS(GP_KP_Left);
+#endif
+#ifdef XK_KP_Up
+ case XK_KP_Up:
+ KNOWN_KEYSYMS(GP_KP_Up);
+#endif
+#ifdef XK_KP_Right
+ case XK_KP_Right:
+ KNOWN_KEYSYMS(GP_KP_Right);
+#endif
+#ifdef XK_KP_Down
+ case XK_KP_Down:
+ KNOWN_KEYSYMS(GP_KP_Down);
+#endif
+#ifdef XK_KP_Page_Up
+ case XK_KP_Page_Up:
+ KNOWN_KEYSYMS(GP_KP_Page_Up);
+#endif
+#ifdef XK_KP_Page_Down
+ case XK_KP_Page_Down:
+ KNOWN_KEYSYMS(GP_KP_Page_Down);
+#endif
+#ifdef XK_KP_End
+ case XK_KP_End:
+ KNOWN_KEYSYMS(GP_KP_End);
+#endif
+#ifdef XK_KP_Begin
+ case XK_KP_Begin:
+ KNOWN_KEYSYMS(GP_KP_Begin);
+#endif
+#ifdef XK_KP_Insert
+ case XK_KP_Insert:
+ KNOWN_KEYSYMS(GP_KP_Insert);
+#endif
+#ifdef XK_KP_Delete
+ case XK_KP_Delete:
+ KNOWN_KEYSYMS(GP_KP_Delete);
+#endif
+ case XK_KP_Equal:
+ KNOWN_KEYSYMS(GP_KP_Equal);
+ case XK_KP_Multiply:
+ KNOWN_KEYSYMS(GP_KP_Multiply);
+ case XK_KP_Add:
+ KNOWN_KEYSYMS(GP_KP_Add);
+ case XK_KP_Separator:
+ KNOWN_KEYSYMS(GP_KP_Separator);
+ case XK_KP_Subtract:
+ KNOWN_KEYSYMS(GP_KP_Subtract);
+ case XK_KP_Decimal:
+ KNOWN_KEYSYMS(GP_KP_Decimal);
+ case XK_KP_Divide:
+ KNOWN_KEYSYMS(GP_KP_Divide);
+
+ case XK_KP_0:
+ KNOWN_KEYSYMS(GP_KP_0);
+ case XK_KP_1:
+ KNOWN_KEYSYMS(GP_KP_1);
+ case XK_KP_2:
+ KNOWN_KEYSYMS(GP_KP_2);
+ case XK_KP_3:
+ KNOWN_KEYSYMS(GP_KP_3);
+ case XK_KP_4:
+ KNOWN_KEYSYMS(GP_KP_4);
+ case XK_KP_5:
+ KNOWN_KEYSYMS(GP_KP_5);
+ case XK_KP_6:
+ KNOWN_KEYSYMS(GP_KP_6);
+ case XK_KP_7:
+ KNOWN_KEYSYMS(GP_KP_7);
+ case XK_KP_8:
+ KNOWN_KEYSYMS(GP_KP_8);
+ case XK_KP_9:
+ KNOWN_KEYSYMS(GP_KP_9);
+
+ case XK_F1:
+ KNOWN_KEYSYMS(GP_F1);
+ case XK_F2:
+ KNOWN_KEYSYMS(GP_F2);
+ case XK_F3:
+ KNOWN_KEYSYMS(GP_F3);
+ case XK_F4:
+ KNOWN_KEYSYMS(GP_F4);
+ case XK_F5:
+ KNOWN_KEYSYMS(GP_F5);
+ case XK_F6:
+ KNOWN_KEYSYMS(GP_F6);
+ case XK_F7:
+ KNOWN_KEYSYMS(GP_F7);
+ case XK_F8:
+ KNOWN_KEYSYMS(GP_F8);
+ case XK_F9:
+ KNOWN_KEYSYMS(GP_F9);
+ case XK_F10:
+ KNOWN_KEYSYMS(GP_F10);
+ case XK_F11:
+ KNOWN_KEYSYMS(GP_F11);
+ case XK_F12:
+ KNOWN_KEYSYMS(GP_F12);
+
+ default:
+ KNOWN_KEYSYMS((int) keysym);
+ break;
+ }
+#endif
+ break;
+ case KeyRelease:
+#ifdef USE_MOUSE
+ update_modifiers(event->xkey.state);
+ keysym = XKeycodeToKeysym(dpy, event->xkey.keycode, 0);
+ if (is_meta(keysym)) {
+ plot = Find_Plot_In_Linked_List_By_Window(event->xkey.window);
+ cursor = cursor_default;
+ if (plot)
+ XDefineCursor(dpy, plot->window, cursor);
+ }
+#endif
+ break;
+
+ case ClientMessage:
+ if (event->xclient.message_type == WM_PROTOCOLS &&
+ event->xclient.format == 32 && event->xclient.data.l[0] == WM_DELETE_WINDOW) {
+ Remove_Plot_From_Linked_List(event->xclient.window);
+ /* EAM FIXME - may have to generate GE_reset event here also */
+ }
+ break;
+
+#ifdef USE_MOUSE
+ case DestroyNotify:
+ plot = Find_Plot_In_Linked_List_By_Window(event->xconfigure.window);
+ if (plot == current_plot) {
+ gp_exec_event(GE_reset, 0, 0, 0, 0, 0);
+ }
+ break;
+
+ case Expose:
+ /*
+ * we need to handle expose events here, because
+ * there might stuff like rulers which has to
+ * be redrawn. Well. It's not really hard to handle
+ * this. Note that the x and y fields are not used
+ * to update the pointer pos because the pointer
+ * might be on another window which generates the
+ * expose events. (joze)
+ */
+
+ /* Check for any ConfigureNotify events further down in the X11
+ * event queue. If one is found, handle it first and let the
+ * expose event that is generated be handled later.
+ * Jay Painter Nov 2003.
+ */
+ if (XCheckTypedWindowEvent(dpy, event->xany.window, ConfigureNotify, event)) {
+ process_configure_notify_event(event);
+ break;
+ }
+ while (XCheckTypedWindowEvent(dpy, event->xany.window, Expose, event));
+
+ plot = Find_Plot_In_Linked_List_By_Window(event->xexpose.window);
+
+ if (plot)
+ UpdateWindow(plot);
+ break;
+
+ case EnterNotify:
+ plot = Find_Plot_In_Linked_List_By_Window(event->xcrossing.window);
+ if (!plot)
+ break;
+ if (plot == current_plot) {
+ Call_display(plot);
+ gp_exec_event(GE_motion, (int) RevX(event->xcrossing.x), (int) RevY(event->xcrossing.y), 0, 0, 0);
+ }
+ if (plot->zoombox_on) {
+ DrawBox(plot);
+ plot->zoombox_x2 = event->xcrossing.x;
+ plot->zoombox_y2 = event->xcrossing.y;
+ DrawBox(plot);
+ }
+ if (plot->ruler_on) {
+ DrawLineToRuler(plot);
+ plot->ruler_lineto_x = event->xcrossing.x;
+ plot->ruler_lineto_y = event->xcrossing.y;
+ DrawLineToRuler(plot);
+ }
+ break;
+ case MotionNotify:
+ update_modifiers(event->xmotion.state);
+ plot = Find_Plot_In_Linked_List_By_Window(event->xmotion.window);
+ if (!plot)
+ break;
+ {
+ Window root, child;
+ int root_x, root_y, pos_x, pos_y;
+ unsigned int keys_buttons;
+ if (!XQueryPointer(dpy, event->xmotion.window, &root, &child, &root_x, &root_y, &pos_x, &pos_y, &keys_buttons))
+ break;
+
+ if (plot == current_plot
+#ifdef PIPE_IPC
+ && !pipe_died
+#endif
+ ) {
+ Call_display(plot);
+ gp_exec_event(GE_motion, (int) RevX(pos_x), (int) RevY(pos_y), 0, 0, 0);
+#if defined(USE_MOUSE) && defined(MOUSE_ALL_WINDOWS)
+ } else if (plot->axis_mask && plot->mouse_on && plot->almost2d) {
+ /* This is not the active plot window, but we can still update the mouse coords */
+ char mouse_format[60];
+ char *m = mouse_format;
+ double x, y, x2, y2;
+
+ *m = '\0';
+ mouse_to_coords(plot, event, &x, &y, &x2, &y2);
+ if (plot->axis_mask & (1<<FIRST_X_AXIS)) {
+ sprintf(m, "x= %10g %c", x, '\0');
+ m += 15;
+ }
+ if (plot->axis_mask & (1<<SECOND_X_AXIS)) {
+ sprintf(m, "x2= %10g %c", x2, '\0');
+ m += 15;
+ }
+ if (plot->axis_mask & (1<<FIRST_Y_AXIS)) {
+ sprintf(m, "y= %10g %c", y, '\0');
+ m += 15;
+ }
+ if (plot->axis_mask & (1<<SECOND_Y_AXIS)) {
+ sprintf(m, "y2= %10g %c", y2, '\0');
+ m += 15;
+ }
+ DisplayCoords(plot, mouse_format);
+ } else if (!plot->mouse_on) {
+ DisplayCoords(plot, "");
+#endif
+ }
+
+ if (plot->zoombox_on) {
+ DrawBox(plot);
+ plot->zoombox_x2 = pos_x;
+ plot->zoombox_y2 = pos_y;
+ DrawBox(plot);
+ }
+ if (plot->ruler_on && plot->ruler_lineto_on) {
+ DrawLineToRuler(plot);
+ plot->ruler_lineto_x = event->xcrossing.x;
+ plot->ruler_lineto_y = event->xcrossing.y;
+ DrawLineToRuler(plot);
+ }
+ }
+ break;
+ case ButtonPress:
+ update_modifiers(event->xbutton.state);
+#ifndef TITLE_BAR_DRAWING_MSG
+ button_pressed |= (1 << event->xbutton.button);
+#endif
+ plot = Find_Plot_In_Linked_List_By_Window(event->xbutton.window);
+ if (!plot)
+ break;
+ {
+ if (plot == current_plot) {
+ Call_display(plot);
+ gp_exec_event(GE_buttonpress, (int) RevX(event->xbutton.x), (int) RevY(event->xbutton.y), event->xbutton.button, 0, 0);
+ }
+ }
+ break;
+ case ButtonRelease:
+#ifndef TITLE_BAR_DRAWING_MSG
+ button_pressed &= ~(1 << event->xbutton.button);
+#endif
+ plot = Find_Plot_In_Linked_List_By_Window(event->xbutton.window);
+ if (!plot)
+ break;
+ if (plot == current_plot) {
+
+ long int doubleclick = SetTime(plot, event->xbutton.time);
+
+ update_modifiers(event->xbutton.state);
+ Call_display(plot);
+ gp_exec_event(GE_buttonrelease,
+ (int) RevX(event->xbutton.x), (int) RevY(event->xbutton.y),
+ event->xbutton.button, (int) doubleclick, 0);
+ }
+
+#ifdef DEBUG
+#if defined(USE_MOUSE) && defined(MOUSE_ALL_WINDOWS)
+ /* This causes gnuplot_x11 to pass mouse clicks back from all plot windows,
+ * not just the current plot. But who should we notify that a click has
+ * happened, and how? The fprintf to stderr is just for debugging. */
+ else if (plot->axis_mask) {
+ double x, y, x2, y2;
+ mouse_to_coords(plot, event, &x, &y, &x2, &y2);
+ fprintf(stderr, "gnuplot_x11 %d: mouse button %1d from window %d at %g %g\n",
+ __LINE__, event->xbutton.button, (plot ? plot->plot_number : 0), x, y);
+ }
+#endif
+#endif
+
+ break;
+#endif /* USE_MOUSE */
+#ifdef EXPORT_SELECTION
+ case SelectionNotify:
+ break;
+ case SelectionRequest:
+ if (exportselection)
+ handle_selection_event(event);
+ break;
+#endif
+
+ }
+}
+
+/*-----------------------------------------------------------------------------
+ * preset - determine options, open display, create window
+ *---------------------------------------------------------------------------*/
+/*
+#define On(v) ( !strcmp(v, "on") || !strcmp(v, "true") || \
+ !strcmp(v, "On") || !strcmp(v, "True") || \
+ !strcmp(v, "ON") || !strcmp(v, "TRUE") )
+*/
+#define On(v) ( !strncasecmp(v, "on", 2) || !strncasecmp(v, "true", 4) )
+
+#define AppDefDir "/usr/lib/X11/app-defaults"
+#ifndef MAXHOSTNAMELEN
+#define MAXHOSTNAMELEN 64
+#endif
+
+static XrmDatabase dbCmd, dbApp, dbDef, dbEnv, db = (XrmDatabase) 0;
+
+static char *type[20];
+static XrmValue value;
+
+static XrmOptionDescRec options[] = {
+ {"-mono", ".mono", XrmoptionNoArg, (XPointer) "on"},
+ {"-gray", ".gray", XrmoptionNoArg, (XPointer) "on"},
+ {"-clear", ".clear", XrmoptionNoArg, (XPointer) "on"},
+ {"-tvtwm", ".tvtwm", XrmoptionNoArg, (XPointer) "on"},
+ {"-pointsize", ".pointsize", XrmoptionSepArg, (XPointer) NULL},
+ {"-display", ".display", XrmoptionSepArg, (XPointer) NULL},
+ {"-name", ".name", XrmoptionSepArg, (XPointer) NULL},
+ {"-geometry", "*geometry", XrmoptionSepArg, (XPointer) NULL},
+ {"-background", "*background", XrmoptionSepArg, (XPointer) NULL},
+ {"-bg", "*background", XrmoptionSepArg, (XPointer) NULL},
+ {"-foreground", "*foreground", XrmoptionSepArg, (XPointer) NULL},
+ {"-fg", "*foreground", XrmoptionSepArg, (XPointer) NULL},
+ {"-bordercolor", "*bordercolor", XrmoptionSepArg, (XPointer) NULL},
+ {"-bd", "*bordercolor", XrmoptionSepArg, (XPointer) NULL},
+ {"-borderwidth", ".borderWidth", XrmoptionSepArg, (XPointer) NULL},
+ {"-bw", ".borderWidth", XrmoptionSepArg, (XPointer) NULL},
+ {"-font", "*font", XrmoptionSepArg, (XPointer) NULL},
+ {"-fn", "*font", XrmoptionSepArg, (XPointer) NULL},
+ {"-reverse", "*reverseVideo", XrmoptionNoArg, (XPointer) "on"},
+ {"-rv", "*reverseVideo", XrmoptionNoArg, (XPointer) "on"},
+ {"+rv", "*reverseVideo", XrmoptionNoArg, (XPointer) "off"},
+ {"-iconic", "*iconic", XrmoptionNoArg, (XPointer) "on"},
+ {"-synchronous", "*synchronous", XrmoptionNoArg, (XPointer) "on"},
+ {"-xnllanguage", "*xnllanguage", XrmoptionSepArg, (XPointer) NULL},
+ {"-selectionTimeout", "*selectionTimeout", XrmoptionSepArg, (XPointer) NULL},
+ {"-title", ".title", XrmoptionSepArg, (XPointer) NULL},
+ {"-xrm", NULL, XrmoptionResArg, (XPointer) NULL},
+ {"-raise", "*raise", XrmoptionNoArg, (XPointer) "on"},
+ {"-noraise", "*raise", XrmoptionNoArg, (XPointer) "off"},
+ {"-feedback", "*feedback", XrmoptionNoArg, (XPointer) "on"},
+ {"-nofeedback", "*feedback", XrmoptionNoArg, (XPointer) "off"},
+ {"-ctrlq", "*ctrlq", XrmoptionNoArg, (XPointer) "on"},
+ {"-dashed", "*dashed", XrmoptionNoArg, (XPointer) "on"},
+ {"-solid", "*dashed", XrmoptionNoArg, (XPointer) "off"},
+ {"-persist", "*persist", XrmoptionNoArg, (XPointer) "on"}
+};
+
+#define Nopt (sizeof(options) / sizeof(options[0]))
+
+static void
+preset(int argc, char *argv[])
+{
+ int Argc = argc;
+ char **Argv = argv;
+
+#ifdef VMS
+ char *ldisplay = (char *) 0;
+#else
+ char *ldisplay = getenv("DISPLAY");
+#endif
+ char *home = getenv("HOME");
+ char *server_defaults, *env, buffer[256];
+#if 0
+ Visual *TrueColor_vis, *PseudoColor_vis, *StaticGray_vis, *GrayScale_vis;
+ int TrueColor_depth, PseudoColor_depth, StaticGray_depth, GrayScale_depth;
+#endif
+ char *db_string;
+
+ FPRINTF((stderr, "(preset) \n"));
+
+ /* avoid bus error when env vars are not set */
+ if (ldisplay == NULL)
+ ldisplay = "";
+ if (home == NULL)
+ home = "";
+
+/*---set to ignore ^C and ^Z----------------------------------------------*/
+
+ signal(SIGINT, SIG_IGN);
+#ifdef SIGTSTP
+ signal(SIGTSTP, SIG_IGN);
+#endif
+
+/*---prescan arguments for "-name"----------------------------------------*/
+
+ while (++Argv, --Argc > 0) {
+ if (!strcmp(*Argv, "-name") && Argc > 1) {
+ strncpy(X_Name, Argv[1], sizeof(X_Name) - 1);
+ strncpy(X_Class, Argv[1], sizeof(X_Class) - 1);
+ /* just in case */
+ X_Name[sizeof(X_Name) - 1] = NUL;
+ X_Class[sizeof(X_Class) - 1] = NUL;
+ if (X_Class[0] >= 'a' && X_Class[0] <= 'z')
+ X_Class[0] -= 0x20;
+ }
+ }
+ Argc = argc;
+ Argv = argv;
+
+/*---parse command line---------------------------------------------------*/
+
+ XrmInitialize();
+ XrmParseCommand(&dbCmd, options, Nopt, X_Name, &Argc, Argv);
+ if (Argc > 1) {
+#ifdef PIPE_IPC
+ if (!strcmp(Argv[1], "-noevents")) {
+ pipe_died = 1;
+ } else {
+#endif
+ fprintf(stderr, "\n\
+gnuplot: bad option: %s\n\
+gnuplot: X11 aborted.\n", Argv[1]);
+ EXIT(1);
+#ifdef PIPE_IPC
+ }
+#endif
+ }
+ if (pr_GetR(dbCmd, ".display"))
+ ldisplay = (char *) value.addr;
+
+/*---open display---------------------------------------------------------*/
+
+#ifdef USE_MOUSE
+ XSetErrorHandler(ErrorHandler);
+#endif
+ dpy = XOpenDisplay(ldisplay);
+ if (!dpy) {
+ fprintf(stderr, "\n\
+gnuplot: unable to open display '%s'\n\
+gnuplot: X11 aborted.\n", ldisplay);
+ EXIT(1);
+ }
+ scr = DefaultScreen(dpy);
+ root = DefaultRootWindow(dpy);
+ server_defaults = XResourceManagerString(dpy);
+ vis = DefaultVisual(dpy, scr);
+ dep = DefaultDepth(dpy, scr);
+ default_cmap.colormap = DefaultColormap(dpy, scr);
+ current_cmap = &default_cmap;
+ max_request_size = XMaxRequestSize(dpy) / 2;
+
+
+/**** atoms we will need later ****/
+
+ WM_PROTOCOLS = XInternAtom(dpy, "WM_PROTOCOLS", False);
+ WM_DELETE_WINDOW = XInternAtom(dpy, "WM_DELETE_WINDOW", False);
+
+
+/*---get application defaults--(subset of Xt processing)------------------*/
+
+#ifdef VMS
+ strcpy(buffer, "DECW$USER_DEFAULTS:GNUPLOT_X11.INI");
+#elif defined OS2
+/* for XFree86 ... */
+ {
+ char appdefdir[MAXPATHLEN];
+ strncpy(appdefdir,
+ __XOS2RedirRoot("/XFree86/lib/X11/app-defaults"),
+ sizeof(appdefdir));
+ sprintf(buffer, "%s/%s", appdefdir, "Gnuplot");
+ }
+# else /* !OS/2 */
+ strcpy(buffer, AppDefDir);
+ strcat(buffer, "/");
+ strcat(buffer, "Gnuplot");
+#endif /* !VMS */
+
+ dbApp = XrmGetFileDatabase(buffer);
+ XrmMergeDatabases(dbApp, &db);
+
+/*---get server or ~/.Xdefaults-------------------------------------------*/
+
+ if (server_defaults)
+ dbDef = XrmGetStringDatabase(server_defaults);
+ else {
+#ifdef VMS
+ strcpy(buffer, "DECW$USER_DEFAULTS:DECW$XDEFAULTS.DAT");
+#else
+ strcpy(buffer, home);
+ strcat(buffer, "/.Xdefaults");
+#endif
+ dbDef = XrmGetFileDatabase(buffer);
+ }
+ XrmMergeDatabases(dbDef, &db);
+
+/*---get XENVIRONMENT or ~/.Xdefaults-hostname---------------------------*/
+
+#ifndef VMS
+ if ((env = getenv("XENVIRONMENT")) != NULL)
+ dbEnv = XrmGetFileDatabase(env);
+ else {
+ char *p = NULL, host[MAXHOSTNAMELEN];
+
+ if (GP_SYSTEMINFO(host) < 0) {
+ fprintf(stderr, "gnuplot: %s failed. X11 aborted.\n", SYSINFO_METHOD);
+ EXIT(1);
+ }
+ if ((p = strchr(host, '.')) != NULL)
+ *p = '\0';
+ strcpy(buffer, home);
+ strcat(buffer, "/.Xdefaults-");
+ strcat(buffer, host);
+ dbEnv = XrmGetFileDatabase(buffer);
+ }
+ XrmMergeDatabases(dbEnv, &db);
+#endif /* not VMS */
+
+/*---merge command line options-------------------------------------------*/
+
+ XrmMergeDatabases(dbCmd, &db);
+
+/*---set geometry, font, colors, line widths, dash styles, point size-----*/
+
+ /* a specific visual can be forced by the X resource visual */
+ db_string = pr_GetR(db, ".visual") ? (char *) value.addr : (char *) 0;
+ if (db_string) {
+ Visual *visual = (Visual *) 0;
+ int depth = (int) 0;
+ char **ptr = visual_name;
+ int i;
+ for (i = 0; *ptr; i++, ptr++) {
+ if (!strcmp(db_string, *ptr)) {
+#if 0
+ if (DirectColor == i) {
+ fprintf(stderr, "DirectColor not supported by pm3d, using default.\n");
+ } else
+#endif
+ if (GetVisual(i, &visual, &depth)) {
+ vis = visual;
+ dep = depth;
+ if (vis != DefaultVisual(dpy, scr)) {
+ /* this will be the default colormap */
+ default_cmap.colormap = XCreateColormap(dpy, root, vis, AllocNone);
+ }
+ } else {
+ fprintf(stderr, "%s not supported by %s, using default.\n", *ptr, ldisplay);
+ }
+ break;
+ }
+ }
+ }
+#if 0
+ if (DirectColor == vis->class) {
+ have_pm3d = 0;
+ }
+#endif
+#if 0
+ /* removed this message as it is annoying
+ * when using gnuplot in a pipe (joze) */
+ if (vis->class < (sizeof(visual_name) / sizeof(char **)) - 1) {
+ fprintf(stderr, "Using %s at depth %d.\n", visual_name[vis->class], dep);
+ }
+#endif
+ CmapClear(&default_cmap);
+
+ /* set default of maximal_possible_colors */
+ if (dep > 12) {
+ maximal_possible_colors = 0x200;
+ } else if (dep > 8) {
+ maximal_possible_colors = 0x100;
+ } else {
+ /* will be something like PseudoColor * 8 */
+ maximal_possible_colors = 240; /* leave 16 for line colors */
+ }
+
+ /* check database for maxcolors */
+ db_string = pr_GetR(db, ".maxcolors") ? (char *) value.addr : (char *) 0;
+ if (db_string) {
+ int itmp;
+ if (sscanf(db_string, "%d", &itmp)) {
+ if (itmp <= 0) {
+ fprintf(stderr, "\nmaxcolors must be strictly positive.\n");
+ } else if (itmp > pow((double) 2, (double) dep)) {
+ fprintf(stderr, "\noops, cannot use this many colors on a %d bit deep display.\n", dep);
+ } else {
+ maximal_possible_colors = itmp;
+ }
+ } else {
+ fprintf(stderr, "\nunable to parse '%s' as integer\n", db_string);
+ }
+ }
+
+ /* setting a default for minimal_possible_colors */
+ minimal_possible_colors = maximal_possible_colors / (num_colormaps > 1 ? 2 : 8); /* 0x20 / 30 */
+ /* check database for mincolors */
+ db_string = pr_GetR(db, ".mincolors") ? (char *) value.addr : (char *) 0;
+ if (db_string) {
+ int itmp;
+ if (sscanf(db_string, "%d", &itmp)) {
+ if (itmp <= 0) {
+ fprintf(stderr, "\nmincolors must be strictly positive.\n");
+ } else if (itmp > pow((double) 2, (double) dep)) {
+ fprintf(stderr, "\noops, cannot use this many colors on a %d bit deep display.\n", dep);
+ } else if (itmp > maximal_possible_colors) {
+ fprintf(stderr, "\nmincolors must be <= %d\n", maximal_possible_colors);
+ } else {
+ minimal_possible_colors = itmp;
+ }
+ } else {
+ fprintf(stderr, "\nunable to parse '%s' as integer\n", db_string);
+ }
+ }
+
+ pr_geometry(NULL);
+ pr_encoding(); /* check for global default encoding */
+ pr_font(NULL); /* set current font to default font */
+ pr_color(&default_cmap); /* set colors for default colormap */
+ pr_width();
+ pr_dashes();
+ pr_pointsize();
+ pr_raise();
+ pr_persist();
+ pr_feedback();
+ pr_ctrlq();
+ pr_fastrotate();
+#ifdef EXPORT_SELECTION
+ pr_exportselection();
+#endif
+
+}
+
+/*-----------------------------------------------------------------------------
+ * pr_GetR - get resource from database using "-name" option (if any)
+ *---------------------------------------------------------------------------*/
+
+static char *
+pr_GetR(XrmDatabase xrdb, char *resource)
+{
+ char name[128], class[128], *rc;
+
+ strcpy(name, X_Name);
+ strcat(name, resource);
+ strcpy(class, X_Class);
+ strcat(class, resource);
+ rc = XrmGetResource(xrdb, name, class, type, &value)
+ ? (char *) value.addr : (char *) 0;
+ return (rc);
+}
+
+/*-----------------------------------------------------------------------------
+ * pr_color - determine color values
+ *---------------------------------------------------------------------------*/
+
+static const char color_keys[Ncolors][30] = {
+ "background", "bordercolor", "text", "border", "axis",
+ "line1", "line2", "line3", "line4",
+ "line5", "line6", "line7", "line8"
+};
+static char color_values[Ncolors][30] = {
+ "white", "black", "black", "black", "black",
+ "red", "green", "blue", "magenta",
+ "cyan", "sienna", "orange", "coral"
+};
+static char color_values_rv[Ncolors][30] = {
+ "black", "white", "white", "white", "white",
+ "red", "green", "blue", "magenta",
+ "cyan", "sienna", "orange", "coral"
+};
+static char gray_values[Ncolors][30] = {
+ "black", "white", "white", "gray50", "gray50",
+ "gray100", "gray60", "gray80", "gray40",
+ "gray90", "gray50", "gray70", "gray30"
+};
+
+static void
+pr_color(cmap_t * cmap_ptr)
+{
+ unsigned long black = BlackPixel(dpy, scr), white = WhitePixel(dpy, scr);
+ char option[20], color[30], *v, *ctype;
+ XColor xcolor;
+ double intensity = -1;
+ int n;
+
+ if (pr_GetR(db, ".mono") && On(value.addr))
+ Mono++;
+ if (pr_GetR(db, ".gray") && On(value.addr))
+ Gray++;
+ if (pr_GetR(db, ".reverseVideo") && On(value.addr))
+ Rv++;
+
+ if (!Gray && (vis->class == GrayScale || vis->class == StaticGray))
+ Mono++;
+
+ if (!Mono) {
+
+ ctype = (Gray) ? "Gray" : "Color";
+
+ if (current_cmap != cmap_ptr) {
+ /* for private colormaps: make sure
+ * that pixel 0 gets black (joze) */
+ xcolor.red = 0;
+ xcolor.green = 0;
+ xcolor.blue = 0;
+ XAllocColor(dpy, cmap_ptr->colormap, &xcolor);
+ }
+
+ for (n = 0; n < Ncolors; n++) {
+ strcpy(option, ".");
+ strcat(option, color_keys[n]);
+ if (n > 1)
+ strcat(option, ctype);
+ v = pr_GetR(db, option) ? (char *) value.addr
+ : ((Gray) ? gray_values[n]
+ : (Rv ? color_values_rv[n] : color_values[n]));
+
+ if (sscanf(v, "%30[^, ], %lf", color, &intensity) == 2) {
+ if (intensity < 0 || intensity > 1) {
+ fprintf(stderr, "\ngnuplot: invalid color intensity in '%s'\n", color);
+ intensity = 1;
+ }
+ } else {
+ strcpy(color, v);
+ intensity = 1;
+ }
+
+ if (!XParseColor(dpy, cmap_ptr->colormap, color, &xcolor)) {
+ fprintf(stderr, "\ngnuplot: unable to parse '%s'. Using black.\n", color);
+ cmap_ptr->colors[n] = black;
+ } else {
+ xcolor.red *= intensity;
+ xcolor.green *= intensity;
+ xcolor.blue *= intensity;
+ if (XAllocColor(dpy, cmap_ptr->colormap, &xcolor)) {
+ cmap_ptr->colors[n] = xcolor.pixel;
+ cmap_ptr->rgbcolors[n] = ((xcolor.red>>8 & 0xff) << 16)
+ + ((xcolor.green>>8 & 0xff) << 8)
+ + (xcolor.blue>>8);
+ } else {
+ fprintf(stderr, "\ngnuplot: can't allocate '%s'. Using black.\n", v);
+ cmap_ptr->colors[n] = black;
+ }
+ }
+ }
+ } else {
+ cmap_ptr->colors[0] = (Rv) ? black : white;
+ for (n = 1; n < Ncolors; n++)
+ cmap_ptr->colors[n] = (Rv) ? white : black;
+ }
+#ifdef USE_MOUSE
+ {
+ /* create the xor GC just for allocating the xor value
+ * before a palette is created. This way the xor foreground
+ * will be available. */
+ AllocateXorPixel(cmap_ptr);
+ }
+#endif
+}
+
+/*-----------------------------------------------------------------------------
+ * pr_dashes - determine line dash styles
+ *---------------------------------------------------------------------------*/
+
+static const char dash_keys[Ndashes][10] = {
+ "border", "axis",
+ "line1", "line2", "line3", "line4", "line5", "line6", "line7", "line8"
+};
+
+static char dash_mono[Ndashes][10] = {
+ "0", "16",
+ "0", "42", "13", "44", "15", "4441", "42", "13"
+};
+
+static char dash_color[Ndashes][10] = {
+ "0", "16",
+ "0", "0", "0", "0", "0", "0", "0", "0"
+};
+
+static void
+pr_dashes()
+{
+ int n, j, l, ok;
+ char option[20], *v;
+
+ if (pr_GetR(db, ".dashed")) {
+ dashedlines = (!strncasecmp(value.addr, "on", 2) || !strncasecmp(value.addr, "true", 4));
+ }
+
+ for (n = 0; n < Ndashes; n++) {
+ strcpy(option, ".");
+ strcat(option, dash_keys[n]);
+ strcat(option, "Dashes");
+ v = pr_GetR(db, option)
+ ? (char *) value.addr : ((Mono) ? dash_mono[n] : dash_color[n]);
+ l = strlen(v);
+ if (l == 1 && *v == '0') {
+ dashes[n][0] = (unsigned char) 0;
+ continue;
+ }
+ for (ok = 0, j = 0; j < l; j++) {
+ if (v[j] >= '1' && v[j] <= '9')
+ ok++;
+ }
+ if (ok != l || (ok != 2 && ok != 4)) {
+ fprintf(stderr, "gnuplot: illegal dashes value %s:%s\n",
+ option, v);
+ dashes[n][0] = (unsigned char) 0;
+ continue;
+ }
+ for (j = 0; j < l; j++) {
+ dashes[n][j] = (unsigned char) (v[j] - '0');
+ }
+ dashes[n][l] = (unsigned char) 0;
+ }
+}
+
+/*-----------------------------------------------------------------------------
+ * pr_font - determine font
+ *---------------------------------------------------------------------------*/
+
+/* wrapper functions */
+int gpXTextWidth (XFontStruct *cfont, const char *str, int count)
+{
+#ifdef USE_X11_MULTIBYTE
+ if (usemultibyte)
+ return XmbTextEscapement(mbfont, str, count);
+#endif
+ return XTextWidth(cfont, str, count);
+}
+
+int gpXTextHeight (XFontStruct *cfont)
+{
+#ifdef USE_X11_MULTIBYTE
+ static XFontSetExtents *extents;
+ if (usemultibyte) {
+ extents = XExtentsOfFontSet(mbfont);
+ return extents->max_logical_extent.height;
+ } else
+#endif
+ return cfont->ascent + cfont->descent;
+}
+
+void gpXSetFont (Display *disp, GC gc, Font fontid)
+{
+#ifdef USE_X11_MULTIBYTE
+ if (!usemultibyte)
+#endif
+ XSetFont(disp, gc, fontid);
+}
+
+void gpXDrawImageString (Display *disp, Drawable d, GC gc, int x, int y,
+ const char *str, int len)
+{
+#ifdef USE_X11_MULTIBYTE
+ if (usemultibyte) {
+ XmbDrawImageString(disp, d, mbfont, gc, x, y, str, len);
+ return;
+ }
+#endif
+ XDrawImageString(disp, d, gc, x, y, str, len);
+}
+
+void gpXDrawString (Display *disp, Drawable d, GC gc, int x, int y,
+ const char *str, int len)
+{
+#ifdef USE_X11_MULTIBYTE
+ if (usemultibyte) {
+ XmbDrawString(disp, d, mbfont, gc, x, y, str, len);
+ return;
+ }
+#endif
+ XDrawString(disp, d, gc, x, y, str, len);
+}
+
+void gpXFreeFont(Display *disp, XFontStruct *cfont)
+{
+#ifndef USE_X11_MULTIBYTE
+ if (cfont)
+ XFreeFont(disp, cfont);
+#else
+ if (font) {
+ XFreeFont(disp, font);
+ font=NULL;
+ }
+ if (mbfont) {
+ XFreeFontSet(disp, mbfont);
+ mbfont=NULL;
+ }
+#endif
+}
+
+XFontStruct *gpXLoadQueryFont (Display *disp, char *fontname)
+{
+#ifndef USE_X11_MULTIBYTE
+ return XLoadQueryFont(disp, fontname);
+#else
+ static char **miss, *def;
+ static TBOOLEAN first_time = TRUE;
+ int n_miss;
+ char tmpfname[256];
+
+ if (!usemultibyte)
+ return XLoadQueryFont(disp, fontname);
+ else {
+ fontset_transsep(tmpfname, fontname, 256-1);
+ mbfont = XCreateFontSet(disp, tmpfname, &miss, &n_miss, &def);
+
+ /* This test seemed to make sense for Japanese locales, which only */
+ /* claim to require a small number of character sets. But it is */
+ /* highly likely to fail for more generic locales like en_US.UTF-8 */
+ /* that claim to "require" about 2 dozen obscure character sets. */
+ /* EAM - do not fail the request; just continue after a warning. */
+ if (n_miss>0) {
+#if (0)
+ if (mbfont) {
+ XFreeFontSet(disp, mbfont);
+ mbfont=NULL;
+ }
+#else
+ if (first_time) {
+ fprintf(stderr,"gnuplot_x11: Some character sets not available\n");
+ first_time = FALSE;
+ }
+ while (n_miss-- > 0)
+ FPRINTF((stderr,"Missing charset: %s\n", miss[n_miss]));
+#endif
+ XFreeStringList(miss);
+ }
+
+ return NULL;
+ }
+#endif
+}
+
+char *gpFallbackFont(void)
+{
+#ifdef USE_X11_MULTIBYTE
+ if (usemultibyte)
+ return FallbackFontMB;
+#endif
+ return FallbackFont;
+}
+
+int gpXGetFontascent(XFontStruct *cfont)
+{
+#ifndef USE_X11_MULTIBYTE
+ return cfont->ascent;
+#else
+ static XFontStruct **eachfonts;
+ char **fontnames;
+ int max_ascent = 0;
+ int i, n_fonts;
+
+ if(!usemultibyte) return font->ascent;
+ n_fonts = XFontsOfFontSet(mbfont, &eachfonts, &fontnames);
+ for (i = 0; i < n_fonts; i++) {
+ if (eachfonts[i]->ascent > max_ascent)
+ max_ascent = eachfonts[i]->ascent;
+ }
+ return max_ascent;
+#endif
+}
+
+#ifdef USE_X11_MULTIBYTE
+int fontset_transsep(char *nfname, char *ofname, int n)
+{
+ char *s;
+
+ strncpy(nfname, ofname, n);
+ if (nfname[n-1]!='\0')
+ nfname[n]='\0';
+ if (strchr(nfname, ','))
+ return 1;
+ s = nfname;
+ while ((s = strchr(nfname, FontSetSep)) != NULL){
+ *s = ',';
+ nfname = s;
+ }
+ return 0;
+}
+#endif
+
+static void
+pr_encoding()
+{
+ char *encoding;
+ if ((encoding = pr_GetR(db, ".encoding"))) {
+ strncpy(default_encoding, encoding, sizeof(default_encoding)-1);
+ }
+}
+
+static void
+pr_font( fontname )
+char *fontname;
+{
+ static char previous_font_name[128];
+ char fontspec[128];
+ int fontsize = 0;
+#ifdef USE_X11_MULTIBYTE
+ char *orgfontname = NULL;
+#endif
+
+ if (!fontname || !(*fontname))
+ fontname = default_font;
+
+ if (!fontname || !(*fontname)) {
+ if ((fontname = pr_GetR(db, ".font")))
+ strncpy(default_font, fontname, sizeof(default_font)-1);
+ FPRINTF((stderr, "gnuplot_x11: setting default font %s from Xresource\n",
+ fontname));
+ }
+
+#ifdef USE_X11_MULTIBYTE
+ if (fontname && strncmp(fontname, "mbfont:", 7) == 0) {
+ if (multibyte_fonts_usable) {
+ usemultibyte = 1;
+ orgfontname = fontname;
+ fontname = &fontname[7];
+ } else {
+ usemultibyte=0;
+ fontname=NULL;
+ }
+ } else usemultibyte=0;
+#endif
+ if (!fontname)
+ fontname = gpFallbackFont();
+
+ /* EAM DEBUG - Free previous font before searching for a new one. */
+ /* This doesn't seem right, since we will probably need it again */
+ /* very soon. But if we don't free it, we gradually leak memory. */
+ gpXFreeFont(dpy, font);
+
+ font = gpXLoadQueryFont(dpy, fontname);
+
+#ifndef USE_X11_MULTIBYTE
+ if (!font) {
+#else
+ if (!font && !mbfont && !strchr(fontname, FontSetSep)) {
+#endif
+ /* EAM 19-Aug-2002 Try to construct a plausible X11 full font spec */
+ /* We are passed "font<, size><, slant>" */
+ char shortname[64], *fontencoding, slant, *weight;
+ int sep;
+#ifdef USE_X11_MULTIBYTE
+ int backfont = 0;
+#endif
+
+ /* Enhanced font processing wants a method of requesting a new size */
+ /* for whatever the previously selected font was, so we have to save */
+ /* and reuse the previous font name to construct the new spec. */
+ if (!strncmp(fontname, "DEFAULT", 7)) {
+ sscanf(&fontname[8], "%d", &fontsize);
+ fontname = default_font;
+#ifdef USE_X11_MULTIBYTE
+ backfont = 1;
+#endif
+ } else if (*fontname == ',') {
+ sscanf(&fontname[1], "%d", &fontsize);
+ fontname = previous_font_name;
+#ifdef USE_X11_MULTIBYTE
+ backfont = 1;
+#endif
+ }
+#ifdef USE_X11_MULTIBYTE
+ if (backfont && fontname && strncmp(fontname, "mbfont:", 7) == 0
+ && multibyte_fonts_usable) {
+ usemultibyte = 1;
+ orgfontname = fontname;
+ fontname = &fontname[7];
+ }
+#endif
+
+ sep = strcspn(fontname, ",");
+ if (sep >= sizeof(shortname))
+ sep = sizeof(shortname) - 1;
+ strncpy(shortname, fontname, sep);
+ shortname[sep] = '\0';
+ if (!fontsize)
+ sscanf( &(fontname[sep+1]), "%d", &fontsize);
+ if (fontsize > 99 || fontsize < 1)
+ fontsize = 12;
+
+ slant = strstr(&fontname[sep+1], "italic") ? 'i' :
+ strstr(&fontname[sep+1], "oblique") ? 'o' :
+ 'r' ;
+
+ weight = strstr(&fontname[sep+1], "bold") ? "bold" :
+ strstr(&fontname[sep+1], "medium") ? "medium" :
+ "*" ;
+
+ if (!strncmp("Symbol", shortname, 6) || !strncmp("symbol", shortname, 6))
+ fontencoding = "*-*";
+#ifdef USE_X11_MULTIBYTE
+ else if (usemultibyte)
+ fontencoding = "*-*";
+#endif
+ else
+ fontencoding = (
+ encoding == S_ENC_CP437 ? "dosencoding-cp437" :
+ encoding == S_ENC_CP850 ? "dosencoding-cp850" :
+ encoding == S_ENC_ISO8859_1 ? "iso8859-1" :
+ encoding == S_ENC_ISO8859_2 ? "iso8859-2" :
+ encoding == S_ENC_ISO8859_15 ? "iso8859-15" :
+ encoding == S_ENC_KOI8_R ? "koi8-r" :
+ encoding == S_ENC_KOI8_U ? "koi8-u" :
+ default_encoding[0] ? default_encoding :
+ "*-*" ) ;
+
+ sprintf(fontspec, "-*-%s-%s-%c-*-*-%d-*-*-*-*-*-%s",
+ shortname, weight, slant, fontsize, fontencoding
+ );
+ font = gpXLoadQueryFont(dpy, fontspec);
+
+#ifndef USE_X11_MULTIBYTE
+ if (!font) {
+#else
+ if (!font && !mbfont) {
+#endif
+ /* Try to decode some common PostScript font names */
+ if (!strcmp("Times-Bold", shortname)
+ || !strcmp("times-bold", shortname)) {
+ sprintf(fontspec, "-*-times-bold-r-*-*-%d-*-*-*-*-*-%s",
+ fontsize, fontencoding);
+ } else if (!strcmp("Times-Roman", shortname)
+ || !strcmp("times-roman", shortname)) {
+ sprintf(fontspec, "-*-times-medium-r-*-*-%d-*-*-*-*-*-%s",
+ fontsize, fontencoding);
+ } else if (!strcmp("Times-Italic", shortname)
+ || !strcmp("times-italic", shortname)) {
+ sprintf(fontspec, "-*-times-medium-i-*-*-%d-*-*-*-*-*-%s",
+ fontsize, fontencoding);
+ } else if (!strcmp("Times-BoldItalic", shortname)
+ || !strcmp("times-bolditalic", shortname)) {
+ sprintf(fontspec, "-*-times-bold-i-*-*-%d-*-*-*-*-*-%s",
+ fontsize, fontencoding);
+ } else if (!strcmp("Helvetica-Bold", shortname) ||
+ !strcmp("helvetica-bold", shortname)) {
+ sprintf(fontspec, "-*-helvetica-bold-r-*-*-%d-*-*-*-*-*-%s",
+ fontsize, fontencoding);
+ } else if (!strcmp("Helvetica-Oblique", shortname)
+ || !strcmp("helvetica-oblique", shortname)) {
+ sprintf(fontspec, "-*-helvetica-medium-o-*-*-%d-*-*-*-*-*-%s",
+ fontsize, fontencoding);
+ } else if (!strcmp("Helvetica-BoldOblique", shortname)
+ || !strcmp("helvetica-boldoblique", shortname)) {
+ sprintf(fontspec, "-*-helvetica-bold-o-*-*-%d-*-*-*-*-*-%s",
+ fontsize, fontencoding);
+ } else if (!strcmp("Helvetica-Narrow-Bold", shortname)
+ || !strcmp("helvetica-narrow-bold", shortname)) {
+ sprintf(fontspec, "-*-arial narrow-bold-r-*-*-%d-*-*-*-*-*-%s",
+ fontsize, fontencoding);
+ }
+#ifdef USE_X11_MULTIBYTE
+ /* Japanese standard PostScript font names (adviced from
+ * N.Matsuda). */
+ else if (multibyte_fonts_usable
+ && (!strncmp("Ryumin-Light", shortname,
+ strlen("Ryumin-Light"))
+ || !strncmp("ryumin-light", shortname,
+ strlen("ryumin-light")))) {
+ if (!usemultibyte) {
+ usemultibyte = 1;
+ orgfontname = fontname;
+ }
+ sprintf(fontspec, "-*-mincho-medium-%c-*--%d-*",
+ slant, fontsize);
+ }
+ else if (multibyte_fonts_usable
+ && (!strncmp("GothicBBB-Medium", shortname,
+ strlen("GothicBBB-Medium"))
+ || !strncmp("gothicbbb-medium", shortname,
+ strlen("gothicbbb-medium")))) {
+ if (!usemultibyte) {
+ usemultibyte = 1;
+ orgfontname = fontname;
+ }
+ /* FIXME: Doesn't work on most non-japanese setups, because */
+ /* many purely Western fonts are gothic-bold. */
+ sprintf(fontspec, "-*-gothic-bold-%c-*--%d-*", slant, fontsize);
+ }
+#endif /* USE_X11_MULTIBYTE */
+ font = gpXLoadQueryFont(dpy, fontspec);
+
+#ifdef USE_X11_MULTIBYTE
+ if (usemultibyte && !mbfont) {
+ /* But (mincho|gothic) X fonts are not provided
+ * on some X servers even in Japan
+ */
+ sprintf(fontspec, "*-%s-%c-*--%d-*", weight, slant, fontsize);
+ font = gpXLoadQueryFont(dpy, fontspec);
+ }
+#endif /* USE_X11_MULTIBYTE */
+ }
+
+ }
+
+#ifndef USE_X11_MULTIBYTE
+ if (font) {
+#else
+ if (font || mbfont) {
+ if (usemultibyte && orgfontname)
+ fontname = orgfontname;
+#endif
+ strncpy(previous_font_name, fontname, sizeof(previous_font_name)-1);
+ FPRINTF((stderr, "gnuplot_x11:saving current font name \"%s\"\n", previous_font_name));
+ }
+
+ /* By now we have tried everything we can to honor the specific request. */
+ /* Try some common scaleable fonts before falling back to a last resort */
+ /* fixed font. */
+#ifndef USE_X11_MULTIBYTE
+ if (!font) {
+#else
+ if (!usemultibyte && !font) {
+#endif
+ sprintf(fontspec, "-*-bitstream vera sans-bold-r-*-*-%d-*-*-*-*-*-*-*", fontsize);
+ font = gpXLoadQueryFont(dpy, fontspec);
+ fontname = fontspec;
+ if (!font) {
+ sprintf(fontspec, "-*-arial-medium-r-*-*-%d-*-*-*-*-*-*-*", fontsize);
+ font = gpXLoadQueryFont(dpy, fontspec);
+ }
+ if (!font) {
+ sprintf(fontspec, "-*-helvetica-medium-r-*-*-%d-*-*-*-*-*-*", fontsize);
+ font = gpXLoadQueryFont(dpy, fontspec);
+ }
+ if (!font) {
+ font = gpXLoadQueryFont(dpy, gpFallbackFont());
+ fontname = gpFallbackFont();
+ }
+ if (!font) {
+ fprintf(stderr, "\ngnuplot_x11: can't find usable font - X11 aborted.\n");
+ EXIT(1);
+ }
+ FPRINTF((stderr, "\ngnuplot_x11: requested font not found, using '%s' instead.\n", fontname));
+ }
+#ifdef USE_X11_MULTIBYTE
+ if (usemultibyte && !mbfont) { /* multibyte font setting */
+ font = gpXLoadQueryFont(dpy, gpFallbackFont());
+ if (!mbfont) {
+ usemultibyte=0;
+ font = gpXLoadQueryFont(dpy, gpFallbackFont());
+ if (!font) {
+ fprintf(stderr, "\ngnuplot_x11: can't find usable font - X11 aborted.\n");
+ EXIT(1);
+ }
+ }
+ fontname = gpFallbackFont();
+ }
+#endif /* USE_X11_MULTIBYTE */
+
+ vchar = gpXTextHeight(font);
+ hchar = gpXTextWidth(font, "0123456789", 10) / 10;
+
+ FPRINTF((stderr, "gnuplot_x11: pr_font() set font %s, vchar %d hchar %d\n",
+ fontname, vchar, hchar));
+
+}
+
+/*-----------------------------------------------------------------------------
+ * pr_geometry - determine window geometry
+ *---------------------------------------------------------------------------*/
+
+static void
+pr_geometry(char *instr)
+{
+ char *geometry = (instr != NULL)? instr : pr_GetR(db, ".geometry");
+ int x, y, flags;
+ unsigned int w, h;
+
+ if (geometry) {
+ flags = XParseGeometry(geometry, &x, &y, &w, &h);
+ if (flags & WidthValue)
+ gW = w;
+ if (flags & HeightValue)
+ gH = h;
+ if (flags & (WidthValue | HeightValue))
+ gFlags = (gFlags & ~PSize) | USSize;
+
+ if (flags & XValue)
+ gX = (flags & XNegative) ? x + DisplayWidth(dpy, scr) - gW - BorderWidth * 2 : x;
+
+ if (flags & YValue)
+ gY = (flags & YNegative) ? y + DisplayHeight(dpy, scr) - gH - BorderWidth * 2 : y;
+
+ if (flags & (XValue | YValue))
+ gFlags = (gFlags & ~PPosition) | USPosition;
+ }
+}
+
+/*-----------------------------------------------------------------------------
+ * pr_pointsize - determine size of points for 'points' plotting style
+ *---------------------------------------------------------------------------*/
+
+static void
+pr_pointsize()
+{
+ if (pr_GetR(db, ".pointsize")) {
+ if (sscanf((char *) value.addr, "%lf", &pointsize) == 1) {
+ if (pointsize <= 0 || pointsize > 10) {
+ fprintf(stderr, "\ngnuplot: invalid pointsize '%s'\n", value.addr);
+ pointsize = 1;
+ }
+ } else {
+ fprintf(stderr, "\ngnuplot: invalid pointsize '%s'\n", value.addr);
+ pointsize = 1;
+ }
+ } else {
+ pointsize = 1;
+ }
+}
+
+/*-----------------------------------------------------------------------------
+ * pr_width - determine line width values
+ *---------------------------------------------------------------------------*/
+
+static const char width_keys[Nwidths][30] = {
+ "border", "axis",
+ "line1", "line2", "line3", "line4", "line5", "line6", "line7", "line8"
+};
+
+static void
+pr_width()
+{
+ int n;
+ char option[20], *v;
+
+ for (n = 0; n < Nwidths; n++) {
+ strcpy(option, ".");
+ strcat(option, width_keys[n]);
+ strcat(option, "Width");
+ if ((v = pr_GetR(db, option)) != NULL) {
+ if (*v < '0' || *v > '9' || strlen(v) > 1)
+ fprintf(stderr, "gnuplot: illegal width value %s:%s\n", option, v);
+ else
+ widths[n] = (unsigned int) atoi(v);
+ }
+ }
+}
+
+/*-----------------------------------------------------------------------------
+ * pr_window - create window
+ *---------------------------------------------------------------------------*/
+static void
+ProcessEvents(Window win)
+{
+ XSelectInput(dpy, win, KeyPressMask | KeyReleaseMask
+ | StructureNotifyMask | PointerMotionMask | PointerMotionHintMask
+ | ButtonPressMask | ButtonReleaseMask | ExposureMask | EnterWindowMask);
+ XSync(dpy, 0);
+}
+
+static void
+pr_window(plot_struct *plot)
+{
+ char *title = pr_GetR(db, ".title");
+ static XSizeHints hints;
+ int Tvtwm = 0;
+
+ FPRINTF((stderr, "(pr_window) \n"));
+
+ if (have_pm3d) {
+ XSetWindowAttributes attr;
+ unsigned long mask = CWBackPixel | CWBorderPixel | CWColormap;
+ attr.background_pixel = plot->cmap->colors[0];
+ attr.border_pixel = plot->cmap->colors[1];
+ attr.colormap = plot->cmap->colormap;
+ plot->window = XCreateWindow(dpy, root, plot->x, plot->y, plot->width,
+ plot->height, BorderWidth, dep, InputOutput, vis, mask, &attr);
+ } else
+ plot->window = XCreateSimpleWindow(dpy, root, plot->x, plot->y,
+ plot->width, plot->height, BorderWidth, plot->cmap->colors[1], plot->cmap->colors[0]);
+
+ /* Return if something wrong. */
+ if (!plot->window)
+ return;
+
+ /* ask ICCCM-compliant window manager to tell us when close window
+ * has been chosen, rather than just killing us
+ */
+
+ XChangeProperty(dpy, plot->window, WM_PROTOCOLS, XA_ATOM, 32, PropModeReplace, (unsigned char *) &WM_DELETE_WINDOW, 1);
+
+ if (pr_GetR(db, ".clear") && On(value.addr))
+ Clear++;
+ if (pr_GetR(db, ".tvtwm") && On(value.addr))
+ Tvtwm++;
+
+ if (!Tvtwm) {
+ hints.flags = plot->posn_flags;
+ } else {
+ hints.flags = (plot->posn_flags & ~USPosition) | PPosition; /* ? */
+ }
+ hints.x = gX;
+ hints.y = gY;
+ hints.width = plot->width;
+ hints.height = plot->height;
+
+ XSetNormalHints(dpy, plot->window, &hints);
+
+ if (pr_GetR(db, ".iconic") && On(value.addr)) {
+ XWMHints wmh;
+
+ wmh.flags = StateHint;
+ wmh.initial_state = IconicState;
+ XSetWMHints(dpy, plot->window, &wmh);
+ }
+#if 0 /* 1 clear, 0 do not clear */
+ if (plot->titlestring) {
+ free(plot->titlestring);
+ plot->titlestring = 0;
+ }
+#endif
+
+ ProcessEvents(plot->window);
+
+ /* If title doesn't exist, create one. */
+#if 1
+#define ICON_TEXT "gplt"
+#define TEMP_NUM_LEN 16
+ {
+ /* append the X11 terminal number (if greater than zero) */
+ char numstr[sizeof(ICON_TEXT)+TEMP_NUM_LEN+1]; /* space for text, number and terminating \0 */
+ if (plot->plot_number)
+ sprintf(numstr, "%s%d%c", ICON_TEXT, plot->plot_number, '\0');
+ else
+ sprintf(numstr, "%s%c", ICON_TEXT, '\0');
+ FPRINTF((stderr, "term_number is %d", plot->plot_number));
+ XSetIconName(dpy, plot->window, numstr);
+#undef TEMP_NUM_LEN
+ if (!plot->titlestring) {
+ int orig_len;
+ if (!title) title = X_Class;
+ orig_len = strlen(title);
+ /* memory for text, white space, number and terminating \0 */
+ if ((plot->titlestring = (char *) malloc(orig_len + ((orig_len && plot->plot_number) ? 1 : 0) + strlen(numstr) - strlen(ICON_TEXT) + 1))) {
+ strcpy(plot->titlestring, title);
+ if (orig_len && plot->plot_number)
+ plot->titlestring[orig_len++] = ' ';
+ strcpy(plot->titlestring + orig_len, numstr + strlen(ICON_TEXT));
+ XStoreName(dpy, plot->window, plot->titlestring);
+ } else
+ XStoreName(dpy, plot->window, title);
+ } else {
+ XStoreName(dpy, plot->window, plot->titlestring);
+ }
+ }
+#undef ICON_TEXT
+#endif
+
+ XMapWindow(dpy, plot->window);
+
+ windows_open++;
+}
+
+
+/***** pr_raise ***/
+static void
+pr_raise()
+{
+ if (pr_GetR(db, ".raise"))
+ do_raise = (On(value.addr));
+}
+
+
+static void
+pr_persist()
+{
+ if (pr_GetR(db, ".persist"))
+ persist = (On(value.addr));
+}
+
+static void
+pr_feedback()
+{
+ if (pr_GetR(db, ".feedback"))
+ feedback = !(!strncasecmp(value.addr, "off", 3) || !strncasecmp(value.addr, "false", 5));
+ FPRINTF((stderr, "gplt_x11: set feedback to %d (%s)\n", feedback, value.addr));
+}
+
+static void
+pr_ctrlq()
+{
+ if (pr_GetR(db, ".ctrlq")) {
+ ctrlq = (!strncasecmp(value.addr, "on", 2) || !strncasecmp(value.addr, "true", 4));
+ FPRINTF((stderr, "gplt_x11: require <ctrl>q and <ctrl><space>\n"));
+ }
+}
+
+static void
+pr_fastrotate()
+{
+ if (pr_GetR(db, ".fastrotate")) {
+ fast_rotate = (!strncasecmp(value.addr, "on", 2) || !strncasecmp(value.addr, "true", 4));
+ FPRINTF((stderr, "gplt_x11: Use fast but imperfect text rotation\n"));
+ }
+}
+
+#ifdef EXPORT_SELECTION
+static void
+pr_exportselection()
+{
+ /* Allow export selection to be turned on or off using X resource *exportselection */
+ if (pr_GetR(db, ".exportselection")) {
+ if (!strncmp((char *)value.addr, "off", 3) || !strncmp((char *)value.addr, "false", 5)) {
+ exportselection = FALSE;
+ FPRINTF((stderr, "gnuplot_x11: exportselection is disabled\n"));
+ }
+ }
+}
+#endif
+
+/************ code to handle selection export *********************/
+
+#ifdef EXPORT_SELECTION
+
+/* bit of a bodge, but ... */
+static struct plot_struct *exported_plot;
+static Time export_time;
+
+static void
+export_graph(struct plot_struct *plot)
+{
+ FPRINTF((stderr, "export_graph(0x%x)\n", plot));
+
+ XSetSelectionOwner(dpy, EXPORT_SELECTION, plot->window, CurrentTime);
+ /* to check we have selection, we would have to do a
+ * GetSelectionOwner(), but if it failed, it failed - no big deal
+ */
+ if (! *selection) {
+ exported_plot = plot;
+ export_time = (!plot || !(plot->time)) ? 1 : plot->time;
+ }
+}
+
+static void
+handle_selection_event(XEvent *event)
+{
+ switch (event->type) {
+ case SelectionRequest:
+ {
+ XEvent reply;
+
+ static Atom XA_TARGETS = (Atom) 0;
+ static Atom XA_TIMESTAMP = (Atom) 0;
+
+ if (XA_TARGETS == 0)
+ XA_TARGETS = XInternAtom(dpy, "TARGETS", False);
+ if (XA_TIMESTAMP == 0)
+ XA_TIMESTAMP = XInternAtom(dpy, "TIMESTAMP", False);
+
+ reply.type = SelectionNotify;
+ reply.xselection.send_event = True;
+ reply.xselection.display = event->xselectionrequest.display;
+ reply.xselection.requestor = event->xselectionrequest.requestor;
+ reply.xselection.selection = event->xselectionrequest.selection;
+ reply.xselection.target = event->xselectionrequest.target;
+ reply.xselection.property = event->xselectionrequest.property;
+ reply.xselection.time = event->xselectionrequest.time;
+
+ if (reply.xselection.target == XA_TARGETS) {
+ static Atom targets[] = { XA_PIXMAP, XA_COLORMAP };
+ static Atom mousecoord[] = { XA_STRING };
+
+ FPRINTF((stderr, "Targets request from %d\n", reply.xselection.requestor));
+
+ if (*selection)
+ XChangeProperty(dpy, reply.xselection.requestor,
+ reply.xselection.property, reply.xselection.target,
+ 32, PropModeReplace, (unsigned char *) (mousecoord), 1);
+ else if (exported_plot)
+ XChangeProperty(dpy, reply.xselection.requestor,
+ reply.xselection.property, reply.xselection.target,
+ 32, PropModeReplace, (unsigned char *) targets, 2);
+ } else if (reply.xselection.target == XA_COLORMAP) {
+
+ FPRINTF((stderr, "colormap request from %d\n", reply.xselection.requestor));
+
+ XChangeProperty(dpy, reply.xselection.requestor,
+ reply.xselection.property, reply.xselection.target,
+ 32, PropModeReplace, (unsigned char *) &(default_cmap.colormap), 1);
+ } else if (reply.xselection.target == XA_PIXMAP && exported_plot) {
+
+ FPRINTF((stderr, "pixmap request from %d\n", reply.xselection.requestor));
+
+ XChangeProperty(dpy, reply.xselection.requestor,
+ reply.xselection.property, reply.xselection.target,
+ 32, PropModeReplace, (unsigned char *) &(exported_plot->pixmap), 1);
+ exported_plot = NULL;
+ } else if (reply.xselection.target == XA_TIMESTAMP) {
+
+ FPRINTF((stderr, "timestamp request from %d : %ld\n",
+ reply.xselection.requestor, export_time));
+
+ XChangeProperty(dpy, reply.xselection.requestor,
+ reply.xselection.property, reply.xselection.target,
+ 32, PropModeReplace, (unsigned char *) &(export_time), 1);
+#ifdef PIPE_IPC
+ } else if (reply.xselection.target == XA_STRING && *selection) {
+ FPRINTF((stderr, "XA_STRING request\n"));
+ XChangeProperty(dpy, reply.xselection.requestor,
+ reply.xselection.property, reply.xselection.target,
+ 8, PropModeReplace, (unsigned char *) selection, strlen(selection));
+ *selection = '\0';
+#endif
+ } else {
+ FPRINTF((stderr, "selection request target: %d\n", reply.xselection.target));
+ reply.xselection.property = None;
+ if (!exported_plot && ! *selection)
+ /* We have now satisfied the select request. Say goodbye */
+ XSetSelectionOwner(dpy, EXPORT_SELECTION, None, CurrentTime);
+ }
+
+ XSendEvent(dpy, reply.xselection.requestor, False, 0L, &reply);
+ /* we never block on XNextEvent(), so must flush manually
+ * (took me *ages* to find this out !)
+ */
+
+ XFlush(dpy);
+ }
+ break;
+ }
+}
+
+#endif /* EXPORT_SELECTION */
+
+#if defined(USE_MOUSE) && defined(MOUSE_ALL_WINDOWS)
+/* Convert X-window mouse coordinates to coordinate system of plot axes */
+static void
+mouse_to_coords(plot_struct *plot, XEvent *event,
+ double *x, double *y, double *x2, double *y2)
+{
+ int xx = 4096. * (event->xbutton.x + 0.5)/ plot->width;
+ int yy = 4095. - 4096. * (event->xbutton.y + 0.5)/ plot->gheight;
+
+ FPRINTF((stderr, "gnuplot_x11 %d: mouse at %d %d\t", __LINE__, xx, yy));
+
+ *x = mouse_to_axis(xx, &(plot->axis_scale[FIRST_X_AXIS]));
+ *y = mouse_to_axis(yy, &(plot->axis_scale[FIRST_Y_AXIS]));
+ *x2 = mouse_to_axis(xx, &(plot->axis_scale[SECOND_X_AXIS]));
+ *y2 = mouse_to_axis(yy, &(plot->axis_scale[SECOND_Y_AXIS]));
+
+ FPRINTF((stderr, "mouse x y %10g %10g x2 y2 %10g %10g\n", *x, *y, *x2, *y2 ));
+}
+
+static double
+mouse_to_axis(int mouse_coord, axis_scale_t *axis)
+{
+ double axis_coord;
+
+ if (axis->term_scale == 0.0)
+ return 0.;
+
+ axis_coord = ((double)(mouse_coord - axis->term_lower)) / axis->term_scale + axis->min;
+ if (axis->logbase > 0.0)
+ axis_coord = exp(axis_coord * axis->logbase);
+
+ return axis_coord;
+}
+#endif
+
+
+/*-----------------------------------------------------------------------------
+ * Add_Plot_To_Linked_List - Create space for plot and put in linked list.
+ *---------------------------------------------------------------------------*/
+
+static plot_struct *
+Add_Plot_To_Linked_List(int plot_number)
+{
+ /* Make sure plot does not already exist in the list. */
+ plot_struct *psp = Find_Plot_In_Linked_List_By_Number(plot_number);
+
+ if (psp == NULL) {
+ psp = (plot_struct *) malloc(sizeof(plot_struct));
+ if (psp) {
+ /* Initialize structure variables. */
+ memset((void*)psp, 0, sizeof(plot_struct));
+ psp->plot_number = plot_number;
+ /* Add link to beginning of the list. */
+ psp->prev_plot = NULL;
+ if (plot_list_start != NULL) {
+ plot_list_start->prev_plot = psp;
+ psp->next_plot = plot_list_start;
+ } else
+ psp->next_plot = NULL;
+ plot_list_start = psp;
+ }
+ else {
+ psp = NULL;
+ fprintf(stderr, ERROR_NOTICE("Could not allocate memory for plot.\n\n"));
+ }
+ }
+
+ return psp;
+}
+
+
+/*-----------------------------------------------------------------------------
+ * Remove_Plot_From_Linked_List - Remove from linked list and free memory.
+ *---------------------------------------------------------------------------*/
+
+static void
+Remove_Plot_From_Linked_List(Window plot_window)
+{
+ /* Make sure plot exists in the list. */
+ plot_struct *psp = Find_Plot_In_Linked_List_By_Window(plot_window);
+
+ if (psp != NULL) {
+ /* Remove link from the list. */
+ if (psp->next_plot != NULL)
+ psp->next_plot->prev_plot = psp->prev_plot;
+ if (psp->prev_plot != NULL) {
+ psp->prev_plot->next_plot = psp->next_plot;
+ } else {
+ plot_list_start = psp->next_plot;
+ }
+ /* If global pointers point at this plot, reassign them. */
+ if (current_plot == psp) {
+#if 0 /* Make some other plot current. */
+ if (psp->prev_plot != NULL)
+ current_plot = psp->prev_plot;
+ else
+ current_plot = psp->next_plot;
+#else /* No current plot. */
+ current_plot = NULL;
+#endif
+ }
+ if (plot == psp)
+ plot = current_plot;
+ /* Deallocate memory. Make sure plot removed from list first. */
+ delete_plot(psp);
+ free(psp);
+ }
+}
+
+
+/*-----------------------------------------------------------------------------
+ * Find_Plot_In_Linked_List_By_Number - Search for the plot in the linked list.
+ *---------------------------------------------------------------------------*/
+
+static plot_struct *
+Find_Plot_In_Linked_List_By_Number(int plot_number)
+{
+ plot_struct *psp = plot_list_start;
+
+ while (psp != NULL) {
+ if (psp->plot_number == plot_number)
+ break;
+ psp = psp->next_plot;
+ }
+
+ return psp;
+}
+
+
+/*-----------------------------------------------------------------------------
+ * Find_Plot_In_Linked_List_By_Window - Search for the plot in the linked list.
+ *---------------------------------------------------------------------------*/
+
+static plot_struct *
+Find_Plot_In_Linked_List_By_Window(Window window)
+{
+ plot_struct *psp = plot_list_start;
+
+ while (psp != NULL) {
+ if (psp->window == window)
+ break;
+ psp = psp->next_plot;
+ }
+
+ return psp;
+}
+
+
+/*-----------------------------------------------------------------------------
+ * Find_Plot_In_Linked_List_By_CMap - Search for the plot in the linked list.
+ *---------------------------------------------------------------------------*/
+
+static plot_struct *
+Find_Plot_In_Linked_List_By_CMap(cmap_t *cmp)
+{
+ plot_struct *psp = plot_list_start;
+
+ while (psp != NULL) {
+ if (psp->cmap == cmp)
+ break;
+ psp = psp->next_plot;
+ }
+
+ return psp;
+}
+
+
+/* NOTE: The removing of plots via the ErrorHandler routine is rather
+ tricky. The error events can happen at any time during execution of
+ the program, very similar to an interrupt. The consequence is that
+ the error handling routine can't remove plots from the linked list
+ directly. Instead we use a queuing system in which the main code
+ eventually removes the plots.
+
+ Furthermore, to be safe, only the error handling routine should create
+ and delete elements in the FIFO. Otherwise, the possibility of bogus
+ pointers can arise if error events happen at the exact wrong time.
+ (Requires a lot of thought.)
+
+ The scheme here is for the error handler to put elements in the
+ queue marked as "processed = 0" and then indicate that the main
+ code should process elements in the queue. The main code then
+ copies the information about the plot to remove and sets the value
+ "processed = 1". Afterward the main code removes the plot.
+*/
+
+/*-----------------------------------------------------------------------------
+ * Add_Plot_To_Remove_FIFO_Queue - Method for error handler to destroy plot.
+ *---------------------------------------------------------------------------*/
+
+static void
+Add_Plot_To_Remove_FIFO_Queue(Window plot_window)
+{
+ /* Clean up any processed links. */
+ plot_remove_struct *prsp = remove_fifo_queue_start;
+ FPRINTF((stderr, "Add plot to remove FIFO queue called.\n"));
+ while (prsp != NULL) {
+ if (prsp->processed) {
+ remove_fifo_queue_start = prsp->next_remove;
+ free(prsp);
+ prsp = remove_fifo_queue_start;
+ FPRINTF((stderr, " -> Removed a processed element from FIFO queue.\n"));
+ } else {
+ break;
+ }
+ }
+
+ /* Go to end of list while checking if this window is already in list. */
+ while (prsp != NULL) {
+ if (prsp->plot_window_to_remove == plot_window) {
+ /* Discard this request because the same window is yet to be processed.
+ X11 could be stuck sending the same error message again and again
+ while the main program is not responding for some reason. This would
+ lead to the FIFO queue growing indefinitely. */
+ return;
+ }
+ if (prsp->next_remove == NULL)
+ break;
+ else
+ prsp = prsp->next_remove;
+ }
+
+ /* Create link and add to end of queue. */
+ {plot_remove_struct *prsp_new = (plot_remove_struct *) malloc(sizeof(plot_remove_struct));
+ if (prsp_new) {
+ /* Initialize structure variables. */
+ prsp_new->next_remove = NULL;
+ prsp_new->plot_window_to_remove = plot_window;
+ prsp_new->processed = 0;
+ if (remove_fifo_queue_start)
+ prsp->next_remove = prsp_new;
+ else
+ remove_fifo_queue_start = prsp_new;
+ process_remove_fifo_queue = 1; /* Indicate to main loop that there is a plot to remove. */
+ FPRINTF((stderr, " -> Added an element to FIFO queue.\n"));
+ }
+ else {
+ fprintf(stderr, ERROR_NOTICE("Could not allocate memory for plot remove queue.\n\n"));
+ }}
+}
+
+
+/*-----------------------------------------------------------------------------
+ * Process_Remove_FIFO_Queue - Remove plots queued by error handler.
+ *---------------------------------------------------------------------------*/
+
+static void
+Process_Remove_FIFO_Queue()
+{
+ plot_remove_struct *prsp = remove_fifo_queue_start;
+
+ /* Clear flag before processing so that if an ErrorHandler event
+ * comes along while running the remainder of this routine, any new
+ * error events that were missed because of timing issues will be
+ * processed next time through the main loop.
+ */
+ process_remove_fifo_queue = 0;
+
+ /* Go through the list and process any unprocessed queue request.
+ * No clean up is done here because having two asynchronous routines
+ * modifying the queue would be too dodgy. The ErrorHandler creates
+ * and removes links in the queue based upon the processed flag.
+ */
+ while (prsp != NULL) {
+
+ /* Make a copy of the remove information structure before changing flag.
+ * Otherwise, there would be the possibility of the error handler routine
+ * removing the associated link upon seeing the "processed" flag set. From
+ * this side of things, the pointer becomes invalid once that flag is set.
+ */
+ plot_remove_struct prs;
+ prs.plot_window_to_remove = prsp->plot_window_to_remove;
+ prs.next_remove = prsp->next_remove;
+ prs.processed = prsp->processed;
+
+ /* Set processed flag before processing the event. This is so
+ * that the FIFO queue does not have to repeat window entries.
+ * If the error handler were to break in right before the
+ * "processed" flag is set to 1 and not put another link in
+ * the FIFO because it sees the window in question is already
+ * in the FIFO, we're OK. The reason is that the window in
+ * question is still to be processed. On the other hand, if
+ * we were to process then set the flag, an entry in the queue
+ * could potentially be lost.
+ */
+ prsp->processed = 1;
+ FPRINTF((stderr, "Processed element in remove FIFO queue.\n"));
+
+ /* NOW process the plot to remove. */
+ if (!prs.processed)
+ Remove_Plot_From_Linked_List(prs.plot_window_to_remove);
+
+ prsp = prs.next_remove;
+ }
+
+ /* Issue an X11 error so that error handler cleans up queue?
+ * Really, this isn't super important. Without issuing a bogus
+ * error, the processed queue elements will be deleted the
+ * next time there is an error. Until another error comes
+ * along it means there is maybe ten or so words of memory
+ * reserved on the heap for the FIFO queue. In some sense,
+ * the extra code is hardly worth the effort, especially
+ * when X11 documentation is so sparse on the matter of errors.
+ */
+
+}
+
+
+#ifdef WITH_IMAGE
+/* Extract details about the extent of a bit mask by doing a
+ * single bit shift up and then down (left shift) and down
+ * and then up (right shift). When the pre- and post-shift
+ * numbers are not equal, a bit was lost so that is the
+ * extent of the mask.
+ */
+static unsigned short
+BitMaskDetails(unsigned long mask, unsigned short *left_shift, unsigned short *right_shift)
+{
+ unsigned short i;
+ unsigned long m = mask;
+
+ if (mask == 0) {
+ *left_shift = 0;
+ *right_shift = 0;
+ return 0;
+ }
+
+ for (i=0; i < 32; i++) {
+ if ( (((m << 1)&0xffffffff) >> 1) != m )
+ break;
+ else
+ m <<= 1;
+ }
+ *left_shift = i;
+
+ m = mask;
+ for (i=0; i < 32 ; i++) {
+ if ( ((m >> 1) << 1) != m )
+ break;
+ else
+ m >>= 1;
+ }
+ *right_shift = i;
+
+ return (unsigned short) m;
+}
+#endif
+
+
+/*-----------------------------------------------------------------------------
+ * Add_CMap_To_Linked_List - Create space for colormap and put in linked list.
+ *---------------------------------------------------------------------------*/
+
+static cmap_t *
+Add_CMap_To_Linked_List(void)
+{
+ cmap_t *cmp = (cmap_t *) malloc(sizeof(cmap_t));
+ if (cmp) {
+ /* Add link to beginning of the list. */
+ cmp->prev_cmap = NULL;
+ if (cmap_list_start != NULL) {
+ cmap_list_start->prev_cmap = cmp;
+ cmp->next_cmap = cmap_list_start;
+ } else
+ cmp->next_cmap = NULL;
+ cmap_list_start = cmp;
+ } else {
+ cmp = NULL;
+ fprintf(stderr, ERROR_NOTICE("Could not allocate memory for color map.\n\n"));
+ }
+ /* Initialize structure variables. */
+ CmapClear(cmp);
+ cmp->colormap = XCreateColormap(dpy, root, vis, AllocNone);
+ assert(cmp->colormap);
+ pr_color(cmp); /* set default colors for lines */
+ return cmp;
+}
+
+
+/*-----------------------------------------------------------------------------
+ * Remove_CMap_From_Linked_List - Remove from linked list and free memory.
+ *---------------------------------------------------------------------------*/
+
+static void
+Remove_CMap_From_Linked_List(cmap_t *cmp)
+{
+ /* Make sure colormap exists in the list. */
+ cmp = Find_CMap_In_Linked_List(cmp);
+
+ if (cmp != NULL) {
+ /* Remove link from the list. */
+ if (cmp->next_cmap != NULL)
+ cmp->next_cmap->prev_cmap = cmp->prev_cmap;
+ if (cmp->prev_cmap != NULL) {
+ cmp->prev_cmap->next_cmap = cmp->next_cmap;
+ } else {
+ cmap_list_start = cmp->next_cmap;
+ }
+ /* If global pointers point at this plot, reassign them. */
+ if (current_cmap == cmp) {
+#if 0 /* Make some other cmap current. */
+ if (cmp->prev_cmap != NULL)
+ current_cmap = cmp->prev_cmap;
+ else
+ current_cmap = psp->next_cmap;
+#else /* No current cmap. */
+ current_cmap = &default_cmap;
+#endif
+ }
+ /* Remove any memory for pixels and memory for colormap structure. */
+ ReleaseColormap(cmp);
+ }
+}
+
+
+/*-----------------------------------------------------------------------------
+ * Find_CMap_In_Linked_List - Search for the color map in the linked list.
+ *---------------------------------------------------------------------------*/
+
+static cmap_t *
+Find_CMap_In_Linked_List(cmap_t *colormap)
+{
+ cmap_t *cmp = cmap_list_start;
+
+ while (cmp != NULL) {
+ if (cmp == colormap)
+ break;
+ cmp = cmp->next_cmap;
+ }
+
+ return cmp;
+}
+
+
+/*-----------------------------------------------------------------------------
+ * cmaps_differ - Compare two colormaps, return 1 if differ.
+ *---------------------------------------------------------------------------*/
+
+static int
+cmaps_differ(cmap_t *cmap1, cmap_t *cmap2)
+{
+
+ /* First compare non-pointer elements. */
+ if ( memcmp(&(cmap1->colors[0]), &(cmap2->colors[0]), (long)&(cmap1->pixels)-(long)&(cmap1->colors[0])) )
+ return 1;
+
+ /* Now compare pointer elements. */
+ if (cmap1->allocated) {
+ if (cmap1->pixels && cmap2->pixels) {
+ if ( memcmp(cmap1->pixels, cmap2->pixels, cmap1->allocated*sizeof(cmap1->pixels[0])) )
+ return 1;
+ } else
+ return 1;
+ }
+
+ return 0; /* They are the same. */
+
+}
+
+/*
+ * Shared code for setting fill style
+ */
+static void
+x11_setfill(GC *gc, int style, TBOOLEAN poly)
+{
+ int fillpar, idx;
+ XColor xcolor, bgnd;
+ float dim;
+
+ /* upper 3 nibbles contain fillparameter (ULIG) */
+ fillpar = style >> 4;
+
+ /* lower nibble contains fillstyle */
+ switch (style & 0xf) {
+ case FS_SOLID:
+ /* filldensity is from 0..100 percent */
+ if (fillpar >= 100)
+ break;
+ dim = (double)(fillpar)/100.;
+ /* retrieve current rgb color and shift it towards the background color */
+ xcolor.red = (double)(0xffff) * (double)((plot->current_rgb >> 16) & 0xff) /255.;
+ xcolor.green = (double)(0xffff) * (double)((plot->current_rgb >> 8) & 0xff) /255.;
+ xcolor.blue = (double)(0xffff) * (double)(plot->current_rgb & 0xff) /255.;
+ bgnd.red = (double)(0xffff) * (double)((plot->cmap->rgbcolors[0] >> 16) & 0xff) /255.;
+ bgnd.green = (double)(0xffff) * (double)((plot->cmap->rgbcolors[0] >> 8) & 0xff) /255.;
+ bgnd.blue = (double)(0xffff) * (double)(plot->cmap->rgbcolors[0] & 0xff) /255.;
+ xcolor.red = dim*xcolor.red + (1.-dim)*bgnd.red;
+ xcolor.green = dim*xcolor.green + (1.-dim)*bgnd.green;
+ xcolor.blue = dim*xcolor.blue + (1.-dim)*bgnd.blue;
+ if (XAllocColor(dpy, plot->cmap->colormap, &xcolor))
+ XSetForeground(dpy, *gc, xcolor.pixel);
+ break;
+ case FS_PATTERN:
+ /* use fill pattern according to fillpattern */
+ idx = (int) fillpar; /* fillpattern is enumerated */
+ if (idx < 0)
+ idx = 0;
+ idx = idx % stipple_pattern_num;
+ XSetStipple(dpy, *gc, stipple_pattern[idx]);
+ XSetFillStyle(dpy, *gc, FillOpaqueStippled);
+ if (poly)
+ XSetBackground(dpy, *gc, plot->cmap->colors[0]);
+ else
+ XSetForeground(dpy, *gc, plot->cmap->colors[plot->lt + 3]);
+ break;
+ case FS_EMPTY: /* fill with background color */
+ XSetFillStyle(dpy, *gc, FillSolid);
+ XSetForeground(dpy, *gc, plot->cmap->colors[0]);
+ break;
+ default:
+ XSetFillStyle(dpy, *gc, FillSolid);
+ if (!poly)
+ XSetForeground(dpy, *gc, plot->cmap->colors[0]);
+ }
+}