This is winNeXT.m in view mode; [Download] [Up]
/* winNeXT.m - NextStep Window-system specific routines * * Eric P. Scott, San Francisco State University, January 1990 * (portions adapted from Bob Brown's Sunview implementation) * * If this file doesn't look like normal C, that's because it's not. * It's Objective-C, and perhaps not the greatest example of it. * (But it demonstrates what things would be like without Interface * builder.) * * Some things might have to be changed if NeXT fixes a few bugs... * * * Copyright 1990 by Eric P. Scott. All Rights Reserved. * Material herein may be used, copied, modified, or distributed * for any purpose as long as proper authorship credit is given. * This software is provided as is, without warranty, and with * no claims of fitness for any particular purpose. Use at your * own risk. In no event shall the author, San Francisco State * University, the Trustees of the California State University, * or the State of California be held liable for any damages or * losses resulting from use of this software or any derivative * works. * * NeXT Mazewar was developed on my own time using facilities * provided by the San Francisco State University Computer * Science Department. Thanks to Stan Osborne as faculty * advisor, J. Keith Wood for sharing his NeXT programming * experience, Dr. Sergio Aragon for the use of a SPARCstation 1, * MazeWar author Christoper A. Kent for his support and * encouragement, and all the beta testers. * * You will get two inconsequential warnings when you compile this. * */ #import <appkit/Application.h> #import <appkit/Button.h> #import <appkit/ButtonCell.h> #import <appkit/Font.h> #import <appkit/Form.h> #import <appkit/Menu.h> #import <appkit/MenuCell.h> #import <appkit/NXCursor.h> #import <appkit/NXImage.h> #import <appkit/obsoleteBitmap.h> #import <appkit/Panel.h> #import <appkit/ScrollView.h> #import <appkit/Text.h> #import <appkit/TextField.h> #import <appkit/TextFieldCell.h> #import <appkit/View.h> #import <appkit/Window.h> #import <defaults/defaults.h> #import <appkit/graphics.h> #import <appkit/tiff.h> #import <dpsclient/psops.h> #import <dpsclient/wraps.h> #import <streams/streams.h> #include <errno.h> #include <strings.h> #include <sys/types.h> #include <sys/time.h> #include <sys/socket.h> #include <netinet/in.h> #include "mazewar.h" #include "winNeXT.h" #define VIEW_X_DIM 400 #define VIEW_Y_DIM 400 #define VIEW_X_ORIGIN 100 #define VIEW_Y_ORIGIN 50 #define MAZE_X_DIM (MAZEXMAX*16) #define MAZE_Y_DIM (MAZEYMAX*16) #define MAZE_X_ORIGIN 48 #define MAZE_Y_ORIGIN 451 #define SCORE_X_DIM 192 #define SCORE_Y_DIM 96 #define SCORE_X_ORIGIN 208 #define SCORE_Y_ORIGIN 708 #define ICON_FLASH_PERIOD 4 @interface MazeApp:Application { id currentCursor; id okButton; } + new; - setCurrentCursor:anObject; - setOkButton:anObject; - gotName:sender; - gotDuke:sender; - gotButton:sender; - userQuit:sender; - windowDidBecomeKey:sender; - appDidInit:sender; - applicationDefined:(NXEvent *)theEvent; @end @interface MazeWin:Window { } - keyDown:(NXEvent *)theEvent; - mouseDown:(NXEvent *)theEvent; - windowDidBecomeKey:sender; @end @interface MouseView:View { } + new; - (BOOL)acceptsFirstMouse; - rightMouseDown:(NXEvent *)theEvent; - rightMouseUp:(NXEvent *)theEvent; - windowDidBecomeKey:sender; @end @interface HelpPanelClass:Panel { NXSize maxSize; } - constrain; - initialHelp; - windowWillResize:sender toSize:(NXSize *)frameSize; - windowDidBecomeKey:sender; @end static RatPacket incoming; static MWEvent event; static id InfoPanel, editMenu, editSubmenu, cutMenuCell, copyMenuCell, pasteMenuCell, deleteMenuCell, selectAllMenuCell, HelpPanel; mazerun() { // make menus and windows, run event loop const NXRect infoRect = { { 285.0, 528.0 }, { 392.0, 130.0 } }, helpRect = { { 206.0, 368.0 }, { 512.0, 440.0 } }, appRect = { { 22.0, 50.0 }, { 64.0, 64.0 } }, titleRect = { { 120.0, 56.0}, { 211.0, 60.0 } }, title2Rect = { { 118.0, 57.0}, { 211.0, 60.0 } }, authorRect = { { 119.0, 22.0}, { 176.0, 45.0 } }, author2Rect = { { 5.0, 5.0}, { 387.0, 15.0 } }, versRect = { { 18.0, 25.0}, { 69.0, 30.0 } }; const NXSize maxSize = { 16000.0, 1.0e38 }; id mwMenu, v, f, t; NXStream *s; static NXRect docRect; event.eventDetail = &incoming; NXApp = [MazeApp new]; mwMenu = [Menu newTitle:"MazeWar"]; InfoPanel = [Panel newContent:&infoRect style:NX_TITLEDSTYLE backing:NX_BUFFERED buttonMask:NX_CLOSEBUTTONMASK defer:YES]; [[mwMenu addItem:"Info..." action:@selector(makeKeyAndOrderFront:) keyEquivalent:'\0'] setTarget:InfoPanel]; editMenu = [mwMenu addItem:"Edit" action:0 keyEquivalent:'\0']; editSubmenu = [Menu newTitle:"Edit"]; cutMenuCell = [editSubmenu addItem:"Cut" action:@selector(cut:) keyEquivalent:'x']; copyMenuCell = [editSubmenu addItem:"Copy" action:@selector(copy:) keyEquivalent:'c']; pasteMenuCell = [editSubmenu addItem:"Paste" action:@selector(paste:) keyEquivalent:'v']; deleteMenuCell = [editSubmenu addItem:"Delete" action:@selector(delete:) keyEquivalent:'\0']; selectAllMenuCell = [editSubmenu addItem:"Select All" action:@selector(selectAll:) keyEquivalent:'a']; [editSubmenu sizeToFit]; [mwMenu setSubmenu:editSubmenu forItem:editMenu]; HelpPanel = [HelpPanelClass newContent:&helpRect style:NX_SIZEBARSTYLE backing:NX_BUFFERED buttonMask:NX_CLOSEBUTTONMASK defer:YES]; [[mwMenu addItem:"Help..." action:@selector(makeKeyAndOrderFront:) keyEquivalent:'?'] setTarget:HelpPanel]; [[mwMenu addItem:"Hide" action:@selector(hide:) keyEquivalent:'h'] setTarget:NXApp]; [[mwMenu addItem:"Quit" action:@selector(userQuit:) keyEquivalent:'q'] setTarget:NXApp]; [mwMenu sizeToFit]; [NXApp setMainMenu:mwMenu]; [InfoPanel setTitle:"Info" ]; v = [InfoPanel contentView]; [v addSubview:[[Button newFrame:&appRect icon:"app" tag:0 target:nil action:0 key:'\0' enabled:NO] setBordered:NO]]; t = [[[[TextField newFrame:&titleRect] setSelectable:NO] setBezeled:NO] setBackgroundGray:NX_LTGRAY]; f = [Font newFont:"Times-Roman" size:48.0]; [[[t cell] setStringValueNoCopy:"MazeWar"] setFont:f]; [v addSubview:t]; t = [[[[[TextField newFrame:&title2Rect] setSelectable:NO] setBezeled:NO] setTextGray:NX_WHITE] setBackgroundGray:-1.0]; [[[t cell] setStringValueNoCopy:"MazeWar"] setFont:f]; [v addSubview:t]; // why doesn't [[t copy] ...] work? t = [[[[TextField newFrame:&authorRect] setSelectable:NO] setBezeled:NO] setBackgroundGray:NX_LTGRAY]; f = [Font newFont:"Helvetica" size:12.0]; [[[t cell] setStringValueNoCopy:"by Christopher A. Kent\ \nWestern Research Laboratory\nDigital Equipment Corporation"] setFont:f]; [v addSubview:t]; t = [[[[[TextField newFrame:&versRect] setSelectable:NO] setBezeled:NO] setTextGray:NX_DKGRAY] setBackgroundGray:NX_LTGRAY]; [[[[t cell] setAlignment:NX_CENTERED] setStringValueNoCopy: "1/27/1990\nrelease"] setFont:f]; [v addSubview:t]; t = [[[[[TextField newFrame:&author2Rect] setSelectable:NO] setBezeled:NO] setTextGray:NX_WHITE] setBackgroundGray:NX_LTGRAY]; f = [Font newFont:"Helvetica-Bold" size:12.0]; [[[t cell] setStringValueNoCopy:"NeXT Interface by Eric P. Scott, \ San Francisco State University"] setFont:f]; [v addSubview:t]; v = [ScrollView newFrame:&helpRect]; [v setBackgroundGray:NX_WHITE]; [[v setVertScrollerRequired:YES] getContentSize:&docRect.size]; t = [Text newFrame:&docRect]; [v setDocView:t]; [[[t setAutosizing:NX_WIDTHSIZABLE] superview] setAutoresizeSubviews:YES]; [[[[[[[t setOpaque:YES] setMonoFont:NO] notifyAncestorWhenFrameChanged:YES] setVertResizable:YES] setHorizResizable:NO] setMarginLeft:6.0 right:6.0 top:0.0 bottom:0.0] setMaxSize:&maxSize]; s = NXOpenMemory(helpText, sizeof helpText-1, NX_READONLY); [t readRichText:s]; NXClose(s); // do I have to vm_deallocate() anything? [t setEditable:NO]; [[HelpPanel setTitle:"Help"] setContentView:v]; [HelpPanel constrain]; [t setSel:0:0]; [NXApp run]; [NXApp free]; exit(0); } static id mwWindow; InitWindow(int argc, char *argv[]) // make MazeWar window { const NXRect mwRect = { { 262.0, 2.0}, { 608.0, 808.0 } }, // Alto sized! mvRect = { { VIEW_X_ORIGIN, VIEW_Y_ORIGIN}, { VIEW_X_DIM, VIEW_Y_DIM } }; id mView; mwWindow = [MazeWin newContent:&mwRect style:NX_TITLEDSTYLE backing:NX_BUFFERED buttonMask:0 defer:YES]; [mwWindow setTitle:"MazeWar" ]; [mwWindow setBackgroundGray:NX_WHITE]; mView = [MouseView newFrame:&mvRect]; [InfoPanel setDelegate:mView]; [[[[mwWindow contentView] setOpaque:YES] setFlipped:YES] addSubview:mView]; [mwWindow setDelegate:mwWindow]; [mwWindow makeKeyAndOrderFront:nil]; } static id scoreFont; #ifdef NOTDEF StartWindow(short *ratBits, int ratWidth, int ratHeight) #else StartWindow() // initialize everything properly #endif { extern void initCursors(), HourGlassCursor(), initMaze(), invertRats(), initRats(), drawMaze(); extern ShowPosition(), ShowView(), ShowAllPositions(), NewScoreCard(); initCursors(); HourGlassCursor(); scoreFont = [Font newFont:"Times-Roman" size:10.0]; // fits in 12 rows [[mwWindow contentView] lockFocus]; // this is about as dumb as X-Windows [scoreFont set]; initMaze(); #if __LITTLE_ENDIAN__ SwapBitmaps(); #endif #ifdef NOTDEF initRats(ratBits, ratWidth, ratHeight); #else initRats(); #endif drawMaze(); ShowPosition(M.xloc, M.yloc, M.invincible, M.dir); PSsetlinewidth(1.0); // ...and it still looks thick ShowView(M.xloc, M.yloc, M.dir); ShowAllPositions(); NewScoreCard(); [mwWindow setTitle:"MazeWar"]; // undo advise() } StopWindow() // shut down the window system { id v; v = [mwWindow contentView]; if ([v isFocusView]) [v unlockFocus]; // don't ask [NXApp terminate:nil]; // no point continuing } static int dirty; ClearView() // clear out perspective view { const NXRect canvas={ { VIEW_X_ORIGIN, VIEW_Y_ORIGIN}, { VIEW_X_DIM+1, VIEW_Y_DIM+1 } }; NXEraseRect(&canvas); PSsetgray(NX_BLACK); dirty++; } FlashTop() // hit an opponent { PSsetinstance(TRUE); PSsetalpha(1.0/6.0); PScompositerect((float)VIEW_X_ORIGIN, (float)VIEW_Y_ORIGIN, (float)VIEW_X_DIM, (float)VIEW_Y_DIM, NX_SOVER); NXPing(); PSsetalpha(1.0); PSnewinstance(); PSsetinstance(FALSE); } FlashScreen() // we were hit { PSsetinstance(TRUE); PSsetalpha(1.0/6.0); PScompositerect(0.0, 0.0, 607.0, 807.0, NX_SOVER); NXPing(); PSsetalpha(1.0); PSnewinstance(); PSsetinstance(FALSE); } DrawViewLine(int x1, int y1, int x2, int y2) { PSmoveto((float)x1+VIEW_X_ORIGIN, (float)y1+VIEW_Y_ORIGIN); PSlineto((float)x2+VIEW_X_ORIGIN, (float)y2+VIEW_Y_ORIGIN); PSstroke(); dirty++; } Boolean KBEventPending() // defer SendLocation() if unprocessed keystrokes { NXEvent ev; // Before I put in the check for [NXApp isActive], this routine was // the source of one of the strangest bugs I've seen. // If the application was launched from the Workspace, and you hid // it, double clicking on its icon would have no apparent effect; // after doing this a few times, Workspace would hang, and after // a while would put up an attention panel saying that MazeWar was // not responding to being unhidden. However, if another player // came along and killed you, THEN the application would restore. // Getting killed called NXPing(), which somehow unfroze things. // If the application was started from a terminal, everything // worked fine! I'm at a loss to explain why this should make any // difference. return([NXApp isActive] && [NXApp peekNextEvent:NX_KEYDOWN into:&ev]!=(NXEvent *)NULL); } static id hourGlass, rat, deadRat; void HourGlassCursor() // but it's not really { if ([mwWindow isKeyWindow]) { [hourGlass set]; PSrevealcursor(); } [NXApp setCurrentCursor:hourGlass]; } RatCursor() { if ([mwWindow isKeyWindow]) [rat set]; [NXApp setCurrentCursor:rat]; } DeadRatCursor() { if ([mwWindow isKeyWindow]) [deadRat set]; [NXApp setCurrentCursor:deadRat]; } HackMazeBitmap(int x, int y, BitCell *newBits) // paint an arrow { static NXRect where={ { 0.0, 0.0 }, { 16.0, 16.0 } }; const unsigned char *my_data[5] = { (unsigned char *)newBits, (unsigned char *)NULL, (unsigned char *)NULL, (unsigned char *)NULL, (unsigned char *)NULL}; where.origin.x=(float)(x*16+MAZE_X_ORIGIN); where.origin.y=(float)(y*16+MAZE_Y_ORIGIN); NXDrawBitmap(&where, 16, 16, 1, 1, 1, 2, YES, NO, 0, my_data); dirty++; } static id ratBitmap; DisplayRatBitmap(int screenX, int screenY, int width, int height, int srcX, int srcY) // draw an opponent { NXRect src; NXPoint dst; src.origin.x = srcX; src.origin.y = srcY; src.size.width = width; src.size.height = height; dst.x = (float)(VIEW_X_ORIGIN+screenX); dst.y = (float)(VIEW_Y_ORIGIN+screenY); [ratBitmap composite:NX_SOVER fromRect:&src toPoint:&dst]; dirty++; } WriteScoreString(RatId rat) // show score for one player { char buf[64]; PSmoveto((float)(SCORE_X_ORIGIN+1), (float)(SCORE_Y_ORIGIN+12+rat*12)); PSshow(M.ratcb.rats[rat].name); sprintf(buf, "%d", (unsigned int)M.ratcb.rats[rat].score); PSmoveto((float)(SCORE_X_ORIGIN+SCORE_X_DIM-[scoreFont getWidthOf:buf]), (float)(SCORE_Y_ORIGIN+12+rat*12)); PSshow(buf); dirty++; } ClearScoreLine(RatId rat) // clear score line { NXRect eRect; eRect.origin.x = SCORE_X_ORIGIN; eRect.origin.y = (float)(SCORE_Y_ORIGIN+3+rat*12); eRect.size.width = SCORE_X_DIM; eRect.size.height = 12.0; NXEraseRect(&eRect); dirty++; } WriteInvertedScoreString(RatId rat) // clear+show+invert { char buf[64]; PSsetgray(NX_DKGRAY); PSrectfill((float)SCORE_X_ORIGIN, (float)(SCORE_Y_ORIGIN+3+rat*12), (float)SCORE_X_DIM, 12.0); PSsetgray(NX_WHITE); PSmoveto((float)(SCORE_X_ORIGIN+1), (float)(SCORE_Y_ORIGIN+12+rat*12)); PSshow(M.ratcb.rats[rat].name); sprintf(buf, "%d", (unsigned int)M.ratcb.rats[rat].score); PSmoveto((float)(SCORE_X_ORIGIN+SCORE_X_DIM-[scoreFont getWidthOf:buf]), (float)(SCORE_Y_ORIGIN+12+rat*12)); PSshow(buf); PSsetgray(NX_BLACK); dirty++; } void initCursors() { const NXPoint ratHot={ 0.0, 6.0 }, deadRatHot={ 0.0, 9.0 }, hourGlassHot={ 1.0, 12.0 }; rat = [[NXCursor alloc] initFromImage:[NXImage findImageNamed:"rat.tiff"]]; [rat setHotSpot:&ratHot]; deadRat = [[NXCursor alloc] initFromImage:[NXImage findImageNamed:"dRat.tiff"]]; [deadRat setHotSpot:&deadRatHot]; hourGlass = [[NXCursor alloc] initFromImage:[NXImage findImageNamed:"cup.tiff"]]; [hourGlass setHotSpot:&hourGlassHot]; } static id mazeBitmap; void initMaze() // construct maze Bitmap. Sorry, I prefer LTGRAY to BLACK. { register int i, j, k, line, index; long mazeBits[MAZEXMAX*MAZEYMAX*16]; for (i = 0; i < MAZEYMAX; i++) { line = i * MAZEXMAX * MAZEYMAX; for (j = 0; j < MAZEXMAX; j++) { index = line + j; for (k = 0; k < 16; k++) { if (!M.maze[j].y[i]) mazeBits[index] = 037777777777; // NX_WHITE else mazeBits[index] = 025252525252; // NX_LTGRAY index += 32; } } } mazeBitmap = [Bitmap newSize:MAZE_X_DIM:MAZE_Y_DIM type:NX_UNIQUEBITMAP]; [mazeBitmap image:mazeBits width:MAZE_X_DIM height:MAZE_Y_DIM bps:2 spp:1]; } void drawMaze() // BLiT! { const NXPoint mazeLoc = { MAZE_X_ORIGIN, MAZE_Y_ORIGIN }; [mazeBitmap composite:NX_COPY toPoint:&mazeLoc]; dirty++; } #ifdef NOTDEF void initRats(short *bits, int width, int height) #else void initRats() // now with alpha for SOVER compositing #endif { #ifdef NOTDEF ratBitmap = [Bitmap newSize:width:height type:NX_UNIQUEBITMAP]; [ratBitmap image:bits width:width height:height bps:1 spp:1]; #else ratBitmap = [Bitmap newFromMachO:"rats.tiff"]; #endif } static BOOL flashIcon; NotifyPlayer() { flashIcon = TRUE; } // Exceedingly NeXT-specific routines follow flushIt() { PSflushgraphics(); dirty = 0; } static id namePanel, nameForm; void getPlayerName(char *buf) // char buf[128] { // we don't have a terminal to run on! const NXRect nwRect = { { 361.0, 401.0}, { 360.0, 161.0 } }, nfRect = { { 0.0, 69.0 }, { 356.0, 45.0 } }, okRect = { { 280.0, 8.0 }, { 72.0, 24.0 } }, quitRect = { { 200.0, 8.0 }, { 72.0, 24.0 } }, helpRect = { { 120.0, 8.0 }, { 72.0, 24.0 } }, appRect = { { 8.0, 104.0 }, { 48.0, 48.0 } }; id v, okButton, quitButton, helpButton, appButton; namePanel = [Panel newContent:&nwRect style:NX_TITLEDSTYLE backing:NX_BUFFERED buttonMask:0 defer:YES]; [[namePanel setTitle:"Welcome to Alto MazeWar!" ] setDelegate:NXApp]; nameForm = [Form newFrame:&nfRect]; [nameForm addEntry:"Your Name:" tag:0 target:NXApp action:@selector(gotName:)]; [nameForm addEntry:"Duke Host (Return for any game):" tag:1 target:NXApp action:@selector(gotDuke:)]; v = [namePanel contentView]; [v addSubview:nameForm]; okButton = [Button newFrame:&okRect title:"OK" tag:NX_OKTAG target:NXApp action:@selector(gotButton:) key:'\r' enabled:YES]; [[okButton setIcon:"returnSign" position:NX_ICONRIGHT] setAltIcon:"returnSignH"]; [v addSubview:okButton]; [NXApp setOkButton:okButton]; quitButton = [Button newFrame:&quitRect title:"Quit" tag:NX_CANCELTAG target:NXApp action:@selector(gotButton:) key:0 enabled:YES]; [v addSubview:quitButton]; helpButton = [Button newFrame:&helpRect title:"Help" tag:-1 target:NXApp action:@selector(gotButton:) key:0 enabled:YES]; [[helpButton setType:NX_TOGGLE] setAltTitle:"Hide Help"]; [v addSubview:helpButton]; appButton = [Button newFrame:&appRect]; [[[[appButton setBordered:NO] setIcon:"app"] cell] setEnabled:NO]; [v addSubview:appButton]; [nameForm selectTextAt:0]; for (;;) { switch ([NXApp runModalFor:namePanel]) { case NX_CANCELTAG: StopWindow(); // doesn't return in this version /*FALL THROUGH*/ case NX_OKTAG: break; default: [HelpPanel initialHelp]; // [[helpButton cell] setEnabled:NO]; continue; } break; } (void)strncpy(buf, [nameForm stringValueAt:0], 127); // no gets()!!! } void getDukeName(char *ratName, char *buf) { static int invokations; char sorry[144]; switch (invokations) { // what a crock case 1: [nameForm setStringValue:ratName at:0]; [[nameForm cellAt:0:0] setEnabled:NO]; /*FALL THROUGH*/ default: (void)strcat(strcpy(sorry, "Don't know host "), [nameForm stringValueAt:1]); [namePanel setTitle:sorry]; [nameForm setStringValue:"" at:1]; // sigh [nameForm selectTextAt:1]; for (;;) { switch ([NXApp runModalFor:namePanel]) { case NX_CANCELTAG: StopWindow(); /*FALL THROUGH*/ case NX_OKTAG: break; default: [HelpPanel initialHelp]; continue; } break; } /*FALL THROUGH*/ case 0: (void)strncpy(buf, [nameForm stringValueAt:1], 127); break; } invokations++; } void endGetName() // more bogosity { [namePanel close]; NXPing(); } void advise(char *s) // we're doing something that will take a while { [mwWindow setTitle:s]; DPSFlush(); } static int netOK; /* -1 = idle 0 = defer 1 = active 2 = pending */ void beginNetwork() // nothing like healthy paranoia { DPSFDProc netHandler(); if (netOK < 0) { DPSAddFD(M.theSocket, (DPSFDProc)netHandler, (void *)NULL, NX_MODALRESPTHRESHOLD); netOK = 1; } else if (netOK == 0) netOK = 2; } void endNetwork() { switch (netOK) { case 1: DPSRemoveFD(M.theSocket); netOK = -1; break; case 2: netOK = 0; break; default: break; } } static int dog; DPSFDProc netHandler(int fd, void *userData) { extern ConvertIncoming(), play(); int fromLen = sizeof(event.eventSource); int cc; event.eventType = EVENT_NETWORK; cc = recvfrom(M.theSocket, event.eventDetail, sizeof(RatPacket), 0, (struct sockaddr *)&event.eventSource, &fromLen); ConvertIncoming(event.eventDetail); if (cc <= 0) { if (cc < 0 && errno != EINTR) perror("event recvfrom"); return; } dog++; play(&event); if (dirty) flushIt(); } DPSTimedEntryProc timeHandler(DPSTimedEntry te, double now, void *userData) { extern play(); id v; static int icon_flash; if (flashIcon) { // this WILL get your attention icon_flash = (++icon_flash) % ICON_FLASH_PERIOD; switch (icon_flash) { case 1: v = [[NXApp appIcon] contentView]; [v lockFocus]; PSsetinstance(TRUE); PScompositerect(0.0, 0.0, 64.0, 64.0, NX_HIGHLIGHT); [v unlockFocus]; break; case ICON_FLASH_PERIOD/2+1: v = [[NXApp appIcon] contentView]; [v lockFocus]; PSnewinstance(); PSsetinstance(FALSE); [v unlockFocus]; if ([NXApp isActive]) { flashIcon = FALSE; icon_flash = 0; } break; } } if (netOK == 1) { // let's not crash the program, ok? if (dog) dog = 0; else { event.eventType = EVENT_TIMEOUT; play(&event); if (dirty) flushIt(); } } } MWError(char *s) /* normally this is in the window-independent stuff! */ { (void)NXRunAlertPanel("Fatal Error", s, "OK", (char *)NULL, (char *)NULL); StopWindow(); exit(0); } @implementation MazeApp + new { self = [super new]; [self setDelegate:self]; return self; } - setCurrentCursor:anObject { currentCursor = anObject; return self; } - setOkButton:anObject { okButton = anObject; return self; } - gotName:sender { [sender selectTextAt:1]; return self; } - gotDuke:sender { [okButton performClick:sender]; return self; } - gotButton:sender { return [self stopModal:[sender tag]]; } - userQuit:sender { extern quit(); quit(); // try to exit cleanly return [self terminate:sender]; // shouldn't get here } - windowDidBecomeKey:sender // used by initial panel { [editSubmenu disableFlushWindow]; if (![cutMenuCell isEnabled]) [cutMenuCell setEnabled:YES]; if (![copyMenuCell isEnabled]) [copyMenuCell setEnabled:YES]; if (![pasteMenuCell isEnabled]) [pasteMenuCell setEnabled:YES]; if (![deleteMenuCell isEnabled]) [deleteMenuCell setEnabled:YES]; if (![selectAllMenuCell isEnabled]) [selectAllMenuCell setEnabled:YES]; [[editSubmenu reenableFlushWindow] flushWindowIfNeeded]; return self; } - appDidInit:sender // the docs lie, things are *not* ready yet { static NXEvent ev = { NX_APPDEFINED }; ev.window = [[self mainMenu] windowNum]; ev.data.compound.subtype = 0; ev.ctxt = [self context]; (void)DPSPostEvent(&ev, FALSE); // post to tail of event queue return self; } - applicationDefined:(NXEvent *)theEvent { extern MazeInit(); DPSTimedEntryProc timeHandler(); switch (theEvent->data.compound.subtype) { case 0: MazeInit(NXArgc, NXArgv); // do the once-only stuff if (netOK == 2) { netOK = -1; beginNetwork(); } (void)DPSAddTimedEntry(.25, (DPSTimedEntryProc)timeHandler, (void *)NULL, NX_RUNMODALTHRESHOLD); break; default: [currentCursor set]; break; } return self; } @end static int sentPeek; @implementation MazeWin - keyDown:(NXEvent *)theEvent // at last, some "real" code { extern play(); if ((theEvent->flags&(NX_CONTROLMASK|NX_ALTERNATEMASK|NX_COMMANDMASK))==0 && theEvent->data.key.charSet==NX_ASCIISET) switch (theEvent->data.key.charCode) { case '4': if ((theEvent->flags&NX_NUMERICPADMASK)==0) return self; case 'a': event.eventType = EVENT_A; break; case '5': if ((theEvent->flags&NX_NUMERICPADMASK)==0) return self; case 's': event.eventType = EVENT_S; break; case '6': if ((theEvent->flags&NX_NUMERICPADMASK)==0) return self; case 'd': event.eventType = EVENT_D; break; case '+': if ((theEvent->flags&NX_NUMERICPADMASK)==0) return self; case 'f': event.eventType = EVENT_F; break; case ' ': event.eventType = EVENT_BAR; break; case 'i': event.eventType = EVENT_I; break; case 'k': event.eventType = EVENT_K; break; case 'o': event.eventType = EVENT_O; break; case 'l': event.eventType = EVENT_L; break; case 'q': event.eventType = EVENT_INT; break; default: return self; } else if ((theEvent->flags&(NX_CONTROLMASK|NX_ALTERNATEMASK| NX_COMMANDMASK|NX_NUMERICPADMASK))==NX_NUMERICPADMASK && theEvent->data.key.charSet==NX_SYMBOLSET && theEvent->data.key.charCode == 174) event.eventType = EVENT_BAR; else return [super keyDown:theEvent]; dog++; play(&event); if (dirty) flushIt(); return self; } - mouseDown:(NXEvent *)theEvent // I suppose this could be in MouseView { extern play(); const NXRect Mrect = { { VIEW_X_ORIGIN, VIEW_Y_ORIGIN}, { VIEW_X_DIM, VIEW_Y_DIM } }; NXPoint vLoc; vLoc = theEvent->location; [[self contentView] convertPoint:&vLoc fromView:nil]; if (!NXMouseInRect(&vLoc, &Mrect, YES)) return [super mouseDown:theEvent]; event.eventType = EVENT_MIDDLE_D; dog++; play(&event); if (dirty) flushIt(); return self; } - windowDidBecomeKey:sender { static NXEvent ev = { NX_APPDEFINED }; [editSubmenu disableFlushWindow]; if ([cutMenuCell isEnabled]) [cutMenuCell setEnabled:NO]; if ([copyMenuCell isEnabled]) [copyMenuCell setEnabled:NO]; if ([pasteMenuCell isEnabled]) [pasteMenuCell setEnabled:NO]; if ([deleteMenuCell isEnabled]) [deleteMenuCell setEnabled:NO]; if ([selectAllMenuCell isEnabled]) [selectAllMenuCell setEnabled:NO]; [[editSubmenu reenableFlushWindow] flushWindowIfNeeded]; ev.window = [sender windowNum]; // NextStep will change the ev.data.compound.subtype = 1; // cursor AFTER this is called ev.ctxt = [NXApp context]; // so we have to cheat again (void)DPSPostEvent(&ev, TRUE); // post to head of event queue return self; } @end @implementation MouseView { } + new { self = [super new]; [self setFlipped:YES]; return self; } - (BOOL)acceptsFirstMouse // catch right mouse events { return YES; } - rightMouseDown:(NXEvent *)theEvent { extern play(); NXPoint vLoc; vLoc = theEvent->location; // why only a 2-button mouse? Grr... [self convertPoint:&vLoc fromView:nil]; event.eventType = vLoc.x < VIEW_X_DIM/2.0 ? EVENT_LEFT_D : EVENT_RIGHT_D; sentPeek = 1; dog++; play(&event); if (dirty) flushIt(); return self; } - rightMouseUp:(NXEvent *)theEvent { extern play(); if (!sentPeek) return [super rightMouseUp:theEvent]; sentPeek = 0; event.eventType = EVENT_RIGHT_U; // don't need to distinguish here dog++; play(&event); if (dirty) flushIt(); return self; } - windowDidBecomeKey:sender // used by InfoPanel { [editSubmenu disableFlushWindow]; if ([cutMenuCell isEnabled]) [cutMenuCell setEnabled:NO]; if ([copyMenuCell isEnabled]) [copyMenuCell setEnabled:NO]; if ([pasteMenuCell isEnabled]) [pasteMenuCell setEnabled:NO]; if ([deleteMenuCell isEnabled]) [deleteMenuCell setEnabled:NO]; if ([selectAllMenuCell isEnabled]) [selectAllMenuCell setEnabled:NO]; [[editSubmenu reenableFlushWindow] flushWindowIfNeeded]; return self; } @end @implementation HelpPanelClass { NXSize maxSize; } - constrain { [NXApp getScreenSize:&maxSize]; // because it looks better this way maxSize.width -= 71.0, maxSize.height -= 2.0; [self setDelegate:self]; return self; } - initialHelp // main menu is useless during modal, this is a hack { id t; int sPos, ePos; #ifndef NOTDEF int xPos; #endif NXRect pRect; if ([self isVisible]) return [self orderOut:nil]; t = [[self contentView] docView]; #ifdef NOTDEF [t getParagraph:INIT_PARA start:&sPos end:&ePos rect:&pRect]; #else // getParagraph:start:end:rect: starts with the last physical line // of the chosen paragraph, i.e. not what a triple-click would // select. Something is major-league broken here. [t getParagraph:INIT_PARA-1 start:&sPos end:&xPos rect:&pRect]; sPos++; // can't use the end position here either! [t getParagraph:INIT_PARA start:&xPos end:&ePos rect:&pRect]; #endif [[t setSel:sPos:ePos] scrollSelToVisible]; [self moveTo:530.0:46.0]; #ifdef NOTDEF return [self orderWindow:NX_BELOW relativeTo:namePanel]; // rangechecks #else return [self orderFront:nil]; #endif } - windowWillResize:sender toSize:(NXSize *)frameSize { const NXSize minSize = { 280.0, 85.0 }; // be reasonable if (frameSize->width < minSize.width) frameSize->width = minSize.width; else if (frameSize->width > maxSize.width) frameSize->width = maxSize.width; if (frameSize->height < minSize.height) frameSize->height = minSize.height; else if (frameSize->height > maxSize.height) frameSize->height = maxSize.height; return self; } - windowDidBecomeKey:sender { [editSubmenu disableFlushWindow]; if ([cutMenuCell isEnabled]) [cutMenuCell setEnabled:NO]; if (![copyMenuCell isEnabled]) [copyMenuCell setEnabled:YES]; if ([pasteMenuCell isEnabled]) [pasteMenuCell setEnabled:NO]; if ([deleteMenuCell isEnabled]) [deleteMenuCell setEnabled:NO]; if (![selectAllMenuCell isEnabled]) [selectAllMenuCell setEnabled:YES]; [[editSubmenu reenableFlushWindow] flushWindowIfNeeded]; return self; } @end
These are the contents of the former NiCE NeXT User Group NeXTSTEP/OpenStep software archive, currently hosted by Netfuture.ch.