/* -*-mode: C; fill-column: 77; c-basic-offset: 4; compile-command: "make ARCH=68K_ONLY MEMDEBUG=TRUE"; -*- */ /* * Copyright 1999 - 2004 by Eric House (xwords@eehouse.org). All rights reserved. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #ifdef FEATURE_SILK # include #endif #include "comtypes.h" #include "comms.h" #include "xwords4defines.h" #include "palmmain.h" #include "newgame.h" #include "dictui.h" #include "dictlist.h" #include "palmutil.h" #include "palmdict.h" #include "palmsavg.h" #include "memstream.h" #include "strutils.h" #include "palmir.h" #include "palmip.h" #include "palmbt.h" #include "xwcolors.h" #include "prefsdlg.h" #include "connsdlg.h" #include "gameutil.h" #include "dictui.h" #include "LocalizedStrIncludes.h" #include "callback.h" #include "pace_man.h" /* for crash() macro */ #ifdef SUPPORT_SONY_JOGDIAL #include "SonyChars.h" #endif #define PALM_TIMER_DELAY 25 #ifdef IR_SUPPORT # ifndef IR_EXCHMGR /* These are globals! But the ptrs must remain valid until they're removed from the IR lib, and is seems stupid to alloc them on the heap when they have to live in the code anyway */ UInt8 deviceInfo[] = { IR_HINT_PDA, IR_CHAR_ASCII, 'X','W','O','R','D','S','4' }; UInt8 deviceName[] = { IAS_ATTRIB_USER_STRING, IR_CHAR_ASCII, 7,'X','W','O','R','D','S','4'}; UInt8 xwordsIRResult[] = { 0x01, /* Type for Integer is 1 */ 0x00,0x00,0x00,0x02 /* Assumed Lsap */ }; /* IrDemo attribute */ const IrIasAttribute xwordsIRAttribs = { (UInt8*) "IrDA:IrLMP:LsapSel",18, 0, (UInt8*)xwordsIRResult, sizeof(xwordsIRResult) }; static IrIasObject xwordsIRObject = { (UInt8*)"Xwords4", 7, 1, NULL /* (IrIasAttribute*)&irdemoAttribs */ }; # endif # else # define ir_setup(g) #endif /*-------------------------------- defines and consts-----------------------*/ /* #define COLORCHANGE_THRESHOLD 300 */ /*-------------------------------- prototypes ------------------------------*/ static XP_Bool startApplication( PalmAppGlobals** globalsP ); static void eventLoop( PalmAppGlobals* globals ); static void stopApplication( PalmAppGlobals* globals ); static Boolean applicationHandleEvent( EventPtr event ); static Boolean mainViewHandleEvent( EventPtr event ); static UInt16 romVersion( void ); static Boolean handleHintRequest( PalmAppGlobals* globals ); static XP_Bool timeForTimer( PalmAppGlobals* globals, XWTimerReason* why, XP_U32* when ); static XP_S16 palm_send( const XP_U8* buf, XP_U16 len, const CommsAddrRec* addr, void* closure ); static void palm_send_on_close( XWStreamCtxt* stream, void* closure ); /* callbacks */ static VTableMgr* palm_util_getVTManager( XW_UtilCtxt* uc ); static void palm_util_userError( XW_UtilCtxt* uc, UtilErrID id ); static XP_Bool palm_util_userQuery( XW_UtilCtxt* uc, UtilQueryID id, XWStreamCtxt* stream ); static XWBonusType palm_util_getSquareBonus( XW_UtilCtxt* uc, ModelCtxt* model, XP_U16 col, XP_U16 row ); static XP_S16 palm_util_userPickTile( XW_UtilCtxt* uc, const PickInfo* pi, XP_U16 playerNum, const XP_UCHAR4* texts, XP_U16 nTiles ); static XP_Bool palm_util_askPassword( XW_UtilCtxt* uc, const XP_UCHAR* name, XP_UCHAR* buf, XP_U16* len ); static void palm_util_trayHiddenChange( XW_UtilCtxt* uc, XW_TrayVisState newState, XP_U16 nVisibleRows ); static void palm_util_yOffsetChange( XW_UtilCtxt* uc, XP_U16 oldOffset, XP_U16 newOffset ); static void palm_util_notifyGameOver( XW_UtilCtxt* uc ); static XP_Bool palm_util_hiliteCell( XW_UtilCtxt* uc, XP_U16 col, XP_U16 row ); static XP_Bool palm_util_engineProgressCallback( XW_UtilCtxt* uc ); static void palm_util_setTimer( XW_UtilCtxt* uc, XWTimerReason why, XP_U16 when, TimerProc proc, void* closure ); static XP_U32 palm_util_getCurSeconds( XW_UtilCtxt* uc ); static void palm_util_requestTime( XW_UtilCtxt* uc ); static DictionaryCtxt* palm_util_makeEmptyDict( XW_UtilCtxt* uc ); #ifndef XWFEATURE_STANDALONE_ONLY static XWStreamCtxt* palm_util_makeStreamFromAddr( XW_UtilCtxt* uc, XP_U16 channelNo ); #endif static XP_UCHAR* palm_util_getUserString( XW_UtilCtxt* uc, XP_U16 stringCode ); static XP_Bool palm_util_warnIllegalWord( XW_UtilCtxt* uc, BadWordInfo* bwi, XP_U16 turn, XP_Bool turnLost ); #ifdef BEYOND_IR static void palm_util_addrChange( XW_UtilCtxt* uc, const CommsAddrRec* oldAddr, const CommsAddrRec* newAddr, XP_Bool isServer ); #endif #ifdef XWFEATURE_SEARCHLIMIT static XP_Bool palm_util_getTraySearchLimits( XW_UtilCtxt* uc, XP_U16* min, XP_U16* max ); #endif static void userErrorFromStrId( PalmAppGlobals* globals, XP_U16 strID ); static Boolean askFromStream( PalmAppGlobals* globals, XWStreamCtxt* stream, XP_S16 titleID, Boolean closeAndDestroy ); static void displayFinalScores( PalmAppGlobals* globals ); static void updateScrollbar( PalmAppGlobals* globals, Int16 newValue ); static void askStartNewGame( PalmAppGlobals* globals ); static void palmSetCtrlsForTray( PalmAppGlobals* globals ); static void drawFormButtons( PalmAppGlobals* globals ); static MemHandle findXWPrefsRsrc( PalmAppGlobals* globals, UInt32 resType, UInt16 resID ); #ifdef SHOW_PROGRESS static void palm_util_engineStarting( XW_UtilCtxt* uc, XP_U16 nBlanks ); static void palm_util_engineStopping( XW_UtilCtxt* uc ); #endif static void initAndStartBoard( PalmAppGlobals* globals, XP_Bool newGame ); /*-------------------------------- Globals ---------------------------------*/ /* NONE!!! */ /***************************************************************************** * ****************************************************************************/ #define XW_MOVE_EXG_TYPE "XwMv" UInt32 PM2(PilotMain)( UInt16 cmd, MemPtr cmdPBP, UInt16 launchFlags) { PalmAppGlobals* globals; if ( cmd == sysAppLaunchCmdNormalLaunch ) { if ( ((launchFlags & sysAppLaunchFlagNewGlobals) != 0) && startApplication( &globals ) ) { XP_ASSERT( (launchFlags & sysAppLaunchFlagNewGlobals) != 0 ); // Initialize the application's global variables and database. eventLoop( globals ); } stopApplication( globals ); #ifdef IR_EXCHMGR } else if ( cmd == sysAppLaunchCmdExgAskUser ) { if ( (launchFlags & sysAppLaunchFlagSubCall) != 0 ) { ((ExgAskParamPtr)cmdPBP)->result = exgAskOk; } } else if ( cmd == sysAppLaunchCmdSyncNotify ) { if ( romVersion() >= 30 ) { ExgRegisterData( APPID, exgRegTypeID, XW_MOVE_EXG_TYPE ); } } else if ( cmd == sysAppLaunchCmdExgReceiveData ) { if ( (launchFlags & sysAppLaunchFlagSubCall) != 0 ) { globals = getFormRefcon(); palm_ir_receiveMove( globals, (ExgSocketPtr)cmdPBP ); } #endif } return 0; } /* PilotMain */ /***************************************************************************** * ****************************************************************************/ static UInt16 romVersion( void ) { UInt32 dwOSVer; FtrGet(sysFtrCreator, sysFtrNumROMVersion, &dwOSVer ); /* should turn 3 and 5 into 35 */ return (sysGetROMVerMajor(dwOSVer)*10) + sysGetROMVerMinor(dwOSVer); } /* romVersion */ #ifdef COLOR_SUPPORT /***************************************************************************** * ****************************************************************************/ static UInt32 cur_screen_depth( void ) { UInt32 curDepth; XP_ASSERT( romVersion() >= 30 ); /* */ WinScreenMode( winScreenModeGet, 0, 0, &curDepth, 0 ); return curDepth; } /* cur_screen_depth */ #endif static void getSizes( PalmAppGlobals* globals ) { XP_U16 width, height; width = 160; height = 160; if ( globals->hasHiRes ) { XP_U32 tmp; if ( WinScreenGetAttribute( winScreenWidth, &tmp ) == errNone ) { width = tmp; } if ( WinScreenGetAttribute( winScreenHeight, &tmp ) == errNone ) { height = tmp; } } if ( width == 320 ) { FormPtr form = FrmGetActiveForm(); WinGetDisplayExtent( &width, &height ); if ( !!form ) { RectangleType r; r.topLeft.x = 0; r.topLeft.y = 0; r.extent.x = width; r.extent.y = height; WinSetBounds( FrmGetWindowHandle(FrmGetActiveForm()), &r ); } width *= 2; height *= 2; globals->useHiRes = width >= 320 && height >= 320; } globals->width = width; globals->height = height; } /* getSizes */ /* The resources place the tray-related buttons for the high-res case. If * the device is going to want them in the higher low-res position, move them * here. And resize 'em too. */ static void locateTrayButtons( PalmAppGlobals* globals ) { if ( !globals->useHiRes ) { /* we need to put the buttons into the old position and set their sizes for the larger tray. */ XP_U16 buttonInfoTriplets[] = { XW_MAIN_HIDE_BUTTON_ID, TRAY_BUTTONS_Y_LR, XW_MAIN_JUGGLE_BUTTON_ID, TRAY_BUTTONS_Y_LR, XW_MAIN_TRADE_BUTTON_ID, TRAY_BUTTONS_Y_LR + TRAY_BUTTON_HEIGHT_LR, XW_MAIN_DONE_BUTTON_ID, TRAY_BUTTONS_Y_LR + TRAY_BUTTON_HEIGHT_LR }; XP_U16* ptr; XP_U16 i; for ( i = 0, ptr = buttonInfoTriplets; i < 4; ++i, ptr += 2 ) { RectangleType rect; getObjectBounds( ptr[0], &rect ); rect.topLeft.y = ptr[1]; rect.extent.y = TRAY_BUTTON_HEIGHT_LR; setObjectBounds( ptr[0], &rect ); } } } /* locateTrayButtons */ static XP_Bool positionBoard( PalmAppGlobals* globals ) { XP_U16 bWidth = globals->width; XP_Bool erase = XP_FALSE; XP_Bool isLefty = globals->isLefty; XP_U16 nCols, leftEdge; XP_U16 scale = PALM_BOARD_SCALE; XP_U16 scaleH, scaleV; XP_U16 boardHeight, trayTop, trayScaleV; XP_U16 boardTop, scoreTop, scoreLeft, scoreWidth, scoreHeight; XP_U16 timerWidth, timerLeft; XP_U16 freeSpaceH; XP_Bool showGrid = globals->gState.showGrid; XP_U16 doubler = globals->useHiRes? 2 : 1; #ifdef SHOW_PROGRESS RectangleType bounds; #endif XP_ASSERT( !!globals->game.model ); nCols = model_numCols( globals->game.model ); XP_ASSERT( nCols <= PALM_MAX_ROWS ); /* With the screen having variable width and height, we do away with * constants and calculate everything on the fly. Horizontally, the * screen consists of the board and scrollbar/buttons. Vertically, it's * the scoreboard, the board, and the tray. If the board is square the * tray must overlap the board -- but not if the smallest font we can fit * in a cell allows the squares to squeeze down! * * Be careful with squeezing cells! Custom chars won't allow it. So * cell size needs to stay constant... For now we only avoid overlap * cute when the screen is taller than it is wide. */ /* since we only want the lines between cells one pixel wide, we can increase scale more than 2x when doubling. */ if ( !showGrid ) { --scale; } scale = scale * doubler; scaleV = scaleH = scale; if ( globals->useHiRes ) { scaleV -= 2; } freeSpaceH = ((PALM_MAX_COLS-nCols)/2) * scaleH; if ( isLefty ) { leftEdge = bWidth - (nCols * scaleH) - freeSpaceH - 1; } else { leftEdge = PALM_BOARD_LEFT_RH + freeSpaceH; } /* position the timer. There are really four cases: width depends on whether the grid's visible, and left edge depends on isLefty _and_ width in the non-lefty case. */ if ( showGrid ) { timerWidth = FntCharsWidth( "-00:00", 6 ); /* the ideal */ } else { timerWidth = PALM_GRIDLESS_SCORE_WIDTH; } timerWidth *= doubler; if ( isLefty && !showGrid ) { timerLeft = 0; } else { timerLeft = bWidth - timerWidth; } board_setTimerLoc( globals->game.board, timerLeft, PALM_TIMER_TOP, timerWidth, PALM_TIMER_HEIGHT * doubler ); if ( showGrid ) { boardTop = PALM_BOARD_TOP; scoreLeft = PALM_SCORE_LEFT; scoreTop = PALM_SCORE_TOP; scoreWidth = (bWidth/doubler) - PALM_SCORE_LEFT - (timerWidth/doubler); scoreHeight = PALM_SCORE_HEIGHT; } else { boardTop = PALM_GRIDLESS_BOARD_TOP; scoreLeft = isLefty? 0: PALM_GRIDLESS_SCORE_LEFT; scoreTop = PALM_GRIDLESS_SCORE_TOP; scoreWidth = PALM_GRIDLESS_SCORE_WIDTH; scoreHeight = PALM_TRAY_TOP - PALM_GRIDLESS_SCORE_TOP - 2; if ( !isLefty ) { leftEdge += doubler; /* for the frame */ } } boardTop *= doubler; scoreLeft *= doubler; scoreTop *= doubler; scoreWidth *= doubler; scoreHeight *= doubler; board_setPos( globals->game.board, leftEdge, boardTop, isLefty ); board_setScale( globals->game.board, scaleH, scaleV ); board_setScoreboardLoc( globals->game.board, scoreLeft, scoreTop, scoreWidth, scoreHeight, showGrid ); board_setShowColors( globals->game.board, globals->gState.showColors ); board_setYOffset( globals->game.board, 0 ); /* figure location for the tray. If possible, make it smaller than the ideal to avoid using a scrollbar. Also, note at this point whether a scrollbar will be required. */ globals->needsScrollbar = false; /* default */ boardHeight = scaleV * nCols; if ( globals->useHiRes ) { trayTop = ((160 - TRAY_HEIGHT_HR) * doubler) - 1; globals->needsScrollbar = false; } else { trayTop = 160 - TRAY_HEIGHT_LR; globals->needsScrollbar = showGrid && (nCols == PALM_MAX_COLS); } trayScaleV = globals->useHiRes? (TRAY_HEIGHT_HR*doubler) + 1: TRAY_HEIGHT_LR; board_setTrayLoc( globals->game.board, (isLefty? PALM_TRAY_LEFT_LH:PALM_TRAY_LEFT_RH) * doubler, trayTop, PALM_TRAY_WIDTH * doubler, trayScaleV, PALM_DIVIDER_WIDTH ); board_prefsChanged( globals->game.board, &globals->gState.cp ); #ifdef SHOW_PROGRESS if ( showGrid ) { getObjectBounds( XW_MAIN_SCROLLBAR_ID, &bounds ); bounds.topLeft.x += doubler; bounds.extent.x -= (doubler << 1); } else { bounds.topLeft.y = (PALM_TIMER_HEIGHT + 2) * doubler; bounds.topLeft.x = (globals->isLefty? FLIP_BUTTON_WIDTH+3: PALM_GRIDLESS_SCORE_LEFT+2) * doubler; bounds.extent.x = (RECOMMENDED_SBAR_WIDTH + 2) * doubler; bounds.extent.y = (PALM_GRIDLESS_SCORE_TOP - bounds.topLeft.y - 2) * doubler; } globals->progress.boundsRect = bounds; #endif updateScrollbar( globals, globals->scrollValue ); /* changing visibility? */ palmSetCtrlsForTray( globals ); drawFormButtons( globals ); return erase; } /* positionBoard */ static XWStreamCtxt* makeSimpleStream( PalmAppGlobals* globals, MemStreamCloseCallback cb ) { return mem_stream_make( MPPARM(globals->mpool) globals->vtMgr, globals, CHANNEL_NONE, cb ); } /* makeSimpleStream */ static XWStreamCtxt* gameRecordToStream( PalmAppGlobals* globals, XP_U16 index ) { XWStreamCtxt* recStream = NULL; LocalID id; MemHandle handle; Err err; id = DMFINDDATABASE( globals, CARD_0, XW_GAMES_DBNAME ); if ( id != 0 ) { UInt16 numRecs; DmOpenRef dbP; dbP = DMOPENDATABASE( globals, CARD_0, id, dmModeReadOnly ); numRecs = DmNumRecords( dbP ); if ( index < numRecs ) { handle = DmGetRecord( dbP, index ); recStream = makeSimpleStream( globals, NULL ); stream_open( recStream ); stream_putBytes( recStream, MemHandleLock(handle), MemHandleSize(handle) ); MemHandleUnlock(handle); err = DmReleaseRecord( dbP, index, false ); XP_ASSERT( err == 0 ); } DMCLOSEDATABASE( dbP ); } return recStream; } /* gameRecordToStream */ static void loadGamePrefs( /*PalmAppGlobals* globals, */XWStreamCtxt* stream ) { /* Keep in sync with games saved in prev version, which foolishly saved hintsNotAllowed separate from the current game's value for the same thing. When the version changes get rid of this bit. PENDING */ (void)stream_getBits( stream, 1 ); } /* loadGamePrefs */ static void saveGamePrefs( /*PalmAppGlobals* globals, */XWStreamCtxt* stream ) { stream_putBits( stream, 1, 0 ); } /* saveGamePrefs */ static void reportMissingDict( PalmAppGlobals* globals, XP_UCHAR* name ) { /* FrmCustomAlert crashes on some OS versions when there's no form under it to "return" to. */ if ( FrmGetActiveForm() != NULL ) { XP_UCHAR buf[48]; XP_UCHAR* str = getResString( globals, STRS_CANNOT_FIND_DICT ); StrPrintF( buf, str, name ); (void)FrmCustomAlert( XW_ERROR_ALERT_ID, (const char*)buf, " ", " " ); } } /* reportMissingDict */ static Boolean loadCurrentGame( PalmAppGlobals* globals, XP_U16 gIndex, XWGame* game, CurGameInfo* ginfo ) { XP_Bool hasDict; XWStreamCtxt* recStream; Boolean success = false; DictionaryCtxt* dict; recStream = gameRecordToStream( globals, gIndex ); /* now read everything out of the stream */ if ( !!recStream ) { char ignore[MAX_GAMENAME_LENGTH]; /* skip the name */ stream_getBytes( recStream, ignore, MAX_GAMENAME_LENGTH ); loadGamePrefs( /*globals, */recStream ); hasDict = stream_getU8( recStream ); if ( hasDict ) { XP_UCHAR* name = stringFromStream( MPPARM(globals->mpool) recStream ); dict = palm_dictionary_make( MPPARM(globals->mpool) globals, name, globals->dictList ); success = dict != NULL; if ( !success ) { reportMissingDict( globals, name ); XP_FREE( globals->mpool, name ); beep(); } } else { dict = NULL; success = true; } if ( success ) { game_makeFromStream( MEMPOOL recStream, game, ginfo, dict, &globals->util, globals->draw, &globals->gState.cp, palm_send, globals ); } stream_destroy( recStream ); } return success; } /* loadCurrentGame */ static void initUtilFuncs( PalmAppGlobals* globals ) { UtilVtable* vtable = globals->util.vtable = XP_MALLOC( globals->mpool, sizeof( UtilVtable ) ); globals->util.closure = (void*)globals; globals->util.gameInfo = &globals->gameInfo; MPASSIGN( globals->util.mpool, globals->mpool ); vtable->m_util_getVTManager = palm_util_getVTManager; vtable->m_util_userError = palm_util_userError; vtable->m_util_getSquareBonus = palm_util_getSquareBonus; vtable->m_util_userQuery = palm_util_userQuery; vtable->m_util_userPickTile = palm_util_userPickTile; vtable->m_util_askPassword = palm_util_askPassword; vtable->m_util_trayHiddenChange = palm_util_trayHiddenChange; vtable->m_util_yOffsetChange = palm_util_yOffsetChange; vtable->m_util_notifyGameOver = palm_util_notifyGameOver; vtable->m_util_hiliteCell = palm_util_hiliteCell; vtable->m_util_engineProgressCallback = palm_util_engineProgressCallback; vtable->m_util_setTimer = palm_util_setTimer; vtable->m_util_requestTime = palm_util_requestTime; vtable->m_util_getCurSeconds = palm_util_getCurSeconds; vtable->m_util_makeEmptyDict = palm_util_makeEmptyDict; #ifndef XWFEATURE_STANDALONE_ONLY vtable->m_util_makeStreamFromAddr = palm_util_makeStreamFromAddr; #endif vtable->m_util_getUserString = palm_util_getUserString; vtable->m_util_warnIllegalWord = palm_util_warnIllegalWord; #ifdef BEYOND_IR vtable->m_util_addrChange = palm_util_addrChange; #endif #ifdef XWFEATURE_SEARCHLIMIT vtable->m_util_getTraySearchLimits = palm_util_getTraySearchLimits; #endif #ifdef SHOW_PROGRESS vtable->m_util_engineStarting = palm_util_engineStarting; vtable->m_util_engineStopping = palm_util_engineStopping; #endif } /* initUtilFuncs */ #ifdef IR_SUPPORT #ifndef IR_EXCHMGR /* Make the IR library available if possible. Or should this be put off * until we know we're not playing stand-alone? */ static void ir_setup( PalmAppGlobals* globals ) { Err err; UInt16 refNum; if ( globals->irLibRefNum == 0 ) { /* been inited before? */ err = SysLibFind( irLibName, &refNum ); XP_ASSERT( !err ); if ( err == 0 ) { /* we have the library */ err = IrOpen( refNum, irOpenOptSpeed9600 ); XP_ASSERT( !err ); if ( err == 0 ) { /* IrSetConTypeLMP( &globals->irC_in.irCon ); */ IrSetConTypeLMP( &globals->irC_out.irCon ); /* globals->irC_in.globals = globals; */ /* err = IrBind( refNum, (IrConnect*)&globals->irC_in, */ /* ir_callback_in ); */ /* XP_ASSERT( !err ); */ globals->irC_out.globals = globals; err = IrBind( refNum, &globals->irC_out.irCon, ir_callback_out); XP_ASSERT( !err ); if ( err == 0 ) { XP_DEBUGF( "con->lLsap == %d", globals->irC_out.irCon.lLsap ); if (IR_STATUS_SUCCESS == IrSetDeviceInfo( refNum, deviceInfo, sizeof(deviceInfo) ) ) { #if 1 xwordsIRObject.attribs = (IrIasAttribute*)&xwordsIRAttribs; IrIAS_SetDeviceName( refNum, deviceName, sizeof(deviceName)); IrIAS_Add( refNum, &xwordsIRObject ); #endif globals->irLibRefNum = refNum; } else { IrUnbind( refNum, &globals->irC_out.irCon ); IrClose( refNum ); } } else { IrClose( refNum ); } } } } ir_cleanup( globals ); } /* ir_setup */ # endif #endif #ifdef COLOR_SUPPORT static void loadColorsFromRsrc( DrawingPrefs* prefs, MemHandle colorH ) { RGBColorType color; UInt8* colorP; short index = 0; UInt32 count; count = MemHandleSize( colorH ); XP_ASSERT( count < 0xFFFF ); XP_ASSERT( (((XP_U16)count) % 3) == 0 ); colorP = MemHandleLock( colorH ); do { color.r = *colorP++; color.g = *colorP++; color.b = *colorP++; prefs->drawColors[index++] = WinRGBToIndex( &color ); } while ( (count -= 3) != 0 ); MemHandleUnlock( colorH ); } /* loadColorsFromRsrc */ #endif static void palmInitPrefs( PalmAppGlobals* globals ) { globals->gState.showGrid = true; globals->gState.versionNum = CUR_PREFS_VERS; globals->gState.cp.showBoardArrow = XP_TRUE; #ifdef SHOW_PROGRESS globals->gState.showProgress = true; #endif } /* palmInitPrefs */ static void openXWPrefsDB( PalmAppGlobals* globals ) { Err err; err = DmCreateDatabase( CARD_0, XW_PREFS_DBNAME, APPID, XWORDS_PREFS_TYPE, true ); XP_ASSERT( err == errNone || err == dmErrAlreadyExists ); globals->boardDBID = DmFindDatabase( CARD_0, XW_PREFS_DBNAME ); globals->boardDBP = DmOpenDatabase( CARD_0, globals->boardDBID, dmModeWrite ); } /* openXWPrefsDB */ static XP_Bool setupBonusPtrs( PalmAppGlobals* globals ) { XP_U16 i; for ( i = 0; i < NUM_BOARD_SIZES; ++i ) { MemHandle hand = findXWPrefsRsrc( globals, BOARD_RES_TYPE, BOARD_RES_ID + i ); if ( !hand ) { return XP_FALSE; } XP_ASSERT( MemHandleLockCount(hand) == 0 ); globals->bonusResPtr[i] = MemHandleLock( hand ); } return XP_TRUE; } /* setupBonusPtrs */ static void unlockBonusPtrs( PalmAppGlobals* globals ) { XP_U16 i; for ( i = 0; i < NUM_BOARD_SIZES; ++i ) { MemPtrUnlock( (MemPtr)globals->bonusResPtr[i] ); } } /* unlockBonusPtrs */ static void openGamesDB( PalmAppGlobals* globals ) { Err err; err = DmCreateDatabase( CARD_0, XW_GAMES_DBNAME, APPID, XWORDS_GAMES_TYPE, false ); globals->gamesDBID = DmFindDatabase( CARD_0, XW_GAMES_DBNAME ); globals->gamesDBP = DmOpenDatabase( CARD_0, globals->gamesDBID, dmModeReadWrite ); XP_ASSERT( !!globals->gamesDBP ); } /* openGamesDB */ static MemHandle findXWPrefsRsrc( PalmAppGlobals* globals, UInt32 resType, UInt16 resID ) { Int16 index; MemHandle handle = NULL; Boolean beenThere = false; for ( ; ; ) { XP_ASSERT( !!globals->boardDBP ); index = DmFindResource( globals->boardDBP, resType, resID, NULL ); if ( index == -1 ) { /* not found */ MemHandle builtinH; MemHandle newH; UInt32 size; if ( beenThere ) { return NULL; } builtinH = DmGetResource( resType, resID ); XP_ASSERT( !!builtinH ); size = MemHandleSize( builtinH ); newH = DmNewResource( globals->boardDBP, resType, resID, size ); XP_ASSERT( !!newH ); DmWrite( MemHandleLock( newH ), 0, MemHandleLock(builtinH), size ); MemHandleUnlock( newH ); MemHandleUnlock( builtinH ); DmReleaseResource( newH ); DmReleaseResource( builtinH ); beenThere = true; continue; } handle = DmGetResourceIndex( globals->boardDBP, index ); break; } return handle; } /* findXWPrefsRsrc */ static XP_Bool initResources( PalmAppGlobals* globals ) { /* strings */ MemHandle hand; XP_ASSERT( !globals->stringsResPtr ); hand = DmGetResource( STRL_RES_TYPE, STRL_RES_ID ); XP_ASSERT( !!hand ); XP_ASSERT( MemHandleLockCount(hand) == 0 ); globals->stringsResPtr = (XP_UCHAR*)MemHandleLock( hand ); /* bonus square and color values. These live in a separate database, which we create if it doesn't already exist. */ openXWPrefsDB( globals ); if ( !setupBonusPtrs( globals ) ) { return XP_FALSE; } openGamesDB( globals ); if ( globals->able == COLOR ) { hand = findXWPrefsRsrc( globals, COLORS_RES_TYPE, COLORS_RES_ID ); if ( !hand ) { return XP_FALSE; } loadColorsFromRsrc( &globals->drawingPrefs, hand ); DmReleaseResource( hand ); } return XP_TRUE; } /* initResources */ static void freeAndUnlockPtr( MemPtr ptr ) { MemHandle hand; XP_ASSERT( !!ptr ); hand = MemPtrRecoverHandle(ptr ); XP_ASSERT( !!hand ); MemHandleUnlock( hand ); DmReleaseResource( hand ); } /* freeAndUnlockPtr */ static void uninitResources( PalmAppGlobals* globals ) { XP_U16 i; /* strings */ freeAndUnlockPtr( globals->stringsResPtr ); globals->stringsResPtr = NULL; /* bonus square values */ for ( i = 0; i < NUM_BOARD_SIZES; ++i ) { freeAndUnlockPtr( globals->bonusResPtr[i] ); } XP_ASSERT( !!globals->boardDBP ); DmCloseDatabase( globals->boardDBP ); } /* uninitResources */ XP_UCHAR* getResString( PalmAppGlobals* globals, XP_U16 strID ) { XP_ASSERT( !!globals->stringsResPtr ); XP_ASSERT( strID < MemPtrSize( globals->stringsResPtr ) ); XP_ASSERT( (strID == 0) || (globals->stringsResPtr[strID-1] == '\0') ); XP_ASSERT( strID < STR_LAST_STRING ); return &globals->stringsResPtr[strID]; } /* getResString */ static Err volChangeEventProc( SysNotifyParamType* XP_UNUSED_SILK(notifyParamsP) ) { #ifndef REALLY_HANDLE_MEDIA EventType eventToPost; #endif #if 0 if ( notifyParamsP->notifyType == sysNotifyVolumeUnmountedEvent ) { DictListHandleUnmount( globals->dictList ); } else if ( notifyParamsP->notifyType == sysNotifyVolumeMountedEvent ) { DictListHandleMount( &globals->dictList ); } else { XP_ASSERT(0); return errNone; } #endif #ifdef FEATURE_SILK if ( notifyParamsP->notifyType == sysNotifyDisplayChangeEvent ) { eventToPost.eType = doResizeWinEvent; EvtAddEventToQueue( &eventToPost ); return errNone; } #endif /* for now, just blow outta here! Force the app to rebuild datastructures when it's relaunched. This is a hack but I like it. :-) */ #ifndef REALLY_HANDLE_MEDIA eventToPost.eType = appStopEvent; EvtAddEventToQueue( &eventToPost ); #endif return errNone; } /* volChangeEventProc */ static void doCallbackReg( PalmAppGlobals* globals, XP_Bool reg ) { /* The mounted/unmounted events aren't there unless we're PalmOS version 4.0 or greater. No need to use FtrGet to check for Notification Mgr here, as it's useless without these. */ if ( globals->romVersion >= 40 ) { XP_U16 i; UInt32 notifyTypes[] = { sysNotifyVolumeUnmountedEvent , sysNotifyVolumeMountedEvent #ifdef FEATURE_SILK , sysNotifyDisplayChangeEvent #endif }; for ( i = 0; i < sizeof(notifyTypes) / sizeof(notifyTypes[0]); ++i ) { UInt32 notifyType = notifyTypes[i]; if ( reg ) { SysNotifyRegister( 0, 0, notifyType, volChangeEventProc, sysNotifyNormalPriority, globals ); } else { SysNotifyUnregister( 0, 0, notifyType, sysNotifyNormalPriority); } } } } /* doCallbackReg */ /* temp workarounds for some sony include file trouble */ # ifdef FEATURE_SILK extern Err SilkLibEnableResizeFoo(UInt16 refNum) SILK_LIB_TRAP(sysLibTrapCustom+1); extern Err VskSetStateFoo(UInt16 refNum, UInt16 stateType, UInt16 state) SILK_LIB_TRAP(sysLibTrapCustom+3+3); # endif static void initHighResGlobals( PalmAppGlobals* globals ) { Err err; XP_U32 vers; err = FtrGet( sysFtrCreator, sysFtrNumWinVersion, &vers ); globals->hasHiRes = ( err == errNone && vers >= 4 ); globals->oneDotFiveAvail = ( err == errNone && vers >= 5 ); XP_LOGF( "hasHiRes = %d", globals->hasHiRes ); #ifdef FEATURE_SILK if ( globals->hasHiRes ) { XP_U16 ref; err = SysLibFind(sonySysLibNameSilk, &ref ); if ( err == sysErrLibNotFound ) { err = SysLibLoad( 'libr', sonySysFileCSilkLib, &ref ); } if ( err == errNone ) { XP_U32 tmp; globals->sonyLibRef = ref; err = FtrGet( sonySysFtrCreator, sonySysFtrNumVskVersion, &tmp ); if ( err == errNone ) { globals->doVSK = XP_TRUE; if ( VskOpen( ref ) == errNone ) { VskSetStateFoo( ref, vskStateEnable, 1 ); } } else { if ( SilkLibOpen( ref ) == errNone ) { SilkLibEnableResizeFoo( ref ); } } } } #endif } /* initHighResGlobals */ static void uninitHighResGlobals( PalmAppGlobals* XP_UNUSED_SILK(globals) ) { #ifdef FEATURE_SILK if ( globals->hasHiRes && globals->sonyLibRef != 0 ) { if ( globals->doVSK ) { VskClose( globals->sonyLibRef ); } else { SilkLibClose( globals->sonyLibRef ); } } #endif } /* uninitHighResGlobals */ static XP_Bool canConvertPrefs( XWords4PreferenceType* prefs, UInt16 prefSize, XP_S16 vers ) { XP_Bool success = XP_FALSE; if ( vers == VERSION_NUM_405 ) { if ( prefSize < sizeof(XWords4PreferenceType) ) { XP_U8* newRgn = ((XP_U8*)prefs) + prefSize; XP_MEMSET( newRgn, 0, sizeof(XWords4PreferenceType) - prefSize ); } success = XP_TRUE; } return success; } /* canConvertPrefs */ /***************************************************************************** * ****************************************************************************/ static XP_Bool startApplication( PalmAppGlobals** globalsP ) { UInt16 prefSize; Boolean prefsFound; XWords4PreferenceType prefs; PalmAppGlobals* globals; Boolean leftyFlag; Int16 vers; UInt32 ignore; MPSLOT; #if defined FOR_GREMLINS SysRandom( 1 ); #else SysRandom( TimGetTicks() ); /* initialize */ #endif #ifdef MEM_DEBUG mpool = mpool_make(); #endif globals = (PalmAppGlobals*)XP_MALLOC( mpool, sizeof( PalmAppGlobals ) ); *globalsP = globals; setFormRefcon( globals ); XP_MEMSET( globals, 0, sizeof(PalmAppGlobals) ); MPASSIGN( globals->mpool, mpool ); initHighResGlobals( globals ); getSizes( globals ); globals->runningOnPOSE = FtrGet( 'pose', 0, &ignore) != ftrErrNoSuchFeature; globals->vtMgr = make_vtablemgr( MPPARM_NOCOMMA(globals->mpool) ); globals->romVersion = romVersion(); globals->isFirstLaunch = true; leftyFlag = 0; if ( !PrefGetAppPreferencesV10('Lfty', 1, &leftyFlag, sizeof(leftyFlag) )) { leftyFlag = 0; } globals->isLefty = leftyFlag != 0; #ifdef COLOR_SUPPORT if ( (globals->romVersion >= 35) && (cur_screen_depth() >= 8) ) { globals->able = COLOR; } else { globals->able = ONEBIT; } #else globals->able = ONEBIT; #endif if ( !initResources( globals ) ) { return XP_FALSE; } #ifdef BEYOND_IR palm_ip_setup( globals ); #endif doCallbackReg( globals, XP_TRUE ); initUtilFuncs( globals ); offerConvertOldDicts( globals ); globals->dictList = DictListMake( MPPARM_NOCOMMA(globals->mpool) ); if ( DictListCount( globals->dictList ) == 0 ) { userErrorFromStrId( globals, STR_NO_DICT_INSTALLED ); return XP_FALSE; } prefSize = sizeof( prefs ); vers = PrefGetAppPreferences( AppType, PrefID, &prefs, &prefSize, true); if ( vers == VERSION_NUM ) { prefsFound = XP_TRUE; } else if ( vers != noPreferenceFound ) { prefsFound = canConvertPrefs( &prefs, prefSize, vers ); } else { prefsFound = XP_FALSE; } if ( prefsFound ) { prefs.versionNum = XP_NTOHS( prefs.versionNum ); prefs.curGameIndex = XP_NTOHS( prefs.curGameIndex ); MemMove( &globals->gState, &prefs, sizeof(prefs) ); } globals->draw = palm_drawctxt_make( MPPARM(globals->mpool) globals->able, globals, getResString, &globals->drawingPrefs ); FrmGotoForm( XW_MAIN_FORM ); /* do this first so players who don't exist have default names */ gi_initPlayerInfo( MEMPOOL &globals->gameInfo, getResString( globals, STR_DEFAULT_NAME ) ); if ( prefsFound && loadCurrentGame( globals, globals->gState.curGameIndex, &globals->game, &globals->gameInfo) ) { EventType eventToPost; eventToPost.eType = loadGameEvent; EvtAddEventToQueue( &eventToPost ); globals->isFirstLaunch = false; } else { DictListEntry* dlep; XP_U32 gameID; /* if we're here because dict missing, don't re-init all prefs! */ if ( !prefsFound ) { palmInitPrefs( globals ); } else if ( checkUserName() ) { /* increment count so we get a new game rather than replace existing one. We want it still there if somebody puts the missing dict back. */ globals->gState.curGameIndex = countGameRecords( globals ); } globals->isNewGame = true; getNthDict( globals->dictList, 0, &dlep ); globals->gameInfo.dictName = copyString( MPPARM(globals->mpool) dlep->baseName ); gameID = TimGetSeconds(); game_makeNewGame( MEMPOOL &globals->game, &globals->gameInfo, &globals->util, globals->draw, gameID, &globals->gState.cp, palm_send, globals ); FrmPopupForm( XW_NEWGAMES_FORM ); } return XP_TRUE; } /* startApplication */ /* save the stream's contents to a database. */ static void writeToDb( XWStreamCtxt* stream, void* closure ) { PalmAppGlobals* globals = (PalmAppGlobals*)closure; Err err; err = DmCreateDatabase( CARD_0, XW_GAMES_DBNAME, APPID, XWORDS_GAMES_TYPE, false ); streamToGameRecord( globals, stream, globals->gState.curGameIndex ); } /* writeToDb */ static void saveOpenGame( PalmAppGlobals* globals ) { if ( !!globals->game.server ) { XWStreamCtxt* memStream; DictionaryCtxt* dict; XP_UCHAR* dictName; char namebuf[MAX_GAMENAME_LENGTH]; if ( gi_countHumans( &globals->gameInfo ) > 1 ) { board_hideTray( globals->game.board ); /* so won't be visible when next opened */ } memStream = makeSimpleStream( globals, writeToDb ); stream_open( memStream ); /* write the things's name. Name is first because we want to be able to manipulate it without knowing about the other stuff. */ nameFromRecord( globals, globals->gState.curGameIndex, namebuf ); stream_putBytes( memStream, namebuf, MAX_GAMENAME_LENGTH ); saveGamePrefs( /*globals, */memStream ); /* the dictionary */ dict = model_getDictionary( globals->game.model ); dictName = !!dict? dict_getName( dict ) : NULL; stream_putU8( memStream, !!dictName ); if ( !!dictName ) { stringToStream( memStream, dictName ); } game_saveToStream( &globals->game, &globals->gameInfo, memStream ); stream_destroy( memStream ); } } /* saveOpenGame */ /***************************************************************************** * ****************************************************************************/ static void stopApplication( PalmAppGlobals* globals ) { if ( globals != NULL ) { MPSLOT; saveOpenGame( globals ); FrmCloseAllForms(); uninitResources( globals ); /* Write the state information -- once we're ready to read it in. But skip the save if user cancelled launching the first time. */ if ( !globals->isFirstLaunch ) { XWords4PreferenceType prefs; /* temporarily don't save prefs since we crash on opening them. */ XP_MEMCPY( &prefs, &globals->gState, sizeof(prefs) ); prefs.versionNum = XP_HTONS( prefs.versionNum ); prefs.curGameIndex = XP_HTONS( prefs.curGameIndex ); PrefSetAppPreferences( AppType, PrefID, VERSION_NUM, &prefs, sizeof(prefs), true ); } if ( !!globals->draw ) { palm_drawctxt_destroy( globals->draw ); } game_dispose( &globals->game ); gi_disposePlayerInfo( MEMPOOL &globals->gameInfo ); #ifdef IR_SUPPORT #ifdef BEYOND_IR palm_ip_close( globals ); #endif #endif #ifdef XWFEATURE_PALM_BLUETOOTH palm_bt_close( globals ); #endif if ( !!globals->dictList ) { DictListFree( MPPARM(globals->mpool) globals->dictList ); } if ( !!globals->util.vtable ) { XP_FREE( globals->mpool, globals->util.vtable ); } if ( !!globals->prefsDlgState ) { XP_FREE( globals->mpool, globals->prefsDlgState ); } #if defined OWNER_HASH || defined NO_REG_REQUIRED if ( !!globals->savedGamesState && !globals->isFirstLaunch ) { freeSavedGamesData( MPPARM(globals->mpool) globals->savedGamesState ); XP_FREE( globals->mpool, globals->savedGamesState ); } #endif uninitHighResGlobals( globals ); XP_ASSERT( !!globals->gamesDBP ); DmCloseDatabase( globals->gamesDBP ); if ( !!globals->vtMgr ) { vtmgr_destroy( MPPARM(globals->mpool) globals->vtMgr ); } doCallbackReg( globals, XP_FALSE ); MPASSIGN( mpool, globals->mpool ); XP_FREE( globals->mpool, globals ); mpool_destroy( mpool ); } } /* stopApplication */ static Int32 figureWaitTicks( PalmAppGlobals* globals ) { Int32 result = evtWaitForever; XP_U32 when; XWTimerReason why; if ( 0 ) { #ifdef BEYOND_IR } else if ( ipSocketIsOpen(globals) ) { /* we'll do our sleeping in NetLibSelect */ result = 0; # ifdef XWFEATURE_PALM_BLUETOOTH } else if ( btSocketIsOpen(globals) ) { /* From Whiteboard. But: what to use here? BTLib needs nil events AFAIK. */ result = SysTicksPerSecond() / 10; # endif #endif } else if ( globals->timeRequested || globals->hintPending ) { result = 0; } else if ( timeForTimer( globals, &why, &when ) ) { result = when - TimGetTicks(); if ( result < 0 ) { result = 0; } } else { /* leave it */ } /* XP_DEBUGF( "figureWaitTicks returning %d", result ); */ return result; } /* figureWaitTicks */ static XP_Bool closeNonMainForms( PalmAppGlobals* globals ) { #if 1 return FrmGetActiveForm() == globals->mainForm; #else /* This doesn't work. If there's a form in front of the main form sending it the close event closes it, but then FrmGetActiveForm() returns null the next time called.*/ FormPtr prevActive; FormPtr curActive = NULL; for ( ; ; ) { EventType event; prevActive = curActive; curActive = FrmGetActiveForm(); if ( prevActive == curActive ) { return XP_FALSE; } if ( curActive == globals->mainForm ) { return XP_TRUE; } event.eType = frmCloseEvent; event.data.frmClose.formID = FrmGetFormId(curActive); FrmDispatchEvent( &event ); } #endif } /* closeNonMainForms */ /***************************************************************************** * ****************************************************************************/ static void eventLoop( PalmAppGlobals* globals ) { EventType event; do { #ifdef BEYOND_IR if ( !!globals->game.comms && (comms_getConType(globals->game.comms) == COMMS_CONN_RELAY) ) { checkHandleNetEvents( globals ); } #endif /* EvtGetEvent( &event, evtWaitForever ); */ EvtGetEvent( &event, figureWaitTicks(globals) ); if ( event.eType == keyDownEvent ) { if ( 0 ) { #ifdef FOR_GREMLINS } else if ( event.data.keyDown.chr == findChr ) { continue; #endif } else if ( (event.data.keyDown.modifiers & commandKeyMask) != 0 && ( (event.data.keyDown.chr == autoOffChr) || (event.data.keyDown.chr == hardPowerChr) ) && !!globals->game.board ) { if ( !globals->menuIsDown /* hi Marcus :-) */ && closeNonMainForms(globals) && gi_countHumans( &globals->gameInfo ) > 1 && board_hideTray( globals->game.board ) ) { board_draw( globals->game.board ); } } } /* Give the system a chance to handle the event. */ if ( !SysHandleEvent(&event)) { UInt16 error; if ( !MenuHandleEvent( NULL, &event, &error)) { if ( !applicationHandleEvent( &event )) { FrmDispatchEvent(&event); } } } } while (event.eType != appStopEvent); } /* eventLoop */ /********************************************************************** * applicationHandleEvent **********************************************************************/ static Boolean applicationHandleEvent( EventPtr event ) { FormPtr frm = NULL; Int16 formId; Boolean result = false; FormEventHandlerType* handler = NULL; if ( event->eType == frmLoadEvent ) { /*Load the form resource specified in the event then activate the form.*/ formId = event->data.frmLoad.formID; frm = FrmInitForm(formId); FrmSetActiveForm(frm); switch (formId) { case XW_MAIN_FORM: handler = mainViewHandleEvent; break; case XW_NEWGAMES_FORM: handler = newGameHandleEvent; break; case XW_DICTINFO_FORM: handler = dictFormHandleEvent; break; case XW_PREFS_FORM: handler = PrefsFormHandleEvent; break; #ifdef BEYOND_IR case XW_CONNS_FORM: handler = ConnsFormHandleEvent; break; #endif #if defined OWNER_HASH || defined NO_REG_REQUIRED case XW_SAVEDGAMES_DIALOG_ID: handler = savedGamesHandleEvent; break; #endif } if ( !!handler ) { XP_ASSERT( !!frm ); result = true; FrmSetEventHandler( frm, handler ); } } return result; } // applicationHandleEvent #if 0 static void destroy_on_close( XWStreamCtxt* p_stream ) { MemStreamCtxt* stream = (MemStreamCtxt*)p_stream; /* PalmAppGlobals* globals = stream->globals; */ MemHandle handle; XP_WARNF( "destroy_on_close called" ); handle = stream->bufHandle; MemHandleFree( handle ); stream_destroy( p_stream ); } /* destroy_on_close */ #endif static void palmFireTimer( PalmAppGlobals* globals, XWTimerReason why ) { TimerProc proc = globals->timerProcs[why]; void* closure = globals->timerClosures[why]; XP_ASSERT( TimGetTicks() >= globals->timerFireAt[why] ); globals->timerProcs[why] = NULL; (*proc)( closure, why ); } /* fireTimer */ static XP_Bool timeForTimer( PalmAppGlobals* globals, XWTimerReason* why, XP_U32* when ) { XP_U16 i; XWTimerReason nextWhy = 0; XP_U32 nextWhen = 0xFFFFFFFF; XP_Bool found; for ( i = 1; i < NUM_TIMERS_PLUS_ONE; ++i ) { if ( (globals->timerProcs[i] != NULL) && (globals->timerFireAt[i] < nextWhen) ) { nextWhy = i; nextWhen = globals->timerFireAt[i]; } } found = nextWhy != 0; if ( found ) { *why = nextWhy; *when = nextWhen; } return found; } /* timeForTimer */ static Boolean handleNilEvent( PalmAppGlobals* globals ) { Boolean handled = true; XP_U32 when; XWTimerReason why; if ( globals->menuIsDown ) { /* do nothing */ } else if ( globals->hintPending ) { handled = handleHintRequest( globals ); } else if ( timeForTimer( globals, &why, &when ) && (when <= TimGetTicks()) ) { palmFireTimer( globals, why ); } else if ( globals->timeRequested ) { globals->timeRequested = false; if ( globals->msgReceivedDraw ) { XP_ASSERT ( !!globals->game.board ); board_draw( globals->game.board ); globals->msgReceivedDraw = XP_FALSE; } handled = server_do( globals->game.server ); } else { handled = false; } return handled; } /* handleNilEvent */ static Boolean handleFlip( PalmAppGlobals* globals ) { XP_ASSERT( !!globals->game.board ); return board_flip( globals->game.board ); } /* handle_flip_button */ static Boolean handleValueToggle( PalmAppGlobals* globals ) { return board_toggle_showValues( globals->game.board ); } /* handleValueToggle */ static Boolean handleHideTray( PalmAppGlobals* globals ) { Boolean draw; if ( TRAY_REVEALED == board_getTrayVisState( globals->game.board ) ) { draw = board_hideTray( globals->game.board ); } else { draw = board_showTray( globals->game.board ); } return draw; } /* handleHideTray */ #ifdef XWFEATURE_SEARCHLIMIT static Boolean popupLists( EventPtr event ) { Boolean handled = false; XP_U16 ctlID; ListPtr list = NULL; XP_S16 chosen; if ( event->eType == ctlSelectEvent ) { ctlID = event->data.ctlSelect.controlID; if ( ctlID == XW_HINTCONFIG_MINSELECTOR_ID ) { list = getActiveObjectPtr( XW_HINTCONFIG_MINLIST_ID ); } else if ( ctlID == XW_HINTCONFIG_MAXSELECTOR_ID ) { list = getActiveObjectPtr( XW_HINTCONFIG_MAXLIST_ID ); } if ( !!list ) { chosen = LstPopupList( list ); if ( chosen >= 0 ) { setSelectorFromList( ctlID, list, chosen ); } handled = true; } } return handled; } /* popupLists */ static XP_Bool doHintConfig( XP_U16* minP, XP_U16* maxP ) { FormPtr form, prevForm; ListPtr listMin, listMax; XP_Bool confirmed; prevForm = FrmGetActiveForm(); form = FrmInitForm( XW_HINTCONFIG_FORM_ID ); FrmSetEventHandler( form, popupLists ); FrmSetActiveForm( form ); listMin = getActiveObjectPtr( XW_HINTCONFIG_MINLIST_ID ); LstSetSelection( listMin, *minP - 1 ); setSelectorFromList( XW_HINTCONFIG_MINSELECTOR_ID, listMin, *minP - 1 ); listMax = getActiveObjectPtr( XW_HINTCONFIG_MAXLIST_ID ); LstSetSelection( listMax, *maxP - 1 ); setSelectorFromList( XW_HINTCONFIG_MAXSELECTOR_ID, listMax, *maxP - 1 ); confirmed = FrmDoDialog( form ) == XW_HINTCONFIG_OK_ID; if ( confirmed ) { *minP = LstGetSelection( listMin ) + 1; *maxP = LstGetSelection( listMax ) + 1; } FrmDeleteForm( form ); FrmSetActiveForm( prevForm ); return confirmed; } /* doHintConfig */ #endif static Boolean handleHintRequest( PalmAppGlobals* globals ) { Boolean notDone; Boolean draw; XP_ASSERT( !!globals->game.board ); draw = board_requestHint( globals->game.board, #ifdef XWFEATURE_SEARCHLIMIT globals->askTrayLimits, #endif ¬Done ); globals->hintPending = notDone; return draw; } /* handleHintRequest */ static Boolean handleDone( PalmAppGlobals* globals ) { return board_commitTurn( globals->game.board ); } /* handleDone */ static Boolean handleJuggle( PalmAppGlobals* globals ) { return board_juggleTray( globals->game.board ); } /* handleJuggle */ static Boolean handleTrade( PalmAppGlobals* globals ) { return board_beginTrade( globals->game.board ); } /* handleJuggle */ static Boolean buttonIsUsable( ControlPtr button ) { return CtlEnabled( button ); } /* buttonIsUsable */ static void drawBitmapButton( PalmAppGlobals* globals, UInt16 ctrlID, UInt16 resID, XP_Bool eraseIfDisabled ) { FormPtr form = FrmGetActiveForm(); UInt16 index = FrmGetObjectIndex( form, ctrlID ); RectangleType bounds; FrmGetObjectBounds( form, index, &bounds ); if ( buttonIsUsable( getActiveObjectPtr( ctrlID ) ) ) { draw_drawBitmapAt( globals->draw, resID, bounds.topLeft.x, bounds.topLeft.y ); } else if ( eraseIfDisabled ) { /* gross hack; the button represents a larger bitmap; erase the whole thing.*/ #ifndef EIGHT_TILES if ( ctrlID == XW_MAIN_HIDE_BUTTON_ID ) { bounds.extent.x += TRAY_BUTTON_WIDTH + 1; bounds.extent.y += TRAY_BUTTON_WIDTH + 1; } #endif WinEraseRectangle( &bounds, 0 ); } } /* drawBitmapButton */ static void drawFormButtons( PalmAppGlobals* globals ) { XP_U16 pairs[] = { XW_MAIN_FLIP_BUTTON_ID, FLIP_BUTTON_BMP_RES_ID, XP_TRUE, XW_MAIN_VALUE_BUTTON_ID, VALUE_BUTTON_BMP_RES_ID, XP_TRUE, XW_MAIN_HINT_BUTTON_ID, HINT_BUTTON_BMP_RES_ID, XP_TRUE, #ifndef EIGHT_TILES XW_MAIN_HIDE_BUTTON_ID, TRAY_BUTTONS_BMP_RES_ID, XP_TRUE, #endif XW_MAIN_SHOWTRAY_BUTTON_ID, SHOWTRAY_BUTTON_BMP_RES_ID, XP_FALSE, 0, }; XP_U16* pair = (XP_U16*)pairs; if ( FrmGetActiveFormID() == XW_MAIN_FORM ) { while ( !!*pair ) { drawBitmapButton( globals, pair[0], pair[1], pair[2] ); pair += 3; } } } /* drawFormButtons */ static void updateScrollbar( PalmAppGlobals* globals, Int16 newValue ) { if ( FrmGetActiveFormID() == XW_MAIN_FORM ) { ScrollBarPtr scroll = getActiveObjectPtr( XW_MAIN_SCROLLBAR_ID ); XW_TrayVisState state = board_getTrayVisState( globals->game.board ); XP_U16 max, min; max = model_numRows( globals->game.model ); min = max; if ( globals->needsScrollbar && (max == SBAR_MAX) && state != TRAY_HIDDEN ) { min -= 2; /* fragile!!! PENDING */ } SclSetScrollBar( scroll, newValue + min, min, max, SBAR_PAGESIZE ); } } /* updateScrollbar */ static void palmSetCtrlsForTray( PalmAppGlobals* globals ) { XW_TrayVisState state = board_getTrayVisState( globals->game.board ); FormPtr form = globals->mainForm; /* In rare circumstances, e.g. when an appStopEvent comes in while the prefs dialog is up, this'll get called when the main form's not on top. In that case it's probably ok to just do nothing. But if not I'll need to queue an event of some sort so it gets done later. */ if ( FrmGetActiveFormID() == XW_MAIN_FORM ) { disOrEnable( form, XW_MAIN_HINT_BUTTON_ID, (state==TRAY_REVEALED) && !globals->gameInfo.hintsNotAllowed ); #ifndef EIGHT_TILES disOrEnable( form, XW_MAIN_DONE_BUTTON_ID, state==TRAY_REVEALED ); disOrEnable( form, XW_MAIN_JUGGLE_BUTTON_ID, state==TRAY_REVEALED ); disOrEnable( form, XW_MAIN_TRADE_BUTTON_ID, state==TRAY_REVEALED ); disOrEnable( form, XW_MAIN_HIDE_BUTTON_ID, state!=TRAY_HIDDEN ); #endif disOrEnable( form, XW_MAIN_SHOWTRAY_BUTTON_ID, state==TRAY_HIDDEN && globals->gameInfo.nPlayers > 0 ); globals->scrollValue = board_getYOffset( globals->game.board ); updateScrollbar( globals, globals->scrollValue ); /* PENDING(ehouse) Can't the board just do this itself? */ if ( state==TRAY_HIDDEN ) { board_setYOffset( globals->game.board, 0 ); } } } /* palmSetCtrlsForTray */ static Boolean scrollBoard( PalmAppGlobals* globals, Int16 newValue, Boolean fromBar ) { XP_Bool result = XP_FALSE; XP_U16 curYOffset; XP_ASSERT( !!globals->game.board ); curYOffset = board_getYOffset( globals->game.board ); result = curYOffset != newValue; if ( result ) { result = board_setYOffset( globals->game.board, newValue ); } if ( !fromBar ) { updateScrollbar( globals, newValue ); } return result; } /* scrollBoard */ /* We can't create the board back in newgame.c because the wrong form's * frontmost at that point. So we do it here instead -- and must also call * server_do. */ static void initAndStartBoard( PalmAppGlobals* globals, XP_Bool newGame ) { DictionaryCtxt* dict; XP_UCHAR* newDictName = globals->gameInfo.dictName; /* This needs to happen even when !newGame because it's how the dict slots in PlayerInfo get loaded. That really ought to happen earlier, though. */ XP_ASSERT( !!globals->game.model ); dict = model_getDictionary( globals->game.model ); if ( !!dict ) { XP_UCHAR* dictName = dict_getName( dict ); if ( !!newDictName && 0 != XP_STRCMP( (const char*)dictName, (const char*)newDictName ) ) { dict_destroy( dict ); dict = NULL; } else { replaceStringIfDifferent( MEMPOOL &globals->gameInfo.dictName, dictName ); } } if ( !dict ) { XP_ASSERT( !!newDictName ); dict = palm_dictionary_make( MPPARM(globals->mpool) globals, copyString( MEMPOOL newDictName ), globals->dictList ); XP_ASSERT( !!dict ); model_setDictionary( globals->game.model, dict ); } if ( newGame ) { XP_U32 newGameID = TimGetSeconds(); game_reset( MEMPOOL &globals->game, &globals->gameInfo, &globals->util, newGameID, &globals->gState.cp, palm_send, globals ); #ifdef BEYOND_IR if ( !!globals->game.comms ) { comms_setAddr( globals->game.comms, &globals->newGameState.addr ); } #endif } XP_ASSERT( !!globals->game.board ); getSizes( globals ); (void)positionBoard( globals ); #ifdef IR_SUPPORT # ifndef IR_EXCHMGR if ( globals->gameInfo.serverRole == SERVER_STANDALONE ) { ir_cleanup( globals ); } else { ir_setup( globals ); } # endif if ( newGame && globals->gameInfo.serverRole == SERVER_ISCLIENT ) { XWStreamCtxt* stream; XP_ASSERT( !!globals->game.comms ); stream = makeSimpleStream( globals, palm_send_on_close ); server_initClientConnection( globals->game.server, stream ); } #endif /* do this before drawing the board. If it assigns tiles, for example, that'll make a difference on the screen. */ (void)server_do( globals->game.server ); board_invalAll( globals->game.board ); board_draw( globals->game.board ); globals->isNewGame = false; } /* initAndStartBoard */ #ifdef DEBUG static void askOnClose( XWStreamCtxt* stream, void* closure ) { PalmAppGlobals* globals = (PalmAppGlobals*)closure; askFromStream( globals, stream, -1, false ); } /* askOnClose */ #endif static void updateForLefty( PalmAppGlobals* globals, FormPtr form ) { XP_S16 idsAndXs[] = { /* ButtonID, x-coord-when-lefty, */ XW_MAIN_FLIP_BUTTON_ID, 0, XW_MAIN_VALUE_BUTTON_ID, 0, XW_MAIN_HINT_BUTTON_ID, 0, XW_MAIN_SCROLLBAR_ID, 0, XW_MAIN_SHOWTRAY_BUTTON_ID, 0, #ifdef FOR_GREMLINS GREMLIN_BOARD_GADGET_IDAUTOID, 9, GREMLIN_TRAY_GADGET_IDAUTOID, 9, #endif #ifndef EIGHT_TILES XW_MAIN_HIDE_BUTTON_ID, -1, XW_MAIN_JUGGLE_BUTTON_ID, TRAY_BUTTON_WIDTH-1, XW_MAIN_TRADE_BUTTON_ID, -1, XW_MAIN_DONE_BUTTON_ID, TRAY_BUTTON_WIDTH-1, #endif 0, }; if ( globals->isLefty ) { UInt16 id; UInt16* idp = (UInt16*)idsAndXs; for ( id = *idp; !!id; id = *(idp+=2) ) { XP_S16 x, y; UInt16 objIndex = FrmGetObjectIndex( form, id ); FrmGetObjectPosition( form, objIndex, &x, &y ); FrmSetObjectPosition( form, objIndex, idp[1], y ); } } } /* updateForLefty */ static void beamBoard( PalmAppGlobals* globals ) { Err err; XP_UCHAR prcName[50]; unlockBonusPtrs( globals ); DmCloseDatabase( globals->boardDBP ); /* do we need to close the db first, and reopen after? */ XP_SNPRINTF( prcName, sizeof(prcName), (XP_UCHAR*)"%s.prc", XW_PREFS_DBNAME ); err = sendDatabase( CARD_0, globals->boardDBID, prcName, (XP_UCHAR*)"board prefs" ); globals->boardDBP = DmOpenDatabase( CARD_0, globals->boardDBID, dmModeWrite ); setupBonusPtrs( globals ); } /* beamBoard */ static XP_Bool considerMenuShow( EventPtr event ) { XP_S16 y = event->screenY; XP_Bool penInRightPlace = (y < PALM_BOARD_TOP) && (y >= 0); if ( penInRightPlace ) { EventType menuEvent; XP_MEMSET( &menuEvent, 0, sizeof(menuEvent) ); menuEvent.eType = keyDownEvent; menuEvent.data.keyDown.chr = menuChr; menuEvent.data.keyDown.keyCode = 0; menuEvent.data.keyDown.modifiers = commandKeyMask; EvtAddEventToQueue( &menuEvent ); } return penInRightPlace; } /* considerMenuShow */ /* Draw immediately, because we've made a change we need reflected immediately. */ static void drawChangedBoard( PalmAppGlobals* globals ) { if ( !!globals->game.board && !globals->menuIsDown ) { board_draw( globals->game.board ); } } /* drawChangedBoard */ static XP_Bool tryLoadSavedGame( PalmAppGlobals* globals, XP_U16 newIndex ) { XWGame tmpGame; CurGameInfo tmpGInfo; XP_Bool loaded; XP_MEMSET( &tmpGame, 0, sizeof(tmpGame) ); XP_MEMSET( &tmpGInfo, 0, sizeof(tmpGInfo) ); loaded = loadCurrentGame( globals, newIndex, &tmpGame, &tmpGInfo ); if ( loaded ) { game_dispose( &globals->game ); gi_disposePlayerInfo( MEMPOOL &globals->gameInfo ); globals->game = tmpGame; /* we leaking dictName here? PENDING(ehouse) */ XP_MEMCPY( &globals->gameInfo, &tmpGInfo, sizeof(globals->gameInfo) ); globals->gState.curGameIndex = newIndex; } else { game_dispose( &tmpGame ); gi_disposePlayerInfo( MEMPOOL &tmpGInfo ); } return loaded; } /* tryLoadSavedGame */ static XP_U16 hresX( PalmAppGlobals* globals, XP_U16 screenX ) { if ( globals->useHiRes ) { screenX *= 2; } return screenX; } static XP_U16 hresY( PalmAppGlobals* globals, XP_U16 screenY ) { if ( globals->useHiRes ) { screenY *= 2; } return screenY; } static void hresRect( PalmAppGlobals* globals, RectangleType* r ) { if ( globals->useHiRes ) { r->topLeft.x *= 2; r->topLeft.y *= 2; r->extent.x *= 2; r->extent.y *= 2; } } /***************************************************************************** * ****************************************************************************/ static Boolean mainViewHandleEvent( EventPtr event ) { Boolean handled = true; Boolean draw = false; Boolean erase; #if defined CURSOR_MOVEMENT && defined DEBUG CursorDirection cursorDir; Boolean movePiece; #endif PalmAppGlobals* globals; OpenSavedGameData* savedGameData; char newName[MAX_GAMENAME_LENGTH]; XP_U16 prevSize; XWStreamCtxt* stream; CALLBACK_PROLOGUE(); globals = getFormRefcon(); switch ( event->eType ) { case nilEvent: draw = handled = handleNilEvent( globals ); break; case newGameCancelEvent: /* If user cancelled the new game dialog that came up the first time he launched (i.e. when there's no game to fall back to) then just quit. It's easier than dealing with everything that can go wrong in this state. */ if ( globals->isFirstLaunch ) { EventType eventToPost; eventToPost.eType = appStopEvent; EvtAddEventToQueue( &eventToPost ); } globals->isNewGame = false; break; case openSavedGameEvent: globals->postponeDraw = XP_FALSE; prevSize = globals->gameInfo.boardSize; savedGameData = (OpenSavedGameData*)&event->data.generic; if ( tryLoadSavedGame( globals, savedGameData->newGameIndex ) ) { if ( prevSize > globals->gameInfo.boardSize ) { WinEraseWindow(); } initAndStartBoard( globals, XP_FALSE ); } draw = true; break; case newGameOkEvent: if ( globals->newGameIsNew ) { globals->gState.curGameIndex = countGameRecords( globals ); } globals->postponeDraw = false; makeDefaultGameName( newName ); writeNameToGameRecord( globals, globals->gState.curGameIndex, newName, XP_STRLEN(newName) ); globals->isFirstLaunch = false; /* so we'll save the game */ case loadGameEvent: XP_ASSERT( !!globals->game.server ); initAndStartBoard( globals, event->eType == newGameOkEvent ); draw = true; XP_ASSERT( !!globals->game.board ); break; #ifdef FEATURE_SILK case doResizeWinEvent: getSizes( globals ); positionBoard( globals ); board_invalAll( globals->game.board ); FrmUpdateForm( 0, frmRedrawUpdateCode ); break; #endif case prefsChangedEvent: erase = LocalPrefsToGlobal( globals ); draw = board_prefsChanged( globals->game.board, &globals->gState.cp ); server_prefsChanged( globals->game.server, &globals->gState.cp ); /* watch out for short-circuiting. Both must be called */ erase = positionBoard( globals ) || erase; if ( erase ) { WinEraseWindow(); } globals->postponeDraw = false; FrmUpdateForm( 0, frmRedrawUpdateCode ); /* <- why is this necessary? */ break; case winExitEvent: if ( event->data.winExit.exitWindow == (WinHandle)FrmGetActiveForm() ) { globals->menuIsDown = true; } break; case winEnterEvent: // From PalmOS's "Knowledge base": In the current code, the menu // doesn't remove itself when it receives a winExitEvent so we need // an extra check to make sure that the window being entered is the // first form. This may be different in your implementation (ie: if // the first form opened is not the one you are currently watching // for) if (event->data.winEnter.enterWindow == (WinHandle)FrmGetActiveForm() && event->data.winEnter.enterWindow == (WinHandle)FrmGetFirstForm() ) { globals->menuIsDown = false; } break; case frmOpenEvent: globals->mainForm = FrmGetActiveForm(); locateTrayButtons( globals ); updateForLefty( globals, globals->mainForm ); FrmDrawForm( globals->mainForm ); break; case frmUpdateEvent: FrmDrawForm( globals->mainForm ); /* on 3.5 and higher, this erases the window before drawing, so there's nothing to be done about the erase after user clicks OK in prefs dialog. */ if ( !!globals->game.board ) { RectangleType clip; WinGetClip( &clip ); drawFormButtons( globals ); hresRect( globals, &clip ); board_invalRect( globals->game.board, (XP_Rect*)&clip ); draw = !globals->postponeDraw; } break; case penDownEvent: draw = board_handlePenDown( globals->game.board, hresX(globals, event->screenX), hresY(globals, event->screenY), &handled ); globals->penDown = handled; break; case penMoveEvent: if ( globals->penDown ) { handled = board_handlePenMove( globals->game.board, hresX( globals, event->screenX ), hresY( globals, event->screenY )); draw = handled; } break; case penUpEvent: if ( globals->penDown ) { draw = board_handlePenUp( globals->game.board, hresX( globals, event->screenX), hresY( globals, event->screenY ) ); handled = draw; /* this is wrong!!!! */ globals->penDown = false; if ( !handled ) { handled = considerMenuShow( event ); } } break; case menuEvent: MenuEraseStatus(0); switch ( event->data.menu.itemID ) { case XW_TILEVALUES_PULLDOWN_ID: if ( !!globals->game.server ) { stream = makeSimpleStream( globals, NULL ); server_formatDictCounts( globals->game.server, stream, 4 ); /* 4: ncols */ askFromStream( globals, stream, STR_VALUES_TITLE, true ); } break; case XW_TILESLEFT_PULLDOWN_ID: if ( !!globals->game.board ) { stream = makeSimpleStream( globals, NULL ); board_formatRemainingTiles( globals->game.board, stream ); askFromStream( globals, stream, STR_REMAINS_TITLE, true ); } break; case XW_HISTORY_PULLDOWN_ID: if ( !!globals->game.server ) { XP_Bool gameOver = server_getGameIsOver(globals->game.server); stream = makeSimpleStream( globals, NULL ); model_writeGameHistory( globals->game.model, stream, globals->game.server, gameOver ); if ( stream_getSize( stream ) > 0 ) { askFromStream( globals, stream, STR_HISTORY_TITLE, XP_FALSE ); } else { beep(); } stream_destroy( stream ); } break; case XW_NEWGAME_PULLDOWN_ID: askStartNewGame( globals ); break; case XW_SAVEDGAMES_PULLDOWN_ID: if ( 0 ) { #if defined OWNER_HASH || defined NO_REG_REQUIRED } else if ( checkUserName() ) { saveOpenGame( globals );/* so it can be accurately duped */ /* save game changes state; reflect on screen before popping up dialog */ drawChangedBoard( globals ); FrmPopupForm( XW_SAVEDGAMES_DIALOG_ID ); #endif #ifndef NO_REG_REQUIRED } else { userErrorFromStrId( globals, STR_NOT_UNREG_VERS ); #endif } break; case XW_FINISH_PULLDOWN_ID: if ( server_getGameIsOver( globals->game.server ) ) { displayFinalScores( globals ); } else if ( palmaskFromStrId( globals, STR_CONFIRM_END_GAME, -1 ) ) { server_endGame( globals->game.server ); draw = true; } break; #ifndef XWFEATURE_STANDALONE_ONLY /* Would be better to beep when no remote players.... */ case XW_RESENDIR_PULLDOWN_ID: if ( !!globals->game.comms ) { (void)comms_resendAll( globals->game.comms ); } break; #endif case XW_BEAMDICT_PULLDOWN_ID: globals->dictuiForBeaming = true; FrmPopupForm( XW_DICTINFO_FORM ); break; case XW_BEAMBOARD_PULLDOWN_ID: beamBoard( globals ); break; #ifdef FEATURE_DUALCHOOSE /* This probably goes away at ship.... */ case XW_RUN68K_PULLDOWN_ID: case XW_RUNARM_PULLDOWN_ID: { Err err; LocalID dbID; (void)FtrUnregister( APPID, FEATURE_WANTS_68K ); err = FtrSet( APPID, FEATURE_WANTS_68K, event->data.menu.itemID == XW_RUN68K_PULLDOWN_ID? WANTS_68K : WANTS_ARM ); dbID = DmFindDatabase( CARD_0, APPNAME ); if ( dbID != 0 ) { (void)SysUIAppSwitch( 0, dbID, sysAppLaunchCmdNormalLaunch, NULL ); } } break; #endif case XW_PASSWORDS_PULLDOWN_ID: globals->isNewGame = false; FrmPopupForm( XW_NEWGAMES_FORM ); break; #ifdef COLOR_EDIT case XW_EDITCOLORS_PULLDOWN_ID: if ( globals->able == COLOR ) { FrmPopupForm( XW_COLORPREF_DIALOG_ID ); } break; # ifdef DEBUG case XW_DUMPCOLORS_PULLDOWN_ID: dumpColors( globals ); break; # endif #endif case XW_PREFS_PULLDOWN_ID: globals->stateTypeIsGlobal = XP_TRUE; GlobalPrefsToLocal( globals ); FrmPopupForm( XW_PREFS_FORM ); break; case XW_ABOUT_PULLDOWN_ID: palmaskFromStrId( globals, STR_ABOUT_CONTENT, STR_ABOUT_TITLE ); break; case XW_HINT_PULLDOWN_ID: board_resetEngine( globals->game.board ); globals->askTrayLimits = XP_FALSE; case XW_NEXTHINT_PULLDOWN_ID: draw = handleHintRequest( globals ); break; #ifdef XWFEATURE_SEARCHLIMIT case XW_HINTCONFIG_PULLDOWN_ID: board_resetEngine( globals->game.board ); globals->askTrayLimits = XP_TRUE; draw = handleHintRequest( globals ); break; #endif case XW_UNDOCUR_PULLDOWN_ID: draw = board_replaceTiles( globals->game.board ); break; case XW_UNDOLAST_PULLDOWN_ID: draw = server_handleUndo( globals->game.server ); break; case XW_DONE_PULLDOWN_ID: draw = handleDone( globals ); break; case XW_JUGGLE_PULLDOWN_ID: draw = handleJuggle( globals ); break; case XW_TRADEIN_PULLDOWN_ID: draw = handleTrade( globals ); break; case XW_HIDESHOWTRAY_PULLDOWN_ID: draw = handleHideTray( globals ); break; #ifdef FOR_GREMLINS case XW_GREMLIN_DIVIDER_RIGHT: if ( !!globals->game.board ) { board_moveDivider( globals->game.board, XP_TRUE ); draw = XP_TRUE; } break; case XW_GREMLIN_DIVIDER_LEFT: if ( !!globals->game.board ) { board_moveDivider( globals->game.board, XP_FALSE ); draw = XP_TRUE; } break; #endif #ifdef DEBUG case XW_DEBUGSHOW_PULLDOWN_ID: globals->gState.showDebugstrs = true; break; case XW_DEBUGHIDE_PULLDOWN_ID: globals->gState.showDebugstrs = false; board_invalAll( globals->game.board ); draw = true; break; case XW_DEBUGMEMO_PULLDOWN_ID: globals->gState.logToMemo = true; break; case XW_DEBUGSCREEN_PULLDOWN_ID: globals->gState.logToMemo = false; break; # if 0 case XW_RESET_PULLDOWN_ID: { EventType eventToPost; eventToPost.eType = appStopEvent; EvtAddEventToQueue( &eventToPost ); } globals->resetGame = true; break; # endif case XW_NETSTATS_PULLDOWN_ID: if ( !!globals->game.comms ) { stream = makeSimpleStream( globals, askOnClose ); comms_getStats( globals->game.comms, stream ); stream_destroy( stream ); } break; #ifdef MEM_DEBUG case XW_MEMSTATS_PULLDOWN_ID : if ( !!globals->mpool ) { stream = makeSimpleStream( globals, askOnClose ); mpool_stats( globals->mpool, stream ); stream_destroy( stream ); } break; #endif #endif default: break; } break; case keyDownEvent: { XP_Key xpkey = XP_KEY_NONE; Int16 ch = event->data.keyDown.chr; switch ( ch ) { case pageUpChr: draw = scrollBoard( globals, 0, false ); break; case pageDownChr: draw = scrollBoard( globals, 2, false ); break; case backspaceChr: xpkey = XP_CURSOR_KEY_DEL; break; default: /* I'm not interested in being dependent on a particular version of the OS, (can't manage to link against the intl library anyway) and so don't want to use the 3.5-only text tests. So let's give the board two shots at each char, one lower case and another upper. */ if ( ch < 255 && ch > ' ' ) { draw = board_handleKey( globals->game.board, ch ); if ( !draw && ch >= 'a' ) { draw = board_handleKey( globals->game.board, ch - ('a' - 'A') ); } } } if ( xpkey != XP_KEY_NONE ) { draw = board_handleKey( globals->game.board, xpkey ); } } break; case sclRepeatEvent: draw = scrollBoard( globals, event->data.sclRepeat.newValue-SBAR_MIN, true ); handled = false; break; case ctlSelectEvent: handled = true; switch ( event->data.ctlEnter.controlID ) { case XW_MAIN_FLIP_BUTTON_ID: draw = handleFlip( globals ); break; case XW_MAIN_VALUE_BUTTON_ID: draw = handleValueToggle( globals ); break; case XW_MAIN_HINT_BUTTON_ID: draw = handleHintRequest( globals ); break; #ifndef EIGHT_TILES case XW_MAIN_DONE_BUTTON_ID: draw = handleDone( globals ); break; case XW_MAIN_JUGGLE_BUTTON_ID: draw = handleJuggle( globals ); break; case XW_MAIN_TRADE_BUTTON_ID: draw = handleTrade( globals ); break; case XW_MAIN_HIDE_BUTTON_ID: draw = handleHideTray( globals ); break; #endif case XW_MAIN_SHOWTRAY_BUTTON_ID: draw = board_showTray( globals->game.board ); break; default: handled = false; break; } /* switch event->data.ctlEnter.controlID */ default: handled = false; break; } if ( draw && !!globals->game.board && !globals->menuIsDown ) { XP_Bool drewAll = board_draw( globals->game.board ); if ( !drewAll ) { globals->msgReceivedDraw = XP_TRUE; palm_util_requestTime( &globals->util ); } } CALLBACK_EPILOGUE(); return handled; } /* mainViewHandleEvent */ static void askStartNewGame( PalmAppGlobals* globals ) { if ( palmaskFromStrId( globals, STR_ASK_REPLACE_GAME, -1 )) { /* do nothing; popping up the NEWGAMES dlg will do it -- if not cancelled */ globals->newGameIsNew = XP_FALSE; } else if ( checkUserName() ) { saveOpenGame( globals ); drawChangedBoard( globals ); globals->newGameIsNew = XP_TRUE; } else { return; } globals->isNewGame = true; globals->isFirstLaunch = false; FrmPopupForm( XW_NEWGAMES_FORM ); } /* askStartNewGame */ static void displayFinalScores( PalmAppGlobals* globals ) { XWStreamCtxt* stream; stream = makeSimpleStream( globals, NULL ); server_writeFinalScores( globals->game.server, stream ); stream_putU8( stream, '\0' ); askFromStream( globals, stream, STR_FINAL_SCORES_TITLE, true ); } /* displayFinalScores */ XP_S16 palm_memcmp( XP_U8* p1, XP_U8* p2, XP_U16 nBytes ) { /* man memcmp: The memcmp() function compares the first n bytes of the memory areas s1 and s2. It returns an integer less than, equal to, or greater than zero if s1 is found, respectively, to be less than, to match, or be greater than s2.*/ XP_S16 result = 0; while ( result == 0 && nBytes-- ) { result = *p1++ - *p2++; } return result; } /* palm_memcmp */ static void askScrollbarAdjust( PalmAppGlobals* globals, FieldPtr field ) { UInt16 scrollPos; UInt16 textHeight; UInt16 fieldHeight; UInt16 maxValue; ScrollBarPtr scroll; FldGetScrollValues( field, &scrollPos, &textHeight, &fieldHeight ); if ( textHeight > fieldHeight ) { maxValue = textHeight - fieldHeight; } else if ( scrollPos != 0 ) { maxValue = scrollPos; } else { maxValue = 0; } scroll = getActiveObjectPtr( XW_ASK_SCROLLBAR_ID ); SclSetScrollBar( scroll, scrollPos, 0, maxValue, fieldHeight-1 ); globals->prevScroll = scrollPos; } /* askScrollbarAdjust */ static void getWindowBounds( WinHandle wHand, RectangleType* r ) { WinHandle prev = WinSetDrawWindow( wHand ); WinGetDrawWindowBounds( r ); (void)WinSetDrawWindow( prev ); } /* getWindowBounds */ static void tryGrowAskToFit( FormPtr form, FieldPtr field, XP_UCHAR* str ) { RectangleType fieldRect, dlgRect; UInt16 scrollPos; UInt16 textHeight; UInt16 fieldHeight; WinHandle wHand; XP_S16 fldWidth, fldHeight, maxHeight, needsHeight; XP_U16 lineHeight; XP_U16 growthAmt, i; RectangleType objBounds; FldGetScrollValues( field, &scrollPos, &textHeight, &fieldHeight ); getObjectBounds( XW_ASK_TXT_FIELD_ID, &fieldRect ); fldWidth = fieldRect.extent.x; fldHeight = fieldRect.extent.y; /* max is cur height plus diff between dialog's height and what it could be */ wHand = FrmGetWindowHandle( form ); getWindowBounds( wHand, &dlgRect ); maxHeight = fldHeight + (156 - dlgRect.extent.y); lineHeight = FntLineHeight(); needsHeight = FldCalcFieldHeight((const char*)str, fldWidth) * lineHeight; if ( needsHeight > maxHeight ) { /* make window as large as it can be */ needsHeight = maxHeight; } /* now round down to a multiple of lineHeight */ needsHeight = (needsHeight / lineHeight) * lineHeight; growthAmt = needsHeight - fldHeight; /* now reflect the new size by moving things around. Window first */ dlgRect.topLeft.y -= growthAmt; dlgRect.extent.y += growthAmt; WinSetBounds( wHand, &dlgRect ); /* then the field */ fieldRect.extent.y += growthAmt; setObjectBounds( XW_ASK_TXT_FIELD_ID, &fieldRect ); /* the scrollbar */ getObjectBounds( XW_ASK_SCROLLBAR_ID, &objBounds ); objBounds.extent.y = fieldRect.extent.y; setObjectBounds( XW_ASK_SCROLLBAR_ID, &objBounds ); /* and the buttons */ XP_ASSERT( XW_ASK_NO_BUTTON_ID - XW_ASK_YES_BUTTON_ID == 1 ); for ( i = XW_ASK_YES_BUTTON_ID; i <= XW_ASK_NO_BUTTON_ID; ++i ){ getObjectBounds( i, &objBounds ); objBounds.topLeft.y += growthAmt; setObjectBounds( i, &objBounds ); } } /* tryGrowAskToFit */ static Boolean handleScrollInAsk( EventPtr event ) { UInt16 linesToScroll = 0; Boolean scrollFromButton = false; Boolean result = true; WinDirectionType direction = 5; PalmAppGlobals* globals = (PalmAppGlobals*)getFormRefcon(); FieldPtr field; UInt16 endPosition; XP_ASSERT ( !!globals ); field = getActiveObjectPtr( XW_ASK_TXT_FIELD_ID ); switch ( event->eType ) { case penUpEvent: /* When user drags pen through text and causes a scroll the scrollbar will get out of sync. So we listen to the event but don't claim to have handled it. */ askScrollbarAdjust( globals, field ); result = false; break; case keyDownEvent: /* don't scroll a menu if open! */ if ( FrmGetWindowHandle( FrmGetActiveForm() ) == WinGetDrawWindow() ) { switch ( event->data.keyDown.chr ) { case pageUpChr: case vchrRockerUp: direction = winUp; break; case pageDownChr: case vchrRockerDown: direction = winDown; break; default: result = false; } linesToScroll = 3; scrollFromButton = true; } break; case sclRepeatEvent: { XP_S16 newVal = event->data.sclRepeat.newValue; XP_S16 tmp = newVal - globals->prevScroll; linesToScroll = XP_ABS( tmp ); XP_ASSERT( linesToScroll != 0 ); direction = newVal > globals->prevScroll? winDown: winUp; globals->prevScroll = newVal; scrollFromButton = false; } break; case menuEvent: MenuEraseStatus(0); result = true; switch ( event->data.menu.itemID ) { case ASK_COPY_PULLDOWN_ID: FldCopy( field ); break; case ASK_SELECTALL_PULLDOWN_ID: endPosition = FldGetTextLength( field ); FldSetSelection( field, 0, endPosition ); break; } break; default: result = false; } if ( result && FldScrollable( field, direction ) ) { FldScrollField( field, linesToScroll, direction ); if ( scrollFromButton ) { askScrollbarAdjust( globals, field ); } else { result = false; /* for some reason this is necessary to make scrolbar work right. */ } } return result; } /* handleScrollInAsk */ /* Swap the two elements, preserving their outside borders */ static void moveLeftOf( UInt16 rightID, UInt16 leftID ) { UInt16 leftIndex, rightIndex; UInt16 middleMargin; RectangleType leftBounds, rightBounds; FormPtr form = FrmGetActiveForm(); leftIndex = FrmGetObjectIndex( form, leftID ); rightIndex = FrmGetObjectIndex( form, rightID ); FrmGetObjectBounds( form, rightIndex, &rightBounds ); FrmGetObjectBounds( form, leftIndex, &leftBounds ); XP_ASSERT( rightBounds.topLeft.y == leftBounds.topLeft.y ); middleMargin = rightBounds.topLeft.x - (leftBounds.topLeft.x+leftBounds.extent.x); FrmSetObjectPosition( form, rightIndex, leftBounds.topLeft.x, leftBounds.topLeft.y ); FrmSetObjectPosition( form, leftIndex, leftBounds.topLeft.x + rightBounds.extent.x + middleMargin, leftBounds.topLeft.y ); } /* moveLeftOf */ Boolean palmaskFromStrId( PalmAppGlobals* globals, XP_U16 strId, XP_S16 titleID ) { XP_UCHAR* message; XP_UCHAR* yes; message = getResString( globals, strId ); XP_ASSERT( !!message ); yes = titleID < 0? NULL: getResString( globals, STR_OK ); return palmask( globals, message, yes, titleID ); } /* palmaskFromStrId */ Boolean palmask( PalmAppGlobals* globals, XP_UCHAR* str, XP_UCHAR* yesButton, XP_S16 titleID ) { FormPtr form, prevForm; FieldPtr field; XP_UCHAR* title; UInt16 buttonHit; if ( !!globals->game.board ) { board_pushTimerSave( globals->game.board ); } title = titleID >= 0? getResString( globals, titleID ): NULL; prevForm = FrmGetActiveForm(); form = FrmInitForm( XW_ASK_FORM_ID ); FrmSetActiveForm( form ); if ( !!yesButton ) { CtlSetLabel( getActiveObjectPtr(XW_ASK_YES_BUTTON_ID), (const char*)yesButton ); fitButtonToString( XW_ASK_YES_BUTTON_ID ); } if ( title != NULL ) { FrmSetTitle( form, (char*)title ); /* Hack: take advantage of fact that for now only non-queries should not have a cancel button. */ disOrEnable( form, XW_ASK_NO_BUTTON_ID, false ); centerControl( form, XW_ASK_YES_BUTTON_ID ); } FrmSetEventHandler( form, handleScrollInAsk ); globals->prevScroll = 0; if ( globals->isLefty ) { moveLeftOf( XW_ASK_SCROLLBAR_ID, XW_ASK_TXT_FIELD_ID ); } field = getActiveObjectPtr( XW_ASK_TXT_FIELD_ID ); FldSetTextPtr( field, (char*)str ); FldRecalculateField( field, true ); if ( globals->romVersion >= 35 ) { /* I'm not sure how to do this pre 3.5 */ tryGrowAskToFit( form, field, str ); } askScrollbarAdjust( globals, field ); FrmDrawForm( form ); buttonHit = FrmDoDialog( form ); FrmDeleteForm( form ); FrmSetActiveForm( prevForm ); if ( !!globals->game.board ) { board_popTimerSave( globals->game.board ); } return buttonHit == XW_ASK_YES_BUTTON_ID; } /* palmask */ static Boolean askFromStream( PalmAppGlobals* globals, XWStreamCtxt* stream, XP_S16 titleID, Boolean closeAndDestroy ) { XP_U16 nBytes = stream_getSize( stream ); Boolean result; XP_UCHAR* buffer; XP_ASSERT( nBytes < maxFieldTextLen ); buffer = XP_MALLOC( globals->mpool, nBytes + 1 ); stream_getBytes( stream, buffer, nBytes ); /* nuke trailing chars to they don't extend length of field */ while ( buffer[nBytes-1] == '\n' ) { --nBytes; } buffer[nBytes] = '\0'; /* just to be safe */ result = palmask( globals, buffer, getResString( globals, STR_OK ), titleID ); XP_FREE( globals->mpool, buffer ); if ( closeAndDestroy ) { stream_destroy( stream ); } return result; } /* askFromStream */ Boolean askPassword( const XP_UCHAR* name, Boolean isNew, XP_UCHAR* retbuf, XP_U16* len ) { Boolean result = false; FormPtr prevForm, form; FieldPtr field; UInt16 showMe; prevForm = FrmGetActiveForm(); form = FrmInitForm( XW_PASSWORD_DIALOG_ID ); FrmSetActiveForm( form ); if ( isNew ) { showMe = XW_PASSWORD_NEWNAME_LABEL; } else { showMe = XW_PASSWORD_NAME_LABEL; } FrmShowObject( form, FrmGetObjectIndex( form, showMe ) ); FrmDrawForm( form ); if ( !!name ) { field = getActiveObjectPtr( XW_PASSWORD_NAME_FIELD ); FldSetTextPtr( field, (char*)name ); FldDrawField( field ); } FrmSetFocus( form, FrmGetObjectIndex( form, XW_PASSWORD_PASS_FIELD ) ); field = getActiveObjectPtr( XW_PASSWORD_PASS_FIELD ); if ( FrmDoDialog( form ) == XW_PASSWORD_OK_BUTTON ) { char* enteredPass = FldGetTextPtr( field ); XP_U16 enteredLen = !enteredPass? 0: StrLen(enteredPass); if ( enteredLen < *len ) { result = true; if ( enteredLen > 0 ) { XP_MEMCPY( retbuf, enteredPass, enteredLen ); } retbuf[enteredLen] = '\0'; *len = enteredLen; } } FrmDeleteForm( form ); FrmSetActiveForm( prevForm ); return result; } /* askPassword */ static Boolean handleKeysInBlank( EventPtr event ) { Boolean handled = false; if ( event->eType == keyDownEvent ) { char ch = event->data.keyDown.chr; if ( ch >= 'a' && ch <= 'z' ) { ch += 'A' - 'a'; } if ( ch >= 'A' && ch <= 'Z' ) { ListPtr lettersList = getActiveObjectPtr( XW_BLANK_LIST_ID ); XP_U16 nItems; XP_U16 i; XP_ASSERT( !!lettersList ); nItems = LstGetNumberOfItems( lettersList ); for ( i = 0; i < nItems; ++i ) { XP_UCHAR* itext = LstGetSelectionText( lettersList, i ); if ( !!itext && (itext[0] == ch) ) { LstSetSelection( lettersList, i ); LstMakeItemVisible( lettersList, i ); handled = true; break; } } } else if ( ch == '\n' ) { EventType eventToPost; eventToPost.eType = ctlSelectEvent; eventToPost.data.ctlSelect.controlID = XW_BLANK_OK_BUTTON_ID; eventToPost.data.ctlSelect.pControl = getActiveObjectPtr( XW_BLANK_OK_BUTTON_ID ); EvtAddEventToQueue( &eventToPost ); } } return handled; } /* handleKeysInBlank */ static XP_S16 askBlankValue( PalmAppGlobals* globals, XP_U16 playerNum, const PickInfo* pi, XP_U16 nTiles, const XP_UCHAR4* texts ) { FormPtr form, prevForm; ListPtr lettersList; ListData ld; XP_U16 i; XP_S16 chosen; XP_UCHAR labelBuf[96]; XP_UCHAR* name; XP_UCHAR* labelFmt; FieldPtr fld; XP_U16 tapped; #ifdef FEATURE_TRAY_EDIT XP_Bool forBlank = pi->why == PICK_FOR_BLANK; #endif initListData( MEMPOOL &ld, nTiles ); for ( i = 0; i < nTiles; ++i ) { addListTextItem( MEMPOOL &ld, (XP_UCHAR*)texts[i] ); } prevForm = FrmGetActiveForm(); form = FrmInitForm( XW_BLANK_DIALOG_ID ); FrmSetActiveForm( form ); #ifdef FEATURE_TRAY_EDIT disOrEnable( form, XW_BLANK_PICK_BUTTON_ID, !forBlank ); disOrEnable( form, XW_BLANK_BACKUP_BUTTON_ID, !forBlank && pi->thisPick > 0 ); #endif lettersList = getActiveObjectPtr( XW_BLANK_LIST_ID ); setListChoices( &ld, lettersList, NULL ); LstSetSelection( lettersList, 0 ); name = globals->gameInfo.players[playerNum].name; labelFmt = getResString( globals, #ifdef FEATURE_TRAY_EDIT !forBlank? STRS_PICK_TILE: #endif STR_PICK_BLANK ); XP_SNPRINTF( labelBuf, sizeof(labelBuf), labelFmt, name ); #ifdef FEATURE_TRAY_EDIT if ( !forBlank ) { XP_U16 lenSoFar; XP_U16 i; lenSoFar = XP_STRLEN(labelBuf); lenSoFar += XP_SNPRINTF( labelBuf + lenSoFar, sizeof(labelBuf) - lenSoFar, " (%d/%d)\nCur", pi->thisPick+1, pi->nTotal ); for ( i = 0; i < pi->nCurTiles; ++i ) { lenSoFar += XP_SNPRINTF( labelBuf+lenSoFar, sizeof(labelBuf)-lenSoFar, "%s%s", i==0?": ":", ", pi->curTiles[i] ); } } #endif fld = getActiveObjectPtr( XW_BLANK_LABEL_FIELD_ID ); FldSetTextPtr( fld, labelBuf ); FldRecalculateField( fld, false ); FrmDrawForm( form ); FrmSetEventHandler( form, handleKeysInBlank ); tapped = FrmDoDialog( form ); if ( 0 ) { #ifdef FEATURE_TRAY_EDIT } else if ( tapped == XW_BLANK_PICK_BUTTON_ID ) { chosen = PICKER_PICKALL; } else if ( tapped == XW_BLANK_BACKUP_BUTTON_ID ) { chosen = PICKER_BACKUP; #endif } else { chosen = LstGetSelection( lettersList ); } FrmDeleteForm( form ); FrmSetActiveForm( prevForm ); freeListData( MEMPOOL &ld ); return chosen; } /* askBlankValue */ /***************************************************************************** * Callbacks ****************************************************************************/ static VTableMgr* palm_util_getVTManager( XW_UtilCtxt* uc ) { PalmAppGlobals* globals = (PalmAppGlobals*)uc->closure; return globals->vtMgr; } /* palm_util_getVTManager */ static void palm_util_userError( XW_UtilCtxt* uc, UtilErrID id ) { PalmAppGlobals* globals; XP_U16 strID = STR_LAST_STRING; switch( id ) { case ERR_TILES_NOT_IN_LINE: strID = STR_ALL_IN_LINE_ERR; break; case ERR_NO_EMPTIES_IN_TURN: strID = STR_NO_EMPTIES_ERR; break; case ERR_TWO_TILES_FIRST_MOVE: strID = STR_FIRST_MOVE_ERR; break; case ERR_TILES_MUST_CONTACT: strID = STR_MUST_CONTACT_ERR; break; case ERR_NOT_YOUR_TURN: strID = STR_NOT_YOUR_TURN; break; case ERR_NO_PEEK_ROBOT_TILES: strID = STR_NO_PEEK_ROBOT_TILES; break; #ifndef XWFEATURE_STANDALONE_ONLY case ERR_NO_PEEK_REMOTE_TILES: strID = STR_NO_PEEK_REMOTE_TILES; break; case ERR_SERVER_DICT_WINS: strID = STR_SERVER_DICT_WINS; break; case ERR_REG_UNEXPECTED_USER: strID = STR_REG_UNEXPECTED_USER; break; #endif case ERR_CANT_TRADE_MID_MOVE: strID = STR_CANT_TRADE_MIDTURN; break; case ERR_TOO_FEW_TILES_LEFT_TO_TRADE: strID = STR_TOO_FEW_TILES; break; case ERR_CANT_UNDO_TILEASSIGN: strID = STR_CANT_UNDO_TILEASSIGN; break; #ifdef BEYOND_IR case ERR_RELAY_BASE + XWRELAY_ERROR_TIMEOUT: strID = STR_RELAY_TIMEOUT; break; case ERR_RELAY_BASE + XWRELAY_ERROR_HEART_YOU: strID = STR_RELAY_GENERIC; break; case ERR_RELAY_BASE + XWRELAY_ERROR_HEART_OTHER: case ERR_RELAY_BASE + XWRELAY_ERROR_LOST_OTHER: strID = STR_RELAY_LOST_OTHER; break; #endif default: XP_DEBUGF( "errcode=%d", id ); break; } XP_ASSERT( strID < STR_LAST_STRING ); globals = (PalmAppGlobals*)uc->closure; userErrorFromStrId( globals, strID ); } /* palm_util_userError */ static void userErrorFromStrId( PalmAppGlobals* globals, XP_U16 strID ) { XP_UCHAR* message = getResString( globals, strID ); (void)FrmCustomAlert( XW_ERROR_ALERT_ID, (const char*)message, " ", " " ); } /* userErrorFromStrId */ static XP_Bool palm_util_userQuery( XW_UtilCtxt* uc, UtilQueryID id, XWStreamCtxt* stream ) { PalmAppGlobals* globals = (PalmAppGlobals*)uc->closure; XP_U16 strID = STR_LAST_STRING; /* error if not changed */ switch( id ) { case QUERY_COMMIT_TURN: return askFromStream( globals, stream, -1, false ); break; case QUERY_COMMIT_TRADE: strID = STR_CONFIRM_TRADE; break; case QUERY_ROBOT_MOVE: case QUERY_ROBOT_TRADE: return askFromStream( globals, stream, STR_ROBOT_TITLE, false ); break; default: XP_ASSERT(0); break; } return (XP_Bool)palmaskFromStrId( globals, strID, -1 ); } /* palm_util_userQuery */ static XWBonusType palm_util_getSquareBonus( XW_UtilCtxt* uc, ModelCtxt* model, XP_U16 col, XP_U16 row ) { PalmAppGlobals* globals = (PalmAppGlobals*)uc->closure; XP_U16 nCols, nRows; XP_U16 midCol, index, resIndex; XP_UCHAR* bonusResPtr; XP_U8 value; XP_ASSERT( !!model ); nCols = model_numCols( model ); nRows = model_numRows( model ); midCol = nCols / 2; resIndex = (PALM_MAX_COLS - nCols) / 2; bonusResPtr = globals->bonusResPtr[resIndex]; if ( col > midCol ) col = nCols - 1 - col; if ( row > midCol ) row = nRows - 1 - row; index = (row*(midCol+1)) + col; XP_ASSERT( index/2 < MemPtrSize(bonusResPtr) ); value = bonusResPtr[index/2]; if ( index%2 == 0 ) { value >>= 4; } return value & 0x0F; } /* palm_util_getSquareBonus */ static XP_S16 palm_util_userPickTile( XW_UtilCtxt* uc, const PickInfo* pi, XP_U16 playerNum, const XP_UCHAR4* texts, XP_U16 nTiles ) { PalmAppGlobals* globals = (PalmAppGlobals*)uc->closure; return askBlankValue( globals, playerNum, pi, nTiles, texts ); } /* palm_util_userPickTile */ static XP_Bool palm_util_askPassword( XW_UtilCtxt* XP_UNUSED(uc), const XP_UCHAR* name, XP_UCHAR* buf, XP_U16* len ) { return askPassword( name, false, buf, len ); } /* palm_util_askPassword */ static void palm_util_trayHiddenChange( XW_UtilCtxt* uc, XW_TrayVisState XP_UNUSED(newState), XP_U16 XP_UNUSED(nVisibleRows) ) { PalmAppGlobals* globals = (PalmAppGlobals*)uc->closure; palmSetCtrlsForTray( globals ); drawFormButtons( globals ); } /* palm_util_trayHiddenChange */ static void palm_util_yOffsetChange( XW_UtilCtxt* uc, XP_U16 XP_UNUSED_DBG(oldOffset), XP_U16 newOffset ) { PalmAppGlobals* globals = (PalmAppGlobals*)uc->closure; XP_ASSERT( oldOffset != newOffset ); updateScrollbar( globals, newOffset ); } /* palm_util_yOffsetChange */ static void palm_util_notifyGameOver( XW_UtilCtxt* uc ) { PalmAppGlobals* globals = (PalmAppGlobals*)uc->closure; board_draw( globals->game.board ); /* refresh scoreboard so it agrees with dialog */ displayFinalScores( globals ); } /* palm_util_notifyGameOver */ static XP_Bool palm_util_hiliteCell( XW_UtilCtxt* uc, XP_U16 col, XP_U16 row ) { /* EvtSysEventAvail, not EvtEventAvail, because the former ignores nil events, and it appears that when there's an IR connection up the system floods us with nil events.*/ XP_Bool eventPending = EvtSysEventAvail( true ); #ifdef SHOW_PROGRESS if ( !eventPending ) { PalmAppGlobals* globals = (PalmAppGlobals*)uc->closure; if ( globals->progress.curLine >= 0 ) { board_hiliteCellAt( globals->game.board, col, row ); } } #endif return !eventPending; } /* palm_util_hiliteCell */ static XP_Bool palm_util_engineProgressCallback( XW_UtilCtxt* uc ) { #ifdef SHOW_PROGRESS PalmAppGlobals* globals = (PalmAppGlobals*)uc->closure; if ( globals->gState.showProgress && globals->progress.curLine >= 0 ) { RectangleType rect = globals->progress.boundsRect; short line; Boolean draw; globals->progress.curLine %= rect.extent.y * 2; draw = globals->progress.curLine < rect.extent.y; line = globals->progress.curLine % (rect.extent.y) + 1; line = rect.topLeft.y + rect.extent.y - line; if ( draw ) { WinDrawLine( rect.topLeft.x, line, rect.topLeft.x + rect.extent.x - 1, line); } else { WinEraseLine( rect.topLeft.x, line, rect.topLeft.x + rect.extent.x - 1, line ); } ++globals->progress.curLine; } #endif return !EvtSysEventAvail( true ); } /* palm_util_engineProgressCallback */ static void palm_util_setTimer( XW_UtilCtxt* uc, XWTimerReason why, XP_U16 XP_UNUSED_IR(secsFromNow), TimerProc proc, void* closure ) { PalmAppGlobals* globals = (PalmAppGlobals*)uc->closure; XP_U32 now = TimGetTicks(); if ( why == TIMER_PENDOWN ) { now += PALM_TIMER_DELAY; } else if ( why == TIMER_TIMERTICK ) { now += SysTicksPerSecond(); #ifdef BEYOND_IR } else if ( why == TIMER_HEARTBEAT ) { now += (secsFromNow * SysTicksPerSecond()); #endif } else { XP_ASSERT( 0 ); } globals->timerProcs[why] = proc; globals->timerClosures[why] = closure; globals->timerFireAt[why] = now; } /* palm_util_setTimer */ static void palm_util_requestTime( XW_UtilCtxt* uc ) { PalmAppGlobals* globals = (PalmAppGlobals*)uc->closure; globals->timeRequested = true; } /* palm_util_requestTime */ static XP_U32 palm_util_getCurSeconds( XW_UtilCtxt* XP_UNUSED(uc) ) { return TimGetSeconds(); } /* palm_util_getCurSeconds */ static DictionaryCtxt* palm_util_makeEmptyDict( XW_UtilCtxt* uc ) { PalmAppGlobals* globals = (PalmAppGlobals*)uc->closure; DictionaryCtxt* result = palm_dictionary_make( MPPARM(uc->mpool) globals, NULL, NULL ); XP_ASSERT( !!result ); return result; } /* palm_util_makeEmptyDict */ #ifndef XWFEATURE_STANDALONE_ONLY static XWStreamCtxt* palm_util_makeStreamFromAddr( XW_UtilCtxt* uc, XP_U16 channelNo ) { XWStreamCtxt* stream; PalmAppGlobals* globals = (PalmAppGlobals*)uc->closure; XP_ASSERT( !!globals->game.comms ); /* shouldn't be making stream in case where can't send -- or should I just be passing a null on-close function? */ XP_LOGF( "making stream for channel %d", channelNo ); stream = makeSimpleStream( globals, palm_send_on_close ); stream_setAddress( stream, channelNo ); return stream; } /* palm_util_makeStreamFromAddr */ #endif static void palm_send_on_close( XWStreamCtxt* stream, void* closure ) { PalmAppGlobals* globals = (PalmAppGlobals*)closure; XP_ASSERT( !!globals->game.comms ); comms_send( globals->game.comms, stream ); } /* palm_send_on_close */ static XP_S16 palm_send( const XP_U8* buf, XP_U16 len, const CommsAddrRec* XP_UNUSED_IR(addr), /* !!!? */ void* closure ) { PalmAppGlobals* globals = (PalmAppGlobals*)closure; #ifdef BEYOND_IR XP_S16 result = 0; switch( comms_getConType( globals->game.comms ) ) { case COMMS_CONN_IR: result = palm_ir_send( buf, len, globals ); break; case COMMS_CONN_RELAY: result = palm_ip_send( buf, len, addr, globals ); break; #ifdef XWFEATURE_PALM_BLUETOOTH case COMMS_CONN_BT: result = palm_bt_send( buf, len, globals ); break; #endif default: XP_ASSERT(0); } return result; #else return palm_ir_send( buf, len, globals ); #endif } /* palm_send */ void checkAndDeliver( PalmAppGlobals* globals, XWStreamCtxt* instream ) { if ( comms_checkIncomingStream( globals->game.comms, instream, NULL ) ) { globals->msgReceivedDraw = server_receiveMessage( globals->game.server, instream ); globals->msgReceivedDraw = true; } stream_destroy( instream ); palm_util_requestTime( &globals->util ); } /* checkAndDeliver */ static XP_UCHAR* palm_util_getUserString( XW_UtilCtxt* uc, XP_U16 stringCode ) { PalmAppGlobals* globals = (PalmAppGlobals*)uc->closure; XP_UCHAR* str = getResString( globals, stringCode ); return str; } /* palm_util_getUserString */ static void formatBadWords( BadWordInfo* bwi, char buf[] ) { XP_U16 i; for ( i = 0, buf[0] = '\0'; ; ) { char wordBuf[18]; StrPrintF( wordBuf, "\"%s\"", bwi->words[i] ); StrCat( buf, wordBuf ); if ( ++i == bwi->nWords ) { break; } StrCat( buf, ", " ); } } /* formatBadWords */ static XP_Bool palm_util_warnIllegalWord( XW_UtilCtxt* uc, BadWordInfo* bwi, XP_U16 XP_UNUSED(turn), XP_Bool turnLost ) { XP_Bool result = XP_TRUE; PalmAppGlobals* globals = (PalmAppGlobals*)uc->closure; XP_UCHAR buf[200]; char wordsBuf[150]; XP_UCHAR* format = getResString( globals, STR_ILLEGAL_WORD ); formatBadWords( bwi, wordsBuf ); StrPrintF( (char*)buf, (const char*)format, wordsBuf ); if ( turnLost ) { (void)FrmCustomAlert( XW_ERROR_ALERT_ID, (const char*)buf, " ", " " ); } else { result = palmask( globals, buf, NULL, -1 ); } return result; } /* palm_util_warnIllegalWord */ #ifdef BEYOND_IR #ifdef XWFEATURE_PALM_BLUETOOTH static void btDataHandler( PalmAppGlobals* globals, const XP_U8* data, XP_U16 len ) { XWStreamCtxt* instream; LOG_FUNC(); instream = makeSimpleStream( globals, NULL ); stream_putBytes( instream, data, len ); checkAndDeliver( globals, instream ); LOG_RETURN_VOID(); } #endif static void palm_util_addrChange( XW_UtilCtxt* uc, const CommsAddrRec* oldAddr, const CommsAddrRec* newAddr, XP_Bool isServer ) { PalmAppGlobals* globals = (PalmAppGlobals*)uc->closure; if ( COMMS_CONN_RELAY == newAddr->conType ) { ip_addr_change( globals, oldAddr, newAddr ); #ifdef XWFEATURE_PALM_BLUETOOTH } else if ( COMMS_CONN_BT == newAddr->conType ) { palm_bt_init( globals, btDataHandler, isServer ); #endif } } #endif #ifdef XWFEATURE_SEARCHLIMIT static XP_Bool palm_util_getTraySearchLimits( XW_UtilCtxt* XP_UNUSED(uc), XP_U16* min, XP_U16* max ) { return doHintConfig( min, max ); } /* palm_util_getTraySearchLimits */ #endif #ifdef SHOW_PROGRESS static void palm_util_engineStarting( XW_UtilCtxt* uc, XP_U16 nBlanks ) { PalmAppGlobals* globals = (PalmAppGlobals*)uc->closure; if ( globals->gState.showProgress #ifdef XW_TARGET_PNO && ( nBlanks > 0 ) #endif ) { RectangleType* bounds = &globals->progress.boundsRect; WinEraseRectangle( bounds, 0 ); WinDrawRectangleFrame( rectangleFrame, bounds ); globals->progress.curLine = 0; } else { globals->progress.curLine = -1; } } /* palm_util_engineStarting */ static void palm_util_engineStopping( XW_UtilCtxt* uc ) { PalmAppGlobals* globals = (PalmAppGlobals*)uc->closure; if ( globals->gState.showProgress && globals->progress.curLine >= 0 ) { WinEraseRectangle( &globals->progress.boundsRect, 0 ); WinEraseRectangleFrame( rectangleFrame, &globals->progress.boundsRect ); if ( globals->needsScrollbar ) { SclDrawScrollBar( getActiveObjectPtr( XW_MAIN_SCROLLBAR_ID ) ); } } } /* palm_util_engineStopping */ #endif