// // ZDisplay.m // ZoomCocoa // // Created by Andrew Hunter on Wed Sep 10 2003. // Copyright (c) 2003 Andrew Hunter. All rights reserved. // #import "ZoomProtocol.h" #import "ZoomZMachine.h" #import "ZoomServer.h" #include "file.h" #include "display.h" #include "v6display.h" #include "zmachine.h" #include "blorb.h" #include "zscii.h" #ifdef DEBUG # define NOTE(x) NSLog(@"ZDisplay: %@", x) #else # define NOTE(x) #endif // = Display state = NSAutoreleasePool* displayPool = nil; static BOOL zDisplayForceFixed[8] = { NO, NO, NO, NO, NO, NO, NO, NO }; static int is_v6 = 0; int zDisplayCurrentWindow = 0; ZStyle* zDisplayCurrentStyle = nil; BOOL zPixmapDisplay = NO; // = Display = static int cocoa_to_zscii(int theChar) { // Convert the character to ZSCII unichar badChar = '?'; unichar key = 0; // Deal with special keys (ie, convert to ZSCII) switch (theChar) { // Arrow keys case NSUpArrowFunctionKey: case 129: key = 129; break; case NSDownArrowFunctionKey: case 130: key = 130; break; case NSLeftArrowFunctionKey: case 131: key = 131; break; case NSRightArrowFunctionKey: case 132: key = 132; break; // Delete/return case 10: key = 13; break; case NSDeleteFunctionKey: key = 8; break; // Function keys case NSF1FunctionKey: case 133: key = 133; break; case NSF2FunctionKey: case 134: key = 134; break; case NSF3FunctionKey: case 135: key = 135; break; case NSF4FunctionKey: case 136: key = 136; break; case NSF5FunctionKey: case 137: key = 137; break; case NSF6FunctionKey: case 138: key = 138; break; case NSF7FunctionKey: case 139: key = 139; break; case NSF8FunctionKey: case 140: key = 140; break; case NSF9FunctionKey: case 141: key = 141; break; case NSF10FunctionKey: case 142: key = 142; break; case NSF11FunctionKey: case 143: key = 143; break; case NSF12FunctionKey: case 144: key = 144; break; // Mouse buttons (we use fake function keys for this) case NSF33FunctionKey: case 252: key = 252; break; case NSF34FunctionKey: case 254: key = 254; break; case NSF35FunctionKey: case 253: key = 253; break; // Numeric keypad case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9': /* if ([event modifierFlags]&NSNumericPadKeyMask) { // Report this as a numeric keypad event key = 145 + (chr-'0'); } */ break; default: // Unicode/other characters if (theChar >= 127) { // The character must be in the equivalence table... key = zscii_get_char(theChar); if (key <= 0) key = badChar; } break; } if (key > 0) theChar = key; return theChar; } // = Debugging functions = void printf_debug(char* format, ...) { va_list ap; char string[8192]; va_start(ap, format); vsnprintf(string, 8192, format, ap); string[8191] = 0; va_end(ap); NSLog(@"DEBUG: %s", string); fputs(string, stdout); } void printf_info (char* format, ...) { NSLog(@"Function not implemented: %s %i", __FILE__, __LINE__); } void printf_info_done(void) { NSLog(@"Function not implemented: %s %i", __FILE__, __LINE__); } void printf_error(char* format, ...) { NSLog(@"Function not implemented: %s %i", __FILE__, __LINE__); } void printf_error_done(void) { NSLog(@"Function not implemented: %s %i", __FILE__, __LINE__); } ZDisplay* display_get_info(void) { static ZDisplay dis; dis.status_line = 1; dis.can_split = 1; dis.variable_font = 1; dis.colours = 1; dis.boldface = 1; dis.italic = 1; dis.fixed_space = 1; dis.sound_effects = 0; dis.timed_input = 1; dis.mouse = 1; int xsize, ysize; int pxsize, pysize; int fontwidth, fontheight; [[mainMachine display] dimensionX: &xsize Y: &ysize]; [[mainMachine display] pixmapX: &pxsize Y: &pysize]; [[mainMachine display] fontWidth: &fontwidth height: &fontheight]; dis.lines = ysize; dis.columns = xsize; dis.width = pxsize; dis.height = pysize; dis.font_width = fontwidth; dis.font_height = fontheight + 2.0; dis.pictures = 1; dis.fore = rc_get_foreground(); dis.back = rc_get_background(); NOTE(@"display_get_info"); return &dis; } void display_initialise(void) { NOTE(@"display_initialise"); if (zDisplayCurrentStyle) [zDisplayCurrentStyle release]; zDisplayCurrentStyle = [[ZStyle alloc] init]; // Clear out the image cache if (zoomImageCache != NULL) free(zoomImageCache); zoomImageCache = NULL; zoomImageCacheSize = 0; //display_clear(); (Commented out to support autosave) } void display_reinitialise(void) { NOTE(@"display_reinitialise"); if (zDisplayCurrentStyle) [zDisplayCurrentStyle release]; zDisplayCurrentStyle = [[ZStyle alloc] init]; // Clear out the image cache if (zoomImageCache != NULL) free(zoomImageCache); zoomImageCache = NULL; zoomImageCacheSize = 0; display_clear(); } void display_finalise(void) { NOTE(@"display_finalise"); [mainMachine flushBuffers]; if (zDisplayCurrentStyle) [zDisplayCurrentStyle release]; zDisplayCurrentStyle = nil; } void display_exit(int code) { #ifdef DEBUG NSLog(@"ZDisplay: display_exit(%i)", code); #endif [mainMachine flushBuffers]; NSLog(@"Server exited with code %i (clean)", code); exit(code); } // Clearing/erasure functions void display_clear(void) { NSObject* win; NOTE(@"display_clear"); zDisplayCurrentWindow = 0; [mainMachine flushBuffers]; win = [mainMachine windowNumber: 1]; [win clearWithStyle: zDisplayCurrentStyle]; [(NSObject*)win startAtLine: 0]; [(NSObject*)win endAtLine: 0]; win = [mainMachine windowNumber: 2]; [win clearWithStyle: zDisplayCurrentStyle]; [(NSObject*)win startAtLine: 0]; [(NSObject*)win endAtLine: 0]; win = [mainMachine windowNumber: 0]; [win clearWithStyle: zDisplayCurrentStyle]; } void display_erase_window(void) { NOTE(@"display_erase_window"); [[mainMachine buffer] clearWindow: [mainMachine windowNumber: zDisplayCurrentWindow] withStyle: zDisplayCurrentStyle]; } void display_erase_line(int val) { NOTE(@"display_erase_line"); [[mainMachine buffer] eraseLineInWindow: (NSObject*)[mainMachine windowNumber: zDisplayCurrentWindow] withStyle: zDisplayCurrentStyle]; } // = Display functions = void display_prints(const int* buf) { if (is_v6) { #ifdef DEBUG NSLog(@"display_prints: redirecting to v6..."); #endif v6_prints(buf); return; } // Convert buf to an NSString int length; static unichar* bufU = NULL; for (length=0; buf[length] != 0; length++) { bufU = realloc(bufU, sizeof(unichar)*((length>>4)+1)<<4); bufU[length] = buf[length]; } if (length == 0) return; NSString* str = [NSString stringWithCharacters: bufU length: length]; #ifdef DEBUG NSLog(@"ZDisplay: display_prints(\"%@\")", str); #endif // Send to the window [[mainMachine buffer] writeString: str withStyle: zDisplayCurrentStyle toWindow: [mainMachine windowNumber: zDisplayCurrentWindow]]; } void display_prints_c(const char* buf) { #ifdef DEBUG NSLog(@"ZDisplay: display_prints_c(\"%s\")", buf); #endif if ([mainMachine windowNumber: zDisplayCurrentWindow] == nil) { NSLog(@"No window: leaking '%s'", buf); return; } NSString* str = [NSString stringWithCString: buf]; [[mainMachine buffer] writeString: str withStyle: zDisplayCurrentStyle toWindow: [mainMachine windowNumber: zDisplayCurrentWindow]]; } void display_printc(int chr) { #ifdef DEBUG NSLog(@"ZDisplay: display_printc(\"%c\")", chr); #endif if ([mainMachine windowNumber: zDisplayCurrentWindow] == nil) { NSLog(@"No window: leaking '%c'", chr); return; } unichar bufU[1]; bufU[0] = chr; NSString* str = [NSString stringWithCharacters: bufU length: 1]; [[mainMachine buffer] writeString: str withStyle: zDisplayCurrentStyle toWindow: [mainMachine windowNumber: zDisplayCurrentWindow]]; } void display_printf(const char* format, ...) { va_list ap; char string[512]; NOTE(@"display_printf"); va_start(ap, format); vsnprintf(string, 512, format, ap); string[511] = 0; va_end(ap); display_prints_c(string); } // = Input = int display_readline(int* buf, int len, long int timeout) { NOTE(@"display_readline"); [mainMachine flushBuffers]; NSObject* display = [mainMachine display]; if (len <= 0) { zmachine_fatal("display_readline called with a buffer length of %i", len); return 0; } // Prefix NSString* prefix = [@"" retain]; if (buf[0] != 0) { unichar* prefixBuf = malloc(sizeof(unichar)*len); int x; for (x=0; x 0) { // Ask the display to backtrack input if possible prefix = [[display backtrackInputOver: [prefix autorelease]] retain]; } NSDate* when; if (timeout > 0) { when = [NSDate dateWithTimeIntervalSinceNow: ((double)timeout)/1000.0]; } else { when = [NSDate distantFuture]; } [when retain]; // Wait for input while (mainMachine != nil && [mainMachine terminatingCharacter] == 0 && [[mainMachine inputBuffer] length] == 0 && [when compare: [NSDate date]] == NSOrderedDescending) { // Cycle the autorelease pool [displayPool release]; displayPool = [[NSAutoreleasePool alloc] init]; [mainLoop acceptInputForMode: NSDefaultRunLoopMode beforeDate: when]; } [when release]; // Cycle the autorelease pool [displayPool release]; displayPool = [[NSAutoreleasePool alloc] init]; // If there was a timeout, get the text so far NSString* inputToDate = nil; if ([mainMachine terminatingCharacter] == 0 && [[mainMachine inputBuffer] length] == 0) { inputToDate = [display receivedTextToDate]; } // Finish up [display stopReceiving]; // Copy the data NSMutableString* inputBuffer = inputToDate==nil?[mainMachine inputBuffer]:[[inputToDate mutableCopy] autorelease]; // Add the prefix, if any if (prefix) { [inputBuffer insertString: prefix atIndex: 0]; } #ifdef DEBUG NSLog(@"ZDisplay: display_readline = %@", inputBuffer); #endif int realLen = [inputBuffer length]; if (realLen > (len-1)) { realLen = len-1; } // Remove any newlines at the end // If there's a newline at the end, then we didn't get a terminating character or timeout int chr; int termChar = 0; for (chr = 0; chr* display = [mainMachine display]; // Cycle the autorelease pool [displayPool release]; displayPool = [[NSAutoreleasePool alloc] init]; // Send the input style across [[mainMachine windowNumber: zDisplayCurrentWindow] setInputStyle: zDisplayCurrentStyle]; // Request input [[mainMachine inputBuffer] setString: @""]; [[mainMachine windowNumber: zDisplayCurrentWindow] setFocus]; [display shouldReceiveCharacters]; NSDate* when; if (timeout > 0) { when = [NSDate dateWithTimeIntervalSinceNow: ((double)timeout)/1000.0]; } else { when = [NSDate distantFuture]; } [when retain]; // Wait for input while (mainMachine != nil && [[mainMachine inputBuffer] length] == 0 && [when compare: [NSDate date]] == NSOrderedDescending) { [mainLoop acceptInputForMode: NSDefaultRunLoopMode beforeDate: when]; } [when release]; // Cycle the autorelease pool [displayPool release]; displayPool = [[NSAutoreleasePool alloc] init]; // Finish up [display stopReceiving]; // Copy the data unichar theChar; if ([[mainMachine inputBuffer] length] == 0) { theChar = 0; // Timeout occured } else { NSMutableString* inputBuffer = [mainMachine inputBuffer]; theChar = cocoa_to_zscii([inputBuffer characterAtIndex: 0]); } #ifdef DEBUG NSLog(@"ZDisplay: display_readchar = %i", theChar); #endif return theChar; } // = Used by the debugger = void display_sanitise (void) { NOTE(@"display_santise"); NSLog(@"Function not implemented: %s %i", __FILE__, __LINE__); } void display_desanitise(void) { NOTE(@"display_desanitise"); NSLog(@"Function not implemented: %s %i", __FILE__, __LINE__); } // = Display styling = void display_is_v6(void) { NOTE(@"display_is_v6"); is_v6 = 1; } int display_set_font(int font) { switch (font) { case -1: display_set_style(-16); break; default: break; } return 0; } int display_set_style(int style) { NOTE(@"display_set_style"); if (is_v6) { v6_set_style(style); return style; } // Copy the old style ZStyle* newStyle = [zDisplayCurrentStyle copy]; int oldStyle = ([newStyle reversed]?1:0)| ([newStyle bold]?2:0)| ([newStyle underline]?4:0)| ([newStyle fixed]?8:0)| ([newStyle symbolic]?16:0); // Not using this any more if (zDisplayCurrentStyle) [zDisplayCurrentStyle release]; BOOL flag = (style<0)?NO:YES; if (style < 0) style = -style; // Set the flags if (style == 0) { [newStyle setBold: NO]; [newStyle setUnderline: NO]; [newStyle setFixed: NO]; [newStyle setSymbolic: NO]; [newStyle setReversed: NO]; zDisplayCurrentStyle = newStyle; return oldStyle; } if (style&1) [newStyle setReversed: flag]; if (style&2) [newStyle setBold: flag]; if (style&4) [newStyle setUnderline: flag]; if (style&8) [newStyle setFixed: flag]; if (style&16) [newStyle setSymbolic: flag]; [newStyle setForceFixed: zDisplayForceFixed[zDisplayCurrentWindow]]; // Set as the current style zDisplayCurrentStyle = newStyle; return oldStyle; } static NSColor* getTrue(int col) { double r,g,b; r = ((double)(col&0x1f))/31.0; g = ((double)(col&0x3e0))/992.0; b = ((double)(col&0x7c00))/31744.0; return [NSColor colorWithDeviceRed: r green: g blue: b alpha: 1.0]; } void display_set_colour(int fore, int back) { #ifdef DEBUG NSLog(@"ZDisplay: display_set_colour(%i, %i)", fore, back); #endif zDisplayCurrentStyle = [[zDisplayCurrentStyle autorelease] copy]; if (fore == -1) fore = rc_get_foreground(); if (back == -1) back = rc_get_background(); if (fore < 16) { if (fore >= 0) { [zDisplayCurrentStyle setForegroundTrue: nil]; [zDisplayCurrentStyle setForegroundColour: fore]; } } else { [zDisplayCurrentStyle setForegroundTrue: getTrue(fore-16)]; } if (back < 16) { if (back >= 0) { [zDisplayCurrentStyle setBackgroundTrue: nil]; [zDisplayCurrentStyle setBackgroundColour: back]; } } else { [zDisplayCurrentStyle setBackgroundTrue: getTrue(back-16)]; } } void display_split(int lines, int window) { #ifdef DEBUG NSLog(@"ZDisplay: display_split(%i, %i)", lines, window); #endif [[mainMachine buffer] setWindow: (NSObject*)[mainMachine windowNumber: window] startLine: 0 endLine: lines]; } void display_join(int win1, int win2) { #ifdef DEBUG NSLog(@"ZDisplay: display_join(%i, %i)", win1, win2); #endif [[mainMachine buffer] setWindow: (NSObject*)[mainMachine windowNumber: win2] startLine: 0 endLine: 0]; } void display_set_window(int window) { #ifdef DEBUG NSLog(@"ZDisplay: display_set_window(%i)", window); #endif zDisplayCurrentWindow = window; // Set the 'force fixed' attribute appropriately ZStyle* newStyle = [zDisplayCurrentStyle copy]; [newStyle setForceFixed: zDisplayForceFixed[zDisplayCurrentWindow]]; [zDisplayCurrentStyle autorelease]; zDisplayCurrentStyle = newStyle; } int display_get_window(void) { NOTE(@"display_get_window"); return zDisplayCurrentWindow; } void display_set_cursor(int x, int y) { #ifdef DEBUG NSLog(@"ZDisplay: display_set_cursor(%i, %i)", x, y); #endif if (zDisplayCurrentWindow > 0) { [[mainMachine buffer] moveTo: NSMakePoint(x,y) inWindow: (NSObject*)[mainMachine windowNumber: zDisplayCurrentWindow]]; } } int display_get_cur_x(void) { NOTE(@"display_get_cur_x"); if (zDisplayCurrentWindow == 0) { NSLog(@"Get_cur_x called for lower window"); return -1; // No cursor position for the lower window } [mainMachine flushBuffers]; NSPoint pos = [(NSObject*)[mainMachine windowNumber: zDisplayCurrentWindow] cursorPosition]; return pos.x; } int display_get_cur_y(void) { NOTE(@"display_get_cur_y"); if (zDisplayCurrentWindow == 0) { NSLog(@"Get_cur_y called for lower window"); return -1; // No cursor position for the lower window } [mainMachine flushBuffers]; NSPoint pos = [(NSObject*)[mainMachine windowNumber: zDisplayCurrentWindow] cursorPosition]; return pos.y; } void display_force_fixed (int window, int val) { if (window >= 0 && window <= 8) zDisplayForceFixed[window] = (val!=0); NOTE(@"display_force_fixed"); } BOOL zdisplay_is_fixed(int window) { if (window >= 0 && window <= 8) return zDisplayForceFixed[window]; else return NO; } void display_terminating (unsigned char* table) { NOTE(@"display_terminating"); if (table == NULL) { [[mainMachine display] setTerminatingCharacters: nil]; return; } // Create a set of characters NSMutableSet* term = [NSMutableSet set]; int x, y; for (x=0; table[x] != 0; x++) { switch (table[x]) { // Arrow keys case 129: [term addObject: [NSNumber numberWithInt: NSUpArrowFunctionKey]]; break; case 130: [term addObject: [NSNumber numberWithInt: NSDownArrowFunctionKey]]; break; case 131: [term addObject: [NSNumber numberWithInt: NSLeftArrowFunctionKey]]; break; case 132: [term addObject: [NSNumber numberWithInt: NSRightArrowFunctionKey]]; break; // Function keys case 133: [term addObject: [NSNumber numberWithInt: NSF1FunctionKey]]; break; case 134: [term addObject: [NSNumber numberWithInt: NSF2FunctionKey]]; break; case 135: [term addObject: [NSNumber numberWithInt: NSF3FunctionKey]]; break; case 136: [term addObject: [NSNumber numberWithInt: NSF4FunctionKey]]; break; case 137: [term addObject: [NSNumber numberWithInt: NSF5FunctionKey]]; break; case 138: [term addObject: [NSNumber numberWithInt: NSF6FunctionKey]]; break; case 139: [term addObject: [NSNumber numberWithInt: NSF7FunctionKey]]; break; case 140: [term addObject: [NSNumber numberWithInt: NSF8FunctionKey]]; break; case 141: [term addObject: [NSNumber numberWithInt: NSF9FunctionKey]]; break; case 142: [term addObject: [NSNumber numberWithInt: NSF10FunctionKey]]; break; case 143: [term addObject: [NSNumber numberWithInt: NSF11FunctionKey]]; break; case 144: [term addObject: [NSNumber numberWithInt: NSF12FunctionKey]]; break; // Keypad not currently supported // Various click characters case 252: [term addObject: [NSNumber numberWithInt: NSF33FunctionKey]]; break; // Menu click case 253: [term addObject: [NSNumber numberWithInt: NSF35FunctionKey]]; break; // Double click case 254: [term addObject: [NSNumber numberWithInt: NSF34FunctionKey]]; break; // Single click case 255: // Same as 129-154 and 252-254 { // Deal with this by passing an alternative table unsigned char newTable[30]; int p = 0; for (y=129; y<=154; y++) newTable[p++] = y; for (y=252; y<=254; y++) newTable[p++] = y; newTable[p++] = 0; display_terminating(newTable); return; } default: if ((table[x] >= 129 && table[x] <= 154) || (table[x] >= 252 /* && table[x] <= 255 - always true*/)) { //NSLog(@"Oops, character '%i' is a valid terminating character, but isn't supported", (int)table[x]); } else { NSLog(@"Character '%i' is not a valid terminating character", (int)table[x]); } } } // Pass the table to the interpreter [[mainMachine display] setTerminatingCharacters: term]; } int display_get_mouse_x(void) { return [mainMachine mousePosX]; } int display_get_mouse_y(void) { return [mainMachine mousePosY]; } void display_set_title(const char* title) { NOTE(@"display_set_title"); NSLog(@"Function not implemented: %s %i", __FILE__, __LINE__); } void display_update(void) { NOTE(@"display_update"); [mainMachine flushBuffers]; } void display_beep(void) { [mainMachine flushBuffers]; [[mainMachine display] beep]; NOTE(@"display_beep"); } // = Getting files = static ZFileType convert_file_type(ZFile_type typein) { switch (typein) { case ZFile_save: return ZFileQuetzal; case ZFile_data: return ZFileData; case ZFile_transcript: return ZFileTranscript; case ZFile_recording: return ZFileRecording; default: return ZFileData; } } static void wait_for_file(void) { [mainMachine flushBuffers]; while (mainMachine != nil && ![mainMachine filePromptFinished]) { [mainLoop acceptInputForMode: NSDefaultRunLoopMode beforeDate: [NSDate distantFuture]]; } } ZFile* get_file_write(int* size, char* name, ZFile_type purpose) { // FIXME: fill in size NSObject* res = NULL; [mainMachine filePromptStarted]; [[mainMachine display] promptForFileToWrite: convert_file_type(purpose) defaultName: [NSString stringWithCString: name]]; wait_for_file(); res = [[mainMachine lastFile] retain]; [mainMachine clearFile]; if (res) { if (size) *size = [mainMachine lastSize]; return open_file_from_object([res autorelease]); } else { if (size) *size = -1; return NULL; } } ZFile* get_file_read(int* size, char* name, ZFile_type purpose) { NSObject* res = NULL; [mainMachine filePromptStarted]; [[mainMachine display] promptForFileToRead: convert_file_type(purpose) defaultName: [NSString stringWithCString: name]]; wait_for_file(); res = [[mainMachine lastFile] retain]; [mainMachine clearFile]; if (res) { if (size) *size = [mainMachine lastSize]; return open_file_from_object([res autorelease]); } else { if (size) *size = -1; return NULL; } }