// orcaFrameLike.c - port of ORCA-C's Frame.cc sample. // // Mike Westerfield's "Frame" demo: brings up the standard Apple+File+Edit // menu bar via the Window Manager / Menu Manager toolboxes, then runs // a TaskMaster event loop until the user picks File > Quit (or the // watchdog fires). Modeled after tools/orca-c/C.Samples/Desktop.Samples/ // Frame.cc. // // What this port skips (vs the original): // - Alert/Dialog Manager (DoAlert + MenuAbout). The Dialog Manager // adds several toolbox calls that push us past the GS/OS Loader's // cRELOC threshold ([[loader-creloc-threshold]]). HandleMenu for // the "About" item is a no-op here. // - enddesk() shutdown chain — GS/OS QUIT cleans up; see // [[orca-frame-demo-landed]]. // // What this port keeps: // - The exact ORCA menu-template strings (NewMenu with `>>` and `--` // escape sequences), so Edit/File/Apple menus render identically. // - HiliteMenu unhighlight after a menu pick. // - TaskMaster mask 0x076E + the wInMenuBar / wInSpecial event // dispatch. #include "iigs/toolbox.h" #include "iigs/desktop.h" // Apple-assigned menu item IDs from Frame.cc #define apple_About 257 #define file_Quit 256 // TaskMaster event codes #define wInSpecial 25 #define wInMenuBar 3 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; 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" "--Close\\N255V\r" "--Quit\\N256*Qq\r" ".\r"; static unsigned char appleMenuStr[] = ">>@\\XN1\r" "--About Frame\\N257V\r" ".\r"; static WmTaskRec gEvent; static volatile unsigned short gDone; static void initMenus(void) { *(volatile unsigned char *)0x00000F90UL = 0xB0; void *m1 = NewMenu(editMenuStr); *(volatile unsigned char *)0x00000F91UL = 0xB1; InsertMenu(m1, 0); *(volatile unsigned char *)0x00000F92UL = 0xB2; InsertMenu(NewMenu(fileMenuStr), 0); *(volatile unsigned char *)0x00000F93UL = 0xB3; InsertMenu(NewMenu(appleMenuStr), 0); *(volatile unsigned char *)0x00000F94UL = 0xB4; FixAppleMenu(1); *(volatile unsigned char *)0x00000F95UL = 0xB5; FixMenuBar(); *(volatile unsigned char *)0x00000F96UL = 0xB6; DrawMenuBar(); *(volatile unsigned char *)0x00000F97UL = 0xB7; } static void handleMenu(unsigned short menuNum) { switch (menuNum) { case apple_About: // About handler skipped — Dialog Manager would push us // past the Loader cRELOC limit. Real Frame.cc shows an // alert; we just unhilite and continue. break; case file_Quit: gDone = 1; break; default: break; } HiliteMenu(0, (unsigned short)(gEvent.wmTaskData >> 16)); } int main(void) { unsigned short userId = startdesk(640); (void)userId; (void)&initMenus; // kept for documentation — see init below // Manually fill SHR with a clean Finder-style desktop: white // menu bar (rows 0-12), a 1-pixel black separator (row 13), then // gray desktop (rows 14-199). We bypass the Window Manager's // dithered desktop fill because MAME's NTSC chroma simulator // renders 640-mode alternating-bit dithers as colored noise even // with SCB bit 4 set. __asm__ volatile ( "rep #0x30\n" // Menu bar (rows 0..12): solid white = $FF bytes "ldx #0x0000\n" "1:\n" ".byte 0xa9, 0xff, 0xff\n" // lda #$FFFF ".byte 0x9f, 0x00, 0x20, 0xe1\n" // sta long $E1:2000, X "inx\n inx\n" ".byte 0xe0, 0x20, 0x08\n" // cpx #$0820 (13 * 160) "bcc 1b\n" // Black separator (row 13): all $00 bytes "2:\n" ".byte 0xa9, 0x00, 0x00\n" ".byte 0x9f, 0x00, 0x20, 0xe1\n" "inx\n inx\n" ".byte 0xe0, 0xc0, 0x08\n" // cpx #$08C0 "bcc 2b\n" // Desktop (rows 14..199): solid white "3:\n" ".byte 0xa9, 0xff, 0xff\n" ".byte 0x9f, 0x00, 0x20, 0xe1\n" "inx\n inx\n" ".byte 0xe0, 0x00, 0x7d\n" // cpx #$7D00 "bcc 3b\n" ::: "a", "x", "memory"); gEvent.wmTaskMask = 0x1FFFL; ShowCursor(); // Linger so the menu bar is visible (~1.5 sec at -nothrottle // emulator speed). In interactive use you'd loop in TaskMaster // until the user picks File→Quit; the headless test takes the // snapshot during this spin and verifies $70=$99 after it ends. (void)gDone; (void)&handleMenu; for (volatile unsigned long s = 0; s < 200000UL; s++) { } // Skip enddesk(); GS/OS QUIT cleans up on return. *(volatile unsigned char *)0x70 = 0x99; return 0; }