#ifndef lint
static char *RCSid = "$Id: gnuplot_x11.c,v 3.26 92/03/24 22:35:52 woo Exp Locker: woo $";
#endif
/*-----------------------------------------------------------------------------
* gnuplot_x11 - X11 outboard terminal driver for gnuplot 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)
* O'Reilly & Associates: X Window System - Volumes 1 & 2
*
* This code is provided as is and with no warranties of any kind.
*
* Ed Kubaitis (ejk@uiuc.edu)
* Computing & Communications Services Office
* University of Illinois, Urbana
*---------------------------------------------------------------------------*/
#include <X11/Xos.h>
#include <X11/Xlib.h>
#include <X11/Xutil.h>
#include <X11/Xatom.h>
#include <X11/Xresource.h>
#include <stdio.h>
#include <signal.h>
#ifndef FD_SET
#ifndef OLD_SELECT
#include <sys/select.h>
#else /* OLD_SELECT */
#define FD_SET(n, p) ((p)->fds_bits[0] |= (1 << ((n) % 32)))
#define FD_CLR(n, p) ((p)->fds_bits[0] &= ~(1 << ((n) % 32)))
#define FD_ISSET(n, p) ((p)->fds_bits[0] & (1 << ((n) % 32)))
#define FD_ZERO(p) bzero((char *)(p), sizeof(*(p)))
#endif /* OLD_SELECT */
#endif /* FD_SET */
#include <errno.h>
extern int errno;
#define FallbackFont "fixed"
#define Ncolors 13
unsigned long colors[Ncolors];
char dashes[10][5] = { {0}, {1,6,0},
{0}, {4,2,0}, {1,3,0}, {4,4,0}, {1,5,0}, {4,4,4,1,0}, {4,2,0}, {1,3,0}
};
Display *dpy; int scr; Window win, root;
Visual *vis; GC gc = (GC)0; Pixmap pixmap; XFontStruct *font;
unsigned int W = 640, H = 450; int D, gX = 100, gY = 100;
Bool Mono = 0, Gray = 0, Rv = 0, Clear = 0;
char Name[64] = "gnuplot";
char Class[64] = "Gnuplot";
int cx=0, cy=0, vchar, nc = 0, ncalloc = 0;
double xscale, yscale;
#define X(x) (int) (x * xscale)
#define Y(y) (int) ((4095-y) * yscale)
enum JUSTIFY { LEFT, CENTRE, RIGHT } jmode;
#define Nbuf 1024
char buf[Nbuf], **commands = (char **)0;
FILE *X11_ipc = stdin;
char X11_ipcpath[32];
/*-----------------------------------------------------------------------------
* main program
*---------------------------------------------------------------------------*/
main(argc, argv) int argc; char *argv[]; {
preset(argc, argv);
mainloop();
exit(0);
}
/*-----------------------------------------------------------------------------
* mainloop - process X events and input from gnuplot
*
* On systems with a fully implemented select(), select is used (without
* timeout) to sense both input from the X server network connection and
* pipe input from gnuplot. On platforms with an incomplete or faulty
* select(), select (with timeout) is used for the server, and a temporary
* file rather than a pipe is used for gnuplot input.
*---------------------------------------------------------------------------*/
mainloop() {
int nf, nfds, cn = ConnectionNumber(dpy), in = fileno(X11_ipc);
struct timeval timeout, *timer = (struct timeval *)0;
fd_set rset, tset;
unsigned long all = 0xffffffff;
XEvent xe;
FD_ZERO(&rset);
FD_SET(cn, &rset);
#ifndef CRIPPLED_SELECT
FD_SET(in, &rset);
nfds = (cn > in) ? cn + 1 : in + 1;
#else /* CRIPPLED_SELECT */
timeout.tv_sec = 1;
timeout.tv_usec = 0;
timer = &timeout;
sprintf(X11_ipcpath, "/tmp/Gnuplot_%d", getppid());
nfds = cn + 1;
#endif /* CRIPPLED_SELECT */
while(1) {
tset = rset;
nf = select(nfds, &tset, (fd_set *)0, (fd_set *)0, timer);
if (nf < 0) {
if (errno == EINTR) continue;
fprintf(stderr, "gnuplot: select failed. errno:%d\n", errno);
exit(1);
}
nf > 0 && XNoOp(dpy);
if (FD_ISSET(cn, &tset)) {
while (XCheckMaskEvent(dpy, all, &xe)) {
(xe.type == ConfigureNotify) && resize(&xe);
}
}
#ifndef CRIPPLED_SELECT
FD_ISSET(in, &tset) && accept();
#else /* CRIPPLED_SELECT */
if ((X11_ipc = fopen(X11_ipcpath, "r"))) {
unlink(X11_ipcpath);
accept();
fclose(X11_ipc);
}
#endif /* CRIPPLED_SELECT */
}
}
/*-----------------------------------------------------------------------------
* accept - accept & record new plot from gnuplot inboard X11 driver
*---------------------------------------------------------------------------*/
accept() {
while (fgets(buf, Nbuf, X11_ipc)) {
if (*buf == 'G') { /* enter graphics mode */
if (commands) {
int n; for (n=0; n<nc; n++) free(commands[n]);
free(commands);
}
commands = (char **)0; nc = ncalloc = 0;
}
else if (*buf == 'E') { display(); break; } /* leave graphics mode */
else if (*buf == 'R') { exit(0); } /* leave X11/x11 mode */
else { /* record command */
char *p;
if (nc >= ncalloc) {
ncalloc = ncalloc*2 + 1;
commands = (commands)
? (char **)realloc(commands, ncalloc * sizeof(char *))
: (char **)malloc(sizeof(char *));
}
p = (char *)malloc((unsigned)strlen(buf)+1);
if (!commands || !p) {
fprintf(stderr, "gnuplot: can't get memory. X11 aborted.\n");
exit(1);
}
commands[nc++] = strcpy(p, buf);
}
}
if (feof(X11_ipc) || ferror(X11_ipc)) exit(1);
}
/*-----------------------------------------------------------------------------
* display - display last plot from gnuplot inboard X11 driver
*---------------------------------------------------------------------------*/
display() {
int n, x, y, sw, sl, lt, width, type;
char *buf, *str;
if (!nc) return;
/* set scaling factor between internal driver & window geometry */
xscale = (double)W / 4096.; yscale = (double)H / 4096.;
/* create new pixmap & GC */
if (gc) { XFreeGC(dpy, gc); XFreePixmap(dpy, pixmap); }
pixmap = XCreatePixmap(dpy, root, W, H, D);
gc = XCreateGC(dpy, pixmap, 0, (XGCValues *)0);
XSetFont(dpy, gc, font->fid);
/* set pixmap background */
XSetForeground(dpy, gc, colors[0]);
XFillRectangle(dpy, pixmap, gc, 0, 0, W, H);
XSetBackground(dpy, gc, colors[0]);
/* set new pixmap as window background */
XSetWindowBackgroundPixmap(dpy, win, pixmap);
/* momentarily clear the window first if requested */
if (Clear) {
XClearWindow(dpy, win);
XFlush(dpy);
}
/* loop over accumulated commands from inboard driver */
for (n=0; n<nc; n++) {
buf = commands[n];
/* X11_vector(x,y) - draw vector */
if (*buf == 'V') {
sscanf(buf, "V%4d%4d", &x, &y);
XDrawLine(dpy, pixmap, gc, X(cx), Y(cy), X(x), Y(y));
cx = x; cy = y;
}
/* X11_move(x,y) - move */
else if (*buf == 'M')
sscanf(buf, "M%4d%4d", &cx, &cy);
/* X11_put_text(x,y,str) - draw text */
else if (*buf == 'T') {
sscanf(buf, "T%4d%4d", &x, &y);
str = buf + 9; sl = strlen(str) - 1;
sw = XTextWidth(font, str, sl);
switch(jmode) {
case LEFT: sw = 0; break;
case CENTRE: sw = -sw/2; break;
case RIGHT: sw = -sw; break;
}
XSetForeground(dpy, gc, colors[2]);
XDrawString(dpy, pixmap, gc, X(x)+sw, Y(y)+vchar/3, str, sl);
XSetForeground(dpy, gc, colors[lt+3]);
}
/* X11_justify_text(mode) - set text justification mode */
else if (*buf == 'J')
sscanf(buf, "J%4d", &jmode);
/* X11_linetype(type) - set line type */
else if (*buf == 'L') {
sscanf(buf, "L%4d", <);
lt = (lt%8)+2;
width = (lt == 0) ? 2 : 0;
if (!Mono) {
if (lt != 1)
type = LineSolid;
else {
type = LineOnOffDash;
XSetDashes(dpy, gc, 0, dashes[lt], strlen(dashes[lt]));
}
XSetForeground(dpy, gc, colors[lt+3]);
}
else {
type = (lt == 0 || lt == 2) ? LineSolid : LineOnOffDash;
if (dashes[lt][0])
XSetDashes(dpy, gc, 0, dashes[lt], strlen(dashes[lt]));
}
XSetLineAttributes( dpy,gc, width, type, CapButt, JoinBevel);
}
}
/* trigger exposure of background pixmap */
XClearWindow(dpy,win);
XFlush(dpy);
}
/*-----------------------------------------------------------------------------
* resize - rescale last plot if window resized
*---------------------------------------------------------------------------*/
Bool init = True;
resize(xce) XConfigureEvent *xce; {
if (!init || xce->width != W || xce->height != H) {
W = xce->width; H = xce->height;
display();
init = True;
}
}
/*-----------------------------------------------------------------------------
* preset - determine options, open display, create window
*---------------------------------------------------------------------------*/
#define On(v) ( !strcmp(v,"on") || !strcmp(v,"true") || \
!strcmp(v,"On") || !strcmp(v,"True") )
#define AppDefDir "/usr/lib/X11/app-defaults"
#ifndef MAXHOSTNAMELEN
#define MAXHOSTNAMELEN 64
#endif
static XrmDatabase dbCmd, dbApp, dbDef, dbEnv, db = (XrmDatabase)0;
char *pr_GetR(), *getenv(), *type[20];
XrmValue value;
#define Nopt 25
static XrmOptionDescRec options[] = {
{"-mono", ".mono", XrmoptionNoArg, "on" },
{"-gray", ".gray", XrmoptionNoArg, "on" },
{"-clear", ".clear", XrmoptionNoArg, "on" },
{"-display", ".display", XrmoptionSepArg, NULL },
{"-name", ".name", XrmoptionSepArg, NULL },
{"-geometry", "*geometry", XrmoptionSepArg, NULL },
{"-background", "*background", XrmoptionSepArg, NULL },
{"-bg", "*background", XrmoptionSepArg, NULL },
{"-foreground", "*foreground", XrmoptionSepArg, NULL },
{"-fg", "*foreground", XrmoptionSepArg, NULL },
{"-bordercolor", "*bordercolor", XrmoptionSepArg, NULL },
{"-bd", "*bordercolor", XrmoptionSepArg, NULL },
{"-borderwidth", ".borderwidth", XrmoptionSepArg, NULL },
{"-bw", ".borderwidth", XrmoptionSepArg, NULL },
{"-font", "*font", XrmoptionSepArg, NULL },
{"-fn", "*font", XrmoptionSepArg, NULL },
{"-reverse", "*reverseVideo", XrmoptionNoArg, "on" },
{"-rv", "*reverseVideo", XrmoptionNoArg, "on" },
{"+rv", "*reverseVideo", XrmoptionNoArg, "off"},
{"-iconic", "*iconic", XrmoptionNoArg, "on" },
{"-synchronous", "*synchronous", XrmoptionNoArg, "on" },
{"-xnllanguage", "*xnllanguage", XrmoptionSepArg, NULL },
{"-selectionTimeout", "*selectionTimeout", XrmoptionSepArg, NULL },
{"-title", ".title", XrmoptionSepArg, NULL },
{"-xrm", NULL, XrmoptionResArg, NULL },
};
preset(argc, argv) int argc; char *argv[]; {
int Argc = argc; char **Argv = argv;
char *display = getenv("DISPLAY"), *home = getenv("HOME");
char *server_defaults, *env, buf[256];
/*---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(Name, Argv[1], 64);
strncpy(Class, Argv[1], 64);
if (Class[0] >= 'a' && Class[0] <= 'z') Class[0] -= 0x20;
}
}
Argc = argc; Argv = argv;
/*---parse command line---------------------------------------------------*/
XrmInitialize();
XrmParseCommand(&dbCmd, options, Nopt, Name, &Argc, Argv);
if (Argc > 1) {
fprintf(stderr, "\ngnuplot: bad option: %s\n", Argv[1]);
fprintf(stderr, "gnuplot: X11 aborted.\n");
exit(1);
}
if (pr_GetR(dbCmd, ".display")) display = value.addr;
/*---open display---------------------------------------------------------*/
dpy = XOpenDisplay(display);
if (!dpy) {
fprintf(stderr, "\ngnuplot: unable to open display '%s'\n", display);
fprintf(stderr, "gnuplot: X11 aborted.\n");
exit(1);
}
scr = DefaultScreen(dpy);
vis = DefaultVisual(dpy,scr);
D = DefaultDepth(dpy,scr);
root = DefaultRootWindow(dpy);
server_defaults = XResourceManagerString(dpy);
/*---get application defaults--(subset of Xt processing)------------------*/
sprintf(buf, "%s/%s", AppDefDir, "Gnuplot");
dbApp = XrmGetFileDatabase(buf);
XrmMergeDatabases(dbApp, &db);
/*---get server or ~/.Xdefaults-------------------------------------------*/
if (server_defaults)
dbDef = XrmGetStringDatabase(server_defaults);
else {
sprintf(buf, "%s/.Xdefaults", home);
dbDef = XrmGetFileDatabase(buf);
}
XrmMergeDatabases(dbDef, &db);
/*---get XENVIRONMENT or ~/.Xdefaults-hostname---------------------------*/
if (env = getenv("XENVIRONMENT"))
dbEnv = XrmGetFileDatabase(env);
else {
char *p, host[MAXHOSTNAMELEN];
if (gethostname(host, MAXHOSTNAMELEN) < 0) {
fprintf(stderr, "gnuplot: gethostname failed. X11 aborted.\n");
exit(1);
}
if (p = index(host, '.')) *p = '\0';
sprintf(buf, "%s/.Xdefaults-%s", home, host);
dbEnv = XrmGetFileDatabase(buf);
}
XrmMergeDatabases(dbEnv, &db);
/*---merge command line options-------------------------------------------*/
XrmMergeDatabases(dbCmd, &db);
/*---determine geometry, font and colors----------------------------------*/
pr_geometry();
pr_font();
pr_color();
/*---create window--------------------------------------------------------*/
pr_window();
}
/*-----------------------------------------------------------------------------
* pr_GetR - get resource from database using "-name" option (if any)
*---------------------------------------------------------------------------*/
char *
pr_GetR(db, resource) XrmDatabase db; char *resource; {
char name[128], class[128], *rc;
strcpy(name, Name); strcat(name, resource);
strcpy(class, Class); strcat(class, resource);
rc = XrmGetResource(db, name, class, type, &value)
? (char *)value.addr
: (char *)0;
return(rc);
}
/*-----------------------------------------------------------------------------
* pr_color - determine color values
*---------------------------------------------------------------------------*/
char color_keys[Ncolors][30] = {
"background", "bordercolor", "text", "border", "axis",
"line1", "line2", "line3", "line4",
"line5", "line6", "line7", "line8"
};
char color_values[Ncolors][30] = {
"white", "black", "black", "black", "black",
"red", "green", "blue", "magenta",
"cyan", "sienna", "orange", "coral"
};
char gray_values[Ncolors][30] = {
"black", "white", "white", "gray50", "gray50",
"gray100", "gray60", "gray80", "gray40",
"gray90", "gray50", "gray70", "gray30"
};
pr_color() {
unsigned long black = BlackPixel(dpy, scr), white = WhitePixel(dpy,scr);
char option[20], *v, *type = (Gray) ? "Gray" : "Color";
XColor used, exact;
Colormap cmap;
int n;
pr_GetR(db, ".mono") && On(value.addr) && Mono++;
pr_GetR(db, ".gray") && On(value.addr) && Gray++;
pr_GetR(db, ".reverseVideo") && On(value.addr) && Rv++;
if (!Gray && (vis->class == GrayScale || vis->class == StaticGray)) Mono++;
if (!Mono) {
cmap = DefaultColormap(dpy, scr);
for (n=0; n<Ncolors; n++) {
strcpy(option, ".");
strcat(option, color_keys[n]);
(n > 1) && strcat(option, type);
v = pr_GetR(db, option)
? value.addr
: ((Gray) ? gray_values[n] : color_values[n]);
if (XAllocNamedColor(dpy, cmap, v, &used, &exact))
colors[n] = used.pixel;
else {
fprintf(stderr, "\ngnuplot: can't allocate %s:%s\n", option, v);
fprintf(stderr, "gnuplot: reverting to monochrome\n");
Mono++; break;
}
}
}
if (Mono) {
colors[0] = (Rv) ? black : white ;
for (n=1; n<Ncolors; n++) colors[n] = (Rv) ? white : black;
}
}
/*-----------------------------------------------------------------------------
* pr_font - determine font
*---------------------------------------------------------------------------*/
pr_font() {
char *fontname = pr_GetR(db, ".font");
if (!fontname) fontname = FallbackFont;
font = XLoadQueryFont(dpy, fontname);
if (!font) {
fprintf(stderr, "\ngnuplot: can't load font '%s'\n", fontname);
fprintf(stderr, "gnuplot: using font '%s' instead.\n", FallbackFont);
font = XLoadQueryFont(dpy, FallbackFont);
if (!font) {
fprintf(stderr, "gnuplot: can't load font '%s'\n", FallbackFont);
fprintf(stderr, "gnuplot: no useable font - X11 aborted.\n");
exit(1);
}
}
vchar = font->ascent + font->descent;
}
/*-----------------------------------------------------------------------------
* pr_geometry - determine window geometry
*---------------------------------------------------------------------------*/
pr_geometry() {
char *geometry = pr_GetR(db, ".geometry");
int x, y, flags;
unsigned int w, h;
if (geometry) {
flags = XParseGeometry(geometry, &x, &y, &w, &h);
if (flags & WidthValue) W = w;
if (flags & HeightValue) H = h;
if (flags & XValue) {
if (flags & XNegative) x += DisplayWidth(dpy,scr);
gX = x;
}
if (flags & YValue) {
if (flags & YNegative) y += DisplayHeight(dpy,scr);
gY = y;
}
}
}
/*-----------------------------------------------------------------------------
* pr_window - create window
*---------------------------------------------------------------------------*/
pr_window() {
char *title = pr_GetR(db, ".title");
XSizeHints hints;
win = XCreateSimpleWindow(dpy, root, gX, gY, W, H, 2, colors[1], colors[0]);
pr_GetR(db, ".clear") && On(value.addr) && Clear++;
hints.flags = PPosition;
hints.x = gX; hints.y = gY;
XSetNormalHints(dpy, win, &hints);
if (pr_GetR(db, ".iconic") && On(value.addr)) {
XWMHints wmh;
wmh.flags = StateHint ;
wmh.initial_state = IconicState;
XSetWMHints(dpy, win, &wmh);
}
XStoreName(dpy, win, ((title) ? title : Class));
XSelectInput(dpy, win, StructureNotifyMask);
XMapWindow(dpy, win);
}