OpenFPGA/vpr7_x2p/vpr/SRC/base/graphics.c

3992 lines
114 KiB
C
Raw Normal View History

2018-07-26 12:28:21 -05:00
/*
* Easygl Version 2.0.1
* Written by Vaughn Betz at the University of Toronto, Department of *
* Electrical and Computer Engineering, with additions by Paul Leventis *
* and William Chow of Altera, and Guy Lemieux of the University of *
* Brish Columbia. *
* 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 author info above in it.
* *
* Revision History: *
* *
* V2.0.1 Sept. 2012 (Vaughn Betz)
* - Fixed a bug in Win32 where postscript output would make the graphics
* crash when you redrew.
* - Made a cleaner makefile to simplify platform selection.
* - Commented and reorganized some of the code. Started cleaning up some of the
* win32 code. Win32 looks inefficient; it is saving and updating graphics contexts
* all the time even though we know when the context is valid vs. not-valid.
* TODO: make win32 work more like X11 (minimize gc updates).
*
* V2.0: Nov. 21, 2011 (Vaughn Betz)
* - Updated example code, and some cleanup and bug fixes to win32 code.
* - Removed some win32 code that had no X11 equivalent or wasn't well
* documented.
* - Used const char * where appropriate to get rid of g++ warnings.
* - Made interface to things like xor drawing more consistent, and added to
* example program.
* - Made a simpler (easygl.cpp) interface to the graphics library for
* use by undergraduate students.
*
* V1.06 : July 23, 2003 : (Guy Lemieux)
* - added some typecasts to cleanly compile with g++ and MS c++ tools
* - if WIN32 not defined, it defines X11 automatically
* - fixed X11 compilation; WIN32 broke some things
*
* V1.05 : July 26, 2001 : (William) *
* - changed keyboard detect function to accept an int (virtual key) *
* *
* V1.04 : June 29, 2001 : (William) *
* - added drawcurve(), fillcurve() using Bezier curves *
* (support WIN32 screen / ps) *
* - added pt on object capability : using a memory buffer to draw an *
* graphics objects, then query if a point fall on the object (bear the *
* object's colour) : object_start(), object_end(), pt_on_object() *
* - added drawellipticarc(), fillellipticarc() *
* - added findfontsize() to help find a pointsize of a given height *
* - extended t_report to keep xleft, xright, ytop, ybot *
* - added update_window() to set the window bb *
* *
* V1.03 : June 18, 2001 : (William) *
* - added change_button_text() *
* *
* V1.02 : June 13, 2001 : (William) *
* - extension to mouse click function : can tell if ctrl/shift keys are *
* pressed *
* *
* V1.01 : June 1, 2001 : (William) *
* - add tooltip support *
* *
* V1.0 : May 14, 2001 : (William) *
* - fixed a problem with line styles, initial release on the internet *
* *
* March 27, 2001 : (William) *
* - added setcolor_by_colorref to make more colors available (in Win32) *
* *
* February 16, 2001 : (William) *
* - added quick zoom using right mouse clicks *
* *
* February 11, 2001 : (William) *
* - can define cleanup(), passed in when calling init_graphics(), and *
* called when shutting down *
* *
* February 1, 2001 : (William) *
* - fix xor mode redraw problem *
* *
* September 19, 2000 : (William) *
* - can define mouse_move callback function *
* - can add separators in between buttons *
* *
* September 8, 2000 : (William) *
* - added result_structure(), *
* - can define background color in init_graphics *
* *
* August 10, 2000 : (William Chow, choww@eecg.utoronto.ca) *
* - Finished all Win32 support functions *
* - use XOR mode for window zooming box *
* - added double buffering feature *
* *
* January 12, 1999: (Paul) *
* - Fixed a bunch of stuff with the Win32 support (memory leaks, etc) *
* - Made the clipping function using the update rectangle for Win32 *
* *
* January 9, 1999: (Paul Leventis, leventi@eecg.utoronto.ca) *
* - Added Win32 support. Should work under Windows98/95/NT 4.0/NT 5.0. *
* - Added a check to deselect_all to determine whether the screen needs to *
* be updated or not. Should elminate flicker from mouse clicks *
* - Added invalidate_screen() call to graphics.c that in turn calls *
* update_screen, so this function was made non-static and added to the *
* header file. This is due to differences in the structure of Win32 *
* windowing apps. *
* - Win32 needs clipping (though done automatically, could be faster) *
* *
* 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_and destroy_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. */
#ifndef NO_GRAPHICS // Strip everything out and just put in stubs if NO_GRAPHICS defined
/**********************************
* Common Preprocessor Directives *
**********************************/
#define TRUE 1
#define FALSE 0
#include <math.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <string>
#include <iostream>
#include "graphics.h"
using namespace std;
#if defined(X11) || defined(WIN32)
/* 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)*xdiv + xleft)
#define YTOWORLD(y) (((float) y)*ydiv + ytop)
#ifndef max
#define max(a,b) (((a) > (b))? (a) : (b))
#endif
#ifndef min
#define min(a,b) ((a) > (b)? (b) : (a))
#endif
#define MWIDTH 104 /* width of menu window */
#define T_AREA_HEIGHT 24 /* Height of text window */
#define MAX_FONT_SIZE 24 /* Largest point size of text. */
// Some computers only have up to 24 point
#define PI 3.141592654
#define BUTTON_TEXT_LEN 100
#define BUFSIZE 1000
#endif
/*********************************************
* X-Windows Specific Preprocessor Directives *
*********************************************/
#ifdef X11
#include <X11/Xlib.h>
#include <X11/Xutil.h>
#include <X11/Xos.h>
#include <X11/Xatom.h>
/* Uncomment the line below if your X11 header files don't define XPointer */
/* typedef char *XPointer; */
// Really large pixel values cna make some X11 implementations draw crazy things
// (internal overflow in the X11 library). Use these constants to clip.
#define MAXPIXEL 15000
#define MINPIXEL -15000
#endif /* X11 Preprocessor Directives */
/*************************************************************
* Microsoft Windows (WIN32) Specific Preprocessor Directives *
*************************************************************/
#ifdef WIN32
#pragma warning(disable : 4996) // Turn off annoying warnings about strcmp.
#include <windows.h>
// Lines below are for displaying errors in a message box on windows.
#define SELECT_ERROR() { char msg[BUFSIZE]; sprintf (msg, "Error %i: Couldn't select graphics object on line %d of graphics.c\n", GetLastError(), __LINE__); MessageBox(NULL, msg, NULL, MB_OK); exit(-1); }
#define DELETE_ERROR() { char msg[BUFSIZE]; sprintf (msg, "Error %i: Couldn't delete graphics object on line %d of graphics.c\n", GetLastError(), __LINE__); MessageBox(NULL, msg, NULL, MB_OK); exit(-1); }
#define CREATE_ERROR() { char msg[BUFSIZE]; sprintf (msg, "Error %i: Couldn't create graphics object on line %d of graphics.c\n", GetLastError(), __LINE__); MessageBox(NULL, msg, NULL, MB_OK); exit(-1); }
#define DRAW_ERROR() { char msg[BUFSIZE]; sprintf (msg, "Error %i: Couldn't draw graphics object on line %d of graphics.c\n", GetLastError(), __LINE__); MessageBox(NULL, msg, NULL, MB_OK); exit(-1); }
/* Avoid funny clipping problems under windows that I suspect are caused by round-off
* in the Win32 libraries.
*/
#define MAXPIXEL 3000
#define MINPIXEL -3000
#define DEGTORAD(x) ((x)/180.*PI)
#define FONTMAG 1.3
#endif /* Win32 preprocessor Directives */
/*************************************************************
* Common Type Definitions *
*************************************************************/
/* Used to define where the output of drawscreen (graphics primitives in the user-controlled
* area) currently goes to: the screen or a postscript file.
*/
typedef enum {
SCREEN = 0,
POSTSCRIPT = 1
} t_display_type;
/* Indicates if this button displays text, a polygon or is just a separator.
*/
typedef enum {
BUTTON_TEXT = 0,
BUTTON_POLY,
BUTTON_SEPARATOR
} t_button_type;
/* Structure used to define the buttons on the right hand side of the main window (menu region).
* width, height: button size, in pixels.
* xleft, ytop: coordinates, in pixels, of the top-left corner of the button relative to its
* containing (menu) window.
* fcn: a callback function that is called when the button is pressed. This function takes one
* argument, a function pointer to the routine that can draw the graphics area (user routine).
* win, hwnd: X11 and Win32 data pointer to the window, respectively.
* button_type: indicates if this button displays text, a polygon or is just a separator.
* text: the text to display if this is a text button.
* poly: the polygon (up to 3 points right now) to display if this is a polygon button
* is_pressed: has the button been pressed, and is currently executing its callback?
* is_enabled: can you press this button right now? Visually will look "pushed in" when
* not enabled, and won't respond to clicks.
*/
typedef struct {
int width;
int height;
int xleft;
int ytop;
void (*fcn) (void (*drawscreen) (void));
#ifdef X11
Window win;
#else
HWND hwnd;
#endif
t_button_type type;
char text[BUTTON_TEXT_LEN];
int poly[3][2];
bool ispressed;
bool enabled;
} t_button;
/* Structure used to store overall graphics state variables.
* TODO: Gradually move more file scope variables in here.
* initialized: true if the graphics window & state have been
* created and initialized, false otherwise.
* disp_type: Selects SCREEN or POSTSCRIPT
* background_cindex: index of the window (or page for PS) background colour
*/
typedef struct {
bool initialized;
int disp_type;
int background_cindex;
} t_gl_state;
/*********************************************************************
* File scope variables. TODO: group in structs *
*********************************************************************/
// Need to initialize graphics_loaded to false, since checking it is
// how we avoid multiple construction or destruction of the graphics
// window.
static t_gl_state gl_state = {false, SCREEN, 0};
static const int menu_font_size = 12; /* Font for menus and dialog boxes. */
static t_button *button = NULL; /* [0..num_buttons-1] */
static int num_buttons = 0; /* Number of menu buttons */
static int display_width, display_height; /* screen size */
static int top_width, top_height; /* window size */
static float xleft, xright, ytop, ybot; /* 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 float xdiv, ydiv;
static int currentcolor;
static int currentlinestyle;
static int currentlinewidth;
static int currentfontsize;
static e_draw_mode current_draw_mode;
/* For PostScript output */
static FILE *ps;
static int ProceedPressed;
static char statusMessage[BUFSIZE] = ""; /* User message to display */
static bool font_is_loaded[MAX_FONT_SIZE + 1];
static bool get_keypress_input, get_mouse_move_input;
static const 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"};
/*********************************************
* Common Subroutine Declarations *
*********************************************/
static void *my_malloc(int ibytes);
static void *my_realloc(void *memblk, int ibytes);
static int xcoord (float worldx);
static int ycoord (float worldy);
static void force_setcolor(int cindex);
static void force_setlinestyle(int linestyle);
static void force_setlinewidth(int linewidth);
static void force_setfontsize (int pointsize);
static void load_font(int pointsize);
static void reset_common_state ();
static void build_default_menu (void);
/* 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 void map_button (int bnum);
static void unmap_button (int bnum);
#ifdef X11
/*************************************************
* X-Windows Specific File-scope Variables *
**************************************************/
static Display *display;
static int screen_num;
static GC gc, gcxor, gc_menus, current_gc;
static XFontStruct *font_info[MAX_FONT_SIZE+1]; /* Data for each size */
static Window toplevel, menu, textarea; /* various windows */
static Colormap private_cmap; /* "None" unless a private cmap was allocated. */
/* Color indices passed back from X Windows. */
static int colors[NUM_COLOR];
/****************************************************
* X-Windows Specific Subroutine Declarations *
*****************************************************/
static Bool test_if_exposed (Display *disp, XEvent *event_ptr,
XPointer dummy);
static void build_textarea (void);
static void drawbut (int bnum);
static int which_button (Window win);
static void turn_on_off (int pressed);
static void drawmenu(void);
#endif /* X11 Declarations */
#ifdef WIN32
/*****************************************************
* Microsoft Windows (Win32) File Scope Variables *
*****************************************************/
static const int win32_line_styles[2] = { PS_SOLID, PS_DASH };
static const COLORREF win32_colors[NUM_COLOR] = { RGB(255, 255, 255),
RGB(0, 0, 0), RGB(128, 128, 128), RGB(192, 192, 192), RGB(0, 0, 255),
RGB(0, 255, 0), RGB(255, 255, 0), RGB(0, 255, 255), RGB(255, 0, 0), RGB(0, 128, 0),
RGB(255, 0, 255), RGB(255, 228, 196), RGB(173, 216, 230), RGB(216, 191, 216), RGB(221, 160, 221),
RGB(240, 230, 140), RGB(255, 127, 80), RGB(64, 224, 208), RGB(147, 112, 219), RGB(72, 61, 139),
RGB(189, 183, 107)};
static TCHAR szAppName[256],
szGraphicsName[] = TEXT("VPR Graphics"),
szStatusName[] = TEXT("VPR Status"),
szButtonsName[] = TEXT("VPR Buttons");
static HPEN hGraphicsPen;
static HBRUSH hGraphicsBrush, hGrayBrush;
static HDC hGraphicsDC, hForegroundDC, hBackgroundDC,
hCurrentDC, /* WC : double-buffer */
hObjtestDC, hAllObjtestDC; /* object test */
/* WC */
static HFONT hGraphicsFont;
static LOGFONT *font_info[MAX_FONT_SIZE+1]; /* Data for each size */
/* Handles to the top level window and 3 subwindows. */
static HWND hMainWnd, hGraphicsWnd, hButtonsWnd, hStatusWnd;
static int cxClient, cyClient;
/* These are used for the "Window" graphics button. They keep track of whether we're entering
* the window rectangle to zoom to, etc.
*/
static int windowAdjustFlag = 0, adjustButton = -1;
static RECT adjustRect, updateRect;
static boolean InEventLoop = FALSE;
//static HBITMAP buttonImages[4];
/*******************************************************************
* Win32-specific subroutine declarations *
*******************************************************************/
/* Callback functions for the top-level window and 3 sub-windows.
* Windows uses an odd mix of events and callbacks, so it needs these.
*/
static LRESULT CALLBACK GraphicsWND(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam);
static LRESULT CALLBACK StatusWND(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam);
static LRESULT CALLBACK ButtonsWND(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam);
static LRESULT CALLBACK MainWND(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam);
// For Win32, need to save pointers to these callback functions at file
// scope, since windows has a bizarre event loop structure where you poll
// for events, but then dispatch the event and get called via a callback
// from windows (GraphicsWND, below). I can't figure out why windows
// does things this way, but it is what makes saving these function pointers
// necessary. VB.
static void (*mouseclick_ptr)(float x, float y);
static void (*mousemove_ptr)(float x, float y);
static void (*keypress_ptr)(char entered_char);
static void (*drawscreen_ptr)(void);
static void invalidate_screen();
static void reset_win32_state ();
static void win32_drain_message_queue ();
void drawtoscreen();
void displaybuffer();
#endif /* Win32 Declarations */
/*********************************************************
* Common Subroutine Definitions *
*********************************************************/
/* safer malloc */
static void *my_malloc(int ibytes) {
void *mem;
mem = (void*)malloc(ibytes);
if (mem == NULL) {
printf("memory allocation failed!");
exit(-1);
}
return mem;
}
/* safer realloc */
static void *my_realloc(void *memblk, int ibytes) {
void *mem;
mem = (void*)realloc(memblk, ibytes);
if (mem == NULL) {
printf("memory allocation failed!");
exit(-1);
}
return mem;
}
/* Translates from my internal coordinates to real-world coordinates *
* in the x direction. Add 0.5 at end for extra half-pixel accuracy. */
static int xcoord (float worldx)
{
int winx;
winx = (int) ((worldx-xleft)*xmult + 0.5);
/* Avoids overflow in the 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 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);
}
/* Translates from my internal coordinates to real-world coordinates *
* in the y direction. Add 0.5 at end for extra half-pixel accuracy. */
static int ycoord (float worldy)
{
int winy;
winy = (int) ((worldy-ytop)*ymult + 0.5);
/* Avoid overflow in the X/Win32 Window routines. */
winy = max (winy, MINPIXEL);
winy = min (winy, MAXPIXEL);
return (winy);
}
#ifdef WIN32
static void invalidate_screen(void)
{
/* Tells the graphics engine to redraw the graphics display since information has changed */
if(!InvalidateRect(hGraphicsWnd, NULL, FALSE))
DRAW_ERROR();
if(!UpdateWindow(hGraphicsWnd))
DRAW_ERROR();
}
#endif
/* Sets the current graphics context colour to cindex, regardless of whether we think it is
* needed or not.
*/
static void force_setcolor (int cindex)
{
currentcolor = cindex;
if (gl_state.disp_type == SCREEN) {
#ifdef X11
XSetForeground (display, current_gc, colors[cindex]);
#else /* Win32 */
int win_linestyle;
LOGBRUSH lb;
lb.lbStyle = BS_SOLID;
lb.lbColor = win32_colors[cindex];
lb.lbHatch = (LONG)NULL;
win_linestyle = win32_line_styles[currentlinestyle];
if(!DeleteObject(hGraphicsPen))
DELETE_ERROR();
int linewidth = max (currentlinewidth, 1); // Win32 won't draw 0 width dashed lines.
hGraphicsPen = ExtCreatePen(PS_GEOMETRIC | win_linestyle |
PS_ENDCAP_FLAT, linewidth, &lb, (LONG)NULL, NULL);
if(!hGraphicsPen)
CREATE_ERROR();
if(!DeleteObject(hGraphicsBrush))
DELETE_ERROR();
hGraphicsBrush = CreateSolidBrush(win32_colors[currentcolor]);
if(!hGraphicsBrush)
CREATE_ERROR();
#endif
}
else {
fprintf (ps,"%s\n", ps_cnames[cindex]);
}
}
/* Sets the current graphics context colour to cindex if it differs from the old colour */
void setcolor (int cindex)
{
if (currentcolor != cindex)
force_setcolor (cindex);
}
/* Sets the current graphics context color to the index that corresponds to the
* string name passed in. Slower, but maybe more convenient for simple
* client code.
*/
void setcolor (string cname) {
int icolor = -1;
for (int i = 0; i < NUM_COLOR; i++) {
if (cname == ps_cnames[i]) {
icolor = i;
break;
}
}
if (icolor == -1) {
cout << "Error: unknown color " << cname << endl;
}
else {
setcolor (icolor);
}
}
int getcolor() {
return currentcolor;
}
/* Sets the current linestyle to linestyle in the graphics context.
* Note SOLID is 0 and DASHED is 1 for linestyle.
*/
static void force_setlinestyle (int linestyle)
{
currentlinestyle = linestyle;
if (gl_state.disp_type == SCREEN) {
#ifdef X11
static int x_vals[2] = {LineSolid, LineOnOffDash};
XSetLineAttributes (display, current_gc, currentlinewidth, x_vals[linestyle],
CapButt, JoinMiter);
#else // Win32
LOGBRUSH lb;
lb.lbStyle = BS_SOLID;
lb.lbColor = win32_colors[currentcolor];
lb.lbHatch = (LONG)NULL;
int win_linestyle = win32_line_styles[linestyle];
if(!DeleteObject(hGraphicsPen))
DELETE_ERROR();
int linewidth = max (currentlinewidth, 1); // Win32 won't draw 0 width dashed lines.
hGraphicsPen = ExtCreatePen(PS_GEOMETRIC | win_linestyle |
PS_ENDCAP_FLAT, linewidth, &lb, (LONG)NULL, NULL);
if(!hGraphicsPen)
CREATE_ERROR();
#endif
}
else {
if (linestyle == SOLID)
fprintf (ps,"linesolid\n");
else if (linestyle == DASHED)
fprintf (ps, "linedashed\n");
else {
printf ("Error: invalid linestyle: %d\n", linestyle);
exit (1);
}
}
}
/* Change the linestyle in the graphics context only if it differs from the current
* linestyle.
*/
void setlinestyle (int linestyle)
{
if (linestyle != currentlinestyle)
force_setlinestyle (linestyle);
}
/* Sets current linewidth in the graphics context.
* linewidth should be greater than or equal to 0 to make any sense.
*/
static void force_setlinewidth (int linewidth)
{
currentlinewidth = linewidth;
if (gl_state.disp_type == SCREEN) {
#ifdef X11
static int x_vals[2] = {LineSolid, LineOnOffDash};
XSetLineAttributes (display, current_gc, linewidth, x_vals[currentlinestyle],
CapButt, JoinMiter);
#else /* Win32 */
LOGBRUSH lb;
lb.lbStyle = BS_SOLID;
lb.lbColor = win32_colors[currentcolor];
lb.lbHatch = (LONG)NULL;
int win_linestyle = win32_line_styles[currentlinestyle];
if(!DeleteObject(hGraphicsPen))
DELETE_ERROR();
if (linewidth == 0)
linewidth = 1; // Win32 won't draw dashed 0 width lines.
hGraphicsPen = ExtCreatePen(PS_GEOMETRIC | win_linestyle |
PS_ENDCAP_FLAT, linewidth, &lb, (LONG)NULL, NULL);
if(!hGraphicsPen)
CREATE_ERROR();
#endif
}
else {
fprintf(ps,"%d setlinewidth\n", linewidth);
}
}
/* Sets the linewidth in the grahpics context, if it differs from the current value.
*/
void setlinewidth (int linewidth)
{
if (linewidth != currentlinewidth)
force_setlinewidth (linewidth);
}
/* Force the selected fontsize to be applied to the graphics context,
* whether or not it appears to match the current fontsize. This is necessary
* when switching between postscript and window out, for example.
* Valid point sizes are between 1 and MAX_FONT_SIZE.
*/
static void force_setfontsize (int pointsize)
{
if (pointsize < 1)
pointsize = 1;
#ifdef WIN32
pointsize = (int)((float)pointsize * FONTMAG);
#endif
if (pointsize > MAX_FONT_SIZE)
pointsize = MAX_FONT_SIZE;
currentfontsize = pointsize;
if (gl_state.disp_type == SCREEN) {
load_font (pointsize);
#ifdef X11
XSetFont(display, current_gc, font_info[pointsize]->fid);
#else /* Win32 */
if(!DeleteObject(hGraphicsFont))
DELETE_ERROR();
hGraphicsFont = CreateFontIndirect(font_info[pointsize]);
if(!hGraphicsFont)
CREATE_ERROR();
if(!SelectObject(hGraphicsDC, hGraphicsFont) )
SELECT_ERROR();
#endif
}
else {
/* PostScript: set up font and centering function */
fprintf(ps,"%d setfontsize\n",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).
*/
void setfontsize (int pointsize)
{
if (pointsize != currentfontsize)
force_setfontsize (pointsize);
}
/* Puts a triangle in the poly array for button[bnum]. Haven't made this work for
* win32 yet and instead put "U", "D" excetra on the arrow buttons.
* VB To-do: make work for win32 someday.
*/
#ifdef X11
static void setpoly (int bnum, int xc, int yc, int r, float theta)
{
int i;
button[bnum].type = BUTTON_POLY;
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 += (float)(2*PI/3);
}
}
#endif // X11
/* Maps a button onto the screen and set it up for input, etc. */
static void map_button (int bnum)
{
button[bnum].ispressed = 0;
if (button[bnum].type != BUTTON_SEPARATOR) {
#ifdef X11
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);
#else
button[bnum].hwnd = CreateWindow( TEXT("button"), TEXT(button[bnum].text),
WS_CHILD | WS_VISIBLE | BS_PUSHBUTTON, button[bnum].xleft, button[bnum].ytop,
button[bnum].width, button[bnum].height, hButtonsWnd, (HMENU)(200+bnum),
(HINSTANCE) GetWindowLong(hMainWnd, GWL_HINSTANCE), NULL);
if(!InvalidateRect(hButtonsWnd, NULL, TRUE))
DRAW_ERROR();
if(!UpdateWindow(hButtonsWnd))
DRAW_ERROR();
#endif
}
else { // Separator, not a button.
#ifdef X11
button[bnum].win = -1;
#else // WIN32
button[bnum].hwnd = NULL;
if(!InvalidateRect(hButtonsWnd, NULL, TRUE))
DRAW_ERROR();
if(!UpdateWindow(hButtonsWnd))
DRAW_ERROR();
#endif
}
}
static void unmap_button (int bnum)
{
/* Unmaps (removes) a button from the screen. */
if (button[bnum].type != BUTTON_SEPARATOR) {
#ifdef X11
XUnmapWindow (display, button[bnum].win);
#else
if(!DestroyWindow(button[bnum].hwnd))
DRAW_ERROR();
if(!InvalidateRect(hButtonsWnd, NULL, TRUE))
DRAW_ERROR();
if(!UpdateWindow(hButtonsWnd))
DRAW_ERROR();
#endif
}
}
/* 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. */
void create_button (const char *prev_button_text , const char *button_text,
void (*button_func) (void (*drawscreen) (void)))
{
int i, bnum, space, bheight;
t_button_type button_type = BUTTON_TEXT;
space = 8;
/* Only allow new buttons that are text or separator (not poly) types.
* They can also only go after buttons that are text buttons.
*/
bnum = -1;
for (i=0; i < num_buttons;i++) {
if (button[i].type == BUTTON_TEXT &&
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);
}
button = (t_button *) my_realloc (button, (num_buttons+1) * 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. */
/* Special string to make a separator. */
if (!strncmp(button_text, "---", 3)) {
bheight = 2;
button_type = BUTTON_SEPARATOR;
}
else
bheight = 26;
for (i=num_buttons;i>bnum;i--) {
button[i].xleft = button[i-1].xleft;
button[i].ytop = button[i-1].ytop + bheight + space;
button[i].height = button[i-1].height;
button[i].width = button[i-1].width;
button[i].type = button[i-1].type;
strcpy (button[i].text, button[i-1].text);
button[i].fcn = button[i-1].fcn;
button[i].ispressed = button[i-1].ispressed;
button[i].enabled = button[i-1].enabled;
unmap_button (i-1);
}
i = bnum;
button[i].xleft = 6;
button[i].ytop = button[i-1].ytop + button[i-1].height + space;
button[i].height = bheight;
button[i].width = 90;
button[i].type = button_type;
strncpy (button[i].text, button_text, BUTTON_TEXT_LEN);
button[i].fcn = button_func;
button[i].ispressed = false;
button[i].enabled = true;
num_buttons++;
for (i = 0; i<num_buttons;i++)
map_button (i);
}
/* Destroys the button with text button_text. */
void
destroy_button (const char *button_text)
{
int i, bnum, space, bheight;
bnum = -1;
space = 8;
for (i = 0; i < num_buttons; i++) {
if (button[i].type == BUTTON_TEXT &&
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);
}
bheight = button[bnum].height;
for (i=bnum+1;i<num_buttons;i++) {
button[i-1].xleft = button[i].xleft;
button[i-1].ytop = button[i].ytop - bheight - space;
button[i-1].height = button[i].height;
button[i-1].width = button[i].width;
button[i-1].type = button[i].type;
strcpy (button[i-1].text, button[i].text);
button[i-1].fcn = button[i].fcn;
button[i-1].ispressed = button[i].ispressed;
button[i-1].enabled = button[i].enabled;
unmap_button (i);
}
unmap_button(bnum);
button = (t_button *) my_realloc (button, (num_buttons-1) * sizeof (t_button));
num_buttons--;
for (i=bnum; i<num_buttons;i++)
map_button (i);
}
/* 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. */
void
init_graphics (const char *window_name, int cindex)
{
if (gl_state.initialized) // Singleton graphics.
return;
reset_common_state ();
#ifdef WIN32
reset_win32_state ();
#endif
gl_state.disp_type = SCREEN;
gl_state.background_cindex = cindex;
#ifdef X11
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. */
const 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;
unsigned long valuemask = 0; /* ignore XGCvalues and use defaults */
XGCValues values;
XEvent event;
/* 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 = 0;
y = 0;
top_width = 2 * display_width / 3;
top_height = 4 * display_height / 5;
cmap = DefaultColormap(display, screen_num);
private_cmap = None;
for (int 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;
} // End setting up colours
toplevel = XCreateSimpleWindow(display,RootWindow(display,screen_num),
x, y, top_width, top_height, border_width, colors[BLACK],
colors[cindex]);
if (private_cmap != None)
XSetWindowColormap (display, toplevel, private_cmap);
XSelectInput (display, toplevel, ExposureMask | StructureNotifyMask |
ButtonPressMask | PointerMotionMask | KeyPressMask);
/* Create default Graphics Contexts. valuemask = 0 -> use defaults. */
current_gc = 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[cindex];
gcxor = XCreateGC(display, toplevel, (GCFunction | GCForeground),
&values);
/* specify font for menus. */
load_font(menu_font_size);
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);
// Need a non-const name to pass to XStringListTo...
// (even though X11 won't change it).
char *window_name_copy = (char *) my_malloc (BUFSIZE * sizeof (char));
strncpy (window_name_copy, window_name, BUFSIZE);
XStringListToTextProperty(&window_name_copy, 1, &windowName);
free (window_name_copy);
window_name_copy = NULL;
XSetWMName (display, toplevel, &windowName);
/* 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);
#else /* WIN32 */
WNDCLASS wndclass;
HINSTANCE hInstance = GetModuleHandle(NULL);
int x, y;
LOGBRUSH lb;
lb.lbStyle = BS_SOLID;
lb.lbColor = win32_colors[currentcolor];
lb.lbHatch = (LONG)NULL;
x = 0;
y = 0;
/* get screen size from display structure macro */
display_width = GetSystemMetrics( SM_CXSCREEN );
if (!(display_width))
CREATE_ERROR();
display_height = GetSystemMetrics( SM_CYSCREEN );
if (!(display_height))
CREATE_ERROR();
top_width = 2*display_width/3;
top_height = 4*display_height/5;
/* Grab the Application name */
wsprintf(szAppName, TEXT(window_name));
//hGraphicsPen = CreatePen(win32_line_styles[SOLID], 1, win32_colors[BLACK]);
hGraphicsPen = ExtCreatePen(PS_GEOMETRIC | win32_line_styles[currentlinestyle] |
PS_ENDCAP_FLAT, 1, &lb, (LONG)NULL, NULL);
if(!hGraphicsPen)
CREATE_ERROR();
hGraphicsBrush = CreateSolidBrush(win32_colors[DARKGREY]);
if(!hGraphicsBrush)
CREATE_ERROR();
hGrayBrush = CreateSolidBrush(win32_colors[LIGHTGREY]);
if(!hGrayBrush)
CREATE_ERROR();
load_font (currentfontsize);
hGraphicsFont = CreateFontIndirect(font_info[currentfontsize]);
if (!hGraphicsFont)
CREATE_ERROR();
/* Register the Main Window class */
wndclass.style = CS_HREDRAW | CS_VREDRAW | CS_OWNDC;
wndclass.lpfnWndProc = MainWND;
wndclass.cbClsExtra = 0;
wndclass.cbWndExtra = 0;
wndclass.hInstance = hInstance;
wndclass.hIcon = LoadIcon (NULL, IDI_APPLICATION);
wndclass.hCursor = LoadCursor( NULL, IDC_ARROW);
wndclass.hbrBackground = (HBRUSH) CreateSolidBrush(win32_colors[cindex]);
wndclass.lpszMenuName = NULL;
wndclass.lpszClassName = szAppName;
if (!RegisterClass(&wndclass)) {
printf ("Error code: %d\n", GetLastError());
MessageBox(NULL, TEXT("Initialization of Windows graphics (init_graphics) failed."),
szAppName, MB_ICONERROR);
exit(-1);
}
/* Register the Graphics Window class */
wndclass.lpfnWndProc = GraphicsWND;
wndclass.hIcon = NULL;
wndclass.lpszClassName = szGraphicsName;
if(!RegisterClass(&wndclass))
DRAW_ERROR();
/* Register the Status Window class */
wndclass.lpfnWndProc = StatusWND;
wndclass.hIcon = NULL;
wndclass.lpszClassName = szStatusName;
wndclass.hbrBackground = hGrayBrush;
if(!RegisterClass(&wndclass))
DRAW_ERROR();
/* Register the Buttons Window class */
wndclass.lpfnWndProc = ButtonsWND;
wndclass.hIcon = NULL;
wndclass.lpszClassName = szButtonsName;
wndclass.hbrBackground = hGrayBrush;
if (!RegisterClass(&wndclass))
DRAW_ERROR();
hMainWnd = CreateWindow(szAppName, TEXT(window_name),
WS_OVERLAPPEDWINDOW, x, y, top_width, top_height,
NULL, NULL, hInstance, NULL);
if(!hMainWnd)
DRAW_ERROR();
/* Set drawing defaults for user-drawable area. Use whatever the *
* initial values of the current stuff was set to. */
if (ShowWindow(hMainWnd, SW_SHOWNORMAL))
DRAW_ERROR();
build_default_menu();
if (!UpdateWindow(hMainWnd))
DRAW_ERROR();
win32_drain_message_queue ();
#endif
gl_state.initialized = true;
}
static void reset_common_state () {
currentcolor = BLACK;
currentlinestyle = SOLID;
currentlinewidth = 0;
currentfontsize = 12;
current_draw_mode = DRAW_NORMAL;
for (int i=0;i<=MAX_FONT_SIZE;i++)
font_is_loaded[i] = false; /* No fonts loaded yet. */
ProceedPressed = false;
get_keypress_input = false;
get_mouse_move_input = false;
}
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 = (top_width - 1 - MWIDTH) / (xright - xleft);
ymult = (top_height - 1 - T_AREA_HEIGHT)/ (ybot - ytop);
/* Need to use same scaling factor to preserve aspect ratio */
if (fabs(xmult) <= fabs(ymult)) {
mult = (float)(fabs(ymult/xmult));
y1 = ytop - (ybot-ytop)*(mult-1)/2;
y2 = ybot + (ybot-ytop)*(mult-1)/2;
ytop = y1;
ybot = y2;
}
else {
mult = (float)(fabs(xmult/ymult));
x1 = xleft - (xright-xleft)*(mult-1)/2;
x2 = xright + (xright-xleft)*(mult-1)/2;
xleft = x1;
xright = x2;
}
xmult = (top_width - 1 - MWIDTH) / (xright - xleft);
ymult = (top_height - 1 - T_AREA_HEIGHT)/ (ybot - ytop);
xdiv = 1/xmult;
ydiv = 1/ymult;
}
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 = (float)(36. + ps_width);
ps_bot = (float)(396. - fabs(ps_xmult * (ytop - ybot))/2);
ps_top = (float)(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 = (float)(36. + ps_height);
ps_left = (float)(306. - fabs(ps_ymult * (xright - xleft))/2);
ps_right = (float)(306. + fabs(ps_ymult * (xright - xleft))/2);
/* Maintain aspect ratio but watch signs */
ps_xmult = (ps_xmult*ps_ymult < 0) ? -ps_ymult : ps_ymult;
}
}
/* 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 mousebutton in the graphics
* (toplevel) area, the act_on_mousebutton routine passed in is called.
*/
void
event_loop (void (*act_on_mousebutton)(float x, float y),
void (*act_on_mousemove)(float x, float y),
void (*act_on_keypress)(char key_pressed),
void (*drawscreen) (void))
{
#ifdef X11
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();
drawmenu();
draw_message();
#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_mousebutton (x, y);
}
else { /* A menu button was pressed. */
bnum = which_button(report.xbutton.window);
#ifdef VERBOSE
printf("Button number is %d\n",bnum);
#endif
if (button[bnum].enabled) {
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;
case MotionNotify:
#ifdef VERBOSE
printf("Got a MotionNotify Event.\n");
printf("x: %d y: %d\n",report.xmotion.x,report.xmotion.y);
#endif
if (get_mouse_move_input &&
report.xmotion.x <= top_width-MWIDTH &&
report.xmotion.y <= top_height-T_AREA_HEIGHT)
act_on_mousemove(XTOWORLD(report.xmotion.x), YTOWORLD(report.xmotion.y));
break;
case KeyPress:
#ifdef VERBOSE
printf("Got a KeyPress Event.\n");
#endif
if (get_keypress_input)
{
char keyb_buffer[20];
XComposeStatus composestatus;
KeySym keysym;
int length, max_bytes;
max_bytes = 1;
length = XLookupString( &report.xkey, keyb_buffer, max_bytes, &keysym,
&composestatus );
keyb_buffer[length] = '\0'; /* terminating NULL */
act_on_keypress(keyb_buffer[0]);
}
break;
}
}
#else /* Win32 */
MSG msg;
mouseclick_ptr = act_on_mousebutton;
mousemove_ptr = act_on_mousemove;
keypress_ptr = act_on_keypress;
drawscreen_ptr = drawscreen;
ProceedPressed = FALSE;
InEventLoop = TRUE;
invalidate_screen();
while(!ProceedPressed && GetMessage(&msg, NULL, 0, 0)) {
//TranslateMessage(&msg);
if (msg.message == WM_CHAR) { // only the top window can get keyboard events
msg.hwnd = hMainWnd;
}
DispatchMessage(&msg);
}
InEventLoop = FALSE;
#endif
}
void
clearscreen (void)
{
int savecolor;
if (gl_state.disp_type == SCREEN) {
#ifdef X11
XClearWindow (display, toplevel);
#else /* Win32 */
savecolor = currentcolor;
setcolor(gl_state.background_cindex);
fillrect (xleft, ytop, xright, ybot);
setcolor(savecolor);
#endif
}
else { // Postscript
/* 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 (gl_state.background_cindex);
fprintf(ps,"clippath fill\n\n");
setcolor (savecolor);
}
}
/* 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. */
static int
rect_off_screen (float x1, float y1, float x2, float y2)
{
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. */
#ifdef WIN32
HPEN hOldPen;
#endif
if (rect_off_screen(x1,y1,x2,y2))
return;
if (gl_state.disp_type == SCREEN) {
#ifdef X11
/* Xlib.h prototype has x2 and y1 mixed up. */
XDrawLine(display, toplevel, current_gc, xcoord(x1), ycoord(y1), xcoord(x2), ycoord(y2));
#else /* Win32 */
hOldPen = (HPEN)SelectObject(hGraphicsDC, hGraphicsPen);
if(!(hOldPen))
SELECT_ERROR();
if (!BeginPath(hGraphicsDC))
DRAW_ERROR();
if(!MoveToEx (hGraphicsDC, xcoord(x1), ycoord(y1), NULL))
DRAW_ERROR();
if(!LineTo (hGraphicsDC, xcoord(x2), ycoord(y2)))
DRAW_ERROR();
if (!EndPath(hGraphicsDC))
DRAW_ERROR();
if (!StrokePath(hGraphicsDC))
DRAW_ERROR();
if(!SelectObject(hGraphicsDC, hOldPen))
SELECT_ERROR();
#endif
}
else {
fprintf(ps,"%.2f %.2f %.2f %.2f drawline\n",XPOST(x1),YPOST(y1),
XPOST(x2),YPOST(y2));
}
}
/* (x1,y1) and (x2,y2) are diagonally opposed corners, in world coords. */
void
drawrect (float x1, float y1, float x2, float y2)
{
int xw1, yw1, xw2, yw2;
#ifdef WIN32
HPEN hOldPen;
HBRUSH hOldBrush;
#else
unsigned int width, height;
int xl, yt;
#endif
if (rect_off_screen(x1,y1,x2,y2))
return;
if (gl_state.disp_type == SCREEN) {
/* translate to X Windows calling convention. */
xw1 = xcoord(x1);
xw2 = xcoord(x2);
yw1 = ycoord(y1);
yw2 = ycoord(y2);
#ifdef X11
xl = min(xw1,xw2);
yt = min(yw1,yw2);
width = abs (xw1-xw2);
height = abs (yw1-yw2);
XDrawRectangle(display, toplevel, current_gc, xl, yt, width, height);
#else /* Win32 */
if(xw1 > xw2) {
int temp = xw1;
xw1 = xw2;
xw2 = temp;
}
if(yw1 > yw2) {
int temp = yw1;
yw1 = yw2;
yw2 = temp;
}
hOldPen = (HPEN)SelectObject(hGraphicsDC, hGraphicsPen);
if(!(hOldPen))
SELECT_ERROR();
hOldBrush = (HBRUSH)SelectObject(hGraphicsDC, GetStockObject(NULL_BRUSH));
if(!(hOldBrush))
SELECT_ERROR();
if(!Rectangle(hGraphicsDC, xw1, yw1, xw2, yw2))
DRAW_ERROR();
if(!SelectObject(hGraphicsDC, hOldPen))
SELECT_ERROR();
if(!SelectObject(hGraphicsDC, hOldBrush))
SELECT_ERROR();
#endif
}
else {
fprintf(ps,"%.2f %.2f %.2f %.2f drawrect\n",XPOST(x1),YPOST(y1),
XPOST(x2),YPOST(y2));
}
}
/* (x1,y1) and (x2,y2) are diagonally opposed corners in world coords. */
void
fillrect (float x1, float y1, float x2, float y2)
{
int xw1, yw1, xw2, yw2;
#ifdef WIN32
HPEN hOldPen;
HBRUSH hOldBrush;
#else
unsigned int width, height;
int xl, yt;
#endif
if (rect_off_screen(x1,y1,x2,y2))
return;
if (gl_state.disp_type == SCREEN) {
/* translate to X Windows calling convention. */
xw1 = xcoord(x1);
xw2 = xcoord(x2);
yw1 = ycoord(y1);
yw2 = ycoord(y2);
#ifdef X11
xl = min(xw1,xw2);
yt = min(yw1,yw2);
width = abs (xw1-xw2);
height = abs (yw1-yw2);
XFillRectangle(display, toplevel, current_gc, xl, yt, width, height);
#else /* Win32 */
if(xw1 > xw2) {
int temp = xw1;
xw1 = xw2;
xw2 = temp;
}
if(yw1 > yw2) {
int temp = yw1;
yw1 = yw2;
yw2 = temp;
}
hOldPen = (HPEN)SelectObject(hGraphicsDC, hGraphicsPen);
if(!(hOldPen))
SELECT_ERROR();
hOldBrush = (HBRUSH)SelectObject(hGraphicsDC, hGraphicsBrush);
if(!(hOldBrush))
SELECT_ERROR();
if(!Rectangle(hGraphicsDC, xw1, yw1, xw2, yw2))
DRAW_ERROR();
if(!SelectObject(hGraphicsDC, hOldPen))
SELECT_ERROR();
if(!SelectObject(hGraphicsDC, hOldBrush))
SELECT_ERROR();
#endif
}
else {
fprintf(ps,"%.2f %.2f %.2f %.2f fillrect\n",XPOST(x1),YPOST(y1),
XPOST(x2),YPOST(y2));
}
}
/* Normalizes an angle to be between 0 and 360 degrees. */
static float
angnorm (float ang)
{
int scale;
if (ang < 0) {
scale = (int) (ang / 360. - 1);
}
else {
scale = (int) (ang / 360.);
}
ang = ang - scale * 360;
return (ang);
}
void
drawellipticarc (float xc, float yc, float radx, float rady, float startang, float angextent)
{
int xl, yt;
unsigned int width, height;
#ifdef WIN32
HPEN hOldPen;
int p1, p2, p3, p4;
#endif
/* Conservative (but fast) clip test -- check containing rectangle of *
* an ellipse. */
if (rect_off_screen (xc-radx,yc-rady,xc+radx,yc+rady))
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 (gl_state.disp_type == SCREEN) {
xl = (int) (xcoord(xc) - fabs(xmult*radx));
yt = (int) (ycoord(yc) - fabs(ymult*rady));
width = (unsigned int) (2*fabs(xmult*radx));
height = (unsigned int) (2*fabs(ymult*rady));
#ifdef X11
XDrawArc (display, toplevel, current_gc, xl, yt, width, height,
(int) (startang*64), (int) (angextent*64));
#else // Win32
/* set arc direction */
if (angextent > 0) {
p1 = (int)(xcoord(xc) + fabs(xmult*radx)*cos(DEGTORAD(startang)));
p2 = (int)(ycoord(yc) - fabs(ymult*rady)*sin(DEGTORAD(startang)));
p3 = (int)(xcoord(xc) + fabs(xmult*radx)*cos(DEGTORAD(startang+angextent-.001)));
p4 = (int)(ycoord(yc) - fabs(ymult*rady)*sin(DEGTORAD(startang+angextent-.001)));
}
else {
p1 = (int)(xcoord(xc) + fabs(xmult*radx)*cos(DEGTORAD(startang+angextent+.001)));
p2 = (int)(ycoord(yc) - fabs(ymult*rady)*sin(DEGTORAD(startang+angextent+.001)));
p3 = (int)(xcoord(xc) + fabs(xmult*radx)*cos(DEGTORAD(startang)));
p4 = (int)(ycoord(yc) - fabs(ymult*rady)*sin(DEGTORAD(startang)));
}
hOldPen = (HPEN)SelectObject(hGraphicsDC, hGraphicsPen);
if(!(hOldPen))
SELECT_ERROR();
if(!Arc(hGraphicsDC, xl, yt, xl+width, yt+height, p1, p2, p3, p4))
DRAW_ERROR();
if(!SelectObject(hGraphicsDC, hOldPen))
SELECT_ERROR();
#endif
}
else {
fprintf(ps, "gsave\n");
fprintf(ps, "%.2f %.2f translate\n", XPOST(xc), YPOST(yc));
fprintf(ps, "%.2f 1 scale\n", fabs(radx*ps_xmult)/fabs(rady*ps_ymult));
fprintf(ps, "0 0 %.2f %.2f %.2f %s\n", /*XPOST(xc)*/
/*YPOST(yc)*/ fabs(rady*ps_xmult), startang, startang+angextent,
(angextent < 0) ? "drawarcn" : "drawarc") ;
fprintf(ps, "grestore\n");
}
}
/* Startang is relative to the Window's positive x direction. Angles in degrees.
*/
void
drawarc (float xc, float yc, float rad, float startang,
float angextent)
{
drawellipticarc(xc, yc, rad, rad, startang, angextent);
}
/* Fills a elliptic arc. Startang is relative to the Window's positive x
* direction. Angles in degrees.
*/
void
fillellipticarc (float xc, float yc, float radx, float rady, float startang,
float angextent)
{
int xl, yt;
unsigned int width, height;
#ifdef WIN32
HPEN hOldPen;
HBRUSH hOldBrush;
int p1, p2, p3, p4;
#endif
/* Conservative (but fast) clip test -- check containing rectangle of *
* a circle. */
if (rect_off_screen (xc-radx,yc-rady,xc+radx,yc+rady))
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 (gl_state.disp_type == SCREEN) {
xl = (int) (xcoord(xc) - fabs(xmult*radx));
yt = (int) (ycoord(yc) - fabs(ymult*rady));
width = (unsigned int) (2*fabs(xmult*radx));
height = (unsigned int) (2*fabs(ymult*rady));
#ifdef X11
XFillArc (display, toplevel, current_gc, xl, yt, width, height,
(int) (startang*64), (int) (angextent*64));
#else // Win32
/* set pie direction */
if (angextent > 0) {
p1 = (int)(xcoord(xc) + fabs(xmult*radx)*cos(DEGTORAD(startang)));
p2 = (int)(ycoord(yc) - fabs(ymult*rady)*sin(DEGTORAD(startang)));
p3 = (int)(xcoord(xc) + fabs(xmult*radx)*cos(DEGTORAD(startang+angextent-.001)));
p4 = (int)(ycoord(yc) - fabs(ymult*rady)*sin(DEGTORAD(startang+angextent-.001)));
}
else {
p1 = (int)(xcoord(xc) + fabs(xmult*radx)*cos(DEGTORAD(startang+angextent+.001)));
p2 = (int)(ycoord(yc) - fabs(ymult*rady)*sin(DEGTORAD(startang+angextent+.001)));
p3 = (int)(xcoord(xc) + fabs(xmult*radx)*cos(DEGTORAD(startang)));
p4 = (int)(ycoord(yc) - fabs(ymult*rady)*sin(DEGTORAD(startang)));
}
hOldPen = (HPEN)SelectObject(hGraphicsDC, GetStockObject(NULL_PEN));
if(!(hOldPen))
SELECT_ERROR();
hOldBrush = (HBRUSH)SelectObject(hGraphicsDC, hGraphicsBrush);
if(!(hOldBrush))
SELECT_ERROR();
// Win32 API says a zero return value indicates an error, but it seems to always
// return zero. Don't check for an error on Pie.
Pie(hGraphicsDC, xl, yt, xl+width, yt+height, p1, p2, p3, p4);
// if(!Pie(hGraphicsDC, xl, yt, xl+width, yt+height, p1, p2, p3, p4));
// DRAW_ERROR();
if(!SelectObject(hGraphicsDC, hOldPen))
SELECT_ERROR();
if(!SelectObject(hGraphicsDC, hOldBrush))
SELECT_ERROR();
#endif
}
else {
fprintf(ps, "gsave\n");
fprintf(ps, "%.2f %.2f translate\n", XPOST(xc), YPOST(yc));
fprintf(ps, "%.2f 1 scale\n", fabs(radx*ps_xmult)/fabs(rady*ps_ymult));
fprintf(ps, "%.2f %.2f %.2f 0 0 %s\n", /*XPOST(xc)*/
/*YPOST(yc)*/ fabs(rady*ps_xmult), startang, startang+angextent,
(angextent < 0) ? "fillarcn" : "fillarc") ;
fprintf(ps, "grestore\n");
}
}
void
fillarc (float xc, float yc, float rad, float startang, float angextent) {
fillellipticarc(xc, yc, rad, rad, startang, angextent);
}
void
fillpoly (t_point *points, int npoints)
{
#ifdef X11
XPoint transpoints[MAXPTS];
#else
POINT transpoints[MAXPTS];
HPEN hOldPen;
HBRUSH hOldBrush;
#endif
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 (gl_state.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);
}
#ifdef X11
XFillPolygon(display, toplevel, current_gc, transpoints, npoints, Complex,
CoordModeOrigin);
#else
hOldPen = (HPEN)SelectObject(hGraphicsDC, GetStockObject(NULL_PEN));
if(!(hOldPen))
SELECT_ERROR();
hOldBrush = (HBRUSH)SelectObject(hGraphicsDC, hGraphicsBrush);
if(!(hOldBrush))
SELECT_ERROR();
if(!Polygon (hGraphicsDC, transpoints, npoints))
DRAW_ERROR();
if(!SelectObject(hGraphicsDC, hOldPen))
SELECT_ERROR();
if(!SelectObject(hGraphicsDC, hOldBrush))
SELECT_ERROR();
#endif
}
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);
}
}
/* Draws text centered on xc,yc if it fits in boundx */
void
drawtext (float xc, float yc, const char *text, float boundx)
{
int len, width, xw_off, yw_off, font_ascent, font_descent;
#ifdef X11
len = strlen(text);
width = XTextWidth(font_info[currentfontsize], text, len);
font_ascent = font_info[currentfontsize]->ascent;
font_descent = font_info[currentfontsize]->descent;
#else /* WC : WIN32 */
HFONT hOldFont;
SIZE textsize;
TEXTMETRIC textmetric;
hOldFont = (HFONT)SelectObject(hGraphicsDC, hGraphicsFont);
if(!(hOldFont))
SELECT_ERROR();
if(SetTextColor(hGraphicsDC, win32_colors[currentcolor]) == CLR_INVALID)
DRAW_ERROR();
len = strlen(text);
if (!GetTextExtentPoint32(hGraphicsDC, text, len, &textsize))
DRAW_ERROR();
width = textsize.cx;
if (!GetTextMetrics(hGraphicsDC, &textmetric))
DRAW_ERROR();
font_ascent = textmetric.tmAscent;
font_descent = textmetric.tmDescent;
#endif
if (width > fabs(boundx*xmult)) {
#ifdef WIN32
if(!SelectObject(hGraphicsDC, hOldFont))
SELECT_ERROR();
#endif
return; /* Don't draw if it won't fit */
}
xw_off = (int)(width/(2.*xmult)); /* NB: sign doesn't matter. */
/* NB: 2 * descent makes this slightly conservative but simplifies code. */
yw_off = (int)((font_ascent + 2 * font_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)) {
#ifdef WIN32
if(!SelectObject(hGraphicsDC, hOldFont))
SELECT_ERROR();
#endif
return;
}
if (gl_state.disp_type == SCREEN) {
#ifdef X11
XDrawString(display, toplevel, current_gc, xcoord(xc)-width/2, ycoord(yc) +
(font_info[currentfontsize]->ascent - font_info[currentfontsize]->descent)/2,
text, len);
#else /* Win32 */
SetBkMode(hGraphicsDC, TRANSPARENT);
if(!TextOut (hGraphicsDC, xcoord(xc)-width/2, ycoord(yc) - (font_ascent + font_descent)/2,
text, len))
DRAW_ERROR();
if(!SelectObject(hGraphicsDC, hOldFont))
SELECT_ERROR();
#endif
}
else {
fprintf(ps,"(%s) %.2f %.2f censhow\n",text,XPOST(xc),YPOST(yc));
}
}
void
flushinput (void)
{
if (gl_state.disp_type != SCREEN)
return;
#ifdef X11
XFlush(display);
#endif
}
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 (gl_state.disp_type == SCREEN) {
update_transform();
}
else {
update_ps_transform();
}
}
/* Draw the current message in the text area at the screen bottom. */
void
draw_message (void)
{
int savefontsize, savecolor;
float ylow;
#ifdef X11
int len, width;
#endif
if (gl_state.disp_type == SCREEN) {
#ifdef X11
XClearWindow (display, textarea);
len = strlen (statusMessage);
width = XTextWidth(font_info[menu_font_size], statusMessage, len);
XSetForeground(display, gc_menus,colors[WHITE]);
XDrawRectangle(display, textarea, gc_menus, 0, 0, top_width - MWIDTH, T_AREA_HEIGHT);
XSetForeground(display, gc_menus,colors[BLACK]);
XDrawLine(display, textarea, gc_menus, 0, T_AREA_HEIGHT-1, top_width-MWIDTH, T_AREA_HEIGHT-1);
XDrawLine(display, textarea, gc_menus, top_width-MWIDTH-1, 0, top_width-MWIDTH-1, T_AREA_HEIGHT-1);
XDrawString(display, textarea, gc_menus,
(top_width - MWIDTH - width)/2,
T_AREA_HEIGHT/2 + (font_info[menu_font_size]->ascent -
font_info[menu_font_size]->descent)/2, statusMessage, len);
#else
if(!InvalidateRect(hStatusWnd, NULL, TRUE))
DRAW_ERROR();
if(!UpdateWindow(hStatusWnd))
DRAW_ERROR();
#endif
}
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",statusMessage,(ps_left+ps_right)/2.,
ylow);
setcolor (savecolor);
setfontsize (savefontsize);
}
}
/* Changes the message to be displayed on screen. */
void
update_message (const char *msg)
{
strncpy (statusMessage, msg, BUFSIZE);
draw_message ();
#ifdef X11
// Make this appear immediately. Win32 does that automaticaly.
XFlush (display);
#endif // X11
}
/* Zooms in by a factor of 1.666. */
static void
zoom_in (void (*drawscreen) (void))
{
float xdiff, ydiff;
xdiff = xright - xleft;
ydiff = ybot - ytop;
xleft += xdiff/5;
xright -= xdiff/5;
ytop += ydiff/5;
ybot -= ydiff/5;
update_transform ();
drawscreen();
}
/* Zooms out by a factor of 1.666. */
static void
zoom_out (void (*drawscreen) (void))
{
float xdiff, ydiff;
xdiff = xright - xleft;
ydiff = ybot - ytop;
xleft -= xdiff/3;
xright += xdiff/3;
ytop -= ydiff/3;
ybot += ydiff/3;
update_transform ();
drawscreen();
}
/* Sets the view back to the initial view set by init_world (i.e. a full *
* view) of all the graphics. */
static void
zoom_fit (void (*drawscreen) (void))
{
xleft = saved_xleft;
xright = saved_xright;
ytop = saved_ytop;
ybot = saved_ybot;
update_transform ();
drawscreen();
}
/* Moves view 1/2 screen up. */
static void
translate_up (void (*drawscreen) (void))
{
float ystep;
ystep = (ybot - ytop)/2;
ytop -= ystep;
ybot -= ystep;
update_transform();
drawscreen();
}
/* Moves view 1/2 screen down. */
static void
translate_down (void (*drawscreen) (void))
{
float ystep;
ystep = (ybot - ytop)/2;
ytop += ystep;
ybot += ystep;
update_transform();
drawscreen();
}
/* Moves view 1/2 screen left. */
static void
translate_left (void (*drawscreen) (void))
{
float xstep;
xstep = (xright - xleft)/2;
xleft -= xstep;
xright -= xstep;
update_transform();
drawscreen();
}
/* Moves view 1/2 screen right. */
static void
translate_right (void (*drawscreen) (void))
{
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();
}
/* The window button was pressed. Let the user click on the two *
* diagonally opposed corners, and zoom in on this area. */
static void
adjustwin (void (*drawscreen) (void))
{
#ifdef X11
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();
drawmenu();
draw_message();
#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:
if (corner) {
#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 */
// Erase prior box.
set_draw_mode(DRAW_XOR);
XDrawRectangle(display,toplevel,gcxor,min(x[0],xold),
min(y[0],yold),abs(x[0]-xold),abs(y[0]-yold));
set_draw_mode(DRAW_NORMAL);
}
/* Don't allow user to window under menu region */
xold = min(report.xmotion.x,top_width-1-MWIDTH);
yold = report.xmotion.y;
set_draw_mode(DRAW_XOR);
// Use forcing versions, as we want these modes to apply
// to the xor drawing context, and the regular versions
// won't update the drawing context if there is no change in line
// width etc. (but they might only be on the normal context)
force_setlinewidth(1);
force_setlinestyle(DASHED);
force_setcolor(gl_state.background_cindex);
// Draw rubber band box.
XDrawRectangle(display,toplevel,gcxor,min(x[0],xold),
min(y[0],yold),abs(x[0]-xold),abs(y[0]-yold));
set_draw_mode(DRAW_NORMAL);
}
break;
}
}
/* XSelectInput (display, toplevel, ExposureMask | StructureNotifyMask
| ButtonPressMask); */
#else /* Win32 */
/* Implemented as WM_LB... events */
/* Begin window adjust */
if (!windowAdjustFlag) {
windowAdjustFlag = 1;
}
#endif
}
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[BUFSIZE];
sprintf(fname,"pic%d.ps",piccount);
printf("Writing postscript output to file %s\n", fname);
success = init_postscript (fname);
if (success) {
drawscreen();
close_postscript ();
piccount++;
}
else {
printf ("Error initializing for postscript output.\n");
#ifdef WIN32
MessageBox(hMainWnd, "Error initializing postscript output.", NULL, MB_OK);
#endif
}
}
static void
proceed (void (*drawscreen) (void))
{
ProceedPressed = TRUE;
}
static void
quit (void (*drawscreen) (void))
{
close_graphics();
exit(0);
}
/* Release all my drawing structures (through the X server) and *
* close down the connection. */
void
close_graphics (void)
{
if (!gl_state.initialized)
return;
#ifdef X11
int i;
for (i=0;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);
#else /* Win32 */
int i;
// Free the font data structure for each loaded font.
for (i = 0; i <= MAX_FONT_SIZE; i++) {
if (font_is_loaded[i]) {
free (font_info[i]);
}
}
// Destroy the window
if(!DestroyWindow(hMainWnd))
DRAW_ERROR();
// free the window class (type information held by MS Windows)
// for each of the four window types we created. Otherwise a
// later call to init_graphics to open the graphics window up again
// will fail.
if (!UnregisterClass (szAppName, GetModuleHandle(NULL)) )
DRAW_ERROR();
if (!UnregisterClass (szGraphicsName, GetModuleHandle(NULL)) )
DRAW_ERROR();
if (!UnregisterClass (szStatusName, GetModuleHandle(NULL)) )
DRAW_ERROR();
if (!UnregisterClass (szButtonsName, GetModuleHandle(NULL)) )
DRAW_ERROR();
#endif
free(button);
button = NULL;
for (i = 0; i <= MAX_FONT_SIZE; i++) {
font_is_loaded[i] = false;
font_info[i] = NULL;
}
gl_state.initialized = false;
}
/* 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. */
int init_postscript (const char *fname)
{
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);
}
gl_state.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,"%%%%HiResBoundingBox: %.2f %.2f %.2f %.2f\n",
ps_left, ps_bot - 15., ps_right, 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," 8 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.89 0.77 setrgbcolor } def\n");
fprintf(ps,"/lightblue { 0.68 0.85 0.9 setrgbcolor } def\n");
fprintf(ps,"/thistle { 0.85 0.75 0.85 setrgbcolor } def\n");
fprintf(ps,"/plum { 0.87 0.63 0.87 setrgbcolor } def\n");
fprintf(ps,"/khaki { 0.94 0.9 0.55 setrgbcolor } def\n");
fprintf(ps,"/coral { 1 0.5 0.31 setrgbcolor } def\n");
fprintf(ps,"/turquoise { 0.25 0.88 0.82 setrgbcolor } def\n");
fprintf(ps,"/mediumpurple { 0.58 0.44 0.86 setrgbcolor } def\n");
fprintf(ps,"/darkslateblue { 0.28 0.24 0.55 setrgbcolor } def\n");
fprintf(ps,"/darkkhaki { 0.74 0.72 0.42 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);
}
/* Properly ends postscript output and redirects output to screen. */
void close_postscript (void)
{
fprintf(ps,"showpage\n");
fprintf(ps,"\n%%%%Trailer\n");
fclose (ps);
gl_state.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.
* The current font set indicates the last font used in a postscript call,
* etc., *NOT* the font set in the X11 or Win32 graphics context. Force the
* current font, colour etc. to be applied to the graphics context, so
* subsequent drawing commands work properly.
*/
force_setcolor (currentcolor);
force_setlinestyle (currentlinestyle);
force_setlinewidth (currentlinewidth);
force_setfontsize (currentfontsize);
}
/* Sets up the default menu buttons on the right hand side of the window. */
static void
build_default_menu (void)
{
int i, xcen, x1, y1, bwid, bheight, space;
const int NUM_ARROW_BUTTONS = 4, NUM_STANDARD_BUTTONS = 12, SEPARATOR_BUTTON_INDEX = 8;
#ifdef X11
unsigned long valuemask;
XSetWindowAttributes menu_attributes;
menu = XCreateSimpleWindow(display,toplevel,
top_width-MWIDTH, 0, MWIDTH, display_height, 0,
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);
#endif
button = (t_button *) my_malloc (NUM_STANDARD_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;
#ifdef X11
setpoly (0, bwid/2, bwid/2, bwid/3, -PI/2.); /* Up */
#else
button[0].type = BUTTON_TEXT;
strcpy(button[0].text, "U");
#endif
button[0].fcn = translate_up;
y1 += bwid + space;
x1 = xcen - 3*bwid/2 - space;
button[1].xleft = x1;
button[1].ytop = y1;
#ifdef X11
setpoly (1, bwid/2, bwid/2, bwid/3, PI); /* Left */
#else
button[1].type = BUTTON_TEXT;
strcpy(button[1].text, "L");
#endif
button[1].fcn = translate_left;
x1 = xcen + bwid/2 + space;
button[2].xleft = x1;
button[2].ytop = y1;
#ifdef X11
setpoly (2, bwid/2, bwid/2, bwid/3, 0); /* Right */
#else
button[2].type = BUTTON_TEXT;
strcpy(button[2].text, "R");
#endif
button[2].fcn = translate_right;
y1 += bwid + space;
x1 = xcen - bwid/2;
button[3].xleft = x1;
button[3].ytop = y1;
#ifdef X11
setpoly (3, bwid/2, bwid/2, bwid/3, +PI/2.); /* Down */
#else
button[3].type = BUTTON_TEXT;
strcpy(button[3].text, "D");
#endif
button[3].fcn = translate_down;
for (i = 0; i < NUM_ARROW_BUTTONS; i++) {
button[i].width = bwid;
button[i].height = bwid;
button[i].enabled = 1;
}
/* Rectangular buttons */
y1 += bwid + space + 6;
space = 8;
bwid = 90;
bheight = 26;
x1 = xcen - bwid/2;
for (i = NUM_ARROW_BUTTONS; i < NUM_STANDARD_BUTTONS; i++) {
button[i].xleft = x1;
button[i].ytop = y1;
button[i].type = BUTTON_TEXT;
button[i].width = bwid;
button[i].enabled = 1;
if (i != SEPARATOR_BUTTON_INDEX) {
button[i].height = bheight;
y1 += bheight + space;
}
else {
button[i].height = 2;
button[i].type = BUTTON_SEPARATOR;
y1 += 2 + space;
}
}
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,"---1");
strcpy (button[9].text,"PostScript");
strcpy (button[10].text,"Proceed");
strcpy (button[11].text,"Exit");
button[4].fcn = zoom_in;
button[5].fcn = zoom_out;
button[6].fcn = zoom_fit;
button[7].fcn = adjustwin; // see 'adjustButton' below in WIN32 section
button[8].fcn = NULL;
button[9].fcn = postscript;
button[10].fcn = proceed;
button[11].fcn = quit;
for (i = 0; i < NUM_STANDARD_BUTTONS; i++)
map_button (i);
num_buttons = NUM_STANDARD_BUTTONS;
#ifdef WIN32
adjustButton = 7;
if(!InvalidateRect(hButtonsWnd, NULL, TRUE))
DRAW_ERROR();
if(!UpdateWindow(hButtonsWnd))
DRAW_ERROR();
#endif
}
/* Makes sure the font of the specified size is loaded. Point_size *
* MUST be between 1 and MAX_FONT_SIZE. */
static void
load_font(int pointsize)
{
if (pointsize > MAX_FONT_SIZE || pointsize < 1) {
printf ("Error: font size %d is out of valid range, 1 to %d.\n",
pointsize, MAX_FONT_SIZE);
return;
}
if (font_is_loaded[pointsize]) // Nothing to do.
return;
#ifdef X11
#define NUM_FONT_TYPES 3
char fontname[NUM_FONT_TYPES][BUFSIZE];
int ifont;
bool success = false;
/* Use proper point-size medium-weight upright helvetica font */
// Exists on most X11 systems.
// Backup font: lucidasans, in the new naming style.
sprintf(fontname[0],"-*-helvetica-medium-r-*--*-%d0-*-*-*-*-*-*",
pointsize);
sprintf(fontname[1], "lucidasans-%d", pointsize);
sprintf(fontname[2],"-schumacher-clean-medium-r-*--*-%d0-*-*-*-*-*-*",
pointsize);
for (ifont = 0; ifont < NUM_FONT_TYPES; ifont++) {
#ifdef VERBOSE
printf ("Loading font: point size: %d, fontname: %s\n",pointsize,
fontname[ifont]);
#endif
/* Load font and get font information structure. */
if ((font_info[pointsize] = XLoadQueryFont(display,fontname[ifont])) == NULL) {
#ifdef VERBOSE
fprintf( stderr, "Cannot open font %s\n", fontname[ifont]);
#endif
}
else {
success = true;
break;
}
}
if (!success) {
printf ("Error in load_font: cannot load any font of pointsize %d.\n",
pointsize);
printf ("Use xlsfonts to list available fonts, and modify load_font\n");
printf ("in graphics.cpp.\n");
exit (1);
}
#else /* WIN32 */
LOGFONT *lf = font_info[pointsize] = (LOGFONT*)my_malloc(sizeof(LOGFONT));
ZeroMemory(lf, sizeof(LOGFONT));
lf->lfHeight = pointsize;
lf->lfWeight = FW_NORMAL;
lf->lfCharSet = ANSI_CHARSET;
lf->lfOutPrecision = OUT_DEFAULT_PRECIS;
lf->lfClipPrecision = CLIP_DEFAULT_PRECIS;
lf->lfQuality = PROOF_QUALITY;
lf->lfPitchAndFamily = VARIABLE_PITCH | FF_SWISS;
strcpy(lf->lfFaceName, "Arial");
#endif
font_is_loaded[pointsize] = true;
}
/* Return information useful for debugging.
* Used to return the top-level window object too, but that made graphics.h
* export all windows and X11 headers to the client program, so VB deleted
* that object (mainwnd) from this structure.
*/
void report_structure(t_report *report) {
report->xmult = xmult;
report->ymult = ymult;
report->xleft = xleft;
report->xright = xright;
report->ytop = ytop;
report->ybot = ybot;
report->ps_xmult = ps_xmult;
report->ps_ymult = ps_ymult;
report->top_width = top_width;
report->top_height = top_height;
}
void set_mouse_move_input (bool enable) {
get_mouse_move_input = enable;
}
void set_keypress_input (bool enable) {
get_keypress_input = enable;
}
void enable_or_disable_button (int ibutton, bool enabled) {
if (button[ibutton].type != BUTTON_SEPARATOR) {
button[ibutton].enabled = enabled;
#ifdef WIN32
EnableWindow(button[ibutton].hwnd, button[ibutton].enabled);
#else // X11
drawbut(ibutton);
XFlush(display);
#endif
}
}
void set_draw_mode (enum e_draw_mode draw_mode) {
/* Set normal (overwrite) or xor (useful for rubber-banding)
* drawing mode.
*/
if (draw_mode == DRAW_NORMAL) {
#ifdef X11
current_gc = gc;
#else
if (!SetROP2(hGraphicsDC, R2_COPYPEN))
SELECT_ERROR();
#endif
}
else { // DRAW_XOR
#ifdef X11
current_gc = gcxor;
#else
if (!SetROP2(hGraphicsDC, R2_XORPEN))
SELECT_ERROR();
#endif
}
current_draw_mode = draw_mode;
}
void change_button_text(const char *button_name, const char *new_button_text) {
/* Change the text on a button with button_name to new_button_text.
* Not a strictly necessary function, since you could intead just
* destroy button_name and make a new buton.
*/
int i, bnum;
bnum = -1;
for (i=4;i<num_buttons;i++) {
if (button[i].type == BUTTON_TEXT &&
strcmp (button[i].text, button_name) == 0) {
bnum = i;
break;
}
}
if (bnum != -1) {
strncpy (button[i].text, new_button_text, BUTTON_TEXT_LEN);
#ifdef X11
drawbut (i);
#else // Win32
SetWindowText(button[bnum].hwnd, new_button_text);
#endif
}
}
/**********************************
* X-Windows Specific Definitions *
*********************************/
#ifdef X11
/* Creates a small window at the top of the graphics area for text messages */
static void build_textarea (void)
{
XSetWindowAttributes menu_attributes;
unsigned long valuemask;
textarea = XCreateSimpleWindow(display,toplevel,
0, top_height-T_AREA_HEIGHT, display_width-MWIDTH, T_AREA_HEIGHT, 0,
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 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, const 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];
width = button[bnum].width;
height = button[bnum].height;
if (button[bnum].type == BUTTON_SEPARATOR) {
int x,y;
x = button[bnum].xleft;
y = button[bnum].ytop;
XSetForeground(display, gc_menus,colors[WHITE]);
XDrawLine(display, menu, gc_menus, x, y+1, x+width, y+1);
XSetForeground(display, gc_menus,colors[BLACK]);
XDrawLine(display, menu, gc_menus, x, y, x+width, y);
return;
}
ispressed = button[bnum].ispressed;
thick = 2;
/* 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].type == BUTTON_POLY) {
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].type == BUTTON_TEXT) {
if (button[bnum].enabled)
XSetForeground(display, gc_menus,colors[BLACK]);
else
XSetForeground(display, gc_menus,colors[DARKGREY]);
menutext(button[bnum].win,button[bnum].width/2,
button[bnum].height/2,button[bnum].text);
}
}
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 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 void drawmenu(void)
{
int i;
XClearWindow (display, menu);
XSetForeground(display, gc_menus,colors[WHITE]);
XDrawRectangle(display, menu, gc_menus, 0, 0, MWIDTH, top_height);
XSetForeground(display, gc_menus,colors[BLACK]);
XDrawLine(display, menu, gc_menus, 0, top_height-1, MWIDTH, top_height-1);
XDrawLine(display, menu, gc_menus, MWIDTH-1, top_height, MWIDTH-1, 0);
for (i=0;i<num_buttons;i++) {
drawbut(i);
}
}
#endif /* X-Windows Specific Definitions */
/*************************************************
* Microsoft Windows (WIN32) Specific Definitions *
*************************************************/
#ifdef WIN32
static LRESULT CALLBACK
MainWND(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
{
MINMAXINFO FAR *lpMinMaxInfo;
switch(message)
{
case WM_CREATE:
hStatusWnd = CreateWindow(szStatusName, NULL, WS_CHILDWINDOW | WS_VISIBLE,
0, 0, 0, 0, hwnd, (HMENU) 102, (HINSTANCE) GetWindowLong(hwnd, GWL_HINSTANCE), NULL);
hButtonsWnd = CreateWindow(szButtonsName, NULL, WS_CHILDWINDOW | WS_VISIBLE,
0, 0, 0, 0, hwnd, (HMENU) 103, (HINSTANCE) GetWindowLong(hwnd, GWL_HINSTANCE), NULL);
hGraphicsWnd = CreateWindow(szGraphicsName, NULL, WS_CHILDWINDOW | WS_VISIBLE,
0, 0, 0, 0, hwnd, (HMENU) 101, (HINSTANCE) GetWindowLong(hwnd, GWL_HINSTANCE), NULL);
return 0;
case WM_SIZE:
/* Window has been resized. Save the new client dimensions */
top_width = LOWORD (lParam);
top_height = HIWORD (lParam);
/* Resize the children windows */
if(!MoveWindow(hGraphicsWnd, 1, 1, top_width - MWIDTH - 1, top_height - T_AREA_HEIGHT - 1, TRUE))
DRAW_ERROR();
// if (drawscreen_ptr)
// zoom_fit(drawscreen_ptr);
if(!MoveWindow(hStatusWnd, 0, top_height - T_AREA_HEIGHT, top_width - MWIDTH, T_AREA_HEIGHT, TRUE))
DRAW_ERROR();
if(!MoveWindow(hButtonsWnd, top_width - MWIDTH, 0, MWIDTH, top_height, TRUE))
DRAW_ERROR();
return 0;
// WC : added to solve window resizing problem
case WM_GETMINMAXINFO:
// set the MINMAXINFO structure pointer
lpMinMaxInfo = (MINMAXINFO FAR *) lParam;
lpMinMaxInfo->ptMinTrackSize.x = display_width / 2;
lpMinMaxInfo->ptMinTrackSize.y = display_height / 2;
return 0;
case WM_DESTROY:
if(!DeleteObject(hGrayBrush))
DELETE_ERROR();
PostQuitMessage(0);
return 0;
case WM_KEYDOWN:
if (get_keypress_input)
keypress_ptr((char) wParam);
return 0;
}
return DefWindowProc(hwnd, message, wParam, lParam);
}
static LRESULT CALLBACK
GraphicsWND(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
{
static TEXTMETRIC tm;
PAINTSTRUCT ps;
static RECT oldAdjustRect;
static HPEN hDotPen = 0;
static HBITMAP hbmBuffer = 0, hbmObjtest, hbmAllObjtest;
static int X, Y, i;
switch(message)
{
case WM_CREATE:
/* Get the text metrics once upon creation (system font cannot change) */
hCurrentDC = hGraphicsDC = hForegroundDC = GetDC (hwnd);
if(!hGraphicsDC)
DRAW_ERROR();
hBackgroundDC = CreateCompatibleDC(hForegroundDC);
if (!hBackgroundDC)
CREATE_ERROR();
if (!SetMapMode(hBackgroundDC, MM_TEXT))
CREATE_ERROR();
hbmBuffer = CreateCompatibleBitmap(hForegroundDC, display_width, display_height);
if (!(hbmBuffer))
CREATE_ERROR();
if (!SelectObject(hBackgroundDC, hbmBuffer))
SELECT_ERROR();
// monochrome bitmap
hObjtestDC = CreateCompatibleDC(hForegroundDC);
if (!hObjtestDC)
CREATE_ERROR();
if (!SetMapMode(hObjtestDC, MM_TEXT))
CREATE_ERROR();
hbmObjtest = CreateCompatibleBitmap(hObjtestDC, display_width, display_height);
if (!(hbmObjtest))
CREATE_ERROR();
if (!SelectObject(hObjtestDC, hbmObjtest))
SELECT_ERROR();
// monochrome bitmap
hAllObjtestDC = CreateCompatibleDC(hForegroundDC);
if (!hObjtestDC)
CREATE_ERROR();
if (!SetMapMode(hAllObjtestDC, MM_TEXT))
CREATE_ERROR();
hbmAllObjtest = CreateCompatibleBitmap(hAllObjtestDC, display_width, display_height);
if (!(hbmAllObjtest))
CREATE_ERROR();
if (!SelectObject(hAllObjtestDC, hbmAllObjtest))
SELECT_ERROR();
//if(!GetTextMetrics (hGraphicsDC, &tm))
// DRAW_ERROR();
if(!SetBkMode(hGraphicsDC, TRANSPARENT))
DRAW_ERROR();
/* Setup the pens, etc */
currentlinestyle = SOLID;
currentcolor = BLACK;
currentlinewidth = 1;
/*
if(!ReleaseDC (hwnd, hGraphicsDC))
DRAW_ERROR();
hGraphicsDC = 0;
*/ currentfontsize = 12;
return 0;
case WM_PAINT:
// was in xor mode, but got a general redraw.
// switch to normal drawing so we repaint properly.
if (current_draw_mode == DRAW_XOR) {
set_draw_mode(DRAW_NORMAL);
invalidate_screen();
return 0;
}
hCurrentDC = hGraphicsDC;
drawtoscreen();
/*hGraphicsDC =*/ BeginPaint(hwnd, &ps);
if(!hGraphicsDC)
DRAW_ERROR();
if (InEventLoop) {
if(!GetUpdateRect(hwnd, &updateRect, FALSE)) {
updateRect.left = 0;
updateRect.right = top_width;
updateRect.top = 0;
updateRect.bottom = top_height;
}
if(windowAdjustFlag > 1) {
hDotPen = CreatePen(PS_DASH, 1, win32_colors[gl_state.background_cindex]);
if(!hDotPen)
CREATE_ERROR();
if (!SetROP2(hGraphicsDC, R2_XORPEN))
SELECT_ERROR();
if(!SelectObject(hGraphicsDC, GetStockObject(NULL_BRUSH)))
SELECT_ERROR();
if(!SelectObject(hGraphicsDC, hDotPen))
SELECT_ERROR();
if(!Rectangle(hGraphicsDC, oldAdjustRect.left, oldAdjustRect.top,
oldAdjustRect.right, oldAdjustRect.bottom))
DRAW_ERROR();
if(!Rectangle(hGraphicsDC, adjustRect.left, adjustRect.top, adjustRect.right,
adjustRect.bottom))
DRAW_ERROR();
oldAdjustRect = adjustRect;
if (!SetROP2(hGraphicsDC, R2_COPYPEN))
SELECT_ERROR();
if(!SelectObject(hGraphicsDC, GetStockObject(NULL_PEN)))
SELECT_ERROR();
if(!DeleteObject(hDotPen))
DELETE_ERROR();
}
else
drawscreen_ptr();
}
EndPaint(hwnd, &ps);
hGraphicsDC = hCurrentDC;
/* Crash hard if called at wrong time */
/* hGraphicsDC = NULL;*/
return 0;
case WM_SIZE:
/* Window has been resized. Save the new client dimensions */
cxClient = LOWORD (lParam);
cyClient = HIWORD (lParam);
update_transform();
return 0;
case WM_DESTROY:
if(!DeleteObject(hGraphicsPen))
DELETE_ERROR();
if(!DeleteObject(hGraphicsBrush))
DELETE_ERROR();
if(!DeleteObject(hGraphicsFont))
DELETE_ERROR();
if (!DeleteObject(hbmBuffer))
DELETE_ERROR();
if (!DeleteObject(hbmObjtest))
DELETE_ERROR();
if (!DeleteObject(hbmAllObjtest))
DELETE_ERROR();
if(!DeleteDC(hBackgroundDC))
DELETE_ERROR();
if(!DeleteDC(hObjtestDC))
DELETE_ERROR();
if(!DeleteDC(hAllObjtestDC))
DELETE_ERROR();
if(!ReleaseDC(hwnd, hForegroundDC))
DELETE_ERROR();
PostQuitMessage(0);
return 0;
case WM_LBUTTONDOWN:
if (!windowAdjustFlag) {
mouseclick_ptr(XTOWORLD(LOWORD(lParam)), YTOWORLD(HIWORD(lParam)));
}
else {
// Special handling for the "Window" command, which takes multiple clicks.
// First you push the button, then you click for one corner, then you click for the other
// corner.
if(windowAdjustFlag == 1) {
windowAdjustFlag ++;
X = adjustRect.left = adjustRect.right = LOWORD(lParam);
Y = adjustRect.top = adjustRect.bottom = HIWORD(lParam);
oldAdjustRect = adjustRect;
}
else {
int i;
int adjustx[2], adjusty[2];
windowAdjustFlag = 0;
button[adjustButton].ispressed = 0;
SendMessage(button[adjustButton].hwnd, BM_SETSTATE, 0, 0);
for (i=0; i<num_buttons; i++) {
if (button[i].type != BUTTON_SEPARATOR && button[i].enabled) {
if(!EnableWindow (button[i].hwnd, TRUE))
DRAW_ERROR();
}
}
adjustx[0] = adjustRect.left;
adjustx[1] = adjustRect.right;
adjusty[0] = adjustRect.top;
adjusty[1] = adjustRect.bottom;
update_win(adjustx, adjusty, invalidate_screen);
}
}
return 0;
// right click : a quick way to zoom in
case WM_RBUTTONDOWN:
if (!windowAdjustFlag) {
// first disable some buttons
//adjustButton = LOWORD(wParam) - 200;
button[adjustButton].ispressed = 1;
for (i=0; i<num_buttons; i++) {
EnableWindow(button[i].hwnd, FALSE);
SendMessage(button[i].hwnd, BM_SETSTATE, button[i].ispressed, 0);
}
windowAdjustFlag = 2;
X = adjustRect.left = adjustRect.right = LOWORD(lParam);
Y = adjustRect.top = adjustRect.bottom = HIWORD(lParam);
oldAdjustRect = adjustRect;
}
else {
int i;
int adjustx[2], adjusty[2];
windowAdjustFlag = 0;
button[adjustButton].ispressed = 0;
SendMessage(button[adjustButton].hwnd, BM_SETSTATE, 0, 0);
for (i=0; i<num_buttons; i++) {
if (button[i].type != BUTTON_SEPARATOR && button[i].enabled) {
if(!EnableWindow (button[i].hwnd, TRUE))
DRAW_ERROR();
}
}
adjustx[0] = adjustRect.left;
adjustx[1] = adjustRect.right;
adjusty[0] = adjustRect.top;
adjusty[1] = adjustRect.bottom;
update_win(adjustx, adjusty, invalidate_screen);
}
return 0;
case WM_MOUSEMOVE:
if(windowAdjustFlag == 1) {
return 0;
}
else if (windowAdjustFlag >= 2) {
if ( X > LOWORD(lParam)) {
adjustRect.left = LOWORD(lParam);
adjustRect.right = X;
}
else {
adjustRect.left = X;
adjustRect.right = LOWORD(lParam);
}
if ( Y > HIWORD(lParam)) {
adjustRect.top = HIWORD(lParam);
adjustRect.bottom = Y;
}
else {
adjustRect.top = Y;
adjustRect.bottom = HIWORD(lParam);
}
if(!InvalidateRect(hGraphicsWnd, &oldAdjustRect, FALSE))
DRAW_ERROR();
if(!InvalidateRect(hGraphicsWnd, &adjustRect, FALSE))
DRAW_ERROR();
if(!UpdateWindow(hGraphicsWnd))
DRAW_ERROR();
return 0;
}
else if (get_mouse_move_input)
mousemove_ptr(XTOWORLD(LOWORD(lParam)), YTOWORLD(HIWORD(lParam)));
return 0;
}
return DefWindowProc(hwnd, message, wParam, lParam);
}
static LRESULT CALLBACK
StatusWND(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
{
HDC hdc;
PAINTSTRUCT ps;
RECT rect;
switch(message)
{
case WM_CREATE:
hdc = GetDC(hwnd);
if(!hdc)
DRAW_ERROR();
if(!SetBkMode(hdc, TRANSPARENT))
DRAW_ERROR();
if(!ReleaseDC(hwnd, hdc))
DRAW_ERROR();
return 0;
case WM_PAINT:
hdc = BeginPaint(hwnd, &ps);
if(!hdc)
DRAW_ERROR();
if(!GetClientRect(hwnd, &rect))
DRAW_ERROR();
if(!SelectObject(hdc, GetStockObject(NULL_BRUSH)))
SELECT_ERROR();
if(!SelectObject(hdc, GetStockObject(WHITE_PEN)))
SELECT_ERROR();
if(!Rectangle(hdc, rect.left, rect.top, rect.right, rect.bottom))
DRAW_ERROR();
if(!SelectObject(hdc, GetStockObject(BLACK_PEN)))
SELECT_ERROR();
if(!MoveToEx(hdc, rect.left, rect.bottom-1, NULL))
DRAW_ERROR();
if(!LineTo(hdc, rect.right-1, rect.bottom-1))
DRAW_ERROR();
if(!LineTo(hdc, rect.right-1, rect.top))
DRAW_ERROR();
if(!DrawText(hdc, TEXT(statusMessage), -1, &rect, DT_CENTER | DT_VCENTER | DT_SINGLELINE))
DRAW_ERROR();
if(!EndPaint(hwnd, &ps))
DRAW_ERROR();
return 0;
case WM_SIZE:
return 0;
case WM_DESTROY:
PostQuitMessage(0);
return 0;
}
return DefWindowProc(hwnd, message, wParam, lParam);
}
static LRESULT CALLBACK
ButtonsWND(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
{
HDC hdc;
PAINTSTRUCT ps;
RECT rect;
static HBRUSH hBrush;
int i;
switch(message)
{
case WM_COMMAND:
if (!windowAdjustFlag) {
button[LOWORD(wParam) - 200].fcn(invalidate_screen);
if (windowAdjustFlag) {
adjustButton = LOWORD(wParam) - 200;
button[adjustButton].ispressed = 1;
for (i=0; i<num_buttons; i++) {
EnableWindow(button[i].hwnd, FALSE);
SendMessage(button[i].hwnd, BM_SETSTATE, button[i].ispressed, 0);
}
}
}
SetFocus(hMainWnd);
return 0;
case WM_CREATE:
hdc = GetDC(hwnd);
if(!hdc)
DRAW_ERROR();
hBrush = CreateSolidBrush(win32_colors[LIGHTGREY]);
if(!hBrush)
CREATE_ERROR();
if(!SelectObject(hdc, hBrush))
SELECT_ERROR();
if(!SetBkMode(hdc, TRANSPARENT))
DRAW_ERROR();
if(!ReleaseDC(hwnd, hdc))
DRAW_ERROR();
return 0;
case WM_PAINT:
hdc = BeginPaint(hwnd, &ps);
if(!hdc)
DRAW_ERROR();
if(!GetClientRect(hwnd, &rect))
DRAW_ERROR();
if(!SelectObject(hdc, GetStockObject(NULL_BRUSH)))
SELECT_ERROR();
if(!SelectObject(hdc, GetStockObject(WHITE_PEN)))
SELECT_ERROR();
if(!Rectangle(hdc, rect.left, rect.top, rect.right, rect.bottom))
DRAW_ERROR();
if(!SelectObject(hdc, GetStockObject(BLACK_PEN)))
SELECT_ERROR();
if(!MoveToEx(hdc, rect.left, rect.bottom-1, NULL))
DRAW_ERROR();
if(!LineTo(hdc, rect.right-1, rect.bottom-1))
DRAW_ERROR();
if(!LineTo(hdc, rect.right-1, rect.top))
DRAW_ERROR();
for (i=0; i < num_buttons; i++) {
if(button[i].type == BUTTON_SEPARATOR) {
int x, y, w;
x = button[i].xleft;
y = button[i].ytop;
w = button[i].width;
if(!MoveToEx (hdc, x, y, NULL))
DRAW_ERROR();
if(!LineTo (hdc, x + w, y))
DRAW_ERROR();
if(!SelectObject(hdc, GetStockObject(WHITE_PEN)))
SELECT_ERROR();
if(!MoveToEx (hdc, x, y+1, NULL))
DRAW_ERROR();
if(!LineTo (hdc, x + w, y+1))
DRAW_ERROR();
if(!SelectObject(hdc, GetStockObject(BLACK_PEN)))
SELECT_ERROR();
}
}
if(!EndPaint(hwnd, &ps))
DRAW_ERROR();
return 0;
case WM_DESTROY:
for (i=0; i<num_buttons; i++) {
}
if(!DeleteObject(hBrush))
DELETE_ERROR();
PostQuitMessage(0);
return 0;
}
return DefWindowProc(hwnd, message, wParam, lParam);
}
void reset_win32_state () {
// Not sure exactly what needs to be reset to NULL etc.
// Resetting everthing to be safe.
hGraphicsPen = 0;
hGraphicsBrush = 0;
hGrayBrush = 0;
hGraphicsDC = 0;
hForegroundDC = 0;
hBackgroundDC = 0;
hCurrentDC = 0, /* WC : double-buffer */
hObjtestDC = 0;
hAllObjtestDC = 0; /* object test */
hGraphicsFont = 0;
/* These are used for the "Window" graphics button. They keep track of whether we're entering
* the window rectangle to zoom to, etc.
*/
windowAdjustFlag = 0;
adjustButton = -1;
InEventLoop = FALSE;
}
void win32_drain_message_queue () {
// Drain the message queue, so we don't have a quit message lying around
// that will stop us from re-opening a window later if desired.
MSG msg;
while (PeekMessage(&msg, hMainWnd, 0, 0, PM_REMOVE)) {
if (msg.message == WM_QUIT) {
printf ("Got the quit message.\n");
}
}
}
void drawtobuffer(void) {
hGraphicsDC = hBackgroundDC;
}
void drawtoscreen(void) {
hGraphicsDC = hForegroundDC;
}
void displaybuffer(void) {
if (!BitBlt(hForegroundDC, xcoord(xleft), ycoord(ytop),
xcoord(xright)-xcoord(xleft), ycoord(ybot)-ycoord(ytop), hBackgroundDC,//hAllObjtestDC,
0, 0, SRCCOPY))
DRAW_ERROR();
}
static void _drawcurve(t_point *points, int npoints, int fill) {
/* Draw a beizer curve.
* Must have 3I+1 points, since each Beizer curve needs 3 points and we also
* need an initial starting point
*/
HPEN hOldPen;
HBRUSH hOldBrush;
float xmin, ymin, xmax, ymax;
int i;
if ((npoints - 1) % 3 != 0 || npoints > MAXPTS)
DRAW_ERROR();
/* 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 (gl_state.disp_type == SCREEN) {
#ifdef X11
/* implement X11 version here */
#else /* Win32 */
// create POINT array
POINT pts[MAXPTS];
int i;
for (i = 0; i < npoints; i++) {
pts[i].x = xcoord(points[i].x);
pts[i].y = ycoord(points[i].y);
}
if (!fill) {
hOldPen = (HPEN)SelectObject(hGraphicsDC, hGraphicsPen);
if(!(hOldPen))
SELECT_ERROR();
}
else {
hOldPen = (HPEN)SelectObject(hGraphicsDC, GetStockObject(NULL_PEN));
if(!(hOldPen))
SELECT_ERROR();
hOldBrush = (HBRUSH)SelectObject(hGraphicsDC, hGraphicsBrush);
if(!(hOldBrush))
SELECT_ERROR();
}
if (!BeginPath(hGraphicsDC))
DRAW_ERROR();
if(!PolyBezier(hGraphicsDC, pts, npoints))
DRAW_ERROR();
if (!EndPath(hGraphicsDC))
DRAW_ERROR();
if (!fill) {
if (!StrokePath(hGraphicsDC))
DRAW_ERROR();
}
else {
if (!FillPath(hGraphicsDC))
DRAW_ERROR();
}
if(!SelectObject(hGraphicsDC, hOldPen))
SELECT_ERROR();
if (fill) {
if(!SelectObject(hGraphicsDC, hOldBrush))
SELECT_ERROR();
}
#endif
}
else {
int i;
fprintf(ps, "newpath\n");
fprintf(ps, "%.2f %.2f moveto\n", XPOST(points[0].x), YPOST(points[0].y));
for (i = 1; i < npoints; i+= 3)
fprintf(ps,"%.2f %.2f %.2f %.2f %.2f %.2f curveto\n", XPOST(points[i].x), YPOST(points[i].y),
XPOST(points[i+1].x), YPOST(points[i+1].y), XPOST(points[i+2].x), YPOST(points[i+2].y));
if (!fill)
fprintf(ps, "stroke\n");
else
fprintf(ps, "fill\n");
}
}
void drawcurve(t_point *points,
int npoints) {
_drawcurve(points, npoints, 0);
}
void fillcurve(t_point *points,
int npoints) {
_drawcurve(points, npoints, 1);
}
void object_start(int all) {
if (all)
hGraphicsDC = hAllObjtestDC;
else
hGraphicsDC = hObjtestDC;
setcolor(WHITE);
fillrect (xleft, ytop, xright, ybot);
setcolor(BLACK);
}
void object_end() {
hGraphicsDC = hCurrentDC;
}
int pt_on_object(int all, float x, float y) {
COLORREF c;
if (all)
c = GetPixel(hAllObjtestDC, xcoord(x), ycoord(y));
else
c = GetPixel(hObjtestDC, xcoord(x), ycoord(y));
// printf("c = %x\n", c);
return c == win32_colors[BLACK];
}
static int check_fontsize(int pointsize,
float ymax) {
// return 0 if matches, 1 if font too big, -1 if font too small
// a font matches if it's height is 90-100% of ymax tall
float height;
TEXTMETRIC textmetric;
HFONT hOldFont;
int ret;
setfontsize(pointsize);
hOldFont = (HFONT)SelectObject(hGraphicsDC, hGraphicsFont);
if(!(hOldFont))
SELECT_ERROR();
if (!GetTextMetrics(hGraphicsDC, &textmetric))
DRAW_ERROR();
height = (textmetric.tmAscent + 2 * textmetric.tmDescent) / ymult;
if (height >= ymax * 0.9) {
if (height <= ymax)
ret = 0;
else
ret = 1;
}
else
ret = -1;
if(!SelectObject(hGraphicsDC, hOldFont))
SELECT_ERROR();
return ret;
}
int findfontsize(float ymax) {
// find the correct point size which will fit in the specified ymax as the max
// height of the font, using a binary search
int bot = 1;
int top = MAX_FONT_SIZE;
int mid, check;
while (bot <= top) {
mid = (bot+top)/2;
check = check_fontsize(mid, ymax);
if (!(check))
return mid;
else if (check > 0) // too big
top = mid - 1;
else // too small
bot = mid + 1;
}
if (bot > MAX_FONT_SIZE)
return MAX_FONT_SIZE;
return -1; // can't fit
}
#endif /******** Win32 Specific Definitions ********************/
#else /***** NO_GRAPHICS *******/
/* No graphics at all. Stub everything out so calling program doesn't have to change
* but of course graphics won't do anything.
*/
#include "graphics.h"
void event_loop (void (*act_on_mousebutton) (float x, float y),
void (*act_on_mousemove) (float x, float y),
void (*act_on_keypress) (char key_pressed),
void (*drawscreen) (void)) { }
void init_graphics (const char *window_name, int cindex) { }
void close_graphics (void) { }
void update_message (const 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 (void) { 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 drawellipticarc (float xc, float yc, float radx, float rady, float startang, float angextent) { }
void fillarc (float xcen, float ycen, float rad, float startang,
float angextent) { }
void fillellipticarc (float xc, float yc, float radx, float rady, float startang, float angextent) { }
void drawtext (float xc, float yc, const char *text, float boundx) { }
void clearscreen (void) { }
void create_button (const char *prev_button_text , const char *button_text,
void (*button_func) (void (*drawscreen) (void))) { }
void destroy_button (const char *button_text) { }
int init_postscript (const char *fname) {
return (1);
}
void close_postscript (void) { }
void report_structure(t_report*) { }
void set_mouse_move_input (bool) { }
void set_keypress_input (bool) { }
void set_draw_mode (enum e_draw_mode draw_mode) { }
void enable_or_disable_button(int ibutton, bool enabled) { }
void change_button_text(const char *button_text, const char *new_button_text) { }
#ifdef WIN32
void drawtobuffer(void) { }
void drawtoscreen(void) { }
void displaybuffer(void) { }
void drawcurve(t_point *points, int npoints) { }
void fillcurve(t_point *points, int npoints) { }
void object_start(int all) { }
void object_end() { }
int pt_on_object(float x, float y) { }
int findfontsize(float ymax) { }
#endif // WIN32 (subset of commands)
#endif // NO_GRAPHICS