// 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. // // Phase 4.1 migration: menu mini-format strings, AlertTemplate, and // NewWindowParm folded into iigs/uiBuilder.h. #include "iigs/toolbox.h" #include "iigs/desktop.h" #include "iigs/eventLoop.h" #include "iigs/uiBuilder.h" #define apple_About 257 #define file_Quit 256 #define file_New 258 #define file_Close 255 #define wInGoAway 17 #define wInContent 19 #define mUpMask 0x0002 #define modeCopy 0 #define modeXOR 2 #define maxWindows 4 #define maxLines 50 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; } EventRec; typedef struct { void *wPtr; unsigned char *name; unsigned short numLines; LineRec lines[maxLines]; } WindowRecord; // --- alphabetised forward decls ----------------------------------- static void doClose(void); static void doNew(void); static void drawWindow(void); static void onAbout(uint16_t cmdId); static void onCloseMenu(uint16_t cmdId); static void onMenu(uint16_t menuId, uint16_t itemId); static void onNew(uint16_t cmdId); static void onQuit(uint16_t cmdId); static void sketch(const IigsEventT *ev); static void onNew(uint16_t cmdId) { (void)cmdId; doNew(); } static void onCloseMenu(uint16_t cmdId) { (void)cmdId; doClose(); } static const UiCmdHandlerT gCmdTable[] = { { apple_About, onAbout }, { file_Quit, onQuit }, { file_New, onNew }, { file_Close, onCloseMenu }, }; static const UiMenuItemT gEditItems[] = { { 250, "Undo", 'Z', MI_CHECKED }, { 251, "Cut", 'X', 0 }, { 252, "Copy", 'C', 0 }, { 253, "Paste", 'V', 0 }, { 254, "Clear", 0, 0 }, }; static const UiMenuItemT gFileItems[] = { { 258, "New", 'N', 0 }, { 255, "Close", 0, MI_CHECKED }, { 256, "Quit", 'Q', 0 }, }; static const UiMenuItemT gAppleItems[] = { { 257, "About...", 0, MI_CHECKED }, }; static const UiMenuT gMenus[] = { { 1, "Apple", MN_APPLE, 1, gAppleItems }, { 2, " File ", 0, 3, gFileItems }, { 3, " Edit ", 0, 5, gEditItems }, }; 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} } } } }; // Window-content def-proc. Called by the WM with our bank set up // (Loader sets DBR via JSL). Uses GetWRefCon to identify 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) { unsigned short i = 0; while (i < maxWindows && gWindows[i].wPtr != (void *)0) { i++; } if (i >= maxWindows) { return; } gWindows[i].numLines = 0; // We pass a Pascal title directly via uiBuilderOpenWindow's // contract... but uiBuilder takes a C string. Convert by skipping // the pascal length byte and stuffing into a temporary. char title[16]; unsigned short tn = gWindows[i].name[0]; if (tn > 14) { tn = 14; } for (unsigned short k = 0; k < tn; k++) { title[k] = (char)gWindows[i].name[k + 1]; } title[tn] = '\0'; UiWindowT spec = { title, UW_STD_DOC_GZ, { (int16_t)(25 + i * 10), (int16_t)(10 + i * 10), (int16_t)(180 + i * 10), (int16_t)(600 + i * 10) }, 188, 615, (uint32_t)i, (void *)&drawWindow }; gWindows[i].wPtr = uiBuilderOpenWindow(&spec); 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 onAbout(uint16_t cmdId) { (void)cmdId; uiBuilderAlert(UA_NOTE, "Mini-CAD 1.0\r" "Copyright 1989\r" "Byte Works, Inc.\r\r" "By Mike Westerfield"); } static volatile uint16_t gDone; static void onQuit(uint16_t cmdId) { (void)cmdId; gDone = 1; iigsEventLoopQuit(); } static void onMenu(uint16_t menuId, uint16_t itemId) { (void)menuId; uiBuilderDispatch(itemId, gCmdTable, (uint16_t)(sizeof gCmdTable / sizeof gCmdTable[0])); } static void sketch(const IigsEventT *ev) { void *fw = FrontWindow(); if (!fw) { return; } unsigned short i = (unsigned short)(unsigned long)GetWRefCon(fw); if (i >= maxWindows) { return; } if (gWindows[i].numLines >= maxLines) { uiBuilderAlert(UA_STOP, "The window is full -\r" "more lines cannot be\r" "added."); return; } StartDrawing(fw); SetSolidPenPat(15); SetPenSize(2, 1); SetPenMode(modeXOR); Point firstPt; firstPt.h = ev->whereX; firstPt.v = ev->whereY; GlobalToLocal(&firstPt); MoveTo(firstPt.h, firstPt.v); LineTo(firstPt.h, firstPt.v); Point endPt = firstPt; EventRec evDrag; while (!GetNextEvent(mUpMask, &evDrag)) { Point cur; cur.h = evDrag.wmWhereH; cur.v = evDrag.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); } } int main(void) { unsigned short userId = startdesk(640); (void)userId; paintDesktopBackdrop(); uiBuilderInstallMenuBar(gMenus, (uint16_t)(sizeof gMenus / sizeof gMenus[0])); ShowCursor(); // Open one window so the demo has visible content immediately. doNew(); // Use a direct TaskMaster loop so the watchdog increments on // every iteration regardless of TaskMaster's return code. // iigsEventLoop's onIdle only ticks on EV_NULL which TaskMaster // rarely emits with our task mask. IigsEventT ev; { unsigned char *p = (unsigned char *)&ev; for (uint16_t i = 0; i < sizeof ev; i++) { p[i] = 0; } } ev.taskMask = 0x1FFF; uint16_t watchdog = 0; do { uint16_t code = TaskMaster(0x076E, &ev); switch (code) { case 3: // wInMenuBar case 25: // wInSpecial onMenu(0, (uint16_t)ev.taskData); break; case wInGoAway: doClose(); break; case wInContent: sketch(&ev); break; default: break; } watchdog++; } while (!gDone && watchdog < 4000); *(volatile unsigned char *)0x70 = 0x99; return 0; }