65816-llvm-mos/demos/minicad.c
Scott Duensing d95c30e819 Update.
2026-05-20 20:14:20 -05:00

376 lines
10 KiB
C

// minicad.c - faithful port of ORCA-C's MiniCAD.cc sample.
//
// Mike Westerfield, Byte Works 1989. Original at
// tools/orca-c/C.Samples/Desktop.Samples/MiniCAD.cc.
//
// A simple multi-window CAD: File>New opens a drawing window (up to
// 4), click+drag inside a window's content rubber-bands a line,
// release commits it. File>Close closes the front window. Each
// window's lines are remembered so the WM can repaint on update.
#include "iigs/toolbox.h"
#include "iigs/desktop.h"
#define apple_About 257
#define file_Quit 256
#define file_New 258
#define file_Close 255
#define wInMenuBar 3
#define wInSpecial 25
#define wInGoAway 17
#define wInContent 19
#define mUpMask 0x0002
#define modeCopy 0
#define modeXOR 2
#define topMost ((void *)-1L)
#define bottomMost ((void *)0)
#define maxWindows 4
#define maxLines 50
#define norml 0
#define stop 1
#define note 2
#define caution 3
#define buttonItem 10
#define statText 136
#define itemDisable 0x8000
typedef struct { short v1, h1, v2, h2; } Rect;
typedef struct { short v, h; } Point;
typedef struct { Point p1, p2; } LineRec;
typedef struct {
unsigned short wmWhat;
unsigned long wmMessage;
unsigned long wmWhen;
short wmWhereV, wmWhereH;
unsigned short wmModifiers;
unsigned long wmTaskData;
unsigned long wmTaskMask;
unsigned long wmLastClickTick;
unsigned long wmClickCount;
unsigned long wmTaskData2;
unsigned long wmTaskData3;
unsigned long wmTaskData4;
} WmTaskRec;
typedef struct {
unsigned short wmWhat;
unsigned long wmMessage;
unsigned long wmWhen;
short wmWhereV, wmWhereH;
unsigned short wmModifiers;
} EventRec;
typedef struct {
unsigned short paramLength;
unsigned short wFrameBits;
void *wTitle;
unsigned long wRefCon;
Rect wZoom;
void *wColor;
short wYOrigin, wXOrigin;
short wDataH, wDataV;
short wMaxHeight, wMaxWidth;
short wScrollVer, wScrollHor;
short wPageVer, wPageHor;
unsigned long wInfoRefCon;
short wInfoHeight;
void *wFrameDefProc;
void *wInfoDefProc;
void *wContDefProc;
Rect wPosition;
void *wPlane;
void *wStorage;
} NewWindowParm;
typedef struct {
short itemID;
short itemRectV1, itemRectH1, itemRectV2, itemRectH2;
unsigned short itemType;
void *itemDescr;
short itemValue;
short itemFlag;
void *itemColor;
} ItemTemplate;
typedef struct {
short atRectV1, atRectH1, atRectV2, atRectH2;
short atBtnHorz;
short atBeep0, atBeep1, atBeep2, atBeep3;
void *atSound;
void *atResv1;
void *atResv2;
void *atItemList[8];
} AlertTemplate;
typedef struct {
void *wPtr;
unsigned char *name;
unsigned short numLines;
LineRec lines[maxLines];
} WindowRecord;
static unsigned char editMenuStr[] = ">> Edit \\N3\r"
"--Undo\\N250V*Zz\r"
"--Cut\\N251*Xx\r"
"--Copy\\N252*Cc\r"
"--Paste\\N253*Vv\r"
"--Clear\\N254\r"
".\r";
static unsigned char fileMenuStr[] = ">> File \\N2\r"
"--New\\N258*Nn\r"
"--Close\\N255V\r"
"--Quit\\N256*Qq\r"
".\r";
static unsigned char appleMenuStr[] = ">>@\\XN1\r"
"--About...\\N257V\r"
".\r";
static unsigned char gAboutMsg[] =
"\x3d" "Mini-CAD 1.0\r"
"Copyright 1989\r"
"Byte Works, Inc.\r\r"
"By Mike Westerfield";
static unsigned char gTitle0[] = "\x07Paint 1";
static unsigned char gTitle1[] = "\x07Paint 2";
static unsigned char gTitle2[] = "\x07Paint 3";
static unsigned char gTitle3[] = "\x07Paint 4";
static WindowRecord gWindows[maxWindows] = {
{ (void *)0, gTitle0, 0, { { {0,0}, {0,0} } } },
{ (void *)0, gTitle1, 0, { { {0,0}, {0,0} } } },
{ (void *)0, gTitle2, 0, { { {0,0}, {0,0} } } },
{ (void *)0, gTitle3, 0, { { {0,0}, {0,0} } } }
};
static WmTaskRec gEvent;
static volatile unsigned short gDone;
static void doAlert(unsigned short kind, void *msg) {
static unsigned char okStr[] = "\x02OK";
static ItemTemplate button = {
1, 36, 15, 0, 0, buttonItem, okStr, 0, 0, (void *)0
};
static ItemTemplate message = {
100, 5, 100, 90, 280, itemDisable | statText, (void *)0, 0, 0, (void *)0
};
static AlertTemplate alertRec = {
50, 180, 107, 460, 2, 0x80, 0x80, 0x80, 0x80,
(void *)0, (void *)0, (void *)0,
{ (void *)0, (void *)0, (void *)0, (void *)0,
(void *)0, (void *)0, (void *)0, (void *)0 }
};
SetForeColor(0);
SetBackColor(15);
message.itemDescr = msg;
alertRec.atItemList[0] = (void *)&button;
alertRec.atItemList[1] = (void *)&message;
alertRec.atItemList[2] = (void *)0;
switch (kind) {
case norml: (void)Alert(&alertRec, (void *)0); break;
case stop: (void)StopAlert(&alertRec, (void *)0); break;
case note: (void)NoteAlert(&alertRec, (void *)0); break;
case caution: (void)CautionAlert(&alertRec, (void *)0); break;
default: break;
}
}
// Window-content def-proc. The WM calls this with DBR set to our
// bank (Loader sets up the JSL chain). We use GetWRefCon on the
// current port to know which gWindows[] entry to redraw.
static void drawWindow(void) {
unsigned long refcon = (unsigned long)GetWRefCon(GetPort());
unsigned short i = (unsigned short)refcon;
if (i >= maxWindows) return;
WindowRecord *wp = &gWindows[i];
if (wp->numLines == 0) return;
SetPenMode(modeCopy);
SetSolidPenPat(0);
SetPenSize(2, 1);
for (unsigned short j = 0; j < wp->numLines; j++) {
LineRec *lp = &wp->lines[j];
MoveTo(lp->p1.h, lp->p1.v);
LineTo(lp->p2.h, lp->p2.v);
}
}
static void doNew(void) {
static NewWindowParm wp;
unsigned short i = 0;
while (i < maxWindows && gWindows[i].wPtr != (void *)0) i++;
if (i >= maxWindows) return;
gWindows[i].numLines = 0;
unsigned char *p = (unsigned char *)&wp;
for (unsigned short k = 0; k < sizeof wp; k++) p[k] = 0;
wp.paramLength = (unsigned short)sizeof wp;
wp.wFrameBits = 0x4007 | 0x0020 | 0x0080 | 0x0400 | 0x4000; // fTitle+fClose+fVis+fMove+fGrow
wp.wTitle = gWindows[i].name;
wp.wRefCon = (unsigned long)i;
wp.wMaxHeight = 188;
wp.wMaxWidth = 615;
wp.wPosition.v1 = (short)(25 + i * 10);
wp.wPosition.h1 = (short)(10 + i * 10);
wp.wPosition.v2 = (short)(180 + i * 10);
wp.wPosition.h2 = (short)(600 + i * 10);
wp.wContDefProc = (void *)&drawWindow;
wp.wPlane = topMost;
gWindows[i].wPtr = NewWindow(&wp);
if (i == maxWindows - 1) {
DisableMItem(file_New);
}
}
static void doClose(void) {
void *fw = FrontWindow();
if (!fw) return;
unsigned short i = (unsigned short)(unsigned long)GetWRefCon(fw);
if (i >= maxWindows) return;
CloseWindow(gWindows[i].wPtr);
gWindows[i].wPtr = (void *)0;
EnableMItem(file_New);
}
static void menuAbout(void) {
doAlert(note, gAboutMsg);
}
static void sketch(void) {
void *fw = FrontWindow();
if (!fw) return;
unsigned short i = (unsigned short)(unsigned long)GetWRefCon(fw);
if (i >= maxWindows) return;
if (gWindows[i].numLines >= maxLines) {
static unsigned char fullMsg[] =
"\x3a" "The window is full -\r"
"more lines cannot be\r"
"added.";
doAlert(stop, fullMsg);
return;
}
StartDrawing(fw);
SetSolidPenPat(15);
SetPenSize(2, 1);
SetPenMode(modeXOR);
Point firstPt;
firstPt.h = gEvent.wmWhereH;
firstPt.v = gEvent.wmWhereV;
GlobalToLocal(&firstPt);
MoveTo(firstPt.h, firstPt.v);
LineTo(firstPt.h, firstPt.v);
Point endPt = firstPt;
EventRec ev;
while (!GetNextEvent(mUpMask, &ev)) {
Point cur;
cur.h = ev.wmWhereH;
cur.v = ev.wmWhereV;
GlobalToLocal(&cur);
if (cur.h != endPt.h || cur.v != endPt.v) {
MoveTo(firstPt.h, firstPt.v);
LineTo(endPt.h, endPt.v);
MoveTo(firstPt.h, firstPt.v);
LineTo(cur.h, cur.v);
endPt = cur;
}
}
// Erase final XOR line.
MoveTo(firstPt.h, firstPt.v);
LineTo(endPt.h, endPt.v);
if (firstPt.h != endPt.h || firstPt.v != endPt.v) {
unsigned short n = gWindows[i].numLines++;
gWindows[i].lines[n].p1 = firstPt;
gWindows[i].lines[n].p2 = endPt;
SetPenMode(modeCopy);
SetSolidPenPat(0);
MoveTo(firstPt.h, firstPt.v);
LineTo(endPt.h, endPt.v);
}
}
static void handleMenu(unsigned short menuNum) {
switch (menuNum) {
case apple_About: menuAbout(); break;
case file_Quit: gDone = 1; break;
case file_New: doNew(); break;
case file_Close: doClose(); break;
default: break;
}
HiliteMenu(0, (unsigned short)(gEvent.wmTaskData >> 16));
}
static void initMenus(void) {
InsertMenu(NewMenu(editMenuStr), 0);
InsertMenu(NewMenu(fileMenuStr), 0);
InsertMenu(NewMenu(appleMenuStr), 0);
FixAppleMenu(1);
FixMenuBar();
DrawMenuBar();
}
int main(void) {
unsigned short userId = startdesk(640);
(void)userId;
paintDesktopBackdrop();
initMenus();
gEvent.wmTaskMask = 0x1FFFL;
ShowCursor();
// Open one window so the demo has visible content immediately.
doNew();
gDone = 0;
unsigned short watchdog = 0;
do {
unsigned short event = TaskMaster(0x076E, &gEvent);
switch (event) {
case wInSpecial:
case wInMenuBar:
handleMenu((unsigned short)gEvent.wmTaskData);
break;
case wInGoAway:
doClose();
break;
case wInContent:
sketch();
break;
default:
break;
}
watchdog++;
} while (!gDone && watchdog < 4000);
*(volatile unsigned char *)0x70 = 0x99;
return 0;
}