/* * File: main-acn.c * StrongED$Mode=C */ #ifdef __riscos #define PORTVERSION "1.26 (02 Sep 1999)" /* for the info box */ /* * Copyright (c) 1997/8/9 Musus Umbra, Ben Harrison, and others * * This software may be copied and distributed for educational, research, * and not for profit purposes provided that this copyright and statement * are included in all such copies. * */ /* | Purpose: Support for Acorn RISC OS Angband (and variants based on 2.8.x) | | Author: Musus Umbra | | NB: This code is still under continuous development - if you want to use | it for your own compilation/variant, please contact me so that I can | keep you up to date and give you support :) | | Prerequisites to compiling: | | DeskLib 2.30 or later (earlier versions may be OK though) | | An ANSI C compiler (tested with Acorn's C/C++ and GCC, but should | be OK with any decent compiler) | | My binary distribution (for the templates and other bits) | | A suitable "!!Variant.h" file for the variant being compiled; | this should simply #define the appropriate symbols. | | NB: The following symbols are *required* and *must* be defined properly: | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ | | VARIANT Name of this game eg. "Angband", "Zangband", etc. | Note that this *must* match the entry in the !Variant Obey file | and also that it must only contain characters that are valid as | part of a RISC OS path variable, eg. "Yin-Yangband" is NOT okay. | | VERSION Version num (and optionally the date) eg. "2.8.3" or "2.8.3 (06 Feb 1998)" | AUTHORS For the info box eg. "Ben Harrison" | PORTERS for the info box eg. "Musus Umbra" | | VFILETYPE filetype of saved games eg. 0x118 | As of rel 1.18 this may be overridden by supplying | a 3 digit hex number on the command line, eg. | "... -T118 ..." | | RESPATH Resource path variable eg. "Angband$Path" | This should be defined as VARIANT"$Path" unless you've | altered my standard !Run file for some reason. | | RESDIR Where templates are, eg. "Angband" ($Dir is assumed) | Again, this should be defined as VARIANT unless !Run | has been modified accordingly. | | ICONNAME iconbar icon sprite name eg. "!angband" | | SOUNDPATH Path to sound samples, eg. "AngbandSound:" | Yet again, this should be defined as VARIANT unless !Run | and/or !Angsound has been modified accordingly. | | | The following symbols may be required, depending on the variant itself: | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ | | OLD_RFREE define if rfree() should return an errr instead of a vptr | | PDEADCHK should expand to an expression that is true if the | player is dead (eg. (p_ptr->is_dead) for Angband | or (!alive || dead) for Zang220). | If this isn't defined then (p_ptr->is_dead) will be | used. | | The following symbols control the (optional) file-cache: | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ | NB: Variants that don't repeatedly read any files whilst running | (eg. vanilla, sang, etc) should NOT define USE_FILECACHE, etc. as | it causes a non-negligable amount of code to be compiled in. | | NB: The file-cache functions require that some code in files.c is modified | to use the cached_* functions. This should be utterly trivial. | | NB: The returned handle from cached_fopen() is almost certainly *NOT* | a |FILE*| (although it may be if the cache cannot accomodate the file). | | Therefore, you *MUST* ensure that any file opened with cached_fopen() | is only ever accessed via cached_fgets() and cached_fclose(). | | Failure to do so will result in, ahem, unpleasantness. Extreme | unpleasantness. "Him fall down, go boom." | | This /may/ change in the near future (ie. to apply caching in a | transparent manner), so do keep a backup of files.c (and any other files | you modify). You always keep backups anyway, don't you? Don't you?! | | USE_FILECACHE if defined then some caching functions will | be compiled for use by the various get_rnd_line(), etc. | in files.c. | | This could probably also be used for files in lib/edit/ | although whether that's worth doing is debatable. | (ISTR that an ealry Kamband repeatedly read a file from | lib/edit when rolling up certain character classes). | | SMART_FILECACHE SMARRT_FILECACHE causes lines beginning with '#' (and | blank lines) to be discarded when caching files. This | should help Zangband 2.2.5+ but could cause trouble | for other variants. | If defined, this symbol causes the smart file cache | option to be on by default. | | ABBR_FILECACHE ABBR_FILECACHE causes data read into file-cache to | be compressed (using a simple set of abbreviations) | by default. This can be overridden using a command | line option. | | If this symbol is not defined then no compression | code will be compiled and the user option will be | ignored/unavailable. | | (Irony: it's only really useful on old (ie.slow) | machines on which the compression will suck speedwise). | | The following symbols control the debugging options: | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ | | FE_DEBUG_INFO If defined, some functions will be compiled to display | some info. on the state of the front-end (accessible) | from the '!' user menu. | | NB: For actual releases you should NOT define this | symbol since it causes a non-negligable amount of | code/data to be sucked in. */ /* Constants, etc. ---------------------------------------------------------*/ #include "!!Variant.h" /* Make FE_DEBUG_INFO into something we can use in boolean expressions */ #ifdef FE_DEBUG_INFO # define ALLOW_DEBUG_INFO 1 #else # define ALLOW_DEBUG_INFO 0 #endif /* Deal with any un-defined (or odd) file-caching symbols */ #ifdef USE_FILECACHE # define ALLOW_USE_FILECACHE 1 #else # define ALLOW_USE_FILECACHE 0 # undef ABBR_FILECACHE # undef SMART_FILECACHE #endif #ifdef ABBR_FILECACHE # define ALLOW_ABBR_FILECACHE 1 #else # define ALLOW_ABBR_FILECACHE 0 #endif #ifdef SMART_FILECACHE # define DEFAULT_SMART_FILECACHE 1 #else # define DEFAULT_SMART_FILECACHE 0 #endif /*--------------------------------------------------------------------------*/ /* Menu entry numbers */ #define IBAR_MENU_INFO 0 #define IBAR_MENU_SAVE 1 #define IBAR_MENU_FULLSCREEN 2 #define IBAR_MENU_GAMMA 3 #define IBAR_MENU_SOUND 4 #define IBAR_MENU_WINDOWS 5 #define IBAR_MENU_SAVECHOICES 6 #define IBAR_MENU_QUIT 7 #define TERM_MENU_INFO 0 #define TERM_MENU_SAVE 1 #define TERM_MENU_FONT 2 #define TERM_MENU_WINDOWS 3 /* Icon numbers */ #define SND_VOL_SLIDER 0 #define SND_VOL_DOWN 1 #define SND_VOL_UP 2 #define SND_ENABLE 3 #define GAMMA_ICN 0 #define GAMMA_DOWN 1 #define GAMMA_UP 2 #define SAVE_ICON 2 #define SAVE_PATH 1 #define SAVE_OK 0 #define SAVE_CANCEL 3 /* Position and size of the colours strip in the gamma window */ #define GC_XOFF 20 #define GC_YOFF -14 #define GC_WIDTH 512 #define GC_HEIGHT 72 /* Maximum and minimum allowed volume levels */ #define SOUND_VOL_MIN 16 #define SOUND_VOL_MAX 176 /*--------------------------------------------------------------------------*/ #include "angband.h" #undef rename #undef remove #include "Desklib:Event.h" #include "Desklib:EventMsg.h" #include "Desklib:Template.h" #include "Desklib:Window.h" #include "Desklib:Handler.h" #include "Desklib:Screen.h" #include "Desklib:Menu.h" #include "Desklib:Msgs.h" #include "Desklib:Icon.h" #include "Desklib:Resource.h" #include "Desklib:SWI.h" #include "Desklib:Time.h" #include "Desklib:Sound.h" #include "Desklib:KeyCodes.h" #include "Desklib:Kbd.h" #include "Desklib:GFX.h" #include "Desklib:ColourTran.h" #include "Desklib:Error.h" #include "Desklib:Coord.h" #include "Desklib:Slider.h" #include "Desklib:Hourglass.h" #include "Desklib:Save.h" #include "Desklib:Sprite.h" #include "Desklib:KernelSWIs.h" #include #include #include #include #include #include #include #include /*--------------------------------------------------------------------------*/ /* | We use the hourglass around calls to Wimp_Poll in an attempt to stop | users thinking that the game has 'hung'. | Kamband/Zangband and the Borg in particular can have quite long delays at | times. */ #define Start_Hourglass \ { if ( use_glass && !glass_on ) { glass_on=1; Hourglass_Start(50); } } #define Stop_Hourglass \ { if ( glass_on ) { glass_on=0; Hourglass_Off(); } } /*--------------------------------------------------------------------------*/ /* Types */ /*--------------------------------------------------------------------------*/ /* | A ZapRedraw block */ typedef struct { union { unsigned int value; struct { unsigned int vdu : 1; unsigned int double_height : 1; unsigned int extension : 1; unsigned int padding : 29; } bits; } r_flags; int r_minx; /* min x of redraw in pixels from LHS, incl */ int r_miny; /* min y of redraw in pixels from top, incl */ int r_maxx; /* max x of redraw in pixels from LHS, excl */ int r_maxy; /* max y of redraw in pixels from top, excl */ void *r_screen; /* DSA: address of screen to write to (0=>read) */ int r_bpl; /* DSA: bytes per raster line */ int r_bpp; /* log base 2 of bits per pixel */ int r_charw; /* width of a character in pixels */ int r_charh; /* height of a character in pixels */ void *r_caddr; /* DSA: ->character cache | VDU: ->font name */ int r_cbpl; /* DSA: #bytes/character line | VDU: x OS offset */ int r_cbpc; /* DSA: #bytes/character | VDU: y OS offset */ int r_linesp; /* line spacing (pixels) */ void *r_data; /* -> text to display */ int r_scrollx; /* see Redraw dox */ int r_scrolly; /* see Redraw dox */ void *r_palette; /* -> palette lookup table */ int r_for; /* foreground colour at start of line */ int r_bac; /* background colour at start of line */ void *r_workarea;/* -> word aligned workspace */ int r_magx; /* log2 x OS coords per pixel */ int r_magy; /* log2 y OS coords per pixel */ int r_xsize; /* width of screen in pixels */ int r_ysize; /* height of screen in pixels */ int r_mode; /* current screen mode */ } ZapRedrawBlock; /* | We cache font data using an array of 'font handles' (since there is a | known maximum no. of fonts required). | This is what a font 'handle' looks like: */ typedef struct { char *name; /* font name */ int usage; /* usage count */ int w,h; /* width, height */ int f,l; /* first and last character defined */ void *bpp_1; /* source bitmap */ void *bpp_n; /* bitmap for the current screen mode */ } ZapFont; /* | A struct to hold all the data relevant to a term window */ typedef struct { term t; /* The Term itself */ window_handle w; /* Window handle */ ZapFont *font; /* Font */ wimp_box changed_box;/* Area out of date */ struct { wimp_point pos; /* Cursor position */ BOOL visible; /* visibility flag */ } cursor; char name[12]; /* Name to give menus opened from the term */ int def_open; /* Open by default? */ wimp_box def_pos; /* default position */ wimp_point def_scroll; /* default scroll offset */ int unopened; /* Has this window not been opened yet? */ } term_data; /*--------------------------------------------------------------------------*/ /* ZapRedraw SWI numbers */ /*--------------------------------------------------------------------------*/ #define SWI_ZapRedraw_ 0x48480 #define SWI_ZapRedraw_RedrawArea (SWI_ZapRedraw_ + 0x00) #define SWI_ZapRedraw_GetPaletteEntry (SWI_ZapRedraw_ + 0x01) #define SWI_ZapRedraw_RedrawRaster (SWI_ZapRedraw_ + 0x02) #define SWI_ZapRedraw_ConvertBitmap (SWI_ZapRedraw_ + 0x03) #define SWI_ZapRedraw_PrepareDataLine (SWI_ZapRedraw_ + 0x04) #define SWI_ZapRedraw_AddCursor (SWI_ZapRedraw_ + 0x05) #define SWI_ZapRedraw_FindCharacter (SWI_ZapRedraw_ + 0x06) #define SWI_ZapRedraw_MoveBytes (SWI_ZapRedraw_ + 0x07) #define SWI_ZapRedraw_CachedCharSize (SWI_ZapRedraw_ + 0x08) #define SWI_ZapRedraw_ConvBitmapChar (SWI_ZapRedraw_ + 0x09) #define SWI_ZapRedraw_CreatePalette (SWI_ZapRedraw_ + 0x0a) #define SWI_ZapRedraw_InsertChar (SWI_ZapRedraw_ + 0x0b) #define SWI_ZapRedraw_ReadSystemChars (SWI_ZapRedraw_ + 0x0c) #define SWI_ZapRedraw_ReverseBitmaps (SWI_ZapRedraw_ + 0x0d) #define SWI_ZapRedraw_ReadVduVars (SWI_ZapRedraw_ + 0x0e) #define SWI_ZapRedraw_GetRectangle (SWI_ZapRedraw_ + 0x0f) #define SWI_ZapRedraw_AddVduBitmaps (SWI_ZapRedraw_ + 0x10) #define SWI_ZapRedraw_CacheFontChars (SWI_ZapRedraw_ + 0x11) #define SWI_ZapRedraw_SpriteSize (SWI_ZapRedraw_ + 0x12) #define SWI_ZapRedraw_RedrawWindow (SWI_ZapRedraw_ + 0x13) /* | Other SWI numbers that aren't defined in DeskLib's SWI.h: */ #define SWI_OS_ScreenMode 0x65 #define SWI_OS_DynamicArea 0x66 #define SWI_ColourTrans_ReturnColourNumber 0x40744 #define SWI_Wimp_ReportError 0x400df #define SWI_PlayIt_Volume 0x4d146 /*--------------------------------------------------------------------------* | File scope variables | *--------------------------------------------------------------------------*/ static int ftype = 0xffd; /* hack so saved games get the right type */ static int filehandle[16]; /* we keep track of open files with this */ static int openfiles = 0; /* how many files are currently open */ /* | Paths we use... */ static char resource_path[260] = ""; /* Path pointng to "!Angband.Lib." */ static char scrap_path[260] = ""; /* Path to create scrap files on */ static char choices_file[3][260] = { "","","" }; /* Choices paths (read/write, mirror, read) */ static char alarm_file[2][260] = { "","" }; /* Alarm choices paths (read/write, mirror, read) */ /* | So we can use something more meaningful later... | NB: Mirror is only meaningful for Choices and we don't | even reserve space for alarm_file[CHFILE_MIRROR]. */ #define CHFILE_WRITE 0 #define CHFILE_READ 1 #define CHFILE_MIRROR 2 /* | Other 'globals': */ static int initialised = 0; /* Used to determine whether to try to save */ static int game_in_progress = 0;/* if Quit (or core() is called), etc. */ static char a_palette[256][4]; /* a copy of the raw Angband palette */ static unsigned int palette[256]; /* palette as gamma'd bbggrrxx words */ static unsigned int zpalette[256]; /* And our version for ZapRedraw */ static double gamma = 1.0; /* assume gamma of 1.0 if unspecified */ static int enable_sound = 0; /* enable sound FX */ static int sound_volume = 127; /* Full volume */ static int force_mono = 0; /* force monochrome */ static int start_fullscreen = 0; /* start up full screen (added in 1.18) */ static int hack_flush = 0; /* Should TERM_XTRA_FLUSH wait for all keys to be released? */ static int flush_scrap = 1; /* Should any scrapfiles (incl. filecache) be deleted at exit? */ static int max_file_cache_size = 64<<10; static unsigned int vfiletype = VFILETYPE; static int allow_iclear_hack = 0; /* Allow the hideously evil Iclear workaround thing */ static int alarm_type = 0; /* is there an alarm set? */ static int alarm_h = 0, alarm_m = 0; /* alarm time (midnight) */ static char alarm_message[80] = "Time for bed!"; /* the message to give */ static int alarm_disp = 0; /* is the alarm being displayed? */ static int alarm_beep = 0; /* should be beep? */ static char *alarm_types[] = { "Off", "On (one-shot)", "On (repeating)", "On (one-shot)" }; /* A little macro to save some typing later: */ #define COLOUR_CHANGED(x) \ ( (angband_color_table[x][1]!=a_palette[x][1]) || \ (angband_color_table[x][2]!=a_palette[x][2]) || \ (angband_color_table[x][3]!=a_palette[x][3]) ) static int got_caret = 0; /* Do we own the caret? */ static int key_pressed = 0; /* 'Key has been pressed' Flag */ static int use_glass = 1; /* use the hourglass between WimpPolls? */ static int glass_on = 1; /* is the hourglass on? */ static int user_menu_active = FALSE; /* set to TRUE when the user menu is active */ /* Font system variables */ static ZapFont fonts[MAX_TERM_DATA+1]; /* The +1 is for the system font */ /* The system font is always font 0 */ #define SYSTEM_FONT (&(fonts[0])) /* Term system variables */ static term_data data[MAX_TERM_DATA]; /* One per term */ static char r_data[24*(80*5+4)+25*4]; /* buffer for ZapRedraw data */ /* Wimp variables */ static icon_handle ibar_icon; /* Iconbar icon handle */ static window_handle info_box; /* handle of the info window */ static window_handle gamma_win; /* gamma correction window */ static window_handle sound_win; /* sound options window */ static window_handle save_box; /* The savebox */ static menu_ptr ibar_menu; /* Iconbar menu */ static menu_ptr term_menu; /* Term window menu */ static menu_ptr wind_menu; /* windows (sub) menu */ static menu_ptr font_menu; /* Font (sub)menu */ static save_saveblock *saveblk = NULL; /* For the save box */ /* List of Wimp messages we want to be given */ static int message_list[] = { message_MODECHANGE, message_PALETTECHANGE, /* For the savebox */ message_MENUWARN, message_DATASAVEACK, message_PREQUIT, 0 }; static term_data *menu_term; /* term the last menu was opened for */ static ZapRedrawBlock zrb; /* a redraw block */ /* Cursor colour */ #define CURSOR_COLOUR 255 /* Cursor's Angband colour */ #define CURSOR_RGB 0x00ffff00 /* if undefined, use bbggrrxx */ static int cursor_rgb = -1; /* colour to use for cursor */ static int fullscreen_mode = 0; /* screen mode in use */ static int old_screenmode = 0; /* Mode we started out in */ static int *fullscreen_font = 0; /* font data for fullscreen use */ static int *fullscreen_base = 0; /* base address of screen */ static int fullscreen_height; /* height of the fullscreen font */ static int fullscreen_topline; /* raster offset of fullscreen */ #define KEYPRESS_QUIT 0x1cc /* f12 gets back to the desktop */ #define TERM_TOPLINE_HR 32 /* vertical pixel offset in mode 27 */ #define TERM_TOPLINE_LR 16 /* vertical pixel offset in mode 12 */ #define TIME_LINE 26 /* Line to display the clock on */ /* text to display at the bottom left of the fullscreen display */ static char *fs_quit_key_text = "Press f12 to return to the desktop"; static char *alarm_cancel_text = "(Press ^Escape to cancel the alarm)"; #ifndef PDEADCHK #define PDEADCHK \ (p_ptr->is_dead) #endif /* Debugging flags, etc. */ static int log_g_malloc = 0; /* Log calls to ralloc, etc */ static int show_sound_alloc = 0; /* Log sound mappings, etc */ static int abbr_filecache = ALLOW_ABBR_FILECACHE; /* compress text in the file-cache? */ static int abbr_tmpfile = 1; /* Cache compressed files on disc? */ static int smart_filecache = DEFAULT_SMART_FILECACHE; /* skip non-useful lines? */ static int use_filecache = ALLOW_USE_FILECACHE; /* Activate file caching? */ static int minimise_memory = 0; /* Cripple some things to save memory */ /* Forward declarations of some of the Full Screen Mode stuff */ static void enter_fullscreen_mode( void ); static void leave_fullscreen_mode( void ); static void set_keys( int claim ); /* Forwards declarations of the sound stuff */ static void initialise_sound( void ); static void play_sound( int event ); /* Forward declarations of Term hooks, etc. */ static errr Term_curs_acn( int x, int y ); static errr Term_curs_acnFS(int x, int y); static void Term_init_acn( term *t ); static errr Term_text_acn( int x, int y, int n, byte a, cptr s ); static errr Term_text_acnFS(int x, int y, int n, byte a, cptr s); static errr Term_user_acn( int n ); static errr Term_wipe_acn( int x, int y, int n ); static errr Term_wipe_acnFS(int x, int y, int n); static errr Term_xtra_acn(int n, int v); static errr Term_xtra_acnFS(int n, int v); static errr Term_xtra_acn_check( void ); static errr Term_xtra_acn_checkFS(void); static errr Term_xtra_acn_clearFS( void ); static errr Term_xtra_acn_event( void ); static errr Term_xtra_acn_eventFS(void); static errr Term_xtra_acn_react( void ); static errr Term_xtra_acn_reactFS(int force); /* Forward declarations of the memory stuff */ static void init_memory( int, int ); /* Forward declarations of the alarm stuff */ static void check_alarm( BOOL desktop ); static void trigger_alarm_desktop( void ); static void ack_alarm( void ); static void write_alarm_choices( void ); static void read_alarm_choices( void ); /* This just shows some debugging info (if enabled with FE_DEBUG_INFO) */ static void show_debug_info( void ); /* File-caching functions (if enabled at compile time) */ #ifdef USE_FILECACHE FILE *cached_fopen( char *name, char *mode ); errr cached_fclose( FILE *fch ); errr cached_fgets( FILE *fch, char *buffer, int max_len ); #endif /* | These functions act as malloc/free, but (if possible) using memory | in the 'Game' Dynamic Area created by init_memory() | We attach these functions to the ralloc_aux and rnfree_aux hooks | that z-virt.c provides. */ static vptr g_malloc( huge size ); #ifdef OLD_RFREE static errr g_free( vptr blk, huge size ); #else static vptr g_free( vptr blk, huge size ); #endif /* | These functions act as malloc/free, but (if possible) using memory | in the 'Fonts' Dynamic Area created by init_memory() */ static void *f_malloc( size_t size ); static void f_free( void *blk ); /* | These two functions perpetrate great evil to stop IClear from mucking | with the cursor keys in fullscreen mode. */ static void iclear_hack( void ); static void remove_iclear_hack( void ); /* | We use this to locate the choices file(s)... */ static char *find_choices( int write ); static char *find_choices_mirror( void ); static char *find_alarmfile( int write ); /* | This function is supplied as a wrapper to the save_player function. | | Its purpose is to change the filename that the game will be saved with | the leafname "!!PANIC!!" so that panic saves that break the savefile | won't overwrite the original savefile. | | To get this to work, you'll need to ammend files.c and change the call | to save_player in the panic save function(s) (search for "panic save") | to a call to save_player_panic_acn. You can declare a prototype for | the function if you like. */ extern int save_player_panic_acn( void ) { char *e,*l; /* Find the final / in the savefile name */ for ( l = e = savefile; *e; e++ ) if ( *e=='/' ) { l=e+1; } /* Write over the current leaf with the special panic one */ strcpy(l,"!!PANIC!!"); /* save the game */ return save_player(); } /*--------------------------------------------------------------------------*/ /* Error reporting, etc. */ /*--------------------------------------------------------------------------*/ /* Tell the user something important */ static void plog_hook( cptr str ) { Msgs_Report( 1, "err.plog", str ); } /* Tell the user something, then quit */ static void quit_hook( cptr str ) { /* str may be null */ if ( str ) Msgs_Report( 1, "err.quit", str ); exit(0); } /* Tell the user something then crash ;) */ static void core_hook( cptr str ) { Msgs_Report( 1, "err.core", str ); if ( game_in_progress && character_generated ) save_player_panic_acn(); quit(NULL); } static void debug( char *fmt, ... ) { va_list ap; char buffer[260]; va_start( ap, fmt ); vsprintf(buffer,fmt,ap); va_end(ap); plog(buffer); } /* static void oserror_handler( int sig ) { core(_kernel_last_oserror()->errmess); } */ /*--------------------------------------------------------------------------*/ /* File handling */ /*--------------------------------------------------------------------------*/ static int myFile_Open( const char *name, int mode ) { int handle; if ( SWI( 2,1, SWI_OS_Find, mode, name, /**/ &handle ) ) return 0; return handle; } static int myFile_Size( const char *name ) { int size, type; if ( SWI( 2,5, SWI_OS_File, 17, name, /**/ &type,0,0,0, &size ) ) return -2; return type ? size : -1; } static os_error *myFile_Close( const int handle ) { return SWI( 2,0, SWI_OS_Find, 0, handle ); } static os_error *myFile_Seek( const int handle, const int offset ) { return SWI( 3,0, SWI_OS_Args, 1, handle, offset ); } static int myFile_WriteBytes( const int handle, void *buf, const int n ) { int ntf; if ( SWI( 4,4, SWI_OS_GBPB, 2,handle,buf,n, /**/ NULL,NULL,NULL,&ntf ) ) return n; return ntf; } static int myFile_ReadBytes( const int handle, void *buf, const int n ) { int ntf; if ( SWI( 4,4, SWI_OS_GBPB, 4,handle,buf,n, /**/ NULL,NULL,NULL,&ntf ) ) return n; return ntf; } static os_error *myFile_SetType( const char *n, const int type ) { return SWI( 3,0, SWI_OS_File, 18, n, type ); } static int myFile_Extent( const int handle ) { int ext; if ( SWI( 2,3, SWI_OS_Args, 2, handle, /**/ NULL, NULL, &ext ) ) return -1; return ext; } /* | Determine if one file is newer than another. | | The filenames should be specified in RISC OS style. | | Returns -1 if 'a' is newer than 'b'. */ static int file_is_newer( const char *a, const char *b ) { os_error *e; struct { unsigned int msw; unsigned int lsw; } a_time; struct { unsigned int msw; unsigned int lsw; } b_time; int a_type, b_type; /* Get the datestamp of the 'a' file */ e = SWI ( 2, 4, SWI_OS_File, /* In */ 17, (int) a, /* Out */ &a_type, /* object type */ NULL, &(a_time.msw), /* Load Addr */ &(a_time.lsw) /* Exec Addr */ ); if ( e ) { core( e->errmess ); } /* Get the datestamp of the 'b' file */ e = SWI ( 2, 4, SWI_OS_File, /* In */ 17, (int) b, /* Out */ &b_type, /* object type */ NULL, &(b_time.msw), /* Load Addr */ &(b_time.lsw) /* Exec Addr */ ); if ( e ) { core( e->errmess ); } /* If 'b' doesn't exist then 'b' is OOD. */ if ( !b_type ) { return -1; } /* If 'a' doesn't exist then 'b' isn't OOD. ??? */ if ( !a_type ) { return 0; } /* Compare the timestamps (assume that the files are typed) */ if ( (a_time.msw & 0xff) >= (b_time.msw & 0xff) ) if ( (a_time.lsw) > (b_time.lsw) ) return -1; /* OOD */ return 0; /* Not OOD */ } /* | As fprintf, but outout to all files (if their handles are non zero). | NB: void type. */ static void f2printf( FILE *a, FILE *b, char *fmt, ... ) { va_list ap; char buffer[2048]; va_start(ap,fmt); vsprintf(buffer,fmt,ap); va_end(ap); if ( a ) { fprintf(a,buffer); } if ( b ) { fprintf(b,buffer); } va_end(ap); } /*--------------------------------------------------------------------------*/ /* Clean up (ie. close files, etc). */ /*--------------------------------------------------------------------------*/ static void final_acn( void ) { int i; for ( i=0; i r~c~v~f" */ } Stop_Hourglass; } /*--------------------------------------------------------------------------* | Various UNIX-like support funtions | *--------------------------------------------------------------------------*/ /* | Hack: determine whether filenames should be truncated to 10 chars or not. | | Needed since RO2 (and RO3 with Truncate configured off) will return | errors instead of automatically truncating long filenames. */ static int truncate_names( void ) { int r1,r2; /* First, check the OS version */ OS_Byte( osbyte_READOSIDENTIFIER,0x00,0xff, &r1, &r2 ); /* Assume that we need to truncate if running under RO2 */ if ( r1==0xa1 || r1==0xa2 ) return TRUE; /* Okay, so we've got RO3 (or later), so check the CMOS RAM */ OS_Byte( osbyte_READCMOSRAM,28,0, &r1, &r2 ); /* Bit 0 of byte 28 is the Truncate flag */ return ! (r2 & 1); } /* | The PathName translation is now done by two seperate functions: | unixify_name() and riscosify_name(). | | This is done because only the UNIX=>RISCOS translation should | ever affect the length of the leafname (ie. by truncating it to | 10 chars if necessary). | | Note that the two functions are identical but for the truncation | check so all that's really been done is that translate_name() now | takes an extra argument: 'trunc' that controls whether truncation | is applied, and riscosify and unixify just call translate_name(). */ static char *translate_name( const char *path, int trunc ) { static char buf[260]; char c,*p; /* Copy 'path' into 'buf', swapping dots and slashes */ p = buf; /* Output position */ do { c = *path++; if ( c=='/' ) c = '.'; else if ( c=='.' ) c = '/'; *p++ = c; } while (c); /* Terminator /is/ copied */ /* | When saving a game, the old game is renamed as | "SavedGame.old", the new one is saved as "SavedGame.new", | "SavedGame.old" is deleted, "SavedGame.new" is renamed | as "SavedGame". This will go wrong on a Filecore based filing | system if the saved game has a leafname > 8 chars. */ if ( (p=strstr(buf, "/old"))==NULL ) { p=strstr(buf, "/new"); } if ( !p ) { ftype=0xffd; } else { char *q=strrchr(buf, '.'); if (q) if (p-q > 6) { memmove(q+6, p, 5); } ftype=vfiletype; } /* | Hack: Do we need to truncate the leafname? */ if ( trunc ) { if ( truncate_names() ) { char *a, *b; /* | Assume that only the leafname needs attention | (this should be true for any variant) */ for ( a = b = buf ; *a; a++ ) if ( *a == '.' ) b = a+1; /* | Now b points to the start of the leafname. | If the leafname is >10 chars, write over the 10th with a | terminator. */ if ( strlen(b) >10 ) { b[10] = 0; }; } } return buf; } static char *riscosify_name( const char *path ) { return translate_name( path, TRUE ); } static char *unixify_name( const char *path ) { return translate_name( path, FALSE ); } /*--------------------------------------------------------------------------*/ /* Open a file [as fopen()] but translate the requested filename first */ FILE *my_fopen( const char *f, const char *m ) { FILE *fp; char *n = riscosify_name(f); /* translate for RO */ /* Try to open the file */ fp = fopen( n, m ); /* If it succeded and the file was opened for binary output | then set the type according to the 'ftype' hack. | NB: This will fail on some filing systems. */ if ( fp && strstr(m,"wb") ) { myFile_SetType( n, ftype ); } return fp; } /* Close a file, a la fclose() */ errr my_fclose( FILE *fp ) { /* Close the file, return 1 for an error, 0 otherwise */ return fclose(fp) ? 1 : 0; } /* Open/Create a file */ int fd_make( cptr file, int mode ) { char *real_path; int handle; /* Translate the filename into a RISCOS one */ real_path = riscosify_name( file ); /* Try to OPENOUT the file (no path, error if dir or not found) */ handle = myFile_Open( real_path, 0x8f ); /* Check for failure */ if ( !handle ) { return -1; } /* Try to set the filetype according to the ftype hack */ myFile_SetType( real_path, ftype ); /* We keep track of up to 16 open files at any given time */ if ( openfiles<16 ) filehandle[openfiles++] = handle; return handle; } /* Delete a file [as remove()] */ errr fd_kill( cptr file ) { return remove(riscosify_name(file)) ? 1 : 0; } /* Rename a file [as rename()] */ errr fd_move( cptr old, cptr new ) { char new_[260]; strcpy(new_,riscosify_name(new)); return rename( riscosify_name(old), new_ ) ? 1 : 0; } /* Open a file */ int fd_open( cptr path, int flags ) { int handle = 0; char *real_path = riscosify_name( path ); switch ( flags & 0x0f ) { case O_RDONLY : /* Read only */ handle = myFile_Open( real_path, 0x4f ); break; case O_WRONLY : /* Write only */ case O_RDWR : /* Read/Write */ handle = myFile_Open( real_path, 0xcf ); } /* Check for failure */ if ( !handle ) { return -1; } /* Keep track of upto 16 open files... */ if ( openfiles<16 ) filehandle[openfiles++] = handle; return handle; } /* Close a file opened with fd_make or fd_open */ errr fd_close( int handle ) { int i; if ( handle<=0 ) { return -1; } /* Illegal handle */ /* Try to close the file */ if ( myFile_Close(handle) ) { return 1; } /* Mark the file as closed in our array of file handles */ openfiles--; /* Find the entry in the array (if it exists) */ for ( i = 0; i<16 ; i++ ) if ( filehandle[i] == handle ) { break; } /* Shuffle the remaining entries down */ for ( ; ibpp_1 ) { f_free(sys->bpp_1); sys->bpp_1=0; } sys->bpp_1 = f_malloc( 8*256 ); /* 2K */ if ( !sys->bpp_1 ) { return 0; } /* Mung so that undefined characters show up as inverted ?s */ work_area[3] = '?'; SWI( 2,0, SWI_OS_Word, 10, work_area+3 ); for ( i=4; i<12; i++ ) work_area[i] ^= 255; /* invert colours */ SWI( 4,0, SWI_ZapRedraw_ReverseBitmaps, 0, work_area+4, work_area+4, 8 ); for ( i=0; i<0x20; i++ ) memcpy(((char*)sys->bpp_1)+i*8,work_area+4,8); /* Read the system font */ zrb.r_workarea = work_area; SWI( 2,0, SWI_ZapRedraw_ReadSystemChars, sys->bpp_1, &zrb ); /* Set up some little bits of info */ sys->name = ""; sys->w = sys->h = 8; sys->f = 0; sys->l = 255; return 1; } /* | Prepare the font system */ static void initialise_fonts( void ) { /* Initialise the array */ memset(fonts,0,sizeof(fonts)); /* Clear to zeroes */ /* Cache the system font */ cache_system_font(); fonts[0].usage = 0; /* No users */ } /* | Find a font (by name) in the array. | Returns 0 if the font isn't loaded, or a ZapFont* for it if it is. */ static ZapFont *find_font_by_name( char *name ) { int i; for ( i=0; i<=MAX_TERM_DATA; i++ ) if ( fonts[i].name ) if ( !strcmp(fonts[i].name,name) ) return &(fonts[i]); return NULL; } /* | Find a free slot in the fonts array */ static ZapFont *find_free_font( void ) { int i; for ( i=1; i<=MAX_TERM_DATA; i++ ) if ( !fonts[i].name ) { return &(fonts[i]); } return NULL; } /* | Load a font from disc and set up the header info, etc. | NB: doesn't cache the nbpp data, just the 1bpp data. | (Sets usage to 1) | Returns NULL if failed. */ static ZapFont *load_font( char *name, ZapFont *f ) { int handle,extent; char path[260]; struct { char id[8]; int w,h,f,l,r1,r2; } header; char *font_path; char *t; char *real_name = name; /* need to preserve this */ /* | 1.10 - the first element of the name determines the path to load | the font from. */ /* The font paths start $ */ t = path + sprintf(path,"%s$",VARIANT); /* Copy the path specifier and move 'name' past it */ for ( ; *name!='.' ; *t++=*name++ ) ; name++; /* name now points to the font name-proper */ /* Append the end of the path name */ strcpy(t,"$FontPath"); /* Get the path setting */ font_path = getenv( path ); if ( !font_path || !*font_path ) strcpy(path,"null:$."); else { strcpy(path,font_path); for ( t=path; *t>' '; t++ ) ; if ( t[-1]!='.' && t[-1]!=':' ) { *t++='.'; } *t=0; } strcat(path,name); /* Open the file */ handle = myFile_Open( path, 0x4f ); if ( !handle ) { return NULL; } /* Read the header */ if ( myFile_ReadBytes( handle, &header, sizeof(header) ) ) { myFile_Close(handle); return NULL; } /* Check that it's a zapfont */ if ( strncmp(header.id,"ZapFont\r",8) ) { myFile_Close(handle); return NULL; } /* Calculate the size of the 1bpp data */ extent = myFile_Extent( handle ) - sizeof(header); /* Allocate the storage for the 1bpp data */ f->bpp_1 = f_malloc( extent ); if ( !f->bpp_1 ) { myFile_Close(handle); return NULL; } /* Load the 1bpp data */ if ( myFile_ReadBytes( handle, f->bpp_1, extent ) ) { f_free(f->bpp_1); f->bpp_1=0; myFile_Close(handle); return NULL; } /* Close the file and set the header, etc. */ myFile_Close( handle ); f->name = f_malloc(strlen(real_name)+1); if ( !f->name ) { f_free(f->bpp_1); f->bpp_1=0; return NULL; } strcpy(f->name,real_name); f->w = header.w; f->h = header.h; f->f = header.f; f->l = header.l; f->usage = 1; return f; } /* | Cache a font at a suitable number of bpp for the current mode | Returns 0 for failure, 1 for sucess. | If the call fails then the font's bpp_n entry will be NULL. */ static int cache_font_for_mode( ZapFont *f ) { ZapRedrawBlock b; char work_area[128]; int size; if ( !f ) { return 0; } if ( !f->bpp_1 ) { return 0; } b.r_workarea = work_area; SWI( 2,0, SWI_ZapRedraw_ReadVduVars, 0, &b ); b.r_workarea = work_area; /* Paranoia */ b.r_charh = f->h; b.r_charw = f->w; SWI( 4,4, SWI_ZapRedraw_CachedCharSize, b.r_bpp, 0, f->w, f->h, NULL, NULL, &(b.r_cbpl), &(b.r_cbpc) ); size = 256 * b.r_cbpc; if ( f->bpp_n ) { f_free(f->bpp_n); f->bpp_n = NULL; } f->bpp_n = f_malloc(size); if ( !f->bpp_n ) { return 0; } b.r_workarea = work_area; /* Paranoia */ b.r_caddr = f->bpp_n; SWI( 5,0, SWI_ZapRedraw_ConvertBitmap, 0, &b, 0, 255, f->bpp_1 ); return 1; } /* | Stop using a font. | If the font's usage drops to zero then the font data is purged. */ static void lose_font( ZapFont *f ) { if ( --f->usage ) { /*debug("Losing font %s (still cached)",f->name);*/ return; } /*debug("Losing font %s (no longer in use)",f->name);*/ f_free( f->name ); f_free( f->bpp_1 ); if ( f->bpp_n ) { f_free(f->bpp_n); } memset(f,0,sizeof(ZapFont)); } /* | Get a font. */ static ZapFont *find_font( char *name ) { ZapFont *f; /* Check to see if it's already loaded */ f = find_font_by_name( name ); if ( f ) { /*debug("Find font %s (already cached)",name);*/ f->usage++; if ( f == SYSTEM_FONT ) { if ( !cache_system_font() ) core("Failed to cache system font!"); if ( !cache_font_for_mode( SYSTEM_FONT ) ) core("Failed to cache system font!"); } return f; } /* Ok, now check to see if there's a free slot for it */ f = find_free_font(); if ( !f ) { return NULL; } /* Oh dear :( */ /* Load the font */ /*debug("Find font %s (loading)",name);*/ f = load_font( name, f ); if ( f ) { if ( !cache_font_for_mode( f ) ) return NULL; return f; } return NULL; } /* | Cache the n_bpp data for all the active fonts (including system) */ static void cache_fonts( void ) { int i; for ( i=0; i<=MAX_TERM_DATA; i++ ) if ( fonts[i].name ) if ( !cache_font_for_mode( &(fonts[i]) ) ) core("Failed to (re)cache font tables"); } typedef struct { int load, exec, size, attr, type; char name[4]; /* Actual size is unknown */ } osgbpb10_block; static char *leafname( char *path ) { char *s = path+strlen(path); while ( --s > path ) if ( *s=='.' || *s==':' ) { return s+1; } return path; } /* | NB: This function is recursive. */ static menu_ptr make_zfont_menu( char *dir ) { int entries,entry; int read,offset; int max_width; menu_ptr m; menu_item *mi; char *temp; osgbpb10_block *item_info; char buffer[1024]; /* 1Kb buffer */ /* Count the entries in the directory */ entries = read = offset = 0; while ( offset != -1 ) { if ( SWI( 7,5, SWI_OS_GBPB,10, dir, buffer, 77, offset, 1024, 0, NULL, NULL, NULL, &read, &offset ) ) { offset = -1; read = 0; } entries += read; } if ( !entries ) { return NULL; } /* Allocate a big enough area of storage for the number of entries */ m = f_malloc( sizeof(menu_block)+entries*sizeof(menu_item) ); if ( !m ) { return NULL; } memset( m, 0, sizeof(menu_block)+entries*sizeof(menu_item) ); /* Set up the menu header */ strncpy(m->title,leafname(dir),12); m->titlefore=7; m->titleback=2; m->workfore=7; m->workback=0; m->height=44; m->gap=0; mi = (menu_item*) (((int)m)+sizeof(menu_block)); max_width = strlen(m->title); entry = 0; /* Read the entries */ read = offset = 0; while ( offset != -1 ) { if ( SWI( 7,5, SWI_OS_GBPB,10, dir, buffer, 77, offset, 1024, 0, NULL, NULL, NULL, &read, &offset ) ) { offset = -1; read = 0; /*free(m);return NULL;*/ } item_info = (osgbpb10_block*)buffer; /* Create a menu item for each entry read (if it fits) */ while ( read-- > 0 ) { switch ( item_info->type ) { case 1 : /* File */ if ( (item_info->load & 0xffffff00) == 0xfffffd00 ) { /* Data file */ mi[entry].submenu.value = -1; mi[entry].iconflags.data.text = 1; mi[entry].iconflags.data.filled = 1; mi[entry].iconflags.data.foreground = 7; mi[entry].iconflags.data.background = 0; strncpy(mi[entry].icondata.text,item_info->name,12); if ( strlen(mi[entry].icondata.text)>max_width ) max_width = strlen(mi[entry].icondata.text); entry++; } break; case 2 : /* Directory */ case 3 : /* Image */ { menu_ptr sub; char new_path[260]; if ( strchr(":.",dir[strlen(dir)-1]) ) sprintf(new_path,"%s%s",dir,item_info->name); else sprintf(new_path,"%s.%s",dir,item_info->name); sub = make_zfont_menu( new_path ); if ( sub ) { /* Add the submenu */ mi[entry].submenu.menu = sub; mi[entry].iconflags.data.text = 1; mi[entry].iconflags.data.filled = 1; mi[entry].iconflags.data.foreground = 7; mi[entry].iconflags.data.background = 0; strncpy(mi[entry].icondata.text,item_info->name,12); if ( strlen(mi[entry].icondata.text)>max_width ) max_width = strlen(mi[entry].icondata.text); entry++; } } break; } temp = ((char*) item_info)+20; while ( *temp++ ) ; item_info = (osgbpb10_block*) ((((int)temp)+3) & ~3); } } if ( entry ) { m->width = (max_width+2)*16; mi[entry-1].menuflags.data.last = 1; /* | We could possibly realloc() the storage to fit the | actual no. of entries read, but this is probably more | trouble than it's worth. */ } else { /* | No point in returning an empty menu. */ f_free(m); m = NULL; } return m; } /*--------------------------------------------------------------------------*/ /* | Initialise the palette stuff */ static void initialise_palette( void ) { memset( a_palette, 0, sizeof(a_palette) ); memset( palette, 0, sizeof(palette) ); memset( zpalette, 0, sizeof(zpalette) ); } /* | Cache the ZapRedraw palette */ static void cache_palette( void ) { static ZapRedrawBlock b; char workspace[128]; int i; static double old_gamma = -1.0; /* Idiocy check: */ if ( gamma < 0.01 ) { plog("Internal error: Attempt to apply zero gamma - recovering..."); gamma = 1.00; } if ( gamma != old_gamma ) { memset(a_palette,0,sizeof(a_palette)); old_gamma = gamma; } /* Go through the palette updating any changed values */ for ( i=0; i<256; i++ ) { if ( COLOUR_CHANGED(i) ) { int r,g,b; r = (int) (255.0*pow(angband_color_table[i][1]/255.0, 1.0/gamma)); g = (int) (255.0*pow(angband_color_table[i][2]/255.0, 1.0/gamma)); b = (int) (255.0*pow(angband_color_table[i][3]/255.0, 1.0/gamma)); palette[i] = (b<<24) | (g<<16) | (r<<8); a_palette[i][1] = angband_color_table[i][1]; a_palette[i][2] = angband_color_table[i][2]; a_palette[i][3] = angband_color_table[i][3]; } } cursor_rgb = palette[CURSOR_COLOUR]; /* Cache the ZapRedraw palette for it */ b.r_workarea = workspace; if ( b.r_mode != screen_mode ) SWI( 2,0, SWI_ZapRedraw_ReadVduVars, 0, &b ); SWI( 5,0, SWI_ZapRedraw_CreatePalette, 2, &b, palette, zpalette, 256 ); } /*--------------------------------------------------------------------------*/ /* | Functions for dealing with the SaveBox */ /* | Create the window and claim various handlers for it */ static void init_save_window( void ) { /* Create the window */ save_box = Window_Create( "save", template_TITLEMIN ); /* Set the file icon */ Icon_printf( save_box, SAVE_ICON, "file_%03x", vfiletype ); } /* | Hack: can't use Str.h without defining HAS_STRICMP. Rather than | require that the header files are altered we simply provide our | own strnicmp() function. */ static int my_strnicmp( char *a, char *b, int n ) { int i; n--; for ( i=0; i<=n; i++ ) { if ( tolower(b[i])!=tolower(a[i]) ) break; } return tolower(b[i])-tolower(a[i]); } /* | This is the handler called when a 'save' occurrs. | All it does is to update the game's own savefile setting and | then (if possible) save the character. */ static BOOL SaveHnd_FileSave( char *filename, void *ref ) { char old_savefile[1024]; /* Hack: refuse to save if the character is dead */ if ( PDEADCHK ) { Msgs_Report( 0, "err.cheat" ); return FALSE; } /* Hack: disallow saves to * */ if ( !my_strnicmp("",filename,12) ) { Msgs_Report( 0, "err.scrap" ); return FALSE; } /* Preserve the old path, in case something goes wrong... */ strcpy( old_savefile, savefile ); /* Set the new path */ strcpy( savefile, unixify_name( filename ) ); /* Try a save (if sensible) */ if ( game_in_progress && character_generated ) { if ( !save_player() ) { Msgs_Report( 0, "err.save", filename ); strcpy( savefile, old_savefile ); return FALSE; /* => failure */ } } /* Set the pathname icon */ Icon_printf( save_box, SAVE_PATH, "%s", riscosify_name(savefile) ); /* Kill the menu */ Wimp_CreateMenu( (menu_block*) -1, -1, -1 ); return TRUE; /* => Success */ } /* | Init the handlers for the savebox (eg. as a result of a menuwarning | being received for the savebox) */ static void init_savehandlers( void ) { if ( saveblk ) { Save_ReleaseSaveHandlers( saveblk ); saveblk = 0; } saveblk = Save_InitSaveWindowHandler( save_box, /* Window handle */ TRUE, /* it's part of a menu */ FALSE, /* not a window */ FALSE, /* Don't auto release the handlers */ SAVE_ICON, /* The file icon */ SAVE_OK, /* The OK icon */ SAVE_CANCEL, /* The cancel icon */ SAVE_PATH, /* The pathname icon */ SaveHnd_FileSave, /* Handler to "save the file" */ NULL, /* No RAM transfer support */ NULL, /* No 'result handler' */ 100<<10, /* Est. size (irelevant anyway) */ vfiletype, /* filetype (irelevant) */ NULL /* ref */ ); } /* | Handle a MenuWarning message for the savebox */ static BOOL Hnd_SaveWarning( event_pollblock *pb, void *ref ) { os_error *e; init_savehandlers(); /* Set the pathname */ Icon_printf( save_box, SAVE_PATH, "%s", riscosify_name(savefile) ); /* Open the submenu */ e = Wimp_CreateSubMenu( (menu_block*) save_box, pb->data.message.data.menuwarn.openpos.x, pb->data.message.data.menuwarn.openpos.y ); if ( e ) { Msgs_ReportFatal( 0, "err.swi", __LINE__, e->errmess ); } return TRUE; } /*--------------------------------------------------------------------------*/ /* | Initialise the r_data array | Mainly we just set up the line offset pointers and make sure that the | lines themselves are 'safe' by writing end-of-line codes to them. */ static void initialise_r_data( void ) { int *lo = (int*) r_data; char *ld; int j; for ( j=0; j<24; j++ ) { lo[j] = 25*4 + (80*5+4)*j; /* Offset of line */ ld = r_data+lo[j]; *ld++ = 0; /* 0,2 == */ *ld = 2; /* end of line */ } lo[j] = 0; /* Terminate line index */ } /* | Create the r_data array for a term | This is typically quite fast (1ms or so on a RPC700) | so we don't bother caching r_data for each term or using the | 'frosh' concept. */ static void make_r_data( term_data *t ) { char **c = t->t.old->c; /* char array [24][80] */ byte **a = t->t.old->a; /* attr array [24][80] */ char *o; int i,j,cf; /* New code: */ o = r_data + 25*4; /* First byte of r_data after line index */ if ( force_mono ) { for ( j=0; j<24; j++ ) { /* Set up the line offset entry */ ((int*)r_data)[j] = o - r_data; for ( i=0; i<80; i++ ) *o++ = a[j][i]!=TERM_DARK ? c[j][i] : ' '; /* 0,2 => end of line */ *o++ = 0; *o++ = 2; } } else { for ( j=0; j<24; j++ ) { /* Set up the line offset entry */ ((int*)r_data)[j] = o - r_data; /* Each line starts in white */ cf = TERM_WHITE; for ( i=0; i<80; i++ ) { if ( a[j][i] != cf ) { /* 0,6 => change FG */ *o++ = 0; *o++ = 6; cf = *o++ = a[j][i]; } *o++ = c[j][i]; } /* 0,2 => end of line */ *o++ = 0; *o++ = 2; } } } /* | Set up 'zrb' for the current screen mode. */ static void set_up_zrb_for_mode( void ) { static char work_area[4096]; zrb.r_workarea = work_area; zrb.r_palette = zpalette; zrb.r_linesp = 0; zrb.r_for = TERM_WHITE; zrb.r_bac = 0; zrb.r_data = r_data; SWI( 2,0, SWI_ZapRedraw_ReadVduVars, 0, &zrb ); } /* | Set up the ZapRedrawBlock ready to redraw term 't' | (caches the r_data as part of the process) */ static void set_up_zrb( term_data *t ) { int fw,fh; zrb.r_flags.value = 0; /* Set font info up */ fw = t->font->w; fh = t->font->h; SWI( 4,4, SWI_ZapRedraw_CachedCharSize, zrb.r_bpp, 0, fw, fh, NULL, NULL, &(zrb.r_cbpl), &(zrb.r_cbpc) ); zrb.r_caddr = (void*) (((int)t->font->bpp_n) -(t->font->f * zrb.r_cbpc)); zrb.r_charw = fw; /* Character size in pixels */ zrb.r_charh = fh; if ( t->font == SYSTEM_FONT ) zrb.r_flags.bits.double_height = screen_eig.y==1; else zrb.r_flags.bits.double_height = 0; make_r_data( t ); /* Cache the r_data */ } static void redraw_window( window_redrawblock *rb, BOOL *more, term_data *t) { int cx,cy,cw,ch; /* set GCOL for cursor colour */ if ( t->cursor.visible ) { cw = zrb.r_charw<cursor.pos.x * cw; cy = t->cursor.pos.y * ch; cx += ( rb->rect.min.x - rb->scroll.x ); cy += ( rb->rect.max.y - rb->scroll.y ); cw -= (1<cursor.visible ) { ColourTrans_SetGCOL( cursor_rgb, 0, 0 ); GFX_Move( cx,cy ); GFX_DrawBy( cw, 0 ); GFX_DrawBy( 0, ch ); GFX_DrawBy( -cw, 0 ); GFX_DrawBy( 0, -ch ); } Wimp_GetRectangle(rb,more); } } static BOOL Hnd_Redraw( event_pollblock *pb, void *ref ) { term_data *t = (term_data *) ref; window_redrawblock rb; BOOL more; rb.window = t->w; Wimp_RedrawWindow( &rb, &more ); set_up_zrb( t ); redraw_window( &rb, &more, t ); return TRUE; } static void refresh_window( term_data *t ) { window_redrawblock rb; BOOL more; int fw, fh; if ( (t->changed_box.min.x >= t->changed_box.max.x) || (t->changed_box.min.y >= t->changed_box.max.y) ) return; set_up_zrb( t ); fw = zrb.r_charw<w; rb.rect.min.x = fw * t->changed_box.min.x; rb.rect.max.x = fw * t->changed_box.max.x; rb.rect.max.y = fh * t->changed_box.min.y; rb.rect.min.y = fh * t->changed_box.max.y; Wimp_UpdateWindow( &rb, &more ); redraw_window( &rb, &more, t ); t->changed_box.min.x = t->changed_box.min.y = 255; t->changed_box.max.x = t->changed_box.max.y = 0; } static void refresh_windows( void ) { int i; window_info info; for ( i=0; iw, fw, fh ); } static BOOL Hnd_Caret( event_pollblock *pb, void *ref ) { if ( ref ) got_caret = 1; else got_caret = 0; return TRUE; } /* | Attach a (named) font to the specified term. | If 'font' is NULL then the system font is attached. | The bpp_n data is calculated if necessary | returns: | 1 => the font was attached OK | 0 => the system font was substituted */ static int attach_font_to_term( term_data *t, char *font ) { if ( t->font != SYSTEM_FONT ) { lose_font(t->font); } if ( font ) { t->font = find_font( font ); } if ( !t->font ) { t->font=SYSTEM_FONT; if ( font ) { Msgs_Report( 1,"err.font_l",font); } } else { if ( !t->font->bpp_n ) { lose_font(t->font); t->font=SYSTEM_FONT; if ( font ) { Msgs_Report( 1,"err.font_c",font); } } } resize_term_for_font( t ); return !(t->font==SYSTEM_FONT); } /*--------------------------------------------------------------------------*/ /* | Create a menu of all the (probable!) fonts in the specified location | NB: Any file of type 'data' is considered a font. | | Subdirectories are recursively searched. | | 1.10 - Uses $FontPaths to get a (space seperated) list of paths | to search. For each path name, the menu text will be the name and the | path searched will be $$FontPath | | Eg. (for angband): | Angband$FontPaths Zap Angband | Angband$Zap$FontPath ZapFonts: | Angband$Angband$FontPath Angband:xtra.fonts. */ static void make_font_menu( void ) { char *t,buffer[260]; char menu_buffer[260]; int paths; int i,max_width; char *path[64]; /* pointers to path names */ menu_item *mi; font_menu = NULL; /* Get the path (ie. dir) to look under */ t = getenv( VARIANT "$FontPaths" ); /* Hack: cope if the path isn't set */ if ( !t ) { t = ""; } strcpy(buffer,t); /* | Count how many paths there are, build an array of pointers to them | and terminate them in the buffer */ paths = 1; /* including the system font fake path '' */ for ( t=buffer; *t; t++ ) { if ( *t==' ' ) { *t = 0; } else { if (t==buffer || !t[-1]) { path[paths] = t; paths++; } } } /* | Create the menu */ path[0] = SYSTEM_FONT->name; font_menu = f_malloc(sizeof(menu_block)+paths*sizeof(menu_item)); if ( !font_menu ) { core("Out of memory (building font menu)"); } memset( font_menu, 0, sizeof(menu_block)+paths*sizeof(menu_item) ); strncpy(font_menu->title,"Fonts",12); font_menu->titlefore=7; font_menu->titleback=2; font_menu->workfore=7; font_menu->workback=0; font_menu->height=44; font_menu->gap=0; max_width = strlen(font_menu->title); mi = (menu_item*) (font_menu+1); for ( i=0; imax_width ) max_width = strlen(mi[i].icondata.text); } font_menu->width = (max_width+2)*16; mi[i-1].menuflags.data.last = 1; /* | Hack: add a dotted line after the system font entry if appropriate */ if ( paths>1 ) { mi[0].menuflags.data.dotted = 1; } /* | Iterate over the paths building the appropriate submenus */ for ( i=1; i' '; t++ ) ; if ( t[-1]=='.' ) { t--; } *t=0; /* Build the menu. Don't bother if the path variable was empty */ if ( *menu_buffer ) sub_menu = make_zfont_menu( menu_buffer ); if ( !sub_menu ) { mi[i].iconflags.data.shaded = 1; } else { mi[i].submenu.menu = sub_menu; /* Override the title of the 'root' sub-menu */ strncpy(sub_menu->title,path[i],12); /* Add the submenu to the main menu */ } } } /* | Create and set up the Info box */ static void create_info_box( void ) { info_box = Window_Create("info",template_TITLEMIN); Icon_printf(info_box,0,"%s %s",VARIANT,VERSION); Icon_SetText(info_box,2,AUTHORS); Icon_SetText(info_box,3,PORTERS); Icon_SetText(info_box,7,PORTVERSION); } /* | Create the various menus */ static void init_menus( void ) { char buffer1[256]; char buffer2[32]; char *o; create_info_box(); /* For the Info> entries */ make_font_menu(); /* Make the fonts menu */ Msgs_Lookup("menu.ibar:Info|>Save As|Full screen,Gamma correction,Sound," "Windows|Save choices|Quit (& save)",buffer1,256); ibar_menu = Menu_New( VARIANT, buffer1 ); if ( !ibar_menu ) { core("Can't create Iconbar menu!"); } Msgs_Lookup("menu.term:Info|>Save As|Font,Windows",buffer1,256); term_menu = Menu_New( VARIANT, buffer1 ); if ( !term_menu ) { core("Can't create Term menu!"); } #ifndef OLD_TERM_MENU o = buffer1; o += sprintf(buffer1,"%s|",VARIANT); o += sprintf(o,"%s,%s,%s|",angband_term_name[1],angband_term_name[2],angband_term_name[3]); sprintf(o,"%s,%s,%s,%s",angband_term_name[4],angband_term_name[5],angband_term_name[6],angband_term_name[7]); #else Msgs_printf(buffer1,"menu.windows:%s|Term-1 (Mirror),Term-2 (Recall)," "Term-3 (Choice)|Term-4,Term-5,Term-6,Term-7", VARIANT); #endif Msgs_Lookup("menu.winT:Windows",buffer2,32); wind_menu = Menu_New( buffer2, buffer1 ); if ( !wind_menu ) { core("Can't create Windows menu!"); } /* Now attach the various submenus to where they belong */ Menu_AddSubMenu( ibar_menu, IBAR_MENU_INFO, (menu_ptr) info_box ); Menu_AddSubMenu( ibar_menu, IBAR_MENU_GAMMA, (menu_ptr) gamma_win ); Menu_AddSubMenu( ibar_menu, IBAR_MENU_SOUND, (menu_ptr) sound_win ); Menu_AddSubMenu( ibar_menu, IBAR_MENU_WINDOWS, (menu_ptr) wind_menu ); Menu_AddSubMenu( term_menu, TERM_MENU_INFO, (menu_ptr) info_box ); Menu_AddSubMenu( term_menu, TERM_MENU_WINDOWS, wind_menu ); /* Add the savebox */ Menu_Warn( ibar_menu, IBAR_MENU_SAVE, TRUE, Hnd_SaveWarning, NULL ); Menu_Warn( term_menu, TERM_MENU_SAVE, TRUE, Hnd_SaveWarning, NULL ); if ( font_menu ) /* add the submenu */ Menu_AddSubMenu( term_menu, TERM_MENU_FONT, font_menu ); else /* If the font menu is buggered, shade its entry */ /* unticked, shaded */ Menu_SetFlags( term_menu, TERM_MENU_FONT, FALSE, TRUE ); } static void grab_caret( void ) { caret_block cb; cb.window = data[0].w; cb.icon = -1; cb.height = 1<<25; /* Invisible */ Wimp_SetCaretPosition( &cb ); } /* | (Recursively) clear all ticks from the specified menu */ static void clear_all_menu_ticks( menu_ptr mp ) { menu_item *mi = (menu_item*) (mp+1); do { if ( mi->menuflags.data.ticked ) mi->menuflags.data.ticked=0; if ( mi->submenu.value != -1 ) clear_all_menu_ticks( mi->submenu.menu ); mi++; } while ( mi[-1].menuflags.data.last!=1 ); } /* | Set the font menu's ticks to match the specifed font name. | | fm is the (sub) menu to scan down (recursing into it's submenus (if any)) | fn is the font name to match | prefix is the menu text to be prepended to the menu entries due to | previous menus (eg. "08x16" will cause fn="08x16.fred" to match menu | entry "fred". | | NB: recursive. | */ static void set_font_menu_ticks( menu_ptr fm, char *fn, char *prefix ) { char buffer[260]; char *b_leaf; /* -> menu 'leaf' text in buffer */ int pl; /* prefix string length */ menu_item *mi = (menu_item*) (fm+1); strcpy(buffer,prefix); pl = strlen(buffer); b_leaf = buffer + pl; do { /* Check for (substring) match */ strncpy(b_leaf,mi->icondata.text,12); /* Is it a sub-menu? */ if ( mi->submenu.value == -1 ) { /* No - must be an exact match */ mi->menuflags.data.ticked = !strcmp(buffer,fn); } else { /* Yes - must be a partial match (with a dot on :) */ strcat(b_leaf,"."); mi->menuflags.data.ticked = !strncmp(buffer,fn,pl+strlen(b_leaf)); if ( mi->menuflags.data.ticked ) set_font_menu_ticks( mi->submenu.menu, fn, buffer ); else clear_all_menu_ticks( mi->submenu.menu ); } /* Next item */ mi++; } while (mi[-1].menuflags.data.last != 1 ); /* Until finished */ } /* | Set ticks, etc. in the term_menu to reflect the current state of the | term 't' */ static void set_up_term_menu( term_data *t ) { int i; menu_ptr mp; menu_item *mi; window_state ws; /* First of all, set up menu title to be the term's title */ strncpy(term_menu->title,t->name,12); /* Now set the ticks in the Windows> submenu (cuz it's easy) */ mp = wind_menu; /* Windows submenu */ mi = (menu_item*) (mp+1); /* First entry */ for ( i=0; ifont->name, "" ); /* Shade the 'Save>' entry if saving isn't possible (yet) */ if ( game_in_progress && character_generated) Menu_SetFlags( term_menu, TERM_MENU_SAVE, 0, PDEADCHK ); else Menu_SetFlags( term_menu, TERM_MENU_SAVE, 0, TRUE ); } /* | Generic 'click' handler for windows - turns a drag on the window | into a move-window style drag. */ static BOOL Hnd_Click( event_pollblock *pb, void *ref ) { if ( pb->data.mouse.button.data.dragselect || pb->data.mouse.button.data.dragadjust) { drag_block b; b.window = pb->data.mouse.window; b.screenrect.min.x = b.screenrect.min.y = 0; b.screenrect.max.x = screen_size.x; b.screenrect.max.y = screen_size.y; b.type = drag_MOVEWINDOW; Wimp_DragBox( &b ); } return TRUE; } /* | Handle a click on a Term window. */ static BOOL Hnd_TermClick( event_pollblock *pb, void *ref ) { term_data *t = (term_data*) ref; if ( pb->data.mouse.button.data.menu ) { menu_term = t; set_up_term_menu( t ); Menu_Show( term_menu,pb->data.mouse.pos.x-32,pb->data.mouse.pos.y+32 ); } else { grab_caret(); Hnd_Click(pb,ref); } return TRUE; } static void mark_ood( term_data *t, int minx, int miny, int maxx, int maxy ) { if ( t->changed_box.min.x > minx ) t->changed_box.min.x = minx; if ( t->changed_box.max.x < maxx ) t->changed_box.max.x = maxx; if ( t->changed_box.min.y > miny ) t->changed_box.min.y = miny; if ( t->changed_box.max.y < maxy ) t->changed_box.max.y = maxy; } /* Check for an event (ie. key press) */ static errr Term_xtra_acn_check( void ) { static int last_poll = 0; int curr_time; int bh,bl; /* | Only poll the wimp if there's something in the keyboard buffer | or every 10cs */ /* Check the kbd buffer */ SWI( 3,3, SWI_OS_Byte, 128, 255, 0, /**/ NULL, &bl, &bh ); bl = (bl & 0xff) + (bh<<8); /* Check how long it is since we last polled */ curr_time = Time_Monotonic(); if ( (bl>0 && got_caret) || ( (curr_time-last_poll)>9 ) ) { event_pollmask old_mask = event_mask; last_poll = curr_time; event_mask.data.null = 0; Stop_Hourglass; Event_Poll(); Start_Hourglass; event_mask = old_mask; } /* | This allows the user to interrupt the borg. */ if ( key_pressed ) return key_pressed = 0; return 1; } /* | Wait for an event (ie. keypress) | Note that we idle poll once a second to allow us to implement the | alarm system. */ static errr Term_xtra_acn_event( void ) { event_pollmask old_mask = event_mask; event_mask.data.null = 0; Stop_Hourglass; while ( !key_pressed && !fullscreen_font ) { int curr_time = Time_Monotonic(); Wimp_PollIdle( event_mask, &event_lastevent, curr_time+100 ); Event_Process( &event_lastevent ); check_alarm(TRUE); } Start_Hourglass; event_mask = old_mask; return key_pressed = 0; } /* React to changes (eg. palette change) */ static errr Term_xtra_acn_react( void ) { int c; cache_palette(); /* Mark the entirety of each window as out of date */ for ( c=0; chgt; i++ ) t->froshed[i] = 1;*/ mark_ood( t, 0,0, Term->wid,Term->hgt ); /*refresh_window( t ); - NB: Term isn't actually cleared yet! */ /* Success */ return 0; case TERM_XTRA_EVENT : /* Wait/check for an event */ check_alarm(TRUE); if ( v ) return Term_xtra_acn_event(); else return Term_xtra_acn_check(); case TERM_XTRA_BORED : /* Bored */ check_alarm(TRUE); return Term_xtra_acn_check(); case TERM_XTRA_FLUSH : /* Flush input */ if ( got_caret ) { /* 1.21 - Hack: wait until no keys are pressed */ if ( hack_flush ) for ( v=0 ; v!=0xff ; ) SWI( 1,2, SWI_OS_Byte, 122, 0, &v ); SWI( 3,0, SWI_OS_Byte, 21,0,0 ); /* Flush Kbd buffer */ } return 0; case TERM_XTRA_FRESH : /* Flush output */ refresh_window( t ); return 0; case TERM_XTRA_FROSH : /* Ensure line 'v' is plotted */ /* Doesn't do anything */ return 0; case TERM_XTRA_SHAPE : /* Set cursor visibility */ t->cursor.visible = v ? TRUE : FALSE; mark_ood( t, t->cursor.pos.x,t->cursor.pos.y, t->cursor.pos.x+1,t->cursor.pos.y+1); refresh_window( t ); /* needed? */ return 0; case TERM_XTRA_NOISE : /* Make a beep */ Sound_SysBeep(); return 0; case TERM_XTRA_REACT : /* React to, eg. palette changes */ return Term_xtra_acn_react(); case TERM_XTRA_DELAY : /* Delay for 'v' ms */ if ( v>0 ) { int start = Time_Monotonic(); v=(v+5)/10; /* Round to nearest cs */ GFX_Wait(); while ( (Time_Monotonic()-start)< v ) ; } return 0; case TERM_XTRA_SOUND : /* Play a sound :) */ if ( enable_sound ) { play_sound( v ); } return 0; default : return 1; /* Unsupported */ } } /* Move (but don't necessarily display) the cursor */ static errr Term_curs_acn( int x, int y ) { term_data *t = (term_data*) Term; if ( t->cursor.visible ) mark_ood( t, t->cursor.pos.x,t->cursor.pos.y, t->cursor.pos.x+1,t->cursor.pos.y+1); t->cursor.pos.x = x; t->cursor.pos.y = y; if ( t->cursor.visible ) mark_ood( t, t->cursor.pos.x,t->cursor.pos.y, t->cursor.pos.x+1,t->cursor.pos.y+1); return 0; } /* | NB: these two are very simple since we use the Term's contents | directly to generate the r_data for ZapRedraw. */ /* Erase 'n' characters at (x,y) */ static errr Term_wipe_acn( int x, int y, int n ) { mark_ood( (term_data*) Term, x, y, x+n, y+1 ); return 0; } /* Write 'n' characters from 's' with attr 'a' at (x,y) */ static errr Term_text_acn( int x, int y, int n, byte a, cptr s ) { mark_ood( (term_data*) Term, x, y, x+n, y+1 ); return 0; } /* Initialise one of our terms */ static void Term_init_acn( term *t ) { term_data *term = (term_data*) t; /* Ludicrous changed box settings :) */ term->changed_box.min.x = 256; term->changed_box.min.y = 256; term->changed_box.max.x = 0; term->changed_box.max.y = 0; } static void term_data_link( term_data *td, int k ) { term *t = &(td->t); /* Initialise the term */ term_init( t, 80, 24, k ); /* Set flags and hooks */ t->attr_blank = TERM_WHITE; t->char_blank = ' '; /* Experiment (FS mode requires them) */ t->always_text = TRUE; t->never_frosh = TRUE; /* Experiment (FS mode requires them) */ t->init_hook = Term_init_acn; t->xtra_hook = Term_xtra_acn; t->wipe_hook = Term_wipe_acn; t->curs_hook = Term_curs_acn; t->text_hook = Term_text_acn; t->user_hook = Term_user_acn; t->data = (vptr) td; Term_activate(t); } /* Open default windows (ie. as set in choices) at the appropriate sizes */ static void show_windows( void ) { int i; for ( i=MAX_TERM_DATA; i-->0; ) { if ( !data[i].unopened ) { if ( data[i].def_open ) Window_Show( data[i].w, open_WHEREVER ); } else { if ( data[i].def_open ) { window_openblock ob; ob.window = data[i].w; ob.screenrect = data[i].def_pos; ob.scroll = data[i].def_scroll; ob.behind = -1; Wimp_OpenWindow( &ob ); data[i].unopened = 0; } } } } /* | 'ref' is used to indicate whether this close is being forced by some other | part of the code (eg. the Windows> submenu code). This is used to modify | the 'adjust doesn't close other terms' behavior. */ static BOOL Hnd_MainClose( event_pollblock *pb, void *ref ) { int i; window_state ws; mouse_block mb; /* New in 1.08: don't close other Terms if closed with adjust */ Wimp_GetPointerInfo( &mb ); if ( ref || mb.button.data.adjust ) { Wimp_CloseWindow( data[0].w ); } else { /* Close all the terms, but mark the open ones as 'def_open' */ for ( i=0; idata.key.code; /* Check whether this key was pressed in Term 0 */ if ( pb->data.key.caret.window == data[0].w ) { switch ( c ) { case keycode_F12 : case keycode_SHIFT_F12 : case keycode_CTRL_F12 : case keycode_CTRL_SHIFT_F12 : /* Never intercept these */ break; case 27 : /* handle escape specially */ if ( Kbd_KeyDown( inkey_CTRL ) ) { ack_alarm(); return TRUE; } default : /* Pass to the angband engine */ /* Allow shift & ctrl to modify the keypad keys */ if ( c>='0' && c<='9' ) { kbd_modifiers m = Kbd_GetModifiers( FALSE ); if ( m.shift ) { c |= 0x800; } if ( m.ctrl ) { c |= 0x400; } /* Could maybe add ALT as 0x1000 ??? */ } /* Keys >255 have to be send as escape sequences (31=escape) */ if ( c>255 || c==31 ) { Term_keypress(31); Term_keypress( hex[(c & 0xf00)>>8] ); Term_keypress( hex[(c & 0x0f0)>>4] ); Term_keypress( hex[(c & 0x00f)] ); c = 13; } Term_keypress(c); key_pressed = 1; /*if ( c==27 ) { escape_pressed = 1; }*/ return TRUE; } } Wimp_ProcessKey( c ); return TRUE; } /*--------------------------------------------------------------------------*/ /* Gamma correction window stuff */ /*--------------------------------------------------------------------------*/ static void redraw_gamma( window_redrawblock *rb, BOOL *more ) { int i,y,x,h,w; int bx,by; int dither; bx = Coord_XToScreen( GC_XOFF, (convert_block*) &(rb->rect) ); by = Coord_YToScreen( GC_YOFF, (convert_block*) &(rb->rect) ); h = GC_HEIGHT/4; w = GC_WIDTH/16; x = bx; while ( *more ) { for ( i=0; i<16; i++ ) { y = by; for ( dither = 0; dither<2; dither++ ) { /* Solid block: */ ColourTrans_SetGCOL( palette[i], dither<<8 , 0 ); GFX_RectangleFill(x,y,w,-h); y-=h; /* Dot on black: */ ColourTrans_SetGCOL( palette[0], dither<<8, 0 ); GFX_RectangleFill(x,y,w,-h); ColourTrans_SetGCOL( palette[i], dither<<8, 0 ); GFX_RectangleFill(x+(w/2)-2,y-(h/2),2,2); y-=h; } x += w; } Wimp_GetRectangle(rb,more); } } static void update_gamma( void ) { window_redrawblock rb; BOOL more; rb.window = gamma_win; rb.rect.min.x = GC_XOFF; rb.rect.min.y = GC_YOFF - GC_HEIGHT; rb.rect.max.y = GC_XOFF+GC_WIDTH + screen_delta.x; rb.rect.max.y = GC_YOFF + screen_delta.y; Wimp_UpdateWindow( &rb, &more ); if ( more ) { redraw_gamma(&rb,&more); } } static BOOL Hnd_RedrawGamma( event_pollblock *pb, void *ref ) { window_redrawblock rb; BOOL more; rb.window = pb->data.openblock.window; Wimp_RedrawWindow( &rb, &more ); if ( more ) { redraw_gamma(&rb,&more); } return TRUE; } static BOOL Hnd_GammaClick( event_pollblock *pb, void *ref ) { int up = (ref==0); if ( up ) { if ( gamma<9.0 ) { gamma += 0.05; Icon_SetDouble( gamma_win, 0, gamma, 2 ); Term_xtra_acn_react(); update_gamma(); } } else { if ( gamma>0.05 ) { gamma -= 0.05; Icon_SetDouble( gamma_win, GAMMA_ICN, gamma, 2 ); Term_xtra_acn_react(); update_gamma(); } } /* Hack: if the user menu is active then force it to redraw */ if ( user_menu_active ) { Term_keypress(18); key_pressed = 1; } return TRUE; } /* | Reflect the current options in the gamma window */ static void set_gamma_window_state( void ) { Icon_SetDouble( gamma_win, 0, gamma, 2 ); } static void init_gamma_window( void ) { Template_UseSpriteArea( resource_sprites ); gamma_win = Window_Create( "gamma", template_TITLEMIN ); Template_UseSpriteArea( NULL ); Event_Claim( event_REDRAW, gamma_win, event_ANY, Hnd_RedrawGamma, 0 ); Event_Claim( event_CLICK, gamma_win, GAMMA_DOWN, Hnd_GammaClick, (void*)1 ); Event_Claim( event_CLICK, gamma_win, GAMMA_UP, Hnd_GammaClick, (void*)0 ); set_gamma_window_state(); } /*--------------------------------------------------------------------------*/ /* Sound options window stuff */ /*--------------------------------------------------------------------------*/ static slider_info volume_slider; /* | Reflect the current sound config in the sound options window */ static void set_sound_window_state( void ) { Icon_SetSelect( sound_win, SND_ENABLE, enable_sound ); Slider_SetValue( &volume_slider, sound_volume, NULL, NULL ); if ( sound_volume >127 ) volume_slider.colour.foreground = colour_RED; else volume_slider.colour.foreground = colour_GREEN; } /* | The sound slider has been dragged, so update the sound volume setting */ static int update_volume_from_slider( void *siv, void *ref ) { slider_info *si = (slider_info*) siv; sound_volume = Slider_ReadValue( si ); if ( sound_volume >127 ) volume_slider.colour.foreground = colour_RED; else volume_slider.colour.foreground = colour_GREEN; return 0; } /* | Handle redraw events for the sound options window */ static BOOL Hnd_RedrawSnd( event_pollblock *pb, void *ref ) { window_redrawblock redraw; BOOL more; redraw.window = pb->data.openblock.window; Wimp_RedrawWindow(&redraw, &more); while (more) { Slider_Redraw( ((slider_info*)ref), &redraw.cliprect ); Wimp_GetRectangle(&redraw, &more); } return(TRUE); } /* | Handle clicks on the sound options window */ static BOOL Hnd_SndClick( event_pollblock *pb, void *ref ) { int icn = pb->data.mouse.icon; int adj = pb->data.mouse.button.data.adjust; slider_info *si = (slider_info*) ref; switch ( icn ) { /* Bump arrows for the slider: */ case SND_VOL_DOWN : adj = !adj; case SND_VOL_UP : adj = adj ? -1 : 1; sound_volume += adj; if ( sound_volumeSOUND_VOL_MAX ) { sound_volume = SOUND_VOL_MAX; } set_sound_window_state(); break; /* The slider itself */ case SND_VOL_SLIDER : Icon_ForceRedraw( sound_win, SND_VOL_SLIDER ); Slider_Drag( si, NULL, NULL, NULL ); break; /* The enable/disable icon */ case SND_ENABLE : enable_sound = !enable_sound; Icon_SetSelect( sound_win, SND_ENABLE, enable_sound ); if ( enable_sound ) { initialise_sound(); } break; } /* Hack: if the user menu is active then force it to redraw */ if ( user_menu_active ) { Term_keypress(18); key_pressed = 1; } return TRUE; } /* | Set the sound options window up, ready to rock :) */ static void init_sound_window( void ) { Template_UseSpriteArea( resource_sprites ); sound_win = Window_Create( "sound", template_TITLEMIN ); Template_UseSpriteArea( NULL ); Event_Claim( event_REDRAW, sound_win, event_ANY, Hnd_RedrawSnd, (void*) &volume_slider); Event_Claim( event_CLICK, sound_win, event_ANY, Hnd_SndClick, (void*) &volume_slider); /* Set up the slider info */ volume_slider.window = sound_win; volume_slider.icon = SND_VOL_SLIDER; volume_slider.limits.min = SOUND_VOL_MIN; volume_slider.limits.max = SOUND_VOL_MAX; volume_slider.colour.foreground = colour_GREEN; volume_slider.colour.background = colour_WHITE; volume_slider.border.x = 8; volume_slider.border.y = 8; volume_slider.update = update_volume_from_slider; set_sound_window_state(); } /*--------------------------------------------------------------------------*/ /* | A font has been selected. | At this point, menu_term is a pointer to the term for which the | menu was opened. */ static void handle_font_selection( int *s ) { char name[260]; os_error *e; char *r; menu_ptr mp = font_menu; int *mis; /* Follow the >s to the entry specified */ for ( mis = s; *mis!=-1; mis++ ) mp = ((menu_item*)(mp+1))[*mis].submenu.menu; /* | Now, check to see if we've hit a leaf entry. | NB: If the entry isn't a leaf entry then the first entry in its submenu | is used instead */ if ( ((int) mp) != -1 ) { mis[0] = 0; mis[1] = -1; mp = ((menu_item*)(mp+1))[0].submenu.menu; } if ( ((int) mp) != -1 ) return; e = Wimp_DecodeMenu( font_menu, s, name ); if ( e ) { plog(e->errmess); return; } /* Make sure that the string is NULL terminated */ for ( r=name; *r>=' '; r++ ) ; *r = 0; attach_font_to_term( menu_term, name ); mark_ood( menu_term, 0,0, 80,24 ); refresh_window( menu_term ); } static void load_choices( void ) { FILE *fp = NULL; char *cf; int i; char buffer[260]; cf = find_choices( FALSE ); if ( *cf ) fp = fopen( cf, "r" ); /* Implement default choices */ data[0].def_open = 1; data[0].unopened = 1; /* ie. force def_pos */ data[0].def_pos.min.x = (screen_size.x - 1280)/2; data[0].def_pos.max.x = (screen_size.x + 1280)/2; data[0].def_pos.min.y = (screen_size.y - 768)/2 -32; data[0].def_pos.max.y = (screen_size.y + 768)/2 -32; data[0].def_scroll.x = data[0].def_scroll.y = 0; for ( i=1; i=0 && tname); f2printf(fp,fpm,"%d ",ws.openblock.screenrect.min.x); f2printf(fp,fpm,"%d ",ws.openblock.screenrect.min.y); f2printf(fp,fpm,"%d ",ws.openblock.screenrect.max.x); f2printf(fp,fpm,"%d ",ws.openblock.screenrect.max.y); f2printf(fp,fpm,"%d %d\n",ws.openblock.scroll.x,ws.openblock.scroll.y); } fclose(fp); if (fpm) { fclose(fpm); } } /* | Update the Alarm choices file to reflect changed alarm settings. */ static void write_alarm_choices( void ) { FILE *fp; char *cf; /* Open the choices file for reading */ cf = find_alarmfile( TRUE ); if ( !*cf ) { plog( "Can't determine Alarm file location!"); return; } fp = fopen( cf, "w" ); if ( !fp ) { plog("Can't write Alarm file"); return; } /* Write the new alarm options */ fprintf(fp,"AlarmType %s\n",alarm_types[alarm_type] ); fprintf(fp,"AlarmTimeH %d\n",alarm_h); fprintf(fp,"AlarmTimeM %d\n",alarm_m); fprintf(fp,"AlarmText %s\n",alarm_message); fprintf(fp,"AlarmBeep %s\n",alarm_beep ? "on" : "off"); fclose(fp); } /* | Read the Alarm choices file. */ static void read_alarm_choices( void ) { char buffer[260]; FILE *fp; char *cf; cf = find_alarmfile( FALSE ); if ( !*cf ) { return; } fp = fopen( cf, "r" ); if ( fp ) { char *t_,*o_; /* Load choices */ while ( fgets(buffer,260,fp) ) { t_ = strtok(buffer," "); /* Keyword */ o_ = strtok(NULL,"\n"); /* argument string */ if ( !o_ ) { o_=""; } /* missing (or null) argument? */ if ( t_ ) { if ( !strcmp(t_,"AlarmTimeH") ) alarm_h = atoi(o_); else if ( !strcmp(t_,"AlarmTimeM") ) alarm_m = atoi(o_); else if ( !strcmp(t_,"AlarmText") ) strcpy(alarm_message,o_); else if ( !strcmp(t_,"AlarmBeep") ) alarm_beep = !strcmp(o_,"on"); else if ( !strcmp(t_,"AlarmType") ) { int i; for ( i=0; i<4; i++ ) if ( !strcmp(alarm_types[i],o_) ) alarm_type = i; } } } fclose(fp); } } /* | Handle selections from the term menu(s) */ static BOOL Hnd_TermMenu( event_pollblock *pb, void *ref ) { mouse_block mb; int i; Wimp_GetPointerInfo( &mb ); switch ( pb->data.selection[0] ) { case TERM_MENU_INFO : /* Info> */ break; case TERM_MENU_FONT : /* Font> */ /* Sub item selected? */ if ( pb->data.selection[1] == -1 ) { break; } handle_font_selection(pb->data.selection+1); break; case TERM_MENU_WINDOWS : /* Windows> */ if ( pb->data.selection[1] == -1 ) { break; } i = pb->data.selection[1]; { window_state ws; Wimp_GetWindowState(data[i].w,&ws); if ( ws.flags.data.open ) { if ( !i ) Hnd_MainClose( NULL, (void*) TRUE ); else Window_Hide( data[i].w ); } else { if ( !i ) { show_windows(); grab_caret(); } else { if ( !data[i].unopened ) { Window_Show( data[i].w, open_WHEREVER ); } else { window_openblock ob; ob.window = data[i].w; ob.screenrect = data[i].def_pos; ob.scroll = data[i].def_scroll; ob.behind = -1; /* could use data[0].w; ? */ Wimp_OpenWindow( &ob ); data[i].unopened = 0; } } } } break; } if ( mb.button.data.adjust ) { set_up_term_menu( menu_term ); Menu_ShowLast(); } return TRUE; } /* | Handle selections from the iconbar menu */ static BOOL Hnd_IbarMenu( event_pollblock *pb, void *ref ) { mouse_block mb; Wimp_GetPointerInfo( &mb ); switch ( pb->data.selection[0] ) { case IBAR_MENU_INFO : /* Info> */ break; case IBAR_MENU_FULLSCREEN : /* Full screen */ /* Do Full Screen mode */ enter_fullscreen_mode(); break; case IBAR_MENU_GAMMA : /* Gamma correction */ break; case IBAR_MENU_SOUND : /* Sound */ /* | enable_sound = !enable_sound; | if ( enable_sound ) { initialise_sound(); } | Menu_SetFlags( ibar_menu, IBAR_MENU_SOUND, enable_sound, 0 ); | set_sound_window_state(); */ break; case IBAR_MENU_WINDOWS : /* Windows> */ /* | Hack: pass it off as the equivalent selection from | the term menu. */ pb->data.selection[0] = TERM_MENU_WINDOWS; return Hnd_TermMenu(pb,ref); break; case IBAR_MENU_SAVECHOICES : /* Save choices */ save_choices(); break; case IBAR_MENU_QUIT : /* Quit */ if ( game_in_progress && character_generated ) save_player(); quit(NULL); break; } if ( mb.button.data.adjust ) Menu_ShowLast(); return TRUE; } static BOOL Hnd_MenuSel( event_pollblock *pb, void *ref ) { if ( menu_currentopen == ibar_menu ) return Hnd_IbarMenu(pb,ref); else if ( menu_currentopen == term_menu ) return Hnd_TermMenu(pb,ref); return FALSE; } static BOOL Hnd_IbarClick( event_pollblock *pb, void *ref ) { if ( pb->data.mouse.button.data.menu ) { set_gamma_window_state(); set_sound_window_state(); /* Hack: shade the Save> option if appropriate */ if ( game_in_progress && character_generated) Menu_SetFlags( ibar_menu, IBAR_MENU_SAVE, 0, PDEADCHK ); else Menu_SetFlags( ibar_menu, IBAR_MENU_SAVE, 0, TRUE ); /* | Hack: set up the Term menu as if it was opened over the main | window (so that the Windows> submenu is set correctly) */ menu_term = (term_data*) &data[0]; set_up_term_menu( menu_term ); Menu_Show( ibar_menu, pb->data.mouse.pos.x, -1 ); return TRUE; } if ( pb->data.mouse.button.data.select ) { show_windows(); grab_caret(); return TRUE; } if ( pb->data.mouse.button.data.adjust ) { enter_fullscreen_mode(); return TRUE; } return FALSE; } /* | Handler for PreQuit messages (eg. at shutdown). */ static BOOL Hnd_PreQuit( event_pollblock *b, void *ref ) { BOOL shutdown = (b->data.message.data.words[0] & 1) == 0; task_handle originator = b->data.message.header.sender; unsigned int quitref; message_block mb; char buffer1[64]; os_error e; int ok; if ( !(game_in_progress && character_generated) ) return TRUE; /* ignore, we're OK to die */ /* Stop the shutdown/quit */ memcpy( &mb, &(b->data.message), 24 ); quitref = mb.header.yourref; mb.header.yourref = mb.header.myref; Wimp_SendMessage( event_ACK, &mb, originator, 0 ); /* | We handle this differently depending on the version of the Wimp; | newer versions give us much more flexibility. */ if ( event_wimpversion<350 ) { /* | Older versions - use 'OK' and 'Cancel'. | There is no "Save & Quit" button. */ Msgs_Lookup( "err.shuttitl:Query from %s",e.errmess,64 ); sprintf(buffer1,e.errmess,VARIANT); Msgs_Lookup("err.shutdown:Unsaved game; are you sure you want to quit?", e.errmess,260); e.errnum = 0; SWI( 3,2, SWI_Wimp_ReportError, &e,3|16,buffer1, NULL,&ok ); if ( ok!=1 ) return TRUE; /* no! Pleeeeeease don't kill leeeeddle ol' me! */ } else { /* | Newer version: can add buttons to the dialog. | we add a 'Save and Quit' button to allow the shutdown to | continue /after/ saving. */ int flags; char buttons[64]; Msgs_Lookup( "err.shutbuts:Save & quit,Don't quit,Quit anyway", buttons,64); Msgs_Lookup( "err.shuttitl:Query from %s",e.errmess,64 ); sprintf(buffer1,e.errmess,VARIANT); Msgs_Lookup("err.shutdown:Unsaved game; are you sure you want to quit?", e.errmess,260); e.errnum = 0; flags = 0 | 16 | 256 | (4<<9); SWI( 6,2, SWI_Wimp_ReportError, &e,flags,buffer1,ICONNAME,0, buttons, NULL,&ok ); if ( ok==4 ) return TRUE; /* no! Pleeeeeease don't kill leeeeddle ol' me! */ if ( ok==3 ) save_player(); /* Save & Quit */ } /* RO2 doesn't use the shudown flag */ if ( shutdown && event_wimpversion>=300 && mb.header.size>=24 ) { key_block kb; kb.code = 0x1fc; /* restart shutdown sequence */ Wimp_SendMessage( event_KEY, (message_block*) &kb, originator, 0 ); } /* "Time... to die." */ Event_CloseDown(); exit(0); return TRUE; /* The one great certainty (sic) */ } static void initialise_terms( void ) { char t[80]; int i; /* Create a window for each term. Term 0 is special (no scroll bars) */ data[0].w = Window_Create( "angband", template_TITLEMIN ); data[0].font = SYSTEM_FONT; data[0].def_open = 1; data[0].unopened = 1; sprintf(t,"%s %s",VARIANT,VERSION); Window_SetTitle( data[0].w, t ); strncpy(data[0].name,VARIANT,12); Event_Claim( event_KEY, data[0].w, event_ANY, Hnd_Keypress, (void*) &(data[0]) ); Event_Claim( event_REDRAW, data[0].w, event_ANY, Hnd_Redraw, (void*) &(data[0]) ); Event_Claim( event_CLICK, data[0].w, event_ANY, Hnd_TermClick, (void*) &(data[0]) ); Event_Claim( event_CLOSE, data[0].w, event_ANY, Hnd_MainClose, NULL ); for ( i=1; i 0 ) return choices_file[CHFILE_WRITE]; if ( myFile_Size(choices_file[CHFILE_MIRROR]) > 0 ) return choices_file[CHFILE_MIRROR]; return choices_file[CHFILE_READ]; } static char *find_choices_mirror( void ) { return choices_file[CHFILE_MIRROR]; } static char *find_alarmfile( int write ) { if ( write ) return alarm_file[CHFILE_WRITE]; if ( myFile_Size(alarm_file[CHFILE_WRITE]) > 0 ) return alarm_file[CHFILE_WRITE]; return alarm_file[CHFILE_READ]; } int main( int argc, char *argv[] ) { int i,j; int start_full = 0; char *arg_savefile=0; char *t; int da_font=1, da_game=1; atexit( final_acn ); /* "I never did care about the little things." */ Start_Hourglass; /* Parse arguments */ for ( i=1; i] */ for ( j=2; argv[i][j] ; j++ ) { int on = isupper(argv[i][j]); switch ( tolower(argv[i][j]) ) { case 'a' : abbr_filecache = on && ALLOW_ABBR_FILECACHE; break; case 's' : smart_filecache = on; break; case 'f' : abbr_tmpfile = on && ALLOW_ABBR_FILECACHE; break; case 'p' : flush_scrap = !on; break; default : if ( isdigit(argv[i][j]) ) { max_file_cache_size = atoi(argv[i]+j)<<10; while ( isdigit(argv[i][++j]) ) ; if ( max_file_cache_size<= 0 ) { use_filecache = 0; } j--; } else { fprintf(stderr,"Unrecognised option: -c%s",argv[i]+j); exit(EXIT_FAILURE); } } } break; case 'w' : /* -waitrelease */ hack_flush = 1; break; case 'e' : /* -Evil */ allow_iclear_hack = 1; break; case 's' : /* -s */ if ( argv[i][2] ) arg_savefile = argv[i]+2; break; case 'f' : /* -fullscreen */ start_full = 1; break; case 'h' : /* -hourglass */ use_glass = 1; break; case 't' : /* -T */ if ( argv[i][2] ) vfiletype = htoi( argv[i]+2 ); break; case 'd' : /* -df, -dg, -dc or -d : disable DAs */ switch ( tolower(argv[i][2]) ) { case 0 : /* -d => disable both */ da_font = da_game = 0; break; case 'f' : /* -df => disable font only */ da_font = 0; break; case 'g' : /* -dg => disable game only */ da_game = 0; break; } break; case '%' : /* -% */ { int v = read_unsigned(argv[i]+2); log_g_malloc = v & 1; show_sound_alloc = v & 2; } break; default: fprintf(stderr,"Unrecognised option: %s",argv[i]); exit(EXIT_FAILURE); } } } init_memory(da_font,da_game); /* Set up dynamic areas, etc. if possible */ /* Install memory allocation hooks */ ralloc_aux = g_malloc; rnfree_aux = g_free; /* Install replacement error reporting routines */ quit_aux = quit_hook; plog_aux = plog_hook; core_aux = core_hook; /* Expand the (Angband) resource path */ t = getenv( RESPATH ); if ( !t || !*t ) { Msgs_ReportFatal( 0,"%s is not defined",RESPATH ); } strcpy(resource_path,t); /* Decide where scrap, choices and alarm files live: */ init_paths(); /* Hack: if no savefile specified, use a default */ if ( !arg_savefile ) { arg_savefile = malloc(strlen(resource_path)+32); if ( !arg_savefile ) { Msgs_ReportFatal( 0,"err.mem" ); } sprintf(arg_savefile,"%s%s",resource_path,">Save.Savefile"); } /* This crap appears here so that plog() will work properly before init_acn() is called... */ Resource_Initialise( RESDIR ); Msgs_LoadFile("Messages"); /* This is a hack to only call Event_Initialise3 under RO3 */ if ( os_version() < 300 ) Event_Initialise( VARIANT ); else Event_Initialise3( VARIANT, 300, message_list ); EventMsg_Initialise(); /* | This is a possible workaround for the FP regs getting | bolloxed in the ! menu because the compiler sets them | up before a call to Wimp_Poll if CSE optimisation is on. | At the moment I've just turned off CSE for the function | affected. | | event_mask.data.keepfpregisters = 1; */ /* Load Templates */ Template_Initialise(); if ( templates2d() ) Template_LoadFile("Templates2"); else Template_LoadFile("Templates"); /* Load Sprites */ if ( sprites22() ) resource_sprites = Sprite_LoadFile( "<" RESDIR "$Dir>.Sprites22" ); else resource_sprites = Sprite_LoadFile( "<" RESDIR "$Dir>.Sprites" ); Screen_CacheModeInfo(); /* Initialise some ZapRedraw stuff */ initialise_palette(); initialise_fonts(); /* Set up the fonts */ initialise_r_data(); /* Set up the r_data buffer */ /* Initialise some Wimp specific stuff */ init_gamma_window(); init_sound_window(); init_save_window(); init_menus(); ibar_icon = Icon_BarIcon( ICONNAME, iconbar_RIGHT ); /* Global handlers */ Event_Claim( event_OPEN, event_ANY, event_ANY, Handler_OpenWindow, NULL ); Event_Claim( event_CLOSE, event_ANY, event_ANY, Handler_CloseWindow, NULL ); Event_Claim( event_GAINCARET, event_ANY, event_ANY, Hnd_Caret, (void*)1 ); Event_Claim( event_LOSECARET, event_ANY, event_ANY, Hnd_Caret, (void*)0 ); Event_Claim( event_MENU, event_ANY, event_ANY, Hnd_MenuSel, NULL ); Event_Claim( event_CLICK, window_ICONBAR, ibar_icon, Hnd_IbarClick, NULL ); Event_Claim( event_CLICK, event_ANY, event_ANY, Hnd_Click, NULL ); EventMsg_Claim( message_PALETTECHANGE, event_ANY, Hnd_PaletteChange, NULL ); EventMsg_Claim( message_MODECHANGE, event_ANY, Hnd_ModeChange, NULL ); EventMsg_Claim( message_PREQUIT, event_ANY, Hnd_PreQuit, NULL ); /* Initialise the sound stuff */ initialise_sound(); /* Initialise some Angband stuff */ initialise_terms(); load_choices(); read_alarm_choices(); init_file_paths(unixify_name(resource_path)); Start_Hourglass; /* Paranoia */ /* Hack - override the saved options if -F was on the command line */ start_fullscreen |= start_full; /* hack so that the cursor is yellow if undefined */ if ( palette[CURSOR_COLOUR] == palette[0] ) { angband_color_table[CURSOR_COLOUR][1] = (CURSOR_RGB & 0xff00)>>8; angband_color_table[CURSOR_COLOUR][2] = (CURSOR_RGB & 0xff0000)>>16; angband_color_table[CURSOR_COLOUR][3] = (CURSOR_RGB & 0xff000000)>>24; } /* Catch nasty signals */ signals_init(); /* use pref-acn.prf */ ANGBAND_SYS = "acn"; if ( start_fullscreen ) { enter_fullscreen_mode(); } else { Start_Hourglass; /* Paranoia */ Hnd_ModeChange( NULL, NULL );/* Caches the various fonts/palettes */ show_windows(); grab_caret(); /* Wait for a null poll so that the windows can appear */ do { event_pollmask old_mask = event_mask; event_mask.data.null = 0; Event_Poll(); event_mask = old_mask; } while ( event_lastevent.type != 0 ); } /* Initialise Angband */ Start_Hourglass; /* Paranoia */ strcpy(savefile, unixify_name(arg_savefile) ); use_sound=1; init_angband(); initialised = 1; game_in_progress = 1; pause_line(23); flush(); /* Stop_Hourglass; */ play_game(FALSE); if ( fullscreen_mode ) leave_fullscreen_mode(); Stop_Hourglass; quit(NULL); return 0; debug("to stop the 'unused' warning :)"); } /*--------------------------------------------------------------------------*/ /* Stuff below here is for the full screen display */ /*--------------------------------------------------------------------------*/ static errr Term_xtra_acn_checkFS(void); static errr Term_xtra_acn_eventFS(void); static errr Term_xtra_acn_reactFS(int force); static errr Term_curs_acnFS(int x, int y); static errr Term_xtra_acn_clearFS( void ); static errr Term_xtra_acnFS(int n, int v); static errr Term_wipe_acnFS(int x, int y, int n); static errr Term_text_acnFS(int x, int y, int n, byte a, cptr s); static void bored( void ); static void redraw_areaFS( int x, int y, int w, int h ); static void draw_cursor( int x, int y ); /* | We use this to keep the mouse in the same place on return from full-screen | mode. */ static wimp_point old_mouse_posn; /* | Take a copy of the current mode descriptor/number and return either | a pointer to it (as an int) if it's a new mode, or the mode number. | NB: A static pointer is used and the descriptor returned is only | valid until the next call to this function. | | Basically, a replacement for OS_Byte 135 / OS_ScreenMode1, IYSWIM. */ static int current_mode( void ) { static void *descriptor = NULL; int mode; int size; int i; int *vals; if ( descriptor ) { free( descriptor ); descriptor = NULL; } SWI( 1,3, SWI_OS_Byte, 135, NULL, NULL, &mode ); if ( mode < 256 ) { return mode; } vals = (int*) (mode+20); for ( i=0; vals[i] != -1; i+=2 ) ; size = 24+8*i; /* Size of data */ descriptor = malloc( size ); if ( !descriptor ) { core("Out of memory!"); } memcpy( descriptor, (void*) mode, size ); return (int) descriptor; } /* | Select the best mode we can for full screen. | Returns 12 for (low-res, ie. mode 12) or 27 for high-res, | or a pointer to a mode descriptor (as an int). */ static int select_fullscreen_mode( void ) { static struct { int flags, x, y, l2bpp, hz, term; } desc; int mode = 0; desc.flags = 1; /* format 0 */ desc.x = 640; desc.y = 480; /* 640x480 */ desc.l2bpp = 2; /* 16 colours */ desc.hz = -1; /* best we can get */ desc.term = -1; /* don't fuss about modevars */ SWI( 1,1, SWI_OS_CheckModeValid, &desc, &mode ); if ( mode != (int) &desc ) { SWI( 1,1, SWI_OS_CheckModeValid, 27, /**/ &mode ); if ( mode != 27 ) { SWI( 1,1, SWI_OS_CheckModeValid, 12, /**/ &mode ); if ( mode !=12 ) { mode = 0; } } } return mode; } /* | Change screen mode */ static void change_screenmode( int to ) { if ( SWI( 2,0, SWI_OS_ScreenMode, 0, to ) ) { if ( to<256 ) { GFX_VDU(22); GFX_VDU(to); } else { /* Finished with my woman / cos she couldn't help me with ... */ core("Eeek! mode isn't valid, but it /should/ be..."); } } } /* | Constrain the mouse pointer to a point - this means that the damn | hourglass won't move around with the mouse :) */ static void constrain_pointer( void ) { wimp_rect r; int ys = screen_eig.y == 1 ? 32 : 64; /* Cope with dbl height glass */ Screen_CacheModeInfo(); /* Make sure we know the screen size */ r.min.x = r.max.x = screen_size.x - 32; r.min.y = r.max.y = screen_size.y - ys; Pointer_RestrictToRect( r ); } /* | Convert a 1bpp bitmap into a 4bpp bitmap (bit flipped) */ static int byte_to_word_flipped( int b ) { int w; if ( b & 128 ) { w = 0xf0000000; } else { w=0; } if ( b & 64 ) { w |= 0x0f000000; } if ( b & 32) { w |= 0x00f00000; } if ( b & 16 ) { w |= 0x000f0000; } if ( b & 8 ) { w |= 0x0000f000; } if ( b & 4 ) { w |= 0x00000f00; } if ( b & 2 ) { w |= 0x000000f0; } if ( b & 1 ) { w |= 0x0000000f; } return w; } /* | try to load the fallback fullscreen font and convert it to 4bpp */ static int cache_zapfontHR( void ) { int handle,extent; char buffer[260]; struct { char id[8]; int w,h,f,l,r1,r2; } zfh; int *op; char *ip; int l,i; /* Try to open the file */ sprintf(buffer,"%s%s",resource_path,"xtra.FullScreen"); handle = myFile_Open( buffer, 0x4f ); if ( !handle ) { return 0; } /* Check file's extent */ extent = myFile_Extent( handle ); if ( extent > sizeof(zfh)+256*16 ) { myFile_Close(handle); return 0; } /* Load the header */ if ( myFile_ReadBytes( handle, &zfh, sizeof(zfh) ) ) { myFile_Close(handle); return 0; } /* Check font size */ if ( (zfh.w!=8) || (zfh.h>16) ) { myFile_Close(handle); return 0; } /* Load the 1bpp data */ if ( myFile_ReadBytes( handle, fullscreen_font, extent-sizeof(zfh) ) ) { myFile_Close(handle); return 0; } myFile_Close(handle); l = zfh.l>255 ? 255 : zfh.l; if ( zfh.h>8 ) { op = (int*) (((int)fullscreen_font)+(l+1)*zfh.h*4); ip = (char*) (((int)fullscreen_font)+(l+1)*zfh.h-(zfh.f*zfh.h)); while ( l-->=zfh.f ) { for ( i=0; i=zfh.f ) { for ( i=-zfh.h; iw==8) && (data[0].font->h<=16) ) src = data[0].font; /* | Hack: if we're forced to use the system font, try to load the | 'fullscreen' font from lib.xtra. If that fails, then I guess we're | stuck with the system font. */ if ( src == SYSTEM_FONT ) if ( cache_zapfontHR() ) { return 1; } ip = (char*) (src->bpp_1); /* Now, create the font */ if ( src->h>8 ) { int e = src->h * (src->l>256 ? 256 : src->l); op += (src->f*src->h); for ( c=src->f*src->h; ch; } else { int e = src->h * (src->l>256 ? 256 : src->l); op += (src->f*src->h)*2; for ( c=src->f*src->h; ch*2; } fullscreen_topline = TERM_TOPLINE_HR; fullscreen_topline += ((16-fullscreen_height)*13); return 1; } static int cache_fs_fontLR( void ) { ZapFont *src = SYSTEM_FONT; int c,e; int *op; char *ip; /* Allocate the storage for the font */ fullscreen_font = f_malloc( 256*4*8 ); if ( !fullscreen_font ) { return 0; } op = (int*)fullscreen_font; /* Check to see if the main term's font is suitable (ie. 8x8) */ if ( (data[0].font->w==8) && (data[0].font->h<=8) ) src = data[0].font; ip = (char*) (src->bpp_1); /* Now, create the font */ e = src->h * (src->l>256 ? 256 : src->l); op += (src->f*src->h); for ( c=src->f*src->h; ch; fullscreen_topline = TERM_TOPLINE_LR; fullscreen_topline += ((8-fullscreen_height)*13); return 1; } static void set_keys( int claim ) { static int old_c_state; static int old_f_state[8]; int i; if ( claim ) { /* Cursors/copy act as function keys */ /* f0-f9, cursors, generate 0x80-0x8f */ /* sh-f0-f9,cursors, generate 0x90-0x9f */ /* ctrl f0-f9,cursors, generate 0xa0-0xaf */ /* sh-c-f0-f9,cursors, generate 0xb0-0xbf */ /* f10-f12 generate 0xca-0xcc */ /* shift f10-f12 generate 0xda-0xdc */ /* ctrl f10-f12 generate 0xea-0xec */ /* ctrlshift f10-f12 generate 0xfa-0xfc */ SWI( 3,2, SWI_OS_Byte, 4, 2, 0, /**/ NULL, &old_c_state ); for ( i=0; i<4; i++ ) { SWI(3,2,SWI_OS_Byte,225+i,0x80+(i*0x10),0, NULL, old_f_state+i ); SWI(3,2,SWI_OS_Byte,221+i,0xc0+(i*0x10),0, NULL, old_f_state+i+4 ); } } else { SWI( 3,0, SWI_OS_Byte, 4, old_c_state, 0 ); for ( i=0; i<4; i++ ) { SWI(3,0, SWI_OS_Byte, 225+i, old_f_state[i], 0 ); SWI(3,0, SWI_OS_Byte, 221+i, old_f_state[i+4], 0 ); } } } /* | Enter the full screen mode. | | Full screen display uses either mode 27 (if supported) and 8x16 fonts | (or system font 'twiddled' to double height), or mode 12 (if mode 27 | is unavailable) and the system font (or an 8x8 font). | */ static void enter_fullscreen_mode( void ) { int vduvars[2] = { 149, -1 }; int i; mouse_block mb; /* New in 1.18 - protect against 're-entracy' */ if ( fullscreen_font ) return; /* New in 1.20 - hack IClear out of the way */ if ( allow_iclear_hack ) iclear_hack(); /* Remember where the mouse is */ Wimp_GetPointerInfo( &mb ); old_mouse_posn = mb.pos; /* Choose the mode we want */ fullscreen_mode = select_fullscreen_mode(); if ( !fullscreen_mode ) { plog("Unable to select a suitable screen mode (27 or 12)"); return; } if ( ! ( (fullscreen_mode==12) ? cache_fs_fontLR() : cache_fs_fontHR() ) ) { plog("Unable to cache a font for full screen mode"); return; } /* Read the current screen mode */ /* SWI( 1,3, SWI_OS_Byte, 135, NULL, NULL, &old_screenmode ); */ old_screenmode = current_mode(); Stop_Hourglass; /* Change to the chosen screen mode */ change_screenmode( fullscreen_mode ); /* Restrict the pointer */ constrain_pointer(); /* Remove the cursors */ SWI( 0,0, SWI_OS_RemoveCursors ); Start_Hourglass; /* Get the base address of screen memory */ SWI( 2,0, SWI_OS_ReadVduVariables, vduvars, vduvars ); fullscreen_base = (int*) (vduvars[0]); /* Fudge the Term interface */ for ( i=0; ixtra_hook = Term_xtra_acnFS; t->wipe_hook = Term_wipe_acnFS; t->curs_hook = Term_curs_acnFS; t->text_hook = Term_text_acnFS; } /* Grab the palette */ Term_xtra_acn_reactFS( TRUE ); /* Make sure that the keys work properly */ set_keys( TRUE ); /* refresh the term */ /*Term_activate( &(data[0].t) );*/ redraw_areaFS( 0,0, 80,24 ); if ( data[0].cursor.visible ) draw_cursor(data[0].cursor.pos.x,data[0].cursor.pos.y); /* Display a reminder of how to get back... */ /* Hack: disable force_mono */ i = force_mono; force_mono = 0; Term_text_acnFS( 0,TIME_LINE, strlen(fs_quit_key_text), 8, fs_quit_key_text ); force_mono = i; } static void leave_fullscreen_mode( void ) { int i; unsigned char blk[8]; /* New in 1.18 - protect against 're-entracy' */ if ( !fullscreen_font ) return; /* Restore the Term interface */ for ( i=0; ixtra_hook = Term_xtra_acn; t->wipe_hook = Term_wipe_acn; t->curs_hook = Term_curs_acn; t->text_hook = Term_text_acn; mark_ood( &(data[i]), 0,0, 80,24 ); } /* Deallocate the font */ f_free(fullscreen_font); fullscreen_font = 0; fullscreen_mode = 0; Stop_Hourglass; /* Restore the screen mode */ Wimp_SetMode( old_screenmode ); /* Restore the pointer position */ blk[0] = 3; /* Set mouse position sub-reason code */ blk[1] = (old_mouse_posn.x & 0xff); blk[2] = (old_mouse_posn.x & 0xff00)>>8; blk[3] = (old_mouse_posn.y & 0xff); blk[4] = (old_mouse_posn.y & 0xff00)>>8; OS_Word( osword_DEFINEPOINTERANDMOUSE, blk ); Start_Hourglass; /*Start_Hourglass;*/ /* Restore the various soft keys */ set_keys( FALSE ); /* New in 1.20 Remove the IClear hack */ if ( allow_iclear_hack ) remove_iclear_hack(); /* Refresh the windows - this probably isn't necessary anyway */ refresh_windows(); } static void fs_writechars( int x, int y, int n, char *chars, char attr ) { int *scr,*scrb; int *cdat; int j; unsigned int fgm; if ( force_mono ) { if (attr!=TERM_DARK) { attr = TERM_WHITE; } } fgm = (unsigned int) zpalette[attr]; scrb = (int*) ( ((int)fullscreen_base)+y*fullscreen_height*320 + x*4+320*fullscreen_topline ); while (n--) { scr = scrb++; cdat = (int*) ( ((int)fullscreen_font) + (*chars++)*(fullscreen_height<<2) ); for ( j=0; jc[j][i],data[0].t.old->a[j][i] ); } static int wimp_code( int c ) { /* shift/ctrl keypad? */ if ( c>='0' && c<='9' ) { kbd_modifiers m = Kbd_GetModifiers( FALSE ); if ( m.shift ) { c |= 0x800; } if ( m.ctrl ) { c |= 0x400; } return c; } if ( c==9 ) { return 0x18a; } /* Tab */ if ( c<=127 ) { return c; } /* normal ASCII/ctrl */ if ( c>=0x80 && c<=0xff ) { return c+0x100; } /* f0-f9, etc. */ return -1; /* unknown */ } static void do_keypress(int code) { const static char hex[] = "0123456789ABCDEF"; if ( code==KEYPRESS_QUIT ) { /*Sound_SysBeep();*/ leave_fullscreen_mode(); return; } if ( code==27 ) { if ( Kbd_KeyDown( inkey_CTRL ) ) { ack_alarm(); return; } } if ( code<=255 ) { Term_keypress(code); } else { Term_keypress(31); Term_keypress(hex[(code & 0xf00)>>8]); Term_keypress(hex[(code & 0x0f0)>>4]); Term_keypress(hex[(code & 0x00f)]); Term_keypress(13); } } static errr Term_xtra_acn_checkFS(void) { int bh,bl; int c; Stop_Hourglass; bored(); SWI( 3,3, SWI_OS_Byte, 128, 255, 0, /**/ NULL, &bl, &bh ); bl = (bl & 0xff) + (bh<<8); if ( bl>0 ) { SWI( 0,1, SWI_OS_ReadC, &c ); bl = wimp_code(c); if ( bl>=0 ) { do_keypress( bl ); } } Start_Hourglass; return 0; } static errr Term_xtra_acn_eventFS(void) { int c; int w = -1; Stop_Hourglass; for ( w=-1 ; w==-1 ; ) { int bh,bl; do { bored(); SWI( 3,3, SWI_OS_Byte, 128, 255, 0, /**/ NULL, &bl, &bh ); bl = (bl & 0xff) + (bh<<8); } while ( !bl ); SWI( 0,1, SWI_OS_ReadC, &c ); w = wimp_code(c); if ( w>=0 ) { do_keypress( w ); } } Start_Hourglass; return 0; } /* * React to changes */ static errr Term_xtra_acn_reactFS(int force) { int i; int p,r,g,b; static double old_gamma = -1.0; if ( gamma != old_gamma ) { force = 1; old_gamma = gamma; } /* Set the screen colours */ for ( i=0; i<16; i++ ) { if ( COLOUR_CHANGED(i) || force ) { r = (int) (255.0*pow(angband_color_table[i][1]/255.0, 1.0/gamma)); g = (int) (255.0*pow(angband_color_table[i][2]/255.0, 1.0/gamma)); b = (int) (255.0*pow(angband_color_table[i][3]/255.0, 1.0/gamma)); GFX_VDU( 19 ); GFX_VDU( i ); GFX_VDU( 16 ); GFX_VDU( r ); GFX_VDU( g ); GFX_VDU( b ); palette[i] = (b<<24) | (g<<16) | (r<<8); p = i; p |= (p<<4); p |= (p<<8); p |= (p<<16); zpalette[i] = p; a_palette[i][1] = angband_color_table[i][1]; a_palette[i][2] = angband_color_table[i][2]; a_palette[i][3] = angband_color_table[i][3]; /* Find any higher colour numbers and make them "wrong" */ for ( p=16; p<256; p++ ) if ( (zpalette[p] & 0xf) == i ) a_palette[p][1]=angband_color_table[p][1]+2; } } /* Go through the palette updating any changed values */ for ( i=16; i<256; i++ ) { if ( COLOUR_CHANGED(i) || force ) { r = (int) (255.0*pow(angband_color_table[i][1]/255.0, 1.0/gamma)); g = (int) (255.0*pow(angband_color_table[i][2]/255.0, 1.0/gamma)); b = (int) (255.0*pow(angband_color_table[i][3]/255.0, 1.0/gamma)); p = (b<<24) | (g<<16) | (r<<8); palette[i] = p; SWI( 1,1, SWI_ColourTrans_ReturnColourNumber, palette[i], &p ); p |= (p<<4); p |= (p<<8); p |= (p<<16); zpalette[i] = p; a_palette[i][1] = angband_color_table[i][1]; a_palette[i][2] = angband_color_table[i][2]; a_palette[i][3] = angband_color_table[i][3]; } } cursor_rgb = palette[CURSOR_COLOUR]; return 0; } static errr Term_curs_acnFS(int x, int y) { if ( Term == &(data[0].t) ) { if ( data[0].cursor.visible ) redraw_areaFS(data[0].cursor.pos.x,data[0].cursor.pos.y,1,1); data[0].cursor.pos.x = x; data[0].cursor.pos.y = y; if ( data[0].cursor.visible ) draw_cursor(x,y); } return 0; } static errr Term_xtra_acn_clearFS( void ) { char e[80]; int j; if ( Term == &(data[0].t) ) { for ( j=0; j<80; j++ ) e[j] = ' '; GFX_Wait(); for ( j=0; j<24; j++ ) fs_writechars( 0,j, 80, e, 0 ); } return 0; } static errr Term_xtra_acnFS(int n, int v) { term_data *t = (term_data*) Term; switch (n) { case TERM_XTRA_CLEAR: if ( t==(&data[0]) ) Term_xtra_acn_clearFS(); return 0; case TERM_XTRA_EVENT: if (v) return Term_xtra_acn_eventFS(); else return Term_xtra_acn_checkFS(); case TERM_XTRA_BORED: bored(); return Term_xtra_acn_checkFS(); case TERM_XTRA_FLUSH: /* 1.21 - Hack: wait until no keys are pressed */ if ( hack_flush ) for ( v=0 ; v!=0xff ; ) SWI( 1,2, SWI_OS_Byte, 122, 0, &v ); SWI( 3,0, SWI_OS_Byte, 21,0,0 ); /* Flush Kbd buffer */ return 0; case TERM_XTRA_FRESH: return 0; case TERM_XTRA_FROSH: return 0; case TERM_XTRA_SHAPE: if ( t==(&data[0]) ) { t->cursor.visible = v; if ( v ) draw_cursor( t->cursor.pos.x, t->cursor.pos.y ); else redraw_areaFS( t->cursor.pos.x, t->cursor.pos.y, 1, 1 ); } return 0; case TERM_XTRA_NOISE: Sound_SysBeep(); return 0; case TERM_XTRA_REACT: return Term_xtra_acn_reactFS(FALSE); case TERM_XTRA_DELAY: if ( v>0 ) { int start = Time_Monotonic(); v=(v+5)/10; /* Round to nearest cs */ GFX_Wait(); while ( (Time_Monotonic()-start)< v ) ; } return (0); case TERM_XTRA_SOUND : /* Play a sound :) */ if ( enable_sound ) { play_sound( v ); } return 0; default: return 1; } } static errr Term_wipe_acnFS(int x, int y, int n) { if ( Term == &(data[0].t) ) while ( n-- ) fs_writechar(x++,y,' ',0); return 0; } static errr Term_text_acnFS(int x, int y, int n, byte a, cptr s) { if ( Term == &(data[0].t) ) fs_writechars(x,y,n,(char*)s,(char)a); return 0; } static void bored() { static int last = -1; char ts[80]; time_t ct; struct tm *lt; int l; int ofm; static int alarm_flash = 1; check_alarm( FALSE ); l = Time_Monotonic(); if ( (l-last) < (alarm_flash ? 25 : 50) ) { return; } last = l; time( &ct ); lt = localtime( &ct ); l = strftime( ts, 80, "%c %Z", lt ); /* Hack: disable force_mono around printing the time */ ofm = force_mono; force_mono = 0; /* Hack: Is the alarm supposed to be going off? */ if ( alarm_disp || alarm_flash ) { char blk[60]; int c = 8; if ( !alarm_disp ) { alarm_flash = 11; } switch ( alarm_flash/2 ) { case 4 : sprintf(blk,"%-57s",alarm_cancel_text); break; case 5 : sprintf(blk,"%-57s",fs_quit_key_text); break; default : c = alarm_flash & 1 ? TERM_RED : TERM_WHITE; sprintf(blk,"%02d:%02d %-51s",alarm_h, alarm_m, alarm_message); } fs_writechars( 0,TIME_LINE, 57, blk, c ); if ( ++alarm_flash>11 ) { alarm_flash = 0; } } /* Display time */ fs_writechar(79-l,TIME_LINE,' ',0); fs_writechars(80-l,TIME_LINE,l,ts,8); force_mono = ofm; } /*--------------------------------------------------------------------------*/ /* (Simple) Heap management (using OS_Heap) */ /*--------------------------------------------------------------------------*/ typedef void* heap; static os_error *Heap_Initialise( heap h, size_t size ) { return SWI( 4,0, SWI_OS_Heap, 0, h, 0, size ); } static void *Heap_Claim( heap h, size_t size ) { void *fred; os_error *e; e = SWI( 4,3, SWI_OS_Heap, 2, h, 0, size, NULL, NULL, &fred ); return e ? NULL : fred; } static os_error *Heap_Release( heap h, void *block ) { return SWI( 3,0, SWI_OS_Heap, 3, h, block ); } static int Heap_ChangeHeapSize( heap h, int resize_by ) { int by; SWI( 4,4, SWI_OS_Heap, 5,h,0,resize_by, 0,0,0,&by ); return by; } /*-------------------------