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/Cursor.h>
#import <appkit/Font.h>
#import <appkit/Form.h>
#import <appkit/Menu.h>
#import <appkit/MenuCell.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 <appkit/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
extern exit();
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] setFlip: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();
#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;
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 } };
where.origin.x=(float)(x*16+MAZE_X_ORIGIN);
where.origin.y=(float)(y*16+MAZE_Y_ORIGIN);
NXImageBitmap(&where, 16, 16, 1, 1, NX_PLANAR, 0,
(void *)newBits, (void *)NULL, (void *)NULL,
(void *)NULL, (void *)NULL);
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 = [Cursor newFromMachO:"rat.tiff"];
[rat setHotSpot:&ratHot];
deadRat = [Cursor newFromMachO:"dRat.tiff"];
[deadRat setHotSpot:&deadRatHot];
hourGlass = [Cursor newFromMachO:"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
{
extern DPSFDProc netHandler();
if (netOK < 0) {
DPSAddFD(M.theSocket, 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! */
{
extern exit();
(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, 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 setFlip: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.