blob: 6e300b56050598dd31a0c0247e3ebcb9fce74f8b [file] [log] [blame]
#include <math.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include "util.h"
#include "graphics.h"
#include "vpr_types.h"
/*#include "draw.h" */
#ifndef NO_GRAPHICS
#include <X11/Xlib.h>
#include <X11/Xutil.h>
#include <X11/Xos.h>
#include <X11/Xatom.h>
#endif
/* Written by Vaughn Betz at the University of Toronto, Department of *
* Electrical and Computer Engineering. Graphics package Version 1.3. *
* All rights reserved by U of T, etc. *
* *
* You may freely use this graphics interface for non-commercial purposes *
* as long as you leave the written by Vaughn Betz message in it -- who *
* knows, maybe someday an employer will see it and give me a job or large *
* sums of money :). *
* *
* Revision History: *
* *
* Sept. 19, 1997: Incorporated Zoom Fit code of Haneef Mohammed at *
* Cypress. Makes it easy to zoom to a full view of the graphics. *
* *
* Sept. 11, 1997: Added the create_button and delete_button interface to *
* make it easy to add and destroy buttons from user code. Removed the *
* bnum parameter to the button functions, since it wasn't really needed. *
* *
* June 28, 1997: Added filled arc drawing primitive. Minor modifications *
* to PostScript driver to make the PostScript output slightly smaller. *
* *
* April 15, 1997: Added code to init_graphics so it waits for a window *
* to be exposed before returning. This ensures that users of non- *
* interactive graphics can never draw to a window before it is available. *
* *
* Feb. 24, 1997: Added code so the package will allocate a private *
* colormap if the default colormap doesn't have enough free colours. *
* *
* June 28, 1996: Converted all internal functions in graphics.c to have *
* internal (static) linkage to avoid any conflicts with user routines in *
* the rest of the program. *
* *
* June 12, 1996: Added setfontsize and setlinewidth attributes. Added *
* pre-clipping of objects for speed (and compactness of PS output) when *
* graphics are zoomed in. Rewrote PostScript engine to shrink the output *
* and make it easier to read. Made drawscreen a callback function passed *
* in rather than a global. Graphics attribute calls are more efficient -- *
* they check if they have to change anything before doing it. *
* *
* October 27, 1995: Added the message area, a callback function for *
* interacting with user button clicks, and implemented a workaround for a *
* Sun X Server bug that misdisplays extremely highly zoomed graphics. *
* *
* Jan. 13, 1995: Modified to incorporate PostScript Support. */
/****************** Types and defines local to this module ******************/
#ifndef NO_GRAPHICS
/* Uncomment the line below if your X11 header files don't define XPointer */
/* typedef char *XPointer; */
/* Macros for translation from world to PostScript coordinates */
#define XPOST(worldx) (((worldx)-xleft)*ps_xmult + ps_left)
#define YPOST(worldy) (((worldy)-ybot)*ps_ymult + ps_bot)
/* Macros to convert from X Windows Internal Coordinates to my *
* World Coordinates. (This macro is used only rarely, so *
* the divides don't hurt speed). */
#define XTOWORLD(x) (((float) x)/xmult + xleft)
#define YTOWORLD(y) (((float) y)/ymult + ytop)
#define max(a,b) (((a) > (b))? (a) : (b))
#define min(a,b) ((a) > (b)? (b) : (a))
#define MWIDTH 104 /* width of menu window */
#define T_AREA_HEIGHT 24 /* Height of text window */
#define MAX_FONT_SIZE 40 /* Largest point size of text */
#define PI 3.141592654
#define BUTTON_TEXT_LEN 20
typedef struct
{
int width;
int height;
int xleft;
int ytop;
void (*fcn) (void (*drawscreen) (void));
Window win;
int istext;
char text[BUTTON_TEXT_LEN];
int ispoly;
int poly[3][2];
int ispressed;
}
t_button;
/********************* Static variables local to this module ****************/
static const int menu_font_size = 14; /* Font for menus and dialog boxes. */
static t_button *button; /* [0..num_buttons-1] */
static int num_buttons; /* Number of menu buttons */
static int disp_type; /* Selects SCREEN or POSTSCRIPT */
static Display *display;
static int screen_num;
static GC gc, gcxor, gc_menus;
static XFontStruct *font_info[MAX_FONT_SIZE + 1]; /* Data for each size */
static int font_is_loaded[MAX_FONT_SIZE + 1]; /* 1: loaded, 0: not */
static unsigned int display_width, display_height; /* screen size */
static unsigned int top_width, top_height; /* window size */
static Window toplevel, menu, textarea; /* various windows */
static float xleft, xright, ytop, ybot; /* world coordinates */
/* Initial world coordinates */
static float saved_xleft, saved_xright, saved_ytop, saved_ybot;
static float ps_left, ps_right, ps_top, ps_bot; /* Figure boundaries for *
* * PostScript output, in PostScript coordinates. */
static float ps_xmult, ps_ymult; /* Transformation for PostScript. */
static float xmult, ymult; /* Transformation factors */
static Colormap private_cmap; /* "None" unless a private cmap was allocated. */
/* Graphics state. Set start-up defaults here. */
static int currentcolor = BLACK;
static int currentlinestyle = SOLID;
static int currentlinewidth = 0;
static int currentfontsize = 10;
static char message[BUFSIZE] = "\0"; /* User message to display */
/* Color indices passed back from X Windows. */
static int colors[NUM_COLOR];
/* For PostScript output */
static FILE *ps;
/* MAXPIXEL and MINPIXEL are set to prevent what appears to be *
* overflow with very large pixel values on the Sun X Server. */
#define MAXPIXEL 15000
#define MINPIXEL -15000
/********************** Subroutines local to this module ********************/
/* Function declarations for button responses */
static void translate_up(void (*drawscreen) (void));
static void translate_left(void (*drawscreen) (void));
static void translate_right(void (*drawscreen) (void));
static void translate_down(void (*drawscreen) (void));
static void zoom_in(void (*drawscreen) (void));
static void zoom_out(void (*drawscreen) (void));
static void zoom_fit(void (*drawscreen) (void));
static void adjustwin(void (*drawscreen) (void));
static void postscript(void (*drawscreen) (void));
static void proceed(void (*drawscreen) (void));
static void quit(void (*drawscreen) (void));
static Bool test_if_exposed(Display * disp,
XEvent * event_ptr,
XPointer dummy);
static void map_button(int bnum);
static void unmap_button(int bnum);
/********************** Subroutine definitions ******************************/
static int
xcoord(float worldx)
{
/* Translates from my internal coordinates to X Windows coordinates *
* in the x direction. Add 0.5 at end for extra half-pixel accuracy. */
int winx;
winx = (int)((worldx - xleft) * xmult + 0.5);
/* Avoid overflow in the X Window routines. This will allow horizontal *
* and vertical lines to be drawn correctly regardless of zooming, but *
* will cause diagonal lines that go way off screen to change their *
* slope as you zoom in. The only way I can think of to completely fix *
* this problem is to do all the clipping in advance in floating point, *
* then convert to integers and call X Windows. This is a lot of extra *
* coding, and means that coordinates will be clipped twice, even though *
* this "Super Zoom" problem won't occur unless users zoom way in on *
* the graphics. */
winx = max(winx, MINPIXEL);
winx = min(winx, MAXPIXEL);
return (winx);
}
static int
ycoord(float worldy)
{
/* Translates from my internal coordinates to X Windows coordinates *
* in the y direction. Add 0.5 at end for extra half-pixel accuracy. */
int winy;
winy = (int)((worldy - ytop) * ymult + 0.5);
/* Avoid overflow in the X Window routines. */
winy = max(winy, MINPIXEL);
winy = min(winy, MAXPIXEL);
return (winy);
}
static void
load_font(int pointsize)
{
/* Makes sure the font of the specified size is loaded. Point_size *
* MUST be between 1 and MAX_FONT_SIZE -- no check is performed here. */
/* Use proper point-size medium-weight upright helvetica font */
char fontname[44];
sprintf(fontname, "-*-helvetica-medium-r-*--*-%d0-*-*-*-*-*-*",
pointsize);
#ifdef VERBOSE
printf("Loading font: point size: %d, fontname: %s\n", pointsize,
fontname);
#endif
/* Load font and get font information structure. */
if((font_info[pointsize] = XLoadQueryFont(display, fontname)) == NULL)
{
fprintf(stderr, "Cannot open desired font\n");
exit(-1);
}
}
static void
force_setcolor(int cindex)
{
static char *ps_cnames[NUM_COLOR] =
{ "white", "black", "grey55", "grey75",
"blue", "green", "yellow", "cyan", "red", "darkgreen", "magenta",
"bisque", "lightblue", "thistle", "plum", "khaki",
"coral", "turquoise", "mediumpurple", "darkslateblue", "darkkhaki"
};
currentcolor = cindex;
if(disp_type == SCREEN)
{
XSetForeground(display, gc, colors[cindex]);
}
else
{
fprintf(ps, "%s\n", ps_cnames[cindex]);
}
}
void
setcolor(int cindex)
{
if(currentcolor != cindex)
force_setcolor(cindex);
}
int getcolor()
{
return currentcolor;
}
static void
force_setlinestyle(int linestyle)
{
/* Note SOLID is 0 and DASHED is 1 for linestyle. */
/* PostScript and X commands needed, respectively. */
static char *ps_text[2] = { "linesolid", "linedashed" };
static int x_vals[2] = { LineSolid, LineOnOffDash };
currentlinestyle = linestyle;
if(disp_type == SCREEN)
{
XSetLineAttributes(display, gc, currentlinewidth,
x_vals[linestyle], CapButt, JoinMiter);
}
else
{
fprintf(ps, "%s\n", ps_text[linestyle]);
}
}
void
setlinestyle(int linestyle)
{
if(linestyle != currentlinestyle)
force_setlinestyle(linestyle);
}
static void
force_setlinewidth(int linewidth)
{
/* linewidth should be greater than or equal to 0 to make any sense. */
/* Note SOLID is 0 and DASHED is 1 for linestyle. */
static int x_vals[2] = { LineSolid, LineOnOffDash };
currentlinewidth = linewidth;
if(disp_type == SCREEN)
{
XSetLineAttributes(display, gc, linewidth,
x_vals[currentlinestyle], CapButt, JoinMiter);
}
else
{
fprintf(ps, "%d setlinewidth\n", linewidth);
}
}
void
setlinewidth(int linewidth)
{
if(linewidth != currentlinewidth)
force_setlinewidth(linewidth);
}
static void
force_setfontsize(int pointsize)
{
/* Valid point sizes are between 1 and MAX_FONT_SIZE */
if(pointsize < 1)
pointsize = 1;
else if(pointsize > MAX_FONT_SIZE)
pointsize = MAX_FONT_SIZE;
currentfontsize = pointsize;
if(disp_type == SCREEN)
{
if(!font_is_loaded[pointsize])
{
load_font(pointsize);
font_is_loaded[pointsize] = 1;
}
XSetFont(display, gc, font_info[pointsize]->fid);
}
else
{
/* PostScript: set up font and centering function */
fprintf(ps, "%d setfontsize\n", pointsize);
}
}
void
setfontsize(int pointsize)
{
/* For efficiency, this routine doesn't do anything if no change is *
* implied. If you want to force the graphics context or PS file *
* to have font info set, call force_setfontsize (this is necessary *
* in initialization and X11 / Postscript switches). */
if(pointsize != currentfontsize)
force_setfontsize(pointsize);
}
static void
build_textarea(void)
{
/* Creates a small window at the top of the graphics area for text messages */
XSetWindowAttributes menu_attributes;
unsigned long valuemask;
textarea = XCreateSimpleWindow(display, toplevel,
0, top_height - T_AREA_HEIGHT,
display_width, T_AREA_HEIGHT - 4, 2,
colors[BLACK], colors[LIGHTGREY]);
menu_attributes.event_mask = ExposureMask;
/* ButtonPresses in this area are ignored. */
menu_attributes.do_not_propagate_mask = ButtonPressMask;
/* Keep text area on bottom left */
menu_attributes.win_gravity = SouthWestGravity;
valuemask = CWWinGravity | CWEventMask | CWDontPropagate;
XChangeWindowAttributes(display, textarea, valuemask, &menu_attributes);
XMapWindow(display, textarea);
}
static void
setpoly(int bnum,
int xc,
int yc,
int r,
float theta)
{
/* Puts a triangle in the poly array for button[bnum] */
int i;
button[bnum].istext = 0;
button[bnum].ispoly = 1;
for(i = 0; i < 3; i++)
{
button[bnum].poly[i][0] = (int)(xc + r * cos(theta) + 0.5);
button[bnum].poly[i][1] = (int)(yc + r * sin(theta) + 0.5);
theta += 2 * PI / 3;
}
}
static void
build_default_menu(void)
{
/* Sets up the default menu buttons on the right hand side of the window. */
XSetWindowAttributes menu_attributes;
unsigned long valuemask;
int i, xcen, x1, y1, bwid, bheight, space;
menu = XCreateSimpleWindow(display, toplevel,
top_width - MWIDTH, 0, MWIDTH - 4,
display_height, 2, colors[BLACK],
colors[LIGHTGREY]);
menu_attributes.event_mask = ExposureMask;
/* Ignore button presses on the menu background. */
menu_attributes.do_not_propagate_mask = ButtonPressMask;
/* Keep menu on top right */
menu_attributes.win_gravity = NorthEastGravity;
valuemask = CWWinGravity | CWEventMask | CWDontPropagate;
XChangeWindowAttributes(display, menu, valuemask, &menu_attributes);
XMapWindow(display, menu);
num_buttons = 11;
button = (t_button *) my_malloc(num_buttons * sizeof(t_button));
/* Now do the arrow buttons */
bwid = 28;
space = 3;
y1 = 10;
xcen = 51;
x1 = xcen - bwid / 2;
button[0].xleft = x1;
button[0].ytop = y1;
setpoly(0, bwid / 2, bwid / 2, bwid / 3, -PI / 2.); /* Up */
button[0].fcn = translate_up;
y1 += bwid + space;
x1 = xcen - 3 * bwid / 2 - space;
button[1].xleft = x1;
button[1].ytop = y1;
setpoly(1, bwid / 2, bwid / 2, bwid / 3, PI); /* Left */
button[1].fcn = translate_left;
x1 = xcen + bwid / 2 + space;
button[2].xleft = x1;
button[2].ytop = y1;
setpoly(2, bwid / 2, bwid / 2, bwid / 3, 0); /* Right */
button[2].fcn = translate_right;
y1 += bwid + space;
x1 = xcen - bwid / 2;
button[3].xleft = x1;
button[3].ytop = y1;
setpoly(3, bwid / 2, bwid / 2, bwid / 3, +PI / 2.); /* Down */
button[3].fcn = translate_down;
for(i = 0; i < 4; i++)
{
button[i].width = bwid;
button[i].height = bwid;
}
/* Rectangular buttons */
y1 += bwid + space + 6;
space = 8;
bwid = 90;
bheight = 26;
x1 = xcen - bwid / 2;
for(i = 4; i < num_buttons; i++)
{
button[i].xleft = x1;
button[i].ytop = y1;
y1 += bheight + space;
button[i].istext = 1;
button[i].ispoly = 0;
button[i].width = bwid;
button[i].height = bheight;
}
strcpy(button[4].text, "Zoom In");
strcpy(button[5].text, "Zoom Out");
strcpy(button[6].text, "Zoom Fit");
strcpy(button[7].text, "Window");
strcpy(button[8].text, "PostScript");
strcpy(button[9].text, "Proceed");
strcpy(button[10].text, "Exit");
button[4].fcn = zoom_in;
button[5].fcn = zoom_out;
button[6].fcn = zoom_fit;
button[7].fcn = adjustwin;
button[8].fcn = postscript;
button[9].fcn = proceed;
button[10].fcn = quit;
for(i = 0; i < num_buttons; i++)
map_button(i);
}
static void
map_button(int bnum)
{
/* Maps a button onto the screen and set it up for input, etc. */
button[bnum].win = XCreateSimpleWindow(display, menu,
button[bnum].xleft,
button[bnum].ytop,
button[bnum].width,
button[bnum].height, 0,
colors[WHITE], colors[LIGHTGREY]);
XMapWindow(display, button[bnum].win);
XSelectInput(display, button[bnum].win, ButtonPressMask);
button[bnum].ispressed = 1;
}
static void
unmap_button(int bnum)
{
/* Unmaps a button from the screen. */
XUnmapWindow(display, button[bnum].win);
}
void
create_button(char *prev_button_text,
char *button_text,
void (*button_func) (void (*drawscreen) (void)))
{
/* Creates a new button below the button containing prev_button_text. *
* The text and button function are set according to button_text and *
* button_func, respectively. */
int i, bnum, space;
space = 8;
/* Only allow new buttons that are text (not poly) types. */
bnum = -1;
for(i = 4; i < num_buttons; i++)
{
if(button[i].istext == 1 &&
strcmp(button[i].text, prev_button_text) == 0)
{
bnum = i + 1;
break;
}
}
if(bnum == -1)
{
printf
("Error in create_button: button with text %s not found.\n",
prev_button_text);
exit(1);
}
num_buttons++;
button = (t_button *) my_realloc(button, num_buttons * sizeof(t_button));
/* NB: Requirement that you specify the button that this button goes under *
* guarantees that button[num_buttons-2] exists and is a text button. */
button[num_buttons - 1].xleft = button[num_buttons - 2].xleft;
button[num_buttons - 1].ytop = button[num_buttons - 2].ytop +
button[num_buttons - 2].height + space;
button[num_buttons - 1].height = button[num_buttons - 2].height;
button[num_buttons - 1].width = button[num_buttons - 2].width;
map_button(num_buttons - 1);
for(i = num_buttons - 1; i > bnum; i--)
{
button[i].ispoly = button[i - 1].ispoly;
/* No poly copy for now, as I'm only providing the ability to create text *
* buttons. */
button[i].istext = button[i - 1].istext;
strcpy(button[i].text, button[i - 1].text);
button[i].fcn = button[i - 1].fcn;
button[i].ispressed = button[i - 1].ispressed;
}
button[bnum].istext = 1;
button[bnum].ispoly = 0;
my_strncpy(button[bnum].text, button_text, BUTTON_TEXT_LEN);
button[bnum].fcn = button_func;
button[bnum].ispressed = 1;
}
void
destroy_button(char *button_text)
{
/* Destroys the button with text button_text. */
int i, bnum;
bnum = -1;
for(i = 4; i < num_buttons; i++)
{
if(button[i].istext == 1 &&
strcmp(button[i].text, button_text) == 0)
{
bnum = i;
break;
}
}
if(bnum == -1)
{
printf
("Error in destroy_button: button with text %s not found.\n",
button_text);
exit(1);
}
for(i = bnum + 1; i < num_buttons; i++)
{
button[i - 1].ispoly = button[i].ispoly;
/* No poly copy for now, as I'm only providing the ability to create text *
* buttons. */
button[i - 1].istext = button[i].istext;
strcpy(button[i - 1].text, button[i].text);
button[i - 1].fcn = button[i].fcn;
button[i - 1].ispressed = button[i].ispressed;
}
unmap_button(num_buttons - 1);
num_buttons--;
button = (t_button *) my_realloc(button, num_buttons * sizeof(t_button));
}
void
init_graphics(char *window_name)
{
/* Open the toplevel window, get the colors, 2 graphics *
* contexts, load a font, and set up the toplevel window *
* Calls build_default_menu to set up the default menu. */
char *display_name = NULL;
int x, y; /* window position */
unsigned int border_width = 2; /* ignored by OpenWindows */
XTextProperty windowName;
/* X Windows' names for my colours. */
char *cnames[NUM_COLOR] = { "white", "black", "grey55", "grey75", "blue",
"green", "yellow", "cyan", "red", "RGBi:0.0/0.5/0.0", "magenta",
"bisque", "lightblue", "thistle", "plum", "khaki",
"coral", "turquoise", "mediumpurple", "darkslateblue", "darkkhaki"
};
XColor exact_def;
Colormap cmap;
int i;
unsigned long valuemask = 0; /* ignore XGCvalues and use defaults */
XGCValues values;
XEvent event;
disp_type = SCREEN; /* Graphics go to screen, not ps */
for(i = 0; i <= MAX_FONT_SIZE; i++)
font_is_loaded[i] = 0; /* No fonts loaded yet. */
/* connect to X server */
if((display = XOpenDisplay(display_name)) == NULL)
{
fprintf(stderr, "Cannot connect to X server %s\n",
XDisplayName(display_name));
exit(-1);
}
/* get screen size from display structure macro */
screen_num = DefaultScreen(display);
display_width = DisplayWidth(display, screen_num);
display_height = DisplayHeight(display, screen_num);
x = y = 0;
top_width = 2 * display_width / 3;
top_height = 4 * display_height / 5;
cmap = DefaultColormap(display, screen_num);
private_cmap = None;
for(i = 0; i < NUM_COLOR; i++)
{
if(!XParseColor(display, cmap, cnames[i], &exact_def))
{
fprintf(stderr, "Color name %s not in database",
cnames[i]);
exit(-1);
}
if(!XAllocColor(display, cmap, &exact_def))
{
fprintf(stderr, "Couldn't allocate color %s.\n",
cnames[i]);
if(private_cmap == None)
{
fprintf(stderr,
"Will try to allocate a private colourmap.\n");
fprintf(stderr,
"Colours will only display correctly when your "
"cursor is in the graphics window.\n"
"Exit other colour applications and rerun this "
"program if you don't like that.\n\n");
private_cmap =
XCopyColormapAndFree(display, cmap);
cmap = private_cmap;
if(!XAllocColor(display, cmap, &exact_def))
{
fprintf(stderr,
"Couldn't allocate color %s as private.\n",
cnames[i]);
exit(1);
}
}
else
{
fprintf(stderr,
"Couldn't allocate color %s as private.\n",
cnames[i]);
exit(1);
}
}
colors[i] = exact_def.pixel;
}
toplevel =
XCreateSimpleWindow(display, RootWindow(display, screen_num), x, y,
top_width, top_height, border_width,
colors[BLACK], colors[WHITE]);
if(private_cmap != None)
XSetWindowColormap(display, toplevel, private_cmap);
/* hints stuff deleted. */
XSelectInput(display, toplevel, ExposureMask | StructureNotifyMask |
ButtonPressMask);
/* Create default Graphics Contexts. valuemask = 0 -> use defaults. */
gc = XCreateGC(display, toplevel, valuemask, &values);
gc_menus = XCreateGC(display, toplevel, valuemask, &values);
/* Create XOR graphics context for Rubber Banding */
values.function = GXxor;
values.foreground = colors[BLACK];
gcxor = XCreateGC(display, toplevel, (GCFunction | GCForeground),
&values);
/* specify font for menus. */
load_font(menu_font_size);
font_is_loaded[menu_font_size] = 1;
XSetFont(display, gc_menus, font_info[menu_font_size]->fid);
/* Set drawing defaults for user-drawable area. Use whatever the *
* initial values of the current stuff was set to. */
force_setfontsize(currentfontsize);
force_setcolor(currentcolor);
force_setlinestyle(currentlinestyle);
force_setlinewidth(currentlinewidth);
XStringListToTextProperty(&window_name, 1, &windowName);
XSetWMName(display, toplevel, &windowName);
/* Uncomment to set icon name */
/* XSetWMIconName (display, toplevel, &windowName); */
/* XStringListToTextProperty copies the window_name string into *
* windowName.value. Free this memory now. */
free(windowName.value);
XMapWindow(display, toplevel);
build_textarea();
build_default_menu();
/* The following is completely unnecessary if the user is using the *
* interactive (event_loop) graphics. It waits for the first Expose *
* event before returning so that I can tell the window manager has got *
* the top-level window up and running. Thus the user can start drawing *
* into this window immediately, and there's no danger of the window not *
* being ready and output being lost. */
XPeekIfEvent(display, &event, test_if_exposed, NULL);
}
static Bool
test_if_exposed(Display * disp,
XEvent * event_ptr,
XPointer dummy)
{
/* Returns True if the event passed in is an exposure event. Note that *
* the bool type returned by this function is defined in Xlib.h. */
if(event_ptr->type == Expose)
{
return (True);
}
return (False);
}
static void
menutext(Window win,
int xc,
int yc,
char *text)
{
/* draws text center at xc, yc -- used only by menu drawing stuff */
int len, width;
len = strlen(text);
width = XTextWidth(font_info[menu_font_size], text, len);
XDrawString(display, win, gc_menus, xc - width / 2, yc +
(font_info[menu_font_size]->ascent -
font_info[menu_font_size]->descent) / 2, text, len);
}
static void
drawbut(int bnum)
{
/* Draws button bnum in either its pressed or unpressed state. */
int width, height, thick, i, ispressed;
XPoint mypoly[6];
ispressed = button[bnum].ispressed;
thick = 2;
width = button[bnum].width;
height = button[bnum].height;
/* Draw top and left edges of 3D box. */
if(ispressed)
{
XSetForeground(display, gc_menus, colors[BLACK]);
}
else
{
XSetForeground(display, gc_menus, colors[WHITE]);
}
/* Note: X Windows doesn't appear to draw the bottom pixel of *
* a polygon with XFillPolygon, so I make this 1 pixel thicker *
* to compensate. */
mypoly[0].x = 0;
mypoly[0].y = height;
mypoly[1].x = 0;
mypoly[1].y = 0;
mypoly[2].x = width;
mypoly[2].y = 0;
mypoly[3].x = width - thick;
mypoly[3].y = thick;
mypoly[4].x = thick;
mypoly[4].y = thick;
mypoly[5].x = thick;
mypoly[5].y = height - thick;
XFillPolygon(display, button[bnum].win, gc_menus, mypoly, 6, Convex,
CoordModeOrigin);
/* Draw bottom and right edges of 3D box. */
if(ispressed)
{
XSetForeground(display, gc_menus, colors[WHITE]);
}
else
{
XSetForeground(display, gc_menus, colors[BLACK]);
}
mypoly[0].x = 0;
mypoly[0].y = height;
mypoly[1].x = width;
mypoly[1].y = height;
mypoly[2].x = width;
mypoly[2].y = 0;
mypoly[3].x = width - thick;
mypoly[3].y = thick;
mypoly[4].x = width - thick;
mypoly[4].y = height - thick;
mypoly[5].x = thick;
mypoly[5].y = height - thick;
XFillPolygon(display, button[bnum].win, gc_menus, mypoly, 6, Convex,
CoordModeOrigin);
/* Draw background */
if(ispressed)
{
XSetForeground(display, gc_menus, colors[DARKGREY]);
}
else
{
XSetForeground(display, gc_menus, colors[LIGHTGREY]);
}
/* Give x,y of top corner and width and height */
XFillRectangle(display, button[bnum].win, gc_menus, thick, thick,
width - 2 * thick, height - 2 * thick);
/* Draw polygon, if there is one */
if(button[bnum].ispoly)
{
for(i = 0; i < 3; i++)
{
mypoly[i].x = button[bnum].poly[i][0];
mypoly[i].y = button[bnum].poly[i][1];
}
XSetForeground(display, gc_menus, colors[BLACK]);
XFillPolygon(display, button[bnum].win, gc_menus, mypoly, 3,
Convex, CoordModeOrigin);
}
/* Draw text, if there is any */
if(button[bnum].istext)
{
XSetForeground(display, gc_menus, colors[BLACK]);
menutext(button[bnum].win, button[bnum].width / 2,
button[bnum].height / 2, button[bnum].text);
}
}
static void
turn_on_off(int pressed)
{
/* Shows when the menu is active or inactive by colouring the *
* buttons. */
int i;
for(i = 0; i < num_buttons; i++)
{
button[i].ispressed = pressed;
drawbut(i);
}
}
static int
which_button(Window win)
{
int i;
for(i = 0; i < num_buttons; i++)
{
if(button[i].win == win)
return (i);
}
printf("Error: Unknown button ID in which_button.\n");
return (0);
}
static void
drawmenu(void)
{
int i;
for(i = 0; i < num_buttons; i++)
{
drawbut(i);
}
}
static void
update_transform(void)
{
/* Set up the factors for transforming from the user world to X Windows *
* coordinates. */
float mult, y1, y2, x1, x2;
/* X Window coordinates go from (0,0) to (width-1,height-1) */
xmult = ((float)top_width - 1. - MWIDTH) / (xright - xleft);
ymult = ((float)top_height - 1. - T_AREA_HEIGHT) / (ybot - ytop);
/* Need to use same scaling factor to preserve aspect ratio */
if(fabs(xmult) <= fabs(ymult))
{
mult = fabs(ymult / xmult);
y1 = ytop - (ybot - ytop) * (mult - 1.) / 2.;
y2 = ybot + (ybot - ytop) * (mult - 1.) / 2.;
ytop = y1;
ybot = y2;
}
else
{
mult = fabs(xmult / ymult);
x1 = xleft - (xright - xleft) * (mult - 1.) / 2.;
x2 = xright + (xright - xleft) * (mult - 1.) / 2.;
xleft = x1;
xright = x2;
}
xmult = ((float)top_width - 1. - MWIDTH) / (xright - xleft);
ymult = ((float)top_height - 1. - T_AREA_HEIGHT) / (ybot - ytop);
}
static void
update_ps_transform(void)
{
/* Postscript coordinates start at (0,0) for the lower left hand corner *
* of the page and increase upwards and to the right. For 8.5 x 11 *
* sheet, coordinates go from (0,0) to (612,792). Spacing is 1/72 inch.*
* I'm leaving a minimum of half an inch (36 units) of border around *
* each edge. */
float ps_width, ps_height;
ps_width = 540.; /* 72 * 7.5 */
ps_height = 720.; /* 72 * 10 */
ps_xmult = ps_width / (xright - xleft);
ps_ymult = ps_height / (ytop - ybot);
/* Need to use same scaling factor to preserve aspect ratio. *
* I show exactly as much on paper as the screen window shows, *
* or the user specifies. */
if(fabs(ps_xmult) <= fabs(ps_ymult))
{
ps_left = 36.;
ps_right = 36. + ps_width;
ps_bot = 396. - fabs(ps_xmult * (ytop - ybot)) / 2.;
ps_top = 396. + fabs(ps_xmult * (ytop - ybot)) / 2.;
/* Maintain aspect ratio but watch signs */
ps_ymult = (ps_xmult * ps_ymult < 0) ? -ps_xmult : ps_xmult;
}
else
{
ps_bot = 36.;
ps_top = 36. + ps_height;
ps_left = 306. - fabs(ps_ymult * (xright - xleft)) / 2.;
ps_right = 306. + fabs(ps_ymult * (xright - xleft)) / 2.;
/* Maintain aspect ratio but watch signs */
ps_xmult = (ps_xmult * ps_ymult < 0) ? -ps_ymult : ps_ymult;
}
}
void
event_loop(void (*act_on_button) (float x,
float y),
void (*drawscreen) (void))
{
/* The program's main event loop. Must be passed a user routine *
* drawscreen which redraws the screen. It handles all window resizing *
* zooming etc. itself. If the user clicks a button in the graphics *
* (toplevel) area, the act_on_button routine passed in is called. */
XEvent report;
int bnum;
float x, y;
#define OFF 1
#define ON 0
turn_on_off(ON);
while(1)
{
XNextEvent(display, &report);
switch (report.type)
{
case Expose:
#ifdef VERBOSE
printf("Got an expose event.\n");
printf("Count is: %d.\n", report.xexpose.count);
printf("Window ID is: %d.\n", report.xexpose.window);
#endif
if(report.xexpose.count != 0)
break;
if(report.xexpose.window == menu)
drawmenu();
else if(report.xexpose.window == toplevel)
drawscreen();
else if(report.xexpose.window == textarea)
draw_message();
break;
case ConfigureNotify:
top_width = report.xconfigure.width;
top_height = report.xconfigure.height;
update_transform();
#ifdef VERBOSE
printf("Got a ConfigureNotify.\n");
printf("New width: %d New height: %d.\n", top_width,
top_height);
#endif
break;
case ButtonPress:
#ifdef VERBOSE
printf("Got a buttonpress.\n");
printf("Window ID is: %d.\n", report.xbutton.window);
#endif
if(report.xbutton.window == toplevel)
{
x = XTOWORLD(report.xbutton.x);
y = YTOWORLD(report.xbutton.y);
act_on_button(x, y);
}
else
{ /* A menu button was pressed. */
bnum = which_button(report.xbutton.window);
#ifdef VERBOSE
printf("Button number is %d\n", bnum);
#endif
button[bnum].ispressed = 1;
drawbut(bnum);
XFlush(display); /* Flash the button */
button[bnum].fcn(drawscreen);
button[bnum].ispressed = 0;
drawbut(bnum);
if(button[bnum].fcn == proceed)
{
turn_on_off(OFF);
flushinput();
return; /* Rather clumsy way of returning *
* control to the simulator */
}
}
break;
}
}
}
void
clearscreen(void)
{
int savecolor;
if(disp_type == SCREEN)
{
XClearWindow(display, toplevel);
}
else
{
/* erases current page. Don't use erasepage, since this will erase *
* everything, (even stuff outside the clipping path) causing *
* problems if this picture is incorporated into a larger document. */
savecolor = currentcolor;
setcolor(WHITE);
fprintf(ps, "clippath fill\n\n");
setcolor(savecolor);
}
}
static int
rect_off_screen(float x1,
float y1,
float x2,
float y2)
{
/* Return 1 if I can quarantee no part of this rectangle will *
* lie within the user drawing area. Otherwise return 0. *
* Note: this routine is only used to help speed (and to shrink ps *
* files) -- it will be highly effective when the graphics are zoomed *
* in and lots are off-screen. I don't have to pre-clip for *
* correctness. */
float xmin, xmax, ymin, ymax;
xmin = min(xleft, xright);
if(x1 < xmin && x2 < xmin)
return (1);
xmax = max(xleft, xright);
if(x1 > xmax && x2 > xmax)
return (1);
ymin = min(ytop, ybot);
if(y1 < ymin && y2 < ymin)
return (1);
ymax = max(ytop, ybot);
if(y1 > ymax && y2 > ymax)
return (1);
return (0);
}
void
drawline(float x1,
float y1,
float x2,
float y2)
{
/* Draw a line from (x1,y1) to (x2,y2) in the user-drawable area. *
* Coordinates are in world (user) space. */
if(rect_off_screen(x1, y1, x2, y2))
return;
if(disp_type == SCREEN)
{
/* Xlib.h prototype has x2 and y1 mixed up. */
XDrawLine(display, toplevel, gc, xcoord(x1), ycoord(y1),
xcoord(x2), ycoord(y2));
}
else
{
fprintf(ps, "%.2f %.2f %.2f %.2f drawline\n", XPOST(x1),
YPOST(y1), XPOST(x2), YPOST(y2));
}
}
void
drawrect(float x1,
float y1,
float x2,
float y2)
{
/* (x1,y1) and (x2,y2) are diagonally opposed corners, in world coords. */
unsigned int width, height;
int xw1, yw1, xw2, yw2, xl, yt;
if(rect_off_screen(x1, y1, x2, y2))
return;
if(disp_type == SCREEN)
{
/* translate to X Windows calling convention. */
xw1 = xcoord(x1);
xw2 = xcoord(x2);
yw1 = ycoord(y1);
yw2 = ycoord(y2);
xl = min(xw1, xw2);
yt = min(yw1, yw2);
width = abs(xw1 - xw2);
height = abs(yw1 - yw2);
XDrawRectangle(display, toplevel, gc, xl, yt, width, height);
}
else
{
fprintf(ps, "%.2f %.2f %.2f %.2f drawrect\n", XPOST(x1),
YPOST(y1), XPOST(x2), YPOST(y2));
}
}
void
fillrect(float x1,
float y1,
float x2,
float y2)
{
/* (x1,y1) and (x2,y2) are diagonally opposed corners in world coords. */
unsigned int width, height;
int xw1, yw1, xw2, yw2, xl, yt;
if(rect_off_screen(x1, y1, x2, y2))
return;
if(disp_type == SCREEN)
{
/* translate to X Windows calling convention. */
xw1 = xcoord(x1);
xw2 = xcoord(x2);
yw1 = ycoord(y1);
yw2 = ycoord(y2);
xl = min(xw1, xw2);
yt = min(yw1, yw2);
width = abs(xw1 - xw2);
height = abs(yw1 - yw2);
XFillRectangle(display, toplevel, gc, xl, yt, width, height);
}
else
{
fprintf(ps, "%.2f %.2f %.2f %.2f fillrect\n", XPOST(x1),
YPOST(y1), XPOST(x2), YPOST(y2));
}
}
static float
angnorm(float ang)
{
/* Normalizes an angle to be between 0 and 360 degrees. */
int scale;
if(ang < 0)
{
scale = (int)(ang / 360. - 1);
}
else
{
scale = (int)(ang / 360.);
}
ang = ang - scale * 360.;
return (ang);
}
void
drawarc(float xc,
float yc,
float rad,
float startang,
float angextent)
{
/* Draws a circular arc. X11 can do elliptical arcs quite simply, and *
* PostScript could do them by scaling the coordinate axes. Too much *
* work for now, and probably too complex an object for users to draw *
* much, so I'm just doing circular arcs. Startang is relative to the *
* Window's positive x direction. Angles in degrees. */
int xl, yt;
unsigned int width, height;
/* Conservative (but fast) clip test -- check containing rectangle of *
* a circle. */
if(rect_off_screen(xc - rad, yc - rad, xc + rad, yc + rad))
return;
/* X Windows has trouble with very large angles. (Over 360). *
* Do following to prevent its inaccurate (overflow?) problems. */
if(fabs(angextent) > 360.)
angextent = 360.;
startang = angnorm(startang);
if(disp_type == SCREEN)
{
xl = (int)(xcoord(xc) - fabs(xmult * rad));
yt = (int)(ycoord(yc) - fabs(ymult * rad));
width = (unsigned int)(2 * fabs(xmult * rad));
height = width;
XDrawArc(display, toplevel, gc, xl, yt, width, height,
(int)(startang * 64), (int)(angextent * 64));
}
else
{
fprintf(ps, "%.2f %.2f %.2f %.2f %.2f %s stroke\n", XPOST(xc),
YPOST(yc), fabs(rad * ps_xmult), startang,
startang + angextent,
(angextent < 0) ? "drawarcn" : "drawarc");
}
}
void
fillarc(float xc,
float yc,
float rad,
float startang,
float angextent)
{
/* Fills a circular arc. Startang is relative to the Window's positive x *
* direction. Angles in degrees. */
int xl, yt;
unsigned int width, height;
/* Conservative (but fast) clip test -- check containing rectangle of *
* a circle. */
if(rect_off_screen(xc - rad, yc - rad, xc + rad, yc + rad))
return;
/* X Windows has trouble with very large angles. (Over 360). *
* Do following to prevent its inaccurate (overflow?) problems. */
if(fabs(angextent) > 360.)
angextent = 360.;
startang = angnorm(startang);
if(disp_type == SCREEN)
{
xl = (int)(xcoord(xc) - fabs(xmult * rad));
yt = (int)(ycoord(yc) - fabs(ymult * rad));
width = (unsigned int)(2 * fabs(xmult * rad));
height = width;
XFillArc(display, toplevel, gc, xl, yt, width, height,
(int)(startang * 64), (int)(angextent * 64));
}
else
{
fprintf(ps, "%.2f %.2f %.2f %.2f %.2f %s\n", fabs(rad * ps_xmult),
startang, startang + angextent, XPOST(xc), YPOST(yc),
(angextent < 0) ? "fillarcn" : "fillarc");
}
}
void
fillpoly(t_point * points,
int npoints)
{
XPoint transpoints[MAXPTS];
int i;
float xmin, ymin, xmax, ymax;
if(npoints > MAXPTS)
{
printf
("Error in fillpoly: Only %d points allowed per polygon.\n",
MAXPTS);
printf("%d points were requested. Polygon is not drawn.\n",
npoints);
return;
}
/* Conservative (but fast) clip test -- check containing rectangle of *
* polygon. */
xmin = xmax = points[0].x;
ymin = ymax = points[0].y;
for(i = 1; i < npoints; i++)
{
xmin = min(xmin, points[i].x);
xmax = max(xmax, points[i].x);
ymin = min(ymin, points[i].y);
ymax = max(ymax, points[i].y);
}
if(rect_off_screen(xmin, ymin, xmax, ymax))
return;
if(disp_type == SCREEN)
{
for(i = 0; i < npoints; i++)
{
transpoints[i].x = (short)xcoord(points[i].x);
transpoints[i].y = (short)ycoord(points[i].y);
}
XFillPolygon(display, toplevel, gc, transpoints, npoints, Complex,
CoordModeOrigin);
}
else
{
fprintf(ps, "\n");
for(i = npoints - 1; i >= 0; i--)
fprintf(ps, "%.2f %.2f\n", XPOST(points[i].x),
YPOST(points[i].y));
fprintf(ps, "%d fillpoly\n", npoints);
}
}
void
drawtext(float xc,
float yc,
const char *text,
float boundx)
{
/* Draws text centered on xc,yc if it fits in boundx */
int len, width, xw_off, yw_off;
len = strlen(text);
width = XTextWidth(font_info[currentfontsize], text, len);
if(width > fabs(boundx * xmult))
return; /* Don't draw if it won't fit */
xw_off = width / (2. * xmult); /* NB: sign doesn't matter. */
/* NB: 2 * descent makes this slightly conservative but simplifies code. */
yw_off = (font_info[currentfontsize]->ascent +
2 * font_info[currentfontsize]->descent) / (2. * ymult);
/* Note: text can be clipped when a little bit of it would be visible *
* right now. Perhaps X doesn't return extremely accurate width and *
* ascent values, etc? Could remove this completely by multiplying *
* xw_off and yw_off by, 1.2 or 1.5. */
if(rect_off_screen(xc - xw_off, yc - yw_off, xc + xw_off, yc + yw_off))
return;
if(disp_type == SCREEN)
{
XDrawString(display, toplevel, gc, xcoord(xc) - width / 2,
ycoord(yc) + (font_info[currentfontsize]->ascent -
font_info[currentfontsize]->descent) /
2, text, len);
}
else
{
fprintf(ps, "(%s) %.2f %.2f censhow\n", text, XPOST(xc),
YPOST(yc));
}
}
void
flushinput(void)
{
if(disp_type != SCREEN)
return;
XFlush(display);
}
void
init_world(float x1,
float y1,
float x2,
float y2)
{
/* Sets the coordinate system the user wants to draw into. */
xleft = x1;
xright = x2;
ytop = y1;
ybot = y2;
saved_xleft = xleft; /* Save initial world coordinates to allow full */
saved_xright = xright; /* view button to zoom all the way out. */
saved_ytop = ytop;
saved_ybot = ybot;
if(disp_type == SCREEN)
{
update_transform();
}
else
{
update_ps_transform();
}
}
void
draw_message(void)
{
/* Draw the current message in the text area at the screen bottom. */
int len, width, savefontsize, savecolor;
float ylow;
if(disp_type == SCREEN)
{
XClearWindow(display, textarea);
len = strlen(message);
width = XTextWidth(font_info[menu_font_size], message, len);
XSetForeground(display, gc_menus, colors[BLACK]);
XDrawString(display, textarea, gc_menus,
(top_width - MWIDTH - width) / 2,
(T_AREA_HEIGHT - 4) / 2 +
(font_info[menu_font_size]->ascent -
font_info[menu_font_size]->descent) / 2, message,
len);
}
else
{
/* Draw the message in the bottom margin. Printer's generally can't *
* print on the bottom 1/4" (area with y < 18 in PostScript coords.) */
savecolor = currentcolor;
setcolor(BLACK);
savefontsize = currentfontsize;
setfontsize(menu_font_size - 2); /* Smaller OK on paper */
ylow = ps_bot - 8.;
fprintf(ps, "(%s) %.2f %.2f censhow\n", message,
(ps_left + ps_right) / 2., ylow);
setcolor(savecolor);
setfontsize(savefontsize);
}
}
void
update_message(char *msg)
{
/* Changes the message to be displayed on screen. */
my_strncpy(message, msg, BUFSIZE);
draw_message();
}
static void
zoom_in(void (*drawscreen) (void))
{
/* Zooms in by a factor of 1.666. */
float xdiff, ydiff;
xdiff = xright - xleft;
ydiff = ybot - ytop;
xleft += xdiff / 5.;
xright -= xdiff / 5.;
ytop += ydiff / 5.;
ybot -= ydiff / 5.;
update_transform();
drawscreen();
}
static void
zoom_out(void (*drawscreen) (void))
{
/* Zooms out by a factor of 1.666. */
float xdiff, ydiff;
xdiff = xright - xleft;
ydiff = ybot - ytop;
xleft -= xdiff / 3.;
xright += xdiff / 3.;
ytop -= ydiff / 3.;
ybot += ydiff / 3.;
update_transform();
drawscreen();
}
static void
zoom_fit(void (*drawscreen) (void))
{
/* Sets the view back to the initial view set by init_world (i.e. a full *
* view) of all the graphics. */
xleft = saved_xleft;
xright = saved_xright;
ytop = saved_ytop;
ybot = saved_ybot;
update_transform();
drawscreen();
}
static void
translate_up(void (*drawscreen) (void))
{
/* Moves view 1/2 screen up. */
float ystep;
ystep = (ybot - ytop) / 2.;
ytop -= ystep;
ybot -= ystep;
update_transform();
drawscreen();
}
static void
translate_down(void (*drawscreen) (void))
{
/* Moves view 1/2 screen down. */
float ystep;
ystep = (ybot - ytop) / 2.;
ytop += ystep;
ybot += ystep;
update_transform();
drawscreen();
}
static void
translate_left(void (*drawscreen) (void))
{
/* Moves view 1/2 screen left. */
float xstep;
xstep = (xright - xleft) / 2.;
xleft -= xstep;
xright -= xstep;
update_transform();
drawscreen();
}
static void
translate_right(void (*drawscreen) (void))
{
/* Moves view 1/2 screen right. */
float xstep;
xstep = (xright - xleft) / 2.;
xleft += xstep;
xright += xstep;
update_transform();
drawscreen();
}
static void
update_win(int x[2],
int y[2],
void (*drawscreen) (void))
{
float x1, x2, y1, y2;
x[0] = min(x[0], top_width - MWIDTH); /* Can't go under menu */
x[1] = min(x[1], top_width - MWIDTH);
y[0] = min(y[0], top_height - T_AREA_HEIGHT); /* Can't go under text area */
y[1] = min(y[1], top_height - T_AREA_HEIGHT);
if((x[0] == x[1]) || (y[0] == y[1]))
{
printf("Illegal (zero area) window. Window unchanged.\n");
return;
}
x1 = XTOWORLD(min(x[0], x[1]));
x2 = XTOWORLD(max(x[0], x[1]));
y1 = YTOWORLD(min(y[0], y[1]));
y2 = YTOWORLD(max(y[0], y[1]));
xleft = x1;
xright = x2;
ytop = y1;
ybot = y2;
update_transform();
drawscreen();
}
static void
adjustwin(void (*drawscreen) (void))
{
/* The window button was pressed. Let the user click on the two *
* diagonally opposed corners, and zoom in on this area. */
XEvent report;
int corner, xold, yold, x[2], y[2];
corner = 0;
xold = -1;
yold = -1; /* Don't need to init yold, but stops compiler warning. */
while(corner < 2)
{
XNextEvent(display, &report);
switch (report.type)
{
case Expose:
#ifdef VERBOSE
printf("Got an expose event.\n");
printf("Count is: %d.\n", report.xexpose.count);
printf("Window ID is: %d.\n", report.xexpose.window);
#endif
if(report.xexpose.count != 0)
break;
if(report.xexpose.window == menu)
drawmenu();
else if(report.xexpose.window == toplevel)
{
drawscreen();
xold = -1; /* No rubber band on screen */
}
else if(report.xexpose.window == textarea)
draw_message();
break;
case ConfigureNotify:
top_width = report.xconfigure.width;
top_height = report.xconfigure.height;
update_transform();
#ifdef VERBOSE
printf("Got a ConfigureNotify.\n");
printf("New width: %d New height: %d.\n", top_width,
top_height);
#endif
break;
case ButtonPress:
#ifdef VERBOSE
printf("Got a buttonpress.\n");
printf("Window ID is: %d.\n", report.xbutton.window);
printf("Location (%d, %d).\n", report.xbutton.x,
report.xbutton.y);
#endif
if(report.xbutton.window != toplevel)
break;
x[corner] = report.xbutton.x;
y[corner] = report.xbutton.y;
if(corner == 0)
{
XSelectInput(display, toplevel, ExposureMask |
StructureNotifyMask | ButtonPressMask
| PointerMotionMask);
}
else
{
update_win(x, y, drawscreen);
}
corner++;
break;
case MotionNotify:
#ifdef VERBOSE
printf("Got a MotionNotify Event.\n");
printf("x: %d y: %d\n", report.xmotion.x,
report.xmotion.y);
#endif
if(xold >= 0)
{ /* xold set -ve before we draw first box */
XDrawRectangle(display, toplevel, gcxor,
min(x[0], xold), min(y[0], yold),
abs(x[0] - xold),
abs(y[0] - yold));
}
/* Don't allow user to window under menu region */
xold = min(report.xmotion.x, top_width - 1 - MWIDTH);
yold = report.xmotion.y;
XDrawRectangle(display, toplevel, gcxor, min(x[0], xold),
min(y[0], yold), abs(x[0] - xold),
abs(y[0] - yold));
break;
}
}
XSelectInput(display, toplevel, ExposureMask | StructureNotifyMask
| ButtonPressMask);
}
static void
postscript(void (*drawscreen) (void))
{
/* Takes a snapshot of the screen and stores it in pic?.ps. The *
* first picture goes in pic1.ps, the second in pic2.ps, etc. */
static int piccount = 1;
int success;
char fname[20];
sprintf(fname, "pic%d.ps", piccount);
success = init_postscript(fname);
if(success == 0)
return; /* Couldn't open file, abort. */
drawscreen();
close_postscript();
piccount++;
}
static void
proceed(void (*drawscreen) (void))
{
/* Dummy routine. Just exit the event loop. */
}
static void
quit(void (*drawscreen) (void))
{
close_graphics();
exit(0);
}
void
close_graphics(void)
{
/* Release all my drawing structures (through the X server) and *
* close down the connection. */
int i;
for(i = 1; i <= MAX_FONT_SIZE; i++)
if(font_is_loaded[i])
XFreeFont(display, font_info[i]);
XFreeGC(display, gc);
XFreeGC(display, gcxor);
XFreeGC(display, gc_menus);
if(private_cmap != None)
XFreeColormap(display, private_cmap);
XCloseDisplay(display);
free(button);
}
int
init_postscript(char *fname)
{
/* Opens a file for PostScript output. The header information, *
* clipping path, etc. are all dumped out. If the file could *
* not be opened, the routine returns 0; otherwise it returns 1. */
ps = fopen(fname, "w");
if(ps == NULL)
{
printf("Error: could not open %s for PostScript output.\n",
fname);
printf("Drawing to screen instead.\n");
return (0);
}
disp_type = POSTSCRIPT; /* Graphics go to postscript file now. */
/* Header for minimal conformance with the Adobe structuring convention */
fprintf(ps, "%%!PS-Adobe-1.0\n");
fprintf(ps, "%%%%DocumentFonts: Helvetica\n");
fprintf(ps, "%%%%Pages: 1\n");
/* Set up postscript transformation macros and page boundaries */
update_ps_transform();
/* Bottom margin is at ps_bot - 15. to leave room for the on-screen message. */
fprintf(ps, "%%%%BoundingBox: %d %d %d %d\n",
(int)ps_left, (int)(ps_bot - 15.), (int)ps_right, (int)ps_top);
fprintf(ps, "%%%%EndComments\n");
fprintf(ps, "/censhow %%draw a centered string\n");
fprintf(ps, " { moveto %% move to proper spot\n");
fprintf(ps, " dup stringwidth pop %% get x length of string\n");
fprintf(ps, " -2 div %% Proper left start\n");
fprintf(ps,
" yoff rmoveto %% Move left that much and down half font height\n");
fprintf(ps, " show newpath } def %% show the string\n\n");
fprintf(ps, "/setfontsize %% set font to desired size and compute "
"centering yoff\n");
fprintf(ps, " { /Helvetica findfont\n");
fprintf(ps, " exch scalefont\n");
fprintf(ps, " setfont %% Font size set ...\n\n");
fprintf(ps, " 0 0 moveto %% Get vertical centering offset\n");
fprintf(ps, " (Xg) true charpath\n");
fprintf(ps, " flattenpath pathbbox\n");
fprintf(ps, " /ascent exch def pop -1 mul /descent exch def pop\n");
fprintf(ps, " newpath\n");
fprintf(ps, " descent ascent sub 2 div /yoff exch def } def\n\n");
fprintf(ps, "%% Next two lines for debugging only.\n");
fprintf(ps, "/str 20 string def\n");
fprintf(ps, "/pnum {str cvs print ( ) print} def\n");
fprintf(ps, "/drawline %% draw a line from (x2,y2) to (x1,y1)\n");
fprintf(ps, " { moveto lineto stroke } def\n\n");
fprintf(ps, "/rect %% outline a rectangle \n");
fprintf(ps, " { /y2 exch def /x2 exch def /y1 exch def /x1 exch def\n");
fprintf(ps, " x1 y1 moveto\n");
fprintf(ps, " x2 y1 lineto\n");
fprintf(ps, " x2 y2 lineto\n");
fprintf(ps, " x1 y2 lineto\n");
fprintf(ps, " closepath } def\n\n");
fprintf(ps, "/drawrect %% draw outline of a rectanagle\n");
fprintf(ps, " { rect stroke } def\n\n");
fprintf(ps, "/fillrect %% fill in a rectanagle\n");
fprintf(ps, " { rect fill } def\n\n");
fprintf(ps, "/drawarc { arc stroke } def %% draw an arc\n");
fprintf(ps, "/drawarcn { arcn stroke } def "
" %% draw an arc in the opposite direction\n\n");
fprintf(ps, "%%Fill a counterclockwise or clockwise arc sector, "
"respectively.\n");
fprintf(ps,
"/fillarc { moveto currentpoint 5 2 roll arc closepath fill } "
"def\n");
fprintf(ps,
"/fillarcn { moveto currentpoint 5 2 roll arcn closepath fill } "
"def\n\n");
fprintf(ps,
"/fillpoly { 3 1 roll moveto %% move to first point\n"
" 2 exch 1 exch {pop lineto} for %% line to all other points\n"
" closepath fill } def\n\n");
fprintf(ps, "%%Color Definitions:\n");
fprintf(ps, "/white { 1 setgray } def\n");
fprintf(ps, "/black { 0 setgray } def\n");
fprintf(ps, "/grey55 { .55 setgray } def\n");
fprintf(ps, "/grey75 { .75 setgray } def\n");
fprintf(ps, "/blue { 0 0 1 setrgbcolor } def\n");
fprintf(ps, "/green { 0 1 0 setrgbcolor } def\n");
fprintf(ps, "/yellow { 1 1 0 setrgbcolor } def\n");
fprintf(ps, "/cyan { 0 1 1 setrgbcolor } def\n");
fprintf(ps, "/red { 1 0 0 setrgbcolor } def\n");
fprintf(ps, "/darkgreen { 0 0.5 0 setrgbcolor } def\n");
fprintf(ps, "/magenta { 1 0 1 setrgbcolor } def\n");
fprintf(ps, "/bisque { 1 0.9 0.8 setrgbcolor } def\n");
fprintf(ps, "/lightblue { 0.7 0.8 0.9 setrgbcolor } def\n");
fprintf(ps, "/thistle { 0.8 0.7 0.8 setrgbcolor } def\n");
fprintf(ps, "/plum {0.8 0.6 0.8 setrgbcolor } def\n");
fprintf(ps, "/khaki { 1 0.9 0.6 setrgbcolor } def\n");
fprintf(ps, "/coral { 1 0.7 0.6 setrgbcolor } def\n");
fprintf(ps, "/turquoise { 0.5 0.6 0.9 setrgbcolor } def\n");
fprintf(ps, "/mediumpurple { 0.7 0.6 0.7 setrgbcolor } def\n");
fprintf(ps, "/darkslateblue { 0.7 0.5 0.7 setrgbcolor } def\n");
fprintf(ps, "/darkkhaki { 0.9 0.7 0.4 setrgbcolor } def\n");
fprintf(ps, "\n%%Solid and dashed line definitions:\n");
fprintf(ps, "/linesolid {[] 0 setdash} def\n");
fprintf(ps, "/linedashed {[3 3] 0 setdash} def\n");
fprintf(ps, "\n%%%%EndProlog\n");
fprintf(ps, "%%%%Page: 1 1\n\n");
/* Set up PostScript graphics state to match current one. */
force_setcolor(currentcolor);
force_setlinestyle(currentlinestyle);
force_setlinewidth(currentlinewidth);
force_setfontsize(currentfontsize);
/* Draw this in the bottom margin -- must do before the clippath is set */
draw_message();
/* Set clipping on page. */
fprintf(ps, "%.2f %.2f %.2f %.2f rect ", ps_left, ps_bot, ps_right,
ps_top);
fprintf(ps, "clip newpath\n\n");
return (1);
}
void
close_postscript(void)
{
/* Properly ends postscript output and redirects output to screen. */
fprintf(ps, "showpage\n");
fprintf(ps, "\n%%%%Trailer\n");
fclose(ps);
disp_type = SCREEN;
update_transform(); /* Ensure screen world reflects any changes *
* made while printing. */
/* Need to make sure that we really set up the graphics context -- *
* don't want the change requested to match the current setting and *
* do nothing -> force the changes. */
force_setcolor(currentcolor);
force_setlinestyle(currentlinestyle);
force_setlinewidth(currentlinewidth);
force_setfontsize(currentfontsize);
}
#else /* NO_GRAPHICS build -- rip out graphics */
void
event_loop(void (*act_on_button) (float x,
float y),
void (*drawscreen) (void))
{
}
void
init_graphics(char *window_name)
{
}
void
close_graphics(void)
{
}
void
update_message(char *msg)
{
}
void
draw_message(void)
{
}
void
init_world(float xl,
float yt,
float xr,
float yb)
{
}
void
flushinput(void)
{
}
void
setcolor(int cindex)
{
}
int
getcolor(int cindex)
{
return 0;
}
void
setlinestyle(int linestyle)
{
}
void
setlinewidth(int linewidth)
{
}
void
setfontsize(int pointsize)
{
}
void
drawline(float x1,
float y1,
float x2,
float y2)
{
}
void
drawrect(float x1,
float y1,
float x2,
float y2)
{
}
void
fillrect(float x1,
float y1,
float x2,
float y2)
{
}
void
fillpoly(t_point * points,
int npoints)
{
}
void
drawarc(float xcen,
float ycen,
float rad,
float startang,
float angextent)
{
}
void
fillarc(float xcen,
float ycen,
float rad,
float startang,
float angextent)
{
}
void
drawtext(float xc,
float yc,
const char *text,
float boundx)
{
}
void
clearscreen(void)
{
}
void
create_button(char *prev_button_text,
char *button_text,
void (*button_func) (void (*drawscreen) (void)))
{
}
void
destroy_button(char *button_text)
{
}
int
init_postscript(char *fname)
{
return (1);
}
void
close_postscript(void)
{
}
#endif