From 9494d866a5d163b9ed6c74456f18c6fe4158fff8 Mon Sep 17 00:00:00 2001 From: simon Date: Sat, 22 Jan 2005 18:34:18 +0000 Subject: [PATCH] Initial checkin of a Mac OS X port of puzzles, using Cocoa. All puzzles are compiled together into a single monolithic application which allows you to select each one from one of its menus. git-svn-id: svn://svn.tartarus.org/sgt/puzzles@5173 cda61777-01e9-0310-a592-d414129be87e --- Recipe | 6 + macosx.m | 642 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ mkfiles.pl | 103 +++++++--- puzzles.h | 8 + 4 files changed, 736 insertions(+), 23 deletions(-) create mode 100644 macosx.m diff --git a/Recipe b/Recipe index f68c738..99c800b 100644 --- a/Recipe +++ b/Recipe @@ -11,12 +11,15 @@ !makefile gtk Makefile !makefile vc Makefile.vc !makefile cygwin Makefile.cyg +!makefile osx Makefile.osx WINDOWS = windows user32.lib gdi32.lib comctl32.lib COMMON = midend misc malloc random NET = net tree234 NETSLIDE = netslide tree234 +ALL = list NET NETSLIDE cube fifteen sixteen rect pattern + net : [X] gtk COMMON NET netslide : [X] gtk COMMON NETSLIDE cube : [X] gtk COMMON cube @@ -35,6 +38,9 @@ sixteen : [G] WINDOWS COMMON sixteen rect : [G] WINDOWS COMMON rect pattern : [G] WINDOWS COMMON pattern +# Mac OS X unified application containing all the puzzles. +Puzzles : [MX] macosx COMMON ALL + # The `nullgame' source file is a largely blank one, which contains # all the correct function definitions to compile and link, but # which defines the null game in which nothing is ever drawn and diff --git a/macosx.m b/macosx.m new file mode 100644 index 0000000..d257127 --- /dev/null +++ b/macosx.m @@ -0,0 +1,642 @@ +/* + * Mac OS X / Cocoa front end to puzzles. + * + * TODO: + * + * Before initial checkin: + * + * - create an OS X makefile target in mkfiles.pl rather than the + * current ad-hockery. + * + * Things that can reasonably be left to future checkins: + * + * - status bar support. + * + * - preset selection. Should be reasonably simple: just a matter + * of dynamically frobbing the menu bar. + * + * - configurability. Will no doubt involve learning all about the + * dialog control side of Cocoa. + * + * - needs an icon. + * + * - not sure what I should be doing about default window + * placement. Centring new windows is a bit feeble, but what's + * better? Is there a standard way to tell the OS "here's the + * _size_ of window I want, now use your best judgment about the + * initial position"? + * + * - a brief frob of the Mac numeric keypad suggests that it + * generates numbers no matter what you do. I wonder if I should + * try to figure out a way of detecting keypad codes so I can + * implement UP_LEFT and friends. + * + * - proper fatal errors. + * + * - is there a better approach to frontend_default_colour? + * + * - some options in the Window menu! Close and Minimise, I think, + * at least. + */ + +#include +#include +#import +#include "puzzles.h" + +void fatal(char *fmt, ...) +{ + /* FIXME: This will do for testing, but should be GUI-ish instead. */ + va_list ap; + + fprintf(stderr, "fatal error: "); + + va_start(ap, fmt); + vfprintf(stderr, fmt, ap); + va_end(ap); + + fprintf(stderr, "\n"); + exit(1); +} + +void frontend_default_colour(frontend *fe, float *output) +{ + /* FIXME */ + output[0] = output[1] = output[2] = 0.8F; +} +void status_bar(frontend *fe, char *text) +{ + /* FIXME */ +} + +void get_random_seed(void **randseed, int *randseedsize) +{ + time_t *tp = snew(time_t); + time(tp); + *randseed = (void *)tp; + *randseedsize = sizeof(time_t); +} + +/* ---------------------------------------------------------------------- + * The front end presented to midend.c. + * + * This is mostly a subclass of NSWindow. The actual `frontend' + * structure passed to the midend contains a variety of pointers, + * including that window object but also including the image we + * draw on, an ImageView to display it in the window, and so on. + */ + +@class GameWindow; +@class MyImageView; + +struct frontend { + GameWindow *window; + NSImage *image; + MyImageView *view; + NSColor **colours; + int ncolours; + int clipped; +}; + +@interface MyImageView : NSImageView +{ + GameWindow *ourwin; +} +- (void)setWindow:(GameWindow *)win; +- (BOOL)isFlipped; +- (void)mouseEvent:(NSEvent *)ev button:(int)b; +- (void)mouseDown:(NSEvent *)ev; +- (void)mouseDragged:(NSEvent *)ev; +- (void)mouseUp:(NSEvent *)ev; +- (void)rightMouseDown:(NSEvent *)ev; +- (void)rightMouseDragged:(NSEvent *)ev; +- (void)rightMouseUp:(NSEvent *)ev; +- (void)otherMouseDown:(NSEvent *)ev; +- (void)otherMouseDragged:(NSEvent *)ev; +- (void)otherMouseUp:(NSEvent *)ev; +@end + +@interface GameWindow : NSWindow +{ + const game *ourgame; + midend_data *me; + struct frontend fe; + struct timeval last_time; + NSTimer *timer; +} +- (id)initWithGame:(const game *)g; +- dealloc; +- (void)processButton:(int)b x:(int)x y:(int)y; +- (void)keyDown:(NSEvent *)ev; +- (void)activateTimer; +- (void)deactivateTimer; +@end + +@implementation MyImageView + +- (void)setWindow:(GameWindow *)win +{ + ourwin = win; +} + +- (BOOL)isFlipped +{ + return YES; +} + +- (void)mouseEvent:(NSEvent *)ev button:(int)b +{ + NSPoint point = [self convertPoint:[ev locationInWindow] fromView:nil]; + [ourwin processButton:b x:point.x y:point.y]; +} + +- (void)mouseDown:(NSEvent *)ev +{ + unsigned mod = [ev modifierFlags]; + [self mouseEvent:ev button:((mod & NSCommandKeyMask) ? RIGHT_BUTTON : + (mod & NSShiftKeyMask) ? MIDDLE_BUTTON : + LEFT_BUTTON)]; +} +- (void)mouseDragged:(NSEvent *)ev +{ + unsigned mod = [ev modifierFlags]; + [self mouseEvent:ev button:((mod & NSCommandKeyMask) ? RIGHT_DRAG : + (mod & NSShiftKeyMask) ? MIDDLE_DRAG : + LEFT_DRAG)]; +} +- (void)mouseUp:(NSEvent *)ev +{ + unsigned mod = [ev modifierFlags]; + [self mouseEvent:ev button:((mod & NSCommandKeyMask) ? RIGHT_RELEASE : + (mod & NSShiftKeyMask) ? MIDDLE_RELEASE : + LEFT_RELEASE)]; +} +- (void)rightMouseDown:(NSEvent *)ev +{ + unsigned mod = [ev modifierFlags]; + [self mouseEvent:ev button:((mod & NSShiftKeyMask) ? MIDDLE_BUTTON : + RIGHT_BUTTON)]; +} +- (void)rightMouseDragged:(NSEvent *)ev +{ + unsigned mod = [ev modifierFlags]; + [self mouseEvent:ev button:((mod & NSShiftKeyMask) ? MIDDLE_DRAG : + RIGHT_DRAG)]; +} +- (void)rightMouseUp:(NSEvent *)ev +{ + unsigned mod = [ev modifierFlags]; + [self mouseEvent:ev button:((mod & NSShiftKeyMask) ? MIDDLE_RELEASE : + RIGHT_RELEASE)]; +} +- (void)otherMouseDown:(NSEvent *)ev +{ + [self mouseEvent:ev button:MIDDLE_BUTTON]; +} +- (void)otherMouseDragged:(NSEvent *)ev +{ + [self mouseEvent:ev button:MIDDLE_DRAG]; +} +- (void)otherMouseUp:(NSEvent *)ev +{ + [self mouseEvent:ev button:MIDDLE_RELEASE]; +} +@end + +@implementation GameWindow +- (id)initWithGame:(const game *)g +{ + NSRect rect = { {0,0}, {0,0} }; + int w, h; + + ourgame = g; + + fe.window = self; + + me = midend_new(&fe, ourgame); + /* + * If we ever need to open a fresh window using a provided game + * ID, I think the right thing is to move most of this method + * into a new initWithGame:gameID: method, and have + * initWithGame: simply call that one and pass it NULL. + */ + midend_new_game(me); + midend_size(me, &w, &h); + rect.size.width = w; + rect.size.height = h; + + self = [super initWithContentRect:rect + styleMask:(NSTitledWindowMask | NSMiniaturizableWindowMask | + NSClosableWindowMask) + backing:NSBackingStoreBuffered + defer:true]; + [self setTitle:[NSString stringWithCString:ourgame->name]]; + + { + float *colours; + int i, ncolours; + + colours = midend_colours(me, &ncolours); + fe.ncolours = ncolours; + fe.colours = snewn(ncolours, NSColor *); + + for (i = 0; i < ncolours; i++) { + fe.colours[i] = [[NSColor colorWithDeviceRed:colours[i*3] + green:colours[i*3+1] blue:colours[i*3+2] + alpha:1.0] retain]; + } + } + + fe.image = [[NSImage alloc] initWithSize:rect.size]; + [fe.image setFlipped:YES]; + fe.view = [[MyImageView alloc] + initWithFrame:[self contentRectForFrameRect:[self frame]]]; + [fe.view setImage:fe.image]; + [fe.view setWindow:self]; + [self setContentView:fe.view]; + [self setIgnoresMouseEvents:NO]; + + midend_redraw(me); + + [self center]; /* :-) */ + + return self; +} + +- dealloc +{ + int i; + for (i = 0; i < fe.ncolours; i++) { + [fe.colours[i] release]; + } + sfree(fe.colours); + midend_free(me); + return [super dealloc]; +} + +- (void)processButton:(int)b x:(int)x y:(int)y +{ + if (!midend_process_key(me, x, y, b)) + [self close]; +} + +- (void)keyDown:(NSEvent *)ev +{ + NSString *s = [ev characters]; + int i, n = [s length]; + + for (i = 0; i < n; i++) { + int c = [s characterAtIndex:i]; + + /* + * ASCII gets passed straight to midend_process_key. + * Anything above that has to be translated to our own + * function key codes. + */ + if (c >= 0x80) { + switch (c) { + case NSUpArrowFunctionKey: + c = CURSOR_UP; + break; + case NSDownArrowFunctionKey: + c = CURSOR_DOWN; + break; + case NSLeftArrowFunctionKey: + c = CURSOR_LEFT; + break; + case NSRightArrowFunctionKey: + c = CURSOR_RIGHT; + break; + default: + continue; + } + } + + [self processButton:c x:-1 y:-1]; + } +} + +- (void)activateTimer +{ + if (timer != nil) + return; + + timer = [NSTimer scheduledTimerWithTimeInterval:0.02 + target:self selector:@selector(timerTick:) + userInfo:nil repeats:YES]; + gettimeofday(&last_time, NULL); +} + +- (void)deactivateTimer +{ + if (timer == nil) + return; + + [timer invalidate]; + timer = nil; +} + +- (void)timerTick:(id)sender +{ + struct timeval now; + float elapsed; + gettimeofday(&now, NULL); + elapsed = ((now.tv_usec - last_time.tv_usec) * 0.000001F + + (now.tv_sec - last_time.tv_sec)); + midend_timer(me, elapsed); + last_time = now; +} + +@end + +/* + * Drawing routines called by the midend. + */ +void draw_polygon(frontend *fe, int *coords, int npoints, + int fill, int colour) +{ + NSBezierPath *path = [NSBezierPath bezierPath]; + int i; + + [[NSGraphicsContext currentContext] setShouldAntialias:YES]; + + assert(colour >= 0 && colour < fe->ncolours); + [fe->colours[colour] set]; + + for (i = 0; i < npoints; i++) { + NSPoint p = { coords[i*2] + 0.5, coords[i*2+1] + 0.5 }; + if (i == 0) + [path moveToPoint:p]; + else + [path lineToPoint:p]; + } + + [path closePath]; + + if (fill) + [path fill]; + else + [path stroke]; +} +void draw_line(frontend *fe, int x1, int y1, int x2, int y2, int colour) +{ + NSBezierPath *path = [NSBezierPath bezierPath]; + NSPoint p1 = { x1 + 0.5, y1 + 0.5 }, p2 = { x2 + 0.5, y2 + 0.5 }; + + [[NSGraphicsContext currentContext] setShouldAntialias:NO]; + + assert(colour >= 0 && colour < fe->ncolours); + [fe->colours[colour] set]; + + [path moveToPoint:p1]; + [path lineToPoint:p2]; + [path stroke]; +} +void draw_rect(frontend *fe, int x, int y, int w, int h, int colour) +{ + NSRect r = { {x,y}, {w,h} }; + + [[NSGraphicsContext currentContext] setShouldAntialias:NO]; + + assert(colour >= 0 && colour < fe->ncolours); + [fe->colours[colour] set]; + + NSRectFill(r); +} +void draw_text(frontend *fe, int x, int y, int fonttype, int fontsize, + int align, int colour, char *text) +{ + NSString *string = [NSString stringWithCString:text]; + NSDictionary *attr; + NSFont *font; + NSSize size; + NSPoint point; + + [[NSGraphicsContext currentContext] setShouldAntialias:YES]; + + assert(colour >= 0 && colour < fe->ncolours); + + if (fonttype == FONT_FIXED) + font = [NSFont userFixedPitchFontOfSize:fontsize]; + else + font = [NSFont userFontOfSize:fontsize]; + + attr = [NSDictionary dictionaryWithObjectsAndKeys: + fe->colours[colour], NSForegroundColorAttributeName, + font, NSFontAttributeName, nil]; + + point.x = x; + point.y = y; + + size = [string sizeWithAttributes:attr]; + if (align & ALIGN_HRIGHT) + point.x -= size.width; + else if (align & ALIGN_HCENTRE) + point.x -= size.width / 2; + if (align & ALIGN_VCENTRE) + point.y -= size.height / 2; + + [string drawAtPoint:point withAttributes:attr]; +} +void draw_update(frontend *fe, int x, int y, int w, int h) +{ + /* FIXME */ +} +void clip(frontend *fe, int x, int y, int w, int h) +{ + NSRect r = { {x,y}, {w,h} }; + + if (!fe->clipped) + [[NSGraphicsContext currentContext] saveGraphicsState]; + [NSBezierPath clipRect:r]; + fe->clipped = TRUE; +} +void unclip(frontend *fe) +{ + if (fe->clipped) + [[NSGraphicsContext currentContext] restoreGraphicsState]; + fe->clipped = FALSE; +} +void start_draw(frontend *fe) +{ + [fe->image lockFocus]; + fe->clipped = FALSE; +} +void end_draw(frontend *fe) +{ + [fe->image unlockFocus]; + [fe->view setNeedsDisplay]; +} + +void deactivate_timer(frontend *fe) +{ + [fe->window deactivateTimer]; +} +void activate_timer(frontend *fe) +{ + [fe->window activateTimer]; +} + +/* ---------------------------------------------------------------------- + * Utility routines for constructing OS X menus. +~|~ */ + +NSMenu *newmenu(const char *title) +{ + return [[[NSMenu allocWithZone:[NSMenu menuZone]] + initWithTitle:[NSString stringWithCString:title]] + autorelease]; +} + +NSMenu *newsubmenu(NSMenu *parent, const char *title) +{ + NSMenuItem *item; + NSMenu *child; + + item = [[[NSMenuItem allocWithZone:[NSMenu menuZone]] + initWithTitle:[NSString stringWithCString:title] + action:NULL + keyEquivalent:@""] + autorelease]; + child = newmenu(title); + [item setEnabled:YES]; + [item setSubmenu:child]; + [parent addItem:item]; + return child; +} + +id initnewitem(NSMenuItem *item, NSMenu *parent, const char *title, + const char *key, id target, SEL action) +{ + unsigned mask = NSCommandKeyMask; + + if (key[strcspn(key, "-")]) { + while (*key && *key != '-') { + int c = tolower((unsigned char)*key); + if (c == 's') { + mask |= NSShiftKeyMask; + } else if (c == 'o' || c == 'a') { + mask |= NSAlternateKeyMask; + } + key++; + } + if (*key) + key++; + } + + item = [[item initWithTitle:[NSString stringWithCString:title] + action:NULL + keyEquivalent:[NSString stringWithCString:key]] + autorelease]; + + if (*key) + [item setKeyEquivalentModifierMask: mask]; + + [item setEnabled:YES]; + [item setTarget:target]; + [item setAction:action]; + + [parent addItem:item]; + + return item; +} + +NSMenuItem *newitem(NSMenu *parent, char *title, char *key, + id target, SEL action) +{ + return initnewitem([NSMenuItem allocWithZone:[NSMenu menuZone]], + parent, title, key, target, action); +} + +/* ---------------------------------------------------------------------- + * Tiny extension to NSMenuItem which carries a payload of a `const + * game *', allowing our AppController to work out _which_ game + * needs to be launched when it receives a newGame message. + */ +@interface GameMenuItem : NSMenuItem +{ + const game *ourgame; +} +- (void)setGame:(const game *)g; +- (const game *)getGame; +@end +@implementation GameMenuItem +- (void)setGame:(const game *)g +{ + ourgame = g; +} +- (const game *)getGame +{ + return ourgame; +} +@end + +/* ---------------------------------------------------------------------- + * AppController: the object which receives the messages from all + * menu selections that aren't standard OS X functions. + */ +@interface AppController : NSObject +{ +} +- (IBAction)newGame:(id)sender; +@end + +@implementation AppController + +- (IBAction)newGame:(id)sender +{ + const game *g = [sender getGame]; + id win; + + win = [[GameWindow alloc] initWithGame:g]; + [win makeKeyAndOrderFront:self]; +} + +@end + +/* ---------------------------------------------------------------------- + * Main program. Constructs the menus and runs the application. + */ +int main(int argc, char **argv) +{ + NSAutoreleasePool *pool; + NSMenu *menu; + NSMenuItem *item; + AppController *controller; + + pool = [[NSAutoreleasePool alloc] init]; + [NSApplication sharedApplication]; + + controller = [[[AppController alloc] init] autorelease]; + + [NSApp setMainMenu: newmenu("Main Menu")]; + + menu = newsubmenu([NSApp mainMenu], "Apple Menu"); + [NSApp setServicesMenu:newsubmenu(menu, "Services")]; + [menu addItem:[NSMenuItem separatorItem]]; + item = newitem(menu, "Hide Puzzles", "h", NSApp, @selector(hide:)); + item = newitem(menu, "Hide Others", "o-h", NSApp, @selector(hideOtherApplications:)); + item = newitem(menu, "Show All", "", NSApp, @selector(unhideAllApplications:)); + [menu addItem:[NSMenuItem separatorItem]]; + item = newitem(menu, "Quit", "q", NSApp, @selector(terminate:)); + [NSApp setAppleMenu: menu]; + + menu = newsubmenu([NSApp mainMenu], "Game"); + { + int i; + + for (i = 0; i < gamecount; i++) { + id item = + initnewitem([GameMenuItem allocWithZone:[NSMenu menuZone]], + menu, gamelist[i]->name, "", controller, + @selector(newGame:)); + [item setGame:gamelist[i]]; + } + } + + menu = newsubmenu([NSApp mainMenu], "Windows"); + [NSApp setWindowsMenu: menu]; + + [NSApp run]; + [pool release]; +} diff --git a/mkfiles.pl b/mkfiles.pl index 8c23127..382a40b 100755 --- a/mkfiles.pl +++ b/mkfiles.pl @@ -91,8 +91,8 @@ while () { if ($groups{$i}) { foreach $j (@{$groups{$i}}) { unshift @objs, $j; } } elsif (($i eq "[G]" or $i eq "[C]" or $i eq "[M]" or - $i eq "[X]" or $i eq "[U]") and defined $prog) { - $type = substr($i,1,1); + $i eq "[X]" or $i eq "[U]" or $i eq "[MX]") and defined $prog) { + $type = substr($i,1,(length $i)-2); } else { push @$listref, $i; } @@ -121,7 +121,8 @@ foreach $i (@prognames) { sort @{$programs{$i}}; $programs{$i} = [@list]; foreach $j (@list) { - # Dependencies for "x" start with "x.c". + # Dependencies for "x" start with "x.c" or "x.m" (depending on + # which one exists). # Dependencies for "x.res" start with "x.rc". # Dependencies for "x.rsrc" start with "x.r". # Both types of file are pushed on the list of files to scan. @@ -138,6 +139,7 @@ foreach $i (@prognames) { # libraries don't have dependencies } else { $file = "$j.c"; + $file = "$j.m" unless &findfile($file); $depends{$j} = [$file]; push @scanlist, $file; } @@ -208,7 +210,7 @@ sub mfval($) { # Returns true if the argument is a known makefile type. Otherwise, # prints a warning and returns false; if (grep { $type eq $_ } - ("vc","vcproj","cygwin","borland","lcc","gtk","mpw")) { + ("vc","vcproj","cygwin","borland","lcc","gtk","mpw","osx")) { return 1; } warn "$.:unknown makefile type '$type'\n"; @@ -230,14 +232,16 @@ sub dirpfx { sub findfile { my ($name) = @_; - my $dir, $i, $outdir = ""; + my $dir; + my $i; + my $outdir = undef; unless (defined $findfilecache{$name}) { $i = 0; foreach $dir (@srcdirs) { $outdir = $dir, $i++ if -f "$dir$name"; } die "multiple instances of source file $name\n" if $i > 1; - $findfilecache{$name} = $outdir . $name; + $findfilecache{$name} = (defined $outdir ? $outdir . $name : undef); } return $findfilecache{$name}; } @@ -309,7 +313,7 @@ sub prognames { @ret = (); foreach $n (@prognames) { ($prog, $type) = split ",", $n; - push @ret, $n if index($types, $type) >= 0; + push @ret, $n if index(":$types:", ":$type:") >= 0; } return @ret; } @@ -321,7 +325,7 @@ sub progrealnames { @ret = (); foreach $n (@prognames) { ($prog, $type) = split ",", $n; - push @ret, $prog if index($types, $type) >= 0; + push @ret, $prog if index(":$types:", ":$type:") >= 0; } return @ret; } @@ -330,7 +334,7 @@ sub manpages { my ($types,$suffix) = @_; # assume that all UNIX programs have a man page - if($suffix eq "1" && $types =~ /X/) { + if($suffix eq "1" && $types =~ /:X:/) { return map("$_.1", &progrealnames($types)); } return (); @@ -379,9 +383,9 @@ if (defined $makefiles{'cygwin'}) { "%.res.o: %.rc\n". "\t\$(RC) \$(FWHACK) \$(RCFL) \$(RCFLAGS) \$< \$\@\n". "\n"; - print &splitline("all:" . join "", map { " $_.exe" } &progrealnames("GC")); + print &splitline("all:" . join "", map { " $_.exe" } &progrealnames("G:C")); print "\n\n"; - foreach $p (&prognames("GC")) { + foreach $p (&prognames("G:C")) { ($prog, $type) = split ",", $p; $objstr = &objects($p, "X.o", "X.res.o", undef); print &splitline($prog . ".exe: " . $objstr), "\n"; @@ -451,16 +455,16 @@ if (defined $makefiles{'borland'}) { &splitline("\tbrcc32 \$(FWHACK) \$(RCFL) -i \$(BCB)\\include -r". " -DNO_WINRESRC_H -DWIN32 -D_WIN32 -DWINVER=0x0401 \$*.rc",69)."\n". "\n"; - print &splitline("all:" . join "", map { " $_.exe" } &progrealnames("GC")); + print &splitline("all:" . join "", map { " $_.exe" } &progrealnames("G:C")); print "\n\n"; - foreach $p (&prognames("GC")) { + foreach $p (&prognames("G:C")) { ($prog, $type) = split ",", $p; $objstr = &objects($p, "X.obj", "X.res", undef); print &splitline("$prog.exe: " . $objstr . " $prog.rsp"), "\n"; my $ap = ($type eq "G") ? "-aa" : "-ap"; print "\tilink32 $ap -Gn -L\$(BCB)\\lib \@$prog.rsp\n\n"; } - foreach $p (&prognames("GC")) { + foreach $p (&prognames("G:C")) { ($prog, $type) = split ",", $p; print $prog, ".rsp: \$(MAKEFILE)\n"; $objstr = &objects($p, "X.obj", undef, undef); @@ -533,15 +537,15 @@ if (defined $makefiles{'vc'}) { ".rc.res:\n". "\trc \$(FWHACK) \$(RCFL) -r -DWIN32 -D_WIN32 -DWINVER=0x0400 \$*.rc\n". "\n"; - print &splitline("all:" . join "", map { " $_.exe" } &progrealnames("GC")); + print &splitline("all:" . join "", map { " $_.exe" } &progrealnames("G:C")); print "\n\n"; - foreach $p (&prognames("GC")) { + foreach $p (&prognames("G:C")) { ($prog, $type) = split ",", $p; $objstr = &objects($p, "X.obj", "X.res", undef); print &splitline("$prog.exe: " . $objstr . " $prog.rsp"), "\n"; print "\tlink \$(LFLAGS) -out:$prog.exe -map:$prog.map \@$prog.rsp\n\n"; } - foreach $p (&prognames("GC")) { + foreach $p (&prognames("G:C")) { ($prog, $type) = split ",", $p; print $prog, ".rsp: \$(MAKEFILE)\n"; $objstr = &objects($p, "X.obj", "X.res", "X.lib"); @@ -608,7 +612,7 @@ if (defined $makefiles{'vcproj'}) { %all_object_deps = map {$_->{obj} => $_->{deps}} @deps; # Create the project files # Get names of all Windows projects (GUI and console) - my @prognames = &prognames("GC"); + my @prognames = &prognames("G:C"); foreach $progname (@prognames) { create_project(\%all_object_deps, $progname); } @@ -879,9 +883,9 @@ if (defined $makefiles{'gtk'}) { "%.o:\n". "\t\$(CC) \$(COMPAT) \$(FWHACK) \$(XFLAGS) \$(CFLAGS) -c \$<\n". "\n"; - print &splitline("all:" . join "", map { " $_" } &progrealnames("XU")); + print &splitline("all:" . join "", map { " $_" } &progrealnames("X:U")); print "\n\n"; - foreach $p (&prognames("XU")) { + foreach $p (&prognames("X:U")) { ($prog, $type) = split ",", $p; $objstr = &objects($p, "X.o", undef, undef); print &splitline($prog . ": " . $objstr), "\n"; @@ -896,7 +900,7 @@ if (defined $makefiles{'gtk'}) { print "\n"; print $makefile_extra{'gtk'}; print "\nclean:\n". - "\trm -f *.o". (join "", map { " $_" } &progrealnames("XU")) . "\n"; + "\trm -f *.o". (join "", map { " $_" } &progrealnames("X:U")) . "\n"; select STDOUT; close OUT; } @@ -1071,9 +1075,9 @@ if (defined $makefiles{'lcc'}) { ".rc.res:\n". &splitline("\tlrc \$(FWHACK) \$(RCFL) -r \$*.rc",69)."\n". "\n"; - print &splitline("all:" . join "", map { " $_.exe" } &progrealnames("GC")); + print &splitline("all:" . join "", map { " $_.exe" } &progrealnames("G:C")); print "\n\n"; - foreach $p (&prognames("GC")) { + foreach $p (&prognames("G:C")) { ($prog, $type) = split ",", $p; $objstr = &objects($p, "X.obj", "X.res", undef); print &splitline("$prog.exe: " . $objstr ), "\n"; @@ -1097,3 +1101,56 @@ if (defined $makefiles{'lcc'}) { select STDOUT; close OUT; } + +if (defined $makefiles{'osx'}) { + $dirpfx = &dirpfx($makefiles{'osx'}, "/"); + + ##-- Mac OS X makefile + open OUT, ">$makefiles{'osx'}"; select OUT; + print + "# Makefile for $project_name under Mac OS X.\n". + "#\n# This file was created by `mkfiles.pl' from the `Recipe' file.\n". + "# DO NOT EDIT THIS FILE DIRECTLY; edit Recipe or mkfiles.pl instead.\n"; + # gcc command line option is -D not /D + ($_ = $help) =~ s/=\/D/=-D/gs; + print $_; + print + "CC = \$(TOOLPATH)gcc\n". + "\n". + &splitline("CFLAGS = -O2 -Wall -Werror -g -DMAC_OS_X " . + (join " ", map {"-I$dirpfx$_"} @srcdirs))."\n". + "LDFLAGS = -framework Cocoa\n". + "\n". + ".SUFFIXES: .o .c .m\n". + "\n". + ".c.o:\n". + "\t\$(CC) \$(COMPAT) \$(FWHACK) \$(XFLAGS) \$(CFLAGS) -c \$<\n". + ".m.o:\n". + "\t\$(CC) -x objective-c \$(COMPAT) \$(FWHACK) \$(XFLAGS) \$(CFLAGS) -c \$<\n". + "\n"; + print &splitline("all:" . join "", map { " $_" } &progrealnames("MX")); + print "\n\n"; + foreach $p (&prognames("MX")) { + ($prog, $type) = split ",", $p; + $objstr = &objects($p, "X.o", undef, undef); + print "${prog}: ${prog}.app/Contents/MacOS/$prog\n\n"; + print "${prog}.app:\n\tmkdir \$\@\n"; + print "${prog}.app/Contents: ${prog}.app\n\tmkdir \$\@\n"; + print "${prog}.app/Contents/MacOS: ${prog}.app/Contents\n\tmkdir \$\@\n"; + print &splitline("${prog}.app/Contents/MacOS/$prog: ". + "${prog}.app/Contents/MacOS " . $objstr), "\n"; + $libstr = &objects($p, undef, undef, "-lX"); + print &splitline("\t\$(CC)" . $mw . " \$(LDFLAGS) -o \$@ " . + $objstr . " $libstr", 69), "\n\n"; + } + foreach $d (&deps("X.o", undef, $dirpfx, "/")) { + print &splitline(sprintf("%s: %s", $d->{obj}, join " ", @{$d->{deps}})), + "\n"; + } + print "\n"; + print $makefile_extra{'osx'}; + print "\nclean:\n". + "\trm -f *.o\n". + "\trm -rf *.app\n"; + select STDOUT; close OUT; +} diff --git a/puzzles.h b/puzzles.h index 4de81a7..d0882e3 100644 --- a/puzzles.h +++ b/puzzles.h @@ -200,6 +200,14 @@ struct game { }; /* + * Here we include the knowledge of which platforms are of which + * type. + */ +#ifdef MAC_OS_X /* this must be defined in the OS X Makefile */ +#define COMBINED +#endif + +/* * For one-game-at-a-time platforms, there's a single structure * like the above, under a fixed name. For all-at-once platforms, * there's a list of all available puzzles in array form. -- 2.11.0