bradley@DSL.CIS.UPENN.EDU.UUCP (03/27/87)
This is a vaguely-nifty little program that brings a scientific calculator up on your favorite X display. The calculator bears more than a passing resemblance to a TI-30SLR. I've gotten it to run on both an IBM RT (under BSD 4.2), and a MicroVAX II, (under Ultrix 1.2). You shouldn't have any problems porting it to any other 4.2 systems, particularly if you are familiar (or willing to get that way) with your machine's floating point routines. Comments, Fan Mail, and Verbal Abuse cheerfully accepted. John Bradley, University of Pennsylvania (bradley@cis.upenn.edu) P.S. Sorry if this is the wrong place to post this, but net.sources seemed like the wrong place to post something as 'specialized' as X sources. P.P.S Sorry if this is a repost, but the first shot didn't seem to properly escape from the local cluster. -----------------------------------(cut here)--------------------------------- #!/bin/sh # to extract, remove the header and type "sh filename" if `test ! -s ./Makefile` then echo "writing ./Makefile" cat > ./Makefile << '\Rogue\Monster\' INCLUDES = -I../include XLIB = -lX CFLAGS = $(INCLUDES) # add -DIEEE if you have the IEEE fp routines CLIBS = -lm .SUFFIXES: .o .h .c .a OBJS = xcalc.o all: xcalc xcalc: $(OBJS) cc $(CFLAGS) -o xcalc $(OBJS) $(XLIB) $(CLIBS) \Rogue\Monster\ else echo "will not over write ./Makefile" fi if `test ! -s ./README` then echo "writing ./README" cat > ./README << '\Rogue\Monster\' Notes on xcalc: I've gotten the sucker to run on an IBM RT, under BSD 4.2, and on a MicroVAX GPX, under Ultrix 1.2. The RT is lucky enough to have the IEEE math routines. Under these routines, if you say "x = 1.0 / 0.0", it doesn't blow up. Instead, when you decide to printf() x, it shows up as "INF". The routines also have the nice feature of being terribly accurate. Needless to say, this all makes writing a calculator fun/E-Z. On the MicroVAX, however, I had to use a wretched math package, one that only goes between 1E-38 and 1E+38, one that isn't very accurate, and one that will cause an FP Exception at the slightest prompting. As a result, I had to put in all sorts of evil nasty 'sigvec's and 'setjmp's and things. I think it takes away from the intrinsic beauty (hah!) of the code, but at least it doesn't say "floating exception: core dumped" anymore, so it's a step in the right direction. The point is: If you're lucky enough to have the IEEE routines, by all means add a -DIEEE to the CFLAGS line in the Makefile. -jhb, 3/16/86 \Rogue\Monster\ else echo "will not over write ./README" fi if `test ! -s ./cursor` then echo "writing ./cursor" cat > ./cursor << '\Rogue\Monster\' #define cursor_width 16 #define cursor_height 16 #define cursor_x_hot 3 #define cursor_y_hot 1 static short cursor_bits[] = { 0x0000, 0x0008, 0x0018, 0x0038, 0x0078, 0x00f8, 0x01f8, 0x03f8, 0x07f8, 0x00f8, 0x00d8, 0x0188, 0x0180, 0x0300, 0x0300, 0x0000}; \Rogue\Monster\ else echo "will not over write ./cursor" fi if `test ! -s ./cursor.mask` then echo "writing ./cursor.mask" cat > ./cursor.mask << '\Rogue\Monster\' #define cursor_mask_width 16 #define cursor_mask_height 16 static short cursor_mask_bits[] = { 0x000c, 0x001c, 0x003c, 0x007c, 0x00fc, 0x01fc, 0x03fc, 0x07fc, 0x0ffc, 0x0ffc, 0x01fc, 0x03dc, 0x03cc, 0x0780, 0x0780, 0x0300}; \Rogue\Monster\ else echo "will not over write ./cursor.mask" fi if `test ! -s ./deg.bm` then echo "writing ./deg.bm" cat > ./deg.bm << '\Rogue\Monster\' #define deg_width 18 #define deg_height 5 static short deg_bits[] = { 0xe7cf, 0x0001, 0x1051, 0x0000, 0xd1d1, 0x0001, 0x1051, 0x0001, 0xe7cf, 0x0001}; \Rogue\Monster\ else echo "will not over write ./deg.bm" fi if `test ! -s ./e.bm` then echo "writing ./e.bm" cat > ./e.bm << '\Rogue\Monster\' #define e_width 8 #define e_height 8 static short e_bits[] = { 0x00ff, 0x00ff, 0x0003, 0x001f, 0x001f, 0x0003, 0x00ff, 0x00ff}; \Rogue\Monster\ else echo "will not over write ./e.bm" fi if `test ! -s ./grad.bm` then echo "writing ./grad.bm" cat > ./grad.bm << '\Rogue\Monster\' #define grad_width 24 #define grad_height 5 static short grad_bits[] = { 0xe3de, 0x003c, 0x1441, 0x0045, 0xf3dd, 0x0045, 0x1251, 0x0045, 0x145e, 0x003d}; \Rogue\Monster\ else echo "will not over write ./grad.bm" fi if `test ! -s ./icon` then echo "writing ./icon" cat > ./icon << '\Rogue\Monster\' #define icon_width 48 #define icon_height 48 static short icon_bits[] = { 0xffff, 0x0fff, 0x0000, 0xffff, 0x0fff, 0x0000, 0x0003, 0x0c00, 0x0000, 0x5dd3, 0x0c5d, 0x0000, 0x5113, 0x0c45, 0x0000, 0xddd3, 0x0ddd, 0x0000, 0x1053, 0x0d51, 0x0000, 0x1dd3, 0x0ddd, 0x0000, 0x0003, 0x0c00, 0x0000, 0xffff, 0x0fff, 0x0000, 0xffff, 0x0fff, 0x0000, 0x0001, 0x0800, 0x0000, 0xf7bd, 0x0bde, 0x0000, 0xf7bd, 0x0bde, 0x0000, 0x0001, 0x0800, 0x0000, 0xf7bd, 0x0bde, 0x0000, 0x94a5, 0x0a52, 0x0000, 0xf7bd, 0x0bde, 0x0000, 0x0001, 0x0800, 0x0000, 0xf7bd, 0x0bde, 0x0000, 0xf7bd, 0x0bde, 0x0000, 0xf7bd, 0x0bde, 0x0000, 0x0001, 0x0800, 0x0000, 0xf7bd, 0x0bde, 0x0000, 0xf7bd, 0x0bde, 0x0000, 0xf7bd, 0x0bde, 0x0000, 0x0001, 0x0800, 0x0000, 0xf7bd, 0x0bde, 0x0000, 0x94a5, 0x0bde, 0x0000, 0x94a5, 0x0bde, 0x0000, 0xf7bd, 0x0bde, 0x0000, 0x0001, 0x0800, 0x0000, 0xf7bd, 0x0bde, 0x0000, 0x94a5, 0x0bde, 0x0000, 0x94a5, 0x0bde, 0x0000, 0xf7bd, 0x0bde, 0x0000, 0x0001, 0x0800, 0x0000, 0xf7bd, 0x0bde, 0x0000, 0x94a5, 0x0bde, 0x0000, 0x94a5, 0x0bde, 0x0000, 0xf7bd, 0x0bde, 0x0000, 0x0001, 0x0800, 0x0000, 0xf7fd, 0x0bde, 0x0000, 0x9405, 0x0bde, 0x0000, 0x9405, 0x0bde, 0x0000, 0xf7fd, 0x0bde, 0x0000, 0x0001, 0x0800, 0x0000, 0xffff, 0x0fff, 0x0000}; \Rogue\Monster\ else echo "will not over write ./icon" fi if `test ! -s ./inv.bm` then echo "writing ./inv.bm" cat > ./inv.bm << '\Rogue\Monster\' #define inv_width 18 #define inv_height 5 static short inv_bits[] = { 0x4517, 0x0000, 0x4532, 0x0000, 0x4552, 0x0000, 0x2992, 0x0000, 0x1117, 0x0000}; \Rogue\Monster\ else echo "will not over write ./inv.bm" fi if `test ! -s ./k.bm` then echo "writing ./k.bm" cat > ./k.bm << '\Rogue\Monster\' #define k_width 6 #define k_height 5 static short k_bits[] = { 0x0011, 0x0009, 0x0007, 0x0009, 0x0011}; \Rogue\Monster\ else echo "will not over write ./k.bm" fi if `test ! -s ./m.bm` then echo "writing ./m.bm" cat > ./m.bm << '\Rogue\Monster\' #define m_width 8 #define m_height 8 static short m_bits[] = { 0x00c3, 0x00e7, 0x00ff, 0x00ff, 0x00db, 0x00c3, 0x00c3, 0x00c3}; \Rogue\Monster\ else echo "will not over write ./m.bm" fi if `test ! -s ./paren.bm` then echo "writing ./paren.bm" cat > ./paren.bm << '\Rogue\Monster\' #define paren_width 12 #define paren_height 5 static short paren_bits[] = { 0x0104, 0x0202, 0x0202, 0x0202, 0x0104}; \Rogue\Monster\ else echo "will not over write ./paren.bm" fi if `test ! -s ./rad.bm` then echo "writing ./rad.bm" cat > ./rad.bm << '\Rogue\Monster\' #define rad_width 18 #define rad_height 5 static short rad_bits[] = { 0xf38f, 0x0000, 0x1451, 0x0001, 0x17cf, 0x0001, 0x1449, 0x0001, 0xf451, 0x0000}; \Rogue\Monster\ else echo "will not over write ./rad.bm" fi if `test ! -s ./xcalc.1` then echo "writing ./xcalc.1" cat > ./xcalc.1 << '\Rogue\Monster\' .TH XCALC 1 "16 March 1987" "X Version 10" .SH NAME xcalc \- X based scientific calculator .SH SYNOPSIS .B xcalc [host:display] [-bw <pixels>] [-stip] [-rv] [=geometry] .SH DESCRIPTION .I Xcalc is a program that brings up a scientific calculator desk accessory. The calculator should remind you more than a little bit of the TI-30. .SH OPTIONS .PP .TP 14 .B \-bw The border width in pixels .PP .TP 14 .B \-stip Sets the 'stipple' option. See 'defaults'. .PP .TP 14 .B \-rv Reverse video (on monochrom displays). .PP .TP 14 .B \=geometry The width and height shouldn't be set by the user, as the default size is also the minimum size, and anything larger than that won't be 'right'. You can, however feel free to set the position. .PP .TP 14 .B \fIhost\fP:\fIdisplay\fP Normally, .I xcalc gets the host and display number to use from the environment variable ``DISPLAY''. One can, however specify them explicitly. The .I host specifies which machine to create the .I xcalc window on, and the .I display argument specifies the display number. .SH OPERATION .PP .I Mouse Operation: The left button is the only one (really) used to operate the calculator. Pressing the AC key with the right button terminates the calculator. .PP .I Key Usage: The number keys, the +/- key, and the +, -, *, /, and = keys all do exactly what you would expect them to. It should be noted that the operators obey the standard rules of precedence. Thus, entering "3+4*5=" results in "23", not "35". The parentheses can be used to override this. For example, "(1+2+3)*(4+5+6)=" results in "6*15=90". The non-obvious keys are detailed below. .PP .B 1/x replaces the number in the display with its reciprocal. .PP .B x^2 squares the number in the display. .PP .B SQRT takes the square root of the number in the display. .PP .B CE/C when pressed once, clears the number in the display without clearing the state of the machine. Allows you to re-enter a number if you screw it up. Pressing it twice clears the state, also. .PP .B AC clears everything, the display, the state, the memory, everything. Pressing it with the right button 'turns off' the calculator, in that it exits the program. Somewhat more equivalent to throwing the calculator in the trash, if we were to pursue the analogy. .PP .B INV inverts the meaning of the function keys. See the individual function keys for details. .PP .B sin computes the sine of the number in the display, as interpreted by the current DRG mode (see DRG, below). If inverted, computes the arcsine. .PP .B cos computes the cosine, or arccosine when inverted. .PP .B tan computes the tangent, or arctangent when inverted. .PP .B DRG changes the DRG mode, as indicated by 'DEG', 'RAD', or 'GRAD' at the bottom of the display. When in 'DEG' mode, numbers in the display are taken as being degrees. In 'RAD' mode, numbers are in radians, and in 'GRAD' mode, numbers are in gradians. When inverted, the DRG key has the nifty feature of converting degrees to radians to gradians and vice-versa. Example: put the calculator into 'DEG' mode, and type "45 INV DRG". The display should now show something along the lines of ".785398", which is 45 degrees converted to radians. .PP .B e the constant 'e'. (2.7182818...) .PP .B EE used for entering exponential numbers. For example, to enter "-2.3E-4" you'd type "2 . 3 +/- EE 4 +/-" .PP .B log calculates the log (base 10) of the number in the display. When inverted, raises "10.0" to the number in the display. For example, typing "3 INV log" should result in "1000". .PP .B ln calcuates the log (base e) of the number in the display. When inverted, raises "e" to the number in the display. For example, typing "e ln" should result in "1" .PP .B y^x raises the number on the left to the power of the number on the right. For example "2 y^x 3 =" results in "8", which is 2^3. For a further example, "(1+2+3) y^x (1+2) =" equals "6 y^x 3" which equals "216". .PP .B PI the constant 'pi'. (3.1415927....) .PP .B x! computes the factorial of the number in the display. The number in the display must be an integer in the range 0-500, though, depending on your math library, it might overflow long before that. .PP .B STO copies the number in the display to the memory location. .PP .B RCL copies the number from the memory location to the display. .PP .B SUM adds the number in the display to the number in the memory location. .PP .B EXC swaps the number in the display with the number in the memory location. .SH KEYBOARD EQUIVALENTS If you have the mouse in the xcalc window, you can use the keyboard to speed entry, as almost all of the calculator keys have a keyboard equivalent. The number keys, the operator keys, and the parentheses all have the obvious equivalent. The less-obvious equivalents are as follows: .PP .EX n: +/- !: x! p: PI e: EE l: ln ^: y^x i: INV s: sin c: cos t: tan d: DRG BS, DEL: CE/C .SH COLOR USAGE .I Xcalc uses a lot of colors, given the opportunity. In the default case, it will just use two colors (Foreground and Background) for everything. This works out nicely. However, if you're a color fanatic you can specify the colors used for the number keys, the operator (+-*/=) keys, the function keys, the display, and the icon. .SH X DEFAULTS .PP .TP 8 .B BorderWidth width of border. Default is '2'. .PP .TP 8 .B ReverseVideo reverses colors on monochrome displays .PP .TP 8 .B Stipple makes the calculator background a 50% stipple. Default is 'on' on monochrome displays, 'off' on color displays. .PP .TP 8 .B Foreground the default color used for borders and text. .PP .TP 8 .B Background the default color used for the background. .B NKeyFore, NKeyBack the colors used for the number keys. .PP .TP 8 .B OKeyFore, OKeyBack the colors used for the operator keys. .PP .TP 8 .B FKeyFore, FKeyBack the colors used for the function keys. .B DispFore, DispBack the colors used for the display. .B IconFore, IconBack the colors used for the icon. .SH SAMPLE .XDEFAULTS ENTRY If you're running on a monochrome display, you shouldn't need any .Xdefaults entries for xcalc. On a color display, you might want to try the following: .EX xcalc.Foreground: Black xcalc.Background: LightSteelBlue xcalc.NKeyFore: Black xcalc.NKeyBack: White xcalc.OKeyFore: Aquamarine xcalc.OKeyBack: DarkSlateGray xcalc.FKeyFore: White xcalc.FKeyBack: #900 xcalc.DispFore: Yellow xcalc.DispBack: #777 xcalc.IconFore: Red xcalc.IconBack: White <well, *I* like them.> .SH BUGS Well, it would be really nice if you could (usefully) rescale the calculator, and the redraw of the keys is sort of slow. Nothing fatal though, I think. .SH AUTHOR John Bradley, University of Pennsylvania (bradley@cis.upenn.edu) \Rogue\Monster\ else echo "will not over write ./xcalc.1" fi if `test ! -s ./xcalc.c` then echo "writing ./xcalc.c" cat > ./xcalc.c << '\Rogue\Monster\' /* * xcalc.c - a hand calculator for the X Window system * * Author: John H. Bradley, University of Pennsylvania * (bradley@cis.upenn.edu) * March, 1987 */ /* * color usage: ForeColor is used for all frames, BackColor is the color of * the calculator body. * NKeyFore, NKeyBack are the colors used for the number keys. * OKeyFore, OKeyBack are used for the operator keys (+-*=/). * FKeyFore, FKeyBack are used for all the other keys. * DispFore, DispBack are used for the display. * IconFore, IconBack are used for the display. * * if running on monochrome monitor, or if 'stipple' option * set, the calculator body is a 50% stipple of ForeColor * and BackColor. This looks nice in mono, but can prevent * you from getting the 'right' colors in color mode, so * it's an option. */ #include <stdio.h> #include <math.h> #include <strings.h> #include <signal.h> #include <X/Xlib.h> #include <sys/time.h> #include <setjmp.h> /* nnw arrow cursor */ #include "cursor" #include "cursor.mask" /* program icon */ #include "icon" #define iconRwidth 28 /* actual width of icon */ /* bitmaps for display flags */ #include "deg.bm" #include "rad.bm" #include "grad.bm" #include "inv.bm" #include "k.bm" #include "paren.bm" #include "m.bm" #include "e.bm" /* constants used for setting up the calculator. changing them would probably be bad. If you do, don't forget to change the values in the syntax routine */ #define FONT "6x10" #define DFONT "9x15" #define PADDINGW 4 #define PADDINGH 8 #define DEF_BDRWIDE 2 #define MAXDISP 11 #define KEYPADH 2 #define KEYPADW 2 #define EXTRAH 2 #define DISPPADH 4 #define DISPPADW 8 #define FLAGH 9 #define PI 3.14159265358979 #define E 2.71828182845904 /* DRG mode. used for trig calculations */ #define DEG 0 #define RAD 1 #define GRAD 2 /* handy-dandy functions */ #define max(a, b) ((a) > (b) ? (a) : (b)) #define min(a, b) ((a) < (b) ? (a) : (b)) #define abs(a) ((a) < 0 ? -(a) : (a)) /* colors */ int ForeColor, BackColor, NKeyFore, NKeyBack, OKeyFore, OKeyBack; int FKeyFore, FKeyBack, DispFore, DispBack, IconFore, IconBack; int border = DEF_BDRWIDE; /* objects */ Window theWindow, iconWindow, dispwid; Cursor arrow; Font keyfont, dispfont; FontInfo *kfontinfo, *dfontinfo; Bitmap stippleBit; Pixmap stipplePix; Pixmap regBorder, dimBorder; /* variables used in setup */ int dispwide, disphigh, keywide, keyhigh; /* display flags */ int flagK, flagINV, flagPAREN, flagM, flagE, drgmode; char dispstr[32]; /* stuff defining the keyboard layout. The keypad is a 5x8 matrix of keys */ #define NUMKEYS 40 struct _key { Window wid; char *st; short x,y,width,height; int fore,back; } key[NUMKEYS]; char *keystrings[NUMKEYS]= {"1/x", "x^2", "SQRT","CE/C", "AC", "INV", "sin", "cos", "tan", "DRG", "e", "EE", "log", "ln", "y^x", "PI", "x!", "(", ")", "/", "STO", "7", "8", "9", "*", "RCL", "4", "5", "6", "-", "SUM", "1", "2", "3", "+", "EXC", "0", ".", "+/-", "=" }; #define kRECIP 0 #define kSQR 1 #define kSQRT 2 #define kCLR 3 #define kOFF 4 #define kINV 5 #define kSIN 6 #define kCOS 7 #define kTAN 8 #define kDRG 9 #define kE 10 #define kEE 11 #define kLOG 12 #define kLN 13 #define kPOW 14 #define kPI 15 #define kFACT 16 #define kLPAR 17 #define kRPAR 18 #define kDIV 19 #define kSTO 20 #define kSEVEN 21 #define kEIGHT 22 #define kNINE 23 #define kMUL 24 #define kRCL 25 #define kFOUR 26 #define kFIVE 27 #define kSIX 28 #define kSUB 29 #define kSUM 30 #define kONE 31 #define kTWO 32 #define kTHREE 33 #define kADD 34 #define kEXC 35 #define kZERO 36 #define kDEC 37 #define kNEG 38 #define kEQU 39 /* checkerboard used in mono mode */ static short check_bits[] = { 0x5555, 0xaaaa, 0x5555, 0xaaaa, 0x5555, 0xaaaa, 0x5555, 0xaaaa, 0x5555, 0xaaaa, 0x5555, 0xaaaa, 0x5555, 0xaaaa, 0x5555, 0xaaaa}; #ifndef IEEE jmp_buf env; #endif /**************/ main(argc, argv) int argc; char *argv[]; /**************/ { int i, status,dpcs; char *strind; #ifndef IEEE extern fperr(); #endif char *fc, *bc, *nfc, *nbc, *ofc, *obc, *ffc, *fbc, *dfc, *dbc, *ifc, *ibc; char *geom = NULL; char *display = NULL; int rvflag = 0; /* don't use reverse video as a default */ int stip = 0; /* don't stipple background by default */ int invkey = -1; Color cdef; XEvent event; char *def; /*********************Defaults*********************/ if ((def=XGetDefault(argv[0],"BorderWidth"))!=NULL) border=atoi(def); if ((def=XGetDefault(argv[0],"ReverseVideo"))!=NULL) if (strcmp(def,"on")==0) rvflag=1; if ((def=XGetDefault(argv[0],"Stipple"))!=NULL) if (strcmp(def,"on")==0) stip=1; fc = XGetDefault(argv[0], "Foreground"); bc = XGetDefault(argv[0], "Background"); nfc = XGetDefault(argv[0], "NKeyFore"); nbc = XGetDefault(argv[0], "NKeyBack"); ofc = XGetDefault(argv[0], "OKeyFore"); obc = XGetDefault(argv[0], "OKeyBack"); ffc = XGetDefault(argv[0], "FKeyFore"); fbc = XGetDefault(argv[0], "FKeyBack"); dfc = XGetDefault(argv[0], "DispFore"); dbc = XGetDefault(argv[0], "DispBack"); ifc = XGetDefault(argv[0], "IconFore"); ibc = XGetDefault(argv[0], "IconBack"); /*********************Options*********************/ for (i = 1; i < argc; i++) { if (argv[i][0] == '=') { geom = argv[i]; continue; } strind = index(argv[i], ':'); if(strind != NULL) { display = argv[i]; continue; } if (strcmp(argv [i], "-bw") == 0) { if (++i >= argc) Syntax(argv[0]); border = atoi(argv [i]); continue; } if (strcmp(argv [i], "-stip") == 0) { stip=1; continue; } if (strcmp(argv [i], "-help") == 0) { Syntax(argv[0]); } if (strcmp(argv [i], "-rv") == 0) { rvflag++; continue; } Syntax(argv[0]); } /*****************************************************/ /* Open up the display. */ if (XOpenDisplay(display) == NULL) { fprintf(stderr, "%s: Can't open display '%s'\n", argv[0], DisplayName()); exit(1); } /* Set up colors and pixmaps. */ /* Set normal default colors */ if (!rvflag) { ForeColor=BlackPixel; BackColor=WhitePixel; } else { ForeColor=WhitePixel; BackColor=BlackPixel; } dpcs=DisplayCells(); if (dpcs<=2) stip=1; /* monochrome display */ if (dpcs>2&&(fc !=NULL)&&XParseColor(fc ,&cdef)&&XGetHardwareColor(&cdef)) ForeColor=cdef.pixel; if (dpcs>2&&(bc !=NULL)&&XParseColor(bc ,&cdef)&&XGetHardwareColor(&cdef)) BackColor=cdef.pixel; NKeyFore = OKeyFore = FKeyFore = DispFore = IconFore = ForeColor; NKeyBack = OKeyBack = FKeyBack = DispBack = IconBack = BackColor; if (dpcs>2&&(nfc!=NULL)&&XParseColor(nfc,&cdef)&&XGetHardwareColor(&cdef)) NKeyFore=cdef.pixel; if (dpcs>2&&(nbc!=NULL)&&XParseColor(nbc,&cdef)&&XGetHardwareColor(&cdef)) NKeyBack=cdef.pixel; if (dpcs>2&&(ofc!=NULL)&&XParseColor(ofc,&cdef)&&XGetHardwareColor(&cdef)) OKeyFore=cdef.pixel; if (dpcs>2&&(obc!=NULL)&&XParseColor(obc,&cdef)&&XGetHardwareColor(&cdef)) OKeyBack=cdef.pixel; if (dpcs>2&&(ffc!=NULL)&&XParseColor(ffc,&cdef)&&XGetHardwareColor(&cdef)) FKeyFore=cdef.pixel; if (dpcs>2&&(fbc!=NULL)&&XParseColor(fbc,&cdef)&&XGetHardwareColor(&cdef)) FKeyBack=cdef.pixel; if (dpcs>2&&(dfc!=NULL)&&XParseColor(dfc,&cdef)&&XGetHardwareColor(&cdef)) DispFore=cdef.pixel; if (dpcs>2&&(dbc!=NULL)&&XParseColor(dbc,&cdef)&&XGetHardwareColor(&cdef)) DispBack=cdef.pixel; if (dpcs>2&&(ifc!=NULL)&&XParseColor(ifc,&cdef)&&XGetHardwareColor(&cdef)) IconFore=cdef.pixel; if (dpcs>2&&(ibc!=NULL)&&XParseColor(ibc,&cdef)&&XGetHardwareColor(&cdef)) IconBack=cdef.pixel; /* load fonts, figure out sizes of keypad and display */ kfontinfo = XOpenFont(FONT); if (!kfontinfo) XCalcError("Can't open '%s' font\n",FONT); keyfont = kfontinfo->id; dfontinfo = XOpenFont(DFONT); if (!dfontinfo) XCalcError("Can't open '%s' font\n",DFONT); dispfont = dfontinfo->id; keywide = XQueryWidth("MMMM", keyfont) + KEYPADW; keyhigh = kfontinfo->height + KEYPADH; dispwide = 5*keywide+4*PADDINGW; disphigh = dfontinfo->height + DISPPADH + FLAGH; /* Open the main window. */ { int min_width, min_height; char def_geom[32]; OpaqueFrame frame; min_width = dispwide + 2*PADDINGW + 2; min_height = disphigh + 2 + 8*keyhigh + 10*PADDINGH + 10*EXTRAH; sprintf (def_geom, "=%dx%d-16-16", min_width, min_height); frame.bdrwidth = border; stippleBit = XStoreBitmap(16,16,check_bits); stipplePix = XMakePixmap(stippleBit,ForeColor,BackColor); regBorder = XMakeTile(ForeColor); dimBorder = stipplePix; frame.border = dimBorder; if (stip) frame.background=stipplePix; else frame.background=XMakeTile(BackColor); theWindow = XCreate("Calculator", argv[0], geom, def_geom, &frame, min_width, min_height); if (!theWindow) XCalcError("Can't open calculator window"); iconWindow = XCreateWindow(RootWindow,0,0,iconRwidth,icon_height, 0,frame.border,XMakeTile(BackColor)); XSetIconWindow(theWindow,iconWindow); SetupCalc(); } arrow=XCreateCursor(cursor_width,cursor_height,cursor_bits, cursor_mask_bits, cursor_x_hot, cursor_y_hot, ForeColor, BackColor, GXcopy); XSelectInput(theWindow, ExposeWindow|ExposeRegion|KeyPressed|EnterWindow|LeaveWindow); XSelectInput(iconWindow,ExposeWindow|ExposeRegion); XSelectInput(dispwid, ExposeWindow|ExposeRegion|EnterWindow|LeaveWindow); for (i=0; i<NUMKEYS; i++) XSelectInput(key[i].wid,ExposeWindow|ExposeRegion| ButtonPressed|ButtonReleased| EnterWindow|LeaveWindow); XMapWindow (theWindow); XMapSubwindows(theWindow); XDefineCursor(theWindow,arrow); /*********** Set up SIGFPE hander ***********/ #ifndef IEEE signal(SIGFPE,fperr); #endif /**************** Main loop *****************/ while (1) { Window wind; XNextEvent(&event); switch (event.type) { case ExposeRegion: case ExposeWindow: { XExposeWindowEvent *exp_event = (XExposeWindowEvent *) &event; wind = exp_event->window; if (wind==dispwid) DrawDisplay(); else if (wind==iconWindow) XBitmapBitsPut(iconWindow,0,0,icon_width,icon_height, icon_bits, IconFore, IconBack, 0, GXcopy, AllPlanes); else { for (i=0; i<NUMKEYS; i++) { if (key[i].wid==wind) DrawKey(i); } } } break; case ButtonPressed: { XButtonEvent *but_event = (XButtonEvent *) &event; wind = but_event->window; if ((but_event->detail & 0xff) == LeftButton) { for (i=0; i<NUMKEYS; i++) { if (key[i].wid==wind) { PressedKey(i); invkey=i; } } } else if ((but_event->detail & 0xff) == RightButton) { if (key[kOFF].wid==wind) Quit(); else XFeep(0); } } break; case ButtonReleased: { XButtonEvent *but_event = (XButtonEvent *) &event; wind = but_event->window; if ((but_event->detail & 0xff) == LeftButton) { for (i=0; i<NUMKEYS; i++) { if (key[i].wid==wind) LetgoKey(invkey); } invkey = -1; } } break; case KeyPressed: { XKeyEvent *key_event = (XKeyEvent *) &event; wind = key_event->window; if (wind==theWindow) { char *st; st = XLookupMapping(key_event,&i); for ( ; i>0; i--) TypeChar(*st++); } else { printf("KeyPressed in window %ld\n",wind); } } break; case EnterWindow: case LeaveWindow: { XMouseOrCrossingEvent *cross_event = (XMouseOrCrossingEvent *) &event; if ( ((cross_event->detail & 0xff)==2) || ( ((cross_event->detail & 0xff)==0) && (cross_event->window==theWindow) ) ) { if (cross_event->type==EnterWindow) XChangeBorder(theWindow,regBorder); else XChangeBorder(theWindow,dimBorder); } } break; default: printf("event type=%ld\n",event.type); XCalcError("Unexpected X_Event"); } /* end of switch */ } /* end main loop */ } /***********************************/ Syntax(call) char *call; { printf ("Usage: %s [-bw <pixels>] [-stip] [-help] [-rv] [[<host>]:[<vs>]]\n"); printf (" [=geometry]\n\n"); printf ("Default: %s -bw %d =156x226-16-16\n\n",call, DEF_BDRWIDE); exit(0); } /***********************************/ XCalcError (identifier) char *identifier; { fprintf(stderr, "xcalc: %s\n", identifier); exit(1); } /***********************************/ SetupCalc() { Pixmap bd,nb,ob,fb,db; OpaqueFrame keyfrm[NUMKEYS], display; int i; bd=XMakeTile(ForeColor); nb=XMakeTile(NKeyBack); ob=XMakeTile(OKeyBack); fb=XMakeTile(FKeyBack); db=XMakeTile(DispBack); for (i=0; i<NUMKEYS; i++) { keyfrm[i].x = PADDINGW+(i%5)*(keywide+PADDINGW); keyfrm[i].y = disphigh+2+2*PADDINGH+(i/5)*(keyhigh+PADDINGH); keyfrm[i].width = keywide; keyfrm[i].height = keyhigh; if (i>=15) { keyfrm[i].height+=(2*EXTRAH); keyfrm[i].y += ((i-15)/5)*EXTRAH*2; } keyfrm[i].bdrwidth=1; keyfrm[i].border=bd; switch (i) { case kZERO: case kONE: case kTWO: case kTHREE: case kFOUR: case kFIVE: case kSIX: case kSEVEN: case kEIGHT: case kNINE: case kDEC: case kNEG: keyfrm[i].background=nb; key[i].fore=NKeyFore; key[i].back=NKeyBack; break; case kADD: case kSUB: case kMUL: case kDIV: case kEQU: keyfrm[i].background=ob; key[i].fore=OKeyFore; key[i].back=OKeyBack; break; default: keyfrm[i].background=fb; key[i].fore=FKeyFore; key[i].back=FKeyBack; } } dispwid = XCreateWindow(theWindow,PADDINGW,PADDINGH,dispwide-2,disphigh, 2,bd,db); XCreateWindows(theWindow,keyfrm,NUMKEYS); for (i=0; i<NUMKEYS; i++) { key[i].wid = keyfrm[i].self; key[i].st = keystrings[i]; key[i].x = keyfrm[i].x; key[i].y = keyfrm[i].y; key[i].width = keyfrm[i].width; key[i].height = keyfrm[i].height; } ResetCalc(); } /**************/ DrawDisplay() { WindowInfo info; int strwide; if (strlen(dispstr)>12) { /* strip out some decimal digits */ char tmp[32]; char *exp = index(dispstr,'e'); /* search for exponent part */ if (!exp) dispstr[12]='\0'; /* no exp, just trunc. */ else { if (strlen(exp)<=4) sprintf(tmp,"%.8s",dispstr); /* leftmost 8 chars */ else sprintf(tmp,"%.7s",dispstr); /* leftmost 7 chars */ strcat (tmp,exp); /* plus exponent */ strcpy (dispstr,tmp); } } strwide=XStringWidth(dispstr,dfontinfo,0,0); XPixSet(dispwid,10,0,dispwide-20,disphigh,DispBack); XPixSet(dispwid,0,0,10,disphigh,ForeColor); XPixSet(dispwid,dispwide-10,0,10,disphigh,ForeColor); XText(dispwid,dispwide-10-DISPPADW/2-strwide,DISPPADH/2, dispstr,strlen(dispstr),dispfont,DispFore,DispBack); if (flagM) XBitmapBitsPut(dispwid,12,2, m_width,m_height,m_bits, DispFore,DispBack,0,GXcopy,AllPlanes); if (flagE) XBitmapBitsPut(dispwid,12,12, e_width,e_height,e_bits, DispFore,DispBack,0,GXcopy,AllPlanes); if (flagK) XBitmapBitsPut(dispwid,20,disphigh-FLAGH+2, k_width,k_height,k_bits, DispFore,DispBack,0,GXcopy,AllPlanes); if (flagINV) XBitmapBitsPut(dispwid,30,disphigh-FLAGH+2, inv_width,inv_height,inv_bits, DispFore,DispBack,0,GXcopy,AllPlanes); if (flagPAREN) XBitmapBitsPut(dispwid,dispwide-22,disphigh-FLAGH+2, paren_width,paren_height,paren_bits, DispFore,DispBack,0,GXcopy,AllPlanes); if (drgmode==DEG) XBitmapBitsPut(dispwid,60,disphigh-FLAGH+2, deg_width,deg_height,deg_bits, DispFore,DispBack,0,GXcopy,AllPlanes); if (drgmode==RAD) XBitmapBitsPut(dispwid,80,disphigh-FLAGH+2, rad_width,rad_height,rad_bits, DispFore,DispBack,0,GXcopy,AllPlanes); if (drgmode==GRAD) XBitmapBitsPut(dispwid,100,disphigh-FLAGH+2, grad_width,grad_height,grad_bits, DispFore,DispBack,0,GXcopy,AllPlanes); } /***************/ DrawKey(keynum) int keynum; { WindowInfo info; char *str; int strwide,extrapad; struct _key *kp; kp = &key[keynum]; str = kp->st; strwide = XStringWidth(str,kfontinfo,0,0); extrapad = (keynum>=15) ? EXTRAH : 0; XText(kp->wid,(kp->width-strwide)/2,KEYPADH/2+extrapad, str,strlen(str),keyfont,kp->fore,kp->back); } /*********************************/ InvertKey(keynum) int keynum; { WindowInfo info; struct _key *kp; kp = &key[keynum]; XPixFill(kp->wid,0,0,kp->width,kp->height,1,NULL,GXinvert,1); } /*********************************/ PressedKey(keynum) int keynum; { /* currently, pressing a key merely inverts it. Nothing happens until it is released. */ InvertKey(keynum); } static double drg2rad=PI/180.0; /* Conversion factors for trig funcs */ static double rad2drg=180.0/PI; static int entered=1; /* true if display contains a valid number. if==2, then use 'dnum', rather than the string stored in the display. (for accuracy) if==3, then error occurred, only CLR & AC work */ /*********************************/ LetgoKey(keynum) int keynum; { int i,j; static int clear =0; /* CLR clears display. if 1, clears acc, also */ static int off =0; /* once clears mem, twice quits */ static int decimal=0; /* to prevent using decimal pt twice in a # */ static int clrdisp=1; /* if true clears display before entering # */ static int accset =0; static int lastop =kCLR; static int exponent=0; static double acc =0.0; static double dnum=0.0; static double mem =0.0; double dtmp, PopNum(); if (keynum==-1) return; InvertKey(keynum); if ( (entered==3) && !(keynum==kCLR || keynum==kOFF)) { XFeep(0); return; } if (keynum != kCLR) clear=0; if (keynum != kOFF) off=0; #ifndef IEEE i=setjmp(env); if (i) { switch (i) { case FPE_FLTDIV_TRAP: strcpy(dispstr,"div by zero"); break; case FPE_FLTDIV_FAULT: strcpy(dispstr,"div by zero"); break; case FPE_FLTOVF_TRAP: strcpy(dispstr,"overflow"); break; case FPE_FLTOVF_FAULT: strcpy(dispstr,"overflow"); break; case FPE_FLTUND_TRAP: strcpy(dispstr,"underflow"); break; case FPE_FLTUND_FAULT: strcpy(dispstr,"underflow"); break; default: strcpy(dispstr,"error"); } entered=3; DrawDisplay(); return; } #endif switch (keynum) { case kZERO: case kONE: case kTWO: case kTHREE: case kFOUR: case kFIVE: case kSIX: case kSEVEN: case kEIGHT: case kNINE: flagINV=0; if (clrdisp) { dispstr[0]='\0'; exponent=decimal=0; } if (strlen(dispstr)>=MAXDISP) break; strcat(dispstr,key[keynum].st); DrawDisplay(); if (clrdisp && keynum!=kZERO) clrdisp=0; /*no leading 0s*/ entered=1; break; case kDEC: flagINV=0; if (clrdisp) strcpy(dispstr,"0"); if (!decimal) { strcat(dispstr,"."); DrawDisplay(); decimal++; } clrdisp=0; entered=1; break; case kEE: flagINV=0; if (clrdisp) strcpy(dispstr,"0"); if (!exponent) { strcat(dispstr,"E+"); DrawDisplay(); exponent=strlen(dispstr)-1; /* where the '-' goes */ } clrdisp=0; entered=1; break; case kCLR: flagINV=0; if (clear) { /* clear all */ ClearStacks(); flagPAREN=0; } clear++; exponent=decimal=0; clrdisp=1; entered=1; strcpy(dispstr,"0"); DrawDisplay(); break; case kNEG: flagINV=0; if (exponent) { /* neg the exponent */ if (dispstr[exponent]=='-') dispstr[exponent]='+'; else dispstr[exponent]='-'; DrawDisplay(); break; } if (strcmp("0",dispstr)==0) break; /* don't neg a zero */ if (dispstr[0]=='-') { /* already neg-ed */ strcpy(dispstr,dispstr+1); /* move str left once */ } else { /* not neg-ed. add a '-' */ char tmp[32]; sprintf(tmp,"-%s",dispstr); strcpy(dispstr,tmp); } if (entered==2) dnum = -1.0 * dnum; DrawDisplay(); break; case kADD: case kSUB: case kMUL: case kDIV: case kPOW: if (flagINV) { flagINV=0; DrawDisplay(); } if (!entered) { /* something like "5+*" */ if (!isopempty()) PopOp(); /* replace the prev op */ PushOp(keynum); /* with the new one */ break; } if (entered==1) sscanf(dispstr,"%lf",&dnum); clrdisp++; entered=decimal=exponent=0; clear=1; if (!isopempty()) { /* there was a previous op */ lastop=PopOp(); /* get it */ if (lastop==kLPAR) { /* put it back */ PushOp(kLPAR); PushOp(keynum); PushNum(dnum); break; } /* now, if the current op (keynum) is of higher priority than the lastop, the current op and number are just pushed on top Priorities: (Y^X) > *,/ > +,- */ if (priority(keynum) > priority(lastop)) { PushNum(dnum); PushOp(lastop); PushOp(keynum); } else { /* execute lastop on lastnum and dnum, push result and current op on stack */ acc=PopNum(); switch (lastop) { /* perform the operation */ case kADD: acc += dnum; break; case kSUB: acc -= dnum; break; case kMUL: acc *= dnum; break; case kDIV: acc /= dnum; break; case kPOW: acc = pow(acc,dnum); break; } PushNum(acc); PushOp(keynum); sprintf(dispstr,"%.8g",acc); DrawDisplay(); dnum=acc; } } else { /* op stack is empty, push op and num */ PushOp(keynum); PushNum(dnum); } break; case kEQU: flagINV=0; if (!entered) break; clrdisp++; decimal=0; exponent=0; clear=1; if (entered==1) sscanf(dispstr,"%lf",&dnum); entered=2; PushNum(dnum); while (!isopempty()) { /* do all pending ops */ dnum=PopNum(); acc=PopNum(); lastop=PopOp(); switch (lastop) { case kADD: acc += dnum; break; case kSUB: acc -= dnum; break; case kMUL: acc *= dnum; break; case kDIV: acc /= dnum; break; case kPOW: acc = pow(acc,dnum); break; } dnum=acc; PushNum(dnum); } sprintf(dispstr,"%.8g",dnum); DrawDisplay(); break; case kLPAR: flagINV=0; PushOp(kLPAR); flagPAREN++; DrawDisplay(); break; case kRPAR: flagINV=0; if (!entered) break; if (!flagPAREN) break; clrdisp++; decimal=0; exponent=0; if (entered==1) sscanf(dispstr,"%lf",&dnum); entered=2; PushNum(dnum); while (!isopempty() && (lastop=PopOp())!=kLPAR) { /* do all pending ops, back to left paren */ dnum=PopNum(); acc=PopNum(); switch (lastop) { case kADD: acc += dnum; break; case kSUB: acc -= dnum; break; case kMUL: acc *= dnum; break; case kDIV: acc /= dnum; break; case kPOW: acc = pow(acc,dnum); break; } dnum=acc; PushNum(dnum); } PopNum(); flagPAREN--; entered=2; sprintf(dispstr,"%.8g",dnum); DrawDisplay(); break; case kDRG: if (flagINV) { if (entered==1) sscanf(dispstr,"%lf",&dnum); switch (drgmode) { case DEG: dnum=dnum*PI/180.0; break; case RAD: dnum=dnum*200.0/PI; break; case GRAD: dnum=dnum*90.0/100.0; break; } entered=2; clrdisp=1; flagINV=0; sprintf(dispstr,"%.8g",dnum); } flagINV=0; drgmode = ++drgmode % 3; switch (drgmode) { case DEG: drg2rad=PI / 180.0; rad2drg=180.0 / PI; break; case RAD: drg2rad=1.0; rad2drg=1.0; break; case GRAD: drg2rad=PI / 200.0; rad2drg=200.0 / PI; break; } DrawDisplay(); break; case kINV: flagINV = ~flagINV; DrawDisplay(); break; case kE: case kPI: case kRECIP: case kSQR: case kSQRT: case kLOG: case kLN: case kSIN: case kCOS: case kTAN: case kSTO: case kRCL: case kSUM: case kEXC: case kFACT: if (entered==1) sscanf(dispstr,"%lf",&dnum); switch (keynum) { /* do the actual math fn. */ case kE: dnum=E; break; case kPI: dnum=PI; break; case kRECIP: dnum=1.0/dnum; break; case kSQR: dnum=dnum*dnum; break; case kSQRT: dnum=sqrt(dnum); break; case kLOG: if (flagINV) dnum=pow(10.0,dnum); else dnum=log10(dnum); break; case kLN: if (flagINV) dnum=exp(dnum); else dnum=log(dnum); break; case kSIN: if (flagINV) dnum=asin(dnum)*rad2drg; else dnum=sin(dnum*drg2rad); break; case kCOS: if (flagINV) dnum=acos(dnum)*rad2drg; else dnum=cos(dnum*drg2rad); break; case kTAN: if (flagINV) dnum=atan(dnum)*rad2drg; else dnum=tan(dnum*drg2rad); break; case kSTO: mem=dnum; flagM=!(mem==0.0); break; case kRCL: dnum=mem; flagM=!(mem==0.0); break; case kSUM: mem+=dnum; flagM=!(mem==0.0); break; case kEXC: dtmp=dnum; dnum=mem; mem=dtmp; flagM=!(mem==0.0); break; case kFACT: if (floor(dnum)!=dnum || dnum<0.0 || dnum>500.0) { strcpy(dispstr,"error"); entered=3; break; } i=(int) (floor(dnum)); for (j=1,dnum=1.0; j<=i; j++) dnum*=(float) j; break; } if (entered==3) { /* error */ DrawDisplay(); break; } entered=2; clrdisp=1; flagINV=0; sprintf(dispstr,"%.8g",dnum); DrawDisplay(); break; case kOFF: /* full reset */ ResetCalc(); entered=1; dnum=0.0; mem=0.0; accset=0; clrdisp=1; exponent=0; decimal=0; DrawDisplay(); break; default: XFeep(0); } #ifndef IEEE if (errno) { strcpy(dispstr,"error"); DrawDisplay(); entered=3; errno=0; } #endif } /*******/ Quit() /*******/ { exit(0); } #define STACKMAX 32 static int opstack[STACKMAX]; static int opsp; static double numstack[STACKMAX]; static int numsp; /*******/ PushOp(op) int op; /*******/ { if (opsp==STACKMAX) {strcpy(dispstr,"stack error"); entered=3;} else opstack[opsp++]=op; } /*******/ int PopOp() /*******/ { if (opsp==0) {strcpy(dispstr,"stack error"); entered=3;} else return(opstack[--opsp]); } /*******/ int isopempty() /*******/ { return( opsp ? 0 : 1 ); } /*******/ PushNum(num) double num; /*******/ { if (numsp==STACKMAX) {strcpy(dispstr,"stack error"); entered=3;} else numstack[numsp++]=num; } /*******/ double PopNum() /*******/ { if (numsp==0) {strcpy(dispstr,"stack error"); entered=3;} else return(numstack[--numsp]); } /*******/ int isnumempty() /*******/ { return( numsp ? 0 : 1 ); } /*******/ ClearStacks() /*******/ { opsp=numsp=0; } /*******/ int priority(op) int op; /*******/ { switch (op) { case kPOW: return(2); break; case kMUL: case kDIV: return(1); break; case kADD: case kSUB: return(0); break; } } /********/ ResetCalc() /********/ { flagM=flagK=flagINV=flagE=flagPAREN=0; drgmode=DEG; strcpy(dispstr,"0"); ClearStacks(); drg2rad=PI/180.0; rad2drg=180.0/PI; } /*********/ TypeChar(c) char c; /*********/ { /* figure out if person typed a valid calculator key. If so, press the key, wait a bit, and release the key else Feep() */ int i,key; switch (c) { case '0': key=kZERO; break; case '1': key=kONE; break; case '2': key=kTWO; break; case '3': key=kTHREE; break; case '4': key=kFOUR; break; case '5': key=kFIVE; break; case '6': key=kSIX; break; case '7': key=kSEVEN; break; case '8': key=kEIGHT; break; case '9': key=kNINE; break; case '.': key=kDEC; break; case '+': key=kADD; break; case '-': key=kSUB; break; case '*': key=kMUL; break; case '/': key=kDIV; break; case '(': key=kLPAR; break; case ')': key=kRPAR; break; case '!': key=kFACT; break; case 'e': key=kEE; break; case '^': key=kPOW; break; case 'p': key=kPI; break; case 'i': key=kINV; break; case 's': key=kSIN; break; case 'c': key=kCOS; break; case 't': key=kTAN; break; case 'd': key=kDRG; break; case 'l': key=kLN; break; case '=': key=kEQU; break; case 'n': key=kNEG; break; case '\177': case '\010': key=kCLR; break; case '\003': Quit(); break; default: key = -1; } if (key != -1) { PressedKey(key); XFlush(); Timer(100000L); LetgoKey(key); XFlush(); } else XFeep(0); } static int timerdone; /*******/ onalarm() /*******/ { timerdone=1; } /*******/ Timer(val) long val; /*******/ { struct itimerval it; bzero(&it, sizeof(it)); it.it_value.tv_usec = val; timerdone=0; signal(SIGALRM,onalarm); setitimer(ITIMER_REAL, &it, (struct itimerval *)0); while (!timerdone); signal(SIGALRM,SIG_DFL); } #ifndef IEEE /******************/ fperr(sig,code,scp) int sig,code; struct sigcontext *scp; /******************/ { longjmp(env,code); } #endif \Rogue\Monster\ else echo "will not over write ./xcalc.c" fi echo "Finished archive 1 of 1" exit